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, 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::rc::Rc;
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)]
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)]
30pub struct GridLayoutChildIdx(usize);
31
32/// Describes one child in a repeated Row template.
33/// Used by code generators to handle any number of interleaved static children and
34/// inner repeaters within a repeated Row in a GridLayout.
35#[derive(Debug, Clone)]
36pub enum RowChildTemplateInfo {
37    /// A static child. `child_index` is an index into `SubComponent::grid_layout_children`.
38    Static { child_index: GridLayoutChildIdx },
39    /// An inner repeated child.
40    Repeated { repeater_index: RepeatedElementIdx },
41}
42
43/// Returns `true` if the optional template list contains at least one inner repeater.
44pub fn has_inner_repeaters(templates: &Option<Vec<RowChildTemplateInfo>>) -> bool {
45    templates
46        .as_ref()
47        .is_some_and(|t| t.iter().any(|e| matches!(e, RowChildTemplateInfo::Repeated { .. })))
48}
49
50/// Count the static children in a template list.
51pub fn static_child_count(templates: &[RowChildTemplateInfo]) -> usize {
52    templates.iter().filter(|e| matches!(e, RowChildTemplateInfo::Static { .. })).count()
53}
54
55#[derive(Debug, Clone)]
56pub struct LayoutRepeatedElement {
57    pub repeater_index: RepeatedElementIdx,
58    /// Template of children for a repeated Row (statics and inner repeaters in declaration order).
59    /// `None` means a single child per repeater entry (no Row with multiple children).
60    pub row_child_templates: Option<Vec<RowChildTemplateInfo>>,
61}
62
63#[derive(Debug, Clone)]
64pub struct GridLayoutRepeatedElement {
65    pub new_row: bool,
66    pub repeater_index: RepeatedElementIdx,
67    /// Template of children for a repeated Row (statics and inner repeaters in declaration order).
68    /// `None` means a single child per repeater entry (no Row with multiple children).
69    pub row_child_templates: Option<Vec<RowChildTemplateInfo>>,
70}
71
72impl PropertyIdx {
73    pub const REPEATER_DATA: Self = Self(0);
74    pub const REPEATER_INDEX: Self = Self(1);
75}
76
77/// Layout info (constraints) for a direct child of a repeated Row in a GridLayout.
78/// Used to generate `layout_item_info` which returns layout info for a specific child.
79#[derive(Debug, Clone)]
80pub struct GridLayoutChildLayoutInfo {
81    pub layout_info_h: MutExpression,
82    pub layout_info_v: MutExpression,
83}
84
85#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut)]
86pub struct MutExpression(RefCell<Expression>);
87
88impl From<Expression> for MutExpression {
89    fn from(e: Expression) -> Self {
90        Self(e.into())
91    }
92}
93
94impl MutExpression {
95    pub fn ty(&self, ctx: &dyn super::TypeResolutionContext) -> Type {
96        self.0.borrow().ty(ctx)
97    }
98}
99
100#[derive(Debug, Clone)]
101pub enum Animation {
102    /// The expression is a Struct with the animation fields
103    Static(Expression),
104    Transition(Expression),
105}
106
107#[derive(Debug, Clone)]
108pub struct BindingExpression {
109    pub expression: MutExpression,
110    pub animation: Option<Animation>,
111    /// When true, we can initialize the property with `set` otherwise, `set_binding` must be used
112    pub is_constant: bool,
113    /// When true, the expression is a "state binding".  Despite the type of the expression being a integer
114    /// the property is of type StateInfo and the `set_state_binding` need to be used on the property
115    pub is_state_info: bool,
116
117    /// The amount of time this binding is used
118    /// This property is only valid after the [`count_property_use`](super::optim_passes::count_property_use) pass
119    pub use_count: Cell<usize>,
120}
121
122#[derive(Debug)]
123pub struct GlobalComponent {
124    pub name: SmolStr,
125    pub properties: TiVec<PropertyIdx, Property>,
126    pub callbacks: TiVec<CallbackIdx, Callback>,
127    pub functions: TiVec<FunctionIdx, Function>,
128    /// One entry per property
129    pub init_values: BTreeMap<LocalMemberIndex, BindingExpression>,
130    // maps property to its changed callback
131    pub change_callbacks: BTreeMap<PropertyIdx, MutExpression>,
132    pub const_properties: TiVec<PropertyIdx, bool>,
133    pub public_properties: PublicProperties,
134    pub private_properties: PrivateProperties,
135    /// true if we should expose the global in the generated API
136    pub exported: bool,
137    /// The extra names under which this component should be accessible
138    /// if it is exported several time.
139    pub aliases: Vec<SmolStr>,
140    /// True when this is a built-in global that does not need to be generated
141    pub is_builtin: bool,
142    /// True if this component is imported from an external library
143    pub from_library: bool,
144    /// Analysis for each properties
145    pub prop_analysis: TiVec<PropertyIdx, crate::object_tree::PropertyAnalysis>,
146}
147
148impl GlobalComponent {
149    pub fn must_generate(&self) -> bool {
150        !self.from_library
151            && (self.exported
152                || !self.functions.is_empty()
153                || self.properties.iter().any(|p| p.use_count.get() > 0)
154                || self.callbacks.iter().any(|c| c.use_count.get() > 0))
155    }
156}
157
158#[derive(Clone, Debug, Hash, PartialEq, Eq, From, PartialOrd, Ord)]
159pub enum LocalMemberIndex {
160    #[from]
161    Property(PropertyIdx),
162    #[from]
163    Function(FunctionIdx),
164    #[from]
165    Callback(CallbackIdx),
166    Native {
167        item_index: ItemInstanceIdx,
168        prop_name: SmolStr,
169    },
170}
171impl LocalMemberIndex {
172    pub fn property(&self) -> Option<PropertyIdx> {
173        if let LocalMemberIndex::Property(p) = self { Some(*p) } else { None }
174    }
175}
176
177/// A reference to a property, callback, or function, in the context of a SubComponent
178#[derive(Clone, Debug, Hash, PartialEq, Eq)]
179pub enum MemberReference {
180    /// The property or callback is withing a global
181    Global { global_index: GlobalIdx, member: LocalMemberIndex },
182
183    /// The reference is relative to the current SubComponent
184    Relative {
185        /// Go up so many level to reach the parent
186        parent_level: usize,
187        local_reference: LocalMemberReference,
188    },
189}
190impl MemberReference {
191    /// this is only valid for relative local reference
192    #[track_caller]
193    pub fn local(&self) -> LocalMemberReference {
194        match self {
195            MemberReference::Relative { parent_level: 0, local_reference, .. } => {
196                local_reference.clone()
197            }
198            _ => panic!("not a local reference"),
199        }
200    }
201
202    pub fn is_function(&self) -> bool {
203        matches!(
204            self,
205            MemberReference::Global { member: LocalMemberIndex::Function(..), .. }
206                | MemberReference::Relative {
207                    local_reference: LocalMemberReference {
208                        reference: LocalMemberIndex::Function(..),
209                        ..
210                    },
211                    ..
212                }
213        )
214    }
215}
216
217impl From<LocalMemberReference> for MemberReference {
218    fn from(local_reference: LocalMemberReference) -> Self {
219        MemberReference::Relative { parent_level: 0, local_reference }
220    }
221}
222
223/// A reference to something within an ItemTree
224#[derive(Debug, Clone, PartialEq, Eq, Hash)]
225pub struct LocalMemberReference {
226    pub sub_component_path: Vec<SubComponentInstanceIdx>,
227    pub reference: LocalMemberIndex,
228}
229
230impl<T: Into<LocalMemberIndex>> From<T> for LocalMemberReference {
231    fn from(reference: T) -> Self {
232        Self { sub_component_path: Vec::new(), reference: reference.into() }
233    }
234}
235
236#[derive(Debug, Default)]
237pub struct Property {
238    pub name: SmolStr,
239    pub ty: Type,
240    /// The amount of time this property is used of another property
241    /// This property is only valid after the [`count_property_use`](super::optim_passes::count_property_use) pass
242    pub use_count: Cell<usize>,
243}
244
245#[derive(Debug, Default)]
246pub struct Callback {
247    pub name: SmolStr,
248    pub ret_ty: Type,
249    pub args: Vec<Type>,
250
251    /// The Type::Callback
252    /// (This shouldn't be needed but it is because we call property_ty that returns a &Type)
253    pub ty: Type,
254
255    /// Same as for Property::use_count
256    pub use_count: Cell<usize>,
257}
258
259#[derive(Debug)]
260pub struct Function {
261    pub name: SmolStr,
262    pub ret_ty: Type,
263    pub args: Vec<Type>,
264    pub code: Expression,
265}
266
267#[derive(Debug, Clone)]
268/// The property references might be either in the parent context, or in the
269/// repeated's component context
270pub struct ListViewInfo {
271    pub viewport_y: MemberReference,
272    pub viewport_height: MemberReference,
273    pub viewport_width: MemberReference,
274    /// The ListView's inner visible height (not counting eventual scrollbar)
275    pub listview_height: MemberReference,
276    /// The ListView's inner visible width (not counting eventual scrollbar)
277    pub listview_width: MemberReference,
278
279    // In the repeated component context
280    pub prop_y: MemberReference,
281    // In the repeated component context
282    pub prop_height: MemberReference,
283}
284
285#[derive(Debug)]
286pub struct RepeatedElement {
287    pub model: MutExpression,
288    /// Within the sub_tree's root component. None for `if`
289    pub index_prop: Option<PropertyIdx>,
290    /// Within the sub_tree's root component. None for `if`
291    pub data_prop: Option<PropertyIdx>,
292    pub sub_tree: ItemTree,
293    /// The index of the item node in the parent tree
294    pub index_in_tree: u32,
295
296    pub listview: Option<ListViewInfo>,
297
298    /// Access through this in case of the element being a `is_component_placeholder`
299    pub container_item_index: Option<ItemInstanceIdx>,
300}
301
302#[derive(Debug)]
303pub struct ComponentContainerElement {
304    /// The index of the `ComponentContainer` in the enclosing components `item_tree` array
305    pub component_container_item_tree_index: u32,
306    /// The index of the `ComponentContainer` item in the enclosing components `items` array
307    pub component_container_items_index: ItemInstanceIdx,
308    /// The index to a dynamic tree node where the component is supposed to be embedded at
309    pub component_placeholder_item_tree_index: u32,
310}
311
312pub struct Item {
313    pub ty: Rc<NativeClass>,
314    pub name: SmolStr,
315    /// Index in the item tree array
316    pub index_in_tree: u32,
317}
318
319impl std::fmt::Debug for Item {
320    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        f.debug_struct("Item")
322            .field("ty", &self.ty.class_name)
323            .field("name", &self.name)
324            .field("index_in_tree", &self.index_in_tree)
325            .finish()
326    }
327}
328
329#[derive(Debug)]
330pub struct TreeNode {
331    pub sub_component_path: Vec<SubComponentInstanceIdx>,
332    /// Either an index in the items, or the local dynamic index for repeater or component container
333    pub item_index: itertools::Either<ItemInstanceIdx, u32>,
334    pub children: Vec<TreeNode>,
335    pub is_accessible: bool,
336}
337
338impl TreeNode {
339    fn children_count(&self) -> usize {
340        let mut count = self.children.len();
341        for c in &self.children {
342            count += c.children_count();
343        }
344        count
345    }
346
347    /// Visit this, and the children.
348    /// `children_offset` must be set to `1` for the root
349    pub fn visit_in_array(
350        &self,
351        visitor: &mut dyn FnMut(
352            &TreeNode,
353            /*children_offset: */ usize,
354            /*parent_index: */ usize,
355        ),
356    ) {
357        visitor(self, 1, 0);
358        visit_in_array_recursive(self, 1, 0, visitor);
359
360        fn visit_in_array_recursive(
361            node: &TreeNode,
362            children_offset: usize,
363            current_index: usize,
364            visitor: &mut dyn FnMut(&TreeNode, usize, usize),
365        ) {
366            let mut offset = children_offset + node.children.len();
367            for c in &node.children {
368                visitor(c, offset, current_index);
369                offset += c.children_count();
370            }
371
372            let mut offset = children_offset + node.children.len();
373            for (i, c) in node.children.iter().enumerate() {
374                visit_in_array_recursive(c, offset, children_offset + i, visitor);
375                offset += c.children_count();
376            }
377        }
378    }
379}
380
381#[derive(Debug)]
382pub struct SubComponent {
383    pub name: SmolStr,
384    pub properties: TiVec<PropertyIdx, Property>,
385    pub callbacks: TiVec<CallbackIdx, Callback>,
386    pub functions: TiVec<FunctionIdx, Function>,
387    pub items: TiVec<ItemInstanceIdx, Item>,
388    pub repeated: TiVec<RepeatedElementIdx, RepeatedElement>,
389    pub component_containers: Vec<ComponentContainerElement>,
390    pub popup_windows: Vec<PopupWindow>,
391    /// The MenuItem trees. The index is stored in a Expression::NumberLiteral in the arguments of BuiltinFunction::ShowPopupMenu and BuiltinFunction::SetupMenuBar
392    pub menu_item_trees: Vec<ItemTree>,
393    pub timers: Vec<Timer>,
394    pub sub_components: TiVec<SubComponentInstanceIdx, SubComponentInstance>,
395    /// The initial value or binding for properties.
396    /// This is ordered in the order they must be set.
397    pub property_init: Vec<(MemberReference, BindingExpression)>,
398    pub change_callbacks: Vec<(MemberReference, MutExpression)>,
399    /// The animation for properties which are animated
400    pub animations: HashMap<LocalMemberReference, Expression>,
401    /// The two way bindings that map the first property to the second wih optional field access
402    pub two_way_bindings: Vec<(MemberReference, MemberReference, Vec<SmolStr>)>,
403    pub const_properties: Vec<LocalMemberReference>,
404    /// Code that is run in the sub component constructor, after property initializations
405    pub init_code: Vec<MutExpression>,
406
407    /// For each node, an expression that returns a `{x: length, y: length, width: length, height: length}`
408    pub geometries: Vec<Option<MutExpression>>,
409
410    pub layout_info_h: MutExpression,
411    pub layout_info_v: MutExpression,
412    pub child_of_layout: bool,
413    pub grid_layout_input_for_repeated: Option<MutExpression>,
414    /// Expression that builds a FlexboxLayoutItemInfo for a repeated element in a FlexboxLayout.
415    /// Contains property references to flex-grow, flex-shrink, flex-basis, align-self, order.
416    pub flexbox_layout_item_info_for_repeated: Option<MutExpression>,
417    /// True when this is a repeated Row in a GridLayout, meaning layout_item_info
418    /// needs to be able to return layout info for individual children
419    pub is_repeated_row: bool,
420    /// The list of direct grid layout children for a repeated Row.
421    /// Used to generate `layout_item_info` which returns layout info for a specific child.
422    pub grid_layout_children: TiVec<GridLayoutChildIdx, GridLayoutChildLayoutInfo>,
423    /// For repeated Rows with children: template of children in declaration order
424    /// (statics and inner repeaters). Used by code generators to produce
425    /// `grid_layout_input_data` and `layout_item_info`.
426    pub row_child_templates: Option<Vec<RowChildTemplateInfo>>,
427
428    /// Maps (item_index, property) to an expression
429    pub accessible_prop: BTreeMap<(u32, String), MutExpression>,
430
431    /// Maps item index to a list of encoded element infos of the element  (type name, qualified ids).
432    pub element_infos: BTreeMap<u32, String>,
433
434    pub prop_analysis: HashMap<MemberReference, PropAnalysis>,
435}
436
437#[derive(Debug)]
438pub struct PopupWindow {
439    pub item_tree: ItemTree,
440    pub position: MutExpression,
441}
442
443#[derive(Debug)]
444pub struct PopupMenu {
445    pub item_tree: ItemTree,
446    pub sub_menu: MemberReference,
447    pub activated: MemberReference,
448    pub close: MemberReference,
449    pub entries: MemberReference,
450}
451
452#[derive(Debug)]
453pub struct Timer {
454    pub interval: MutExpression,
455    pub running: MutExpression,
456    pub triggered: MutExpression,
457}
458
459#[derive(Debug, Clone)]
460pub struct PropAnalysis {
461    /// Index in SubComponent::property_init for this property
462    pub property_init: Option<usize>,
463    pub analysis: crate::object_tree::PropertyAnalysis,
464}
465
466impl SubComponent {
467    /// total count of repeater, including in sub components
468    pub fn repeater_count(&self, cu: &CompilationUnit) -> u32 {
469        let mut count = (self.repeated.len() + self.component_containers.len()) as u32;
470        for x in self.sub_components.iter() {
471            count += cu.sub_components[x.ty].repeater_count(cu);
472        }
473        count
474    }
475
476    /// total count of items, including in sub components
477    pub fn child_item_count(&self, cu: &CompilationUnit) -> u32 {
478        let mut count = self.items.len() as u32;
479        for x in self.sub_components.iter() {
480            count += cu.sub_components[x.ty].child_item_count(cu);
481        }
482        count
483    }
484}
485
486#[derive(Debug)]
487pub struct SubComponentInstance {
488    pub ty: SubComponentIdx,
489    pub name: SmolStr,
490    pub index_in_tree: u32,
491    pub index_of_first_child_in_tree: u32,
492    pub repeater_offset: u32,
493}
494
495#[derive(Debug)]
496pub struct ItemTree {
497    pub root: SubComponentIdx,
498    pub tree: TreeNode,
499}
500
501#[derive(Debug)]
502pub struct PublicComponent {
503    pub public_properties: PublicProperties,
504    pub private_properties: PrivateProperties,
505    pub item_tree: ItemTree,
506    pub name: SmolStr,
507}
508
509#[derive(Debug)]
510pub struct CompilationUnit {
511    pub public_components: Vec<PublicComponent>,
512    /// Storage for all sub-components
513    pub sub_components: TiVec<SubComponentIdx, SubComponent>,
514    /// The sub-components that are not item-tree root
515    pub used_sub_components: Vec<SubComponentIdx>,
516    pub globals: TiVec<GlobalIdx, GlobalComponent>,
517    pub popup_menu: Option<PopupMenu>,
518    pub has_debug_info: bool,
519    #[cfg(feature = "bundle-translations")]
520    pub translations: Option<crate::translations::Translations>,
521}
522
523impl CompilationUnit {
524    pub fn for_each_sub_components<'a>(
525        &'a self,
526        visitor: &mut dyn FnMut(&'a SubComponent, &EvaluationContext<'_>),
527    ) {
528        fn visit_component<'a>(
529            root: &'a CompilationUnit,
530            c: SubComponentIdx,
531            visitor: &mut dyn FnMut(&'a SubComponent, &EvaluationContext<'_>),
532            parent: Option<&ParentScope<'_>>,
533        ) {
534            let ctx = EvaluationContext::new_sub_component(root, c, (), parent);
535            let sc = &root.sub_components[c];
536            visitor(sc, &ctx);
537            for (idx, r) in sc.repeated.iter_enumerated() {
538                visit_component(
539                    root,
540                    r.sub_tree.root,
541                    visitor,
542                    Some(&ParentScope::new(&ctx, Some(idx))),
543                );
544            }
545            for popup in &sc.popup_windows {
546                visit_component(
547                    root,
548                    popup.item_tree.root,
549                    visitor,
550                    Some(&ParentScope::new(&ctx, None)),
551                );
552            }
553            for menu_tree in &sc.menu_item_trees {
554                visit_component(root, menu_tree.root, visitor, Some(&ParentScope::new(&ctx, None)));
555            }
556        }
557        for c in &self.used_sub_components {
558            visit_component(self, *c, visitor, None);
559        }
560        for p in &self.public_components {
561            visit_component(self, p.item_tree.root, visitor, None);
562        }
563        if let Some(p) = &self.popup_menu {
564            visit_component(self, p.item_tree.root, visitor, None);
565        }
566    }
567
568    pub fn for_each_expression<'a>(
569        &'a self,
570        visitor: &mut dyn FnMut(&'a super::MutExpression, &EvaluationContext<'_>),
571    ) {
572        self.for_each_sub_components(&mut |sc, ctx| {
573            for e in &sc.init_code {
574                visitor(e, ctx);
575            }
576            for (_, e) in &sc.property_init {
577                visitor(&e.expression, ctx);
578            }
579            visitor(&sc.layout_info_h, ctx);
580            visitor(&sc.layout_info_v, ctx);
581            if let Some(e) = &sc.grid_layout_input_for_repeated {
582                visitor(e, ctx);
583            }
584            if let Some(e) = &sc.flexbox_layout_item_info_for_repeated {
585                visitor(e, ctx);
586            }
587            for e in sc.accessible_prop.values() {
588                visitor(e, ctx);
589            }
590            for i in sc.geometries.iter().flatten() {
591                visitor(i, ctx);
592            }
593            for (_, e) in sc.change_callbacks.iter() {
594                visitor(e, ctx);
595            }
596        });
597        for (idx, g) in self.globals.iter_enumerated() {
598            let ctx = EvaluationContext::new_global(self, idx, ());
599            for e in g.init_values.values() {
600                visitor(&e.expression, &ctx)
601            }
602            for e in g.change_callbacks.values() {
603                visitor(e, &ctx)
604            }
605        }
606    }
607}
608
609/// Depending on the type, this can also be a Callback or a Function
610#[derive(Debug, Clone)]
611pub struct PublicProperty {
612    pub name: SmolStr,
613    pub ty: Type,
614    pub prop: MemberReference,
615    pub read_only: bool,
616}
617pub type PublicProperties = Vec<PublicProperty>;
618pub type PrivateProperties = Vec<(SmolStr, Type)>;