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