From 7965dfcdcc617fe956b8472a8b1f75b8e333fbe7 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Thu, 20 Mar 2025 01:42:24 +0530 Subject: [PATCH 1/6] Added initial logic for dragging --- .../messages/input_mapper/input_mappings.rs | 2 +- .../messages/tool/tool_messages/path_tool.rs | 53 ++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 9879bd459b..d40fe9538b 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -212,7 +212,7 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(Delete); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDown(Backspace); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDownNoRepeat(Tab); action_dispatch=PathToolMessage::SwapSelectedHandles), - entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift, lasso_select: Control }), + entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift, lasso_select: Control, handle_drag_from_anchor: Alt}), entry!(KeyDown(MouseRight); action_dispatch=PathToolMessage::RightClick), entry!(KeyDown(Escape); action_dispatch=PathToolMessage::Escape), entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }), diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index af194fec22..4e43d207dc 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -65,6 +65,7 @@ pub enum PathToolMessage { direct_insert_without_sliding: Key, extend_selection: Key, lasso_select: Key, + handle_drag_from_anchor: Key, }, NudgeSelectedPoints { delta_x: f64, @@ -375,6 +376,7 @@ struct PathToolData { angle: f64, opposite_handle_position: Option, snapping_axis: Option, + stored_handle_positions: Option<(DVec2, DVec2)>, } impl PathToolData { @@ -489,6 +491,7 @@ impl PathToolData { extend_selection: bool, direct_insert_without_sliding: bool, lasso_select: bool, + handle_drag_from_anchor: bool, ) -> PathToolFsmState { self.double_click_handled = false; self.opposing_handle_lengths = None; @@ -516,6 +519,40 @@ impl PathToolData { self.saved_points_before_handle_drag = old_selection; } + //here if Alt pressed and dragging only an anchor then do the desired changes + let single_anchor_selected = shape_editor.selected_points().count() == 1 && shape_editor.selected_points().any(|point| matches!(point, ManipulatorPointId::Anchor(_))); + + if single_anchor_selected && handle_drag_from_anchor { + let manipulator_point_id = shape_editor.selected_points().next().unwrap(); + let point_id = manipulator_point_id.as_anchor().unwrap(); + + let layer = document.network_interface.selected_nodes().selected_layers(document.metadata()).next().unwrap(); + let vector_data = document.network_interface.compute_modified_vector(layer).unwrap(); + + let handles: Vec<_> = vector_data.all_connected(point_id).collect(); + // log::info!("Handles are these: {:?}", handles); + + //TODO: Store the previous handle positions in tool data + + let pos1 = handles[0].to_manipulator_point().get_position(&vector_data).unwrap(); + let pos2 = handles[1].to_manipulator_point().get_position(&vector_data).unwrap(); + self.stored_handle_positions = Some((pos1, pos2)); + + for handle in &handles { + let modification_type = handle.set_relative_position(DVec2::ZERO); + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + + let manipulator_point_id = handles[0].to_manipulator_point(); + + //here decide which anchor point we need to select based on the drag? No actually do it in drag wala function + + // change the selection to one of the handles + shape_editor.deselect_all_points(); + shape_editor.select_points_by_manipulator_id(&vec![manipulator_point_id]); + responses.add(PathToolMessage::SelectionChanged); + } + self.start_dragging_point(selected_points, input, document, shape_editor); responses.add(OverlaysMessage::Draw); } @@ -831,6 +868,9 @@ impl PathToolData { let unsnapped_delta = current_mouse - previous_mouse; if self.snapping_axis.is_none() { + if self.stored_handle_positions.is_some() && !equidistant { + //here add modification for handles + } shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, opposite, responses); self.previous_mouse_position += document_to_viewport.inverse().transform_vector2(snapped_delta); } else { @@ -1021,16 +1061,27 @@ impl Fsm for PathToolFsmState { direct_insert_without_sliding, extend_selection, lasso_select, + handle_drag_from_anchor, }, ) => { let extend_selection = input.keyboard.get(extend_selection as usize); let lasso_select = input.keyboard.get(lasso_select as usize); let direct_insert_without_sliding = input.keyboard.get(direct_insert_without_sliding as usize); + let handle_drag_from_anchor = input.keyboard.get(handle_drag_from_anchor as usize); tool_data.selection_mode = None; tool_data.lasso_polygon.clear(); - tool_data.mouse_down(shape_editor, document, input, responses, extend_selection, direct_insert_without_sliding, lasso_select) + tool_data.mouse_down( + shape_editor, + document, + input, + responses, + extend_selection, + direct_insert_without_sliding, + lasso_select, + handle_drag_from_anchor, + ) } ( PathToolFsmState::Drawing { selection_shape }, From 05db905937339399508c1757d6909a853c67d768 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Sun, 30 Mar 2025 17:22:52 +0530 Subject: [PATCH 2/6] Alt drag stop makes opposite handle back to its position --- .../tool/common_functionality/shape_editor.rs | 7 +- .../messages/tool/tool_messages/path_tool.rs | 160 +++++++++++++----- 2 files changed, 124 insertions(+), 43 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 561b9601ee..bf4242b4a5 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -745,6 +745,7 @@ impl ShapeState { delta: DVec2, equidistant: bool, in_viewport_space: bool, + was_alt_dragging: bool, opposite_handle_position: Option, responses: &mut VecDeque, ) { @@ -813,9 +814,11 @@ impl ShapeState { let length = opposing_handle.copied().unwrap_or_else(|| transform.transform_vector2(other_position - anchor_position).length()); direction.map_or(other_position - anchor_position, |direction| transform.inverse().transform_vector2(-direction * length)) }; - let modification_type = other.set_relative_position(new_relative); - responses.add(GraphOperationMessage::Vector { layer, modification_type }); + if !was_alt_dragging { + let modification_type = other.set_relative_position(new_relative); + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } } } } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 4e43d207dc..3f35fae058 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -16,7 +16,7 @@ use crate::messages::tool::common_functionality::shape_editor::{ use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager}; use graphene_core::renderer::Quad; use graphene_core::vector::{ManipulatorPointId, PointId}; -use graphene_std::vector::{NoHashBuilder, SegmentId}; +use graphene_std::vector::{HandleId, NoHashBuilder, SegmentId, VectorData, VectorModificationType}; use std::vec; #[derive(Default)] @@ -376,7 +376,8 @@ struct PathToolData { angle: f64, opposite_handle_position: Option, snapping_axis: Option, - stored_handle_positions: Option<(DVec2, DVec2)>, + stored_handle_positions: Option>, + alt_dragging_from_anchor: bool, } impl PathToolData { @@ -519,38 +520,33 @@ impl PathToolData { self.saved_points_before_handle_drag = old_selection; } - //here if Alt pressed and dragging only an anchor then do the desired changes - let single_anchor_selected = shape_editor.selected_points().count() == 1 && shape_editor.selected_points().any(|point| matches!(point, ManipulatorPointId::Anchor(_))); - - if single_anchor_selected && handle_drag_from_anchor { - let manipulator_point_id = shape_editor.selected_points().next().unwrap(); - let point_id = manipulator_point_id.as_anchor().unwrap(); - - let layer = document.network_interface.selected_nodes().selected_layers(document.metadata()).next().unwrap(); - let vector_data = document.network_interface.compute_modified_vector(layer).unwrap(); - - let handles: Vec<_> = vector_data.all_connected(point_id).collect(); - // log::info!("Handles are these: {:?}", handles); - - //TODO: Store the previous handle positions in tool data - - let pos1 = handles[0].to_manipulator_point().get_position(&vector_data).unwrap(); - let pos2 = handles[1].to_manipulator_point().get_position(&vector_data).unwrap(); - self.stored_handle_positions = Some((pos1, pos2)); - - for handle in &handles { - let modification_type = handle.set_relative_position(DVec2::ZERO); - responses.add(GraphOperationMessage::Vector { layer, modification_type }); + if handle_drag_from_anchor { + if let Some((layer, point)) = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) { + // Check that selected point is an anchor + if let (Some(point_id), Some(vector_data)) = (point.as_anchor(), document.network_interface.compute_modified_vector(layer)) { + let position = ManipulatorPointId::Anchor(point_id).get_position(&vector_data).unwrap_or_default(); + let handles = vector_data.all_connected(point_id).collect::>(); + if vector_data.connected_count(point_id) == 2 { + if let (Some(pos1), Some(pos2)) = ( + handles[0].to_manipulator_point().get_position(&vector_data), + handles[1].to_manipulator_point().get_position(&vector_data), + ) { + let mut map = HashMap::new(); + map.insert(handles[0], pos1 - position); + map.insert(handles[1], pos2 - position); + self.stored_handle_positions = Some(map); + } + } + for handle in &handles { + let modification_type = handle.set_relative_position(DVec2::ZERO); + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + let manipulator_point_id = handles[0].to_manipulator_point(); + shape_editor.deselect_all_points(); + shape_editor.select_points_by_manipulator_id(&vec![manipulator_point_id]); + responses.add(PathToolMessage::SelectionChanged); + } } - - let manipulator_point_id = handles[0].to_manipulator_point(); - - //here decide which anchor point we need to select based on the drag? No actually do it in drag wala function - - // change the selection to one of the handles - shape_editor.deselect_all_points(); - shape_editor.select_points_by_manipulator_id(&vec![manipulator_point_id]); - responses.add(PathToolMessage::SelectionChanged); } self.start_dragging_point(selected_points, input, document, shape_editor); @@ -781,7 +777,7 @@ impl PathToolData { let drag_start = self.drag_start_pos; let opposite_delta = drag_start - current_mouse; - shape_editor.move_selected_points(None, document, opposite_delta, false, true, None, responses); + shape_editor.move_selected_points(None, document, opposite_delta, false, true, false, None, responses); // Calculate the projected delta and shift the points along that delta let delta = current_mouse - drag_start; @@ -793,7 +789,7 @@ impl PathToolData { _ => DVec2::new(delta.x, 0.), }; - shape_editor.move_selected_points(None, document, projected_delta, false, true, None, responses); + shape_editor.move_selected_points(None, document, projected_delta, false, true, false, None, responses); } fn stop_snap_along_axis(&mut self, shape_editor: &mut ShapeState, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { @@ -809,16 +805,33 @@ impl PathToolData { _ => DVec2::new(opposite_delta.x, 0.), }; - shape_editor.move_selected_points(None, document, opposite_projected_delta, false, true, None, responses); + shape_editor.move_selected_points(None, document, opposite_projected_delta, false, true, false, None, responses); // Calculate what actually would have been the original delta for the point, and apply that let delta = current_mouse - drag_start; - shape_editor.move_selected_points(None, document, delta, false, true, None, responses); + shape_editor.move_selected_points(None, document, delta, false, true, false, None, responses); self.snapping_axis = None; } + fn get_normalized_tangent(&mut self, point: PointId, segment: SegmentId, vector_data: &VectorData) -> Option { + let other_point = vector_data.other_point(segment, point)?; + let position = ManipulatorPointId::Anchor(point).get_position(vector_data)?; + + let mut handles = vector_data.all_connected(other_point); + let other_handle = handles.find(|handle| handle.segment == segment)?; + + let target_position = if other_handle.length(vector_data) == 0. { + ManipulatorPointId::Anchor(other_point).get_position(vector_data)? + } else { + other_handle.to_manipulator_point().get_position(vector_data)? + }; + + let tangent_vector = target_position - position; + tangent_vector.try_normalize() + } + #[allow(clippy::too_many_arguments)] fn drag( &mut self, @@ -866,12 +879,71 @@ impl PathToolData { let handle_lengths = if equidistant { None } else { self.opposing_handle_lengths.take() }; let opposite = if lock_angle { None } else { self.opposite_handle_position }; let unsnapped_delta = current_mouse - previous_mouse; + let mut was_alt_dragging = false; if self.snapping_axis.is_none() { - if self.stored_handle_positions.is_some() && !equidistant { - //here add modification for handles + if self.stored_handle_positions.is_some() && !self.alt_dragging_from_anchor && self.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD { + //checking that drag is in which direction + self.alt_dragging_from_anchor = true; + let Some(layer) = document.network_interface.selected_nodes().selected_layers(document.metadata()).next() else { + return; + }; + let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { return }; + let Some(point_id) = shape_editor.selected_points().next().unwrap().get_anchor(&vector_data) else { + return; + }; + + if vector_data.connected_count(point_id) == 2 { + let connected_segments: Vec = vector_data.all_connected(point_id).collect(); + let segment1 = connected_segments[0]; + let Some(tangent1) = self.get_normalized_tangent(point_id, segment1.segment, &vector_data) else { + return; + }; + let segment2 = connected_segments[1]; + let Some(tangent2) = self.get_normalized_tangent(point_id, segment2.segment, &vector_data) else { + return; + }; + + let delta = input.mouse.position - self.drag_start_pos; + let handle = if delta.dot(tangent1) >= delta.dot(tangent2) { + segment1.to_manipulator_point() + } else { + segment2.to_manipulator_point() + }; + + //now change the selection to this handle + shape_editor.deselect_all_points(); + shape_editor.select_points_by_manipulator_id(&vec![handle]); + responses.add(PathToolMessage::SelectionChanged); + } } - shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, opposite, responses); + + if self.alt_dragging_from_anchor && !equidistant && self.stored_handle_positions.is_some() { + // Move other handle to the position where it started + let Some(layer) = document.network_interface.selected_nodes().selected_layers(document.metadata()).next() else { + return; + }; + let Some(selected_handle) = shape_editor.selected_points().next() else { return }; + let Some(position_map) = self.stored_handle_positions.clone() else { return }; + let Some((opposite, opp_position)) = position_map + .iter() + .find_map(|(&handle, &pos)| if handle.to_manipulator_point() != *selected_handle { Some((handle, pos)) } else { None }) + else { + return; + }; + + let Ok(handles) = position_map.keys().copied().collect::>().try_into().map(|v: Vec| [v[0], v[1]]); + + let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + let modification_type = opposite.set_relative_position(opp_position); + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + + was_alt_dragging = true; + self.alt_dragging_from_anchor = false; + self.stored_handle_positions = None; + } + shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, was_alt_dragging, opposite, responses); self.previous_mouse_position += document_to_viewport.inverse().transform_vector2(snapped_delta); } else { let Some(axis) = self.snapping_axis else { return }; @@ -880,7 +952,7 @@ impl PathToolData { Axis::Y => DVec2::new(0., unsnapped_delta.y), _ => DVec2::new(unsnapped_delta.x, 0.), }; - shape_editor.move_selected_points(handle_lengths, document, projected_delta, equidistant, true, opposite, responses); + shape_editor.move_selected_points(handle_lengths, document, projected_delta, equidistant, true, false, opposite, responses); self.previous_mouse_position += document_to_viewport.inverse().transform_vector2(unsnapped_delta); } @@ -1343,6 +1415,11 @@ impl Fsm for PathToolFsmState { tool_data.handle_drag_toggle = false; } + if tool_data.alt_dragging_from_anchor { + tool_data.alt_dragging_from_anchor = false; + tool_data.stored_handle_positions = None; + } + if tool_data.select_anchor_toggled { shape_editor.deselect_all_points(); shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_anchor_select_toggle); @@ -1433,6 +1510,7 @@ impl Fsm for PathToolFsmState { (delta_x, delta_y).into(), true, false, + false, tool_data.opposite_handle_position, responses, ); From 4b2fcd9389155cc28701222313e2bdf53fc32ca5 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Sun, 13 Apr 2025 20:43:54 +0530 Subject: [PATCH 3/6] Implement new requested behaviour --- .../messages/tool/tool_messages/path_tool.rs | 45 +++---------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 60d6943989..a397ccb103 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -16,7 +16,7 @@ use crate::messages::tool::common_functionality::shape_editor::{ use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager}; use graphene_core::renderer::Quad; use graphene_core::vector::{ManipulatorPointId, PointId}; -use graphene_std::vector::{HandleId, NoHashBuilder, SegmentId, VectorData, VectorModificationType}; +use graphene_std::vector::{HandleId, NoHashBuilder, SegmentId, VectorData}; use std::vec; #[derive(Default)] @@ -376,7 +376,7 @@ struct PathToolData { angle: f64, opposite_handle_position: Option, snapping_axis: Option, - stored_handle_positions: Option>, + alt_clicked_on_anchor: bool, alt_dragging_from_anchor: bool, } @@ -524,19 +524,8 @@ impl PathToolData { if let Some((layer, point)) = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) { // Check that selected point is an anchor if let (Some(point_id), Some(vector_data)) = (point.as_anchor(), document.network_interface.compute_modified_vector(layer)) { - let position = ManipulatorPointId::Anchor(point_id).get_position(&vector_data).unwrap_or_default(); let handles = vector_data.all_connected(point_id).collect::>(); - if vector_data.connected_count(point_id) == 2 { - if let (Some(pos1), Some(pos2)) = ( - handles[0].to_manipulator_point().get_position(&vector_data), - handles[1].to_manipulator_point().get_position(&vector_data), - ) { - let mut map = HashMap::new(); - map.insert(handles[0], pos1 - position); - map.insert(handles[1], pos2 - position); - self.stored_handle_positions = Some(map); - } - } + self.alt_clicked_on_anchor = true; for handle in &handles { let modification_type = handle.set_relative_position(DVec2::ZERO); responses.add(GraphOperationMessage::Vector { layer, modification_type }); @@ -882,7 +871,7 @@ impl PathToolData { let mut was_alt_dragging = false; if self.snapping_axis.is_none() { - if self.stored_handle_positions.is_some() && !self.alt_dragging_from_anchor && self.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD { + if self.alt_clicked_on_anchor && !self.alt_dragging_from_anchor && self.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD { //checking that drag is in which direction self.alt_dragging_from_anchor = true; let Some(layer) = document.network_interface.selected_nodes().selected_layers(document.metadata()).next() else { @@ -918,30 +907,10 @@ impl PathToolData { } } - if self.alt_dragging_from_anchor && !equidistant && self.stored_handle_positions.is_some() { - // Move other handle to the position where it started - let Some(layer) = document.network_interface.selected_nodes().selected_layers(document.metadata()).next() else { - return; - }; - let Some(selected_handle) = shape_editor.selected_points().next() else { return }; - let Some(position_map) = self.stored_handle_positions.clone() else { return }; - let Some((opposite, opp_position)) = position_map - .iter() - .find_map(|(&handle, &pos)| if handle.to_manipulator_point() != *selected_handle { Some((handle, pos)) } else { None }) - else { - return; - }; - - let Ok(handles) = position_map.keys().copied().collect::>().try_into().map(|v: Vec| [v[0], v[1]]); - - let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; - responses.add(GraphOperationMessage::Vector { layer, modification_type }); - let modification_type = opposite.set_relative_position(opp_position); - responses.add(GraphOperationMessage::Vector { layer, modification_type }); - + if self.alt_dragging_from_anchor && !equidistant && self.alt_clicked_on_anchor { was_alt_dragging = true; self.alt_dragging_from_anchor = false; - self.stored_handle_positions = None; + self.alt_clicked_on_anchor = false; } shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, was_alt_dragging, opposite, responses); self.previous_mouse_position += document_to_viewport.inverse().transform_vector2(snapped_delta); @@ -1420,7 +1389,7 @@ impl Fsm for PathToolFsmState { if tool_data.alt_dragging_from_anchor { tool_data.alt_dragging_from_anchor = false; - tool_data.stored_handle_positions = None; + tool_data.alt_clicked_on_anchor = false; } if tool_data.select_anchor_toggled { From 91214367e74d2d607e7b72a2174c6e873faa0179 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Wed, 16 Apr 2025 23:27:37 +0530 Subject: [PATCH 4/6] Fix sharp point bug --- .../messages/tool/tool_messages/path_tool.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index a397ccb103..fbab59e15c 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -15,7 +15,7 @@ use crate::messages::tool::common_functionality::shape_editor::{ }; use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager}; use graphene_core::renderer::Quad; -use graphene_core::vector::{ManipulatorPointId, PointId}; +use graphene_core::vector::{ManipulatorPointId, PointId, VectorModificationType}; use graphene_std::vector::{HandleId, NoHashBuilder, SegmentId, VectorData}; use std::vec; @@ -529,11 +529,18 @@ impl PathToolData { for handle in &handles { let modification_type = handle.set_relative_position(DVec2::ZERO); responses.add(GraphOperationMessage::Vector { layer, modification_type }); + for &handles in &vector_data.colinear_manipulators { + if handles.contains(&handle) { + let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + } } + let manipulator_point_id = handles[0].to_manipulator_point(); shape_editor.deselect_all_points(); shape_editor.select_points_by_manipulator_id(&vec![manipulator_point_id]); - responses.add(PathToolMessage::SelectionChanged); + responses.add(PathToolMessage::SelectedPointUpdated); } } } @@ -1387,10 +1394,8 @@ impl Fsm for PathToolFsmState { tool_data.handle_drag_toggle = false; } - if tool_data.alt_dragging_from_anchor { - tool_data.alt_dragging_from_anchor = false; - tool_data.alt_clicked_on_anchor = false; - } + tool_data.alt_dragging_from_anchor = false; + tool_data.alt_clicked_on_anchor = false; if tool_data.select_anchor_toggled { shape_editor.deselect_all_points(); From abaf64999a47b224ef008c70b114867c905771ea Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Wed, 16 Apr 2025 20:47:34 -0700 Subject: [PATCH 5/6] Apply suggestions from code review --- editor/src/messages/input_mapper/input_mappings.rs | 2 +- editor/src/messages/tool/tool_messages/path_tool.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 7814c74152..0448726b0a 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -212,7 +212,7 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(Delete); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDown(Backspace); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath), entry!(KeyDownNoRepeat(Tab); action_dispatch=PathToolMessage::SwapSelectedHandles), - entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift, lasso_select: Control, handle_drag_from_anchor: Alt}), + entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift, lasso_select: Control, handle_drag_from_anchor: Alt }), entry!(KeyDown(MouseRight); action_dispatch=PathToolMessage::RightClick), entry!(KeyDown(Escape); action_dispatch=PathToolMessage::Escape), entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }), diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index fbab59e15c..ea417be8c5 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -879,7 +879,7 @@ impl PathToolData { if self.snapping_axis.is_none() { if self.alt_clicked_on_anchor && !self.alt_dragging_from_anchor && self.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD { - //checking that drag is in which direction + // Checking which direction the dragging begins self.alt_dragging_from_anchor = true; let Some(layer) = document.network_interface.selected_nodes().selected_layers(document.metadata()).next() else { return; @@ -907,7 +907,7 @@ impl PathToolData { segment2.to_manipulator_point() }; - //now change the selection to this handle + // Now change the selection to this handle shape_editor.deselect_all_points(); shape_editor.select_points_by_manipulator_id(&vec![handle]); responses.add(PathToolMessage::SelectionChanged); From ab4710d32d57af7e552022f5d0e96551a81614d2 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Wed, 16 Apr 2025 22:51:11 -0700 Subject: [PATCH 6/6] Add hints --- editor/src/messages/tool/tool_messages/path_tool.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index ea417be8c5..6b348cb42d 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1549,7 +1549,11 @@ impl Fsm for PathToolFsmState { HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Select Area"), HintInfo::keys([Key::Control], "Lasso").prepend_plus()]), HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Insert Point on Segment")]), // TODO: Only show if at least one anchor is selected, and dynamically show either "Smooth" or "Sharp" based on the current state - HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDouble, "Make Anchor Smooth/Sharp")]), + HintGroup(vec![ + HintInfo::mouse(MouseMotion::LmbDouble, "Convert Anchor Point"), + HintInfo::keys_and_mouse([Key::Alt], MouseMotion::Lmb, "To Sharp"), + HintInfo::keys_and_mouse([Key::Alt], MouseMotion::LmbDrag, "To Smooth"), + ]), // TODO: Only show the following hints if at least one point is selected HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]), HintGroup(vec![HintInfo::multi_keys([[Key::KeyG], [Key::KeyR], [Key::KeyS]], "Grab/Rotate/Scale Selected")]),