diff --git a/editor/src/communication/dispatcher.rs b/editor/src/communication/dispatcher.rs index 9ab383bb36..5b346ce2ba 100644 --- a/editor/src/communication/dispatcher.rs +++ b/editor/src/communication/dispatcher.rs @@ -41,6 +41,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[ MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerDetails), MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerTreeStructure), MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateOpenDocumentsList), + MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad), MessageDiscriminant::Tool(ToolMessageDiscriminant::DocumentIsDirty), ]; @@ -108,6 +109,7 @@ impl Dispatcher { ( self.message_handlers.portfolio_message_handler.active_document(), &self.message_handlers.input_preprocessor_message_handler, + self.message_handlers.portfolio_message_handler.font_cache(), ), &mut self.message_queue, ); diff --git a/editor/src/consts.rs b/editor/src/consts.rs index b04c69ad8b..4f3412133d 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -1,4 +1,5 @@ use graphene::color::Color; +use graphene::layers::text_layer::Font; // Viewport pub const VIEWPORT_ZOOM_WHEEL_RATE: f64 = 1. / 600.; @@ -65,6 +66,10 @@ pub const FILE_SAVE_SUFFIX: &str = ".graphite"; // Colors pub const COLOR_ACCENT: Color = Color::from_unsafe(0x00 as f32 / 255., 0xA8 as f32 / 255., 0xFF as f32 / 255.); +// Fonts +pub const DEFAULT_FONT_FAMILY: &str = "Merriweather"; +pub const DEFAULT_FONT_STYLE: &str = "Normal (400)"; + // Document pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.8"; // Remember to save a simple document and replace the test file at: editor\src\communication\graphite-test-document.graphite pub const VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR: f32 = 1.05; diff --git a/editor/src/dialog/dialogs/coming_soon_dialog.rs b/editor/src/dialog/dialogs/coming_soon_dialog.rs index f5898540b2..f2ce4120f4 100644 --- a/editor/src/dialog/dialogs/coming_soon_dialog.rs +++ b/editor/src/dialog/dialogs/coming_soon_dialog.rs @@ -1,5 +1,7 @@ use crate::{layout::widgets::*, message_prelude::FrontendMessage}; +use std::fmt::Write; + /// A dialog to notify users of an unfinished issue, optionally with an issue number. pub struct ComingSoon { pub issue: Option, @@ -16,7 +18,7 @@ impl PropertyHolder for ComingSoon { ..Default::default() }))]; if let Some(issue) = self.issue { - details += &format!("— but you can help add it!\nSee issue #{issue} on GitHub."); + let _ = write!(details, "— but you can help add it!\nSee issue #{issue} on GitHub."); buttons.push(WidgetHolder::new(Widget::TextButton(TextButton { label: format!("Issue #{issue}"), min_width: 96, diff --git a/editor/src/document/artboard_message_handler.rs b/editor/src/document/artboard_message_handler.rs index 6c44404304..32a1d30424 100644 --- a/editor/src/document/artboard_message_handler.rs +++ b/editor/src/document/artboard_message_handler.rs @@ -3,6 +3,7 @@ use crate::message_prelude::*; use graphene::color::Color; use graphene::document::Document as GrapheneDocument; use graphene::layers::style::{self, Fill, ViewMode}; +use graphene::layers::text_layer::FontCache; use graphene::DocumentResponse; use graphene::Operation as DocumentOperation; @@ -22,16 +23,16 @@ impl ArtboardMessageHandler { } } -impl MessageHandler for ArtboardMessageHandler { +impl MessageHandler for ArtboardMessageHandler { #[remain::check] - fn process_action(&mut self, message: ArtboardMessage, _: (), responses: &mut VecDeque) { + fn process_action(&mut self, message: ArtboardMessage, font_cache: &FontCache, responses: &mut VecDeque) { use ArtboardMessage::*; #[remain::sorted] match message { // Sub-messages #[remain::unsorted] - DispatchOperation(operation) => match self.artboards_graphene_document.handle_operation(*operation) { + DispatchOperation(operation) => match self.artboards_graphene_document.handle_operation(*operation, font_cache) { Ok(Some(document_responses)) => { for response in document_responses { match &response { @@ -86,7 +87,7 @@ impl MessageHandler for ArtboardMessageHandler { } else { responses.push_back( FrontendMessage::UpdateDocumentArtboards { - svg: self.artboards_graphene_document.render_root(ViewMode::Normal), + svg: self.artboards_graphene_document.render_root(ViewMode::Normal, font_cache), } .into(), ); diff --git a/editor/src/document/document_message.rs b/editor/src/document/document_message.rs index ec072599d2..2a189951f5 100644 --- a/editor/src/document/document_message.rs +++ b/editor/src/document/document_message.rs @@ -72,18 +72,10 @@ pub enum DocumentMessage { FolderChanged { affected_folder_path: Vec, }, - FontLoaded { - font_file_url: String, - data: Vec, - is_default: bool, - }, GroupSelectedLayers, LayerChanged { affected_layer_path: Vec, }, - LoadFont { - font_file_url: String, - }, MoveSelectedLayersTo { folder_path: Vec, insert_index: isize, diff --git a/editor/src/document/document_message_handler.rs b/editor/src/document/document_message_handler.rs index 9922122aa5..aeb6fae42f 100644 --- a/editor/src/document/document_message_handler.rs +++ b/editor/src/document/document_message_handler.rs @@ -23,6 +23,7 @@ use graphene::layers::blend_mode::BlendMode; use graphene::layers::folder_layer::FolderLayer; use graphene::layers::layer_info::LayerDataType; use graphene::layers::style::{Fill, ViewMode}; +use graphene::layers::text_layer::FontCache; use graphene::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation}; use glam::{DAffine2, DVec2}; @@ -136,12 +137,12 @@ impl DocumentMessageHandler { && self.name.starts_with(DEFAULT_DOCUMENT_NAME) } - fn select_layer(&mut self, path: &[LayerId]) -> Option { + fn select_layer(&mut self, path: &[LayerId], font_cache: &FontCache) -> Option { println!("Select_layer fail: {:?}", self.all_layers_sorted()); if let Some(layer) = self.layer_metadata.get_mut(path) { layer.selected = true; - let data = self.layer_panel_entry(path.to_vec()).ok()?; + let data = self.layer_panel_entry(path.to_vec(), font_cache).ok()?; (!path.is_empty()).then(|| FrontendMessage::UpdateDocumentLayerDetails { data }.into()) } else { log::warn!("Tried to select non existing layer {:?}", path); @@ -149,17 +150,17 @@ impl DocumentMessageHandler { } } - pub fn selected_visible_layers_bounding_box(&self) -> Option<[DVec2; 2]> { + pub fn selected_visible_layers_bounding_box(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> { let paths = self.selected_visible_layers(); - self.graphene_document.combined_viewport_bounding_box(paths) + self.graphene_document.combined_viewport_bounding_box(paths, font_cache) } - pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId]) -> Option<([DVec2; 2], DAffine2)> { - self.artboard_message_handler.artboards_graphene_document.bounding_box_and_transform(path).unwrap_or(None) + pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId], font_cache: &FontCache) -> Option<([DVec2; 2], DAffine2)> { + self.artboard_message_handler.artboards_graphene_document.bounding_box_and_transform(path, font_cache).unwrap_or(None) } /// Create a new vector shape representation with the underlying kurbo data, VectorManipulatorShape - pub fn selected_visible_layers_vector_shapes(&self, responses: &mut VecDeque) -> Vec { + pub fn selected_visible_layers_vector_shapes(&self, responses: &mut VecDeque, font_cache: &FontCache) -> Vec { let shapes = self.selected_layers().filter_map(|path_to_shape| { let viewport_transform = self.graphene_document.generate_transform_relative_to_viewport(path_to_shape).ok()?; let layer = self.graphene_document.layer(path_to_shape); @@ -172,13 +173,7 @@ impl DocumentMessageHandler { // TODO: Create VectorManipulatorShape when creating a kurbo shape as a stopgap, rather than on each new selection match &layer.ok()?.data { LayerDataType::Shape(shape) => Some(VectorShape::new(path_to_shape.to_vec(), viewport_transform, &shape.path, shape.closed, responses)), - LayerDataType::Text(text) => Some(VectorShape::new( - path_to_shape.to_vec(), - viewport_transform, - &text.to_bez_path_nonmut(&self.graphene_document.font_cache), - true, - responses, - )), + LayerDataType::Text(text) => Some(VectorShape::new(path_to_shape.to_vec(), viewport_transform, &text.to_bez_path_nonmut(font_cache), true, responses)), _ => None, } }); @@ -230,16 +225,16 @@ 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) -> impl Iterator + 'a { + pub fn bounding_boxes<'a>(&'a self, ignore_document: Option<&'a Vec>>, ignore_artboard: Option, font_cache: &'a FontCache) -> 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.graphene_document.viewport_bounding_box(path).ok()?) + .filter_map(|path| self.graphene_document.viewport_bounding_box(path, font_cache).ok()?) .chain( self.artboard_message_handler .artboard_ids .iter() .filter(move |&&id| Some(id) != ignore_artboard) - .filter_map(|&path| self.artboard_message_handler.artboards_graphene_document.viewport_bounding_box(&[path]).ok()?), + .filter_map(|&path| self.artboard_message_handler.artboards_graphene_document.viewport_bounding_box(&[path], font_cache).ok()?), ) } @@ -431,26 +426,26 @@ impl DocumentMessageHandler { } // TODO: This should probably take a slice not a vec, also why does this even exist when `layer_panel_entry_from_path` also exists? - pub fn layer_panel_entry(&mut self, path: Vec) -> Result { + pub fn layer_panel_entry(&mut self, path: Vec, font_cache: &FontCache) -> Result { let data: LayerMetadata = *self .layer_metadata .get_mut(&path) .ok_or_else(|| EditorError::Document(format!("Could not get layer metadata for {:?}", path)))?; let layer = self.graphene_document.layer(&path)?; - let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path, &self.graphene_document.font_cache); + let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path, font_cache); Ok(entry) } /// Returns a list of `LayerPanelEntry`s intended for display purposes. These don't contain /// any actual data, but rather attributes such as visibility and names of the layers. - pub fn layer_panel(&mut self, path: &[LayerId]) -> Result, EditorError> { + pub fn layer_panel(&mut self, path: &[LayerId], font_cache: &FontCache) -> Result, EditorError> { let folder = self.graphene_document.folder(path)?; let paths: Vec> = folder.layer_ids.iter().map(|id| [path, &[*id]].concat()).collect(); - let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path)).collect(); + let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path, font_cache)).collect(); Ok(entries) } - pub fn layer_panel_entry_from_path(&self, path: &[LayerId]) -> Option { + pub fn layer_panel_entry_from_path(&self, path: &[LayerId], font_cache: &FontCache) -> Option { let layer_metadata = self.layer_metadata(path); let transform = self .graphene_document @@ -458,7 +453,7 @@ impl DocumentMessageHandler { .ok()?; let layer = self.graphene_document.layer(path).ok()?; - Some(layer_panel_entry(layer_metadata, transform, layer, path.to_vec(), &self.graphene_document.font_cache)) + Some(layer_panel_entry(layer_metadata, transform, layer, path.to_vec(), font_cache)) } /// When working with an insert index, deleting the layers may cause the insert index to point to a different location (if the layer being deleted was located before the insert index). @@ -473,16 +468,16 @@ impl DocumentMessageHandler { } /// Calculates the bounding box of all layers in the document - pub fn all_layer_bounds(&self) -> Option<[DVec2; 2]> { - self.graphene_document.viewport_bounding_box(&[]).ok().flatten() + pub fn all_layer_bounds(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> { + self.graphene_document.viewport_bounding_box(&[], font_cache).ok().flatten() } /// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable)) - pub fn document_bounds(&self) -> Option<[DVec2; 2]> { + pub fn document_bounds(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> { if self.artboard_message_handler.is_infinite_canvas() { - self.all_layer_bounds() + self.all_layer_bounds(font_cache) } else { - self.artboard_message_handler.artboards_graphene_document.viewport_bounding_box(&[]).ok().flatten() + self.artboard_message_handler.artboards_graphene_document.viewport_bounding_box(&[], font_cache).ok().flatten() } } @@ -523,13 +518,6 @@ impl DocumentMessageHandler { } } - // TODO: Loading the default font should happen on a per-application basis, not a per-document basis - pub fn load_default_font(&self, responses: &mut VecDeque) { - if !self.graphene_document.font_cache.has_default() { - responses.push_back(FrontendMessage::TriggerFontLoadDefault.into()) - } - } - pub fn update_document_widgets(&self, responses: &mut VecDeque) { let document_bar_layout = WidgetLayout::new(vec![LayoutRow::Row { widgets: vec![ @@ -719,7 +707,7 @@ impl DocumentMessageHandler { ); } - pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque) { + pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque, font_cache: &FontCache) { let mut opacity = None; let mut opacity_is_mixed = false; @@ -728,7 +716,7 @@ impl DocumentMessageHandler { self.layer_metadata .keys() - .filter_map(|path| self.layer_panel_entry_from_path(path)) + .filter_map(|path| self.layer_panel_entry_from_path(path, font_cache)) .filter(|layer_panel_entry| layer_panel_entry.layer_metadata.selected) .flat_map(|layer_panel_entry| self.graphene_document.layer(layer_panel_entry.path.as_slice())) .for_each(|layer| { @@ -834,16 +822,16 @@ impl DocumentMessageHandler { } } -impl MessageHandler for DocumentMessageHandler { +impl MessageHandler for DocumentMessageHandler { #[remain::check] - fn process_action(&mut self, message: DocumentMessage, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { + fn process_action(&mut self, message: DocumentMessage, (ipp, font_cache): (&InputPreprocessorMessageHandler, &FontCache), responses: &mut VecDeque) { use DocumentMessage::*; #[remain::sorted] match message { // Sub-messages #[remain::unsorted] - DispatchOperation(op) => match self.graphene_document.handle_operation(*op) { + DispatchOperation(op) => match self.graphene_document.handle_operation(*op, font_cache) { Ok(Some(document_responses)) => { for response in document_responses { match &response { @@ -877,7 +865,7 @@ impl MessageHandler for Docum }, #[remain::unsorted] Artboard(message) => { - self.artboard_message_handler.process_action(message, (), responses); + self.artboard_message_handler.process_action(message, font_cache, responses); } #[remain::unsorted] Movement(message) => { @@ -885,13 +873,13 @@ impl MessageHandler for Docum } #[remain::unsorted] Overlays(message) => { - self.overlays_message_handler.process_action(message, self.overlays_visible, responses); + self.overlays_message_handler.process_action(message, (self.overlays_visible, font_cache), responses); // responses.push_back(OverlaysMessage::RenderOverlays.into()); } #[remain::unsorted] TransformLayers(message) => { self.transform_layer_handler - .process_action(message, (&mut self.layer_metadata, &mut self.graphene_document, ipp), responses); + .process_action(message, (&mut self.layer_metadata, &mut self.graphene_document, ipp, font_cache), responses); } #[remain::unsorted] PropertiesPanel(message) => { @@ -900,6 +888,7 @@ impl MessageHandler for Docum PropertiesPanelMessageHandlerData { artwork_document: &self.graphene_document, artboard_document: &self.artboard_message_handler.artboards_graphene_document, + font_cache, }, responses, ); @@ -912,7 +901,7 @@ impl MessageHandler for Docum } AddSelectedLayers { additional_layers } => { for layer_path in &additional_layers { - responses.extend(self.select_layer(layer_path)); + responses.extend(self.select_layer(layer_path, font_cache)); } let selected_paths: Vec> = self.selected_layers().map(|path| path.to_vec()).collect(); @@ -932,13 +921,13 @@ impl MessageHandler for Docum responses.push_back(FolderChanged { affected_folder_path: vec![] }.into()); responses.push_back(DocumentMessage::SelectionChanged.into()); - self.update_layer_tree_options_bar_widgets(responses); + self.update_layer_tree_options_bar_widgets(responses, font_cache); } AlignSelectedLayers { axis, aggregate } => { self.backup(responses); let (paths, boxes): (Vec<_>, Vec<_>) = self .selected_layers() - .filter_map(|path| self.graphene_document.viewport_bounding_box(path).ok()?.map(|b| (path, b))) + .filter_map(|path| self.graphene_document.viewport_bounding_box(path, font_cache).ok()?.map(|b| (path, b))) .unzip(); let axis = match axis { @@ -946,7 +935,7 @@ impl MessageHandler for Docum AlignAxis::Y => DVec2::Y, }; let lerp = |bbox: &[DVec2; 2]| bbox[0].lerp(bbox[1], 0.5); - if let Some(combined_box) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers()) { + if let Some(combined_box) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers(), font_cache) { let aggregated = match aggregate { AlignAggregate::Min => combined_box[0], AlignAggregate::Max => combined_box[1], @@ -1054,13 +1043,13 @@ impl MessageHandler for Docum // Calculates the bounding box of the region to be exported let bbox = match bounds { - crate::frontend::utility_types::ExportBounds::AllArtwork => self.all_layer_bounds(), + crate::frontend::utility_types::ExportBounds::AllArtwork => self.all_layer_bounds(font_cache), crate::frontend::utility_types::ExportBounds::Artboard(id) => self .artboard_message_handler .artboards_graphene_document .layer(&[id]) .ok() - .and_then(|layer| layer.aabounding_box(&self.graphene_document.font_cache)), + .and_then(|layer| layer.aabounding_box(font_cache)), } .unwrap_or_default(); let size = bbox[1] - bbox[0]; @@ -1071,7 +1060,7 @@ impl MessageHandler for Docum false => file_name + file_suffix, }; - let rendered = self.graphene_document.render_root(self.view_mode); + let rendered = self.graphene_document.render_root(self.view_mode, font_cache); let document = format!( r#"{}{}"#, bbox[0].x, bbox[0].y, size.x, size.y, size.x, size.y, "\n", rendered @@ -1094,7 +1083,7 @@ impl MessageHandler for Docum FlipAxis::X => DVec2::new(-1., 1.), FlipAxis::Y => DVec2::new(1., -1.), }; - if let Some([min, max]) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers()) { + if let Some([min, max]) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers(), font_cache) { let center = (max + min) / 2.; let bbox_trans = DAffine2::from_translation(-center); for path in self.selected_layers() { @@ -1111,14 +1100,10 @@ impl MessageHandler for Docum } } FolderChanged { affected_folder_path } => { - let _ = self.graphene_document.render_root(self.view_mode); + let _ = self.graphene_document.render_root(self.view_mode, font_cache); let affected_layer_path = affected_folder_path; responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]); } - FontLoaded { font_file_url, data, is_default } => { - self.graphene_document.font_cache.insert(font_file_url, data, is_default); - responses.push_back(DocumentMessage::DirtyRenderDocument.into()); - } GroupSelectedLayers => { let mut new_folder_path = self.graphene_document.shallowest_common_folder(self.selected_layers()).unwrap_or(&[]).to_vec(); @@ -1149,16 +1134,11 @@ impl MessageHandler for Docum ); } LayerChanged { affected_layer_path } => { - if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone()) { + if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone(), font_cache) { responses.push_back(FrontendMessage::UpdateDocumentLayerDetails { data: layer_entry }.into()); } responses.push_back(PropertiesPanelMessage::CheckSelectedWasUpdated { path: affected_layer_path }.into()); - self.update_layer_tree_options_bar_widgets(responses); - } - LoadFont { font_file_url } => { - if !self.graphene_document.font_cache.loaded_font(&font_file_url) { - responses.push_front(FrontendMessage::TriggerFontLoad { font_file_url }.into()); - } + self.update_layer_tree_options_bar_widgets(responses, font_cache); } MoveSelectedLayersTo { folder_path, @@ -1240,7 +1220,7 @@ impl MessageHandler for Docum RenderDocument => { responses.push_back( FrontendMessage::UpdateDocumentArtwork { - svg: self.graphene_document.render_root(self.view_mode), + svg: self.graphene_document.render_root(self.view_mode, font_cache), } .into(), ); @@ -1250,7 +1230,7 @@ impl MessageHandler for Docum let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT; let viewport_size = ipp.viewport_bounds.size(); let viewport_mid = ipp.viewport_bounds.center(); - let [bounds1, bounds2] = self.document_bounds().unwrap_or([viewport_mid; 2]); + let [bounds1, bounds2] = self.document_bounds(font_cache).unwrap_or([viewport_mid; 2]); let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale; let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale; let bounds_length = (bounds2 - bounds1) * (1. + SCROLLBAR_SPACING); @@ -1423,7 +1403,7 @@ impl MessageHandler for Docum responses.push_back(LayerChanged { affected_layer_path: layer_path }.into()) } SetLayerName { layer_path, name } => { - if let Some(layer) = self.layer_panel_entry_from_path(&layer_path) { + if let Some(layer) = self.layer_panel_entry_from_path(&layer_path, font_cache) { // Only save the history state if the name actually changed to something different if layer.name != name { self.backup(responses); @@ -1532,7 +1512,7 @@ impl MessageHandler for Docum self.layer_metadata.insert(layer_path, layer_metadata); } ZoomCanvasToFitAll => { - if let Some(bounds) = self.document_bounds() { + if let Some(bounds) = self.document_bounds(font_cache) { responses.push_back( MovementMessage::FitViewportToBounds { bounds, diff --git a/editor/src/document/layer_panel.rs b/editor/src/document/layer_panel.rs index 81075a700a..eb6ff31406 100644 --- a/editor/src/document/layer_panel.rs +++ b/editor/src/document/layer_panel.rs @@ -1,6 +1,6 @@ -use graphene::document::FontCache; use graphene::layers::layer_info::{Layer, LayerData, LayerDataType}; use graphene::layers::style::ViewMode; +use graphene::layers::text_layer::FontCache; use graphene::LayerId; use glam::{DAffine2, DVec2}; @@ -8,7 +8,7 @@ use serde::ser::SerializeStruct; use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Copy)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Copy)] pub struct LayerMetadata { pub selected: bool, pub expanded: bool, @@ -54,7 +54,7 @@ pub fn layer_panel_entry(layer_metadata: &LayerMetadata, transform: DAffine2, la } } -#[derive(Debug, Clone, Deserialize, PartialEq)] +#[derive(Debug, Clone, Deserialize, PartialEq, Eq)] pub struct RawBuffer(Vec); impl From> for RawBuffer { @@ -81,7 +81,7 @@ impl Serialize for RawBuffer { } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct LayerPanelEntry { pub name: String, pub visible: bool, diff --git a/editor/src/document/overlays_message_handler.rs b/editor/src/document/overlays_message_handler.rs index 47080ad156..91ea809fbb 100644 --- a/editor/src/document/overlays_message_handler.rs +++ b/editor/src/document/overlays_message_handler.rs @@ -2,22 +2,23 @@ use crate::message_prelude::*; use graphene::document::Document as GrapheneDocument; use graphene::layers::style::ViewMode; +use graphene::layers::text_layer::FontCache; #[derive(Debug, Clone, Default)] pub struct OverlaysMessageHandler { pub overlays_graphene_document: GrapheneDocument, } -impl MessageHandler for OverlaysMessageHandler { +impl MessageHandler for OverlaysMessageHandler { #[remain::check] - fn process_action(&mut self, message: OverlaysMessage, overlays_visible: bool, responses: &mut VecDeque) { + fn process_action(&mut self, message: OverlaysMessage, (overlays_visible, font_cache): (bool, &FontCache), responses: &mut VecDeque) { use OverlaysMessage::*; #[remain::sorted] match message { // Sub-messages #[remain::unsorted] - DispatchOperation(operation) => match self.overlays_graphene_document.handle_operation(*operation) { + DispatchOperation(operation) => match self.overlays_graphene_document.handle_operation(*operation, font_cache) { Ok(_) => responses.push_back(OverlaysMessage::Rerender.into()), Err(e) => log::error!("OverlaysError: {:?}", e), }, @@ -30,7 +31,7 @@ impl MessageHandler for OverlaysMessageHandler { responses.push_back( FrontendMessage::UpdateDocumentOverlays { svg: if overlays_visible { - self.overlays_graphene_document.render_root(ViewMode::Normal) + self.overlays_graphene_document.render_root(ViewMode::Normal, font_cache) } else { String::from("") }, diff --git a/editor/src/document/portfolio_message.rs b/editor/src/document/portfolio_message.rs index 9312b82660..b0f0a9ebd3 100644 --- a/editor/src/document/portfolio_message.rs +++ b/editor/src/document/portfolio_message.rs @@ -1,6 +1,7 @@ use super::clipboards::Clipboard; use crate::message_prelude::*; +use graphene::layers::text_layer::Font; use graphene::LayerId; use serde::{Deserialize, Serialize}; @@ -33,6 +34,17 @@ pub enum PortfolioMessage { Cut { clipboard: Clipboard, }, + FontLoaded { + font_family: String, + font_style: String, + preview_url: String, + data: Vec, + is_default: bool, + }, + LoadFont { + font: Font, + is_default: bool, + }, NewDocument, NewDocumentWithName { name: String, diff --git a/editor/src/document/portfolio_message_handler.rs b/editor/src/document/portfolio_message_handler.rs index f9dcefb3b8..8f2b723283 100644 --- a/editor/src/document/portfolio_message_handler.rs +++ b/editor/src/document/portfolio_message_handler.rs @@ -7,6 +7,7 @@ use crate::layout::layout_message::LayoutTarget; use crate::layout::widgets::PropertyHolder; use crate::{dialog, message_prelude::*}; +use graphene::layers::text_layer::{Font, FontCache}; use graphene::Operation as DocumentOperation; use log::warn; @@ -18,6 +19,7 @@ pub struct PortfolioMessageHandler { document_ids: Vec, active_document_id: u64, copy_buffer: [Vec; INTERNAL_CLIPBOARD_COUNT as usize], + font_cache: FontCache, } impl PortfolioMessageHandler { @@ -72,15 +74,13 @@ impl PortfolioMessageHandler { new_document .layer_metadata .keys() - .filter_map(|path| new_document.layer_panel_entry_from_path(path)) + .filter_map(|path| new_document.layer_panel_entry_from_path(path, &self.font_cache)) .map(|entry| FrontendMessage::UpdateDocumentLayerDetails { data: entry }.into()) .collect::>(), ); - new_document.update_layer_tree_options_bar_widgets(responses); + new_document.update_layer_tree_options_bar_widgets(responses, &self.font_cache); new_document.load_image_data(responses, &new_document.graphene_document.root.data, Vec::new()); - // TODO: Loading the default font should happen on a per-application basis, not a per-document basis - new_document.load_default_font(responses); self.documents.insert(document_id, new_document); @@ -110,6 +110,10 @@ impl PortfolioMessageHandler { fn document_index(&self, document_id: u64) -> usize { self.document_ids.iter().position(|id| id == &document_id).expect("Active document is missing from document ids") } + + pub fn font_cache(&self) -> &FontCache { + &self.font_cache + } } impl Default for PortfolioMessageHandler { @@ -125,6 +129,7 @@ impl Default for PortfolioMessageHandler { document_ids: vec![starting_key], copy_buffer: [EMPTY_VEC; INTERNAL_CLIPBOARD_COUNT as usize], active_document_id: starting_key, + font_cache: Default::default(), } } } @@ -139,7 +144,7 @@ impl MessageHandler for Port match message { // Sub-messages #[remain::unsorted] - Document(message) => self.active_document_mut().process_action(message, ipp, responses), + Document(message) => self.documents.get_mut(&self.active_document_id).unwrap().process_action(message, (ipp, &self.font_cache), responses), // Messages AutoSaveActiveDocument => responses.push_back(PortfolioMessage::AutoSaveDocument { document_id: self.active_document_id }.into()), @@ -263,6 +268,21 @@ impl MessageHandler for Port responses.push_back(Copy { clipboard }.into()); responses.push_back(DeleteSelectedLayers.into()); } + FontLoaded { + font_family, + font_style, + preview_url, + data, + is_default, + } => { + self.font_cache.insert(Font::new(font_family, font_style), preview_url, data, is_default); + responses.push_back(DocumentMessage::DirtyRenderDocument.into()); + } + LoadFont { font, is_default } => { + if !self.font_cache.loaded_font(&font) { + responses.push_front(FrontendMessage::TriggerFontLoad { font, is_default }.into()); + } + } NewDocument => { let name = self.generate_new_document_name(); let new_document = DocumentMessageHandler::with_name(name, ipp); diff --git a/editor/src/document/properties_panel_message.rs b/editor/src/document/properties_panel_message.rs index 44a465ae0e..844b54990a 100644 --- a/editor/src/document/properties_panel_message.rs +++ b/editor/src/document/properties_panel_message.rs @@ -9,43 +9,20 @@ use serde::{Deserialize, Serialize}; #[impl_message(Message, DocumentMessage, PropertiesPanel)] #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] pub enum PropertiesPanelMessage { - CheckSelectedWasDeleted { - path: Vec, - }, - CheckSelectedWasUpdated { - path: Vec, - }, + CheckSelectedWasDeleted { path: Vec }, + CheckSelectedWasUpdated { path: Vec }, ClearSelection, - ModifyFill { - fill: Fill, - }, - ModifyFont { - font_family: String, - font_style: String, - font_file: Option, - size: f64, - }, - ModifyName { - name: String, - }, - ModifyStroke { - stroke: Stroke, - }, - ModifyText { - new_text: String, - }, - ModifyTransform { - value: f64, - transform_op: TransformOp, - }, + ModifyFill { fill: Fill }, + ModifyFont { font_family: String, font_style: String, size: f64 }, + ModifyName { name: String }, + ModifyStroke { stroke: Stroke }, + ModifyText { new_text: String }, + ModifyTransform { value: f64, transform_op: TransformOp }, ResendActiveProperties, - SetActiveLayers { - paths: Vec>, - document: TargetDocument, - }, + SetActiveLayers { paths: Vec>, document: TargetDocument }, } -#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum TransformOp { X, Y, diff --git a/editor/src/document/properties_panel_message_handler.rs b/editor/src/document/properties_panel_message_handler.rs index 06d5efffb4..a76d4cc7fd 100644 --- a/editor/src/document/properties_panel_message_handler.rs +++ b/editor/src/document/properties_panel_message_handler.rs @@ -9,10 +9,10 @@ use crate::layout::widgets::{ use crate::message_prelude::*; use graphene::color::Color; -use graphene::document::{Document as GrapheneDocument, FontCache}; +use graphene::document::Document as GrapheneDocument; use graphene::layers::layer_info::{Layer, LayerDataType}; use graphene::layers::style::{Fill, Gradient, GradientType, LineCap, LineJoin, Stroke}; -use graphene::layers::text_layer::TextLayer; +use graphene::layers::text_layer::{FontCache, TextLayer}; use graphene::{LayerId, Operation}; use glam::{DAffine2, DVec2}; @@ -111,12 +111,17 @@ impl PropertiesPanelMessageHandler { pub struct PropertiesPanelMessageHandlerData<'a> { pub artwork_document: &'a GrapheneDocument, pub artboard_document: &'a GrapheneDocument, + pub font_cache: &'a FontCache, } impl<'a> MessageHandler> for PropertiesPanelMessageHandler { #[remain::check] fn process_action(&mut self, message: PropertiesPanelMessage, data: PropertiesPanelMessageHandlerData, responses: &mut VecDeque) { - let PropertiesPanelMessageHandlerData { artwork_document, artboard_document } = data; + let PropertiesPanelMessageHandlerData { + artwork_document, + artboard_document, + font_cache, + } = data; let get_document = |document_selector: TargetDocument| match document_selector { TargetDocument::Artboard => artboard_document, TargetDocument::Artwork => artwork_document, @@ -150,21 +155,10 @@ impl<'a> MessageHandler { + ModifyFont { font_family, font_style, size } => { let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer"); - responses.push_back(self.create_document_operation(Operation::ModifyFont { - path, - font_family, - font_style, - font_file, - size, - })); + responses.push_back(self.create_document_operation(Operation::ModifyFont { path, font_family, font_style, size })); responses.push_back(ResendActiveProperties.into()); } ModifyTransform { value, transform_op } => { @@ -181,8 +175,8 @@ impl<'a> MessageHandler layer.bounding_transform(&get_document(*target_document).font_cache).scale_x() / layer.transform.scale_x(), - Height => layer.bounding_transform(&get_document(*target_document).font_cache).scale_y() / layer.transform.scale_y(), + Width => layer.bounding_transform(font_cache).scale_x() / layer.transform.scale_x(), + Height => layer.bounding_transform(font_cache).scale_y() / layer.transform.scale_y(), _ => 1., }; @@ -235,8 +229,8 @@ impl<'a> MessageHandler register_artboard_layer_properties(layer, responses, &get_document(target_document).font_cache), - TargetDocument::Artwork => register_artwork_layer_properties(layer, responses, &get_document(target_document).font_cache), + TargetDocument::Artboard => register_artboard_layer_properties(layer, responses, font_cache), + TargetDocument::Artwork => register_artwork_layer_properties(layer, responses, font_cache), } } } @@ -677,9 +671,7 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow { } fn node_section_font(layer: &TextLayer) -> LayoutRow { - let font_family = layer.font_family.clone(); - let font_style = layer.font_style.clone(); - let font_file = layer.font_file.clone(); + let font = layer.font.clone(); let size = layer.size; LayoutRow::Section { name: "Font".into(), @@ -712,14 +704,12 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow { })), WidgetHolder::new(Widget::FontInput(FontInput { is_style_picker: false, - font_family: layer.font_family.clone(), - font_style: layer.font_style.clone(), - font_file_url: String::new(), + font_family: layer.font.font_family.clone(), + font_style: layer.font.font_style.clone(), on_update: WidgetCallback::new(move |font_input: &FontInput| { PropertiesPanelMessage::ModifyFont { font_family: font_input.font_family.clone(), font_style: font_input.font_style.clone(), - font_file: Some(font_input.font_file_url.clone()), size, } .into() @@ -739,14 +729,12 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow { })), WidgetHolder::new(Widget::FontInput(FontInput { is_style_picker: true, - font_family: layer.font_family.clone(), - font_style: layer.font_style.clone(), - font_file_url: String::new(), + font_family: layer.font.font_family.clone(), + font_style: layer.font.font_style.clone(), on_update: WidgetCallback::new(move |font_input: &FontInput| { PropertiesPanelMessage::ModifyFont { font_family: font_input.font_family.clone(), font_style: font_input.font_style.clone(), - font_file: Some(font_input.font_file_url.clone()), size, } .into() @@ -770,9 +758,8 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow { unit: " px".into(), on_update: WidgetCallback::new(move |number_input: &NumberInput| { PropertiesPanelMessage::ModifyFont { - font_family: font_family.clone(), - font_style: font_style.clone(), - font_file: font_file.clone(), + font_family: font.font_family.clone(), + font_style: font.font_style.clone(), size: number_input.value.unwrap(), } .into() diff --git a/editor/src/document/transform_layer_message.rs b/editor/src/document/transform_layer_message.rs index e995ebdeb5..cd22e8427c 100644 --- a/editor/src/document/transform_layer_message.rs +++ b/editor/src/document/transform_layer_message.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; #[remain::sorted] #[impl_message(Message, DocumentMessage, TransformLayers)] -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] pub enum TransformLayerMessage { ApplyTransformOperation, BeginGrab, diff --git a/editor/src/document/transform_layer_message_handler.rs b/editor/src/document/transform_layer_message_handler.rs index 3827ed6d3f..b7a309ef5a 100644 --- a/editor/src/document/transform_layer_message_handler.rs +++ b/editor/src/document/transform_layer_message_handler.rs @@ -8,6 +8,7 @@ use crate::message_prelude::*; use graphene::document::Document; use glam::DVec2; +use graphene::layers::text_layer::FontCache; use std::collections::{HashMap, VecDeque}; #[derive(Debug, Clone, Default, PartialEq)] @@ -25,18 +26,12 @@ pub struct TransformLayerMessageHandler { pivot: DVec2, } -impl MessageHandler, LayerMetadata>, &mut Document, &InputPreprocessorMessageHandler)> for TransformLayerMessageHandler { +type TransformData<'a> = (&'a mut HashMap, LayerMetadata>, &'a mut Document, &'a InputPreprocessorMessageHandler, &'a FontCache); +impl<'a> MessageHandler> for TransformLayerMessageHandler { #[remain::check] - fn process_action( - &mut self, - message: TransformLayerMessage, - data: (&mut HashMap, LayerMetadata>, &mut Document, &InputPreprocessorMessageHandler), - responses: &mut VecDeque, - ) { + fn process_action(&mut self, message: TransformLayerMessage, (layer_metadata, document, ipp, font_cache): TransformData, responses: &mut VecDeque) { use TransformLayerMessage::*; - let (layer_metadata, document, ipp) = data; - let selected_layers = layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then(|| layer_path)).collect::>(); let mut selected = Selected::new(&mut self.original_transforms, &mut self.pivot, &selected_layers, responses, document); @@ -45,7 +40,7 @@ impl MessageHandler, LayerMeta selected.revert_operation(); typing.clear(); } else { - *selected.pivot = selected.calculate_pivot(&document.font_cache); + *selected.pivot = selected.calculate_pivot(font_cache); } *mouse_position = ipp.mouse.position; @@ -128,7 +123,7 @@ impl MessageHandler, LayerMeta self.transform_operation.apply_transform_operation(&mut selected, self.snap); } TransformOperation::Rotating(rotation) => { - let selected_pivot = selected.calculate_pivot(&document.font_cache); + let selected_pivot = selected.calculate_pivot(font_cache); let angle = { let start_offset = self.mouse_position - selected_pivot; let end_offset = ipp.mouse.position - selected_pivot; diff --git a/editor/src/document/transformation.rs b/editor/src/document/transformation.rs index 1cad76ff3e..fe752065d7 100644 --- a/editor/src/document/transformation.rs +++ b/editor/src/document/transformation.rs @@ -1,7 +1,8 @@ use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL}; use crate::message_prelude::*; -use graphene::document::{Document, FontCache}; +use graphene::document::Document; +use graphene::layers::text_layer::FontCache; use graphene::Operation as DocumentOperation; use glam::{DAffine2, DVec2}; @@ -9,7 +10,7 @@ use std::collections::{HashMap, VecDeque}; pub type OriginalTransforms = HashMap, DAffine2>; -#[derive(Debug, Clone, PartialEq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub enum Axis { Both, X, @@ -270,7 +271,7 @@ impl<'a> Selected<'a> { } } -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Typing { pub digits: Vec, pub contains_decimal: bool, diff --git a/editor/src/document/utility_types.rs b/editor/src/document/utility_types.rs index 70fb435833..1f669158ac 100644 --- a/editor/src/document/utility_types.rs +++ b/editor/src/document/utility_types.rs @@ -8,19 +8,19 @@ use std::fmt; pub type DocumentSave = (GrapheneDocument, HashMap, LayerMetadata>); -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Hash)] +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)] pub enum FlipAxis { X, Y, } -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Hash)] +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)] pub enum AlignAxis { X, Y, } -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Hash)] +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)] pub enum AlignAggregate { Min, Max, @@ -28,13 +28,13 @@ pub enum AlignAggregate { Average, } -#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum TargetDocument { Artboard, Artwork, } -#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum DocumentMode { DesignMode, SelectMode, diff --git a/editor/src/frontend/frontend_message.rs b/editor/src/frontend/frontend_message.rs index bc2139ad89..8ae40e58f1 100644 --- a/editor/src/frontend/frontend_message.rs +++ b/editor/src/frontend/frontend_message.rs @@ -6,6 +6,8 @@ use crate::message_prelude::*; use crate::misc::HintData; use crate::Color; +use graphene::layers::text_layer::Font; + use serde::{Deserialize, Serialize}; #[remain::sorted] @@ -23,8 +25,7 @@ pub enum FrontendMessage { TriggerAboutGraphiteLocalizedCommitDate { commit_date: String }, TriggerFileDownload { document: String, name: String }, TriggerFileUpload, - TriggerFontLoad { font_file_url: String }, - TriggerFontLoadDefault, + TriggerFontLoad { font: Font, is_default: bool }, TriggerIndexedDbRemoveDocument { document_id: u64 }, TriggerIndexedDbWriteDocument { document: String, details: FrontendDocumentDetails, version: String }, TriggerRasterDownload { document: String, name: String, mime: String, size: (f64, f64) }, diff --git a/editor/src/frontend/utility_types.rs b/editor/src/frontend/utility_types.rs index 41a6ec2416..4da111232f 100644 --- a/editor/src/frontend/utility_types.rs +++ b/editor/src/frontend/utility_types.rs @@ -1,14 +1,14 @@ use graphene::LayerId; use serde::{Deserialize, Serialize}; -#[derive(PartialEq, Clone, Deserialize, Serialize, Debug)] +#[derive(PartialEq, Eq, Clone, Deserialize, Serialize, Debug)] pub struct FrontendDocumentDetails { pub is_saved: bool, pub name: String, pub id: u64, } -#[derive(PartialEq, Clone, Deserialize, Serialize, Debug)] +#[derive(PartialEq, Eq, Clone, Deserialize, Serialize, Debug)] pub struct FrontendImageData { pub path: Vec, pub mime: String, diff --git a/editor/src/global/global_message.rs b/editor/src/global/global_message.rs index 3f7173f456..259c17894c 100644 --- a/editor/src/global/global_message.rs +++ b/editor/src/global/global_message.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; #[remain::sorted] #[impl_message(Message, Global)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum GlobalMessage { LogDebug, LogInfo, diff --git a/editor/src/input/input_mapper_message.rs b/editor/src/input/input_mapper_message.rs index 454ebcaa56..9c34c49aed 100644 --- a/editor/src/input/input_mapper_message.rs +++ b/editor/src/input/input_mapper_message.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; #[remain::sorted] #[impl_message(Message, InputMapper)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum InputMapperMessage { // Sub-messages #[remain::unsorted] diff --git a/editor/src/layout/layout_message_handler.rs b/editor/src/layout/layout_message_handler.rs index 505858d508..e41ccb9254 100644 --- a/editor/src/layout/layout_message_handler.rs +++ b/editor/src/layout/layout_message_handler.rs @@ -3,6 +3,8 @@ use super::widgets::WidgetLayout; use crate::layout::widgets::Widget; use crate::message_prelude::*; +use graphene::layers::text_layer::Font; + use serde_json::Value; use std::collections::VecDeque; @@ -95,17 +97,20 @@ impl MessageHandler for LayoutMessageHandler { let update_value = value.as_object().expect("FontInput update was not of type: object"); let font_family_value = update_value.get("fontFamily").expect("FontInput update does not have a fontFamily"); let font_style_value = update_value.get("fontStyle").expect("FontInput update does not have a fontStyle"); - let font_file_url_value = update_value.get("fontFileUrl").expect("FontInput update does not have a fontFileUrl"); let font_family = font_family_value.as_str().expect("FontInput update fontFamily was not of type: string"); let font_style = font_style_value.as_str().expect("FontInput update fontStyle was not of type: string"); - let font_file_url = font_file_url_value.as_str().expect("FontInput update fontFileUrl was not of type: string"); font_input.font_family = font_family.into(); font_input.font_style = font_style.into(); - font_input.font_file_url = font_file_url.into(); - responses.push_back(DocumentMessage::LoadFont { font_file_url: font_file_url.into() }.into()); + responses.push_back( + PortfolioMessage::LoadFont { + font: Font::new(font_family.into(), font_style.into()), + is_default: false, + } + .into(), + ); let callback_message = (font_input.on_update.callback)(font_input); responses.push_back(callback_message); } diff --git a/editor/src/layout/widgets.rs b/editor/src/layout/widgets.rs index 0999e0d2d4..3aebb7f037 100644 --- a/editor/src/layout/widgets.rs +++ b/editor/src/layout/widgets.rs @@ -255,14 +255,12 @@ pub struct FontInput { pub font_family: String, #[serde(rename = "fontStyle")] pub font_style: String, - #[serde(rename = "fontFileUrl")] - pub font_file_url: String, #[serde(skip)] #[derivative(Debug = "ignore", PartialEq = "ignore")] pub on_update: WidgetCallback, } -#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] pub enum NumberInputIncrementBehavior { Add, Multiply, @@ -275,7 +273,7 @@ impl Default for NumberInputIncrementBehavior { } } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Separator { pub direction: SeparatorDirection, @@ -283,13 +281,13 @@ pub struct Separator { pub separator_type: SeparatorType, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum SeparatorDirection { Horizontal, Vertical, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum SeparatorType { Related, Unrelated, @@ -414,14 +412,14 @@ pub struct RadioEntryData { pub on_update: WidgetCallback<()>, } -#[derive(Clone, Serialize, Deserialize, Derivative, Debug, PartialEq)] +#[derive(Clone, Serialize, Deserialize, Derivative, Debug, PartialEq, Eq)] pub struct IconLabel { pub icon: String, #[serde(rename = "gapAfter")] pub gap_after: bool, } -#[derive(Clone, Serialize, Deserialize, Derivative, Debug, PartialEq, Default)] +#[derive(Clone, Serialize, Deserialize, Derivative, Debug, PartialEq, Eq, Default)] pub struct TextLabel { pub value: String, pub bold: bool, diff --git a/editor/src/viewport_tools/tool.rs b/editor/src/viewport_tools/tool.rs index 5bbcdd7a08..71794f93e0 100644 --- a/editor/src/viewport_tools/tool.rs +++ b/editor/src/viewport_tools/tool.rs @@ -6,28 +6,20 @@ use crate::layout::widgets::{IconButton, LayoutRow, PropertyHolder, Separator, S use crate::message_prelude::*; use graphene::color::Color; +use graphene::layers::text_layer::FontCache; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; use std::fmt::{self, Debug}; -pub type ToolActionHandlerData<'a> = (&'a DocumentMessageHandler, &'a DocumentToolData, &'a InputPreprocessorMessageHandler); +pub type ToolActionHandlerData<'a> = (&'a DocumentMessageHandler, &'a DocumentToolData, &'a InputPreprocessorMessageHandler, &'a FontCache); pub trait Fsm { type ToolData; type ToolOptions; #[must_use] - fn transition( - self, - message: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, - options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, - messages: &mut VecDeque, - ) -> Self; + fn transition(self, message: ToolMessage, tool_data: &mut Self::ToolData, transition_data: ToolActionHandlerData, options: &Self::ToolOptions, messages: &mut VecDeque) -> Self; fn update_hints(&self, responses: &mut VecDeque); fn update_cursor(&self, responses: &mut VecDeque); diff --git a/editor/src/viewport_tools/tool_message_handler.rs b/editor/src/viewport_tools/tool_message_handler.rs index 21c8ddb43f..7cae254296 100644 --- a/editor/src/viewport_tools/tool_message_handler.rs +++ b/editor/src/viewport_tools/tool_message_handler.rs @@ -6,6 +6,7 @@ use crate::layout::widgets::PropertyHolder; use crate::message_prelude::*; use graphene::color::Color; +use graphene::layers::text_layer::FontCache; use std::collections::VecDeque; @@ -14,12 +15,12 @@ pub struct ToolMessageHandler { tool_state: ToolFsmState, } -impl MessageHandler for ToolMessageHandler { +impl MessageHandler for ToolMessageHandler { #[remain::check] - fn process_action(&mut self, message: ToolMessage, data: (&DocumentMessageHandler, &InputPreprocessorMessageHandler), responses: &mut VecDeque) { + fn process_action(&mut self, message: ToolMessage, data: (&DocumentMessageHandler, &InputPreprocessorMessageHandler, &FontCache), responses: &mut VecDeque) { use ToolMessage::*; - let (document, input) = data; + let (document, input, font_cache) = data; #[remain::sorted] match message { // Messages @@ -41,11 +42,11 @@ impl MessageHandler { let document_data = &mut self.tool_state.document_tool_data; @@ -157,7 +162,7 @@ impl MessageHandler MessageHandler> for ArtboardTool return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.data, data, &(), responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -99,11 +97,9 @@ impl Fsm for ArtboardToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - _tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, _global_tool_data, input, font_cache): ToolActionHandlerData, _tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { if let ToolMessage::Artboard(event) = event { @@ -111,8 +107,8 @@ impl Fsm for ArtboardToolFsmState { (ArtboardToolFsmState::Ready | ArtboardToolFsmState::ResizingBounds | ArtboardToolFsmState::Dragging, ArtboardToolMessage::DocumentIsDirty) => { let mut buffer = Vec::new(); match ( - data.selected_board.map(|path| document.artboard_bounding_box_and_transform(&[path])).unwrap_or(None), - data.bounding_box_overlays.take(), + tool_data.selected_board.map(|path| document.artboard_bounding_box_and_transform(&[path], font_cache)).unwrap_or(None), + tool_data.bounding_box_overlays.take(), ) { (None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(&mut buffer), (Some((bounds, transform)), paths) => { @@ -123,12 +119,12 @@ impl Fsm for ArtboardToolFsmState { bounding_box_overlays.transform(&mut buffer); - data.bounding_box_overlays = Some(bounding_box_overlays); + tool_data.bounding_box_overlays = Some(bounding_box_overlays); responses.push_back(OverlaysMessage::Rerender.into()); responses.push_back( PropertiesPanelMessage::SetActiveLayers { - paths: vec![vec![data.selected_board.unwrap()]], + paths: vec![vec![tool_data.selected_board.unwrap()]], document: TargetDocument::Artboard, } .into(), @@ -140,10 +136,10 @@ impl Fsm for ArtboardToolFsmState { self } (ArtboardToolFsmState::Ready, ArtboardToolMessage::PointerDown) => { - data.drag_start = input.mouse.position; - data.drag_current = input.mouse.position; + tool_data.drag_start = input.mouse.position; + tool_data.drag_current = input.mouse.position; - let dragging_bounds = if let Some(bounding_box) = &mut data.bounding_box_overlays { + let dragging_bounds = if let Some(bounding_box) = &mut tool_data.bounding_box_overlays { let edges = bounding_box.check_selected_edges(input.mouse.position); bounding_box.selected_edges = edges.map(|(top, bottom, left, right)| { @@ -161,22 +157,25 @@ impl Fsm for ArtboardToolFsmState { let snap_x = selected_edges.2 || selected_edges.3; let snap_y = selected_edges.0 || selected_edges.1; - data.snap_handler - .start_snap(document, document.bounding_boxes(None, Some(data.selected_board.unwrap())), snap_x, snap_y); - data.snap_handler.add_all_document_handles(document, &[], &[]); + tool_data + .snap_handler + .start_snap(document, document.bounding_boxes(None, Some(tool_data.selected_board.unwrap()), font_cache), snap_x, snap_y); + tool_data.snap_handler.add_all_document_handles(document, &[], &[]); ArtboardToolFsmState::ResizingBounds } else { 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_graphene_document.intersects_quad_root(quad); + let intersection = document.artboard_message_handler.artboards_graphene_document.intersects_quad_root(quad, font_cache); responses.push_back(ToolMessage::DocumentIsDirty.into()); if let Some(intersection) = intersection.last() { - data.selected_board = Some(intersection[0]); + tool_data.selected_board = Some(intersection[0]); - data.snap_handler.start_snap(document, document.bounding_boxes(None, Some(intersection[0])), true, true); - data.snap_handler.add_all_document_handles(document, &[], &[]); + tool_data + .snap_handler + .start_snap(document, document.bounding_boxes(None, Some(intersection[0]), font_cache), true, true); + tool_data.snap_handler.add_all_document_handles(document, &[], &[]); responses.push_back( PropertiesPanelMessage::SetActiveLayers { @@ -189,10 +188,10 @@ impl Fsm for ArtboardToolFsmState { ArtboardToolFsmState::Dragging } else { let id = generate_uuid(); - data.selected_board = Some(id); + tool_data.selected_board = Some(id); - data.snap_handler.start_snap(document, document.bounding_boxes(None, Some(id)), true, true); - data.snap_handler.add_all_document_handles(document, &[], &[]); + tool_data.snap_handler.start_snap(document, document.bounding_boxes(None, Some(id), font_cache), true, true); + tool_data.snap_handler.add_all_document_handles(document, &[], &[]); responses.push_back( ArtboardMessage::AddArtboard { @@ -210,20 +209,20 @@ impl Fsm for ArtboardToolFsmState { } } (ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => { - if let Some(bounds) = &data.bounding_box_overlays { + if let Some(bounds) = &tool_data.bounding_box_overlays { if let Some(movement) = &bounds.selected_edges { let from_center = input.keyboard.get(center as usize); let constrain_square = input.keyboard.get(constrain_axis_or_aspect as usize); let mouse_position = input.mouse.position; - let snapped_mouse_position = data.snap_handler.snap_position(responses, document, mouse_position); + let snapped_mouse_position = tool_data.snap_handler.snap_position(responses, document, mouse_position); let [position, size] = movement.new_size(snapped_mouse_position, bounds.transform, from_center, constrain_square); let position = movement.center_position(position, size, from_center); responses.push_back( ArtboardMessage::ResizeArtboard { - artboard: data.selected_board.unwrap(), + artboard: tool_data.selected_board.unwrap(), position: position.round().into(), size: size.round().into(), } @@ -236,22 +235,22 @@ impl Fsm for ArtboardToolFsmState { ArtboardToolFsmState::ResizingBounds } (ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, .. }) => { - if let Some(bounds) = &data.bounding_box_overlays { + if let Some(bounds) = &tool_data.bounding_box_overlays { let axis_align = input.keyboard.get(constrain_axis_or_aspect as usize); - let mouse_position = axis_align_drag(axis_align, input.mouse.position, data.drag_start); - let mouse_delta = mouse_position - data.drag_current; + let mouse_position = axis_align_drag(axis_align, input.mouse.position, tool_data.drag_start); + let mouse_delta = mouse_position - tool_data.drag_current; let snap = bounds.evaluate_transform_handle_positions().into_iter().collect(); - let closest_move = data.snap_handler.snap_layers(responses, document, snap, mouse_delta); + let closest_move = tool_data.snap_handler.snap_layers(responses, document, snap, mouse_delta); let size = bounds.bounds[1] - bounds.bounds[0]; - let position = bounds.bounds[0] + bounds.transform.inverse().transform_vector2(mouse_position - data.drag_current + closest_move); + let position = bounds.bounds[0] + bounds.transform.inverse().transform_vector2(mouse_position - tool_data.drag_current + closest_move); responses.push_back( ArtboardMessage::ResizeArtboard { - artboard: data.selected_board.unwrap(), + artboard: tool_data.selected_board.unwrap(), position: position.round().into(), size: size.round().into(), } @@ -260,17 +259,17 @@ impl Fsm for ArtboardToolFsmState { responses.push_back(ToolMessage::DocumentIsDirty.into()); - data.drag_current = mouse_position + closest_move; + tool_data.drag_current = mouse_position + closest_move; } ArtboardToolFsmState::Dragging } (ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => { let mouse_position = input.mouse.position; - let snapped_mouse_position = data.snap_handler.snap_position(responses, document, mouse_position); + let snapped_mouse_position = tool_data.snap_handler.snap_position(responses, document, mouse_position); let root_transform = document.graphene_document.root.transform.inverse(); - let mut start = data.drag_start; + let mut start = tool_data.drag_start; let mut size = snapped_mouse_position - start; // Constrain axis if input.keyboard.get(constrain_axis_or_aspect as usize) { @@ -287,7 +286,7 @@ impl Fsm for ArtboardToolFsmState { responses.push_back( ArtboardMessage::ResizeArtboard { - artboard: data.selected_board.unwrap(), + artboard: tool_data.selected_board.unwrap(), position: start.round().into(), size: size.round().into(), } @@ -298,7 +297,7 @@ impl Fsm for ArtboardToolFsmState { // This might result in a few more calls but it is not reliant on the order of messages responses.push_back( PropertiesPanelMessage::SetActiveLayers { - paths: vec![vec![data.selected_board.unwrap()]], + paths: vec![vec![tool_data.selected_board.unwrap()]], document: TargetDocument::Artboard, } .into(), @@ -309,28 +308,28 @@ impl Fsm for ArtboardToolFsmState { ArtboardToolFsmState::Drawing } (ArtboardToolFsmState::Ready, ArtboardToolMessage::PointerMove { .. }) => { - let cursor = data.bounding_box_overlays.as_ref().map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, false)); + let cursor = tool_data.bounding_box_overlays.as_ref().map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, false)); - if data.cursor != cursor { - data.cursor = cursor; + if tool_data.cursor != cursor { + tool_data.cursor = cursor; responses.push_back(FrontendMessage::UpdateMouseCursor { cursor }.into()); } ArtboardToolFsmState::Ready } (ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerUp) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); - if let Some(bounds) = &mut data.bounding_box_overlays { + if let Some(bounds) = &mut tool_data.bounding_box_overlays { bounds.original_transforms.clear(); } ArtboardToolFsmState::Ready } (ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerUp) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); - if let Some(bounds) = &mut data.bounding_box_overlays { + if let Some(bounds) = &mut tool_data.bounding_box_overlays { bounds.original_transforms.clear(); } @@ -339,23 +338,23 @@ impl Fsm for ArtboardToolFsmState { ArtboardToolFsmState::Ready } (ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerUp) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); - if let Some(bounds) = &mut data.bounding_box_overlays { + if let Some(bounds) = &mut tool_data.bounding_box_overlays { bounds.original_transforms.clear(); } ArtboardToolFsmState::Ready } (_, ArtboardToolMessage::DeleteSelected) => { - if let Some(artboard) = data.selected_board.take() { + if let Some(artboard) = tool_data.selected_board.take() { responses.push_back(ArtboardMessage::DeleteArtboard { artboard }.into()); responses.push_back(ToolMessage::DocumentIsDirty.into()); } ArtboardToolFsmState::Ready } (_, ArtboardToolMessage::Abort) => { - if let Some(bounding_box_overlays) = data.bounding_box_overlays.take() { + if let Some(bounding_box_overlays) = tool_data.bounding_box_overlays.take() { bounding_box_overlays.delete(responses); } @@ -368,7 +367,7 @@ impl Fsm for ArtboardToolFsmState { .into(), ); - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); ArtboardToolFsmState::Ready } _ => self, diff --git a/editor/src/viewport_tools/tools/ellipse_tool.rs b/editor/src/viewport_tools/tools/ellipse_tool.rs index 07e9922fe5..820b7fd236 100644 --- a/editor/src/viewport_tools/tools/ellipse_tool.rs +++ b/editor/src/viewport_tools/tools/ellipse_tool.rs @@ -1,13 +1,11 @@ use super::shared::resize::Resize; use crate::consts::DRAG_THRESHOLD; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::PropertyHolder; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use graphene::layers::style; use graphene::Operation; @@ -23,7 +21,7 @@ pub struct EllipseTool { #[remain::sorted] #[impl_message(Message, ToolMessage, Ellipse)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum EllipseToolMessage { // Standard messages #[remain::unsorted] @@ -52,7 +50,7 @@ impl<'a> MessageHandler> for EllipseTool return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.data, data, &(), responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -95,22 +93,20 @@ impl Fsm for EllipseToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, _tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use EllipseToolFsmState::*; use EllipseToolMessage::*; - let mut shape_data = &mut data.data; + let mut shape_data = &mut tool_data.data; if let ToolMessage::Ellipse(event) = event { match (self, event) { (Ready, DragStart) => { - shape_data.start(responses, document, input.mouse.position); + shape_data.start(responses, document, input.mouse.position, font_cache); responses.push_back(DocumentMessage::StartTransaction.into()); shape_data.path = Some(document.get_path_for_new_layer()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); @@ -120,7 +116,7 @@ impl Fsm for EllipseToolFsmState { path: shape_data.path.clone().unwrap(), insert_index: -1, transform: DAffine2::ZERO.to_cols_array(), - style: style::PathStyle::new(None, style::Fill::solid(tool_data.primary_color)), + style: style::PathStyle::new(None, style::Fill::solid(global_tool_data.primary_color)), } .into(), ); diff --git a/editor/src/viewport_tools/tools/eyedropper_tool.rs b/editor/src/viewport_tools/tools/eyedropper_tool.rs index 9b7a5aaba8..a3ef40bc6f 100644 --- a/editor/src/viewport_tools/tools/eyedropper_tool.rs +++ b/editor/src/viewport_tools/tools/eyedropper_tool.rs @@ -1,12 +1,10 @@ use crate::consts::SELECTION_TOLERANCE; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::MouseMotion; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::PropertyHolder; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo}; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use graphene::intersection::Quad; use graphene::layers::layer_info::LayerDataType; @@ -22,7 +20,7 @@ pub struct EyedropperTool { #[remain::sorted] #[impl_message(Message, ToolMessage, Eyedropper)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum EyedropperToolMessage { // Standard messages #[remain::unsorted] @@ -47,7 +45,7 @@ impl<'a> MessageHandler> for EyedropperTo return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.data, data, &(), responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -80,11 +78,9 @@ impl Fsm for EyedropperToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - _tool_data: &DocumentToolData, - _data: &mut Self::ToolData, + _tool_data: &mut Self::ToolData, + (document, _global_tool_data, input, font_cache): ToolActionHandlerData, _tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use EyedropperToolFsmState::*; @@ -98,7 +94,7 @@ impl Fsm for EyedropperToolFsmState { let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]); // TODO: Destroy this pyramid - if let Some(path) = document.graphene_document.intersects_quad_root(quad).last() { + if let Some(path) = document.graphene_document.intersects_quad_root(quad, font_cache).last() { if let Ok(layer) = document.graphene_document.layer(path) { if let LayerDataType::Shape(shape) = &layer.data { if shape.style.fill().is_some() { diff --git a/editor/src/viewport_tools/tools/fill_tool.rs b/editor/src/viewport_tools/tools/fill_tool.rs index 5452b0a469..7eaac71256 100644 --- a/editor/src/viewport_tools/tools/fill_tool.rs +++ b/editor/src/viewport_tools/tools/fill_tool.rs @@ -1,12 +1,10 @@ use crate::consts::SELECTION_TOLERANCE; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::MouseMotion; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::PropertyHolder; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo}; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use graphene::intersection::Quad; use graphene::Operation; @@ -23,7 +21,7 @@ pub struct FillTool { #[remain::sorted] #[impl_message(Message, ToolMessage, Fill)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum FillToolMessage { // Standard messages #[remain::unsorted] @@ -48,7 +46,7 @@ impl<'a> MessageHandler> for FillTool { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.data, data, &(), responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -81,11 +79,9 @@ impl Fsm for FillToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - _data: &mut Self::ToolData, + _tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, _tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use FillToolFsmState::*; @@ -98,10 +94,10 @@ impl Fsm for FillToolFsmState { let tolerance = DVec2::splat(SELECTION_TOLERANCE); let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]); - if let Some(path) = document.graphene_document.intersects_quad_root(quad).last() { + if let Some(path) = document.graphene_document.intersects_quad_root(quad, font_cache).last() { let color = match lmb_or_rmb { - LeftMouseDown => tool_data.primary_color, - RightMouseDown => tool_data.secondary_color, + LeftMouseDown => global_tool_data.primary_color, + RightMouseDown => global_tool_data.secondary_color, Abort => unreachable!(), }; let fill = Fill::Solid(color); diff --git a/editor/src/viewport_tools/tools/freehand_tool.rs b/editor/src/viewport_tools/tools/freehand_tool.rs index a6a94448e3..f2e49ce427 100644 --- a/editor/src/viewport_tools/tools/freehand_tool.rs +++ b/editor/src/viewport_tools/tools/freehand_tool.rs @@ -1,7 +1,5 @@ -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::MouseMotion; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout}; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo}; @@ -92,7 +90,7 @@ impl<'a> MessageHandler> for FreehandTool return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.data, data, &self.options, responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -130,11 +128,9 @@ impl Fsm for FreehandToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, _font_cache): ToolActionHandlerData, tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use FreehandToolFsmState::*; @@ -147,42 +143,42 @@ impl Fsm for FreehandToolFsmState { (Ready, DragStart) => { responses.push_back(DocumentMessage::StartTransaction.into()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); - data.path = Some(document.get_path_for_new_layer()); + tool_data.path = Some(document.get_path_for_new_layer()); let pos = transform.inverse().transform_point2(input.mouse.position); - data.points.push(pos); + tool_data.points.push(pos); - data.weight = tool_options.line_weight; + tool_data.weight = tool_options.line_weight; - responses.push_back(add_polyline(data, tool_data)); + responses.push_back(add_polyline(tool_data, global_tool_data)); Drawing } (Drawing, PointerMove) => { let pos = transform.inverse().transform_point2(input.mouse.position); - if data.points.last() != Some(&pos) { - data.points.push(pos); + if tool_data.points.last() != Some(&pos) { + tool_data.points.push(pos); } - responses.push_back(remove_preview(data)); - responses.push_back(add_polyline(data, tool_data)); + responses.push_back(remove_preview(tool_data)); + responses.push_back(add_polyline(tool_data, global_tool_data)); Drawing } (Drawing, DragStop) | (Drawing, Abort) => { - if data.points.len() >= 2 { + if tool_data.points.len() >= 2 { responses.push_back(DocumentMessage::DeselectAllLayers.into()); - responses.push_back(remove_preview(data)); - responses.push_back(add_polyline(data, tool_data)); + responses.push_back(remove_preview(tool_data)); + responses.push_back(add_polyline(tool_data, global_tool_data)); responses.push_back(DocumentMessage::CommitTransaction.into()); } else { responses.push_back(DocumentMessage::AbortTransaction.into()); } - data.path = None; - data.points.clear(); + tool_data.path = None; + tool_data.points.clear(); Ready } diff --git a/editor/src/viewport_tools/tools/gradient_tool.rs b/editor/src/viewport_tools/tools/gradient_tool.rs index bb1ae97eca..154dddc36b 100644 --- a/editor/src/viewport_tools/tools/gradient_tool.rs +++ b/editor/src/viewport_tools/tools/gradient_tool.rs @@ -2,12 +2,11 @@ use crate::consts::{COLOR_ACCENT, LINE_ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE, V use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::{LayoutRow, PropertyHolder, RadioEntryData, RadioInput, Widget, WidgetCallback, WidgetHolder, WidgetLayout}; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; use crate::viewport_tools::snapping::SnapHandler; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use graphene::color::Color; use graphene::intersection::Quad; @@ -16,6 +15,7 @@ use graphene::layers::style::{Fill, Gradient, GradientType, PathStyle, Stroke}; use graphene::Operation; use glam::{DAffine2, DVec2}; +use graphene::layers::text_layer::FontCache; use serde::{Deserialize, Serialize}; #[derive(Default)] @@ -37,7 +37,7 @@ impl Default for GradientOptions { #[remain::sorted] #[impl_message(Message, ToolMessage, Gradient)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum GradientToolMessage { // Standard messages #[remain::unsorted] @@ -55,7 +55,7 @@ pub enum GradientToolMessage { } #[remain::sorted] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum GradientOptionsUpdate { Type(GradientType), } @@ -78,7 +78,7 @@ impl<'a> MessageHandler> for GradientTool return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.data, data, &self.options, responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -128,8 +128,8 @@ impl Default for GradientToolFsmState { } /// Computes the transform from gradient space to layer space (where gradient space is 0..1 in layer space) -fn gradient_space_transform(path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler) -> DAffine2 { - let bounds = layer.aabounding_box_for_transform(DAffine2::IDENTITY, &document.graphene_document.font_cache).unwrap(); +fn gradient_space_transform(path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, font_cache: &FontCache) -> DAffine2 { + let bounds = layer.aabounding_box_for_transform(DAffine2::IDENTITY, font_cache).unwrap(); let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]); let multiplied = document.graphene_document.multiply_transforms(path).unwrap(); @@ -183,8 +183,8 @@ impl GradientOverlay { path } - pub fn new(fill: &Gradient, dragging_start: Option, path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, responses: &mut VecDeque) -> Self { - let transform = gradient_space_transform(path, layer, document); + pub fn new(fill: &Gradient, dragging_start: Option, path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, responses: &mut VecDeque, font_cache: &FontCache) -> Self { + let transform = gradient_space_transform(path, layer, document, font_cache); let Gradient { start, end, .. } = fill; let [start, end] = [transform.transform_point2(*start), transform.transform_point2(*end)]; @@ -232,8 +232,8 @@ struct SelectedGradient { } impl SelectedGradient { - pub fn new(gradient: Gradient, path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler) -> Self { - let transform = gradient_space_transform(path, layer, document); + pub fn new(gradient: Gradient, path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, font_cache: &FontCache) -> Self { + let transform = gradient_space_transform(path, layer, document, font_cache); Self { path: path.to_vec(), transform, @@ -291,8 +291,8 @@ struct GradientToolData { snap_handler: SnapHandler, } -pub fn start_snap(snap_handler: &mut SnapHandler, document: &DocumentMessageHandler) { - snap_handler.start_snap(document, document.bounding_boxes(None, None), true, true); +pub fn start_snap(snap_handler: &mut SnapHandler, document: &DocumentMessageHandler, font_cache: &FontCache) { + snap_handler.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); snap_handler.add_all_document_handles(document, &[], &[]); } @@ -303,17 +303,15 @@ impl Fsm for GradientToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { if let ToolMessage::Gradient(event) = event { match (self, event) { (_, GradientToolMessage::DocumentIsDirty) => { - while let Some(overlay) = data.gradient_overlays.pop() { + while let Some(overlay) = tool_data.gradient_overlays.pop() { overlay.delete_overlays(responses); } @@ -321,11 +319,13 @@ impl Fsm for GradientToolFsmState { let layer = document.graphene_document.layer(path).unwrap(); if let Ok(Fill::Gradient(gradient)) = layer.style().map(|style| style.fill()) { - let dragging_start = data + let dragging_start = tool_data .selected_gradient .as_ref() .and_then(|selected| if selected.path == path { Some(selected.dragging_start) } else { None }); - data.gradient_overlays.push(GradientOverlay::new(gradient, dragging_start, path, layer, document, responses)) + tool_data + .gradient_overlays + .push(GradientOverlay::new(gradient, dragging_start, path, layer, document, responses, font_cache)) } } @@ -338,11 +338,11 @@ impl Fsm for GradientToolFsmState { let tolerance = VECTOR_MANIPULATOR_ANCHOR_MARKER_SIZE.powi(2); let mut dragging = false; - for overlay in &data.gradient_overlays { + for overlay in &tool_data.gradient_overlays { if overlay.evaluate_gradient_start().distance_squared(mouse) < tolerance { dragging = true; - start_snap(&mut data.snap_handler, document); - data.selected_gradient = Some(SelectedGradient { + start_snap(&mut tool_data.snap_handler, document, font_cache); + tool_data.selected_gradient = Some(SelectedGradient { path: overlay.path.clone(), transform: overlay.transform, gradient: overlay.gradient.clone(), @@ -351,8 +351,8 @@ impl Fsm for GradientToolFsmState { } if overlay.evaluate_gradient_end().distance_squared(mouse) < tolerance { dragging = true; - start_snap(&mut data.snap_handler, document); - data.selected_gradient = Some(SelectedGradient { + start_snap(&mut tool_data.snap_handler, document, font_cache); + tool_data.selected_gradient = Some(SelectedGradient { path: overlay.path.clone(), transform: overlay.transform, gradient: overlay.gradient.clone(), @@ -365,7 +365,7 @@ impl Fsm for GradientToolFsmState { } else { let tolerance = DVec2::splat(SELECTION_TOLERANCE); let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]); - let intersection = document.graphene_document.intersects_quad_root(quad).pop(); + let intersection = document.graphene_document.intersects_quad_root(quad, font_cache).pop(); if let Some(intersection) = intersection { if !document.selected_layers_contains(&intersection) { @@ -378,19 +378,19 @@ impl Fsm for GradientToolFsmState { let gradient = Gradient::new( DVec2::ZERO, - tool_data.secondary_color, + global_tool_data.secondary_color, DVec2::ONE, - tool_data.primary_color, + global_tool_data.primary_color, DAffine2::IDENTITY, generate_uuid(), tool_options.gradient_type, ); - let mut selected_gradient = SelectedGradient::new(gradient, &intersection, layer, document).with_gradient_start(input.mouse.position); + let mut selected_gradient = SelectedGradient::new(gradient, &intersection, layer, document, font_cache).with_gradient_start(input.mouse.position); selected_gradient.update_gradient(input.mouse.position, responses, false, tool_options.gradient_type); - data.selected_gradient = Some(selected_gradient); + tool_data.selected_gradient = Some(selected_gradient); - start_snap(&mut data.snap_handler, document); + start_snap(&mut tool_data.snap_handler, document, font_cache); GradientToolFsmState::Drawing } else { @@ -399,23 +399,23 @@ impl Fsm for GradientToolFsmState { } } (GradientToolFsmState::Drawing, GradientToolMessage::PointerMove { constrain_axis }) => { - if let Some(selected_gradient) = &mut data.selected_gradient { - let mouse = data.snap_handler.snap_position(responses, document, input.mouse.position); + if let Some(selected_gradient) = &mut tool_data.selected_gradient { + let mouse = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); selected_gradient.update_gradient(mouse, responses, input.keyboard.get(constrain_axis as usize), selected_gradient.gradient.gradient_type); } GradientToolFsmState::Drawing } (GradientToolFsmState::Drawing, GradientToolMessage::PointerUp) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); GradientToolFsmState::Ready } (_, GradientToolMessage::Abort) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); - while let Some(overlay) = data.gradient_overlays.pop() { + while let Some(overlay) = tool_data.gradient_overlays.pop() { overlay.delete_overlays(responses); } GradientToolFsmState::Ready diff --git a/editor/src/viewport_tools/tools/line_tool.rs b/editor/src/viewport_tools/tools/line_tool.rs index 8fb0b962b2..2346cd8fa3 100644 --- a/editor/src/viewport_tools/tools/line_tool.rs +++ b/editor/src/viewport_tools/tools/line_tool.rs @@ -1,14 +1,12 @@ use crate::consts::{DRAG_THRESHOLD, LINE_ROTATE_SNAP_ANGLE}; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; use crate::input::mouse::ViewportPosition; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout}; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; use crate::viewport_tools::snapping::SnapHandler; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use graphene::layers::style; use graphene::Operation; @@ -19,7 +17,7 @@ use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct LineTool { fsm_state: LineToolFsmState, - data: LineToolData, + tool_data: LineToolData, options: LineOptions, } @@ -75,7 +73,7 @@ impl PropertyHolder for LineTool { } impl<'a> MessageHandler> for LineTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -93,7 +91,7 @@ impl<'a> MessageHandler> for LineTool { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &self.options, responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -141,11 +139,9 @@ impl Fsm for LineToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use LineToolFsmState::*; @@ -154,22 +150,22 @@ impl Fsm for LineToolFsmState { if let ToolMessage::Line(event) = event { match (self, event) { (Ready, DragStart) => { - data.snap_handler.start_snap(document, document.bounding_boxes(None, None), true, true); - data.snap_handler.add_all_document_handles(document, &[], &[]); - data.drag_start = data.snap_handler.snap_position(responses, document, input.mouse.position); + tool_data.snap_handler.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); + tool_data.snap_handler.add_all_document_handles(document, &[], &[]); + tool_data.drag_start = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); responses.push_back(DocumentMessage::StartTransaction.into()); - data.path = Some(document.get_path_for_new_layer()); + tool_data.path = Some(document.get_path_for_new_layer()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); - data.weight = tool_options.line_weight; + tool_data.weight = tool_options.line_weight; responses.push_back( Operation::AddLine { - path: data.path.clone().unwrap(), + path: tool_data.path.clone().unwrap(), insert_index: -1, transform: DAffine2::ZERO.to_cols_array(), - style: style::PathStyle::new(Some(style::Stroke::new(tool_data.primary_color, data.weight)), style::Fill::None), + style: style::PathStyle::new(Some(style::Stroke::new(global_tool_data.primary_color, tool_data.weight)), style::Fill::None), } .into(), ); @@ -177,30 +173,30 @@ impl Fsm for LineToolFsmState { Drawing } (Drawing, Redraw { center, snap_angle, lock_angle }) => { - data.drag_current = data.snap_handler.snap_position(responses, document, input.mouse.position); + tool_data.drag_current = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); let values: Vec<_> = [lock_angle, snap_angle, center].iter().map(|k| input.keyboard.get(*k as usize)).collect(); - responses.push_back(generate_transform(data, values[0], values[1], values[2])); + responses.push_back(generate_transform(tool_data, values[0], values[1], values[2])); Drawing } (Drawing, DragStop) => { - data.drag_current = data.snap_handler.snap_position(responses, document, input.mouse.position); - data.snap_handler.cleanup(responses); + tool_data.drag_current = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); + tool_data.snap_handler.cleanup(responses); - match data.drag_start.distance(input.mouse.position) <= DRAG_THRESHOLD { + match tool_data.drag_start.distance(input.mouse.position) <= DRAG_THRESHOLD { true => responses.push_back(DocumentMessage::AbortTransaction.into()), false => responses.push_back(DocumentMessage::CommitTransaction.into()), } - data.path = None; + tool_data.path = None; Ready } (Drawing, Abort) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); responses.push_back(DocumentMessage::AbortTransaction.into()); - data.path = None; + tool_data.path = None; Ready } _ => self, @@ -268,16 +264,16 @@ impl Fsm for LineToolFsmState { } } -fn generate_transform(data: &mut LineToolData, lock: bool, snap: bool, center: bool) -> Message { - let mut start = data.drag_start; - let stop = data.drag_current; +fn generate_transform(tool_data: &mut LineToolData, lock: bool, snap: bool, center: bool) -> Message { + let mut start = tool_data.drag_start; + let stop = tool_data.drag_current; let dir = stop - start; let mut angle = -dir.angle_between(DVec2::X); if lock { - angle = data.angle + angle = tool_data.angle }; if snap { @@ -285,7 +281,7 @@ fn generate_transform(data: &mut LineToolData, lock: bool, snap: bool, center: b angle = (angle / snap_resolution).round() * snap_resolution; } - data.angle = angle; + tool_data.angle = angle; let mut scale = dir.length(); @@ -300,7 +296,7 @@ fn generate_transform(data: &mut LineToolData, lock: bool, snap: bool, center: b } Operation::SetLayerTransformInViewport { - path: data.path.clone().unwrap(), + path: tool_data.path.clone().unwrap(), transform: glam::DAffine2::from_scale_angle_translation(DVec2::new(scale, 1.), angle, start).to_cols_array(), } .into() diff --git a/editor/src/viewport_tools/tools/navigate_tool.rs b/editor/src/viewport_tools/tools/navigate_tool.rs index 0fb6266129..a277f8be18 100644 --- a/editor/src/viewport_tools/tools/navigate_tool.rs +++ b/editor/src/viewport_tools/tools/navigate_tool.rs @@ -1,11 +1,9 @@ -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::PropertyHolder; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use glam::DVec2; use serde::{Deserialize, Serialize}; @@ -13,12 +11,12 @@ use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct NavigateTool { fsm_state: NavigateToolFsmState, - data: NavigateToolData, + tool_data: NavigateToolData, } #[remain::sorted] #[impl_message(Message, ToolMessage, Navigate)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum NavigateToolMessage { // Standard messages #[remain::unsorted] @@ -41,7 +39,7 @@ pub enum NavigateToolMessage { impl PropertyHolder for NavigateTool {} impl<'a> MessageHandler> for NavigateTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -52,7 +50,7 @@ impl<'a> MessageHandler> for NavigateTool return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &(), responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -97,11 +95,9 @@ impl Fsm for NavigateToolFsmState { fn transition( self, message: ToolMessage, - _document: &DocumentMessageHandler, - _tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (_document, _global_tool_data, input, _font_cache): ToolActionHandlerData, _tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, messages: &mut VecDeque, ) -> Self { if let ToolMessage::Navigate(navigate) = message { @@ -112,7 +108,7 @@ impl Fsm for NavigateToolFsmState { messages.push_front(MovementMessage::TransformCanvasEnd.into()); // Mouse has not moved from pointerdown to pointerup - if data.drag_start == input.mouse.position { + if tool_data.drag_start == input.mouse.position { messages.push_front(if zoom_in { MovementMessage::IncreaseCanvasZoom { center_on_mouse: true }.into() } else { @@ -128,24 +124,24 @@ impl Fsm for NavigateToolFsmState { snap_angle, wait_for_snap_angle_release: false, snap_zoom, - zoom_from_viewport: Some(data.drag_start), + zoom_from_viewport: Some(tool_data.drag_start), } .into(), ); self } TranslateCanvasBegin => { - data.drag_start = input.mouse.position; + tool_data.drag_start = input.mouse.position; messages.push_front(MovementMessage::TranslateCanvasBegin.into()); NavigateToolFsmState::Panning } RotateCanvasBegin => { - data.drag_start = input.mouse.position; + tool_data.drag_start = input.mouse.position; messages.push_front(MovementMessage::RotateCanvasBegin.into()); NavigateToolFsmState::Tilting } ZoomCanvasBegin => { - data.drag_start = input.mouse.position; + tool_data.drag_start = input.mouse.position; messages.push_front(MovementMessage::ZoomCanvasBegin.into()); NavigateToolFsmState::Zooming } diff --git a/editor/src/viewport_tools/tools/path_tool.rs b/editor/src/viewport_tools/tools/path_tool.rs index 858e23a722..5b0e626f97 100644 --- a/editor/src/viewport_tools/tools/path_tool.rs +++ b/editor/src/viewport_tools/tools/path_tool.rs @@ -1,13 +1,11 @@ use crate::consts::SELECTION_THRESHOLD; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::PropertyHolder; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; use crate::viewport_tools::snapping::SnapHandler; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use crate::viewport_tools::vector_editor::shape_editor::ShapeEditor; use graphene::intersection::Quad; @@ -18,12 +16,12 @@ use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct PathTool { fsm_state: PathToolFsmState, - data: PathToolData, + tool_data: PathToolData, } #[remain::sorted] #[impl_message(Message, ToolMessage, Path)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum PathToolMessage { // Standard messages #[remain::unsorted] @@ -47,7 +45,7 @@ pub enum PathToolMessage { impl PropertyHolder for PathTool {} impl<'a> MessageHandler> for PathTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -58,7 +56,7 @@ impl<'a> MessageHandler> for PathTool { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &(), responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -107,11 +105,9 @@ impl Fsm for PathToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - _tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, _global_tool_data, input, font_cache): ToolActionHandlerData, _tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { if let ToolMessage::Path(event) = event { @@ -122,17 +118,17 @@ impl Fsm for PathToolFsmState { // TODO: Capture a tool event instead of doing this? (_, SelectionChanged) => { // Remove any residual overlays that might exist on selection change - data.shape_editor.remove_overlays(responses); + tool_data.shape_editor.remove_overlays(responses); // This currently creates new VectorManipulatorShapes for every shape, which is not ideal // At least it is only on selection change for now - data.shape_editor.set_shapes_to_modify(document.selected_visible_layers_vector_shapes(responses)); + tool_data.shape_editor.set_shapes_to_modify(document.selected_visible_layers_vector_shapes(responses, font_cache)); self } (_, DocumentIsDirty) => { - // Update the VectorManipulatorShapes by reference so they match the kurbo data - for shape in &mut data.shape_editor.shapes_to_modify { + // Update the VectorManipulatorShapes by reference so they match the kurbo tool_data + for shape in &mut tool_data.shape_editor.shapes_to_modify { shape.update_shape(document, responses); } self @@ -142,16 +138,18 @@ impl Fsm for PathToolFsmState { let add_to_selection = input.keyboard.get(add_to_selection as usize); // Select the first point within the threshold (in pixels) - if data.shape_editor.select_point(input.mouse.position, SELECTION_THRESHOLD, add_to_selection, responses) { + if tool_data.shape_editor.select_point(input.mouse.position, SELECTION_THRESHOLD, add_to_selection, responses) { responses.push_back(DocumentMessage::StartTransaction.into()); - let ignore_document = data.shape_editor.shapes_to_modify.iter().map(|shape| shape.layer_path.clone()).collect::>(); - data.snap_handler.start_snap(document, document.bounding_boxes(Some(&ignore_document), None), true, true); + let ignore_document = tool_data.shape_editor.shapes_to_modify.iter().map(|shape| shape.layer_path.clone()).collect::>(); + tool_data + .snap_handler + .start_snap(document, document.bounding_boxes(Some(&ignore_document), None, font_cache), true, true); - let include_handles = data.shape_editor.shapes_to_modify.iter().map(|shape| shape.layer_path.as_slice()).collect::>(); - data.snap_handler.add_all_document_handles(document, &include_handles, &[]); + let include_handles = tool_data.shape_editor.shapes_to_modify.iter().map(|shape| shape.layer_path.as_slice()).collect::>(); + tool_data.snap_handler.add_all_document_handles(document, &include_handles, &[]); - data.drag_start_pos = input.mouse.position; + tool_data.drag_start_pos = input.mouse.position; Dragging } // We didn't find a point nearby, so consider selecting the nearest shape instead @@ -160,7 +158,7 @@ impl Fsm for PathToolFsmState { // Select shapes directly under our mouse let intersection = document .graphene_document - .intersects_quad_root(Quad::from_box([input.mouse.position - selection_size, input.mouse.position + selection_size])); + .intersects_quad_root(Quad::from_box([input.mouse.position - selection_size, input.mouse.position + selection_size]), font_cache); if !intersection.is_empty() { if add_to_selection { responses.push_back(DocumentMessage::AddSelectedLayers { additional_layers: intersection }.into()); @@ -191,33 +189,33 @@ impl Fsm for PathToolFsmState { ) => { // Determine when alt state changes let alt_pressed = input.keyboard.get(alt_mirror_angle as usize); - if alt_pressed != data.alt_debounce { - data.alt_debounce = alt_pressed; + if alt_pressed != tool_data.alt_debounce { + tool_data.alt_debounce = alt_pressed; // Only on alt down if alt_pressed { - data.shape_editor.toggle_selected_mirror_angle(); + tool_data.shape_editor.toggle_selected_mirror_angle(); } } // Determine when shift state changes let shift_pressed = input.keyboard.get(shift_mirror_distance as usize); - if shift_pressed != data.shift_debounce { - data.shift_debounce = shift_pressed; - data.shape_editor.toggle_selected_mirror_distance(); + if shift_pressed != tool_data.shift_debounce { + tool_data.shift_debounce = shift_pressed; + tool_data.shape_editor.toggle_selected_mirror_distance(); } // Move the selected points by the mouse position - let snapped_position = data.snap_handler.snap_position(responses, document, input.mouse.position); - data.shape_editor.move_selected_points(snapped_position - data.drag_start_pos, true, responses); + let snapped_position = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); + tool_data.shape_editor.move_selected_points(snapped_position - tool_data.drag_start_pos, true, responses); Dragging } // Mouse up (_, DragStop) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); Ready } (_, Abort) => { - data.shape_editor.remove_overlays(responses); + tool_data.shape_editor.remove_overlays(responses); Ready } ( diff --git a/editor/src/viewport_tools/tools/pen_tool.rs b/editor/src/viewport_tools/tools/pen_tool.rs index b6b1f50efd..d439d8e4bc 100644 --- a/editor/src/viewport_tools/tools/pen_tool.rs +++ b/editor/src/viewport_tools/tools/pen_tool.rs @@ -7,7 +7,7 @@ use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, Wid use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; use crate::viewport_tools::snapping::SnapHandler; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use crate::viewport_tools::vector_editor::constants::ControlPointType; use crate::viewport_tools::vector_editor::shape_editor::ShapeEditor; use crate::viewport_tools::vector_editor::vector_shape::VectorShape; @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct PenTool { fsm_state: PenToolFsmState, - data: PenToolData, + tool_data: PenToolData, options: PenOptions, } @@ -84,7 +84,7 @@ impl PropertyHolder for PenTool { } impl<'a> MessageHandler> for PenTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -102,7 +102,7 @@ impl<'a> MessageHandler> for PenTool { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &self.options, responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -144,11 +144,9 @@ impl Fsm for PenToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use PenToolFsmState::*; @@ -159,7 +157,7 @@ impl Fsm for PenToolFsmState { if let ToolMessage::Pen(event) = event { match (self, event) { (_, DocumentIsDirty) => { - data.shape_editor.update_shapes(document, responses); + tool_data.shape_editor.update_shapes(document, responses); self } (Ready, DragStart) => { @@ -167,76 +165,76 @@ impl Fsm for PenToolFsmState { responses.push_back(DocumentMessage::DeselectAllLayers.into()); // Create a new layer and prep snap system - data.path = Some(document.get_path_for_new_layer()); - data.snap_handler.start_snap(document, document.bounding_boxes(None, None), true, true); - data.snap_handler.add_all_document_handles(document, &[], &[]); - let snapped_position = data.snap_handler.snap_position(responses, document, input.mouse.position); + tool_data.path = Some(document.get_path_for_new_layer()); + tool_data.snap_handler.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); + tool_data.snap_handler.add_all_document_handles(document, &[], &[]); + let snapped_position = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); // Get the position and set properties let start_position = transform.inverse().transform_point2(snapped_position); - data.weight = tool_options.line_weight; + tool_data.weight = tool_options.line_weight; // Create the initial shape with a `bez_path` (only contains a moveto initially) - if let Some(layer_path) = &data.path { - data.bez_path = start_bez_path(start_position); + if let Some(layer_path) = &tool_data.path { + tool_data.bez_path = start_bez_path(start_position); responses.push_back( Operation::AddShape { path: layer_path.clone(), transform: transform.to_cols_array(), insert_index: -1, - bez_path: data.bez_path.clone().into_iter().collect(), - style: style::PathStyle::new(Some(style::Stroke::new(tool_data.primary_color, data.weight)), style::Fill::None), + bez_path: tool_data.bez_path.clone().into_iter().collect(), + style: style::PathStyle::new(Some(style::Stroke::new(global_tool_data.primary_color, tool_data.weight)), style::Fill::None), closed: false, } .into(), ); } - add_to_curve(data, input, transform, document, responses); + add_to_curve(tool_data, input, transform, document, responses); Drawing } (Drawing, DragStart) => { - data.drag_start_position = input.mouse.position; - add_to_curve(data, input, transform, document, responses); + tool_data.drag_start_position = input.mouse.position; + add_to_curve(tool_data, input, transform, document, responses); Drawing } (Drawing, DragStop) => { // Deselect everything (this means we are no longer dragging the handle) - data.shape_editor.deselect_all(responses); + tool_data.shape_editor.deselect_all(responses); // If the drag does not exceed the threshold, then replace the curve with a line - if data.drag_start_position.distance(input.mouse.position) < CREATE_CURVE_THRESHOLD { + if tool_data.drag_start_position.distance(input.mouse.position) < CREATE_CURVE_THRESHOLD { // Modify the second to last element (as we have an unplaced element tracing to the cursor as the last element) - let replace_index = data.bez_path.len() - 2; - let line_from_curve = convert_curve_to_line(data.bez_path[replace_index]); - replace_path_element(data, transform, replace_index, line_from_curve, responses); + let replace_index = tool_data.bez_path.len() - 2; + let line_from_curve = convert_curve_to_line(tool_data.bez_path[replace_index]); + replace_path_element(tool_data, transform, replace_index, line_from_curve, responses); } // Reselect the last point - if let Some(last_anchor) = data.shape_editor.select_last_anchor() { + if let Some(last_anchor) = tool_data.shape_editor.select_last_anchor() { last_anchor.select_point(ControlPointType::Anchor as usize, true, responses); } // Move the newly selected points to the cursor - let snapped_position = data.snap_handler.snap_position(responses, document, input.mouse.position); - data.shape_editor.move_selected_points(snapped_position, false, responses); + let snapped_position = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); + tool_data.shape_editor.move_selected_points(snapped_position, false, responses); Drawing } (Drawing, PointerMove) => { // Move selected points - let snapped_position = data.snap_handler.snap_position(responses, document, input.mouse.position); - data.shape_editor.move_selected_points(snapped_position, false, responses); + let snapped_position = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); + tool_data.shape_editor.move_selected_points(snapped_position, false, responses); Drawing } (Drawing, Confirm) | (Drawing, Abort) => { // Cleanup, we are either canceling or finished drawing - if data.bez_path.len() >= 2 { + if tool_data.bez_path.len() >= 2 { // Remove the last segment - remove_from_curve(data); - if let Some(layer_path) = &data.path { - responses.push_back(apply_bez_path(layer_path.clone(), data.bez_path.clone(), transform)); + remove_from_curve(tool_data); + if let Some(layer_path) = &tool_data.path { + responses.push_back(apply_bez_path(layer_path.clone(), tool_data.bez_path.clone(), transform)); } responses.push_back(DocumentMessage::DeselectAllLayers.into()); @@ -245,17 +243,17 @@ impl Fsm for PenToolFsmState { responses.push_back(DocumentMessage::AbortTransaction.into()); } - data.shape_editor.remove_overlays(responses); - data.shape_editor.clear_shapes_to_modify(); + tool_data.shape_editor.remove_overlays(responses); + tool_data.shape_editor.clear_shapes_to_modify(); - data.path = None; - data.snap_handler.cleanup(responses); + tool_data.path = None; + tool_data.snap_handler.cleanup(responses); Ready } (_, Abort) => { - data.shape_editor.remove_overlays(responses); - data.shape_editor.clear_shapes_to_modify(); + tool_data.shape_editor.remove_overlays(responses); + tool_data.shape_editor.clear_shapes_to_modify(); Ready } _ => self, @@ -298,56 +296,56 @@ impl Fsm for PenToolFsmState { } /// Add to the curve and select the second anchor of the last point and the newly added anchor point -fn add_to_curve(data: &mut PenToolData, input: &InputPreprocessorMessageHandler, transform: DAffine2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - // Refresh data's representation of the path - update_path_representation(data); +fn add_to_curve(tool_data: &mut PenToolData, input: &InputPreprocessorMessageHandler, transform: DAffine2, document: &DocumentMessageHandler, responses: &mut VecDeque) { + // Refresh tool_data's representation of the path + update_path_representation(tool_data); // Setup our position params - let snapped_position = data.snap_handler.snap_position(responses, document, input.mouse.position); + let snapped_position = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); let position = transform.inverse().transform_point2(snapped_position); // Add a curve to the path - if let Some(layer_path) = &data.path { + if let Some(layer_path) = &tool_data.path { // Push curve onto path let point = Point { x: position.x, y: position.y }; - data.bez_path.push(PathEl::CurveTo(point, point, point)); + tool_data.bez_path.push(PathEl::CurveTo(point, point, point)); - responses.push_back(apply_bez_path(layer_path.clone(), data.bez_path.clone(), transform)); + responses.push_back(apply_bez_path(layer_path.clone(), tool_data.bez_path.clone(), transform)); // Clear previous overlays - data.shape_editor.remove_overlays(responses); + tool_data.shape_editor.remove_overlays(responses); // Create a new `shape` from the updated `bez_path` - let bez_path = data.bez_path.clone().into_iter().collect(); - data.curve_shape = VectorShape::new(layer_path.to_vec(), transform, &bez_path, false, responses); - data.shape_editor.set_shapes_to_modify(vec![data.curve_shape.clone()]); + let bez_path = tool_data.bez_path.clone().into_iter().collect(); + tool_data.curve_shape = VectorShape::new(layer_path.to_vec(), transform, &bez_path, false, responses); + tool_data.shape_editor.set_shapes_to_modify(vec![tool_data.curve_shape.clone()]); // Select the second to last `PathEl`'s handle - data.shape_editor.set_shape_selected(0); - let handle_element = data.shape_editor.select_nth_anchor(0, -2); + tool_data.shape_editor.set_shape_selected(0); + let handle_element = tool_data.shape_editor.select_nth_anchor(0, -2); handle_element.select_point(ControlPointType::Handle2 as usize, true, responses); // Select the last `PathEl`'s anchor point - if let Some(last_anchor) = data.shape_editor.select_last_anchor() { + if let Some(last_anchor) = tool_data.shape_editor.select_last_anchor() { last_anchor.select_point(ControlPointType::Anchor as usize, true, responses); } - data.shape_editor.set_selected_mirror_options(true, true); + tool_data.shape_editor.set_selected_mirror_options(true, true); } } /// Replace a `PathEl` with another inside of `bez_path` by index -fn replace_path_element(data: &mut PenToolData, transform: DAffine2, replace_index: usize, replacement: PathEl, responses: &mut VecDeque) { - data.bez_path[replace_index] = replacement; - if let Some(layer_path) = &data.path { - responses.push_back(apply_bez_path(layer_path.clone(), data.bez_path.clone(), transform)); +fn replace_path_element(tool_data: &mut PenToolData, transform: DAffine2, replace_index: usize, replacement: PathEl, responses: &mut VecDeque) { + tool_data.bez_path[replace_index] = replacement; + if let Some(layer_path) = &tool_data.path { + responses.push_back(apply_bez_path(layer_path.clone(), tool_data.bez_path.clone(), transform)); } } /// Remove a curve from the end of the `bez_path` -fn remove_from_curve(data: &mut PenToolData) { - // Refresh data's representation of the path - update_path_representation(data); - data.bez_path.pop(); +fn remove_from_curve(tool_data: &mut PenToolData) { + // Refresh tool_data's representation of the path + update_path_representation(tool_data); + tool_data.bez_path.pop(); } /// Create the initial moveto for the `bez_path` @@ -366,13 +364,13 @@ fn convert_curve_to_line(curve: PathEl) -> PathEl { } } -/// Update data's version of `bez_path` to match `ShapeEditor`'s version -fn update_path_representation(data: &mut PenToolData) { +/// Update tool_data's version of `bez_path` to match `ShapeEditor`'s version +fn update_path_representation(tool_data: &mut PenToolData) { // TODO Update ShapeEditor to provide similar functionality // We need to make sure we have the most up-to-date bez_path - if !data.shape_editor.shapes_to_modify.is_empty() { + if !tool_data.shape_editor.shapes_to_modify.is_empty() { // Hacky way of saving the curve changes - data.bez_path = data.shape_editor.shapes_to_modify[0].bez_path.elements().to_vec(); + tool_data.bez_path = tool_data.shape_editor.shapes_to_modify[0].bez_path.elements().to_vec(); } } diff --git a/editor/src/viewport_tools/tools/rectangle_tool.rs b/editor/src/viewport_tools/tools/rectangle_tool.rs index 5393aad2f8..a19d6144a5 100644 --- a/editor/src/viewport_tools/tools/rectangle_tool.rs +++ b/editor/src/viewport_tools/tools/rectangle_tool.rs @@ -1,13 +1,11 @@ use super::shared::resize::Resize; use crate::consts::DRAG_THRESHOLD; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::PropertyHolder; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use graphene::layers::style; use graphene::Operation; @@ -18,12 +16,12 @@ use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct RectangleTool { fsm_state: RectangleToolFsmState, - data: RectangleToolData, + tool_data: RectangleToolData, } #[remain::sorted] #[impl_message(Message, ToolMessage, Rectangle)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum RectangleToolMessage { // Standard messages #[remain::unsorted] @@ -41,7 +39,7 @@ pub enum RectangleToolMessage { impl PropertyHolder for RectangleTool {} impl<'a> MessageHandler> for RectangleTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -52,7 +50,7 @@ impl<'a> MessageHandler> for RectangleToo return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &(), responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -94,22 +92,20 @@ impl Fsm for RectangleToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, _tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use RectangleToolFsmState::*; use RectangleToolMessage::*; - let mut shape_data = &mut data.data; + let mut shape_data = &mut tool_data.data; if let ToolMessage::Rectangle(event) = event { match (self, event) { (Ready, DragStart) => { - shape_data.start(responses, document, input.mouse.position); + shape_data.start(responses, document, input.mouse.position, font_cache); responses.push_back(DocumentMessage::StartTransaction.into()); shape_data.path = Some(document.get_path_for_new_layer()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); @@ -119,7 +115,7 @@ impl Fsm for RectangleToolFsmState { path: shape_data.path.clone().unwrap(), insert_index: -1, transform: DAffine2::ZERO.to_cols_array(), - style: style::PathStyle::new(None, style::Fill::solid(tool_data.primary_color)), + style: style::PathStyle::new(None, style::Fill::solid(global_tool_data.primary_color)), } .into(), ); diff --git a/editor/src/viewport_tools/tools/select_tool.rs b/editor/src/viewport_tools/tools/select_tool.rs index d5ce0eab23..be3886b4cc 100644 --- a/editor/src/viewport_tools/tools/select_tool.rs +++ b/editor/src/viewport_tools/tools/select_tool.rs @@ -1,16 +1,14 @@ use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE}; use crate::document::transformation::Selected; use crate::document::utility_types::{AlignAggregate, AlignAxis, FlipAxis}; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; use crate::input::mouse::ViewportPosition; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::{IconButton, LayoutRow, PopoverButton, PropertyHolder, Separator, SeparatorDirection, SeparatorType, Widget, WidgetCallback, WidgetHolder, WidgetLayout}; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; use crate::viewport_tools::snapping::{self, SnapHandler}; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData, ToolType}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData, ToolType}; use graphene::boolean_ops::BooleanOperation; use graphene::document::Document; use graphene::intersection::Quad; @@ -26,12 +24,12 @@ use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct SelectTool { fsm_state: SelectToolFsmState, - data: SelectToolData, + tool_data: SelectToolData, } #[remain::sorted] #[impl_message(Message, ToolMessage, Select)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum SelectToolMessage { // Standard messages #[remain::unsorted] @@ -231,7 +229,7 @@ impl PropertyHolder for SelectTool { } impl<'a> MessageHandler> for SelectTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -242,7 +240,7 @@ impl<'a> MessageHandler> for SelectTool { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &(), responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -310,11 +308,9 @@ impl Fsm for SelectToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - _tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, _global_tool_data, input, font_cache): ToolActionHandlerData, _tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use SelectToolFsmState::*; @@ -324,7 +320,7 @@ impl Fsm for SelectToolFsmState { match (self, event) { (_, DocumentIsDirty) => { let mut buffer = Vec::new(); - match (document.selected_visible_layers_bounding_box(), data.bounding_box_overlays.take()) { + match (document.selected_visible_layers_bounding_box(font_cache), tool_data.bounding_box_overlays.take()) { (None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(&mut buffer), (Some(bounds), paths) => { let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(&mut buffer)); @@ -334,13 +330,13 @@ impl Fsm for SelectToolFsmState { bounding_box_overlays.transform(&mut buffer); - data.bounding_box_overlays = Some(bounding_box_overlays); + tool_data.bounding_box_overlays = Some(bounding_box_overlays); } (_, _) => {} }; buffer.into_iter().rev().for_each(|message| responses.push_front(message)); - data.path_outlines.update_selected(document.selected_visible_layers(), document, responses); + tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, font_cache); self } @@ -349,7 +345,12 @@ impl Fsm for SelectToolFsmState { let tolerance = DVec2::splat(SELECTION_TOLERANCE); let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]); - if let Some(Ok(intersect)) = document.graphene_document.intersects_quad_root(quad).last().map(|path| document.graphene_document.layer(path)) { + if let Some(Ok(intersect)) = document + .graphene_document + .intersects_quad_root(quad, font_cache) + .last() + .map(|path| document.graphene_document.layer(path)) + { match intersect.data { LayerDataType::Text(_) => { responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Text }.into()); @@ -365,13 +366,13 @@ impl Fsm for SelectToolFsmState { self } (Ready, DragStart { add_to_selection }) => { - data.path_outlines.clear_hovered(responses); + tool_data.path_outlines.clear_hovered(responses); - data.drag_start = input.mouse.position; - data.drag_current = input.mouse.position; + tool_data.drag_start = input.mouse.position; + tool_data.drag_current = input.mouse.position; let mut buffer = Vec::new(); - let dragging_bounds = if let Some(bounding_box) = &mut data.bounding_box_overlays { + let dragging_bounds = if let Some(bounding_box) = &mut tool_data.bounding_box_overlays { let edges = bounding_box.check_selected_edges(input.mouse.position); bounding_box.selected_edges = edges.map(|(top, bottom, left, right)| { @@ -385,15 +386,15 @@ impl Fsm for SelectToolFsmState { None }; - let rotating_bounds = if let Some(bounding_box) = &mut data.bounding_box_overlays { + let rotating_bounds = if let Some(bounding_box) = &mut tool_data.bounding_box_overlays { bounding_box.check_rotate(input.mouse.position) } else { false }; let mut selected: Vec<_> = document.selected_visible_layers().map(|path| path.to_vec()).collect(); - let quad = data.selection_quad(); - let mut intersection = document.graphene_document.intersects_quad_root(quad); + let quad = tool_data.selection_quad(); + let mut intersection = document.graphene_document.intersects_quad_root(quad, font_cache); // If the user is dragging the bounding box bounds, go into ResizingBounds mode. // If the user is dragging the rotate trigger, go into RotatingBounds mode. // If the user clicks on a layer that is in their current selection, go into the dragging mode. @@ -403,46 +404,52 @@ impl Fsm for SelectToolFsmState { let snap_x = selected_edges.2 || selected_edges.3; let snap_y = selected_edges.0 || selected_edges.1; - data.snap_handler.start_snap(document, document.bounding_boxes(Some(&selected), None), snap_x, snap_y); - data.snap_handler.add_all_document_handles(document, &[], &selected.iter().map(|x| x.as_slice()).collect::>()); + tool_data.snap_handler.start_snap(document, document.bounding_boxes(Some(&selected), None, font_cache), snap_x, snap_y); + tool_data + .snap_handler + .add_all_document_handles(document, &[], &selected.iter().map(|x| x.as_slice()).collect::>()); - data.layers_dragging = selected; + tool_data.layers_dragging = selected; ResizingBounds } else if rotating_bounds { - if let Some(bounds) = &mut data.bounding_box_overlays { + if let Some(bounds) = &mut tool_data.bounding_box_overlays { let selected = selected.iter().collect::>(); let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.pivot, &selected, responses, &document.graphene_document); - *selected.pivot = selected.calculate_pivot(&document.graphene_document.font_cache); + *selected.pivot = selected.calculate_pivot(font_cache); } - data.layers_dragging = selected; + tool_data.layers_dragging = selected; RotatingBounds } else if selected.iter().any(|path| intersection.contains(path)) { buffer.push(DocumentMessage::StartTransaction.into()); - data.layers_dragging = selected; + tool_data.layers_dragging = selected; - data.snap_handler.start_snap(document, document.bounding_boxes(Some(&data.layers_dragging), None), true, true); + tool_data + .snap_handler + .start_snap(document, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true); Dragging } else { if !input.keyboard.get(add_to_selection as usize) { buffer.push(DocumentMessage::DeselectAllLayers.into()); - data.layers_dragging.clear(); + tool_data.layers_dragging.clear(); } if let Some(intersection) = intersection.pop() { selected = vec![intersection]; buffer.push(DocumentMessage::AddSelectedLayers { additional_layers: selected.clone() }.into()); buffer.push(DocumentMessage::StartTransaction.into()); - data.layers_dragging.append(&mut selected); - data.snap_handler.start_snap(document, document.bounding_boxes(Some(&data.layers_dragging), None), true, true); + tool_data.layers_dragging.append(&mut selected); + tool_data + .snap_handler + .start_snap(document, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true); Dragging } else { - data.drag_box_overlay_layer = Some(add_bounding_box(&mut buffer)); + tool_data.drag_box_overlay_layer = Some(add_bounding_box(&mut buffer)); DrawingBox } }; @@ -454,20 +461,20 @@ impl Fsm for SelectToolFsmState { // TODO: This is a cheat. Break out the relevant functionality from the handler above and call it from there and here. responses.push_front(SelectToolMessage::DocumentIsDirty.into()); - let mouse_position = axis_align_drag(input.keyboard.get(axis_align as usize), input.mouse.position, data.drag_start); + let mouse_position = axis_align_drag(input.keyboard.get(axis_align as usize), input.mouse.position, tool_data.drag_start); - let mouse_delta = mouse_position - data.drag_current; + let mouse_delta = mouse_position - tool_data.drag_current; - let snap = data + let snap = tool_data .layers_dragging .iter() - .filter_map(|path| document.graphene_document.viewport_bounding_box(path).ok()?) + .filter_map(|path| document.graphene_document.viewport_bounding_box(path, font_cache).ok()?) .flat_map(snapping::expand_bounds) .collect(); - let closest_move = data.snap_handler.snap_layers(responses, document, snap, mouse_delta); + let closest_move = tool_data.snap_handler.snap_layers(responses, document, snap, mouse_delta); // TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481 - for path in Document::shallowest_unique_layers(data.layers_dragging.iter()) { + for path in Document::shallowest_unique_layers(tool_data.layers_dragging.iter()) { responses.push_front( Operation::TransformLayerInViewport { path: path.clone(), @@ -476,22 +483,22 @@ impl Fsm for SelectToolFsmState { .into(), ); } - data.drag_current = mouse_position + closest_move; + tool_data.drag_current = mouse_position + closest_move; Dragging } (ResizingBounds, PointerMove { axis_align, center, .. }) => { - if let Some(bounds) = &mut data.bounding_box_overlays { + if let Some(bounds) = &mut tool_data.bounding_box_overlays { if let Some(movement) = &mut bounds.selected_edges { let (center, axis_align) = (input.keyboard.get(center as usize), input.keyboard.get(axis_align as usize)); let mouse_position = input.mouse.position; - let snapped_mouse_position = data.snap_handler.snap_position(responses, document, mouse_position); + let snapped_mouse_position = tool_data.snap_handler.snap_position(responses, document, mouse_position); let [_position, size] = movement.new_size(snapped_mouse_position, bounds.transform, center, axis_align); let delta = movement.bounds_to_scale_transform(center, size); - let selected = data.layers_dragging.iter().collect::>(); + let selected = tool_data.layers_dragging.iter().collect::>(); let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.pivot, &selected, responses, &document.graphene_document); selected.update_transforms(delta); @@ -500,9 +507,9 @@ impl Fsm for SelectToolFsmState { ResizingBounds } (RotatingBounds, PointerMove { snap_angle, .. }) => { - if let Some(bounds) = &mut data.bounding_box_overlays { + if let Some(bounds) = &mut tool_data.bounding_box_overlays { let angle = { - let start_offset = data.drag_start - bounds.pivot; + let start_offset = tool_data.drag_start - bounds.pivot; let end_offset = input.mouse.position - bounds.pivot; start_offset.angle_between(end_offset) @@ -517,7 +524,7 @@ impl Fsm for SelectToolFsmState { let delta = DAffine2::from_angle(snapped_angle); - let selected = data.layers_dragging.iter().collect::>(); + let selected = tool_data.layers_dragging.iter().collect::>(); let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.pivot, &selected, responses, &document.graphene_document); selected.update_transforms(delta); @@ -526,13 +533,13 @@ impl Fsm for SelectToolFsmState { RotatingBounds } (DrawingBox, PointerMove { .. }) => { - data.drag_current = input.mouse.position; + tool_data.drag_current = input.mouse.position; responses.push_front( DocumentMessage::Overlays( Operation::SetLayerTransformInViewport { - path: data.drag_box_overlay_layer.clone().unwrap(), - transform: transform_from_box(data.drag_start, data.drag_current, DAffine2::IDENTITY).to_cols_array(), + path: tool_data.drag_box_overlay_layer.clone().unwrap(), + transform: transform_from_box(tool_data.drag_start, tool_data.drag_current, DAffine2::IDENTITY).to_cols_array(), } .into(), ) @@ -541,73 +548,73 @@ impl Fsm for SelectToolFsmState { DrawingBox } (Ready, PointerMove { .. }) => { - let cursor = data.bounding_box_overlays.as_ref().map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, true)); + let cursor = tool_data.bounding_box_overlays.as_ref().map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, true)); // Generate the select outline (but not if the user is going to use the bound overlays) if cursor == MouseCursorIcon::Default { // Get the layer the user is hovering over let tolerance = DVec2::splat(SELECTION_TOLERANCE); let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]); - let mut intersection = document.graphene_document.intersects_quad_root(quad); + let mut intersection = document.graphene_document.intersects_quad_root(quad, font_cache); // If the user is hovering over a layer they have not already selected, then update outline if let Some(path) = intersection.pop() { if !document.selected_visible_layers().any(|visible| visible == path.as_slice()) { - data.path_outlines.update_hovered(path, document, responses) + tool_data.path_outlines.update_hovered(path, document, responses, font_cache) } else { - data.path_outlines.clear_hovered(responses); + tool_data.path_outlines.clear_hovered(responses); } } else { - data.path_outlines.clear_hovered(responses); + tool_data.path_outlines.clear_hovered(responses); } } else { - data.path_outlines.clear_hovered(responses); + tool_data.path_outlines.clear_hovered(responses); } - if data.cursor != cursor { - data.cursor = cursor; + if tool_data.cursor != cursor { + tool_data.cursor = cursor; responses.push_back(FrontendMessage::UpdateMouseCursor { cursor }.into()); } Ready } (Dragging, DragStop) => { - let response = match input.mouse.position.distance(data.drag_start) < 10. * f64::EPSILON { + let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON { true => DocumentMessage::Undo, false => DocumentMessage::CommitTransaction, }; - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); responses.push_front(response.into()); Ready } (ResizingBounds, DragStop) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); - if let Some(bounds) = &mut data.bounding_box_overlays { + if let Some(bounds) = &mut tool_data.bounding_box_overlays { bounds.original_transforms.clear(); } Ready } (RotatingBounds, DragStop) => { - if let Some(bounds) = &mut data.bounding_box_overlays { + if let Some(bounds) = &mut tool_data.bounding_box_overlays { bounds.original_transforms.clear(); } Ready } (DrawingBox, DragStop) => { - let quad = data.selection_quad(); + let quad = tool_data.selection_quad(); responses.push_front( DocumentMessage::AddSelectedLayers { - additional_layers: document.graphene_document.intersects_quad_root(quad), + additional_layers: document.graphene_document.intersects_quad_root(quad, font_cache), } .into(), ); responses.push_front( DocumentMessage::Overlays( Operation::DeleteLayer { - path: data.drag_box_overlay_layer.take().unwrap(), + path: tool_data.drag_box_overlay_layer.take().unwrap(), } .into(), ) @@ -616,19 +623,19 @@ impl Fsm for SelectToolFsmState { Ready } (Dragging, Abort) => { - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); responses.push_back(DocumentMessage::Undo.into()); - data.path_outlines.clear_selected(responses); + tool_data.path_outlines.clear_selected(responses); Ready } (_, Abort) => { - if let Some(path) = data.drag_box_overlay_layer.take() { + if let Some(path) = tool_data.drag_box_overlay_layer.take() { responses.push_front(DocumentMessage::Overlays(Operation::DeleteLayer { path }.into()).into()) }; - if let Some(mut bounding_box_overlays) = data.bounding_box_overlays.take() { - let selected = data.layers_dragging.iter().collect::>(); + if let Some(mut bounding_box_overlays) = tool_data.bounding_box_overlays.take() { + let selected = tool_data.layers_dragging.iter().collect::>(); let mut selected = Selected::new( &mut bounding_box_overlays.original_transforms, &mut bounding_box_overlays.pivot, @@ -642,10 +649,10 @@ impl Fsm for SelectToolFsmState { bounding_box_overlays.delete(responses); } - data.path_outlines.clear_hovered(responses); - data.path_outlines.clear_selected(responses); + tool_data.path_outlines.clear_hovered(responses); + tool_data.path_outlines.clear_selected(responses); - data.snap_handler.cleanup(responses); + tool_data.snap_handler.cleanup(responses); Ready } (_, Align { axis, aggregate }) => { diff --git a/editor/src/viewport_tools/tools/shape_tool.rs b/editor/src/viewport_tools/tools/shape_tool.rs index 01b6c229cc..1ffd04bba7 100644 --- a/editor/src/viewport_tools/tools/shape_tool.rs +++ b/editor/src/viewport_tools/tools/shape_tool.rs @@ -1,13 +1,11 @@ use super::shared::resize::Resize; use crate::consts::DRAG_THRESHOLD; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout}; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; use graphene::layers::style; use graphene::Operation; @@ -18,7 +16,7 @@ use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct ShapeTool { fsm_state: ShapeToolFsmState, - data: ShapeToolData, + tool_data: ShapeToolData, options: ShapeOptions, } @@ -34,7 +32,7 @@ impl Default for ShapeOptions { #[remain::sorted] #[impl_message(Message, ToolMessage, Shape)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum ShapeToolMessage { // Standard messages #[remain::unsorted] @@ -51,7 +49,7 @@ pub enum ShapeToolMessage { } #[remain::sorted] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum ShapeOptionsUpdate { Vertices(u32), } @@ -73,7 +71,7 @@ impl PropertyHolder for ShapeTool { } impl<'a> MessageHandler> for ShapeTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -91,7 +89,7 @@ impl<'a> MessageHandler> for ShapeTool { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &self.options, responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -134,34 +132,32 @@ impl Fsm for ShapeToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use ShapeToolFsmState::*; use ShapeToolMessage::*; - let mut shape_data = &mut data.data; + let mut shape_data = &mut tool_data.data; if let ToolMessage::Shape(event) = event { match (self, event) { (Ready, DragStart) => { - shape_data.start(responses, document, input.mouse.position); + shape_data.start(responses, document, input.mouse.position, font_cache); responses.push_back(DocumentMessage::StartTransaction.into()); shape_data.path = Some(document.get_path_for_new_layer()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); - data.sides = tool_options.vertices; + tool_data.sides = tool_options.vertices; responses.push_back( Operation::AddNgon { path: shape_data.path.clone().unwrap(), insert_index: -1, transform: DAffine2::ZERO.to_cols_array(), - sides: data.sides, - style: style::PathStyle::new(None, style::Fill::solid(tool_data.primary_color)), + sides: tool_data.sides, + style: style::PathStyle::new(None, style::Fill::solid(global_tool_data.primary_color)), } .into(), ); diff --git a/editor/src/viewport_tools/tools/shared/path_outline.rs b/editor/src/viewport_tools/tools/shared/path_outline.rs index 44754ecbb3..9766d631a8 100644 --- a/editor/src/viewport_tools/tools/shared/path_outline.rs +++ b/editor/src/viewport_tools/tools/shared/path_outline.rs @@ -4,6 +4,7 @@ use crate::message_prelude::*; use graphene::layers::layer_info::LayerDataType; use graphene::layers::style::{self, Fill, Stroke}; +use graphene::layers::text_layer::FontCache; use graphene::{LayerId, Operation}; use glam::DAffine2; @@ -20,16 +21,22 @@ pub struct PathOutline { impl PathOutline { /// Creates an outline of a layer either with a pre-existing overlay or by generating a new one - fn create_outline(document_layer_path: Vec, overlay_path: Option>, document: &DocumentMessageHandler, responses: &mut VecDeque) -> Option> { + fn create_outline( + document_layer_path: Vec, + overlay_path: Option>, + document: &DocumentMessageHandler, + responses: &mut VecDeque, + font_cache: &FontCache, + ) -> Option> { // Get layer data let document_layer = document.graphene_document.layer(&document_layer_path).ok()?; // Get the bezpath from the shape or text let path = match &document_layer.data { LayerDataType::Shape(shape) => Some(shape.path.clone()), - LayerDataType::Text(text) => Some(text.to_bez_path_nonmut(&document.graphene_document.font_cache)), + LayerDataType::Text(text) => Some(text.to_bez_path_nonmut(font_cache)), _ => document_layer - .aabounding_box_for_transform(DAffine2::IDENTITY, &document.graphene_document.font_cache) + .aabounding_box_for_transform(DAffine2::IDENTITY, font_cache) .map(|bounds| kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y).to_path(0.)), }?; @@ -78,10 +85,10 @@ impl PathOutline { } /// Updates the overlay, generating a new one if necessary - pub fn update_hovered(&mut self, new_layer_path: Vec, document: &DocumentMessageHandler, responses: &mut VecDeque) { + pub fn update_hovered(&mut self, new_layer_path: Vec, document: &DocumentMessageHandler, responses: &mut VecDeque, font_cache: &FontCache) { // Check if we are hovering over a different layer than before if self.hovered_layer_path.as_ref().map_or(true, |old| &new_layer_path != old) { - self.hovered_overlay_path = Self::create_outline(new_layer_path.clone(), self.hovered_overlay_path.take(), document, responses); + self.hovered_overlay_path = Self::create_outline(new_layer_path.clone(), self.hovered_overlay_path.take(), document, responses, font_cache); if self.hovered_overlay_path.is_none() { self.clear_hovered(responses); } @@ -98,11 +105,11 @@ impl PathOutline { } /// Updates the selected overlays, generating or removing overlays if necessary - pub fn update_selected<'a>(&mut self, selected: impl Iterator, document: &DocumentMessageHandler, responses: &mut VecDeque) { + pub fn update_selected<'a>(&mut self, selected: impl Iterator, document: &DocumentMessageHandler, responses: &mut VecDeque, font_cache: &FontCache) { let mut old_overlay_paths = std::mem::take(&mut self.selected_overlay_paths); for document_layer_path in selected { - if let Some(overlay_path) = Self::create_outline(document_layer_path.to_vec(), old_overlay_paths.pop(), document, responses) { + if let Some(overlay_path) = Self::create_outline(document_layer_path.to_vec(), old_overlay_paths.pop(), document, responses, font_cache) { self.selected_overlay_paths.push(overlay_path); } } diff --git a/editor/src/viewport_tools/tools/shared/resize.rs b/editor/src/viewport_tools/tools/shared/resize.rs index 3500a87fd3..a20d25095e 100644 --- a/editor/src/viewport_tools/tools/shared/resize.rs +++ b/editor/src/viewport_tools/tools/shared/resize.rs @@ -5,6 +5,7 @@ use crate::input::InputPreprocessorMessageHandler; use crate::message_prelude::*; use crate::viewport_tools::snapping::SnapHandler; +use graphene::layers::text_layer::FontCache; use graphene::Operation; use glam::{DAffine2, DVec2, Vec2Swizzles}; @@ -18,8 +19,8 @@ pub struct Resize { impl Resize { /// Starts a resize, assigning the snap targets and snapping the starting position. - pub fn start(&mut self, responses: &mut VecDeque, document: &DocumentMessageHandler, mouse_position: DVec2) { - self.snap_handler.start_snap(document, document.bounding_boxes(None, None), true, true); + pub fn start(&mut self, responses: &mut VecDeque, document: &DocumentMessageHandler, mouse_position: DVec2, font_cache: &FontCache) { + self.snap_handler.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); self.snap_handler.add_all_document_handles(document, &[], &[]); self.drag_start = self.snap_handler.snap_position(responses, document, mouse_position); } diff --git a/editor/src/viewport_tools/tools/spline_tool.rs b/editor/src/viewport_tools/tools/spline_tool.rs index bf53a22472..6601b920ce 100644 --- a/editor/src/viewport_tools/tools/spline_tool.rs +++ b/editor/src/viewport_tools/tools/spline_tool.rs @@ -1,8 +1,6 @@ use crate::consts::DRAG_THRESHOLD; -use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout}; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; @@ -18,7 +16,7 @@ use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct SplineTool { fsm_state: SplineToolFsmState, - data: SplineToolData, + tool_data: SplineToolData, options: SplineOptions, } @@ -78,7 +76,7 @@ impl PropertyHolder for SplineTool { } impl<'a> MessageHandler> for SplineTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -96,7 +94,7 @@ impl<'a> MessageHandler> for SplineTool { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &self.options, responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -136,11 +134,9 @@ impl Fsm for SplineToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use SplineToolFsmState::*; @@ -153,62 +149,62 @@ impl Fsm for SplineToolFsmState { (Ready, DragStart) => { responses.push_back(DocumentMessage::StartTransaction.into()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); - data.path = Some(document.get_path_for_new_layer()); + tool_data.path = Some(document.get_path_for_new_layer()); - data.snap_handler.start_snap(document, document.bounding_boxes(None, None), true, true); - data.snap_handler.add_all_document_handles(document, &[], &[]); - let snapped_position = data.snap_handler.snap_position(responses, document, input.mouse.position); + tool_data.snap_handler.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); + tool_data.snap_handler.add_all_document_handles(document, &[], &[]); + let snapped_position = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); let pos = transform.inverse().transform_point2(snapped_position); - data.points.push(pos); - data.next_point = pos; + tool_data.points.push(pos); + tool_data.next_point = pos; - data.weight = tool_options.line_weight; + tool_data.weight = tool_options.line_weight; - responses.push_back(add_spline(data, tool_data, true)); + responses.push_back(add_spline(tool_data, global_tool_data, true)); Drawing } (Drawing, DragStop) => { - let snapped_position = data.snap_handler.snap_position(responses, document, input.mouse.position); + let snapped_position = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); let pos = transform.inverse().transform_point2(snapped_position); - if let Some(last_pos) = data.points.last() { + if let Some(last_pos) = tool_data.points.last() { if last_pos.distance(pos) > DRAG_THRESHOLD { - data.points.push(pos); - data.next_point = pos; + tool_data.points.push(pos); + tool_data.next_point = pos; } } - responses.push_back(remove_preview(data)); - responses.push_back(add_spline(data, tool_data, true)); + responses.push_back(remove_preview(tool_data)); + responses.push_back(add_spline(tool_data, global_tool_data, true)); Drawing } (Drawing, PointerMove) => { - let snapped_position = data.snap_handler.snap_position(responses, document, input.mouse.position); + let snapped_position = tool_data.snap_handler.snap_position(responses, document, input.mouse.position); let pos = transform.inverse().transform_point2(snapped_position); - data.next_point = pos; + tool_data.next_point = pos; - responses.push_back(remove_preview(data)); - responses.push_back(add_spline(data, tool_data, true)); + responses.push_back(remove_preview(tool_data)); + responses.push_back(add_spline(tool_data, global_tool_data, true)); Drawing } (Drawing, Confirm) | (Drawing, Abort) => { - if data.points.len() >= 2 { + if tool_data.points.len() >= 2 { responses.push_back(DocumentMessage::DeselectAllLayers.into()); - responses.push_back(remove_preview(data)); - responses.push_back(add_spline(data, tool_data, false)); + responses.push_back(remove_preview(tool_data)); + responses.push_back(add_spline(tool_data, global_tool_data, false)); responses.push_back(DocumentMessage::CommitTransaction.into()); } else { responses.push_back(DocumentMessage::AbortTransaction.into()); } - data.path = None; - data.points.clear(); - data.snap_handler.cleanup(responses); + tool_data.path = None; + tool_data.points.clear(); + tool_data.snap_handler.cleanup(responses); Ready } @@ -251,22 +247,25 @@ impl Fsm for SplineToolFsmState { } } -fn remove_preview(data: &SplineToolData) -> Message { - Operation::DeleteLayer { path: data.path.clone().unwrap() }.into() +fn remove_preview(tool_data: &SplineToolData) -> Message { + Operation::DeleteLayer { + path: tool_data.path.clone().unwrap(), + } + .into() } -fn add_spline(data: &SplineToolData, tool_data: &DocumentToolData, show_preview: bool) -> Message { - let mut points: Vec<(f64, f64)> = data.points.iter().map(|p| (p.x, p.y)).collect(); +fn add_spline(tool_data: &SplineToolData, global_tool_data: &DocumentToolData, show_preview: bool) -> Message { + let mut points: Vec<(f64, f64)> = tool_data.points.iter().map(|p| (p.x, p.y)).collect(); if show_preview { - points.push((data.next_point.x, data.next_point.y)) + points.push((tool_data.next_point.x, tool_data.next_point.y)) } Operation::AddSpline { - path: data.path.clone().unwrap(), + path: tool_data.path.clone().unwrap(), insert_index: -1, transform: DAffine2::IDENTITY.to_cols_array(), points, - style: style::PathStyle::new(Some(style::Stroke::new(tool_data.primary_color, data.weight)), style::Fill::None), + style: style::PathStyle::new(Some(style::Stroke::new(global_tool_data.primary_color, tool_data.weight)), style::Fill::None), } .into() } diff --git a/editor/src/viewport_tools/tools/text_tool.rs b/editor/src/viewport_tools/tools/text_tool.rs index df7f88f731..dae4c070a4 100644 --- a/editor/src/viewport_tools/tools/text_tool.rs +++ b/editor/src/viewport_tools/tools/text_tool.rs @@ -2,25 +2,25 @@ use crate::consts::{COLOR_ACCENT, SELECTION_TOLERANCE}; use crate::document::DocumentMessageHandler; use crate::frontend::utility_types::MouseCursorIcon; use crate::input::keyboard::{Key, MouseMotion}; -use crate::input::InputPreprocessorMessageHandler; use crate::layout::layout_message::LayoutTarget; use crate::layout::widgets::{FontInput, LayoutRow, NumberInput, PropertyHolder, Separator, SeparatorDirection, SeparatorType, Widget, WidgetCallback, WidgetHolder, WidgetLayout}; use crate::message_prelude::*; use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; -use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData}; +use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData}; -use glam::{DAffine2, DVec2}; -use graphene::document::FontCache; use graphene::intersection::Quad; use graphene::layers::style::{self, Fill, Stroke}; +use graphene::layers::text_layer::FontCache; use graphene::Operation; + +use glam::{DAffine2, DVec2}; use kurbo::Shape; use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct TextTool { fsm_state: TextToolFsmState, - data: TextToolData, + tool_data: TextToolData, options: TextOptions, } @@ -28,7 +28,6 @@ pub struct TextOptions { font_size: u32, font_name: String, font_style: String, - font_file: Option, } impl Default for TextOptions { @@ -37,14 +36,13 @@ impl Default for TextOptions { font_size: 24, font_name: "Merriweather".into(), font_style: "Normal (400)".into(), - font_file: None, } } } #[remain::sorted] #[impl_message(Message, ToolMessage, Text)] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum TextMessage { // Standard messages #[remain::unsorted] @@ -66,9 +64,9 @@ pub enum TextMessage { } #[remain::sorted] -#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum TextOptionsUpdate { - Font { family: String, style: String, file: String }, + Font { family: String, style: String }, FontSize(u32), } @@ -84,11 +82,9 @@ impl PropertyHolder for TextTool { TextMessage::UpdateOptions(TextOptionsUpdate::Font { family: font_input.font_family.clone(), style: font_input.font_style.clone(), - file: font_input.font_file_url.clone(), }) .into() }), - ..Default::default() })), WidgetHolder::new(Widget::Separator(Separator { direction: SeparatorDirection::Horizontal, @@ -102,11 +98,9 @@ impl PropertyHolder for TextTool { TextMessage::UpdateOptions(TextOptionsUpdate::Font { family: font_input.font_family.clone(), style: font_input.font_style.clone(), - file: font_input.font_file_url.clone(), }) .into() }), - ..Default::default() })), WidgetHolder::new(Widget::Separator(Separator { direction: SeparatorDirection::Horizontal, @@ -127,7 +121,7 @@ impl PropertyHolder for TextTool { } impl<'a> MessageHandler> for TextTool { - fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { + fn process_action(&mut self, action: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { if action == ToolMessage::UpdateHints { self.fsm_state.update_hints(responses); return; @@ -140,10 +134,9 @@ impl<'a> MessageHandler> for TextTool { if let ToolMessage::Text(TextMessage::UpdateOptions(action)) = action { match action { - TextOptionsUpdate::Font { family, style, file } => { + TextOptionsUpdate::Font { family, style } => { self.options.font_name = family; self.options.font_style = style; - self.options.font_file = Some(file); self.register_properties(responses, LayoutTarget::ToolOptions); } @@ -152,7 +145,7 @@ impl<'a> MessageHandler> for TextTool { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses); + let new_state = self.fsm_state.transition(action, &mut self.tool_data, tool_data, &self.options, responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -211,13 +204,13 @@ fn resize_overlays(overlays: &mut Vec>, responses: &mut VecDeque, font_cache: &FontCache) { +fn update_overlays(document: &DocumentMessageHandler, tool_data: &mut TextToolData, responses: &mut VecDeque, font_cache: &FontCache) { let visible_text_layers = document.selected_visible_text_layers().collect::>(); - resize_overlays(&mut data.overlays, responses, visible_text_layers.len()); + resize_overlays(&mut tool_data.overlays, responses, visible_text_layers.len()); let bounds = visible_text_layers .into_iter() - .zip(&data.overlays) + .zip(&tool_data.overlays) .filter_map(|(layer_path, overlay_path)| { document .graphene_document @@ -237,7 +230,7 @@ fn update_overlays(document: &DocumentMessageHandler, data: &mut TextToolData, r }; responses.push_back(DocumentMessage::Overlays(operation.into()).into()); } - resize_overlays(&mut data.overlays, responses, new_len); + resize_overlays(&mut tool_data.overlays, responses, new_len); } impl Fsm for TextToolFsmState { @@ -247,11 +240,9 @@ impl Fsm for TextToolFsmState { fn transition( self, event: ToolMessage, - document: &DocumentMessageHandler, - tool_data: &DocumentToolData, - data: &mut Self::ToolData, + tool_data: &mut Self::ToolData, + (document, global_tool_data, input, font_cache): ToolActionHandlerData, tool_options: &Self::ToolOptions, - input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, ) -> Self { use TextMessage::*; @@ -260,7 +251,7 @@ impl Fsm for TextToolFsmState { if let ToolMessage::Text(event) = event { match (self, event) { (state, DocumentIsDirty) => { - update_overlays(document, data, responses, &document.graphene_document.font_cache); + update_overlays(document, tool_data, responses, font_cache); state } @@ -271,7 +262,7 @@ impl Fsm for TextToolFsmState { let new_state = if let Some(l) = document .graphene_document - .intersects_quad_root(quad) + .intersects_quad_root(quad, font_cache) .last() .filter(|l| document.graphene_document.layer(l).map(|l| l.as_text().is_ok()).unwrap_or(false)) // Editing existing text @@ -279,25 +270,25 @@ impl Fsm for TextToolFsmState { if state == TextToolFsmState::Editing { responses.push_back( DocumentMessage::SetTexboxEditability { - path: data.path.clone(), + path: tool_data.path.clone(), editable: false, } .into(), ); } - data.path = l.clone(); + tool_data.path = l.clone(); responses.push_back( DocumentMessage::SetTexboxEditability { - path: data.path.clone(), + path: tool_data.path.clone(), editable: true, } .into(), ); responses.push_back( DocumentMessage::SetSelectedLayers { - replacement_selected_layers: vec![data.path.clone()], + replacement_selected_layers: vec![tool_data.path.clone()], } .into(), ); @@ -310,28 +301,32 @@ impl Fsm for TextToolFsmState { let font_size = tool_options.font_size; let font_name = tool_options.font_name.clone(); let font_style = tool_options.font_style.clone(); - let font_file = tool_options.font_file.clone(); - data.path = document.get_path_for_new_layer(); + tool_data.path = document.get_path_for_new_layer(); responses.push_back( Operation::AddText { - path: data.path.clone(), + path: tool_data.path.clone(), transform: DAffine2::ZERO.to_cols_array(), insert_index: -1, text: r#""#.to_string(), - style: style::PathStyle::new(None, Fill::solid(tool_data.primary_color)), + style: style::PathStyle::new(None, Fill::solid(global_tool_data.primary_color)), size: font_size as f64, font_name, font_style, - font_file, } .into(), ); - responses.push_back(Operation::SetLayerTransformInViewport { path: data.path.clone(), transform }.into()); + responses.push_back( + Operation::SetLayerTransformInViewport { + path: tool_data.path.clone(), + transform, + } + .into(), + ); responses.push_back( DocumentMessage::SetTexboxEditability { - path: data.path.clone(), + path: tool_data.path.clone(), editable: true, } .into(), @@ -339,7 +334,7 @@ impl Fsm for TextToolFsmState { responses.push_back( DocumentMessage::SetSelectedLayers { - replacement_selected_layers: vec![data.path.clone()], + replacement_selected_layers: vec![tool_data.path.clone()], } .into(), ); @@ -349,13 +344,13 @@ impl Fsm for TextToolFsmState { // Removing old text as editable responses.push_back( DocumentMessage::SetTexboxEditability { - path: data.path.clone(), + path: tool_data.path.clone(), editable: false, } .into(), ); - resize_overlays(&mut data.overlays, responses, 0); + resize_overlays(&mut tool_data.overlays, responses, 0); Ready }; @@ -366,14 +361,14 @@ impl Fsm for TextToolFsmState { if state == TextToolFsmState::Editing { responses.push_back( DocumentMessage::SetTexboxEditability { - path: data.path.clone(), + path: tool_data.path.clone(), editable: false, } .into(), ); } - resize_overlays(&mut data.overlays, responses, 0); + resize_overlays(&mut tool_data.overlays, responses, 0); Ready } @@ -383,35 +378,41 @@ impl Fsm for TextToolFsmState { Editing } (Editing, TextChange { new_text }) => { - responses.push_back(Operation::SetTextContent { path: data.path.clone(), new_text }.into()); + responses.push_back( + Operation::SetTextContent { + path: tool_data.path.clone(), + new_text, + } + .into(), + ); responses.push_back( DocumentMessage::SetTexboxEditability { - path: data.path.clone(), + path: tool_data.path.clone(), editable: false, } .into(), ); - resize_overlays(&mut data.overlays, responses, 0); + resize_overlays(&mut tool_data.overlays, responses, 0); Ready } (Editing, UpdateBounds { new_text }) => { - resize_overlays(&mut data.overlays, responses, 1); - let text = document.graphene_document.layer(&data.path).unwrap().as_text().unwrap(); - let mut path = text.bounding_box(&new_text, text.load_face(&document.graphene_document.font_cache)).to_path(0.1); + resize_overlays(&mut tool_data.overlays, responses, 1); + let text = document.graphene_document.layer(&tool_data.path).unwrap().as_text().unwrap(); + let mut path = text.bounding_box(&new_text, text.load_face(font_cache)).to_path(0.1); fn glam_to_kurbo(transform: DAffine2) -> kurbo::Affine { kurbo::Affine::new(transform.to_cols_array()) } - path.apply_affine(glam_to_kurbo(document.graphene_document.multiply_transforms(&data.path).unwrap())); + path.apply_affine(glam_to_kurbo(document.graphene_document.multiply_transforms(&tool_data.path).unwrap())); let kurbo::Rect { x0, y0, x1, y1 } = path.bounding_box(); let operation = Operation::SetLayerTransformInViewport { - path: data.overlays[0].clone(), + path: tool_data.overlays[0].clone(), transform: transform_from_box(DVec2::new(x0, y0), DVec2::new(x1, y1)), }; responses.push_back(DocumentMessage::Overlays(operation.into()).into()); diff --git a/editor/src/viewport_tools/vector_editor/constants.rs b/editor/src/viewport_tools/vector_editor/constants.rs index 26d1d1de2b..a620a29b3a 100644 --- a/editor/src/viewport_tools/vector_editor/constants.rs +++ b/editor/src/viewport_tools/vector_editor/constants.rs @@ -6,7 +6,7 @@ pub const ROUNDING_BIAS: f64 = 0.0001; pub const MINIMUM_MIRROR_THRESHOLD: f64 = 0.1; #[repr(usize)] -#[derive(PartialEq, Clone, Debug)] +#[derive(PartialEq, Eq, Clone, Debug)] pub enum ControlPointType { Anchor = 0, Handle1 = 1, diff --git a/editor/src/workspace/workspace_message.rs b/editor/src/workspace/workspace_message.rs index 903b4da0a8..49eb33abe3 100644 --- a/editor/src/workspace/workspace_message.rs +++ b/editor/src/workspace/workspace_message.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; #[remain::sorted] #[impl_message(Message, Workspace)] -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] pub enum WorkspaceMessage { // Messages NodeGraphToggleVisibility, diff --git a/frontend/src/components/widgets/inputs/DropdownInput.vue b/frontend/src/components/widgets/inputs/DropdownInput.vue index 15840f1b0e..47a677ce17 100644 --- a/frontend/src/components/widgets/inputs/DropdownInput.vue +++ b/frontend/src/components/widgets/inputs/DropdownInput.vue @@ -1,12 +1,12 @@