mirror of
https://github.com/typst/typst
synced 2025-06-28 16:22:53 +08:00
Refactor column resolving
This commit is contained in:
parent
4c37ebb936
commit
a61ee46ed2
@ -89,6 +89,16 @@ pub enum SpecAxis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SpecAxis {
|
impl SpecAxis {
|
||||||
|
/// The direction with the given positivity for this axis.
|
||||||
|
pub fn dir(self, positive: bool) -> Dir {
|
||||||
|
match (self, positive) {
|
||||||
|
(Self::Vertical, true) => Dir::TTB,
|
||||||
|
(Self::Vertical, false) => Dir::BTT,
|
||||||
|
(Self::Horizontal, true) => Dir::LTR,
|
||||||
|
(Self::Horizontal, false) => Dir::RTL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The other axis.
|
/// The other axis.
|
||||||
pub fn other(self) -> Self {
|
pub fn other(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
|
@ -3,14 +3,17 @@ use super::*;
|
|||||||
/// A node that arranges its children in a grid.
|
/// A node that arranges its children in a grid.
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
pub struct GridNode {
|
pub struct GridNode {
|
||||||
/// The column (cross) direction of this stack.
|
/// The `main` and `cross` directions of this grid.
|
||||||
pub column_dir: Dir,
|
///
|
||||||
|
/// The rows go along the `main` direction and the columns along the `cross`
|
||||||
|
/// direction.
|
||||||
|
pub dirs: Gen<Dir>,
|
||||||
|
/// Defines sizing for content rows and columns.
|
||||||
|
pub tracks: Gen<Vec<TrackSizing>>,
|
||||||
|
/// Defines sizing of gutter rows and columns between content.
|
||||||
|
pub gutter: Gen<Vec<TrackSizing>>,
|
||||||
/// The nodes to be arranged in a grid.
|
/// The nodes to be arranged in a grid.
|
||||||
pub children: Vec<AnyNode>,
|
pub children: Vec<AnyNode>,
|
||||||
/// Defines sizing for rows and columns.
|
|
||||||
pub tracks: Gen<Tracks>,
|
|
||||||
/// Defines sizing of the gutter between rows and columns.
|
|
||||||
pub gutter: Gen<Tracks>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for GridNode {
|
impl Layout for GridNode {
|
||||||
@ -33,8 +36,8 @@ struct GridLayouter<'a> {
|
|||||||
rows: Vec<TrackSizing>,
|
rows: Vec<TrackSizing>,
|
||||||
cells: Vec<Cell<'a>>,
|
cells: Vec<Cell<'a>>,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
rrows: Vec<(usize, Option<Length>, Option<Vec<Option<Frame>>>)>,
|
|
||||||
rcols: Vec<Length>,
|
rcols: Vec<Length>,
|
||||||
|
rrows: Vec<(usize, Option<Length>, Option<Vec<Option<Frame>>>)>,
|
||||||
finished: Vec<Frame>,
|
finished: Vec<Frame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,35 +49,38 @@ enum Cell<'a> {
|
|||||||
|
|
||||||
impl<'a> GridLayouter<'a> {
|
impl<'a> GridLayouter<'a> {
|
||||||
fn new(grid: &'a GridNode, regions: Regions) -> Self {
|
fn new(grid: &'a GridNode, regions: Regions) -> Self {
|
||||||
let mut col_sizes = vec![];
|
let cross = grid.dirs.cross.axis();
|
||||||
let mut row_sizes = vec![];
|
let main = grid.dirs.main.axis();
|
||||||
|
|
||||||
|
let mut cols = vec![];
|
||||||
|
let mut rows = vec![];
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
|
|
||||||
// A grid always needs to have at least one column.
|
// A grid always needs to have at least one column.
|
||||||
let cols = grid.tracks.cross.0.len().max(1);
|
let content_cols = grid.tracks.cross.len().max(1);
|
||||||
|
|
||||||
// Create at least as many rows as specified and also at least as many
|
// Create at least as many rows as specified and also at least as many
|
||||||
// as necessary to place each item.
|
// as necessary to place each item.
|
||||||
let rows = {
|
let content_rows = {
|
||||||
let len = grid.children.len();
|
let len = grid.children.len();
|
||||||
let specified = grid.tracks.main.0.len();
|
let specified = grid.tracks.main.len();
|
||||||
let necessary = len / cols + (len % cols).clamp(0, 1);
|
let necessary = len / content_cols + (len % content_cols).clamp(0, 1);
|
||||||
specified.max(necessary)
|
specified.max(necessary)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Collect the track sizing for all columns, including gutter columns.
|
// Collect the track sizing for all columns, including gutter columns.
|
||||||
for i in 0 .. cols {
|
for i in 0 .. content_cols {
|
||||||
col_sizes.push(grid.tracks.cross.get(i));
|
cols.push(grid.tracks.cross.get_or_last(i));
|
||||||
if i < cols - 1 {
|
if i < content_cols - 1 {
|
||||||
col_sizes.push(grid.gutter.cross.get(i));
|
cols.push(grid.gutter.cross.get_or_last(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect the track sizing for all rows, including gutter rows.
|
// Collect the track sizing for all rows, including gutter rows.
|
||||||
for i in 0 .. rows {
|
for i in 0 .. content_rows {
|
||||||
row_sizes.push(grid.tracks.main.get(i));
|
rows.push(grid.tracks.main.get_or_last(i));
|
||||||
if i < rows - 1 {
|
if i < content_rows - 1 {
|
||||||
row_sizes.push(grid.gutter.main.get(i));
|
rows.push(grid.gutter.main.get_or_last(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,152 +88,155 @@ impl<'a> GridLayouter<'a> {
|
|||||||
for (i, item) in grid.children.iter().enumerate() {
|
for (i, item) in grid.children.iter().enumerate() {
|
||||||
cells.push(Cell::Node(item));
|
cells.push(Cell::Node(item));
|
||||||
|
|
||||||
let row = i / cols;
|
let row = i / content_cols;
|
||||||
let col = i % cols;
|
let col = i % content_cols;
|
||||||
|
|
||||||
if col < cols - 1 {
|
if col < content_cols - 1 {
|
||||||
// Push gutter after each child.
|
// Push gutter after each child.
|
||||||
cells.push(Cell::Gutter);
|
cells.push(Cell::Gutter);
|
||||||
} else if row < rows - 1 {
|
} else if row < content_rows - 1 {
|
||||||
// Except for the last child of each row.
|
// Except for the last child of each row.
|
||||||
// There we push a gutter row.
|
// There we push a gutter row.
|
||||||
for _ in 0 .. col_sizes.len() {
|
for _ in 0 .. cols.len() {
|
||||||
cells.push(Cell::Gutter);
|
cells.push(Cell::Gutter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill the thing up.
|
// Fill the thing up.
|
||||||
while cells.len() < col_sizes.len() * row_sizes.len() {
|
while cells.len() < cols.len() * rows.len() {
|
||||||
cells.push(Cell::Gutter)
|
cells.push(Cell::Gutter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
cross: grid.column_dir.axis(),
|
cross,
|
||||||
main: grid.column_dir.axis().other(),
|
main,
|
||||||
cols: col_sizes,
|
cols,
|
||||||
rows: row_sizes,
|
rows,
|
||||||
cells,
|
cells,
|
||||||
regions,
|
regions,
|
||||||
rrows: vec![],
|
|
||||||
rcols: vec![],
|
rcols: vec![],
|
||||||
|
rrows: vec![],
|
||||||
finished: vec![],
|
finished: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Frame> {
|
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Frame> {
|
||||||
// Shrink area by linear sizing.
|
self.rcols = self.resolve_columns(ctx);
|
||||||
let mut available = self.regions.current.get(self.cross);
|
self.layout_rows(ctx);
|
||||||
available -= self
|
self.finished
|
||||||
.cols
|
}
|
||||||
.iter()
|
|
||||||
.filter_map(|x| match x {
|
/// Determine the size of all columns.
|
||||||
TrackSizing::Linear(l) => {
|
fn resolve_columns(&self, ctx: &mut LayoutContext) -> Vec<Length> {
|
||||||
Some(l.resolve(self.regions.base.get(self.cross)))
|
let current = self.regions.current.to_gen(self.main);
|
||||||
}
|
let base = self.regions.base.to_gen(self.main);
|
||||||
_ => None,
|
|
||||||
})
|
// Prepare vector for resolved column lengths.
|
||||||
.sum();
|
let mut rcols = vec![Length::zero(); self.cols.len()];
|
||||||
|
|
||||||
let col_frac: f64 = self
|
// - Sum of sizes of resolved linear tracks,
|
||||||
.cols
|
// - Sum of fractions of all fractional tracks,
|
||||||
.iter()
|
// - Sum of sizes of resolved (through layouting) auto tracks,
|
||||||
.filter_map(|x| match x {
|
// - Number of auto tracks.
|
||||||
TrackSizing::Fractional(f) => Some(f.get()),
|
let mut linear = Length::zero();
|
||||||
_ => None,
|
let mut fr = Fractional::zero();
|
||||||
})
|
let mut auto = Length::zero();
|
||||||
.sum();
|
let mut auto_count = 0;
|
||||||
|
|
||||||
let auto_columns = self
|
// Resolve size of linear columns and compute the sum of all fractional
|
||||||
.cols
|
// tracks.
|
||||||
.iter()
|
for (&col, rcol) in self.cols.iter().zip(&mut rcols) {
|
||||||
.enumerate()
|
match col {
|
||||||
.filter_map(|(i, x)| (x == &TrackSizing::Auto).then(|| i));
|
TrackSizing::Auto => {}
|
||||||
|
TrackSizing::Linear(v) => {
|
||||||
let mut col_width = vec![];
|
let resolved = v.resolve(base.cross);
|
||||||
|
*rcol = resolved;
|
||||||
// For each of the auto columns, lay out all elements with
|
linear += resolved;
|
||||||
// `preliminary_length` rows and build max.
|
|
||||||
for x in auto_columns {
|
|
||||||
let mut max = Length::zero();
|
|
||||||
|
|
||||||
for (y, row) in self.rows.iter().enumerate() {
|
|
||||||
let mut size = self.regions.current;
|
|
||||||
if let TrackSizing::Linear(l) = row {
|
|
||||||
*size.get_mut(self.main) =
|
|
||||||
l.resolve(self.regions.base.get(self.main));
|
|
||||||
}
|
|
||||||
|
|
||||||
let region = Regions::one(size, Spec::splat(false));
|
|
||||||
if let Cell::Node(node) = self.get(x, y) {
|
|
||||||
let frame = node.layout(ctx, ®ion).remove(0);
|
|
||||||
max = max.max(frame.size.get(self.cross))
|
|
||||||
}
|
}
|
||||||
|
TrackSizing::Fractional(v) => fr += v,
|
||||||
}
|
}
|
||||||
|
|
||||||
col_width.push((x, max));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If accumulated auto column size exceeds available size, redistribute
|
// Size available to auto columns (not used by fixed-size columns).
|
||||||
// space proportionally amongst elements that exceed their size
|
let available = current.cross - linear;
|
||||||
// allocation.
|
if available <= Length::zero() {
|
||||||
let mut total: Length = col_width.iter().map(|(_, x)| *x).sum();
|
return rcols;
|
||||||
if total > available {
|
}
|
||||||
let alloc = available / col_width.len() as f64;
|
|
||||||
|
|
||||||
let mut count: usize = 0;
|
// Resolve size of auto columns by laying out all cells in those
|
||||||
let mut redistributable = Length::zero();
|
// columns, measuring them and finding the largest one.
|
||||||
|
for (x, (&col, rcol)) in self.cols.iter().zip(&mut rcols).enumerate() {
|
||||||
|
if col == TrackSizing::Auto {
|
||||||
|
let mut resolved = Length::zero();
|
||||||
|
|
||||||
for &(_, l) in &col_width {
|
for (y, &row) in self.rows.iter().enumerate() {
|
||||||
if l > alloc {
|
if let Cell::Node(node) = self.get(x, y) {
|
||||||
redistributable += l;
|
// Set the correct main size if the row is fixed-size.
|
||||||
count += 1;
|
let main = match row {
|
||||||
|
TrackSizing::Linear(v) => v.resolve(base.main),
|
||||||
|
_ => current.main,
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = Gen::new(available, main).to_size(self.main);
|
||||||
|
let regions = Regions::one(size, Spec::splat(false));
|
||||||
|
let frame = node.layout(ctx, ®ions).remove(0);
|
||||||
|
resolved = resolved.max(frame.size.get(self.cross))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*rcol = resolved;
|
||||||
|
auto += resolved;
|
||||||
|
auto_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is remaining space, distribute it to fractional columns,
|
||||||
|
// otherwise shrink auto columns.
|
||||||
|
let remaining = available - auto;
|
||||||
|
if remaining >= Length::zero() {
|
||||||
|
for (&col, rcol) in self.cols.iter().zip(&mut rcols) {
|
||||||
|
if let TrackSizing::Fractional(v) = col {
|
||||||
|
let ratio = v / fr;
|
||||||
|
if ratio.is_finite() {
|
||||||
|
*rcol = ratio * remaining;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// The fair share each auto column may have.
|
||||||
|
let fair = available / auto_count as f64;
|
||||||
|
|
||||||
let x = (available - total + redistributable) / count as f64;
|
// The number of overlarge auto columns and the space that will be
|
||||||
|
// equally redistributed to them.
|
||||||
|
let mut overlarge: usize = 0;
|
||||||
|
let mut redistribute = available;
|
||||||
|
|
||||||
if !redistributable.is_zero() {
|
for (&col, rcol) in self.cols.iter().zip(&mut rcols) {
|
||||||
for (_, l) in &mut col_width {
|
if col == TrackSizing::Auto {
|
||||||
if *l > alloc {
|
if *rcol > fair {
|
||||||
*l = x;
|
overlarge += 1;
|
||||||
|
} else {
|
||||||
|
redistribute -= *rcol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
total = available;
|
// Redistribute the space equally.
|
||||||
}
|
let share = redistribute / overlarge as f64;
|
||||||
|
if overlarge > 0 {
|
||||||
// Build rcols
|
for (&col, rcol) in self.cols.iter().zip(&mut rcols) {
|
||||||
for (x, len) in col_width
|
if col == TrackSizing::Auto && *rcol > fair {
|
||||||
.into_iter()
|
*rcol = share;
|
||||||
.map(|(x, s)| (x, Some(s)))
|
|
||||||
.chain(std::iter::once((self.cols.len(), None)))
|
|
||||||
{
|
|
||||||
for i in self.rcols.len() .. x {
|
|
||||||
let len = match self.cols[i] {
|
|
||||||
TrackSizing::Linear(l) => {
|
|
||||||
l.resolve(self.regions.base.get(self.cross))
|
|
||||||
}
|
}
|
||||||
TrackSizing::Fractional(f) => {
|
}
|
||||||
if col_frac == 0.0 {
|
|
||||||
Length::zero()
|
|
||||||
} else {
|
|
||||||
let res: Length = (available - total) * (f.get() / col_frac);
|
|
||||||
if res.is_finite() { res } else { Length::zero() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TrackSizing::Auto => unreachable!("x is an auto track"),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.rcols.push(len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(len) = len {
|
|
||||||
self.rcols.push(len);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcols
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_rows(&mut self, ctx: &mut LayoutContext) {
|
||||||
// Determine non-`fr` row heights
|
// Determine non-`fr` row heights
|
||||||
let mut total_frs = 0.0;
|
let mut total_frs = 0.0;
|
||||||
let mut current = self.regions.current.get(self.main);
|
let mut current = self.regions.current.get(self.main);
|
||||||
@ -276,11 +285,12 @@ impl<'a> GridLayouter<'a> {
|
|||||||
self.rrows.push((y, Some(local_max), None));
|
self.rrows.push((y, Some(local_max), None));
|
||||||
let res = self.finish_region(ctx, total_frs, Some(last_size));
|
let res = self.finish_region(ctx, total_frs, Some(last_size));
|
||||||
max = if let Some(overflow) = res.as_ref() {
|
max = if let Some(overflow) = res.as_ref() {
|
||||||
overflow.iter()
|
overflow
|
||||||
.filter_map(|x| x.as_ref())
|
.iter()
|
||||||
.map(|x| x.size.get(self.main))
|
.filter_map(|x| x.as_ref())
|
||||||
.max()
|
.map(|x| x.size.get(self.main))
|
||||||
.unwrap_or(Length::zero())
|
.max()
|
||||||
|
.unwrap_or(Length::zero())
|
||||||
} else {
|
} else {
|
||||||
local_max
|
local_max
|
||||||
};
|
};
|
||||||
@ -319,7 +329,6 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.finish_region(ctx, total_frs, None);
|
self.finish_region(ctx, total_frs, None);
|
||||||
self.finished
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_region(
|
fn finish_region(
|
||||||
@ -367,7 +376,6 @@ impl<'a> GridLayouter<'a> {
|
|||||||
total_main += h;
|
total_main += h;
|
||||||
|
|
||||||
if let Some(layouted) = layouted {
|
if let Some(layouted) = layouted {
|
||||||
|
|
||||||
for (col_index, frame) in layouted.into_iter().enumerate() {
|
for (col_index, frame) in layouted.into_iter().enumerate() {
|
||||||
if let Some(frame) = frame {
|
if let Some(frame) = frame {
|
||||||
self.finished
|
self.finished
|
||||||
@ -401,7 +409,10 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
let (last_region, last_size) = last_sizes[x];
|
let (last_region, last_size) = last_sizes[x];
|
||||||
regions.unique_regions(last_region + 1);
|
regions.unique_regions(last_region + 1);
|
||||||
*regions.nth_mut(last_region).unwrap().get_mut(self.main) = last_size;
|
*regions
|
||||||
|
.nth_mut(last_region)
|
||||||
|
.unwrap()
|
||||||
|
.get_mut(self.main) = last_size;
|
||||||
regions
|
regions
|
||||||
} else {
|
} else {
|
||||||
Regions::one(region_size, Spec::splat(true))
|
Regions::one(region_size, Spec::splat(true))
|
||||||
@ -431,7 +442,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
if overshoot_columns.iter().any(|(_, items)| !items.is_empty()) {
|
if overshoot_columns.iter().any(|(_, items)| !items.is_empty()) {
|
||||||
for (x, col) in overshoot_columns {
|
for (x, col) in overshoot_columns {
|
||||||
let mut cross_offset = Length::zero();
|
let mut cross_offset = Length::zero();
|
||||||
for col in 0..x {
|
for col in 0 .. x {
|
||||||
cross_offset += self.rcols[col];
|
cross_offset += self.rcols[col];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,18 +512,15 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A list of track sizing definitions.
|
trait TracksExt {
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Hash)]
|
/// Get the sizing for the track at the given `idx` or fallback to the
|
||||||
pub struct Tracks(pub Vec<TrackSizing>);
|
/// last defined track or `auto`.
|
||||||
|
fn get_or_last(&self, idx: usize) -> TrackSizing;
|
||||||
|
}
|
||||||
|
|
||||||
impl Tracks {
|
impl TracksExt for Vec<TrackSizing> {
|
||||||
/// Get the sizing for the track at the given `idx`.
|
fn get_or_last(&self, idx: usize) -> TrackSizing {
|
||||||
fn get(&self, idx: usize) -> TrackSizing {
|
self.get(idx).or(self.last()).copied().unwrap_or(TrackSizing::Auto)
|
||||||
self.0
|
|
||||||
.get(idx)
|
|
||||||
.or(self.0.last())
|
|
||||||
.copied()
|
|
||||||
.unwrap_or(TrackSizing::Auto)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +535,7 @@ impl<'a> LineLayout<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper methods for BiDi levels.
|
/// Additional methods for BiDi levels.
|
||||||
trait LevelExt: Sized {
|
trait LevelExt: Sized {
|
||||||
fn from_dir(dir: Dir) -> Option<Self>;
|
fn from_dir(dir: Dir) -> Option<Self>;
|
||||||
fn dir(self) -> Dir;
|
fn dir(self) -> Dir;
|
||||||
|
@ -41,7 +41,7 @@ impl From<StackNode> for AnyNode {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct StackLayouter<'a> {
|
struct StackLayouter<'a> {
|
||||||
/// The directions of the stack.
|
/// The stack node to layout.
|
||||||
stack: &'a StackNode,
|
stack: &'a StackNode,
|
||||||
/// The axis of the main direction.
|
/// The axis of the main direction.
|
||||||
main: SpecAxis,
|
main: SpecAxis,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::layout::{GridNode, TrackSizing, Tracks};
|
use crate::layout::{GridNode, TrackSizing};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -10,10 +10,11 @@ use super::*;
|
|||||||
/// # Named parameters
|
/// # Named parameters
|
||||||
/// - Column sizing: `columns`, of type `tracks`.
|
/// - Column sizing: `columns`, of type `tracks`.
|
||||||
/// - Row sizing: `rows`, of type `tracks`.
|
/// - Row sizing: `rows`, of type `tracks`.
|
||||||
/// - Column direction: `column-dir`, of type `direction`.
|
/// - Gutter: `gutter`, shorthand for equal gutter everywhere, of type `length`.
|
||||||
/// - Gutter: `gutter`, shorthand for equal column and row gutter, of type `tracks`.
|
|
||||||
/// - Gutter for rows: `gutter-rows`, of type `tracks`.
|
/// - Gutter for rows: `gutter-rows`, of type `tracks`.
|
||||||
/// - Gutter for columns: `gutter-columns`, of type `tracks`.
|
/// - Gutter for columns: `gutter-columns`, of type `tracks`.
|
||||||
|
/// - Column direction: `column-dir`, of type `direction`.
|
||||||
|
/// - Row direction: `row-dir`, of type `direction`.
|
||||||
///
|
///
|
||||||
/// # Return value
|
/// # Return value
|
||||||
/// A template that arranges its children along the specified grid cells.
|
/// A template that arranges its children along the specified grid cells.
|
||||||
@ -35,12 +36,14 @@ use super::*;
|
|||||||
pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||||
let columns = args.eat_named::<Tracks>(ctx, "columns").unwrap_or_default();
|
let columns = args.eat_named::<Tracks>(ctx, "columns").unwrap_or_default();
|
||||||
let rows = args.eat_named::<Tracks>(ctx, "rows").unwrap_or_default();
|
let rows = args.eat_named::<Tracks>(ctx, "rows").unwrap_or_default();
|
||||||
let column_dir = args.eat_named(ctx, "column-dir");
|
let gutter = args
|
||||||
let gutter = args.eat_named::<Linear>(ctx, "gutter")
|
.eat_named::<Linear>(ctx, "gutter")
|
||||||
.map(|v| Tracks(vec![TrackSizing::Linear(v)]))
|
.map(|v| vec![TrackSizing::Linear(v)])
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let gutter_columns = args.eat_named::<Tracks>(ctx, "gutter-columns");
|
let gutter_columns = args.eat_named::<Tracks>(ctx, "gutter-columns");
|
||||||
let gutter_rows = args.eat_named::<Tracks>(ctx, "gutter-rows");
|
let gutter_rows = args.eat_named::<Tracks>(ctx, "gutter-rows");
|
||||||
|
let column_dir = args.eat_named(ctx, "column-dir");
|
||||||
|
let row_dir = args.eat_named(ctx, "row-dir");
|
||||||
let children = args.eat_all::<TemplateValue>(ctx);
|
let children = args.eat_all::<TemplateValue>(ctx);
|
||||||
|
|
||||||
Value::template("grid", move |ctx| {
|
Value::template("grid", move |ctx| {
|
||||||
@ -49,26 +52,31 @@ pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
.map(|child| ctx.exec_template_stack(child).into())
|
.map(|child| ctx.exec_template_stack(child).into())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let cross_dir = column_dir.unwrap_or(ctx.state.lang.dir);
|
||||||
|
let main_dir = row_dir.unwrap_or(cross_dir.axis().other().dir(true));
|
||||||
|
|
||||||
ctx.push_into_stack(GridNode {
|
ctx.push_into_stack(GridNode {
|
||||||
column_dir: column_dir.unwrap_or(ctx.state.lang.dir),
|
dirs: Gen::new(cross_dir, main_dir),
|
||||||
children,
|
|
||||||
tracks: Gen::new(columns.clone(), rows.clone()),
|
tracks: Gen::new(columns.clone(), rows.clone()),
|
||||||
gutter: Gen::new(
|
gutter: Gen::new(
|
||||||
gutter_columns.as_ref().unwrap_or(&gutter).clone(),
|
gutter_columns.as_ref().unwrap_or(&gutter).clone(),
|
||||||
gutter_rows.as_ref().unwrap_or(&gutter).clone(),
|
gutter_rows.as_ref().unwrap_or(&gutter).clone(),
|
||||||
),
|
),
|
||||||
|
children,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines size of rows and columns in a grid.
|
||||||
|
type Tracks = Vec<TrackSizing>;
|
||||||
|
|
||||||
value! {
|
value! {
|
||||||
Tracks: "array of `auto`s, linears, and fractionals",
|
Tracks: "array of `auto`s, linears, and fractionals",
|
||||||
Value::Int(count) => Self(vec![TrackSizing::Auto; count.max(0) as usize]),
|
Value::Int(count) => vec![TrackSizing::Auto; count.max(0) as usize],
|
||||||
Value::Array(values) => Self(values
|
Value::Array(values) => values
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|v| v.cast().ok())
|
.filter_map(|v| v.cast().ok())
|
||||||
.collect()
|
.collect(),
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
value! {
|
value! {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user