Refactor the TypeScript data flow for full type safety and auto-generation of Rust types (#3865)

* Migrate Specta to Tsify to auto-generate messages.ts, working except colors and widgets

* Adopt the generated FillColor/Color/GradientStops

* Fix widget typing

* Separate WidgetGroup enum variants into wrapper structs

* Small rename

* Simplify widgets further

* Clean up message type references

* Switch type imports to the auto-generated file

* Remove lowercase serde rename

* Fix FillChoice deserialization

* Fix small regression from #3837

* Improve type safety

* Make WidgetSpan type-safe

* More cleanup and type safety

* More type safety

* More type safety

* Get the rest to type-check without errors; improve widget builder macro to have optional icons; improve Svelte 5 configs

* Cargo fmt

* Fix imports

* Update outdated readme info

* Fix lint command rename references

* Fix typos

* One more typos fix

* Remove unnecessary dep: prefix from the edited Cargo.toml files

* Remove excess parts from Cargo.toml

* Fix compiling on desktop

* Revert "Remove excess parts from Cargo.toml"

This reverts commit 6b711117b3a5d5d8a3ee20f36a43bc74930b7c82.

* Update dev docs with simpler, more accurate instructions
This commit is contained in:
Keavon Chambers
2026-03-09 16:35:04 -07:00
parent fbd2658148
commit 52d2b38a82
199 changed files with 2265 additions and 2811 deletions
@@ -1,8 +1,7 @@
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use graphene_std::raster::color::Color;
use graphene_std::vector::style::{FillChoice, GradientStop, GradientStops};
use graphene_std::vector::style::FillChoice;
use serde_json::Value;
use std::collections::HashMap;
@@ -63,7 +62,7 @@ impl LayoutMessageHandler {
while let Some((mut widget_path, layout_group)) = stack.pop() {
match layout_group {
// Check if any of the widgets in the current column or row have the correct id
LayoutGroup::Column { widgets } | LayoutGroup::Row { widgets } => {
LayoutGroup::Column(WidgetColumn { widgets }) | LayoutGroup::Row(WidgetRow { widgets }) => {
for (index, widget) in widgets.iter().enumerate() {
// Return if this is the correct ID
if widget.widget_id == widget_id {
@@ -84,10 +83,10 @@ impl LayoutMessageHandler {
}
}
// A section contains more LayoutGroups which we add to the stack.
LayoutGroup::Section { layout, .. } => {
LayoutGroup::Section(WidgetSection { layout, .. }) => {
stack.extend(layout.0.iter().enumerate().map(|(index, val)| ([widget_path.as_slice(), &[index]].concat(), val)));
}
LayoutGroup::Table { rows, .. } => {
LayoutGroup::Table(WidgetTable { rows, .. }) => {
for (row_index, row) in rows.iter().enumerate() {
for (cell_index, cell) in row.iter().enumerate() {
// Return if this is the correct ID
@@ -158,60 +157,12 @@ impl LayoutMessageHandler {
let callback_message = match action {
WidgetValueAction::Commit => (color_button.on_commit.callback)(&()),
WidgetValueAction::Update => {
// Decodes the colors in gamma, not linear
let decode_color = |color: &serde_json::map::Map<String, serde_json::value::Value>| -> Option<Color> {
let red = color.get("red").and_then(|x| x.as_f64()).map(|x| x as f32);
let green = color.get("green").and_then(|x| x.as_f64()).map(|x| x as f32);
let blue = color.get("blue").and_then(|x| x.as_f64()).map(|x| x as f32);
let alpha = color.get("alpha").and_then(|x| x.as_f64()).map(|x| x as f32);
if let (Some(red), Some(green), Some(blue), Some(alpha)) = (red, green, blue, alpha)
&& let Some(color) = Color::from_rgbaf32(red, green, blue, alpha)
{
return Some(color);
}
None
let Ok(fill_choice) = serde_json::from_value::<FillChoice>(value) else {
warn!("ColorInput update was not able to be parsed as FillChoice: {color_button:?}");
return;
};
(|| {
let Some(update_value) = value.as_object() else {
warn!("ColorInput update was not of type: object");
return Message::NoOp;
};
// None
let is_none = update_value.get("none").and_then(|x| x.as_bool());
if is_none == Some(true) {
color_button.value = FillChoice::None;
return (color_button.on_update.callback)(color_button);
}
// Solid
if let Some(color) = decode_color(update_value) {
color_button.value = FillChoice::Solid(color);
return (color_button.on_update.callback)(color_button);
}
// Gradient
let positions = update_value.get("position").and_then(|x| x.as_array());
let midpoints = update_value.get("midpoint").and_then(|x| x.as_array());
let colors = update_value.get("color").and_then(|x| x.as_array());
if let (Some(positions), Some(midpoints), Some(colors)) = (positions, midpoints, colors) {
let gradient_stops = positions.iter().zip(midpoints.iter()).zip(colors.iter()).filter_map(|((pos, mid), col)| {
let position = pos.as_f64()?;
let midpoint = mid.as_f64()?;
let color = col.as_object().and_then(decode_color)?;
Some(GradientStop { position, midpoint, color })
});
color_button.value = FillChoice::Gradient(GradientStops::new(gradient_stops));
return (color_button.on_update.callback)(color_button);
}
warn!("ColorInput update was not able to be parsed with color data: {color_button:?}");
Message::NoOp
})()
color_button.value = fill_choice;
(color_button.on_update.callback)(color_button)
}
};
@@ -10,7 +10,8 @@ use std::hash::{Hash, Hasher};
use std::sync::Arc;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
pub struct WidgetId(pub u64);
impl core::fmt::Display for WidgetId {
@@ -19,7 +20,8 @@ impl core::fmt::Display for WidgetId {
}
}
#[derive(PartialEq, Clone, Debug, Hash, Eq, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(PartialEq, Clone, Debug, Hash, Eq, Copy, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum LayoutTarget {
/// The spreadsheet panel allows for the visualisation of data in the graph.
@@ -59,6 +61,7 @@ pub enum LayoutTarget {
// KEEP THIS ENUM LAST
// This is a marker that is used to define an array that is used to hold widgets
#[serde(skip)]
_LayoutTargetLength,
}
@@ -151,7 +154,8 @@ fn compute_checkbox_id(layout_target: LayoutTarget, widget_path: &[usize], widge
}
/// Contains an arrangement of widgets mounted somewhere specific in the frontend.
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct Layout(pub Vec<LayoutGroup>);
impl Layout {
@@ -241,19 +245,19 @@ impl<'a> Iterator for WidgetIter<'a> {
}
match self.stack.pop() {
Some(LayoutGroup::Column { widgets }) => {
Some(LayoutGroup::Column(WidgetColumn { widgets })) => {
self.current_slice = Some(widgets);
self.next()
}
Some(LayoutGroup::Row { widgets }) => {
Some(LayoutGroup::Row(WidgetRow { widgets })) => {
self.current_slice = Some(widgets);
self.next()
}
Some(LayoutGroup::Table { rows, .. }) => {
Some(LayoutGroup::Table(WidgetTable { rows, .. })) => {
self.table.extend(rows.iter().flatten().rev());
self.next()
}
Some(LayoutGroup::Section { layout, .. }) => {
Some(LayoutGroup::Section(WidgetSection { layout, .. })) => {
for layout_row in &layout.0 {
self.stack.push(layout_row);
}
@@ -293,19 +297,19 @@ impl<'a> Iterator for WidgetIterMut<'a> {
}
match self.stack.pop() {
Some(LayoutGroup::Column { widgets }) => {
Some(LayoutGroup::Column(WidgetColumn { widgets })) => {
self.current_slice = Some(widgets);
self.next()
}
Some(LayoutGroup::Row { widgets }) => {
Some(LayoutGroup::Row(WidgetRow { widgets })) => {
self.current_slice = Some(widgets);
self.next()
}
Some(LayoutGroup::Table { rows, .. }) => {
Some(LayoutGroup::Table(WidgetTable { rows, .. })) => {
self.table.extend(rows.iter_mut().flatten().rev());
self.next()
}
Some(LayoutGroup::Section { layout, .. }) => {
Some(LayoutGroup::Section(WidgetSection { layout, .. })) => {
for layout_row in &mut layout.0 {
self.stack.push(layout_row);
}
@@ -316,52 +320,88 @@ impl<'a> Iterator for WidgetIterMut<'a> {
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum LayoutGroup {
#[serde(rename = "column")]
Column {
#[serde(rename = "columnWidgets")]
widgets: Vec<WidgetInstance>,
},
#[serde(rename = "row")]
Row {
#[serde(rename = "rowWidgets")]
widgets: Vec<WidgetInstance>,
},
#[serde(rename = "table")]
Table {
#[serde(rename = "tableWidgets")]
rows: Vec<Vec<WidgetInstance>>,
unstyled: bool,
},
#[serde(rename = "section")]
Section {
name: String,
description: String,
visible: bool,
pinned: bool,
id: u64,
layout: Layout,
},
Column(WidgetColumn),
Row(WidgetRow),
Table(WidgetTable),
Section(WidgetSection),
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct WidgetColumn {
#[serde(rename = "columnWidgets")]
pub widgets: Vec<WidgetInstance>,
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct WidgetRow {
#[serde(rename = "rowWidgets")]
pub widgets: Vec<WidgetInstance>,
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct WidgetTable {
#[serde(rename = "tableWidgets")]
pub rows: Vec<Vec<WidgetInstance>>,
pub unstyled: bool,
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct WidgetSection {
pub name: String,
pub description: String,
pub visible: bool,
pub pinned: bool,
pub id: u64,
pub layout: Layout,
}
impl Default for LayoutGroup {
fn default() -> Self {
Self::Row { widgets: Vec::new() }
Self::Row(Default::default())
}
}
impl From<Vec<WidgetInstance>> for LayoutGroup {
fn from(widgets: Vec<WidgetInstance>) -> LayoutGroup {
LayoutGroup::Row { widgets }
LayoutGroup::Row(WidgetRow { widgets })
}
}
impl LayoutGroup {
pub fn row(widgets: Vec<WidgetInstance>) -> Self {
Self::Row(WidgetRow { widgets })
}
pub fn column(widgets: Vec<WidgetInstance>) -> Self {
Self::Column(WidgetColumn { widgets })
}
pub fn table(rows: Vec<Vec<WidgetInstance>>, unstyled: bool) -> Self {
Self::Table(WidgetTable { rows, unstyled })
}
pub fn section(name: impl Into<String>, description: impl Into<String>, visible: bool, pinned: bool, id: u64, layout: Layout) -> Self {
Self::Section(WidgetSection {
name: name.into(),
description: description.into(),
visible,
pinned,
id,
layout,
})
}
/// Applies a tooltip description to all widgets without a tooltip in this row or column.
pub fn with_tooltip_description(self, description: impl Into<String>) -> Self {
let (is_col, mut widgets) = match self {
LayoutGroup::Column { widgets } => (true, widgets),
LayoutGroup::Row { widgets } => (false, widgets),
LayoutGroup::Column(WidgetColumn { widgets }) => (true, widgets),
LayoutGroup::Row(WidgetRow { widgets }) => (false, widgets),
_ => unimplemented!(),
};
let description = description.into();
@@ -394,7 +434,7 @@ impl LayoutGroup {
val.clone_from(&description);
}
}
if is_col { Self::Column { widgets } } else { Self::Row { widgets } }
if is_col { Self::Column(WidgetColumn { widgets }) } else { Self::Row(WidgetRow { widgets }) }
}
pub fn iter_mut(&mut self) -> WidgetIterMut<'_> {
@@ -413,7 +453,8 @@ impl Diffable for LayoutGroup {
fn diff(&mut self, new: Self, widget_path: &mut Vec<usize>, widget_diffs: &mut Vec<WidgetDiff>) {
let is_column = matches!(new, Self::Column { .. });
match (self, new) {
(Self::Column { widgets: current_widgets }, Self::Column { widgets: new_widgets }) | (Self::Row { widgets: current_widgets }, Self::Row { widgets: new_widgets }) => {
(Self::Column(WidgetColumn { widgets: current_widgets }), Self::Column(WidgetColumn { widgets: new_widgets }))
| (Self::Row(WidgetRow { widgets: current_widgets }), Self::Row(WidgetRow { widgets: new_widgets })) => {
// If the lengths are different then resend the entire panel
// TODO: Diff insersion and deletion of items
if current_widgets.len() != new_widgets.len() {
@@ -421,7 +462,12 @@ impl Diffable for LayoutGroup {
current_widgets.clone_from(&new_widgets);
// Push back a LayoutGroup update to the diff
let new_value = (if is_column { Self::Column { widgets: new_widgets } } else { Self::Row { widgets: new_widgets } }).into_diff_update();
let new_value = (if is_column {
Self::Column(WidgetColumn { widgets: new_widgets })
} else {
Self::Row(WidgetRow { widgets: new_widgets })
})
.into_diff_update();
let widget_path = widget_path.to_vec();
widget_diffs.push(WidgetDiff { widget_path, new_value });
return;
@@ -434,22 +480,22 @@ impl Diffable for LayoutGroup {
}
}
(
Self::Section {
Self::Section(WidgetSection {
name: current_name,
description: current_description,
visible: current_visible,
pinned: current_pinned,
id: current_id,
layout: current_layout,
},
Self::Section {
}),
Self::Section(WidgetSection {
name: new_name,
description: new_description,
visible: new_visible,
pinned: new_pinned,
id: new_id,
layout: new_layout,
},
}),
) => {
// Resend the entire panel if the lengths, names, visibility, or node IDs are different
// TODO: Diff insersion and deletion of items
@@ -469,14 +515,14 @@ impl Diffable for LayoutGroup {
current_layout.clone_from(&new_layout);
// Push an update layout group to the diff
let new_value = Self::Section {
let new_value = Self::Section(WidgetSection {
name: new_name,
description: new_description,
visible: new_visible,
pinned: new_pinned,
id: new_id,
layout: new_layout,
}
})
.into_diff_update();
let widget_path = widget_path.to_vec();
widget_diffs.push(WidgetDiff { widget_path, new_value });
@@ -501,14 +547,14 @@ impl Diffable for LayoutGroup {
fn collect_checkbox_ids(&self, layout_target: LayoutTarget, widget_path: &mut Vec<usize>, checkbox_map: &mut HashMap<CheckboxId, CheckboxId>) {
match self {
Self::Column { widgets } | Self::Row { widgets } => {
Self::Column(WidgetColumn { widgets }) | Self::Row(WidgetRow { widgets }) => {
for (index, widget) in widgets.iter().enumerate() {
widget_path.push(index);
widget.collect_checkbox_ids(layout_target, widget_path, checkbox_map);
widget_path.pop();
}
}
Self::Table { rows, .. } => {
Self::Table(WidgetTable { rows, .. }) => {
for (row_idx, row) in rows.iter().enumerate() {
for (col_idx, widget) in row.iter().enumerate() {
widget_path.push(row_idx);
@@ -519,7 +565,7 @@ impl Diffable for LayoutGroup {
}
}
}
Self::Section { layout, .. } => {
Self::Section(WidgetSection { layout, .. }) => {
layout.collect_checkbox_ids(layout_target, widget_path, checkbox_map);
}
}
@@ -527,14 +573,14 @@ impl Diffable for LayoutGroup {
fn replace_widget_ids(&mut self, layout_target: LayoutTarget, widget_path: &mut Vec<usize>, checkbox_map: &HashMap<CheckboxId, CheckboxId>) {
match self {
Self::Column { widgets } | Self::Row { widgets } => {
Self::Column(WidgetColumn { widgets }) | Self::Row(WidgetRow { widgets }) => {
for (index, widget) in widgets.iter_mut().enumerate() {
widget_path.push(index);
widget.replace_widget_ids(layout_target, widget_path, checkbox_map);
widget_path.pop();
}
}
Self::Table { rows, .. } => {
Self::Table(WidgetTable { rows, .. }) => {
for (row_idx, row) in rows.iter_mut().enumerate() {
for (col_idx, widget) in row.iter_mut().enumerate() {
widget_path.push(row_idx);
@@ -545,14 +591,15 @@ impl Diffable for LayoutGroup {
}
}
}
Self::Section { layout, .. } => {
Self::Section(WidgetSection { layout, .. }) => {
layout.replace_widget_ids(layout_target, widget_path, checkbox_map);
}
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct WidgetInstance {
#[serde(rename = "widgetId")]
pub widget_id: WidgetId,
@@ -674,9 +721,8 @@ impl Diffable for WidgetInstance {
}
}
#[derive(Clone, specta::Type)]
#[derive(Clone)]
pub struct WidgetCallback<T> {
#[specta(skip)]
pub callback: Arc<dyn Fn(&T) -> Message + 'static + Send + Sync>,
}
@@ -692,7 +738,8 @@ impl<T> Default for WidgetCallback<T> {
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Widget {
BreadcrumbTrailButtons(BreadcrumbTrailButtons),
CheckboxInput(CheckboxInput),
@@ -719,7 +766,8 @@ pub enum Widget {
}
/// A single change to part of the UI, containing the location of the change and the new value.
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct WidgetDiff {
/// A path to the change
/// e.g. [0, 1, 2] in the properties panel is the first section, second row and third widget.
@@ -732,7 +780,8 @@ pub struct WidgetDiff {
}
/// The new value of the UI, sent as part of a diff.
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum DiffUpdate {
#[serde(rename = "layout")]
Layout(Layout),
@@ -1,3 +1,4 @@
use crate::messages::frontend::IconName;
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
@@ -6,12 +7,14 @@ use derivative::*;
use graphene_std::vector::style::FillChoice;
use graphite_proc_macros::WidgetBuilder;
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct IconButton {
// Content
#[widget_builder(constructor)]
pub icon: String,
#[widget_builder(string)]
pub icon: IconName,
#[serde(rename = "hoverIcon")]
pub hover_icon: Option<String>,
#[widget_builder(constructor)]
@@ -38,12 +41,14 @@ pub struct IconButton {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct PopoverButton {
// Content
pub style: Option<String>,
pub icon: Option<String>,
#[widget_builder(string)]
pub icon: Option<IconName>,
pub disabled: bool,
// Children
@@ -63,7 +68,8 @@ pub struct PopoverButton {
pub tooltip_shortcut: Option<ActionShortcut>,
}
#[derive(Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum MenuDirection {
Top,
#[default]
@@ -77,7 +83,8 @@ pub enum MenuDirection {
Center,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct ParameterExposeButton {
// Content
@@ -102,13 +109,15 @@ pub struct ParameterExposeButton {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct TextButton {
// Content
#[widget_builder(constructor)]
pub label: String,
pub icon: Option<String>,
#[widget_builder(string)]
pub icon: Option<IconName>,
#[serde(rename = "hoverIcon")]
pub hover_icon: Option<String>,
pub disabled: bool,
@@ -146,7 +155,8 @@ pub struct TextButton {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct ImageButton {
// Content
@@ -172,7 +182,8 @@ pub struct ImageButton {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct ColorInput {
// Content
@@ -207,7 +218,8 @@ pub struct ColorInput {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct BreadcrumbTrailButtons {
// Content
@@ -1,3 +1,4 @@
use crate::messages::frontend::IconName;
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
@@ -7,14 +8,15 @@ use graphene_std::raster::curve::Curve;
use graphene_std::transform::ReferencePoint;
use graphite_proc_macros::WidgetBuilder;
#[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder)]
#[derivative(Debug, Default, PartialEq)]
pub struct CheckboxInput {
// Content
#[widget_builder(constructor)]
pub checked: bool,
#[derivative(Default(value = "\"Checkmark\".to_string()"))]
pub icon: String,
#[widget_builder(string)]
pub icon: Option<IconName>,
#[serde(rename = "forLabel")]
pub for_label: CheckboxId,
pub disabled: bool,
@@ -36,6 +38,7 @@ pub struct CheckboxInput {
pub on_commit: WidgetCallback<()>,
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
pub struct CheckboxId(pub u64);
@@ -49,14 +52,9 @@ impl Default for CheckboxId {
Self::new()
}
}
impl specta::Type for CheckboxId {
fn inline(_type_map: &mut specta::TypeCollection, _generics: specta::Generics) -> specta::datatype::DataType {
// TODO: This might not be right, but it works for now. We just need the type `bigint | undefined`.
specta::datatype::DataType::Primitive(specta::datatype::PrimitiveType::u64)
}
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct DropdownInput {
// Content
@@ -102,7 +100,8 @@ pub struct DropdownInput {
pub type MenuListEntrySections = Vec<Vec<MenuListEntry>>;
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
#[widget_builder(not_widget_instance)]
pub struct MenuListEntry {
@@ -110,7 +109,8 @@ pub struct MenuListEntry {
#[widget_builder(constructor)]
pub value: String,
pub label: String,
pub icon: String,
#[widget_builder(string)]
pub icon: Option<IconName>,
pub disabled: bool,
// Children
@@ -120,7 +120,7 @@ pub struct MenuListEntry {
pub children_hash: u64,
// Styling
pub font: String,
pub font: Option<String>,
// Tooltips
#[serde(rename = "tooltipLabel")]
@@ -148,7 +148,8 @@ impl std::hash::Hash for MenuListEntry {
}
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct NumberInput {
// Content
@@ -246,22 +247,30 @@ impl NumberInput {
}
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Eq, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Eq)]
pub enum NumberInputIncrementBehavior {
/// The value is added by `step`.
#[default]
Add,
/// The value is multiplied by `step`.
Multiply,
/// The functions `incrementCallbackIncrease` and `incrementCallbackDecrease` call custom behavior.
Callback,
/// The increment arrows are not shown.
None,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Eq, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Eq)]
pub enum NumberInputMode {
#[default]
Increment,
Range,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct NodeCatalog {
// Content
@@ -280,7 +289,8 @@ pub struct NodeCatalog {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct RadioInput {
// Content
@@ -303,7 +313,8 @@ pub struct RadioInput {
// Callbacks exists on the `RadioEntryData` children, not this parent `RadioInput`
}
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
#[widget_builder(not_widget_instance)]
pub struct RadioEntryData {
@@ -311,7 +322,8 @@ pub struct RadioEntryData {
#[widget_builder(constructor)]
pub value: String,
pub label: String,
pub icon: String,
#[widget_builder(string)]
pub icon: Option<IconName>,
// Tooltips
#[serde(rename = "tooltipLabel")]
@@ -330,7 +342,8 @@ pub struct RadioEntryData {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct WorkingColorsInput {
// Content
@@ -340,7 +353,8 @@ pub struct WorkingColorsInput {
pub secondary: Color,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct TextAreaInput {
// Content
@@ -366,7 +380,8 @@ pub struct TextAreaInput {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct TextInput {
// Content
@@ -403,7 +418,8 @@ pub struct TextInput {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
#[derivative(Debug, PartialEq, Default)]
pub struct CurveInput {
// Content
@@ -427,7 +443,8 @@ pub struct CurveInput {
pub on_commit: WidgetCallback<()>,
}
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct ReferencePointInput {
// Content
@@ -1,13 +1,15 @@
use super::input_widgets::CheckboxId;
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::{frontend::IconName, input_mapper::utility_types::misc::ActionShortcut};
use derivative::*;
use graphite_proc_macros::WidgetBuilder;
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, Default, PartialEq, Eq, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, Default, PartialEq, Eq, WidgetBuilder)]
pub struct IconLabel {
// Content
#[widget_builder(constructor)]
pub icon: String,
#[widget_builder(string)]
pub icon: IconName,
pub disabled: bool,
// Tooltips
@@ -19,7 +21,8 @@ pub struct IconLabel {
pub tooltip_shortcut: Option<ActionShortcut>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, WidgetBuilder)]
pub struct Separator {
// Content
pub direction: SeparatorDirection,
@@ -27,14 +30,16 @@ pub struct Separator {
pub style: SeparatorStyle,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum SeparatorDirection {
#[default]
Horizontal,
Vertical,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum SeparatorStyle {
Related,
#[default]
@@ -42,7 +47,8 @@ pub enum SeparatorStyle {
Section,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, Eq, Default, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, Eq, Default, WidgetBuilder)]
#[derivative(PartialEq)]
pub struct TextLabel {
// Content
@@ -78,7 +84,8 @@ pub struct TextLabel {
pub tooltip_shortcut: Option<ActionShortcut>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct ImageLabel {
// Content
@@ -96,7 +103,8 @@ pub struct ImageLabel {
pub tooltip_shortcut: Option<ActionShortcut>,
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder)]
#[derivative(Debug, PartialEq)]
pub struct ShortcutLabel {
// Content