The chip in the corner was holding the keyboard, and the planes were holding the bed

Two reports, three defects, all three measured on the running app rather than reasoned about.

"Keyboard focus in drawing tool is broken so that now they are slow and cumbersome." After a
dimensioned entity the bottom-right readout chip — 119x31, borderless, a wxFrame — held the X
input focus. Pressing r produced NO [KEYTRACE] line at all: the key never reached the panel's
CHAR_HOOK. One bare canvas click moved focus back to the main window and the identical key armed
the tool. So every shortcut was dead after every dimension, and the way to get the keyboard back
was to click somewhere harmless. That is the whole of "slow and cumbersome".

Its sibling, the status chip, is a wxPopupWindow for exactly this reason and carries a comment
warning against turning it back into a frame. The readout was left a frame on the premise that
"it appears mid-gesture and the next input is the mouse" — which the measurement falsifies: the
chip keeps the last value on screen after the gesture ends, and a frame that has the focus does
not give it back. It is now a popup too, with the placement and the iconise/deactivate lifecycle
its sibling already needed, because an override-redirect window would otherwise sit on the bare
desktop when the app is minimised.

"Planes hide the bed." Literally true, twice over. The reference planes were half-extent 0.6 *
the bed's larger side — a square 1.2x the plate — and all three are drawn with depth testing
off, so they painted over the plate grid from edge to edge. 0.3 puts them inside the bed, which
is also the Onshape look the size was reaching for: a modest square at the origin, not a
tablecloth.

And the other half was mine. 3f52166e32 muted the bed for the duration of a sketch, on the
argument that a plate grid and a sketch grid are the same visual language. The argument is right
and the call was wrong, because there IS no sketch grid to take over. Pick XY, arm Line, and the
viewport was an empty grey field: no bed, no grid, no origin, nothing to judge a length or a
direction against. The plate grid was carrying the ground reference for the whole tab. The banner
already says where you are; taking the floor away as well only made the sketch harder to draw.
The Bed checkbox is the one thing that governs the bed, in every mode.

Verified on behemoth :10 with the rebuilt binary: focus after a dimension chain is the main
window, r arms Rectangle with no click in between, and the plate grid is under the sketch.

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 13:58:40 +02:00
co-authored by Claude Opus 5
parent cf444a6ab6
commit 00d6c191dc
4 changed files with 50 additions and 16 deletions
+24 -3
View File
@@ -121,11 +121,13 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
// Bottom-right viewport HUD: a borderless, non-focusable float label showing the active
// tool's current values. Top-level (a child widget is hidden by the GL surface, same as
// the inline editor). Fed every frame by the tool's on_readout; empty text hides it.
// NON-FOCUSABLE IS THE LOAD-BEARING WORD, and a wxFrame is not: see the header. The chip
// outlives the gesture that drew it, and while it held the X input focus every sketch
// shortcut was swallowed until the user clicked the canvas. Same window class as the status
// chip below for the same reason. Do not "simplify" it back to a wxFrame.
{
wxWindow* top = wxGetTopLevelParent(m_canvas_widget);
m_hud = new wxFrame(top, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxFRAME_NO_TASKBAR | wxBORDER_NONE | wxFRAME_FLOAT_ON_PARENT |
wxSTAY_ON_TOP | wxTRANSPARENT_WINDOW);
m_hud = new wxPopupWindow(top, wxBORDER_NONE);
m_hud->SetBackgroundColour(wxColour(28, 30, 34));
m_hud_label = new wxStaticText(m_hud, wxID_ANY, wxEmptyString);
m_hud_label->SetForegroundColour(wxColour(0x46, 0xE0, 0xC8)); // teal, reads on dark bed
@@ -1158,6 +1160,13 @@ void DesignCanvas::set_readout(const std::string& text)
m_hud_last = text;
if (text.empty()) { m_hud->Hide(); return; }
m_hud_label->SetLabel(wxString::FromUTF8(text));
place_readout_hud();
}
void DesignCanvas::place_readout_hud()
{
if (!m_hud || !m_hud_label || !m_canvas_widget) return;
if (m_hud_last.empty() || !m_canvas_widget->IsShownOnScreen()) { m_hud->Hide(); return; }
m_hud->Fit();
// Anchor to the canvas's bottom-right corner with a small margin (screen coords).
const wxSize cs = m_canvas_widget->GetClientSize();
@@ -1169,6 +1178,16 @@ void DesignCanvas::set_readout(const std::string& text)
m_hud->Raise();
}
// A popup is override-redirect: the window manager does not own it, so an iconised or
// deactivated app would leave the chip sitting on the bare desktop. The status chip already
// had to answer this; now that the readout is a popup too, it answers it the same way.
void DesignCanvas::show_readout_hud(bool on)
{
if (!m_hud) return;
if (on) place_readout_hud();
else m_hud->Hide();
}
// Clear of the view cube and the two round view buttons, which own the bottom-left corner.
// Shared by the placement and by the wrap width, which have to agree or the chip wraps to a
// width it is then not given.
@@ -1230,12 +1249,14 @@ void DesignCanvas::place_status_hud()
void DesignCanvas::on_frame_iconize(wxIconizeEvent& e)
{
show_status_hud(!e.IsIconized());
show_readout_hud(!e.IsIconized());
e.Skip();
}
void DesignCanvas::on_frame_activate(wxActivateEvent& e)
{
show_status_hud(e.GetActive());
show_readout_hud(e.GetActive());
e.Skip();
}