From 312b8cd7da1a336ab9925590beb5dff5217dd96f Mon Sep 17 00:00:00 2001 From: mfish33 Date: Thu, 6 Jan 2022 14:51:01 -0800 Subject: [PATCH 1/6] - graphite document artboard implementation - autosave document load hitch fix - Autosave will delete saved files when graphite document version changes --- editor/src/consts.rs | 3 + .../src/document/artboard_message_handler.rs | 87 +++++++++++++++++++ editor/src/document/document_file.rs | 40 ++++++++- .../src/document/document_message_handler.rs | 5 +- editor/src/document/mod.rs | 4 + editor/src/document/movement_handler.rs | 11 +++ .../src/frontend/frontend_message_handler.rs | 3 +- editor/src/lib.rs | 1 + frontend/src/components/panels/Document.vue | 16 +++- .../widgets/inputs/MenuBarInput.vue | 12 ++- frontend/src/dispatcher/js-messages.ts | 7 ++ frontend/src/lifetime/auto-save.ts | 30 +++++-- frontend/src/state/wasm-loader.ts | 2 +- frontend/wasm/src/api.rs | 13 ++- graphene/src/consts.rs | 3 - graphene/src/document.rs | 7 +- 16 files changed, 213 insertions(+), 31 deletions(-) create mode 100644 editor/src/document/artboard_message_handler.rs diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 2a76e1ccfe..dd414e6e72 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -43,3 +43,6 @@ pub const FILE_EXPORT_SUFFIX: &str = ".svg"; // COLORS pub const COLOR_ACCENT: Color = Color::from_unsafe(0x00 as f32 / 255., 0xA8 as f32 / 255., 0xFF as f32 / 255.); + +// Document +pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.1"; diff --git a/editor/src/document/artboard_message_handler.rs b/editor/src/document/artboard_message_handler.rs new file mode 100644 index 0000000000..ada6e9c861 --- /dev/null +++ b/editor/src/document/artboard_message_handler.rs @@ -0,0 +1,87 @@ +pub use crate::document::layer_panel::*; +use crate::document::{DocumentMessage, LayerMetadata}; +use crate::input::InputPreprocessor; +use crate::message_prelude::*; +use glam::{DAffine2, DVec2}; +use graphene::color::Color; +use graphene::document::Document; +use graphene::layers::style; +use graphene::layers::style::Fill; +use graphene::Operation as DocumentOperation; + +use graphene::document::Document as GrapheneDocument; +use graphene::layers::style::ViewMode; +use serde::{Deserialize, Serialize}; +use std::collections::VecDeque; + +#[impl_message(Message, DocumentMessage, Artboard)] +#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] +pub enum ArtboardMessage { + DispatchOperation(Box), + AddArtboard { top: f64, left: f64, height: f64, width: f64 }, + RenderArtboards, +} + +impl From for ArtboardMessage { + fn from(operation: DocumentOperation) -> Self { + Self::DispatchOperation(Box::new(operation)) + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ArtboardMessageHandler { + pub artboards_graphene_document: GrapheneDocument, + pub artboard_ids: Vec, +} + +impl MessageHandler for ArtboardMessageHandler { + fn process_action(&mut self, message: ArtboardMessage, _data: (&mut LayerMetadata, &Document, &InputPreprocessor), responses: &mut VecDeque) { + // let (layer_metadata, document, ipp) = data; + use ArtboardMessage::*; + match message { + DispatchOperation(operation) => match self.artboards_graphene_document.handle_operation(&operation) { + Ok(_) => (), + Err(e) => log::error!("Artboard Error: {:?}", e), + }, + AddArtboard { top, left, height, width } => { + let artboard_id = generate_uuid(); + self.artboard_ids.push(artboard_id); + + responses.push_back( + ArtboardMessage::DispatchOperation( + DocumentOperation::AddRect { + path: vec![artboard_id], + insert_index: -1, + transform: DAffine2::from_scale_angle_translation(DVec2::new(height, width), 0., DVec2::new(top, left)).to_cols_array(), + style: style::PathStyle::new(None, Some(Fill::new(Color::WHITE))), + } + .into(), + ) + .into(), + ); + } + RenderArtboards => {} + } + + // Render an infinite canvas if there is no artboards + if self.artboard_ids.is_empty() { + responses.push_back( + FrontendMessage::UpdateArtboards { + svg: "".to_string(), + } + .into(), + ) + } else { + responses.push_back( + FrontendMessage::UpdateArtboards { + svg: self.artboards_graphene_document.render_root(ViewMode::Normal), + } + .into(), + ); + } + } + + fn actions(&self) -> ActionList { + actions!(ArtBoardMessageDiscriminant;) + } +} diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index f33fb1404f..7ffffa3b89 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use std::collections::VecDeque; +use super::artboard_message_handler::ArtboardMessage; +use super::artboard_message_handler::ArtboardMessageHandler; pub use super::layer_panel::*; use super::movement_handler::{MovementMessage, MovementMessageHandler}; use super::overlay_message_handler::OverlayMessageHandler; @@ -8,6 +10,7 @@ use super::transform_layer_handler::{TransformLayerMessage, TransformLayerMessag use super::vectorize_layer_metadata; use crate::consts::DEFAULT_DOCUMENT_NAME; +use crate::consts::GRAPHITE_DOCUMENT_VERSION; use crate::consts::{ASYMPTOTIC_EFFECT, FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING}; use crate::document::Clipboard; use crate::input::InputPreprocessor; @@ -76,10 +79,12 @@ pub struct DocumentMessageHandler { movement_handler: MovementMessageHandler, #[serde(skip)] overlay_message_handler: OverlayMessageHandler, + artboard_message_handler: ArtboardMessageHandler, #[serde(skip)] transform_layer_handler: TransformLayerMessageHandler, pub snapping_enabled: bool, pub view_mode: ViewMode, + pub version: String, } impl Default for DocumentMessageHandler { @@ -94,9 +99,11 @@ impl Default for DocumentMessageHandler { layer_range_selection_reference: Vec::new(), movement_handler: MovementMessageHandler::default(), overlay_message_handler: OverlayMessageHandler::default(), + artboard_message_handler: ArtboardMessageHandler::default(), transform_layer_handler: TransformLayerMessageHandler::default(), snapping_enabled: true, view_mode: ViewMode::default(), + version: GRAPHITE_DOCUMENT_VERSION.to_string(), } } } @@ -111,6 +118,8 @@ pub enum DocumentMessage { DispatchOperation(Box), #[child] Overlay(OverlayMessage), + #[child] + Artboard(ArtboardMessage), UpdateLayerMetadata { layer_path: Vec, layer_metadata: LayerMetadata, @@ -186,20 +195,34 @@ impl DocumentMessageHandler { } pub fn deserialize_document(serialized_content: &str) -> Result { - log::info!("Deserializing: {:?}", serialized_content); - serde_json::from_str(serialized_content).map_err(|e| DocumentError::InvalidFile(e.to_string())) + let deserialized_result: Result = serde_json::from_str(serialized_content).map_err(|e| DocumentError::InvalidFile(e.to_string())); + match deserialized_result { + Ok(document) => { + if document.version != GRAPHITE_DOCUMENT_VERSION { + Err(DocumentError::InvalidFile("Graphite Document Version Mismatch".to_string())) + } else { + Ok(document) + } + } + Err(e) => Err(e), + } } pub fn with_name(name: String, ipp: &InputPreprocessor) -> Self { let mut document = Self { name, ..Self::default() }; - document.graphene_document.root.transform = document.movement_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.); + let starting_root_transform = document.movement_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.); + document.graphene_document.root.transform = starting_root_transform; + document.artboard_message_handler.artboards_graphene_document.root.transform = starting_root_transform; document } - pub fn with_name_and_content(name: String, serialized_content: String) -> Result { + pub fn with_name_and_content(name: String, serialized_content: String, ipp: &InputPreprocessor) -> Result { match Self::deserialize_document(&serialized_content) { Ok(mut document) => { document.name = name; + let starting_root_transform = document.movement_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.); + document.graphene_document.root.transform = starting_root_transform; + document.artboard_message_handler.artboards_graphene_document.root.transform = starting_root_transform; Ok(document) } Err(DocumentError::InvalidFile(msg)) => Err(EditorError::Document(msg)), @@ -514,6 +537,13 @@ impl MessageHandler for DocumentMessageHand ); // responses.push_back(OverlayMessage::RenderOverlays.into()); } + Artboard(message) => { + self.artboard_message_handler.process_action( + message, + (Self::layer_metadata_mut_no_borrow_self(&mut self.layer_metadata, &[]), &self.graphene_document, ipp), + responses, + ); + } ExportDocument => { let bbox = self.graphene_document.visible_layers_bounding_box().unwrap_or([DVec2::ZERO, ipp.viewport_bounds.size()]); let size = bbox[1] - bbox[0]; @@ -760,6 +790,8 @@ impl MessageHandler for DocumentMessageHand } .into(), ); + responses.push_back(ArtboardMessage::RenderArtboards.into()); + let document_transform = &self.movement_handler; let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform.scale * SCALE_EFFECT; diff --git a/editor/src/document/document_message_handler.rs b/editor/src/document/document_message_handler.rs index 7ece129f1b..fe4307d522 100644 --- a/editor/src/document/document_message_handler.rs +++ b/editor/src/document/document_message_handler.rs @@ -1,5 +1,5 @@ use super::{DocumentMessageHandler, LayerMetadata}; -use crate::consts::DEFAULT_DOCUMENT_NAME; +use crate::consts::{DEFAULT_DOCUMENT_NAME, GRAPHITE_DOCUMENT_VERSION}; use crate::frontend::frontend_message_handler::FrontendDocumentDetails; use crate::input::InputPreprocessor; use crate::message_prelude::*; @@ -293,7 +293,7 @@ impl MessageHandler for DocumentsMessageHa document, document_is_saved, } => { - let document = DocumentMessageHandler::with_name_and_content(document_name, document); + let document = DocumentMessageHandler::with_name_and_content(document_name, document, ipp); match document { Ok(mut document) => { document.set_save_state(document_is_saved); @@ -333,6 +333,7 @@ impl MessageHandler for DocumentsMessageHa id, name: document.name.clone(), }, + version: GRAPHITE_DOCUMENT_VERSION.to_string(), } .into(), ) diff --git a/editor/src/document/mod.rs b/editor/src/document/mod.rs index 35cc531e00..34e8764152 100644 --- a/editor/src/document/mod.rs +++ b/editor/src/document/mod.rs @@ -3,6 +3,7 @@ mod document_message_handler; pub mod layer_panel; mod movement_handler; mod overlay_message_handler; +mod artboard_message_handler; mod transform_layer_handler; mod vectorize_layer_metadata; @@ -17,5 +18,8 @@ pub use document_message_handler::{Clipboard, DocumentsMessage, DocumentsMessage pub use movement_handler::{MovementMessage, MovementMessageDiscriminant}; #[doc(inline)] pub use overlay_message_handler::{OverlayMessage, OverlayMessageDiscriminant}; + +#[doc(inline)] +pub use artboard_message_handler::{ArtboardMessage, ArtboardMessageDiscriminant}; #[doc(inline)] pub use transform_layer_handler::{TransformLayerMessage, TransformLayerMessageDiscriminant}; diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index e5d625bf82..478807212e 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -88,6 +88,17 @@ impl MovementMessageHandler { } .into(), ); + + responses.push_back( + ArtboardMessage::DispatchOperation( + DocumentOperation::SetLayerTransform { + path: vec![], + transform: self.calculate_offset_transform(scaled_half_viewport).to_cols_array(), + } + .into(), + ) + .into(), + ); } } diff --git a/editor/src/frontend/frontend_message_handler.rs b/editor/src/frontend/frontend_message_handler.rs index 65049c9fd6..5eb6b9ccf0 100644 --- a/editor/src/frontend/frontend_message_handler.rs +++ b/editor/src/frontend/frontend_message_handler.rs @@ -28,11 +28,12 @@ pub enum FrontendMessage { UpdateLayer { data: LayerPanelEntry }, UpdateArtwork { svg: String }, UpdateOverlays { svg: String }, + UpdateArtboards { svg: String }, UpdateScrollbars { position: (f64, f64), size: (f64, f64), multiplier: (f64, f64) }, UpdateRulers { origin: (f64, f64), spacing: f64, interval: f64 }, ExportDocument { document: String, name: String }, SaveDocument { document: String, name: String }, - AutoSaveDocument { document: String, details: FrontendDocumentDetails }, + AutoSaveDocument { document: String, details: FrontendDocumentDetails, version: String }, RemoveAutoSaveDocument { document_id: u64 }, OpenDocumentBrowse, UpdateWorkingColors { primary: Color, secondary: Color }, diff --git a/editor/src/lib.rs b/editor/src/lib.rs index b1ac444ddc..9be63d5dfe 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -62,6 +62,7 @@ pub mod message_prelude { pub use crate::document::{DocumentsMessage, DocumentsMessageDiscriminant}; pub use crate::document::{MovementMessage, MovementMessageDiscriminant}; pub use crate::document::{OverlayMessage, OverlayMessageDiscriminant}; + pub use crate::document::{ArtboardMessage, ArtboardMessageDiscriminant}; pub use crate::document::{TransformLayerMessage, TransformLayerMessageDiscriminant}; pub use crate::frontend::{FrontendMessage, FrontendMessageDiscriminant}; pub use crate::global::{GlobalMessage, GlobalMessageDiscriminant}; diff --git a/frontend/src/components/panels/Document.vue b/frontend/src/components/panels/Document.vue index 7ec283b3b1..24db878898 100644 --- a/frontend/src/components/panels/Document.vue +++ b/frontend/src/components/panels/Document.vue @@ -124,6 +124,7 @@
+
@@ -234,8 +235,12 @@ width: 100%; height: 100%; - &.artwork { - background: #ffffff; + // &.artwork { + // background: #ffffff; + // } + + &.artboards { + user-select: none; } &.overlays { @@ -251,7 +256,7 @@