The reported defect: sketch dimension labels are "not editable" — you draw a
rectangle, its Width field opens, you type, and the as-drawn number is committed
instead. It affects every sketch tool, not just the rounded rectangle.
WHAT THIS ADDS
1. The arbiter (DesignPanel CHAR_HOOK -> DesignCanvas::inline_type_char ->
SketchInlineEditor::type_char). Routes a key by what it IS, not by who the
window manager focused: digits, sign, decimal separator and Backspace/Delete
go to the open value field, Enter/Tab commit, letters stay tool shortcuts.
This is FreeCAD Sketcher's rule (DrawSketchKeyboardManager::
detectKeyboardEventHandlingMode), and the reason its sketcher behaves the same
on every desktop: it never asks who has focus.
2. The [UX] trace (SNAPORCA_UXTRACE) in SketchInlineEditor: open/commit/refused/
cancel, with the prefill and what the control actually held at Enter. It did
not exist — the ladder below was written against a surface no build emitted,
so it could only ever report "nothing opened". typed == prefill on a commit is
the defect's signature and nothing else makes it visible.
3. A draw-then-edit trace in DesignSketchTool: four early returns can swallow the
value-field chain and from outside they are indistinguishable.
4. scripts/CAD/check-gui-click-edit.py — types WITHOUT clicking the field, as a
person does, across Line/Rectangle/Circle/Slot/Polygon/Ellipse/Arc plus label
click-to-edit, and asserts committed == typed != prefill.
5. scripts/CAD/focus-loop.sh — sync/build/assert on behemoth. NOT the orcacad-gui
rig: its image pins deps 216 non-CAD files behind cad-mainline, so today's CAD
sources cannot build there without a deps rebuild.
WHAT IS PROVEN, AND WHAT IS NOT
Green under openbox: 28 checks, every tool, committed == typed != prefill.
But openbox CANNOT adjudicate this bug and the ladder says so in place. There the
field always wins the keyboard, so the same ladder also passes against a binary
with the arbiter compiled out — measured twice. Two ways of removing the keyboard
were tried and both are recorded as dead ends: XSetInputFocus loses to the field's
own re-focus CallAfter, and XSendEvent (xdotool --window) is dropped by GTK, which
made every run red regardless of the code.
Under metacity — same focus-stealing-prevention lineage as the user's mutter — the
mechanism appears in the WM's own log:
Buggy client sent a _NET_ACTIVE_WINDOW message with a timestamp of 0
That is the activation being refused, which is exactly the reported symptom.
present_toplevel() already asks for a server timestamp, so a path is still falling
through to frame->Raise(), which sends time 0. That is the next thing to fix, and
it is tracked; the arbiter alone does not close it. metacity also aborts on this
window (frames.c:1239), so the gate needs a WM that survives before it can return
a verdict.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
5.2 KiB
DELEGATION SPECIFICATION: HARNESS-DRIVEN VALIDATION LOOP
slug: sketch-focus-arbiter · repo: /home/tommaso/projects/apps/orca_cad · branch: cad-mainline
1. TARGET GOAL
Functional Objective. Keyboard input in the Design tab is routed by WHAT THE KEY IS, not by
which widget the window manager decided to focus. Adopted from FreeCAD's
DrawSketchKeyboardManager::detectKeyboardEventHandlingMode
(src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.cpp), which never queries focus at all:
- digit,
-,.,,-> the open value field - Backspace / Delete -> the open value field (when one is open)
- Enter / Return / Tab -> commit the field, control returns to the view
- a letter -> the sketch-tool shortcut map, as today
- Esc -> the existing CadLevel LIFO (DesignInteraction.hpp), unchanged
- anything else -> sticky: whoever had it keeps it
Observable postcondition: for EVERY sketch tool that opens a value field, a value typed immediately after the field appears — with NO click into the field — is the value committed. Today the prefill is committed instead whenever the WM withholds focus.
Target Files / Scope (writable). src/slic3r/GUI/CAD/DesignPanel.cpp (the arbiter lives in the existing wxEVT_CHAR_HOOK) src/slic3r/GUI/CAD/DesignCanvas.cpp/.hpp (forwarding entry points only) src/slic3r/GUI/CAD/SketchInlineEditor.cpp/.hpp (accept a programmatically delivered character) scripts/CAD/check-gui-click-edit.py (F2P oracle — authoring exception, see §4) Everything else read-only. No dependency additions, no reformatting.
Open Bindings.
- The in-canvas ImGui field on wip/in-canvas-value-field is NOT in scope. Default: the arbiter is implemented against the CURRENT wxFrame field on cad-mainline, because content-based routing makes the window's focus irrelevant either way. If it later moves in-canvas the arbiter is unchanged.
- Tools whose field is opened by a toolbar button rather than a gesture (Constrain path) are covered by the same arbiter but are not in the F2P tool list. Default: assert them in P2P only.
2. HARNESS ENVIRONMENT & GROUND TRUTH
The rig container orcacad-gui on nativedev IS the harness. Xvfb :11 + openbox, the app under
test, xdotool for synthetic input, and an MCP socket at /tmp/mcp.sock that reports sketch
state as JSON. It is a closed loop: drive input, read geometry back, assert. No window manager
politics, no human.
Harness interface (ordered; each slot one invocation, one exit code): S1 sync docker cp orcacad-gui:/OrcaSlicer/ S2 build docker exec orcacad-gui ninja -C /OrcaSlicer/build orca-slicer S3 restart docker exec orcacad-gui /OrcaSlicer/scripts/CAD/start-headless-gui.sh S4 F2P docker exec -e DISPLAY=:11 orcacad-gui python3 /tmp/check-gui-click-edit.py --attach S5 P2P docker exec -e DISPLAY=:11 orcacad-gui python3 /tmp/check-gui-sketching.py
F2P. scripts/CAD/check-gui-click-edit.py. For each of Line, Rectangle, Circle, Slot,
Polygon, Ellipse and Rounded rectangle: arm the tool, draw it, and type a value that differs
from the prefill WITHOUT clicking the field. Assert the committed value equals the typed value.
The ladder must FAIL against unmodified cad-mainline — that is what proves it asserts something.
P2P. scripts/CAD/check-gui-sketching.py, the existing gesture ladder, minus anything red at
baseline. NOTE: it calls focus_field() — one click into the field before typing — which is the
workaround this whole task removes. It stays green as a regression guard; it is NOT evidence.
Test Integrity Constraint. focus_field() in check-gui-sketching.py must NOT be deleted to
make things pass, and check-gui-click-edit.py must NOT be weakened. Either invalidates the run.
3. VERIFICATION COMMANDS
- Static:
docker exec orcacad-gui ninja -C /OrcaSlicer/build orca-slicer(warnings delta only; this repo configures no linter — the compiler is the static gate. Absolute-zero is NOT the gate.) - Harness:
docker exec -e DISPLAY=:11 orcacad-gui python3 /tmp/check-gui-click-edit.py --attach - Regression:
docker exec -e DISPLAY=:11 orcacad-gui python3 /tmp/check-gui-sketching.py
4. CONVERGENCE LOOP — ceiling 8 iterations
EDIT (scoped) -> EXECUTE S1..S5 -> PARSE the ladder's per-tool assertions and the [UX]/[KEYTRACE] lines -> PATCH from the parsed cause. On ceiling without convergence: stop, report the last diff and the unresolved failure set. Do not report success.
F2P authoring exception: check-gui-click-edit.py is writable, and must be shown RED against unmodified source before any source edit counts.
5. TERMINATION CRITERIA
- S2 exits 0, and introduces no compiler warning absent from the baseline.
- S4 ALL_PASSED — every tool commits the typed value, no click into the field.
- S5 shows zero regressions against its recorded baseline pass count.
- F2P proven red without the fix (source stashed, ladder re-run, must FAIL).
6. GUARDRAILS
Zero-assumption: no completion claim without captured stdout and exit codes. Oracle supremacy: the ladder's verdict overrides my judgement. Blast radius: §1 files only. Baseline obligation: run §3 once before the first edit and record it.