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