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