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()