From be973ebaa22248e2fa983b0ba68b220e2834cacb Mon Sep 17 00:00:00 2001 From: "Dr.Rx" Date: Wed, 8 Jan 2020 18:40:18 -0500 Subject: [PATCH] Fix Pen barrel button support for rigth tapped on Android --- .../Uno/UI/UnoMotionHelper.java | 5 +- .../Input/PointerRoutedEventArgs.Android.cs | 64 ++++++++++++++----- .../UI/Input/PointerPointProperties.cs | 2 + 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/Uno.UI.BindingHelper.Android/Uno/UI/UnoMotionHelper.java b/src/Uno.UI.BindingHelper.Android/Uno/UI/UnoMotionHelper.java index b598187e16..e990ba59bc 100644 --- a/src/Uno.UI.BindingHelper.Android/Uno/UI/UnoMotionHelper.java +++ b/src/Uno.UI.BindingHelper.Android/Uno/UI/UnoMotionHelper.java @@ -76,7 +76,7 @@ import android.view.ViewParent; private View _currentMotionOriginalSource; // To trace the pointer events (dispatchTouchEvent and dispatchGenericMotionEvent), - // uncomment this and then uncomment logs in the method itself. + // uncomment this and then uncomment logs in the method itself (Replace all "// Log" by "Log"). /* private static String _indent = ""; public boolean dispatchMotionEvent(Uno.UI.MotionTargetAdapter adapter, MotionEvent event) @@ -454,6 +454,9 @@ import android.view.ViewParent; case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_HOVER_EXIT: + case 211: // Stylus down while holding the barrel button + case 213: // Stylus move while holding the barrel button + case 212: // Stylus up while holding the barrel button return true; default: return false; diff --git a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs index a86363d368..05a177fd9f 100644 --- a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs +++ b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs @@ -15,12 +15,28 @@ namespace Windows.UI.Xaml.Input { partial class PointerRoutedEventArgs { + /// + /// The stylus is pressed while holding the barrel button + /// + internal const MotionEventActions StylusWithBarrelDown = (MotionEventActions)211; + /// + /// The stylus is moved after having been pressed while holding the barrel button + /// + internal const MotionEventActions StylusWithBarrelMove = (MotionEventActions)213; + /// + /// The stylus is released after having been pressed while holding the barrel button + /// + internal const MotionEventActions StylusWithBarrelUp = (MotionEventActions)212; + private const int _pointerIdsCount = (int)MotionEventActions.PointerIndexMask >> (int)MotionEventActions.PointerIndexShift; // 0xff private const int _pointerIdsShift = 31 - (int)MotionEventActions.PointerIndexShift; // 23 private readonly MotionEvent _nativeEvent; private readonly int _pointerIndex; private readonly UIElement _receiver; + private readonly PointerPointProperties _properties; + + internal bool HasPressedButton => _properties.HasPressedButton; internal PointerRoutedEventArgs(MotionEvent nativeEvent, int pointerIndex, UIElement originalSource, UIElement receiver) : this() { @@ -36,26 +52,27 @@ namespace Windows.UI.Xaml.Input // Note: Make sure to use the GetPointerId in order to make sure to keep the same id while: down_1 / down_2 / up_1 / up_2 // otherwise up_2 will be with the id of 1 var pointerId = ((uint)nativeEvent.GetPointerId(pointerIndex) & _pointerIdsCount) << _pointerIdsShift | (uint)nativeEvent.DeviceId; - var type = nativeEvent.GetToolType(pointerIndex).ToPointerDeviceType(); - var isInContact = IsInContact(type, nativeEvent, pointerIndex); + var nativePointerType = nativeEvent.GetToolType(pointerIndex); + var pointerType = nativePointerType.ToPointerDeviceType(); + var isInContact = IsInContact(pointerType, nativeEvent, pointerIndex); var keys = nativeEvent.MetaState.ToVirtualKeyModifiers(); FrameId = (uint)_nativeEvent.EventTime; - Pointer = new Pointer(pointerId, type, isInContact, isInRange: true); + Pointer = new Pointer(pointerId, pointerType, isInContact, isInRange: true); KeyModifiers = keys; OriginalSource = originalSource; CanBubbleNatively = true; + + _properties = GetProperties(nativePointerType); // Last: we need the Pointer property to be set! } public PointerPoint GetCurrentPoint(UIElement relativeTo) { var timestamp = ToTimeStamp(_nativeEvent.EventTime); var device = PointerDevice.For(Pointer.PointerDeviceType); - var (rawPosition, position) = GetPositions(relativeTo); - var properties = GetProperties(); - return new PointerPoint(FrameId, timestamp, device, Pointer.PointerId, rawPosition, position, Pointer.IsInContact, properties); + return new PointerPoint(FrameId, timestamp, device, Pointer.PointerId, rawPosition, position, Pointer.IsInContact, _properties); } private (Point raw, Point relative) GetPositions(UIElement relativeTo) @@ -99,7 +116,7 @@ namespace Windows.UI.Xaml.Input return (raw, relative); } - private PointerPointProperties GetProperties() + private PointerPointProperties GetProperties(MotionEventToolType type) { var props = new PointerPointProperties { @@ -107,7 +124,6 @@ namespace Windows.UI.Xaml.Input IsInRange = Pointer.IsInRange }; - var type = _nativeEvent.GetToolType(_pointerIndex); var action = _nativeEvent.Action; var isDown = action.HasFlag(MotionEventActions.Down) || action.HasFlag(MotionEventActions.PointerDown); var isUp = action.HasFlag(MotionEventActions.Up) || action.HasFlag(MotionEventActions.PointerUp); @@ -118,19 +134,34 @@ namespace Windows.UI.Xaml.Input props.IsLeftButtonPressed = Pointer.IsInContact; updates = isDown ? _fingerDownUpdates : isUp ? _fingerUpUpdates : _none; break; + case MotionEventToolType.Mouse: props.IsLeftButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.Primary); props.IsMiddleButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.Tertiary); props.IsRightButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.Secondary); updates = isDown ? _mouseDownUpdates : isUp ? _mouseUpUpdates : _none; break; + + // Note: On UWP, if you touch screen while already holding the barrel button, you will get a right + barrel, + // ** BUT ** if you touch screen and THEN press the barrel button props will be left + barrel until released. + // On Android this distinction seems to be flagged by the "1101 ****" action flag (i.e. "StylusWithBarrel***" actions), + // so here we set the IsButtonPressed based on the action and we don't try to link it to the barrel button state. + case MotionEventToolType.Stylus when action == StylusWithBarrelDown: + case MotionEventToolType.Stylus when action == StylusWithBarrelMove: + case MotionEventToolType.Stylus when action == StylusWithBarrelUp: + // Note: We still validate the "IsButtonPressed(StylusPrimary)" as the user might release the button while pressed. + // In that case we will still receive moves and up with the "StylusWithBarrel***" actions. + props.IsBarrelButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.StylusPrimary); + props.IsRightButtonPressed = Pointer.IsInContact; + break; case MotionEventToolType.Stylus: props.IsBarrelButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.StylusPrimary); - props.IsLeftButtonPressed = Pointer.IsInContact && !props.IsBarrelButtonPressed; + props.IsLeftButtonPressed = Pointer.IsInContact; break; case MotionEventToolType.Eraser: props.IsEraser = true; break; + case MotionEventToolType.Unknown: // used by Xamarin.UITest props.IsLeftButtonPressed = true; break; @@ -198,14 +229,17 @@ namespace Windows.UI.Xaml.Input case PointerDeviceType.Mouse: return nativeEvent.ButtonState != 0; - case PointerDeviceType.Pen: - return nativeEvent.GetAxisValue(Axis.Distance, pointerIndex) == 0; - default: + case PointerDeviceType.Pen: case PointerDeviceType.Touch: - return nativeEvent.Action.HasFlag(MotionEventActions.Down) - || nativeEvent.Action.HasFlag(MotionEventActions.PointerDown) - || nativeEvent.Action.HasFlag(MotionEventActions.Move); + // WARNING: MotionEventActions.Down == 0, so action.HasFlag(MotionEventActions.Up) is always true! + var action = nativeEvent.Action; + return !action.HasFlag(MotionEventActions.Up) + && !action.HasFlag(MotionEventActions.PointerUp) + && !action.HasFlag(MotionEventActions.Cancel) + && !action.HasFlag(MotionEventActions.HoverEnter) + && !action.HasFlag(MotionEventActions.HoverMove) + && !action.HasFlag(MotionEventActions.HoverExit); } } #endregion diff --git a/src/Uno.UWP/UI/Input/PointerPointProperties.cs b/src/Uno.UWP/UI/Input/PointerPointProperties.cs index e8fec8957a..dc400c2f8d 100644 --- a/src/Uno.UWP/UI/Input/PointerPointProperties.cs +++ b/src/Uno.UWP/UI/Input/PointerPointProperties.cs @@ -8,6 +8,8 @@ namespace Windows.UI.Input { } + internal bool HasPressedButton => IsLeftButtonPressed || IsMiddleButtonPressed || IsRightButtonPressed || IsXButton1Pressed || IsXButton2Pressed || IsBarrelButtonPressed; + public bool IsPrimary { get; internal set; } public bool IsInRange { get; internal set; }