feat: Focus indicator (#10613)

* feat: Focus indicator

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Snapshot update

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* feat: Move visualdebug to utils and introduce volume bindable volume visualization

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Move visualdebug to elements

Due to dep circles

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Possible test timeout

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Incorrect hit test point input

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* feat: Add fallback when dragged outside of allowed area

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Elbow arrows don't need focus point mgmt

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: End bound indirect fix

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Show indicator when arrow endpoint dragging

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Update bound arrow endpoint at mid-point drag

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* chore: Refactor

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Curve endpoint intersection

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Outline focus point is reset on existing arrow drag

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Tests

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* chore: Fix lint

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* feat: Dragging focus point off

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Don't show the focus indicator when arrow endpoint is dragged

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Drag area for focus handles

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Focus point size unified

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Size bump for focus knob

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* feat: Cache hits and scene lookups

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* chore: Remove debug

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Consider hit threshold and inside override too

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Other shape switching

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* perf: Update tolerance params

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Focus know line width

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: knob offset

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Full overlap

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* chore: Remove Map caching

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: incorrect threshold

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: threshold setting

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Hit caching

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: cache override

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Snapshots

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* feat: Redesigned focus point handling

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Inside-inside mode

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* chore: Remove comment

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* feat: Allow focus knob outside the shape

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Arrow endpoint offset

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Focus knob element distance

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Increase iteration on curve intersection calc

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Handle disabled binding

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Alt mode

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Nested shape focus rewrite

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Alt + Ctrl + arrow endpoitn

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Hit ordering for focus points

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Focus point visibility

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* dry out renderFocusPointIndicator

* do not higlight point when dragging & make focus point smaller

* optimize retrieval of selectedLinearElement

* move focus highlighting into renderFocusPointIndicator to DRY out and colocate

* remove `disabled` state from focus highlight

* make focus point stroke color less prominent

* fix: No focus point for multi-point arrows

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Arrow edit mode drag focus point release

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* DRY out arrow point-like drag

* move `focus.ts` to `arrows/focus.ts`

---------

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Márk Tolmács
2026-01-31 15:08:28 +01:00
committed by GitHub
co-authored by dwelle
parent 216afc3625
commit b552c60714
16 changed files with 1112 additions and 90 deletions
@@ -2398,7 +2398,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": "353.93938",
"height": "390.11932",
"id": "id4",
"index": "a2",
"isDeleted": false,
@@ -2412,8 +2412,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
0,
],
[
"478.03877",
"-353.93938",
"478.03878",
"-390.11932",
],
],
"roughness": 1,
@@ -2424,7 +2424,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"startBinding": {
"elementId": "id0",
"fixedPoint": [
"0.50010",
1,
"0.50010",
],
"mode": "orbit",
@@ -2435,9 +2435,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"type": "arrow",
"updated": 1,
"version": 12,
"width": "478.03877",
"width": "478.03878",
"x": 11,
"y": "-45.14705",
"y": "-8.96692",
}
`;
@@ -2566,7 +2566,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": "353.93938",
"height": "390.11932",
"index": "a2",
"isDeleted": false,
"link": null,
@@ -2578,8 +2578,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
0,
],
[
"478.03877",
"-353.93938",
"478.03878",
"-390.11932",
],
],
"roughness": 1,
@@ -2590,7 +2590,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"startBinding": {
"elementId": "id0",
"fixedPoint": [
"0.50010",
1,
"0.50010",
],
"mode": "orbit",
@@ -2600,9 +2600,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"strokeWidth": 2,
"type": "arrow",
"version": 12,
"width": "478.03877",
"width": "478.03878",
"x": 11,
"y": "-45.14705",
"y": "-8.96692",
},
"inserted": {
"isDeleted": true,
@@ -8743,9 +8743,11 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1`
"selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor {
"customLineAngle": null,
"draggedFocusPointBinding": null,
"elbowed": false,
"elementId": "id0",
"hoverPointIndex": -1,
"hoveredFocusPointBinding": null,
"initialState": {
"altFocusPoint": null,
"arrowStartIsInside": false,
@@ -8976,9 +8978,11 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`]
"selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor {
"customLineAngle": null,
"draggedFocusPointBinding": null,
"elbowed": false,
"elementId": "id0",
"hoverPointIndex": -1,
"hoveredFocusPointBinding": null,
"initialState": {
"altFocusPoint": null,
"arrowStartIsInside": false,
@@ -9402,9 +9406,11 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1`
"selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor {
"customLineAngle": null,
"draggedFocusPointBinding": null,
"elbowed": false,
"elementId": "id0",
"hoverPointIndex": -1,
"hoveredFocusPointBinding": null,
"initialState": {
"altFocusPoint": null,
"arrowStartIsInside": false,
@@ -9818,9 +9824,11 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`]
"selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor {
"customLineAngle": null,
"draggedFocusPointBinding": null,
"elbowed": false,
"elementId": "id0",
"hoverPointIndex": -1,
"hoveredFocusPointBinding": null,
"initialState": {
"altFocusPoint": null,
"arrowStartIsInside": false,
+1 -1
View File
@@ -5051,7 +5051,7 @@ describe("history", () => {
id: arrowId,
startBinding: expect.objectContaining({
elementId: rect1.id,
fixedPoint: expect.arrayContaining([0.5001, 0.5001]),
fixedPoint: expect.arrayContaining([1, 0.5001]),
}),
endBinding: expect.objectContaining({
elementId: rect2.id,