mirror of
https://github.com/typst/typst
synced 2025-05-15 09:35:28 +08:00
Allow grid cells to span multiple regions. (#30)
This commit is contained in:
parent
4017b5a9f6
commit
fec1f41106
@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// An absolute length.
|
/// An absolute length.
|
||||||
#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Hash)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Length {
|
pub struct Length {
|
||||||
@ -85,7 +85,7 @@ impl Length {
|
|||||||
|
|
||||||
/// Set to the minimum of this and another length.
|
/// Set to the minimum of this and another length.
|
||||||
pub fn set_min(&mut self, other: Self) {
|
pub fn set_min(&mut self, other: Self) {
|
||||||
*self = self.min(other);
|
*self = (*self).min(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum of this and another length.
|
/// The maximum of this and another length.
|
||||||
@ -95,7 +95,7 @@ impl Length {
|
|||||||
|
|
||||||
/// Set to the maximum of this and another length.
|
/// Set to the maximum of this and another length.
|
||||||
pub fn set_max(&mut self, other: Self) {
|
pub fn set_max(&mut self, other: Self) {
|
||||||
*self = self.max(other);
|
*self = (*self).max(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the other length fits into this one (i.e. is smaller).
|
/// Whether the other length fits into this one (i.e. is smaller).
|
||||||
|
@ -25,6 +25,7 @@ impl From<GridNode> for AnyNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct GridLayouter<'a> {
|
struct GridLayouter<'a> {
|
||||||
cross: SpecAxis,
|
cross: SpecAxis,
|
||||||
main: SpecAxis,
|
main: SpecAxis,
|
||||||
@ -32,11 +33,12 @@ 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>)>,
|
rrows: Vec<(usize, Option<Length>, Option<Vec<Option<Frame>>>)>,
|
||||||
rcols: Vec<Length>,
|
rcols: Vec<Length>,
|
||||||
finished: Vec<Frame>,
|
finished: Vec<Frame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum Cell<'a> {
|
enum Cell<'a> {
|
||||||
Node(&'a AnyNode),
|
Node(&'a AnyNode),
|
||||||
Gutter,
|
Gutter,
|
||||||
@ -233,95 +235,263 @@ impl<'a> GridLayouter<'a> {
|
|||||||
for y in 0 .. self.rows.len() {
|
for y in 0 .. self.rows.len() {
|
||||||
let resolved = match self.rows[y] {
|
let resolved = match self.rows[y] {
|
||||||
TrackSizing::Linear(l) => {
|
TrackSizing::Linear(l) => {
|
||||||
Some(l.resolve(self.regions.base.get(self.main)))
|
(Some(l.resolve(self.regions.base.get(self.main))), None)
|
||||||
}
|
}
|
||||||
TrackSizing::Auto => {
|
TrackSizing::Auto => {
|
||||||
let mut max = Length::zero();
|
let mut max = Length::zero();
|
||||||
for (x, len) in self.rcols.iter().enumerate() {
|
let mut local_max = max;
|
||||||
|
let mut multi_region = false;
|
||||||
|
let mut last_size = vec![];
|
||||||
|
for (x, &col_size) in self.rcols.iter().enumerate() {
|
||||||
if let Cell::Node(node) = self.get(x, y) {
|
if let Cell::Node(node) = self.get(x, y) {
|
||||||
let regions = Regions::one(
|
let colsize = Gen::new(col_size, current).to_size(self.main);
|
||||||
Gen::new(*len, current).to_size(self.main),
|
|
||||||
Spec::splat(false),
|
let mut regions = self.regions.map(|mut s| {
|
||||||
);
|
*s.get_mut(self.cross) = col_size;
|
||||||
let frame = node.layout(ctx, ®ions).remove(0);
|
s
|
||||||
max = max.max(frame.size.get(self.main));
|
});
|
||||||
|
|
||||||
|
regions.base = colsize;
|
||||||
|
regions.current = colsize;
|
||||||
|
regions.expand = Spec::splat(false);
|
||||||
|
|
||||||
|
let mut frames = node.layout(ctx, ®ions);
|
||||||
|
multi_region |= frames.len() > 1;
|
||||||
|
last_size.push((
|
||||||
|
frames.len() - 1,
|
||||||
|
frames.last().unwrap().size.get(self.main),
|
||||||
|
));
|
||||||
|
let frame = frames.remove(0);
|
||||||
|
local_max = local_max.max(frame.size.get(self.main));
|
||||||
|
|
||||||
|
if !multi_region {
|
||||||
|
max = local_max;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
last_size.push((0, Length::zero()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(max)
|
|
||||||
|
let overshoot = if multi_region {
|
||||||
|
self.rrows.push((y, Some(local_max), None));
|
||||||
|
let res = self.finish_region(ctx, total_frs, Some(last_size));
|
||||||
|
max = if let Some(overflow) = res.as_ref() {
|
||||||
|
overflow.iter()
|
||||||
|
.filter_map(|x| x.as_ref())
|
||||||
|
.map(|x| x.size.get(self.main))
|
||||||
|
.max()
|
||||||
|
.unwrap_or(Length::zero())
|
||||||
|
} else {
|
||||||
|
local_max
|
||||||
|
};
|
||||||
|
|
||||||
|
current = self.regions.current.get(self.main);
|
||||||
|
total_frs = 0.0;
|
||||||
|
if res.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// If multi-region results: finish_regions, returning
|
||||||
|
// the last non-set frames.
|
||||||
|
(Some(max), overshoot)
|
||||||
}
|
}
|
||||||
TrackSizing::Fractional(f) => {
|
TrackSizing::Fractional(f) => {
|
||||||
total_frs += f.get();
|
total_frs += f.get();
|
||||||
None
|
(None, None)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(resolved) = resolved {
|
if let (Some(resolved), _) = resolved {
|
||||||
while !current.fits(resolved) && !self.regions.in_full_last() {
|
while !current.fits(resolved) && !self.regions.in_full_last() {
|
||||||
self.finish_region(ctx, total_frs);
|
self.finish_region(ctx, total_frs, None);
|
||||||
current = self.regions.current.get(self.main);
|
current = self.regions.current.get(self.main);
|
||||||
total_frs = 0.0;
|
total_frs = 0.0;
|
||||||
}
|
}
|
||||||
current -= resolved;
|
current -= resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.rrows.push((y, resolved));
|
self.rrows.push((y, resolved.0, resolved.1));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_region(ctx, total_frs);
|
self.finish_region(ctx, total_frs, None);
|
||||||
self.finished
|
self.finished
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_region(&mut self, ctx: &mut LayoutContext, total_frs: f64) {
|
fn finish_region(
|
||||||
let mut pos = Gen::splat(Length::zero());
|
&mut self,
|
||||||
let mut frame = Frame::new(Size::zero(), Length::zero());
|
ctx: &mut LayoutContext,
|
||||||
let mut total_cross = Length::zero();
|
total_frs: f64,
|
||||||
let mut total_main = Length::zero();
|
multiregion_sizing: Option<Vec<(usize, Length)>>,
|
||||||
|
) -> Option<Vec<Option<Frame>>> {
|
||||||
for (x, &w) in self.rcols.iter().enumerate() {
|
if self.rrows.is_empty() {
|
||||||
let total: Length = self.rrows.iter().filter_map(|(_, x)| *x).sum();
|
return None;
|
||||||
let available = self.regions.current.get(self.main) - total;
|
|
||||||
total_cross += w;
|
|
||||||
|
|
||||||
for (y, h) in self.rrows.iter() {
|
|
||||||
let element = self.get(x, *y);
|
|
||||||
let h = if let Some(len) = h {
|
|
||||||
*len
|
|
||||||
} else if let TrackSizing::Fractional(f) = self.rows[*y] {
|
|
||||||
if total_frs > 0.0 {
|
|
||||||
let res = available * (f.get() / total_frs);
|
|
||||||
if res.is_finite() { res } else { Length::zero() }
|
|
||||||
} else {
|
|
||||||
Length::zero()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!("non-fractional tracks are already resolved");
|
|
||||||
};
|
|
||||||
|
|
||||||
if x == 0 {
|
|
||||||
total_main += h;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Cell::Node(n) = element {
|
|
||||||
let regions = Regions::one(
|
|
||||||
Gen::new(w, h).to_size(self.main),
|
|
||||||
Spec::splat(false),
|
|
||||||
);
|
|
||||||
let item = n.layout(ctx, ®ions).remove(0);
|
|
||||||
frame.push_frame(pos.to_point(self.main), item);
|
|
||||||
}
|
|
||||||
|
|
||||||
pos.main += h;
|
|
||||||
}
|
|
||||||
pos.main = Length::zero();
|
|
||||||
pos.cross += w;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut pos = Gen::splat(Length::zero());
|
||||||
|
let frame = Frame::new(Size::zero(), Length::zero());
|
||||||
|
let mut total_cross = Length::zero();
|
||||||
|
let mut total_main = Length::zero();
|
||||||
|
let mut max_regions = 0;
|
||||||
|
let mut collected_frames = if multiregion_sizing.is_some() {
|
||||||
|
Some(vec![None; self.rcols.len()])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.finished.push(frame);
|
||||||
|
|
||||||
|
let frame_len = self.finished.len();
|
||||||
|
|
||||||
|
let total_row_height: Length = self.rrows.iter().filter_map(|(_, x, _)| *x).sum();
|
||||||
|
|
||||||
|
for &(y, h, ref layouted) in self.rrows.iter().as_ref() {
|
||||||
|
let last = self.rrows.last().map_or(false, |(o, _, _)| &y == o);
|
||||||
|
let available = self.regions.current.get(self.main) - total_row_height;
|
||||||
|
let h = if let Some(len) = h {
|
||||||
|
len
|
||||||
|
} else if let TrackSizing::Fractional(f) = self.rows[y] {
|
||||||
|
if total_frs > 0.0 {
|
||||||
|
let res = available * (f.get() / total_frs);
|
||||||
|
if res.is_finite() { res } else { Length::zero() }
|
||||||
|
} else {
|
||||||
|
Length::zero()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!("non-fractional tracks are already resolved");
|
||||||
|
};
|
||||||
|
total_main += h;
|
||||||
|
|
||||||
|
if let Some(layouted) = layouted {
|
||||||
|
|
||||||
|
for (col_index, frame) in layouted.into_iter().enumerate() {
|
||||||
|
if let Some(frame) = frame {
|
||||||
|
self.finished
|
||||||
|
.get_mut(frame_len - 1)
|
||||||
|
.unwrap()
|
||||||
|
.push_frame(pos.to_point(self.main), frame.clone());
|
||||||
|
}
|
||||||
|
pos.cross += self.rcols[col_index];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut overshoot_columns = vec![];
|
||||||
|
for (x, &w) in self.rcols.iter().enumerate() {
|
||||||
|
let element = self.get(x, y);
|
||||||
|
|
||||||
|
if y == 0 {
|
||||||
|
total_cross += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Cell::Node(n) = element {
|
||||||
|
let region_size = Gen::new(w, h).to_size(self.main);
|
||||||
|
let regions = if last {
|
||||||
|
if let Some(last_sizes) = multiregion_sizing.as_ref() {
|
||||||
|
let mut regions = self.regions.map(|mut s| {
|
||||||
|
*s.get_mut(self.cross) = w;
|
||||||
|
s
|
||||||
|
});
|
||||||
|
|
||||||
|
regions.base = region_size;
|
||||||
|
regions.current = region_size;
|
||||||
|
regions.expand = Spec::splat(true);
|
||||||
|
|
||||||
|
let (last_region, last_size) = last_sizes[x];
|
||||||
|
regions.unique_regions(last_region + 1);
|
||||||
|
*regions.nth_mut(last_region).unwrap().get_mut(self.main) = last_size;
|
||||||
|
regions
|
||||||
|
} else {
|
||||||
|
Regions::one(region_size, Spec::splat(true))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Regions::one(region_size, Spec::splat(true))
|
||||||
|
};
|
||||||
|
let mut items = n.layout(ctx, ®ions);
|
||||||
|
let item = items.remove(0);
|
||||||
|
|
||||||
|
if last && multiregion_sizing.is_some() {
|
||||||
|
max_regions = max_regions.max(items.len());
|
||||||
|
overshoot_columns.push((x, items));
|
||||||
|
} else {
|
||||||
|
assert_eq!(items.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.finished
|
||||||
|
.get_mut(frame_len - 1)
|
||||||
|
.unwrap()
|
||||||
|
.push_frame(pos.to_point(self.main), item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.cross += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
if overshoot_columns.iter().any(|(_, items)| !items.is_empty()) {
|
||||||
|
for (x, col) in overshoot_columns {
|
||||||
|
let mut cross_offset = Length::zero();
|
||||||
|
for col in 0..x {
|
||||||
|
cross_offset += self.rcols[col];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let collected_frames = collected_frames.as_mut().unwrap();
|
||||||
|
*collected_frames.get_mut(x).unwrap() =
|
||||||
|
col.get(max_regions - 1).cloned();
|
||||||
|
|
||||||
|
for (cell_index, subcell) in col.into_iter().enumerate() {
|
||||||
|
if cell_index >= max_regions - 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let frame = if let Some(frame) =
|
||||||
|
self.finished.get_mut(frame_len + cell_index)
|
||||||
|
{
|
||||||
|
frame
|
||||||
|
} else {
|
||||||
|
let frame = Frame::new(Size::zero(), Length::zero());
|
||||||
|
// The previous frame always exists: either the
|
||||||
|
// last iteration created it or it is the normal
|
||||||
|
// frame.
|
||||||
|
self.finished.push(frame);
|
||||||
|
self.finished.last_mut().unwrap()
|
||||||
|
};
|
||||||
|
let pos = Gen::new(cross_offset, Length::zero());
|
||||||
|
frame
|
||||||
|
.size
|
||||||
|
.get_mut(self.cross)
|
||||||
|
.set_max(pos.cross + subcell.size.get(self.cross));
|
||||||
|
frame
|
||||||
|
.size
|
||||||
|
.get_mut(self.main)
|
||||||
|
.set_max(subcell.size.get(self.main));
|
||||||
|
frame.baseline = frame.size.height;
|
||||||
|
frame.push_frame(pos.to_point(self.main), subcell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.cross = Length::zero();
|
||||||
|
pos.main += h;
|
||||||
|
}
|
||||||
|
|
||||||
|
let frame = self.finished.get_mut(frame_len - 1).unwrap();
|
||||||
frame.size = Gen::new(total_cross, total_main).to_size(self.main);
|
frame.size = Gen::new(total_cross, total_main).to_size(self.main);
|
||||||
frame.baseline = frame.size.height;
|
frame.baseline = frame.size.height;
|
||||||
|
|
||||||
self.rrows.clear();
|
self.rrows.clear();
|
||||||
self.regions.next();
|
for _ in 0 .. (max_regions.max(1)) {
|
||||||
self.finished.push(frame);
|
self.regions.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(frames) = collected_frames.as_ref() {
|
||||||
|
if frames.iter().all(|i| i.is_none()) {
|
||||||
|
collected_frames = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collected_frames
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, x: usize, y: usize) -> &Cell<'a> {
|
fn get(&self, x: usize, y: usize) -> &Cell<'a> {
|
||||||
|
@ -280,4 +280,32 @@ impl Regions {
|
|||||||
let height = width / aspect.into_inner();
|
let height = width / aspect.into_inner();
|
||||||
self.current = Size::new(width, height);
|
self.current = Size::new(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Appends new elements to the backlog such that the behavior of `next`
|
||||||
|
/// does not change. Panics when used with finite regions.
|
||||||
|
pub fn backlogify(&mut self, count: usize) {
|
||||||
|
for _ in 0 .. count {
|
||||||
|
self.backlog.push(self.last.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that enough unique regions are present, including the current
|
||||||
|
/// and last ones. Panics when used with finite regions.
|
||||||
|
pub fn unique_regions(&mut self, count: usize) {
|
||||||
|
if self.backlog.len() < count.max(2) - 2 {
|
||||||
|
self.backlogify((count - 2) - self.backlog.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the size of the `n`th region.
|
||||||
|
pub fn nth_mut(&mut self, n: usize) -> Option<&mut Size> {
|
||||||
|
let backlog_len = self.backlog.len();
|
||||||
|
if n == 0 {
|
||||||
|
Some(&mut self.current)
|
||||||
|
} else if n <= backlog_len {
|
||||||
|
self.backlog.get_mut(backlog_len - n)
|
||||||
|
} else {
|
||||||
|
self.last.as_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,9 @@ 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 column_dir = args.eat_named(ctx, "column-dir");
|
||||||
let gutter = args.eat_named::<Tracks>(ctx, "gutter").unwrap_or_default();
|
let gutter = args.eat_named::<Linear>(ctx, "gutter")
|
||||||
|
.map(|v| Tracks(vec![TrackSizing::Linear(v)]))
|
||||||
|
.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 children = args.eat_all::<TemplateValue>(ctx);
|
let children = args.eat_all::<TemplateValue>(ctx);
|
||||||
|
BIN
tests/ref/layout/grid-3.png
Normal file
BIN
tests/ref/layout/grid-3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
@ -25,7 +25,8 @@
|
|||||||
---
|
---
|
||||||
#grid(
|
#grid(
|
||||||
columns: (auto, auto, 40%),
|
columns: (auto, auto, 40%),
|
||||||
gutter: (1fr,),
|
gutter-columns: (1fr,),
|
||||||
|
gutter-rows: (1fr,),
|
||||||
rect(fill: eastern)[dddaa aaa aaa],
|
rect(fill: eastern)[dddaa aaa aaa],
|
||||||
rect(fill: conifer)[ccc],
|
rect(fill: conifer)[ccc],
|
||||||
rect(width: 100%, fill: #dddddd)[aaa],
|
rect(width: 100%, fill: #dddddd)[aaa],
|
||||||
@ -52,3 +53,4 @@
|
|||||||
rows: (1fr, auto, 2fr),
|
rows: (1fr, auto, 2fr),
|
||||||
[], rect(width: 100%)[A bit more to the top], [],
|
[], rect(width: 100%)[A bit more to the top], [],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
79
tests/typ/layout/grid-3.typ
Normal file
79
tests/typ/layout/grid-3.typ
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Test grid cells that overflow to the next region.
|
||||||
|
|
||||||
|
---
|
||||||
|
#page(width: 5cm, height: 3cm)
|
||||||
|
#grid(
|
||||||
|
columns: 2,
|
||||||
|
gutter-rows: 3 * (8pt,),
|
||||||
|
[Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
|
||||||
|
|
||||||
|
Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis.],
|
||||||
|
[Text that is rather short],
|
||||||
|
[Fireflies],
|
||||||
|
[Critical],
|
||||||
|
[Decorum],
|
||||||
|
[Rampage],
|
||||||
|
)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test a column that starts overflowing right after another row/column did
|
||||||
|
// that.
|
||||||
|
#page(width: 5cm, height: 2cm)
|
||||||
|
#grid(
|
||||||
|
columns: 4 * (1fr,),
|
||||||
|
gutter-rows: (10pt,),
|
||||||
|
gutter-columns: (0pt, 10%),
|
||||||
|
image("../../res/rhino.png"),
|
||||||
|
align(right, rect(width: 100%, fill: eastern)[LoL]),
|
||||||
|
"rofl",
|
||||||
|
"\nA" * 3,
|
||||||
|
"Ha!\n" * 3,
|
||||||
|
)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test two columns in the same row overflowing by a different amount.
|
||||||
|
#page(width: 5cm, height: 2cm)
|
||||||
|
#grid(
|
||||||
|
columns: 3 * (1fr,),
|
||||||
|
gutter-rows: (8pt,),
|
||||||
|
gutter-columns: (0pt, 10%),
|
||||||
|
[A], [B], [C],
|
||||||
|
"Ha!\n" * 6,
|
||||||
|
"rofl",
|
||||||
|
"\nA" * 3,
|
||||||
|
[hello],
|
||||||
|
[darkness],
|
||||||
|
[my old]
|
||||||
|
)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test grid within a grid, overflowing.
|
||||||
|
#page(width: 5cm, height: 2.25cm)
|
||||||
|
#grid(
|
||||||
|
columns: 4 * (1fr,),
|
||||||
|
gutter-rows: (10pt,),
|
||||||
|
gutter-columns: (0pt, 10%),
|
||||||
|
[A], [B], [C], [D],
|
||||||
|
grid(columns: 2, [A], [B], "C\n"*3, [D]),
|
||||||
|
align(right, rect(width: 100%, fill: eastern)[LoL]),
|
||||||
|
"rofl",
|
||||||
|
"E\n"*4,
|
||||||
|
)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test partition of `fr` units before and after multi-region layout.
|
||||||
|
#page(width: 5cm, height: 4cm)
|
||||||
|
#grid(
|
||||||
|
columns: 2 * (1fr,),
|
||||||
|
rows: (1fr, 2fr, auto, 1fr, 1cm),
|
||||||
|
gutter-rows: 4 * (10pt,),
|
||||||
|
rect(height: 100%, width: 100%, fill: #ff0000)[No height],
|
||||||
|
[foo],
|
||||||
|
rect(height: 100%, width: 100%, fill: #fc0030)[Still no height],
|
||||||
|
[bar],
|
||||||
|
[The nature of being itself is in question. Am I One? Am I Many? What is being alive?],
|
||||||
|
[baz],
|
||||||
|
[The answer],
|
||||||
|
[42],
|
||||||
|
[Other text of interest],
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user