use elicitation::ToCodeLiteral;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub struct ColorJson {
pub r: u8,
pub g: u8,
pub b: u8,
#[serde(default = "default_alpha")]
pub a: u8,
}
fn default_alpha() -> u8 {
255
}
impl ColorJson {
pub fn rgb(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b, a: 255 }
}
pub fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
}
impl From<egui::Color32> for ColorJson {
fn from(c: egui::Color32) -> Self {
Self {
r: c.r(),
g: c.g(),
b: c.b(),
a: c.a(),
}
}
}
impl From<ColorJson> for egui::Color32 {
fn from(c: ColorJson) -> Self {
egui::Color32::from_rgba_unmultiplied(c.r, c.g, c.b, c.a)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub struct StrokeJson {
pub width: f32,
pub color: ColorJson,
}
impl From<egui::Stroke> for StrokeJson {
fn from(s: egui::Stroke) -> Self {
Self {
width: s.width,
color: s.color.into(),
}
}
}
impl From<StrokeJson> for egui::Stroke {
fn from(s: StrokeJson) -> Self {
egui::Stroke::new(s.width, egui::Color32::from(s.color))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub struct RangeJson {
pub min: f64,
pub max: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub struct Vec2Json {
pub x: f32,
pub y: f32,
}
impl From<egui::Vec2> for Vec2Json {
fn from(v: egui::Vec2) -> Self {
Self { x: v.x, y: v.y }
}
}
impl From<Vec2Json> for egui::Vec2 {
fn from(v: Vec2Json) -> Self {
egui::Vec2::new(v.x, v.y)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub struct RectJson {
pub min_x: f32,
pub min_y: f32,
pub max_x: f32,
pub max_y: f32,
}
impl From<egui::Rect> for RectJson {
fn from(r: egui::Rect) -> Self {
Self {
min_x: r.min.x,
min_y: r.min.y,
max_x: r.max.x,
max_y: r.max.y,
}
}
}
impl From<RectJson> for egui::Rect {
fn from(r: RectJson) -> Self {
egui::Rect::from_min_max(egui::pos2(r.min_x, r.min_y), egui::pos2(r.max_x, r.max_y))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub struct CornerRadiusJson {
pub nw: u8,
pub ne: u8,
pub sw: u8,
pub se: u8,
}
impl From<egui::CornerRadius> for CornerRadiusJson {
fn from(r: egui::CornerRadius) -> Self {
Self {
nw: r.nw,
ne: r.ne,
sw: r.sw,
se: r.se,
}
}
}
impl From<CornerRadiusJson> for egui::CornerRadius {
fn from(r: CornerRadiusJson) -> Self {
egui::CornerRadius {
nw: r.nw,
ne: r.ne,
sw: r.sw,
se: r.se,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub struct MarginJson {
pub left: f32,
pub right: f32,
pub top: f32,
pub bottom: f32,
}
impl From<egui::Margin> for MarginJson {
fn from(m: egui::Margin) -> Self {
Self {
left: m.left as f32,
right: m.right as f32,
top: m.top as f32,
bottom: m.bottom as f32,
}
}
}
impl From<MarginJson> for egui::Margin {
fn from(m: MarginJson) -> Self {
egui::Margin {
left: m.left as i8,
right: m.right as i8,
top: m.top as i8,
bottom: m.bottom as i8,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
#[serde(tag = "type")]
pub enum WidgetJson {
Label {
text: String,
#[serde(default)]
wrap: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
color: Option<ColorJson>,
},
Button {
text: String,
#[serde(default)]
wrap: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
fill: Option<ColorJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
stroke: Option<StrokeJson>,
#[serde(default)]
selected: bool,
#[serde(default = "default_true")]
frame: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
min_size: Option<Vec2Json>,
},
SmallButton {
text: String,
},
Checkbox {
text: String,
checked: bool,
},
RadioValue {
text: String,
selected: bool,
},
SelectableLabel {
text: String,
selected: bool,
},
Hyperlink {
text: String,
url: String,
},
Heading {
text: String,
},
Monospace {
text: String,
},
Code {
text: String,
},
Small {
text: String,
},
Strong {
text: String,
},
Weak {
text: String,
},
ColoredLabel {
text: String,
color: ColorJson,
},
Separator,
Spinner,
TextEditSingleline {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
hint: Option<String>,
#[serde(default = "default_true")]
interactive: bool,
},
TextEditMultiline {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
hint: Option<String>,
#[serde(default = "default_true")]
interactive: bool,
},
CodeEditor {
text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
language: Option<String>,
#[serde(default = "default_true")]
interactive: bool,
},
Slider {
value: f64,
range: RangeJson,
#[serde(default, skip_serializing_if = "Option::is_none")]
step: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
text: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
prefix: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
suffix: Option<String>,
#[serde(default)]
logarithmic: bool,
#[serde(default = "default_true")]
clamping: bool,
#[serde(default = "default_true")]
show_value: bool,
},
DragValue {
value: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
range: Option<RangeJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
speed: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
prefix: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
suffix: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
min_decimals: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_decimals: Option<usize>,
},
ProgressBar {
progress: f32,
#[serde(default, skip_serializing_if = "Option::is_none")]
text: Option<String>,
#[serde(default)]
animate: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
fill: Option<ColorJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
desired_width: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
corner_radius: Option<CornerRadiusJson>,
},
Image {
uri: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
size: Option<Vec2Json>,
#[serde(default = "default_true")]
maintain_aspect_ratio: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
tint: Option<ColorJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
corner_radius: Option<CornerRadiusJson>,
},
Link {
text: String,
},
ToggleValue {
text: String,
selected: bool,
},
Radio {
text: String,
selected: bool,
},
DragAngle {
radians: f64,
},
DragAngleTau {
radians: f64,
},
ColorEditButtonSrgba {
color: ColorJson,
#[serde(default = "default_true")]
alpha: bool,
},
ColorEditButtonHsva {
color: ColorJson,
#[serde(default = "default_true")]
alpha: bool,
},
SliderVertical {
value: f64,
range: RangeJson,
#[serde(default, skip_serializing_if = "Option::is_none")]
step: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
text: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
suffix: Option<String>,
#[serde(default)]
logarithmic: bool,
},
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
#[serde(tag = "type")]
pub enum ContainerJson {
Window {
title: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
default_pos: Option<Vec2Json>,
#[serde(default, skip_serializing_if = "Option::is_none")]
default_size: Option<Vec2Json>,
#[serde(default = "default_true")]
resizable: bool,
#[serde(default = "default_true")]
collapsible: bool,
#[serde(default)]
scroll: bool,
#[serde(default = "default_true")]
title_bar: bool,
},
LeftPanel {
id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
default_width: Option<f32>,
#[serde(default = "default_true")]
resizable: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
min_width: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_width: Option<f32>,
},
RightPanel {
id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
default_width: Option<f32>,
#[serde(default = "default_true")]
resizable: bool,
},
TopPanel {
id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
default_height: Option<f32>,
#[serde(default)]
resizable: bool,
},
BottomPanel {
id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
default_height: Option<f32>,
#[serde(default)]
resizable: bool,
},
CentralPanel,
ScrollArea {
#[serde(default = "default_true")]
vertical: bool,
#[serde(default)]
horizontal: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_height: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_width: Option<f32>,
#[serde(default)]
auto_shrink: bool,
#[serde(default)]
always_show_scroll: bool,
},
CollapsingHeader {
text: String,
#[serde(default)]
default_open: bool,
},
Group,
Frame {
#[serde(default, skip_serializing_if = "Option::is_none")]
fill: Option<ColorJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
stroke: Option<StrokeJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
corner_radius: Option<CornerRadiusJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
inner_margin: Option<MarginJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
outer_margin: Option<MarginJson>,
},
MenuBar,
Menu {
title: String,
},
Tooltip {
text: String,
},
Popup {
id: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub enum LayoutDirection {
LeftToRight,
RightToLeft,
TopDown,
BottomUp,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub enum LayoutAlign {
Min,
Center,
Max,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
#[serde(tag = "type")]
pub enum LayoutJson {
Horizontal {
#[serde(default, skip_serializing_if = "Option::is_none")]
align: Option<LayoutAlign>,
},
Vertical {
#[serde(default, skip_serializing_if = "Option::is_none")]
align: Option<LayoutAlign>,
},
HorizontalCentered,
VerticalCentered,
HorizontalJustified,
VerticalJustified,
HorizontalWrapped,
Columns {
count: usize,
},
Grid {
id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
num_columns: Option<usize>,
#[serde(default)]
striped: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
min_col_width: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
max_col_width: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
spacing: Option<Vec2Json>,
},
Indent {
#[serde(default, skip_serializing_if = "Option::is_none")]
indent: Option<f32>,
},
AddSpace {
amount: f32,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
pub struct ResponseJson {
pub clicked: bool,
pub double_clicked: bool,
pub hovered: bool,
pub has_focus: bool,
pub changed: bool,
pub dragged: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub drag_delta: Option<Vec2Json>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rect: Option<RectJson>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, ToCodeLiteral)]
#[serde(tag = "node_type")]
pub enum UiNode {
Widget {
widget: WidgetJson,
},
Container {
container: ContainerJson,
#[serde(default)]
children: Vec<UiNode>,
},
Layout {
layout: LayoutJson,
#[serde(default)]
children: Vec<UiNode>,
},
}