Path type brackets: 8 px reach, hover highlight, and the nearest of a bracket and a port takes the press

Squashed from #5332 onto main.
This commit is contained in:
ham9zah
2026-11-03 16:03:40 +07:00
committed by bflatastic
parent 2b6cbfda5d
commit 2bc284aa25
5 changed files with 197 additions and 36 deletions
+14 -4
View File
@@ -57,7 +57,7 @@ use crate::bbox::move_delta;
use crate::corners::{self, CornerDrag, over_widget};
use crate::guides::{HandleSnap, Leave, PointSnap, Targets};
use crate::meshedit::{MeshEdit, grid_of};
use crate::pathtype::{self, BracketDrag, over_bracket};
use crate::pathtype::{self, BracketDrag, near_bracket};
use crate::rulerguide::GuideEdit;
use crate::select::{MoveSnap, hold_point, is_area_type, is_path_type, matrix_json, release_guide_at};
use crate::{Action, Cursor, Mods, Overlay, PointerEvent, PointerKind, Tool, ToolContext, ToolKey};
@@ -227,6 +227,8 @@ pub struct DirectSelectionTool {
/// The already selected frame anchor the press landed on without Shift: a click (no drag)
/// selects it alone, as a drag moves every selected frame anchor with it.
frame_click: Option<(NodeId, AnchorRef)>,
/// Where the pointer last hovered: the type on a path bracket under it draws thicker.
bracket_hover: Option<Point>,
}
impl DirectSelectionTool {
@@ -253,6 +255,7 @@ impl DirectSelectionTool {
frame: BTreeMap::new(),
frame_revision: (0, 0),
frame_click: None,
bracket_hover: None,
}
}
@@ -643,6 +646,11 @@ fn segment_at(cx: &ToolContext, id: NodeId, p: Point) -> Option<SegmentHit> {
}
impl Tool for DirectSelectionTool {
/// The pointer hovers nothing for the tool: no bracket is highlighted.
fn hover_ended(&mut self, _cx: &ToolContext) {
self.bracket_hover = None;
}
fn id(&self) -> &'static str {
if self.group { "groupSelection" } else { "directSelection" }
}
@@ -703,6 +711,7 @@ impl Tool for DirectSelectionTool {
}
}
fn pointer(&mut self, cx: &ToolContext, ev: &PointerEvent) -> Vec<Action> {
self.bracket_hover = (ev.kind == PointerKind::Move).then_some(ev.pos);
if let Some(out) = self.guide.pointer(cx, ev) {
return out;
}
@@ -727,7 +736,7 @@ impl Tool for DirectSelectionTool {
return vec![];
}
if !over_path_type_anchor(cx, p)
&& let Some(b) = BracketDrag::hit(cx, ev)
&& let Some(b) = BracketDrag::hit_any(cx, ev)
{
self.state = State::Bracket(b);
return vec![];
@@ -915,7 +924,8 @@ impl Tool for DirectSelectionTool {
out.extend(self.mesh.overlays(cx, &|id| cx.selection.contains(id)));
if !self.group {
out.extend(frame_overlays(cx, &self.frame_anchors(cx)));
out.extend(pathtype::overlays(cx));
let drag = if let State::Bracket(b) = &self.state { Some(b) } else { None };
out.extend(pathtype::overlays(cx, self.bracket_hover, drag));
}
if matches!(self.state, State::Handle { .. } | State::MoveAnchors { .. } | State::MoveObject { .. }) {
out.extend(self.guides.iter().cloned());
@@ -932,7 +942,7 @@ impl Tool for DirectSelectionTool {
if !self.group && (matches!(self.state, State::Corner(_)) || over_widget(cx, p, true)) {
return Cursor::CornerRadius;
}
if !self.group && (matches!(self.state, State::Bracket(_)) || over_bracket(cx, p) && !over_path_type_anchor(cx, p)) {
if !self.group && (matches!(self.state, State::Bracket(_)) || near_bracket(cx, p) && !over_path_type_anchor(cx, p)) {
return Cursor::PathBracket;
}
let rest = if self.group { Cursor::GroupSelection } else { Cursor::ArrowHollow };