エディタ等で使用するUndo/Redo機能を実現するQtのUndo Frameworkを調べてみました。
Table of Contents
1 Undo/Redo機能
直前の操作を取り消すことをUndo、Undoした後に同じ操作を実行し直すこと をRedoと言います。Windowsではctrl-zでUndo、ctrl-tでRedoのショートカッ トキーになっていることが多いです。Undo/Redo機能を実現する場合、 Commandパターンを利用する場合が多いようです。
1.1 独自にUndo/Redo機能を実装する場合
- 基底クラスとなるCommandを定義し、基底クラスのオブジェクトを保持す るリストを確保します。基底クラスではUndo/Redo実行時に呼び出すメソッ ド群を定義します。
- Undo/Redoが必要な操作を実行する際に、リストへCommandを追加します。 その際、基底クラスを継承し、基底クラスで定義したメソッド群をオーバ ライドしたクラスをリストへ追加します。
- Undo/Redoを実行する際に、リストからCommandを取得し、基底クラスで定 義されたメソッド群を呼び出します。取得したCommandのオブジェクトは 基底クラスをオーバライドしたクラスである為、継承したクラスでオーバ ライドしたメソッドが呼ばれます。
- Undo/Redoを実行した場合、どこまでUndo/Redoを実行したかを記憶する必 要があります。例えば、リストのサイズが10でUndoを2回実行した場合、7 というインデックス値(先頭のインデックス値を0とした場合)を保持し ておくことで、次にUndo/Redoを実行した場合のCommandがどれかが分かる ようになります。また、その際にUndo/Redoを実行しなかった場合、イン デックス値以降のCommandを削除し、新たに実行したCommandをリストに追 加していくことになります。
以上の通り、Commandパターンとなる基底クラスの定義、各種操作を実現す る基底クラスを継承したクラスの定義、基底クラスのリスト操作を実装する 必要があります。
2 QtのUndo Framework
QtはUndo/Redoを実現するフレームワークを持ちます。フレームワークには Commandパターンの基底クラスとなるQUndoCommandと基底クラスのリストと なるQUndoStackが含まれます。
2.1 QUndoCommand Class Reference
Commandパターンの基底クラスです。ユーザはQUndoCommandを継承したクラ スを定義することで各種操作を実現します。ユーザは以下を実装する必要が あります。
- redo()メソッドとundo()メソッドを継承したクラスでオーバライド。
- 復元する情報は各種クラスで独自に定義。
情報の保存に必要な情報はコンストラクタで設定します。例えばエディタの 状態を保持するオブジェクトのアドレスをコンストラクタで設定しておけば、 そのオブジェクトをundo()メソッドとredo()メソッドで使用することができ ます。
2.2 QUndoStack Class Reference
QUndoCommandを保持するリストです。本クラスが各種コマンドの履歴となり ます。ユーザは以下を実装する必要があります。
- QUndoStackの領域確保。
- createUndoAction でRedoを実行するトリガとなるQActionを取得。
- createRedoAction でUndoを実行するトリガとなるQActionを取得。
- Undo/Redoが必要な時点でQUndoStackへnewしたQCommandオブジェクトを push(不要になった時点でQUndoStackがdeleteしてくれる)。
取得したQActionをQMenuBarにセットすることで、メニューを選択した際に Undo/Redoが実行されるようになります。また、QActionのsetShortcuts()メ ソッドを使用することで、簡単にショートカットキーと結びつけることがで きます。
2.3 QtのUndo Frameworkのメリット
QtのUndo Frameworkを使用するメリットは以下です。
- QUndoStackの操作はpushだけで良い(どこまでUndo/Redoしたかのインデッ クス操作が不要)。
- newしたQUndoCommandはQUndoStack側でdeleteしてくれる。
- Redo/Undo用のQActionを取得できるので、メニュー操作とショートカット キーへの結びつけが簡単。
以上により、ユーザは各種コマンドを実現するQUndoCommandを継承したクラ スを定義すれば良いことになります。
2.4 サンプル
Qtをインストールしたディレクトリ配下にサンプルがあります。
- /<path-to>/Qt/Examples/Qt-5.3/widgets/tools/undoframework
- Undo Framework Example
3 その他のUndo/Redo用フレームワーク
GUIを実現するライブラリの多くはUndo/Redo用のフレームワークを持ちます。 独自に定義する前にライブラリにUndo/Redo用のフレームワークがあるかど うかを確認すると良いでしょう。
3.1 Java
javax.swing.undoを用いたUndo/Redo実装