#pragma once #include #include #include #include /** * @brief 保存有限数量的编辑前快照并提供撤销、重做 * * State 由具体编辑服务决定,可以是 HMI 页面集合或控制逻辑集合 * 历史只存在当前编辑会话内,不写入工程文件 * 新编辑会清空重做栈,超过容量时丢弃最早记录 */ template class EditorHistory final { public: // 单个编辑服务最多保留 30 条撤销和重做历史 static constexpr std::size_t kMaximumEntries = 30U; template /** * @brief 记录一次编辑前状态 * @param before 编辑前状态 * @param after 编辑完成后的当前状态 * @param equal 比较前后状态是否相同的可调用对象 * * 状态没有变化时不创建空历史 * 新编辑成功记录后清空重做栈 */ void record(State before, const State &after, Equal equal) { if (equal(before, after)) { return; } undo_states_.push_back(std::move(before)); trim(&undo_states_); redo_states_.clear(); } /** * @brief 撤销最近一次编辑 * @param current 当前状态 * @return 撤销后的目标状态;没有可撤销历史时返回空值 * * 当前状态会先进入重做栈,之后取出撤销栈顶的编辑前快照 */ std::optional undo(State current) { if (undo_states_.empty()) { return std::nullopt; } redo_states_.push_back(std::move(current)); trim(&redo_states_); State target = std::move(undo_states_.back()); undo_states_.pop_back(); return target; } /** * @brief 重做最近一次被撤销的编辑 * @param current 当前状态 * @return 重做后的目标状态;没有可重做历史时返回空值 * * 当前状态会先放回撤销栈,之后取出重做栈顶的目标状态 */ std::optional redo(State current) { if (redo_states_.empty()) { return std::nullopt; } undo_states_.push_back(std::move(current)); trim(&undo_states_); State target = std::move(redo_states_.back()); redo_states_.pop_back(); return target; } /** * @brief 清空撤销栈和重做栈 */ void clear() { undo_states_.clear(); redo_states_.clear(); } /** * @brief 判断是否存在可撤销历史 */ bool canUndo() const { return !undo_states_.empty(); } /** * @brief 判断是否存在可重做历史 */ bool canRedo() const { return !redo_states_.empty(); } private: // 保持历史不超过上限,超出时从最早的记录开始丢弃 static void trim(std::vector *states) { while (states->size() > kMaximumEntries) { states->erase(states->begin()); } } std::vector undo_states_; // 编辑前快照栈,末尾是下一次撤销要恢复的状态 std::vector redo_states_; // 撤销后状态栈,末尾是下一次重做要恢复的状态 };