Design: Measure-style dimension labels (+ projection fix), New Design, tree auto-fit, i18n pin, UX

Sketch dimension labels — now identical to the Prepare/Preview Measure gizmo:
- draw_text repurposed to draw_dim_label: white ImGui text in a translucent-white box,
  mirroring GLGizmoMeasure::render_dimensioning exactly (push_common_window_style sets the
  text colour, BringWindowToDisplayFront, imgui_internal.h).
- ROOT-CAUSE FIX: world_to_screen_px multiplied two Eigen Transform3d objects
  ((proj * view).matrix()); a projection is not affine so Eigen mangled it -> garbage screen
  coords, so labels never appeared. Now proj.matrix() * view.matrix() like Measure. (That
  helper was previously [[maybe_unused]] dead code, never exercised.)
- Leaders: offset clear of the sketch line (no longer coincident with the geometry), single
  point-to-point dimension line + arrows, neutral colour, width 0.6 -> 0.2.
- dim_text appends mm/in on linear dims (angles keep the degree sign).

New Design + delete:
- New "New Design" button wipes the whole document (confirm dialog) — the clear-all the
  per-row Delete can't give. CadDocument::clear() now also clears bodies + display_body_meshes
  (it left them stale, so solids lingered after a clear).
- on_delete_feature: a Body-row selection now shows a helpful hint (bodies are recomputed
  results with no directly-removable feature) instead of silently doing nothing.

Feature tree: auto-fits its content (refresh_tree clamps height 1..9 rows, scrolls past),
instead of a fixed 140px block.

i18n (Design tab pinned English, per the UX contract):
- Restore the lost #undef _L / #define _L(s) wxString::FromUTF8(s) override atop DesignPanel.cpp;
  wrap all ~54 dropdown options in _L so the single lever governs them. feature_type_name left
  untranslated (machine-facing MCP JSON).

UX: per-card Value Confirm/Cancel buttons removed — the single ribbon action bar owns value
confirm/cancel via an m_value_cont guard in tool_confirm/tool_cancel. "needs a body" status
messages unified.

Both forks; DesignSketchTool.{cpp,hpp} + CadDocument.cpp byte-identical across forks. Built
clean; New Design / feature-delete / rotation / tree auto-fit live-verified on :10.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
This commit is contained in:
Tommaso Bianchi
2026-06-30 23:21:04 +02:00
co-authored by Claude Opus 4.8
parent 6e11cfc49b
commit 8cbf5b393b
5 changed files with 71 additions and 8 deletions
+45 -1
View File
@@ -1410,7 +1410,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
root->Add(m_box_constraints, 0, wxEXPAND);
root->Add(new wxStaticText(m_form, wxID_ANY, _L("Feature tree")), 0, wxLEFT | wxTOP, 12);
m_tree = new wxTreeCtrl(m_form, wxID_ANY, wxDefaultPosition, wxSize(-1, 140),
m_tree = new wxTreeCtrl(m_form, wxID_ANY, wxDefaultPosition, wxSize(-1, 64),
wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES |
wxTR_FULL_ROW_HIGHLIGHT | wxBORDER_SIMPLE);
if (!dp_dark()) m_tree->SetBackgroundColour(dp_panel_bg());
@@ -1512,6 +1512,10 @@ DesignPanel::DesignPanel(wxWindow* parent)
}
root->Add(m_dof_status, 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
auto* new_design = new wxButton(m_form, wxID_ANY, _L("New Design"));
new_design->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_new_design(); });
root->Add(new_design, 0, wxLEFT | wxRIGHT | wxTOP, 12);
auto* commit = new wxButton(m_form, wxID_ANY, _L("Commit to Plate"));
commit->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_commit(); });
root->Add(commit, 0, wxLEFT | wxRIGHT | wxTOP, 12);
@@ -2941,6 +2945,17 @@ void DesignPanel::refresh_tree()
}
if (keep >= 0 && keep < int(m_tree_items.size()))
m_tree->SelectItem(m_tree_items[keep]);
// Size the tree to its content (clamped) so it doesn't waste a fixed-height block when
// there are few features, and scrolls internally past ~9 rows instead of growing forever.
int rows = int(m_tree_items.size());
if (m_doc.bodies.size() > 1) rows += 1 + int(m_doc.bodies.size()); // "Bodies" header + rows
const int rowH = std::max(m_tree->GetCharHeight() + 8, 20);
const int shown = std::min(std::max(rows, 1), 9);
const wxSize ts(-1, shown * rowH + 8);
m_tree->SetMinSize(ts);
m_tree->SetMaxSize(ts);
if (m_form && m_form->GetSizer()) { m_form->Layout(); m_form->FitInside(); }
}
int DesignPanel::tree_body_selection() const
@@ -3143,8 +3158,37 @@ void DesignPanel::after_tree_edit(bool ok)
m_status->Refresh();
}
// Erase the whole document (every feature + body) and start fresh. The single "wipe" the
// feature tree's per-row Delete can't give you — also the way out when a body has no
// removable owning feature.
void DesignPanel::on_new_design()
{
if (m_doc.features.empty() && m_doc.bodies.empty()) { set_status_ok(); return; }
wxMessageDialog dlg(this,
_L("Erase all features and bodies and start a new design? This cannot be undone."),
_L("New Design"), wxYES_NO | wxICON_EXCLAMATION);
if (dlg.ShowModal() != wxID_YES) return;
tool_cancel(); // leave any active tool / sketch / constrain cleanly
m_doc.clear(); // features + bodies + meshes + history
m_edit_index = -1;
m_move_body = -1;
m_body_xform.clear();
if (m_viewport) { m_viewport->clear_move_gizmo(); m_viewport->clear_mesh(); }
after_tree_edit(true); // rebuild the (now empty) tree + clear the viewport
update_action_bar();
set_status_ok();
}
void DesignPanel::on_delete_feature()
{
// A Body row has no directly-removable feature (bodies are recomputed results); guide the
// user to delete the feature that created it, or use New Design to wipe everything.
if (tree_body_selection() >= 0) {
m_status->SetForegroundColour(wxColour(235, 110, 110));
m_status->SetLabel(_L("Select the FEATURE that created this body (or use New Design)"));
m_status->Refresh();
return;
}
int sel = tree_selection();
if (sel == wxNOT_FOUND) {
m_status->SetLabel(_L("Select a feature in the tree first"));