mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 23:48:11 +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:
@@ -11,37 +11,46 @@ impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
|
||||
fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) {
|
||||
match message {
|
||||
AppWindowMessage::PointerLock => {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowPointerLock);
|
||||
}
|
||||
AppWindowMessage::PointerLockMove { x, y } => {
|
||||
responses.add(FrontendMessage::WindowPointerLockMove { x, y });
|
||||
responses.add(FrontendMessage::WindowPointerLockMove { position: (x, y) });
|
||||
}
|
||||
AppWindowMessage::Close => {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowClose);
|
||||
}
|
||||
AppWindowMessage::Minimize => {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowMinimize);
|
||||
}
|
||||
AppWindowMessage::Maximize => {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowMaximize);
|
||||
}
|
||||
AppWindowMessage::Fullscreen => {
|
||||
responses.add(FrontendMessage::WindowFullscreen);
|
||||
}
|
||||
AppWindowMessage::Drag => {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowDrag);
|
||||
}
|
||||
AppWindowMessage::Hide => {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowHide);
|
||||
}
|
||||
AppWindowMessage::HideOthers => {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowHideOthers);
|
||||
}
|
||||
AppWindowMessage::ShowAll => {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowShowAll);
|
||||
}
|
||||
AppWindowMessage::Restart => {
|
||||
responses.add(PortfolioMessage::AutoSaveAllDocuments);
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
responses.add(FrontendMessage::WindowRestart);
|
||||
}
|
||||
}
|
||||
@@ -57,7 +66,8 @@ impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum AppWindowPlatform {
|
||||
#[default]
|
||||
Web,
|
||||
|
||||
@@ -85,7 +85,7 @@ impl DialogLayoutHolder for ExportDialogMessageHandler {
|
||||
TextButton::new("Cancel").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,10 +170,10 @@ impl LayoutHolder for ExportDialogMessageHandler {
|
||||
];
|
||||
|
||||
Layout(vec![
|
||||
LayoutGroup::Row { widgets: export_type },
|
||||
LayoutGroup::Row { widgets: resolution },
|
||||
LayoutGroup::Row { widgets: export_area },
|
||||
LayoutGroup::Row { widgets: transparent_background },
|
||||
LayoutGroup::row(export_type),
|
||||
LayoutGroup::row(resolution),
|
||||
LayoutGroup::row(export_area),
|
||||
LayoutGroup::row(transparent_background),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -71,7 +71,7 @@ impl DialogLayoutHolder for NewDocumentDialogMessageHandler {
|
||||
TextButton::new("Cancel").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,6 @@ impl LayoutHolder for NewDocumentDialogMessageHandler {
|
||||
.widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets: name }, LayoutGroup::Row { widgets: infinite }, LayoutGroup::Row { widgets: scale }])
|
||||
Layout(vec![LayoutGroup::row(name), LayoutGroup::row(infinite), LayoutGroup::row(scale)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,7 +389,7 @@ impl PreferencesDialogMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
Layout(rows.into_iter().map(|r| LayoutGroup::Row { widgets: r }).collect())
|
||||
Layout(rows.into_iter().map(|r| LayoutGroup::row(r)).collect())
|
||||
}
|
||||
|
||||
pub fn send_layout(&self, responses: &mut VecDeque<Message>, layout_target: LayoutTarget, preferences: &PreferencesMessageHandler) {
|
||||
@@ -416,7 +416,7 @@ impl PreferencesDialogMessageHandler {
|
||||
TextButton::new("Reset to Defaults").on_update(|_| PreferencesMessage::ResetToDefaults.into()).widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
|
||||
fn send_layout_buttons(&self, responses: &mut VecDeque<Message>, layout_target: LayoutTarget) {
|
||||
|
||||
@@ -15,7 +15,7 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
|
||||
fn layout_buttons(&self) -> Layout {
|
||||
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
|
||||
fn layout_column_2(&self) -> Layout {
|
||||
@@ -29,7 +29,7 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
|
||||
.into_iter()
|
||||
.map(|(icon, label, url)| {
|
||||
TextButton::new(label)
|
||||
.icon(Some(icon.into()))
|
||||
.icon(icon)
|
||||
.flush(true)
|
||||
.on_update(|_| FrontendMessage::TriggerVisitLink { url: url.into() }.into())
|
||||
.widget_instance()
|
||||
@@ -40,7 +40,7 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
|
||||
let localized_commit_year = self.localized_commit_year.clone();
|
||||
widgets.push(
|
||||
TextButton::new("Licenses")
|
||||
.icon(Some("License".into()))
|
||||
.icon("License")
|
||||
.flush(true)
|
||||
.on_update(move |_| {
|
||||
DialogMessage::RequestLicensesDialogWithLocalizedCommitDate {
|
||||
@@ -51,22 +51,16 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
|
||||
.widget_instance(),
|
||||
);
|
||||
|
||||
Layout(vec![LayoutGroup::Column { widgets }])
|
||||
Layout(vec![LayoutGroup::column(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutHolder for AboutGraphiteDialog {
|
||||
fn layout(&self) -> Layout {
|
||||
Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("About this release").bold(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(commit_info_localized(&self.localized_commit_date)).multiline(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(format!("Copyright © {} Graphite contributors", self.localized_commit_year)).widget_instance()],
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new("About this release").bold(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new(commit_info_localized(&self.localized_commit_date)).multiline(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new(format!("Copyright © {} Graphite contributors", self.localized_commit_year)).widget_instance()]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ impl DialogLayoutHolder for CloseAllDocumentsDialog {
|
||||
TextButton::new("Cancel").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,12 +33,8 @@ impl LayoutHolder for CloseAllDocumentsDialog {
|
||||
let unsaved_list = "• ".to_string() + &self.unsaved_document_names.join("\n• ");
|
||||
|
||||
Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Save documents before closing them?").bold(true).multiline(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(format!("Documents with unsaved changes:\n{unsaved_list}")).multiline(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new("Save documents before closing them?").bold(true).multiline(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new(format!("Documents with unsaved changes:\n{unsaved_list}")).multiline(true).widget_instance()]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ impl DialogLayoutHolder for CloseDocumentDialog {
|
||||
TextButton::new("Cancel").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,12 +52,8 @@ impl LayoutHolder for CloseDocumentDialog {
|
||||
let break_lines = if self.document_name.len() > max_one_line_length { '\n' } else { ' ' };
|
||||
|
||||
Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Save document before closing it?").bold(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(format!("\"{name}{ellipsis}\"{break_lines}has unsaved changes")).multiline(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new("Save document before closing it?").bold(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new(format!("\"{name}{ellipsis}\"{break_lines}has unsaved changes")).multiline(true).widget_instance()]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ impl DialogLayoutHolder for ConfirmRestartDialog {
|
||||
TextButton::new("Later").on_update(|_| FrontendMessage::DialogClose.into()).widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,27 +33,23 @@ impl LayoutHolder for ConfirmRestartDialog {
|
||||
let changed_settings = "• ".to_string() + &self.changed_settings.join("\n• ");
|
||||
|
||||
Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Restart to apply changes?").bold(true).multiline(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new(
|
||||
format!(
|
||||
"
|
||||
LayoutGroup::row(vec![TextLabel::new("Restart to apply changes?").bold(true).multiline(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![
|
||||
TextLabel::new(
|
||||
format!(
|
||||
"
|
||||
Settings that only take effect on next launch:\n\
|
||||
{changed_settings}\n\
|
||||
\n\
|
||||
This only takes a few seconds. Open documents,\n\
|
||||
even unsaved ones, will be automatically restored.
|
||||
"
|
||||
)
|
||||
.trim(),
|
||||
)
|
||||
.multiline(true)
|
||||
.widget_instance(),
|
||||
],
|
||||
},
|
||||
.trim(),
|
||||
)
|
||||
.multiline(true)
|
||||
.widget_instance(),
|
||||
]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl DialogLayoutHolder for DemoArtworkDialog {
|
||||
fn layout_buttons(&self) -> Layout {
|
||||
let widgets = vec![TextButton::new("Close").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl LayoutHolder for DemoArtworkDialog {
|
||||
|
||||
let images = chunk
|
||||
.iter()
|
||||
.map(|(name, thumbnail, filename)| ImageButton::new(*thumbnail).width(Some("256px".into())).on_update(|_| make_dialog(name, filename)).widget_instance())
|
||||
.map(|(name, thumbnail, filename)| ImageButton::new(*thumbnail).width("256px").on_update(|_| make_dialog(name, filename)).widget_instance())
|
||||
.collect();
|
||||
|
||||
let buttons = chunk
|
||||
@@ -54,7 +54,7 @@ impl LayoutHolder for DemoArtworkDialog {
|
||||
.map(|(name, _, filename)| TextButton::new(*name).min_width(256).flush(true).on_update(|_| make_dialog(name, filename)).widget_instance())
|
||||
.collect();
|
||||
|
||||
vec![LayoutGroup::Row { widgets: images }, LayoutGroup::Row { widgets: buttons }, LayoutGroup::Row { widgets: vec![] }]
|
||||
vec![LayoutGroup::row(images), LayoutGroup::row(buttons), LayoutGroup::row(vec![])]
|
||||
})
|
||||
.collect();
|
||||
let _ = rows_of_images_with_buttons.pop();
|
||||
|
||||
@@ -14,19 +14,15 @@ impl DialogLayoutHolder for ErrorDialog {
|
||||
fn layout_buttons(&self) -> Layout {
|
||||
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutHolder for ErrorDialog {
|
||||
fn layout(&self) -> Layout {
|
||||
Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(&self.title).bold(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(&self.description).multiline(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new(&self.title).bold(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new(&self.description).multiline(true).widget_instance()]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ impl DialogLayoutHolder for LicensesDialog {
|
||||
fn layout_buttons(&self) -> Layout {
|
||||
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
|
||||
fn layout_column_2(&self) -> Layout {
|
||||
@@ -34,10 +34,10 @@ impl DialogLayoutHolder for LicensesDialog {
|
||||
];
|
||||
let widgets = button_definitions
|
||||
.iter()
|
||||
.map(|&(icon, label, message_factory)| TextButton::new(label).icon(Some((icon).into())).flush(true).on_update(move |_| message_factory()).widget_instance())
|
||||
.map(|&(icon, label, message_factory)| TextButton::new(label).icon(icon).flush(true).on_update(move |_| message_factory()).widget_instance())
|
||||
.collect();
|
||||
|
||||
Layout(vec![LayoutGroup::Column { widgets }])
|
||||
Layout(vec![LayoutGroup::column(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +56,8 @@ impl LayoutHolder for LicensesDialog {
|
||||
let description = description.trim();
|
||||
|
||||
Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Graphite is free, open source software").bold(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(description).multiline(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new("Graphite is free, open source software").bold(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new(description).multiline(true).widget_instance()]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ impl DialogLayoutHolder for LicensesThirdPartyDialog {
|
||||
fn layout_buttons(&self) -> Layout {
|
||||
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DialogClose.into()).widget_instance()];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,14 +31,12 @@ impl LayoutHolder for LicensesThirdPartyDialog {
|
||||
// Two characters (one before, one after) the sequence of underscore characters, plus one additional column to provide a space between the text and the scrollbar
|
||||
let non_wrapping_column_width = license_text.split('\n').map(|line| line.chars().filter(|&c| c == '_').count() as u32).max().unwrap_or(0) + 2 + 1;
|
||||
|
||||
Layout(vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new(license_text)
|
||||
.monospace(true)
|
||||
.multiline(true)
|
||||
.min_width_characters(non_wrapping_column_width)
|
||||
.widget_instance(),
|
||||
],
|
||||
}])
|
||||
Layout(vec![LayoutGroup::row(vec![
|
||||
TextLabel::new(license_text)
|
||||
.monospace(true)
|
||||
.multiline(true)
|
||||
.min_width_characters(non_wrapping_column_width)
|
||||
.widget_instance(),
|
||||
])])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use super::IconName;
|
||||
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
||||
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
||||
use crate::messages::frontend::utility_types::EyedropperPreviewImage;
|
||||
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::{
|
||||
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, NodeGraphErrorDiagnostic, Transform,
|
||||
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, NodeGraphErrorDiagnostic,
|
||||
};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::{LayerPanelEntry, LayerStructureEntry};
|
||||
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
|
||||
use crate::messages::prelude::*;
|
||||
use glam::IVec2;
|
||||
use crate::messages::tool::tool_messages::eyedropper_tool::PrimarySecondary;
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::raster::color::Color;
|
||||
@@ -20,13 +21,14 @@ use std::path::PathBuf;
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
|
||||
#[impl_message(Message, Frontend)]
|
||||
#[derive(derivative::Derivative, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(derivative::Derivative, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derivative(Debug, PartialEq)]
|
||||
pub enum FrontendMessage {
|
||||
// Display prefix: make the frontend show something, like a dialog
|
||||
DisplayDialog {
|
||||
title: String,
|
||||
icon: String,
|
||||
icon: IconName,
|
||||
},
|
||||
DialogClose,
|
||||
DisplayDialogPanic {
|
||||
@@ -41,7 +43,7 @@ pub enum FrontendMessage {
|
||||
font_size: f64,
|
||||
color: String,
|
||||
#[serde(rename = "fontData")]
|
||||
font_data: Vec<u8>,
|
||||
font_data: serde_bytes::ByteBuf,
|
||||
transform: [f64; 6],
|
||||
#[serde(rename = "maxWidth")]
|
||||
max_width: Option<f64>,
|
||||
@@ -51,7 +53,7 @@ pub enum FrontendMessage {
|
||||
},
|
||||
DisplayEditableTextboxUpdateFontData {
|
||||
#[serde(rename = "fontData")]
|
||||
font_data: Vec<u8>,
|
||||
font_data: serde_bytes::ByteBuf,
|
||||
},
|
||||
DisplayEditableTextboxTransform {
|
||||
transform: [f64; 6],
|
||||
@@ -87,11 +89,11 @@ pub enum FrontendMessage {
|
||||
document_id: DocumentId,
|
||||
name: String,
|
||||
path: Option<PathBuf>,
|
||||
content: Vec<u8>,
|
||||
content: serde_bytes::ByteBuf,
|
||||
},
|
||||
TriggerSaveFile {
|
||||
name: String,
|
||||
content: Vec<u8>,
|
||||
content: serde_bytes::ByteBuf,
|
||||
},
|
||||
TriggerExportImage {
|
||||
svg: String,
|
||||
@@ -125,6 +127,7 @@ pub enum FrontendMessage {
|
||||
TriggerOpen,
|
||||
TriggerImport,
|
||||
TriggerSavePreferences {
|
||||
#[tsify(type = "unknown")]
|
||||
preferences: PreferencesMessageHandler,
|
||||
},
|
||||
TriggerSaveActiveDocument {
|
||||
@@ -152,9 +155,8 @@ pub enum FrontendMessage {
|
||||
document_id: DocumentId,
|
||||
},
|
||||
UpdateGradientStopColorPickerPosition {
|
||||
color: Color,
|
||||
x: f64,
|
||||
y: f64,
|
||||
color: Color, // TODO: Color (without `none`) -> Color (with `none`)
|
||||
position: (f64, f64),
|
||||
},
|
||||
UpdateImportsExports {
|
||||
/// If the primary import is not visible, then it is None.
|
||||
@@ -163,10 +165,10 @@ pub enum FrontendMessage {
|
||||
exports: Vec<Option<FrontendGraphInput>>,
|
||||
/// The primary import location.
|
||||
#[serde(rename = "importPosition")]
|
||||
import_position: IVec2,
|
||||
import_position: (i32, i32),
|
||||
/// The primary export location.
|
||||
#[serde(rename = "exportPosition")]
|
||||
export_position: IVec2,
|
||||
export_position: (i32, i32),
|
||||
/// The document network does not have an add import or export button.
|
||||
#[serde(rename = "addImportExport")]
|
||||
add_import_export: bool,
|
||||
@@ -202,7 +204,7 @@ pub enum FrontendMessage {
|
||||
UpdateLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
diff: Vec<WidgetDiff>,
|
||||
diff: Vec<WidgetDiff>, // TODO: Align this with what's generated
|
||||
},
|
||||
UpdateImportReorderIndex {
|
||||
#[serde(rename = "importIndex")]
|
||||
@@ -223,6 +225,8 @@ pub enum FrontendMessage {
|
||||
UpdateDocumentArtwork {
|
||||
svg: String,
|
||||
},
|
||||
// This message is intercepted before being sent to the frontend
|
||||
#[serde(skip)]
|
||||
UpdateImageData {
|
||||
image_data: Vec<(u64, Image<Color>)>,
|
||||
},
|
||||
@@ -253,7 +257,7 @@ pub enum FrontendMessage {
|
||||
#[serde(rename = "secondaryColor")]
|
||||
secondary_color: String,
|
||||
#[serde(rename = "setColorChoice")]
|
||||
set_color_choice: Option<String>,
|
||||
set_color_choice: Option<PrimarySecondary>,
|
||||
},
|
||||
UpdateGraphFadeArtwork {
|
||||
percentage: f64,
|
||||
@@ -278,7 +282,8 @@ pub enum FrontendMessage {
|
||||
selected: Vec<NodeId>,
|
||||
},
|
||||
UpdateNodeGraphTransform {
|
||||
transform: Transform,
|
||||
translation: (f64, f64),
|
||||
scale: f64,
|
||||
},
|
||||
UpdateNodeThumbnail {
|
||||
id: NodeId,
|
||||
@@ -304,6 +309,7 @@ pub enum FrontendMessage {
|
||||
UpdateViewportHolePunch {
|
||||
active: bool,
|
||||
},
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
UpdateViewportPhysicalBounds {
|
||||
x: f64,
|
||||
y: f64,
|
||||
@@ -322,18 +328,26 @@ pub enum FrontendMessage {
|
||||
},
|
||||
|
||||
// Window prefix: cause the application window to do something
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowPointerLock,
|
||||
WindowPointerLockMove {
|
||||
x: f64,
|
||||
y: f64,
|
||||
position: (f64, f64),
|
||||
},
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowClose,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowMinimize,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowMaximize,
|
||||
WindowFullscreen,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowDrag,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowHide,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowHideOthers,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowShowAll,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
WindowRestart,
|
||||
}
|
||||
|
||||
@@ -4,3 +4,7 @@ pub mod utility_types;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use frontend_message::{FrontendMessage, FrontendMessageDiscriminant};
|
||||
|
||||
// TODO: Make this an enum with the actual icon names, somehow derived from or tied to the frontend icon set.
|
||||
// TODO: Then remove `#[widget_builder(string)]` from all icon fields.
|
||||
pub type IconName = String;
|
||||
|
||||
@@ -3,13 +3,15 @@ use std::path::PathBuf;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OpenDocument {
|
||||
pub id: DocumentId,
|
||||
pub details: DocumentDetails,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DocumentDetails {
|
||||
pub name: String,
|
||||
pub path: Option<PathBuf>,
|
||||
@@ -19,7 +21,8 @@ pub struct DocumentDetails {
|
||||
pub is_auto_saved: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum MouseCursorIcon {
|
||||
#[default]
|
||||
Default,
|
||||
@@ -37,7 +40,8 @@ pub enum MouseCursorIcon {
|
||||
Rotate,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum FileType {
|
||||
#[default]
|
||||
Png,
|
||||
@@ -55,7 +59,8 @@ impl FileType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ExportBounds {
|
||||
#[default]
|
||||
AllArtwork,
|
||||
@@ -63,9 +68,10 @@ pub enum ExportBounds {
|
||||
Artboard(LayerNodeIdentifier),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct EyedropperPreviewImage {
|
||||
pub data: Vec<u8>,
|
||||
pub data: serde_bytes::ByteBuf,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@ bitflags! {
|
||||
// (although we ignore the shift key, so the user doesn't have to press `Ctrl Shift +` on a US keyboard), even if the keyboard layout
|
||||
// is for a different locale where the `+` key is somewhere entirely different, shifted or not. This would then also work for numpad `+`.
|
||||
#[impl_message(Message, InputMapperMessage, KeyDown)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type, num_enum::TryFromPrimitive)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Key {
|
||||
// Writing system keys
|
||||
@@ -379,7 +380,8 @@ impl fmt::Display for KeysGroup {
|
||||
// LabeledKey
|
||||
// ==========
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LabeledKey {
|
||||
key: Key,
|
||||
label: String,
|
||||
@@ -395,7 +397,8 @@ impl LabeledKey {
|
||||
// MouseMotion
|
||||
// ===========
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum MouseMotion {
|
||||
None,
|
||||
Lmb,
|
||||
@@ -415,7 +418,8 @@ pub enum MouseMotion {
|
||||
// LabeledKeyOrMouseMotion
|
||||
// =======================
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum LabeledKeyOrMouseMotion {
|
||||
Key(LabeledKey),
|
||||
@@ -437,7 +441,8 @@ impl From<Key> for LabeledKeyOrMouseMotion {
|
||||
// LabeledShortcut
|
||||
// ===============
|
||||
|
||||
#[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 struct LabeledShortcut(pub Vec<LabeledKeyOrMouseMotion>);
|
||||
|
||||
impl From<KeysGroup> for LabeledShortcut {
|
||||
|
||||
@@ -110,7 +110,8 @@ bitflags! {
|
||||
}
|
||||
|
||||
#[impl_message(Message, InputMapperMessage, DoubleClick)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type, num_enum::TryFromPrimitive)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
|
||||
@@ -106,8 +106,10 @@ pub struct MappingEntry {
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ActionShortcut {
|
||||
#[serde(skip)]
|
||||
Action(MessageDiscriminant),
|
||||
#[serde(rename = "shortcut")]
|
||||
Shortcut(LabeledShortcut),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -76,7 +76,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||
TextButton::new("Graphite")
|
||||
.label("")
|
||||
.flush(true)
|
||||
.icon(Some("GraphiteLogo".into()))
|
||||
.icon("GraphiteLogo")
|
||||
.on_commit(|_| FrontendMessage::TriggerVisitLink { url: "https://graphite.art".into() }.into())
|
||||
.widget_instance(),
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -750,6 +750,6 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||
.widget_instance(),
|
||||
];
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets: menu_bar_buttons }])
|
||||
Layout(vec![LayoutGroup::row(menu_bar_buttons)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,14 +45,6 @@ pub enum Message {
|
||||
NoOp,
|
||||
}
|
||||
|
||||
/// Provides an impl of `specta::Type` for `MessageDiscriminant`, the struct created by `impl_message`.
|
||||
/// Specta isn't integrated with `impl_message`, so a remote impl must be provided using this struct.
|
||||
impl specta::Type for MessageDiscriminant {
|
||||
fn inline(_type_map: &mut specta::TypeCollection, _generics: specta::Generics) -> specta::DataType {
|
||||
specta::DataType::Any
|
||||
}
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn message_tree() -> DebugMessageTree {
|
||||
Self::build_message_tree()
|
||||
|
||||
@@ -128,7 +128,7 @@ impl DataPanelMessageHandler {
|
||||
}
|
||||
|
||||
if !widgets.is_empty() {
|
||||
layout.0.insert(0, LayoutGroup::Row { widgets });
|
||||
layout.0.insert(0, LayoutGroup::row(widgets));
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
@@ -185,7 +185,7 @@ fn column_headings(value: &[&str]) -> Vec<WidgetInstance> {
|
||||
|
||||
fn label(x: impl Into<String>) -> Vec<LayoutGroup> {
|
||||
let error = vec![TextLabel::new(x).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets: error }]
|
||||
vec![LayoutGroup::row(error)]
|
||||
}
|
||||
|
||||
trait TableRowLayout {
|
||||
@@ -234,7 +234,7 @@ impl<T: TableRowLayout> TableRowLayout for Vec<T> {
|
||||
|
||||
rows.insert(0, column_headings(&["", "element"]));
|
||||
|
||||
vec![LayoutGroup::Table { rows, unstyled: false }]
|
||||
vec![LayoutGroup::table(rows, false)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
|
||||
|
||||
rows.insert(0, column_headings(&["", "element", "transform", "alpha_blending", "source_node_id"]));
|
||||
|
||||
vec![LayoutGroup::Table { rows, unstyled: false }]
|
||||
vec![LayoutGroup::table(rows, false)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ impl TableRowLayout for Vector {
|
||||
}
|
||||
}
|
||||
|
||||
vec![LayoutGroup::Row { widgets: table_tabs }, LayoutGroup::Table { rows: table_rows, unstyled: false }]
|
||||
vec![LayoutGroup::row(table_tabs), LayoutGroup::table(table_rows, false)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,7 +504,7 @@ impl TableRowLayout for Raster<CPU> {
|
||||
|
||||
if raster.width == 0 || raster.height == 0 {
|
||||
let widgets = vec![TextLabel::new("Image has no area").widget_instance()];
|
||||
return vec![LayoutGroup::Row { widgets }];
|
||||
return vec![LayoutGroup::row(widgets)];
|
||||
}
|
||||
|
||||
let base64_string = raster.base64_string.clone().unwrap_or_else(|| {
|
||||
@@ -519,7 +519,7 @@ impl TableRowLayout for Raster<CPU> {
|
||||
});
|
||||
|
||||
let widgets = vec![ImageLabel::new(base64_string).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ impl TableRowLayout for Raster<GPU> {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new("Raster is a texture on the GPU and cannot currently be displayed here").widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ impl TableRowLayout for Color {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![self.element_widget(0)];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ impl TableRowLayout for GradientStops {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![self.element_widget(0)];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,7 +585,7 @@ impl TableRowLayout for f64 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(self.to_string()).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,7 +598,7 @@ impl TableRowLayout for u32 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(self.to_string()).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,7 +611,7 @@ impl TableRowLayout for u64 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(self.to_string()).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +624,7 @@ impl TableRowLayout for bool {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(self.to_string()).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,7 +643,7 @@ impl TableRowLayout for String {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextAreaInput::new(self.to_string()).disabled(true).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,7 +656,7 @@ impl TableRowLayout for Option<f64> {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(format!("{self:?}")).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,7 +669,7 @@ impl TableRowLayout for DVec2 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(format!("({}, {})", self.x, self.y)).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,7 +682,7 @@ impl TableRowLayout for Vec2 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(format!("({}, {})", self.x, self.y)).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +695,7 @@ impl TableRowLayout for DAffine2 {
|
||||
}
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let widgets = vec![TextLabel::new(format_transform_matrix(self)).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,7 +709,7 @@ impl TableRowLayout for Affine2 {
|
||||
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
let matrix = DAffine2::from_cols_array(&self.to_cols_array().map(|x| x as f64));
|
||||
let widgets = vec![TextLabel::new(format_transform_matrix(&matrix)).widget_instance()];
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::row(widgets)]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::node_graph::document_node_definitions;
|
||||
use super::node_graph::utility_types::Transform;
|
||||
use super::utility_types::error::EditorError;
|
||||
use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState};
|
||||
use super::utility_types::network_interface::{self, NodeNetworkInterface, TransactionStatus};
|
||||
@@ -848,7 +847,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
document_id,
|
||||
name: format!("{}.{}", self.name.clone(), FILE_EXTENSION),
|
||||
path: self.path.clone(),
|
||||
content: self.serialize_document().into_bytes(),
|
||||
content: self.serialize_document().into_bytes().into(),
|
||||
})
|
||||
}
|
||||
DocumentMessage::SavedDocument { path } => {
|
||||
@@ -1332,11 +1331,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
|
||||
responses.add(FrontendMessage::UpdateNodeGraphTransform {
|
||||
transform: Transform {
|
||||
scale: transform.matrix2.x_axis.x,
|
||||
x: transform.translation.x,
|
||||
y: transform.translation.y,
|
||||
},
|
||||
translation: transform.translation.into(),
|
||||
scale: transform.matrix2.x_axis.x,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2216,256 +2212,222 @@ impl DocumentMessageHandler {
|
||||
.widget_instance(),
|
||||
PopoverButton::new()
|
||||
.popover_layout(Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Overlays").bold(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("General").widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.artboard_name)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::ArtboardName),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Artboard Name".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.transform_measurement)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::TransformMeasurement),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("G/R/S Measurement".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Select Tool").widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.quick_measurement)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::QuickMeasurement),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Quick Measurement".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.transform_cage)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::TransformCage),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Cage".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.compass_rose)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::CompassRose),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Dial".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.pivot)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Pivot),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Pivot".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.pivot)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Origin),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Origin".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.hover_outline)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::HoverOutline),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Hover Outline".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.selection_outline)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::SelectionOutline),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Selection Outline".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.layer_origin_cross)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::LayerOriginCross),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Layer Origin".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Pen & Path Tools").widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.path)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Path),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Path".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.anchors)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Anchors),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Anchors".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: {
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.handles)
|
||||
.disabled(!self.overlays_visibility_settings.anchors)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Handles),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Handles".to_string())
|
||||
.disabled(!self.overlays_visibility_settings.anchors)
|
||||
.for_checkbox(checkbox_id)
|
||||
.widget_instance(),
|
||||
]
|
||||
},
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new("Overlays").bold(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new("General").widget_instance()]),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.artboard_name)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::ArtboardName),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Artboard Name".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.transform_measurement)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::TransformMeasurement),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("G/R/S Measurement".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row(vec![TextLabel::new("Select Tool").widget_instance()]),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.quick_measurement)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::QuickMeasurement),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Quick Measurement".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.transform_cage)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::TransformCage),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Cage".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.compass_rose)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::CompassRose),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Dial".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.pivot)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Pivot),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Pivot".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.origin)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Origin),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Transform Origin".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.hover_outline)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::HoverOutline),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Hover Outline".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.selection_outline)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::SelectionOutline),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Selection Outline".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.layer_origin_cross)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::LayerOriginCross),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Layer Origin".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row(vec![TextLabel::new("Pen & Path Tools").widget_instance()]),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.path)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Path),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Path".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.anchors)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Anchors),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Anchors".to_string()).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
}),
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(self.overlays_visibility_settings.handles)
|
||||
.disabled(!self.overlays_visibility_settings.anchors)
|
||||
.on_update(|optional_input: &CheckboxInput| {
|
||||
DocumentMessage::SetOverlaysVisibility {
|
||||
visible: optional_input.checked,
|
||||
overlays_type: Some(OverlaysType::Handles),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_label(checkbox_id)
|
||||
.widget_instance(),
|
||||
TextLabel::new("Handles".to_string())
|
||||
.disabled(!self.overlays_visibility_settings.anchors)
|
||||
.for_checkbox(checkbox_id)
|
||||
.widget_instance(),
|
||||
]
|
||||
}),
|
||||
]))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
@@ -2484,16 +2446,12 @@ impl DocumentMessageHandler {
|
||||
PopoverButton::new()
|
||||
.popover_layout(Layout(
|
||||
[
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Snapping").bold(true).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(SnappingOptions::BoundingBoxes.to_string()).widget_instance()],
|
||||
},
|
||||
LayoutGroup::row(vec![TextLabel::new("Snapping").bold(true).widget_instance()]),
|
||||
LayoutGroup::row(vec![TextLabel::new(SnappingOptions::BoundingBoxes.to_string()).widget_instance()]),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(SNAP_FUNCTIONS_FOR_BOUNDING_BOXES.into_iter().map(|(name, closure, description)| LayoutGroup::Row {
|
||||
widgets: {
|
||||
.chain(SNAP_FUNCTIONS_FOR_BOUNDING_BOXES.into_iter().map(|(name, closure, description)| {
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(*closure(&mut snapping_state))
|
||||
@@ -2510,13 +2468,11 @@ impl DocumentMessageHandler {
|
||||
.widget_instance(),
|
||||
TextLabel::new(name).tooltip_label(name).tooltip_description(description).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
})
|
||||
}))
|
||||
.chain([LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new(SnappingOptions::Paths.to_string()).widget_instance()],
|
||||
}])
|
||||
.chain(SNAP_FUNCTIONS_FOR_PATHS.into_iter().map(|(name, closure, description)| LayoutGroup::Row {
|
||||
widgets: {
|
||||
.chain([LayoutGroup::row(vec![TextLabel::new(SnappingOptions::Paths.to_string()).widget_instance()])])
|
||||
.chain(SNAP_FUNCTIONS_FOR_PATHS.into_iter().map(|(name, closure, description)| {
|
||||
LayoutGroup::row({
|
||||
let checkbox_id = CheckboxId::new();
|
||||
vec![
|
||||
CheckboxInput::new(*closure(&mut snapping_state2))
|
||||
@@ -2533,7 +2489,7 @@ impl DocumentMessageHandler {
|
||||
.widget_instance(),
|
||||
TextLabel::new(name).tooltip_label(name).tooltip_description(description).for_checkbox(checkbox_id).widget_instance(),
|
||||
]
|
||||
},
|
||||
})
|
||||
}))
|
||||
.collect(),
|
||||
))
|
||||
@@ -2630,8 +2586,8 @@ impl DocumentMessageHandler {
|
||||
widgets.extend([
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
TextButton::new("Node Graph")
|
||||
.icon(Some((if self.graph_view_overlay_open { "GraphViewOpen" } else { "GraphViewClosed" }).into()))
|
||||
.hover_icon(Some((if self.graph_view_overlay_open { "GraphViewClosed" } else { "GraphViewOpen" }).into()))
|
||||
.icon(if self.graph_view_overlay_open { "GraphViewOpen" } else { "GraphViewClosed" })
|
||||
.hover_icon(if self.graph_view_overlay_open { "GraphViewClosed" } else { "GraphViewOpen" })
|
||||
.tooltip_label(if self.graph_view_overlay_open { "Hide Node Graph" } else { "Show Node Graph" })
|
||||
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||
@@ -2639,7 +2595,7 @@ impl DocumentMessageHandler {
|
||||
]);
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: Layout(vec![LayoutGroup::Row { widgets }]),
|
||||
layout: Layout(vec![LayoutGroup::row(widgets)]),
|
||||
layout_target: LayoutTarget::DocumentBar,
|
||||
});
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
@@ -2777,25 +2733,25 @@ impl DocumentMessageHandler {
|
||||
.tooltip_label("Fill")
|
||||
.widget_instance(),
|
||||
];
|
||||
let layers_panel_control_bar_left = Layout(vec![LayoutGroup::Row { widgets }]);
|
||||
let layers_panel_control_bar_left = Layout(vec![LayoutGroup::row(widgets)]);
|
||||
|
||||
let widgets = vec![
|
||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||
.hover_icon(if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" })
|
||||
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedLocked))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_instance(),
|
||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||
.hover_icon(if selection_all_visible { "EyeHide" } else { "EyeShow" })
|
||||
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
|
||||
.disabled(!has_selection)
|
||||
.widget_instance(),
|
||||
];
|
||||
let layers_panel_control_bar_right = Layout(vec![LayoutGroup::Row { widgets }]);
|
||||
let layers_panel_control_bar_right = Layout(vec![LayoutGroup::row(widgets)]);
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: layers_panel_control_bar_left,
|
||||
@@ -2821,7 +2777,7 @@ impl DocumentMessageHandler {
|
||||
|
||||
let widgets = vec![
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.icon("Node")
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.tooltip_description("Add an operation to the end of this layer's chain of nodes.")
|
||||
.disabled(!has_selection || has_multiple_selection)
|
||||
@@ -2849,7 +2805,7 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
})
|
||||
.widget_instance();
|
||||
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
|
||||
Layout(vec![LayoutGroup::row(vec![node_chooser])])
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
@@ -2875,7 +2831,7 @@ impl DocumentMessageHandler {
|
||||
.widget_instance(),
|
||||
];
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: Layout(vec![LayoutGroup::Row { widgets }]),
|
||||
layout: Layout(vec![LayoutGroup::row(widgets)]),
|
||||
layout_target: LayoutTarget::LayersPanelBottomBar,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ impl NodePropertiesContext<'_> {
|
||||
/// The key used to access definitions for a network node or proto node.
|
||||
/// For proto nodes, this is their [`ProtoNodeIdentifier`].
|
||||
/// For network nodes, it doesn't necessarily have to be the same as the network's display name, but it often is.
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type", content = "data")]
|
||||
pub enum DefinitionIdentifier {
|
||||
ProtoNode(ProtoNodeIdentifier),
|
||||
@@ -2191,9 +2192,10 @@ fn static_input_properties() -> InputProperties {
|
||||
true
|
||||
});
|
||||
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
widgets: node_properties::number_widget(ParameterWidgetsInfo::new(node_id, index, blank_assist, context), number_input),
|
||||
}])
|
||||
Ok(vec![LayoutGroup::row(node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, blank_assist, context),
|
||||
number_input,
|
||||
))])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2227,10 +2229,12 @@ fn static_input_properties() -> InputProperties {
|
||||
number_input = number_input.step(number_step);
|
||||
}
|
||||
};
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
// NOTE: The bool input MUST be at the input index directly before the f64 input!
|
||||
widgets: node_properties::optional_f64_widget(ParameterWidgetsInfo::new(node_id, index, false, context), index - 1, number_input),
|
||||
}])
|
||||
// NOTE: The bool input MUST be at the input index directly before the f64 input!
|
||||
Ok(vec![LayoutGroup::row(node_properties::optional_f64_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, false, context),
|
||||
index - 1,
|
||||
number_input,
|
||||
))])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2298,7 +2302,7 @@ fn static_input_properties() -> InputProperties {
|
||||
"noise_properties_noise_type".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let noise_type_row = enum_choice::<NoiseType>().for_socket(ParameterWidgetsInfo::new(node_id, index, true, context)).property_row();
|
||||
Ok(vec![noise_type_row, LayoutGroup::Row { widgets: Vec::new() }])
|
||||
Ok(vec![noise_type_row, LayoutGroup::row(Vec::new())])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2320,7 +2324,7 @@ fn static_input_properties() -> InputProperties {
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
NumberInput::default().min(0.).disabled(!coherent_noise_active || !domain_warp_active),
|
||||
);
|
||||
Ok(vec![domain_warp_amplitude.into(), LayoutGroup::Row { widgets: Vec::new() }])
|
||||
Ok(vec![domain_warp_amplitude.into(), LayoutGroup::row(Vec::new())])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2408,7 +2412,7 @@ fn static_input_properties() -> InputProperties {
|
||||
.range_max(Some(10.))
|
||||
.disabled(!ping_pong_active || !coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||
);
|
||||
Ok(vec![fractal_ping_pong_strength.into(), LayoutGroup::Row { widgets: Vec::new() }])
|
||||
Ok(vec![fractal_ping_pong_strength.into(), LayoutGroup::row(Vec::new())])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
@@ -2504,7 +2508,7 @@ fn static_input_properties() -> InputProperties {
|
||||
]);
|
||||
}
|
||||
|
||||
Ok(vec![LayoutGroup::Row { widgets }])
|
||||
Ok(vec![LayoutGroup::row(widgets)])
|
||||
}),
|
||||
);
|
||||
// Skew has a custom override that maps to degrees
|
||||
@@ -2548,24 +2552,20 @@ fn static_input_properties() -> InputProperties {
|
||||
]);
|
||||
}
|
||||
|
||||
Ok(vec![LayoutGroup::Row { widgets }])
|
||||
Ok(vec![LayoutGroup::row(widgets)])
|
||||
}),
|
||||
);
|
||||
map.insert(
|
||||
"text_area".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
widgets: node_properties::text_area_widget(ParameterWidgetsInfo::new(node_id, index, true, context)),
|
||||
}])
|
||||
}),
|
||||
Box::new(|node_id, index, context| Ok(vec![LayoutGroup::row(node_properties::text_area_widget(ParameterWidgetsInfo::new(node_id, index, true, context)))])),
|
||||
);
|
||||
map.insert(
|
||||
"text_font".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let (font, style) = node_properties::font_inputs(ParameterWidgetsInfo::new(node_id, index, true, context));
|
||||
let mut result = vec![LayoutGroup::Row { widgets: font }];
|
||||
let mut result = vec![LayoutGroup::row(font)];
|
||||
if let Some(style) = style {
|
||||
result.push(LayoutGroup::Row { widgets: style });
|
||||
result.push(LayoutGroup::row(style));
|
||||
}
|
||||
Ok(result)
|
||||
}),
|
||||
|
||||
@@ -847,7 +847,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
};
|
||||
|
||||
self.context_menu = Some(ContextMenuInformation {
|
||||
context_menu_coordinates: (node_graph_point + node_graph_shift).as_ivec2(),
|
||||
context_menu_coordinates: (node_graph_point + node_graph_shift).as_ivec2().into(),
|
||||
context_menu_data,
|
||||
});
|
||||
|
||||
@@ -1280,7 +1280,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
let compatible_type = network_interface.output_type(&output_connector, selection_network_path).add_node_string();
|
||||
|
||||
self.context_menu = Some(ContextMenuInformation {
|
||||
context_menu_coordinates: (point + node_graph_shift).as_ivec2(),
|
||||
context_menu_coordinates: (point + node_graph_shift).as_ivec2().into(),
|
||||
context_menu_data: ContextMenuData::CreateNode { compatible_type },
|
||||
});
|
||||
|
||||
@@ -2050,8 +2050,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
responses.add(FrontendMessage::UpdateImportsExports {
|
||||
imports,
|
||||
exports,
|
||||
import_position,
|
||||
export_position,
|
||||
import_position: import_position.into(),
|
||||
export_position: export_position.into(),
|
||||
add_import_export,
|
||||
});
|
||||
}
|
||||
@@ -2181,7 +2181,7 @@ impl NodeGraphMessageHandler {
|
||||
|
||||
let mut widgets = vec![
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.icon("Node")
|
||||
.tooltip_label("New Node")
|
||||
.tooltip_description("To add a node at the pointer location, perform the shortcut in an open area of the graph.")
|
||||
.tooltip_shortcut(action_shortcut_manual!(Key::MouseRight))
|
||||
@@ -2222,7 +2222,7 @@ impl NodeGraphMessageHandler {
|
||||
}
|
||||
})
|
||||
.widget_instance();
|
||||
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
|
||||
Layout(vec![LayoutGroup::row(vec![node_chooser])])
|
||||
})
|
||||
.widget_instance(),
|
||||
//
|
||||
@@ -2252,14 +2252,14 @@ impl NodeGraphMessageHandler {
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
//
|
||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||
.hover_icon(if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" })
|
||||
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||
.tooltip_shortcut(action_shortcut!(NodeGraphMessageDiscriminant::ToggleSelectedLocked))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||
.disabled(!has_selection || !selection_includes_layers)
|
||||
.widget_instance(),
|
||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||
.hover_icon(if selection_all_visible { "EyeHide" } else { "EyeShow" })
|
||||
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||
.tooltip_shortcut(action_shortcut!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedVisibility.into())
|
||||
@@ -2286,7 +2286,7 @@ impl NodeGraphMessageHandler {
|
||||
// If only one node is selected then show the preview or stop previewing button
|
||||
if let Some(node_id) = previewing {
|
||||
let button = TextButton::new("End Preview")
|
||||
.icon(Some("FrameAll".to_string()))
|
||||
.icon("FrameAll")
|
||||
.tooltip_description("Restore preview to the graph output.")
|
||||
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||
.widget_instance();
|
||||
@@ -2298,7 +2298,7 @@ impl NodeGraphMessageHandler {
|
||||
.any(|export| matches!(export, NodeInput::Node { node_id: export_node_id, .. } if *export_node_id == node_id));
|
||||
if selection_is_not_already_the_output && no_other_selections {
|
||||
let button = TextButton::new("Preview")
|
||||
.icon(Some("FrameAll".to_string()))
|
||||
.icon("FrameAll")
|
||||
.tooltip_label("Preview")
|
||||
.tooltip_description("Temporarily set the graph output to the selected node or layer. Perform the shortcut on a node or layer for quick access.")
|
||||
.tooltip_shortcut(action_shortcut_manual!(Key::Alt, Key::MouseLeft))
|
||||
@@ -2323,7 +2323,7 @@ impl NodeGraphMessageHandler {
|
||||
]);
|
||||
}
|
||||
|
||||
self.widgets[0] = LayoutGroup::Row { widgets };
|
||||
self.widgets[0] = LayoutGroup::row(widgets);
|
||||
}
|
||||
|
||||
fn update_graph_bar_right(
|
||||
@@ -2357,15 +2357,15 @@ impl NodeGraphMessageHandler {
|
||||
widgets.extend([
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
TextButton::new("Node Graph")
|
||||
.icon(Some("GraphViewOpen".into()))
|
||||
.hover_icon(Some("GraphViewClosed".into()))
|
||||
.icon("GraphViewOpen")
|
||||
.hover_icon("GraphViewClosed")
|
||||
.tooltip_label("Hide Node Graph")
|
||||
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||
.widget_instance(),
|
||||
]);
|
||||
|
||||
self.widgets[1] = LayoutGroup::Row { widgets };
|
||||
self.widgets[1] = LayoutGroup::row(widgets);
|
||||
}
|
||||
|
||||
/// Collate the properties panel sections for a node graph
|
||||
@@ -2407,25 +2407,23 @@ impl NodeGraphMessageHandler {
|
||||
let mut properties = Vec::new();
|
||||
|
||||
if let [node_id] = *nodes.as_slice() {
|
||||
properties.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("Node").tooltip_description("Name of the selected node.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.network_interface.display_name(&node_id, context.selection_network_path))
|
||||
.tooltip_description("Name of the selected node.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id,
|
||||
alias: text_input.value.clone(),
|
||||
skip_adding_history_step: false,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
],
|
||||
});
|
||||
properties.push(LayoutGroup::row(vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("Node").tooltip_description("Name of the selected node.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.network_interface.display_name(&node_id, context.selection_network_path))
|
||||
.tooltip_description("Name of the selected node.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id,
|
||||
alias: text_input.value.clone(),
|
||||
skip_adding_history_step: false,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
]));
|
||||
}
|
||||
|
||||
properties.extend(selected_nodes);
|
||||
@@ -2435,18 +2433,16 @@ impl NodeGraphMessageHandler {
|
||||
|
||||
// TODO: Display properties for encapsulating node when no nodes are selected in a nested network
|
||||
// This may require store a separate path for the properties panel
|
||||
let mut properties = vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("File").tooltip_description("Name of the current document.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.document_name)
|
||||
.tooltip_description("Name of the current document.")
|
||||
.on_update(|text_input| DocumentMessage::RenameDocument { new_name: text_input.value.clone() }.into())
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
],
|
||||
}];
|
||||
let mut properties = vec![LayoutGroup::row(vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("File").tooltip_description("Name of the current document.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.document_name)
|
||||
.tooltip_description("Name of the current document.")
|
||||
.on_update(|text_input| DocumentMessage::RenameDocument { new_name: text_input.value.clone() }.into())
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
])];
|
||||
|
||||
let Some(network) = context.network_interface.nested_network(context.selection_network_path) else {
|
||||
warn!("No network in collate_properties");
|
||||
@@ -2482,50 +2478,48 @@ impl NodeGraphMessageHandler {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut layer_properties = vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("Layer").tooltip_description("Name of the selected layer.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.network_interface.display_name(&layer, context.selection_network_path))
|
||||
.tooltip_description("Name of the selected layer.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id: layer,
|
||||
alias: text_input.value.clone(),
|
||||
skip_adding_history_step: false,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.tooltip_description("Add an operation to the end of this layer's chain of nodes.")
|
||||
.popover_layout({
|
||||
let compatible_type = context
|
||||
.network_interface
|
||||
.upstream_output_connector(&InputConnector::node(layer, 1), &[])
|
||||
.and_then(|upstream_output| context.network_interface.output_type(&upstream_output, &[]).add_node_string());
|
||||
let mut layer_properties = vec![LayoutGroup::row(vec![
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
IconLabel::new("Layer").tooltip_description("Name of the selected layer.").widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
TextInput::new(context.network_interface.display_name(&layer, context.selection_network_path))
|
||||
.tooltip_description("Name of the selected layer.")
|
||||
.on_update(move |text_input| {
|
||||
NodeGraphMessage::SetDisplayName {
|
||||
node_id: layer,
|
||||
alias: text_input.value.clone(),
|
||||
skip_adding_history_step: false,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
PopoverButton::new()
|
||||
.icon("Node")
|
||||
.tooltip_description("Add an operation to the end of this layer's chain of nodes.")
|
||||
.popover_layout({
|
||||
let compatible_type = context
|
||||
.network_interface
|
||||
.upstream_output_connector(&InputConnector::node(layer, 1), &[])
|
||||
.and_then(|upstream_output| context.network_interface.output_type(&upstream_output, &[]).add_node_string());
|
||||
|
||||
let mut node_chooser = NodeCatalog::new();
|
||||
node_chooser.intial_search = compatible_type.unwrap_or("".to_string());
|
||||
let mut node_chooser = NodeCatalog::new();
|
||||
node_chooser.intial_search = compatible_type.unwrap_or("".to_string());
|
||||
|
||||
let node_chooser = node_chooser
|
||||
.on_update(move |node_type| {
|
||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||
node_type: node_type.clone(),
|
||||
layer: LayerNodeIdentifier::new_unchecked(layer),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance();
|
||||
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
],
|
||||
}];
|
||||
let node_chooser = node_chooser
|
||||
.on_update(move |node_type| {
|
||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||
node_type: node_type.clone(),
|
||||
layer: LayerNodeIdentifier::new_unchecked(layer),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance();
|
||||
Layout(vec![LayoutGroup::row(vec![node_chooser])])
|
||||
})
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
])];
|
||||
|
||||
// Iterate through all the upstream nodes, but stop when we reach another layer (since that's a point where we switch from horizontal to vertical flow)
|
||||
let node_properties = context
|
||||
@@ -2651,7 +2645,7 @@ impl NodeGraphMessageHandler {
|
||||
exposed_outputs,
|
||||
primary_output_connected_to_layer,
|
||||
primary_input_connected_to_layer,
|
||||
position,
|
||||
position: position.into(),
|
||||
previewed,
|
||||
visible,
|
||||
locked,
|
||||
@@ -2695,6 +2689,7 @@ impl NodeGraphMessageHandler {
|
||||
if network_interface.is_layer(&error_node, breadcrumb_network_path) {
|
||||
position += IVec2::new(12, -12)
|
||||
}
|
||||
let position = position.into();
|
||||
|
||||
Some(NodeGraphErrorDiagnostic { position, error })
|
||||
}
|
||||
@@ -2841,7 +2836,7 @@ impl Default for NodeGraphMessageHandler {
|
||||
Self {
|
||||
network: Vec::new(),
|
||||
has_selection: false,
|
||||
widgets: [LayoutGroup::Row { widgets: Vec::new() }, LayoutGroup::Row { widgets: Vec::new() }],
|
||||
widgets: [LayoutGroup::row(Vec::new()), LayoutGroup::row(Vec::new())],
|
||||
drag_start: None,
|
||||
begin_dragging: false,
|
||||
node_has_moved_in_drag: false,
|
||||
|
||||
@@ -31,7 +31,7 @@ use graphene_std::vector::style::{Fill, FillChoice, FillType, GradientStops, Gra
|
||||
|
||||
pub(crate) fn string_properties(text: &str) -> Vec<LayoutGroup> {
|
||||
let widget = TextLabel::new(text).widget_instance();
|
||||
vec![LayoutGroup::Row { widgets: vec![widget] }]
|
||||
vec![LayoutGroup::row(vec![widget])]
|
||||
}
|
||||
|
||||
fn optionally_update_value<T>(value: impl Fn(&T) -> Option<TaggedValue> + 'static + Send + Sync, node_id: NodeId, input_index: usize) -> impl Fn(&T) -> Message + 'static + Send + Sync {
|
||||
@@ -538,11 +538,7 @@ pub fn footprint_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widg
|
||||
);
|
||||
}
|
||||
|
||||
let widgets = [
|
||||
LayoutGroup::Row { widgets: location_widgets },
|
||||
LayoutGroup::Row { widgets: scale_widgets },
|
||||
LayoutGroup::Row { widgets: resolution_widgets },
|
||||
];
|
||||
let widgets = [LayoutGroup::row(location_widgets), LayoutGroup::row(scale_widgets), LayoutGroup::row(resolution_widgets)];
|
||||
let (last, rest) = widgets.split_last().expect("Footprint widget should return multiple rows");
|
||||
*extra_widgets = rest.to_vec();
|
||||
last.clone()
|
||||
@@ -651,13 +647,9 @@ pub fn transform_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widg
|
||||
.widget_instance(),
|
||||
]);
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: location_widgets },
|
||||
LayoutGroup::Row { widgets: rotation_widgets },
|
||||
LayoutGroup::Row { widgets: scale_widgets },
|
||||
]
|
||||
vec![LayoutGroup::row(location_widgets), LayoutGroup::row(rotation_widgets), LayoutGroup::row(scale_widgets)]
|
||||
} else {
|
||||
vec![LayoutGroup::Row { widgets: location_widgets }]
|
||||
vec![LayoutGroup::row(location_widgets)]
|
||||
};
|
||||
|
||||
if let Some((last, rest)) = widgets.split_last() {
|
||||
@@ -676,7 +668,7 @@ pub fn vec2_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str, y: &st
|
||||
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
return LayoutGroup::row(vec![]);
|
||||
};
|
||||
match input.as_non_exposed_value() {
|
||||
Some(&TaggedValue::DVec2(dvec2)) => {
|
||||
@@ -730,7 +722,7 @@ pub fn vec2_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str, y: &st
|
||||
_ => {}
|
||||
}
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
LayoutGroup::row(widgets)
|
||||
}
|
||||
|
||||
pub fn array_of_number_widget(parameter_widgets_info: ParameterWidgetsInfo, text_input: TextInput) -> Vec<WidgetInstance> {
|
||||
@@ -1101,7 +1093,7 @@ pub fn blend_mode_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Layout
|
||||
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
return LayoutGroup::row(vec![]);
|
||||
};
|
||||
if let Some(&TaggedValue::BlendMode(blend_mode)) = input.as_non_exposed_value() {
|
||||
let entries = BlendMode::list_svg_subset()
|
||||
@@ -1126,7 +1118,7 @@ pub fn blend_mode_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Layout
|
||||
.widget_instance(),
|
||||
]);
|
||||
}
|
||||
LayoutGroup::Row { widgets }.with_tooltip_description("Formula used for blending.")
|
||||
LayoutGroup::row(widgets).with_tooltip_description("Formula used for blending.")
|
||||
}
|
||||
|
||||
pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button: ColorInput) -> LayoutGroup {
|
||||
@@ -1137,7 +1129,7 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||
// Return early with just the label if the input is exposed to the graph, meaning we don't want to show the color picker widget in the Properties panel
|
||||
let NodeInput::Value { tagged_value, exposed: false } = &document_node.inputs[index] else {
|
||||
return LayoutGroup::Row { widgets };
|
||||
return LayoutGroup::row(widgets);
|
||||
};
|
||||
|
||||
// Add a separator
|
||||
@@ -1176,7 +1168,7 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
x => warn!("Color {x:?}"),
|
||||
}
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
LayoutGroup::row(widgets)
|
||||
}
|
||||
|
||||
pub fn font_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup {
|
||||
@@ -1192,7 +1184,7 @@ pub fn curve_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup
|
||||
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
return LayoutGroup::row(vec![]);
|
||||
};
|
||||
if let Some(TaggedValue::Curve(curve)) = &input.as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
@@ -1203,7 +1195,7 @@ pub fn curve_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup
|
||||
.widget_instance(),
|
||||
])
|
||||
}
|
||||
LayoutGroup::Row { widgets }
|
||||
LayoutGroup::row(widgets)
|
||||
}
|
||||
|
||||
pub fn get_document_node<'a>(node_id: NodeId, context: &'a NodePropertiesContext<'a>) -> Result<&'a DocumentNode, String> {
|
||||
@@ -1306,10 +1298,10 @@ pub(crate) fn brightness_contrast_properties(node_id: NodeId, context: &mut Node
|
||||
.range_max(Some(100.)),
|
||||
);
|
||||
|
||||
let mut layout = vec![LayoutGroup::Row { widgets: brightness }, LayoutGroup::Row { widgets: contrast }];
|
||||
let mut layout = vec![LayoutGroup::row(brightness), LayoutGroup::row(contrast)];
|
||||
if includes_use_classic {
|
||||
// TODO: When we no longer use this function in the temporary "Brightness/Contrast Classic" node, remove this conditional pushing and just always include this
|
||||
layout.push(LayoutGroup::Row { widgets: use_classic });
|
||||
layout.push(LayoutGroup::row(use_classic));
|
||||
}
|
||||
|
||||
layout
|
||||
@@ -1358,18 +1350,13 @@ pub(crate) fn channel_mixer_properties(node_id: NodeId, context: &mut NodeProper
|
||||
let constant = number_widget(ParameterWidgetsInfo::new(node_id, constant_output_index, true, context), number_input);
|
||||
|
||||
// Monochrome
|
||||
let mut layout = vec![LayoutGroup::Row { widgets: is_monochrome }];
|
||||
let mut layout = vec![LayoutGroup::row(is_monochrome)];
|
||||
// Output channel choice
|
||||
if !is_monochrome_value {
|
||||
layout.push(output_channel);
|
||||
}
|
||||
// Channel values
|
||||
layout.extend([
|
||||
LayoutGroup::Row { widgets: red },
|
||||
LayoutGroup::Row { widgets: green },
|
||||
LayoutGroup::Row { widgets: blue },
|
||||
LayoutGroup::Row { widgets: constant },
|
||||
]);
|
||||
layout.extend([LayoutGroup::row(red), LayoutGroup::row(green), LayoutGroup::row(blue), LayoutGroup::row(constant)]);
|
||||
layout
|
||||
}
|
||||
|
||||
@@ -1422,10 +1409,10 @@ pub(crate) fn selective_color_properties(node_id: NodeId, context: &mut NodeProp
|
||||
// Colors choice
|
||||
colors,
|
||||
// CMYK
|
||||
LayoutGroup::Row { widgets: cyan },
|
||||
LayoutGroup::Row { widgets: magenta },
|
||||
LayoutGroup::Row { widgets: yellow },
|
||||
LayoutGroup::Row { widgets: black },
|
||||
LayoutGroup::row(cyan),
|
||||
LayoutGroup::row(magenta),
|
||||
LayoutGroup::row(yellow),
|
||||
LayoutGroup::row(black),
|
||||
// Mode
|
||||
mode,
|
||||
]
|
||||
@@ -1458,12 +1445,10 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
widgets.push(spacing);
|
||||
}
|
||||
GridType::Isometric => {
|
||||
let spacing = LayoutGroup::Row {
|
||||
widgets: number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, SpacingInput::<f64>::INDEX, true, context),
|
||||
NumberInput::default().label("H").min(0.).unit(" px"),
|
||||
),
|
||||
};
|
||||
let spacing = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, SpacingInput::<f64>::INDEX, true, context),
|
||||
NumberInput::default().label("H").min(0.).unit(" px"),
|
||||
));
|
||||
let angles = vec2_widget(ParameterWidgetsInfo::new(node_id, AnglesInput::INDEX, true, context), "", "", "°", None, false);
|
||||
widgets.extend([spacing, angles]);
|
||||
}
|
||||
@@ -1473,7 +1458,7 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
let columns = number_widget(ParameterWidgetsInfo::new(node_id, ColumnsInput::INDEX, true, context), NumberInput::default().min(1.));
|
||||
let rows = number_widget(ParameterWidgetsInfo::new(node_id, RowsInput::INDEX, true, context), NumberInput::default().min(1.));
|
||||
|
||||
widgets.extend([LayoutGroup::Row { widgets: columns }, LayoutGroup::Row { widgets: rows }]);
|
||||
widgets.extend([LayoutGroup::row(columns), LayoutGroup::row(rows)]);
|
||||
|
||||
widgets
|
||||
}
|
||||
@@ -1487,7 +1472,7 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon
|
||||
let turns = number_widget(ParameterWidgetsInfo::new(node_id, TurnsInput::INDEX, true, context), NumberInput::default().min(0.1));
|
||||
let start_angle = number_widget(ParameterWidgetsInfo::new(node_id, StartAngleInput::INDEX, true, context), NumberInput::default().unit("°"));
|
||||
|
||||
let mut widgets = vec![spiral_type, LayoutGroup::Row { widgets: turns }, LayoutGroup::Row { widgets: start_angle }];
|
||||
let mut widgets = vec![spiral_type, LayoutGroup::row(turns), LayoutGroup::row(start_angle)];
|
||||
|
||||
let document_node = match get_document_node(node_id, context) {
|
||||
Ok(document_node) => document_node,
|
||||
@@ -1504,24 +1489,28 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon
|
||||
if let Some(&TaggedValue::SpiralType(spiral_type)) = spiral_type_input.as_non_exposed_value() {
|
||||
match spiral_type {
|
||||
SpiralType::Archimedean => {
|
||||
let inner_radius = LayoutGroup::Row {
|
||||
widgets: number_widget(ParameterWidgetsInfo::new(node_id, InnerRadiusInput::INDEX, true, context), NumberInput::default().min(0.).unit(" px")),
|
||||
};
|
||||
let inner_radius = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, InnerRadiusInput::INDEX, true, context),
|
||||
NumberInput::default().min(0.).unit(" px"),
|
||||
));
|
||||
|
||||
let outer_radius = LayoutGroup::Row {
|
||||
widgets: number_widget(ParameterWidgetsInfo::new(node_id, OuterRadiusInput::INDEX, true, context), NumberInput::default().unit(" px")),
|
||||
};
|
||||
let outer_radius = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, OuterRadiusInput::INDEX, true, context),
|
||||
NumberInput::default().unit(" px"),
|
||||
));
|
||||
|
||||
widgets.extend([inner_radius, outer_radius]);
|
||||
}
|
||||
SpiralType::Logarithmic => {
|
||||
let inner_radius = LayoutGroup::Row {
|
||||
widgets: number_widget(ParameterWidgetsInfo::new(node_id, InnerRadiusInput::INDEX, true, context), NumberInput::default().min(0.).unit(" px")),
|
||||
};
|
||||
let inner_radius = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, InnerRadiusInput::INDEX, true, context),
|
||||
NumberInput::default().min(0.).unit(" px"),
|
||||
));
|
||||
|
||||
let outer_radius = LayoutGroup::Row {
|
||||
widgets: number_widget(ParameterWidgetsInfo::new(node_id, OuterRadiusInput::INDEX, true, context), NumberInput::default().min(0.1).unit(" px")),
|
||||
};
|
||||
let outer_radius = LayoutGroup::row(number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, OuterRadiusInput::INDEX, true, context),
|
||||
NumberInput::default().min(0.1).unit(" px"),
|
||||
));
|
||||
|
||||
widgets.extend([inner_radius, outer_radius]);
|
||||
}
|
||||
@@ -1533,7 +1522,7 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon
|
||||
NumberInput::default().min(1.).max(180.).unit("°"),
|
||||
);
|
||||
|
||||
widgets.push(LayoutGroup::Row { widgets: angular_resolution });
|
||||
widgets.push(LayoutGroup::row(angular_resolution));
|
||||
|
||||
widgets
|
||||
}
|
||||
@@ -1574,13 +1563,13 @@ pub(crate) fn sample_polyline_properties(node_id: NodeId, context: &mut NodeProp
|
||||
vec![
|
||||
spacing.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_SPACING),
|
||||
match current_spacing {
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Separation)) => LayoutGroup::Row { widgets: separation }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_SEPARATION),
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Quantity)) => LayoutGroup::Row { widgets: quantity }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_QUANTITY),
|
||||
_ => LayoutGroup::Row { widgets: vec![] },
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Separation)) => LayoutGroup::row(separation).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_SEPARATION),
|
||||
Some(TaggedValue::PointSpacingType(PointSpacingType::Quantity)) => LayoutGroup::row(quantity).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_QUANTITY),
|
||||
_ => LayoutGroup::row(vec![]),
|
||||
},
|
||||
LayoutGroup::Row { widgets: start_offset }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_START_OFFSET),
|
||||
LayoutGroup::Row { widgets: stop_offset }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_STOP_OFFSET),
|
||||
LayoutGroup::Row { widgets: adaptive_spacing }.with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_ADAPTIVE_SPACING),
|
||||
LayoutGroup::row(start_offset).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_START_OFFSET),
|
||||
LayoutGroup::row(stop_offset).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_STOP_OFFSET),
|
||||
LayoutGroup::row(adaptive_spacing).with_tooltip_description(SAMPLE_POLYLINE_DESCRIPTION_ADAPTIVE_SPACING),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1594,11 +1583,7 @@ pub(crate) fn exposure_properties(node_id: NodeId, context: &mut NodePropertiesC
|
||||
NumberInput::default().min(0.01).max(9.99).increment_step(0.1),
|
||||
);
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: exposure },
|
||||
LayoutGroup::Row { widgets: offset },
|
||||
LayoutGroup::Row { widgets: gamma_correction },
|
||||
]
|
||||
vec![LayoutGroup::row(exposure), LayoutGroup::row(offset), LayoutGroup::row(gamma_correction)]
|
||||
}
|
||||
|
||||
pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
@@ -1722,11 +1707,11 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
||||
let clamped = bool_widget(ParameterWidgetsInfo::new(node_id, ClampedInput::INDEX, true, context), CheckboxInput::default());
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: size_x },
|
||||
LayoutGroup::Row { widgets: size_y },
|
||||
LayoutGroup::Row { widgets: corner_radius_row_1 },
|
||||
LayoutGroup::Row { widgets: corner_radius_row_2 },
|
||||
LayoutGroup::Row { widgets: clamped },
|
||||
LayoutGroup::row(size_x),
|
||||
LayoutGroup::row(size_y),
|
||||
LayoutGroup::row(corner_radius_row_1),
|
||||
LayoutGroup::row(corner_radius_row_2),
|
||||
LayoutGroup::row(clamped),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1825,14 +1810,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
|
||||
let visible = context.network_interface.is_visible(&node_id, context.selection_network_path);
|
||||
let pinned = context.network_interface.is_pinned(&node_id, context.selection_network_path);
|
||||
|
||||
LayoutGroup::Section {
|
||||
name,
|
||||
description,
|
||||
visible,
|
||||
pinned,
|
||||
id: node_id.0,
|
||||
layout: Layout(layout),
|
||||
}
|
||||
LayoutGroup::section(name, description, visible, pinned, node_id.0, Layout(layout))
|
||||
}
|
||||
|
||||
/// Fill Node Widgets LayoutGroup
|
||||
@@ -1856,7 +1834,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
) {
|
||||
(fill, backup_color, backup_gradient)
|
||||
} else {
|
||||
return vec![LayoutGroup::Row { widgets: widgets_first_row }];
|
||||
return vec![LayoutGroup::row(widgets_first_row)];
|
||||
};
|
||||
let fill2 = fill.clone();
|
||||
let backup_color_fill: Fill = backup_color.clone().into();
|
||||
@@ -1899,7 +1877,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
.on_commit(commit_value)
|
||||
.widget_instance(),
|
||||
);
|
||||
let mut widgets = vec![LayoutGroup::Row { widgets: widgets_first_row }];
|
||||
let mut widgets = vec![LayoutGroup::row(widgets_first_row)];
|
||||
|
||||
let fill_type_switch = {
|
||||
let mut row = vec![TextLabel::new("").widget_instance()];
|
||||
@@ -1942,7 +1920,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
RadioInput::new(entries).selected_index(Some(if fill.as_gradient().is_some() { 1 } else { 0 })).widget_instance(),
|
||||
]);
|
||||
|
||||
LayoutGroup::Row { widgets: row }
|
||||
LayoutGroup::row(row)
|
||||
};
|
||||
widgets.push(fill_type_switch);
|
||||
|
||||
@@ -2011,7 +1989,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
RadioInput::new(entries).selected_index(Some(gradient.gradient_type as u32)).widget_instance(),
|
||||
]);
|
||||
|
||||
widgets.push(LayoutGroup::Row { widgets: row });
|
||||
widgets.push(LayoutGroup::row(row));
|
||||
}
|
||||
|
||||
widgets
|
||||
@@ -2069,14 +2047,14 @@ pub fn stroke_properties(node_id: NodeId, context: &mut NodePropertiesContext) -
|
||||
|
||||
vec![
|
||||
color,
|
||||
LayoutGroup::Row { widgets: weight },
|
||||
LayoutGroup::row(weight),
|
||||
align,
|
||||
cap,
|
||||
join,
|
||||
LayoutGroup::Row { widgets: miter_limit },
|
||||
LayoutGroup::row(miter_limit),
|
||||
paint_order,
|
||||
LayoutGroup::Row { widgets: dash_lengths },
|
||||
LayoutGroup::Row { widgets: dash_offset },
|
||||
LayoutGroup::row(dash_lengths),
|
||||
LayoutGroup::row(dash_offset),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2106,7 +2084,7 @@ pub fn offset_path_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
});
|
||||
let miter_limit = number_widget(ParameterWidgetsInfo::new(node_id, MiterLimitInput::INDEX, true, context), number_input);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: distance }, join, LayoutGroup::Row { widgets: miter_limit }]
|
||||
vec![LayoutGroup::row(distance), join, LayoutGroup::row(miter_limit)]
|
||||
}
|
||||
|
||||
pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
@@ -2158,9 +2136,9 @@ pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) ->
|
||||
let operand_a_hint = vec![TextLabel::new("(Operand A is the primary input)").widget_instance()];
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: expression }.with_tooltip_description(r#"A math expression that may incorporate "A" and/or "B", such as "sqrt(A + B) - B^2"."#),
|
||||
LayoutGroup::Row { widgets: operand_b }.with_tooltip_description(r#"The value of "B" when calculating the expression."#),
|
||||
LayoutGroup::Row { widgets: operand_a_hint }.with_tooltip_description(r#""A" is fed by the value from the previous node in the primary data flow, or it is 0 if disconnected."#),
|
||||
LayoutGroup::row(expression).with_tooltip_description(r#"A math expression that may incorporate "A" and/or "B", such as "sqrt(A + B) - B^2"."#),
|
||||
LayoutGroup::row(operand_b).with_tooltip_description(r#"The value of "B" when calculating the expression."#),
|
||||
LayoutGroup::row(operand_a_hint).with_tooltip_description(r#""A" is fed by the value from the previous node in the primary data flow, or it is 0 if disconnected."#),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2348,14 +2326,14 @@ pub mod choice {
|
||||
let ParameterWidgetsInfo { document_node, node_id, index, .. } = self.parameter_info;
|
||||
let Some(document_node) = document_node else {
|
||||
log::error!("Could not get document node when building property row for node {node_id:?}");
|
||||
return LayoutGroup::Row { widgets: Vec::new() };
|
||||
return LayoutGroup::row(Vec::new());
|
||||
};
|
||||
|
||||
let mut widgets = super::start_widgets(self.parameter_info);
|
||||
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
return LayoutGroup::row(vec![]);
|
||||
};
|
||||
|
||||
let input: Option<W::Value> = input.as_non_exposed_value().and_then(|v| <&W::Value as TryFrom<&TaggedValue>>::try_from(v).ok()).cloned();
|
||||
@@ -2367,7 +2345,7 @@ pub mod choice {
|
||||
widgets.extend_from_slice(&[Separator::new(SeparatorStyle::Unrelated).widget_instance(), widget]);
|
||||
}
|
||||
|
||||
let mut row = LayoutGroup::Row { widgets };
|
||||
let mut row = LayoutGroup::row(widgets);
|
||||
if let Some(desc) = self.widget_factory.description() {
|
||||
row = row.with_tooltip_description(desc);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use glam::IVec2;
|
||||
use graph_craft::document::NodeId;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::Type;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum FrontendGraphDataType {
|
||||
#[default]
|
||||
General,
|
||||
@@ -42,7 +42,8 @@ impl FrontendGraphDataType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendGraphInput {
|
||||
#[serde(rename = "dataType")]
|
||||
pub data_type: FrontendGraphDataType,
|
||||
@@ -57,21 +58,23 @@ pub struct FrontendGraphInput {
|
||||
pub connected_to: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendGraphOutput {
|
||||
#[serde(rename = "dataType")]
|
||||
pub data_type: FrontendGraphDataType,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
#[serde(rename = "resolvedType")]
|
||||
pub resolved_type: String,
|
||||
pub description: String,
|
||||
/// If connected to an export, it is "export index {index}".
|
||||
/// If connected to a node, it is "{node name} input {input_index}".
|
||||
#[serde(rename = "connectedTo")]
|
||||
pub connected_to: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendNode {
|
||||
pub id: graph_craft::document::NodeId,
|
||||
#[serde(rename = "isLayer")]
|
||||
@@ -95,13 +98,14 @@ pub struct FrontendNode {
|
||||
pub primary_input_connected_to_layer: bool,
|
||||
#[serde(rename = "primaryOutputConnectedToLayer")]
|
||||
pub primary_output_connected_to_layer: bool,
|
||||
pub position: IVec2,
|
||||
pub position: (i32, i32),
|
||||
pub previewed: bool,
|
||||
pub visible: bool,
|
||||
pub locked: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendNodeType {
|
||||
pub identifier: String,
|
||||
pub name: String,
|
||||
@@ -110,7 +114,8 @@ pub struct FrontendNodeType {
|
||||
pub input_types: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DragStart {
|
||||
pub start_x: f64,
|
||||
pub start_y: f64,
|
||||
@@ -118,14 +123,8 @@ pub struct DragStart {
|
||||
pub round_y: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub struct Transform {
|
||||
pub scale: f64,
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct BoxSelection {
|
||||
#[serde(rename = "startX")]
|
||||
pub start_x: u32,
|
||||
@@ -137,7 +136,8 @@ pub struct BoxSelection {
|
||||
pub end_y: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type", content = "data")]
|
||||
pub enum ContextMenuData {
|
||||
ModifyNode {
|
||||
@@ -158,22 +158,25 @@ pub enum ContextMenuData {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ContextMenuInformation {
|
||||
// Stores whether the context menu is open and its position in graph coordinates
|
||||
#[serde(rename = "contextMenuCoordinates")]
|
||||
pub context_menu_coordinates: IVec2,
|
||||
pub context_menu_coordinates: (i32, i32),
|
||||
#[serde(rename = "contextMenuData")]
|
||||
pub context_menu_data: ContextMenuData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct NodeGraphErrorDiagnostic {
|
||||
pub position: IVec2,
|
||||
pub position: (i32, i32),
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendClickTargets {
|
||||
#[serde(rename = "nodeClickTargets")]
|
||||
pub node_click_targets: Vec<String>,
|
||||
@@ -189,7 +192,8 @@ pub struct FrontendClickTargets {
|
||||
pub modify_import_export: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
|
||||
@@ -228,42 +228,38 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
||||
})
|
||||
};
|
||||
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![TextLabel::new("Grid").bold(true).widget_instance()],
|
||||
});
|
||||
widgets.push(LayoutGroup::row(vec![TextLabel::new("Grid").bold(true).widget_instance()]));
|
||||
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Type").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
RadioInput::new(vec![
|
||||
RadioEntryData::new("rectangular").label("Rectangular").on_update(update_val(grid, |grid, _| {
|
||||
if let GridType::Isometric { y_axis_spacing, angle_a, angle_b } = grid.grid_type {
|
||||
grid.isometric_y_spacing = y_axis_spacing;
|
||||
grid.isometric_angle_a = angle_a;
|
||||
grid.isometric_angle_b = angle_b;
|
||||
}
|
||||
grid.grid_type = GridType::Rectangular { spacing: grid.rectangular_spacing };
|
||||
})),
|
||||
RadioEntryData::new("isometric").label("Isometric").on_update(update_val(grid, |grid, _| {
|
||||
if let GridType::Rectangular { spacing } = grid.grid_type {
|
||||
grid.rectangular_spacing = spacing;
|
||||
}
|
||||
grid.grid_type = GridType::Isometric {
|
||||
y_axis_spacing: grid.isometric_y_spacing,
|
||||
angle_a: grid.isometric_angle_a,
|
||||
angle_b: grid.isometric_angle_b,
|
||||
};
|
||||
})),
|
||||
])
|
||||
.min_width(200)
|
||||
.selected_index(Some(match grid.grid_type {
|
||||
GridType::Rectangular { .. } => 0,
|
||||
GridType::Isometric { .. } => 1,
|
||||
}))
|
||||
.widget_instance(),
|
||||
],
|
||||
});
|
||||
widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Type").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
RadioInput::new(vec![
|
||||
RadioEntryData::new("rectangular").label("Rectangular").on_update(update_val(grid, |grid, _| {
|
||||
if let GridType::Isometric { y_axis_spacing, angle_a, angle_b } = grid.grid_type {
|
||||
grid.isometric_y_spacing = y_axis_spacing;
|
||||
grid.isometric_angle_a = angle_a;
|
||||
grid.isometric_angle_b = angle_b;
|
||||
}
|
||||
grid.grid_type = GridType::Rectangular { spacing: grid.rectangular_spacing };
|
||||
})),
|
||||
RadioEntryData::new("isometric").label("Isometric").on_update(update_val(grid, |grid, _| {
|
||||
if let GridType::Rectangular { spacing } = grid.grid_type {
|
||||
grid.rectangular_spacing = spacing;
|
||||
}
|
||||
grid.grid_type = GridType::Isometric {
|
||||
y_axis_spacing: grid.isometric_y_spacing,
|
||||
angle_a: grid.isometric_angle_a,
|
||||
angle_b: grid.isometric_angle_b,
|
||||
};
|
||||
})),
|
||||
])
|
||||
.min_width(200)
|
||||
.selected_index(Some(match grid.grid_type {
|
||||
GridType::Rectangular { .. } => 0,
|
||||
GridType::Isometric { .. } => 1,
|
||||
}))
|
||||
.widget_instance(),
|
||||
]));
|
||||
|
||||
let mut color_widgets = vec![
|
||||
TextLabel::new("Display").table_align(true).widget_instance(),
|
||||
@@ -288,80 +284,72 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
||||
}))
|
||||
.widget_instance(),
|
||||
);
|
||||
widgets.push(LayoutGroup::Row { widgets: color_widgets });
|
||||
widgets.push(LayoutGroup::row(color_widgets));
|
||||
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Origin").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(grid.origin.x))
|
||||
.label("X")
|
||||
.unit(" px")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| Some(&mut grid.origin.x)))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(grid.origin.y))
|
||||
.label("Y")
|
||||
.unit(" px")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| Some(&mut grid.origin.y)))
|
||||
.widget_instance(),
|
||||
],
|
||||
});
|
||||
widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Origin").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(grid.origin.x))
|
||||
.label("X")
|
||||
.unit(" px")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| Some(&mut grid.origin.x)))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(grid.origin.y))
|
||||
.label("Y")
|
||||
.unit(" px")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| Some(&mut grid.origin.y)))
|
||||
.widget_instance(),
|
||||
]));
|
||||
|
||||
match grid.grid_type {
|
||||
GridType::Rectangular { spacing } => widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Spacing").table_align(true).widget_instance(),
|
||||
GridType::Rectangular { spacing } => widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Spacing").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(spacing.x))
|
||||
.label("X")
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.rectangular_spacing().map(|spacing| &mut spacing.x)))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(spacing.y))
|
||||
.label("Y")
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.rectangular_spacing().map(|spacing| &mut spacing.y)))
|
||||
.widget_instance(),
|
||||
])),
|
||||
GridType::Isometric { y_axis_spacing, angle_a, angle_b } => {
|
||||
widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Y Spacing").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(spacing.x))
|
||||
.label("X")
|
||||
NumberInput::new(Some(y_axis_spacing))
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.min_width(200)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.isometric_y_spacing()))
|
||||
.widget_instance(),
|
||||
]));
|
||||
widgets.push(LayoutGroup::row(vec![
|
||||
TextLabel::new("Angles").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(angle_a))
|
||||
.unit("°")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.rectangular_spacing().map(|spacing| &mut spacing.x)))
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.angle_a()))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(spacing.y))
|
||||
.label("Y")
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
NumberInput::new(Some(angle_b))
|
||||
.unit("°")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.rectangular_spacing().map(|spacing| &mut spacing.y)))
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.angle_b()))
|
||||
.widget_instance(),
|
||||
],
|
||||
}),
|
||||
GridType::Isometric { y_axis_spacing, angle_a, angle_b } => {
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Y Spacing").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(y_axis_spacing))
|
||||
.unit(" px")
|
||||
.min(0.)
|
||||
.min_width(200)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.isometric_y_spacing()))
|
||||
.widget_instance(),
|
||||
],
|
||||
});
|
||||
widgets.push(LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
TextLabel::new("Angles").table_align(true).widget_instance(),
|
||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||
NumberInput::new(Some(angle_a))
|
||||
.unit("°")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.angle_a()))
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
NumberInput::new(Some(angle_b))
|
||||
.unit("°")
|
||||
.min_width(98)
|
||||
.on_update(update_origin(grid, |grid| grid.grid_type.angle_b()))
|
||||
.widget_instance(),
|
||||
],
|
||||
});
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ pub enum GizmoEmphasis {
|
||||
|
||||
// TODO Remove duplicated definition of this in `utility_types_web.rs`
|
||||
/// Types of overlays used by DocumentMessage to enable/disable the selected set of viewport overlays.
|
||||
#[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 OverlaysType {
|
||||
ArtboardName,
|
||||
CompassRose,
|
||||
@@ -60,7 +61,8 @@ pub enum OverlaysType {
|
||||
}
|
||||
|
||||
// TODO Remove duplicated definition of this in `utility_types_web.rs`
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct OverlaysVisibilitySettings {
|
||||
pub all: bool,
|
||||
@@ -160,11 +162,11 @@ impl OverlaysVisibilitySettings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct OverlayContext {
|
||||
// Serde functionality isn't used but is required by the message system macros
|
||||
#[serde(skip)]
|
||||
#[specta(skip)]
|
||||
internal: Arc<Mutex<OverlayContextInternal>>,
|
||||
pub viewport: ViewportMessageHandler,
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
|
||||
@@ -35,7 +35,8 @@ pub enum GizmoEmphasis {
|
||||
}
|
||||
|
||||
/// Types of overlays used by DocumentMessage to enable/disable the selected set of viewport overlays.
|
||||
#[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 OverlaysType {
|
||||
ArtboardName,
|
||||
CompassRose,
|
||||
@@ -52,7 +53,8 @@ pub enum OverlaysType {
|
||||
Handles,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct OverlaysVisibilitySettings {
|
||||
pub all: bool,
|
||||
@@ -150,11 +152,11 @@ impl OverlaysVisibilitySettings {
|
||||
}
|
||||
}
|
||||
|
||||
#[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 struct OverlayContext {
|
||||
// Serde functionality isn't used but is required by the message system macros
|
||||
#[serde(skip, default = "overlay_canvas_context")]
|
||||
#[specta(skip)]
|
||||
pub render_context: web_sys::CanvasRenderingContext2d,
|
||||
pub viewport: ViewportMessageHandler,
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
|
||||
@@ -2,7 +2,8 @@ use super::network_interface::NodeTemplate;
|
||||
use graph_craft::document::NodeId;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Clipboard {
|
||||
Internal,
|
||||
Device,
|
||||
|
||||
@@ -234,7 +234,8 @@ impl DocumentMetadata {
|
||||
// ===================
|
||||
|
||||
/// ID of a layer node
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LayerNodeIdentifier(NonZeroU64);
|
||||
|
||||
impl core::fmt::Debug for LayerNodeIdentifier {
|
||||
|
||||
@@ -3,7 +3,8 @@ use glam::DVec2;
|
||||
use std::fmt;
|
||||
|
||||
#[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 DocumentId(pub u64);
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash)]
|
||||
@@ -12,13 +13,15 @@ pub enum FlipAxis {
|
||||
Y,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash)]
|
||||
pub enum AlignAxis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash)]
|
||||
pub enum AlignAggregate {
|
||||
Min,
|
||||
Max,
|
||||
|
||||
@@ -5723,14 +5723,16 @@ impl Iterator for FlowIter<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ImportOrExport {
|
||||
Import(usize),
|
||||
Export(usize),
|
||||
}
|
||||
|
||||
/// Represents an input connector with index based on the [`DocumentNode::inputs`] index, not the visible input index
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum InputConnector {
|
||||
#[serde(rename = "node")]
|
||||
Node {
|
||||
@@ -5770,7 +5772,8 @@ impl InputConnector {
|
||||
}
|
||||
|
||||
/// Represents an output connector
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum OutputConnector {
|
||||
#[serde(rename = "node")]
|
||||
Node {
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use super::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::frontend::IconName;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use glam::DVec2;
|
||||
use graph_craft::document::{NodeId, NodeNetwork};
|
||||
|
||||
/// Represents an entry in the layer tree hierarchy, sent to the frontend.
|
||||
/// Each entry contains its layer ID and a list of its visible children.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct LayerStructureEntry {
|
||||
#[serde(rename = "layerId")]
|
||||
pub layer_id: NodeId,
|
||||
pub children: Vec<LayerStructureEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct LayerPanelEntry {
|
||||
pub id: NodeId,
|
||||
#[serde(rename = "implementationName")]
|
||||
pub implementation_name: String,
|
||||
#[serde(rename = "iconName")]
|
||||
pub icon_name: Option<String>,
|
||||
pub icon_name: Option<IconName>,
|
||||
pub alias: String,
|
||||
#[serde(rename = "inSelectedNetwork")]
|
||||
pub in_selected_network: bool,
|
||||
@@ -47,7 +50,8 @@ pub struct LayerPanelEntry {
|
||||
}
|
||||
|
||||
/// IMPORTANT: the same node may appear multiple times.
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct SelectedNodes(pub Vec<NodeId>);
|
||||
|
||||
impl SelectedNodes {
|
||||
@@ -157,5 +161,6 @@ impl SelectedNodes {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct CollapsedLayers(pub Vec<LayerNodeIdentifier>);
|
||||
|
||||
@@ -3,7 +3,8 @@ use glam::{DVec2, IVec2};
|
||||
use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point};
|
||||
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct WirePath {
|
||||
#[serde(rename = "pathString")]
|
||||
pub path_string: String,
|
||||
@@ -13,7 +14,8 @@ pub struct WirePath {
|
||||
pub dashed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct WirePathUpdate {
|
||||
pub id: NodeId,
|
||||
#[serde(rename = "inputIndex")]
|
||||
@@ -23,7 +25,8 @@ pub struct WirePathUpdate {
|
||||
pub wire_path_update: Option<WirePath>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum GraphWireStyle {
|
||||
#[default]
|
||||
Direct = 0,
|
||||
|
||||
@@ -1011,12 +1011,11 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
PortfolioMessage::RequestWelcomeScreenButtonsLayout => {
|
||||
let donate = "https://graphite.art/donate/";
|
||||
|
||||
let table = LayoutGroup::Table {
|
||||
unstyled: true,
|
||||
rows: vec![
|
||||
let table = LayoutGroup::table(
|
||||
vec![
|
||||
vec![
|
||||
TextButton::new("New Document")
|
||||
.icon(Some("File".into()))
|
||||
.icon("File")
|
||||
.flush(true)
|
||||
.on_commit(|_| DialogMessage::RequestNewDocumentDialog.into())
|
||||
.widget_instance(),
|
||||
@@ -1024,7 +1023,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
],
|
||||
vec![
|
||||
TextButton::new("Open Document")
|
||||
.icon(Some("Folder".into()))
|
||||
.icon("Folder")
|
||||
.flush(true)
|
||||
.on_commit(|_| PortfolioMessage::Open.into())
|
||||
.widget_instance(),
|
||||
@@ -1032,20 +1031,21 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
],
|
||||
vec![
|
||||
TextButton::new("Open Demo Artwork")
|
||||
.icon(Some("Image".into()))
|
||||
.icon("Image")
|
||||
.flush(true)
|
||||
.on_commit(|_| DialogMessage::RequestDemoArtworkDialog.into())
|
||||
.widget_instance(),
|
||||
],
|
||||
vec![
|
||||
TextButton::new("Support the Development Fund")
|
||||
.icon(Some("Heart".into()))
|
||||
.icon("Heart")
|
||||
.flush(true)
|
||||
.on_commit(move |_| FrontendMessage::TriggerVisitLink { url: donate.to_string() }.into())
|
||||
.widget_instance(),
|
||||
],
|
||||
],
|
||||
};
|
||||
true,
|
||||
);
|
||||
|
||||
responses.add(LayoutMessage::DestroyLayout {
|
||||
layout_target: LayoutTarget::WelcomeScreenButtons,
|
||||
@@ -1061,7 +1061,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
#[cfg(target_family = "wasm")]
|
||||
let widgets = vec![];
|
||||
|
||||
let row = LayoutGroup::Row { widgets };
|
||||
let row = LayoutGroup::row(widgets);
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: Layout(vec![row]),
|
||||
|
||||
@@ -11,7 +11,8 @@ pub struct PreferencesMessageContext<'a> {
|
||||
pub tool_message_handler: &'a ToolMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, specta::Type, ExtractField)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, ExtractField)]
|
||||
#[serde(default)]
|
||||
pub struct PreferencesMessageHandler {
|
||||
pub selection_mode: SelectionMode,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type, Hash)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash)]
|
||||
pub enum SelectionMode {
|
||||
#[default]
|
||||
Touched = 0,
|
||||
|
||||
@@ -4,7 +4,8 @@ use crate::messages::prelude::*;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
|
||||
#[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 ToolColorType {
|
||||
Primary,
|
||||
Secondary,
|
||||
|
||||
@@ -150,7 +150,8 @@ impl PivotGizmo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum PivotGizmoType {
|
||||
// Pivot
|
||||
#[default]
|
||||
@@ -161,7 +162,8 @@ pub enum PivotGizmoType {
|
||||
// TODO: Add "Individual"
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PivotGizmoState {
|
||||
pub enabled: bool,
|
||||
pub gizmo_type: PivotGizmoType,
|
||||
|
||||
@@ -23,7 +23,8 @@ use kurbo::{BezPath, PathEl, Shape};
|
||||
use std::collections::VecDeque;
|
||||
use std::f64::consts::{PI, TAU};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ShapeType {
|
||||
#[default]
|
||||
Polygon = 0,
|
||||
|
||||
@@ -22,7 +22,8 @@ pub struct ArtboardTool {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Artboard)]
|
||||
#[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 ArtboardToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
|
||||
@@ -13,7 +13,8 @@ use graphene_std::raster::BlendMode;
|
||||
|
||||
const BRUSH_MAX_SIZE: f64 = 5000.;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum DrawMode {
|
||||
Draw = 0,
|
||||
Erase,
|
||||
@@ -52,7 +53,8 @@ impl Default for BrushOptions {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Brush)]
|
||||
#[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 BrushToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
@@ -65,7 +67,8 @@ pub enum BrushToolMessage {
|
||||
UpdateOptions { options: BrushToolMessageOptionsUpdate },
|
||||
}
|
||||
|
||||
#[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 BrushToolMessageOptionsUpdate {
|
||||
BlendMode(BlendMode),
|
||||
ChangeDiameter(f64),
|
||||
@@ -220,7 +223,7 @@ impl LayoutHolder for BrushTool {
|
||||
.widget_instance(),
|
||||
);
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ pub struct EyedropperTool {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Eyedropper)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum EyedropperToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
@@ -46,9 +47,9 @@ impl LayoutHolder for EyedropperTool {
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for EyedropperTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||
if let ToolMessage::Eyedropper(EyedropperToolMessage::PreviewImage { data, width, height }) = message {
|
||||
let image = EyedropperPreviewImage { data, width, height };
|
||||
let image = EyedropperPreviewImage { data: data.into(), width, height };
|
||||
|
||||
update_cursor_preview_common(responses, Some(image), context.input, context.global_tool_data, self.data.color_choice.clone());
|
||||
update_cursor_preview_common(responses, Some(image), context.input, context.global_tool_data, self.data.color_choice);
|
||||
|
||||
if !self.data.preview {
|
||||
disable_cursor_preview(responses, &mut self.data);
|
||||
@@ -87,10 +88,18 @@ enum EyedropperToolFsmState {
|
||||
SamplingSecondary,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum PrimarySecondary {
|
||||
#[default]
|
||||
Primary,
|
||||
Secondary,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct EyedropperToolData {
|
||||
preview: bool,
|
||||
color_choice: Option<String>,
|
||||
color_choice: Option<PrimarySecondary>,
|
||||
}
|
||||
|
||||
impl Fsm for EyedropperToolFsmState {
|
||||
@@ -127,7 +136,11 @@ impl Fsm for EyedropperToolFsmState {
|
||||
}
|
||||
// Sampling -> Ready
|
||||
(EyedropperToolFsmState::SamplingPrimary, EyedropperToolMessage::SamplePrimaryColorEnd) | (EyedropperToolFsmState::SamplingSecondary, EyedropperToolMessage::SampleSecondaryColorEnd) => {
|
||||
let set_color_choice = if self == EyedropperToolFsmState::SamplingPrimary { "Primary" } else { "Secondary" }.to_string();
|
||||
let set_color_choice = match self {
|
||||
EyedropperToolFsmState::SamplingPrimary => PrimarySecondary::Primary,
|
||||
EyedropperToolFsmState::SamplingSecondary => PrimarySecondary::Secondary,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
update_cursor_preview(responses, tool_data, input, global_tool_data, Some(set_color_choice));
|
||||
disable_cursor_preview(responses, tool_data);
|
||||
|
||||
@@ -185,7 +198,7 @@ fn update_cursor_preview(
|
||||
tool_data: &mut EyedropperToolData,
|
||||
_input: &InputPreprocessorMessageHandler,
|
||||
_global_tool_data: &DocumentToolData,
|
||||
set_color_choice: Option<String>,
|
||||
set_color_choice: Option<PrimarySecondary>,
|
||||
) {
|
||||
tool_data.preview = true;
|
||||
tool_data.color_choice = set_color_choice;
|
||||
@@ -198,7 +211,7 @@ fn update_cursor_preview(
|
||||
tool_data: &mut EyedropperToolData,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
global_tool_data: &DocumentToolData,
|
||||
set_color_choice: Option<String>,
|
||||
set_color_choice: Option<PrimarySecondary>,
|
||||
) {
|
||||
tool_data.preview = true;
|
||||
tool_data.color_choice = set_color_choice.clone();
|
||||
@@ -211,7 +224,7 @@ fn update_cursor_preview_common(
|
||||
image: Option<EyedropperPreviewImage>,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
global_tool_data: &DocumentToolData,
|
||||
set_color_choice: Option<String>,
|
||||
set_color_choice: Option<PrimarySecondary>,
|
||||
) {
|
||||
responses.add(FrontendMessage::UpdateEyedropperSamplingState {
|
||||
image,
|
||||
|
||||
@@ -9,7 +9,8 @@ pub struct FillTool {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Fill)]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum FillToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
|
||||
@@ -37,7 +37,8 @@ impl Default for FreehandOptions {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Freehand)]
|
||||
#[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 FreehandToolMessage {
|
||||
// Standard messages
|
||||
Overlays { context: OverlayContext },
|
||||
@@ -51,7 +52,8 @@ pub enum FreehandToolMessage {
|
||||
UpdateOptions { options: FreehandOptionsUpdate },
|
||||
}
|
||||
|
||||
#[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 FreehandOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
@@ -151,7 +153,7 @@ impl LayoutHolder for FreehandTool {
|
||||
widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ pub struct GradientOptions {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Gradient)]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum GradientToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
@@ -46,7 +47,8 @@ pub enum GradientToolMessage {
|
||||
UpdateOptions { options: GradientOptionsUpdate },
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum GradientOptionsUpdate {
|
||||
Type(GradientType),
|
||||
ReverseStops,
|
||||
@@ -199,7 +201,7 @@ impl LayoutHolder for GradientTool {
|
||||
widgets.push(reverse_direction);
|
||||
}
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,8 +772,8 @@ impl Fsm for GradientToolFsmState {
|
||||
if stop_index < gradient.stops.position.len() {
|
||||
let color = gradient.stops.color[stop_index].to_gamma_srgb();
|
||||
let position = gradient.stops.position[stop_index];
|
||||
let DVec2 { x, y } = transform.transform_point2(gradient.start.lerp(gradient.end, position));
|
||||
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition { color, x, y });
|
||||
let position = transform.transform_point2(gradient.start.lerp(gradient.end, position)).into();
|
||||
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition { color, position });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -824,13 +826,10 @@ impl Fsm for GradientToolFsmState {
|
||||
let viewport_pos = selected_gradient
|
||||
.transform
|
||||
.transform_point2(selected_gradient.gradient.start.lerp(selected_gradient.gradient.end, stop_pos));
|
||||
let position = viewport_pos.into();
|
||||
let color = selected_gradient.gradient.stops.color[stop_index].to_gamma_srgb();
|
||||
tool_data.color_picker_editing_color_stop = Some(stop_index);
|
||||
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition {
|
||||
color,
|
||||
x: viewport_pos.x,
|
||||
y: viewport_pos.y,
|
||||
});
|
||||
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition { color, position });
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
@@ -7,7 +7,8 @@ pub struct NavigateTool {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Navigate)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum NavigateToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
|
||||
@@ -50,7 +50,8 @@ pub struct PathToolOptions {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Path)]
|
||||
#[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 PathToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
@@ -155,7 +156,8 @@ pub enum PathToolMessage {
|
||||
ToggleSegmentEditing,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum PathOverlayMode {
|
||||
AllHandles = 0,
|
||||
#[default]
|
||||
@@ -178,7 +180,8 @@ impl Default for PathEditingMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum PathOptionsUpdate {
|
||||
OverlayModeType(PathOverlayMode),
|
||||
PointEditingMode { enabled: bool },
|
||||
@@ -326,7 +329,7 @@ impl LayoutHolder for PathTool {
|
||||
|
||||
// Works only if a single layer is selected and its type is Vector
|
||||
let path_node_button = TextButton::new("Make Path Editable")
|
||||
.icon(Some("NodeShape".into()))
|
||||
.icon("NodeShape")
|
||||
.tooltip_label("Make Path Editable")
|
||||
.tooltip_description(
|
||||
"Enables the Pen and Path tools to directly edit layer geometry resulting from nondestructive operations. This inserts a 'Path' node as the last operation of the selected layer.",
|
||||
@@ -349,32 +352,30 @@ impl LayoutHolder for PathTool {
|
||||
|
||||
let _pin_pivot = pin_pivot_widget(self.tool_data.pivot_gizmo.pin_active(), false, PivotToolSource::Path);
|
||||
|
||||
Layout(vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
x_location,
|
||||
related_seperator.clone(),
|
||||
y_location,
|
||||
unrelated_seperator.clone(),
|
||||
colinear_handle_checkbox,
|
||||
related_seperator.clone(),
|
||||
colinear_handles_label,
|
||||
unrelated_seperator.clone(),
|
||||
point_editing_mode,
|
||||
related_seperator.clone(),
|
||||
segment_editing_mode,
|
||||
unrelated_seperator.clone(),
|
||||
path_overlay_mode_widget,
|
||||
unrelated_seperator.clone(),
|
||||
path_node_button,
|
||||
// checkbox.clone(),
|
||||
// related_seperator.clone(),
|
||||
// dropdown.clone(),
|
||||
// unrelated_seperator,
|
||||
// pivot_reference,
|
||||
// related_seperator.clone(),
|
||||
// pin_pivot,
|
||||
],
|
||||
}])
|
||||
Layout(vec![LayoutGroup::row(vec![
|
||||
x_location,
|
||||
related_seperator.clone(),
|
||||
y_location,
|
||||
unrelated_seperator.clone(),
|
||||
colinear_handle_checkbox,
|
||||
related_seperator.clone(),
|
||||
colinear_handles_label,
|
||||
unrelated_seperator.clone(),
|
||||
point_editing_mode,
|
||||
related_seperator.clone(),
|
||||
segment_editing_mode,
|
||||
unrelated_seperator.clone(),
|
||||
path_overlay_mode_widget,
|
||||
unrelated_seperator.clone(),
|
||||
path_node_button,
|
||||
// checkbox.clone(),
|
||||
// related_seperator.clone(),
|
||||
// dropdown.clone(),
|
||||
// unrelated_seperator,
|
||||
// pivot_reference,
|
||||
// related_seperator.clone(),
|
||||
// pin_pivot,
|
||||
])])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@ impl Default for PenOptions {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Pen)]
|
||||
#[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 PenToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
@@ -107,13 +108,15 @@ enum PenToolFsmState {
|
||||
GRSHandle,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum PenOverlayMode {
|
||||
AllHandles = 0,
|
||||
FrontierHandles = 1,
|
||||
}
|
||||
|
||||
#[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 PenOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
@@ -238,7 +241,7 @@ impl LayoutHolder for PenTool {
|
||||
.widget_instance(),
|
||||
);
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ pub struct SelectOptions {
|
||||
nested_selection_behavior: NestedSelectionBehavior,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum SelectOptionsUpdate {
|
||||
NestedSelectionBehavior(NestedSelectionBehavior),
|
||||
PivotGizmoType(PivotGizmoType),
|
||||
@@ -50,7 +51,8 @@ pub enum SelectOptionsUpdate {
|
||||
TogglePivotPinned,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum NestedSelectionBehavior {
|
||||
#[default]
|
||||
Shallowest,
|
||||
@@ -66,7 +68,8 @@ impl fmt::Display for NestedSelectionBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
#[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 struct SelectToolPointerKeys {
|
||||
pub axis_align: Key,
|
||||
pub snap_angle: Key,
|
||||
@@ -75,7 +78,8 @@ pub struct SelectToolPointerKeys {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Select)]
|
||||
#[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 SelectToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
@@ -265,7 +269,7 @@ impl LayoutHolder for SelectTool {
|
||||
widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||
widgets.extend(self.boolean_widgets(self.tool_data.selected_layers_count));
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,8 @@ impl Default for ShapeToolOptions {
|
||||
}
|
||||
}
|
||||
|
||||
#[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 ShapeOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
@@ -88,7 +89,8 @@ pub enum ShapeOptionsUpdate {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Shape)]
|
||||
#[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 ShapeToolMessage {
|
||||
// Standard messages
|
||||
Overlays { context: OverlayContext },
|
||||
@@ -423,7 +425,7 @@ impl LayoutHolder for ShapeTool {
|
||||
widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ impl Default for SplineOptions {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Spline)]
|
||||
#[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 SplineToolMessage {
|
||||
// Standard messages
|
||||
Overlays { context: OverlayContext },
|
||||
@@ -65,7 +66,8 @@ enum SplineToolFsmState {
|
||||
MergingEndpoints,
|
||||
}
|
||||
|
||||
#[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 SplineOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
@@ -158,7 +160,7 @@ impl LayoutHolder for SplineTool {
|
||||
widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ impl Default for TextOptions {
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Text)]
|
||||
#[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 TextToolMessage {
|
||||
// Standard messages
|
||||
Abort,
|
||||
@@ -75,7 +76,8 @@ pub enum TextToolMessage {
|
||||
RefreshEditingFontData,
|
||||
}
|
||||
|
||||
#[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 TextOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
@@ -271,7 +273,7 @@ impl TextTool {
|
||||
},
|
||||
));
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +415,7 @@ impl TextToolData {
|
||||
line_height_ratio: editing_text.typesetting.line_height_ratio,
|
||||
font_size: editing_text.typesetting.font_size,
|
||||
color: editing_text.color.map_or("#000000".to_string(), |color| format!("#{}", color.to_rgba_hex_srgb())),
|
||||
font_data: font_cache.get(&editing_text.font).map(|(data, _)| data.clone()).unwrap_or_default(),
|
||||
font_data: font_cache.get(&editing_text.font).map(|(data, _)| data.clone()).unwrap_or_default().into(),
|
||||
transform: editing_text.transform.to_cols_array(),
|
||||
max_width: editing_text.typesetting.max_width,
|
||||
max_height: editing_text.typesetting.max_height,
|
||||
@@ -925,7 +927,7 @@ impl Fsm for TextToolFsmState {
|
||||
(TextToolFsmState::Editing, TextToolMessage::RefreshEditingFontData) => {
|
||||
let font = Font::new(tool_options.font.font_family.clone(), tool_options.font.font_style.clone());
|
||||
responses.add(FrontendMessage::DisplayEditableTextboxUpdateFontData {
|
||||
font_data: font_cache.get(&font).map(|(data, _)| data.clone()).unwrap_or_default(),
|
||||
font_data: font_cache.get(&font).map(|(data, _)| data.clone()).unwrap_or_default().into(),
|
||||
});
|
||||
|
||||
TextToolFsmState::Editing
|
||||
|
||||
@@ -128,23 +128,21 @@ pub struct DocumentToolData {
|
||||
impl DocumentToolData {
|
||||
pub fn update_working_colors(&self, responses: &mut VecDeque<Message>) {
|
||||
let layout = Layout(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WorkingColorsInput::new(self.primary_color.to_gamma_srgb(), self.secondary_color.to_gamma_srgb()).widget_instance()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
IconButton::new("SwapVertical", 16)
|
||||
.tooltip_label("Swap Working Colors")
|
||||
.tooltip_shortcut(action_shortcut!(ToolMessageDiscriminant::SwapColors))
|
||||
.on_update(|_| ToolMessage::SwapColors.into())
|
||||
.widget_instance(),
|
||||
IconButton::new("WorkingColors", 16)
|
||||
.tooltip_label("Reset Working Colors")
|
||||
.tooltip_shortcut(action_shortcut!(ToolMessageDiscriminant::ResetColors))
|
||||
.on_update(|_| ToolMessage::ResetColors.into())
|
||||
.widget_instance(),
|
||||
],
|
||||
},
|
||||
LayoutGroup::row(vec![
|
||||
WorkingColorsInput::new(self.primary_color.to_gamma_srgb(), self.secondary_color.to_gamma_srgb()).widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![
|
||||
IconButton::new("SwapVertical", 16)
|
||||
.tooltip_label("Swap Working Colors")
|
||||
.tooltip_shortcut(action_shortcut!(ToolMessageDiscriminant::SwapColors))
|
||||
.on_update(|_| ToolMessage::SwapColors.into())
|
||||
.widget_instance(),
|
||||
IconButton::new("WorkingColors", 16)
|
||||
.tooltip_label("Reset Working Colors")
|
||||
.tooltip_shortcut(action_shortcut!(ToolMessageDiscriminant::ResetColors))
|
||||
.on_update(|_| ToolMessage::ResetColors.into())
|
||||
.widget_instance(),
|
||||
]),
|
||||
]);
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
@@ -308,7 +306,7 @@ impl ToolData {
|
||||
.skip(1)
|
||||
.collect();
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets: tool_groups_layout }])
|
||||
Layout(vec![LayoutGroup::row(tool_groups_layout)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,7 +358,8 @@ impl ToolFsmState {
|
||||
}
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default)]
|
||||
pub enum ToolType {
|
||||
// General tool group
|
||||
#[default]
|
||||
@@ -524,7 +523,8 @@ pub fn tool_type_to_activate_tool_message(tool_type: ToolType) -> ToolMessageDis
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct HintData(pub Vec<HintGroup>);
|
||||
|
||||
impl HintData {
|
||||
@@ -558,7 +558,7 @@ impl HintData {
|
||||
}
|
||||
}
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
Layout(vec![LayoutGroup::row(widgets)])
|
||||
}
|
||||
|
||||
pub fn send_layout(&self, responses: &mut VecDeque<Message>) {
|
||||
@@ -576,10 +576,12 @@ impl HintData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct HintGroup(pub Vec<HintInfo>);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct HintInfo {
|
||||
/// A `KeysGroup` specifies all the keys pressed simultaneously to perform an action (like "Ctrl C" to copy).
|
||||
/// Usually at most one is given, but less commonly, multiple can be used to describe additional hotkeys not used simultaneously (like the four different arrow keys to nudge a layer).
|
||||
|
||||
@@ -3,7 +3,8 @@ use std::ops::{Add, Div, Mul, Sub};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::tool_messages::tool_prelude::DVec2;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type, ExtractField)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, ExtractField)]
|
||||
pub struct ViewportMessageHandler {
|
||||
bounds: Bounds,
|
||||
// Ratio of logical pixels to physical pixels
|
||||
@@ -157,7 +158,8 @@ pub trait Position {
|
||||
fn y(&self) -> f64;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
struct Point {
|
||||
x: f64,
|
||||
y: f64,
|
||||
@@ -183,7 +185,8 @@ impl Position for Point {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LogicalPoint {
|
||||
inner: Point,
|
||||
scale: f64,
|
||||
@@ -217,7 +220,8 @@ impl FromWithScale<Point> for LogicalPoint {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PhysicalPoint {
|
||||
inner: Point,
|
||||
scale: f64,
|
||||
@@ -258,7 +262,8 @@ pub trait Rect<P: Position>: Position {
|
||||
fn height(&self) -> f64;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type, ExtractField)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, ExtractField)]
|
||||
struct Bounds {
|
||||
offset: Point,
|
||||
size: Point,
|
||||
@@ -286,7 +291,8 @@ impl Rect<Point> for Bounds {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LogicalBounds {
|
||||
offset: Point,
|
||||
size: Point,
|
||||
@@ -338,7 +344,8 @@ impl FromWithScale<Bounds> for LogicalBounds {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PhysicalBounds {
|
||||
offset: Point,
|
||||
size: Point,
|
||||
|
||||
Reference in New Issue
Block a user