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