CAD: three Design-tab fixes — consumed holed loop, constraint rows, hover ghost

1. A consumed loop WITH HOLES was never dropped from the sketch overlay.
   sync_sketch_display compares each region's entity list against what a per-loop
   extrude stored, but rebuilt the candidate from the region's OWN entities while
   the extrude stores the region's entities PLUS every hole's (see
   selected_loop_entities). For a plate with one bore that is 4 against 5, so the
   match never fired and the extruded rectangle stayed drawn on top of the solid
   it had become. Adds region_entity_indices_with_holes, which returns the same
   order the extrude uses, and compares against that; a matching region now drops
   its holes' entities too. Hole-less regions are unaffected.

   This is also the artefact that made a correct plate-with-a-bore read as a
   plate with a plug in it during rig testing.

2. The Constraints card drew its header and its first row on top of each other.
   The rows and the delete buttons were parented to m_form rather than m_cards,
   so they were laid out in the wrong window's coordinate space and started at
   the card's top edge. Re-parented; nothing else about the card changed.

3. The mate hover ghost never appeared. The highlight handler asked for a repaint
   with request_repaint(), which only queues a Refresh — and a wxMenu popup runs
   its own modal loop, so the paint was not serviced until the menu closed, by
   which time the ghost had been dropped. Adds DesignCanvas::repaint_now(), which
   flushes the paint immediately, mirroring the m_status->Update() the status line
   in the same function already needed for the same reason.

Delegated to opencode (DeepSeek V4 Pro) and reviewed by diff. Fix 1 needed an
accessor on DesignSketchTool because region_loops/RegionLoop are private — that
was outside the file list it was given, and it said so rather than working around
it.

Verified on the rig: a rectangle-plus-circle sketch extrudes to a plate with a
bore and NO overlay left on top of it, and the Constraints card shows its header
clear of eight readable rows.
This commit is contained in:
Tommaso Bianchi
2026-08-14 09:15:51 +02:00
parent 9340d4c7dc
commit c45f84edf4
5 changed files with 48 additions and 6 deletions
+6 -6
View File
@@ -4079,7 +4079,7 @@ void DesignPanel::sync_sketch_display()
// Drop the entities of any loop already extruded; keep the rest (other
// loops + non-loop entities) so they stay visible and selectable.
std::vector<char> drop(f.entities.size(), 0);
for (const std::vector<int>& loop : m_viewport->region_entity_indices(f.entities)) {
for (const std::vector<int>& loop : m_viewport->region_entity_indices_with_holes(f.entities)) {
std::vector<SketchEntity> es;
for (int ei : loop)
if (ei >= 0 && ei < int(f.entities.size())) es.push_back(f.entities[ei]);
@@ -5768,7 +5768,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
if (!mate_ghost) return;
m_viewport->clear_preview();
m_viewport->set_body_hidden(false);
m_viewport->request_repaint();
m_viewport->repaint_now();
mate_ghost = false;
};
menu.Bind(wxEVT_MENU_HIGHLIGHT,
@@ -5779,7 +5779,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
if (i < 0 || i >= int(opts.size()) || !opts[i].viable) { drop_ghost(); return; }
std::string err;
mate_ghost = show_mate_ghost(opts[i].kind, cs_a, cs_b, 0.0, 0.0, false, err);
m_viewport->request_repaint();
m_viewport->repaint_now(); // synchronous: the popup owns the loop, a queued repaint is never serviced
});
menu.Bind(wxEVT_MENU, [this, cs_a, cs_b, opts, mate_base, drop_ghost](wxCommandEvent& e) {
@@ -7137,7 +7137,7 @@ void DesignPanel::rebuild_constraint_list()
m_hdr_constraints->SetLabel(wxString::Format(_L("Constraints (%d)"), int(cons.size())));
if (cons.empty()) {
auto* none = new wxStaticText(m_form, wxID_ANY, _L("No constraints yet"));
auto* none = new wxStaticText(m_cards, wxID_ANY, _L("No constraints yet"));
none->SetForegroundColour(dp_sec_text());
m_constraint_rows->Add(none, 0, wxTOP, 4);
}
@@ -7145,12 +7145,12 @@ void DesignPanel::rebuild_constraint_list()
auto* row = new wxBoxSizer(wxHORIZONTAL);
// Delete button first (fixed left position, always visible — long labels can
// horizontally scroll but ✗ stays put and clickable). BMP-safe ✗ glyph.
auto* del = new wxButton(m_form, wxID_ANY, wxString::FromUTF8(""),
auto* del = new wxButton(m_cards, wxID_ANY, wxString::FromUTF8(""),
wxDefaultPosition, wxSize(26, -1));
del->SetToolTip(_L("Delete constraint"));
del->Bind(wxEVT_BUTTON, [this, i](wxCommandEvent&) { delete_constraint(i); });
// Clickable label: selecting it highlights the referenced entities.
auto* lbl = new wxButton(m_form, wxID_ANY, constraint_label(cons[i]),
auto* lbl = new wxButton(m_cards, wxID_ANY, constraint_label(cons[i]),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBORDER_NONE);
lbl->Bind(wxEVT_BUTTON, [this, i](wxCommandEvent&) { highlight_constraint_entities(i); });
row->Add(del, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);