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, ParentCtx};
5use crate::langtype::{NativeClass, Type};
6use smol_str::SmolStr;
7use std::cell::{Cell, RefCell};
8use std::collections::{BTreeMap, HashMap};
9use std::num::NonZeroUsize;
10use std::rc::Rc;
11use typed_index_collections::TiVec;
12
13#[derive(
14    Debug, Clone, Copy, derive_more::Into, derive_more::From, Hash, PartialEq, Eq, PartialOrd, Ord,
15)]
16pub struct PropertyIdx(usize);
17#[derive(Debug, Clone, Copy, derive_more::Into, derive_more::From, Hash, PartialEq, Eq)]
18pub struct FunctionIdx(usize);
19#[derive(Debug, Clone, Copy, derive_more::Into, derive_more::From)]
20pub struct SubComponentIdx(usize);
21#[derive(Debug, Clone, Copy, derive_more::Into, derive_more::From, Hash, PartialEq, Eq)]
22pub struct GlobalIdx(usize);
23#[derive(Debug, Clone, Copy, derive_more::Into, derive_more::From, Hash, PartialEq, Eq)]
24pub struct SubComponentInstanceIdx(usize);
25#[derive(Debug, Clone, Copy, derive_more::Into, derive_more::From, Hash, PartialEq, Eq)]
26pub struct ItemInstanceIdx(usize);
27#[derive(Debug, Clone, Copy, derive_more::Into, derive_more::From, Hash, PartialEq, Eq)]
28pub struct RepeatedElementIdx(usize);
29
30#[derive(Debug, Clone, derive_more::Deref)]
31pub struct MutExpression(RefCell<Expression>);
32
33impl From<Expression> for MutExpression {
34    fn from(e: Expression) -> Self {
35        Self(e.into())
36    }
37}
38
39impl MutExpression {
40    pub fn ty(&self, ctx: &dyn super::TypeResolutionContext) -> Type {
41        self.0.borrow().ty(ctx)
42    }
43}
44
45#[derive(Debug, Clone)]
46pub enum Animation {
47    /// The expression is a Struct with the animation fields
48    Static(Expression),
49    Transition(Expression),
50}
51
52#[derive(Debug, Clone)]
53pub struct BindingExpression {
54    pub expression: MutExpression,
55    pub animation: Option<Animation>,
56    /// When true, we can initialize the property with `set` otherwise, `set_binding` must be used
57    pub is_constant: bool,
58    /// When true, the expression is a "state binding".  Despite the type of the expression being a integer
59    /// the property is of type StateInfo and the `set_state_binding` need to be used on the property
60    pub is_state_info: bool,
61
62    /// The amount of time this binding is used
63    /// This property is only valid after the [`count_property_use`](super::optim_passes::count_property_use) pass
64    pub use_count: Cell<usize>,
65}
66
67#[derive(Debug)]
68pub struct GlobalComponent {
69    pub name: SmolStr,
70    pub properties: TiVec<PropertyIdx, Property>,
71    pub functions: TiVec<FunctionIdx, Function>,
72    /// One entry per property
73    pub init_values: TiVec<PropertyIdx, Option<BindingExpression>>,
74    // maps property to its changed callback
75    pub change_callbacks: BTreeMap<PropertyIdx, MutExpression>,
76    pub const_properties: TiVec<PropertyIdx, bool>,
77    pub public_properties: PublicProperties,
78    pub private_properties: PrivateProperties,
79    /// true if we should expose the global in the generated API
80    pub exported: bool,
81    /// The extra names under which this component should be accessible
82    /// if it is exported several time.
83    pub aliases: Vec<SmolStr>,
84    /// True when this is a built-in global that does not need to be generated
85    pub is_builtin: bool,
86    /// True if this component is imported from an external library
87    pub from_library: bool,
88    /// Analysis for each properties
89    pub prop_analysis: TiVec<PropertyIdx, crate::object_tree::PropertyAnalysis>,
90}
91
92impl GlobalComponent {
93    pub fn must_generate(&self) -> bool {
94        !self.is_builtin
95            && !self.from_library
96            && (self.exported
97                || !self.functions.is_empty()
98                || self.properties.iter().any(|p| p.use_count.get() > 0))
99    }
100}
101
102/// a Reference to a property, in the context of a SubComponent
103#[derive(Clone, Debug, Hash, PartialEq, Eq)]
104pub enum PropertyReference {
105    /// A property relative to this SubComponent
106    Local { sub_component_path: Vec<SubComponentInstanceIdx>, property_index: PropertyIdx },
107    /// A property in a Native item
108    InNativeItem {
109        sub_component_path: Vec<SubComponentInstanceIdx>,
110        item_index: ItemInstanceIdx,
111        prop_name: String,
112    },
113    /// The properties is a property relative to a parent ItemTree (`level` level deep)
114    InParent { level: NonZeroUsize, parent_reference: Box<PropertyReference> },
115    /// The property within a GlobalComponent
116    Global { global_index: GlobalIdx, property_index: PropertyIdx },
117
118    /// A function in a sub component.
119    Function { sub_component_path: Vec<SubComponentInstanceIdx>, function_index: FunctionIdx },
120    /// A function in a global.
121    GlobalFunction { global_index: GlobalIdx, function_index: FunctionIdx },
122}
123
124#[derive(Debug, Default)]
125pub struct Property {
126    pub name: SmolStr,
127    pub ty: Type,
128    /// The amount of time this property is used of another property
129    /// This property is only valid after the [`count_property_use`](super::optim_passes::count_property_use) pass
130    pub use_count: Cell<usize>,
131}
132
133#[derive(Debug)]
134pub struct Function {
135    pub name: SmolStr,
136    pub ret_ty: Type,
137    pub args: Vec<Type>,
138    pub code: Expression,
139}
140
141#[derive(Debug, Clone)]
142/// The property references might be either in the parent context, or in the
143/// repeated's component context
144pub struct ListViewInfo {
145    pub viewport_y: PropertyReference,
146    pub viewport_height: PropertyReference,
147    pub viewport_width: PropertyReference,
148    /// The ListView's inner visible height (not counting eventual scrollbar)
149    pub listview_height: PropertyReference,
150    /// The ListView's inner visible width (not counting eventual scrollbar)
151    pub listview_width: PropertyReference,
152
153    // In the repeated component context
154    pub prop_y: PropertyReference,
155    // In the repeated component context
156    pub prop_height: PropertyReference,
157}
158
159#[derive(Debug)]
160pub struct RepeatedElement {
161    pub model: MutExpression,
162    /// Within the sub_tree's root component. None for `if`
163    pub index_prop: Option<PropertyIdx>,
164    /// Within the sub_tree's root component. None for `if`
165    pub data_prop: Option<PropertyIdx>,
166    pub sub_tree: ItemTree,
167    /// The index of the item node in the parent tree
168    pub index_in_tree: u32,
169
170    pub listview: Option<ListViewInfo>,
171
172    /// Access through this in case of the element being a `is_component_placeholder`
173    pub container_item_index: Option<ItemInstanceIdx>,
174}
175
176#[derive(Debug)]
177pub struct ComponentContainerElement {
178    /// The index of the `ComponentContainer` in the enclosing components `item_tree` array
179    pub component_container_item_tree_index: u32,
180    /// The index of the `ComponentContainer` item in the enclosing components `items` array
181    pub component_container_items_index: ItemInstanceIdx,
182    /// The index to a dynamic tree node where the component is supposed to be embedded at
183    pub component_placeholder_item_tree_index: u32,
184}
185
186pub struct Item {
187    pub ty: Rc<NativeClass>,
188    pub name: SmolStr,
189    /// Index in the item tree array
190    pub index_in_tree: u32,
191}
192
193impl std::fmt::Debug for Item {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        f.debug_struct("Item")
196            .field("ty", &self.ty.class_name)
197            .field("name", &self.name)
198            .field("index_in_tree", &self.index_in_tree)
199            .finish()
200    }
201}
202
203#[derive(Debug)]
204pub struct TreeNode {
205    pub sub_component_path: Vec<SubComponentInstanceIdx>,
206    /// Either an index in the items, or the local dynamic index for repeater or component container
207    pub item_index: itertools::Either<ItemInstanceIdx, u32>,
208    pub children: Vec<TreeNode>,
209    pub is_accessible: bool,
210}
211
212impl TreeNode {
213    fn children_count(&self) -> usize {
214        let mut count = self.children.len();
215        for c in &self.children {
216            count += c.children_count();
217        }
218        count
219    }
220
221    /// Visit this, and the children.
222    /// `children_offset` must be set to `1` for the root
223    pub fn visit_in_array(
224        &self,
225        visitor: &mut dyn FnMut(
226            &TreeNode,
227            /*children_offset: */ usize,
228            /*parent_index: */ usize,
229        ),
230    ) {
231        visitor(self, 1, 0);
232        visit_in_array_recursive(self, 1, 0, visitor);
233
234        fn visit_in_array_recursive(
235            node: &TreeNode,
236            children_offset: usize,
237            current_index: usize,
238            visitor: &mut dyn FnMut(&TreeNode, usize, usize),
239        ) {
240            let mut offset = children_offset + node.children.len();
241            for c in &node.children {
242                visitor(c, offset, current_index);
243                offset += c.children_count();
244            }
245
246            let mut offset = children_offset + node.children.len();
247            for (i, c) in node.children.iter().enumerate() {
248                visit_in_array_recursive(c, offset, children_offset + i, visitor);
249                offset += c.children_count();
250            }
251        }
252    }
253}
254
255#[derive(Debug)]
256pub struct SubComponent {
257    pub name: SmolStr,
258    pub properties: TiVec<PropertyIdx, Property>,
259    pub functions: TiVec<FunctionIdx, Function>,
260    pub items: TiVec<ItemInstanceIdx, Item>,
261    pub repeated: TiVec<RepeatedElementIdx, RepeatedElement>,
262    pub component_containers: Vec<ComponentContainerElement>,
263    pub popup_windows: Vec<PopupWindow>,
264    /// The MenuItem trees. The index is stored in a Expression::NumberLiteral in the arguments of BuiltinFunction::ShowPopupMenu and BuiltinFunction::SetupNativeMenuBar
265    pub menu_item_trees: Vec<ItemTree>,
266    pub timers: Vec<Timer>,
267    pub sub_components: TiVec<SubComponentInstanceIdx, SubComponentInstance>,
268    /// The initial value or binding for properties.
269    /// This is ordered in the order they must be set.
270    pub property_init: Vec<(PropertyReference, BindingExpression)>,
271    pub change_callbacks: Vec<(PropertyReference, MutExpression)>,
272    /// The animation for properties which are animated
273    pub animations: HashMap<PropertyReference, Expression>,
274    pub two_way_bindings: Vec<(PropertyReference, PropertyReference)>,
275    pub const_properties: Vec<PropertyReference>,
276    /// Code that is run in the sub component constructor, after property initializations
277    pub init_code: Vec<MutExpression>,
278
279    /// For each node, an expression that returns a `{x: length, y: length, width: length, height: length}`
280    pub geometries: Vec<Option<MutExpression>>,
281
282    pub layout_info_h: MutExpression,
283    pub layout_info_v: MutExpression,
284
285    /// Maps (item_index, property) to an expression
286    pub accessible_prop: BTreeMap<(u32, String), MutExpression>,
287
288    /// Maps item index to a list of encoded element infos of the element  (type name, qualified ids).
289    pub element_infos: BTreeMap<u32, String>,
290
291    pub prop_analysis: HashMap<PropertyReference, PropAnalysis>,
292}
293
294#[derive(Debug)]
295pub struct PopupWindow {
296    pub item_tree: ItemTree,
297    pub position: MutExpression,
298}
299
300#[derive(Debug)]
301pub struct PopupMenu {
302    pub item_tree: ItemTree,
303    pub sub_menu: PropertyReference,
304    pub activated: PropertyReference,
305    pub close: PropertyReference,
306    pub entries: PropertyReference,
307}
308
309#[derive(Debug)]
310pub struct Timer {
311    pub interval: MutExpression,
312    pub running: MutExpression,
313    pub triggered: MutExpression,
314}
315
316#[derive(Debug, Clone)]
317pub struct PropAnalysis {
318    /// Index in SubComponent::property_init for this property
319    pub property_init: Option<usize>,
320    pub analysis: crate::object_tree::PropertyAnalysis,
321}
322
323impl SubComponent {
324    /// total count of repeater, including in sub components
325    pub fn repeater_count(&self, cu: &CompilationUnit) -> u32 {
326        let mut count = (self.repeated.len() + self.component_containers.len()) as u32;
327        for x in self.sub_components.iter() {
328            count += cu.sub_components[x.ty].repeater_count(cu);
329        }
330        count
331    }
332
333    /// total count of items, including in sub components
334    pub fn child_item_count(&self, cu: &CompilationUnit) -> u32 {
335        let mut count = self.items.len() as u32;
336        for x in self.sub_components.iter() {
337            count += cu.sub_components[x.ty].child_item_count(cu);
338        }
339        count
340    }
341
342    /// Return if a local property is used. (unused property shouldn't be generated)
343    pub fn prop_used(&self, prop: &PropertyReference, cu: &CompilationUnit) -> bool {
344        if let PropertyReference::Local { property_index, sub_component_path } = prop {
345            let mut sc = self;
346            for i in sub_component_path {
347                sc = &cu.sub_components[sc.sub_components[*i].ty];
348            }
349            if sc.properties[*property_index].use_count.get() == 0 {
350                return false;
351            }
352        }
353        true
354    }
355}
356
357#[derive(Debug)]
358pub struct SubComponentInstance {
359    pub ty: SubComponentIdx,
360    pub name: SmolStr,
361    pub index_in_tree: u32,
362    pub index_of_first_child_in_tree: u32,
363    pub repeater_offset: u32,
364}
365
366#[derive(Debug)]
367pub struct ItemTree {
368    pub root: SubComponentIdx,
369    pub tree: TreeNode,
370    /// This tree has a parent. e.g: it is a Repeater or a PopupWindow whose property can access
371    /// the parent ItemTree.
372    /// The String is the type of the parent ItemTree
373    pub parent_context: Option<SmolStr>,
374}
375
376#[derive(Debug)]
377pub struct PublicComponent {
378    pub public_properties: PublicProperties,
379    pub private_properties: PrivateProperties,
380    pub item_tree: ItemTree,
381    pub name: SmolStr,
382}
383
384#[derive(Debug)]
385pub struct CompilationUnit {
386    pub public_components: Vec<PublicComponent>,
387    /// Storage for all sub-components
388    pub sub_components: TiVec<SubComponentIdx, SubComponent>,
389    /// The sub-components that are not item-tree root
390    pub used_sub_components: Vec<SubComponentIdx>,
391    pub globals: TiVec<GlobalIdx, GlobalComponent>,
392    pub popup_menu: Option<PopupMenu>,
393    pub has_debug_info: bool,
394    #[cfg(feature = "bundle-translations")]
395    pub translations: Option<crate::translations::Translations>,
396}
397
398impl CompilationUnit {
399    pub fn for_each_sub_components<'a>(
400        &'a self,
401        visitor: &mut dyn FnMut(&'a SubComponent, &EvaluationContext<'_>),
402    ) {
403        fn visit_component<'a>(
404            root: &'a CompilationUnit,
405            c: SubComponentIdx,
406            visitor: &mut dyn FnMut(&'a SubComponent, &EvaluationContext<'_>),
407            parent: Option<ParentCtx<'_>>,
408        ) {
409            let ctx = EvaluationContext::new_sub_component(root, c, (), parent);
410            let sc = &root.sub_components[c];
411            visitor(sc, &ctx);
412            for (idx, r) in sc.repeated.iter_enumerated() {
413                visit_component(
414                    root,
415                    r.sub_tree.root,
416                    visitor,
417                    Some(ParentCtx::new(&ctx, Some(idx))),
418                );
419            }
420            for popup in &sc.popup_windows {
421                visit_component(
422                    root,
423                    popup.item_tree.root,
424                    visitor,
425                    Some(ParentCtx::new(&ctx, None)),
426                );
427            }
428            for menu_tree in &sc.menu_item_trees {
429                visit_component(root, menu_tree.root, visitor, Some(ParentCtx::new(&ctx, None)));
430            }
431        }
432        for c in &self.used_sub_components {
433            visit_component(self, *c, visitor, None);
434        }
435        for p in &self.public_components {
436            visit_component(self, p.item_tree.root, visitor, None);
437        }
438        if let Some(p) = &self.popup_menu {
439            visit_component(self, p.item_tree.root, visitor, None);
440        }
441    }
442
443    pub fn for_each_expression<'a>(
444        &'a self,
445        visitor: &mut dyn FnMut(&'a super::MutExpression, &EvaluationContext<'_>),
446    ) {
447        self.for_each_sub_components(&mut |sc, ctx| {
448            for e in &sc.init_code {
449                visitor(e, ctx);
450            }
451            for (_, e) in &sc.property_init {
452                visitor(&e.expression, ctx);
453            }
454            visitor(&sc.layout_info_h, ctx);
455            visitor(&sc.layout_info_v, ctx);
456            for e in sc.accessible_prop.values() {
457                visitor(e, ctx);
458            }
459            for i in sc.geometries.iter().flatten() {
460                visitor(i, ctx);
461            }
462            for (_, e) in sc.change_callbacks.iter() {
463                visitor(e, ctx);
464            }
465        });
466        for (idx, g) in self.globals.iter_enumerated() {
467            let ctx = EvaluationContext::new_global(self, idx, ());
468            for e in g.init_values.iter().filter_map(|x| x.as_ref()) {
469                visitor(&e.expression, &ctx)
470            }
471            for e in g.change_callbacks.values() {
472                visitor(e, &ctx)
473            }
474        }
475    }
476}
477
478#[derive(Debug, Clone)]
479pub struct PublicProperty {
480    pub name: SmolStr,
481    pub ty: Type,
482    pub prop: PropertyReference,
483    pub read_only: bool,
484}
485pub type PublicProperties = Vec<PublicProperty>;
486pub type PrivateProperties = Vec<(SmolStr, Type)>;