软件中Undo(撤回)和Redo(重做)的实现
在一般软件中,都会有Undo和Redo的功能,那么这个功能该怎么实现呢?在此介绍一种实现方法。(以lua语言为例)
1. 操作事件化。将用户的操作转换成一个事件数据,里面包括事件类型、操作目标、目标原始状态、操作后状态等。比如:
-- 移动某结点可转换为 local moveEvent = {event = "Move", target = "selectNode", orginPos = cc.p(0, 0), endPos = cc.p(100, 100)} -- 新建结点可转换为 local newEvent = {event = "Delete", name = "bgSprite"}
2. 事件处理与存储。新建一个类EventManager,用于存储所有的操作事件。在用户操作时并不直接执行对应的处理,而是将操作命令发给EventManager进行统一的处理。EventManager大致像这样:
-- eventList:存储用户操作的事件列表 -- statusIndex: 当前软件的执行事件位置 -- saveIndex: 当前软件的保存事件位置 EventManager = {eventList = {}, statusIndex = 0}
-- 新加事件 function EventManager:doEvent(cmd) -- 执行真正的操作变化 UIController:doEvent(cmd) -- 将当前操作位置之后的抛弃,statusIndex在发生Redo后将不再是列表长度 for i=#self.eventList, (self.statusIndex + 1), -1 do table.remove(self.eventList, i) end -- 加入事件队列 table.insert(self.eventList, cmd) self.statusIndex = #self.eventList end
用户撤回(Undo)时,需要将上一个事件转换为撤回事件,如上文的moveEvent可转换为:
{event = "Move", target = "selectNode", orginPos = cc.p(100, 100), endPos = cc.p(0, 0)}
newEvent可转换为:
{event = "Delete", name = "bgSprite"}
具体undo方法大致如下:
-- 撤回操作 function EventManager:undo() if self.statusIndex > 0 then -- 获取撤回事件,依事件情况实现 local undoCmd = self:getUndoCmd(self.eventList[self.statusIndex]) self.statusIndex = self.statusIndex - 1 -- 执行真正的操作变化 UIController:doEvent(undoCmd) end end
redo方法就比较简单了,只需将存储的事件再次执行即可:
-- 重做 function EventManager:redo() if self.statusIndex < #self.eventList then self.statusIndex = self.statusIndex + 1 local redoCmd = self.eventList[self.statusIndex] -- 执行真正的操作变化 UIController:doEvent(redoCmd) end end
以上我们就实现了操作的Undo和Redo功能。基于此我们可以引出另外一个功能:在软件中标注当前的修改状态,如图和
。我们只需要在EventManager中添加一个变量saveIndex来标记当前的保存位置。当saveIndex==statusIndex时表示当前状态已保存,否则当前的修改未保存。需要在doEvent/undo/redo方法中刷新软件标题的显示状态。当用户保存时,则重置saveIndex的位置:
function EventManager:save() self.saveIndex = self.statusIndex -- 重置软件的标题 fc.SetWindowTitle("fcediter") end
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)