编辑器的模式(1)—undo/redo

"我不能预见每个人的未来,我只能预见我自己的,而且只能预见两分钟"——尼古拉斯.凯奇《惊魂下一秒》2007

无论人写字,画画一样,我们常常有笔误不可避免,
回到过去的某个修改点,做出不同的修改,并继续,

在程序设计的概念里,这常常指版本管理,版本管理保存了每一次(所有)修改的历史,不同时间线,还有合并

编辑器的模式(1)—undo/redo_第1张图片
图1:版本管理

而编辑器的,undo/redo, 则有几点简化,

  1. 只保留一部分修改记录——通常我们只关心近期的修改,
  2. undo/redo 为逆操作,但undo不销毁历史,而任何undo之后的修改,则会销毁redo序列
编辑器的模式(1)—undo/redo_第2张图片
图2:undo/redo历史线

这像是《惊魂下一秒》里的故事,修正有限历史,并让下一秒冲刷掉未来。

undo/redo模式,即为,维护一定长度的修改点队列,并在所有历史修改点里,进行版本切换.

以下我实现了一个简单的undo/redo,版本管理,

//版本数据库,为了实现备份,data须可clone  
public class DataBackup where Data : class, ICloneable, new()
{
    //版本队列
    List mDataBackup = new List() { new Data() };
    const int MAX_LEN = 15;//版本历史限制
    int dataIdx = 0;//版本号

    public Data data
    {
        get
        {
            return mDataBackup[dataIdx];
        }
    }

    private void trim()
    {
        mDataBackup.RemoveRange(dataIdx + 1, mDataBackup.Count - dataIdx - 1);
    }
    
    //备份
    public void backup()
    {
        trim();//消除所有未来版本
        mDataBackup.Add(data.Clone() as Data);
        if (dataIdx > MAX_LEN)
        {
            mDataBackup.RemoveAt(0);
        }
        dataIdx = mDataBackup.Count - 1;
    }

    //撤销
    public void undo()
    {
        dataIdx--;
        if (dataIdx < 0)
            dataIdx = 0;
    }

    //重做
    public void redo()
    {
        dataIdx++;
        if (dataIdx >= mDataBackup.Count)
            dataIdx = mDataBackup.Count - 1;
    }
}
编辑器的模式(1)—undo/redo_第3张图片
图三. 撤销/重做测试

可以看到,这样简单的undo/redo已经足够工作。

实现更精巧的 redo/undo功能,你需要考虑以下问题:

  • 当data特别大,每一个版本仅需要保存版本增量,因此需要实现,
    gain(data_v1, diff) == data_v2
    revert(data_v2, diff) == data_v1

  • MVC模式下,不一定是对于modeller的数据集, 每次备份能可以是controller 操作集 action list,因此control需要实现 一组可逆接口,例如,
    addEntity/removeEntiy
    changeDeltaPos(x, y) / changeDeltaPos(-x, -y)

而现实中,对于有些编辑器的实现来说,效率并不是一个严重的问题,简单则是更为重要的。

你可能感兴趣的:(编辑器的模式(1)—undo/redo)