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