| 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 | |