Extended interface of project dirty state manager

This commit is contained in:
enricoturri1966 2021-04-06 13:17:29 +02:00
parent 144e37c274
commit 5d4b7c03b6
9 changed files with 219 additions and 6 deletions

View File

@ -904,6 +904,14 @@ bool GUI_App::on_init_inner()
} }
else else
load_current_presets(); load_current_presets();
#if ENABLE_PROJECT_DIRTY_STATE
if (plater_ != nullptr) {
// plater_->reset_project_initial_presets();
plater_->update_project_dirty_from_presets();
}
#endif // ENABLE_PROJECT_DIRTY_STATE
mainframe->Show(true); mainframe->Show(true);
obj_list()->set_min_height(); obj_list()->set_min_height();

View File

@ -7,6 +7,9 @@
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "Plater.hpp" #include "Plater.hpp"
#if ENABLE_PROJECT_DIRTY_STATE
#include "MainFrame.hpp"
#endif // ENABLE_PROJECT_DIRTY_STATE
#include "OptionsGroup.hpp" #include "OptionsGroup.hpp"
#include "Tab.hpp" #include "Tab.hpp"
@ -1457,12 +1460,15 @@ void ObjectList::load_shape_object(const std::string& type_name)
if (obj_idx < 0) if (obj_idx < 0)
return; return;
take_snapshot(_(L("Add Shape"))); take_snapshot(_L("Add Shape"));
// Create mesh // Create mesh
BoundingBoxf3 bb; BoundingBoxf3 bb;
TriangleMesh mesh = create_mesh(type_name, bb); TriangleMesh mesh = create_mesh(type_name, bb);
load_mesh_object(mesh, _(L("Shape")) + "-" + _(type_name)); load_mesh_object(mesh, _L("Shape") + "-" + _(type_name));
#if ENABLE_PROJECT_DIRTY_STATE
wxGetApp().mainframe->update_title();
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center) void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center)

View File

@ -206,6 +206,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
// declare events // declare events
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) { Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
#if ENABLE_PROJECT_DIRTY_STATE
if (m_plater != nullptr)
m_plater->save_project_if_dirty();
#endif // ENABLE_PROJECT_DIRTY_STATE
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) { if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
event.Veto(); event.Veto();
return; return;
@ -487,8 +492,14 @@ void MainFrame::update_title()
// m_plater->get_project_filename() produces file name including path, but excluding extension. // m_plater->get_project_filename() produces file name including path, but excluding extension.
// Don't try to remove the extension, it would remove part of the file name after the last dot! // Don't try to remove the extension, it would remove part of the file name after the last dot!
wxString project = from_path(into_path(m_plater->get_project_filename()).filename()); wxString project = from_path(into_path(m_plater->get_project_filename()).filename());
#if ENABLE_PROJECT_DIRTY_STATE
wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : "";
if (!dirty_marker.empty() || !project.empty())
title = dirty_marker + project + " - ";
#else
if (!project.empty()) if (!project.empty())
title += (project + " - "); title += (project + " - ");
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID; std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID;
@ -668,10 +679,36 @@ bool MainFrame::can_start_new_project() const
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty()); return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty());
} }
#if ENABLE_PROJECT_DIRTY_STATE
bool MainFrame::can_save() const
{
return (m_plater != nullptr) && !m_plater->model().objects.empty() && !m_plater->get_project_filename().empty() && m_plater->is_project_dirty();
}
bool MainFrame::can_save_as() const
{
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
void MainFrame::save_project()
{
save_project_as(m_plater->get_project_filename(".3mf"));
}
void MainFrame::save_project_as(const wxString& filename)
{
bool ret = (m_plater != nullptr) ? m_plater->export_3mf(into_path(filename)) : false;
if (ret) {
// wxGetApp().update_saved_preset_from_current_preset();
m_plater->reset_project_dirty_after_save();
}
}
#else
bool MainFrame::can_save() const bool MainFrame::can_save() const
{ {
return (m_plater != nullptr) && !m_plater->model().objects.empty(); return (m_plater != nullptr) && !m_plater->model().objects.empty();
} }
#endif // ENABLE_PROJECT_DIRTY_STATE
bool MainFrame::can_export_model() const bool MainFrame::can_export_model() const
{ {
@ -977,16 +1014,27 @@ void MainFrame::init_menubar_as_editor()
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId()); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
#if ENABLE_PROJECT_DIRTY_STATE
append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"),
[this](wxCommandEvent&) { save_project(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#else
append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"), append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, "save", nullptr, [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this); [this](){return m_plater != nullptr && can_save(); }, this);
#endif // ENABLE_PROJECT_DIRTY_STATE
#ifdef __APPLE__ #ifdef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"), append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"),
#else #else
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"), append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"),
#endif // __APPLE__ #endif // __APPLE__
#if ENABLE_PROJECT_DIRTY_STATE
[this](wxCommandEvent&) { save_project_as(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save_as(); }, this);
#else
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr, [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this); [this](){return m_plater != nullptr && can_save(); }, this);
#endif // ENABLE_PROJECT_DIRTY_STATE
fileMenu->AppendSeparator(); fileMenu->AppendSeparator();

View File

@ -91,7 +91,9 @@ class MainFrame : public DPIFrame
void on_value_changed(wxCommandEvent&); void on_value_changed(wxCommandEvent&);
bool can_start_new_project() const; bool can_start_new_project() const;
#if !ENABLE_PROJECT_DIRTY_STATE
bool can_save() const; bool can_save() const;
#endif // !ENABLE_PROJECT_DIRTY_STATE
bool can_export_model() const; bool can_export_model() const;
bool can_export_toolpaths() const; bool can_export_toolpaths() const;
bool can_export_supports() const; bool can_export_supports() const;
@ -184,6 +186,13 @@ public:
// Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig // Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
void on_config_changed(DynamicPrintConfig* cfg) const ; void on_config_changed(DynamicPrintConfig* cfg) const ;
#if ENABLE_PROJECT_DIRTY_STATE
bool can_save() const;
bool can_save_as() const;
void save_project();
void save_project_as(const wxString& filename = wxString());
#endif // ENABLE_PROJECT_DIRTY_STATE
void add_to_recent_projects(const wxString& filename); void add_to_recent_projects(const wxString& filename);
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }

View File

@ -1393,7 +1393,13 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi
this->MSWUpdateDragImageOnLeave(); this->MSWUpdateDragImageOnLeave();
#endif // WIN32 #endif // WIN32
#if ENABLE_PROJECT_DIRTY_STATE
bool res = (m_plater != nullptr) ? m_plater->load_files(filenames) : false;
wxGetApp().mainframe->update_title();
return res;
#else
return (m_plater != nullptr) ? m_plater->load_files(filenames) : false; return (m_plater != nullptr) ? m_plater->load_files(filenames) : false;
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
// State to manage showing after export notifications and device ejecting // State to manage showing after export notifications and device ejecting
@ -1511,9 +1517,26 @@ struct Plater::priv
priv(Plater *q, MainFrame *main_frame); priv(Plater *q, MainFrame *main_frame);
~priv(); ~priv();
#if ENABLE_PROJECT_DIRTY_STATE
bool is_project_dirty() const { return dirty_state.is_dirty(); }
void update_project_dirty_from_presets() { dirty_state.update_from_presets(); }
bool save_project_if_dirty() {
if (dirty_state.is_dirty()) {
MainFrame* mainframe = wxGetApp().mainframe;
if (mainframe->can_save_as()) {
wxMessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL);
if (dlg.ShowModal() == wxID_CANCEL)
return false;
mainframe->save_project_as(wxGetApp().plater()->get_project_filename());
}
}
return true;
}
void reset_project_dirty_after_save() { dirty_state.reset_after_save(); }
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_project_state_debug_window() const { dirty_state.render_debug_window(); } void render_project_state_debug_window() const { dirty_state.render_debug_window(); }
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
#endif // ENABLE_PROJECT_DIRTY_STATE
enum class UpdateParams { enum class UpdateParams {
FORCE_FULL_SCREEN_REFRESH = 1, FORCE_FULL_SCREEN_REFRESH = 1,
@ -4216,6 +4239,11 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name)
} }
this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), snapshot_data); this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), snapshot_data);
this->undo_redo_stack().release_least_recently_used(); this->undo_redo_stack().release_least_recently_used();
#if ENABLE_PROJECT_DIRTY_STATE
dirty_state.update_from_undo_redo_stack(undo_redo_stack_main(), undo_redo_stack());
#endif // ENABLE_PROJECT_DIRTY_STATE
// Save the last active preset name of a particular printer technology. // Save the last active preset name of a particular printer technology.
((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name(); ((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name();
BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info();
@ -4346,6 +4374,10 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active) if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active)
view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting"));
} }
#if ENABLE_PROJECT_DIRTY_STATE
dirty_state.update_from_undo_redo_stack(undo_redo_stack_main(), undo_redo_stack());
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */) void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */)
@ -4429,9 +4461,15 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame)
// Initialization performed in the private c-tor // Initialization performed in the private c-tor
} }
#if ENABLE_PROJECT_DIRTY_STATE
bool Plater::is_project_dirty() const { return p->is_project_dirty(); }
void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); }
bool Plater::save_project_if_dirty() { return p->save_project_if_dirty(); }
void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); }
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); } void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); }
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
#endif // ENABLE_PROJECT_DIRTY_STATE
Sidebar& Plater::sidebar() { return *p->sidebar; } Sidebar& Plater::sidebar() { return *p->sidebar; }
Model& Plater::model() { return p->model; } Model& Plater::model() { return p->model; }
@ -4442,12 +4480,30 @@ SLAPrint& Plater::sla_print() { return p->sla_print; }
void Plater::new_project() void Plater::new_project()
{ {
#if ENABLE_PROJECT_DIRTY_STATE
if (!p->save_project_if_dirty())
return;
#endif // ENABLE_PROJECT_DIRTY_STATE
p->select_view_3D("3D"); p->select_view_3D("3D");
#if ENABLE_PROJECT_DIRTY_STATE
take_snapshot(_L("New Project"));
Plater::SuppressSnapshots suppress(this);
reset();
// reset_project_initial_presets();
update_project_dirty_from_presets();
#else
wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL));
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
void Plater::load_project() void Plater::load_project()
{ {
#if ENABLE_PROJECT_DIRTY_STATE
if (!p->save_project_if_dirty())
return;
#endif // ENABLE_PROJECT_DIRTY_STATE
// Ask user for a project file name. // Ask user for a project file name.
wxString input_file; wxString input_file;
wxGetApp().load_project(this, input_file); wxGetApp().load_project(this, input_file);
@ -4471,8 +4527,16 @@ void Plater::load_project(const wxString& filename)
std::vector<size_t> res = load_files(input_paths); std::vector<size_t> res = load_files(input_paths);
// if res is empty no data has been loaded // if res is empty no data has been loaded
#if ENABLE_PROJECT_DIRTY_STATE
if (!res.empty()) {
p->set_project_filename(filename);
// reset_project_initial_presets();
update_project_dirty_from_presets();
}
#else
if (!res.empty()) if (!res.empty())
p->set_project_filename(filename); p->set_project_filename(filename);
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
void Plater::add_model(bool imperial_units/* = false*/) void Plater::add_model(bool imperial_units/* = false*/)
@ -4503,7 +4567,13 @@ void Plater::add_model(bool imperial_units/* = false*/)
} }
Plater::TakeSnapshot snapshot(this, snapshot_label); Plater::TakeSnapshot snapshot(this, snapshot_label);
#if ENABLE_PROJECT_DIRTY_STATE
std::vector<size_t> res = load_files(paths, true, false, imperial_units);
if (!res.empty())
wxGetApp().mainframe->update_title();
#else
load_files(paths, true, false, imperial_units); load_files(paths, true, false, imperial_units);
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
void Plater::import_sl1_archive() void Plater::import_sl1_archive()
@ -5187,24 +5257,39 @@ void Plater::export_amf()
} }
} }
#if ENABLE_PROJECT_DIRTY_STATE
bool Plater::export_3mf(const boost::filesystem::path& output_path)
#else
void Plater::export_3mf(const boost::filesystem::path& output_path) void Plater::export_3mf(const boost::filesystem::path& output_path)
#endif // ENABLE_PROJECT_DIRTY_STATE
{ {
if (p->model.objects.empty() if (p->model.objects.empty()
|| canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) || canvas3D()->get_gizmos_manager().is_in_editing_mode(true))
#if ENABLE_PROJECT_DIRTY_STATE
return false;
#else
return; return;
#endif // ENABLE_PROJECT_DIRTY_STATE
wxString path; wxString path;
bool export_config = true; bool export_config = true;
if (output_path.empty()) if (output_path.empty()) {
{
path = p->get_export_file(FT_3MF); path = p->get_export_file(FT_3MF);
#if ENABLE_PROJECT_DIRTY_STATE
if (path.empty()) { return false; }
#else
if (path.empty()) { return; } if (path.empty()) { return; }
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
else else
path = from_path(output_path); path = from_path(output_path);
if (!path.Lower().EndsWith(".3mf")) if (!path.Lower().EndsWith(".3mf"))
#if ENABLE_PROJECT_DIRTY_STATE
return false;
#else
return; return;
#endif // ENABLE_PROJECT_DIRTY_STATE
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
const std::string path_u8 = into_u8(path); const std::string path_u8 = into_u8(path);
@ -5212,6 +5297,19 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1"; bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
ThumbnailData thumbnail_data; ThumbnailData thumbnail_data;
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true); p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true);
#if ENABLE_PROJECT_DIRTY_STATE
bool ret = Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data);
if (ret) {
// Success
p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
p->set_project_filename(path);
}
else {
// Failure
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
}
return ret;
#else
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) { if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) {
// Success // Success
p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path)); p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
@ -5221,6 +5319,7 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
// Failure // Failure
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path)); p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
} }
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
void Plater::reload_from_disk() void Plater::reload_from_disk()

View File

@ -130,9 +130,15 @@ public:
Plater &operator=(const Plater &) = delete; Plater &operator=(const Plater &) = delete;
~Plater() = default; ~Plater() = default;
#if ENABLE_PROJECT_DIRTY_STATE
bool is_project_dirty() const;
void update_project_dirty_from_presets();
bool save_project_if_dirty();
void reset_project_dirty_after_save();
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_project_state_debug_window() const; void render_project_state_debug_window() const;
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
#endif // ENABLE_PROJECT_DIRTY_STATE
Sidebar& sidebar(); Sidebar& sidebar();
Model& model(); Model& model();
@ -201,7 +207,11 @@ public:
void export_gcode(bool prefer_removable); void export_gcode(bool prefer_removable);
void export_stl(bool extended = false, bool selection_only = false); void export_stl(bool extended = false, bool selection_only = false);
void export_amf(); void export_amf();
#if ENABLE_PROJECT_DIRTY_STATE
bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
#else
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
#endif // ENABLE_PROJECT_DIRTY_STATE
void reload_from_disk(); void reload_from_disk();
void reload_all_from_disk(); void reload_all_from_disk();
bool has_toolpaths_to_export() const; bool has_toolpaths_to_export() const;

View File

@ -2,12 +2,32 @@
#include "ProjectDirtyStateManager.hpp" #include "ProjectDirtyStateManager.hpp"
#include "ImGuiWrapper.hpp" #include "ImGuiWrapper.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "MainFrame.hpp"
#if ENABLE_PROJECT_DIRTY_STATE #if ENABLE_PROJECT_DIRTY_STATE
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack)
{
if (!wxGetApp().initialized())
return;
wxGetApp().mainframe->update_title();
}
void ProjectDirtyStateManager::update_from_presets()
{
wxGetApp().mainframe->update_title();
}
void ProjectDirtyStateManager::reset_after_save()
{
m_state.reset();
wxGetApp().mainframe->update_title();
}
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void ProjectDirtyStateManager::render_debug_window() const void ProjectDirtyStateManager::render_debug_window() const
{ {

View File

@ -4,6 +4,9 @@
#if ENABLE_PROJECT_DIRTY_STATE #if ENABLE_PROJECT_DIRTY_STATE
namespace Slic3r { namespace Slic3r {
namespace UndoRedo {
class Stack;
} // namespace UndoRedo
namespace GUI { namespace GUI {
class ProjectDirtyStateManager class ProjectDirtyStateManager
@ -14,12 +17,19 @@ class ProjectDirtyStateManager
bool presets{ false }; bool presets{ false };
bool is_dirty() const { return plater || presets; } bool is_dirty() const { return plater || presets; }
void reset() {
plater = false;
presets = false;
}
}; };
DirtyState m_state; DirtyState m_state;
public: public:
bool is_dirty() const { return m_state.is_dirty(); } bool is_dirty() const { return m_state.is_dirty(); }
void update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack);
void update_from_presets();
void reset_after_save();
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_debug_window() const; void render_debug_window() const;

View File

@ -1215,9 +1215,8 @@ void Tab::apply_config_from_cache()
// to update number of "filament" selection boxes when the number of extruders change. // to update number of "filament" selection boxes when the number of extruders change.
void Tab::on_presets_changed() void Tab::on_presets_changed()
{ {
if (wxGetApp().plater() == nullptr) { if (wxGetApp().plater() == nullptr)
return; return;
}
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets // Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
wxGetApp().plater()->sidebar().update_presets(m_type); wxGetApp().plater()->sidebar().update_presets(m_type);
@ -1235,6 +1234,10 @@ void Tab::on_presets_changed()
// clear m_dependent_tabs after first update from select_preset() // clear m_dependent_tabs after first update from select_preset()
// to avoid needless preset loading from update() function // to avoid needless preset loading from update() function
m_dependent_tabs.clear(); m_dependent_tabs.clear();
#if ENABLE_PROJECT_DIRTY_STATE
wxGetApp().plater()->update_project_dirty_from_presets();
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup) void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup)