倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

はじめての幻獣 Griffon 研 (11) : レイアウトを調整する install-plugin miglayout

前回に Look & Feel を変更しましたが、それに続いてやるならやっぱりレイアウトだよね!ということで(誰が言ったんだ)、今回はレイアウトの調整(一覧)。 例によって、今までのレイアウトに不具合があるわけでもないのでサンプルはいくらか恣意的ですが。

Griffon でレイアウトと言えば・・・


大抵、MiGLayout でしょう。 Griffon に限らないかも知れないけど。 ってことで、これを使って今までのレイアウトを再現してみましょう。 あくまで再現。 何てったって、「関数描画アプリケーション」は BorderLayout 使ってくれって言ってるようなレイアウトだもんで。

で、MigLayout にも BorderLayout のように north, east, west, south に配置するレイアウトが使えるようなのでこれを使おうとしたんですが、どうも CENTER の canvas (グラフを描画するパネル)部分がうまくいかなかったので(おそらく vbox のせい)、全体のレイアウトは前と同じ BorderLayout にして、各方位のコンテナ内を MiGLayout によってレイアウトすることにします*1

MiGLayout プラグインのインストール


まずは MiGLayout プラグインのインストール。 これはプロジェクト・ルートのフォルダ下で以下のコマンドを実行します:

griffon install-plugin miglayout

ただし、前回の方法で Look & Feel に関するプラグインをインストールしている場合は、それに付随して既に MiGLayout プラグインもインストールされているかも知れません。

レイアウトの適用


MiGLayout は大雑把に言えば「テーブル状にコンポーネントを配置する」レイアウトです。 で、コンポーネントを追加する際の constraints 属性に独特の文字列を渡すことでレイアウトを制御します。

MiGLayout の使用手順は

  1. レイアウトを適用したいコンポーネントの属性に「layout: new MigLayout()」を指定する
  2. (必要なら)子コンポーネントを追加する際に「constraints: '《レイアウトの制御》'」を指定する

です。

以下で MiGLayout の簡単な使い方を見ていきます。 といっても、かなりさわりだけ。 詳しくは MiGLayout のサイトを参照して下さい。 

FunctionPlotterView.groovy

準備として、View のうち MiGLayout を使わない部分(と MigLayout クラスの import 文)を載せておきます:

import net.miginfocom.swing.MigLayout    // MigLayout クラスの import

actions{ ... }

application( ... ){
    menuBar(){ ... }

    panel(border:BF.createEmptyBorder(6, 6, 6, 6)){
        borderLayout()

        vbox(border:BF.createTitledBorder('Function Plot')){
            panel(id:'canvas')
        }

        // 以下、ここにコードを追加
    }
}

def labeledSpinner(label, value){
    this.label(label)
    spinner(value:bind(target:model, label), stateChanged:controller.paintGraph, 
            model:spinnerNumberModel(value:value))
}

NORTH : シンプルに使う

では MiGLayout のコードを。 まずは、最もシンプルな使い方。 以下のコードでは constraints の指定はしてません。

        panel(constraints:BL.NORTH, layout:new MigLayout()){
            label 'f(x) = '
            textField 'sin(x)', action:paint, columns: 22, text: bind(target: model, 'function')
            button action:paint
        }

うむ、MigLayout 使う必然性が感じられん(笑)

特に constraints が指定されてない場合は、左から右(そして上から下)に順にコンポーネントを配置していきます。 1行だけだと FlowLayout と大差ないかと。 2行以上になると、MiGLayout では列(縦)が揃うようになっています。 ちなみに配置を改行したい場合は、constraints (もしくはMigLayout クラスのコンストラクタ)に 'wrap' を指定します。

SOUTH : 各列の幅を指定する

次は各列の幅を指定してみましょう。 ここでは MigLayout クラスのコンストラクタに列幅を指定しています:

        panel(constraints:BL.SOUTH, layout:new MigLayout('', '[40][20][40][160][20][40]')){
            panel()                                            // [40]
            labeledSpinner('from', 0d)                 // [20][40]
            panel()                                           // [160]
            labeledSpinner('to', CMath.TWO_PI)  // [20][40]
        }
  • コンストラクタで列の幅を指定する場合は、第2引数に で囲って幅を指定します((「10[]」のように指定すると、その列間のギャップ幅を指定できます。))。
  • constraints 属性によって幅を指定する場合は「panel(constraints:'width 40')」や「panel constratins:'w 40'」のようにします。
  • 列幅には単位を指定することもできます。 20mm, 1cm, 30% など。
  • 指定された幅が常に再現されるとは限りません。 強制したい場合は「!」を付けます*2。 20mm! など。

WEST : 各行の幅を指定する

最後はコンポーネントを縦に配置して行幅を指定:

        panel(constraints:BL.WEST, layout:new MigLayout('wrap 1', '[40]', '[10][20][150][10][20]')){
            labeledSpinner('max', 1d)   // [10][20]
            panel()                             // [150]
            labeledSpinner('min', -1d)  // [10][20]
        }
  • 縦1列にしたい場合は1行で折り返すので、コンストラクタの第1引数に「'wrap 1'」を指定します。
  • コンストラクタで行の幅を指定する場合は、第3引数に [] で囲って幅を指定します。
  • constraints 属性によって幅を指定する場合は「panel(constraints:'height 10')」や「panel constratins:'h 40'」のようにします。


以上、MiGLayout の簡単な使い方でした。

追記


Griffon では、MigLayout オブジェクトをコンストラクタから生成せずに migLayout() メソッドを用いた方が、コードが簡潔になるようです。 MigLayout クラスの import も必要なくなるので便利。

コンストラクタでレイアウトを指定していた場合は、以下のパラメータで同様の指定ができます:

上記のサンプルコードは以下のようにできます:

actions{ ... }

application( ... ){
    menuBar(){ ... }

    panel(border:emptyBorder(6)){
        borderLayout()

        vbox(border:titledBorder(title:'Function Plot')){
            panel(id:'canvas')
        }

        // NORTH : シンプルに使う
        panel(constraints:BL.NORTH){
            migLayout()    // MigLayout を使用

            label 'f(x) = '
            textField 'sin(x)', action:paint, columns: 22, text: bind(target: model, 'function')
            button action:paint
        }

        // SOUTH : 各列の幅を指定する
        panel(constraints:BL.SOUTH){
            migLayout(columnConstraints:'[40][20][40][160][20][40]')   // MigLayout を使用

            panel()
            labeledSpinner('from', 0d)
            panel()
            labeledSpinner('to', CMath.TWO_PI)
        }

        // WEST : 各行の幅を指定する
        panel(constraints:BL.WEST){
            migLayout(layoutConstraints:'wrap 1',
                           columnConstraints:'[40]',
                           rowConstraints:'[10][20][150][10][20]')   // MigLayout を使用

            labeledSpinner('max', 1d)   // [10][20]
            panel()                             // [150]
            labeledSpinner('min', -1d)  // [10][20]
        }
    }
}

def labeledSpinner(label, value){ ... }

詳しくはこちらを参照のこと。

Java Swing Hacks ―今日から使える驚きのGUIプログラミング集

Java Swing Hacks ―今日から使える驚きのGUIプログラミング集

*1:そのうちグラフ部分を Charts プラグインで書こうと思っているので、そのときには全体を MiGLayout でレイアウトできるようになるかも。

*2:サイズには「最小値 min」、「適正値 pref」、「最大値 max」を指定することができ、「min:pref:max」のように記述します。 これら3つに同じ値を指定したい場合に「!」を使用します。