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 install_bindings_for_repeated_row(&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>) {
805 install_bindings_for_repeated_row(vrc);
806 if vrc.init_code_run.get().is_some() {
807 return;
808 }
809 let _ = vrc.init_code_run.set(());
810 if vrc.public_component_index.is_some() && vrc.embedded_in.get().is_none() {
818 vrc.attach_to_window();
819 }
820 {
827 let dyn_rc = vtable::VRc::into_dyn(vrc.self_weak.get().unwrap().upgrade().unwrap());
828 let adapter = vrc.window_adapter_or_default();
829 i_slint_core::item_tree::register_item_tree(&dyn_rc, adapter);
830 }
831 crate::bindings::run_init_code_for_instance(vrc);
832}
833
834pub(crate) fn install_bindings_for_repeated_row(vrc: &VRc<ItemTreeVTable, Instance>) {
839 if vrc.bindings_installed.get().is_some() {
840 return;
841 }
842 let _ = vrc.bindings_installed.set(());
843 let is_root = vrc.parent_instance.upgrade().is_none();
844 if is_root {
845 crate::globals::install_global_bindings(&vrc.globals);
846 }
847 crate::bindings::install_bindings_only(vrc);
848}
849
850fn propagate_root(sub: &Pin<Rc<SubComponentInstance>>, weak: &VWeak<ItemTreeVTable, Instance>) {
852 let _ = sub.root.set(weak.clone());
853 for nested in &sub.sub_components {
854 propagate_root(nested, weak);
855 }
856}
857
858fn build_sub_component_instance(
860 cu: &Rc<CompilationUnit>,
861 sub_idx: SubComponentIdx,
862 parent: Weak<SubComponentInstance>,
863) -> Pin<Rc<SubComponentInstance>> {
864 let sc = &cu.sub_components[sub_idx];
865 let registry = ItemRegistry::global();
866
867 let properties = sc
868 .properties
869 .iter()
870 .map(|p| Rc::pin(Property::new(crate::eval::default_value_for_type(&p.ty))))
871 .collect();
872 let callbacks = sc.callbacks.iter().map(|_| Rc::pin(Callback::default())).collect();
873 let callback_trackers =
874 sc.callbacks.iter().map(|c| c.needs_tracker.then(|| Rc::pin(Property::new(())))).collect();
875 let items =
876 sc.items
877 .iter()
878 .map(|item| {
879 registry.factory(&item.ty.class_name).unwrap_or_else(|| {
880 panic!("native item `{}` is not registered", item.ty.class_name)
881 })()
882 })
883 .collect();
884 let repeaters = sc
885 .repeated
886 .iter()
887 .map(|rep| {
888 if rep.data_prop.is_none() {
889 RepeaterOrConditional::Conditional(Box::pin(Conditional::default()))
890 } else {
891 RepeaterOrConditional::Repeater(Box::pin(Repeater::default()))
892 }
893 })
894 .collect();
895
896 let rc = Rc::new_cyclic(|weak_self: &Weak<SubComponentInstance>| {
900 let sub_components = sc
901 .sub_components
902 .iter()
903 .map(|nested| build_sub_component_instance(cu, nested.ty, weak_self.clone()))
904 .collect();
905 SubComponentInstance {
906 compilation_unit: cu.clone(),
907 sub_component_idx: sub_idx,
908 properties,
909 callbacks,
910 callback_trackers,
911 items,
912 sub_components,
913 repeaters,
914 parent,
915 root: OnceCell::new(),
916 change_trackers: std::iter::repeat_with(ChangeTracker::default)
917 .take(2 * sc.timers.len() + sc.change_callbacks.len())
918 .collect(),
919 timers: std::iter::repeat_with(Default::default).take(sc.timers.len()).collect(),
920 popup_ids: vec![std::cell::Cell::new(None); sc.popup_windows.len()],
921 repeated_in: OnceCell::new(),
922 menubar: RefCell::new(None),
923 }
924 });
925 Pin::new(rc)
926}
927
928fn read_logical_length(
932 sub: &Pin<Rc<SubComponentInstance>>,
933 mr: &llr::MemberReference,
934) -> i_slint_core::lengths::LogicalLength {
935 let mut ctx = crate::eval::EvalContext::new(sub.clone());
936 let v = crate::eval::load_property(&ctx, mr);
937 let _ = &mut ctx;
938 let n: f64 = v.try_into().unwrap_or(0.0);
939 i_slint_core::lengths::LogicalLength::new(n as f32)
940}
941
942struct ValueListViewProps {
948 content_y: llr::MemberReference,
949 content_width: Option<llr::MemberReference>,
952 content_height: Option<llr::MemberReference>,
953 ctx_sub: Pin<Rc<SubComponentInstance>>,
954}
955
956impl i_slint_core::model::ListViewProperties for ValueListViewProps {
957 fn content_y_get(&self) -> i_slint_core::lengths::LogicalLength {
958 read_logical_length(&self.ctx_sub, &self.content_y)
959 }
960 fn content_y_get_internal(&self) -> i_slint_core::lengths::LogicalLength {
961 read_logical_length(&self.ctx_sub, &self.content_y)
965 }
966 fn content_y_set(&self, value: i_slint_core::lengths::LogicalLength) {
967 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
968 crate::eval::store_property(
969 &ctx,
970 &self.content_y,
971 crate::Value::Number(value.get() as f64),
972 );
973 }
974 fn content_y_has_binding(&self) -> bool {
975 false
979 }
980 fn computes_content_height(&self) -> bool {
981 self.content_height.is_some()
982 }
983 fn content_width_set(&self, value: i_slint_core::lengths::LogicalLength) {
984 let Some(content_width) = &self.content_width else { return };
985 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
986 crate::eval::store_property(&ctx, content_width, crate::Value::Number(value.get() as f64));
987 }
988 fn content_height_set(&self, value: i_slint_core::lengths::LogicalLength) {
989 let Some(content_height) = &self.content_height else { return };
990 let ctx = crate::eval::EvalContext::new(self.ctx_sub.clone());
991 crate::eval::store_property(&ctx, content_height, crate::Value::Number(value.get() as f64));
992 }
993 fn register_as_dependencies(&self) {
994 if let Some(content_width) = &self.content_width {
997 let _ = read_logical_length(&self.ctx_sub, content_width);
998 }
999 if let Some(content_height) = &self.content_height {
1000 let _ = read_logical_length(&self.ctx_sub, content_height);
1001 }
1002 let _ = read_logical_length(&self.ctx_sub, &self.content_y);
1003 }
1004}
1005
1006type DynamicEntry = Option<(Box<[SubComponentInstanceIdx]>, RepeatedElementIdx)>;
1007type ItemEntry = Option<(Box<[SubComponentInstanceIdx]>, ItemInstanceIdx)>;
1008type ZSortEntry = Option<Vec<llr::ZSource>>;
1009
1010fn build_tree_nodes(
1020 root: &llr::TreeNode,
1021) -> (Vec<ItemTreeNode>, Vec<DynamicEntry>, Vec<ItemEntry>, Vec<ZSortEntry>) {
1022 use itertools::Either;
1023
1024 let mut out = Vec::new();
1025 let mut dyn_table: Vec<DynamicEntry> = Vec::new();
1026 let mut item_table: Vec<ItemEntry> = Vec::new();
1027 let mut z_sort_table: Vec<ZSortEntry> = Vec::new();
1028 root.visit_in_array(&mut |node, children_offset, parent_index| {
1029 let parent_index = parent_index as u32;
1030 let (entry, dyn_entry, item_entry) = match node.item_index {
1031 Either::Left(item_idx) => (
1032 ItemTreeNode::Item {
1033 is_accessible: node.is_accessible,
1034 children_count: node.children.len() as u32,
1035 children_index: children_offset as u32,
1036 parent_index,
1037 item_array_index: out.len() as u32,
1040 },
1041 None,
1042 Some((node.sub_component_path.clone().into_boxed_slice(), item_idx)),
1043 ),
1044 Either::Right(dynamic_index) => (
1045 ItemTreeNode::DynamicTree { index: out.len() as u32, parent_index },
1053 Some((
1054 node.sub_component_path.clone().into_boxed_slice(),
1055 (dynamic_index as usize).into(),
1056 )),
1057 None,
1058 ),
1059 };
1060 out.push(entry);
1061 dyn_table.push(dyn_entry);
1062 item_table.push(item_entry);
1063 z_sort_table.push(node.z_sort_order_property.clone());
1064 });
1065 (out, dyn_table, item_table, z_sort_table)
1066}
1067
1068fn repeated_align_self(
1071 sc: &i_slint_compiler::llr::SubComponent,
1072 ctx: &mut crate::eval::EvalContext,
1073 orientation: i_slint_core::items::Orientation,
1074) -> i_slint_core::items::CrossAxisAlignment {
1075 match &sc.cross_axis_self_alignment_for_repeated {
1076 Some((cross_o, expr)) if crate::eval::llr_to_core_orientation(*cross_o) == orientation => {
1077 crate::eval::eval_expression(ctx, &expr.borrow()).try_into().unwrap_or_default()
1078 }
1079 _ => Default::default(),
1080 }
1081}
1082
1083fn repeated_layout_order(
1086 sc: &i_slint_compiler::llr::SubComponent,
1087 ctx: &mut crate::eval::EvalContext,
1088 orientation: i_slint_core::items::Orientation,
1089) -> i32 {
1090 match &sc.layout_order_for_repeated {
1091 Some((main_o, expr)) if crate::eval::llr_to_core_orientation(*main_o) == orientation => {
1092 match crate::eval::eval_expression(ctx, &expr.borrow()) {
1093 crate::Value::Number(n) => n as i32,
1094 _ => 0,
1095 }
1096 }
1097 _ => 0,
1098 }
1099}
1100
1101impl i_slint_core::model::RepeatedItemTree for Instance {
1106 type Data = crate::Value;
1107
1108 fn update(&self, index: usize, data: Self::Data) {
1109 let sc_idx = self.root_sub_component.sub_component_idx;
1110 let cu = self.root_sub_component.compilation_unit.clone();
1111 let sc = &cu.sub_components[sc_idx];
1112 for (idx, prop) in sc.properties.iter_enumerated() {
1117 let target = &self.root_sub_component.properties[idx];
1118 match prop.name.as_str() {
1119 "model_data" => Pin::as_ref(target).set(data.clone()),
1120 "model_index" => Pin::as_ref(target).set(crate::Value::Number(index as f64)),
1121 _ => {}
1122 }
1123 }
1124 }
1125
1126 fn init(&self) {
1127 if let Some(weak) = self.self_weak.get()
1133 && let Some(vrc) = weak.upgrade()
1134 {
1135 finalize_instance(&vrc);
1136 }
1137 }
1138
1139 fn z_order(self: Pin<&Self>) -> Option<f32> {
1140 let this = self.get_ref();
1143 let (parent_weak, rep_idx) = this.root_sub_component.repeated_in.get()?;
1144 let parent_sub = parent_weak.upgrade()?;
1145 let parent_sc = &parent_sub.compilation_unit.sub_components[parent_sub.sub_component_idx];
1146 let z_ref = parent_sc.repeated[*rep_idx].dynamic_z.as_ref()?;
1147 let ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1148 let z: f64 = crate::eval::load_property(&ctx, z_ref).try_into().unwrap_or(0.0);
1149 Some(z as f32)
1150 }
1151
1152 fn listview_layout(
1153 self: Pin<&Self>,
1154 offset_y: &mut i_slint_core::lengths::LogicalLength,
1155 ) -> i_slint_core::lengths::LogicalLength {
1156 use i_slint_core::item_tree::ItemTree as _;
1157 use i_slint_core::lengths::LogicalLength;
1158 let this = self.get_ref();
1162 let Some((parent_weak, rep_idx)) = this.root_sub_component.repeated_in.get() else {
1163 return LogicalLength::default();
1164 };
1165 let Some(parent_sub) = parent_weak.upgrade() else { return LogicalLength::default() };
1166 let parent_sub = Pin::new(parent_sub);
1167 let parent_cu = parent_sub.compilation_unit.clone();
1168 let parent_sc = &parent_cu.sub_components[parent_sub.sub_component_idx];
1169 let Some(lv) = parent_sc.repeated[*rep_idx].listview.as_ref() else {
1170 return LogicalLength::default();
1171 };
1172
1173 let row_sub = this.root_sub_component.clone();
1177 let ctx = crate::eval::EvalContext::new(row_sub.clone());
1178 crate::eval::store_property(&ctx, &lv.prop_y, crate::Value::Number(offset_y.get() as f64));
1179 let height_v = crate::eval::load_property(&ctx, &lv.prop_height);
1180 let height: f64 = height_v.try_into().unwrap_or(0.0);
1181 *offset_y += LogicalLength::new(height as f32);
1182 let info = self.layout_info(i_slint_core::items::Orientation::Horizontal);
1183 LogicalLength::new(info.min)
1184 }
1185
1186 fn layout_item_info(
1187 self: Pin<&Self>,
1188 orientation: i_slint_core::items::Orientation,
1189 child_index: Option<usize>,
1190 ) -> i_slint_core::layout::LayoutItemInfo {
1191 let this = self.get_ref();
1200 let cu = this.root_sub_component.compilation_unit.clone();
1201 let sc_idx = this.root_sub_component.sub_component_idx;
1202 let sc = &cu.sub_components[sc_idx];
1203
1204 if let (Some(index), true, Some(templates)) =
1205 (child_index, sc.is_repeated_row, sc.row_child_templates.as_ref())
1206 {
1207 return row_child_layout_item_info(this, sc, templates, orientation, index);
1208 }
1209
1210 let expr = match orientation {
1211 i_slint_core::items::Orientation::Horizontal => sc.layout_info_h.borrow(),
1212 i_slint_core::items::Orientation::Vertical => sc.layout_info_v.borrow(),
1213 };
1214 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1215 let constraint =
1216 crate::eval::eval_expression(&mut ctx, &expr).try_into().unwrap_or_default();
1217 i_slint_core::layout::LayoutItemInfo {
1218 constraint,
1219 cross_axis_self_alignment: repeated_align_self(sc, &mut ctx, orientation),
1220 layout_order: repeated_layout_order(sc, &mut ctx, orientation),
1221 }
1222 }
1223
1224 fn layout_item_info_at_cross_width(
1225 self: Pin<&Self>,
1226 cross_width: f32,
1227 ) -> i_slint_core::layout::LayoutItemInfo {
1228 self.box_layout_item_info_at_cross(i_slint_core::items::Orientation::Vertical, cross_width)
1229 }
1230
1231 fn layout_item_info_at_cross_height(
1232 self: Pin<&Self>,
1233 cross_height: f32,
1234 ) -> i_slint_core::layout::LayoutItemInfo {
1235 self.box_layout_item_info_at_cross(
1236 i_slint_core::items::Orientation::Horizontal,
1237 cross_height,
1238 )
1239 }
1240
1241 fn flexbox_layout_item_info(
1242 self: Pin<&Self>,
1243 orientation: i_slint_core::items::Orientation,
1244 child_index: Option<usize>,
1245 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1246 let cu = self.root_sub_component.compilation_unit.clone();
1250 let sc_idx = self.root_sub_component.sub_component_idx;
1251 let sc = &cu.sub_components[sc_idx];
1252 if let Some(expr) = &sc.flexbox_layout_item_info_for_repeated {
1253 let expr = expr.borrow();
1254 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1255 let value = crate::eval::eval_expression(&mut ctx, &expr);
1256 let mut info = value_to_flexbox_layout_item_info(value, orientation, self);
1257 if matches!(orientation, i_slint_core::items::Orientation::Vertical)
1263 && child_index.is_none()
1264 && let Some(v_expr) = &sc.layout_info_v_constrained_for_repeated
1265 {
1266 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1267 info.constraint = crate::eval::eval_expression(&mut ctx, &v_expr.borrow())
1268 .try_into()
1269 .unwrap_or_default();
1270 return info;
1271 }
1272 if matches!(orientation, i_slint_core::items::Orientation::Horizontal)
1276 && child_index.is_none()
1277 && let Some(h_expr) = &sc.layout_info_h_constrained_for_repeated
1278 {
1279 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1280 info.constraint = crate::eval::eval_expression(&mut ctx, &h_expr.borrow())
1281 .try_into()
1282 .unwrap_or_default();
1283 return info;
1284 }
1285 info.constraint = self.layout_item_info(orientation, child_index).constraint;
1288 return info;
1289 }
1290 let info = self.layout_item_info(orientation, None);
1291 info.into()
1292 }
1293}
1294
1295impl Instance {
1296 fn box_layout_item_info_at_cross(
1305 self: Pin<&Self>,
1306 orientation: i_slint_core::items::Orientation,
1307 cross_size: f32,
1308 ) -> i_slint_core::layout::LayoutItemInfo {
1309 use i_slint_compiler::llr::lower_layout_expression::{
1310 CROSS_HEIGHT_LOCAL, CROSS_WIDTH_LOCAL,
1311 };
1312 let cu = self.root_sub_component.compilation_unit.clone();
1313 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1314 let (expr, local) = match orientation {
1315 i_slint_core::items::Orientation::Vertical => {
1316 (sc.layout_info_v_at_cross_width_for_repeated.as_ref(), CROSS_WIDTH_LOCAL)
1317 }
1318 i_slint_core::items::Orientation::Horizontal => {
1319 (sc.layout_info_h_at_cross_height_for_repeated.as_ref(), CROSS_HEIGHT_LOCAL)
1320 }
1321 };
1322 let Some(expr) = expr.filter(|_| sc.flexbox_layout_item_info_for_repeated.is_none()) else {
1323 return i_slint_core::model::RepeatedItemTree::layout_item_info(
1324 self,
1325 orientation,
1326 None,
1327 );
1328 };
1329 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1330 ctx.locals.insert(local.into(), crate::Value::Number(cross_size as f64));
1331 let constraint =
1332 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().unwrap_or_default();
1333 i_slint_core::layout::LayoutItemInfo {
1337 constraint,
1338 cross_axis_self_alignment: repeated_align_self(sc, &mut ctx, orientation),
1339 layout_order: repeated_layout_order(sc, &mut ctx, orientation),
1340 }
1341 }
1342
1343 pub fn flexbox_layout_item_info_at_cross_width(
1347 self: Pin<&Self>,
1348 cross_width: f32,
1349 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1350 use i_slint_core::items::Orientation;
1351 use i_slint_core::model::RepeatedItemTree;
1352 let mut info =
1353 RepeatedItemTree::flexbox_layout_item_info(self, Orientation::Vertical, None);
1354 let cu = self.root_sub_component.compilation_unit.clone();
1355 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1356 if let Some(v_expr) = &sc.layout_info_v_at_cross_width_for_repeated {
1357 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1358 ctx.locals.insert(
1359 i_slint_compiler::llr::lower_layout_expression::CROSS_WIDTH_LOCAL.into(),
1360 crate::Value::Number(cross_width as f64),
1361 );
1362 info.constraint = crate::eval::eval_expression(&mut ctx, &v_expr.borrow())
1363 .try_into()
1364 .unwrap_or_default();
1365 }
1366 info
1367 }
1368
1369 pub fn flexbox_layout_item_info_at_cross_height(
1373 self: Pin<&Self>,
1374 cross_height: f32,
1375 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1376 use i_slint_core::items::Orientation;
1377 use i_slint_core::model::RepeatedItemTree;
1378 let mut info =
1379 RepeatedItemTree::flexbox_layout_item_info(self, Orientation::Horizontal, None);
1380 let cu = self.root_sub_component.compilation_unit.clone();
1381 let sc = &cu.sub_components[self.root_sub_component.sub_component_idx];
1382 if let Some(h_expr) = &sc.layout_info_h_at_cross_height_for_repeated {
1383 let mut ctx = crate::eval::EvalContext::new(self.root_sub_component.clone());
1384 ctx.locals.insert(
1385 i_slint_compiler::llr::lower_layout_expression::CROSS_HEIGHT_LOCAL.into(),
1386 crate::Value::Number(cross_height as f64),
1387 );
1388 info.constraint = crate::eval::eval_expression(&mut ctx, &h_expr.borrow())
1389 .try_into()
1390 .unwrap_or_default();
1391 }
1392 info
1393 }
1394}
1395
1396fn row_child_layout_item_info(
1400 this: &Instance,
1401 sc: &i_slint_compiler::llr::SubComponent,
1402 templates: &[i_slint_compiler::llr::RowChildTemplateInfo],
1403 orientation: i_slint_core::items::Orientation,
1404 mut index: usize,
1405) -> i_slint_core::layout::LayoutItemInfo {
1406 use i_slint_compiler::llr::RowChildTemplateInfo;
1407 use i_slint_core::model::RepeatedItemTree;
1408 let flat_index = index;
1411 for entry in templates {
1412 match entry {
1413 RowChildTemplateInfo::Static { child_index } => {
1414 if index == 0 {
1415 let child = &sc.grid_layout_children[*child_index];
1416 let expr = match orientation {
1417 i_slint_core::items::Orientation::Horizontal => {
1418 child.layout_info_h.borrow()
1419 }
1420 i_slint_core::items::Orientation::Vertical => child.layout_info_v.borrow(),
1421 };
1422 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1423 let constraint = crate::eval::eval_expression(&mut ctx, &expr)
1424 .try_into()
1425 .unwrap_or_default();
1426 return i_slint_core::layout::LayoutItemInfo {
1427 constraint,
1428 ..Default::default()
1429 };
1430 }
1431 index -= 1;
1432 }
1433 RowChildTemplateInfo::Repeated { repeater_index, measure_at_cross_width } => {
1434 let repeater = &this.root_sub_component.repeaters[*repeater_index];
1435 repeater.track_instance_changes();
1436 let count = repeater.range().len();
1437 if index < count {
1438 if let Some(inner) = repeater.instance_at(index) {
1439 if *measure_at_cross_width
1443 && orientation == i_slint_core::items::Orientation::Vertical
1444 && let Some(w) = row_child_cross_width(this, sc, flat_index)
1445 {
1446 return RepeatedItemTree::layout_item_info_at_cross_width(
1447 inner.as_pin_ref(),
1448 w,
1449 );
1450 }
1451 return RepeatedItemTree::layout_item_info(
1452 inner.as_pin_ref(),
1453 orientation,
1454 None,
1455 );
1456 }
1457 return i_slint_core::layout::LayoutItemInfo::default();
1458 }
1459 index -= count;
1460 }
1461 }
1462 }
1463 i_slint_core::layout::LayoutItemInfo::default()
1464}
1465
1466fn row_child_cross_width(
1471 this: &Instance,
1472 sc: &i_slint_compiler::llr::SubComponent,
1473 flat_index: usize,
1474) -> Option<f32> {
1475 use i_slint_compiler::llr::lower_layout_expression::GRID_MEASURE_CHILD_INDEX_LOCAL;
1476 let expr = sc.grid_row_child_cross_width.as_ref()?;
1477 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
1478 ctx.locals
1479 .insert(GRID_MEASURE_CHILD_INDEX_LOCAL.into(), crate::Value::Number(flat_index as f64));
1480 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().ok()
1481}
1482
1483fn value_to_flexbox_layout_item_info(
1484 v: crate::Value,
1485 orientation: i_slint_core::items::Orientation,
1486 instance: Pin<&Instance>,
1487) -> i_slint_core::layout::FlexboxLayoutItemInfo {
1488 use i_slint_core::model::RepeatedItemTree;
1489 let crate::Value::Struct(s) = v else {
1490 let info = RepeatedItemTree::layout_item_info(instance, orientation, None);
1491 return info.into();
1492 };
1493 crate::eval_layout::flexbox_item_info_from_struct(&s)
1494}