6.0 KiB
Canvas focus continuity after accepted edits and history
Native parent observation at immutable f7d84fa: allocation work canvas click,
Delete placement, Save, Undo, Save, Redo, Save, Undo, then Shift+Delete produces
no dialog. Clicking the same work restores the expected model confirmation.
Parent evidence: .local/native-qa/delete-20260912/evidence/03-shift-delete-after-undo-no-dialog.png
and 04modelconfirmafterclick.
Isolated worktree: C:/Users/User/Documents/Git/ARCADIA-Studio/.local/delete-undo-focus-20260912.
Branch: codex/delete-undo-focus-956atf-20260912, baseline f7d84fa.
Reviewed change commit: 7bf57db (six production/test files; no runtime gate run
by this worker).
Source diagnosis and change
diagram::focus::Owner::diff previously revoked keyboard focus whenever any
route_edit::Context field changed. The context includes document revision,
so accepting a placement removal or Undo/Redo revoked the focus that still
belonged to the displayed canvas. This is a production defect, unlike the
earlier retained-Apply evaluator ordering issue.
Owner now preserves its existing focused and traversing values across a revision change only. It clears pointer history for every changed full context. It still revokes focus on document, diagram or canvas epoch change, disabled input, window loss/resize, a pointer press outside the canvas, and actual focus operations. It never grants focus on a revision change. The inner canvas still receives the new full context, and model/confirmation intent validation is unchanged.
A second necessary cause is passive Save: save_to sets busy, while the old
single enabled flag treated busy like a modal owner transfer. The shared keyboard
predicate now distinguishes logical owner availability from busy command
admission, with the previous command predicate behavior unchanged. Architecture
and state callers pass both owner availability and busy suspension to Owner.
Save/export suspension retains existing focus but denies keyboard/pointer
commands and focus acquisition. Explicit unfocus operations, outside clicks and
window loss still revoke the owner. No App save logic was changed.
Entering suspension recreates only the wrapped canvas child tree once, retiring
transient drags that could otherwise miss a release during I/O. Outer keyboard
focus and App-owned diagram presentation remain intact.
refresh_draft restores the selected architecture/state placement from the
preserved semantic selection after Undo; no selection-history rewrite is needed.
Actual mounted regression changes
app/diagram_removal_tests.rs adds:
- OA and SA repeated placement Delete after Undo/Redo, then Shift+Delete without another pointer event or explicit focus operation; exact bundle comparisons.
- Native allocation work click/hide/Undo/Redo/Undo/Shift+Delete sequence with the expected work confirmation target and unchanged model/source bundle. Real saves are included between history steps; each task is held while the actual busy tree receives F2/Shift+Delete, then completed to a temporary source file.
- Persisted State placement hide/Undo/Shift+Delete, preserving the architecture placement and correctly targeting the state.
- A canvas-versus-Inspector test that starts with actual pointer focus, accepts an unrelated rename, traverses Undo/Redo/Undo, checks the real widget operation focus witness, and verifies F2 reaches only the canvas. Repeated F2/Delete cannot reach the diagram from either Inspector or the newly opened inline name.
- Actual Inspector click or programmatic field focus during a held Save must revoke the retained canvas owner, including after the Save task completes.
- Passive Save cannot grant focus through either a focus operation or pointer press, and cannot restore focus lost to a window Unfocused event while busy.
- An actual held diagram drag is released while Save is busy; later motion and release must not commit that old gesture. A fresh drag must work and Undo must restore the exact original source bundle.
Existing stale revision/epoch/document/selection intent, confirmation replay, modal/window/dirty/modified-key, clean Inspector and Tab tests remain unchanged.
Derived viewport assessment
The process/scenario Viewport has analogous revision revocation, but its Scope
epoch is camera_epoch, mixing revisions and keyboard ownership boundaries.
Passing canvas_epoch as a replacement focus lifetime is unsafe: process/scenario
ordinary pan, zoom, viewport sizing and scroll changes also increment that epoch.
A separate derived focus-lifetime contract needs a bounded follow-up; this patch
does not broaden to those views or weaken their current guards.
Verification boundary
git diff --check passes. This worker has run no Cargo/compiler or native GUI
commands and made no Gitea changes. Parent owns executable gates and native
recheck. Fresh Astra/high source review found no issue in the revision-only
change, then identified a high-priority focus-theft bug in the first busy draft:
omitting the suspended canvas from focus operations and outside clicks prevented
Inspector acquisition from revoking retained canvas focus. The current change
keeps unfocus and outside-click revocation active, denies only acquisition, and
adds both actual click and operation regressions.
The same critic then identified the swallowed-release gesture risk described
above; the child-tree retirement and actual held/fresh drag regression address
it. The final Astra/high source re-review reports no further findings and judges
the actual gesture regression meaningful. No executable pass is claimed.
Recommended existing root Windows gate filters:
app::diagram_removal_tests::(all affected actual-root tests)app::architecture_authoring::tests::(inline F2 and ownership dependents)
The new test is
app::diagram_removal_tests::mounted_revision_and_history_preserve_the_actual_canvas_or_inspector_focus_owner.
The original allocation filter remains
app::diagram_removal_tests::mounted_allocation_work_click_then_delete_without_redraw_removes_placement_once.