Files
XCEngine/editor/src/Core/UndoManager.cpp
ssdfasd 5c3566774b docs: 更新 containers 和 threading 模块文档
- containers: 更新 string 类的多个方法文档
- threading: 更新 mutex 和 task-group 方法文档
2026-03-26 01:59:14 +08:00

166 lines
4.1 KiB
C++

#include "Core/UndoManager.h"
#include "Core/ISelectionManager.h"
#include "Managers/SceneManager.h"
#include <algorithm>
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<std::ptrdiff_t>(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<uint64_t> 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