伸縮可能なボックス

JavaFX面白いのに反応がほとんどないのが悲しい今日この頃。

さて、こんな感じのGUIで、ボックスの端をマウスでドラッグすることで値を変化させたい、ありがちなコントロールかと思います。

JavaFXで作ってみました。動くサンプルはこちらから。
結構、手の込んだ事をしないと実装できなそうな気がしますが、JavaFXだと80行程度で実現できます。

ソース解説

public class ResizableBox extends CustomNode {
// 中略
}

基本的にカスタムコンポーネント(Node)を作成するにはCustomNode のサブクラスを作成し、createメソッドでカスタムコンポーネントを返します。

    override function create():Node {
        return Group {
            content: [
                Rectangle { // Right
                    x: bind x + width - 5, y: bind y - 5
                    width: 10, height: bind height + 10
                    fill: Color.LIGHTBLUE
                    opacity: bind opacityRight
                    onMouseEntered: function(e) {
                        println(e);
                        opacityRight = 0.8;
                        cursor = Cursor.H_RESIZE;
                    }
                    onMouseExited: function(e) {
                        println(e);
                        opacityRight = 0.0;
                        cursor = Cursor.DEFAULT;
                    }
                    onMouseDragged: function(e) {
                        dragging = true;
                        draggingValue = e.dragX as Integer;
                        println(e);
                    }
                    onMouseReleased: function(e) {
                        update();
                        println(e);
                    }
                }
                Rectangle { // Main box
                    x: bind x, y: bind y
                    width: bind width, height: bind height
                    fill: color
                    stroke: Color.BLACK
                    opacity: bind opacityBox
                }
                Rectangle { // resizable box
                    x: bind x, y: bind y
                    width: bind width + draggingValue, height: bind height
                    fill: color
                    stroke: Color.BLACK
                    opacity: bind 1.0 - opacityBox
                }
                Text {
                    x: bind x + 20, y: bind y + height - 10
                    content: bind toLabelString(this.width as Integer)
                }
            ]
        }
    }

返すNodeは、メインのボックス、伸縮時に表示するボックス、右端に表示される強調ボックス、値を表示するラベル用テキストの4つをグループ化しています。Groupもまた1つのNodeとなるため、幾つかのNodeを組み合わせて返すのは基本になるでしょう。
右端に表示されるボックスは、マウスのイベントを処理し、マウスの進入やドラッグ時にステータスを変化させています。ポイントはステータスを変化させているだけで、後はbindにお任せして再描画させている所でしょう。このあたりがJavaFXが簡単に書けるポイントになります。

    var dragging:Boolean = false on replace {
        if (dragging) {
            opacityBox = 0.4;
        } else {
            opacityBox = 1.0;
        }
    };
    var opacityBox:Number = 1.0;

ここではbindではなくon replaceでステータスがドラッグになった(ならない)時に透明度(opacityBox )の設定を行っています。
opacityBoxはBoxにbindされているので、変更通知は各Boxへ反映されます。

ソースコード全体はこちらから。
尚、プロジェクトサイトからソースコードはダウンロード可能です。