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, 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 #[cfg(feature = "internal-file-watcher")]
962 let watch_paths = loader.all_files_to_watch().into_iter().collect();
963 if diag.has_errors() {
964 return CompilationResult {
965 components: HashMap::new(),
966 diagnostics: diag.into_iter().collect(),
967 #[cfg(feature = "internal-file-watcher")]
968 watch_paths,
969 #[cfg(feature = "internal")]
970 structs_and_enums: Vec::new(),
971 #[cfg(feature = "internal")]
972 named_exports: Vec::new(),
973 };
974 }
975
976 #[cfg(feature = "internal-highlight")]
977 let loader = Rc::new(loader);
978 #[cfg(feature = "internal-highlight")]
979 let raw_type_loader = raw_type_loader.map(Rc::new);
980
981 let doc = loader.get_document(&path).unwrap();
982
983 let compiled_globals = Rc::new(CompiledGlobalCollection::compile(doc));
984 let mut components = HashMap::new();
985
986 let popup_menu_description = if let Some(popup_menu_impl) = &doc.popup_menu_impl {
987 PopupMenuDescription::Rc(Rc::new_cyclic(|weak| {
988 generativity::make_guard!(guard);
989 ErasedItemTreeDescription::from(generate_item_tree(
990 popup_menu_impl,
991 Some(compiled_globals.clone()),
992 PopupMenuDescription::Weak(weak.clone()),
993 true,
994 guard,
995 ))
996 }))
997 } else {
998 PopupMenuDescription::Weak(Default::default())
999 };
1000
1001 for c in doc.exported_roots() {
1002 generativity::make_guard!(guard);
1003 #[allow(unused_mut)]
1004 let mut it = generate_item_tree(
1005 &c,
1006 Some(compiled_globals.clone()),
1007 popup_menu_description.clone(),
1008 false,
1009 guard,
1010 );
1011 #[cfg(feature = "internal-highlight")]
1012 {
1013 let _ = it.type_loader.set(loader.clone());
1014 let _ = it.raw_type_loader.set(raw_type_loader.clone());
1015 }
1016 components.insert(c.id.to_string(), ComponentDefinition { inner: it.into() });
1017 }
1018
1019 if components.is_empty() {
1020 diag.push_error_with_span("No component found".into(), Default::default());
1021 };
1022
1023 #[cfg(feature = "internal")]
1024 let structs_and_enums = doc.used_types.borrow().structs_and_enums.clone();
1025
1026 #[cfg(feature = "internal")]
1027 let named_exports = doc
1028 .exports
1029 .iter()
1030 .filter_map(|export| match &export.1 {
1031 Either::Left(component) if !component.is_global() => {
1032 Some((&export.0.name, &component.id))
1033 }
1034 Either::Right(ty) => match &ty {
1035 Type::Struct(s) if s.node().is_some() => {
1036 if let StructName::User { name, .. } = &s.name {
1037 Some((&export.0.name, name))
1038 } else {
1039 None
1040 }
1041 }
1042 Type::Enumeration(en) => Some((&export.0.name, &en.name)),
1043 _ => None,
1044 },
1045 _ => None,
1046 })
1047 .filter(|(export_name, type_name)| *export_name != *type_name)
1048 .map(|(export_name, type_name)| (type_name.to_string(), export_name.to_string()))
1049 .collect::<Vec<_>>();
1050
1051 CompilationResult {
1052 diagnostics: diag.into_iter().collect(),
1053 components,
1054 #[cfg(feature = "internal-file-watcher")]
1055 watch_paths,
1056 #[cfg(feature = "internal")]
1057 structs_and_enums,
1058 #[cfg(feature = "internal")]
1059 named_exports,
1060 }
1061}
1062
1063fn generate_rtti() -> HashMap<&'static str, Rc<ItemRTTI>> {
1064 let mut rtti = HashMap::new();
1065 use i_slint_core::items::*;
1066 rtti.extend(
1067 [
1068 rtti_for::<ComponentContainer>(),
1069 rtti_for::<Empty>(),
1070 rtti_for::<ImageItem>(),
1071 rtti_for::<ClippedImage>(),
1072 rtti_for::<ComplexText>(),
1073 rtti_for::<StyledTextItem>(),
1074 rtti_for::<SimpleText>(),
1075 rtti_for::<Rectangle>(),
1076 rtti_for::<BasicBorderRectangle>(),
1077 rtti_for::<BorderRectangle>(),
1078 rtti_for::<TouchArea>(),
1079 rtti_for::<TooltipArea>(),
1080 rtti_for::<FocusScope>(),
1081 rtti_for::<KeyBinding>(),
1082 rtti_for::<SwipeGestureHandler>(),
1083 rtti_for::<ScaleRotateGestureHandler>(),
1084 rtti_for::<Path>(),
1085 rtti_for::<Flickable>(),
1086 rtti_for::<WindowItem>(),
1087 rtti_for::<TextInput>(),
1088 rtti_for::<Clip>(),
1089 rtti_for::<BoxShadow>(),
1090 rtti_for::<Transform>(),
1091 rtti_for::<Opacity>(),
1092 rtti_for::<Layer>(),
1093 rtti_for::<DragArea>(),
1094 rtti_for::<DropArea>(),
1095 rtti_for::<ContextMenu>(),
1096 rtti_for::<MenuItem>(),
1097 rtti_for::<SystemTrayIcon>(),
1098 ]
1099 .iter()
1100 .cloned(),
1101 );
1102
1103 trait NativeHelper {
1104 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>);
1105 }
1106 impl NativeHelper for () {
1107 fn push(_rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {}
1108 }
1109 impl<
1110 T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>,
1111 Next: NativeHelper,
1112 > NativeHelper for (T, Next)
1113 {
1114 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {
1115 let info = rtti_for::<T>();
1116 rtti.insert(info.0, info.1);
1117 Next::push(rtti);
1118 }
1119 }
1120 i_slint_backend_selector::NativeWidgets::push(&mut rtti);
1121
1122 rtti
1123}
1124
1125pub(crate) fn generate_item_tree<'id>(
1126 component: &Rc<object_tree::Component>,
1127 compiled_globals: Option<Rc<CompiledGlobalCollection>>,
1128 popup_menu_description: PopupMenuDescription,
1129 is_popup_menu_impl: bool,
1130 guard: generativity::Guard<'id>,
1131) -> Rc<ItemTreeDescription<'id>> {
1132 thread_local! {
1135 static RTTI: Lazy<HashMap<&'static str, Rc<ItemRTTI>>> = Lazy::new(generate_rtti);
1136 }
1137
1138 struct TreeBuilder<'id> {
1139 tree_array: Vec<ItemTreeNode>,
1140 item_array:
1141 Vec<vtable::VOffset<crate::dynamic_type::Instance<'id>, ItemVTable, vtable::AllowPin>>,
1142 original_elements: Vec<ElementRc>,
1143 items_types: HashMap<SmolStr, ItemWithinItemTree>,
1144 type_builder: dynamic_type::TypeBuilder<'id>,
1145 repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
1146 repeater_names: HashMap<SmolStr, usize>,
1147 change_callbacks: Vec<(NamedReference, Expression)>,
1148 popup_menu_description: PopupMenuDescription,
1149 }
1150 impl generator::ItemTreeBuilder for TreeBuilder<'_> {
1151 type SubComponentState = ();
1152
1153 fn push_repeated_item(
1154 &mut self,
1155 item_rc: &ElementRc,
1156 repeater_count: u32,
1157 parent_index: u32,
1158 _component_state: &Self::SubComponentState,
1159 ) {
1160 self.tree_array.push(ItemTreeNode::DynamicTree { index: repeater_count, parent_index });
1161 self.original_elements.push(item_rc.clone());
1162 let item = item_rc.borrow();
1163 let base_component = item.base_type.as_component();
1164 self.repeater_names.insert(item.id.clone(), self.repeater.len());
1165 generativity::make_guard!(guard);
1166 let repeated_element_info = item.repeated.as_ref().unwrap();
1167 self.repeater.push(
1168 RepeaterWithinItemTree {
1169 item_tree_to_repeat: generate_item_tree(
1170 base_component,
1171 None,
1172 self.popup_menu_description.clone(),
1173 false,
1174 guard,
1175 ),
1176 offset: self.type_builder.add_field_type::<Repeater<ErasedItemTreeBox>>(),
1177 model: repeated_element_info.model.clone(),
1178 is_conditional: repeated_element_info.is_conditional_element,
1179 }
1180 .into(),
1181 );
1182 }
1183
1184 fn push_native_item(
1185 &mut self,
1186 rc_item: &ElementRc,
1187 child_offset: u32,
1188 parent_index: u32,
1189 _component_state: &Self::SubComponentState,
1190 ) {
1191 let item = rc_item.borrow();
1192 let rt = RTTI.with(|rtti| {
1193 rtti.get(&*item.base_type.as_native().class_name)
1194 .unwrap_or_else(|| {
1195 panic!(
1196 "Native type not registered: {}",
1197 item.base_type.as_native().class_name
1198 )
1199 })
1200 .clone()
1201 });
1202
1203 let offset = self.type_builder.add_field(rt.type_info);
1204
1205 self.tree_array.push(ItemTreeNode::Item {
1206 is_accessible: !item.accessibility_props.0.is_empty(),
1207 children_index: child_offset,
1208 children_count: item.children.len() as u32,
1209 parent_index,
1210 item_array_index: self.item_array.len() as u32,
1211 });
1212 self.item_array.push(unsafe { vtable::VOffset::from_raw(rt.vtable, offset) });
1213 self.original_elements.push(rc_item.clone());
1214 debug_assert_eq!(self.original_elements.len(), self.tree_array.len());
1215 self.items_types.insert(
1216 item.id.clone(),
1217 ItemWithinItemTree { offset, rtti: rt, elem: rc_item.clone() },
1218 );
1219 for (prop, expr) in &item.change_callbacks {
1220 self.change_callbacks.push((
1221 NamedReference::new(rc_item, prop.clone()),
1222 Expression::CodeBlock(expr.borrow().clone()),
1223 ));
1224 }
1225 }
1226
1227 fn enter_component(
1228 &mut self,
1229 _item: &ElementRc,
1230 _sub_component: &Rc<object_tree::Component>,
1231 _children_offset: u32,
1232 _component_state: &Self::SubComponentState,
1233 ) -> Self::SubComponentState {
1234 }
1236
1237 fn enter_component_children(
1238 &mut self,
1239 _item: &ElementRc,
1240 _repeater_count: u32,
1241 _component_state: &Self::SubComponentState,
1242 _sub_component_state: &Self::SubComponentState,
1243 ) {
1244 todo!()
1245 }
1246 }
1247
1248 let mut builder = TreeBuilder {
1249 tree_array: Vec::new(),
1250 item_array: Vec::new(),
1251 original_elements: Vec::new(),
1252 items_types: HashMap::new(),
1253 type_builder: dynamic_type::TypeBuilder::new(guard),
1254 repeater: Vec::new(),
1255 repeater_names: HashMap::new(),
1256 change_callbacks: Vec::new(),
1257 popup_menu_description,
1258 };
1259
1260 if !component.is_global() {
1261 generator::build_item_tree(component, &(), &mut builder);
1262 } else {
1263 for (prop, expr) in component.root_element.borrow().change_callbacks.iter() {
1264 builder.change_callbacks.push((
1265 NamedReference::new(&component.root_element, prop.clone()),
1266 Expression::CodeBlock(expr.borrow().clone()),
1267 ));
1268 }
1269 }
1270
1271 let mut custom_properties = HashMap::new();
1272 let mut custom_callbacks = HashMap::new();
1273 fn property_info<T>() -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1274 where
1275 T: PartialEq + Clone + Default + std::convert::TryInto<Value> + 'static,
1276 Value: std::convert::TryInto<T>,
1277 {
1278 (
1280 Box::new(unsafe {
1281 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0)
1282 }),
1283 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1284 )
1285 }
1286 fn animated_property_info<T>()
1287 -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1288 where
1289 T: Clone + Default + InterpolatedPropertyValue + std::convert::TryInto<Value> + 'static,
1290 Value: std::convert::TryInto<T>,
1291 {
1292 (
1294 Box::new(unsafe {
1295 rtti::MaybeAnimatedPropertyInfoWrapper(
1296 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0),
1297 )
1298 }),
1299 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1300 )
1301 }
1302
1303 fn property_info_for_type(
1304 ty: &Type,
1305 name: &str,
1306 ) -> Option<(Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)> {
1307 Some(match ty {
1308 Type::Float32 => animated_property_info::<f32>(),
1309 Type::Int32 => animated_property_info::<i32>(),
1310 Type::String => property_info::<SharedString>(),
1311 Type::Color => animated_property_info::<Color>(),
1312 Type::Brush => animated_property_info::<Brush>(),
1313 Type::Duration => animated_property_info::<i64>(),
1314 Type::Angle => animated_property_info::<f32>(),
1315 Type::PhysicalLength => animated_property_info::<f32>(),
1316 Type::LogicalLength => animated_property_info::<f32>(),
1317 Type::Rem => animated_property_info::<f32>(),
1318 Type::Image => property_info::<i_slint_core::graphics::Image>(),
1319 Type::Bool => property_info::<bool>(),
1320 Type::ComponentFactory => property_info::<ComponentFactory>(),
1321 Type::Struct(s)
1322 if matches!(
1323 s.name,
1324 StructName::BuiltinPrivate(BuiltinPrivateStruct::StateInfo)
1325 ) =>
1326 {
1327 property_info::<i_slint_core::properties::StateInfo>()
1328 }
1329 Type::Struct(_) => property_info::<Value>(),
1330 Type::Array(_) => property_info::<Value>(),
1331 Type::Easing => property_info::<i_slint_core::animations::EasingCurve>(),
1332 Type::Percent => animated_property_info::<f32>(),
1333 Type::Enumeration(e) => {
1334 macro_rules! match_enum_type {
1335 ($( $(#[$enum_doc:meta])* enum $Name:ident { $($body:tt)* })*) => {
1336 match e.name.as_str() {
1337 $(
1338 stringify!($Name) => property_info::<i_slint_core::items::$Name>(),
1339 )*
1340 x => unreachable!("Unknown non-builtin enum {x}"),
1341 }
1342 }
1343 }
1344 if e.node.is_some() {
1345 property_info::<Value>()
1346 } else {
1347 i_slint_common::for_each_enums!(match_enum_type)
1348 }
1349 }
1350 Type::Keys => property_info::<Keys>(),
1351 Type::LayoutCache => property_info::<SharedVector<f32>>(),
1352 Type::ArrayOfU16 => property_info::<SharedVector<u16>>(),
1353 Type::Function { .. } | Type::Callback { .. } => return None,
1354 Type::StyledText => property_info::<StyledText>(),
1355 Type::Invalid
1357 | Type::Void
1358 | Type::InferredProperty
1359 | Type::InferredCallback
1360 | Type::Model
1361 | Type::PathData
1362 | Type::UnitProduct(_)
1363 | Type::ElementReference => panic!("bad type {ty:?} for property {name}"),
1364 })
1365 }
1366
1367 for (name, decl) in &component.root_element.borrow().property_declarations {
1368 if decl.is_alias.is_some() {
1369 continue;
1370 }
1371 if matches!(&decl.property_type, Type::Callback { .. }) {
1372 custom_callbacks
1373 .insert(name.clone(), builder.type_builder.add_field_type::<Callback>());
1374 continue;
1375 }
1376 let Some((prop, type_info)) = property_info_for_type(&decl.property_type, name) else {
1377 continue;
1378 };
1379 custom_properties.insert(
1380 name.clone(),
1381 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1382 );
1383 }
1384 if let Some(parent_element) = component.parent_element()
1385 && let Some(r) = &parent_element.borrow().repeated
1386 && !r.is_conditional_element
1387 {
1388 let (prop, type_info) = property_info::<u32>();
1389 custom_properties.insert(
1390 SPECIAL_PROPERTY_INDEX.into(),
1391 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1392 );
1393
1394 let model_ty = Expression::RepeaterModelReference {
1395 element: component.parent_element.borrow().clone(),
1396 }
1397 .ty();
1398 let (prop, type_info) =
1399 property_info_for_type(&model_ty, SPECIAL_PROPERTY_MODEL_DATA).unwrap();
1400 custom_properties.insert(
1401 SPECIAL_PROPERTY_MODEL_DATA.into(),
1402 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1403 );
1404 }
1405
1406 let parent_item_tree_offset = if component.parent_element().is_some() || is_popup_menu_impl {
1407 Some(builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>())
1408 } else {
1409 None
1410 };
1411
1412 let root_offset = builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>();
1413 let extra_data_offset = builder.type_builder.add_field_type::<ComponentExtraData>();
1414
1415 let change_trackers = (!builder.change_callbacks.is_empty()).then(|| {
1416 (
1417 builder.type_builder.add_field_type::<OnceCell<Vec<ChangeTracker>>>(),
1418 builder.change_callbacks,
1419 )
1420 });
1421 let timers = component
1422 .timers
1423 .borrow()
1424 .iter()
1425 .map(|_| builder.type_builder.add_field_type::<Timer>())
1426 .collect();
1427
1428 let public_properties = if component.parent_element().is_none() {
1430 component.root_element.borrow().property_declarations.clone()
1431 } else {
1432 Default::default()
1433 };
1434
1435 let t = ItemTreeVTable {
1436 visit_children_item,
1437 layout_info,
1438 get_item_ref,
1439 get_item_tree,
1440 get_subtree_range,
1441 get_subtree,
1442 parent_node,
1443 embed_component,
1444 subtree_index,
1445 item_geometry,
1446 accessible_role,
1447 accessible_string_property,
1448 accessibility_action,
1449 supported_accessibility_actions,
1450 item_element_infos,
1451 window_adapter,
1452 drop_in_place,
1453 dealloc,
1454 };
1455 let t = ItemTreeDescription {
1456 ct: t,
1457 dynamic_type: builder.type_builder.build(),
1458 item_tree: builder.tree_array,
1459 item_array: builder.item_array,
1460 items: builder.items_types,
1461 custom_properties,
1462 custom_callbacks,
1463 original: component.clone(),
1464 original_elements: builder.original_elements,
1465 repeater: builder.repeater,
1466 repeater_names: builder.repeater_names,
1467 parent_item_tree_offset,
1468 root_offset,
1469 extra_data_offset,
1470 public_properties,
1471 compiled_globals,
1472 change_trackers,
1473 timers,
1474 popup_ids: std::cell::RefCell::new(HashMap::new()),
1475 popup_menu_description: builder.popup_menu_description,
1476 #[cfg(feature = "internal-highlight")]
1477 type_loader: std::cell::OnceCell::new(),
1478 #[cfg(feature = "internal-highlight")]
1479 raw_type_loader: std::cell::OnceCell::new(),
1480 debug_handler: std::cell::RefCell::new(Rc::new(|_, text| {
1481 i_slint_core::debug_log!("{text}")
1482 })),
1483 };
1484
1485 Rc::new(t)
1486}
1487
1488pub fn animation_for_property(
1489 component: InstanceRef,
1490 animation: &Option<i_slint_compiler::object_tree::PropertyAnimation>,
1491) -> AnimatedBindingKind {
1492 match animation {
1493 Some(i_slint_compiler::object_tree::PropertyAnimation::Static(anim_elem)) => {
1494 AnimatedBindingKind::Animation(Box::new({
1495 let component_ptr = component.as_ptr();
1496 let vtable = NonNull::from(&component.description.ct).cast();
1497 let anim_elem = Rc::clone(anim_elem);
1498 move || -> PropertyAnimation {
1499 generativity::make_guard!(guard);
1500 let component = unsafe {
1501 InstanceRef::from_pin_ref(
1502 Pin::new_unchecked(vtable::VRef::from_raw(
1503 vtable,
1504 NonNull::new_unchecked(component_ptr as *mut u8),
1505 )),
1506 guard,
1507 )
1508 };
1509
1510 eval::new_struct_with_bindings(
1511 &anim_elem.borrow().bindings,
1512 &mut eval::EvalLocalContext::from_component_instance(component),
1513 )
1514 }
1515 }))
1516 }
1517 Some(i_slint_compiler::object_tree::PropertyAnimation::Transition {
1518 animations,
1519 state_ref,
1520 }) => {
1521 let component_ptr = component.as_ptr();
1522 let vtable = NonNull::from(&component.description.ct).cast();
1523 let animations = animations.clone();
1524 let state_ref = state_ref.clone();
1525 AnimatedBindingKind::Transition(Box::new(
1526 move || -> (PropertyAnimation, i_slint_core::animations::Instant) {
1527 generativity::make_guard!(guard);
1528 let component = unsafe {
1529 InstanceRef::from_pin_ref(
1530 Pin::new_unchecked(vtable::VRef::from_raw(
1531 vtable,
1532 NonNull::new_unchecked(component_ptr as *mut u8),
1533 )),
1534 guard,
1535 )
1536 };
1537
1538 let mut context = eval::EvalLocalContext::from_component_instance(component);
1539 let state = eval::eval_expression(&state_ref, &mut context);
1540 let state_info: i_slint_core::properties::StateInfo = state.try_into().unwrap();
1541 for a in &animations {
1542 let is_previous_state = a.state_id == state_info.previous_state;
1543 let is_current_state = a.state_id == state_info.current_state;
1544 match (a.direction, is_previous_state, is_current_state) {
1545 (TransitionDirection::In, false, true)
1546 | (TransitionDirection::Out, true, false)
1547 | (TransitionDirection::InOut, false, true)
1548 | (TransitionDirection::InOut, true, false) => {
1549 return (
1550 eval::new_struct_with_bindings(
1551 &a.animation.borrow().bindings,
1552 &mut context,
1553 ),
1554 state_info.change_time,
1555 );
1556 }
1557 _ => {}
1558 }
1559 }
1560 Default::default()
1561 },
1562 ))
1563 }
1564 None => AnimatedBindingKind::NotAnimated,
1565 }
1566}
1567
1568fn make_callback_eval_closure(
1569 expr: Expression,
1570 self_weak: ErasedItemTreeBoxWeak,
1571) -> impl Fn(&[Value]) -> Value {
1572 move |args| {
1573 let self_rc = self_weak.upgrade().unwrap();
1574 generativity::make_guard!(guard);
1575 let self_ = self_rc.unerase(guard);
1576 let instance_ref = self_.borrow_instance();
1577 let mut local_context =
1578 eval::EvalLocalContext::from_function_arguments(instance_ref, args.to_vec());
1579 eval::eval_expression(&expr, &mut local_context)
1580 }
1581}
1582
1583fn make_binding_eval_closure(
1584 expr: Expression,
1585 self_weak: ErasedItemTreeBoxWeak,
1586) -> impl Fn() -> Value {
1587 move || {
1588 let self_rc = self_weak.upgrade().unwrap();
1589 generativity::make_guard!(guard);
1590 let self_ = self_rc.unerase(guard);
1591 let instance_ref = self_.borrow_instance();
1592 eval::eval_expression(
1593 &expr,
1594 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1595 )
1596 }
1597}
1598
1599pub fn instantiate(
1600 description: Rc<ItemTreeDescription>,
1601 parent_ctx: Option<ErasedItemTreeBoxWeak>,
1602 root: Option<ErasedItemTreeBoxWeak>,
1603 window_options: Option<&WindowOptions>,
1604 globals: crate::global_component::GlobalStorage,
1605) -> DynamicComponentVRc {
1606 let instance = description.dynamic_type.clone().create_instance();
1607
1608 let component_box = ItemTreeBox { instance, description: description.clone() };
1609
1610 let self_rc = vtable::VRc::new(ErasedItemTreeBox::from(component_box));
1611 let self_weak = vtable::VRc::downgrade(&self_rc);
1612
1613 generativity::make_guard!(guard);
1614 let comp = self_rc.unerase(guard);
1615 let instance_ref = comp.borrow_instance();
1616 instance_ref.self_weak().set(self_weak.clone()).ok();
1617 let description = comp.description();
1618
1619 if let Some(WindowOptions::UseExistingWindow(existing_adapter)) = &window_options
1620 && let Err((a, b)) = globals.window_adapter().unwrap().try_insert(existing_adapter.clone())
1621 {
1622 assert!(Rc::ptr_eq(a, &b), "window not the same as parent window");
1623 }
1624
1625 if let Some(parent) = parent_ctx {
1626 description
1627 .parent_item_tree_offset
1628 .unwrap()
1629 .apply(instance_ref.as_ref())
1630 .set(parent)
1631 .ok()
1632 .unwrap();
1633 } else if let Some(g) = description.compiled_globals.as_ref() {
1634 for g in g.compiled_globals.iter() {
1635 crate::global_component::instantiate(g, &globals, self_weak.clone());
1636 }
1637 }
1638 let extra_data = description.extra_data_offset.apply(instance_ref.as_ref());
1639 extra_data.globals.set(globals).ok().unwrap();
1640 if let Some(WindowOptions::Embed { parent_item_tree, parent_item_tree_index }) = window_options
1641 {
1642 vtable::VRc::borrow_pin(&self_rc)
1643 .as_ref()
1644 .embed_component(parent_item_tree, *parent_item_tree_index);
1645 description.root_offset.apply(instance_ref.as_ref()).set(self_weak.clone()).ok().unwrap();
1646 } else {
1647 generativity::make_guard!(guard);
1648 let root = root
1649 .or_else(|| {
1650 instance_ref.parent_instance(guard).map(|parent| parent.root_weak().clone())
1651 })
1652 .unwrap_or_else(|| self_weak.clone());
1653 description.root_offset.apply(instance_ref.as_ref()).set(root).ok().unwrap();
1654 }
1655
1656 if !description.original.is_global() {
1657 let maybe_window_adapter =
1658 if let Some(WindowOptions::UseExistingWindow(adapter)) = window_options.as_ref() {
1659 Some(adapter.clone())
1660 } else {
1661 instance_ref.maybe_window_adapter()
1662 };
1663
1664 let component_rc = vtable::VRc::into_dyn(self_rc.clone());
1665 i_slint_core::item_tree::register_item_tree(&component_rc, maybe_window_adapter);
1666 }
1667
1668 for (prop_name, decl) in &description.original.root_element.borrow().property_declarations {
1670 if !matches!(
1671 decl.property_type,
1672 Type::Struct { .. } | Type::Array(_) | Type::Enumeration(_)
1673 ) || decl.is_alias.is_some()
1674 {
1675 continue;
1676 }
1677 if let Some(b) = description.original.root_element.borrow().bindings.get(prop_name)
1678 && b.borrow().two_way_bindings.is_empty()
1679 {
1680 continue;
1681 }
1682 let p = description.custom_properties.get(prop_name).unwrap();
1683 unsafe {
1684 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(p.offset));
1685 p.prop.set(item, eval::default_value_for_type(&decl.property_type), None).unwrap();
1686 }
1687 }
1688
1689 #[cfg(slint_debug_property)]
1690 {
1691 let component_id = description.original.id.as_str();
1692
1693 for (prop_name, prop_info) in &description.custom_properties {
1695 let name = format!("{}.{}", component_id, prop_name);
1696 unsafe {
1697 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(prop_info.offset));
1698 prop_info.prop.set_debug_name(item, name);
1699 }
1700 }
1701
1702 for (item_name, item_within_component) in &description.items {
1704 let item = unsafe { item_within_component.item_from_item_tree(instance_ref.as_ptr()) };
1705 for (prop_name, prop_rtti) in &item_within_component.rtti.properties {
1706 let name = format!("{}::{}.{}", component_id, item_name, prop_name);
1707 prop_rtti.set_debug_name(item, name);
1708 }
1709 }
1710 }
1711
1712 generator::handle_property_bindings_init(
1713 &description.original,
1714 |elem, prop_name, binding| unsafe {
1715 let is_root = Rc::ptr_eq(
1716 elem,
1717 &elem.borrow().enclosing_component.upgrade().unwrap().root_element,
1718 );
1719 let elem = elem.borrow();
1720 let is_const = binding.analysis.as_ref().is_some_and(|a| a.is_const);
1721
1722 let property_type = elem.lookup_property(prop_name).property_type;
1723 if let Type::Function { .. } = property_type {
1724 } else if let Type::Callback { .. } = property_type {
1726 if !matches!(binding.expression, Expression::Invalid) {
1727 let expr = binding.expression.clone();
1728 let description = description.clone();
1729 if let Some(callback_offset) =
1730 description.custom_callbacks.get(prop_name).filter(|_| is_root)
1731 {
1732 let callback = callback_offset.apply(instance_ref.as_ref());
1733 callback.set_handler(make_callback_eval_closure(expr, self_weak.clone()));
1734 } else {
1735 let item_within_component = &description.items[&elem.id];
1736 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1737 if let Some(callback) =
1738 item_within_component.rtti.callbacks.get(prop_name.as_str())
1739 {
1740 callback.set_handler(
1741 item,
1742 Box::new(make_callback_eval_closure(expr, self_weak.clone())),
1743 );
1744 } else {
1745 panic!("unknown callback {prop_name}")
1746 }
1747 }
1748 }
1749 } else if let Some(PropertiesWithinComponent { offset, prop: prop_info, .. }) =
1750 description.custom_properties.get(prop_name).filter(|_| is_root)
1751 {
1752 let is_state_info = matches!(&property_type, Type::Struct (s) if matches!(s.name, StructName::BuiltinPrivate(BuiltinPrivateStruct::StateInfo)));
1753 if is_state_info {
1754 let prop = Pin::new_unchecked(
1755 &*(instance_ref.as_ptr().add(*offset)
1756 as *const Property<i_slint_core::properties::StateInfo>),
1757 );
1758 let e = binding.expression.clone();
1759 let state_binding = make_binding_eval_closure(e, self_weak.clone());
1760 i_slint_core::properties::set_state_binding(prop, move || {
1761 state_binding().try_into().unwrap()
1762 });
1763 return;
1764 }
1765
1766 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1767 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset));
1768
1769 if !matches!(binding.expression, Expression::Invalid) {
1770 if is_const {
1771 let v = eval::eval_expression(
1772 &binding.expression,
1773 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1774 );
1775 prop_info.set(item, v, None).unwrap();
1776 } else {
1777 let e = binding.expression.clone();
1778 prop_info
1779 .set_binding(
1780 item,
1781 Box::new(make_binding_eval_closure(e, self_weak.clone())),
1782 maybe_animation,
1783 )
1784 .unwrap();
1785 }
1786 }
1787 for twb in &binding.two_way_bindings {
1788 match twb {
1789 TwoWayBinding::Property { property, field_access }
1790 if field_access.is_empty()
1791 && !matches!(
1792 &property_type,
1793 Type::Struct(..) | Type::Array(..)
1794 ) =>
1795 {
1796 prop_info.link_two_ways(item, get_property_ptr(property, instance_ref));
1799 }
1800 TwoWayBinding::Property { property, field_access } => {
1801 let (common, map) =
1802 prepare_for_two_way_binding(instance_ref, property, field_access);
1803 prop_info.link_two_way_with_map(item, common, map);
1804 }
1805 TwoWayBinding::ModelData { repeated_element, field_access } => {
1806 let (getter, setter) = prepare_model_two_way_binding(
1807 instance_ref,
1808 repeated_element,
1809 field_access,
1810 );
1811 prop_info.link_two_way_to_model_data(item, getter, setter);
1812 }
1813 }
1814 }
1815 } else {
1816 let item_within_component = &description.items[&elem.id];
1817 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1818 if let Some(prop_rtti) =
1819 item_within_component.rtti.properties.get(prop_name.as_str())
1820 {
1821 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1822
1823 for twb in &binding.two_way_bindings {
1824 match twb {
1825 TwoWayBinding::Property { property, field_access }
1826 if field_access.is_empty()
1827 && !matches!(
1828 &property_type,
1829 Type::Struct(..) | Type::Array(..)
1830 ) =>
1831 {
1832 prop_rtti
1835 .link_two_ways(item, get_property_ptr(property, instance_ref));
1836 }
1837 TwoWayBinding::Property { property, field_access } => {
1838 let (common, map) = prepare_for_two_way_binding(
1839 instance_ref,
1840 property,
1841 field_access,
1842 );
1843 prop_rtti.link_two_way_with_map(item, common, map);
1844 }
1845 TwoWayBinding::ModelData { repeated_element, field_access } => {
1846 let (getter, setter) = prepare_model_two_way_binding(
1847 instance_ref,
1848 repeated_element,
1849 field_access,
1850 );
1851 prop_rtti.link_two_way_to_model_data(item, getter, setter);
1852 }
1853 }
1854 }
1855 if !matches!(binding.expression, Expression::Invalid) {
1856 if is_const {
1857 prop_rtti
1858 .set(
1859 item,
1860 eval::eval_expression(
1861 &binding.expression,
1862 &mut eval::EvalLocalContext::from_component_instance(
1863 instance_ref,
1864 ),
1865 ),
1866 maybe_animation.as_animation(),
1867 )
1868 .unwrap();
1869 } else {
1870 let e = binding.expression.clone();
1871 prop_rtti.set_binding(
1872 item,
1873 Box::new(make_binding_eval_closure(e, self_weak.clone())),
1874 maybe_animation,
1875 );
1876 }
1877 }
1878 } else {
1879 panic!("unknown property {} in {}", prop_name, elem.id);
1880 }
1881 }
1882 },
1883 );
1884
1885 for rep_in_comp in &description.repeater {
1886 generativity::make_guard!(guard);
1887 let rep_in_comp = rep_in_comp.unerase(guard);
1888
1889 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
1890 let expr = rep_in_comp.model.clone();
1891 let model_binding_closure = make_binding_eval_closure(expr, self_weak.clone());
1892 if rep_in_comp.is_conditional {
1893 let bool_model = Rc::new(crate::value_model::BoolModel::default());
1894 repeater.set_model_binding(move || {
1895 let v = model_binding_closure();
1896 bool_model.set_value(v.try_into().expect("condition model is bool"));
1897 ModelRc::from(bool_model.clone())
1898 });
1899 } else {
1900 repeater.set_model_binding(move || {
1901 let m = model_binding_closure();
1902 if let Value::Model(m) = m {
1903 m
1904 } else {
1905 ModelRc::new(crate::value_model::ValueModel::new(m))
1906 }
1907 });
1908 }
1909 }
1910 self_rc
1911}
1912
1913fn prepare_for_two_way_binding(
1914 instance_ref: InstanceRef,
1915 property: &NamedReference,
1916 field_access: &[SmolStr],
1917) -> (Pin<Rc<Property<Value>>>, Option<Rc<dyn rtti::TwoWayBindingMapping<Value>>>) {
1918 let element = property.element();
1919 let name = property.name().as_str();
1920
1921 generativity::make_guard!(guard);
1922 let enclosing_component = eval::enclosing_component_instance_for_element(
1923 &element,
1924 &eval::ComponentInstance::InstanceRef(instance_ref),
1925 guard,
1926 );
1927 let map: Option<Rc<dyn rtti::TwoWayBindingMapping<Value>>> = if field_access.is_empty() {
1928 None
1929 } else {
1930 struct FieldAccess(Vec<SmolStr>);
1931 impl rtti::TwoWayBindingMapping<Value> for FieldAccess {
1932 fn map_to(&self, value: &Value) -> Value {
1933 walk_struct_field_path(value.clone(), &self.0).unwrap_or_default()
1934 }
1935 fn map_from(&self, root: &mut Value, from: &Value) {
1936 if let Some(leaf) = walk_struct_field_path_mut(root, &self.0) {
1937 *leaf = from.clone();
1938 }
1939 }
1940 }
1941 Some(Rc::new(FieldAccess(field_access.to_vec())))
1942 };
1943 let common = match enclosing_component {
1944 eval::ComponentInstance::InstanceRef(enclosing_component) => {
1945 let element = element.borrow();
1946 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1947 && let Some(x) = enclosing_component.description.custom_properties.get(name)
1948 {
1949 let item =
1950 unsafe { Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset)) };
1951 let common = x.prop.prepare_for_two_way_binding(item);
1952 return (common, map);
1953 }
1954 let item_info = enclosing_component
1955 .description
1956 .items
1957 .get(element.id.as_str())
1958 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, name));
1959 let prop_info = item_info
1960 .rtti
1961 .properties
1962 .get(name)
1963 .unwrap_or_else(|| panic!("Property {} not in {}", name, element.id));
1964 core::mem::drop(element);
1965 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1966 prop_info.prepare_for_two_way_binding(item)
1967 }
1968 eval::ComponentInstance::GlobalComponent(glob) => {
1969 glob.as_ref().prepare_for_two_way_binding(name).unwrap()
1970 }
1971 };
1972 (common, map)
1973}
1974
1975fn prepare_model_two_way_binding(
1979 instance_ref: InstanceRef,
1980 repeated_element: &i_slint_compiler::object_tree::ElementWeak,
1981 field_access: &[SmolStr],
1982) -> (Box<dyn Fn() -> Option<Value>>, Box<dyn Fn(&Value)>) {
1983 let self_weak = instance_ref.self_weak().get().unwrap().clone();
1984 let repeated_element = repeated_element.clone();
1985 let field_access: Vec<SmolStr> = field_access.to_vec();
1986
1987 let getter = {
1988 let self_weak = self_weak.clone();
1989 let repeated_element = repeated_element.clone();
1990 let field_access = field_access.clone();
1991 Box::new(move || -> Option<Value> {
1992 with_repeater_row(&self_weak, &repeated_element, |repeater, row| {
1993 walk_struct_field_path(repeater.model_row_data(row)?, &field_access)
1994 })
1995 })
1996 };
1997
1998 let setter = Box::new(move |new_value: &Value| {
1999 with_repeater_row(&self_weak, &repeated_element, |repeater, row| {
2000 let mut data = repeater.model_row_data(row)?;
2001 let leaf = walk_struct_field_path_mut(&mut data, &field_access)?;
2003 if &*leaf == new_value {
2004 return Some(());
2005 }
2006 *leaf = new_value.clone();
2007 repeater.model_set_row_data(row, data);
2008 Some(())
2009 });
2010 });
2011
2012 (getter, setter)
2013}
2014
2015fn with_repeater_row<R>(
2018 self_weak: &ErasedItemTreeBoxWeak,
2019 repeated_element: &i_slint_compiler::object_tree::ElementWeak,
2020 f: impl FnOnce(Pin<&Repeater<ErasedItemTreeBox>>, usize) -> Option<R>,
2021) -> Option<R> {
2022 let self_rc = self_weak.upgrade()?;
2023 generativity::make_guard!(guard);
2024 let s = self_rc.unerase(guard);
2025 let instance = s.borrow_instance();
2026 let element = repeated_element.upgrade()?;
2027 let index = crate::eval::load_property(
2028 instance,
2029 &element.borrow().base_type.as_component().root_element,
2030 crate::dynamic_item_tree::SPECIAL_PROPERTY_INDEX,
2031 )
2032 .ok()?;
2033 let row = usize::try_from(i32::try_from(index).ok()?).ok()?;
2034 generativity::make_guard!(guard);
2035 let enclosing = crate::eval::enclosing_component_for_element(&element, instance, guard);
2036 generativity::make_guard!(guard);
2037 let (repeater, _) = get_repeater_by_name(enclosing, element.borrow().id.as_str(), guard);
2038 f(repeater, row)
2039}
2040
2041fn walk_struct_field_path(mut value: Value, fields: &[SmolStr]) -> Option<Value> {
2043 for f in fields {
2044 match value {
2045 Value::Struct(o) => value = o.get_field(f).cloned().unwrap_or_default(),
2046 Value::Void => return None,
2047 _ => return None,
2048 }
2049 }
2050 Some(value)
2051}
2052
2053fn walk_struct_field_path_mut<'a>(
2055 mut value: &'a mut Value,
2056 fields: &[SmolStr],
2057) -> Option<&'a mut Value> {
2058 for f in fields {
2059 match value {
2060 Value::Struct(o) => value = o.0.get_mut(f)?,
2061 _ => return None,
2062 }
2063 }
2064 Some(value)
2065}
2066
2067pub(crate) fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const () {
2068 let element = nr.element();
2069 generativity::make_guard!(guard);
2070 let enclosing_component = eval::enclosing_component_instance_for_element(
2071 &element,
2072 &eval::ComponentInstance::InstanceRef(instance),
2073 guard,
2074 );
2075 match enclosing_component {
2076 eval::ComponentInstance::InstanceRef(enclosing_component) => {
2077 let element = element.borrow();
2078 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
2079 && let Some(x) = enclosing_component.description.custom_properties.get(nr.name())
2080 {
2081 return unsafe { enclosing_component.as_ptr().add(x.offset).cast() };
2082 };
2083 let item_info = enclosing_component
2084 .description
2085 .items
2086 .get(element.id.as_str())
2087 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, nr.name()));
2088 let prop_info = item_info
2089 .rtti
2090 .properties
2091 .get(nr.name().as_str())
2092 .unwrap_or_else(|| panic!("Property {} not in {}", nr.name(), element.id));
2093 core::mem::drop(element);
2094 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
2095 unsafe { item.as_ptr().add(prop_info.offset()).cast() }
2096 }
2097 eval::ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property_ptr(nr.name()),
2098 }
2099}
2100
2101pub struct ErasedItemTreeBox(ItemTreeBox<'static>);
2102impl ErasedItemTreeBox {
2103 pub fn unerase<'a, 'id>(
2104 &'a self,
2105 _guard: generativity::Guard<'id>,
2106 ) -> Pin<&'a ItemTreeBox<'id>> {
2107 Pin::new(
2108 unsafe { core::mem::transmute::<&ItemTreeBox<'static>, &ItemTreeBox<'id>>(&self.0) },
2110 )
2111 }
2112
2113 pub fn borrow(&self) -> ItemTreeRefPin<'_> {
2114 self.0.borrow()
2116 }
2117
2118 pub fn window_adapter_ref(&self) -> Result<&WindowAdapterRc, PlatformError> {
2119 self.0.window_adapter_ref()
2120 }
2121
2122 pub fn run_setup_code(&self) {
2123 generativity::make_guard!(guard);
2124 let compo_box = self.unerase(guard);
2125 let instance_ref = compo_box.borrow_instance();
2126 for extra_init_code in self.0.description.original.init_code.borrow().iter() {
2127 eval::eval_expression(
2128 extra_init_code,
2129 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
2130 );
2131 }
2132 if let Some(cts) = instance_ref.description.change_trackers.as_ref() {
2133 let self_weak = instance_ref.self_weak().get().unwrap();
2134 let v = cts
2135 .1
2136 .iter()
2137 .enumerate()
2138 .map(|(idx, _)| {
2139 let ct = ChangeTracker::default();
2140 ct.init(
2141 self_weak.clone(),
2142 move |self_weak| {
2143 let s = self_weak.upgrade().unwrap();
2144 generativity::make_guard!(guard);
2145 let compo_box = s.unerase(guard);
2146 let instance_ref = compo_box.borrow_instance();
2147 let nr = &s.0.description.change_trackers.as_ref().unwrap().1[idx].0;
2148 eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap()
2149 },
2150 move |self_weak, _| {
2151 let s = self_weak.upgrade().unwrap();
2152 generativity::make_guard!(guard);
2153 let compo_box = s.unerase(guard);
2154 let instance_ref = compo_box.borrow_instance();
2155 let e = &s.0.description.change_trackers.as_ref().unwrap().1[idx].1;
2156 eval::eval_expression(
2157 e,
2158 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
2159 );
2160 },
2161 );
2162 ct
2163 })
2164 .collect::<Vec<_>>();
2165 cts.0
2166 .apply_pin(instance_ref.instance)
2167 .set(v)
2168 .unwrap_or_else(|_| panic!("run_setup_code called twice?"));
2169 }
2170 update_timers(instance_ref);
2171 }
2172}
2173impl<'id> From<ItemTreeBox<'id>> for ErasedItemTreeBox {
2174 fn from(inner: ItemTreeBox<'id>) -> Self {
2175 unsafe {
2178 ErasedItemTreeBox(core::mem::transmute::<ItemTreeBox<'id>, ItemTreeBox<'static>>(inner))
2179 }
2180 }
2181}
2182
2183pub fn get_repeater_by_name<'a, 'id>(
2184 instance_ref: InstanceRef<'a, '_>,
2185 name: &str,
2186 guard: generativity::Guard<'id>,
2187) -> (std::pin::Pin<&'a Repeater<ErasedItemTreeBox>>, Rc<ItemTreeDescription<'id>>) {
2188 let rep_index = instance_ref.description.repeater_names[name];
2189 let rep_in_comp = instance_ref.description.repeater[rep_index].unerase(guard);
2190 (rep_in_comp.offset.apply_pin(instance_ref.instance), rep_in_comp.item_tree_to_repeat.clone())
2191}
2192
2193#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2194extern "C" fn layout_info(component: ItemTreeRefPin, orientation: Orientation) -> LayoutInfo {
2195 generativity::make_guard!(guard);
2196 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2198 let orientation = crate::eval_layout::from_runtime(orientation);
2199
2200 let mut result = crate::eval_layout::get_layout_info(
2201 &instance_ref.description.original.root_element,
2202 instance_ref,
2203 &instance_ref.window_adapter(),
2204 orientation,
2205 );
2206
2207 let constraints = instance_ref.description.original.root_constraints.borrow();
2208 if constraints.has_explicit_restrictions(orientation) {
2209 crate::eval_layout::fill_layout_info_constraints(
2210 &mut result,
2211 &constraints,
2212 orientation,
2213 &|nr: &NamedReference| {
2214 eval::load_property(instance_ref, &nr.element(), nr.name())
2215 .unwrap()
2216 .try_into()
2217 .unwrap()
2218 },
2219 );
2220 }
2221 result
2222}
2223
2224#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2225unsafe extern "C" fn get_item_ref(component: ItemTreeRefPin, index: u32) -> Pin<ItemRef> {
2226 let tree = get_item_tree(component);
2227 match &tree[index as usize] {
2228 ItemTreeNode::Item { item_array_index, .. } => unsafe {
2229 generativity::make_guard!(guard);
2230 let instance_ref = InstanceRef::from_pin_ref(component, guard);
2231 core::mem::transmute::<Pin<ItemRef>, Pin<ItemRef>>(
2232 instance_ref.description.item_array[*item_array_index as usize]
2233 .apply_pin(instance_ref.instance),
2234 )
2235 },
2236 ItemTreeNode::DynamicTree { .. } => panic!("get_item_ref called on dynamic tree"),
2237 }
2238}
2239
2240#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2241extern "C" fn get_subtree_range(component: ItemTreeRefPin, index: u32) -> IndexRange {
2242 generativity::make_guard!(guard);
2243 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2244 if index as usize >= instance_ref.description.repeater.len() {
2245 let container_index = {
2246 let tree_node = &component.as_ref().get_item_tree()[index as usize];
2247 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
2248 *parent_index
2249 } else {
2250 u32::MAX
2251 }
2252 };
2253 let container = component.as_ref().get_item_ref(container_index);
2254 let container = i_slint_core::items::ItemRef::downcast_pin::<
2255 i_slint_core::items::ComponentContainer,
2256 >(container)
2257 .unwrap();
2258 container.ensure_updated();
2259 container.subtree_range()
2260 } else {
2261 let rep_in_comp =
2262 unsafe { instance_ref.description.repeater[index as usize].get_untagged() };
2263 ensure_repeater_updated(instance_ref, rep_in_comp);
2264
2265 let repeater = rep_in_comp.offset.apply(&instance_ref.instance);
2266 repeater.range().into()
2267 }
2268}
2269
2270#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2271extern "C" fn get_subtree(
2272 component: ItemTreeRefPin,
2273 index: u32,
2274 subtree_index: usize,
2275 result: &mut ItemTreeWeak,
2276) {
2277 generativity::make_guard!(guard);
2278 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2279 if index as usize >= instance_ref.description.repeater.len() {
2280 let container_index = {
2281 let tree_node = &component.as_ref().get_item_tree()[index as usize];
2282 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
2283 *parent_index
2284 } else {
2285 u32::MAX
2286 }
2287 };
2288 let container = component.as_ref().get_item_ref(container_index);
2289 let container = i_slint_core::items::ItemRef::downcast_pin::<
2290 i_slint_core::items::ComponentContainer,
2291 >(container)
2292 .unwrap();
2293 container.ensure_updated();
2294 if subtree_index == 0 {
2295 *result = container.subtree_component();
2296 }
2297 } else {
2298 let rep_in_comp =
2299 unsafe { instance_ref.description.repeater[index as usize].get_untagged() };
2300 ensure_repeater_updated(instance_ref, rep_in_comp);
2301
2302 let repeater = rep_in_comp.offset.apply(&instance_ref.instance);
2303 if let Some(instance_at) = repeater.instance_at(subtree_index) {
2304 *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(instance_at))
2305 }
2306 }
2307}
2308
2309#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2310extern "C" fn get_item_tree(component: ItemTreeRefPin) -> Slice<ItemTreeNode> {
2311 generativity::make_guard!(guard);
2312 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2313 let tree = instance_ref.description.item_tree.as_slice();
2314 unsafe { core::mem::transmute::<&[ItemTreeNode], &[ItemTreeNode]>(tree) }.into()
2315}
2316
2317#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2318extern "C" fn subtree_index(component: ItemTreeRefPin) -> usize {
2319 generativity::make_guard!(guard);
2320 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2321 if let Ok(value) = instance_ref.description.get_property(component, SPECIAL_PROPERTY_INDEX) {
2322 value.try_into().unwrap()
2323 } else {
2324 usize::MAX
2325 }
2326}
2327
2328#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2329unsafe extern "C" fn parent_node(component: ItemTreeRefPin, result: &mut ItemWeak) {
2330 generativity::make_guard!(guard);
2331 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2332
2333 let component_and_index = {
2334 if let Some(parent_offset) = instance_ref.description.parent_item_tree_offset {
2336 let parent_item_index = instance_ref
2337 .description
2338 .original
2339 .parent_element
2340 .borrow()
2341 .upgrade()
2342 .and_then(|e| e.borrow().item_index.get().cloned())
2343 .unwrap_or(u32::MAX);
2344 let parent_component = parent_offset
2345 .apply(instance_ref.as_ref())
2346 .get()
2347 .and_then(|p| p.upgrade())
2348 .map(vtable::VRc::into_dyn);
2349
2350 (parent_component, parent_item_index)
2351 } else if let Some((parent_component, parent_index)) = instance_ref
2352 .description
2353 .extra_data_offset
2354 .apply(instance_ref.as_ref())
2355 .embedding_position
2356 .get()
2357 {
2358 (parent_component.upgrade(), *parent_index)
2359 } else {
2360 (None, u32::MAX)
2361 }
2362 };
2363
2364 if let (Some(component), index) = component_and_index {
2365 *result = ItemRc::new(component, index).downgrade();
2366 }
2367}
2368
2369#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2370unsafe extern "C" fn embed_component(
2371 component: ItemTreeRefPin,
2372 parent_component: &ItemTreeWeak,
2373 parent_item_tree_index: u32,
2374) -> bool {
2375 generativity::make_guard!(guard);
2376 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2377
2378 if instance_ref.description.parent_item_tree_offset.is_some() {
2379 return false;
2381 }
2382
2383 {
2384 let prc = parent_component.upgrade().unwrap();
2386 let pref = vtable::VRc::borrow_pin(&prc);
2387 let it = pref.as_ref().get_item_tree();
2388 if !matches!(
2389 it.get(parent_item_tree_index as usize),
2390 Some(ItemTreeNode::DynamicTree { .. })
2391 ) {
2392 panic!("Trying to embed into a non-dynamic index in the parents item tree")
2393 }
2394 }
2395
2396 let extra_data = instance_ref.description.extra_data_offset.apply(instance_ref.as_ref());
2397 extra_data.embedding_position.set((parent_component.clone(), parent_item_tree_index)).is_ok()
2398}
2399
2400#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2401extern "C" fn item_geometry(component: ItemTreeRefPin, item_index: u32) -> LogicalRect {
2402 generativity::make_guard!(guard);
2403 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2404
2405 let e = instance_ref.description.original_elements[item_index as usize].borrow();
2406 let g = e.geometry_props.as_ref().unwrap();
2407
2408 let load_f32 = |nr: &NamedReference| -> f32 {
2409 crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2410 .unwrap()
2411 .try_into()
2412 .unwrap()
2413 };
2414
2415 LogicalRect {
2416 origin: (load_f32(&g.x), load_f32(&g.y)).into(),
2417 size: (load_f32(&g.width), load_f32(&g.height)).into(),
2418 }
2419}
2420
2421#[allow(improper_ctypes_definitions)]
2423#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2424extern "C" fn accessible_role(component: ItemTreeRefPin, item_index: u32) -> AccessibleRole {
2425 generativity::make_guard!(guard);
2426 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2427 let nr = instance_ref.description.original_elements[item_index as usize]
2428 .borrow()
2429 .accessibility_props
2430 .0
2431 .get("accessible-role")
2432 .cloned();
2433 match nr {
2434 Some(nr) => crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2435 .unwrap()
2436 .try_into()
2437 .unwrap(),
2438 None => AccessibleRole::default(),
2439 }
2440}
2441
2442#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2443extern "C" fn accessible_string_property(
2444 component: ItemTreeRefPin,
2445 item_index: u32,
2446 what: AccessibleStringProperty,
2447 result: &mut SharedString,
2448) -> bool {
2449 generativity::make_guard!(guard);
2450 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2451 let prop_name = format!("accessible-{what}");
2452 let nr = instance_ref.description.original_elements[item_index as usize]
2453 .borrow()
2454 .accessibility_props
2455 .0
2456 .get(&prop_name)
2457 .cloned();
2458 if let Some(nr) = nr {
2459 let value = crate::eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap();
2460 match value {
2461 Value::String(s) => *result = s,
2462 Value::Bool(b) => *result = if b { "true" } else { "false" }.into(),
2463 Value::Number(x) => *result = x.to_string().into(),
2464 Value::EnumerationValue(_, v) => *result = v.into(),
2465 _ => unimplemented!("invalid type for accessible_string_property"),
2466 };
2467 true
2468 } else {
2469 false
2470 }
2471}
2472
2473#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2474extern "C" fn accessibility_action(
2475 component: ItemTreeRefPin,
2476 item_index: u32,
2477 action: &AccessibilityAction,
2478) {
2479 let perform = |prop_name, args: &[Value]| {
2480 generativity::make_guard!(guard);
2481 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2482 let nr = instance_ref.description.original_elements[item_index as usize]
2483 .borrow()
2484 .accessibility_props
2485 .0
2486 .get(prop_name)
2487 .cloned();
2488 if let Some(nr) = nr {
2489 let instance_ref = eval::ComponentInstance::InstanceRef(instance_ref);
2490 crate::eval::invoke_callback(&instance_ref, &nr.element(), nr.name(), args).unwrap();
2491 }
2492 };
2493
2494 match action {
2495 AccessibilityAction::Default => perform("accessible-action-default", &[]),
2496 AccessibilityAction::Decrement => perform("accessible-action-decrement", &[]),
2497 AccessibilityAction::Increment => perform("accessible-action-increment", &[]),
2498 AccessibilityAction::Expand => perform("accessible-action-expand", &[]),
2499 AccessibilityAction::ReplaceSelectedText(_a) => {
2500 i_slint_core::debug_log!(
2502 "AccessibilityAction::ReplaceSelectedText not implemented in interpreter's accessibility_action"
2503 );
2504 }
2505 AccessibilityAction::SetValue(a) => {
2506 perform("accessible-action-set-value", &[Value::String(a.clone())])
2507 }
2508 };
2509}
2510
2511#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2512extern "C" fn supported_accessibility_actions(
2513 component: ItemTreeRefPin,
2514 item_index: u32,
2515) -> SupportedAccessibilityAction {
2516 generativity::make_guard!(guard);
2517 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2518 instance_ref.description.original_elements[item_index as usize]
2519 .borrow()
2520 .accessibility_props
2521 .0
2522 .keys()
2523 .filter_map(|x| x.strip_prefix("accessible-action-"))
2524 .fold(SupportedAccessibilityAction::default(), |acc, value| {
2525 SupportedAccessibilityAction::from_name(&i_slint_compiler::generator::to_pascal_case(
2526 value,
2527 ))
2528 .unwrap_or_else(|| panic!("Not an accessible action: {value:?}"))
2529 | acc
2530 })
2531}
2532
2533#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2534extern "C" fn item_element_infos(
2535 component: ItemTreeRefPin,
2536 item_index: u32,
2537 result: &mut SharedString,
2538) -> bool {
2539 generativity::make_guard!(guard);
2540 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2541 *result = instance_ref.description.original_elements[item_index as usize]
2542 .borrow()
2543 .element_infos()
2544 .into();
2545 true
2546}
2547
2548#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2549extern "C" fn window_adapter(
2550 component: ItemTreeRefPin,
2551 do_create: bool,
2552 result: &mut Option<WindowAdapterRc>,
2553) {
2554 generativity::make_guard!(guard);
2555 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2556 if do_create {
2557 *result = Some(instance_ref.window_adapter());
2558 } else {
2559 *result = instance_ref.maybe_window_adapter();
2560 }
2561}
2562
2563#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2564unsafe extern "C" fn drop_in_place(component: vtable::VRefMut<ItemTreeVTable>) -> vtable::Layout {
2565 unsafe {
2566 let instance_ptr = component.as_ptr() as *mut Instance<'static>;
2567 let layout = (*instance_ptr).type_info().layout();
2568 dynamic_type::TypeInfo::drop_in_place(instance_ptr);
2569 layout.into()
2570 }
2571}
2572
2573#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2574unsafe extern "C" fn dealloc(_vtable: &ItemTreeVTable, ptr: *mut u8, layout: vtable::Layout) {
2575 unsafe { std::alloc::dealloc(ptr, layout.try_into().unwrap()) };
2576}
2577
2578#[derive(Copy, Clone)]
2579pub struct InstanceRef<'a, 'id> {
2580 pub instance: Pin<&'a Instance<'id>>,
2581 pub description: &'a ItemTreeDescription<'id>,
2582}
2583
2584impl<'a, 'id> InstanceRef<'a, 'id> {
2585 pub unsafe fn from_pin_ref(
2586 component: ItemTreeRefPin<'a>,
2587 _guard: generativity::Guard<'id>,
2588 ) -> Self {
2589 unsafe {
2590 Self {
2591 instance: Pin::new_unchecked(
2592 &*(component.as_ref().as_ptr() as *const Instance<'id>),
2593 ),
2594 description: &*(Pin::into_inner_unchecked(component).get_vtable()
2595 as *const ItemTreeVTable
2596 as *const ItemTreeDescription<'id>),
2597 }
2598 }
2599 }
2600
2601 pub fn as_ptr(&self) -> *const u8 {
2602 (&*self.instance.as_ref()) as *const Instance as *const u8
2603 }
2604
2605 pub fn as_ref(&self) -> &Instance<'id> {
2606 &self.instance
2607 }
2608
2609 pub fn borrow(self) -> ItemTreeRefPin<'a> {
2611 unsafe {
2612 Pin::new_unchecked(vtable::VRef::from_raw(
2613 NonNull::from(&self.description.ct).cast(),
2614 NonNull::from(self.instance.get_ref()).cast(),
2615 ))
2616 }
2617 }
2618
2619 pub fn self_weak(&self) -> &OnceCell<ErasedItemTreeBoxWeak> {
2620 let extra_data = self.description.extra_data_offset.apply(self.as_ref());
2621 &extra_data.self_weak
2622 }
2623
2624 pub fn root_weak(&self) -> &ErasedItemTreeBoxWeak {
2625 self.description.root_offset.apply(self.as_ref()).get().unwrap()
2626 }
2627
2628 pub fn window_adapter(&self) -> WindowAdapterRc {
2629 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2630 let root = self.root_weak().upgrade().unwrap();
2631 generativity::make_guard!(guard);
2632 let comp = root.unerase(guard);
2633 Self::get_or_init_window_adapter_ref(
2634 &comp.description,
2635 root_weak,
2636 true,
2637 comp.instance.as_pin_ref().get_ref(),
2638 )
2639 .unwrap()
2640 .clone()
2641 }
2642
2643 pub fn get_or_init_window_adapter_ref<'b, 'id2>(
2644 description: &'b ItemTreeDescription<'id2>,
2645 root_weak: ItemTreeWeak,
2646 do_create: bool,
2647 instance: &'b Instance<'id2>,
2648 ) -> Result<&'b WindowAdapterRc, PlatformError> {
2649 description
2651 .extra_data_offset
2652 .apply(instance)
2653 .globals
2654 .get()
2655 .unwrap()
2656 .window_adapter()
2657 .unwrap()
2658 .get_or_try_init(|| {
2659 let mut parent_node = ItemWeak::default();
2660 if let Some(rc) = vtable::VWeak::upgrade(&root_weak) {
2661 vtable::VRc::borrow_pin(&rc).as_ref().parent_node(&mut parent_node);
2662 }
2663
2664 if let Some(parent) = parent_node.upgrade() {
2665 let mut result = None;
2667 vtable::VRc::borrow_pin(parent.item_tree())
2668 .as_ref()
2669 .window_adapter(do_create, &mut result);
2670 result.ok_or(PlatformError::NoPlatform)
2671 } else if do_create {
2672 let extra_data = description.extra_data_offset.apply(instance);
2673 let window_adapter = i_slint_backend_selector::with_platform(|_b| {
2675 _b.create_window_adapter()
2676 })?;
2677
2678 let comp_rc = extra_data.self_weak.get().unwrap().upgrade().unwrap();
2679 WindowInner::from_pub(window_adapter.window())
2680 .set_component(&vtable::VRc::into_dyn(comp_rc));
2681 Ok(window_adapter)
2682 } else {
2683 Err(PlatformError::NoPlatform)
2684 }
2685 })
2686 }
2687
2688 pub fn maybe_window_adapter(&self) -> Option<WindowAdapterRc> {
2689 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2690 let root = self.root_weak().upgrade()?;
2691 generativity::make_guard!(guard);
2692 let comp = root.unerase(guard);
2693 Self::get_or_init_window_adapter_ref(
2694 &comp.description,
2695 root_weak,
2696 false,
2697 comp.instance.as_pin_ref().get_ref(),
2698 )
2699 .ok()
2700 .cloned()
2701 }
2702
2703 pub fn access_window<R>(
2704 self,
2705 callback: impl FnOnce(&'_ i_slint_core::window::WindowInner) -> R,
2706 ) -> R {
2707 callback(WindowInner::from_pub(self.window_adapter().window()))
2708 }
2709
2710 pub fn parent_instance<'id2>(
2711 &self,
2712 _guard: generativity::Guard<'id2>,
2713 ) -> Option<InstanceRef<'a, 'id2>> {
2714 if let Some(parent_offset) = self.description.parent_item_tree_offset
2717 && let Some(parent) =
2718 parent_offset.apply(self.as_ref()).get().and_then(vtable::VWeak::upgrade)
2719 {
2720 let parent_instance = parent.unerase(_guard);
2721 let parent_instance = unsafe {
2723 std::mem::transmute::<InstanceRef<'_, 'id2>, InstanceRef<'a, 'id2>>(
2724 parent_instance.borrow_instance(),
2725 )
2726 };
2727 return Some(parent_instance);
2728 }
2729 None
2730 }
2731}
2732
2733pub fn show_popup(
2735 element: ElementRc,
2736 instance: InstanceRef,
2737 popup: &object_tree::PopupWindow,
2738 pos_getter: impl FnOnce(InstanceRef<'_, '_>) -> LogicalPosition,
2739 close_policy: PopupClosePolicy,
2740 parent_comp: ErasedItemTreeBoxWeak,
2741 parent_window_adapter: WindowAdapterRc,
2742 parent_item: &ItemRc,
2743) {
2744 generativity::make_guard!(guard);
2745 let debug_handler = instance.description.debug_handler.borrow().clone();
2746
2747 let compiled = generate_item_tree(
2749 &popup.component,
2750 None,
2751 parent_comp.upgrade().unwrap().0.description().popup_menu_description.clone(),
2752 false,
2753 guard,
2754 );
2755 compiled.recursively_set_debug_handler(debug_handler);
2756
2757 let extra_data = instance.description.extra_data_offset.apply(instance.as_ref());
2758 let globals = if let Some(window_adapter) =
2760 WindowInner::from_pub(parent_window_adapter.window()).create_popup_window_adapter()
2761 {
2762 extra_data.globals.get().unwrap().clone_with_window_adapter(window_adapter)
2763 } else {
2764 extra_data.globals.get().unwrap().clone()
2765 };
2766
2767 let popup_window_adapter = globals
2768 .window_adapter()
2769 .and_then(|window_adapter| window_adapter.get().cloned())
2770 .unwrap_or_else(|| parent_window_adapter.clone());
2771
2772 let inst = instantiate(
2773 compiled,
2774 Some(parent_comp),
2775 None,
2776 Some(&WindowOptions::UseExistingWindow(popup_window_adapter)),
2777 globals,
2778 );
2779 inst.run_setup_code();
2780 let pos = {
2781 generativity::make_guard!(guard);
2782 let compo_box = inst.unerase(guard);
2783 let instance_ref = compo_box.borrow_instance();
2784 pos_getter(instance_ref)
2785 };
2786 close_popup(element.clone(), instance, parent_window_adapter.clone());
2787 instance.description.popup_ids.borrow_mut().insert(
2788 element.borrow().id.clone(),
2789 WindowInner::from_pub(parent_window_adapter.window()).show_popup(
2790 &vtable::VRc::into_dyn(inst.clone()),
2791 pos,
2792 close_policy,
2793 parent_item,
2794 match popup.popup_kind {
2795 object_tree::PopupWindowKind::Regular => i_slint_core::window::PopupKind::Regular,
2796 object_tree::PopupWindowKind::Tooltip => i_slint_core::window::PopupKind::Tooltip,
2797 },
2798 ),
2799 );
2800}
2801
2802pub fn close_popup(
2803 element: ElementRc,
2804 instance: InstanceRef,
2805 parent_window_adapter: WindowAdapterRc,
2806) {
2807 if let Some(current_id) =
2808 instance.description.popup_ids.borrow_mut().remove(&element.borrow().id)
2809 {
2810 WindowInner::from_pub(parent_window_adapter.window()).close_popup(current_id);
2811 }
2812}
2813
2814pub fn make_menu_item_tree(
2815 menu_item_tree: &Rc<object_tree::Component>,
2816 enclosing_component: &InstanceRef,
2817 condition: Option<&Expression>,
2818) -> vtable::VRc<i_slint_core::menus::MenuVTable, MenuFromItemTree> {
2819 generativity::make_guard!(guard);
2820 let mit_compiled = generate_item_tree(
2821 menu_item_tree,
2822 None,
2823 enclosing_component.description.popup_menu_description.clone(),
2824 false,
2825 guard,
2826 );
2827 let enclosing_component_weak = enclosing_component.self_weak().get().unwrap();
2828 let extra_data =
2829 enclosing_component.description.extra_data_offset.apply(enclosing_component.as_ref());
2830 let mit_inst = instantiate(
2831 mit_compiled.clone(),
2832 Some(enclosing_component_weak.clone()),
2833 None,
2834 None,
2835 extra_data.globals.get().unwrap().clone(),
2836 );
2837 mit_inst.run_setup_code();
2838 let item_tree = vtable::VRc::into_dyn(mit_inst);
2839 let menu = match condition {
2840 Some(condition) => {
2841 let binding =
2842 make_binding_eval_closure(condition.clone(), enclosing_component_weak.clone());
2843 MenuFromItemTree::new_with_condition(item_tree, move || binding().try_into().unwrap())
2844 }
2845 None => MenuFromItemTree::new(item_tree),
2846 };
2847 vtable::VRc::new(menu)
2848}
2849
2850pub fn update_timers(instance: InstanceRef) {
2851 let ts = instance.description.original.timers.borrow();
2852 for (desc, offset) in ts.iter().zip(&instance.description.timers) {
2853 let timer = offset.apply(instance.as_ref());
2854 let running =
2855 eval::load_property(instance, &desc.running.element(), desc.running.name()).unwrap();
2856 if matches!(running, Value::Bool(true)) {
2857 let millis: i64 =
2858 eval::load_property(instance, &desc.interval.element(), desc.interval.name())
2859 .unwrap()
2860 .try_into()
2861 .expect("interval must be a duration");
2862 if millis < 0 {
2863 timer.stop();
2864 continue;
2865 }
2866 let interval = core::time::Duration::from_millis(millis as _);
2867 if !timer.running() || interval != timer.interval() {
2868 let callback = desc.triggered.clone();
2869 let self_weak = instance.self_weak().get().unwrap().clone();
2870 timer.start(i_slint_core::timers::TimerMode::Repeated, interval, move || {
2871 if let Some(instance) = self_weak.upgrade() {
2872 generativity::make_guard!(guard);
2873 let c = instance.unerase(guard);
2874 let c = c.borrow_instance();
2875 let inst = eval::ComponentInstance::InstanceRef(c);
2876 eval::invoke_callback(&inst, &callback.element(), callback.name(), &[])
2877 .unwrap();
2878 }
2879 });
2880 }
2881 } else {
2882 timer.stop();
2883 }
2884 }
2885}
2886
2887pub fn restart_timer(element: ElementWeak, instance: InstanceRef) {
2888 let timers = instance.description.original.timers.borrow();
2889 if let Some((_, offset)) = timers
2890 .iter()
2891 .zip(&instance.description.timers)
2892 .find(|(desc, _)| Weak::ptr_eq(&desc.element, &element))
2893 {
2894 let timer = offset.apply(instance.as_ref());
2895 timer.restart();
2896 }
2897}