Add selection cycle and gray pre-selection outlines to the Path tool, and Tab to swap Select/Path tools (#2818)

* Added initial version of this feature for the path tool

* Removed debug statements

* Thickened the overlay width

* Added hover highlighting for path tool

* Experimental switch to path tool at leaf layer

* Ghost outline initial implementation

* Added tab swap for select tool -> path tool

* Minor fix for Select Tool dbl click -> Path Tool

* Added support for ghosts when using GRS in the path tool

* Fixed GRS undo bug, vastly improved hover behavior and now clearly visualize next double click target

* Fixed unused import warnings

* Updated behavior to handle mouse movement cases, reverted line width to 1px

* Fixed merge behavioral issues

* Disabled Select Tool to Path Tool double click toggle, fixed single click drill through for special case

* Clean up of unused consts and comment

* Properly cancel the drill through state when the mouse moves

* Fix some stuff

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Oliver Davies
2025-07-13 08:26:36 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent d6d1bbb1e6
commit e89cded4b8
9 changed files with 198 additions and 28 deletions
@@ -1683,6 +1683,11 @@ impl DocumentMessageHandler {
})
}
pub fn click_list_no_parents<'a>(&'a self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
self.click_xray(ipp)
.filter(move |&layer| !self.network_interface.is_artboard(&layer.to_node(), &[]) && !layer.has_children(self.network_interface.document_metadata()))
}
/// Find the deepest layer that has been clicked on from a location in viewport space.
pub fn click(&self, ipp: &InputPreprocessorMessageHandler) -> Option<LayerNodeIdentifier> {
self.click_list(ipp).last()
@@ -1,7 +1,8 @@
use super::utility_functions::overlay_canvas_context;
use crate::consts::{
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER,
COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE,
COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH,
PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
};
use crate::messages::prelude::Message;
use bezier_rs::{Bezier, Subpath};
@@ -349,6 +350,7 @@ impl OverlayContext {
self.render_context.rect(corner.x, corner.y, size, size);
self.render_context.set_fill_style_str(color_fill);
self.render_context.set_stroke_style_str(color_stroke);
self.render_context.set_line_width(1.);
self.render_context.fill();
self.render_context.stroke();
@@ -631,11 +633,9 @@ impl OverlayContext {
pub fn outline_overlay_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
self.start_dpi_aware_transform();
let color = Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
self.render_context.begin_path();
self.bezier_command(bezier, transform, true);
self.render_context.set_stroke_style_str(&color);
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE_50);
self.render_context.set_line_width(4.);
self.render_context.stroke();
@@ -727,6 +727,7 @@ impl OverlayContext {
let color = color.unwrap_or(COLOR_OVERLAY_BLUE);
self.render_context.set_stroke_style_str(color);
self.render_context.set_line_width(1.);
self.render_context.stroke();
}
}