diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/BeginInteractiveChange.md b/docs/api/XCEngine/Editor/Core/UndoManager/BeginInteractiveChange.md new file mode 100644 index 00000000..3414422a --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/BeginInteractiveChange.md @@ -0,0 +1,26 @@ +# UndoManager::BeginInteractiveChange + +开始一段可合并的交互式修改。 + +```cpp +void BeginInteractiveChange(const std::string& label) override; +``` + +## 参数 + +- `label` - 最终写入历史的命令标签。 + +## 行为说明 + +当前实现只有在没有 pending interactive change 时才会生效: + +- 保存 `label` +- 立刻调用 [CaptureCurrentState](CaptureCurrentState.md) 抓取 `before` 快照 + +如果已经有一段 pending 交互,这次调用会被直接忽略。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [HasPendingInteractiveChange](HasPendingInteractiveChange.md) +- [FinalizeInteractiveChange](FinalizeInteractiveChange.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/CanRedo.md b/docs/api/XCEngine/Editor/Core/UndoManager/CanRedo.md new file mode 100644 index 00000000..777ea4be --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/CanRedo.md @@ -0,0 +1,21 @@ +# UndoManager::CanRedo + +判断当前是否可以重做。 + +```cpp +bool CanRedo() const override; +``` + +## 返回值 + +- 当 `m_nextIndex < m_history.size()` 时返回 `true`。 + +## 当前实现说明 + +- 只要插入一条新命令并截断 redo 尾巴,之前可重做的历史就会全部失效。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [Redo](Redo.md) +- [PushCommand](PushCommand.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/CanUndo.md b/docs/api/XCEngine/Editor/Core/UndoManager/CanUndo.md new file mode 100644 index 00000000..a621e756 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/CanUndo.md @@ -0,0 +1,22 @@ +# UndoManager::CanUndo + +判断当前是否可以撤销。 + +```cpp +bool CanUndo() const override; +``` + +## 返回值 + +- 当 `m_nextIndex > 0` 时返回 `true`。 + +## 当前实现说明 + +- 只看历史游标,不会因为存在 pending interactive change 而直接返回 `true`。 +- 如果想把 pending 交互也并入历史,需要先 [FinalizeInteractiveChange](FinalizeInteractiveChange.md)。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [Undo](Undo.md) +- [GetUndoLabel](GetUndoLabel.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/CancelInteractiveChange.md b/docs/api/XCEngine/Editor/Core/UndoManager/CancelInteractiveChange.md new file mode 100644 index 00000000..c3710321 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/CancelInteractiveChange.md @@ -0,0 +1,24 @@ +# UndoManager::CancelInteractiveChange + +取消当前 pending 的交互式修改记录。 + +```cpp +void CancelInteractiveChange() override; +``` + +## 行为说明 + +当前实现只做一件事: + +- `m_pendingInteractiveChange.reset()` + +## 注意事项 + +- 它不会恢复场景或选择集到交互开始前的状态。 +- 它只意味着“不要把这段交互写进 undo 历史”。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [BeginInteractiveChange](BeginInteractiveChange.md) +- [FinalizeInteractiveChange](FinalizeInteractiveChange.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/CaptureCurrentState.md b/docs/api/XCEngine/Editor/Core/UndoManager/CaptureCurrentState.md new file mode 100644 index 00000000..9bb27aa7 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/CaptureCurrentState.md @@ -0,0 +1,27 @@ +# UndoManager::CaptureCurrentState + +抓取当前场景和选择集的 undo 快照。 + +```cpp +UndoStateSnapshot CaptureCurrentState() const override; +``` + +## 行为说明 + +当前实现会: + +1. 调用 `SceneManager::CaptureSceneSnapshot()` 保存场景快照。 +2. 如果当前没有 active scene,直接返回该快照。 +3. 遍历 `selectionManager.GetSelectedEntities()`。 +4. 只把仍能通过 `SceneManager::GetEntity(id)` 找到的实体 ID 写入 `selectionIds`。 + +## 当前实现含义 + +- 选择集快照会自动去掉无效实体,避免把悬空 ID 写进历史。 +- 这也是交互式 undo 在开始和结束阶段抓取 `before` / `after` 状态的基础接口。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [PushCommand](PushCommand.md) +- [BeginInteractiveChange](BeginInteractiveChange.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/ClearHistory.md b/docs/api/XCEngine/Editor/Core/UndoManager/ClearHistory.md new file mode 100644 index 00000000..534d1792 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/ClearHistory.md @@ -0,0 +1,26 @@ +# UndoManager::ClearHistory + +清空撤销/重做历史。 + +```cpp +void ClearHistory() override; +``` + +## 行为说明 + +当前实现会同时: + +- 清空 `m_history` +- 把 `m_nextIndex` 重置为 `0` +- 丢弃尚未结束的交互式修改 + +## 当前实现含义 + +- 调用后 `CanUndo()` 和 `CanRedo()` 都会变为 `false`。 +- 这不会修改当前场景内容,只清理历史状态。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [CanUndo](CanUndo.md) +- [CancelInteractiveChange](CancelInteractiveChange.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/Constructor.md b/docs/api/XCEngine/Editor/Core/UndoManager/Constructor.md new file mode 100644 index 00000000..c03aafa1 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/Constructor.md @@ -0,0 +1,21 @@ +# UndoManager::Constructor + +构造默认的撤销/重做管理器。 + +```cpp +UndoManager(SceneManager& sceneManager, ISelectionManager& selectionManager); +``` + +## 参数 + +- `sceneManager` - 用于抓取和恢复场景快照。 +- `selectionManager` - 用于读取和恢复当前选择集。 + +## 行为说明 + +当前构造函数只保存这两个依赖的引用,不会在构造时自动抓取初始快照,也不会预填任何历史记录。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [CaptureCurrentState](CaptureCurrentState.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/FinalizeInteractiveChange.md b/docs/api/XCEngine/Editor/Core/UndoManager/FinalizeInteractiveChange.md new file mode 100644 index 00000000..552d229f --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/FinalizeInteractiveChange.md @@ -0,0 +1,29 @@ +# UndoManager::FinalizeInteractiveChange + +结束当前交互式修改,并把它提交为一条历史记录。 + +```cpp +void FinalizeInteractiveChange() override; +``` + +## 行为说明 + +如果当前没有 pending interactive change,直接返回。 + +否则当前实现会: + +1. 取出 pending 的 `label` 和 `before` 快照。 +2. 再次调用 [CaptureCurrentState](CaptureCurrentState.md) 抓取当前 `after` 快照。 +3. 通过 [PushCommand](PushCommand.md) 写入历史。 +4. 清空 pending 状态。 + +## 当前实现含义 + +- 如果交互前后状态完全没变,`PushCommand()` 会把这次提交静默丢弃。 +- `Undo()` / `Redo()` 在发现有 pending 交互时,也会先走这里。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [PushCommand](PushCommand.md) +- [CancelInteractiveChange](CancelInteractiveChange.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/GetRedoLabel.md b/docs/api/XCEngine/Editor/Core/UndoManager/GetRedoLabel.md new file mode 100644 index 00000000..d2f5cfd4 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/GetRedoLabel.md @@ -0,0 +1,18 @@ +# UndoManager::GetRedoLabel + +获取下一次重做操作的标签。 + +```cpp +const std::string& GetRedoLabel() const override; +``` + +## 返回值 + +- 若当前可重做,返回 `m_history[m_nextIndex].label`。 +- 否则返回内部持有的空字符串引用 `m_emptyLabel`。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [CanRedo](CanRedo.md) +- [Redo](Redo.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/GetUndoLabel.md b/docs/api/XCEngine/Editor/Core/UndoManager/GetUndoLabel.md new file mode 100644 index 00000000..34ba2eb2 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/GetUndoLabel.md @@ -0,0 +1,23 @@ +# UndoManager::GetUndoLabel + +获取下一次撤销操作的标签。 + +```cpp +const std::string& GetUndoLabel() const override; +``` + +## 返回值 + +- 若当前可撤销,返回 `m_history[m_nextIndex - 1].label`。 +- 否则返回内部持有的空字符串引用 `m_emptyLabel`。 + +## 注意事项 + +- 返回的是稳定引用,不是临时字符串。 +- 标签只用于 UI 展示,不代表命令类型系统。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [CanUndo](CanUndo.md) +- [Undo](Undo.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/HasPendingInteractiveChange.md b/docs/api/XCEngine/Editor/Core/UndoManager/HasPendingInteractiveChange.md new file mode 100644 index 00000000..bb7cd67b --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/HasPendingInteractiveChange.md @@ -0,0 +1,17 @@ +# UndoManager::HasPendingInteractiveChange + +判断当前是否有尚未结束的交互式修改。 + +```cpp +bool HasPendingInteractiveChange() const override; +``` + +## 返回值 + +- 当 `m_pendingInteractiveChange.has_value()` 为真时返回 `true`。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [BeginInteractiveChange](BeginInteractiveChange.md) +- [FinalizeInteractiveChange](FinalizeInteractiveChange.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/PushCommand.md b/docs/api/XCEngine/Editor/Core/UndoManager/PushCommand.md new file mode 100644 index 00000000..001fbd03 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/PushCommand.md @@ -0,0 +1,34 @@ +# UndoManager::PushCommand + +向历史中压入一条新的 undo/redo 记录。 + +```cpp +void PushCommand(const std::string& label, UndoStateSnapshot before, UndoStateSnapshot after) override; +``` + +## 参数 + +- `label` - 命令显示标签。 +- `before` - 执行前快照。 +- `after` - 执行后快照。 + +## 行为说明 + +当前实现会: + +1. 用私有 `AreStatesEqual()` 比较 `before` 和 `after`,完全一致则直接丢弃。 +2. 如果 `m_nextIndex` 还在历史中间位置,先删除 redo 尾巴。 +3. 追加新的 `CommandEntry`。 +4. 若历史超过 `128` 条,则删除最旧一条,并同步调整游标。 +5. 最终把 `m_nextIndex` 设到历史末尾。 + +## 当前实现含义 + +- 任何新命令都会使旧的 redo 分支失效。 +- 历史上限是固定窗口,不是无限增长日志。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [Undo](Undo.md) +- [Redo](Redo.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/Redo.md b/docs/api/XCEngine/Editor/Core/UndoManager/Redo.md new file mode 100644 index 00000000..7863e333 --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/Redo.md @@ -0,0 +1,22 @@ +# UndoManager::Redo + +重做当前历史游标指向的下一条命令。 + +```cpp +void Redo() override; +``` + +## 行为说明 + +当前实现会按顺序执行: + +1. 如果存在 pending interactive change,先调用 [FinalizeInteractiveChange](FinalizeInteractiveChange.md)。 +2. 如果 [CanRedo](CanRedo.md) 为假,直接返回。 +3. 应用 `m_history[m_nextIndex].after` 快照。 +4. 把 `m_nextIndex` 向后移动一格。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [Undo](Undo.md) +- [CanRedo](CanRedo.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/Undo.md b/docs/api/XCEngine/Editor/Core/UndoManager/Undo.md new file mode 100644 index 00000000..c778073f --- /dev/null +++ b/docs/api/XCEngine/Editor/Core/UndoManager/Undo.md @@ -0,0 +1,27 @@ +# UndoManager::Undo + +回退到上一条历史记录之前的状态。 + +```cpp +void Undo() override; +``` + +## 行为说明 + +当前实现会按顺序执行: + +1. 如果存在 pending interactive change,先调用 [FinalizeInteractiveChange](FinalizeInteractiveChange.md)。 +2. 如果 [CanUndo](CanUndo.md) 为假,直接返回。 +3. 把 `m_nextIndex` 向前移动一格。 +4. 对新的当前位置应用 `before` 快照。 + +## 当前实现含义 + +- `Undo()` 会自动把尚未提交的交互式修改压成一条历史,再执行撤销。 +- 选择集恢复会经过实体有效性过滤,已不存在的实体不会重新选中。 + +## 相关文档 + +- [UndoManager](UndoManager.md) +- [Redo](Redo.md) +- [FinalizeInteractiveChange](FinalizeInteractiveChange.md) diff --git a/docs/api/XCEngine/Editor/Core/UndoManager/UndoManager.md b/docs/api/XCEngine/Editor/Core/UndoManager/UndoManager.md index a366d5cd..71f5db74 100644 --- a/docs/api/XCEngine/Editor/Core/UndoManager/UndoManager.md +++ b/docs/api/XCEngine/Editor/Core/UndoManager/UndoManager.md @@ -6,34 +6,102 @@ **源文件**: `editor/src/Core/UndoManager.h` -**描述**: 基于场景快照和选择快照实现编辑器撤销/重做历史,并支持交互式修改的合并提交。 +**描述**: 基于场景快照和选择快照维护编辑器撤销/重做历史,并把 gizmo 拖拽、属性编辑这类交互式修改合并成单条 undo 命令。 -## 概述 +## 角色概述 -`UndoManager` 是当前 [IUndoManager](../IUndoManager/IUndoManager.md) 的默认实现。 +`UndoManager` 是当前 [IUndoManager](../IUndoManager/IUndoManager.md) 的默认实现。它不是细粒度命令对象系统,而是“场景快照 + 选择快照”的历史管理器。 -它内部维护: +核心依赖有两个: -- 有标签的命令历史 `m_history` -- 当前历史游标 `m_nextIndex` -- 一个可选的交互式修改暂存 `m_pendingInteractiveChange` +- [SceneManager](../../Managers/SceneManager/SceneManager.md):负责抓取和恢复 `SceneSnapshot`。 +- `ISelectionManager`:负责恢复选择集。 -## 当前实现说明 +因此它真正管理的不是单个字段改动,而是整个编辑器状态快照: -- 历史上限当前固定为 `128`。 -- `Undo()` 会先 finalize 未结束的交互修改,再回退到 `before` 快照。 -- `Redo()` 同理会应用 `after` 快照。 -- `CaptureCurrentState()` 会抓取当前场景快照,并仅保留仍然有效的选中实体 ID。 -- `PushCommand()` 会在状态没变化时直接丢弃该记录。 +- 场景文档内容 +- 场景路径和 dirty 标记 +- 当前仍然有效的选中实体 ID 集合 + +## 当前状态模型 + +内部主要维护三组状态: + +| 状态 | 类型 | 作用 | +|------|------|------| +| `m_history` | `std::vector` | 按顺序保存 undo/redo 历史。每项同时持有 `before` 和 `after` 快照。 | +| `m_nextIndex` | `size_t` | 指向“下一次 redo 应用的历史项”。也可理解为当前历史游标。 | +| `m_pendingInteractiveChange` | `std::optional` | 用于把拖拽或连续输入期间的多次变化合并成一条命令。 | + +当前历史上限固定为 `128` 条;超出后会丢弃最旧记录。 + +## 历史行为 + +### `PushCommand()` + +[PushCommand](PushCommand.md) 会把一条命令写入历史,并自动处理两件事: + +- 如果当前在历史中间位置插入新命令,会先丢弃 redo 尾巴。 +- 如果 `before` 和 `after` 快照完全相同,则直接忽略,不写空命令。 + +### `Undo()` / `Redo()` + +[Undo](Undo.md) 与 [Redo](Redo.md) 都会先检查是否有尚未结束的交互式修改;如果有,会先 [FinalizeInteractiveChange](FinalizeInteractiveChange.md)。 + +之后: + +- `Undo()` 回退到当前命令的 `before` 快照 +- `Redo()` 应用当前命令的 `after` 快照 + +实际恢复由私有 `ApplyState()` 完成,它会先恢复场景,再把选择集过滤到仍然存在的实体上。 + +### 交互式修改合并 + +以下 API 共同组成“开始拖拽 -> 持续修改 -> 结束提交”的交互式 undo 流程: + +- [BeginInteractiveChange](BeginInteractiveChange.md) +- [HasPendingInteractiveChange](HasPendingInteractiveChange.md) +- [FinalizeInteractiveChange](FinalizeInteractiveChange.md) +- [CancelInteractiveChange](CancelInteractiveChange.md) + +这套模式当前被 viewport gizmo、PropertyGrid 和部分 Inspector 编辑器调用,用来把一整次拖拽合并成单条历史记录,而不是每帧都生成一条 undo。 + +## 快照内容 + +[CaptureCurrentState](CaptureCurrentState.md) 会生成 [UndoStateSnapshot](../IUndoManager/IUndoManager.md): + +- `scene` 来自 `SceneManager::CaptureSceneSnapshot()` +- `selectionIds` 只保留当前仍能通过 `SceneManager::GetEntity()` 找到的实体 + +如果当前没有 active scene,快照仍会返回,但其中 `scene.hasScene` 为假,选择集也不会继续扩展。 ## 当前实现边界 -- 这是快照式 undo,不是细粒度命令式 undo。 -- 历史存储大小和场景序列化数据量直接相关。 -- `ApplyState()` 当前依赖 [SceneManager](../../Managers/SceneManager/SceneManager.md) 恢复场景,然后再恢复选择。 +- 当前是快照式 undo,不适合超大场景的高频细粒度编辑。 +- `CancelInteractiveChange()` 只丢弃 pending 记录,不会把场景回滚到交互开始前。 +- `Undo()` / `Redo()` 调用了 `ApplyState()` 但没有处理其 `false` 返回值;当前行为更偏“尽力恢复”。 +- 历史标签只是 UI 文本,不携带额外命令元数据。 + +## 相关方法 + +- [Constructor](Constructor.md) +- [ClearHistory](ClearHistory.md) +- [CanUndo](CanUndo.md) +- [CanRedo](CanRedo.md) +- [GetUndoLabel](GetUndoLabel.md) +- [GetRedoLabel](GetRedoLabel.md) +- [Undo](Undo.md) +- [Redo](Redo.md) +- [CaptureCurrentState](CaptureCurrentState.md) +- [PushCommand](PushCommand.md) +- [BeginInteractiveChange](BeginInteractiveChange.md) +- [HasPendingInteractiveChange](HasPendingInteractiveChange.md) +- [FinalizeInteractiveChange](FinalizeInteractiveChange.md) +- [CancelInteractiveChange](CancelInteractiveChange.md) ## 相关文档 - [Core](../Core.md) - [IUndoManager](../IUndoManager/IUndoManager.md) +- [SceneSnapshot](../SceneSnapshot/SceneSnapshot.md) - [SceneManager](../../Managers/SceneManager/SceneManager.md)