フォームの作成

懲りずにJavaFXのネタです。
JavaFXの可能性を探っているわけですが、JavaFXでは非常に扱いやすいGUIコンポーネントの記述が特徴です。しかし、実際に開発を行うとすればGUIがどれだけ記述しやすくとも、管理画面などがサクサク書けなければ意味がありません。JavaFXではちょっと工夫すれば簡単にカスタムコンポーネントを作ることができますし、LayoutManager的なものも簡単に作れます。
そこでこんなイメージのフォームを作れるように試行錯誤してみました。

動く画面とソースコードこちらから。

この画面のソースコード

まず、この画面を記述するに当たってユーザが記述しなければならないコードはこのようになります。

    Form {
        labelWidth: 150, fieldWidth: 350
        hgap: 5, vgap: 5
        font: Font {
            size: 14
        }
        model: FormModel {
            fields: [
                TextField {
                    name: "name"
                    label: "氏名"
                    width: 250
                }
                IntegerField {
                    name: "age"
                    label: "年齢"
                    width: 50
                }
                CheckBoxField {
                    name: "manager"
                    label: "マネージャー"
                }
                ComboBoxField {
                    name: "group"
                    label: "グループ"
                    choices: ["開発チーム", "サポートチーム"]
                }
            ]
        }
    }

Form はそれ自体がNodeなので、シーングラフの適当な所に配置できます。

中でどんな事が行われているかはともかく、見たままのイメージが画面に反映されているのが解るでしょうか?クラス名や構造に関してはDjangoのAdminFormを参考にしましたが、各フィールドのタイプと属性を宣言的に記述できます。これならば面倒なGUIビルダを起動しなくとも、簡単に管理画面が作成できるでしょう。さらに言えば、自動生成することも容易です。

舞台裏

舞台裏といっても、たいしたコードがあるわけではありません。このようにFormクラスと幾つかのクラスを使い、内部のシーングラフを動的に組み立てているだけです。

Form.fx

Formはそれ自体がNodeであり、CustomNodeを継承したクラスとなっています。このやり方のメリットはそれ自体をシーングラフの一部とすることが出来る事で、結果として宣言的に自然にフォームを記述できることです。手続き的にやるのであればForm自体がNodeでなくてもいいのですが、サンプルのような記述がしたいが為にCustomNodeを継承しています。

    override function create():Node {
        forms.content = for (field in model.fields) {
            [
                Rectangle {
                    width: this.labelWidth + this.fieldWidth
                }
                Label {
                    text: field.label
                    width: this.labelWidth - 2 * hgap
                    font: font
                    vpos: VPos.BOTTOM, hpos: HPos.RIGHT
                }// Label
                field.widget()
            ]
        }
        Group {
            content: [
                Rectangle {
                    width: labelWidth + fieldWidth + 10
                    height: bind forms.height + 10
                    arcWidth: 5, arcHeight: 5
                    fill: Color.WHITE
                } // Rectangle - Background
                Rectangle {
                    width: forms.width
                }
                this.forms
            ]
        }// Group
    }

Formでは背景となる矩形、各フィールドの背景・ラベル・ウィジェット(フォームの部品)などを構築しています。

class FormLayout extends Container {}

ここで使われているFormLayoutはContainerのサブクラスで、JavaFXでのLayoutManagerに相当するノードです。doLayoutで内包するノードのレイアウト位置を調整しています。

FormModel.fx

今のところなにも実装していません。なんとなく、Form(View)部分からロジック的な部分を抽出するかなーと考えているだけです。後ほど利ファクタリングされてなくなる可能性もあります。

Widget.fx

現在、JavaFXではコントロールが不足しています。例えば、ComboBoxが1.2ではいったん消えてしまいました。これは、モバイルなど様々な媒体で動くことを想定しているJavaFXなので、ディスクトップだけ動けばかまわないということにならないからだと思います。とはいえ、Swing拡張があるため、ディスクトップ限定であれば、Swingで動くものであれば原則として描画可能です(バグはあるようですが・・・)。
そんな事情より、一律にControlとできないため、リサイズ可能なノードとしてContainer をFieldのウィジェットとして使うことにしました。こちらもなんらかの拡張を考慮してWidgetとしています。

public class Widget extends Container {
}

このWidgetは各Fieldのサブクラスで、各タイプにあわせたコントロールを返すことになります。

まとめ

このようにJavaFXでは簡単に記述できるGUIフレームワークを作ることができます。すべては宣言的な文法とJavaの持つオブジェクト指向的な性質がほどよくまとまっているからではないでしょうか?
尚、このフレームワークに関しては、今後はバリデーションや各種の入力フィールドを用意するなどの拡張をしていきたいかと考えています。