mirror of
https://github.com/typst/typst
synced 2025-08-20 09:49:02 +08:00
Move grid cell locator creation to GridLayouter (#6746)
This commit is contained in:
parent
a7c8fd6872
commit
343a57b50d
@ -5,7 +5,7 @@ use typst_library::diag::{At, bail, warning};
|
|||||||
use typst_library::foundations::{
|
use typst_library::foundations::{
|
||||||
Content, NativeElement, NativeRuleMap, ShowFn, Smart, StyleChain, Target,
|
Content, NativeElement, NativeRuleMap, ShowFn, Smart, StyleChain, Target,
|
||||||
};
|
};
|
||||||
use typst_library::introspection::{Counter, Locator};
|
use typst_library::introspection::Counter;
|
||||||
use typst_library::layout::resolve::{Cell, CellGrid, Entry, table_to_cellgrid};
|
use typst_library::layout::resolve::{Cell, CellGrid, Entry, table_to_cellgrid};
|
||||||
use typst_library::layout::{BlockBody, BlockElem, BoxElem, OuterVAlignment, Sizing};
|
use typst_library::layout::{BlockBody, BlockElem, BoxElem, OuterVAlignment, Sizing};
|
||||||
use typst_library::model::{
|
use typst_library::model::{
|
||||||
@ -278,9 +278,7 @@ const REF_RULE: ShowFn<RefElem> = |elem, engine, styles| elem.realize(engine, st
|
|||||||
const CITE_GROUP_RULE: ShowFn<CiteGroup> = |elem, engine, _| elem.realize(engine);
|
const CITE_GROUP_RULE: ShowFn<CiteGroup> = |elem, engine, _| elem.realize(engine);
|
||||||
|
|
||||||
const TABLE_RULE: ShowFn<TableElem> = |elem, engine, styles| {
|
const TABLE_RULE: ShowFn<TableElem> = |elem, engine, styles| {
|
||||||
// The locator is not used by HTML export, so we can just fabricate one.
|
Ok(show_cellgrid(table_to_cellgrid(elem, engine, styles)?, styles))
|
||||||
let locator = Locator::root();
|
|
||||||
Ok(show_cellgrid(table_to_cellgrid(elem, engine, locator, styles)?, styles))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn show_cellgrid(grid: CellGrid, styles: StyleChain) -> Content {
|
fn show_cellgrid(grid: CellGrid, styles: StyleChain) -> Content {
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use typst_library::diag::{SourceResult, bail};
|
use typst_library::diag::{SourceResult, bail};
|
||||||
use typst_library::engine::Engine;
|
use typst_library::engine::Engine;
|
||||||
use typst_library::foundations::{Resolve, StyleChain};
|
use typst_library::foundations::{Resolve, StyleChain};
|
||||||
|
use typst_library::introspection::Locator;
|
||||||
use typst_library::layout::grid::resolve::{
|
use typst_library::layout::grid::resolve::{
|
||||||
Cell, CellGrid, Header, LinePosition, Repeatable,
|
Cell, CellGrid, Header, LinePosition, Repeatable,
|
||||||
};
|
};
|
||||||
|
use typst_library::layout::resolve::Entry;
|
||||||
use typst_library::layout::{
|
use typst_library::layout::{
|
||||||
Abs, Axes, Dir, Fr, Fragment, Frame, FrameItem, Length, Point, Region, Regions, Rel,
|
Abs, Axes, Dir, Fr, Fragment, Frame, FrameItem, Length, Point, Region, Regions, Rel,
|
||||||
Size, Sizing,
|
Size, Sizing,
|
||||||
@ -23,9 +26,11 @@ use super::{
|
|||||||
/// Performs grid layout.
|
/// Performs grid layout.
|
||||||
pub struct GridLayouter<'a> {
|
pub struct GridLayouter<'a> {
|
||||||
/// The grid of cells.
|
/// The grid of cells.
|
||||||
pub(super) grid: &'a CellGrid<'a>,
|
pub(super) grid: &'a CellGrid,
|
||||||
/// The regions to layout children into.
|
/// The regions to layout children into.
|
||||||
pub(super) regions: Regions<'a>,
|
pub(super) regions: Regions<'a>,
|
||||||
|
/// The locators for the each cell in the cell grid.
|
||||||
|
pub(super) cell_locators: FxHashMap<Axes<usize>, Locator<'a>>,
|
||||||
/// The inherited styles.
|
/// The inherited styles.
|
||||||
pub(super) styles: StyleChain<'a>,
|
pub(super) styles: StyleChain<'a>,
|
||||||
/// Resolved column sizes.
|
/// Resolved column sizes.
|
||||||
@ -228,8 +233,9 @@ impl<'a> GridLayouter<'a> {
|
|||||||
///
|
///
|
||||||
/// This prepares grid layout by unifying content and gutter tracks.
|
/// This prepares grid layout by unifying content and gutter tracks.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
grid: &'a CellGrid<'a>,
|
grid: &'a CellGrid,
|
||||||
regions: Regions<'a>,
|
regions: Regions<'a>,
|
||||||
|
locator: Locator<'a>,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -238,9 +244,22 @@ impl<'a> GridLayouter<'a> {
|
|||||||
let mut regions = regions;
|
let mut regions = regions;
|
||||||
regions.expand = Axes::new(true, false);
|
regions.expand = Axes::new(true, false);
|
||||||
|
|
||||||
|
// Prepare the locators for each cell in the cell grid.
|
||||||
|
let mut locator = locator.split();
|
||||||
|
let mut cell_locators = FxHashMap::default();
|
||||||
|
for y in 0..grid.rows.len() {
|
||||||
|
for x in 0..grid.cols.len() {
|
||||||
|
let Some(Entry::Cell(cell)) = grid.entry(x, y) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
cell_locators.insert(Axes::new(x, y), locator.next(&cell.body.span()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
grid,
|
grid,
|
||||||
regions,
|
regions,
|
||||||
|
cell_locators,
|
||||||
styles,
|
styles,
|
||||||
rcols: vec![Abs::zero(); grid.cols.len()],
|
rcols: vec![Abs::zero(); grid.cols.len()],
|
||||||
width: Abs::zero(),
|
width: Abs::zero(),
|
||||||
@ -270,6 +289,22 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a [`Locator`] for use in [`layout_cell`].
|
||||||
|
pub(super) fn cell_locator(
|
||||||
|
&self,
|
||||||
|
pos: Axes<usize>,
|
||||||
|
disambiguator: usize,
|
||||||
|
) -> Locator<'a> {
|
||||||
|
let mut cell_locator = self.cell_locators[&pos].relayout();
|
||||||
|
|
||||||
|
// The disambiguator is used for repeated cells, e.g. in repeated headers.
|
||||||
|
if disambiguator > 0 {
|
||||||
|
cell_locator = cell_locator.split().next_inner(disambiguator as u128);
|
||||||
|
}
|
||||||
|
|
||||||
|
cell_locator
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines the columns sizes and then layouts the grid row-by-row.
|
/// Determines the columns sizes and then layouts the grid row-by-row.
|
||||||
pub fn layout(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
|
pub fn layout(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
|
||||||
self.measure_columns(engine)?;
|
self.measure_columns(engine)?;
|
||||||
@ -1037,8 +1072,9 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
let size = Size::new(available, height);
|
let size = Size::new(available, height);
|
||||||
let pod = Region::new(size, Axes::splat(false));
|
let pod = Region::new(size, Axes::splat(false));
|
||||||
let frame =
|
let locator = self.cell_locator(parent, 0);
|
||||||
layout_cell(cell, engine, 0, self.styles, pod.into())?.into_frame();
|
let frame = layout_cell(cell, engine, locator, self.styles, pod.into())?
|
||||||
|
.into_frame();
|
||||||
resolved.set_max(frame.width() - already_covered_width);
|
resolved.set_max(frame.width() - already_covered_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1276,8 +1312,9 @@ impl<'a> GridLayouter<'a> {
|
|||||||
pod
|
pod
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let locator = self.cell_locator(parent, disambiguator);
|
||||||
let frames =
|
let frames =
|
||||||
layout_cell(cell, engine, disambiguator, self.styles, pod)?.into_frames();
|
layout_cell(cell, engine, locator, self.styles, pod)?.into_frames();
|
||||||
|
|
||||||
// Skip the first region if one cell in it is empty. Then,
|
// Skip the first region if one cell in it is empty. Then,
|
||||||
// remeasure.
|
// remeasure.
|
||||||
@ -1434,8 +1471,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// rows.
|
// rows.
|
||||||
pod.full = self.regions.full;
|
pod.full = self.regions.full;
|
||||||
}
|
}
|
||||||
let frame =
|
let locator = self.cell_locator(Axes::new(x, y), disambiguator);
|
||||||
layout_cell(cell, engine, disambiguator, self.styles, pod)?
|
let frame = layout_cell(cell, engine, locator, self.styles, pod)?
|
||||||
.into_frame();
|
.into_frame();
|
||||||
let mut pos = offset;
|
let mut pos = offset;
|
||||||
if self.is_rtl {
|
if self.is_rtl {
|
||||||
@ -1483,8 +1520,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
pod.size.x = width;
|
pod.size.x = width;
|
||||||
|
|
||||||
// Push the layouted frames into the individual output frames.
|
// Push the layouted frames into the individual output frames.
|
||||||
let fragment =
|
let locator = self.cell_locator(Axes::new(x, y), disambiguator);
|
||||||
layout_cell(cell, engine, disambiguator, self.styles, pod)?;
|
let fragment = layout_cell(cell, engine, locator, self.styles, pod)?;
|
||||||
for (output, frame) in outputs.iter_mut().zip(fragment) {
|
for (output, frame) in outputs.iter_mut().zip(fragment) {
|
||||||
let mut pos = offset;
|
let mut pos = offset;
|
||||||
if self.is_rtl {
|
if self.is_rtl {
|
||||||
|
@ -560,17 +560,15 @@ pub fn hline_stroke_at_column(
|
|||||||
mod test {
|
mod test {
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use typst_library::foundations::Content;
|
use typst_library::foundations::Content;
|
||||||
use typst_library::introspection::Locator;
|
|
||||||
use typst_library::layout::grid::resolve::{Cell, Entry, LinePosition};
|
use typst_library::layout::grid::resolve::{Cell, Entry, LinePosition};
|
||||||
use typst_library::layout::{Axes, Sides, Sizing};
|
use typst_library::layout::{Axes, Sides, Sizing};
|
||||||
use typst_utils::NonZeroExt;
|
use typst_utils::NonZeroExt;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn sample_cell() -> Cell<'static> {
|
fn sample_cell() -> Cell {
|
||||||
Cell {
|
Cell {
|
||||||
body: Content::default(),
|
body: Content::default(),
|
||||||
locator: Locator::root(),
|
|
||||||
fill: None,
|
fill: None,
|
||||||
colspan: NonZeroUsize::ONE,
|
colspan: NonZeroUsize::ONE,
|
||||||
rowspan: NonZeroUsize::ONE,
|
rowspan: NonZeroUsize::ONE,
|
||||||
@ -580,10 +578,9 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cell_with_colspan_rowspan(colspan: usize, rowspan: usize) -> Cell<'static> {
|
fn cell_with_colspan_rowspan(colspan: usize, rowspan: usize) -> Cell {
|
||||||
Cell {
|
Cell {
|
||||||
body: Content::default(),
|
body: Content::default(),
|
||||||
locator: Locator::root(),
|
|
||||||
fill: None,
|
fill: None,
|
||||||
colspan: NonZeroUsize::try_from(colspan).unwrap(),
|
colspan: NonZeroUsize::try_from(colspan).unwrap(),
|
||||||
rowspan: NonZeroUsize::try_from(rowspan).unwrap(),
|
rowspan: NonZeroUsize::try_from(rowspan).unwrap(),
|
||||||
@ -593,7 +590,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_grid_for_vlines(gutters: bool) -> CellGrid<'static> {
|
fn sample_grid_for_vlines(gutters: bool) -> CellGrid {
|
||||||
const COLS: usize = 4;
|
const COLS: usize = 4;
|
||||||
const ROWS: usize = 6;
|
const ROWS: usize = 6;
|
||||||
let entries = vec![
|
let entries = vec![
|
||||||
@ -1116,7 +1113,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_grid_for_hlines(gutters: bool) -> CellGrid<'static> {
|
fn sample_grid_for_hlines(gutters: bool) -> CellGrid {
|
||||||
const COLS: usize = 4;
|
const COLS: usize = 4;
|
||||||
const ROWS: usize = 9;
|
const ROWS: usize = 9;
|
||||||
let entries = vec![
|
let entries = vec![
|
||||||
|
@ -28,14 +28,10 @@ use self::rowspans::{Rowspan, UnbreakableRowGroup};
|
|||||||
pub fn layout_cell(
|
pub fn layout_cell(
|
||||||
cell: &Cell,
|
cell: &Cell,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
disambiguator: usize,
|
locator: Locator,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut locator = cell.locator.relayout();
|
|
||||||
if disambiguator > 0 {
|
|
||||||
locator = locator.split().next_inner(disambiguator as u128);
|
|
||||||
}
|
|
||||||
crate::layout_fragment(engine, &cell.body, locator, styles, regions)
|
crate::layout_fragment(engine, &cell.body, locator, styles, regions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +44,8 @@ pub fn layout_grid(
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let grid = grid_to_cellgrid(elem, engine, locator, styles)?;
|
let grid = grid_to_cellgrid(elem, engine, styles)?;
|
||||||
GridLayouter::new(&grid, regions, styles, elem.span()).layout(engine)
|
GridLayouter::new(&grid, regions, locator, styles, elem.span()).layout(engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout the table.
|
/// Layout the table.
|
||||||
@ -61,6 +57,6 @@ pub fn layout_table(
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let grid = table_to_cellgrid(elem, engine, locator, styles)?;
|
let grid = table_to_cellgrid(elem, engine, styles)?;
|
||||||
GridLayouter::new(&grid, regions, styles, elem.span()).layout(engine)
|
GridLayouter::new(&grid, regions, locator, styles, elem.span()).layout(engine)
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,8 @@ impl GridLayouter<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push the layouted frames directly into the finished frames.
|
// Push the layouted frames directly into the finished frames.
|
||||||
let fragment = layout_cell(cell, engine, disambiguator, self.styles, pod)?;
|
let locator = self.cell_locator(Axes::new(x, y), disambiguator);
|
||||||
|
let fragment = layout_cell(cell, engine, locator, self.styles, pod)?;
|
||||||
let (current_region, current_header_row_height) = current_region_data.unzip();
|
let (current_region, current_header_row_height) = current_region_data.unzip();
|
||||||
|
|
||||||
// Clever trick to process finished header rows:
|
// Clever trick to process finished header rows:
|
||||||
|
@ -36,8 +36,6 @@ pub fn layout_list(
|
|||||||
.aligned(HAlignment::Start + VAlignment::Top);
|
.aligned(HAlignment::Start + VAlignment::Top);
|
||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
let mut locator = locator.split();
|
|
||||||
|
|
||||||
for item in &elem.children {
|
for item in &elem.children {
|
||||||
// Text in wide lists shall always turn into paragraphs.
|
// Text in wide lists shall always turn into paragraphs.
|
||||||
let mut body = item.body.clone();
|
let mut body = item.body.clone();
|
||||||
@ -45,13 +43,10 @@ pub fn layout_list(
|
|||||||
body += ParbreakElem::shared();
|
body += ParbreakElem::shared();
|
||||||
}
|
}
|
||||||
|
|
||||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
cells.push(Cell::new(Content::empty()));
|
||||||
cells.push(Cell::new(marker.clone(), locator.next(&marker.span())));
|
cells.push(Cell::new(marker.clone()));
|
||||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
cells.push(Cell::new(Content::empty()));
|
||||||
cells.push(Cell::new(
|
cells.push(Cell::new(body.set(ListElem::depth, Depth(1))));
|
||||||
body.set(ListElem::depth, Depth(1)),
|
|
||||||
locator.next(&item.body.span()),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let grid = CellGrid::new(
|
let grid = CellGrid::new(
|
||||||
@ -64,7 +59,7 @@ pub fn layout_list(
|
|||||||
Axes::with_y(&[gutter.into()]),
|
Axes::with_y(&[gutter.into()]),
|
||||||
cells,
|
cells,
|
||||||
);
|
);
|
||||||
let layouter = GridLayouter::new(&grid, regions, styles, elem.span());
|
let layouter = GridLayouter::new(&grid, regions, locator, styles, elem.span());
|
||||||
|
|
||||||
layouter.layout(engine)
|
layouter.layout(engine)
|
||||||
}
|
}
|
||||||
@ -88,7 +83,6 @@ pub fn layout_enum(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
let mut locator = locator.split();
|
|
||||||
let mut number = elem
|
let mut number = elem
|
||||||
.start
|
.start
|
||||||
.get(styles)
|
.get(styles)
|
||||||
@ -131,13 +125,10 @@ pub fn layout_enum(
|
|||||||
body += ParbreakElem::shared();
|
body += ParbreakElem::shared();
|
||||||
}
|
}
|
||||||
|
|
||||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
cells.push(Cell::new(Content::empty()));
|
||||||
cells.push(Cell::new(resolved, locator.next(&())));
|
cells.push(Cell::new(resolved));
|
||||||
cells.push(Cell::new(Content::empty(), locator.next(&())));
|
cells.push(Cell::new(Content::empty()));
|
||||||
cells.push(Cell::new(
|
cells.push(Cell::new(body.set(EnumElem::parents, smallvec![number])));
|
||||||
body.set(EnumElem::parents, smallvec![number]),
|
|
||||||
locator.next(&item.body.span()),
|
|
||||||
));
|
|
||||||
number =
|
number =
|
||||||
if reversed { number.saturating_sub(1) } else { number.saturating_add(1) };
|
if reversed { number.saturating_sub(1) } else { number.saturating_add(1) };
|
||||||
}
|
}
|
||||||
@ -152,7 +143,7 @@ pub fn layout_enum(
|
|||||||
Axes::with_y(&[gutter.into()]),
|
Axes::with_y(&[gutter.into()]),
|
||||||
cells,
|
cells,
|
||||||
);
|
);
|
||||||
let layouter = GridLayouter::new(&grid, regions, styles, elem.span());
|
let layouter = GridLayouter::new(&grid, regions, locator, styles, elem.span());
|
||||||
|
|
||||||
layouter.layout(engine)
|
layouter.layout(engine)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ use typst_library::diag::{
|
|||||||
};
|
};
|
||||||
use typst_library::engine::Engine;
|
use typst_library::engine::Engine;
|
||||||
use typst_library::foundations::{Content, Fold, Packed, Smart, StyleChain};
|
use typst_library::foundations::{Content, Fold, Packed, Smart, StyleChain};
|
||||||
use typst_library::introspection::Locator;
|
|
||||||
use typst_library::layout::{
|
use typst_library::layout::{
|
||||||
Abs, Alignment, Axes, Celled, GridCell, GridChild, GridElem, GridItem, Length,
|
Abs, Alignment, Axes, Celled, GridCell, GridChild, GridElem, GridItem, Length,
|
||||||
OuterHAlignment, OuterVAlignment, Rel, ResolvedCelled, Sides, Sizing,
|
OuterHAlignment, OuterVAlignment, Rel, ResolvedCelled, Sides, Sizing,
|
||||||
@ -21,16 +20,13 @@ use typst_library::visualize::{Paint, Stroke};
|
|||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
use typst_utils::{NonZeroExt, SmallBitSet};
|
use typst_utils::{NonZeroExt, SmallBitSet};
|
||||||
|
|
||||||
use crate::introspection::SplitLocator;
|
|
||||||
|
|
||||||
/// Convert a grid to a cell grid.
|
/// Convert a grid to a cell grid.
|
||||||
#[typst_macros::time(span = elem.span())]
|
#[typst_macros::time(span = elem.span())]
|
||||||
pub fn grid_to_cellgrid<'a>(
|
pub fn grid_to_cellgrid(
|
||||||
elem: &Packed<GridElem>,
|
elem: &Packed<GridElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
locator: Locator<'a>,
|
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<CellGrid<'a>> {
|
) -> SourceResult<CellGrid> {
|
||||||
let inset = elem.inset.get_cloned(styles);
|
let inset = elem.inset.get_cloned(styles);
|
||||||
let align = elem.align.get_ref(styles);
|
let align = elem.align.get_ref(styles);
|
||||||
let columns = elem.columns.get_ref(styles);
|
let columns = elem.columns.get_ref(styles);
|
||||||
@ -64,7 +60,6 @@ pub fn grid_to_cellgrid<'a>(
|
|||||||
resolve_cellgrid(
|
resolve_cellgrid(
|
||||||
tracks,
|
tracks,
|
||||||
gutter,
|
gutter,
|
||||||
locator,
|
|
||||||
children,
|
children,
|
||||||
fill,
|
fill,
|
||||||
align,
|
align,
|
||||||
@ -79,12 +74,11 @@ pub fn grid_to_cellgrid<'a>(
|
|||||||
|
|
||||||
/// Convert a table to a cell grid.
|
/// Convert a table to a cell grid.
|
||||||
#[typst_macros::time(span = elem.span())]
|
#[typst_macros::time(span = elem.span())]
|
||||||
pub fn table_to_cellgrid<'a>(
|
pub fn table_to_cellgrid(
|
||||||
elem: &Packed<TableElem>,
|
elem: &Packed<TableElem>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
locator: Locator<'a>,
|
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<CellGrid<'a>> {
|
) -> SourceResult<CellGrid> {
|
||||||
let inset = elem.inset.get_cloned(styles);
|
let inset = elem.inset.get_cloned(styles);
|
||||||
let align = elem.align.get_ref(styles);
|
let align = elem.align.get_ref(styles);
|
||||||
let columns = elem.columns.get_ref(styles);
|
let columns = elem.columns.get_ref(styles);
|
||||||
@ -118,7 +112,6 @@ pub fn table_to_cellgrid<'a>(
|
|||||||
resolve_cellgrid(
|
resolve_cellgrid(
|
||||||
tracks,
|
tracks,
|
||||||
gutter,
|
gutter,
|
||||||
locator,
|
|
||||||
children,
|
children,
|
||||||
fill,
|
fill,
|
||||||
align,
|
align,
|
||||||
@ -206,7 +199,7 @@ fn table_item_to_resolvable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResolvableCell for Packed<TableCell> {
|
impl ResolvableCell for Packed<TableCell> {
|
||||||
fn resolve_cell<'a>(
|
fn resolve_cell(
|
||||||
mut self,
|
mut self,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
@ -215,9 +208,8 @@ impl ResolvableCell for Packed<TableCell> {
|
|||||||
inset: Sides<Option<Rel<Length>>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
locator: Locator<'a>,
|
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell<'a> {
|
) -> Cell {
|
||||||
let cell = &mut *self;
|
let cell = &mut *self;
|
||||||
let colspan = cell.colspan.get(styles);
|
let colspan = cell.colspan.get(styles);
|
||||||
let rowspan = cell.rowspan.get(styles);
|
let rowspan = cell.rowspan.get(styles);
|
||||||
@ -269,7 +261,6 @@ impl ResolvableCell for Packed<TableCell> {
|
|||||||
cell.breakable.set(Smart::Custom(breakable));
|
cell.breakable.set(Smart::Custom(breakable));
|
||||||
Cell {
|
Cell {
|
||||||
body: self.pack(),
|
body: self.pack(),
|
||||||
locator,
|
|
||||||
fill,
|
fill,
|
||||||
colspan,
|
colspan,
|
||||||
rowspan,
|
rowspan,
|
||||||
@ -301,7 +292,7 @@ impl ResolvableCell for Packed<TableCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResolvableCell for Packed<GridCell> {
|
impl ResolvableCell for Packed<GridCell> {
|
||||||
fn resolve_cell<'a>(
|
fn resolve_cell(
|
||||||
mut self,
|
mut self,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
@ -310,9 +301,8 @@ impl ResolvableCell for Packed<GridCell> {
|
|||||||
inset: Sides<Option<Rel<Length>>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
locator: Locator<'a>,
|
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell<'a> {
|
) -> Cell {
|
||||||
let cell = &mut *self;
|
let cell = &mut *self;
|
||||||
let colspan = cell.colspan.get(styles);
|
let colspan = cell.colspan.get(styles);
|
||||||
let rowspan = cell.rowspan.get(styles);
|
let rowspan = cell.rowspan.get(styles);
|
||||||
@ -364,7 +354,6 @@ impl ResolvableCell for Packed<GridCell> {
|
|||||||
cell.breakable.set(Smart::Custom(breakable));
|
cell.breakable.set(Smart::Custom(breakable));
|
||||||
Cell {
|
Cell {
|
||||||
body: self.pack(),
|
body: self.pack(),
|
||||||
locator,
|
|
||||||
fill,
|
fill,
|
||||||
colspan,
|
colspan,
|
||||||
rowspan,
|
rowspan,
|
||||||
@ -507,7 +496,7 @@ pub trait ResolvableCell {
|
|||||||
/// the `breakable` field.
|
/// the `breakable` field.
|
||||||
/// Returns a final Cell.
|
/// Returns a final Cell.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn resolve_cell<'a>(
|
fn resolve_cell(
|
||||||
self,
|
self,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
@ -516,9 +505,8 @@ pub trait ResolvableCell {
|
|||||||
inset: Sides<Option<Rel<Length>>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
stroke: Sides<Option<Option<Arc<Stroke<Abs>>>>>,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
locator: Locator<'a>,
|
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell<'a>;
|
) -> Cell;
|
||||||
|
|
||||||
/// Returns this cell's column override.
|
/// Returns this cell's column override.
|
||||||
fn x(&self, styles: StyleChain) -> Smart<usize>;
|
fn x(&self, styles: StyleChain) -> Smart<usize>;
|
||||||
@ -570,11 +558,9 @@ pub enum ResolvableGridItem<T: ResolvableCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a cell in CellGrid, to be laid out by GridLayouter.
|
/// Represents a cell in CellGrid, to be laid out by GridLayouter.
|
||||||
pub struct Cell<'a> {
|
pub struct Cell {
|
||||||
/// The cell's body.
|
/// The cell's body.
|
||||||
pub body: Content,
|
pub body: Content,
|
||||||
/// The cell's locator.
|
|
||||||
pub locator: Locator<'a>,
|
|
||||||
/// The cell's fill.
|
/// The cell's fill.
|
||||||
pub fill: Option<Paint>,
|
pub fill: Option<Paint>,
|
||||||
/// The amount of columns spanned by the cell.
|
/// The amount of columns spanned by the cell.
|
||||||
@ -600,12 +586,11 @@ pub struct Cell<'a> {
|
|||||||
pub breakable: bool,
|
pub breakable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Cell<'a> {
|
impl Cell {
|
||||||
/// Create a simple cell given its body and its locator.
|
/// Create a simple cell given its body.
|
||||||
pub fn new(body: Content, locator: Locator<'a>) -> Self {
|
pub fn new(body: Content) -> Self {
|
||||||
Self {
|
Self {
|
||||||
body,
|
body,
|
||||||
locator,
|
|
||||||
fill: None,
|
fill: None,
|
||||||
colspan: NonZeroUsize::ONE,
|
colspan: NonZeroUsize::ONE,
|
||||||
rowspan: NonZeroUsize::ONE,
|
rowspan: NonZeroUsize::ONE,
|
||||||
@ -629,9 +614,9 @@ pub enum LinePosition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A grid entry.
|
/// A grid entry.
|
||||||
pub enum Entry<'a> {
|
pub enum Entry {
|
||||||
/// An entry which holds a cell.
|
/// An entry which holds a cell.
|
||||||
Cell(Cell<'a>),
|
Cell(Cell),
|
||||||
/// An entry which is merged with another cell.
|
/// An entry which is merged with another cell.
|
||||||
Merged {
|
Merged {
|
||||||
/// The index of the cell this entry is merged with.
|
/// The index of the cell this entry is merged with.
|
||||||
@ -639,9 +624,9 @@ pub enum Entry<'a> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Entry<'a> {
|
impl Entry {
|
||||||
/// Obtains the cell inside this entry, if this is not a merged cell.
|
/// Obtains the cell inside this entry, if this is not a merged cell.
|
||||||
pub fn as_cell(&self) -> Option<&Cell<'a>> {
|
pub fn as_cell(&self) -> Option<&Cell> {
|
||||||
match self {
|
match self {
|
||||||
Self::Cell(cell) => Some(cell),
|
Self::Cell(cell) => Some(cell),
|
||||||
Self::Merged { .. } => None,
|
Self::Merged { .. } => None,
|
||||||
@ -657,9 +642,9 @@ pub enum ResolvableGridChild<T: ResolvableCell, I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A grid of cells, including the columns, rows, and cell data.
|
/// A grid of cells, including the columns, rows, and cell data.
|
||||||
pub struct CellGrid<'a> {
|
pub struct CellGrid {
|
||||||
/// The grid cells.
|
/// The grid cells.
|
||||||
pub entries: Vec<Entry<'a>>,
|
pub entries: Vec<Entry>,
|
||||||
/// The column tracks including gutter tracks.
|
/// The column tracks including gutter tracks.
|
||||||
pub cols: Vec<Sizing>,
|
pub cols: Vec<Sizing>,
|
||||||
/// The row tracks including gutter tracks.
|
/// The row tracks including gutter tracks.
|
||||||
@ -680,12 +665,12 @@ pub struct CellGrid<'a> {
|
|||||||
pub has_gutter: bool,
|
pub has_gutter: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CellGrid<'a> {
|
impl CellGrid {
|
||||||
/// Generates the cell grid, given the tracks and cells.
|
/// Generates the cell grid, given the tracks and cells.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tracks: Axes<&[Sizing]>,
|
tracks: Axes<&[Sizing]>,
|
||||||
gutter: Axes<&[Sizing]>,
|
gutter: Axes<&[Sizing]>,
|
||||||
cells: impl IntoIterator<Item = Cell<'a>>,
|
cells: impl IntoIterator<Item = Cell>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let entries = cells.into_iter().map(Entry::Cell).collect();
|
let entries = cells.into_iter().map(Entry::Cell).collect();
|
||||||
Self::new_internal(tracks, gutter, vec![], vec![], vec![], None, entries)
|
Self::new_internal(tracks, gutter, vec![], vec![], vec![], None, entries)
|
||||||
@ -699,7 +684,7 @@ impl<'a> CellGrid<'a> {
|
|||||||
hlines: Vec<Vec<Line>>,
|
hlines: Vec<Vec<Line>>,
|
||||||
headers: Vec<Repeatable<Header>>,
|
headers: Vec<Repeatable<Header>>,
|
||||||
footer: Option<Repeatable<Footer>>,
|
footer: Option<Repeatable<Footer>>,
|
||||||
entries: Vec<Entry<'a>>,
|
entries: Vec<Entry>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut rows = vec![];
|
let mut rows = vec![];
|
||||||
@ -761,7 +746,7 @@ impl<'a> CellGrid<'a> {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if it's a gutter cell.
|
/// Returns `None` if it's a gutter cell.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn entry(&self, x: usize, y: usize) -> Option<&Entry<'a>> {
|
pub fn entry(&self, x: usize, y: usize) -> Option<&Entry> {
|
||||||
assert!(x < self.cols.len());
|
assert!(x < self.cols.len());
|
||||||
assert!(y < self.rows.len());
|
assert!(y < self.rows.len());
|
||||||
|
|
||||||
@ -783,7 +768,7 @@ impl<'a> CellGrid<'a> {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if it's a gutter cell or merged position.
|
/// Returns `None` if it's a gutter cell or merged position.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn cell(&self, x: usize, y: usize) -> Option<&Cell<'a>> {
|
pub fn cell(&self, x: usize, y: usize) -> Option<&Cell> {
|
||||||
self.entry(x, y).and_then(Entry::as_cell)
|
self.entry(x, y).and_then(Entry::as_cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,10 +877,9 @@ impl<'a> CellGrid<'a> {
|
|||||||
/// must implement Default in order to fill positions in the grid which
|
/// must implement Default in order to fill positions in the grid which
|
||||||
/// weren't explicitly specified by the user with empty cells.
|
/// weren't explicitly specified by the user with empty cells.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn resolve_cellgrid<'a, 'x, T, C, I>(
|
pub fn resolve_cellgrid<'a, T, C, I>(
|
||||||
tracks: Axes<&'a [Sizing]>,
|
tracks: Axes<&'a [Sizing]>,
|
||||||
gutter: Axes<&'a [Sizing]>,
|
gutter: Axes<&'a [Sizing]>,
|
||||||
locator: Locator<'x>,
|
|
||||||
children: C,
|
children: C,
|
||||||
fill: &'a Celled<Option<Paint>>,
|
fill: &'a Celled<Option<Paint>>,
|
||||||
align: &'a Celled<Smart<Alignment>>,
|
align: &'a Celled<Smart<Alignment>>,
|
||||||
@ -904,7 +888,7 @@ pub fn resolve_cellgrid<'a, 'x, T, C, I>(
|
|||||||
engine: &'a mut Engine,
|
engine: &'a mut Engine,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> SourceResult<CellGrid<'x>>
|
) -> SourceResult<CellGrid>
|
||||||
where
|
where
|
||||||
T: ResolvableCell + Default,
|
T: ResolvableCell + Default,
|
||||||
I: Iterator<Item = ResolvableGridItem<T>>,
|
I: Iterator<Item = ResolvableGridItem<T>>,
|
||||||
@ -914,7 +898,6 @@ where
|
|||||||
CellGridResolver {
|
CellGridResolver {
|
||||||
tracks,
|
tracks,
|
||||||
gutter,
|
gutter,
|
||||||
locator: locator.split(),
|
|
||||||
fill,
|
fill,
|
||||||
align,
|
align,
|
||||||
inset,
|
inset,
|
||||||
@ -926,10 +909,9 @@ where
|
|||||||
.resolve(children)
|
.resolve(children)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CellGridResolver<'a, 'b, 'x> {
|
struct CellGridResolver<'a, 'b> {
|
||||||
tracks: Axes<&'a [Sizing]>,
|
tracks: Axes<&'a [Sizing]>,
|
||||||
gutter: Axes<&'a [Sizing]>,
|
gutter: Axes<&'a [Sizing]>,
|
||||||
locator: SplitLocator<'x>,
|
|
||||||
fill: &'a Celled<Option<Paint>>,
|
fill: &'a Celled<Option<Paint>>,
|
||||||
align: &'a Celled<Smart<Alignment>>,
|
align: &'a Celled<Smart<Alignment>>,
|
||||||
inset: &'a Celled<Sides<Option<Rel<Length>>>>,
|
inset: &'a Celled<Sides<Option<Rel<Length>>>>,
|
||||||
@ -996,8 +978,8 @@ struct RowGroupData {
|
|||||||
top_hlines_end: Option<usize>,
|
top_hlines_end: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'x> CellGridResolver<'_, '_, 'x> {
|
impl CellGridResolver<'_, '_> {
|
||||||
fn resolve<T, C, I>(mut self, children: C) -> SourceResult<CellGrid<'x>>
|
fn resolve<T, C, I>(mut self, children: C) -> SourceResult<CellGrid>
|
||||||
where
|
where
|
||||||
T: ResolvableCell + Default,
|
T: ResolvableCell + Default,
|
||||||
I: Iterator<Item = ResolvableGridItem<T>>,
|
I: Iterator<Item = ResolvableGridItem<T>>,
|
||||||
@ -1138,7 +1120,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
footer: &mut Option<(usize, Span, Footer)>,
|
footer: &mut Option<(usize, Span, Footer)>,
|
||||||
repeat_footer: &mut bool,
|
repeat_footer: &mut bool,
|
||||||
auto_index: &mut usize,
|
auto_index: &mut usize,
|
||||||
resolved_cells: &mut Vec<Option<Entry<'x>>>,
|
resolved_cells: &mut Vec<Option<Entry>>,
|
||||||
at_least_one_cell: &mut bool,
|
at_least_one_cell: &mut bool,
|
||||||
child: ResolvableGridChild<T, I>,
|
child: ResolvableGridChild<T, I>,
|
||||||
) -> SourceResult<()>
|
) -> SourceResult<()>
|
||||||
@ -1441,7 +1423,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
|
|
||||||
// Let's resolve the cell so it can determine its own fields
|
// Let's resolve the cell so it can determine its own fields
|
||||||
// based on its final position.
|
// based on its final position.
|
||||||
let cell = self.resolve_cell(cell, x, y, rowspan, cell_span)?;
|
let cell = self.resolve_cell(cell, x, y, rowspan)?;
|
||||||
|
|
||||||
if largest_index >= resolved_cells.len() {
|
if largest_index >= resolved_cells.len() {
|
||||||
// Ensure the length of the vector of resolved cells is
|
// Ensure the length of the vector of resolved cells is
|
||||||
@ -1538,14 +1520,9 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
// and footers without having to loop through them each time.
|
// and footers without having to loop through them each time.
|
||||||
// Cells themselves, unfortunately, still have to.
|
// Cells themselves, unfortunately, still have to.
|
||||||
assert!(resolved_cells[*local_auto_index].is_none());
|
assert!(resolved_cells[*local_auto_index].is_none());
|
||||||
resolved_cells[*local_auto_index] =
|
resolved_cells[*local_auto_index] = Some(Entry::Cell(
|
||||||
Some(Entry::Cell(self.resolve_cell(
|
self.resolve_cell(T::default(), 0, first_available_row, 1)?,
|
||||||
T::default(),
|
));
|
||||||
0,
|
|
||||||
first_available_row,
|
|
||||||
1,
|
|
||||||
Span::detached(),
|
|
||||||
)?));
|
|
||||||
|
|
||||||
group_start..group_end
|
group_start..group_end
|
||||||
}
|
}
|
||||||
@ -1635,9 +1612,9 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
/// can be affected by show rules and grid-wide styling.
|
/// can be affected by show rules and grid-wide styling.
|
||||||
fn fixup_cells<T>(
|
fn fixup_cells<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
resolved_cells: Vec<Option<Entry<'x>>>,
|
resolved_cells: Vec<Option<Entry>>,
|
||||||
columns: usize,
|
columns: usize,
|
||||||
) -> SourceResult<Vec<Entry<'x>>>
|
) -> SourceResult<Vec<Entry>>
|
||||||
where
|
where
|
||||||
T: ResolvableCell + Default,
|
T: ResolvableCell + Default,
|
||||||
{
|
{
|
||||||
@ -1657,13 +1634,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
let x = i % columns;
|
let x = i % columns;
|
||||||
let y = i / columns;
|
let y = i / columns;
|
||||||
|
|
||||||
Ok(Entry::Cell(self.resolve_cell(
|
Ok(Entry::Cell(self.resolve_cell(T::default(), x, y, 1)?))
|
||||||
T::default(),
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
1,
|
|
||||||
Span::detached(),
|
|
||||||
)?))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<SourceResult<Vec<Entry>>>()
|
.collect::<SourceResult<Vec<Entry>>>()
|
||||||
@ -1915,8 +1886,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
rowspan: usize,
|
rowspan: usize,
|
||||||
cell_span: Span,
|
) -> SourceResult<Cell>
|
||||||
) -> SourceResult<Cell<'x>>
|
|
||||||
where
|
where
|
||||||
T: ResolvableCell + Default,
|
T: ResolvableCell + Default,
|
||||||
{
|
{
|
||||||
@ -1950,7 +1920,6 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
self.inset.resolve(self.engine, self.styles, x, y)?,
|
self.inset.resolve(self.engine, self.styles, x, y)?,
|
||||||
self.stroke.resolve(self.engine, self.styles, x, y)?,
|
self.stroke.resolve(self.engine, self.styles, x, y)?,
|
||||||
breakable,
|
breakable,
|
||||||
self.locator.next(&cell_span),
|
|
||||||
self.styles,
|
self.styles,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -1962,7 +1931,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
/// returned. Otherwise, the new `start..end` range of rows in the row group is
|
/// returned. Otherwise, the new `start..end` range of rows in the row group is
|
||||||
/// returned.
|
/// returned.
|
||||||
fn expand_row_group(
|
fn expand_row_group(
|
||||||
resolved_cells: &[Option<Entry<'_>>],
|
resolved_cells: &[Option<Entry>],
|
||||||
group_range: Option<&Range<usize>>,
|
group_range: Option<&Range<usize>>,
|
||||||
group_kind: RowGroupKind,
|
group_kind: RowGroupKind,
|
||||||
first_available_row: usize,
|
first_available_row: usize,
|
||||||
@ -2280,7 +2249,7 @@ fn resolve_cell_position(
|
|||||||
fn find_next_available_position(
|
fn find_next_available_position(
|
||||||
header_rows: &SmallBitSet,
|
header_rows: &SmallBitSet,
|
||||||
footer: Option<&(usize, Span, Footer)>,
|
footer: Option<&(usize, Span, Footer)>,
|
||||||
resolved_cells: &[Option<Entry<'_>>],
|
resolved_cells: &[Option<Entry>],
|
||||||
columns: usize,
|
columns: usize,
|
||||||
initial_index: usize,
|
initial_index: usize,
|
||||||
skip_rows: bool,
|
skip_rows: bool,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user