Skip to content
Draft
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
415 changes: 204 additions & 211 deletions Cargo.lock

Large diffs are not rendered by default.

28 changes: 15 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,20 @@ tracing-subscriber = "0.3"
# vello_hybrid = { path = "../vello/sparse_strips/vello_hybrid" }
# vello_common = { path = "../vello/sparse_strips/vello_common" }

# [patch.crates-io]
# style = { path = "../stylo/style", package = "stylo" }
# style_traits = { path = "../stylo/style_traits", package = "stylo_traits" }
# style_atoms = { path = "../stylo/stylo_atoms", package = "stylo_atoms" }
# style_config = { path = "../stylo/stylo_config", package = "stylo_config" }
# style_dom = { path = "../stylo/stylo_dom", package = "stylo_dom" }
# selectors = { path = "../stylo/selectors", package = "selectors" }
[patch."https://github.com/dioxuslabs/taffy"]
taffy = { path = "../taffy" }

# [patch.crates-io]
# [patch."https://github.com/dioxuslabs/taffy"]
# taffy = { path = "../taffy" }
[patch."https://github.com/linebender/parley"]
parley = { path = "/Users/jonathankelley/Development/Tinkering/parley/parley" }
fontique = { path = "/Users/jonathankelley/Development/Tinkering/parley/fontique" }

# [patch."https://github.com/linebender/parley"]
# parley = { path = "../parley/parley" }
# fontique = { path = "../parley/fontique" }
[patch."crates-io"]
taffy = { path = "../taffy" }
parley = { path = "/Users/jonathankelley/Development/Tinkering/parley/parley" }
fontique = { path = "/Users/jonathankelley/Development/Tinkering/parley/fontique" }
style = { path = "/Users/jonathankelley/Development/Tinkering/stylo/style", package = "stylo" }
style_traits = { path = "/Users/jonathankelley/Development/Tinkering/stylo/style_traits", package = "stylo_traits" }
style_atoms = { path = "/Users/jonathankelley/Development/Tinkering/stylo/stylo_atoms", package = "stylo_atoms" }
style_dom = { path = "/Users/jonathankelley/Development/Tinkering/stylo/stylo_dom", package = "stylo_dom" }
style_config = { path = "/Users/jonathankelley/Development/Tinkering/stylo/stylo_static_prefs", package = "stylo_static_prefs" }
selectors = { path = "/Users/jonathankelley/Development/Tinkering/stylo/selectors", package = "selectors" }
11 changes: 5 additions & 6 deletions packages/blitz-dom/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ impl BaseDocument {
println!("Lines:");
for (i, line) in inline_layout.layout.lines().enumerate() {
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;
let x = metrics.offset;
let y = metrics.min_coord;
let w = metrics.advance;
let h = metrics.max_coord - metrics.min_coord;
println!("Line {i}: x:{x} y:{y} width:{w} height:{h}");
for item in line.items() {
print!(" ");
Expand All @@ -63,8 +63,7 @@ impl BaseDocument {
)
}
PositionedLayoutItem::InlineBox(ibox) => print!(
"BOX {:?} (id: {} x: {} y: {} w: {}, h: {})",
ibox.kind,
"BOX (id: {} x: {} y: {} w: {}, h: {})",
ibox.id,
ibox.x.round(),
ibox.y.round(),
Expand Down
106 changes: 100 additions & 6 deletions packages/blitz-dom/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use std::time::Instant;
use style::Atom;
use style::animation::DocumentAnimationSet;
use style::attr::{AttrIdentifier, AttrValue};
use style::data::{ElementData as StyloElementData, ElementStyles};
use style::data::ElementStyles;
use style::media_queries::MediaType;
use style::properties::ComputedValues;
use style::properties::style_structs::Font;
Expand Down Expand Up @@ -267,6 +267,8 @@ pub struct BaseDocument {
pub(crate) changed_nodes: HashSet<usize>,
/// Set of changed nodes for updating the accessibility tree
pub(crate) deferred_construction_nodes: Vec<ConstructionTask>,
/// Node IDs of all position:sticky elements, for efficient recomputation on scroll
pub(crate) sticky_nodes: Vec<usize>,

/// Cache of loaded images, keyed by URL. Allows reusing images across multiple
/// elements without re-fetching from the network.
Expand Down Expand Up @@ -415,6 +417,7 @@ impl BaseDocument {
sub_document_nodes: HashSet::new(),
changed_nodes: HashSet::new(),
deferred_construction_nodes: Vec::new(),
sticky_nodes: Vec::new(),
image_cache: HashMap::new(),
pending_images: HashMap::new(),
pending_critical_resources: HashSet::new(),
Expand Down Expand Up @@ -445,8 +448,10 @@ impl BaseDocument {
}

// Stylo data on the root node container is needed to render the node
let stylo_element_data = StyloElementData {
styles: ElementStyles {
let wrapper = style::data::ElementDataWrapper::default();
{
let mut stylo_element_data = wrapper.borrow_mut();
stylo_element_data.styles = ElementStyles {
primary: Some(
ComputedValues::initial_values_with_font_override(Font::initial_values())
.to_arc(),
Expand Down Expand Up @@ -1101,7 +1106,7 @@ impl BaseDocument {
cb(&mut self.nodes[node_id]);
}

// Takes (x, y) co-ordinates (relative to the )
// Takes (x, y) co-ordinates (page coordinates, i.e. viewport + scroll offset)
pub fn hit(&self, x: f32, y: f32) -> Option<HitResult> {
if TDocument::as_node(&&self.nodes[0])
.first_element_child()
Expand All @@ -1112,9 +1117,92 @@ impl BaseDocument {
return None;
}

// Fixed-position elements are painted at viewport-relative positions (scroll-compensated),
// but hit() receives page coordinates (viewport + scroll). Test fixed children first
// with viewport coordinates so they can be hit correctly when the page is scrolled.
if let Some(hit) = self.hit_fixed_children(x, y) {
return Some(hit);
}

self.root_element().hit(x, y)
}

/// Compute the accumulated position offset for a hoisted child relative to a
/// stacking context root, by walking the layout_parent chain.
fn hoisted_child_offset(&self, hoisted_node_id: usize, root_id: usize) -> (f32, f32) {
let mut x = 0.0f32;
let mut y = 0.0f32;
let mut current = hoisted_node_id;
loop {
let Some(parent_id) = self.nodes[current].layout_parent.get() else {
break;
};
if parent_id == root_id {
break;
}
let parent = &self.nodes[parent_id];
x += parent.final_layout.location.x + parent.sticky_offset.x as f32
- parent.scroll_offset.x as f32;
y += parent.final_layout.location.y + parent.sticky_offset.y as f32
- parent.scroll_offset.y as f32;
current = parent_id;
}
(x, y)
}

/// Test fixed-position children of the root element using viewport coordinates.
/// Fixed elements are painted at viewport-relative positions, so hit testing them
/// requires subtracting the viewport scroll from page coordinates.
fn hit_fixed_children(&self, page_x: f32, page_y: f32) -> Option<HitResult> {
use style::properties::generated::longhands::position::computed_value::T as Position;

let root = self.root_element();
let root_id = root.id;
let vx = page_x - self.viewport_scroll.x as f32;
let vy = page_y - self.viewport_scroll.y as f32;

// Helper closure to test a single child node
let test_child = |node_id: usize, offset_x: f32, offset_y: f32| -> Option<HitResult> {
let child = &self.nodes[node_id];
if child.css_position == Position::Fixed && child.layout_parent.get() == Some(root_id) {
child.hit(vx - offset_x, vy - offset_y)
} else {
None
}
};

// Positive z_index hoisted children (highest priority, painted last)
if let Some(hoisted) = &root.stacking_context {
for hc in hoisted.pos_z_hoisted_children().rev() {
let (ox, oy) = self.hoisted_child_offset(hc.node_id, root_id);
if let Some(hit) = test_child(hc.node_id, ox, oy) {
return Some(hit);
}
}
}

// Regular paint children
if let Some(children) = &*root.paint_children.borrow() {
for &child_id in children.iter().rev() {
if let Some(hit) = test_child(child_id, 0.0, 0.0) {
return Some(hit);
}
}
}

// Negative z_index hoisted children
if let Some(hoisted) = &root.stacking_context {
for hc in hoisted.neg_z_hoisted_children().rev() {
let (ox, oy) = self.hoisted_child_offset(hc.node_id, root_id);
if let Some(hit) = test_child(hc.node_id, ox, oy) {
return Some(hit);
}
}
}

None
}

pub fn focus_next_node(&mut self) -> Option<usize> {
let focussed_node_id = self.get_focussed_node_id()?;
let id = self.next_node(&self.nodes[focussed_node_id], |node| node.is_focussable())?;
Expand Down Expand Up @@ -1505,7 +1593,9 @@ impl BaseDocument {
}

pub fn scroll_viewport_by(&mut self, x: f64, y: f64) {
self.scroll_viewport_by_has_changed(x, y);
if self.scroll_viewport_by_has_changed(x, y) {
self.recompute_sticky_offsets();
}
}

/// Scroll the viewport by the given values
Expand Down Expand Up @@ -1535,11 +1625,15 @@ impl BaseDocument {
scroll_y: f64,
dispatch_event: &mut dyn FnMut(DomEvent),
) -> bool {
if let Some(anchor_node_id) = anchor_node_id {
let changed = if let Some(anchor_node_id) = anchor_node_id {
self.scroll_node_by_has_changed(anchor_node_id, scroll_x, scroll_y, dispatch_event)
} else {
self.scroll_viewport_by_has_changed(scroll_x, scroll_y)
};
if changed {
self.recompute_sticky_offsets();
}
changed
}

pub fn viewport_scroll(&self) -> crate::Point<f64> {
Expand Down
39 changes: 23 additions & 16 deletions packages/blitz-dom/src/layout/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ use std::sync::Arc;

use markup5ever::{QualName, local_name, ns};
use parley::{
FontContext, InlineBox, InlineBoxKind, LayoutContext, StyleProperty, TreeBuilder,
WhiteSpaceCollapse,
FontContext, InlineBox, LayoutContext, StyleProperty, TreeBuilder, WhiteSpaceCollapse,
};
use slab::Slab;
use style::{
computed_values::position::T as PositionProperty,
data::ElementData as StyloElementData,
shared_lock::StylesheetGuards,
values::{
computed::{Content, ContentItem, Display, Float, TextTransform},
Expand Down Expand Up @@ -79,7 +77,7 @@ fn push_non_whitespace_children_and_pseudos(layout_children: &mut Vec<usize>, no
}

/// Convert a relative line height to an absolute one
fn resolve_line_height(line_height: parley::LineHeight, font_size: f32) -> f32 {
pub(super) fn resolve_line_height(line_height: parley::LineHeight, font_size: f32) -> f32 {
match line_height {
parley::LineHeight::FontSizeRelative(relative) => relative * font_size,
parley::LineHeight::Absolute(absolute) => absolute,
Expand Down Expand Up @@ -402,8 +400,9 @@ fn flush_pseudo_elements(doc: &mut BaseDocument, node_id: usize) {
let before_style = style_data
.as_ref()
.and_then(|d| d.styles.pseudos.as_array()[1].clone());
let after_style = style_data
.as_ref()
let after_style = node
.stylo_element_data
.borrow()
.and_then(|d| d.styles.pseudos.as_array()[0].clone());

(before_style, after_style, before_node_id, after_node_id)
Expand Down Expand Up @@ -892,14 +891,16 @@ pub(crate) fn build_inline_layout_into(
.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() {
InlineBoxKind::OutOfFlow
} else if float.is_floating() {
InlineBoxKind::CustomOutOfFlow
} else {
InlineBoxKind::InFlow
};

let _is_out_of_flow = position.is_absolutely_positioned() || float.is_floating();
let (alignment_baseline, baseline_shift, baseline_source) = style
.map(|s| {
(
stylo_to_parley::alignment_baseline(&s),
stylo_to_parley::baseline_shift(&s),
stylo_to_parley::baseline_source(&s),
)
})
.unwrap_or_default();
match (display.outside(), display.inside()) {
(DisplayOutside::None, DisplayInside::None) => {
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
Expand Down Expand Up @@ -929,12 +930,15 @@ pub(crate) fn build_inline_layout_into(
{
builder.push_inline_box(InlineBox {
id: node_id as u64,
kind: box_kind,
// Overridden by push_inline_box method
index: 0,
// Width and height are set during layout
width: 0.0,
height: 0.0,
alignment_baseline,
baseline_shift,
baseline_source,
first_baseline: None,
});
} else if *tag_name == local_name!("br") {
// node.remove_damage(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
Expand Down Expand Up @@ -1009,12 +1013,15 @@ pub(crate) fn build_inline_layout_into(
(_, _) => {
builder.push_inline_box(InlineBox {
id: node_id as u64,
kind: box_kind,
// Overridden by push_inline_box method
index: 0,
// Width and height are set during layout
width: 0.0,
height: 0.0,
alignment_baseline,
baseline_shift,
baseline_source,
first_baseline: None,
});
}
};
Expand Down
Loading
Loading