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