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