use crate::ir::WidgetKind;
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WidgetSchema {
pub required: &'static [&'static str],
pub optional: &'static [&'static str],
pub events: &'static [&'static str],
pub style_attributes: &'static [&'static str],
pub layout_attributes: &'static [&'static str],
}
pub const COMMON_STYLE_ATTRIBUTES: &[&str] = &[
"background",
"color",
"border_color",
"border_width",
"border_radius",
"border_style",
"shadow",
"opacity",
"transform",
"style",
"text_color",
"shadow_color",
"shadow_offset",
"shadow_blur_radius",
];
pub const COMMON_LAYOUT_ATTRIBUTES: &[&str] = &[
"id",
"width",
"height",
"min_width",
"max_width",
"min_height",
"max_height",
"padding",
"spacing",
"align_items",
"justify_content",
"align",
"align_x",
"align_y",
"align_self",
"direction",
"position",
"top",
"right",
"bottom",
"left",
"z_index",
"class",
"theme",
"theme_ref",
];
pub const COMMON_EVENTS: &[&str] = &[
"on_click",
"on_press",
"on_release",
"on_change",
"on_input",
"on_submit",
"on_select",
"on_toggle",
"on_scroll",
];
impl WidgetSchema {
pub fn all_valid(&self) -> HashSet<&'static str> {
let mut set = HashSet::new();
set.extend(self.required.iter().cloned());
set.extend(self.optional.iter().cloned());
set.extend(self.events.iter().cloned());
set.extend(self.style_attributes.iter().cloned());
set.extend(self.layout_attributes.iter().cloned());
set
}
pub fn all_valid_names(&self) -> Vec<&'static str> {
let mut names = Vec::new();
names.extend_from_slice(self.required);
names.extend_from_slice(self.optional);
names.extend_from_slice(self.events);
names.extend_from_slice(self.style_attributes);
names.extend_from_slice(self.layout_attributes);
names
}
}
pub fn get_widget_schema(kind: &WidgetKind) -> WidgetSchema {
match kind {
WidgetKind::Text => WidgetSchema {
required: &["value"],
optional: &["size", "weight", "color"],
events: COMMON_EVENTS,
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Image => WidgetSchema {
required: &["src"],
optional: &["width", "height", "fit", "filter_method", "path"],
events: COMMON_EVENTS,
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Button => WidgetSchema {
required: &[],
optional: &["label", "enabled"],
events: &["on_click", "on_press", "on_release"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::TextInput => WidgetSchema {
required: &[],
optional: &["placeholder", "value", "password", "icon", "size"],
events: &["on_input", "on_submit", "on_change", "on_paste"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Checkbox => WidgetSchema {
required: &[],
optional: &["checked", "label", "icon", "size"],
events: &["on_toggle"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Radio => WidgetSchema {
required: &["label", "value"],
optional: &[
"id",
"selected",
"disabled",
"size",
"text_size",
"text_line_height",
"text_shaping",
],
events: &["on_select"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Slider => WidgetSchema {
required: &[],
optional: &["min", "max", "value", "step"],
events: &["on_change", "on_release"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Column | WidgetKind::Row | WidgetKind::Container => WidgetSchema {
required: &[],
optional: &[],
events: COMMON_EVENTS,
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Scrollable => WidgetSchema {
required: &[],
optional: &[],
events: &["on_scroll"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Stack => WidgetSchema {
required: &[],
optional: &[],
events: COMMON_EVENTS,
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Svg => WidgetSchema {
required: &["src"],
optional: &["width", "height", "path"],
events: COMMON_EVENTS,
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::PickList => WidgetSchema {
required: &[],
optional: &["placeholder", "selected", "options"],
events: &["on_select"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Toggler => WidgetSchema {
required: &[],
optional: &["checked", "active", "toggled", "label"],
events: &["on_toggle"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Space | WidgetKind::Rule => WidgetSchema {
required: &[],
optional: &[],
events: &[],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::ComboBox => WidgetSchema {
required: &[],
optional: &["placeholder", "value", "selected", "options"],
events: &["on_input", "on_select"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::ProgressBar => WidgetSchema {
required: &[],
optional: &[
"value",
"min",
"max",
"style",
"bar_color",
"background_color",
"border_radius",
"height",
"width",
],
events: &[],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Tooltip => WidgetSchema {
required: &[],
optional: &["message", "position", "delay"],
events: COMMON_EVENTS,
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: &[],
},
WidgetKind::Grid => WidgetSchema {
required: &[],
optional: &["columns"],
events: COMMON_EVENTS,
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Canvas => WidgetSchema {
required: &[],
optional: &["width", "height", "program", "cache"],
events: &["on_click", "on_drag", "on_move", "on_release"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::CanvasRect => WidgetSchema {
required: &["x", "y", "width", "height"],
optional: &["fill", "stroke", "stroke_width", "radius"],
events: &[],
style_attributes: &[],
layout_attributes: &[],
},
WidgetKind::CanvasCircle => WidgetSchema {
required: &["cx", "cy", "radius"],
optional: &["fill", "stroke", "stroke_width"],
events: &[],
style_attributes: &[],
layout_attributes: &[],
},
WidgetKind::CanvasLine => WidgetSchema {
required: &["x1", "y1", "x2", "y2"],
optional: &["stroke", "stroke_width"],
events: &[],
style_attributes: &[],
layout_attributes: &[],
},
WidgetKind::CanvasText => WidgetSchema {
required: &["x", "y", "content"],
optional: &["size", "color"],
events: &[],
style_attributes: &[],
layout_attributes: &[],
},
WidgetKind::CanvasGroup => WidgetSchema {
required: &[],
optional: &["transform"],
events: &[],
style_attributes: &[],
layout_attributes: &[],
},
WidgetKind::DatePicker => WidgetSchema {
required: &[],
optional: &["value", "format", "show", "min_date", "max_date"],
events: &["on_submit", "on_cancel"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::TimePicker => WidgetSchema {
required: &[],
optional: &["value", "format", "show", "use_24h", "show_seconds"],
events: &["on_submit", "on_cancel"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::ColorPicker => WidgetSchema {
required: &[],
optional: &["value", "show", "show_alpha", "enabled"],
events: &["on_submit", "on_cancel", "on_change"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::Menu => WidgetSchema {
required: &[],
optional: &["position", "close_on_select", "width", "spacing", "class"],
events: &["on_open", "on_close"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: &["width", "padding"],
},
WidgetKind::MenuItem => WidgetSchema {
required: &["label"],
optional: &["icon", "shortcut", "disabled", "class"],
events: &["on_click"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: &["padding"],
},
WidgetKind::MenuSeparator => WidgetSchema {
required: &[],
optional: &[],
events: &[],
style_attributes: &["color", "opacity"],
layout_attributes: &["height"],
},
WidgetKind::ContextMenu => WidgetSchema {
required: &[],
optional: &["context"],
events: &["on_open", "on_close"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: &[],
},
WidgetKind::Float => WidgetSchema {
required: &[],
optional: &[],
events: COMMON_EVENTS,
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::DataTable => WidgetSchema {
required: &["data"],
optional: &[
"width",
"height",
"min_width",
"max_width",
"scrollbar_width",
],
events: &["on_row_click"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::DataColumn => WidgetSchema {
required: &["header"],
optional: &[
"field",
"width",
"min_width",
"max_width",
"align_x",
"align_y",
],
events: &[],
style_attributes: &[],
layout_attributes: &[],
},
WidgetKind::For => WidgetSchema {
required: &["each", "in"],
optional: &["template"],
events: &[],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::If => WidgetSchema {
required: &["condition"],
optional: &[],
events: &[],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::TreeView => WidgetSchema {
required: &[],
optional: &[
"nodes",
"expanded",
"selected",
"indent_size",
"node_height",
"icon_size",
"expand_icon",
"collapse_icon",
"leaf_icon",
],
events: &["on_toggle", "on_select", "on_double_click"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: COMMON_LAYOUT_ATTRIBUTES,
},
WidgetKind::TreeNode => WidgetSchema {
required: &["id", "label"],
optional: &["icon", "expanded", "selected", "disabled", "class"],
events: &[],
style_attributes: &[],
layout_attributes: &[],
},
WidgetKind::TabBar => WidgetSchema {
required: &["selected"],
optional: &[
"spacing",
"padding",
"icon_size",
"text_size",
"width",
"height",
"class",
],
events: &["on_select"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: &["width", "height"],
},
WidgetKind::Tab => WidgetSchema {
required: &[],
optional: &["label", "icon", "enabled"],
events: &["on_click"],
style_attributes: COMMON_STYLE_ATTRIBUTES,
layout_attributes: &[],
},
WidgetKind::Custom(_) => WidgetSchema {
required: &[],
optional: &[],
events: &[],
style_attributes: &[],
layout_attributes: &[],
},
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::WidgetKind;
#[test]
fn test_button_schema_contains_expected_attributes() {
let schema = WidgetKind::Button.schema();
let valid = schema.all_valid();
assert!(valid.contains("on_click"));
assert!(valid.contains("label"));
assert!(valid.contains("width"));
assert!(valid.contains("background"));
}
#[test]
fn test_container_schema_includes_layout_attributes() {
let schema = WidgetKind::Container.schema();
let valid = schema.all_valid();
assert!(valid.contains("padding"));
assert!(valid.contains("align_x"));
assert!(valid.contains("width"));
}
#[test]
fn test_textinput_schema_includes_size() {
let schema = WidgetKind::TextInput.schema();
let valid = schema.all_valid();
assert!(valid.contains("size"));
assert!(valid.contains("placeholder"));
}
#[test]
fn test_custom_widget_returns_permissive_schema() {
let schema = WidgetKind::Custom("MyWidget".to_string()).schema();
assert!(schema.required.is_empty());
}
#[test]
fn test_all_widget_kinds_have_schema() {
let kinds = [
WidgetKind::Column,
WidgetKind::Row,
WidgetKind::Text,
WidgetKind::Button,
WidgetKind::Image,
];
for kind in kinds {
let _ = kind.schema();
}
}
}