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 | |
4 | // cSpell: ignore imum |
5 | |
6 | use smol_str::{format_smolstr, SmolStr, StrExt, ToSmolStr}; |
7 | use std::cell::RefCell; |
8 | use std::collections::{BTreeMap, HashMap, HashSet}; |
9 | use std::rc::Rc; |
10 | |
11 | use crate::expression_tree::BuiltinFunction; |
12 | use crate::langtype::{ |
13 | BuiltinElement, BuiltinPropertyDefault, BuiltinPropertyInfo, ElementType, Enumeration, |
14 | Function, PropertyLookupResult, Struct, Type, |
15 | }; |
16 | use crate::object_tree::{Component, PropertyVisibility}; |
17 | use crate::typeloader; |
18 | |
19 | pub const RESERVED_GEOMETRY_PROPERTIES: &[(&str, Type)] = &[ |
20 | ("x" , Type::LogicalLength), |
21 | ("y" , Type::LogicalLength), |
22 | ("width" , Type::LogicalLength), |
23 | ("height" , Type::LogicalLength), |
24 | ("z" , Type::Float32), |
25 | ]; |
26 | |
27 | pub const RESERVED_LAYOUT_PROPERTIES: &[(&str, Type)] = &[ |
28 | ("min-width" , Type::LogicalLength), |
29 | ("min-height" , Type::LogicalLength), |
30 | ("max-width" , Type::LogicalLength), |
31 | ("max-height" , Type::LogicalLength), |
32 | ("padding" , Type::LogicalLength), |
33 | ("padding-left" , Type::LogicalLength), |
34 | ("padding-right" , Type::LogicalLength), |
35 | ("padding-top" , Type::LogicalLength), |
36 | ("padding-bottom" , Type::LogicalLength), |
37 | ("preferred-width" , Type::LogicalLength), |
38 | ("preferred-height" , Type::LogicalLength), |
39 | ("horizontal-stretch" , Type::Float32), |
40 | ("vertical-stretch" , Type::Float32), |
41 | ]; |
42 | |
43 | pub const RESERVED_GRIDLAYOUT_PROPERTIES: &[(&str, Type)] = &[ |
44 | ("col" , Type::Int32), |
45 | ("row" , Type::Int32), |
46 | ("colspan" , Type::Int32), |
47 | ("rowspan" , Type::Int32), |
48 | ]; |
49 | |
50 | macro_rules! declare_enums { |
51 | ($( $(#[$enum_doc:meta])* enum $Name:ident { $( $(#[$value_doc:meta])* $Value:ident,)* })*) => { |
52 | #[allow(non_snake_case)] |
53 | pub struct BuiltinEnums { |
54 | $(pub $Name : Rc<Enumeration>),* |
55 | } |
56 | impl BuiltinEnums { |
57 | fn new() -> Self { |
58 | Self { |
59 | $($Name : Rc::new(Enumeration { |
60 | name: stringify!($Name).replace_smolstr("_" , "-" ), |
61 | values: vec![$(crate::generator::to_kebab_case(stringify!($Value).trim_start_matches("r#" )).into()),*], |
62 | default_value: 0, |
63 | node: None, |
64 | })),* |
65 | } |
66 | } |
67 | fn fill_register(&self, register: &mut TypeRegister) { |
68 | $(if stringify!($Name) != "PathEvent" { |
69 | register.insert_type_with_name( |
70 | Type::Enumeration(self.$Name.clone()), |
71 | stringify!($Name).replace_smolstr("_" , "-" ) |
72 | ); |
73 | })* |
74 | } |
75 | } |
76 | }; |
77 | } |
78 | |
79 | i_slint_common::for_each_enums!(declare_enums); |
80 | |
81 | pub struct BuiltinTypes { |
82 | pub enums: BuiltinEnums, |
83 | pub noarg_callback_type: Type, |
84 | pub strarg_callback_type: Type, |
85 | pub logical_point_type: Type, |
86 | pub font_metrics_type: Type, |
87 | pub layout_info_type: Rc<Struct>, |
88 | pub path_element_type: Type, |
89 | pub box_layout_cell_data_type: Type, |
90 | } |
91 | |
92 | impl BuiltinTypes { |
93 | fn new() -> Self { |
94 | let layout_info_type = Rc::new(Struct { |
95 | fields: ["min" , "max" , "preferred" ] |
96 | .iter() |
97 | .map(|s| (SmolStr::new_static(s), Type::LogicalLength)) |
98 | .chain( |
99 | ["min_percent" , "max_percent" , "stretch" ] |
100 | .iter() |
101 | .map(|s| (SmolStr::new_static(s), Type::Float32)), |
102 | ) |
103 | .collect(), |
104 | name: Some("slint::private_api::LayoutInfo" .into()), |
105 | node: None, |
106 | rust_attributes: None, |
107 | }); |
108 | Self { |
109 | enums: BuiltinEnums::new(), |
110 | logical_point_type: Type::Struct(Rc::new(Struct { |
111 | fields: IntoIterator::into_iter([ |
112 | (SmolStr::new_static("x" ), Type::LogicalLength), |
113 | (SmolStr::new_static("y" ), Type::LogicalLength), |
114 | ]) |
115 | .collect(), |
116 | name: Some("slint::LogicalPosition" .into()), |
117 | node: None, |
118 | rust_attributes: None, |
119 | })), |
120 | font_metrics_type: Type::Struct(Rc::new(Struct { |
121 | fields: IntoIterator::into_iter([ |
122 | (SmolStr::new_static("ascent" ), Type::LogicalLength), |
123 | (SmolStr::new_static("descent" ), Type::LogicalLength), |
124 | (SmolStr::new_static("x-height" ), Type::LogicalLength), |
125 | (SmolStr::new_static("cap-height" ), Type::LogicalLength), |
126 | ]) |
127 | .collect(), |
128 | name: Some("slint::private_api::FontMetrics" .into()), |
129 | node: None, |
130 | rust_attributes: None, |
131 | })), |
132 | noarg_callback_type: Type::Callback(Rc::new(Function { |
133 | return_type: Type::Void, |
134 | args: vec![], |
135 | arg_names: vec![], |
136 | })), |
137 | strarg_callback_type: Type::Callback(Rc::new(Function { |
138 | return_type: Type::Void, |
139 | args: vec![Type::String], |
140 | arg_names: vec![], |
141 | })), |
142 | layout_info_type: layout_info_type.clone(), |
143 | path_element_type: Type::Struct(Rc::new(Struct { |
144 | fields: Default::default(), |
145 | name: Some("PathElement" .into()), |
146 | node: None, |
147 | rust_attributes: None, |
148 | })), |
149 | box_layout_cell_data_type: Type::Struct(Rc::new(Struct { |
150 | fields: IntoIterator::into_iter([("constraint" .into(), layout_info_type.into())]) |
151 | .collect(), |
152 | name: Some("BoxLayoutCellData" .into()), |
153 | node: None, |
154 | rust_attributes: None, |
155 | })), |
156 | } |
157 | } |
158 | } |
159 | |
160 | thread_local! { |
161 | pub static BUILTIN: BuiltinTypes = BuiltinTypes::new(); |
162 | } |
163 | |
164 | const RESERVED_OTHER_PROPERTIES: &[(&str, Type)] = &[ |
165 | ("clip" , Type::Bool), |
166 | ("opacity" , Type::Float32), |
167 | ("cache-rendering-hint" , Type::Bool), |
168 | ("visible" , Type::Bool), // ("enabled", Type::Bool), |
169 | ]; |
170 | |
171 | pub const RESERVED_DROP_SHADOW_PROPERTIES: &[(&str, Type)] = &[ |
172 | ("drop-shadow-offset-x" , Type::LogicalLength), |
173 | ("drop-shadow-offset-y" , Type::LogicalLength), |
174 | ("drop-shadow-blur" , Type::LogicalLength), |
175 | ("drop-shadow-color" , Type::Color), |
176 | ]; |
177 | |
178 | pub const RESERVED_ROTATION_PROPERTIES: &[(&str, Type)] = &[ |
179 | ("rotation-angle" , Type::Angle), |
180 | ("rotation-origin-x" , Type::LogicalLength), |
181 | ("rotation-origin-y" , Type::LogicalLength), |
182 | ]; |
183 | |
184 | pub fn noarg_callback_type() -> Type { |
185 | BUILTIN.with(|types: &BuiltinTypes| types.noarg_callback_type.clone()) |
186 | } |
187 | |
188 | fn strarg_callback_type() -> Type { |
189 | BUILTIN.with(|types: &BuiltinTypes| types.strarg_callback_type.clone()) |
190 | } |
191 | |
192 | pub fn reserved_accessibility_properties() -> impl Iterator<Item = (&'static str, Type)> { |
193 | [ |
194 | //("accessible-role", ...) |
195 | ("accessible-checkable" , Type::Bool), |
196 | ("accessible-checked" , Type::Bool), |
197 | ("accessible-delegate-focus" , Type::Int32), |
198 | ("accessible-description" , Type::String), |
199 | ("accessible-enabled" , Type::Bool), |
200 | ("accessible-expandable" , Type::Bool), |
201 | ("accessible-expanded" , Type::Bool), |
202 | ("accessible-label" , Type::String), |
203 | ("accessible-value" , Type::String), |
204 | ("accessible-value-maximum" , Type::Float32), |
205 | ("accessible-value-minimum" , Type::Float32), |
206 | ("accessible-value-step" , Type::Float32), |
207 | ("accessible-placeholder-text" , Type::String), |
208 | ("accessible-action-default" , noarg_callback_type()), |
209 | ("accessible-action-increment" , noarg_callback_type()), |
210 | ("accessible-action-decrement" , noarg_callback_type()), |
211 | ("accessible-action-set-value" , strarg_callback_type()), |
212 | ("accessible-action-expand" , noarg_callback_type()), |
213 | ("accessible-item-selectable" , Type::Bool), |
214 | ("accessible-item-selected" , Type::Bool), |
215 | ("accessible-item-index" , Type::Int32), |
216 | ("accessible-item-count" , Type::Int32), |
217 | ("accessible-read-only" , Type::Bool), |
218 | ] |
219 | .into_iter() |
220 | } |
221 | |
222 | /// list of reserved property injected in every item |
223 | pub fn reserved_properties() -> impl Iterator<Item = (&'static str, Type, PropertyVisibility)> { |
224 | RESERVED_GEOMETRY_PROPERTIES |
225 | .iter() |
226 | .chain(RESERVED_LAYOUT_PROPERTIES.iter()) |
227 | .chain(RESERVED_OTHER_PROPERTIES.iter()) |
228 | .chain(RESERVED_DROP_SHADOW_PROPERTIES.iter()) |
229 | .chain(RESERVED_ROTATION_PROPERTIES.iter()) |
230 | .map(|(k, v)| (*k, v.clone(), PropertyVisibility::Input)) |
231 | .chain(reserved_accessibility_properties().map(|(k, v)| (k, v, PropertyVisibility::Input))) |
232 | .chain( |
233 | RESERVED_GRIDLAYOUT_PROPERTIES |
234 | .iter() |
235 | .map(|(k, v)| (*k, v.clone(), PropertyVisibility::Constexpr)), |
236 | ) |
237 | .chain(IntoIterator::into_iter([ |
238 | ("absolute-position" , logical_point_type(), PropertyVisibility::Output), |
239 | ("forward-focus" , Type::ElementReference, PropertyVisibility::Constexpr), |
240 | ( |
241 | "focus" , |
242 | Type::Function(BuiltinFunction::SetFocusItem.ty()), |
243 | PropertyVisibility::Public, |
244 | ), |
245 | ( |
246 | "clear-focus" , |
247 | Type::Function(BuiltinFunction::ClearFocusItem.ty()), |
248 | PropertyVisibility::Public, |
249 | ), |
250 | ( |
251 | "dialog-button-role" , |
252 | Type::Enumeration(BUILTIN.with(|e| e.enums.DialogButtonRole.clone())), |
253 | PropertyVisibility::Constexpr, |
254 | ), |
255 | ( |
256 | "accessible-role" , |
257 | Type::Enumeration(BUILTIN.with(|e| e.enums.AccessibleRole.clone())), |
258 | PropertyVisibility::Constexpr, |
259 | ), |
260 | ])) |
261 | .chain(std::iter::once(("init" , noarg_callback_type(), PropertyVisibility::Private))) |
262 | } |
263 | |
264 | /// lookup reserved property injected in every item |
265 | pub fn reserved_property(name: &str) -> PropertyLookupResult { |
266 | thread_local! { |
267 | static RESERVED_PROPERTIES: HashMap<&'static str, (Type, PropertyVisibility, Option<BuiltinFunction>)> |
268 | = reserved_properties().map(|(name, ty, visibility)| (name, (ty, visibility, reserved_member_function(name)))).collect(); |
269 | } |
270 | if let Some(result) = RESERVED_PROPERTIES.with(|reserved| { |
271 | reserved.get(name).map(|(ty, visibility, builtin_function)| PropertyLookupResult { |
272 | property_type: ty.clone(), |
273 | resolved_name: name.into(), |
274 | is_local_to_component: false, |
275 | is_in_direct_base: false, |
276 | property_visibility: *visibility, |
277 | declared_pure: None, |
278 | builtin_function: builtin_function.clone(), |
279 | }) |
280 | }) { |
281 | return result; |
282 | } |
283 | |
284 | // Report deprecated known reserved properties (maximum_width, minimum_height, ...) |
285 | for pre in &["min" , "max" ] { |
286 | if let Some(a) = name.strip_prefix(pre) { |
287 | for suf in &["width" , "height" ] { |
288 | if let Some(b) = a.strip_suffix(suf) { |
289 | if b == "imum-" { |
290 | return PropertyLookupResult { |
291 | property_type: Type::LogicalLength, |
292 | resolved_name: format!(" {pre}- {suf}" ).into(), |
293 | is_local_to_component: false, |
294 | is_in_direct_base: false, |
295 | property_visibility: crate::object_tree::PropertyVisibility::InOut, |
296 | declared_pure: None, |
297 | builtin_function: None, |
298 | }; |
299 | } |
300 | } |
301 | } |
302 | } |
303 | } |
304 | PropertyLookupResult::invalid(name.into()) |
305 | } |
306 | |
307 | /// These member functions are injected in every time |
308 | pub fn reserved_member_function(name: &str) -> Option<BuiltinFunction> { |
309 | for (m: &'static str, e: BuiltinFunction) in [ |
310 | ("focus" , BuiltinFunction::SetFocusItem), // match for callable "focus" property |
311 | ("clear-focus" , BuiltinFunction::ClearFocusItem), // match for callable "clear-focus" property |
312 | ] { |
313 | if m == name { |
314 | return Some(e); |
315 | } |
316 | } |
317 | None |
318 | } |
319 | |
320 | #[derive (Debug, Default)] |
321 | pub struct TypeRegister { |
322 | /// The set of property types. |
323 | types: HashMap<SmolStr, Type>, |
324 | /// The set of element types |
325 | elements: HashMap<SmolStr, ElementType>, |
326 | supported_property_animation_types: HashSet<String>, |
327 | pub(crate) property_animation_type: ElementType, |
328 | pub(crate) empty_type: ElementType, |
329 | /// Map from a context restricted type to the list of contexts (parent type) it is allowed in. This is |
330 | /// used to construct helpful error messages, such as "Row can only be within a GridLayout element". |
331 | context_restricted_types: HashMap<SmolStr, HashSet<SmolStr>>, |
332 | parent_registry: Option<Rc<RefCell<TypeRegister>>>, |
333 | /// If the lookup function should return types that are marked as internal |
334 | pub(crate) expose_internal_types: bool, |
335 | } |
336 | |
337 | impl TypeRegister { |
338 | pub(crate) fn snapshot(&self, snapshotter: &mut typeloader::Snapshotter) -> Self { |
339 | Self { |
340 | types: self.types.clone(), |
341 | elements: self |
342 | .elements |
343 | .iter() |
344 | .map(|(k, v)| (k.clone(), snapshotter.snapshot_element_type(v))) |
345 | .collect(), |
346 | supported_property_animation_types: self.supported_property_animation_types.clone(), |
347 | property_animation_type: snapshotter |
348 | .snapshot_element_type(&self.property_animation_type), |
349 | empty_type: snapshotter.snapshot_element_type(&self.empty_type), |
350 | context_restricted_types: self.context_restricted_types.clone(), |
351 | parent_registry: self |
352 | .parent_registry |
353 | .as_ref() |
354 | .map(|tr| snapshotter.snapshot_type_register(tr)), |
355 | expose_internal_types: self.expose_internal_types, |
356 | } |
357 | } |
358 | |
359 | /// Insert a type into the type register with its builtin type name. |
360 | /// |
361 | /// Returns false if a it replaced an existing type. |
362 | pub fn insert_type(&mut self, t: Type) -> bool { |
363 | self.types.insert(t.to_smolstr(), t).is_none() |
364 | } |
365 | /// Insert a type into the type register with a specified name. |
366 | /// |
367 | /// Returns false if a it replaced an existing type. |
368 | pub fn insert_type_with_name(&mut self, t: Type, name: SmolStr) -> bool { |
369 | self.types.insert(name, t).is_none() |
370 | } |
371 | |
372 | fn builtin_internal() -> Self { |
373 | let mut register = TypeRegister::default(); |
374 | |
375 | register.insert_type(Type::Float32); |
376 | register.insert_type(Type::Int32); |
377 | register.insert_type(Type::String); |
378 | register.insert_type(Type::PhysicalLength); |
379 | register.insert_type(Type::LogicalLength); |
380 | register.insert_type(Type::Color); |
381 | register.insert_type(Type::ComponentFactory); |
382 | register.insert_type(Type::Duration); |
383 | register.insert_type(Type::Image); |
384 | register.insert_type(Type::Bool); |
385 | register.insert_type(Type::Model); |
386 | register.insert_type(Type::Percent); |
387 | register.insert_type(Type::Easing); |
388 | register.insert_type(Type::Angle); |
389 | register.insert_type(Type::Brush); |
390 | register.insert_type(Type::Rem); |
391 | register.types.insert("Point" .into(), logical_point_type()); |
392 | |
393 | BUILTIN.with(|e| e.enums.fill_register(&mut register)); |
394 | |
395 | register.supported_property_animation_types.insert(Type::Float32.to_string()); |
396 | register.supported_property_animation_types.insert(Type::Int32.to_string()); |
397 | register.supported_property_animation_types.insert(Type::Color.to_string()); |
398 | register.supported_property_animation_types.insert(Type::PhysicalLength.to_string()); |
399 | register.supported_property_animation_types.insert(Type::LogicalLength.to_string()); |
400 | register.supported_property_animation_types.insert(Type::Brush.to_string()); |
401 | register.supported_property_animation_types.insert(Type::Angle.to_string()); |
402 | |
403 | #[rustfmt::skip] |
404 | macro_rules! map_type { |
405 | ($pub_type:ident, bool) => { Type::Bool }; |
406 | ($pub_type:ident, i32) => { Type::Int32 }; |
407 | ($pub_type:ident, f32) => { Type::Float32 }; |
408 | ($pub_type:ident, SharedString) => { Type::String }; |
409 | ($pub_type:ident, Image) => { Type::Image }; |
410 | ($pub_type:ident, Coord) => { Type::LogicalLength }; |
411 | ($pub_type:ident, KeyboardModifiers) => { $pub_type.clone() }; |
412 | ($pub_type:ident, $_:ident) => { |
413 | BUILTIN.with(|e| Type::Enumeration(e.enums.$pub_type.clone())) |
414 | }; |
415 | } |
416 | #[rustfmt::skip] |
417 | macro_rules! maybe_clone { |
418 | ($pub_type:ident, KeyboardModifiers) => { $pub_type.clone() }; |
419 | ($pub_type:ident, $_:ident) => { $pub_type }; |
420 | } |
421 | macro_rules! register_builtin_structs { |
422 | ($( |
423 | $(#[$attr:meta])* |
424 | struct $Name:ident { |
425 | @name = $inner_name:literal |
426 | export { |
427 | $( $(#[$pub_attr:meta])* $pub_field:ident : $pub_type:ident, )* |
428 | } |
429 | private { |
430 | $( $(#[$pri_attr:meta])* $pri_field:ident : $pri_type:ty, )* |
431 | } |
432 | } |
433 | )*) => { $( |
434 | #[allow(non_snake_case)] |
435 | let $Name = Type::Struct(Rc::new(Struct{ |
436 | fields: BTreeMap::from([ |
437 | $((stringify!($pub_field).replace_smolstr("_" , "-" ), map_type!($pub_type, $pub_type))),* |
438 | ]), |
439 | name: Some(format_smolstr!("{}" , $inner_name)), |
440 | node: None, |
441 | rust_attributes: None, |
442 | })); |
443 | register.insert_type_with_name(maybe_clone!($Name, $Name), SmolStr::new(stringify!($Name))); |
444 | )* }; |
445 | } |
446 | i_slint_common::for_each_builtin_structs!(register_builtin_structs); |
447 | |
448 | crate::load_builtins::load_builtins(&mut register); |
449 | |
450 | for e in register.elements.values() { |
451 | if let ElementType::Builtin(b) = e { |
452 | for accepted_child_type_name in b.additional_accepted_child_types.keys() { |
453 | register |
454 | .context_restricted_types |
455 | .entry(accepted_child_type_name.clone()) |
456 | .or_default() |
457 | .insert(b.native_class.class_name.clone()); |
458 | } |
459 | if b.additional_accept_self { |
460 | register |
461 | .context_restricted_types |
462 | .entry(b.native_class.class_name.clone()) |
463 | .or_default() |
464 | .insert(b.native_class.class_name.clone()); |
465 | } |
466 | } |
467 | } |
468 | |
469 | match &mut register.elements.get_mut("PopupWindow" ).unwrap() { |
470 | ElementType::Builtin(ref mut b) => { |
471 | let popup = Rc::get_mut(b).unwrap(); |
472 | popup.properties.insert( |
473 | "show" .into(), |
474 | BuiltinPropertyInfo::from(BuiltinFunction::ShowPopupWindow), |
475 | ); |
476 | |
477 | popup.properties.insert( |
478 | "close" .into(), |
479 | BuiltinPropertyInfo::from(BuiltinFunction::ClosePopupWindow), |
480 | ); |
481 | |
482 | popup.properties.get_mut("close-on-click" ).unwrap().property_visibility = |
483 | PropertyVisibility::Constexpr; |
484 | |
485 | popup.properties.get_mut("close-policy" ).unwrap().property_visibility = |
486 | PropertyVisibility::Constexpr; |
487 | } |
488 | _ => unreachable!(), |
489 | }; |
490 | |
491 | let font_metrics_prop = crate::langtype::BuiltinPropertyInfo { |
492 | ty: font_metrics_type(), |
493 | property_visibility: PropertyVisibility::Output, |
494 | default_value: BuiltinPropertyDefault::WithElement(|elem| { |
495 | crate::expression_tree::Expression::FunctionCall { |
496 | function: BuiltinFunction::ItemFontMetrics.into(), |
497 | arguments: vec![crate::expression_tree::Expression::ElementReference( |
498 | Rc::downgrade(elem), |
499 | )], |
500 | source_location: None, |
501 | } |
502 | }), |
503 | }; |
504 | |
505 | match &mut register.elements.get_mut("TextInput" ).unwrap() { |
506 | ElementType::Builtin(ref mut b) => { |
507 | let text_input = Rc::get_mut(b).unwrap(); |
508 | text_input.properties.insert( |
509 | "set-selection-offsets" .into(), |
510 | BuiltinPropertyInfo::from(BuiltinFunction::SetSelectionOffsets), |
511 | ); |
512 | text_input.properties.insert("font-metrics" .into(), font_metrics_prop.clone()); |
513 | } |
514 | |
515 | _ => unreachable!(), |
516 | }; |
517 | |
518 | match &mut register.elements.get_mut("Text" ).unwrap() { |
519 | ElementType::Builtin(ref mut b) => { |
520 | let text = Rc::get_mut(b).unwrap(); |
521 | text.properties.insert("font-metrics" .into(), font_metrics_prop); |
522 | } |
523 | |
524 | _ => unreachable!(), |
525 | }; |
526 | |
527 | match &mut register.elements.get_mut("Path" ).unwrap() { |
528 | ElementType::Builtin(ref mut b) => { |
529 | let path = Rc::get_mut(b).unwrap(); |
530 | path.properties.get_mut("commands" ).unwrap().property_visibility = |
531 | PropertyVisibility::Fake; |
532 | } |
533 | |
534 | _ => unreachable!(), |
535 | }; |
536 | |
537 | register |
538 | } |
539 | |
540 | #[doc (hidden)] |
541 | /// All builtins incl. experimental ones! Do not use in production code! |
542 | pub fn builtin_experimental() -> Rc<RefCell<Self>> { |
543 | let register = Self::builtin_internal(); |
544 | Rc::new(RefCell::new(register)) |
545 | } |
546 | |
547 | pub fn builtin() -> Rc<RefCell<Self>> { |
548 | let mut register = Self::builtin_internal(); |
549 | |
550 | register.elements.remove("ComponentContainer" ); |
551 | register.types.remove("component-factory" ); |
552 | |
553 | Rc::new(RefCell::new(register)) |
554 | } |
555 | |
556 | pub fn new(parent: &Rc<RefCell<TypeRegister>>) -> Self { |
557 | Self { |
558 | parent_registry: Some(parent.clone()), |
559 | expose_internal_types: parent.borrow().expose_internal_types, |
560 | ..Default::default() |
561 | } |
562 | } |
563 | |
564 | pub fn lookup(&self, name: &str) -> Type { |
565 | self.types |
566 | .get(name) |
567 | .cloned() |
568 | .or_else(|| self.parent_registry.as_ref().map(|r| r.borrow().lookup(name))) |
569 | .unwrap_or_default() |
570 | } |
571 | |
572 | fn lookup_element_as_result( |
573 | &self, |
574 | name: &str, |
575 | ) -> Result<ElementType, HashMap<SmolStr, HashSet<SmolStr>>> { |
576 | match self.elements.get(name).cloned() { |
577 | Some(ty) => Ok(ty), |
578 | None => match &self.parent_registry { |
579 | Some(r) => r.borrow().lookup_element_as_result(name), |
580 | None => Err(self.context_restricted_types.clone()), |
581 | }, |
582 | } |
583 | } |
584 | |
585 | pub fn lookup_element(&self, name: &str) -> Result<ElementType, String> { |
586 | self.lookup_element_as_result(name).map_err(|context_restricted_types| { |
587 | if let Some(permitted_parent_types) = context_restricted_types.get(name) { |
588 | if permitted_parent_types.len() == 1 { |
589 | format!( |
590 | " {} can only be within a {} element" , |
591 | name, |
592 | permitted_parent_types.iter().next().unwrap() |
593 | ) |
594 | } else { |
595 | let mut elements = permitted_parent_types.iter().cloned().collect::<Vec<_>>(); |
596 | elements.sort(); |
597 | format!( |
598 | " {} can only be within the following elements: {}" , |
599 | name, |
600 | elements.join(", " ) |
601 | ) |
602 | } |
603 | } else if let Some(ty) = self.types.get(name) { |
604 | format!("' {ty}' cannot be used as an element" ) |
605 | } else { |
606 | format!("Unknown element ' {name}'" ) |
607 | } |
608 | }) |
609 | } |
610 | |
611 | pub fn lookup_builtin_element(&self, name: &str) -> Option<ElementType> { |
612 | self.parent_registry.as_ref().map_or_else( |
613 | || self.elements.get(name).cloned(), |
614 | |p| p.borrow().lookup_builtin_element(name), |
615 | ) |
616 | } |
617 | |
618 | pub fn lookup_qualified<Member: AsRef<str>>(&self, qualified: &[Member]) -> Type { |
619 | if qualified.len() != 1 { |
620 | return Type::Invalid; |
621 | } |
622 | self.lookup(qualified[0].as_ref()) |
623 | } |
624 | |
625 | /// Add the component with it's defined name |
626 | /// |
627 | /// Returns false if there was already an element with the same name |
628 | pub fn add(&mut self, comp: Rc<Component>) -> bool { |
629 | self.add_with_name(comp.id.clone(), comp) |
630 | } |
631 | |
632 | /// Add the component with a specified name |
633 | /// |
634 | /// Returns false if there was already an element with the same name |
635 | pub fn add_with_name(&mut self, name: SmolStr, comp: Rc<Component>) -> bool { |
636 | self.elements.insert(name, ElementType::Component(comp)).is_none() |
637 | } |
638 | |
639 | pub fn add_builtin(&mut self, builtin: Rc<BuiltinElement>) { |
640 | self.elements.insert(builtin.name.clone(), ElementType::Builtin(builtin)); |
641 | } |
642 | |
643 | pub fn property_animation_type_for_property(&self, property_type: Type) -> ElementType { |
644 | if self.supported_property_animation_types.contains(&property_type.to_string()) { |
645 | self.property_animation_type.clone() |
646 | } else { |
647 | self.parent_registry |
648 | .as_ref() |
649 | .map(|registry| { |
650 | registry.borrow().property_animation_type_for_property(property_type) |
651 | }) |
652 | .unwrap_or_default() |
653 | } |
654 | } |
655 | |
656 | /// Return a hashmap with all the registered type |
657 | pub fn all_types(&self) -> HashMap<SmolStr, Type> { |
658 | let mut all = |
659 | self.parent_registry.as_ref().map(|r| r.borrow().all_types()).unwrap_or_default(); |
660 | for (k, v) in &self.types { |
661 | all.insert(k.clone(), v.clone()); |
662 | } |
663 | all |
664 | } |
665 | |
666 | /// Return a hashmap with all the registered element type |
667 | pub fn all_elements(&self) -> HashMap<SmolStr, ElementType> { |
668 | let mut all = |
669 | self.parent_registry.as_ref().map(|r| r.borrow().all_elements()).unwrap_or_default(); |
670 | for (k, v) in &self.elements { |
671 | all.insert(k.clone(), v.clone()); |
672 | } |
673 | all |
674 | } |
675 | |
676 | pub fn empty_type(&self) -> ElementType { |
677 | match self.parent_registry.as_ref() { |
678 | Some(parent) => parent.borrow().empty_type(), |
679 | None => self.empty_type.clone(), |
680 | } |
681 | } |
682 | } |
683 | |
684 | pub fn logical_point_type() -> Type { |
685 | BUILTIN.with(|types: &BuiltinTypes| types.logical_point_type.clone()) |
686 | } |
687 | |
688 | pub fn font_metrics_type() -> Type { |
689 | BUILTIN.with(|types: &BuiltinTypes| types.font_metrics_type.clone()) |
690 | } |
691 | |
692 | /// The [`Type`] for a runtime LayoutInfo structure |
693 | pub fn layout_info_type() -> Rc<Struct> { |
694 | BUILTIN.with(|types: &BuiltinTypes| types.layout_info_type.clone()) |
695 | } |
696 | |
697 | /// The [`Type`] for a runtime PathElement structure |
698 | pub fn path_element_type() -> Type { |
699 | BUILTIN.with(|types: &BuiltinTypes| types.path_element_type.clone()) |
700 | } |
701 | |
702 | /// The [`Type`] for a runtime BoxLayoutCellData structure |
703 | pub fn box_layout_cell_data_type() -> Type { |
704 | BUILTIN.with(|types: &BuiltinTypes| types.box_layout_cell_data_type.clone()) |
705 | } |
706 | |