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