Skip to main content

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