Section 3.7 イベント

今回からはイベントについての話ね。
それって時計の表示を更新するために必要なんだよね?
ん、そうだよ。
でもイベントって一体どういうものなの?
例えば、マウスが移動したりボタンがクリックされたりとか、キーが押されたりとか、そういうの。
それをイベントって呼ぶの?
うん。
レイヤとかウィンドウのクラスには、そういうイベントが起こったときに自動的に呼び出されるメソッドがあるんだ。
へぇ、そうなんだ。
で、そういうメソッドをイベントハンドラって呼ぶの。
イベントハンドラ、ね…
具体的にはどんなものなの?
んー、実際にイベントハンドラを作ってみた方がわかりやすいかな。
うん、そだね。
じゃあ、レイヤがクリックされた時のイベントハンドラを作ってみるね。

<イベントハンドラの例>

// Layer クラスを継承して MyLayer クラスを作ります
class MyLayer extends Layer
{
    function MyLayer(window, parent)
    {
        super.Layer(window, parent);  // スーパークラスのコンストラクタを呼び出します
    }

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

    // onClick メソッドはレイヤがクリックされた時に呼び出されます
    function onClick(x, y)
    {
        System.inform("レイヤがクリックされました。");

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

class ClockWindow extends Window
{
    // コンストラクタ
    function ClockWindow()
    {
        super.Window();  // スーパークラスのコンストラクタを呼び出します

        // MyLayer クラスのオブジェクトとしてプライマリレイヤを作ります
        add(new MyLayer(thisnull));

        // プライマリレイヤのサイズをウィンドウのクライアント領域の大きさに合わせます
        primaryLayer.setSize(innerWidth, innerHeight);

        visible = true;
    }

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

var win = new ClockWindow();  // ウィンドウを作ります

まずは実行してみて。
うん。

<実行結果>

表示されたウィンドウ

ウィンドウが表示されたね。
じゃ、プライマリレイヤをクリックしてみて。
うん。

表示されたメッセージ

あっ、メッセージが表示された!
ん、ちゃんとイベントハンドラが呼び出されたね。
じゃ、スクリプトを見ていこっか。
はーい。
まず、イベントハンドラを作るために Layer クラスを継承して MyLayer クラス を作ってるんだけど、 これのコンストラクタとデストラクタはわかるよね。
スーパークラスのコンストラクタとデストラクタを呼び出してるだけだよね。
うん、そう。
で、その次にある onClick っていうメソッドが、マウスのボタンがクリックされた時に呼び出されるイベントハンドラ。
xy っていう2つの引数があるけど、これは?
xy は、それぞれクリックされた位置の x 座標と y 座標になってるんだ。
じゃあ、xy を見ればレイヤのどの部分がクリックされたかが判るってこと?
うん、そういうこと。
今回はクリックされたことが判ればいいから、どっちも使ってないけどね。
イベントハンドラの引数って必要なければ使わなくてもいいんだ?
うん。
で、あとは見てのとおり inform メソッドを呼び出してるわけね。
さっきレイヤをクリックした時にメッセージが表示されたのは、この inform メソッドが呼び出されたからなんだね。
そ。
で、onClick メソッドは Layer クラスのをオーバーライドしてるから、 最後にスーパークラスの onClick メソッドを呼んでるんだ。
引数の『...』ってなんだったっけ?
MyLayer クラスの onClick メソッドに渡された引数を、 そのままスーパークラスの onClick メソッドに渡すってこと。
§1.17 で説明したよね。
あっ、そうだった。
えっと、つまりスーパークラスの引数にも、レイヤがクリックされた位置の座標が渡されるってことなんだよね。
ん、そう。後はわかるよね?
プライマリレイヤを MyLayer クラスのオブジェクトにしてるんだよね?
プライマリレイヤには、こんなふうに Layer クラスを継承して作ったクラスのオブジェクトも使えるから、覚えといてね。
うん、わかった。
じゃあ、この辺で1つ問題ね。
あ、今回も問題あるんだ。
レイヤを右クリックするとウィンドウを閉じるイベントハンドラを作ってみて。
うわ、なんかそれって難しくない?
ま、確かにまだ説明してないメソッドが2つ出てくるからね。
それじゃできないよ〜。
まぁまぁ、これから説明するから。
まず、使うイベントハンドラは onMouseUp メソッド。
onClick メソッドじゃないの?
onClick メソッドじゃマウスのどのボタンが押されたかが判らないからね。
あ、そういえば引数にはクリックされた位置しかなかったよね。
だからマウスのどのボタンが押されたかが判る onMouseUp メソッドを使うの。
それってどんな時に呼び出されるメソッドなの?
onMouseUp メソッドは、レイヤ上でマウスのボタンが「離された」時に呼ばれるメソッドなんだ。
離された時?
マウスのボタンをクリックする時は、ボタンを押したあと離すでしょ。
うん。
ボタンが離された時に呼び出されるのが onMouseUp メソッド。
ちなみにボタンが押された時に呼び出される onMouseDown メソッドっていうのもあるよ。
そっか、ボタンを押すとボタンが下がるから onMouseDown で、 離すとボタンが上がるから onMouseUp なんだね。
ん、そーいうこと。
で、今回使う onMouseUp メソッドには引数が4つあるんだ。

onMouseUp メソッドの引数(onMouseDown メソッドも同じ引数を持ちます)>
引数名引数の意味値の種類
第1引数xマウスのボタンが離された位置の x 座標数値
第2引数yマウスのボタンが離された位置の y 座標数値
第3引数button離されたマウスボタンの種類mbLeft(左ボタン), mbRight(右ボタン), mbMiddle(中ボタン)
第4引数shiftマウスボタンが離された時に同時に押されていたシフト系のキーssAlt(ALTキー), ssShift(SHIFTキー), ssCtrl(CTRLキー)のビット OR による組み合わせ

xy の他に、buttonshift っていう引数があるね。
button にはマウスのボタンの種類を表す値が代入されてて、 shift にはマウスのボタンが離された時に押されてたキーを表す値が代入されてるんだ。
じゃあ、右クリックかどうかは button を調べれば判るってこと?
そ。buttonmbRight になってたら、マウスの右ボタンが離されたってことだね。
mbRight って Layer クラスのプロパティ?
ううん、mbRightLayer クラスのプロパティじゃないよ。
とりあえず mbRight っていうのは、マウスの右ボタンを表す、元々定義されてる値って思っといて。
ふぅん、そうなんだ。
あとはウィンドウを閉じるメソッドだね。
それってどんなメソッドなの?
Window クラスのメソッドで、close っていうメソッド。
ちなみに、このメソッドには引数はないよ。
それなら簡単に呼び出せるね。
これで onMouseUp メソッドが作れるよね。
うん、やってみるね。

onMouseUp メソッド>

function onMouseUp(x, y, button, shift)
{
    if(button == mbRight)
        close();

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

これでどうかな?
あー、これじゃダメ。
えっ!? どこか間違ってる!?
さっき close メソッドは Window クラスのメソッドって言ったよね。
う、うん…。
このスクリプトだと、MyLayer クラスの中で単に close(); って書いてるから、 Window クラスじゃなくて MyLayer クラスの close メソッドを呼び出すことになっちゃうんだ。
MyLayer クラスや Layer クラスには close っていうメソッドはないから、これだとエラーになっちゃうよ。
あ、そうなんだ…
じゃあ、どうすればいいの?
Layer クラスには window っていうプロパティがあって、 これはレイヤが所属してるウィンドウオブジェクトを表すんだ。
じゃあ、その window っていうプロパティを使って close メソッドを呼び出すの?
そう。
window.close();』って書けば、ちゃんと Window クラスの close メソッドを呼び出せるよ。
なるほどねぇ。
じゃ、改めて onMouseUp メソッドを作ってみて。
はーい。

onMouseUp メソッド(修正版)>

function onMouseUp(x, y, button, shift)
{
    if(button == mbRight)
        window.close();  // このレイヤが所属しているウィンドウの close メソッドを呼び出します

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

これでいいんだよね?
ん、これで OK。
じゃ、あとは最初のスクリプトonClick メソッドを onMouseUp メソッドに置き換えて、全体のスクリプトを書いてみて。
りょーかい!

<プライマリレイヤを右クリックすると閉じるウィンドウ>

// Layer クラスを継承して MyLayer クラスを作ります
class MyLayer extends Layer
{
    function MyLayer(window, parent)
    {
        super.Layer(window, parent);  // スーパークラスのコンストラクタを呼び出します
    }

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

    // onMouseUp メソッドはレイヤ上でマウスボタンが離された時に呼び出されます
    function onMouseUp(x, y, button, shift)
    {
        if(button == mbRight)
            window.close();  // このレイヤが所属しているウィンドウの close メソッドを呼び出します

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

class ClockWindow extends Window
{
    // コンストラクタ
    function ClockWindow()
    {
        super.Window();  // スーパークラスのコンストラクタを呼び出します

        // MyLayer クラスのオブジェクトとしてプライマリレイヤを作ります
        add(new MyLayer(thisnull));

        // プライマリレイヤのサイズをウィンドウのクライアント領域の大きさに合わせます
        primaryLayer.setSize(innerWidth, innerHeight);

        visible = true;
    }

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

var win = new ClockWindow();  // ウィンドウを作ります

できたよー。
じゃ、実行してみよっか。
うん。
……プライマリレイヤを右クリック、と。
あっ、ウィンドウが消えた!
ん、ちゃんとウィンドウが閉じられたね。
これでいいんだよね?
うん、OK だよ。
よかった〜。
それじゃ、問題も解けたことだし、今回はこれくらいにしとこっか。
うん。
次回もイベントの続きをやるね。
それじゃ、また次回!