From 6c3c2e8529a9d3f63be8fe5afb43f3a68e553425 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 <142654792+0SlowPoke0@users.noreply.github.com> Date: Sat, 28 Dec 2024 19:43:29 +0800 Subject: [PATCH] Add selection removal to the Select tool's box select (Ctrl+Shift modifier) (#2162) * select parent-node,remove deselect all layers message * Comment nits * implement negative selection box * moved comment and formatting * Apply suggestions from code review --------- Co-authored-by: Keavon Chambers --- .../messages/input_mapper/input_mappings.rs | 2 +- .../tool/tool_messages/select_tool.rs | 48 +++++++++++++------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 6b819646..9cdb03d7 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -98,7 +98,7 @@ pub fn input_mappings() -> Mapping { // SelectToolMessage entry!(PointerMove; refresh_keys=[Control, Alt, Shift], action_dispatch=SelectToolMessage::PointerMove(SelectToolPointerKeys { axis_align: Shift, snap_angle: Control, center: Alt, duplicate: Alt })), entry!(KeyDown(MouseLeft); action_dispatch=SelectToolMessage::DragStart { extend_selection: Shift, select_deepest: Accel }), - entry!(KeyUp(MouseLeft); action_dispatch=SelectToolMessage::DragStop { remove_from_selection: Shift }), + entry!(KeyUp(MouseLeft); action_dispatch=SelectToolMessage::DragStop { remove_from_selection: Shift, negative_box_selection: Control }), entry!(KeyDown(Enter); action_dispatch=SelectToolMessage::Enter), entry!(DoubleClick(MouseButton::Left); action_dispatch=SelectToolMessage::EditLayer), entry!(KeyDown(MouseRight); action_dispatch=SelectToolMessage::Abort), diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 6a79b565..9783aebe 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -72,7 +72,7 @@ pub enum SelectToolMessage { // Tool-specific messages DragStart { extend_selection: Key, select_deepest: Key }, - DragStop { remove_from_selection: Key }, + DragStop { remove_from_selection: Key, negative_box_selection: Key }, EditLayer, Enter, PointerMove(SelectToolPointerKeys), @@ -902,7 +902,7 @@ impl Fsm for SelectToolFsmState { let selection = tool_data.nested_selection_behavior; SelectToolFsmState::Ready { selection } } - (SelectToolFsmState::Dragging, SelectToolMessage::DragStop { remove_from_selection }) => { + (SelectToolFsmState::Dragging, SelectToolMessage::DragStop { remove_from_selection, .. }) => { // Deselect layer if not snap dragging responses.add(DocumentMessage::EndTransaction); @@ -1001,20 +1001,34 @@ impl Fsm for SelectToolFsmState { let selection = tool_data.nested_selection_behavior; SelectToolFsmState::Ready { selection } } - (SelectToolFsmState::DrawingBox { .. }, SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter) => { + ( + SelectToolFsmState::DrawingBox { .. }, + SelectToolMessage::DragStop { + remove_from_selection, + negative_box_selection, + }, + ) => { let quad = tool_data.selection_quad(); let new_selected: HashSet<_> = document.intersect_quad_no_artboards(quad, input).collect(); let current_selected: HashSet<_> = document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()).collect(); if new_selected != current_selected { - let parent_selected: HashSet<_> = new_selected - .into_iter() - .map(|layer| { - // Find the parent node - layer.ancestors(document.metadata()).filter(not_artboard(document)).last().unwrap_or(layer) - }) - .collect(); - - tool_data.layers_dragging.extend(parent_selected.iter().copied()); + // Negative selection when both Shift and Ctrl are pressed + if input.keyboard.key(remove_from_selection) && input.keyboard.key(negative_box_selection) { + let updated_selection = current_selected + .into_iter() + .filter(|layer| !new_selected.iter().any(|selected| layer.starts_with(*selected, document.metadata()))) + .collect(); + tool_data.layers_dragging = updated_selection; + } else { + let parent_selected: HashSet<_> = new_selected + .into_iter() + .map(|layer| { + // Find the parent node + layer.ancestors(document.metadata()).filter(not_artboard(document)).last().unwrap_or(layer) + }) + .collect(); + tool_data.layers_dragging.extend(parent_selected.iter().copied()); + } responses.add(NodeGraphMessage::SelectedNodesSet { nodes: tool_data .layers_dragging @@ -1143,9 +1157,13 @@ impl Fsm for SelectToolFsmState { responses.add(FrontendMessage::UpdateInputHints { hint_data }); } SelectToolFsmState::DrawingBox { .. } => { - // TODO: Add hint and implement functionality for holding Shift to extend the selection, thus preventing the prior selection from being cleared - // TODO: Also fix the current functionality so canceling the box select doesn't clear the prior selection - let hint_data = HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]); + let hint_data = HintData(vec![ + HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), + HintGroup(vec![HintInfo::keys([Key::Control, Key::Shift], "Remove from Selection").add_mac_keys([Key::Command, Key::Shift])]), + // TODO: Re-select deselected layers during drag when Shift is pressed, and re-deselect if Shift is released before drag ends. + // TODO: (See https://discord.com/channels/731730685944922173/1216976541947531264/1321360311298818048) + // HintGroup(vec![HintInfo::keys([Key::Shift], "Extend Selection")]) + ]); responses.add(FrontendMessage::UpdateInputHints { hint_data }); } _ => {}