Version: 2020.3
事件类型
处理事件

分发事件

UI 工具包事件系统监听来自操作系统或脚本的事件,并使用 EventDispatcher 将这些事件分发到视觉元素。事件分发程序为发送的每个事件确定相应分发策略。一旦确定后,分发程序就会执行该策略。

视觉元素实现了几个事件的默认行为。这可能涉及额外事件的创建和执行。例如,一个 MouseMoveEvent 可以生成一个额外的 MouseEnterEvent 和一个 MouseLeaveEvent。这些事件进入队列并在当前事件之后处理。例如,MouseMoveEventMouseEnterEventMouseLeaveEvent 事件之前完成处理。

事件类型的分发行为

每个事件类型都有自己的分发行为。每种事件类型的行为分为三个阶段:

  • 涓滴:在涓滴阶段发送到元素的事件。
  • 冒泡:在冒泡阶段发送到元素的事件。
  • 可取消:此类事件可以取消、停止或阻止默认操作的执行。

有关每种事件类型的分发行为的列表,请参阅事件参考页面

事件传播

事件分发程序选择事件目标后,会计算事件的传播路径。传播路径是接收事件的视觉元素的有序列表。传播路径按以下顺序发生:

  1. 路径从视觉元素树的根部开始,并朝着目标下降。此阶段称为涓滴 (trickle-down) 阶段
  2. 事件目标接收事件。
  3. 然后事件沿着树上升到根。此阶段称为冒泡 (bubble-up) 阶段
传播路径
传播路径

大多数事件类型将沿着传播路径发送到所有元素。某些事件类型跳过冒泡阶段,某些事件类型仅发送到事件目标。

如果元素被隐藏或禁用,它将不会接收事件。事件仍会传播到被隐藏或禁用的元素的祖先和后代。

在事件沿着传播路径行进时,Event.currentTarget 更新为当前正在处理该事件的元素。在事件回调函数中,有两个属性记录了分发行为:

  • Event.currentTarget 是注册回调的视觉元素。
  • Event.target 是发生事件的元素,例如鼠标正下方的元素。

事件目标

事件的目标取决于事件类型。对于鼠标事件,目标最常见的是直接位于鼠标下方最上层的可选择元素。对于键盘事件,目标是当前获得焦点的元素。

UI 工具包事件有一个 target 属性,包含对发生事件的元素的引用。对于大多数源自操作系统的事件,分发过程会自动找到事件目标。

目标元素存储在 EventBase.target 中并且在分发过程中不会改变。属性 Event.currentTarget 会更新为当前正在处理该事件的视觉元素。

拾取模式和自定义形状

大多数鼠标事件使用拾取模式来确定其目标。VisualElement 类有一个支持以下值的 pickingMode 属性:

  • PickingMode.Position(默认值):根据位置矩形执行拾取。
  • PickingMode.Ignore:阻止因鼠标事件而拾取。

可以覆盖 VisualElement.ContainsPoint() 方法来执行自定义的交集逻辑。

捕获鼠标

MouseDownEvent 之后,某些元素必须捕获指针位置,以确保能接收所有后续的鼠标事件,即使光标不再悬停在该元素上。例如,在单击按钮、滑动条或滚动条时就属于这种情况。

要捕获鼠标,请调用 element.CaptureMouse()MouseCaptureController.CaptureMouse()

要释放鼠标,请调用 MouseCaptureController.ReleaseMouse()。如果在调用 CaptureMouse() 时已经有另一个元素正在捕获鼠标,该元素将收到 MouseCaptureOutEvent 并失去此捕获。

在应用程序中,任何时候都只能有一个元素具有捕获。当元素具有捕获时,该元素便是除鼠标滚轮事件之外的所有后续鼠标事件的目标。这仅适用于尚未设置目标并依赖分发过程来确定目标的鼠标事件。

有关更多信息,请参阅捕获事件

焦点环和 Tab 顺序

每个 UI 工具包面板都有一个焦点环,用于定义元素的焦点顺序。默认情况下,对视觉元素树执行深度优先搜索 (DFS) 确定元素的焦点顺序。例如,以下所示的树的焦点顺序将是 F、B、A、D、C、E、G、I、H。

焦点顺序
焦点顺序

有些事件使用焦点顺序来确定哪个元素获得焦点。例如,键盘事件的目标是当前获得焦点的元素。

使用 focusable 属性可控制元素的可聚焦性。默认情况下,VisualElements 无法聚焦,但是某些子类(例如 TextField)默认可聚焦。

使用 tabIndex 属性可按如下方式控制焦点顺序(tabIndex 默认值为 0):

  • 如果 tabIndex 为负,则无法用 Tab 键导航到该元素。
  • 如果 tabIndex 为零,该元素保持默认的 Tab 顺序(通过焦点环算法确定的顺序)。
  • 如果 tabIndex 为正,该元素将置于 tabIndex 为零 (tabIndex = 0) 或 tabIndex 更小的其他元素之前。
事件类型
处理事件