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