Skip to content
2 changes: 1 addition & 1 deletion api/rs/slint/private_unstable_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ pub mod re_exports {
};
pub use i_slint_core::item_tree::{
ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, ItemWeak, TraversalOrder,
VisitChildrenResult, visit_item_tree,
VisitChildrenResult, compute_sorted_children_by_z, visit_item_tree,
};
pub use i_slint_core::items::{Transform, *};
pub use i_slint_core::layout::*;
Expand Down
66 changes: 64 additions & 2 deletions internal/compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1594,6 +1594,12 @@ fn generate_item_tree(

let mut item_tree_array: Vec<String> = Default::default();
let mut item_array: Vec<String> = Default::default();
let mut z_sorted_nodes: Vec<(
usize,
Vec<llr::SubComponentInstanceIdx>,
Vec<llr::ZChildSource>,
)> = Vec::new();
let mut current_node_index: usize = 0;

sub_tree.tree.visit_in_array(&mut |node, children_offset, parent_index| {
let parent_index = parent_index as u32;
Expand Down Expand Up @@ -1647,6 +1653,15 @@ fn generate_item_tree(
));
}
}

if let Some(ref z_prop) = node.z_sort_order_property {
z_sorted_nodes.push((
current_node_index,
node.sub_component_path.clone(),
z_prop.clone(),
));
}
current_node_index += 1;
});

let mut visit_children_statements = vec![
Expand Down Expand Up @@ -1674,10 +1689,57 @@ fn generate_item_tree(

visit_children_statements.extend([
"};".into(),
format!("auto self_rc = reinterpret_cast<const {item_tree_class_name}*>(component.instance)->self_weak.lock()->into_dyn();"),
"return slint::cbindgen_private::slint_visit_item_tree(&self_rc, get_item_tree(component) , index, order, visitor, dyn_visit);".to_owned(),
format!("auto self = reinterpret_cast<const {item_tree_class_name}*>(component.instance);"),
"auto self_rc = self->self_weak.lock()->into_dyn();".into(),
]);

if !z_sorted_nodes.is_empty() {
visit_children_statements.push("switch (index) {".into());
for (node_idx, node_sub_component_path, child_z_refs) in &z_sorted_nodes {
let count = child_z_refs.len();
let z_reads: Vec<String> = child_z_refs
.iter()
.map(|z_source| match z_source {
llr::ZChildSource::Constant(val) => format!("{val:.1}f"),
llr::ZChildSource::Property(member_ref) => match member_ref {
llr::MemberReference::Relative { parent_level: 0, local_reference } => {
let full_path: Vec<_> = node_sub_component_path
.iter()
.chain(local_reference.sub_component_path.iter())
.copied()
.collect();
match &local_reference.reference {
llr::LocalMemberIndex::Property(property_index) => {
let (compo_path, sub_component) =
follow_sub_component_path(root, sub_tree.root, &full_path);
let property_name =
ident(&sub_component.properties[*property_index].name);
format!("self->{compo_path}{property_name}.get()")
}
_ => "0.0f".into(),
}
}
_ => "0.0f".into(),
},
})
.collect();
let z_list = z_reads.join(", ");
visit_children_statements.push(format!("case {node_idx}: {{"));
visit_children_statements.push(format!(" float z_values[{count}] = {{{z_list}}};"));
visit_children_statements.push(" slint::SharedVector<uint32_t> sorted;".into());
visit_children_statements.push(format!(" slint::cbindgen_private::slint_compute_sorted_children_by_z(slint::cbindgen_private::Slice<float>{{z_values, {count}}}, &sorted);"));
visit_children_statements.push(" return slint::cbindgen_private::slint_visit_item_tree_with_sorted_children(&self_rc, get_item_tree(component), index, order, visitor, dyn_visit, slint::cbindgen_private::Slice<uint32_t>{sorted.begin(), sorted.size()});".into());
visit_children_statements.push("}".into());
}
visit_children_statements.push("default:".into());
visit_children_statements.push(" return slint::cbindgen_private::slint_visit_item_tree(&self_rc, get_item_tree(component), index, order, visitor, dyn_visit);".into());
visit_children_statements.push("}".into());
} else {
visit_children_statements.push(
"return slint::cbindgen_private::slint_visit_item_tree(&self_rc, get_item_tree(component), index, order, visitor, dyn_visit);".into(),
);
}

target_struct.members.push((
Access::Private,
Declaration::Function(Function {
Expand Down
78 changes: 77 additions & 1 deletion internal/compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1948,10 +1948,23 @@ fn generate_item_tree(
}));
let mut item_tree_array = Vec::new();
let mut item_array = Vec::new();
let mut z_sorted_nodes: Vec<(
usize,
Vec<llr::SubComponentInstanceIdx>,
Vec<llr::ZChildSource>,
)> = Vec::new();
let mut current_node_index: usize = 0;
sub_tree.tree.visit_in_array(&mut |node, children_offset, parent_index| {
let parent_index = parent_index as u32;
let node_idx = current_node_index;
current_node_index += 1;
let (path, component) =
follow_sub_component_path(root, sub_tree.root, &node.sub_component_path);

if let Some(ref z_prop) = node.z_sort_order_property {
z_sorted_nodes.push((node_idx, node.sub_component_path.clone(), z_prop.clone()));
}

match node.item_index {
Either::Right(mut repeater_index) => {
assert_eq!(node.children.len(), 0);
Expand Down Expand Up @@ -2046,6 +2059,69 @@ fn generate_item_tree(
)
};

// Generate visit_children_item body, potentially with z-sorted branches
let z_sorted_visit_body = if z_sorted_nodes.is_empty() {
quote! {
return sp::visit_item_tree(self, &sp::VRcMapped::origin(&self.as_ref().self_weak.get().unwrap().upgrade().unwrap()), self.get_item_tree().as_slice(), index, order, visitor, visit_dynamic, None);
}
} else {
// Generate match arms for z-sorted nodes
let mut z_match_arms = Vec::new();
for (node_idx, node_sub_component_path, child_z_refs) in &z_sorted_nodes {
let idx_lit = *node_idx as isize;
let count = child_z_refs.len();
let z_reads: Vec<_> = child_z_refs.iter().map(|z_source| {
match z_source {
llr::ZChildSource::Constant(val) => {
quote!(#val)
}
llr::ZChildSource::Property(member_ref) => {
match member_ref {
llr::MemberReference::Relative { parent_level: 0, local_reference } => {
let full_path: Vec<_> = node_sub_component_path.iter()
.chain(local_reference.sub_component_path.iter())
.copied()
.collect();
match &local_reference.reference {
llr::LocalMemberIndex::Property(property_index) => {
let (compo_path, sub_component) = follow_sub_component_path(
root,
sub_tree.root,
&full_path,
);
let component_id = self::inner_component_id(sub_component);
let property_name = ident(&sub_component.properties[*property_index].name);
let property_field =
access_component_field_offset(&component_id, &property_name);
quote!((#compo_path #property_field).apply_pin(self).get() as f32)
}
_ => quote!(0.0f32),
}
}
_ => quote!(0.0f32),
}
}
}
}).collect();
z_match_arms.push(quote! {
#idx_lit => {
let z_values: [f32; #count] = [#(#z_reads),*];
let mut sorted = sp::SharedVector::default();
sp::compute_sorted_children_by_z(&z_values, &mut sorted);
return sp::visit_item_tree(self, &sp::VRcMapped::origin(&self.as_ref().self_weak.get().unwrap().upgrade().unwrap()), self.get_item_tree().as_slice(), index, order, visitor, visit_dynamic, Some(sorted.as_slice()));
}
});
}
quote! {
match index {
#(#z_match_arms)*
_ => {
return sp::visit_item_tree(self, &sp::VRcMapped::origin(&self.as_ref().self_weak.get().unwrap().upgrade().unwrap()), self.get_item_tree().as_slice(), index, order, visitor, visit_dynamic, None);
}
}
}
};

quote!(
#sub_comp

Expand Down Expand Up @@ -2087,7 +2163,7 @@ fn generate_item_tree(
fn visit_children_item(self: ::core::pin::Pin<&Self>, index: isize, order: sp::TraversalOrder, visitor: sp::ItemVisitorRefMut<'_>)
-> sp::VisitChildrenResult
{
return sp::visit_item_tree(self, &sp::VRcMapped::origin(&self.as_ref().self_weak.get().unwrap().upgrade().unwrap()), self.get_item_tree().as_slice(), index, order, visitor, visit_dynamic);
#z_sorted_visit_body
#[allow(unused)]
fn visit_dynamic(_self: ::core::pin::Pin<&#inner_component_id>, order: sp::TraversalOrder, visitor: sp::ItemVisitorRefMut<'_>, dyn_index: u32) -> sp::VisitChildrenResult {
_self.visit_dynamic_children(dyn_index, order, visitor)
Expand Down
13 changes: 13 additions & 0 deletions internal/compiler/llr/item_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,19 @@ pub struct TreeNode {
pub item_index: itertools::Either<ItemInstanceIdx, u32>,
pub children: Vec<TreeNode>,
pub is_accessible: bool,
/// If set, this node's children have dynamic z-ordering.
/// Each entry corresponds to a child (by index) and describes the source of its z value.
/// The code generator will read these z values at runtime and sort children accordingly.
pub z_sort_order_property: Option<Vec<ZChildSource>>,
}

/// Source of a child's z value for dynamic z-ordering.
#[derive(Debug, Clone)]
pub enum ZChildSource {
/// Z value read from a property at runtime.
Property(super::MemberReference),
/// Z value known at compile time (e.g., constant z on a repeater child).
Constant(f32),
}

impl TreeNode {
Expand Down
30 changes: 30 additions & 0 deletions internal/compiler/llr/lower_to_item_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,32 @@ fn make_tree(
let e = element.borrow();
let children = e.children.iter().map(|c| make_tree(state, c, component, sub_component_path));
let repeater_count = component.mapping.repeater_count;

// Check if this element has dynamic z-ordering. If so, build the z sources
// from each child's z_order field.
let z_sort_order_property = if e.has_dynamic_z_order {
use crate::object_tree::ZOrder;
let mut z_sources: Vec<ZChildSource> = Vec::with_capacity(e.children.len());
for child in e.children.iter() {
let child_z = child.borrow().z_order.clone();
match child_z {
Some(ZOrder::Constant(val)) => {
z_sources.push(ZChildSource::Constant(val));
}
Some(ZOrder::Dynamic(ref nr)) => {
let member_ref = component.mapping.map_property_reference(nr, state);
z_sources.push(ZChildSource::Property(member_ref));
}
None => {
z_sources.push(ZChildSource::Constant(0.0));
}
}
}
Some(z_sources)
} else {
None
};

match component.mapping.element_mapping.get(&ByAddress(element.clone())).unwrap() {
LoweredElement::SubComponent { sub_component_index } => {
let sub_component = e.sub_component().unwrap();
Expand All @@ -1099,25 +1125,29 @@ fn make_tree(
);
tree_node.children.extend(children);
tree_node.is_accessible |= !e.accessibility_props.0.is_empty();
tree_node.z_sort_order_property = z_sort_order_property;
tree_node
}
LoweredElement::NativeItem { item_index } => TreeNode {
is_accessible: !e.accessibility_props.0.is_empty(),
sub_component_path: sub_component_path.into(),
item_index: itertools::Either::Left(*item_index),
children: children.collect(),
z_sort_order_property,
},
LoweredElement::Repeated { repeated_index } => TreeNode {
is_accessible: false,
sub_component_path: sub_component_path.into(),
item_index: itertools::Either::Right(usize::from(*repeated_index) as u32),
children: Vec::new(),
z_sort_order_property: None,
},
LoweredElement::ComponentPlaceholder { repeated_index } => TreeNode {
is_accessible: false,
sub_component_path: sub_component_path.into(),
item_index: itertools::Either::Right(*repeated_index + repeater_count),
children: Vec::new(),
z_sort_order_property: None,
},
}
}
Expand Down
37 changes: 37 additions & 0 deletions internal/compiler/llr/optim_passes/count_property_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,43 @@ pub fn count_property_use(root: &CompilationUnit) {
visit_property(&p.activated, &ctx);
}

fn visit_tree_z_properties(node: &crate::llr::TreeNode, ctx: &EvaluationContext) {
if let Some(z_props) = &node.z_sort_order_property {
for z_source in z_props {
if let crate::llr::ZChildSource::Property(member_ref) = z_source {
visit_property(member_ref, ctx);
}
}
}
for child in &node.children {
visit_tree_z_properties(child, ctx);
}
}
for c in &root.public_components {
let ctx = EvaluationContext::new_sub_component(root, c.item_tree.root, (), None);
visit_tree_z_properties(&c.item_tree.tree, &ctx);
}
root.for_each_sub_components(&mut |sc, ctx| {
for r in &sc.repeated {
let rep_ctx = EvaluationContext::new_sub_component(root, r.sub_tree.root, (), None);
visit_tree_z_properties(&r.sub_tree.tree, &rep_ctx);
}
for popup in &sc.popup_windows {
let parent_ctx = ParentScope::new(ctx, None);
let popup_ctx = EvaluationContext::new_sub_component(
root,
popup.item_tree.root,
(),
Some(&parent_ctx),
);
visit_tree_z_properties(&popup.item_tree.tree, &popup_ctx);
}
});
if let Some(p) = &root.popup_menu {
let ctx = EvaluationContext::new_sub_component(root, p.item_tree.root, (), None);
visit_tree_z_properties(&p.item_tree.tree, &ctx);
}

clean_unused_bindings(root);
}

Expand Down
23 changes: 23 additions & 0 deletions internal/compiler/llr/optim_passes/remove_unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,25 @@ mod visitor {
for p in public_properties {
visit_public_property(p, &scope, state, visitor);
}
visit_tree_node_z_properties(&mut item_tree.tree, &scope, state, visitor);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we miss some trees. Like what about popup and stuff?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Added z-property visits for popup windows and popup_menu trees in both count_property_use and remove_unused passes.

}

fn visit_tree_node_z_properties(
node: &mut crate::llr::TreeNode,
scope: &EvaluationScope,
state: &VisitorState,
visitor: &mut (impl Visitor + ?Sized),
) {
if let Some(z_props) = &mut node.z_sort_order_property {
for z_source in z_props {
if let crate::llr::ZChildSource::Property(member_ref) = z_source {
visit_member_reference(member_ref, scope, state, visitor);
}
}
}
for child in &mut node.children {
visit_tree_node_z_properties(child, scope, state, visitor);
}
}

pub fn visit_sub_component(
Expand Down Expand Up @@ -338,6 +357,8 @@ mod visitor {
visitor.visit_property_idx(data_prop, &inner_scope, state);
}

visit_tree_node_z_properties(&mut sub_tree.tree, &inner_scope, state, visitor);

if let Some(listview) = listview {
visit_member_reference(&mut listview.viewport_y, &scope, state, visitor);
visit_member_reference(&mut listview.viewport_height, &scope, state, visitor);
Expand All @@ -353,6 +374,7 @@ mod visitor {
for p in popup_windows {
let popup_scope = EvaluationScope::SubComponent(p.item_tree.root, None);
visit_expression(p.position.get_mut(), &popup_scope, state, visitor);
visit_tree_node_z_properties(&mut p.item_tree.tree, &popup_scope, state, visitor);
}
for t in timers {
visit_expression(t.interval.get_mut(), &scope, state, visitor);
Expand Down Expand Up @@ -474,6 +496,7 @@ mod visitor {
visit_member_reference(activated, &scope, state, visitor);
visit_member_reference(close, &scope, state, visitor);
visit_member_reference(entries, &scope, state, visitor);
visit_tree_node_z_properties(&mut item_tree.tree, &scope, state, visitor);
}

pub fn visit_public_property(
Expand Down
Loading
Loading