1use crate::erased::{ErasedItemRc, SubComponentCallback, SubComponentProperty};
8use crate::globals::GlobalStorage;
9use crate::item_registry::ItemRegistry;
10use i_slint_compiler::llr::{
11 self, CompilationUnit, ItemInstanceIdx, PublicComponentIdx, RepeatedElementIdx,
12 SubComponentIdx, 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<PublicComponentIdx>,
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 {
483 let Some((sub, rep_idx)) = self.dynamic_at(tree_index) else { return false };
484 if let Some(cc) = component_container_item(&sub, rep_idx) {
485 return cc.ensure_updated();
486 }
487 let cu = sub.compilation_unit.clone();
488 let sc_idx = sub.sub_component_idx;
489 let sub_weak = Rc::downgrade(&Pin::into_inner(sub.clone()));
490 let globals = self.globals.clone();
491 let repeated = &cu.sub_components[sc_idx].repeated[rep_idx];
492 let listview_factory = repeated.listview.is_some();
493 let listview_info = repeated.listview.clone();
494 let factory = move || {
495 let item_tree = &cu.sub_components[sc_idx].repeated[rep_idx].sub_tree;
496 let vrc = Instance::new_repeated(
497 cu.clone(),
498 item_tree,
499 sub_weak.clone(),
500 rep_idx,
501 globals.clone(),
502 );
503 if listview_factory {
504 init_items_and_bindings(&vrc);
509 }
510 vrc
511 };
512 let repeater = &sub.repeaters[rep_idx];
513 if let Some(lv) = listview_info.as_ref() {
514 let listview_width = read_logical_length(&sub, &lv.listview_width);
515 let listview_height = read_logical_length(&sub, &lv.listview_height);
516 if listview_height.get() <= 0.0 {
521 return false;
522 }
523 let props = ValueListViewProps {
524 content_y: lv.content_y.clone(),
525 content_width: lv.content_width.clone(),
526 content_height: lv.content_height.clone(),
527 ctx_sub: sub.clone(),
528 };
529 repeater.ensure_updated_listview_callback(
530 factory,
531 &props,
532 listview_width,
533 listview_height,
534 )
535 } else {
536 repeater.ensure_updated(factory)
537 }
538 }
539
540 pub fn ensure_instantiated(&self) -> bool {
545 let mut changed = false;
546 for idx in 0..self.dynamic_table.len() {
547 if self.dynamic_table[idx].is_some() {
548 changed |= self.ensure_updated(idx as u32);
549 }
550 }
551 changed
552 }
553
554 pub fn visit_dynamic_children(
562 self: Pin<&Self>,
563 dyn_index: u32,
564 order: i_slint_core::item_tree::TraversalOrder,
565 visitor: vtable::VRefMut<'_, i_slint_core::item_tree::ItemVisitorVTable>,
566 ) -> i_slint_core::item_tree::VisitChildrenResult {
567 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(dyn_index) else {
568 return i_slint_core::item_tree::VisitChildrenResult::CONTINUE;
569 };
570 if let Some(cc) = component_container_item(&sub, rep_idx) {
571 return cc.visit_children_item(-1, order, visitor);
572 }
573 let repeater = &sub.repeaters[rep_idx];
577 let sc = &sub.compilation_unit.sub_components[sub.sub_component_idx];
578 if let (Some(lv), RepeaterOrConditional::Repeater(r)) =
579 (sc.repeated[rep_idx].listview.as_ref(), repeater)
580 {
581 let props = ValueListViewProps {
582 content_y: lv.content_y.clone(),
583 content_width: lv.content_width.clone(),
584 content_height: lv.content_height.clone(),
585 ctx_sub: sub.clone(),
586 };
587 let listview_width = read_logical_length(&sub, &lv.listview_width);
588 let _ = read_logical_length(&sub, &lv.listview_height);
589 Pin::as_ref(r).track_changes_listview_callback(&props, listview_width);
590 }
591 repeater.visit(order, visitor)
592 }
593
594 pub fn collect_z_sorted_children(
599 self: Pin<&Self>,
600 index: isize,
601 push: &mut dyn FnMut(u32, Option<u32>, f32),
602 ) {
603 let Some(Some(sources)) = self.z_sort_table.get(index as usize) else { return };
604 let ItemTreeNode::Item { children_index, .. } = self.tree_nodes[index as usize] else {
605 return;
606 };
607 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
608 for (k, source) in sources.iter().enumerate() {
609 let child_offset = k as u32;
610 match source {
611 llr::ZSource::Expression(e) => {
612 let z: f64 = crate::eval::eval_expression(&mut ctx, &e.borrow())
613 .try_into()
614 .unwrap_or(0.0);
615 push(child_offset, None, z as f32);
616 }
617 llr::ZSource::RepeaterInstances => {
618 if let Some((sub, rep_idx)) =
620 self.get_ref().dynamic_at(children_index + child_offset)
621 {
622 sub.repeaters[rep_idx].for_each_instance_z(&mut |instance, z| {
623 push(child_offset, Some(instance), z)
624 });
625 }
626 }
627 }
628 }
629 }
630
631 pub fn new(
636 compilation_unit: Rc<CompilationUnit>,
637 public_component_index: PublicComponentIdx,
638 ) -> VRc<ItemTreeVTable, Instance> {
639 Self::new_with_window(compilation_unit, public_component_index, None, Default::default())
640 }
641
642 pub fn new_with_window(
646 compilation_unit: Rc<CompilationUnit>,
647 public_component_index: PublicComponentIdx,
648 window_adapter: Option<i_slint_core::window::WindowAdapterRc>,
649 type_loaders: crate::component::TypeLoaders,
650 ) -> VRc<ItemTreeVTable, Instance> {
651 Self::new_with_options(
652 compilation_unit,
653 public_component_index,
654 window_adapter,
655 type_loaders,
656 None,
657 )
658 }
659
660 pub fn new_embedded(
665 compilation_unit: Rc<CompilationUnit>,
666 public_component_index: PublicComponentIdx,
667 type_loaders: crate::component::TypeLoaders,
668 parent: vtable::VWeak<ItemTreeVTable>,
669 parent_item_tree_index: u32,
670 ) -> VRc<ItemTreeVTable, Instance> {
671 Self::new_with_options(
672 compilation_unit,
673 public_component_index,
674 None,
675 type_loaders,
676 Some((parent, parent_item_tree_index)),
677 )
678 }
679
680 fn new_with_options(
681 compilation_unit: Rc<CompilationUnit>,
682 public_component_index: PublicComponentIdx,
683 window_adapter: Option<i_slint_core::window::WindowAdapterRc>,
684 type_loaders: crate::component::TypeLoaders,
685 embedded_in: Option<(vtable::VWeak<ItemTreeVTable>, u32)>,
686 ) -> VRc<ItemTreeVTable, Instance> {
687 let public = &compilation_unit.public_components[public_component_index];
688 let globals = Rc::new(GlobalStorage::new(&compilation_unit));
689 let item_tree = &public.item_tree;
690 let vrc = build_instance(
691 &compilation_unit,
692 item_tree,
693 Weak::new(),
694 globals,
695 Some(public_component_index),
696 type_loaders,
697 );
698 if let Some(adapter) = window_adapter {
699 let _ = vrc.window_adapter.set(adapter);
700 }
701 if let Some((parent, idx)) = embedded_in {
705 let _ = vrc.embedded_in.set((parent, idx));
706 }
707 #[cfg(feature = "bundle-translations")]
710 if let Some(translations) = &compilation_unit.translations
711 && let Some(context) =
712 i_slint_core::window::context_for_root(&vtable::VRc::into_dyn(vrc.clone()))
713 {
714 context.set_bundled_languages(
715 translations.languages.iter().map(|(l, s)| (l.to_string(), *s)),
716 );
717 }
718 finalize_instance(&vrc);
719 vrc
720 }
721
722 pub fn new_repeated(
727 compilation_unit: Rc<CompilationUnit>,
728 item_tree: &llr::ItemTree,
729 parent: Weak<SubComponentInstance>,
730 repeater_idx: RepeatedElementIdx,
731 globals: Rc<GlobalStorage>,
732 ) -> VRc<ItemTreeVTable, Instance> {
733 let vrc = build_instance(
734 &compilation_unit,
735 item_tree,
736 parent.clone(),
737 globals,
738 None,
739 Default::default(),
740 );
741 let _ = vrc.root_sub_component.repeated_in.set((parent, repeater_idx));
742 vrc
743 }
744
745 pub fn new_popup(
749 compilation_unit: Rc<CompilationUnit>,
750 item_tree: &llr::ItemTree,
751 parent: Weak<SubComponentInstance>,
752 globals: Rc<GlobalStorage>,
753 ) -> VRc<ItemTreeVTable, Instance> {
754 build_instance(&compilation_unit, item_tree, parent, globals, None, Default::default())
755 }
756}
757
758fn build_instance(
766 compilation_unit: &Rc<CompilationUnit>,
767 item_tree: &llr::ItemTree,
768 parent: Weak<SubComponentInstance>,
769 globals: Rc<GlobalStorage>,
770 public_component_index: Option<PublicComponentIdx>,
771 type_loaders: crate::component::TypeLoaders,
772) -> VRc<ItemTreeVTable, Instance> {
773 let parent_for_root = parent.clone();
774 let root_sub_component =
775 build_sub_component_instance(compilation_unit, item_tree.root, parent_for_root);
776 let (tree_nodes, dynamic_table, item_table, z_sort_table) = build_tree_nodes(&item_tree.tree);
777
778 let vrc = VRc::new(Instance {
779 root_sub_component,
780 tree_nodes: tree_nodes.into_boxed_slice(),
781 dynamic_table: dynamic_table.into_boxed_slice(),
782 item_table: item_table.into_boxed_slice(),
783 z_sort_table: z_sort_table.into_boxed_slice(),
784 globals,
785 self_weak: OnceCell::new(),
786 parent_instance: parent,
787 public_component_index,
788 window_adapter: OnceCell::new(),
789 window_adapter_error: OnceCell::new(),
790 window_attached: OnceCell::new(),
791 bindings_installed: OnceCell::new(),
792 init_code_run: OnceCell::new(),
793 embedded_in: OnceCell::new(),
794 type_loaders,
795 });
796 let weak = VRc::downgrade(&vrc);
797 let _ = vrc.self_weak.set(weak.clone());
798 let _ = vrc.globals.root.set(weak.clone());
800 propagate_root(&vrc.root_sub_component, &weak);
801 vrc
802}
803
804pub(crate) fn finalize_instance(vrc: &VRc<ItemTreeVTable, Instance>) {
810 init_items_and_bindings(vrc);
811 if vrc.init_code_run.get().is_some() {
812 return;
813 }
814 let _ = vrc.init_code_run.set(());
815 if vrc.public_component_index.is_some() && vrc.embedded_in.get().is_none() {
823 vrc.attach_to_window();
824 }
825 crate::bindings::run_init_code_for_instance(vrc);
826}
827
828pub(crate) fn init_items_and_bindings(vrc: &VRc<ItemTreeVTable, Instance>) {
831 if vrc.bindings_installed.get().is_some() {
832 return;
833 }
834 let _ = vrc.bindings_installed.set(());
835 i_slint_core::item_tree::register_item_tree(
838 &vtable::VRc::into_dyn(vrc.clone()),
839 vrc.window_adapter_or_default(),
840 );
841 let is_root = vrc.parent_instance.upgrade().is_none();
842 if is_root {
843 crate::globals::install_global_bindings(&vrc.globals);
844 }
845 crate::bindings::install_bindings_only(vrc);
846}
847
848fn propagate_root(sub: &Pin<Rc<SubComponentInstance>>, weak: &VWeak<ItemTreeVTable, Instance>) {
850 let _ = sub.root.set(weak.clone());
851 for nested in &sub.sub_components {
852 propagate_root(nested, weak);
853 }
854}
855
856fn build_sub_component_instance(
858 cu: &Rc<CompilationUnit>,
859 sub_idx: SubComponentIdx,
860 parent: Weak<SubComponentInstance>,
861) -> Pin<Rc<SubComponentInstance>> {
862 let sc = &cu.sub_components[sub_idx];
863 let registry = ItemRegistry::global();
864
865 let properties = sc
866 .properties
867 .iter()
868 .map(|p| Rc::pin(Property::new(crate::eval::default_value_for_type(&p.ty))))
869 .collect();
870 let callbacks = sc.callbacks.iter().map(|_| Rc::pin(Callback::default())).collect();
871 let callback_trackers =
872 sc.callbacks.iter().map(|c| c.needs_tracker.then(|| Rc::pin(Property::new(())))).collect();
873 let items =
874 sc.items
875 .iter()
876 .map(|item| {
877 registry.factory(&item.ty.class_name).unwrap_or_else(|| {
878 panic!("native item `{}` is not registered", item.ty.class_name)
879 })()
880 })
881 .collect();
882 let repeaters = sc
883 .repeated
884 .iter()
885 .map(|rep| {
886 if rep.data_prop.is_none() {
887 RepeaterOrConditional::Conditional(Box::pin(Conditional::default()))
888 } else {
889 RepeaterOrConditional::Repeater(Box::pin(Repeater::default()))
890 }
891 })
892 .collect();
893
894 let rc = Rc::new_cyclic(|weak_self: &Weak<SubComponentInstance>| {
898 let sub_components = sc
899 .sub_components
900 .iter()
901 .map(|nested| build_sub_component_instance(cu, nested.ty, weak_self.clone()))
902 .collect();
903 SubComponentInstance {
904 compilation_unit: cu.clone(),
905 sub_component_idx: sub_idx,
906 properties,
907 callbacks,
908 callback_trackers,
909 items,
910 sub_components,
911 repeaters,
912 parent,
913 root: OnceCell::new(),
914 change_trackers: std::iter::repeat_with(ChangeTracker::default)
915 .take(2 * sc.timers.len() + sc.change_callbacks.len())
916 .collect(),
917 timers: std::iter::repeat_with(Default::default).take(sc.timers.len()).collect(),
918 popup_ids: vec![std::cell::Cell::new(None); sc.popup_windows.len()],
919 repeated_in: OnceCell::new(),
920 menubar: RefCell::new(None),
921 }
922 });
923 Pin::new(rc)
924}
925
926fn read_logical_length(
930 sub: &Pin<Rc<SubComponentInstance>>,
931 mr: &llr::MemberReference,
932) -> i_slint_core::lengths::LogicalLength {
933 let mut ctx = crate::eval::EvalContext::new(sub.clone());
934 let v = crate::eval::load_property(&ctx, mr);
935 let _ = &mut ctx;
936 let n: f64 = v.try_into().unwrap_or(0.0);
937 i_slint_core::lengths::LogicalLength::new(n as f32)
938}
939
940struct ValueListViewProps {
946 content_y: llr::MemberReference,
947 content_width: Option<llr::MemberReference>,
950 content_height: Option<llr::MemberReference>,
951 ctx_sub: Pin<Rc<SubComponentInstance>>,
952}
953
954impl i_slint_core::model::ListViewProperties for ValueListViewProps {
955 fn content_y_get(&self) -> i_slint_core::lengths::LogicalLength {
956 read_logical_length(&self.ctx_sub, &self.content_y)
957 }
958 fn content_y_get_internal(&self) -> i_slint_core::lengths::LogicalLength {
959 read_logical_length(&self.ctx_sub, &self.content_y)
963 }
964 fn content_y_set(&self, value: i_slint_core::lengths::LogicalLength) {
965 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
966 crate::eval::store_property(
967 &ctx,
968 &self.content_y,
969 crate::Value::Number(value.get() as f64),
970 );
971 }
972 fn content_y_has_binding(&self) -> bool {
973 false
977 }
978 fn computes_content_height(&self) -> bool {
979 self.content_height.is_some()
980 }
981 fn content_width_set(&self, value: i_slint_core::lengths::LogicalLength) {
982 let Some(content_width) = &self.content_width else { return };
983 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
984 crate::eval::store_property(&ctx, content_width, crate::Value::Number(value.get() as f64));
985 }
986 fn content_height_set(&self, value: i_slint_core::lengths::LogicalLength) {
987 let Some(content_height) = &self.content_height else { return };
988 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
989 crate::eval::store_property(&ctx, content_height, crate::Value::Number(value.get() as f64));
990 }
991 fn register_as_dependencies(&self) {
992 if let Some(content_width) = &self.content_width {
995 let _ = read_logical_length(&self.ctx_sub, content_width);
996 }
997 if let Some(content_height) = &self.content_height {
998 let _ = read_logical_length(&self.ctx_sub, content_height);
999 }
1000 let _ = read_logical_length(&self.ctx_sub, &self.content_y);
1001 }
1002}
1003
1004type DynamicEntry = Option<(Box<[SubComponentInstanceIdx]>, RepeatedElementIdx)>;
1005type ItemEntry = Option<(Box<[SubComponentInstanceIdx]>, ItemInstanceIdx)>;
1006type ZSortEntry = Option<Vec<llr::ZSource>>;
1007
1008fn build_tree_nodes(
1018 root: &llr::TreeNode,
1019) -> (Vec<ItemTreeNode>, Vec<DynamicEntry>, Vec<ItemEntry>, Vec<ZSortEntry>) {
1020 use itertools::Either;
1021
1022 let mut out = Vec::new();
1023 let mut dyn_table: Vec<DynamicEntry> = Vec::new();
1024 let mut item_table: Vec<ItemEntry> = Vec::new();
1025 let mut z_sort_table: Vec<ZSortEntry> = Vec::new();
1026 root.visit_in_array(&mut |node, children_offset, parent_index| {
1027 let parent_index = parent_index as u32;
1028 let (entry, dyn_entry, item_entry) = match node.item_index {
1029 Either::Left(item_idx) => (
1030 ItemTreeNode::Item {
1031 is_accessible: node.is_accessible,
1032 children_count: node.children.len() as u32,
1033 children_index: children_offset as u32,
1034 parent_index,
1035 item_array_index: out.len() as u32,
1038 },
1039 None,
1040 Some((node.sub_component_path.clone().into_boxed_slice(), item_idx)),
1041 ),
1042 Either::Right(dynamic_index) => (
1043 ItemTreeNode::DynamicTree { index: out.len() as u32, parent_index },
1051 Some((
1052 node.sub_component_path.clone().into_boxed_slice(),
1053 (dynamic_index as usize).into(),
1054 )),
1055 None,
1056 ),
1057 };
1058 out.push(entry);
1059 dyn_table.push(dyn_entry);
1060 item_table.push(item_entry);
1061 z_sort_table.push(node.z_sort_order_property.clone());
1062 });
1063 (out, dyn_table, item_table, z_sort_table)
1064}
1065
1066fn repeated_align_self(
1069 sc: &i_slint_compiler::llr::SubComponent,
1070 ctx: &mut crate::eval::EvalContext,
1071 orientation: i_slint_core::items::Orientation,
1072) -> i_slint_core::items::CrossAxisAlignment {
1073 match &sc.cross_axis_self_alignment_for_repeated {
1074 Some((cross_o, expr)) if crate::eval::llr_to_core_orientation(*cross_o) == orientation => {
1075 crate::eval::eval_expression(ctx, &expr.borrow()).try_into().unwrap_or_default()
1076 }
1077 _ => Default::default(),
1078 }
1079}
1080
1081fn repeated_layout_order(
1084 sc: &i_slint_compiler::llr::SubComponent,
1085 ctx: &mut crate::eval::EvalContext,
1086 orientation: i_slint_core::items::Orientation,
1087) -> i32 {
1088 match &sc.layout_order_for_repeated {
1089 Some((main_o, expr)) if crate::eval::llr_to_core_orientation(*main_o) == orientation => {
1090 match crate::eval::eval_expression(ctx, &expr.borrow()) {
1091 crate::Value::Number(n) => n as i32,
1092 _ => 0,
1093 }
1094 }
1095 _ => 0,
1096 }
1097}
1098
1099impl i_slint_core::model::RepeatedItemTree for Instance {
1104 type Data = crate::Value;
1105
1106 fn update(&self, index: usize, data: Self::Data) {
1107 let sc_idx = self.root_sub_component.sub_component_idx;
1108 let cu = self.root_sub_component.compilation_unit.clone();
1109 let sc = &cu.sub_components[sc_idx];
1110 for (idx, prop) in sc.properties.iter_enumerated() {
1115 let target = &self.root_sub_component.properties[idx];
1116 match prop.name.as_str() {
1117 "model_data" => Pin::as_ref(target).set(data.clone()),
1118 "model_index" => Pin::as_ref(target).set(crate::Value::Number(index as f64)),
1119 _ => {}
1120 }
1121 }
1122 }
1123
1124 fn init(&self) {
1125 if let Some(weak) = self.self_weak.get()
1131 && let Some(vrc) = weak.upgrade()
1132 {
1133 finalize_instance(&vrc);
1134 }
1135 }
1136
1137 fn z_order(self: Pin<&Self>) -> Option<f32> {
1138 let this = self.get_ref();
1141 let (parent_weak, rep_idx) = this.root_sub_component.repeated_in.get()?;
1142 let parent_sub = parent_weak.upgrade()?;
1143 let parent_sc = &parent_sub.compilation_unit.sub_components[parent_sub.sub_component_idx];
1144 let z_ref = parent_sc.repeated[*rep_idx].dynamic_z.as_ref()?;
1145 let ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1146 let z: f64 = crate::eval::load_property(&ctx, z_ref).try_into().unwrap_or(0.0);
1147 Some(z as f32)
1148 }
1149
1150 fn listview_layout(
1151 self: Pin<&Self>,
1152 offset_y: &mut i_slint_core::lengths::LogicalLength,
1153 ) -> i_slint_core::lengths::LogicalLength {
1154 use i_slint_core::item_tree::ItemTree as _;
1155 use i_slint_core::lengths::LogicalLength;
1156 let this = self.get_ref();
1160 let Some((parent_weak, rep_idx)) = this.root_sub_component.repeated_in.get() else {
1161 return LogicalLength::default();
1162 };
1163 let Some(parent_sub) = parent_weak.upgrade() else { return LogicalLength::default() };
1164 let parent_sub = Pin::new(parent_sub);
1165 let parent_cu = parent_sub.compilation_unit.clone();
1166 let parent_sc = &parent_cu.sub_components[parent_sub.sub_component_idx];
1167 let Some(lv) = parent_sc.repeated[*rep_idx].listview.as_ref() else {
1168 return LogicalLength::default();
1169 };
1170
1171 let row_sub = this.root_sub_component.clone();
1175 let ctx = crate::eval::EvalContext::new(row_sub.clone());
1176 crate::eval::store_property(&ctx, &lv.prop_y, crate::Value::Number(offset_y.get() as f64));
1177 let height_v = crate::eval::load_property(&ctx, &lv.prop_height);
1178 let height: f64 = height_v.try_into().unwrap_or(0.0);
1179 *offset_y += LogicalLength::new(height as f32);
1180 let info = self.layout_info(i_slint_core::items::Orientation::Horizontal);
1181 LogicalLength::new(info.min)
1182 }
1183
1184 fn layout_item_info(
1185 self: Pin<&Self>,
1186 orientation: i_slint_core::items::Orientation,
1187 child_index: Option<usize>,
1188 ) -> i_slint_core::layout::LayoutItemInfo {
1189 let this = self.get_ref();
1198 let cu = this.root_sub_component.compilation_unit.clone();
1199 let sc_idx = this.root_sub_component.sub_component_idx;
1200 let sc = &cu.sub_components[sc_idx];
1201
1202 if let (Some(index), true, Some(templates)) =
1203 (child_index, sc.is_repeated_row, sc.row_child_templates.as_ref())
1204 {
1205 return row_child_layout_item_info(this, sc, templates, orientation, index);
1206 }
1207
1208 let expr = match orientation {
1209 i_slint_core::items::Orientation::Horizontal => sc.layout_info_h.borrow(),
1210 i_slint_core::items::Orientation::Vertical => sc.layout_info_v.borrow(),
1211 };
1212 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1213 let constraint =
1214 crate::eval::eval_expression(&mut ctx, &expr).try_into().unwrap_or_default();
1215 i_slint_core::layout::LayoutItemInfo {
1216 constraint,
1217 cross_axis_self_alignment: repeated_align_self(sc, &mut ctx, orientation),
1218 layout_order: repeated_layout_order(sc, &mut ctx, orientation),
1219 }
1220 }
1221
1222 fn layout_item_info_at_cross_width(
1228 self: Pin<&Self>,
1229 cross_width: f32,
1230 ) -> i_slint_core::layout::LayoutItemInfo {
1231 use i_slint_compiler::llr::lower_layout_expression::CROSS_WIDTH_LOCAL;
1232 let orientation = i_slint_core::items::Orientation::Vertical;
1233 let cu = self.root_sub_component.compilation_unit.clone();
1234 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1235 let Some(expr) = sc
1236 .layout_info_v_at_cross_width_for_repeated
1237 .as_ref()
1238 .filter(|_| sc.flexbox_layout_item_info_for_repeated.is_none())
1239 else {
1240 return self.layout_item_info(orientation, None);
1241 };
1242 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1243 ctx.locals.insert(CROSS_WIDTH_LOCAL.into(), crate::Value::Number(cross_width as f64));
1244 let constraint =
1245 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().unwrap_or_default();
1246 i_slint_core::layout::LayoutItemInfo {
1250 constraint,
1251 cross_axis_self_alignment: repeated_align_self(sc, &mut ctx, orientation),
1252 layout_order: repeated_layout_order(sc, &mut ctx, orientation),
1253 }
1254 }
1255
1256 fn flexbox_layout_item_info(
1257 self: Pin<&Self>,
1258 orientation: i_slint_core::items::Orientation,
1259 child_index: Option<usize>,
1260 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1261 let cu = self.root_sub_component.compilation_unit.clone();
1265 let sc_idx = self.root_sub_component.sub_component_idx;
1266 let sc = &cu.sub_components[sc_idx];
1267 if let Some(expr) = &sc.flexbox_layout_item_info_for_repeated {
1268 let expr = expr.borrow();
1269 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1270 let value = crate::eval::eval_expression(&mut ctx, &expr);
1271 let mut info = value_to_flexbox_layout_item_info(value, orientation, self);
1272 if matches!(orientation, i_slint_core::items::Orientation::Vertical)
1278 && child_index.is_none()
1279 && let Some(v_expr) = &sc.layout_info_v_constrained_for_repeated
1280 {
1281 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1282 info.constraint = crate::eval::eval_expression(&mut ctx, &v_expr.borrow())
1283 .try_into()
1284 .unwrap_or_default();
1285 return info;
1286 }
1287 info.constraint = self.layout_item_info(orientation, child_index).constraint;
1290 return info;
1291 }
1292 let info = self.layout_item_info(orientation, None);
1293 info.into()
1294 }
1295}
1296
1297impl Instance {
1298 pub fn flexbox_layout_item_info_at_cross_width(
1302 self: Pin<&Self>,
1303 cross_width: f32,
1304 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1305 use i_slint_core::items::Orientation;
1306 use i_slint_core::model::RepeatedItemTree;
1307 let mut info =
1308 RepeatedItemTree::flexbox_layout_item_info(self, Orientation::Vertical, None);
1309 let cu = self.root_sub_component.compilation_unit.clone();
1310 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1311 if let Some(v_expr) = &sc.layout_info_v_at_cross_width_for_repeated {
1312 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1313 ctx.locals.insert(
1314 i_slint_compiler::llr::lower_layout_expression::CROSS_WIDTH_LOCAL.into(),
1315 crate::Value::Number(cross_width as f64),
1316 );
1317 info.constraint = crate::eval::eval_expression(&mut ctx, &v_expr.borrow())
1318 .try_into()
1319 .unwrap_or_default();
1320 }
1321 info
1322 }
1323}
1324
1325fn row_child_layout_item_info(
1329 this: &Instance,
1330 sc: &i_slint_compiler::llr::SubComponent,
1331 templates: &[i_slint_compiler::llr::RowChildTemplateInfo],
1332 orientation: i_slint_core::items::Orientation,
1333 mut index: usize,
1334) -> i_slint_core::layout::LayoutItemInfo {
1335 use i_slint_compiler::llr::RowChildTemplateInfo;
1336 use i_slint_core::model::RepeatedItemTree;
1337 let flat_index = index;
1340 for entry in templates {
1341 match entry {
1342 RowChildTemplateInfo::Static { child_index } => {
1343 if index == 0 {
1344 let child = &sc.grid_layout_children[*child_index];
1345 let expr = match orientation {
1346 i_slint_core::items::Orientation::Horizontal => {
1347 child.layout_info_h.borrow()
1348 }
1349 i_slint_core::items::Orientation::Vertical => child.layout_info_v.borrow(),
1350 };
1351 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1352 let constraint = crate::eval::eval_expression(&mut ctx, &expr)
1353 .try_into()
1354 .unwrap_or_default();
1355 return i_slint_core::layout::LayoutItemInfo {
1356 constraint,
1357 ..Default::default()
1358 };
1359 }
1360 index -= 1;
1361 }
1362 RowChildTemplateInfo::Repeated { repeater_index, measure_at_cross_width } => {
1363 let repeater = &this.root_sub_component.repeaters[*repeater_index];
1364 repeater.track_instance_changes();
1365 let count = repeater.range().len();
1366 if index < count {
1367 if let Some(inner) = repeater.instance_at(index) {
1368 if *measure_at_cross_width
1372 && orientation == i_slint_core::items::Orientation::Vertical
1373 && let Some(w) = row_child_cross_width(this, sc, flat_index)
1374 {
1375 return RepeatedItemTree::layout_item_info_at_cross_width(
1376 inner.as_pin_ref(),
1377 w,
1378 );
1379 }
1380 return RepeatedItemTree::layout_item_info(
1381 inner.as_pin_ref(),
1382 orientation,
1383 None,
1384 );
1385 }
1386 return i_slint_core::layout::LayoutItemInfo::default();
1387 }
1388 index -= count;
1389 }
1390 }
1391 }
1392 i_slint_core::layout::LayoutItemInfo::default()
1393}
1394
1395fn row_child_cross_width(
1400 this: &Instance,
1401 sc: &i_slint_compiler::llr::SubComponent,
1402 flat_index: usize,
1403) -> Option<f32> {
1404 use i_slint_compiler::llr::lower_layout_expression::GRID_MEASURE_CHILD_INDEX_LOCAL;
1405 let expr = sc.grid_row_child_cross_width.as_ref()?;
1406 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1407 ctx.locals
1408 .insert(GRID_MEASURE_CHILD_INDEX_LOCAL.into(), crate::Value::Number(flat_index as f64));
1409 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().ok()
1410}
1411
1412fn value_to_flexbox_layout_item_info(
1413 v: crate::Value,
1414 orientation: i_slint_core::items::Orientation,
1415 instance: Pin<&Instance>,
1416) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1417 use i_slint_core::model::RepeatedItemTree;
1418 let crate::Value::Struct(s) = v else {
1419 let info = RepeatedItemTree::layout_item_info(instance, orientation, None);
1420 return info.into();
1421 };
1422 crate::eval_layout::flexbox_item_info_from_struct(&s)
1423}