Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ab69865
Use local parley and taffy
nicoburns Sep 22, 2025
c00d61c
Float layout WIP
nicoburns Sep 22, 2025
852e35b
Create subcontext to account for inline layout padding/border
nicoburns Oct 14, 2025
1928e1a
Abspos in inline fixes
nicoburns Oct 14, 2025
4ffa810
Apply inline line's line_x
nicoburns Oct 15, 2025
6f9c9a0
Ensure that root-bfc inline layouts get the correct width set
nicoburns Oct 15, 2025
d4975bf
Align lines using their own max-width
nicoburns Oct 15, 2025
e4169e5
Account for padding/border for float-in-inline position
nicoburns Oct 22, 2025
9abf26e
Account for floats when computing intrinsic size of inline context
nicoburns Oct 24, 2025
a7cd8d9
Use positioned_glyphs
nicoburns Oct 30, 2025
01ba767
Use Cluster::from_point_exact for hit testing spans
nicoburns Oct 30, 2025
374f03e
Pass 0.0 line_height when appending a float to the line
nicoburns Oct 31, 2025
376c289
Add MaxHeightExceeded case
nicoburns Oct 31, 2025
55f4fc6
Fast path for case where there are no active floats
nicoburns Nov 3, 2025
b2f108d
Feature flag float support
nicoburns Nov 3, 2025
001819d
Enable floats by default for readme app
nicoburns Nov 3, 2025
ae13c73
Fixup float feature flagging
nicoburns Nov 3, 2025
4f3a4ed
Enable caching for same-bfc blocks
nicoburns Nov 3, 2025
dd547b1
Enable floats in just commands
nicoburns Nov 3, 2025
b726e2a
Enable float layout in the WPT runner
nicoburns Nov 3, 2025
682fb6c
Fixup: feature flag imports
nicoburns Nov 3, 2025
596bf40
Debug print line boxes
nicoburns Nov 3, 2025
eaf07b9
Simplify inline float size logic
nicoburns Nov 3, 2025
4ab4899
WIP Don't use leaf layout for inline layout
nicoburns Nov 4, 2025
8626389
Fixup inline content size scale
nicoburns Nov 5, 2025
0c18bf7
More don't use leaf layout WIP
nicoburns Nov 5, 2025
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
8 changes: 2 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ dioxus-html = { version = "0.7" }
dioxus-hooks = { version = "0.7" }
dioxus-cli-config = { version = "0.7" }
dioxus-devtools = { version = "0.7" }
taffy = { version = "0.9", default-features = false, features = ["std", "flexbox", "grid", "block_layout", "content_size", "calc"] }
taffy = { path = "../taffy", default-features = false, features = ["std", "flexbox", "grid", "block_layout", "content_size", "calc"] }

# AnyRender
anyrender = { version = "0.6" }
Expand All @@ -85,7 +85,7 @@ color = "0.3"
linebender_resource_handle = "0.1"
peniko = "0.5.0"
kurbo = "0.12"
parley = { git = "https://github.com/linebender/parley", rev = "37a99ef6e30d387035a2d3afe5d31aeb0ddab637", default-features = false, features = ["std"] }
parley = { path = "../parley/parley", default-features = false, features = ["std"] }
skrifa = { version = "0.37", default-features = false, features = ["std"] } # Should match parley version
wgpu = "26"
softbuffer = "0.4"
Expand Down
6 changes: 5 additions & 1 deletion apps/readme/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ rust-version.workspace = true
publish = false

[features]
default = ["gpu", "comrak"]
default = ["gpu", "comrak", "floats"]

# Renderers
gpu = ["dep:anyrender_vello"]
hybrid = ["dep:anyrender_vello_hybrid"]
skia = ["dep:anyrender_skia"]
Expand All @@ -17,9 +19,11 @@ cpu = ["cpu-pixels"]
cpu-pixels = ["cpu-base", "anyrender_vello_cpu/pixels_window_renderer"]
cpu-softbuffer = ["cpu-base", "anyrender_vello_cpu/softbuffer_window_renderer"]
cpu-base = ["dep:anyrender_vello_cpu"]

avif = ["dep:image", "image?/avif-native"]
comrak = ["dep:comrak"]
pulldown_cmark = ["dep:pulldown-cmark"]
floats = ["blitz-dom/floats"]
log_frame_times = [
"anyrender_vello_cpu?/log_frame_times",
"anyrender_vello_hybrid?/log_frame_times",
Expand Down
12 changes: 6 additions & 6 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@ dev *ARGS:
cargo run --package readme --features log_frame_times,log_phase_times {{ARGS}}

incr *ARGS:
cargo run --release --package readme --features incremental,log_frame_times,log_phase_times {{ARGS}}
cargo run --release --package readme --features incremental,comrak,floats,log_frame_times,log_phase_times {{ARGS}}

cpu *ARGS:
cargo run --release --package readme --no-default-features --features cpu,comrak,incremental,log_frame_times,log_phase_times {{ARGS}}
cargo run --release --package readme --no-default-features --features cpu,comrak,incremental,floats,log_frame_times,log_phase_times {{ARGS}}

hybrid *ARGS:
cargo run --release --package readme --no-default-features --features hybrid,comrak,incremental,log_frame_times,log_phase_times {{ARGS}}
cargo run --release --package readme --no-default-features --features hybrid,comrak,incremental,floats,log_frame_times,log_phase_times {{ARGS}}

skia *ARGS:
cargo run --release --package readme --no-default-features --features skia,comrak,incremental,log_frame_times,log_phase_times {{ARGS}}
cargo run --release --package readme --no-default-features --features skia,comrak,incremental,floats,log_frame_times,log_phase_times {{ARGS}}

skia-pixels *ARGS:
cargo run --release --package readme --no-default-features --features skia-pixels,comrak,incremental,log_frame_times,log_phase_times {{ARGS}}
cargo run --release --package readme --no-default-features --features skia-pixels,comrak,floats,incremental,log_frame_times,log_phase_times {{ARGS}}

skia-softbuffer *ARGS:
cargo run --release --package readme --no-default-features --features skia-softbuffer,comrak,incremental,log_frame_times,log_phase_times {{ARGS}}
cargo run --release --package readme --no-default-features --features skia-softbuffer,comrak,floats,incremental,log_frame_times,log_phase_times {{ARGS}}

## TodoMVC commands

Expand Down
1 change: 1 addition & 0 deletions packages/blitz-dom/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ woff-rust = ["dep:wuff"]
accessibility = ["accesskit"]
system_fonts = ["parley/system"]
autofocus = []
floats = ["taffy/float_layout", "stylo_taffy/floats"]
file_input = []
incremental = []
parallel-construct = []
Expand Down
7 changes: 6 additions & 1 deletion packages/blitz-dom/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ impl BaseDocument {
println!();
println!("Lines:");
for (i, line) in inline_layout.layout.lines().enumerate() {
println!("Line {i}:");
let metrics = line.metrics();
let x = metrics.inline_min_coord;
let y = metrics.block_min_coord;
let w = metrics.inline_max_coord - metrics.inline_min_coord;
let h = metrics.block_max_coord - metrics.block_min_coord;
println!("Line {i}: x:{x} y:{y} width:{w} height:{h}");
for item in line.items() {
print!(" ");
match item {
Expand Down
106 changes: 89 additions & 17 deletions packages/blitz-dom/src/layout/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ use std::sync::Arc;

use markup5ever::{QualName, local_name, ns};
use parley::{
FontContext, InlineBox, LayoutContext, StyleProperty, TreeBuilder, WhiteSpaceCollapse,
FontContext, InlineBox, InlineBoxKind, LayoutContext, StyleProperty, TreeBuilder,
WhiteSpaceCollapse,
};
use slab::Slab;
use style::{
computed_values::position::T as PositionProperty,
data::ElementData as StyloElementData,
selector_parser::RestyleDamage,
shared_lock::StylesheetGuards,
values::{
computed::{Content, ContentItem, Display},
computed::{Content, ContentItem, Display, Float},
specified::box_::{DisplayInside, DisplayOutside},
},
};
Expand Down Expand Up @@ -60,6 +62,21 @@ fn push_children_and_pseudos(layout_children: &mut Vec<usize>, node: &Node) {
}
}

fn push_non_whitespace_children_and_pseudos(layout_children: &mut Vec<usize>, node: &Node) {
if let Some(before) = node.before {
layout_children.push(before);
}
layout_children.extend(
node.children
.iter()
.copied()
.filter(|child_id| !node.with(*child_id).is_whitespace_node()),
);
if let Some(after) = node.after {
layout_children.push(after);
}
}

/// Convert a relative line height to an absolute one
fn resolve_line_height(line_height: parley::LineHeight, font_size: f32) -> f32 {
match line_height {
Expand Down Expand Up @@ -190,6 +207,7 @@ pub(crate) fn collect_layout_children(
// TODO: make "all_inline" detection work in the presence of display:contents nodes
let mut all_block = true;
let mut all_inline = true;
let mut all_out_of_flow = true;
let mut has_contents = false;
for child in doc.nodes[container_node_id]
.children
Expand All @@ -198,10 +216,37 @@ pub(crate) fn collect_layout_children(
.map(|child_id| &doc.nodes[child_id])
{
// Unwraps on Text and SVG nodes
let display = child.display_style().unwrap_or(Display::inline());
let style = child.primary_styles();
let style = style.as_ref();
let display = style
.map(|s| s.clone_display())
.unwrap_or(Display::inline());
if matches!(display.inside(), DisplayInside::Contents) {
has_contents = true;
all_out_of_flow = false;
} else {
let position = style
.map(|s| s.clone_position())
.unwrap_or(PositionProperty::Static);
let float = style.map(|s| s.clone_float()).unwrap_or(Float::None);

// Ignore nodes that are entirely whitespace
if child.is_whitespace_node() {
continue;
}

let is_in_flow = matches!(
position,
PositionProperty::Static
| PositionProperty::Relative
| PositionProperty::Sticky
) && matches!(float, Float::None);

if !is_in_flow {
continue;
}

all_out_of_flow = false;
match display.outside() {
DisplayOutside::None => {}
DisplayOutside::Block
Expand All @@ -219,6 +264,13 @@ pub(crate) fn collect_layout_children(
}
}

if all_out_of_flow {
return push_non_whitespace_children_and_pseudos(
layout_children,
&doc.nodes[container_node_id],
);
}

// TODO: fix display:contents
if all_inline {
let existing_layout = doc.nodes[container_node_id]
Expand All @@ -241,7 +293,12 @@ pub(crate) fn collect_layout_children(

// If the children are either all inline or all block then simply return the regular children
// as the layout children
if (all_block | all_inline) & !has_contents {
if all_block & !has_contents {
return push_non_whitespace_children_and_pseudos(
layout_children,
&doc.nodes[container_node_id],
);
} else if all_inline & !has_contents {
return push_children_and_pseudos(layout_children, &doc.nodes[container_node_id]);
}

Expand Down Expand Up @@ -273,7 +330,10 @@ pub(crate) fn collect_layout_children(
});

if !has_text_node_or_contents {
return push_children_and_pseudos(layout_children, &doc.nodes[container_node_id]);
return push_non_whitespace_children_and_pseudos(
layout_children,
&doc.nodes[container_node_id],
);
}

fn flex_or_grid_item_needs_wrap(
Expand Down Expand Up @@ -314,7 +374,10 @@ pub(crate) fn collect_layout_children(
}

_ => {
push_children_and_pseudos(layout_children, &doc.nodes[container_node_id]);
push_non_whitespace_children_and_pseudos(
layout_children,
&doc.nodes[container_node_id],
);
}
}
}
Expand Down Expand Up @@ -415,10 +478,7 @@ fn collect_complex_layout_children(
fn block_is_only_whitespace(doc: &BaseDocument, node_id: usize) -> bool {
for child_id in doc.nodes[node_id].children.iter().copied() {
let child = &doc.nodes[child_id];
if child
.text_data()
.is_none_or(|text_data| !text_data.content.chars().all(|c| c.is_ascii_whitespace()))
{
if !child.is_whitespace_node() {
return false;
}
}
Expand All @@ -442,10 +502,7 @@ fn collect_complex_layout_children(
child_display.outside()
};

let is_whitespace_node = match &doc.nodes[child_id].data {
NodeData::Text(data) => data.content.chars().all(|c| c.is_ascii_whitespace()),
_ => false,
};
let is_whitespace_node = doc.nodes[child_id].is_whitespace_node();

// Skip comment nodes. Note that we do *not* skip `Display::None` nodes as they may need to be hidden.
// Taffy knows how to deal with `Display::None` children.
Expand Down Expand Up @@ -806,10 +863,12 @@ pub(crate) fn build_inline_layout_into(
// Set layout_parent for node.
node.layout_parent.set(Some(parent_id));

let style = node.primary_styles();
let style = style.as_ref();

// Set whitespace collapsing mode
let collapse_mode = node
.primary_styles()
.map(|s| s.get_inherited_text().white_space_collapse)
let collapse_mode = style
.map(|s| s.clone_white_space_collapse())
.map(stylo_to_parley::white_space_collapse)
.unwrap_or(collapse_mode);
builder.set_white_space_mode(collapse_mode);
Expand All @@ -824,6 +883,15 @@ pub(crate) fn build_inline_layout_into(
}

let display = node.display_style().unwrap_or(Display::inline());
let position = style
.map(|s| s.clone_position())
.unwrap_or(PositionProperty::Static);
let float = style.map(|s| s.clone_float()).unwrap_or(Float::None);
let box_kind = if position.is_absolutely_positioned() || float.is_floating() {
InlineBoxKind::OutOfFlow
} else {
InlineBoxKind::InFlow
};

match (display.outside(), display.inside()) {
(DisplayOutside::None, DisplayInside::None) => {
Expand Down Expand Up @@ -853,6 +921,8 @@ pub(crate) fn build_inline_layout_into(
{
builder.push_inline_box(InlineBox {
id: node_id as u64,
kind: box_kind,
break_on_box: false,
// Overridden by push_inline_box method
index: 0,
// Width and height are set during layout
Expand Down Expand Up @@ -929,6 +999,8 @@ pub(crate) fn build_inline_layout_into(
(_, _) => {
builder.push_inline_box(InlineBox {
id: node_id as u64,
kind: box_kind,
break_on_box: false,
// Overridden by push_inline_box method
index: 0,
// Width and height are set during layout
Expand Down
Loading
Loading