Esc is the safe key again: one press, one level, nothing destroyed

Two presses used to discard a live sketch. The key was answered in four places that could not
see each other — the inline value field, a sketch branch, a feature-card branch, and the canvas
— so a press aimed at one fell through to the next, and request_exit() carried a fourth layer
that deliberately let the SECOND consecutive press through to cancel_sketch(). The warning it
showed first did not help: the two presses are never one decision, the first is aimed at a field
or a tool and the second at whatever was underneath it.

The stack is now explicit. CadLevel (DesignInteraction.hpp) is four levels deep, the enum value
IS the LIFO depth, and cad_escape_level() is a constexpr function over a POD of four booleans —
so the ordering that is the entire contract is checked by static_assert at compile time, with no
window, GL context or event loop. DesignPanel::escape() acts on the one level escape_level()
names and on no other, and every Esc in the tab routes through it.

The destructive layer is gone from request_exit() itself rather than guarded at its callers, so
the guarantee cannot be re-opened by adding a route: a session holding geometry is left only
through Finish (keep) or Cancel (discard). Cancel now asks before discarding — it used to refuse
and tell the user to press the button they had just pressed, which meant a drawn sketch could be
kept but never thrown away.

Right-click also stops rewarding navigation with a menu: the offer needs BOTH budgets, released
within 200 ms and moved no more than 3 px, and the raycast uses the press position, so the menu
describes what was pointed at rather than where the camera stopped. Two budgets because drift
alone still popped a menu at the end of a slow, careful orbit.

docs/ux/interaction-model.md carries the state machine, the routing and the transition table.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
This commit is contained in:
Tommaso Bianchi
2026-09-05 11:35:12 +02:00
co-authored by Claude Opus 5
parent bb6a1810f6
commit 324b558747
9 changed files with 425 additions and 90 deletions
+9 -1
View File
@@ -11,6 +11,7 @@
#include <map>
#include "libslic3r/CAD/CadDocument.hpp"
#include "slic3r/GUI/CAD/DesignInteraction.hpp" // CadLevel: what one Esc press means
class ComboBox; // Orca dropdown (Widgets/ComboBox.hpp) — replaces wxChoice everywhere here
class StaticBox; // Orca rounded card frame (Widgets/StaticBox.hpp)
@@ -106,7 +107,14 @@ private:
void apply_dof_status(int dof, bool ok, bool has_constraints);
// Unified action-bar dispatch: one Confirm / one Cancel for every tool and mode.
void tool_confirm(); // ✓ : commit the active feature / sketch / constrain session
void tool_cancel(); // ✗ / Esc : cancel the active feature / discard / exit
void tool_cancel(); // ✗ : cancel the active feature / discard / exit
// Esc. ONE press unwinds ONE level of the interaction stack (DesignInteraction.hpp), and no
// level of it destroys committed work. escape_level() answers which level the press belongs
// to; escape() acts on exactly that one. Every Esc in the tab routes through here — the key
// used to be handled in four places that could not see each other, and that is how two
// presses in a row reached past a tool and discarded the sketch under it.
CadLevel escape_level() const;
void escape();
void update_action_bar(); // show the ✓/✗ bar iff a tool or mode is active
void on_shape_changed();