Skip to content
This repository was archived by the owner on Apr 29, 2021. It is now read-only.

Commit 2cab0ec

Browse files
authored
Merge pull request #183 from UnityTech/mouse_tracking
Mouse tracking
2 parents 1478f5b + 8ef99a0 commit 2cab0ec

File tree

12 files changed

+607
-143
lines changed

12 files changed

+607
-143
lines changed

Runtime/engine/UIWidgetsPanel.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,15 @@ void handleMouseScroll() {
269269
}
270270

271271
int getMouseButtonDown() {
272+
//default mouse button key = left mouse button
273+
var defaultKey = 0;
272274
for (int key = 0; key < mouseButtonNum; key++) {
273275
if (Input.GetMouseButton(key)) {
274-
return InputUtils.getMouseButtonKey(key);
276+
defaultKey = key;
277+
break;
275278
}
276279
}
277-
278-
return 0;
280+
return InputUtils.getMouseButtonKey(defaultKey);
279281
}
280282

281283
public void OnPointerDown(PointerEventData eventData) {

Runtime/gestures/binding.cs

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,6 @@ void _handlePointerEvent(PointerEvent evt) {
6060
return;
6161
}
6262

63-
if (evt is PointerHoverEvent) {
64-
this._handlePointerHoverEvent(evt);
65-
}
66-
6763
HitTestResult hitTestResult = null;
6864
if (evt is PointerDownEvent) {
6965
D.assert(!this._hitTests.ContainsKey(evt.pointer));
@@ -113,51 +109,6 @@ void _handlePointerScrollEvent(PointerEvent evt) {
113109
this.dispatchEvent(evt, result);
114110
}
115111

116-
void _handlePointerHoverEvent(PointerEvent evt) {
117-
HitTestResult result = new HitTestResult();
118-
this.hitTest(result, evt.position);
119-
120-
D.assert(this._enteredTargets.Count == 0);
121-
foreach (var hitTestEntry in result.path) {
122-
if (this.lastMoveTargets.Contains(hitTestEntry.target)) {
123-
hitTestEntry.target.handleEvent(evt, hitTestEntry);
124-
this.lastMoveTargets.Remove(hitTestEntry.target);
125-
}
126-
else {
127-
this._enteredTargets.Add(hitTestEntry);
128-
}
129-
}
130-
131-
//leave events
132-
foreach (var lastMoveTarget in this.lastMoveTargets) {
133-
lastMoveTarget.handleEvent(new PointerLeaveEvent(
134-
timeStamp: evt.timeStamp,
135-
pointer: evt.pointer,
136-
device: evt.device,
137-
kind: evt.kind
138-
), null);
139-
}
140-
141-
//enter events
142-
foreach (var hitTestEntry in this._enteredTargets) {
143-
hitTestEntry.target.handleEvent(new PointerEnterEvent(
144-
timeStamp: evt.timeStamp,
145-
pointer: evt.pointer,
146-
device: evt.device,
147-
kind: evt.kind
148-
), hitTestEntry);
149-
}
150-
151-
this.lastMoveTargets.Clear();
152-
foreach (var hitTestEntry in result.path) {
153-
this.lastMoveTargets.Add(hitTestEntry.target);
154-
}
155-
156-
this._enteredTargets.Clear();
157-
158-
this.dispatchEvent(evt, result);
159-
}
160-
161112
public virtual void hitTest(HitTestResult result, Offset position) {
162113
result.add(new HitTestEntry(this));
163114
}

Runtime/gestures/events.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ public PointerHoverEvent(
103103
position: position,
104104
down: false) {
105105
}
106+
107+
public static PointerHoverEvent fromHoverEvent(PointerEvent hover) {
108+
return new PointerHoverEvent(
109+
timeStamp: hover.timeStamp,
110+
pointer: hover.pointer,
111+
kind: hover.kind,
112+
device: hover.device,
113+
position: hover.position
114+
);
115+
}
106116
}
107117

108118
public class PointerEnterEvent : PointerEvent {
@@ -120,10 +130,20 @@ public PointerEnterEvent(
120130
position: position,
121131
down: false) {
122132
}
133+
134+
public static PointerEnterEvent fromHoverEvent(PointerEvent hover) {
135+
return new PointerEnterEvent(
136+
timeStamp: hover.timeStamp,
137+
pointer: hover.pointer,
138+
kind: hover.kind,
139+
device: hover.device,
140+
position: hover.position
141+
);
142+
}
123143
}
124144

125-
public class PointerLeaveEvent : PointerEvent {
126-
public PointerLeaveEvent(
145+
public class PointerExitEvent : PointerEvent {
146+
public PointerExitEvent(
127147
TimeSpan timeStamp,
128148
int pointer = 0,
129149
PointerDeviceKind kind = PointerDeviceKind.mouse,
@@ -137,6 +157,16 @@ public PointerLeaveEvent(
137157
position: position,
138158
down: false) {
139159
}
160+
161+
public static PointerExitEvent fromHoverEvent(PointerEvent hover) {
162+
return new PointerExitEvent(
163+
timeStamp: hover.timeStamp,
164+
pointer: hover.pointer,
165+
kind: hover.kind,
166+
device: hover.device,
167+
position: hover.position
168+
);
169+
}
140170
}
141171

142172
public class PointerDownEvent : PointerEvent {

Runtime/gestures/mouse_tracking.cs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
using System.Collections.Generic;
2+
using Unity.UIWidgets.foundation;
3+
using Unity.UIWidgets.scheduler;
4+
using Unity.UIWidgets.ui;
5+
6+
namespace Unity.UIWidgets.gestures {
7+
8+
public delegate void PointerHoverEventListener(PointerHoverEvent evt);
9+
10+
public delegate void PointerEnterEventListener(PointerEnterEvent evt);
11+
12+
public delegate void PointerExitEventListener(PointerExitEvent evt);
13+
14+
15+
public class MouseTrackerAnnotation {
16+
public MouseTrackerAnnotation(
17+
PointerEnterEventListener onEnter = null,
18+
PointerHoverEventListener onHover = null,
19+
PointerExitEventListener onExit = null
20+
) {
21+
this.onEnter = onEnter;
22+
this.onHover = onHover;
23+
this.onExit = onExit;
24+
}
25+
26+
public readonly PointerEnterEventListener onEnter;
27+
28+
public readonly PointerHoverEventListener onHover;
29+
30+
public readonly PointerExitEventListener onExit;
31+
32+
public override string ToString() {
33+
return $"{this.GetType()}#{this.GetHashCode()}{(this.onEnter == null ? "" : " onEnter")}{(this.onHover == null ? "" : " onHover")}{(this.onExit == null ? "" : " onExit")}";
34+
}
35+
}
36+
37+
public class _TrackedAnnotation {
38+
public _TrackedAnnotation(
39+
MouseTrackerAnnotation annotation) {
40+
this.annotation = annotation;
41+
}
42+
43+
public readonly MouseTrackerAnnotation annotation;
44+
45+
public HashSet<int> activeDevices = new HashSet<int>();
46+
}
47+
48+
public delegate MouseTrackerAnnotation MouseDetectorAnnotationFinder(Offset offset);
49+
50+
51+
public class MouseTracker {
52+
public MouseTracker(
53+
PointerRouter router,
54+
MouseDetectorAnnotationFinder annotationFinder) {
55+
router.addGlobalRoute(this._handleEvent);
56+
this.annotationFinder = annotationFinder;
57+
}
58+
59+
readonly Dictionary<int, PointerEvent> _lastMouseEvent = new Dictionary<int, PointerEvent>();
60+
61+
public bool mouseIsConnected {
62+
get { return this._lastMouseEvent.isNotEmpty(); }
63+
}
64+
65+
public readonly MouseDetectorAnnotationFinder annotationFinder;
66+
67+
public readonly Dictionary<MouseTrackerAnnotation, _TrackedAnnotation> _trackedAnnotations =
68+
new Dictionary<MouseTrackerAnnotation, _TrackedAnnotation>();
69+
70+
71+
public void attachAnnotation(MouseTrackerAnnotation annotation) {
72+
this._trackedAnnotations[annotation] = new _TrackedAnnotation(annotation);
73+
this._scheduleMousePositionCheck();
74+
}
75+
76+
public void detachAnnotation(MouseTrackerAnnotation annotation) {
77+
_TrackedAnnotation trackedAnnotation = this._findAnnotation(annotation);
78+
foreach (int deviceId in trackedAnnotation.activeDevices) {
79+
annotation.onExit(PointerExitEvent.fromHoverEvent((PointerHoverEvent) this._lastMouseEvent[deviceId]));
80+
}
81+
82+
this._trackedAnnotations.Remove(annotation);
83+
}
84+
85+
void _scheduleMousePositionCheck() {
86+
SchedulerBinding.instance.addPostFrameCallback(_ => { this.collectMousePositions();});
87+
SchedulerBinding.instance.scheduleFrame();
88+
}
89+
90+
void _handleEvent(PointerEvent evt) {
91+
if (evt.kind != PointerDeviceKind.mouse) {
92+
return;
93+
}
94+
95+
int deviceId = evt.device;
96+
if (this._trackedAnnotations.isEmpty()) {
97+
this._lastMouseEvent.Remove(deviceId);
98+
return;
99+
}
100+
101+
if (evt is PointerRemovedEvent) {
102+
this._lastMouseEvent.Remove(deviceId);
103+
this._scheduleMousePositionCheck();
104+
}
105+
else {
106+
if (evt is PointerMoveEvent ||
107+
evt is PointerHoverEvent ||
108+
evt is PointerDownEvent) {
109+
if (!this._lastMouseEvent.ContainsKey(deviceId) ||
110+
this._lastMouseEvent[deviceId].position != evt.position) {
111+
this._scheduleMousePositionCheck();
112+
}
113+
114+
this._lastMouseEvent[deviceId] = evt;
115+
}
116+
}
117+
}
118+
119+
_TrackedAnnotation _findAnnotation(MouseTrackerAnnotation annotation) {
120+
if (!this._trackedAnnotations.TryGetValue(annotation, out var trackedAnnotation)) {
121+
D.assert(false, () => "Unable to find annotation $annotation in tracked annotations. " +
122+
"Check that attachAnnotation has been called for all annotated layers.");
123+
}
124+
125+
return trackedAnnotation;
126+
}
127+
128+
bool isAnnotationAttached(MouseTrackerAnnotation annotation) {
129+
return this._trackedAnnotations.ContainsKey(annotation);
130+
}
131+
132+
public void collectMousePositions() {
133+
void exitAnnotation(_TrackedAnnotation trackedAnnotation, int deviceId) {
134+
if (trackedAnnotation.annotation?.onExit != null &&
135+
trackedAnnotation.activeDevices.Contains(deviceId)) {
136+
trackedAnnotation.annotation.onExit(PointerExitEvent.fromHoverEvent(this._lastMouseEvent[deviceId]));
137+
trackedAnnotation.activeDevices.Remove(deviceId);
138+
}
139+
}
140+
141+
void exitAllDevices(_TrackedAnnotation trackedAnnotation) {
142+
if (trackedAnnotation.activeDevices.isNotEmpty()) {
143+
HashSet<int> deviceIds = new HashSet<int>(trackedAnnotation.activeDevices);
144+
foreach (int deviceId in deviceIds) {
145+
exitAnnotation(trackedAnnotation, deviceId);
146+
}
147+
}
148+
}
149+
150+
if (!this.mouseIsConnected) {
151+
foreach (var annotation in this._trackedAnnotations.Values) {
152+
exitAllDevices(annotation);
153+
}
154+
155+
return;
156+
}
157+
158+
foreach (int deviceId in this._lastMouseEvent.Keys) {
159+
PointerEvent lastEvent = this._lastMouseEvent[deviceId];
160+
MouseTrackerAnnotation hit = this.annotationFinder(lastEvent.position);
161+
162+
if (hit == null) {
163+
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
164+
exitAnnotation(trackedAnnotation, deviceId);
165+
}
166+
167+
return;
168+
}
169+
170+
_TrackedAnnotation hitAnnotation = this._findAnnotation(hit);
171+
//enter
172+
if (!hitAnnotation.activeDevices.Contains(deviceId)) {
173+
hitAnnotation.activeDevices.Add(deviceId);
174+
if (hitAnnotation.annotation?.onEnter != null) {
175+
hitAnnotation.annotation.onEnter(PointerEnterEvent.fromHoverEvent(lastEvent));
176+
}
177+
}
178+
179+
//hover
180+
if (hitAnnotation.annotation?.onHover != null) {
181+
hitAnnotation.annotation.onHover(PointerHoverEvent.fromHoverEvent(lastEvent));
182+
}
183+
184+
//leave
185+
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
186+
if (hitAnnotation == trackedAnnotation) {
187+
continue;
188+
}
189+
190+
if (trackedAnnotation.activeDevices.Contains(deviceId)) {
191+
if (trackedAnnotation.annotation?.onExit != null) {
192+
trackedAnnotation.annotation.onExit(PointerExitEvent.fromHoverEvent((PointerHoverEvent)lastEvent));
193+
}
194+
195+
trackedAnnotation.activeDevices.Remove(deviceId);
196+
}
197+
}
198+
}
199+
}
200+
}
201+
}

Runtime/gestures/mouse_tracking.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/rendering/binding.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public RendererBinding() {
2121
this.initRenderView();
2222
D.assert(this.renderView != null);
2323
this.addPersistentFrameCallback(this._handlePersistentFrameCallback);
24+
this._mouseTracker = this._createMouseTracker();
2425
}
2526

2627
public void initRenderView() {
@@ -29,6 +30,11 @@ public void initRenderView() {
2930
this.renderView.scheduleInitialFrame();
3031
}
3132

33+
public MouseTracker mouseTracker {
34+
get { return this._mouseTracker; }
35+
}
36+
MouseTracker _mouseTracker;
37+
3238
public PipelineOwner pipelineOwner {
3339
get { return this._pipelineOwner; }
3440
}
@@ -63,6 +69,14 @@ void _handlePersistentFrameCallback(TimeSpan timeStamp) {
6369
this.drawFrame();
6470
}
6571

72+
MouseTracker _createMouseTracker() {
73+
return new MouseTracker(this.pointerRouter, (Offset offset) => {
74+
return this.renderView.layer.find<MouseTrackerAnnotation>(
75+
offset
76+
);
77+
});
78+
}
79+
6680
protected virtual void drawFrame() {
6781
this.pipelineOwner.flushLayout();
6882
this.pipelineOwner.flushCompositingBits();

0 commit comments

Comments
 (0)