QoL: Make sidebar collapsible, resizable, movable, floatable and dockable (#2972)

* Add sidebar docking

* Fix sidebar initial size

* Fix sidebar collapse

* Hide floating sidebar when switch away from model view

* Add option to reset window layout

* Save & load window layout

* Fix a small issue with gcode toggle

* Properly save & restore sidebar state

* Show collapse icon based on sidebar docking state

* Adjust toolbar position based on docking position

* Fix return toolbar position

* Fix plate list position
This commit is contained in:
Noisyfox 2023-12-08 17:16:16 +08:00 committed by GitHub
parent 3d2ab8e2e2
commit 7cfa4f3bcd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 317 additions and 119 deletions

View file

@ -60,6 +60,7 @@
#endif
#include <wx/clrpicker.h>
#include <wx/tokenzr.h>
#include <wx/aui/aui.h>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Format/STL.hpp"
@ -372,7 +373,6 @@ struct Sidebar::priv
//ScalableButton *btn_eject_device;
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
bool is_collapsed {false};
Search::OptionsSearcher searcher;
std::string ams_list_device;
@ -1759,19 +1759,9 @@ void Sidebar::update_mode()
Layout();
}
bool Sidebar::is_collapsed() { return p->is_collapsed; }
bool Sidebar::is_collapsed() { return p->plater->is_sidebar_collapsed(); }
void Sidebar::collapse(bool collapse)
{
p->is_collapsed = collapse;
this->Show(!collapse);
p->plater->Layout();
// save collapsing state to the AppConfig
//if (wxGetApp().is_editor())
// wxGetApp().app_config->set_bool("collapsed_sidebar", collapse);
}
void Sidebar::collapse(bool collapse) { p->plater->collapse_sidebar(collapse); }
#ifdef _MSW_DARK_MODE
void Sidebar::show_mode_sizer(bool show)
@ -1949,6 +1939,28 @@ 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
{
@ -1971,10 +1983,17 @@ struct Plater::priv
Slic3r::GCodeProcessorResult gcode_result;
// GUI elements
wxSizer* panel_sizer{ nullptr };
AuiMgr m_aui_mgr;
wxString m_default_window_layout;
wxPanel* current_panel{ nullptr };
std::vector<wxPanel*> panels;
Sidebar *sidebar;
struct SidebarLayout
{
bool is_enabled{false};
bool is_collapsed{false};
bool show{false};
} sidebar_layout;
Bed3D bed;
Camera camera;
//BBS: partplate related structure
@ -2166,8 +2185,11 @@ struct Plater::priv
if (current_panel == view3D) view3D->get_canvas3d()->show_overhang(show);
}
bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); }
void enable_sidebar(bool enabled);
void collapse_sidebar(bool collapse);
void update_sidebar(bool force_update = false);
void reset_window_layout();
Sidebar::DockingState get_sidebar_docking_state();
bool is_view3D_layers_editing_enabled() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_layers_editing_enabled(); }
@ -2381,6 +2403,7 @@ 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&);
@ -2390,6 +2413,8 @@ struct Plater::priv
void update_publish_dialog_status(wxString &msg, int percent = -1);
void on_action_print_plate_from_sdcard(SimpleEvent&);
void on_tab_selection_changing(wxBookCtrlEvent&);
// Set the bed shape to a single closed 2D polygon(array of two element arrays),
// triangulate the bed and store the triangles into m_bed.m_triangles,
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
@ -2540,6 +2565,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
//BBS :partplatelist construction
, partplate_list(this->q, &model)
{
m_aui_mgr.SetManagedWindow(q);
m_aui_mgr.SetDockSizeConstraint(1, 1);
//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);
this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font());
//BBS: use the first partplate's print for background process
@ -2581,11 +2613,14 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->Bind(EVT_CREATE_FILAMENT, &priv::on_create_filament, this);
this->q->Bind(EVT_MODIFY_FILAMENT, &priv::on_modify_filament, this);
view3D = new View3D(q, bed, &model, config, &background_process);
//BBS: use partplater's gcode
preview = new Preview(q, bed, &model, config, &background_process, partplate_list.get_current_slice_result(), [this]() { schedule_background_process(); });
main_frame->m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGING, &priv::on_tab_selection_changing, this);
assemble_view = new AssembleView(q, bed, &model, config, &background_process);
auto* panel_3d = new wxPanel(q);
view3D = new View3D(panel_3d, bed, &model, config, &background_process);
//BBS: use partplater's gcode
preview = new Preview(panel_3d, bed, &model, config, &background_process, partplate_list.get_current_slice_result(), [this]() { schedule_background_process(); });
assemble_view = new AssembleView(panel_3d, bed, &model, config, &background_process);
#ifdef __APPLE__
// BBS
@ -2606,23 +2641,35 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
update();
auto* hsizer = new wxBoxSizer(wxHORIZONTAL);
auto* vsizer = new wxBoxSizer(wxVERTICAL);
// Orca: Make sidebar dockable
m_aui_mgr.AddPane(sidebar, wxAuiPaneInfo()
.Name("sidebar")
.Left()
.CloseButton(false)
.TopDockable(false)
.BottomDockable(false)
.Floatable(true)
.BestSize(wxSize(42 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit())));
// BBS: move sidebar to left side
hsizer->Add(sidebar, 0, wxEXPAND | wxLEFT | wxRIGHT, 0);
auto spliter_1 = new ::StaticLine(q, true);
spliter_1->SetLineColour("#A6A9AA");
hsizer->Add(spliter_1, 0, wxEXPAND);
panel_sizer = new wxBoxSizer(wxHORIZONTAL);
auto* panel_sizer = new wxBoxSizer(wxHORIZONTAL);
panel_sizer->Add(view3D, 1, wxEXPAND | wxALL, 0);
panel_sizer->Add(preview, 1, wxEXPAND | wxALL, 0);
panel_sizer->Add(assemble_view, 1, wxEXPAND | wxALL, 0);
vsizer->Add(panel_sizer, 1, wxEXPAND | wxALL, 0);
hsizer->Add(vsizer, 1, wxEXPAND | wxALL, 0);
panel_3d->SetSizer(panel_sizer);
m_aui_mgr.AddPane(panel_3d, wxAuiPaneInfo().Name("main").CenterPane().PaneBorder(false));
q->SetSizer(hsizer);
m_default_window_layout = m_aui_mgr.SavePerspective();
// Load previous window layout
{
const auto cfg = wxGetApp().app_config;
wxString layout = wxString::FromUTF8(cfg->get("window_layout"));
if (!layout.empty()) {
m_aui_mgr.LoadPerspective(layout, false);
auto& sidebar = m_aui_mgr.GetPane(this->sidebar);
sidebar_layout.is_collapsed = !sidebar.IsShown();
}
}
menus.init(q);
@ -2827,6 +2874,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership
q->Layout();
apply_color_mode();
set_current_panel(wxGetApp().is_editor() ? static_cast<wxPanel*>(view3D) : static_cast<wxPanel*>(preview));
// updates camera type from .ini file
@ -2952,6 +3001,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// bool is_collapsed = wxGetApp().app_config->get("collapsed_sidebar") == "1";
// sidebar->collapse(is_collapsed);
//}
update_sidebar(true);
}
Plater::priv::~priv()
@ -2960,6 +3010,7 @@ Plater::priv::~priv()
delete config;
// Saves the database of visited (already shown) hints into hints.ini.
notification_manager->deactivate_loaded_hints();
main_frame->m_tabpanel->Unbind(wxEVT_NOTEBOOK_PAGE_CHANGING, &priv::on_tab_selection_changing, this);
}
void Plater::priv::update(unsigned int flags)
@ -2998,6 +3049,8 @@ void Plater::priv::update(unsigned int flags)
if (get_config("autocenter") == "true" && this->sidebar->obj_manipul()->IsShown())
this->sidebar->obj_manipul()->UpdateAndShow(true);
#endif
update_sidebar();
}
void Plater::priv::select_view(const std::string& direction)
@ -3115,14 +3168,66 @@ void Plater::priv::select_next_view_3D()
// set_current_panel(view3D);
}
void Plater::priv::collapse_sidebar(bool collapse)
void Plater::priv::enable_sidebar(bool enabled)
{
if (q->m_only_gcode && !collapse)
return;
sidebar->collapse(collapse);
notification_manager->set_sidebar_collapsed(collapse);
if (q->m_only_gcode)
enabled = false;
sidebar_layout.is_enabled = enabled;
update_sidebar();
}
void Plater::priv::collapse_sidebar(bool collapse)
{
if (q->m_only_gcode)
return;
sidebar_layout.is_collapsed = collapse;
update_sidebar();
}
void Plater::priv::update_sidebar(bool force_update) {
auto& sidebar = m_aui_mgr.GetPane(this->sidebar);
bool needs_update = force_update;
if (!sidebar_layout.is_enabled) {
if (sidebar.IsShown()) {
sidebar.Hide();
needs_update = true;
}
} else {
bool should_show = sidebar_layout.show && !sidebar_layout.is_collapsed;
if (should_show != sidebar.IsShown()) {
sidebar.Show(should_show);
needs_update = true;
}
}
if (needs_update) {
notification_manager->set_sidebar_collapsed(sidebar.IsShown());
m_aui_mgr.Update();
}
}
void Plater::priv::reset_window_layout()
{
m_aui_mgr.LoadPerspective(m_default_window_layout, false);
sidebar_layout.is_collapsed = false;
update_sidebar(true);
}
Sidebar::DockingState Plater::priv::get_sidebar_docking_state() {
if (!sidebar_layout.is_enabled) {
return Sidebar::None;
}
const auto& sidebar = m_aui_mgr.GetPane(this->sidebar);
if(sidebar.IsFloating()) {
return Sidebar::None;
}
return sidebar.dock_direction == wxAUI_DOCK_RIGHT ? Sidebar::Right : Sidebar::Left;
}
void Plater::priv::reset_all_gizmos()
{
@ -4535,6 +4640,17 @@ void Plater::priv::reset(bool apply_presets_change)
// BBS
m_saved_timestamp = m_backup_timestamp = size_t(-1);
// Save window layout
if (sidebar_layout.is_enabled) {
// Reset show state
auto& sidebar = m_aui_mgr.GetPane(this->sidebar);
if (!sidebar_layout.is_collapsed && !sidebar.IsShown()) {
sidebar.Show();
}
auto layout = m_aui_mgr.SavePerspective();
wxGetApp().app_config->set("window_layout", layout.utf8_string());
}
}
void Plater::priv::center_selection()
@ -5775,7 +5891,7 @@ void Plater::priv::set_current_panel(wxPanel* panel, bool no_slice)
p->Hide();
}
panel_sizer->Layout();
m_aui_mgr.Update();
if (wxGetApp().plater()) {
Camera& cam = wxGetApp().plater()->get_camera();
@ -6628,6 +6744,13 @@ void Plater::priv::on_action_print_plate_from_sdcard(SimpleEvent&)
m_select_machine_dlg->ShowModal();
}
void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
{
const int new_sel = e.GetSelection();
sidebar_layout.show = new_sel == MainFrame::tp3DEditor || new_sel == MainFrame::tpPreview;
update_sidebar();
}
int Plater::priv::update_print_required_data(Slic3r::DynamicPrintConfig config, Slic3r::Model model, Slic3r::PlateDataPtrs plate_data_list, std::string file_name, std::string file_path)
{
if (!m_select_machine_dlg) m_select_machine_dlg = new SelectMachineDialog(q);
@ -6826,6 +6949,8 @@ void Plater::priv::on_apple_change_color_mode(wxSysColourChangedEvent& evt) {
preview->get_canvas3d()->on_change_color_mode(m_is_dark);
assemble_view->get_canvas3d()->on_change_color_mode(m_is_dark);
}
apply_color_mode();
}
void Plater::priv::on_change_color_mode(SimpleEvent& evt) {
@ -6834,6 +6959,37 @@ void Plater::priv::on_change_color_mode(SimpleEvent& evt) {
preview->get_canvas3d()->on_change_color_mode(m_is_dark);
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.Update();
}
static void get_position(wxWindowBase* child, wxWindowBase* until_parent, int& x, int& y) {
int res_x = 0, res_y = 0;
while (child != until_parent && child != nullptr) {
int _x, _y;
child->GetPosition(&_x, &_y);
res_x += _x;
res_y += _y;
child = child->GetParent();
}
x = res_x;
y = res_y;
}
void Plater::priv::on_right_click(RBtnEvent& evt)
@ -6895,8 +7051,8 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
#else
//BBS: GUI refactor: move sidebar to the left
int x, y;
current_panel->GetPosition(&x, &y);
q->PopupMenu(menu, (int)evt.data.first.x() + x, (int)evt.data.first.y());
get_position(current_panel, wxGetApp().mainframe, x, y);
q->PopupMenu(menu, (int) evt.data.first.x() + x, (int) evt.data.first.y() + y);
//q->PopupMenu(menu);
#endif
}
@ -6912,8 +7068,8 @@ void Plater::priv::on_plate_right_click(RBtnPlateEvent& evt)
#else
//BBS: GUI refactor: move sidebar to the left
int x, y;
current_panel->GetPosition(&x, &y);
q->PopupMenu(menu, (int)evt.data.first.x() + x, (int)evt.data.first.y());
get_position(current_panel, wxGetApp().mainframe, x, y);
q->PopupMenu(menu, (int) evt.data.first.x() + x, (int) evt.data.first.y() + y);
//q->PopupMenu(menu);
#endif
}
@ -7270,7 +7426,7 @@ bool Plater::priv::init_collapse_toolbar()
item.name = "collapse_sidebar";
// set collapse svg name
item.icon_filename = "*.svg";
item.icon_filename = "collapse.svg";
item.sprite_id = 0;
item.left.action_callback = []() {
wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed());
@ -8150,9 +8306,6 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_
if (!skip_confirm && (result = close_with_confirm(check)) == wxID_CANCEL)
return wxID_CANCEL;
//BBS: add only gcode mode
bool previous_gcode = m_only_gcode;
m_only_gcode = false;
m_exported_file = false;
m_loading_project = false;
@ -8197,8 +8350,7 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_
p->select_view_3D("3D");
p->select_view("topfront");
p->camera.requires_zoom_to_bed = true;
if (previous_gcode)
collapse_sidebar(false);
enable_sidebar(!m_only_gcode);
up_to_date(true, false);
up_to_date(true, true);
@ -8230,9 +8382,6 @@ void Plater::load_project(wxString const& filename2,
return;
}
//BBS: add only gcode mode
bool previous_gcode = m_only_gcode;
// BBS
if (m_loading_project) {
//some error cases happens
@ -8302,8 +8451,7 @@ void Plater::load_project(wxString const& filename2,
p->partplate_list.select_plate_view();
}
if (previous_gcode)
collapse_sidebar(false);
enable_sidebar(!m_only_gcode);
wxGetApp().app_config->update_last_backup_dir(model().get_backup_path());
if (load_restore && !originfile.empty()) {
@ -10014,8 +10162,13 @@ void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); }
bool Plater::is_view3D_overhang_shown() const { return p->is_view3D_overhang_shown(); }
void Plater::show_view3D_overhang(bool show) { p->show_view3D_overhang(show); }
bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); }
void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); }
bool Plater::is_sidebar_enabled() const { return p->sidebar_layout.is_enabled; }
void Plater::enable_sidebar(bool enabled) { p->enable_sidebar(enabled); }
bool Plater::is_sidebar_collapsed() const { return p->sidebar_layout.is_collapsed; }
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(); }
//BBS
void Plater::select_curr_plate_all() { p->select_curr_plate_all(); }
@ -12193,11 +12346,6 @@ bool Plater::init_collapse_toolbar()
return p->init_collapse_toolbar();
}
void Plater::enable_collapse_toolbar(bool enable)
{
p->collapse_toolbar.set_enabled(enable);
}
const Camera& Plater::get_camera() const
{
return p->camera;