问题描述
给我一些想法,如何实现撤消/重做功能 - 就像我们在文本编辑中一样. 我应该使用哪些算法以及可以阅读的内容.谢谢.
推荐答案
我知道撤消类型的两个主要部门
- 保存状态: 撤消的一类是您实际保存历史状态的地方.在这种情况下,发生的事情是,您一直在某个内存位置保存状态.当您想进行撤消时,您只需交换当前状态并在内存中已经存在的状态交换即可.这就是在Adobe Photoshop中使用历史记录或在Google Chrome中重新打开封闭选项卡的历史.
- 生成状态: 另一个类别是,您只能记住这些行动是什么,而不是维持国家本身.当您需要撤消时,您需要对该特定动作进行逻辑上的反面.对于一个简单的示例,当您执行 ctrl + b 在支持撤消的一些文本编辑器中,它被记住为 bold conike.现在,每个动作都是其逻辑逆转的映射.因此,当您进行 ctrl + z 时,它会从一个反动作表中抬起来,发现撤消动作是 ctrl +< b 再次.这是执行的,您将获得以前的状态.因此,这里您以前的状态没有存储在内存中,而是在您需要时生成的.
对于文本编辑器而言,以这种方式生成状态并不是太密集的,而是对于Adobe Photoshop等程序,它可能在计算很密集或简直是不可能的.例如 - 对于 Blur 操作,您将指定 de -blur 动作,但由于数据已经丢失,因此永远无法使您达到原始状态.因此,根据情况 - 逻辑逆操作及其可行性的可能性,您需要在这两个广泛的类别之间进行选择,然后按照所需的方式实现它们.当然,有可能制定适合您的混合策略.
也有时,就像在Gmail一样,可能有限的撤消是可能的,因为首先从未执行操作(发送邮件).因此,您不是在那里"撤消",而只是"不做"动作本身.
其他推荐答案
我从头开始编写了两个文本编辑器,他们都采用了非常原始的撤消/重做功能的形式.通过"原始",我的意思是该功能非常易于实现,但是在很大的文件中它是不经济的(例如>> 10 MB).但是,系统非常灵活.例如,它支持无限级别的撤消.
基本上,我定义了
之类的结构type TUndoDataItem = record text: /array of/ string; selBegin: integer; selEnd: integer; scrollPos: TPoint; end;
然后定义数组
var UndoData: array of TUndoDataItem;
然后,此数组的每个成员指定文本的保存状态.现在,在文本的每次编辑中(字符键向下,向下删除键,剪切/粘贴,由鼠标移动等),i(re)启动了(例如)一秒钟的计时器.触发时,计时器将当前状态保存为UndoData array的新成员.
在Undo(Ctrl+Z)上,我将编辑器还原到状态UndoData[UndoLevel - 1],然后将UndoLevel降低一个.默认情况下,UndoLevel等于UndoData数组的最后一个成员的索引.在重做(Ctrl+Y或Shift+Ctrl+Z)上,我将编辑器还原到状态UndoData[UndoLevel + 1],然后将UndoLevel增加一个.当然,如果当UndoLevel不等于UndoData数组的长度(减一个)时,触发了编辑计时器,我将在UndoLevel之后清除此数组的所有项目,就像Microsoft Windows Platform上的常见一样(但是,如果我没记错的话,emacs更好 - Microsoft Windows方法的缺点是,如果您撤消了很多更改,然后意外编辑缓冲区,则先前的内容(已解开)将永久丢失).您可能想跳过阵列的减少.
例如,在不同类型的程序中,可以应用相同的技术,但是,当然,具有完全不同的UndoDataItem结构.一种不需要太多内存的更高级的方法是仅保存撤消级别之间的更改(也就是说,而不是保存" alpha \ nbeta \ gamma"和" alpha \ nbeta \" ngamma \ ndelta",您可以保存" alpha \ nbeta \ ngamma"和" add \ ndelta",如果您明白了我的意思).在每个更改与文件大小相比很小的大文件中,这将大大降低撤消数据的内存使用情况,但是实现更棘手,并且可能更容易出错.
其他推荐答案
有几种方法可以做到这一点,但是您可以开始查看命令模式.使用命令列表以通过您的操作向后移动(撤消)或向前(重做).可以找到C#中的一个示例在这里..
问题描述
Give me some thoughts how to implement undo/redo functionality - like we have in text editors. What algorithms should I use and what I may read. thanks.
推荐答案
I know about two major divisions of the types of undo's
- SAVE STATE: One category of undo is where you actually save history states. In this case what happens is that at every point you keep on saving the state in some location of memory. When you want to do an undo, you just swap out the current state and swap in the state which was already there in the memory. This is how it is done with History in Adobe Photoshop or reopening closed tabs in Google Chrome, for example.
- GENERATE STATE: The other category is where instead of maintaining the states themselves, you just remember what the actions were. when you need to undo, you need to do a logical reverse of that particular action. For a simple example, when you do a Ctrl+B in some text editor that supports undo's, it is remembered as a Bold action. Now with each action is a mapping of its logical reverses. So, when you do a Ctrl+Z, it looks up from a inverse actions table and finds that the undo action is a Ctrl+B again. That is performed and you get your previous state. So, here your previous state was not stored in memory but generated when you needed it.
For text editors, generating the state this way is not too computation intensive but for programs like Adobe Photoshop, it might be too computationally intensive or just plain impossible. For example - for a Blur action, you will specify a de-Blur action, but that can never get you to the original state because the data is already lost. So, depending on the situation - possibility of a logical inverse action and the feasibility of it, you need to choose between these two broad categories, and then implement them the way you want. Ofcourse, it is possible to have a hybrid strategy that works for you.
Also, sometimes, like in Gmail, a time limited undo is possible because the action (sending the mail) is never done in the first place. So, you are not "undo"ing there, you are just "not doing" the action itself.
其他推荐答案
I have written two text editors from scratch, and they both employ a very primitive form of undo/redo functionality. By "primitive", I mean that the functionality was very easy to implement, but that it is uneconomical in very large files (say >> 10 MB). However, the system is very flexible; for instance, it supports unlimited levels of undo.
Basically, I define a structure like
type TUndoDataItem = record text: /array of/ string; selBegin: integer; selEnd: integer; scrollPos: TPoint; end;
and then define an array
var UndoData: array of TUndoDataItem;
Then each member of this array specifies a saved state of the text. Now, on each edit of the text (character key down, backspace down, delete key down, cut/paste, selection moved by mouse etc.), I (re)start a timer of (say) one second. When triggered, the timer saves the current state as a new member of the UndoData array.
On undo (Ctrl+Z), I restore the editor to the state UndoData[UndoLevel - 1] and decrease the UndoLevel by one. By default, UndoLevel is equal to the index of the last member of the UndoData array. On redo (Ctrl+Y or Shift+Ctrl+Z), I restore the editor to the state UndoData[UndoLevel + 1] and increase the UndoLevel by one. Of course, if the edit timer is triggered when UndoLevel is not equal to the length (minus one) of the UndoData array, I clear all items of this array after UndoLevel, as is common on the Microsoft Windows platform (but Emacs is better, if I recall correctly -- the disadvantage of the Microsoft Windows approach is that, if you undo a lot of changes and then accidentally edit the buffer, the previous content (that was undid) is permanently lost). You might want to skip this reduction of the array.
In a different type of program, for instance, an image editor, the same technique can be applied, but, of course, with a completely different UndoDataItem structure. A more advanced approach, which does not require as much memory, is to save only the changes between the undo levels (that is, instead of saving "alpha\nbeta\gamma" and "alpha\nbeta\ngamma\ndelta", you could save "alpha\nbeta\ngamma" and "ADD \ndelta", if you see what I mean). In very large files where each change is small compared to the file size, this will greatly decrease the memory usage of the undo data, but it is trickier to implement, and possibly more error-prone.
其他推荐答案
There are several ways to do this, but you could start looking at the Command pattern. Use a list of commands to move back (Undo) or forward (redo) through your actions. An example in C# can be found here.