Code cleanup and refactoring to enhance consistency (#1695)

- Move message handler payload data into structs
- Organize the file structure used by `editor/src/messages/portfolio/document` `/node_graph` and `/graph_operation`
- Make derive attributes use `serde::Serialize, serde::Deserialize` consistently instead of `use serde::{Deserialize, Serialize};` imports
- Various other code cleanup and refactoring
This commit is contained in:
Keavon Chambers 2024-03-20 21:28:51 -07:00 committed by GitHub
parent ed3f7acdd7
commit 0a9bd41be1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
134 changed files with 1860 additions and 1865 deletions

View file

@ -52,20 +52,22 @@ pub fn commit_info_localized(localized_commit_date: &str) -> String {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::messages::{input_mapper::utility_types::input_mouse::ViewportBounds, prelude::*}; use crate::messages::input_mapper::utility_types::input_mouse::ViewportBounds;
use crate::messages::prelude::*;
// TODO: Fix and reenable // TODO: Fix and reenable
#[ignore] #[ignore]
#[test] #[test]
fn debug_ub() { fn debug_ub() {
use super::Message;
let mut editor = super::Editor::new(); let mut editor = super::Editor::new();
let mut responses = Vec::new(); let mut responses = Vec::new();
use super::Message::*;
let messages: Vec<Message> = vec![ let messages: Vec<Message> = vec![
Init, Message::Init,
Preferences(PreferencesMessage::Load { Message::Preferences(PreferencesMessage::Load {
preferences: r#"{"imaginate_server_hostname":"https://exchange-encoding-watched-insured.trycloudflare.com/","imaginate_refresh_frequency":1,"zoom_with_scroll":false}"#.to_string(), preferences: r#"{ "imaginate_server_hostname": "http://localhost:7860/", "imaginate_refresh_frequency": 1, "zoom_with_scroll": false }"#.to_string(),
}), }),
PortfolioMessage::OpenDocumentFileWithId { PortfolioMessage::OpenDocumentFileWithId {
document_id: DocumentId(0), document_id: DocumentId(0),

View file

@ -1,6 +1,6 @@
use crate::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE}; use crate::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE};
use crate::messages::debug::utility_types::MessageLoggingVerbosity; use crate::messages::debug::utility_types::MessageLoggingVerbosity;
use crate::messages::dialog::DialogData; use crate::messages::dialog::DialogMessageData;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use graphene_core::text::Font; use graphene_core::text::Font;
@ -58,8 +58,6 @@ impl Dispatcher {
} }
pub fn handle_message<T: Into<Message>>(&mut self, message: T) { pub fn handle_message<T: Into<Message>>(&mut self, message: T) {
use Message::*;
self.message_queues.push(VecDeque::from_iter([message.into()])); self.message_queues.push(VecDeque::from_iter([message.into()]));
while let Some(message) = self.message_queues.last_mut().and_then(VecDeque::pop_front) { while let Some(message) = self.message_queues.last_mut().and_then(VecDeque::pop_front) {
@ -86,8 +84,8 @@ impl Dispatcher {
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend // Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
match message { match message {
NoOp => {} Message::NoOp => {}
Init => { Message::Init => {
// Load persistent data from the browser database // Load persistent data from the browser database
queue.add(FrontendMessage::TriggerLoadAutoSaveDocuments); queue.add(FrontendMessage::TriggerLoadAutoSaveDocuments);
queue.add(FrontendMessage::TriggerLoadPreferences); queue.add(FrontendMessage::TriggerLoadPreferences);
@ -100,18 +98,18 @@ impl Dispatcher {
queue.add(FrontendMessage::TriggerFontLoad { font, is_default: true }); queue.add(FrontendMessage::TriggerFontLoad { font, is_default: true });
} }
Broadcast(message) => self.message_handlers.broadcast_message_handler.process_message(message, &mut queue, ()), Message::Broadcast(message) => self.message_handlers.broadcast_message_handler.process_message(message, &mut queue, ()),
Debug(message) => { Message::Debug(message) => {
self.message_handlers.debug_message_handler.process_message(message, &mut queue, ()); self.message_handlers.debug_message_handler.process_message(message, &mut queue, ());
} }
Dialog(message) => { Message::Dialog(message) => {
let data = DialogData { let data = DialogMessageData {
portfolio: &self.message_handlers.portfolio_message_handler, portfolio: &self.message_handlers.portfolio_message_handler,
preferences: &self.message_handlers.preferences_message_handler, preferences: &self.message_handlers.preferences_message_handler,
}; };
self.message_handlers.dialog_message_handler.process_message(message, &mut queue, data); self.message_handlers.dialog_message_handler.process_message(message, &mut queue, data);
} }
Frontend(message) => { Message::Frontend(message) => {
// Handle these messages immediately by returning early // Handle these messages immediately by returning early
if let FrontendMessage::TriggerFontLoad { .. } | FrontendMessage::TriggerRefreshBoundsOfViewports = message { if let FrontendMessage::TriggerFontLoad { .. } | FrontendMessage::TriggerRefreshBoundsOfViewports = message {
self.responses.push(message); self.responses.push(message);
@ -124,54 +122,56 @@ impl Dispatcher {
self.responses.push(message); self.responses.push(message);
} }
} }
Globals(message) => { Message::Globals(message) => {
self.message_handlers.globals_message_handler.process_message(message, &mut queue, ()); self.message_handlers.globals_message_handler.process_message(message, &mut queue, ());
} }
InputPreprocessor(message) => { Message::InputPreprocessor(message) => {
let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout(); let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
self.message_handlers.input_preprocessor_message_handler.process_message(message, &mut queue, keyboard_platform); self.message_handlers
.input_preprocessor_message_handler
.process_message(message, &mut queue, InputPreprocessorMessageData { keyboard_platform });
} }
KeyMapping(message) => { Message::KeyMapping(message) => {
let input = &self.message_handlers.input_preprocessor_message_handler;
let actions = self.collect_actions(); let actions = self.collect_actions();
self.message_handlers self.message_handlers
.key_mapping_message_handler .key_mapping_message_handler
.process_message(message, &mut queue, (&self.message_handlers.input_preprocessor_message_handler, actions)); .process_message(message, &mut queue, KeyMappingMessageData { input, actions });
} }
Layout(message) => { Message::Layout(message) => {
let action_input_mapping = &|action_to_find: &MessageDiscriminant| self.message_handlers.key_mapping_message_handler.action_input_mapping(action_to_find); let action_input_mapping = &|action_to_find: &MessageDiscriminant| self.message_handlers.key_mapping_message_handler.action_input_mapping(action_to_find);
self.message_handlers.layout_message_handler.process_message(message, &mut queue, action_input_mapping); self.message_handlers.layout_message_handler.process_message(message, &mut queue, action_input_mapping);
} }
Portfolio(message) => { Message::Portfolio(message) => {
self.message_handlers.portfolio_message_handler.process_message( let ipp = &self.message_handlers.input_preprocessor_message_handler;
message, let preferences = &self.message_handlers.preferences_message_handler;
&mut queue,
(&self.message_handlers.input_preprocessor_message_handler, &self.message_handlers.preferences_message_handler), self.message_handlers
); .portfolio_message_handler
.process_message(message, &mut queue, PortfolioMessageData { ipp, preferences });
} }
Preferences(message) => { Message::Preferences(message) => {
self.message_handlers.preferences_message_handler.process_message(message, &mut queue, ()); self.message_handlers.preferences_message_handler.process_message(message, &mut queue, ());
} }
Tool(message) => { Message::Tool(message) => {
if let Some(document) = self.message_handlers.portfolio_message_handler.active_document() { if let Some(document) = self.message_handlers.portfolio_message_handler.active_document() {
self.message_handlers.tool_message_handler.process_message( let data = ToolMessageData {
message, document_id: self.message_handlers.portfolio_message_handler.active_document_id().unwrap(),
&mut queue, document: document,
( input: &self.message_handlers.input_preprocessor_message_handler,
document, persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
self.message_handlers.portfolio_message_handler.active_document_id().unwrap(), node_graph: &self.message_handlers.portfolio_message_handler.executor,
&self.message_handlers.input_preprocessor_message_handler, };
&self.message_handlers.portfolio_message_handler.persistent_data,
&self.message_handlers.portfolio_message_handler.executor, self.message_handlers.tool_message_handler.process_message(message, &mut queue, data);
),
);
} else { } else {
warn!("Called ToolMessage without an active document.\nGot {message:?}"); warn!("Called ToolMessage without an active document.\nGot {message:?}");
} }
} }
Workspace(message) => { Message::Workspace(message) => {
self.message_handlers.workspace_message_handler.process_message(message, &mut queue, ()); self.message_handlers.workspace_message_handler.process_message(message, &mut queue, ());
} }
} }

View file

@ -1,8 +1,6 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize}; #[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, Hash)]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)]
#[impl_message(Message, BroadcastMessage, TriggerEvent)] #[impl_message(Message, BroadcastMessage, TriggerEvent)]
pub enum BroadcastEvent { pub enum BroadcastEvent {
AnimationFrame, AnimationFrame,

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Broadcast)] #[impl_message(Message, Broadcast)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum BroadcastMessage { pub enum BroadcastMessage {
// Sub-messages // Sub-messages
#[child] #[child]

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Debug)] #[impl_message(Message, Debug)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
pub enum DebugMessage { pub enum DebugMessage {
ToggleTraceLogs, ToggleTraceLogs,
MessageOff, MessageOff,

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Dialog)] #[impl_message(Message, Dialog)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum DialogMessage { pub enum DialogMessage {
// Sub-messages // Sub-messages
#[child] #[child]

View file

@ -3,6 +3,11 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils::is_layer_fed_by_node_of_name; use crate::messages::tool::common_functionality::graph_modification_utils::is_layer_fed_by_node_of_name;
pub struct DialogMessageData<'a> {
pub portfolio: &'a PortfolioMessageHandler,
pub preferences: &'a PreferencesMessageHandler,
}
/// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`. /// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`.
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct DialogMessageHandler { pub struct DialogMessageHandler {
@ -11,17 +16,14 @@ pub struct DialogMessageHandler {
preferences_dialog: PreferencesDialogMessageHandler, preferences_dialog: PreferencesDialogMessageHandler,
} }
pub struct DialogData<'a> { impl MessageHandler<DialogMessage, DialogMessageData<'_>> for DialogMessageHandler {
pub portfolio: &'a PortfolioMessageHandler, fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, data: DialogMessageData) {
pub preferences: &'a PreferencesMessageHandler, let DialogMessageData { portfolio, preferences } = data;
}
impl MessageHandler<DialogMessage, DialogData<'_>> for DialogMessageHandler {
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, DialogData { portfolio, preferences }: DialogData) {
match message { match message {
DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, portfolio), DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, ExportDialogMessageData { portfolio }),
DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()), DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()),
DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, preferences), DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageData { preferences }),
DialogMessage::CloseAllDocumentsWithConfirmation => { DialogMessage::CloseAllDocumentsWithConfirmation => {
let dialog = simple_dialogs::CloseAllDocumentsDialog { let dialog = simple_dialogs::CloseAllDocumentsDialog {

View file

@ -1,10 +1,8 @@
use crate::messages::frontend::utility_types::{ExportBounds, FileType}; use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, DialogMessage, ExportDialog)] #[impl_message(Message, DialogMessage, ExportDialog)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum ExportDialogMessage { pub enum ExportDialogMessage {
FileType(FileType), FileType(FileType),
ScaleFactor(f64), ScaleFactor(f64),

View file

@ -3,6 +3,10 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::prelude::*; use crate::messages::prelude::*;
pub struct ExportDialogMessageData<'a> {
pub portfolio: &'a PortfolioMessageHandler,
}
/// A dialog to allow users to customize their file export. /// A dialog to allow users to customize their file export.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ExportDialogMessageHandler { pub struct ExportDialogMessageHandler {
@ -14,8 +18,10 @@ pub struct ExportDialogMessageHandler {
pub has_selection: bool, pub has_selection: bool,
} }
impl MessageHandler<ExportDialogMessage, &PortfolioMessageHandler> for ExportDialogMessageHandler { impl MessageHandler<ExportDialogMessage, ExportDialogMessageData<'_>> for ExportDialogMessageHandler {
fn process_message(&mut self, message: ExportDialogMessage, responses: &mut VecDeque<Message>, portfolio: &PortfolioMessageHandler) { fn process_message(&mut self, message: ExportDialogMessage, responses: &mut VecDeque<Message>, data: ExportDialogMessageData) {
let ExportDialogMessageData { portfolio } = data;
match message { match message {
ExportDialogMessage::FileType(export_type) => self.file_type = export_type, ExportDialogMessage::FileType(export_type) => self.file_type = export_type,
ExportDialogMessage::ScaleFactor(factor) => self.scale_factor = factor, ExportDialogMessage::ScaleFactor(factor) => self.scale_factor = factor,

View file

@ -4,4 +4,4 @@ mod export_dialog_message_handler;
#[doc(inline)] #[doc(inline)]
pub use export_dialog_message::{ExportDialogMessage, ExportDialogMessageDiscriminant}; pub use export_dialog_message::{ExportDialogMessage, ExportDialogMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use export_dialog_message_handler::ExportDialogMessageHandler; pub use export_dialog_message_handler::{ExportDialogMessageData, ExportDialogMessageHandler};

View file

@ -16,4 +16,4 @@ pub mod simple_dialogs;
#[doc(inline)] #[doc(inline)]
pub use dialog_message::{DialogMessage, DialogMessageDiscriminant}; pub use dialog_message::{DialogMessage, DialogMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use dialog_message_handler::*; pub use dialog_message_handler::{DialogMessageData, DialogMessageHandler};

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, DialogMessage, NewDocumentDialog)] #[impl_message(Message, DialogMessage, NewDocumentDialog)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum NewDocumentDialogMessage { pub enum NewDocumentDialogMessage {
Name(String), Name(String),
Infinite(bool), Infinite(bool),

View file

@ -21,7 +21,6 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
NewDocumentDialogMessage::Infinite(infinite) => self.infinite = infinite, NewDocumentDialogMessage::Infinite(infinite) => self.infinite = infinite,
NewDocumentDialogMessage::DimensionsX(x) => self.dimensions.x = x as u32, NewDocumentDialogMessage::DimensionsX(x) => self.dimensions.x = x as u32,
NewDocumentDialogMessage::DimensionsY(y) => self.dimensions.y = y as u32, NewDocumentDialogMessage::DimensionsY(y) => self.dimensions.y = y as u32,
NewDocumentDialogMessage::Submit => { NewDocumentDialogMessage::Submit => {
responses.add(PortfolioMessage::NewDocumentWithName { name: self.name.clone() }); responses.add(PortfolioMessage::NewDocumentWithName { name: self.name.clone() });

View file

@ -4,4 +4,4 @@ mod preferences_dialog_message_handler;
#[doc(inline)] #[doc(inline)]
pub use preferences_dialog_message::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant}; pub use preferences_dialog_message::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use preferences_dialog_message_handler::PreferencesDialogMessageHandler; pub use preferences_dialog_message_handler::{PreferencesDialogMessageData, PreferencesDialogMessageHandler};

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, DialogMessage, PreferencesDialog)] #[impl_message(Message, DialogMessage, PreferencesDialog)]
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum PreferencesDialogMessage { pub enum PreferencesDialogMessage {
Confirm, Confirm,
} }

View file

@ -1,12 +1,18 @@
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*; use crate::messages::prelude::*;
pub struct PreferencesDialogMessageData<'a> {
pub preferences: &'a PreferencesMessageHandler,
}
/// A dialog to allow users to customize Graphite editor options /// A dialog to allow users to customize Graphite editor options
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct PreferencesDialogMessageHandler {} pub struct PreferencesDialogMessageHandler {}
impl MessageHandler<PreferencesDialogMessage, &PreferencesMessageHandler> for PreferencesDialogMessageHandler { impl MessageHandler<PreferencesDialogMessage, PreferencesDialogMessageData<'_>> for PreferencesDialogMessageHandler {
fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque<Message>, preferences: &PreferencesMessageHandler) { fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque<Message>, data: PreferencesDialogMessageData) {
let PreferencesDialogMessageData { preferences } = data;
match message { match message {
PreferencesDialogMessage::Confirm => {} PreferencesDialogMessage::Confirm => {}
} }

View file

@ -1,6 +1,6 @@
use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon}; use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon};
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::{FrontendNode, FrontendNodeLink, FrontendNodeType}; use crate::messages::portfolio::document::node_graph::utility_types::{FrontendNode, FrontendNodeLink, FrontendNodeType};
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer}; use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::utility_types::HintData; use crate::messages::tool::utility_types::HintData;
@ -9,10 +9,8 @@ use graph_craft::document::NodeId;
use graphene_core::raster::color::Color; use graphene_core::raster::color::Color;
use graphene_core::text::Font; use graphene_core::text::Font;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Frontend)] #[impl_message(Message, Frontend)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendMessage { pub enum FrontendMessage {
// Display prefix: make the frontend show something, like a dialog // Display prefix: make the frontend show something, like a dialog
DisplayDialog { DisplayDialog {

View file

@ -1,9 +1,7 @@
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize}; #[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, specta::Type)]
pub struct FrontendDocumentDetails { pub struct FrontendDocumentDetails {
#[serde(rename = "isAutoSaved")] #[serde(rename = "isAutoSaved")]
pub is_auto_saved: bool, pub is_auto_saved: bool,
@ -13,7 +11,7 @@ pub struct FrontendDocumentDetails {
pub id: DocumentId, pub id: DocumentId,
} }
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum MouseCursorIcon { pub enum MouseCursorIcon {
#[default] #[default]
Default, Default,
@ -31,7 +29,7 @@ pub enum MouseCursorIcon {
Rotate, Rotate,
} }
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FileType { pub enum FileType {
#[default] #[default]
Png, Png,
@ -49,7 +47,7 @@ impl FileType {
} }
} }
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum ExportBounds { pub enum ExportBounds {
#[default] #[default]
AllArtwork, AllArtwork,

View file

@ -1,10 +1,8 @@
use crate::messages::portfolio::utility_types::Platform; use crate::messages::portfolio::utility_types::Platform;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Globals)] #[impl_message(Message, Globals)]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum GlobalsMessage { pub enum GlobalsMessage {
SetPlatform { platform: Platform }, SetPlatform { platform: Platform },
} }

View file

@ -40,9 +40,9 @@ pub fn default_mapping() -> Mapping {
refresh_keys=[Control], refresh_keys=[Control],
action_dispatch=NavigationMessage::PointerMove { snap_angle: Control, wait_for_snap_angle_release: true, snap_zoom: Control, zoom_from_viewport: None }, action_dispatch=NavigationMessage::PointerMove { snap_angle: Control, wait_for_snap_angle_release: true, snap_zoom: Control, zoom_from_viewport: None },
), ),
entry!(KeyDown(Lmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Lmb }), entry!(KeyDown(Lmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Lmb }),
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Mmb }), entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Mmb }),
entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Rmb }), entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Rmb }),
// =============== // ===============
// NORMAL PRIORITY // NORMAL PRIORITY
// =============== // ===============

View file

@ -1,10 +1,9 @@
use crate::messages::input_mapper::utility_types::{input_keyboard::Key, input_mouse::MouseButton}; use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::input_mapper::utility_types::input_mouse::MouseButton;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, KeyMappingMessage, Lookup)] #[impl_message(Message, KeyMappingMessage, Lookup)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
pub enum InputMapperMessage { pub enum InputMapperMessage {
// Sub-messages // Sub-messages
#[child] #[child]

View file

@ -6,13 +6,20 @@ use crate::messages::prelude::*;
use std::fmt::Write; use std::fmt::Write;
pub struct InputMapperMessageData<'a> {
pub input: &'a InputPreprocessorMessageHandler,
pub actions: ActionList,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct InputMapperMessageHandler { pub struct InputMapperMessageHandler {
mapping: Mapping, mapping: Mapping,
} }
impl MessageHandler<InputMapperMessage, (&InputPreprocessorMessageHandler, ActionList)> for InputMapperMessageHandler { impl MessageHandler<InputMapperMessage, InputMapperMessageData<'_>> for InputMapperMessageHandler {
fn process_message(&mut self, message: InputMapperMessage, responses: &mut VecDeque<Message>, (input, actions): (&InputPreprocessorMessageHandler, ActionList)) { fn process_message(&mut self, message: InputMapperMessage, responses: &mut VecDeque<Message>, data: InputMapperMessageData) {
let InputMapperMessageData { input, actions } = data;
if let Some(message) = self.mapping.match_input_message(message, &input.keyboard, actions) { if let Some(message) = self.mapping.match_input_message(message, &input.keyboard, actions) {
responses.add(message); responses.add(message);
} }

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, KeyMapping)] #[impl_message(Message, KeyMapping)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
pub enum KeyMappingMessage { pub enum KeyMappingMessage {
#[child] #[child]
Lookup(InputMapperMessage), Lookup(InputMapperMessage),
@ -12,7 +10,7 @@ pub enum KeyMappingMessage {
} }
#[impl_message(Message, KeyMappingMessage, ModifyMapping)] #[impl_message(Message, KeyMappingMessage, ModifyMapping)]
#[derive(PartialEq, Eq, Clone, Debug, Default, Hash, Serialize, Deserialize)] #[derive(PartialEq, Eq, Clone, Debug, Default, Hash, serde::Serialize, serde::Deserialize)]
pub enum MappingVariant { pub enum MappingVariant {
#[default] #[default]
Default, Default,

View file

@ -1,15 +1,23 @@
use crate::messages::input_mapper::input_mapper_message_handler::InputMapperMessageData;
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup; use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::prelude::*; use crate::messages::prelude::*;
pub struct KeyMappingMessageData<'a> {
pub input: &'a InputPreprocessorMessageHandler,
pub actions: ActionList,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct KeyMappingMessageHandler { pub struct KeyMappingMessageHandler {
mapping_handler: InputMapperMessageHandler, mapping_handler: InputMapperMessageHandler,
} }
impl MessageHandler<KeyMappingMessage, (&InputPreprocessorMessageHandler, ActionList)> for KeyMappingMessageHandler { impl MessageHandler<KeyMappingMessage, KeyMappingMessageData<'_>> for KeyMappingMessageHandler {
fn process_message(&mut self, message: KeyMappingMessage, responses: &mut VecDeque<Message>, data: (&InputPreprocessorMessageHandler, ActionList)) { fn process_message(&mut self, message: KeyMappingMessage, responses: &mut VecDeque<Message>, data: KeyMappingMessageData) {
let KeyMappingMessageData { input, actions } = data;
match message { match message {
KeyMappingMessage::Lookup(input) => self.mapping_handler.process_message(input, responses, data), KeyMappingMessage::Lookup(input_message) => self.mapping_handler.process_message(input_message, responses, InputMapperMessageData { input, actions }),
KeyMappingMessage::ModifyMapping(new_layout) => self.mapping_handler.set_mapping(new_layout.into()), KeyMappingMessage::ModifyMapping(new_layout) => self.mapping_handler.set_mapping(new_layout.into()),
} }
} }

View file

@ -4,4 +4,4 @@ mod key_mapping_message_handler;
#[doc(inline)] #[doc(inline)]
pub use key_mapping_message::{KeyMappingMessage, KeyMappingMessageDiscriminant, MappingVariant, MappingVariantDiscriminant}; pub use key_mapping_message::{KeyMappingMessage, KeyMappingMessageDiscriminant, MappingVariant, MappingVariantDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use key_mapping_message_handler::KeyMappingMessageHandler; pub use key_mapping_message_handler::{KeyMappingMessageData, KeyMappingMessageHandler};

View file

@ -8,4 +8,4 @@ pub mod utility_types;
#[doc(inline)] #[doc(inline)]
pub use input_mapper_message::{InputMapperMessage, InputMapperMessageDiscriminant}; pub use input_mapper_message::{InputMapperMessage, InputMapperMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use input_mapper_message_handler::InputMapperMessageHandler; pub use input_mapper_message_handler::{InputMapperMessageData, InputMapperMessageHandler};

View file

@ -2,7 +2,7 @@ use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign}; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
@ -31,7 +31,7 @@ pub enum KeyPosition {
} }
bitflags! { bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
#[repr(transparent)] #[repr(transparent)]
#[serde(transparent)] #[serde(transparent)]
pub struct ModifierKeys: u8 { pub struct ModifierKeys: u8 {
@ -49,7 +49,7 @@ 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 // (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 `+`. // 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)] #[impl_message(Message, InputMapperMessage, KeyDown)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, specta::Type, num_enum::TryFromPrimitive)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type, num_enum::TryFromPrimitive)]
#[repr(u8)] #[repr(u8)]
pub enum Key { pub enum Key {
// Writing system keys // Writing system keys
@ -305,7 +305,7 @@ impl From<Key> for LayoutKey {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
struct LayoutKey { struct LayoutKey {
key: String, key: String,
label: String, label: String,
@ -328,7 +328,7 @@ impl Serialize for Key {
pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize; pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize;
/// Only `Key`s that exist on a physical keyboard should be used. /// Only `Key`s that exist on a physical keyboard should be used.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct KeysGroup(pub Vec<Key>); pub struct KeysGroup(pub Vec<Key>);
impl fmt::Display for KeysGroup { impl fmt::Display for KeysGroup {
@ -366,7 +366,7 @@ impl From<KeysGroup> for String {
} }
} }
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LayoutKeysGroup(Vec<LayoutKey>); pub struct LayoutKeysGroup(Vec<LayoutKey>);
impl From<KeysGroup> for LayoutKeysGroup { impl From<KeysGroup> for LayoutKeysGroup {
@ -375,7 +375,7 @@ impl From<KeysGroup> for LayoutKeysGroup {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum MouseMotion { pub enum MouseMotion {
None, None,
Lmb, Lmb,

View file

@ -3,14 +3,14 @@ use crate::messages::prelude::*;
use bitflags::bitflags; use bitflags::bitflags;
use glam::DVec2; use glam::DVec2;
use serde::{Deserialize, Serialize};
use std::collections::VecDeque; use std::collections::VecDeque;
// Origin is top left // Origin is top left
pub type ViewportPosition = DVec2; pub type ViewportPosition = DVec2;
pub type EditorPosition = DVec2; pub type EditorPosition = DVec2;
#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct ViewportBounds { pub struct ViewportBounds {
pub top_left: DVec2, pub top_left: DVec2,
pub bottom_right: DVec2, pub bottom_right: DVec2,
@ -37,7 +37,7 @@ impl ViewportBounds {
} }
} }
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
pub struct ScrollDelta { pub struct ScrollDelta {
// TODO: Switch these to `f64` values (not trivial because floats don't provide PartialEq, Eq, and Hash) // TODO: Switch these to `f64` values (not trivial because floats don't provide PartialEq, Eq, and Hash)
pub x: i32, pub x: i32,
@ -60,7 +60,7 @@ impl ScrollDelta {
} }
} }
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct MouseState { pub struct MouseState {
pub position: ViewportPosition, pub position: ViewportPosition,
pub mouse_keys: MouseKeys, pub mouse_keys: MouseKeys,
@ -98,7 +98,7 @@ impl MouseState {
} }
} }
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct EditorMouseState { pub struct EditorMouseState {
pub editor_position: EditorPosition, pub editor_position: EditorPosition,
pub mouse_keys: MouseKeys, pub mouse_keys: MouseKeys,
@ -138,7 +138,7 @@ impl EditorMouseState {
} }
bitflags! { bitflags! {
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(transparent)] #[repr(transparent)]
pub struct MouseKeys: u8 { pub struct MouseKeys: u8 {
const LEFT = 0b0000_0001; const LEFT = 0b0000_0001;
@ -148,7 +148,7 @@ bitflags! {
} }
#[impl_message(Message, InputMapperMessage, DoubleClick)] #[impl_message(Message, InputMapperMessage, DoubleClick)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, specta::Type, num_enum::TryFromPrimitive)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type, num_enum::TryFromPrimitive)]
#[repr(u8)] #[repr(u8)]
pub enum MouseButton { pub enum MouseButton {
Left, Left,

View file

@ -5,7 +5,6 @@ use crate::messages::input_mapper::utility_types::input_mouse::NUMBER_OF_MOUSE_B
use crate::messages::prelude::*; use crate::messages::prelude::*;
use core::time::Duration; use core::time::Duration;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Mapping { pub struct Mapping {
@ -118,7 +117,7 @@ pub struct MappingEntry {
pub modifiers: KeyStates, pub modifiers: KeyStates,
} }
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum ActionKeys { pub enum ActionKeys {
Action(MessageDiscriminant), Action(MessageDiscriminant),
#[serde(rename = "keys")] #[serde(rename = "keys")]
@ -148,7 +147,7 @@ impl ActionKeys {
} }
} }
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct FrameTimeInfo { pub struct FrameTimeInfo {
timestamp: Duration, timestamp: Duration,
prev_timestamp: Option<Duration>, prev_timestamp: Option<Duration>,

View file

@ -3,10 +3,9 @@ use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState
use crate::messages::prelude::*; use crate::messages::prelude::*;
use core::time::Duration; use core::time::Duration;
use serde::{Deserialize, Serialize};
#[impl_message(Message, InputPreprocessor)] #[impl_message(Message, InputPreprocessor)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum InputPreprocessorMessage { pub enum InputPreprocessorMessage {
BoundsOfViewports { bounds_of_viewports: Vec<ViewportBounds> }, BoundsOfViewports { bounds_of_viewports: Vec<ViewportBounds> },
DoubleClick { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys }, DoubleClick { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },

View file

@ -6,6 +6,10 @@ use crate::messages::prelude::*;
use glam::DVec2; use glam::DVec2;
pub struct InputPreprocessorMessageData {
pub keyboard_platform: KeyboardPlatformLayout,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct InputPreprocessorMessageHandler { pub struct InputPreprocessorMessageHandler {
pub frame_time: FrameTimeInfo, pub frame_time: FrameTimeInfo,
@ -14,8 +18,10 @@ pub struct InputPreprocessorMessageHandler {
pub viewport_bounds: ViewportBounds, pub viewport_bounds: ViewportBounds,
} }
impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputPreprocessorMessageHandler { impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for InputPreprocessorMessageHandler {
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, keyboard_platform: KeyboardPlatformLayout) { fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, data: InputPreprocessorMessageData) {
let InputPreprocessorMessageData { keyboard_platform } = data;
match message { match message {
InputPreprocessorMessage::BoundsOfViewports { bounds_of_viewports } => { InputPreprocessorMessage::BoundsOfViewports { bounds_of_viewports } => {
assert_eq!(bounds_of_viewports.len(), 1, "Only one viewport is currently supported"); assert_eq!(bounds_of_viewports.len(), 1, "Only one viewport is currently supported");
@ -189,7 +195,10 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
input_preprocessor.process_message(message, &mut responses, KeyboardPlatformLayout::Standard); let data = InputPreprocessorMessageData {
keyboard_platform: KeyboardPlatformLayout::Standard,
};
input_preprocessor.process_message(message, &mut responses, data);
assert!(input_preprocessor.keyboard.get(Key::Alt as usize)); assert!(input_preprocessor.keyboard.get(Key::Alt as usize));
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Alt).into())); assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Alt).into()));
@ -205,7 +214,10 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
input_preprocessor.process_message(message, &mut responses, KeyboardPlatformLayout::Standard); let data = InputPreprocessorMessageData {
keyboard_platform: KeyboardPlatformLayout::Standard,
};
input_preprocessor.process_message(message, &mut responses, data);
assert!(input_preprocessor.keyboard.get(Key::Control as usize)); assert!(input_preprocessor.keyboard.get(Key::Control as usize));
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Control).into())); assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Control).into()));
@ -221,7 +233,10 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
input_preprocessor.process_message(message, &mut responses, KeyboardPlatformLayout::Standard); let data = InputPreprocessorMessageData {
keyboard_platform: KeyboardPlatformLayout::Standard,
};
input_preprocessor.process_message(message, &mut responses, data);
assert!(input_preprocessor.keyboard.get(Key::Shift as usize)); assert!(input_preprocessor.keyboard.get(Key::Shift as usize));
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Shift).into())); assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Shift).into()));
@ -239,7 +254,10 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
input_preprocessor.process_message(message, &mut responses, KeyboardPlatformLayout::Standard); let data = InputPreprocessorMessageData {
keyboard_platform: KeyboardPlatformLayout::Standard,
};
input_preprocessor.process_message(message, &mut responses, data);
assert!(!input_preprocessor.keyboard.get(Key::Control as usize)); assert!(!input_preprocessor.keyboard.get(Key::Control as usize));
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyUp(Key::Control).into())); assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyUp(Key::Control).into()));
@ -256,7 +274,10 @@ mod test {
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
input_preprocessor.process_message(message, &mut responses, KeyboardPlatformLayout::Standard); let data = InputPreprocessorMessageData {
keyboard_platform: KeyboardPlatformLayout::Standard,
};
input_preprocessor.process_message(message, &mut responses, data);
assert!(input_preprocessor.keyboard.get(Key::Control as usize)); assert!(input_preprocessor.keyboard.get(Key::Control as usize));
assert!(input_preprocessor.keyboard.get(Key::Shift as usize)); assert!(input_preprocessor.keyboard.get(Key::Shift as usize));

View file

@ -4,4 +4,4 @@ mod input_preprocessor_message_handler;
#[doc(inline)] #[doc(inline)]
pub use input_preprocessor_message::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant}; pub use input_preprocessor_message::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use input_preprocessor_message_handler::InputPreprocessorMessageHandler; pub use input_preprocessor_message_handler::{InputPreprocessorMessageData, InputPreprocessorMessageHandler};

View file

@ -1,10 +1,8 @@
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Layout)] #[impl_message(Message, Layout)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum LayoutMessage { pub enum LayoutMessage {
ResendActiveWidget { ResendActiveWidget {
layout_target: LayoutTarget, layout_target: LayoutTarget,

View file

@ -272,9 +272,8 @@ impl LayoutMessageHandler {
impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage, F> for LayoutMessageHandler { impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage, F> for LayoutMessageHandler {
fn process_message(&mut self, message: LayoutMessage, responses: &mut std::collections::VecDeque<Message>, action_input_mapping: F) { fn process_message(&mut self, message: LayoutMessage, responses: &mut std::collections::VecDeque<Message>, action_input_mapping: F) {
use LayoutMessage::*;
match message { match message {
ResendActiveWidget { layout_target, widget_id } => { LayoutMessage::ResendActiveWidget { layout_target, widget_id } => {
// Find the updated diff based on the specified layout target // Find the updated diff based on the specified layout target
let Some(diff) = (match &self.layouts[layout_target as usize] { let Some(diff) = (match &self.layouts[layout_target as usize] {
Layout::MenuLayout(_) => return, Layout::MenuLayout(_) => return,
@ -289,15 +288,15 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
// Resend that diff // Resend that diff
self.send_diff(vec![diff], layout_target, responses, &action_input_mapping); self.send_diff(vec![diff], layout_target, responses, &action_input_mapping);
} }
SendLayout { layout, layout_target } => { LayoutMessage::SendLayout { layout, layout_target } => {
self.diff_and_send_layout_to_frontend(layout_target, layout, responses, &action_input_mapping); self.diff_and_send_layout_to_frontend(layout_target, layout, responses, &action_input_mapping);
} }
WidgetValueCommit { layout_target, widget_id, value } => { LayoutMessage::WidgetValueCommit { layout_target, widget_id, value } => {
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Commit, responses); self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Commit, responses);
} }
WidgetValueUpdate { layout_target, widget_id, value } => { LayoutMessage::WidgetValueUpdate { layout_target, widget_id, value } => {
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Update, responses); self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Update, responses);
responses.add(ResendActiveWidget { layout_target, widget_id }); responses.add(LayoutMessage::ResendActiveWidget { layout_target, widget_id });
} }
} }
} }

View file

@ -7,7 +7,6 @@ use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::input_mapper::utility_types::misc::ActionKeys; use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
#[repr(transparent)] #[repr(transparent)]
@ -20,7 +19,7 @@ impl core::fmt::Display for WidgetId {
} }
} }
#[derive(PartialEq, Clone, Debug, Hash, Eq, Copy, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, Hash, Eq, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
#[repr(u8)] #[repr(u8)]
pub enum LayoutTarget { pub enum LayoutTarget {
/// Contains the action buttons at the bottom of the dialog. Must be shown with the `FrontendMessage::DisplayDialog` message. /// Contains the action buttons at the bottom of the dialog. Must be shown with the `FrontendMessage::DisplayDialog` message.
@ -100,7 +99,7 @@ pub trait DialogLayoutHolder: LayoutHolder {
} }
/// Wraps a choice of layout type. The chosen layout contains an arrangement of widgets mounted somewhere specific in the frontend. /// Wraps a choice of layout type. The chosen layout contains an arrangement of widgets mounted somewhere specific in the frontend.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum Layout { pub enum Layout {
WidgetLayout(WidgetLayout), WidgetLayout(WidgetLayout),
MenuLayout(MenuLayout), MenuLayout(MenuLayout),
@ -157,7 +156,7 @@ impl Default for Layout {
} }
} }
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, specta::Type)] #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq, specta::Type)]
pub struct WidgetLayout { pub struct WidgetLayout {
pub layout: SubLayout, pub layout: SubLayout,
} }
@ -290,7 +289,7 @@ impl<'a> Iterator for WidgetIterMut<'a> {
pub type SubLayout = Vec<LayoutGroup>; pub type SubLayout = Vec<LayoutGroup>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum LayoutGroup { pub enum LayoutGroup {
#[serde(rename = "column")] #[serde(rename = "column")]
Column { Column {
@ -420,7 +419,7 @@ impl LayoutGroup {
} }
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WidgetHolder { pub struct WidgetHolder {
#[serde(rename = "widgetId")] #[serde(rename = "widgetId")]
pub widget_id: WidgetId, pub widget_id: WidgetId,
@ -472,7 +471,7 @@ impl<T> Default for WidgetCallback<T> {
} }
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum Widget { pub enum Widget {
BreadcrumbTrailButtons(BreadcrumbTrailButtons), BreadcrumbTrailButtons(BreadcrumbTrailButtons),
CheckboxInput(CheckboxInput), CheckboxInput(CheckboxInput),
@ -498,7 +497,7 @@ pub enum Widget {
} }
/// A single change to part of the UI, containing the location of the change and the new value. /// A single change to part of the UI, containing the location of the change and the new value.
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WidgetDiff { pub struct WidgetDiff {
/// A path to the change /// A path to the change
/// e.g. [0, 1, 2] in the properties panel is the first section, second row and third widget. /// e.g. [0, 1, 2] in the properties panel is the first section, second row and third widget.
@ -513,7 +512,7 @@ pub struct WidgetDiff {
/// The new value of the UI, sent as part of a diff. /// The new value of the UI, sent as part of a diff.
/// ///
/// An update can represent a single widget or an entire SubLayout, or just a single layout group. /// An update can represent a single widget or an entire SubLayout, or just a single layout group.
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum DiffUpdate { pub enum DiffUpdate {
#[serde(rename = "subLayout")] #[serde(rename = "subLayout")]
SubLayout(SubLayout), SubLayout(SubLayout),

View file

@ -1,14 +1,13 @@
use crate::messages::input_mapper::utility_types::misc::ActionKeys; use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::FrontendGraphDataType; use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use graphene_core::raster::color::Color; use graphene_core::raster::color::Color;
use graphite_proc_macros::WidgetBuilder; use graphite_proc_macros::WidgetBuilder;
use derivative::*; use derivative::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)] #[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
pub struct IconButton { pub struct IconButton {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -36,7 +35,7 @@ pub struct IconButton {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct PopoverButton { pub struct PopoverButton {
pub style: Option<String>, pub style: Option<String>,
@ -65,7 +64,7 @@ pub struct PopoverButton {
pub tooltip_shortcut: Option<ActionKeys>, pub tooltip_shortcut: Option<ActionKeys>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
pub struct ParameterExposeButton { pub struct ParameterExposeButton {
pub exposed: bool, pub exposed: bool,
@ -88,7 +87,7 @@ pub struct ParameterExposeButton {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
pub struct TextButton { pub struct TextButton {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -123,7 +122,7 @@ pub struct TextButton {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)] #[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct ColorButton { pub struct ColorButton {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -156,7 +155,7 @@ pub struct ColorButton {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
pub struct BreadcrumbTrailButtons { pub struct BreadcrumbTrailButtons {
#[widget_builder(constructor)] #[widget_builder(constructor)]

View file

@ -6,9 +6,8 @@ use graphite_proc_macros::WidgetBuilder;
use derivative::*; use derivative::*;
use glam::DVec2; use glam::DVec2;
use serde::{Deserialize, Serialize};
#[derive(Clone, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)] #[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
pub struct CheckboxInput { pub struct CheckboxInput {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -47,7 +46,7 @@ impl Default for CheckboxInput {
} }
} }
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct DropdownInput { pub struct DropdownInput {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -76,7 +75,7 @@ pub struct DropdownInput {
pub type MenuListEntrySections = Vec<Vec<MenuListEntry>>; pub type MenuListEntrySections = Vec<Vec<MenuListEntry>>;
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
#[widget_builder(not_widget_holder)] #[widget_builder(not_widget_holder)]
pub struct MenuListEntry { pub struct MenuListEntry {
@ -106,7 +105,7 @@ pub struct MenuListEntry {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct FontInput { pub struct FontInput {
#[serde(rename = "fontFamily")] #[serde(rename = "fontFamily")]
@ -140,7 +139,7 @@ pub struct FontInput {
/// This widget allows for the flexible use of the layout system. /// This widget allows for the flexible use of the layout system.
/// In a custom layout, one can define a widget that is just used to trigger code on the backend. /// In a custom layout, one can define a widget that is just used to trigger code on the backend.
/// This is used in MenuLayout to pipe the triggering of messages from the frontend to backend. /// This is used in MenuLayout to pipe the triggering of messages from the frontend to backend.
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
pub struct InvisibleStandinInput { pub struct InvisibleStandinInput {
#[serde(skip)] #[serde(skip)]
@ -152,7 +151,7 @@ pub struct InvisibleStandinInput {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct NumberInput { pub struct NumberInput {
// Label // Label
@ -259,7 +258,7 @@ impl NumberInput {
} }
} }
#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq, Eq, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Eq, specta::Type)]
pub enum NumberInputIncrementBehavior { pub enum NumberInputIncrementBehavior {
#[default] #[default]
Add, Add,
@ -267,14 +266,14 @@ pub enum NumberInputIncrementBehavior {
Callback, Callback,
} }
#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq, Eq, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Eq, specta::Type)]
pub enum NumberInputMode { pub enum NumberInputMode {
#[default] #[default]
Increment, Increment,
Range, Range,
} }
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)] #[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
pub struct RadioInput { pub struct RadioInput {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -290,7 +289,7 @@ pub struct RadioInput {
pub min_width: u32, pub min_width: u32,
} }
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)] #[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
#[widget_builder(not_widget_holder)] #[widget_builder(not_widget_holder)]
pub struct RadioEntryData { pub struct RadioEntryData {
@ -316,7 +315,7 @@ pub struct RadioEntryData {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct WorkingColorsInput { pub struct WorkingColorsInput {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -326,7 +325,7 @@ pub struct WorkingColorsInput {
pub secondary: Color, pub secondary: Color,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct TextAreaInput { pub struct TextAreaInput {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -348,7 +347,7 @@ pub struct TextAreaInput {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct TextInput { pub struct TextInput {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -375,7 +374,7 @@ pub struct TextInput {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)] #[derivative(Debug, PartialEq, Default)]
pub struct CurveInput { pub struct CurveInput {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -395,7 +394,7 @@ pub struct CurveInput {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)] #[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)] #[derivative(Debug, PartialEq)]
pub struct PivotInput { pub struct PivotInput {
#[widget_builder(constructor)] #[widget_builder(constructor)]
@ -413,7 +412,7 @@ pub struct PivotInput {
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
} }
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Default, PartialEq, Eq, specta::Type)] #[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Eq, specta::Type)]
pub enum PivotPosition { pub enum PivotPosition {
#[default] #[default]
None, None,

View file

@ -1,8 +1,7 @@
use derivative::*; use derivative::*;
use graphite_proc_macros::WidgetBuilder; use graphite_proc_macros::WidgetBuilder;
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize, Derivative, Debug, Default, PartialEq, Eq, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, Default, PartialEq, Eq, WidgetBuilder, specta::Type)]
pub struct IconLabel { pub struct IconLabel {
#[widget_builder(constructor)] #[widget_builder(constructor)]
pub icon: String, pub icon: String,
@ -12,7 +11,7 @@ pub struct IconLabel {
pub tooltip: String, pub tooltip: String,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, Debug, Default, PartialEq, Eq, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, Default, PartialEq, Eq, WidgetBuilder, specta::Type)]
pub struct ImageLabel { pub struct ImageLabel {
#[widget_builder(constructor)] #[widget_builder(constructor)]
pub image: String, pub image: String,
@ -24,7 +23,7 @@ pub struct ImageLabel {
pub tooltip: String, pub tooltip: String,
} }
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, WidgetBuilder, specta::Type)] #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
pub struct Separator { pub struct Separator {
pub direction: SeparatorDirection, pub direction: SeparatorDirection,
@ -33,14 +32,14 @@ pub struct Separator {
pub separator_type: SeparatorType, pub separator_type: SeparatorType,
} }
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum SeparatorDirection { pub enum SeparatorDirection {
#[default] #[default]
Horizontal, Horizontal,
Vertical, Vertical,
} }
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum SeparatorType { pub enum SeparatorType {
Related, Related,
#[default] #[default]
@ -48,7 +47,7 @@ pub enum SeparatorType {
Section, Section,
} }
#[derive(Clone, Serialize, Deserialize, Derivative, Debug, PartialEq, Eq, Default, WidgetBuilder, specta::Type)] #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, PartialEq, Eq, Default, WidgetBuilder, specta::Type)]
pub struct TextLabel { pub struct TextLabel {
pub disabled: bool, pub disabled: bool,

View file

@ -3,11 +3,9 @@ use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
use super::input_widgets::InvisibleStandinInput; use super::input_widgets::InvisibleStandinInput;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, specta::Type)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Default, specta::Type)]
pub struct MenuBarEntryChildren(pub Vec<Vec<MenuBarEntry>>); pub struct MenuBarEntryChildren(pub Vec<Vec<MenuBarEntry>>);
impl MenuBarEntryChildren { impl MenuBarEntryChildren {
@ -29,7 +27,7 @@ impl MenuBarEntryChildren {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, specta::Type)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, specta::Type)]
pub struct MenuBarEntry { pub struct MenuBarEntry {
pub label: String, pub label: String,
pub icon: Option<String>, pub icon: Option<String>,
@ -71,7 +69,7 @@ impl Default for MenuBarEntry {
} }
} }
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, specta::Type)] #[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct MenuLayout { pub struct MenuLayout {
pub layout: Vec<MenuBarEntry>, pub layout: Vec<MenuBarEntry>,
} }

View file

@ -2,10 +2,8 @@ use crate::messages::prelude::*;
use graphite_proc_macros::*; use graphite_proc_macros::*;
use serde::{Deserialize, Serialize};
#[impl_message] #[impl_message]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Message { pub enum Message {
NoOp, NoOp,
Init, Init,

View file

@ -11,22 +11,22 @@ use graphene_core::vector::style::ViewMode;
use graphene_core::Color; use graphene_core::Color;
use glam::DAffine2; use glam::DAffine2;
use serde::{Deserialize, Serialize};
#[impl_message(Message, PortfolioMessage, Document)] #[impl_message(Message, PortfolioMessage, Document)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum DocumentMessage { pub enum DocumentMessage {
Noop, Noop,
// Sub-messages // Sub-messages
#[child] #[child]
GraphOperation(GraphOperationMessage),
#[child]
Navigation(NavigationMessage), Navigation(NavigationMessage),
#[child] #[child]
NodeGraph(NodeGraphMessage),
#[child]
Overlays(OverlaysMessage), Overlays(OverlaysMessage),
#[child] #[child]
PropertiesPanel(PropertiesPanelMessage), PropertiesPanel(PropertiesPanelMessage),
#[child]
NodeGraph(NodeGraphMessage),
#[child]
GraphOperation(GraphOperationMessage),
// Messages // Messages
AbortTransaction, AbortTransaction,

View file

@ -5,7 +5,8 @@ use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH};
use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING}; use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING};
use crate::messages::input_mapper::utility_types::macros::action_keys; use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::{GraphOperationHandlerData, NodeGraphHandlerData}; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::NodeGraphHandlerData;
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options}; use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData; use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData;
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
@ -29,19 +30,26 @@ use graphene_core::{concrete, generic, ProtoNodeIdentifier};
use graphene_std::wasm_application_io::WasmEditorApi; use graphene_std::wasm_application_io::WasmEditorApi;
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize};
use std::vec; use std::vec;
#[derive(Clone, Debug, Serialize, Deserialize)] pub struct DocumentMessageData<'a> {
pub document_id: DocumentId,
pub ipp: &'a InputPreprocessorMessageHandler,
pub persistent_data: &'a PersistentData,
pub executor: &'a mut NodeGraphExecutor,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct DocumentMessageHandler { pub struct DocumentMessageHandler {
// ====================== // ======================
// Child message handlers // Child message handlers
// ====================== // ======================
#[serde(skip)] #[serde(skip)]
node_graph_handler: NodeGraphMessageHandler,
#[serde(skip)]
navigation_handler: NavigationMessageHandler, navigation_handler: NavigationMessageHandler,
#[serde(skip)] #[serde(skip)]
node_graph_handler: NodeGraphMessageHandler,
#[serde(skip)]
overlays_message_handler: OverlaysMessageHandler, overlays_message_handler: OverlaysMessageHandler,
#[serde(skip)] #[serde(skip)]
properties_panel_message_handler: PropertiesPanelMessageHandler, properties_panel_message_handler: PropertiesPanelMessageHandler,
@ -92,172 +100,34 @@ pub struct DocumentMessageHandler {
pub metadata: DocumentMetadata, pub metadata: DocumentMetadata,
} }
impl Default for DocumentMessageHandler { impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessageHandler {
fn default() -> Self { fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, data: DocumentMessageData) {
Self { let DocumentMessageData {
// ======================
// Child message handlers
// ======================
node_graph_handler: Default::default(),
navigation_handler: NavigationMessageHandler::default(),
overlays_message_handler: OverlaysMessageHandler::default(),
properties_panel_message_handler: PropertiesPanelMessageHandler,
// ============================================
// Fields that are saved in the document format
// ============================================
network: root_network(),
selected_nodes: SelectedNodes::default(),
collapsed: CollapsedLayers::default(),
name: DEFAULT_DOCUMENT_NAME.to_string(),
commit_hash: GRAPHITE_GIT_COMMIT_HASH.to_string(),
navigation: PTZ::default(),
document_mode: DocumentMode::DesignMode,
view_mode: ViewMode::default(),
overlays_visible: true,
rulers_visible: true,
// =============================================
// Fields omitted from the saved document format
// =============================================
document_undo_history: VecDeque::new(),
document_redo_history: VecDeque::new(),
saved_hash: None,
auto_saved_hash: None,
undo_in_progress: false,
graph_view_overlay_open: false,
snapping_state: SnappingState::default(),
layer_range_selection_reference: None,
metadata: Default::default(),
}
}
}
#[inline(always)]
fn default_network() -> NodeNetwork {
DocumentMessageHandler::default().network
}
#[inline(always)]
fn default_selected_nodes() -> SelectedNodes {
DocumentMessageHandler::default().selected_nodes
}
#[inline(always)]
fn default_collapsed() -> CollapsedLayers {
DocumentMessageHandler::default().collapsed
}
#[inline(always)]
fn default_name() -> String {
DocumentMessageHandler::default().name
}
#[inline(always)]
fn default_commit_hash() -> String {
DocumentMessageHandler::default().commit_hash
}
#[inline(always)]
fn default_pan_tilt_zoom() -> PTZ {
DocumentMessageHandler::default().navigation
}
#[inline(always)]
fn default_document_mode() -> DocumentMode {
DocumentMessageHandler::default().document_mode
}
#[inline(always)]
fn default_view_mode() -> ViewMode {
DocumentMessageHandler::default().view_mode
}
#[inline(always)]
fn default_overlays_visible() -> bool {
DocumentMessageHandler::default().overlays_visible
}
#[inline(always)]
fn default_rulers_visible() -> bool {
DocumentMessageHandler::default().rulers_visible
}
fn root_network() -> NodeNetwork {
{
let mut network = NodeNetwork::default();
let node = graph_craft::document::DocumentNode {
name: "Output".into(),
inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true), NodeInput::Network(concrete!(WasmEditorApi))],
implementation: graph_craft::document::DocumentNodeImplementation::Network(NodeNetwork {
imports: vec![NodeId(3), NodeId(0)],
exports: vec![NodeOutput::new(NodeId(3), 0)],
nodes: [
DocumentNode {
name: "EditorApi".to_string(),
inputs: vec![NodeInput::Network(concrete!(WasmEditorApi))],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode")),
..Default::default()
},
DocumentNode {
name: "Create Canvas".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
name: "Cache".to_string(),
manual_composition: Some(concrete!(())),
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
..Default::default()
},
DocumentNode {
name: "RenderNode".to_string(),
inputs: vec![
NodeInput::node(NodeId(0), 0),
NodeInput::Network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T)))),
NodeInput::node(NodeId(2), 0),
],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _, _>")),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
metadata: DocumentNodeMetadata::position((8, 4)),
..Default::default()
};
network.push_node(node);
network
}
}
pub struct DocumentInputs<'a> {
pub document_id: DocumentId,
pub ipp: &'a InputPreprocessorMessageHandler,
pub persistent_data: &'a PersistentData,
pub executor: &'a mut NodeGraphExecutor,
}
impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHandler {
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, document_inputs: DocumentInputs) {
let DocumentInputs {
document_id, document_id,
ipp, ipp,
persistent_data, persistent_data,
executor, executor,
} = document_inputs; } = data;
use DocumentMessage::*;
match message { match message {
// Sub-messages // Sub-messages
Navigation(message) => { DocumentMessage::Navigation(message) => {
let document_bounds = self.metadata().document_bounds_viewport_space(); let document_bounds = self.metadata().document_bounds_viewport_space();
self.navigation_handler.process_message( let data = NavigationMessageData {
message, metadata: &self.metadata,
responses, document_bounds,
(&self.metadata, document_bounds, ipp, self.selected_visible_layers_bounding_box_viewport(), &mut self.navigation), ipp,
); selection_bounds: self.selected_visible_layers_bounding_box_viewport(),
ptz: &mut self.navigation,
};
self.navigation_handler.process_message(message, responses, data);
} }
Overlays(message) => { DocumentMessage::Overlays(message) => {
self.overlays_message_handler.process_message(message, responses, (self.overlays_visible, ipp)); let overlays_visible = self.overlays_visible;
self.overlays_message_handler.process_message(message, responses, OverlaysMessageData { overlays_visible, ipp });
} }
PropertiesPanel(message) => { DocumentMessage::PropertiesPanel(message) => {
let properties_panel_message_handler_data = PropertiesPanelMessageHandlerData { let properties_panel_message_handler_data = PropertiesPanelMessageHandlerData {
node_graph_message_handler: &self.node_graph_handler, node_graph_message_handler: &self.node_graph_handler,
executor, executor,
@ -269,7 +139,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
self.properties_panel_message_handler self.properties_panel_message_handler
.process_message(message, responses, (persistent_data, properties_panel_message_handler_data)); .process_message(message, responses, (persistent_data, properties_panel_message_handler_data));
} }
NodeGraph(message) => { DocumentMessage::NodeGraph(message) => {
self.node_graph_handler.process_message( self.node_graph_handler.process_message(
message, message,
responses, responses,
@ -285,26 +155,26 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}, },
); );
} }
GraphOperation(message) => GraphOperationMessageHandler.process_message( DocumentMessage::GraphOperation(message) => {
message, let data = GraphOperationMessageData {
responses,
GraphOperationHandlerData {
document_network: &mut self.network, document_network: &mut self.network,
document_metadata: &mut self.metadata, document_metadata: &mut self.metadata,
selected_nodes: &mut self.selected_nodes, selected_nodes: &mut self.selected_nodes,
collapsed: &mut self.collapsed, collapsed: &mut self.collapsed,
node_graph: &mut self.node_graph_handler, node_graph: &mut self.node_graph_handler,
}, };
), let mut graph_operation_message_handler = GraphOperationMessageHandler {};
graph_operation_message_handler.process_message(message, responses, data);
}
// Messages // Messages
AbortTransaction => { DocumentMessage::AbortTransaction => {
if !self.undo_in_progress { if !self.undo_in_progress {
self.undo(responses); self.undo(responses);
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
} }
} }
AlignSelectedLayers { axis, aggregate } => { DocumentMessage::AlignSelectedLayers { axis, aggregate } => {
self.backup(responses); self.backup(responses);
let axis = match axis { let axis = match axis {
@ -338,12 +208,12 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}); });
} }
} }
BackupDocument { network } => self.backup_with_document(network, responses), DocumentMessage::BackupDocument { network } => self.backup_with_document(network, responses),
ClearArtboards => { DocumentMessage::ClearArtboards => {
self.backup(responses); self.backup(responses);
responses.add(GraphOperationMessage::ClearArtboards); responses.add(GraphOperationMessage::ClearArtboards);
} }
ClearLayersPanel => { DocumentMessage::ClearLayersPanel => {
// Send an empty layer list // Send an empty layer list
let data_buffer: RawBuffer = Self::default().serialize_root(); let data_buffer: RawBuffer = Self::default().serialize_root();
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer }); responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
@ -354,8 +224,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
layout_target: LayoutTarget::LayersPanelOptions, layout_target: LayoutTarget::LayersPanelOptions,
}); });
} }
CommitTransaction => (), DocumentMessage::CommitTransaction => (),
CreateEmptyFolder => { DocumentMessage::CreateEmptyFolder => {
let id = NodeId(generate_uuid()); let id = NodeId(generate_uuid());
let parent = self let parent = self
@ -378,14 +248,14 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}); });
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
} }
DebugPrintDocument => { DocumentMessage::DebugPrintDocument => {
info!("{:#?}", self.network); info!("{:#?}", self.network);
} }
DeleteLayer { id } => { DocumentMessage::DeleteLayer { id } => {
responses.add(GraphOperationMessage::DeleteLayer { id }); responses.add(GraphOperationMessage::DeleteLayer { id });
responses.add_front(BroadcastEvent::ToolAbort); responses.add_front(BroadcastEvent::ToolAbort);
} }
DeleteSelectedLayers => { DocumentMessage::DeleteSelectedLayers => {
self.backup(responses); self.backup(responses);
responses.add_front(BroadcastEvent::SelectionChanged); responses.add_front(BroadcastEvent::SelectionChanged);
@ -393,20 +263,20 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add_front(DocumentMessage::DeleteLayer { id: path.last().unwrap().to_node() }); responses.add_front(DocumentMessage::DeleteLayer { id: path.last().unwrap().to_node() });
} }
} }
DeselectAllLayers => { DocumentMessage::DeselectAllLayers => {
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
self.layer_range_selection_reference = None; self.layer_range_selection_reference = None;
} }
DocumentHistoryBackward => self.undo_with_history(responses), DocumentMessage::DocumentHistoryBackward => self.undo_with_history(responses),
DocumentHistoryForward => self.redo_with_history(responses), DocumentMessage::DocumentHistoryForward => self.redo_with_history(responses),
DocumentStructureChanged => { DocumentMessage::DocumentStructureChanged => {
self.update_layers_panel_options_bar_widgets(responses); self.update_layers_panel_options_bar_widgets(responses);
self.metadata.load_structure(&self.network, &mut self.selected_nodes); self.metadata.load_structure(&self.network, &mut self.selected_nodes);
let data_buffer: RawBuffer = self.serialize_root(); let data_buffer: RawBuffer = self.serialize_root();
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer }); responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
} }
DuplicateSelectedLayers => { DocumentMessage::DuplicateSelectedLayers => {
self.backup(responses); self.backup(responses);
for layer_ancestors in self.metadata.shallowest_unique_layers(self.selected_nodes.selected_layers(&self.metadata)) { for layer_ancestors in self.metadata.shallowest_unique_layers(self.selected_nodes.selected_layers(&self.metadata)) {
let Some(layer) = layer_ancestors.last().copied() else { continue }; let Some(layer) = layer_ancestors.last().copied() else { continue };
@ -437,7 +307,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}); });
} }
} }
FlipSelectedLayers { flip_axis } => { DocumentMessage::FlipSelectedLayers { flip_axis } => {
self.backup(responses); self.backup(responses);
let scale = match flip_axis { let scale = match flip_axis {
FlipAxis::X => DVec2::new(-1., 1.), FlipAxis::X => DVec2::new(-1., 1.),
@ -456,7 +326,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
} }
} }
} }
GraphViewOverlay { open } => { DocumentMessage::GraphViewOverlay { open } => {
self.graph_view_overlay_open = open; self.graph_view_overlay_open = open;
if open { if open {
@ -464,25 +334,25 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
} }
responses.add(FrontendMessage::TriggerGraphViewOverlay { open }); responses.add(FrontendMessage::TriggerGraphViewOverlay { open });
} }
GraphViewOverlayToggle => { DocumentMessage::GraphViewOverlayToggle => {
responses.add(DocumentMessage::GraphViewOverlay { open: !self.graph_view_overlay_open }); responses.add(DocumentMessage::GraphViewOverlay { open: !self.graph_view_overlay_open });
} }
GridOptions(grid) => { DocumentMessage::GridOptions(grid) => {
self.snapping_state.grid = grid; self.snapping_state.grid = grid;
self.snapping_state.grid_snapping = true; self.snapping_state.grid_snapping = true;
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
} }
GridOverlays(mut overlay_context) => { DocumentMessage::GridOverlays(mut overlay_context) => {
if self.snapping_state.grid_snapping { if self.snapping_state.grid_snapping {
grid_overlay(self, &mut overlay_context) grid_overlay(self, &mut overlay_context)
} }
} }
GridVisible(enabled) => { DocumentMessage::GridVisible(enabled) => {
self.snapping_state.grid_snapping = enabled; self.snapping_state.grid_snapping = enabled;
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
} }
GroupSelectedLayers => { DocumentMessage::GroupSelectedLayers => {
let parent = self let parent = self
.metadata() .metadata()
.deepest_common_ancestor(self.selected_nodes.selected_layers(self.metadata()), true) .deepest_common_ancestor(self.selected_nodes.selected_layers(self.metadata()), true)
@ -521,8 +391,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}); });
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![folder_id] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![folder_id] });
} }
ImaginateGenerate => responses.add(PortfolioMessage::SubmitGraphRender { document_id }), DocumentMessage::ImaginateGenerate => responses.add(PortfolioMessage::SubmitGraphRender { document_id }),
ImaginateRandom { imaginate_node, then_generate } => { DocumentMessage::ImaginateRandom { imaginate_node, then_generate } => {
// Generate a random seed. We only want values between -2^53 and 2^53, because integer values // Generate a random seed. We only want values between -2^53 and 2^53, because integer values
// outside of this range can get rounded in f64 // outside of this range can get rounded in f64
let random_bits = generate_uuid(); let random_bits = generate_uuid();
@ -542,7 +412,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(DocumentMessage::ImaginateGenerate); responses.add(DocumentMessage::ImaginateGenerate);
} }
} }
ImportSvg { DocumentMessage::ImportSvg {
id, id,
svg, svg,
transform, transform,
@ -558,7 +428,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
insert_index, insert_index,
}); });
} }
MoveSelectedLayersTo { parent, insert_index } => { DocumentMessage::MoveSelectedLayersTo { parent, insert_index } => {
let selected_layers = self.selected_nodes.selected_layers(self.metadata()).collect::<Vec<_>>(); let selected_layers = self.selected_nodes.selected_layers(self.metadata()).collect::<Vec<_>>();
// Disallow trying to insert into self // Disallow trying to insert into self
@ -576,7 +446,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
insert_index, insert_index,
}); });
} }
NudgeSelectedLayers { DocumentMessage::NudgeSelectedLayers {
delta_x, delta_x,
delta_y, delta_y,
resize, resize,
@ -628,7 +498,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}; };
} }
} }
PasteImage { image, mouse } => { DocumentMessage::PasteImage { image, mouse } => {
// All the image's pixels have been converted to 0..=1, linear, and premultiplied by `Color::from_rgba8_srgb` // All the image's pixels have been converted to 0..=1, linear, and premultiplied by `Color::from_rgba8_srgb`
let image_size = DVec2::new(image.width as f64, image.height as f64); let image_size = DVec2::new(image.width as f64, image.height as f64);
@ -665,7 +535,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
// Force chosen tool to be Select Tool after importing image. // Force chosen tool to be Select Tool after importing image.
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select }); responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select });
} }
PasteSvg { svg, mouse } => { DocumentMessage::PasteSvg { svg, mouse } => {
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
let viewport_location = mouse.map_or(ipp.viewport_bounds.center() + ipp.viewport_bounds.top_left, |pos| pos.into()); let viewport_location = mouse.map_or(ipp.viewport_bounds.center() + ipp.viewport_bounds.top_left, |pos| pos.into());
let center_in_viewport = DAffine2::from_translation(self.metadata().document_to_viewport.inverse().transform_point2(viewport_location - ipp.viewport_bounds.top_left)); let center_in_viewport = DAffine2::from_translation(self.metadata().document_to_viewport.inverse().transform_point2(viewport_location - ipp.viewport_bounds.top_left));
@ -673,18 +543,18 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select }); responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select });
} }
Redo => { DocumentMessage::Redo => {
responses.add(SelectToolMessage::Abort); responses.add(SelectToolMessage::Abort);
responses.add(DocumentMessage::DocumentHistoryForward); responses.add(DocumentMessage::DocumentHistoryForward);
responses.add(ToolMessage::Redo); responses.add(ToolMessage::Redo);
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
} }
RenameDocument { new_name } => { DocumentMessage::RenameDocument { new_name } => {
self.name = new_name; self.name = new_name;
responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(NodeGraphMessage::UpdateNewNodeGraph); responses.add(NodeGraphMessage::UpdateNewNodeGraph);
} }
RenderRulers => { DocumentMessage::RenderRulers => {
let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom); let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom);
let ruler_origin = self.metadata().document_to_viewport.transform_point2(DVec2::ZERO); let ruler_origin = self.metadata().document_to_viewport.transform_point2(DVec2::ZERO);
@ -699,7 +569,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
visible: self.rulers_visible, visible: self.rulers_visible,
}); });
} }
RenderScrollbars => { DocumentMessage::RenderScrollbars => {
let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom); let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom);
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT; let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
@ -720,7 +590,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
multiplier: scrollbar_multiplier.into(), multiplier: scrollbar_multiplier.into(),
}); });
} }
SaveDocument => { DocumentMessage::SaveDocument => {
self.set_save_state(true); self.set_save_state(true);
responses.add(PortfolioMessage::AutoSaveActiveDocument); responses.add(PortfolioMessage::AutoSaveActiveDocument);
// Update the save status of the just saved document // Update the save status of the just saved document
@ -735,28 +605,28 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
name, name,
}) })
} }
SelectAllLayers => { DocumentMessage::SelectAllLayers => {
let metadata = self.metadata(); let metadata = self.metadata();
let all_layers_except_artboards = metadata.all_layers().filter(move |&layer| !metadata.is_artboard(layer)); let all_layers_except_artboards = metadata.all_layers().filter(move |&layer| !metadata.is_artboard(layer));
let nodes = all_layers_except_artboards.map(|layer| layer.to_node()).collect(); let nodes = all_layers_except_artboards.map(|layer| layer.to_node()).collect();
responses.add(NodeGraphMessage::SelectedNodesSet { nodes }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
} }
SelectedLayersLower => { DocumentMessage::SelectedLayersLower => {
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: 1 }); responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: 1 });
} }
SelectedLayersLowerToBack => { DocumentMessage::SelectedLayersLowerToBack => {
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MAX }); responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MAX });
} }
SelectedLayersRaise => { DocumentMessage::SelectedLayersRaise => {
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: -1 }); responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: -1 });
} }
SelectedLayersRaiseToFront => { DocumentMessage::SelectedLayersRaiseToFront => {
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MIN }); responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MIN });
} }
SelectedLayersReorder { relative_index_offset } => { DocumentMessage::SelectedLayersReorder { relative_index_offset } => {
self.selected_layers_reorder(relative_index_offset, responses); self.selected_layers_reorder(relative_index_offset, responses);
} }
SelectLayer { id, ctrl, shift } => { DocumentMessage::SelectLayer { id, ctrl, shift } => {
let layer = LayerNodeIdentifier::new(id, self.network()); let layer = LayerNodeIdentifier::new(id, self.network());
let mut nodes = vec![]; let mut nodes = vec![];
@ -800,13 +670,13 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
} }
} }
} }
SetBlendModeForSelectedLayers { blend_mode } => { DocumentMessage::SetBlendModeForSelectedLayers { blend_mode } => {
self.backup(responses); self.backup(responses);
for layer in self.selected_nodes.selected_layers_except_artboards(self.metadata()) { for layer in self.selected_nodes.selected_layers_except_artboards(self.metadata()) {
responses.add(GraphOperationMessage::BlendModeSet { layer, blend_mode }); responses.add(GraphOperationMessage::BlendModeSet { layer, blend_mode });
} }
} }
SetOpacityForSelectedLayers { opacity } => { DocumentMessage::SetOpacityForSelectedLayers { opacity } => {
self.backup(responses); self.backup(responses);
let opacity = opacity.clamp(0., 1.); let opacity = opacity.clamp(0., 1.);
@ -814,15 +684,15 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(GraphOperationMessage::OpacitySet { layer, opacity }); responses.add(GraphOperationMessage::OpacitySet { layer, opacity });
} }
} }
SetOverlaysVisibility { visible } => { DocumentMessage::SetOverlaysVisibility { visible } => {
self.overlays_visible = visible; self.overlays_visible = visible;
responses.add(BroadcastEvent::ToolAbort); responses.add(BroadcastEvent::ToolAbort);
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
} }
SetRangeSelectionLayer { new_layer } => { DocumentMessage::SetRangeSelectionLayer { new_layer } => {
self.layer_range_selection_reference = new_layer; self.layer_range_selection_reference = new_layer;
} }
SetSnapping { DocumentMessage::SetSnapping {
snapping_enabled, snapping_enabled,
bounding_box_snapping, bounding_box_snapping,
geometry_snapping, geometry_snapping,
@ -837,12 +707,12 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
self.snapping_state.geometry_snapping = state self.snapping_state.geometry_snapping = state
}; };
} }
SetViewMode { view_mode } => { DocumentMessage::SetViewMode { view_mode } => {
self.view_mode = view_mode; self.view_mode = view_mode;
responses.add_front(NodeGraphMessage::RunDocumentGraph); responses.add_front(NodeGraphMessage::RunDocumentGraph);
} }
StartTransaction => self.backup(responses), DocumentMessage::StartTransaction => self.backup(responses),
ToggleLayerExpansion { id } => { DocumentMessage::ToggleLayerExpansion { id } => {
let layer = LayerNodeIdentifier::new(id, self.network()); let layer = LayerNodeIdentifier::new(id, self.network());
if self.collapsed.0.contains(&layer) { if self.collapsed.0.contains(&layer) {
self.collapsed.0.retain(|&collapsed_layer| collapsed_layer != layer); self.collapsed.0.retain(|&collapsed_layer| collapsed_layer != layer);
@ -851,7 +721,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
} }
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
} }
Undo => { DocumentMessage::Undo => {
self.undo_in_progress = true; self.undo_in_progress = true;
responses.add(ToolMessage::PreUndo); responses.add(ToolMessage::PreUndo);
responses.add(DocumentMessage::DocumentHistoryBackward); responses.add(DocumentMessage::DocumentHistoryBackward);
@ -859,8 +729,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(DocumentMessage::UndoFinished); responses.add(DocumentMessage::UndoFinished);
responses.add(ToolMessage::Undo); responses.add(ToolMessage::Undo);
} }
UndoFinished => self.undo_in_progress = false, DocumentMessage::UndoFinished => self.undo_in_progress = false,
UngroupSelectedLayers => { DocumentMessage::UngroupSelectedLayers => {
responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::StartTransaction);
let folder_paths = self.metadata().folders_sorted_by_most_nested(self.selected_nodes.selected_layers(self.metadata())); let folder_paths = self.metadata().folders_sorted_by_most_nested(self.selected_nodes.selected_layers(self.metadata()));
@ -911,25 +781,25 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
} }
responses.add(DocumentMessage::CommitTransaction); responses.add(DocumentMessage::CommitTransaction);
} }
UpdateDocumentTransform { transform } => { DocumentMessage::UpdateDocumentTransform { transform } => {
self.metadata.document_to_viewport = transform; self.metadata.document_to_viewport = transform;
responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderRulers);
responses.add(DocumentMessage::RenderScrollbars); responses.add(DocumentMessage::RenderScrollbars);
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
} }
ZoomCanvasTo100Percent => { DocumentMessage::ZoomCanvasTo100Percent => {
responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 1. }); responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 1. });
} }
ZoomCanvasTo200Percent => { DocumentMessage::ZoomCanvasTo200Percent => {
responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 2. }); responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 2. });
} }
ZoomCanvasToFitAll => { DocumentMessage::ZoomCanvasToFitAll => {
if let Some(bounds) = self.metadata().document_bounds_document_space(true) { if let Some(bounds) = self.metadata().document_bounds_document_space(true) {
responses.add(NavigationMessage::SetCanvasTilt { angle_radians: 0. }); responses.add(NavigationMessage::SetCanvasTilt { angle_radians: 0. });
responses.add(NavigationMessage::FitViewportToBounds { bounds, prevent_zoom_past_100: true }); responses.add(NavigationMessage::FitViewportToBounds { bounds, prevent_zoom_past_100: true });
} }
} }
Noop => (), DocumentMessage::Noop => (),
} }
} }
@ -1603,3 +1473,138 @@ impl DocumentMessageHandler {
common common
} }
} }
impl Default for DocumentMessageHandler {
fn default() -> Self {
Self {
// ======================
// Child message handlers
// ======================
navigation_handler: NavigationMessageHandler::default(),
node_graph_handler: NodeGraphMessageHandler::default(),
overlays_message_handler: OverlaysMessageHandler::default(),
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
// ============================================
// Fields that are saved in the document format
// ============================================
network: root_network(),
selected_nodes: SelectedNodes::default(),
collapsed: CollapsedLayers::default(),
name: DEFAULT_DOCUMENT_NAME.to_string(),
commit_hash: GRAPHITE_GIT_COMMIT_HASH.to_string(),
navigation: PTZ::default(),
document_mode: DocumentMode::DesignMode,
view_mode: ViewMode::default(),
overlays_visible: true,
rulers_visible: true,
// =============================================
// Fields omitted from the saved document format
// =============================================
document_undo_history: VecDeque::new(),
document_redo_history: VecDeque::new(),
saved_hash: None,
auto_saved_hash: None,
undo_in_progress: false,
graph_view_overlay_open: false,
snapping_state: SnappingState::default(),
layer_range_selection_reference: None,
metadata: Default::default(),
}
}
}
#[inline(always)]
fn default_network() -> NodeNetwork {
DocumentMessageHandler::default().network
}
#[inline(always)]
fn default_selected_nodes() -> SelectedNodes {
DocumentMessageHandler::default().selected_nodes
}
#[inline(always)]
fn default_collapsed() -> CollapsedLayers {
DocumentMessageHandler::default().collapsed
}
#[inline(always)]
fn default_name() -> String {
DocumentMessageHandler::default().name
}
#[inline(always)]
fn default_commit_hash() -> String {
DocumentMessageHandler::default().commit_hash
}
#[inline(always)]
fn default_pan_tilt_zoom() -> PTZ {
DocumentMessageHandler::default().navigation
}
#[inline(always)]
fn default_document_mode() -> DocumentMode {
DocumentMessageHandler::default().document_mode
}
#[inline(always)]
fn default_view_mode() -> ViewMode {
DocumentMessageHandler::default().view_mode
}
#[inline(always)]
fn default_overlays_visible() -> bool {
DocumentMessageHandler::default().overlays_visible
}
#[inline(always)]
fn default_rulers_visible() -> bool {
DocumentMessageHandler::default().rulers_visible
}
fn root_network() -> NodeNetwork {
{
let mut network = NodeNetwork::default();
let node = graph_craft::document::DocumentNode {
name: "Output".into(),
inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true), NodeInput::Network(concrete!(WasmEditorApi))],
implementation: graph_craft::document::DocumentNodeImplementation::Network(NodeNetwork {
imports: vec![NodeId(3), NodeId(0)],
exports: vec![NodeOutput::new(NodeId(3), 0)],
nodes: [
DocumentNode {
name: "EditorApi".to_string(),
inputs: vec![NodeInput::Network(concrete!(WasmEditorApi))],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode")),
..Default::default()
},
DocumentNode {
name: "Create Canvas".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::CreateSurfaceNode")),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
name: "Cache".to_string(),
manual_composition: Some(concrete!(())),
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")),
..Default::default()
},
DocumentNode {
name: "RenderNode".to_string(),
inputs: vec![
NodeInput::node(NodeId(0), 0),
NodeInput::Network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T)))),
NodeInput::node(NodeId(2), 0),
],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _, _>")),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
metadata: DocumentNodeMetadata::position((8, 4)),
..Default::default()
};
network.push_node(node);
network
}
}

View file

@ -1,3 +1,5 @@
use super::utility_types::TransformIn;
use super::utility_types::VectorDataModification;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::prelude::*; use crate::messages::prelude::*;
@ -10,7 +12,6 @@ use graphene_core::text::Font;
use graphene_core::uuid::ManipulatorGroupId; use graphene_core::uuid::ManipulatorGroupId;
use graphene_core::vector::brush_stroke::BrushStroke; use graphene_core::vector::brush_stroke::BrushStroke;
use graphene_core::vector::style::{Fill, Stroke}; use graphene_core::vector::style::{Fill, Stroke};
use graphene_core::vector::ManipulatorPointId;
use graphene_core::{Artboard, Color}; use graphene_core::{Artboard, Color};
use glam::{DAffine2, DVec2, IVec2}; use glam::{DAffine2, DVec2, IVec2};
@ -39,7 +40,6 @@ pub enum GraphOperationMessage {
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
stroke: Stroke, stroke: Stroke,
}, },
TransformChange { TransformChange {
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
transform: DAffine2, transform: DAffine2,
@ -56,7 +56,6 @@ pub enum GraphOperationMessage {
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
pivot: DVec2, pivot: DVec2,
}, },
Vector { Vector {
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
modification: VectorDataModification, modification: VectorDataModification,
@ -65,7 +64,6 @@ pub enum GraphOperationMessage {
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
strokes: Vec<BrushStroke>, strokes: Vec<BrushStroke>,
}, },
NewArtboard { NewArtboard {
id: NodeId, id: NodeId,
artboard: Artboard, artboard: Artboard,
@ -117,26 +115,3 @@ pub enum GraphOperationMessage {
insert_index: isize, insert_index: isize,
}, },
} }
#[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub enum TransformIn {
Local,
Scope { scope: DAffine2 },
Viewport,
}
type ManipulatorGroup = bezier_rs::ManipulatorGroup<ManipulatorGroupId>;
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum VectorDataModification {
AddEndManipulatorGroup { subpath_index: usize, manipulator_group: ManipulatorGroup },
AddManipulatorGroup { manipulator_group: ManipulatorGroup, after_id: ManipulatorGroupId },
AddStartManipulatorGroup { subpath_index: usize, manipulator_group: ManipulatorGroup },
RemoveManipulatorGroup { id: ManipulatorGroupId },
RemoveManipulatorPoint { point: ManipulatorPointId },
SetClosed { index: usize, closed: bool },
SetManipulatorColinearHandlesState { id: ManipulatorGroupId, colinear: bool },
SetManipulatorPosition { point: ManipulatorPointId, position: DVec2 },
ToggleManipulatorColinearHandlesState { id: ManipulatorGroupId },
UpdateSubpaths { subpaths: Vec<Subpath<ManipulatorGroupId>> },
}

View file

@ -0,0 +1,456 @@
use super::transform_utils::{self, LayerBounds};
use super::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, SelectedNodes};
use crate::messages::prelude::*;
use bezier_rs::{ManipulatorGroup, Subpath};
use graph_craft::document::{generate_uuid, NodeId, NodeInput, NodeNetwork};
use graphene_core::renderer::Quad;
use graphene_core::text::Font;
use graphene_core::uuid::ManipulatorGroupId;
use graphene_core::vector::style::{Fill, Gradient, GradientType, LineCap, LineJoin, Stroke};
use graphene_core::Color;
use glam::{DAffine2, DVec2, IVec2};
pub struct GraphOperationMessageData<'a> {
pub document_network: &'a mut NodeNetwork,
pub document_metadata: &'a mut DocumentMetadata,
pub selected_nodes: &'a mut SelectedNodes,
pub collapsed: &'a mut CollapsedLayers,
pub node_graph: &'a mut NodeGraphMessageHandler,
}
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub struct GraphOperationMessageHandler {}
impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for GraphOperationMessageHandler {
fn process_message(&mut self, message: GraphOperationMessage, responses: &mut VecDeque<Message>, data: GraphOperationMessageData) {
let GraphOperationMessageData {
document_network,
document_metadata,
selected_nodes,
collapsed,
node_graph,
} = data;
match message {
GraphOperationMessage::FillSet { layer, fill } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.fill_set(fill);
}
}
GraphOperationMessage::OpacitySet { layer, opacity } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.opacity_set(opacity);
}
}
GraphOperationMessage::BlendModeSet { layer, blend_mode } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.blend_mode_set(blend_mode);
}
}
GraphOperationMessage::UpdateBounds { layer, old_bounds, new_bounds } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.update_bounds(old_bounds, new_bounds);
}
}
GraphOperationMessage::StrokeSet { layer, stroke } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.stroke_set(stroke);
}
}
GraphOperationMessage::TransformChange {
layer,
transform,
transform_in,
skip_rerender,
} => {
let parent_transform = document_metadata.downstream_transform_to_viewport(layer);
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.transform_change(transform, transform_in, parent_transform, bounds, skip_rerender);
}
}
GraphOperationMessage::TransformSet {
layer,
transform,
transform_in,
skip_rerender,
} => {
let parent_transform = document_metadata.downstream_transform_to_viewport(layer);
let current_transform = Some(document_metadata.transform_to_viewport(layer));
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.transform_set(transform, transform_in, parent_transform, current_transform, bounds, skip_rerender);
}
}
GraphOperationMessage::TransformSetPivot { layer, pivot } => {
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.pivot_set(pivot, bounds);
}
}
GraphOperationMessage::Vector { layer, modification } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.vector_modify(modification);
}
}
GraphOperationMessage::Brush { layer, strokes } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.brush_modify(strokes);
}
}
GraphOperationMessage::NewArtboard { id, artboard } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.document_network.original_outputs()[0].node_id, 0, 0) {
modify_inputs.insert_artboard(artboard, layer);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::NewBitmapLayer {
id,
image_frame,
parent,
insert_index,
} => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
modify_inputs.insert_image_data(image_frame, layer);
}
}
GraphOperationMessage::NewCustomLayer {
id,
nodes,
parent,
insert_index,
alias,
} => {
trace!("Inserting new layer {id} as a child of {parent:?} at index {insert_index}");
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
let new_ids: HashMap<_, _> = nodes.iter().map(|(&id, _)| (id, NodeId(generate_uuid()))).collect();
if let Some(node) = modify_inputs.document_network.nodes.get_mut(&id) {
node.alias = alias.clone();
}
let shift = nodes
.get(&NodeId(0))
.and_then(|node| {
modify_inputs
.document_network
.nodes
.get(&layer)
.map(|layer| layer.metadata.position - node.metadata.position + IVec2::new(-8, 0))
})
.unwrap_or_default();
for (old_id, mut document_node) in nodes {
// Shift copied node
document_node.metadata.position += shift;
// Get the new, non-conflicting id
let node_id = *new_ids.get(&old_id).unwrap();
document_node = document_node.map_ids(NodeGraphMessageHandler::default_node_input, &new_ids);
// Insert node into network
modify_inputs.document_network.nodes.insert(node_id, document_node);
}
if let Some(layer_node) = modify_inputs.document_network.nodes.get_mut(&layer) {
if let Some(&input) = new_ids.get(&NodeId(0)) {
layer_node.inputs[0] = NodeInput::node(input, 0)
}
}
modify_inputs.responses.add(NodeGraphMessage::RunDocumentGraph);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
modify_inputs.insert_vector_data(subpaths, layer);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::NewTextLayer {
id,
text,
font,
size,
parent,
insert_index,
} => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
modify_inputs.insert_text(text, font, size, layer);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(id, document_network, document_metadata, node_graph, responses) {
modify_inputs.resize_artboard(location, dimensions);
}
}
GraphOperationMessage::DeleteLayer { id } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
modify_inputs.delete_layer(id, selected_nodes, false);
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::DeleteArtboard { id } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(artboard_id) = modify_inputs.document_network.nodes.get(&id).and_then(|node| node.inputs[0].as_node()) {
modify_inputs.delete_artboard(artboard_id, selected_nodes);
} else {
warn!("Artboard does not exist");
}
modify_inputs.delete_layer(id, selected_nodes, true);
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::ClearArtboards => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
let layer_nodes = modify_inputs.document_network.nodes.iter().filter(|(_, node)| node.is_layer()).map(|(id, _)| *id).collect::<Vec<_>>();
for layer in layer_nodes {
let artboards = modify_inputs
.document_network
.upstream_flow_back_from_nodes(vec![layer], true)
.filter_map(|(node, _id)| if node.is_artboard() { Some(_id) } else { None })
.collect::<Vec<_>>();
if artboards.is_empty() {
continue;
}
for artboard in artboards {
modify_inputs.delete_artboard(artboard, selected_nodes);
}
modify_inputs.delete_layer(layer, selected_nodes, true);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::NewSvg {
id,
svg,
transform,
parent,
insert_index,
} => {
let tree = match usvg::Tree::from_str(&svg, &usvg::Options::default()) {
Ok(t) => t,
Err(e) => {
responses.add(DocumentMessage::DocumentHistoryBackward);
responses.add(DialogMessage::DisplayDialogError {
title: "SVG parsing failed".to_string(),
description: e.to_string(),
});
return;
}
};
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
import_usvg_node(&mut modify_inputs, &usvg::Node::Group(Box::new(tree.root)), transform, id, parent, insert_index);
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
}
}
fn actions(&self) -> ActionList {
actions!(GraphOperationMessage;)
}
}
pub fn load_network_structure(document_network: &NodeNetwork, document_metadata: &mut DocumentMetadata, selected_nodes: &mut SelectedNodes, collapsed: &mut CollapsedLayers) {
document_metadata.load_structure(document_network, selected_nodes);
collapsed.0.retain(|&layer| document_metadata.layer_exists(layer));
}
fn usvg_color(c: usvg::Color, a: f32) -> Color {
Color::from_rgbaf32_unchecked(c.red as f32 / 255., c.green as f32 / 255., c.blue as f32 / 255., a)
}
fn usvg_transform(c: usvg::Transform) -> DAffine2 {
DAffine2::from_cols_array(&[c.sx as f64, c.ky as f64, c.kx as f64, c.sy as f64, c.tx as f64, c.ty as f64])
}
fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node, transform: DAffine2, id: NodeId, parent: LayerNodeIdentifier, insert_index: isize) {
let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) else {
return;
};
modify_inputs.layer_node = Some(layer);
match node {
usvg::Node::Group(group) => {
for child in &group.children {
import_usvg_node(modify_inputs, child, transform, NodeId(generate_uuid()), LayerNodeIdentifier::new_unchecked(layer), -1);
}
modify_inputs.layer_node = Some(layer);
}
usvg::Node::Path(path) => {
let subpaths = convert_usvg_path(path);
let bounds = subpaths.iter().filter_map(|subpath| subpath.bounding_box()).reduce(Quad::combine_bounds).unwrap_or_default();
let transformed_bounds = subpaths
.iter()
.filter_map(|subpath| subpath.bounding_box_with_transform(transform * usvg_transform(node.abs_transform())))
.reduce(Quad::combine_bounds)
.unwrap_or_default();
modify_inputs.insert_vector_data(subpaths, layer);
let center = DAffine2::from_translation((bounds[0] + bounds[1]) / 2.);
modify_inputs.modify_inputs("Transform", true, |inputs, _node_id, _metadata| {
transform_utils::update_transform(inputs, center.inverse() * transform * usvg_transform(node.abs_transform()) * center);
});
let bounds_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
let transformed_bound_transform = DAffine2::from_scale_angle_translation(transformed_bounds[1] - transformed_bounds[0], 0., transformed_bounds[0]);
apply_usvg_fill(
&path.fill,
modify_inputs,
transform * usvg_transform(node.abs_transform()),
bounds_transform,
transformed_bound_transform,
);
apply_usvg_stroke(&path.stroke, modify_inputs);
}
usvg::Node::Image(_image) => {
warn!("Skip image")
}
usvg::Node::Text(text) => {
let font = Font::new(crate::consts::DEFAULT_FONT_FAMILY.to_string(), crate::consts::DEFAULT_FONT_STYLE.to_string());
modify_inputs.insert_text(text.chunks.iter().map(|chunk| chunk.text.clone()).collect(), font, 24., layer);
modify_inputs.fill_set(Fill::Solid(Color::BLACK));
}
}
}
fn apply_usvg_stroke(stroke: &Option<usvg::Stroke>, modify_inputs: &mut ModifyInputsContext) {
if let Some(stroke) = stroke {
if let usvg::Paint::Color(color) = &stroke.paint {
modify_inputs.stroke_set(Stroke {
color: Some(usvg_color(*color, stroke.opacity.get())),
weight: stroke.width.get() as f64,
dash_lengths: stroke.dasharray.as_ref().map(|lengths| lengths.iter().map(|&length| length as f64).collect()).unwrap_or_default(),
dash_offset: stroke.dashoffset as f64,
line_cap: match stroke.linecap {
usvg::LineCap::Butt => LineCap::Butt,
usvg::LineCap::Round => LineCap::Round,
usvg::LineCap::Square => LineCap::Square,
},
line_join: match stroke.linejoin {
usvg::LineJoin::Miter => LineJoin::Miter,
usvg::LineJoin::MiterClip => LineJoin::Miter,
usvg::LineJoin::Round => LineJoin::Round,
usvg::LineJoin::Bevel => LineJoin::Bevel,
},
line_join_miter_limit: stroke.miterlimit.get() as f64,
})
} else {
warn!("Skip non-solid stroke")
}
}
}
fn apply_usvg_fill(fill: &Option<usvg::Fill>, modify_inputs: &mut ModifyInputsContext, transform: DAffine2, bounds_transform: DAffine2, transformed_bound_transform: DAffine2) {
if let Some(fill) = &fill {
modify_inputs.fill_set(match &fill.paint {
usvg::Paint::Color(color) => Fill::solid(usvg_color(*color, fill.opacity.get())),
usvg::Paint::LinearGradient(linear) => {
let local = [DVec2::new(linear.x1 as f64, linear.y1 as f64), DVec2::new(linear.x2 as f64, linear.y2 as f64)];
let to_doc_transform = if linear.base.units == usvg::Units::UserSpaceOnUse {
transform
} else {
transformed_bound_transform
};
let to_doc = to_doc_transform * usvg_transform(linear.transform);
let document = [to_doc.transform_point2(local[0]), to_doc.transform_point2(local[1])];
let layer = [transform.inverse().transform_point2(document[0]), transform.inverse().transform_point2(document[1])];
let [start, end] = [bounds_transform.inverse().transform_point2(layer[0]), bounds_transform.inverse().transform_point2(layer[1])];
Fill::Gradient(Gradient {
start,
end,
transform: DAffine2::IDENTITY,
gradient_type: GradientType::Linear,
positions: linear.stops.iter().map(|stop| (stop.offset.get() as f64, usvg_color(stop.color, stop.opacity.get()))).collect(),
})
}
usvg::Paint::RadialGradient(radial) => {
let local = [DVec2::new(radial.cx as f64, radial.cy as f64), DVec2::new(radial.fx as f64, radial.fy as f64)];
let to_doc_transform = if radial.base.units == usvg::Units::UserSpaceOnUse {
transform
} else {
transformed_bound_transform
};
let to_doc = to_doc_transform * usvg_transform(radial.transform);
let document = [to_doc.transform_point2(local[0]), to_doc.transform_point2(local[1])];
let layer = [transform.inverse().transform_point2(document[0]), transform.inverse().transform_point2(document[1])];
let [start, end] = [bounds_transform.inverse().transform_point2(layer[0]), bounds_transform.inverse().transform_point2(layer[1])];
Fill::Gradient(Gradient {
start,
end,
transform: DAffine2::IDENTITY,
gradient_type: GradientType::Radial,
positions: radial.stops.iter().map(|stop| (stop.offset.get() as f64, usvg_color(stop.color, stop.opacity.get()))).collect(),
})
}
usvg::Paint::Pattern(_) => {
warn!("Skip pattern");
return;
}
});
}
}
fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<ManipulatorGroupId>> {
let mut subpaths = Vec::new();
let mut groups = Vec::new();
let mut points = path.data.points().iter();
let to_vec = |p: &usvg::tiny_skia_path::Point| DVec2::new(p.x as f64, p.y as f64);
for verb in path.data.verbs() {
match verb {
usvg::tiny_skia_path::PathVerb::Move => {
subpaths.push(Subpath::new(std::mem::take(&mut groups), false));
let Some(start) = points.next().map(to_vec) else { continue };
groups.push(ManipulatorGroup::new(start, Some(start), Some(start)));
}
usvg::tiny_skia_path::PathVerb::Line => {
let Some(end) = points.next().map(to_vec) else { continue };
groups.push(ManipulatorGroup::new(end, Some(end), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Quad => {
let Some(handle) = points.next().map(to_vec) else { continue };
let Some(end) = points.next().map(to_vec) else { continue };
if let Some(last) = groups.last_mut() {
last.out_handle = Some(last.anchor + (2. / 3.) * (handle - last.anchor));
}
groups.push(ManipulatorGroup::new(end, Some(end + (2. / 3.) * (handle - end)), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Cubic => {
let Some(first_handle) = points.next().map(to_vec) else { continue };
let Some(second_handle) = points.next().map(to_vec) else { continue };
let Some(end) = points.next().map(to_vec) else { continue };
if let Some(last) = groups.last_mut() {
last.out_handle = Some(first_handle);
}
groups.push(ManipulatorGroup::new(end, Some(second_handle), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Close => {
subpaths.push(Subpath::new(std::mem::take(&mut groups), true));
}
}
}
subpaths.push(Subpath::new(groups, false));
subpaths
}

View file

@ -0,0 +1,9 @@
mod graph_operation_message;
pub mod graph_operation_message_handler;
pub mod transform_utils;
pub mod utility_types;
#[doc(inline)]
pub use graph_operation_message::*;
#[doc(inline)]
pub use graph_operation_message_handler::*;

View file

@ -1,4 +1,3 @@
use crate::messages::portfolio::document::node_graph::VectorDataModification;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use bezier_rs::{ManipulatorGroup, Subpath}; use bezier_rs::{ManipulatorGroup, Subpath};
@ -8,6 +7,8 @@ use graphene_core::vector::{ManipulatorPointId, SelectedType};
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use super::utility_types::VectorDataModification;
/// Convert an affine transform into the tuple `(scale, angle, translation, shear)` assuming `shear.y = 0`. /// Convert an affine transform into the tuple `(scale, angle, translation, shear)` assuming `shear.y = 0`.
pub fn compute_scale_angle_translation_shear(transform: DAffine2) -> (DVec2, f64, DVec2, DVec2) { pub fn compute_scale_angle_translation_shear(transform: DAffine2) -> (DVec2, f64, DVec2, DVec2) {
let x_axis = transform.matrix2.x_axis; let x_axis = transform.matrix2.x_axis;

View file

@ -1,38 +1,58 @@
use super::{resolve_document_node_type, VectorDataModification}; use crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, SelectedNodes}; use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use bezier_rs::{ManipulatorGroup, Subpath}; use bezier_rs::Subpath;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graph_craft::document::{generate_uuid, DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput}; use graph_craft::document::{generate_uuid, DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput};
use graphene_core::raster::{BlendMode, ImageFrame}; use graphene_core::raster::{BlendMode, ImageFrame};
use graphene_core::renderer::Quad;
use graphene_core::text::Font; use graphene_core::text::Font;
use graphene_core::uuid::ManipulatorGroupId; use graphene_core::uuid::ManipulatorGroupId;
use graphene_core::vector::brush_stroke::BrushStroke; use graphene_core::vector::brush_stroke::BrushStroke;
use graphene_core::vector::style::{Fill, FillType, Gradient, GradientType, LineCap, LineJoin, Stroke}; use graphene_core::vector::style::{Fill, FillType, Stroke};
use graphene_core::{Artboard, Color}; use graphene_core::{Artboard, Color};
use transform_utils::LayerBounds; use graphene_std::vector::ManipulatorPointId;
use glam::{DAffine2, DVec2, IVec2}; use glam::{DAffine2, DVec2, IVec2};
pub mod transform_utils; use super::transform_utils::{self, LayerBounds};
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub struct GraphOperationMessageHandler; pub enum TransformIn {
Local,
struct ModifyInputsContext<'a> { Scope { scope: DAffine2 },
document_metadata: &'a mut DocumentMetadata, Viewport,
document_network: &'a mut NodeNetwork,
node_graph: &'a mut NodeGraphMessageHandler,
responses: &'a mut VecDeque<Message>,
outwards_links: HashMap<NodeId, Vec<NodeId>>,
layer_node: Option<NodeId>,
} }
type ManipulatorGroup = bezier_rs::ManipulatorGroup<ManipulatorGroupId>;
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum VectorDataModification {
AddEndManipulatorGroup { subpath_index: usize, manipulator_group: ManipulatorGroup },
AddManipulatorGroup { manipulator_group: ManipulatorGroup, after_id: ManipulatorGroupId },
AddStartManipulatorGroup { subpath_index: usize, manipulator_group: ManipulatorGroup },
RemoveManipulatorGroup { id: ManipulatorGroupId },
RemoveManipulatorPoint { point: ManipulatorPointId },
SetClosed { index: usize, closed: bool },
SetManipulatorColinearHandlesState { id: ManipulatorGroupId, colinear: bool },
SetManipulatorPosition { point: ManipulatorPointId, position: DVec2 },
ToggleManipulatorColinearHandlesState { id: ManipulatorGroupId },
UpdateSubpaths { subpaths: Vec<Subpath<ManipulatorGroupId>> },
}
pub struct ModifyInputsContext<'a> {
pub document_metadata: &'a mut DocumentMetadata,
pub document_network: &'a mut NodeNetwork,
pub node_graph: &'a mut NodeGraphMessageHandler,
pub responses: &'a mut VecDeque<Message>,
pub outwards_links: HashMap<NodeId, Vec<NodeId>>,
pub layer_node: Option<NodeId>,
}
impl<'a> ModifyInputsContext<'a> { impl<'a> ModifyInputsContext<'a> {
/// Get the node network from the document /// Get the node network from the document
fn new(document_network: &'a mut NodeNetwork, document_metadata: &'a mut DocumentMetadata, node_graph: &'a mut NodeGraphMessageHandler, responses: &'a mut VecDeque<Message>) -> Self { pub fn new(document_network: &'a mut NodeNetwork, document_metadata: &'a mut DocumentMetadata, node_graph: &'a mut NodeGraphMessageHandler, responses: &'a mut VecDeque<Message>) -> Self {
Self { Self {
outwards_links: document_network.collect_outwards_links(), outwards_links: document_network.collect_outwards_links(),
document_network, document_network,
@ -43,7 +63,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
} }
fn new_with_layer( pub fn new_with_layer(
id: NodeId, id: NodeId,
document_network: &'a mut NodeNetwork, document_network: &'a mut NodeNetwork,
document_metadata: &'a mut DocumentMetadata, document_metadata: &'a mut DocumentMetadata,
@ -62,7 +82,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
/// Updates the input of an existing node /// Updates the input of an existing node
fn modify_existing_node_inputs(&mut self, node_id: NodeId, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) { pub fn modify_existing_node_inputs(&mut self, node_id: NodeId, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let document_node = self.document_network.nodes.get_mut(&node_id).unwrap(); let document_node = self.document_network.nodes.get_mut(&node_id).unwrap();
update_input(&mut document_node.inputs, node_id, self.document_metadata); update_input(&mut document_node.inputs, node_id, self.document_metadata);
} }
@ -160,7 +180,7 @@ impl<'a> ModifyInputsContext<'a> {
new_id new_id
} }
fn create_layer_with_insert_index(&mut self, new_id: NodeId, insert_index: isize, parent: LayerNodeIdentifier) -> Option<NodeId> { pub fn create_layer_with_insert_index(&mut self, new_id: NodeId, insert_index: isize, parent: LayerNodeIdentifier) -> Option<NodeId> {
let skip_layer_nodes = if insert_index < 0 { (-1 - insert_index) as usize } else { insert_index as usize }; let skip_layer_nodes = if insert_index < 0 { (-1 - insert_index) as usize } else { insert_index as usize };
let output_node_id = if parent == LayerNodeIdentifier::ROOT { let output_node_id = if parent == LayerNodeIdentifier::ROOT {
@ -171,7 +191,7 @@ impl<'a> ModifyInputsContext<'a> {
self.create_layer(new_id, output_node_id, 0, skip_layer_nodes) self.create_layer(new_id, output_node_id, 0, skip_layer_nodes)
} }
fn insert_artboard(&mut self, artboard: Artboard, layer: NodeId) -> Option<NodeId> { pub fn insert_artboard(&mut self, artboard: Artboard, layer: NodeId) -> Option<NodeId> {
let artboard_node = resolve_document_node_type("Artboard").expect("Node").to_document_node_default_inputs( let artboard_node = resolve_document_node_type("Artboard").expect("Node").to_document_node_default_inputs(
[ [
None, None,
@ -186,7 +206,7 @@ impl<'a> ModifyInputsContext<'a> {
self.insert_node_before(NodeId(generate_uuid()), layer, 0, artboard_node, IVec2::new(-8, 0)) self.insert_node_before(NodeId(generate_uuid()), layer, 0, artboard_node, IVec2::new(-8, 0))
} }
fn insert_vector_data(&mut self, subpaths: Vec<Subpath<ManipulatorGroupId>>, layer: NodeId) { pub fn insert_vector_data(&mut self, subpaths: Vec<Subpath<ManipulatorGroupId>>, layer: NodeId) {
let shape = { let shape = {
let node_type = resolve_document_node_type("Shape").expect("Shape node does not exist"); let node_type = resolve_document_node_type("Shape").expect("Shape node does not exist");
node_type.to_document_node_default_inputs([Some(NodeInput::value(TaggedValue::Subpaths(subpaths), false))], Default::default()) node_type.to_document_node_default_inputs([Some(NodeInput::value(TaggedValue::Subpaths(subpaths), false))], Default::default())
@ -206,7 +226,7 @@ impl<'a> ModifyInputsContext<'a> {
self.responses.add(NodeGraphMessage::RunDocumentGraph); self.responses.add(NodeGraphMessage::RunDocumentGraph);
} }
fn insert_text(&mut self, text: String, font: Font, size: f64, layer: NodeId) { pub fn insert_text(&mut self, text: String, font: Font, size: f64, layer: NodeId) {
let text = resolve_document_node_type("Text").expect("Text node does not exist").to_document_node( let text = resolve_document_node_type("Text").expect("Text node does not exist").to_document_node(
[ [
NodeInput::Network(graph_craft::concrete!(graphene_std::wasm_application_io::WasmEditorApi)), NodeInput::Network(graph_craft::concrete!(graphene_std::wasm_application_io::WasmEditorApi)),
@ -231,7 +251,7 @@ impl<'a> ModifyInputsContext<'a> {
self.responses.add(NodeGraphMessage::RunDocumentGraph); self.responses.add(NodeGraphMessage::RunDocumentGraph);
} }
fn insert_image_data(&mut self, image_frame: ImageFrame<Color>, layer: NodeId) { pub fn insert_image_data(&mut self, image_frame: ImageFrame<Color>, layer: NodeId) {
let image = { let image = {
let node_type = resolve_document_node_type("Image").expect("Image node does not exist"); let node_type = resolve_document_node_type("Image").expect("Image node does not exist");
node_type.to_document_node_default_inputs([Some(NodeInput::value(TaggedValue::ImageFrame(image_frame), false))], Default::default()) node_type.to_document_node_default_inputs([Some(NodeInput::value(TaggedValue::ImageFrame(image_frame), false))], Default::default())
@ -247,7 +267,7 @@ impl<'a> ModifyInputsContext<'a> {
self.responses.add(NodeGraphMessage::RunDocumentGraph); self.responses.add(NodeGraphMessage::RunDocumentGraph);
} }
fn shift_upstream(&mut self, node_id: NodeId, shift: IVec2) { pub fn shift_upstream(&mut self, node_id: NodeId, shift: IVec2) {
let mut shift_nodes = HashSet::new(); let mut shift_nodes = HashSet::new();
let mut stack = vec![node_id]; let mut stack = vec![node_id];
while let Some(node_id) = stack.pop() { while let Some(node_id) = stack.pop() {
@ -268,7 +288,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
/// Inserts a new node and modifies the inputs /// Inserts a new node and modifies the inputs
fn modify_new_node(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) { pub fn modify_new_node(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let output_node_id = self.layer_node.unwrap_or(self.document_network.exports[0].node_id); let output_node_id = self.layer_node.unwrap_or(self.document_network.exports[0].node_id);
let Some(output_node) = self.document_network.nodes.get_mut(&output_node_id) else { let Some(output_node) = self.document_network.nodes.get_mut(&output_node_id) else {
warn!("Output node doesn't exist"); warn!("Output node doesn't exist");
@ -297,7 +317,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
/// Changes the inputs of a specific node /// Changes the inputs of a specific node
fn modify_inputs(&mut self, name: &'static str, skip_rerender: bool, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) { pub fn modify_inputs(&mut self, name: &'static str, skip_rerender: bool, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let existing_node_id = self let existing_node_id = self
.document_network .document_network
.upstream_flow_back_from_nodes( .upstream_flow_back_from_nodes(
@ -322,7 +342,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
/// Changes the inputs of a all of the existing instances of a node name /// Changes the inputs of a all of the existing instances of a node name
fn modify_all_node_inputs(&mut self, name: &'static str, skip_rerender: bool, mut update_input: impl FnMut(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) { pub fn modify_all_node_inputs(&mut self, name: &'static str, skip_rerender: bool, mut update_input: impl FnMut(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let existing_nodes: Vec<_> = self let existing_nodes: Vec<_> = self
.document_network .document_network
.upstream_flow_back_from_nodes( .upstream_flow_back_from_nodes(
@ -346,7 +366,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
} }
fn fill_set(&mut self, fill: Fill) { pub fn fill_set(&mut self, fill: Fill) {
self.modify_inputs("Fill", false, |inputs, _node_id, _metadata| { self.modify_inputs("Fill", false, |inputs, _node_id, _metadata| {
let fill_type = match fill { let fill_type = match fill {
Fill::None | Fill::Solid(_) => FillType::Solid, Fill::None | Fill::Solid(_) => FillType::Solid,
@ -367,19 +387,19 @@ impl<'a> ModifyInputsContext<'a> {
}); });
} }
fn opacity_set(&mut self, opacity: f64) { pub fn opacity_set(&mut self, opacity: f64) {
self.modify_inputs("Opacity", false, |inputs, _node_id, _metadata| { self.modify_inputs("Opacity", false, |inputs, _node_id, _metadata| {
inputs[1] = NodeInput::value(TaggedValue::F64(opacity * 100.), false); inputs[1] = NodeInput::value(TaggedValue::F64(opacity * 100.), false);
}); });
} }
fn blend_mode_set(&mut self, blend_mode: BlendMode) { pub fn blend_mode_set(&mut self, blend_mode: BlendMode) {
self.modify_inputs("Blend Mode", false, |inputs, _node_id, _metadata| { self.modify_inputs("Blend Mode", false, |inputs, _node_id, _metadata| {
inputs[1] = NodeInput::value(TaggedValue::BlendMode(blend_mode), false); inputs[1] = NodeInput::value(TaggedValue::BlendMode(blend_mode), false);
}); });
} }
fn stroke_set(&mut self, stroke: Stroke) { pub fn stroke_set(&mut self, stroke: Stroke) {
self.modify_inputs("Stroke", false, |inputs, _node_id, _metadata| { self.modify_inputs("Stroke", false, |inputs, _node_id, _metadata| {
inputs[1] = NodeInput::value(TaggedValue::OptionalColor(stroke.color), false); inputs[1] = NodeInput::value(TaggedValue::OptionalColor(stroke.color), false);
inputs[2] = NodeInput::value(TaggedValue::F64(stroke.weight), false); inputs[2] = NodeInput::value(TaggedValue::F64(stroke.weight), false);
@ -391,7 +411,7 @@ impl<'a> ModifyInputsContext<'a> {
}); });
} }
fn transform_change(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, bounds: LayerBounds, skip_rerender: bool) { pub fn transform_change(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, bounds: LayerBounds, skip_rerender: bool) {
self.modify_inputs("Transform", skip_rerender, |inputs, node_id, metadata| { self.modify_inputs("Transform", skip_rerender, |inputs, node_id, metadata| {
let layer_transform = transform_utils::get_current_transform(inputs); let layer_transform = transform_utils::get_current_transform(inputs);
let upstream_transform = metadata.upstream_transform(node_id); let upstream_transform = metadata.upstream_transform(node_id);
@ -406,7 +426,7 @@ impl<'a> ModifyInputsContext<'a> {
}); });
} }
fn transform_set(&mut self, mut transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, current_transform: Option<DAffine2>, bounds: LayerBounds, skip_rerender: bool) { pub fn transform_set(&mut self, mut transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, current_transform: Option<DAffine2>, bounds: LayerBounds, skip_rerender: bool) {
self.modify_inputs("Transform", skip_rerender, |inputs, node_id, metadata| { self.modify_inputs("Transform", skip_rerender, |inputs, node_id, metadata| {
let upstream_transform = metadata.upstream_transform(node_id); let upstream_transform = metadata.upstream_transform(node_id);
@ -428,7 +448,7 @@ impl<'a> ModifyInputsContext<'a> {
}); });
} }
fn pivot_set(&mut self, new_pivot: DVec2, bounds: LayerBounds) { pub fn pivot_set(&mut self, new_pivot: DVec2, bounds: LayerBounds) {
self.modify_inputs("Transform", false, |inputs, node_id, metadata| { self.modify_inputs("Transform", false, |inputs, node_id, metadata| {
let layer_transform = transform_utils::get_current_transform(inputs); let layer_transform = transform_utils::get_current_transform(inputs);
let upstream_transform = metadata.upstream_transform(node_id); let upstream_transform = metadata.upstream_transform(node_id);
@ -440,7 +460,7 @@ impl<'a> ModifyInputsContext<'a> {
}); });
} }
fn update_bounds(&mut self, [old_bounds_min, old_bounds_max]: [DVec2; 2], [new_bounds_min, new_bounds_max]: [DVec2; 2]) { pub fn update_bounds(&mut self, [old_bounds_min, old_bounds_max]: [DVec2; 2], [new_bounds_min, new_bounds_max]: [DVec2; 2]) {
self.modify_all_node_inputs("Transform", false, |inputs, node_id, metadata| { self.modify_all_node_inputs("Transform", false, |inputs, node_id, metadata| {
let upstream_transform = metadata.upstream_transform(node_id); let upstream_transform = metadata.upstream_transform(node_id);
let layer_transform = transform_utils::get_current_transform(inputs); let layer_transform = transform_utils::get_current_transform(inputs);
@ -456,7 +476,7 @@ impl<'a> ModifyInputsContext<'a> {
}); });
} }
fn vector_modify(&mut self, modification: VectorDataModification) { pub fn vector_modify(&mut self, modification: VectorDataModification) {
let [mut old_bounds_min, mut old_bounds_max] = [DVec2::ZERO, DVec2::ONE]; let [mut old_bounds_min, mut old_bounds_max] = [DVec2::ZERO, DVec2::ONE];
let [mut new_bounds_min, mut new_bounds_max] = [DVec2::ZERO, DVec2::ONE]; let [mut new_bounds_min, mut new_bounds_max] = [DVec2::ZERO, DVec2::ONE];
let mut empty = false; let mut empty = false;
@ -497,20 +517,20 @@ impl<'a> ModifyInputsContext<'a> {
} }
} }
fn brush_modify(&mut self, strokes: Vec<BrushStroke>) { pub fn brush_modify(&mut self, strokes: Vec<BrushStroke>) {
self.modify_inputs("Brush", false, |inputs, _node_id, _metadata| { self.modify_inputs("Brush", false, |inputs, _node_id, _metadata| {
inputs[2] = NodeInput::value(TaggedValue::BrushStrokes(strokes), false); inputs[2] = NodeInput::value(TaggedValue::BrushStrokes(strokes), false);
}); });
} }
fn resize_artboard(&mut self, location: IVec2, dimensions: IVec2) { pub fn resize_artboard(&mut self, location: IVec2, dimensions: IVec2) {
self.modify_inputs("Artboard", false, |inputs, _node_id, _metadata| { self.modify_inputs("Artboard", false, |inputs, _node_id, _metadata| {
inputs[1] = NodeInput::value(TaggedValue::IVec2(location), false); inputs[1] = NodeInput::value(TaggedValue::IVec2(location), false);
inputs[2] = NodeInput::value(TaggedValue::IVec2(dimensions), false); inputs[2] = NodeInput::value(TaggedValue::IVec2(dimensions), false);
}); });
} }
fn delete_layer(&mut self, id: NodeId, selected_nodes: &mut SelectedNodes, is_artboard_layer: bool) { pub fn delete_layer(&mut self, id: NodeId, selected_nodes: &mut SelectedNodes, is_artboard_layer: bool) {
let Some(node) = self.document_network.nodes.get(&id) else { let Some(node) = self.document_network.nodes.get(&id) else {
warn!("Deleting layer node that does not exist"); warn!("Deleting layer node that does not exist");
return; return;
@ -627,7 +647,7 @@ impl<'a> ModifyInputsContext<'a> {
self.responses.add(NodeGraphMessage::RunDocumentGraph); self.responses.add(NodeGraphMessage::RunDocumentGraph);
} }
fn delete_artboard(&mut self, id: NodeId, selected_nodes: &mut SelectedNodes) { pub fn delete_artboard(&mut self, id: NodeId, selected_nodes: &mut SelectedNodes) {
let Some(node) = self.document_network.nodes.get(&id) else { let Some(node) = self.document_network.nodes.get(&id) else {
warn!("Deleting artboard node that does not exist"); warn!("Deleting artboard node that does not exist");
return; return;
@ -661,441 +681,3 @@ impl<'a> ModifyInputsContext<'a> {
self.responses.add(NodeGraphMessage::RunDocumentGraph); self.responses.add(NodeGraphMessage::RunDocumentGraph);
} }
} }
pub struct GraphOperationHandlerData<'a> {
pub document_network: &'a mut NodeNetwork,
pub document_metadata: &'a mut DocumentMetadata,
pub selected_nodes: &'a mut SelectedNodes,
pub collapsed: &'a mut CollapsedLayers,
pub node_graph: &'a mut NodeGraphMessageHandler,
}
impl MessageHandler<GraphOperationMessage, GraphOperationHandlerData<'_>> for GraphOperationMessageHandler {
fn process_message(&mut self, message: GraphOperationMessage, responses: &mut VecDeque<Message>, data: GraphOperationHandlerData) {
let GraphOperationHandlerData {
document_network,
document_metadata,
selected_nodes,
collapsed,
node_graph,
} = data;
match message {
GraphOperationMessage::FillSet { layer, fill } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.fill_set(fill);
}
}
GraphOperationMessage::OpacitySet { layer, opacity } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.opacity_set(opacity);
}
}
GraphOperationMessage::BlendModeSet { layer, blend_mode } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.blend_mode_set(blend_mode);
}
}
GraphOperationMessage::UpdateBounds { layer, old_bounds, new_bounds } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.update_bounds(old_bounds, new_bounds);
}
}
GraphOperationMessage::StrokeSet { layer, stroke } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.stroke_set(stroke);
}
}
GraphOperationMessage::TransformChange {
layer,
transform,
transform_in,
skip_rerender,
} => {
let parent_transform = document_metadata.downstream_transform_to_viewport(layer);
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.transform_change(transform, transform_in, parent_transform, bounds, skip_rerender);
}
}
GraphOperationMessage::TransformSet {
layer,
transform,
transform_in,
skip_rerender,
} => {
let parent_transform = document_metadata.downstream_transform_to_viewport(layer);
let current_transform = Some(document_metadata.transform_to_viewport(layer));
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.transform_set(transform, transform_in, parent_transform, current_transform, bounds, skip_rerender);
}
}
GraphOperationMessage::TransformSetPivot { layer, pivot } => {
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.pivot_set(pivot, bounds);
}
}
GraphOperationMessage::Vector { layer, modification } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.vector_modify(modification);
}
}
GraphOperationMessage::Brush { layer, strokes } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.brush_modify(strokes);
}
}
GraphOperationMessage::NewArtboard { id, artboard } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.document_network.original_outputs()[0].node_id, 0, 0) {
modify_inputs.insert_artboard(artboard, layer);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::NewBitmapLayer {
id,
image_frame,
parent,
insert_index,
} => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
modify_inputs.insert_image_data(image_frame, layer);
}
}
GraphOperationMessage::NewCustomLayer {
id,
nodes,
parent,
insert_index,
alias,
} => {
trace!("Inserting new layer {id} as a child of {parent:?} at index {insert_index}");
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
let new_ids: HashMap<_, _> = nodes.iter().map(|(&id, _)| (id, NodeId(generate_uuid()))).collect();
if let Some(node) = modify_inputs.document_network.nodes.get_mut(&id) {
node.alias = alias.clone();
}
let shift = nodes
.get(&NodeId(0))
.and_then(|node| {
modify_inputs
.document_network
.nodes
.get(&layer)
.map(|layer| layer.metadata.position - node.metadata.position + IVec2::new(-8, 0))
})
.unwrap_or_default();
for (old_id, mut document_node) in nodes {
// Shift copied node
document_node.metadata.position += shift;
// Get the new, non-conflicting id
let node_id = *new_ids.get(&old_id).unwrap();
document_node = document_node.map_ids(NodeGraphMessageHandler::default_node_input, &new_ids);
// Insert node into network
modify_inputs.document_network.nodes.insert(node_id, document_node);
}
if let Some(layer_node) = modify_inputs.document_network.nodes.get_mut(&layer) {
if let Some(&input) = new_ids.get(&NodeId(0)) {
layer_node.inputs[0] = NodeInput::node(input, 0)
}
}
modify_inputs.responses.add(NodeGraphMessage::RunDocumentGraph);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
modify_inputs.insert_vector_data(subpaths, layer);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::NewTextLayer {
id,
text,
font,
size,
parent,
insert_index,
} => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
modify_inputs.insert_text(text, font, size, layer);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(id, document_network, document_metadata, node_graph, responses) {
modify_inputs.resize_artboard(location, dimensions);
}
}
GraphOperationMessage::DeleteLayer { id } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
modify_inputs.delete_layer(id, selected_nodes, false);
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::DeleteArtboard { id } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
if let Some(artboard_id) = modify_inputs.document_network.nodes.get(&id).and_then(|node| node.inputs[0].as_node()) {
modify_inputs.delete_artboard(artboard_id, selected_nodes);
} else {
warn!("Artboard does not exist");
}
modify_inputs.delete_layer(id, selected_nodes, true);
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::ClearArtboards => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
let layer_nodes = modify_inputs.document_network.nodes.iter().filter(|(_, node)| node.is_layer()).map(|(id, _)| *id).collect::<Vec<_>>();
for layer in layer_nodes {
let artboards = modify_inputs
.document_network
.upstream_flow_back_from_nodes(vec![layer], true)
.filter_map(|(node, _id)| if node.is_artboard() { Some(_id) } else { None })
.collect::<Vec<_>>();
if artboards.is_empty() {
continue;
}
for artboard in artboards {
modify_inputs.delete_artboard(artboard, selected_nodes);
}
modify_inputs.delete_layer(layer, selected_nodes, true);
}
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
GraphOperationMessage::NewSvg {
id,
svg,
transform,
parent,
insert_index,
} => {
let tree = match usvg::Tree::from_str(&svg, &usvg::Options::default()) {
Ok(t) => t,
Err(e) => {
responses.add(DocumentMessage::DocumentHistoryBackward);
responses.add(DialogMessage::DisplayDialogError {
title: "SVG parsing failed".to_string(),
description: e.to_string(),
});
return;
}
};
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
import_usvg_node(&mut modify_inputs, &usvg::Node::Group(Box::new(tree.root)), transform, id, parent, insert_index);
load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
}
}
}
fn actions(&self) -> ActionList {
actions!(GraphOperationMessage; )
}
}
pub fn load_network_structure(document_network: &NodeNetwork, document_metadata: &mut DocumentMetadata, selected_nodes: &mut SelectedNodes, collapsed: &mut CollapsedLayers) {
document_metadata.load_structure(document_network, selected_nodes);
collapsed.0.retain(|&layer| document_metadata.layer_exists(layer));
}
fn usvg_color(c: usvg::Color, a: f32) -> Color {
Color::from_rgbaf32_unchecked(c.red as f32 / 255., c.green as f32 / 255., c.blue as f32 / 255., a)
}
fn usvg_transform(c: usvg::Transform) -> DAffine2 {
DAffine2::from_cols_array(&[c.sx as f64, c.ky as f64, c.kx as f64, c.sy as f64, c.tx as f64, c.ty as f64])
}
fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node, transform: DAffine2, id: NodeId, parent: LayerNodeIdentifier, insert_index: isize) {
let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) else {
return;
};
modify_inputs.layer_node = Some(layer);
match node {
usvg::Node::Group(group) => {
for child in &group.children {
import_usvg_node(modify_inputs, child, transform, NodeId(generate_uuid()), LayerNodeIdentifier::new_unchecked(layer), -1);
}
modify_inputs.layer_node = Some(layer);
}
usvg::Node::Path(path) => {
let subpaths = convert_usvg_path(path);
let bounds = subpaths.iter().filter_map(|subpath| subpath.bounding_box()).reduce(Quad::combine_bounds).unwrap_or_default();
let transformed_bounds = subpaths
.iter()
.filter_map(|subpath| subpath.bounding_box_with_transform(transform * usvg_transform(node.abs_transform())))
.reduce(Quad::combine_bounds)
.unwrap_or_default();
modify_inputs.insert_vector_data(subpaths, layer);
let center = DAffine2::from_translation((bounds[0] + bounds[1]) / 2.);
modify_inputs.modify_inputs("Transform", true, |inputs, _node_id, _metadata| {
transform_utils::update_transform(inputs, center.inverse() * transform * usvg_transform(node.abs_transform()) * center);
});
let bounds_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
let transformed_bound_transform = DAffine2::from_scale_angle_translation(transformed_bounds[1] - transformed_bounds[0], 0., transformed_bounds[0]);
apply_usvg_fill(
&path.fill,
modify_inputs,
transform * usvg_transform(node.abs_transform()),
bounds_transform,
transformed_bound_transform,
);
apply_usvg_stroke(&path.stroke, modify_inputs);
}
usvg::Node::Image(_image) => {
warn!("Skip image")
}
usvg::Node::Text(text) => {
let font = Font::new(crate::consts::DEFAULT_FONT_FAMILY.to_string(), crate::consts::DEFAULT_FONT_STYLE.to_string());
modify_inputs.insert_text(text.chunks.iter().map(|chunk| chunk.text.clone()).collect(), font, 24., layer);
modify_inputs.fill_set(Fill::Solid(Color::BLACK));
}
}
}
fn apply_usvg_stroke(stroke: &Option<usvg::Stroke>, modify_inputs: &mut ModifyInputsContext) {
if let Some(stroke) = stroke {
if let usvg::Paint::Color(color) = &stroke.paint {
modify_inputs.stroke_set(Stroke {
color: Some(usvg_color(*color, stroke.opacity.get())),
weight: stroke.width.get() as f64,
dash_lengths: stroke.dasharray.as_ref().map(|lengths| lengths.iter().map(|&length| length as f64).collect()).unwrap_or_default(),
dash_offset: stroke.dashoffset as f64,
line_cap: match stroke.linecap {
usvg::LineCap::Butt => LineCap::Butt,
usvg::LineCap::Round => LineCap::Round,
usvg::LineCap::Square => LineCap::Square,
},
line_join: match stroke.linejoin {
usvg::LineJoin::Miter => LineJoin::Miter,
usvg::LineJoin::MiterClip => LineJoin::Miter,
usvg::LineJoin::Round => LineJoin::Round,
usvg::LineJoin::Bevel => LineJoin::Bevel,
},
line_join_miter_limit: stroke.miterlimit.get() as f64,
})
} else {
warn!("Skip non-solid stroke")
}
}
}
fn apply_usvg_fill(fill: &Option<usvg::Fill>, modify_inputs: &mut ModifyInputsContext, transform: DAffine2, bounds_transform: DAffine2, transformed_bound_transform: DAffine2) {
if let Some(fill) = &fill {
modify_inputs.fill_set(match &fill.paint {
usvg::Paint::Color(color) => Fill::solid(usvg_color(*color, fill.opacity.get())),
usvg::Paint::LinearGradient(linear) => {
let local = [DVec2::new(linear.x1 as f64, linear.y1 as f64), DVec2::new(linear.x2 as f64, linear.y2 as f64)];
let to_doc_transform = if linear.base.units == usvg::Units::UserSpaceOnUse {
transform
} else {
transformed_bound_transform
};
let to_doc = to_doc_transform * usvg_transform(linear.transform);
let document = [to_doc.transform_point2(local[0]), to_doc.transform_point2(local[1])];
let layer = [transform.inverse().transform_point2(document[0]), transform.inverse().transform_point2(document[1])];
let [start, end] = [bounds_transform.inverse().transform_point2(layer[0]), bounds_transform.inverse().transform_point2(layer[1])];
Fill::Gradient(Gradient {
start,
end,
transform: DAffine2::IDENTITY,
gradient_type: GradientType::Linear,
positions: linear.stops.iter().map(|stop| (stop.offset.get() as f64, usvg_color(stop.color, stop.opacity.get()))).collect(),
})
}
usvg::Paint::RadialGradient(radial) => {
let local = [DVec2::new(radial.cx as f64, radial.cy as f64), DVec2::new(radial.fx as f64, radial.fy as f64)];
let to_doc_transform = if radial.base.units == usvg::Units::UserSpaceOnUse {
transform
} else {
transformed_bound_transform
};
let to_doc = to_doc_transform * usvg_transform(radial.transform);
let document = [to_doc.transform_point2(local[0]), to_doc.transform_point2(local[1])];
let layer = [transform.inverse().transform_point2(document[0]), transform.inverse().transform_point2(document[1])];
let [start, end] = [bounds_transform.inverse().transform_point2(layer[0]), bounds_transform.inverse().transform_point2(layer[1])];
Fill::Gradient(Gradient {
start,
end,
transform: DAffine2::IDENTITY,
gradient_type: GradientType::Radial,
positions: radial.stops.iter().map(|stop| (stop.offset.get() as f64, usvg_color(stop.color, stop.opacity.get()))).collect(),
})
}
usvg::Paint::Pattern(_) => {
warn!("Skip pattern");
return;
}
});
}
}
fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<ManipulatorGroupId>> {
let mut subpaths = Vec::new();
let mut groups = Vec::new();
let mut points = path.data.points().iter();
let to_vec = |p: &usvg::tiny_skia_path::Point| DVec2::new(p.x as f64, p.y as f64);
for verb in path.data.verbs() {
match verb {
usvg::tiny_skia_path::PathVerb::Move => {
subpaths.push(Subpath::new(std::mem::take(&mut groups), false));
let Some(start) = points.next().map(to_vec) else { continue };
groups.push(ManipulatorGroup::new(start, Some(start), Some(start)));
}
usvg::tiny_skia_path::PathVerb::Line => {
let Some(end) = points.next().map(to_vec) else { continue };
groups.push(ManipulatorGroup::new(end, Some(end), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Quad => {
let Some(handle) = points.next().map(to_vec) else { continue };
let Some(end) = points.next().map(to_vec) else { continue };
if let Some(last) = groups.last_mut() {
last.out_handle = Some(last.anchor + (2. / 3.) * (handle - last.anchor));
}
groups.push(ManipulatorGroup::new(end, Some(end + (2. / 3.) * (handle - end)), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Cubic => {
let Some(first_handle) = points.next().map(to_vec) else { continue };
let Some(second_handle) = points.next().map(to_vec) else { continue };
let Some(end) = points.next().map(to_vec) else { continue };
if let Some(last) = groups.last_mut() {
last.out_handle = Some(first_handle);
}
groups.push(ManipulatorGroup::new(end, Some(second_handle), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Close => {
subpaths.push(Subpath::new(std::mem::take(&mut groups), true));
}
}
}
subpaths.push(Subpath::new(groups, false));
subpaths
}

View file

@ -1,6 +1,7 @@
mod document_message; mod document_message;
mod document_message_handler; mod document_message_handler;
pub mod graph_operation;
pub mod navigation; pub mod navigation;
pub mod node_graph; pub mod node_graph;
pub mod overlays; pub mod overlays;
@ -10,4 +11,4 @@ pub mod utility_types;
#[doc(inline)] #[doc(inline)]
pub use document_message::{DocumentMessage, DocumentMessageDiscriminant}; pub use document_message::{DocumentMessage, DocumentMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use document_message_handler::{DocumentInputs, DocumentMessageHandler}; pub use document_message_handler::{DocumentMessageData, DocumentMessageHandler};

View file

@ -1,7 +1,8 @@
mod navigation_message; mod navigation_message;
mod navigation_message_handler; mod navigation_message_handler;
pub mod utility_types;
#[doc(inline)] #[doc(inline)]
pub use navigation_message::{NavigationMessage, NavigationMessageDiscriminant}; pub use navigation_message::{NavigationMessage, NavigationMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use navigation_message_handler::NavigationMessageHandler; pub use navigation_message_handler::{NavigationMessageData, NavigationMessageHandler};

View file

@ -2,10 +2,9 @@ use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use glam::DVec2; use glam::DVec2;
use serde::{Deserialize, Serialize};
#[impl_message(Message, DocumentMessage, Navigation)] #[impl_message(Message, DocumentMessage, Navigation)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum NavigationMessage { pub enum NavigationMessage {
// Messages // Messages
DecreaseCanvasZoom { DecreaseCanvasZoom {

View file

@ -5,69 +5,49 @@ use crate::consts::{
use crate::messages::frontend::utility_types::MouseCursorIcon; use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion}; use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition; use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
use crate::messages::portfolio::document::navigation::utility_types::NavigationOperation;
use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata; use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata;
use crate::messages::portfolio::document::utility_types::misc::PTZ; use crate::messages::portfolio::document::utility_types::misc::PTZ;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] pub struct NavigationMessageData<'a> {
enum TransformOperation { pub metadata: &'a DocumentMetadata,
#[default] pub document_bounds: Option<[DVec2; 2]>,
None, pub ipp: &'a InputPreprocessorMessageHandler,
Pan { pub selection_bounds: Option<[DVec2; 2]>,
pre_commit_pan: DVec2, pub ptz: &'a mut PTZ,
},
Rotate {
pre_commit_tilt: f64,
snap_tilt: bool,
snap_tilt_released: bool,
},
Zoom {
pre_commit_zoom: f64,
snap_zoom_enabled: bool,
},
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Default)]
pub struct NavigationMessageHandler { pub struct NavigationMessageHandler {
transform_operation: TransformOperation, navigation_operation: NavigationOperation,
mouse_position: ViewportPosition, mouse_position: ViewportPosition,
finish_operation_with_click: bool, finish_operation_with_click: bool,
} }
impl Default for NavigationMessageHandler { impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for NavigationMessageHandler {
fn default() -> Self { fn process_message(&mut self, message: NavigationMessage, responses: &mut VecDeque<Message>, data: NavigationMessageData) {
Self { let NavigationMessageData {
mouse_position: ViewportPosition::default(), metadata,
finish_operation_with_click: false, document_bounds,
transform_operation: TransformOperation::None, ipp,
} selection_bounds,
} ptz,
} } = data;
impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &InputPreprocessorMessageHandler, Option<[DVec2; 2]>, &mut PTZ)> for NavigationMessageHandler {
fn process_message(
&mut self,
message: NavigationMessage,
responses: &mut VecDeque<Message>,
(metadata, document_bounds, ipp, selection_bounds, ptz): (&DocumentMetadata, Option<[DVec2; 2]>, &InputPreprocessorMessageHandler, Option<[DVec2; 2]>, &mut PTZ),
) {
use NavigationMessage::*;
let old_zoom = ptz.zoom; let old_zoom = ptz.zoom;
match message { match message {
DecreaseCanvasZoom { center_on_mouse } => { NavigationMessage::DecreaseCanvasZoom { center_on_mouse } => {
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom).unwrap_or(&ptz.zoom); let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom).unwrap_or(&ptz.zoom);
if center_on_mouse { if center_on_mouse {
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position)); responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
} }
responses.add(SetCanvasZoom { zoom_factor: new_scale }); responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: new_scale });
} }
FitViewportToBounds { NavigationMessage::FitViewportToBounds {
bounds: [pos1, pos2], bounds: [pos1, pos2],
prevent_zoom_past_100, prevent_zoom_past_100,
} => { } => {
@ -91,35 +71,35 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses); self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
} }
FitViewportToSelection => { NavigationMessage::FitViewportToSelection => {
if let Some(bounds) = selection_bounds { if let Some(bounds) = selection_bounds {
let transform = metadata.document_to_viewport.inverse(); let transform = metadata.document_to_viewport.inverse();
responses.add(FitViewportToBounds { responses.add(NavigationMessage::FitViewportToBounds {
bounds: [transform.transform_point2(bounds[0]), transform.transform_point2(bounds[1])], bounds: [transform.transform_point2(bounds[0]), transform.transform_point2(bounds[1])],
prevent_zoom_past_100: false, prevent_zoom_past_100: false,
}) })
} }
} }
IncreaseCanvasZoom { center_on_mouse } => { NavigationMessage::IncreaseCanvasZoom { center_on_mouse } => {
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom).unwrap_or(&ptz.zoom); let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom).unwrap_or(&ptz.zoom);
if center_on_mouse { if center_on_mouse {
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position)); responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
} }
responses.add(SetCanvasZoom { zoom_factor: new_scale }); responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: new_scale });
} }
PointerMove { NavigationMessage::PointerMove {
snap_angle, snap_angle,
wait_for_snap_angle_release, wait_for_snap_angle_release,
snap_zoom, snap_zoom,
zoom_from_viewport, zoom_from_viewport,
} => { } => {
match self.transform_operation { match self.navigation_operation {
TransformOperation::None => {} NavigationOperation::None => {}
TransformOperation::Pan { .. } => { NavigationOperation::Pan { .. } => {
let delta = ipp.mouse.position - self.mouse_position; let delta = ipp.mouse.position - self.mouse_position;
responses.add(TranslateCanvas { delta }); responses.add(NavigationMessage::TranslateCanvas { delta });
} }
TransformOperation::Rotate { NavigationOperation::Rotate {
snap_tilt, snap_tilt,
snap_tilt_released, snap_tilt_released,
pre_commit_tilt, pre_commit_tilt,
@ -131,7 +111,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
if !new_snap && snap_tilt { if !new_snap && snap_tilt {
ptz.tilt = self.snapped_angle(ptz.tilt); ptz.tilt = self.snapped_angle(ptz.tilt);
} }
self.transform_operation = TransformOperation::Rotate { self.navigation_operation = NavigationOperation::Rotate {
pre_commit_tilt, pre_commit_tilt,
snap_tilt: new_snap, snap_tilt: new_snap,
snap_tilt_released: true, snap_tilt_released: true,
@ -145,9 +125,9 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
start_offset.angle_between(end_offset) start_offset.angle_between(end_offset)
}; };
responses.add(SetCanvasTilt { angle_radians: ptz.tilt + rotation }); responses.add(NavigationMessage::SetCanvasTilt { angle_radians: ptz.tilt + rotation });
} }
TransformOperation::Zoom { snap_zoom_enabled, pre_commit_zoom } => { NavigationOperation::Zoom { snap_zoom_enabled, pre_commit_zoom } => {
let zoom_start = self.snapped_scale(ptz.zoom); let zoom_start = self.snapped_scale(ptz.zoom);
let new_snap = ipp.keyboard.get(snap_zoom as usize); let new_snap = ipp.keyboard.get(snap_zoom as usize);
@ -157,7 +137,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
} }
if snap_zoom_enabled != new_snap { if snap_zoom_enabled != new_snap {
self.transform_operation = TransformOperation::Zoom { self.navigation_operation = NavigationOperation::Zoom {
pre_commit_zoom, pre_commit_zoom,
snap_zoom_enabled: new_snap, snap_zoom_enabled: new_snap,
}; };
@ -172,23 +152,23 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
if let Some(mouse) = zoom_from_viewport { if let Some(mouse) = zoom_from_viewport {
let zoom_factor = self.snapped_scale(ptz.zoom) / zoom_start; let zoom_factor = self.snapped_scale(ptz.zoom) / zoom_start;
responses.add(SetCanvasZoom { zoom_factor: ptz.zoom }); responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: ptz.zoom });
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse)); responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse));
} else { } else {
responses.add(SetCanvasZoom { zoom_factor: ptz.zoom }); responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: ptz.zoom });
} }
} }
} }
self.mouse_position = ipp.mouse.position; self.mouse_position = ipp.mouse.position;
} }
ResetCanvasTiltAndZoomTo100Percent => { NavigationMessage::ResetCanvasTiltAndZoomTo100Percent => {
ptz.tilt = 0.; ptz.tilt = 0.;
ptz.zoom = 1.; ptz.zoom = 1.;
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses); self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
} }
RotateCanvasBegin { was_dispatched_from_menu } => { NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu } => {
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }); responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
responses.add(FrontendMessage::UpdateInputHints { responses.add(FrontendMessage::UpdateInputHints {
hint_data: HintData(vec![ hint_data: HintData(vec![
@ -205,7 +185,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
]), ]),
}); });
self.transform_operation = TransformOperation::Rotate { self.navigation_operation = NavigationOperation::Rotate {
pre_commit_tilt: ptz.tilt, pre_commit_tilt: ptz.tilt,
snap_tilt_released: false, snap_tilt_released: false,
snap_tilt: false, snap_tilt: false,
@ -214,30 +194,30 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
self.mouse_position = ipp.mouse.position; self.mouse_position = ipp.mouse.position;
self.finish_operation_with_click = was_dispatched_from_menu; self.finish_operation_with_click = was_dispatched_from_menu;
} }
SetCanvasTilt { angle_radians } => { NavigationMessage::SetCanvasTilt { angle_radians } => {
ptz.tilt = angle_radians; ptz.tilt = angle_radians;
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses); self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
} }
SetCanvasZoom { zoom_factor } => { NavigationMessage::SetCanvasZoom { zoom_factor } => {
ptz.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX); ptz.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp); ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses); self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
} }
TransformCanvasEnd { abort_transform } => { NavigationMessage::TransformCanvasEnd { abort_transform } => {
if abort_transform { if abort_transform {
match self.transform_operation { match self.navigation_operation {
TransformOperation::None => {} NavigationOperation::None => {}
TransformOperation::Rotate { pre_commit_tilt, .. } => { NavigationOperation::Rotate { pre_commit_tilt, .. } => {
ptz.tilt = pre_commit_tilt; ptz.tilt = pre_commit_tilt;
responses.add(SetCanvasTilt { angle_radians: pre_commit_tilt }); responses.add(NavigationMessage::SetCanvasTilt { angle_radians: pre_commit_tilt });
} }
TransformOperation::Pan { pre_commit_pan, .. } => { NavigationOperation::Pan { pre_commit_pan, .. } => {
ptz.pan = pre_commit_pan; ptz.pan = pre_commit_pan;
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses); self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
} }
TransformOperation::Zoom { pre_commit_zoom, .. } => { NavigationOperation::Zoom { pre_commit_zoom, .. } => {
ptz.zoom = pre_commit_zoom; ptz.zoom = pre_commit_zoom;
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses); self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
@ -250,21 +230,21 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
responses.add(BroadcastEvent::CanvasTransformed); responses.add(BroadcastEvent::CanvasTransformed);
responses.add(ToolMessage::UpdateCursor); responses.add(ToolMessage::UpdateCursor);
responses.add(ToolMessage::UpdateHints); responses.add(ToolMessage::UpdateHints);
self.transform_operation = TransformOperation::None; self.navigation_operation = NavigationOperation::None;
} }
TransformFromMenuEnd { commit_key } => { NavigationMessage::TransformFromMenuEnd { commit_key } => {
let abort_transform = commit_key == Key::Rmb; let abort_transform = commit_key == Key::Rmb;
self.finish_operation_with_click = false; self.finish_operation_with_click = false;
responses.add(TransformCanvasEnd { abort_transform }); responses.add(NavigationMessage::TransformCanvasEnd { abort_transform });
} }
TranslateCanvas { delta } => { NavigationMessage::TranslateCanvas { delta } => {
let transformed_delta = metadata.document_to_viewport.inverse().transform_vector2(delta); let transformed_delta = metadata.document_to_viewport.inverse().transform_vector2(delta);
ptz.pan += transformed_delta; ptz.pan += transformed_delta;
responses.add(BroadcastEvent::CanvasTransformed); responses.add(BroadcastEvent::CanvasTransformed);
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses); self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
} }
TranslateCanvasBegin => { NavigationMessage::TranslateCanvasBegin => {
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing }); responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
responses.add(FrontendMessage::UpdateInputHints { responses.add(FrontendMessage::UpdateInputHints {
@ -273,22 +253,22 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
}); });
self.mouse_position = ipp.mouse.position; self.mouse_position = ipp.mouse.position;
self.transform_operation = TransformOperation::Pan { pre_commit_pan: ptz.pan }; self.navigation_operation = NavigationOperation::Pan { pre_commit_pan: ptz.pan };
} }
TranslateCanvasByViewportFraction { delta } => { NavigationMessage::TranslateCanvasByViewportFraction { delta } => {
let transformed_delta = metadata.document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size()); let transformed_delta = metadata.document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
ptz.pan += transformed_delta; ptz.pan += transformed_delta;
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses); self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
} }
WheelCanvasTranslate { use_y_as_x } => { NavigationMessage::WheelCanvasTranslate { use_y_as_x } => {
let delta = match use_y_as_x { let delta = match use_y_as_x {
false => -ipp.mouse.scroll_delta.as_dvec2(), false => -ipp.mouse.scroll_delta.as_dvec2(),
true => (-ipp.mouse.scroll_delta.y as f64, 0.).into(), true => (-ipp.mouse.scroll_delta.y as f64, 0.).into(),
} * VIEWPORT_SCROLL_RATE; } * VIEWPORT_SCROLL_RATE;
responses.add(TranslateCanvas { delta }); responses.add(NavigationMessage::TranslateCanvas { delta });
} }
WheelCanvasZoom => { NavigationMessage::WheelCanvasZoom => {
let scroll = ipp.mouse.scroll_delta.scroll_delta(); let scroll = ipp.mouse.scroll_delta.scroll_delta();
let mut zoom_factor = 1. + scroll.abs() * VIEWPORT_ZOOM_WHEEL_RATE; let mut zoom_factor = 1. + scroll.abs() * VIEWPORT_ZOOM_WHEEL_RATE;
if ipp.mouse.scroll_delta.y > 0 { if ipp.mouse.scroll_delta.y > 0 {
@ -297,9 +277,9 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp); zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp);
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position)); responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
responses.add(SetCanvasZoom { zoom_factor: ptz.zoom * zoom_factor }); responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: ptz.zoom * zoom_factor });
} }
ZoomCanvasBegin => { NavigationMessage::ZoomCanvasBegin => {
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn }); responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
responses.add(FrontendMessage::UpdateInputHints { responses.add(FrontendMessage::UpdateInputHints {
hint_data: HintData(vec![ hint_data: HintData(vec![
@ -316,7 +296,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
]), ]),
}); });
self.transform_operation = TransformOperation::Zoom { self.navigation_operation = NavigationOperation::Zoom {
pre_commit_zoom: ptz.zoom, pre_commit_zoom: ptz.zoom,
snap_zoom_enabled: false, snap_zoom_enabled: false,
}; };
@ -340,7 +320,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
FitViewportToSelection, FitViewportToSelection,
); );
if self.transform_operation != TransformOperation::None { if self.navigation_operation != NavigationOperation::None {
let transforming = actions!(NavigationMessageDiscriminant; let transforming = actions!(NavigationMessageDiscriminant;
PointerMove, PointerMove,
TransformCanvasEnd, TransformCanvasEnd,
@ -363,7 +343,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
impl NavigationMessageHandler { impl NavigationMessageHandler {
pub fn snapped_angle(&self, tilt: f64) -> f64 { pub fn snapped_angle(&self, tilt: f64) -> f64 {
let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians(); let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians();
if let TransformOperation::Rotate { snap_tilt: true, .. } = self.transform_operation { if let NavigationOperation::Rotate { snap_tilt: true, .. } = self.navigation_operation {
(tilt / increment_radians).round() * increment_radians (tilt / increment_radians).round() * increment_radians
} else { } else {
tilt tilt
@ -371,7 +351,7 @@ impl NavigationMessageHandler {
} }
pub fn snapped_scale(&self, zoom: f64) -> f64 { pub fn snapped_scale(&self, zoom: f64) -> f64 {
if let TransformOperation::Zoom { snap_zoom_enabled: true, .. } = self.transform_operation { if let NavigationOperation::Zoom { snap_zoom_enabled: true, .. } = self.navigation_operation {
*VIEWPORT_ZOOM_LEVELS.iter().min_by(|a, b| (**a - zoom).abs().partial_cmp(&(**b - zoom).abs()).unwrap()).unwrap_or(&zoom) *VIEWPORT_ZOOM_LEVELS.iter().min_by(|a, b| (**a - zoom).abs().partial_cmp(&(**b - zoom).abs()).unwrap()).unwrap_or(&zoom)
} else { } else {
zoom zoom

View file

@ -0,0 +1,19 @@
use glam::DVec2;
#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum NavigationOperation {
#[default]
None,
Pan {
pre_commit_pan: DVec2,
},
Rotate {
pre_commit_tilt: f64,
snap_tilt: bool,
snap_tilt_released: bool,
},
Zoom {
pre_commit_zoom: f64,
snap_zoom_enabled: bool,
},
}

View file

@ -1,4 +1,5 @@
use super::{node_properties, FrontendGraphDataType, FrontendNodeType}; use super::node_properties;
use super::utility_types::{FrontendGraphDataType, FrontendNodeType};
use crate::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE}; use crate::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE};
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata; use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata;
@ -7,12 +8,10 @@ use crate::messages::prelude::Message;
use crate::node_graph_executor::NodeGraphExecutor; use crate::node_graph_executor::NodeGraphExecutor;
use graph_craft::concrete; use graph_craft::concrete;
use graph_craft::document::value::*;
use graph_craft::document::*; use graph_craft::document::*;
use graph_craft::document::{value::*, DocumentNodeMetadata};
use graph_craft::imaginate_input::ImaginateSamplingMethod; use graph_craft::imaginate_input::ImaginateSamplingMethod;
use graph_craft::ProtoNodeIdentifier; use graph_craft::ProtoNodeIdentifier;
#[cfg(feature = "gpu")]
use graphene_core::application_io::SurfaceHandle;
use graphene_core::raster::brush_cache::BrushCache; use graphene_core::raster::brush_cache::BrushCache;
use graphene_core::raster::{ use graphene_core::raster::{
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, Image, ImageFrame, LuminanceCalculation, NoiseType, RedGreenBlue, RelativeAbsolute, BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, Image, ImageFrame, LuminanceCalculation, NoiseType, RedGreenBlue, RelativeAbsolute,
@ -22,14 +21,12 @@ use graphene_core::text::Font;
use graphene_core::transform::Footprint; use graphene_core::transform::Footprint;
use graphene_core::vector::VectorData; use graphene_core::vector::VectorData;
use graphene_core::*; use graphene_core::*;
#[cfg(feature = "gpu")]
use gpu_executor::*;
use graphene_std::wasm_application_io::WasmEditorApi; use graphene_std::wasm_application_io::WasmEditorApi;
#[cfg(feature = "gpu")]
use {gpu_executor::*, graphene_core::application_io::SurfaceHandle, wgpu_executor::WgpuExecutor};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::VecDeque; use std::collections::VecDeque;
#[cfg(feature = "gpu")]
use wgpu_executor::WgpuExecutor;
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq, Hash)]
pub struct DocumentInputType { pub struct DocumentInputType {

View file

@ -1,15 +1,10 @@
pub mod document_node_types;
mod node_graph_message; mod node_graph_message;
mod node_graph_message_handler; mod node_graph_message_handler;
pub mod node_properties;
pub mod utility_types;
#[doc(inline)] #[doc(inline)]
pub use node_graph_message::{NodeGraphMessage, NodeGraphMessageDiscriminant}; pub use node_graph_message::{NodeGraphMessage, NodeGraphMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use node_graph_message_handler::*; pub use node_graph_message_handler::*;
mod graph_operation_message;
mod graph_operation_message_handler;
#[doc(inline)]
pub use graph_operation_message::*;
#[doc(inline)]
pub use graph_operation_message_handler::*;

View file

@ -1,4 +1,5 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput}; use graph_craft::document::{DocumentNode, NodeId, NodeInput};
use graph_craft::proto::GraphErrors; use graph_craft::proto::GraphErrors;

View file

@ -1,523 +1,21 @@
pub use self::document_node_types::*;
use super::load_network_structure;
use crate::application::generate_uuid;
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerClassification, LayerPanelEntry, SelectedNodes};
use crate::messages::prelude::*;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput, Source}; use graph_craft::document::{DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput, Source};
use graph_craft::proto::GraphErrors; use graph_craft::proto::GraphErrors;
use graphene_core::*; use graphene_core::*;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes; use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
mod document_node_types;
mod node_properties; use super::utility_types::{FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeLink};
use super::{document_node_types, node_properties};
use crate::application::generate_uuid;
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::graph_operation::load_network_structure;
use crate::messages::portfolio::document::node_graph::document_node_types::{resolve_document_node_type, DocumentInputType, NodePropertiesContext};
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerClassification, LayerPanelEntry, SelectedNodes};
use crate::messages::prelude::*;
use glam::IVec2; use glam::IVec2;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendGraphDataType {
#[default]
#[serde(rename = "general")]
General,
#[serde(rename = "raster")]
Raster,
#[serde(rename = "color")]
Color,
#[serde(rename = "general")]
Text,
#[serde(rename = "vector")]
Subpath,
#[serde(rename = "number")]
Number,
#[serde(rename = "general")]
Boolean,
/// Refers to the mathematical vector, with direction and magnitude.
#[serde(rename = "number")]
Vector,
#[serde(rename = "raster")]
GraphicGroup,
#[serde(rename = "artboard")]
Artboard,
#[serde(rename = "color")]
Palette,
}
impl FrontendGraphDataType {
pub const fn with_tagged_value(value: &TaggedValue) -> Self {
match value {
TaggedValue::String(_) => Self::Text,
TaggedValue::F32(_) | TaggedValue::F64(_) | TaggedValue::U32(_) | TaggedValue::DAffine2(_) => Self::Number,
TaggedValue::Bool(_) => Self::Boolean,
TaggedValue::DVec2(_) | TaggedValue::IVec2(_) => Self::Vector,
TaggedValue::Image(_) => Self::Raster,
TaggedValue::ImageFrame(_) => Self::Raster,
TaggedValue::Color(_) => Self::Color,
TaggedValue::RcSubpath(_) | TaggedValue::Subpaths(_) | TaggedValue::VectorData(_) => Self::Subpath,
TaggedValue::GraphicGroup(_) => Self::GraphicGroup,
TaggedValue::Artboard(_) => Self::Artboard,
TaggedValue::Palette(_) => Self::Palette,
_ => Self::General,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphInput {
#[serde(rename = "dataType")]
data_type: FrontendGraphDataType,
name: String,
#[serde(rename = "resolvedType")]
resolved_type: Option<String>,
connected: Option<NodeId>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphOutput {
#[serde(rename = "dataType")]
data_type: FrontendGraphDataType,
name: String,
#[serde(rename = "resolvedType")]
resolved_type: Option<String>,
connected: Option<NodeId>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNode {
pub id: graph_craft::document::NodeId,
#[serde(rename = "isLayer")]
pub is_layer: bool,
pub alias: String,
pub name: String,
#[serde(rename = "primaryInput")]
pub primary_input: Option<FrontendGraphInput>,
#[serde(rename = "exposedInputs")]
pub exposed_inputs: Vec<FrontendGraphInput>,
#[serde(rename = "primaryOutput")]
pub primary_output: Option<FrontendGraphOutput>,
#[serde(rename = "exposedOutputs")]
pub exposed_outputs: Vec<FrontendGraphOutput>,
pub position: (i32, i32),
pub disabled: bool,
pub previewed: bool,
pub errors: Option<String>,
}
// (link_start, link_end, link_end_input_index)
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeLink {
#[serde(rename = "linkStart")]
pub link_start: NodeId,
#[serde(rename = "linkStartOutputIndex")]
pub link_start_output_index: usize,
#[serde(rename = "linkEnd")]
pub link_end: NodeId,
#[serde(rename = "linkEndInputIndex")]
pub link_end_input_index: usize,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeType {
pub name: String,
pub category: String,
}
impl FrontendNodeType {
pub fn new(name: &'static str, category: &'static str) -> Self {
Self {
name: name.to_string(),
category: category.to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct NodeGraphMessageHandler {
pub network: Vec<NodeId>,
pub resolved_types: ResolvedDocumentNodeTypes,
pub node_graph_errors: GraphErrors,
has_selection: bool,
widgets: [LayoutGroup; 2],
}
impl Default for NodeGraphMessageHandler {
fn default() -> Self {
let right_side_widgets = vec![
// TODO: Replace this with an "Add Node" button, also next to an "Add Layer" button
TextLabel::new("Right Click in Graph to Add Nodes").italic(true).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextButton::new("Node Graph")
.icon(Some("GraphViewOpen".into()))
.tooltip("Hide Node Graph")
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
.widget_holder(),
];
Self {
network: Vec::new(),
resolved_types: ResolvedDocumentNodeTypes::default(),
node_graph_errors: Vec::new(),
has_selection: false,
widgets: [LayoutGroup::Row { widgets: Vec::new() }, LayoutGroup::Row { widgets: right_side_widgets }],
}
}
}
impl NodeGraphMessageHandler {
/// Send the cached layout to the frontend for the options bar at the top of the node panel
fn send_node_bar_layout(&self, responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(self.widgets.to_vec())),
layout_target: LayoutTarget::NodeGraphBar,
});
}
/// Updates the buttons for disable and preview
fn update_selection_action_buttons(&mut self, document_network: &NodeNetwork, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) {
if let Some(network) = document_network.nested_network(&self.network) {
let mut widgets = Vec::new();
// Don't allow disabling input or output nodes
let mut selection = selected_nodes.selected_nodes().filter(|&&id| !network.imports.contains(&id) && !network.original_outputs_contain(id));
// If there is at least one other selected node then show the hide or show button
if selection.next().is_some() {
// Check if any of the selected nodes are disabled
let is_hidden = selected_nodes.selected_nodes().any(|id| network.disabled.contains(id));
// Check if multiple nodes are selected
let multiple_nodes = selection.next().is_some();
// Generate the enable or disable button accordingly
let (hide_show_label, hide_show_icon) = if is_hidden { ("Make Visible", "EyeHidden") } else { ("Make Hidden", "EyeVisible") };
let hide_button = TextButton::new(hide_show_label)
.icon(Some(hide_show_icon.to_string()))
.tooltip(if is_hidden { "Show selected nodes/layers" } else { "Hide selected nodes/layers" }.to_string() + if multiple_nodes { "s" } else { "" })
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedHidden))
.on_update(move |_| NodeGraphMessage::ToggleSelectedHidden.into())
.widget_holder();
widgets.push(hide_button);
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
}
// If only one node is selected then show the preview or stop previewing button
let mut selection = selected_nodes.selected_nodes();
if let (Some(&node_id), None) = (selection.next(), selection.next()) {
// Is this node the current output
let is_output = network.outputs_contain(node_id);
// Don't show stop previewing button on the original output node
if !(is_output && network.previous_outputs_contain(node_id).unwrap_or(true)) {
let output_button = TextButton::new(if is_output { "End Preview" } else { "Preview" })
.icon(Some("Rescale".to_string()))
.tooltip(if is_output { "Restore preview to the graph output" } else { "Preview selected node/layer" }.to_string() + " (Shortcut: Alt-click node/layer)")
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
.widget_holder();
widgets.push(output_button);
}
}
self.widgets[0] = LayoutGroup::Row { widgets };
}
self.send_node_bar_layout(responses);
}
/// Collate the properties panel sections for a node graph
pub fn collate_properties(&self, context: &mut NodePropertiesContext, selected_nodes: &SelectedNodes) -> Vec<LayoutGroup> {
let mut network = context.network;
for segment in &self.network {
network = network.nodes.get(segment).and_then(|node| node.implementation.get_network()).unwrap();
}
// We want:
// - If only nodes (no layers) are selected: display each node's properties
// - If one layer is selected, and zero or more of its upstream nodes: display the properties for the layer and its upstream nodes
// - If multiple layers are selected, or one node plus other non-upstream nodes: display nothing
// First, we filter all the selections into layers and nodes
let (mut layers, mut nodes) = (Vec::new(), Vec::new());
for node_id in selected_nodes.selected_nodes() {
if let Some(layer_or_node) = network.nodes.get(node_id) {
if layer_or_node.is_layer() {
layers.push(*node_id);
} else {
nodes.push(*node_id);
}
};
}
// Next, we decide what to display based on the number of layers and nodes selected
match layers.len() {
// If no layers are selected, show properties for all selected nodes
0 => nodes
.iter()
.filter_map(|node_id| network.nodes.get(node_id).map(|node| node_properties::generate_node_properties(node, *node_id, context)))
.collect(),
// If one layer is selected, filter out all selected nodes that are not upstream of it. If there are no nodes left, show properties for the layer. Otherwise, show nothing.
1 => {
let nodes_not_upstream_of_layer = nodes
.into_iter()
.filter(|&selected_node_id| !network.is_node_upstream_of_another_by_primary_flow(layers[0], selected_node_id));
if nodes_not_upstream_of_layer.count() > 0 {
return Vec::new();
}
// 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)
network
.upstream_flow_back_from_nodes(vec![layers[0]], true)
.enumerate()
.take_while(|(i, (node, _))| if *i == 0 { true } else { !node.is_layer() })
.map(|(_, (node, node_id))| node_properties::generate_node_properties(node, node_id, context))
.collect()
}
// If multiple layers and/or nodes are selected, show nothing
_ => Vec::new(),
}
}
fn collect_links(network: &NodeNetwork) -> Vec<FrontendNodeLink> {
network
.nodes
.iter()
.flat_map(|(link_end, node)| node.inputs.iter().filter(|input| input.is_exposed()).enumerate().map(move |(index, input)| (input, link_end, index)))
.filter_map(|(input, &link_end, link_end_input_index)| {
if let NodeInput::Node {
node_id: link_start,
output_index: link_start_output_index,
// TODO: add ui for lambdas
lambda: _,
} = *input
{
Some(FrontendNodeLink {
link_start,
link_start_output_index,
link_end,
link_end_input_index,
})
} else {
None
}
})
.collect::<Vec<_>>()
}
fn collect_nodes(&self, links: &[FrontendNodeLink], network: &NodeNetwork) -> Vec<FrontendNode> {
let connected_node_to_output_lookup = links.iter().map(|link| ((link.link_start, link.link_start_output_index), link.link_end)).collect::<HashMap<_, _>>();
let mut nodes = Vec::new();
for (&node_id, node) in &network.nodes {
let node_path = vec![node_id];
// TODO: This should be based on the graph runtime type inference system in order to change the colors of node connectors to match the data type in use
let Some(document_node_definition) = document_node_types::resolve_document_node_type(&node.name) else {
warn!("Node '{}' does not exist in library", node.name);
continue;
};
// Inputs
let mut inputs = {
let frontend_graph_inputs = document_node_definition.inputs.iter().enumerate().map(|(index, input_type)| {
// Convert the index in all inputs to the index in only the exposed inputs
let index = node.inputs.iter().take(index).filter(|input| input.is_exposed()).count();
FrontendGraphInput {
data_type: input_type.data_type,
name: input_type.name.to_string(),
resolved_type: self.resolved_types.inputs.get(&Source { node: node_path.clone(), index }).map(|input| format!("{input:?}")),
connected: None,
}
});
node.inputs.iter().zip(frontend_graph_inputs).map(|(node_input, mut frontend_graph_input)| {
if let NodeInput::Node { node_id: connected_node_id, .. } = node_input {
frontend_graph_input.connected = Some(*connected_node_id);
}
(node_input, frontend_graph_input)
})
};
let primary_input = inputs.next().filter(|(input, _)| input.is_exposed()).map(|(_, input_type)| input_type);
let exposed_inputs = inputs.filter(|(input, _)| input.is_exposed()).map(|(_, input_type)| input_type).collect();
// Outputs
let mut outputs = document_node_definition.outputs.iter().enumerate().map(|(index, output_type)| FrontendGraphOutput {
data_type: output_type.data_type,
name: output_type.name.to_string(),
resolved_type: self.resolved_types.outputs.get(&Source { node: node_path.clone(), index }).map(|output| format!("{output:?}")),
connected: connected_node_to_output_lookup.get(&(node_id, index)).copied(),
});
let primary_output = node.has_primary_output.then(|| outputs.next()).flatten();
let exposed_outputs = outputs.collect::<Vec<_>>();
// Errors
let errors = self.node_graph_errors.iter().find(|error| error.node_path.starts_with(&node_path)).map(|error| error.error.clone());
nodes.push(FrontendNode {
id: node_id,
is_layer: node.is_layer(),
alias: node.alias.clone(),
name: node.name.clone(),
primary_input,
exposed_inputs,
primary_output,
exposed_outputs,
position: node.metadata.position.into(),
previewed: network.outputs_contain(node_id),
disabled: network.disabled.contains(&node_id),
errors: errors.map(|e| format!("{e:?}")),
});
}
nodes
}
fn update_layer_panel(network: &NodeNetwork, metadata: &DocumentMetadata, collapsed: &CollapsedLayers, responses: &mut VecDeque<Message>) {
for (&node_id, node) in &network.nodes {
if node.is_layer() {
let layer = LayerNodeIdentifier::new(node_id, network);
let layer_classification = {
if metadata.is_artboard(layer) {
LayerClassification::Artboard
} else if metadata.is_folder(layer) {
LayerClassification::Folder
} else {
LayerClassification::Layer
}
};
let data = LayerPanelEntry {
id: node_id,
layer_classification,
expanded: layer.has_children(metadata) && !collapsed.0.contains(&layer),
depth: layer.ancestors(metadata).count() - 1,
parent_id: layer.parent(metadata).map(|parent| parent.to_node()),
name: network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(),
tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() },
disabled: network.disabled.contains(&node_id),
};
responses.add(FrontendMessage::UpdateDocumentLayerDetails { data });
}
}
}
fn send_graph(&self, network: &NodeNetwork, graph_open: bool, metadata: &mut DocumentMetadata, selected_nodes: &mut SelectedNodes, collapsed: &CollapsedLayers, responses: &mut VecDeque<Message>) {
metadata.load_structure(network, selected_nodes);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(PropertiesPanelMessage::Refresh);
Self::update_layer_panel(network, metadata, collapsed, responses);
if graph_open {
let links = Self::collect_links(network);
let nodes = self.collect_nodes(&links, network);
responses.add(FrontendMessage::UpdateNodeGraph { nodes, links });
}
}
/// Updates the frontend's selection state in line with the backend
fn update_selected(&mut self, document_network: &NodeNetwork, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) {
self.update_selection_action_buttons(document_network, selected_nodes, responses);
responses.add(FrontendMessage::UpdateNodeGraphSelection {
selected: selected_nodes.selected_nodes_ref().clone(),
});
}
fn remove_references_from_network(network: &mut NodeNetwork, deleting_node_id: NodeId, reconnect: bool) -> bool {
if network.imports.contains(&deleting_node_id) {
warn!("Deleting input node!");
return false;
}
if network.outputs_contain(deleting_node_id) {
warn!("Deleting the output node!");
return false;
}
let mut reconnect_to_input: Option<NodeInput> = None;
if reconnect {
// Check whether the being-deleted node's first (primary) input is a node
if let Some(node) = network.nodes.get(&deleting_node_id) {
// Reconnect to the node below when deleting a layer node.
let reconnect_from_input_index = if node.is_layer() { 1 } else { 0 };
if matches!(&node.inputs.get(reconnect_from_input_index), Some(NodeInput::Node { .. })) {
reconnect_to_input = Some(node.inputs[reconnect_from_input_index].clone());
}
}
}
for (node_id, node) in network.nodes.iter_mut() {
if *node_id == deleting_node_id {
continue;
}
for (input_index, input) in node.inputs.iter_mut().enumerate() {
let NodeInput::Node {
node_id: upstream_node_id,
output_index,
..
} = input
else {
continue;
};
if *upstream_node_id != deleting_node_id {
continue;
}
let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) else {
warn!("Removing input of invalid node type '{}'", node.name);
return false;
};
if let NodeInput::Value { tagged_value, .. } = &node_type.inputs[input_index].default {
let mut refers_to_output_node = false;
// Use the first input node as the new input if deleting node's first input is a node,
// and the current node uses its primary output too
if let Some(reconnect_to_input) = &reconnect_to_input {
if *output_index == 0 {
refers_to_output_node = true;
*input = reconnect_to_input.clone()
}
}
if !refers_to_output_node {
*input = NodeInput::value(tagged_value.clone(), true);
}
}
}
}
true
}
/// Tries to remove a node from the network, returning true on success.
fn remove_node(&mut self, document_network: &mut NodeNetwork, selected_nodes: &mut SelectedNodes, node_id: NodeId, responses: &mut VecDeque<Message>, reconnect: bool) -> bool {
let Some(network) = document_network.nested_network_mut(&self.network) else {
return false;
};
if !Self::remove_references_from_network(network, node_id, reconnect) {
return false;
}
network.nodes.remove(&node_id);
selected_nodes.retain_selected_nodes(|&id| id != node_id);
responses.add(BroadcastEvent::SelectionChanged);
true
}
/// Gets the default node input based on the node name and the input index
pub fn default_node_input(name: String, index: usize) -> Option<NodeInput> {
resolve_document_node_type(&name)
.and_then(|node| node.inputs.get(index))
.map(|input: &DocumentInputType| input.default.clone())
}
/// Returns an iterator of nodes to be copied and their ids, excluding output and input nodes
pub fn copy_nodes<'a>(network: &'a NodeNetwork, new_ids: &'a HashMap<NodeId, NodeId>) -> impl Iterator<Item = (NodeId, DocumentNode)> + 'a {
new_ids
.iter()
.filter(|&(&id, _)| !network.outputs_contain(id))
.filter_map(|(&id, &new)| network.nodes.get(&id).map(|node| (new, node.clone())))
.map(move |(new, node)| (new, node.map_ids(Self::default_node_input, new_ids)))
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct NodeGraphHandlerData<'a> { pub struct NodeGraphHandlerData<'a> {
pub document_network: &'a mut NodeNetwork, pub document_network: &'a mut NodeNetwork,
@ -530,6 +28,15 @@ pub struct NodeGraphHandlerData<'a> {
pub graph_view_overlay_open: bool, pub graph_view_overlay_open: bool,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct NodeGraphMessageHandler {
pub network: Vec<NodeId>,
pub resolved_types: ResolvedDocumentNodeTypes,
pub node_graph_errors: GraphErrors,
has_selection: bool,
widgets: [LayoutGroup; 2],
}
impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGraphMessageHandler { impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGraphMessageHandler {
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, data: NodeGraphHandlerData<'a>) { fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, data: NodeGraphHandlerData<'a>) {
let NodeGraphHandlerData { let NodeGraphHandlerData {
@ -541,6 +48,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
graph_view_overlay_open, graph_view_overlay_open,
.. ..
} = data; } = data;
match message { match message {
// TODO: automatically remove broadcast messages. // TODO: automatically remove broadcast messages.
NodeGraphMessage::Init => { NodeGraphMessage::Init => {
@ -1037,4 +545,380 @@ impl NodeGraphMessageHandler {
actions!(NodeGraphMessageDiscriminant;) actions!(NodeGraphMessageDiscriminant;)
} }
} }
/// Send the cached layout to the frontend for the options bar at the top of the node panel
fn send_node_bar_layout(&self, responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(self.widgets.to_vec())),
layout_target: LayoutTarget::NodeGraphBar,
});
}
/// Updates the buttons for disable and preview
fn update_selection_action_buttons(&mut self, document_network: &NodeNetwork, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) {
if let Some(network) = document_network.nested_network(&self.network) {
let mut widgets = Vec::new();
// Don't allow disabling input or output nodes
let mut selection = selected_nodes.selected_nodes().filter(|&&id| !network.imports.contains(&id) && !network.original_outputs_contain(id));
// If there is at least one other selected node then show the hide or show button
if selection.next().is_some() {
// Check if any of the selected nodes are disabled
let is_hidden = selected_nodes.selected_nodes().any(|id| network.disabled.contains(id));
// Check if multiple nodes are selected
let multiple_nodes = selection.next().is_some();
// Generate the enable or disable button accordingly
let (hide_show_label, hide_show_icon) = if is_hidden { ("Make Visible", "EyeHidden") } else { ("Make Hidden", "EyeVisible") };
let hide_button = TextButton::new(hide_show_label)
.icon(Some(hide_show_icon.to_string()))
.tooltip(if is_hidden { "Show selected nodes/layers" } else { "Hide selected nodes/layers" }.to_string() + if multiple_nodes { "s" } else { "" })
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedHidden))
.on_update(move |_| NodeGraphMessage::ToggleSelectedHidden.into())
.widget_holder();
widgets.push(hide_button);
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
}
// If only one node is selected then show the preview or stop previewing button
let mut selection = selected_nodes.selected_nodes();
if let (Some(&node_id), None) = (selection.next(), selection.next()) {
// Is this node the current output
let is_output = network.outputs_contain(node_id);
// Don't show stop previewing button on the original output node
if !(is_output && network.previous_outputs_contain(node_id).unwrap_or(true)) {
let output_button = TextButton::new(if is_output { "End Preview" } else { "Preview" })
.icon(Some("Rescale".to_string()))
.tooltip(if is_output { "Restore preview to the graph output" } else { "Preview selected node/layer" }.to_string() + " (Shortcut: Alt-click node/layer)")
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
.widget_holder();
widgets.push(output_button);
}
}
self.widgets[0] = LayoutGroup::Row { widgets };
}
self.send_node_bar_layout(responses);
}
/// Collate the properties panel sections for a node graph
pub fn collate_properties(&self, context: &mut NodePropertiesContext, selected_nodes: &SelectedNodes) -> Vec<LayoutGroup> {
let mut network = context.network;
for segment in &self.network {
network = network.nodes.get(segment).and_then(|node| node.implementation.get_network()).unwrap();
}
// We want:
// - If only nodes (no layers) are selected: display each node's properties
// - If one layer is selected, and zero or more of its upstream nodes: display the properties for the layer and its upstream nodes
// - If multiple layers are selected, or one node plus other non-upstream nodes: display nothing
// First, we filter all the selections into layers and nodes
let (mut layers, mut nodes) = (Vec::new(), Vec::new());
for node_id in selected_nodes.selected_nodes() {
if let Some(layer_or_node) = network.nodes.get(node_id) {
if layer_or_node.is_layer() {
layers.push(*node_id);
} else {
nodes.push(*node_id);
}
};
}
// Next, we decide what to display based on the number of layers and nodes selected
match layers.len() {
// If no layers are selected, show properties for all selected nodes
0 => nodes
.iter()
.filter_map(|node_id| network.nodes.get(node_id).map(|node| node_properties::generate_node_properties(node, *node_id, context)))
.collect(),
// If one layer is selected, filter out all selected nodes that are not upstream of it. If there are no nodes left, show properties for the layer. Otherwise, show nothing.
1 => {
let nodes_not_upstream_of_layer = nodes
.into_iter()
.filter(|&selected_node_id| !network.is_node_upstream_of_another_by_primary_flow(layers[0], selected_node_id));
if nodes_not_upstream_of_layer.count() > 0 {
return Vec::new();
}
// 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)
network
.upstream_flow_back_from_nodes(vec![layers[0]], true)
.enumerate()
.take_while(|(i, (node, _))| if *i == 0 { true } else { !node.is_layer() })
.map(|(_, (node, node_id))| node_properties::generate_node_properties(node, node_id, context))
.collect()
}
// If multiple layers and/or nodes are selected, show nothing
_ => Vec::new(),
}
}
fn collect_links(network: &NodeNetwork) -> Vec<FrontendNodeLink> {
network
.nodes
.iter()
.flat_map(|(link_end, node)| node.inputs.iter().filter(|input| input.is_exposed()).enumerate().map(move |(index, input)| (input, link_end, index)))
.filter_map(|(input, &link_end, link_end_input_index)| {
if let NodeInput::Node {
node_id: link_start,
output_index: link_start_output_index,
// TODO: add ui for lambdas
lambda: _,
} = *input
{
Some(FrontendNodeLink {
link_start,
link_start_output_index,
link_end,
link_end_input_index,
})
} else {
None
}
})
.collect::<Vec<_>>()
}
fn collect_nodes(&self, links: &[FrontendNodeLink], network: &NodeNetwork) -> Vec<FrontendNode> {
let connected_node_to_output_lookup = links.iter().map(|link| ((link.link_start, link.link_start_output_index), link.link_end)).collect::<HashMap<_, _>>();
let mut nodes = Vec::new();
for (&node_id, node) in &network.nodes {
let node_path = vec![node_id];
// TODO: This should be based on the graph runtime type inference system in order to change the colors of node connectors to match the data type in use
let Some(document_node_definition) = document_node_types::resolve_document_node_type(&node.name) else {
warn!("Node '{}' does not exist in library", node.name);
continue;
};
// Inputs
let mut inputs = {
let frontend_graph_inputs = document_node_definition.inputs.iter().enumerate().map(|(index, input_type)| {
// Convert the index in all inputs to the index in only the exposed inputs
let index = node.inputs.iter().take(index).filter(|input| input.is_exposed()).count();
FrontendGraphInput {
data_type: input_type.data_type,
name: input_type.name.to_string(),
resolved_type: self.resolved_types.inputs.get(&Source { node: node_path.clone(), index }).map(|input| format!("{input:?}")),
connected: None,
}
});
node.inputs.iter().zip(frontend_graph_inputs).map(|(node_input, mut frontend_graph_input)| {
if let NodeInput::Node { node_id: connected_node_id, .. } = node_input {
frontend_graph_input.connected = Some(*connected_node_id);
}
(node_input, frontend_graph_input)
})
};
let primary_input = inputs.next().filter(|(input, _)| input.is_exposed()).map(|(_, input_type)| input_type);
let exposed_inputs = inputs.filter(|(input, _)| input.is_exposed()).map(|(_, input_type)| input_type).collect();
// Outputs
let mut outputs = document_node_definition.outputs.iter().enumerate().map(|(index, output_type)| FrontendGraphOutput {
data_type: output_type.data_type,
name: output_type.name.to_string(),
resolved_type: self.resolved_types.outputs.get(&Source { node: node_path.clone(), index }).map(|output| format!("{output:?}")),
connected: connected_node_to_output_lookup.get(&(node_id, index)).copied(),
});
let primary_output = node.has_primary_output.then(|| outputs.next()).flatten();
let exposed_outputs = outputs.collect::<Vec<_>>();
// Errors
let errors = self.node_graph_errors.iter().find(|error| error.node_path.starts_with(&node_path)).map(|error| error.error.clone());
nodes.push(FrontendNode {
id: node_id,
is_layer: node.is_layer(),
alias: node.alias.clone(),
name: node.name.clone(),
primary_input,
exposed_inputs,
primary_output,
exposed_outputs,
position: node.metadata.position.into(),
previewed: network.outputs_contain(node_id),
disabled: network.disabled.contains(&node_id),
errors: errors.map(|e| format!("{e:?}")),
});
}
nodes
}
fn update_layer_panel(network: &NodeNetwork, metadata: &DocumentMetadata, collapsed: &CollapsedLayers, responses: &mut VecDeque<Message>) {
for (&node_id, node) in &network.nodes {
if node.is_layer() {
let layer = LayerNodeIdentifier::new(node_id, network);
let layer_classification = {
if metadata.is_artboard(layer) {
LayerClassification::Artboard
} else if metadata.is_folder(layer) {
LayerClassification::Folder
} else {
LayerClassification::Layer
}
};
let data = LayerPanelEntry {
id: node_id,
layer_classification,
expanded: layer.has_children(metadata) && !collapsed.0.contains(&layer),
depth: layer.ancestors(metadata).count() - 1,
parent_id: layer.parent(metadata).map(|parent| parent.to_node()),
name: network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(),
tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() },
disabled: network.disabled.contains(&node_id),
};
responses.add(FrontendMessage::UpdateDocumentLayerDetails { data });
}
}
}
fn send_graph(&self, network: &NodeNetwork, graph_open: bool, metadata: &mut DocumentMetadata, selected_nodes: &mut SelectedNodes, collapsed: &CollapsedLayers, responses: &mut VecDeque<Message>) {
metadata.load_structure(network, selected_nodes);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(PropertiesPanelMessage::Refresh);
Self::update_layer_panel(network, metadata, collapsed, responses);
if graph_open {
let links = Self::collect_links(network);
let nodes = self.collect_nodes(&links, network);
responses.add(FrontendMessage::UpdateNodeGraph { nodes, links });
}
}
/// Updates the frontend's selection state in line with the backend
fn update_selected(&mut self, document_network: &NodeNetwork, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) {
self.update_selection_action_buttons(document_network, selected_nodes, responses);
responses.add(FrontendMessage::UpdateNodeGraphSelection {
selected: selected_nodes.selected_nodes_ref().clone(),
});
}
fn remove_references_from_network(network: &mut NodeNetwork, deleting_node_id: NodeId, reconnect: bool) -> bool {
if network.imports.contains(&deleting_node_id) {
warn!("Deleting input node!");
return false;
}
if network.outputs_contain(deleting_node_id) {
warn!("Deleting the output node!");
return false;
}
let mut reconnect_to_input: Option<NodeInput> = None;
if reconnect {
// Check whether the being-deleted node's first (primary) input is a node
if let Some(node) = network.nodes.get(&deleting_node_id) {
// Reconnect to the node below when deleting a layer node.
let reconnect_from_input_index = if node.is_layer() { 1 } else { 0 };
if matches!(&node.inputs.get(reconnect_from_input_index), Some(NodeInput::Node { .. })) {
reconnect_to_input = Some(node.inputs[reconnect_from_input_index].clone());
}
}
}
for (node_id, node) in network.nodes.iter_mut() {
if *node_id == deleting_node_id {
continue;
}
for (input_index, input) in node.inputs.iter_mut().enumerate() {
let NodeInput::Node {
node_id: upstream_node_id,
output_index,
..
} = input
else {
continue;
};
if *upstream_node_id != deleting_node_id {
continue;
}
let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) else {
warn!("Removing input of invalid node type '{}'", node.name);
return false;
};
if let NodeInput::Value { tagged_value, .. } = &node_type.inputs[input_index].default {
let mut refers_to_output_node = false;
// Use the first input node as the new input if deleting node's first input is a node,
// and the current node uses its primary output too
if let Some(reconnect_to_input) = &reconnect_to_input {
if *output_index == 0 {
refers_to_output_node = true;
*input = reconnect_to_input.clone()
}
}
if !refers_to_output_node {
*input = NodeInput::value(tagged_value.clone(), true);
}
}
}
}
true
}
/// Tries to remove a node from the network, returning true on success.
fn remove_node(&mut self, document_network: &mut NodeNetwork, selected_nodes: &mut SelectedNodes, node_id: NodeId, responses: &mut VecDeque<Message>, reconnect: bool) -> bool {
let Some(network) = document_network.nested_network_mut(&self.network) else {
return false;
};
if !Self::remove_references_from_network(network, node_id, reconnect) {
return false;
}
network.nodes.remove(&node_id);
selected_nodes.retain_selected_nodes(|&id| id != node_id);
responses.add(BroadcastEvent::SelectionChanged);
true
}
/// Gets the default node input based on the node name and the input index
pub fn default_node_input(name: String, index: usize) -> Option<NodeInput> {
resolve_document_node_type(&name)
.and_then(|node| node.inputs.get(index))
.map(|input: &DocumentInputType| input.default.clone())
}
/// Returns an iterator of nodes to be copied and their ids, excluding output and input nodes
pub fn copy_nodes<'a>(network: &'a NodeNetwork, new_ids: &'a HashMap<NodeId, NodeId>) -> impl Iterator<Item = (NodeId, DocumentNode)> + 'a {
new_ids
.iter()
.filter(|&(&id, _)| !network.outputs_contain(id))
.filter_map(|(&id, &new)| network.nodes.get(&id).map(|node| (new, node.clone())))
.map(move |(new, node)| (new, node.map_ids(Self::default_node_input, new_ids)))
}
}
impl Default for NodeGraphMessageHandler {
fn default() -> Self {
let right_side_widgets = vec![
// TODO: Replace this with an "Add Node" button, also next to an "Add Layer" button
TextLabel::new("Right Click in Graph to Add Nodes").italic(true).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextButton::new("Node Graph")
.icon(Some("GraphViewOpen".into()))
.tooltip("Hide Node Graph")
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
.widget_holder(),
];
Self {
network: Vec::new(),
resolved_types: ResolvedDocumentNodeTypes::default(),
node_graph_errors: Vec::new(),
has_selection: false,
widgets: [LayoutGroup::Row { widgets: Vec::new() }, LayoutGroup::Row { widgets: right_side_widgets }],
}
}
} }

View file

@ -1,7 +1,7 @@
#![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_arguments)]
use super::document_node_types::NodePropertiesContext; use super::document_node_types::{NodePropertiesContext, IMAGINATE_NODE};
use super::FrontendGraphDataType; use super::utility_types::FrontendGraphDataType;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*; use crate::messages::prelude::*;
@ -1609,13 +1609,7 @@ pub fn node_section_font(document_node: &DocumentNode, node_id: NodeId, _context
pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> { pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let imaginate_node = [context.nested_path, &[node_id]].concat(); let imaginate_node = [context.nested_path, &[node_id]].concat();
let resolve_input = |name: &str| { let resolve_input = |name: &str| IMAGINATE_NODE.inputs.iter().position(|input| input.name == name).unwrap_or_else(|| panic!("Input {name} not found"));
super::IMAGINATE_NODE
.inputs
.iter()
.position(|input| input.name == name)
.unwrap_or_else(|| panic!("Input {name} not found"))
};
let seed_index = resolve_input("Seed"); let seed_index = resolve_input("Seed");
let resolution_index = resolve_input("Resolution"); let resolution_index = resolve_input("Resolution");
let samples_index = resolve_input("Samples"); let samples_index = resolve_input("Samples");

View file

@ -0,0 +1,118 @@
use graph_craft::document::value::TaggedValue;
use graph_craft::document::NodeId;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendGraphDataType {
#[default]
#[serde(rename = "general")]
General,
#[serde(rename = "raster")]
Raster,
#[serde(rename = "color")]
Color,
#[serde(rename = "general")]
Text,
#[serde(rename = "vector")]
Subpath,
#[serde(rename = "number")]
Number,
#[serde(rename = "general")]
Boolean,
/// Refers to the mathematical vector, with direction and magnitude.
#[serde(rename = "number")]
Vector,
#[serde(rename = "raster")]
GraphicGroup,
#[serde(rename = "artboard")]
Artboard,
#[serde(rename = "color")]
Palette,
}
impl FrontendGraphDataType {
pub const fn with_tagged_value(value: &TaggedValue) -> Self {
match value {
TaggedValue::String(_) => Self::Text,
TaggedValue::F32(_) | TaggedValue::F64(_) | TaggedValue::U32(_) | TaggedValue::DAffine2(_) => Self::Number,
TaggedValue::Bool(_) => Self::Boolean,
TaggedValue::DVec2(_) | TaggedValue::IVec2(_) => Self::Vector,
TaggedValue::Image(_) => Self::Raster,
TaggedValue::ImageFrame(_) => Self::Raster,
TaggedValue::Color(_) => Self::Color,
TaggedValue::RcSubpath(_) | TaggedValue::Subpaths(_) | TaggedValue::VectorData(_) => Self::Subpath,
TaggedValue::GraphicGroup(_) => Self::GraphicGroup,
TaggedValue::Artboard(_) => Self::Artboard,
TaggedValue::Palette(_) => Self::Palette,
_ => Self::General,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphInput {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: String,
#[serde(rename = "resolvedType")]
pub resolved_type: Option<String>,
pub connected: Option<NodeId>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphOutput {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: String,
#[serde(rename = "resolvedType")]
pub resolved_type: Option<String>,
pub connected: Option<NodeId>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNode {
pub id: graph_craft::document::NodeId,
#[serde(rename = "isLayer")]
pub is_layer: bool,
pub alias: String,
pub name: String,
#[serde(rename = "primaryInput")]
pub primary_input: Option<FrontendGraphInput>,
#[serde(rename = "exposedInputs")]
pub exposed_inputs: Vec<FrontendGraphInput>,
#[serde(rename = "primaryOutput")]
pub primary_output: Option<FrontendGraphOutput>,
#[serde(rename = "exposedOutputs")]
pub exposed_outputs: Vec<FrontendGraphOutput>,
pub position: (i32, i32),
pub disabled: bool,
pub previewed: bool,
pub errors: Option<String>,
}
// (link_start, link_end, link_end_input_index)
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeLink {
#[serde(rename = "linkStart")]
pub link_start: NodeId,
#[serde(rename = "linkStartOutputIndex")]
pub link_start_output_index: usize,
#[serde(rename = "linkEnd")]
pub link_end: NodeId,
#[serde(rename = "linkEndInputIndex")]
pub link_end_input_index: usize,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeType {
pub name: String,
pub category: String,
}
impl FrontendNodeType {
pub fn new(name: &'static str, category: &'static str) -> Self {
Self {
name: name.to_string(),
category: category.to_string(),
}
}
}

View file

@ -5,6 +5,6 @@ pub mod utility_functions;
pub mod utility_types; pub mod utility_types;
#[doc(inline)] #[doc(inline)]
pub use overlays_message::*; pub use overlays_message::{OverlaysMessage, OverlaysMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use overlays_message_handler::*; pub use overlays_message_handler::{OverlaysMessageData, OverlaysMessageHandler};

View file

@ -1,10 +1,8 @@
use super::utility_types::{empty_provider, OverlayProvider}; use super::utility_types::{empty_provider, OverlayProvider};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, DocumentMessage, Overlays)] #[impl_message(Message, DocumentMessage, Overlays)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum OverlaysMessage { pub enum OverlaysMessage {
Draw, Draw,

View file

@ -1,6 +1,11 @@
use super::utility_types::OverlayProvider; use super::utility_types::OverlayProvider;
use crate::messages::prelude::*; use crate::messages::prelude::*;
pub struct OverlaysMessageData<'a> {
pub overlays_visible: bool,
pub ipp: &'a InputPreprocessorMessageHandler,
}
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct OverlaysMessageHandler { pub struct OverlaysMessageHandler {
pub overlay_providers: HashSet<OverlayProvider>, pub overlay_providers: HashSet<OverlayProvider>,
@ -8,8 +13,10 @@ pub struct OverlaysMessageHandler {
context: Option<web_sys::CanvasRenderingContext2d>, context: Option<web_sys::CanvasRenderingContext2d>,
} }
impl MessageHandler<OverlaysMessage, (bool, &InputPreprocessorMessageHandler)> for OverlaysMessageHandler { impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessageHandler {
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, (overlays_visible, ipp): (bool, &InputPreprocessorMessageHandler)) { fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, data: OverlaysMessageData) {
let OverlaysMessageData { overlays_visible, ipp } = data;
match message { match message {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
OverlaysMessage::Draw => { OverlaysMessage::Draw => {

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, DocumentMessage, PropertiesPanel)] #[impl_message(Message, DocumentMessage, PropertiesPanel)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum PropertiesPanelMessage { pub enum PropertiesPanelMessage {
// Messages // Messages
Clear, Clear,

View file

@ -1,16 +1,14 @@
use super::utility_types::PropertiesPanelMessageHandlerData; use super::utility_types::PropertiesPanelMessageHandlerData;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::NodePropertiesContext; use crate::messages::portfolio::document::node_graph::document_node_types::NodePropertiesContext;
use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*; use crate::messages::prelude::*;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct PropertiesPanelMessageHandler; pub struct PropertiesPanelMessageHandler {}
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler { impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, (persistent_data, data): (&PersistentData, PropertiesPanelMessageHandlerData)) { fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, (persistent_data, data): (&PersistentData, PropertiesPanelMessageHandlerData)) {
use PropertiesPanelMessage::*;
let PropertiesPanelMessageHandlerData { let PropertiesPanelMessageHandlerData {
node_graph_message_handler, node_graph_message_handler,
executor, executor,
@ -21,7 +19,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
} = data; } = data;
match message { match message {
Clear => { PropertiesPanelMessage::Clear => {
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])), layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesOptions, layout_target: LayoutTarget::PropertiesOptions,
@ -31,7 +29,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
layout_target: LayoutTarget::PropertiesSections, layout_target: LayoutTarget::PropertiesSections,
}); });
} }
Refresh => { PropertiesPanelMessage::Refresh => {
let mut context = NodePropertiesContext { let mut context = NodePropertiesContext {
persistent_data, persistent_data,
responses, responses,

View file

@ -1,11 +1,10 @@
use graph_craft::document::DocumentNode; use graph_craft::document::DocumentNode;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
#[repr(u8)] #[repr(u8)]
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)] #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)]
pub enum Clipboard { pub enum Clipboard {
Internal, Internal,
@ -16,7 +15,7 @@ pub enum Clipboard {
pub const INTERNAL_CLIPBOARD_COUNT: u8 = Clipboard::_InternalClipboardCount as u8; pub const INTERNAL_CLIPBOARD_COUNT: u8 = Clipboard::_InternalClipboardCount as u8;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CopyBufferEntry { pub struct CopyBufferEntry {
pub nodes: HashMap<NodeId, DocumentNode>, pub nodes: HashMap<NodeId, DocumentNode>,
pub selected: bool, pub selected: bool,

View file

@ -1,31 +1,31 @@
use glam::DVec2; use glam::DVec2;
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[repr(transparent)] #[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct DocumentId(pub u64); pub struct DocumentId(pub u64);
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Hash)] #[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash)]
pub enum FlipAxis { pub enum FlipAxis {
X, X,
Y, Y,
} }
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Hash, specta::Type)] #[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash, specta::Type)]
pub enum AlignAxis { pub enum AlignAxis {
X, X,
Y, Y,
} }
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Hash, specta::Type)] #[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Hash, specta::Type)]
pub enum AlignAggregate { pub enum AlignAggregate {
Min, Min,
Max, Max,
Center, Center,
} }
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] #[derive(Default, PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub enum DocumentMode { pub enum DocumentMode {
#[default] #[default]
DesignMode, DesignMode,
@ -124,14 +124,14 @@ impl SnappingState {
} }
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct BoundsSnapping { pub struct BoundsSnapping {
pub edges: bool, pub edges: bool,
pub corners: bool, pub corners: bool,
pub edge_midpoints: bool, pub edge_midpoints: bool,
pub centers: bool, pub centers: bool,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct PointSnapping { pub struct PointSnapping {
pub paths: bool, pub paths: bool,
pub path_intersections: bool, pub path_intersections: bool,
@ -141,7 +141,7 @@ pub struct PointSnapping {
pub normals: bool, pub normals: bool,
pub tangents: bool, pub tangents: bool,
} }
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
pub enum GridType { pub enum GridType {
Rectangle { spacing: DVec2 }, Rectangle { spacing: DVec2 },
Isometric { y_axis_spacing: f64, angle_a: f64, angle_b: f64 }, Isometric { y_axis_spacing: f64, angle_a: f64, angle_b: f64 },
@ -178,7 +178,7 @@ impl GridType {
} }
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct GridSnapping { pub struct GridSnapping {
pub origin: DVec2, pub origin: DVec2,
pub grid_type: GridType, pub grid_type: GridType,
@ -310,7 +310,7 @@ impl fmt::Display for SnappingOptions {
} }
} }
#[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub struct PTZ { pub struct PTZ {
pub pan: DVec2, pub pan: DVec2,
pub tilt: f64, pub tilt: f64,

View file

@ -1,11 +1,10 @@
use graph_craft::document::{NodeId, NodeNetwork}; use graph_craft::document::{NodeId, NodeNetwork};
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, specta::Type)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct RawBuffer(Vec<u8>); pub struct RawBuffer(Vec<u8>);
impl From<&[u64]> for RawBuffer { impl From<&[u64]> for RawBuffer {
@ -14,7 +13,7 @@ impl From<&[u64]> for RawBuffer {
Self(v_from_raw) Self(v_from_raw)
} }
} }
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, specta::Type)] #[derive(Debug, Clone, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct JsRawBuffer(Vec<u8>); pub struct JsRawBuffer(Vec<u8>);
impl From<RawBuffer> for JsRawBuffer { impl From<RawBuffer> for JsRawBuffer {
@ -22,7 +21,7 @@ impl From<RawBuffer> for JsRawBuffer {
Self(buffer.0) Self(buffer.0)
} }
} }
impl Serialize for JsRawBuffer { impl serde::Serialize for JsRawBuffer {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut buffer = serializer.serialize_struct("Buffer", 2)?; let mut buffer = serializer.serialize_struct("Buffer", 2)?;
buffer.serialize_field("pointer", &(self.0.as_ptr() as usize))?; buffer.serialize_field("pointer", &(self.0.as_ptr() as usize))?;
@ -31,7 +30,7 @@ impl Serialize for JsRawBuffer {
} }
} }
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, specta::Type)] #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub enum LayerClassification { pub enum LayerClassification {
#[default] #[default]
Folder, Folder,
@ -39,7 +38,7 @@ pub enum LayerClassification {
Layer, Layer,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, specta::Type)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct LayerPanelEntry { pub struct LayerPanelEntry {
pub id: NodeId, pub id: NodeId,
pub name: String, pub name: String,
@ -53,7 +52,7 @@ pub struct LayerPanelEntry {
pub depth: usize, pub depth: usize,
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, specta::Type)] #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct SelectedNodes(pub Vec<NodeId>); pub struct SelectedNodes(pub Vec<NodeId>);
impl SelectedNodes { impl SelectedNodes {
@ -106,5 +105,5 @@ impl SelectedNodes {
} }
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, specta::Type)] #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct CollapsedLayers(pub Vec<LayerNodeIdentifier>); pub struct CollapsedLayers(pub Vec<LayerNodeIdentifier>);

View file

@ -1,5 +1,5 @@
use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL}; use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL};
use crate::messages::portfolio::document::node_graph::VectorDataModification; use crate::messages::portfolio::document::graph_operation::utility_types::{TransformIn, VectorDataModification};
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
@ -398,11 +398,9 @@ impl<'a> Selected<'a> {
let new_pos_viewport = layerspace_rotation.transform_point2(viewport_point); let new_pos_viewport = layerspace_rotation.transform_point2(viewport_point);
let point = *point_id; let point = *point_id;
let position = new_pos_viewport; let position = new_pos_viewport;
let modification = VectorDataModification::SetManipulatorPosition { point, position };
responses.add(GraphOperationMessage::Vector { responses.add(GraphOperationMessage::Vector { layer, modification });
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
} }
} }

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, PortfolioMessage, MenuBar)] #[impl_message(Message, PortfolioMessage, MenuBar)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize)]
pub enum MenuBarMessage { pub enum MenuBarMessage {
// Messages // Messages
SendLayout, SendLayout,

View file

@ -3,21 +3,26 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::prelude::*; use crate::messages::prelude::*;
pub struct MenuBarMessageData {
pub has_active_document: bool,
pub rulers_visible: bool,
}
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct MenuBarMessageHandler { pub struct MenuBarMessageHandler {
has_active_document: bool, has_active_document: bool,
rulers_visible: bool, rulers_visible: bool,
} }
impl MessageHandler<MenuBarMessage, (bool, bool)> for MenuBarMessageHandler { impl MessageHandler<MenuBarMessage, MenuBarMessageData> for MenuBarMessageHandler {
fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, (has_active_document, rulers_visible): (bool, bool)) { fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, data: MenuBarMessageData) {
use MenuBarMessage::*; let MenuBarMessageData { has_active_document, rulers_visible } = data;
self.has_active_document = has_active_document; self.has_active_document = has_active_document;
self.rulers_visible = rulers_visible; self.rulers_visible = rulers_visible;
match message { match message {
SendLayout => self.send_layout(responses, LayoutTarget::MenuBar), MenuBarMessage::SendLayout => self.send_layout(responses, LayoutTarget::MenuBar),
} }
} }

View file

@ -4,4 +4,4 @@ mod menu_bar_message_handler;
#[doc(inline)] #[doc(inline)]
pub use menu_bar_message::{MenuBarMessage, MenuBarMessageDiscriminant}; pub use menu_bar_message::{MenuBarMessage, MenuBarMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use menu_bar_message_handler::MenuBarMessageHandler; pub use menu_bar_message_handler::{MenuBarMessageData, MenuBarMessageHandler};

View file

@ -8,4 +8,4 @@ pub mod utility_types;
#[doc(inline)] #[doc(inline)]
pub use portfolio_message::{PortfolioMessage, PortfolioMessageDiscriminant}; pub use portfolio_message::{PortfolioMessage, PortfolioMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use portfolio_message_handler::PortfolioMessageHandler; pub use portfolio_message_handler::{PortfolioMessageData, PortfolioMessageHandler};

View file

@ -5,10 +5,8 @@ use crate::messages::prelude::*;
use graphene_core::text::Font; use graphene_core::text::Font;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Portfolio)] #[impl_message(Message, Portfolio)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum PortfolioMessage { pub enum PortfolioMessage {
// Sub-messages // Sub-messages
#[child] #[child]

View file

@ -5,7 +5,7 @@ use crate::messages::dialog::simple_dialogs;
use crate::messages::frontend::utility_types::FrontendDocumentDetails; use crate::messages::frontend::utility_types::FrontendDocumentDetails;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
use crate::messages::portfolio::document::DocumentInputs; use crate::messages::portfolio::document::DocumentMessageData;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup}; use crate::messages::tool::utility_types::{HintData, HintGroup};
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor}; use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
@ -15,6 +15,11 @@ use graphene_core::text::Font;
use std::sync::Arc; use std::sync::Arc;
pub struct PortfolioMessageData<'a> {
pub ipp: &'a InputPreprocessorMessageHandler,
pub preferences: &'a PreferencesMessageHandler,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct PortfolioMessageHandler { pub struct PortfolioMessageHandler {
menu_bar_message_handler: MenuBarMessageHandler, menu_bar_message_handler: MenuBarMessageHandler,
@ -26,8 +31,10 @@ pub struct PortfolioMessageHandler {
pub executor: NodeGraphExecutor, pub executor: NodeGraphExecutor,
} }
impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &PreferencesMessageHandler)> for PortfolioMessageHandler { impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMessageHandler {
fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, (ipp, preferences): (&InputPreprocessorMessageHandler, &PreferencesMessageHandler)) { fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, data: PortfolioMessageData) {
let PortfolioMessageData { ipp, preferences } = data;
match message { match message {
// Sub-messages // Sub-messages
PortfolioMessage::MenuBar(message) => { PortfolioMessage::MenuBar(message) => {
@ -39,12 +46,13 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
rulers_visible = document.rulers_visible; rulers_visible = document.rulers_visible;
} }
self.menu_bar_message_handler.process_message(message, responses, (has_active_document, rulers_visible)); self.menu_bar_message_handler
.process_message(message, responses, MenuBarMessageData { has_active_document, rulers_visible });
} }
PortfolioMessage::Document(message) => { PortfolioMessage::Document(message) => {
if let Some(document_id) = self.active_document_id { if let Some(document_id) = self.active_document_id {
if let Some(document) = self.documents.get_mut(&document_id) { if let Some(document) = self.documents.get_mut(&document_id) {
let document_inputs = DocumentInputs { let document_inputs = DocumentMessageData {
document_id, document_id,
ipp, ipp,
persistent_data: &self.persistent_data, persistent_data: &self.persistent_data,
@ -58,7 +66,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
// Messages // Messages
PortfolioMessage::DocumentPassMessage { document_id, message } => { PortfolioMessage::DocumentPassMessage { document_id, message } => {
if let Some(document) = self.documents.get_mut(&document_id) { if let Some(document) = self.documents.get_mut(&document_id) {
let document_inputs = DocumentInputs { let document_inputs = DocumentMessageData {
document_id, document_id,
ipp, ipp,
persistent_data: &self.persistent_data, persistent_data: &self.persistent_data,

View file

@ -1,14 +1,12 @@
use graphene_std::{imaginate::ImaginatePersistentData, text::FontCache}; use graphene_std::{imaginate::ImaginatePersistentData, text::FontCache};
use serde::{Deserialize, Serialize};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct PersistentData { pub struct PersistentData {
pub font_cache: FontCache, pub font_cache: FontCache,
pub imaginate: ImaginatePersistentData, pub imaginate: ImaginatePersistentData,
} }
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, Serialize, Deserialize)] #[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
pub enum Platform { pub enum Platform {
#[default] #[default]
Unknown, Unknown,
@ -30,7 +28,7 @@ impl Platform {
} }
} }
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, Serialize, Deserialize)] #[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
pub enum KeyboardPlatformLayout { pub enum KeyboardPlatformLayout {
/// Standard keyboard mapping used by Windows and Linux /// Standard keyboard mapping used by Windows and Linux
#[default] #[default]

View file

@ -1,9 +1,7 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Preferences)] #[impl_message(Message, Preferences)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum PreferencesMessage { pub enum PreferencesMessage {
Load { preferences: String }, Load { preferences: String },
ResetToDefaults, ResetToDefaults,

View file

@ -2,9 +2,7 @@ use crate::messages::input_mapper::key_mapping::MappingVariant;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use graph_craft::imaginate_input::ImaginatePreferences; use graph_craft::imaginate_input::ImaginatePreferences;
use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, specta::Type)]
pub struct PreferencesMessageHandler { pub struct PreferencesMessageHandler {
pub imaginate_server_hostname: String, pub imaginate_server_hostname: String,
pub imaginate_refresh_frequency: f64, pub imaginate_refresh_frequency: f64,

View file

@ -1,30 +1,30 @@
// Root // Root
pub use crate::utility_traits::{ActionList, AsMessage, MessageHandler, ToDiscriminant, TransitiveChild}; pub use crate::utility_traits::{ActionList, AsMessage, MessageHandler, ToDiscriminant, TransitiveChild};
// Message, MessageDiscriminant, MessageHandler // Message, MessageData, MessageDiscriminant, MessageHandler
pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler}; pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler};
pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMessageHandler}; pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMessageHandler};
pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageDiscriminant, ExportDialogMessageHandler}; pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageData, ExportDialogMessageDiscriminant, ExportDialogMessageHandler};
pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler}; pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler};
pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler}; pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageData, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler};
pub use crate::messages::dialog::{DialogMessage, DialogMessageDiscriminant, DialogMessageHandler}; pub use crate::messages::dialog::{DialogMessage, DialogMessageData, DialogMessageDiscriminant, DialogMessageHandler};
pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant}; pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant};
pub use crate::messages::globals::{GlobalsMessage, GlobalsMessageDiscriminant, GlobalsMessageHandler}; pub use crate::messages::globals::{GlobalsMessage, GlobalsMessageDiscriminant, GlobalsMessageHandler};
pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappingMessageDiscriminant, KeyMappingMessageHandler}; pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappingMessageData, KeyMappingMessageDiscriminant, KeyMappingMessageHandler};
pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageDiscriminant, InputMapperMessageHandler}; pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageData, InputMapperMessageDiscriminant, InputMapperMessageHandler};
pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler}; pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageData, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler};
pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler}; pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler};
pub use crate::messages::portfolio::document::navigation::{NavigationMessage, NavigationMessageDiscriminant, NavigationMessageHandler}; pub use crate::messages::portfolio::document::graph_operation::{GraphOperationMessage, GraphOperationMessageData, GraphOperationMessageDiscriminant, GraphOperationMessageHandler};
pub use crate::messages::portfolio::document::node_graph::{GraphOperationMessage, GraphOperationMessageDiscriminant, GraphOperationMessageHandler}; pub use crate::messages::portfolio::document::navigation::{NavigationMessage, NavigationMessageData, NavigationMessageDiscriminant, NavigationMessageHandler};
pub use crate::messages::portfolio::document::node_graph::{NodeGraphMessage, NodeGraphMessageDiscriminant, NodeGraphMessageHandler}; pub use crate::messages::portfolio::document::node_graph::{NodeGraphMessage, NodeGraphMessageDiscriminant, NodeGraphMessageHandler};
pub use crate::messages::portfolio::document::overlays::{OverlaysMessage, OverlaysMessageDiscriminant, OverlaysMessageHandler}; pub use crate::messages::portfolio::document::overlays::{OverlaysMessage, OverlaysMessageData, OverlaysMessageDiscriminant, OverlaysMessageHandler};
pub use crate::messages::portfolio::document::properties_panel::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant, PropertiesPanelMessageHandler}; pub use crate::messages::portfolio::document::properties_panel::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant, PropertiesPanelMessageHandler};
pub use crate::messages::portfolio::document::{DocumentMessage, DocumentMessageDiscriminant, DocumentMessageHandler}; pub use crate::messages::portfolio::document::{DocumentMessage, DocumentMessageData, DocumentMessageDiscriminant, DocumentMessageHandler};
pub use crate::messages::portfolio::menu_bar::{MenuBarMessage, MenuBarMessageDiscriminant, MenuBarMessageHandler}; pub use crate::messages::portfolio::menu_bar::{MenuBarMessage, MenuBarMessageData, MenuBarMessageDiscriminant, MenuBarMessageHandler};
pub use crate::messages::portfolio::{PortfolioMessage, PortfolioMessageDiscriminant, PortfolioMessageHandler}; pub use crate::messages::portfolio::{PortfolioMessage, PortfolioMessageData, PortfolioMessageDiscriminant, PortfolioMessageHandler};
pub use crate::messages::preferences::{PreferencesMessage, PreferencesMessageDiscriminant, PreferencesMessageHandler}; pub use crate::messages::preferences::{PreferencesMessage, PreferencesMessageDiscriminant, PreferencesMessageHandler};
pub use crate::messages::tool::transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant, TransformLayerMessageHandler}; pub use crate::messages::tool::transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant, TransformLayerMessageHandler};
pub use crate::messages::tool::{ToolMessage, ToolMessageDiscriminant, ToolMessageHandler}; pub use crate::messages::tool::{ToolMessage, ToolMessageData, ToolMessageDiscriminant, ToolMessageHandler};
pub use crate::messages::workspace::{WorkspaceMessage, WorkspaceMessageDiscriminant, WorkspaceMessageHandler}; pub use crate::messages::workspace::{WorkspaceMessage, WorkspaceMessageDiscriminant, WorkspaceMessageHandler};
// Message, MessageDiscriminant // Message, MessageDiscriminant
@ -50,7 +50,6 @@ pub use crate::messages::tool::tool_messages::text_tool::{TextToolMessage, TextT
// Helper // Helper
pub use crate::messages::globals::global_variables::*; pub use crate::messages::globals::global_variables::*;
pub use crate::messages::portfolio::document::node_graph::TransformIn;
pub use crate::messages::portfolio::document::utility_types::misc::DocumentId; pub use crate::messages::portfolio::document::utility_types::misc::DocumentId;
pub use graphite_proc_macros::*; pub use graphite_proc_macros::*;

View file

@ -3,9 +3,7 @@ use crate::messages::prelude::Message;
use graphene_core::Color; use graphene_core::Color;
use serde::{Deserialize, Serialize}; #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
pub enum ToolColorType { pub enum ToolColorType {
Primary, Primary,
Secondary, Secondary,

View file

@ -1,4 +1,4 @@
use crate::messages::portfolio::document::node_graph::VectorDataModification; use crate::messages::portfolio::document::graph_operation::utility_types::VectorDataModification;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::prelude::*; use crate::messages::prelude::*;

View file

@ -1,8 +1,8 @@
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition; use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::snapping::SnapManager; use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::{input_mapper::utility_types::input_keyboard::Key, portfolio::document::graph_operation::utility_types::TransformIn};
use glam::{DAffine2, DVec2, Vec2Swizzles}; use glam::{DAffine2, DVec2, Vec2Swizzles};
use super::snapping::{SnapCandidatePoint, SnapConstraint, SnapData}; use super::snapping::{SnapCandidatePoint, SnapConstraint, SnapData};

View file

@ -1,7 +1,7 @@
use super::graph_modification_utils; use super::graph_modification_utils;
use super::snapping::{are_manipulator_handles_colinear, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint}; use super::snapping::{are_manipulator_handles_colinear, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint};
use crate::consts::{DRAG_THRESHOLD, INSERT_POINT_ON_SEGMENT_TOO_CLOSE_DISTANCE}; use crate::consts::{DRAG_THRESHOLD, INSERT_POINT_ON_SEGMENT_TOO_CLOSE_DISTANCE};
use crate::messages::portfolio::document::node_graph::VectorDataModification; use crate::messages::portfolio::document::graph_operation::utility_types::VectorDataModification;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::misc::{GeometrySnapSource, SnapSource}; use crate::messages::portfolio::document::utility_types::misc::{GeometrySnapSource, SnapSource};
use crate::messages::prelude::*; use crate::messages::prelude::*;

View file

@ -9,6 +9,6 @@ pub mod utility_types;
#[doc(inline)] #[doc(inline)]
pub use tool_message::{ToolMessage, ToolMessageDiscriminant}; pub use tool_message::{ToolMessage, ToolMessageDiscriminant};
#[doc(inline)] #[doc(inline)]
pub use tool_message_handler::ToolMessageHandler; pub use tool_message_handler::{ToolMessageData, ToolMessageHandler};
#[doc(inline)] #[doc(inline)]
pub use transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant}; pub use transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant};

View file

@ -3,10 +3,8 @@ use crate::messages::prelude::*;
use graphene_core::raster::color::Color; use graphene_core::raster::color::Color;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Tool)] #[impl_message(Message, Tool)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum ToolMessage { pub enum ToolMessage {
// Sub-messages // Sub-messages
#[child] #[child]

View file

@ -9,6 +9,14 @@ use crate::node_graph_executor::NodeGraphExecutor;
use graphene_core::raster::color::Color; use graphene_core::raster::color::Color;
pub struct ToolMessageData<'a> {
pub document_id: DocumentId,
pub document: &'a DocumentMessageHandler,
pub input: &'a InputPreprocessorMessageHandler,
pub persistent_data: &'a PersistentData,
pub node_graph: &'a NodeGraphExecutor,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ToolMessageHandler { pub struct ToolMessageHandler {
pub tool_state: ToolFsmState, pub tool_state: ToolFsmState,
@ -16,13 +24,15 @@ pub struct ToolMessageHandler {
pub shape_editor: ShapeState, pub shape_editor: ShapeState,
} }
impl MessageHandler<ToolMessage, (&DocumentMessageHandler, DocumentId, &InputPreprocessorMessageHandler, &PersistentData, &NodeGraphExecutor)> for ToolMessageHandler { impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
fn process_message( fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, data: ToolMessageData) {
&mut self, let ToolMessageData {
message: ToolMessage, document_id,
responses: &mut VecDeque<Message>, document,
(document, document_id, input, persistent_data, node_graph): (&DocumentMessageHandler, DocumentId, &InputPreprocessorMessageHandler, &PersistentData, &NodeGraphExecutor), input,
) { persistent_data,
node_graph,
} = data;
let font_cache = &persistent_data.font_cache; let font_cache = &persistent_data.font_cache;
match message { match message {

View file

@ -17,7 +17,7 @@ pub struct ArtboardTool {
} }
#[impl_message(Message, ToolMessage, Artboard)] #[impl_message(Message, ToolMessage, Artboard)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum ArtboardToolMessage { pub enum ArtboardToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -50,8 +50,6 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Artboar
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use ArtboardToolFsmState::*;
let mut common = actions!(ArtboardToolMessageDiscriminant; let mut common = actions!(ArtboardToolMessageDiscriminant;
DeleteSelected, DeleteSelected,
NudgeSelected, NudgeSelected,
@ -59,7 +57,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Artboar
); );
let additional = match self.fsm_state { let additional = match self.fsm_state {
Ready => actions!(ArtboardToolMessageDiscriminant; PointerDown), ArtboardToolFsmState::Ready => actions!(ArtboardToolMessageDiscriminant; PointerDown),
_ => actions!(ArtboardToolMessageDiscriminant; PointerUp, Abort), _ => actions!(ArtboardToolMessageDiscriminant; PointerUp, Abort),
}; };
common.extend(additional); common.extend(additional);

View file

@ -1,6 +1,6 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::messages::portfolio::document::node_graph::resolve_document_node_type; use crate::messages::portfolio::document::graph_operation::transform_utils::{get_current_normalized_pivot, get_current_transform};
use crate::messages::portfolio::document::node_graph::transform_utils::{get_current_normalized_pivot, get_current_transform}; use crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
@ -13,7 +13,7 @@ use graphene_core::Color;
const BRUSH_MAX_SIZE: f64 = 5000.; const BRUSH_MAX_SIZE: f64 = 5000.;
#[derive(PartialEq, Copy, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum DrawMode { pub enum DrawMode {
Draw = 0, Draw = 0,
Erase, Erase,
@ -52,7 +52,7 @@ impl Default for BrushOptions {
} }
#[impl_message(Message, ToolMessage, Brush)] #[impl_message(Message, ToolMessage, Brush)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum BrushToolMessage { pub enum BrushToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -65,7 +65,7 @@ pub enum BrushToolMessage {
UpdateOptions(BrushToolMessageOptionsUpdate), UpdateOptions(BrushToolMessageOptionsUpdate),
} }
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum BrushToolMessageOptionsUpdate { pub enum BrushToolMessageOptionsUpdate {
BlendMode(BlendMode), BlendMode(BlendMode),
ChangeDiameter(f64), ChangeDiameter(f64),
@ -222,15 +222,13 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for BrushTo
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use BrushToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(BrushToolMessageDiscriminant; BrushToolFsmState::Ready => actions!(BrushToolMessageDiscriminant;
DragStart, DragStart,
DragStop, DragStop,
UpdateOptions, UpdateOptions,
), ),
Drawing => actions!(BrushToolMessageDiscriminant; BrushToolFsmState::Drawing => actions!(BrushToolMessageDiscriminant;
DragStop, DragStop,
PointerMove, PointerMove,
Abort, Abort,

View file

@ -34,7 +34,7 @@ impl Default for EllipseToolOptions {
} }
} }
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum EllipseOptionsUpdate { pub enum EllipseOptionsUpdate {
FillColor(Option<Color>), FillColor(Option<Color>),
FillColorType(ToolColorType), FillColorType(ToolColorType),
@ -45,7 +45,7 @@ pub enum EllipseOptionsUpdate {
} }
#[impl_message(Message, ToolMessage, Ellipse)] #[impl_message(Message, ToolMessage, Ellipse)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum EllipseToolMessage { pub enum EllipseToolMessage {
// Standard messages // Standard messages
Overlays(OverlayContext), Overlays(OverlayContext),
@ -138,14 +138,12 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Ellipse
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use EllipseToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(EllipseToolMessageDiscriminant; EllipseToolFsmState::Ready => actions!(EllipseToolMessageDiscriminant;
DragStart, DragStart,
PointerMove, PointerMove,
), ),
Drawing => actions!(EllipseToolMessageDiscriminant; EllipseToolFsmState::Drawing => actions!(EllipseToolMessageDiscriminant;
DragStop, DragStop,
Abort, Abort,
PointerMove, PointerMove,

View file

@ -8,7 +8,7 @@ pub struct EyedropperTool {
} }
#[impl_message(Message, ToolMessage, Eyedropper)] #[impl_message(Message, ToolMessage, Eyedropper)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum EyedropperToolMessage { pub enum EyedropperToolMessage {
// Standard messages // Standard messages
Abort, Abort,

View file

@ -8,7 +8,7 @@ pub struct FillTool {
} }
#[impl_message(Message, ToolMessage, Fill)] #[impl_message(Message, ToolMessage, Fill)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FillToolMessage { pub enum FillToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -42,14 +42,12 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FillToo
self.fsm_state.process_event(message, &mut (), tool_data, &(), responses, true); self.fsm_state.process_event(message, &mut (), tool_data, &(), responses, true);
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use FillToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(FillToolMessageDiscriminant; FillToolFsmState::Ready => actions!(FillToolMessageDiscriminant;
FillPrimaryColor, FillPrimaryColor,
FillSecondaryColor, FillSecondaryColor,
), ),
Filling => actions!(FillToolMessageDiscriminant; FillToolFsmState::Filling => actions!(FillToolMessageDiscriminant;
PointerUp, PointerUp,
Abort, Abort,
), ),

View file

@ -1,5 +1,5 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::messages::portfolio::document::node_graph::VectorDataModification; use crate::messages::portfolio::document::graph_operation::utility_types::VectorDataModification;
use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays; use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@ -14,7 +14,6 @@ use graphene_core::Color;
use bezier_rs::ManipulatorGroup; use bezier_rs::ManipulatorGroup;
use glam::DVec2; use glam::DVec2;
use serde::{Deserialize, Serialize};
#[derive(Default)] #[derive(Default)]
pub struct FreehandTool { pub struct FreehandTool {
@ -40,7 +39,7 @@ impl Default for FreehandOptions {
} }
#[impl_message(Message, ToolMessage, Freehand)] #[impl_message(Message, ToolMessage, Freehand)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FreehandToolMessage { pub enum FreehandToolMessage {
// Standard messages // Standard messages
Overlays(OverlayContext), Overlays(OverlayContext),
@ -54,7 +53,7 @@ pub enum FreehandToolMessage {
UpdateOptions(FreehandOptionsUpdate), UpdateOptions(FreehandOptionsUpdate),
} }
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FreehandOptionsUpdate { pub enum FreehandOptionsUpdate {
FillColor(Option<Color>), FillColor(Option<Color>),
FillColorType(ToolColorType), FillColorType(ToolColorType),
@ -149,14 +148,12 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Freehan
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use FreehandToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(FreehandToolMessageDiscriminant; FreehandToolFsmState::Ready => actions!(FreehandToolMessageDiscriminant;
DragStart, DragStart,
DragStop, DragStop,
), ),
Drawing => actions!(FreehandToolMessageDiscriminant; FreehandToolFsmState::Drawing => actions!(FreehandToolMessageDiscriminant;
DragStop, DragStop,
PointerMove, PointerMove,
Abort, Abort,

View file

@ -21,7 +21,7 @@ pub struct GradientOptions {
} }
#[impl_message(Message, ToolMessage, Gradient)] #[impl_message(Message, ToolMessage, Gradient)]
#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum GradientToolMessage { pub enum GradientToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -37,7 +37,7 @@ pub enum GradientToolMessage {
UpdateOptions(GradientOptionsUpdate), UpdateOptions(GradientOptionsUpdate),
} }
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum GradientOptionsUpdate { pub enum GradientOptionsUpdate {
Type(GradientType), Type(GradientType),
} }

View file

@ -1,9 +1,10 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::messages::portfolio::document::node_graph::{self, IMAGINATE_NODE}; use crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type;
use crate::messages::portfolio::document::node_graph::document_node_types::{new_image_network, IMAGINATE_NODE};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::tool::common_functionality::resize::Resize; use crate::messages::tool::common_functionality::resize::Resize;
use serde::{Deserialize, Serialize}; use graph_craft::document::{generate_uuid, DocumentNodeMetadata, NodeId, NodeInput};
#[derive(Default)] #[derive(Default)]
pub struct ImaginateTool { pub struct ImaginateTool {
@ -12,7 +13,7 @@ pub struct ImaginateTool {
} }
#[impl_message(Message, ToolMessage, Imaginate)] #[impl_message(Message, ToolMessage, Imaginate)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum ImaginateToolMessage { pub enum ImaginateToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -35,13 +36,11 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Imagina
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use ImaginateToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(ImaginateToolMessageDiscriminant; ImaginateToolFsmState::Ready => actions!(ImaginateToolMessageDiscriminant;
DragStart, DragStart,
), ),
Drawing => actions!(ImaginateToolMessageDiscriminant; ImaginateToolFsmState::Drawing => actions!(ImaginateToolMessageDiscriminant;
DragStop, DragStop,
Abort, Abort,
Resize, Resize,
@ -107,17 +106,15 @@ impl Fsm for ImaginateToolFsmState {
shape_data.layer = Some(LayerNodeIdentifier::new(NodeId(generate_uuid()), document.network())); shape_data.layer = Some(LayerNodeIdentifier::new(NodeId(generate_uuid()), document.network()));
responses.add(DocumentMessage::DeselectAllLayers); responses.add(DocumentMessage::DeselectAllLayers);
use graph_craft::document::*;
// Utility function to offset the position of each consecutive node // Utility function to offset the position of each consecutive node
let mut pos = 8; let mut pos = 8;
let mut next_pos = || { let mut next_pos = || {
pos += 8; pos += 8;
graph_craft::document::DocumentNodeMetadata::position((pos, 4)) DocumentNodeMetadata::position((pos, 4))
}; };
// Get the node type for the Transform and Imaginate nodes // Get the node type for the Transform and Imaginate nodes
let Some(transform_node_type) = crate::messages::portfolio::document::node_graph::resolve_document_node_type("Transform") else { let Some(transform_node_type) = resolve_document_node_type("Transform") else {
warn!("Transform node should be in registry"); warn!("Transform node should be in registry");
return ImaginateToolFsmState::Drawing; return ImaginateToolFsmState::Drawing;
}; };
@ -128,7 +125,7 @@ impl Fsm for ImaginateToolFsmState {
let imaginate_node_id = NodeId(101); let imaginate_node_id = NodeId(101);
// Create the network based on the Input -> Output passthrough default network // Create the network based on the Input -> Output passthrough default network
let mut network = node_graph::new_image_network(16, imaginate_node_id); let mut network = new_image_network(16, imaginate_node_id);
// Insert the nodes into the default network // Insert the nodes into the default network
network.nodes.insert( network.nodes.insert(
@ -137,7 +134,7 @@ impl Fsm for ImaginateToolFsmState {
); );
network.nodes.insert( network.nodes.insert(
imaginate_node_id, imaginate_node_id,
imaginate_node_type.to_document_node_default_inputs([Some(graph_craft::document::NodeInput::node(transform_node_id, 0))], next_pos()), imaginate_node_type.to_document_node_default_inputs([Some(NodeInput::node(transform_node_id, 0))], next_pos()),
); );
responses.add(NodeGraphMessage::ShiftNode { node_id: imaginate_node_id }); responses.add(NodeGraphMessage::ShiftNode { node_id: imaginate_node_id });

View file

@ -1,5 +1,6 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::consts::LINE_ROTATE_SNAP_ANGLE; use crate::consts::LINE_ROTATE_SNAP_ANGLE;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
@ -34,7 +35,7 @@ impl Default for LineOptions {
} }
#[impl_message(Message, ToolMessage, Line)] #[impl_message(Message, ToolMessage, Line)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum LineToolMessage { pub enum LineToolMessage {
// Standard messages // Standard messages
Overlays(OverlayContext), Overlays(OverlayContext),
@ -49,7 +50,7 @@ pub enum LineToolMessage {
UpdateOptions(LineOptionsUpdate), UpdateOptions(LineOptionsUpdate),
} }
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum LineOptionsUpdate { pub enum LineOptionsUpdate {
LineWeight(f64), LineWeight(f64),
StrokeColor(Option<Color>), StrokeColor(Option<Color>),

View file

@ -25,5 +25,4 @@ pub mod tool_prelude {
pub use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; pub use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
pub use glam::{DAffine2, DVec2}; pub use glam::{DAffine2, DVec2};
pub use serde::{Deserialize, Serialize};
} }

View file

@ -7,7 +7,7 @@ pub struct NavigateTool {
} }
#[impl_message(Message, ToolMessage, Navigate)] #[impl_message(Message, ToolMessage, Navigate)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum NavigateToolMessage { pub enum NavigateToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -45,10 +45,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Navigat
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use NavigateToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(NavigateToolMessageDiscriminant; NavigateToolFsmState::Ready => actions!(NavigateToolMessageDiscriminant;
TranslateCanvasBegin, TranslateCanvasBegin,
RotateCanvasBegin, RotateCanvasBegin,
ZoomCanvasBegin, ZoomCanvasBegin,

View file

@ -21,7 +21,7 @@ pub struct PathTool {
} }
#[impl_message(Message, ToolMessage, Path)] #[impl_message(Message, ToolMessage, Path)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum PathToolMessage { pub enum PathToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -174,10 +174,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
// Different actions depending on state may be wanted: // Different actions depending on state may be wanted:
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use PathToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(PathToolMessageDiscriminant; PathToolFsmState::Ready => actions!(PathToolMessageDiscriminant;
FlipSmoothSharp, FlipSmoothSharp,
MouseDown, MouseDown,
Delete, Delete,
@ -188,7 +186,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
BreakPath, BreakPath,
DeleteAndBreakPath, DeleteAndBreakPath,
), ),
Dragging => actions!(PathToolMessageDiscriminant; PathToolFsmState::Dragging => actions!(PathToolMessageDiscriminant;
Escape, Escape,
RightClick, RightClick,
FlipSmoothSharp, FlipSmoothSharp,
@ -198,7 +196,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
BreakPath, BreakPath,
DeleteAndBreakPath, DeleteAndBreakPath,
), ),
DrawingBox => actions!(PathToolMessageDiscriminant; PathToolFsmState::DrawingBox => actions!(PathToolMessageDiscriminant;
FlipSmoothSharp, FlipSmoothSharp,
DragStop, DragStop,
PointerMove, PointerMove,
@ -209,7 +207,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
Escape, Escape,
RightClick, RightClick,
), ),
InsertPoint => actions!(PathToolMessageDiscriminant; PathToolFsmState::InsertPoint => actions!(PathToolMessageDiscriminant;
Enter, Enter,
MouseDown, MouseDown,
PointerMove, PointerMove,

View file

@ -1,6 +1,6 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::consts::LINE_ROTATE_SNAP_ANGLE; use crate::consts::LINE_ROTATE_SNAP_ANGLE;
use crate::messages::portfolio::document::node_graph::VectorDataModification; use crate::messages::portfolio::document::graph_operation::utility_types::VectorDataModification;
use crate::messages::portfolio::document::overlays::utility_functions::path_overlays; use crate::messages::portfolio::document::overlays::utility_functions::path_overlays;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@ -41,7 +41,7 @@ impl Default for PenOptions {
} }
#[impl_message(Message, ToolMessage, Pen)] #[impl_message(Message, ToolMessage, Pen)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum PenToolMessage { pub enum PenToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -68,7 +68,7 @@ enum PenToolFsmState {
PlacingAnchor, PlacingAnchor,
} }
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum PenOptionsUpdate { pub enum PenOptionsUpdate {
FillColor(Option<Color>), FillColor(Option<Color>),
FillColorType(ToolColorType), FillColorType(ToolColorType),
@ -217,12 +217,10 @@ impl PenToolData {
let first_or_last = if from_start { manipulator_groups.first() } else { manipulator_groups.last() }; let first_or_last = if from_start { manipulator_groups.first() } else { manipulator_groups.last() };
let Some(last_handle) = first_or_last else { return }; let Some(last_handle) = first_or_last else { return };
let id = last_handle.id; let id = last_handle.id;
let modification = VectorDataModification::SetManipulatorColinearHandlesState { id, colinear: false };
// Stop the handles on the first point from being colinear // Stop the handles on the first point from being colinear
responses.add(GraphOperationMessage::Vector { responses.add(GraphOperationMessage::Vector { layer, modification });
layer,
modification: VectorDataModification::SetManipulatorColinearHandlesState { id, colinear: false },
});
} }
fn create_new_path( fn create_new_path(

View file

@ -39,7 +39,7 @@ impl Default for PolygonOptions {
} }
#[impl_message(Message, ToolMessage, Polygon)] #[impl_message(Message, ToolMessage, Polygon)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum PolygonToolMessage { pub enum PolygonToolMessage {
// Standard messages // Standard messages
Overlays(OverlayContext), Overlays(OverlayContext),
@ -54,13 +54,13 @@ pub enum PolygonToolMessage {
UpdateOptions(PolygonOptionsUpdate), UpdateOptions(PolygonOptionsUpdate),
} }
#[derive(PartialEq, Copy, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum PolygonType { pub enum PolygonType {
Convex = 0, Convex = 0,
Star = 1, Star = 1,
} }
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum PolygonOptionsUpdate { pub enum PolygonOptionsUpdate {
FillColor(Option<Color>), FillColor(Option<Color>),
FillColorType(ToolColorType), FillColorType(ToolColorType),
@ -182,14 +182,12 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Polygon
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use PolygonToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(PolygonToolMessageDiscriminant; PolygonToolFsmState::Ready => actions!(PolygonToolMessageDiscriminant;
DragStart, DragStart,
PointerMove, PointerMove,
), ),
Drawing => actions!(PolygonToolMessageDiscriminant; PolygonToolFsmState::Drawing => actions!(PolygonToolMessageDiscriminant;
DragStop, DragStop,
Abort, Abort,
PointerMove, PointerMove,

View file

@ -34,7 +34,7 @@ impl Default for RectangleToolOptions {
} }
} }
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum RectangleOptionsUpdate { pub enum RectangleOptionsUpdate {
FillColor(Option<Color>), FillColor(Option<Color>),
FillColorType(ToolColorType), FillColorType(ToolColorType),
@ -45,7 +45,7 @@ pub enum RectangleOptionsUpdate {
} }
#[impl_message(Message, ToolMessage, Rectangle)] #[impl_message(Message, ToolMessage, Rectangle)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum RectangleToolMessage { pub enum RectangleToolMessage {
// Standard messages // Standard messages
Overlays(OverlayContext), Overlays(OverlayContext),
@ -127,14 +127,12 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Rectang
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use RectangleToolFsmState::*;
match self.fsm_state { match self.fsm_state {
Ready => actions!(RectangleToolMessageDiscriminant; RectangleToolFsmState::Ready => actions!(RectangleToolMessageDiscriminant;
DragStart, DragStart,
PointerMove, PointerMove,
), ),
Drawing => actions!(RectangleToolMessageDiscriminant; RectangleToolFsmState::Drawing => actions!(RectangleToolMessageDiscriminant;
DragStop, DragStop,
Abort, Abort,
PointerMove, PointerMove,

View file

@ -4,6 +4,7 @@ use super::tool_prelude::*;
use crate::application::generate_uuid; use crate::application::generate_uuid;
use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE}; use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE};
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition; use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis}; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis};
@ -31,12 +32,12 @@ pub struct SelectOptions {
nested_selection_behavior: NestedSelectionBehavior, nested_selection_behavior: NestedSelectionBehavior,
} }
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum SelectOptionsUpdate { pub enum SelectOptionsUpdate {
NestedSelectionBehavior(NestedSelectionBehavior), NestedSelectionBehavior(NestedSelectionBehavior),
} }
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, Serialize, Deserialize, specta::Type)] #[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum NestedSelectionBehavior { pub enum NestedSelectionBehavior {
#[default] #[default]
Deepest, Deepest,
@ -52,7 +53,7 @@ impl fmt::Display for NestedSelectionBehavior {
} }
} }
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct SelectToolPointerKeys { pub struct SelectToolPointerKeys {
pub axis_align: Key, pub axis_align: Key,
pub snap_angle: Key, pub snap_angle: Key,
@ -61,7 +62,7 @@ pub struct SelectToolPointerKeys {
} }
#[impl_message(Message, ToolMessage, Select)] #[impl_message(Message, ToolMessage, Select)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum SelectToolMessage { pub enum SelectToolMessage {
// Standard messages // Standard messages
Abort, Abort,
@ -204,8 +205,6 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SelectT
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
use SelectToolFsmState::*;
let mut common = actions!(SelectToolMessageDiscriminant; let mut common = actions!(SelectToolMessageDiscriminant;
PointerMove, PointerMove,
Abort, Abort,
@ -214,7 +213,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SelectT
); );
let additional = match self.fsm_state { let additional = match self.fsm_state {
Ready { .. } => actions!(SelectToolMessageDiscriminant; DragStart), SelectToolFsmState::Ready { .. } => actions!(SelectToolMessageDiscriminant; DragStart),
_ => actions!(SelectToolMessageDiscriminant; DragStop), _ => actions!(SelectToolMessageDiscriminant; DragStop),
}; };
common.extend(additional); common.extend(additional);

Some files were not shown because too many files have changed in this diff Show more