void ProcessEvent(EventBase evt, [NotNull] IPanel panel) { Event e = evt.imguiEvent; // Sometimes (in tests only?) we receive Used events. Protect our verification from this case. bool imguiEventIsInitiallyUsed = e != null && e.rawType == EventType.Used; using (new EventDispatcherGate(this)) { evt.PreDispatch(panel); if (!evt.stopDispatch && !evt.isPropagationStopped) { ApplyDispatchingStrategies(evt, panel, imguiEventIsInitiallyUsed); } // Last chance to build a path. Some dispatching strategies (e.g. PointerCaptureDispatchingStrategy) // don't call PropagateEvents but still need to call ExecuteDefaultActions on composite roots. var path = evt.path; if (path == null && evt.bubblesOrTricklesDown && evt.leafTarget is VisualElement leafTarget) { path = PropagationPaths.Build(leafTarget, evt); evt.path = path; EventDebugger.LogPropagationPaths(evt, path); } if (path != null) { foreach (var element in path.targetElements) { if (element.panel == panel) { evt.target = element; EventDispatchUtilities.ExecuteDefaultAction(evt); } } // Reset target to leaf target evt.target = evt.leafTarget; } else { // If no propagation path, make sure EventDispatchUtilities.ExecuteDefaultAction has a target if (!(evt.target is VisualElement target)) { evt.target = target = panel.visualTree; } if (target.panel == panel) { EventDispatchUtilities.ExecuteDefaultAction(evt); } } m_DebuggerEventDispatchingStrategy.PostDispatch(evt, panel); evt.PostDispatch(panel); m_ClickDetector.ProcessEvent(evt); Debug.Assert(imguiEventIsInitiallyUsed || evt.isPropagationStopped || e == null || e.rawType != EventType.Used, "Event is used but not stopped."); } }
private static void HandleEventAcrossPropagationPath(EventBase evt) { // Build and store propagation path var leafTarget = (VisualElement)evt.leafTarget; var path = PropagationPaths.Build(leafTarget, evt); evt.path = path; EventDebugger.LogPropagationPaths(evt, path); var panel = leafTarget.panel; // Phase 1: TrickleDown phase // Propagate event from root to target.parent if (evt.tricklesDown) { evt.propagationPhase = PropagationPhase.TrickleDown; for (int i = path.trickleDownPath.Count - 1; i >= 0; i--) { if (evt.isPropagationStopped) { break; } var element = path.trickleDownPath[i]; if (evt.Skip(element) || element.panel != panel) { continue; } evt.currentTarget = element; evt.currentTarget.HandleEvent(evt); } } // Phase 2: Target / DefaultActionAtTarget // Propagate event from target parent up to root for the target phase // Call HandleEvent() even if propagation is stopped, for the default actions at target. evt.propagationPhase = PropagationPhase.AtTarget; foreach (var element in path.targetElements) { if (evt.Skip(element) || element.panel != panel) { continue; } evt.target = element; evt.currentTarget = evt.target; evt.currentTarget.HandleEvent(evt); } // Call ExecuteDefaultActionAtTarget evt.propagationPhase = PropagationPhase.DefaultActionAtTarget; foreach (var element in path.targetElements) { if (evt.Skip(element) || element.panel != panel) { continue; } evt.target = element; evt.currentTarget = evt.target; evt.currentTarget.HandleEvent(evt); } // Reset target to original target evt.target = evt.leafTarget; // Phase 3: bubble up phase // Propagate event from target parent up to root if (evt.bubbles) { evt.propagationPhase = PropagationPhase.BubbleUp; foreach (var element in path.bubbleUpPath) { if (evt.Skip(element) || element.panel != panel) { continue; } evt.currentTarget = element; evt.currentTarget.HandleEvent(evt); } } evt.propagationPhase = PropagationPhase.None; evt.currentTarget = null; }