mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 09:11:08 +00:00
Keyboard and mouse - Esc drops what is pending (picks, a dimension's first point, an edit-op or transform) and never applies it; Enter applies a ready edit-op/transform, ends a polyline/spline chain, ends an armed tool, and confirms a feature card exactly when its ✓ is enabled. - Right-click only abandons the gesture in progress; with nothing pending it opens the offer in every tool (Trim, edit-ops, transforms, Dimension, Constrain, TransformArt, move gizmo). Clicking empty space no longer commits. The offer needs no timing, only a still press. - Delete removes only an explicit selection. Undo/redo inside a sketch go through the same route as the buttons (whole shapes, with redo); Edit > Undo follows the shown tab. - The canvas no longer handles Delete/Esc/Ctrl+Z itself (Backspace in a value field deleted the geometry it measured); F is in the panel's key map. - Value fields: a refused value keeps the field open with the reason; click outside and Tab commit; an untouched field commits the exact value; any decimal separator is accepted; lengths are always mm; validation is the same for every editor. - Snapping: the marker shows only where the click will actually snap; pick tolerances are one set of pixel budgets (Constrain picks within reach; no mm floor on labels). Messages and consistency - set_status(kind, text) gives every status line its own colour and glyph; kernel errors are translated into sentences and formatted, not concatenated; sketch refusals go to the status line instead of the per-frame HUD that erased them. - Hints describe the gestures that now work; Dimension shows its second step; Constrain uses the sketch palette (red means conflict only); the straight slot's value is its width. - Hole/Thread/Project keep the user's pick or refuse up front; circular pattern opens with its own preview; thread fields use the nominal diameter and the ISO internal depth. Integration - MCP loads the project's recipe before touching the document, refuses to mutate it while the tab is busy, never runs a request that already timed out, only replaces a socket at its path, caps line length and removes the socket at exit; not started in the G-code viewer. - Hiding a feature keeps later body references on their bodies; a design keeps its modeling origin across printer changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
60 lines
3.4 KiB
C++
60 lines
3.4 KiB
C++
#ifndef slic3r_GUI_DesignInteraction_hpp_
|
|
#define slic3r_GUI_DesignInteraction_hpp_
|
|
|
|
namespace Slic3r { namespace GUI {
|
|
|
|
// The Design tab's interaction stack, and the ONE rule Esc obeys.
|
|
//
|
|
// Esc unwinds exactly one level per press, deepest first, and never more. The enum value IS
|
|
// the LIFO depth, so "which level does this press belong to" is a comparison, not a chain of
|
|
// special cases scattered over three files — which is what it was, and why two presses in a
|
|
// row could reach past a tool and destroy the sketch underneath it.
|
|
//
|
|
// STRICT INVARIANT (the bug this exists to make unrepresentable): no level of Esc deletes a
|
|
// feature, discards a sketch that holds geometry, or rolls history back. Destroying work needs
|
|
// a gesture that says so — Delete/Backspace on an explicit selection, the banner's Cancel, or
|
|
// Ctrl+Z. An Esc that can destroy is an Esc nobody can press with confidence, and being the
|
|
// safe key is the whole point of it.
|
|
enum class CadLevel : int {
|
|
Idle = 0, // nothing transient is up: Esc clears the selection
|
|
Tool = 1, // a feature card / armed sketch tool / constrain session: Esc exits it
|
|
Gesture = 2, // an uncommitted delta (entity being drawn, body being dragged): Esc reverts it
|
|
Transient = 3, // a value field or a popup menu: Esc closes just that
|
|
};
|
|
|
|
// What the tab is doing, reduced to the four bits the routing actually needs. Kept as a POD of
|
|
// answers rather than a pointer to the panel so the rule below is decidable — and checkable —
|
|
// without a window, a GL context or an event loop.
|
|
struct CadInteractionState {
|
|
bool value_field_open{false}; // in-canvas value field, or the panel's value card
|
|
bool gesture_active{false}; // in-progress entity points, or a body being moved
|
|
bool tool_armed{false}; // feature card open, sketch draw tool armed, constrain session
|
|
bool has_selection{false}; // something is picked (model or sketch)
|
|
};
|
|
|
|
// The whole routing rule. Deepest live level wins; Idle is the floor.
|
|
constexpr CadLevel cad_escape_level(const CadInteractionState& s)
|
|
{
|
|
if (s.value_field_open) return CadLevel::Transient;
|
|
if (s.gesture_active) return CadLevel::Gesture;
|
|
if (s.tool_armed) return CadLevel::Tool;
|
|
return CadLevel::Idle;
|
|
}
|
|
|
|
// The ordering is the entire contract, so it is checked where it is defined, at compile time.
|
|
static_assert(cad_escape_level({true, true, true, true}) == CadLevel::Transient, "value field is deepest");
|
|
static_assert(cad_escape_level({false, true, true, true}) == CadLevel::Gesture, "gesture beats tool");
|
|
static_assert(cad_escape_level({false, false, true, true}) == CadLevel::Tool, "tool beats idle");
|
|
static_assert(cad_escape_level({false, false, false, true}) == CadLevel::Idle, "selection is idle-level");
|
|
static_assert(cad_escape_level({false, false, false, false}) == CadLevel::Idle, "empty is idle");
|
|
|
|
// Right-click vs. right-drag-orbit. A press that stays put summons the offer; one that travelled
|
|
// was navigation, and navigation must never be rewarded with a menu over wherever the camera
|
|
// happened to stop. Distance only, never duration: charter 6.2 rules out any gesture that
|
|
// depends on timing, and a press that did not move did not move the camera either.
|
|
inline constexpr int kCadRightClickDriftPx = 3; // cursor drift budget, max(|dx|,|dy|)
|
|
|
|
}} // namespace Slic3r::GUI
|
|
|
|
#endif // slic3r_GUI_DesignInteraction_hpp_
|