private void UpdatePointersInViews(UIElement reactView, PointerPoint rootPoint, PointerRoutedEventArgs e) { // Create list of react views that should be tracked based on the // view hierarchy starting from reactView, keeping in just the views that intersect the rootPoint var newViews = reactView != null ? new HashSet <DependencyObject>( RootViewHelper.GetReactViewHierarchy(reactView).Where((v) => { if (v is FrameworkElement element) { var viewPoint = e.GetCurrentPoint(element); return(viewPoint.Position.X >= 0 && viewPoint.Position.X < element.ActualWidth && viewPoint.Position.Y >= 0 && viewPoint.Position.Y < element.ActualHeight); } else { return(false); } })) : new HashSet <DependencyObject>(); // Get existing list of react views for the pointer id HashSet <DependencyObject> existingViews; if (!_pointersInViews.TryGetValue(e.Pointer.PointerId, out existingViews)) { existingViews = new HashSet <DependencyObject>(); } // Return quick if list didn't change if (newViews.SetEquals(existingViews)) { return; } // Notify the tags that disappeared from the list if: // - there's still a tag associated with the view (it hasn't been removed from tree) // - there's a need (driven by handlers hooked on the JS side) to send events foreach (var existingView in existingViews) { if (!newViews.Contains(existingView) && existingView.HasTag() && ShouldSendPointerEnterLeaveOverOutEvent(existingView, out var enterOrLeave, out var overOrOut)) { OnPointerEnteredExited(TouchEventType.Exited, (UIElement)existingView, rootPoint, e, enterOrLeave, overOrOut); } } // Notify the new views that showed up if: // - there's a need (driven by handlers hooked on the JS side) to send events foreach (var newView in newViews) { if (!existingViews.Contains(newView) && ShouldSendPointerEnterLeaveOverOutEvent(newView, out var enterOrLeave, out var overOrOut)) { OnPointerEnteredExited(TouchEventType.Entered, (UIElement)newView, rootPoint, e, enterOrLeave, overOrOut); } } _pointersInViews[e.Pointer.PointerId] = newViews; }
private static bool ShouldSendPointerEnterLeaveOverOutEvent(DependencyObject view, out bool enterOrLeave, out bool overOrOut) { // "view" parameter can be any view in the view hierarchy starting from the chosen react target. // Due to the way the choosing of the react target works, these views are guaranteed not to be "box-only" nor "none". // Still, some can be "box-none", and they should NOT be chosen as enter/leave/over/out targets if (view.GetPointerEvents() == PointerEvents.BoxNone) { enterOrLeave = false; overOrOut = false; return(false); } enterOrLeave = view.GetMouseHandlerPresent( ViewExtensions.MouseHandlerMask.MouseEnter | ViewExtensions.MouseHandlerMask.MouseLeave); overOrOut = RootViewHelper.GetReactViewHierarchy(view).Any( v => v.GetMouseHandlerPresent( ViewExtensions.MouseHandlerMask.MouseOver | ViewExtensions.MouseHandlerMask.MouseOverCapture | ViewExtensions.MouseHandlerMask.MouseOut | ViewExtensions.MouseHandlerMask.MouseOutCapture)); return(enterOrLeave || overOrOut); }
private UIElement GetReactViewFromView(DependencyObject originalSource) { var viewHierarchy = RootViewHelper.GetReactViewHierarchy(originalSource); if (viewHierarchy.Count == 0) { return(null); } var target = -1; for (var i = 0; i < viewHierarchy.Count; ++i) { var view = viewHierarchy[i]; var pointerEvents = view.GetPointerEvents(); if (pointerEvents != PointerEvents.None && pointerEvents != PointerEvents.BoxNone) { target = i; } if (pointerEvents == PointerEvents.BoxOnly || pointerEvents == PointerEvents.None) { break; } } return(target < 0 ? null : viewHierarchy[target]); }
private DependencyObject GetReactViewTarget(PointerRoutedEventArgs e) { // Ensure the original source is a DependencyObject var originalSource = e.OriginalSource as DependencyObject; if (originalSource == null) { return(null); } // Get the React view hierarchy from the original source. var enumerator = RootViewHelper.GetReactViewHierarchy(originalSource).GetEnumerator(); // Try to get the first React view. if (!enumerator.MoveNext()) { return(null); } // If the first React view has `pointerEvents: box-none`, revert // to using the `VisualTreeHelper` to find potentially occluded views. // This condition should be rare. if (enumerator.Current.GetPointerEvents() == PointerEvents.BoxNone) { var rootPoint = e.GetCurrentPoint(_view); var transform = _view.TransformToVisual(Window.Current.Content); var point = transform.TransformPoint(rootPoint.Position); var adjustedPoint = AdjustPointForStatusBar(point); // Get the first view in at the pointer point that is not `box-none`. var nonBoxOnlyView = VisualTreeHelper.FindElementsInHostCoordinates(adjustedPoint, _view) .First(v => v.HasTag() && v.GetPointerEvents() != PointerEvents.BoxNone); // Update the enumerator for the non-`box-only` view. enumerator = RootViewHelper.GetReactViewHierarchy(nonBoxOnlyView).GetEnumerator(); if (!enumerator.MoveNext()) { return(null); } } // Views with `pointerEvents: none` are not hit test enabled, so we // will not encounter any view subtrees with this value. Given the // prior conditional for `pointerEvents: box-none`, we only need to // loop through the parents of the current React view target to // check for uses of `pointerEvents: box-only`. If found, the // parent becomes the new target. var source = enumerator.Current; while (enumerator.MoveNext()) { var current = enumerator.Current; if (source == null || current.GetPointerEvents() == PointerEvents.BoxOnly) { source = current; } } return(source); }
private UIElement GetReactViewTarget(DependencyObject originalSource, Point point) { // If the target is not a child of the root view, then this pointer // event does not belong to React. if (!RootViewHelper.IsReactSubview(originalSource)) { return null; } // population of sources provided by: // http://stackoverflow.com/questions/2059475/what-is-the-silverlights-findelementsinhostcoordinates-equivalent-in-wpf var sources = new List<DependencyObject>(); //changed from external edits, because VisualHit is //only a DependencyObject and may not be a UIElement //this could cause exceptions or may not be compiling at all //simply filter the result for class UIElement and //cast it to IEnumerable<UIElement> if you need //the very exact same result including type VisualTreeHelper.HitTest( _view, null, new HitTestResultCallback( hit => { var source = hit.VisualHit; if (!source.HasTag()) { return HitTestResultBehavior.Continue; } var pointerEvents = source.GetPointerEvents(); if (pointerEvents == PointerEvents.None || pointerEvents == PointerEvents.BoxNone) { return HitTestResultBehavior.Continue; } sources.Add(hit.VisualHit); return HitTestResultBehavior.Continue; }), new PointHitTestParameters(point)); // Get the first React view that does not have pointer events set // to 'none' or 'box-none', and is not a child of a view with // 'box-only' or 'none' settings for pointer events. // TODO: use pooled data structure var isBoxOnlyCache = new Dictionary<DependencyObject, bool>(); foreach (var source in sources) { var viewHierarchy = RootViewHelper.GetReactViewHierarchy(source); var isBoxOnly = IsBoxOnlyWithCache(viewHierarchy, isBoxOnlyCache); if (!isBoxOnly) { return (UIElement)source; } } return null; }
private static bool ShouldSendPointerMoveEvent(DependencyObject view) { // This is a bubbling event, so we have to check if mouse move handler is hooked to any ancestor return(RootViewHelper.GetReactViewHierarchy(view).Any( v => v.GetMouseHandlerPresent( ViewExtensions.MouseHandlerMask.MouseMove | ViewExtensions.MouseHandlerMask.MouseMoveCapture))); }
public int GetReactTagAtPoint(UIElement reactView, Point point) { var richTextBlock = reactView.As <TextBlock>(); var textPointer = richTextBlock.GetPositionFromPoint(point, true); var parentView = RootViewHelper.GetReactViewHierarchy(textPointer.Parent).First(); return(parentView.GetTag()); }
private UIElement GetReactViewTarget(DependencyObject originalSource, Point point) { // If the target is not a child of the root view, then this pointer // event does not belong to React. if (!RootViewHelper.IsReactSubview(originalSource)) { return(null); } var adjustedPoint = AdjustPointForStatusBar(point); var sources = VisualTreeHelper.FindElementsInHostCoordinates(adjustedPoint, _view); // Get the first React view that does not have pointer events set // to 'none' or 'box-none', and is not a child of a view with // 'box-only' or 'none' settings for pointer events. // TODO: use pooled data structure var isBoxOnlyCache = new Dictionary <DependencyObject, bool>(); foreach (var source in sources) { if (!source.HasTag()) { continue; } var pointerEvents = source.GetPointerEvents(); if (pointerEvents == PointerEvents.None || pointerEvents == PointerEvents.BoxNone) { continue; } var viewHierarchy = RootViewHelper.GetReactViewHierarchy(source); var isBoxOnly = IsBoxOnlyWithCache(viewHierarchy, isBoxOnlyCache); if (!isBoxOnly) { return(source); } } return(null); }
private static bool ShouldSendEnterLeaveEvent(DependencyObject view) { // If the target is not a child of the root view, then this pointer // event does not belong to React. if (!RootViewHelper.IsReactSubview(view)) { return false; } var viewHierarchy = RootViewHelper.GetReactViewHierarchy(view); foreach (var ancestor in viewHierarchy) { var pointerEvents = ancestor.GetPointerEvents(); if (pointerEvents == PointerEvents.None || pointerEvents == PointerEvents.BoxNone) { return false; } } return true; }
private UIElement GetReactViewTarget(DependencyObject originalSource, Point point) { // If the target is not a child of the root view, then this pointer // event does not belong to React. if (!RootViewHelper.IsReactSubview(originalSource)) { return(null); } // population of sources provided by: // http://stackoverflow.com/questions/2059475/what-is-the-silverlights-findelementsinhostcoordinates-equivalent-in-wpf var sources = new List <DependencyObject>(); //changed from external edits, because VisualHit is //only a DependencyObject and may not be a UIElement //this could cause exceptions or may not be compiling at all //simply filter the result for class UIElement and //cast it to IEnumerable<UIElement> if you need //the very exact same result including type VisualTreeHelper.HitTest( _view, null, new HitTestResultCallback( hit => { var source = hit.VisualHit; if (!source.HasTag()) { return(HitTestResultBehavior.Continue); } var pointerEvents = source.GetPointerEvents(); if (pointerEvents == PointerEvents.None || pointerEvents == PointerEvents.BoxNone) { return(HitTestResultBehavior.Continue); } sources.Add(hit.VisualHit); return(HitTestResultBehavior.Continue); }), new PointHitTestParameters(point)); var viewHierarchy = RootViewHelper.GetReactViewHierarchy(originalSource); var dependencyObjects = viewHierarchy as DependencyObject[] ?? viewHierarchy.ToArray(); var enumerator = dependencyObjects.GetEnumerator(); if (!enumerator.MoveNext()) { return(null); } if (enumerator.Current.GetPointerEvents() == PointerEvents.BoxNone) { var nonBoxOnlyView = sources.FirstOrDefault(x => x.HasTag() && x.GetPointerEvents() != PointerEvents.BoxNone); enumerator = RootViewHelper.GetReactViewHierarchy(nonBoxOnlyView).GetEnumerator(); if (!enumerator.MoveNext()) { return(null); } } var element = enumerator.Current; while (enumerator.MoveNext()) { var current = enumerator.Current; if (element == null || current.GetPointerEvents() == PointerEvents.BoxOnly || !(element is UIElement)) { element = current; } } return((UIElement)element); }