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