1use crate::ir::WidgetKind;
14use std::collections::HashSet;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct WidgetSchema {
37 pub required: &'static [&'static str],
39 pub optional: &'static [&'static str],
41 pub events: &'static [&'static str],
43 pub style_attributes: &'static [&'static str],
45 pub layout_attributes: &'static [&'static str],
47}
48
49pub const COMMON_STYLE_ATTRIBUTES: &[&str] = &[
51 "background",
52 "color",
53 "border_color",
54 "border_width",
55 "border_radius",
56 "border_style",
57 "shadow",
58 "opacity",
59 "transform",
60 "style",
61 "text_color",
62 "shadow_color",
63 "shadow_offset",
64 "shadow_blur_radius",
65];
66
67pub const COMMON_LAYOUT_ATTRIBUTES: &[&str] = &[
69 "width",
70 "height",
71 "min_width",
72 "max_width",
73 "min_height",
74 "max_height",
75 "padding",
76 "spacing",
77 "align_items",
78 "justify_content",
79 "align",
80 "align_x",
81 "align_y",
82 "align_self",
83 "direction",
84 "position",
85 "top",
86 "right",
87 "bottom",
88 "left",
89 "z_index",
90 "class",
91 "theme",
92 "theme_ref",
93];
94
95pub const COMMON_EVENTS: &[&str] = &[
97 "on_click",
98 "on_press",
99 "on_release",
100 "on_change",
101 "on_input",
102 "on_submit",
103 "on_select",
104 "on_toggle",
105 "on_scroll",
106];
107
108impl WidgetSchema {
109 pub fn all_valid(&self) -> HashSet<&'static str> {
113 let mut set = HashSet::new();
114 set.extend(self.required.iter().cloned());
115 set.extend(self.optional.iter().cloned());
116 set.extend(self.events.iter().cloned());
117 set.extend(self.style_attributes.iter().cloned());
118 set.extend(self.layout_attributes.iter().cloned());
119 set
120 }
121
122 pub fn all_valid_names(&self) -> Vec<&'static str> {
124 let mut names = Vec::new();
125 names.extend_from_slice(self.required);
126 names.extend_from_slice(self.optional);
127 names.extend_from_slice(self.events);
128 names.extend_from_slice(self.style_attributes);
129 names.extend_from_slice(self.layout_attributes);
130 names
131 }
132}
133
134pub fn get_widget_schema(kind: &WidgetKind) -> WidgetSchema {
146 match kind {
147 WidgetKind::Text => WidgetSchema {
148 required: &["value"],
149 optional: &["size", "weight", "color"],
150 events: COMMON_EVENTS,
151 style_attributes: COMMON_STYLE_ATTRIBUTES,
152 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
153 },
154 WidgetKind::Image => WidgetSchema {
155 required: &["src"],
156 optional: &["width", "height", "fit", "filter_method", "path"],
157 events: COMMON_EVENTS,
158 style_attributes: COMMON_STYLE_ATTRIBUTES,
159 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
160 },
161 WidgetKind::Button => WidgetSchema {
162 required: &[],
163 optional: &["label", "enabled"],
164 events: &["on_click", "on_press", "on_release"],
165 style_attributes: COMMON_STYLE_ATTRIBUTES,
166 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
167 },
168 WidgetKind::TextInput => WidgetSchema {
169 required: &[],
170 optional: &["placeholder", "value", "password", "icon", "size"],
171 events: &["on_input", "on_submit", "on_change", "on_paste"],
172 style_attributes: COMMON_STYLE_ATTRIBUTES,
173 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
174 },
175 WidgetKind::Checkbox => WidgetSchema {
176 required: &[],
177 optional: &["checked", "label", "icon", "size"],
178 events: &["on_toggle"],
179 style_attributes: COMMON_STYLE_ATTRIBUTES,
180 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
181 },
182 WidgetKind::Radio => WidgetSchema {
183 required: &["label", "value"],
184 optional: &[
185 "selected",
186 "disabled",
187 "size",
188 "text_size",
189 "text_line_height",
190 "text_shaping",
191 ],
192 events: &["on_select"],
193 style_attributes: COMMON_STYLE_ATTRIBUTES,
194 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
195 },
196 WidgetKind::Slider => WidgetSchema {
197 required: &[],
198 optional: &["min", "max", "value", "step"],
199 events: &["on_change", "on_release"],
200 style_attributes: COMMON_STYLE_ATTRIBUTES,
201 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
202 },
203 WidgetKind::Column | WidgetKind::Row | WidgetKind::Container => WidgetSchema {
204 required: &[],
205 optional: &[],
206 events: COMMON_EVENTS,
207 style_attributes: COMMON_STYLE_ATTRIBUTES,
208 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
209 },
210 WidgetKind::Scrollable => WidgetSchema {
211 required: &[],
212 optional: &[],
213 events: &["on_scroll"],
214 style_attributes: COMMON_STYLE_ATTRIBUTES,
215 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
216 },
217 WidgetKind::Stack => WidgetSchema {
218 required: &[],
219 optional: &[],
220 events: COMMON_EVENTS,
221 style_attributes: COMMON_STYLE_ATTRIBUTES,
222 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
223 },
224 WidgetKind::Svg => WidgetSchema {
225 required: &["src"],
226 optional: &["width", "height", "path"],
227 events: COMMON_EVENTS,
228 style_attributes: COMMON_STYLE_ATTRIBUTES,
229 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
230 },
231 WidgetKind::PickList => WidgetSchema {
232 required: &[],
233 optional: &["placeholder", "selected", "options"],
234 events: &["on_select"],
235 style_attributes: COMMON_STYLE_ATTRIBUTES,
236 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
237 },
238 WidgetKind::Toggler => WidgetSchema {
239 required: &[],
240 optional: &["checked", "active", "label"],
241 events: &["on_toggle"],
242 style_attributes: COMMON_STYLE_ATTRIBUTES,
243 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
244 },
245 WidgetKind::Space | WidgetKind::Rule => WidgetSchema {
246 required: &[],
247 optional: &[],
248 events: &[],
249 style_attributes: COMMON_STYLE_ATTRIBUTES,
250 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
251 },
252 WidgetKind::ComboBox => WidgetSchema {
253 required: &[],
254 optional: &["placeholder", "value", "options"],
255 events: &["on_input", "on_select"],
256 style_attributes: COMMON_STYLE_ATTRIBUTES,
257 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
258 },
259 WidgetKind::ProgressBar => WidgetSchema {
260 required: &[],
261 optional: &["value", "min", "max", "style"],
262 events: &[],
263 style_attributes: COMMON_STYLE_ATTRIBUTES,
264 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
265 },
266 WidgetKind::Tooltip => WidgetSchema {
267 required: &[],
268 optional: &["message", "position", "delay"],
269 events: COMMON_EVENTS,
270 style_attributes: COMMON_STYLE_ATTRIBUTES,
271 layout_attributes: &[],
274 },
275 WidgetKind::Grid => WidgetSchema {
276 required: &[],
277 optional: &["columns"],
278 events: COMMON_EVENTS,
279 style_attributes: COMMON_STYLE_ATTRIBUTES,
280 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
281 },
282 WidgetKind::Canvas => WidgetSchema {
283 required: &[],
284 optional: &["program"],
285 events: &["on_draw"],
286 style_attributes: COMMON_STYLE_ATTRIBUTES,
287 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
288 },
289 WidgetKind::Float => WidgetSchema {
290 required: &[],
291 optional: &[],
292 events: COMMON_EVENTS,
293 style_attributes: COMMON_STYLE_ATTRIBUTES,
294 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
295 },
296 WidgetKind::For => WidgetSchema {
297 required: &["each", "in"],
298 optional: &["template"],
299 events: &[],
300 style_attributes: COMMON_STYLE_ATTRIBUTES,
301 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
302 },
303 WidgetKind::If => WidgetSchema {
304 required: &["condition"],
305 optional: &[],
306 events: &[],
307 style_attributes: COMMON_STYLE_ATTRIBUTES,
308 layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
309 },
310 WidgetKind::Custom(_) => WidgetSchema {
311 required: &[],
312 optional: &[],
313 events: &[],
314 style_attributes: &[],
315 layout_attributes: &[],
316 },
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323 use crate::ir::WidgetKind;
324
325 #[test]
326 fn test_button_schema_contains_expected_attributes() {
327 let schema = WidgetKind::Button.schema();
328 let valid = schema.all_valid();
329
330 assert!(valid.contains("on_click"));
331 assert!(valid.contains("label"));
332 assert!(valid.contains("width"));
334 assert!(valid.contains("background"));
335 }
336
337 #[test]
338 fn test_container_schema_includes_layout_attributes() {
339 let schema = WidgetKind::Container.schema();
340 let valid = schema.all_valid();
341
342 assert!(valid.contains("padding"));
343 assert!(valid.contains("align_x"));
344 assert!(valid.contains("width"));
345 }
346
347 #[test]
348 fn test_textinput_schema_includes_size() {
349 let schema = WidgetKind::TextInput.schema();
350 let valid = schema.all_valid();
351
352 assert!(valid.contains("size"));
353 assert!(valid.contains("placeholder"));
354 }
355
356 #[test]
357 fn test_custom_widget_returns_permissive_schema() {
358 let schema = WidgetKind::Custom("MyWidget".to_string()).schema();
359 assert!(schema.required.is_empty());
360 }
361
362 #[test]
363 fn test_all_widget_kinds_have_schema() {
364 let kinds = [
365 WidgetKind::Column,
366 WidgetKind::Row,
367 WidgetKind::Text,
368 WidgetKind::Button,
369 WidgetKind::Image,
370 ];
371
372 for kind in kinds {
373 let _ = kind.schema();
374 }
375 }
376}