Auto-detect grid row semantics

This commit is contained in:
Martin Haug 2022-06-08 13:11:51 +02:00
parent 6d8b65c4b2
commit 995a7882d2
4 changed files with 31 additions and 27 deletions

View File

@ -9,8 +9,6 @@ pub struct GridNode {
pub gutter: Spec<Vec<TrackSizing>>, pub gutter: Spec<Vec<TrackSizing>>,
/// The nodes to be arranged in a grid. /// The nodes to be arranged in a grid.
pub cells: Vec<LayoutNode>, pub cells: Vec<LayoutNode>,
/// The role of the grid in the semantic tree.
pub semantic: GridSemantics,
} }
#[node] #[node]
@ -28,7 +26,6 @@ impl GridNode {
row_gutter.unwrap_or(base_gutter), row_gutter.unwrap_or(base_gutter),
), ),
cells: args.all()?, cells: args.all()?,
semantic: GridSemantics::None,
})) }))
} }
} }
@ -48,7 +45,6 @@ impl Layout for GridNode {
&self.cells, &self.cells,
regions, regions,
styles, styles,
self.semantic,
); );
// Measure the columns and layout the grid row-by-row. // Measure the columns and layout the grid row-by-row.
@ -71,7 +67,7 @@ pub enum TrackSizing {
/// Defines what kind of semantics a grid should represent. /// Defines what kind of semantics a grid should represent.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum GridSemantics { enum GridSemantics {
/// The grid is transparent to the semantic tree. /// The grid is transparent to the semantic tree.
None, None,
/// The grid is a list, its rows are list items. The bool indicates whether /// The grid is a list, its rows are list items. The bool indicates whether
@ -82,6 +78,7 @@ pub enum GridSemantics {
} }
impl GridSemantics { impl GridSemantics {
/// The role of a row in a grid with these semantics.
fn row(self) -> Option<Role> { fn row(self) -> Option<Role> {
match self { match self {
Self::None => None, Self::None => None,
@ -89,6 +86,18 @@ impl GridSemantics {
Self::Table => Some(Role::TableRow), Self::Table => Some(Role::TableRow),
} }
} }
/// Returns the semantic role of a grid row given the previous semantics and
/// the cell's role.
fn determine(other: Option<Self>, role: Option<Role>) -> Self {
match (other, role) {
(None, Some(Role::ListItem | Role::ListLabel)) => Self::List,
(Some(Self::List), Some(Role::ListItem | Role::ListLabel)) => Self::List,
(None, Some(Role::TableCell)) => Self::Table,
(Some(Self::Table), Some(Role::TableCell)) => Self::Table,
_ => Self::None,
}
}
} }
castable! { castable! {
@ -130,8 +139,6 @@ pub struct GridLayouter<'a> {
regions: Regions, regions: Regions,
/// The inherited styles. /// The inherited styles.
styles: StyleChain<'a>, styles: StyleChain<'a>,
/// The role of the grid in the semantic tree.
semantic: GridSemantics,
/// Resolved column sizes. /// Resolved column sizes.
rcols: Vec<Length>, rcols: Vec<Length>,
/// Rows in the current region. /// Rows in the current region.
@ -167,7 +174,6 @@ impl<'a> GridLayouter<'a> {
cells: &'a [LayoutNode], cells: &'a [LayoutNode],
regions: &Regions, regions: &Regions,
styles: StyleChain<'a>, styles: StyleChain<'a>,
semantic: GridSemantics,
) -> Self { ) -> Self {
let mut cols = vec![]; let mut cols = vec![];
let mut rows = vec![]; let mut rows = vec![];
@ -222,7 +228,6 @@ impl<'a> GridLayouter<'a> {
rows, rows,
regions, regions,
styles, styles,
semantic,
rcols, rcols,
lrows, lrows,
full, full,
@ -480,11 +485,9 @@ impl<'a> GridLayouter<'a> {
/// Layout a row with fixed height and return its frame. /// Layout a row with fixed height and return its frame.
fn layout_single_row(&mut self, height: Length, y: usize) -> TypResult<Frame> { fn layout_single_row(&mut self, height: Length, y: usize) -> TypResult<Frame> {
let mut output = Frame::new(Size::new(self.used.x, height)); let mut output = Frame::new(Size::new(self.used.x, height));
if let Some(role) = self.semantic.row() {
output.apply_role(role);
}
let mut pos = Point::zero(); let mut pos = Point::zero();
let mut semantic = None;
for (x, &rcol) in self.rcols.iter().enumerate() { for (x, &rcol) in self.rcols.iter().enumerate() {
if let Some(node) = self.cell(x, y) { if let Some(node) = self.cell(x, y) {
@ -498,6 +501,7 @@ impl<'a> GridLayouter<'a> {
let pod = Regions::one(size, base, Spec::splat(true)); let pod = Regions::one(size, base, Spec::splat(true));
let frame = node.layout(self.ctx, &pod, self.styles)?.remove(0); let frame = node.layout(self.ctx, &pod, self.styles)?.remove(0);
semantic = Some(GridSemantics::determine(semantic, frame.role()));
output.push_frame(pos, frame); output.push_frame(pos, frame);
} }
@ -505,6 +509,10 @@ impl<'a> GridLayouter<'a> {
pos.x += rcol; pos.x += rcol;
} }
if let Some(role) = semantic.and_then(GridSemantics::row) {
output.apply_role(role);
}
Ok(output) Ok(output)
} }
@ -517,14 +525,7 @@ impl<'a> GridLayouter<'a> {
// Prepare frames. // Prepare frames.
let mut outputs: Vec<_> = heights let mut outputs: Vec<_> = heights
.iter() .iter()
.map(|&h| { .map(|&h| Frame::new(Size::new(self.used.x, h)))
let mut f = Frame::new(Size::new(self.used.x, h));
if let Some(role) = self.semantic.row() {
f.apply_role(role);
}
f
})
.collect(); .collect();
// Prepare regions. // Prepare regions.
@ -534,6 +535,7 @@ impl<'a> GridLayouter<'a> {
// Layout the row. // Layout the row.
let mut pos = Point::zero(); let mut pos = Point::zero();
let mut semantic = None;
for (x, &rcol) in self.rcols.iter().enumerate() { for (x, &rcol) in self.rcols.iter().enumerate() {
if let Some(node) = self.cell(x, y) { if let Some(node) = self.cell(x, y) {
pod.first.x = rcol; pod.first.x = rcol;
@ -547,6 +549,7 @@ impl<'a> GridLayouter<'a> {
// Push the layouted frames into the individual output frames. // Push the layouted frames into the individual output frames.
let frames = node.layout(self.ctx, &pod, self.styles)?; let frames = node.layout(self.ctx, &pod, self.styles)?;
for (output, frame) in outputs.iter_mut().zip(frames) { for (output, frame) in outputs.iter_mut().zip(frames) {
semantic = Some(GridSemantics::determine(semantic, frame.role()));
output.push_frame(pos, frame); output.push_frame(pos, frame);
} }
} }
@ -554,6 +557,12 @@ impl<'a> GridLayouter<'a> {
pos.x += rcol; pos.x += rcol;
} }
for output in outputs.iter_mut() {
if let Some(role) = semantic.and_then(GridSemantics::row) {
output.apply_role(role);
}
}
Ok(outputs) Ok(outputs)
} }

View File

@ -1,7 +1,6 @@
use crate::library::layout::BlockSpacing; use crate::library::layout::BlockSpacing;
use crate::library::prelude::*; use crate::library::prelude::*;
use crate::library::text::{FontFamily, TextNode, TextSize}; use crate::library::text::{FontFamily, TextNode, TextSize};
use crate::model::StyleEntry;
/// A section heading. /// A section heading.
#[derive(Debug, Hash)] #[derive(Debug, Hash)]

View File

@ -2,11 +2,10 @@ use std::fmt::Write;
use unscanny::Scanner; use unscanny::Scanner;
use crate::library::layout::{BlockSpacing, GridNode, GridSemantics, TrackSizing}; use crate::library::layout::{BlockSpacing, GridNode, TrackSizing};
use crate::library::prelude::*; use crate::library::prelude::*;
use crate::library::text::ParNode; use crate::library::text::ParNode;
use crate::library::utility::Numbering; use crate::library::utility::Numbering;
use crate::model::StyleEntry;
/// An unordered (bulleted) or ordered (numbered) list. /// An unordered (bulleted) or ordered (numbered) list.
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
@ -141,7 +140,6 @@ impl<const L: ListKind> Show for ListNode<L> {
]), ]),
gutter: Spec::with_y(vec![TrackSizing::Relative(gutter.into())]), gutter: Spec::with_y(vec![TrackSizing::Relative(gutter.into())]),
cells, cells,
semantic: GridSemantics::List,
})) }))
} }

View File

@ -1,6 +1,5 @@
use crate::library::layout::{BlockSpacing, GridNode, GridSemantics, TrackSizing}; use crate::library::layout::{BlockSpacing, GridNode, TrackSizing};
use crate::library::prelude::*; use crate::library::prelude::*;
use crate::model::StyleEntry;
/// A table of items. /// A table of items.
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
@ -105,7 +104,6 @@ impl Show for TableNode {
tracks: self.tracks.clone(), tracks: self.tracks.clone(),
gutter: self.gutter.clone(), gutter: self.gutter.clone(),
cells, cells,
semantic: GridSemantics::Table,
}) })
.role(Role::Table)) .role(Role::Table))
} }