浏览代码

Merge pull request #183 from UnityTech/mouse_tracking

Mouse tracking
/main
GitHub 6 年前
当前提交
ca1c1002
共有 12 个文件被更改,包括 607 次插入143 次删除
  1. 8
      Runtime/engine/UIWidgetsPanel.cs
  2. 49
      Runtime/gestures/binding.cs
  3. 34
      Runtime/gestures/events.cs
  4. 14
      Runtime/rendering/binding.cs
  5. 137
      Runtime/rendering/layer.cs
  6. 99
      Runtime/rendering/paragraph.cs
  7. 160
      Runtime/rendering/proxy_box.cs
  8. 19
      Runtime/widgets/basic.cs
  9. 3
      Runtime/widgets/widget_inspector.cs
  10. 15
      Tests/Editor/MouseHover.cs
  11. 201
      Runtime/gestures/mouse_tracking.cs
  12. 11
      Runtime/gestures/mouse_tracking.cs.meta

8
Runtime/engine/UIWidgetsPanel.cs


}
int getMouseButtonDown() {
//default mouse button key = left mouse button
var defaultKey = 0;
return InputUtils.getMouseButtonKey(key);
defaultKey = key;
break;
return 0;
return InputUtils.getMouseButtonKey(defaultKey);
}
public void OnPointerDown(PointerEventData eventData) {

49
Runtime/gestures/binding.cs


return;
}
if (evt is PointerHoverEvent) {
this._handlePointerHoverEvent(evt);
}
HitTestResult hitTestResult = null;
if (evt is PointerDownEvent) {
D.assert(!this._hitTests.ContainsKey(evt.pointer));

HitTestResult result = new HitTestResult();
this.hitTest(result, evt.position);
this.dispatchEvent(evt, result);
}
void _handlePointerHoverEvent(PointerEvent evt) {
HitTestResult result = new HitTestResult();
this.hitTest(result, evt.position);
D.assert(this._enteredTargets.Count == 0);
foreach (var hitTestEntry in result.path) {
if (this.lastMoveTargets.Contains(hitTestEntry.target)) {
hitTestEntry.target.handleEvent(evt, hitTestEntry);
this.lastMoveTargets.Remove(hitTestEntry.target);
}
else {
this._enteredTargets.Add(hitTestEntry);
}
}
//leave events
foreach (var lastMoveTarget in this.lastMoveTargets) {
lastMoveTarget.handleEvent(new PointerLeaveEvent(
timeStamp: evt.timeStamp,
pointer: evt.pointer,
device: evt.device,
kind: evt.kind
), null);
}
//enter events
foreach (var hitTestEntry in this._enteredTargets) {
hitTestEntry.target.handleEvent(new PointerEnterEvent(
timeStamp: evt.timeStamp,
pointer: evt.pointer,
device: evt.device,
kind: evt.kind
), hitTestEntry);
}
this.lastMoveTargets.Clear();
foreach (var hitTestEntry in result.path) {
this.lastMoveTargets.Add(hitTestEntry.target);
}
this._enteredTargets.Clear();
this.dispatchEvent(evt, result);
}

34
Runtime/gestures/events.cs


position: position,
down: false) {
}
public static PointerHoverEvent fromHoverEvent(PointerEvent hover) {
return new PointerHoverEvent(
timeStamp: hover.timeStamp,
pointer: hover.pointer,
kind: hover.kind,
device: hover.device,
position: hover.position
);
}
}
public class PointerEnterEvent : PointerEvent {

position: position,
down: false) {
}
public static PointerEnterEvent fromHoverEvent(PointerEvent hover) {
return new PointerEnterEvent(
timeStamp: hover.timeStamp,
pointer: hover.pointer,
kind: hover.kind,
device: hover.device,
position: hover.position
);
}
public class PointerLeaveEvent : PointerEvent {
public PointerLeaveEvent(
public class PointerExitEvent : PointerEvent {
public PointerExitEvent(
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,

device: device,
position: position,
down: false) {
}
public static PointerExitEvent fromHoverEvent(PointerEvent hover) {
return new PointerExitEvent(
timeStamp: hover.timeStamp,
pointer: hover.pointer,
kind: hover.kind,
device: hover.device,
position: hover.position
);
}
}

14
Runtime/rendering/binding.cs


this.initRenderView();
D.assert(this.renderView != null);
this.addPersistentFrameCallback(this._handlePersistentFrameCallback);
this._mouseTracker = this._createMouseTracker();
}
public void initRenderView() {

}
public MouseTracker mouseTracker {
get { return this._mouseTracker; }
}
MouseTracker _mouseTracker;
public PipelineOwner pipelineOwner {
get { return this._pipelineOwner; }

void _handlePersistentFrameCallback(TimeSpan timeStamp) {
this.drawFrame();
}
MouseTracker _createMouseTracker() {
return new MouseTracker(this.pointerRouter, (Offset offset) => {
return this.renderView.layer.find<MouseTrackerAnnotation>(
offset
);
});
}
protected virtual void drawFrame() {

137
Runtime/rendering/layer.cs


using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Unity.UIWidgets.external.simplejson;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;

D.assert(!this.attached);
}
internal abstract S find<S>(Offset regionOffset) where S : class;
internal abstract flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null);
internal void _addToSceneWithRetainedRendering(SceneBuilder builder) {

}
}
internal override S find<S>(Offset regionOffset) {
return null;
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

public readonly bool freeze;
internal override S find<S>(Offset regionOffset) {
return null;
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

public Layer lastChild {
get { return this._lastChild; }
}
internal override S find<S>(Offset regionOffset) {
Layer current = this.lastChild;
while (current != null) {
S value = current.find<S>(regionOffset);
if (value != null) {
return value;
}
current = current.previousSibling;
}
return null;
}
internal Layer _lastChild;

}
}
internal override S find<S>(Offset regionOffset) {
return base.find<S>(regionOffset - this.offset);
}
public override void applyTransform(Layer child, Matrix3 transform) {
D.assert(child != null);
D.assert(transform != null);

}
}
internal override S find<S>(Offset regionOffset) {
if (!this.clipRect.contains(regionOffset)) {
return null;
}
return base.find<S>(regionOffset);
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

}
}
internal override S find<S>(Offset regionOffset) {
if (!this.clipRRect.contains(regionOffset)) {
return null;
}
return base.find<S>(regionOffset);
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

}
}
internal override S find<S>(Offset regionOffset) {
if (!this.clipPath.contains(regionOffset)) {
return null;
}
return base.find<S>(regionOffset);
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

public Matrix3 transform {
get { return this._transform; }
set { this._transform = value; }
set {
this._transform = value;
this._inverseDirty = true;
}
readonly Matrix3 _invertedTransform = Matrix3.I();
bool _inverseDirty = true;
internal override S find<S>(Offset regionOffset) {
if (this._inverseDirty) {
this.transform.invert(this._invertedTransform);
this._inverseDirty = false;
}
if (this._invertedTransform == null) {
return null;
}
Offset transform = this._invertedTransform.mapXY(regionOffset.dx, regionOffset.dy);
return base.find<S>(transform);
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

internal Offset _lastOffset;
internal override S find<S>(Offset regionOffset) {
return base.find<S>(regionOffset - this.offset);
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

Offset _lastOffset;
Matrix3 _lastTransform;
readonly Matrix3 _invertedTransform = Matrix3.I();
bool _inverseDirty = true;
internal override S find<S>(Offset regionOffset) {
if (this.link.leader == null) {
return this.showWhenUnlinked ? base.find<S>(regionOffset - this.unlinkedOffset) : null;
}
if (this._inverseDirty) {
this.getLastTransform().invert(this._invertedTransform);
this._inverseDirty = false;
}
if (this._invertedTransform == null) {
return null;
}
Offset transform = this._invertedTransform.mapXY(regionOffset.dx, regionOffset.dy);
return base.find<S>(transform - this.linkedOffset);
}
public Matrix3 getLastTransform() {
if (this._lastTransform == null) {

inverseTransform.preConcat(forwardTransform);
inverseTransform.preTranslate(this.linkedOffset.dx, this.linkedOffset.dy);
this._lastTransform = inverseTransform;
this._inverseDirty = true;
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

this._lastTransform = null;
this._lastOffset = null;
this._inverseDirty = true;
return null;
}

builder.pop();
}
this._inverseDirty = true;
return null;
}

public readonly int optionsMask;
internal override S find<S>(Offset regionOffset) {
return null;
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

public readonly Offset offset;
internal override S find<S>(Offset regionOffset) {
S result = base.find<S>(regionOffset);
if (result != null) {
return result;
}
if (this.size != null && !(this.offset & this.size).contains(regionOffset)) {
return null;
}
if (typeof(T) == typeof(S)) {
S typedResult = this.value as S;
return typedResult;
}
return base.find<S>(regionOffset);
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<T>("value", this.value));

}
Color _shadowColor;
internal override S find<S>(Offset regionOffset) {
if (!this.clipPath.contains(regionOffset)) {
return null;
}
return base.find<S>(regionOffset);
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

99
Runtime/rendering/paragraph.cs


return;
case RenderComparison.function:
this._textPainter.text = value;
this.markNeedsPaint();
break;
case RenderComparison.paint:
this._textPainter.text = value;

TextSpan _previousHoverSpan;
bool _pointerHoverInside;
bool _hasHoverRecognizer;
MouseTrackerAnnotation _hoverAnnotation;
if (this._hoverAnnotation != null && this.attached) {
RendererBinding.instance.mouseTracker.detachAnnotation(this._hoverAnnotation);
}
if (this._hasHoverRecognizer) {
this._hoverAnnotation = new MouseTrackerAnnotation(
onEnter: this._onPointerEnter,
onHover: this._onPointerHover,
onExit: this._onPointerExit);
if (this.attached) {
RendererBinding.instance.mouseTracker.attachAnnotation(this._hoverAnnotation);
}
}
else {
this._hoverAnnotation = null;
}
}
void _handleKeyEvent(RawKeyEvent keyEvent) {

}
}
public override void attach(object owner) {
base.attach(owner);
if (this._hoverAnnotation != null) {
RendererBinding.instance.mouseTracker.attachAnnotation(this._hoverAnnotation);
}
}
public override void detach() {
if (this._listenerAttached) {
RawKeyboard.instance.removeListener(this._handleKeyEvent);

if (this._hoverAnnotation != null) {
RendererBinding.instance.mouseTracker.detachAnnotation(this._hoverAnnotation);
}
}
TextSelection _selection;

this.onSelectionChanged?.Invoke();
}
void _onPointerEnter(PointerEvent evt) {
this._pointerHoverInside = true;
}
void _handlePointerHover(PointerEvent evt) {
if (!this._hasHoverRecognizer) {
return;
}
void _onPointerExit(PointerEvent evt) {
this._pointerHoverInside = false;
this._previousHoverSpan?.hoverRecognizer?.OnPointerLeave?.Invoke();
this._previousHoverSpan = null;
}
if (evt is PointerEnterEvent) {
this._pointerHoverInside = true;
}
else if (evt is PointerLeaveEvent) {
this._pointerHoverInside = false;
void _onPointerHover(PointerEvent evt) {
this._layoutTextWithConstraints(this.constraints);
Offset offset = this.globalToLocal(evt.position);
TextPosition position = this._textPainter.getPositionForOffset(offset);
TextSpan span = this._textPainter.text.getSpanForPosition(position);
if (this._previousHoverSpan != span) {
this._previousHoverSpan = null;
}
else if (evt is PointerHoverEvent && this._pointerHoverInside) {
this._layoutTextWithConstraints(this.constraints);
Offset offset = this.globalToLocal(evt.position);
TextPosition position = this._textPainter.getPositionForOffset(offset);
TextSpan span = this._textPainter.text.getSpanForPosition(position);
if (this._previousHoverSpan != span) {
this._previousHoverSpan?.hoverRecognizer?.OnPointerLeave?.Invoke();
span?.hoverRecognizer?.OnPointerEnter?.Invoke((PointerHoverEvent) evt);
this._previousHoverSpan = span;
}
span?.hoverRecognizer?.OnPointerEnter?.Invoke((PointerHoverEvent) evt);
this._previousHoverSpan = span;
if (evt is PointerDownEvent) {
this._layoutTextWithConstraints(this.constraints);
Offset offset = ((BoxHitTestEntry) entry).localPosition;
TextPosition position = this._textPainter.getPositionForOffset(offset);
TextSpan span = this._textPainter.text.getSpanForPosition(position);
span?.recognizer?.addPointer((PointerDownEvent) evt);
if (!(evt is PointerDownEvent)) {
this._handlePointerHover(evt);
this._layoutTextWithConstraints(this.constraints);
Offset offset = ((BoxHitTestEntry) entry).localPosition;
TextPosition position = this._textPainter.getPositionForOffset(offset);
TextSpan span = this._textPainter.text.getSpanForPosition(position);
span?.recognizer?.addPointer((PointerDownEvent) evt);
}
protected override void performLayout() {

this._selectionRects = null;
}
public override void paint(PaintingContext context, Offset offset) {
void paintParagraph(PaintingContext context, Offset offset) {
this._layoutTextWithConstraints(this.constraints);
var canvas = context.canvas;

this._textPainter.paint(canvas, offset);
if (this._hasVisualOverflow) {
canvas.restore();
}
}
public override void paint(PaintingContext context, Offset offset) {
if (this._hoverAnnotation != null) {
AnnotatedRegionLayer<MouseTrackerAnnotation> layer = new AnnotatedRegionLayer<MouseTrackerAnnotation>(
this._hoverAnnotation, size: this.size, offset: offset);
context.pushLayer(layer, this.paintParagraph, offset);
}
else {
this.paintParagraph(context, offset);
}
}

160
Runtime/rendering/proxy_box.cs


public delegate void PointerCancelEventListener(PointerCancelEvent evt);
public delegate void PointerHoverEventListener(PointerHoverEvent evt);
public delegate void PointerEnterEventListener(PointerEnterEvent evt);
public delegate void PointerLeaveEventListener(PointerLeaveEvent evt);
public delegate void PointerScrollEventListener(PointerScrollEvent evt);
public class RenderPointerListener : RenderProxyBoxWithHitTestBehavior {

PointerEnterEventListener onPointerEnter = null,
PointerHoverEventListener onPointerHover = null,
PointerExitEventListener onPointerExit = null,
PointerHoverEventListener onPointerHover = null,
PointerLeaveEventListener onPointerLeave = null,
PointerEnterEventListener onPointerEnter = null,
PointerScrollEventListener onPointerScroll = null,
HitTestBehavior behavior = HitTestBehavior.deferToChild,
RenderBox child = null

this.onPointerUp = onPointerUp;
this.onPointerCancel = onPointerCancel;
this.onPointerHover = onPointerHover;
this.onPointerLeave = onPointerLeave;
this.onPointerEnter = onPointerEnter;
this._onPointerEnter = onPointerEnter;
this._onPointerHover = onPointerHover;
this._onPointerExit = onPointerExit;
if (this._onPointerEnter != null || this._onPointerHover != null || this._onPointerExit != null) {
this._hoverAnnotation = new MouseTrackerAnnotation(
onEnter: this._onPointerEnter,
onHover: this._onPointerHover,
onExit: this._onPointerExit);
}
}
public PointerDownEventListener onPointerDown;

public PointerEnterEventListener onPointerEnter {
get { return this._onPointerEnter; }
set {
if (this._onPointerEnter != value) {
this._onPointerEnter = value;
this._updateAnnotations();
}
}
}
PointerEnterEventListener _onPointerEnter;
public PointerHoverEventListener onPointerHover {
get { return this._onPointerHover; }
set {
if (this._onPointerHover != value) {
this._onPointerHover = value;
this._updateAnnotations();
}
}
}
PointerHoverEventListener _onPointerHover;
public PointerExitEventListener onPointerExit {
get { return this._onPointerExit; }
set {
if (this._onPointerExit != value) {
this._onPointerExit = value;
this._updateAnnotations();
}
}
}
PointerExitEventListener _onPointerExit;
public PointerHoverEventListener onPointerHover;
public PointerScrollEventListener onPointerScroll;
public PointerLeaveEventListener onPointerLeave;
MouseTrackerAnnotation _hoverAnnotation;
public PointerEnterEventListener onPointerEnter;
void _updateAnnotations() {
D.assert(this._onPointerEnter != this._hoverAnnotation.onEnter ||
this._onPointerHover != this._hoverAnnotation.onHover ||
this._onPointerExit != this._hoverAnnotation.onExit,
() => "Shouldn't call _updateAnnotations if nothing has changed.");
public PointerScrollEventListener onPointerScroll;
if (this._hoverAnnotation != null && this.attached) {
RendererBinding.instance.mouseTracker.detachAnnotation(this._hoverAnnotation);
}
if (this._onPointerEnter != null || this._onPointerHover != null || this._onPointerExit != null) {
this._hoverAnnotation = new MouseTrackerAnnotation(
onEnter: this._onPointerEnter,
onHover: this._onPointerHover,
onExit: this._onPointerExit);
if (this.attached) {
RendererBinding.instance.mouseTracker.attachAnnotation(this._hoverAnnotation);
}
}
else {
this._hoverAnnotation = null;
}
this.markNeedsPaint();
}
public override void attach(object owner) {
base.attach(owner);
if (this._hoverAnnotation != null) {
RendererBinding.instance.mouseTracker.attachAnnotation(this._hoverAnnotation);
}
}
public override void detach() {
base.detach();
if (this._hoverAnnotation != null) {
RendererBinding.instance.mouseTracker.detachAnnotation(this._hoverAnnotation);
}
}
public override void paint(PaintingContext context, Offset offset) {
if (this._hoverAnnotation != null) {
AnnotatedRegionLayer<MouseTrackerAnnotation> layer = new AnnotatedRegionLayer<MouseTrackerAnnotation>(
this._hoverAnnotation, size: this.size, offset: offset);
context.pushLayer(layer, base.paint, offset);
}
else {
base.paint(context, offset);
}
}
protected override void performResize() {
this.size = this.constraints.biggest;

return;
}
if (this.onPointerHover != null && evt is PointerHoverEvent) {
this.onPointerHover((PointerHoverEvent) evt);
return;
}
if (this.onPointerLeave != null && evt is PointerLeaveEvent) {
this.onPointerLeave((PointerLeaveEvent) evt);
return;
}
if (this.onPointerEnter != null && evt is PointerEnterEvent) {
this.onPointerEnter((PointerEnterEvent) evt);
return;
}
if (this.onPointerScroll != null && evt is PointerScrollEvent) {
this.onPointerScroll((PointerScrollEvent) evt);
}

listeners.Add("move");
}
if (this.onPointerUp != null) {
listeners.Add("up");
if (this.onPointerEnter != null) {
listeners.Add("enter");
if (this.onPointerCancel != null) {
listeners.Add("cancel");
if (this.onPointerHover != null) {
listeners.Add("hover");
if (this.onPointerHover != null) {
listeners.Add("hover");
if (this.onPointerExit != null) {
listeners.Add("exit");
if (this.onPointerEnter != null) {
listeners.Add("enter");
if (this.onPointerUp != null) {
listeners.Add("up");
if (this.onPointerLeave != null) {
listeners.Add("leave");
if (this.onPointerCancel != null) {
listeners.Add("cancel");
}
if (listeners.isEmpty()) {

public override void paint(PaintingContext context, Offset offset) {
AnnotatedRegionLayer<T> layer =
new AnnotatedRegionLayer<T>(value: this.value, size: this.sized ? this.size : null);
new AnnotatedRegionLayer<T>(
value: this.value,
size: this.sized ? this.size : null,
offset: this.sized ? offset : null);
context.pushLayer(layer, base.paint, offset);
}
}

19
Runtime/widgets/basic.cs


using System.Linq;
using UIWidgets.Runtime.rendering;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;

Key key = null,
PointerDownEventListener onPointerDown = null,
PointerMoveEventListener onPointerMove = null,
PointerEnterEventListener onPointerEnter = null,
PointerExitEventListener onPointerExit = null,
PointerHoverEventListener onPointerHover = null,
PointerHoverEventListener onPointerHover = null,
PointerLeaveEventListener onPointerLeave = null,
PointerEnterEventListener onPointerEnter = null,
PointerScrollEventListener onPointerScroll = null,
HitTestBehavior behavior = HitTestBehavior.deferToChild,
Widget child = null

this.onPointerUp = onPointerUp;
this.onPointerCancel = onPointerCancel;
this.onPointerHover = onPointerHover;
this.onPointerLeave = onPointerLeave;
this.onPointerExit = onPointerExit;
this.onPointerEnter = onPointerEnter;
this.onPointerScroll = onPointerScroll;
this.behavior = behavior;

public readonly PointerEnterEventListener onPointerEnter;
public readonly PointerLeaveEventListener onPointerLeave;
public readonly PointerExitEventListener onPointerExit;
public readonly PointerScrollEventListener onPointerScroll;

onPointerUp: this.onPointerUp,
onPointerCancel: this.onPointerCancel,
onPointerEnter: this.onPointerEnter,
onPointerLeave: this.onPointerLeave,
onPointerExit: this.onPointerExit,
onPointerHover: this.onPointerHover,
onPointerScroll: this.onPointerScroll,
behavior: this.behavior

renderObject.onPointerCancel = this.onPointerCancel;
renderObject.onPointerEnter = this.onPointerEnter;
renderObject.onPointerHover = this.onPointerHover;
renderObject.onPointerLeave = this.onPointerLeave;
renderObject.onPointerExit = this.onPointerExit;
renderObject.onPointerScroll = this.onPointerScroll;
renderObject.behavior = this.behavior;
}

listeners.Add("hover");
}
if (this.onPointerLeave != null) {
listeners.Add("leave");
if (this.onPointerExit != null) {
listeners.Add("exit");
}
if (this.onPointerScroll != null) {

3
Runtime/widgets/widget_inspector.cs


// public TextPainter _textPainter;
// public float _textPainterMaxWidth;
internal override S find<S>(Offset regionOffset) {
return null;
}
internal override flow.Layer addToScene(SceneBuilder builder, Offset layerOffset = null) {
layerOffset = layerOffset ?? Offset.zero;

15
Tests/Editor/MouseHover.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
namespace UIWidgets.Tests {
public class MouseHoverWidget : StatefulWidget {

Widget result = new Container(width: 200, height: 60, color: Color.fromARGB(255, 255, 0, 255));
if (canHover) {
result = new HoverTrackWidget(null,
result);
result, "inner");
}
if (nest) {

result);
result, "outer");
}
return result;

public class HoverTrackWidget : StatefulWidget {
public readonly Widget child;
public readonly string name;
public HoverTrackWidget(Key key, Widget child) : base(key) {
public HoverTrackWidget(Key key, Widget child, string name) : base(key) {
this.name = name;
}
public override State createState() {

),
onPointerEnter: (evt) => {
if (this.mounted) {
Debug.Log(this.widget.name + " pointer enter");
onPointerLeave: (evt) => {
onPointerExit: (evt) => {
Debug.Log(this.widget.name + " pointer exit");
this.setState(() => { this.hover = false; });
}
}

201
Runtime/gestures/mouse_tracking.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.gestures {
public delegate void PointerHoverEventListener(PointerHoverEvent evt);
public delegate void PointerEnterEventListener(PointerEnterEvent evt);
public delegate void PointerExitEventListener(PointerExitEvent evt);
public class MouseTrackerAnnotation {
public MouseTrackerAnnotation(
PointerEnterEventListener onEnter = null,
PointerHoverEventListener onHover = null,
PointerExitEventListener onExit = null
) {
this.onEnter = onEnter;
this.onHover = onHover;
this.onExit = onExit;
}
public readonly PointerEnterEventListener onEnter;
public readonly PointerHoverEventListener onHover;
public readonly PointerExitEventListener onExit;
public override string ToString() {
return $"{this.GetType()}#{this.GetHashCode()}{(this.onEnter == null ? "" : " onEnter")}{(this.onHover == null ? "" : " onHover")}{(this.onExit == null ? "" : " onExit")}";
}
}
public class _TrackedAnnotation {
public _TrackedAnnotation(
MouseTrackerAnnotation annotation) {
this.annotation = annotation;
}
public readonly MouseTrackerAnnotation annotation;
public HashSet<int> activeDevices = new HashSet<int>();
}
public delegate MouseTrackerAnnotation MouseDetectorAnnotationFinder(Offset offset);
public class MouseTracker {
public MouseTracker(
PointerRouter router,
MouseDetectorAnnotationFinder annotationFinder) {
router.addGlobalRoute(this._handleEvent);
this.annotationFinder = annotationFinder;
}
readonly Dictionary<int, PointerEvent> _lastMouseEvent = new Dictionary<int, PointerEvent>();
public bool mouseIsConnected {
get { return this._lastMouseEvent.isNotEmpty(); }
}
public readonly MouseDetectorAnnotationFinder annotationFinder;
public readonly Dictionary<MouseTrackerAnnotation, _TrackedAnnotation> _trackedAnnotations =
new Dictionary<MouseTrackerAnnotation, _TrackedAnnotation>();
public void attachAnnotation(MouseTrackerAnnotation annotation) {
this._trackedAnnotations[annotation] = new _TrackedAnnotation(annotation);
this._scheduleMousePositionCheck();
}
public void detachAnnotation(MouseTrackerAnnotation annotation) {
_TrackedAnnotation trackedAnnotation = this._findAnnotation(annotation);
foreach (int deviceId in trackedAnnotation.activeDevices) {
annotation.onExit(PointerExitEvent.fromHoverEvent((PointerHoverEvent) this._lastMouseEvent[deviceId]));
}
this._trackedAnnotations.Remove(annotation);
}
void _scheduleMousePositionCheck() {
SchedulerBinding.instance.addPostFrameCallback(_ => { this.collectMousePositions();});
SchedulerBinding.instance.scheduleFrame();
}
void _handleEvent(PointerEvent evt) {
if (evt.kind != PointerDeviceKind.mouse) {
return;
}
int deviceId = evt.device;
if (this._trackedAnnotations.isEmpty()) {
this._lastMouseEvent.Remove(deviceId);
return;
}
if (evt is PointerRemovedEvent) {
this._lastMouseEvent.Remove(deviceId);
this._scheduleMousePositionCheck();
}
else {
if (evt is PointerMoveEvent ||
evt is PointerHoverEvent ||
evt is PointerDownEvent) {
if (!this._lastMouseEvent.ContainsKey(deviceId) ||
this._lastMouseEvent[deviceId].position != evt.position) {
this._scheduleMousePositionCheck();
}
this._lastMouseEvent[deviceId] = evt;
}
}
}
_TrackedAnnotation _findAnnotation(MouseTrackerAnnotation annotation) {
if (!this._trackedAnnotations.TryGetValue(annotation, out var trackedAnnotation)) {
D.assert(false, () => "Unable to find annotation $annotation in tracked annotations. " +
"Check that attachAnnotation has been called for all annotated layers.");
}
return trackedAnnotation;
}
bool isAnnotationAttached(MouseTrackerAnnotation annotation) {
return this._trackedAnnotations.ContainsKey(annotation);
}
public void collectMousePositions() {
void exitAnnotation(_TrackedAnnotation trackedAnnotation, int deviceId) {
if (trackedAnnotation.annotation?.onExit != null &&
trackedAnnotation.activeDevices.Contains(deviceId)) {
trackedAnnotation.annotation.onExit(PointerExitEvent.fromHoverEvent(this._lastMouseEvent[deviceId]));
trackedAnnotation.activeDevices.Remove(deviceId);
}
}
void exitAllDevices(_TrackedAnnotation trackedAnnotation) {
if (trackedAnnotation.activeDevices.isNotEmpty()) {
HashSet<int> deviceIds = new HashSet<int>(trackedAnnotation.activeDevices);
foreach (int deviceId in deviceIds) {
exitAnnotation(trackedAnnotation, deviceId);
}
}
}
if (!this.mouseIsConnected) {
foreach (var annotation in this._trackedAnnotations.Values) {
exitAllDevices(annotation);
}
return;
}
foreach (int deviceId in this._lastMouseEvent.Keys) {
PointerEvent lastEvent = this._lastMouseEvent[deviceId];
MouseTrackerAnnotation hit = this.annotationFinder(lastEvent.position);
if (hit == null) {
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
exitAnnotation(trackedAnnotation, deviceId);
}
return;
}
_TrackedAnnotation hitAnnotation = this._findAnnotation(hit);
//enter
if (!hitAnnotation.activeDevices.Contains(deviceId)) {
hitAnnotation.activeDevices.Add(deviceId);
if (hitAnnotation.annotation?.onEnter != null) {
hitAnnotation.annotation.onEnter(PointerEnterEvent.fromHoverEvent(lastEvent));
}
}
//hover
if (hitAnnotation.annotation?.onHover != null) {
hitAnnotation.annotation.onHover(PointerHoverEvent.fromHoverEvent(lastEvent));
}
//leave
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
if (hitAnnotation == trackedAnnotation) {
continue;
}
if (trackedAnnotation.activeDevices.Contains(deviceId)) {
if (trackedAnnotation.annotation?.onExit != null) {
trackedAnnotation.annotation.onExit(PointerExitEvent.fromHoverEvent((PointerHoverEvent)lastEvent));
}
trackedAnnotation.activeDevices.Remove(deviceId);
}
}
}
}
}
}

11
Runtime/gestures/mouse_tracking.cs.meta


fileFormatVersion: 2
guid: 635ab9c5cd0cd4509989d0d108b8b530
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存