public override void TouchesCancelled(NSSet touches, UIEvent evt) { try { var isHandledOrBubblingInManaged = default(bool); foreach (UITouch touch in touches) { var pt = TransientNativePointer.Get(this, touch); var args = new PointerRoutedEventArgs(pt.Id, touch, evt, this); // Note: We should have raise either PointerCaptureLost or PointerCancelled here depending of the reason which // drives the system to bubble a lost. However we don't have this kind of information on iOS, and it's // usually due to the ScrollView which kicks in. So we always raise the CaptureLost which is the behavior // on UWP when scroll starts (even if no capture are actives at this time). isHandledOrBubblingInManaged |= OnNativePointerCancel(args, isSwallowedBySystem: true); pt.Release(this); } if (!isHandledOrBubblingInManaged) { // Continue native bubbling up of the event base.TouchesCancelled(touches, evt); } NotifyParentTouchesManagersManipulationEnded(); } catch (Exception e) { Application.Current.RaiseRecoverableUnhandledException(e); } }
public override void TouchesMoved(NSSet touches, UIEvent evt) { try { var isHandledOrBubblingInManaged = default(bool); foreach (UITouch touch in touches) { var pt = TransientNativePointer.Get(this, touch); var args = new PointerRoutedEventArgs(pt.Id, touch, evt, this); var isPointerOver = touch.IsTouchInView(this); // This is acceptable to keep that flag in a kind-of static way, since iOS do "implicit captures", // a potential move will be dispatched to all elements "registered" on this "TransientNativePointer". pt.HadMove = true; // As we don't have enter/exit equivalents on iOS, we have to update the IsOver on each move // Note: Entered / Exited are raised *before* the Move (Checked using the args timestamp) isHandledOrBubblingInManaged |= OnNativePointerMoveWithOverCheck(args, isPointerOver); } if (!isHandledOrBubblingInManaged) { // Continue native bubbling up of the event base.TouchesMoved(touches, evt); } } catch (Exception e) { Application.Current.RaiseRecoverableUnhandledException(e); } }
public static TransientNativePointer Get(UIElement element, UITouch touch) { if (!_instances.TryGetValue(touch.Handle, out var id)) { _instances[touch.Handle] = id = new TransientNativePointer(touch.Handle); } id._leases.Add(element); return(id); }
public override void TouchesEnded(NSSet touches, UIEvent evt) { try { var isHandledOrBubblingInManaged = default(bool); foreach (UITouch touch in touches) { var pt = TransientNativePointer.Get(this, touch); var args = new PointerRoutedEventArgs(pt.Id, touch, evt, this); if (!pt.HadMove) { // The event will bubble in managed, so as this flag is "pseudo static", make sure to raise it only once. pt.HadMove = true; // On iOS if the gesture is really fast (like a flick), we can get only 'down' and 'up'. // But on UWP it seems that we always have a least one move (for fingers and pen!), and even internally, // the manipulation events are requiring at least one move to kick-in. // Here we are just making sure to raise that event with the final location. // Note: In case of multi-touch we might raise it unnecessarily, but it won't have any negative impact. // Note: We do not consider the result of that move for the 'isHandledOrBubblingInManaged' // as it's kind of un-related to the 'up' itself. var mixedArgs = new PointerRoutedEventArgs(previous: pt.DownArgs, current: args); OnNativePointerMove(mixedArgs); } isHandledOrBubblingInManaged |= OnNativePointerUp(args); isHandledOrBubblingInManaged |= OnNativePointerExited(args); pt.Release(this); } if (!isHandledOrBubblingInManaged) { // Continue native bubbling up of the event base.TouchesEnded(touches, evt); } NotifyParentTouchesManagersManipulationEnded(); } catch (Exception e) { Application.Current.RaiseRecoverableUnhandledException(e); } }
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); } }
public override void TouchesEnded(NSSet touches, UIEvent evt) { /* Note: Here we have a mismatching behavior with UWP, if the events bubble natively we're going to get * (with Ctrl_02 is a child of Ctrl_01): * Ctrl_02: Released * Exited * Ctrl_01: Released * Exited * * While on UWP we will get: * Ctrl_02: Released * Ctrl_01: Released * Ctrl_02: Exited * Ctrl_01: Exited * * However, to fix this is would mean that we handle all events in managed code, but this would * break lots of control (ScrollViewer) and ability to easily integrate an external component. */ try { var isHandledOrBubblingInManaged = default(bool); foreach (UITouch touch in touches) { var pt = TransientNativePointer.Get(this, touch); var args = new PointerRoutedEventArgs(pt.Id, touch, evt, this); if (!pt.HadMove) { // The event will bubble in managed, so as this flag is "pseudo static", make sure to raise it only once. pt.HadMove = true; // On iOS if the gesture is really fast (like a flick), we can get only 'down' and 'up'. // But on UWP it seems that we always have a least one move (for fingers and pen!), and even internally, // the manipulation events are requiring at least one move to kick-in. // Here we are just making sure to raise that event with the final location. // Note: In case of multi-touch we might raise it unnecessarily, but it won't have any negative impact. // Note: We do not consider the result of that move for the 'isHandledOrBubblingInManaged' // as it's kind of un-related to the 'up' itself. var mixedArgs = new PointerRoutedEventArgs(previous: pt.DownArgs, current: args); OnNativePointerMove(mixedArgs); } isHandledOrBubblingInManaged |= OnNativePointerUp(args); if (isHandledOrBubblingInManaged) { // Like for the Down, we need to manually generate an Exited. // This is expected to be done by the RootVisual, except if the "up" has been handled // (in order to ensure the "up" has been fully processed, including gesture recognition). // In that case we need to sent it by our-own directly from teh element that has handled the event. RootVisual.ProcessPointerUp(args, isAfterHandledUp: true); } pt.Release(this); } if (!isHandledOrBubblingInManaged) { // Continue native bubbling up of the event base.TouchesEnded(touches, evt); } NotifyParentTouchesManagersManipulationEnded(); } catch (Exception e) { Application.Current.RaiseRecoverableUnhandledException(e); } }
public override void TouchesBegan(NSSet touches, UIEvent evt) { if (IsPointersSuspended) { return; // Will also prevent subsequents events } /* Note: Here we have a mismatching behavior with UWP, if the events bubble natively we're going to get * (with Ctrl_02 is a child of Ctrl_01): * Ctrl_02: Entered * Pressed * Ctrl_01: Entered * Pressed * * While on UWP we will get: * Ctrl_02: Entered * Ctrl_01: Entered * Ctrl_02: Pressed * Ctrl_01: Pressed * * However, to fix this is would mean that we handle all events in managed code, but this would * break lots of control (ScrollViewer) and ability to easily integrate an external component. */ 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; } isHandledOrBubblingInManaged |= OnNativePointerEnter(args); isHandledOrBubblingInManaged |= OnNativePointerDown(args); 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); } }