#include "Core/UndoManager.h" #include "Core/ISelectionManager.h" #include "Managers/SceneManager.h" #include namespace XCEngine { namespace Editor { namespace { constexpr size_t kMaxUndoHistory = 128; } // namespace UndoManager::UndoManager(SceneManager& sceneManager, ISelectionManager& selectionManager) : m_sceneManager(sceneManager) , m_selectionManager(selectionManager) {} void UndoManager::ClearHistory() { m_history.clear(); m_nextIndex = 0; m_pendingInteractiveChange.reset(); } bool UndoManager::CanUndo() const { return m_nextIndex > 0; } bool UndoManager::CanRedo() const { return m_nextIndex < m_history.size(); } const std::string& UndoManager::GetUndoLabel() const { return CanUndo() ? m_history[m_nextIndex - 1].label : m_emptyLabel; } const std::string& UndoManager::GetRedoLabel() const { return CanRedo() ? m_history[m_nextIndex].label : m_emptyLabel; } void UndoManager::Undo() { if (HasPendingInteractiveChange()) { FinalizeInteractiveChange(); } if (!CanUndo()) { return; } --m_nextIndex; ApplyState(m_history[m_nextIndex].before); } void UndoManager::Redo() { if (HasPendingInteractiveChange()) { FinalizeInteractiveChange(); } if (!CanRedo()) { return; } ApplyState(m_history[m_nextIndex].after); ++m_nextIndex; } UndoStateSnapshot UndoManager::CaptureCurrentState() const { UndoStateSnapshot snapshot; snapshot.scene = m_sceneManager.CaptureSceneSnapshot(); if (!snapshot.scene.hasScene) { return snapshot; } for (uint64_t entityId : m_selectionManager.GetSelectedEntities()) { if (m_sceneManager.GetEntity(entityId)) { snapshot.selectionIds.push_back(entityId); } } return snapshot; } void UndoManager::PushCommand(const std::string& label, UndoStateSnapshot before, UndoStateSnapshot after) { if (AreStatesEqual(before, after)) { return; } if (m_nextIndex < m_history.size()) { m_history.erase(m_history.begin() + static_cast(m_nextIndex), m_history.end()); } m_history.push_back(CommandEntry{ label, std::move(before), std::move(after) }); if (m_history.size() > kMaxUndoHistory) { m_history.erase(m_history.begin()); if (m_nextIndex > 0) { --m_nextIndex; } } m_nextIndex = m_history.size(); } void UndoManager::BeginInteractiveChange(const std::string& label) { if (m_pendingInteractiveChange.has_value()) { return; } m_pendingInteractiveChange = PendingInteractiveChange{ label, CaptureCurrentState() }; } bool UndoManager::HasPendingInteractiveChange() const { return m_pendingInteractiveChange.has_value(); } void UndoManager::FinalizeInteractiveChange() { if (!m_pendingInteractiveChange.has_value()) { return; } PushCommand( m_pendingInteractiveChange->label, std::move(m_pendingInteractiveChange->before), CaptureCurrentState()); m_pendingInteractiveChange.reset(); } void UndoManager::CancelInteractiveChange() { m_pendingInteractiveChange.reset(); } bool UndoManager::ApplyState(const UndoStateSnapshot& state) { if (!m_sceneManager.RestoreSceneSnapshot(state.scene)) { return false; } std::vector validSelection; validSelection.reserve(state.selectionIds.size()); for (uint64_t entityId : state.selectionIds) { if (m_sceneManager.GetEntity(entityId)) { validSelection.push_back(entityId); } } if (validSelection.empty()) { m_selectionManager.ClearSelection(); } else { m_selectionManager.SetSelectedEntities(validSelection); } return true; } bool UndoManager::AreStatesEqual(const UndoStateSnapshot& lhs, const UndoStateSnapshot& rhs) { return lhs.scene.hasScene == rhs.scene.hasScene && lhs.scene.scenePath == rhs.scene.scenePath && lhs.scene.sceneData == rhs.scene.sceneData && lhs.scene.dirty == rhs.scene.dirty && lhs.selectionIds == rhs.selectionIds; } } // namespace Editor } // namespace XCEngine