diff --git a/.claude/skills/orca-wxwidgets/references/orca-architecture.md b/.claude/skills/orca-wxwidgets/references/orca-architecture.md index f7e046e108..29f3cb5caa 100644 --- a/.claude/skills/orca-wxwidgets/references/orca-architecture.md +++ b/.claude/skills/orca-wxwidgets/references/orca-architecture.md @@ -617,8 +617,10 @@ Spacing constants come from `SidebarProps` (`Plater.hpp`): `TitlebarMargin()`, ` ### Docking -`Plater::priv` owns `AuiMgr m_aui_mgr` (a `wxAuiManager` subclass whose `CreateFloatingFrame` returns a -themed `FloatFrame : wxAuiFloatingFrame`), managing the plater. Panes: `"sidebar"` (left, no close +Docks are `AuiMgr`s (`AuiMgr.hpp`), a `wxAuiManager` subclass carrying Orca's dock art, theme and +Wayland rule (`references/webview-gl-aui-media.md`). `Plater::priv` owns `m_aui_mgr`, managing the +plater; the Design tab owns its own for its sidebar (`DesignPanel::m_aui`, layout in +`design_window_layout`). Plater panes: `"sidebar"` (left, no close button, not top/bottom dockable), `"main"` (`CenterPane()`, the `panel_3d`), `"uv_editor"` (right, hidden until the texture-displacement gizmo shows it), plus dynamic dock panes. The default perspective is saved right after `AddPane`; the app-config `window_layout` is applied with diff --git a/.claude/skills/orca-wxwidgets/references/webview-gl-aui-media.md b/.claude/skills/orca-wxwidgets/references/webview-gl-aui-media.md index 9ad1f9a4b2..0e6df9f63f 100644 --- a/.claude/skills/orca-wxwidgets/references/webview-gl-aui-media.md +++ b/.claude/skills/orca-wxwidgets/references/webview-gl-aui-media.md @@ -953,11 +953,12 @@ widgets beside the canvas (a sibling, not a child over it), or in a popup. - `wxAuiNotebook` (not used by Orca): page indices are logical; use `GetPagePosition()` for on-screen order (`docs/changes.txt:121-126`); the default tab art changed (`35-37`). -**OrcaSlicer.** `Plater::priv` owns `AuiMgr m_aui_mgr`, a `wxAuiManager` whose `CreateFloatingFrame` -returns `FloatFrame : wxAuiFloatingFrame` (applies `UpdateFrameDarkUI`). -- Setup: `SetManagedWindow(q)` (the Plater), `SetDockSizeConstraint(1, 1)`; on Wayland clears - `wxAUI_MGR_ALLOW_FLOATING` (`disable_wayland_floating`); dock-art metrics set here, colours in the - dark-mode update (`Plater::priv::apply_color_mode`). +**OrcaSlicer.** Orca's docks use `AuiMgr` (`AuiMgr.hpp`), a `wxAuiManager` whose `CreateFloatingFrame` +returns a `wxAuiFloatingFrame` that applies `UpdateFrameDarkUI`. `init(window)` calls +`SetManagedWindow(window)` and `SetDockSizeConstraint(1, 1)`, clears `wxAUI_MGR_ALLOW_FLOATING` on +Wayland and sets the dock-art metrics and colours; `apply_color_mode()` re-colours captions, sashes and +borders and is called from the owner's theme-switch handler. `Plater::priv::m_aui_mgr` manages the +Plater (Prepare and Preview); `DesignPanel::m_aui` manages the Design tab's body below its toolbar. - Panes: `"sidebar"` (left, no close button, not top/bottom dockable, `BestSize` in em units), `"uv_editor"` (right, hidden; forced hidden again after a layout load), `"main"` (`CenterPane().PaneBorder(false)`, holding `panel_3d` with View3D, Preview and AssembleView in one @@ -966,8 +967,8 @@ returns `FloatFrame : wxAuiFloatingFrame` (applies `UpdateFrameDarkUI`). `window_layout` app-config string goes through `sanitize_window_layout_for_wayland` (strips floating/floatable bits on Wayland) and `LoadPerspective(layout, false)`, falling back to the default on failure; then one `Update()`. `Plater::priv::reset` saves `SavePerspective()` back to - `window_layout`. An idle handler copies the docked sidebar width into `BestSize` (wxAUI does not record - a dragged sash there), so the width persists. `Plater::priv::reset_window_layout` is the model for + `window_layout`. `AuiMgr::track_docked_size` copies a docked pane's size into `BestSize` on idle + (wxAUI does not record a dragged sash there), so the width persists. `Plater::priv::reset_window_layout` is the model for re-establishing panes after a load: it loads the default perspective, then re-shows each plugin pane the perspective does not list. - Plugin panes: `Plater::add_dock_pane(window, name, caption, dock, size, on_close)` (window must be a @@ -977,13 +978,27 @@ returns `FloatFrame : wxAuiFloatingFrame` (applies `UpdateFrameDarkUI`). `Destroy()`s without running `on_close`; `show_dock_pane` toggles and updates. Pane names come from `plugin_pane_name` (`DockPanel.cpp`), which replaces `|`, `;`, `=`, `\` — the perspective-string delimiters. +- Other pages: a page outside the Plater docks through its own `AuiMgr` member, since the Plater's + manages only the Plater. Its managed window is a body panel below the page's toolbar; a sidebar + takes `AuiMgr::sidebar_pane_info()`; the layout has its own app-config key, is reset from + `Plater::reset_window_layout`, and is saved by a `shutdown()` called from `MainFrame::shutdown`, + which also detaches a floating pane (Lifetime below). Where floating is disabled it forces + `Dock().Floatable(false)` after loading, as plugin panes do. `DesignPanel` is the model + (`docs/HLSD/design-tab.md`). A `GLCanvas3D` beside such a sidebar gets that sidebar's collapse + button with `set_collapse_toolbar`; without it the canvas falls back to the Plater's button, which + collapses Prepare's sidebar. - Lifetime: no explicit `UnInit`. `m_aui_mgr` is a `Plater::priv` member, so it is destroyed in `~Plater`, before `~wxWindow` destroys the Plater's children (`wxWindowBase::DestroyChildren`, `src/common/wincmn.cpp:586-609`); a floating frame is such a child (`CreateFloatingFrame(m_frame, …)`, `src/aui/framemanager.cpp:3151`), so a pane still floating at that point outlives the manager [source]. - Plugin panes are removed earlier (`MainFrame::shutdown` → `Plater::remove_dock_panes`). The sidebar - `GetPane()` reference captured by the idle lambda is safe across `AddPane`; never `DetachPane` a pane - whose reference is captured. + Plugin panes are removed earlier (`MainFrame::shutdown` → `Plater::remove_dock_panes`). A handler + that can outlive its pane looks the pane up when it runs, as `track_docked_size` does: an idle + handler bound on the pane's window outlives the `DetachPane` in `DesignPanel::shutdown`. Never detach + a pane whose `GetPane()` reference is captured (Pane references above). `DesignPanel::shutdown` is + the model for a member manager: it detaches a + floating sidebar, which `Destroy()`s the frame into the pending-delete list, and the main frame's + deletion deletes pending top-level children before its own children (`~wxTopLevelWindowBase`, + `src/common/toplvcmn.cpp:67-93`), while the panel and its manager are still alive [source]. **Pitfalls.** - **Rule:** Call `Update()` once after a batch of `AddPane`/`wxAuiPaneInfo` changes. diff --git a/.claude/skills/orca-wxwidgets/references/wx-33-changes.md b/.claude/skills/orca-wxwidgets/references/wx-33-changes.md index 4a292d89af..3af66805c9 100644 --- a/.claude/skills/orca-wxwidgets/references/wx-33-changes.md +++ b/.claude/skills/orca-wxwidgets/references/wx-33-changes.md @@ -198,7 +198,7 @@ change, its line, and what it means for Orca. | `wxAuiGenericTabArt` subclasses (also via `wxAuiMSWTabArt`) override `DrawPageTab()`/`GetPageTabSize()` instead of `DrawTab()`/`GetTabSize()`; direct `wxAuiTabArt` subclasses still work | :115-120 | none: no Orca tab art | | `wxAuiNotebook` page index is logical (reorder-independent); `GetPagePosition()` gives the screen position | :122-126 | none; index math under `wxAUI_NB_TAB_MOVE` is what breaks | | `wxListbook`/`wxChoicebook` interpret mnemonics in page titles "just as the other wx*book classes already did" | :128-130 | Orca uses neither (`BedShapeDialog` uses `wxSimplebook` + a combo); every book interprets `&` (`interface/wx/bookctrl.h:141-147`) → rule 11 | -| `wxAUI_MGR_HINT_FADE` is not in the default `wxAuiManager` style | :132-133 | Plater's `AuiMgr` keeps the default flags (minus `wxAUI_MGR_ALLOW_FLOATING` on Wayland, `Plater::priv::priv`), so the docking hint no longer fades; add the flag if wanted | +| `wxAUI_MGR_HINT_FADE` is not in the default `wxAuiManager` style | :132-133 | Orca's `AuiMgr` keeps the default flags (minus `wxAUI_MGR_ALLOW_FLOATING` on Wayland, `AuiMgr::init`), so the docking hint no longer fades; add the flag if wanted | | `wxPrintDialogData::SetAllPages(false)`/`SetSelection(false)` changed meaning | :135-137 | none | | `wxGetTranslation()` returns `wxString` by value | :139-142 | pitfall below | | `wxWindow::Raise()` no longer shows a hidden window on any port | :144-146 | pitfall below; ba867cc534 | diff --git a/docs/HLSD/design-tab.md b/docs/HLSD/design-tab.md index 49a4e7256b..20299a51a0 100644 --- a/docs/HLSD/design-tab.md +++ b/docs/HLSD/design-tab.md @@ -204,6 +204,14 @@ The tab is a page of Orca's main window and answers to the same settings as Prep - **Undo.** The tab keeps its own history (the recipe is not part of Prepare's snapshots), but it has no Undo/Redo of its own: the top bar, `Ctrl+Z` and Edit drive it while the tab is shown, greyed to what an undo would actually do. +- **Docking.** The sidebar docks like Prepare's — either side, floating, resized, or collapsed + with the canvas's collapse button or `Shift+Tab` — through its own AUI manager under the + toolbar, because Prepare's manages the Plater and the Plater is not on this page. The button is + the canvas's own toolbar rather than Prepare's, which collapses Prepare's sidebar. The layout, + collapse included, is kept apart from Prepare's (`design_window_layout`) and starts where + Prepare's sidebar is, at its width, so the canvas edge holds still across the tab switch until + the user moves one of them. A floating sidebar is a top-level window, so it is hidden with the + tab rather than left over the other pages, and View > Reset Window Layout resets both tabs. ## The offer is generated, not hand-written diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 67c4080b56..9611c0fa68 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -147,6 +147,8 @@ set(SLIC3R_GUI_SOURCES GUI/WebPanel.hpp GUI/Widgets/WebHosting.cpp GUI/Widgets/WebHosting.hpp + GUI/AuiMgr.cpp + GUI/AuiMgr.hpp GUI/AuiPaneLayout.cpp GUI/AuiPaneLayout.hpp GUI/DragCanvas.cpp diff --git a/src/slic3r/GUI/AuiMgr.cpp b/src/slic3r/GUI/AuiMgr.cpp new file mode 100644 index 0000000000..d1c8fdb1ab --- /dev/null +++ b/src/slic3r/GUI/AuiMgr.cpp @@ -0,0 +1,80 @@ +#include "AuiMgr.hpp" + +#include "GUI_App.hpp" +#ifdef __WXGTK__ +#include "LinuxDisplayBackend.hpp" +#endif + +#include +#include + +namespace Slic3r { namespace GUI { + +namespace { + +// TODO: listen on dark ui change +class FloatFrame : public wxAuiFloatingFrame +{ +public: + FloatFrame(wxWindow* parent, wxAuiManager* ownerMgr, const wxAuiPaneInfo& pane) : wxAuiFloatingFrame(parent, ownerMgr, pane) + { + wxGetApp().UpdateFrameDarkUI(this); + } +}; + +} // namespace + +void AuiMgr::init(wxWindow* window) +{ + SetManagedWindow(window); + SetDockSizeConstraint(1, 1); +#ifdef __WXGTK__ + // Floating and its docking hints need global pointer positions and window moves, which Wayland does not provide. + if (is_running_on_wayland()) + SetFlags(GetFlags() & ~wxAUI_MGR_ALLOW_FLOATING); +#endif + GetArtProvider()->SetMetric(wxAUI_DOCKART_CAPTION_SIZE, 18); + GetArtProvider()->SetMetric(wxAUI_DOCKART_GRADIENT_TYPE, wxAUI_GRADIENT_NONE); + apply_color_mode(); +} + +void AuiMgr::apply_color_mode() +{ + const bool is_dark = wxGetApp().dark_mode(); + const wxColour sash_color = is_dark ? wxColour(38, 46, 48) : wxColour(206, 206, 206); + GetArtProvider()->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR, sash_color); + GetArtProvider()->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR, *wxWHITE); + GetArtProvider()->SetColour(wxAUI_DOCKART_SASH_COLOUR, sash_color); + GetArtProvider()->SetColour(wxAUI_DOCKART_BORDER_COLOUR, is_dark ? *wxBLACK : wxColour(165, 165, 165)); +} + +void AuiMgr::track_docked_size(wxWindow* window) +{ + window->Bind(wxEVT_IDLE, [this, window](wxIdleEvent& evt) { + wxAuiPaneInfo& pane = GetPane(window); + if (pane.IsOk() && pane.IsShown() && pane.IsDocked() && pane.rect.GetWidth() > 0 && pane.rect.GetHeight() > 0) { + const bool horizontal = pane.dock_direction == wxAUI_DOCK_TOP || pane.dock_direction == wxAUI_DOCK_BOTTOM; + pane.BestSize(horizontal ? pane.best_size.GetWidth() : pane.rect.GetWidth(), + horizontal ? pane.rect.GetHeight() : pane.best_size.GetHeight()); + } + evt.Skip(); + }); +} + +wxAuiPaneInfo AuiMgr::sidebar_pane_info() +{ + return wxAuiPaneInfo() + .Name("sidebar") + .Left() + .CloseButton(false) + .TopDockable(false) + .BottomDockable(false) + .BestSize(wxSize(39 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit())); +} + +wxAuiFloatingFrame* AuiMgr::CreateFloatingFrame(wxWindow* parent, const wxAuiPaneInfo& pane) +{ + return new FloatFrame(parent, this, pane); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/AuiMgr.hpp b/src/slic3r/GUI/AuiMgr.hpp new file mode 100644 index 0000000000..57b3950b93 --- /dev/null +++ b/src/slic3r/GUI/AuiMgr.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace Slic3r { namespace GUI { + +// The wxAuiManager behind Orca's docked sidebars: Orca's caption and sash art in both themes, +// floating frames that follow the app theme, and no floating on Wayland. +class AuiMgr : public wxAuiManager +{ +public: + // Manages `window`, whose own wxEVT_SIZE handlers must Skip(). + void init(wxWindow* window); + // Captions, sashes and borders in the current theme. + void apply_color_mode(); + // Keeps the docked size of `window`'s pane in its best_size, which re-docking and a saved layout + // use: wxAUI does not record a dragged sash there. + void track_docked_size(wxWindow* window); + + // A tab's sidebar pane: docked left by default, movable to the right or floating, no close button. + static wxAuiPaneInfo sidebar_pane_info(); + + wxAuiFloatingFrame* CreateFloatingFrame(wxWindow* parent, const wxAuiPaneInfo& pane) override; +}; + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/CAD/DesignCanvas.cpp b/src/slic3r/GUI/CAD/DesignCanvas.cpp index 5b1484c05c..adf1ce2a89 100644 --- a/src/slic3r/GUI/CAD/DesignCanvas.cpp +++ b/src/slic3r/GUI/CAD/DesignCanvas.cpp @@ -570,6 +570,21 @@ void DesignCanvas::set_show_bed(bool b) request_repaint(); } +void DesignCanvas::set_sidebar_collapse(std::function side, std::function toggle) +{ + if (!m_canvas || !m_canvas_widget || !setup_collapse_toolbar(m_collapse_toolbar, toggle)) + return; + m_canvas->set_collapse_toolbar(&m_collapse_toolbar, std::move(side)); + m_canvas->enable_collapse_toolbar(true); + // Shift+Tab: the canvas posts this for its sidebar, as Prepare's canvases do. + m_canvas_widget->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [toggle](SimpleEvent&) { toggle(); }); +} + +void DesignCanvas::set_sidebar_collapse_tooltip(const std::string& tooltip) +{ + m_collapse_toolbar.set_tooltip(m_collapse_toolbar.get_item_id("collapse_sidebar"), tooltip); +} + bool DesignCanvas::is_sketching() const { return m_sketch_tool.is_active(); } void DesignCanvas::cancel_sketch() diff --git a/src/slic3r/GUI/CAD/DesignCanvas.hpp b/src/slic3r/GUI/CAD/DesignCanvas.hpp index 940f6a7604..94cce664d7 100644 --- a/src/slic3r/GUI/CAD/DesignCanvas.hpp +++ b/src/slic3r/GUI/CAD/DesignCanvas.hpp @@ -17,6 +17,7 @@ #include "slic3r/GUI/3DBed.hpp" #include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/GLToolbar.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/CAD/SketchEngine.hpp" #include "slic3r/GUI/CAD/DesignSketchTool.hpp" @@ -87,6 +88,10 @@ public: void unbind_canvas_event_handlers(); // app close / language switch, from the plater's teardown void reset_canvas_volumes(); void set_show_bed(bool b); // view option: draw the printer bed + plate grid, or not + // The sidebar's collapse button, as Prepare's: drawn on the edge `side` reports, it runs + // `toggle` on a click and on Shift+Tab. Set before the first paint. + void set_sidebar_collapse(std::function side, std::function toggle); + void set_sidebar_collapse_tooltip(const std::string& tooltip); // N: look straight down the sketch plane's normal, keeping the current zoom. A sketch drawn // at an angle is a sketch drawn wrong, and no amount of orbiting by hand lands exactly square. bool view_normal_to_sketch(); @@ -368,6 +373,7 @@ private: wxGLCanvas* m_canvas_widget{nullptr}; GLCanvas3D* m_canvas{nullptr}; int m_sw_gl{-1}; // -1 unknown, 0 hardware GL, 1 software GL + GLToolbar m_collapse_toolbar{GLToolbar::Normal, "Collapse"}; std::function m_on_context_menu; bool m_ctx_bound{false}; // bind the RIGHT_UP handler once, however often the cb is set diff --git a/src/slic3r/GUI/CAD/DesignPanel.cpp b/src/slic3r/GUI/CAD/DesignPanel.cpp index 5b1b64be05..e4335e18e1 100644 --- a/src/slic3r/GUI/CAD/DesignPanel.cpp +++ b/src/slic3r/GUI/CAD/DesignPanel.cpp @@ -104,6 +104,7 @@ #include "libslic3r/BuildVolume.hpp" #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/Shortcuts.hpp" // English-only pin for the Design tab (see design-ux-contract): one lever // de-translates this whole TU so our strings never half-translate against the host's @@ -357,10 +358,12 @@ DesignPanel::DesignPanel(wxWindow* parent) // Left column: a slim feature-tree + docked tool-dialog column. All form // controls are parented to m_form so it can scroll independently of the // live GL viewport. The tool buttons live in the top toolbar (built below). - m_form = new wxScrolledWindow(this, wxID_ANY); + auto* body_panel = new wxPanel(this, wxID_ANY); // below the toolbar, managed by m_aui + m_form = new wxScrolledWindow(body_panel, wxID_ANY); // Explicit token background in both themes, so a theme switch can move it (an inherited // colour stays whatever the theme was when the panel was built). SetBackgroundColour(dp_panel_bg()); + body_panel->SetBackgroundColour(dp_panel_bg()); m_form->SetBackgroundColour(dp_panel_bg()); auto* root = new wxBoxSizer(wxVERTICAL); @@ -3521,7 +3524,9 @@ DesignPanel::DesignPanel(wxWindow* parent) build_phase("sidebar and tool cards"); // Right column: a small view toolbar over the live 3D viewport that mirrors // the CadDocument body. - m_viewport = new DesignCanvas(this); + auto* view_col = new wxPanel(body_panel, wxID_ANY); // the centre pane: sketch banner over the viewport + view_col->SetBackgroundColour(dp_panel_bg()); + m_viewport = new DesignCanvas(view_col); build_phase("3D canvas"); m_viewport->set_on_sketch_commit([this](const SketchProfile& prof, const SketchPlane& plane) { @@ -4339,7 +4344,7 @@ DesignPanel::DesignPanel(wxWindow* parent) // The sketch banner sits ABOVE the viewport rather than floating inside it: a child window // over a wxGLCanvas is a platform argument (it is a native window on GTK and does not // reliably stack over GL), and the banner's job is to be unmissable, not to be clever. - m_sketch_banner = new wxPanel(this, wxID_ANY); + m_sketch_banner = new wxPanel(view_col, wxID_ANY); m_sketch_banner->SetBackgroundColour(wxColour(0, 122, 116)); // Orca teal: not a plate colour m_sketch_banner_txt = new wxStaticText(m_sketch_banner, wxID_ANY, wxString()); m_sketch_banner_txt->SetForegroundColour(*wxWHITE); @@ -4356,16 +4361,33 @@ DesignPanel::DesignPanel(wxWindow* parent) // The bottom 3D-navigator orb handles all view orientation, so no separate view buttons. // Fit view is a double-click on the viewport (the tool intercepts it -> zoom_to_volumes). vcol->Add(m_viewport, 1, wxEXPAND); + view_col->SetSizer(vcol); - // Onshape layout: top toolbar over [ slim left column | center viewport ]. - auto* body = new wxBoxSizer(wxHORIZONTAL); - body->Add(m_form, 0, wxEXPAND); - body->Add(vcol, 1, wxEXPAND); + // Onshape layout: top toolbar over [ slim left column | center viewport ], with the column + // docked like Prepare's sidebar: left by default, movable to the right or floating, resizable + // and collapsible. + m_aui.init(body_panel); + m_aui.AddPane(m_form, AuiMgr::sidebar_pane_info().MinSize(m_form->GetMinSize())); + m_aui.AddPane(view_col, wxAuiPaneInfo().Name("main").CenterPane().PaneBorder(false)); + m_default_layout = m_aui.SavePerspective(); + load_window_layout(); + m_aui.track_docked_size(m_form); + // The collapse button sits on the canvas edge the sidebar is docked on, and is gone while it + // floats. A click arrives inside the canvas's mouse handler, so the relayout waits for it. + m_viewport->set_sidebar_collapse( + [this] { + const wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form); + if (!sidebar.IsOk() || sidebar.IsFloating()) + return CollapseSide::None; + return sidebar.dock_direction == wxAUI_DOCK_RIGHT ? CollapseSide::Right : CollapseSide::Left; + }, + [this] { CallAfter([this] { collapse_sidebar(!m_sidebar_collapsed); }); }); + collapse_sidebar(m_sidebar_collapsed); // the button's tooltip auto* outer = new wxBoxSizer(wxVERTICAL); outer->Add(m_toolbar, 0, wxEXPAND); outer->Add(new wxStaticLine(this, wxID_ANY), 0, wxEXPAND); - outer->Add(body, 1, wxEXPAND); + outer->Add(body_panel, 1, wxEXPAND); SetSizer(outer); // The atlas says a verb is wired; the registrations above say what it runs. Nothing checks @@ -7215,6 +7237,7 @@ void DesignPanel::on_tab_hidden() { if (m_viewport) m_viewport->leave_viewport(); // hand the shared camera back to the editor tabs + update_sidebar_pane(); } void DesignPanel::on_tab_shown() @@ -7232,7 +7255,7 @@ void DesignPanel::on_tab_shown() update_reference_planes(); // entering the Design tab: show the XY/XZ/YZ planes if no object yet if (show_clock.Time() > 100) // a slow first show is what users report; the usual one is not news BOOST_LOG_TRIVIAL(info) << "Design tab shown: bed, project recipe and planes in " << show_clock.Time() << " ms"; - sync_sidebar_width(); // keep the panel as wide as Prepare's so the canvas edge doesn't jump + update_sidebar_pane(); if (m_viewport) m_viewport->force_repaint(); // the page was just re-shown: paint it for real } @@ -7267,6 +7290,7 @@ void DesignPanel::msw_rescale() void DesignPanel::on_sys_color_changed() { + m_aui.apply_color_mode(); // Every chrome colour here was set from a DpToken in the theme that was current at the time. // Move each one, background and text, onto the same token in the new theme; any other colour // (the teal accents, the status colours) is the same in both and stays. @@ -7325,19 +7349,83 @@ void DesignPanel::reset_canvas_volumes() if (m_viewport) m_viewport->reset_canvas_volumes(); } -// 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() +void DesignPanel::shutdown() { - 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(); + wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form); + if (!sidebar.IsOk()) // shut down already, floating: a layout saved now would lack the sidebar + return; + // Saved hidden only when collapsed, whichever tab is shown. + sidebar.Show(!m_sidebar_collapsed); + wxGetApp().app_config->set("design_window_layout", m_aui.SavePerspective().utf8_string()); + // A floating frame is a child of the managed panel and dereferences its manager when it is + // deleted, so it must not outlive m_aui, a member. Detaching queues the frame for deletion, which + // runs before the main frame destroys its children (~wxTopLevelWindowBase deletes pending child + // frames). + if (sidebar.IsFloating()) + m_aui.DetachPane(m_form); +} + +void DesignPanel::reset_window_layout() +{ + load_default_layout(); + collapse_sidebar(false); + update_sidebar_pane(true); +} + +// The sidebar where the user left it, collapsed if it was. +void DesignPanel::load_window_layout() +{ + const wxString saved = wxString::FromUTF8(wxGetApp().app_config->get("design_window_layout")); + if (saved.empty() || !m_aui.LoadPerspective(saved, false)) { + if (!saved.empty()) + BOOST_LOG_TRIVIAL(warning) << "Design tab: failed to restore the saved window layout"; + load_default_layout(); + } + wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form); + m_sidebar_collapsed = !sidebar.IsShown(); + if ((m_aui.GetFlags() & wxAUI_MGR_ALLOW_FLOATING) == 0) // saved where windows can float + sidebar.Dock().Floatable(false); + update_sidebar_pane(true); +} + +// Docked where Prepare's sidebar is, and as wide, so the canvas edge does not move between the tabs. +void DesignPanel::load_default_layout() +{ + m_aui.LoadPerspective(m_default_layout, false); + Plater* plater = wxGetApp().plater(); + const Sidebar::DockingState prepare = plater != nullptr ? plater->get_sidebar_docking_state() : Sidebar::None; + if (prepare == Sidebar::None) + return; + wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form); + if (prepare == Sidebar::Right) + sidebar.Right(); + const int width = plater->sidebar().GetSize().GetWidth(); + if (width >= sidebar.min_size.GetWidth()) // Prepare's sidebar has been laid out + sidebar.BestSize(width, sidebar.best_size.GetHeight()); +} + +// The collapse button on the canvas and Shift+Tab, as in Prepare. +void DesignPanel::collapse_sidebar(bool collapse) +{ + m_sidebar_collapsed = collapse; + if (m_viewport) + m_viewport->set_sidebar_collapse_tooltip(wxGetApp().shortcuts().with_key( + (collapse ? _L("Expand sidebar") : _L("Collapse sidebar")).utf8_string(), Shortcut::CollapseSidebar)); + update_sidebar_pane(); +} + +// The sidebar shows unless collapsed; a floating one also only while the tab is shown, since it is +// a top-level window and does not hide with the page. +void DesignPanel::update_sidebar_pane(bool force_update) +{ + wxAuiPaneInfo& sidebar = m_aui.GetPane(m_form); + const bool show = !m_sidebar_collapsed && (sidebar.IsDocked() || IsShownOnScreen()); + if (sidebar.IsOk() && sidebar.IsShown() != show) { + sidebar.Show(show); + force_update = true; + } + if (force_update) + m_aui.Update(); } void DesignPanel::load_recipe(const std::string& blob) diff --git a/src/slic3r/GUI/CAD/DesignPanel.hpp b/src/slic3r/GUI/CAD/DesignPanel.hpp index 24c0f48796..4c7bc643ac 100644 --- a/src/slic3r/GUI/CAD/DesignPanel.hpp +++ b/src/slic3r/GUI/CAD/DesignPanel.hpp @@ -21,6 +21,7 @@ #include "libslic3r/CAD/CadDocument.hpp" #include "slic3r/GUI/CAD/DesignInteraction.hpp" // CadLevel: what one Esc press means +#include "slic3r/GUI/AuiMgr.hpp" #include "slic3r/GUI/Lazy.hpp" class ComboBox; // Orca dropdown (Widgets/ComboBox.hpp) — replaces wxChoice everywhere here @@ -70,6 +71,8 @@ public: void on_sys_color_changed(); void unbind_canvas_event_handlers(); // app close / language switch, from the plater's teardown void reset_canvas_volumes(); + void shutdown(); // app close / language switch, from MainFrame: save the window layout + void reset_window_layout(); // View > Reset Window Layout, with Prepare's void clear_document(); // New Project / Open Project: drop the document with the project // Rebuild off the UI thread (progress dialog only if it turns out to be slow), so a feature // op on a heavy imported solid does not freeze the window. Returns m_doc.recompute()'s result. @@ -497,6 +500,17 @@ private: wxScrolledWindow* m_form{nullptr}; DesignCanvas* m_viewport{nullptr}; + // Below the toolbar, m_form docks beside the viewport column like Prepare's sidebar: it can + // move to the other side, float, be resized and be collapsed, and the layout is kept between + // sessions. + AuiMgr m_aui; + wxString m_default_layout; + bool m_sidebar_collapsed{false}; + void load_window_layout(); + void load_default_layout(); + void collapse_sidebar(bool collapse); + void update_sidebar_pane(bool force_update = false); + // Top contextual toolbar (parented to the panel, above the form/viewport row). UiMode m_ui_mode{UiMode::Feature}; // Sketch environment banner: a strip across the top of the viewport saying, in words, that @@ -938,7 +952,6 @@ 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); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 050b2acfa3..ebb70d3589 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1440,7 +1440,7 @@ void GLCanvas3D::on_change_color_mode(bool is_dark, bool reinit) { // set dirty to re-generate icon texture m_separator_toolbar.set_icon_dirty(); m_main_toolbar.set_icon_dirty(); - wxGetApp().plater()->get_collapse_toolbar().set_icon_dirty(); + collapse_toolbar().set_icon_dirty(); m_assemble_view_toolbar.set_icon_dirty(); m_gizmos.set_icon_dirty(); } @@ -1972,6 +1972,12 @@ void GLCanvas3D::enable_collapse_toolbar(bool enable) m_collapse_toolbar_enabled = enable; } +void GLCanvas3D::set_collapse_toolbar(GLToolbar* toolbar, std::function side) +{ + m_collapse_toolbar = toolbar; + m_collapse_side = std::move(side); +} + void GLCanvas3D::enable_plate_chrome(bool enable) { m_plate_chrome_enabled = enable; @@ -2052,24 +2058,33 @@ void GLCanvas3D::update_volumes_colors_by_extruder() bool GLCanvas3D::is_collapse_toolbar_on_left() const { - auto state = wxGetApp().plater()->get_sidebar_docking_state(); - return state == Sidebar::Left; + return collapse_side() == CollapseSide::Left; } float GLCanvas3D::get_collapse_toolbar_width() const { - GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - const auto state = wxGetApp().plater()->get_sidebar_docking_state(); - - return state != Sidebar::None ? collapse_toolbar.get_width() : 0; + return collapse_side() != CollapseSide::None ? collapse_toolbar().get_width() : 0; } float GLCanvas3D::get_collapse_toolbar_height() const { - GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - const auto state = wxGetApp().plater()->get_sidebar_docking_state(); + return collapse_side() != CollapseSide::None ? collapse_toolbar().get_height() : 0; +} - return state != Sidebar::None ? collapse_toolbar.get_height() : 0; +GLToolbar& GLCanvas3D::collapse_toolbar() const +{ + return m_collapse_toolbar != nullptr ? *m_collapse_toolbar : wxGetApp().plater()->get_collapse_toolbar(); +} + +CollapseSide GLCanvas3D::collapse_side() const +{ + if (m_collapse_side) + return m_collapse_side(); + switch (wxGetApp().plater()->get_sidebar_docking_state()) { + case Sidebar::Left: return CollapseSide::Left; + case Sidebar::Right: return CollapseSide::Right; + default: return CollapseSide::None; + } } bool GLCanvas3D::make_current_for_postinit() { @@ -2274,7 +2289,7 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init) tooltip = m_assemble_view_toolbar.get_tooltip(); if (tooltip.empty()) - tooltip = wxGetApp().plater()->get_collapse_toolbar().get_tooltip(); + tooltip = collapse_toolbar().get_tooltip(); // BBS #if 0 @@ -3475,7 +3490,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_overlay_dirty |= m_assemble_view_toolbar.update_items_state(); // BBS //m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state(); - m_overlay_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state(); + m_overlay_dirty |= collapse_toolbar().update_items_state(); // apply() DRAINS the 3D-mouse queue, so only the canvas actually on screen may call it: a // hidden canvas renders nothing, so the motion it swallowed moves the shared camera without // ever being drawn and the next visible frame jumps several states at once. @@ -4267,6 +4282,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // (drag/orbit/wheel) so the camera keeps working over the display-only plate. #ifdef SLIC3R_CAD if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display()) { + // Except over the sidebar collapse button, which is drawn over the sketch. + if (collapse_toolbar().on_mouse(evt, *this)) { + if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) + mouse_up_cleanup(); + m_mouse.set_start_position_3D_as_invalid(); + return; + } if (evt.LeftDown() && m_canvas != nullptr) m_canvas->SetFocus(); // grab keyboard focus so Delete/keys reach this canvas if (m_design_sketch_tool->on_mouse(evt, *this)) { @@ -4315,7 +4337,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) return; } - if (!mouse_in_layer_editing && wxGetApp().plater()->get_collapse_toolbar().on_mouse(evt, *this)) { + if (!mouse_in_layer_editing && collapse_toolbar().on_mouse(evt, *this)) { if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); m_mouse.set_start_position_3D_as_invalid(); @@ -6892,7 +6914,7 @@ void GLCanvas3D::_switch_toolbars_icon_filename() m_main_toolbar.init(background_data); m_assemble_view_toolbar.init(background_data); m_separator_toolbar.init(background_data); - wxGetApp().plater()->get_collapse_toolbar().init(background_data); + collapse_toolbar().init(background_data); // main toolbar { @@ -7320,7 +7342,17 @@ bool GLCanvas3D::_init_view_toolbar() bool GLCanvas3D::_init_collapse_toolbar() { - return wxGetApp().plater()->init_collapse_toolbar(); + if (m_collapse_toolbar == nullptr) + return wxGetApp().plater()->init_collapse_toolbar(); + + // Its owner laid it out; the background needs the GL context. + BackgroundTexture::Metadata background_data; + background_data.filename = m_is_dark ? "toolbar_background_dark.png" : "toolbar_background.png"; + background_data.left = 16; + background_data.top = 16; + background_data.right = 16; + background_data.bottom = 16; + return m_collapse_toolbar->init(background_data); } bool GLCanvas3D::_set_current() @@ -9085,11 +9117,12 @@ void GLCanvas3D::_render_sequential_clearance() void GLCanvas3D::_check_and_update_toolbar_icon_scale() { // Update collapse toolbar - GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - collapse_toolbar.set_enabled(wxGetApp().plater()->get_sidebar_docking_state() != Sidebar::None); + GLToolbar& collapse_toolbar = this->collapse_toolbar(); + collapse_toolbar.set_enabled(collapse_side() != CollapseSide::None); - // Don't update a toolbar scale, when we are on a Preview - if (wxGetApp().plater()->is_preview_shown()) { + // Don't update a toolbar scale, when we are on a Preview. Asked of this canvas, not of the + // plater: the Design tab's canvas is on screen while the plater's current view may be Preview. + if (m_canvas_type == CanvasPreview) { IMSlider *m_layers_slider = get_gcode_viewer().get_layers_slider(); IMSlider *m_moves_slider = get_gcode_viewer().get_moves_slider(); float sc = get_scale(); @@ -9161,7 +9194,9 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() // set minimum scale as a auto scale for the toolbars float new_scale = std::min(new_h_scale, new_v_scale); new_scale /= get_scale(); - if (fabs(new_scale - scale) > 0.05) // scale is changed by 5% and more + // Only a canvas with toolbars to fit sets the shared size: a collapse button alone (the Design + // tab's canvas) always fits at the largest. + if (items_cnt > collapse_toolbar.get_visible_items_cnt() && fabs(new_scale - scale) > 0.05) // scale is changed by 5% and more wxGetApp().set_auto_toolbar_icon_scale(new_scale); } @@ -9243,7 +9278,7 @@ size_t GLCanvas3D::_overlay_signature(const ImDrawData* draw_data) const size_t hash = ImGuiWrapper::draw_data_signature(draw_data); for (size_t state_hash : { m_main_toolbar.get_state_hash(), m_separator_toolbar.get_state_hash(), m_assemble_view_toolbar.get_state_hash(), - wxGetApp().plater()->get_collapse_toolbar().get_state_hash(), + collapse_toolbar().get_state_hash(), m_gizmos.get_overlay_state_hash() }) boost::hash_combine(hash, state_hash); return hash; @@ -10298,18 +10333,17 @@ void GLCanvas3D::_render_collapse_toolbar() const if (!m_collapse_toolbar_enabled) return; - auto& plater = *wxGetApp().plater(); - const auto sidebar_docking_dir = plater.get_sidebar_docking_state(); - if (sidebar_docking_dir == Sidebar::None) { + const CollapseSide side = collapse_side(); + if (side == CollapseSide::None) { return; } - GLToolbar& collapse_toolbar = plater.get_collapse_toolbar(); + GLToolbar& collapse_toolbar = this->collapse_toolbar(); const Size cnv_size = get_canvas_size(); const float top = 0.5f * (float)cnv_size.get_height(); - const float left = sidebar_docking_dir == Sidebar::Right ? 0.5f * (float) cnv_size.get_width() - (float) collapse_toolbar.get_width() : - -0.5f * (float) cnv_size.get_width(); + const float left = side == CollapseSide::Right ? 0.5f * (float) cnv_size.get_width() - (float) collapse_toolbar.get_width() : + -0.5f * (float) cnv_size.get_width(); collapse_toolbar.set_position(top, left); collapse_toolbar.render(*this); @@ -11705,7 +11739,7 @@ bool GLCanvas3D::_deactivate_layersediting_menu() bool GLCanvas3D::_deactivate_collapse_toolbar_items() { - GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); + GLToolbar& collapse_toolbar = this->collapse_toolbar(); if (collapse_toolbar.is_item_pressed("print")) { collapse_toolbar.force_left_action(collapse_toolbar.get_item_id("print"), *this); return true; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index c844b7ede2..047ae56d9b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -20,6 +20,7 @@ #include "libslic3r/Line.hpp" #include "slic3r/GUI/Gizmos/GLGizmoBase.hpp" #include +#include #include #include #include @@ -594,6 +595,9 @@ private: mutable float m_sc{1}; mutable float m_paint_toolbar_width; bool m_collapse_toolbar_enabled{true}; + // The collapse button of a sidebar other than Prepare's, from set_collapse_toolbar(). + GLToolbar* m_collapse_toolbar{nullptr}; + std::function m_collapse_side; bool m_plate_chrome_enabled{true}; // Design tab: render the world-axis triad at the bed centre (= modeling origin) instead of // the bed corner. Default false preserves the main editor's corner triad. @@ -1008,6 +1012,10 @@ public: void enable_return_toolbar(bool enable); void enable_separator_toolbar(bool enable); void enable_collapse_toolbar(bool enable); + // A canvas beside a sidebar other than Prepare's shows that sidebar's collapse button: `toolbar`, + // set up with setup_collapse_toolbar(), on the edge `side` reports. Call before the canvas is + // initialized, which loads the toolbar's background. + void set_collapse_toolbar(GLToolbar* toolbar, std::function side); void enable_plate_chrome(bool enable); void set_axes_at_bed_center(bool b) { m_axes_at_bed_center = b; } void set_show_bed(bool b) { m_show_bed = b; } @@ -1374,6 +1382,8 @@ private: // BBS //bool _init_view_toolbar(); bool _init_collapse_toolbar(); + GLToolbar& collapse_toolbar() const; + CollapseSide collapse_side() const; bool _set_current(); bool _set_shown_canvas_current(); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 91afb33370..5f4c8335a5 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -1672,5 +1672,27 @@ float GLToolbar::get_scaled_icon_size() return m_layout.icons_size * m_layout.scale; } +bool setup_collapse_toolbar(GLToolbar& toolbar, std::function toggle) +{ + toolbar.set_layout_type(GLToolbar::Layout::Vertical); + toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Right); + toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top); + toolbar.set_border(4.0f); + toolbar.set_separator_size(4); + toolbar.set_gap_size(2); + + toolbar.del_all_item(); + + GLToolbarItem::Data item; + + item.name = "collapse_sidebar"; + // set collapse svg name + item.icon_filename = "collapse.svg"; + item.sprite_id = 0; + item.left.action_callback = std::move(toggle); + + return toolbar.add_item(item); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 192af7e50d..571f75a35c 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -457,6 +457,14 @@ private: bool update_items_enabled_state(); }; +// The canvas edge a sidebar collapse button sits on: the side its sidebar is docked on, or none +// while that sidebar floats. +enum class CollapseSide { None, Left, Right }; + +// Lays `toolbar` out as a sidebar collapse button that runs `toggle`: Prepare's, and those of +// other sidebars (GLCanvas3D::set_collapse_toolbar()). +bool setup_collapse_toolbar(GLToolbar& toolbar, std::function toggle); + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index a63e5c6443..08aaa2c636 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1249,6 +1249,10 @@ void MainFrame::shutdown() m_plugin_pages.shutdown(); if (m_plater != nullptr) m_plater->remove_dock_panes(); +#ifdef SLIC3R_CAD + if (DesignPanel* design = DesignPanel::if_built()) + design->shutdown(); +#endif #ifdef __WXGTK__ // Edge panels are child windows — wxWidgets destroys them automatically. m_edge_bottom = nullptr; @@ -3377,6 +3381,10 @@ void MainFrame::init_menubar_as_editor() viewMenu, wxID_ANY, _L("Reset Window Layout"), _L("Reset to default window layout"), [this](wxCommandEvent&) { m_plater->reset_window_layout(); }, "", this, [this]() { +#ifdef SLIC3R_CAD + if (shown_design_panel() != nullptr) + return true; +#endif return is_prepare_or_preview_tab() && m_plater->is_sidebar_enabled(); }, this); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 625f1142bd..dd68e542e2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -191,6 +191,7 @@ #ifdef __WXGTK__ #include "LinuxDisplayBackend.hpp" #endif +#include "AuiMgr.hpp" #include "AuiPaneLayout.hpp" #include "GUI_Utils.hpp" #include "GUI_Factories.hpp" @@ -7111,28 +7112,6 @@ enum ExportingStatus{ EXPORTING_TO_LOCAL }; - -// TODO: listen on dark ui change -class FloatFrame : public wxAuiFloatingFrame -{ -public: - FloatFrame(wxWindow* parent, wxAuiManager* ownerMgr, const wxAuiPaneInfo& pane) : wxAuiFloatingFrame(parent, ownerMgr, pane) - { - wxGetApp().UpdateFrameDarkUI(this); - } -}; - -class AuiMgr : public wxAuiManager -{ -public: - AuiMgr() : wxAuiManager(){} - - virtual wxAuiFloatingFrame* CreateFloatingFrame(wxWindow* parent, const wxAuiPaneInfo& p) override - { - return new FloatFrame(parent, this, p); - } -}; - // Plater / private struct Plater::priv { @@ -7616,7 +7595,6 @@ struct Plater::priv //BBS: change dark/light mode void on_change_color_mode(SimpleEvent& evt); void on_apple_change_color_mode(wxSysColourChangedEvent& evt); - void apply_color_mode(); void on_update_geometry(Vec3dsEvent<2>&); void on_3dcanvas_mouse_dragging_started(SimpleEvent&); void on_3dcanvas_mouse_dragging_finished(SimpleEvent&); @@ -7829,20 +7807,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) { m_is_dark = wxGetApp().app_config->get("dark_color_mode") == "1"; -#ifdef __WXGTK__ - const bool disable_wayland_floating = Slic3r::GUI::is_running_on_wayland(); -#endif - - m_aui_mgr.SetManagedWindow(q); - m_aui_mgr.SetDockSizeConstraint(1, 1); -#ifdef __WXGTK__ - if (disable_wayland_floating) - m_aui_mgr.SetFlags(m_aui_mgr.GetFlags() & ~wxAUI_MGR_ALLOW_FLOATING); -#endif - //m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE, 0); - //m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_SASH_SIZE, 2); - m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_CAPTION_SIZE, 18); - m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_GRADIENT_TYPE, wxAUI_GRADIENT_NONE); + m_aui_mgr.init(q); this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font()); @@ -7925,13 +7890,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) update(); // Orca: Make sidebar dockable - m_aui_mgr.AddPane(sidebar, wxAuiPaneInfo() - .Name("sidebar") - .Left() - .CloseButton(false) - .TopDockable(false) - .BottomDockable(false) - .BestSize(wxSize(39 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit()))); + m_aui_mgr.AddPane(sidebar, AuiMgr::sidebar_pane_info()); // UV editor pane for GLGizmoTextureDisplacement's LSCM unwrap preview - a resizable/dockable // sibling of "sidebar"/"main" like everything else registered on this same AUI manager, not a @@ -7983,8 +7942,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) if (!layout.empty()) { bool removed_floating_state = false; #ifdef __WXGTK__ - if (disable_wayland_floating) - layout = sanitize_window_layout_for_wayland(layout, &removed_floating_state); + layout = sanitize_window_layout_for_wayland(layout, &removed_floating_state); #endif if (!m_aui_mgr.LoadPerspective(layout, false)) { @@ -8006,12 +7964,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Keep tracking the current sidebar size, by storing it using `best_size`, which will be stored // in the config and re-applied when the app is opened again. - this->sidebar->Bind(wxEVT_IDLE, [&sidebar](wxIdleEvent& e) { - if (sidebar.IsShown() && sidebar.IsDocked() && sidebar.rect.GetWidth() > 0) { - sidebar.BestSize(sidebar.rect.GetWidth(), sidebar.best_size.GetHeight()); - } - e.Skip(); - }); + m_aui_mgr.track_docked_size(this->sidebar); // Hide sidebar initially, will re-show it after initialization when we got proper window size sidebar.Hide(); @@ -8236,8 +8189,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) q->SetDropTarget(new PlaterDropTarget(*main_frame, *q)); // if my understanding is right, wxWindow takes the owenership q->Layout(); - apply_color_mode(); - set_current_panel(wxGetApp().is_editor() ? static_cast(view3D) : static_cast(preview)); // updates camera type from .ini file @@ -8716,18 +8667,7 @@ void Plater::priv::add_dock_pane(wxWindow* window, const std::string& name, cons info.Show(dock_pane_visible(dock_pane, info)); m_aui_mgr.AddPane(window, info); - // wxAUI does not record a dragged sash in best_size, so track the docked size like the sidebar - // does, for the saved layout. - window->Bind(wxEVT_IDLE, [this, window](wxIdleEvent& evt) { - wxAuiPaneInfo& pane = m_aui_mgr.GetPane(window); - if (pane.IsOk() && pane.IsShown() && pane.IsDocked() && pane.rect.GetWidth() > 0 && pane.rect.GetHeight() > 0) { - const bool horizontal = pane.dock_direction == wxAUI_DOCK_TOP || pane.dock_direction == wxAUI_DOCK_BOTTOM; - pane.BestSize(horizontal ? pane.best_size.GetWidth() : pane.rect.GetWidth(), - horizontal ? pane.rect.GetHeight() : pane.best_size.GetHeight()); - } - evt.Skip(); - }); - + m_aui_mgr.track_docked_size(window); m_aui_mgr.Update(); } @@ -13908,7 +13848,7 @@ void Plater::priv::on_apple_change_color_mode(wxSysColourChangedEvent& evt) { assemble_view->get_canvas3d()->on_change_color_mode(m_is_dark); } - apply_color_mode(); + m_aui_mgr.apply_color_mode(); } void Plater::priv::on_change_color_mode(SimpleEvent& evt) { @@ -13919,19 +13859,7 @@ void Plater::priv::on_change_color_mode(SimpleEvent& evt) { assemble_view->get_canvas3d()->on_change_color_mode(m_is_dark); if (m_send_to_sdcard_dlg) m_send_to_sdcard_dlg->on_change_color_mode(); - apply_color_mode(); -} - -void Plater::priv::apply_color_mode() -{ - const bool is_dark = wxGetApp().dark_mode(); - wxColour orca_color = wxColour(59, 68, 70);//wxColour(ColorRGBA::ORCA().r_uchar(), ColorRGBA::ORCA().g_uchar(), ColorRGBA::ORCA().b_uchar()); - orca_color = is_dark ? StateColor::darkModeColorFor(orca_color) : StateColor::lightModeColorFor(orca_color); - wxColour sash_color = is_dark ? wxColour(38, 46, 48) : wxColour(206, 206, 206); - m_aui_mgr.GetArtProvider()->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR, sash_color); - m_aui_mgr.GetArtProvider()->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR, *wxWHITE); - m_aui_mgr.GetArtProvider()->SetColour(wxAUI_DOCKART_SASH_COLOUR, sash_color); - m_aui_mgr.GetArtProvider()->SetColour(wxAUI_DOCKART_BORDER_COLOUR, is_dark ? *wxBLACK : wxColour(165, 165, 165)); + m_aui_mgr.apply_color_mode(); } static void get_position(wxWindowBase* child, wxWindowBase* until_parent, int& x, int& y) { @@ -14592,26 +14520,9 @@ bool Plater::priv::init_collapse_toolbar() if (!collapse_toolbar.init(background_data)) return false; - collapse_toolbar.set_layout_type(GLToolbar::Layout::Vertical); - collapse_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Right); - collapse_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top); - collapse_toolbar.set_border(4.0f); - collapse_toolbar.set_separator_size(4); - collapse_toolbar.set_gap_size(2); - - collapse_toolbar.del_all_item(); - - GLToolbarItem::Data item; - - item.name = "collapse_sidebar"; - // set collapse svg name - item.icon_filename = "collapse.svg"; - item.sprite_id = 0; - item.left.action_callback = []() { - wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed()); - }; - - if (!collapse_toolbar.add_item(item)) + if (!setup_collapse_toolbar(collapse_toolbar, []() { + wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed()); + })) return false; // Now "collapse" sidebar to current state. This is done so the tooltip @@ -18481,7 +18392,15 @@ bool Plater::is_sidebar_collapsed() const { return p->sidebar_layout.is_collapse void Plater::collapse_sidebar(bool collapse) { p->collapse_sidebar(collapse); } Sidebar::DockingState Plater::get_sidebar_docking_state() const { return p->get_sidebar_docking_state(); } -void Plater::reset_window_layout() { p->reset_window_layout(); } +void Plater::reset_window_layout() +{ + p->reset_window_layout(); +#ifdef SLIC3R_CAD + // The Design tab docks its own sidebar. + if (DesignPanel* design = DesignPanel::if_built()) + design->reset_window_layout(); +#endif +} void Plater::add_dock_pane(wxWindow* window, const std::string& name, const wxString& caption, const std::string& dock, const wxSize& size, std::function on_close)