前回はMaster(一覧)で選択したフィールドをDetails(詳細)で表示できるようにしました。今回は最初にパッケージとクラスの構造を整理します。そのあとで、各ボタンが動作するようにし、Details(詳細)で変更された内容が反映されるようにします。
パッケージ・クラスの構造の整理
今までは「とりあえず実装して動作させる」ことに重点を置いてきたので、FieldsPageクラスにすべて詰め込んだかなり見にくいソースコードになっています。そこで新しい機能を実装する前に、パッケージとクラスの構造を整理することにします。
パッケージの分割
まず、パッケージの構成を見直すことにします。現在は、com.piece_framework.piece_ide.form_designerにすべてのクラスが配置されています。これらのパッケージ構成を以下のように変更します(パッケージ名にはプリフィックスとしてcom.piece_framework.piece_ide.form_designerを付けます)。
パッケージ名 | 説明 | 所属するファイル |
plugin | プラグイン全体で使用するクラスを配置する | Activator.java |
model | モデルクラスを配置する | Field.java |
ui | ユーザーインタフェースに関係するクラスを配置する | FormDesignerEditor.java, FieldsPage.java |
Master(一覧)のクラス化
現在のフォームデザイナーのソースコードの中で一番複雑なのは、なんといってもFieldsPageクラスでしょう。そこで、まずMaster(一覧)の部分を別クラスに抽出することにします。
マニフェストエディターの「拡張」ページではMaster(一覧)をSectionPartクラスを継承したクラスで実装しているので、同じように継承したFieldsMasterSectionPartクラスを作成します。Compositeオブジェクトと ManagedFormオブジェクトを引数として受け取るコンストラクターを作成し、その中で初期化とウィジェットの生成・配置を行います。
FieldsMasterSectionPartクラスでウィジェットの生成・配置を行うcreateContents()メソッドはFieldsBlockクラスのcreateMasterPart()メソッドをほぼそのまま移動します。この移動に伴い、サンプルデータを生成するcreateSample()メソッドもFieldsMasterSectionPartクラスに移動します。
ここまで実装できたら、実行して動作に影響がないことを確認します。
Details(詳細)のクラス化
続いて、Details(詳細)を別クラスに抽出します。このクラスはFieldsBlockクラスのregisterPages()メソッドでIDetailsPageインタフェースの無名クラスとして実装している部分をFieldDetailsPageクラスとして抽出します。
ここまで実装できたら、実行して動作に影響がないことを確認します。
ボタンの実装
ソースコードはだいぶ整理できましたので、続いて各ボタンの実装を行います。ボタンにイベントを追加するにはaddSelectionListener ()メソッドを使用し、SelectionListenerインタフェースのインスタンスを追加します。このインタフェースにはwidgetSelected()メソッドとwidgetDefaultSelected()メソッドがあり、前者はウィジェットを選択したときに呼び出されるのに対して、後者はリストなどでダブルクリックされたときに呼び出されます。これはワンクリック時とダブルクリック時で異なる動作をさせたいときに有効です。今回はボタンですので、widgetSelected()メソッドのみを実装することにします。
[追加]ボタン
[追加]ボタンがクリックされたときの動作は、フィールド名を入力するダイアログを表示します。次にそのダイアログで入力された文字列をフィールド名としてFieldオブジェクトを生成、テーブルにその情報をセットして選択状態にします。
widgetSelected()メソッドの中でviewerとbuttonsを使用するので、これらをfinalにします。入力ダイアログは既存のInputDialogクラスを使用します。特筆すべきは生成したFieldオブジェクトをTableViewerオブジェクトにadd()メソッドで追加するだけで、テーブルに追加されているという点です。テーブルビューアーを導入したことでウィジェットとモデルの連携が簡単になっていることがわかります。
それでは実行してみましょう。[追加]ボタンをクリックするとダイアログが表示され、入力した文字列がフィールド名となってテーブルに追加されます。
[削除]ボタン
削除は選択されているFieldオブジェクトを取得し、それをTableViewerオブジェクトから削除することで実現できます。
それでは実行してみましょう。Master(一覧)のテーブルから削除したいフィールドを選択し、[削除]ボタンをクリックします。これで選択したフィールドが削除されます。
[上へ][下へ]ボタン
最後に[上へ]ボタン、[下へ]ボタンを実装します。フィールドの順序の入れ替えはTableViewerクラスのreplace()メソッドを使用して行います。最後に入れ替えたFieldオブジェクトを選択することで、連続して移動できるようにします。
それでは実行してみましょう。Master(一覧)のテーブルから移動したいフィールドを選択し、[上へ]ボタン、[下へ]ボタンをクリックすると、フィールドが上下に移動します。
Details(詳細)の変更の反映
Master(一覧)からDetails(詳細)への変更通知はManagedFormオブジェクトを介して行いました。同じくDetails(詳細)からMaster(一覧)への変更通知もManagedFormオブジェクトを介して行うことにします。
ManagedFormクラスのfireSelectionChanged()メソッドの実装を見るとわかるのですが、addPart()メソッドで追加されたフォームパートのうちIPartSelectionListenerインタフェースを実装しているものを順番に呼び出しています。そこでMaster(一覧)でも変更通知イベントを受け取れるように、FieldsMasterSectionPartクラスでIPartSelectionListenerインタフェースを実装します。
IPartSelectionListenerインタフェースはselectionChanged()メソッドを定義しているので、これを実装する必要があります。このメソッドではTableViewerクラスのrefresh()メソッドを使って、テーブルのデータを更新します。これに伴い、 TableViewerオブジェクトをfViewerとしてFieldsMasterSectionPartクラスのプロパティーに変更します。また、変更通知元が自分自身の場合には処理を行わないようにします。
次に変更を通知するDetails(詳細)の実装を行います。まず、どのタイミングで変更を通知するかですが、今回は各ウィジェットがフォーカスを失ったタイミングで通知することにします。またFieldsMasterSectionPartクラスの実装と同じく、通知元が自分自身の場合は処理を行わないようにします。
それでは実行してみましょう。フィールド名や概要を変更して、フォーカスを移すと即座にテーブルに反映されるのがわかります。また別のフィールドを選択して、再度変更したフィールドを選択しても変更した内容が保持されています。これはTableViewerオブジェクトがデータをFieldオブジェクトとして管理しているためです。このようにテーブル、Master(一覧)、Details(詳細)の間のデータのやりとりをFieldオブジェクトで行うことができるため、データとユーザインタフェースの間をやりとりするコードをほとんど実装する必要がありません。
おわりに
今回はテーブル、Master(一覧)、Details(詳細)といったユーザーインタフェースとFieldオブジェクトの連携部分を実装しました。従来のUIプログラミングでは煩雑であったUI部品とオブジェクトとの連携が、TableViewerクラスやManagedFormクラスを介して行うことで、スムーズに行われていることをご理解いただけたと思います。
次回はバリデータ定義フォームの実装を説明します。