i_slint_compiler/
langtype.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 std::borrow::Cow;
5use std::collections::{BTreeMap, HashMap};
6use std::fmt::Display;
7use std::rc::Rc;
8
9use itertools::Itertools;
10
11use smol_str::SmolStr;
12
13use crate::expression_tree::{BuiltinFunction, Expression, Unit};
14use crate::object_tree::{Component, PropertyVisibility};
15use crate::parser::syntax_nodes;
16use crate::typeregister::TypeRegister;
17
18#[derive(Debug, Clone, Default)]
19pub enum Type {
20    /// Correspond to an uninitialized type, or an error
21    #[default]
22    Invalid,
23    /// The type of an expression that return nothing
24    Void,
25    /// The type of a property two way binding whose type was not yet inferred
26    InferredProperty,
27    /// The type of a callback alias whose type was not yet inferred
28    InferredCallback,
29
30    Callback(Rc<Function>),
31    Function(Rc<Function>),
32
33    ComponentFactory,
34
35    // Other property types:
36    Float32,
37    Int32,
38    String,
39    Color,
40    Duration,
41    PhysicalLength,
42    LogicalLength,
43    Rem,
44    Angle,
45    Percent,
46    Image,
47    Bool,
48    /// Fake type that can represent anything that can be converted into a model.
49    Model,
50    PathData, // Either a vector of path elements or a two vectors of events and coordinates
51    Easing,
52    Brush,
53    /// This is usually a model
54    Array(Rc<Type>),
55    Struct(Rc<Struct>),
56    Enumeration(Rc<Enumeration>),
57
58    /// A type made up of the product of several "unit" types.
59    /// The first parameter is the unit, and the second parameter is the power.
60    /// The vector should be sorted by 1) the power, 2) the unit.
61    UnitProduct(Vec<(Unit, i8)>),
62
63    ElementReference,
64
65    /// This is a `SharedArray<f32>`
66    LayoutCache,
67}
68
69impl core::cmp::PartialEq for Type {
70    fn eq(&self, other: &Self) -> bool {
71        match self {
72            Type::Invalid => matches!(other, Type::Invalid),
73            Type::Void => matches!(other, Type::Void),
74            Type::InferredProperty => matches!(other, Type::InferredProperty),
75            Type::InferredCallback => matches!(other, Type::InferredCallback),
76            Type::Callback(lhs) => {
77                matches!(other, Type::Callback(rhs) if lhs == rhs)
78            }
79            Type::Function(lhs) => {
80                matches!(other, Type::Function(rhs) if lhs == rhs)
81            }
82            Type::ComponentFactory => matches!(other, Type::ComponentFactory),
83            Type::Float32 => matches!(other, Type::Float32),
84            Type::Int32 => matches!(other, Type::Int32),
85            Type::String => matches!(other, Type::String),
86            Type::Color => matches!(other, Type::Color),
87            Type::Duration => matches!(other, Type::Duration),
88            Type::Angle => matches!(other, Type::Angle),
89            Type::PhysicalLength => matches!(other, Type::PhysicalLength),
90            Type::LogicalLength => matches!(other, Type::LogicalLength),
91            Type::Rem => matches!(other, Type::Rem),
92            Type::Percent => matches!(other, Type::Percent),
93            Type::Image => matches!(other, Type::Image),
94            Type::Bool => matches!(other, Type::Bool),
95            Type::Model => matches!(other, Type::Model),
96            Type::PathData => matches!(other, Type::PathData),
97            Type::Easing => matches!(other, Type::Easing),
98            Type::Brush => matches!(other, Type::Brush),
99            Type::Array(a) => matches!(other, Type::Array(b) if a == b),
100            Type::Struct(lhs) => {
101                matches!(other, Type::Struct(rhs) if lhs.fields == rhs.fields && lhs.name == rhs.name)
102            }
103            Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
104            Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
105            Type::ElementReference => matches!(other, Type::ElementReference),
106            Type::LayoutCache => matches!(other, Type::LayoutCache),
107        }
108    }
109}
110
111impl Display for Type {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        match self {
114            Type::Invalid => write!(f, "<error>"),
115            Type::Void => write!(f, "void"),
116            Type::InferredProperty => write!(f, "?"),
117            Type::InferredCallback => write!(f, "callback"),
118            Type::Callback(callback) => {
119                write!(f, "callback")?;
120                if !callback.args.is_empty() {
121                    write!(f, "(")?;
122                    for (i, arg) in callback.args.iter().enumerate() {
123                        if i > 0 {
124                            write!(f, ",")?;
125                        }
126                        write!(f, "{arg}")?;
127                    }
128                    write!(f, ")")?
129                }
130                write!(f, "-> {}", callback.return_type)?;
131                Ok(())
132            }
133            Type::ComponentFactory => write!(f, "component-factory"),
134            Type::Function(function) => {
135                write!(f, "function(")?;
136                for (i, arg) in function.args.iter().enumerate() {
137                    if i > 0 {
138                        write!(f, ",")?;
139                    }
140                    write!(f, "{arg}")?;
141                }
142                write!(f, ") -> {}", function.return_type)
143            }
144            Type::Float32 => write!(f, "float"),
145            Type::Int32 => write!(f, "int"),
146            Type::String => write!(f, "string"),
147            Type::Duration => write!(f, "duration"),
148            Type::Angle => write!(f, "angle"),
149            Type::PhysicalLength => write!(f, "physical-length"),
150            Type::LogicalLength => write!(f, "length"),
151            Type::Rem => write!(f, "relative-font-size"),
152            Type::Percent => write!(f, "percent"),
153            Type::Color => write!(f, "color"),
154            Type::Image => write!(f, "image"),
155            Type::Bool => write!(f, "bool"),
156            Type::Model => write!(f, "model"),
157            Type::Array(t) => write!(f, "[{t}]"),
158            Type::Struct(t) => write!(f, "{t}"),
159            Type::PathData => write!(f, "pathdata"),
160            Type::Easing => write!(f, "easing"),
161            Type::Brush => write!(f, "brush"),
162            Type::Enumeration(enumeration) => write!(f, "enum {}", enumeration.name),
163            Type::UnitProduct(vec) => {
164                const POWERS: &[char] = &['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
165                let mut x = vec.iter().map(|(unit, power)| {
166                    if *power == 1 {
167                        return unit.to_string();
168                    }
169                    let mut res = format!("{}{}", unit, if *power < 0 { "⁻" } else { "" });
170                    let value = power.abs().to_string();
171                    for x in value.as_bytes() {
172                        res.push(POWERS[(x - b'0') as usize]);
173                    }
174
175                    res
176                });
177                write!(f, "({})", x.join("×"))
178            }
179            Type::ElementReference => write!(f, "element ref"),
180            Type::LayoutCache => write!(f, "layout cache"),
181        }
182    }
183}
184
185impl From<Rc<Struct>> for Type {
186    fn from(value: Rc<Struct>) -> Self {
187        Self::Struct(value)
188    }
189}
190
191impl Type {
192    /// valid type for properties
193    pub fn is_property_type(&self) -> bool {
194        matches!(
195            self,
196            Self::Float32
197                | Self::Int32
198                | Self::String
199                | Self::Color
200                | Self::ComponentFactory
201                | Self::Duration
202                | Self::Angle
203                | Self::PhysicalLength
204                | Self::LogicalLength
205                | Self::Rem
206                | Self::Percent
207                | Self::Image
208                | Self::Bool
209                | Self::Easing
210                | Self::Enumeration(_)
211                | Self::ElementReference
212                | Self::Struct { .. }
213                | Self::Array(_)
214                | Self::Brush
215                | Self::InferredProperty
216        )
217    }
218
219    pub fn ok_for_public_api(&self) -> bool {
220        !matches!(self, Self::Easing)
221    }
222
223    /// Assume it is an enumeration, panic if it isn't
224    pub fn as_enum(&self) -> &Rc<Enumeration> {
225        match self {
226            Type::Enumeration(e) => e,
227            _ => panic!("should be an enumeration, bug in compiler pass"),
228        }
229    }
230
231    /// Return true if the type can be converted to the other type
232    pub fn can_convert(&self, other: &Self) -> bool {
233        let can_convert_struct = |a: &BTreeMap<SmolStr, Type>, b: &BTreeMap<SmolStr, Type>| {
234            // the struct `b` has property that the struct `a` doesn't
235            let mut has_more_property = false;
236            for (k, v) in b {
237                match a.get(k) {
238                    Some(t) if !t.can_convert(v) => return false,
239                    None => has_more_property = true,
240                    _ => (),
241                }
242            }
243            if has_more_property {
244                // we should reject the conversion if `a` has property that `b` doesn't have
245                if a.keys().any(|k| !b.contains_key(k)) {
246                    return false;
247                }
248            }
249            true
250        };
251        match (self, other) {
252            (a, b) if a == b => true,
253            (_, Type::Invalid)
254            | (_, Type::Void)
255            | (Type::Float32, Type::Int32)
256            | (Type::Float32, Type::String)
257            | (Type::Int32, Type::Float32)
258            | (Type::Int32, Type::String)
259            | (Type::Float32, Type::Model)
260            | (Type::Int32, Type::Model)
261            | (Type::PhysicalLength, Type::LogicalLength)
262            | (Type::LogicalLength, Type::PhysicalLength)
263            | (Type::Rem, Type::LogicalLength)
264            | (Type::Rem, Type::PhysicalLength)
265            | (Type::LogicalLength, Type::Rem)
266            | (Type::PhysicalLength, Type::Rem)
267            | (Type::Percent, Type::Float32)
268            | (Type::Brush, Type::Color)
269            | (Type::Color, Type::Brush) => true,
270            (Type::Array(a), Type::Model) if a.is_property_type() => true,
271            (Type::Struct(a), Type::Struct(b)) => can_convert_struct(&a.fields, &b.fields),
272            (Type::UnitProduct(u), o) => match o.as_unit_product() {
273                Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
274                None => false,
275            },
276            (o, Type::UnitProduct(u)) => match o.as_unit_product() {
277                Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
278                None => false,
279            },
280            _ => false,
281        }
282    }
283
284    /// If this is a number type which should be used with an unit, this returns the default unit
285    /// otherwise, returns None
286    pub fn default_unit(&self) -> Option<Unit> {
287        match self {
288            Type::Duration => Some(Unit::Ms),
289            Type::PhysicalLength => Some(Unit::Phx),
290            Type::LogicalLength => Some(Unit::Px),
291            Type::Rem => Some(Unit::Rem),
292            // Unit::Percent is special that it does not combine with other units like
293            Type::Percent => None,
294            Type::Angle => Some(Unit::Deg),
295            Type::Invalid => None,
296            Type::Void => None,
297            Type::InferredProperty | Type::InferredCallback => None,
298            Type::Callback { .. } => None,
299            Type::ComponentFactory => None,
300            Type::Function { .. } => None,
301            Type::Float32 => None,
302            Type::Int32 => None,
303            Type::String => None,
304            Type::Color => None,
305            Type::Image => None,
306            Type::Bool => None,
307            Type::Model => None,
308            Type::PathData => None,
309            Type::Easing => None,
310            Type::Brush => None,
311            Type::Array(_) => None,
312            Type::Struct { .. } => None,
313            Type::Enumeration(_) => None,
314            Type::UnitProduct(_) => None,
315            Type::ElementReference => None,
316            Type::LayoutCache => None,
317        }
318    }
319
320    /// Return a unit product vector even for single scalar
321    pub fn as_unit_product(&self) -> Option<Vec<(Unit, i8)>> {
322        match self {
323            Type::UnitProduct(u) => Some(u.clone()),
324            Type::Float32 | Type::Int32 => Some(Vec::new()),
325            Type::Percent => Some(Vec::new()),
326            _ => self.default_unit().map(|u| vec![(u, 1)]),
327        }
328    }
329}
330
331#[derive(Debug, Clone)]
332pub enum BuiltinPropertyDefault {
333    None,
334    Expr(Expression),
335    /// When materializing a property of this type, it will be initialized with an Expression that depends on the ElementRc
336    WithElement(fn(&crate::object_tree::ElementRc) -> Expression),
337    /// The property is actually not a property but a builtin function
338    BuiltinFunction(BuiltinFunction),
339}
340
341impl BuiltinPropertyDefault {
342    pub fn expr(&self, elem: &crate::object_tree::ElementRc) -> Option<Expression> {
343        match self {
344            BuiltinPropertyDefault::None => None,
345            BuiltinPropertyDefault::Expr(expression) => Some(expression.clone()),
346            BuiltinPropertyDefault::WithElement(init_expr) => Some(init_expr(elem)),
347            BuiltinPropertyDefault::BuiltinFunction(..) => {
348                unreachable!("can't get an expression for functions")
349            }
350        }
351    }
352}
353
354/// Information about properties in NativeClass
355#[derive(Debug, Clone)]
356pub struct BuiltinPropertyInfo {
357    /// The property type
358    pub ty: Type,
359    /// When != None, this is the initial value that we will have to set if no other binding were specified
360    pub default_value: BuiltinPropertyDefault,
361    pub property_visibility: PropertyVisibility,
362}
363
364impl BuiltinPropertyInfo {
365    pub fn new(ty: Type) -> Self {
366        Self {
367            ty,
368            default_value: BuiltinPropertyDefault::None,
369            property_visibility: PropertyVisibility::InOut,
370        }
371    }
372
373    pub fn is_native_output(&self) -> bool {
374        matches!(self.property_visibility, PropertyVisibility::InOut | PropertyVisibility::Output)
375    }
376}
377
378impl From<BuiltinFunction> for BuiltinPropertyInfo {
379    fn from(function: BuiltinFunction) -> Self {
380        Self {
381            ty: Type::Function(function.ty()),
382            default_value: BuiltinPropertyDefault::BuiltinFunction(function),
383            property_visibility: PropertyVisibility::Public,
384        }
385    }
386}
387
388/// The base of an element
389#[derive(Clone, Debug, derive_more::From)]
390pub enum ElementType {
391    /// The element is based of a component
392    Component(Rc<Component>),
393    /// The element is a builtin element
394    Builtin(Rc<BuiltinElement>),
395    /// The native type was resolved by the resolve_native_class pass.
396    Native(Rc<NativeClass>),
397    /// The base element couldn't be looked up
398    Error,
399    /// This should be the base type of the root element of a global component
400    Global,
401}
402
403impl PartialEq for ElementType {
404    fn eq(&self, other: &Self) -> bool {
405        match (self, other) {
406            (Self::Component(a), Self::Component(b)) => Rc::ptr_eq(a, b),
407            (Self::Builtin(a), Self::Builtin(b)) => Rc::ptr_eq(a, b),
408            (Self::Native(a), Self::Native(b)) => Rc::ptr_eq(a, b),
409            (Self::Error, Self::Error) | (Self::Global, Self::Global) => true,
410            _ => false,
411        }
412    }
413}
414
415impl ElementType {
416    pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
417        match self {
418            Self::Component(c) => c.root_element.borrow().lookup_property(name),
419            Self::Builtin(b) => {
420                let resolved_name =
421                    if let Some(alias_name) = b.native_class.lookup_alias(name.as_ref()) {
422                        Cow::Owned(alias_name.to_string())
423                    } else {
424                        Cow::Borrowed(name)
425                    };
426                match b.properties.get(resolved_name.as_ref()) {
427                    None => {
428                        if b.is_non_item_type {
429                            PropertyLookupResult::invalid(resolved_name)
430                        } else {
431                            crate::typeregister::reserved_property(name)
432                        }
433                    }
434                    Some(p) => PropertyLookupResult {
435                        resolved_name,
436                        property_type: p.ty.clone(),
437                        property_visibility: p.property_visibility,
438                        declared_pure: None,
439                        is_local_to_component: false,
440                        is_in_direct_base: false,
441                        builtin_function: match &p.default_value {
442                            BuiltinPropertyDefault::BuiltinFunction(f) => Some(f.clone()),
443                            _ => None,
444                        },
445                    },
446                }
447            }
448            Self::Native(n) => {
449                let resolved_name = if let Some(alias_name) = n.lookup_alias(name.as_ref()) {
450                    Cow::Owned(alias_name.to_string())
451                } else {
452                    Cow::Borrowed(name)
453                };
454                let property_type =
455                    n.lookup_property(resolved_name.as_ref()).cloned().unwrap_or_default();
456                PropertyLookupResult {
457                    resolved_name,
458                    property_type,
459                    property_visibility: PropertyVisibility::InOut,
460                    declared_pure: None,
461                    is_local_to_component: false,
462                    is_in_direct_base: false,
463                    builtin_function: None,
464                }
465            }
466            _ => PropertyLookupResult::invalid(Cow::Borrowed(name)),
467        }
468    }
469
470    /// List of sub properties valid for the auto completion
471    pub fn property_list(&self) -> Vec<(SmolStr, Type)> {
472        match self {
473            Self::Component(c) => {
474                let mut r = c.root_element.borrow().base_type.property_list();
475                r.extend(
476                    c.root_element
477                        .borrow()
478                        .property_declarations
479                        .iter()
480                        .filter(|(_, d)| d.visibility != PropertyVisibility::Private)
481                        .map(|(k, d)| (k.clone(), d.property_type.clone())),
482                );
483                r
484            }
485            Self::Builtin(b) => {
486                b.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
487            }
488            Self::Native(n) => {
489                n.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
490            }
491            _ => Vec::new(),
492        }
493    }
494
495    /// This function looks at the element and checks whether it can have Elements of type `name` as children.
496    /// In addition to what `accepts_child_element` does, this method also probes the type of `name`.
497    /// It returns an Error if that is not possible or an `ElementType` if it is.
498    pub fn lookup_type_for_child_element(
499        &self,
500        name: &str,
501        tr: &TypeRegister,
502    ) -> Result<ElementType, String> {
503        match self {
504            Self::Component(component) => {
505                let base_type = match &*component.child_insertion_point.borrow() {
506                    Some(insert_in) => insert_in.parent.borrow().base_type.clone(),
507                    None => {
508                        let base_type = component.root_element.borrow().base_type.clone();
509                        if base_type == tr.empty_type() {
510                            return Err(format!("'{}' cannot have children. Only components with @children can have children", component.id));
511                        }
512                        base_type
513                    }
514                };
515                base_type.lookup_type_for_child_element(name, tr)
516            }
517            Self::Builtin(builtin) => {
518                if builtin.disallow_global_types_as_child_elements {
519                    if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
520                        return Ok(child_type.clone().into());
521                    } else if builtin.additional_accept_self && name == builtin.native_class.class_name {
522                        return Ok(builtin.clone().into());
523                    }
524                    let mut valid_children: Vec<_> =
525                        builtin.additional_accepted_child_types.keys().cloned().collect();
526                    if builtin.additional_accept_self {
527                        valid_children.push(builtin.native_class.class_name.clone());
528                    }
529                    valid_children.sort();
530
531                    let err = if valid_children.is_empty() {
532                        format!("{} cannot have children elements", builtin.native_class.class_name,)
533                    } else {
534                        format!(
535                            "{} is not allowed within {}. Only {} are valid children",
536                            name,
537                            builtin.native_class.class_name,
538                            valid_children.join(" ")
539                        )
540                    };
541                    return Err(err);
542                }
543                let err = match tr.lookup_element(name) {
544                    Err(e) => e,
545                    Ok(t) => {
546                        if !tr.expose_internal_types
547                            && matches!(&t, Self::Builtin(e) if e.is_internal)
548                        {
549                            format!("Unknown element '{name}'. (The type exists as an internal type, but cannot be accessed in this scope)")
550                        } else {
551                            return Ok(t);
552                        }
553                    }
554                };
555                if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
556                    return Ok(child_type.clone().into());
557                } else if builtin.additional_accept_self && name == builtin.native_class.class_name {
558                    return Ok(builtin.clone().into());
559                }
560                match tr.lookup(name) {
561                    Type::Invalid => Err(err),
562                    ty => Err(format!("'{ty}' cannot be used as an element")),
563                }
564            }
565            _ => tr.lookup_element(name).and_then(|t| {
566                if !tr.expose_internal_types && matches!(&t, Self::Builtin(e) if e.is_internal) {
567                    Err(format!("Unknown element '{name}'. (The type exists as an internal type, but cannot be accessed in this scope)"))
568                } else {
569                    Ok(t)
570                }
571            })
572        }
573    }
574
575    /// Assume this is a builtin type, panic if it isn't
576    pub fn as_builtin(&self) -> &BuiltinElement {
577        match self {
578            Self::Builtin(b) => b,
579            Self::Component(_) => panic!("This should not happen because of inlining"),
580            _ => panic!("invalid type"),
581        }
582    }
583
584    /// Assume this is a builtin type, panic if it isn't
585    pub fn as_native(&self) -> &NativeClass {
586        match self {
587            Self::Native(b) => b,
588            Self::Component(_) => {
589                panic!("This should not happen because of native class resolution")
590            }
591            _ => panic!("invalid type"),
592        }
593    }
594
595    /// Assume it is a Component, panic if it isn't
596    pub fn as_component(&self) -> &Rc<Component> {
597        match self {
598            Self::Component(c) => c,
599            _ => panic!("should be a component because of the repeater_component pass"),
600        }
601    }
602
603    /// Returns the Slint type name if applicable (for example `Rectangle` or `MyButton` when `component MyButton {}` is used as `MyButton` element)
604    pub fn type_name(&self) -> Option<&str> {
605        match self {
606            ElementType::Component(component) => Some(&component.id),
607            ElementType::Builtin(b) => Some(&b.name),
608            ElementType::Native(_) => None, // Too late, caller should call this function before the native class lowering
609            ElementType::Error => None,
610            ElementType::Global => None,
611        }
612    }
613}
614
615impl Display for ElementType {
616    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
617        match self {
618            Self::Component(c) => c.id.fmt(f),
619            Self::Builtin(b) => b.name.fmt(f),
620            Self::Native(b) => b.class_name.fmt(f),
621            Self::Error => write!(f, "<error>"),
622            Self::Global => Ok(()),
623        }
624    }
625}
626
627impl Default for ElementType {
628    fn default() -> Self {
629        Self::Error
630    }
631}
632
633#[derive(Debug, Clone, Default)]
634pub struct NativeClass {
635    pub parent: Option<Rc<NativeClass>>,
636    pub class_name: SmolStr,
637    pub cpp_vtable_getter: String,
638    pub properties: HashMap<SmolStr, BuiltinPropertyInfo>,
639    pub deprecated_aliases: HashMap<SmolStr, SmolStr>,
640    pub cpp_type: Option<SmolStr>,
641    pub rust_type_constructor: Option<SmolStr>,
642}
643
644impl NativeClass {
645    pub fn new(class_name: &str) -> Self {
646        let cpp_vtable_getter = format!("SLINT_GET_ITEM_VTABLE({class_name}VTable)");
647        Self {
648            class_name: class_name.into(),
649            cpp_vtable_getter,
650            properties: Default::default(),
651            ..Default::default()
652        }
653    }
654
655    pub fn new_with_properties(
656        class_name: &str,
657        properties: impl IntoIterator<Item = (SmolStr, BuiltinPropertyInfo)>,
658    ) -> Self {
659        let mut class = Self::new(class_name);
660        class.properties = properties.into_iter().collect();
661        class
662    }
663
664    pub fn property_count(&self) -> usize {
665        self.properties.len() + self.parent.clone().map(|p| p.property_count()).unwrap_or_default()
666    }
667
668    pub fn lookup_property(&self, name: &str) -> Option<&Type> {
669        if let Some(bty) = self.properties.get(name) {
670            Some(&bty.ty)
671        } else if let Some(parent_class) = &self.parent {
672            parent_class.lookup_property(name)
673        } else {
674            None
675        }
676    }
677
678    pub fn lookup_alias(&self, name: &str) -> Option<&str> {
679        if let Some(alias_target) = self.deprecated_aliases.get(name) {
680            Some(alias_target)
681        } else if self.properties.contains_key(name) {
682            None
683        } else if let Some(parent_class) = &self.parent {
684            parent_class.lookup_alias(name)
685        } else {
686            None
687        }
688    }
689}
690
691#[derive(Debug, Clone, Copy, PartialEq, Default)]
692pub enum DefaultSizeBinding {
693    /// There should not be a default binding for the size
694    #[default]
695    None,
696    /// The size should default to `width:100%; height:100%`
697    ExpandsToParentGeometry,
698    /// The size should default to the item's implicit size
699    ImplicitSize,
700}
701
702#[derive(Debug, Clone, Default)]
703pub struct BuiltinElement {
704    pub name: SmolStr,
705    pub native_class: Rc<NativeClass>,
706    pub properties: BTreeMap<SmolStr, BuiltinPropertyInfo>,
707    /// Additional builtin element that can be accepted as child of this element
708    /// (example `Tab` in `TabWidget`, `Row` in `GridLayout` and the path elements in `Path`)
709    pub additional_accepted_child_types: HashMap<SmolStr, Rc<BuiltinElement>>,
710    /// `Self` is conceptually in `additional_accepted_child_types` (which it can't otherwise that'd make a Rc loop)
711    pub additional_accept_self: bool,
712    pub disallow_global_types_as_child_elements: bool,
713    /// Non-item type do not have reserved properties (x/width/rowspan/...) added to them  (eg: PropertyAnimation)
714    pub is_non_item_type: bool,
715    pub accepts_focus: bool,
716    pub is_global: bool,
717    pub default_size_binding: DefaultSizeBinding,
718    /// When true this is an internal type not shown in the auto-completion
719    pub is_internal: bool,
720}
721
722impl BuiltinElement {
723    pub fn new(native_class: Rc<NativeClass>) -> Self {
724        Self { name: native_class.class_name.clone(), native_class, ..Default::default() }
725    }
726}
727
728#[derive(PartialEq, Debug)]
729pub struct PropertyLookupResult<'a> {
730    pub resolved_name: std::borrow::Cow<'a, str>,
731    pub property_type: Type,
732    pub property_visibility: PropertyVisibility,
733    pub declared_pure: Option<bool>,
734    /// True if the property is part of the current component (for visibility purposes)
735    pub is_local_to_component: bool,
736    /// True if the property in the direct base of the component (for protected visibility purposes)
737    pub is_in_direct_base: bool,
738
739    /// If the property is a builtin function
740    pub builtin_function: Option<BuiltinFunction>,
741}
742
743impl<'a> PropertyLookupResult<'a> {
744    pub fn is_valid(&self) -> bool {
745        self.property_type != Type::Invalid
746    }
747
748    /// Can this property be used in an assignment
749    pub fn is_valid_for_assignment(&self) -> bool {
750        !matches!(
751            (self.property_visibility, self.is_local_to_component),
752            (PropertyVisibility::Private, false)
753                | (PropertyVisibility::Input, true)
754                | (PropertyVisibility::Output, false)
755        )
756    }
757
758    pub fn invalid(resolved_name: Cow<'a, str>) -> Self {
759        Self {
760            resolved_name,
761            property_type: Type::Invalid,
762            property_visibility: PropertyVisibility::Private,
763            declared_pure: None,
764            is_local_to_component: false,
765            is_in_direct_base: false,
766            builtin_function: None,
767        }
768    }
769}
770
771#[derive(Debug, Clone, PartialEq)]
772pub struct Function {
773    pub return_type: Type,
774    pub args: Vec<Type>,
775    /// The optional names of the arguments (empty string means not set).
776    /// The names are not technically part of the type, but it is good to have them available for auto-completion
777    pub arg_names: Vec<SmolStr>,
778}
779
780#[derive(Debug, Clone)]
781pub struct Struct {
782    pub fields: BTreeMap<SmolStr, Type>,
783    /// When declared in .slint as  `struct Foo := { }`, then the name is "Foo"
784    /// When there is no node, but there is a name, then it is a builtin type
785    pub name: Option<SmolStr>,
786    /// When declared in .slint, this is the node of the declaration.
787    pub node: Option<syntax_nodes::ObjectType>,
788    /// derived
789    pub rust_attributes: Option<Vec<SmolStr>>,
790}
791
792impl Display for Struct {
793    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
794        if let Some(name) = &self.name {
795            if let Some(separator_pos) = name.rfind("::") {
796                // write the slint type and not the native type
797                write!(f, "{}", &name[separator_pos + 2..])
798            } else {
799                write!(f, "{name}")
800            }
801        } else {
802            write!(f, "{{ ")?;
803            for (k, v) in &self.fields {
804                write!(f, "{k}: {v},")?;
805            }
806            write!(f, "}}")
807        }
808    }
809}
810
811#[derive(Debug, Clone)]
812pub struct Enumeration {
813    pub name: SmolStr,
814    pub values: Vec<SmolStr>,
815    pub default_value: usize, // index in values
816    // For non-builtins enums, this is the declaration node
817    pub node: Option<syntax_nodes::EnumDeclaration>,
818}
819
820impl PartialEq for Enumeration {
821    fn eq(&self, other: &Self) -> bool {
822        self.name.eq(&other.name)
823    }
824}
825
826impl Enumeration {
827    pub fn default_value(self: Rc<Self>) -> EnumerationValue {
828        EnumerationValue { value: self.default_value, enumeration: self.clone() }
829    }
830
831    pub fn try_value_from_string(self: Rc<Self>, value: &str) -> Option<EnumerationValue> {
832        self.values.iter().enumerate().find_map(|(idx, name)| {
833            if name == value {
834                Some(EnumerationValue { value: idx, enumeration: self.clone() })
835            } else {
836                None
837            }
838        })
839    }
840}
841
842#[derive(Clone, Debug)]
843pub struct EnumerationValue {
844    pub value: usize, // index in enumeration.values
845    pub enumeration: Rc<Enumeration>,
846}
847
848impl PartialEq for EnumerationValue {
849    fn eq(&self, other: &Self) -> bool {
850        Rc::ptr_eq(&self.enumeration, &other.enumeration) && self.value == other.value
851    }
852}
853
854impl std::fmt::Display for EnumerationValue {
855    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
856        self.enumeration.values[self.value].fmt(f)
857    }
858}
859
860impl EnumerationValue {
861    pub fn to_pascal_case(&self) -> String {
862        crate::generator::to_pascal_case(&self.enumeration.values[self.value])
863    }
864}
865
866#[derive(Debug, PartialEq)]
867pub struct LengthConversionPowers {
868    pub rem_to_px_power: i8,
869    pub px_to_phx_power: i8,
870}
871
872/// If the `Type::UnitProduct(a)` can be converted to `Type::UnitProduct(b)` by multiplying
873/// by the scale factor, return that scale factor, otherwise, return None
874pub fn unit_product_length_conversion(
875    a: &[(Unit, i8)],
876    b: &[(Unit, i8)],
877) -> Option<LengthConversionPowers> {
878    // e.g. float to int conversion, no units
879    if a.is_empty() && b.is_empty() {
880        return Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: 0 });
881    }
882
883    let mut units = [0i8; 16];
884    for (u, count) in a {
885        units[*u as usize] += count;
886    }
887    for (u, count) in b {
888        units[*u as usize] -= count;
889    }
890
891    if units[Unit::Px as usize] + units[Unit::Phx as usize] + units[Unit::Rem as usize] != 0 {
892        return None;
893    }
894
895    if units[Unit::Rem as usize] != 0
896        && units[Unit::Phx as usize] == -units[Unit::Rem as usize]
897        && units[Unit::Px as usize] == 0
898    {
899        units[Unit::Px as usize] = -units[Unit::Rem as usize];
900        units[Unit::Phx as usize] = -units[Unit::Rem as usize];
901    }
902
903    let result = LengthConversionPowers {
904        rem_to_px_power: if units[Unit::Rem as usize] != 0 { units[Unit::Px as usize] } else { 0 },
905        px_to_phx_power: if units[Unit::Px as usize] != 0 { units[Unit::Phx as usize] } else { 0 },
906    };
907
908    units[Unit::Px as usize] = 0;
909    units[Unit::Phx as usize] = 0;
910    units[Unit::Rem as usize] = 0;
911    units.into_iter().all(|x| x == 0).then_some(result)
912}
913
914#[test]
915fn unit_product_length_conversion_test() {
916    use Option::None;
917    use Unit::*;
918    assert_eq!(
919        unit_product_length_conversion(&[], &[]),
920        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: 0 })
921    );
922    assert_eq!(
923        unit_product_length_conversion(&[(Px, 1)], &[(Phx, 1)]),
924        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -1 })
925    );
926    assert_eq!(
927        unit_product_length_conversion(&[(Phx, -2)], &[(Px, -2)]),
928        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -2 })
929    );
930    assert_eq!(
931        unit_product_length_conversion(&[(Px, 1), (Phx, -2)], &[(Phx, -1)]),
932        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -1 })
933    );
934    assert_eq!(
935        unit_product_length_conversion(
936            &[(Deg, 3), (Phx, 2), (Ms, -1)],
937            &[(Phx, 4), (Deg, 3), (Ms, -1), (Px, -2)]
938        ),
939        Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -2 })
940    );
941    assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
942    assert_eq!(unit_product_length_conversion(&[(Deg, 1), (Phx, -2)], &[(Px, -2)]), None);
943    assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
944
945    assert_eq!(
946        unit_product_length_conversion(&[(Rem, 1)], &[(Px, 1)]),
947        Some(LengthConversionPowers { rem_to_px_power: -1, px_to_phx_power: 0 })
948    );
949    assert_eq!(
950        unit_product_length_conversion(&[(Rem, 1)], &[(Phx, 1)]),
951        Some(LengthConversionPowers { rem_to_px_power: -1, px_to_phx_power: -1 })
952    );
953    assert_eq!(
954        unit_product_length_conversion(&[(Rem, 2)], &[(Phx, 2)]),
955        Some(LengthConversionPowers { rem_to_px_power: -2, px_to_phx_power: -2 })
956    );
957}