Better table lines

This commit is contained in:
Laurenz 2023-02-14 12:08:05 +01:00
parent e64b2379a8
commit cc9b52ddd4
12 changed files with 116 additions and 33 deletions

View File

@ -139,7 +139,7 @@ impl Layout for GridNode {
); );
// Measure the columns and layout the grid row-by-row. // Measure the columns and layout the grid row-by-row.
layouter.layout() Ok(layouter.layout()?.fragment)
} }
} }
@ -165,7 +165,7 @@ castable! {
} }
/// Performs grid layout. /// Performs grid layout.
struct GridLayouter<'a, 'v> { pub struct GridLayouter<'a, 'v> {
/// The core context. /// The core context.
vt: &'a mut Vt<'v>, vt: &'a mut Vt<'v>,
/// The grid cells. /// The grid cells.
@ -184,6 +184,8 @@ struct GridLayouter<'a, 'v> {
styles: StyleChain<'a>, styles: StyleChain<'a>,
/// Resolved column sizes. /// Resolved column sizes.
rcols: Vec<Abs>, rcols: Vec<Abs>,
/// Resolve row sizes, by region.
rrows: Vec<Vec<RowPiece>>,
/// Rows in the current region. /// Rows in the current region.
lrows: Vec<Row>, lrows: Vec<Row>,
/// The used-up size of the current region. The horizontal size is /// The used-up size of the current region. The horizontal size is
@ -195,11 +197,31 @@ struct GridLayouter<'a, 'v> {
finished: Vec<Frame>, finished: Vec<Frame>,
} }
/// The resulting sizes of columns and rows in a grid.
#[derive(Debug)]
pub struct GridLayout {
/// The fragment.
pub fragment: Fragment,
/// The column widths.
pub cols: Vec<Abs>,
/// The heights of the resulting rows segments, by region.
pub rows: Vec<Vec<RowPiece>>,
}
/// Details about a resulting row piece.
#[derive(Debug)]
pub struct RowPiece {
/// The height of the segment.
pub height: Abs,
/// The index of the row.
pub y: usize,
}
/// Produced by initial row layout, auto and relative rows are already finished, /// Produced by initial row layout, auto and relative rows are already finished,
/// fractional rows not yet. /// fractional rows not yet.
enum Row { enum Row {
/// Finished row frame of auto or relative row. /// Finished row frame of auto or relative row with y index.
Frame(Frame), Frame(Frame, usize),
/// Fractional row with y index. /// Fractional row with y index.
Fr(Fr, usize), Fr(Fr, usize),
} }
@ -208,7 +230,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
/// Create a new grid layouter. /// Create a new grid layouter.
/// ///
/// This prepares grid layout by unifying content and gutter tracks. /// This prepares grid layout by unifying content and gutter tracks.
fn new( pub fn new(
vt: &'a mut Vt<'v>, vt: &'a mut Vt<'v>,
tracks: Axes<&[Sizing]>, tracks: Axes<&[Sizing]>,
gutter: Axes<&[Sizing]>, gutter: Axes<&[Sizing]>,
@ -284,6 +306,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
regions, regions,
styles, styles,
rcols, rcols,
rrows: vec![],
lrows, lrows,
used: Size::zero(), used: Size::zero(),
fr: Fr::zero(), fr: Fr::zero(),
@ -292,7 +315,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
} }
/// Determines the columns sizes and then layouts the grid row-by-row. /// Determines the columns sizes and then layouts the grid row-by-row.
fn layout(mut self) -> SourceResult<Fragment> { pub fn layout(mut self) -> SourceResult<GridLayout> {
self.measure_columns()?; self.measure_columns()?;
for y in 0..self.rows.len() { for y in 0..self.rows.len() {
@ -313,7 +336,12 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
} }
self.finish_region()?; self.finish_region()?;
Ok(Fragment::frames(self.finished))
Ok(GridLayout {
fragment: Fragment::frames(self.finished),
cols: self.rcols,
rows: self.rrows,
})
} }
/// Determine all column sizes. /// Determine all column sizes.
@ -485,7 +513,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
// Layout into a single region. // Layout into a single region.
if let &[first] = resolved.as_slice() { if let &[first] = resolved.as_slice() {
let frame = self.layout_single_row(first, y)?; let frame = self.layout_single_row(first, y)?;
self.push_row(frame); self.push_row(frame, y);
return Ok(()); return Ok(());
} }
@ -508,7 +536,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
let fragment = self.layout_multi_row(&resolved, y)?; let fragment = self.layout_multi_row(&resolved, y)?;
let len = fragment.len(); let len = fragment.len();
for (i, frame) in fragment.into_iter().enumerate() { for (i, frame) in fragment.into_iter().enumerate() {
self.push_row(frame); self.push_row(frame, y);
if i + 1 < len { if i + 1 < len {
self.finish_region()?; self.finish_region()?;
} }
@ -534,7 +562,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
} }
} }
self.push_row(frame); self.push_row(frame, y);
Ok(()) Ok(())
} }
@ -595,10 +623,10 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
} }
/// Push a row frame into the current region. /// Push a row frame into the current region.
fn push_row(&mut self, frame: Frame) { fn push_row(&mut self, frame: Frame, y: usize) {
self.regions.size.y -= frame.height(); self.regions.size.y -= frame.height();
self.used.y += frame.height(); self.used.y += frame.height();
self.lrows.push(Row::Frame(frame)); self.lrows.push(Row::Frame(frame, y));
} }
/// Finish rows for one region. /// Finish rows for one region.
@ -613,25 +641,28 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
// The frame for the region. // The frame for the region.
let mut output = Frame::new(size); let mut output = Frame::new(size);
let mut pos = Point::zero(); let mut pos = Point::zero();
let mut rrows = vec![];
// Place finished rows and layout fractional rows. // Place finished rows and layout fractional rows.
for row in std::mem::take(&mut self.lrows) { for row in std::mem::take(&mut self.lrows) {
let frame = match row { let (frame, y) = match row {
Row::Frame(frame) => frame, Row::Frame(frame, y) => (frame, y),
Row::Fr(v, y) => { Row::Fr(v, y) => {
let remaining = self.regions.full - self.used.y; let remaining = self.regions.full - self.used.y;
let height = v.share(self.fr, remaining); let height = v.share(self.fr, remaining);
self.layout_single_row(height, y)? (self.layout_single_row(height, y)?, y)
} }
}; };
let height = frame.height(); let height = frame.height();
output.push_frame(pos, frame); output.push_frame(pos, frame);
rrows.push(RowPiece { height, y });
pos.y += height; pos.y += height;
} }
self.finished.push(output); self.finished.push(output);
self.regions.next(); self.regions.next();
self.rrows.push(rrows);
self.used.y = Abs::zero(); self.used.y = Abs::zero();
self.fr = Fr::zero(); self.fr = Fr::zero();

View File

@ -1,4 +1,4 @@
use crate::layout::{AlignNode, GridNode, Sizing, TrackSizings}; use crate::layout::{AlignNode, GridLayouter, Sizing, TrackSizings};
use crate::prelude::*; use crate::prelude::*;
/// # Table /// # Table
@ -153,13 +153,11 @@ impl Layout for TableNode {
styles: StyleChain, styles: StyleChain,
regions: Regions, regions: Regions,
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
let fill = styles.get(Self::FILL);
let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);
let inset = styles.get(Self::INSET); let inset = styles.get(Self::INSET);
let align = styles.get(Self::ALIGN); let align = styles.get(Self::ALIGN);
let cols = self.tracks.x.len().max(1); let cols = self.tracks.x.len().max(1);
let cells = self let cells: Vec<_> = self
.cells .cells
.iter() .iter()
.cloned() .cloned()
@ -173,27 +171,81 @@ impl Layout for TableNode {
child = child.styled(AlignNode::ALIGNS, alignment) child = child.styled(AlignNode::ALIGNS, alignment)
} }
if let Some(stroke) = stroke {
child = child.stroked(stroke);
}
if let Some(fill) = fill.resolve(vt, x, y)? {
child = child.filled(fill);
}
Ok(child) Ok(child)
}) })
.collect::<SourceResult<_>>()?; .collect::<SourceResult<_>>()?;
GridNode { let fill = styles.get(Self::FILL);
tracks: self.tracks.clone(), let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);
gutter: self.gutter.clone(),
cells, // Prepare grid layout by unifying content and gutter tracks.
let layouter = GridLayouter::new(
vt,
self.tracks.as_deref(),
self.gutter.as_deref(),
&cells,
regions,
styles,
);
// Measure the columns and layout the grid row-by-row.
let mut layout = layouter.layout()?;
// Add lines and backgrounds.
for (frame, rows) in layout.fragment.iter_mut().zip(&layout.rows) {
// Render table lines.
if let Some(stroke) = stroke {
let thickness = stroke.thickness;
let half = thickness / 2.0;
// Render horizontal lines.
for offset in points(rows.iter().map(|piece| piece.height)) {
let target = Point::with_x(frame.width() + thickness);
let hline = Geometry::Line(target).stroked(stroke);
frame.prepend(Point::new(-half, offset), Element::Shape(hline));
} }
.layout(vt, styles, regions)
// Render vertical lines.
for offset in points(layout.cols.iter().copied()) {
let target = Point::with_y(frame.height() + thickness);
let vline = Geometry::Line(target).stroked(stroke);
frame.prepend(Point::new(offset, -half), Element::Shape(vline));
} }
} }
// Render cell backgrounds.
let mut dx = Abs::zero();
for (x, &col) in layout.cols.iter().enumerate() {
let mut dy = Abs::zero();
for row in rows {
if let Some(fill) = fill.resolve(vt, x, row.y)? {
let pos = Point::new(dx, dy);
let size = Size::new(col, row.height);
let rect = Geometry::Rect(size).filled(fill);
frame.prepend(pos, Element::Shape(rect));
}
dy += row.height;
}
dx += col;
}
}
Ok(layout.fragment)
}
}
/// Turn an iterator extents into an iterator of offsets before, in between, and
/// after the extents, e.g. [10mm, 5mm] -> [0mm, 10mm, 15mm].
fn points(extents: impl IntoIterator<Item = Abs>) -> impl Iterator<Item = Abs> {
let mut offset = Abs::zero();
std::iter::once(Abs::zero())
.chain(extents.into_iter())
.map(move |extent| {
offset += extent;
offset
})
}
/// A value that can be configured per cell. /// A value that can be configured per cell.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq, Hash)]
pub enum Celled<T> { pub enum Celled<T> {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 928 B

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -6,4 +6,4 @@
--- ---
#set text(dir: rtl) #set text(dir: rtl)
#table(columns: 2, gutter: 5pt)[A][B][C][D] #table(columns: 2)[A][B][C][D]