UIElements は、オペレーティングシステムやスクリプトからのイベントをリッスンし、EventDispatcher でこれらのイベントをディスパッチします。イベントディスパッチャーは、イベントを送信するためのディスパッチ方法を決定し、方法を決定したらそれを実行します。
ビジュアル要素やその他のサポートするクラスは、いくつかのイベントに対してデフォルトの動作を実装します。追加のイベントを作成して送信する場合もあります。例えば、MouseMoveEvent は追加の MouseEnterEvent と MouseLeaveEvent を作成できます。これらの追加イベントはキューに置かれ、現在のイベントが完了した後に処理されます。例えば、MouseMoveEvent は MouseEnterEvent と MouseLeaveEvent イベントを処理する前に完了します。
EventDispatcher.DispatchEvent() の最初のタスクは、イベントのターゲットを見つけることです。
イベントの target プロパティが既に設定されているため、これは簡単なことです。ただし、オペレーティングシステムから発生するほとんどのイベントの場合は、簡単ではありません。
イベントのターゲットは、イベントの種類によって異なります。マウスイベントの場合、ターゲットは通常、直接マウスの下にある最も選択される可能性の高い要素です。キーボードイベントの場合、ターゲットは現在フォーカスされている要素です。
ターゲットが見つかると EventBase.target に格納され、ディスパッチ処理の間は変更されません。Event.currentTarget プロパティは、現在イベントを処理している要素に更新されます。
ほとんどのマウスイベントは、選択 (picking) モードを使用してターゲットを決定します。VisualElement クラスは以下の値をサポートする pickingMode プロパティを持っています。
PickingMode.Position(デフォルト): 位置の矩形に基づいて選択します。PickingMode.Ignore: マウスイベントの結果によって選択することを防ぎます。VisualElement.ContainsPoint() メソッドをオーバーライドすると、カスタムの交差ロジックを行えます。
マウスダウンされた後、ポインターがその要素上にもうマウスオーバーしていない場合でも、要素がマウスの位置を取得し、後続のすべてのマウスイベントを排他的に受信できるようにする必要があります。これは、マウスダウンとマウスアップのイベントの間にマウスが移動する動きに対応する制御には典型的です。ボタン、スライダー、スクロールバーなどのクリックなどが、その例です。
マウスをキャプチャするには、element.CaptureMouse() か MouseCaptureController.CaptureMouse() を呼び出します。
マウスをリリースするには MouseCaptureController.ReleaseMouse() を呼び出します。CaptureMouse() の呼び出し時に他の要素がすでにマウスをキャプチャしている場合、その要素は MouseCaptureOutEvent を受け取り、キャプチャしなくなります。
どのような時でも、アプリケーションの 1 つの要素だけがキャプチャできます。要素はキャプチャできますが、マウスホイールイベントを除くすべての後続のマウスイベントのターゲットにもなります。
注意: これは、まだターゲットが設定されていないマウスイベントにのみ当てはまります。
各 UIElement パネルには、要素のフォーカスの順序を定義するフォーカスリングがあります。デフォルトでは、要素のフォーカス順序は、ビジュアル要素ツリーで深さ優先探索 (DFS) を実行することによって決まります。例えば、下に描かれているツリーのフォーカス順序は、F、B、A、D、C、E、G、I、H です。
イベントによっては、どの要素にフォーカスがあるかを決定するためにフォーカス順序を使用するものがあります。例えば、キーボードイベントのターゲットは、現在フォーカスを保持している要素です。
以下のように focus-index プロパティを使ってフォーカスの順序を制御します。
focus-index が負の場合、要素はフォーカスが可能ではありません。デフォルトで VisualElements はフォーカス可能ではありませんが、TextField などのサブクラスの中には、デフォルトでフォーカス可能なものがあります。focus-index が 0 の場合、要素はフォーカスリングアルゴリズムによって決定されたデフォルトのフォーカス順序を維持します。focus-index が正である場合、要素は focus-index = 0 を持つすべての要素の前、かつ、focus-index が高いすべての要素の前に置かれます。イベントターゲットを選択すると、ディスパッチャーはイベントの伝搬経路を計算します。伝播経路は、イベントを受け取るビジュアル要素が順序通りに並べられたリストです。
要素のリストは、ビジュアル要素のツリーのルートから開始し、ターゲットに向かって降りながら取得されます。ターゲットに達した後、ルートに向かって昇りながら取得されます。
伝播経路の最初の段階はルートからターゲットの親に下降します。これを 下降段階 (TrickleDown) と呼びます。
伝播経路の最後の段階は、ターゲットの親からルートに向かって上昇します。これを 上昇段階 (BubbleUp) と呼びます。
イベントターゲットは伝搬経路の途中にあります。
ほとんどのイベントタイプは、伝播経路に沿ったすべての要素に送信されます。ただし、上昇段階をスキップするイベントもあります。さらに、一部のイベントタイプはイベントターゲットにのみ送信されます。
要素が非表示または無効の場合、イベントを受信しません。イベントは非表示の要素や無効な要素の祖先や子孫にも伝播されます。
イベントが伝播経路に沿って送信されるとき、Event.currentTarget は現在イベントを処理している要素に更新されます。これは、イベントコールバック関数内で、Event.currentTarget はコールバックが登録された要素であり、Event.target はイベントが発生した要素であることを意味します。
各イベントタイプには独自のディスパッチ動作があります。以下の表は、各イベントタイプの挙動をまとめたものです。
| 下降 | 上昇 | キャンセル可能 | |
|---|---|---|---|
| MouseCaptureOutEvent | ✔ | ✔ | |
| MouseCaptureEvent | ✔ | ✔ | |
| ChangeEvent | ✔ | ✔ | |
| ValidateCommandEvent | ✔ | ✔ | ✔ |
| ExecuteCommandEvent | ✔ | ✔ | ✔ |
| DragExitedEvent | ✔ | ✔ | |
| DragUpdatedEvent | ✔ | ✔ | ✔ |
| DragPerformEvent | ✔ | ✔ | ✔ |
| DragEnterEvent | ✔ | ||
| DragLeaveEvent | ✔ | ||
| FocusOutEvent | ✔ | ✔ | |
| BlurEvent | ✔ | ||
| FocusInEvent | ✔ | ✔ | |
| FocusEvent | ✔ | ||
| InputEvent | ✔ | ✔ | |
| KeyDownEvent | ✔ | ✔ | ✔ |
| KeyUpEvent | ✔ | ✔ | ✔ |
| GeometryChangedEvent | |||
| MouseDownEvent | ✔ | ✔ | ✔ |
| MouseUpEvent | ✔ | ✔ | ✔ |
| MouseMoveEvent | ✔ | ✔ | ✔ |
| ContextClickEvent | ✔ | ✔ | ✔ |
| WheelEvent | ✔ | ✔ | ✔ |
| MouseEnterEvent | ✔ | ||
| MouseLeaveEvent | ✔ | ||
| MouseEnterWindowEvent | ✔ | ||
| MouseLeaveWindowEvent | ✔ | ||
| MouseOverEvent | ✔ | ✔ | ✔ |
| MouseOutEvent | ✔ | ✔ | ✔ |
| ContextualMenuPopulateEvent | ✔ | ✔ | ✔ |
| AttachToPanelEvent | |||
| DetachFromPanelEvent | |||
| TooltipEvent | ✔ | ✔ | |
| IMGUIEvent | ✔ | ✔ | ✔ |