From d4904b8e2efdcf367e89f8a3fc84ae5e27823acf Mon Sep 17 00:00:00 2001 From: Tommaso Bianchi Date: Sat, 25 Jul 2026 17:39:44 +0200 Subject: [PATCH] Design tab: stop eight tool cards rendering at startup; regroup the drawers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sidebar opened with eight tool cards stacked in it — Transform, Mirror, Thicken, Rib, Project, Delete Face, Helix and Mate. A card added to the cards sizer is visible until something hides it, and close_tool()'s hide-all only runs on a tool SWITCH, so anything missing from the construction-time hide block is on screen from the moment the tab opens. Those eight were wired into close_tool() but never added here. All 37 cards are now hidden at startup. Worth stating because it invalidates a check I ran while diagnosing this: every card IS hidden somewhere in the file, so grepping for "hidden anywhere" says nothing. The block that matters is the one in the constructor. Second, the drawers mixed unrelated operations, and two group tooltips no longer described their contents — Dress-up listed eight tools spanning three different kinds of operation, and Add material still claimed to hold only extrude/revolve/sweep/loft after Thicken and Rib were added to it. One concept per drawer now: Add material extrude, revolve, sweep, loft, thicken, rib -> grows new solid material, whether from a profile, a face or a line Surface unchanged; already coherent Datum / Curve plane, axis, coord sys, helix, PROJECT -> reference geometry and derived curves. Project consumes a body but PRODUCES sketch entities, so it is curve creation, not a finishing operation Placement TRANSFORM, MIRROR, MATE (new) -> moves a body without changing its shape; a mate places one body relative to another Dress-up fillet/chamfer, draft, shell, delete face -> finishing on the faces and edges of an existing solid Hole / thread unchanged The new drawer costs no toolbar width: the layout order already contained an empty put("place") slot between "material" and "plane" with nothing registered to it. Shift+Y and Shift+Z follow Transform and Mirror; every tool still appears exactly once. Compiles clean; kernel untouched, so the suite is unaffected at 139 cases / 1960 assertions. Co-Authored-By: Claude Opus 5 (1M context) --- src/slic3r/GUI/DesignPanel.cpp | 149 +++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 63 deletions(-) diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index c9ab417ce8..63e2af8930 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -493,8 +493,9 @@ DesignPanel::DesignPanel(wxWindow* parent) b_sketch->Bind(wxEVT_BUTTON, [act_sketch](wxCommandEvent&) { act_sketch(); }); m_keys_feature[SHIFT('S')] = act_sketch; fadd("sketch", b_sketch); - // Add material: Extrude / Revolve / Sweep / Loft - feat_dropdown("material", "design_extrude", _L("Add material (extrude / revolve / sweep / loft)"), { + // Add material: every feature that grows new solid material — from a profile + // (extrude/revolve/sweep/loft), from a face (thicken) or from a line (rib). + feat_dropdown("material", "design_extrude", _L("Add material (extrude / revolve / sweep / loft / thicken / rib)"), { {"design_extrude", _L("Extrude"), _L("Extrude a sketch profile, or push/pull a picked face"), [this] { // Onshape push/pull: an explicitly picked solid face (Face-level cycle, no loop @@ -699,7 +700,7 @@ DesignPanel::DesignPanel(wxWindow* parent) }, SHIFT('V')}, }); - feat_dropdown("plane", "design_plane", _L("Datum / Curve (plane / axis / coord sys / helix)"), { + feat_dropdown("plane", "design_plane", _L("Datum / Curve (plane / axis / coord sys / helix / project)"), { {"design_plane", _L("Plane"), _L("Reference plane (offset / tilt / midplane / tangent / two edges / coincident)"), [this] { populate_plane_choices(m_plane_base); @@ -723,6 +724,73 @@ DesignPanel::DesignPanel(wxWindow* parent) populate_plane_choices(m_helix_plane); open_tool(Tool::Helix); }, 0}, + // Project belongs here, not in Dress-up: it consumes a body but PRODUCES sketch + // geometry, so it is reference/curve creation like the four above, not a finishing op. + {"design_sketch", _L("Project"), _L("Project body edges onto a plane as sketch entities"), + [this] { + if (m_doc.bodies.empty()) { + m_status->SetForegroundColour(wxColour(235, 110, 110)); + m_status->SetLabel(_L("Project needs a body — add or import one first")); + m_status->Refresh(); + return; + } + { + m_proj_source_body->Clear(); + for (size_t i = 0; i < m_doc.bodies.size(); ++i) { + const std::string& n = m_doc.bodies[i].name; + m_proj_source_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n)); + } + if (m_proj_source_body->GetCount() > 0) m_proj_source_body->SetSelection(0); + } + populate_plane_choices(m_proj_plane); + m_sel_solid_face = -1; + m_proj_face_label->SetLabel(_L("(all edges)")); + open_tool(Tool::Project); + }, 0}, + }); + + // Placement — operations that MOVE a body without changing its shape. Transform and + // Mirror place one body directly; a Mate places one body relative to another. They were + // in Dress-up (fillet/draft/shell) and Datum, which mixed shape-finishing and reference + // geometry with rigid-body placement. "place" was already an empty slot in the bar order. + feat_dropdown("place", "design_move", _L("Placement (transform / mirror / mate)"), { + {"design_move", _L("Transform"), _L("Move and/or rotate an existing body"), + [this] { + if (m_doc.bodies.empty()) { + m_status->SetForegroundColour(wxColour(235, 110, 110)); + m_status->SetLabel(_L("Transform needs a body — add or import one first")); + m_status->Refresh(); + return; + } + { + m_xf_body->Clear(); + for (size_t i = 0; i < m_doc.bodies.size(); ++i) { + const std::string& n = m_doc.bodies[i].name; + m_xf_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n)); + } + if (m_xf_body->GetCount() > 0) m_xf_body->SetSelection(0); + } + open_tool(Tool::Transform); + }, SHIFT('Y')}, + {"design_mirror", _L("Mirror"), _L("Reflect a body about a plane"), + [this] { + if (m_doc.bodies.empty()) { + m_status->SetForegroundColour(wxColour(235, 110, 110)); + m_status->SetLabel(_L("Mirror needs a body — add or import one first")); + m_status->Refresh(); + return; + } + { + m_mirror_body->Clear(); + for (size_t i = 0; i < m_doc.bodies.size(); ++i) { + const std::string& n = m_doc.bodies[i].name; + m_mirror_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n)); + } + if (m_mirror_body->GetCount() > 0) m_mirror_body->SetSelection(0); + } + populate_plane_choices(m_mirror_plane); + open_tool(Tool::Mirror); + }, SHIFT('Z')}, {"design_plane", _L("Mate"), _L("Assembly: align two CoordSys features (fastened, planar, revolute, slider, cylindrical)"), [this] { open_tool(Tool::Mate); @@ -767,72 +835,15 @@ DesignPanel::DesignPanel(wxWindow* parent) b_color->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_set_body_color(); }); fadd("color", b_color); - // Dress-up: Fillet/Chamfer / Draft / Shell / Transform / Mirror / Project / Delete Face - feat_dropdown("dressup", "design_dressup", _L("Dress-up (fillet / chamfer / draft / shell / transform / mirror / project / delete)"), { + // Dress-up: finishing operations on the faces and edges of an existing solid — nothing + // that moves a body (see the Placement drawer) and nothing that creates geometry. + feat_dropdown("dressup", "design_dressup", _L("Dress-up (fillet / chamfer / draft / shell / delete face)"), { {"design_dressup", _L("Fillet / Chamfer"), _L("Round or bevel a picked edge"), [this] { open_tool(Tool::Dressup); }, SHIFT('F')}, {"design_draft", _L("Draft (taper a face)"), _L("Tilt a picked face by a draft angle"), [this] { open_tool(Tool::Draft); }, SHIFT('D')}, {"design_shell", _L("Shell"), _L("Hollow the body to a wall thickness, opening a picked face"), [this] { open_tool(Tool::Shell); }, SHIFT('K')}, - {"design_move", _L("Transform"), _L("Move and/or rotate an existing body"), - [this] { - if (m_doc.bodies.empty()) { - m_status->SetForegroundColour(wxColour(235, 110, 110)); - m_status->SetLabel(_L("Transform needs a body — add or import one first")); - m_status->Refresh(); - return; - } - { - m_xf_body->Clear(); - for (size_t i = 0; i < m_doc.bodies.size(); ++i) { - const std::string& n = m_doc.bodies[i].name; - m_xf_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n)); - } - if (m_xf_body->GetCount() > 0) m_xf_body->SetSelection(0); - } - open_tool(Tool::Transform); - }, SHIFT('Y')}, - {"design_mirror", _L("Mirror"), _L("Reflect a body about a plane"), - [this] { - if (m_doc.bodies.empty()) { - m_status->SetForegroundColour(wxColour(235, 110, 110)); - m_status->SetLabel(_L("Mirror needs a body — add or import one first")); - m_status->Refresh(); - return; - } - { - m_mirror_body->Clear(); - for (size_t i = 0; i < m_doc.bodies.size(); ++i) { - const std::string& n = m_doc.bodies[i].name; - m_mirror_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n)); - } - if (m_mirror_body->GetCount() > 0) m_mirror_body->SetSelection(0); - } - populate_plane_choices(m_mirror_plane); - open_tool(Tool::Mirror); - }, SHIFT('Z')}, - {"design_sketch", _L("Project"), _L("Project body edges onto a plane as sketch entities"), - [this] { - if (m_doc.bodies.empty()) { - m_status->SetForegroundColour(wxColour(235, 110, 110)); - m_status->SetLabel(_L("Project needs a body — add or import one first")); - m_status->Refresh(); - return; - } - { - m_proj_source_body->Clear(); - for (size_t i = 0; i < m_doc.bodies.size(); ++i) { - const std::string& n = m_doc.bodies[i].name; - m_proj_source_body->Append(n.empty() ? wxString::Format(_L("Body %zu"), i + 1) : wxString::FromUTF8(n)); - } - if (m_proj_source_body->GetCount() > 0) m_proj_source_body->SetSelection(0); - } - populate_plane_choices(m_proj_plane); - m_sel_solid_face = -1; - m_proj_face_label->SetLabel(_L("(all edges)")); - open_tool(Tool::Project); - }, 0}, {"design_dressup", _L("Delete Face"), _L("Remove faces from a body and heal the solid"), [this] { if (m_doc.bodies.empty()) { @@ -2856,6 +2867,18 @@ DesignPanel::DesignPanel(wxWindow* parent) cards->Show(m_box_sketch_session, false, true); cards->Show(m_box_constraints, false, true); cards->Show(m_box_expr, false, true); + // A card added to the cards sizer is VISIBLE until something hides it. close_tool()'s + // hide-all only runs on a tool switch, so any card missing from THIS block renders + // stacked in the sidebar from the moment the tab opens. These eight were wired into + // close_tool() but not here, which is what bloated the panel. + cards->Show(m_box_transform, false, true); + cards->Show(m_box_mirror, false, true); + cards->Show(m_box_thicken, false, true); + cards->Show(m_box_rib, false, true); + cards->Show(m_box_project, false, true); + cards->Show(m_box_delete_face, false, true); + cards->Show(m_box_helix, false, true); + cards->Show(m_box_mate, false, true); m_form->FitInside(); m_form->SetScrollRate(0, 10); // vertical only, like Prepare's sidebar: never scroll labels out