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