ValueTreeでUndoManagerを使う
アプリケーションに取り消し/やり直しアクションを実装します。で以前の中間状態を簡単に復元できます。UndoableActionオブジェクトを作成し、取り消し可能なアクションをトランザクションにグループ化する方法を学ぶ。
レベル:中級
プラットフォーム:Windows, macOS, Linux
クラス: UndoManager,UndoableAction,ValueTree,TreeView,TreeViewItem
スタート
このチュートリアルではValueTreeで説明されているようにTutorial: The ValueTree class.もしまだなら、まずそのチュートリアルを読むべきだ。
このチュートリアルのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。
このステップにヘルプが必要な場合は、以下を参照してください。Tutorial: Projucer Part 1: Getting started with the Projucer.
デモ・プロジェクト
このデモ・プロジェクトはUndoManagerクラスはValueTree過去の歴史がいかに簡単に復元できるかを示すために。それはValueTreeデータをツリー構造として扱うにはTreeViewそしてTreeViewItemクラスを作成します。プロジェクトをビルドして実行すると、このように表示されるはずだ:

現時点では、ドラッグ&ドロップでValueTreeノードを作成し、データ構造の階層を変更できます。子ノードを展開したり折りたたんだりすることもできるが、変更を元に戻したりやり直したりすることはできない。この機能をUndoManagerクラスである。
ここで紹介するコードは、大まかに以下のものと似ている。バリューツリーデモJUCEデモより。
元に戻す/やり直しボタンの追加
まず、次の2つを加えてみよう。TextButtonオブジェクトをユーザー・インタフェースに追加し、アンドゥとリドゥの機能を使えるようにしました。このセクションではラムダ関数に慣れている必要があります。Tutorial: Listeners and Broadcastersチュートリアル
の中でMainContentComponent
クラスを宣言します。TextButton各ボタンの変数[1]:
juce::TreeView tree;
juce::TextButton undoButton, redoButton; // [1]
std::unique_ptr rootItem;
コンストラクタのメンバー初期化リストで、以下のテキストを設定する。TextButtonオブジェクト[2]:
MainContentComponent()
: undoButton ("Undo"),
redoButton ("Redo") // [2]
{
最後に、ボタンを表示させます。[3]に割り当てるラムダ関数を準備する。Button::onClickヘルパーオブジェクト[4]:
//...
addAndMakeVisible (undoButton);
addAndMakeVisible (redoButton); // [3]
undoButton.onClick = [this] { };
redoButton.onClick = [this] { }; // [4]
setSize (600, 400);
次に、ボタンの境界をresized()
メソッドを使用する:
void resized() override
{
// This is called when the MainContentComponent is resized.
// If you add any child components, this is where you should
// update their positions.
auto r = getLocalBounds();
auto buttons = r.removeFromBottom (20);
undoButton.setBounds (buttons.removeFromLeft (100));
redoButton.setBounds (buttons.removeFromLeft (100));
tree.setBounds (r);
}
UndoManagerインスタンスを引数として渡す
以来ValueTreeクラスはアンドゥ/リドゥの動作を自動的に処理する。UndoManagerインスタンスをパラメータとして 登録します。UndoableActionオブジェクトのインスタンスを宣言する。これを実装するには、まずUndoManagerクラス[1]:
juce::UndoManager undoManager; // [1]
次に、対応するアンドゥ/リドゥの動作を処理するために、ボタンがクリックされたときに呼び出される関数を代入する。ラムダ関数では、それぞれ[UndoManager::undo()](https://docs.juce.com/master/classUndoManager.html#a39f45c284e8d0df1a0d378e676246931 "Tries to roll-back the last transaction.")
そして[UndoManager::redo()](https://docs.juce.com/master/classUndoManager.html#aaea507a3b9eaea3360c0e393edf69ccb "Tries to redo the last transaction that was undone.")
以下の通りである:
addAndMakeVisible (undoButton);
addAndMakeVisible (redoButton); // [3]
undoButton.onClick = [this] { undoManager.undo(); };
redoButton.onClick = [this] { undoManager.redo(); }; // [4]
setSize (600, 400);
の中でValueTreeItem
クラスへの参照も保持する。UndoManagerインスタンス[2]:
private:
juce::ValueTree tree;
juce::UndoManager& undoManager; // [2]
クラス・コンストラクタのメン バー初期化リストにUndoManager参照[3]:
ValueTreeItem (const juce::ValueTree& v, juce::UndoManager& um)
: tree (v), undoManager (um) // [3]
{
ValueTreeItemのサブ項目が再帰的に作成されるときは、必ずUndoManagerインスタンス[4]:
void refreshSubItems()
{
clearSubItems();
for (auto i = 0; i < tree.getNumChildren(); ++i)
addSubItem (new ValueTreeItem (tree.getChild (i), undoManager)); // [4]
}
これで、ルートValueTreeItemをインスタンス化できます。UndoManagerインスタンス[5]でのMainContentComponent
クラスである:
tree.setDefaultOpenness (true);
tree.setMultiSelectEnabled (true);
rootItem.reset (new ValueTreeItem (createRootValueTree(), undoManager)); // [5]
tree.setRootItem (rootItem.get());
このように、3つの異なるメソッドがあり、それらを更新する必要がある。TreeView.
void itemDropped (const juce::DragAndDropTarget::SourceDetails&, int insertIndex) override
{
juce::OwnedArray selectedTrees;
getSelectedTreeViewItems (*getOwnerView(), selectedTrees);
moveItems (*getOwnerView(), selectedTrees, tree, insertIndex, undoManager); // [1]
}
static void moveItems (juce::TreeView& treeView, const juce::OwnedArray& items,
juce::ValueTree newParent, int insertIndex, juce::UndoManager& undoManager)
{
v.getParent().removeChild (v, &undoManager); // [2]
newParent.addChild (v, insertIndex, &undoManager); // [3]
- [1]アイテムがドラッグ&ドロップされるたびに、移動を処理する静的関数にundoManagerを渡します。
- [2]このアクションをundoManagerに登録する必要がある。
- [3]次に、その子を新しい親に追加し、新しいアクションをundoManagerに登録します。
を通過させる。undoManager
について言及する。ValueTree機能addChild()
そしてremoveChild()
とする。UndoManagerを実行する。UndoableActionを呼び出してください。perform()
関数について説明する。私たちは以下をカバーする。UndoableActionオブジェクトは今後のチュートリアルで紹介する。
に保存されたアンドゥとリドゥの説明を表示します。LabelコンポーネントをそれぞれのTextButtonオブジェクトを使用する。getUndoDescription()
そしてgetRedoDescription()
関数をそれぞれ使用する。
イベントをトランザクションとして扱う
のもう1つの便利な機能である。UndoManagerは、複数のアクションを1つのアンドゥ/リドゥ・トランザクションとしてグループ化できる機能である。この機能はbeginNewTransaction()
関数を使用する。undoManager
インスタンスへの呼び出しはすべてperform()
の機能である。