1use 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 #[default]
22 Invalid,
23 Void,
25 InferredProperty,
27 InferredCallback,
29
30 Callback(Rc<Function>),
31 Function(Rc<Function>),
32
33 ComponentFactory,
34
35 Float32,
37 Int32,
38 String,
39 Color,
40 Duration,
41 PhysicalLength,
42 LogicalLength,
43 Rem,
44 Angle,
45 Percent,
46 Image,
47 Bool,
48 Model,
50 PathData, Easing,
52 Brush,
53 Array(Rc<Type>),
55 Struct(Rc<Struct>),
56 Enumeration(Rc<Enumeration>),
57
58 UnitProduct(Vec<(Unit, i8)>),
62
63 ElementReference,
64
65 LayoutCache,
67 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 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 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 pub fn can_convert(&self, other: &Self) -> bool {
242 let can_convert_struct = |a: &BTreeMap<SmolStr, Type>, b: &BTreeMap<SmolStr, Type>| {
243 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 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 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 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 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 WithElement(fn(&crate::object_tree::ElementRc) -> Expression),
348 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#[derive(Debug, Clone)]
367pub struct BuiltinPropertyInfo {
368 pub ty: Type,
370 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#[derive(Clone, Debug, derive_more::From, Default)]
401pub enum ElementType {
402 Component(Rc<Component>),
404 Builtin(Rc<BuiltinElement>),
406 Native(Rc<NativeClass>),
408 #[default]
410 Error,
411 Global,
413 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 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 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 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 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 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 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, 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 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 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 #[default]
796 None,
797 ExpandsToParentGeometry,
799 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 pub additional_accepted_child_types: HashMap<SmolStr, Rc<BuiltinElement>>,
811 pub additional_accept_self: bool,
813 pub disallow_global_types_as_child_elements: bool,
814 pub is_non_item_type: bool,
816 pub accepts_focus: bool,
817 pub is_global: bool,
818 pub default_size_binding: DefaultSizeBinding,
819 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 pub is_local_to_component: bool,
837 pub is_in_direct_base: bool,
839
840 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 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 pub arg_names: Vec<SmolStr>,
879}
880
881#[derive(Debug, Clone)]
882pub enum StructName {
883 None,
884 User {
886 name: SmolStr,
887 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, 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, 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
1036pub fn unit_product_length_conversion(
1039 a: &[(Unit, i8)],
1040 b: &[(Unit, i8)],
1041) -> Option<LengthConversionPowers> {
1042 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}