1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
//! Multi-column layouts.
use super::prelude::*;
use super::ParNode;
/// A node that separates a region into multiple equally sized columns.
#[derive(Debug, Hash)]
pub struct ColumnsNode {
/// How many columns there should be.
pub columns: NonZeroUsize,
/// The child to be layouted into the columns. Most likely, this should be a
/// flow or stack node.
pub child: PackedNode,
}
#[class]
impl ColumnsNode {
/// The size of the gutter space between each column.
pub const GUTTER: Linear = Relative::new(0.04).into();
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
Ok(Node::block(Self {
columns: args.expect("column count")?,
child: args.expect("body")?,
}))
}
fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
styles.set_opt(Self::GUTTER, args.named("gutter")?);
Ok(())
}
}
impl Layout for ColumnsNode {
fn layout(
&self,
ctx: &mut LayoutContext,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
let columns = self.columns.get();
// Separating the infinite space into infinite columns does not make
// much sense. Note that this line assumes that no infinitely wide
// region will follow if the first region's width is finite.
if regions.current.x.is_infinite() {
return self.child.layout(ctx, regions, styles);
}
// Gutter width for each region. (Can be different because the relative
// component is calculated seperately for each region.)
let mut gutters = vec![];
// Sizes of all columns resulting from `region.current`,
// `region.backlog` and `regions.last`.
let mut sizes = vec![];
for (current, base) in regions
.iter()
.take(1 + regions.backlog.len() + regions.last.iter().len())
{
let gutter = styles.get(Self::GUTTER).resolve(base.x);
let width = (current.x - gutter * (columns - 1) as f64) / columns as f64;
let size = Size::new(width, current.y);
gutters.push(gutter);
sizes.extend(std::iter::repeat(size).take(columns));
}
let first = sizes.remove(0);
let mut pod = Regions::one(
first,
Size::new(first.x, regions.base.y),
Spec::new(true, regions.expand.y),
);
// Retrieve elements for the last region from the vectors.
let last_gutter = regions.last.map(|_| {
let gutter = gutters.pop().unwrap();
let size = sizes.drain(sizes.len() - columns ..).next().unwrap();
pod.last = Some(size);
gutter
});
pod.backlog = sizes.into_iter();
let mut finished = vec![];
let mut frames = self.child.layout(ctx, &pod, styles).into_iter();
let dir = styles.get(ParNode::DIR);
let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize;
// Stitch together the columns for each region.
for ((current, base), gutter) in regions
.iter()
.take(total_regions)
.zip(gutters.into_iter().chain(last_gutter.into_iter().cycle()))
{
// The height should be the parent height if the node shall expand.
// Otherwise its the maximum column height for the frame. In that
// case, the frame is first created with zero height and then
// resized.
let height = if regions.expand.y { current.y } else { Length::zero() };
let mut output = Frame::new(Size::new(regions.current.x, height));
let mut cursor = Length::zero();
for _ in 0 .. columns {
let frame = match frames.next() {
Some(frame) => frame.item,
None => break,
};
if !regions.expand.y {
output.size.y.set_max(frame.size.y);
}
let width = frame.size.x;
let x = if dir.is_positive() {
cursor
} else {
regions.current.x - cursor - width
};
output.push_frame(Point::with_x(x), frame);
cursor += width + gutter;
}
let mut cts = Constraints::new(regions.expand);
cts.base = base.map(Some);
cts.exact = current.map(Some);
finished.push(output.constrain(cts));
}
finished
}
}
/// A column break.
pub struct ColbreakNode;
#[class]
impl ColbreakNode {
fn construct(_: &mut EvalContext, _: &mut Args) -> TypResult<Node> {
Ok(Node::Colbreak)
}
}
|