Skip to content

Commit c9984a8

Browse files
authored
Make Select tool hover overlay in 'shallow select' mode visualize the next deeper object (#2639)
* Fix overlay behaviour on hovering over a layer * Add the deepen overlay code * cleanup
1 parent 4487020 commit c9984a8

File tree

6 files changed

+90
-28
lines changed

6 files changed

+90
-28
lines changed

editor/src/messages/portfolio/document/overlays/utility_types.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ impl core::hash::Hash for OverlayContext {
140140
}
141141

142142
impl OverlayContext {
143-
pub fn quad(&mut self, quad: Quad, color_fill: Option<&str>) {
144-
self.dashed_polygon(&quad.0, color_fill, None, None, None);
143+
pub fn quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>) {
144+
self.dashed_polygon(&quad.0, stroke_color, color_fill, None, None, None);
145145
}
146146

147147
pub fn draw_triangle(&mut self, base: DVec2, direction: DVec2, size: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
@@ -168,15 +168,15 @@ impl OverlayContext {
168168
self.end_dpi_aware_transform();
169169
}
170170

171-
pub fn dashed_quad(&mut self, quad: Quad, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
172-
self.dashed_polygon(&quad.0, color_fill, dash_width, dash_gap_width, dash_offset);
171+
pub fn dashed_quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
172+
self.dashed_polygon(&quad.0, stroke_color, color_fill, dash_width, dash_gap_width, dash_offset);
173173
}
174174

175-
pub fn polygon(&mut self, polygon: &[DVec2], color_fill: Option<&str>) {
176-
self.dashed_polygon(polygon, color_fill, None, None, None);
175+
pub fn polygon(&mut self, polygon: &[DVec2], stroke_color: Option<&str>, color_fill: Option<&str>) {
176+
self.dashed_polygon(polygon, stroke_color, color_fill, None, None, None);
177177
}
178178

179-
pub fn dashed_polygon(&mut self, polygon: &[DVec2], color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
179+
pub fn dashed_polygon(&mut self, polygon: &[DVec2], stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
180180
if polygon.len() < 2 {
181181
return;
182182
}
@@ -214,7 +214,8 @@ impl OverlayContext {
214214
self.render_context.fill();
215215
}
216216

217-
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
217+
let stroke_color = stroke_color.unwrap_or(COLOR_OVERLAY_BLUE);
218+
self.render_context.set_stroke_style_str(stroke_color);
218219
self.render_context.stroke();
219220

220221
// Reset the dash pattern back to solid
@@ -647,10 +648,11 @@ impl OverlayContext {
647648
}
648649

649650
/// Used by the Select tool to outline a path selected or hovered.
650-
pub fn outline(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2) {
651+
pub fn outline(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: Option<&str>) {
651652
self.push_path(subpaths, transform);
652653

653-
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
654+
let color = color.unwrap_or(COLOR_OVERLAY_BLUE);
655+
self.render_context.set_stroke_style_str(color);
654656
self.render_context.stroke();
655657
}
656658

editor/src/messages/tool/common_functionality/snapping.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,10 +449,10 @@ impl SnapManager {
449449
if let Some(ind) = &self.indicator {
450450
for layer in &ind.outline_layers {
451451
let &Some(layer) = layer else { continue };
452-
overlay_context.outline(snap_data.document.metadata().layer_outline(layer), snap_data.document.metadata().transform_to_viewport(layer));
452+
overlay_context.outline(snap_data.document.metadata().layer_outline(layer), snap_data.document.metadata().transform_to_viewport(layer), None);
453453
}
454454
if let Some(quad) = ind.target_bounds {
455-
overlay_context.quad(to_viewport * quad, None);
455+
overlay_context.quad(to_viewport * quad, None, None);
456456
}
457457
let viewport = to_viewport.transform_point2(ind.snapped_point_document);
458458

editor/src/messages/tool/common_functionality/transformation_cage.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ impl BoundingBoxManager {
570570
let quad = self.transform * Quad::from_box(self.bounds);
571571

572572
// Draw the bounding box rectangle
573-
overlay_context.quad(quad, None);
573+
overlay_context.quad(quad, None, None);
574574
}
575575

576576
/// Update the position of the bounding box and transform handles
@@ -587,7 +587,7 @@ impl BoundingBoxManager {
587587

588588
let mut draw_handle = |point: DVec2, angle: f64| {
589589
let quad = DAffine2::from_angle_translation(angle, point) * Quad::from_box([DVec2::splat(-RESIZE_HANDLE_SIZE / 2.), DVec2::splat(RESIZE_HANDLE_SIZE / 2.)]);
590-
overlay_context.quad(quad, Some(COLOR_OVERLAY_WHITE));
590+
overlay_context.quad(quad, None, Some(COLOR_OVERLAY_WHITE));
591591
};
592592

593593
let horizontal_angle = (quad.top_left() - quad.bottom_left()).to_angle();

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,10 +1064,10 @@ impl Fsm for PathToolFsmState {
10641064
let polygon = &tool_data.lasso_polygon;
10651065

10661066
match (selection_shape, selection_mode) {
1067-
(SelectionShapeType::Box, SelectionMode::Enclosed) => overlay_context.dashed_quad(quad, fill_color, Some(4.), Some(4.), Some(0.5)),
1068-
(SelectionShapeType::Lasso, SelectionMode::Enclosed) => overlay_context.dashed_polygon(polygon, fill_color, Some(4.), Some(4.), Some(0.5)),
1069-
(SelectionShapeType::Box, _) => overlay_context.quad(quad, fill_color),
1070-
(SelectionShapeType::Lasso, _) => overlay_context.polygon(polygon, fill_color),
1067+
(SelectionShapeType::Box, SelectionMode::Enclosed) => overlay_context.dashed_quad(quad, None, fill_color, Some(4.), Some(4.), Some(0.5)),
1068+
(SelectionShapeType::Lasso, SelectionMode::Enclosed) => overlay_context.dashed_polygon(polygon, None, fill_color, Some(4.), Some(4.), Some(0.5)),
1069+
(SelectionShapeType::Box, _) => overlay_context.quad(quad, None, fill_color),
1070+
(SelectionShapeType::Lasso, _) => overlay_context.polygon(polygon, None, fill_color),
10711071
}
10721072
}
10731073
Self::Dragging(_) => {

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

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,11 @@ impl Fsm for SelectToolFsmState {
527527
.selected_visible_and_unlocked_layers(&document.network_interface)
528528
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]))
529529
{
530-
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
530+
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), None);
531531

532532
if is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text") {
533533
let transformed_quad = document.metadata().transform_to_viewport(layer) * text_bounding_box(layer, document, font_cache);
534-
overlay_context.dashed_quad(transformed_quad, None, Some(7.), Some(5.), None);
534+
overlay_context.dashed_quad(transformed_quad, None, None, Some(7.), Some(5.), None);
535535
}
536536
}
537537
}
@@ -573,7 +573,38 @@ impl Fsm for SelectToolFsmState {
573573
let not_selected_click = click.filter(|&hovered_layer| !document.network_interface.selected_nodes().selected_layers_contains(hovered_layer, document.metadata()));
574574
if let Some(layer) = not_selected_click {
575575
if overlay_context.visibility_settings.hover_outline() {
576-
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
576+
let mut hover_overlay_draw = |layer: LayerNodeIdentifier, color: Option<&str>| {
577+
if layer.has_children(document.metadata()) {
578+
if let Some(bounds) = document.metadata().bounding_box_viewport(layer) {
579+
overlay_context.quad(Quad::from_box(bounds), color, None);
580+
}
581+
} else {
582+
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), color);
583+
}
584+
};
585+
let layer = match tool_data.nested_selection_behavior {
586+
NestedSelectionBehavior::Deepest => document.find_deepest(&[layer]),
587+
NestedSelectionBehavior::Shallowest => layer_selected_shallowest(layer, document),
588+
}
589+
.unwrap_or(layer);
590+
hover_overlay_draw(layer, None);
591+
if matches!(tool_data.nested_selection_behavior, NestedSelectionBehavior::Shallowest) {
592+
let mut selected = document.network_interface.selected_nodes();
593+
selected.add_selected_nodes(vec![layer.to_node()]);
594+
if let Some(new_selected) = click.unwrap().ancestors(document.metadata()).filter(not_artboard(document)).find(|ancestor| {
595+
ancestor
596+
.parent(document.metadata())
597+
.is_some_and(|parent| selected.selected_layers_contains(parent, document.metadata()))
598+
}) {
599+
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
600+
.unwrap()
601+
.with_alpha(0.5)
602+
.to_rgba_hex_srgb();
603+
fill_color.insert(0, '#');
604+
let fill_color = Some(fill_color.as_str());
605+
hover_overlay_draw(new_selected, fill_color);
606+
}
607+
}
577608
}
578609

579610
// Measure with Alt held down
@@ -786,7 +817,7 @@ impl Fsm for SelectToolFsmState {
786817
if overlay_context.visibility_settings.selection_outline() {
787818
// Draws a temporary outline on the layers that will be selected by the current box/lasso area
788819
for layer in layers_to_outline {
789-
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
820+
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), None);
790821
}
791822
}
792823

@@ -801,10 +832,10 @@ impl Fsm for SelectToolFsmState {
801832
let polygon = &tool_data.lasso_polygon;
802833

803834
match (selection_shape, current_selection_mode) {
804-
(SelectionShapeType::Box, SelectionMode::Enclosed) => overlay_context.dashed_quad(quad, fill_color, Some(4.), Some(4.), Some(0.5)),
805-
(SelectionShapeType::Lasso, SelectionMode::Enclosed) => overlay_context.dashed_polygon(polygon, fill_color, Some(4.), Some(4.), Some(0.5)),
806-
(SelectionShapeType::Box, _) => overlay_context.quad(quad, fill_color),
807-
(SelectionShapeType::Lasso, _) => overlay_context.polygon(polygon, fill_color),
835+
(SelectionShapeType::Box, SelectionMode::Enclosed) => overlay_context.dashed_quad(quad, None, fill_color, Some(4.), Some(4.), Some(0.5)),
836+
(SelectionShapeType::Lasso, SelectionMode::Enclosed) => overlay_context.dashed_polygon(polygon, None, fill_color, Some(4.), Some(4.), Some(0.5)),
837+
(SelectionShapeType::Box, _) => overlay_context.quad(quad, None, fill_color),
838+
(SelectionShapeType::Lasso, _) => overlay_context.polygon(polygon, None, fill_color),
808839
}
809840
}
810841
self
@@ -1733,6 +1764,34 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec
17331764
});
17341765
}
17351766

1767+
fn layer_selected_shallowest(clicked_layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option<LayerNodeIdentifier> {
1768+
let metadata = document.metadata();
1769+
let selected_layers = document.network_interface.selected_nodes().selected_layers(document.metadata()).collect::<Vec<_>>();
1770+
let final_selection: Option<LayerNodeIdentifier> = (!selected_layers.is_empty() && selected_layers != vec![LayerNodeIdentifier::ROOT_PARENT]).then_some(()).and_then(|_| {
1771+
let mut relevant_layers = document.network_interface.selected_nodes().selected_layers(document.metadata()).collect::<Vec<_>>();
1772+
if !relevant_layers.contains(&clicked_layer) {
1773+
relevant_layers.push(clicked_layer);
1774+
}
1775+
clicked_layer
1776+
.ancestors(metadata)
1777+
.filter(not_artboard(document))
1778+
.find(|&ancestor| relevant_layers.iter().all(|layer| *layer == ancestor || ancestor.is_ancestor_of(metadata, layer)))
1779+
.and_then(|least_common_ancestor| {
1780+
let common_siblings: Vec<_> = least_common_ancestor.children(metadata).collect();
1781+
(clicked_layer == least_common_ancestor)
1782+
.then_some(least_common_ancestor)
1783+
.or_else(|| common_siblings.iter().find(|&&child| clicked_layer == child || child.is_ancestor_of(metadata, &clicked_layer)).copied())
1784+
})
1785+
});
1786+
1787+
if final_selection.is_some_and(|layer| selected_layers.iter().any(|selected| layer.is_child_of(metadata, selected))) {
1788+
return None;
1789+
}
1790+
1791+
let new_selected = final_selection.unwrap_or_else(|| clicked_layer.ancestors(document.metadata()).filter(not_artboard(document)).last().unwrap_or(clicked_layer));
1792+
Some(new_selected)
1793+
}
1794+
17361795
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData, document: &DocumentMessageHandler, remove: bool) {
17371796
let layer = document.find_deepest(&selected).unwrap_or(
17381797
LayerNodeIdentifier::ROOT_PARENT

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ impl Fsm for TextToolFsmState {
473473
if far.x != 0. && far.y != 0. {
474474
let quad = Quad::from_box([DVec2::ZERO, far]);
475475
let transformed_quad = document.metadata().transform_to_viewport(tool_data.layer) * quad;
476-
overlay_context.quad(transformed_quad, Some(&("#".to_string() + &fill_color)));
476+
overlay_context.quad(transformed_quad, None, Some(&("#".to_string() + &fill_color)));
477477
}
478478
}
479479

@@ -488,11 +488,12 @@ impl Fsm for TextToolFsmState {
488488
for layer in document.intersect_quad_no_artboards(quad, input) {
489489
overlay_context.quad(
490490
Quad::from_box(document.metadata().bounding_box_viewport(layer).unwrap_or([DVec2::ZERO; 2])),
491+
None,
491492
Some(&("#".to_string() + &fill_color)),
492493
);
493494
}
494495

495-
overlay_context.quad(quad, Some(&("#".to_string() + &fill_color)));
496+
overlay_context.quad(quad, None, Some(&("#".to_string() + &fill_color)));
496497
}
497498

498499
// TODO: implement bounding box for multiple layers

0 commit comments

Comments
 (0)