Skip to content

Commit 2cc6978

Browse files
kevinvangeldermatthargett
authored andcommitted
added mouse-enter/leave support to WPF (microsoft#1111)
* revert unnecessary change * minor changes to address PR feedback
1 parent 76a043e commit 2cc6978

File tree

2 files changed

+138
-14
lines changed

2 files changed

+138
-14
lines changed

ReactWindows/ReactNative.Net46/Touch/TouchHandler.cs

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,30 @@ public void Dispose()
4040
_view.TouchUp -= OnTouchReleased;
4141
}
4242

43+
public static void OnPointerEntered(DependencyObject view, MouseEventArgs e)
44+
{
45+
if (ShouldSendEnterLeaveEvent(view))
46+
{
47+
view.GetReactContext()
48+
.GetNativeModule<UIManagerModule>()
49+
.EventDispatcher
50+
.DispatchEvent(
51+
new PointerEnterExitEvent(TouchEventType.Entered, view.GetTag()));
52+
}
53+
}
54+
55+
public static void OnPointerExited(DependencyObject view, MouseEventArgs e)
56+
{
57+
if (ShouldSendEnterLeaveEvent(view))
58+
{
59+
view.GetReactContext()
60+
.GetNativeModule<UIManagerModule>()
61+
.EventDispatcher
62+
.DispatchEvent(
63+
new PointerEnterExitEvent(TouchEventType.Exited, view.GetTag()));
64+
}
65+
}
66+
4367
private void OnTouchPressed(object sender, TouchEventArgs e)
4468
{
4569
var originalSource = e.OriginalSource as DependencyObject;
@@ -334,6 +358,28 @@ private static bool IsBoxOnlyWithCacheRecursive(IEnumerator<DependencyObject> en
334358
return isBoxOnly;
335359
}
336360

361+
private static bool ShouldSendEnterLeaveEvent(DependencyObject view)
362+
{
363+
// If the target is not a child of the root view, then this pointer
364+
// event does not belong to React.
365+
if (!RootViewHelper.IsReactSubview(view))
366+
{
367+
return false;
368+
}
369+
370+
var viewHierarchy = RootViewHelper.GetReactViewHierarchy(view);
371+
foreach (var ancestor in viewHierarchy)
372+
{
373+
var pointerEvents = ancestor.GetPointerEvents();
374+
if (pointerEvents == PointerEvents.None || pointerEvents == PointerEvents.BoxNone)
375+
{
376+
return false;
377+
}
378+
}
379+
380+
return true;
381+
}
382+
337383
class TouchEvent : Event
338384
{
339385
private readonly TouchEventType _touchEventType;
@@ -350,21 +396,9 @@ public TouchEvent(TouchEventType touchEventType, JArray touches, JArray changedI
350396
_coalescingKey = coalescingKey;
351397
}
352398

353-
public override string EventName
354-
{
355-
get
356-
{
357-
return _touchEventType.GetJavaScriptEventName();
358-
}
359-
}
399+
public override string EventName => _touchEventType.GetJavaScriptEventName();
360400

361-
public override bool CanCoalesce
362-
{
363-
get
364-
{
365-
return _touchEventType == TouchEventType.Move;
366-
}
367-
}
401+
public override bool CanCoalesce => _touchEventType == TouchEventType.Move;
368402

369403
public override short CoalescingKey
370404
{
@@ -383,6 +417,47 @@ public override void Dispatch(RCTEventEmitter eventEmitter)
383417
}
384418
}
385419

420+
class PointerEnterExitEvent : Event
421+
{
422+
private readonly TouchEventType _touchEventType;
423+
424+
public PointerEnterExitEvent(TouchEventType touchEventType, int viewTag)
425+
: base(viewTag, TimeSpan.FromTicks(Environment.TickCount))
426+
{
427+
_touchEventType = touchEventType;
428+
}
429+
430+
public override string EventName => _touchEventType.GetJavaScriptEventName();
431+
432+
public override bool CanCoalesce => false;
433+
434+
public override void Dispatch(RCTEventEmitter eventEmitter)
435+
{
436+
var eventData = new JObject
437+
{
438+
{ "target", ViewTag },
439+
};
440+
441+
var enterLeaveEventName = default(string);
442+
switch (_touchEventType)
443+
{
444+
case TouchEventType.Entered:
445+
enterLeaveEventName = "topMouseEnter";
446+
break;
447+
case TouchEventType.Exited:
448+
enterLeaveEventName = "topMouseLeave";
449+
break;
450+
}
451+
452+
if (enterLeaveEventName != null)
453+
{
454+
eventEmitter.receiveEvent(ViewTag, enterLeaveEventName, eventData);
455+
}
456+
457+
eventEmitter.receiveEvent(ViewTag, EventName, eventData);
458+
}
459+
}
460+
386461
class ReactPointer
387462
{
388463
[JsonProperty(PropertyName = "target")]

ReactWindows/ReactNative.Net46/UIManager/BaseViewManager.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using Newtonsoft.Json.Linq;
2+
using ReactNative.Touch;
23
using ReactNative.UIManager.Annotations;
34
using System;
45
using System.Windows;
56
using System.Windows.Automation;
67
using System.Windows.Controls;
8+
using System.Windows.Input;
79
using System.Windows.Media;
810
using System.Windows.Media.Effects;
911
using System.Windows.Media.Media3D;
@@ -88,6 +90,53 @@ public void SetTestId(TFrameworkElement view, string testId)
8890
AutomationProperties.SetAutomationId(view, testId ?? "");
8991
}
9092

93+
/// <summary>
94+
/// Called when view is detached from view hierarchy and allows for
95+
/// additional cleanup by the <see cref="IViewManager"/> subclass.
96+
/// </summary>
97+
/// <param name="reactContext">The React context.</param>
98+
/// <param name="view">The view.</param>
99+
/// <remarks>
100+
/// Be sure to call this base class method to register for pointer
101+
/// entered and pointer exited events.
102+
/// </remarks>
103+
public override void OnDropViewInstance(ThemedReactContext reactContext, TFrameworkElement view)
104+
{
105+
view.MouseEnter -= OnPointerEntered;
106+
view.MouseLeave -= OnPointerExited;
107+
}
108+
109+
/// <summary>
110+
/// Subclasses can override this method to install custom event
111+
/// emitters on the given view.
112+
/// </summary>
113+
/// <param name="reactContext">The React context.</param>
114+
/// <param name="view">The view instance.</param>
115+
/// <remarks>
116+
/// Consider overriding this method if your view needs to emit events
117+
/// besides basic touch events to JavaScript (e.g., scroll events).
118+
///
119+
/// Make sure you call the base implementation to ensure base pointer
120+
/// event handlers are subscribed.
121+
/// </remarks>
122+
protected override void AddEventEmitters(ThemedReactContext reactContext, TFrameworkElement view)
123+
{
124+
view.MouseEnter += OnPointerEntered;
125+
view.MouseLeave += OnPointerExited;
126+
}
127+
128+
private void OnPointerEntered(object sender, MouseEventArgs e)
129+
{
130+
var view = (TFrameworkElement)sender;
131+
TouchHandler.OnPointerEntered(view, e);
132+
}
133+
134+
private void OnPointerExited(object sender, MouseEventArgs e)
135+
{
136+
var view = (TFrameworkElement)sender;
137+
TouchHandler.OnPointerExited(view, e);
138+
}
139+
91140
/// <summary>
92141
/// Sets the shadow color of the <typeparamref name="TFrameworkElement"/>.
93142
/// </summary>

0 commit comments

Comments
 (0)