diff --git a/Assets/Tests/InputSystem/Plugins/UITests.cs b/Assets/Tests/InputSystem/Plugins/UITests.cs index cc9d066098..bd026bc480 100644 --- a/Assets/Tests/InputSystem/Plugins/UITests.cs +++ b/Assets/Tests/InputSystem/Plugins/UITests.cs @@ -1460,33 +1460,73 @@ public IEnumerator UI_CanDriveUIFromMultiplePointers(UIPointerBehavior pointerBe scene.leftChildReceiver.events.Clear(); scene.rightChildReceiver.events.Clear(); - // Test if creating Pointer events from different devices at the same time results in only one event - BeginTouch(0, firstPosition, screen: touch1, queueEventOnly: true); + // End previous touches that started so that we can do a cleanup from the last test. + EndTouch(1, secondPosition, screen: touch1); + yield return null; + EndTouch(1, firstPosition, screen: touch2); + yield return null; + // Set a mouse position without any clicks to "emulate" a real movement before a button press. + Set(mouse1.position, secondPosition + new Vector2(-10, 0)); + yield return null; + + scene.leftChildReceiver.events.Clear(); + scene.rightChildReceiver.events.Clear(); + + // Test a press and release from both a Mouse and Touchscreen at the same time + // This is to simulate some platforms that always send Mouse/Pen and Touches (e.g. Android). + // Also, this mostly assets the expected behavior for the options SingleMouseOrPenButMultiTouchAndTrack. + var touchId = 2; + BeginTouch(touchId, secondPosition, screen: touch1, queueEventOnly: true); + Set(mouse1.position, secondPosition, queueEventOnly: true); Press(mouse1.leftButton); + yield return null; - EndTouch(0, firstPosition, screen: touch1, queueEventOnly: true); + + EndTouch(touchId, secondPosition, screen: touch1, queueEventOnly: true); Release(mouse1.leftButton); yield return null; + Func eventDeviceCondition = null; + var expectedCount = 0; switch (pointerBehavior) { + case UIPointerBehavior.SingleMouseOrPenButMultiTouchAndTrack: + // Expects only mouse events for PointerClick, PointerDown, and PointerUp + eventDeviceCondition = (e) => e.pointerData.device == mouse1; + expectedCount = 1; + // Make sure that the touch does not generate a UI events. + Assert.That(scene.rightChildReceiver.events, Has.None.Matches((UICallbackReceiver.Event e) => + e.pointerData != null && e.pointerData.device == touch1)); + break; + case UIPointerBehavior.SingleUnifiedPointer: - //// Getting "Drop" event even if using only one type of input device for Press/Release. - //// E.g. the following test would also produce only a Drop event: - //// Press(mouse1.leftButton); - //// yield return null; - //// Release(mouse1.leftButton); - //// yield return null; + // Expects only single UI events with touch source since they are the first events in the queue + eventDeviceCondition = (e) => e.pointerData.device == touch1; + expectedCount = 1; break; - case UIPointerBehavior.SingleMouseOrPenButMultiTouchAndTrack: + case UIPointerBehavior.AllPointersAsIs: - // Single pointer click on the left object - Assert.That(scene.leftChildReceiver.events, - Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerClick).And - .Matches((UICallbackReceiver.Event e) => e.pointerData.device == mouse1).And - .Matches((UICallbackReceiver.Event e) => e.pointerData.position == firstPosition)); + // Expects both pointer devices to generate PointerClick, PointerDown, and PointerUp events + eventDeviceCondition = (e) => e.pointerData.device == mouse1 || e.pointerData.device == touch1; + expectedCount = 2; break; + + default: + throw new ArgumentOutOfRangeException(nameof(pointerBehavior), pointerBehavior, null); } + + Assert.That(scene.rightChildReceiver.events, + Has.Exactly(expectedCount).With.Property("type").EqualTo(EventType.PointerClick).And + .Matches((UICallbackReceiver.Event e) => eventDeviceCondition(e)).And + .Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition)); + Assert.That(scene.rightChildReceiver.events, + Has.Exactly(expectedCount).With.Property("type").EqualTo(EventType.PointerDown).And + .Matches((UICallbackReceiver.Event e) => eventDeviceCondition(e)).And + .Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition)); + Assert.That(scene.rightChildReceiver.events, + Has.Exactly(expectedCount).With.Property("type").EqualTo(EventType.PointerUp).And + .Matches((UICallbackReceiver.Event e) => eventDeviceCondition(e)).And + .Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition)); } [UnityTest] diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs index 2ce58a635a..375f2546c9 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs @@ -2072,13 +2072,13 @@ private bool RemovePointerAtIndex(int index) { Debug.Assert(m_PointerStates[index].eventData.pointerEnter == null, "Pointer should have exited all objects before being removed"); - // We don't want to release touch pointers on the same frame they are released (unpressed). They get cleaned up one frame later in Process() - ref var state = ref GetPointerStateForIndex(index); - if (state.pointerType == UIPointerType.Touch && (state.leftButton.isPressed || state.leftButton.wasReleasedThisFrame)) - { - // The pointer was not removed - return false; - } + // // We don't want to release touch pointers on the same frame they are released (unpressed). They get cleaned up one frame later in Process() + // ref var state = ref GetPointerStateForIndex(index); + // if (state.pointerType == UIPointerType.Touch && (state.leftButton.isPressed || state.leftButton.wasReleasedThisFrame)) + // { + // // The pointer was not removed + // return false; + // } // Retain event data so that we can reuse the event the next time we allocate a PointerModel record. var eventData = m_PointerStates[index].eventData; @@ -2350,13 +2350,19 @@ private void FilterPointerStatesByType() // We have input on a mouse or pen. Kill all touch and tracked pointers we may have. for (var i = 0; i < m_PointerStates.length; ++i) { - ref var state = ref GetPointerStateForIndex(i); - // Touch pointers need to get forced to no longer be pressed otherwise they will not get released in subsequent frames. - if (m_PointerStates[i].pointerType == UIPointerType.Touch) - { - state.leftButton.isPressed = false; - } - if (m_PointerStates[i].pointerType != UIPointerType.MouseOrPen && m_PointerStates[i].pointerType != UIPointerType.Touch || (m_PointerStates[i].pointerType == UIPointerType.Touch && !state.leftButton.isPressed && !state.leftButton.wasReleasedThisFrame)) + // ref var state = ref GetPointerStateForIndex(i); + // // Touch pointers need to get forced to no longer be pressed otherwise they will not get released in subsequent frames. + // if (m_PointerStates[i].pointerType == UIPointerType.Touch) + // { + // state.leftButton.isPressed = false; + // } + // if (m_PointerStates[i].pointerType != UIPointerType.MouseOrPen && m_PointerStates[i].pointerType != UIPointerType.Touch || (m_PointerStates[i].pointerType == UIPointerType.Touch && !state.leftButton.isPressed && !state.leftButton.wasReleasedThisFrame)) + // { + // if (SendPointerExitEventsAndRemovePointer(i)) + // --i; + // } + + if (m_PointerStates[i].pointerType != UIPointerType.MouseOrPen) { if (SendPointerExitEventsAndRemovePointer(i)) --i;