From 48dca4801f66c427f770dab894e574788abad2d2 Mon Sep 17 00:00:00 2001 From: seam0s Date: Sun, 6 Apr 2025 11:09:21 +0300 Subject: [PATCH 01/25] Added granular overlays control based on features --- .../portfolio/document/document_message.rs | 2 + .../document/document_message_handler.rs | 105 ++++++++++++++++-- .../overlays/overlays_message_handler.rs | 20 +++- .../document/overlays/utility_types.rs | 44 ++++++++ .../transformation_cage.rs | 4 + .../tool/tool_messages/select_tool.rs | 10 +- 6 files changed, 165 insertions(+), 20 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index a48ab8658c..798eb9021e 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -1,6 +1,7 @@ use super::utility_types::misc::{GroupFolderType, SnappingState}; use crate::messages::input_mapper::utility_types::input_keyboard::Key; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; +use crate::messages::portfolio::document::overlays::utility_types::OverlaysType; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GridSnapping}; use crate::messages::portfolio::utility_types::PanelType; @@ -143,6 +144,7 @@ pub enum DocumentMessage { }, SetOverlaysVisibility { visible: bool, + overlays_type: OverlaysType, }, SetRangeSelectionLayer { new_layer: Option, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 11b8ba85ba..cb642d664d 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -12,6 +12,7 @@ use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::NodeGraphHandlerData; use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options}; +use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings}; use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ}; @@ -84,7 +85,7 @@ pub struct DocumentMessageHandler { pub view_mode: ViewMode, /// Sets whether or not all the viewport overlays should be drawn on top of the artwork. /// This includes tool interaction visualizations (like the transform cage and path anchors/handles), the grid, and more. - pub overlays_visible: bool, + pub overlays_visibility_settings: OverlaysVisibilitySettings, /// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area. pub rulers_visible: bool, /// The current user choices for snapping behavior, including whether snapping is enabled at all. @@ -145,7 +146,7 @@ impl Default for DocumentMessageHandler { document_ptz: PTZ::default(), document_mode: DocumentMode::DesignMode, view_mode: ViewMode::default(), - overlays_visible: true, + overlays_visibility_settings: OverlaysVisibilitySettings::default(), rulers_visible: true, graph_view_overlay_open: false, snapping_state: SnappingState::default(), @@ -199,12 +200,14 @@ impl MessageHandler> for DocumentMessag self.navigation_handler.process_message(message, responses, data); } DocumentMessage::Overlays(message) => { - let overlays_visible = self.overlays_visible; + let overlays_visibility_settings = self.overlays_visibility_settings; + + // Send the overlays message to the overlays message handler self.overlays_message_handler.process_message( message, responses, OverlaysMessageData { - overlays_visible, + overlays_visibility_settings, ipp, device_pixel_ratio, }, @@ -347,6 +350,10 @@ impl MessageHandler> for DocumentMessag responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer }); } DocumentMessage::DrawArtboardOverlays(overlay_context) => { + if !overlay_context.overlays_visibility_settings.artboard_name { + return; + } + for layer in self.metadata().all_layers() { if !self.network_interface.is_artboard(&layer.to_node(), &[]) { continue; @@ -1133,8 +1140,18 @@ impl MessageHandler> for DocumentMessag responses.add(GraphOperationMessage::OpacitySet { layer, opacity }); } } - DocumentMessage::SetOverlaysVisibility { visible } => { - self.overlays_visible = visible; + DocumentMessage::SetOverlaysVisibility { visible, overlays_type } => { + match overlays_type { + OverlaysType::All => self.overlays_visibility_settings.all = visible, + OverlaysType::ArtboardName => self.overlays_visibility_settings.artboard_name = visible, + OverlaysType::CompassRose => self.overlays_visibility_settings.compass_rose = visible, + OverlaysType::Measurement => self.overlays_visibility_settings.measurement = visible, + OverlaysType::TransformCage => self.overlays_visibility_settings.transform_cage = visible, + OverlaysType::Pivot => self.overlays_visibility_settings.pivot = visible, + OverlaysType::Path => self.overlays_visibility_settings.path = visible, + OverlaysType::Anchors => self.overlays_visibility_settings.anchors = visible, + OverlaysType::Handles => self.overlays_visibility_settings.handles = visible, + } responses.add(BroadcastEvent::ToolAbort); responses.add(OverlaysMessage::Draw); } @@ -1235,8 +1252,9 @@ impl MessageHandler> for DocumentMessag responses.add(OverlaysMessage::Draw); responses.add(PortfolioMessage::UpdateDocumentWidgets); } + // TODO: ToggleOverlaysVisibility does not reflect as a good name for Overlays::All DocumentMessage::ToggleOverlaysVisibility => { - self.overlays_visible = !self.overlays_visible; + self.overlays_visibility_settings.all = !self.overlays_visibility_settings.all; responses.add(OverlaysMessage::Draw); responses.add(PortfolioMessage::UpdateDocumentWidgets); } @@ -1677,7 +1695,7 @@ impl DocumentMessageHandler { pub view_mode: ViewMode, /// Sets whether or not all the viewport overlays should be drawn on top of the artwork. /// This includes tool interaction visualizations (like the transform cage and path anchors/handles), the grid, and more. - pub overlays_visible: bool, + pub overlays_visibility_settings: OverlaysVisibilitySettings, /// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area. pub rulers_visible: bool, /// Sets whether or not the node graph is drawn (as an overlay) on top of the viewport area, or otherwise if it's hidden. @@ -1693,7 +1711,7 @@ impl DocumentMessageHandler { document_ptz: old_message_handler.document_ptz, document_mode: old_message_handler.document_mode, view_mode: old_message_handler.view_mode, - overlays_visible: old_message_handler.overlays_visible, + overlays_visibility_settings: old_message_handler.overlays_visibility_settings, rulers_visible: old_message_handler.rulers_visible, graph_view_overlay_open: old_message_handler.graph_view_overlay_open, snapping_state: old_message_handler.snapping_state, @@ -2051,11 +2069,17 @@ impl DocumentMessageHandler { .on_update(|_| AnimationMessage::ToggleLivePreview.into()) .widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder(), - CheckboxInput::new(self.overlays_visible) + CheckboxInput::new(self.overlays_visibility_settings.all) .icon("Overlays") .tooltip("Overlays") .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleOverlaysVisibility)) - .on_update(|optional_input: &CheckboxInput| DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked }.into()) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::All, + } + .into() + }) .widget_holder(), PopoverButton::new() .popover_layout(vec![ @@ -2063,7 +2087,64 @@ impl DocumentMessageHandler { widgets: vec![TextLabel::new("Overlays").bold(true).widget_holder()], }, LayoutGroup::Row { - widgets: vec![TextLabel::new("Granular settings in this menu are coming soon").widget_holder()], + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.artboard_name) + .tooltip("Enable or disable the artboard names overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::ArtboardName, + } + .into() + }) + .widget_holder(), + TextLabel::new("Artboard Name".to_string()).widget_holder(), + ], + }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.compass_rose) + .tooltip("Enable or disable the compass rose overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::CompassRose, + } + .into() + }) + .widget_holder(), + TextLabel::new("Compass Rose".to_string()).widget_holder(), + ], + }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.measurement) + .tooltip("Enable or disable the measurement overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::Measurement, + } + .into() + }) + .widget_holder(), + TextLabel::new("Measurement".to_string()).widget_holder(), + ], + }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.transform_cage) + .tooltip("Enable or disable the transform cage overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::TransformCage, + } + .into() + }) + .widget_holder(), + TextLabel::new("Transform Cage".to_string()).widget_holder(), + ], }, ]) .widget_holder(), diff --git a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs index 2ddee78dab..36a22d30a6 100644 --- a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs +++ b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs @@ -1,8 +1,9 @@ use super::utility_types::OverlayProvider; +use super::utility_types::OverlaysVisibilitySettings; use crate::messages::prelude::*; pub struct OverlaysMessageData<'a> { - pub overlays_visible: bool, + pub overlays_visibility_settings: OverlaysVisibilitySettings, pub ipp: &'a InputPreprocessorMessageHandler, pub device_pixel_ratio: f64, } @@ -18,7 +19,9 @@ pub struct OverlaysMessageHandler { impl MessageHandler> for OverlaysMessageHandler { fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque, data: OverlaysMessageData) { - let OverlaysMessageData { overlays_visible, ipp, .. } = data; + let OverlaysMessageData { + overlays_visibility_settings, ipp, .. + } = data; match message { #[cfg(target_arch = "wasm32")] @@ -50,24 +53,31 @@ impl MessageHandler> for OverlaysMessag context.clear_rect(0., 0., ipp.viewport_bounds.size().x, ipp.viewport_bounds.size().y); let _ = context.reset_transform(); - if overlays_visible { + if overlays_visibility_settings.all { responses.add(DocumentMessage::GridOverlays(OverlayContext { render_context: context.clone(), size: size.as_dvec2(), device_pixel_ratio, + overlays_visibility_settings: overlays_visibility_settings.clone(), })); for provider in &self.overlay_providers { responses.add(provider(OverlayContext { render_context: context.clone(), size: size.as_dvec2(), device_pixel_ratio, - })); + overlays_visibility_settings: overlays_visibility_settings.clone(), + }); + // debug!("OverlaysMessageHandler: {:?}", message.clone()); + responses.add(message); } } } #[cfg(not(target_arch = "wasm32"))] OverlaysMessage::Draw => { - warn!("Cannot render overlays on non-Wasm targets.\n{responses:?} {overlays_visible} {ipp:?}",); + warn!( + "Cannot render overlays on non-Wasm targets.\n{responses:?} {overlays_visibility_settings:?} {ipp:?} {:?} {:?}", + self.canvas, self.context + ); } OverlaysMessage::AddProvider(message) => { self.overlay_providers.insert(message); diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index 79cc7f20c3..7c886debc7 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -19,6 +19,49 @@ pub fn empty_provider() -> OverlayProvider { |_| Message::NoOp } +// Types of overlays used by DocumentMessage to enable/disable select group of overlays in the frontend +#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] +pub enum OverlaysType { + All, + ArtboardName, + CompassRose, + Measurement, + TransformCage, + Pivot, + Path, + Anchors, + Handles, +} + +#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct OverlaysVisibilitySettings { + pub all: bool, + pub artboard_name: bool, + pub compass_rose: bool, + pub measurement: bool, + pub transform_cage: bool, + pub pivot: bool, + pub path: bool, + pub anchors: bool, + pub handles: bool, +} + +impl Default for OverlaysVisibilitySettings { + fn default() -> Self { + Self { + all: true, + artboard_name: true, + compass_rose: true, + measurement: true, + transform_cage: true, + pivot: true, + path: true, + anchors: true, + handles: true, + } + } +} + #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] pub struct OverlayContext { // Serde functionality isn't used but is required by the message system macros @@ -29,6 +72,7 @@ pub struct OverlayContext { // The device pixel ratio is a property provided by the browser window and is the CSS pixel size divided by the physical monitor's pixel size. // It allows better pixel density of visualizations on high-DPI displays where the OS display scaling is not 100%, or where the browser is zoomed. pub device_pixel_ratio: f64, + pub overlays_visibility_settings: OverlaysVisibilitySettings, } // Message hashing isn't used but is required by the message system macros impl core::hash::Hash for OverlayContext { diff --git a/editor/src/messages/tool/common_functionality/transformation_cage.rs b/editor/src/messages/tool/common_functionality/transformation_cage.rs index 09d4097b1c..0234d9d998 100644 --- a/editor/src/messages/tool/common_functionality/transformation_cage.rs +++ b/editor/src/messages/tool/common_functionality/transformation_cage.rs @@ -575,6 +575,10 @@ impl BoundingBoxManager { /// Update the position of the bounding box and transform handles pub fn render_overlays(&mut self, overlay_context: &mut OverlayContext, render_quad: bool) { + if !overlay_context.overlays_visibility_settings.transform_cage { + return; + } + let quad = self.transform * Quad::from_box(self.bounds); let category = self.overlay_display_category(); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index fc897a9a7a..ebe9093527 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -581,8 +581,10 @@ impl Fsm for SelectToolFsmState { // Use the viewport-aligned AABBs for measurement if let (Some(selected_bounds), Some(hovered_bounds)) = (selected_bounds_viewport, hovered_bounds_viewport) { - // Since we're already in viewport space, use identity transform - measure::overlay(selected_bounds, hovered_bounds, DAffine2::IDENTITY, DAffine2::IDENTITY, &mut overlay_context); + if overlay_context.overlays_visibility_settings.measurement { + // Since we're already in viewport space, use identity transform + measure::overlay(selected_bounds, hovered_bounds, DAffine2::IDENTITY, DAffine2::IDENTITY, &mut overlay_context); + } } } } @@ -664,7 +666,9 @@ impl Fsm for SelectToolFsmState { if !matches!(self, Self::Dragging { .. }) { tool_data.line_center = compass_center; } - overlay_context.compass_rose(compass_center, angle, show_compass_with_ring); + if overlay_context.overlays_visibility_settings.compass_rose { + overlay_context.compass_rose(compass_center, angle, show_compass_with_ring); + } let axis_state = if let SelectToolFsmState::Dragging { axis, .. } = self { Some((axis, false)) From 3beb5cff6507ace3aba9b6af4dd39a1587f98ac0 Mon Sep 17 00:00:00 2001 From: seam0s Date: Wed, 9 Apr 2025 22:41:34 +0300 Subject: [PATCH 02/25] Added basic support for pivot, path, anchors and handles overlay settings --- .../document/document_message_handler.rs | 60 ++++++++++ .../document/overlays/utility_functions.rs | 81 +++++++------ .../tool/common_functionality/pivot.rs | 4 + .../tool/tool_messages/select_tool.rs | 109 +++++++++--------- 4 files changed, 166 insertions(+), 88 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index cb642d664d..b6f44540cb 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -2146,6 +2146,66 @@ impl DocumentMessageHandler { TextLabel::new("Transform Cage".to_string()).widget_holder(), ], }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.pivot) + .tooltip("Enable or disable the pivot overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::Pivot, + } + .into() + }) + .widget_holder(), + TextLabel::new("Pivot".to_string()).widget_holder(), + ], + }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.path) + .tooltip("Enable or disable the path overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::Path, + } + .into() + }) + .widget_holder(), + TextLabel::new("Path".to_string()).widget_holder(), + ], + }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.anchors) + .tooltip("Enable or disable the anchors overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::Anchors, + } + .into() + }) + .widget_holder(), + TextLabel::new("Anchors".to_string()).widget_holder(), + ], + }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.handles) + .tooltip("Enable or disable the handles overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::Handles, + } + .into() + }) + .widget_holder(), + TextLabel::new("Handles".to_string()).widget_holder(), + ], + }, ]) .widget_holder(), Separator::new(SeparatorType::Related).widget_holder(), diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index a6a77b1726..4831a086b4 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -77,7 +77,7 @@ fn overlay_bezier_handles(bezier: Bezier, segment_id: SegmentId, transform: DAff } } -pub fn overlay_bezier_handle_specific_point( +fn overlay_bezier_handle_specific_point( bezier: Bezier, segment_id: SegmentId, (start, end): (PointId, PointId), @@ -112,59 +112,72 @@ pub fn overlay_bezier_handle_specific_point( } pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandles, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext) { + let display_path = overlay_context.overlays_visibility_settings.path; + let display_handles = overlay_context.overlays_visibility_settings.handles; + let display_anchors = overlay_context.overlays_visibility_settings.anchors; + for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) { let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue }; let transform = document.metadata().transform_to_viewport(layer); - overlay_context.outline_vector(&vector_data, transform); + if display_path { + overlay_context.outline_vector(&vector_data, transform); + } let selected = shape_editor.selected_shape_state.get(&layer); let is_selected = |point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_selected(point)); - let opposite_handles_data: Vec<(PointId, SegmentId)> = shape_editor.selected_points().filter_map(|point_id| vector_data.adjacent_segment(point_id)).collect(); + if display_handles { + let opposite_handles_data: Vec<(PointId, SegmentId)> = shape_editor.selected_points().filter_map(|point_id| vector_data.adjacent_segment(point_id)).collect(); - match draw_handles { - DrawHandles::All => { - vector_data.segment_bezier_iter().for_each(|(segment_id, bezier, _start, _end)| { - overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context); - }); - } - DrawHandles::SelectedAnchors(ref selected_segments) => { - vector_data - .segment_bezier_iter() - .filter(|(segment_id, ..)| selected_segments.contains(segment_id)) - .for_each(|(segment_id, bezier, _start, _end)| { + match draw_handles { + DrawHandles::All => { + vector_data.segment_bezier_iter().for_each(|(segment_id, bezier, _start, _end)| { overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context); }); - - for (segment_id, bezier, start, end) in vector_data.segment_bezier_iter() { - if let Some((corresponding_anchor, _)) = opposite_handles_data.iter().find(|(_, adj_segment_id)| adj_segment_id == &segment_id) { - overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), *corresponding_anchor, transform, is_selected, overlay_context); - } } - } - DrawHandles::FrontierHandles(ref segment_endpoints) => { - vector_data - .segment_bezier_iter() - .filter(|(segment_id, ..)| segment_endpoints.contains_key(segment_id)) - .for_each(|(segment_id, bezier, start, end)| { - if segment_endpoints.get(&segment_id).unwrap().len() == 1 { - let point_to_render = segment_endpoints.get(&segment_id).unwrap()[0]; - overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), point_to_render, transform, is_selected, overlay_context); - } else { + DrawHandles::SelectedAnchors(ref selected_segments) => { + vector_data + .segment_bezier_iter() + .filter(|(segment_id, ..)| selected_segments.contains(segment_id)) + .for_each(|(segment_id, bezier, _start, _end)| { overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context); + }); + + for (segment_id, bezier, start, end) in vector_data.segment_bezier_iter() { + if let Some((corresponding_anchor, _)) = opposite_handles_data.iter().find(|(_, adj_segment_id)| adj_segment_id == &segment_id) { + overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), *corresponding_anchor, transform, is_selected, overlay_context); } - }); + } + } + DrawHandles::FrontierHandles(ref segment_endpoints) => { + vector_data + .segment_bezier_iter() + .filter(|(segment_id, ..)| segment_endpoints.contains_key(segment_id)) + .for_each(|(segment_id, bezier, start, end)| { + if segment_endpoints.get(&segment_id).unwrap().len() == 1 { + let point_to_render = segment_endpoints.get(&segment_id).unwrap()[0]; + overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), point_to_render, transform, is_selected, overlay_context); + } else { + overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context); + } + }); + } + DrawHandles::None => {} } - DrawHandles::None => {} } - - for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) { - overlay_context.manipulator_anchor(transform.transform_point2(position), is_selected(ManipulatorPointId::Anchor(id)), None); + if display_anchors { + for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) { + overlay_context.manipulator_anchor(transform.transform_point2(position), is_selected(ManipulatorPointId::Anchor(id)), None); + } } } } pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext, preferences: &PreferencesMessageHandler) { + if !overlay_context.overlays_visibility_settings.path { + return; + } + for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) { let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue; diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index f9efcf84c5..b8b655fb00 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -82,6 +82,10 @@ impl Pivot { } pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, draw_data: Option<(f64,)>) { + if !overlay_context.overlays_visibility_settings.pivot { + return; + } + self.recalculate_pivot(document); if let (Some(pivot), Some(data)) = (self.pivot, draw_data) { overlay_context.pivot(pivot, data.0); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index ebe9093527..170cf8a124 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -661,72 +661,73 @@ impl Fsm for SelectToolFsmState { tool_data.pivot.update_pivot(document, &mut overlay_context, Some((angle,))); // Update compass rose - tool_data.compass_rose.refresh_position(document); - let compass_center = tool_data.compass_rose.compass_rose_position(); - if !matches!(self, Self::Dragging { .. }) { - tool_data.line_center = compass_center; - } if overlay_context.overlays_visibility_settings.compass_rose { + tool_data.compass_rose.refresh_position(document); + let compass_center = tool_data.compass_rose.compass_rose_position(); + if !matches!(self, Self::Dragging { .. }) { + tool_data.line_center = compass_center; + } + overlay_context.compass_rose(compass_center, angle, show_compass_with_ring); - } - let axis_state = if let SelectToolFsmState::Dragging { axis, .. } = self { + let axis_state = if let SelectToolFsmState::Dragging { axis, .. } = self { Some((axis, false)) - } else { - compass_rose_state.axis_type().and_then(|axis| axis.is_constraint().then_some((axis, true))) - }; + } else { + compass_rose_state.axis_type().and_then(|axis| axis.is_constraint().then_some((axis, true))) + }; - if show_compass_with_ring.is_some() { - if let Some((axis, hover)) = axis_state { - if axis.is_constraint() { - let e0 = tool_data - .bounding_box_manager - .as_ref() - .map(|bounding_box_manager| bounding_box_manager.transform * Quad::from_box(bounding_box_manager.bounds)) - .map_or(DVec2::X, |quad| (quad.top_left() - quad.top_right()).normalize_or(DVec2::X)); - - let (direction, color) = match axis { - Axis::X => (e0, COLOR_OVERLAY_RED), - Axis::Y => (e0.perp(), COLOR_OVERLAY_GREEN), - _ => unreachable!(), - }; - - let viewport_diagonal = input.viewport_bounds.size().length(); - - let color = if !hover { - color - } else { - let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb(); - &format!("#{}", color_string) - }; - let line_center = tool_data.line_center; - overlay_context.line(line_center - direction * viewport_diagonal, line_center + direction * viewport_diagonal, Some(color), None); + if show_compass_with_ring.is_some() { + if let Some((axis, hover)) = axis_state { + if axis.is_constraint() { + let e0 = tool_data + .bounding_box_manager + .as_ref() + .map(|bounding_box_manager| bounding_box_manager.transform * Quad::from_box(bounding_box_manager.bounds)) + .map_or(DVec2::X, |quad| (quad.top_left() - quad.top_right()).normalize_or(DVec2::X)); + + let (direction, color) = match axis { + Axis::X => (e0, COLOR_OVERLAY_RED), + Axis::Y => (e0.perp(), COLOR_OVERLAY_GREEN), + _ => unreachable!(), + }; + + let viewport_diagonal = input.viewport_bounds.size().length(); + + let color = if !hover { + color + } else { + let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb(); + &format!("#{}", color_string) + }; + let line_center = tool_data.line_center; + overlay_context.line(line_center - direction * viewport_diagonal, line_center + direction * viewport_diagonal, Some(color), None); + } } } - } - if axis_state.is_none_or(|(axis, _)| !axis.is_constraint()) && tool_data.axis_align { - let mouse_position = mouse_position - tool_data.drag_start; - let snap_resolution = SELECTION_DRAG_ANGLE.to_radians(); - let angle = -mouse_position.angle_to(DVec2::X); - let snapped_angle = (angle / snap_resolution).round() * snap_resolution; + if axis_state.is_none_or(|(axis, _)| !axis.is_constraint()) && tool_data.axis_align { + let mouse_position = mouse_position - tool_data.drag_start; + let snap_resolution = SELECTION_DRAG_ANGLE.to_radians(); + let angle = -mouse_position.angle_to(DVec2::X); + let snapped_angle = (angle / snap_resolution).round() * snap_resolution; - let mut other = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.25) - .to_rgba_hex_srgb(); - other.insert(0, '#'); - let other = other.as_str(); + let mut other = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) + .unwrap() + .with_alpha(0.25) + .to_rgba_hex_srgb(); + other.insert(0, '#'); + let other = other.as_str(); - let extension = tool_data.drag_current - tool_data.drag_start; - let origin = compass_center - extension; - let viewport_diagonal = input.viewport_bounds.size().length(); + let extension = tool_data.drag_current - tool_data.drag_start; + let origin = compass_center - extension; + let viewport_diagonal = input.viewport_bounds.size().length(); - let edge = DVec2::from_angle(snapped_angle) * viewport_diagonal; - let perp = edge.perp(); + let edge = DVec2::from_angle(snapped_angle) * viewport_diagonal; + let perp = edge.perp(); - overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(COLOR_OVERLAY_BLUE), None); - overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(other), None); + overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(other), None); + } } // Check if the tool is in selection mode From ba5aa04197f53c7e4fda7c58f00b557baf5ad22a Mon Sep 17 00:00:00 2001 From: seam0s Date: Wed, 9 Apr 2025 23:45:53 +0300 Subject: [PATCH 03/25] Added more overlay checks on anchors and handles --- .../document/overlays/utility_functions.rs | 2 +- .../messages/tool/tool_messages/gradient_tool.rs | 13 +++++++++---- editor/src/messages/tool/tool_messages/path_tool.rs | 13 ++++++++++--- editor/src/messages/tool/tool_messages/pen_tool.rs | 11 +++++++---- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index 4831a086b4..fda277d862 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -174,7 +174,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle } pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext, preferences: &PreferencesMessageHandler) { - if !overlay_context.overlays_visibility_settings.path { + if !overlay_context.overlays_visibility_settings.anchors { return; } diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index ec0b6c9c63..24b71db9b3 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -252,6 +252,8 @@ impl Fsm for GradientToolFsmState { (_, GradientToolMessage::Overlays(mut overlay_context)) => { let selected = tool_data.selected_gradient.as_ref(); + let display_handles = overlay_context.overlays_visibility_settings.handles; + for layer in document.network_interface.selected_nodes().selected_visible_layers(&document.network_interface) { let Some(gradient) = get_gradient(layer, &document.network_interface) else { continue }; let transform = gradient_space_transform(layer, document); @@ -263,15 +265,18 @@ impl Fsm for GradientToolFsmState { let (start, end) = (transform.transform_point2(start), transform.transform_point2(end)); overlay_context.line(start, end, None, None); - overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start), None); - overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End), None); - + if display_handles { + overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start), None); + overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End), None); + } for (index, (position, _)) in stops.into_iter().enumerate() { if position.abs() < f64::EPSILON * 1000. || (1. - position).abs() < f64::EPSILON * 1000. { continue; } - overlay_context.manipulator_handle(start.lerp(end, position), dragging == Some(GradientDragTarget::Step(index)), None); + if display_handles { + overlay_context.manipulator_handle(start.lerp(end, position), dragging == Some(GradientDragTarget::Step(index)), None); + } } } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c31855fae8..44804b047d 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -983,13 +983,20 @@ impl Fsm for PathToolFsmState { Self::InsertPoint => { let state = tool_data.update_insertion(shape_editor, document, responses, input); + let display_anchors = overlay_context.overlays_visibility_settings.anchors; + let display_handles = overlay_context.overlays_visibility_settings.handles; + if let Some(closest_segment) = &tool_data.segment { - overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); + if display_anchors { + overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); + } if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE), None); overlay_context.line(closest_segment.closest_point_to_viewport(), handle2, Some(COLOR_OVERLAY_BLUE), None); - overlay_context.manipulator_handle(handle1, false, Some(COLOR_OVERLAY_BLUE)); - overlay_context.manipulator_handle(handle2, false, Some(COLOR_OVERLAY_BLUE)); + if display_handles { + overlay_context.manipulator_handle(handle1, false, Some(COLOR_OVERLAY_BLUE)); + overlay_context.manipulator_handle(handle2, false, Some(COLOR_OVERLAY_BLUE)); + } } } diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 1dbdad5c30..45fe84540b 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1521,6 +1521,9 @@ impl Fsm for PenToolFsmState { self } (_, PenToolMessage::Overlays(mut overlay_context)) => { + let display_anchors = overlay_context.overlays_visibility_settings.anchors; + let display_handles = overlay_context.overlays_visibility_settings.handles; + let valid = |point: DVec2, handle: DVec2| point.distance_squared(handle) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE; let transform = document.metadata().document_to_viewport * transform; @@ -1580,13 +1583,13 @@ impl Fsm for PenToolFsmState { overlay_context.dashed_line(anchor_start, next_anchor, None, None, Some(4.), Some(4.), Some(0.5)); } - if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) && valid(next_anchor, handle_end) { + if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) && valid(next_anchor, handle_end) && display_handles { // Draw the handle circle for the currently-being-dragged-out incoming handle (opposite the one currently being dragged out) let selected = tool_data.handle_type == TargetHandle::PreviewInHandle; overlay_context.manipulator_handle(handle_end, selected, None); } - if valid(anchor_start, handle_start) { + if valid(anchor_start, handle_start) && display_handles { // Draw the handle circle for the most recently placed anchor's outgoing handle (which is currently influencing the currently-being-placed segment) overlay_context.manipulator_handle(handle_start, false, None); } @@ -1602,13 +1605,13 @@ impl Fsm for PenToolFsmState { } } - if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) && valid(next_anchor, next_handle_start) { + if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) && valid(next_anchor, next_handle_start) && display_handles { // Draw the handle circle for the currently-being-dragged-out outgoing handle (the one currently being dragged out, under the user's cursor) let selected = tool_data.handle_type == TargetHandle::FuturePreviewOutHandle; overlay_context.manipulator_handle(next_handle_start, selected, None); } - if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) { + if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) && display_anchors { // Draw the anchor square for the most recently placed anchor overlay_context.manipulator_anchor(next_anchor, false, None); } From 938ad0790e9e5a0487f2c0cf4e233d4e6c957d42 Mon Sep 17 00:00:00 2001 From: seam0s Date: Tue, 15 Apr 2025 23:16:10 +0300 Subject: [PATCH 04/25] Add new settings over measurements, hover and selection overlays --- .../layout/utility_types/layout_widget.rs | 2 +- .../document/document_message_handler.rs | 199 +++++++++++------- .../document/overlays/utility_types.rs | 15 +- .../transformation_cage.rs | 4 - .../tool/tool_messages/artboard_tool.rs | 8 +- .../tool/tool_messages/gradient_tool.rs | 12 +- .../messages/tool/tool_messages/path_tool.rs | 2 +- .../tool/tool_messages/select_tool.rs | 54 ++--- .../messages/tool/tool_messages/text_tool.rs | 36 ++-- .../transform_layer_message_handler.rs | 4 + 10 files changed, 201 insertions(+), 135 deletions(-) diff --git a/editor/src/messages/layout/utility_types/layout_widget.rs b/editor/src/messages/layout/utility_types/layout_widget.rs index b1d9550b02..8f154fa960 100644 --- a/editor/src/messages/layout/utility_types/layout_widget.rs +++ b/editor/src/messages/layout/utility_types/layout_widget.rs @@ -49,7 +49,7 @@ pub enum LayoutTarget { WorkingColors, // KEEP THIS ENUM LAST - // This is a marker that is used to define an array that is used to hold widgets + // This is a marker that is used to define an array to hold widgets LayoutTargetLength, } diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index b6f44540cb..a7a8b81990 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1021,6 +1021,10 @@ impl MessageHandler> for DocumentMessag } } DocumentMessage::SelectAllLayers => { + if !self.overlays_visibility_settings.selection_outline { + return; + } + let metadata = self.metadata(); let all_layers_except_artboards_invisible_and_locked = metadata.all_layers().filter(|&layer| !self.network_interface.is_artboard(&layer.to_node(), &[])).filter(|&layer| { self.network_interface.selected_nodes().layer_visible(layer, &self.network_interface) && !self.network_interface.selected_nodes().layer_locked(layer, &self.network_interface) @@ -1145,8 +1149,11 @@ impl MessageHandler> for DocumentMessag OverlaysType::All => self.overlays_visibility_settings.all = visible, OverlaysType::ArtboardName => self.overlays_visibility_settings.artboard_name = visible, OverlaysType::CompassRose => self.overlays_visibility_settings.compass_rose = visible, - OverlaysType::Measurement => self.overlays_visibility_settings.measurement = visible, + OverlaysType::QuickMeasurement => self.overlays_visibility_settings.quick_measurement = visible, + OverlaysType::TransformMeasurement => self.overlays_visibility_settings.transform_measurement = visible, OverlaysType::TransformCage => self.overlays_visibility_settings.transform_cage = visible, + OverlaysType::HoverOutline => self.overlays_visibility_settings.hover_outline = visible, + OverlaysType::SelectionOutline => self.overlays_visibility_settings.selection_outline = visible, OverlaysType::Pivot => self.overlays_visibility_settings.pivot = visible, OverlaysType::Path => self.overlays_visibility_settings.path = visible, OverlaysType::Anchors => self.overlays_visibility_settings.anchors = visible, @@ -2118,17 +2125,32 @@ impl DocumentMessageHandler { }, LayoutGroup::Row { widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.measurement) - .tooltip("Enable or disable the measurement overlay") + CheckboxInput::new(self.overlays_visibility_settings.quick_measurement) + .tooltip("Enable or disable the quick measurement overlay") .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::Measurement, + overlays_type: OverlaysType::QuickMeasurement, } .into() }) .widget_holder(), - TextLabel::new("Measurement".to_string()).widget_holder(), + TextLabel::new("Quick Measurement".to_string()).widget_holder(), + ], + }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.transform_measurement) + .tooltip("Enable or disable the transform measurement overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::TransformMeasurement, + } + .into() + }) + .widget_holder(), + TextLabel::new("Transform Measurement".to_string()).widget_holder(), ], }, LayoutGroup::Row { @@ -2146,6 +2168,36 @@ impl DocumentMessageHandler { TextLabel::new("Transform Cage".to_string()).widget_holder(), ], }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.hover_outline) + .tooltip("Enable or disable the hover outline overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::HoverOutline, + } + .into() + }) + .widget_holder(), + TextLabel::new("Hover Outline".to_string()).widget_holder(), + ], + }, + LayoutGroup::Row { + widgets: vec![ + CheckboxInput::new(self.overlays_visibility_settings.selection_outline) + .tooltip("Enable or disable the selection outline overlay") + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: OverlaysType::SelectionOutline, + } + .into() + }) + .widget_holder(), + TextLabel::new("Selection Outline".to_string()).widget_holder(), + ], + }, LayoutGroup::Row { widgets: vec![ CheckboxInput::new(self.overlays_visibility_settings.pivot) @@ -2419,75 +2471,74 @@ impl DocumentMessageHandler { .selected_layers(self.metadata()) .all(|layer| self.network_interface.is_locked(&layer.to_node(), &[])); - let layers_panel_control_bar = WidgetLayout::new(vec![LayoutGroup::Row { - widgets: vec![ - DropdownInput::new(blend_mode_menu_entries) - .selected_index(blend_mode.and_then(|blend_mode| blend_mode.index_in_list_svg_subset()).map(|index| index as u32)) - .disabled(disabled) - .draw_icon(false) - .widget_holder(), - Separator::new(SeparatorType::Related).widget_holder(), - NumberInput::new(opacity) - .label("Opacity") - .unit("%") - .display_decimal_places(2) - .disabled(disabled) - .min(0.) - .max(100.) - .range_min(Some(0.)) - .range_max(Some(100.)) - .mode_range() - .on_update(|number_input: &NumberInput| { - if let Some(value) = number_input.value { - DocumentMessage::SetOpacityForSelectedLayers { opacity: value / 100. }.into() - } else { - Message::NoOp - } - }) - .on_commit(|_| DocumentMessage::AddTransaction.into()) - .widget_holder(), - // - Separator::new(SeparatorType::Unrelated).widget_holder(), - // - IconButton::new("NewLayer", 24) - .tooltip("New Layer") - .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder)) - .on_update(|_| DocumentMessage::CreateEmptyFolder.into()) - .widget_holder(), - IconButton::new("Folder", 24) - .tooltip("Group Selected") - .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers)) - .on_update(|_| { - let group_folder_type = GroupFolderType::Layer; - DocumentMessage::GroupSelectedLayers { group_folder_type }.into() - }) - .disabled(!has_selection) - .widget_holder(), - IconButton::new("Trash", 24) - .tooltip("Delete Selected") - .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers)) - .on_update(|_| DocumentMessage::DeleteSelectedLayers.into()) - .disabled(!has_selection) - .widget_holder(), - // - Separator::new(SeparatorType::Unrelated).widget_holder(), - // - IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24) - .hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into())) - .tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" }) - .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked)) - .on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into()) - .disabled(!has_selection) - .widget_holder(), - IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24) - .hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into())) - .tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" }) - .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility)) - .on_update(|_| DocumentMessage::ToggleSelectedVisibility.into()) - .disabled(!has_selection) - .widget_holder(), - ], - }]); + let widgets = vec![ + DropdownInput::new(blend_mode_menu_entries) + .selected_index(blend_mode.and_then(|blend_mode| blend_mode.index_in_list_svg_subset()).map(|index| index as u32)) + .disabled(disabled) + .draw_icon(false) + .widget_holder(), + Separator::new(SeparatorType::Related).widget_holder(), + NumberInput::new(opacity) + .label("Opacity") + .unit("%") + .display_decimal_places(2) + .disabled(disabled) + .min(0.) + .max(100.) + .range_min(Some(0.)) + .range_max(Some(100.)) + .mode_range() + .on_update(|number_input: &NumberInput| { + if let Some(value) = number_input.value { + DocumentMessage::SetOpacityForSelectedLayers { opacity: value / 100. }.into() + } else { + Message::NoOp + } + }) + .on_commit(|_| DocumentMessage::AddTransaction.into()) + .widget_holder(), + // + Separator::new(SeparatorType::Unrelated).widget_holder(), + // + IconButton::new("NewLayer", 24) + .tooltip("New Layer") + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder)) + .on_update(|_| DocumentMessage::CreateEmptyFolder.into()) + .widget_holder(), + IconButton::new("Folder", 24) + .tooltip("Group Selected") + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers)) + .on_update(|_| { + let group_folder_type = GroupFolderType::Layer; + DocumentMessage::GroupSelectedLayers { group_folder_type }.into() + }) + .disabled(!has_selection) + .widget_holder(), + IconButton::new("Trash", 24) + .tooltip("Delete Selected") + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers)) + .on_update(|_| DocumentMessage::DeleteSelectedLayers.into()) + .disabled(!has_selection) + .widget_holder(), + // + Separator::new(SeparatorType::Unrelated).widget_holder(), + // + IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24) + .hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into())) + .tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" }) + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked)) + .on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into()) + .disabled(!has_selection) + .widget_holder(), + IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24) + .hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into())) + .tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" }) + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility)) + .on_update(|_| DocumentMessage::ToggleSelectedVisibility.into()) + .disabled(!has_selection) + .widget_holder(), + ]; + let layers_panel_control_bar = WidgetLayout::new(vec![LayoutGroup::Row { widgets }]); responses.add(LayoutMessage::SendLayout { layout: Layout::WidgetLayout(layers_panel_control_bar), diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index 7c886debc7..1bee90f851 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -25,8 +25,11 @@ pub enum OverlaysType { All, ArtboardName, CompassRose, - Measurement, + QuickMeasurement, + TransformMeasurement, TransformCage, + HoverOutline, + SelectionOutline, Pivot, Path, Anchors, @@ -38,8 +41,11 @@ pub struct OverlaysVisibilitySettings { pub all: bool, pub artboard_name: bool, pub compass_rose: bool, - pub measurement: bool, + pub quick_measurement: bool, + pub transform_measurement: bool, pub transform_cage: bool, + pub hover_outline: bool, + pub selection_outline: bool, pub pivot: bool, pub path: bool, pub anchors: bool, @@ -52,8 +58,11 @@ impl Default for OverlaysVisibilitySettings { all: true, artboard_name: true, compass_rose: true, - measurement: true, + quick_measurement: true, + transform_measurement: true, transform_cage: true, + hover_outline: true, + selection_outline: true, pivot: true, path: true, anchors: true, diff --git a/editor/src/messages/tool/common_functionality/transformation_cage.rs b/editor/src/messages/tool/common_functionality/transformation_cage.rs index 0234d9d998..09d4097b1c 100644 --- a/editor/src/messages/tool/common_functionality/transformation_cage.rs +++ b/editor/src/messages/tool/common_functionality/transformation_cage.rs @@ -575,10 +575,6 @@ impl BoundingBoxManager { /// Update the position of the bounding box and transform handles pub fn render_overlays(&mut self, overlay_context: &mut OverlayContext, render_quad: bool) { - if !overlay_context.overlays_visibility_settings.transform_cage { - return; - } - let quad = self.transform * Quad::from_box(self.bounds); let category = self.overlay_display_category(); diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 6e67fa6823..3343d82bf7 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -225,17 +225,19 @@ impl Fsm for ArtboardToolFsmState { let ToolMessage::Artboard(event) = event else { return self }; match (self, event) { (state, ArtboardToolMessage::Overlays(mut overlay_context)) => { - if state != ArtboardToolFsmState::Drawing { + let display_transform_cage = overlay_context.overlays_visibility_settings.transform_cage; + if display_transform_cage && state != ArtboardToolFsmState::Drawing { if let Some(bounds) = tool_data.selected_artboard.and_then(|layer| document.metadata().bounding_box_document(layer)) { let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); bounding_box_manager.bounds = bounds; bounding_box_manager.transform = document.metadata().document_to_viewport; bounding_box_manager.render_overlays(&mut overlay_context, true); - } else { - tool_data.bounding_box_manager.take(); } } + else { + tool_data.bounding_box_manager.take(); + } tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index 24b71db9b3..26be525a3c 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -252,8 +252,6 @@ impl Fsm for GradientToolFsmState { (_, GradientToolMessage::Overlays(mut overlay_context)) => { let selected = tool_data.selected_gradient.as_ref(); - let display_handles = overlay_context.overlays_visibility_settings.handles; - for layer in document.network_interface.selected_nodes().selected_visible_layers(&document.network_interface) { let Some(gradient) = get_gradient(layer, &document.network_interface) else { continue }; let transform = gradient_space_transform(layer, document); @@ -265,18 +263,14 @@ impl Fsm for GradientToolFsmState { let (start, end) = (transform.transform_point2(start), transform.transform_point2(end)); overlay_context.line(start, end, None, None); - if display_handles { - overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start), None); - overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End), None); - } + overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start), None); + overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End), None); for (index, (position, _)) in stops.into_iter().enumerate() { if position.abs() < f64::EPSILON * 1000. || (1. - position).abs() < f64::EPSILON * 1000. { continue; } - if display_handles { - overlay_context.manipulator_handle(start.lerp(end, position), dragging == Some(GradientDragTarget::Step(index)), None); - } + overlay_context.manipulator_handle(start.lerp(end, position), dragging == Some(GradientDragTarget::Step(index)), None); } } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 44804b047d..f31286b056 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -988,7 +988,7 @@ impl Fsm for PathToolFsmState { if let Some(closest_segment) = &tool_data.segment { if display_anchors { - overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); + overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); } if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE), None); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 170cf8a124..f6d8c883ff 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -552,11 +552,13 @@ impl Fsm for SelectToolFsmState { let click = document.click(input); let not_selected_click = click.filter(|&hovered_layer| !document.network_interface.selected_nodes().selected_layers_contains(hovered_layer, document.metadata())); if let Some(layer) = not_selected_click { - overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); + if overlay_context.overlays_visibility_settings.hover_outline { + overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); + } // Measure with Alt held down // TODO: Don't use `Key::Alt` directly, instead take it as a variable from the input mappings list like in all other places - if !matches!(self, Self::ResizingBounds { .. }) && input.keyboard.get(Key::Alt as usize) { + if overlay_context.overlays_visibility_settings.quick_measurement && !matches!(self, Self::ResizingBounds { .. }) && input.keyboard.get(Key::Alt as usize) { // Get all selected layers and compute their viewport-aligned AABB let selected_bounds_viewport = document .network_interface @@ -581,23 +583,25 @@ impl Fsm for SelectToolFsmState { // Use the viewport-aligned AABBs for measurement if let (Some(selected_bounds), Some(hovered_bounds)) = (selected_bounds_viewport, hovered_bounds_viewport) { - if overlay_context.overlays_visibility_settings.measurement { - // Since we're already in viewport space, use identity transform - measure::overlay(selected_bounds, hovered_bounds, DAffine2::IDENTITY, DAffine2::IDENTITY, &mut overlay_context); - } + // Since we're already in viewport space, use identity transform + measure::overlay(selected_bounds, hovered_bounds, DAffine2::IDENTITY, DAffine2::IDENTITY, &mut overlay_context); } } } } - if let Some(bounds) = bounds { - let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); + let display_transform_cage = overlay_context.overlays_visibility_settings.transform_cage; + if display_transform_cage { + if let Some(bounds) = bounds { + let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); - bounding_box_manager.bounds = bounds; - bounding_box_manager.transform = transform; - bounding_box_manager.transform_tampered = transform_tampered; - bounding_box_manager.render_overlays(&mut overlay_context, true); - } else { + bounding_box_manager.bounds = bounds; + bounding_box_manager.transform = transform; + bounding_box_manager.transform_tampered = transform_tampered; + bounding_box_manager.render_overlays(&mut overlay_context, true); + } + } + else { tool_data.bounding_box_manager.take(); } @@ -628,16 +632,18 @@ impl Fsm for SelectToolFsmState { let is_resizing_or_rotating = matches!(self, SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. } | SelectToolFsmState::RotatingBounds); - if let Some(bounds) = tool_data.bounding_box_manager.as_mut() { - let edges = bounds.check_selected_edges(input.mouse.position); - let is_skewing = matches!(self, SelectToolFsmState::SkewingBounds { .. }); - let is_near_square = edges.is_some_and(|hover_edge| bounds.over_extended_edge_midpoint(input.mouse.position, hover_edge)); - if is_skewing || (dragging_bounds && is_near_square && !is_resizing_or_rotating) { - bounds.render_skew_gizmos(&mut overlay_context, tool_data.skew_edge); - } - if !is_skewing && dragging_bounds { - if let Some(edges) = edges { - tool_data.skew_edge = bounds.get_closest_edge(edges, input.mouse.position); + if display_transform_cage { + if let Some(bounds) = tool_data.bounding_box_manager.as_mut() { + let edges = bounds.check_selected_edges(input.mouse.position); + let is_skewing = matches!(self, SelectToolFsmState::SkewingBounds { .. }); + let is_near_square = edges.is_some_and(|hover_edge| bounds.over_extended_edge_midpoint(input.mouse.position, hover_edge)); + if is_skewing || (dragging_bounds && is_near_square && !is_resizing_or_rotating) { + bounds.render_skew_gizmos(&mut overlay_context, tool_data.skew_edge); + } + if !is_skewing && dragging_bounds { + if let Some(edges) = edges { + tool_data.skew_edge = bounds.get_closest_edge(edges, input.mouse.position); + } } } } @@ -671,7 +677,7 @@ impl Fsm for SelectToolFsmState { overlay_context.compass_rose(compass_center, angle, show_compass_with_ring); let axis_state = if let SelectToolFsmState::Dragging { axis, .. } = self { - Some((axis, false)) + Some((axis, false)) } else { compass_rose_state.axis_type().and_then(|axis| axis.is_constraint().then_some((axis, true))) }; diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index b1c5fe5a73..3027ab6c26 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -506,24 +506,28 @@ impl Fsm for TextToolFsmState { return self; } - if let Some(bounds) = bounds { - let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); - bounding_box_manager.bounds = [bounds.0[0], bounds.0[2]]; - bounding_box_manager.transform = layer_transform; - - bounding_box_manager.render_quad(&mut overlay_context); - // Draw red overlay if text is clipped - let transformed_quad = layer_transform * bounds; - if let Some((text, font, typesetting)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface) { - let buzz_face = font_cache.get(font).map(|data| load_face(data)); - if lines_clipping(text.as_str(), buzz_face, typesetting) { - overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED), Some(3.)); + let display_transform_cage = overlay_context.overlays_visibility_settings.transform_cage; + if display_transform_cage { + if let Some(bounds) = bounds { + let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); + bounding_box_manager.bounds = [bounds.0[0], bounds.0[2]]; + bounding_box_manager.transform = layer_transform; + + bounding_box_manager.render_quad(&mut overlay_context); + // Draw red overlay if text is clipped + let transformed_quad = layer_transform * bounds; + if let Some((text, font, typesetting)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface) { + let buzz_face = font_cache.get(font).map(|data| load_face(data)); + if lines_clipping(text.as_str(), buzz_face, typesetting) { + overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED), Some(3.)); + } } - } - bounding_box_manager.render_overlays(&mut overlay_context, false); - tool_data.pivot.update_pivot(document, &mut overlay_context, None); - } else { + bounding_box_manager.render_overlays(&mut overlay_context, false); + tool_data.pivot.update_pivot(document, &mut overlay_context, None); + } + } + else { tool_data.bounding_box_manager.take(); } diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index 30f1c239e1..667d36d7b0 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -209,6 +209,10 @@ impl MessageHandler> for TransformLayer match message { // Overlays TransformLayerMessage::Overlays(mut overlay_context) => { + if !overlay_context.overlays_visibility_settings.transform_measurement { + return; + } + for layer in document.metadata().all_layers() { if !document.network_interface.is_artboard(&layer.to_node(), &[]) { continue; From f1f2a7bd9b42f644c214603c9fc1ae55318dda10 Mon Sep 17 00:00:00 2001 From: seam0s Date: Wed, 16 Apr 2025 15:03:50 +0300 Subject: [PATCH 05/25] Fix errors introduced while rebasing --- .../portfolio/document/overlays/overlays_message_handler.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs index 36a22d30a6..f4683455b9 100644 --- a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs +++ b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs @@ -66,9 +66,8 @@ impl MessageHandler> for OverlaysMessag size: size.as_dvec2(), device_pixel_ratio, overlays_visibility_settings: overlays_visibility_settings.clone(), - }); + })); // debug!("OverlaysMessageHandler: {:?}", message.clone()); - responses.add(message); } } } From d335e3c30e08ecdbeda71baf4e27ec867dfbc292 Mon Sep 17 00:00:00 2001 From: seam0s Date: Thu, 17 Apr 2025 14:50:03 +0300 Subject: [PATCH 06/25] Disable anchors and handles functionality with their overlays, extended selection outline check --- .../overlays/overlays_message_handler.rs | 5 +-- .../tool/common_functionality/shape_editor.rs | 36 +++++++++++++++++++ .../messages/tool/tool_messages/path_tool.rs | 11 ++++-- .../tool/tool_messages/select_tool.rs | 7 ++-- 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs index f4683455b9..7b60ff0ec5 100644 --- a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs +++ b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs @@ -73,10 +73,7 @@ impl MessageHandler> for OverlaysMessag } #[cfg(not(target_arch = "wasm32"))] OverlaysMessage::Draw => { - warn!( - "Cannot render overlays on non-Wasm targets.\n{responses:?} {overlays_visibility_settings:?} {ipp:?} {:?} {:?}", - self.canvas, self.context - ); + warn!("Cannot render overlays on non-Wasm targets.\n{responses:?} {overlays_visibility_settings:?} {ipp:?}",); } OverlaysMessage::AddProvider(message) => { self.overlay_providers.insert(message); diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index c1d4ebfd3d..cd6f4d15e5 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -465,6 +465,42 @@ impl ShapeState { } } + /// Deselects all the anchors across every selected layer. + pub fn deselect_all_anchors(&mut self) { + for (_, state) in self.selected_shape_state.iter_mut() { + let selected_anchor_points: Vec = state + .selected_points + .iter() + .filter(|selected_point| if let ManipulatorPointId::Anchor(_) = selected_point { true } else { false }) + .cloned() + .collect(); + + for point in selected_anchor_points { + state.deselect_point(point); + } + } + } + + /// Deselects all the handles across every selected layer. + pub fn deselect_all_handles(&mut self) { + for (_, state) in self.selected_shape_state.iter_mut() { + let selected_handle_points: Vec = state + .selected_points + .iter() + .filter(|selected_point| match selected_point { + ManipulatorPointId::PrimaryHandle(_) => true, + ManipulatorPointId::EndHandle(_) => true, + _ => false, + }) + .cloned() + .collect(); + + for point in selected_handle_points { + state.deselect_point(point); + } + } + } + /// Set the shapes we consider for selection, we will choose draggable manipulators from these shapes. pub fn set_selected_layers(&mut self, target_layers: Vec) { self.selected_shape_state.retain(|layer_path, _| target_layers.contains(layer_path)); diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index f31286b056..8057f901bb 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -875,6 +875,14 @@ impl Fsm for PathToolFsmState { self } (_, PathToolMessage::Overlays(mut overlay_context)) => { + let display_anchors = overlay_context.overlays_visibility_settings.anchors; + let display_handles = overlay_context.overlays_visibility_settings.handles; + if !display_handles { + shape_editor.deselect_all_handles(); + } else if !display_anchors { + shape_editor.deselect_all_anchors(); + } + // TODO: find the segment ids of which the selected points are a part of match tool_options.path_overlay_mode { @@ -983,9 +991,6 @@ impl Fsm for PathToolFsmState { Self::InsertPoint => { let state = tool_data.update_insertion(shape_editor, document, responses, input); - let display_anchors = overlay_context.overlays_visibility_settings.anchors; - let display_handles = overlay_context.overlays_visibility_settings.handles; - if let Some(closest_segment) = &tool_data.segment { if display_anchors { overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index f6d8c883ff..bec427f9b0 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -764,8 +764,11 @@ impl Fsm for SelectToolFsmState { SelectionMode::Directional => unreachable!(), }); - for layer in layers_to_outline { - overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); + if overlay_context.overlays_visibility_settings.selection_outline { + // Draws a temporary outline on the layers that will be selected + for layer in layers_to_outline { + overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); + } } // Update the selection box From d48cfdefef5e8947c7bc00862a48f35a5719c85f Mon Sep 17 00:00:00 2001 From: seam0s Date: Fri, 18 Apr 2025 23:20:28 +0300 Subject: [PATCH 07/25] Add check to enable/disable outlines on selected layers --- .../tool/tool_messages/select_tool.rs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index bec427f9b0..4f084f8d36 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -502,17 +502,19 @@ impl Fsm for SelectToolFsmState { tool_data.selected_layers_count = selected_layers_count; // Outline selected layers, but not artboards - for layer in document - .network_interface - .selected_nodes() - .selected_visible_and_unlocked_layers(&document.network_interface) - .filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[])) - { - overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); - - if is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text") { - let transformed_quad = document.metadata().transform_to_viewport(layer) * text_bounding_box(layer, document, font_cache); - overlay_context.dashed_quad(transformed_quad, None, Some(7.), Some(5.), None); + if overlay_context.overlays_visibility_settings.selection_outline { + for layer in document + .network_interface + .selected_nodes() + .selected_visible_and_unlocked_layers(&document.network_interface) + .filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[])) + { + overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); + + if is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text") { + let transformed_quad = document.metadata().transform_to_viewport(layer) * text_bounding_box(layer, document, font_cache); + overlay_context.dashed_quad(transformed_quad, None, Some(7.), Some(5.), None); + } } } From c4c5379ec08350892b555656174f723846de3c05 Mon Sep 17 00:00:00 2001 From: seam0s Date: Fri, 18 Apr 2025 23:22:19 +0300 Subject: [PATCH 08/25] Toggle handles checkbox in sync with anchors checkbox --- .../portfolio/document/document_message_handler.rs | 9 ++++++++- .../messages/tool/common_functionality/shape_editor.rs | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index a96ae7878a..3a8a3c8da1 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1161,6 +1161,9 @@ impl MessageHandler> for DocumentMessag } responses.add(BroadcastEvent::ToolAbort); responses.add(OverlaysMessage::Draw); + + // TODO: Updating the widgets is only needed for anchors and handles, find a better way to do this + responses.add(PortfolioMessage::UpdateDocumentWidgets); } DocumentMessage::SetRangeSelectionLayer { new_layer } => { self.layer_range_selection_reference = new_layer; @@ -2063,6 +2066,8 @@ impl DocumentMessageHandler { let mut snapping_state = self.snapping_state.clone(); let mut snapping_state2 = self.snapping_state.clone(); + debug!("overlays_visibility_settings: {:?}", self.overlays_visibility_settings); + let mut widgets = vec![ IconButton::new("PlaybackToStart", 24) .tooltip("Restart Animation") @@ -2246,6 +2251,7 @@ impl DocumentMessageHandler { LayoutGroup::Row { widgets: vec![ CheckboxInput::new(self.overlays_visibility_settings.handles) + .disabled(!self.overlays_visibility_settings.anchors) .tooltip("Enable or disable the handles overlay") .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { @@ -2255,7 +2261,7 @@ impl DocumentMessageHandler { .into() }) .widget_holder(), - TextLabel::new("Handles".to_string()).widget_holder(), + TextLabel::new("Handles".to_string()).disabled(!self.overlays_visibility_settings.anchors).widget_holder(), ], }, ]) @@ -2403,6 +2409,7 @@ impl DocumentMessageHandler { layout: Layout::WidgetLayout(document_bar_layout), layout_target: LayoutTarget::DocumentBar, }); + responses.add(NodeGraphMessage::ForceRunDocumentGraph); } pub fn update_layers_panel_control_bar_widgets(&self, responses: &mut VecDeque) { diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 120b20e78a..59ad6e51a5 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -502,7 +502,10 @@ impl ShapeState { let selected_anchor_points: Vec = state .selected_points .iter() - .filter(|selected_point| if let ManipulatorPointId::Anchor(_) = selected_point { true } else { false }) + .filter(|selected_point| match selected_point { + ManipulatorPointId::Anchor(_) => true, + _ => false, + }) .cloned() .collect(); From d5d5d4cb4453532472727b2f29719cf45ae2a016 Mon Sep 17 00:00:00 2001 From: seam0s Date: Sat, 19 Apr 2025 00:07:13 +0300 Subject: [PATCH 09/25] Refactor overlays checks --- .../document/document_message_handler.rs | 31 +++++------ .../overlays/overlays_message_handler.rs | 14 +++-- .../document/overlays/utility_functions.rs | 8 +-- .../document/overlays/utility_types.rs | 52 ++++++++++++++++++- .../tool/common_functionality/pivot.rs | 2 +- .../tool/tool_messages/artboard_tool.rs | 5 +- .../messages/tool/tool_messages/path_tool.rs | 4 +- .../messages/tool/tool_messages/pen_tool.rs | 4 +- .../tool/tool_messages/select_tool.rs | 18 +++---- .../messages/tool/tool_messages/text_tool.rs | 6 +-- .../transform_layer_message_handler.rs | 2 +- 11 files changed, 95 insertions(+), 51 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 3a8a3c8da1..660b1963df 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -207,7 +207,7 @@ impl MessageHandler> for DocumentMessag message, responses, OverlaysMessageData { - overlays_visibility_settings, + visibility_settings: overlays_visibility_settings, ipp, device_pixel_ratio, }, @@ -350,7 +350,7 @@ impl MessageHandler> for DocumentMessag responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer }); } DocumentMessage::DrawArtboardOverlays(overlay_context) => { - if !overlay_context.overlays_visibility_settings.artboard_name { + if !overlay_context.visibility_settings.artboard_name() { return; } @@ -1021,7 +1021,7 @@ impl MessageHandler> for DocumentMessag } } DocumentMessage::SelectAllLayers => { - if !self.overlays_visibility_settings.selection_outline { + if !self.overlays_visibility_settings.selection_outline() { return; } @@ -1145,19 +1145,20 @@ impl MessageHandler> for DocumentMessag } } DocumentMessage::SetOverlaysVisibility { visible, overlays_type } => { + let visibility_settings = &mut self.overlays_visibility_settings; match overlays_type { - OverlaysType::All => self.overlays_visibility_settings.all = visible, - OverlaysType::ArtboardName => self.overlays_visibility_settings.artboard_name = visible, - OverlaysType::CompassRose => self.overlays_visibility_settings.compass_rose = visible, - OverlaysType::QuickMeasurement => self.overlays_visibility_settings.quick_measurement = visible, - OverlaysType::TransformMeasurement => self.overlays_visibility_settings.transform_measurement = visible, - OverlaysType::TransformCage => self.overlays_visibility_settings.transform_cage = visible, - OverlaysType::HoverOutline => self.overlays_visibility_settings.hover_outline = visible, - OverlaysType::SelectionOutline => self.overlays_visibility_settings.selection_outline = visible, - OverlaysType::Pivot => self.overlays_visibility_settings.pivot = visible, - OverlaysType::Path => self.overlays_visibility_settings.path = visible, - OverlaysType::Anchors => self.overlays_visibility_settings.anchors = visible, - OverlaysType::Handles => self.overlays_visibility_settings.handles = visible, + OverlaysType::All => visibility_settings.all = visible, + OverlaysType::ArtboardName => visibility_settings.artboard_name = visible, + OverlaysType::CompassRose => visibility_settings.compass_rose = visible, + OverlaysType::QuickMeasurement => visibility_settings.quick_measurement = visible, + OverlaysType::TransformMeasurement => visibility_settings.transform_measurement = visible, + OverlaysType::TransformCage => visibility_settings.transform_cage = visible, + OverlaysType::HoverOutline => visibility_settings.hover_outline = visible, + OverlaysType::SelectionOutline => visibility_settings.selection_outline = visible, + OverlaysType::Pivot => visibility_settings.pivot = visible, + OverlaysType::Path => visibility_settings.path = visible, + OverlaysType::Anchors => visibility_settings.anchors = visible, + OverlaysType::Handles => visibility_settings.handles = visible, } responses.add(BroadcastEvent::ToolAbort); responses.add(OverlaysMessage::Draw); diff --git a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs index 7b60ff0ec5..94cd4266f7 100644 --- a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs +++ b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs @@ -3,7 +3,7 @@ use super::utility_types::OverlaysVisibilitySettings; use crate::messages::prelude::*; pub struct OverlaysMessageData<'a> { - pub overlays_visibility_settings: OverlaysVisibilitySettings, + pub visibility_settings: OverlaysVisibilitySettings, pub ipp: &'a InputPreprocessorMessageHandler, pub device_pixel_ratio: f64, } @@ -19,9 +19,7 @@ pub struct OverlaysMessageHandler { impl MessageHandler> for OverlaysMessageHandler { fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque, data: OverlaysMessageData) { - let OverlaysMessageData { - overlays_visibility_settings, ipp, .. - } = data; + let OverlaysMessageData { visibility_settings, ipp, .. } = data; match message { #[cfg(target_arch = "wasm32")] @@ -53,19 +51,19 @@ impl MessageHandler> for OverlaysMessag context.clear_rect(0., 0., ipp.viewport_bounds.size().x, ipp.viewport_bounds.size().y); let _ = context.reset_transform(); - if overlays_visibility_settings.all { + if visibility_settings.all() { responses.add(DocumentMessage::GridOverlays(OverlayContext { render_context: context.clone(), size: size.as_dvec2(), device_pixel_ratio, - overlays_visibility_settings: overlays_visibility_settings.clone(), + visibility_settings: visibility_settings.clone(), })); for provider in &self.overlay_providers { responses.add(provider(OverlayContext { render_context: context.clone(), size: size.as_dvec2(), device_pixel_ratio, - overlays_visibility_settings: overlays_visibility_settings.clone(), + visibility_settings: visibility_settings.clone(), })); // debug!("OverlaysMessageHandler: {:?}", message.clone()); } @@ -73,7 +71,7 @@ impl MessageHandler> for OverlaysMessag } #[cfg(not(target_arch = "wasm32"))] OverlaysMessage::Draw => { - warn!("Cannot render overlays on non-Wasm targets.\n{responses:?} {overlays_visibility_settings:?} {ipp:?}",); + warn!("Cannot render overlays on non-Wasm targets.\n{responses:?} {visibility_settings:?} {ipp:?}",); } OverlaysMessage::AddProvider(message) => { self.overlay_providers.insert(message); diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index fda277d862..bcb1f32ea9 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -112,9 +112,9 @@ fn overlay_bezier_handle_specific_point( } pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandles, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext) { - let display_path = overlay_context.overlays_visibility_settings.path; - let display_handles = overlay_context.overlays_visibility_settings.handles; - let display_anchors = overlay_context.overlays_visibility_settings.anchors; + let display_path = overlay_context.visibility_settings.path(); + let display_handles = overlay_context.visibility_settings.handles(); + let display_anchors = overlay_context.visibility_settings.anchors(); for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) { let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue }; @@ -174,7 +174,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle } pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext, preferences: &PreferencesMessageHandler) { - if !overlay_context.overlays_visibility_settings.anchors { + if !overlay_context.visibility_settings.anchors() { return; } diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index 1bee90f851..35702849c2 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -71,6 +71,56 @@ impl Default for OverlaysVisibilitySettings { } } +impl OverlaysVisibilitySettings { + pub fn all(&self) -> bool { + self.all + } + + pub fn artboard_name(&self) -> bool { + self.all && self.artboard_name + } + + pub fn compass_rose(&self) -> bool { + self.all && self.compass_rose + } + + pub fn quick_measurement(&self) -> bool { + self.all && self.quick_measurement + } + + pub fn transform_measurement(&self) -> bool { + self.all && self.transform_measurement + } + + pub fn transform_cage(&self) -> bool { + self.all && self.transform_cage + } + + pub fn hover_outline(&self) -> bool { + self.all && self.hover_outline + } + + pub fn selection_outline(&self) -> bool { + self.all && self.selection_outline + } + + pub fn pivot(&self) -> bool { + self.all && self.pivot + } + + pub fn path(&self) -> bool { + self.all && self.path + } + + pub fn anchors(&self) -> bool { + self.all && self.anchors + } + + pub fn handles(&self) -> bool { + self.all && self.handles + } +} + #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] pub struct OverlayContext { // Serde functionality isn't used but is required by the message system macros @@ -81,7 +131,7 @@ pub struct OverlayContext { // The device pixel ratio is a property provided by the browser window and is the CSS pixel size divided by the physical monitor's pixel size. // It allows better pixel density of visualizations on high-DPI displays where the OS display scaling is not 100%, or where the browser is zoomed. pub device_pixel_ratio: f64, - pub overlays_visibility_settings: OverlaysVisibilitySettings, + pub visibility_settings: OverlaysVisibilitySettings, } // Message hashing isn't used but is required by the message system macros impl core::hash::Hash for OverlayContext { diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index b8b655fb00..fe71c48933 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -82,7 +82,7 @@ impl Pivot { } pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, draw_data: Option<(f64,)>) { - if !overlay_context.overlays_visibility_settings.pivot { + if !overlay_context.visibility_settings.pivot() { return; } diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 3343d82bf7..65fb90cdca 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -225,7 +225,7 @@ impl Fsm for ArtboardToolFsmState { let ToolMessage::Artboard(event) = event else { return self }; match (self, event) { (state, ArtboardToolMessage::Overlays(mut overlay_context)) => { - let display_transform_cage = overlay_context.overlays_visibility_settings.transform_cage; + let display_transform_cage = overlay_context.visibility_settings.transform_cage(); if display_transform_cage && state != ArtboardToolFsmState::Drawing { if let Some(bounds) = tool_data.selected_artboard.and_then(|layer| document.metadata().bounding_box_document(layer)) { let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); @@ -234,8 +234,7 @@ impl Fsm for ArtboardToolFsmState { bounding_box_manager.render_overlays(&mut overlay_context, true); } - } - else { + } else { tool_data.bounding_box_manager.take(); } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c708a82149..097af95752 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -974,8 +974,8 @@ impl Fsm for PathToolFsmState { self } (_, PathToolMessage::Overlays(mut overlay_context)) => { - let display_anchors = overlay_context.overlays_visibility_settings.anchors; - let display_handles = overlay_context.overlays_visibility_settings.handles; + let display_anchors = overlay_context.visibility_settings.anchors(); + let display_handles = overlay_context.visibility_settings.handles(); if !display_handles { shape_editor.deselect_all_handles(); } else if !display_anchors { diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 45fe84540b..c9c8b62c07 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1521,8 +1521,8 @@ impl Fsm for PenToolFsmState { self } (_, PenToolMessage::Overlays(mut overlay_context)) => { - let display_anchors = overlay_context.overlays_visibility_settings.anchors; - let display_handles = overlay_context.overlays_visibility_settings.handles; + let display_anchors = overlay_context.visibility_settings.anchors(); + let display_handles = overlay_context.visibility_settings.handles(); let valid = |point: DVec2, handle: DVec2| point.distance_squared(handle) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE; diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 4f084f8d36..a40b7ed018 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -502,7 +502,7 @@ impl Fsm for SelectToolFsmState { tool_data.selected_layers_count = selected_layers_count; // Outline selected layers, but not artboards - if overlay_context.overlays_visibility_settings.selection_outline { + if overlay_context.visibility_settings.selection_outline() { for layer in document .network_interface .selected_nodes() @@ -554,13 +554,13 @@ impl Fsm for SelectToolFsmState { let click = document.click(input); let not_selected_click = click.filter(|&hovered_layer| !document.network_interface.selected_nodes().selected_layers_contains(hovered_layer, document.metadata())); if let Some(layer) = not_selected_click { - if overlay_context.overlays_visibility_settings.hover_outline { + if overlay_context.visibility_settings.hover_outline() { overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); } // Measure with Alt held down // TODO: Don't use `Key::Alt` directly, instead take it as a variable from the input mappings list like in all other places - if overlay_context.overlays_visibility_settings.quick_measurement && !matches!(self, Self::ResizingBounds { .. }) && input.keyboard.get(Key::Alt as usize) { + if overlay_context.visibility_settings.quick_measurement() && !matches!(self, Self::ResizingBounds { .. }) && input.keyboard.get(Key::Alt as usize) { // Get all selected layers and compute their viewport-aligned AABB let selected_bounds_viewport = document .network_interface @@ -592,8 +592,7 @@ impl Fsm for SelectToolFsmState { } } - let display_transform_cage = overlay_context.overlays_visibility_settings.transform_cage; - if display_transform_cage { + if overlay_context.visibility_settings.transform_cage() { if let Some(bounds) = bounds { let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); @@ -602,8 +601,7 @@ impl Fsm for SelectToolFsmState { bounding_box_manager.transform_tampered = transform_tampered; bounding_box_manager.render_overlays(&mut overlay_context, true); } - } - else { + } else { tool_data.bounding_box_manager.take(); } @@ -634,7 +632,7 @@ impl Fsm for SelectToolFsmState { let is_resizing_or_rotating = matches!(self, SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. } | SelectToolFsmState::RotatingBounds); - if display_transform_cage { + if overlay_context.visibility_settings.transform_cage() { if let Some(bounds) = tool_data.bounding_box_manager.as_mut() { let edges = bounds.check_selected_edges(input.mouse.position); let is_skewing = matches!(self, SelectToolFsmState::SkewingBounds { .. }); @@ -669,7 +667,7 @@ impl Fsm for SelectToolFsmState { tool_data.pivot.update_pivot(document, &mut overlay_context, Some((angle,))); // Update compass rose - if overlay_context.overlays_visibility_settings.compass_rose { + if overlay_context.visibility_settings.compass_rose() { tool_data.compass_rose.refresh_position(document); let compass_center = tool_data.compass_rose.compass_rose_position(); if !matches!(self, Self::Dragging { .. }) { @@ -766,7 +764,7 @@ impl Fsm for SelectToolFsmState { SelectionMode::Directional => unreachable!(), }); - if overlay_context.overlays_visibility_settings.selection_outline { + if overlay_context.visibility_settings.selection_outline() { // Draws a temporary outline on the layers that will be selected for layer in layers_to_outline { overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index 3027ab6c26..866f987884 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -506,8 +506,7 @@ impl Fsm for TextToolFsmState { return self; } - let display_transform_cage = overlay_context.overlays_visibility_settings.transform_cage; - if display_transform_cage { + if overlay_context.visibility_settings.transform_cage() { if let Some(bounds) = bounds { let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); bounding_box_manager.bounds = [bounds.0[0], bounds.0[2]]; @@ -526,8 +525,7 @@ impl Fsm for TextToolFsmState { bounding_box_manager.render_overlays(&mut overlay_context, false); tool_data.pivot.update_pivot(document, &mut overlay_context, None); } - } - else { + } else { tool_data.bounding_box_manager.take(); } diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index 667d36d7b0..1609b58545 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -209,7 +209,7 @@ impl MessageHandler> for TransformLayer match message { // Overlays TransformLayerMessage::Overlays(mut overlay_context) => { - if !overlay_context.overlays_visibility_settings.transform_measurement { + if !overlay_context.visibility_settings.transform_measurement() { return; } From c55d6795c3a1221c075e6371245dca92e4ea1c6e Mon Sep 17 00:00:00 2001 From: seam0s Date: Sat, 19 Apr 2025 00:21:18 +0300 Subject: [PATCH 10/25] Remove debug statements --- .../portfolio/document/document_message_handler.rs | 13 +++++-------- .../document/overlays/overlays_message_handler.rs | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 660b1963df..272d0f31e5 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1157,14 +1157,14 @@ impl MessageHandler> for DocumentMessag OverlaysType::SelectionOutline => visibility_settings.selection_outline = visible, OverlaysType::Pivot => visibility_settings.pivot = visible, OverlaysType::Path => visibility_settings.path = visible, - OverlaysType::Anchors => visibility_settings.anchors = visible, + OverlaysType::Anchors => { + visibility_settings.anchors = visible; + responses.add(PortfolioMessage::UpdateDocumentWidgets); + }, OverlaysType::Handles => visibility_settings.handles = visible, } responses.add(BroadcastEvent::ToolAbort); responses.add(OverlaysMessage::Draw); - - // TODO: Updating the widgets is only needed for anchors and handles, find a better way to do this - responses.add(PortfolioMessage::UpdateDocumentWidgets); } DocumentMessage::SetRangeSelectionLayer { new_layer } => { self.layer_range_selection_reference = new_layer; @@ -1263,9 +1263,8 @@ impl MessageHandler> for DocumentMessag responses.add(OverlaysMessage::Draw); responses.add(PortfolioMessage::UpdateDocumentWidgets); } - // TODO: ToggleOverlaysVisibility does not reflect as a good name for Overlays::All DocumentMessage::ToggleOverlaysVisibility => { - self.overlays_visibility_settings.all = !self.overlays_visibility_settings.all; + self.overlays_visibility_settings.all = !self.overlays_visibility_settings.all(); responses.add(OverlaysMessage::Draw); responses.add(PortfolioMessage::UpdateDocumentWidgets); } @@ -2067,8 +2066,6 @@ impl DocumentMessageHandler { let mut snapping_state = self.snapping_state.clone(); let mut snapping_state2 = self.snapping_state.clone(); - debug!("overlays_visibility_settings: {:?}", self.overlays_visibility_settings); - let mut widgets = vec![ IconButton::new("PlaybackToStart", 24) .tooltip("Restart Animation") diff --git a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs index 94cd4266f7..b7ac62fd1e 100644 --- a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs +++ b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs @@ -65,7 +65,6 @@ impl MessageHandler> for OverlaysMessag device_pixel_ratio, visibility_settings: visibility_settings.clone(), })); - // debug!("OverlaysMessageHandler: {:?}", message.clone()); } } } From e13afc8197e57c6bc7a7384efdd5fbebfa6ddfc1 Mon Sep 17 00:00:00 2001 From: seam0s-dev <153828136+seam0s-dev@users.noreply.github.com> Date: Sat, 19 Apr 2025 00:56:18 +0300 Subject: [PATCH 11/25] Update select_tool.rs to resolve conflict --- .../tool/tool_messages/select_tool.rs | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index d4ef3af3cd..a9dcbfd377 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -717,24 +717,25 @@ impl Fsm for SelectToolFsmState { let angle = -mouse_position.angle_to(DVec2::X); let snapped_angle = (angle / snap_resolution).round() * snap_resolution; - let extension = tool_data.drag_current - tool_data.drag_start; - let origin = compass_center - extension; - let viewport_diagonal = input.viewport_bounds.size().length(); - - let edge = DVec2::from_angle(snapped_angle).normalize_or(DVec2::X) * viewport_diagonal; - let perp = edge.perp(); - - let (edge_color, perp_color) = if edge.x.abs() > edge.y.abs() { - (COLOR_OVERLAY_RED, COLOR_OVERLAY_GREEN) - } else { - (COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED) - }; - let mut perp_color = graphene_std::Color::from_rgb_str(perp_color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb(); - perp_color.insert(0, '#'); - let perp_color = perp_color.as_str(); - overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(edge_color), None); - overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(perp_color), None); - } + let extension = tool_data.drag_current - tool_data.drag_start; + let origin = compass_center - extension; + let viewport_diagonal = input.viewport_bounds.size().length(); + + let edge = DVec2::from_angle(snapped_angle).normalize_or(DVec2::X) * viewport_diagonal; + let perp = edge.perp(); + + let (edge_color, perp_color) = if edge.x.abs() > edge.y.abs() { + (COLOR_OVERLAY_RED, COLOR_OVERLAY_GREEN) + } else { + (COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED) + }; + let mut perp_color = graphene_std::Color::from_rgb_str(perp_color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb(); + perp_color.insert(0, '#'); + let perp_color = perp_color.as_str(); + overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(edge_color), None); + overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(perp_color), None); + } + } // Check if the tool is in selection mode if let Self::Drawing { selection_shape, .. } = self { From 792893ae6bbb4facf2366493ad8615b8a0ce369e Mon Sep 17 00:00:00 2001 From: seam0s Date: Sat, 19 Apr 2025 10:49:10 +0300 Subject: [PATCH 12/25] Minor fix to reflect anchor checkbox state on the handles --- .../src/messages/portfolio/document/overlays/utility_types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index 35702849c2..9c553731b4 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -117,7 +117,7 @@ impl OverlaysVisibilitySettings { } pub fn handles(&self) -> bool { - self.all && self.handles + self.all && self.anchors && self.handles } } From 857fb706eb89f1700dd5885d7af0a3f658426b20 Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Thu, 24 Apr 2025 08:31:23 +0300 Subject: [PATCH 13/25] Minor fix to make anchors checkbox work --- editor/src/messages/tool/tool_messages/path_tool.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 1c64aa0d1b..c28b017b77 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -979,7 +979,8 @@ impl Fsm for PathToolFsmState { let display_handles = overlay_context.visibility_settings.handles(); if !display_handles { shape_editor.deselect_all_handles(); - } else if !display_anchors { + } + if !display_anchors { shape_editor.deselect_all_anchors(); } From 4151b795107320d177c59c4a9fedd64bcc5b5f0f Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Thu, 24 Apr 2025 02:06:42 -0700 Subject: [PATCH 14/25] Rearrange menu items, and code review --- .../layout/utility_types/layout_widget.rs | 2 +- .../document/document_message_handler.rs | 64 ++++++++-------- .../overlays/overlays_message_handler.rs | 3 +- .../tool/common_functionality/shape_editor.rs | 11 +-- .../tool/tool_messages/gradient_tool.rs | 1 + .../tool/tool_messages/select_tool.rs | 38 +++++----- libraries/math-parser/benches/bench.rs | 74 +++++++++---------- 7 files changed, 92 insertions(+), 101 deletions(-) diff --git a/editor/src/messages/layout/utility_types/layout_widget.rs b/editor/src/messages/layout/utility_types/layout_widget.rs index 8e5bd94829..6ebf10203b 100644 --- a/editor/src/messages/layout/utility_types/layout_widget.rs +++ b/editor/src/messages/layout/utility_types/layout_widget.rs @@ -49,7 +49,7 @@ pub enum LayoutTarget { WorkingColors, // KEEP THIS ENUM LAST - // This is a marker that is used to define an array to hold widgets + // This is a marker that is used to define an array that is used to hold widgets LayoutTargetLength, } diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 56c53e94a5..1df4afbb6c 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -200,14 +200,14 @@ impl MessageHandler> for DocumentMessag self.navigation_handler.process_message(message, responses, data); } DocumentMessage::Overlays(message) => { - let overlays_visibility_settings = self.overlays_visibility_settings; + let visibility_settings = self.overlays_visibility_settings; // Send the overlays message to the overlays message handler self.overlays_message_handler.process_message( message, responses, OverlaysMessageData { - visibility_settings: overlays_visibility_settings, + visibility_settings, ipp, device_pixel_ratio, }, @@ -1157,10 +1157,10 @@ impl MessageHandler> for DocumentMessag OverlaysType::SelectionOutline => visibility_settings.selection_outline = visible, OverlaysType::Pivot => visibility_settings.pivot = visible, OverlaysType::Path => visibility_settings.path = visible, - OverlaysType::Anchors => { + OverlaysType::Anchors => { visibility_settings.anchors = visible; responses.add(PortfolioMessage::UpdateDocumentWidgets); - }, + } OverlaysType::Handles => visibility_settings.handles = visible, } responses.add(BroadcastEvent::ToolAbort); @@ -2096,10 +2096,12 @@ impl DocumentMessageHandler { LayoutGroup::Row { widgets: vec![TextLabel::new("Overlays").bold(true).widget_holder()], }, + LayoutGroup::Row { + widgets: vec![TextLabel::new("General").widget_holder()], + }, LayoutGroup::Row { widgets: vec![ CheckboxInput::new(self.overlays_visibility_settings.artboard_name) - .tooltip("Enable or disable the artboard names overlay") .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, @@ -2113,23 +2115,24 @@ impl DocumentMessageHandler { }, LayoutGroup::Row { widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.compass_rose) - .tooltip("Enable or disable the compass rose overlay") + CheckboxInput::new(self.overlays_visibility_settings.transform_measurement) .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::CompassRose, + overlays_type: OverlaysType::TransformMeasurement, } .into() }) .widget_holder(), - TextLabel::new("Compass Rose".to_string()).widget_holder(), + TextLabel::new("G/R/S Measurement".to_string()).widget_holder(), ], }, + LayoutGroup::Row { + widgets: vec![TextLabel::new("Select Tool").widget_holder()], + }, LayoutGroup::Row { widgets: vec![ CheckboxInput::new(self.overlays_visibility_settings.quick_measurement) - .tooltip("Enable or disable the quick measurement overlay") .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, @@ -2143,83 +2146,80 @@ impl DocumentMessageHandler { }, LayoutGroup::Row { widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.transform_measurement) - .tooltip("Enable or disable the transform measurement overlay") + CheckboxInput::new(self.overlays_visibility_settings.transform_cage) .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::TransformMeasurement, + overlays_type: OverlaysType::TransformCage, } .into() }) .widget_holder(), - TextLabel::new("Transform Measurement".to_string()).widget_holder(), + TextLabel::new("Transform Cage".to_string()).widget_holder(), ], }, LayoutGroup::Row { widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.transform_cage) - .tooltip("Enable or disable the transform cage overlay") + CheckboxInput::new(self.overlays_visibility_settings.compass_rose) .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::TransformCage, + overlays_type: OverlaysType::CompassRose, } .into() }) .widget_holder(), - TextLabel::new("Transform Cage".to_string()).widget_holder(), + TextLabel::new("Transform Dial".to_string()).widget_holder(), ], }, LayoutGroup::Row { widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.hover_outline) - .tooltip("Enable or disable the hover outline overlay") + CheckboxInput::new(self.overlays_visibility_settings.pivot) .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::HoverOutline, + overlays_type: OverlaysType::Pivot, } .into() }) .widget_holder(), - TextLabel::new("Hover Outline".to_string()).widget_holder(), + TextLabel::new("Transform Pivot".to_string()).widget_holder(), ], }, LayoutGroup::Row { widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.selection_outline) - .tooltip("Enable or disable the selection outline overlay") + CheckboxInput::new(self.overlays_visibility_settings.hover_outline) .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::SelectionOutline, + overlays_type: OverlaysType::HoverOutline, } .into() }) .widget_holder(), - TextLabel::new("Selection Outline".to_string()).widget_holder(), + TextLabel::new("Hover Outline".to_string()).widget_holder(), ], }, LayoutGroup::Row { widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.pivot) - .tooltip("Enable or disable the pivot overlay") + CheckboxInput::new(self.overlays_visibility_settings.selection_outline) .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::Pivot, + overlays_type: OverlaysType::SelectionOutline, } .into() }) .widget_holder(), - TextLabel::new("Pivot".to_string()).widget_holder(), + TextLabel::new("Selection Outline".to_string()).widget_holder(), ], }, + LayoutGroup::Row { + widgets: vec![TextLabel::new("Pen & Path Tools").widget_holder()], + }, LayoutGroup::Row { widgets: vec![ CheckboxInput::new(self.overlays_visibility_settings.path) - .tooltip("Enable or disable the path overlay") .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, @@ -2234,7 +2234,6 @@ impl DocumentMessageHandler { LayoutGroup::Row { widgets: vec![ CheckboxInput::new(self.overlays_visibility_settings.anchors) - .tooltip("Enable or disable the anchors overlay") .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, @@ -2250,7 +2249,6 @@ impl DocumentMessageHandler { widgets: vec![ CheckboxInput::new(self.overlays_visibility_settings.handles) .disabled(!self.overlays_visibility_settings.anchors) - .tooltip("Enable or disable the handles overlay") .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, diff --git a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs index 27ad713a93..97daaa91ea 100644 --- a/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs +++ b/editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs @@ -1,5 +1,4 @@ -use super::utility_types::OverlayProvider; -use super::utility_types::OverlaysVisibilitySettings; +use super::utility_types::{OverlayProvider, OverlaysVisibilitySettings}; use crate::messages::prelude::*; pub struct OverlaysMessageData<'a> { diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index da23bc49e9..53b8dad84a 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -502,10 +502,7 @@ impl ShapeState { let selected_anchor_points: Vec = state .selected_points .iter() - .filter(|selected_point| match selected_point { - ManipulatorPointId::Anchor(_) => true, - _ => false, - }) + .filter(|selected_point| matches!(selected_point, ManipulatorPointId::Anchor(_))) .cloned() .collect(); @@ -521,11 +518,7 @@ impl ShapeState { let selected_handle_points: Vec = state .selected_points .iter() - .filter(|selected_point| match selected_point { - ManipulatorPointId::PrimaryHandle(_) => true, - ManipulatorPointId::EndHandle(_) => true, - _ => false, - }) + .filter(|selected_point| matches!(selected_point, ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_))) .cloned() .collect(); diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index 65e76e9f5c..8080ac9dcb 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -265,6 +265,7 @@ impl Fsm for GradientToolFsmState { overlay_context.line(start, end, None, None); overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start), None); overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End), None); + for (index, (position, _)) in stops.into_iter().enumerate() { if position.abs() < f64::EPSILON * 1000. || (1. - position).abs() < f64::EPSILON * 1000. { continue; diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 43f5f248b3..76b5bea6ed 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -717,24 +717,24 @@ impl Fsm for SelectToolFsmState { let angle = -mouse_position.angle_to(DVec2::X); let snapped_angle = (angle / snap_resolution).round() * snap_resolution; - let extension = tool_data.drag_current - tool_data.drag_start; - let origin = compass_center - extension; - let viewport_diagonal = input.viewport_bounds.size().length(); - - let edge = DVec2::from_angle(snapped_angle).normalize_or(DVec2::X) * viewport_diagonal; - let perp = edge.perp(); - - let (edge_color, perp_color) = if edge.x.abs() > edge.y.abs() { - (COLOR_OVERLAY_RED, COLOR_OVERLAY_GREEN) - } else { - (COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED) - }; - let mut perp_color = graphene_std::Color::from_rgb_str(perp_color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb(); - perp_color.insert(0, '#'); - let perp_color = perp_color.as_str(); - overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(edge_color), None); - overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(perp_color), None); - } + let extension = tool_data.drag_current - tool_data.drag_start; + let origin = compass_center - extension; + let viewport_diagonal = input.viewport_bounds.size().length(); + + let edge = DVec2::from_angle(snapped_angle).normalize_or(DVec2::X) * viewport_diagonal; + let perp = edge.perp(); + + let (edge_color, perp_color) = if edge.x.abs() > edge.y.abs() { + (COLOR_OVERLAY_RED, COLOR_OVERLAY_GREEN) + } else { + (COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED) + }; + let mut perp_color = graphene_std::Color::from_rgb_str(perp_color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb(); + perp_color.insert(0, '#'); + let perp_color = perp_color.as_str(); + overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(edge_color), None); + overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(perp_color), None); + } } // Check if the tool is in selection mode @@ -766,7 +766,7 @@ impl Fsm for SelectToolFsmState { }); if overlay_context.visibility_settings.selection_outline() { - // Draws a temporary outline on the layers that will be selected + // Draws a temporary outline on the layers that will be selected by the current box/lasso area for layer in layers_to_outline { overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); } diff --git a/libraries/math-parser/benches/bench.rs b/libraries/math-parser/benches/bench.rs index a1952bc2fa..fd1824c9a0 100644 --- a/libraries/math-parser/benches/bench.rs +++ b/libraries/math-parser/benches/bench.rs @@ -3,47 +3,47 @@ use math_parser::ast; use math_parser::context::EvalContext; macro_rules! generate_benchmarks { - ($( $input:expr_2021 ),* $(,)?) => { - fn parsing_bench(c: &mut Criterion) { - $( - c.bench_function(concat!("parse ", $input), |b| { - b.iter(|| { - let _ = black_box(ast::Node::try_parse_from_str($input)).unwrap(); - }); - }); - )* - } + ($( $input:expr_2021 ),* $(,)?) => { + fn parsing_bench(c: &mut Criterion) { + $( + c.bench_function(concat!("parse ", $input), |b| { + b.iter(|| { + let _ = black_box(ast::Node::try_parse_from_str($input)).unwrap(); + }); + }); + )* + } - fn evaluation_bench(c: &mut Criterion) { - $( - let expr = ast::Node::try_parse_from_str($input).unwrap().0; - let context = EvalContext::default(); + fn evaluation_bench(c: &mut Criterion) { + $( + let expr = ast::Node::try_parse_from_str($input).unwrap().0; + let context = EvalContext::default(); - c.bench_function(concat!("eval ", $input), |b| { - b.iter(|| { - let _ = black_box(expr.eval(&context)); - }); - }); - )* - } + c.bench_function(concat!("eval ", $input), |b| { + b.iter(|| { + let _ = black_box(expr.eval(&context)); + }); + }); + )* + } - criterion_group!(benches, parsing_bench, evaluation_bench); - criterion_main!(benches); - }; + criterion_group!(benches, parsing_bench, evaluation_bench); + criterion_main!(benches); + }; } generate_benchmarks! { - "(3 * (4 + sqrt(25)) - cos(pi/3) * (2^3)) + 5 * e", // Mixed nested functions, constants, and operations - "((5 + 2 * (3 - sqrt(49)))^2) / (1 + sqrt(16)) + tau / 2", // Complex nested expression with constants - "log(100, 10) + (5 * sin(pi/4) + sqrt(81)) / (2 * phi)", // Logarithmic and trigonometric functions - "(sqrt(144) * 2 + 5) / (3 * (4 - sin(pi / 6))) + e^2", // Combined square root, trigonometric, and exponential operations - "cos(2 * pi) + tan(pi / 3) * log(32, 2) - sqrt(256)", // Multiple trigonometric and logarithmic functions - "(10 * (3 + 2) - 8 / 2)^2 + 7 * (2^4) - sqrt(225) + phi", // Mixed arithmetic with constants - "(5^2 + 3^3) * (sqrt(81) + sqrt(64)) - tau * log(1000, 10)", // Power and square root with constants - "((8 * sqrt(49) - 2 * e) + log(256, 2) / (2 + cos(pi))) * 1.5", // Nested functions and constants - "(tan(pi / 4) + 5) * (3 + sqrt(36)) / (log(1024, 2) - 4)", // Nested functions with trigonometry and logarithm - "((3 * e + 2 * sqrt(100)) - cos(tau / 4)) * log(27, 3) + phi", // Mixed constant usage and functions - "(sqrt(100) + 5 * sin(pi / 6) - 8 / log(64, 2)) + e^(1.5)", // Complex mix of square root, division, and exponentiation - "((sin(pi/2) + cos(0)) * (e^2 - 2 * sqrt(16))) / (log(100, 10) + pi)", // Nested trigonometric, exponential, and logarithmic functions - "(5 * (7 + sqrt(121)) - (log(243, 3) * phi)) + 3^5 / tau", // + "(3 * (4 + sqrt(25)) - cos(pi/3) * (2^3)) + 5 * e", // Mixed nested functions, constants, and operations + "((5 + 2 * (3 - sqrt(49)))^2) / (1 + sqrt(16)) + tau / 2", // Complex nested expression with constants + "log(100, 10) + (5 * sin(pi/4) + sqrt(81)) / (2 * phi)", // Logarithmic and trigonometric functions + "(sqrt(144) * 2 + 5) / (3 * (4 - sin(pi / 6))) + e^2", // Combined square root, trigonometric, and exponential operations + "cos(2 * pi) + tan(pi / 3) * log(32, 2) - sqrt(256)", // Multiple trigonometric and logarithmic functions + "(10 * (3 + 2) - 8 / 2)^2 + 7 * (2^4) - sqrt(225) + phi", // Mixed arithmetic with constants + "(5^2 + 3^3) * (sqrt(81) + sqrt(64)) - tau * log(1000, 10)", // Power and square root with constants + "((8 * sqrt(49) - 2 * e) + log(256, 2) / (2 + cos(pi))) * 1.5", // Nested functions and constants + "(tan(pi / 4) + 5) * (3 + sqrt(36)) / (log(1024, 2) - 4)", // Nested functions with trigonometry and logarithm + "((3 * e + 2 * sqrt(100)) - cos(tau / 4)) * log(27, 3) + phi", // Mixed constant usage and functions + "(sqrt(100) + 5 * sin(pi / 6) - 8 / log(64, 2)) + e^(1.5)", // Complex mix of square root, division, and exponentiation + "((sin(pi/2) + cos(0)) * (e^2 - 2 * sqrt(16))) / (log(100, 10) + pi)", // Nested trigonometric, exponential, and logarithmic functions + "(5 * (7 + sqrt(121)) - (log(243, 3) * phi)) + 3^5 / tau", // } From 2fcc50f36ae2e2850ba3ca8d4a3c59d2e2c1664e Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Sun, 27 Apr 2025 18:24:04 +0300 Subject: [PATCH 15/25] Fix pivot dragging --- .../tool/common_functionality/pivot.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index 157e77c30b..2b7aca30fe 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -19,6 +19,8 @@ pub struct Pivot { pivot: Option, /// The old pivot position in the GUI, used to reduce refreshes of the document bar old_pivot_position: PivotPosition, + /// Used to enable and disable the pivot + active: bool } impl Default for Pivot { @@ -28,6 +30,7 @@ impl Default for Pivot { transform_from_normalized: Default::default(), pivot: Default::default(), old_pivot_position: PivotPosition::Center, + active: true } } } @@ -44,6 +47,8 @@ impl Pivot { /// Recomputes the pivot position and transform. fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) { + if !self.active { return; } + let selected_nodes = document.network_interface.selected_nodes(); let mut layers = selected_nodes.selected_visible_and_unlocked_layers(&document.network_interface); let Some(first) = layers.next() else { @@ -83,8 +88,12 @@ impl Pivot { pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, draw_data: Option<(f64,)>) { if !overlay_context.visibility_settings.pivot() { + self.active = false; return; } + else { + self.active = true; + } self.recalculate_pivot(document); if let (Some(pivot), Some(data)) = (self.pivot, draw_data) { @@ -94,6 +103,10 @@ impl Pivot { /// Answers if the pivot widget has changed (so we should refresh the tool bar at the top of the canvas). pub fn should_refresh_pivot_position(&mut self) -> bool { + if !self.active { + return false; + } + let new = self.to_pivot_position(); let should_refresh = new != self.old_pivot_position; self.old_pivot_position = new; @@ -106,6 +119,8 @@ impl Pivot { /// Sets the viewport position of the pivot for all selected layers. pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { + if !self.active { return; } + for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) { let transform = Self::get_layer_pivot_transform(layer, document); // Only update the pivot when computed position is finite. @@ -119,11 +134,16 @@ impl Pivot { /// Set the pivot using the normalized transform that is set above. pub fn set_normalized_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { + if !self.active { return; } + self.set_viewport_position(self.transform_from_normalized.transform_point2(position), document, responses); } /// Answers if the pointer is currently positioned over the pivot. pub fn is_over(&self, mouse: DVec2) -> bool { + if !self.active { + return false; + } self.pivot.filter(|&pivot| mouse.distance_squared(pivot) < (PIVOT_DIAMETER / 2.).powi(2)).is_some() } } From 5fd735b4a3d7dcadf023cba23c3374f969de1f74 Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Sun, 27 Apr 2025 18:24:50 +0300 Subject: [PATCH 16/25] Add handles overlay check when drawing with pen tool --- .../messages/tool/tool_messages/pen_tool.rs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 690ba7c8ee..f0ceb00b9c 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1526,9 +1526,10 @@ impl Fsm for PenToolFsmState { } } - // Draw the line between the currently-being-placed anchor and its currently-being-dragged-out outgoing handle (opposite the one currently being dragged out) - overlay_context.line(next_anchor, next_handle_start, None, None); - + if display_handles { + // Draw the line between the currently-being-placed anchor and its currently-being-dragged-out outgoing handle (opposite the one currently being dragged out) + overlay_context.line(next_anchor, next_handle_start, None, None); + } match tool_options.pen_overlay_mode { PenOverlayMode::AllHandles => { path_overlays(document, DrawHandles::All, shape_editor, &mut overlay_context); @@ -1543,11 +1544,13 @@ impl Fsm for PenToolFsmState { } if let (Some(anchor_start), Some(handle_start), Some(handle_end)) = (anchor_start, handle_start, handle_end) { - // Draw the line between the most recently placed anchor and its outgoing handle (which is currently influencing the currently-being-placed segment) - overlay_context.line(anchor_start, handle_start, None, None); + if display_handles { + // Draw the line between the most recently placed anchor and its outgoing handle (which is currently influencing the currently-being-placed segment) + overlay_context.line(anchor_start, handle_start, None, None); - // Draw the line between the currently-being-placed anchor and its incoming handle (opposite the one currently being dragged out) - overlay_context.line(next_anchor, handle_end, None, None); + // Draw the line between the currently-being-placed anchor and its incoming handle (opposite the one currently being dragged out) + overlay_context.line(next_anchor, handle_end, None, None); + } if self == PenToolFsmState::PlacingAnchor && anchor_start != handle_start && tool_data.modifiers.lock_angle { // Draw the line between the currently-being-placed anchor and last-placed point (lock angle bent overlays) @@ -1562,7 +1565,10 @@ impl Fsm for PenToolFsmState { if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) && valid(next_anchor, handle_end) && display_handles { // Draw the handle circle for the currently-being-dragged-out incoming handle (opposite the one currently being dragged out) let selected = tool_data.handle_type == TargetHandle::PreviewInHandle; - overlay_context.manipulator_handle(handle_end, selected, None); + if display_handles { + overlay_context.manipulator_handle(handle_end, selected, None); + overlay_context.manipulator_handle(handle_end, selected, None); + } } if valid(anchor_start, handle_start) && display_handles { From b7fb45342bf916e8ff8442b84845e152080f0efb Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Sun, 27 Apr 2025 18:26:40 +0300 Subject: [PATCH 17/25] Fix constrained dragging when transform cage is disabled --- .../tool/tool_messages/select_tool.rs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 76b5bea6ed..0653590e19 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -632,18 +632,16 @@ impl Fsm for SelectToolFsmState { let is_resizing_or_rotating = matches!(self, SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. } | SelectToolFsmState::RotatingBounds); - if overlay_context.visibility_settings.transform_cage() { - if let Some(bounds) = tool_data.bounding_box_manager.as_mut() { - let edges = bounds.check_selected_edges(input.mouse.position); - let is_skewing = matches!(self, SelectToolFsmState::SkewingBounds { .. }); - let is_near_square = edges.is_some_and(|hover_edge| bounds.over_extended_edge_midpoint(input.mouse.position, hover_edge)); - if is_skewing || (dragging_bounds && is_near_square && !is_resizing_or_rotating) { - bounds.render_skew_gizmos(&mut overlay_context, tool_data.skew_edge); - } - if !is_skewing && dragging_bounds { - if let Some(edges) = edges { - tool_data.skew_edge = bounds.get_closest_edge(edges, input.mouse.position); - } + if let Some(bounds) = tool_data.bounding_box_manager.as_mut() { + let edges = bounds.check_selected_edges(input.mouse.position); + let is_skewing = matches!(self, SelectToolFsmState::SkewingBounds { .. }); + let is_near_square = edges.is_some_and(|hover_edge| bounds.over_extended_edge_midpoint(input.mouse.position, hover_edge)); + if is_skewing || (dragging_bounds && is_near_square && !is_resizing_or_rotating) { + bounds.render_skew_gizmos(&mut overlay_context, tool_data.skew_edge); + } + if !is_skewing && dragging_bounds { + if let Some(edges) = edges { + tool_data.skew_edge = bounds.get_closest_edge(edges, input.mouse.position); } } } @@ -854,7 +852,8 @@ impl Fsm for SelectToolFsmState { let is_over_pivot = tool_data.pivot.is_over(mouse_position); let show_compass = bounds.is_some_and(|quad| quad.all_sides_at_least_width(COMPASS_ROSE_HOVER_RING_DIAMETER) && quad.contains(mouse_position)); - let can_grab_compass_rose = compass_rose_state.can_grab() && show_compass; + /// If bounding box is some, compass_rose_state.can_grab() && show_compass is evaluated else compass_rose_state.can_grab() is evaluated + let can_grab_compass_rose = compass_rose_state.can_grab() && (show_compass || bounds.is_none()); let is_flat_layer = tool_data .bounding_box_manager .as_ref() @@ -917,7 +916,7 @@ impl Fsm for SelectToolFsmState { // Dragging the selected layers around to transform them else if can_grab_compass_rose || intersection.is_some_and(|intersection| selected.iter().any(|selected_layer| intersection.starts_with(*selected_layer, document.metadata()))) { responses.add(DocumentMessage::StartTransaction); - + if input.keyboard.key(select_deepest) || tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest { tool_data.select_single_layer = intersection; } else { From 623e263b309f492c2d21dac29031de9b5b9b16f4 Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Sun, 27 Apr 2025 21:45:54 +0300 Subject: [PATCH 18/25] Fix deselecting user selection when anchors are disabled --- .../tool/common_functionality/shape_editor.rs | 44 +++++++++++++++++-- .../messages/tool/tool_messages/path_tool.rs | 10 ++++- .../tool/tool_messages/select_tool.rs | 21 ++++++--- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 53b8dad84a..d5fde65b19 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -42,6 +42,8 @@ pub enum ManipulatorAngle { #[derive(Clone, Debug, Default)] pub struct SelectedLayerState { selected_points: HashSet, + ignore_handles: bool, + ignore_anchors: bool, } impl SelectedLayerState { @@ -52,11 +54,23 @@ impl SelectedLayerState { self.selected_points.contains(&point) } pub fn select_point(&mut self, point: ManipulatorPointId) { + if (point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors) { + return; + } self.selected_points.insert(point); } pub fn deselect_point(&mut self, point: ManipulatorPointId) { + if (point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors) { + return; + } self.selected_points.remove(&point); } + pub fn set_handles_status(&mut self, ignore: bool) { + self.ignore_handles = ignore; + } + pub fn set_anchors_status(&mut self, ignore: bool) { + self.ignore_anchors = ignore; + } pub fn clear_points(&mut self) { self.selected_points.clear(); } @@ -496,13 +510,37 @@ impl ShapeState { } } + pub fn mark_selected_anchors(&mut self) { + for state in self.selected_shape_state.values_mut() { + state.set_anchors_status(false); + } + } + + pub fn mark_selected_handles(&mut self) { + for state in self.selected_shape_state.values_mut() { + state.set_handles_status(false); + } + } + + pub fn ignore_selected_anchors(&mut self) { + for state in self.selected_shape_state.values_mut() { + state.set_anchors_status(true); + } + } + + pub fn ignore_selected_handles(&mut self) { + for state in self.selected_shape_state.values_mut() { + state.set_handles_status(true); + } + } + /// Deselects all the anchors across every selected layer. pub fn deselect_all_anchors(&mut self) { for (_, state) in self.selected_shape_state.iter_mut() { let selected_anchor_points: Vec = state .selected_points .iter() - .filter(|selected_point| matches!(selected_point, ManipulatorPointId::Anchor(_))) + .filter(|selected_point| selected_point.as_anchor().is_some()) .cloned() .collect(); @@ -518,7 +556,7 @@ impl ShapeState { let selected_handle_points: Vec = state .selected_points .iter() - .filter(|selected_point| matches!(selected_point, ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_))) + .filter(|selected_point| selected_point.as_handle().is_some()) .cloned() .collect(); @@ -636,7 +674,7 @@ impl ShapeState { Some(()) } - /// Iterates over the selected manipulator groups exluding endpoints, returning whether their handles have mixed, colinear, or free angles. + /// Iterates over the selected manipulator groups excluding endpoints, returning whether their handles have mixed, colinear, or free angles. /// If there are no points selected this function returns mixed. pub fn selected_manipulator_angles(&self, network_interface: &NodeNetworkInterface) -> ManipulatorAngle { // This iterator contains a bool indicating whether or not selected points' manipulator groups have colinear handles. diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index fbc018c2aa..8756294f82 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1015,10 +1015,16 @@ impl Fsm for PathToolFsmState { let display_anchors = overlay_context.visibility_settings.anchors(); let display_handles = overlay_context.visibility_settings.handles(); if !display_handles { - shape_editor.deselect_all_handles(); + shape_editor.ignore_selected_handles(); + } + else { + shape_editor.mark_selected_handles(); } if !display_anchors { - shape_editor.deselect_all_anchors(); + shape_editor.ignore_selected_anchors(); + } + else { + shape_editor.mark_selected_anchors(); } // TODO: find the segment ids of which the selected points are a part of diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 0653590e19..af3b025f50 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -852,7 +852,7 @@ impl Fsm for SelectToolFsmState { let is_over_pivot = tool_data.pivot.is_over(mouse_position); let show_compass = bounds.is_some_and(|quad| quad.all_sides_at_least_width(COMPASS_ROSE_HOVER_RING_DIAMETER) && quad.contains(mouse_position)); - /// If bounding box is some, compass_rose_state.can_grab() && show_compass is evaluated else compass_rose_state.can_grab() is evaluated + // If bounding box is some, compass_rose_state.can_grab() && show_compass is evaluated else compass_rose_state.can_grab() is evaluated let can_grab_compass_rose = compass_rose_state.can_grab() && (show_compass || bounds.is_none()); let is_flat_layer = tool_data .bounding_box_manager @@ -916,7 +916,9 @@ impl Fsm for SelectToolFsmState { // Dragging the selected layers around to transform them else if can_grab_compass_rose || intersection.is_some_and(|intersection| selected.iter().any(|selected_layer| intersection.starts_with(*selected_layer, document.metadata()))) { responses.add(DocumentMessage::StartTransaction); - + debug!("Starting drag on the layers!"); + debug!("can_grab_compass_rose: {:?} ---> compass_rose_state.can_grab(): {:?}, show_compass: {:?}", can_grab_compass_rose, compass_rose_state.can_grab(), show_compass); + if input.keyboard.key(select_deepest) || tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest { tool_data.select_single_layer = intersection; } else { @@ -1024,9 +1026,18 @@ impl Fsm for SelectToolFsmState { let mouse_delta = snap_drag(start, current, tool_data.axis_align, axis, snap_data, &mut tool_data.snap_manager, &tool_data.snap_candidates); let mouse_delta = match axis { - Axis::X => mouse_delta.project_onto(e0), - Axis::Y => mouse_delta.project_onto(e0.perp()), - Axis::None => mouse_delta, + Axis::X => { + debug!("The axis is on X!"); + mouse_delta.project_onto(e0) + }, + Axis::Y => { + debug!("The axis is on Y!"); + mouse_delta.project_onto(e0.perp()) + }, + Axis::None => { + debug!("The axis is on both X and Y!"); + 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 From 16c0c4fa8b51a11962d79f23ca5132637198375f Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Mon, 28 Apr 2025 12:37:51 +0300 Subject: [PATCH 19/25] Minor fix for disabling anchors --- editor/src/messages/tool/common_functionality/shape_editor.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index d5fde65b19..c690aae177 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -72,6 +72,9 @@ impl SelectedLayerState { self.ignore_anchors = ignore; } pub fn clear_points(&mut self) { + if self.ignore_handles || self.ignore_anchors { + return; + } self.selected_points.clear(); } pub fn selected_points_count(&self) -> usize { From 1634862b1dd9a1c427019f71e3caa0f184f56a33 Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Mon, 28 Apr 2025 13:11:02 +0300 Subject: [PATCH 20/25] Remove All from OverlaysType --- .../portfolio/document/document_message.rs | 2 +- .../document/document_message_handler.rs | 33 +++++++++++-------- .../document/overlays/utility_types.rs | 1 - 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index 099e842b3b..f6d2f1470a 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -144,7 +144,7 @@ pub enum DocumentMessage { }, SetOverlaysVisibility { visible: bool, - overlays_type: OverlaysType, + overlays_type: Option, }, SetRangeSelectionLayer { new_layer: Option, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 1df4afbb6c..cede5c0e48 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1146,8 +1146,14 @@ impl MessageHandler> for DocumentMessag } DocumentMessage::SetOverlaysVisibility { visible, overlays_type } => { let visibility_settings = &mut self.overlays_visibility_settings; + let overlays_type = match overlays_type { + Some(overlays_type) => overlays_type, + None => { + visibility_settings.all = visible; + return; + } + }; match overlays_type { - OverlaysType::All => visibility_settings.all = visible, OverlaysType::ArtboardName => visibility_settings.artboard_name = visible, OverlaysType::CompassRose => visibility_settings.compass_rose = visible, OverlaysType::QuickMeasurement => visibility_settings.quick_measurement = visible, @@ -1163,6 +1169,7 @@ impl MessageHandler> for DocumentMessag } OverlaysType::Handles => visibility_settings.handles = visible, } + responses.add(BroadcastEvent::ToolAbort); responses.add(OverlaysMessage::Draw); } @@ -2086,7 +2093,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::All, + overlays_type: None, } .into() }) @@ -2105,7 +2112,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::ArtboardName, + overlays_type: Some(OverlaysType::ArtboardName), } .into() }) @@ -2119,7 +2126,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::TransformMeasurement, + overlays_type: Some(OverlaysType::TransformMeasurement), } .into() }) @@ -2136,7 +2143,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::QuickMeasurement, + overlays_type: Some(OverlaysType::QuickMeasurement), } .into() }) @@ -2150,7 +2157,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::TransformCage, + overlays_type: Some(OverlaysType::TransformCage), } .into() }) @@ -2164,7 +2171,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::CompassRose, + overlays_type: Some(OverlaysType::CompassRose), } .into() }) @@ -2178,7 +2185,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::Pivot, + overlays_type: Some(OverlaysType::Pivot), } .into() }) @@ -2192,7 +2199,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::HoverOutline, + overlays_type: Some(OverlaysType::HoverOutline), } .into() }) @@ -2206,7 +2213,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::SelectionOutline, + overlays_type: Some(OverlaysType::SelectionOutline), } .into() }) @@ -2223,7 +2230,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::Path, + overlays_type: Some(OverlaysType::Path), } .into() }) @@ -2237,7 +2244,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::Anchors, + overlays_type: Some(OverlaysType::Anchors), } .into() }) @@ -2252,7 +2259,7 @@ impl DocumentMessageHandler { .on_update(|optional_input: &CheckboxInput| { DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked, - overlays_type: OverlaysType::Handles, + overlays_type: Some(OverlaysType::Handles), } .into() }) diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index a33153ccd6..3794481c31 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -24,7 +24,6 @@ pub fn empty_provider() -> OverlayProvider { // Types of overlays used by DocumentMessage to enable/disable select group of overlays in the frontend #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] pub enum OverlaysType { - All, ArtboardName, CompassRose, QuickMeasurement, From ddcbafcac9bd1d190572519076711f0647d1c587 Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Mon, 28 Apr 2025 14:47:55 +0300 Subject: [PATCH 21/25] Remove debug statements --- .../messages/tool/tool_messages/select_tool.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index af3b025f50..070ed5fdc3 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -916,8 +916,6 @@ impl Fsm for SelectToolFsmState { // Dragging the selected layers around to transform them else if can_grab_compass_rose || intersection.is_some_and(|intersection| selected.iter().any(|selected_layer| intersection.starts_with(*selected_layer, document.metadata()))) { responses.add(DocumentMessage::StartTransaction); - debug!("Starting drag on the layers!"); - debug!("can_grab_compass_rose: {:?} ---> compass_rose_state.can_grab(): {:?}, show_compass: {:?}", can_grab_compass_rose, compass_rose_state.can_grab(), show_compass); if input.keyboard.key(select_deepest) || tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest { tool_data.select_single_layer = intersection; @@ -1026,18 +1024,9 @@ impl Fsm for SelectToolFsmState { let mouse_delta = snap_drag(start, current, tool_data.axis_align, axis, snap_data, &mut tool_data.snap_manager, &tool_data.snap_candidates); let mouse_delta = match axis { - Axis::X => { - debug!("The axis is on X!"); - mouse_delta.project_onto(e0) - }, - Axis::Y => { - debug!("The axis is on Y!"); - mouse_delta.project_onto(e0.perp()) - }, - Axis::None => { - debug!("The axis is on both X and Y!"); - mouse_delta - }, + Axis::X => mouse_delta.project_onto(e0), + Axis::Y => mouse_delta.project_onto(e0.perp()), + Axis::None => 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 From 4e6f773c58a43780849a2940d39b83c363e340fc Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Tue, 29 Apr 2025 17:16:56 +0300 Subject: [PATCH 22/25] Fix editor crash when selecting other layers with path tool and anchors disabled --- .../src/messages/tool/common_functionality/shape_editor.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index c690aae177..29b1efaa6e 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -71,6 +71,11 @@ impl SelectedLayerState { pub fn set_anchors_status(&mut self, ignore: bool) { self.ignore_anchors = ignore; } + pub fn clear_points_force(&mut self) { + self.selected_points.clear(); + self.ignore_handles = false; + self.ignore_anchors = false; + } pub fn clear_points(&mut self) { if self.ignore_handles || self.ignore_anchors { return; @@ -1517,7 +1522,7 @@ impl ShapeState { pub fn select_all_in_shape(&mut self, network_interface: &NodeNetworkInterface, selection_shape: SelectionShape, selection_change: SelectionChange) { for (&layer, state) in &mut self.selected_shape_state { if selection_change == SelectionChange::Clear { - state.clear_points() + state.clear_points_force() } let vector_data = network_interface.compute_modified_vector(layer); From 97ca65db2b4ca12f3e8faa76cf40869fa4907f4a Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Wed, 30 Apr 2025 11:45:31 +0300 Subject: [PATCH 23/25] Minor fix on overlays check for all overlays --- .../src/messages/portfolio/document/document_message_handler.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 5fd382c08d..9467dbb630 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1152,6 +1152,8 @@ impl MessageHandler> for DocumentMessag Some(overlays_type) => overlays_type, None => { visibility_settings.all = visible; + responses.add(BroadcastEvent::ToolAbort); + responses.add(OverlaysMessage::Draw); return; } }; From 293612e1d00069276a935aafdf9c3ce3a4c8509c Mon Sep 17 00:00:00 2001 From: seam0s-dev Date: Wed, 30 Apr 2025 11:47:09 +0300 Subject: [PATCH 24/25] Add proper code formatting --- .../document/document_message_handler.rs | 2 +- .../tool/common_functionality/pivot.rs | 19 ++++++++++++------- .../tool/common_functionality/shape_editor.rs | 14 ++------------ .../tool/tool_messages/select_tool.rs | 2 +- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 9467dbb630..d324c4931d 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1173,7 +1173,7 @@ impl MessageHandler> for DocumentMessag } OverlaysType::Handles => visibility_settings.handles = visible, } - + responses.add(BroadcastEvent::ToolAbort); responses.add(OverlaysMessage::Draw); } diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index 028d673412..67ce7f5dc5 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -47,7 +47,9 @@ impl Pivot { /// Recomputes the pivot position and transform. fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) { - if !self.active { return; } + if !self.active { + return; + } let selected_nodes = document.network_interface.selected_nodes(); let mut layers = selected_nodes.selected_visible_and_unlocked_layers(&document.network_interface); @@ -90,8 +92,7 @@ impl Pivot { if !overlay_context.visibility_settings.pivot() { self.active = false; return; - } - else { + } else { self.active = true; } @@ -119,8 +120,10 @@ impl Pivot { /// Sets the viewport position of the pivot for all selected layers. pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - if !self.active { return; } - + if !self.active { + return; + } + for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) { let transform = Self::get_layer_pivot_transform(layer, document); // Only update the pivot when computed position is finite. @@ -134,8 +137,10 @@ impl Pivot { /// Set the pivot using the normalized transform that is set above. pub fn set_normalized_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - if !self.active { return; } - + if !self.active { + return; + } + self.set_viewport_position(self.transform_from_normalized.transform_point2(position), document, responses); } diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index ad26002a4c..7fa761acdc 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -573,12 +573,7 @@ impl ShapeState { /// Deselects all the anchors across every selected layer. pub fn deselect_all_anchors(&mut self) { for (_, state) in self.selected_shape_state.iter_mut() { - let selected_anchor_points: Vec = state - .selected_points - .iter() - .filter(|selected_point| selected_point.as_anchor().is_some()) - .cloned() - .collect(); + let selected_anchor_points: Vec = state.selected_points.iter().filter(|selected_point| selected_point.as_anchor().is_some()).cloned().collect(); for point in selected_anchor_points { state.deselect_point(point); @@ -589,12 +584,7 @@ impl ShapeState { /// Deselects all the handles across every selected layer. pub fn deselect_all_handles(&mut self) { for (_, state) in self.selected_shape_state.iter_mut() { - let selected_handle_points: Vec = state - .selected_points - .iter() - .filter(|selected_point| selected_point.as_handle().is_some()) - .cloned() - .collect(); + let selected_handle_points: Vec = state.selected_points.iter().filter(|selected_point| selected_point.as_handle().is_some()).cloned().collect(); for point in selected_handle_points { state.deselect_point(point); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 4d0bc754a4..6ba9eb6b39 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -1048,7 +1048,7 @@ impl Fsm for SelectToolFsmState { let mouse_delta = snap_drag(start, current, tool_data.axis_align, axis, snap_data, &mut tool_data.snap_manager, &tool_data.snap_candidates); let mouse_delta = match axis { - Axis::X => mouse_delta.project_onto(e0), + Axis::X => mouse_delta.project_onto(e0), Axis::Y => mouse_delta.project_onto(e0.perp()), Axis::None => mouse_delta, }; From fd5629b904539c6995d27cc68733365f6d5277cf Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Wed, 30 Apr 2025 03:56:34 -0700 Subject: [PATCH 25/25] Nits --- .../portfolio/document/overlays/utility_functions.rs | 1 + editor/src/messages/tool/tool_messages/select_tool.rs | 1 - editor/src/messages/tool/utility_types.rs | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index bcb1f32ea9..6f2b1aef6a 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -165,6 +165,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle DrawHandles::None => {} } } + if display_anchors { for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) { overlay_context.manipulator_anchor(transform.transform_point2(position), is_selected(ManipulatorPointId::Anchor(id)), None); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 6ba9eb6b39..e92980d27c 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -866,7 +866,6 @@ impl Fsm for SelectToolFsmState { let is_over_pivot = tool_data.pivot.is_over(mouse_position); let show_compass = bounds.is_some_and(|quad| quad.all_sides_at_least_width(COMPASS_ROSE_HOVER_RING_DIAMETER) && quad.contains(mouse_position)); - // If bounding box is some, compass_rose_state.can_grab() && show_compass is evaluated else compass_rose_state.can_grab() is evaluated let can_grab_compass_rose = compass_rose_state.can_grab() && (show_compass || bounds.is_none()); let is_flat_layer = tool_data .bounding_box_manager diff --git a/editor/src/messages/tool/utility_types.rs b/editor/src/messages/tool/utility_types.rs index 2fca871cdc..88a1a8423d 100644 --- a/editor/src/messages/tool/utility_types.rs +++ b/editor/src/messages/tool/utility_types.rs @@ -240,9 +240,9 @@ impl LayoutHolder for ToolData { let separator = std::iter::once(Separator::new(SeparatorType::Section).direction(SeparatorDirection::Vertical).widget_holder()); let buttons = group.into_iter().map(|ToolEntry { tooltip, tooltip_shortcut, tool_type, icon_name }| { IconButton::new(icon_name, 32) - .disabled( false) - .active( self.active_tool_type == tool_type) - .tooltip( tooltip.clone()) + .disabled(false) + .active(self.active_tool_type == tool_type) + .tooltip(tooltip.clone()) .tooltip_shortcut(tooltip_shortcut) .on_update(move |_| { if !tooltip.contains("Coming Soon") {