Skip to content

Commit bd97c15

Browse files
0SlowPoke0Keavon
andauthored
Make the Select tool box-select the deepest individual layers or their common shared parent (#2424)
* fixed deep-select and overlays * minor fix * made minor fixes in filtering * small fix * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent ca5810c commit bd97c15

File tree

1 file changed

+65
-24
lines changed

1 file changed

+65
-24
lines changed

editor/src/messages/tool/tool_messages/select_tool.rs

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::consts::{
88
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
99
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
1010
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
11-
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
11+
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
1212
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
1313
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeNetworkInterface, NodeTemplate};
1414
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
@@ -727,29 +727,31 @@ impl Fsm for SelectToolFsmState {
727727
// Get the updated selection box bounds
728728
let quad = Quad::from_box([tool_data.drag_start, tool_data.drag_current]);
729729

730-
let selection_mode = match tool_action_data.preferences.get_selection_mode() {
730+
let current_selection_mode = match tool_action_data.preferences.get_selection_mode() {
731731
SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(),
732-
selection_mode => selection_mode,
732+
SelectionMode::Touched => SelectionMode::Touched,
733+
SelectionMode::Enclosed => SelectionMode::Enclosed,
733734
};
734735

735736
// Draw outline visualizations on the layers to be selected
736-
let mut draw_layer_outline = |layer| overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
737-
let intersection: Vec<LayerNodeIdentifier> = match selection_shape {
737+
let intersected_layers = match selection_shape {
738738
SelectionShapeType::Box => document.intersect_quad_no_artboards(quad, input).collect(),
739739
SelectionShapeType::Lasso => tool_data.intersect_lasso_no_artboards(document, input),
740740
};
741-
if selection_mode == SelectionMode::Enclosed {
742-
let is_inside = |layer: &LayerNodeIdentifier| match selection_shape {
741+
let layers_to_outline = intersected_layers.into_iter().filter(|layer| match current_selection_mode {
742+
SelectionMode::Enclosed => match selection_shape {
743743
SelectionShapeType::Box => document.is_layer_fully_inside(layer, quad),
744744
SelectionShapeType::Lasso => tool_data.is_layer_inside_lasso_polygon(layer, document, input),
745-
};
746-
for layer in intersection.into_iter().filter(is_inside) {
747-
draw_layer_outline(layer);
748-
}
749-
} else {
750-
for layer in intersection {
751-
draw_layer_outline(layer);
752-
}
745+
},
746+
SelectionMode::Touched => match tool_data.nested_selection_behavior {
747+
NestedSelectionBehavior::Deepest => !layer.has_children(document.metadata()),
748+
NestedSelectionBehavior::Shallowest => true,
749+
},
750+
SelectionMode::Directional => unreachable!(),
751+
});
752+
753+
for layer in layers_to_outline {
754+
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
753755
}
754756

755757
// Update the selection box
@@ -762,7 +764,7 @@ impl Fsm for SelectToolFsmState {
762764

763765
let polygon = &tool_data.lasso_polygon;
764766

765-
match (selection_shape, selection_mode) {
767+
match (selection_shape, current_selection_mode) {
766768
(SelectionShapeType::Box, SelectionMode::Enclosed) => overlay_context.dashed_quad(quad, fill_color, Some(4.), Some(4.), Some(0.5)),
767769
(SelectionShapeType::Lasso, SelectionMode::Enclosed) => overlay_context.dashed_polygon(polygon, fill_color, Some(4.), Some(4.), Some(0.5)),
768770
(SelectionShapeType::Box, _) => overlay_context.quad(quad, fill_color),
@@ -1399,6 +1401,7 @@ impl Fsm for SelectToolFsmState {
13991401
let current_selected: HashSet<_> = document.network_interface.selected_nodes().selected_layers(document.metadata()).collect();
14001402
let negative_selection = input.keyboard.key(remove_from_selection);
14011403
let selection_modified = new_selected != current_selected;
1404+
14021405
// Negative selection when both Shift and Ctrl are pressed
14031406
if negative_selection {
14041407
let updated_selection = current_selected
@@ -1407,14 +1410,20 @@ impl Fsm for SelectToolFsmState {
14071410
.collect();
14081411
tool_data.layers_dragging = updated_selection;
14091412
} else if selection_modified {
1410-
let parent_selected: HashSet<_> = new_selected
1411-
.into_iter()
1412-
.map(|layer| {
1413-
// Find the parent node
1414-
layer.ancestors(document.metadata()).filter(not_artboard(document)).last().unwrap_or(layer)
1415-
})
1416-
.collect();
1417-
tool_data.layers_dragging.extend(parent_selected.iter().copied());
1413+
match tool_data.nested_selection_behavior {
1414+
NestedSelectionBehavior::Deepest => {
1415+
let filtered_selections = filter_nested_selection(document.metadata(), &new_selected);
1416+
tool_data.layers_dragging.extend(filtered_selections);
1417+
}
1418+
NestedSelectionBehavior::Shallowest => {
1419+
// Find each new_selected's parent node
1420+
let parent_selected: HashSet<_> = new_selected
1421+
.into_iter()
1422+
.map(|layer| layer.ancestors(document.metadata()).filter(not_artboard(document)).last().unwrap_or(layer))
1423+
.collect();
1424+
tool_data.layers_dragging.extend(parent_selected.iter().copied());
1425+
}
1426+
}
14181427
}
14191428

14201429
if negative_selection || selection_modified {
@@ -1702,3 +1711,35 @@ pub fn extend_lasso(lasso_polygon: &mut Vec<DVec2>, point: DVec2) {
17021711
lasso_polygon.push(point);
17031712
}
17041713
}
1714+
1715+
pub fn filter_nested_selection(metadata: &DocumentMetadata, new_selected: &HashSet<LayerNodeIdentifier>) -> HashSet<LayerNodeIdentifier> {
1716+
// First collect childless layers
1717+
let mut filtered_selection: HashSet<_> = new_selected.iter().copied().filter(|layer| !layer.has_children(metadata)).collect();
1718+
1719+
// Then process parents with all children selected
1720+
for &layer in new_selected {
1721+
// Skip if the layer is not a parent
1722+
if !layer.has_children(metadata) {
1723+
continue;
1724+
}
1725+
1726+
// If any ancestor is already present in the filtered selection, don't include its child
1727+
if layer.ancestors(metadata).any(|ancestor| filtered_selection.contains(&ancestor)) {
1728+
continue;
1729+
}
1730+
1731+
// Skip if any of the children are not selected
1732+
if !layer.descendants(metadata).all(|descendant| new_selected.contains(&descendant)) {
1733+
continue;
1734+
}
1735+
1736+
// Remove all descendants of the parent
1737+
for child in layer.descendants(metadata) {
1738+
filtered_selection.remove(&child);
1739+
}
1740+
// Add the parent
1741+
filtered_selection.insert(layer);
1742+
}
1743+
1744+
filtered_selection
1745+
}

0 commit comments

Comments
 (0)