mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 23:18:12 +08:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user