Esempio n. 1
0
        internal static void ProcessPointerUp(PointerRoutedEventArgs args, bool isAfterHandledUp = false)
        {
            // We don't want handled events raised on RootVisual,
            // instead we wait for the element that handled it to directly forward it to us,
            // but only ** AFTER ** the up has been fully processed (with isAfterHandledUp = true).
            // This is required to be sure that element process gestures and manipulations before we raise the exit
            // (e.g. the 'tapped' event on a Button would be fired after the 'exit').
            var isHandled           = args.Handled;   // Capture here as args might be reset before checked for focus
            var isUpFullyDispatched = isAfterHandledUp || !isHandled;

            if (!isUpFullyDispatched)
            {
                return;
            }

#if __ANDROID__ || __WASM__ || __IOS__
#if __ANDROID__ || __IOS__ // Not needed on WASM as we do have native support of the exit event
                           // On Android and iOS we use the RootVisual to raise the UWP only exit event (in managed only)

            if (args.Pointer.PointerDeviceType is PointerDeviceType.Touch &&
                args.OriginalSource is UIElement src)
            {
                // It's acceptable to use only the OriginalSource on Android and iOS:
                // since those platforms have "implicit capture" and captures are propagated to the OS,
                // the OriginalSource will be the element that has capture (if any).

                src.RedispatchPointerExited(args.Reset(canBubbleNatively: false));
            }
#endif

            // Uno specific: To ensure focus is properly lost when clicking "outside" app's content,
            // we set focus here. In case UWP, focus is set to the root ScrollViewer instead,
            // but Uno does not have it on all targets yet.
            if (!isHandled &&          // so isAfterHandledUp is false!
                args.GetCurrentPoint(null).Properties.PointerUpdateKind is PointerUpdateKind.LeftButtonReleased &&
                !PointerCapture.TryGet(args.Pointer, out _) &&
                FocusManager.GetFocusedElement() is UIElement uiElement)
            {
                uiElement.Unfocus();
                args.Handled = true;
            }

            ReleaseCaptures(args.Reset(canBubbleNatively: false));
#endif
        }
        private bool OnNativeMotionEvent(MotionEvent nativeEvent, PointerRoutedEventArgs args, MotionEventActions action, bool isInView)
        {
            // Warning: MotionEvent of other kinds are filtered out in native code (UnoMotionHelper.java)
            switch (action)
            {
            case MotionEventActions.HoverEnter:
                return(OnNativePointerEnter(args));

            case MotionEventActions.HoverExit when !args.Pointer.IsInContact:
                // When a mouse button is pressed or pen touches the screen (a.k.a. becomes in contact), we receive an HoverExit before the Down.
                // We validate here if pointer 'isInContact' (which is the case for HoverExit when mouse button pressed / pen touched the screen)
                // and we ignore them (as on UWP Exit is raised only when pointer moves out of bounds of the control, no matter the pressed state).
                // As a side effect we will have to update the hover state on each Move in order to handle the case of press -> move out -> release.
                return(OnNativePointerExited(args));

            case MotionEventActions.HoverExit:
                return(false);                        // avoid useless logging

            case MotionEventActions.Down when args.Pointer.PointerDeviceType == PointerDeviceType.Touch:
            case MotionEventActions.PointerDown when args.Pointer.PointerDeviceType == PointerDeviceType.Touch:
                // We don't have any enter / exit on Android for touches, so we explicitly generate one on down / up.
                // That event args is requested to bubble in managed code only (args.CanBubbleNatively = false),
                // so we follow the same sequence as UWP (the whole tree gets entered before the pressed),
                // and we make sure that the event will bubble through the whole tree, no matter if the Pressed event is handle or not.
                // Note: Parents will also try to raise the "Enter" but they will be silent since the pointer is already considered as pressed.
                args.CanBubbleNatively = false;
                OnNativePointerEnter(args);
                return(OnNativePointerDown(args.Reset()));

            case PointerRoutedEventArgs.StylusWithBarrelDown:
            case MotionEventActions.Down:
            case MotionEventActions.PointerDown:
                return(OnNativePointerDown(args));

            case MotionEventActions.Up when args.Pointer.PointerDeviceType == PointerDeviceType.Touch:
            case MotionEventActions.PointerUp when args.Pointer.PointerDeviceType == PointerDeviceType.Touch:
                // For touch pointer, in the RootVisual we will redispatch this event to raise exit,
                // but if the event has been handled, we need to raise it after the 'up' has been processed.
                if (OnNativePointerUp(args))
                {
                    Uno.UI.Xaml.Core.RootVisual.ProcessPointerUp(args, isAfterHandledUp: true);
                    return(true);
                }
                else
                {
                    return(false);
                }

            case PointerRoutedEventArgs.StylusWithBarrelUp:
            case MotionEventActions.Up:
            case MotionEventActions.PointerUp:
                return(OnNativePointerUp(args));

            // We get ACTION_DOWN and ACTION_UP only for "left" button, and instead we get a HOVER_MOVE when pressing/releasing the right button of the mouse.
            // So on each POINTER_MOVE we make sure to update the pressed state if it does not match.
            // Note: We can also have HOVER_MOVE with barrel button pressed, so we make sure to "PointerDown" only for Mouse.
            case MotionEventActions.HoverMove when args.Pointer.PointerDeviceType == PointerDeviceType.Mouse && args.HasPressedButton && !IsPressed(args.Pointer):
                return(OnNativePointerDown(args) | OnNativePointerMoveWithOverCheck(args.Reset(), isInView));

            case MotionEventActions.HoverMove when !args.HasPressedButton && IsPressed(args.Pointer):
                return(OnNativePointerUp(args) | OnNativePointerMoveWithOverCheck(args.Reset(), isInView));

            case PointerRoutedEventArgs.StylusWithBarrelMove:
            case MotionEventActions.Move:
            case MotionEventActions.HoverMove:
                // Note: We use the OnNativePointerMove**WithOverCheck** in order to update the over state in case of press -> move out -> release
                //		 where Android won't raise the HoverExit (as it has raised it on press, but we have ignored it cf. HoverExit case.)
                return(OnNativePointerMoveWithOverCheck(args, isInView));

            case MotionEventActions.Cancel:
                return(OnNativePointerCancel(args, isSwallowedBySystem: true));

            default:
                if (this.Log().IsEnabled(LogLevel.Warning))
                {
                    this.Log().Warn($"We receive a native motion event of '{action}', but this is not supported and should have been filtered out in native code.");
                }

                return(false);
            }
        }
 /// <summary>
 /// Used by the VisualRoot to redispatch a pointer exit on pointer up
 /// </summary>
 /// <param name="args"></param>
 internal void RedispatchPointerExited(PointerRoutedEventArgs args)
 => OnNativePointerExited(args.Reset(canBubbleNatively: false));
Esempio n. 4
0
        public override void TouchesBegan(NSSet touches, UIEvent evt)
        {
            if (!ArePointersEnabled)
            {
                return;                 // Will also prevent subsequents events
            }

            try
            {
                if (ManipulationMode == ManipulationModes.None)
                {
                    // If manipulation mode is None, we make sure to disable scrollers directly on pointer pressed
                    NotifyParentTouchesManagersManipulationStarted();
                }

                var isHandledOrBubblingInManaged = default(bool);
                foreach (UITouch touch in touches)
                {
                    var pt   = TransientNativePointer.Get(this, touch);
                    var args = new PointerRoutedEventArgs(pt.Id, touch, evt, this);

                    // We set the DownArgs only for the top most element (a.k.a. OriginalSource)
                    pt.DownArgs ??= args;

                    if (pt.LastManagedOnlyFrameId >= args.FrameId)
                    {
                        continue;
                    }

                    // We don't have any enter on iOS for touches, so we explicitly generate one on down.
                    // That event args is requested to bubble in managed code only (args.CanBubbleNatively = false),
                    // so we follow the same sequence as UWP (the whole tree gets entered before the pressed),
                    // and we make sure that the event will bubble through the whole tree, no matter if the Pressed event is handle or not.
                    // Note: Parents will also try to raise the "Enter" but they will be silent since the pointer is already considered as pressed.
                    args.CanBubbleNatively = false;
                    OnNativePointerEnter(args);

                    isHandledOrBubblingInManaged |= OnNativePointerDown(args.Reset());

                    if (isHandledOrBubblingInManaged)
                    {
                        pt.LastManagedOnlyFrameId = args.FrameId;
                    }
                }

                /*
                 * If we do not propagate the "TouchesBegan" to the parents (if isHandledOrBubblingInManaged),
                 * they won't receive the "TouchesMoved" nor the "TouchesEnded".
                 *
                 * It means that if a control (like the Button) handles the "Pressed" (or the "Entered")
                 * parent won't receive any touch event.
                 *
                 * To avoid that, we never prevent the base.TouchesBegan, but instead we keep track of the FrameId,
                 * and then in parents control filter out events that was already raised in managed.
                 */

                // Continue native bubbling up of the event
                base.TouchesBegan(touches, evt);
            }
            catch (Exception e)
            {
                Application.Current.RaiseRecoverableUnhandledException(e);
            }
        }