TranslatorFX Part 2

今回は前回の続きです。前回から少し間が開いてしまいましたが...
前回紹介した 『TranslatorFX』 を作成した目的の一つは...
実際のアプリケーションで 新しく JavaFX 1.2 で追加されたレイアウトを利用してみることです。

ということで、今回も懲りずにレイアウトの話です。
前回作成したアプリケーションでは、真ん中のラジオボタンと翻訳ボタンのレイアウトに Flow を使っています。
Layout in JavaFX 1.2』 でデモを作った時は非常に素直だった Flow...
実際にアプリケーションで次のようなことをしようとすると なかなか言うことを聞いてくれない。

  • ラジオボタンと翻訳ボタンを両端に寄せるようにする。
  • ウィンドウの幅を狭めた際にラジオボタンと翻訳ボタンがウィンドウの幅に収まるように折り返すようにする。

実際の画面はこんな感じ...


これらは、JavaFX1.2 で新しく追加された LayoutInfo を使えば、もっと簡単に実現できるのかと思ったのですが...
標準の Flow の実装では思い通りにレイアウトできず、仕方なく自分で拡張することに... LayoutInfo って一体?
なぜ、わざわざ Flow を拡張したのかと言うと...

  • 最大幅、最小幅が制限できない。
  • Node を折り返した際に Flow の高さが正しく計算されない。
という問題があったからです。
前者については、getMaxWidth() の API リファレンスに 「レイアウトコンテナは この値を超えないように幅を設定するべき」 と書いてあるのに完全に無視!!
まぁ MUST ではなく SHOULD ってところがなんとも微妙な感じではありますが...
後者については、バグじゃないかな。下図のような感じで翻訳ボタンが2行目に表示された際に正しく表示されません。


これらの問題に対応したのが以下の MyFlow です。
この MyFlow を3つ使うことで上記のレイアウトを実現しています。

class MyFlow extends Flow {

   var initPrefWidth = -1.0;

   init {
       if (not isInitialized(width)) {
           initPrefWidth = super.getPrefWidth(-1);
       }
   }

   override function getPrefWidth(h: Number) {
       var prefWidth = if (initPrefWidth == -1.0) width else initPrefWidth;
       return Math.max(getMinWidth(), Math.min(prefWidth, getMaxWidth()));
   }

   override function getPrefHeight(w: Number) {
       var prefWidth  = getPrefWidth(-1);
       var prefHeight = 0.0;
       var rowWidth   = 0.0;
       var rowHeight  = 0.0;

       for (node in content) {
           var nodeWidth  = Container.getNodePrefWidth(node);
           var nodeHeight = Container.getNodePrefHeight(node);
           if (rowWidth > 0.0 and nodeWidth > prefWidth - rowWidth) {
               prefHeight += rowHeight + vgap;
               rowWidth = rowHeight = 0.0;
           }
           rowHeight = Math.max(rowHeight, nodeHeight);
           rowWidth += Math.min(prefWidth - rowWidth, nodeWidth) + hgap;
       }
       prefHeight += rowHeight;
   }
}

ラジオボタンのための Flow

ポイントは、getMaxWidth() で ctrlFrame の幅を返すようにしたこと。
最大幅を ctrlFrame の幅に制限することで、ウィンドウ幅を縮めた際にラジオボタンが折り返し表示するようになります。

var radioButtonFrame: Flow = MyFlow {
   content: [ radioButtonE2J, radioButtonJ2E ]
   override function getMaxWidth() { ctrlFrame.width }
}

翻訳ボタンのための Flow

ポイントは 3つ...

 ctrlFrame の幅から radioButtonFrame の幅を除いた残りの幅を width に bind したこと。
 もちろん右寄せになるように HPos.RIGHT を hpos に設定したこと。
 getMinWidth() で button の幅を返すようにしたこと。

最小幅を button の幅に制限することで、ウィンドウ幅を縮めた際に翻訳ボタンが折り返し表示するようになります。

var buttonFrame: Flow = MyFlow {
   content: button
   width: bind ctrlFrame.width  - radioButtonFrame.width
   hpos: HPos.RIGHT
   override function getMinWidth() { button.width }
}

ラジオボタンと翻訳ボタンのための Flow

ポイントは特にない...
強いて言えば、scene の幅を width に bind したことかな...

var ctrlFrame: Flow = MyFlow {
   content: [ radioButtonFrame, buttonFrame ]
   width:  bind scene.width
}

しかし、JavaFX 1.2 は Java One 2009 の話題作りのためか、相当無理してリリースしたようですね。まだまだ実装が追いついていないような...
やっぱり JavaFX も 1.3 ぐらいからいい感じになるのかな? J2SEJ2EE (Servlet, JSP) がそうだったように...