Version: 2023.1
言語: 日本語
Dispatch events
Manipulators

イベントの処理

UI Toolkit のイベントは、HTML イベント に似ています。イベントが発生すると、ターゲットのビジュアル要素と、ビジュアル要素ツリー内の伝播パス内のすべての要素に送信されます。

イベント処理の流れは以下の通りです。

  1. ルート要素からイベントターゲットの親までの要素にイベントコールバックを実行する。これはディスパッチプロセスの 下降段階 です。
  2. イベントターゲットでイベントコールバックを実行します。これはディスパッチ処理の ターゲット段階 です。
  3. イベントターゲットで ExecuteDefaultActionAtTarget() を呼び出します。
  4. イベントターゲットの親からルートまでの要素のイベントコールバックを実行します。これはディスパッチプロセスの 上昇段階 です。
  5. ExecuteDefaultAction() をイベントターゲット上で呼び出します。

イベントが伝搬経路を移動すると、Event.currentTarget プロパティは、現在そのイベントを処理する要素に更新されます。イベントコールバック関数内では、以下の通りです。

  • Event.currentTarget は、コールバックが登録されるビジュアル要素です。
  • Event.target は、元のイベントが発生するビジュアル要素です。

詳しくは、イベントのディスパッチ を参照してください。

イベントコールバックを登録する

イベントコールバックを登録して、テキストラベル上でマウスをクリックしたときの動作など、既存クラスの個々のインスタンスの動作をカスタマイズできます。

伝搬経路上の各要素 (ターゲットを除く) は、イベントを 2 回受け取ることができます。

  • 降下段階で 1 度。
  • 上昇段階で 1 度

デフォルトでは、登録されたコールバックは、ターゲットフェーズとバブルアップフェーズの間に実行されます。このデフォルトの動作により、親要素が子要素の後に反応するようになります。

一方、親要素を子要素より先に反応させたい場合は、TrickleDown.TrickleDownオプションでコールバックを登録します。

using UnityEngine;
using UnityEngine.UIElements;

...
VisualElement myElement = new VisualElement();

// Register a callback for the trickle-down phase.
myElement.RegisterCallback<MouseDownEvent>(MyCallback, TrickleDown.TrickleDown);
...

これは、ディスパッチャーに、ターゲット段階と降下段階でコールバックを実行するように通知します。

カスタムビヘイビアを特定のビジュアル要素に加えるには、その要素にイベントコールバックを登録します。

例えば、以下のコードは MouseDownEvent のコールバックを登録します。

// マウスダウンイベントのコールバックの登録event
myElement.RegisterCallback<MouseDownEvent>(MyCallback);

コールバック関数のシグネチャは以下のようになります。

void MyCallback(MouseDownEvent evt) { /* ... */ }

1 つのイベントに対して複数のコールバックを登録することができます。ただし、同じコールバック関数を登録できるのは、同じイベントと同じ伝播段階に 1 度だけです。

VisualElement からコールバックを削除するには、myElement.UnregisterCallback()メソッドを呼び出します。

カスタムデータをイベントコールバックに送信する

イベントのコールバックと一緒にカスタムデータを送ることができます。カスタムデータを添付するには、呼び出しを拡張してコールバックを登録する必要があります。

以下の例では、MouseDownEvent のコールバックを登録し、カスタムデータをコールバック関数に送信しています。

// ユーザーデータをコールバックとともに送信
myElement.RegisterCallback<MouseDownEvent, MyType>(MyCallbackWithData, myData);

コールバック関数のシグネチャは以下のようになります。

void MyCallbackWithData(MouseDownEvent evt, MyType data) { /* ... */ }

値の変化をリッスンする

UI コントロールは、value プロパティを使用して、以下の例のように内部状態のデータを保持することができます。

  • ToggleToggle がオンまたはオフになると変化するブール値を格納します。
  • IntegerField は、フィールドの値を保持する整数を格納します。

コントロールの値は以下のようにして取得できます。

  • コントロールから直接値を取得します: int val = myIntegerField.value;

  • コントロールから送信される ChangeEvent をリッスンし、変更が発生したときに処理を行います。以下のように、イベントに対するコールバックを登録する必要があります。

    //RegisterValueChangedCallback is a shortcut for RegisterCallback<ChangeEvent>. 
    //It constrains the right type of T for any VisualElement that implements an 
    //INotifyValueChange interface.
    myIntegerField.RegisterValueChangedCallback(OnIntegerFieldChange);
    

    コールバック関数のシグネチャは以下のようになります。

    void OnIntegerFieldChange(ChangeEvent<int> evt) { /* ... */ }
    

コントロールの値を以下のようにして変更できます。

  • value 変数を直接変更します。myControl.value = myNewValue; これは新しいChangeEvent をトリガーします。
  • myControl.SetValueWithoutNotify(myNewValue); を使用します。これは新しいChangeEvent をトリガーしません。

詳しくは、イベントの変更 を参照してください。

Capture the pointer

ポインターの入力を処理する場合、コントロールにポインターをキャプチャさせたい場合があります。ビジュアル要素がポインターをキャプチャすると、ポインターがビジュアル要素上にあるかどうかにかかわらず、Unity はポインターに関連するすべてのイベントをビジュアル要素に送信します。例えば、ドラッグイベントを受信するコントロールを作成し、ポインターをキャプチャすると、コントロールはポインターの位置に関係なくドラッグイベントを受信します。

ポインターをキャプチャするには、キャプチャイベント を使用します。例は、カスタムエディターウィンドウ内にドラッグアンドドロップ UI を作成する を参照してください。

カスタムコントロールでイベントに応答する

カスタムコントロールを実装している場合、UI Toolkit のイベントに 2 つの方法で応答することができます。

  • イベントコールバックの登録
  • デフォルトアクションの実装

イベントに対してどのように対応するかは、状況によって異なります。

コールバックとデフォルトアクションの違いは以下の通りです。

  • コールバックはクラスのインスタンスに登録する必要があります。デフォルトアクションは、クラスの仮想関数として実行されます。
  • コールバックは、伝搬経路上のすべてのビジュアル要素に対して実行されます。デフォルトのアクションは、イベントターゲットに対してのみ実行されます。
  • コールバックは、イベントに反応すべきかどうかを判断するために、追加確認を行う場合があります。例えば、マウスのクリックを処理するコールバックは、その要素がイベントのターゲットであるかどうかを確認する場合があります。デフォルトのアクションはこの手順を省略できます。
  • デフォルトアクションは伝搬段階でコールバックレジストリの検索を必要としないため、パフォーマンス面で若干有利です。

デフォルトアクションの実装

デフォルトアクションは、クラスのすべてのインスタンスに適用されます。デフォルトアクションを実装するクラスは、そのインスタンスにコールバックを登録することもできます。

クラスにデフォルトのアクションを実装する場合、VisualElement の新しいサブクラスを派生させ、ExecuteDefaultActionAtTarget() メソッドか ExecuteDefaultAction() メソッドのいずれか、または両方を実装する必要があります。

デフォルトアクションは、ビジュアル要素のサブクラスのインスタンスがイベントを受け取ったときに、各インスタンス上で実行されます。デフォルトアクションをカスタマイズするには、以下の例のように、 ExecuteDefaultActionAtTarget()ExecuteDefaultAction() をオーバーライドします。

override void ExecuteDefaultActionAtTarget(EventBase evt)
{
    // Call the base function.
    base.ExecuteDefaultActionAtTarget(evt);

    if (evt.eventTypeId == MouseDownEvent.TypeId())
    {
        // ...
    }
    else if (evt.eventTypeId == MouseUpEvent.TypeId())
    {
        // ...
    }
    // More event types
}

ExecuteDefaultAction() にデフォルトアクションを実装すると、デフォルトのアクションの実行を停止または防止できます。

ターゲットのデフォルトアクションを親のコールバックの前に実行したい場合は、デフォルトアクションを ExecuteDefaultActionAtTarget() に実装します。

デフォルトアクションは、あるタイプの要素がイベントを受け取ったときの動作と考える必要があります。例えば、チェックボックスは、クリックイベントに反応して、その状態を切り替えます。これを実行するには、すべてのチェックボックスにコールバックを登録するのではなく、デフォルトアクションの仮想関数をオーバーライドします。

カスタム制御の実践例

以下は、カスタムコントロールの効率的な実践例です。

動作の実装

要素の動作をデフォルトの動作で実装する必要があります。インスタンスに接続されたコールバックで PreventDefault() を呼び出すことで、要素のデフォルトの動作をキャンセルすることができます。

動作をデフォルトアクションとして実装することによるその他の利点は以下の通りです。

  • デフォルトアクションが、コールバックレジストリのルックアップを必要としません。
  • コールバックのないインスタンスは伝搬プロセスに入りません。

柔軟性を高めるために、イベントのディスパッチ処理中にイベントターゲットのデフォルトのアクションを 2 か所で実行できます。

  • 下降と上昇の伝播段階の間で、ターゲットコールバックの実行直後に、ExecuteDefaultActionsAtTarget() をオーバーライドします。
  • イベントのディスパッチプロセスの最後に、ExecuteDefaultActions() をオーバーライドします。

クラスのデフォルトアクション

可能であれば ExecuteDefaultActions() にクラスのデフォルトアクションを実装してください。これにより、クラスをオーバーライドするためのオプションが増えます。PreventDefault() を呼び出して、イベント伝播プロセスの降下段階または上昇段階でクラスをオーバーライドすることができます。

イベントが親要素に伝搬してはいけない場合は、デフォルトアクション中にイベントの伝搬を停止する必要があります。例えば、テキストフィールドは KeyDownEvent を受け取って、その値を変更します。例えば、Delete キーでコンテンツを削除するように。このイベントは、親のビジュアル要素に伝播してはいけません。ExecuteDefaultActionsAtTarget() を使ってデフォルトアクションを実装し、StopPropagation() を呼び出して上昇段階でイベントが処理されないようにします。

デフォルトアクションは、イベントターゲットに対してのみ実行されます。クラスが子要素や親要素を対象としたイベントに反応するには、降下または上昇の伝搬段階で、イベントを受け取るコールバックを登録する必要があります。パフォーマンスを向上させるために、クラスにコールバックを登録することは避けてください。

イベント伝播の停止とデフォルトアクションのキャンセル

コールバックやデフォルトアクションの中でイベントを処理する場合、イベントの伝搬やデフォルトアクションの実行を停止することができます。例えば、親パネルは降下段階で伝搬を停止し、子パネルがイベントを受け取らないようにすることができます。

EventBase.PreDispatch()EventBase.PostDispatch() メソッドの実行は、イベントクラスの内部で防ぐことはできません。

以下のメソッドは、イベント伝播とデフォルトアクションに影響します。

  • StopImmediatePropagation(): イベントの伝搬プロセスを直ちに停止します。そのため、そのイベントに対する他のコールバックは実行されません。ただし、ExecuteDefaultActionAtTarget()ExecuteDefaultAction() のデフォルトアクションは引き続き実行されます。
  • StopPropagation(): 現在の要素の最後のコールバック後のイベント伝播プロセスを停止します。これにより、現在の要素のすべてのコールバックが確実に実行されますが、それ以降の要素はイベントに反応しません。ExecuteDefaultActionAtTarget()ExecuteDefaultAction() のデフォルトアクションだけは引き続き実行されます。
  • PreventDefault(): Prevents the event propagation process from calling the ExecuteDefaultActionAtTarget() and ExecuteDefaultAction() default actions. PreventDefault() doesn’t prevent the execution of other callbacks and doesn’t affect the ExecuteDefaultActionAtTarget() action during the bubble-up phase.

その他の参考資料

Dispatch events
Manipulators