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 window_attachment_disabled: bool,
240 pub bindings_installed: OnceCell<()>,
243 pub init_code_run: OnceCell<()>,
249 pub embedded_in: OnceCell<(VWeak<ItemTreeVTable>, u32)>,
255 pub type_loaders: crate::component::TypeLoaders,
260}
261
262impl Drop for Instance {
263 fn drop(&mut self) {
264 let Some(adapter) = self.window_adapter.get().cloned().or_else(|| {
274 let mut parent = self.parent_instance.upgrade();
275 while let Some(sub) = parent {
276 let root = sub.root.get().and_then(|w| w.upgrade())?;
277 if let Some(a) = root.window_adapter.get() {
278 return Some(a.clone());
279 }
280 parent = root.parent_instance.upgrade();
281 }
282 None
283 }) else {
284 return;
285 };
286 vtable::new_vref!(let item_tree_ref : VRef<i_slint_core::item_tree::ItemTreeVTable> for i_slint_core::item_tree::ItemTree = self);
287 let items = collect_item_refs(&self.root_sub_component);
288 for item in &items {
293 item.as_ref().deinit(&adapter);
294 }
295 let _ =
296 adapter.renderer().free_graphics_resources(item_tree_ref, &mut items.iter().copied());
297 if let Some(internal) = adapter.internal(i_slint_core::InternalToken) {
298 internal.unregister_item_tree(item_tree_ref, &mut items.iter().copied());
299 }
300 let window_inner = i_slint_core::window::WindowInner::from_pub(adapter.window());
301 let to_close_popups = window_inner
302 .active_popups()
303 .iter()
304 .filter_map(|p| p.parent_item.upgrade().is_none().then_some(p.popup_id))
305 .collect::<Vec<_>>();
306 for popup_id in to_close_popups {
307 window_inner.close_popup(popup_id);
308 }
309 }
310}
311
312fn collect_item_refs<'a>(
315 sub: &'a Pin<Rc<SubComponentInstance>>,
316) -> Vec<Pin<vtable::VRef<'a, i_slint_core::items::ItemVTable>>> {
317 let mut out = Vec::new();
318 fn walk<'a>(
319 sub: &'a Pin<Rc<SubComponentInstance>>,
320 out: &mut Vec<Pin<vtable::VRef<'a, i_slint_core::items::ItemVTable>>>,
321 ) {
322 for item in &sub.items {
323 out.push(Pin::as_ref(item).as_item_ref());
324 }
325 for nested in &sub.sub_components {
326 walk(nested, out);
327 }
328 }
329 walk(sub, &mut out);
330 out
331}
332
333impl Instance {
334 pub fn window_adapter_or_default(&self) -> Option<WindowAdapterRc> {
337 self.try_window_adapter().ok()
338 }
339
340 pub fn try_window_adapter(&self) -> Result<WindowAdapterRc, i_slint_core::api::PlatformError> {
356 if let Some(a) = self.window_adapter.get() {
357 return Ok(a.clone());
358 }
359 if let Some((outer_weak, _)) = self.embedded_in.get()
365 && let Some(outer) = outer_weak.upgrade()
366 {
367 let mut result = None;
368 vtable::VRc::borrow_pin(&outer).as_ref().window_adapter(true, &mut result);
369 if let Some(a) = result {
370 let _ = self.window_adapter.set(a.clone());
371 return Ok(a);
372 }
373 }
374 let mut outermost_root = None;
377 let mut parent_sub = self.parent_instance.upgrade();
378 while let Some(sub) = parent_sub {
379 let Some(root_vrc) = sub.root.get().and_then(|w| w.upgrade()) else { break };
380 if let Some(a) = root_vrc.window_adapter.get() {
381 let cloned = a.clone();
382 let _ = self.window_adapter.set(cloned.clone());
385 return Ok(cloned);
386 }
387 parent_sub = root_vrc.parent_instance.upgrade();
388 outermost_root = Some(root_vrc);
389 }
390 if let Some(e) = self
391 .window_adapter_error
392 .get()
393 .or_else(|| outermost_root.as_ref().and_then(|root| root.window_adapter_error.get()))
394 {
395 return Err(i_slint_core::api::PlatformError::Other(e.clone()));
396 }
397 let context = &self.globals.context;
398 let adapter = context.create_window_adapter().inspect_err(|e| {
399 let msg = e.to_string();
400 if let Some(root) = &outermost_root {
401 let _ = root.window_adapter_error.set(msg.clone());
402 }
403 let _ = self.window_adapter_error.set(msg);
404 })?;
405 adapter.renderer().set_window_adapter(&adapter);
409 if let Some(root) = outermost_root {
413 let _ = root.window_adapter.set(adapter.clone());
414 }
415 let _ = self.window_adapter.set(adapter.clone());
416 Ok(adapter)
417 }
418
419 pub fn attach_to_window(&self) {
428 if self.window_attached.get().is_some()
431 || self.window_attachment_disabled
432 || self.embedded_in.get().is_some()
433 {
434 return;
435 }
436 let Some(adapter) = self.window_adapter_or_default() else { return };
437 let Some(self_rc) = self.self_weak.get().and_then(|w| w.upgrade()) else { return };
438 let _ = self.window_attached.set(());
439 i_slint_core::window::WindowInner::from_pub(adapter.window())
440 .set_component(&vtable::VRc::into_dyn(self_rc));
441 }
442}
443
444pub(crate) fn component_container_item(
450 sub: &Pin<Rc<SubComponentInstance>>,
451 rep_idx: RepeatedElementIdx,
452) -> Option<Pin<&i_slint_core::items::ComponentContainer>> {
453 let sc = &sub.compilation_unit.sub_components[sub.sub_component_idx];
454 let cc_item_idx = sc.repeated.get(rep_idx)?.container_item_index?;
455 let item = sub.items.get(cc_item_idx)?;
456 i_slint_core::items::ItemRef::downcast_pin::<i_slint_core::items::ComponentContainer>(
457 Pin::as_ref(item).as_item_ref(),
458 )
459}
460
461impl Instance {
462 pub fn dynamic_at(
466 &self,
467 tree_index: u32,
468 ) -> Option<(Pin<Rc<SubComponentInstance>>, RepeatedElementIdx)> {
469 let entry = self.dynamic_table.get(tree_index as usize)?.as_ref()?;
470 let mut current = self.root_sub_component.clone();
471 for &idx in entry.0.iter() {
472 let next = current.sub_components[idx].clone();
473 current = next;
474 }
475 Some((current, entry.1))
476 }
477
478 pub fn ensure_updated(&self, tree_index: u32) -> bool {
487 let Some((sub, rep_idx)) = self.dynamic_at(tree_index) else { return false };
488 if let Some(cc) = component_container_item(&sub, rep_idx) {
489 return cc.ensure_updated();
490 }
491 let cu = sub.compilation_unit.clone();
492 let sc_idx = sub.sub_component_idx;
493 let sub_weak = Rc::downgrade(&Pin::into_inner(sub.clone()));
494 let globals = self.globals.clone();
495 let repeated = &cu.sub_components[sc_idx].repeated[rep_idx];
496 let listview_factory = repeated.listview.is_some();
497 let listview_info = repeated.listview.clone();
498 let factory = move || {
499 let item_tree = &cu.sub_components[sc_idx].repeated[rep_idx].sub_tree;
500 let vrc = Instance::new_repeated(
501 cu.clone(),
502 item_tree,
503 sub_weak.clone(),
504 rep_idx,
505 globals.clone(),
506 );
507 if listview_factory {
508 init_items_and_bindings(&vrc);
513 }
514 vrc
515 };
516 let repeater = &sub.repeaters[rep_idx];
517 if let Some(lv) = listview_info.as_ref() {
518 let listview_width = read_logical_length(&sub, &lv.listview_width);
519 let listview_height = read_logical_length(&sub, &lv.listview_height);
520 if listview_height.get() <= 0.0 {
525 return false;
526 }
527 let props = ValueListViewProps {
528 content_y: lv.content_y.clone(),
529 content_width: lv.content_width.clone(),
530 content_height: lv.content_height.clone(),
531 ctx_sub: sub.clone(),
532 };
533 repeater.ensure_updated_listview_callback(
534 factory,
535 &props,
536 listview_width,
537 listview_height,
538 )
539 } else {
540 repeater.ensure_updated(factory)
541 }
542 }
543
544 pub fn ensure_instantiated(&self) -> bool {
549 let mut changed = false;
550 for idx in 0..self.dynamic_table.len() {
551 if self.dynamic_table[idx].is_some() {
552 changed |= self.ensure_updated(idx as u32);
553 }
554 }
555 changed
556 }
557
558 pub fn visit_dynamic_children(
566 self: Pin<&Self>,
567 dyn_index: u32,
568 order: i_slint_core::item_tree::TraversalOrder,
569 visitor: vtable::VRefMut<'_, i_slint_core::item_tree::ItemVisitorVTable>,
570 ) -> i_slint_core::item_tree::VisitChildrenResult {
571 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(dyn_index) else {
572 return i_slint_core::item_tree::VisitChildrenResult::CONTINUE;
573 };
574 if let Some(cc) = component_container_item(&sub, rep_idx) {
575 return cc.visit_children_item(-1, order, visitor);
576 }
577 let repeater = &sub.repeaters[rep_idx];
581 let sc = &sub.compilation_unit.sub_components[sub.sub_component_idx];
582 if let (Some(lv), RepeaterOrConditional::Repeater(r)) =
583 (sc.repeated[rep_idx].listview.as_ref(), repeater)
584 {
585 let props = ValueListViewProps {
586 content_y: lv.content_y.clone(),
587 content_width: lv.content_width.clone(),
588 content_height: lv.content_height.clone(),
589 ctx_sub: sub.clone(),
590 };
591 let listview_width = read_logical_length(&sub, &lv.listview_width);
592 let _ = read_logical_length(&sub, &lv.listview_height);
593 Pin::as_ref(r).track_changes_listview_callback(&props, listview_width);
594 }
595 repeater.visit(order, visitor)
596 }
597
598 pub fn collect_z_sorted_children(
603 self: Pin<&Self>,
604 index: isize,
605 push: &mut dyn FnMut(u32, Option<u32>, f32),
606 ) {
607 let Some(Some(sources)) = self.z_sort_table.get(index as usize) else { return };
608 let ItemTreeNode::Item { children_index, .. } = self.tree_nodes[index as usize] else {
609 return;
610 };
611 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
612 for (k, source) in sources.iter().enumerate() {
613 let child_offset = k as u32;
614 match source {
615 llr::ZSource::Expression(e) => {
616 let z: f64 = crate::eval::eval_expression(&mut ctx, &e.borrow())
617 .try_into()
618 .unwrap_or(0.0);
619 push(child_offset, None, z as f32);
620 }
621 llr::ZSource::RepeaterInstances => {
622 if let Some((sub, rep_idx)) =
624 self.get_ref().dynamic_at(children_index + child_offset)
625 {
626 sub.repeaters[rep_idx].for_each_instance_z(&mut |instance, z| {
627 push(child_offset, Some(instance), z)
628 });
629 }
630 }
631 }
632 }
633 }
634
635 pub fn new_with_window(
639 compilation_unit: Rc<CompilationUnit>,
640 public_component_index: PublicComponentIdx,
641 window_adapter: i_slint_core::window::WindowAdapterRc,
642 type_loaders: crate::component::TypeLoaders,
643 ) -> VRc<ItemTreeVTable, Instance> {
644 let context =
645 i_slint_core::window::WindowInner::from_pub(window_adapter.window()).context().clone();
646 Self::new_with_options(
647 compilation_unit,
648 public_component_index,
649 Some(window_adapter),
650 type_loaders,
651 None,
652 context,
653 false,
654 )
655 }
656
657 pub fn new_with_context(
660 compilation_unit: Rc<CompilationUnit>,
661 public_component_index: PublicComponentIdx,
662 type_loaders: crate::component::TypeLoaders,
663 context: i_slint_core::SlintContext,
664 ) -> VRc<ItemTreeVTable, Instance> {
665 Self::new_with_options(
666 compilation_unit,
667 public_component_index,
668 None,
669 type_loaders,
670 None,
671 context,
672 false,
673 )
674 }
675
676 #[cfg(feature = "internal")]
677 pub fn new_detached_with_window(
678 compilation_unit: Rc<CompilationUnit>,
679 public_component_index: PublicComponentIdx,
680 window_adapter: i_slint_core::window::WindowAdapterRc,
681 type_loaders: crate::component::TypeLoaders,
682 ) -> VRc<ItemTreeVTable, Instance> {
683 let context =
684 i_slint_core::window::WindowInner::from_pub(window_adapter.window()).context().clone();
685 Self::new_with_options(
686 compilation_unit,
687 public_component_index,
688 Some(window_adapter),
689 type_loaders,
690 None,
691 context,
692 true,
693 )
694 }
695
696 pub fn new_embedded(
701 compilation_unit: Rc<CompilationUnit>,
702 public_component_index: PublicComponentIdx,
703 type_loaders: crate::component::TypeLoaders,
704 parent: vtable::VWeak<ItemTreeVTable>,
705 parent_item_tree_index: u32,
706 ) -> VRc<ItemTreeVTable, Instance> {
707 let host = parent
708 .upgrade()
709 .expect("the host item tree is alive while it builds its embedded component");
710 let context = i_slint_core::window::context_for_root(&host);
711 Self::new_with_options(
712 compilation_unit,
713 public_component_index,
714 None,
715 type_loaders,
716 Some((parent, parent_item_tree_index)),
717 context,
718 false,
719 )
720 }
721
722 fn new_with_options(
725 compilation_unit: Rc<CompilationUnit>,
726 public_component_index: PublicComponentIdx,
727 window_adapter: Option<i_slint_core::window::WindowAdapterRc>,
728 type_loaders: crate::component::TypeLoaders,
729 embedded_in: Option<(vtable::VWeak<ItemTreeVTable>, u32)>,
730 context: i_slint_core::SlintContext,
731 disable_window_attachment: bool,
732 ) -> VRc<ItemTreeVTable, Instance> {
733 let public = &compilation_unit.public_components[public_component_index];
734 let globals = Rc::new(GlobalStorage::new(&compilation_unit, context));
735 let item_tree = &public.item_tree;
736 let vrc = build_instance(
737 &compilation_unit,
738 item_tree,
739 Weak::new(),
740 globals,
741 Some(public_component_index),
742 type_loaders,
743 disable_window_attachment,
744 );
745 if let Some(adapter) = window_adapter {
746 let _ = vrc.window_adapter.set(adapter);
747 }
748 if let Some((parent, idx)) = embedded_in {
752 let _ = vrc.embedded_in.set((parent, idx));
753 }
754 #[cfg(feature = "bundle-translations")]
756 if let Some(translations) = &compilation_unit.translations {
757 vrc.globals.context.set_bundled_languages(
758 translations.languages.iter().map(|(l, s)| (l.to_string(), *s)),
759 );
760 }
761 finalize_instance(&vrc);
762 vrc
763 }
764
765 pub fn new_repeated(
770 compilation_unit: Rc<CompilationUnit>,
771 item_tree: &llr::ItemTree,
772 parent: Weak<SubComponentInstance>,
773 repeater_idx: RepeatedElementIdx,
774 globals: Rc<GlobalStorage>,
775 ) -> VRc<ItemTreeVTable, Instance> {
776 let vrc = build_instance(
777 &compilation_unit,
778 item_tree,
779 parent.clone(),
780 globals,
781 None,
782 Default::default(),
783 false,
784 );
785 let _ = vrc.root_sub_component.repeated_in.set((parent, repeater_idx));
786 vrc
787 }
788
789 pub fn new_popup(
793 compilation_unit: Rc<CompilationUnit>,
794 item_tree: &llr::ItemTree,
795 parent: Weak<SubComponentInstance>,
796 globals: Rc<GlobalStorage>,
797 ) -> VRc<ItemTreeVTable, Instance> {
798 build_instance(
799 &compilation_unit,
800 item_tree,
801 parent,
802 globals,
803 None,
804 Default::default(),
805 false,
806 )
807 }
808}
809
810fn build_instance(
818 compilation_unit: &Rc<CompilationUnit>,
819 item_tree: &llr::ItemTree,
820 parent: Weak<SubComponentInstance>,
821 globals: Rc<GlobalStorage>,
822 public_component_index: Option<PublicComponentIdx>,
823 type_loaders: crate::component::TypeLoaders,
824 disable_window_attachment: bool,
825) -> VRc<ItemTreeVTable, Instance> {
826 let parent_for_root = parent.clone();
827 let root_sub_component =
828 build_sub_component_instance(compilation_unit, item_tree.root, parent_for_root);
829 let (tree_nodes, dynamic_table, item_table, z_sort_table) = build_tree_nodes(&item_tree.tree);
830
831 let vrc = VRc::new(Instance {
832 root_sub_component,
833 tree_nodes: tree_nodes.into_boxed_slice(),
834 dynamic_table: dynamic_table.into_boxed_slice(),
835 item_table: item_table.into_boxed_slice(),
836 z_sort_table: z_sort_table.into_boxed_slice(),
837 globals,
838 self_weak: OnceCell::new(),
839 parent_instance: parent,
840 public_component_index,
841 window_adapter: OnceCell::new(),
842 window_adapter_error: OnceCell::new(),
843 window_attached: OnceCell::new(),
844 window_attachment_disabled: disable_window_attachment,
845 bindings_installed: OnceCell::new(),
846 init_code_run: OnceCell::new(),
847 embedded_in: OnceCell::new(),
848 type_loaders,
849 });
850 let weak = VRc::downgrade(&vrc);
851 let _ = vrc.self_weak.set(weak.clone());
852 let _ = vrc.globals.root.set(weak.clone());
854 propagate_root(&vrc.root_sub_component, &weak);
855 vrc
856}
857
858pub(crate) fn finalize_instance(vrc: &VRc<ItemTreeVTable, Instance>) {
864 init_items_and_bindings(vrc);
865 if vrc.init_code_run.get().is_some() {
866 return;
867 }
868 let _ = vrc.init_code_run.set(());
869 if vrc.public_component_index.is_some() && vrc.embedded_in.get().is_none() {
877 vrc.attach_to_window();
878 }
879 crate::bindings::run_init_code_for_instance(vrc);
880}
881
882pub(crate) fn init_items_and_bindings(vrc: &VRc<ItemTreeVTable, Instance>) {
885 if vrc.bindings_installed.get().is_some() {
886 return;
887 }
888 let _ = vrc.bindings_installed.set(());
889 i_slint_core::item_tree::register_item_tree(
892 &vtable::VRc::into_dyn(vrc.clone()),
893 vrc.window_adapter_or_default(),
894 );
895 let is_root = vrc.parent_instance.upgrade().is_none();
896 if is_root {
897 crate::globals::install_global_bindings(&vrc.globals);
898 }
899 crate::bindings::install_bindings_only(vrc);
900}
901
902fn propagate_root(sub: &Pin<Rc<SubComponentInstance>>, weak: &VWeak<ItemTreeVTable, Instance>) {
904 let _ = sub.root.set(weak.clone());
905 for nested in &sub.sub_components {
906 propagate_root(nested, weak);
907 }
908}
909
910fn build_sub_component_instance(
912 cu: &Rc<CompilationUnit>,
913 sub_idx: SubComponentIdx,
914 parent: Weak<SubComponentInstance>,
915) -> Pin<Rc<SubComponentInstance>> {
916 let sc = &cu.sub_components[sub_idx];
917 let registry = ItemRegistry::global();
918
919 let properties = sc
920 .properties
921 .iter()
922 .map(|p| Rc::pin(Property::new(crate::eval::default_value_for_type(&p.ty))))
923 .collect();
924 let callbacks = sc.callbacks.iter().map(|_| Rc::pin(Callback::default())).collect();
925 let callback_trackers =
926 sc.callbacks.iter().map(|c| c.needs_tracker.then(|| Rc::pin(Property::new(())))).collect();
927 let items =
928 sc.items
929 .iter()
930 .map(|item| {
931 registry.factory(&item.ty.class_name).unwrap_or_else(|| {
932 panic!("native item `{}` is not registered", item.ty.class_name)
933 })()
934 })
935 .collect();
936 let repeaters = sc
937 .repeated
938 .iter()
939 .map(|rep| {
940 if rep.data_prop.is_none() {
941 RepeaterOrConditional::Conditional(Box::pin(Conditional::default()))
942 } else {
943 RepeaterOrConditional::Repeater(Box::pin(Repeater::default()))
944 }
945 })
946 .collect();
947
948 let rc = Rc::new_cyclic(|weak_self: &Weak<SubComponentInstance>| {
952 let sub_components = sc
953 .sub_components
954 .iter()
955 .map(|nested| build_sub_component_instance(cu, nested.ty, weak_self.clone()))
956 .collect();
957 SubComponentInstance {
958 compilation_unit: cu.clone(),
959 sub_component_idx: sub_idx,
960 properties,
961 callbacks,
962 callback_trackers,
963 items,
964 sub_components,
965 repeaters,
966 parent,
967 root: OnceCell::new(),
968 change_trackers: std::iter::repeat_with(ChangeTracker::default)
969 .take(2 * sc.timers.len() + sc.change_callbacks.len())
970 .collect(),
971 timers: std::iter::repeat_with(Default::default).take(sc.timers.len()).collect(),
972 popup_ids: vec![std::cell::Cell::new(None); sc.popup_windows.len()],
973 repeated_in: OnceCell::new(),
974 menubar: RefCell::new(None),
975 }
976 });
977 Pin::new(rc)
978}
979
980fn read_logical_length(
984 sub: &Pin<Rc<SubComponentInstance>>,
985 mr: &llr::MemberReference,
986) -> i_slint_core::lengths::LogicalLength {
987 let mut ctx = crate::eval::EvalContext::new(sub.clone());
988 let v = crate::eval::load_property(&ctx, mr);
989 let _ = &mut ctx;
990 let n: f64 = v.try_into().unwrap_or(0.0);
991 i_slint_core::lengths::LogicalLength::new(n as f32)
992}
993
994struct ValueListViewProps {
1000 content_y: llr::MemberReference,
1001 content_width: Option<llr::MemberReference>,
1004 content_height: Option<llr::MemberReference>,
1005 ctx_sub: Pin<Rc<SubComponentInstance>>,
1006}
1007
1008impl i_slint_core::model::ListViewProperties for ValueListViewProps {
1009 fn content_y_get(&self) -> i_slint_core::lengths::LogicalLength {
1010 read_logical_length(&self.ctx_sub, &self.content_y)
1011 }
1012 fn content_y_get_internal(&self) -> i_slint_core::lengths::LogicalLength {
1013 read_logical_length(&self.ctx_sub, &self.content_y)
1017 }
1018 fn content_y_set(&self, value: i_slint_core::lengths::LogicalLength) {
1019 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
1020 crate::eval::store_property(
1021 &ctx,
1022 &self.content_y,
1023 crate::Value::Number(value.get() as f64),
1024 );
1025 }
1026 fn content_y_has_binding(&self) -> bool {
1027 false
1031 }
1032 fn computes_content_height(&self) -> bool {
1033 self.content_height.is_some()
1034 }
1035 fn content_width_set(&self, value: i_slint_core::lengths::LogicalLength) {
1036 let Some(content_width) = &self.content_width else { return };
1037 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
1038 crate::eval::store_property(&ctx, content_width, crate::Value::Number(value.get() as f64));
1039 }
1040 fn content_height_set(&self, value: i_slint_core::lengths::LogicalLength) {
1041 let Some(content_height) = &self.content_height else { return };
1042 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
1043 crate::eval::store_property(&ctx, content_height, crate::Value::Number(value.get() as f64));
1044 }
1045 fn register_as_dependencies(&self) {
1046 if let Some(content_width) = &self.content_width {
1049 let _ = read_logical_length(&self.ctx_sub, content_width);
1050 }
1051 if let Some(content_height) = &self.content_height {
1052 let _ = read_logical_length(&self.ctx_sub, content_height);
1053 }
1054 let _ = read_logical_length(&self.ctx_sub, &self.content_y);
1055 }
1056}
1057
1058type DynamicEntry = Option<(Box<[SubComponentInstanceIdx]>, RepeatedElementIdx)>;
1059type ItemEntry = Option<(Box<[SubComponentInstanceIdx]>, ItemInstanceIdx)>;
1060type ZSortEntry = Option<Vec<llr::ZSource>>;
1061
1062fn build_tree_nodes(
1072 root: &llr::TreeNode,
1073) -> (Vec<ItemTreeNode>, Vec<DynamicEntry>, Vec<ItemEntry>, Vec<ZSortEntry>) {
1074 use itertools::Either;
1075
1076 let mut out = Vec::new();
1077 let mut dyn_table: Vec<DynamicEntry> = Vec::new();
1078 let mut item_table: Vec<ItemEntry> = Vec::new();
1079 let mut z_sort_table: Vec<ZSortEntry> = Vec::new();
1080 root.visit_in_array(&mut |node, children_offset, parent_index| {
1081 let parent_index = parent_index as u32;
1082 let (entry, dyn_entry, item_entry) = match node.item_index {
1083 Either::Left(item_idx) => (
1084 ItemTreeNode::Item {
1085 is_accessible: node.is_accessible,
1086 children_count: node.children.len() as u32,
1087 children_index: children_offset as u32,
1088 parent_index,
1089 item_array_index: out.len() as u32,
1092 },
1093 None,
1094 Some((node.sub_component_path.clone().into_boxed_slice(), item_idx)),
1095 ),
1096 Either::Right(dynamic_index) => (
1097 ItemTreeNode::DynamicTree { index: out.len() as u32, parent_index },
1105 Some((
1106 node.sub_component_path.clone().into_boxed_slice(),
1107 (dynamic_index as usize).into(),
1108 )),
1109 None,
1110 ),
1111 };
1112 out.push(entry);
1113 dyn_table.push(dyn_entry);
1114 item_table.push(item_entry);
1115 z_sort_table.push(node.z_sort_order_property.clone());
1116 });
1117 (out, dyn_table, item_table, z_sort_table)
1118}
1119
1120fn repeated_align_self(
1123 sc: &i_slint_compiler::llr::SubComponent,
1124 ctx: &mut crate::eval::EvalContext,
1125 orientation: i_slint_core::items::Orientation,
1126) -> i_slint_core::items::CrossAxisAlignment {
1127 match &sc.cross_axis_self_alignment_for_repeated {
1128 Some((cross_o, expr)) if crate::eval::llr_to_core_orientation(*cross_o) == orientation => {
1129 crate::eval::eval_expression(ctx, &expr.borrow()).try_into().unwrap_or_default()
1130 }
1131 _ => Default::default(),
1132 }
1133}
1134
1135fn repeated_layout_order(
1138 sc: &i_slint_compiler::llr::SubComponent,
1139 ctx: &mut crate::eval::EvalContext,
1140 orientation: i_slint_core::items::Orientation,
1141) -> i32 {
1142 match &sc.layout_order_for_repeated {
1143 Some((main_o, expr)) if crate::eval::llr_to_core_orientation(*main_o) == orientation => {
1144 match crate::eval::eval_expression(ctx, &expr.borrow()) {
1145 crate::Value::Number(n) => n as i32,
1146 _ => 0,
1147 }
1148 }
1149 _ => 0,
1150 }
1151}
1152
1153impl i_slint_core::model::RepeatedItemTree for Instance {
1158 type Data = crate::Value;
1159
1160 fn update(&self, index: usize, data: Self::Data) {
1161 let sc_idx = self.root_sub_component.sub_component_idx;
1162 let cu = self.root_sub_component.compilation_unit.clone();
1163 let sc = &cu.sub_components[sc_idx];
1164 for (idx, prop) in sc.properties.iter_enumerated() {
1169 let target = &self.root_sub_component.properties[idx];
1170 match prop.name.as_str() {
1171 "model_data" => Pin::as_ref(target).set(data.clone()),
1172 "model_index" => Pin::as_ref(target).set(crate::Value::Number(index as f64)),
1173 _ => {}
1174 }
1175 }
1176 }
1177
1178 fn init(&self) {
1179 if let Some(weak) = self.self_weak.get()
1185 && let Some(vrc) = weak.upgrade()
1186 {
1187 finalize_instance(&vrc);
1188 }
1189 }
1190
1191 fn z_order(self: Pin<&Self>) -> Option<f32> {
1192 let this = self.get_ref();
1195 let (parent_weak, rep_idx) = this.root_sub_component.repeated_in.get()?;
1196 let parent_sub = parent_weak.upgrade()?;
1197 let parent_sc = &parent_sub.compilation_unit.sub_components[parent_sub.sub_component_idx];
1198 let z_ref = parent_sc.repeated[*rep_idx].dynamic_z.as_ref()?;
1199 let ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1200 let z: f64 = crate::eval::load_property(&ctx, z_ref).try_into().unwrap_or(0.0);
1201 Some(z as f32)
1202 }
1203
1204 fn listview_layout(
1205 self: Pin<&Self>,
1206 offset_y: &mut i_slint_core::lengths::LogicalLength,
1207 ) -> i_slint_core::lengths::LogicalLength {
1208 use i_slint_core::item_tree::ItemTree as _;
1209 use i_slint_core::lengths::LogicalLength;
1210 let this = self.get_ref();
1214 let Some((parent_weak, rep_idx)) = this.root_sub_component.repeated_in.get() else {
1215 return LogicalLength::default();
1216 };
1217 let Some(parent_sub) = parent_weak.upgrade() else { return LogicalLength::default() };
1218 let parent_sub = Pin::new(parent_sub);
1219 let parent_cu = parent_sub.compilation_unit.clone();
1220 let parent_sc = &parent_cu.sub_components[parent_sub.sub_component_idx];
1221 let Some(lv) = parent_sc.repeated[*rep_idx].listview.as_ref() else {
1222 return LogicalLength::default();
1223 };
1224
1225 let row_sub = this.root_sub_component.clone();
1229 let ctx = crate::eval::EvalContext::new(row_sub.clone());
1230 crate::eval::store_property(&ctx, &lv.prop_y, crate::Value::Number(offset_y.get() as f64));
1231 let height_v = crate::eval::load_property(&ctx, &lv.prop_height);
1232 let height: f64 = height_v.try_into().unwrap_or(0.0);
1233 *offset_y += LogicalLength::new(height as f32);
1234 let info = self.layout_info(i_slint_core::items::Orientation::Horizontal);
1235 LogicalLength::new(info.min)
1236 }
1237
1238 fn layout_item_info(
1239 self: Pin<&Self>,
1240 orientation: i_slint_core::items::Orientation,
1241 child_index: Option<usize>,
1242 ) -> i_slint_core::layout::LayoutItemInfo {
1243 let this = self.get_ref();
1252 let cu = this.root_sub_component.compilation_unit.clone();
1253 let sc_idx = this.root_sub_component.sub_component_idx;
1254 let sc = &cu.sub_components[sc_idx];
1255
1256 if let (Some(index), true, Some(templates)) =
1257 (child_index, sc.is_repeated_row, sc.row_child_templates.as_ref())
1258 {
1259 return row_child_layout_item_info(this, sc, templates, orientation, index);
1260 }
1261
1262 let expr = match orientation {
1263 i_slint_core::items::Orientation::Horizontal => sc.layout_info_h.borrow(),
1264 i_slint_core::items::Orientation::Vertical => sc.layout_info_v.borrow(),
1265 };
1266 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1267 let constraint =
1268 crate::eval::eval_expression(&mut ctx, &expr).try_into().unwrap_or_default();
1269 i_slint_core::layout::LayoutItemInfo {
1270 constraint,
1271 cross_axis_self_alignment: repeated_align_self(sc, &mut ctx, orientation),
1272 layout_order: repeated_layout_order(sc, &mut ctx, orientation),
1273 }
1274 }
1275
1276 fn layout_item_info_at_cross_width(
1282 self: Pin<&Self>,
1283 cross_width: f32,
1284 ) -> i_slint_core::layout::LayoutItemInfo {
1285 use i_slint_compiler::llr::lower_layout_expression::CROSS_WIDTH_LOCAL;
1286 let orientation = i_slint_core::items::Orientation::Vertical;
1287 let cu = self.root_sub_component.compilation_unit.clone();
1288 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1289 let Some(expr) = sc
1290 .layout_info_v_at_cross_width_for_repeated
1291 .as_ref()
1292 .filter(|_| sc.flexbox_layout_item_info_for_repeated.is_none())
1293 else {
1294 return self.layout_item_info(orientation, None);
1295 };
1296 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1297 ctx.locals.insert(CROSS_WIDTH_LOCAL.into(), crate::Value::Number(cross_width as f64));
1298 let constraint =
1299 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().unwrap_or_default();
1300 i_slint_core::layout::LayoutItemInfo {
1304 constraint,
1305 cross_axis_self_alignment: repeated_align_self(sc, &mut ctx, orientation),
1306 layout_order: repeated_layout_order(sc, &mut ctx, orientation),
1307 }
1308 }
1309
1310 fn flexbox_layout_item_info(
1311 self: Pin<&Self>,
1312 orientation: i_slint_core::items::Orientation,
1313 child_index: Option<usize>,
1314 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1315 let cu = self.root_sub_component.compilation_unit.clone();
1319 let sc_idx = self.root_sub_component.sub_component_idx;
1320 let sc = &cu.sub_components[sc_idx];
1321 if let Some(expr) = &sc.flexbox_layout_item_info_for_repeated {
1322 let expr = expr.borrow();
1323 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1324 let value = crate::eval::eval_expression(&mut ctx, &expr);
1325 let mut info = value_to_flexbox_layout_item_info(value, orientation, self);
1326 if matches!(orientation, i_slint_core::items::Orientation::Vertical)
1332 && child_index.is_none()
1333 && let Some(v_expr) = &sc.layout_info_v_constrained_for_repeated
1334 {
1335 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1336 info.constraint = crate::eval::eval_expression(&mut ctx, &v_expr.borrow())
1337 .try_into()
1338 .unwrap_or_default();
1339 return info;
1340 }
1341 info.constraint = self.layout_item_info(orientation, child_index).constraint;
1344 return info;
1345 }
1346 let info = self.layout_item_info(orientation, None);
1347 info.into()
1348 }
1349}
1350
1351impl Instance {
1352 pub fn flexbox_layout_item_info_at_cross_width(
1356 self: Pin<&Self>,
1357 cross_width: f32,
1358 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1359 use i_slint_core::items::Orientation;
1360 use i_slint_core::model::RepeatedItemTree;
1361 let mut info =
1362 RepeatedItemTree::flexbox_layout_item_info(self, Orientation::Vertical, None);
1363 let cu = self.root_sub_component.compilation_unit.clone();
1364 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1365 if let Some(v_expr) = &sc.layout_info_v_at_cross_width_for_repeated {
1366 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1367 ctx.locals.insert(
1368 i_slint_compiler::llr::lower_layout_expression::CROSS_WIDTH_LOCAL.into(),
1369 crate::Value::Number(cross_width as f64),
1370 );
1371 info.constraint = crate::eval::eval_expression(&mut ctx, &v_expr.borrow())
1372 .try_into()
1373 .unwrap_or_default();
1374 }
1375 info
1376 }
1377}
1378
1379fn row_child_layout_item_info(
1383 this: &Instance,
1384 sc: &i_slint_compiler::llr::SubComponent,
1385 templates: &[i_slint_compiler::llr::RowChildTemplateInfo],
1386 orientation: i_slint_core::items::Orientation,
1387 mut index: usize,
1388) -> i_slint_core::layout::LayoutItemInfo {
1389 use i_slint_compiler::llr::RowChildTemplateInfo;
1390 use i_slint_core::model::RepeatedItemTree;
1391 let flat_index = index;
1394 for entry in templates {
1395 match entry {
1396 RowChildTemplateInfo::Static { child_index } => {
1397 if index == 0 {
1398 let child = &sc.grid_layout_children[*child_index];
1399 let expr = match orientation {
1400 i_slint_core::items::Orientation::Horizontal => {
1401 child.layout_info_h.borrow()
1402 }
1403 i_slint_core::items::Orientation::Vertical => child.layout_info_v.borrow(),
1404 };
1405 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1406 let constraint = crate::eval::eval_expression(&mut ctx, &expr)
1407 .try_into()
1408 .unwrap_or_default();
1409 return i_slint_core::layout::LayoutItemInfo {
1410 constraint,
1411 ..Default::default()
1412 };
1413 }
1414 index -= 1;
1415 }
1416 RowChildTemplateInfo::Repeated { repeater_index, measure_at_cross_width } => {
1417 let repeater = &this.root_sub_component.repeaters[*repeater_index];
1418 repeater.track_instance_changes();
1419 let count = repeater.range().len();
1420 if index < count {
1421 if let Some(inner) = repeater.instance_at(index) {
1422 if *measure_at_cross_width
1426 && orientation == i_slint_core::items::Orientation::Vertical
1427 && let Some(w) = row_child_cross_width(this, sc, flat_index)
1428 {
1429 return RepeatedItemTree::layout_item_info_at_cross_width(
1430 inner.as_pin_ref(),
1431 w,
1432 );
1433 }
1434 return RepeatedItemTree::layout_item_info(
1435 inner.as_pin_ref(),
1436 orientation,
1437 None,
1438 );
1439 }
1440 return i_slint_core::layout::LayoutItemInfo::default();
1441 }
1442 index -= count;
1443 }
1444 }
1445 }
1446 i_slint_core::layout::LayoutItemInfo::default()
1447}
1448
1449fn row_child_cross_width(
1454 this: &Instance,
1455 sc: &i_slint_compiler::llr::SubComponent,
1456 flat_index: usize,
1457) -> Option<f32> {
1458 use i_slint_compiler::llr::lower_layout_expression::GRID_MEASURE_CHILD_INDEX_LOCAL;
1459 let expr = sc.grid_row_child_cross_width.as_ref()?;
1460 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1461 ctx.locals
1462 .insert(GRID_MEASURE_CHILD_INDEX_LOCAL.into(), crate::Value::Number(flat_index as f64));
1463 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().ok()
1464}
1465
1466fn value_to_flexbox_layout_item_info(
1467 v: crate::Value,
1468 orientation: i_slint_core::items::Orientation,
1469 instance: Pin<&Instance>,
1470) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1471 use i_slint_core::model::RepeatedItemTree;
1472 let crate::Value::Struct(s) = v else {
1473 let info = RepeatedItemTree::layout_item_info(instance, orientation, None);
1474 return info.into();
1475 };
1476 crate::eval_layout::flexbox_item_info_from_struct(&s)
1477}