From 19111efb61cdc7745d3baf13bd8872624842b8a6 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Wed, 30 Jul 2025 10:24:37 +0530 Subject: [PATCH 1/6] fixed cursor,arc bugs --- .../document/overlays/utility_types.rs | 8 +-- .../gizmos/gizmo_manager.rs | 19 ++++++ .../gizmos/shape_gizmos/sweep_angle_gizmo.rs | 30 +++++---- .../common_functionality/shapes/arc_shape.rs | 12 +++- .../shapes/polygon_shape.rs | 12 ++++ .../shapes/shape_utility.rs | 3 + .../common_functionality/shapes/star_shape.rs | 12 ++++ .../messages/tool/tool_messages/shape_tool.rs | 67 +++++++++++++++---- 8 files changed, 129 insertions(+), 34 deletions(-) diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index d50dfb9d85..a71a3ca3cc 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -423,11 +423,9 @@ impl OverlayContext { self.render_context.stroke(); } - pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, dash_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) { + pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) { let end_point1 = pivot + bold_radius * DVec2::from_angle(angle + offset_angle); - let end_point2 = pivot + dash_radius * DVec2::from_angle(offset_angle); self.line(pivot, end_point1, None, None); - self.dashed_line(pivot, end_point2, None, None, Some(2.), Some(2.), Some(0.5)); self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle); } @@ -592,9 +590,9 @@ impl OverlayContext { self.end_dpi_aware_transform(); } - pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, dash_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) { + pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) { self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED)); - self.draw_arc_gizmo_angle(pivot, bold_radius, dash_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians()); + self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians()); self.text(&text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]); } diff --git a/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs b/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs index b22aa998ab..f547590669 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs @@ -1,3 +1,4 @@ +use crate::messages::frontend::utility_types::MouseCursorIcon; use crate::messages::message::Message; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; @@ -123,6 +124,15 @@ impl ShapeGizmoHandlers { Self::None => {} } } + + pub fn gizmo_cursor_icon(&self) -> Option { + match self { + Self::Star(h) => h.mouse_cursor_icon(), + Self::Polygon(h) => h.mouse_cursor_icon(), + Self::Arc(h) => h.mouse_cursor_icon(), + Self::None => None, + } + } } /// Central manager that coordinates shape gizmo handlers for interactive editing on the canvas. @@ -256,4 +266,13 @@ impl GizmoManager { } } } + + /// Returns the cursor icon to display when hovering or dragging a gizmo. + /// + /// If a gizmo is active (hovered or being manipulated), it returns the cursor icon associated with that gizmo; + /// otherwise, returns `None` to indicate the default crosshair cursor should be used. + + pub fn mouse_cursor_icon(&self) -> Option { + self.active_shape_handler.as_ref().and_then(|h| h.gizmo_cursor_icon()) + } } diff --git a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/sweep_angle_gizmo.rs b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/sweep_angle_gizmo.rs index 4a529ff8bb..b0a45addf9 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/sweep_angle_gizmo.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/sweep_angle_gizmo.rs @@ -1,10 +1,9 @@ use crate::consts::{ARC_SNAP_THRESHOLD, COLOR_OVERLAY_RED, GIZMO_HIDE_THRESHOLD}; -use crate::messages::frontend::utility_types::MouseCursorIcon; use crate::messages::message::Message; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::InputConnector; -use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage}; +use crate::messages::prelude::DocumentMessageHandler; use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::shapes::shape_utility::{arc_end_points, calculate_arc_text_transform, extract_arc_parameters, format_rounded}; use crate::messages::tool::tool_messages::tool_prelude::*; @@ -57,7 +56,7 @@ impl SweepAngleGizmo { self.handle_state == SweepAngleGizmoState::Dragging || self.handle_state == SweepAngleGizmoState::Snapped } - pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2, responses: &mut VecDeque) { + pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2) { if self.handle_state == SweepAngleGizmoState::Inactive { let Some((start, end)) = arc_end_points(Some(layer), document) else { return }; let Some((_, start_angle, sweep_angle, _)) = extract_arc_parameters(Some(layer), document) else { @@ -89,8 +88,6 @@ impl SweepAngleGizmo { self.snap_angles = Self::calculate_snap_angles(); self.update_state(SweepAngleGizmoState::Hover); - - responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }); } } } @@ -127,6 +124,11 @@ impl SweepAngleGizmo { // Depending on which endpoint is being dragged, draw guides relative to the static point let point = if self.endpoint == EndpointType::End { current_end } else { current_start }; + + // Draw the dashed line from center to drag start position + overlay_context.dashed_line(self.position_before_rotation, viewport.transform_point2(DVec2::ZERO), None, None, Some(5.), Some(5.), Some(0.5)); + + // Draw the angle, text and the bold line self.dragging_snapping_overlays(self.position_before_rotation, point, tilt_offset, viewport, overlay_context); } SweepAngleGizmoState::Snapped => { @@ -143,6 +145,9 @@ impl SweepAngleGizmo { // Draw lines from endpoints to the arc center overlay_context.line(start, center, Some(COLOR_OVERLAY_RED), Some(2.)); overlay_context.line(end, center, Some(COLOR_OVERLAY_RED), Some(2.)); + + // Draw the line from drag start to arc center + overlay_context.dashed_line(self.position_before_rotation, center, None, None, Some(5.), Some(5.), Some(0.5)); } } } @@ -155,7 +160,6 @@ impl SweepAngleGizmo { let final_vector = final_point - center; let offset_angle = initial_vector.to_angle() + tilt_offset; - let dash_radius = initial_point.distance(center); let bold_radius = final_point.distance(center); let angle = initial_vector.angle_to(final_vector).to_degrees(); @@ -170,7 +174,7 @@ impl SweepAngleGizmo { let transform = calculate_arc_text_transform(angle, offset_angle, center, text_texture_width); - overlay_context.arc_sweep_angle(offset_angle, angle, final_point, bold_radius, dash_radius, center, &text, transform); + overlay_context.arc_sweep_angle(offset_angle, angle, final_point, bold_radius, center, &text, transform); } pub fn update_arc(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { @@ -209,12 +213,11 @@ impl SweepAngleGizmo { let wrapped = new_sweep_angle % 360.; self.total_angle_delta = -wrapped; - // Remaining drag gets passed to the ending endpoint - let rest_angle = angle_delta + wrapped; self.endpoint = EndpointType::End; self.initial_sweep_angle = 360.; - self.initial_start_angle = current_start_angle + rest_angle; + self.initial_start_angle = current_start_angle; + self.update_state(SweepAngleGizmoState::Snapped); self.apply_arc_update(node_id, self.initial_start_angle, self.initial_sweep_angle - wrapped, input, responses); } @@ -288,12 +291,13 @@ impl SweepAngleGizmo { } // Clamp sweep angle above 360°, switch to start () if new_sweep_angle > 360. => { - let delta = angle_delta - (360. - current_sweep_angle); + let delta = angle_delta - (360. - new_sweep_angle); let sign = -delta.signum(); - self.total_angle_delta = angle_delta; + self.total_angle_delta = angle_delta - (360. - new_sweep_angle); self.initial_sweep_angle = 360.; self.endpoint = EndpointType::Start; + self.update_state(SweepAngleGizmoState::Snapped); self.apply_arc_update(node_id, self.initial_start_angle + angle_delta, self.initial_sweep_angle + angle_delta.abs() * sign, input, responses); } @@ -339,7 +343,7 @@ impl SweepAngleGizmo { pub fn calculate_snap_angles() -> Vec { let mut snap_points = Vec::new(); - for i in 0..8 { + for i in 0..=8 { let snap_point = i as f64 * FRAC_PI_4; snap_points.push(snap_point.to_degrees()); } diff --git a/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs b/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs index 8f1b347546..76e3a21e10 100644 --- a/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs @@ -26,8 +26,8 @@ impl ArcGizmoHandler { } impl ShapeGizmoHandler for ArcGizmoHandler { - fn handle_state(&mut self, selected_shape_layers: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - self.sweep_angle_gizmo.handle_actions(selected_shape_layers, document, mouse_position, responses); + fn handle_state(&mut self, selected_shape_layers: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, _responses: &mut VecDeque) { + self.sweep_angle_gizmo.handle_actions(selected_shape_layers, document, mouse_position); } fn is_any_gizmo_hovered(&self) -> bool { @@ -74,6 +74,14 @@ impl ShapeGizmoHandler for ArcGizmoHandler { arc_outline(selected_shape_layers.or(self.sweep_angle_gizmo.layer), document, overlay_context); } + fn mouse_cursor_icon(&self) -> Option { + if self.sweep_angle_gizmo.hovered() || self.sweep_angle_gizmo.is_dragging_or_snapped() { + return Some(MouseCursorIcon::Default); + } + + None + } + fn cleanup(&mut self) { self.sweep_angle_gizmo.cleanup(); } diff --git a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs index 82dcf10cfc..fc3c79c120 100644 --- a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs @@ -89,6 +89,18 @@ impl ShapeGizmoHandler for PolygonGizmoHandler { } } + fn mouse_cursor_icon(&self) -> Option { + if self.number_of_points_dial.is_dragging() || self.number_of_points_dial.is_hovering() { + return Some(MouseCursorIcon::EWResize); + } + + if self.point_radius_handle.is_dragging_or_snapped() || self.point_radius_handle.hovered() { + return Some(MouseCursorIcon::Default); + } + + None + } + fn cleanup(&mut self) { self.number_of_points_dial.cleanup(); self.point_radius_handle.cleanup(); diff --git a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs index b274d5de54..4b897b2505 100644 --- a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs +++ b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs @@ -1,5 +1,6 @@ use super::ShapeToolData; use crate::consts::{ARC_SWEEP_GIZMO_RADIUS, ARC_SWEEP_GIZMO_TEXT_HEIGHT}; +use crate::messages::frontend::utility_types::MouseCursorIcon; use crate::messages::message::Message; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; @@ -127,6 +128,8 @@ pub trait ShapeGizmoHandler { /// /// For example, dragging states or hover flags should be cleared to avoid visual glitches when switching tools or shapes. fn cleanup(&mut self); + + fn mouse_cursor_icon(&self) -> Option; } /// Center, Lock Ratio, Lock Angle, Snap Angle, Increase/Decrease Side diff --git a/editor/src/messages/tool/common_functionality/shapes/star_shape.rs b/editor/src/messages/tool/common_functionality/shapes/star_shape.rs index 653b22f3ba..6830329e2c 100644 --- a/editor/src/messages/tool/common_functionality/shapes/star_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/star_shape.rs @@ -90,6 +90,18 @@ impl ShapeGizmoHandler for StarGizmoHandler { self.number_of_points_dial.cleanup(); self.point_radius_handle.cleanup(); } + + fn mouse_cursor_icon(&self) -> Option { + if self.number_of_points_dial.is_dragging() || self.number_of_points_dial.is_hovering() { + return Some(MouseCursorIcon::EWResize); + } + + if self.point_radius_handle.is_dragging_or_snapped() || self.point_radius_handle.hovered() { + return Some(MouseCursorIcon::Default); + } + + None + } } #[derive(Default)] diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index 4c4ab4e4a1..c6d95016f0 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -335,7 +335,7 @@ pub struct ShapeToolData { current_shape: ShapeType, // Gizmos - gizmo_manger: GizmoManager, + gizmo_manager: GizmoManager, } impl ShapeToolData { @@ -351,6 +351,19 @@ impl ShapeToolData { } } } + + fn transform_cage_mouse_icon(&mut self, input: &InputPreprocessorMessageHandler) -> MouseCursorIcon { + let dragging_bounds = self + .bounding_box_manager + .as_mut() + .and_then(|bounding_box| bounding_box.check_selected_edges(input.mouse.position)) + .is_some(); + + self.bounding_box_manager.as_ref().map_or(MouseCursorIcon::Crosshair, |bounds| { + let cursor_icon = bounds.get_cursor(input, true, dragging_bounds, Some(self.skew_edge)); + if cursor_icon == MouseCursorIcon::Default { MouseCursorIcon::Crosshair } else { cursor_icon } + }) + } } impl Fsm for ShapeToolFsmState { @@ -388,30 +401,34 @@ impl Fsm for ShapeToolFsmState { .indicator_pos() .map(|pos| document.metadata().document_to_viewport.transform_point2(pos)) .unwrap_or(input.mouse.position); - let is_resizing_or_rotating = matches!(self, ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::SkewingBounds { .. } | ShapeToolFsmState::RotatingBounds); if matches!(self, Self::Ready(_)) && !input.keyboard.key(Key::Control) { - tool_data.gizmo_manger.handle_actions(mouse_position, document, responses); - tool_data.gizmo_manger.overlays(document, input, shape_editor, mouse_position, &mut overlay_context); + tool_data.gizmo_manager.handle_actions(mouse_position, document, responses); + tool_data.gizmo_manager.overlays(document, input, shape_editor, mouse_position, &mut overlay_context); } if matches!(self, ShapeToolFsmState::ModifyingGizmo) && !input.keyboard.key(Key::Control) { - tool_data.gizmo_manger.dragging_overlays(document, input, shape_editor, mouse_position, &mut overlay_context); + tool_data.gizmo_manager.dragging_overlays(document, input, shape_editor, mouse_position, &mut overlay_context); + let cursor = tool_data.gizmo_manager.mouse_cursor_icon().unwrap_or(MouseCursorIcon::Crosshair); + tool_data.cursor = cursor; + responses.add(FrontendMessage::UpdateMouseCursor { cursor }); } let modifying_transform_cage = matches!(self, ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::RotatingBounds | ShapeToolFsmState::SkewingBounds { .. }); - let hovering_over_gizmo = tool_data.gizmo_manger.hovering_over_gizmo(); + let hovering_over_gizmo = tool_data.gizmo_manager.hovering_over_gizmo(); - if !is_resizing_or_rotating && !matches!(self, ShapeToolFsmState::ModifyingGizmo) && !modifying_transform_cage && !hovering_over_gizmo { + if !matches!(self, ShapeToolFsmState::ModifyingGizmo) && !modifying_transform_cage && !hovering_over_gizmo { tool_data.data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); } if modifying_transform_cage && !matches!(self, ShapeToolFsmState::ModifyingGizmo) { transform_cage_overlays(document, tool_data, &mut overlay_context); + responses.add(FrontendMessage::UpdateMouseCursor { cursor: tool_data.cursor }); } if input.keyboard.key(Key::Control) && matches!(self, ShapeToolFsmState::Ready(_)) { anchor_overlays(document, &mut overlay_context); + responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Crosshair }); } else if matches!(self, ShapeToolFsmState::Ready(_)) { Line::overlays(document, tool_data, &mut overlay_context); @@ -433,7 +450,7 @@ impl Fsm for ShapeToolFsmState { let edges = bounds.check_selected_edges(input.mouse.position); let is_skewing = matches!(self, ShapeToolFsmState::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 && !hovering_over_gizmo) { + if is_skewing || (dragging_bounds && is_near_square && !hovering_over_gizmo) { bounds.render_skew_gizmos(&mut overlay_context, tool_data.skew_edge); } if !is_skewing && dragging_bounds && !hovering_over_gizmo { @@ -442,6 +459,16 @@ impl Fsm for ShapeToolFsmState { } } } + + let mut cursor; + + cursor = tool_data.transform_cage_mouse_icon(input); + if let Some(gizmo_cursor_icon) = tool_data.gizmo_manager.mouse_cursor_icon() { + cursor = gizmo_cursor_icon; + } + + tool_data.cursor = cursor; + responses.add(FrontendMessage::UpdateMouseCursor { cursor }); } if matches!(self, ShapeToolFsmState::Drawing(_) | ShapeToolFsmState::DraggingLineEndpoints) { @@ -562,8 +589,14 @@ impl Fsm for ShapeToolFsmState { tool_data.line_data.drag_current = mouse_pos; - if tool_data.gizmo_manger.handle_click() { + if tool_data.gizmo_manager.handle_click() && !input.keyboard.key(Key::Accel) { tool_data.data.drag_start = document.metadata().document_to_viewport.inverse().transform_point2(mouse_pos); + responses.add(DocumentMessage::StartTransaction); + + let cursor = tool_data.gizmo_manager.mouse_cursor_icon().unwrap_or(MouseCursorIcon::Crosshair); + tool_data.cursor = cursor; + responses.add(FrontendMessage::UpdateMouseCursor { cursor }); + return ShapeToolFsmState::ModifyingGizmo; } @@ -584,6 +617,10 @@ impl Fsm for ShapeToolFsmState { let (resize, rotate, skew) = transforming_transform_cage(document, &mut tool_data.bounding_box_manager, input, responses, &mut tool_data.layers_dragging, None); if !input.keyboard.key(Key::Control) { + let cursor = tool_data.transform_cage_mouse_icon(input); + tool_data.cursor = cursor; + responses.add(FrontendMessage::UpdateMouseCursor { cursor }); + match (resize, rotate, skew) { (true, false, false) => { tool_data.get_snap_candidates(document, input); @@ -591,10 +628,12 @@ impl Fsm for ShapeToolFsmState { } (false, true, false) => { tool_data.data.drag_start = mouse_pos; + return ShapeToolFsmState::RotatingBounds; } (false, false, true) => { tool_data.get_snap_candidates(document, input); + return ShapeToolFsmState::SkewingBounds { skew: Key::Control }; } _ => {} @@ -686,8 +725,7 @@ impl Fsm for ShapeToolFsmState { self } (ShapeToolFsmState::ModifyingGizmo, ShapeToolMessage::PointerMove(..)) => { - responses.add(DocumentMessage::StartTransaction); - tool_data.gizmo_manger.handle_update(tool_data.data.drag_start, document, input, responses); + tool_data.gizmo_manager.handle_update(tool_data.data.drag_start, document, input, responses); responses.add(OverlaysMessage::Draw); @@ -758,7 +796,7 @@ impl Fsm for ShapeToolFsmState { if cursor == MouseCursorIcon::Default { MouseCursorIcon::Crosshair } else { cursor } }); - if tool_data.cursor != cursor && !input.keyboard.key(Key::Control) && !all_selected_layers_line { + if tool_data.cursor != cursor { tool_data.cursor = cursor; responses.add(FrontendMessage::UpdateMouseCursor { cursor }); } @@ -797,7 +835,7 @@ impl Fsm for ShapeToolFsmState { input.mouse.finish_transaction(tool_data.data.drag_start, responses); tool_data.data.cleanup(responses); - tool_data.gizmo_manger.handle_cleanup(); + tool_data.gizmo_manager.handle_cleanup(); if let Some(bounds) = &mut tool_data.bounding_box_manager { bounds.original_transforms.clear(); @@ -822,12 +860,13 @@ impl Fsm for ShapeToolFsmState { tool_data.data.cleanup(responses); tool_data.line_data.dragging_endpoint = None; - tool_data.gizmo_manger.handle_cleanup(); + tool_data.gizmo_manager.handle_cleanup(); if let Some(bounds) = &mut tool_data.bounding_box_manager { bounds.original_transforms.clear(); } + tool_data.cursor = MouseCursorIcon::Crosshair; responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Crosshair }); ShapeToolFsmState::Ready(tool_data.current_shape) From c849a9222f49a3a35fb856c7aeb6fa66edd8729a Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Wed, 30 Jul 2025 13:06:04 +0530 Subject: [PATCH 2/6] Update cursor icon --- editor/src/messages/tool/tool_messages/shape_tool.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index c6d95016f0..2b2e2c71c7 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -364,6 +364,10 @@ impl ShapeToolData { if cursor_icon == MouseCursorIcon::Default { MouseCursorIcon::Crosshair } else { cursor_icon } }) } + + fn shape_tool_modifier_keys() -> [Key; 4] { + [Key::Alt, Key::Shift, Key::Control, Key::Shift] + } } impl Fsm for ShapeToolFsmState { @@ -596,6 +600,8 @@ impl Fsm for ShapeToolFsmState { let cursor = tool_data.gizmo_manager.mouse_cursor_icon().unwrap_or(MouseCursorIcon::Crosshair); tool_data.cursor = cursor; responses.add(FrontendMessage::UpdateMouseCursor { cursor }); + // Send a PointerMove message to refresh the cursor icon + responses.add(ShapeToolMessage::PointerMove(ShapeToolData::shape_tool_modifier_keys())); return ShapeToolFsmState::ModifyingGizmo; } @@ -620,7 +626,8 @@ impl Fsm for ShapeToolFsmState { let cursor = tool_data.transform_cage_mouse_icon(input); tool_data.cursor = cursor; responses.add(FrontendMessage::UpdateMouseCursor { cursor }); - + // Send a PointerMove message to refresh the cursor icon + responses.add(ShapeToolMessage::PointerMove(ShapeToolData::shape_tool_modifier_keys())); match (resize, rotate, skew) { (true, false, false) => { tool_data.get_snap_candidates(document, input); From c8ce7bb53a0aaac81773ea2370633f0d3bc04f7c Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Wed, 30 Jul 2025 13:30:30 +0530 Subject: [PATCH 3/6] send pointer-move only when required --- .../messages/tool/tool_messages/shape_tool.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index 2b2e2c71c7..02a6384678 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -623,23 +623,30 @@ impl Fsm for ShapeToolFsmState { let (resize, rotate, skew) = transforming_transform_cage(document, &mut tool_data.bounding_box_manager, input, responses, &mut tool_data.layers_dragging, None); if !input.keyboard.key(Key::Control) { - let cursor = tool_data.transform_cage_mouse_icon(input); - tool_data.cursor = cursor; - responses.add(FrontendMessage::UpdateMouseCursor { cursor }); - // Send a PointerMove message to refresh the cursor icon - responses.add(ShapeToolMessage::PointerMove(ShapeToolData::shape_tool_modifier_keys())); + // Helper function to update cursor and send pointer move message + let update_cursor_and_pointer = |tool_data: &mut ShapeToolData, responses: &mut VecDeque| { + let cursor = tool_data.transform_cage_mouse_icon(input); + tool_data.cursor = cursor; + responses.add(FrontendMessage::UpdateMouseCursor { cursor }); + responses.add(ShapeToolMessage::PointerMove(ShapeToolData::shape_tool_modifier_keys())); + }; + match (resize, rotate, skew) { (true, false, false) => { tool_data.get_snap_candidates(document, input); + update_cursor_and_pointer(tool_data, responses); + return ShapeToolFsmState::ResizingBounds; } (false, true, false) => { tool_data.data.drag_start = mouse_pos; + update_cursor_and_pointer(tool_data, responses); return ShapeToolFsmState::RotatingBounds; } (false, false, true) => { tool_data.get_snap_candidates(document, input); + update_cursor_and_pointer(tool_data, responses); return ShapeToolFsmState::SkewingBounds { skew: Key::Control }; } From f705db35b7f318dc6cd00a85216147000a5239b4 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Wed, 30 Jul 2025 13:52:22 +0530 Subject: [PATCH 4/6] make it compile --- .../portfolio/document/overlays/utility_types_vello.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/editor/src/messages/portfolio/document/overlays/utility_types_vello.rs b/editor/src/messages/portfolio/document/overlays/utility_types_vello.rs index b0f9ad34c3..f62adcf926 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types_vello.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types_vello.rs @@ -379,11 +379,9 @@ impl OverlayContext { self.scene.stroke(&kurbo::Stroke::new(1.0), self.get_transform(), Self::parse_color(COLOR_OVERLAY_BLUE), None, &path); } - pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, dash_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) { + pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) { let end_point1 = pivot + bold_radius * DVec2::from_angle(angle + offset_angle); - let end_point2 = pivot + dash_radius * DVec2::from_angle(offset_angle); self.line(pivot, end_point1, None, None); - self.dashed_line(pivot, end_point2, None, None, Some(2.), Some(2.), Some(0.5)); self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle); } @@ -542,9 +540,9 @@ impl OverlayContext { } #[allow(clippy::too_many_arguments)] - pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, dash_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) { + pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) { self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED)); - self.draw_arc_gizmo_angle(pivot, bold_radius, dash_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians()); + self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians()); self.text(text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]); } From 7bdb29dbe36df83ca667462f4132ff849f6cfd61 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Wed, 30 Jul 2025 23:09:16 -0700 Subject: [PATCH 5/6] Code review --- .../tool/common_functionality/gizmos/gizmo_manager.rs | 1 - editor/src/messages/tool/tool_messages/shape_tool.rs | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs b/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs index f547590669..4f3d9a4e89 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs @@ -271,7 +271,6 @@ impl GizmoManager { /// /// If a gizmo is active (hovered or being manipulated), it returns the cursor icon associated with that gizmo; /// otherwise, returns `None` to indicate the default crosshair cursor should be used. - pub fn mouse_cursor_icon(&self) -> Option { self.active_shape_handler.as_ref().and_then(|h| h.gizmo_cursor_icon()) } diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index 02a6384678..98290cce0d 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -464,12 +464,7 @@ impl Fsm for ShapeToolFsmState { } } - let mut cursor; - - cursor = tool_data.transform_cage_mouse_icon(input); - if let Some(gizmo_cursor_icon) = tool_data.gizmo_manager.mouse_cursor_icon() { - cursor = gizmo_cursor_icon; - } + let cursor = tool_data.gizmo_manager.mouse_cursor_icon().unwrap_or_else(|| tool_data.transform_cage_mouse_icon(input)); tool_data.cursor = cursor; responses.add(FrontendMessage::UpdateMouseCursor { cursor }); From e952dd6649d266d0b929cde9143c25c7cd2558b3 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Thu, 31 Jul 2025 12:02:43 +0530 Subject: [PATCH 6/6] remove repeated modifier key --- editor/src/messages/input_mapper/input_mappings.rs | 2 +- .../tool/common_functionality/shapes/ellipse_shape.rs | 2 +- .../messages/tool/common_functionality/shapes/line_shape.rs | 2 +- .../tool/common_functionality/shapes/polygon_shape.rs | 2 +- .../tool/common_functionality/shapes/rectangle_shape.rs | 2 +- .../tool/common_functionality/shapes/shape_utility.rs | 2 +- .../messages/tool/common_functionality/shapes/star_shape.rs | 2 +- editor/src/messages/tool/tool_messages/shape_tool.rs | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 71fb27a01c..bf53a1ee3b 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -178,7 +178,7 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(Escape); action_dispatch=ShapeToolMessage::Abort), entry!(KeyDown(BracketLeft); action_dispatch=ShapeToolMessage::DecreaseSides), entry!(KeyDown(BracketRight); action_dispatch=ShapeToolMessage::IncreaseSides), - entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=ShapeToolMessage::PointerMove([Alt, Shift, Control, Shift])), + entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=ShapeToolMessage::PointerMove([Alt, Shift, Control])), entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowLeft], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }), entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowRight], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }), entry!(KeyDown(ArrowUp); modifiers=[Shift], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: 0., delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }), diff --git a/editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs b/editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs index 700f3c2779..3cc86193dd 100644 --- a/editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs @@ -28,7 +28,7 @@ impl Ellipse { modifier: ShapeToolModifierKey, responses: &mut VecDeque, ) { - let [center, lock_ratio, _, _] = modifier; + let [center, lock_ratio, _] = modifier; if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) { let Some(node_id) = graph_modification_utils::get_ellipse_id(layer, &document.network_interface) else { diff --git a/editor/src/messages/tool/common_functionality/shapes/line_shape.rs b/editor/src/messages/tool/common_functionality/shapes/line_shape.rs index 985457c208..8bd22b0b16 100644 --- a/editor/src/messages/tool/common_functionality/shapes/line_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/line_shape.rs @@ -53,7 +53,7 @@ impl Line { modifier: ShapeToolModifierKey, responses: &mut VecDeque, ) { - let [center, _, lock_angle, snap_angle] = modifier; + let [center, snap_angle, lock_angle] = modifier; shape_tool_data.line_data.drag_current = ipp.mouse.position; diff --git a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs index fc3c79c120..f4a1ae2c30 100644 --- a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs @@ -124,7 +124,7 @@ impl Polygon { modifier: ShapeToolModifierKey, responses: &mut VecDeque, ) { - let [center, lock_ratio, _, _] = modifier; + let [center, lock_ratio, _] = modifier; if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) { // TODO: We need to determine how to allow the polygon node to make irregular shapes diff --git a/editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs b/editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs index cbd6722960..56cdbe4d1f 100644 --- a/editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs @@ -28,7 +28,7 @@ impl Rectangle { modifier: ShapeToolModifierKey, responses: &mut VecDeque, ) { - let [center, lock_ratio, _, _] = modifier; + let [center, lock_ratio, _] = modifier; if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) { let Some(node_id) = graph_modification_utils::get_rectangle_id(layer, &document.network_interface) else { diff --git a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs index 4b897b2505..c5a6351cbe 100644 --- a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs +++ b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs @@ -75,7 +75,7 @@ impl ShapeType { } } -pub type ShapeToolModifierKey = [Key; 4]; +pub type ShapeToolModifierKey = [Key; 3]; /// The `ShapeGizmoHandler` trait defines the interactive behavior and overlay logic for shape-specific tools in the editor. /// A gizmo is a visual handle or control point used to manipulate a shape's properties (e.g., number of sides, radius, angle). diff --git a/editor/src/messages/tool/common_functionality/shapes/star_shape.rs b/editor/src/messages/tool/common_functionality/shapes/star_shape.rs index 6830329e2c..24d11762e6 100644 --- a/editor/src/messages/tool/common_functionality/shapes/star_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/star_shape.rs @@ -126,7 +126,7 @@ impl Star { modifier: ShapeToolModifierKey, responses: &mut VecDeque, ) { - let [center, lock_ratio, _, _] = modifier; + let [center, lock_ratio, _] = modifier; if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) { // TODO: We need to determine how to allow the polygon node to make irregular shapes diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index 98290cce0d..9fec5cb480 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -365,8 +365,8 @@ impl ShapeToolData { }) } - fn shape_tool_modifier_keys() -> [Key; 4] { - [Key::Alt, Key::Shift, Key::Control, Key::Shift] + fn shape_tool_modifier_keys() -> [Key; 3] { + [Key::Alt, Key::Shift, Key::Control] } }