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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
use crate::prelude::*;
use crate::text::TextElem;
/// Separates a region into multiple equally sized columns.
///
/// The `column` function allows to separate the interior of any container into
/// multiple columns. It will not equalize the height of the columns, instead,
/// the columns will take up the height of their container or the remaining
/// height on the page. The columns function can break across pages if
/// necessary.
///
/// ## Example { #example }
/// ```example
/// = Towards Advanced Deep Learning
///
/// #box(height: 68pt,
/// columns(2, gutter: 11pt)[
/// #set par(justify: true)
/// This research was funded by the
/// National Academy of Sciences.
/// NAoS provided support for field
/// tests and interviews with a
/// grant of up to USD 40.000 for a
/// period of 6 months.
/// ]
/// )
///
/// In recent years, deep learning has
/// increasingly been used to solve a
/// variety of problems.
/// ```
///
/// Display: Columns
/// Category: layout
#[element(Layout)]
pub struct ColumnsElem {
/// The number of columns.
#[positional]
#[default(NonZeroUsize::new(2).unwrap())]
pub count: NonZeroUsize,
/// The size of the gutter space between each column.
#[resolve]
#[default(Ratio::new(0.04).into())]
pub gutter: Rel<Length>,
/// The content that should be layouted into the columns.
#[required]
pub body: Content,
}
impl Layout for ColumnsElem {
#[tracing::instrument(name = "ColumnsElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let body = self.body();
// Separating the infinite space into infinite columns does not make
// much sense.
if !regions.size.x.is_finite() {
return body.layout(vt, styles, regions);
}
// Determine the width of the gutter and each column.
let columns = self.count(styles).get();
let gutter = self.gutter(styles).relative_to(regions.base().x);
let width = (regions.size.x - gutter * (columns - 1) as f64) / columns as f64;
let backlog: Vec<_> = std::iter::once(®ions.size.y)
.chain(regions.backlog)
.flat_map(|&height| std::iter::repeat(height).take(columns))
.skip(1)
.collect();
// Create the pod regions.
let pod = Regions {
size: Size::new(width, regions.size.y),
full: regions.full,
backlog: &backlog,
last: regions.last,
expand: Axes::new(true, regions.expand.y),
root: regions.root,
};
// Layout the children.
let mut frames = body.layout(vt, styles, pod)?.into_iter();
let mut finished = vec![];
let dir = TextElem::dir_in(styles);
let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize;
// Stitch together the columns for each region.
for region in regions.iter().take(total_regions) {
// The height should be the parent height if we should 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 { region.y } else { Abs::zero() };
let mut output = Frame::new(Size::new(regions.size.x, height));
let mut cursor = Abs::zero();
for _ in 0..columns {
let Some(frame) = frames.next() else { break };
if !regions.expand.y {
output.size_mut().y.set_max(frame.height());
}
let width = frame.width();
let x = if dir == Dir::LTR {
cursor
} else {
regions.size.x - cursor - width
};
output.push_frame(Point::with_x(x), frame);
cursor += width + gutter;
}
finished.push(output);
}
Ok(Fragment::frames(finished))
}
}
/// Forces a column break.
///
/// The function will behave like a [page break]($func/pagebreak) when used in a
/// single column layout or the last column on a page. Otherwise, content after
/// the column break will be placed in the next column.
///
/// ## Example { #example }
/// ```example
/// #set page(columns: 2)
/// Preliminary findings from our
/// ongoing research project have
/// revealed a hitherto unknown
/// phenomenon of extraordinary
/// significance.
///
/// #colbreak()
/// Through rigorous experimentation
/// and analysis, we have discovered
/// a hitherto uncharacterized process
/// that defies our current
/// understanding of the fundamental
/// laws of nature.
/// ```
///
/// Display: Column Break
/// Category: layout
#[element(Behave)]
pub struct ColbreakElem {
/// If `{true}`, the column break is skipped if the current column is
/// already empty.
#[default(false)]
pub weak: bool,
}
impl Behave for ColbreakElem {
fn behaviour(&self) -> Behaviour {
if self.weak(StyleChain::default()) {
Behaviour::Weak(1)
} else {
Behaviour::Destructive
}
}
}
|