1use crate::erased::{ErasedItemRc, SubComponentCallback, SubComponentProperty};
8use crate::globals::GlobalStorage;
9use crate::item_registry::ItemRegistry;
10use i_slint_compiler::llr::{
11 self, CompilationUnit, ItemInstanceIdx, RepeatedElementIdx, SubComponentIdx,
12 SubComponentInstanceIdx,
13};
14use i_slint_core::item_tree::{ItemTreeNode, ItemTreeVTable};
15use i_slint_core::model::{Conditional, Repeater};
16use i_slint_core::properties::ChangeTracker;
17use i_slint_core::window::WindowAdapterRc;
18use i_slint_core::{Callback, Property};
19use std::cell::{OnceCell, RefCell};
20use std::pin::Pin;
21use std::rc::{Rc, Weak};
22use typed_index_collections::TiVec;
23use vtable::{VRc, VWeak};
24
25pub enum RepeaterOrConditional {
30 Repeater(Pin<Box<Repeater<Instance>>>),
31 Conditional(Pin<Box<Conditional<Instance>>>),
32}
33
34impl RepeaterOrConditional {
35 pub fn visit(
36 &self,
37 order: i_slint_core::item_tree::TraversalOrder,
38 visitor: i_slint_core::item_tree::ItemVisitorRefMut<'_>,
39 ) -> i_slint_core::item_tree::VisitChildrenResult {
40 match self {
41 Self::Repeater(r) => Pin::as_ref(r).visit(order, visitor),
42 Self::Conditional(c) => Pin::as_ref(c).visit(order, visitor),
43 }
44 }
45
46 pub fn for_each_instance_z(&self, cb: &mut dyn FnMut(u32, f32)) {
49 match self {
50 Self::Repeater(r) => Pin::as_ref(r).for_each_instance_z(cb),
51 Self::Conditional(c) => Pin::as_ref(c).for_each_instance_z(cb),
52 }
53 }
54
55 pub fn range(&self) -> core::ops::Range<usize> {
56 match self {
57 Self::Repeater(r) => r.range(),
58 Self::Conditional(c) => c.range(),
59 }
60 }
61
62 pub fn instance_at(&self, subindex: usize) -> Option<VRc<ItemTreeVTable, Instance>> {
63 match self {
64 Self::Repeater(r) => r.instance_at(subindex),
65 Self::Conditional(c) => c.instance_at(subindex),
66 }
67 }
68
69 pub fn instances_vec(&self) -> Vec<VRc<ItemTreeVTable, Instance>> {
70 match self {
71 Self::Repeater(r) => r.instances_vec(),
72 Self::Conditional(c) => c.instances_vec(),
73 }
74 }
75
76 pub fn track_instance_changes(&self) {
81 match self {
82 Self::Repeater(r) => Pin::as_ref(r).track_instance_changes(),
83 Self::Conditional(c) => Pin::as_ref(c).track_instance_changes(),
84 }
85 }
86
87 pub fn ensure_updated(
91 &self,
92 init: impl Fn() -> VRc<ItemTreeVTable, Instance> + 'static,
93 ) -> bool {
94 match self {
95 Self::Repeater(r) => Pin::as_ref(r).ensure_updated(init),
96 Self::Conditional(c) => Pin::as_ref(c).ensure_updated(init),
97 }
98 }
99
100 pub fn ensure_updated_listview_callback(
107 &self,
108 init: impl Fn() -> VRc<ItemTreeVTable, Instance> + 'static,
109 props: &dyn i_slint_core::model::ListViewProperties,
110 listview_width: i_slint_core::lengths::LogicalLength,
111 listview_height: i_slint_core::lengths::LogicalLength,
112 ) -> bool {
113 match self {
114 Self::Repeater(r) => Pin::as_ref(r).ensure_updated_listview_callback(
115 init,
116 props,
117 listview_width,
118 listview_height,
119 ),
120 Self::Conditional(_) => unreachable!("listview on a conditional element"),
121 }
122 }
123
124 pub fn set_model_binding(
126 &self,
127 binding: impl Fn() -> i_slint_core::model::ModelRc<crate::Value> + 'static,
128 ) {
129 match self {
130 Self::Repeater(r) => Pin::as_ref(r).set_model_binding(binding),
131 Self::Conditional(_) => unreachable!("set_model_binding on conditional"),
132 }
133 }
134
135 pub fn set_condition_binding(&self, binding: impl Fn() -> bool + 'static) {
137 match self {
138 Self::Conditional(c) => c.set_model_binding(binding),
139 Self::Repeater(_) => unreachable!("set_condition_binding on repeater"),
140 }
141 }
142
143 pub fn model_set_row_data(&self, row: usize, data: crate::Value) {
145 match self {
146 Self::Repeater(r) => Pin::as_ref(r).model_set_row_data(row, data),
147 Self::Conditional(_) => {} }
149 }
150
151 pub fn is_conditional(&self) -> bool {
152 matches!(self, Self::Conditional(_))
153 }
154}
155
156pub struct SubComponentInstance {
160 pub compilation_unit: Rc<CompilationUnit>,
161 pub sub_component_idx: SubComponentIdx,
162 pub properties: TiVec<llr::PropertyIdx, SubComponentProperty>,
163 pub callbacks: TiVec<llr::CallbackIdx, SubComponentCallback>,
164 pub callback_trackers: TiVec<llr::CallbackIdx, Option<Pin<Rc<Property<()>>>>>,
169 pub items: TiVec<ItemInstanceIdx, ErasedItemRc>,
170 pub sub_components: TiVec<SubComponentInstanceIdx, Pin<Rc<SubComponentInstance>>>,
171 pub repeaters: TiVec<RepeatedElementIdx, RepeaterOrConditional>,
176 pub parent: Weak<SubComponentInstance>,
178 pub root: OnceCell<VWeak<ItemTreeVTable, Instance>>,
180 pub change_trackers: Vec<ChangeTracker>,
183 pub timers: Vec<i_slint_core::timers::Timer>,
188 pub popup_ids: Vec<std::cell::Cell<Option<std::num::NonZeroU32>>>,
193 pub repeated_in: OnceCell<(Weak<SubComponentInstance>, RepeatedElementIdx)>,
197 pub menubar: RefCell<Option<vtable::VRc<i_slint_core::menus::MenuVTable>>>,
200}
201
202pub struct Instance {
204 pub root_sub_component: Pin<Rc<SubComponentInstance>>,
205 pub tree_nodes: Box<[ItemTreeNode]>,
207 pub dynamic_table: Box<[Option<(Box<[SubComponentInstanceIdx]>, RepeatedElementIdx)>]>,
211 pub item_table: Box<[Option<(Box<[SubComponentInstanceIdx]>, ItemInstanceIdx)>]>,
215 pub z_sort_table: Box<[Option<Vec<llr::ZSource>>]>,
218 pub globals: Rc<GlobalStorage>,
219 pub self_weak: OnceCell<VWeak<ItemTreeVTable, Instance>>,
220 pub parent_instance: Weak<SubComponentInstance>,
223 pub public_component_index: Option<usize>,
227 pub window_adapter: OnceCell<WindowAdapterRc>,
230 window_adapter_error: OnceCell<String>,
234 pub window_attached: OnceCell<()>,
239 pub bindings_installed: OnceCell<()>,
242 pub init_code_run: OnceCell<()>,
248 pub embedded_in: OnceCell<(VWeak<ItemTreeVTable>, u32)>,
254 pub type_loaders: crate::component::TypeLoaders,
259}
260
261impl Drop for Instance {
262 fn drop(&mut self) {
263 let Some(adapter) = self.window_adapter.get().cloned().or_else(|| {
273 let mut parent = self.parent_instance.upgrade();
274 while let Some(sub) = parent {
275 let root = sub.root.get().and_then(|w| w.upgrade())?;
276 if let Some(a) = root.window_adapter.get() {
277 return Some(a.clone());
278 }
279 parent = root.parent_instance.upgrade();
280 }
281 None
282 }) else {
283 return;
284 };
285 vtable::new_vref!(let item_tree_ref : VRef<i_slint_core::item_tree::ItemTreeVTable> for i_slint_core::item_tree::ItemTree = self);
286 let items = collect_item_refs(&self.root_sub_component);
287 for item in &items {
292 item.as_ref().deinit(&adapter);
293 }
294 let _ =
295 adapter.renderer().free_graphics_resources(item_tree_ref, &mut items.iter().copied());
296 if let Some(internal) = adapter.internal(i_slint_core::InternalToken) {
297 internal.unregister_item_tree(item_tree_ref, &mut items.iter().copied());
298 }
299 let window_inner = i_slint_core::window::WindowInner::from_pub(adapter.window());
300 let to_close_popups = window_inner
301 .active_popups()
302 .iter()
303 .filter_map(|p| p.parent_item.upgrade().is_none().then_some(p.popup_id))
304 .collect::<Vec<_>>();
305 for popup_id in to_close_popups {
306 window_inner.close_popup(popup_id);
307 }
308 }
309}
310
311fn collect_item_refs<'a>(
314 sub: &'a Pin<Rc<SubComponentInstance>>,
315) -> Vec<Pin<vtable::VRef<'a, i_slint_core::items::ItemVTable>>> {
316 let mut out = Vec::new();
317 fn walk<'a>(
318 sub: &'a Pin<Rc<SubComponentInstance>>,
319 out: &mut Vec<Pin<vtable::VRef<'a, i_slint_core::items::ItemVTable>>>,
320 ) {
321 for item in &sub.items {
322 out.push(Pin::as_ref(item).as_item_ref());
323 }
324 for nested in &sub.sub_components {
325 walk(nested, out);
326 }
327 }
328 walk(sub, &mut out);
329 out
330}
331
332impl Instance {
333 pub fn window_adapter_or_default(&self) -> Option<WindowAdapterRc> {
336 self.try_window_adapter().ok()
337 }
338
339 pub fn try_window_adapter(&self) -> Result<WindowAdapterRc, i_slint_core::api::PlatformError> {
355 if let Some(a) = self.window_adapter.get() {
356 return Ok(a.clone());
357 }
358 if let Some((outer_weak, _)) = self.embedded_in.get()
364 && let Some(outer) = outer_weak.upgrade()
365 {
366 let mut result = None;
367 vtable::VRc::borrow_pin(&outer).as_ref().window_adapter(true, &mut result);
368 if let Some(a) = result {
369 let _ = self.window_adapter.set(a.clone());
370 return Ok(a);
371 }
372 }
373 let mut outermost_root = None;
376 let mut parent_sub = self.parent_instance.upgrade();
377 while let Some(sub) = parent_sub {
378 let Some(root_vrc) = sub.root.get().and_then(|w| w.upgrade()) else { break };
379 if let Some(a) = root_vrc.window_adapter.get() {
380 let cloned = a.clone();
381 let _ = self.window_adapter.set(cloned.clone());
384 return Ok(cloned);
385 }
386 parent_sub = root_vrc.parent_instance.upgrade();
387 outermost_root = Some(root_vrc);
388 }
389 if let Some(e) = self
390 .window_adapter_error
391 .get()
392 .or_else(|| outermost_root.as_ref().and_then(|root| root.window_adapter_error.get()))
393 {
394 return Err(i_slint_core::api::PlatformError::Other(e.clone()));
395 }
396 let adapter = i_slint_backend_selector::with_platform(|p| p.create_window_adapter())
397 .inspect_err(|e| {
398 let msg = e.to_string();
399 if let Some(root) = &outermost_root {
400 let _ = root.window_adapter_error.set(msg.clone());
401 }
402 let _ = self.window_adapter_error.set(msg);
403 })?;
404 adapter.renderer().set_window_adapter(&adapter);
408 if let Some(root) = outermost_root {
412 let _ = root.window_adapter.set(adapter.clone());
413 }
414 let _ = self.window_adapter.set(adapter.clone());
415 Ok(adapter)
416 }
417
418 pub fn attach_to_window(&self) {
427 if self.window_attached.get().is_some() || self.embedded_in.get().is_some() {
430 return;
431 }
432 let Some(adapter) = self.window_adapter_or_default() else { return };
433 let Some(self_rc) = self.self_weak.get().and_then(|w| w.upgrade()) else { return };
434 let _ = self.window_attached.set(());
435 i_slint_core::window::WindowInner::from_pub(adapter.window())
436 .set_component(&vtable::VRc::into_dyn(self_rc));
437 }
438}
439
440pub(crate) fn component_container_item(
446 sub: &Pin<Rc<SubComponentInstance>>,
447 rep_idx: RepeatedElementIdx,
448) -> Option<Pin<&i_slint_core::items::ComponentContainer>> {
449 let sc = &sub.compilation_unit.sub_components[sub.sub_component_idx];
450 let cc_item_idx = sc.repeated.get(rep_idx)?.container_item_index?;
451 let item = sub.items.get(cc_item_idx)?;
452 i_slint_core::items::ItemRef::downcast_pin::<i_slint_core::items::ComponentContainer>(
453 Pin::as_ref(item).as_item_ref(),
454 )
455}
456
457impl Instance {
458 pub fn dynamic_at(
462 &self,
463 tree_index: u32,
464 ) -> Option<(Pin<Rc<SubComponentInstance>>, RepeatedElementIdx)> {
465 let entry = self.dynamic_table.get(tree_index as usize)?.as_ref()?;
466 let mut current = self.root_sub_component.clone();
467 for &idx in entry.0.iter() {
468 let next = current.sub_components[idx].clone();
469 current = next;
470 }
471 Some((current, entry.1))
472 }
473
474 pub fn ensure_updated(&self, tree_index: u32) -> bool {
485 let Some((sub, rep_idx)) = self.dynamic_at(tree_index) else { return false };
486 if let Some(cc) = component_container_item(&sub, rep_idx) {
487 return cc.ensure_updated();
488 }
489 let cu = sub.compilation_unit.clone();
490 let sc_idx = sub.sub_component_idx;
491 let sub_weak = Rc::downgrade(&Pin::into_inner(sub.clone()));
492 let globals = self.globals.clone();
493 let repeated = &cu.sub_components[sc_idx].repeated[rep_idx];
494 let listview_factory = repeated.listview.is_some();
495 let listview_info = repeated.listview.clone();
496 let factory = move || {
497 let item_tree = &cu.sub_components[sc_idx].repeated[rep_idx].sub_tree;
498 let vrc = Instance::new_repeated(
499 cu.clone(),
500 item_tree,
501 sub_weak.clone(),
502 rep_idx,
503 globals.clone(),
504 );
505 if listview_factory {
506 init_items_and_bindings(&vrc);
511 }
512 vrc
513 };
514 let repeater = &sub.repeaters[rep_idx];
515 if let Some(lv) = listview_info.as_ref() {
516 let listview_width = read_logical_length(&sub, &lv.listview_width);
517 let listview_height = read_logical_length(&sub, &lv.listview_height);
518 if listview_height.get() <= 0.0 {
523 return false;
524 }
525 let props = ValueListViewProps {
526 content_y: lv.content_y.clone(),
527 content_width: lv.content_width.clone(),
528 content_height: lv.content_height.clone(),
529 ctx_sub: sub.clone(),
530 };
531 repeater.ensure_updated_listview_callback(
532 factory,
533 &props,
534 listview_width,
535 listview_height,
536 )
537 } else {
538 repeater.ensure_updated(factory)
539 }
540 }
541
542 pub fn ensure_instantiated(&self) -> bool {
547 let mut changed = false;
548 for idx in 0..self.dynamic_table.len() {
549 if self.dynamic_table[idx].is_some() {
550 changed |= self.ensure_updated(idx as u32);
551 }
552 }
553 changed
554 }
555
556 pub fn visit_dynamic_children(
564 self: Pin<&Self>,
565 dyn_index: u32,
566 order: i_slint_core::item_tree::TraversalOrder,
567 visitor: vtable::VRefMut<'_, i_slint_core::item_tree::ItemVisitorVTable>,
568 ) -> i_slint_core::item_tree::VisitChildrenResult {
569 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(dyn_index) else {
570 return i_slint_core::item_tree::VisitChildrenResult::CONTINUE;
571 };
572 if let Some(cc) = component_container_item(&sub, rep_idx) {
573 return cc.visit_children_item(-1, order, visitor);
574 }
575 let repeater = &sub.repeaters[rep_idx];
579 let sc = &sub.compilation_unit.sub_components[sub.sub_component_idx];
580 if let (Some(lv), RepeaterOrConditional::Repeater(r)) =
581 (sc.repeated[rep_idx].listview.as_ref(), repeater)
582 {
583 let props = ValueListViewProps {
584 content_y: lv.content_y.clone(),
585 content_width: lv.content_width.clone(),
586 content_height: lv.content_height.clone(),
587 ctx_sub: sub.clone(),
588 };
589 let listview_width = read_logical_length(&sub, &lv.listview_width);
590 let _ = read_logical_length(&sub, &lv.listview_height);
591 Pin::as_ref(r).track_changes_listview_callback(&props, listview_width);
592 }
593 repeater.visit(order, visitor)
594 }
595
596 pub fn collect_z_sorted_children(
601 self: Pin<&Self>,
602 index: isize,
603 push: &mut dyn FnMut(u32, Option<u32>, f32),
604 ) {
605 let Some(Some(sources)) = self.z_sort_table.get(index as usize) else { return };
606 let ItemTreeNode::Item { children_index, .. } = self.tree_nodes[index as usize] else {
607 return;
608 };
609 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
610 for (k, source) in sources.iter().enumerate() {
611 let child_offset = k as u32;
612 match source {
613 llr::ZSource::Expression(e) => {
614 let z: f64 = crate::eval::eval_expression(&mut ctx, &e.borrow())
615 .try_into()
616 .unwrap_or(0.0);
617 push(child_offset, None, z as f32);
618 }
619 llr::ZSource::RepeaterInstances => {
620 if let Some((sub, rep_idx)) =
622 self.get_ref().dynamic_at(children_index + child_offset)
623 {
624 sub.repeaters[rep_idx].for_each_instance_z(&mut |instance, z| {
625 push(child_offset, Some(instance), z)
626 });
627 }
628 }
629 }
630 }
631 }
632
633 pub fn new(
638 compilation_unit: Rc<CompilationUnit>,
639 public_component_index: usize,
640 ) -> VRc<ItemTreeVTable, Instance> {
641 Self::new_with_window(compilation_unit, public_component_index, None, Default::default())
642 }
643
644 pub fn new_with_window(
648 compilation_unit: Rc<CompilationUnit>,
649 public_component_index: usize,
650 window_adapter: Option<i_slint_core::window::WindowAdapterRc>,
651 type_loaders: crate::component::TypeLoaders,
652 ) -> VRc<ItemTreeVTable, Instance> {
653 Self::new_with_options(
654 compilation_unit,
655 public_component_index,
656 window_adapter,
657 type_loaders,
658 None,
659 )
660 }
661
662 pub fn new_embedded(
667 compilation_unit: Rc<CompilationUnit>,
668 public_component_index: usize,
669 type_loaders: crate::component::TypeLoaders,
670 parent: vtable::VWeak<ItemTreeVTable>,
671 parent_item_tree_index: u32,
672 ) -> VRc<ItemTreeVTable, Instance> {
673 Self::new_with_options(
674 compilation_unit,
675 public_component_index,
676 None,
677 type_loaders,
678 Some((parent, parent_item_tree_index)),
679 )
680 }
681
682 fn new_with_options(
683 compilation_unit: Rc<CompilationUnit>,
684 public_component_index: usize,
685 window_adapter: Option<i_slint_core::window::WindowAdapterRc>,
686 type_loaders: crate::component::TypeLoaders,
687 embedded_in: Option<(vtable::VWeak<ItemTreeVTable>, u32)>,
688 ) -> VRc<ItemTreeVTable, Instance> {
689 let public = &compilation_unit.public_components[public_component_index];
690 let globals = Rc::new(GlobalStorage::new(&compilation_unit));
691 let item_tree = &public.item_tree;
692 let vrc = build_instance(
693 &compilation_unit,
694 item_tree,
695 Weak::new(),
696 globals,
697 Some(public_component_index),
698 type_loaders,
699 );
700 if let Some(adapter) = window_adapter {
701 let _ = vrc.window_adapter.set(adapter);
702 }
703 if let Some((parent, idx)) = embedded_in {
707 let _ = vrc.embedded_in.set((parent, idx));
708 }
709 finalize_instance(&vrc);
710 vrc
711 }
712
713 pub fn new_repeated(
718 compilation_unit: Rc<CompilationUnit>,
719 item_tree: &llr::ItemTree,
720 parent: Weak<SubComponentInstance>,
721 repeater_idx: RepeatedElementIdx,
722 globals: Rc<GlobalStorage>,
723 ) -> VRc<ItemTreeVTable, Instance> {
724 let vrc = build_instance(
725 &compilation_unit,
726 item_tree,
727 parent.clone(),
728 globals,
729 None,
730 Default::default(),
731 );
732 let _ = vrc.root_sub_component.repeated_in.set((parent, repeater_idx));
733 vrc
734 }
735
736 pub fn new_popup(
740 compilation_unit: Rc<CompilationUnit>,
741 item_tree: &llr::ItemTree,
742 parent: Weak<SubComponentInstance>,
743 globals: Rc<GlobalStorage>,
744 ) -> VRc<ItemTreeVTable, Instance> {
745 build_instance(&compilation_unit, item_tree, parent, globals, None, Default::default())
746 }
747}
748
749fn build_instance(
757 compilation_unit: &Rc<CompilationUnit>,
758 item_tree: &llr::ItemTree,
759 parent: Weak<SubComponentInstance>,
760 globals: Rc<GlobalStorage>,
761 public_component_index: Option<usize>,
762 type_loaders: crate::component::TypeLoaders,
763) -> VRc<ItemTreeVTable, Instance> {
764 let parent_for_root = parent.clone();
765 let root_sub_component =
766 build_sub_component_instance(compilation_unit, item_tree.root, parent_for_root);
767 let (tree_nodes, dynamic_table, item_table, z_sort_table) = build_tree_nodes(&item_tree.tree);
768
769 let vrc = VRc::new(Instance {
770 root_sub_component,
771 tree_nodes: tree_nodes.into_boxed_slice(),
772 dynamic_table: dynamic_table.into_boxed_slice(),
773 item_table: item_table.into_boxed_slice(),
774 z_sort_table: z_sort_table.into_boxed_slice(),
775 globals,
776 self_weak: OnceCell::new(),
777 parent_instance: parent,
778 public_component_index,
779 window_adapter: OnceCell::new(),
780 window_adapter_error: OnceCell::new(),
781 window_attached: OnceCell::new(),
782 bindings_installed: OnceCell::new(),
783 init_code_run: OnceCell::new(),
784 embedded_in: OnceCell::new(),
785 type_loaders,
786 });
787 let weak = VRc::downgrade(&vrc);
788 let _ = vrc.self_weak.set(weak.clone());
789 let _ = vrc.globals.root.set(weak.clone());
791 propagate_root(&vrc.root_sub_component, &weak);
792 vrc
793}
794
795pub(crate) fn finalize_instance(vrc: &VRc<ItemTreeVTable, Instance>) {
801 init_items_and_bindings(vrc);
802 if vrc.init_code_run.get().is_some() {
803 return;
804 }
805 let _ = vrc.init_code_run.set(());
806 if vrc.public_component_index.is_some() && vrc.embedded_in.get().is_none() {
814 vrc.attach_to_window();
815 }
816 crate::bindings::run_init_code_for_instance(vrc);
817}
818
819pub(crate) fn init_items_and_bindings(vrc: &VRc<ItemTreeVTable, Instance>) {
822 if vrc.bindings_installed.get().is_some() {
823 return;
824 }
825 let _ = vrc.bindings_installed.set(());
826 i_slint_core::item_tree::register_item_tree(
829 &vtable::VRc::into_dyn(vrc.clone()),
830 vrc.window_adapter_or_default(),
831 );
832 let is_root = vrc.parent_instance.upgrade().is_none();
833 if is_root {
834 crate::globals::install_global_bindings(&vrc.globals);
835 }
836 crate::bindings::install_bindings_only(vrc);
837}
838
839fn propagate_root(sub: &Pin<Rc<SubComponentInstance>>, weak: &VWeak<ItemTreeVTable, Instance>) {
841 let _ = sub.root.set(weak.clone());
842 for nested in &sub.sub_components {
843 propagate_root(nested, weak);
844 }
845}
846
847fn build_sub_component_instance(
849 cu: &Rc<CompilationUnit>,
850 sub_idx: SubComponentIdx,
851 parent: Weak<SubComponentInstance>,
852) -> Pin<Rc<SubComponentInstance>> {
853 let sc = &cu.sub_components[sub_idx];
854 let registry = ItemRegistry::global();
855
856 let properties = sc
857 .properties
858 .iter()
859 .map(|p| Rc::pin(Property::new(crate::eval::default_value_for_type(&p.ty))))
860 .collect();
861 let callbacks = sc.callbacks.iter().map(|_| Rc::pin(Callback::default())).collect();
862 let callback_trackers =
863 sc.callbacks.iter().map(|c| c.needs_tracker.then(|| Rc::pin(Property::new(())))).collect();
864 let items =
865 sc.items
866 .iter()
867 .map(|item| {
868 registry.factory(&item.ty.class_name).unwrap_or_else(|| {
869 panic!("native item `{}` is not registered", item.ty.class_name)
870 })()
871 })
872 .collect();
873 let repeaters = sc
874 .repeated
875 .iter()
876 .map(|rep| {
877 if rep.data_prop.is_none() {
878 RepeaterOrConditional::Conditional(Box::pin(Conditional::default()))
879 } else {
880 RepeaterOrConditional::Repeater(Box::pin(Repeater::default()))
881 }
882 })
883 .collect();
884
885 let rc = Rc::new_cyclic(|weak_self: &Weak<SubComponentInstance>| {
889 let sub_components = sc
890 .sub_components
891 .iter()
892 .map(|nested| build_sub_component_instance(cu, nested.ty, weak_self.clone()))
893 .collect();
894 SubComponentInstance {
895 compilation_unit: cu.clone(),
896 sub_component_idx: sub_idx,
897 properties,
898 callbacks,
899 callback_trackers,
900 items,
901 sub_components,
902 repeaters,
903 parent,
904 root: OnceCell::new(),
905 change_trackers: std::iter::repeat_with(ChangeTracker::default)
906 .take(2 * sc.timers.len() + sc.change_callbacks.len())
907 .collect(),
908 timers: std::iter::repeat_with(Default::default).take(sc.timers.len()).collect(),
909 popup_ids: vec![std::cell::Cell::new(None); sc.popup_windows.len()],
910 repeated_in: OnceCell::new(),
911 menubar: RefCell::new(None),
912 }
913 });
914 Pin::new(rc)
915}
916
917fn read_logical_length(
921 sub: &Pin<Rc<SubComponentInstance>>,
922 mr: &llr::MemberReference,
923) -> i_slint_core::lengths::LogicalLength {
924 let mut ctx = crate::eval::EvalContext::new(sub.clone());
925 let v = crate::eval::load_property(&ctx, mr);
926 let _ = &mut ctx;
927 let n: f64 = v.try_into().unwrap_or(0.0);
928 i_slint_core::lengths::LogicalLength::new(n as f32)
929}
930
931struct ValueListViewProps {
937 content_y: llr::MemberReference,
938 content_width: Option<llr::MemberReference>,
941 content_height: Option<llr::MemberReference>,
942 ctx_sub: Pin<Rc<SubComponentInstance>>,
943}
944
945impl i_slint_core::model::ListViewProperties for ValueListViewProps {
946 fn content_y_get(&self) -> i_slint_core::lengths::LogicalLength {
947 read_logical_length(&self.ctx_sub, &self.content_y)
948 }
949 fn content_y_get_internal(&self) -> i_slint_core::lengths::LogicalLength {
950 read_logical_length(&self.ctx_sub, &self.content_y)
954 }
955 fn content_y_set(&self, value: i_slint_core::lengths::LogicalLength) {
956 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
957 crate::eval::store_property(
958 &ctx,
959 &self.content_y,
960 crate::Value::Number(value.get() as f64),
961 );
962 }
963 fn content_y_has_binding(&self) -> bool {
964 false
968 }
969 fn computes_content_height(&self) -> bool {
970 self.content_height.is_some()
971 }
972 fn content_width_set(&self, value: i_slint_core::lengths::LogicalLength) {
973 let Some(content_width) = &self.content_width else { return };
974 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
975 crate::eval::store_property(&ctx, content_width, crate::Value::Number(value.get() as f64));
976 }
977 fn content_height_set(&self, value: i_slint_core::lengths::LogicalLength) {
978 let Some(content_height) = &self.content_height else { return };
979 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
980 crate::eval::store_property(&ctx, content_height, crate::Value::Number(value.get() as f64));
981 }
982 fn register_as_dependencies(&self) {
983 if let Some(content_width) = &self.content_width {
986 let _ = read_logical_length(&self.ctx_sub, content_width);
987 }
988 if let Some(content_height) = &self.content_height {
989 let _ = read_logical_length(&self.ctx_sub, content_height);
990 }
991 let _ = read_logical_length(&self.ctx_sub, &self.content_y);
992 }
993}
994
995type DynamicEntry = Option<(Box<[SubComponentInstanceIdx]>, RepeatedElementIdx)>;
996type ItemEntry = Option<(Box<[SubComponentInstanceIdx]>, ItemInstanceIdx)>;
997type ZSortEntry = Option<Vec<llr::ZSource>>;
998
999fn build_tree_nodes(
1009 root: &llr::TreeNode,
1010) -> (Vec<ItemTreeNode>, Vec<DynamicEntry>, Vec<ItemEntry>, Vec<ZSortEntry>) {
1011 use itertools::Either;
1012
1013 let mut out = Vec::new();
1014 let mut dyn_table: Vec<DynamicEntry> = Vec::new();
1015 let mut item_table: Vec<ItemEntry> = Vec::new();
1016 let mut z_sort_table: Vec<ZSortEntry> = Vec::new();
1017 root.visit_in_array(&mut |node, children_offset, parent_index| {
1018 let parent_index = parent_index as u32;
1019 let (entry, dyn_entry, item_entry) = match node.item_index {
1020 Either::Left(item_idx) => (
1021 ItemTreeNode::Item {
1022 is_accessible: node.is_accessible,
1023 children_count: node.children.len() as u32,
1024 children_index: children_offset as u32,
1025 parent_index,
1026 item_array_index: out.len() as u32,
1029 },
1030 None,
1031 Some((node.sub_component_path.clone().into_boxed_slice(), item_idx)),
1032 ),
1033 Either::Right(dynamic_index) => (
1034 ItemTreeNode::DynamicTree { index: out.len() as u32, parent_index },
1042 Some((
1043 node.sub_component_path.clone().into_boxed_slice(),
1044 (dynamic_index as usize).into(),
1045 )),
1046 None,
1047 ),
1048 };
1049 out.push(entry);
1050 dyn_table.push(dyn_entry);
1051 item_table.push(item_entry);
1052 z_sort_table.push(node.z_sort_order_property.clone());
1053 });
1054 (out, dyn_table, item_table, z_sort_table)
1055}
1056
1057fn repeated_align_self(
1060 sc: &i_slint_compiler::llr::SubComponent,
1061 ctx: &mut crate::eval::EvalContext,
1062 orientation: i_slint_core::items::Orientation,
1063) -> i_slint_core::items::CrossAxisAlignment {
1064 match &sc.cross_axis_self_alignment_for_repeated {
1065 Some((cross_o, expr)) if crate::eval::llr_to_core_orientation(*cross_o) == orientation => {
1066 crate::eval::eval_expression(ctx, &expr.borrow()).try_into().unwrap_or_default()
1067 }
1068 _ => Default::default(),
1069 }
1070}
1071
1072fn repeated_layout_order(
1075 sc: &i_slint_compiler::llr::SubComponent,
1076 ctx: &mut crate::eval::EvalContext,
1077 orientation: i_slint_core::items::Orientation,
1078) -> i32 {
1079 match &sc.layout_order_for_repeated {
1080 Some((main_o, expr)) if crate::eval::llr_to_core_orientation(*main_o) == orientation => {
1081 match crate::eval::eval_expression(ctx, &expr.borrow()) {
1082 crate::Value::Number(n) => n as i32,
1083 _ => 0,
1084 }
1085 }
1086 _ => 0,
1087 }
1088}
1089
1090impl i_slint_core::model::RepeatedItemTree for Instance {
1095 type Data = crate::Value;
1096
1097 fn update(&self, index: usize, data: Self::Data) {
1098 let sc_idx = self.root_sub_component.sub_component_idx;
1099 let cu = self.root_sub_component.compilation_unit.clone();
1100 let sc = &cu.sub_components[sc_idx];
1101 for (idx, prop) in sc.properties.iter_enumerated() {
1106 let target = &self.root_sub_component.properties[idx];
1107 match prop.name.as_str() {
1108 "model_data" => Pin::as_ref(target).set(data.clone()),
1109 "model_index" => Pin::as_ref(target).set(crate::Value::Number(index as f64)),
1110 _ => {}
1111 }
1112 }
1113 }
1114
1115 fn init(&self) {
1116 if let Some(weak) = self.self_weak.get()
1122 && let Some(vrc) = weak.upgrade()
1123 {
1124 finalize_instance(&vrc);
1125 }
1126 }
1127
1128 fn z_order(self: Pin<&Self>) -> Option<f32> {
1129 let this = self.get_ref();
1132 let (parent_weak, rep_idx) = this.root_sub_component.repeated_in.get()?;
1133 let parent_sub = parent_weak.upgrade()?;
1134 let parent_sc = &parent_sub.compilation_unit.sub_components[parent_sub.sub_component_idx];
1135 let z_ref = parent_sc.repeated[*rep_idx].dynamic_z.as_ref()?;
1136 let ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1137 let z: f64 = crate::eval::load_property(&ctx, z_ref).try_into().unwrap_or(0.0);
1138 Some(z as f32)
1139 }
1140
1141 fn listview_layout(
1142 self: Pin<&Self>,
1143 offset_y: &mut i_slint_core::lengths::LogicalLength,
1144 ) -> i_slint_core::lengths::LogicalLength {
1145 use i_slint_core::item_tree::ItemTree as _;
1146 use i_slint_core::lengths::LogicalLength;
1147 let this = self.get_ref();
1151 let Some((parent_weak, rep_idx)) = this.root_sub_component.repeated_in.get() else {
1152 return LogicalLength::default();
1153 };
1154 let Some(parent_sub) = parent_weak.upgrade() else { return LogicalLength::default() };
1155 let parent_sub = Pin::new(parent_sub);
1156 let parent_cu = parent_sub.compilation_unit.clone();
1157 let parent_sc = &parent_cu.sub_components[parent_sub.sub_component_idx];
1158 let Some(lv) = parent_sc.repeated[*rep_idx].listview.as_ref() else {
1159 return LogicalLength::default();
1160 };
1161
1162 let row_sub = this.root_sub_component.clone();
1166 let ctx = crate::eval::EvalContext::new(row_sub.clone());
1167 crate::eval::store_property(&ctx, &lv.prop_y, crate::Value::Number(offset_y.get() as f64));
1168 let height_v = crate::eval::load_property(&ctx, &lv.prop_height);
1169 let height: f64 = height_v.try_into().unwrap_or(0.0);
1170 *offset_y += LogicalLength::new(height as f32);
1171 let info = self.layout_info(i_slint_core::items::Orientation::Horizontal);
1172 LogicalLength::new(info.min)
1173 }
1174
1175 fn layout_item_info(
1176 self: Pin<&Self>,
1177 orientation: i_slint_core::items::Orientation,
1178 child_index: Option<usize>,
1179 ) -> i_slint_core::layout::LayoutItemInfo {
1180 let this = self.get_ref();
1189 let cu = this.root_sub_component.compilation_unit.clone();
1190 let sc_idx = this.root_sub_component.sub_component_idx;
1191 let sc = &cu.sub_components[sc_idx];
1192
1193 if let (Some(index), true, Some(templates)) =
1194 (child_index, sc.is_repeated_row, sc.row_child_templates.as_ref())
1195 {
1196 return row_child_layout_item_info(this, sc, templates, orientation, index);
1197 }
1198
1199 let expr = match orientation {
1200 i_slint_core::items::Orientation::Horizontal => sc.layout_info_h.borrow(),
1201 i_slint_core::items::Orientation::Vertical => sc.layout_info_v.borrow(),
1202 };
1203 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1204 let constraint =
1205 crate::eval::eval_expression(&mut ctx, &expr).try_into().unwrap_or_default();
1206 i_slint_core::layout::LayoutItemInfo {
1207 constraint,
1208 cross_axis_self_alignment: repeated_align_self(sc, &mut ctx, orientation),
1209 layout_order: repeated_layout_order(sc, &mut ctx, orientation),
1210 }
1211 }
1212
1213 fn layout_item_info_at_cross_width(
1219 self: Pin<&Self>,
1220 cross_width: f32,
1221 ) -> i_slint_core::layout::LayoutItemInfo {
1222 use i_slint_compiler::llr::lower_layout_expression::CROSS_WIDTH_LOCAL;
1223 let orientation = i_slint_core::items::Orientation::Vertical;
1224 let cu = self.root_sub_component.compilation_unit.clone();
1225 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1226 let Some(expr) = sc
1227 .layout_info_v_at_cross_width_for_repeated
1228 .as_ref()
1229 .filter(|_| sc.flexbox_layout_item_info_for_repeated.is_none())
1230 else {
1231 return self.layout_item_info(orientation, None);
1232 };
1233 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1234 ctx.locals.insert(CROSS_WIDTH_LOCAL.into(), crate::Value::Number(cross_width as f64));
1235 let constraint =
1236 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().unwrap_or_default();
1237 i_slint_core::layout::LayoutItemInfo {
1241 constraint,
1242 cross_axis_self_alignment: repeated_align_self(sc, &mut ctx, orientation),
1243 layout_order: repeated_layout_order(sc, &mut ctx, orientation),
1244 }
1245 }
1246
1247 fn flexbox_layout_item_info(
1248 self: Pin<&Self>,
1249 orientation: i_slint_core::items::Orientation,
1250 child_index: Option<usize>,
1251 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1252 let cu = self.root_sub_component.compilation_unit.clone();
1256 let sc_idx = self.root_sub_component.sub_component_idx;
1257 let sc = &cu.sub_components[sc_idx];
1258 if let Some(expr) = &sc.flexbox_layout_item_info_for_repeated {
1259 let expr = expr.borrow();
1260 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1261 let value = crate::eval::eval_expression(&mut ctx, &expr);
1262 let mut info = value_to_flexbox_layout_item_info(value, orientation, self);
1263 if matches!(orientation, i_slint_core::items::Orientation::Vertical)
1269 && child_index.is_none()
1270 && let Some(v_expr) = &sc.layout_info_v_constrained_for_repeated
1271 {
1272 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1273 info.constraint = crate::eval::eval_expression(&mut ctx, &v_expr.borrow())
1274 .try_into()
1275 .unwrap_or_default();
1276 return info;
1277 }
1278 info.constraint = self.layout_item_info(orientation, child_index).constraint;
1281 return info;
1282 }
1283 let info = self.layout_item_info(orientation, None);
1284 info.into()
1285 }
1286}
1287
1288impl Instance {
1289 pub fn flexbox_layout_item_info_at_cross_width(
1293 self: Pin<&Self>,
1294 cross_width: f32,
1295 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1296 use i_slint_core::items::Orientation;
1297 use i_slint_core::model::RepeatedItemTree;
1298 let mut info =
1299 RepeatedItemTree::flexbox_layout_item_info(self, Orientation::Vertical, None);
1300 let cu = self.root_sub_component.compilation_unit.clone();
1301 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1302 if let Some(v_expr) = &sc.layout_info_v_at_cross_width_for_repeated {
1303 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1304 ctx.locals.insert(
1305 i_slint_compiler::llr::lower_layout_expression::CROSS_WIDTH_LOCAL.into(),
1306 crate::Value::Number(cross_width as f64),
1307 );
1308 info.constraint = crate::eval::eval_expression(&mut ctx, &v_expr.borrow())
1309 .try_into()
1310 .unwrap_or_default();
1311 }
1312 info
1313 }
1314}
1315
1316fn row_child_layout_item_info(
1320 this: &Instance,
1321 sc: &i_slint_compiler::llr::SubComponent,
1322 templates: &[i_slint_compiler::llr::RowChildTemplateInfo],
1323 orientation: i_slint_core::items::Orientation,
1324 mut index: usize,
1325) -> i_slint_core::layout::LayoutItemInfo {
1326 use i_slint_compiler::llr::RowChildTemplateInfo;
1327 use i_slint_core::model::RepeatedItemTree;
1328 let flat_index = index;
1331 for entry in templates {
1332 match entry {
1333 RowChildTemplateInfo::Static { child_index } => {
1334 if index == 0 {
1335 let child = &sc.grid_layout_children[*child_index];
1336 let expr = match orientation {
1337 i_slint_core::items::Orientation::Horizontal => {
1338 child.layout_info_h.borrow()
1339 }
1340 i_slint_core::items::Orientation::Vertical => child.layout_info_v.borrow(),
1341 };
1342 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1343 let constraint = crate::eval::eval_expression(&mut ctx, &expr)
1344 .try_into()
1345 .unwrap_or_default();
1346 return i_slint_core::layout::LayoutItemInfo {
1347 constraint,
1348 ..Default::default()
1349 };
1350 }
1351 index -= 1;
1352 }
1353 RowChildTemplateInfo::Repeated { repeater_index, measure_at_cross_width } => {
1354 let repeater = &this.root_sub_component.repeaters[*repeater_index];
1355 repeater.track_instance_changes();
1356 let count = repeater.range().len();
1357 if index < count {
1358 if let Some(inner) = repeater.instance_at(index) {
1359 if *measure_at_cross_width
1363 && orientation == i_slint_core::items::Orientation::Vertical
1364 && let Some(w) = row_child_cross_width(this, sc, flat_index)
1365 {
1366 return RepeatedItemTree::layout_item_info_at_cross_width(
1367 inner.as_pin_ref(),
1368 w,
1369 );
1370 }
1371 return RepeatedItemTree::layout_item_info(
1372 inner.as_pin_ref(),
1373 orientation,
1374 None,
1375 );
1376 }
1377 return i_slint_core::layout::LayoutItemInfo::default();
1378 }
1379 index -= count;
1380 }
1381 }
1382 }
1383 i_slint_core::layout::LayoutItemInfo::default()
1384}
1385
1386fn row_child_cross_width(
1391 this: &Instance,
1392 sc: &i_slint_compiler::llr::SubComponent,
1393 flat_index: usize,
1394) -> Option<f32> {
1395 use i_slint_compiler::llr::lower_layout_expression::GRID_MEASURE_CHILD_INDEX_LOCAL;
1396 let expr = sc.grid_row_child_cross_width.as_ref()?;
1397 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1398 ctx.locals
1399 .insert(GRID_MEASURE_CHILD_INDEX_LOCAL.into(), crate::Value::Number(flat_index as f64));
1400 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().ok()
1401}
1402
1403fn value_to_flexbox_layout_item_info(
1404 v: crate::Value,
1405 orientation: i_slint_core::items::Orientation,
1406 instance: Pin<&Instance>,
1407) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1408 use i_slint_core::model::RepeatedItemTree;
1409 let crate::Value::Struct(s) = v else {
1410 let info = RepeatedItemTree::layout_item_info(instance, orientation, None);
1411 return info.into();
1412 };
1413 crate::eval_layout::flexbox_item_info_from_struct(&s)
1414}