From 82e7ee937f2d4bd506b945896b4730e8ce96e9a6 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Sat, 25 Apr 2026 18:14:51 +0800 Subject: [PATCH] Add Expert user mode (#13348) Add Expert user mode with three-way mode switch. the old ModeButton has been removed. Co-authored-by: Copilot --- src/libslic3r/Config.hpp | 1 + src/slic3r/GUI/FilamentMapDialog.cpp | 1 + src/slic3r/GUI/FilamentMapPanel.hpp | 3 +- src/slic3r/GUI/GUI_App.cpp | 56 +++++-- src/slic3r/GUI/GUI_App.hpp | 2 + src/slic3r/GUI/HintNotification.cpp | 3 +- src/slic3r/GUI/Notebook.cpp | 19 --- src/slic3r/GUI/Notebook.hpp | 2 - src/slic3r/GUI/ParamsPanel.cpp | 91 +++++------ src/slic3r/GUI/ParamsPanel.hpp | 3 +- src/slic3r/GUI/Preferences.cpp | 6 +- src/slic3r/GUI/Tab.cpp | 87 +++------- src/slic3r/GUI/Tab.hpp | 6 +- src/slic3r/GUI/Widgets/SwitchButton.cpp | 204 ++++++++++++++++++++++-- src/slic3r/GUI/Widgets/SwitchButton.hpp | 38 ++++- src/slic3r/GUI/wxExtensions.cpp | 144 ----------------- src/slic3r/GUI/wxExtensions.hpp | 71 --------- 17 files changed, 338 insertions(+), 399 deletions(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 7c449f8107..a77946cc9b 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -199,6 +199,7 @@ enum ConfigOptionType { enum ConfigOptionMode { comSimple = 0, comAdvanced, + comExpert, comDevelop, }; diff --git a/src/slic3r/GUI/FilamentMapDialog.cpp b/src/slic3r/GUI/FilamentMapDialog.cpp index 957b2b9d0e..73f8041a28 100644 --- a/src/slic3r/GUI/FilamentMapDialog.cpp +++ b/src/slic3r/GUI/FilamentMapDialog.cpp @@ -2,6 +2,7 @@ #include "PartPlate.hpp" #include "Widgets/Button.hpp" #include "Widgets/DialogButtons.hpp" +#include "Widgets/Label.hpp" #include "I18N.hpp" #include "GUI_App.hpp" #include "CapsuleButton.hpp" diff --git a/src/slic3r/GUI/FilamentMapPanel.hpp b/src/slic3r/GUI/FilamentMapPanel.hpp index d2b6ea21d6..ad5cefa459 100644 --- a/src/slic3r/GUI/FilamentMapPanel.hpp +++ b/src/slic3r/GUI/FilamentMapPanel.hpp @@ -4,7 +4,8 @@ #include "GUI.hpp" #include "DragDropPanel.hpp" -#include "Widgets/SwitchButton.hpp" +#include "wxExtensions.hpp" +#include "Widgets/Label.hpp" namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c79826733e..f8d27c4631 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -6572,31 +6572,59 @@ Tab* GUI_App::get_layer_tab() return model_tabs_list[2]; } -ConfigOptionMode GUI_App::get_mode() +namespace +{ +ConfigOptionMode saved_mode_from_string(const std::string& mode) +{ + return mode == "expert" ? comExpert : + mode == "advanced" ? comAdvanced : + mode == "develop" ? comAdvanced : + comSimple; +} + +std::string saved_mode_to_string(ConfigOptionMode mode) +{ + return mode == comExpert ? "expert" : + mode == comAdvanced ? "advanced" : + "simple"; +} + +std::string effective_mode_to_string(ConfigOptionMode mode) +{ + return mode == comDevelop ? "develop" : saved_mode_to_string(mode); +} +} + +ConfigOptionMode GUI_App::get_saved_mode() { if (!app_config->has("user_mode")) return comSimple; - //BBS - const auto mode = app_config->get("user_mode"); - return mode == "advanced" ? comAdvanced : - mode == "simple" ? comSimple : - mode == "develop" ? comDevelop : comSimple; + + return saved_mode_from_string(app_config->get("user_mode")); +} + +ConfigOptionMode GUI_App::get_mode() +{ + return app_config->get_bool("developer_mode") ? comDevelop : get_saved_mode(); +} + +std::string GUI_App::get_saved_mode_str() +{ + return saved_mode_to_string(get_saved_mode()); } std::string GUI_App::get_mode_str() { - if (!app_config->has("user_mode")) - return "simple"; - return app_config->get("user_mode"); + return effective_mode_to_string(get_mode()); } void GUI_App::save_mode(const /*ConfigOptionMode*/int mode) { - //BBS - const std::string mode_str = mode == comAdvanced ? "advanced" : - mode == comSimple ? "simple" : - mode == comDevelop ? "develop" : "simple"; - app_config->set("user_mode", mode_str); + const auto saved_mode = mode == comExpert ? comExpert : + mode == comAdvanced ? comAdvanced : + mode == comSimple ? comSimple : + get_saved_mode(); + app_config->set("user_mode", saved_mode_to_string(saved_mode)); update_mode(); } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index e89ac33cae..0cbc93221e 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -534,7 +534,9 @@ public: Tab* get_plate_tab(); Tab* get_model_tab(bool part = false); Tab* get_layer_tab(); + ConfigOptionMode get_saved_mode(); ConfigOptionMode get_mode(); + std::string get_saved_mode_str(); std::string get_mode_str(); void save_mode(const /*ConfigOptionMode*/int mode) ; void update_mode(); diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index fa26cd17a2..a716112347 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -115,7 +115,8 @@ namespace { ConfigOptionMode config_mode = wxGetApp().get_mode(); if (config_mode == ConfigOptionMode::comSimple) return (tag == "simple" ? TagCheckAffirmative : TagCheckNegative); else if (config_mode == ConfigOptionMode::comAdvanced) return (tag == "advanced" ? TagCheckAffirmative : TagCheckNegative); - //else if (config_mode == ConfigOptionMode::comDevelop) return (tag == "develop" ? TagCheckAffirmative : TagCheckNegative); + else if (config_mode == ConfigOptionMode::comExpert || config_mode == ConfigOptionMode::comDevelop) + return (tag == "expert" ? TagCheckAffirmative : TagCheckNegative); } return TagCheckNotCompatible; } diff --git a/src/slic3r/GUI/Notebook.cpp b/src/slic3r/GUI/Notebook.cpp index 89f00f9447..ceda3fc0d6 100644 --- a/src/slic3r/GUI/Notebook.cpp +++ b/src/slic3r/GUI/Notebook.cpp @@ -87,25 +87,6 @@ void ButtonsListCtrl::OnPaint(wxPaintEvent&) dc.SetBrush(clr); dc.DrawRectangle(pos.x, pos.y + size.y, size.x, sz.y - size.y); } - -#if 0 - // highlight selected mode button - if (m_mode_sizer) { - const std::vector& mode_btns = m_mode_sizer->get_btns(); - for (int idx = 0; idx < int(mode_btns.size()); idx++) { - ModeButton* btn = mode_btns[idx]; - btn->SetBackgroundColor(btn->is_selected() ? selected_btn_bg : default_btn_bg); - - //wxPoint pos = btn->GetPosition(); - //wxSize size = btn->GetSize(); - //const wxColour& clr = btn->is_selected() ? btn_marker_color : default_btn_bg; - //dc.SetPen(clr); - //dc.SetBrush(clr); - //dc.DrawRectangle(pos.x, pos.y + size.y, size.x, sz.y - size.y); - } - } -#endif - // Draw orange bottom line dc.SetPen(btn_marker_color); diff --git a/src/slic3r/GUI/Notebook.hpp b/src/slic3r/GUI/Notebook.hpp index 10abfedb95..d333956561 100644 --- a/src/slic3r/GUI/Notebook.hpp +++ b/src/slic3r/GUI/Notebook.hpp @@ -6,7 +6,6 @@ #include #include -class ModeSizer; class ScalableButton; class Button; @@ -40,7 +39,6 @@ private: int m_selection {-1}; int m_btn_margin; int m_line_margin; - //ModeSizer* m_mode_sizer {nullptr}; std::vector m_pageLabels; // ORCA }; diff --git a/src/slic3r/GUI/ParamsPanel.cpp b/src/slic3r/GUI/ParamsPanel.cpp index a7f371a9a8..2c18edfaeb 100644 --- a/src/slic3r/GUI/ParamsPanel.cpp +++ b/src/slic3r/GUI/ParamsPanel.cpp @@ -22,6 +22,16 @@ namespace Slic3r { namespace GUI { +namespace +{ +int mode_to_selection(ConfigOptionMode mode) +{ + return mode == comExpert ? 2 : + mode == comAdvanced ? 1 : + 0; +} +} + TipsDialog::TipsDialog(wxWindow *parent, const wxString &title, const wxString &description, std::string app_key, long style,std::map option_map) : DPIDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX), @@ -261,15 +271,17 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c m_mode_icon = new ScalableButton(m_top_panel, wxID_ANY, "advanced"); // ORCA m_mode_icon->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { - if(wxGetApp().get_mode() == comDevelop) return; // prevent change on dev mode - m_mode_view->SetValue(!m_mode_view->GetValue()); - wxCommandEvent evt(wxEVT_TOGGLEBUTTON, m_mode_view->GetId()); // ParamsPanel::OnToggled(evt) - evt.SetEventObject(m_mode_view); - m_mode_view->wxEvtHandler::ProcessEvent(evt); + if (wxGetApp().get_mode() == comDevelop || m_mode_view == nullptr) + return; // prevent change on dev mode + + const int selection = m_mode_view->GetSelection(); + m_mode_view->SelectAndNotify((selection + 1) % 3); }); - m_mode_icon->SetToolTip(_L("Show/Hide advanced parameters")); - m_mode_view = new SwitchButton(m_top_panel, wxID_ABOUT); - m_mode_view->SetToolTip(_L("Show/Hide advanced parameters")); + m_mode_icon->SetToolTip(_L("Cycle settings visibility")); + m_mode_view = new ModeSwitchButton(m_top_panel); + m_mode_view->SetSelection(mode_to_selection(wxGetApp().get_saved_mode())); + if (wxGetApp().get_mode() == comDevelop) + m_mode_view->Enable(false); // BBS: new layout //m_search_btn = new ScalableButton(m_top_panel, wxID_ANY, "search", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); @@ -382,9 +394,6 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c if (m_mode_region) m_mode_region->Bind(wxEVT_TOGGLEBUTTON, &ParamsPanel::OnToggled, this); - if (m_mode_view) - m_mode_view->Bind(wxEVT_TOGGLEBUTTON, &ParamsPanel::OnToggled, this); - Bind(wxEVT_TOGGLEBUTTON, &ParamsPanel::OnToggled, this); // For Tab's mode switch //Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { wxGetApp().plater()->search(false); }, wxID_FIND); //m_export_to_file->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { wxGetApp().mainframe->export_config(); }); //m_import_from_file->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { wxGetApp().mainframe->load_config_file(); }); @@ -560,28 +569,6 @@ void ParamsPanel::OnToggled(wxCommandEvent& event) return; } - if (wxID_ABOUT != event.GetId()) { - return; - } - - // this is from tab's mode switch - bool value = dynamic_cast(event.GetEventObject())->GetValue(); - int mode_id; - - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Advanced mode toogle to %1%") % value; - - if (value) - { - //m_mode_region->SetBitmap(m_toggle_on_icon); - mode_id = comAdvanced; - } - else - { - //m_mode_region->SetBitmap(m_toggle_off_icon); - mode_id = comSimple; - } - - Slic3r::GUI::wxGetApp().save_mode(mode_id); event.Skip(); } @@ -657,27 +644,22 @@ bool ParamsPanel::is_active_and_shown_tab(wxPanel* tab) void ParamsPanel::update_mode() { int app_mode = Slic3r::GUI::wxGetApp().get_mode(); - SwitchButton * mode_view = m_current_tab ? dynamic_cast(m_current_tab)->m_mode_view : nullptr; - if (mode_view == nullptr) mode_view = m_mode_view; - if (mode_view == nullptr) return; + auto sync_mode_view = [&](ModeSwitchButton* mode_view) { + if (mode_view == nullptr) + return; - //BBS: disable the mode tab and return directly when enable develop mode - if (app_mode == comDevelop) - { - mode_view->Disable(); - return; - } - if (!mode_view->IsEnabled()) - mode_view->Enable(); + mode_view->SetSelection(mode_to_selection(Slic3r::GUI::wxGetApp().get_saved_mode())); + if (app_mode == comDevelop) { + mode_view->Enable(false); + return; + } - if (app_mode == comAdvanced) - { - mode_view->SetValue(true); - } - else - { - mode_view->SetValue(false); - } + if (!mode_view->IsEnabled()) + mode_view->Enable(); + }; + + sync_mode_view(m_mode_view); + sync_mode_view(m_current_tab ? dynamic_cast(m_current_tab)->m_mode_view : nullptr); } void ParamsPanel::msw_rescale() @@ -694,7 +676,9 @@ void ParamsPanel::msw_rescale() ((SwitchButton* )m_mode_region)->Rescale(); if (m_mode_icon) m_mode_icon->msw_rescale(); if (m_mode_view) - ((SwitchButton* )m_mode_view)->Rescale(); + { + m_mode_view->Rescale(); + } for (auto tab : {m_tab_print, m_tab_print_plate, m_tab_print_object, m_tab_print_part, m_tab_print_layer, m_tab_filament, m_tab_printer}) { if (tab) dynamic_cast(tab)->msw_rescale(); } @@ -790,7 +774,6 @@ void ParamsPanel::delete_subwindows() if (m_mode_view) { - delete m_mode_view; m_mode_view = nullptr; } diff --git a/src/slic3r/GUI/ParamsPanel.hpp b/src/slic3r/GUI/ParamsPanel.hpp index 36ed957cc4..0726db91d3 100644 --- a/src/slic3r/GUI/ParamsPanel.hpp +++ b/src/slic3r/GUI/ParamsPanel.hpp @@ -31,6 +31,7 @@ #include "GUI_Utils.hpp" #include "Widgets/Button.hpp" +class ModeSwitchButton; class SwitchButton; class StaticBox; @@ -85,7 +86,7 @@ class ParamsPanel : public wxPanel ScalableButton *m_tips_arrow{nullptr}; bool m_tips_arror_blink{false}; ScalableButton* m_mode_icon { nullptr }; // ORCA - SwitchButton* m_mode_view { nullptr }; + ModeSwitchButton* m_mode_view { nullptr }; //wxBitmapButton* m_search_button { nullptr }; wxStaticLine* m_staticline_print { nullptr }; //wxBoxSizer* m_print_sizer { nullptr }; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 2774f6fcd7..a07e352798 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1004,11 +1004,7 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too if (param == "developer_mode") { m_developer_mode_def = app_config->get("developer_mode"); - if (m_developer_mode_def == "true") { - Slic3r::GUI::wxGetApp().save_mode(comDevelop); - } else { - Slic3r::GUI::wxGetApp().save_mode(comAdvanced); - } + Slic3r::GUI::wxGetApp().update_mode(); } // webview dump_vedio diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9d93098069..49c68efb8a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -43,7 +43,9 @@ #include "MsgDialog.hpp" #include "Notebook.hpp" +#include "Widgets/ComboBox.hpp" #include "Widgets/Label.hpp" +#include "Widgets/SwitchButton.hpp" #include "Widgets/TabCtrl.hpp" #include "MarkdownTip.hpp" #include "Search.hpp" @@ -65,6 +67,16 @@ t_config_option_keys deep_diff(const ConfigBase &config_this, const ConfigBase & namespace GUI { +namespace +{ +int mode_to_selection(ConfigOptionMode mode) +{ + return mode == comExpert ? 2 : + mode == comAdvanced ? 1 : + 0; +} +} + #define DISABLE_UNDO_SYS // Forward declaration for early use; definitions live later in this translation unit. @@ -390,17 +402,19 @@ void Tab::create_preset_tab() if (dynamic_cast(this) == nullptr) { m_mode_icon = new ScalableButton(m_top_panel, wxID_ANY, "advanced"); // ORCA - m_mode_icon->SetToolTip(_L("Show/Hide advanced parameters")); + m_mode_icon->SetToolTip(_L("Cycle settings visibility")); m_mode_icon->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { - if(wxGetApp().get_mode() == comDevelop) return; // prevent change on dev mode - m_mode_view->SetValue(!m_mode_view->GetValue()); - wxCommandEvent evt(wxEVT_TOGGLEBUTTON, m_mode_view->GetId()); // ParamsPanel::OnToggled(evt) - evt.SetEventObject(m_mode_view); - m_mode_view->wxEvtHandler::ProcessEvent(evt); + if (wxGetApp().get_mode() == comDevelop || m_mode_view == nullptr) + return; // prevent change on dev mode + + const int selection = m_mode_view->GetSelection(); + m_mode_view->SelectAndNotify((selection + 1) % 3); }); m_top_sizer->Add(m_mode_icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(SidebarProps::WideSpacing())); - m_mode_view = new SwitchButton(m_top_panel, wxID_ABOUT); - m_mode_view->SetToolTip(_L("Show/Hide advanced parameters")); + m_mode_view = new ModeSwitchButton(m_top_panel); + m_mode_view->SetSelection(mode_to_selection(wxGetApp().get_saved_mode())); + if (wxGetApp().get_mode() == comDevelop) + m_mode_view->Enable(false); m_top_sizer->AddSpacer(FromDIP(SidebarProps::ElementSpacing())); m_top_sizer->Add( m_mode_view, 0, wxALIGN_CENTER_VERTICAL); } @@ -414,56 +428,6 @@ void Tab::create_preset_tab() else m_top_panel->Hide(); -#if 0 -#ifdef _MSW_DARK_MODE - // Sizer with buttons for mode changing - if (wxGetApp().tabs_as_menu()) -#endif - m_mode_sizer = new ModeSizer(panel, int (0.5*em_unit(this))); - - const float scale_factor = /*wxGetApp().*/em_unit(this)*0.1;// GetContentScaleFactor(); - m_hsizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_hsizer, 0, wxEXPAND | wxBOTTOM, 3); - m_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); - m_hsizer->AddSpacer(int(4*scale_factor)); - m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(4 * scale_factor)); - m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL); - if (m_btn_edit_ph_printer) { - m_hsizer->AddSpacer(int(4 * scale_factor)); - m_hsizer->Add(m_btn_edit_ph_printer, 0, wxALIGN_CENTER_VERTICAL); - } - m_hsizer->AddSpacer(int(/*16*/8 * scale_factor)); - m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(8 * scale_factor)); - m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(32 * scale_factor)); - m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(32 * scale_factor)); - m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(8*scale_factor)); - m_hsizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(16*scale_factor)); - // m_hsizer->AddStretchSpacer(32); - // StretchSpacer has a strange behavior under OSX, so - // There is used just additional sizer for m_mode_sizer with right alignment - if (m_mode_sizer) { - auto mode_sizer = new wxBoxSizer(wxVERTICAL); - // Don't set the 2nd parameter to 1, making the sizer rubbery scalable in Y axis may lead - // to wrong vertical size assigned to wxBitmapComboBoxes, see GH issue #7176. - mode_sizer->Add(m_mode_sizer, 0, wxALIGN_RIGHT); - m_hsizer->Add(mode_sizer, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, wxOSX ? 15 : 10); - } - - //Horizontal sizer to hold the tree and the selected page. - m_hsizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_hsizer, 1, wxEXPAND, 0); - - //left vertical sizer - m_left_sizer = new wxBoxSizer(wxVERTICAL); - m_hsizer->Add(m_left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3); -#endif // tree m_tabctrl = new TabCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1), wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_NONE | wxWANTS_CHARS | wxTR_FULL_ROW_HIGHLIGHT); @@ -1274,11 +1238,6 @@ void Tab::update_mode() { m_mode = wxGetApp().get_mode(); - //BBS: GUI refactor - // update mode for ModeSizer - //if (m_mode_sizer) - // m_mode_sizer->SetMode(m_mode); - update_visibility(); update_changed_tree_ui(); @@ -1322,7 +1281,9 @@ void Tab::msw_rescale() bmp->msw_rescale(); if (m_mode_view) + { m_mode_view->Rescale(); + } if (m_detach_preset_btn) m_detach_preset_btn->msw_rescale(); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 96b35b26e1..b423d64ad9 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -43,6 +43,8 @@ #include "Widgets/CheckBox.hpp" // ORCA class TabCtrl; +class ModeSwitchButton; +class SwitchButton; namespace Slic3r { @@ -162,8 +164,6 @@ protected: wxScrolledWindow* m_page_view {nullptr}; //wxBoxSizer* m_page_sizer {nullptr}; - //ModeSizer* m_mode_sizer {nullptr}; - struct PresetDependencies { Preset::Type type = Preset::TYPE_INVALID; ::CheckBox* checkbox = nullptr; @@ -303,7 +303,7 @@ public: // 3. propagate changed configuration to the Plater when (m_update_cnt == 0) only int m_update_cnt = 0; - SwitchButton *m_mode_view = nullptr; + ModeSwitchButton *m_mode_view = nullptr; SwitchButton *m_extruder_switch = nullptr; public: diff --git a/src/slic3r/GUI/Widgets/SwitchButton.cpp b/src/slic3r/GUI/Widgets/SwitchButton.cpp index bedc068e1b..aedd868f8f 100644 --- a/src/slic3r/GUI/Widgets/SwitchButton.cpp +++ b/src/slic3r/GUI/Widgets/SwitchButton.cpp @@ -3,6 +3,8 @@ #include "StaticBox.hpp" #include "../wxExtensions.hpp" +#include "../GUI_App.hpp" +#include "slic3r/GUI/I18N.hpp" #include "../Utils/MacDarkMode.hpp" #include "../Utils/WxFontUtils.hpp" #ifdef __APPLE__ @@ -156,10 +158,10 @@ void SwitchButton::Rescale() #endif dc2.SetBrush(wxBrush(track_color.colorForStates(state))); dc2.SetPen(wxPen(track_color.colorForStates(state))); - dc2.DrawRoundedRectangle(wxRect({0, 0}, trackSize), trackSize.y / 2); + dc2.DrawRoundedRectangle(wxRect({0, 0}, trackSize), trackSize.y / 2.0); dc2.SetBrush(wxBrush(thumb_color.colorForStates(StateColor::Checked | StateColor::Enabled))); dc2.SetPen(wxPen(thumb_color.colorForStates(StateColor::Checked | StateColor::Enabled))); - dc2.DrawRoundedRectangle(wxRect({ i == 0 ? BS : (trackSize.x - thumbSize.x - BS), BS}, thumbSize), thumbSize.y / 2); + dc2.DrawRoundedRectangle(wxRect({ i == 0 ? BS : (trackSize.x - thumbSize.x - BS), BS}, thumbSize), thumbSize.y / 2.0); } memdc.SetTextForeground(text_color.colorForStates(state ^ StateColor::Checked)); auto text_y = BS + (thumbSize.y - textSize[0].y) / 2; @@ -202,6 +204,184 @@ void SwitchButton::update() SetBitmap((GetValue() ? m_on : m_off).bmp()); } +ModeSwitchButton::ModeSwitchButton(wxWindow* parent, wxWindowID id) +{ + background_color = StateColor( + std::make_pair(wxColour(0xF1, 0xF1, 0xF1), (int) StateColor::Disabled), + std::make_pair(wxColour(0xE3, 0xE3, 0xE3), (int) StateColor::Pressed), + std::make_pair(wxColour(0xD9, 0xD9, 0xD9), (int) StateColor::Normal)); + border_color = StateColor( + std::make_pair(wxColour(0xEA, 0xEA, 0xEA), (int) StateColor::Disabled), + std::make_pair(wxColour(0xBC, 0xBC, 0xBC), (int) StateColor::Hovered), + std::make_pair(wxColour(0xC8, 0xC8, 0xC8), (int) StateColor::Focused), + std::make_pair(wxColour(0xCE, 0xCE, 0xCE), (int) StateColor::Normal)); + + StaticBox::Create(parent, id, wxDefaultPosition, wxDefaultSize, 0); + SetBackgroundColour(StaticBox::GetParentBackgroundColor(parent)); + SetCursor(wxCursor(wxCURSOR_HAND)); + + m_tooltips[0] = _L("Simple settings"); + m_tooltips[1] = _L("Advanced settings"); + m_tooltips[2] = _L("Expert settings"); + + Bind(wxEVT_LEFT_DOWN, &ModeSwitchButton::mouseDown, this); + Bind(wxEVT_LEFT_UP, &ModeSwitchButton::mouseReleased, this); + Bind(wxEVT_LEFT_DCLICK, &ModeSwitchButton::mouseDown, this); + Bind(wxEVT_MOUSE_CAPTURE_LOST, &ModeSwitchButton::mouseCaptureLost, this); + + Rescale(); +} + +void ModeSwitchButton::SetSelection(int selection) +{ + m_selection = std::clamp(selection, 0, 2); + update_tooltip(); + Refresh(); +} + +void ModeSwitchButton::SelectAndNotify(int selection) +{ + if (!IsEnabled()) + return; + + SetSelection(selection); + Slic3r::GUI::wxGetApp().save_mode(m_selection); +} + +void ModeSwitchButton::Rescale() +{ + const wxSize button_size = FromDIP(wxSize(48, 20)); + SetMinSize(button_size); + SetMaxSize(button_size); + SetSize(button_size); + SetCornerRadius(button_size.y / 2.0); + Refresh(); +} + +bool ModeSwitchButton::Enable(bool enable /* = true */) +{ + const bool changed = StaticBox::Enable(enable); + if (changed) + Refresh(); + return changed; +} + +void ModeSwitchButton::doRender(wxDC& dc) +{ + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(wxBrush(GetBackgroundColour())); + dc.DrawRectangle(GetClientRect()); + + const wxRect bounds = GetClientRect().Deflate(1); + if (bounds.width <= 0 || bounds.height <= 0) + return; + + const int states = state_handler.states(); + const bool hovered = (states & StateHandler::Hovered) != 0; + const bool focused = (states & StateHandler::Focused) != 0; + const bool disabled = !IsEnabled(); + + const wxColour track_fill = disabled ? wxColour(0xD0, 0xD0, 0xD4) : + m_pressed ? wxColour(0x5A, 0x5D, 0x64) : wxColour(0x66, 0x69, 0x70); + const wxColour track_border = disabled ? wxColour(0xDD, 0xDD, 0xE0) : + focused ? wxColour("#009688") : + hovered ? wxColour(0x7A, 0x7D, 0x84) : wxColour(0x75, 0x78, 0x7F); + const wxColour active_fill = disabled ? wxColour(0x9E, 0xBE, 0xB9) : + m_pressed ? wxColour(0x00877B) : wxColour("#009688"); + const wxColour active_dot = disabled ? wxColour(0xEC, 0xF4, 0xF2) : wxColour(0xB7, 0xEB, 0xE3); + const wxColour inactive_dot = disabled ? wxColour(0xF2, 0xF2, 0xF4) : wxColour(0xB5, 0xB7, 0xBD); + const wxColour thumb_fill = disabled ? wxColour(0xFA, 0xFA, 0xFA) : *wxWHITE; + const wxColour thumb_border = disabled ? wxColour(0xE7, 0xE7, 0xEA) : wxColour(0xDD, 0xDF, 0xE3); + + dc.SetPen(wxPen(track_border, 1)); + dc.SetBrush(wxBrush(track_fill)); + dc.DrawRoundedRectangle(bounds, bounds.height / 2.0); + + const wxRect thumb = thumb_rect_for(m_selection); + const int fill_right = std::min(bounds.GetRight(), thumb.GetX() + thumb.GetWidth() / 2 + FromDIP(2)); + wxRect active(bounds.x, bounds.y, fill_right - bounds.x + 1, bounds.height); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(wxBrush(active_fill)); + dc.DrawRoundedRectangle(active, bounds.height / 2.0); + + const int dot_radius = std::max(FromDIP(1), thumb.height / 7); + for (int idx = 0; idx < 3; ++idx) { + if (idx == m_selection) + continue; + + const wxRect slot = thumb_rect_for(idx); + const wxPoint center(slot.GetX() + slot.GetWidth() / 2, slot.GetY() + slot.GetHeight() / 2); + dc.SetBrush(wxBrush(idx < m_selection ? active_dot : inactive_dot)); + dc.DrawCircle(center, dot_radius); + } + + dc.SetPen(wxPen(thumb_border, 1)); + dc.SetBrush(wxBrush(thumb_fill)); + dc.DrawRoundedRectangle(thumb, thumb.height / 2.0); +} + +void ModeSwitchButton::mouseDown(wxMouseEvent& event) +{ + if (!IsEnabled()) { + event.Skip(); + return; + } + + m_pressed = true; + if (!HasCapture()) + CaptureMouse(); + + Refresh(); + + event.Skip(); +} + +void ModeSwitchButton::mouseReleased(wxMouseEvent& event) +{ + if (m_pressed) { + m_pressed = false; + if (HasCapture()) + ReleaseMouse(); + + if (GetClientRect().Contains(event.GetPosition())) + SelectAndNotify(hit_test_selection(event.GetPosition())); + + Refresh(); + } + + event.Skip(); +} + +void ModeSwitchButton::mouseCaptureLost(wxMouseCaptureLostEvent& event) +{ + m_pressed = false; + Refresh(); + event.Skip(); +} + +int ModeSwitchButton::hit_test_selection(const wxPoint& point) const +{ + const int width = std::max(1, GetClientSize().x); + const int x = std::clamp(point.x, 0, width - 1); + return std::clamp((x * 3) / width, 0, 2); +} + +wxRect ModeSwitchButton::thumb_rect_for(int selection) const +{ + const wxRect bounds = GetClientRect().Deflate(3); + const int thumb_diameter = std::max(FromDIP(10), bounds.height - FromDIP(2)); + const int y = bounds.y + (bounds.height - thumb_diameter) / 2; + + const int travel = std::max(0, bounds.width - thumb_diameter); + const int x = bounds.x + (travel * std::clamp(selection, 0, 2)) / 2; + return wxRect(x, y, thumb_diameter, thumb_diameter); +} + +void ModeSwitchButton::update_tooltip() +{ + SetToolTip(m_tooltips[m_selection]); +} + SwitchBoard::SwitchBoard(wxWindow *parent, wxString leftL, wxString right, wxSize size) : wxWindow(parent, wxID_ANY, wxDefaultPosition, size) { @@ -355,24 +535,14 @@ void SwitchBoard::on_left_down(wxMouseEvent &evt) wxPostEvent(this, event); } -void SwitchBoard::Enable() +bool SwitchBoard::Enable(bool enable /* = true */) { - if (is_enable == true) + if (is_enable == enable) { - return; + return false; } - is_enable = true; - Refresh(); -} - -void SwitchBoard::Disable() -{ - if (is_enable == false) - { - return; - } - - is_enable = false; + is_enable = enable; Refresh(); + return true; } \ No newline at end of file diff --git a/src/slic3r/GUI/Widgets/SwitchButton.hpp b/src/slic3r/GUI/Widgets/SwitchButton.hpp index 18a2a2c55d..077ee86597 100644 --- a/src/slic3r/GUI/Widgets/SwitchButton.hpp +++ b/src/slic3r/GUI/Widgets/SwitchButton.hpp @@ -3,10 +3,9 @@ #include "../wxExtensions.hpp" #include "StateColor.hpp" +#include "StaticBox.hpp" #include -#include "Label.hpp" -#include "Button.hpp" wxDECLARE_EVENT(wxCUSTOMEVT_SWITCH_POS, wxCommandEvent); @@ -46,6 +45,37 @@ private: StateColor thumb_color; }; +class ModeSwitchButton : public StaticBox +{ +public: + ModeSwitchButton(wxWindow* parent = nullptr, wxWindowID id = wxID_ANY); + + int GetSelection() const { return m_selection; } + void SetSelection(int selection); + void SelectAndNotify(int selection); + + void Rescale(); + void msw_rescale() { Rescale(); } + + bool Enable(bool enable = true) override; + +protected: + void doRender(wxDC& dc) override; + +private: + void mouseDown(wxMouseEvent& event); + void mouseReleased(wxMouseEvent& event); + void mouseCaptureLost(wxMouseCaptureLostEvent& event); + int hit_test_selection(const wxPoint& point) const; + wxRect thumb_rect_for(int selection) const; + void update_tooltip(); + +private: + int m_selection { 0 }; + bool m_pressed { false }; + wxString m_tooltips[3]; +}; + class SwitchBoard : public wxWindow { public: @@ -62,8 +92,8 @@ public: void* client_data = nullptr;/*MachineObject* in StatusPanel*/ public: - void Enable(); - void Disable(); + bool Enable(bool enable = true) override; + bool Disable() { return Enable(false); } bool IsEnabled(){return is_enable;}; void SetClientData(void* data) { client_data = data; }; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index ee256a9ace..66ad109fab 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -413,15 +413,6 @@ int em_unit(wxWindow* win) return Slic3r::GUI::wxGetApp().em_unit(); } -int mode_icon_px_size() -{ -#ifdef __APPLE__ - return 10; -#else - return 12; -#endif -} - wxBitmap create_menu_bitmap(const std::string& bmp_name) { return create_scaled_bitmap(bmp_name, nullptr, 16, false, "", true); @@ -895,141 +886,6 @@ void LockButton::update_button_bitmaps() -// ---------------------------------------------------------------------------- -// ModeButton -// ---------------------------------------------------------------------------- - -ModeButton::ModeButton( wxWindow * parent, - wxWindowID id, - const std::string& icon_name /* = ""*/, - const wxString& mode /* = wxEmptyString*/, - const wxSize& size /* = wxDefaultSize*/, - const wxPoint& pos /* = wxDefaultPosition*/) : - ScalableButton(parent, id, icon_name, mode, size, pos, wxBU_EXACTFIT) -{ - Init(mode); -} - -ModeButton::ModeButton( wxWindow* parent, - const wxString& mode/* = wxEmptyString*/, - const std::string& icon_name/* = ""*/, - int px_cnt/* = 16*/) : - ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT) -{ - Init(mode); -} - -void ModeButton::Init(const wxString &mode) -{ - std::string mode_str = std::string(mode.ToUTF8()); - //m_tt_focused = Slic3r::GUI::from_u8((boost::format(_utf8(L("Switch to the %s mode"))) % mode_str).str()); - //m_tt_selected = Slic3r::GUI::from_u8((boost::format(_utf8(L("Current mode is %s"))) % mode_str).str()); - - SetBitmapMargins(3, 0); - - //button events - Bind(wxEVT_BUTTON, &ModeButton::OnButton, this); - Bind(wxEVT_ENTER_WINDOW, &ModeButton::OnEnterBtn, this); - Bind(wxEVT_LEAVE_WINDOW, &ModeButton::OnLeaveBtn, this); -} - -void ModeButton::OnButton(wxCommandEvent& event) -{ - m_is_selected = true; - focus_button(m_is_selected); - - event.Skip(); -} - -void ModeButton::SetState(const bool state) -{ - m_is_selected = state; - focus_button(m_is_selected); - SetToolTip(state ? m_tt_selected : m_tt_focused); -} - -void ModeButton::focus_button(const bool focus) -{ - const wxFont& new_font = focus ? - Slic3r::GUI::wxGetApp().bold_font() : - Slic3r::GUI::wxGetApp().normal_font(); - - SetFont(new_font); -#ifdef _WIN32 - GetParent()->Refresh(); // force redraw a background of the selected mode button -#else - SetForegroundColour(wxSystemSettings::GetColour(focus ? wxSYS_COLOUR_BTNTEXT : -#if defined (__linux__) && defined (__WXGTK3__) - wxSYS_COLOUR_GRAYTEXT -#elif defined (__linux__) && defined (__WXGTK2__) - wxSYS_COLOUR_BTNTEXT -#else - wxSYS_COLOUR_BTNSHADOW -#endif - )); -#endif /* no _WIN32 */ - - Refresh(); - Update(); -} - - -// ---------------------------------------------------------------------------- -// ModeSizer -// ---------------------------------------------------------------------------- - -ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) : - wxFlexGridSizer(3, 0, hgap), - m_parent(parent), - m_hgap_unscaled((double)(hgap)/em_unit(parent)) -{ - SetFlexibleDirection(wxHORIZONTAL); - - std::vector < std::pair < wxString, std::string >> buttons = { - //{_(L("Simple")), "mode_simple"}, - //{_(L("Advanced")), "mode_advanced"}, - //{_CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), "mode_advanced"} - }; - - auto modebtnfn = [](wxCommandEvent &event, int mode_id) { - Slic3r::GUI::wxGetApp().save_mode(mode_id); - event.Skip(); - }; - - m_mode_btns.reserve(3); - for (const auto& button : buttons) { - m_mode_btns.push_back(new ModeButton(parent, button.first, button.second, mode_icon_px_size())); - - m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, int(m_mode_btns.size() - 1))); - Add(m_mode_btns.back()); - } -} - -void ModeSizer::SetMode(const int mode) -{ - for (size_t m = 0; m < m_mode_btns.size(); m++) - m_mode_btns[m]->SetState(int(m) == mode); -} - -void ModeSizer::set_items_flag(int flag) -{ - for (wxSizerItem* item : this->GetChildren()) - item->SetFlag(flag); -} - -void ModeSizer::set_items_border(int border) -{ - for (wxSizerItem* item : this->GetChildren()) - item->SetBorder(border); -} - -void ModeSizer::msw_rescale() -{ - this->SetHGap(std::lround(m_hgap_unscaled * em_unit(m_parent))); - for (size_t m = 0; m < m_mode_btns.size(); m++) - m_mode_btns[m]->msw_rescale(); -} - // ---------------------------------------------------------------------------- // MenuWithSeparators // ---------------------------------------------------------------------------- diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 4681fb4822..502614eb92 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -52,7 +52,6 @@ class wxDialog; void edit_tooltip(wxString& tooltip); void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector& btn_ids); int em_unit(wxWindow* win); -int mode_icon_px_size(); wxBitmap create_menu_bitmap(const std::string& bmp_name); @@ -285,76 +284,6 @@ private: }; -// ---------------------------------------------------------------------------- -// ModeButton -// ---------------------------------------------------------------------------- - -class ModeButton : public ScalableButton -{ -public: - ModeButton( - wxWindow* parent, - wxWindowID id, - const std::string& icon_name = "", - const wxString& mode = wxEmptyString, - const wxSize& size = wxDefaultSize, - const wxPoint& pos = wxDefaultPosition); - - ModeButton( - wxWindow* parent, - const wxString& mode = wxEmptyString, - const std::string& icon_name = "", - int px_cnt = 16); - - ~ModeButton() {} - - void Init(const wxString& mode); - - void OnButton(wxCommandEvent& event); - void OnEnterBtn(wxMouseEvent& event) { focus_button(true); event.Skip(); } - void OnLeaveBtn(wxMouseEvent& event) { focus_button(m_is_selected); event.Skip(); } - - void SetState(const bool state); - bool is_selected() { return m_is_selected; } - -protected: - void focus_button(const bool focus); - -private: - bool m_is_selected = false; - - wxString m_tt_selected; - wxString m_tt_focused; -}; - - - -// ---------------------------------------------------------------------------- -// ModeSizer -// ---------------------------------------------------------------------------- - -class ModeSizer : public wxFlexGridSizer -{ -public: - ModeSizer( wxWindow *parent, int hgap = 0); - ~ModeSizer() {} - - void SetMode(const /*ConfigOptionMode*/int mode); - - void set_items_flag(int flag); - void set_items_border(int border); - - void msw_rescale(); - const std::vector& get_btns() { return m_mode_btns; } - -private: - std::vector m_mode_btns; - wxWindow* m_parent {nullptr}; - double m_hgap_unscaled; -}; - - - // ---------------------------------------------------------------------------- // MenuWithSeparators // ----------------------------------------------------------------------------