8.12 ドラッガブルシステムボタン(その1)

今回からドラッガブルシステムボタン、つまり動かせるシステムボタンを作っていくね。
えっと、そのドラッガブルシステムボタンって、 第5章で作ったシステムボタンをドラッグして動かせるようにしたものなんだよね?
ん、そう。
大体どんなものかはこの章のはじめにちょっと説明したよね。
もうかなり前の話になるよねぇ…
まぁパズルの方が結構長くなっちゃったからね。
で、ドラッガブルシステムボタンは名前からわかると思うけど、 ドラッガブルレイヤとシステムボタンを組み合わせたものなわけね。
それってシステムボタン用のレイヤをドラッガブルレイヤにするってこと?
そ。§5.6§5.7 で作った ExSystemButtonLayer クラスに §8.2 で作った DraggableLayer クラスのドラッグして動かせる機能を追加するの。
じゃあ ExSystemButtonLayer クラスを継承してドラッガブルシステムボタンを作るの?
そうそう。
それじゃ早速スクリプトの方を見ていくね。
まずコンストラクタとデストラクタ、あとメンバ変数はこんな感じ。

DraggableSystemButtonLayer クラスのメンバ変数とコンストラクタ・デストラクタ>

class DraggableSystemButtonLayer extends ExSystemButtonLayer
{
    var dragging = false// ドラッグ中の時は true、それ以外の時は false
    var dragged = false// onMouseDown メソッドが呼び出された後 onMouseUp メソッドが呼び出されるまでの間に onMouseMove メソッドが呼び出された(ドラッグされた)か
    var dragOriginX, dragOriginY; // ドラッグが開始された時点でのマウスカーソルの位置
    var onDragFunction; // ドラッグ中に呼び出されるメソッドへの参照

    // コンストラクタ
    function DraggableSystemButtonLayer(window, parent, clickfunc, dragfunc)
    {
        // スーパークラスのコンストラクタを呼び出します
        super.ExSystemButtonLayer(window, parent, clickfunc);
        // ドラッグ中に呼び出されるメソッドへの参照を onDragFunction にセットしておきます
        onDragFunction = dragfunc;
    }

    // デストラクタ
    function finalize()
    {
        // スーパークラスのデストラクタを呼び出します
        super.finalize();
    }
}

まずメンバ変数だけど、ExSystemButtonLayer クラスを継承しててシステムボタン関係のメンバ変数は元々使える状態になってるから、 必要なのはドラッグ関係のメンバ変数だけになるわけね。
確かに DraggableLayer クラスで使ってたメンバ変数もあるみたいだけど… dragged とか onDragFunction ってゆーメンバ変数は無かったよね?
DraggableLayer クラスについては §8.2 参照。
ん、その2つは今回追加したメンバ変数だよ。
まず dragged はシステムボタン用のレイヤがドラッグされたかどうかを表すメンバ変数なんだ。
それって何のために使うの?
ボタンを押したのかドラッグしたのかを見分けるためだよ。
どーいうこと?
§8.1 で、 「普通にボタンをクリックした時だけボタンが押されたって見なすことにして、 ボタンがドラッグされた時はボタンが押されたって見なさないようにする」って言ったよね。
そーいえばわたしが「システムボタンを動かせるようにしたら、 ドラッグした時にボタンが押されたって見なされちゃうんじゃない?」って訊いた時にそー言ってたね。
dragged は「普通にボタンをクリックした」のか「ボタンをドラッグした」のかを見分けるのに使うの。
あ、そーなんだ。
ま、詳しくはまた後で見ていくことにするね。
で、onDragFunction の方はドラッグしてる時に呼び出されるメソッド(への参照)なんだ。
ってことは、ドラッグしてる時に何か呼び出さなくちゃいけないメソッドがあるってこと?
システムボタンって普通は何種類かあるよね。 メッセージスキップ用のボタンとかオートモード用のボタンとかメッセージ履歴表示用のボタンとか。
うん、そだね。
だから、例えばメッセージ履歴表示用のボタンをドラッグすると、 こんなふうに他のボタンも一緒に動かすようにしないとシステムボタンの位置がバラバラになっちゃうでしょ。

<メッセージ履歴表示用のボタン(HIST)をドラッグすると他の3つのボタン(SKIP,AUTO,×)も一緒に移動します>

なるほどねぇ…
じゃあ他のボタンを一緒に動かすために onDragFunction を使うってこと?
そういうこと。
この辺はドラッガブルシステムボタンのプラグインの話になるから、次回詳しく見ていくことにするね。
はーい。
メンバ変数はこんなとこだから、次はコンストラクタを見ていくね。
…って言ってもそんなに大したことはやってないけどね。
えっと、スーパークラスのコンストラクタを呼び出して、 その後 onDragFunction にコンストラクタの第4引数の dragfunc の値を代入してるね。
ってことは、第1〜第3引数は ExSystemButtonLayer クラスのコンストラクタの引数とおんなじで、 第4引数はドラッグ中に呼び出されるメソッドへの参照になってるってことかな。
ん、そう。つまり最初の3つの引数は、それぞれこのボタンが所属するウィンドウと親レイヤ、 それからクリックされた時に呼び出されるメソッドってことだね。
だね。
じゃ次はデストラクタだけど、これは問題ないよね。
スーパークラスのデストラクタを呼び出してるだけだね。
ん。で、ドラッガブルシステムボタンはドラッグで動かせること以外はシステムボタンと同じだから、 loadImages メソッドと drawState メソッドは ExSystemButtonLayer クラスのをそのまま使えるわけね。
ExSystemButtonLayer クラスの loadImages メソッドdrawState メソッドについては §5.6 参照。
えっと、それってつまりボタンの画像の表示のやり方は ExSystemButtonLayer クラスの時とおんなじってこと?
そういうこと。
だから DraggableSystemButtonLayer クラスでオーバーライドする必要があるメソッドは onMouseDown メソッド、onMouseUp メソッド、 onMouseMove メソッドの3つだけ。
ドラッグで動かせるようにするから、 マウス関係のイベントを処理するメソッドをオーバーライドしなくちゃいけないんだね。
そう。
新しく作るメソッドってあるの?
ううん、ないよ。
あ、そーなんだ。
じゃさっき言った3つのイベントハンドラを見ていくね。
まずは onMouseDown メソッドから。

onMouseDown メソッド>

function onMouseDown(x, y, button, shift)
{
    dragging = true// ドラッグが開始されたとみなします
    dragged = false// この時点ではまだドラッグされていないので false にしておきます

    // ドラッグが開始された時の(このレイヤの座標系での)マウスカーソルの位置を保存しておきます
    dragOriginX = x;
    dragOriginY = y;

    // スーパークラス(ExSystemButtonLayer クラス)の onMouseDown メソッドを呼び出します
    super.onMouseDown(...);
}

なんか DraggableLayer クラスの onMouseDown メソッドとほとんどおんなじみたいだね。
あ、dragged = false; っていうのが増えてるのかな?
onMouseDown メソッドはマウスのボタンを押した時に呼び出されるから、 このメソッドが呼び出された後にマウスを動かせばドラッグしたことになるし、 マウスを動かさずにボタンを離せばボタンを押したことになるよね。
うん、そーだね。
つまり、ドラッグされた時に draggedtrue にすることで、 マウスのボタンが離された時に dragged の値をチェックすれば、 ボタンが押されたのかそれともドラッグされたのかが判るようになるわけ。
えっと、ドラッグされてたらマウスのボタンが離された時に draggedtrue になってて、ボタンが押されただけだったら false になってるってコト?
そーいうこと。
だから onMouseUp メソッドはこんな感じになるわけね。

onMouseUp メソッド>

function onMouseUp(x, y, button, shift)
{
    // ドラッグが終わったとみなします
    dragging = false;

    if(dragged)
    {
        // ドラッグされていた場合はボタンは押されていないとみなして
        // ButtonLayer クラスの onMouseUp メソッドを呼び出します
        global.ButtonLayer.onMouseUp(...);
    }
    else
    {
        // ドラッグされていなかった場合はボタンが押されたとみなして
        // スーパークラス(ExSystemButtonLayer クラス)の onMouseUp メソッドを呼び出します
        super.onMouseUp(...);
    }
}

そんなにフクザツじゃないみたいだけど、何してるのかはちょっとよくわかんないかな…
最初の dragging = false; は大丈夫だよね?
onMouseUp メソッドが呼び出されたってことはドラッグが終わったってことだから、 draggingfalse にしてるんだよね。
これって DraggableLayer クラスの onMouseUp メソッドでもやってたし。
そうだね。
次の if の条件もわかるでしょ?
if(dragged) だから…
ドラッグされてたら条件が真になって global.ButtonLayer.onMouseUp(...); が実行されて、 ドラッグされてなかったら条件は偽になるから super.onMouseUp(...); の方が実行されるんだよね?
ん、そういうこと。じゃまずドラッグされてなかった場合から考えることにするね。
ドラッグされてない場合はボタンが押されたってみなすから、 スーパークラスの onMouseUp メソッドを呼び出すことでボタンを押した時の処理をしてるの。
スーパークラスの onMouseUp メソッドって…ExSystemButtonLayer クラスの onMouseUp メソッドってことだよね?
そ。んじゃ ExSystemButtonLayer クラスの onMouseUp メソッドでは何してた?
んと、clickse が指定されてたら効果音を鳴らしてるね。
その後は?
え? あとはスーパークラスの onMouseUp メソッドを呼び出してるだけだよ?
そのスーパークラスってどのクラスのこと?
ExSystemButtonLayer クラスのスーパークラスだから…
SystemButtonLayer クラスかな。
ん、じゃ SystemButtonLayer クラスの onMouseUp メソッドを見てみよっか。
え〜と…if(enabled && button == mbLeft) だから、 ボタンが使える状態になってて、マウスの左ボタンが押された時に onClickFunction が呼び出されるってことだよね。
つまり、ここでボタンが押された時の処理が実行されるから、 ボタンを押した後ドラッグしなかったらちゃんとボタンが押された時の処理が実行されるわけね。
なるほどね。
ちなみに onClickFunction が呼び出された後はどうなってる?
またスーパークラスの onMouseUp メソッドを呼び出してるね。
SystemButtonLayer クラスのスーパークラスだから ButtonLayer クラスだよね。
つまり、ExSystemButtonLayer クラスと SystemButtonLayer クラスの onMouseUp メソッドを呼び出さなかったら、ボタンが押された時の処理は実行されないってことだよね。
えっ? え〜っと…ButtonLayer クラスの onMouseUp メソッドってボタンを押した時の処理はやってなかったんだっけ?
ButtonLayer クラスはマウスのボタンを押したりした時に画像を切り替えたりはするけど、 ボタンを押した時の処理はやってないよ。
ButtonLayer クラスの onMouseUp メソッドにボタンを押した時の処理は書かれてないでしょ。
あ、ホントだ。
ってワケで、ボタンがドラッグされた時、つまり if の条件が真だった時は ExSystemButtonLayer クラスと SystemButtonLayer クラスの onMouseUp メソッドは呼び出さずに、直接 ButtonLayer クラスの onMouseUp メソッドを呼び出すから、ボタンが押された時の処理は実行されないんだ。
なんかややこしーね…
じゃボタンを押した時とドラッグした時の処理の流れを図にしてみるね。

<ボタンが押された時とドラッグされた時の onMouseUp メソッドの処理の流れ>

なるほど、確かにこれならドラッグした時はボタンが押された時の処理は実行されないね。
じゃあ、その「直接 ButtonLayer クラスの onMouseUp メソッドを呼び出す」っていうのが global.ButtonLayer.onMouseUp(...); ってトコ?
そ。前にも言ったと思うけど、super.super.super.onMouseUp(...); っていう書き方はできないから、こう書いてるの。
§5.6 参照。
ふぅん、なるほどねぇ…
ま、ホントはこういうやり方はあんまり良くないとは思うんだけどね。
え、そうなの?
ん、イベントハンドラとかはスーパークラスのメソッドを呼び出すようにするのが普通だからね。
でも今回はそうするとドラッグした時にうまくいかなくなっちゃうからこうしてるんだ。
じゃあ、もしかしてちゃんと動かなかったりすることもあるの?
いや、それは大丈夫。
ただ、スクリプトの設計としてはあんまり良くないかなってことだよ。
そーなんだ。
ちなみに、DraggableSystemButtonLayer クラスを ButtonLayer クラスから直接継承して作るって手もあるよ。
さて、それじゃ最後は onMouseMove メソッドね。

onMouseMove メソッド>

function onMouseMove(x, y, shift)
{
    if(dragging) // ドラッグ中の場合は…
    {
        dragged = true// ドラッグされたので dragged を true にします
        // レイヤの移動先の x, y 座標(newLeft, newTop)を計算します
        var newLeft = left + x - dragOriginX;
        var newTop = top + y - dragOriginY;
        // レイヤが親レイヤの範囲外に出ないように位置を調整します
        if(newLeft < -width + 4) newLeft = -width + 4;
        else if(newLeft > parent.width - 4) newLeft = parent.width - 4;
        if(newTop < -height + 4) newTop = -height + 4;
        else if(newTop > parent.height - 4) newTop = parent.height - 4;
        // 移動した距離を引数に指定して onDragFunction を呼び出します
        onDragFunction(newLeft - left, newTop - top);
    }
    // スーパークラス(ButtonLayer クラス)の onMouseMove メソッドを呼び出します
    super.onMouseMove(...);
}

ややこしそーに見えるけど、これって DraggableLayer クラスの onMouseMove メソッドとほとんどおんなじなんだね。
draggedtrue にしてるとこと onDragFunction を呼び出してるとこ以外は。
そうだよ。
まず、onMouseMove メソッドが呼び出されたってことはドラッグされたってことだから、 ここで draggedtrue にしてるわけね。
あと、onDragFunction の引数がどうなってるかはわかる?
ん〜と…newLeftnewTop が移動した後の位置で、 lefttop は移動する前の位置だったはずだから…
第1引数の newLeft - left は横方向にどれだけ移動したかで、 第2引数の newTop - top は縦方向にどれだけ移動したかってことになるんじゃないかな?
ん、そのとーり。
ちなみに newLeft - left がプラスだったら右方向、マイナスだったら左方向に移動したってことね。
あと、newTop - top がプラスなら下方向、マイナスだったら上方向に移動したってこと。
引数には移動した後の位置じゃなくて、どれだけ移動したかを指定するんだね。
こうすると他のボタンを動かしやすくなるからね。
ん? どーいうこと?
例えば、このボタンが右に 10 ピクセル、下に 5 ピクセル移動したってことが判れば、 他のボタンの lefttop にそれぞれ 10 と 5 を足せばこのボタンと同じように移動させられるけど、 このボタンの lefttop が 100 と 50 になったってことが判っても、 それだけじゃ他のボタンをどれだけ動かせばいいか判らないでしょ?
なるほど。確かにそーだね。
あ、そーいえば DraggableLayer クラスの onMouseMove メソッドだと setPos メソッドを呼び出して位置を設定してたけど、今回はやらなくていいの?
onDragFunction の方で全部のボタンの位置を設定するから、 ここでは位置の設定はしなくてもいいんだ。
あ、そーなんだ。
じゃ、これで DraggableSystemButtonLayer クラスは完成だよ。
次回はドラッガブルシステムボタンを管理するためのプラグインを作っていくことにするね。
それって ExSystemButtonPlugin クラスみたいなプラグインのこと?
ExSystemButtonPlugin クラスについては §5.8§5.9§5.10 参照。
そうそう。
プラグインの方も基本的にやってることはシステムボタンの時とほとんどおんなじだから、 プラグインも次回だけで作れるんじゃないかな。
そうなの?
ん、だから第8章は次回で終わりってことになると思うよ。
ドラッガブルシステムボタンの方はずいぶんあっさり終わっちゃうんだね。
まぁ特に新しい要素とかもないしね。
それじゃ、また次回ね!


前へ | TOP | 次へ