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::{BuiltinPrivateStruct, 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};
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 pub(crate) debug_handler: std::cell::RefCell<
509 Rc<dyn Fn(Option<&i_slint_compiler::diagnostics::SourceLocation>, &str)>,
510 >,
511}
512
513#[derive(Clone, derive_more::From)]
514pub(crate) enum PopupMenuDescription {
515 Rc(Rc<ErasedItemTreeDescription>),
516 Weak(Weak<ErasedItemTreeDescription>),
517}
518impl PopupMenuDescription {
519 pub fn unerase<'id>(&self, guard: generativity::Guard<'id>) -> Rc<ItemTreeDescription<'id>> {
520 match self {
521 PopupMenuDescription::Rc(rc) => rc.unerase(guard).clone(),
522 PopupMenuDescription::Weak(weak) => weak.upgrade().unwrap().unerase(guard).clone(),
523 }
524 }
525}
526
527fn internal_properties_to_public<'a>(
528 prop_iter: impl Iterator<Item = (&'a SmolStr, &'a PropertyDeclaration)> + 'a,
529) -> impl Iterator<
530 Item = (
531 SmolStr,
532 i_slint_compiler::langtype::Type,
533 i_slint_compiler::object_tree::PropertyVisibility,
534 ),
535> + 'a {
536 prop_iter.filter(|(_, v)| v.expose_in_public_api).map(|(s, v)| {
537 let name = v
538 .node
539 .as_ref()
540 .and_then(|n| {
541 n.child_node(parser::SyntaxKind::DeclaredIdentifier)
542 .and_then(|n| n.child_token(parser::SyntaxKind::Identifier))
543 })
544 .map(|n| n.to_smolstr())
545 .unwrap_or_else(|| s.to_smolstr());
546 (name, v.property_type.clone(), v.visibility)
547 })
548}
549
550#[derive(Default)]
551pub enum WindowOptions {
552 #[default]
553 CreateNewWindow,
554 UseExistingWindow(WindowAdapterRc),
555 Embed {
556 parent_item_tree: ItemTreeWeak,
557 parent_item_tree_index: u32,
558 },
559}
560
561impl ItemTreeDescription<'_> {
562 pub fn id(&self) -> &str {
564 self.original.id.as_str()
565 }
566
567 pub fn properties(
571 &self,
572 ) -> impl Iterator<
573 Item = (
574 SmolStr,
575 i_slint_compiler::langtype::Type,
576 i_slint_compiler::object_tree::PropertyVisibility,
577 ),
578 > + '_ {
579 internal_properties_to_public(self.public_properties.iter())
580 }
581
582 pub fn global_names(&self) -> impl Iterator<Item = SmolStr> + '_ {
584 self.compiled_globals
585 .as_ref()
586 .expect("Root component should have globals")
587 .compiled_globals
588 .iter()
589 .filter(|g| g.visible_in_public_api())
590 .flat_map(|g| g.names().into_iter())
591 }
592
593 pub fn global_properties(
594 &self,
595 name: &str,
596 ) -> Option<
597 impl Iterator<
598 Item = (
599 SmolStr,
600 i_slint_compiler::langtype::Type,
601 i_slint_compiler::object_tree::PropertyVisibility,
602 ),
603 > + '_,
604 > {
605 let g = self.compiled_globals.as_ref().expect("Root component should have globals");
606 g.exported_globals_by_name
607 .get(&crate::normalize_identifier(name))
608 .and_then(|global_idx| g.compiled_globals.get(*global_idx))
609 .map(|global| internal_properties_to_public(global.public_properties()))
610 }
611
612 pub fn create(
614 self: Rc<Self>,
615 options: WindowOptions,
616 ) -> Result<DynamicComponentVRc, PlatformError> {
617 i_slint_backend_selector::with_platform(|_b| {
618 Ok(())
620 })?;
621
622 let instance = instantiate(self, None, None, Some(&options), Default::default());
623 if let WindowOptions::UseExistingWindow(existing_adapter) = options {
624 WindowInner::from_pub(existing_adapter.window())
625 .set_component(&vtable::VRc::into_dyn(instance.clone()));
626 }
627 instance.run_setup_code();
628 Ok(instance)
629 }
630
631 pub fn set_property(
637 &self,
638 component: ItemTreeRefPin,
639 name: &str,
640 value: Value,
641 ) -> Result<(), crate::api::SetPropertyError> {
642 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
643 panic!("mismatch instance and vtable");
644 }
645 generativity::make_guard!(guard);
646 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
647 if let Some(alias) = self
648 .original
649 .root_element
650 .borrow()
651 .property_declarations
652 .get(name)
653 .and_then(|d| d.is_alias.as_ref())
654 {
655 eval::store_property(c, &alias.element(), alias.name(), value)
656 } else {
657 eval::store_property(c, &self.original.root_element, name, value)
658 }
659 }
660
661 pub fn set_binding(
666 &self,
667 component: ItemTreeRefPin,
668 name: &str,
669 binding: Box<dyn Fn() -> Value>,
670 ) -> Result<(), ()> {
671 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
672 return Err(());
673 }
674 let x = self.custom_properties.get(name).ok_or(())?;
675 unsafe {
676 x.prop
677 .set_binding(
678 Pin::new_unchecked(&*component.as_ptr().add(x.offset)),
679 binding,
680 i_slint_core::rtti::AnimatedBindingKind::NotAnimated,
681 )
682 .unwrap()
683 };
684 Ok(())
685 }
686
687 pub fn get_property(&self, component: ItemTreeRefPin, name: &str) -> Result<Value, ()> {
692 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
693 return Err(());
694 }
695 generativity::make_guard!(guard);
696 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
698 if let Some(alias) = self
699 .original
700 .root_element
701 .borrow()
702 .property_declarations
703 .get(name)
704 .and_then(|d| d.is_alias.as_ref())
705 {
706 eval::load_property(c, &alias.element(), alias.name())
707 } else {
708 eval::load_property(c, &self.original.root_element, name)
709 }
710 }
711
712 pub fn set_callback_handler(
717 &self,
718 component: Pin<ItemTreeRef>,
719 name: &str,
720 handler: CallbackHandler,
721 ) -> Result<(), ()> {
722 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
723 return Err(());
724 }
725 if let Some(alias) = self
726 .original
727 .root_element
728 .borrow()
729 .property_declarations
730 .get(name)
731 .and_then(|d| d.is_alias.as_ref())
732 {
733 generativity::make_guard!(guard);
734 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
736 let inst = eval::ComponentInstance::InstanceRef(c);
737 eval::set_callback_handler(&inst, &alias.element(), alias.name(), handler)?
738 } else {
739 let x = self.custom_callbacks.get(name).ok_or(())?;
740 let inst = unsafe { &*(component.as_ptr() as *const dynamic_type::Instance) };
741 let sig = x.apply(inst);
742 sig.set_handler(handler);
743 if let Some(tracker_offset) = self.callback_trackers.get(name) {
744 tracker_offset.apply_pin(unsafe { Pin::new_unchecked(inst) }).mark_dirty();
745 }
746 }
747 Ok(())
748 }
749
750 pub fn invoke(
755 &self,
756 component: ItemTreeRefPin,
757 name: &SmolStr,
758 args: &[Value],
759 ) -> Result<Value, ()> {
760 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
761 return Err(());
762 }
763 generativity::make_guard!(guard);
764 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
766 let borrow = self.original.root_element.borrow();
767 let decl = borrow.property_declarations.get(name).ok_or(())?;
768
769 let (elem, name) = if let Some(alias) = &decl.is_alias {
770 (alias.element(), alias.name())
771 } else {
772 (self.original.root_element.clone(), name)
773 };
774
775 let inst = eval::ComponentInstance::InstanceRef(c);
776
777 if matches!(&decl.property_type, Type::Function { .. }) {
778 eval::call_function(&inst, &elem, name, args.to_vec()).ok_or(())
779 } else {
780 eval::invoke_callback(&inst, &elem, name, args).ok_or(())
781 }
782 }
783
784 pub fn get_global(
786 &self,
787 component: ItemTreeRefPin,
788 global_name: &str,
789 ) -> Result<Pin<Rc<dyn crate::global_component::GlobalComponent>>, ()> {
790 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
791 return Err(());
792 }
793 generativity::make_guard!(guard);
794 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
796 let extra_data = c.description.extra_data_offset.apply(c.instance.get_ref());
797 let g = extra_data.globals.get().unwrap().get(global_name).clone();
798 g.ok_or(())
799 }
800
801 pub fn recursively_set_debug_handler(
802 &self,
803 handler: Rc<dyn Fn(Option<&i_slint_compiler::diagnostics::SourceLocation>, &str)>,
804 ) {
805 *self.debug_handler.borrow_mut() = handler.clone();
806
807 for r in &self.repeater {
808 generativity::make_guard!(guard);
809 r.unerase(guard).item_tree_to_repeat.recursively_set_debug_handler(handler.clone());
810 }
811 }
812}
813
814#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
815extern "C" fn visit_children_item(
816 component: ItemTreeRefPin,
817 index: isize,
818 order: TraversalOrder,
819 v: ItemVisitorRefMut,
820) -> VisitChildrenResult {
821 generativity::make_guard!(guard);
822 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
823 let comp_rc = instance_ref.self_weak().get().unwrap().upgrade().unwrap();
824 i_slint_core::item_tree::visit_item_tree(
825 instance_ref.instance,
826 &vtable::VRc::into_dyn(comp_rc),
827 get_item_tree(component).as_slice(),
828 index,
829 order,
830 v,
831 |_, order, visitor, index| {
832 if index as usize >= instance_ref.description.repeater.len() {
833 VisitChildrenResult::CONTINUE
835 } else {
836 generativity::make_guard!(guard);
837 let rep_in_comp = instance_ref.description.repeater[index as usize].unerase(guard);
838 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
839 repeater.visit(order, visitor)
840 }
841 },
842 )
843}
844
845pub(crate) struct ItemRTTI {
847 vtable: &'static ItemVTable,
848 type_info: dynamic_type::StaticTypeInfo,
849 pub(crate) properties: HashMap<&'static str, Box<dyn eval::ErasedPropertyInfo>>,
850 pub(crate) callbacks: HashMap<&'static str, Box<dyn eval::ErasedCallbackInfo>>,
851}
852
853fn rtti_for<T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>>()
854-> (&'static str, Rc<ItemRTTI>) {
855 let rtti = ItemRTTI {
856 vtable: T::static_vtable(),
857 type_info: dynamic_type::StaticTypeInfo::new::<T>(),
858 properties: T::properties()
859 .into_iter()
860 .map(|(k, v)| (k, Box::new(v) as Box<dyn eval::ErasedPropertyInfo>))
861 .collect(),
862 callbacks: T::callbacks()
863 .into_iter()
864 .map(|(k, v)| (k, Box::new(v) as Box<dyn eval::ErasedCallbackInfo>))
865 .collect(),
866 };
867 (T::name(), Rc::new(rtti))
868}
869
870pub async fn load(
874 source: String,
875 path: std::path::PathBuf,
876 mut compiler_config: CompilerConfiguration,
877) -> CompilationResult {
878 let is_native = compiler_config.style.as_deref() == Some("native");
880 if is_native {
881 #[cfg(target_arch = "wasm32")]
883 let target = web_sys::window()
884 .and_then(|window| window.navigator().platform().ok())
885 .map_or("wasm", |platform| {
886 let platform = platform.to_ascii_lowercase();
887 if platform.contains("mac")
888 || platform.contains("iphone")
889 || platform.contains("ipad")
890 {
891 "apple"
892 } else if platform.contains("android") {
893 "android"
894 } else if platform.contains("win") {
895 "windows"
896 } else if platform.contains("linux") {
897 "linux"
898 } else {
899 "wasm"
900 }
901 });
902 #[cfg(not(target_arch = "wasm32"))]
903 let target = "";
904 compiler_config.style = Some(
905 i_slint_common::get_native_style(i_slint_backend_selector::HAS_NATIVE_STYLE, target)
906 .to_string(),
907 );
908 }
909
910 let diag = BuildDiagnostics::default();
911 #[cfg(feature = "internal-highlight")]
912 let (path, mut diag, loader, raw_type_loader) =
913 i_slint_compiler::load_root_file_with_raw_type_loader(
914 &path,
915 &path,
916 source,
917 diag,
918 compiler_config,
919 )
920 .await;
921 #[cfg(not(feature = "internal-highlight"))]
922 let (path, mut diag, loader) =
923 i_slint_compiler::load_root_file(&path, &path, source, diag, compiler_config).await;
924 #[cfg(feature = "internal-file-watcher")]
925 let watch_paths = loader.all_files_to_watch().into_iter().collect();
926 if diag.has_errors() {
927 return CompilationResult {
928 components: HashMap::new(),
929 diagnostics: diag.into_iter().collect(),
930 #[cfg(feature = "internal-file-watcher")]
931 watch_paths,
932 #[cfg(feature = "internal")]
933 structs_and_enums: Vec::new(),
934 #[cfg(feature = "internal")]
935 named_exports: Vec::new(),
936 };
937 }
938
939 #[cfg(feature = "internal-highlight")]
940 let loader = Rc::new(loader);
941 #[cfg(feature = "internal-highlight")]
942 let raw_type_loader = raw_type_loader.map(Rc::new);
943
944 let doc = loader.get_document(&path).unwrap();
945
946 let compiled_globals = Rc::new(CompiledGlobalCollection::compile(doc));
947 let mut components = HashMap::new();
948
949 let popup_menu_description = if let Some(popup_menu_impl) = &doc.popup_menu_impl {
950 PopupMenuDescription::Rc(Rc::new_cyclic(|weak| {
951 generativity::make_guard!(guard);
952 ErasedItemTreeDescription::from(generate_item_tree(
953 popup_menu_impl,
954 Some(compiled_globals.clone()),
955 PopupMenuDescription::Weak(weak.clone()),
956 true,
957 guard,
958 ))
959 }))
960 } else {
961 PopupMenuDescription::Weak(Default::default())
962 };
963
964 for c in doc.exported_roots() {
965 generativity::make_guard!(guard);
966 #[allow(unused_mut)]
967 let mut it = generate_item_tree(
968 &c,
969 Some(compiled_globals.clone()),
970 popup_menu_description.clone(),
971 false,
972 guard,
973 );
974 #[cfg(feature = "internal-highlight")]
975 {
976 let _ = it.type_loader.set(loader.clone());
977 let _ = it.raw_type_loader.set(raw_type_loader.clone());
978 }
979 components.insert(c.id.to_string(), ComponentDefinition { inner: it.into() });
980 }
981
982 if components.is_empty() {
983 diag.push_error_with_span("No component found".into(), Default::default());
984 };
985
986 #[cfg(feature = "internal")]
987 let structs_and_enums = doc.used_types.borrow().structs_and_enums.clone();
988
989 #[cfg(feature = "internal")]
990 let named_exports = doc
991 .exports
992 .iter()
993 .filter_map(|export| match &export.1 {
994 Either::Left(component) if !component.is_global() => {
995 Some((&export.0.name, &component.id))
996 }
997 Either::Right(ty) => match &ty {
998 Type::Struct(s) if s.node().is_some() => {
999 if let StructName::User { name, .. } = &s.name {
1000 Some((&export.0.name, name))
1001 } else {
1002 None
1003 }
1004 }
1005 Type::Enumeration(en) => Some((&export.0.name, &en.name)),
1006 _ => None,
1007 },
1008 _ => None,
1009 })
1010 .filter(|(export_name, type_name)| *export_name != *type_name)
1011 .map(|(export_name, type_name)| (type_name.to_string(), export_name.to_string()))
1012 .collect::<Vec<_>>();
1013
1014 CompilationResult {
1015 diagnostics: diag.into_iter().collect(),
1016 components,
1017 #[cfg(feature = "internal-file-watcher")]
1018 watch_paths,
1019 #[cfg(feature = "internal")]
1020 structs_and_enums,
1021 #[cfg(feature = "internal")]
1022 named_exports,
1023 }
1024}
1025
1026fn generate_rtti() -> HashMap<&'static str, Rc<ItemRTTI>> {
1027 let mut rtti = HashMap::new();
1028 use i_slint_core::items::*;
1029 rtti.extend(
1030 [
1031 rtti_for::<ComponentContainer>(),
1032 rtti_for::<Empty>(),
1033 rtti_for::<ImageItem>(),
1034 rtti_for::<ClippedImage>(),
1035 rtti_for::<ComplexText>(),
1036 rtti_for::<StyledTextItem>(),
1037 rtti_for::<SimpleText>(),
1038 rtti_for::<Rectangle>(),
1039 rtti_for::<BasicBorderRectangle>(),
1040 rtti_for::<BorderRectangle>(),
1041 rtti_for::<TouchArea>(),
1042 rtti_for::<TooltipArea>(),
1043 rtti_for::<FocusScope>(),
1044 rtti_for::<KeyBinding>(),
1045 rtti_for::<SwipeGestureHandler>(),
1046 rtti_for::<ScaleRotateGestureHandler>(),
1047 rtti_for::<Path>(),
1048 rtti_for::<Flickable>(),
1049 rtti_for::<WindowItem>(),
1050 rtti_for::<TextInput>(),
1051 rtti_for::<Clip>(),
1052 rtti_for::<BoxShadow>(),
1053 rtti_for::<Transform>(),
1054 rtti_for::<Opacity>(),
1055 rtti_for::<Layer>(),
1056 rtti_for::<DragArea>(),
1057 rtti_for::<DropArea>(),
1058 rtti_for::<ContextMenu>(),
1059 rtti_for::<MenuItem>(),
1060 rtti_for::<SystemTrayIcon>(),
1061 ]
1062 .iter()
1063 .cloned(),
1064 );
1065
1066 trait NativeHelper {
1067 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>);
1068 }
1069 impl NativeHelper for () {
1070 fn push(_rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {}
1071 }
1072 impl<
1073 T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>,
1074 Next: NativeHelper,
1075 > NativeHelper for (T, Next)
1076 {
1077 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {
1078 let info = rtti_for::<T>();
1079 rtti.insert(info.0, info.1);
1080 Next::push(rtti);
1081 }
1082 }
1083 i_slint_backend_selector::NativeWidgets::push(&mut rtti);
1084
1085 rtti
1086}
1087
1088pub(crate) fn generate_item_tree<'id>(
1089 component: &Rc<object_tree::Component>,
1090 compiled_globals: Option<Rc<CompiledGlobalCollection>>,
1091 popup_menu_description: PopupMenuDescription,
1092 is_popup_menu_impl: bool,
1093 guard: generativity::Guard<'id>,
1094) -> Rc<ItemTreeDescription<'id>> {
1095 thread_local! {
1098 static RTTI: Lazy<HashMap<&'static str, Rc<ItemRTTI>>> = Lazy::new(generate_rtti);
1099 }
1100
1101 struct TreeBuilder<'id> {
1102 tree_array: Vec<ItemTreeNode>,
1103 item_array:
1104 Vec<vtable::VOffset<crate::dynamic_type::Instance<'id>, ItemVTable, vtable::AllowPin>>,
1105 original_elements: Vec<ElementRc>,
1106 items_types: HashMap<SmolStr, ItemWithinItemTree>,
1107 type_builder: dynamic_type::TypeBuilder<'id>,
1108 repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
1109 repeater_names: HashMap<SmolStr, usize>,
1110 change_callbacks: Vec<(NamedReference, Expression)>,
1111 popup_menu_description: PopupMenuDescription,
1112 }
1113 impl generator::ItemTreeBuilder for TreeBuilder<'_> {
1114 type SubComponentState = ();
1115
1116 fn push_repeated_item(
1117 &mut self,
1118 item_rc: &ElementRc,
1119 repeater_count: u32,
1120 parent_index: u32,
1121 _component_state: &Self::SubComponentState,
1122 ) {
1123 self.tree_array.push(ItemTreeNode::DynamicTree { index: repeater_count, parent_index });
1124 self.original_elements.push(item_rc.clone());
1125 let item = item_rc.borrow();
1126 let base_component = item.base_type.as_component();
1127 self.repeater_names.insert(item.id.clone(), self.repeater.len());
1128 generativity::make_guard!(guard);
1129 let repeated_element_info = item.repeated.as_ref().unwrap();
1130 self.repeater.push(
1131 RepeaterWithinItemTree {
1132 item_tree_to_repeat: generate_item_tree(
1133 base_component,
1134 None,
1135 self.popup_menu_description.clone(),
1136 false,
1137 guard,
1138 ),
1139 offset: self.type_builder.add_field_type::<Repeater<ErasedItemTreeBox>>(),
1140 model: repeated_element_info.model.clone(),
1141 is_conditional: repeated_element_info.is_conditional_element,
1142 }
1143 .into(),
1144 );
1145 }
1146
1147 fn push_native_item(
1148 &mut self,
1149 rc_item: &ElementRc,
1150 child_offset: u32,
1151 parent_index: u32,
1152 _component_state: &Self::SubComponentState,
1153 ) {
1154 let item = rc_item.borrow();
1155 let rt = RTTI.with(|rtti| {
1156 rtti.get(&*item.base_type.as_native().class_name)
1157 .unwrap_or_else(|| {
1158 panic!(
1159 "Native type not registered: {}",
1160 item.base_type.as_native().class_name
1161 )
1162 })
1163 .clone()
1164 });
1165
1166 let offset = self.type_builder.add_field(rt.type_info);
1167
1168 self.tree_array.push(ItemTreeNode::Item {
1169 is_accessible: !item.accessibility_props.0.is_empty(),
1170 children_index: child_offset,
1171 children_count: item.children.len() as u32,
1172 parent_index,
1173 item_array_index: self.item_array.len() as u32,
1174 });
1175 self.item_array.push(unsafe { vtable::VOffset::from_raw(rt.vtable, offset) });
1176 self.original_elements.push(rc_item.clone());
1177 debug_assert_eq!(self.original_elements.len(), self.tree_array.len());
1178 self.items_types.insert(
1179 item.id.clone(),
1180 ItemWithinItemTree { offset, rtti: rt, elem: rc_item.clone() },
1181 );
1182 for (prop, expr) in &item.change_callbacks {
1183 self.change_callbacks.push((
1184 NamedReference::new(rc_item, prop.clone()),
1185 Expression::CodeBlock(expr.borrow().clone()),
1186 ));
1187 }
1188 }
1189
1190 fn enter_component(
1191 &mut self,
1192 _item: &ElementRc,
1193 _sub_component: &Rc<object_tree::Component>,
1194 _children_offset: u32,
1195 _component_state: &Self::SubComponentState,
1196 ) -> Self::SubComponentState {
1197 }
1199
1200 fn enter_component_children(
1201 &mut self,
1202 _item: &ElementRc,
1203 _repeater_count: u32,
1204 _component_state: &Self::SubComponentState,
1205 _sub_component_state: &Self::SubComponentState,
1206 ) {
1207 todo!()
1208 }
1209 }
1210
1211 let mut builder = TreeBuilder {
1212 tree_array: Vec::new(),
1213 item_array: Vec::new(),
1214 original_elements: Vec::new(),
1215 items_types: HashMap::new(),
1216 type_builder: dynamic_type::TypeBuilder::new(guard),
1217 repeater: Vec::new(),
1218 repeater_names: HashMap::new(),
1219 change_callbacks: Vec::new(),
1220 popup_menu_description,
1221 };
1222
1223 if !component.is_global() {
1224 generator::build_item_tree(component, &(), &mut builder);
1225 } else {
1226 for (prop, expr) in component.root_element.borrow().change_callbacks.iter() {
1227 builder.change_callbacks.push((
1228 NamedReference::new(&component.root_element, prop.clone()),
1229 Expression::CodeBlock(expr.borrow().clone()),
1230 ));
1231 }
1232 }
1233
1234 let mut custom_properties = HashMap::new();
1235 let mut custom_callbacks = HashMap::new();
1236 let mut callback_trackers = HashMap::new();
1237 fn property_info<T>() -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1238 where
1239 T: PartialEq + Clone + Default + std::convert::TryInto<Value> + 'static,
1240 Value: std::convert::TryInto<T>,
1241 {
1242 (
1244 Box::new(unsafe {
1245 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0)
1246 }),
1247 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1248 )
1249 }
1250 fn animated_property_info<T>()
1251 -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1252 where
1253 T: Clone + Default + InterpolatedPropertyValue + std::convert::TryInto<Value> + 'static,
1254 Value: std::convert::TryInto<T>,
1255 {
1256 (
1258 Box::new(unsafe {
1259 rtti::MaybeAnimatedPropertyInfoWrapper(
1260 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0),
1261 )
1262 }),
1263 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1264 )
1265 }
1266
1267 fn property_info_for_type(
1268 ty: &Type,
1269 name: &str,
1270 ) -> Option<(Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)> {
1271 Some(match ty {
1272 Type::Float32 => animated_property_info::<f32>(),
1273 Type::Int32 => animated_property_info::<i32>(),
1274 Type::String => property_info::<SharedString>(),
1275 Type::Color => animated_property_info::<Color>(),
1276 Type::Brush => animated_property_info::<Brush>(),
1277 Type::Duration => animated_property_info::<i64>(),
1278 Type::Angle => animated_property_info::<f32>(),
1279 Type::PhysicalLength => animated_property_info::<f32>(),
1280 Type::LogicalLength => animated_property_info::<f32>(),
1281 Type::Rem => animated_property_info::<f32>(),
1282 Type::Image => property_info::<i_slint_core::graphics::Image>(),
1283 Type::Bool => property_info::<bool>(),
1284 Type::ComponentFactory => property_info::<ComponentFactory>(),
1285 Type::Struct(s)
1286 if matches!(
1287 s.name,
1288 StructName::BuiltinPrivate(BuiltinPrivateStruct::StateInfo)
1289 ) =>
1290 {
1291 property_info::<i_slint_core::properties::StateInfo>()
1292 }
1293 Type::Struct(_) => property_info::<Value>(),
1294 Type::Array(_) => property_info::<Value>(),
1295 Type::Easing => property_info::<i_slint_core::animations::EasingCurve>(),
1296 Type::Percent => animated_property_info::<f32>(),
1297 Type::Enumeration(e) => {
1298 macro_rules! match_enum_type {
1299 ($( $(#[$enum_doc:meta])* $vis:vis enum $Name:ident { $($body:tt)* })*) => {
1300 match e.name.as_str() {
1301 $(
1302 stringify!($Name) => property_info::<i_slint_core::items::$Name>(),
1303 )*
1304 x => unreachable!("Unknown non-builtin enum {x}"),
1305 }
1306 }
1307 }
1308
1309 if e.node.is_some() {
1310 property_info::<Value>()
1311 } else {
1312 i_slint_common::for_each_enums!(match_enum_type)
1313 }
1314 }
1315 Type::Keys => property_info::<Keys>(),
1316 Type::DataTransfer => property_info::<DataTransfer>(),
1317 Type::LayoutCache => property_info::<SharedVector<f32>>(),
1318 Type::ArrayOfU16 => property_info::<SharedVector<u16>>(),
1319 Type::Function { .. } | Type::Callback { .. } => return None,
1320 Type::StyledText => property_info::<StyledText>(),
1321 Type::Invalid
1323 | Type::Void
1324 | Type::InferredProperty
1325 | Type::InferredCallback
1326 | Type::Model
1327 | Type::PathData
1328 | Type::UnitProduct(_)
1329 | Type::ElementReference => panic!("bad type {ty:?} for property {name}"),
1330 })
1331 }
1332
1333 for (name, decl) in &component.root_element.borrow().property_declarations {
1334 if decl.is_alias.is_some() {
1335 continue;
1336 }
1337 if matches!(&decl.property_type, Type::Callback { .. }) {
1338 custom_callbacks
1339 .insert(name.clone(), builder.type_builder.add_field_type::<Callback>());
1340 if decl.expose_in_public_api {
1341 callback_trackers
1342 .insert(name.clone(), builder.type_builder.add_field_type::<Property<()>>());
1343 }
1344 continue;
1345 }
1346 let Some((prop, type_info)) = property_info_for_type(&decl.property_type, name) else {
1347 continue;
1348 };
1349 custom_properties.insert(
1350 name.clone(),
1351 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1352 );
1353 }
1354 if let Some(parent_element) = component.parent_element()
1355 && let Some(r) = &parent_element.borrow().repeated
1356 && !r.is_conditional_element
1357 {
1358 let (prop, type_info) = property_info::<u32>();
1359 custom_properties.insert(
1360 SPECIAL_PROPERTY_INDEX.into(),
1361 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1362 );
1363
1364 let model_ty = Expression::RepeaterModelReference {
1365 element: component.parent_element.borrow().clone(),
1366 }
1367 .ty();
1368 let (prop, type_info) =
1369 property_info_for_type(&model_ty, SPECIAL_PROPERTY_MODEL_DATA).unwrap();
1370 custom_properties.insert(
1371 SPECIAL_PROPERTY_MODEL_DATA.into(),
1372 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1373 );
1374 }
1375
1376 let parent_item_tree_offset = if component.parent_element().is_some() || is_popup_menu_impl {
1377 Some(builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>())
1378 } else {
1379 None
1380 };
1381
1382 let root_offset = builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>();
1383 let extra_data_offset = builder.type_builder.add_field_type::<ComponentExtraData>();
1384
1385 let change_trackers = (!builder.change_callbacks.is_empty()).then(|| {
1386 (
1387 builder.type_builder.add_field_type::<OnceCell<Vec<ChangeTracker>>>(),
1388 builder.change_callbacks,
1389 )
1390 });
1391 let timers = component
1392 .timers
1393 .borrow()
1394 .iter()
1395 .map(|_| builder.type_builder.add_field_type::<Timer>())
1396 .collect();
1397
1398 let public_properties = if component.parent_element().is_none() {
1400 component.root_element.borrow().property_declarations.clone()
1401 } else {
1402 Default::default()
1403 };
1404
1405 let t = ItemTreeVTable {
1406 visit_children_item,
1407 layout_info,
1408 ensure_instantiated,
1409 get_item_ref,
1410 get_item_tree,
1411 get_subtree_range,
1412 get_subtree,
1413 parent_node,
1414 embed_component,
1415 subtree_index,
1416 item_geometry,
1417 accessible_role,
1418 accessible_string_property,
1419 accessibility_action,
1420 supported_accessibility_actions,
1421 item_element_infos,
1422 window_adapter,
1423 drop_in_place,
1424 dealloc,
1425 };
1426 let t = ItemTreeDescription {
1427 ct: t,
1428 dynamic_type: builder.type_builder.build(),
1429 item_tree: builder.tree_array,
1430 item_array: builder.item_array,
1431 items: builder.items_types,
1432 custom_properties,
1433 custom_callbacks,
1434 callback_trackers,
1435 original: component.clone(),
1436 original_elements: builder.original_elements,
1437 repeater: builder.repeater,
1438 repeater_names: builder.repeater_names,
1439 parent_item_tree_offset,
1440 root_offset,
1441 extra_data_offset,
1442 public_properties,
1443 compiled_globals,
1444 change_trackers,
1445 timers,
1446 popup_ids: std::cell::RefCell::new(HashMap::new()),
1447 popup_menu_description: builder.popup_menu_description,
1448 #[cfg(feature = "internal-highlight")]
1449 type_loader: std::cell::OnceCell::new(),
1450 #[cfg(feature = "internal-highlight")]
1451 raw_type_loader: std::cell::OnceCell::new(),
1452 debug_handler: std::cell::RefCell::new(Rc::new(|_, text| {
1453 i_slint_core::debug_log!("{text}")
1454 })),
1455 };
1456
1457 Rc::new(t)
1458}
1459
1460pub fn animation_for_property(
1461 component: InstanceRef,
1462 animation: &Option<i_slint_compiler::object_tree::PropertyAnimation>,
1463) -> AnimatedBindingKind {
1464 match animation {
1465 Some(i_slint_compiler::object_tree::PropertyAnimation::Static(anim_elem)) => {
1466 AnimatedBindingKind::Animation(Box::new({
1467 let component_ptr = component.as_ptr();
1468 let vtable = NonNull::from(&component.description.ct).cast();
1469 let anim_elem = Rc::clone(anim_elem);
1470 move || -> PropertyAnimation {
1471 generativity::make_guard!(guard);
1472 let component = unsafe {
1473 InstanceRef::from_pin_ref(
1474 Pin::new_unchecked(vtable::VRef::from_raw(
1475 vtable,
1476 NonNull::new_unchecked(component_ptr as *mut u8),
1477 )),
1478 guard,
1479 )
1480 };
1481
1482 eval::new_struct_with_bindings(
1483 &anim_elem.borrow().bindings,
1484 &mut eval::EvalLocalContext::from_component_instance(component),
1485 )
1486 }
1487 }))
1488 }
1489 Some(i_slint_compiler::object_tree::PropertyAnimation::Transition {
1490 animations,
1491 state_ref,
1492 }) => {
1493 let component_ptr = component.as_ptr();
1494 let vtable = NonNull::from(&component.description.ct).cast();
1495 let animations = animations.clone();
1496 let state_ref = state_ref.clone();
1497 AnimatedBindingKind::Transition(Box::new(
1498 move || -> (PropertyAnimation, i_slint_core::animations::Instant) {
1499 generativity::make_guard!(guard);
1500 let component = unsafe {
1501 InstanceRef::from_pin_ref(
1502 Pin::new_unchecked(vtable::VRef::from_raw(
1503 vtable,
1504 NonNull::new_unchecked(component_ptr as *mut u8),
1505 )),
1506 guard,
1507 )
1508 };
1509
1510 let mut context = eval::EvalLocalContext::from_component_instance(component);
1511 let state = eval::eval_expression(&state_ref, &mut context);
1512 let state_info: i_slint_core::properties::StateInfo = state.try_into().unwrap();
1513 for a in &animations {
1514 let is_previous_state = a.state_id == state_info.previous_state;
1515 let is_current_state = a.state_id == state_info.current_state;
1516 match (a.direction, is_previous_state, is_current_state) {
1517 (TransitionDirection::In, false, true)
1518 | (TransitionDirection::Out, true, false)
1519 | (TransitionDirection::InOut, false, true)
1520 | (TransitionDirection::InOut, true, false) => {
1521 return (
1522 eval::new_struct_with_bindings(
1523 &a.animation.borrow().bindings,
1524 &mut context,
1525 ),
1526 state_info.change_time,
1527 );
1528 }
1529 _ => {}
1530 }
1531 }
1532 Default::default()
1533 },
1534 ))
1535 }
1536 None => AnimatedBindingKind::NotAnimated,
1537 }
1538}
1539
1540fn make_callback_eval_closure(
1541 expr: Expression,
1542 self_weak: ErasedItemTreeBoxWeak,
1543) -> impl Fn(&[Value]) -> Value {
1544 move |args| {
1545 let self_rc = self_weak.upgrade().unwrap();
1546 generativity::make_guard!(guard);
1547 let self_ = self_rc.unerase(guard);
1548 let instance_ref = self_.borrow_instance();
1549 let mut local_context =
1550 eval::EvalLocalContext::from_function_arguments(instance_ref, args.to_vec());
1551 eval::eval_expression(&expr, &mut local_context)
1552 }
1553}
1554
1555fn make_binding_eval_closure(
1556 expr: Expression,
1557 self_weak: ErasedItemTreeBoxWeak,
1558) -> impl Fn() -> Value {
1559 move || {
1560 let self_rc = self_weak.upgrade().unwrap();
1561 generativity::make_guard!(guard);
1562 let self_ = self_rc.unerase(guard);
1563 let instance_ref = self_.borrow_instance();
1564 eval::eval_expression(
1565 &expr,
1566 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1567 )
1568 }
1569}
1570
1571pub fn instantiate(
1572 description: Rc<ItemTreeDescription>,
1573 parent_ctx: Option<ErasedItemTreeBoxWeak>,
1574 root: Option<ErasedItemTreeBoxWeak>,
1575 window_options: Option<&WindowOptions>,
1576 globals: crate::global_component::GlobalStorage,
1577) -> DynamicComponentVRc {
1578 let instance = description.dynamic_type.clone().create_instance();
1579
1580 let component_box = ItemTreeBox { instance, description: description.clone() };
1581
1582 let self_rc = vtable::VRc::new(ErasedItemTreeBox::from(component_box));
1583 let self_weak = vtable::VRc::downgrade(&self_rc);
1584
1585 generativity::make_guard!(guard);
1586 let comp = self_rc.unerase(guard);
1587 let instance_ref = comp.borrow_instance();
1588 instance_ref.self_weak().set(self_weak.clone()).ok();
1589 let description = comp.description();
1590
1591 if let Some(WindowOptions::UseExistingWindow(existing_adapter)) = &window_options
1592 && let Err((a, b)) = globals.window_adapter().unwrap().try_insert(existing_adapter.clone())
1593 {
1594 assert!(Rc::ptr_eq(a, &b), "window not the same as parent window");
1595 }
1596
1597 if let Some(parent) = parent_ctx {
1598 description
1599 .parent_item_tree_offset
1600 .unwrap()
1601 .apply(instance_ref.as_ref())
1602 .set(parent)
1603 .ok()
1604 .unwrap();
1605 } else if let Some(g) = description.compiled_globals.as_ref() {
1606 for g in g.compiled_globals.iter() {
1607 crate::global_component::instantiate(g, &globals, self_weak.clone());
1608 }
1609 }
1610 let extra_data = description.extra_data_offset.apply(instance_ref.as_ref());
1611 extra_data.globals.set(globals).ok().unwrap();
1612 if let Some(WindowOptions::Embed { parent_item_tree, parent_item_tree_index }) = window_options
1613 {
1614 vtable::VRc::borrow_pin(&self_rc)
1615 .as_ref()
1616 .embed_component(parent_item_tree, *parent_item_tree_index);
1617 description.root_offset.apply(instance_ref.as_ref()).set(self_weak.clone()).ok().unwrap();
1618 } else {
1619 generativity::make_guard!(guard);
1620 let root = root
1621 .or_else(|| {
1622 instance_ref.parent_instance(guard).map(|parent| parent.root_weak().clone())
1623 })
1624 .unwrap_or_else(|| self_weak.clone());
1625 description.root_offset.apply(instance_ref.as_ref()).set(root).ok().unwrap();
1626 }
1627
1628 if !description.original.is_global() {
1629 let maybe_window_adapter =
1630 if let Some(WindowOptions::UseExistingWindow(adapter)) = window_options.as_ref() {
1631 Some(adapter.clone())
1632 } else {
1633 instance_ref.maybe_window_adapter()
1634 };
1635
1636 let component_rc = vtable::VRc::into_dyn(self_rc.clone());
1637 i_slint_core::item_tree::register_item_tree(&component_rc, maybe_window_adapter);
1638 }
1639
1640 for (prop_name, decl) in &description.original.root_element.borrow().property_declarations {
1642 if !matches!(
1643 decl.property_type,
1644 Type::Struct { .. } | Type::Array(_) | Type::Enumeration(_)
1645 ) || decl.is_alias.is_some()
1646 {
1647 continue;
1648 }
1649 if let Some(b) = description.original.root_element.borrow().bindings.get(prop_name)
1650 && b.borrow().two_way_bindings.is_empty()
1651 {
1652 continue;
1653 }
1654 let p = description.custom_properties.get(prop_name).unwrap();
1655 unsafe {
1656 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(p.offset));
1657 p.prop.set(item, eval::default_value_for_type(&decl.property_type), None).unwrap();
1658 }
1659 }
1660
1661 #[cfg(slint_debug_property)]
1662 {
1663 let component_id = description.original.id.as_str();
1664
1665 for (prop_name, prop_info) in &description.custom_properties {
1667 let name = format!("{}.{}", component_id, prop_name);
1668 unsafe {
1669 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(prop_info.offset));
1670 prop_info.prop.set_debug_name(item, name);
1671 }
1672 }
1673
1674 for (item_name, item_within_component) in &description.items {
1676 let item = unsafe { item_within_component.item_from_item_tree(instance_ref.as_ptr()) };
1677 for (prop_name, prop_rtti) in &item_within_component.rtti.properties {
1678 let name = format!("{}::{}.{}", component_id, item_name, prop_name);
1679 prop_rtti.set_debug_name(item, name);
1680 }
1681 }
1682 }
1683
1684 generator::handle_property_bindings_init(
1685 &description.original,
1686 |elem, prop_name, binding| unsafe {
1687 let is_root = Rc::ptr_eq(
1688 elem,
1689 &elem.borrow().enclosing_component.upgrade().unwrap().root_element,
1690 );
1691 let elem = elem.borrow();
1692 let is_const = binding.analysis.as_ref().is_some_and(|a| a.is_const);
1693
1694 let property_type = elem.lookup_property(prop_name).property_type;
1695 if let Type::Function { .. } = property_type {
1696 } else if let Type::Callback { .. } = property_type {
1698 if !matches!(binding.expression, Expression::Invalid) {
1699 let expr = binding.expression.clone();
1700 let description = description.clone();
1701 if let Some(callback_offset) =
1702 description.custom_callbacks.get(prop_name).filter(|_| is_root)
1703 {
1704 let callback = callback_offset.apply(instance_ref.as_ref());
1705 callback.set_handler(make_callback_eval_closure(expr, self_weak.clone()));
1706 } else {
1707 let item_within_component = &description.items[&elem.id];
1708 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1709 if let Some(callback) =
1710 item_within_component.rtti.callbacks.get(prop_name.as_str())
1711 {
1712 callback.set_handler(
1713 item,
1714 Box::new(make_callback_eval_closure(expr, self_weak.clone())),
1715 );
1716 } else {
1717 panic!("unknown callback {prop_name}")
1718 }
1719 }
1720 }
1721 } else if let Some(PropertiesWithinComponent { offset, prop: prop_info, .. }) =
1722 description.custom_properties.get(prop_name).filter(|_| is_root)
1723 {
1724 let is_state_info = matches!(&property_type, Type::Struct (s) if matches!(s.name, StructName::BuiltinPrivate(BuiltinPrivateStruct::StateInfo)));
1725 if is_state_info {
1726 let prop = Pin::new_unchecked(
1727 &*(instance_ref.as_ptr().add(*offset)
1728 as *const Property<i_slint_core::properties::StateInfo>),
1729 );
1730 let e = binding.expression.clone();
1731 let state_binding = make_binding_eval_closure(e, self_weak.clone());
1732 i_slint_core::properties::set_state_binding(prop, move || {
1733 state_binding().try_into().unwrap()
1734 });
1735 return;
1736 }
1737
1738 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1739 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset));
1740
1741 if !matches!(binding.expression, Expression::Invalid) {
1742 if is_const {
1743 let v = eval::eval_expression(
1744 &binding.expression,
1745 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1746 );
1747 prop_info.set(item, v, None).unwrap();
1748 } else {
1749 let e = binding.expression.clone();
1750 prop_info
1751 .set_binding(
1752 item,
1753 Box::new(make_binding_eval_closure(e, self_weak.clone())),
1754 maybe_animation,
1755 )
1756 .unwrap();
1757 }
1758 }
1759 for twb in &binding.two_way_bindings {
1760 match twb {
1761 TwoWayBinding::Property { property, field_access }
1762 if field_access.is_empty()
1763 && !matches!(
1764 &property_type,
1765 Type::Struct(..) | Type::Array(..)
1766 ) =>
1767 {
1768 prop_info.link_two_ways(item, get_property_ptr(property, instance_ref));
1771 }
1772 TwoWayBinding::Property { property, field_access } => {
1773 let (common, map) =
1774 prepare_for_two_way_binding(instance_ref, property, field_access);
1775 prop_info.link_two_way_with_map(item, common, map);
1776 }
1777 TwoWayBinding::ModelData { repeated_element, field_access } => {
1778 let (getter, setter) = prepare_model_two_way_binding(
1779 instance_ref,
1780 repeated_element,
1781 field_access,
1782 );
1783 prop_info.link_two_way_to_model_data(item, getter, setter);
1784 }
1785 }
1786 }
1787 } else {
1788 let item_within_component = &description.items[&elem.id];
1789 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1790 if let Some(prop_rtti) =
1791 item_within_component.rtti.properties.get(prop_name.as_str())
1792 {
1793 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1794
1795 for twb in &binding.two_way_bindings {
1796 match twb {
1797 TwoWayBinding::Property { property, field_access }
1798 if field_access.is_empty()
1799 && !matches!(
1800 &property_type,
1801 Type::Struct(..) | Type::Array(..)
1802 ) =>
1803 {
1804 prop_rtti
1807 .link_two_ways(item, get_property_ptr(property, instance_ref));
1808 }
1809 TwoWayBinding::Property { property, field_access } => {
1810 let (common, map) = prepare_for_two_way_binding(
1811 instance_ref,
1812 property,
1813 field_access,
1814 );
1815 prop_rtti.link_two_way_with_map(item, common, map);
1816 }
1817 TwoWayBinding::ModelData { repeated_element, field_access } => {
1818 let (getter, setter) = prepare_model_two_way_binding(
1819 instance_ref,
1820 repeated_element,
1821 field_access,
1822 );
1823 prop_rtti.link_two_way_to_model_data(item, getter, setter);
1824 }
1825 }
1826 }
1827 if !matches!(binding.expression, Expression::Invalid) {
1828 if is_const {
1829 prop_rtti
1830 .set(
1831 item,
1832 eval::eval_expression(
1833 &binding.expression,
1834 &mut eval::EvalLocalContext::from_component_instance(
1835 instance_ref,
1836 ),
1837 ),
1838 maybe_animation.as_animation(),
1839 )
1840 .unwrap();
1841 } else {
1842 let e = binding.expression.clone();
1843 prop_rtti.set_binding(
1844 item,
1845 Box::new(make_binding_eval_closure(e, self_weak.clone())),
1846 maybe_animation,
1847 );
1848 }
1849 }
1850 } else {
1851 panic!("unknown property {} in {}", prop_name, elem.id);
1852 }
1853 }
1854 },
1855 );
1856
1857 for rep_in_comp in &description.repeater {
1858 generativity::make_guard!(guard);
1859 let rep_in_comp = rep_in_comp.unerase(guard);
1860
1861 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
1862 let expr = rep_in_comp.model.clone();
1863 let model_binding_closure = make_binding_eval_closure(expr, self_weak.clone());
1864 if rep_in_comp.is_conditional {
1865 let bool_model = Rc::new(crate::value_model::BoolModel::default());
1866 repeater.set_model_binding(move || {
1867 let v = model_binding_closure();
1868 bool_model.set_value(v.try_into().expect("condition model is bool"));
1869 ModelRc::from(bool_model.clone())
1870 });
1871 } else {
1872 repeater.set_model_binding(move || {
1873 let m = model_binding_closure();
1874 if let Value::Model(m) = m {
1875 m
1876 } else {
1877 ModelRc::new(crate::value_model::ValueModel::new(m))
1878 }
1879 });
1880 }
1881 }
1882 self_rc
1883}
1884
1885fn prepare_for_two_way_binding(
1886 instance_ref: InstanceRef,
1887 property: &NamedReference,
1888 field_access: &[SmolStr],
1889) -> (Pin<Rc<Property<Value>>>, Option<Rc<dyn rtti::TwoWayBindingMapping<Value>>>) {
1890 let element = property.element();
1891 let name = property.name().as_str();
1892
1893 generativity::make_guard!(guard);
1894 let enclosing_component = eval::enclosing_component_instance_for_element(
1895 &element,
1896 &eval::ComponentInstance::InstanceRef(instance_ref),
1897 guard,
1898 );
1899 let map: Option<Rc<dyn rtti::TwoWayBindingMapping<Value>>> = if field_access.is_empty() {
1900 None
1901 } else {
1902 struct FieldAccess(Vec<SmolStr>);
1903 impl rtti::TwoWayBindingMapping<Value> for FieldAccess {
1904 fn map_to(&self, value: &Value) -> Value {
1905 walk_struct_field_path(value.clone(), &self.0).unwrap_or_default()
1906 }
1907 fn map_from(&self, root: &mut Value, from: &Value) {
1908 if let Some(leaf) = walk_struct_field_path_mut(root, &self.0) {
1909 *leaf = from.clone();
1910 }
1911 }
1912 }
1913 Some(Rc::new(FieldAccess(field_access.to_vec())))
1914 };
1915 let common = match enclosing_component {
1916 eval::ComponentInstance::InstanceRef(enclosing_component) => {
1917 let element = element.borrow();
1918 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1919 && let Some(x) = enclosing_component.description.custom_properties.get(name)
1920 {
1921 let item =
1922 unsafe { Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset)) };
1923 let common = x.prop.prepare_for_two_way_binding(item);
1924 return (common, map);
1925 }
1926 let item_info = enclosing_component
1927 .description
1928 .items
1929 .get(element.id.as_str())
1930 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, name));
1931 let prop_info = item_info
1932 .rtti
1933 .properties
1934 .get(name)
1935 .unwrap_or_else(|| panic!("Property {} not in {}", name, element.id));
1936 core::mem::drop(element);
1937 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1938 prop_info.prepare_for_two_way_binding(item)
1939 }
1940 eval::ComponentInstance::GlobalComponent(glob) => {
1941 glob.as_ref().prepare_for_two_way_binding(name).unwrap()
1942 }
1943 };
1944 (common, map)
1945}
1946
1947fn prepare_model_two_way_binding(
1951 instance_ref: InstanceRef,
1952 repeated_element: &i_slint_compiler::object_tree::ElementWeak,
1953 field_access: &[SmolStr],
1954) -> (Box<dyn Fn() -> Option<Value>>, Box<dyn Fn(&Value)>) {
1955 let self_weak = instance_ref.self_weak().get().unwrap().clone();
1956 let repeated_element = repeated_element.clone();
1957 let field_access: Vec<SmolStr> = field_access.to_vec();
1958
1959 let getter = {
1960 let self_weak = self_weak.clone();
1961 let repeated_element = repeated_element.clone();
1962 let field_access = field_access.clone();
1963 Box::new(move || -> Option<Value> {
1964 with_repeater_row(&self_weak, &repeated_element, |repeater, row| {
1965 walk_struct_field_path(repeater.model_row_data(row)?, &field_access)
1966 })
1967 })
1968 };
1969
1970 let setter = Box::new(move |new_value: &Value| {
1971 with_repeater_row(&self_weak, &repeated_element, |repeater, row| {
1972 let mut data = repeater.model_row_data(row)?;
1973 let leaf = walk_struct_field_path_mut(&mut data, &field_access)?;
1975 if &*leaf == new_value {
1976 return Some(());
1977 }
1978 *leaf = new_value.clone();
1979 repeater.model_set_row_data(row, data);
1980 Some(())
1981 });
1982 });
1983
1984 (getter, setter)
1985}
1986
1987fn with_repeater_row<R>(
1990 self_weak: &ErasedItemTreeBoxWeak,
1991 repeated_element: &i_slint_compiler::object_tree::ElementWeak,
1992 f: impl FnOnce(Pin<&Repeater<ErasedItemTreeBox>>, usize) -> Option<R>,
1993) -> Option<R> {
1994 let self_rc = self_weak.upgrade()?;
1995 generativity::make_guard!(guard);
1996 let s = self_rc.unerase(guard);
1997 let instance = s.borrow_instance();
1998 let element = repeated_element.upgrade()?;
1999 let index = crate::eval::load_property(
2000 instance,
2001 &element.borrow().base_type.as_component().root_element,
2002 crate::dynamic_item_tree::SPECIAL_PROPERTY_INDEX,
2003 )
2004 .ok()?;
2005 let row = usize::try_from(i32::try_from(index).ok()?).ok()?;
2006 generativity::make_guard!(guard);
2007 let enclosing = crate::eval::enclosing_component_for_element(&element, instance, guard);
2008 generativity::make_guard!(guard);
2009 let (repeater, _) = get_repeater_by_name(enclosing, element.borrow().id.as_str(), guard);
2010 f(repeater, row)
2011}
2012
2013fn walk_struct_field_path(mut value: Value, fields: &[SmolStr]) -> Option<Value> {
2015 for f in fields {
2016 match value {
2017 Value::Struct(o) => value = o.get_field(f).cloned().unwrap_or_default(),
2018 Value::Void => return None,
2019 _ => return None,
2020 }
2021 }
2022 Some(value)
2023}
2024
2025fn walk_struct_field_path_mut<'a>(
2027 mut value: &'a mut Value,
2028 fields: &[SmolStr],
2029) -> Option<&'a mut Value> {
2030 for f in fields {
2031 match value {
2032 Value::Struct(o) => value = o.0.get_mut(f)?,
2033 _ => return None,
2034 }
2035 }
2036 Some(value)
2037}
2038
2039pub(crate) fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const c_void {
2040 let element = nr.element();
2041 generativity::make_guard!(guard);
2042 let enclosing_component = eval::enclosing_component_instance_for_element(
2043 &element,
2044 &eval::ComponentInstance::InstanceRef(instance),
2045 guard,
2046 );
2047 match enclosing_component {
2048 eval::ComponentInstance::InstanceRef(enclosing_component) => {
2049 let element = element.borrow();
2050 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
2051 && let Some(x) = enclosing_component.description.custom_properties.get(nr.name())
2052 {
2053 return unsafe { enclosing_component.as_ptr().add(x.offset).cast() };
2054 };
2055 let item_info = enclosing_component
2056 .description
2057 .items
2058 .get(element.id.as_str())
2059 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, nr.name()));
2060 let prop_info = item_info
2061 .rtti
2062 .properties
2063 .get(nr.name().as_str())
2064 .unwrap_or_else(|| panic!("Property {} not in {}", nr.name(), element.id));
2065 core::mem::drop(element);
2066 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
2067 unsafe { item.as_ptr().add(prop_info.offset()).cast() }
2068 }
2069 eval::ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property_ptr(nr.name()),
2070 }
2071}
2072
2073pub struct ErasedItemTreeBox(ItemTreeBox<'static>);
2074impl ErasedItemTreeBox {
2075 pub fn unerase<'a, 'id>(
2076 &'a self,
2077 _guard: generativity::Guard<'id>,
2078 ) -> Pin<&'a ItemTreeBox<'id>> {
2079 Pin::new(
2080 unsafe { core::mem::transmute::<&ItemTreeBox<'static>, &ItemTreeBox<'id>>(&self.0) },
2082 )
2083 }
2084
2085 pub fn borrow(&self) -> ItemTreeRefPin<'_> {
2086 self.0.borrow()
2088 }
2089
2090 pub fn window_adapter_ref(&self) -> Result<&WindowAdapterRc, PlatformError> {
2091 self.0.window_adapter_ref()
2092 }
2093
2094 pub fn run_setup_code(&self) {
2095 generativity::make_guard!(guard);
2096 let compo_box = self.unerase(guard);
2097 let instance_ref = compo_box.borrow_instance();
2098 for extra_init_code in self.0.description.original.init_code.borrow().iter() {
2099 eval::eval_expression(
2100 extra_init_code,
2101 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
2102 );
2103 }
2104 if let Some(cts) = instance_ref.description.change_trackers.as_ref() {
2105 let self_weak = instance_ref.self_weak().get().unwrap();
2106 let v = cts
2107 .1
2108 .iter()
2109 .enumerate()
2110 .map(|(idx, _)| {
2111 let ct = ChangeTracker::default();
2112 ct.init(
2113 self_weak.clone(),
2114 move |self_weak| {
2115 let s = self_weak.upgrade().unwrap();
2116 generativity::make_guard!(guard);
2117 let compo_box = s.unerase(guard);
2118 let instance_ref = compo_box.borrow_instance();
2119 let nr = &s.0.description.change_trackers.as_ref().unwrap().1[idx].0;
2120 eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap()
2121 },
2122 move |self_weak, _| {
2123 let s = self_weak.upgrade().unwrap();
2124 generativity::make_guard!(guard);
2125 let compo_box = s.unerase(guard);
2126 let instance_ref = compo_box.borrow_instance();
2127 let e = &s.0.description.change_trackers.as_ref().unwrap().1[idx].1;
2128 eval::eval_expression(
2129 e,
2130 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
2131 );
2132 },
2133 );
2134 ct
2135 })
2136 .collect::<Vec<_>>();
2137 cts.0
2138 .apply_pin(instance_ref.instance)
2139 .set(v)
2140 .unwrap_or_else(|_| panic!("run_setup_code called twice?"));
2141 }
2142 update_timers(instance_ref);
2143 }
2144}
2145impl<'id> From<ItemTreeBox<'id>> for ErasedItemTreeBox {
2146 fn from(inner: ItemTreeBox<'id>) -> Self {
2147 unsafe {
2150 ErasedItemTreeBox(core::mem::transmute::<ItemTreeBox<'id>, ItemTreeBox<'static>>(inner))
2151 }
2152 }
2153}
2154
2155pub fn get_repeater_by_name<'a, 'id>(
2156 instance_ref: InstanceRef<'a, '_>,
2157 name: &str,
2158 guard: generativity::Guard<'id>,
2159) -> (std::pin::Pin<&'a Repeater<ErasedItemTreeBox>>, Rc<ItemTreeDescription<'id>>) {
2160 let rep_index = instance_ref.description.repeater_names[name];
2161 let rep_in_comp = instance_ref.description.repeater[rep_index].unerase(guard);
2162 (rep_in_comp.offset.apply_pin(instance_ref.instance), rep_in_comp.item_tree_to_repeat.clone())
2163}
2164
2165#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2166extern "C" fn ensure_instantiated(component: ItemTreeRefPin) -> bool {
2167 generativity::make_guard!(guard);
2168 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2170
2171 let mut changed = false;
2172 for (tree_index, node) in instance_ref.description.item_tree.iter().enumerate() {
2173 if !matches!(node, ItemTreeNode::Item { .. }) {
2174 continue;
2175 }
2176 let item_ref = component.as_ref().get_item_ref(tree_index as u32);
2177 if let Some(container) = i_slint_core::items::ItemRef::downcast_pin::<
2178 i_slint_core::items::ComponentContainer,
2179 >(item_ref)
2180 {
2181 changed |= container.ensure_updated();
2182 }
2183 }
2184
2185 for rep_in_comp in &instance_ref.description.repeater {
2186 let rep_in_comp = unsafe { rep_in_comp.get_untagged() };
2188 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
2189 let init = || {
2190 let extra_data =
2191 instance_ref.description.extra_data_offset.apply(instance_ref.as_ref());
2192 instantiate(
2193 rep_in_comp.item_tree_to_repeat.clone(),
2194 instance_ref.self_weak().get().cloned(),
2195 None,
2196 None,
2197 extra_data.globals.get().unwrap().clone(),
2198 )
2199 };
2200 if let Some(lv) = &rep_in_comp
2201 .item_tree_to_repeat
2202 .original
2203 .parent_element
2204 .borrow()
2205 .upgrade()
2206 .unwrap()
2207 .borrow()
2208 .repeated
2209 .as_ref()
2210 .unwrap()
2211 .is_listview
2212 {
2213 let assume_property_logical_length =
2214 |prop| unsafe { Pin::new_unchecked(&*(prop as *const Property<LogicalLength>)) };
2215 changed |= repeater.ensure_updated_listview(
2216 init,
2217 assume_property_logical_length(get_property_ptr(&lv.viewport_width, instance_ref)),
2218 assume_property_logical_length(get_property_ptr(&lv.viewport_height, instance_ref)),
2219 assume_property_logical_length(get_property_ptr(&lv.viewport_y, instance_ref)),
2220 eval::load_property(
2221 instance_ref,
2222 &lv.listview_width.element(),
2223 lv.listview_width.name(),
2224 )
2225 .unwrap()
2226 .try_into()
2227 .unwrap(),
2228 assume_property_logical_length(get_property_ptr(&lv.listview_height, instance_ref)),
2229 );
2230 } else {
2231 changed |= repeater.ensure_updated(init);
2232 }
2233 }
2234 changed
2235}
2236
2237#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2238extern "C" fn layout_info(component: ItemTreeRefPin, orientation: Orientation) -> LayoutInfo {
2239 generativity::make_guard!(guard);
2240 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2242 let orientation = crate::eval_layout::from_runtime(orientation);
2243
2244 let mut result = crate::eval_layout::get_layout_info(
2245 &instance_ref.description.original.root_element,
2246 instance_ref,
2247 &instance_ref.window_adapter(),
2248 orientation,
2249 );
2250
2251 let constraints = instance_ref.description.original.root_constraints.borrow();
2252 if constraints.has_explicit_restrictions(orientation) {
2253 crate::eval_layout::fill_layout_info_constraints(
2254 &mut result,
2255 &constraints,
2256 orientation,
2257 &|nr: &NamedReference| {
2258 eval::load_property(instance_ref, &nr.element(), nr.name())
2259 .unwrap()
2260 .try_into()
2261 .unwrap()
2262 },
2263 );
2264 }
2265 result
2266}
2267
2268#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2269unsafe extern "C" fn get_item_ref(component: ItemTreeRefPin, index: u32) -> Pin<ItemRef> {
2270 let tree = get_item_tree(component);
2271 match &tree[index as usize] {
2272 ItemTreeNode::Item { item_array_index, .. } => unsafe {
2273 generativity::make_guard!(guard);
2274 let instance_ref = InstanceRef::from_pin_ref(component, guard);
2275 core::mem::transmute::<Pin<ItemRef>, Pin<ItemRef>>(
2276 instance_ref.description.item_array[*item_array_index as usize]
2277 .apply_pin(instance_ref.instance),
2278 )
2279 },
2280 ItemTreeNode::DynamicTree { .. } => panic!("get_item_ref called on dynamic tree"),
2281 }
2282}
2283
2284#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2285extern "C" fn get_subtree_range(component: ItemTreeRefPin, index: u32) -> IndexRange {
2286 generativity::make_guard!(guard);
2287 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2288 if index as usize >= instance_ref.description.repeater.len() {
2289 let container_index = {
2290 let tree_node = &component.as_ref().get_item_tree()[index as usize];
2291 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
2292 *parent_index
2293 } else {
2294 u32::MAX
2295 }
2296 };
2297 let container = component.as_ref().get_item_ref(container_index);
2298 let container = i_slint_core::items::ItemRef::downcast_pin::<
2299 i_slint_core::items::ComponentContainer,
2300 >(container)
2301 .unwrap();
2302 container.subtree_range()
2303 } else {
2304 generativity::make_guard!(guard);
2305 let rep_in_comp = instance_ref.description.repeater[index as usize].unerase(guard);
2306
2307 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
2308 repeater.track_instance_changes();
2309 repeater.range().into()
2310 }
2311}
2312
2313#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2314extern "C" fn get_subtree(
2315 component: ItemTreeRefPin,
2316 index: u32,
2317 subtree_index: usize,
2318 result: &mut ItemTreeWeak,
2319) {
2320 generativity::make_guard!(guard);
2321 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2322 if index as usize >= instance_ref.description.repeater.len() {
2323 let container_index = {
2324 let tree_node = &component.as_ref().get_item_tree()[index as usize];
2325 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
2326 *parent_index
2327 } else {
2328 u32::MAX
2329 }
2330 };
2331 let container = component.as_ref().get_item_ref(container_index);
2332 let container = i_slint_core::items::ItemRef::downcast_pin::<
2333 i_slint_core::items::ComponentContainer,
2334 >(container)
2335 .unwrap();
2336 if subtree_index == 0 {
2337 *result = container.subtree_component();
2338 }
2339 } else {
2340 generativity::make_guard!(guard);
2341 let rep_in_comp = instance_ref.description.repeater[index as usize].unerase(guard);
2342
2343 let repeater = rep_in_comp.offset.apply(&instance_ref.instance);
2344 if let Some(instance_at) = repeater.instance_at(subtree_index) {
2345 *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(instance_at))
2346 }
2347 }
2348}
2349
2350#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2351extern "C" fn get_item_tree(component: ItemTreeRefPin) -> Slice<ItemTreeNode> {
2352 generativity::make_guard!(guard);
2353 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2354 let tree = instance_ref.description.item_tree.as_slice();
2355 unsafe { core::mem::transmute::<&[ItemTreeNode], &[ItemTreeNode]>(tree) }.into()
2356}
2357
2358#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2359extern "C" fn subtree_index(component: ItemTreeRefPin) -> usize {
2360 generativity::make_guard!(guard);
2361 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2362 if let Ok(value) = instance_ref.description.get_property(component, SPECIAL_PROPERTY_INDEX) {
2363 value.try_into().unwrap()
2364 } else {
2365 usize::MAX
2366 }
2367}
2368
2369#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2370unsafe extern "C" fn parent_node(component: ItemTreeRefPin, result: &mut ItemWeak) {
2371 generativity::make_guard!(guard);
2372 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2373
2374 let component_and_index = {
2375 if let Some(parent_offset) = instance_ref.description.parent_item_tree_offset {
2377 let parent_item_index = instance_ref
2378 .description
2379 .original
2380 .parent_element
2381 .borrow()
2382 .upgrade()
2383 .and_then(|e| e.borrow().item_index.get().cloned())
2384 .unwrap_or(u32::MAX);
2385 let parent_component = parent_offset
2386 .apply(instance_ref.as_ref())
2387 .get()
2388 .and_then(|p| p.upgrade())
2389 .map(vtable::VRc::into_dyn);
2390
2391 (parent_component, parent_item_index)
2392 } else if let Some((parent_component, parent_index)) = instance_ref
2393 .description
2394 .extra_data_offset
2395 .apply(instance_ref.as_ref())
2396 .embedding_position
2397 .get()
2398 {
2399 (parent_component.upgrade(), *parent_index)
2400 } else {
2401 (None, u32::MAX)
2402 }
2403 };
2404
2405 if let (Some(component), index) = component_and_index {
2406 *result = ItemRc::new(component, index).downgrade();
2407 }
2408}
2409
2410#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2411unsafe extern "C" fn embed_component(
2412 component: ItemTreeRefPin,
2413 parent_component: &ItemTreeWeak,
2414 parent_item_tree_index: u32,
2415) -> bool {
2416 generativity::make_guard!(guard);
2417 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2418
2419 if instance_ref.description.parent_item_tree_offset.is_some() {
2420 return false;
2422 }
2423
2424 {
2425 let prc = parent_component.upgrade().unwrap();
2427 let pref = vtable::VRc::borrow_pin(&prc);
2428 let it = pref.as_ref().get_item_tree();
2429 if !matches!(
2430 it.get(parent_item_tree_index as usize),
2431 Some(ItemTreeNode::DynamicTree { .. })
2432 ) {
2433 panic!("Trying to embed into a non-dynamic index in the parents item tree")
2434 }
2435 }
2436
2437 let extra_data = instance_ref.description.extra_data_offset.apply(instance_ref.as_ref());
2438 extra_data.embedding_position.set((parent_component.clone(), parent_item_tree_index)).is_ok()
2439}
2440
2441#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2442extern "C" fn item_geometry(component: ItemTreeRefPin, item_index: u32) -> LogicalRect {
2443 generativity::make_guard!(guard);
2444 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2445
2446 let e = instance_ref.description.original_elements[item_index as usize].borrow();
2447 let g = e.geometry_props.as_ref().unwrap();
2448
2449 let load_f32 = |nr: &NamedReference| -> f32 {
2450 crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2451 .unwrap()
2452 .try_into()
2453 .unwrap()
2454 };
2455
2456 LogicalRect {
2457 origin: (load_f32(&g.x), load_f32(&g.y)).into(),
2458 size: (load_f32(&g.width), load_f32(&g.height)).into(),
2459 }
2460}
2461
2462#[allow(improper_ctypes_definitions)]
2464#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2465extern "C" fn accessible_role(component: ItemTreeRefPin, item_index: u32) -> AccessibleRole {
2466 generativity::make_guard!(guard);
2467 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2468 let nr = instance_ref.description.original_elements[item_index as usize]
2469 .borrow()
2470 .accessibility_props
2471 .0
2472 .get("accessible-role")
2473 .cloned();
2474 match nr {
2475 Some(nr) => crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2476 .unwrap()
2477 .try_into()
2478 .unwrap(),
2479 None => AccessibleRole::default(),
2480 }
2481}
2482
2483#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2484extern "C" fn accessible_string_property(
2485 component: ItemTreeRefPin,
2486 item_index: u32,
2487 what: AccessibleStringProperty,
2488 result: &mut SharedString,
2489) -> bool {
2490 generativity::make_guard!(guard);
2491 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2492 let prop_name = format!("accessible-{what}");
2493 let nr = instance_ref.description.original_elements[item_index as usize]
2494 .borrow()
2495 .accessibility_props
2496 .0
2497 .get(&prop_name)
2498 .cloned();
2499 if let Some(nr) = nr {
2500 let value = crate::eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap();
2501 match value {
2502 Value::String(s) => *result = s,
2503 Value::Bool(b) => *result = if b { "true" } else { "false" }.into(),
2504 Value::Number(x) => *result = x.to_string().into(),
2505 Value::EnumerationValue(_, v) => *result = v.into(),
2506 _ => unimplemented!("invalid type for accessible_string_property"),
2507 };
2508 true
2509 } else {
2510 false
2511 }
2512}
2513
2514#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2515extern "C" fn accessibility_action(
2516 component: ItemTreeRefPin,
2517 item_index: u32,
2518 action: &AccessibilityAction,
2519) {
2520 let perform = |prop_name, args: &[Value]| {
2521 generativity::make_guard!(guard);
2522 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2523 let nr = instance_ref.description.original_elements[item_index as usize]
2524 .borrow()
2525 .accessibility_props
2526 .0
2527 .get(prop_name)
2528 .cloned();
2529 if let Some(nr) = nr {
2530 let instance_ref = eval::ComponentInstance::InstanceRef(instance_ref);
2531 crate::eval::invoke_callback(&instance_ref, &nr.element(), nr.name(), args).unwrap();
2532 }
2533 };
2534
2535 match action {
2536 AccessibilityAction::Default => perform("accessible-action-default", &[]),
2537 AccessibilityAction::Decrement => perform("accessible-action-decrement", &[]),
2538 AccessibilityAction::Increment => perform("accessible-action-increment", &[]),
2539 AccessibilityAction::Expand => perform("accessible-action-expand", &[]),
2540 AccessibilityAction::ReplaceSelectedText(_a) => {
2541 i_slint_core::debug_log!(
2543 "AccessibilityAction::ReplaceSelectedText not implemented in interpreter's accessibility_action"
2544 );
2545 }
2546 AccessibilityAction::SetValue(a) => {
2547 perform("accessible-action-set-value", &[Value::String(a.clone())])
2548 }
2549 };
2550}
2551
2552#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2553extern "C" fn supported_accessibility_actions(
2554 component: ItemTreeRefPin,
2555 item_index: u32,
2556) -> SupportedAccessibilityAction {
2557 generativity::make_guard!(guard);
2558 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2559 instance_ref.description.original_elements[item_index as usize]
2560 .borrow()
2561 .accessibility_props
2562 .0
2563 .keys()
2564 .filter_map(|x| x.strip_prefix("accessible-action-"))
2565 .fold(SupportedAccessibilityAction::default(), |acc, value| {
2566 SupportedAccessibilityAction::from_name(&i_slint_compiler::generator::to_pascal_case(
2567 value,
2568 ))
2569 .unwrap_or_else(|| panic!("Not an accessible action: {value:?}"))
2570 | acc
2571 })
2572}
2573
2574#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2575extern "C" fn item_element_infos(
2576 component: ItemTreeRefPin,
2577 item_index: u32,
2578 result: &mut SharedString,
2579) -> bool {
2580 generativity::make_guard!(guard);
2581 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2582 *result = instance_ref.description.original_elements[item_index as usize]
2583 .borrow()
2584 .element_infos()
2585 .into();
2586 true
2587}
2588
2589#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2590extern "C" fn window_adapter(
2591 component: ItemTreeRefPin,
2592 do_create: bool,
2593 result: &mut Option<WindowAdapterRc>,
2594) {
2595 generativity::make_guard!(guard);
2596 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2597 if do_create {
2598 *result = Some(instance_ref.window_adapter());
2599 } else {
2600 *result = instance_ref.maybe_window_adapter();
2601 }
2602}
2603
2604#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2605unsafe extern "C" fn drop_in_place(component: vtable::VRefMut<ItemTreeVTable>) -> vtable::Layout {
2606 unsafe {
2607 let instance_ptr = component.as_ptr() as *mut Instance<'static>;
2608 let layout = (*instance_ptr).type_info().layout();
2609 dynamic_type::TypeInfo::drop_in_place(instance_ptr);
2610 layout.into()
2611 }
2612}
2613
2614#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2615unsafe extern "C" fn dealloc(_vtable: &ItemTreeVTable, ptr: *mut u8, layout: vtable::Layout) {
2616 unsafe { std::alloc::dealloc(ptr, layout.try_into().unwrap()) };
2617}
2618
2619#[derive(Copy, Clone)]
2620pub struct InstanceRef<'a, 'id> {
2621 pub instance: Pin<&'a Instance<'id>>,
2622 pub description: &'a ItemTreeDescription<'id>,
2623}
2624
2625impl<'a, 'id> InstanceRef<'a, 'id> {
2626 pub unsafe fn from_pin_ref(
2627 component: ItemTreeRefPin<'a>,
2628 _guard: generativity::Guard<'id>,
2629 ) -> Self {
2630 unsafe {
2631 Self {
2632 instance: Pin::new_unchecked(
2633 &*(component.as_ref().as_ptr() as *const Instance<'id>),
2634 ),
2635 description: &*(Pin::into_inner_unchecked(component).get_vtable()
2636 as *const ItemTreeVTable
2637 as *const ItemTreeDescription<'id>),
2638 }
2639 }
2640 }
2641
2642 pub fn as_ptr(&self) -> *const u8 {
2643 (&*self.instance.as_ref()) as *const Instance as *const u8
2644 }
2645
2646 pub fn as_ref(&self) -> &Instance<'id> {
2647 &self.instance
2648 }
2649
2650 pub fn borrow(self) -> ItemTreeRefPin<'a> {
2652 unsafe {
2653 Pin::new_unchecked(vtable::VRef::from_raw(
2654 NonNull::from(&self.description.ct).cast(),
2655 NonNull::from(self.instance.get_ref()).cast(),
2656 ))
2657 }
2658 }
2659
2660 pub fn self_weak(&self) -> &OnceCell<ErasedItemTreeBoxWeak> {
2661 let extra_data = self.description.extra_data_offset.apply(self.as_ref());
2662 &extra_data.self_weak
2663 }
2664
2665 pub fn root_weak(&self) -> &ErasedItemTreeBoxWeak {
2666 self.description.root_offset.apply(self.as_ref()).get().unwrap()
2667 }
2668
2669 pub fn window_adapter(&self) -> WindowAdapterRc {
2670 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2671 let root = self.root_weak().upgrade().unwrap();
2672 generativity::make_guard!(guard);
2673 let comp = root.unerase(guard);
2674 Self::get_or_init_window_adapter_ref(
2675 &comp.description,
2676 root_weak,
2677 true,
2678 comp.instance.as_pin_ref().get_ref(),
2679 )
2680 .unwrap()
2681 .clone()
2682 }
2683
2684 pub fn get_or_init_window_adapter_ref<'b, 'id2>(
2685 description: &'b ItemTreeDescription<'id2>,
2686 root_weak: ItemTreeWeak,
2687 do_create: bool,
2688 instance: &'b Instance<'id2>,
2689 ) -> Result<&'b WindowAdapterRc, PlatformError> {
2690 description
2692 .extra_data_offset
2693 .apply(instance)
2694 .globals
2695 .get()
2696 .unwrap()
2697 .window_adapter()
2698 .unwrap()
2699 .get_or_try_init(|| {
2700 let mut parent_node = ItemWeak::default();
2701 if let Some(rc) = vtable::VWeak::upgrade(&root_weak) {
2702 vtable::VRc::borrow_pin(&rc).as_ref().parent_node(&mut parent_node);
2703 }
2704
2705 if let Some(parent) = parent_node.upgrade() {
2706 let mut result = None;
2708 vtable::VRc::borrow_pin(parent.item_tree())
2709 .as_ref()
2710 .window_adapter(do_create, &mut result);
2711 result.ok_or(PlatformError::NoPlatform)
2712 } else if do_create {
2713 let extra_data = description.extra_data_offset.apply(instance);
2714 let window_adapter = i_slint_backend_selector::with_platform(|_b| {
2716 _b.create_window_adapter()
2717 })?;
2718
2719 let comp_rc = extra_data.self_weak.get().unwrap().upgrade().unwrap();
2720 WindowInner::from_pub(window_adapter.window())
2721 .set_component(&vtable::VRc::into_dyn(comp_rc));
2722 Ok(window_adapter)
2723 } else {
2724 Err(PlatformError::NoPlatform)
2725 }
2726 })
2727 }
2728
2729 pub fn maybe_window_adapter(&self) -> Option<WindowAdapterRc> {
2730 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2731 let root = self.root_weak().upgrade()?;
2732 generativity::make_guard!(guard);
2733 let comp = root.unerase(guard);
2734 Self::get_or_init_window_adapter_ref(
2735 &comp.description,
2736 root_weak,
2737 false,
2738 comp.instance.as_pin_ref().get_ref(),
2739 )
2740 .ok()
2741 .cloned()
2742 }
2743
2744 pub fn access_window<R>(
2745 self,
2746 callback: impl FnOnce(&'_ i_slint_core::window::WindowInner) -> R,
2747 ) -> R {
2748 callback(WindowInner::from_pub(self.window_adapter().window()))
2749 }
2750
2751 pub fn parent_instance<'id2>(
2752 &self,
2753 _guard: generativity::Guard<'id2>,
2754 ) -> Option<InstanceRef<'a, 'id2>> {
2755 if let Some(parent_offset) = self.description.parent_item_tree_offset
2758 && let Some(parent) =
2759 parent_offset.apply(self.as_ref()).get().and_then(vtable::VWeak::upgrade)
2760 {
2761 let parent_instance = parent.unerase(_guard);
2762 let parent_instance = unsafe {
2764 std::mem::transmute::<InstanceRef<'_, 'id2>, InstanceRef<'a, 'id2>>(
2765 parent_instance.borrow_instance(),
2766 )
2767 };
2768 return Some(parent_instance);
2769 }
2770 None
2771 }
2772}
2773
2774pub fn show_popup(
2776 element: ElementRc,
2777 instance: InstanceRef,
2778 popup: &object_tree::PopupWindow,
2779 pos_getter: impl Fn(InstanceRef<'_, '_>) -> LogicalPosition + 'static,
2780 close_policy: PopupClosePolicy,
2781 parent_comp: ErasedItemTreeBoxWeak,
2782 parent_window_adapter: WindowAdapterRc,
2783 parent_item: &ItemRc,
2784) {
2785 generativity::make_guard!(guard);
2786 let debug_handler = instance.description.debug_handler.borrow().clone();
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 compiled.recursively_set_debug_handler(debug_handler);
2797
2798 let extra_data = instance.description.extra_data_offset.apply(instance.as_ref());
2799 let globals = if !popup.is_tooltip
2803 && let Some(window_adapter) =
2804 WindowInner::from_pub(parent_window_adapter.window()).create_popup_window_adapter()
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 instance.description.popup_ids.borrow_mut().insert(
2832 element.borrow().id.clone(),
2833 WindowInner::from_pub(parent_window_adapter.window()).show_popup(
2834 &vtable::VRc::into_dyn(inst.clone()),
2835 access_position,
2836 close_policy,
2837 parent_item,
2838 popup.is_tooltip,
2839 false,
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) -> vtable::VRc<i_slint_core::menus::MenuVTable, MenuFromItemTree> {
2862 generativity::make_guard!(guard);
2863 let mit_compiled = generate_item_tree(
2864 menu_item_tree,
2865 None,
2866 enclosing_component.description.popup_menu_description.clone(),
2867 false,
2868 guard,
2869 );
2870 let enclosing_component_weak = enclosing_component.self_weak().get().unwrap();
2871 let extra_data =
2872 enclosing_component.description.extra_data_offset.apply(enclosing_component.as_ref());
2873 let mit_inst = instantiate(
2874 mit_compiled.clone(),
2875 Some(enclosing_component_weak.clone()),
2876 None,
2877 None,
2878 extra_data.globals.get().unwrap().clone(),
2879 );
2880 mit_inst.run_setup_code();
2881 let item_tree = vtable::VRc::into_dyn(mit_inst);
2882 let menu = match condition {
2883 Some(condition) => {
2884 let binding =
2885 make_binding_eval_closure(condition.clone(), enclosing_component_weak.clone());
2886 MenuFromItemTree::new_with_condition(item_tree, move || binding().try_into().unwrap())
2887 }
2888 None => MenuFromItemTree::new(item_tree),
2889 };
2890 vtable::VRc::new(menu)
2891}
2892
2893pub fn update_timers(instance: InstanceRef) {
2894 let ts = instance.description.original.timers.borrow();
2895 for (desc, offset) in ts.iter().zip(&instance.description.timers) {
2896 let timer = offset.apply(instance.as_ref());
2897 let running =
2898 eval::load_property(instance, &desc.running.element(), desc.running.name()).unwrap();
2899 if matches!(running, Value::Bool(true)) {
2900 let millis: i64 =
2901 eval::load_property(instance, &desc.interval.element(), desc.interval.name())
2902 .unwrap()
2903 .try_into()
2904 .expect("interval must be a duration");
2905 if millis < 0 {
2906 timer.stop();
2907 continue;
2908 }
2909 let interval = core::time::Duration::from_millis(millis as _);
2910 if !timer.running() || interval != timer.interval() {
2911 let callback = desc.triggered.clone();
2912 let self_weak = instance.self_weak().get().unwrap().clone();
2913 timer.start(i_slint_core::timers::TimerMode::Repeated, interval, move || {
2914 if let Some(instance) = self_weak.upgrade() {
2915 generativity::make_guard!(guard);
2916 let c = instance.unerase(guard);
2917 let c = c.borrow_instance();
2918 let inst = eval::ComponentInstance::InstanceRef(c);
2919 eval::invoke_callback(&inst, &callback.element(), callback.name(), &[])
2920 .unwrap();
2921 }
2922 });
2923 }
2924 } else {
2925 timer.stop();
2926 }
2927 }
2928}
2929
2930pub fn restart_timer(element: ElementWeak, instance: InstanceRef) {
2931 let timers = instance.description.original.timers.borrow();
2932 if let Some((_, offset)) = timers
2933 .iter()
2934 .zip(&instance.description.timers)
2935 .find(|(desc, _)| Weak::ptr_eq(&desc.element, &element))
2936 {
2937 let timer = offset.apply(instance.as_ref());
2938 timer.restart();
2939 }
2940}