Skip to main content

i_slint_compiler/
typeregister.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
4// cSpell: ignore imum
5
6use smol_str::{SmolStr, StrExt, ToSmolStr};
7use std::cell::RefCell;
8use std::collections::{BTreeMap, HashMap, HashSet};
9use std::rc::Rc;
10
11use crate::expression_tree::BuiltinFunction;
12use crate::langtype::{
13    BuiltinElement, BuiltinPrivateStruct, BuiltinPropertyDefault, BuiltinPropertyInfo,
14    BuiltinPublicStruct, ElementType, Enumeration, Function, PropertyLookupResult, Struct, Type,
15};
16use crate::object_tree::{Component, PropertyVisibility};
17use crate::typeloader;
18
19pub const RESERVED_GEOMETRY_PROPERTIES: &[(&str, Type)] = &[
20    ("x", Type::LogicalLength),
21    ("y", Type::LogicalLength),
22    ("width", Type::LogicalLength),
23    ("height", Type::LogicalLength),
24    ("z", Type::Float32),
25];
26
27pub const RESERVED_LAYOUT_PROPERTIES: &[(&str, Type)] = &[
28    ("min-width", Type::LogicalLength),
29    ("min-height", Type::LogicalLength),
30    ("max-width", Type::LogicalLength),
31    ("max-height", Type::LogicalLength),
32    ("padding", Type::LogicalLength),
33    ("padding-left", Type::LogicalLength),
34    ("padding-right", Type::LogicalLength),
35    ("padding-top", Type::LogicalLength),
36    ("padding-bottom", Type::LogicalLength),
37    ("preferred-width", Type::LogicalLength),
38    ("preferred-height", Type::LogicalLength),
39    ("horizontal-stretch", Type::Float32),
40    ("vertical-stretch", Type::Float32),
41];
42
43pub const RESERVED_GRIDLAYOUT_PROPERTIES: &[(&str, Type)] = &[
44    ("col", Type::Int32),
45    ("row", Type::Int32),
46    ("colspan", Type::Int32),
47    ("rowspan", Type::Int32),
48];
49
50// Note: flex-align-self is also a flexbox property but is added in reserved_properties()
51// because Type::Enumeration requires a runtime Rc allocation.
52pub const RESERVED_FLEXBOXLAYOUT_PROPERTIES: &[(&str, Type)] = &[
53    ("flex-grow", Type::Float32),
54    ("flex-shrink", Type::Float32),
55    ("flex-basis", Type::LogicalLength),
56    ("flex-order", Type::Int32),
57];
58
59macro_rules! declare_enums {
60    ($( $(#[$enum_doc:meta])* enum $Name:ident { $( $(#[$value_doc:meta])* $Value:ident,)* })*) => {
61        #[allow(non_snake_case)]
62        pub struct BuiltinEnums {
63            $(pub $Name : Rc<Enumeration>),*
64        }
65        impl BuiltinEnums {
66            fn new() -> Self {
67                Self {
68                    $($Name : Rc::new(Enumeration {
69                        name: stringify!($Name).replace_smolstr("_", "-"),
70                        values: vec![$(crate::generator::to_kebab_case(stringify!($Value).trim_start_matches("r#")).into()),*],
71                        default_value: 0,
72                        node: None,
73                    })),*
74                }
75            }
76            fn fill_register(&self, register: &mut TypeRegister) {
77                $(if stringify!($Name) != "PathEvent" {
78                    register.insert_type_with_name(
79                        Type::Enumeration(self.$Name.clone()),
80                        stringify!($Name).replace_smolstr("_", "-")
81                    );
82                })*
83            }
84        }
85    };
86}
87
88i_slint_common::for_each_enums!(declare_enums);
89
90pub struct BuiltinTypes {
91    pub enums: BuiltinEnums,
92    pub noarg_callback_type: Type,
93    pub strarg_callback_type: Type,
94    pub logical_point_type: Rc<Struct>,
95    pub logical_size_type: Rc<Struct>,
96    pub font_metrics_type: Type,
97    pub layout_info_type: Rc<Struct>,
98    pub state_info_type: Rc<Struct>,
99    pub gridlayout_input_data_type: Type,
100    pub path_element_type: Type,
101    pub layout_item_info_type: Type,
102    pub flexbox_layout_item_info_type: Type,
103}
104
105impl BuiltinTypes {
106    fn new() -> Self {
107        let layout_info_type = Rc::new(Struct {
108            fields: ["min", "max", "preferred"]
109                .iter()
110                .map(|s| (SmolStr::new_static(s), Type::LogicalLength))
111                .chain(
112                    ["min_percent", "max_percent", "stretch"]
113                        .iter()
114                        .map(|s| (SmolStr::new_static(s), Type::Float32)),
115                )
116                .collect(),
117            name: BuiltinPrivateStruct::LayoutInfo.into(),
118        });
119        let enums = BuiltinEnums::new();
120        let flex_align_self_type = Type::Enumeration(enums.FlexboxLayoutAlignSelf.clone());
121        Self {
122            enums,
123            logical_point_type: Rc::new(Struct {
124                fields: IntoIterator::into_iter([
125                    (SmolStr::new_static("x"), Type::LogicalLength),
126                    (SmolStr::new_static("y"), Type::LogicalLength),
127                ])
128                .collect(),
129                name: BuiltinPublicStruct::LogicalPosition.into(),
130            }),
131            logical_size_type: Rc::new(Struct {
132                fields: IntoIterator::into_iter([
133                    (SmolStr::new_static("width"), Type::LogicalLength),
134                    (SmolStr::new_static("height"), Type::LogicalLength),
135                ])
136                .collect(),
137                name: BuiltinPublicStruct::LogicalSize.into(),
138            }),
139            font_metrics_type: Type::Struct(Rc::new(Struct {
140                fields: IntoIterator::into_iter([
141                    (SmolStr::new_static("ascent"), Type::LogicalLength),
142                    (SmolStr::new_static("descent"), Type::LogicalLength),
143                    (SmolStr::new_static("x-height"), Type::LogicalLength),
144                    (SmolStr::new_static("cap-height"), Type::LogicalLength),
145                ])
146                .collect(),
147                name: BuiltinPrivateStruct::FontMetrics.into(),
148            })),
149            noarg_callback_type: Type::Callback(Rc::new(Function {
150                return_type: Type::Void,
151                args: Vec::new(),
152                arg_names: Vec::new(),
153            })),
154            strarg_callback_type: Type::Callback(Rc::new(Function {
155                return_type: Type::Void,
156                args: vec![Type::String],
157                arg_names: Vec::new(),
158            })),
159            layout_info_type: layout_info_type.clone(),
160            state_info_type: Rc::new(Struct {
161                fields: IntoIterator::into_iter([
162                    (SmolStr::new_static("current-state"), Type::Int32),
163                    (SmolStr::new_static("previous-state"), Type::Int32),
164                    (SmolStr::new_static("change-time"), Type::Duration),
165                ])
166                .collect(),
167                name: BuiltinPrivateStruct::StateInfo.into(),
168            }),
169            path_element_type: Type::Struct(Rc::new(Struct {
170                fields: Default::default(),
171                name: BuiltinPrivateStruct::PathElement.into(),
172            })),
173            layout_item_info_type: Type::Struct(Rc::new(Struct {
174                fields: IntoIterator::into_iter([(
175                    "constraint".into(),
176                    layout_info_type.clone().into(),
177                )])
178                .collect(),
179                name: BuiltinPrivateStruct::LayoutItemInfo.into(),
180            })),
181            flexbox_layout_item_info_type: Type::Struct(Rc::new(Struct {
182                fields: IntoIterator::into_iter([
183                    ("constraint".into(), layout_info_type.into()),
184                    ("flex-grow".into(), Type::Float32),
185                    ("flex-shrink".into(), Type::Float32),
186                    ("flex-basis".into(), Type::Float32),
187                    ("flex-align-self".into(), flex_align_self_type),
188                    ("flex-order".into(), Type::Int32),
189                ])
190                .collect(),
191                name: BuiltinPrivateStruct::FlexboxLayoutItemInfo.into(),
192            })),
193            gridlayout_input_data_type: Type::Struct(Rc::new(Struct {
194                fields: IntoIterator::into_iter([
195                    ("row".into(), Type::Int32),
196                    ("column".into(), Type::Int32),
197                    ("rowspan".into(), Type::Int32),
198                    ("colspan".into(), Type::Int32),
199                ])
200                .collect(),
201                name: BuiltinPrivateStruct::GridLayoutInputData.into(),
202            })),
203        }
204    }
205}
206
207thread_local! {
208    pub static BUILTIN: BuiltinTypes = BuiltinTypes::new();
209}
210
211const RESERVED_OTHER_PROPERTIES: &[(&str, Type)] = &[
212    ("clip", Type::Bool),
213    ("opacity", Type::Float32),
214    ("cache-rendering-hint", Type::Bool),
215    ("visible", Type::Bool), // ("enabled", Type::Bool),
216];
217
218pub const RESERVED_DROP_SHADOW_PROPERTIES: &[(&str, Type)] = &[
219    ("drop-shadow-offset-x", Type::LogicalLength),
220    ("drop-shadow-offset-y", Type::LogicalLength),
221    ("drop-shadow-blur", Type::LogicalLength),
222    ("drop-shadow-color", Type::Color),
223];
224
225pub const RESERVED_TRANSFORM_PROPERTIES: &[(&str, Type)] = &[
226    ("transform-rotation", Type::Angle),
227    ("transform-scale-x", Type::Float32),
228    ("transform-scale-y", Type::Float32),
229    ("transform-scale", Type::Float32),
230];
231
232pub fn transform_origin_property() -> (&'static str, Rc<Struct>) {
233    ("transform-origin", logical_point_type())
234}
235
236pub const DEPRECATED_ROTATION_ORIGIN_PROPERTIES: [(&str, Type); 2] =
237    [("rotation-origin-x", Type::LogicalLength), ("rotation-origin-y", Type::LogicalLength)];
238
239pub fn noarg_callback_type() -> Type {
240    BUILTIN.with(|types| types.noarg_callback_type.clone())
241}
242
243fn strarg_callback_type() -> Type {
244    BUILTIN.with(|types| types.strarg_callback_type.clone())
245}
246
247pub fn reserved_accessibility_properties() -> impl Iterator<Item = (&'static str, Type)> {
248    [
249        //("accessible-role", ...)
250        ("accessible-checkable", Type::Bool),
251        ("accessible-checked", Type::Bool),
252        ("accessible-delegate-focus", Type::Int32),
253        ("accessible-description", Type::String),
254        ("accessible-enabled", Type::Bool),
255        ("accessible-expandable", Type::Bool),
256        ("accessible-expanded", Type::Bool),
257        ("accessible-id", Type::String),
258        ("accessible-label", Type::String),
259        ("accessible-value", Type::String),
260        ("accessible-value-maximum", Type::Float32),
261        ("accessible-value-minimum", Type::Float32),
262        ("accessible-value-step", Type::Float32),
263        ("accessible-placeholder-text", Type::String),
264        ("accessible-action-default", noarg_callback_type()),
265        ("accessible-action-increment", noarg_callback_type()),
266        ("accessible-action-decrement", noarg_callback_type()),
267        ("accessible-action-set-value", strarg_callback_type()),
268        ("accessible-action-expand", noarg_callback_type()),
269        ("accessible-item-selectable", Type::Bool),
270        ("accessible-item-selected", Type::Bool),
271        ("accessible-item-index", Type::Int32),
272        ("accessible-item-count", Type::Int32),
273        ("accessible-read-only", Type::Bool),
274    ]
275    .into_iter()
276}
277
278/// list of reserved property injected in every item
279pub fn reserved_properties() -> impl Iterator<Item = (&'static str, Type, PropertyVisibility)> {
280    RESERVED_GEOMETRY_PROPERTIES
281        .iter()
282        .chain(RESERVED_LAYOUT_PROPERTIES.iter())
283        .chain(RESERVED_OTHER_PROPERTIES.iter())
284        .chain(RESERVED_DROP_SHADOW_PROPERTIES.iter())
285        .chain(RESERVED_TRANSFORM_PROPERTIES.iter())
286        .chain(DEPRECATED_ROTATION_ORIGIN_PROPERTIES.iter())
287        .map(|(k, v)| (*k, v.clone(), PropertyVisibility::Input))
288        .chain(
289            std::iter::once(transform_origin_property())
290                .map(|(k, v)| (k, v.into(), PropertyVisibility::Input)),
291        )
292        .chain(reserved_accessibility_properties().map(|(k, v)| (k, v, PropertyVisibility::Input)))
293        .chain(
294            RESERVED_GRIDLAYOUT_PROPERTIES
295                .iter()
296                .map(|(k, v)| (*k, v.clone(), PropertyVisibility::Input)),
297        )
298        .chain(
299            RESERVED_FLEXBOXLAYOUT_PROPERTIES
300                .iter()
301                .map(|(k, v)| (*k, v.clone(), PropertyVisibility::Input)),
302        )
303        // flex-align-self is a flexbox-layout property but can't be in the const array
304        // because Type::Enumeration requires a runtime Rc allocation.
305        .chain(std::iter::once((
306            "flex-align-self",
307            Type::Enumeration(BUILTIN.with(|e| e.enums.FlexboxLayoutAlignSelf.clone())),
308            PropertyVisibility::Input,
309        )))
310        .chain(IntoIterator::into_iter([
311            ("absolute-position", logical_point_type().into(), PropertyVisibility::Output),
312            ("forward-focus", Type::ElementReference, PropertyVisibility::Constexpr),
313            (
314                "focus",
315                Type::Function(BuiltinFunction::SetFocusItem.ty()),
316                PropertyVisibility::Public,
317            ),
318            (
319                "clear-focus",
320                Type::Function(BuiltinFunction::ClearFocusItem.ty()),
321                PropertyVisibility::Public,
322            ),
323            (
324                "dialog-button-role",
325                Type::Enumeration(BUILTIN.with(|e| e.enums.DialogButtonRole.clone())),
326                PropertyVisibility::Constexpr,
327            ),
328            (
329                "accessible-role",
330                Type::Enumeration(BUILTIN.with(|e| e.enums.AccessibleRole.clone())),
331                PropertyVisibility::Constexpr,
332            ),
333        ]))
334        .chain(std::iter::once(("init", noarg_callback_type(), PropertyVisibility::Private)))
335}
336
337/// lookup reserved property injected in every item
338pub fn reserved_property(name: std::borrow::Cow<'_, str>) -> PropertyLookupResult<'_> {
339    thread_local! {
340        static RESERVED_PROPERTIES: HashMap<&'static str, (Type, PropertyVisibility, Option<BuiltinFunction>)>
341            = reserved_properties().map(|(name, ty, visibility)| (name, (ty, visibility, reserved_member_function(name)))).collect();
342    }
343    if let Some((ty, visibility, builtin_function)) =
344        RESERVED_PROPERTIES.with(|reserved| reserved.get(name.as_ref()).cloned())
345    {
346        return PropertyLookupResult {
347            property_type: ty,
348            resolved_name: name,
349            is_local_to_component: false,
350            is_in_direct_base: false,
351            property_visibility: visibility,
352            declared_pure: None,
353            builtin_function,
354        };
355    }
356
357    // Report deprecated known reserved properties (maximum_width, minimum_height, ...)
358    for pre in &["min", "max"] {
359        if let Some(a) = name.strip_prefix(pre) {
360            for suf in &["width", "height"] {
361                if let Some(b) = a.strip_suffix(suf)
362                    && b == "imum-"
363                {
364                    return PropertyLookupResult {
365                        property_type: Type::LogicalLength,
366                        resolved_name: format!("{pre}-{suf}").into(),
367                        is_local_to_component: false,
368                        is_in_direct_base: false,
369                        property_visibility: crate::object_tree::PropertyVisibility::InOut,
370                        declared_pure: None,
371                        builtin_function: None,
372                    };
373                }
374            }
375        }
376    }
377    PropertyLookupResult::invalid(name)
378}
379
380/// These member functions are injected in every time
381pub fn reserved_member_function(name: &str) -> Option<BuiltinFunction> {
382    for (m, e) in [
383        ("focus", BuiltinFunction::SetFocusItem), // match for callable "focus" property
384        ("clear-focus", BuiltinFunction::ClearFocusItem), // match for callable "clear-focus" property
385    ] {
386        if m == name {
387            return Some(e);
388        }
389    }
390    None
391}
392
393/// All types (datatypes, internal elements, properties, ...) are stored in this type
394#[derive(Debug, Default)]
395pub struct TypeRegister {
396    /// The set of property types.
397    types: HashMap<SmolStr, Type>,
398    /// The set of element types
399    elements: HashMap<SmolStr, ElementType>,
400    supported_property_animation_types: HashSet<String>,
401    pub(crate) property_animation_type: ElementType,
402    pub(crate) empty_type: ElementType,
403    /// Map from a context restricted type to the list of contexts (parent type) it is allowed in. This is
404    /// used to construct helpful error messages, such as "Row can only be within a GridLayout element".
405    context_restricted_types: HashMap<SmolStr, HashSet<SmolStr>>,
406    parent_registry: Option<Rc<RefCell<TypeRegister>>>,
407    /// If the lookup function should return types that are marked as internal
408    pub(crate) expose_internal_types: bool,
409}
410
411impl TypeRegister {
412    pub(crate) fn snapshot(&self, snapshotter: &mut typeloader::Snapshotter) -> Self {
413        Self {
414            types: self.types.clone(),
415            elements: self
416                .elements
417                .iter()
418                .map(|(k, v)| (k.clone(), snapshotter.snapshot_element_type(v)))
419                .collect(),
420            supported_property_animation_types: self.supported_property_animation_types.clone(),
421            property_animation_type: snapshotter
422                .snapshot_element_type(&self.property_animation_type),
423            empty_type: snapshotter.snapshot_element_type(&self.empty_type),
424            context_restricted_types: self.context_restricted_types.clone(),
425            parent_registry: self
426                .parent_registry
427                .as_ref()
428                .map(|tr| snapshotter.snapshot_type_register(tr)),
429            expose_internal_types: self.expose_internal_types,
430        }
431    }
432
433    /// Insert a type into the type register with its builtin type name.
434    ///
435    /// Returns false if it replaced an existing type.
436    pub fn insert_type(&mut self, t: Type) -> bool {
437        self.types.insert(t.to_smolstr(), t).is_none()
438    }
439    /// Insert a type into the type register with a specified name.
440    ///
441    /// Returns false if it replaced an existing type.
442    pub fn insert_type_with_name(&mut self, t: Type, name: SmolStr) -> bool {
443        self.types.insert(name, t).is_none()
444    }
445
446    fn builtin_internal() -> Self {
447        let mut register = TypeRegister::default();
448
449        register.insert_type(Type::Float32);
450        register.insert_type(Type::Int32);
451        register.insert_type(Type::String);
452        register.insert_type(Type::PhysicalLength);
453        register.insert_type(Type::LogicalLength);
454        register.insert_type(Type::Color);
455        register.insert_type(Type::ComponentFactory);
456        register.insert_type(Type::Duration);
457        register.insert_type(Type::Image);
458        register.insert_type(Type::Bool);
459        register.insert_type(Type::Model);
460        register.insert_type(Type::Percent);
461        register.insert_type(Type::Easing);
462        register.insert_type(Type::Angle);
463        register.insert_type(Type::Brush);
464        register.insert_type(Type::Rem);
465        register.insert_type(Type::StyledText);
466        register.insert_type(Type::Keys);
467        register.types.insert("Point".into(), logical_point_type().into());
468        register.types.insert("Size".into(), logical_size_type().into());
469
470        BUILTIN.with(|e| e.enums.fill_register(&mut register));
471
472        register.supported_property_animation_types.insert(Type::Float32.to_string());
473        register.supported_property_animation_types.insert(Type::Int32.to_string());
474        register.supported_property_animation_types.insert(Type::Color.to_string());
475        register.supported_property_animation_types.insert(Type::PhysicalLength.to_string());
476        register.supported_property_animation_types.insert(Type::LogicalLength.to_string());
477        register.supported_property_animation_types.insert(Type::Brush.to_string());
478        register.supported_property_animation_types.insert(Type::Angle.to_string());
479
480        macro_rules! register_builtin_structs {
481            ($(
482                $(#[$attr:meta])*
483                struct $Name:ident {
484                    @name = $inner_name:expr,
485                    export {
486                        $( $(#[$pub_attr:meta])* $pub_field:ident : $pub_type:ident, )*
487                    }
488                    private {
489                        $( $(#[$pri_attr:meta])* $pri_field:ident : $pri_type:ty, )*
490                    }
491                }
492            )*) => { $(
493                register.insert_type_with_name(Type::Struct(builtin_structs::$Name()), SmolStr::new(stringify!($Name)));
494            )* };
495        }
496        i_slint_common::for_each_builtin_structs!(register_builtin_structs);
497
498        crate::load_builtins::load_builtins(&mut register);
499
500        for e in register.elements.values() {
501            if let ElementType::Builtin(b) = e {
502                for accepted_child_type_name in b.additional_accepted_child_types.keys() {
503                    register
504                        .context_restricted_types
505                        .entry(accepted_child_type_name.clone())
506                        .or_default()
507                        .insert(b.native_class.class_name.clone());
508                }
509                if b.additional_accept_self {
510                    register
511                        .context_restricted_types
512                        .entry(b.native_class.class_name.clone())
513                        .or_default()
514                        .insert(b.native_class.class_name.clone());
515                }
516            }
517        }
518
519        match &mut register.elements.get_mut("PopupWindow").unwrap() {
520            ElementType::Builtin(b) => {
521                let popup = Rc::get_mut(b).unwrap();
522                popup.properties.insert(
523                    "show".into(),
524                    BuiltinPropertyInfo::from(BuiltinFunction::ShowPopupWindow),
525                );
526
527                popup.properties.insert(
528                    "close".into(),
529                    BuiltinPropertyInfo::from(BuiltinFunction::ClosePopupWindow),
530                );
531
532                popup.properties.get_mut("close-on-click").unwrap().property_visibility =
533                    PropertyVisibility::Constexpr;
534
535                popup.properties.get_mut("close-policy").unwrap().property_visibility =
536                    PropertyVisibility::Constexpr;
537            }
538            _ => unreachable!(),
539        };
540
541        match &mut register.elements.get_mut("Timer").unwrap() {
542            ElementType::Builtin(b) => {
543                let timer = Rc::get_mut(b).unwrap();
544                timer
545                    .properties
546                    .insert("start".into(), BuiltinPropertyInfo::from(BuiltinFunction::StartTimer));
547                timer
548                    .properties
549                    .insert("stop".into(), BuiltinPropertyInfo::from(BuiltinFunction::StopTimer));
550                timer.properties.insert(
551                    "restart".into(),
552                    BuiltinPropertyInfo::from(BuiltinFunction::RestartTimer),
553                );
554            }
555            _ => unreachable!(),
556        }
557
558        let font_metrics_prop = crate::langtype::BuiltinPropertyInfo {
559            ty: font_metrics_type(),
560            property_visibility: PropertyVisibility::Output,
561            default_value: BuiltinPropertyDefault::WithElement(|elem| {
562                crate::expression_tree::Expression::FunctionCall {
563                    function: BuiltinFunction::ItemFontMetrics.into(),
564                    arguments: vec![crate::expression_tree::Expression::ElementReference(
565                        Rc::downgrade(elem),
566                    )],
567                    source_location: None,
568                }
569            }),
570        };
571
572        match &mut register.elements.get_mut("TextInput").unwrap() {
573            ElementType::Builtin(b) => {
574                let text_input = Rc::get_mut(b).unwrap();
575                text_input.properties.insert(
576                    "set-selection-offsets".into(),
577                    BuiltinPropertyInfo::from(BuiltinFunction::SetSelectionOffsets),
578                );
579                text_input.properties.insert("font-metrics".into(), font_metrics_prop.clone());
580            }
581
582            _ => unreachable!(),
583        };
584
585        match &mut register.elements.get_mut("Text").unwrap() {
586            ElementType::Builtin(b) => {
587                let text = Rc::get_mut(b).unwrap();
588                text.properties.insert("font-metrics".into(), font_metrics_prop);
589            }
590
591            _ => unreachable!(),
592        };
593
594        match &mut register.elements.get_mut("Path").unwrap() {
595            ElementType::Builtin(b) => {
596                let path = Rc::get_mut(b).unwrap();
597                path.properties.get_mut("commands").unwrap().property_visibility =
598                    PropertyVisibility::Fake;
599            }
600
601            _ => unreachable!(),
602        };
603
604        match &mut register.elements.get_mut("TabWidget").unwrap() {
605            ElementType::Builtin(b) => {
606                let tabwidget = Rc::get_mut(b).unwrap();
607                tabwidget.properties.get_mut("orientation").unwrap().property_visibility =
608                    PropertyVisibility::Constexpr;
609            }
610            _ => unreachable!(),
611        }
612
613        register
614    }
615
616    #[doc(hidden)]
617    /// All builtins incl. experimental ones! Do not use in production code!
618    pub fn builtin_experimental() -> Rc<RefCell<Self>> {
619        let register = Self::builtin_internal();
620        Rc::new(RefCell::new(register))
621    }
622
623    pub fn builtin() -> Rc<RefCell<Self>> {
624        let mut register = Self::builtin_internal();
625
626        register.elements.remove("ComponentContainer").unwrap();
627        register.types.remove("component-factory").unwrap();
628
629        register.elements.remove("DragArea").unwrap();
630        register.elements.remove("DropArea").unwrap();
631        register.types.remove("DropEvent").unwrap(); // Also removed in xtask/src/slintdocs.rs
632
633        register.elements.remove("FlexboxLayout").unwrap();
634        register.types.remove("FlexboxLayoutDirection").unwrap();
635        register.types.remove("FlexboxLayoutAlignContent").unwrap();
636        register.types.remove("FlexboxLayoutAlignItems").unwrap();
637        register.types.remove("FlexboxLayoutWrap").unwrap();
638        register.types.remove("FlexboxLayoutAlignSelf").unwrap();
639
640        match register.elements.get_mut("Window").unwrap() {
641            ElementType::Builtin(b) => {
642                Rc::get_mut(b)
643                    .expect("Should not be shared at this point")
644                    .properties
645                    .remove("hide")
646                    .unwrap();
647            }
648            _ => unreachable!(),
649        }
650
651        Rc::new(RefCell::new(register))
652    }
653
654    pub fn new(parent: &Rc<RefCell<TypeRegister>>) -> Self {
655        Self {
656            parent_registry: Some(parent.clone()),
657            expose_internal_types: parent.borrow().expose_internal_types,
658            ..Default::default()
659        }
660    }
661
662    pub fn lookup(&self, name: &str) -> Type {
663        self.types
664            .get(name)
665            .cloned()
666            .or_else(|| self.parent_registry.as_ref().map(|r| r.borrow().lookup(name)))
667            .unwrap_or_default()
668    }
669
670    fn lookup_element_as_result(
671        &self,
672        name: &str,
673    ) -> Result<ElementType, HashMap<SmolStr, HashSet<SmolStr>>> {
674        match self.elements.get(name).cloned() {
675            Some(ty) => Ok(ty),
676            None => match &self.parent_registry {
677                Some(r) => r.borrow().lookup_element_as_result(name),
678                None => Err(self.context_restricted_types.clone()),
679            },
680        }
681    }
682
683    pub fn lookup_element(&self, name: &str) -> Result<ElementType, String> {
684        self.lookup_element_as_result(name).map_err(|context_restricted_types| {
685            if let Some(permitted_parent_types) = context_restricted_types.get(name) {
686                if permitted_parent_types.len() == 1 {
687                    format!(
688                        "{} can only be within a {} element",
689                        name,
690                        permitted_parent_types.iter().next().unwrap()
691                    )
692                } else {
693                    let mut elements = permitted_parent_types.iter().cloned().collect::<Vec<_>>();
694                    elements.sort();
695                    format!(
696                        "{} can only be within the following elements: {}",
697                        name,
698                        elements.join(", ")
699                    )
700                }
701            } else if let Some(ty) = self.types.get(name) {
702                format!("'{ty}' cannot be used as an element")
703            } else {
704                format!("Unknown element '{name}'")
705            }
706        })
707    }
708
709    pub fn lookup_builtin_element(&self, name: &str) -> Option<ElementType> {
710        self.parent_registry.as_ref().map_or_else(
711            || self.elements.get(name).cloned(),
712            |p| p.borrow().lookup_builtin_element(name),
713        )
714    }
715
716    pub fn lookup_qualified<Member: AsRef<str>>(&self, qualified: &[Member]) -> Type {
717        if qualified.len() != 1 {
718            return Type::Invalid;
719        }
720        self.lookup(qualified[0].as_ref())
721    }
722
723    /// Add the component with its defined name
724    ///
725    /// Returns false if there was already an element with the same name
726    pub fn add(&mut self, comp: Rc<Component>) -> bool {
727        self.add_with_name(comp.id.clone(), comp)
728    }
729
730    /// Add the component with a specified name
731    ///
732    /// Returns false if there was already an element with the same name
733    pub fn add_with_name(&mut self, name: SmolStr, comp: Rc<Component>) -> bool {
734        self.elements.insert(name, ElementType::Component(comp)).is_none()
735    }
736
737    pub fn add_builtin(&mut self, builtin: Rc<BuiltinElement>) {
738        self.elements.insert(builtin.name.clone(), ElementType::Builtin(builtin));
739    }
740
741    pub fn property_animation_type_for_property(&self, property_type: Type) -> ElementType {
742        if self.supported_property_animation_types.contains(&property_type.to_string()) {
743            self.property_animation_type.clone()
744        } else {
745            self.parent_registry
746                .as_ref()
747                .map(|registry| {
748                    registry.borrow().property_animation_type_for_property(property_type)
749                })
750                .unwrap_or_default()
751        }
752    }
753
754    /// Return a hashmap with all the registered type
755    pub fn all_types(&self) -> HashMap<SmolStr, Type> {
756        let mut all =
757            self.parent_registry.as_ref().map(|r| r.borrow().all_types()).unwrap_or_default();
758        for (k, v) in &self.types {
759            all.insert(k.clone(), v.clone());
760        }
761        all
762    }
763
764    /// Return a hashmap with all the registered element type
765    pub fn all_elements(&self) -> HashMap<SmolStr, ElementType> {
766        let mut all =
767            self.parent_registry.as_ref().map(|r| r.borrow().all_elements()).unwrap_or_default();
768        for (k, v) in &self.elements {
769            all.insert(k.clone(), v.clone());
770        }
771        all
772    }
773
774    pub fn empty_type(&self) -> ElementType {
775        match self.parent_registry.as_ref() {
776            Some(parent) => parent.borrow().empty_type(),
777            None => self.empty_type.clone(),
778        }
779    }
780}
781
782/// Type definitions for each builtin struct
783pub mod builtin_structs {
784    use super::*;
785
786    thread_local! {
787        pub static BUILTIN_STRUCTS: BuiltinStructs = BuiltinStructs::new();
788    }
789
790    #[rustfmt::skip]
791    macro_rules! map_type {
792        ($pub_type:ident, bool) => { Type::Bool };
793        ($pub_type:ident, i32) => { Type::Int32 };
794        ($pub_type:ident, f32) => { Type::Float32 };
795        ($pub_type:ident, SharedString) => { Type::String };
796        ($pub_type:ident, Image) => { Type::Image };
797        ($pub_type:ident, Coord) => { Type::LogicalLength };
798        ($pub_type:ident, Keys) => { Type::Keys };
799        ($pub_type:ident, LogicalPosition) => { Type::Struct(logical_point_type()) };
800        ($pub_type:ident, LogicalSize) => { Type::Struct(logical_size_type()) };
801        // builtin structs
802        ($pub_type:ident, KeyboardModifiers) => {
803            // Note, this references the local variable in the BuiltinStructs constructor
804            Type::Struct($pub_type.clone())
805        };
806        // builtin enums
807        ($pub_type:ident, $_:ident) => {
808            BUILTIN.with(|e| Type::Enumeration(e.enums.$pub_type.clone()))
809        };
810    }
811
812    macro_rules! declare_builtin_structs {
813        ($(
814            $(#[$attr:meta])*
815            struct $Name:ident {
816                @name = $inner_name:expr,
817                export {
818                    $( $(#[$pub_attr:meta])* $pub_field:ident : $pub_type:ident, )*
819                }
820                private {
821                    $( $(#[$pri_attr:meta])* $pri_field:ident : $pri_type:ty, )*
822                }
823            }
824        )*) => {
825            pub struct BuiltinStructs {
826                $(
827                #[allow(non_snake_case)]
828                $Name: Rc<Struct>
829                ),*
830            }
831            impl BuiltinStructs {
832                pub fn new() -> Self {
833                    $(
834                    #[allow(non_snake_case)]
835                    let $Name = Rc::new(Struct{
836                        fields: BTreeMap::from([
837                            $((stringify!($pub_field).replace_smolstr("_", "-"), map_type!($pub_type, $pub_type))),*
838                        ]),
839                        name: $inner_name.into(),
840                    });
841                    )*
842
843                    Self {
844                        $($Name),*
845                    }
846                }
847            }
848
849            impl Default for BuiltinStructs {
850                fn default() -> Self {
851                    Self::new()
852                }
853            }
854
855            $(
856            #[allow(non_snake_case)]
857            pub fn $Name() -> Rc<Struct> {
858                BUILTIN_STRUCTS.with(|types| types.$Name.clone())
859            }
860            )*
861        };
862    }
863    i_slint_common::for_each_builtin_structs!(declare_builtin_structs);
864}
865
866pub fn logical_point_type() -> Rc<Struct> {
867    BUILTIN.with(|types| types.logical_point_type.clone())
868}
869
870pub fn logical_size_type() -> Rc<Struct> {
871    BUILTIN.with(|types| types.logical_size_type.clone())
872}
873
874pub fn font_metrics_type() -> Type {
875    BUILTIN.with(|types| types.font_metrics_type.clone())
876}
877
878/// The [`Type`] for a runtime LayoutInfo structure
879pub fn layout_info_type() -> Rc<Struct> {
880    BUILTIN.with(|types| types.layout_info_type.clone())
881}
882
883/// The [`Type`] for a runtime PathElement structure
884pub fn path_element_type() -> Type {
885    BUILTIN.with(|types| types.path_element_type.clone())
886}
887
888/// The [`Type`] for a runtime LayoutItemInfo structure
889pub fn layout_item_info_type() -> Type {
890    BUILTIN.with(|types| types.layout_item_info_type.clone())
891}
892
893/// The [`Type`] for a runtime FlexboxLayoutItemInfo structure
894pub fn flexbox_layout_item_info_type() -> Type {
895    BUILTIN.with(|types| types.flexbox_layout_item_info_type.clone())
896}