From 144e37c2745b188c99e8c88fde7c6f4da3603ef3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Apr 2021 10:00:17 +0200 Subject: [PATCH 01/13] 1st installment of project dirty state manager --- src/libslic3r/Technologies.hpp | 5 +++ src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 5 +++ src/slic3r/GUI/Plater.cpp | 17 ++++++-- src/slic3r/GUI/Plater.hpp | 6 ++- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 47 +++++++++++++++++++++ src/slic3r/GUI/ProjectDirtyStateManager.hpp | 35 +++++++++++++++ 7 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 src/slic3r/GUI/ProjectDirtyStateManager.cpp create mode 100644 src/slic3r/GUI/ProjectDirtyStateManager.hpp diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d6b2cff8ee..fcb59f1a10 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,5 +58,10 @@ // Enable exporting lines M73 for remaining time to next printer stop to gcode #define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE) +// Enable project dirty state manager +#define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) +// Enable project dirty state manager debug window +#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (1 && ENABLE_PROJECT_DIRTY_STATE) + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 4b3a1c6ca9..5b904c87d0 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -189,6 +189,8 @@ set(SLIC3R_GUI_SOURCES GUI/UnsavedChangesDialog.hpp GUI/ExtraRenderers.cpp GUI/ExtraRenderers.hpp + GUI/ProjectDirtyStateManager.hpp + GUI/ProjectDirtyStateManager.cpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7bbdc72b11..4570670cbf 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1718,6 +1718,11 @@ void GLCanvas3D::render() } #endif // ENABLE_RENDER_STATISTICS +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + if (wxGetApp().is_editor() && wxGetApp().plater()->is_view3D_shown()) + wxGetApp().plater()->render_project_state_debug_window(); +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + #if ENABLE_CAMERA_STATISTICS camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b4b025cd2e..0434d2555c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -81,6 +81,9 @@ #include "InstanceCheck.hpp" #include "NotificationManager.hpp" #include "PresetComboBoxes.hpp" +#if ENABLE_PROJECT_DIRTY_STATE +#include "ProjectDirtyStateManager.hpp" +#endif // ENABLE_PROJECT_DIRTY_STATE #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -1434,6 +1437,10 @@ struct Plater::priv Preview *preview; NotificationManager* notification_manager { nullptr }; +#if ENABLE_PROJECT_DIRTY_STATE + ProjectDirtyStateManager dirty_state; +#endif // ENABLE_PROJECT_DIRTY_STATE + BackgroundSlicingProcess background_process; bool suppressed_backround_processing_update { false }; @@ -1504,6 +1511,10 @@ struct Plater::priv priv(Plater *q, MainFrame *main_frame); ~priv(); +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + void render_project_state_debug_window() const { dirty_state.render_debug_window(); } +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + enum class UpdateParams { FORCE_FULL_SCREEN_REFRESH = 1, FORCE_BACKGROUND_PROCESSING_UPDATE = 2, @@ -4418,9 +4429,9 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) // Initialization performed in the private c-tor } -Plater::~Plater() -{ -} +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); } +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW Sidebar& Plater::sidebar() { return *p->sidebar; } Model& Plater::model() { return p->model; } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index ff81dad26e..f2d60d8013 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -128,7 +128,11 @@ public: Plater(const Plater &) = delete; Plater &operator=(Plater &&) = delete; Plater &operator=(const Plater &) = delete; - ~Plater(); + ~Plater() = default; + +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + void render_project_state_debug_window() const; +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW Sidebar& sidebar(); Model& model(); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp new file mode 100644 index 0000000000..9a19676b25 --- /dev/null +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -0,0 +1,47 @@ +#include "libslic3r/libslic3r.h" +#include "ProjectDirtyStateManager.hpp" +#include "ImGuiWrapper.hpp" +#include "GUI_App.hpp" + +#if ENABLE_PROJECT_DIRTY_STATE + +namespace Slic3r { +namespace GUI { + +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +void ProjectDirtyStateManager::render_debug_window() const +{ + auto color = [](bool value) { + return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + }; + auto text = [](bool value) { + return value ? "true" : "false"; + }; + + std::string title = "Project dirty state statistics"; + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(title, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + bool dirty = is_dirty(); + imgui.text_colored(color(dirty), "State:"); + ImGui::SameLine(); + imgui.text_colored(color(dirty), text(dirty)); + + ImGui::Separator(); + imgui.text_colored(color(m_state.plater), "Plater:"); + ImGui::SameLine(); + imgui.text_colored(color(m_state.plater), text(m_state.plater)); + + imgui.text_colored(color(m_state.presets), "Presets:"); + ImGui::SameLine(); + imgui.text_colored(color(m_state.presets), text(m_state.presets)); + + imgui.end(); +} +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_PROJECT_DIRTY_STATE + diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp new file mode 100644 index 0000000000..81ac28915f --- /dev/null +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -0,0 +1,35 @@ +#ifndef slic3r_ProjectDirtyStateManager_hpp_ +#define slic3r_ProjectDirtyStateManager_hpp_ + +#if ENABLE_PROJECT_DIRTY_STATE + +namespace Slic3r { +namespace GUI { + +class ProjectDirtyStateManager +{ + struct DirtyState + { + bool plater{ false }; + bool presets{ false }; + + bool is_dirty() const { return plater || presets; } + }; + + DirtyState m_state; + +public: + bool is_dirty() const { return m_state.is_dirty(); } + +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + void render_debug_window() const; +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // ENABLE_PROJECT_DIRTY_STATE + +#endif // slic3r_ProjectDirtyStateManager_hpp_ + From 5d4b7c03b603945cec03e270faca14f957f08cb0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Apr 2021 13:17:29 +0200 Subject: [PATCH 02/13] Extended interface of project dirty state manager --- src/slic3r/GUI/GUI_App.cpp | 8 ++ src/slic3r/GUI/GUI_ObjectList.cpp | 10 +- src/slic3r/GUI/MainFrame.cpp | 48 +++++++++ src/slic3r/GUI/MainFrame.hpp | 9 ++ src/slic3r/GUI/Plater.cpp | 103 +++++++++++++++++++- src/slic3r/GUI/Plater.hpp | 10 ++ src/slic3r/GUI/ProjectDirtyStateManager.cpp | 20 ++++ src/slic3r/GUI/ProjectDirtyStateManager.hpp | 10 ++ src/slic3r/GUI/Tab.cpp | 7 +- 9 files changed, 219 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b22cd6009b..793ef80b70 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -904,6 +904,14 @@ bool GUI_App::on_init_inner() } else 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); obj_list()->set_min_height(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 58560c8bd7..bce64f9ea7 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -7,6 +7,9 @@ #include "GUI_App.hpp" #include "I18N.hpp" #include "Plater.hpp" +#if ENABLE_PROJECT_DIRTY_STATE +#include "MainFrame.hpp" +#endif // ENABLE_PROJECT_DIRTY_STATE #include "OptionsGroup.hpp" #include "Tab.hpp" @@ -1457,12 +1460,15 @@ void ObjectList::load_shape_object(const std::string& type_name) if (obj_idx < 0) return; - take_snapshot(_(L("Add Shape"))); + take_snapshot(_L("Add Shape")); // Create mesh BoundingBoxf3 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) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 35b1c16d82..832ebc2579 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -206,6 +206,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // declare events 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()) { event.Veto(); return; @@ -487,8 +492,14 @@ void MainFrame::update_title() // 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! 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()) title += (project + " - "); +#endif // ENABLE_PROJECT_DIRTY_STATE } 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()); } +#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 { return (m_plater != nullptr) && !m_plater->model().objects.empty(); } +#endif // ENABLE_PROJECT_DIRTY_STATE 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()); +#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"), [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); +#endif // ENABLE_PROJECT_DIRTY_STATE #ifdef __APPLE__ append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"), #else append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"), #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](){return m_plater != nullptr && can_save(); }, this); +#endif // ENABLE_PROJECT_DIRTY_STATE fileMenu->AppendSeparator(); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 0971fdc77a..307cdf1ae8 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -91,7 +91,9 @@ class MainFrame : public DPIFrame void on_value_changed(wxCommandEvent&); bool can_start_new_project() const; +#if !ENABLE_PROJECT_DIRTY_STATE bool can_save() const; +#endif // !ENABLE_PROJECT_DIRTY_STATE bool can_export_model() const; bool can_export_toolpaths() 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 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); PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0434d2555c..9de4641c04 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1393,7 +1393,13 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi this->MSWUpdateDragImageOnLeave(); #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; +#endif // ENABLE_PROJECT_DIRTY_STATE } // State to manage showing after export notifications and device ejecting @@ -1511,9 +1517,26 @@ struct Plater::priv priv(Plater *q, MainFrame *main_frame); ~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 void render_project_state_debug_window() const { dirty_state.render_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +#endif // ENABLE_PROJECT_DIRTY_STATE enum class UpdateParams { 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().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. ((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(); @@ -4346,6 +4374,10 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator 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")); } + +#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 */) @@ -4429,9 +4461,15 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) // 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 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 Sidebar& Plater::sidebar() { return *p->sidebar; } Model& Plater::model() { return p->model; } @@ -4442,12 +4480,30 @@ SLAPrint& Plater::sla_print() { return p->sla_print; } 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"); +#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)); +#endif // ENABLE_PROJECT_DIRTY_STATE } 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. wxString input_file; wxGetApp().load_project(this, input_file); @@ -4471,8 +4527,16 @@ void Plater::load_project(const wxString& filename) std::vector res = load_files(input_paths); // 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()) p->set_project_filename(filename); +#endif // ENABLE_PROJECT_DIRTY_STATE } 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); +#if ENABLE_PROJECT_DIRTY_STATE + std::vector res = load_files(paths, true, false, imperial_units); + if (!res.empty()) + wxGetApp().mainframe->update_title(); +#else load_files(paths, true, false, imperial_units); +#endif // ENABLE_PROJECT_DIRTY_STATE } 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) +#endif // ENABLE_PROJECT_DIRTY_STATE { if (p->model.objects.empty() || canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) +#if ENABLE_PROJECT_DIRTY_STATE + return false; +#else return; +#endif // ENABLE_PROJECT_DIRTY_STATE wxString path; bool export_config = true; - if (output_path.empty()) - { + if (output_path.empty()) { path = p->get_export_file(FT_3MF); +#if ENABLE_PROJECT_DIRTY_STATE + if (path.empty()) { return false; } +#else if (path.empty()) { return; } +#endif // ENABLE_PROJECT_DIRTY_STATE } else path = from_path(output_path); if (!path.Lower().EndsWith(".3mf")) +#if ENABLE_PROJECT_DIRTY_STATE + return false; +#else return; +#endif // ENABLE_PROJECT_DIRTY_STATE DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); 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"; ThumbnailData thumbnail_data; 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)) { // Success 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 p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path)); } +#endif // ENABLE_PROJECT_DIRTY_STATE } void Plater::reload_from_disk() diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index f2d60d8013..ead9679c7c 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -130,9 +130,15 @@ public: Plater &operator=(const Plater &) = delete; ~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 void render_project_state_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +#endif // ENABLE_PROJECT_DIRTY_STATE Sidebar& sidebar(); Model& model(); @@ -201,7 +207,11 @@ public: void export_gcode(bool prefer_removable); void export_stl(bool extended = false, bool selection_only = false); 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()); +#endif // ENABLE_PROJECT_DIRTY_STATE void reload_from_disk(); void reload_all_from_disk(); bool has_toolpaths_to_export() const; diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 9a19676b25..5cf7274bb2 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -2,12 +2,32 @@ #include "ProjectDirtyStateManager.hpp" #include "ImGuiWrapper.hpp" #include "GUI_App.hpp" +#include "MainFrame.hpp" #if ENABLE_PROJECT_DIRTY_STATE namespace Slic3r { 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 void ProjectDirtyStateManager::render_debug_window() const { diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 81ac28915f..b488c00bbe 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -4,6 +4,9 @@ #if ENABLE_PROJECT_DIRTY_STATE namespace Slic3r { +namespace UndoRedo { +class Stack; +} // namespace UndoRedo namespace GUI { class ProjectDirtyStateManager @@ -14,12 +17,19 @@ class ProjectDirtyStateManager bool presets{ false }; bool is_dirty() const { return plater || presets; } + void reset() { + plater = false; + presets = false; + } }; DirtyState m_state; public: 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 void render_debug_window() const; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 11c4875ebd..97117f4189 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1215,9 +1215,8 @@ void Tab::apply_config_from_cache() // to update number of "filament" selection boxes when the number of extruders change. void Tab::on_presets_changed() { - if (wxGetApp().plater() == nullptr) { + if (wxGetApp().plater() == nullptr) return; - } // Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets 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() // to avoid needless preset loading from update() function 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) From edbb1d0f69b6dddc46420acada77c7f5d4d45130 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Apr 2021 16:29:05 +0200 Subject: [PATCH 03/13] Project dirty state manager -> presets dirty state --- src/libslic3r/Preset.cpp | 14 ++++- src/libslic3r/Preset.hpp | 30 +++++++++- src/slic3r/GUI/GUI_App.cpp | 63 ++++++++++++++++++++- src/slic3r/GUI/GUI_App.hpp | 10 +++- src/slic3r/GUI/MainFrame.cpp | 16 +++++- src/slic3r/GUI/Plater.cpp | 18 ++++-- src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/ProjectDirtyStateManager.cpp | 17 ++++++ src/slic3r/GUI/ProjectDirtyStateManager.hpp | 7 ++- src/slic3r/GUI/Tab.cpp | 6 ++ src/slic3r/GUI/Tab.hpp | 42 ++++++++++++-- 11 files changed, 206 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index ecb20a18ec..aae6a29dcc 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -617,11 +617,17 @@ const std::vector& Preset::sla_printer_options() PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) : m_type(type), m_edited_preset(type, "", false), +#if ENABLE_PROJECT_DIRTY_STATE + m_saved_preset(type, "", false), +#endif // ENABLE_PROJECT_DIRTY_STATE m_idx_selected(0) { // Insert just the default preset. this->add_default_preset(keys, defaults, default_name); m_edited_preset.config.apply(m_presets.front().config); +#if ENABLE_PROJECT_DIRTY_STATE + update_saved_preset_from_current_preset(); +#endif // ENABLE_PROJECT_DIRTY_STATE } void PresetCollection::reset(bool delete_files) @@ -798,7 +804,10 @@ std::pair PresetCollection::load_external_preset( // The source config may contain keys from many possible preset types. Just copy those that relate to this preset. this->get_edited_preset().config.apply_only(combined_config, keys, true); this->update_dirty(); - assert(this->get_edited_preset().is_dirty); +#if ENABLE_PROJECT_DIRTY_STATE + update_saved_preset_from_current_preset(); +#endif // ENABLE_PROJECT_DIRTY_STATE + assert(this->get_edited_preset().is_dirty); return std::make_pair(&(*it), this->get_edited_preset().is_dirty); } if (inherits.empty()) { @@ -1208,6 +1217,9 @@ Preset& PresetCollection::select_preset(size_t idx) idx = first_visible_idx(); m_idx_selected = idx; m_edited_preset = m_presets[idx]; +#if ENABLE_PROJECT_DIRTY_STATE + update_saved_preset_from_current_preset(); +#endif // ENABLE_PROJECT_DIRTY_STATE bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets; for (size_t i = 0; i < m_num_default_presets; ++i) m_presets[i].is_visible = default_visible; diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 6e56ad911d..8d407fb649 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -346,6 +346,11 @@ public: Preset& get_edited_preset() { return m_edited_preset; } const Preset& get_edited_preset() const { return m_edited_preset; } +#if ENABLE_PROJECT_DIRTY_STATE + // Return the last saved preset. + const Preset& get_saved_preset() const { return m_saved_preset; } +#endif // ENABLE_PROJECT_DIRTY_STATE + // Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist. PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const; PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); } @@ -365,8 +370,16 @@ public: // Return a preset by an index. If the preset is active, a temporary copy is returned. Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; } const Preset& preset(size_t idx) const { return const_cast(this)->preset(idx); } +#if ENABLE_PROJECT_DIRTY_STATE + void discard_current_changes() { + m_presets[m_idx_selected].reset_dirty(); + m_edited_preset = m_presets[m_idx_selected]; + update_saved_preset_from_current_preset(); + } +#else void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; } - +#endif // ENABLE_PROJECT_DIRTY_STATE + // Return a preset by its name. If the preset is active, a temporary copy is returned. // If a preset is not found by its name, null is returned. Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false); @@ -440,6 +453,16 @@ public: std::vector current_different_from_parent_options(const bool deep_compare = false) const { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); } +#if ENABLE_PROJECT_DIRTY_STATE + // Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ. + bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); } + // Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ. + std::vector saved_dirty_options(const bool deep_compare = false) const + { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); } + // Copy edited preset into saved preset. + void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; } +#endif // ENABLE_PROJECT_DIRTY_STATE + // Return a sorted list of system preset names. // Used for validating the "inherits" flag when importing user's config bundles. // Returns names of all system presets including the former names of these presets. @@ -527,6 +550,11 @@ private: std::map m_map_system_profile_renamed; // Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user. Preset m_edited_preset; +#if ENABLE_PROJECT_DIRTY_STATE + // Contains a copy of the last saved selected preset. + Preset m_saved_preset; +#endif // ENABLE_PROJECT_DIRTY_STATE + // Selected preset. size_t m_idx_selected; // Is the "- default -" preset suppressed? diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 793ef80b70..610e5f07ac 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -907,7 +907,7 @@ bool GUI_App::on_init_inner() #if ENABLE_PROJECT_DIRTY_STATE if (plater_ != nullptr) { -// plater_->reset_project_initial_presets(); + plater_->reset_project_dirty_initial_presets(); plater_->update_project_dirty_from_presets(); } #endif // ENABLE_PROJECT_DIRTY_STATE @@ -1673,7 +1673,11 @@ void GUI_App::add_config_menu(wxMenuBar *menu) break; case ConfigMenuTakeSnapshot: // Take a configuration snapshot. +#if ENABLE_PROJECT_DIRTY_STATE + if (check_and_save_current_preset_changes()) { +#else if (check_unsaved_changes()) { +#endif // ENABLE_PROJECT_DIRTY_STATE wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name")); // set current normal font for dialog children, @@ -1688,7 +1692,11 @@ void GUI_App::add_config_menu(wxMenuBar *menu) } break; case ConfigMenuSnapshots: +#if ENABLE_PROJECT_DIRTY_STATE + if (check_and_save_current_preset_changes()) { +#else if (check_unsaved_changes()) { +#endif // ENABLE_PROJECT_DIRTY_STATE std::string on_snapshot; if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config)) on_snapshot = app_config->get("on_snapshot"); @@ -1789,8 +1797,57 @@ void GUI_App::add_config_menu(wxMenuBar *menu) menu->Append(local_menu, _L("&Configuration")); } +#if ENABLE_PROJECT_DIRTY_STATE +bool GUI_App::has_unsaved_preset_changes() const +{ + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (const Tab* const tab : tabs_list) { + if (tab->supports_printer_technology(printer_technology) && tab->saved_preset_is_dirty()) + return true; + } + return false; +} + +bool GUI_App::has_current_preset_changes() const +{ + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (const Tab* const tab : tabs_list) { + if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + return true; + } + return false; +} + +void GUI_App::update_saved_preset_from_current_preset() +{ + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (Tab* tab : tabs_list) { + if (tab->supports_printer_technology(printer_technology)) + tab->update_saved_preset_from_current_preset(); + } +} + +std::vector> GUI_App::get_selected_presets() const +{ + std::vector> ret; + PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + for (Tab* tab : tabs_list) { + if (tab->supports_printer_technology(printer_technology)) { + const PresetCollection* presets = tab->get_presets(); + ret.push_back({ static_cast(presets->type()), presets->get_selected_preset_name() }); + } + } + return ret; +} +#endif // ENABLE_PROJECT_DIRTY_STATE + // This is called when closing the application, when loading a config file or when starting the config wizard // to notify the user whether he is aware that some preset changes will be lost. +#if ENABLE_PROJECT_DIRTY_STATE +bool GUI_App::check_and_save_current_preset_changes(const wxString& header) +{ + if (this->plater()->model().objects.empty() && has_current_preset_changes()) { +#else bool GUI_App::check_unsaved_changes(const wxString &header) { PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); @@ -1802,8 +1859,8 @@ bool GUI_App::check_unsaved_changes(const wxString &header) break; } - if (has_unsaved_changes) - { + if (has_unsaved_changes) { +#endif // ENABLE_PROJECT_DIRTY_STATE UnsavedChangesDialog dlg(header); if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL) return false; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index f1ee0746a0..d1df4212b8 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -209,7 +209,15 @@ public: void update_mode(); void add_config_menu(wxMenuBar *menu); - bool check_unsaved_changes(const wxString &header = wxString()); +#if ENABLE_PROJECT_DIRTY_STATE + bool has_unsaved_preset_changes() const; + bool has_current_preset_changes() const; + void update_saved_preset_from_current_preset(); + std::vector> get_selected_presets() const; + bool check_and_save_current_preset_changes(const wxString& header = wxString()); +#else + bool check_unsaved_changes(const wxString& header = wxString()); +#endif // ENABLE_PROJECT_DIRTY_STATE bool check_print_host_queue(); bool checked_tab(Tab* tab); void load_current_presets(bool check_printer_presets = true); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 832ebc2579..f556431f6f 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -209,9 +209,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #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_and_save_current_preset_changes()) { +#else if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) { +#endif // ENABLE_PROJECT_DIRTY_STATE event.Veto(); return; } @@ -1559,7 +1561,11 @@ void MainFrame::export_config() // Load a config file containing a Print, Filament & Printer preset. void MainFrame::load_config_file() { +#if ENABLE_PROJECT_DIRTY_STATE + if (!wxGetApp().check_and_save_current_preset_changes()) +#else if (!wxGetApp().check_unsaved_changes()) +#endif // ENABLE_PROJECT_DIRTY_STATE return; wxFileDialog dlg(this, _L("Select configuration to load:"), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), @@ -1588,7 +1594,11 @@ bool MainFrame::load_config_file(const std::string &path) void MainFrame::export_configbundle(bool export_physical_printers /*= false*/) { +#if ENABLE_PROJECT_DIRTY_STATE + if (!wxGetApp().check_and_save_current_preset_changes()) +#else if (!wxGetApp().check_unsaved_changes()) +#endif // ENABLE_PROJECT_DIRTY_STATE return; // validate current configuration in case it's dirty auto err = wxGetApp().preset_bundle->full_config().validate(); @@ -1620,7 +1630,11 @@ void MainFrame::export_configbundle(bool export_physical_printers /*= false*/) // but that behavior was not documented and likely buggy. void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/) { +#if ENABLE_PROJECT_DIRTY_STATE + if (!wxGetApp().check_and_save_current_preset_changes()) +#else if (!wxGetApp().check_unsaved_changes()) +#endif // ENABLE_PROJECT_DIRTY_STATE return; if (file.IsEmpty()) { wxFileDialog dlg(this, _L("Select configuration to load:"), diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9de4641c04..0ea20bd5e4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1525,14 +1525,18 @@ struct Plater::priv 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) + int res = dlg.ShowModal(); + if (res == wxID_YES) + mainframe->save_project_as(wxGetApp().plater()->get_project_filename()); + else if (res == 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(); } + void reset_project_dirty_initial_presets() { dirty_state.reset_initial_presets(); } + #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_project_state_debug_window() const { dirty_state.render_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW @@ -4284,8 +4288,13 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator if (printer_technology_changed) { // Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type. std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA"; +#if ENABLE_PROJECT_DIRTY_STATE + if (!wxGetApp().check_and_save_current_preset_changes(format_wxstr(_L( + "%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt))) +#else if (! wxGetApp().check_unsaved_changes(format_wxstr(_L( "%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt))) +#endif // ENABLE_PROJECT_DIRTY_STATE // Don't switch the profiles. return; } @@ -4466,6 +4475,7 @@ 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(); } +void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_initial_presets(); } #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW @@ -4490,7 +4500,7 @@ void Plater::new_project() take_snapshot(_L("New Project")); Plater::SuppressSnapshots suppress(this); reset(); -// reset_project_initial_presets(); + reset_project_dirty_initial_presets(); update_project_dirty_from_presets(); #else wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); @@ -4530,7 +4540,7 @@ void Plater::load_project(const wxString& filename) #if ENABLE_PROJECT_DIRTY_STATE if (!res.empty()) { p->set_project_filename(filename); -// reset_project_initial_presets(); + reset_project_dirty_initial_presets(); update_project_dirty_from_presets(); } #else diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index ead9679c7c..9b42863ecf 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -135,6 +135,7 @@ public: void update_project_dirty_from_presets(); bool save_project_if_dirty(); void reset_project_dirty_after_save(); + void reset_project_dirty_initial_presets(); #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_project_state_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 5cf7274bb2..5fc45ccec9 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -19,15 +19,32 @@ void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRed void ProjectDirtyStateManager::update_from_presets() { + m_state.presets = false; + std::vector> selected_presets = wxGetApp().get_selected_presets(); + for (const auto& [type, name] : selected_presets) { + m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name; + } + m_state.presets |= wxGetApp().has_unsaved_preset_changes(); wxGetApp().mainframe->update_title(); } void ProjectDirtyStateManager::reset_after_save() { + reset_initial_presets(); + m_state.reset(); wxGetApp().mainframe->update_title(); } +void ProjectDirtyStateManager::reset_initial_presets() +{ + m_initial_presets = std::array(); + std::vector> selected_presets = wxGetApp().get_selected_presets(); + for (const auto& [type, name] : selected_presets) { + m_initial_presets[type] = name; + } +} + #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void ProjectDirtyStateManager::render_debug_window() const { diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index b488c00bbe..2aa6680c65 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_ProjectDirtyStateManager_hpp_ #define slic3r_ProjectDirtyStateManager_hpp_ +#include "libslic3r/Preset.hpp" + #if ENABLE_PROJECT_DIRTY_STATE namespace Slic3r { @@ -25,12 +27,15 @@ class ProjectDirtyStateManager DirtyState m_state; + // keeps track of initial selected presets + std::array m_initial_presets; + public: 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(); - + void reset_initial_presets(); #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 97117f4189..294af5f76b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2113,10 +2113,16 @@ wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticTex return sizer; } +#if ENABLE_PROJECT_DIRTY_STATE +bool Tab::saved_preset_is_dirty() const { return m_presets->saved_is_dirty(); } +void Tab::update_saved_preset_from_current_preset() { m_presets->update_saved_preset_from_current_preset(); } +bool Tab::current_preset_is_dirty() const { return m_presets->current_is_dirty(); } +#else bool Tab::current_preset_is_dirty() { return m_presets->current_is_dirty(); } +#endif // ENABLE_PROJECT_DIRTY_STATE void TabPrinter::build() { diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 8cbc6585a7..0a11e838a0 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -270,7 +270,11 @@ public: Preset::Type type() const { return m_type; } // The tab is already constructed. bool completed() const { return m_completed; } - virtual bool supports_printer_technology(const PrinterTechnology tech) = 0; +#if ENABLE_PROJECT_DIRTY_STATE + virtual bool supports_printer_technology(const PrinterTechnology tech) const = 0; +#else + virtual bool supports_printer_technology(const PrinterTechnology tech) = 0; +#endif // ENABLE_PROJECT_DIRTY_STATE void create_preset_tab(); void add_scaled_button(wxWindow* parent, ScalableButton** btn, const std::string& icon_name, @@ -333,7 +337,13 @@ public: Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1); void toggle_option(const std::string& opt_key, bool toggle, int opt_index = -1); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString); +#if ENABLE_PROJECT_DIRTY_STATE + bool current_preset_is_dirty() const; + bool saved_preset_is_dirty() const; + void update_saved_preset_from_current_preset(); +#else bool current_preset_is_dirty(); +#endif // ENABLE_PROJECT_DIRTY_STATE DynamicPrintConfig* get_config() { return m_config; } PresetCollection* get_presets() { return m_presets; } @@ -387,7 +397,11 @@ public: void toggle_options() override; void update() override; void clear_pages() override; - bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; } +#else + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } +#endif // ENABLE_PROJECT_DIRTY_STATE private: ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr; @@ -417,7 +431,11 @@ public: void toggle_options() override; void update() override; void clear_pages() override; - bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; } +#else + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; } +#endif // ENABLE_PROJECT_DIRTY_STATE }; class TabPrinter : public Tab @@ -471,7 +489,11 @@ public: void init_options_list() override; void msw_rescale() override; void sys_color_changed() override; - bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; } +#else + bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } +#endif // ENABLE_PROJECT_DIRTY_STATE wxSizer* create_bed_shape_widget(wxWindow* parent); void cache_extruder_cnt(); @@ -491,7 +513,11 @@ public: void toggle_options() override {}; void update() override; void init_options_list() override; - bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; } +#else + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +#endif // ENABLE_PROJECT_DIRTY_STATE }; class TabSLAPrint : public Tab @@ -510,7 +536,11 @@ public: void toggle_options() override; void update() override; void clear_pages() override; - bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +#if ENABLE_PROJECT_DIRTY_STATE + bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; } +#else + bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; } +#endif // ENABLE_PROJECT_DIRTY_STATE }; } // GUI From 926ecd0585d3d40b8f9e9adc9e968a01351ea723 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Apr 2021 12:58:14 +0200 Subject: [PATCH 04/13] Project dirty state manager -> plater dirty state --- src/slic3r/GUI/Plater.cpp | 3 + src/slic3r/GUI/Plater.hpp | 3 + src/slic3r/GUI/ProjectDirtyStateManager.cpp | 131 +++++++++++++++++--- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 20 ++- src/slic3r/GUI/Tab.hpp | 18 +-- 5 files changed, 149 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0ea20bd5e4..5ffd4e51c6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6190,6 +6190,9 @@ bool Plater::can_mirror() const { return p->can_mirror(); } bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); } const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); } void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); } +#if ENABLE_PROJECT_DIRTY_STATE +const UndoRedo::Stack& Plater::undo_redo_stack_active() const { return p->undo_redo_stack(); } +#endif // ENABLE_PROJECT_DIRTY_STATE void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); } void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); } bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 9b42863ecf..8d228e68f3 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -242,6 +242,9 @@ public: // For the memory statistics. const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const; void clear_undo_redo_stack_main(); +#if ENABLE_PROJECT_DIRTY_STATE + const Slic3r::UndoRedo::Stack& undo_redo_stack_active() const; +#endif // ENABLE_PROJECT_DIRTY_STATE // Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo. void enter_gizmos_stack(); void leave_gizmos_stack(); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 5fc45ccec9..5d4cd8605a 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -1,19 +1,68 @@ #include "libslic3r/libslic3r.h" + #include "ProjectDirtyStateManager.hpp" #include "ImGuiWrapper.hpp" #include "GUI_App.hpp" #include "MainFrame.hpp" +#include "I18N.hpp" +#include "Plater.hpp" +#include "../Utils/UndoRedo.hpp" + +#include + +#include +#include #if ENABLE_PROJECT_DIRTY_STATE namespace Slic3r { namespace GUI { +enum class EStackType +{ + Main, + Gizmo +}; + +static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stack) { + const std::vector& snapshots = stack.snapshots(); + const size_t active_snapshot_time = stack.active_snapshot_time(); + const auto it = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot_time)); + const int idx = it - snapshots.begin() - 1; + const Slic3r::UndoRedo::Snapshot* ret = (0 < idx && (size_t)idx < snapshots.size() - 1) ? + &snapshots[idx] : nullptr; + + assert(ret != nullptr); + + return ret; +} + +static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack) { + auto skip_main = [](const UndoRedo::Snapshot& snapshot) { + return boost::starts_with(snapshot.name, _utf8("Selection")); + }; + + const UndoRedo::Snapshot* snapshot = get_active_snapshot(stack); + + const UndoRedo::Snapshot* curr = snapshot; + const std::vector& snapshots = stack.snapshots(); + while (type == EStackType::Main && skip_main(*curr)) { + curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - 1))); + } + + return curr; +} + void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) { if (!wxGetApp().initialized()) return; + if (&main_stack == &active_stack) + update_from_undo_redo_main_stack(main_stack); + else + update_from_undo_redo_gizmo_stack(active_stack); + wxGetApp().mainframe->update_title(); } @@ -31,8 +80,27 @@ void ProjectDirtyStateManager::update_from_presets() void ProjectDirtyStateManager::reset_after_save() { reset_initial_presets(); - m_state.reset(); + + const Plater* plater = wxGetApp().plater(); + const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main(); + const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); + + if (&main_stack == &active_stack) { + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); + +// std::cout << "SAVE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; +// std::cout << "SAVE - valid: " << valid_snapshot->timestamp << " - " << valid_snapshot->name << "\n"; + + m_last_save.main = valid_snapshot->timestamp; + } + else { + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); + +// std::cout << "SAVE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; + } + wxGetApp().mainframe->update_title(); } @@ -48,35 +116,66 @@ void ProjectDirtyStateManager::reset_initial_presets() #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void ProjectDirtyStateManager::render_debug_window() const { + ImGuiWrapper& imgui = *wxGetApp().imgui(); + auto color = [](bool value) { return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); }; auto text = [](bool value) { return value ? "true" : "false"; }; + auto append_item = [color, text, &imgui](const std::string& name, bool value) { + imgui.text_colored(color(value), name); + ImGui::SameLine(); + imgui.text_colored(color(value), text(value)); + }; - std::string title = "Project dirty state statistics"; - ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.begin(title, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - - bool dirty = is_dirty(); - imgui.text_colored(color(dirty), "State:"); - ImGui::SameLine(); - imgui.text_colored(color(dirty), text(dirty)); + imgui.begin(std::string( "Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + append_item("State:", is_dirty()); ImGui::Separator(); - imgui.text_colored(color(m_state.plater), "Plater:"); - ImGui::SameLine(); - imgui.text_colored(color(m_state.plater), text(m_state.plater)); - - imgui.text_colored(color(m_state.presets), "Presets:"); - ImGui::SameLine(); - imgui.text_colored(color(m_state.presets), text(m_state.presets)); + append_item("Plater:", m_state.plater); + append_item("Presets:", m_state.presets); + append_item("Current gizmo:", m_state.current_gizmo); imgui.end(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +void ProjectDirtyStateManager::update_from_undo_redo_main_stack(const Slic3r::UndoRedo::Stack& stack) +{ + m_state.plater = false; + + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + +// std::cout << "UPDATE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; + + if (active_snapshot->name == _utf8("New Project") || + active_snapshot->name == _utf8("Reset Project") || + boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) + return; + + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack); + +// std::cout << "UPDATE - valid: " << valid_snapshot->timestamp << " - " << valid_snapshot->name << "\n"; + + m_state.plater = valid_snapshot->timestamp != m_last_save.main; +} + +void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(const Slic3r::UndoRedo::Stack& stack) +{ + m_state.current_gizmo = false; + + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + +// std::cout << "UPDATE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; + + if (active_snapshot->name == "Gizmos-Initial") + return; + + m_state.current_gizmo = true; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 2aa6680c65..2eac4ba923 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -17,15 +17,29 @@ class ProjectDirtyStateManager { bool plater{ false }; bool presets{ false }; + bool current_gizmo{ false }; - bool is_dirty() const { return plater || presets; } + bool is_dirty() const { return plater || presets || current_gizmo; } void reset() { plater = false; presets = false; + current_gizmo = false; + } + }; + + struct Timestamps + { + size_t main{ 0 }; + size_t gizmo{ 0 }; + + void reset() { + main = 0; + gizmo = 0; } }; DirtyState m_state; + Timestamps m_last_save; // keeps track of initial selected presets std::array m_initial_presets; @@ -39,6 +53,10 @@ public: #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + +private: + void update_from_undo_redo_main_stack(const Slic3r::UndoRedo::Stack& stack); + void update_from_undo_redo_gizmo_stack(const Slic3r::UndoRedo::Stack& stack); }; } // namespace GUI diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 0a11e838a0..c1806cfcd2 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -387,8 +387,8 @@ class TabPrint : public Tab { public: TabPrint(wxNotebook* parent) : -// Tab(parent, _(L("Print Settings")), L("print")) {} - Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_PRINT) {} +// Tab(parent, _L("Print Settings"), L("print")) {} + Tab(parent, _L("Print Settings"), Slic3r::Preset::TYPE_PRINT) {} ~TabPrint() {} void build() override; @@ -421,8 +421,8 @@ private: std::map m_overrides_options; public: TabFilament(wxNotebook* parent) : -// Tab(parent, _(L("Filament Settings")), L("filament")) {} - Tab(parent, _(L("Filament Settings")), Slic3r::Preset::TYPE_FILAMENT) {} +// Tab(parent, _L("Filament Settings"), L("filament")) {} + Tab(parent, _L("Filament Settings"), Slic3r::Preset::TYPE_FILAMENT) {} ~TabFilament() {} void build() override; @@ -467,7 +467,7 @@ public: // TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), L("printer")) {} TabPrinter(wxNotebook* parent) : - Tab(parent, _(L("Printer Settings")), Slic3r::Preset::TYPE_PRINTER) {} + Tab(parent, _L("Printer Settings"), Slic3r::Preset::TYPE_PRINTER) {} ~TabPrinter() {} void build() override; @@ -504,8 +504,8 @@ class TabSLAMaterial : public Tab { public: TabSLAMaterial(wxNotebook* parent) : -// Tab(parent, _(L("Material Settings")), L("sla_material")) {} - Tab(parent, _(L("Material Settings")), Slic3r::Preset::TYPE_SLA_MATERIAL) {} +// Tab(parent, _L("Material Settings"), L("sla_material")) {} + Tab(parent, _L("Material Settings"), Slic3r::Preset::TYPE_SLA_MATERIAL) {} ~TabSLAMaterial() {} void build() override; @@ -524,8 +524,8 @@ class TabSLAPrint : public Tab { public: TabSLAPrint(wxNotebook* parent) : -// Tab(parent, _(L("Print Settings")), L("sla_print")) {} - Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_SLA_PRINT) {} +// Tab(parent, _L("Print Settings"), L("sla_print")) {} + Tab(parent, _L("Print Settings"), Slic3r::Preset::TYPE_SLA_PRINT) {} ~TabSLAPrint() {} ogStaticText* m_support_object_elevation_description_line = nullptr; From bfbc683a59c00487eba8f1b01f2aeaf1bac586d0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Apr 2021 14:26:04 +0200 Subject: [PATCH 05/13] Follow-up of 926ecd0585d3d40b8f9e9adc9e968a01351ea723 -> Improved management of plater dirty state --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 97 +++++++++++--------- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 20 +--- 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b200623f49..7ff274aacf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -45,18 +45,18 @@ bool GLGizmoSlaSupports::on_init() { m_shortcut_key = WXK_CONTROL_L; - m_desc["head_diameter"] = _(L("Head diameter")) + ": "; - m_desc["lock_supports"] = _(L("Lock supports under new islands")); - m_desc["remove_selected"] = _(L("Remove selected points")); - m_desc["remove_all"] = _(L("Remove all points")); - m_desc["apply_changes"] = _(L("Apply changes")); - m_desc["discard_changes"] = _(L("Discard changes")); - m_desc["minimal_distance"] = _(L("Minimal points distance")) + ": "; - m_desc["points_density"] = _(L("Support points density")) + ": "; - m_desc["auto_generate"] = _(L("Auto-generate points")); - m_desc["manual_editing"] = _(L("Manual editing")); - m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": "; - m_desc["reset_direction"] = _(L("Reset direction")); + m_desc["head_diameter"] = _L("Head diameter") + ": "; + m_desc["lock_supports"] = _L("Lock supports under new islands"); + m_desc["remove_selected"] = _L("Remove selected points"); + m_desc["remove_all"] = _L("Remove all points"); + m_desc["apply_changes"] = _L("Apply changes"); + m_desc["discard_changes"] = _L("Discard changes"); + m_desc["minimal_distance"] = _L("Minimal points distance") + ": "; + m_desc["points_density"] = _L("Support points density") + ": "; + m_desc["auto_generate"] = _L("Auto-generate points"); + m_desc["manual_editing"] = _L("Manual editing"); + m_desc["clipping_of_view"] = _L("Clipping of view")+ ": "; + m_desc["reset_direction"] = _L("Reset direction"); return true; } @@ -372,7 +372,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (m_selection_empty) { std::pair pos_and_normal; if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add support point")); m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); m_parent.set_as_dirty(); m_wait_for_up_event = true; @@ -512,7 +512,7 @@ void GLGizmoSlaSupports::delete_selected_points(bool force) std::abort(); } - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete support point")); for (unsigned int idx=0; idxconfig.set("support_points_minimal_distance", m_minimal_point_distance_stash); mo->config.set("support_points_density_relative", (int)m_density_stash); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Support parameter change")); mo->config.set("support_points_minimal_distance", minimal_point_distance); mo->config.set("support_points_density_relative", (int)density); wxGetApp().obj_list()->update_and_show_object_settings_item(); @@ -867,10 +867,9 @@ bool GLGizmoSlaSupports::on_is_selectable() const std::string GLGizmoSlaSupports::on_get_name() const { - return (_(L("SLA Support Points")) + " [L]").ToUTF8().data(); + return (_L("SLA Support Points") + " [L]").ToUTF8().data(); } - CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const { return CommonGizmosDataID( @@ -895,7 +894,11 @@ void GLGizmoSlaSupports::on_set_state() // data are not yet available, the CallAfter will postpone taking the // snapshot until they are. No, it does not feel right. wxGetApp().CallAfter([]() { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); +#if ENABLE_PROJECT_DIRTY_STATE + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Entering SLA gizmo")); +#else + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("SLA gizmo turned on")); +#endif // ENABLE_PROJECT_DIRTY_STATE }); } @@ -909,8 +912,8 @@ void GLGizmoSlaSupports::on_set_state() wxGetApp().CallAfter([this]() { // Following is called through CallAfter, because otherwise there was a problem // on OSX with the wxMessageDialog being shown several times when clicked into. - wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually " - "edited support points?")) + "\n",_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO); + wxMessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually " + "edited support points?") + "\n",_L("Save changes?"), wxICON_QUESTION | wxYES | wxNO); if (dlg.ShowModal() == wxID_YES) editing_mode_apply_changes(); else @@ -922,7 +925,11 @@ void GLGizmoSlaSupports::on_set_state() else { // we are actually shutting down disable_editing_mode(); // so it is not active next time the gizmo opens - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); +#if ENABLE_PROJECT_DIRTY_STATE + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Leaving SLA gizmo")); +#else + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("SLA gizmo turned off")); +#endif // ENABLE_PROJECT_DIRTY_STATE m_normal_cache.clear(); m_old_mo_id = -1; } @@ -953,7 +960,7 @@ void GLGizmoSlaSupports::on_stop_dragging() && backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected { m_editing_cache[m_hover_id] = m_point_before_drag; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move support point")); m_editing_cache[m_hover_id] = backup; } } @@ -1046,7 +1053,7 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() disable_editing_mode(); // this leaves the editing mode undo/redo stack and must be done before the snapshot is taken if (unsaved_changes()) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support points edit"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Support points edit")); m_normal_cache.clear(); for (const CacheEntry& ce : m_editing_cache) @@ -1125,14 +1132,14 @@ void GLGizmoSlaSupports::get_data_from_backend() void GLGizmoSlaSupports::auto_generate() { wxMessageDialog dlg(GUI::wxGetApp().plater(), - _(L("Autogeneration will erase all manually edited points.")) + "\n\n" + - _(L("Are you sure you want to do it?")) + "\n", - _(L("Warning")), wxICON_WARNING | wxYES | wxNO); + _L("Autogeneration will erase all manually edited points.") + "\n\n" + + _L("Are you sure you want to do it?") + "\n", + _L("Warning"), wxICON_WARNING | wxYES | wxNO); ModelObject* mo = m_c->selection_info()->model_object(); if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points")); wxGetApp().CallAfter([this]() { reslice_SLA_supports(); }); mo->sla_points_status = sla::PointsStatus::Generating; } @@ -1180,7 +1187,7 @@ bool GLGizmoSlaSupports::unsaved_changes() const } SlaGizmoHelpDialog::SlaGizmoHelpDialog() -: wxDialog(nullptr, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +: wxDialog(nullptr, wxID_ANY, _L("SLA gizmo keyboard shortcuts"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); const wxString ctrl = GUI::shortkey_ctrl_prefix(); @@ -1191,7 +1198,7 @@ SlaGizmoHelpDialog::SlaGizmoHelpDialog() const wxFont& font = wxGetApp().small_font(); const wxFont& bold_font = wxGetApp().bold_font(); - auto note_text = new wxStaticText(this, wxID_ANY, _(L("Note: some shortcuts work in (non)editing mode only."))); + auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: some shortcuts work in (non)editing mode only.")); note_text->SetFont(font); auto vsizer = new wxBoxSizer(wxVERTICAL); @@ -1209,21 +1216,21 @@ SlaGizmoHelpDialog::SlaGizmoHelpDialog() vsizer->AddSpacer(20); std::vector> shortcuts; - shortcuts.push_back(std::make_pair(_(L("Left click")), _(L("Add point")))); - shortcuts.push_back(std::make_pair(_(L("Right click")), _(L("Remove point")))); - shortcuts.push_back(std::make_pair(_(L("Drag")), _(L("Move point")))); - shortcuts.push_back(std::make_pair(ctrl+_(L("Left click")), _(L("Add point to selection")))); - shortcuts.push_back(std::make_pair(alt+_(L("Left click")), _(L("Remove point from selection")))); - shortcuts.push_back(std::make_pair(wxString("Shift+")+_(L("Drag")), _(L("Select by rectangle")))); - shortcuts.push_back(std::make_pair(alt+_(L("Drag")), _(L("Deselect by rectangle")))); - shortcuts.push_back(std::make_pair(ctrl+"A", _(L("Select all points")))); - shortcuts.push_back(std::make_pair("Delete", _(L("Remove selected points")))); - shortcuts.push_back(std::make_pair(ctrl+_(L("Mouse wheel")), _(L("Move clipping plane")))); - shortcuts.push_back(std::make_pair("R", _(L("Reset clipping plane")))); - shortcuts.push_back(std::make_pair("Enter", _(L("Apply changes")))); - shortcuts.push_back(std::make_pair("Esc", _(L("Discard changes")))); - shortcuts.push_back(std::make_pair("M", _(L("Switch to editing mode")))); - shortcuts.push_back(std::make_pair("A", _(L("Auto-generate points")))); + shortcuts.push_back(std::make_pair(_L("Left click"), _L("Add point"))); + shortcuts.push_back(std::make_pair(_L("Right click"), _L("Remove point"))); + shortcuts.push_back(std::make_pair(_L("Drag"), _L("Move point"))); + shortcuts.push_back(std::make_pair(ctrl+_L("Left click"), _L("Add point to selection"))); + shortcuts.push_back(std::make_pair(alt+_L("Left click"), _L("Remove point from selection"))); + shortcuts.push_back(std::make_pair(wxString("Shift+")+_L("Drag"), _L("Select by rectangle"))); + shortcuts.push_back(std::make_pair(alt+_(L("Drag")), _L("Deselect by rectangle"))); + shortcuts.push_back(std::make_pair(ctrl+"A", _L("Select all points"))); + shortcuts.push_back(std::make_pair("Delete", _L("Remove selected points"))); + shortcuts.push_back(std::make_pair(ctrl+_L("Mouse wheel"), _L("Move clipping plane"))); + shortcuts.push_back(std::make_pair("R", _L("Reset clipping plane"))); + shortcuts.push_back(std::make_pair("Enter", _L("Apply changes"))); + shortcuts.push_back(std::make_pair("Esc", _L("Discard changes"))); + shortcuts.push_back(std::make_pair("M", _L("Switch to editing mode"))); + shortcuts.push_back(std::make_pair("A", _L("Auto-generate points"))); for (const auto& pair : shortcuts) { auto shortcut = new wxStaticText(this, wxID_ANY, pair.first); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 5d4cd8605a..a14674ed89 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -29,7 +29,7 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac const size_t active_snapshot_time = stack.active_snapshot_time(); const auto it = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot_time)); const int idx = it - snapshots.begin() - 1; - const Slic3r::UndoRedo::Snapshot* ret = (0 < idx && (size_t)idx < snapshots.size() - 1) ? + const Slic3r::UndoRedo::Snapshot* ret = (0 <= idx && (size_t)idx < snapshots.size() - 1) ? &snapshots[idx] : nullptr; assert(ret != nullptr); @@ -39,7 +39,9 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack) { auto skip_main = [](const UndoRedo::Snapshot& snapshot) { - return boost::starts_with(snapshot.name, _utf8("Selection")); + return boost::starts_with(snapshot.name, _utf8("Selection")) || + boost::starts_with(snapshot.name, _utf8("Entering")) || + boost::starts_with(snapshot.name, _utf8("Leaving")); }; const UndoRedo::Snapshot* snapshot = get_active_snapshot(stack); @@ -90,15 +92,12 @@ void ProjectDirtyStateManager::reset_after_save() const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); -// std::cout << "SAVE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; -// std::cout << "SAVE - valid: " << valid_snapshot->timestamp << " - " << valid_snapshot->name << "\n"; - m_last_save.main = valid_snapshot->timestamp; } else { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); -// std::cout << "SAVE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; } wxGetApp().mainframe->update_title(); @@ -147,18 +146,12 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(const Slic3r::Un m_state.plater = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - -// std::cout << "UPDATE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; - if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) return; const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack); - -// std::cout << "UPDATE - valid: " << valid_snapshot->timestamp << " - " << valid_snapshot->name << "\n"; - m_state.plater = valid_snapshot->timestamp != m_last_save.main; } @@ -167,9 +160,6 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(const Slic3r::U m_state.current_gizmo = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - -// std::cout << "UPDATE - active: " << active_snapshot->timestamp << " - " << active_snapshot->name << "\n"; - if (active_snapshot->name == "Gizmos-Initial") return; From e89a14c8a7343753b3390e1317eaecc32f31b638 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 9 Apr 2021 08:26:48 +0200 Subject: [PATCH 06/13] Project dirty state manager -> current gizmo dirty state --- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 104 +++++++++++++++----- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 8 +- 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index a14674ed89..82557c0001 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -37,24 +37,38 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac return ret; } -static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack) { - auto skip_main = [](const UndoRedo::Snapshot& snapshot) { +static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack, size_t last_save_timestamp) { + auto skip_main = [last_save_timestamp](const UndoRedo::Snapshot& snapshot) { return boost::starts_with(snapshot.name, _utf8("Selection")) || - boost::starts_with(snapshot.name, _utf8("Entering")) || - boost::starts_with(snapshot.name, _utf8("Leaving")); + ((boost::starts_with(snapshot.name, _utf8("Entering")) || boost::starts_with(snapshot.name, _utf8("Leaving"))) && + (last_save_timestamp == 0 || last_save_timestamp != snapshot.timestamp)); }; - const UndoRedo::Snapshot* snapshot = get_active_snapshot(stack); - - const UndoRedo::Snapshot* curr = snapshot; + const UndoRedo::Snapshot* curr = get_active_snapshot(stack); const std::vector& snapshots = stack.snapshots(); + size_t shift = 1; while (type == EStackType::Main && skip_main(*curr)) { - curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - 1))); + const UndoRedo::Snapshot* temp = curr; + curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift))); + shift = (curr == temp) ? shift + 1 : 1; } - return curr; } +static std::string extract_gizmo_name(const std::string& s) { + static const std::array prefixes = { _utf8("Entering"), _utf8("Leaving") }; + + std::string ret; + for (const std::string& prefix : prefixes) { + if (boost::starts_with(s, prefix)) + ret = s.substr(prefix.length() + 1); + + if (!ret.empty()) + break; + } + return ret; +} + void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) { if (!wxGetApp().initialized()) @@ -81,25 +95,44 @@ void ProjectDirtyStateManager::update_from_presets() void ProjectDirtyStateManager::reset_after_save() { - reset_initial_presets(); - m_state.reset(); - const Plater* plater = wxGetApp().plater(); const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main(); const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); if (&main_stack == &active_stack) { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack, m_last_save.main); + +// if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { +// if (m_state.current_gizmo) { +// int a = 0; +// } +// else { +// int a = 0; +// } +// } m_last_save.main = valid_snapshot->timestamp; } else { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack); + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Gizmo, active_stack, m_last_save.gizmo); + const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); + if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { + if (m_state.current_gizmo) { + m_last_save.main = main_active_snapshot->timestamp; + } +// else { +// int a = 0; +// } + } + + m_last_save.gizmo = valid_snapshot->timestamp; } + reset_initial_presets(); + m_state.reset(); wxGetApp().mainframe->update_title(); } @@ -123,19 +156,32 @@ void ProjectDirtyStateManager::render_debug_window() const auto text = [](bool value) { return value ? "true" : "false"; }; - auto append_item = [color, text, &imgui](const std::string& name, bool value) { + auto append_bool_item = [color, text, &imgui](const std::string& name, bool value) { imgui.text_colored(color(value), name); ImGui::SameLine(); imgui.text_colored(color(value), text(value)); }; + auto append_int_item = [color, text, &imgui](const std::string& name, int value) { + imgui.text(name); + ImGui::SameLine(); + imgui.text(std::to_string(value)); + }; imgui.begin(std::string( "Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - append_item("State:", is_dirty()); - ImGui::Separator(); - append_item("Plater:", m_state.plater); - append_item("Presets:", m_state.presets); - append_item("Current gizmo:", m_state.current_gizmo); + if (ImGui::CollapsingHeader("Dirty state")) { + append_bool_item("Overall:", is_dirty()); + ImGui::Separator(); + append_bool_item("Plater:", m_state.plater); + append_bool_item("Presets:", m_state.presets); + append_bool_item("Current gizmo:", m_state.current_gizmo); + append_bool_item("Any gizmo:", !m_state.gizmos.empty()); + } + + if (ImGui::CollapsingHeader("Last save timestamps")) { + append_int_item("Main:", m_last_save.main); + append_int_item("Gizmo:", m_last_save.gizmo); + } imgui.end(); } @@ -151,7 +197,16 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(const Slic3r::Un boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) return; - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack); + size_t search_timestamp = 0; + if (boost::starts_with(active_snapshot->name, _utf8("Leaving")) || boost::starts_with(active_snapshot->name, _utf8("Entering"))) { + if (m_state.current_gizmo) + m_state.gizmos.push_back(extract_gizmo_name(active_snapshot->name)); + m_state.current_gizmo = false; + m_last_save.gizmo = 0; + search_timestamp = m_last_save.main; + } + + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack, search_timestamp); m_state.plater = valid_snapshot->timestamp != m_last_save.main; } @@ -160,10 +215,13 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(const Slic3r::U m_state.current_gizmo = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - if (active_snapshot->name == "Gizmos-Initial") + if (active_snapshot->name == "Gizmos-Initial") { + m_state.current_gizmo = (m_last_save.gizmo != 0); return; + } - m_state.current_gizmo = true; + const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Gizmo, stack, m_last_save.gizmo); + m_state.current_gizmo = valid_snapshot->timestamp != m_last_save.gizmo; } } // namespace GUI diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 2eac4ba923..a1fdd1fda8 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -18,16 +18,18 @@ class ProjectDirtyStateManager bool plater{ false }; bool presets{ false }; bool current_gizmo{ false }; + std::vector gizmos; - bool is_dirty() const { return plater || presets || current_gizmo; } + bool is_dirty() const { return plater || presets || current_gizmo || !gizmos.empty(); } void reset() { plater = false; presets = false; current_gizmo = false; + gizmos.clear(); } }; - struct Timestamps + struct LastSaveTimestamps { size_t main{ 0 }; size_t gizmo{ 0 }; @@ -39,7 +41,7 @@ class ProjectDirtyStateManager }; DirtyState m_state; - Timestamps m_last_save; + LastSaveTimestamps m_last_save; // keeps track of initial selected presets std::array m_initial_presets; From 8c3d098ff68674939ce8617ed177d6116cdbe456 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Apr 2021 15:19:03 +0200 Subject: [PATCH 07/13] Project dirty state manager -> management of gizmos dirty state WIP --- src/slic3r/GUI/ImGuiWrapper.cpp | 3 +- src/slic3r/GUI/Plater.cpp | 4 +- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 225 +++++++++++++++----- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 43 +++- 4 files changed, 212 insertions(+), 63 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index db7af046bf..5726477ab4 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -488,8 +488,7 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool ( int i=0; const char* item_text; - while (items_getter(is_undo, i, &item_text)) - { + while (items_getter(is_undo, i, &item_text)) { ImGui::Selectable(item_text, i < hovered); if (ImGui::IsItemHovered()) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5ffd4e51c6..dad3fcef01 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4245,7 +4245,7 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name) 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()); + dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot, undo_redo_stack_main(), undo_redo_stack()); #endif // ENABLE_PROJECT_DIRTY_STATE // Save the last active preset name of a particular printer technology. @@ -4385,7 +4385,7 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator } #if ENABLE_PROJECT_DIRTY_STATE - dirty_state.update_from_undo_redo_stack(undo_redo_stack_main(), undo_redo_stack()); + dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo, undo_redo_stack_main(), undo_redo_stack()); #endif // ENABLE_PROJECT_DIRTY_STATE } diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 82557c0001..dfdeabd022 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -37,22 +37,59 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac return ret; } -static const UndoRedo::Snapshot* get_last_valid_snapshot(EStackType type, const UndoRedo::Stack& stack, size_t last_save_timestamp) { - auto skip_main = [last_save_timestamp](const UndoRedo::Snapshot& snapshot) { - return boost::starts_with(snapshot.name, _utf8("Selection")) || - ((boost::starts_with(snapshot.name, _utf8("Entering")) || boost::starts_with(snapshot.name, _utf8("Leaving"))) && - (last_save_timestamp == 0 || last_save_timestamp != snapshot.timestamp)); +static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos) { + auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) { + if (boost::starts_with(snapshot.name, _utf8("Entering"))) { + if (gizmos.current) + return true; + + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) { + const std::vector& snapshots = stack.snapshots(); + const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(snapshot.timestamp + 1))); + if (gizmos.is_used_and_modified(*leaving_snapshot)) + return true; + } + } + return false; + }; + + auto skip_main = [&gizmos, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) { + if (snapshot.name == _utf8("New Project")) + return true; + else if (snapshot.name == _utf8("Reset Project")) + return true; + else if (boost::starts_with(snapshot.name, _utf8("Load Project:"))) + return true; + else if (boost::starts_with(snapshot.name, _utf8("Selection"))) + return true; + else if (boost::starts_with(snapshot.name, _utf8("Entering"))) { + if (!is_gizmo_with_modifications(snapshot)) + return true; + } + else if (boost::starts_with(snapshot.name, _utf8("Leaving"))) { + if (!gizmos.is_used_and_modified(snapshot)) + return true; + } + + return false; + }; + + auto skip_gizmo = [&gizmos](const UndoRedo::Snapshot& snapshot) { + // put here any needed condition to skip the snapshot + return false; }; const UndoRedo::Snapshot* curr = get_active_snapshot(stack); const std::vector& snapshots = stack.snapshots(); size_t shift = 1; - while (type == EStackType::Main && skip_main(*curr)) { + while (curr->timestamp > 0 && ((type == EStackType::Main && skip_main(*curr)) || (type == EStackType::Gizmo && skip_gizmo(*curr)))) { const UndoRedo::Snapshot* temp = curr; curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift))); shift = (curr == temp) ? shift + 1 : 1; } - return curr; + return curr->timestamp > 0 ? curr : nullptr; } static std::string extract_gizmo_name(const std::string& s) { @@ -69,15 +106,63 @@ static std::string extract_gizmo_name(const std::string& s) { return ret; } -void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) +void ProjectDirtyStateManager::DirtyState::Gizmos::add_used(const UndoRedo::Snapshot& snapshot) +{ + const std::string name = extract_gizmo_name(snapshot.name); + auto it = used.find(name); + if (it == used.end()) + it = used.insert({ name, { {} } }).first; + + it->second.modified_timestamps.push_back(snapshot.timestamp); +} + +void ProjectDirtyStateManager::DirtyState::Gizmos::remove_obsolete_used(const Slic3r::UndoRedo::Stack& main_stack) +{ + const std::vector& snapshots = main_stack.snapshots(); + for (auto& [name, gizmo] : used) { + auto it = gizmo.modified_timestamps.begin(); + while (it != gizmo.modified_timestamps.end()) { + size_t timestamp = *it; + auto snapshot_it = std::find_if(snapshots.begin(), snapshots.end(), [timestamp](const Slic3r::UndoRedo::Snapshot& snapshot) { return snapshot.timestamp == timestamp; }); + if (snapshot_it == snapshots.end()) + it = gizmo.modified_timestamps.erase(it); + else + ++it; + } + } +} + +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +bool ProjectDirtyStateManager::DirtyState::Gizmos::any_used_modified() const +{ + for (auto& [name, gizmo] : used) { + if (!gizmo.modified_timestamps.empty()) + return true; + } + return false; +} +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + +bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const UndoRedo::Snapshot& snapshot) const +{ + for (auto& [name, gizmo] : used) { + for (size_t i : gizmo.modified_timestamps) { + if (i == snapshot.timestamp) + return true; + } + } + return false; +} + +void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) { if (!wxGetApp().initialized()) return; if (&main_stack == &active_stack) - update_from_undo_redo_main_stack(main_stack); + update_from_undo_redo_main_stack(type, main_stack); else - update_from_undo_redo_gizmo_stack(active_stack); + update_from_undo_redo_gizmo_stack(type, active_stack); wxGetApp().mainframe->update_title(); } @@ -101,34 +186,21 @@ void ProjectDirtyStateManager::reset_after_save() if (&main_stack == &active_stack) { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, main_stack, m_last_save.main); - -// if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { -// if (m_state.current_gizmo) { -// int a = 0; -// } -// else { -// int a = 0; -// } -// } - - m_last_save.main = valid_snapshot->timestamp; + const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos); + assert(saveable_snapshot != nullptr); + m_last_save.main = saveable_snapshot->timestamp; } else { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Gizmo, active_stack, m_last_save.gizmo); - + const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, active_stack, m_state.gizmos); const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { - if (m_state.current_gizmo) { + if (m_state.gizmos.current) { m_last_save.main = main_active_snapshot->timestamp; } -// else { -// int a = 0; -// } } - m_last_save.gizmo = valid_snapshot->timestamp; + m_last_save.gizmo = saveable_snapshot->timestamp; } reset_initial_presets(); @@ -153,75 +225,128 @@ void ProjectDirtyStateManager::render_debug_window() const auto color = [](bool value) { return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); }; - auto text = [](bool value) { + auto bool_to_text = [](bool value) { return value ? "true" : "false"; }; - auto append_bool_item = [color, text, &imgui](const std::string& name, bool value) { + auto append_bool_item = [color, bool_to_text, &imgui](const std::string& name, bool value) { imgui.text_colored(color(value), name); ImGui::SameLine(); - imgui.text_colored(color(value), text(value)); + imgui.text_colored(color(value), bool_to_text(value)); }; - auto append_int_item = [color, text, &imgui](const std::string& name, int value) { + auto append_int_item = [&imgui](const std::string& name, int value) { imgui.text(name); ImGui::SameLine(); imgui.text(std::to_string(value)); }; + auto append_snapshot_item = [&imgui](const std::string& label, const UndoRedo::Snapshot* snapshot) { + imgui.text(label); + ImGui::SameLine(100); + if (snapshot != nullptr) + imgui.text(snapshot->name + " (" + std::to_string(snapshot->timestamp) + ")"); + else + imgui.text("-"); + }; - imgui.begin(std::string( "Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + imgui.begin(std::string("Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - if (ImGui::CollapsingHeader("Dirty state")) { + if (ImGui::CollapsingHeader("Dirty state", ImGuiTreeNodeFlags_DefaultOpen)) { append_bool_item("Overall:", is_dirty()); ImGui::Separator(); append_bool_item("Plater:", m_state.plater); append_bool_item("Presets:", m_state.presets); - append_bool_item("Current gizmo:", m_state.current_gizmo); - append_bool_item("Any gizmo:", !m_state.gizmos.empty()); + append_bool_item("Current gizmo:", m_state.gizmos.current); } - if (ImGui::CollapsingHeader("Last save timestamps")) { + if (ImGui::CollapsingHeader("Last save timestamps", ImGuiTreeNodeFlags_DefaultOpen)) { append_int_item("Main:", m_last_save.main); append_int_item("Gizmo:", m_last_save.gizmo); } + if (ImGui::CollapsingHeader("Main snapshots", ImGuiTreeNodeFlags_DefaultOpen)) { + const UndoRedo::Stack& stack = wxGetApp().plater()->undo_redo_stack_main(); + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + append_snapshot_item("Active:", active_snapshot); + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos); + append_snapshot_item("Last saveable:", last_saveable_snapshot); + if (ImGui::CollapsingHeader("Main undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) { + const std::vector& snapshots = stack.snapshots(); + for (const UndoRedo::Snapshot& snapshot : snapshots) { + bool active = active_snapshot->timestamp == snapshot.timestamp; + imgui.text_colored(color(active), snapshot.name); + ImGui::SameLine(150); + imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")"); + if (&snapshot == last_saveable_snapshot) { + ImGui::SameLine(); + imgui.text_colored(color(active), " (S)"); + } + } + } + } + + if (m_state.gizmos.any_used_modified()) { + if (ImGui::CollapsingHeader("Gizmos", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Indent(10.0f); + for (const auto& [name, gizmo] : m_state.gizmos.used) { + if (!gizmo.modified_timestamps.empty()) { + if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + for (size_t i : gizmo.modified_timestamps) { + imgui.text(std::to_string(i)); + } + } + } + } + ImGui::Unindent(10.0f); + } + } + imgui.end(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW -void ProjectDirtyStateManager::update_from_undo_redo_main_stack(const Slic3r::UndoRedo::Stack& stack) +void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack) { m_state.plater = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + + if (type == UpdateType::TakeSnapshot) + m_state.gizmos.remove_obsolete_used(stack); + if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) return; size_t search_timestamp = 0; - if (boost::starts_with(active_snapshot->name, _utf8("Leaving")) || boost::starts_with(active_snapshot->name, _utf8("Entering"))) { - if (m_state.current_gizmo) - m_state.gizmos.push_back(extract_gizmo_name(active_snapshot->name)); - m_state.current_gizmo = false; + if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { + m_state.gizmos.current = false; + m_last_save.gizmo = 0; + search_timestamp = m_last_save.main; + } + else if (boost::starts_with(active_snapshot->name, _utf8("Leaving"))) { + if (m_state.gizmos.current) + m_state.gizmos.add_used(*active_snapshot); + m_state.gizmos.current = false; m_last_save.gizmo = 0; search_timestamp = m_last_save.main; } - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Main, stack, search_timestamp); - m_state.plater = valid_snapshot->timestamp != m_last_save.main; + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos); + m_state.plater = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.main); } -void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(const Slic3r::UndoRedo::Stack& stack) +void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack) { - m_state.current_gizmo = false; + m_state.gizmos.current = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); if (active_snapshot->name == "Gizmos-Initial") { - m_state.current_gizmo = (m_last_save.gizmo != 0); + m_state.gizmos.current = (m_last_save.gizmo != 0); return; } - const UndoRedo::Snapshot* valid_snapshot = get_last_valid_snapshot(EStackType::Gizmo, stack, m_last_save.gizmo); - m_state.current_gizmo = valid_snapshot->timestamp != m_last_save.gizmo; + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos); + m_state.gizmos.current = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.gizmo); } } // namespace GUI diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index a1fdd1fda8..a7c16705b0 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -8,27 +8,52 @@ namespace Slic3r { namespace UndoRedo { class Stack; +struct Snapshot; } // namespace UndoRedo -namespace GUI { +namespace GUI { class ProjectDirtyStateManager { +public: + enum class UpdateType : unsigned char + { + TakeSnapshot, + UndoRedoTo + }; + struct DirtyState { + struct Gizmos + { + struct Gizmo + { + std::vector modified_timestamps; + }; + + bool current{ false }; + std::map used; + + void add_used(const UndoRedo::Snapshot& snapshot); + void remove_obsolete_used(const Slic3r::UndoRedo::Stack& main_stack); +#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + bool any_used_modified() const; +#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW + bool is_used_and_modified(const UndoRedo::Snapshot& snapshot) const; + }; + bool plater{ false }; bool presets{ false }; - bool current_gizmo{ false }; - std::vector gizmos; + Gizmos gizmos; - bool is_dirty() const { return plater || presets || current_gizmo || !gizmos.empty(); } + bool is_dirty() const { return plater || presets || gizmos.current; } void reset() { plater = false; presets = false; - current_gizmo = false; - gizmos.clear(); + gizmos.current = false; } }; +private: struct LastSaveTimestamps { size_t main{ 0 }; @@ -48,7 +73,7 @@ class ProjectDirtyStateManager public: 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_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack); void update_from_presets(); void reset_after_save(); void reset_initial_presets(); @@ -57,8 +82,8 @@ public: #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW private: - void update_from_undo_redo_main_stack(const Slic3r::UndoRedo::Stack& stack); - void update_from_undo_redo_gizmo_stack(const Slic3r::UndoRedo::Stack& stack); + void update_from_undo_redo_main_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack); + void update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack); }; } // namespace GUI From ce73671f475583b4cedab2d8f17b1d695e16fd03 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Apr 2021 09:36:19 +0200 Subject: [PATCH 08/13] Project dirty state manager -> Improvements to management of gizmos dirty state --- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 37 ++++++++++++++++++++ src/slic3r/GUI/ProjectDirtyStateManager.cpp | 22 ++++++++++-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 91aef75d9a..3a932c5985 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -32,6 +32,42 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic +#if ENABLE_PROJECT_DIRTY_STATE +// port of 948bc382655993721d93d3b9fce9b0186fcfb211 +void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) +{ + Plater* plater = wxGetApp().plater(); + + // Following is needed to prevent taking an extra snapshot when the activation of + // the internal stack happens when the gizmo is already active (such as open gizmo, + // close gizmo, undo, start painting). The internal stack does not activate on the + // undo, because that would obliterate all future of the main stack (user would + // have to close the gizmo himself, he has no access to main undo/redo after the + // internal stack opens). We don't want the "entering" snapshot taken in this case, + // because there already is one. + std::string last_snapshot_name; + plater->undo_redo_topmost_string_getter(plater->can_undo(), last_snapshot_name); + + if (activate && !m_internal_stack_active) { + std::string str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS + ? _u8L("Entering Paint-on supports") + : _u8L("Entering Seam painting"); + if (last_snapshot_name != str) + Plater::TakeSnapshot(plater, str); + plater->enter_gizmos_stack(); + m_internal_stack_active = true; + } + if (!activate && m_internal_stack_active) { + plater->leave_gizmos_stack(); + std::string str = get_painter_type() == PainterGizmoType::SEAM + ? _u8L("Leaving Seam painting") + : _u8L("Leaving Paint-on supports"); + if (last_snapshot_name != str) + Plater::TakeSnapshot(plater, str); + m_internal_stack_active = false; + } +} +#else void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) { if (activate && ! m_internal_stack_active) { @@ -51,6 +87,7 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) m_internal_stack_active = false; } } +#endif // ENABLE_PROJECT_DIRTY_STATE diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index dfdeabd022..06f54359fd 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -259,7 +259,7 @@ void ProjectDirtyStateManager::render_debug_window() const if (ImGui::CollapsingHeader("Last save timestamps", ImGuiTreeNodeFlags_DefaultOpen)) { append_int_item("Main:", m_last_save.main); - append_int_item("Gizmo:", m_last_save.gizmo); + append_int_item("Current gizmo:", m_last_save.gizmo); } if (ImGui::CollapsingHeader("Main snapshots", ImGuiTreeNodeFlags_DefaultOpen)) { @@ -279,6 +279,10 @@ void ProjectDirtyStateManager::render_debug_window() const ImGui::SameLine(); imgui.text_colored(color(active), " (S)"); } + if (m_last_save.main > 0 && m_last_save.main == snapshot.timestamp) { + ImGui::SameLine(); + imgui.text_colored(color(active), " (LS)"); + } } } } @@ -307,11 +311,11 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, { m_state.plater = false; - const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - if (type == UpdateType::TakeSnapshot) m_state.gizmos.remove_obsolete_used(stack); + const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); + if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) @@ -319,6 +323,18 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, size_t search_timestamp = 0; if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) { + if (type == UpdateType::UndoRedoTo) { + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) { + const std::vector& snapshots = stack.snapshots(); + const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot->timestamp + 1))); + if (m_state.gizmos.is_used_and_modified(*leaving_snapshot)) { + m_state.plater = (leaving_snapshot != nullptr && leaving_snapshot->timestamp != m_last_save.main); + return; + } + } + } m_state.gizmos.current = false; m_last_save.gizmo = 0; search_timestamp = m_last_save.main; From c691464659e634934a889dd6226697671ff27cf1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 09:09:06 +0200 Subject: [PATCH 09/13] Project dirty state manager -> Improvements update of plater dirty state after save commands --- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 102 +++++++++++++------- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 1 + 2 files changed, 70 insertions(+), 33 deletions(-) diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 06f54359fd..9c6d097d77 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -37,7 +37,8 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac return ret; } -static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos) { +static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, + const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos, size_t last_save_main) { auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) { if (boost::starts_with(snapshot.name, _utf8("Entering"))) { if (gizmos.current) @@ -55,7 +56,7 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con return false; }; - auto skip_main = [&gizmos, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) { + auto skip_main = [&gizmos, last_save_main, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) { if (snapshot.name == _utf8("New Project")) return true; else if (snapshot.name == _utf8("Reset Project")) @@ -65,11 +66,11 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con else if (boost::starts_with(snapshot.name, _utf8("Selection"))) return true; else if (boost::starts_with(snapshot.name, _utf8("Entering"))) { - if (!is_gizmo_with_modifications(snapshot)) + if (last_save_main != snapshot.timestamp + 1 && !is_gizmo_with_modifications(snapshot)) return true; } else if (boost::starts_with(snapshot.name, _utf8("Leaving"))) { - if (!gizmos.is_used_and_modified(snapshot)) + if (last_save_main != snapshot.timestamp && !gizmos.is_used_and_modified(snapshot)) return true; } @@ -89,6 +90,12 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift))); shift = (curr == temp) ? shift + 1 : 1; } + if (boost::starts_with(curr->name, _utf8("Entering")) && last_save_main == curr->timestamp + 1) { + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) + curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp + 1))); + } return curr->timestamp > 0 ? curr : nullptr; } @@ -154,6 +161,11 @@ bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const Un return false; } +void ProjectDirtyStateManager::DirtyState::Gizmos::reset() +{ + used.clear(); +} + void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) { if (!wxGetApp().initialized()) @@ -186,20 +198,18 @@ void ProjectDirtyStateManager::reset_after_save() if (&main_stack == &active_stack) { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); - const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos); + const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main); assert(saveable_snapshot != nullptr); m_last_save.main = saveable_snapshot->timestamp; } else { const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); - const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, active_stack, m_state.gizmos); const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { - if (m_state.gizmos.current) { - m_last_save.main = main_active_snapshot->timestamp; - } + if (m_state.gizmos.current) + m_last_save.main = main_active_snapshot->timestamp + 1; } - + const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, active_stack, m_state.gizmos, m_last_save.main); m_last_save.gizmo = saveable_snapshot->timestamp; } @@ -262,27 +272,43 @@ void ProjectDirtyStateManager::render_debug_window() const append_int_item("Current gizmo:", m_last_save.gizmo); } + const UndoRedo::Stack& main_stack = wxGetApp().plater()->undo_redo_stack_main(); + const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); + const UndoRedo::Snapshot* main_last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main); + const std::vector& main_snapshots = main_stack.snapshots(); + if (ImGui::CollapsingHeader("Main snapshots", ImGuiTreeNodeFlags_DefaultOpen)) { - const UndoRedo::Stack& stack = wxGetApp().plater()->undo_redo_stack_main(); - const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - append_snapshot_item("Active:", active_snapshot); - const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos); - append_snapshot_item("Last saveable:", last_saveable_snapshot); - if (ImGui::CollapsingHeader("Main undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) { - const std::vector& snapshots = stack.snapshots(); - for (const UndoRedo::Snapshot& snapshot : snapshots) { - bool active = active_snapshot->timestamp == snapshot.timestamp; + append_snapshot_item("Active:", main_active_snapshot); + append_snapshot_item("Last saveable:", main_last_saveable_snapshot); + } + + if (ImGui::CollapsingHeader("Main undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) { + for (const UndoRedo::Snapshot& snapshot : main_snapshots) { + bool active = main_active_snapshot->timestamp == snapshot.timestamp; + imgui.text_colored(color(active), snapshot.name); + ImGui::SameLine(150); + imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")"); + if (&snapshot == main_last_saveable_snapshot) { + ImGui::SameLine(); + imgui.text_colored(color(active), " (S)"); + } + if (m_last_save.main > 0 && m_last_save.main == snapshot.timestamp) { + ImGui::SameLine(); + imgui.text_colored(color(active), " (LS)"); + } + } + } + + const UndoRedo::Stack& active_stack = wxGetApp().plater()->undo_redo_stack_active(); + if (&active_stack != &main_stack) { + if (ImGui::CollapsingHeader("Gizmo undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) { + const UndoRedo::Snapshot* active_active_snapshot = get_active_snapshot(active_stack); + const std::vector& active_snapshots = active_stack.snapshots(); + for (const UndoRedo::Snapshot& snapshot : active_snapshots) { + bool active = active_active_snapshot->timestamp == snapshot.timestamp; imgui.text_colored(color(active), snapshot.name); ImGui::SameLine(150); imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")"); - if (&snapshot == last_saveable_snapshot) { - ImGui::SameLine(); - imgui.text_colored(color(active), " (S)"); - } - if (m_last_save.main > 0 && m_last_save.main == snapshot.timestamp) { - ImGui::SameLine(); - imgui.text_colored(color(active), " (LS)"); - } } } } @@ -293,9 +319,13 @@ void ProjectDirtyStateManager::render_debug_window() const for (const auto& [name, gizmo] : m_state.gizmos.used) { if (!gizmo.modified_timestamps.empty()) { if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - for (size_t i : gizmo.modified_timestamps) { - imgui.text(std::to_string(i)); + std::string modified_timestamps; + for (size_t i = 0; i < gizmo.modified_timestamps.size(); ++i) { + if (i > 0) + modified_timestamps += " | "; + modified_timestamps += std::to_string(gizmo.modified_timestamps[i]); } + imgui.text(modified_timestamps); } } } @@ -311,11 +341,17 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, { m_state.plater = false; - if (type == UpdateType::TakeSnapshot) + if (type == UpdateType::TakeSnapshot) { + if (m_last_save.main != 0) { + const std::vector& snapshots = stack.snapshots(); + auto snapshot_it = std::find_if(snapshots.begin(), snapshots.end(), [this](const Slic3r::UndoRedo::Snapshot& snapshot) { return snapshot.timestamp == m_last_save.main; }); + if (snapshot_it == snapshots.end()) + m_last_save.main = 0; + } m_state.gizmos.remove_obsolete_used(stack); + } const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - if (active_snapshot->name == _utf8("New Project") || active_snapshot->name == _utf8("Reset Project") || boost::starts_with(active_snapshot->name, _utf8("Load Project:"))) @@ -347,7 +383,7 @@ void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, search_timestamp = m_last_save.main; } - const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos); + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos, m_last_save.main); m_state.plater = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.main); } @@ -361,7 +397,7 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type return; } - const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos); + const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos, m_last_save.main); m_state.gizmos.current = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.gizmo); } diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index a7c16705b0..79a16edc4e 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -39,6 +39,7 @@ public: bool any_used_modified() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW bool is_used_and_modified(const UndoRedo::Snapshot& snapshot) const; + void reset(); }; bool plater{ false }; From f486dedb52754d9db3ba060b75650b089f005b9c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 10:41:38 +0200 Subject: [PATCH 10/13] Disabled tech ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW --- src/libslic3r/Technologies.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index fcb59f1a10..a74ff55ae5 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -61,7 +61,7 @@ // Enable project dirty state manager #define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) // Enable project dirty state manager debug window -#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (1 && ENABLE_PROJECT_DIRTY_STATE) +#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE) #endif // _prusaslicer_technologies_h_ From 9cd5ba13f24161b341466d6e2a40150705b5ff85 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 16:07:39 +0200 Subject: [PATCH 11/13] Some refactoring into ProjectDirtyStateManager --- src/slic3r/GUI/Plater.cpp | 4 +- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 66 ++++++++++++--------- src/slic3r/GUI/ProjectDirtyStateManager.hpp | 2 +- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index dad3fcef01..0da345c1e4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4245,7 +4245,7 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name) this->undo_redo_stack().release_least_recently_used(); #if ENABLE_PROJECT_DIRTY_STATE - dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot, undo_redo_stack_main(), undo_redo_stack()); + dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot); #endif // ENABLE_PROJECT_DIRTY_STATE // Save the last active preset name of a particular printer technology. @@ -4385,7 +4385,7 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator } #if ENABLE_PROJECT_DIRTY_STATE - dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo, undo_redo_stack_main(), undo_redo_stack()); + dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo); #endif // ENABLE_PROJECT_DIRTY_STATE } diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 9c6d097d77..95cfa042e9 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -24,6 +24,7 @@ enum class EStackType Gizmo }; +// returns the current active snapshot (the topmost snapshot in the undo part of the stack) in the given stack static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stack) { const std::vector& snapshots = stack.snapshots(); const size_t active_snapshot_time = stack.active_snapshot_time(); @@ -33,30 +34,32 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac &snapshots[idx] : nullptr; assert(ret != nullptr); - return ret; } +// returns the last saveable snapshot (the topmost snapshot in the undo part of the stack that can be saved) in the given stack static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos, size_t last_save_main) { - auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) { - if (boost::starts_with(snapshot.name, _utf8("Entering"))) { - if (gizmos.current) - return true; - std::string topmost_redo; - wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); - if (boost::starts_with(topmost_redo, _utf8("Leaving"))) { - const std::vector& snapshots = stack.snapshots(); - const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(snapshot.timestamp + 1))); - if (gizmos.is_used_and_modified(*leaving_snapshot)) + // returns true if the given snapshot is not saveable + auto skip_main = [&gizmos, last_save_main, &stack](const UndoRedo::Snapshot& snapshot) { + auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) { + if (boost::starts_with(snapshot.name, _utf8("Entering"))) { + if (gizmos.current) return true; - } - } - return false; - }; - auto skip_main = [&gizmos, last_save_main, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) { + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) { + const std::vector& snapshots = stack.snapshots(); + const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(snapshot.timestamp + 1))); + if (gizmos.is_used_and_modified(*leaving_snapshot)) + return true; + } + } + return false; + }; + if (snapshot.name == _utf8("New Project")) return true; else if (snapshot.name == _utf8("Reset Project")) @@ -77,7 +80,8 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con return false; }; - auto skip_gizmo = [&gizmos](const UndoRedo::Snapshot& snapshot) { + // returns true if the given snapshot is not saveable + auto skip_gizmo = [](const UndoRedo::Snapshot& snapshot) { // put here any needed condition to skip the snapshot return false; }; @@ -90,15 +94,18 @@ static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, con curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift))); shift = (curr == temp) ? shift + 1 : 1; } - if (boost::starts_with(curr->name, _utf8("Entering")) && last_save_main == curr->timestamp + 1) { - std::string topmost_redo; - wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); - if (boost::starts_with(topmost_redo, _utf8("Leaving"))) - curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp + 1))); + if (type == EStackType::Main) { + if (boost::starts_with(curr->name, _utf8("Entering")) && last_save_main == curr->timestamp + 1) { + std::string topmost_redo; + wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo); + if (boost::starts_with(topmost_redo, _utf8("Leaving"))) + curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp + 1))); + } } return curr->timestamp > 0 ? curr : nullptr; } +// returns the name of the gizmo contained in the given string static std::string extract_gizmo_name(const std::string& s) { static const std::array prefixes = { _utf8("Entering"), _utf8("Leaving") }; @@ -150,6 +157,7 @@ bool ProjectDirtyStateManager::DirtyState::Gizmos::any_used_modified() const } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +// returns true if the given snapshot is contained in any of the gizmos caches bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const UndoRedo::Snapshot& snapshot) const { for (auto& [name, gizmo] : used) { @@ -166,11 +174,15 @@ void ProjectDirtyStateManager::DirtyState::Gizmos::reset() used.clear(); } -void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack) +void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type) { if (!wxGetApp().initialized()) return; + const Plater* plater = wxGetApp().plater(); + const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main(); + const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); + if (&main_stack == &active_stack) update_from_undo_redo_main_stack(type, main_stack); else @@ -197,13 +209,11 @@ void ProjectDirtyStateManager::reset_after_save() const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active(); if (&main_stack == &active_stack) { - const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack); const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main); assert(saveable_snapshot != nullptr); m_last_save.main = saveable_snapshot->timestamp; } else { - const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack); const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { if (m_state.gizmos.current) @@ -233,7 +243,7 @@ void ProjectDirtyStateManager::render_debug_window() const ImGuiWrapper& imgui = *wxGetApp().imgui(); auto color = [](bool value) { - return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) /* orange */: ImVec4(1.0f, 1.0f, 1.0f, 1.0f) /* white */; }; auto bool_to_text = [](bool value) { return value ? "true" : "false"; @@ -392,10 +402,8 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type m_state.gizmos.current = false; const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack); - if (active_snapshot->name == "Gizmos-Initial") { - m_state.gizmos.current = (m_last_save.gizmo != 0); + if (active_snapshot->name == "Gizmos-Initial") return; - } const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos, m_last_save.main); m_state.gizmos.current = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.gizmo); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 79a16edc4e..f7ce81a623 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -74,7 +74,7 @@ private: public: bool is_dirty() const { return m_state.is_dirty(); } - void update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack); + void update_from_undo_redo_stack(UpdateType type); void update_from_presets(); void reset_after_save(); void reset_initial_presets(); From a91306032ca5d3a6e04d11d18bb24f5b2b39d21d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 5 May 2021 13:17:20 +0200 Subject: [PATCH 12/13] Project dirty state manager -> Fixed crash when loading/saving a 3mf file --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/ProjectDirtyStateManager.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index efb7ab4861..1d4c63fb62 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -67,7 +67,7 @@ // Enable project dirty state manager #define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) // Enable project dirty state manager debug window -#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE) +#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (1 && ENABLE_PROJECT_DIRTY_STATE) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 95cfa042e9..30d41f8081 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -210,8 +210,7 @@ void ProjectDirtyStateManager::reset_after_save() if (&main_stack == &active_stack) { const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main); - assert(saveable_snapshot != nullptr); - m_last_save.main = saveable_snapshot->timestamp; + m_last_save.main = (saveable_snapshot != nullptr) ? saveable_snapshot->timestamp : 0; } else { const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); From 389955966cce168b46dc985f32121658bb8cf853 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 May 2021 13:42:53 +0200 Subject: [PATCH 13/13] Disabled tech ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW --- src/libslic3r/Technologies.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 4cc5fbfec2..99684e93e0 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,7 +69,7 @@ // Enable project dirty state manager #define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0) // Enable project dirty state manager debug window -#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (1 && ENABLE_PROJECT_DIRTY_STATE) +#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE) #endif // _prusaslicer_technologies_h_