1use crate::api::{CompilationResult, ComponentDefinition, Value};
5use crate::global_component::CompiledGlobalCollection;
6use crate::{dynamic_type, eval};
7use core::ffi::c_void;
8use core::ptr::NonNull;
9use dynamic_type::{Instance, InstanceBox};
10use i_slint_compiler::expression_tree::{Expression, NamedReference, TwoWayBinding};
11use i_slint_compiler::langtype::{BuiltinStruct, StructName, Type};
12use i_slint_compiler::object_tree::{ElementRc, ElementWeak, TransitionDirection};
13use i_slint_compiler::{CompilerConfiguration, generator, object_tree, parser};
14use i_slint_compiler::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration};
15use i_slint_core::accessibility::{
16 AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction,
17};
18use i_slint_core::api::LogicalPosition;
19use i_slint_core::component_factory::ComponentFactory;
20use i_slint_core::input::Keys;
21use i_slint_core::item_tree::{
22 IndexRange, ItemRc, ItemTree, ItemTreeNode, ItemTreeRef, ItemTreeRefPin, ItemTreeVTable,
23 ItemTreeWeak, ItemVisitorRefMut, ItemVisitorVTable, ItemWeak, TraversalOrder,
24 VisitChildrenResult,
25};
26use i_slint_core::items::{
27 AccessibleRole, ItemRef, ItemVTable, PopupClosePolicy, PropertyAnimation,
28};
29use i_slint_core::layout::{LayoutInfo, LayoutItemInfo, Orientation};
30use i_slint_core::lengths::{LogicalLength, LogicalRect};
31use i_slint_core::menus::MenuFromItemTree;
32use i_slint_core::model::{ModelRc, RepeatedItemTree, Repeater};
33use i_slint_core::platform::PlatformError;
34use i_slint_core::properties::{ChangeTracker, InterpolatedPropertyValue};
35use i_slint_core::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo};
36use i_slint_core::slice::Slice;
37use i_slint_core::styled_text::StyledText;
38use i_slint_core::timers::Timer;
39use i_slint_core::window::{WindowAdapterRc, WindowInner, WindowKind};
40use i_slint_core::{Brush, Color, DataTransfer, Property, SharedString, SharedVector};
41#[cfg(feature = "internal")]
42use itertools::Either;
43use once_cell::unsync::{Lazy, OnceCell};
44use smol_str::{SmolStr, ToSmolStr};
45use std::collections::BTreeMap;
46use std::collections::HashMap;
47use std::num::NonZeroU32;
48use std::rc::Weak;
49use std::{pin::Pin, rc::Rc};
50
51pub const SPECIAL_PROPERTY_INDEX: &str = "$index";
52pub const SPECIAL_PROPERTY_MODEL_DATA: &str = "$model_data";
53
54pub(crate) type CallbackHandler = Box<dyn Fn(&[Value]) -> Value>;
55
56pub struct ItemTreeBox<'id> {
57 instance: InstanceBox<'id>,
58 description: Rc<ItemTreeDescription<'id>>,
59}
60
61impl<'id> ItemTreeBox<'id> {
62 pub fn borrow(&self) -> ItemTreeRefPin<'_> {
64 self.borrow_instance().borrow()
65 }
66
67 pub fn description(&self) -> Rc<ItemTreeDescription<'id>> {
69 self.description.clone()
70 }
71
72 pub fn borrow_instance<'a>(&'a self) -> InstanceRef<'a, 'id> {
73 InstanceRef { instance: self.instance.as_pin_ref(), description: &self.description }
74 }
75
76 pub fn window_adapter_ref(&self) -> Result<&WindowAdapterRc, PlatformError> {
77 let root_weak = vtable::VWeak::into_dyn(self.borrow_instance().root_weak().clone());
78 InstanceRef::get_or_init_window_adapter_ref(
79 &self.description,
80 root_weak,
81 true,
82 self.instance.as_pin_ref().get_ref(),
83 )
84 }
85}
86
87pub(crate) type ErasedItemTreeBoxWeak = vtable::VWeak<ItemTreeVTable, ErasedItemTreeBox>;
88
89pub(crate) struct ItemWithinItemTree {
90 offset: usize,
91 pub(crate) rtti: Rc<ItemRTTI>,
92 elem: ElementRc,
93}
94
95impl ItemWithinItemTree {
96 pub(crate) unsafe fn item_from_item_tree(
98 &self,
99 mem: *const u8,
100 ) -> Pin<vtable::VRef<'_, ItemVTable>> {
101 unsafe {
102 Pin::new_unchecked(vtable::VRef::from_raw(
103 NonNull::from(self.rtti.vtable),
104 NonNull::new(mem.add(self.offset) as _).unwrap(),
105 ))
106 }
107 }
108
109 pub(crate) fn item_index(&self) -> u32 {
110 *self.elem.borrow().item_index.get().unwrap()
111 }
112}
113
114pub(crate) struct PropertiesWithinComponent {
115 pub(crate) offset: usize,
116 pub(crate) prop: Box<dyn PropertyInfo<u8, Value>>,
117}
118
119pub(crate) struct RepeaterWithinItemTree<'par_id, 'sub_id> {
120 pub(crate) item_tree_to_repeat: Rc<ItemTreeDescription<'sub_id>>,
122 pub(crate) model: Expression,
124 offset: FieldOffset<Instance<'par_id>, Repeater<ErasedItemTreeBox>>,
126 is_conditional: bool,
129}
130
131impl RepeatedItemTree for ErasedItemTreeBox {
132 type Data = Value;
133
134 fn update(&self, index: usize, data: Self::Data) {
135 generativity::make_guard!(guard);
136 let s = self.unerase(guard);
137 let is_repeated = s.description.original.parent_element().is_some_and(|p| {
138 p.borrow().repeated.as_ref().is_some_and(|r| !r.is_conditional_element)
139 });
140 if is_repeated {
141 s.description.set_property(s.borrow(), SPECIAL_PROPERTY_INDEX, index.into()).unwrap();
142 s.description.set_property(s.borrow(), SPECIAL_PROPERTY_MODEL_DATA, data).unwrap();
143 }
144 }
145
146 fn init(&self) {
147 self.run_setup_code();
148 }
149
150 fn listview_layout(self: Pin<&Self>, offset_y: &mut LogicalLength) -> LogicalLength {
151 generativity::make_guard!(guard);
152 let s = self.unerase(guard);
153
154 let geom = s.description.original.root_element.borrow().geometry_props.clone().unwrap();
155
156 crate::eval::store_property(
157 s.borrow_instance(),
158 &geom.y.element(),
159 geom.y.name(),
160 Value::Number(offset_y.get() as f64),
161 )
162 .expect("cannot set y");
163
164 let h: LogicalLength = crate::eval::load_property(
165 s.borrow_instance(),
166 &geom.height.element(),
167 geom.height.name(),
168 )
169 .expect("missing height")
170 .try_into()
171 .expect("height not the right type");
172
173 *offset_y += h;
174 LogicalLength::new(self.borrow().as_ref().layout_info(Orientation::Horizontal).min)
175 }
176
177 fn layout_item_info(
178 self: Pin<&Self>,
179 o: Orientation,
180 child_index: Option<usize>,
181 ) -> LayoutItemInfo {
182 generativity::make_guard!(guard);
183 let s = self.unerase(guard);
184
185 if let Some(index) = child_index {
186 let instance_ref = s.borrow_instance();
187 let root_element = &s.description.original.root_element;
188
189 let children = root_element.borrow().children.clone();
190 if let Some(child_elem) = children.get(index) {
191 let layout_info = crate::eval_layout::get_layout_info(
193 child_elem,
194 instance_ref,
195 &instance_ref.window_adapter(),
196 crate::eval_layout::from_runtime(o),
197 );
198 return LayoutItemInfo { constraint: layout_info };
199 } else {
200 panic!(
201 "child_index {} out of bounds for repeated item {}",
202 index,
203 s.description().id()
204 );
205 }
206 }
207
208 LayoutItemInfo { constraint: self.borrow().as_ref().layout_info(o) }
209 }
210
211 fn flexbox_layout_item_info(
212 self: Pin<&Self>,
213 o: Orientation,
214 child_index: Option<usize>,
215 ) -> i_slint_core::layout::FlexboxLayoutItemInfo {
216 generativity::make_guard!(guard);
217 let s = self.unerase(guard);
218 let instance_ref = s.borrow_instance();
219 let root_element = &s.description.original.root_element;
220
221 let load_f32 = |name: &str| -> f32 {
222 eval::load_property(instance_ref, root_element, name)
223 .ok()
224 .and_then(|v| v.try_into().ok())
225 .unwrap_or(0.0)
226 };
227
228 let flex_grow = load_f32("flex-grow");
229 let flex_shrink = load_f32("flex-shrink");
230 let flex_basis = if root_element.borrow().bindings.contains_key("flex-basis") {
231 load_f32("flex-basis")
232 } else {
233 -1.0
234 };
235 let flex_align_self = eval::load_property(instance_ref, root_element, "flex-align-self")
236 .ok()
237 .and_then(|v| v.try_into().ok())
238 .unwrap_or(i_slint_core::items::FlexboxLayoutAlignSelf::Auto);
239 let flex_order = load_f32("flex-order") as i32;
240
241 i_slint_core::layout::FlexboxLayoutItemInfo {
242 constraint: self.layout_item_info(o, child_index).constraint,
243 flex_grow,
244 flex_shrink,
245 flex_basis,
246 flex_align_self,
247 flex_order,
248 }
249 }
250}
251
252impl ItemTree for ErasedItemTreeBox {
253 fn visit_children_item(
254 self: Pin<&Self>,
255 index: isize,
256 order: TraversalOrder,
257 visitor: ItemVisitorRefMut,
258 ) -> VisitChildrenResult {
259 self.borrow().as_ref().visit_children_item(index, order, visitor)
260 }
261
262 fn layout_info(self: Pin<&Self>, orientation: Orientation) -> i_slint_core::layout::LayoutInfo {
263 self.borrow().as_ref().layout_info(orientation)
264 }
265
266 fn ensure_instantiated(self: Pin<&Self>) -> bool {
267 self.borrow().as_ref().ensure_instantiated()
268 }
269
270 fn get_item_tree(self: Pin<&Self>) -> Slice<'_, ItemTreeNode> {
271 get_item_tree(self.get_ref().borrow())
272 }
273
274 fn get_item_ref(self: Pin<&Self>, index: u32) -> Pin<ItemRef<'_>> {
275 unsafe { get_item_ref(self.get_ref().borrow(), index) }
279 }
280
281 fn get_subtree_range(self: Pin<&Self>, index: u32) -> IndexRange {
282 self.borrow().as_ref().get_subtree_range(index)
283 }
284
285 fn get_subtree(self: Pin<&Self>, index: u32, subindex: usize, result: &mut ItemTreeWeak) {
286 self.borrow().as_ref().get_subtree(index, subindex, result);
287 }
288
289 fn parent_node(self: Pin<&Self>, result: &mut ItemWeak) {
290 self.borrow().as_ref().parent_node(result)
291 }
292
293 fn embed_component(
294 self: core::pin::Pin<&Self>,
295 parent_component: &ItemTreeWeak,
296 item_tree_index: u32,
297 ) -> bool {
298 self.borrow().as_ref().embed_component(parent_component, item_tree_index)
299 }
300
301 fn subtree_index(self: Pin<&Self>) -> usize {
302 self.borrow().as_ref().subtree_index()
303 }
304
305 fn item_geometry(self: Pin<&Self>, item_index: u32) -> i_slint_core::lengths::LogicalRect {
306 self.borrow().as_ref().item_geometry(item_index)
307 }
308
309 fn accessible_role(self: Pin<&Self>, index: u32) -> AccessibleRole {
310 self.borrow().as_ref().accessible_role(index)
311 }
312
313 fn accessible_string_property(
314 self: Pin<&Self>,
315 index: u32,
316 what: AccessibleStringProperty,
317 result: &mut SharedString,
318 ) -> bool {
319 self.borrow().as_ref().accessible_string_property(index, what, result)
320 }
321
322 fn window_adapter(self: Pin<&Self>, do_create: bool, result: &mut Option<WindowAdapterRc>) {
323 self.borrow().as_ref().window_adapter(do_create, result);
324 }
325
326 fn accessibility_action(self: core::pin::Pin<&Self>, index: u32, action: &AccessibilityAction) {
327 self.borrow().as_ref().accessibility_action(index, action)
328 }
329
330 fn supported_accessibility_actions(
331 self: core::pin::Pin<&Self>,
332 index: u32,
333 ) -> SupportedAccessibilityAction {
334 self.borrow().as_ref().supported_accessibility_actions(index)
335 }
336
337 fn item_element_infos(
338 self: core::pin::Pin<&Self>,
339 index: u32,
340 result: &mut SharedString,
341 ) -> bool {
342 self.borrow().as_ref().item_element_infos(index, result)
343 }
344}
345
346i_slint_core::ItemTreeVTable_static!(static COMPONENT_BOX_VT for ErasedItemTreeBox);
347
348impl Drop for ErasedItemTreeBox {
349 fn drop(&mut self) {
350 generativity::make_guard!(guard);
351 let unerase = self.unerase(guard);
352 let instance_ref = unerase.borrow_instance();
353
354 let maybe_window_adapter = instance_ref
355 .description
356 .extra_data_offset
357 .apply(instance_ref.as_ref())
358 .globals
359 .get()
360 .and_then(|globals| globals.window_adapter())
361 .and_then(|wa| wa.get());
362 if let Some(window_adapter) = maybe_window_adapter {
363 i_slint_core::item_tree::unregister_item_tree(
364 instance_ref.instance,
365 vtable::VRef::new(self),
366 instance_ref.description.item_array.as_slice(),
367 window_adapter,
368 );
369 }
370 }
371}
372
373pub type DynamicComponentVRc = vtable::VRc<ItemTreeVTable, ErasedItemTreeBox>;
374
375#[derive(Default)]
376pub(crate) struct ComponentExtraData {
377 pub(crate) globals: OnceCell<crate::global_component::GlobalStorage>,
378 pub(crate) self_weak: OnceCell<ErasedItemTreeBoxWeak>,
379 pub(crate) embedding_position: OnceCell<(ItemTreeWeak, u32)>,
380}
381
382struct ErasedRepeaterWithinComponent<'id>(RepeaterWithinItemTree<'id, 'static>);
383impl<'id, 'sub_id> From<RepeaterWithinItemTree<'id, 'sub_id>>
384 for ErasedRepeaterWithinComponent<'id>
385{
386 fn from(from: RepeaterWithinItemTree<'id, 'sub_id>) -> Self {
387 Self(unsafe {
390 core::mem::transmute::<
391 RepeaterWithinItemTree<'id, 'sub_id>,
392 RepeaterWithinItemTree<'id, 'static>,
393 >(from)
394 })
395 }
396}
397impl<'id> ErasedRepeaterWithinComponent<'id> {
398 pub fn unerase<'a, 'sub_id>(
399 &'a self,
400 _guard: generativity::Guard<'sub_id>,
401 ) -> &'a RepeaterWithinItemTree<'id, 'sub_id> {
402 unsafe {
404 core::mem::transmute::<
405 &'a RepeaterWithinItemTree<'id, 'static>,
406 &'a RepeaterWithinItemTree<'id, 'sub_id>,
407 >(&self.0)
408 }
409 }
410
411 unsafe fn get_untagged(&self) -> &RepeaterWithinItemTree<'id, 'static> {
415 &self.0
416 }
417}
418
419type Callback = i_slint_core::Callback<[Value], Value>;
420
421#[derive(Clone)]
422pub struct ErasedItemTreeDescription(Rc<ItemTreeDescription<'static>>);
423impl ErasedItemTreeDescription {
424 pub fn unerase<'a, 'id>(
425 &'a self,
426 _guard: generativity::Guard<'id>,
427 ) -> &'a Rc<ItemTreeDescription<'id>> {
428 unsafe {
430 core::mem::transmute::<
431 &'a Rc<ItemTreeDescription<'static>>,
432 &'a Rc<ItemTreeDescription<'id>>,
433 >(&self.0)
434 }
435 }
436}
437impl<'id> From<Rc<ItemTreeDescription<'id>>> for ErasedItemTreeDescription {
438 fn from(from: Rc<ItemTreeDescription<'id>>) -> Self {
439 Self(unsafe {
441 core::mem::transmute::<Rc<ItemTreeDescription<'id>>, Rc<ItemTreeDescription<'static>>>(
442 from,
443 )
444 })
445 }
446}
447
448#[repr(C)]
455pub struct ItemTreeDescription<'id> {
456 pub(crate) ct: ItemTreeVTable,
457 dynamic_type: Rc<dynamic_type::TypeInfo<'id>>,
459 item_tree: Vec<ItemTreeNode>,
460 item_array:
461 Vec<vtable::VOffset<crate::dynamic_type::Instance<'id>, ItemVTable, vtable::AllowPin>>,
462 pub(crate) items: HashMap<SmolStr, ItemWithinItemTree>,
463 pub(crate) custom_properties: HashMap<SmolStr, PropertiesWithinComponent>,
464 pub(crate) custom_callbacks: HashMap<SmolStr, FieldOffset<Instance<'id>, Callback>>,
465 pub(crate) callback_trackers: HashMap<SmolStr, FieldOffset<Instance<'id>, Property<()>>>,
469 repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
470 pub repeater_names: HashMap<SmolStr, usize>,
472 pub(crate) parent_item_tree_offset:
474 Option<FieldOffset<Instance<'id>, OnceCell<ErasedItemTreeBoxWeak>>>,
475 pub(crate) root_offset: FieldOffset<Instance<'id>, OnceCell<ErasedItemTreeBoxWeak>>,
476 pub(crate) extra_data_offset: FieldOffset<Instance<'id>, ComponentExtraData>,
478 pub(crate) original: Rc<object_tree::Component>,
480 pub(crate) original_elements: Vec<ElementRc>,
482 public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
484 change_trackers: Option<(
485 FieldOffset<Instance<'id>, OnceCell<Vec<ChangeTracker>>>,
486 Vec<(NamedReference, Expression)>,
487 )>,
488 timers: Vec<FieldOffset<Instance<'id>, Timer>>,
489 popup_ids: std::cell::RefCell<HashMap<SmolStr, NonZeroU32>>,
491
492 pub(crate) popup_menu_description: PopupMenuDescription,
493
494 compiled_globals: Option<Rc<CompiledGlobalCollection>>,
496
497 #[cfg(feature = "internal-highlight")]
500 pub(crate) type_loader:
501 std::cell::OnceCell<std::rc::Rc<i_slint_compiler::typeloader::TypeLoader>>,
502 #[cfg(feature = "internal-highlight")]
505 pub(crate) raw_type_loader:
506 std::cell::OnceCell<Option<std::rc::Rc<i_slint_compiler::typeloader::TypeLoader>>>,
507}
508
509#[derive(Clone, derive_more::From)]
510pub(crate) enum PopupMenuDescription {
511 Rc(Rc<ErasedItemTreeDescription>),
512 Weak(Weak<ErasedItemTreeDescription>),
513}
514impl PopupMenuDescription {
515 pub fn unerase<'id>(&self, guard: generativity::Guard<'id>) -> Rc<ItemTreeDescription<'id>> {
516 match self {
517 PopupMenuDescription::Rc(rc) => rc.unerase(guard).clone(),
518 PopupMenuDescription::Weak(weak) => weak.upgrade().unwrap().unerase(guard).clone(),
519 }
520 }
521}
522
523fn internal_properties_to_public<'a>(
524 prop_iter: impl Iterator<Item = (&'a SmolStr, &'a PropertyDeclaration)> + 'a,
525) -> impl Iterator<
526 Item = (
527 SmolStr,
528 i_slint_compiler::langtype::Type,
529 i_slint_compiler::object_tree::PropertyVisibility,
530 ),
531> + 'a {
532 prop_iter.filter(|(_, v)| v.expose_in_public_api).map(|(s, v)| {
533 let name = v
534 .node
535 .as_ref()
536 .and_then(|n| {
537 n.child_node(parser::SyntaxKind::DeclaredIdentifier)
538 .and_then(|n| n.child_token(parser::SyntaxKind::Identifier))
539 })
540 .map(|n| n.to_smolstr())
541 .unwrap_or_else(|| s.to_smolstr());
542 (name, v.property_type.clone(), v.visibility)
543 })
544}
545
546#[derive(Default)]
547pub enum WindowOptions {
548 #[default]
549 CreateNewWindow,
550 UseExistingWindow(WindowAdapterRc),
551 Embed {
552 parent_item_tree: ItemTreeWeak,
553 parent_item_tree_index: u32,
554 },
555}
556
557impl ItemTreeDescription<'_> {
558 pub fn id(&self) -> &str {
560 self.original.id.as_str()
561 }
562
563 pub fn properties(
567 &self,
568 ) -> impl Iterator<
569 Item = (
570 SmolStr,
571 i_slint_compiler::langtype::Type,
572 i_slint_compiler::object_tree::PropertyVisibility,
573 ),
574 > + '_ {
575 internal_properties_to_public(self.public_properties.iter())
576 }
577
578 pub fn global_names(&self) -> impl Iterator<Item = SmolStr> + '_ {
580 self.compiled_globals
581 .as_ref()
582 .expect("Root component should have globals")
583 .compiled_globals
584 .iter()
585 .filter(|g| g.visible_in_public_api())
586 .flat_map(|g| g.names().into_iter())
587 }
588
589 pub fn global_properties(
590 &self,
591 name: &str,
592 ) -> Option<
593 impl Iterator<
594 Item = (
595 SmolStr,
596 i_slint_compiler::langtype::Type,
597 i_slint_compiler::object_tree::PropertyVisibility,
598 ),
599 > + '_,
600 > {
601 let g = self.compiled_globals.as_ref().expect("Root component should have globals");
602 g.exported_globals_by_name
603 .get(&crate::normalize_identifier(name))
604 .and_then(|global_idx| g.compiled_globals.get(*global_idx))
605 .map(|global| internal_properties_to_public(global.public_properties()))
606 }
607
608 pub fn create(
610 self: Rc<Self>,
611 options: WindowOptions,
612 ) -> Result<DynamicComponentVRc, PlatformError> {
613 i_slint_backend_selector::with_platform(|_b| {
614 Ok(())
616 })?;
617
618 let instance = instantiate(self, None, None, Some(&options), Default::default());
619 if let WindowOptions::UseExistingWindow(existing_adapter) = options {
620 WindowInner::from_pub(existing_adapter.window())
621 .set_component(&vtable::VRc::into_dyn(instance.clone()));
622 }
623 instance.run_setup_code();
624 Ok(instance)
625 }
626
627 pub fn set_property(
633 &self,
634 component: ItemTreeRefPin,
635 name: &str,
636 value: Value,
637 ) -> Result<(), crate::api::SetPropertyError> {
638 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
639 panic!("mismatch instance and vtable");
640 }
641 generativity::make_guard!(guard);
642 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
643 if let Some(alias) = self
644 .original
645 .root_element
646 .borrow()
647 .property_declarations
648 .get(name)
649 .and_then(|d| d.is_alias.as_ref())
650 {
651 eval::store_property(c, &alias.element(), alias.name(), value)
652 } else {
653 eval::store_property(c, &self.original.root_element, name, value)
654 }
655 }
656
657 pub fn set_binding(
662 &self,
663 component: ItemTreeRefPin,
664 name: &str,
665 binding: Box<dyn Fn() -> Value>,
666 ) -> Result<(), ()> {
667 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
668 return Err(());
669 }
670 let x = self.custom_properties.get(name).ok_or(())?;
671 unsafe {
672 x.prop
673 .set_binding(
674 Pin::new_unchecked(&*component.as_ptr().add(x.offset)),
675 binding,
676 i_slint_core::rtti::AnimatedBindingKind::NotAnimated,
677 )
678 .unwrap()
679 };
680 Ok(())
681 }
682
683 pub fn get_property(&self, component: ItemTreeRefPin, name: &str) -> Result<Value, ()> {
688 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
689 return Err(());
690 }
691 generativity::make_guard!(guard);
692 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
694 if let Some(alias) = self
695 .original
696 .root_element
697 .borrow()
698 .property_declarations
699 .get(name)
700 .and_then(|d| d.is_alias.as_ref())
701 {
702 eval::load_property(c, &alias.element(), alias.name())
703 } else {
704 eval::load_property(c, &self.original.root_element, name)
705 }
706 }
707
708 pub fn set_callback_handler(
713 &self,
714 component: Pin<ItemTreeRef>,
715 name: &str,
716 handler: CallbackHandler,
717 ) -> Result<(), ()> {
718 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
719 return Err(());
720 }
721 if let Some(alias) = self
722 .original
723 .root_element
724 .borrow()
725 .property_declarations
726 .get(name)
727 .and_then(|d| d.is_alias.as_ref())
728 {
729 generativity::make_guard!(guard);
730 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
732 let inst = eval::ComponentInstance::InstanceRef(c);
733 eval::set_callback_handler(&inst, &alias.element(), alias.name(), handler)?
734 } else {
735 let x = self.custom_callbacks.get(name).ok_or(())?;
736 let inst = unsafe { &*(component.as_ptr() as *const dynamic_type::Instance) };
737 let sig = x.apply(inst);
738 sig.set_handler(handler);
739 if let Some(tracker_offset) = self.callback_trackers.get(name) {
740 tracker_offset.apply_pin(unsafe { Pin::new_unchecked(inst) }).mark_dirty();
741 }
742 }
743 Ok(())
744 }
745
746 pub fn invoke(
751 &self,
752 component: ItemTreeRefPin,
753 name: &SmolStr,
754 args: &[Value],
755 ) -> Result<Value, ()> {
756 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
757 return Err(());
758 }
759 generativity::make_guard!(guard);
760 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
762 let borrow = self.original.root_element.borrow();
763 let decl = borrow.property_declarations.get(name).ok_or(())?;
764
765 let (elem, name) = if let Some(alias) = &decl.is_alias {
766 (alias.element(), alias.name())
767 } else {
768 (self.original.root_element.clone(), name)
769 };
770
771 let inst = eval::ComponentInstance::InstanceRef(c);
772
773 if matches!(&decl.property_type, Type::Function { .. }) {
774 eval::call_function(&inst, &elem, name, args.to_vec()).ok_or(())
775 } else {
776 eval::invoke_callback(&inst, &elem, name, args).ok_or(())
777 }
778 }
779
780 pub fn get_global(
782 &self,
783 component: ItemTreeRefPin,
784 global_name: &str,
785 ) -> Result<Pin<Rc<dyn crate::global_component::GlobalComponent>>, ()> {
786 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
787 return Err(());
788 }
789 generativity::make_guard!(guard);
790 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
792 let extra_data = c.description.extra_data_offset.apply(c.instance.get_ref());
793 let g = extra_data.globals.get().unwrap().get(global_name).clone();
794 g.ok_or(())
795 }
796}
797
798#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
799extern "C" fn visit_children_item(
800 component: ItemTreeRefPin,
801 index: isize,
802 order: TraversalOrder,
803 v: ItemVisitorRefMut,
804) -> VisitChildrenResult {
805 generativity::make_guard!(guard);
806 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
807 let comp_rc = instance_ref.self_weak().get().unwrap().upgrade().unwrap();
808 i_slint_core::item_tree::visit_item_tree(
809 instance_ref.instance,
810 &vtable::VRc::into_dyn(comp_rc),
811 get_item_tree(component).as_slice(),
812 index,
813 order,
814 v,
815 |_, order, visitor, index| {
816 if index as usize >= instance_ref.description.repeater.len() {
817 VisitChildrenResult::CONTINUE
819 } else {
820 generativity::make_guard!(guard);
821 let rep_in_comp = instance_ref.description.repeater[index as usize].unerase(guard);
822 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
823 repeater.visit(order, visitor)
824 }
825 },
826 )
827}
828
829pub(crate) struct ItemRTTI {
831 vtable: &'static ItemVTable,
832 type_info: dynamic_type::StaticTypeInfo,
833 pub(crate) properties: HashMap<&'static str, Box<dyn eval::ErasedPropertyInfo>>,
834 pub(crate) callbacks: HashMap<&'static str, Box<dyn eval::ErasedCallbackInfo>>,
835}
836
837fn rtti_for<T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>>()
838-> (&'static str, Rc<ItemRTTI>) {
839 let rtti = ItemRTTI {
840 vtable: T::static_vtable(),
841 type_info: dynamic_type::StaticTypeInfo::new::<T>(),
842 properties: T::properties()
843 .into_iter()
844 .map(|(k, v)| (k, Box::new(v) as Box<dyn eval::ErasedPropertyInfo>))
845 .collect(),
846 callbacks: T::callbacks()
847 .into_iter()
848 .map(|(k, v)| (k, Box::new(v) as Box<dyn eval::ErasedCallbackInfo>))
849 .collect(),
850 };
851 (T::name(), Rc::new(rtti))
852}
853
854pub async fn load(
858 source: String,
859 path: std::path::PathBuf,
860 mut compiler_config: CompilerConfiguration,
861) -> CompilationResult {
862 let is_native = compiler_config.style.as_deref() == Some("native");
864 if is_native {
865 #[cfg(target_arch = "wasm32")]
867 let target = web_sys::window()
868 .and_then(|window| window.navigator().platform().ok())
869 .map_or("wasm", |platform| {
870 let platform = platform.to_ascii_lowercase();
871 if platform.contains("mac")
872 || platform.contains("iphone")
873 || platform.contains("ipad")
874 {
875 "apple"
876 } else if platform.contains("android") {
877 "android"
878 } else if platform.contains("win") {
879 "windows"
880 } else if platform.contains("linux") {
881 "linux"
882 } else {
883 "wasm"
884 }
885 });
886 #[cfg(not(target_arch = "wasm32"))]
887 let target = "";
888 compiler_config.style = Some(
889 i_slint_common::get_native_style(i_slint_backend_selector::HAS_NATIVE_STYLE, target)
890 .to_string(),
891 );
892 }
893
894 let diag = BuildDiagnostics::default();
895 #[cfg(feature = "internal-highlight")]
896 let (path, mut diag, loader, raw_type_loader) =
897 i_slint_compiler::load_root_file_with_raw_type_loader(
898 &path,
899 &path,
900 source,
901 diag,
902 compiler_config,
903 )
904 .await;
905 #[cfg(not(feature = "internal-highlight"))]
906 let (path, mut diag, loader) =
907 i_slint_compiler::load_root_file(&path, &path, source, diag, compiler_config).await;
908 #[cfg(feature = "internal")]
909 let watch_paths = loader.all_files_to_watch().into_iter().collect();
910 if diag.has_errors() {
911 return CompilationResult {
912 components: HashMap::new(),
913 diagnostics: diag.into_iter().collect(),
914 #[cfg(feature = "internal")]
915 watch_paths,
916 #[cfg(feature = "internal")]
917 structs_and_enums: Vec::new(),
918 #[cfg(feature = "internal")]
919 named_exports: Vec::new(),
920 };
921 }
922
923 #[cfg(feature = "internal-highlight")]
924 let loader = Rc::new(loader);
925 #[cfg(feature = "internal-highlight")]
926 let raw_type_loader = raw_type_loader.map(Rc::new);
927
928 let doc = loader.get_document(&path).unwrap();
929
930 let compiled_globals = Rc::new(CompiledGlobalCollection::compile(doc));
931 let mut components = HashMap::new();
932
933 let popup_menu_description = if let Some(popup_menu_impl) = &doc.popup_menu_impl {
934 PopupMenuDescription::Rc(Rc::new_cyclic(|weak| {
935 generativity::make_guard!(guard);
936 ErasedItemTreeDescription::from(generate_item_tree(
937 popup_menu_impl,
938 Some(compiled_globals.clone()),
939 PopupMenuDescription::Weak(weak.clone()),
940 true,
941 guard,
942 ))
943 }))
944 } else {
945 PopupMenuDescription::Weak(Default::default())
946 };
947
948 for c in doc.exported_roots() {
949 generativity::make_guard!(guard);
950 #[allow(unused_mut)]
951 let mut it = generate_item_tree(
952 &c,
953 Some(compiled_globals.clone()),
954 popup_menu_description.clone(),
955 false,
956 guard,
957 );
958 #[cfg(feature = "internal-highlight")]
959 {
960 let _ = it.type_loader.set(loader.clone());
961 let _ = it.raw_type_loader.set(raw_type_loader.clone());
962 }
963 components.insert(c.id.to_string(), ComponentDefinition { inner: it.into() });
964 }
965
966 if components.is_empty() {
967 diag.push_error_with_span("No component found".into(), Default::default());
968 };
969
970 #[cfg(feature = "internal")]
971 let structs_and_enums = doc.used_types.borrow().structs_and_enums.clone();
972
973 #[cfg(feature = "internal")]
974 let named_exports = doc
975 .exports
976 .iter()
977 .filter_map(|export| match &export.1 {
978 Either::Left(component) if !component.is_global() => {
979 Some((&export.0.name, &component.id))
980 }
981 Either::Right(ty) => match &ty {
982 Type::Struct(s) if s.node().is_some() => {
983 if let StructName::User { name, .. } = &s.name {
984 Some((&export.0.name, name))
985 } else {
986 None
987 }
988 }
989 Type::Enumeration(en) => Some((&export.0.name, &en.name)),
990 _ => None,
991 },
992 _ => None,
993 })
994 .filter(|(export_name, type_name)| *export_name != *type_name)
995 .map(|(export_name, type_name)| (type_name.to_string(), export_name.to_string()))
996 .collect::<Vec<_>>();
997
998 CompilationResult {
999 diagnostics: diag.into_iter().collect(),
1000 components,
1001 #[cfg(feature = "internal")]
1002 watch_paths,
1003 #[cfg(feature = "internal")]
1004 structs_and_enums,
1005 #[cfg(feature = "internal")]
1006 named_exports,
1007 }
1008}
1009
1010fn generate_rtti() -> HashMap<&'static str, Rc<ItemRTTI>> {
1011 let mut rtti = HashMap::new();
1012 use i_slint_core::items::*;
1013 rtti.extend(
1014 [
1015 rtti_for::<ComponentContainer>(),
1016 rtti_for::<Empty>(),
1017 rtti_for::<ImageItem>(),
1018 rtti_for::<ClippedImage>(),
1019 rtti_for::<ComplexText>(),
1020 rtti_for::<StyledTextItem>(),
1021 rtti_for::<SimpleText>(),
1022 rtti_for::<Rectangle>(),
1023 rtti_for::<BasicBorderRectangle>(),
1024 rtti_for::<BorderRectangle>(),
1025 rtti_for::<TouchArea>(),
1026 rtti_for::<TooltipArea>(),
1027 rtti_for::<FocusScope>(),
1028 rtti_for::<KeyBinding>(),
1029 rtti_for::<SwipeGestureHandler>(),
1030 rtti_for::<ScaleRotateGestureHandler>(),
1031 rtti_for::<Path>(),
1032 rtti_for::<Flickable>(),
1033 rtti_for::<WindowItem>(),
1034 rtti_for::<TextInput>(),
1035 rtti_for::<Clip>(),
1036 rtti_for::<BoxShadow>(),
1037 rtti_for::<Transform>(),
1038 rtti_for::<Opacity>(),
1039 rtti_for::<Layer>(),
1040 rtti_for::<DragArea>(),
1041 rtti_for::<DropArea>(),
1042 rtti_for::<ContextMenu>(),
1043 rtti_for::<MenuItem>(),
1044 rtti_for::<SystemTrayIcon>(),
1045 ]
1046 .iter()
1047 .cloned(),
1048 );
1049
1050 trait NativeHelper {
1051 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>);
1052 }
1053 impl NativeHelper for () {
1054 fn push(_rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {}
1055 }
1056 impl<
1057 T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>,
1058 Next: NativeHelper,
1059 > NativeHelper for (T, Next)
1060 {
1061 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {
1062 let info = rtti_for::<T>();
1063 rtti.insert(info.0, info.1);
1064 Next::push(rtti);
1065 }
1066 }
1067 i_slint_backend_selector::NativeWidgets::push(&mut rtti);
1068
1069 rtti
1070}
1071
1072pub(crate) fn generate_item_tree<'id>(
1073 component: &Rc<object_tree::Component>,
1074 compiled_globals: Option<Rc<CompiledGlobalCollection>>,
1075 popup_menu_description: PopupMenuDescription,
1076 is_popup_menu_impl: bool,
1077 guard: generativity::Guard<'id>,
1078) -> Rc<ItemTreeDescription<'id>> {
1079 thread_local! {
1082 static RTTI: Lazy<HashMap<&'static str, Rc<ItemRTTI>>> = Lazy::new(generate_rtti);
1083 }
1084
1085 struct TreeBuilder<'id> {
1086 tree_array: Vec<ItemTreeNode>,
1087 item_array:
1088 Vec<vtable::VOffset<crate::dynamic_type::Instance<'id>, ItemVTable, vtable::AllowPin>>,
1089 original_elements: Vec<ElementRc>,
1090 items_types: HashMap<SmolStr, ItemWithinItemTree>,
1091 type_builder: dynamic_type::TypeBuilder<'id>,
1092 repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
1093 repeater_names: HashMap<SmolStr, usize>,
1094 change_callbacks: Vec<(NamedReference, Expression)>,
1095 popup_menu_description: PopupMenuDescription,
1096 }
1097 impl generator::ItemTreeBuilder for TreeBuilder<'_> {
1098 type SubComponentState = ();
1099
1100 fn push_repeated_item(
1101 &mut self,
1102 item_rc: &ElementRc,
1103 repeater_count: u32,
1104 parent_index: u32,
1105 _component_state: &Self::SubComponentState,
1106 ) {
1107 self.tree_array.push(ItemTreeNode::DynamicTree { index: repeater_count, parent_index });
1108 self.original_elements.push(item_rc.clone());
1109 let item = item_rc.borrow();
1110 let base_component = item.base_type.as_component();
1111 self.repeater_names.insert(item.id.clone(), self.repeater.len());
1112 generativity::make_guard!(guard);
1113 let repeated_element_info = item.repeated.as_ref().unwrap();
1114 self.repeater.push(
1115 RepeaterWithinItemTree {
1116 item_tree_to_repeat: generate_item_tree(
1117 base_component,
1118 None,
1119 self.popup_menu_description.clone(),
1120 false,
1121 guard,
1122 ),
1123 offset: self.type_builder.add_field_type::<Repeater<ErasedItemTreeBox>>(),
1124 model: repeated_element_info.model.clone(),
1125 is_conditional: repeated_element_info.is_conditional_element,
1126 }
1127 .into(),
1128 );
1129 }
1130
1131 fn push_native_item(
1132 &mut self,
1133 rc_item: &ElementRc,
1134 child_offset: u32,
1135 parent_index: u32,
1136 _component_state: &Self::SubComponentState,
1137 ) {
1138 let item = rc_item.borrow();
1139 let rt = RTTI.with(|rtti| {
1140 rtti.get(&*item.base_type.as_native().class_name)
1141 .unwrap_or_else(|| {
1142 panic!(
1143 "Native type not registered: {}",
1144 item.base_type.as_native().class_name
1145 )
1146 })
1147 .clone()
1148 });
1149
1150 let offset = self.type_builder.add_field(rt.type_info);
1151
1152 self.tree_array.push(ItemTreeNode::Item {
1153 is_accessible: !item.accessibility_props.0.is_empty(),
1154 children_index: child_offset,
1155 children_count: item.children.len() as u32,
1156 parent_index,
1157 item_array_index: self.item_array.len() as u32,
1158 });
1159 self.item_array.push(unsafe { vtable::VOffset::from_raw(rt.vtable, offset) });
1160 self.original_elements.push(rc_item.clone());
1161 debug_assert_eq!(self.original_elements.len(), self.tree_array.len());
1162 self.items_types.insert(
1163 item.id.clone(),
1164 ItemWithinItemTree { offset, rtti: rt, elem: rc_item.clone() },
1165 );
1166 for (prop, expr) in &item.change_callbacks {
1167 self.change_callbacks.push((
1168 NamedReference::new(rc_item, prop.clone()),
1169 Expression::CodeBlock(expr.borrow().clone()),
1170 ));
1171 }
1172 }
1173
1174 fn enter_component(
1175 &mut self,
1176 _item: &ElementRc,
1177 _sub_component: &Rc<object_tree::Component>,
1178 _children_offset: u32,
1179 _component_state: &Self::SubComponentState,
1180 ) -> Self::SubComponentState {
1181 }
1183
1184 fn enter_component_children(
1185 &mut self,
1186 _item: &ElementRc,
1187 _repeater_count: u32,
1188 _component_state: &Self::SubComponentState,
1189 _sub_component_state: &Self::SubComponentState,
1190 ) {
1191 todo!()
1192 }
1193 }
1194
1195 let mut builder = TreeBuilder {
1196 tree_array: Vec::new(),
1197 item_array: Vec::new(),
1198 original_elements: Vec::new(),
1199 items_types: HashMap::new(),
1200 type_builder: dynamic_type::TypeBuilder::new(guard),
1201 repeater: Vec::new(),
1202 repeater_names: HashMap::new(),
1203 change_callbacks: Vec::new(),
1204 popup_menu_description,
1205 };
1206
1207 if !component.is_global() {
1208 generator::build_item_tree(component, &(), &mut builder);
1209 } else {
1210 for (prop, expr) in component.root_element.borrow().change_callbacks.iter() {
1211 builder.change_callbacks.push((
1212 NamedReference::new(&component.root_element, prop.clone()),
1213 Expression::CodeBlock(expr.borrow().clone()),
1214 ));
1215 }
1216 }
1217
1218 let mut custom_properties = HashMap::new();
1219 let mut custom_callbacks = HashMap::new();
1220 let mut callback_trackers = HashMap::new();
1221 fn property_info<T>() -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1222 where
1223 T: PartialEq + Clone + Default + std::convert::TryInto<Value> + 'static,
1224 Value: std::convert::TryInto<T>,
1225 {
1226 (
1228 Box::new(unsafe {
1229 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0)
1230 }),
1231 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1232 )
1233 }
1234 fn animated_property_info<T>()
1235 -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1236 where
1237 T: Clone + Default + InterpolatedPropertyValue + std::convert::TryInto<Value> + 'static,
1238 Value: std::convert::TryInto<T>,
1239 {
1240 (
1242 Box::new(unsafe {
1243 rtti::MaybeAnimatedPropertyInfoWrapper(
1244 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0),
1245 )
1246 }),
1247 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1248 )
1249 }
1250
1251 fn property_info_for_type(
1252 ty: &Type,
1253 name: &str,
1254 ) -> Option<(Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)> {
1255 Some(match ty {
1256 Type::Float32 => animated_property_info::<f32>(),
1257 Type::Int32 => animated_property_info::<i32>(),
1258 Type::String => property_info::<SharedString>(),
1259 Type::Color => animated_property_info::<Color>(),
1260 Type::Brush => animated_property_info::<Brush>(),
1261 Type::Duration => animated_property_info::<i64>(),
1262 Type::Angle => animated_property_info::<f32>(),
1263 Type::PhysicalLength => animated_property_info::<f32>(),
1264 Type::LogicalLength => animated_property_info::<f32>(),
1265 Type::Rem => animated_property_info::<f32>(),
1266 Type::Image => property_info::<i_slint_core::graphics::Image>(),
1267 Type::Bool => property_info::<bool>(),
1268 Type::ComponentFactory => property_info::<ComponentFactory>(),
1269 Type::Struct(s) if matches!(s.name, StructName::Builtin(BuiltinStruct::StateInfo)) => {
1270 property_info::<i_slint_core::properties::StateInfo>()
1271 }
1272 Type::Struct(_) => property_info::<Value>(),
1273 Type::Array(_) => property_info::<Value>(),
1274 Type::Easing => property_info::<i_slint_core::animations::EasingCurve>(),
1275 Type::Percent => animated_property_info::<f32>(),
1276 Type::Enumeration(e) => {
1277 macro_rules! match_enum_type {
1278 ($( $(#[$enum_doc:meta])* $vis:vis enum $Name:ident { $($body:tt)* })*) => {
1279 match e.name.as_str() {
1280 $(
1281 stringify!($Name) => property_info::<i_slint_core::items::$Name>(),
1282 )*
1283 x => unreachable!("Unknown non-builtin enum {x}"),
1284 }
1285 }
1286 }
1287
1288 if e.node.is_some() {
1289 property_info::<Value>()
1290 } else {
1291 i_slint_common::for_each_enums!(match_enum_type)
1292 }
1293 }
1294 Type::Keys => property_info::<Keys>(),
1295 Type::DataTransfer => property_info::<DataTransfer>(),
1296 Type::LayoutCache => property_info::<SharedVector<f32>>(),
1297 Type::ArrayOfU16 => property_info::<SharedVector<u16>>(),
1298 Type::Function { .. } | Type::Callback { .. } => return None,
1299 Type::StyledText => property_info::<StyledText>(),
1300 Type::Invalid
1302 | Type::Void
1303 | Type::InferredProperty
1304 | Type::InferredCallback
1305 | Type::Model
1306 | Type::PathData
1307 | Type::UnitProduct(_)
1308 | Type::ElementReference => panic!("bad type {ty:?} for property {name}"),
1309 })
1310 }
1311
1312 for (name, decl) in &component.root_element.borrow().property_declarations {
1313 if decl.is_alias.is_some() {
1314 continue;
1315 }
1316 if matches!(&decl.property_type, Type::Callback { .. }) {
1317 custom_callbacks
1318 .insert(name.clone(), builder.type_builder.add_field_type::<Callback>());
1319 if decl.expose_in_public_api {
1320 callback_trackers
1321 .insert(name.clone(), builder.type_builder.add_field_type::<Property<()>>());
1322 }
1323 continue;
1324 }
1325 let Some((prop, type_info)) = property_info_for_type(&decl.property_type, name) else {
1326 continue;
1327 };
1328 custom_properties.insert(
1329 name.clone(),
1330 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1331 );
1332 }
1333 if let Some(parent_element) = component.parent_element()
1334 && let Some(r) = &parent_element.borrow().repeated
1335 && !r.is_conditional_element
1336 {
1337 let (prop, type_info) = property_info::<u32>();
1338 custom_properties.insert(
1339 SPECIAL_PROPERTY_INDEX.into(),
1340 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1341 );
1342
1343 let model_ty = Expression::RepeaterModelReference {
1344 element: component.parent_element.borrow().clone(),
1345 }
1346 .ty();
1347 let (prop, type_info) =
1348 property_info_for_type(&model_ty, SPECIAL_PROPERTY_MODEL_DATA).unwrap();
1349 custom_properties.insert(
1350 SPECIAL_PROPERTY_MODEL_DATA.into(),
1351 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1352 );
1353 }
1354
1355 let parent_item_tree_offset = if component.parent_element().is_some() || is_popup_menu_impl {
1356 Some(builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>())
1357 } else {
1358 None
1359 };
1360
1361 let root_offset = builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>();
1362 let extra_data_offset = builder.type_builder.add_field_type::<ComponentExtraData>();
1363
1364 let change_trackers = (!builder.change_callbacks.is_empty()).then(|| {
1365 (
1366 builder.type_builder.add_field_type::<OnceCell<Vec<ChangeTracker>>>(),
1367 builder.change_callbacks,
1368 )
1369 });
1370 let timers = component
1371 .timers
1372 .borrow()
1373 .iter()
1374 .map(|_| builder.type_builder.add_field_type::<Timer>())
1375 .collect();
1376
1377 let public_properties = if component.parent_element().is_none() {
1379 component.root_element.borrow().property_declarations.clone()
1380 } else {
1381 Default::default()
1382 };
1383
1384 let t = ItemTreeVTable {
1385 visit_children_item,
1386 layout_info,
1387 ensure_instantiated,
1388 get_item_ref,
1389 get_item_tree,
1390 get_subtree_range,
1391 get_subtree,
1392 parent_node,
1393 embed_component,
1394 subtree_index,
1395 item_geometry,
1396 accessible_role,
1397 accessible_string_property,
1398 accessibility_action,
1399 supported_accessibility_actions,
1400 item_element_infos,
1401 window_adapter,
1402 drop_in_place,
1403 dealloc,
1404 };
1405 let t = ItemTreeDescription {
1406 ct: t,
1407 dynamic_type: builder.type_builder.build(),
1408 item_tree: builder.tree_array,
1409 item_array: builder.item_array,
1410 items: builder.items_types,
1411 custom_properties,
1412 custom_callbacks,
1413 callback_trackers,
1414 original: component.clone(),
1415 original_elements: builder.original_elements,
1416 repeater: builder.repeater,
1417 repeater_names: builder.repeater_names,
1418 parent_item_tree_offset,
1419 root_offset,
1420 extra_data_offset,
1421 public_properties,
1422 compiled_globals,
1423 change_trackers,
1424 timers,
1425 popup_ids: std::cell::RefCell::new(HashMap::new()),
1426 popup_menu_description: builder.popup_menu_description,
1427 #[cfg(feature = "internal-highlight")]
1428 type_loader: std::cell::OnceCell::new(),
1429 #[cfg(feature = "internal-highlight")]
1430 raw_type_loader: std::cell::OnceCell::new(),
1431 };
1432
1433 Rc::new(t)
1434}
1435
1436pub fn animation_for_property(
1437 component: InstanceRef,
1438 animation: &Option<i_slint_compiler::object_tree::PropertyAnimation>,
1439) -> AnimatedBindingKind {
1440 match animation {
1441 Some(i_slint_compiler::object_tree::PropertyAnimation::Static(anim_elem)) => {
1442 AnimatedBindingKind::Animation(Box::new({
1443 let component_ptr = component.as_ptr();
1444 let vtable = NonNull::from(&component.description.ct).cast();
1445 let anim_elem = Rc::clone(anim_elem);
1446 move || -> PropertyAnimation {
1447 generativity::make_guard!(guard);
1448 let component = unsafe {
1449 InstanceRef::from_pin_ref(
1450 Pin::new_unchecked(vtable::VRef::from_raw(
1451 vtable,
1452 NonNull::new_unchecked(component_ptr as *mut u8),
1453 )),
1454 guard,
1455 )
1456 };
1457
1458 eval::new_struct_with_bindings(
1459 &anim_elem.borrow().bindings,
1460 &mut eval::EvalLocalContext::from_component_instance(component),
1461 )
1462 }
1463 }))
1464 }
1465 Some(i_slint_compiler::object_tree::PropertyAnimation::Transition {
1466 animations,
1467 state_ref,
1468 }) => {
1469 let component_ptr = component.as_ptr();
1470 let vtable = NonNull::from(&component.description.ct).cast();
1471 let animations = animations.clone();
1472 let state_ref = state_ref.clone();
1473 AnimatedBindingKind::Transition(Box::new(
1474 move || -> (PropertyAnimation, i_slint_core::animations::Instant) {
1475 generativity::make_guard!(guard);
1476 let component = unsafe {
1477 InstanceRef::from_pin_ref(
1478 Pin::new_unchecked(vtable::VRef::from_raw(
1479 vtable,
1480 NonNull::new_unchecked(component_ptr as *mut u8),
1481 )),
1482 guard,
1483 )
1484 };
1485
1486 let mut context = eval::EvalLocalContext::from_component_instance(component);
1487 let state = eval::eval_expression(&state_ref, &mut context);
1488 let state_info: i_slint_core::properties::StateInfo = state.try_into().unwrap();
1489 for a in &animations {
1490 let is_previous_state = a.state_id == state_info.previous_state;
1491 let is_current_state = a.state_id == state_info.current_state;
1492 match (a.direction, is_previous_state, is_current_state) {
1493 (TransitionDirection::In, false, true)
1494 | (TransitionDirection::Out, true, false)
1495 | (TransitionDirection::InOut, false, true)
1496 | (TransitionDirection::InOut, true, false) => {
1497 return (
1498 eval::new_struct_with_bindings(
1499 &a.animation.borrow().bindings,
1500 &mut context,
1501 ),
1502 state_info.change_time,
1503 );
1504 }
1505 _ => {}
1506 }
1507 }
1508 Default::default()
1509 },
1510 ))
1511 }
1512 None => AnimatedBindingKind::NotAnimated,
1513 }
1514}
1515
1516fn make_callback_eval_closure(
1517 expr: Expression,
1518 self_weak: ErasedItemTreeBoxWeak,
1519) -> impl Fn(&[Value]) -> Value {
1520 move |args| {
1521 let self_rc = self_weak.upgrade().unwrap();
1522 generativity::make_guard!(guard);
1523 let self_ = self_rc.unerase(guard);
1524 let instance_ref = self_.borrow_instance();
1525 let mut local_context =
1526 eval::EvalLocalContext::from_function_arguments(instance_ref, args.to_vec());
1527 eval::eval_expression(&expr, &mut local_context)
1528 }
1529}
1530
1531fn make_binding_eval_closure(
1532 expr: Expression,
1533 self_weak: ErasedItemTreeBoxWeak,
1534) -> impl Fn() -> Value {
1535 move || {
1536 let self_rc = self_weak.upgrade().unwrap();
1537 generativity::make_guard!(guard);
1538 let self_ = self_rc.unerase(guard);
1539 let instance_ref = self_.borrow_instance();
1540 eval::eval_expression(
1541 &expr,
1542 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1543 )
1544 }
1545}
1546
1547pub fn instantiate(
1548 description: Rc<ItemTreeDescription>,
1549 parent_ctx: Option<ErasedItemTreeBoxWeak>,
1550 root: Option<ErasedItemTreeBoxWeak>,
1551 window_options: Option<&WindowOptions>,
1552 globals: crate::global_component::GlobalStorage,
1553) -> DynamicComponentVRc {
1554 let instance = description.dynamic_type.clone().create_instance();
1555
1556 let component_box = ItemTreeBox { instance, description: description.clone() };
1557
1558 let self_rc = vtable::VRc::new(ErasedItemTreeBox::from(component_box));
1559 let self_weak = vtable::VRc::downgrade(&self_rc);
1560
1561 generativity::make_guard!(guard);
1562 let comp = self_rc.unerase(guard);
1563 let instance_ref = comp.borrow_instance();
1564 instance_ref.self_weak().set(self_weak.clone()).ok();
1565 let description = comp.description();
1566
1567 if let Some(WindowOptions::UseExistingWindow(existing_adapter)) = &window_options
1568 && let Err((a, b)) = globals.window_adapter().unwrap().try_insert(existing_adapter.clone())
1569 {
1570 assert!(Rc::ptr_eq(a, &b), "window not the same as parent window");
1571 }
1572
1573 let has_parent = parent_ctx.is_some();
1574 if let Some(parent) = parent_ctx {
1575 description
1576 .parent_item_tree_offset
1577 .unwrap()
1578 .apply(instance_ref.as_ref())
1579 .set(parent)
1580 .ok()
1581 .unwrap();
1582 }
1583 let extra_data = description.extra_data_offset.apply(instance_ref.as_ref());
1584 extra_data.globals.set(globals.clone()).ok().unwrap();
1585
1586 let resolved_root = if let Some(WindowOptions::Embed { .. }) = window_options {
1587 self_weak.clone()
1588 } else {
1589 generativity::make_guard!(guard);
1590 root.or_else(|| {
1591 instance_ref.parent_instance(guard).map(|parent| parent.root_weak().clone())
1592 })
1593 .unwrap_or_else(|| self_weak.clone())
1594 };
1595 description.root_offset.apply(instance_ref.as_ref()).set(resolved_root).ok().unwrap();
1596
1597 if !has_parent && let Some(g) = description.compiled_globals.as_ref() {
1598 for g in g.compiled_globals.iter() {
1599 crate::global_component::instantiate(g, &globals, self_weak.clone());
1600 }
1601 }
1602
1603 if let Some(WindowOptions::Embed { parent_item_tree, parent_item_tree_index }) = window_options
1604 {
1605 vtable::VRc::borrow_pin(&self_rc)
1606 .as_ref()
1607 .embed_component(parent_item_tree, *parent_item_tree_index);
1608 }
1609
1610 if !description.original.is_global() {
1611 let maybe_window_adapter =
1612 if let Some(WindowOptions::UseExistingWindow(adapter)) = window_options.as_ref() {
1613 Some(adapter.clone())
1614 } else {
1615 extra_data.globals.get().unwrap().window_adapter().and_then(|wa| wa.get().cloned())
1616 };
1617
1618 let component_rc = vtable::VRc::into_dyn(self_rc.clone());
1619 i_slint_core::item_tree::register_item_tree(&component_rc, maybe_window_adapter);
1620 }
1621
1622 for (prop_name, decl) in &description.original.root_element.borrow().property_declarations {
1624 if !matches!(
1625 decl.property_type,
1626 Type::Struct { .. } | Type::Array(_) | Type::Enumeration(_)
1627 ) || decl.is_alias.is_some()
1628 {
1629 continue;
1630 }
1631 if let Some(b) = description.original.root_element.borrow().bindings.get(prop_name)
1632 && b.borrow().two_way_bindings.is_empty()
1633 {
1634 continue;
1635 }
1636 let p = description.custom_properties.get(prop_name).unwrap();
1637 unsafe {
1638 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(p.offset));
1639 p.prop.set(item, eval::default_value_for_type(&decl.property_type), None).unwrap();
1640 }
1641 }
1642
1643 #[cfg(slint_debug_property)]
1644 {
1645 let component_id = description.original.id.as_str();
1646
1647 for (prop_name, prop_info) in &description.custom_properties {
1649 let name = format!("{}.{}", component_id, prop_name);
1650 unsafe {
1651 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(prop_info.offset));
1652 prop_info.prop.set_debug_name(item, name);
1653 }
1654 }
1655
1656 for (item_name, item_within_component) in &description.items {
1658 let item = unsafe { item_within_component.item_from_item_tree(instance_ref.as_ptr()) };
1659 for (prop_name, prop_rtti) in &item_within_component.rtti.properties {
1660 let name = format!("{}::{}.{}", component_id, item_name, prop_name);
1661 prop_rtti.set_debug_name(item, name);
1662 }
1663 }
1664 }
1665
1666 generator::handle_property_bindings_init(
1667 &description.original,
1668 |elem, prop_name, binding| unsafe {
1669 let is_root = Rc::ptr_eq(
1670 elem,
1671 &elem.borrow().enclosing_component.upgrade().unwrap().root_element,
1672 );
1673 let elem = elem.borrow();
1674 let is_const = binding.analysis.as_ref().is_some_and(|a| a.is_const);
1675
1676 let property_type = elem.lookup_property(prop_name).property_type;
1677 if let Type::Function { .. } = property_type {
1678 } else if let Type::Callback { .. } = property_type {
1680 if !matches!(binding.expression, Expression::Invalid) {
1681 let expr = binding.expression.clone();
1682 let description = description.clone();
1683 if let Some(callback_offset) =
1684 description.custom_callbacks.get(prop_name).filter(|_| is_root)
1685 {
1686 let callback = callback_offset.apply(instance_ref.as_ref());
1687 callback.set_handler(make_callback_eval_closure(expr, self_weak.clone()));
1688 } else {
1689 let item_within_component = &description.items[&elem.id];
1690 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1691 if let Some(callback) =
1692 item_within_component.rtti.callbacks.get(prop_name.as_str())
1693 {
1694 callback.set_handler(
1695 item,
1696 Box::new(make_callback_eval_closure(expr, self_weak.clone())),
1697 );
1698 } else {
1699 panic!("unknown callback {prop_name}")
1700 }
1701 }
1702 }
1703 } else if let Some(PropertiesWithinComponent { offset, prop: prop_info, .. }) =
1704 description.custom_properties.get(prop_name).filter(|_| is_root)
1705 {
1706 let is_state_info = matches!(&property_type, Type::Struct (s) if matches!(s.name, StructName::Builtin(BuiltinStruct::StateInfo)));
1707 if is_state_info {
1708 let prop = Pin::new_unchecked(
1709 &*(instance_ref.as_ptr().add(*offset)
1710 as *const Property<i_slint_core::properties::StateInfo>),
1711 );
1712 let e = binding.expression.clone();
1713 let state_binding = make_binding_eval_closure(e, self_weak.clone());
1714 i_slint_core::properties::set_state_binding(prop, move || {
1715 state_binding().try_into().unwrap()
1716 });
1717 return;
1718 }
1719
1720 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1721 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset));
1722
1723 if !matches!(binding.expression, Expression::Invalid) {
1724 if is_const {
1725 let v = eval::eval_expression(
1726 &binding.expression,
1727 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1728 );
1729 prop_info.set(item, v, None).unwrap();
1730 } else {
1731 let e = binding.expression.clone();
1732 prop_info
1733 .set_binding(
1734 item,
1735 Box::new(make_binding_eval_closure(e, self_weak.clone())),
1736 maybe_animation,
1737 )
1738 .unwrap();
1739 }
1740 }
1741 for twb in &binding.two_way_bindings {
1742 match twb {
1743 TwoWayBinding::Property { property, field_access }
1744 if field_access.is_empty()
1745 && !matches!(
1746 &property_type,
1747 Type::Struct(..) | Type::Array(..)
1748 ) =>
1749 {
1750 prop_info.link_two_ways(item, get_property_ptr(property, instance_ref));
1753 }
1754 TwoWayBinding::Property { property, field_access } => {
1755 let (common, map) =
1756 prepare_for_two_way_binding(instance_ref, property, field_access);
1757 prop_info.link_two_way_with_map(item, common, map);
1758 }
1759 TwoWayBinding::ModelData { repeated_element, field_access } => {
1760 let (getter, setter) = prepare_model_two_way_binding(
1761 instance_ref,
1762 repeated_element,
1763 field_access,
1764 );
1765 prop_info.link_two_way_to_model_data(item, getter, setter);
1766 }
1767 }
1768 }
1769 } else {
1770 let item_within_component = &description.items[&elem.id];
1771 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1772 if let Some(prop_rtti) =
1773 item_within_component.rtti.properties.get(prop_name.as_str())
1774 {
1775 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1776
1777 for twb in &binding.two_way_bindings {
1778 match twb {
1779 TwoWayBinding::Property { property, field_access }
1780 if field_access.is_empty()
1781 && !matches!(
1782 &property_type,
1783 Type::Struct(..) | Type::Array(..)
1784 ) =>
1785 {
1786 prop_rtti
1789 .link_two_ways(item, get_property_ptr(property, instance_ref));
1790 }
1791 TwoWayBinding::Property { property, field_access } => {
1792 let (common, map) = prepare_for_two_way_binding(
1793 instance_ref,
1794 property,
1795 field_access,
1796 );
1797 prop_rtti.link_two_way_with_map(item, common, map);
1798 }
1799 TwoWayBinding::ModelData { repeated_element, field_access } => {
1800 let (getter, setter) = prepare_model_two_way_binding(
1801 instance_ref,
1802 repeated_element,
1803 field_access,
1804 );
1805 prop_rtti.link_two_way_to_model_data(item, getter, setter);
1806 }
1807 }
1808 }
1809 if !matches!(binding.expression, Expression::Invalid) {
1810 if is_const {
1811 prop_rtti
1812 .set(
1813 item,
1814 eval::eval_expression(
1815 &binding.expression,
1816 &mut eval::EvalLocalContext::from_component_instance(
1817 instance_ref,
1818 ),
1819 ),
1820 maybe_animation.as_animation(),
1821 )
1822 .unwrap();
1823 } else {
1824 let e = binding.expression.clone();
1825 prop_rtti.set_binding(
1826 item,
1827 Box::new(make_binding_eval_closure(e, self_weak.clone())),
1828 maybe_animation,
1829 );
1830 }
1831 }
1832 } else {
1833 panic!("unknown property {} in {}", prop_name, elem.id);
1834 }
1835 }
1836 },
1837 );
1838
1839 for rep_in_comp in &description.repeater {
1840 generativity::make_guard!(guard);
1841 let rep_in_comp = rep_in_comp.unerase(guard);
1842
1843 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
1844 let expr = rep_in_comp.model.clone();
1845 let model_binding_closure = make_binding_eval_closure(expr, self_weak.clone());
1846 if rep_in_comp.is_conditional {
1847 let bool_model = Rc::new(crate::value_model::BoolModel::default());
1848 repeater.set_model_binding(move || {
1849 let v = model_binding_closure();
1850 bool_model.set_value(v.try_into().expect("condition model is bool"));
1851 ModelRc::from(bool_model.clone())
1852 });
1853 } else {
1854 repeater.set_model_binding(move || {
1855 let m = model_binding_closure();
1856 if let Value::Model(m) = m {
1857 m
1858 } else {
1859 ModelRc::new(crate::value_model::ValueModel::new(m))
1860 }
1861 });
1862 }
1863 }
1864 self_rc
1865}
1866
1867fn prepare_for_two_way_binding(
1868 instance_ref: InstanceRef,
1869 property: &NamedReference,
1870 field_access: &[SmolStr],
1871) -> (Pin<Rc<Property<Value>>>, Option<Rc<dyn rtti::TwoWayBindingMapping<Value>>>) {
1872 let element = property.element();
1873 let name = property.name().as_str();
1874
1875 generativity::make_guard!(guard);
1876 let enclosing_component = eval::enclosing_component_instance_for_element(
1877 &element,
1878 &eval::ComponentInstance::InstanceRef(instance_ref),
1879 guard,
1880 );
1881 let map: Option<Rc<dyn rtti::TwoWayBindingMapping<Value>>> = if field_access.is_empty() {
1882 None
1883 } else {
1884 struct FieldAccess(Vec<SmolStr>);
1885 impl rtti::TwoWayBindingMapping<Value> for FieldAccess {
1886 fn map_to(&self, value: &Value) -> Value {
1887 walk_struct_field_path(value.clone(), &self.0).unwrap_or_default()
1888 }
1889 fn map_from(&self, root: &mut Value, from: &Value) {
1890 if let Some(leaf) = walk_struct_field_path_mut(root, &self.0) {
1891 *leaf = from.clone();
1892 }
1893 }
1894 }
1895 Some(Rc::new(FieldAccess(field_access.to_vec())))
1896 };
1897 let common = match enclosing_component {
1898 eval::ComponentInstance::InstanceRef(enclosing_component) => {
1899 let element = element.borrow();
1900 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1901 && let Some(x) = enclosing_component.description.custom_properties.get(name)
1902 {
1903 let item =
1904 unsafe { Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset)) };
1905 let common = x.prop.prepare_for_two_way_binding(item);
1906 return (common, map);
1907 }
1908 let item_info = enclosing_component
1909 .description
1910 .items
1911 .get(element.id.as_str())
1912 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, name));
1913 let prop_info = item_info
1914 .rtti
1915 .properties
1916 .get(name)
1917 .unwrap_or_else(|| panic!("Property {} not in {}", name, element.id));
1918 core::mem::drop(element);
1919 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1920 prop_info.prepare_for_two_way_binding(item)
1921 }
1922 eval::ComponentInstance::GlobalComponent(glob) => {
1923 glob.as_ref().prepare_for_two_way_binding(name).unwrap()
1924 }
1925 };
1926 (common, map)
1927}
1928
1929fn prepare_model_two_way_binding(
1933 instance_ref: InstanceRef,
1934 repeated_element: &i_slint_compiler::object_tree::ElementWeak,
1935 field_access: &[SmolStr],
1936) -> (Box<dyn Fn() -> Option<Value>>, Box<dyn Fn(&Value)>) {
1937 let self_weak = instance_ref.self_weak().get().unwrap().clone();
1938 let repeated_element = repeated_element.clone();
1939 let field_access: Vec<SmolStr> = field_access.to_vec();
1940
1941 let getter = {
1942 let self_weak = self_weak.clone();
1943 let repeated_element = repeated_element.clone();
1944 let field_access = field_access.clone();
1945 Box::new(move || -> Option<Value> {
1946 with_repeater_row(&self_weak, &repeated_element, |repeater, row| {
1947 walk_struct_field_path(repeater.model_row_data(row)?, &field_access)
1948 })
1949 })
1950 };
1951
1952 let setter = Box::new(move |new_value: &Value| {
1953 with_repeater_row(&self_weak, &repeated_element, |repeater, row| {
1954 let mut data = repeater.model_row_data(row)?;
1955 let leaf = walk_struct_field_path_mut(&mut data, &field_access)?;
1957 if &*leaf == new_value {
1958 return Some(());
1959 }
1960 *leaf = new_value.clone();
1961 repeater.model_set_row_data(row, data);
1962 Some(())
1963 });
1964 });
1965
1966 (getter, setter)
1967}
1968
1969fn with_repeater_row<R>(
1972 self_weak: &ErasedItemTreeBoxWeak,
1973 repeated_element: &i_slint_compiler::object_tree::ElementWeak,
1974 f: impl FnOnce(Pin<&Repeater<ErasedItemTreeBox>>, usize) -> Option<R>,
1975) -> Option<R> {
1976 let self_rc = self_weak.upgrade()?;
1977 generativity::make_guard!(guard);
1978 let s = self_rc.unerase(guard);
1979 let instance = s.borrow_instance();
1980 let element = repeated_element.upgrade()?;
1981 let index = crate::eval::load_property(
1982 instance,
1983 &element.borrow().base_type.as_component().root_element,
1984 crate::dynamic_item_tree::SPECIAL_PROPERTY_INDEX,
1985 )
1986 .ok()?;
1987 let row = usize::try_from(i32::try_from(index).ok()?).ok()?;
1988 generativity::make_guard!(guard);
1989 let enclosing = crate::eval::enclosing_component_for_element(&element, instance, guard);
1990 generativity::make_guard!(guard);
1991 let (repeater, _) = get_repeater_by_name(enclosing, element.borrow().id.as_str(), guard);
1992 f(repeater, row)
1993}
1994
1995fn walk_struct_field_path(mut value: Value, fields: &[SmolStr]) -> Option<Value> {
1997 for f in fields {
1998 match value {
1999 Value::Struct(o) => value = o.get_field(f).cloned().unwrap_or_default(),
2000 Value::Void => return None,
2001 _ => return None,
2002 }
2003 }
2004 Some(value)
2005}
2006
2007fn walk_struct_field_path_mut<'a>(
2009 mut value: &'a mut Value,
2010 fields: &[SmolStr],
2011) -> Option<&'a mut Value> {
2012 for f in fields {
2013 match value {
2014 Value::Struct(o) => value = o.0.get_mut(f)?,
2015 _ => return None,
2016 }
2017 }
2018 Some(value)
2019}
2020
2021pub(crate) fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const c_void {
2022 let element = nr.element();
2023 generativity::make_guard!(guard);
2024 let enclosing_component = eval::enclosing_component_instance_for_element(
2025 &element,
2026 &eval::ComponentInstance::InstanceRef(instance),
2027 guard,
2028 );
2029 match enclosing_component {
2030 eval::ComponentInstance::InstanceRef(enclosing_component) => {
2031 let element = element.borrow();
2032 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
2033 && let Some(x) = enclosing_component.description.custom_properties.get(nr.name())
2034 {
2035 return unsafe { enclosing_component.as_ptr().add(x.offset).cast() };
2036 };
2037 let item_info = enclosing_component
2038 .description
2039 .items
2040 .get(element.id.as_str())
2041 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, nr.name()));
2042 let prop_info = item_info
2043 .rtti
2044 .properties
2045 .get(nr.name().as_str())
2046 .unwrap_or_else(|| panic!("Property {} not in {}", nr.name(), element.id));
2047 core::mem::drop(element);
2048 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
2049 unsafe { item.as_ptr().add(prop_info.offset()).cast() }
2050 }
2051 eval::ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property_ptr(nr.name()),
2052 }
2053}
2054
2055pub struct ErasedItemTreeBox(ItemTreeBox<'static>);
2056impl ErasedItemTreeBox {
2057 pub fn unerase<'a, 'id>(
2058 &'a self,
2059 _guard: generativity::Guard<'id>,
2060 ) -> Pin<&'a ItemTreeBox<'id>> {
2061 Pin::new(
2062 unsafe { core::mem::transmute::<&ItemTreeBox<'static>, &ItemTreeBox<'id>>(&self.0) },
2064 )
2065 }
2066
2067 pub fn borrow(&self) -> ItemTreeRefPin<'_> {
2068 self.0.borrow()
2070 }
2071
2072 pub fn window_adapter_ref(&self) -> Result<&WindowAdapterRc, PlatformError> {
2073 self.0.window_adapter_ref()
2074 }
2075
2076 pub fn run_setup_code(&self) {
2077 generativity::make_guard!(guard);
2078 let compo_box = self.unerase(guard);
2079 let instance_ref = compo_box.borrow_instance();
2080 for extra_init_code in self.0.description.original.init_code.borrow().iter() {
2081 eval::eval_expression(
2082 extra_init_code,
2083 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
2084 );
2085 }
2086 if let Some(cts) = instance_ref.description.change_trackers.as_ref() {
2087 let self_weak = instance_ref.self_weak().get().unwrap();
2088 let v = cts
2089 .1
2090 .iter()
2091 .enumerate()
2092 .map(|(idx, _)| {
2093 let ct = ChangeTracker::default();
2094 ct.init(
2095 self_weak.clone(),
2096 move |self_weak| {
2097 let s = self_weak.upgrade().unwrap();
2098 generativity::make_guard!(guard);
2099 let compo_box = s.unerase(guard);
2100 let instance_ref = compo_box.borrow_instance();
2101 let nr = &s.0.description.change_trackers.as_ref().unwrap().1[idx].0;
2102 eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap()
2103 },
2104 move |self_weak, _| {
2105 let s = self_weak.upgrade().unwrap();
2106 generativity::make_guard!(guard);
2107 let compo_box = s.unerase(guard);
2108 let instance_ref = compo_box.borrow_instance();
2109 let e = &s.0.description.change_trackers.as_ref().unwrap().1[idx].1;
2110 eval::eval_expression(
2111 e,
2112 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
2113 );
2114 },
2115 );
2116 ct
2117 })
2118 .collect::<Vec<_>>();
2119 cts.0
2120 .apply_pin(instance_ref.instance)
2121 .set(v)
2122 .unwrap_or_else(|_| panic!("run_setup_code called twice?"));
2123 }
2124 update_timers(instance_ref);
2125 }
2126}
2127impl<'id> From<ItemTreeBox<'id>> for ErasedItemTreeBox {
2128 fn from(inner: ItemTreeBox<'id>) -> Self {
2129 unsafe {
2132 ErasedItemTreeBox(core::mem::transmute::<ItemTreeBox<'id>, ItemTreeBox<'static>>(inner))
2133 }
2134 }
2135}
2136
2137pub fn get_repeater_by_name<'a, 'id>(
2138 instance_ref: InstanceRef<'a, '_>,
2139 name: &str,
2140 guard: generativity::Guard<'id>,
2141) -> (std::pin::Pin<&'a Repeater<ErasedItemTreeBox>>, Rc<ItemTreeDescription<'id>>) {
2142 let rep_index = instance_ref.description.repeater_names[name];
2143 let rep_in_comp = instance_ref.description.repeater[rep_index].unerase(guard);
2144 (rep_in_comp.offset.apply_pin(instance_ref.instance), rep_in_comp.item_tree_to_repeat.clone())
2145}
2146
2147#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2148extern "C" fn ensure_instantiated(component: ItemTreeRefPin) -> bool {
2149 generativity::make_guard!(guard);
2150 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2152
2153 let mut changed = false;
2154 for (tree_index, node) in instance_ref.description.item_tree.iter().enumerate() {
2155 if !matches!(node, ItemTreeNode::Item { .. }) {
2156 continue;
2157 }
2158 let item_ref = component.as_ref().get_item_ref(tree_index as u32);
2159 if let Some(container) = i_slint_core::items::ItemRef::downcast_pin::<
2160 i_slint_core::items::ComponentContainer,
2161 >(item_ref)
2162 {
2163 changed |= container.ensure_updated();
2164 }
2165 }
2166
2167 for rep_in_comp in &instance_ref.description.repeater {
2168 let rep_in_comp = unsafe { rep_in_comp.get_untagged() };
2170 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
2171 let init = || {
2172 let extra_data =
2173 instance_ref.description.extra_data_offset.apply(instance_ref.as_ref());
2174 instantiate(
2175 rep_in_comp.item_tree_to_repeat.clone(),
2176 instance_ref.self_weak().get().cloned(),
2177 None,
2178 None,
2179 extra_data.globals.get().unwrap().clone(),
2180 )
2181 };
2182 if let Some(lv) = &rep_in_comp
2183 .item_tree_to_repeat
2184 .original
2185 .parent_element
2186 .borrow()
2187 .upgrade()
2188 .unwrap()
2189 .borrow()
2190 .repeated
2191 .as_ref()
2192 .unwrap()
2193 .is_listview
2194 {
2195 let assume_property_logical_length =
2196 |prop| unsafe { Pin::new_unchecked(&*(prop as *const Property<LogicalLength>)) };
2197 changed |= repeater.ensure_updated_listview(
2198 init,
2199 assume_property_logical_length(get_property_ptr(&lv.viewport_width, instance_ref)),
2200 assume_property_logical_length(get_property_ptr(&lv.viewport_height, instance_ref)),
2201 assume_property_logical_length(get_property_ptr(&lv.viewport_y, instance_ref)),
2202 eval::load_property(
2203 instance_ref,
2204 &lv.listview_width.element(),
2205 lv.listview_width.name(),
2206 )
2207 .unwrap()
2208 .try_into()
2209 .unwrap(),
2210 assume_property_logical_length(get_property_ptr(&lv.listview_height, instance_ref)),
2211 );
2212 } else {
2213 changed |= repeater.ensure_updated(init);
2214 }
2215 }
2216 changed
2217}
2218
2219#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2220extern "C" fn layout_info(component: ItemTreeRefPin, orientation: Orientation) -> LayoutInfo {
2221 generativity::make_guard!(guard);
2222 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2224 let orientation = crate::eval_layout::from_runtime(orientation);
2225
2226 let root = &instance_ref.description.original.root_element;
2236 let cross_axis_constraint = match orientation {
2237 i_slint_compiler::layout::Orientation::Vertical => {
2238 root.borrow().layout_info_v_with_constraint.is_some().then_some(f32::MAX)
2239 }
2240 i_slint_compiler::layout::Orientation::Horizontal => {
2241 root.borrow().layout_info_h_with_constraint.is_some().then_some(f32::MAX)
2242 }
2243 };
2244 let mut result = crate::eval_layout::get_layout_info_with_constraint(
2245 root,
2246 instance_ref,
2247 &instance_ref.window_adapter(),
2248 orientation,
2249 cross_axis_constraint,
2250 );
2251
2252 let constraints = instance_ref.description.original.root_constraints.borrow();
2253 if constraints.has_explicit_restrictions(orientation) {
2254 crate::eval_layout::fill_layout_info_constraints(
2255 &mut result,
2256 &constraints,
2257 orientation,
2258 &|nr: &NamedReference| {
2259 eval::load_property(instance_ref, &nr.element(), nr.name())
2260 .unwrap()
2261 .try_into()
2262 .unwrap()
2263 },
2264 );
2265 }
2266 result
2267}
2268
2269#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2270unsafe extern "C" fn get_item_ref(component: ItemTreeRefPin, index: u32) -> Pin<ItemRef> {
2271 let tree = get_item_tree(component);
2272 match &tree[index as usize] {
2273 ItemTreeNode::Item { item_array_index, .. } => unsafe {
2274 generativity::make_guard!(guard);
2275 let instance_ref = InstanceRef::from_pin_ref(component, guard);
2276 core::mem::transmute::<Pin<ItemRef>, Pin<ItemRef>>(
2277 instance_ref.description.item_array[*item_array_index as usize]
2278 .apply_pin(instance_ref.instance),
2279 )
2280 },
2281 ItemTreeNode::DynamicTree { .. } => panic!("get_item_ref called on dynamic tree"),
2282 }
2283}
2284
2285#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2286extern "C" fn get_subtree_range(component: ItemTreeRefPin, index: u32) -> IndexRange {
2287 generativity::make_guard!(guard);
2288 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2289 if index as usize >= instance_ref.description.repeater.len() {
2290 let container_index = {
2291 let tree_node = &component.as_ref().get_item_tree()[index as usize];
2292 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
2293 *parent_index
2294 } else {
2295 u32::MAX
2296 }
2297 };
2298 let container = component.as_ref().get_item_ref(container_index);
2299 let container = i_slint_core::items::ItemRef::downcast_pin::<
2300 i_slint_core::items::ComponentContainer,
2301 >(container)
2302 .unwrap();
2303 container.subtree_range()
2304 } else {
2305 generativity::make_guard!(guard);
2306 let rep_in_comp = instance_ref.description.repeater[index as usize].unerase(guard);
2307
2308 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
2309 repeater.track_instance_changes();
2310 repeater.range().into()
2311 }
2312}
2313
2314#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2315extern "C" fn get_subtree(
2316 component: ItemTreeRefPin,
2317 index: u32,
2318 subtree_index: usize,
2319 result: &mut ItemTreeWeak,
2320) {
2321 generativity::make_guard!(guard);
2322 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2323 if index as usize >= instance_ref.description.repeater.len() {
2324 let container_index = {
2325 let tree_node = &component.as_ref().get_item_tree()[index as usize];
2326 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
2327 *parent_index
2328 } else {
2329 u32::MAX
2330 }
2331 };
2332 let container = component.as_ref().get_item_ref(container_index);
2333 let container = i_slint_core::items::ItemRef::downcast_pin::<
2334 i_slint_core::items::ComponentContainer,
2335 >(container)
2336 .unwrap();
2337 if subtree_index == 0 {
2338 *result = container.subtree_component();
2339 }
2340 } else {
2341 generativity::make_guard!(guard);
2342 let rep_in_comp = instance_ref.description.repeater[index as usize].unerase(guard);
2343
2344 let repeater = rep_in_comp.offset.apply(&instance_ref.instance);
2345 if let Some(instance_at) = repeater.instance_at(subtree_index) {
2346 *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(instance_at))
2347 }
2348 }
2349}
2350
2351#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2352extern "C" fn get_item_tree(component: ItemTreeRefPin) -> Slice<ItemTreeNode> {
2353 generativity::make_guard!(guard);
2354 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2355 let tree = instance_ref.description.item_tree.as_slice();
2356 unsafe { core::mem::transmute::<&[ItemTreeNode], &[ItemTreeNode]>(tree) }.into()
2357}
2358
2359#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2360extern "C" fn subtree_index(component: ItemTreeRefPin) -> usize {
2361 generativity::make_guard!(guard);
2362 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2363 if let Ok(value) = instance_ref.description.get_property(component, SPECIAL_PROPERTY_INDEX) {
2364 value.try_into().unwrap()
2365 } else {
2366 usize::MAX
2367 }
2368}
2369
2370#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2371unsafe extern "C" fn parent_node(component: ItemTreeRefPin, result: &mut ItemWeak) {
2372 generativity::make_guard!(guard);
2373 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2374
2375 let component_and_index = {
2376 if let Some(parent_offset) = instance_ref.description.parent_item_tree_offset {
2378 let parent_item_index = instance_ref
2379 .description
2380 .original
2381 .parent_element
2382 .borrow()
2383 .upgrade()
2384 .and_then(|e| e.borrow().item_index.get().cloned())
2385 .unwrap_or(u32::MAX);
2386 let parent_component = parent_offset
2387 .apply(instance_ref.as_ref())
2388 .get()
2389 .and_then(|p| p.upgrade())
2390 .map(vtable::VRc::into_dyn);
2391
2392 (parent_component, parent_item_index)
2393 } else if let Some((parent_component, parent_index)) = instance_ref
2394 .description
2395 .extra_data_offset
2396 .apply(instance_ref.as_ref())
2397 .embedding_position
2398 .get()
2399 {
2400 (parent_component.upgrade(), *parent_index)
2401 } else {
2402 (None, u32::MAX)
2403 }
2404 };
2405
2406 if let (Some(component), index) = component_and_index {
2407 *result = ItemRc::new(component, index).downgrade();
2408 }
2409}
2410
2411#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2412unsafe extern "C" fn embed_component(
2413 component: ItemTreeRefPin,
2414 parent_component: &ItemTreeWeak,
2415 parent_item_tree_index: u32,
2416) -> bool {
2417 generativity::make_guard!(guard);
2418 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2419
2420 if instance_ref.description.parent_item_tree_offset.is_some() {
2421 return false;
2423 }
2424
2425 {
2426 let prc = parent_component.upgrade().unwrap();
2428 let pref = vtable::VRc::borrow_pin(&prc);
2429 let it = pref.as_ref().get_item_tree();
2430 if !matches!(
2431 it.get(parent_item_tree_index as usize),
2432 Some(ItemTreeNode::DynamicTree { .. })
2433 ) {
2434 panic!("Trying to embed into a non-dynamic index in the parents item tree")
2435 }
2436 }
2437
2438 let extra_data = instance_ref.description.extra_data_offset.apply(instance_ref.as_ref());
2439 extra_data.embedding_position.set((parent_component.clone(), parent_item_tree_index)).is_ok()
2440}
2441
2442#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2443extern "C" fn item_geometry(component: ItemTreeRefPin, item_index: u32) -> LogicalRect {
2444 generativity::make_guard!(guard);
2445 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2446
2447 let e = instance_ref.description.original_elements[item_index as usize].borrow();
2448 let g = e.geometry_props.as_ref().unwrap();
2449
2450 let load_f32 = |nr: &NamedReference| -> f32 {
2451 crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2452 .unwrap()
2453 .try_into()
2454 .unwrap()
2455 };
2456
2457 LogicalRect {
2458 origin: (load_f32(&g.x), load_f32(&g.y)).into(),
2459 size: (load_f32(&g.width), load_f32(&g.height)).into(),
2460 }
2461}
2462
2463#[allow(improper_ctypes_definitions)]
2465#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2466extern "C" fn accessible_role(component: ItemTreeRefPin, item_index: u32) -> AccessibleRole {
2467 generativity::make_guard!(guard);
2468 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2469 let nr = instance_ref.description.original_elements[item_index as usize]
2470 .borrow()
2471 .accessibility_props
2472 .0
2473 .get("accessible-role")
2474 .cloned();
2475 match nr {
2476 Some(nr) => crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2477 .unwrap()
2478 .try_into()
2479 .unwrap(),
2480 None => AccessibleRole::default(),
2481 }
2482}
2483
2484#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2485extern "C" fn accessible_string_property(
2486 component: ItemTreeRefPin,
2487 item_index: u32,
2488 what: AccessibleStringProperty,
2489 result: &mut SharedString,
2490) -> bool {
2491 generativity::make_guard!(guard);
2492 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2493 let prop_name = format!("accessible-{what}");
2494 let nr = instance_ref.description.original_elements[item_index as usize]
2495 .borrow()
2496 .accessibility_props
2497 .0
2498 .get(&prop_name)
2499 .cloned();
2500 if let Some(nr) = nr {
2501 let value = crate::eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap();
2502 match value {
2503 Value::String(s) => *result = s,
2504 Value::Bool(b) => *result = if b { "true" } else { "false" }.into(),
2505 Value::Number(x) => *result = x.to_string().into(),
2506 Value::EnumerationValue(_, v) => *result = v.into(),
2507 _ => unimplemented!("invalid type for accessible_string_property"),
2508 };
2509 true
2510 } else {
2511 false
2512 }
2513}
2514
2515#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2516extern "C" fn accessibility_action(
2517 component: ItemTreeRefPin,
2518 item_index: u32,
2519 action: &AccessibilityAction,
2520) {
2521 let perform = |prop_name, args: &[Value]| {
2522 generativity::make_guard!(guard);
2523 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2524 let nr = instance_ref.description.original_elements[item_index as usize]
2525 .borrow()
2526 .accessibility_props
2527 .0
2528 .get(prop_name)
2529 .cloned();
2530 if let Some(nr) = nr {
2531 let instance_ref = eval::ComponentInstance::InstanceRef(instance_ref);
2532 crate::eval::invoke_callback(&instance_ref, &nr.element(), nr.name(), args).unwrap();
2533 }
2534 };
2535
2536 match action {
2537 AccessibilityAction::Default => perform("accessible-action-default", &[]),
2538 AccessibilityAction::Decrement => perform("accessible-action-decrement", &[]),
2539 AccessibilityAction::Increment => perform("accessible-action-increment", &[]),
2540 AccessibilityAction::Expand => perform("accessible-action-expand", &[]),
2541 AccessibilityAction::ReplaceSelectedText(_a) => {
2542 i_slint_core::debug_log!(
2544 "AccessibilityAction::ReplaceSelectedText not implemented in interpreter's accessibility_action"
2545 );
2546 }
2547 AccessibilityAction::SetValue(a) => {
2548 perform("accessible-action-set-value", &[Value::String(a.clone())])
2549 }
2550 };
2551}
2552
2553#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2554extern "C" fn supported_accessibility_actions(
2555 component: ItemTreeRefPin,
2556 item_index: u32,
2557) -> SupportedAccessibilityAction {
2558 generativity::make_guard!(guard);
2559 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2560 instance_ref.description.original_elements[item_index as usize]
2561 .borrow()
2562 .accessibility_props
2563 .0
2564 .keys()
2565 .filter_map(|x| x.strip_prefix("accessible-action-"))
2566 .fold(SupportedAccessibilityAction::default(), |acc, value| {
2567 SupportedAccessibilityAction::from_name(&i_slint_compiler::generator::to_pascal_case(
2568 value,
2569 ))
2570 .unwrap_or_else(|| panic!("Not an accessible action: {value:?}"))
2571 | acc
2572 })
2573}
2574
2575#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2576extern "C" fn item_element_infos(
2577 component: ItemTreeRefPin,
2578 item_index: u32,
2579 result: &mut SharedString,
2580) -> bool {
2581 generativity::make_guard!(guard);
2582 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2583 *result = instance_ref.description.original_elements[item_index as usize]
2584 .borrow()
2585 .element_infos()
2586 .into();
2587 true
2588}
2589
2590#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2591extern "C" fn window_adapter(
2592 component: ItemTreeRefPin,
2593 do_create: bool,
2594 result: &mut Option<WindowAdapterRc>,
2595) {
2596 generativity::make_guard!(guard);
2597 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2598 if do_create {
2599 *result = Some(instance_ref.window_adapter());
2600 } else {
2601 *result = instance_ref.maybe_window_adapter();
2602 }
2603}
2604
2605#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2606unsafe extern "C" fn drop_in_place(component: vtable::VRefMut<ItemTreeVTable>) -> vtable::Layout {
2607 unsafe {
2608 let instance_ptr = component.as_ptr() as *mut Instance<'static>;
2609 let layout = (*instance_ptr).type_info().layout();
2610 dynamic_type::TypeInfo::drop_in_place(instance_ptr);
2611 layout.into()
2612 }
2613}
2614
2615#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2616unsafe extern "C" fn dealloc(_vtable: &ItemTreeVTable, ptr: *mut u8, layout: vtable::Layout) {
2617 unsafe { std::alloc::dealloc(ptr, layout.try_into().unwrap()) };
2618}
2619
2620#[derive(Copy, Clone)]
2621pub struct InstanceRef<'a, 'id> {
2622 pub instance: Pin<&'a Instance<'id>>,
2623 pub description: &'a ItemTreeDescription<'id>,
2624}
2625
2626impl<'a, 'id> InstanceRef<'a, 'id> {
2627 pub unsafe fn from_pin_ref(
2628 component: ItemTreeRefPin<'a>,
2629 _guard: generativity::Guard<'id>,
2630 ) -> Self {
2631 unsafe {
2632 Self {
2633 instance: Pin::new_unchecked(
2634 &*(component.as_ref().as_ptr() as *const Instance<'id>),
2635 ),
2636 description: &*(Pin::into_inner_unchecked(component).get_vtable()
2637 as *const ItemTreeVTable
2638 as *const ItemTreeDescription<'id>),
2639 }
2640 }
2641 }
2642
2643 pub fn as_ptr(&self) -> *const u8 {
2644 (&*self.instance.as_ref()) as *const Instance as *const u8
2645 }
2646
2647 pub fn as_ref(&self) -> &Instance<'id> {
2648 &self.instance
2649 }
2650
2651 pub fn borrow(self) -> ItemTreeRefPin<'a> {
2653 unsafe {
2654 Pin::new_unchecked(vtable::VRef::from_raw(
2655 NonNull::from(&self.description.ct).cast(),
2656 NonNull::from(self.instance.get_ref()).cast(),
2657 ))
2658 }
2659 }
2660
2661 pub fn self_weak(&self) -> &OnceCell<ErasedItemTreeBoxWeak> {
2662 let extra_data = self.description.extra_data_offset.apply(self.as_ref());
2663 &extra_data.self_weak
2664 }
2665
2666 pub fn root_weak(&self) -> &ErasedItemTreeBoxWeak {
2667 self.description.root_offset.apply(self.as_ref()).get().unwrap()
2668 }
2669
2670 pub fn window_adapter(&self) -> WindowAdapterRc {
2671 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2672 let root = self.root_weak().upgrade().unwrap();
2673 generativity::make_guard!(guard);
2674 let comp = root.unerase(guard);
2675 Self::get_or_init_window_adapter_ref(
2676 &comp.description,
2677 root_weak,
2678 true,
2679 comp.instance.as_pin_ref().get_ref(),
2680 )
2681 .unwrap()
2682 .clone()
2683 }
2684
2685 pub fn get_or_init_window_adapter_ref<'b, 'id2>(
2686 description: &'b ItemTreeDescription<'id2>,
2687 root_weak: ItemTreeWeak,
2688 do_create: bool,
2689 instance: &'b Instance<'id2>,
2690 ) -> Result<&'b WindowAdapterRc, PlatformError> {
2691 description
2693 .extra_data_offset
2694 .apply(instance)
2695 .globals
2696 .get()
2697 .unwrap()
2698 .window_adapter()
2699 .unwrap()
2700 .get_or_try_init(|| {
2701 let mut parent_node = ItemWeak::default();
2702 if let Some(rc) = vtable::VWeak::upgrade(&root_weak) {
2703 vtable::VRc::borrow_pin(&rc).as_ref().parent_node(&mut parent_node);
2704 }
2705
2706 if let Some(parent) = parent_node.upgrade() {
2707 let mut result = None;
2709 vtable::VRc::borrow_pin(parent.item_tree())
2710 .as_ref()
2711 .window_adapter(do_create, &mut result);
2712 result.ok_or(PlatformError::NoPlatform)
2713 } else if do_create {
2714 let extra_data = description.extra_data_offset.apply(instance);
2715 let window_adapter = i_slint_backend_selector::with_platform(|_b| {
2717 _b.create_window_adapter()
2718 })?;
2719
2720 let comp_rc = extra_data.self_weak.get().unwrap().upgrade().unwrap();
2721 WindowInner::from_pub(window_adapter.window())
2722 .set_component(&vtable::VRc::into_dyn(comp_rc));
2723 Ok(window_adapter)
2724 } else {
2725 Err(PlatformError::NoPlatform)
2726 }
2727 })
2728 }
2729
2730 pub fn maybe_window_adapter(&self) -> Option<WindowAdapterRc> {
2731 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2732 let root = self.root_weak().upgrade()?;
2733 generativity::make_guard!(guard);
2734 let comp = root.unerase(guard);
2735 Self::get_or_init_window_adapter_ref(
2736 &comp.description,
2737 root_weak,
2738 false,
2739 comp.instance.as_pin_ref().get_ref(),
2740 )
2741 .ok()
2742 .cloned()
2743 }
2744
2745 pub fn access_window<R>(
2746 self,
2747 callback: impl FnOnce(&'_ i_slint_core::window::WindowInner) -> R,
2748 ) -> R {
2749 callback(WindowInner::from_pub(self.window_adapter().window()))
2750 }
2751
2752 pub fn parent_instance<'id2>(
2753 &self,
2754 _guard: generativity::Guard<'id2>,
2755 ) -> Option<InstanceRef<'a, 'id2>> {
2756 if let Some(parent_offset) = self.description.parent_item_tree_offset
2759 && let Some(parent) =
2760 parent_offset.apply(self.as_ref()).get().and_then(vtable::VWeak::upgrade)
2761 {
2762 let parent_instance = parent.unerase(_guard);
2763 let parent_instance = unsafe {
2765 std::mem::transmute::<InstanceRef<'_, 'id2>, InstanceRef<'a, 'id2>>(
2766 parent_instance.borrow_instance(),
2767 )
2768 };
2769 return Some(parent_instance);
2770 }
2771 None
2772 }
2773}
2774
2775pub fn show_popup(
2777 element: ElementRc,
2778 instance: InstanceRef,
2779 popup: &object_tree::PopupWindow,
2780 pos_getter: impl Fn(InstanceRef<'_, '_>) -> LogicalPosition + 'static,
2781 close_policy: PopupClosePolicy,
2782 parent_comp: ErasedItemTreeBoxWeak,
2783 parent_window_adapter: WindowAdapterRc,
2784 parent_item: &ItemRc,
2785) {
2786 generativity::make_guard!(guard);
2787
2788 let compiled = generate_item_tree(
2790 &popup.component,
2791 None,
2792 parent_comp.upgrade().unwrap().0.description().popup_menu_description.clone(),
2793 false,
2794 guard,
2795 );
2796
2797 let extra_data = instance.description.extra_data_offset.apply(instance.as_ref());
2798 let window_kind = if popup.is_tooltip { WindowKind::ToolTip } else { WindowKind::Popup };
2802 let globals = if let Some(window_adapter) =
2803 WindowInner::from_pub(parent_window_adapter.window())
2804 .create_child_window_adapter(window_kind)
2805 {
2806 extra_data.globals.get().unwrap().clone_with_window_adapter(window_adapter)
2807 } else {
2808 extra_data.globals.get().unwrap().clone()
2809 };
2810
2811 let popup_window_adapter = globals
2812 .window_adapter()
2813 .and_then(|window_adapter| window_adapter.get().cloned())
2814 .unwrap_or_else(|| parent_window_adapter.clone());
2815
2816 let inst = instantiate(
2817 compiled,
2818 Some(parent_comp),
2819 None,
2820 Some(&WindowOptions::UseExistingWindow(popup_window_adapter)),
2821 globals,
2822 );
2823 let inst_for_position = inst.clone();
2824 let access_position = Box::new(move || {
2825 generativity::make_guard!(guard);
2826 let compo_box = inst_for_position.unerase(guard);
2827 let instance_ref = compo_box.borrow_instance();
2828 pos_getter(instance_ref)
2829 });
2830 close_popup(element.clone(), instance, parent_window_adapter.clone());
2831 let window_kind = if popup.is_tooltip { WindowKind::ToolTip } else { WindowKind::Popup };
2832 instance.description.popup_ids.borrow_mut().insert(
2833 element.borrow().id.clone(),
2834 WindowInner::from_pub(parent_window_adapter.window()).show_popup(
2835 &vtable::VRc::into_dyn(inst.clone()),
2836 access_position,
2837 close_policy,
2838 parent_item,
2839 window_kind,
2840 ),
2841 );
2842 inst.run_setup_code();
2843}
2844
2845pub fn close_popup(
2846 element: ElementRc,
2847 instance: InstanceRef,
2848 parent_window_adapter: WindowAdapterRc,
2849) {
2850 if let Some(current_id) =
2851 instance.description.popup_ids.borrow_mut().remove(&element.borrow().id)
2852 {
2853 WindowInner::from_pub(parent_window_adapter.window()).close_popup(current_id);
2854 }
2855}
2856
2857pub fn make_menu_item_tree(
2858 menu_item_tree: &Rc<object_tree::Component>,
2859 enclosing_component: &InstanceRef,
2860 condition: Option<&Expression>,
2861 visible: Option<&Expression>,
2862) -> vtable::VRc<i_slint_core::menus::MenuVTable, MenuFromItemTree> {
2863 generativity::make_guard!(guard);
2864 let mit_compiled = generate_item_tree(
2865 menu_item_tree,
2866 None,
2867 enclosing_component.description.popup_menu_description.clone(),
2868 false,
2869 guard,
2870 );
2871 let enclosing_component_weak = enclosing_component.self_weak().get().unwrap();
2872 let extra_data =
2873 enclosing_component.description.extra_data_offset.apply(enclosing_component.as_ref());
2874 let mit_inst = instantiate(
2875 mit_compiled.clone(),
2876 Some(enclosing_component_weak.clone()),
2877 None,
2878 None,
2879 extra_data.globals.get().unwrap().clone(),
2880 );
2881 mit_inst.run_setup_code();
2882 let item_tree = vtable::VRc::into_dyn(mit_inst);
2883 let condition = condition.map(|condition| {
2884 let binding =
2885 make_binding_eval_closure(condition.clone(), enclosing_component_weak.clone());
2886 move || binding().try_into().unwrap()
2887 });
2888 let visible = visible.map(|visible| {
2889 let binding = make_binding_eval_closure(visible.clone(), enclosing_component_weak.clone());
2890 move || binding().try_into().unwrap()
2891 });
2892 let menu = match (condition, visible) {
2893 (None, None) => MenuFromItemTree::new(item_tree),
2894 (None, Some(visible)) => {
2895 MenuFromItemTree::new_with_condition_and_visible(item_tree, || true, visible)
2896 }
2897 (Some(condition), None) => {
2898 MenuFromItemTree::new_with_condition_and_visible(item_tree, condition, || true)
2899 }
2900 (Some(condition), Some(visible)) => {
2901 MenuFromItemTree::new_with_condition_and_visible(item_tree, condition, visible)
2902 }
2903 };
2904 vtable::VRc::new(menu)
2905}
2906
2907pub fn update_timers(instance: InstanceRef) {
2908 let ts = instance.description.original.timers.borrow();
2909 for (desc, offset) in ts.iter().zip(&instance.description.timers) {
2910 let timer = offset.apply(instance.as_ref());
2911 let running =
2912 eval::load_property(instance, &desc.running.element(), desc.running.name()).unwrap();
2913 if matches!(running, Value::Bool(true)) {
2914 let millis: i64 =
2915 eval::load_property(instance, &desc.interval.element(), desc.interval.name())
2916 .unwrap()
2917 .try_into()
2918 .expect("interval must be a duration");
2919 if millis < 0 {
2920 timer.stop();
2921 continue;
2922 }
2923 let interval = core::time::Duration::from_millis(millis as _);
2924 if !timer.running() || interval != timer.interval() {
2925 let callback = desc.triggered.clone();
2926 let self_weak = instance.self_weak().get().unwrap().clone();
2927 timer.start(i_slint_core::timers::TimerMode::Repeated, interval, move || {
2928 if let Some(instance) = self_weak.upgrade() {
2929 generativity::make_guard!(guard);
2930 let c = instance.unerase(guard);
2931 let c = c.borrow_instance();
2932 let inst = eval::ComponentInstance::InstanceRef(c);
2933 eval::invoke_callback(&inst, &callback.element(), callback.name(), &[])
2934 .unwrap();
2935 }
2936 });
2937 }
2938 } else {
2939 timer.stop();
2940 }
2941 }
2942}
2943
2944pub fn restart_timer(element: ElementWeak, instance: InstanceRef) {
2945 let timers = instance.description.original.timers.borrow();
2946 if let Some((_, offset)) = timers
2947 .iter()
2948 .zip(&instance.description.timers)
2949 .find(|(desc, _)| Weak::ptr_eq(&desc.element, &element))
2950 {
2951 let timer = offset.apply(instance.as_ref());
2952 timer.restart();
2953 }
2954}