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