Skip to content

Commit 6ae4661

Browse files
authored
fix(SplitView): Ensure clicking outside SplitView pane closes the pane (microsoft#747)
A recent change to the touch handler to support cases where overlays that did not support any pointer events were used (so the underlying React view that did support pointer events could be reached) ignored the fact that some clicks simply don't belong to the React hierarchy (e.g., native app overlays, as is the case for SplitView). This change first checks that the original pointer target actually belongs to the React before calculating the target using pointer events logic. Fixes microsoft#746
1 parent 6641456 commit 6ae4661

File tree

2 files changed

+27
-8
lines changed

2 files changed

+27
-8
lines changed

ReactWindows/ReactNative/Touch/TouchHandler.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using ReactNative.UIManager.Events;
55
using System;
66
using System.Collections.Generic;
7-
using Windows.Foundation;
87
using Windows.UI.Xaml;
98
using Windows.UI.Xaml.Input;
109
using Windows.UI.Xaml.Media;
@@ -47,7 +46,7 @@ private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
4746
throw new InvalidOperationException("A pointer with this ID already exists.");
4847
}
4948

50-
var reactView = GetReactViewFromPoint(e.GetCurrentPoint(_view).Position);
49+
var reactView = GetReactViewTarget(e);
5150
if (reactView != null && _view.CapturePointer(e.Pointer))
5251
{
5352
var reactTag = reactView.GetReactCompoundView().GetReactTagAtPoint(reactView,
@@ -124,16 +123,24 @@ private int IndexOfPointerWithId(uint pointerId)
124123
return -1;
125124
}
126125

127-
private UIElement GetReactViewFromPoint(Point point)
126+
private UIElement GetReactViewTarget(PointerRoutedEventArgs e)
128127
{
128+
// If the target is not a child of the root view, then this pointer
129+
// event does not belong to React.
130+
if (!RootViewHelper.IsReactSubview(e.OriginalSource as DependencyObject))
131+
{
132+
return null;
133+
}
134+
135+
var point = e.GetCurrentPoint(_view).Position;
129136
var sources = VisualTreeHelper.FindElementsInHostCoordinates(point, _view);
130137

131138
// Get the first React view that does not have pointer events set
132139
// to 'none' or 'box-none', and is not a child of a view with
133140
// 'box-only' or 'none' settings for pointer events.
134141

135142
// TODO: use pooled data structure
136-
var isBoxOnlyCache = new Dictionary<UIElement, bool>();
143+
var isBoxOnlyCache = new Dictionary<DependencyObject, bool>();
137144
foreach (var source in sources)
138145
{
139146
if (!source.HasTag())
@@ -192,7 +199,7 @@ private void DispatchTouchEvent(TouchEventType touchEventType, List<ReactPointer
192199
.DispatchEvent(touchEvent);
193200
}
194201

195-
private static bool IsBoxOnlyWithCache(IEnumerable<UIElement> hierarchy, IDictionary<UIElement, bool> cache)
202+
private static bool IsBoxOnlyWithCache(IEnumerable<DependencyObject> hierarchy, IDictionary<DependencyObject, bool> cache)
196203
{
197204
var enumerator = hierarchy.GetEnumerator();
198205

@@ -205,7 +212,7 @@ private static bool IsBoxOnlyWithCache(IEnumerable<UIElement> hierarchy, IDictio
205212
return IsBoxOnlyWithCacheRecursive(enumerator, cache);
206213
}
207214

208-
private static bool IsBoxOnlyWithCacheRecursive(IEnumerator<UIElement> enumerator, IDictionary<UIElement, bool> cache)
215+
private static bool IsBoxOnlyWithCacheRecursive(IEnumerator<DependencyObject> enumerator, IDictionary<DependencyObject, bool> cache)
209216
{
210217
if (!enumerator.MoveNext())
211218
{

ReactWindows/ReactNative/UIManager/RootViewHelper.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Collections.Generic;
2-
using System.Linq;
32
using Windows.UI.Xaml;
43
using Windows.UI.Xaml.Media;
54

@@ -40,7 +39,7 @@ public static ReactRootView GetRootView(DependencyObject view)
4039
/// </summary>
4140
/// <param name="view">The view.</param>
4241
/// <returns>The view hierarchy.</returns>
43-
public static IEnumerable<UIElement> GetReactViewHierarchy(DependencyObject view)
42+
public static IEnumerable<DependencyObject> GetReactViewHierarchy(DependencyObject view)
4443
{
4544
var current = view;
4645
while (true)
@@ -65,6 +64,19 @@ public static IEnumerable<UIElement> GetReactViewHierarchy(DependencyObject view
6564
}
6665
}
6766

67+
/// <summary>
68+
/// Checks if the view is part of the React hierarchy.
69+
/// </summary>
70+
/// <param name="view">The view instance.</param>
71+
/// <returns>
72+
/// <code>true</code> if the view is part of the hierarchy, otherwise
73+
/// <code>false</code>.
74+
/// </returns>
75+
public static bool IsReactSubview(DependencyObject view)
76+
{
77+
return GetReactViewHierarchy(view).GetEnumerator().MoveNext();
78+
}
79+
6880
private static DependencyObject GetParent(DependencyObject view, bool findRoot)
6981
{
7082
//

0 commit comments

Comments
 (0)