mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-25 07:34:03 -06:00
Redo of the project state, implementation of Undo/Redo stack compression
This commit is contained in:
parent
96f4d71c71
commit
1f3b272d77
9 changed files with 222 additions and 376 deletions
|
@ -421,7 +421,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
&& m_button_down != Button::None) {
|
||||
// Take snapshot and update ModelVolume data.
|
||||
wxString action_name = this->handle_snapshot_action_name(shift_down, m_button_down);
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), action_name);
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), action_name, UndoRedo::SnapshotType::GizmoAction);
|
||||
update_model_object();
|
||||
|
||||
m_button_down = Button::None;
|
||||
|
|
|
@ -1220,13 +1220,15 @@ bool GLGizmosManager::activate_gizmo(EType type)
|
|||
if (! m_parent.get_gizmos_manager().is_serializing()
|
||||
&& old_gizmo->wants_enter_leave_snapshots())
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(),
|
||||
Slic3r::format(_utf8("Leaving %1%"), old_gizmo->get_name(false)));
|
||||
Slic3r::format(_utf8("Leaving %1%"), old_gizmo->get_name(false)),
|
||||
UndoRedo::SnapshotType::LeavingGizmoWithAction);
|
||||
}
|
||||
|
||||
if (new_gizmo && ! m_parent.get_gizmos_manager().is_serializing()
|
||||
&& new_gizmo->wants_enter_leave_snapshots())
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(),
|
||||
Slic3r::format(_utf8("Entering %1%"), new_gizmo->get_name(false)));
|
||||
Slic3r::format(_utf8("Entering %1%"), new_gizmo->get_name(false)),
|
||||
UndoRedo::SnapshotType::EnteringGizmo);
|
||||
|
||||
m_current = type;
|
||||
|
||||
|
|
|
@ -620,7 +620,8 @@ 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());
|
||||
wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : "";
|
||||
// wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : "";
|
||||
wxString dirty_marker = m_plater->is_project_dirty() ? "*" : "";
|
||||
if (!dirty_marker.empty() || !project.empty()) {
|
||||
if (!dirty_marker.empty() && project.empty())
|
||||
project = _L("Untitled");
|
||||
|
|
|
@ -1595,7 +1595,7 @@ struct Plater::priv
|
|||
}
|
||||
return res;
|
||||
}
|
||||
void reset_project_dirty_after_save() { dirty_state.reset_after_save(); }
|
||||
void reset_project_dirty_after_save() { m_undo_redo_stack_main.mark_current_as_saved(); dirty_state.reset_after_save(); }
|
||||
void reset_project_dirty_initial_presets() { dirty_state.reset_initial_presets(); }
|
||||
|
||||
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
|
@ -2064,6 +2064,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
|
||||
// Initialize the Undo / Redo stack with a first snapshot.
|
||||
this->take_snapshot(_L("New Project"), UndoRedo::SnapshotType::ProjectSeparator);
|
||||
// Reset the "dirty project" flag.
|
||||
m_undo_redo_stack_main.mark_current_as_saved();
|
||||
dirty_state.update_from_undo_redo_stack(false);
|
||||
|
||||
this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent& evt) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Received load from other instance event.";
|
||||
|
@ -4693,9 +4696,12 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed
|
|||
model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle");
|
||||
}
|
||||
this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), snapshot_data);
|
||||
if (snapshot_type == UndoRedo::SnapshotType::LeavingGizmoWithAction)
|
||||
// Filter all but the last UndoRedo::SnapshotType::GizmoAction in a row between the last UndoRedo::SnapshotType::EnteringGizmo and UndoRedo::SnapshotType::LeavingGizmoWithAction.
|
||||
this->undo_redo_stack().reduce_noisy_snapshots();
|
||||
this->undo_redo_stack().release_least_recently_used();
|
||||
|
||||
dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot);
|
||||
dirty_state.update_from_undo_redo_stack(m_undo_redo_stack_main.project_modified());
|
||||
|
||||
// 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();
|
||||
|
@ -4832,7 +4838,7 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
|||
view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting"));
|
||||
}
|
||||
|
||||
dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo);
|
||||
dirty_state.update_from_undo_redo_stack(m_undo_redo_stack_main.project_modified());
|
||||
}
|
||||
|
||||
void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */)
|
||||
|
@ -6743,7 +6749,6 @@ 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(); }
|
||||
const UndoRedo::Stack& Plater::undo_redo_stack_active() const { return p->undo_redo_stack(); }
|
||||
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(); }
|
||||
|
|
|
@ -254,7 +254,6 @@ public:
|
|||
// For the memory statistics.
|
||||
const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
|
||||
void clear_undo_redo_stack_main();
|
||||
const Slic3r::UndoRedo::Stack& undo_redo_stack_active() const;
|
||||
// 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();
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "MainFrame.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "../Utils/UndoRedo.hpp"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
@ -16,226 +15,38 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
enum class EStackType
|
||||
void ProjectDirtyStateManager::update_from_undo_redo_stack(bool dirty)
|
||||
{
|
||||
Main,
|
||||
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<UndoRedo::Snapshot>& 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 && idx < int(snapshots.size()) - 1) ?
|
||||
&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) {
|
||||
|
||||
// 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;
|
||||
|
||||
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<UndoRedo::Snapshot>& 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"))
|
||||
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 (last_save_main != snapshot.timestamp + 1 && !is_gizmo_with_modifications(snapshot))
|
||||
return true;
|
||||
}
|
||||
else if (boost::starts_with(snapshot.name, _utf8("Leaving"))) {
|
||||
if (last_save_main != snapshot.timestamp && !gizmos.is_used_and_modified(snapshot))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
const UndoRedo::Snapshot* curr = get_active_snapshot(stack);
|
||||
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
||||
size_t shift = 1;
|
||||
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;
|
||||
}
|
||||
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<std::string, 2> 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::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<UndoRedo::Snapshot>& snapshots = main_stack.snapshots();
|
||||
for (auto& item : used) {
|
||||
auto it = item.second.modified_timestamps.begin();
|
||||
while (it != item.second.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 = item.second.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
|
||||
|
||||
// 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 (const auto& item : used) {
|
||||
for (size_t i : item.second.modified_timestamps) {
|
||||
if (i == snapshot.timestamp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::DirtyState::Gizmos::reset()
|
||||
{
|
||||
used.clear();
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type)
|
||||
{
|
||||
if (!wxGetApp().initialized())
|
||||
return;
|
||||
|
||||
const Plater* plater = wxGetApp().plater();
|
||||
if (plater == nullptr)
|
||||
return;
|
||||
|
||||
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
|
||||
update_from_undo_redo_gizmo_stack(type, active_stack);
|
||||
|
||||
wxGetApp().mainframe->update_title();
|
||||
m_plater_dirty = dirty;
|
||||
if (const Plater *plater = wxGetApp().plater(); plater && wxGetApp().initialized())
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::update_from_presets()
|
||||
{
|
||||
m_state.presets = false;
|
||||
m_presets_dirty = false;
|
||||
// check switching of the presets only for exist/loaded project, but not for new
|
||||
if (!wxGetApp().plater()->get_project_filename().IsEmpty()) {
|
||||
for (const auto& [type, name] : wxGetApp().get_selected_presets())
|
||||
m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
|
||||
m_presets_dirty |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
|
||||
}
|
||||
m_state.presets |= wxGetApp().has_unsaved_preset_changes();
|
||||
m_presets_dirty |= wxGetApp().has_unsaved_preset_changes();
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::reset_after_save()
|
||||
{
|
||||
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* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main);
|
||||
m_last_save.main = (saveable_snapshot != nullptr) ? saveable_snapshot->timestamp : 0;
|
||||
}
|
||||
else {
|
||||
// Gizmo is active with its own Undo / Redo stack (for example the SLA support point editing 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.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 != nullptr) ? saveable_snapshot->timestamp : 0;
|
||||
}
|
||||
|
||||
reset_initial_presets();
|
||||
m_state.reset();
|
||||
this->reset_initial_presets();
|
||||
m_plater_dirty = false;
|
||||
m_presets_dirty = false;
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::reset_initial_presets()
|
||||
{
|
||||
m_initial_presets = std::array<std::string, Preset::TYPE_COUNT>();
|
||||
for (const auto& [type, name] : wxGetApp().get_selected_presets()) {
|
||||
m_initial_presets.fill(std::string{});
|
||||
for (const auto& [type, name] : wxGetApp().get_selected_presets())
|
||||
m_initial_presets[type] = name;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
|
@ -324,89 +135,10 @@ void ProjectDirtyStateManager::render_debug_window() const
|
|||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Unindent(10.0f);
|
||||
}
|
||||
}
|
||||
|
||||
imgui.end();
|
||||
}
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
|
||||
void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack)
|
||||
{
|
||||
m_state.plater = false;
|
||||
|
||||
if (type == UpdateType::TakeSnapshot) {
|
||||
if (m_last_save.main != 0) {
|
||||
const std::vector<UndoRedo::Snapshot>& 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")))
|
||||
return;
|
||||
|
||||
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<UndoRedo::Snapshot>& 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack)
|
||||
{
|
||||
m_state.gizmos.current = false;
|
||||
|
||||
const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack);
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -4,89 +4,32 @@
|
|||
#include "libslic3r/Preset.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace UndoRedo {
|
||||
class Stack;
|
||||
struct Snapshot;
|
||||
} // namespace UndoRedo
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class ProjectDirtyStateManager
|
||||
{
|
||||
public:
|
||||
enum class UpdateType : unsigned char
|
||||
{
|
||||
TakeSnapshot,
|
||||
UndoRedoTo
|
||||
};
|
||||
|
||||
struct DirtyState
|
||||
{
|
||||
struct Gizmos
|
||||
{
|
||||
struct Gizmo
|
||||
{
|
||||
std::vector<size_t> modified_timestamps;
|
||||
};
|
||||
|
||||
bool current{ false };
|
||||
std::map<std::string, Gizmo> 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;
|
||||
void reset();
|
||||
};
|
||||
|
||||
bool plater{ false };
|
||||
bool presets{ false };
|
||||
Gizmos gizmos;
|
||||
|
||||
bool is_dirty() const { return plater || presets || gizmos.current; }
|
||||
void reset() {
|
||||
plater = false;
|
||||
presets = false;
|
||||
gizmos.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
struct LastSaveTimestamps
|
||||
{
|
||||
size_t main{ 0 };
|
||||
size_t gizmo{ 0 };
|
||||
|
||||
void reset() {
|
||||
main = 0;
|
||||
gizmo = 0;
|
||||
}
|
||||
};
|
||||
|
||||
DirtyState m_state;
|
||||
LastSaveTimestamps m_last_save;
|
||||
|
||||
// keeps track of initial selected presets
|
||||
std::array<std::string, Preset::TYPE_COUNT> m_initial_presets;
|
||||
|
||||
public:
|
||||
bool is_dirty() const { return m_state.is_dirty(); }
|
||||
void update_from_undo_redo_stack(UpdateType type);
|
||||
public:
|
||||
void update_from_undo_redo_stack(bool dirty);
|
||||
void update_from_presets();
|
||||
void reset_after_save();
|
||||
void reset_initial_presets();
|
||||
|
||||
bool is_dirty() const { return m_plater_dirty || m_presets_dirty; }
|
||||
|
||||
#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(UpdateType type, const Slic3r::UndoRedo::Stack& stack);
|
||||
void update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack);
|
||||
// Does the Undo / Redo stack indicate the project is dirty?
|
||||
bool m_plater_dirty { false };
|
||||
// Do the presets indicate the project is dirty?
|
||||
bool m_presets_dirty { false };
|
||||
// Keeps track of preset names selected at the time of last project save.
|
||||
std::array<std::string, Preset::TYPE_COUNT> m_initial_presets;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_ProjectDirtyStateManager_hpp_
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue