1use std::cell::RefCell;
5use std::collections::{HashMap, HashSet};
6use std::rc::Rc;
7
8use crate::expression_tree::{BuiltinFunction, Expression};
9use crate::langtype::{BuiltinPropertyInfo, Enumeration, PropertyLookupResult, Type};
10use crate::object_tree::Component;
11
12pub(crate) const RESERVED_GEOMETRY_PROPERTIES: &[(&str, Type)] = &[
13 ("x", Type::LogicalLength),
14 ("y", Type::LogicalLength),
15 ("width", Type::LogicalLength),
16 ("height", Type::LogicalLength),
17 ("z", Type::Float32),
18];
19
20const RESERVED_LAYOUT_PROPERTIES: &[(&str, Type)] = &[
21 ("min-width", Type::LogicalLength),
22 ("min-height", Type::LogicalLength),
23 ("max-width", Type::LogicalLength),
24 ("max-height", Type::LogicalLength),
25 ("padding", Type::LogicalLength),
26 ("padding-left", Type::LogicalLength),
27 ("padding-right", Type::LogicalLength),
28 ("padding-top", Type::LogicalLength),
29 ("padding-bottom", Type::LogicalLength),
30 ("preferred-width", Type::LogicalLength),
31 ("preferred-height", Type::LogicalLength),
32 ("horizontal-stretch", Type::Float32),
33 ("vertical-stretch", Type::Float32),
34 ("col", Type::Int32),
35 ("row", Type::Int32),
36 ("colspan", Type::Int32),
37 ("rowspan", Type::Int32),
38];
39
40thread_local! {
41 pub static DIALOG_BUTTON_ROLE_ENUM: Rc<Enumeration> =
42 Rc::new(Enumeration {
43 name: "DialogButtonRole".into(),
44 values: IntoIterator::into_iter([
45 "none".to_owned(),
46 "accept".to_owned(),
47 "reject".to_owned(),
48 "apply".to_owned(),
49 "reset".to_owned(),
50 "action".to_owned(),
51 "help".to_owned(),
52 ])
53 .collect(),
54 default_value: 0,
55 });
56
57 pub static LAYOUT_ALIGNMENT_ENUM: Rc<Enumeration> =
58 Rc::new(Enumeration {
59 name: "LayoutAlignment".into(),
60 values: IntoIterator::into_iter(
61 ["stretch", "center", "start", "end", "space-between", "space-around"]
62 ).map(String::from).collect(),
63 default_value: 0,
64 });
65
66 pub static PATH_EVENT_ENUM: Rc<Enumeration> =
67 Rc::new(Enumeration {
68 name: "PathEvent".into(),
69 values: IntoIterator::into_iter(
70 ["begin", "line", "quadratic", "cubic", "end_open", "end_closed"]
71 ).map(String::from).collect(),
72 default_value: 0,
73 });
74}
75
76const RESERVED_OTHER_PROPERTIES: &[(&str, Type)] = &[
77 ("clip", Type::Bool),
78 ("opacity", Type::Float32),
79 ("visible", Type::Bool), ];
81
82pub(crate) const RESERVED_DROP_SHADOW_PROPERTIES: &[(&str, Type)] = &[
83 ("drop-shadow-offset-x", Type::LogicalLength),
84 ("drop-shadow-offset-y", Type::LogicalLength),
85 ("drop-shadow-blur", Type::LogicalLength),
86 ("drop-shadow-color", Type::Color),
87];
88
89pub fn reserved_properties() -> impl Iterator<Item = (&'static str, Type)> {
91 RESERVED_GEOMETRY_PROPERTIES
92 .iter()
93 .chain(RESERVED_LAYOUT_PROPERTIES.iter())
94 .chain(RESERVED_OTHER_PROPERTIES.iter())
95 .chain(RESERVED_DROP_SHADOW_PROPERTIES.iter())
96 .map(|(k, v)| (*k, v.clone()))
97 .chain(IntoIterator::into_iter([
98 ("forward-focus", Type::ElementReference),
99 ("focus", BuiltinFunction::SetFocusItem.ty()),
100 ("dialog-button-role", Type::Enumeration(DIALOG_BUTTON_ROLE_ENUM.with(|e| e.clone()))),
101 ]))
102}
103
104pub fn reserved_property(name: &str) -> PropertyLookupResult {
106 for (p, t) in reserved_properties() {
107 if p == name {
108 return PropertyLookupResult { property_type: t, resolved_name: name.into() };
109 }
110 }
111
112 for pre in &["min", "max"] {
114 if let Some(a) = name.strip_prefix(pre) {
115 for suf in &["width", "height"] {
116 if let Some(b) = a.strip_suffix(suf) {
117 if b == "imum-" {
118 return PropertyLookupResult {
119 property_type: Type::LogicalLength,
120 resolved_name: format!("{}-{}", pre, suf).into(),
121 };
122 }
123 }
124 }
125 }
126 }
127 PropertyLookupResult { resolved_name: name.into(), property_type: Type::Invalid }
128}
129
130pub fn reserved_member_function(name: &str) -> Expression {
132 for (m, e) in [
133 ("focus", Expression::BuiltinFunctionReference(BuiltinFunction::SetFocusItem, None)), ]
135 .iter()
136 {
137 if *m == name {
138 return e.clone();
139 }
140 }
141 Expression::Invalid
142}
143
144#[derive(Debug, Default)]
145pub struct TypeRegister {
146 types: HashMap<String, Type>,
148 supported_property_animation_types: HashSet<String>,
149 pub(crate) property_animation_type: Type,
150 context_restricted_types: HashMap<String, HashSet<String>>,
153 parent_registry: Option<Rc<RefCell<TypeRegister>>>,
154 pub(crate) expose_internal_types: bool,
156}
157
158impl TypeRegister {
159 pub fn insert_type(&mut self, t: Type) {
161 self.types.insert(t.to_string(), t);
162 }
163 pub fn insert_type_with_name(&mut self, t: Type, name: String) {
164 self.types.insert(name, t);
165 }
166
167 pub fn builtin() -> Rc<RefCell<Self>> {
168 let mut register = TypeRegister::default();
169
170 register.insert_type(Type::Float32);
171 register.insert_type(Type::Int32);
172 register.insert_type(Type::String);
173 register.insert_type(Type::PhysicalLength);
174 register.insert_type(Type::LogicalLength);
175 register.insert_type(Type::Color);
176 register.insert_type(Type::Duration);
177 register.insert_type(Type::Image);
178 register.insert_type(Type::Bool);
179 register.insert_type(Type::Model);
180 register.insert_type(Type::Percent);
181 register.insert_type(Type::Easing);
182 register.insert_type(Type::Angle);
183 register.insert_type(Type::Brush);
184
185 let mut declare_enum = |name: &str, values: &[&str]| {
186 register.insert_type_with_name(
187 Type::Enumeration(Rc::new(Enumeration {
188 name: name.to_owned(),
189 values: values.iter().cloned().map(String::from).collect(),
190 default_value: 0,
191 })),
192 name.to_owned(),
193 );
194 };
195
196 declare_enum("TextHorizontalAlignment", &["left", "center", "right"]);
197 declare_enum("TextVerticalAlignment", &["top", "center", "bottom"]);
198 declare_enum("TextWrap", &["no-wrap", "word-wrap"]);
199 declare_enum("TextOverflow", &["clip", "elide"]);
200 declare_enum("ImageFit", &["fill", "contain", "cover"]);
201 declare_enum("ImageRendering", &["smooth", "pixelated"]);
202 declare_enum("EventResult", &["reject", "accept"]);
203 declare_enum("FillRule", &["nonzero", "evenodd"]);
204 declare_enum(
205 "MouseCursor",
206 &[
207 "default",
208 "none",
209 "help",
210 "pointer",
211 "progress",
212 "wait",
213 "crosshair",
214 "text",
215 "alias",
216 "copy",
217 "no-drop",
218 "not-allowed",
219 "grab",
220 "grabbing",
221 "col-resize",
222 "row-resize",
223 "n-resize",
224 "e-resize",
225 "s-resize",
226 "w-resize",
227 "ne-resize",
228 "nw-resize",
229 "se-resize",
230 "sw-resize",
231 "ew-resize",
232 "ns-resize",
233 "nesw-resize",
234 "nwse-resize",
235 ],
236 );
237 declare_enum(
238 "StandardButtonKind",
239 &[
240 "ok", "cancel", "apply", "close", "reset", "help", "yes", "no", "abort", "retry",
241 "ignore",
242 ],
243 );
244 declare_enum("PointerEventKind", &["cancel", "down", "up"]);
245 declare_enum("PointerEventButton", &["none", "left", "right", "middle"]);
246 DIALOG_BUTTON_ROLE_ENUM
247 .with(|e| register.insert_type_with_name(Type::Enumeration(e.clone()), e.name.clone()));
248 LAYOUT_ALIGNMENT_ENUM
249 .with(|e| register.insert_type_with_name(Type::Enumeration(e.clone()), e.name.clone()));
250
251 register.supported_property_animation_types.insert(Type::Float32.to_string());
252 register.supported_property_animation_types.insert(Type::Int32.to_string());
253 register.supported_property_animation_types.insert(Type::Color.to_string());
254 register.supported_property_animation_types.insert(Type::PhysicalLength.to_string());
255 register.supported_property_animation_types.insert(Type::LogicalLength.to_string());
256 register.supported_property_animation_types.insert(Type::Brush.to_string());
257 register.supported_property_animation_types.insert(Type::Angle.to_string());
258
259 crate::load_builtins::load_builtins(&mut register);
260
261 let mut context_restricted_types = HashMap::new();
262 register
263 .types
264 .values()
265 .for_each(|ty| ty.collect_contextual_types(&mut context_restricted_types));
266 register.context_restricted_types = context_restricted_types;
267
268 match &mut register.types.get_mut("PopupWindow").unwrap() {
269 Type::Builtin(ref mut b) => {
270 Rc::get_mut(b).unwrap().properties.insert(
271 "show".into(),
272 BuiltinPropertyInfo::new(BuiltinFunction::ShowPopupWindow.ty()),
273 );
274 Rc::get_mut(b).unwrap().member_functions.insert(
275 "show".into(),
276 Expression::BuiltinFunctionReference(BuiltinFunction::ShowPopupWindow, None),
277 );
278 }
279 _ => unreachable!(),
280 };
281
282 Rc::new(RefCell::new(register))
283 }
284
285 pub fn new(parent: &Rc<RefCell<TypeRegister>>) -> Self {
286 Self {
287 parent_registry: Some(parent.clone()),
288 expose_internal_types: parent.borrow().expose_internal_types,
289 ..Default::default()
290 }
291 }
292
293 pub fn lookup(&self, name: &str) -> Type {
294 self.types
295 .get(name)
296 .cloned()
297 .or_else(|| self.parent_registry.as_ref().map(|r| r.borrow().lookup(name)))
298 .unwrap_or_default()
299 }
300
301 fn lookup_element_as_result(
302 &self,
303 name: &str,
304 ) -> Result<Type, HashMap<String, HashSet<String>>> {
305 match self.types.get(name).cloned() {
306 Some(ty) => Ok(ty),
307 None => match &self.parent_registry {
308 Some(r) => r.borrow().lookup_element_as_result(name),
309 None => Err(self.context_restricted_types.clone()),
310 },
311 }
312 }
313
314 pub fn lookup_element(&self, name: &str) -> Result<Type, String> {
315 self.lookup_element_as_result(name).map_err(|context_restricted_types| {
316 if let Some(permitted_parent_types) = context_restricted_types.get(name) {
317 if permitted_parent_types.len() == 1 {
318 format!(
319 "{} can only be within a {} element",
320 name,
321 permitted_parent_types.iter().next().unwrap()
322 )
323 } else {
324 let mut elements = permitted_parent_types.iter().cloned().collect::<Vec<_>>();
325 elements.sort();
326 format!(
327 "{} can only be within the following elements: {}",
328 name,
329 elements.join(", ")
330 )
331 }
332 } else {
333 format!("Unknown type {}", name)
334 }
335 })
336 }
337
338 pub fn lookup_qualified<Member: AsRef<str>>(&self, qualified: &[Member]) -> Type {
339 if qualified.len() != 1 {
340 return Type::Invalid;
341 }
342 self.lookup(qualified[0].as_ref())
343 }
344
345 pub fn add(&mut self, comp: Rc<Component>) {
346 self.add_with_name(comp.id.clone(), comp);
347 }
348
349 pub fn add_with_name(&mut self, name: String, comp: Rc<Component>) {
350 self.types.insert(name, Type::Component(comp));
351 }
352
353 pub fn property_animation_type_for_property(&self, property_type: Type) -> Type {
354 if self.supported_property_animation_types.contains(&property_type.to_string()) {
355 self.property_animation_type.clone()
356 } else {
357 self.parent_registry
358 .as_ref()
359 .map(|registry| {
360 registry.borrow().property_animation_type_for_property(property_type)
361 })
362 .unwrap_or_default()
363 }
364 }
365
366 pub fn all_types(&self) -> HashMap<String, Type> {
368 let mut all =
369 self.parent_registry.as_ref().map(|r| r.borrow().all_types()).unwrap_or_default();
370 for (k, v) in &self.types {
371 all.insert(k.clone(), v.clone());
372 }
373 all
374 }
375}