暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

PanelEventHandler.cs 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. using UnityEngine.EventSystems;
  2. namespace UnityEngine.UIElements
  3. {
  4. #if PACKAGE_UITOOLKIT
  5. /// <summary>
  6. /// Use this class to handle input and send events to UI Toolkit runtime panels.
  7. /// </summary>
  8. [AddComponentMenu("UI Toolkit/Panel Event Handler (UI Toolkit)")]
  9. public class PanelEventHandler : UIBehaviour, IPointerMoveHandler, IPointerUpHandler, IPointerDownHandler,
  10. ISubmitHandler, ICancelHandler, IMoveHandler, IScrollHandler, ISelectHandler, IDeselectHandler,
  11. IPointerExitHandler, IPointerEnterHandler, IRuntimePanelComponent
  12. {
  13. private BaseRuntimePanel m_Panel;
  14. /// <summary>
  15. /// The panel that this component relates to. If panel is null, this component will have no effect.
  16. /// Will be set to null automatically if panel is Disposed from an external source.
  17. /// </summary>
  18. public IPanel panel
  19. {
  20. get => m_Panel;
  21. set
  22. {
  23. var newPanel = (BaseRuntimePanel)value;
  24. if (m_Panel != newPanel)
  25. {
  26. UnregisterCallbacks();
  27. m_Panel = newPanel;
  28. RegisterCallbacks();
  29. }
  30. }
  31. }
  32. private GameObject selectableGameObject => m_Panel?.selectableGameObject;
  33. private EventSystem eventSystem => UIElementsRuntimeUtility.activeEventSystem as EventSystem;
  34. private readonly PointerEvent m_PointerEvent = new PointerEvent();
  35. protected override void OnEnable()
  36. {
  37. base.OnEnable();
  38. RegisterCallbacks();
  39. }
  40. protected override void OnDisable()
  41. {
  42. base.OnDisable();
  43. UnregisterCallbacks();
  44. }
  45. void RegisterCallbacks()
  46. {
  47. if (m_Panel != null)
  48. {
  49. m_Panel.destroyed += OnPanelDestroyed;
  50. m_Panel.visualTree.RegisterCallback<FocusEvent>(OnElementFocus, TrickleDown.TrickleDown);
  51. m_Panel.visualTree.RegisterCallback<BlurEvent>(OnElementBlur, TrickleDown.TrickleDown);
  52. }
  53. }
  54. void UnregisterCallbacks()
  55. {
  56. if (m_Panel != null)
  57. {
  58. m_Panel.destroyed -= OnPanelDestroyed;
  59. m_Panel.visualTree.UnregisterCallback<FocusEvent>(OnElementFocus, TrickleDown.TrickleDown);
  60. m_Panel.visualTree.UnregisterCallback<BlurEvent>(OnElementBlur, TrickleDown.TrickleDown);
  61. }
  62. }
  63. void OnPanelDestroyed()
  64. {
  65. panel = null;
  66. }
  67. void OnElementFocus(FocusEvent e)
  68. {
  69. if (!m_Selecting && eventSystem != null)
  70. eventSystem.SetSelectedGameObject(selectableGameObject);
  71. }
  72. void OnElementBlur(BlurEvent e)
  73. {
  74. // Important: if panel discards focus entirely, it doesn't discard EventSystem selection necessarily.
  75. // Also note that if we arrive here through eventSystem.SetSelectedGameObject -> OnDeselect,
  76. // eventSystem.currentSelectedGameObject will still have its old value and we can't reaffect it immediately.
  77. }
  78. private bool m_Selecting;
  79. public void OnSelect(BaseEventData eventData)
  80. {
  81. m_Selecting = true;
  82. try
  83. {
  84. // This shouldn't conflict with EditorWindow calling Panel.Focus (only on Editor-type panels).
  85. m_Panel?.Focus();
  86. }
  87. finally
  88. {
  89. m_Selecting = false;
  90. }
  91. }
  92. public void OnDeselect(BaseEventData eventData)
  93. {
  94. m_Panel?.Blur();
  95. }
  96. public void OnPointerMove(PointerEventData eventData)
  97. {
  98. if (m_Panel == null || !ReadPointerData(m_PointerEvent, eventData))
  99. return;
  100. using (var e = PointerMoveEvent.GetPooled(m_PointerEvent))
  101. {
  102. SendEvent(e, eventData);
  103. }
  104. }
  105. public void OnPointerUp(PointerEventData eventData)
  106. {
  107. if (m_Panel == null || !ReadPointerData(m_PointerEvent, eventData, PointerEventType.Up))
  108. return;
  109. using (var e = PointerUpEvent.GetPooled(m_PointerEvent))
  110. {
  111. SendEvent(e, eventData);
  112. if (e.pressedButtons == 0)
  113. PointerDeviceState.SetPlayerPanelWithSoftPointerCapture(e.pointerId, null);
  114. }
  115. }
  116. public void OnPointerDown(PointerEventData eventData)
  117. {
  118. if (m_Panel == null || !ReadPointerData(m_PointerEvent, eventData, PointerEventType.Down))
  119. return;
  120. if (eventSystem != null)
  121. eventSystem.SetSelectedGameObject(selectableGameObject);
  122. using (var e = PointerDownEvent.GetPooled(m_PointerEvent))
  123. {
  124. SendEvent(e, eventData);
  125. PointerDeviceState.SetPlayerPanelWithSoftPointerCapture(e.pointerId, m_Panel);
  126. }
  127. }
  128. public void OnPointerExit(PointerEventData eventData)
  129. {
  130. if (m_Panel == null || !ReadPointerData(m_PointerEvent, eventData))
  131. return;
  132. // If a pointer exit is called while the pointer is still on top of this object, it means
  133. // there's something else removing the pointer, so we might need to send a PointerCancelEvent.
  134. // This is necessary for touch pointers that are being released, because in UGUI the object
  135. // that was last hovered will not always be the one receiving the pointer up.
  136. if (eventData.pointerCurrentRaycast.gameObject == gameObject &&
  137. eventData.pointerPressRaycast.gameObject != gameObject &&
  138. m_PointerEvent.pointerId != PointerId.mousePointerId)
  139. {
  140. using (var e = PointerCancelEvent.GetPooled(m_PointerEvent))
  141. {
  142. SendEvent(e, eventData);
  143. }
  144. }
  145. m_Panel.PointerLeavesPanel(m_PointerEvent.pointerId, m_PointerEvent.position);
  146. }
  147. public void OnPointerEnter(PointerEventData eventData)
  148. {
  149. if (m_Panel == null || !ReadPointerData(m_PointerEvent, eventData))
  150. return;
  151. m_Panel.PointerEntersPanel(m_PointerEvent.pointerId, m_PointerEvent.position);
  152. }
  153. public void OnSubmit(BaseEventData eventData)
  154. {
  155. if (m_Panel == null)
  156. return;
  157. using (var e = NavigationSubmitEvent.GetPooled())
  158. {
  159. SendEvent(e, eventData);
  160. }
  161. }
  162. public void OnCancel(BaseEventData eventData)
  163. {
  164. if (m_Panel == null)
  165. return;
  166. using (var e = NavigationCancelEvent.GetPooled())
  167. {
  168. SendEvent(e, eventData);
  169. }
  170. }
  171. public void OnMove(AxisEventData eventData)
  172. {
  173. if (m_Panel == null)
  174. return;
  175. using (var e = NavigationMoveEvent.GetPooled(eventData.moveVector))
  176. {
  177. SendEvent(e, eventData);
  178. }
  179. // TODO: if runtime panel has no internal navigation, switch to the next UGUI selectable element.
  180. }
  181. public void OnScroll(PointerEventData eventData)
  182. {
  183. if (m_Panel == null || !ReadPointerData(m_PointerEvent, eventData))
  184. return;
  185. var scrollDelta = eventData.scrollDelta;
  186. scrollDelta.y = -scrollDelta.y;
  187. const float kPixelPerLine = 20;
  188. // The old input system reported scroll deltas in lines, we report pixels.
  189. // Need to scale as the UI system expects lines.
  190. scrollDelta /= kPixelPerLine;
  191. using (var e = WheelEvent.GetPooled(scrollDelta, m_PointerEvent))
  192. {
  193. SendEvent(e, eventData);
  194. }
  195. }
  196. private void SendEvent(EventBase e, BaseEventData sourceEventData)
  197. {
  198. //e.runtimeEventData = sourceEventData;
  199. m_Panel.SendEvent(e);
  200. if (e.isPropagationStopped)
  201. sourceEventData.Use();
  202. }
  203. private void SendEvent(EventBase e, Event sourceEvent)
  204. {
  205. m_Panel.SendEvent(e);
  206. if (e.isPropagationStopped)
  207. sourceEvent.Use();
  208. }
  209. void Update()
  210. {
  211. if (m_Panel != null && eventSystem != null && eventSystem.currentSelectedGameObject == selectableGameObject)
  212. ProcessImguiEvents(true);
  213. }
  214. void LateUpdate()
  215. {
  216. // Empty the Event queue, look for EventModifiers.
  217. ProcessImguiEvents(false);
  218. }
  219. private Event m_Event = new Event();
  220. private static EventModifiers s_Modifiers = EventModifiers.None;
  221. void ProcessImguiEvents(bool isSelected)
  222. {
  223. bool first = true;
  224. while (Event.PopEvent(m_Event))
  225. {
  226. if (m_Event.type == EventType.Ignore || m_Event.type == EventType.Repaint ||
  227. m_Event.type == EventType.Layout)
  228. continue;
  229. s_Modifiers = first ? m_Event.modifiers : (s_Modifiers | m_Event.modifiers);
  230. first = false;
  231. if (isSelected)
  232. {
  233. ProcessKeyboardEvent(m_Event);
  234. if (m_Event.type != EventType.Used)
  235. ProcessTabEvent(m_Event);
  236. }
  237. }
  238. }
  239. void ProcessKeyboardEvent(Event e)
  240. {
  241. if (e.type == EventType.KeyUp)
  242. {
  243. SendKeyUpEvent(e);
  244. }
  245. else if (e.type == EventType.KeyDown)
  246. {
  247. SendKeyDownEvent(e);
  248. }
  249. }
  250. // TODO: add an ITabHandler interface
  251. void ProcessTabEvent(Event e)
  252. {
  253. if (e.type == EventType.KeyDown && e.character == '\t')
  254. {
  255. SendTabEvent(e, e.shift ? -1 : 1);
  256. }
  257. }
  258. private void SendTabEvent(Event e, int direction)
  259. {
  260. using (var ev = NavigationTabEvent.GetPooled(direction))
  261. {
  262. SendEvent(ev, e);
  263. }
  264. }
  265. private void SendKeyUpEvent(Event e)
  266. {
  267. using (var ev = KeyUpEvent.GetPooled('\0', e.keyCode, e.modifiers))
  268. {
  269. SendEvent(ev, e);
  270. }
  271. }
  272. private void SendKeyDownEvent(Event e)
  273. {
  274. using (var ev = KeyDownEvent.GetPooled(e.character, e.keyCode, e.modifiers))
  275. {
  276. SendEvent(ev, e);
  277. }
  278. }
  279. private bool ReadPointerData(PointerEvent pe, PointerEventData eventData, PointerEventType eventType = PointerEventType.Default)
  280. {
  281. if (eventSystem == null || eventSystem.currentInputModule == null)
  282. return false;
  283. pe.Read(this, eventData, eventType);
  284. // PointerEvents making it this far have been validated by PanelRaycaster already
  285. m_Panel.ScreenToPanel(pe.position, pe.deltaPosition,
  286. out var panelPosition, out var panelDelta, allowOutside:true);
  287. pe.SetPosition(panelPosition, panelDelta);
  288. return true;
  289. }
  290. enum PointerEventType
  291. {
  292. Default, Down, Up
  293. }
  294. class PointerEvent : IPointerEvent
  295. {
  296. public int pointerId { get; private set; }
  297. public string pointerType { get; private set; }
  298. public bool isPrimary { get; private set; }
  299. public int button { get; private set; }
  300. public int pressedButtons { get; private set; }
  301. public Vector3 position { get; private set; }
  302. public Vector3 localPosition { get; private set; }
  303. public Vector3 deltaPosition { get; private set; }
  304. public float deltaTime { get; private set; }
  305. public int clickCount { get; private set; }
  306. public float pressure { get; private set; }
  307. public float tangentialPressure { get; private set; }
  308. public float altitudeAngle { get; private set; }
  309. public float azimuthAngle { get; private set; }
  310. public float twist { get; private set; }
  311. public Vector2 radius { get; private set; }
  312. public Vector2 radiusVariance { get; private set; }
  313. public EventModifiers modifiers { get; private set; }
  314. public bool shiftKey => (modifiers & EventModifiers.Shift) != 0;
  315. public bool ctrlKey => (modifiers & EventModifiers.Control) != 0;
  316. public bool commandKey => (modifiers & EventModifiers.Command) != 0;
  317. public bool altKey => (modifiers & EventModifiers.Alt) != 0;
  318. public bool actionKey =>
  319. Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer
  320. ? commandKey
  321. : ctrlKey;
  322. public void Read(PanelEventHandler self, PointerEventData eventData, PointerEventType eventType)
  323. {
  324. pointerId = self.eventSystem.currentInputModule.ConvertUIToolkitPointerId(eventData);
  325. bool InRange(int i, int start, int count) => i >= start && i < start + count;
  326. pointerType =
  327. InRange(pointerId, PointerId.touchPointerIdBase, PointerId.touchPointerCount) ? PointerType.touch :
  328. InRange(pointerId, PointerId.penPointerIdBase, PointerId.penPointerCount) ? PointerType.pen :
  329. PointerType.mouse;
  330. isPrimary = pointerId == PointerId.mousePointerId ||
  331. pointerId == PointerId.touchPointerIdBase ||
  332. pointerId == PointerId.penPointerIdBase;
  333. button = (int)eventData.button;
  334. clickCount = eventData.clickCount;
  335. // Flip Y axis between input and UITK
  336. var h = Screen.height;
  337. var eventPosition = Display.RelativeMouseAt(eventData.position);
  338. if (eventPosition != Vector3.zero)
  339. {
  340. // We support multiple display and display identification based on event position.
  341. int eventDisplayIndex = (int)eventPosition.z;
  342. if (eventDisplayIndex > 0 && eventDisplayIndex < Display.displays.Length)
  343. h = Display.displays[eventDisplayIndex].systemHeight;
  344. }
  345. else
  346. {
  347. eventPosition = eventData.position;
  348. }
  349. var delta = eventData.delta;
  350. eventPosition.y = h - eventPosition.y;
  351. delta.y = -delta.y;
  352. localPosition = position = eventPosition;
  353. deltaPosition = delta;
  354. deltaTime = 0; //TODO: find out what's expected here. Time since last frame? Since last sent event?
  355. pressure = eventData.pressure;
  356. tangentialPressure = eventData.tangentialPressure;
  357. altitudeAngle = eventData.altitudeAngle;
  358. azimuthAngle = eventData.azimuthAngle;
  359. twist = eventData.twist;
  360. radius = eventData.radius;
  361. radiusVariance = eventData.radiusVariance;
  362. modifiers = s_Modifiers;
  363. if (eventType == PointerEventType.Default)
  364. {
  365. button = -1;
  366. clickCount = 0;
  367. }
  368. else
  369. {
  370. button = button >= 0 ? button : 0;
  371. clickCount = Mathf.Max(1, clickCount);
  372. if (eventType == PointerEventType.Down)
  373. PointerDeviceState.PressButton(pointerId, button);
  374. else if (eventType == PointerEventType.Up)
  375. PointerDeviceState.ReleaseButton(pointerId, button);
  376. }
  377. pressedButtons = PointerDeviceState.GetPressedButtons(pointerId);
  378. }
  379. public void SetPosition(Vector3 positionOverride, Vector3 deltaOverride)
  380. {
  381. localPosition = position = positionOverride;
  382. deltaPosition = deltaOverride;
  383. }
  384. }
  385. }
  386. #endif
  387. }