From e044f1315fa18447c3c835a37e7ea7ec0c8f2e64 Mon Sep 17 00:00:00 2001 From: krVatsal Date: Wed, 4 Feb 2026 23:49:36 +0530 Subject: [PATCH 1/8] fixed Box selection with the Select tool and Path tool to follow canvas while panning --- .../tool/tool_messages/select_tool.rs | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index bf30e782b0..76ce41a4b9 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -402,6 +402,7 @@ struct SelectToolData { snap_candidates: Vec, auto_panning: AutoPanning, drag_start_center: ViewportPosition, + drag_start_document: Option, } impl SelectToolData { @@ -418,13 +419,13 @@ impl SelectToolData { } } - pub fn selection_quad(&self) -> Quad { - let bbox = self.selection_box(); + pub fn selection_quad(&self, metadata: &DocumentMetadata) -> Quad { + let bbox = self.selection_box(metadata); Quad::from_box(bbox) } - pub fn calculate_selection_mode_from_direction(&mut self) -> SelectionMode { - let bbox: [DVec2; 2] = self.selection_box(); + pub fn calculate_selection_mode_from_direction(&mut self, metadata: &DocumentMetadata) -> SelectionMode { + let bbox: [DVec2; 2] = self.selection_box(metadata); let above_threshold = bbox[1].distance_squared(bbox[0]) > DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD.powi(2); if self.selection_mode.is_none() && above_threshold { @@ -440,12 +441,19 @@ impl SelectToolData { self.selection_mode.unwrap_or(SelectionMode::Touched) } - pub fn selection_box(&self) -> [DVec2; 2] { - if self.drag_current == self.drag_start { + pub fn selection_box(&self, metadata: &DocumentMetadata) -> [DVec2; 2] { + // If we have a document-anchored start point, transform it to viewport + let start_viewport = if let Some(start_doc) = self.drag_start_document { + metadata.document_to_viewport.transform_point2(start_doc) + } else { + self.drag_start + }; + + if self.drag_current == start_viewport { let tolerance = DVec2::splat(SELECTION_TOLERANCE); - [self.drag_start - tolerance, self.drag_start + tolerance] + [start_viewport - tolerance, start_viewport + tolerance] } else { - [self.drag_start, self.drag_current] + [start_viewport, self.drag_current] } } @@ -938,10 +946,10 @@ impl Fsm for SelectToolFsmState { // Check if the tool is in selection mode if let Self::Drawing { selection_shape, .. } = self { // Get the updated selection box bounds - let quad = Quad::from_box([tool_data.drag_start, tool_data.drag_current]); + let quad = tool_data.selection_quad(document.metadata()); let current_selection_mode = match tool_action_data.preferences.get_selection_mode() { - SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(), + SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(document.metadata()), SelectionMode::Touched => SelectionMode::Touched, SelectionMode::Enclosed => SelectionMode::Enclosed, }; @@ -1140,6 +1148,8 @@ impl Fsm for SelectToolFsmState { } } else { let selection_shape = if input.keyboard.key(lasso_select) { SelectionShapeType::Lasso } else { SelectionShapeType::Box }; + // Anchor the drag start to document coordinates so panning/zooming doesn't move the start point + tool_data.drag_start_document = Some(document.metadata().document_to_viewport.inverse().transform_point2(tool_data.drag_start)); SelectToolFsmState::Drawing { selection_shape, has_drawn: false } } }; @@ -1380,10 +1390,8 @@ impl Fsm for SelectToolFsmState { self } (SelectToolFsmState::Drawing { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => { - // Auto-panning - if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) { - tool_data.drag_start += shift; - } + // Auto-panning - the start is anchored to document space, so no need to shift `drag_start` here + let _ = tool_data.auto_panning.shift_viewport(input, viewport, responses); self } @@ -1404,8 +1412,8 @@ impl Fsm for SelectToolFsmState { if !has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() { // When you click on the layer with remove from selection key (shift) pressed, we deselect all nodes that are children. - let quad = tool_data.selection_quad(); - let intersection = document.intersect_quad_no_artboards(quad, viewport); + let quad = tool_data.selection_quad(document.metadata()); + let intersection: Vec<_> = document.intersect_quad_no_artboards(quad, viewport).collect(); if let Some(path) = intersection.last() { let replacement_selected_layers: Vec<_> = document @@ -1504,10 +1512,10 @@ impl Fsm for SelectToolFsmState { SelectToolFsmState::Ready { selection } } (SelectToolFsmState::Drawing { selection_shape, .. }, SelectToolMessage::DragStop { remove_from_selection }) => { - let quad = tool_data.selection_quad(); + let quad = tool_data.selection_quad(document.metadata()); let selection_mode = match tool_action_data.preferences.get_selection_mode() { - SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(), + SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(document.metadata()), selection_mode => selection_mode, }; @@ -1572,6 +1580,9 @@ impl Fsm for SelectToolFsmState { tool_data.lasso_polygon.clear(); + // Clear the document-anchored drag start when finishing drawing + tool_data.drag_start_document = None; + responses.add(OverlaysMessage::Draw); let selection = tool_data.nested_selection_behavior; From 962ab292867e70febeccb6e09578e77816dae907 Mon Sep 17 00:00:00 2001 From: krVatsal Date: Sun, 1 Mar 2026 20:00:41 +0530 Subject: [PATCH 2/8] Move drag_start_document into Drawing state variant to make invalid state unrepresentable --- .../tool/tool_messages/select_tool.rs | 64 ++++++++++++------- node_modules/.vite/deps/_metadata.json | 8 +++ node_modules/.vite/deps/package.json | 3 + 3 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 node_modules/.vite/deps/_metadata.json create mode 100644 node_modules/.vite/deps/package.json diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 76ce41a4b9..58fa9eedfe 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -344,7 +344,7 @@ impl ToolTransition for SelectTool { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq)] enum SelectToolFsmState { Ready { selection: NestedSelectionBehavior, @@ -352,6 +352,7 @@ enum SelectToolFsmState { Drawing { selection_shape: SelectionShapeType, has_drawn: bool, + drag_start_document: DVec2, }, Dragging { axis: Axis, @@ -402,7 +403,6 @@ struct SelectToolData { snap_candidates: Vec, auto_panning: AutoPanning, drag_start_center: ViewportPosition, - drag_start_document: Option, } impl SelectToolData { @@ -419,13 +419,13 @@ impl SelectToolData { } } - pub fn selection_quad(&self, metadata: &DocumentMetadata) -> Quad { - let bbox = self.selection_box(metadata); + pub fn selection_quad(&self, drag_start_document: Option, metadata: &DocumentMetadata) -> Quad { + let bbox = self.selection_box(drag_start_document, metadata); Quad::from_box(bbox) } - pub fn calculate_selection_mode_from_direction(&mut self, metadata: &DocumentMetadata) -> SelectionMode { - let bbox: [DVec2; 2] = self.selection_box(metadata); + pub fn calculate_selection_mode_from_direction(&mut self, drag_start_document: DVec2, metadata: &DocumentMetadata) -> SelectionMode { + let bbox: [DVec2; 2] = self.selection_box(Some(drag_start_document), metadata); let above_threshold = bbox[1].distance_squared(bbox[0]) > DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD.powi(2); if self.selection_mode.is_none() && above_threshold { @@ -441,9 +441,9 @@ impl SelectToolData { self.selection_mode.unwrap_or(SelectionMode::Touched) } - pub fn selection_box(&self, metadata: &DocumentMetadata) -> [DVec2; 2] { + pub fn selection_box(&self, drag_start_document: Option, metadata: &DocumentMetadata) -> [DVec2; 2] { // If we have a document-anchored start point, transform it to viewport - let start_viewport = if let Some(start_doc) = self.drag_start_document { + let start_viewport = if let Some(start_doc) = drag_start_document { metadata.document_to_viewport.transform_point2(start_doc) } else { self.drag_start @@ -944,12 +944,15 @@ impl Fsm for SelectToolFsmState { } // Check if the tool is in selection mode - if let Self::Drawing { selection_shape, .. } = self { + if let Self::Drawing { + selection_shape, drag_start_document, .. + } = self + { // Get the updated selection box bounds - let quad = tool_data.selection_quad(document.metadata()); + let quad = tool_data.selection_quad(Some(drag_start_document), document.metadata()); let current_selection_mode = match tool_action_data.preferences.get_selection_mode() { - SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(document.metadata()), + SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(drag_start_document, document.metadata()), SelectionMode::Touched => SelectionMode::Touched, SelectionMode::Enclosed => SelectionMode::Enclosed, }; @@ -1149,8 +1152,12 @@ impl Fsm for SelectToolFsmState { } else { let selection_shape = if input.keyboard.key(lasso_select) { SelectionShapeType::Lasso } else { SelectionShapeType::Box }; // Anchor the drag start to document coordinates so panning/zooming doesn't move the start point - tool_data.drag_start_document = Some(document.metadata().document_to_viewport.inverse().transform_point2(tool_data.drag_start)); - SelectToolFsmState::Drawing { selection_shape, has_drawn: false } + let drag_start_document = document.metadata().document_to_viewport.inverse().transform_point2(tool_data.drag_start); + SelectToolFsmState::Drawing { + selection_shape, + has_drawn: false, + drag_start_document, + } } }; tool_data.non_duplicated_layers = None; @@ -1299,7 +1306,14 @@ impl Fsm for SelectToolFsmState { SelectToolFsmState::DraggingPivot } - (SelectToolFsmState::Drawing { selection_shape, has_drawn }, SelectToolMessage::PointerMove { modifier_keys }) => { + ( + SelectToolFsmState::Drawing { + selection_shape, + has_drawn, + drag_start_document, + }, + SelectToolMessage::PointerMove { modifier_keys }, + ) => { if !has_drawn { responses.add(ToolMessage::UpdateHints); } @@ -1318,7 +1332,11 @@ impl Fsm for SelectToolFsmState { ]; tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses); - SelectToolFsmState::Drawing { selection_shape, has_drawn: true } + SelectToolFsmState::Drawing { + selection_shape, + has_drawn: true, + drag_start_document, + } } (SelectToolFsmState::Ready { .. }, SelectToolMessage::PointerMove { .. }) => { let dragging_bounds = tool_data @@ -1412,7 +1430,7 @@ impl Fsm for SelectToolFsmState { if !has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() { // When you click on the layer with remove from selection key (shift) pressed, we deselect all nodes that are children. - let quad = tool_data.selection_quad(document.metadata()); + let quad = tool_data.selection_quad(None, document.metadata()); let intersection: Vec<_> = document.intersect_quad_no_artboards(quad, viewport).collect(); if let Some(path) = intersection.last() { @@ -1511,11 +1529,16 @@ impl Fsm for SelectToolFsmState { let selection = tool_data.nested_selection_behavior; SelectToolFsmState::Ready { selection } } - (SelectToolFsmState::Drawing { selection_shape, .. }, SelectToolMessage::DragStop { remove_from_selection }) => { - let quad = tool_data.selection_quad(document.metadata()); + ( + SelectToolFsmState::Drawing { + selection_shape, drag_start_document, .. + }, + SelectToolMessage::DragStop { remove_from_selection }, + ) => { + let quad = tool_data.selection_quad(Some(drag_start_document), document.metadata()); let selection_mode = match tool_action_data.preferences.get_selection_mode() { - SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(document.metadata()), + SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(drag_start_document, document.metadata()), selection_mode => selection_mode, }; @@ -1580,9 +1603,6 @@ impl Fsm for SelectToolFsmState { tool_data.lasso_polygon.clear(); - // Clear the document-anchored drag start when finishing drawing - tool_data.drag_start_document = None; - responses.add(OverlaysMessage::Draw); let selection = tool_data.nested_selection_behavior; diff --git a/node_modules/.vite/deps/_metadata.json b/node_modules/.vite/deps/_metadata.json new file mode 100644 index 0000000000..88afc1fe38 --- /dev/null +++ b/node_modules/.vite/deps/_metadata.json @@ -0,0 +1,8 @@ +{ + "hash": "00a36998", + "configHash": "5d045020", + "lockfileHash": "9395696f", + "browserHash": "621fa28a", + "optimized": {}, + "chunks": {} +} \ No newline at end of file diff --git a/node_modules/.vite/deps/package.json b/node_modules/.vite/deps/package.json new file mode 100644 index 0000000000..3dbc1ca591 --- /dev/null +++ b/node_modules/.vite/deps/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} From cd5217665400ff24633ecbfadfe670caaa22dafa Mon Sep 17 00:00:00 2001 From: krVatsal Date: Sun, 1 Mar 2026 20:22:18 +0530 Subject: [PATCH 3/8] Make selection_box() unconditionally use document-space coordinates --- .../tool/tool_messages/select_tool.rs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 58fa9eedfe..ffbe60474f 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -419,13 +419,13 @@ impl SelectToolData { } } - pub fn selection_quad(&self, drag_start_document: Option, metadata: &DocumentMetadata) -> Quad { + pub fn selection_quad(&self, drag_start_document: DVec2, metadata: &DocumentMetadata) -> Quad { let bbox = self.selection_box(drag_start_document, metadata); Quad::from_box(bbox) } pub fn calculate_selection_mode_from_direction(&mut self, drag_start_document: DVec2, metadata: &DocumentMetadata) -> SelectionMode { - let bbox: [DVec2; 2] = self.selection_box(Some(drag_start_document), metadata); + let bbox: [DVec2; 2] = self.selection_box(drag_start_document, metadata); let above_threshold = bbox[1].distance_squared(bbox[0]) > DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD.powi(2); if self.selection_mode.is_none() && above_threshold { @@ -441,13 +441,9 @@ impl SelectToolData { self.selection_mode.unwrap_or(SelectionMode::Touched) } - pub fn selection_box(&self, drag_start_document: Option, metadata: &DocumentMetadata) -> [DVec2; 2] { - // If we have a document-anchored start point, transform it to viewport - let start_viewport = if let Some(start_doc) = drag_start_document { - metadata.document_to_viewport.transform_point2(start_doc) - } else { - self.drag_start - }; + pub fn selection_box(&self, drag_start_document: DVec2, metadata: &DocumentMetadata) -> [DVec2; 2] { + // Transform the document-anchored start point to viewport + let start_viewport = metadata.document_to_viewport.transform_point2(drag_start_document); if self.drag_current == start_viewport { let tolerance = DVec2::splat(SELECTION_TOLERANCE); @@ -949,7 +945,7 @@ impl Fsm for SelectToolFsmState { } = self { // Get the updated selection box bounds - let quad = tool_data.selection_quad(Some(drag_start_document), document.metadata()); + let quad = tool_data.selection_quad(drag_start_document, document.metadata()); let current_selection_mode = match tool_action_data.preferences.get_selection_mode() { SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(drag_start_document, document.metadata()), @@ -1430,7 +1426,8 @@ impl Fsm for SelectToolFsmState { if !has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() { // When you click on the layer with remove from selection key (shift) pressed, we deselect all nodes that are children. - let quad = tool_data.selection_quad(None, document.metadata()); + let drag_start_document = document.metadata().document_to_viewport.inverse().transform_point2(tool_data.drag_start); + let quad = tool_data.selection_quad(drag_start_document, document.metadata()); let intersection: Vec<_> = document.intersect_quad_no_artboards(quad, viewport).collect(); if let Some(path) = intersection.last() { @@ -1535,7 +1532,7 @@ impl Fsm for SelectToolFsmState { }, SelectToolMessage::DragStop { remove_from_selection }, ) => { - let quad = tool_data.selection_quad(Some(drag_start_document), document.metadata()); + let quad = tool_data.selection_quad(drag_start_document, document.metadata()); let selection_mode = match tool_action_data.preferences.get_selection_mode() { SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(drag_start_document, document.metadata()), From bde38550400b588c0dd84e6ea339b6d34bae585e Mon Sep 17 00:00:00 2001 From: krVatsal Date: Sun, 1 Mar 2026 20:30:45 +0530 Subject: [PATCH 4/8] fix tiny square drawing bug on initial click down --- editor/src/messages/tool/tool_messages/select_tool.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index ffbe60474f..173e2d4b17 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -939,9 +939,9 @@ impl Fsm for SelectToolFsmState { } } - // Check if the tool is in selection mode + // Check if the tool is in selection mode and has started drawing if let Self::Drawing { - selection_shape, drag_start_document, .. + selection_shape, drag_start_document, has_drawn: true, .. } = self { // Get the updated selection box bounds From 26c9a7f114acf294caabd7adbe22e6db13f5a0c5 Mon Sep 17 00:00:00 2001 From: krVatsal Date: Sun, 1 Mar 2026 20:54:17 +0530 Subject: [PATCH 5/8] replicated same changes to path tool --- .../messages/tool/tool_messages/path_tool.rs | 109 ++++++++++-------- .../tool/tool_messages/select_tool.rs | 5 +- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index d07e198437..b55a740e8d 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -543,13 +543,14 @@ pub struct SlidingPointInfo { connected_segments: [SlidingSegmentData; 2], } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] enum PathToolFsmState { #[default] Ready, Dragging(DraggingState), Drawing { selection_shape: SelectionShapeType, + drag_start_document: DVec2, }, SlidingPoint, } @@ -617,13 +618,13 @@ impl PathToolData { PathToolFsmState::Dragging(self.dragging_state) } - pub fn selection_quad(&self, metadata: &DocumentMetadata) -> Quad { - let bbox = self.selection_box(metadata); + pub fn selection_quad(&self, drag_start_document: DVec2, metadata: &DocumentMetadata) -> Quad { + let bbox = self.selection_box(drag_start_document, metadata); Quad::from_box(bbox) } - pub fn calculate_selection_mode_from_direction(&mut self, metadata: &DocumentMetadata) -> SelectionMode { - let bbox = self.selection_box(metadata); + pub fn calculate_selection_mode_from_direction(&mut self, drag_start_document: DVec2, metadata: &DocumentMetadata) -> SelectionMode { + let bbox = self.selection_box(drag_start_document, metadata); let above_threshold = bbox[1].distance_squared(bbox[0]) > DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD.powi(2); if self.selection_mode.is_none() && above_threshold { @@ -639,15 +640,16 @@ impl PathToolData { self.selection_mode.unwrap_or(SelectionMode::Touched) } - pub fn selection_box(&self, metadata: &DocumentMetadata) -> [DVec2; 2] { - // Convert previous mouse position to viewport space first + pub fn selection_box(&self, drag_start_document: DVec2, metadata: &DocumentMetadata) -> [DVec2; 2] { + // Transform the document-anchored start point to viewport let document_to_viewport = metadata.document_to_viewport; + let start_viewport = document_to_viewport.transform_point2(drag_start_document); let previous_mouse = document_to_viewport.transform_point2(self.previous_mouse_position); - if previous_mouse == self.drag_start_pos { + if previous_mouse == start_viewport { let tolerance = DVec2::splat(SELECTION_TOLERANCE); - [self.drag_start_pos - tolerance, self.drag_start_pos + tolerance] + [start_viewport - tolerance, start_viewport + tolerance] } else { - [self.drag_start_pos, previous_mouse] + [start_viewport, previous_mouse] } } @@ -898,19 +900,21 @@ impl PathToolData { self.started_drawing_from_inside = true; + let drag_start_document = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position); self.drag_start_pos = input.mouse.position; - self.previous_mouse_position = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position); + self.previous_mouse_position = drag_start_document; let selection_shape = if lasso_select { SelectionShapeType::Lasso } else { SelectionShapeType::Box }; - PathToolFsmState::Drawing { selection_shape } + PathToolFsmState::Drawing { selection_shape, drag_start_document } } // Start drawing else { + let drag_start_document = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position); self.drag_start_pos = input.mouse.position; - self.previous_mouse_position = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position); + self.previous_mouse_position = drag_start_document; let selection_shape = if lasso_select { SelectionShapeType::Lasso } else { SelectionShapeType::Box }; - PathToolFsmState::Drawing { selection_shape } + PathToolFsmState::Drawing { selection_shape, drag_start_document } } } @@ -1887,7 +1891,7 @@ impl Fsm for PathToolFsmState { overlay_context.outline(outline, layer_to_viewport, Some(color)); } } - Self::Drawing { selection_shape } => { + Self::Drawing { selection_shape, drag_start_document } => { let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) .unwrap() .with_alpha(0.05) @@ -1896,18 +1900,20 @@ impl Fsm for PathToolFsmState { let fill_color = Some(fill_color.as_str()); let selection_mode = match tool_action_data.preferences.get_selection_mode() { - SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(document.metadata()), + SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(drag_start_document, document.metadata()), selection_mode => selection_mode, }; - let quad = tool_data.selection_quad(document.metadata()); + let quad = tool_data.selection_quad(drag_start_document, document.metadata()); let select_segments = tool_options.path_editing_mode.segment_editing_mode; let select_points = tool_options.path_editing_mode.point_editing_mode; let (points_inside, segments_inside) = match selection_shape { SelectionShapeType::Box => { - let previous_mouse = document.metadata().document_to_viewport.transform_point2(tool_data.previous_mouse_position); - let bbox = Rect::new(tool_data.drag_start_pos.x, tool_data.drag_start_pos.y, previous_mouse.x, previous_mouse.y).abs(); + let document_to_viewport = document.metadata().document_to_viewport; + let start_viewport = document_to_viewport.transform_point2(drag_start_document); + let previous_mouse = document_to_viewport.transform_point2(tool_data.previous_mouse_position); + let bbox = Rect::new(start_viewport.x, start_viewport.y, previous_mouse.x, previous_mouse.y).abs(); shape_editor.get_inside_points_and_segments( &document.network_interface, SelectionShape::Box(bbox), @@ -2039,7 +2045,7 @@ impl Fsm for PathToolFsmState { ) } ( - PathToolFsmState::Drawing { selection_shape }, + PathToolFsmState::Drawing { selection_shape, drag_start_document }, PathToolMessage::PointerMove { equidistant, toggle_colinear, @@ -2089,7 +2095,7 @@ impl Fsm for PathToolFsmState { ]; tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses); - PathToolFsmState::Drawing { selection_shape } + PathToolFsmState::Drawing { selection_shape, drag_start_document } } ( PathToolFsmState::Dragging(_), @@ -2250,13 +2256,20 @@ impl Fsm for PathToolFsmState { self } - (PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => { + ( + PathToolFsmState::Drawing { + selection_shape: selection_type, + drag_start_document, + }, + PathToolMessage::PointerOutsideViewport { .. }, + ) => { // Auto-panning - if let Some(offset) = tool_data.auto_panning.shift_viewport(input, viewport, responses) { - tool_data.drag_start_pos += offset; - } + tool_data.auto_panning.shift_viewport(input, viewport, responses); - PathToolFsmState::Drawing { selection_shape: selection_type } + PathToolFsmState::Drawing { + selection_shape: selection_type, + drag_start_document, + } } (PathToolFsmState::Dragging(dragging_state), PathToolMessage::PointerOutsideViewport { .. }) => { // Auto-panning @@ -2308,7 +2321,7 @@ impl Fsm for PathToolFsmState { state } - (PathToolFsmState::Drawing { selection_shape }, PathToolMessage::Enter { extend_selection, shrink_selection }) => { + (PathToolFsmState::Drawing { selection_shape, drag_start_document }, PathToolMessage::Enter { extend_selection, shrink_selection }) => { let extend_selection = input.keyboard.get(extend_selection as usize); let shrink_selection = input.keyboard.get(shrink_selection as usize); @@ -2322,17 +2335,18 @@ impl Fsm for PathToolFsmState { let document_to_viewport = document.metadata().document_to_viewport; let previous_mouse = document_to_viewport.transform_point2(tool_data.previous_mouse_position); - if tool_data.drag_start_pos == previous_mouse { + let start_viewport = document_to_viewport.transform_point2(drag_start_document); + if start_viewport == previous_mouse { responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] }); } else { let selection_mode = match tool_action_data.preferences.get_selection_mode() { - SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(document.metadata()), + SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(drag_start_document, document.metadata()), selection_mode => selection_mode, }; match selection_shape { SelectionShapeType::Box => { - let bbox = Rect::new(tool_data.drag_start_pos.x, tool_data.drag_start_pos.y, previous_mouse.x, previous_mouse.y).abs(); + let bbox = Rect::new(start_viewport.x, start_viewport.y, previous_mouse.x, previous_mouse.y).abs(); shape_editor.select_all_in_shape( &document.network_interface, @@ -2345,16 +2359,18 @@ impl Fsm for PathToolFsmState { selection_mode, ); } - SelectionShapeType::Lasso => shape_editor.select_all_in_shape( - &document.network_interface, - SelectionShape::Lasso(&tool_data.lasso_polygon), - selection_change, - tool_options.path_overlay_mode, - tool_data.frontier_handles_info.as_ref(), - tool_options.path_editing_mode.segment_editing_mode, - tool_options.path_editing_mode.point_editing_mode, - selection_mode, - ), + SelectionShapeType::Lasso => { + shape_editor.select_all_in_shape( + &document.network_interface, + SelectionShape::Lasso(&tool_data.lasso_polygon), + selection_change, + tool_options.path_overlay_mode, + tool_data.frontier_handles_info.as_ref(), + tool_options.path_editing_mode.segment_editing_mode, + tool_options.path_editing_mode.point_editing_mode, + selection_mode, + ); + } } } @@ -2397,7 +2413,7 @@ impl Fsm for PathToolFsmState { PathToolFsmState::Ready } // Mouse up - (PathToolFsmState::Drawing { selection_shape }, PathToolMessage::DragStop { extend_selection, shrink_selection }) => { + (PathToolFsmState::Drawing { selection_shape, drag_start_document }, PathToolMessage::DragStop { extend_selection, shrink_selection }) => { let extend_selection = input.keyboard.get(extend_selection as usize); let shrink_selection = input.keyboard.get(shrink_selection as usize); @@ -2413,12 +2429,13 @@ impl Fsm for PathToolFsmState { let previous_mouse = document_to_viewport.transform_point2(tool_data.previous_mouse_position); let selection_mode = match tool_action_data.preferences.get_selection_mode() { - SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(document.metadata()), + SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(drag_start_document, document.metadata()), selection_mode => selection_mode, }; tool_data.started_drawing_from_inside = false; - if tool_data.drag_start_pos.distance(previous_mouse) < 1e-8 { + let start_viewport = document_to_viewport.transform_point2(drag_start_document); + if start_viewport.distance(previous_mouse) < 1e-8 { // Clicked inside or outside the shape then deselect all of the points/segments if document.click(input, viewport).is_some() && tool_data.stored_selection.is_none() { tool_data.stored_selection = Some(shape_editor.selected_shape_state.clone()); @@ -2429,7 +2446,7 @@ impl Fsm for PathToolFsmState { } else { match selection_shape { SelectionShapeType::Box => { - let bbox = Rect::new(tool_data.drag_start_pos.x, tool_data.drag_start_pos.y, previous_mouse.x, previous_mouse.y).abs(); + let bbox = Rect::new(start_viewport.x, start_viewport.y, previous_mouse.x, previous_mouse.y).abs(); shape_editor.select_all_in_shape( &document.network_interface, @@ -2690,7 +2707,7 @@ impl Fsm for PathToolFsmState { let mut end_point_id = None; // Get the merged layer's transform to convert local positions to document space - let layer_transform = document.metadata().transform_to_document(layer); + let layer_transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface); for (i, &local_pos) in positions.iter().enumerate() { // Transform the local position to document space for comparison @@ -2749,7 +2766,7 @@ impl Fsm for PathToolFsmState { let Some(old_vector) = document.network_interface.compute_modified_vector(layer) else { continue }; // Also get the transform node that is applied on the layer if it exists - let transform = document.metadata().transform_to_document(layer); + let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface); let mut new_vector = Vector::default(); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 173e2d4b17..134cb80623 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -941,7 +941,10 @@ impl Fsm for SelectToolFsmState { // Check if the tool is in selection mode and has started drawing if let Self::Drawing { - selection_shape, drag_start_document, has_drawn: true, .. + selection_shape, + drag_start_document, + has_drawn: true, + .. } = self { // Get the updated selection box bounds From cc4cf5718c5b44998c7c3a5f0e99b5b3966264f5 Mon Sep 17 00:00:00 2001 From: krVatsal Date: Sun, 1 Mar 2026 22:49:38 +0530 Subject: [PATCH 6/8] fix fill color bug --- editor/src/messages/tool/tool_messages/path_tool.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 1e55ba1831..db56ab237f 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1892,13 +1892,8 @@ impl Fsm for PathToolFsmState { overlay_context.outline(outline, layer_to_viewport, Some(color)); } } - Self::Drawing { selection_shape, drag_start_document } => { - let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.05) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - let fill_color = Some(fill_color.as_str()); + Self::Drawing { selection_shape, drag_start_document } => { + let fill_color = Some(COLOR_OVERLAY_BLUE_05); let selection_mode = match tool_action_data.preferences.get_selection_mode() { SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(drag_start_document, document.metadata()), From bc3d9ec97c99c9d3470d38e282b205bbb0f625fb Mon Sep 17 00:00:00 2001 From: krVatsal Date: Mon, 2 Mar 2026 00:58:24 +0530 Subject: [PATCH 7/8] fix roundtrip drift path --- editor/src/messages/tool/tool_messages/select_tool.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index ba33b083c0..9dee0a74c2 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -444,8 +444,9 @@ impl SelectToolData { pub fn selection_box(&self, drag_start_document: DVec2, metadata: &DocumentMetadata) -> [DVec2; 2] { // Transform the document-anchored start point to viewport let start_viewport = metadata.document_to_viewport.transform_point2(drag_start_document); + let roundtrip_epsilon = 10. * f64::EPSILON; - if self.drag_current == start_viewport { + if self.drag_current.distance_squared(start_viewport) <= roundtrip_epsilon.powi(2) { let tolerance = DVec2::splat(SELECTION_TOLERANCE); [start_viewport - tolerance, start_viewport + tolerance] } else { From db44b584ae1429b70cb9563a85be07fa3427cec8 Mon Sep 17 00:00:00 2001 From: krVatsal Date: Sun, 8 Mar 2026 22:16:32 +0530 Subject: [PATCH 8/8] Remove accidentally committed node_modules files --- node_modules/.vite/deps/_metadata.json | 8 -------- node_modules/.vite/deps/package.json | 3 --- 2 files changed, 11 deletions(-) delete mode 100644 node_modules/.vite/deps/_metadata.json delete mode 100644 node_modules/.vite/deps/package.json diff --git a/node_modules/.vite/deps/_metadata.json b/node_modules/.vite/deps/_metadata.json deleted file mode 100644 index 88afc1fe38..0000000000 --- a/node_modules/.vite/deps/_metadata.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "hash": "00a36998", - "configHash": "5d045020", - "lockfileHash": "9395696f", - "browserHash": "621fa28a", - "optimized": {}, - "chunks": {} -} \ No newline at end of file diff --git a/node_modules/.vite/deps/package.json b/node_modules/.vite/deps/package.json deleted file mode 100644 index 3dbc1ca591..0000000000 --- a/node_modules/.vite/deps/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -}