설명 없음
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

InputSystemProvider.cs 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. #if UNITY_2023_2_OR_NEWER // UnityEngine.InputForUI Module unavailable in earlier releases
  2. using System;
  3. using System.Collections.Generic;
  4. using Unity.IntegerTime;
  5. using UnityEngine.InputSystem.Controls;
  6. using UnityEngine.InputForUI;
  7. namespace UnityEngine.InputSystem.Plugins.InputForUI
  8. {
  9. using Event = UnityEngine.InputForUI.Event;
  10. using EventModifiers = UnityEngine.InputForUI.EventModifiers;
  11. using EventProvider = UnityEngine.InputForUI.EventProvider;
  12. internal class InputSystemProvider : IEventProviderImpl
  13. {
  14. Configuration m_Cfg;
  15. InputEventPartialProvider m_InputEventPartialProvider;
  16. InputActionAsset m_InputActionAsset;
  17. InputActionReference m_PointAction;
  18. InputActionReference m_MoveAction;
  19. InputActionReference m_SubmitAction;
  20. InputActionReference m_CancelAction;
  21. InputActionReference m_LeftClickAction;
  22. InputActionReference m_MiddleClickAction;
  23. InputActionReference m_RightClickAction;
  24. InputActionReference m_ScrollWheelAction;
  25. InputAction m_NextPreviousAction;
  26. List<Event> m_Events = new List<Event>();
  27. PointerState m_MouseState;
  28. PointerState m_PenState;
  29. bool m_SeenPenEvents;
  30. PointerState m_TouchState;
  31. bool m_SeenTouchEvents;
  32. const float k_SmallestReportedMovementSqrDist = 0.01f;
  33. NavigationEventRepeatHelper m_RepeatHelper = new();
  34. bool m_ResetSeenEventsOnUpdate;
  35. const float kScrollUGUIScaleFactor = 3.0f;
  36. static Action<InputActionAsset> s_OnRegisterActions;
  37. static InputSystemProvider()
  38. {
  39. // Only if InputSystem is enabled in the PlayerSettings do we set it as the provider.
  40. // This includes situations where both InputManager and InputSystem are enabled.
  41. #if ENABLE_INPUT_SYSTEM
  42. EventProvider.SetInputSystemProvider(new InputSystemProvider());
  43. #endif // ENABLE_INPUT_SYSTEM
  44. }
  45. [RuntimeInitializeOnLoadMethod(loadType: RuntimeInitializeLoadType.SubsystemRegistration)]
  46. static void Bootstrap() {} // Empty function. Exists only to invoke the static class constructor in Runtime Players
  47. EventModifiers m_EventModifiers => m_InputEventPartialProvider._eventModifiers;
  48. DiscreteTime m_CurrentTime => (DiscreteTime)Time.timeAsRational;
  49. const uint k_DefaultPlayerId = 0u;
  50. public void Initialize()
  51. {
  52. m_InputEventPartialProvider ??= new InputEventPartialProvider();
  53. m_InputEventPartialProvider.Initialize();
  54. m_Events.Clear();
  55. m_MouseState.Reset();
  56. m_PenState.Reset();
  57. m_SeenPenEvents = false;
  58. m_TouchState.Reset();
  59. m_SeenTouchEvents = false;
  60. m_Cfg = Configuration.GetDefaultConfiguration();
  61. RegisterActions();
  62. InputSystem.onActionsChange += OnActionsChange;
  63. }
  64. public void Shutdown()
  65. {
  66. UnregisterActions();
  67. m_InputEventPartialProvider.Shutdown();
  68. m_InputEventPartialProvider = null;
  69. InputSystem.onActionsChange -= OnActionsChange;
  70. }
  71. public void OnActionsChange()
  72. {
  73. UnregisterActions();
  74. m_Cfg = Configuration.GetDefaultConfiguration();
  75. RegisterActions();
  76. }
  77. public void Update()
  78. {
  79. #if UNITY_EDITOR
  80. // Ensure we are in a good (initialized) state before running updates.
  81. // This could be in a bad state for a duration while the build pipeline is running
  82. // when building tests to run in the Standalone Player.
  83. if (m_InputActionAsset == null)
  84. return;
  85. #endif
  86. m_InputEventPartialProvider.Update();
  87. // Sort events added by input actions callbacks, based on type.
  88. // This is necessary to ensure that events are dispatched in the correct order.
  89. // If all events are of the PointerEvents type, sorting is based on reverse order of the EventSource enum.
  90. // Touch -> Pen -> Mouse.
  91. m_Events.Sort(SortEvents);
  92. var currentTime = (DiscreteTime)Time.timeAsRational;
  93. DirectionNavigation(currentTime);
  94. foreach (var ev in m_Events)
  95. {
  96. // We need to ignore some pointer events based on priority (Touch->Pen->Mouse)
  97. // This is mostly used to filter out simulated input, e.g. when pen is active it also generates mouse input
  98. if (m_SeenTouchEvents && ev.type == Event.Type.PointerEvent && ev.eventSource == EventSource.Pen)
  99. m_PenState.Reset();
  100. else if ((m_SeenTouchEvents || m_SeenPenEvents) &&
  101. ev.type == Event.Type.PointerEvent && (ev.eventSource == EventSource.Mouse || ev.eventSource == EventSource.Unspecified))
  102. m_MouseState.Reset();
  103. else
  104. EventProvider.Dispatch(ev);
  105. }
  106. // Sometimes single lower priority events can be received when using Touch or Pen, on a different frame.
  107. // To avoid dispatching them, the seen event flags aren't reset in between calls to OnPointerPerformed.
  108. // Essentially, if we're moving with Touch or Pen, lower priority events aren't dispatch as well.
  109. // Once OnClickPerformed is called, the seen flags are reset
  110. if (m_ResetSeenEventsOnUpdate)
  111. {
  112. ResetSeenEvents();
  113. m_ResetSeenEventsOnUpdate = false;
  114. }
  115. m_Events.Clear();
  116. }
  117. void ResetSeenEvents()
  118. {
  119. m_SeenTouchEvents = false;
  120. m_SeenPenEvents = false;
  121. }
  122. public bool ActionAssetIsNotNull()
  123. {
  124. return m_InputActionAsset != null;
  125. }
  126. //TODO: Refactor as there is no need for having almost the same implementation in the IM and ISX?
  127. void DirectionNavigation(DiscreteTime currentTime)
  128. {
  129. var(move, axesButtonWerePressed) = ReadCurrentNavigationMoveVector();
  130. var direction = NavigationEvent.DetermineMoveDirection(move);
  131. // Checks for next/previous directions if no movement was detected
  132. if (direction == NavigationEvent.Direction.None)
  133. {
  134. direction = ReadNextPreviousDirection();
  135. axesButtonWerePressed = m_NextPreviousAction.WasPressedThisFrame();
  136. }
  137. if (direction == NavigationEvent.Direction.None)
  138. {
  139. m_RepeatHelper.Reset();
  140. }
  141. else
  142. {
  143. if (m_RepeatHelper.ShouldSendMoveEvent(currentTime, direction, axesButtonWerePressed))
  144. {
  145. EventProvider.Dispatch(Event.From(new NavigationEvent
  146. {
  147. type = NavigationEvent.Type.Move,
  148. direction = direction,
  149. timestamp = currentTime,
  150. eventSource = GetEventSource(GetActiveDeviceFromDirection(direction)),
  151. playerId = k_DefaultPlayerId,
  152. eventModifiers = m_EventModifiers
  153. }));
  154. }
  155. }
  156. }
  157. InputDevice GetActiveDeviceFromDirection(NavigationEvent.Direction direction)
  158. {
  159. switch (direction)
  160. {
  161. case NavigationEvent.Direction.Left:
  162. case NavigationEvent.Direction.Up:
  163. case NavigationEvent.Direction.Right:
  164. case NavigationEvent.Direction.Down:
  165. if (m_MoveAction != null)
  166. return m_MoveAction.action.activeControl.device;
  167. break;
  168. case NavigationEvent.Direction.Next:
  169. case NavigationEvent.Direction.Previous:
  170. if (m_NextPreviousAction != null)
  171. return m_NextPreviousAction.activeControl.device;
  172. break;
  173. case NavigationEvent.Direction.None:
  174. default:
  175. break;
  176. }
  177. return Keyboard.current;
  178. }
  179. (Vector2, bool) ReadCurrentNavigationMoveVector()
  180. {
  181. // In case action has not been configured we return defaults
  182. if (m_MoveAction == null)
  183. return (default, default);
  184. var move = m_MoveAction.action.ReadValue<Vector2>();
  185. // Check if the action was "pressed" this frame to deal with repeating events
  186. var axisWasPressed = m_MoveAction.action.WasPressedThisFrame();
  187. return (move, axisWasPressed);
  188. }
  189. NavigationEvent.Direction ReadNextPreviousDirection()
  190. {
  191. if (m_NextPreviousAction.IsPressed()) // Note: never null since created through code
  192. {
  193. //TODO: For now it only deals with Keyboard, needs to deal with other devices if we can add bindings
  194. // for Gamepad, etc
  195. //TODO: An alternative could be to have an action for next and for previous since shortcut support does
  196. // not work properly
  197. if (m_NextPreviousAction.activeControl.device is Keyboard)
  198. {
  199. var keyboard = m_NextPreviousAction.activeControl.device as Keyboard;
  200. // Return direction based on whether shift is pressed or not
  201. return keyboard.shiftKey.isPressed ? NavigationEvent.Direction.Previous : NavigationEvent.Direction.Next;
  202. }
  203. }
  204. return NavigationEvent.Direction.None;
  205. }
  206. static int SortEvents(Event a, Event b)
  207. {
  208. return Event.CompareType(a, b);
  209. }
  210. public void OnFocusChanged(bool focus)
  211. {
  212. m_InputEventPartialProvider.OnFocusChanged(focus);
  213. }
  214. public bool RequestCurrentState(Event.Type type)
  215. {
  216. if (m_InputEventPartialProvider.RequestCurrentState(type))
  217. return true;
  218. switch (type)
  219. {
  220. case Event.Type.PointerEvent:
  221. {
  222. if (m_TouchState.LastPositionValid)
  223. EventProvider.Dispatch(Event.From(ToPointerStateEvent(m_CurrentTime, m_TouchState, EventSource.Touch)));
  224. if (m_PenState.LastPositionValid)
  225. EventProvider.Dispatch(Event.From(ToPointerStateEvent(m_CurrentTime, m_PenState, EventSource.Pen)));
  226. if (m_MouseState.LastPositionValid)
  227. EventProvider.Dispatch(Event.From(ToPointerStateEvent(m_CurrentTime, m_MouseState, EventSource.Mouse)));
  228. else
  229. {
  230. // TODO maybe it's reasonable to poll and dispatch mouse state here anyway?
  231. }
  232. return m_TouchState.LastPositionValid ||
  233. m_PenState.LastPositionValid ||
  234. m_MouseState.LastPositionValid;
  235. }
  236. // TODO
  237. case Event.Type.IMECompositionEvent:
  238. default:
  239. return false;
  240. }
  241. }
  242. public uint playerCount => 1; // TODO
  243. // copied from UIElementsRuntimeUtility.cs
  244. static Vector2 ScreenBottomLeftToPanelPosition(Vector2 position, int targetDisplay)
  245. {
  246. // Flip positions Y axis between input and UITK
  247. var screenHeight = Screen.height;
  248. if (targetDisplay > 0 && targetDisplay < Display.displays.Length)
  249. screenHeight = Display.displays[targetDisplay].systemHeight;
  250. position.y = screenHeight - position.y;
  251. return position;
  252. }
  253. PointerEvent ToPointerStateEvent(DiscreteTime currentTime, in PointerState state, EventSource eventSource)
  254. {
  255. return new PointerEvent
  256. {
  257. type = PointerEvent.Type.State,
  258. pointerIndex = 0,
  259. position = state.LastPosition,
  260. deltaPosition = Vector2.zero,
  261. scroll = Vector2.zero,
  262. displayIndex = state.LastDisplayIndex,
  263. // TODO
  264. // tilt = eventSource == EventSource.Pen ? _lastPenData.tilt : Vector2.zero,
  265. // twist = eventSource == EventSource.Pen ? _lastPenData.twist : 0.0f,
  266. // pressure = eventSource == EventSource.Pen ? _lastPenData.pressure : 0.0f,
  267. // isInverted = eventSource == EventSource.Pen && ((_lastPenData.penStatus & PenStatus.Inverted) != 0),
  268. button = 0,
  269. buttonsState = state.ButtonsState,
  270. clickCount = 0,
  271. timestamp = currentTime,
  272. eventSource = eventSource,
  273. playerId = k_DefaultPlayerId,
  274. eventModifiers = m_EventModifiers
  275. };
  276. }
  277. EventSource GetEventSource(InputAction.CallbackContext ctx)
  278. {
  279. var device = ctx.control.device;
  280. return GetEventSource(device);
  281. }
  282. EventSource GetEventSource(InputDevice device)
  283. {
  284. if (device is Touchscreen)
  285. return EventSource.Touch;
  286. if (device is Pen)
  287. return EventSource.Pen;
  288. if (device is Mouse)
  289. return EventSource.Mouse;
  290. if (device is Keyboard)
  291. return EventSource.Keyboard;
  292. if (device is Gamepad)
  293. return EventSource.Gamepad;
  294. return EventSource.Unspecified;
  295. }
  296. ref PointerState GetPointerStateForSource(EventSource eventSource)
  297. {
  298. switch (eventSource)
  299. {
  300. case EventSource.Touch:
  301. return ref m_TouchState;
  302. case EventSource.Pen:
  303. return ref m_PenState;
  304. default:
  305. return ref m_MouseState;
  306. }
  307. }
  308. void DispatchFromCallback(in Event ev)
  309. {
  310. m_Events.Add(ev);
  311. }
  312. static int FindTouchFingerIndex(Touchscreen touchscreen, InputAction.CallbackContext ctx)
  313. {
  314. if (touchscreen == null)
  315. return 0;
  316. var asVector2Control = ctx.control is Vector2Control ? (Vector2Control)ctx.control : null;
  317. var asTouchPressControl = ctx.control is TouchPressControl ? (TouchPressControl)ctx.control : null;
  318. var asTouchControl = ctx.control is TouchControl ? (TouchControl)ctx.control : null;
  319. // Finds the index of the matching control type in the Touchscreen device lost of touch controls (touches)
  320. for (var i = 0; i < touchscreen.touches.Count; ++i)
  321. {
  322. if (asVector2Control != null && asVector2Control == touchscreen.touches[i].position)
  323. return i;
  324. if (asTouchPressControl != null && asTouchPressControl == touchscreen.touches[i].press)
  325. return i;
  326. if (asTouchControl != null && asTouchControl == touchscreen.touches[i])
  327. return i;
  328. }
  329. return 0;
  330. }
  331. void OnPointerPerformed(InputAction.CallbackContext ctx)
  332. {
  333. var eventSource = GetEventSource(ctx);
  334. ref var pointerState = ref GetPointerStateForSource(eventSource);
  335. // Overall I'm not happy how leaky this is, we're using input actions to have flexibility to bind to different controls,
  336. // but instead we just kinda abuse it to bind to different devices ...
  337. var asPointerDevice = ctx.control.device is Pointer ? (Pointer)ctx.control.device : null;
  338. var asPenDevice = ctx.control.device is Pen ? (Pen)ctx.control.device : null;
  339. var asTouchscreenDevice = ctx.control.device is Touchscreen ? (Touchscreen)ctx.control.device : null;
  340. var asTouchControl = ctx.control is TouchControl ? (TouchControl)ctx.control : null;
  341. var pointerIndex = FindTouchFingerIndex(asTouchscreenDevice, ctx);
  342. m_ResetSeenEventsOnUpdate = false;
  343. if (asTouchControl != null || asTouchscreenDevice != null)
  344. m_SeenTouchEvents = true;
  345. else if (asPenDevice != null)
  346. m_SeenPenEvents = true;
  347. var positionISX = ctx.ReadValue<Vector2>();
  348. var targetDisplay = asPointerDevice != null ? asPointerDevice.displayIndex.ReadValue() : (asTouchscreenDevice != null ? asTouchscreenDevice.displayIndex.ReadValue() : 0);
  349. var position = ScreenBottomLeftToPanelPosition(positionISX, targetDisplay);
  350. var delta = pointerState.LastPositionValid ? position - pointerState.LastPosition : Vector2.zero;
  351. var tilt = asPenDevice != null ? asPenDevice.tilt.ReadValue() : Vector2.zero;
  352. var twist = asPenDevice != null ? asPenDevice.twist.ReadValue() : 0.0f;
  353. var pressure = asPenDevice != null
  354. ? asPenDevice.pressure.ReadValue()
  355. : (asTouchControl != null ? asTouchControl.pressure.ReadValue() : 0.0f);
  356. var isInverted = asPenDevice != null
  357. ? asPenDevice.eraser.isPressed
  358. : false; // TODO any way to detect that pen is inverted but not touching?
  359. if (delta.sqrMagnitude >= k_SmallestReportedMovementSqrDist)
  360. {
  361. DispatchFromCallback(Event.From(new PointerEvent
  362. {
  363. type = PointerEvent.Type.PointerMoved,
  364. pointerIndex = pointerIndex,
  365. position = position,
  366. deltaPosition = delta,
  367. scroll = Vector2.zero,
  368. displayIndex = targetDisplay,
  369. tilt = tilt,
  370. twist = twist,
  371. pressure = pressure,
  372. isInverted = isInverted,
  373. button = 0,
  374. buttonsState = pointerState.ButtonsState,
  375. clickCount = 0,
  376. timestamp = m_CurrentTime,
  377. eventSource = eventSource,
  378. playerId = k_DefaultPlayerId,
  379. eventModifiers = m_EventModifiers
  380. }));
  381. // only record if we send an event
  382. pointerState.OnMove(m_CurrentTime, position, targetDisplay);
  383. }
  384. else if (!pointerState.LastPositionValid)
  385. pointerState.OnMove(m_CurrentTime, position, targetDisplay);
  386. }
  387. void OnSubmitPerformed(InputAction.CallbackContext ctx)
  388. {
  389. DispatchFromCallback(Event.From(new NavigationEvent
  390. {
  391. type = NavigationEvent.Type.Submit,
  392. direction = NavigationEvent.Direction.None,
  393. timestamp = m_CurrentTime,
  394. eventSource = GetEventSource(ctx),
  395. playerId = k_DefaultPlayerId,
  396. eventModifiers = m_EventModifiers
  397. }));
  398. }
  399. void OnCancelPerformed(InputAction.CallbackContext ctx)
  400. {
  401. DispatchFromCallback(Event.From(new NavigationEvent
  402. {
  403. type = NavigationEvent.Type.Cancel,
  404. direction = NavigationEvent.Direction.None,
  405. timestamp = m_CurrentTime,
  406. eventSource = GetEventSource(ctx),
  407. playerId = k_DefaultPlayerId,
  408. eventModifiers = m_EventModifiers
  409. }));
  410. }
  411. void OnClickPerformed(InputAction.CallbackContext ctx, EventSource eventSource, PointerEvent.Button button)
  412. {
  413. ref var state = ref GetPointerStateForSource(eventSource);
  414. var asTouchscreenDevice = ctx.control.device is Touchscreen ? (Touchscreen)ctx.control.device : null;
  415. var asTouchControl = ctx.control is TouchControl ? (TouchControl)ctx.control : null;
  416. var pointerIndex = FindTouchFingerIndex(asTouchscreenDevice, ctx);
  417. m_ResetSeenEventsOnUpdate = true;
  418. if (asTouchControl != null || asTouchscreenDevice != null)
  419. m_SeenTouchEvents = true;
  420. var wasPressed = state.ButtonsState.Get(button);
  421. var isPressed = ctx.ReadValueAsButton();
  422. state.OnButtonChange(m_CurrentTime, button, wasPressed, isPressed);
  423. DispatchFromCallback(Event.From(new PointerEvent
  424. {
  425. type = isPressed ? PointerEvent.Type.ButtonPressed : PointerEvent.Type.ButtonReleased,
  426. pointerIndex = pointerIndex,
  427. position = state.LastPosition,
  428. deltaPosition = Vector2.zero,
  429. scroll = Vector2.zero,
  430. displayIndex = state.LastDisplayIndex,
  431. tilt = Vector2.zero,
  432. twist = 0.0f,
  433. pressure = 0.0f,
  434. isInverted = false,
  435. button = button,
  436. buttonsState = state.ButtonsState,
  437. clickCount = state.ClickCount,
  438. timestamp = m_CurrentTime,
  439. eventSource = eventSource,
  440. playerId = k_DefaultPlayerId,
  441. eventModifiers = m_EventModifiers
  442. }));
  443. }
  444. void OnLeftClickPerformed(InputAction.CallbackContext ctx) => OnClickPerformed(ctx, GetEventSource(ctx), PointerEvent.Button.MouseLeft);
  445. void OnMiddleClickPerformed(InputAction.CallbackContext ctx) => OnClickPerformed(ctx, GetEventSource(ctx), PointerEvent.Button.MouseMiddle);
  446. void OnRightClickPerformed(InputAction.CallbackContext ctx) => OnClickPerformed(ctx, GetEventSource(ctx), PointerEvent.Button.MouseRight);
  447. void OnScrollWheelPerformed(InputAction.CallbackContext ctx)
  448. {
  449. var scrollDelta = ctx.ReadValue<Vector2>();
  450. if (scrollDelta.sqrMagnitude < k_SmallestReportedMovementSqrDist)
  451. return;
  452. var eventSource = GetEventSource(ctx);
  453. ref var state = ref GetPointerStateForSource(eventSource);
  454. var position = Vector2.zero;
  455. var targetDisplay = 0;
  456. if (state.LastPositionValid)
  457. {
  458. position = state.LastPosition;
  459. targetDisplay = state.LastDisplayIndex;
  460. }
  461. else if (eventSource == EventSource.Mouse && Mouse.current != null)
  462. {
  463. position = Mouse.current.position.ReadValue();
  464. targetDisplay = Mouse.current.displayIndex.ReadValue();
  465. }
  466. // Make it look similar to IMGUI event scroll values.
  467. scrollDelta.x *= kScrollUGUIScaleFactor;
  468. scrollDelta.y *= -kScrollUGUIScaleFactor;
  469. DispatchFromCallback(Event.From(new PointerEvent
  470. {
  471. type = PointerEvent.Type.Scroll,
  472. pointerIndex = 0,
  473. position = position,
  474. deltaPosition = Vector2.zero,
  475. scroll = scrollDelta,
  476. displayIndex = targetDisplay,
  477. tilt = Vector2.zero,
  478. twist = 0.0f,
  479. pressure = 0.0f,
  480. isInverted = false,
  481. button = 0,
  482. buttonsState = state.ButtonsState,
  483. clickCount = 0,
  484. timestamp = m_CurrentTime,
  485. eventSource = EventSource.Mouse,
  486. playerId = k_DefaultPlayerId,
  487. eventModifiers = m_EventModifiers
  488. }));
  489. }
  490. void RegisterNextPreviousAction()
  491. {
  492. m_NextPreviousAction = new InputAction(name: "nextPreviousAction", type: InputActionType.Button);
  493. // TODO add more default bindings, or make them configurable
  494. m_NextPreviousAction.AddBinding("<Keyboard>/tab");
  495. m_NextPreviousAction.Enable();
  496. }
  497. void UnregisterFixedActions()
  498. {
  499. // The Next/Previous action is not part of the input actions asset
  500. if (m_NextPreviousAction != null)
  501. {
  502. m_NextPreviousAction.Disable();
  503. m_NextPreviousAction = null;
  504. }
  505. }
  506. void RegisterActions()
  507. {
  508. m_InputActionAsset = m_Cfg.ActionAsset;
  509. // Invoke potential lister observing registration
  510. s_OnRegisterActions?.Invoke(m_InputActionAsset);
  511. m_PointAction = InputActionReference.Create(m_InputActionAsset.FindAction(m_Cfg.PointAction));
  512. m_MoveAction = InputActionReference.Create(m_InputActionAsset.FindAction(m_Cfg.MoveAction));
  513. m_SubmitAction = InputActionReference.Create(m_InputActionAsset.FindAction(m_Cfg.SubmitAction));
  514. m_CancelAction = InputActionReference.Create(m_InputActionAsset.FindAction(m_Cfg.CancelAction));
  515. m_LeftClickAction = InputActionReference.Create(m_InputActionAsset.FindAction(m_Cfg.LeftClickAction));
  516. m_MiddleClickAction = InputActionReference.Create(m_InputActionAsset.FindAction(m_Cfg.MiddleClickAction));
  517. m_RightClickAction = InputActionReference.Create(m_InputActionAsset.FindAction(m_Cfg.RightClickAction));
  518. m_ScrollWheelAction = InputActionReference.Create(m_InputActionAsset.FindAction(m_Cfg.ScrollWheelAction));
  519. if (m_PointAction != null && m_PointAction.action != null)
  520. m_PointAction.action.performed += OnPointerPerformed;
  521. if (m_SubmitAction != null && m_SubmitAction.action != null)
  522. m_SubmitAction.action.performed += OnSubmitPerformed;
  523. if (m_CancelAction != null && m_CancelAction.action != null)
  524. m_CancelAction.action.performed += OnCancelPerformed;
  525. if (m_LeftClickAction != null && m_LeftClickAction.action != null)
  526. m_LeftClickAction.action.performed += OnLeftClickPerformed;
  527. if (m_MiddleClickAction != null && m_MiddleClickAction.action != null)
  528. m_MiddleClickAction.action.performed += OnMiddleClickPerformed;
  529. if (m_RightClickAction != null && m_RightClickAction.action != null)
  530. m_RightClickAction.action.performed += OnRightClickPerformed;
  531. if (m_ScrollWheelAction != null && m_ScrollWheelAction.action != null)
  532. m_ScrollWheelAction.action.performed += OnScrollWheelPerformed;
  533. // When adding new actions, don't forget to add them to UnregisterActions
  534. if (InputSystem.actions == null)
  535. {
  536. // If we've not loaded a user-created set of actions, just enable the UI actions from our defaults.
  537. m_InputActionAsset.FindActionMap("UI", true).Enable();
  538. }
  539. else
  540. m_InputActionAsset.Enable();
  541. // TODO make it configurable as it is not part of default config
  542. // The Next/Previous action is not part of the input actions asset
  543. RegisterNextPreviousAction();
  544. }
  545. void UnregisterActions()
  546. {
  547. if (m_PointAction != null && m_PointAction.action != null)
  548. m_PointAction.action.performed -= OnPointerPerformed;
  549. if (m_SubmitAction != null && m_SubmitAction.action != null)
  550. m_SubmitAction.action.performed -= OnSubmitPerformed;
  551. if (m_CancelAction != null && m_CancelAction.action != null)
  552. m_CancelAction.action.performed -= OnCancelPerformed;
  553. if (m_LeftClickAction != null && m_LeftClickAction.action != null)
  554. m_LeftClickAction.action.performed -= OnLeftClickPerformed;
  555. if (m_MiddleClickAction != null && m_MiddleClickAction.action != null)
  556. m_MiddleClickAction.action.performed -= OnMiddleClickPerformed;
  557. if (m_RightClickAction != null && m_RightClickAction.action != null)
  558. m_RightClickAction.action.performed -= OnRightClickPerformed;
  559. if (m_ScrollWheelAction != null && m_ScrollWheelAction.action != null)
  560. m_ScrollWheelAction.action.performed -= OnScrollWheelPerformed;
  561. m_PointAction = null;
  562. m_MoveAction = null;
  563. m_SubmitAction = null;
  564. m_CancelAction = null;
  565. m_LeftClickAction = null;
  566. m_MiddleClickAction = null;
  567. m_RightClickAction = null;
  568. m_ScrollWheelAction = null;
  569. if (m_InputActionAsset != null)
  570. m_InputActionAsset.Disable();
  571. UnregisterFixedActions();
  572. }
  573. public struct Configuration
  574. {
  575. public InputActionAsset ActionAsset;
  576. public string PointAction;
  577. public string MoveAction;
  578. public string SubmitAction;
  579. public string CancelAction;
  580. public string LeftClickAction;
  581. public string MiddleClickAction;
  582. public string RightClickAction;
  583. public string ScrollWheelAction;
  584. public static Configuration GetDefaultConfiguration()
  585. {
  586. // Only use default actions asset configuration if (ISX-1954):
  587. // - Project-wide Input Actions have not been configured, OR
  588. // - Project-wide Input Actions have been configured but contains no UI action map.
  589. var projectWideInputActions = InputSystem.actions;
  590. var useProjectWideInputActions =
  591. projectWideInputActions != null &&
  592. projectWideInputActions.FindActionMap("UI") != null;
  593. // Use InputSystem.actions (Project-wide Actions) if available, else use default asset if
  594. // user didn't specifically set one, so that UI functions still work (ISXB-811).
  595. return new Configuration
  596. {
  597. ActionAsset = useProjectWideInputActions ? InputSystem.actions : new DefaultInputActions().asset,
  598. PointAction = "UI/Point",
  599. MoveAction = "UI/Navigate",
  600. SubmitAction = "UI/Submit",
  601. CancelAction = "UI/Cancel",
  602. LeftClickAction = "UI/Click",
  603. MiddleClickAction = "UI/MiddleClick",
  604. RightClickAction = "UI/RightClick",
  605. ScrollWheelAction = "UI/ScrollWheel",
  606. };
  607. }
  608. }
  609. internal static void SetOnRegisterActions(Action<InputActionAsset> callback)
  610. {
  611. s_OnRegisterActions = callback;
  612. }
  613. }
  614. }
  615. #endif // UNITY_2023_2_OR_NEWER