Feat(canvas): Add temporary bbox move with C key hold - #9104
Conversation
Good catch. I was only clearing the temporary bbox hotkey state on I fixed this by explicitly stopping any active bbox interaction before clearing temporary tool overrides. That should prevent the bbox from getting stuck half-active after alt-tab. |
# Conflicts: # invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasToolModule.ts # invokeai/frontend/web/src/features/ui/layouts/DockviewCanvasHeaderActions.tsx
joshistoast
left a comment
There was a problem hiding this comment.
-
useEditImagestill sets$tooldirectly — the selection gets silently discardedfeatures/gallery/hooks/useEditImage.ts:48canvasManager.tool.$tool.set('brush');
This is the only remaining external
$tool.setin the codebase (verified by grep), and it's exactly the call the rest of the PR converted. Now that$toolis derived, writing to it directly leaves$baseToolstale — the nextapplyToolHotkeyStatehe brush away.Repro: Gallery → Edit image → canvas opens with brush active → tap and release Alt (or Space) → tool snaps back to move (the default
$baseTool).Fix:
canvasManager.tool.setBaseTool('brush').Worth also considering making
$toolexternally read-only (e.g. expose a readonly typed atom, or rename the writable one) so this class of bug can't reappear. -
pressSpaceKey()duplicates the pan branch thatonKeyDownstill runskonva/CanvasTool/CanvasToolModule.ts:262-270vs:893-908pressSpaceKeyends with a lasso-pan/$cursorPos.set(null)branch, andonKeyDownimmediately re-runs the same lasso check plus the two rect branches it doesn't know about.stage.startDragging()is called twice for the lasso case. Harmless (it's idempotent —CanvasStageModule.ts:525guards ongetIsDragging()), but confusing.- Real behavior change:
pressSpaceKey'selsefires$cursorPos.set(null)forcurrentTool === 'rect', which the old code did not. The rect suspendable/polygon paths then callstartDragging()→tool.render()with a null cursor position.
Fix: make
pressSpaceKeya pure state transition (this.applyToolHotkeyState(pressSpaceInState(this.getToolHotkeyState()))) and leave all pan/cursor decisions inonKeyDown, which is the only place that sees the full branch set. -
bboxToolHotkey.tsis inhooks/but contains no hooksfeatures/controlLayers/hooks/bboxToolHotkey.tsIt's a pure reducer over
CanvasToolHotkeyState, imported bykonva/CanvasTool/CanvasToolModule.ts. A konva module reaching intohooks/is a layering smell, and there is already a home for exactly this:konva/CanvasTool/toolHotkeys.ts, with its colocatedtoolHotkeys.test.tsusing the same relative-import style.Fix: move
bboxToolHotkey.ts+.test.tstokonva/CanvasTool/. Consider merging intotoolHotkeys.ts— it's the same concern, and the namebboxToolHotkeyundersells a module that now owns Space and Alt too (CanvasToolHotkeyStateis the honest name). -
Dead conditions in the lasso pointer-state check
konva/CanvasTool/CanvasToolModule.ts:180-186previousTool !== 'colorPicker' && tool !== 'colorPicker' && (previousTool === 'lasso' || previousTool === 'view') && (tool === 'lasso' || tool === 'view');
The last two clauses already exclude
colorPickerfrom both sides, so the first two can never be false. Drop them. -
Redundant aspect-ratio write in
stopInteractionkonva/CanvasTool/CanvasBboxToolModule.ts:293Konva's
Transformer.stopTransform()firestransformend, and the existingonTransformEnd(:428) already sets$aspectRatioBufferfrom the sameproxyRectdimensions. The explicit write is a no-op. ThestopDrag()half of the method is the part that matters — keep that. -
Minor
hooks/useCanvasSelectBboxToolHotkey.ts:346-ish— theevent.repeatguard in thekeyuphandler is dead;keyupevents never repeat.applyToolHotkeyState(CanvasToolModule.ts:569-590) — the field-by-fieldbboxToolHotkeyStateChangedcompare is unnecessary. Nanostores atoms already no-op on===, and the reducers return the identical object reference when nothing changed, so a plain!==on the object works. Same for the threeif (x.get() !== state.x)guards.public/locales/en.json:709—selectBboxTool.descstill reads "Select the bounding box tool." The behavior is now tap-to-select / hold-to-temporarily-use; the hotkeys modal should say so. TheToolBboxButtontooltip ((C)) could hint at it too.- Escape during a bbox hold does nothing (
getToolToCancelOnEscapehas no bbox case), so the hold only clears onkeyup. Probably intentional — flagging in case it isn't. - Test coverage gap:
setBaseToolInStatewhile a bbox hold is active (does tapping a toolbar button mid-hold do the right thing?). The rest of the reducer coverage is solid.
|
Thanks, addressed point-by-point:
Two notes I did not change in this pass:
|
Summary
This PR adds bbox hotkey behavior for canvas. A short tap on
Cselects the bbox tool, and holdingCtemporarily activates bbox until the key is released.This PR also moves temporary bbox hotkey handling into the shared canvas tool state so it works correctly with the existing temporary Space and Alt tool overrides. Persistent tool changes now update the base tool consistently, and focused tests were added for tap, hold, and nested temporary hotkey flows.
As a result it touches more files than it was originally expected.
Related Issues / Discussions
N/A
QA Instructions
Cquickly. Verify that bbox becomes the selected tool.C. Verify that bbox is active only whileCis held and that releasingCreturns to the previous tool.C, then hold Space, releaseC, then release Space. Verify that the active tool returns to the original base tool and does not stay on bbox.Merge Plan
No special merge plan.
Checklist
What's Newcopy (if doing a release after this PR)