diff --git a/document-legacy/src/document_metadata.rs b/document-legacy/src/document_metadata.rs index 53bfe07d3..31aec7f23 100644 --- a/document-legacy/src/document_metadata.rs +++ b/document-legacy/src/document_metadata.rs @@ -81,36 +81,53 @@ impl DocumentMetadata { self.document_to_viewport * self.transform_from_document(layer) } - /// Runs an intersection test with all layers and a document space quad - pub fn intersect_quad(&self, viewport_quad: Quad) -> Option<(LayerNodeIdentifier, &Vec)> { + /// Runs an intersection test with all layers and a viewport space quad + pub fn intersect_quad(&self, viewport_quad: Quad) -> Option { let document_quad = self.document_to_viewport.inverse() * viewport_quad; self.root() .decendants(self) .filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets))) .find(|(layer, target)| target.iter().any(|target| target.intersect_rectangle(document_quad, self.transform_from_document(*layer)))) + .map(|(layer, _)| layer) } - /// Find the layer that has been clicked on from a document space location - pub fn click(&self, viewport_location: DVec2) -> Option<(LayerNodeIdentifier, &Vec)> { + /// Find all of the layers that were clicked on from a viewport space location + pub fn click_xray<'a>(&'a self, viewport_location: DVec2) -> impl Iterator + 'a { let point = self.document_to_viewport.inverse().transform_point2(viewport_location); self.root() .decendants(self) .filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets))) - .find(|(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.transform_from_document(*layer)))) + .filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.transform_from_document(*layer)))) + .map(|(layer, _)| layer) + } + + /// Find the layer that has been clicked on from a viewport space location + pub fn click(&self, viewport_location: DVec2) -> Option { + self.click_xray(viewport_location).next() } /// Get the bounding box of the click target of the specified layer in the specified transform space - pub fn bounding_box(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> { + pub fn bounding_box_with_transform(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> { self.click_targets .get(&layer)? .iter() .filter_map(|click_target| click_target.subpath.bounding_box_with_transform(transform)) .reduce(Quad::combine_bounds) } + + /// Get the bounding box of the click target of the specified layer in document space + pub fn bounding_box_document(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> { + self.bounding_box_with_transform(layer, self.transform_from_document(layer)) + } + + /// Get the bounding box of the click target of the specified layer in viewport space + pub fn bounding_box_viewport(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> { + self.bounding_box_with_transform(layer, self.transform_from_viewport(layer)) + } } /// Id of a layer node -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, specta::Type)] pub struct LayerNodeIdentifier(NonZeroU64); impl Default for LayerNodeIdentifier { @@ -119,6 +136,18 @@ impl Default for LayerNodeIdentifier { } } +impl core::fmt::Debug for LayerNodeIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("LayerNodeIdentifier").field(&self.to_node()).finish() + } +} + +impl core::fmt::Display for LayerNodeIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Layer(node_id={})", self.to_node())) + } +} + impl LayerNodeIdentifier { const ROOT: Self = LayerNodeIdentifier::new_unchecked(0); diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 350ab6d9c..bd7dbfb4f 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -33,9 +33,6 @@ pub struct DispatcherMessageHandlers { const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[ MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)), MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Rerender))), - MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Artboard( - ArtboardMessageDiscriminant::RenderArtboards, - ))), MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))), MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel( PropertiesPanelMessageDiscriminant::ResendActiveProperties, diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index c7c2ae49c..5f225f61a 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -1,6 +1,7 @@ use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog}; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::prelude::*; +use crate::messages::tool::common_functionality::graph_modification_utils::is_artboard; /// 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)] @@ -67,23 +68,19 @@ impl MessageHandler { if let Some(document) = portfolio.active_document() { - let artboard_handler = &document.artboard_message_handler; let mut index = 0; - let artboards = artboard_handler - .artboard_ids - .iter() - .rev() - .filter_map(|&artboard| artboard_handler.artboards_document.layer(&[artboard]).ok().map(|layer| (artboard, layer))) - .map(|(artboard, layer)| { + let artboards = document + .document_legacy + .metadata + .all_layers() + .filter(|&layer| is_artboard(layer, &document.document_legacy)) + .map(|layer| { ( - artboard, - format!( - "Artboard: {}", - layer.name.clone().unwrap_or_else(|| { - index += 1; - format!("Untitled {index}") - }) - ), + layer, + format!("Artboard: {}", { + index += 1; + format!("Untitled {index}") + }), ) }) .collect(); diff --git a/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs b/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs index 6fe154676..879fb2eb5 100644 --- a/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs +++ b/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs @@ -2,7 +2,7 @@ use crate::messages::frontend::utility_types::{ExportBounds, FileType}; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::prelude::*; -use document_legacy::LayerId; +use document_legacy::document_metadata::LayerNodeIdentifier; /// A dialog to allow users to customize their file export. #[derive(Debug, Clone, Default)] @@ -11,7 +11,7 @@ pub struct ExportDialogMessageHandler { pub scale_factor: f64, pub bounds: ExportBounds, pub transparent_background: bool, - pub artboards: HashMap, + pub artboards: HashMap, pub has_selection: bool, } @@ -86,7 +86,20 @@ impl LayoutHolder for ExportDialogMessageHandler { .widget_holder(), ]; - let artboards = self.artboards.iter().map(|(&val, name)| (ExportBounds::Artboard(val), name.to_string(), false)); + let resolution = vec![ + TextLabel::new("Scale Factor").table_align(true).min_width(100).widget_holder(), + Separator::new(SeparatorType::Unrelated).widget_holder(), + NumberInput::new(Some(self.scale_factor)) + .unit("") + .min(0.) + .max((1u64 << std::f64::MANTISSA_DIGITS) as f64) + .disabled(self.file_type == FileType::Svg) + .on_update(|number_input: &NumberInput| ExportDialogMessage::ScaleFactor(number_input.value.unwrap()).into()) + .min_width(200) + .widget_holder(), + ]; + + let artboards = self.artboards.iter().map(|(&layer, name)| (ExportBounds::Artboard(layer), name.to_string(), false)); let mut export_area_options = vec![ (ExportBounds::AllArtwork, "All Artwork".to_string(), false), (ExportBounds::Selection, "Selection".to_string(), !self.has_selection), diff --git a/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs b/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs index cc608bf23..605264a37 100644 --- a/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs +++ b/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs @@ -26,11 +26,6 @@ impl MessageHandler for NewDocumentDialogMessageHa if !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0 { let id = generate_uuid(); - responses.add(ArtboardMessage::AddArtboard { - id: Some(id), - position: (0., 0.), - size: (self.dimensions.x as f64, self.dimensions.y as f64), - }); responses.add(GraphOperationMessage::NewArtboard { id, artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()), diff --git a/editor/src/messages/frontend/utility_types.rs b/editor/src/messages/frontend/utility_types.rs index 2332ea582..fff7e87d1 100644 --- a/editor/src/messages/frontend/utility_types.rs +++ b/editor/src/messages/frontend/utility_types.rs @@ -1,3 +1,4 @@ +use document_legacy::document_metadata::LayerNodeIdentifier; use document_legacy::LayerId; use serde::{Deserialize, Serialize}; @@ -63,5 +64,5 @@ pub enum ExportBounds { #[default] AllArtwork, Selection, - Artboard(LayerId), + Artboard(LayerNodeIdentifier), } diff --git a/editor/src/messages/portfolio/document/artboard/artboard_message.rs b/editor/src/messages/portfolio/document/artboard/artboard_message.rs deleted file mode 100644 index 37bf038aa..000000000 --- a/editor/src/messages/portfolio/document/artboard/artboard_message.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::messages::prelude::*; - -use document_legacy::LayerId; -use document_legacy::Operation as DocumentOperation; - -use serde::{Deserialize, Serialize}; - -#[remain::sorted] -#[impl_message(Message, DocumentMessage, Artboard)] -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] -pub enum ArtboardMessage { - // Sub-messages - #[remain::unsorted] - DispatchOperation(Box), - - // Messages - AddArtboard { - id: Option, - position: (f64, f64), - size: (f64, f64), - }, - ClearArtboards, - DeleteArtboard { - artboard: LayerId, - }, - RenderArtboards, - ResizeArtboard { - artboard: LayerId, - position: (f64, f64), - size: (f64, f64), - }, -} - -impl From for ArtboardMessage { - fn from(operation: DocumentOperation) -> Self { - Self::DispatchOperation(Box::new(operation)) - } -} diff --git a/editor/src/messages/portfolio/document/artboard/artboard_message_handler.rs b/editor/src/messages/portfolio/document/artboard/artboard_message_handler.rs deleted file mode 100644 index ee2d28a0d..000000000 --- a/editor/src/messages/portfolio/document/artboard/artboard_message_handler.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::application::generate_uuid; -use crate::messages::portfolio::utility_types::PersistentData; -use crate::messages::prelude::*; - -use document_legacy::document::Document as DocumentLegacy; -use document_legacy::layers::style::{self, Fill}; -use document_legacy::DocumentResponse; -use document_legacy::LayerId; -use document_legacy::Operation as DocumentOperation; -use graphene_core::raster::color::Color; - -use glam::DAffine2; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] -pub struct ArtboardMessageHandler { - pub artboards_document: DocumentLegacy, - pub artboard_ids: Vec, -} - -impl MessageHandler for ArtboardMessageHandler { - #[remain::check] - fn process_message(&mut self, message: ArtboardMessage, responses: &mut VecDeque, _persistent_data: &PersistentData) { - use ArtboardMessage::*; - - #[remain::sorted] - match message { - // Sub-messages - #[remain::unsorted] - DispatchOperation(operation) => match self.artboards_document.handle_operation(*operation) { - Ok(Some(document_responses)) => { - for response in document_responses { - match &response { - DocumentResponse::LayerChanged { path } => responses.add(PropertiesPanelMessage::CheckSelectedWasUpdated { path: path.clone() }), - DocumentResponse::DeletedLayer { path } => responses.add(PropertiesPanelMessage::CheckSelectedWasDeleted { path: path.clone() }), - DocumentResponse::DocumentChanged => responses.add(ArtboardMessage::RenderArtboards), - _ => {} - }; - responses.add(BroadcastEvent::DocumentIsDirty); - } - } - Ok(None) => {} - Err(e) => error!("Artboard Error: {:?}", e), - }, - - // Messages - AddArtboard { id, position, size } => { - let artboard_id = id.unwrap_or_else(generate_uuid); - self.artboard_ids.push(artboard_id); - - responses.add(ArtboardMessage::DispatchOperation( - DocumentOperation::AddRect { - path: vec![artboard_id], - insert_index: -1, - transform: DAffine2::from_scale_angle_translation(size.into(), 0., position.into()).to_cols_array(), - style: style::PathStyle::new(None, Fill::solid(Color::WHITE)), - } - .into(), - )); - - responses.add(DocumentMessage::RenderDocument); - } - ClearArtboards => { - // TODO: Make this remove the artboard layers from the graph (and cleanly reconnect the artwork) - responses.add(DialogMessage::RequestComingSoonDialog { issue: None }); - // for &artboard in self.artboard_ids.iter() { - // responses.add_front(ArtboardMessage::DeleteArtboard { artboard }); - // } - } - DeleteArtboard { artboard } => { - self.artboard_ids.retain(|&id| id != artboard); - - responses.add(ArtboardMessage::DispatchOperation(Box::new(DocumentOperation::DeleteLayer { path: vec![artboard] }))); - - responses.add(DocumentMessage::RenderDocument); - } - RenderArtboards => { - // Render an infinite canvas if there are no artboards - if self.artboard_ids.is_empty() { - responses.add(FrontendMessage::UpdateDocumentArtboards { - svg: r##""##.to_string(), - }) - // TODO: Delete this whole legacy code path when cleaning up/removing the old (non-node based) artboard implementation - // TODO: The below code was used to draw the non-node based artboards, but we still need the above code to draw the infinite canvas until the refactor is complete and all this code can be removed - // } else { - // let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None); - // responses.add(FrontendMessage::UpdateDocumentArtboards { - // svg: self.artboards_document.render_root(&render_data), - // }); - } - } - ResizeArtboard { artboard, position, mut size } => { - if size.0.abs() == 0. { - size.0 = size.0.signum(); - } - if size.1.abs() == 0. { - size.1 = size.1.signum(); - } - - responses.add(ArtboardMessage::DispatchOperation(Box::new(DocumentOperation::SetLayerTransform { - path: vec![artboard], - transform: DAffine2::from_scale_angle_translation(size.into(), 0., position.into()).to_cols_array(), - }))); - - responses.add(DocumentMessage::RenderDocument); - } - } - } - - fn actions(&self) -> ActionList { - actions!(ArtboardMessageDiscriminant;) - } -} - -impl ArtboardMessageHandler { - pub fn is_infinite_canvas(&self) -> bool { - self.artboard_ids.is_empty() - } -} diff --git a/editor/src/messages/portfolio/document/artboard/mod.rs b/editor/src/messages/portfolio/document/artboard/mod.rs deleted file mode 100644 index 7eb959b2c..000000000 --- a/editor/src/messages/portfolio/document/artboard/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod artboard_message; -mod artboard_message_handler; - -#[doc(inline)] -pub use artboard_message::{ArtboardMessage, ArtboardMessageDiscriminant}; -#[doc(inline)] -pub use artboard_message_handler::ArtboardMessageHandler; diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index 96b13234b..c9caa7ec9 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -23,9 +23,6 @@ pub enum DocumentMessage { DispatchOperation(Box), #[remain::unsorted] #[child] - Artboard(ArtboardMessage), - #[remain::unsorted] - #[child] Navigation(NavigationMessage), #[remain::unsorted] #[child] @@ -51,7 +48,6 @@ pub enum DocumentMessage { }, BackupDocument { document: DocumentLegacy, - artboard: Box, layer_metadata: HashMap, LayerMetadata>, }, ClearLayerTree, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index e4e808b88..e319a712d 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -62,7 +62,6 @@ pub struct DocumentMessageHandler { navigation_handler: NavigationMessageHandler, #[serde(skip)] overlays_message_handler: OverlaysMessageHandler, - pub artboard_message_handler: ArtboardMessageHandler, properties_panel_message_handler: PropertiesPanelMessageHandler, #[serde(skip)] node_graph_handler: NodeGraphMessageHandler, @@ -95,7 +94,6 @@ impl Default for DocumentMessageHandler { navigation_handler: NavigationMessageHandler::default(), overlays_message_handler: OverlaysMessageHandler::default(), - artboard_message_handler: ArtboardMessageHandler::default(), properties_panel_message_handler: PropertiesPanelMessageHandler::default(), node_graph_handler: Default::default(), } @@ -176,10 +174,6 @@ impl MessageHandler { - self.artboard_message_handler.process_message(message, responses, persistent_data); - } - #[remain::unsorted] Navigation(message) => { let document_bounds = self.document_bounds(&render_data); self.navigation_handler.process_message( @@ -197,7 +191,6 @@ impl MessageHandler self.backup_with_document(document, *artboard, layer_metadata, responses), + BackupDocument { document, layer_metadata } => self.backup_with_document(document, layer_metadata, responses), ClearLayerTree => { // Send an empty layer tree let data_buffer: RawBuffer = Self::default().serialize_root().as_slice().into(); @@ -375,13 +368,13 @@ impl MessageHandler { - let old_transforms = self.remove_document_transform(); + let old_artwork_transform = self.remove_document_transform(); // Calculate the bounding box of the region to be exported let bounds = match bounds { ExportBounds::AllArtwork => self.all_layer_bounds(&render_data), ExportBounds::Selection => self.selected_visible_layers_bounding_box(&render_data), - ExportBounds::Artboard(id) => self.artboard_message_handler.artboards_document.layer(&[id]).ok().and_then(|layer| layer.aabb(&render_data)), + ExportBounds::Artboard(id) => self.document_legacy.metadata.bounding_box_document(id), } .unwrap_or_default(); let size = bounds[1] - bounds[0]; @@ -389,7 +382,7 @@ impl MessageHandler [DAffine2; 2] { - let old_artwork_transform = self.document_legacy.root.transform; - self.document_legacy.root.transform = DAffine2::IDENTITY; + pub(crate) fn remove_document_transform(&mut self) -> DAffine2 { + let old_artwork_transform = self.document_legacy.metadata.document_to_viewport; + self.document_legacy.metadata.document_to_viewport = DAffine2::IDENTITY; DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root); - let old_artboard_transform = self.artboard_message_handler.artboards_document.root.transform; - self.artboard_message_handler.artboards_document.root.transform = DAffine2::IDENTITY; - DocumentLegacy::mark_children_as_dirty(&mut self.artboard_message_handler.artboards_document.root); - - [old_artwork_transform, old_artboard_transform] + old_artwork_transform } /// Transform the artwork and artboard back to their original scales - pub(crate) fn restore_document_transform(&mut self, [old_artwork_transform, old_artboard_transform]: [DAffine2; 2]) { - self.document_legacy.root.transform = old_artwork_transform; + pub(crate) fn restore_document_transform(&mut self, old_artwork_transform: DAffine2) { + self.document_legacy.metadata.document_to_viewport = old_artwork_transform; DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root); - - self.artboard_message_handler.artboards_document.root.transform = old_artboard_transform; - DocumentLegacy::mark_children_as_dirty(&mut self.artboard_message_handler.artboards_document.root); } pub fn render_document(&mut self, size: DVec2, transform: DAffine2, transparent_background: bool, persistent_data: &PersistentData, render_mode: DocumentRenderMode) -> String { @@ -1079,13 +1064,10 @@ impl DocumentMessageHandler { DocumentRenderMode::LayerCutout(layer_path, background) => (self.document_legacy.render_layer(layer_path, &render_data).unwrap(), Some(background)), }; let artboards = match transparent_background { - false => self.artboard_message_handler.artboards_document.render_root(&render_data), + false => "", true => "".into(), }; - let outside_artboards_color = outside.map_or_else( - || if self.artboard_message_handler.artboard_ids.is_empty() { "ffffff" } else { "222222" }.to_string(), - |col| col.rgba_hex(), - ); + let outside_artboards_color = outside.map_or_else(|| if false { "ffffff" } else { "222222" }.to_string(), |col| col.rgba_hex()); let outside_artboards = match transparent_background { false => format!(r##""##, outside_artboards_color), true => "".into(), @@ -1123,11 +1105,11 @@ impl DocumentMessageHandler { } } - pub fn with_name(name: String, ipp: &InputPreprocessorMessageHandler) -> Self { + pub fn with_name(name: String, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque) -> Self { let mut document = Self { name, ..Self::default() }; - let starting_root_transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.); - document.document_legacy.metadata.document_to_viewport = starting_root_transform; - document.artboard_message_handler.artboards_document.root.transform = starting_root_transform; + let transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.); + document.document_legacy.metadata.document_to_viewport = transform; + responses.add(DocumentMessage::UpdateDocumentTransform { transform }); document } @@ -1170,10 +1152,6 @@ impl DocumentMessageHandler { self.document_legacy.combined_viewport_bounding_box(paths, render_data) } - pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId], render_data: &RenderData) -> Option<([DVec2; 2], DAffine2)> { - self.artboard_message_handler.artboards_document.bounding_box_and_transform(path, render_data).unwrap_or(None) - } - pub fn selected_layers(&self) -> impl Iterator { self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice())) } @@ -1216,18 +1194,11 @@ impl DocumentMessageHandler { }) } - /// Returns the bounding boxes for all visible layers and artboards, optionally excluding any paths. - pub fn bounding_boxes<'a>(&'a self, ignore_document: Option<&'a Vec>>, ignore_artboard: Option, render_data: &'a RenderData) -> impl Iterator + 'a { + /// Returns the bounding boxes for all visible layers, optionally excluding any paths. + pub fn bounding_boxes<'a>(&'a self, ignore_document: Option<&'a Vec>>, _ignore_artboard: Option, render_data: &'a RenderData) -> impl Iterator + 'a { self.visible_layers() .filter(move |path| ignore_document.map_or(true, |ignore_document| !ignore_document.iter().any(|ig| ig.as_slice() == *path))) .filter_map(|path| self.document_legacy.viewport_bounding_box(path, render_data).ok()?) - .chain( - self.artboard_message_handler - .artboard_ids - .iter() - .filter(move |&&id| Some(id) != ignore_artboard) - .filter_map(|&path| self.artboard_message_handler.artboards_document.viewport_bounding_box(&[path], render_data).ok()?), - ) } fn serialize_structure(&self, folder: LayerNodeIdentifier, structure: &mut Vec, data: &mut Vec, path: &mut Vec) { @@ -1348,9 +1319,9 @@ impl DocumentMessageHandler { } /// Places a document into the history system - fn backup_with_document(&mut self, document: DocumentLegacy, artboard: ArtboardMessageHandler, layer_metadata: HashMap, LayerMetadata>, responses: &mut VecDeque) { + fn backup_with_document(&mut self, document: DocumentLegacy, layer_metadata: HashMap, LayerMetadata>, responses: &mut VecDeque) { self.document_redo_history.clear(); - self.document_undo_history.push_back(DocumentSave { document, artboard, layer_metadata }); + self.document_undo_history.push_back(DocumentSave { document, layer_metadata }); if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { self.document_undo_history.pop_front(); } @@ -1361,14 +1332,13 @@ impl DocumentMessageHandler { /// Copies the entire document into the history system pub fn backup(&mut self, responses: &mut VecDeque) { - self.backup_with_document(self.document_legacy.clone(), self.artboard_message_handler.clone(), self.layer_metadata.clone(), responses); + self.backup_with_document(self.document_legacy.clone(), self.layer_metadata.clone(), responses); } /// Push a message backing up the document in its current state pub fn backup_nonmut(&self, responses: &mut VecDeque) { responses.add(DocumentMessage::BackupDocument { document: self.document_legacy.clone(), - artboard: Box::new(self.artboard_message_handler.clone()), layer_metadata: self.layer_metadata.clone(), }); } @@ -1380,20 +1350,16 @@ impl DocumentMessageHandler { } /// Replace the document with a new document save, returning the document save. - pub fn replace_document(&mut self, DocumentSave { document, artboard, layer_metadata }: DocumentSave) -> DocumentSave { + pub fn replace_document(&mut self, DocumentSave { document, layer_metadata }: DocumentSave) -> DocumentSave { // Keeping the root is required if the bounds of the viewport have changed during the operation - let old_root = self.document_legacy.root.transform; - let old_artboard_root = self.artboard_message_handler.artboards_document.root.transform; + let old_root = self.document_legacy.metadata.document_to_viewport; let document = std::mem::replace(&mut self.document_legacy, document); - let artboard = std::mem::replace(&mut self.artboard_message_handler, artboard); - self.document_legacy.root.transform = old_root; - self.artboard_message_handler.artboards_document.root.transform = old_artboard_root; + self.document_legacy.metadata.document_to_viewport = old_root; self.document_legacy.root.cache_dirty = true; - self.artboard_message_handler.artboards_document.root.cache_dirty = true; let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata); - DocumentSave { document, artboard, layer_metadata } + DocumentSave { document, layer_metadata } } pub fn undo(&mut self, responses: &mut VecDeque) -> Result<(), EditorError> { @@ -1403,7 +1369,7 @@ impl DocumentMessageHandler { let selected_paths: Vec> = self.selected_layers().map(|path| path.to_vec()).collect(); match self.document_undo_history.pop_back() { - Some(DocumentSave { document, artboard, layer_metadata }) => { + Some(DocumentSave { document, layer_metadata }) => { // Update the currently displayed layer on the Properties panel if the selection changes after an undo action // Also appropriately update the Properties panel if an undo action results in a layer being deleted let prev_selected_paths: Vec> = layer_metadata.iter().filter_map(|(layer_id, metadata)| metadata.selected.then_some(layer_id.clone())).collect(); @@ -1412,7 +1378,7 @@ impl DocumentMessageHandler { responses.add(BroadcastEvent::SelectionChanged); } - let document_save = self.replace_document(DocumentSave { document, artboard, layer_metadata }); + let document_save = self.replace_document(DocumentSave { document, layer_metadata }); self.document_redo_history.push_back(document_save); if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { @@ -1438,7 +1404,7 @@ impl DocumentMessageHandler { let selected_paths: Vec> = self.selected_layers().map(|path| path.to_vec()).collect(); match self.document_redo_history.pop_back() { - Some(DocumentSave { document, artboard, layer_metadata }) => { + Some(DocumentSave { document, layer_metadata }) => { // Update currently displayed layer on property panel if selection changes after redo action // Also appropriately update property panel if redo action results in a layer being added let next_selected_paths: Vec> = layer_metadata.iter().filter_map(|(layer_id, metadata)| metadata.selected.then_some(layer_id.clone())).collect(); @@ -1447,7 +1413,7 @@ impl DocumentMessageHandler { responses.add(BroadcastEvent::SelectionChanged); } - let document_save = self.replace_document(DocumentSave { document, artboard, layer_metadata }); + let document_save = self.replace_document(DocumentSave { document, layer_metadata }); self.document_undo_history.push_back(document_save); if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { self.document_undo_history.pop_front(); @@ -1521,7 +1487,10 @@ impl DocumentMessageHandler { pub fn layer_panel_entry_from_path(&self, path: &[LayerId], render_data: &RenderData) -> Option { let layer_metadata = self.layer_metadata(path); - let transform = self.document_legacy.generate_transform_across_scope(path, Some(self.document_legacy.root.transform.inverse())).ok()?; + let transform = self + .document_legacy + .generate_transform_across_scope(path, Some(self.document_legacy.metadata.document_to_viewport.inverse())) + .ok()?; let layer = self.document_legacy.layer(path).ok()?; Some(LayerPanelEntry::new(layer_metadata, transform, layer, path.to_vec(), render_data)) @@ -1545,11 +1514,7 @@ impl DocumentMessageHandler { /// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable)) pub fn document_bounds(&self, render_data: &RenderData) -> Option<[DVec2; 2]> { - if self.artboard_message_handler.is_infinite_canvas() { - self.all_layer_bounds(render_data) - } else { - self.artboard_message_handler.artboards_document.viewport_bounding_box(&[], render_data).ok().flatten() - } + self.all_layer_bounds(render_data) } /// Calculate the path that new layers should be inserted to. diff --git a/editor/src/messages/portfolio/document/mod.rs b/editor/src/messages/portfolio/document/mod.rs index 922bfe3fd..d8e358984 100644 --- a/editor/src/messages/portfolio/document/mod.rs +++ b/editor/src/messages/portfolio/document/mod.rs @@ -1,7 +1,6 @@ mod document_message; mod document_message_handler; -pub mod artboard; pub mod navigation; pub mod node_graph; pub mod overlays; diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs index ad59e26b6..8e994535b 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs @@ -378,37 +378,42 @@ impl<'a> ModifyInputsContext<'a> { } fn delete_layer(&mut self, id: NodeId) { - if !self.network.nodes.contains_key(&id) { + let Some(node) = self.network.nodes.get(&id) else { warn!("Deleting layer node that does not exist"); return; - } + }; - let mut new_input = None; - let post_node = self.outwards_links.get(&id).and_then(|links| links.first().copied()); - let mut delete_nodes = vec![id]; - let mut is_artboard = false; - for (node, id) in self.network.primary_flow_from_opt(Some(id)) { - delete_nodes.push(id); - if node.name == "Artboard" { - new_input = Some(node.inputs[0].clone()); - is_artboard = true; - break; + LayerNodeIdentifier::new(id, self.network).delete(self.document_metadata); + + let new_input = node.inputs[7].clone(); + + for post_node in self.outwards_links.get(&id).unwrap_or(&Vec::new()) { + let Some(node) = self.network.nodes.get_mut(post_node) else { + continue; + }; + + for input in &mut node.inputs { + if let NodeInput::Node { node_id, .. } = input { + if *node_id == id { + *input = new_input.clone(); + } + } } } - if !is_artboard { - LayerNodeIdentifier::new(id, self.network).delete(self.document_metadata); + + let mut delete_nodes = vec![id]; + for (_node, id) in self.network.primary_flow_from_opt(Some(id)) { + if self.outwards_links.get(&id).is_some_and(|outwards| outwards.len() == 1) { + delete_nodes.push(id); + } } - self.responses.add(DocumentMessage::DocumentStructureChanged); for node_id in delete_nodes { self.network.nodes.remove(&node_id); } - if let (Some(new_input), Some(post_node)) = (new_input, post_node) { - if let Some(node) = self.network.nodes.get_mut(&post_node) { - node.inputs[0] = new_input; - } - } + self.responses.add(DocumentMessage::DocumentStructureChanged); + self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true }); } } diff --git a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message.rs b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message.rs index 2cde5f2e9..2750587f1 100644 --- a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message.rs +++ b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message.rs @@ -1,6 +1,5 @@ use super::utility_types::TransformOp; use crate::messages::layout::utility_types::widget_prelude::*; -use crate::messages::portfolio::document::utility_types::misc::TargetDocument; use crate::messages::prelude::*; use document_legacy::layers::style::{Fill, Stroke}; @@ -24,7 +23,7 @@ pub enum PropertiesPanelMessage { ModifyStroke { stroke: Stroke }, ModifyTransform { value: f64, transform_op: TransformOp }, ResendActiveProperties, - SetActiveLayers { paths: Vec>, document: TargetDocument }, + SetActiveLayers { paths: Vec> }, SetPivot { new_position: PivotPosition }, UpdateSelectedDocumentProperties, } diff --git a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs index 8a9d6207b..578904f42 100644 --- a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs +++ b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs @@ -1,8 +1,7 @@ -use super::utility_functions::{register_artboard_layer_properties, register_artwork_layer_properties, register_document_graph_properties}; +use super::utility_functions::{register_artwork_layer_properties, register_document_graph_properties}; use super::utility_types::PropertiesPanelMessageHandlerData; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::properties_panel::utility_functions::apply_transform_operation; -use crate::messages::portfolio::document::utility_types::misc::TargetDocument; use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::prelude::*; @@ -14,7 +13,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct PropertiesPanelMessageHandler { - active_selection: Option<(Vec, TargetDocument)>, + active_selection: Option>, } impl<'a> MessageHandler)> for PropertiesPanelMessageHandler { @@ -25,28 +24,23 @@ impl<'a> MessageHandler artboard_document, - TargetDocument::Artwork => artwork_document, - }; let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None); match message { - SetActiveLayers { paths, document } => { + SetActiveLayers { paths } => { if paths.len() != 1 { // TODO: Allow for multiple selected layers responses.add(PropertiesPanelMessage::ClearSelection); responses.add(NodeGraphMessage::CloseNodeGraph); } else { let path = paths.into_iter().next().unwrap(); - if Some((path.clone(), document)) != self.active_selection { + if Some(&path) != self.active_selection.as_ref() { // Update the layer visibility - if get_document(document) + if artwork_document .layer(&path) .ok() .filter(|layer| LayerDataTypeDiscriminant::from(&layer.data) == LayerDataTypeDiscriminant::Layer) @@ -57,7 +51,7 @@ impl<'a> MessageHandler MessageHandler { - let (path, target_document) = self.active_selection.as_ref().expect("Received update for properties panel with no active layer"); - let layer = get_document(*target_document).layer(path).unwrap(); + let path = self.active_selection.as_ref().expect("Received update for properties panel with no active layer"); + let layer = artwork_document.layer(path).unwrap(); let transform = apply_transform_operation(layer, transform_op, value, &render_data); self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }, true, responses); } ModifyName { name } => { - let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer"); + let path = self.active_selection.clone().expect("Received update for properties panel with no active layer"); self.create_document_operation(Operation::SetLayerName { path, name }, true, responses); } ModifyPreserveAspect { preserve_aspect } => { - let (layer_path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer"); + let layer_path = self.active_selection.clone().expect("Received update for properties panel with no active layer"); self.create_document_operation(Operation::SetLayerPreserveAspect { layer_path, preserve_aspect }, true, responses); } ModifyFill { fill } => { - let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer"); + let path = self.active_selection.clone().expect("Received update for properties panel with no active layer"); self.create_document_operation(Operation::SetLayerFill { path, fill }, true, responses); } ModifyStroke { stroke } => { - let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer"); + let path = self.active_selection.clone().expect("Received update for properties panel with no active layer"); self.create_document_operation(Operation::SetLayerStroke { path, stroke }, true, responses); } SetPivot { new_position } => { - let (layer, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer"); + let layer = self.active_selection.clone().expect("Received update for properties panel with no active layer"); let position: Option = new_position.into(); let pivot = position.unwrap(); @@ -136,13 +130,9 @@ impl<'a> MessageHandler { - if let Some((path, target_document)) = self.active_selection.clone() { - let document = get_document(target_document); - let layer = document.layer(&path).unwrap(); - match target_document { - TargetDocument::Artboard => register_artboard_layer_properties(layer, responses, persistent_data), - TargetDocument::Artwork => register_artwork_layer_properties(document, path, layer, responses, persistent_data, node_graph_message_handler, executor), - } + if let Some(path) = self.active_selection.clone() { + let layer = artwork_document.layer(&path).unwrap(); + register_artwork_layer_properties(artwork_document, path, layer, responses, persistent_data, node_graph_message_handler, executor); } else { let context = crate::messages::portfolio::document::node_graph::NodePropertiesContext { persistent_data, @@ -158,7 +148,6 @@ impl<'a> MessageHandler responses.add(PropertiesPanelMessage::SetActiveLayers { paths: selected_layers.map(|path| path.to_vec()).collect(), - document: TargetDocument::Artwork, }), } } @@ -170,29 +159,18 @@ impl<'a> MessageHandler bool { - let last_active_path_id = self.active_selection.as_ref().and_then(|(v, _)| v.last().copied()); + let last_active_path_id = self.active_selection.as_ref().and_then(|v| v.last().copied()); let last_modified = path.last().copied(); matches!((last_active_path_id, last_modified), (Some(active_last), Some(modified_last)) if active_last == modified_last) } fn create_document_operation(&self, operation: Operation, commit_history: bool, responses: &mut VecDeque) { - let (_, target_document) = self.active_selection.as_ref().unwrap(); - match *target_document { - TargetDocument::Artboard => { - // Commit history is not respected as the artboard document is not saved in the history system. - - // Dispatch the relevant operation to the artboard document - responses.add(ArtboardMessage::DispatchOperation(Box::new(operation))) - } - TargetDocument::Artwork => { - // Commit to history before the modification - if commit_history { - responses.add(DocumentMessage::StartTransaction); - } - - // Dispatch the relevant operation to the main document - responses.add(DocumentMessage::DispatchOperation(Box::new(operation))); - } + // Commit to history before the modification + if commit_history { + responses.add(DocumentMessage::StartTransaction); } + + // Dispatch the relevant operation to the main document + responses.add(DocumentMessage::DispatchOperation(Box::new(operation))); } } diff --git a/editor/src/messages/portfolio/document/properties_panel/utility_functions.rs b/editor/src/messages/portfolio/document/properties_panel/utility_functions.rs index 46d4956c5..28a8cc0c6 100644 --- a/editor/src/messages/portfolio/document/properties_panel/utility_functions.rs +++ b/editor/src/messages/portfolio/document/properties_panel/utility_functions.rs @@ -64,139 +64,6 @@ pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value ((pivot * delta * pivot.inverse()) * layer.transform).to_cols_array() } -pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDeque, persistent_data: &PersistentData) { - let options_bar = vec![LayoutGroup::Row { - widgets: vec![ - IconLabel::new("NodeArtboard").tooltip("Artboard").widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), - TextInput::new(layer.name.clone().unwrap_or_else(|| "Untitled Artboard".to_string())) - .on_update(|text_input: &TextInput| PropertiesPanelMessage::ModifyName { name: text_input.value.clone() }.into()) - .widget_holder(), - Separator::new(SeparatorType::Related).widget_holder(), - PopoverButton::new("Additional Options", "Coming soon").widget_holder(), - ], - }]; - - let properties_body = { - let LayerDataType::Shape(shape) = &layer.data else { panic!("Artboards can only be shapes") }; - let Fill::Solid(color) = shape.style.fill() else { panic!("Artboard must have a solid fill") }; - - let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None); - let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&render_data)); - - vec![LayoutGroup::Section { - name: "Artboard".into(), - layout: vec![ - LayoutGroup::Row { - widgets: vec![ - TextLabel::new("Location").widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px, - Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area. - Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists. - Separator::new(SeparatorType::Unrelated).widget_holder(), - NumberInput::new(Some(layer.transform.x() + pivot.x)) - .label("X") - .unit(" px") - .min(1.) - .max(std::i32::MAX as f64) - .on_update(move |number_input: &NumberInput| { - PropertiesPanelMessage::ModifyTransform { - value: number_input.value.unwrap() - pivot.x, - transform_op: TransformOp::X, - } - .into() - }) - .widget_holder(), - Separator::new(SeparatorType::Related).widget_holder(), - NumberInput::new(Some(layer.transform.y() + pivot.y)) - .label("Y") - .unit(" px") - .min(1.) - .max(std::i32::MAX as f64) - .on_update(move |number_input: &NumberInput| { - PropertiesPanelMessage::ModifyTransform { - value: number_input.value.unwrap() - pivot.y, - transform_op: TransformOp::Y, - } - .into() - }) - .widget_holder(), - ], - }, - LayoutGroup::Row { - widgets: vec![ - TextLabel::new("Dimensions").widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), - Separator::new(SeparatorType::Related).widget_holder(), - CheckboxInput::new(layer.preserve_aspect) - .icon("Link") - .tooltip("Preserve Aspect Ratio") - .on_update(|input: &CheckboxInput| PropertiesPanelMessage::ModifyPreserveAspect { preserve_aspect: input.checked }.into()) - .widget_holder(), - Separator::new(SeparatorType::Related).widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), - NumberInput::new(Some(layer.bounding_transform(&render_data).scale_x())) - .label("W") - .unit(" px") - .is_integer(true) - .min(1.) - .max(std::i32::MAX as f64) - .on_update(|number_input: &NumberInput| { - PropertiesPanelMessage::ModifyTransform { - value: number_input.value.unwrap(), - transform_op: TransformOp::Width, - } - .into() - }) - .widget_holder(), - Separator::new(SeparatorType::Related).widget_holder(), - NumberInput::new(Some(layer.bounding_transform(&render_data).scale_y())) - .label("H") - .unit(" px") - .is_integer(true) - .min(1.) - .max(std::i32::MAX as f64) - .on_update(|number_input: &NumberInput| { - PropertiesPanelMessage::ModifyTransform { - value: number_input.value.unwrap(), - transform_op: TransformOp::Height, - } - .into() - }) - .widget_holder(), - ], - }, - LayoutGroup::Row { - widgets: vec![ - TextLabel::new("Background").widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px, - Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area. - Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists. - Separator::new(SeparatorType::Unrelated).widget_holder(), - ColorInput::new(Some(*color)) - .on_update(|text_input: &ColorInput| { - let fill = if let Some(value) = text_input.value { value } else { Color::TRANSPARENT }; - PropertiesPanelMessage::ModifyFill { fill: Fill::Solid(fill) }.into() - }) - .widget_holder(), - ], - }, - ], - }] - }; - - responses.add(LayoutMessage::SendLayout { - layout: Layout::WidgetLayout(WidgetLayout::new(options_bar)), - layout_target: LayoutTarget::PropertiesOptions, - }); - responses.add(LayoutMessage::SendLayout { - layout: Layout::WidgetLayout(WidgetLayout::new(properties_body)), - layout_target: LayoutTarget::PropertiesSections, - }); -} - pub fn register_artwork_layer_properties( document: &Document, layer_path: Vec, diff --git a/editor/src/messages/portfolio/document/properties_panel/utility_types.rs b/editor/src/messages/portfolio/document/properties_panel/utility_types.rs index f6040f1c1..72be94970 100644 --- a/editor/src/messages/portfolio/document/properties_panel/utility_types.rs +++ b/editor/src/messages/portfolio/document/properties_panel/utility_types.rs @@ -8,7 +8,6 @@ use crate::{messages::prelude::NodeGraphMessageHandler, node_graph_executor::Nod pub struct PropertiesPanelMessageHandlerData<'a> { pub document_name: &'a str, pub artwork_document: &'a DocumentLegacy, - pub artboard_document: &'a DocumentLegacy, pub selected_layers: &'a mut dyn Iterator, pub node_graph_message_handler: &'a NodeGraphMessageHandler, pub executor: &'a mut NodeGraphExecutor, diff --git a/editor/src/messages/portfolio/document/utility_types/misc.rs b/editor/src/messages/portfolio/document/utility_types/misc.rs index 67f4044f0..7575ecd29 100644 --- a/editor/src/messages/portfolio/document/utility_types/misc.rs +++ b/editor/src/messages/portfolio/document/utility_types/misc.rs @@ -1,6 +1,4 @@ pub use super::layer_panel::{LayerMetadata, LayerPanelEntry}; -use crate::messages::prelude::ArtboardMessageHandler; - use document_legacy::document::Document as DocumentLegacy; use document_legacy::LayerId; use graphene_core::raster::color::Color; @@ -12,7 +10,6 @@ use std::fmt; #[derive(Debug, Clone)] pub struct DocumentSave { pub document: DocumentLegacy, - pub artboard: ArtboardMessageHandler, pub layer_metadata: HashMap, LayerMetadata>, } @@ -36,12 +33,6 @@ pub enum AlignAggregate { Average, } -#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] -pub enum TargetDocument { - Artboard, - Artwork, -} - #[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum DocumentMode { DesignMode, diff --git a/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs b/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs index 9fc715740..aed125a53 100644 --- a/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs +++ b/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs @@ -249,7 +249,7 @@ impl LayoutHolder for MenuBarMessageHandler { no_active_document, MenuBarEntryChildren(vec![vec![MenuBarEntry { label: "Clear Artboards".into(), - action: MenuBarEntry::create_action(|_| ArtboardMessage::ClearArtboards.into()), + action: MenuBarEntry::create_action(|_| DialogMessage::RequestComingSoonDialog { issue: None }.into()), disabled: no_active_document, ..MenuBarEntry::default() }]]), diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 9ae4a61a9..ccd135780 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -299,7 +299,7 @@ impl MessageHandler { - let new_document = DocumentMessageHandler::with_name(name, ipp); + let new_document = DocumentMessageHandler::with_name(name, ipp, responses); let document_id = generate_uuid(); if self.active_document().is_some() { responses.add(BroadcastEvent::ToolAbort); diff --git a/editor/src/messages/prelude.rs b/editor/src/messages/prelude.rs index e36370bda..01f6f7993 100644 --- a/editor/src/messages/prelude.rs +++ b/editor/src/messages/prelude.rs @@ -14,7 +14,6 @@ pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappi pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageDiscriminant, InputMapperMessageHandler}; pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler}; pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler}; -pub use crate::messages::portfolio::document::artboard::{ArtboardMessage, ArtboardMessageDiscriminant, ArtboardMessageHandler}; pub use crate::messages::portfolio::document::navigation::{NavigationMessage, NavigationMessageDiscriminant, NavigationMessageHandler}; pub use crate::messages::portfolio::document::node_graph::{GraphOperationMessage, GraphOperationMessageDiscriminant, GraphOperationMessageHandler}; pub use crate::messages::portfolio::document::node_graph::{NodeGraphMessage, NodeGraphMessageDiscriminant, NodeGraphMessageHandler}; diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index 8a05a9f00..36c111256 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -91,6 +91,11 @@ pub fn get_gradient(layer: LayerNodeIdentifier, document: &Document) -> Option bool { + NodeGraphLayer::new(layer, document).is_some_and(|layer| layer.uses_node("Artboard")) +} + /// Convert subpaths to an iterator of manipulator groups pub fn get_manipulator_groups(subpaths: &[Subpath]) -> impl Iterator> + DoubleEndedIterator { subpaths.iter().flat_map(|subpath| subpath.manipulator_groups()) @@ -145,14 +150,14 @@ impl<'a> NodeGraphLayer<'a> { self.node_graph.primary_flow_from_opt(Some(self.layer_node)) } + /// Does a node exist in the layer's primary flow + pub fn uses_node(&self, node_name: &str) -> bool { + self.primary_layer_flow().any(|(node, _id)| node.name == node_name) + } + /// Find all of the inputs of a specific node within the layer's primary flow pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec> { - for (node, _id) in self.primary_layer_flow() { - if node.name == node_name { - return Some(&node.inputs); - } - } - None + self.primary_layer_flow().find(|(node, _id)| node.name == node_name).map(|(node, _id)| &node.inputs) } /// Find a specific input of a node within the layer's primary flow diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index e8c3a391d..6d605f47b 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -1,12 +1,10 @@ use super::tool_prelude::*; use crate::application::generate_uuid; -use crate::consts::SELECTION_TOLERANCE; -use crate::messages::portfolio::document::utility_types::misc::TargetDocument; +use crate::messages::tool::common_functionality::graph_modification_utils::is_artboard; use crate::messages::tool::common_functionality::snapping::SnapManager; use crate::messages::tool::common_functionality::transformation_cage::*; -use document_legacy::intersection::Quad; -use document_legacy::LayerId; +use document_legacy::document_metadata::LayerNodeIdentifier; use document_legacy::layers::RenderData; use glam::{IVec2, Vec2Swizzles}; @@ -96,7 +94,7 @@ enum ArtboardToolFsmState { #[derive(Clone, Debug, Default)] struct ArtboardToolData { bounding_box_overlays: Option, - selected_artboard: Option, + selected_artboard: Option, snap_manager: SnapManager, cursor: MouseCursorIcon, drag_start: DVec2, @@ -104,15 +102,15 @@ struct ArtboardToolData { } impl ArtboardToolData { - fn refresh_overlays(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque) { - let current_artboard = self.selected_artboard.and_then(|path| document.artboard_bounding_box_and_transform(&[path], render_data)); + fn refresh_overlays(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque) { + let current_artboard = self.selected_artboard.and_then(|layer| document.document_legacy.metadata.bounding_box_document(layer)); match (current_artboard, self.bounding_box_overlays.take()) { (None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses), - (Some((bounds, transform)), paths) => { + (Some(bounds), paths) => { let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses)); bounding_box_overlays.bounds = bounds; - bounding_box_overlays.transform = transform; + bounding_box_overlays.transform = document.document_legacy.metadata.document_to_viewport; bounding_box_overlays.transform(responses); @@ -130,6 +128,7 @@ impl ArtboardToolData { let (top, bottom, left, right) = edges; let selected_edges = SelectedEdges::new(top, bottom, left, right, bounding_box.bounds); bounding_box.opposite_pivot = selected_edges.calculate_pivot(); + bounding_box.selected_edges = Some(selected_edges); Some(edges) } @@ -140,29 +139,29 @@ impl ArtboardToolData { let artboard = self.selected_artboard.unwrap(); self.snap_manager - .start_snap(document, input, document.bounding_boxes(None, Some(artboard), render_data), snap_x, snap_y); + .start_snap(document, input, document.bounding_boxes(None, Some(artboard.to_node()), render_data), snap_x, snap_y); self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); if let Some(bounds) = &mut self.bounding_box_overlays { - let pivot = document.artboard_message_handler.artboards_document.pivot(&[artboard], render_data).unwrap_or_default(); - let root = document.document_legacy.metadata.document_to_viewport; - let pivot = root.inverse().transform_point2(pivot); - bounds.center_of_transformation = pivot; + bounds.center_of_transformation = (bounds.bounds[0] + bounds.bounds[1]) / 2.; } } fn select_artboard(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) -> bool { responses.add(DocumentMessage::StartTransaction); - let tolerance = DVec2::splat(SELECTION_TOLERANCE); - let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]); - let intersection = document.artboard_message_handler.artboards_document.intersects_quad_root(quad, render_data); + + let mut intersections = document + .document_legacy + .metadata + .click_xray(input.mouse.position) + .filter(|&layer| is_artboard(layer, &document.document_legacy)); responses.add(BroadcastEvent::DocumentIsDirty); - if let Some(intersection) = intersection.last() { - self.selected_artboard = Some(intersection[0]); + if let Some(intersection) = intersections.next() { + self.selected_artboard = Some(intersection); self.snap_manager - .start_snap(document, input, document.bounding_boxes(None, Some(intersection[0]), render_data), true, true); + .start_snap(document, input, document.bounding_boxes(None, Some(intersection.to_node()), render_data), true, true); self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); true @@ -186,13 +185,8 @@ impl ArtboardToolData { let snapped_mouse_position: DVec2 = self.snap_manager.snap_position(responses, document, mouse_position); let (position, size) = movement.new_size(snapped_mouse_position, bounds.transform, from_center, bounds.center_of_transformation, constrain_square); - responses.add(ArtboardMessage::ResizeArtboard { - artboard: self.selected_artboard.unwrap(), - position: position.round().into(), - size: size.round().into(), - }); responses.add(GraphOperationMessage::ResizeArtboard { - id: self.selected_artboard.unwrap(), + id: self.selected_artboard.unwrap().to_node(), location: position.round().as_ivec2(), dimensions: size.round().as_ivec2(), }); @@ -214,7 +208,7 @@ impl Fsm for ArtboardToolFsmState { match (self, event) { (state, ArtboardToolMessage::DocumentIsDirty) if state != ArtboardToolFsmState::Drawing => { - tool_data.refresh_overlays(document, render_data, responses); + tool_data.refresh_overlays(document, responses); self } @@ -255,13 +249,8 @@ impl Fsm for ArtboardToolFsmState { let position = bounds.bounds[0] + bounds.transform.inverse().transform_vector2(mouse_position - tool_data.drag_current + closest_move); - responses.add(ArtboardMessage::ResizeArtboard { - artboard: tool_data.selected_artboard.unwrap(), - position: position.round().into(), - size: size.round().into(), - }); responses.add(GraphOperationMessage::ResizeArtboard { - id: tool_data.selected_artboard.unwrap(), + id: tool_data.selected_artboard.unwrap().to_node(), location: position.round().as_ivec2(), dimensions: size.round().as_ivec2(), }); @@ -294,28 +283,18 @@ impl Fsm for ArtboardToolFsmState { let size = root_transform.transform_vector2(size); if let Some(artboard) = tool_data.selected_artboard { - responses.add(ArtboardMessage::ResizeArtboard { - artboard, - position: start.round().into(), - size: size.round().into(), - }); responses.add(GraphOperationMessage::ResizeArtboard { - id: tool_data.selected_artboard.unwrap(), + id: artboard.to_node(), location: start.round().as_ivec2(), dimensions: size.round().as_ivec2(), }); } else { let id = generate_uuid(); - tool_data.selected_artboard = Some(id); + tool_data.selected_artboard = Some(LayerNodeIdentifier::new_unchecked(id)); tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, Some(id), render_data), true, true); tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); - responses.add(ArtboardMessage::AddArtboard { - id: Some(id), - position: start.round().into(), - size: (1., 1.), - }); responses.add(GraphOperationMessage::NewArtboard { id, artboard: graphene_core::Artboard { @@ -376,8 +355,8 @@ impl Fsm for ArtboardToolFsmState { } (_, ArtboardToolMessage::DeleteSelected) => { if let Some(artboard) = tool_data.selected_artboard.take() { - responses.add(ArtboardMessage::DeleteArtboard { artboard }); - responses.add(GraphOperationMessage::DeleteLayer { id: artboard }); + let id = artboard.to_node(); + responses.add(GraphOperationMessage::DeleteLayer { id }); responses.add(BroadcastEvent::DocumentIsDirty); } @@ -385,13 +364,8 @@ impl Fsm for ArtboardToolFsmState { } (_, ArtboardToolMessage::NudgeSelected { delta_x, delta_y }) => { if let Some(bounds) = &mut tool_data.bounding_box_overlays { - responses.add(ArtboardMessage::ResizeArtboard { - artboard: tool_data.selected_artboard.unwrap(), - position: (bounds.bounds[0].x + delta_x, bounds.bounds[0].y + delta_y), - size: (bounds.bounds[1] - bounds.bounds[0]).round().into(), - }); responses.add(GraphOperationMessage::ResizeArtboard { - id: tool_data.selected_artboard.unwrap(), + id: tool_data.selected_artboard.unwrap().to_node(), location: DVec2::new(bounds.bounds[0].x + delta_x, bounds.bounds[0].y + delta_y).round().as_ivec2(), dimensions: (bounds.bounds[1] - bounds.bounds[0]).round().as_ivec2(), }); @@ -407,7 +381,6 @@ impl Fsm for ArtboardToolFsmState { // Register properties when switching back to other tools responses.add(PropertiesPanelMessage::SetActiveLayers { paths: document.selected_layers().map(|path| path.to_vec()).collect(), - document: TargetDocument::Artwork, }); tool_data.snap_manager.cleanup(responses); diff --git a/editor/src/messages/tool/tool_messages/fill_tool.rs b/editor/src/messages/tool/tool_messages/fill_tool.rs index c125477ba..de6e5041c 100644 --- a/editor/src/messages/tool/tool_messages/fill_tool.rs +++ b/editor/src/messages/tool/tool_messages/fill_tool.rs @@ -68,7 +68,7 @@ impl Fsm for FillToolFsmState { let ToolMessage::Fill(event) = event else { return self; }; - let Some((layer_identifier, _)) = document.document_legacy.metadata.click(input.mouse.position) else { + let Some(layer_identifier) = document.document_legacy.metadata.click(input.mouse.position) else { return self; }; let layer = layer_identifier.to_path(); diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index 9e9558858..bc1b15635 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -116,7 +116,7 @@ enum GradientToolFsmState { /// Computes the transform from gradient space to viewport space (where gradient space is 0..1) fn gradient_space_transform(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> DAffine2 { - let bounds = document.document_legacy.metadata.bounding_box(layer, DAffine2::IDENTITY).unwrap(); + let bounds = document.document_legacy.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY).unwrap(); let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]); let multiplied = document.document_legacy.metadata.transform_from_viewport(layer); @@ -529,7 +529,7 @@ impl Fsm for GradientToolFsmState { let selected_layer = document.document_legacy.metadata.click(input.mouse.position); // Apply the gradient to the selected layer - if let Some((layer, _)) = selected_layer { + if let Some(layer) = selected_layer { // let is_bitmap = document // .document_legacy // .layer(&layer) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 6643d9754..8b4dc61e1 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -242,7 +242,7 @@ impl PathToolData { PathToolFsmState::Dragging } // We didn't find a point nearby, so consider selecting the nearest shape instead - else if let Some((layer, _)) = document.document_legacy.metadata.click(input.mouse.position) { + else if let Some(layer) = document.document_legacy.metadata.click(input.mouse.position) { // TODO: Actual selection let layer_list = vec![layer.to_path()]; if shift { diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 0ca1ccfc5..0b11c3205 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -484,7 +484,6 @@ impl NodeGraphExecutor { affected_layer_path: execution_context.layer_path, }); responses.add(DocumentMessage::RenderDocument); - responses.add(ArtboardMessage::RenderArtboards); responses.add(DocumentMessage::DocumentStructureChanged); responses.add(BroadcastEvent::DocumentIsDirty); responses.add(DocumentMessage::DirtyRenderDocument);