mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-24 08:05:04 +00:00
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:
parent
ed3f7acdd7
commit
0a9bd41be1
134 changed files with 1860 additions and 1865 deletions
|
@ -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),
|
||||||
|
|
|
@ -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, ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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() });
|
||||||
|
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 },
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
// ===============
|
// ===============
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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>> },
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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::*;
|
|
@ -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;
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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};
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
}
|
|
@ -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 {
|
|
@ -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::*;
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 }],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
|
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>);
|
||||||
|
|
|
@ -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 },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
),
|
),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
||||||
|
|
|
@ -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>),
|
||||||
|
|
|
@ -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};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue