イベントとイベントハンドラ

ユーザによるキー入力、タッチ操作、表示内容の変化、状態の変化など、さまざまな状況でイベントが発生します。
イベントが発生した場合、後述する過程で対応するイベントハンドラに渡されます。
イベントには同期イベントと非同期イベントがあり、同期イベントはは即時に、非同期イベントはイベントキューを経由して処理されます。
イベントハンドラを用いることで、オブジェクトの状態の変更の応じた処理をユーザアプリケーションで定義できます。
../_images/event_image001.png
イベントハンドラは、"on"+イベント名という名前のFunctionとして定義します。
CRSの実行エンジンは、イベントの伝播先にこのような名前で定義されているFunctionを探し、イベントを伝播していきます。

例)Touchイベントを受け取るイベントハンドラ

function onTouch(e) {
    title = "Hello!";
}
イベントハンドラは呼び出される際に引数としてEventオブジェクトを受け取ります。
Eventオブジェクトには下記のプロパティが定義されており、これらを用いて処理を分岐することも可能です。
e.from イベントの発信元オブジェクト
e.eventName イベント名
例として、イベントの発信元オブジェクトを用いて処理を分岐するコードを示します。
このコードではテキスト入力エリアからキーボードフォーカスが失われた際に入力内容を検査し、結果に合わせて背景色を変化させています。
function onLostFocus(e) {
    if (e.from instanceOf TextBox) {
        if (e.from.value == "") {
            e.from.bgColor = Color.RED; # 未入力ならば赤色
        } else {
            e.from.bgColor = Color.STD; # 入力済みであればデフォルト色(白色)
        }
    }
}
イベントは、イベントハンドラにより捕捉されるまで"//"(Rootオブジェクト)を頂点とするオブジェクトツリーを上位に向かって伝播していきます。
伝播の過程でイベントハンドラにより捕捉されるか、最終的にRootオブジェクトにまで到達したときに消滅します。
../_images/event_image002.png
イベントハンドラ内でpostEventメソッドを引数なしで呼び出すことで、イベントハンドラが捕らえたイベントを再度イベントの伝播の流れに戻すことができます。
../_images/event_image003.png

同期イベント処理のタイミング

同期イベントは、イベントが発生した時点でイベントキューを経由せずに直接イベントハンドラを実行します。

そのため、同期イベントのハンドリングは、関数の呼び出しと同じような流れで実行されます。


var numA = new Number {
    # デフォルトプロパティの変更に対しchangeイベントを発行します
    useChange = true;

    # デフォルトプロパティが変化した際に呼ばれるイベントハンドラです
    function onChange(e) {
        print("2");
    }
};

# 下記の処理を実行すると、1 → 2 → 3 の順で出力されます
print("1");
numA.value = 100; # 代入処理を行った瞬間、onChangeイベントが発生します
print("3");

非同期イベント処理のタイミング

非同期イベントは、常にイベントキューを経由します。イベントキューへのイベント格納と、イベントハンドラの実行は非同期で行われます。
つまり、イベントが発生したタイミングとイベントハンドラが実行されるタイミングは厳密には一致せず、実行時のコンピュータの状態により変動します。
例えば、3個の連続したイベントが発生したとき、コンピュータの状況によりイベントハンドラの実行タイミングは、以下のように変動します。

ケース1

1個目のイベントがキューに格納された後、イベントハンドラが起動される。

ケース2

2個目のイベントがキューに格納された後、イベントハンドラが起動される。

ケース3

すべてのイベントがイベントキューに格納された後、イベントハンドラが起動される。

通常、イベントの発生は何か状態が変化したときに発生します。

連続したイベントは、状態が連続して変化していることになります。

上記のケースのように連続した非同期イベントで、イベントの要因となった状態をスクリプト中で参照すると、 イベントハンドラが実行されるタイミングにより、参照した結果は異なることがあります。

例えば、非同期イベントでオブジェクトの状態変化を通知するような場合、 ケース1の場合ではイベントハンドラの1回目の呼び出しでは1個目のイベントが発生した際の値が取得できます。 ケース3の場合、イベントハンドラの1回目の呼び出しでは3個目のイベントが発生した際の値が取得できます。


最終的にはどのケースでも、イベントと同じ数だけイベントハンドラは起動されますが、 オブジェクトプロパティの現在値とイベントを起こしたときの状態は必ずしも一致しないことがある点は、特に注意してください。

イベントが発生したときのより詳細な状況を特定する必要がある場合は、イベントハンドラに渡されるEventオブジェクトを参照してください。 イベントの種類によっては、イベントオブジェクトに追加情報が格納されている事があります。


非同期イベントに対するイベントハンドラの実行で、ここで説明したような実行タイミングによる影響を抑止するためには、 必ずイベントオブジェクトの付加情報を参照して処理するようにしてください。


イベントの順序性

同期イベントでは、イベントは、イベント発生の瞬間にイベントハンドラに渡されます。

非同期イベントであれば、イベントは、イベントを発生させた動作と同じ順序でイベントキューに格納されます。

また、イベントキューに格納されたイベントは、同じ順序で取り出されてイベントハンドラに渡されます。

これらの順序は、常に一定です。


しかし、イベントを発生させる動作がいつも同じ順序で行われる保証はありません。

例えば、LostFocusイベントの次に、別のオブジェクトのGetFocusイベントが必ず発生するという前提は誤りです。 また、画面の操作により発生するイベントと、CRSスクリプトで発行したイベントのどちらが先になるのかも状況により変わるため、特定の発生順序を前提とした処理は誤りです。

例えば、Buttonオブジェクトを押下したとき発生する可能性のあるイベントは、GetFocusとTouchイベントです。

しかし、はじめからButtonにフォーカスがあるときGetFocusは発生しません。 また、onGetFocusイベントハンドラ内で、ユーザ定義イベントを発行したとき、Touchイベントとユーザ定義イベントのどちらが先にイベントキューに格納されるかは不定です。 これは、Touchイベントの発生要因となる、マウスボタンを離す操作と、CRSスクリプトの実行でpostEventメソッドを実行する処理で、 どちらが先に行われるかを決定する事ができないためです。


イベントハンドラは、どのような順序でイベントが発生しても、正しい動作となるように考慮する必要があります。

イベントの発生順序に依存する必要がある場合、そのイベントを発行する要因が、常に同じ順序で発生することを検証してください。

ただし、Biz/Browserに内蔵されるすべてのクラスは、複数のイベントの発生順序を保障していないため、 CRSスクリプトで発行するユーザ定義イベントのみ、発生順序が保障される可能性があります。