From f85902b0cea6f741b31dd1c488ce1a894eb11ec8 Mon Sep 17 00:00:00 2001 From: Lam Wei Lun Date: Wed, 2 Sep 2026 10:45:47 +0800 Subject: [PATCH] Preserving state of publish dialog --- src/slic3r/GUI/MainFrame.cpp | 8 ++- src/slic3r/GUI/Plater.cpp | 38 ++++++++++ src/slic3r/GUI/Plater.hpp | 4 ++ src/slic3r/GUI/PublishSettingsDialog.cpp | 91 +++++++++++++++++++++++- src/slic3r/GUI/PublishSettingsDialog.hpp | 15 +++- 5 files changed, 153 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 7d73d884b0..87e1271678 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1744,9 +1744,15 @@ void MainFrame::publish_project() { if (m_plater == nullptr) return; - PublishSettingsDialog dlg(this); + // Seed the dialog from the session selection (a remembered state or a freshly loaded + // published 3MF); a null pointer means "fresh", keeping the dirty defaults. + std::vector pending_keys; + std::vector pending_material; + const bool has_prior = m_plater->get_pending_published(pending_keys, pending_material); + PublishSettingsDialog dlg(this, has_prior ? &pending_keys : nullptr, has_prior ? &pending_material : nullptr); if (dlg.ShowModal() != wxID_OK) return; + m_plater->set_pending_published(dlg.GetPublishedKeys(), dlg.GetPublishedMaterialKeys()); m_plater->export_published_3mf(dlg.GetPublishedKeys(), dlg.GetPublishedMaterialKeys()); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7a3789bc96..b5233b123b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6750,6 +6750,12 @@ struct Plater::priv SendToPrinterDialog* m_send_to_sdcard_dlg = nullptr; PublishDialog* m_publish_dlg = nullptr; + // Session-level stash of the last published selection. Written on publish and on + // loading a published 3MF; read when the Publish dialog is opened. + bool m_has_pending_published{false}; + std::vector m_pending_published_keys; + std::vector m_pending_material_keys; + // Data Slic3r::DynamicPrintConfig* config; // FIXME: leak? Slic3r::Print fff_print; @@ -9087,6 +9093,15 @@ std::vector Plater::priv::load_files(const std::vector& input_ NotificationManager::NotificationLevel::WarningNotificationLevel); } + // Remember the imported published selection so the Publish dialog is + // pre-seeded with the file's settings. Stored after the load so any + // per-slot relocations are already reflected in material_keys. + if (published_config.published) { + this->m_has_pending_published = true; + this->m_pending_published_keys = published_config.published_keys; + this->m_pending_material_keys = published_config.material_keys; + } + ConfigOption* bed_type_opt = preset_bundle->project_config.option("curr_bed_type"); if (bed_type_opt != nullptr) { BedType bed_type = (BedType) bed_type_opt->getInt(); @@ -10290,6 +10305,11 @@ void Plater::priv::reset(bool apply_presets_change) clear_warnings(); + // A new project must not inherit the previous project's published selection (Feature A/B). + m_has_pending_published = false; + m_pending_published_keys.clear(); + m_pending_material_keys.clear(); + set_project_filename(""); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " call set_project_filename: empty"; @@ -18411,6 +18431,24 @@ int Plater::export_published_3mf(const std::vector& published_keys, return wxID_YES; } +bool Plater::get_pending_published(std::vector& out_keys, + std::vector& out_material) const +{ + if (!p->m_has_pending_published) + return false; + out_keys = p->m_pending_published_keys; + out_material = p->m_pending_material_keys; + return true; +} + +void Plater::set_pending_published(const std::vector& published_keys, + const std::vector& material_keys) +{ + p->m_has_pending_published = true; + p->m_pending_published_keys = published_keys; + p->m_pending_material_keys = material_keys; +} + Preset* get_printer_preset(const MachineObject* obj) { if (!obj) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 2f83da1cb9..26cd06978b 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -520,6 +520,10 @@ public: // Export a "published" 3MF embedding the author-selected settings in the file metadata; a // pure export that leaves the in-memory project untouched. int export_published_3mf(const std::vector& published_keys, const std::vector& material_keys); + // Session-level stash of the last published selection, seeded into the Publish dialog on + // open and written on publish or on loading a published 3MF + bool get_pending_published(std::vector& out_keys, std::vector& out_material) const; + void set_pending_published(const std::vector& published_keys, const std::vector& material_keys); static TriangleMesh combine_mesh_fff(const ModelObject& mo, int instance_id, std::function notify_func = {}); void export_stl(bool extended = false, bool selection_only = false, bool multi_stls = false, FileType file_type = FT_STL); //BBS: remove amf diff --git a/src/slic3r/GUI/PublishSettingsDialog.cpp b/src/slic3r/GUI/PublishSettingsDialog.cpp index 0e8c47b799..d4d9b5d5d2 100644 --- a/src/slic3r/GUI/PublishSettingsDialog.cpp +++ b/src/slic3r/GUI/PublishSettingsDialog.cpp @@ -434,7 +434,8 @@ PublishSettingsDialog::MixedVisualSpec PublishSettingsDialog::make_mixed_visual_ return spec; } -PublishSettingsDialog::PublishSettingsDialog(wxWindow* parent) +PublishSettingsDialog::PublishSettingsDialog(wxWindow* parent, const std::vector* published_keys, + const std::vector* material_keys) : DPIDialog(parent ? parent : static_cast(wxGetApp().mainframe), wxID_ANY, _L("Publish 3MF..."), @@ -522,6 +523,11 @@ PublishSettingsDialog::PublishSettingsDialog(wxWindow* parent) build_option_model(); + // Seed from a remembered session selection or a freshly loaded published 3MF (authoritative). + if (published_keys != nullptr || material_keys != nullptr) + apply_selection(published_keys != nullptr ? *published_keys : std::vector(), + material_keys != nullptr ? *material_keys : std::vector()); + auto dlg_btns = new DialogButtons(this, {"OK", "Cancel"}); dlg_btns->GetOK()->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { @@ -1691,6 +1697,89 @@ void PublishSettingsDialog::show_menu(wxMouseEvent& evt) PopupMenu(&m, local_pos); } +void PublishSettingsDialog::apply_selection(const std::vector& published_keys, + const std::vector& material_keys) +{ + // The supplied selection is authoritative: clear the dirty-default pre-check first so a + // dirty key the user deselected stays off, then re-select exactly what the selection names. + for (Row& row : m_rows) + if (row.check != nullptr) + row.check->SetValue(false); + for (Category& cat : m_categories) { + if (cat.section != Section::Material) + continue; + if (cat.enable_check != nullptr) + cat.enable_check->SetValue(false); + if (cat.full_check != nullptr) + cat.full_check->SetValue(false); + } + + // Print / printer rows: match by exact row key (the full "#N" opt_id where present). + for (const std::string& key : published_keys) + for (Row& row : m_rows) + if ((row.section == Section::Print || row.section == Section::Printer) && row.check != nullptr && row.key == key) { + row.check->SetValue(true); + break; + } + + // Per-slot material selections, applied positionally by slot. + for (const Slic3r::PublishedMaterialEntry& entry : material_keys) { + if (entry.slot < 0) + continue; + bool entry_mixed = false; + for (const std::string& k : entry.keys) + if (publish_mixed_keys().count(publish_base_key(k)) != 0) { + entry_mixed = true; + break; + } + size_t cat_idx = size_t(-1); + for (size_t c = 0; c < m_categories.size(); ++c) { + const Category& cat = m_categories[c]; + if (cat.section != Section::Material || cat.is_mixed != entry_mixed || cat.filament_slot != size_t(entry.slot)) + continue; + cat_idx = c; + break; + } + if (cat_idx == size_t(-1)) + continue; // slot not present in the receiver (out of range / skipped) + Category& cat = m_categories[cat_idx]; + if (cat.enable_check != nullptr) { + cat.enable_check->SetValue(true); + on_enable_toggle(cat_idx); + } + if (entry.full && cat.full_check != nullptr) { + cat.full_check->SetValue(true); + on_full_toggle(cat_idx); + } else { + // Setting rows are keyed by the base key. + for (const std::string& key : entry.keys) { + const std::string base = publish_base_key(key); + for (const size_t r : cat.rows) { + Row& row = m_rows[r]; + if (row.kind == RowKind::Setting && row.key == base) { + row.check->SetValue(true); + break; + } + } + } + if (entry.publish_type && !entry.publish_type_value.empty()) + for (const size_t r : cat.rows) + if (m_rows[r].kind == RowKind::Type && m_rows[r].check != nullptr) { + m_rows[r].check->SetValue(true); + break; + } + if (entry.publish_color && !entry.color.empty()) + for (const size_t r : cat.rows) + if (m_rows[r].kind == RowKind::Color && m_rows[r].check != nullptr) { + m_rows[r].check->SetValue(true); + break; + } + } + } + + apply_visibility(); +} + std::vector PublishSettingsDialog::GetPublishedKeys() const { std::vector out; diff --git a/src/slic3r/GUI/PublishSettingsDialog.hpp b/src/slic3r/GUI/PublishSettingsDialog.hpp index f2dcf70bba..8d9342fadb 100644 --- a/src/slic3r/GUI/PublishSettingsDialog.hpp +++ b/src/slic3r/GUI/PublishSettingsDialog.hpp @@ -46,7 +46,14 @@ struct MixedDependencyIssue class PublishSettingsDialog : public DPIDialog { public: - PublishSettingsDialog(wxWindow* parent = nullptr); + // Optional published selection (Feature A/B): when the caller supplies one (either a + // remembered session selection or the payload of a freshly loaded published 3MF) the dialog + // is seeded from it, overriding the dirty-default pre-check. A non-null pointer to an empty + // selection means "publish nothing" (an intentional empty state); a null pointer means "no + // remembered selection" (keep the dirty defaults). + PublishSettingsDialog(wxWindow* parent = nullptr, + const std::vector* published_keys = nullptr, + const std::vector* material_keys = nullptr); ~PublishSettingsDialog(); // The selected print/printer setting keys (in display order); printer keys carry a '#N' @@ -179,6 +186,12 @@ private: }; void build_option_model(); + // Seed the dialog from a published selection (print/printer keys + per-slot material keys): + // the supplied selection is authoritative - it is applied after the dirty pre-check and + // overrides it, so deselected dirty keys stay off. Rows/slots not present in the selection + // are left unselected. Unknown or out-of-range entries are skipped gracefully. + void apply_selection(const std::vector& published_keys, + const std::vector& material_keys); // Frozen snapshot of a mixed slot's definition for the page visualization, resolved from // the full config once at dialog-build time. Gradient slots pre-sample exactly what the // slicer will print: the custom curve wins over the gradient_range endpoints over the