MaxScriptTip: SubRolloutに合わせて自動的にサイズを変えるロールアウト

2月 21, 2016
今回は前回の記事に続いてRolloutについての記事です。

UIオブジェクトの一つに、「SubRollout」というコントロールがありますが、今回はこのSubRolloutに合わせて動的にサイズを変更するロールアウトを作りたいと思います。


サンプルロールアウト

起動直後。


ボタンスイッチを押すとそれに対応するサブロールアウトが追加されます。
親ロールアウトは、自動的に子ロールアウトのサイズに合わせます。


全てのサブロールアウトを表示した状態。


ロールアウトを畳んだ時も自動でサイズを変更します。



サブロールアウト作成

初めに、子ロールアウトを作成します。
rollout rltSub1 "Sub1" width:190 height:32 category:1  --- 1. カテゴリの指定
(
    local resizeCallback  -- 2. コールバック変数

    checkbox chkOpt "Option" pos:[8,8] width:152 height:16
    
    -- 3. サイズ変更イベント
    on rltSub1 rolledUp isOpen do
    (
        if resizeCallback != undefined do
            resizeCallback()
    )
)

rollout rltSub2 "Sub2" width:192 height:80 category:2
(
    local resizeCallback
    radiobuttons rdoItems "Items" pos:[8,8] width:96 height:36 \
        labels:#("Item1", "Item2", "Item3")
        
    on rltSub2 rolledUp isOpen do
    (
        if resizeCallback != undefined do
            resizeCallback()
    )
)

rollout rltSub3 "Sub3" width:192 height:112 category:3
(
    local resizeCallback
    editText edtMsg "" pos:[4,8] width:180 height:96
    
    on rltSub3 rolledUp isOpen do
    (
        if resizeCallback != undefined do
            resizeCallback()
    )
)
3つのロールアウトを定義していますが、基本的に3つとも内容は同じです。
要点だけ説明します。
  1. カテゴリの指定
    ロールアウト作成時にcategoryパラメータを設定します。
    こうすることで、サブロールアウトが追加された時に自動的に順番を整列してくれるようになります。例えば、category:3のロールアウトを追加した後からcategory:1のロールアウトを追加した場合でも、category:1が先に来るようになります。
  2. コールバック変数
    この変数は親ロールアウトのresize関数を格納するための変数です。
    サブロールアウト作成時に、親からresize関数オブジェクトを受け取って格納します。
  3. サイズ変更イベント
    サブロールアウトが畳まれたり開かれたりした時に発生します。
    親のresize関数を実行してサイズを変更させます。
以上です。

コールバックの概念が解ってないと少々難解かもしれませんが、基本的にrolledUpイベントを親に伝搬する以外は、特別な機能は持っていません

親ロールアウト作成

次に親ロールアウトを作成します。
rollout rltMain "Main Rollout" width:200 height:180
(
    local self
    local subRollouts
    local btmControls
    subRollout srFloater "" pos:[-1,40] width:206 height:102
    checkbutton ckbSub1 "Sub1" pos:[10,10] width:60 height:20
    checkbutton ckbSub2 "Sub2" pos:[70,10] width:60 height:20
    checkbutton ckbSub3 "Sub3" pos:[130,10] width:60 height:20
    button btnGet "Get" pos:[10,150] width:90 height:20
    button btnClose "Close" pos:[100,150] width:90 height:20
    
    -- 3. サイズ変更関数
    fn resize =
    (
        local lastHeight = srFloater.height
        local maxHeight = 1000
        local subsHeight = 4
        
        for rlt in srFloater.rollouts do
        (
            if rlt.open then
                subsHeight += (rlt.height + 25)
            else
                subsHeight += 21
        )
        subsHeight = amin subsHeight maxHeight
        srFloater.height = subsHeight
        
        local offsetY = subsHeight - lastHeight
        self.height += offsetY
        for c in btmControls do
            c.pos.y += offsetY
    )
    
    -- 2. サブロールアウト追加・削除関数
    fn addSub rlt =
    (
        addSubRollout srFloater rlt
        rlt.resizeCallback = resize
    )
    
    fn removeSub rlt =
    (
        removeSubRollout srFloater rlt
    )
    
    -- 1. ロールアウト初期化
    on rltMain open  do
    (
        local btmY = srFloater.pos.y + srFloater.height
        self = rltMain
        subRollouts = #(rltSub1, rltSub2, rltSub3)
        btmControls = (for c in self.controls where c.pos.y > btmY collect c)
        resize()
    )
    
    -- 4. サブロールアウトON/OFFボタン変更イベント
    on ckbSub1 changed state do
    (
        if state then
            addSub subRollouts[1]
        else
            removeSub subRollouts[1]
        resize()
    )
    
    on ckbSub2 changed state do
    (
        if state then
            addSub subRollouts[2]
        else
            removeSub subRollouts[2]
        resize()
    )
    
    on ckbSub3 changed state do
    (
        if state then
            addSub subRollouts[3]
        else
            removeSub subRollouts[3]
        resize()
    )
    
    -- 5. パラメータ取得ボタンクリックイベント
    on btnGet pressed do
    (
        local opt = subRollouts[1].chkOpt.checked
        local items = subRollouts[2].rdoItems.state
        local msg = try subRollouts[3].edtMsg.text catch ""
        
        ss = StringStream ""
        format "Option: %\n\n" opt to:ss
        format "Items: %\n\n" items to:ss
        format "Text: '%'" msg to:ss
        
        messageBox (ss as string) title:"Result" beep:false
    )
    
    on btnClose pressed do
    (
        destroyDialog self
    )
)

createDialog rltMain

少し順番が前後してしまって見づらいかもしれませんが、番号の順に説明していきます。

  1. ロールアウト初期化
    主にロールアウト変数の初期化を行っています。
    self: このロールアウト自身のインスタンスを保持する為の変数です。理由は前回の記事を参照してください。
    subRollouts: サブロールアウトのインスタンスを保持する配列です。こちらも理由は前回の記事で説明した通りです。
    btmControls: サブロールアウトより下にあるコントロールの配列を格納しています。リサイズ時に、サブロールアウトの高さに合わせて位置を移動させる必要が有るため、予めリスト化しておきます。
    最後にresize()関数を呼び出してウィンドウサイズを初期化します。
  2. サブロールアウト追加・削除関数
    単純にサブロールアウトの追加と削除を行う関数ですが、追加後にサブロールアウトのresizeCallbackに親のresize関数オブジェクトを代入しています。
    こちらも前回の記事で説明していますが、ロールアウトは実体化のタイミングで変数が全てクリアされてしまうので、このタイミングで代入する必要があります。
  3. サイズ変更関数
    全ての子ロールアウトの高さを計算し、SubRolloutと親ロールアウトの高さ、それからbtmControlsに格納したコントロールのY位置を調整しています。
    各ロールアウトの高さを1px単位で微調整していますが、Maxのバージョンによってはこの辺りの細かい数値は変わってきてしまうかもしれません。この値は2014に最適化しています。
    maxHeightの値を変えることによってサブロールアウトの最大高さを変える事ができます。
  4. サブロールアウトON/OFFボタン変更イベント
    ボタンのON/OFF状態を見て、サブロールアウトを追加したり削除したりしています。
    最後にresize()を呼び出して、ロールアウトサイズを調整しています。
  5. パラメータ取得ボタンクリックイベント
    現在のサブロールアウトのパラメータを取得してメッセージボックスに出力します。
self変数とsubRollouts変数は一見意味が無いように見えますが、前回の記事のとおり、ロールアウトを多重起動した時に、確実に自分自身のインスタンスを参照する為に必要な変数です。
これをやっておかないと、ロールアウト多重起動時に正しく処理できなくなってしまいます。

ちょっと長くなってしまいましたが、こんな感じです。

最後に

どちらかというと、MaxよりはMayaっぽい見た目のウィンドウになりますが、パラメータをロールアウト毎にグループ化しておいて、最下部の"実行"ボタンで処理を実行したりと、何かと便利に使える形式だと思います。
使う側から見ても、視線を上から下に移動しながら最後に実行ボタンを押せるので、UIの基本として理にかなっているかと思います。

今回作成したコードはこちらからダウンロードする事ができます。
コードに特に使用制限などは設定しませんので、自由に改変等して使って頂ければ幸いです。

Related Articles