Skip to main content

w_gui/
element.rs

1use std::sync::Arc;
2
3use serde::{Deserialize, Serialize};
4
5pub type ElementId = String;
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8#[serde(tag = "type", content = "data")]
9pub enum Value {
10    Float(f64),
11    Bool(bool),
12    Color3([f32; 3]),
13    Color4([f32; 4]),
14    Vec2([f32; 2]),
15    Vec3([f32; 3]),
16    Int(i64),
17    String(String),
18    Enum {
19        selected: usize,
20        options: Vec<String>,
21    },
22    /// Transient: true for one frame when clicked
23    Button(bool),
24    /// Progress value (0.0 to 1.0)
25    Progress(f64),
26    /// Stat card value with optional subvalue
27    StatValue {
28        value: String,
29        subvalue: Option<String>,
30    },
31    /// Status indicator state
32    StatusValue {
33        active: bool,
34        #[serde(skip_serializing_if = "Option::is_none")]
35        active_text: Option<String>,
36        #[serde(skip_serializing_if = "Option::is_none")]
37        inactive_text: Option<String>,
38        #[serde(skip_serializing_if = "Option::is_none")]
39        active_color: Option<String>,
40        #[serde(skip_serializing_if = "Option::is_none")]
41        inactive_color: Option<String>,
42    },
43    /// Mini chart data
44    ChartValue {
45        values: Vec<f32>,
46        #[serde(skip_serializing_if = "Option::is_none")]
47        current: Option<f32>,
48        #[serde(skip_serializing_if = "Option::is_none")]
49        unit: Option<String>,
50    },
51    /// Grid container data
52    GridValue {
53        cols: usize,
54        children: Vec<String>,
55    },
56    /// Plot data for larger charts
57    PlotValue {
58        series: Vec<PlotSeries>,
59        #[serde(skip_serializing_if = "Option::is_none")]
60        x_label: Option<String>,
61        #[serde(skip_serializing_if = "Option::is_none")]
62        y_label: Option<String>,
63    },
64    /// Null value for container elements
65    Null,
66    /// Image data as a base64 data URI (e.g. "data:image/png;base64,…")
67    ImageValue {
68        data: String,
69        #[serde(skip_serializing_if = "Option::is_none")]
70        width: Option<u32>,
71        #[serde(skip_serializing_if = "Option::is_none")]
72        height: Option<u32>,
73    },
74}
75
76/// A data series for plotting
77#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
78pub struct PlotSeries {
79    pub name: String,
80    pub values: Vec<f32>,
81    pub color: String,
82    /// Whether this series should use relative autoscaling
83    /// When true, the series is scaled independently
84    /// When false, the series uses the plot's shared scale
85    #[serde(default = "default_autoscale")]
86    pub autoscale: bool,
87}
88
89fn default_autoscale() -> bool {
90    true
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
94#[serde(tag = "type", content = "data")]
95pub enum ElementKind {
96    Slider,
97    Checkbox,
98    ColorPicker3,
99    ColorPicker4,
100    TextInput,
101    /// Numeric text field — free-typed number, no slider or range clamp.
102    NumberInput,
103    Dropdown,
104    Button,
105    Label,
106    Separator,
107    /// Section header for grouping
108    Section,
109    /// Progress bar with percentage
110    ProgressBar,
111    /// Stat card display
112    Stat,
113    /// Status indicator with colored dot
114    Status,
115    /// Mini sparkline chart
116    MiniChart,
117    /// Grid layout container
118    Grid,
119    /// Larger plot/chart for data visualization
120    Plot,
121    /// Compact key-value display
122    KeyValue,
123    /// Compact button for dense UIs
124    ButtonCompact,
125    /// Horizontal layout container
126    Horizontal,
127    /// Inline button without label column (for horizontal layouts)
128    ButtonInline,
129    /// Inline text input without label column (for horizontal layouts)
130    TextInputInline,
131    /// Inline label for horizontal layouts (no wrapping)
132    LabelInline,
133    /// Image display widget
134    Image,
135    /// Clickable image — carries an `ImageValue` but reports clicks like a `Button`.
136    ImageButton,
137}
138
139#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
140pub struct ElementMeta {
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub min: Option<f64>,
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub max: Option<f64>,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub step: Option<f64>,
147    /// Accent color for the element
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub accent: Option<AccentColor>,
150    /// Subtitle or secondary text
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub subtitle: Option<String>,
153    /// Number of columns for grid layout
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub cols: Option<usize>,
156    /// Child element IDs for grid layout
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub children: Option<Vec<String>>,
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162pub struct ElementDecl {
163    pub id: ElementId,
164    pub kind: ElementKind,
165    pub label: String,
166    pub value: Value,
167    pub meta: ElementMeta,
168    pub window: Arc<str>,
169}
170
171/// Accent colors available for UI elements
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
173#[serde(rename_all = "lowercase")]
174pub enum AccentColor {
175    Coral,
176    Teal,
177    Blue,
178    Green,
179    Purple,
180    Orange,
181    Yellow,
182    Red,
183    Black,
184    White,
185    Gray,
186}
187
188impl AccentColor {
189    pub fn as_str(&self) -> &'static str {
190        match self {
191            AccentColor::Coral => "coral",
192            AccentColor::Teal => "teal",
193            AccentColor::Blue => "blue",
194            AccentColor::Green => "green",
195            AccentColor::Purple => "purple",
196            AccentColor::Orange => "orange",
197            AccentColor::Yellow => "yellow",
198            AccentColor::Red => "red",
199            AccentColor::Black => "black",
200            AccentColor::White => "white",
201            AccentColor::Gray => "gray",
202        }
203    }
204}