繁体   English   中英

需要帮助为文本编辑器程序编写撤消/重做功能

[英]Need Help writing an undo/redo function for text editor program

我已经用C ++编写了一个文本编辑器程序,该程序具有简单的命令:LEFT,RIGHT,HOME,END,BACKSPACE,DELETE,INSERT,现在我需要执行UNDO和REDO函数。 在我的程序中,用户只能撤消最近十个命令。 我想使用向量实现来完成此操作,但是我不知道如何设置它。 我不确定如何将光标位置和字符存储到向量中。 有人可以提供帮助吗?

#ifndef CURSOR_H

#define CURSOR_H



#include <stdlib.h>
#include <iostream>

template <class Object>
class Cursor;
// Incomplete Declaration

template <class Object>
class CNode
{
        public:

                CNode( const Object & theElement = Object( ), CNode * n = NULL ) : element( theElement ), next( n ) { }
                Object  element;
                CNode *next;
                friend class Cursor<Object>;
};

template <class Object>
class Cursor
{
 public:
  Cursor( );
  bool isEmpty( ) const;
  void makeEmpty( );
  void left ( );
  void right ( );
  void del ( ); //This is the delete operation. I named it del instead of delete as delete conflicts with a C++ keyword.
  void back ( );
  void insert( const Object & x );
  void home ( );
  void end ( );
  void undo ( );


 private:

  void printText ( ) ;

  CNode<Object> *header;
  CNode<Object> *cursorPosition;

};
//#include "Cursor.cpp"
#endif

您想要使用deque以便可以从正面或背面添加和删除; 添加命令时,将其添加到背面;撤消操作时,将其从后面移除;当到达11条命令时,将其从前面移除。

看看Memento设计模式以及GOF

存在这一非常具体的要求。 您可能需要将其与其他设计模式结合使用(例如,Command,Iterator,FlyWeight等)

记忆意图

在不违反封装的情况下,捕获并外部化对象的内部状态,以便以后可以将对象恢复到此状态。

命令意图

将请求封装为对象,从而使您可以将具有不同请求,队列或日志请求的客户端参数化,并支持可撤消的操作。

要考虑的其他一些事项:

通常,您不想对光标移动应用撤消/重做操作(即,它们对十个命令的限制没有影响)。 撤消/重做删除或插入文本时,当然必须在执行操作之前将光标放在适当的位置。 如果用户在不执行任何光标移动或校正(退格键)的情况下键入许多字符,则在应用撤消/重做操作时,通常将这些字符视为一个单位。

恭喜您包含撤消/重做。 在任何类型的编辑器中,它都是一个很棒的功能。 它仍然会变得棘手。 这是给您的一些想法(全部挥手,无需代码)。

我建议学习命令设计模式 您要设计一个“ Command”类,该类的实例可以“执行”单个命令(例如插入字母“ A”)以及“撤消”本身。

当用户调用某个命令(例如添加字母“ A”)时,您“新建”一个命令,定义其“执行”以插入“ A”,还定义其“撤消”以删除A,然后将其添加到顶部撤消列表,然后“执行”它。

不要将撤消限制为仅10个。为什么不使其无限进行?

无论使用哪种结构来列出可撤消的命令,通常的行为是,如果已撤消到某个级别,然后在那一点开始编辑,则应放弃当前级别之上的所有重做。

对于您希望能够撤消的每个操作(例如,插入,退格键和del,但不包括光标移动),我们可以列出“撤消”过程:

  • 插入->将光标定位在该字符上并发出一个del
  • del->将光标置于以下字符处,然后插入
  • 退格键->将光标置于以下字符处并插入

不幸的是,您的游标使用了指针,并且在撤消删除/退格时,新分配的CNode可能与以前的地址不同,这可能会使尝试使用该指针地址的撤消步骤无效。 选项包括:

  • 一些其他数据结构,以跟踪以这种方式使指针无效的指针,如果重新创建了相应的元素,则用新值重新填充它们(痛苦)

  • 在CNode列表中找到更确定的方法来找到正确的索引

    • 文档中的绝对索引(但是可能很难计算并且移动缓慢)
    • 将光标移动保存在撤消历史记录中
      • 将10个实质性的编辑内容(del,退格,插入)与任意数量的散布光标移动混合在一起,需要一个动态调整大小的容器,并且您会携带很多“冗长的行李”
      • 您可以在每个实质性编辑的固定大小的列表中,将光标移动的动态列表挂在每个元素的旁边(尽管效果不佳)

(按原样,您的Cnode列表不会显示为双向链接,因此我看不到如何在没有非常痛苦的重复的情况下通过标题元素中的“文档”向左移动...?)

在解决了此索引编制/光标移动问题之后,您必须在以下各项之间做出决定:

  • 每次操作后,请使用双端队列保存撤消信息:

    struct History {指示仅对插入操作要撤消的操作(例如,枚举Op {左,右,插入,删除...)):对象值}

    然后具有一些on-undo处理功能,该功能可以读取这些History记录并协调它们描述的操作,或者

  • 当执行一个操作时,将一个功能对象推入您的双端队列,该对象对撤消和重做操作进行编码(根据对Cursor对象方法的调用),因此实际上执行撤消或重做操作仅涉及执行该对象(即撤消/ redo操作是在编辑时打包的“黑匣子”)/这更优雅,更灵活,但对于初学者/中级程序员来说可能不太熟悉,因此可能很难正确使用。 Boost库对此具有良好的支持功能。

我同意其他人在执行“ do”命令时捕获不可撤销的命令的观点。

我还建议您定期处理列表并结合撤消命令。

例如,如果您的撤消命令是:

删除A,删除B,删除C,左光标,左光标,右光标,左光标。

将其转换为

删除“ ABC”,左光标(2)。

这样,当用户执行撤消操作时,他们不会看到每一个击键。 相反,撤消发生在逻辑组中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM