8.3 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.
Persisted-state structural correction after root gate
Root's Windows gate at integrated 24bb24839b14353c1a2b668f3bac34235d39f7a7
reported 22 pass / 1 fail with all 531 inputs unchanged. Remaining failure:
mounted_state_delete_uses_state_placement_and_preserves_architecture_placement
had no delete_target after its real Save/Undo/Shift+Delete sequence. Other new
revision, passive-save, Inspector and gesture tests passed in that scoped gate.
Receipt: root .local/allocation-union-20260912/focus-24bb248.result.json.
Source cause is separate from Context focus retention: state_view conditionally
pushed the unplaced-element picker immediately before the canvas Owner. The
installed iced_widget-0.14.2/src/column.rs:207 delegates to Tree::diff_children.
Installed iced_core-0.14.0/src/widget/tree.rs reconciles children positionally
with zip, appending or truncating, and replaces a Tree on widget-tag mismatch.
When Hide adds the picker, the former Owner slot becomes a PickList and a new
unfocused Owner is appended. Undo truncates that Owner and replaces PickList
with yet another new Owner. StateCanvas has no private focused flag; state
viewport resizing does not change canvas_epoch. Loosening Context guards would
therefore not fix the cause.
The corrected view has a permanent body Column with exactly two children: an optional-controls Column, and the canvas Owner. Picker insertion/removal is confined to the first child's subtree. The body uses zero spacing when that slot is empty and eight pixels when present, preserving the previous visible layout without a blank spacer. No new focus is assigned.
The original failing test now checks the unplaced set's empty/one/empty
transition, unchanged document/diagram/epoch ownership after Hide, and actual
mounted focus immediately after Hide, Save and Undo. Redraws after Hide/Undo
exercise the real viewport transition. The final Shift+Delete target assertion
and source preservation remain unchanged. These additional checks are unrun;
root owns the next gate. Source review was requested from root because the
independent critic tool currently rejects follow-up with a thread-limit error.
Root completed source review and authorized the scoped correction, committed
as e24054b on the original isolated branch. Runtime verification remains with
root; no compiler/native action was performed by this worker.