Example 02 realized
This commit is contained in:
@@ -116,6 +116,10 @@ impl InteractionTree {
|
||||
pub fn scroll_metrics_for_element(&self, element_id: ElementId) -> Option<&ScrollMetrics> {
|
||||
scroll_metrics_for_element_node(&self.root, element_id)
|
||||
}
|
||||
|
||||
pub fn rect_for_element(&self, element_id: ElementId) -> Option<Rect> {
|
||||
rect_for_element_node(&self.root, element_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layout_snapshot(version: u64, logical_size: UiSize, root: &Element) -> LayoutSnapshot {
|
||||
@@ -309,7 +313,9 @@ fn layout_element(
|
||||
perf_stats,
|
||||
);
|
||||
let provisional_content_height = content_size.height.max(viewport_rect.size.height);
|
||||
let mut offset_y = scroll_box.offset_y.max(0.0);
|
||||
let provisional_max_offset_y =
|
||||
(provisional_content_height - viewport_rect.size.height).max(0.0);
|
||||
let mut offset_y = scroll_box.offset_y.max(0.0).min(provisional_max_offset_y);
|
||||
|
||||
if viewport_rect.size.width > 0.0 && viewport_rect.size.height > 0.0 {
|
||||
scene.push_clip(viewport_rect, 0.0);
|
||||
@@ -509,6 +515,18 @@ fn scroll_metrics_for_element_node(
|
||||
None
|
||||
}
|
||||
|
||||
fn rect_for_element_node(node: &LayoutNode, element_id: ElementId) -> Option<Rect> {
|
||||
if node.element_id == Some(element_id) {
|
||||
return Some(node.rect);
|
||||
}
|
||||
for child in &node.children {
|
||||
if let Some(rect) = rect_for_element_node(child, element_id) {
|
||||
return Some(rect);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn point_hits_node_shape(node: &LayoutNode, point: crate::scene::Point) -> bool {
|
||||
node.rect.contains(point)
|
||||
&& (node.corner_radius <= 0.0
|
||||
@@ -1598,6 +1616,51 @@ mod tests {
|
||||
assert!(scroll_metrics.scrollbar_thumb.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scroll_box_clamps_stale_offset_before_laying_out_reflowed_content() {
|
||||
let scrollbox_id = ElementId::new(19);
|
||||
let root = Element::column().child(
|
||||
Element::scroll_box(240.0)
|
||||
.id(scrollbox_id)
|
||||
.width(320.0)
|
||||
.height(120.0)
|
||||
.padding(Edges::all(8.0))
|
||||
.child(Element::paragraph(
|
||||
"When the viewport becomes wider, wrapped scroll-box content can get much \
|
||||
shorter. Layout should clamp any now-invalid stale offset before positioning \
|
||||
the children so the viewport does not open up an empty gap at the bottom.",
|
||||
TextStyle::new(16.0, Color::rgb(0xFF, 0xFF, 0xFF))
|
||||
.with_line_height(22.0)
|
||||
.with_wrap(TextWrap::Word),
|
||||
)),
|
||||
);
|
||||
|
||||
let snapshot = layout_snapshot(1, UiSize::new(360.0, 220.0), &root);
|
||||
let scroll_metrics = snapshot
|
||||
.interaction_tree
|
||||
.scroll_metrics_for_element(scrollbox_id)
|
||||
.expect("scroll box should expose scroll metrics");
|
||||
let visible_text = snapshot
|
||||
.scene
|
||||
.items
|
||||
.iter()
|
||||
.find_map(|item| match item {
|
||||
DisplayItem::Text(text) => Some(text),
|
||||
_ => None,
|
||||
})
|
||||
.expect("scroll box should still emit text");
|
||||
|
||||
let text_bounds = visible_text
|
||||
.bounds
|
||||
.expect("prepared text should expose bounds for wrapped content");
|
||||
let text_bottom = visible_text.origin.y + text_bounds.height;
|
||||
let viewport_bottom =
|
||||
scroll_metrics.viewport_rect.origin.y + scroll_metrics.viewport_rect.size.height;
|
||||
|
||||
assert!(scroll_metrics.offset_y <= scroll_metrics.max_offset_y);
|
||||
assert!(text_bottom >= viewport_bottom - 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interaction_tree_hit_test_returns_deepest_pointer_target() {
|
||||
let root = Element::column()
|
||||
|
||||
Reference in New Issue
Block a user