Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions parley/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ impl<'b, B: Brush> TreeBuilder<'b, B> {

#[inline]
pub fn build_into(self, layout: &mut Layout<B>) -> String {
let root_text_wrap_mode = self.lcx.tree_style_builder.root_text_wrap_mode();

// Apply TreeStyleBuilder styles to LayoutContext.
let text = self
.lcx
Expand All @@ -299,6 +301,11 @@ impl<'b, B: Brush> TreeBuilder<'b, B> {
// Call generic layout builder method
build_into_layout(layout, &text, self.lcx, self.fcx, self.options);

// The tree builder knows its root style, which is a better source for the default
// wrap mode than the first entry of the style table (which may be a synthesized
// default style when the layout contains no text).
layout.data.default_text_wrap_mode = root_text_wrap_mode;

text
}

Expand Down Expand Up @@ -358,6 +365,13 @@ fn build_into_layout<B: Brush>(
.styles
.extend(lcx.style_table.iter().map(|s| s.as_layout_style()));

// Record the wrap mode which applies at the start of the text
layout.data.default_text_wrap_mode = lcx
.style_table
.first()
.map(|s| s.text_wrap_mode)
.unwrap_or_default();

// Sort the inline boxes as subsequent code assumes that they are in text index order.
// Note: It's important that this is a stable sort to allow users to control the order of contiguous inline boxes
lcx.inline_boxes.sort_by_key(|b| b.index);
Expand Down
8 changes: 7 additions & 1 deletion parley/src/layout/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ pub(crate) struct LayoutData<B: Brush> {
// Output of style resolution (input to line breaking)
pub(crate) styles: Vec<Style<B>>,
pub(crate) inline_boxes: Vec<InlineBox>,
/// The wrap mode that applies before any text cluster has been processed. This is
/// used to determine whether line breaks are allowed around inline boxes that occur
/// before any text (or in layouts that contain no text at all).
pub(crate) default_text_wrap_mode: TextWrapMode,

// Output of shaping (input to line breaking)
pub(crate) shaped_text: ShapedText,
Expand Down Expand Up @@ -230,6 +234,7 @@ impl<B: Brush> Default for LayoutData<B> {
height: 0.,
styles: Vec::new(),
inline_boxes: Vec::new(),
default_text_wrap_mode: TextWrapMode::Wrap,
shaped_text: ShapedText::new(),
runs: Vec::new(),
items: Vec::new(),
Expand All @@ -256,6 +261,7 @@ impl<B: Brush> LayoutData<B> {
self.height = 0.;
self.styles.clear();
self.inline_boxes.clear();
self.default_text_wrap_mode = TextWrapMode::Wrap;
self.shaped_text.clear();
self.runs.clear();
self.items.clear();
Expand Down Expand Up @@ -370,7 +376,7 @@ impl<B: Brush> LayoutData<B> {

let mut running_min_width = 0.0;
let mut running_max_width = 0.0;
let mut text_wrap_mode = TextWrapMode::Wrap;
let mut text_wrap_mode = self.default_text_wrap_mode;
// The whitespace class of the previous atom's first character, and the atom's advance.
let mut prev_atom: Option<(Whitespace, f32)> = None;
let is_rtl = self.base_level.is_rtl();
Expand Down
12 changes: 9 additions & 3 deletions parley/src/layout/line_break.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,10 +453,14 @@ impl<'a, B: Brush> BreakLines<'a, B> {
lines.swap(&mut layout.data);
lines.lines.clear();
lines.line_items.clear();
// Initialize the wrap mode from the layout's default wrap mode so that inline
// boxes which occur before any text cluster use the correct wrap mode.
let mut state = BreakerState::default();
state.line.text_wrap_mode = layout.data.default_text_wrap_mode;
Self {
layout,
lines,
state: BreakerState::default(),
state,
prev_state: None,
done: false,
}
Expand Down Expand Up @@ -680,8 +684,10 @@ impl<'a, B: Brush> BreakLines<'a, B> {
self.layout.data.quantize,
);

// We can always line break after an inline box
self.state.mark_line_break_opportunity();
// We can line break after an inline box unless wrapping is disabled
if self.state.line.text_wrap_mode == TextWrapMode::Wrap {
self.state.mark_line_break_opportunity();
}
} else {
// If we're at the start of the line, this box will never fit, so consume it and accept the overflow.
let reason = if self.state.line.x == 0.0 {
Expand Down
9 changes: 8 additions & 1 deletion parley/src/resolve/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! Hierarchical tree based style application.
use alloc::{string::String, vec::Vec};

use crate::style::WhiteSpaceCollapse;
use crate::style::{TextWrapMode, WhiteSpaceCollapse};

use super::{Brush, ResolvedProperty, ResolvedStyle, StyleRun};

Expand Down Expand Up @@ -40,6 +40,13 @@ impl<B: Brush> TreeStyleBuilder<B> {
fn current_style(&self) -> ResolvedStyle<B> {
self.tree[self.current_span].style.clone()
}

pub(crate) fn root_text_wrap_mode(&self) -> TextWrapMode {
self.tree
.first()
.map(|node| node.style.text_wrap_mode)
.unwrap_or(TextWrapMode::Wrap)
}
}

impl<B: Brush> Default for TreeStyleBuilder<B> {
Expand Down