summaryrefslogtreecommitdiff
path: root/src/layout/stack.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-04-03 17:36:33 +0200
committerLaurenz <laurmaedje@gmail.com>2021-04-03 21:09:17 +0200
commitd74c9378b81618419dc8c6315e391b6012955218 (patch)
treeae7de73aafa96b407bac5ead7bd827556133e34d /src/layout/stack.rs
parent8245b7b73667dcdd32b32f49729d39083d513817 (diff)
New paragraph layout 🚀
The previous paragraph layout algorithm had a couple of flaws: - It always produced line break opportunities between runs although on the textual level there might have been none. - It didn't handle trailing spacing correctly in some cases. - It wouldn't have been easily adaptable to Knuth-Plass style optimal line breaking because it was fundamentally structured first-fit run-by-run. The new paragraph layout algorithm fixes these flaws. It proceeds roughly in the following stages: 1. Collect all text in the paragraph. 2. Compute BiDi embedding levels. 3. Shape all runs, layout all children and store the resulting items in a reusable (possibly even cacheable) `ParLayout`. 3. Iterate over all line breaks in the concatenated text. 4. Construct lightweight `LineLayout` objects for full lines instead of runs. These mostly borrow from the `ParLayout` and only reshape the first and last run if necessary. The design allows to use Harfbuzz's UNSAFE_TO_BREAK mechanism to make reshaping more efficient. The size of a `LineLayout` can be measured without building the line's frame. 5. Build only the selected line's frames and stack them.
Diffstat (limited to 'src/layout/stack.rs')
-rw-r--r--src/layout/stack.rs43
1 files changed, 22 insertions, 21 deletions
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index b9de1bbc..8e2249d2 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -116,39 +116,40 @@ impl StackLayouter {
size = Size::new(width, width / aspect);
}
- size.switch(self.main)
+ size
};
- let mut output = Frame::new(full_size.switch(self.main).to_size(), Length::ZERO);
- let mut baseline = None;
+ let mut output = Frame::new(full_size, full_size.height);
+ let mut first = true;
+ let full_size = full_size.switch(self.main);
for (before, frame, aligns) in std::mem::take(&mut self.frames) {
let child_size = frame.size.switch(self.main);
// Align along the main axis.
- let main = aligns.main.resolve(if self.dirs.main.is_positive() {
- let after_with_self = self.size.main - before;
- before .. full_size.main - after_with_self
- } else {
- let before_with_self = before + child_size.main;
- let after = self.size.main - (before + child_size.main);
- full_size.main - before_with_self .. after
- });
+ let main = aligns.main.resolve(
+ self.dirs.main,
+ if self.dirs.main.is_positive() {
+ before .. before + full_size.main - self.size.main
+ } else {
+ self.size.main - (before + child_size.main)
+ .. full_size.main - (before + child_size.main)
+ },
+ );
// Align along the cross axis.
- let cross = aligns.cross.resolve(if self.dirs.cross.is_positive() {
- Length::ZERO .. full_size.cross - child_size.cross
- } else {
- full_size.cross - child_size.cross .. Length::ZERO
- });
+ let cross = aligns.cross.resolve(
+ self.dirs.cross,
+ Length::ZERO .. full_size.cross - child_size.cross,
+ );
let pos = Gen::new(main, cross).switch(self.main).to_point();
- baseline.get_or_insert(pos.y + frame.baseline);
- output.push_frame(pos, frame);
- }
+ if first {
+ output.baseline = pos.y + frame.baseline;
+ first = false;
+ }
- if let Some(baseline) = baseline {
- output.baseline = baseline;
+ output.push_frame(pos, frame);
}
self.finished.push(output);