Skip to main content

i_slint_compiler/llr/
item_tree.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use super::{EvaluationContext, EvaluationScope, Expression, ParentScope};
5use crate::langtype::{NativeClass, Type};
6use derive_more::{From, Into};
7use smol_str::SmolStr;
8use std::cell::{Cell, RefCell};
9use std::collections::{BTreeMap, HashMap};
10use std::sync::Arc;
11use typed_index_collections::TiVec;
12
13#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq, PartialOrd, Ord)]
14pub struct PropertyIdx(usize);
15#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq, PartialOrd, Ord)]
16pub struct FunctionIdx(usize);
17#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq, PartialOrd, Ord)]
18pub struct CallbackIdx(usize);
19#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq)]
20pub struct SubComponentIdx(usize);
21#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq)]
22pub struct GlobalIdx(usize);
23#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq, PartialOrd, Ord)]
24pub struct SubComponentInstanceIdx(usize);
25#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq, PartialOrd, Ord)]
26pub struct ItemInstanceIdx(usize);
27#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq)]
28pub struct RepeatedElementIdx(usize);
29#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq, PartialOrd, Ord)]
30pub struct TimerIdx(usize);
31#[derive(Debug, Clone, Copy, Into, From, Hash, PartialEq, Eq)]
32pub struct GridLayoutChildIdx(usize);
33
34/// Describes one child in a repeated Row template.
35/// Used by code generators to handle any number of interleaved static children and
36/// inner repeaters within a repeated Row in a GridLayout.
37#[derive(Debug, Clone)]
38pub enum RowChildTemplateInfo {
39    /// A static child. `child_index` is an index into `SubComponent::grid_layout_children`.
40    Static { child_index: GridLayoutChildIdx },
41    /// An inner repeated child.
42    Repeated {
43        repeater_index: RepeatedElementIdx,
44        /// Whether this child's width comes from the grid's horizontal cache,
45        /// so `layout_item_info(Vertical, ..)` may measure it at that width
46        /// through `SubComponent::grid_row_child_cross_width`. False for a
47        /// child that is not height-for-width (nothing to re-measure) or that
48        /// has a fixed width (the grid never assigns it one).
49        measure_at_cross_width: bool,
50    },
51}
52
53/// Returns `true` if the optional template list contains at least one inner repeater.
54pub fn has_inner_repeaters(templates: &Option<Vec<RowChildTemplateInfo>>) -> bool {
55    templates
56        .as_ref()
57        .is_some_and(|t| t.iter().any(|e| matches!(e, RowChildTemplateInfo::Repeated { .. })))
58}
59
60/// Count the static children in a template list.
61pub fn static_child_count(templates: &[RowChildTemplateInfo]) -> usize {
62    templates.iter().filter(|e| matches!(e, RowChildTemplateInfo::Static { .. })).count()
63}
64
65#[derive(Debug, Clone)]
66pub struct LayoutRepeatedElement {
67    pub repeater_index: RepeatedElementIdx,
68    /// Template of children for a repeated Row (statics and inner repeaters in declaration order).
69    /// `None` means a single child per repeater entry (no Row with multiple children).
70    pub row_child_templates: Option<Vec<RowChildTemplateInfo>>,
71    /// GridLayout vertical pass only: reads this cell's solved column width out
72    /// of the grid's horizontal cache, once per instance with
73    /// `GRID_MEASURE_REPEATER_INDEX_LOCAL` bound to the instance index. The
74    /// generated code measures each instance through
75    /// `layout_item_info_at_cross_width` at that width, so a height-for-width
76    /// instance wraps like an equivalent static cell. `None` everywhere else.
77    pub cross_width: Option<Expression>,
78}
79
80#[derive(Debug, Clone)]
81pub struct GridLayoutRepeatedElement {
82    pub new_row: bool,
83    pub repeater_index: RepeatedElementIdx,
84    /// Template of children for a repeated Row (statics and inner repeaters in declaration order).
85    /// `None` means a single child per repeater entry (no Row with multiple children).
86    pub row_child_templates: Option<Vec<RowChildTemplateInfo>>,
87}
88
89impl PropertyIdx {
90    pub const REPEATER_DATA: Self = Self(0);
91    pub const REPEATER_INDEX: Self = Self(1);
92}
93
94/// Layout info (constraints) for a direct child of a repeated Row in a GridLayout.
95/// Used to generate `layout_item_info` which returns layout info for a specific child.
96#[derive(Debug, Clone)]
97pub struct GridLayoutChildLayoutInfo {
98    pub layout_info_h: MutExpression,
99    pub layout_info_v: MutExpression,
100}
101
102#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut)]
103pub struct MutExpression(RefCell<Expression>);
104
105impl From<Expression> for MutExpression {
106    fn from(e: Expression) -> Self {
107        Self(e.into())
108    }
109}
110
111impl MutExpression {
112    pub fn ty(&self, ctx: &dyn super::TypeResolutionContext) -> Type {
113        self.0.borrow().ty(ctx)
114    }
115}
116
117#[derive(Debug, Clone)]
118pub enum Animation {
119    /// The expression is a Struct with the animation fields
120    Static(Expression),
121    Transition(Expression),
122}
123
124/// How a property binding should be installed at runtime.
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub enum BindingKind {
127    /// A constant expression — can be evaluated once with `set`.
128    Constant,
129    /// A normal binding — install with `set_binding`.
130    Normal,
131    /// A state binding — the expression returns `i32` (the state index)
132    /// but the property stores a `StateInfo` struct. Install with
133    /// `set_state_binding` which tracks `previous_state` and `change_time`.
134    State,
135}
136
137#[derive(Debug, Clone)]
138pub struct BindingExpression {
139    pub expression: MutExpression,
140    pub animation: Option<Animation>,
141    pub kind: BindingKind,
142
143    /// The amount of time this binding is used.
144    /// Only valid after the [`count_property_use`](super::optim_passes::count_property_use) pass.
145    pub use_count: Cell<usize>,
146}
147
148#[derive(Debug)]
149pub struct GlobalComponent {
150    pub name: SmolStr,
151    pub properties: TiVec<PropertyIdx, Property>,
152    pub callbacks: TiVec<CallbackIdx, Callback>,
153    pub functions: TiVec<FunctionIdx, Function>,
154    /// One entry per property
155    pub init_values: BTreeMap<LocalMemberIndex, BindingExpression>,
156    // maps property to its changed callback
157    pub change_callbacks: BTreeMap<PropertyIdx, MutExpression>,
158    pub const_properties: TiVec<PropertyIdx, bool>,
159    pub public_properties: PublicProperties,
160    pub private_properties: PrivateProperties,
161    /// true if we should expose the global in the generated API
162    pub exported: bool,
163    /// The extra names under which this component should be accessible
164    /// if it is exported several time.
165    pub aliases: Vec<SmolStr>,
166    /// True when this is a built-in global that does not need to be generated
167    pub is_builtin: bool,
168    /// True if this component is imported from an external library
169    pub from_library: bool,
170    /// Analysis for each properties
171    pub prop_analysis: TiVec<PropertyIdx, crate::object_tree::PropertyAnalysis>,
172}
173
174impl GlobalComponent {
175    pub fn must_generate(&self) -> bool {
176        !self.from_library
177            && (self.exported
178                || !self.functions.is_empty()
179                || self.properties.iter().any(|p| p.use_count.get() > 0)
180                || self.callbacks.iter().any(|c| c.use_count.get() > 0))
181    }
182}
183
184#[derive(Clone, Debug, Hash, PartialEq, Eq, From, PartialOrd, Ord)]
185pub enum LocalMemberIndex {
186    #[from]
187    Property(PropertyIdx),
188    #[from]
189    Function(FunctionIdx),
190    #[from]
191    Callback(CallbackIdx),
192    /// A `Timer` in [`SubComponent::timers`].
193    /// Only valid as the argument of a `RestartTimer` builtin function call.
194    #[from]
195    Timer(TimerIdx),
196    Native {
197        item_index: ItemInstanceIdx,
198        prop_name: SmolStr,
199        /// Disambiguates rtti property bindings from rtti callback
200        /// handlers (and from member-function calls handled by
201        /// `Expression::ItemMemberFunctionCall`). Lowering resolves
202        /// this from the element's declared property type; the code
203        /// generators and the interpreter dispatch on it rather than
204        /// probing the rtti tables by name.
205        kind: NativeMemberKind,
206    },
207}
208
209#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
210pub enum NativeMemberKind {
211    /// A regular `Property` on the native item (`TouchArea.pressed`,
212    /// `Rectangle.background`, etc.).
213    Property,
214    /// A `Callback` on the native item (`TouchArea.clicked`,
215    /// `Window.close-requested`).
216    Callback,
217    /// A function exposed through the native item's property table
218    /// with a `Type::Function` declaration (`TextInput.select-all`).
219    /// Only reached via `Expression::ItemMemberFunctionCall`.
220    Function,
221}
222impl LocalMemberIndex {
223    pub fn property(&self) -> Option<PropertyIdx> {
224        if let LocalMemberIndex::Property(p) = self { Some(*p) } else { None }
225    }
226}
227
228/// A reference to a property, callback, or function, in the context of a SubComponent
229#[derive(Clone, Debug, Hash, PartialEq, Eq)]
230pub enum MemberReference {
231    /// The property or callback is withing a global
232    Global { global_index: GlobalIdx, member: LocalMemberIndex },
233
234    /// The reference is relative to the current SubComponent
235    Relative {
236        /// Go up so many level to reach the parent
237        parent_level: usize,
238        local_reference: LocalMemberReference,
239    },
240}
241impl MemberReference {
242    /// this is only valid for relative local reference
243    #[track_caller]
244    pub fn local(&self) -> LocalMemberReference {
245        match self {
246            MemberReference::Relative { parent_level: 0, local_reference, .. } => {
247                local_reference.clone()
248            }
249            _ => panic!("not a local reference"),
250        }
251    }
252
253    pub fn is_function(&self) -> bool {
254        matches!(
255            self,
256            MemberReference::Global { member: LocalMemberIndex::Function(..), .. }
257                | MemberReference::Relative {
258                    local_reference: LocalMemberReference {
259                        reference: LocalMemberIndex::Function(..),
260                        ..
261                    },
262                    ..
263                }
264        )
265    }
266}
267
268impl From<LocalMemberReference> for MemberReference {
269    fn from(local_reference: LocalMemberReference) -> Self {
270        MemberReference::Relative { parent_level: 0, local_reference }
271    }
272}
273
274/// A reference to something within an ItemTree
275#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
276pub struct LocalMemberReference {
277    pub sub_component_path: Vec<SubComponentInstanceIdx>,
278    pub reference: LocalMemberIndex,
279}
280
281impl<T: Into<LocalMemberIndex>> From<T> for LocalMemberReference {
282    fn from(reference: T) -> Self {
283        Self { sub_component_path: Vec::new(), reference: reference.into() }
284    }
285}
286
287#[derive(Debug, Clone)]
288pub struct TwoWayBinding {
289    pub prop1: LocalMemberReference,
290    pub prop2: MemberReference,
291    /// `Some` when the binding targets a model row. `prop2` is then the
292    /// `model_data` property of the enclosing `for`'s body sub-component,
293    /// and this index is the `model_index` property in the same sub-component.
294    pub is_model: Option<PropertyIdx>,
295    /// Field path applied to `prop2`, when `prop2` is a struct.
296    pub field_access: Vec<SmolStr>,
297}
298
299/// Resolved view of a model two-way binding, used by code generators to
300/// avoid re-deriving the parent walk and the data/index/repeater references.
301pub struct ResolvedModelTwoWayBinding<'a> {
302    /// Number of `parent` hops up to the body sub-component.
303    pub parent_level: usize,
304    pub body_sub_component: SubComponentIdx,
305    pub data_prop: PropertyIdx,
306    /// Type of `data_prop`, i.e. the starting type of [`TwoWayBinding::field_access`].
307    pub data_prop_ty: &'a Type,
308    pub index_prop: PropertyIdx,
309    pub parent_sub_component: SubComponentIdx,
310    pub repeater_index: RepeatedElementIdx,
311}
312
313impl TwoWayBinding {
314    /// Resolve the parent walk and the data/index/repeater references of a
315    /// model two-way binding. Returns `None` for regular property bindings.
316    pub fn resolve_model<'a, T>(
317        &self,
318        ctx: &EvaluationContext<'a, T>,
319    ) -> Option<ResolvedModelTwoWayBinding<'a>> {
320        let index_prop = self.is_model?;
321        let MemberReference::Relative { parent_level, local_reference } = &self.prop2 else {
322            unreachable!("model two-way binding's prop2 is always a Relative reference")
323        };
324        debug_assert!(local_reference.sub_component_path.is_empty());
325        let LocalMemberIndex::Property(data_prop) = local_reference.reference else {
326            unreachable!("model two-way binding's prop2 always references a property")
327        };
328        let super::EvaluationScope::SubComponent(mut sc, mut par) = ctx.current_scope else {
329            unreachable!("model two-way binding cannot be in a global")
330        };
331        for _ in 0..*parent_level {
332            let x = par.expect("parent_level should be valid");
333            par = x.parent;
334            sc = x.sub_component;
335        }
336        let par = par.expect("repeated item_tree must have a parent");
337        let data_prop_ty = &ctx.compilation_unit.sub_components[sc].properties[data_prop].ty;
338        Some(ResolvedModelTwoWayBinding {
339            parent_level: *parent_level,
340            body_sub_component: sc,
341            data_prop,
342            data_prop_ty,
343            index_prop,
344            parent_sub_component: par.sub_component,
345            repeater_index: par.repeater_index.expect("repeated parent has a repeater_index"),
346        })
347    }
348}
349
350#[derive(Debug, Default)]
351pub struct Property {
352    pub name: SmolStr,
353    pub ty: Type,
354    /// The amount of time this property is used of another property
355    /// This property is only valid after the [`count_property_use`](super::optim_passes::count_property_use) pass
356    pub use_count: Cell<usize>,
357}
358
359#[derive(Debug, Default)]
360pub struct Callback {
361    pub name: SmolStr,
362    pub ret_ty: Type,
363    pub args: Vec<Type>,
364
365    /// The Type::Callback
366    /// (This shouldn't be needed but it is because we call property_ty that returns a &Type)
367    pub ty: Type,
368
369    /// Same as for Property::use_count
370    pub use_count: Cell<usize>,
371
372    /// Whether this callback needs a change tracker `Property<()>` so that
373    /// setting a new handler from native code triggers re-evaluation of
374    /// property bindings that invoke this callback.
375    pub needs_tracker: bool,
376}
377
378#[derive(Debug)]
379pub struct Function {
380    pub name: SmolStr,
381    pub ret_ty: Type,
382    pub args: Vec<Type>,
383    pub code: MutExpression,
384    /// The number of times this function is called.
385    /// Only valid after the [`count_property_use`](super::optim_passes::count_property_use) pass.
386    pub use_count: Cell<usize>,
387}
388
389#[derive(Debug, Clone)]
390/// The property references might be either in the parent context, or in the
391/// repeated's component context
392pub struct ListViewInfo {
393    pub content_y: MemberReference,
394    /// `None` when the user explicitly sets `content-height` on the ListView;
395    /// `Some` when the ListView computes it from the content.
396    pub content_height: Option<MemberReference>,
397    /// `None` when the user explicitly sets `content-width` on the ListView;
398    /// `Some` when the ListView computes it from the content.
399    pub content_width: Option<MemberReference>,
400    /// The ListView's inner visible height (not counting eventual scrollbar)
401    pub listview_height: MemberReference,
402    /// The ListView's inner visible width (not counting eventual scrollbar)
403    pub listview_width: MemberReference,
404
405    // In the repeated component context
406    pub prop_y: MemberReference,
407    // In the repeated component context
408    pub prop_height: MemberReference,
409}
410
411#[derive(Debug)]
412pub struct RepeatedElement {
413    pub model: MutExpression,
414    /// Within the sub_tree's root component. None for `if`
415    pub index_prop: Option<PropertyIdx>,
416    /// Within the sub_tree's root component. None for `if`
417    pub data_prop: Option<PropertyIdx>,
418    /// The z of each instance, evaluated in the context of the repeated component.
419    /// When set, the instances are expanded and sorted individually among the
420    /// siblings of the repeated element during item tree traversal.
421    pub dynamic_z: Option<MemberReference>,
422    pub sub_tree: ItemTree,
423    /// The index of the item node in the parent tree
424    pub index_in_tree: u32,
425
426    pub listview: Option<ListViewInfo>,
427
428    /// Access through this in case of the element being a `is_component_placeholder`
429    pub container_item_index: Option<ItemInstanceIdx>,
430}
431
432#[derive(Debug)]
433pub struct ComponentContainerElement {
434    /// The index of the `ComponentContainer` in the enclosing components `item_tree` array
435    pub component_container_item_tree_index: u32,
436    /// The index of the `ComponentContainer` item in the enclosing components `items` array
437    pub component_container_items_index: ItemInstanceIdx,
438    /// The index to a dynamic tree node where the component is supposed to be embedded at
439    pub component_placeholder_item_tree_index: u32,
440}
441
442pub struct Item {
443    pub ty: Arc<NativeClass>,
444    pub name: SmolStr,
445    /// Index in the item tree array
446    pub index_in_tree: u32,
447}
448
449impl std::fmt::Debug for Item {
450    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451        f.debug_struct("Item")
452            .field("ty", &self.ty.class_name)
453            .field("name", &self.name)
454            .field("index_in_tree", &self.index_in_tree)
455            .finish()
456    }
457}
458
459#[derive(Debug)]
460pub struct TreeNode {
461    pub sub_component_path: Vec<SubComponentInstanceIdx>,
462    /// Either an index in the items, or the local dynamic index for repeater or component container
463    pub item_index: itertools::Either<ItemInstanceIdx, u32>,
464    pub children: Vec<TreeNode>,
465    pub is_accessible: bool,
466    /// If set, this node's children have dynamic z-ordering.
467    /// Each entry corresponds to a child (by index) and gives its z value.
468    /// The code generator will evaluate these on every children visit and sort the
469    /// children accordingly.
470    pub z_sort_order_property: Option<Vec<ZSource>>,
471}
472
473/// The z value of a child in a dynamically z-ordered parent
474#[derive(Debug, Clone)]
475pub enum ZSource {
476    /// The z value of the child. The expression must be side-effect free and only
477    /// reference globals or properties of the item tree root's sub-component
478    /// (never a parent item tree).
479    Expression(MutExpression),
480    /// The child is a repeated element whose instances are expanded and sorted
481    /// individually, each by its own z value. The repeater is the matching child
482    /// node (`parent.children[child_offset]`, a `DynamicTree` node).
483    RepeaterInstances,
484}
485
486impl TreeNode {
487    fn children_count(&self) -> usize {
488        let mut count = self.children.len();
489        for c in &self.children {
490            count += c.children_count();
491        }
492        count
493    }
494
495    /// Visit this, and the children.
496    /// `children_offset` must be set to `1` for the root
497    pub fn visit_in_array<'a>(
498        &'a self,
499        visitor: &mut dyn FnMut(
500            &'a TreeNode,
501            /*children_offset: */ usize,
502            /*parent_index: */ usize,
503        ),
504    ) {
505        visitor(self, 1, 0);
506        visit_in_array_recursive(self, 1, 0, visitor);
507
508        fn visit_in_array_recursive<'a>(
509            node: &'a TreeNode,
510            children_offset: usize,
511            current_index: usize,
512            visitor: &mut dyn FnMut(&'a TreeNode, usize, usize),
513        ) {
514            let mut offset = children_offset + node.children.len();
515            for c in &node.children {
516                visitor(c, offset, current_index);
517                offset += c.children_count();
518            }
519
520            let mut offset = children_offset + node.children.len();
521            for (i, c) in node.children.iter().enumerate() {
522                visit_in_array_recursive(c, offset, children_offset + i, visitor);
523                offset += c.children_count();
524            }
525        }
526    }
527}
528
529#[derive(Debug)]
530pub struct SubComponent {
531    pub name: SmolStr,
532    pub properties: TiVec<PropertyIdx, Property>,
533    pub callbacks: TiVec<CallbackIdx, Callback>,
534    pub functions: TiVec<FunctionIdx, Function>,
535    pub items: TiVec<ItemInstanceIdx, Item>,
536    pub repeated: TiVec<RepeatedElementIdx, RepeatedElement>,
537    pub component_containers: Vec<ComponentContainerElement>,
538    pub popup_windows: Vec<PopupWindow>,
539    /// The MenuItem trees. The index is stored in a Expression::NumberLiteral in the arguments of BuiltinFunction::ShowPopupMenu and BuiltinFunction::SetupMenuBar
540    pub menu_item_trees: Vec<ItemTree>,
541    pub timers: TiVec<TimerIdx, Timer>,
542    pub sub_components: TiVec<SubComponentInstanceIdx, SubComponentInstance>,
543    /// The initial value or binding for properties.
544    /// This is ordered in the order they must be set.
545    pub property_init: Vec<(MemberReference, BindingExpression)>,
546    pub change_callbacks: Vec<(MemberReference, MutExpression)>,
547    /// The animation for properties which are animated
548    pub animations: BTreeMap<LocalMemberReference, Expression>,
549    /// The two way bindings that map the first property to the second wih optional field access
550    pub two_way_bindings: Vec<TwoWayBinding>,
551    pub const_properties: Vec<LocalMemberReference>,
552    /// Code run at the start of the constructor, before the property initialization.
553    /// Custom font registration uses this, so fonts are ready before a property needs them.
554    pub pre_init_code: Vec<MutExpression>,
555    /// Code that is run in the sub component constructor, after property initializations
556    pub init_code: Vec<MutExpression>,
557
558    /// For each node, an expression that returns a `{x: length, y: length, width: length, height: length}`
559    pub geometries: Vec<Option<MutExpression>>,
560
561    pub layout_info_h: MutExpression,
562    pub layout_info_v: MutExpression,
563    pub child_of_layout: bool,
564    pub grid_layout_input_for_repeated: Option<MutExpression>,
565    /// Expression that builds a FlexboxLayoutItemInfo for a repeated element in a FlexboxLayout.
566    /// Contains property references to cross-axis-self-alignment and layout-order.
567    pub flexbox_layout_item_info_for_repeated: Option<MutExpression>,
568    /// The root's `cross-axis-self-alignment` for a repeated element in a box
569    /// layout, returned by the generated `layout_item_info` for the given
570    /// (cross-axis) orientation only, so the main-axis cache stays independent
571    /// of it. The cross-axis layout-info pass shares that accessor and so also
572    /// evaluates it, unlike static cells (`box_layout_info_ortho` ignores it).
573    pub cross_axis_self_alignment_for_repeated: Option<(crate::layout::Orientation, MutExpression)>,
574    /// The root's `layout-order` for a repeated element in a box layout,
575    /// returned by the generated `layout_item_info` for the given (main-axis)
576    /// orientation only, so the cross-axis cache stays independent of it. The
577    /// main-axis layout-info pass shares that accessor and so also evaluates
578    /// it, unlike static cells (`box_layout_info` ignores the field).
579    pub layout_order_for_repeated: Option<(crate::layout::Orientation, MutExpression)>,
580    /// Vertical `LayoutInfo` for a repeated element, computed with a width
581    /// constraint (its preferred width) so a height-for-width instance in a
582    /// column FlexboxLayout doesn't read `self.width` and recurse through the
583    /// parent flex cache. `Some` only when the element carries a
584    /// `layoutinfo-v-with-constraint`. See `flexbox_layout_item_info`.
585    pub layout_info_v_constrained_for_repeated: Option<MutExpression>,
586    /// Same as `layout_info_v_constrained_for_repeated`, but measured at the
587    /// width passed in the `cross_width` local instead of the preferred
588    /// width. Drives the generated `flexbox_layout_item_info_at_cross_width` method,
589    /// which a column FlexboxLayout calls with its real container width so a
590    /// repeated cell wraps to the same height as an equivalent static cell.
591    pub layout_info_v_at_cross_width_for_repeated: Option<MutExpression>,
592    /// GridLayout repeated Row only: reads the solved column width of the child
593    /// at `GRID_MEASURE_CHILD_INDEX_LOCAL` out of the grid's horizontal cache.
594    /// `layout_item_info(Vertical, Some(i))` measures an inner repeated child at
595    /// that width, so it wraps like a static one — which reads its own width
596    /// through its geometry binding. A Row child's cache slot is addressed by
597    /// its flattened index, so this one expression serves every child; which
598    /// children it applies to is `RowChildTemplateInfo::Repeated`'s
599    /// `measure_at_cross_width`. `Some` when at least one of them sets it.
600    pub grid_row_child_cross_width: Option<MutExpression>,
601    /// True when this is a repeated Row in a GridLayout, meaning layout_item_info
602    /// needs to be able to return layout info for individual children
603    pub is_repeated_row: bool,
604    /// The list of direct grid layout children for a repeated Row.
605    /// Used to generate `layout_item_info` which returns layout info for a specific child.
606    pub grid_layout_children: TiVec<GridLayoutChildIdx, GridLayoutChildLayoutInfo>,
607    /// For repeated Rows with children: template of children in declaration order
608    /// (statics and inner repeaters). Used by code generators to produce
609    /// `grid_layout_input_data` and `layout_item_info`.
610    pub row_child_templates: Option<Vec<RowChildTemplateInfo>>,
611
612    /// Maps (item_index, property) to an expression
613    pub accessible_prop: BTreeMap<(u32, String), MutExpression>,
614
615    /// Maps item index to a list of encoded element infos of the element  (type name, qualified ids).
616    pub element_infos: BTreeMap<u32, String>,
617
618    pub prop_analysis: HashMap<MemberReference, PropAnalysis>,
619
620    /// Populated when `CompilerConfiguration::debug_info` is set.
621    /// The interpreter uses it for highlighting and live preview.
622    pub debug_info: Option<super::debug_info::SubComponentDebugInfo>,
623}
624
625#[derive(Debug)]
626pub struct PopupWindow {
627    pub item_tree: ItemTree,
628    pub position: MutExpression,
629    pub is_tooltip: bool,
630}
631
632#[derive(Debug)]
633pub struct PopupMenu {
634    pub item_tree: ItemTree,
635    pub sub_menu: MemberReference,
636    pub activated: MemberReference,
637    pub close: MemberReference,
638    pub entries: MemberReference,
639}
640
641#[derive(Debug)]
642pub struct Timer {
643    pub interval: MutExpression,
644    pub running: MutExpression,
645    pub triggered: MutExpression,
646}
647
648#[derive(Debug, Clone)]
649pub struct PropAnalysis {
650    /// Index in SubComponent::property_init for this property
651    pub property_init: Option<usize>,
652    pub analysis: crate::object_tree::PropertyAnalysis,
653}
654
655impl SubComponent {
656    /// total count of repeater, including in sub components
657    pub fn repeater_count(&self, cu: &CompilationUnit) -> u32 {
658        let mut count = (self.repeated.len() + self.component_containers.len()) as u32;
659        for x in self.sub_components.iter() {
660            count += cu.sub_components[x.ty].repeater_count(cu);
661        }
662        count
663    }
664
665    /// total count of items, including in sub components
666    pub fn child_item_count(&self, cu: &CompilationUnit) -> u32 {
667        let mut count = self.items.len() as u32;
668        for x in self.sub_components.iter() {
669            count += cu.sub_components[x.ty].child_item_count(cu);
670        }
671        count
672    }
673}
674
675#[derive(Debug)]
676pub struct SubComponentInstance {
677    pub ty: SubComponentIdx,
678    pub name: SmolStr,
679    pub index_in_tree: u32,
680    pub index_of_first_child_in_tree: u32,
681    pub repeater_offset: u32,
682}
683
684#[derive(Debug)]
685pub struct ItemTree {
686    pub root: SubComponentIdx,
687    pub tree: TreeNode,
688}
689
690/// What top-level role an exported component plays. Drives whether the
691/// generated public API is `slint::Window`-shaped (a `ComponentHandle` impl
692/// with `show`/`hide`/`run`/`window`) or something else.
693#[derive(Debug, Clone, Copy, PartialEq, Eq)]
694pub enum TopLevelComponentType {
695    Window,
696    SystemTrayIcon,
697}
698
699#[derive(Debug)]
700pub struct PublicComponent {
701    pub public_properties: PublicProperties,
702    pub private_properties: PrivateProperties,
703    pub item_tree: ItemTree,
704    pub name: SmolStr,
705    pub top_level_type: TopLevelComponentType,
706}
707
708/// One name the generated module exposes for a declared type (or a component alias):
709/// its own name, a renamed export, or a name kept only for backward compatibility.
710#[derive(Debug)]
711pub struct TypeExport {
712    /// The name users write.
713    pub exported_name: SmolStr,
714    /// The generated declaration it points at. Equal to `exported_name` for a type
715    /// re-exported under its own name.
716    pub internal_name: SmolStr,
717    /// When set, `exported_name` warns on use: the type is not part of the public API,
718    /// or was renamed on export.
719    pub deprecated: bool,
720}
721
722impl TypeExport {
723    /// True when the type is exposed under a name other than its own — a renamed export,
724    /// or the pre-rename name kept for compatibility. False for a type re-exported under
725    /// its own name.
726    pub fn is_alias(&self) -> bool {
727        self.exported_name != self.internal_name
728    }
729
730    /// The message shown when `exported_name` is used, or `None` when it is not deprecated.
731    /// Shared by the generators so the wording stays identical across languages.
732    pub fn deprecation_note(&self) -> Option<String> {
733        self.deprecated.then(|| {
734            if self.is_alias() {
735                format!(
736                    "`{0}` was renamed to `{1}` on export. Use `{1}`.",
737                    self.exported_name, self.internal_name
738                )
739            } else {
740                format!(
741                    "`{}` is not part of the public API. Re-export it from your main .slint file to make it public.",
742                    self.exported_name
743                )
744            }
745        })
746    }
747}
748
749#[derive(Debug)]
750pub struct CompilationUnit {
751    pub public_components: Vec<PublicComponent>,
752    /// Storage for all sub-components
753    pub sub_components: TiVec<SubComponentIdx, SubComponent>,
754    /// The sub-components that are not item-tree root
755    pub used_sub_components: Vec<SubComponentIdx>,
756    pub globals: TiVec<GlobalIdx, GlobalComponent>,
757    pub popup_menu: Option<PopupMenu>,
758    pub has_debug_info: bool,
759    /// Every name the generated module re-exports for a declared type, plus the
760    /// renamed `export { Original as Alias }` aliases of components. Types renamed to
761    /// resolve a same-name collision are absent: they were never public. (Global
762    /// aliases are on [`GlobalComponent::aliases`].)
763    pub type_exports: Vec<TypeExport>,
764    #[cfg(feature = "bundle-translations")]
765    pub translations: Option<crate::translations::Translations>,
766}
767
768// The code generators may run on another thread, so the LLR must not reference the object tree.
769const _: () = {
770    const fn assert_send<T: Send>() {}
771    assert_send::<CompilationUnit>();
772};
773
774impl CompilationUnit {
775    pub fn needs_window_adapter(&self) -> bool {
776        self.public_components.iter().any(|p| p.top_level_type == TopLevelComponentType::Window)
777            || self.popup_menu.is_some()
778    }
779
780    pub fn for_each_sub_components<'a>(
781        &'a self,
782        visitor: &mut dyn FnMut(SubComponentIdx, &'a SubComponent, &EvaluationContext<'_>),
783    ) {
784        fn visit_component<'a>(
785            root: &'a CompilationUnit,
786            c: SubComponentIdx,
787            visitor: &mut dyn FnMut(SubComponentIdx, &'a SubComponent, &EvaluationContext<'_>),
788            parent: Option<&ParentScope<'_>>,
789        ) {
790            let ctx = EvaluationContext::new_sub_component(root, c, (), parent);
791            let sc = &root.sub_components[c];
792            visitor(c, sc, &ctx);
793            for (idx, r) in sc.repeated.iter_enumerated() {
794                visit_component(
795                    root,
796                    r.sub_tree.root,
797                    visitor,
798                    Some(&ParentScope::new(&ctx, Some(idx))),
799                );
800            }
801            for popup in &sc.popup_windows {
802                visit_component(
803                    root,
804                    popup.item_tree.root,
805                    visitor,
806                    Some(&ParentScope::new(&ctx, None)),
807                );
808            }
809            for menu_tree in &sc.menu_item_trees {
810                visit_component(root, menu_tree.root, visitor, Some(&ParentScope::new(&ctx, None)));
811            }
812        }
813        for c in &self.used_sub_components {
814            visit_component(self, *c, visitor, None);
815        }
816        for p in &self.public_components {
817            visit_component(self, p.item_tree.root, visitor, None);
818        }
819        if let Some(p) = &self.popup_menu {
820            visit_component(self, p.item_tree.root, visitor, None);
821        }
822    }
823
824    pub fn for_each_expression<'a>(
825        &'a self,
826        visitor: &mut dyn FnMut(&'a super::MutExpression, &EvaluationContext<'_>),
827    ) {
828        self.for_each_sub_components(&mut |_, sc, ctx| {
829            for e in &sc.pre_init_code {
830                visitor(e, ctx);
831            }
832            for e in &sc.init_code {
833                visitor(e, ctx);
834            }
835            for (_, e) in &sc.property_init {
836                visitor(&e.expression, ctx);
837            }
838            visitor(&sc.layout_info_h, ctx);
839            visitor(&sc.layout_info_v, ctx);
840            if let Some(e) = &sc.grid_layout_input_for_repeated {
841                visitor(e, ctx);
842            }
843            if let Some(e) = &sc.flexbox_layout_item_info_for_repeated {
844                visitor(e, ctx);
845            }
846            if let Some((_, e)) = &sc.cross_axis_self_alignment_for_repeated {
847                visitor(e, ctx);
848            }
849            if let Some((_, e)) = &sc.layout_order_for_repeated {
850                visitor(e, ctx);
851            }
852            if let Some(e) = &sc.layout_info_v_constrained_for_repeated {
853                visitor(e, ctx);
854            }
855            if let Some(e) = &sc.layout_info_v_at_cross_width_for_repeated {
856                visitor(e, ctx);
857            }
858            if let Some(e) = &sc.grid_row_child_cross_width {
859                visitor(e, ctx);
860            }
861            for e in sc.accessible_prop.values() {
862                visitor(e, ctx);
863            }
864            for i in sc.geometries.iter().flatten() {
865                visitor(i, ctx);
866            }
867            for (_, e) in sc.change_callbacks.iter() {
868                visitor(e, ctx);
869            }
870            for child in &sc.grid_layout_children {
871                visitor(&child.layout_info_h, ctx);
872                visitor(&child.layout_info_v, ctx);
873            }
874            for r in sc.repeated.iter() {
875                visitor(&r.model, ctx);
876            }
877            for t in sc.timers.iter() {
878                visitor(&t.interval, ctx);
879                visitor(&t.running, ctx);
880                visitor(&t.triggered, ctx);
881            }
882            if let EvaluationScope::SubComponent(idx, _) = ctx.current_scope {
883                // A parent-less context, matching how `count_property_use` counts
884                // function bodies, so both passes rewrite the same references.
885                let fn_ctx = EvaluationContext::new_sub_component(self, idx, (), None);
886                visit_function_bodies(&sc.functions, &fn_ctx, visitor);
887            }
888            // Popup positions are intentionally not visited: they are evaluated in a
889            // nested context, so inlining into them would corrupt the parent levels
890            // of their property references.
891        });
892        for (idx, g) in self.globals.iter_enumerated() {
893            let ctx = EvaluationContext::new_global(self, idx, ());
894            for e in g.init_values.values() {
895                visitor(&e.expression, &ctx)
896            }
897            for e in g.change_callbacks.values() {
898                visitor(e, &ctx)
899            }
900            visit_function_bodies(&g.functions, &ctx, visitor);
901        }
902        self.for_each_z_order_expression(visitor);
903    }
904
905    /// Visit the z-order expressions of all item tree nodes.
906    /// The context passed to the visitor is the one of the item tree's root sub-component,
907    /// which is the frame the expressions are resolved in.
908    pub fn for_each_z_order_expression<'a>(
909        &'a self,
910        visitor: &mut dyn FnMut(&'a MutExpression, &EvaluationContext<'_>),
911    ) {
912        fn visit_tree<'a>(
913            node: &'a TreeNode,
914            ctx: &EvaluationContext<'_>,
915            visitor: &mut dyn FnMut(&'a MutExpression, &EvaluationContext<'_>),
916        ) {
917            for e in node.z_sort_order_property.iter().flatten() {
918                if let ZSource::Expression(e) = e {
919                    visitor(e, ctx);
920                }
921            }
922            for child in &node.children {
923                visit_tree(child, ctx, visitor);
924            }
925        }
926        // Every item tree, by its root sub-component
927        let mut trees: HashMap<SubComponentIdx, &TreeNode> = HashMap::new();
928        for c in &self.public_components {
929            trees.insert(c.item_tree.root, &c.item_tree.tree);
930        }
931        if let Some(p) = &self.popup_menu {
932            trees.insert(p.item_tree.root, &p.item_tree.tree);
933        }
934        for sc in self.sub_components.iter() {
935            for r in &sc.repeated {
936                trees.insert(r.sub_tree.root, &r.sub_tree.tree);
937            }
938            for p in &sc.popup_windows {
939                trees.insert(p.item_tree.root, &p.item_tree.tree);
940            }
941        }
942        // Visit with the context from `for_each_sub_components` because it has the
943        // repeater parent scopes set up, which is needed to resolve expressions that
944        // are inlined into the z expressions
945        self.for_each_sub_components(&mut |idx, _, ctx| {
946            if let Some(tree) = trees.get(&idx) {
947                visit_tree(tree, ctx, visitor);
948            }
949        });
950    }
951}
952
953/// Visit the body of each reachable function in `ctx` (which must have no parents,
954/// see [`CompilationUnit::for_each_expression`]) with `argument_types` set.
955///
956/// Only functions with a non-zero use count: `count_property_use` visits exactly
957/// those bodies, so visiting an unreachable one would inline references it never
958/// counted and underflow the use counts.
959fn visit_function_bodies<'a>(
960    functions: &'a TiVec<FunctionIdx, Function>,
961    ctx: &EvaluationContext<'a>,
962    visitor: &mut dyn FnMut(&'a super::MutExpression, &EvaluationContext<'_>),
963) {
964    for f in functions {
965        if f.use_count.get() > 0 {
966            let mut fn_ctx = ctx.clone();
967            fn_ctx.argument_types = &f.args;
968            visitor(&f.code, &fn_ctx);
969        }
970    }
971}
972
973/// Depending on the type, this can also be a Callback or a Function
974#[derive(Debug, Clone)]
975pub struct PublicProperty {
976    /// The identifier as written in the `.slint` source, preserving any
977    /// hyphens and the original casing. The interpreter's public API
978    /// returns this form in the property list so that callers see the same
979    /// name they wrote.
980    pub display_name: SmolStr,
981    pub ty: Type,
982    pub prop: MemberReference,
983    pub visibility: crate::object_tree::PropertyVisibility,
984}
985
986impl PublicProperty {
987    pub fn read_only(&self) -> bool {
988        self.visibility == crate::object_tree::PropertyVisibility::Output
989    }
990}
991/// Public properties of a component or global, keyed by the normalized
992/// identifier (underscores). Iteration order is by sorted key.
993pub type PublicProperties = BTreeMap<SmolStr, PublicProperty>;
994pub type PrivateProperties = Vec<(SmolStr, Type)>;