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.
layouter.layout()
Ok(layouter.layout()?.fragment)
}
}
@ -165,7 +165,7 @@ castable! {
}
/// Performs grid layout.
struct GridLayouter<'a, 'v> {
pub struct GridLayouter<'a, 'v> {
/// The core context.
vt: &'a mut Vt<'v>,
/// The grid cells.
@ -184,6 +184,8 @@ struct GridLayouter<'a, 'v> {
styles: StyleChain<'a>,
/// Resolved column sizes.
rcols: Vec<Abs>,
/// Resolve row sizes, by region.
rrows: Vec<Vec<RowPiece>>,
/// Rows in the current region.
lrows: Vec<Row>,
/// The used-up size of the current region. The horizontal size is
@ -195,11 +197,31 @@ struct GridLayouter<'a, 'v> {
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,
/// fractional rows not yet.
enum Row {
/// Finished row frame of auto or relative row.
Frame(Frame),
/// Finished row frame of auto or relative row with y index.
Frame(Frame, usize),
/// Fractional row with y index.
Fr(Fr, usize),
}
@ -208,7 +230,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
/// Create a new grid layouter.
///
/// This prepares grid layout by unifying content and gutter tracks.
fn new(
pub fn new(
vt: &'a mut Vt<'v>,
tracks: Axes<&[Sizing]>,
gutter: Axes<&[Sizing]>,
@ -284,6 +306,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
regions,
styles,
rcols,
rrows: vec![],
lrows,
used: Size::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.
fn layout(mut self) -> SourceResult<Fragment> {
pub fn layout(mut self) -> SourceResult<GridLayout> {
self.measure_columns()?;
for y in 0..self.rows.len() {
@ -313,7 +336,12 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
}
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.
@ -485,7 +513,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
// Layout into a single region.
if let &[first] = resolved.as_slice() {
let frame = self.layout_single_row(first, y)?;
self.push_row(frame);
self.push_row(frame, y);
return Ok(());
}
@ -508,7 +536,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
let fragment = self.layout_multi_row(&resolved, y)?;
let len = fragment.len();
for (i, frame) in fragment.into_iter().enumerate() {
self.push_row(frame);
self.push_row(frame, y);
if i + 1 < len {
self.finish_region()?;
}
@ -534,7 +562,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
}
}
self.push_row(frame);
self.push_row(frame, y);
Ok(())
}
@ -595,10 +623,10 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
}
/// 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.used.y += frame.height();
self.lrows.push(Row::Frame(frame));
self.lrows.push(Row::Frame(frame, y));
}
/// Finish rows for one region.
@ -613,25 +641,28 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
// The frame for the region.
let mut output = Frame::new(size);
let mut pos = Point::zero();
let mut rrows = vec![];
// Place finished rows and layout fractional rows.
for row in std::mem::take(&mut self.lrows) {
let frame = match row {
Row::Frame(frame) => frame,
let (frame, y) = match row {
Row::Frame(frame, y) => (frame, y),
Row::Fr(v, y) => {
let remaining = self.regions.full - self.used.y;
let height = v.share(self.fr, remaining);
self.layout_single_row(height, y)?
(self.layout_single_row(height, y)?, y)
}
};
let height = frame.height();
output.push_frame(pos, frame);
rrows.push(RowPiece { height, y });
pos.y += height;
}
self.finished.push(output);
self.regions.next();
self.rrows.push(rrows);
self.used.y = Abs::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::*;
/// # Table
@ -153,13 +153,11 @@ impl Layout for TableNode {
styles: StyleChain,
regions: Regions,
) -> 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 align = styles.get(Self::ALIGN);
let cols = self.tracks.x.len().max(1);
let cells = self
let cells: Vec<_> = self
.cells
.iter()
.cloned()
@ -173,27 +171,81 @@ impl Layout for TableNode {
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)
})
.collect::<SourceResult<_>>()?;
GridNode {
tracks: self.tracks.clone(),
gutter: self.gutter.clone(),
cells,
let fill = styles.get(Self::FILL);
let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);
// 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));
}
// 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;
}
}
.layout(vt, styles, regions)
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.
#[derive(Debug, Clone, PartialEq, Hash)]
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)
#table(columns: 2, gutter: 5pt)[A][B][C][D]
#table(columns: 2)[A][B][C][D]