From 93a81179a12176a2327b73b7bf8cca2aade59e51 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Fri, 10 Jul 2026 15:41:57 +0800 Subject: [PATCH] feat(gui): add purge mode selector dialog The engine already implements prime_volume_mode (Default/Saving/Fast) but nothing in the UI could set it, leaving prime-saving unreachable on multi-sub-nozzle extruders and fast purge unreachable on printers that support it. - new PurgeModeDialog with selectable Standard/Fast or Standard/Prime Saving cards depending on printer capability - "Purge mode" sidebar button next to Flushing volumes; opens the dialog and stores the choice in the project config - printer preset-load gating: button shown only when the printer has multiple sub-nozzles per extruder or sets support_fast_purge_mode; stale project values the printer cannot honor reset to Default - enable fast purge on A2L 0.4 (support_fast_purge_mode), explicit default 0 in the common machine base - new dialog strings added to OrcaSlicer.pot Printers without these capabilities never show the button and their projects keep prime_volume_mode at Default, so slicing output is unchanged. --- localization/i18n/OrcaSlicer.pot | 18 ++ resources/images/completed_2.svg | 4 + resources/images/leaf.svg | 4 + resources/images/shield.svg | 4 + .../BBL/machine/Bambu Lab A2L 0.4 nozzle.json | 1 + .../BBL/machine/fdm_machine_common.json | 1 + src/libslic3r/PrintConfig.cpp | 3 +- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Plater.cpp | 38 ++- src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/PurgeModeDialog.cpp | 269 ++++++++++++++++++ src/slic3r/GUI/PurgeModeDialog.hpp | 69 +++++ src/slic3r/GUI/Tab.cpp | 21 ++ 13 files changed, 432 insertions(+), 3 deletions(-) create mode 100644 resources/images/completed_2.svg create mode 100644 resources/images/leaf.svg create mode 100644 resources/images/shield.svg create mode 100644 src/slic3r/GUI/PurgeModeDialog.cpp create mode 100644 src/slic3r/GUI/PurgeModeDialog.hpp diff --git a/localization/i18n/OrcaSlicer.pot b/localization/i18n/OrcaSlicer.pot index c3ca1cbf96..5f77005177 100644 --- a/localization/i18n/OrcaSlicer.pot +++ b/localization/i18n/OrcaSlicer.pot @@ -18475,6 +18475,24 @@ msgstr "" msgid "Whether this printer supports fast purge mode with optimized temperature and multiplier." msgstr "" +msgid "Purge mode" +msgstr "" + +msgid "Purge Mode Settings" +msgstr "" + +msgid "Prime Saving" +msgstr "" + +msgid "Perform full purging with speed and temperature transition for the best print quality." +msgstr "" + +msgid "Uses optimized purge temperature, multiplier and stronger extrusion for faster purging. May cause slight color mixing in some extreme cases." +msgstr "" + +msgid "Reduces prime waste and prints faster. May cause slight color mixing or small surface defects." +msgstr "" + msgid "Deretraction speed (extruder change)" msgstr "" diff --git a/resources/images/completed_2.svg b/resources/images/completed_2.svg new file mode 100644 index 0000000000..99b723b387 --- /dev/null +++ b/resources/images/completed_2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/leaf.svg b/resources/images/leaf.svg new file mode 100644 index 0000000000..48a3acf795 --- /dev/null +++ b/resources/images/leaf.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/shield.svg b/resources/images/shield.svg new file mode 100644 index 0000000000..643ada60ad --- /dev/null +++ b/resources/images/shield.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/profiles/BBL/machine/Bambu Lab A2L 0.4 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab A2L 0.4 nozzle.json index e9719007f4..93bf4afd83 100644 --- a/resources/profiles/BBL/machine/Bambu Lab A2L 0.4 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab A2L 0.4 nozzle.json @@ -87,6 +87,7 @@ "retract_lift_below": [ "326" ], + "support_fast_purge_mode": "1", "support_object_skip_flush": "1", "upward_compatible_machine": [ "Bambu Lab H2S 0.4 nozzle" diff --git a/resources/profiles/BBL/machine/fdm_machine_common.json b/resources/profiles/BBL/machine/fdm_machine_common.json index ed8e87d18d..88d8cbd96c 100644 --- a/resources/profiles/BBL/machine/fdm_machine_common.json +++ b/resources/profiles/BBL/machine/fdm_machine_common.json @@ -141,6 +141,7 @@ "single_extruder_multi_material": "1", "support_air_filtration": "0", "support_chamber_temp_control": "0", + "support_fast_purge_mode": "0", "support_object_skip_flush": "0", "wipe": [ "1" diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 5e3e3d55d9..45c5b3df45 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -7882,7 +7882,8 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionIntsNullable{ 1 }); // Printer flag gating the fast-purge mode selector in the Multi-Filament - // UI. comDevelop, default false; no shipping profile sets it, so the UI stays hidden. + // UI. comDevelop, default false; profiles of printers that support fast purge + // enable it, which shows the purge-mode selector for those printers. def = this->add("support_fast_purge_mode", coBool); def->label = L("Support fast purge mode"); def->tooltip = L("Whether this printer supports fast purge mode with optimized temperature and multiplier."); diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 71d1c3eba0..78ceb762a1 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -411,6 +411,8 @@ set(SLIC3R_GUI_SOURCES GUI/Project.hpp GUI/PublishDialog.cpp GUI/PublishDialog.hpp + GUI/PurgeModeDialog.cpp + GUI/PurgeModeDialog.hpp GUI/RammingChart.cpp GUI/RammingChart.hpp GUI/RecenterDialog.cpp diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ad134dc42b..de9e80afc8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -170,6 +170,7 @@ #include "StepMeshDialog.hpp" #include "FilamentMapDialog.hpp" #include "CloneDialog.hpp" +#include "PurgeModeDialog.hpp" #include "DeviceCore/DevFilaSystem.h" #include "DeviceCore/DevManager.h" @@ -551,6 +552,7 @@ struct Sidebar::priv wxStaticLine* m_staticline2; wxPanel* m_panel_project_title; ScalableButton* m_filament_icon = nullptr; + Button * m_purge_mode_btn = nullptr; Button * m_flushing_volume_btn = nullptr; TextInput* m_search_item = nullptr; StaticBox* m_search_bar = nullptr; @@ -2663,12 +2665,13 @@ Sidebar::Sidebar(Plater *parent) p->m_panel_filament_title->SetBackgroundColor(title_bg); p->m_panel_filament_title->SetBackgroundColor2(0xF1F1F1); p->m_panel_filament_title->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &e) { - if (!p || !p->m_panel_filament_content || !m_scrolled_sizer || !p->m_bpButton_set_filament || !p->m_flushing_volume_btn || !p->m_bpButton_add_filament || !ams_btn) + if (!p || !p->m_panel_filament_content || !m_scrolled_sizer || !p->m_bpButton_set_filament || !p->m_purge_mode_btn || !p->m_flushing_volume_btn || !p->m_bpButton_add_filament || !ams_btn) return; // ORCA exclude area of del button from titlebar collapse/expand feature to fix undesired collapse when user spams del filament button // also block fold/unfold feature when user clicks to spacing between icons int exclude_pt = p->m_bpButton_set_filament->GetPosition().x; // maximum fixed item - if (p->m_flushing_volume_btn->IsShown()) exclude_pt = p->m_flushing_volume_btn->GetPosition().x; + if (p->m_purge_mode_btn->IsShown()) exclude_pt = p->m_purge_mode_btn->GetPosition().x; + else if (p->m_flushing_volume_btn->IsShown()) exclude_pt = p->m_flushing_volume_btn->GetPosition().x; else if (p->m_bpButton_add_filament->IsShown()) exclude_pt = p->m_bpButton_add_filament->GetPosition().x - FromDIP(30); // reserve spacing for delete button else if (ams_btn->IsShown()) exclude_pt = ams_btn->GetPosition().x; if (e.GetPosition().x > exclude_pt) @@ -2703,6 +2706,24 @@ Sidebar::Sidebar(Plater *parent) bSizer39->AddStretchSpacer(1); + p->m_purge_mode_btn = new Button(p->m_panel_filament_title, _L("Purge mode")); + p->m_purge_mode_btn->SetStyle(ButtonStyle::Confirm, ButtonType::Compact); + + p->m_purge_mode_btn->Bind(wxEVT_BUTTON, [](wxCommandEvent &e) { + auto &preset_bundle = *wxGetApp().preset_bundle; + auto support_fast_purge_opt = preset_bundle.printers.get_edited_preset().config.option("support_fast_purge_mode"); + bool support_fast_purge = support_fast_purge_opt ? support_fast_purge_opt->value : false; + auto dlg_type = support_fast_purge ? PurgeModeDialogType::FastMode : PurgeModeDialogType::MultiNozzle; + PurgeModeDialog dlg(static_cast(wxGetApp().mainframe), dlg_type); + if (dlg.ShowModal() == wxID_OK) { + preset_bundle.project_config.set_key_value("prime_volume_mode", new ConfigOptionEnum(dlg.get_selected_mode())); + wxGetApp().plater()->update(); + } + }); + + bSizer39->Add(p->m_purge_mode_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(4)); + bSizer39->Hide(p->m_purge_mode_btn); // hidden on launch; shown only for printers that support purge mode selection + // BBS // add wiping dialog //wiping_dialog_button->SetFont(wxGetApp().normal_font()); @@ -3572,6 +3593,7 @@ void Sidebar::msw_rescale() p->m_bpButton_del_filament->msw_rescale(); p->m_bpButton_ams_filament->msw_rescale(); p->m_bpButton_set_filament->msw_rescale(); + p->m_purge_mode_btn->Rescale(); p->m_flushing_volume_btn->Rescale(); set_flushing_volume_warning(is_flush_config_modified()); // ORCA reapply appearance @@ -3657,6 +3679,7 @@ void Sidebar::sys_color_changed() p->m_bpButton_del_filament->msw_rescale(); p->m_bpButton_ams_filament->msw_rescale(); p->m_bpButton_set_filament->msw_rescale(); + p->m_purge_mode_btn->Rescale(); p->m_flushing_volume_btn->Rescale(); set_flushing_volume_warning(is_flush_config_modified()); // ORCA reapply appearance @@ -4426,6 +4449,17 @@ void Sidebar::show_SEMM_buttons() Layout(); } +void Sidebar::enable_purge_mode_btn(bool enable) +{ + if (!p || !p->m_purge_mode_btn) + return; + p->m_purge_mode_btn->Show(enable); + wxGetApp().CallAfter([this]() { + p->m_panel_filament_title->Layout(); + this->Layout(); + }); +} + void Sidebar::update_dynamic_filament_list() { dynamic_filament_list.update(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 1234cac325..e96dd7804f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -208,6 +208,7 @@ public: // Orca static bool should_show_SEMM_buttons(); void show_SEMM_buttons(); + void enable_purge_mode_btn(bool enable); void update_dynamic_filament_list(); PlaterPresetComboBox * printer_combox(); diff --git a/src/slic3r/GUI/PurgeModeDialog.cpp b/src/slic3r/GUI/PurgeModeDialog.cpp new file mode 100644 index 0000000000..b3191a04e5 --- /dev/null +++ b/src/slic3r/GUI/PurgeModeDialog.cpp @@ -0,0 +1,269 @@ +#include "PurgeModeDialog.hpp" + +#include +#include +#include +#include +#include + +#include "I18N.hpp" +#include "GUI_App.hpp" +#include "wxExtensions.hpp" +#include "Widgets/Button.hpp" +#include "libslic3r/Utils.hpp" + +namespace Slic3r { namespace GUI { + +static const wxColour BgNormalColor = wxColour("#FFFFFF"); +static const wxColour BgSelectColor = wxColour("#EBF9F0"); + +static const wxColour BorderNormalColor = wxColour("#CECECE"); +static const wxColour BorderSelectedColor = wxColour("#00AE42"); + +static const wxColour TextNormalBlackColor = wxColour("#262E30"); +static const wxColour TextNormalGreyColor = wxColour("#6B6B6B"); + +PurgeModeDialog::PurgeModeDialog(wxWindow *parent, PurgeModeDialogType dialog_type) + : DPIDialog(parent, wxID_ANY, _L("Purge Mode Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_dialog_type(dialog_type) +{ + SetBackgroundColour(*wxWHITE); + SetMinSize(wxSize(FromDIP(520), FromDIP(320))); + SetMaxSize(wxSize(FromDIP(520), FromDIP(320))); + std::string icon_path = (boost::format("%1%/images/OrcaSlicerTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + + auto options_panel = new wxPanel(this); + auto options_sizer = new wxBoxSizer(wxVERTICAL); + auto panels_sizer = new wxBoxSizer(wxHORIZONTAL); + + auto &preset_bundle = *wxGetApp().preset_bundle; + auto current_mode = preset_bundle.project_config.option>("prime_volume_mode"); + PrimeVolumeMode mode = current_mode ? current_mode->value : pvmDefault; + m_selected_mode = mode; + + bool is_fast_mode = (m_dialog_type == PurgeModeDialogType::FastMode); + + m_standard_panel = new PurgeModeBtnPanel(options_panel, _L("Standard"), _L("Perform full purging with speed and temperature transition for the best print quality."), + "shield"); + m_standard_panel->SetMinSize(wxSize(FromDIP(200), FromDIP(150))); + m_standard_panel->Select(mode == pvmDefault); + m_standard_panel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &) { select_option(pvmDefault); }); + panels_sizer->Add(m_standard_panel, 1, wxEXPAND | wxRIGHT, FromDIP(12)); + if (is_fast_mode) { + m_saving_panel = new PurgeModeBtnPanel(options_panel, _L("Fast"), + _L("Uses optimized purge temperature, multiplier and stronger extrusion for faster purging. May cause slight color mixing in some extreme cases."), "leaf"); + m_saving_panel->SetMinSize(wxSize(FromDIP(200), FromDIP(150))); + m_saving_panel->Select(mode == pvmFast); + m_saving_panel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &) { select_option(pvmFast); }); + } else { + m_saving_panel = new PurgeModeBtnPanel(options_panel, _L("Prime Saving"), + _L("Reduces prime waste and prints faster. May cause slight color mixing or small surface defects."), "leaf"); + m_saving_panel->SetMinSize(wxSize(FromDIP(200), FromDIP(150))); + m_saving_panel->Select(mode == pvmSaving); + m_saving_panel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &) { select_option(pvmSaving); }); + } + + panels_sizer->Add(m_saving_panel, 1, wxEXPAND | wxLEFT, FromDIP(12)); + + options_sizer->Add(panels_sizer, 0, wxEXPAND | wxALL, FromDIP(20)); + + options_panel->SetSizer(options_sizer); + main_sizer->Add(options_panel, 1, wxEXPAND); + + auto btn_sizer = new wxBoxSizer(wxHORIZONTAL); + btn_sizer->AddStretchSpacer(); + + auto ok_btn = new Button(this, _L("Confirm")); + ok_btn->SetStyle(ButtonStyle::Confirm, ButtonType::Compact); + ok_btn->SetId(wxID_OK); + + auto cancel_btn = new Button(this, _L("Cancel")); + cancel_btn->SetStyle(ButtonStyle::Regular, ButtonType::Compact); + cancel_btn->SetId(wxID_CANCEL); + + btn_sizer->Add(ok_btn, 0, wxRIGHT, FromDIP(12)); + btn_sizer->Add(cancel_btn, 0); + + main_sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, FromDIP(20)); + + SetSizer(main_sizer); + Fit(); + CenterOnParent(); + + wxGetApp().UpdateDlgDarkUI(this); +} + +void PurgeModeDialog::select_option(PrimeVolumeMode mode) +{ + m_selected_mode = mode; + update_panel_selection(); +} + +void PurgeModeDialog::update_panel_selection() +{ + if (m_selected_mode == pvmDefault) { + m_standard_panel->Select(true); + m_saving_panel->Select(false); + } else { + m_standard_panel->Select(false); + m_saving_panel->Select(true); + } +} + +void PurgeModeDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + const int &em = em_unit(); + + msw_buttons_rescale(this, em, {wxID_OK, wxID_CANCEL}); + + const wxSize &size = wxSize(70 * em, 32 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +PurgeModeBtnPanel::PurgeModeBtnPanel(wxWindow *parent, const wxString &label, const wxString &detail, const std::string &icon_path) : wxPanel(parent) +{ + SetBackgroundColour(*wxWHITE); + SetBackgroundStyle(wxBG_STYLE_PAINT); + + const int horizontal_margin = FromDIP(12); + + auto sizer = new wxBoxSizer(wxVERTICAL); + + icon = create_scaled_bitmap(icon_path, nullptr, 20); + m_btn = new wxStaticBitmap(this, wxID_ANY, icon, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); + + check_icon = create_scaled_bitmap("completed_2", nullptr, 20); + m_check_btn = new wxStaticBitmap(this, wxID_ANY, check_icon, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); + m_check_btn->Hide(); + + auto icon_sizer = new wxBoxSizer(wxHORIZONTAL); + icon_sizer->Add(m_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, horizontal_margin); + icon_sizer->AddStretchSpacer(); + icon_sizer->Add(m_check_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, horizontal_margin); + + m_label = new wxStaticText(this, wxID_ANY, label); + m_label->SetFont(Label::Head_14); + m_label->SetForegroundColour(TextNormalBlackColor); + + auto label_sizer = new wxBoxSizer(wxHORIZONTAL); + label_sizer->Add(m_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, horizontal_margin); + + auto detail_sizer = new wxBoxSizer(wxHORIZONTAL); + m_detail = new Label(this, detail); + m_detail->SetFont(Label::Body_12); + m_detail->SetForegroundColour(TextNormalGreyColor); + m_detail->Wrap(FromDIP(200)); + detail_sizer->Add(m_detail, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, horizontal_margin); + + sizer->AddSpacer(FromDIP(15)); + sizer->Add(icon_sizer, 0, wxEXPAND); + sizer->AddSpacer(FromDIP(10)); + sizer->Add(label_sizer, 0, wxEXPAND); + sizer->AddSpacer(FromDIP(6)); + sizer->Add(detail_sizer, 0, wxEXPAND); + sizer->AddSpacer(FromDIP(10)); + + SetSizer(sizer); + Layout(); + Fit(); + + wxGetApp().UpdateDarkUIWin(this); + + // Clicks on child widgets must select the whole card, so re-dispatch them on the panel. + auto forward_click_to_parent = [this](wxMouseEvent &) { + wxCommandEvent click_event(wxEVT_LEFT_DOWN, GetId()); + click_event.SetEventObject(this); + this->ProcessEvent(click_event); + }; + + m_btn->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent); + m_label->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent); + m_detail->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent); + + Bind(wxEVT_PAINT, &PurgeModeBtnPanel::OnPaint, this); + Bind(wxEVT_ENTER_WINDOW, &PurgeModeBtnPanel::OnEnterWindow, this); + Bind(wxEVT_LEAVE_WINDOW, &PurgeModeBtnPanel::OnLeaveWindow, this); +} + +void PurgeModeBtnPanel::OnPaint(wxPaintEvent &event) +{ + wxAutoBufferedPaintDC dc(this); + wxGraphicsContext *gc = wxGraphicsContext::Create(dc); + + if (gc) { + dc.Clear(); + wxRect rect = GetClientRect(); + gc->SetBrush(wxTransparentColour); + gc->DrawRoundedRectangle(0, 0, rect.width, rect.height, 0); + wxColour bg_color = m_selected ? BgSelectColor : BgNormalColor; + + wxColour border_color = m_hover || m_selected ? BorderSelectedColor : BorderNormalColor; + + bg_color = StateColor::darkModeColorFor(bg_color); + border_color = StateColor::darkModeColorFor(border_color); + gc->SetBrush(wxBrush(bg_color)); + gc->SetPen(wxPen(border_color, 1)); + gc->DrawRoundedRectangle(1, 1, rect.width - 2, rect.height - 2, 8); + delete gc; + } +} + +void PurgeModeBtnPanel::UpdateStatus() +{ + if (m_selected) { + m_btn->SetBackgroundColour(BgSelectColor); + m_label->SetBackgroundColour(BgSelectColor); + m_detail->SetBackgroundColour(BgSelectColor); + if (m_check_btn) { + m_check_btn->SetBackgroundColour(BgSelectColor); + m_check_btn->Show(); + } + } else { + m_btn->SetBackgroundColour(BgNormalColor); + m_label->SetBackgroundColour(BgNormalColor); + m_detail->SetBackgroundColour(BgNormalColor); + if (m_check_btn) { + m_check_btn->SetBackgroundColour(BgNormalColor); + m_check_btn->Hide(); + } + } + Layout(); + Refresh(); + wxGetApp().UpdateDarkUIWin(this); +} + +void PurgeModeBtnPanel::OnEnterWindow(wxMouseEvent &event) +{ + if (!m_hover) { + m_hover = true; + UpdateStatus(); + Refresh(); + event.Skip(); + } +} + +void PurgeModeBtnPanel::OnLeaveWindow(wxMouseEvent &event) +{ + if (m_hover) { + wxPoint pos = this->ScreenToClient(wxGetMousePosition()); + if (this->GetClientRect().Contains(pos)) return; + m_hover = false; + UpdateStatus(); + Refresh(); + event.Skip(); + } +} + +void PurgeModeBtnPanel::Select(bool selected) +{ + m_selected = selected; + UpdateStatus(); + Refresh(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PurgeModeDialog.hpp b/src/slic3r/GUI/PurgeModeDialog.hpp new file mode 100644 index 0000000000..e5880e6ab1 --- /dev/null +++ b/src/slic3r/GUI/PurgeModeDialog.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#include "GUI_Utils.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "Widgets/Label.hpp" + +class wxStaticText; +class wxStaticBitmap; + +namespace Slic3r { + +namespace GUI { + +class PurgeModeBtnPanel : public wxPanel +{ +public: + PurgeModeBtnPanel(wxWindow *parent, const wxString &label, const wxString &detail, const std::string &icon_path); + void Select(bool selected); + +protected: + void OnPaint(wxPaintEvent &event); + +private: + void OnEnterWindow(wxMouseEvent &event); + void OnLeaveWindow(wxMouseEvent &event); + + void UpdateStatus(); + + wxBitmap icon; + wxBitmap check_icon; + + wxStaticBitmap *m_btn; + wxStaticBitmap *m_check_btn; + wxStaticText *m_label; + Label *m_detail; + bool m_hover{false}; + bool m_selected{false}; +}; + +enum class PurgeModeDialogType { + MultiNozzle, + FastMode +}; + +class PurgeModeDialog : public DPIDialog +{ +public: + PurgeModeDialog(wxWindow *parent, PurgeModeDialogType dialog_type = PurgeModeDialogType::MultiNozzle); + + PrimeVolumeMode get_selected_mode() const { return m_selected_mode; } + +protected: + void on_dpi_changed(const wxRect &suggested_rect) override; + +private: + void select_option(PrimeVolumeMode mode); + void update_panel_selection(); + + PurgeModeBtnPanel *m_standard_panel; + PurgeModeBtnPanel *m_saving_panel; + PrimeVolumeMode m_selected_mode; + PurgeModeDialogType m_dialog_type; +}; + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b724db1eca..0f4a0caf96 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -5656,6 +5656,27 @@ void TabPrinter::on_preset_loaded() if (wxGetApp().plater()) wxGetApp().plater()->sidebar().reset_fila_switch(); } + + // Purge mode selection is only meaningful for printers with multiple sub-nozzles per + // extruder (prime saving) or fast-purge support; reset stale project values that the + // current printer cannot honor and toggle the sidebar button accordingly. + auto extruder_max_nozzle_count = current_printer.config.option("extruder_max_nozzle_count"); + bool has_multiple_nozzle = extruder_max_nozzle_count && + std::any_of(extruder_max_nozzle_count->values.begin(), extruder_max_nozzle_count->values.end(), + [](int v) { return v > 1 && v != ConfigOptionIntsNullable::nil_value(); }); + auto support_fast_purge_opt = current_printer.config.option("support_fast_purge_mode"); + bool support_fast_purge = support_fast_purge_opt ? support_fast_purge_opt->value : false; + bool show_purge_mode = has_multiple_nozzle || support_fast_purge; + if (auto *prime_volume_mode = m_preset_bundle->project_config.option>("prime_volume_mode")) { + if (!show_purge_mode) + prime_volume_mode->value = PrimeVolumeMode::pvmDefault; + else if (!has_multiple_nozzle && prime_volume_mode->value == PrimeVolumeMode::pvmSaving) + prime_volume_mode->value = PrimeVolumeMode::pvmDefault; + else if (!support_fast_purge && prime_volume_mode->value == PrimeVolumeMode::pvmFast) + prime_volume_mode->value = PrimeVolumeMode::pvmDefault; + } + if (wxGetApp().plater()) + wxGetApp().plater()->sidebar().enable_purge_mode_btn(show_purge_mode); } void TabPrinter::update_pages()