Preserving state of publish dialog

This commit is contained in:
Lam Wei Lun
2026-09-02 10:45:47 +08:00
parent d00af63a61
commit f85902b0ce
5 changed files with 153 additions and 3 deletions

View File

@@ -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<std::string> pending_keys;
std::vector<Slic3r::PublishedMaterialEntry> 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());
}

View File

@@ -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<std::string> m_pending_published_keys;
std::vector<Slic3r::PublishedMaterialEntry> m_pending_material_keys;
// Data
Slic3r::DynamicPrintConfig* config; // FIXME: leak?
Slic3r::Print fff_print;
@@ -9087,6 +9093,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& 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<std::string>& published_keys,
return wxID_YES;
}
bool Plater::get_pending_published(std::vector<std::string>& out_keys,
std::vector<Slic3r::PublishedMaterialEntry>& 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<std::string>& published_keys,
const std::vector<Slic3r::PublishedMaterialEntry>& 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)

View File

@@ -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<std::string>& published_keys, const std::vector<Slic3r::PublishedMaterialEntry>& 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<std::string>& out_keys, std::vector<Slic3r::PublishedMaterialEntry>& out_material) const;
void set_pending_published(const std::vector<std::string>& published_keys, const std::vector<Slic3r::PublishedMaterialEntry>& material_keys);
static TriangleMesh combine_mesh_fff(const ModelObject& mo, int instance_id, std::function<void(const std::string&)> notify_func = {});
void export_stl(bool extended = false, bool selection_only = false, bool multi_stls = false, FileType file_type = FT_STL);
//BBS: remove amf

View File

@@ -434,7 +434,8 @@ PublishSettingsDialog::MixedVisualSpec PublishSettingsDialog::make_mixed_visual_
return spec;
}
PublishSettingsDialog::PublishSettingsDialog(wxWindow* parent)
PublishSettingsDialog::PublishSettingsDialog(wxWindow* parent, const std::vector<std::string>* published_keys,
const std::vector<Slic3r::PublishedMaterialEntry>* material_keys)
: DPIDialog(parent ? parent : static_cast<wxWindow*>(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<std::string>(),
material_keys != nullptr ? *material_keys : std::vector<Slic3r::PublishedMaterialEntry>());
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<std::string>& published_keys,
const std::vector<Slic3r::PublishedMaterialEntry>& 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<std::string> PublishSettingsDialog::GetPublishedKeys() const
{
std::vector<std::string> out;

View File

@@ -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<std::string>* published_keys = nullptr,
const std::vector<Slic3r::PublishedMaterialEntry>* 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<std::string>& published_keys,
const std::vector<Slic3r::PublishedMaterialEntry>& 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