Design sidebar: align with Prepare's visual language

The two tabs used different idioms for the same concepts, which reads as two
different apps: Design was 264 px against Prepare's ~467 (the canvas edge jumped
on every tab switch), used bare micro-labels where Prepare uses icon + Head_14
card headers with a rule, hung its row actions on a loose strip under the tree
instead of in the section header, and drew raw OS-default wxButtons next to
Prepare's Orca-styled ones.

- Width now tracks Prepare's sidebar at runtime (sync_sidebar_width() reads the
  live width on tab activation) rather than being hardcoded, so the two cannot
  drift apart if Orca changes its sidebar.
- "Feature tree" and "Bodies" use the card_header() helper the panel already had
  (icon + Label::Head_14) plus a wxStaticLine, exactly as the tool cards do.
- The six row actions moved into the Feature tree header, Prepare-style, at
  header weight (24 px) instead of 36 px control weight.
- Buttons are Orca Buttons (ButtonType::Expanded, full width); Commit to Plate
  gets ButtonStyle::Confirm as the tab's primary action.
- Margins/spacing come from SidebarProps (ContentMargin/TitlebarMargin/
  ElementSpacing) instead of hardcoded 12/6/4.

Verified on :10: sidebars are the same width and share the header idiom.
Still to do: label-left/control-right rows inside the tool dialogs.

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-07-19 12:09:44 +02:00
co-authored by Claude Opus 4.8
parent b9e927876d
commit 7d2a63ffcb
2 changed files with 83 additions and 29 deletions
+75 -28
View File
@@ -42,6 +42,7 @@
#include "slic3r/GUI/wxExtensions.hpp" // ScalableButton, create_scaled_bitmap
#include "Widgets/Label.hpp" // HarmonyOS Sans fonts (Head_*/Body_*) shared with the rest of Orca
#include "Widgets/DropDown.hpp" // Orca-themed combo dropdown (white/teal selector) for the tool flyouts
#include "Widgets/Button.hpp" // Orca-styled Button (ButtonStyle/ButtonType) — same look as Prepare
#include "libslic3r/SketchImport.hpp" // text_to_regions / svg_to_regions
#include "libslic3r/ThreadStandards.hpp" // ISO metric / Unified imperial thread tables
#include "libslic3r/Model.hpp"
@@ -1542,7 +1543,17 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_box_constraints->Add(m_constraint_rows, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 12);
root->Add(m_box_constraints, 0, wxEXPAND);
root->Add(new wxStaticText(m_form, wxID_ANY, _L("Feature tree")), 0, wxLEFT | wxTOP, 12);
// Feature tree card. Same idiom as Prepare's sections (icon + Head_14 title + rule) via the
// shared card_header helper, instead of the bare micro-label this used to be; the row-edit
// actions live in the header, as Prepare puts its section actions.
m_hdr_tree_row = new wxBoxSizer(wxHORIZONTAL);
m_hdr_tree_row->Add(card_header("design_sketch", _L("Feature tree"), m_hdr_tree), 0,
wxALIGN_CENTER_VERTICAL);
m_hdr_tree_row->AddStretchSpacer();
root->Add(m_hdr_tree_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
FromDIP(SidebarProps::ContentMargin()));
root->Add(new wxStaticLine(m_form), 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
FromDIP(SidebarProps::TitlebarMargin()));
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);
@@ -1573,14 +1584,14 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_viewport->set_body_highlight(body);
});
// Feature-tree edit row: act on the selected feature (delete / reorder).
// Feature-tree edit actions: act on the selected feature (delete / reorder). These sit in the
// card header (Prepare puts its section actions there too) rather than on a loose row below.
{
auto* trow = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer* trow = m_hdr_tree_row;
auto edit_btn = [this](const char* icon, const wxString& tip) {
// Enlarged to match the main ribbon's weight (largest that fits 6
// across the ~264px form column).
auto* b = new ScalableButton(m_form, wxID_ANY, icon, "", wxSize(36, 36),
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 30);
// Header-sized: reads as a section action, not a primary control.
auto* b = new ScalableButton(m_form, wxID_ANY, icon, "", wxSize(24, 24),
wxDefaultPosition, wxBU_EXACTFIT | wxBORDER_NONE, false, 20);
b->SetToolTip(tip);
return b;
};
@@ -1608,20 +1619,27 @@ DesignPanel::DesignPanel(wxWindow* parent)
up->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_move_feature(-1); });
auto* down = edit_btn("design_movedown", _L("Move down"));
down->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_move_feature(+1); });
trow->Add(edit, 0, wxRIGHT, 4);
trow->Add(move, 0, wxRIGHT, 4);
trow->Add(vis, 0, wxRIGHT, 4);
trow->Add(del, 0, wxRIGHT, 4);
trow->Add(up, 0, wxRIGHT, 4);
trow->Add(down, 0);
root->Add(trow, 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
const int gap = FromDIP(SidebarProps::ElementSpacing());
trow->Add(edit, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
trow->Add(move, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
trow->Add(vis, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
trow->Add(del, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
trow->Add(up, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
trow->Add(down, 0, wxALIGN_CENTER_VERTICAL);
// trow IS the header sizer — already added to root above the tree.
}
// Parts list (Onshape's Features + Parts split). Bodies used to be appended after the
// features INSIDE the feature tree, so they were pushed out of view as the history grew —
// with no way to select a body at all. Their own list keeps them reachable regardless.
m_parts_label = new wxStaticText(m_form, wxID_ANY, _L("Bodies"));
root->Add(m_parts_label, 0, wxLEFT | wxTOP, 12);
m_parts_hdr = new wxBoxSizer(wxHORIZONTAL);
m_parts_hdr->Add(card_header("design_extrude", _L("Bodies"), m_parts_label), 0,
wxALIGN_CENTER_VERTICAL);
root->Add(m_parts_hdr, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
FromDIP(SidebarProps::ContentMargin()));
m_parts_rule = new wxStaticLine(m_form);
root->Add(m_parts_rule, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
FromDIP(SidebarProps::TitlebarMargin()));
m_parts = new wxTreeCtrl(m_form, wxID_ANY, wxDefaultPosition, wxSize(-1, 48),
wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES |
wxTR_FULL_ROW_HIGHLIGHT | wxBORDER_SIMPLE);
@@ -1643,11 +1661,20 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_status->Refresh();
});
// Orca-styled full-width buttons (ButtonType::Expanded), so the Design sidebar reads like
// Prepare's instead of showing raw OS-default wxButtons.
const int cm = FromDIP(SidebarProps::ContentMargin());
auto styled_btn = [this](const wxString& label, ButtonStyle style = ButtonStyle::Regular) {
auto* b = new Button(m_form, label);
b->SetStyle(style, ButtonType::Expanded);
return b;
};
// Prepare's "Place on Face (F)" for the selected body: pick a face, lay it flat on the bed.
auto* place = new wxButton(m_form, wxID_ANY, _L("Place on Face (F)"));
auto* place = styled_btn(_L("Place on Face (F)"));
place->SetToolTip(_L("Select a body face (click a solid, click again to a face), then lay that face on the bed"));
place->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { place_on_face(); });
root->Add(place, 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
root->Add(place, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, cm);
m_status = new wxStaticText(m_form, wxID_ANY, "");
root->Add(m_status, 0, wxLEFT | wxRIGHT | wxBOTTOM, 12);
@@ -1664,30 +1691,32 @@ DesignPanel::DesignPanel(wxWindow* parent)
// Section View — clear text button (non-destructive: hides part of the model to inspect
// inside; adds a named "Section View N", never a body). Distinct from the Cut tool.
auto* section_btn = new wxButton(m_form, wxID_ANY, _L("Section View"));
auto* section_btn = styled_btn(_L("Section View"));
section_btn->SetToolTip(_L("Hide part of the model to see inside (non-destructive). "
"PageUp/PageDown move the plane; Delete removes it."));
section_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { toggle_section_view(); });
root->Add(section_btn, 0, wxLEFT | wxRIGHT | wxTOP, 12);
root->Add(section_btn, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, cm);
// Flip the active section to the opposite half — only usable while a section view is active.
m_section_flip_btn = new wxButton(m_form, wxID_ANY, _L("Flip Section"));
m_section_flip_btn = styled_btn(_L("Flip Section"));
m_section_flip_btn->SetToolTip(_L("Show the opposite half of the active section view"));
m_section_flip_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { flip_section_view(); });
m_section_flip_btn->Enable(false);
root->Add(m_section_flip_btn, 0, wxLEFT | wxRIGHT | wxTOP, 6);
root->Add(m_section_flip_btn, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP,
FromDIP(SidebarProps::ElementSpacing())); // related to the button above it
auto* new_design = new wxButton(m_form, wxID_ANY, _L("New Design"));
auto* new_design = styled_btn(_L("New Design"));
new_design->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_new_design(); });
root->Add(new_design, 0, wxLEFT | wxRIGHT | wxTOP, 12);
root->Add(new_design, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, cm);
auto* commit = new wxButton(m_form, wxID_ANY, _L("Commit to Plate"));
// Commit is the primary action of the tab — Confirm style, as Prepare accents its primary.
auto* commit = styled_btn(_L("Commit to Plate"), ButtonStyle::Confirm);
commit->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_commit(); });
root->Add(commit, 0, wxLEFT | wxRIGHT | wxTOP, 12);
root->Add(commit, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, cm);
auto* export_step = new wxButton(m_form, wxID_ANY, _L("Export STEP…"));
auto* export_step = styled_btn(_L("Export STEP…"));
export_step->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_export_step(); });
root->Add(export_step, 0, wxALL, 12);
root->Add(export_step, 0, wxEXPAND | wxALL, cm);
m_shape->Bind(wxEVT_CHOICE, [this](wxCommandEvent& e) { on_shape_changed(); e.Skip(); });
on_shape_changed();
@@ -3292,9 +3321,25 @@ void DesignPanel::on_tab_shown()
}
}
update_reference_planes(); // entering the Design tab: show the XY/XZ/YZ planes if no object yet
sync_sidebar_width(); // keep the panel as wide as Prepare's so the canvas edge doesn't jump
if (m_viewport) m_viewport->force_repaint(); // the page was just re-shown: paint it for real
}
// Match Prepare's sidebar width instead of hardcoding one. Design used a fixed 264 px against
// Prepare's ~467, so the canvas edge jumped sideways on every tab switch; reading the live width
// also means the two stay aligned if Orca ever changes its sidebar.
void DesignPanel::sync_sidebar_width()
{
if (m_form == nullptr) return;
Plater* pl = wxGetApp().plater();
if (pl == nullptr) return;
const int w = pl->sidebar().GetSize().GetWidth();
if (w < 200) return; // sidebar not laid out yet — keep what we have
if (m_form->GetMinSize().GetWidth() == w) return;
m_form->SetMinSize(wxSize(w, -1));
Layout();
}
void DesignPanel::load_recipe(const std::string& blob)
{
if (blob.empty()) return;
@@ -3373,6 +3418,8 @@ void DesignPanel::refresh_parts()
const bool any = !m_tree_body_items.empty();
m_parts->Show(any);
if (m_parts_label) m_parts_label->Show(any);
if (m_parts_hdr) m_parts_hdr->ShowItems(any); // icon + title live in this sizer
if (m_parts_rule) m_parts_rule->Show(any);
if (any) {
const int rowH = std::max(m_parts->GetCharHeight() + 8, 20);
+8 -1
View File
@@ -20,6 +20,8 @@ class wxSpinCtrlDouble;
class wxTreeCtrl;
class wxImageList;
class wxStaticText;
class wxStaticLine;
class Button; // Orca-styled button (Widgets/Button.hpp)
class wxSizer;
class wxButton;
class wxPanel;
@@ -427,6 +429,10 @@ private:
wxTreeCtrl* m_tree{nullptr};
wxTreeCtrl* m_parts{nullptr}; // Bodies list under the feature tree
wxStaticText* m_parts_label{nullptr}; // its "Bodies" caption (hidden when empty)
wxBoxSizer* m_parts_hdr{nullptr}; // Bodies card header (icon + title)
wxStaticLine* m_parts_rule{nullptr}; // rule under that header
wxBoxSizer* m_hdr_tree_row{nullptr}; // Feature tree header: title + row actions
wxStaticText* m_hdr_tree{nullptr}; // its title label
wxImageList* m_tree_images{nullptr};
std::vector<wxTreeItemId> m_tree_items;
// Parts list: tree rows for each body (parallel to m_doc.bodies). Selecting one
@@ -442,7 +448,7 @@ private:
bool m_section_on{false};
double m_section_cut_z{0.0};
bool m_section_upper{false}; // false = keep lower half, true = upper
wxButton* m_section_flip_btn{nullptr}; // enabled only while the section is on
Button* m_section_flip_btn{nullptr}; // enabled only while the section is on
void toggle_section_view(); // Section View button / X: on <-> off
void flip_section_view(); // Flip button / F: opposite half
void update_section_flip_btn(); // enable the Flip button iff the section is on
@@ -464,6 +470,7 @@ private:
int tree_selection() const; // selected feature row, or wxNOT_FOUND
int tree_body_selection() const; // selected Parts-list body index, or -1
void refresh_parts(); // rebuild the Bodies list under the feature tree
void sync_sidebar_width(); // keep the panel as wide as Prepare's sidebar
void set_tree_selection(int row);
static int tree_icon_for(CadFeatureType t);