private void CoreWindow_PointerWheelChanged(CoreWindow sender, PointerEventArgs args) { var source = FindOriginalSource(args); if (source.element is null) { if (this.Log().IsEnabled(LogLevel.Trace)) { this.Log().Trace($"CoreWindow_PointerPressed ({args.CurrentPoint.Position}) **undispatched**"); } return; } if (this.Log().IsEnabled(LogLevel.Trace)) { this.Log().Trace($"CoreWindow_PointerPressed [{source.element.GetDebugName()}"); } var routedArgs = new PointerRoutedEventArgs(args, source.element); // Second raise the event, either on the OriginalSource or on the capture owners if any if (PointerCapture.TryGet(routedArgs.Pointer, out var capture)) { foreach (var target in capture.Targets.ToArray()) { target.Element.OnNativePointerWheel(routedArgs); } } else { source.element.OnNativePointerWheel(routedArgs); } }
private void ReleaseCaptures(PointerRoutedEventArgs routedArgs) { if (PointerCapture.TryGet(routedArgs.Pointer, out var capture)) { foreach (var target in capture.Targets) { target.Element.ReleasePointerCapture(capture.Pointer); } } }
private static void ReleaseCaptures(PointerRoutedEventArgs routedArgs) { if (PointerCapture.TryGet(routedArgs.Pointer, out var capture)) { foreach (var target in capture.Targets.ToList()) { target.Element.ReleasePointerCapture(capture.Pointer.UniqueId, kinds: PointerCaptureKind.Any); } } }
private void CoreWindow_PointerMoved(CoreWindow sender, PointerEventArgs args) { var(originalSource, staleBranch) = VisualTreeHelper.HitTest(args.CurrentPoint.Position, isStale: _isOver); // This is how UWP behaves: when out of the bounds of the Window, the root element is use. // Note that if another app covers your app, then the OriginalSource on UWP is still the element of your app at the pointer's location. originalSource ??= Windows.UI.Xaml.Window.Current.Content; if (originalSource is null) { if (this.Log().IsEnabled(LogLevel.Trace)) { this.Log().Trace($"CoreWindow_PointerMoved ({args.CurrentPoint.Position}) **undispatched**"); } return; } if (this.Log().IsEnabled(LogLevel.Trace)) { this.Log().Trace($"CoreWindow_PointerMoved [{originalSource.GetDebugName()}"); } var routedArgs = new PointerRoutedEventArgs(args, originalSource); // First raise the PointerExited events on the stale branch if (staleBranch.HasValue) { var(root, leaf) = staleBranch.Value; ClearPointerState(routedArgs, root, leaf); } // Second (try to) raise the PointerEnter on the OriginalSource // Note: This won't do anything if already over. routedArgs.Handled = false; originalSource.OnNativePointerEnter(routedArgs); // Finally raise the event, either on the OriginalSource or on the capture owners if any if (PointerCapture.TryGet(routedArgs.Pointer, out var capture)) { foreach (var target in capture.Targets.ToArray()) { routedArgs.Handled = false; target.Element.OnNativePointerMove(routedArgs); } } else { // Note: We prefer to use the "WithOverCheck" overload as we already know that the pointer is effectively over routedArgs.Handled = false; originalSource.OnNativePointerMoveWithOverCheck(routedArgs, isOver: true); } }
private void CoreWindow_PointerReleased(CoreWindow sender, PointerEventArgs args) { var(originalSource, _) = VisualTreeHelper.HitTest(args.CurrentPoint.Position); // Even if impossible for the Release, we are fallbacking on the RootElement for safety // This is how UWP behaves: when out of the bounds of the Window, the root element is use. // Note that if another app covers your app, then the OriginalSource on UWP is still the element of your app at the pointer's location. originalSource ??= Windows.UI.Xaml.Window.Current.Content; if (originalSource is null) { if (this.Log().IsEnabled(LogLevel.Trace)) { this.Log().Trace($"CoreWindow_PointerPressed ({args.CurrentPoint.Position}) **undispatched**"); } return; } if (this.Log().IsEnabled(LogLevel.Trace)) { this.Log().Trace($"CoreWindow_PointerPressed [{originalSource.GetDebugName()}"); } var routedArgs = new PointerRoutedEventArgs(args, originalSource); // Second raise the event, either on the OriginalSource or on the capture owners if any if (PointerCapture.TryGet(routedArgs.Pointer, out var capture)) { foreach (var target in capture.Targets.ToArray()) { target.Element.OnNativePointerUp(routedArgs); } } else { originalSource.OnNativePointerUp(routedArgs); } if (_pressedElements.TryGetValue(routedArgs.Pointer, out var pressedLeaf)) { // We must make sure to clear the pressed state on all elements that was flagged as pressed. // This is required as the current originalSource might not be the same as when we pressed (pointer moved), // ** OR ** the pointer has been captured by a parent element so we didn't raised to released on the sub elements. _pressedElements.Remove(routedArgs.Pointer); ClearPointerState(routedArgs, root: null, pressedLeaf, clearOver: false); } }
private void OnSwipeManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) { #if !DEBUG // On UWP, SwipeControl works only with touch. // We do allow other pointers in DEBUG ... well, because it easier to debug on a PC :) if (e.PointerDeviceType != PointerDeviceType.Touch) { e.Complete(); return; } #endif if (m_isIdle) { m_isIdle = false; } m_lastActionWasClosing = false; m_lastActionWasOpening = false; m_isInteracting = true; //Once the user has started interacting with a SwipeControl in the closed state we are free to unblock contents. //Contents of items opposite the currently opened ones will not be created. if (!m_isOpen) { m_blockNearContent = false; m_blockFarContent = false; } _positionWhenCaptured = new Vector2((float)_transform.X, (float)_transform.Y); // As soon as manipulation starts, we make sure to abort any pending explicit pointer capture, // so button nested (or containing) this SwipeControl won't fire their commands. // It's not the common behavior for buttons, but this has been observed in SwipeControl on UWP as of 2021-07-27. foreach (var pointer in e.Pointers) { if (PointerCapture.TryGet(pointer, out var capture)) { var targets = capture.GetTargets(PointerCaptureKind.Explicit).ToList(); global::System.Diagnostics.Debug.Assert(targets.Count <= 1); foreach (var target in targets) { target.Element.ReleasePointerCapture(capture.Pointer); } } } }
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 void CoreWindow_PointerWheelChanged(CoreWindow sender, PointerEventArgs args) { var(originalSource, _) = VisualTreeHelper.HitTest(args.CurrentPoint.Position); // Even if impossible for the Release, we are fallbacking on the RootElement for safety // This is how UWP behaves: when out of the bounds of the Window, the root element is use. // Note that if another app covers your app, then the OriginalSource on UWP is still the element of your app at the pointer's location. originalSource ??= Windows.UI.Xaml.Window.Current.Content; if (originalSource is null) { if (this.Log().IsEnabled(LogLevel.Trace)) { this.Log().Trace($"CoreWindow_PointerPressed ({args.CurrentPoint.Position}) **undispatched**"); } return; } if (this.Log().IsEnabled(LogLevel.Trace)) { this.Log().Trace($"CoreWindow_PointerPressed [{originalSource.GetDebugName()}"); } var routedArgs = new PointerRoutedEventArgs(args, originalSource); // Second raise the event, either on the OriginalSource or on the capture owners if any if (PointerCapture.TryGet(routedArgs.Pointer, out var capture)) { foreach (var target in capture.Targets.ToArray()) { target.Element.OnNativePointerWheel(routedArgs); } } else { originalSource.OnNativePointerWheel(routedArgs); } }
private static void RaiseUsingCaptures(RaisePointerEventArgs raise, UIElement originalSource, PointerRoutedEventArgs routedArgs) { if (PointerCapture.TryGet(routedArgs.Pointer, out var capture)) { var targets = capture.Targets.ToList(); if (capture.IsImplicitOnly) { raise(originalSource, routedArgs, BubblingContext.Bubble); foreach (var target in targets) { routedArgs.Handled = false; raise(target.Element, routedArgs, BubblingContext.NoBubbling); } } else { var explicitTarget = targets.Find(c => c.Kind == PointerCaptureKind.Explicit) !; raise(explicitTarget.Element, routedArgs, BubblingContext.Bubble); foreach (var target in targets) { if (target == explicitTarget) { continue; } routedArgs.Handled = false; raise(target.Element, routedArgs, BubblingContext.NoBubbling); } } } else { raise(originalSource, routedArgs, BubblingContext.Bubble); } }
public static bool TryGet(Pointer pointer, out PointerCapture capture) => _actives.TryGetValue(pointer.UniqueId, out capture);
: new PointerCapture(pointer); // The capture will be added to the _actives only when a target is added to it. public static bool TryGet(PointerIdentifier pointer, out PointerCapture capture) => _actives.TryGetValue(pointer, out capture);