From 5ab6f2e5ae2e63077ff0e13bda8a2fb907b87558 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Thu, 4 Sep 2025 13:36:25 +0530 Subject: [PATCH] added multi select changed added gizmo context --- .../gizmos/gizmo_manager.rs | 154 +++++++++-------- .../tool/common_functionality/gizmos/mod.rs | 1 + .../gizmos/operation_gizmos/count_gizmos.rs | 156 ++++++++++++++++++ .../gizmos/operation_gizmos/mod.rs | 1 + .../shape_gizmos/circle_arc_radius_handle.rs | 18 +- .../shape_gizmos/number_of_points_dial.rs | 20 ++- .../shape_gizmos/point_radius_handle.rs | 17 +- .../gizmos/shape_gizmos/sweep_angle_gizmo.rs | 22 ++- .../graph_modification_utils.rs | 4 + .../operations/circular_repeat.rs | 73 +++++--- .../common_functionality/shapes/arc_shape.rs | 54 +++--- .../shapes/circle_shape.rs | 34 +--- .../shapes/polygon_shape.rs | 42 ++--- .../shapes/shape_utility.rs | 30 ++-- .../common_functionality/shapes/star_shape.rs | 44 ++--- .../tool/tool_messages/operation_tool.rs | 64 ++++++- .../messages/tool/tool_messages/shape_tool.rs | 36 +++- 17 files changed, 500 insertions(+), 270 deletions(-) create mode 100644 editor/src/messages/tool/common_functionality/gizmos/operation_gizmos/count_gizmos.rs create mode 100644 editor/src/messages/tool/common_functionality/gizmos/operation_gizmos/mod.rs 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 57cbb4ed33..f9d2f4ec57 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs @@ -1,17 +1,16 @@ 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::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler}; +use crate::messages::prelude::DocumentMessageHandler; use crate::messages::tool::common_functionality::graph_modification_utils; -use crate::messages::tool::common_functionality::shape_editor::ShapeState; +use crate::messages::tool::common_functionality::operations::circular_repeat::CircularRepeatGizmoHandler; use crate::messages::tool::common_functionality::shapes::arc_shape::ArcGizmoHandler; use crate::messages::tool::common_functionality::shapes::circle_shape::CircleGizmoHandler; use crate::messages::tool::common_functionality::shapes::polygon_shape::PolygonGizmoHandler; -use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeGizmoHandler}; use crate::messages::tool::common_functionality::shapes::star_shape::StarGizmoHandler; use glam::DVec2; -use std::collections::VecDeque; +use std::collections::HashMap; /// A unified enum wrapper around all available shape-specific gizmo handlers. /// @@ -28,6 +27,7 @@ pub enum ShapeGizmoHandlers { Polygon(PolygonGizmoHandler), Arc(ArcGizmoHandler), Circle(CircleGizmoHandler), + CircularRepeat(CircularRepeatGizmoHandler), } impl ShapeGizmoHandlers { @@ -39,17 +39,19 @@ impl ShapeGizmoHandlers { Self::Polygon(_) => "polygon", Self::Arc(_) => "arc", Self::Circle(_) => "circle", + Self::CircularRepeat(_) => "circular_repeat", Self::None => "none", } } /// Dispatches interaction state updates to the corresponding shape-specific handler. - pub fn handle_state(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { + pub fn handle_state(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { match self { - Self::Star(h) => h.handle_state(layer, mouse_position, document, responses), - Self::Polygon(h) => h.handle_state(layer, mouse_position, document, responses), - Self::Arc(h) => h.handle_state(layer, mouse_position, document, responses), - Self::Circle(h) => h.handle_state(layer, mouse_position, document, responses), + Self::Star(h) => h.handle_state(layer, mouse_position, ctx), + Self::Polygon(h) => h.handle_state(layer, mouse_position, ctx), + Self::Arc(h) => h.handle_state(layer, mouse_position, ctx), + Self::Circle(h) => h.handle_state(layer, mouse_position, ctx), + Self::CircularRepeat(h) => h.handle_state(layer, mouse_position, ctx), Self::None => {} } } @@ -61,6 +63,7 @@ impl ShapeGizmoHandlers { Self::Polygon(h) => h.is_any_gizmo_hovered(), Self::Arc(h) => h.is_any_gizmo_hovered(), Self::Circle(h) => h.is_any_gizmo_hovered(), + Self::CircularRepeat(h) => h.is_any_gizmo_hovered(), Self::None => false, } } @@ -72,17 +75,19 @@ impl ShapeGizmoHandlers { Self::Polygon(h) => h.handle_click(), Self::Arc(h) => h.handle_click(), Self::Circle(h) => h.handle_click(), + Self::CircularRepeat(h) => h.handle_click(), Self::None => {} } } /// Updates the gizmo state while the user is dragging a handle (e.g., adjusting radius). - pub fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { + pub fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { match self { - Self::Star(h) => h.handle_update(drag_start, document, input, responses), - Self::Polygon(h) => h.handle_update(drag_start, document, input, responses), - Self::Arc(h) => h.handle_update(drag_start, document, input, responses), - Self::Circle(h) => h.handle_update(drag_start, document, input, responses), + Self::Star(h) => h.handle_update(drag_start, ctx), + Self::Polygon(h) => h.handle_update(drag_start, ctx), + Self::Arc(h) => h.handle_update(drag_start, ctx), + Self::Circle(h) => h.handle_update(drag_start, ctx), + Self::CircularRepeat(h) => h.handle_update(drag_start, ctx), Self::None => {} } } @@ -94,43 +99,31 @@ impl ShapeGizmoHandlers { Self::Polygon(h) => h.cleanup(), Self::Arc(h) => h.cleanup(), Self::Circle(h) => h.cleanup(), + Self::CircularRepeat(h) => h.cleanup(), Self::None => {} } } /// Draws overlays like control points or outlines for the shape handled by this gizmo. - pub fn overlays( - &self, - document: &DocumentMessageHandler, - layer: Option, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { + pub fn overlays(&self, layer: Option, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { match self { - Self::Star(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context), - Self::Polygon(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context), - Self::Arc(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context), - Self::Circle(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context), + Self::Star(h) => h.overlays(layer, mouse_position, ctx, overlay_context), + Self::Polygon(h) => h.overlays(layer, mouse_position, ctx, overlay_context), + Self::Arc(h) => h.overlays(layer, mouse_position, ctx, overlay_context), + Self::Circle(h) => h.overlays(layer, mouse_position, ctx, overlay_context), + Self::CircularRepeat(h) => h.overlays(layer, mouse_position, ctx, overlay_context), Self::None => {} } } /// Draws live-updating overlays during drag interactions for the shape handled by this gizmo. - pub fn dragging_overlays( - &self, - document: &DocumentMessageHandler, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { + pub fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { match self { - Self::Star(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context), - Self::Polygon(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context), - Self::Arc(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context), - Self::Circle(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context), + Self::Star(h) => h.dragging_overlays(mouse_position, ctx, overlay_context), + Self::Polygon(h) => h.dragging_overlays(mouse_position, ctx, overlay_context), + Self::Arc(h) => h.dragging_overlays(mouse_position, ctx, overlay_context), + Self::Circle(h) => h.dragging_overlays(mouse_position, ctx, overlay_context), + Self::CircularRepeat(h) => h.dragging_overlays(mouse_position, ctx, overlay_context), Self::None => {} } } @@ -141,6 +134,7 @@ impl ShapeGizmoHandlers { Self::Polygon(h) => h.mouse_cursor_icon(), Self::Arc(h) => h.mouse_cursor_icon(), Self::Circle(h) => h.mouse_cursor_icon(), + Self::CircularRepeat(h) => h.mouse_cursor_icon(), Self::None => None, } } @@ -188,6 +182,16 @@ impl GizmoManager { None } + /// Detects and returns a operation gizmo handler based on the layer type (e.g., circular_repeat, repeat). + pub fn detect_operation_gizmo_handler(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option { + // Circular Repeat + if graph_modification_utils::get_circular_repeat(layer, &document.network_interface).is_some() { + return Some(ShapeGizmoHandlers::CircularRepeat(CircularRepeatGizmoHandler::default())); + } + + None + } + /// Returns `true` if a gizmo is currently active (hovered or being interacted with). pub fn hovering_over_gizmo(&self) -> bool { self.active_shape_handler.is_some() @@ -196,12 +200,12 @@ impl GizmoManager { /// Called every frame to check selected layers and update the active shape gizmo, if hovered. /// /// Also groups all shape layers with the same kind of gizmo to support overlays for multi-shape editing. - pub fn handle_actions(&mut self, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { + pub fn handle_actions(&mut self, mouse_position: DVec2, ctx: &mut GizmoContext) { let mut handlers_layer: Vec<(ShapeGizmoHandlers, Vec)> = Vec::new(); - for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) { - if let Some(mut handler) = Self::detect_shape_handler(layer, document) { - handler.handle_state(layer, mouse_position, document, responses); + for layer in ctx.document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&ctx.document.network_interface) { + if let Some(mut handler) = Self::detect_shape_handler(layer, ctx.document) { + handler.handle_state(layer, mouse_position, ctx); let is_hovered = handler.is_any_gizmo_hovered(); if is_hovered { @@ -210,7 +214,7 @@ impl GizmoManager { return; } - // Try to group this handler with others of the same type + // Group same-kind handlers together if let Some((_, layers)) = handlers_layer.iter_mut().find(|(existing_handler, _)| existing_handler.kind() == handler.kind()) { layers.push(layer); } else { @@ -223,6 +227,38 @@ impl GizmoManager { self.active_shape_handler = None; } + pub fn handle_operation_actions(&mut self, mouse_position: DVec2, ctx: &mut GizmoContext) { + self.active_shape_handler = None; + + let mut handlers_map: HashMap<&'static str, ShapeGizmoHandlers> = HashMap::new(); + let mut maybe_active_kind: Option<&'static str> = None; + + for layer in ctx.document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&ctx.document.network_interface) { + if let Some(mut handler) = Self::detect_operation_gizmo_handler(layer, ctx.document) { + let kind = handler.kind(); + + // Reuse existing handler to accumulate layers + if let Some(existing_handler) = handlers_map.remove(kind) { + handler = existing_handler; + } + + handler.handle_state(layer, mouse_position, ctx); + + if handler.is_any_gizmo_hovered() { + maybe_active_kind = Some(kind); + } + + handlers_map.insert(kind, handler); + } + } + + if let Some(kind) = maybe_active_kind { + if let Some(handler) = handlers_map.remove(kind) { + self.active_shape_handler = Some(handler); + } + } + } + /// Handles click interactions if a gizmo is active. Returns `true` if a gizmo handled the click. pub fn handle_click(&mut self) -> bool { if let Some(handle) = &mut self.active_shape_handler { @@ -239,45 +275,31 @@ impl GizmoManager { } /// Passes drag update data to the active gizmo to update shape parameters live. - pub fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { + pub fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { if let Some(handle) = &mut self.active_shape_handler { - handle.handle_update(drag_start, document, input, responses); + handle.handle_update(drag_start, ctx); } } /// Draws overlays for the currently active shape gizmo during a drag interaction. - pub fn dragging_overlays( - &self, - document: &DocumentMessageHandler, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { + pub fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { if let Some(handle) = &self.active_shape_handler { - handle.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context); + handle.dragging_overlays(mouse_position, ctx, overlay_context); } } /// Draws overlays for either the active gizmo (if hovered) or all grouped selected gizmos. /// /// If no single gizmo is active, it renders overlays for all grouped layers with associated handlers. - pub fn overlays( - &self, - document: &DocumentMessageHandler, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { + pub fn overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { if let Some(handler) = &self.active_shape_handler { - handler.overlays(document, None, input, shape_editor, mouse_position, overlay_context); + handler.overlays(None, mouse_position, ctx, overlay_context); return; } for (handler, selected_layers) in &self.layers_handlers { for layer in selected_layers { - handler.overlays(document, Some(*layer), input, shape_editor, mouse_position, overlay_context); + handler.overlays(Some(*layer), mouse_position, ctx, overlay_context); } } } diff --git a/editor/src/messages/tool/common_functionality/gizmos/mod.rs b/editor/src/messages/tool/common_functionality/gizmos/mod.rs index 108c45d6a3..9ad6142b3f 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/mod.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/mod.rs @@ -1,2 +1,3 @@ pub mod gizmo_manager; +pub mod operation_gizmos; pub mod shape_gizmos; diff --git a/editor/src/messages/tool/common_functionality/gizmos/operation_gizmos/count_gizmos.rs b/editor/src/messages/tool/common_functionality/gizmos/operation_gizmos/count_gizmos.rs new file mode 100644 index 0000000000..ca79bb9254 --- /dev/null +++ b/editor/src/messages/tool/common_functionality/gizmos/operation_gizmos/count_gizmos.rs @@ -0,0 +1,156 @@ +use crate::consts::{NUMBER_OF_POINTS_DIAL_SPOKE_EXTENSION, NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH}; +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::NodeGraphMessage; +use crate::messages::prelude::Responses; +use crate::messages::tool::common_functionality::graph_modification_utils; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, extract_circular_repeat_parameters}; +use glam::{DAffine2, DVec2}; +use graph_craft::document::NodeInput; +use graph_craft::document::value::TaggedValue; +use std::collections::HashMap; +use std::f64::consts::{FRAC_PI_2, TAU}; + +#[derive(Clone, Debug, Default, PartialEq)] +pub enum RepeatCountDialState { + #[default] + Inactive, + Hover, + Dragging, +} + +#[derive(Clone, Debug, Default)] +pub struct RepeatCountDial { + pub layer: Option, + pub initial_points: u32, + pub handle_state: RepeatCountDialState, + + // store the other layers whose gizmo is not hovered but still has repeat node in them + selected_layers: HashMap, +} + +impl RepeatCountDial { + pub fn cleanup(&mut self) { + self.handle_state = RepeatCountDialState::Inactive; + self.layer = None; + self.selected_layers.clear(); + } + + pub fn update_state(&mut self, state: RepeatCountDialState) { + self.handle_state = state; + } + + pub fn is_hovering(&self) -> bool { + self.handle_state == RepeatCountDialState::Hover + } + + pub fn is_dragging(&self) -> bool { + self.handle_state == RepeatCountDialState::Dragging + } + + pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, .. } = ctx; + + match &self.handle_state { + RepeatCountDialState::Inactive => { + let Some((_, radius, count)) = extract_circular_repeat_parameters(Some(layer), document) else { + return; + }; + let viewport = document.metadata().transform_to_viewport(layer); + + let center = viewport.transform_point2(DVec2::ZERO); + let offset_vector = document.metadata().downstream_transform_to_viewport(layer).transform_vector2(DVec2::NEG_Y * radius); + + let repeat_center = center + offset_vector; + + if repeat_center.distance(mouse_position) < NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH { + self.layer = Some(layer); + self.update_state(RepeatCountDialState::Hover); + self.initial_points = count; + } + + self.selected_layers.insert(layer, count); + } + RepeatCountDialState::Hover | RepeatCountDialState::Dragging => { + // Even though we the gizmo is in hovered state store the other layers + let Some((_, _, count)) = extract_circular_repeat_parameters(Some(layer), document) else { + return; + }; + self.selected_layers.insert(layer, count); + } + } + } + + pub fn overlays(&self, layer: Option, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + let GizmoContext { document, .. } = ctx; + + match &self.handle_state { + RepeatCountDialState::Inactive => { + let Some(layer) = layer else { return }; + let Some((angle, radius, count)) = extract_circular_repeat_parameters(Some(layer), document) else { + return; + }; + let viewport = document.metadata().transform_to_viewport(layer); + + let center = viewport.transform_point2(DVec2::ZERO); + let offset_vector = document.metadata().downstream_transform_to_viewport(layer).transform_vector2(DVec2::NEG_Y * radius); + let repeat_center = center + offset_vector; + + if repeat_center.distance(mouse_position) < radius.abs() { + self.draw_spokes(repeat_center, document.metadata().downstream_transform_to_viewport(layer), count, angle.to_radians(), overlay_context); + } + } + RepeatCountDialState::Hover | RepeatCountDialState::Dragging => { + let Some(layer) = self.layer else { return }; + let Some((angle, radius, count)) = extract_circular_repeat_parameters(Some(layer), document) else { + return; + }; + let viewport = document.metadata().transform_to_viewport(layer); + + let center = viewport.transform_point2(DVec2::ZERO); + let offset_vector = document.metadata().downstream_transform_to_viewport(layer).transform_vector2(DVec2::NEG_Y * radius); + + let repeat_center = center + offset_vector; + + self.draw_spokes(repeat_center, document.metadata().downstream_transform_to_viewport(layer), count, angle.to_radians(), overlay_context); + } + } + } + + fn draw_spokes(&self, center: DVec2, viewport: DAffine2, count: u32, angle: f64, overlay_context: &mut OverlayContext) { + for i in 0..count { + let angle = ((i as f64) * TAU) / (count as f64) + angle + FRAC_PI_2; + + let direction_vector = viewport.transform_vector2(DVec2 { x: angle.cos(), y: -angle.sin() }); + + let end_point = direction_vector * 20.; + if matches!(self.handle_state, RepeatCountDialState::Hover | RepeatCountDialState::Dragging) { + overlay_context.line(center, end_point * NUMBER_OF_POINTS_DIAL_SPOKE_EXTENSION + center, None, None); + } else { + overlay_context.line(center, end_point + center, None, None); + } + } + } + + pub fn update_number_of_sides(&self, drag_start: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, input, responses, .. } = ctx; + + let delta = input.mouse.position - drag_start; + let sign = (input.mouse.position.x - drag_start.x).signum(); + let net_delta = (delta.length() / 25.).round() * sign; + + for (layer, count) in &self.selected_layers { + let Some(node_id) = graph_modification_utils::get_circular_repeat(*layer, &document.network_interface) else { + return; + }; + let new_point_count = ((*count as i32) + (net_delta as i32)).max(1); + + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, 3), + input: NodeInput::value(TaggedValue::U32(new_point_count as u32), false), + }); + } + responses.add(NodeGraphMessage::RunDocumentGraph); + } +} diff --git a/editor/src/messages/tool/common_functionality/gizmos/operation_gizmos/mod.rs b/editor/src/messages/tool/common_functionality/gizmos/operation_gizmos/mod.rs new file mode 100644 index 0000000000..a9d34b6f30 --- /dev/null +++ b/editor/src/messages/tool/common_functionality/gizmos/operation_gizmos/mod.rs @@ -0,0 +1 @@ +pub mod count_gizmos; diff --git a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/circle_arc_radius_handle.rs b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/circle_arc_radius_handle.rs index 2cbf1c4509..0584308635 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/circle_arc_radius_handle.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/circle_arc_radius_handle.rs @@ -1,17 +1,15 @@ use crate::consts::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, InputPreprocessorMessageHandler, NodeGraphMessage}; +use crate::messages::prelude::{DocumentMessageHandler, NodeGraphMessage}; use crate::messages::prelude::{FrontendMessage, Responses}; use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_arc_id, get_stroke_width}; -use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_arc_parameters, extract_circle_radius}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, extract_arc_parameters, extract_circle_radius}; use glam::{DAffine2, DVec2}; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; -use std::collections::VecDeque; use std::f64::consts::FRAC_PI_2; #[derive(Clone, Debug, Default, PartialEq)] @@ -77,7 +75,9 @@ impl RadiusHandle { stroke_width + extra_spacing } - pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2, responses: &mut VecDeque) { + pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, responses, .. } = ctx; + match &self.handle_state { RadiusHandleState::Inactive => { let Some(radius) = extract_circle_radius(layer, document).or(extract_arc_parameters(Some(layer), document).map(|(r, _, _, _)| r)) else { @@ -107,7 +107,9 @@ impl RadiusHandle { } } - pub fn overlays(&self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext) { + pub fn overlays(&self, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + let GizmoContext { document, .. } = ctx; + match &self.handle_state { RadiusHandleState::Inactive => {} RadiusHandleState::Dragging | RadiusHandleState::Hover => { @@ -145,7 +147,9 @@ impl RadiusHandle { } } - pub fn update_inner_radius(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, drag_start: DVec2) { + pub fn update_inner_radius(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, responses, input, .. } = ctx; + let Some(layer) = self.layer else { return }; let Some(node_id) = graph_modification_utils::get_circle_id(layer, &document.network_interface).or(get_arc_id(layer, &document.network_interface)) else { return; diff --git a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/number_of_points_dial.rs b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/number_of_points_dial.rs index fa4dbe2ed8..66ffe42906 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/number_of_points_dial.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/number_of_points_dial.rs @@ -1,19 +1,18 @@ use crate::consts::{GIZMO_HIDE_THRESHOLD, NUMBER_OF_POINTS_DIAL_SPOKE_EXTENSION, NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH, POINT_RADIUS_HANDLE_SEGMENT_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::Responses; -use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage, InputPreprocessorMessageHandler, NodeGraphMessage}; +use crate::messages::prelude::{FrontendMessage, NodeGraphMessage}; use crate::messages::tool::common_functionality::graph_modification_utils; -use crate::messages::tool::common_functionality::shape_editor::ShapeState; -use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_polygon_parameters, inside_polygon, inside_star, polygon_outline, polygon_vertex_position, star_outline}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{ + GizmoContext, extract_polygon_parameters, inside_polygon, inside_star, polygon_outline, polygon_vertex_position, star_outline, +}; use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position}; use glam::{DAffine2, DVec2}; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; -use std::collections::VecDeque; use std::f64::consts::TAU; #[derive(Clone, Debug, Default, PartialEq)] @@ -49,7 +48,8 @@ impl NumberOfPointsDial { self.handle_state == NumberOfPointsDialState::Dragging } - pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { + pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, responses, .. } = ctx; match &self.handle_state { NumberOfPointsDialState::Inactive => { // Star @@ -97,7 +97,9 @@ impl NumberOfPointsDial { } } - pub fn overlays(&self, document: &DocumentMessageHandler, layer: Option, shape_editor: &mut &mut ShapeState, mouse_position: DVec2, overlay_context: &mut OverlayContext) { + pub fn overlays(&self, layer: Option, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + let GizmoContext { document, shape_editor, .. } = ctx; + match &self.handle_state { NumberOfPointsDialState::Inactive => { let Some(layer) = layer else { return }; @@ -188,7 +190,9 @@ impl NumberOfPointsDial { } } - pub fn update_number_of_sides(&self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, drag_start: DVec2) { + pub fn update_number_of_sides(&self, drag_start: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, input, responses, .. } = ctx; + let delta = input.mouse.position - drag_start; let sign = (input.mouse.position.x - drag_start.x).signum(); let net_delta = (delta.length() / 25.).round() * sign; diff --git a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/point_radius_handle.rs b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/point_radius_handle.rs index 25516284bd..a4e87bf1e6 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/point_radius_handle.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/point_radius_handle.rs @@ -1,19 +1,17 @@ use crate::consts::GIZMO_HIDE_THRESHOLD; use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_THRESHOLD}; use crate::messages::frontend::utility_types::MouseCursorIcon; -use crate::messages::message::Message; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector}; use crate::messages::prelude::FrontendMessage; use crate::messages::prelude::Responses; -use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage}; +use crate::messages::prelude::{DocumentMessageHandler, NodeGraphMessage}; use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer}; -use crate::messages::tool::common_functionality::shapes::shape_utility::{draw_snapping_ticks, extract_polygon_parameters, polygon_outline, polygon_vertex_position, star_outline}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, draw_snapping_ticks, extract_polygon_parameters, polygon_outline, polygon_vertex_position, star_outline}; use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position}; use glam::DVec2; use graph_craft::document::NodeInput; use graph_craft::document::value::TaggedValue; -use std::collections::VecDeque; use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_4, PI, SQRT_2}; #[derive(Clone, Debug, Default, PartialEq)] @@ -54,7 +52,8 @@ impl PointRadiusHandle { self.handle_state = state; } - pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2, responses: &mut VecDeque) { + pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, responses, .. } = ctx; match &self.handle_state { PointRadiusHandleState::Inactive => { // Draw the point handle gizmo for the star shape @@ -142,7 +141,9 @@ impl PointRadiusHandle { } } - pub fn overlays(&self, selected_star_layer: Option, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, overlay_context: &mut OverlayContext) { + pub fn overlays(&self, selected_star_layer: Option, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + let GizmoContext { document, input, .. } = ctx; + match &self.handle_state { PointRadiusHandleState::Inactive => { let Some(layer) = selected_star_layer else { return }; @@ -411,7 +412,9 @@ impl PointRadiusHandle { .map(|(i, rad)| (i, *rad - original_radius)) } - pub fn update_inner_radius(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, drag_start: DVec2) { + pub fn update_inner_radius(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, responses, input, .. } = ctx; + let Some(layer) = self.layer else { return }; let Some(node_id) = graph_modification_utils::get_star_id(layer, &document.network_interface).or(graph_modification_utils::get_polygon_id(layer, &document.network_interface)) else { 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 68d69d9eba..638cf80529 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 @@ -3,9 +3,8 @@ 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; 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::common_functionality::shapes::shape_utility::{GizmoContext, arc_end_points, calculate_arc_text_transform, extract_arc_parameters, format_rounded}; use crate::messages::tool::tool_messages::tool_prelude::*; use glam::DVec2; use graph_craft::document::value::TaggedValue; @@ -56,7 +55,9 @@ 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) { + pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + let GizmoContext { document, .. } = ctx; + 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 { @@ -92,14 +93,9 @@ impl SweepAngleGizmo { } } - pub fn overlays( - &self, - selected_arc_layer: Option, - document: &DocumentMessageHandler, - _input: &InputPreprocessorMessageHandler, - _mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { + pub fn overlays(&self, selected_arc_layer: Option, _mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + let GizmoContext { document, .. } = ctx; + let tilt_offset = document.document_ptz.unmodified_tilt(); match self.handle_state { @@ -183,7 +179,9 @@ impl SweepAngleGizmo { 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) { + pub fn update_arc(&mut self, ctx: &mut GizmoContext) { + let GizmoContext { document, input, responses, .. } = ctx; + let Some(layer) = self.layer else { return }; let Some((_, current_start_angle, current_sweep_angle, _)) = extract_arc_parameters(Some(layer), document) else { return; diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index 4cde2d08d6..c35473ec08 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -367,6 +367,10 @@ pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text") } +pub fn get_circular_repeat(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option { + NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Circular Repeat") +} + /// Gets properties from the Text node pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig, bool)> { let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?; diff --git a/editor/src/messages/tool/common_functionality/operations/circular_repeat.rs b/editor/src/messages/tool/common_functionality/operations/circular_repeat.rs index 19ed343c3c..a210f84690 100644 --- a/editor/src/messages/tool/common_functionality/operations/circular_repeat.rs +++ b/editor/src/messages/tool/common_functionality/operations/circular_repeat.rs @@ -1,10 +1,60 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; -use crate::messages::tool::common_functionality::shapes::shape_utility::extract_circular_repeat_parameters; +use crate::messages::tool::common_functionality::gizmos::operation_gizmos::count_gizmos::{RepeatCountDial, RepeatCountDialState}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeGizmoHandler, extract_circular_repeat_parameters}; use crate::messages::tool::tool_messages::operation_tool::{OperationToolData, OperationToolFsmState}; use crate::messages::tool::tool_messages::tool_prelude::*; use std::collections::VecDeque; +#[derive(Clone, Debug, Default)] +pub struct CircularRepeatGizmoHandler { + count_dial: RepeatCountDial, +} + +impl CircularRepeatGizmoHandler { + pub fn new() -> Self { + Self { ..Default::default() } + } +} + +impl ShapeGizmoHandler for CircularRepeatGizmoHandler { + fn handle_state(&mut self, selected_shape_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + self.count_dial.handle_actions(selected_shape_layer, mouse_position, ctx); + } + + fn is_any_gizmo_hovered(&self) -> bool { + self.count_dial.is_hovering() + } + + fn handle_click(&mut self) { + if self.count_dial.is_hovering() { + self.count_dial.update_state(RepeatCountDialState::Dragging); + } + } + + fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { + if self.count_dial.is_dragging() { + self.count_dial.update_number_of_sides(drag_start, ctx); + } + } + + fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + if self.count_dial.is_dragging() { + self.count_dial.overlays(None, mouse_position, ctx, overlay_context); + } + } + + fn overlays(&self, selected_shape_layer: Option, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + self.count_dial.overlays(selected_shape_layer, mouse_position, ctx, overlay_context); + } + + fn mouse_cursor_icon(&self) -> Option { + None + } + + fn cleanup(&mut self) {} +} + #[derive(Default)] pub struct CircularRepeatOperation; @@ -77,9 +127,7 @@ impl CircularRepeatOperation { for (layer, initial_radius) in &tool_data.circular_operation_data.layers_dragging { // If the layer’s sign differs from the clicked layer, invert delta to preserve consistent in/out dragging behavior - let Some((angle, _, count)) = extract_circular_repeat_parameters(Some(*layer), document) else { - return; - }; + let (angle, _, count) = extract_circular_repeat_parameters(Some(*layer), document).unwrap_or((0.0, 0.0, 6)); let new_radius = if initial_radius.signum() == clicked_radius.signum() { *initial_radius + delta @@ -107,25 +155,12 @@ impl CircularRepeatOperation { ) { match tool_state { OperationToolFsmState::Ready => { - // Draw overlays for all selected layers - for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) { - Self::draw_layer_overlay(layer, document, input, overlay_context) - } - // Also highlight the hovered layer if it’s not selected if let Some(layer) = document.click(&input) { Self::draw_layer_overlay(layer, document, input, overlay_context); } } - _ => { - // While dragging, only draw overlays for the layers being modified - for layer in tool_data.circular_operation_data.layers_dragging.iter().map(|(l, _)| l) { - let Some(vector) = document.network_interface.compute_modified_vector(*layer) else { continue }; - let viewport = document.metadata().transform_to_viewport(*layer); - - overlay_context.outline_vector(&vector, viewport); - } - } + _ => {} } } @@ -133,11 +168,11 @@ impl CircularRepeatOperation { if let Some(vector) = document.network_interface.compute_modified_vector(layer) { let viewport = document.metadata().transform_to_viewport(layer); let center = viewport.transform_point2(DVec2::ZERO); + // Show a small circle if the mouse is near the repeat center if center.distance(input.mouse.position) < 5. { overlay_context.circle(center, 3., None, None); } - overlay_context.outline_vector(&vector, viewport); } } 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 68803598de..c9f60a02cd 100644 --- a/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/arc_shape.rs @@ -2,12 +2,13 @@ use super::shape_utility::ShapeToolModifierKey; use super::*; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; +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, NodeTemplate}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::sweep_angle_gizmo::{SweepAngleGizmo, SweepAngleGizmoState}; use crate::messages::tool::common_functionality::graph_modification_utils; -use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, arc_outline}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeGizmoHandler, arc_outline}; use crate::messages::tool::tool_messages::tool_prelude::*; use glam::DAffine2; use graph_craft::document::NodeInput; @@ -28,9 +29,9 @@ impl ArcGizmoHandler { } impl ShapeGizmoHandler for ArcGizmoHandler { - fn handle_state(&mut self, selected_shape_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - self.sweep_angle_gizmo.handle_actions(selected_shape_layer, document, mouse_position); - self.arc_radius_handle.handle_actions(selected_shape_layer, document, mouse_position, responses); + fn handle_state(&mut self, selected_shape_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + self.sweep_angle_gizmo.handle_actions(selected_shape_layer, mouse_position, ctx); + self.arc_radius_handle.handle_actions(selected_shape_layer, mouse_position, ctx); } fn is_any_gizmo_hovered(&self) -> bool { @@ -54,61 +55,46 @@ impl ShapeGizmoHandler for ArcGizmoHandler { } } - fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { + fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { if self.sweep_angle_gizmo.is_dragging_or_snapped() { - self.sweep_angle_gizmo.update_arc(document, input, responses); + self.sweep_angle_gizmo.update_arc(ctx); } if self.arc_radius_handle.is_dragging() { - self.arc_radius_handle.update_inner_radius(document, input, responses, drag_start); + self.arc_radius_handle.update_inner_radius(drag_start, ctx); } } - fn dragging_overlays( - &self, - document: &DocumentMessageHandler, - input: &InputPreprocessorMessageHandler, - _shape_editor: &mut &mut crate::messages::tool::common_functionality::shape_editor::ShapeState, - mouse_position: DVec2, - overlay_context: &mut crate::messages::portfolio::document::overlays::utility_types::OverlayContext, - ) { + fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { if self.sweep_angle_gizmo.is_dragging_or_snapped() { - self.sweep_angle_gizmo.overlays(None, document, input, mouse_position, overlay_context); - arc_outline(self.sweep_angle_gizmo.layer, document, overlay_context); + self.sweep_angle_gizmo.overlays(None, mouse_position, ctx, overlay_context); + arc_outline(self.sweep_angle_gizmo.layer, ctx.document, overlay_context); } if self.arc_radius_handle.is_dragging() { - self.sweep_angle_gizmo.overlays(self.arc_radius_handle.layer, document, input, mouse_position, overlay_context); - self.arc_radius_handle.overlays(document, overlay_context); + self.sweep_angle_gizmo.overlays(self.arc_radius_handle.layer, mouse_position, ctx, overlay_context); + self.arc_radius_handle.overlays(ctx, overlay_context); } } - fn overlays( - &self, - document: &DocumentMessageHandler, - selected_shape_layer: Option, - input: &InputPreprocessorMessageHandler, - _shape_editor: &mut &mut crate::messages::tool::common_functionality::shape_editor::ShapeState, - mouse_position: DVec2, - overlay_context: &mut crate::messages::portfolio::document::overlays::utility_types::OverlayContext, - ) { + fn overlays(&self, selected_shape_layer: Option, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { // If hovering over both the gizmos give priority to sweep angle gizmo if self.sweep_angle_gizmo.hovered() && self.arc_radius_handle.hovered() { - self.sweep_angle_gizmo.overlays(selected_shape_layer, document, input, mouse_position, overlay_context); + self.sweep_angle_gizmo.overlays(selected_shape_layer, mouse_position, ctx, overlay_context); return; } if self.arc_radius_handle.hovered() { let layer = self.arc_radius_handle.layer; - self.arc_radius_handle.overlays(document, overlay_context); - self.sweep_angle_gizmo.overlays(layer, document, input, mouse_position, overlay_context); + self.arc_radius_handle.overlays(ctx, overlay_context); + self.sweep_angle_gizmo.overlays(layer, mouse_position, ctx, overlay_context); } - self.sweep_angle_gizmo.overlays(selected_shape_layer, document, input, mouse_position, overlay_context); - self.arc_radius_handle.overlays(document, overlay_context); + self.sweep_angle_gizmo.overlays(selected_shape_layer, mouse_position, ctx, overlay_context); + self.arc_radius_handle.overlays(ctx, overlay_context); - arc_outline(selected_shape_layer.or(self.sweep_angle_gizmo.layer), document, overlay_context); + arc_outline(selected_shape_layer.or(self.sweep_angle_gizmo.layer), ctx.document, overlay_context); } fn mouse_cursor_icon(&self) -> Option { diff --git a/editor/src/messages/tool/common_functionality/shapes/circle_shape.rs b/editor/src/messages/tool/common_functionality/shapes/circle_shape.rs index d622950260..a3f86ccf44 100644 --- a/editor/src/messages/tool/common_functionality/shapes/circle_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/circle_shape.rs @@ -5,8 +5,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState}; use crate::messages::tool::common_functionality::graph_modification_utils; -use crate::messages::tool::common_functionality::shape_editor::ShapeState; -use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, ShapeToolModifierKey}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeGizmoHandler, ShapeToolModifierKey}; use crate::messages::tool::tool_messages::shape_tool::ShapeToolData; use crate::messages::tool::tool_messages::tool_prelude::*; use glam::DAffine2; @@ -23,8 +22,8 @@ impl ShapeGizmoHandler for CircleGizmoHandler { self.circle_radius_handle.hovered() } - fn handle_state(&mut self, selected_circle_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - self.circle_radius_handle.handle_actions(selected_circle_layer, document, mouse_position, responses); + fn handle_state(&mut self, selected_circle_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + self.circle_radius_handle.handle_actions(selected_circle_layer, mouse_position, ctx); } fn handle_click(&mut self) { @@ -33,34 +32,19 @@ impl ShapeGizmoHandler for CircleGizmoHandler { } } - fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { + fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { if self.circle_radius_handle.is_dragging() { - self.circle_radius_handle.update_inner_radius(document, input, responses, drag_start); + self.circle_radius_handle.update_inner_radius(drag_start, ctx); } } - fn overlays( - &self, - document: &DocumentMessageHandler, - _selected_circle_layer: Option, - _input: &InputPreprocessorMessageHandler, - _shape_editor: &mut &mut ShapeState, - _mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { - self.circle_radius_handle.overlays(document, overlay_context); + fn overlays(&self, _selected_circle_layer: Option, _mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + self.circle_radius_handle.overlays(ctx, overlay_context); } - fn dragging_overlays( - &self, - document: &DocumentMessageHandler, - _input: &InputPreprocessorMessageHandler, - _shape_editor: &mut &mut ShapeState, - _mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { + fn dragging_overlays(&self, _mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { if self.circle_radius_handle.is_dragging() { - self.circle_radius_handle.overlays(document, overlay_context); + self.circle_radius_handle.overlays(ctx, overlay_context); } } 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 ef90e7a4fa..10ad42b8ae 100644 --- a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs @@ -11,7 +11,7 @@ use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::PointRadiusHandle; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::PointRadiusHandleState; use crate::messages::tool::common_functionality::graph_modification_utils; -use crate::messages::tool::common_functionality::shape_editor::ShapeState; +use crate::messages::tool::common_functionality::shapes::shape_utility::GizmoContext; use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler; use crate::messages::tool::common_functionality::shapes::shape_utility::polygon_outline; use crate::messages::tool::tool_messages::tool_prelude::*; @@ -31,9 +31,9 @@ impl ShapeGizmoHandler for PolygonGizmoHandler { self.number_of_points_dial.is_hovering() || self.point_radius_handle.hovered() } - fn handle_state(&mut self, selected_star_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, document, responses); - self.point_radius_handle.handle_actions(selected_star_layer, document, mouse_position, responses); + fn handle_state(&mut self, selected_polygon_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + self.number_of_points_dial.handle_actions(selected_polygon_layer, mouse_position, ctx); + self.point_radius_handle.handle_actions(selected_polygon_layer, mouse_position, ctx); } fn handle_click(&mut self) { @@ -47,45 +47,31 @@ impl ShapeGizmoHandler for PolygonGizmoHandler { } } - fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { + fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { if self.number_of_points_dial.is_dragging() { - self.number_of_points_dial.update_number_of_sides(document, input, responses, drag_start); + self.number_of_points_dial.update_number_of_sides(drag_start, ctx); } if self.point_radius_handle.is_dragging_or_snapped() { - self.point_radius_handle.update_inner_radius(document, input, responses, drag_start); + self.point_radius_handle.update_inner_radius(drag_start, ctx); } } - fn overlays( - &self, - document: &DocumentMessageHandler, - selected_polygon_layer: Option, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { - self.number_of_points_dial.overlays(document, selected_polygon_layer, shape_editor, mouse_position, overlay_context); - self.point_radius_handle.overlays(selected_polygon_layer, document, input, overlay_context); + fn overlays(&self, selected_polygon_layer: Option, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + self.number_of_points_dial.overlays(selected_polygon_layer, mouse_position, ctx, overlay_context); + self.point_radius_handle.overlays(selected_polygon_layer, ctx, overlay_context); + let GizmoContext { document, .. } = ctx; polygon_outline(selected_polygon_layer, document, overlay_context); } - fn dragging_overlays( - &self, - document: &DocumentMessageHandler, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { + fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { if self.number_of_points_dial.is_dragging() { - self.number_of_points_dial.overlays(document, None, shape_editor, mouse_position, overlay_context); + self.number_of_points_dial.overlays(None, mouse_position, ctx, overlay_context); } if self.point_radius_handle.is_dragging_or_snapped() { - self.point_radius_handle.overlays(None, document, input, overlay_context); + self.point_radius_handle.overlays(None, ctx, overlay_context); } } 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 b049612948..f1e250104e 100644 --- a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs +++ b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs @@ -79,13 +79,20 @@ impl ShapeType { pub type ShapeToolModifierKey = [Key; 3]; +pub struct GizmoContext<'a> { + pub document: &'a DocumentMessageHandler, + pub input: &'a InputPreprocessorMessageHandler, + pub responses: &'a mut VecDeque, + pub shape_editor: &'a mut ShapeState, +} + /// 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). pub trait ShapeGizmoHandler { /// Called every frame to update the gizmo's interaction state based on the mouse position and selection. /// /// This includes detecting hover states and preparing interaction flags or visual feedback (e.g., highlighting a hovered handle). - fn handle_state(&mut self, selected_shape_layers: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque); + fn handle_state(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext); /// Called when a mouse click occurs over the canvas and a gizmo handle is hovered. /// @@ -96,32 +103,17 @@ pub trait ShapeGizmoHandler { /// Called during a drag interaction to update the shape's parameters in real time. /// /// For example, a handle might calculate the distance from the drag start to determine a new radius or update the number of points. - fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque); + fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext); /// Draws the static or hover-dependent overlays associated with the gizmo. /// /// These overlays include visual indicators like shape outlines, control points, and hover highlights. - fn overlays( - &self, - document: &DocumentMessageHandler, - selected_shape_layers: Option, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ); + fn overlays(&self, selected_shape_layers: Option, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext); /// Draws overlays specifically during a drag operation. /// /// Used to give real-time visual feedback based on drag progress, such as showing the updated shape preview or snapping guides. - fn dragging_overlays( - &self, - document: &DocumentMessageHandler, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ); + fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext); /// Returns `true` if any handle or control point in the gizmo is currently being hovered. fn is_any_gizmo_hovered(&self) -> bool; 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 4effa98957..0f44784c1e 100644 --- a/editor/src/messages/tool/common_functionality/shapes/star_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/star_shape.rs @@ -8,8 +8,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::{Inp use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::{NumberOfPointsDial, NumberOfPointsDialState}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::{PointRadiusHandle, PointRadiusHandleState}; use crate::messages::tool::common_functionality::graph_modification_utils; -use crate::messages::tool::common_functionality::shape_editor::ShapeState; -use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, star_outline}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeGizmoHandler, star_outline}; use crate::messages::tool::tool_messages::tool_prelude::*; use core::f64; use glam::DAffine2; @@ -28,9 +27,9 @@ impl ShapeGizmoHandler for StarGizmoHandler { self.number_of_points_dial.is_hovering() || self.point_radius_handle.hovered() } - fn handle_state(&mut self, selected_star_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, document, responses); - self.point_radius_handle.handle_actions(selected_star_layer, document, mouse_position, responses); + fn handle_state(&mut self, selected_star_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) { + self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, ctx); + self.point_radius_handle.handle_actions(selected_star_layer, mouse_position, ctx); } fn handle_click(&mut self) { @@ -44,45 +43,30 @@ impl ShapeGizmoHandler for StarGizmoHandler { } } - fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { + fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) { if self.number_of_points_dial.is_dragging() { - self.number_of_points_dial.update_number_of_sides(document, input, responses, drag_start); + self.number_of_points_dial.update_number_of_sides(drag_start, ctx); } if self.point_radius_handle.is_dragging_or_snapped() { - self.point_radius_handle.update_inner_radius(document, input, responses, drag_start); + self.point_radius_handle.update_inner_radius(drag_start, ctx); } } - fn overlays( - &self, - document: &DocumentMessageHandler, - selected_star_layer: Option, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { - self.number_of_points_dial.overlays(document, selected_star_layer, shape_editor, mouse_position, overlay_context); - self.point_radius_handle.overlays(selected_star_layer, document, input, overlay_context); + fn overlays(&self, selected_star_layer: Option, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { + self.number_of_points_dial.overlays(selected_star_layer, mouse_position, ctx, overlay_context); + self.point_radius_handle.overlays(selected_star_layer, ctx, overlay_context); - star_outline(selected_star_layer, document, overlay_context); + star_outline(selected_star_layer, ctx.document, overlay_context); } - fn dragging_overlays( - &self, - document: &DocumentMessageHandler, - input: &InputPreprocessorMessageHandler, - shape_editor: &mut &mut ShapeState, - mouse_position: DVec2, - overlay_context: &mut OverlayContext, - ) { + fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) { if self.number_of_points_dial.is_dragging() { - self.number_of_points_dial.overlays(document, None, shape_editor, mouse_position, overlay_context); + self.number_of_points_dial.overlays(None, mouse_position, ctx, overlay_context); } if self.point_radius_handle.is_dragging_or_snapped() { - self.point_radius_handle.overlays(None, document, input, overlay_context); + self.point_radius_handle.overlays(None, ctx, overlay_context); } } diff --git a/editor/src/messages/tool/tool_messages/operation_tool.rs b/editor/src/messages/tool/tool_messages/operation_tool.rs index c8e5bf3f6d..40313d257a 100644 --- a/editor/src/messages/tool/tool_messages/operation_tool.rs +++ b/editor/src/messages/tool/tool_messages/operation_tool.rs @@ -1,6 +1,9 @@ use super::tool_prelude::*; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; +use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; +use crate::messages::tool::common_functionality::gizmos::gizmo_manager::GizmoManager; use crate::messages::tool::common_functionality::operations::circular_repeat::{CircularRepeatOperation, CircularRepeatOperationData}; +use crate::messages::tool::common_functionality::shapes::shape_utility::GizmoContext; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)] pub enum OperationType { @@ -52,6 +55,7 @@ pub enum OperationToolMessage { pub enum OperationToolFsmState { #[default] Ready, + ModifyingGizmo, Drawing, } @@ -124,7 +128,7 @@ impl<'a> MessageHandler> for Oper Confirm, Abort, ), - OperationToolFsmState::Drawing => actions!(OperationToolMessageDiscriminant; + OperationToolFsmState::Drawing | OperationToolFsmState::ModifyingGizmo => actions!(OperationToolMessageDiscriminant; DragStop, PointerMove, Confirm, @@ -151,12 +155,24 @@ impl ToolTransition for OperationTool { pub struct OperationToolData { pub drag_start: DVec2, pub circular_operation_data: CircularRepeatOperationData, + gizmo_manager: GizmoManager, } impl OperationToolData { fn cleanup(&mut self) { CircularRepeatOperation::cleanup(self); } + + fn common_overlays(&self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, overlay_context: &mut OverlayContext) { + for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) { + outline_layer(layer, document, overlay_context); + + let Some(hovered_layer) = document.click(input) else { continue }; + if hovered_layer != layer { + outline_layer(hovered_layer, document, overlay_context); + } + } + } } impl Fsm for OperationToolFsmState { @@ -171,19 +187,37 @@ impl Fsm for OperationToolFsmState { tool_options: &Self::ToolOptions, responses: &mut VecDeque, ) -> Self { - let ToolActionMessageContext { document, input, .. } = tool_action_data; + let ToolActionMessageContext { document, input, shape_editor, .. } = tool_action_data; + + let mut ctx = GizmoContext { + document, + input, + responses, + shape_editor, + }; let ToolMessage::Operation(event) = event else { return self }; match (self, event) { (_, OperationToolMessage::Overlays { context: mut overlay_context }) => { - match tool_options.operation_type { - OperationType::CircularRepeat => CircularRepeatOperation::overlays(&self, tool_data, document, input, &mut overlay_context), - _ => {} + if matches!(self, OperationToolFsmState::Ready) { + tool_data.gizmo_manager.handle_operation_actions(input.mouse.position, &mut ctx); + tool_data.gizmo_manager.overlays(input.mouse.position, &mut ctx, &mut overlay_context); + CircularRepeatOperation::overlays(&self, tool_data, document, input, &mut overlay_context); } + if matches!(self, OperationToolFsmState::ModifyingGizmo) { + tool_data.gizmo_manager.overlays(input.mouse.position, &mut ctx, &mut overlay_context); + } + + tool_data.common_overlays(document, input, &mut overlay_context); + self } (OperationToolFsmState::Ready, OperationToolMessage::DragStart) => { + if tool_data.gizmo_manager.handle_click() { + tool_data.drag_start = input.mouse.position; + return OperationToolFsmState::ModifyingGizmo; + } match tool_options.operation_type { OperationType::CircularRepeat => { CircularRepeatOperation::create_node(tool_data, document, responses, input); @@ -193,7 +227,7 @@ impl Fsm for OperationToolFsmState { OperationToolFsmState::Drawing } - (OperationToolFsmState::Drawing, OperationToolMessage::DragStop) => { + (OperationToolFsmState::Drawing | OperationToolFsmState::ModifyingGizmo, OperationToolMessage::DragStop) => { if tool_data.drag_start.distance(input.mouse.position) < 5. { responses.add(DocumentMessage::AbortTransaction); }; @@ -216,6 +250,12 @@ impl Fsm for OperationToolFsmState { OperationToolFsmState::Drawing } + (OperationToolFsmState::ModifyingGizmo, OperationToolMessage::PointerMove) => { + // Don't add the repeat node unless dragging more that 5 px + tool_data.gizmo_manager.handle_update(tool_data.drag_start, &mut ctx); + + OperationToolFsmState::ModifyingGizmo + } (OperationToolFsmState::Drawing, OperationToolMessage::IncreaseCount) => { match tool_options.operation_type { OperationType::CircularRepeat => CircularRepeatOperation::increase_decrease_count(tool_data, true, document, responses), @@ -237,7 +277,7 @@ impl Fsm for OperationToolFsmState { (OperationToolFsmState::Drawing, OperationToolMessage::PointerOutsideViewport) => OperationToolFsmState::Drawing, (state, OperationToolMessage::PointerOutsideViewport) => state, - (OperationToolFsmState::Drawing, OperationToolMessage::Abort) => { + (OperationToolFsmState::Drawing | OperationToolFsmState::ModifyingGizmo, OperationToolMessage::Abort) => { responses.add(DocumentMessage::AbortTransaction); OperationToolFsmState::Ready } @@ -248,11 +288,11 @@ impl Fsm for OperationToolFsmState { fn update_hints(&self, responses: &mut VecDeque) { let hint_data = match self { - OperationToolFsmState::Ready => HintData(vec![HintGroup(vec![ + OperationToolFsmState::Ready | OperationToolFsmState::ModifyingGizmo => HintData(vec![HintGroup(vec![ HintInfo::mouse(MouseMotion::Lmb, "Draw Spline"), HintInfo::keys([Key::Shift], "Append to Selected Layer").prepend_plus(), ])]), - OperationToolFsmState::Drawing => HintData(vec![ + _ => HintData(vec![ HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Extend Spline")]), HintGroup(vec![HintInfo::keys([Key::Enter], "End Spline")]), @@ -266,3 +306,9 @@ impl Fsm for OperationToolFsmState { responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }); } } + +fn outline_layer(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext) { + let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return }; + let viewport = document.metadata().transform_to_viewport(layer); + overlay_context.outline_vector(&vector, viewport); +} diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index 2c6eea957b..8612824399 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -14,7 +14,7 @@ use crate::messages::tool::common_functionality::shapes::arc_shape::Arc; use crate::messages::tool::common_functionality::shapes::circle_shape::Circle; use crate::messages::tool::common_functionality::shapes::line_shape::{LineToolData, clicked_on_line_endpoints}; use crate::messages::tool::common_functionality::shapes::polygon_shape::Polygon; -use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeToolModifierKey, ShapeType, anchor_overlays, transform_cage_overlays}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeToolModifierKey, ShapeType, anchor_overlays, transform_cage_overlays}; use crate::messages::tool::common_functionality::shapes::star_shape::Star; use crate::messages::tool::common_functionality::shapes::{Ellipse, Line, Rectangle}; use crate::messages::tool::common_functionality::snapping::{self, SnapCandidatePoint, SnapData, SnapTypeConfiguration}; @@ -477,12 +477,24 @@ impl Fsm for ShapeToolFsmState { .unwrap_or(input.mouse.position); if matches!(self, Self::Ready(_)) && !input.keyboard.key(Key::Control) { - tool_data.gizmo_manager.handle_actions(mouse_position, document, responses); - tool_data.gizmo_manager.overlays(document, input, shape_editor, mouse_position, &mut overlay_context); + let mut ctx = GizmoContext { + document, + input, + responses, + shape_editor, + }; + tool_data.gizmo_manager.handle_actions(mouse_position, &mut ctx); + tool_data.gizmo_manager.overlays(mouse_position, &mut ctx, &mut overlay_context); } if matches!(self, ShapeToolFsmState::ModifyingGizmo) && !input.keyboard.key(Key::Control) { - tool_data.gizmo_manager.dragging_overlays(document, input, shape_editor, mouse_position, &mut overlay_context); + let mut ctx = GizmoContext { + document, + input, + responses, + shape_editor, + }; + tool_data.gizmo_manager.dragging_overlays(mouse_position, &mut ctx, &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 }); @@ -541,9 +553,15 @@ impl Fsm for ShapeToolFsmState { } if matches!(self, ShapeToolFsmState::Drawing(_) | ShapeToolFsmState::DraggingLineEndpoints) { + let mut ctx = GizmoContext { + document, + input, + responses, + shape_editor, + }; Line::overlays(document, tool_data, &mut overlay_context); if tool_options.shape_type == ShapeType::Circle { - tool_data.gizmo_manager.overlays(document, input, shape_editor, mouse_position, &mut overlay_context); + tool_data.gizmo_manager.overlays(mouse_position, &mut ctx, &mut overlay_context); } } @@ -822,7 +840,13 @@ impl Fsm for ShapeToolFsmState { self } (ShapeToolFsmState::ModifyingGizmo, ShapeToolMessage::PointerMove { .. }) => { - tool_data.gizmo_manager.handle_update(tool_data.data.viewport_drag_start(document), document, input, responses); + let mut ctx = GizmoContext { + document, + input, + responses, + shape_editor, + }; + tool_data.gizmo_manager.handle_update(tool_data.data.viewport_drag_start(document), &mut ctx); responses.add(OverlaysMessage::Draw);