initial macos porting work
This commit is contained in:
@@ -128,7 +128,12 @@ impl LayoutNode {
|
||||
/// Return a copy of this node (and all descendants) with all positions shifted by `offset`.
|
||||
pub fn translated(self, offset: Point) -> Self {
|
||||
fn translate_rect(r: Rect, o: Point) -> Rect {
|
||||
Rect::new(r.origin.x + o.x, r.origin.y + o.y, r.size.width, r.size.height)
|
||||
Rect::new(
|
||||
r.origin.x + o.x,
|
||||
r.origin.y + o.y,
|
||||
r.size.width,
|
||||
r.size.height,
|
||||
)
|
||||
}
|
||||
let rect = translate_rect(self.rect, offset);
|
||||
let clip_rect = self.clip_rect.map(|r| translate_rect(r, offset));
|
||||
@@ -143,7 +148,11 @@ impl LayoutNode {
|
||||
rect: translate_rect(img.rect, offset),
|
||||
..img
|
||||
});
|
||||
let children = self.children.into_iter().map(|c| c.translated(offset)).collect();
|
||||
let children = self
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|c| c.translated(offset))
|
||||
.collect();
|
||||
LayoutNode {
|
||||
rect,
|
||||
clip_rect,
|
||||
@@ -241,6 +250,7 @@ pub fn layout_snapshot_with_cache(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn layout_element(
|
||||
element: &Element,
|
||||
rect: Rect,
|
||||
@@ -256,24 +266,24 @@ fn layout_element(
|
||||
perf_stats.nodes += 1;
|
||||
|
||||
// Viewport culling: skip fully off-screen elements inside a scroll box.
|
||||
if let Some(clip) = clip_rect {
|
||||
if !rects_overlap(rect, clip) {
|
||||
perf_stats.viewport_culled += 1;
|
||||
return LayoutNode {
|
||||
path,
|
||||
element_id: element.id,
|
||||
rect,
|
||||
corner_radius: 0.0,
|
||||
clip_rect: None,
|
||||
pointer_events: element.style.pointer_events,
|
||||
focusable: element.style.focusable,
|
||||
cursor: element.style.cursor.unwrap_or(CursorIcon::Default),
|
||||
scroll_metrics: None,
|
||||
prepared_image: None,
|
||||
prepared_text: None,
|
||||
children: Vec::new(),
|
||||
};
|
||||
}
|
||||
if let Some(clip) = clip_rect
|
||||
&& !rects_overlap(rect, clip)
|
||||
{
|
||||
perf_stats.viewport_culled += 1;
|
||||
return LayoutNode {
|
||||
path,
|
||||
element_id: element.id,
|
||||
rect,
|
||||
corner_radius: 0.0,
|
||||
clip_rect: None,
|
||||
pointer_events: element.style.pointer_events,
|
||||
focusable: element.style.focusable,
|
||||
cursor: element.style.cursor.unwrap_or(CursorIcon::Default),
|
||||
scroll_metrics: None,
|
||||
prepared_image: None,
|
||||
prepared_text: None,
|
||||
children: Vec::new(),
|
||||
};
|
||||
}
|
||||
|
||||
// Incremental layout cache check.
|
||||
@@ -368,7 +378,13 @@ fn layout_element(
|
||||
if pushed_clip {
|
||||
scene.pop_clip();
|
||||
}
|
||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
||||
cache_layout(
|
||||
layout_cache,
|
||||
cache_key,
|
||||
&interaction,
|
||||
&scene.items[scene_start..],
|
||||
rect.origin,
|
||||
);
|
||||
return interaction;
|
||||
}
|
||||
|
||||
@@ -382,7 +398,13 @@ fn layout_element(
|
||||
if pushed_clip {
|
||||
scene.pop_clip();
|
||||
}
|
||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
||||
cache_layout(
|
||||
layout_cache,
|
||||
cache_key,
|
||||
&interaction,
|
||||
&scene.items[scene_start..],
|
||||
rect.origin,
|
||||
);
|
||||
return interaction;
|
||||
}
|
||||
|
||||
@@ -508,7 +530,13 @@ fn layout_element(
|
||||
if pushed_clip {
|
||||
scene.pop_clip();
|
||||
}
|
||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
||||
cache_layout(
|
||||
layout_cache,
|
||||
cache_key,
|
||||
&interaction,
|
||||
&scene.items[scene_start..],
|
||||
rect.origin,
|
||||
);
|
||||
return interaction;
|
||||
}
|
||||
|
||||
@@ -518,7 +546,13 @@ fn layout_element(
|
||||
if pushed_clip {
|
||||
scene.pop_clip();
|
||||
}
|
||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
||||
cache_layout(
|
||||
layout_cache,
|
||||
cache_key,
|
||||
&interaction,
|
||||
&scene.items[scene_start..],
|
||||
rect.origin,
|
||||
);
|
||||
return interaction;
|
||||
}
|
||||
|
||||
@@ -527,7 +561,13 @@ fn layout_element(
|
||||
if pushed_clip {
|
||||
scene.pop_clip();
|
||||
}
|
||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
||||
cache_layout(
|
||||
layout_cache,
|
||||
cache_key,
|
||||
&interaction,
|
||||
&scene.items[scene_start..],
|
||||
rect.origin,
|
||||
);
|
||||
return interaction;
|
||||
}
|
||||
interaction.children = layout_container_children(
|
||||
@@ -545,7 +585,13 @@ fn layout_element(
|
||||
scene.pop_clip();
|
||||
}
|
||||
|
||||
cache_layout(layout_cache, cache_key, &interaction, &scene.items[scene_start..], rect.origin);
|
||||
cache_layout(
|
||||
layout_cache,
|
||||
cache_key,
|
||||
&interaction,
|
||||
&scene.items[scene_start..],
|
||||
rect.origin,
|
||||
);
|
||||
interaction
|
||||
}
|
||||
|
||||
@@ -558,10 +604,13 @@ fn cache_layout(
|
||||
origin: Point,
|
||||
) {
|
||||
let neg = Point::new(-origin.x, -origin.y);
|
||||
cache.results.insert(key, CachedLayout {
|
||||
interaction_node: interaction.clone().translated(neg),
|
||||
scene_items: scene_items.iter().map(|i| i.translated(neg)).collect(),
|
||||
});
|
||||
cache.results.insert(
|
||||
key,
|
||||
CachedLayout {
|
||||
interaction_node: interaction.clone().translated(neg),
|
||||
scene_items: scene_items.iter().map(|i| i.translated(neg)).collect(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn hit_path_node(node: &LayoutNode, point: crate::scene::Point) -> Option<Vec<HitTarget>> {
|
||||
@@ -735,6 +784,7 @@ struct MeasuredChild {
|
||||
is_flex: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn layout_container_children(
|
||||
element: &Element,
|
||||
content: Rect,
|
||||
@@ -986,7 +1036,14 @@ fn intrinsic_size(
|
||||
return cached;
|
||||
}
|
||||
let insets = content_insets(&element.style);
|
||||
let result = intrinsic_size_inner(element, available_size, insets, text_system, perf_stats, layout_cache);
|
||||
let result = intrinsic_size_inner(
|
||||
element,
|
||||
available_size,
|
||||
insets,
|
||||
text_system,
|
||||
perf_stats,
|
||||
layout_cache,
|
||||
);
|
||||
layout_cache.intrinsic_cache.insert(cache_key, result);
|
||||
result
|
||||
}
|
||||
@@ -1047,8 +1104,13 @@ fn intrinsic_size_inner(
|
||||
);
|
||||
}
|
||||
|
||||
let intrinsic_content =
|
||||
intrinsic_container_content_size(element, content_size, text_system, perf_stats, layout_cache);
|
||||
let intrinsic_content = intrinsic_container_content_size(
|
||||
element,
|
||||
content_size,
|
||||
text_system,
|
||||
perf_stats,
|
||||
layout_cache,
|
||||
);
|
||||
|
||||
UiSize::new(
|
||||
explicit_width
|
||||
@@ -1539,9 +1601,9 @@ fn child_rect(
|
||||
mod tests {
|
||||
use super::{LayoutCache, layout_scene, layout_snapshot, layout_snapshot_with_cache};
|
||||
use crate::scene::{Color, DisplayItem, Point, Quad, Rect, UiSize};
|
||||
use crate::text::TextSystem;
|
||||
use crate::text::{TextStyle, TextWrap};
|
||||
use crate::tree::{Edges, Element, ElementId, FlexDirection};
|
||||
use crate::text::TextSystem;
|
||||
|
||||
#[test]
|
||||
fn row_layout_apportions_fixed_and_flex_children() {
|
||||
@@ -2156,10 +2218,12 @@ mod tests {
|
||||
let size = UiSize::new(400.0, 600.0);
|
||||
let snap1 = layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);
|
||||
let snap2 = layout_snapshot_with_cache(2, size, &root, &mut text_system, &mut layout_cache);
|
||||
assert_eq!(snap1.scene.items, snap2.scene.items, "scene items should be identical");
|
||||
assert_eq!(
|
||||
snap1.interaction_tree.root,
|
||||
snap2.interaction_tree.root,
|
||||
snap1.scene.items, snap2.scene.items,
|
||||
"scene items should be identical"
|
||||
);
|
||||
assert_eq!(
|
||||
snap1.interaction_tree.root, snap2.interaction_tree.root,
|
||||
"interaction trees should be identical"
|
||||
);
|
||||
}
|
||||
@@ -2198,12 +2262,14 @@ mod tests {
|
||||
let item_height = 40.0;
|
||||
let viewport_height = 200.0;
|
||||
let n = 100;
|
||||
let mut scroll_box = Element::scroll_box(0.0).width(400.0).height(viewport_height);
|
||||
let mut scroll_box = Element::scroll_box(0.0)
|
||||
.width(400.0)
|
||||
.height(viewport_height);
|
||||
for i in 0..n {
|
||||
scroll_box = scroll_box.child(
|
||||
Element::new()
|
||||
.height(item_height)
|
||||
.child(Element::text(format!("item {i}"), text_style()))
|
||||
.child(Element::text(format!("item {i}"), text_style())),
|
||||
);
|
||||
}
|
||||
let root = Element::new().child(scroll_box);
|
||||
@@ -2214,11 +2280,20 @@ mod tests {
|
||||
let snap = layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);
|
||||
|
||||
// Only text items within the 200px viewport should appear in the scene.
|
||||
let text_items = snap.scene.items.iter().filter(|item| {
|
||||
matches!(item, DisplayItem::Text(_))
|
||||
}).count();
|
||||
let text_items = snap
|
||||
.scene
|
||||
.items
|
||||
.iter()
|
||||
.filter(|item| matches!(item, DisplayItem::Text(_)))
|
||||
.count();
|
||||
// At most 6 text items should be visible (5 fit + 1 partial).
|
||||
assert!(text_items <= 6, "expected <= 6 text items in scene, got {text_items} (culling not working)");
|
||||
assert!(text_items >= 4, "expected >= 4 text items visible, got {text_items}");
|
||||
assert!(
|
||||
text_items <= 6,
|
||||
"expected <= 6 text items in scene, got {text_items} (culling not working)"
|
||||
);
|
||||
assert!(
|
||||
text_items >= 4,
|
||||
"expected >= 4 text items visible, got {text_items}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user