浏览代码
Merge branch 'dragdrop_new' into 'master'
Merge branch 'dragdrop_new' into 'master'
dragdrop codes: fix Axis.none, add drag_utils and format problems See merge request upm-packages/ui-widgets/com.unity.uiwidgets!18/main
Shenhua Gu
6 年前
当前提交
7d789cc4
共有 13 个文件被更改,包括 1304 次插入 和 5 次删除
-
17Runtime/rendering/proxy_box.cs
-
33Runtime/widgets/basic.cs
-
104Samples/UIWidgetSample/UIWidgetSample.unity
-
398Runtime/gestures/multidrag.cs
-
11Runtime/gestures/multidrag.cs.meta
-
8Runtime/utils.meta
-
545Runtime/widgets/drag_target.cs
-
11Runtime/widgets/drag_target.cs.meta
-
149Samples/UIWidgetSample/DragDropCanvas.cs
-
11Samples/UIWidgetSample/DragDropCanvas.cs.meta
-
11Runtime/utils/drag_utils.cs
-
11Runtime/utils/drag_utils.cs.meta
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.async; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.gestures { |
|||
public delegate Drag GestureMultiDragStartCallback(Offset position); |
|||
|
|||
public abstract class MultiDragPointerState { |
|||
public MultiDragPointerState( |
|||
Offset initialPosition = null) { |
|||
D.assert(initialPosition != null); |
|||
this.initialPosition = initialPosition; |
|||
} |
|||
|
|||
|
|||
public readonly Offset initialPosition; |
|||
|
|||
readonly VelocityTracker _velocityTracker = new VelocityTracker(); |
|||
|
|||
Drag _client; |
|||
|
|||
public Offset pendingDelta => this._pendingDelta; |
|||
|
|||
public Offset _pendingDelta = Offset.zero; |
|||
|
|||
TimeSpan? _lastPendingEventTimestamp; |
|||
|
|||
GestureArenaEntry _arenaEntry; |
|||
|
|||
public void _setArenaEntry(GestureArenaEntry entry) { |
|||
D.assert(this._arenaEntry == null); |
|||
D.assert(this.pendingDelta != null); |
|||
D.assert(this._client == null); |
|||
this._arenaEntry = entry; |
|||
} |
|||
|
|||
protected void resolve(GestureDisposition disposition) { |
|||
this._arenaEntry.resolve(disposition); |
|||
} |
|||
|
|||
public void _move(PointerMoveEvent pEvent) { |
|||
D.assert(this._arenaEntry != null); |
|||
if (!pEvent.synthesized) |
|||
this._velocityTracker.addPosition(pEvent.timeStamp, pEvent.position); |
|||
if (this._client != null) { |
|||
D.assert(this.pendingDelta == null); |
|||
this._client.update(new DragUpdateDetails( |
|||
sourceTimeStamp: pEvent.timeStamp, |
|||
delta: pEvent.delta, |
|||
globalPosition: pEvent.position |
|||
)); |
|||
} |
|||
else { |
|||
D.assert(this.pendingDelta != null); |
|||
this._pendingDelta += pEvent.delta; |
|||
this._lastPendingEventTimestamp = pEvent.timeStamp; |
|||
this.checkForResolutionAfterMove(); |
|||
} |
|||
} |
|||
|
|||
public virtual void checkForResolutionAfterMove() { |
|||
} |
|||
|
|||
public abstract void accepted(GestureMultiDragStartCallback starter); |
|||
|
|||
public void rejected() { |
|||
D.assert(this._arenaEntry != null); |
|||
D.assert(this._client == null); |
|||
D.assert(this.pendingDelta != null); |
|||
this._pendingDelta = null; |
|||
this._lastPendingEventTimestamp = null; |
|||
this._arenaEntry = null; |
|||
} |
|||
|
|||
public void _startDrag(Drag client) { |
|||
D.assert(this._arenaEntry != null); |
|||
D.assert(this._client == null); |
|||
D.assert(client != null); |
|||
D.assert(this.pendingDelta != null); |
|||
|
|||
this._client = client; |
|||
DragUpdateDetails details = new DragUpdateDetails( |
|||
sourceTimeStamp: this._lastPendingEventTimestamp ?? TimeSpan.Zero, |
|||
this.pendingDelta, |
|||
globalPosition: this.initialPosition |
|||
); |
|||
|
|||
this._pendingDelta = null; |
|||
this._lastPendingEventTimestamp = null; |
|||
this._client.update(details); |
|||
} |
|||
|
|||
public void _up() { |
|||
D.assert(this._arenaEntry != null); |
|||
if (this._client != null) { |
|||
D.assert(this.pendingDelta == null); |
|||
DragEndDetails details = new DragEndDetails(velocity: this._velocityTracker.getVelocity()); |
|||
Drag client = this._client; |
|||
this._client = null; |
|||
client.end(details); |
|||
} |
|||
else { |
|||
D.assert(this.pendingDelta != null); |
|||
this._pendingDelta = null; |
|||
this._lastPendingEventTimestamp = null; |
|||
} |
|||
} |
|||
|
|||
public void _cancel() { |
|||
D.assert(this._arenaEntry != null); |
|||
if (this._client != null) { |
|||
D.assert(this.pendingDelta == null); |
|||
Drag client = this._client; |
|||
this._client = null; |
|||
client.cancel(); |
|||
} |
|||
else { |
|||
D.assert(this.pendingDelta != null); |
|||
this._pendingDelta = null; |
|||
this._lastPendingEventTimestamp = null; |
|||
} |
|||
} |
|||
|
|||
public virtual void dispose() { |
|||
this._arenaEntry?.resolve(GestureDisposition.rejected); |
|||
this._arenaEntry = null; |
|||
D.assert(() => { |
|||
this._pendingDelta = null; |
|||
return true; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
|
|||
public abstract class MultiDragGestureRecognizer<T> : GestureRecognizer where T : MultiDragPointerState { |
|||
protected MultiDragGestureRecognizer( |
|||
object debugOwner) : base(debugOwner: debugOwner) { |
|||
} |
|||
|
|||
public GestureMultiDragStartCallback onStart; |
|||
|
|||
Dictionary<int, T> _pointers = new Dictionary<int, T>(); |
|||
|
|||
public override void addPointer(PointerDownEvent pEvent) { |
|||
D.assert(this._pointers != null); |
|||
D.assert(pEvent.pointer != null); |
|||
D.assert(pEvent.position != null); |
|||
D.assert(!this._pointers.ContainsKey(pEvent.pointer)); |
|||
|
|||
T state = this.createNewPointerState(pEvent); |
|||
this._pointers[pEvent.pointer] = state; |
|||
GestureBinding.instance.pointerRouter.addRoute(pEvent.pointer, this._handleEvent); |
|||
state._setArenaEntry(GestureBinding.instance.gestureArena.add(pEvent.pointer, this)); |
|||
} |
|||
|
|||
public abstract T createNewPointerState(PointerDownEvent pEvent); |
|||
|
|||
void _handleEvent(PointerEvent pEvent) { |
|||
D.assert(this._pointers != null); |
|||
D.assert(pEvent.pointer != null); |
|||
D.assert(pEvent.timeStamp != null); |
|||
D.assert(pEvent.position != null); |
|||
D.assert(this._pointers.ContainsKey(pEvent.pointer)); |
|||
|
|||
T state = this._pointers[pEvent.pointer]; |
|||
if (pEvent is PointerMoveEvent) { |
|||
state._move((PointerMoveEvent) pEvent); |
|||
} |
|||
else if (pEvent is PointerUpEvent) { |
|||
D.assert(pEvent.delta == Offset.zero); |
|||
state._up(); |
|||
this._removeState(pEvent.pointer); |
|||
} |
|||
else if (pEvent is PointerCancelEvent) { |
|||
D.assert(pEvent.delta == Offset.zero); |
|||
state._cancel(); |
|||
this._removeState(pEvent.pointer); |
|||
} |
|||
else if (!(pEvent is PointerDownEvent)) { |
|||
D.assert(false); |
|||
} |
|||
} |
|||
|
|||
public override void acceptGesture(int pointer) { |
|||
D.assert(this._pointers != null); |
|||
T state = this._pointers[pointer]; |
|||
if (state == null) |
|||
return; |
|||
state.accepted((Offset initialPosition) => this._startDrag(initialPosition, pointer)); |
|||
} |
|||
|
|||
Drag _startDrag(Offset initialPosition, int pointer) { |
|||
D.assert(this._pointers != null); |
|||
T state = this._pointers[pointer]; |
|||
D.assert(state != null); |
|||
D.assert(state._pendingDelta != null); |
|||
Drag drag = null; |
|||
if (this.onStart != null) |
|||
drag = this.invokeCallback("onStart", () => this.onStart(initialPosition)); |
|||
if (drag != null) |
|||
state._startDrag(drag); |
|||
else |
|||
this._removeState(pointer); |
|||
return drag; |
|||
} |
|||
|
|||
public override void rejectGesture(int pointer) { |
|||
D.assert(this._pointers != null); |
|||
if (this._pointers.ContainsKey(pointer)) { |
|||
T state = this._pointers[pointer]; |
|||
D.assert(state != null); |
|||
state.rejected(); |
|||
this._removeState(pointer); |
|||
} |
|||
} |
|||
|
|||
void _removeState(int pointer) { |
|||
if (this._pointers == null) |
|||
return; |
|||
D.assert(this._pointers.ContainsKey(pointer)); |
|||
GestureBinding.instance.pointerRouter.removeRoute(pointer, this._handleEvent); |
|||
this._pointers[pointer].dispose(); |
|||
this._pointers.Remove(pointer); |
|||
} |
|||
|
|||
|
|||
public override void dispose() { |
|||
this._pointers.Keys.ToList().ForEach(this._removeState); |
|||
D.assert(this._pointers.isEmpty); |
|||
this._pointers = null; |
|||
base.dispose(); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class _ImmediatePointerState : MultiDragPointerState { |
|||
public _ImmediatePointerState(Offset initialPosition) : base(initialPosition) { |
|||
} |
|||
|
|||
public override void checkForResolutionAfterMove() { |
|||
D.assert(this.pendingDelta != null); |
|||
if (this.pendingDelta.distance > Constants.kTouchSlop) |
|||
this.resolve(GestureDisposition.accepted); |
|||
} |
|||
|
|||
public override void accepted(GestureMultiDragStartCallback starter) { |
|||
starter(this.initialPosition); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class ImmediateMultiDragGestureRecognizer : MultiDragGestureRecognizer<_ImmediatePointerState> { |
|||
public ImmediateMultiDragGestureRecognizer(object debugOwner) : base(debugOwner: debugOwner) { |
|||
} |
|||
|
|||
public override _ImmediatePointerState createNewPointerState(PointerDownEvent pEvent) { |
|||
return new _ImmediatePointerState(pEvent.position); |
|||
} |
|||
|
|||
public override string debugDescription => "multidrag"; |
|||
} |
|||
|
|||
public class _HorizontalPointerState : MultiDragPointerState { |
|||
public _HorizontalPointerState(Offset initialPosition) : base(initialPosition) { |
|||
} |
|||
|
|||
public override void checkForResolutionAfterMove() { |
|||
D.assert(this.pendingDelta != null); |
|||
if (this.pendingDelta.dx.abs() > Constants.kTouchSlop) |
|||
this.resolve(GestureDisposition.accepted); |
|||
} |
|||
|
|||
public override void accepted(GestureMultiDragStartCallback starter) { |
|||
starter(this.initialPosition); |
|||
} |
|||
} |
|||
|
|||
public class HorizontalMultiDragGestureRecognizer : MultiDragGestureRecognizer<_HorizontalPointerState> { |
|||
public HorizontalMultiDragGestureRecognizer(object debugOwner) : base(debugOwner: debugOwner) { |
|||
} |
|||
|
|||
public override _HorizontalPointerState createNewPointerState(PointerDownEvent pEvent) { |
|||
return new _HorizontalPointerState(pEvent.position); |
|||
} |
|||
|
|||
public override string debugDescription => "horizontal multidrag"; |
|||
} |
|||
|
|||
|
|||
public class _VerticalPointerState : MultiDragPointerState { |
|||
public _VerticalPointerState(Offset initialPosition) : base(initialPosition) { |
|||
} |
|||
|
|||
public override void checkForResolutionAfterMove() { |
|||
D.assert(this.pendingDelta != null); |
|||
if (this.pendingDelta.dy.abs() > Constants.kTouchSlop) |
|||
this.resolve(GestureDisposition.accepted); |
|||
} |
|||
|
|||
public override void accepted(GestureMultiDragStartCallback starter) { |
|||
starter(this.initialPosition); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class VerticalMultiDragGestureRecognizer : MultiDragGestureRecognizer<_VerticalPointerState> { |
|||
public VerticalMultiDragGestureRecognizer(object debugOwner) : base(debugOwner: debugOwner) { |
|||
} |
|||
|
|||
public override _VerticalPointerState createNewPointerState(PointerDownEvent pEvent) { |
|||
return new _VerticalPointerState(pEvent.position); |
|||
} |
|||
|
|||
public override string debugDescription => "vertical multidrag"; |
|||
} |
|||
|
|||
public class _DelayedPointerState : MultiDragPointerState { |
|||
public _DelayedPointerState( |
|||
Offset initialPosition = null, |
|||
TimeSpan? delay = null) |
|||
: base(initialPosition) { |
|||
D.assert(delay != null); |
|||
this._timer = Window.instance.run(delay ?? Constants.kLongPressTimeout, this._delayPassed, true); |
|||
} |
|||
|
|||
Timer _timer; |
|||
GestureMultiDragStartCallback _starter; |
|||
|
|||
void _delayPassed() { |
|||
D.assert(this._timer != null); |
|||
D.assert(this.pendingDelta != null); |
|||
D.assert(this.pendingDelta.distance <= Constants.kTouchSlop); |
|||
this._timer = null; |
|||
if (this._starter != null) { |
|||
this._starter(this.initialPosition); |
|||
this._starter = null; |
|||
} |
|||
else { |
|||
this.resolve(GestureDisposition.accepted); |
|||
} |
|||
|
|||
D.assert(this._starter == null); |
|||
} |
|||
|
|||
void _ensureTimerStopped() { |
|||
this._timer?.cancel(); |
|||
this._timer = null; |
|||
} |
|||
|
|||
public override void accepted(GestureMultiDragStartCallback starter) { |
|||
D.assert(this._starter == null); |
|||
if (this._timer == null) |
|||
starter(this.initialPosition); |
|||
else |
|||
this._starter = starter; |
|||
} |
|||
|
|||
public override void checkForResolutionAfterMove() { |
|||
if (this._timer == null) { |
|||
D.assert(this._starter != null); |
|||
return; |
|||
} |
|||
|
|||
D.assert(this.pendingDelta != null); |
|||
if (this.pendingDelta.distance > Constants.kTouchSlop) { |
|||
this.resolve(GestureDisposition.rejected); |
|||
this._ensureTimerStopped(); |
|||
} |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._ensureTimerStopped(); |
|||
base.dispose(); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class DelayedMultiDragGestureRecognizer : MultiDragGestureRecognizer<_DelayedPointerState> { |
|||
public DelayedMultiDragGestureRecognizer( |
|||
TimeSpan? delay = null, |
|||
object debugOwner = null) : base(debugOwner: debugOwner) { |
|||
if (delay == null) |
|||
delay = Constants.kLongPressTimeout; |
|||
this.delay = delay; |
|||
} |
|||
|
|||
readonly TimeSpan? delay; |
|||
|
|||
public override _DelayedPointerState createNewPointerState(PointerDownEvent pEvent) { |
|||
return new _DelayedPointerState(pEvent.position, this.delay); |
|||
} |
|||
|
|||
public override string debugDescription => "long multidrag"; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5eacb7e32ccac411abd94d3b74872861 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 963d1b7d830374b3a915967d4721dc4a |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.utils; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public delegate bool DragTargetWillAccept<T>(T data); |
|||
|
|||
public delegate void DragTargetAccept<T>(T data); |
|||
|
|||
public delegate Widget DragTargetBuilder<T>(BuildContext context, List<T> candidateData, List<T> rejectedData); |
|||
|
|||
public delegate void DraggableCanceledCallback(Velocity velocity, Offset offset); |
|||
|
|||
public delegate void DragEndCallback(DraggableDetails details); |
|||
|
|||
public delegate void DragTargetLeave<T>(T data); |
|||
|
|||
public enum DragAnchor { |
|||
child, |
|||
pointer |
|||
} |
|||
|
|||
public class Draggable<T> : StatefulWidget { |
|||
public Draggable( |
|||
T data, |
|||
Widget child, |
|||
Widget feedback, |
|||
Key key = null, |
|||
Axis? axis = null, |
|||
Widget childWhenDragging = null, |
|||
Offset feedbackOffset = null, |
|||
DragAnchor dragAnchor = DragAnchor.child, |
|||
Axis? affinity = null, |
|||
int? maxSimultaneousDrags = null, |
|||
VoidCallback onDragStarted = null, |
|||
DraggableCanceledCallback onDraggableCanceled = null, |
|||
DragEndCallback onDragEnd = null, |
|||
VoidCallback onDragCompleted = null) : base(key) { |
|||
D.assert(child != null); |
|||
D.assert(feedback != null); |
|||
D.assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0); |
|||
|
|||
this.child = child; |
|||
this.feedback = feedback; |
|||
this.data = data; |
|||
this.axis = axis; |
|||
this.childWhenDragging = childWhenDragging; |
|||
if (feedbackOffset == null) |
|||
feedbackOffset = Offset.zero; |
|||
this.feedbackOffset = feedbackOffset; |
|||
this.dragAnchor = dragAnchor; |
|||
this.affinity = affinity; |
|||
this.maxSimultaneousDrags = maxSimultaneousDrags; |
|||
this.onDragStarted = onDragStarted; |
|||
this.onDraggableCanceled = onDraggableCanceled; |
|||
this.onDragEnd = onDragEnd; |
|||
this.onDragCompleted = onDragCompleted; |
|||
} |
|||
|
|||
public readonly T data; |
|||
|
|||
public readonly Axis? axis; |
|||
|
|||
public readonly Widget child; |
|||
|
|||
public readonly Widget childWhenDragging; |
|||
|
|||
public readonly Widget feedback; |
|||
|
|||
public readonly Offset feedbackOffset; |
|||
|
|||
public readonly DragAnchor dragAnchor; |
|||
|
|||
readonly Axis? affinity; |
|||
|
|||
public readonly int? maxSimultaneousDrags; |
|||
|
|||
public readonly VoidCallback onDragStarted; |
|||
|
|||
public readonly DraggableCanceledCallback onDraggableCanceled; |
|||
|
|||
public readonly VoidCallback onDragCompleted; |
|||
|
|||
public readonly DragEndCallback onDragEnd; |
|||
|
|||
|
|||
public virtual GestureRecognizer createRecognizer(GestureMultiDragStartCallback onStart) { |
|||
switch (this.affinity) { |
|||
case Axis.horizontal: { |
|||
return new HorizontalMultiDragGestureRecognizer(this) {onStart = onStart}; |
|||
} |
|||
case Axis.vertical: { |
|||
return new VerticalMultiDragGestureRecognizer(this) {onStart = onStart}; |
|||
} |
|||
} |
|||
|
|||
return new ImmediateMultiDragGestureRecognizer(this) {onStart = onStart}; |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new _DraggableState<T>(); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class LongPressDraggable<T> : Draggable<T> { |
|||
readonly bool hapticFeedbackOnStart; |
|||
|
|||
public LongPressDraggable( |
|||
T data, |
|||
Widget child, |
|||
Widget feedback, |
|||
Axis? axis = null, |
|||
Key key = null, |
|||
Widget childWhenDragging = null, |
|||
Offset feedbackOffset = null, |
|||
DragAnchor dragAnchor = DragAnchor.child, |
|||
int? maxSimultaneousDrags = null, |
|||
VoidCallback onDragStarted = null, |
|||
DraggableCanceledCallback onDraggableCanceled = null, |
|||
DragEndCallback onDragEnd = null, |
|||
VoidCallback onDragCompleted = null, |
|||
bool hapticFeedbackOnStart = true |
|||
) : base( |
|||
key: key, |
|||
child: child, |
|||
feedback: feedback, |
|||
data: data, |
|||
axis: axis, |
|||
childWhenDragging: childWhenDragging, |
|||
feedbackOffset: feedbackOffset, |
|||
dragAnchor: dragAnchor, |
|||
maxSimultaneousDrags: maxSimultaneousDrags, |
|||
onDragStarted: onDragStarted, |
|||
onDraggableCanceled: onDraggableCanceled, |
|||
onDragEnd: onDragEnd, |
|||
onDragCompleted: onDragCompleted |
|||
) { |
|||
this.hapticFeedbackOnStart = hapticFeedbackOnStart; |
|||
} |
|||
|
|||
public override GestureRecognizer createRecognizer(GestureMultiDragStartCallback onStart) { |
|||
return new DelayedMultiDragGestureRecognizer(Constants.kLongPressTimeout) { |
|||
onStart = (Offset position) => { |
|||
Drag result = onStart(position); |
|||
if (result != null && this.hapticFeedbackOnStart) { |
|||
//HapticFeedback.selectionClick();
|
|||
} |
|||
|
|||
return result; |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public class _DraggableState<T> : State<Draggable<T>> { |
|||
public override void initState() { |
|||
base.initState(); |
|||
this._recognizer = this.widget.createRecognizer(this._startDrag); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._disposeRecognizerIfInactive(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
GestureRecognizer _recognizer; |
|||
int _activeCount; |
|||
|
|||
void _disposeRecognizerIfInactive() { |
|||
if (this._activeCount > 0) |
|||
return; |
|||
this._recognizer.dispose(); |
|||
this._recognizer = null; |
|||
} |
|||
|
|||
|
|||
void _routePointer(PointerEvent pEvent) { |
|||
if (this.widget.maxSimultaneousDrags != null && |
|||
this._activeCount >= this.widget.maxSimultaneousDrags) |
|||
return; |
|||
|
|||
if (pEvent is PointerDownEvent) |
|||
this._recognizer.addPointer((PointerDownEvent) pEvent); |
|||
} |
|||
|
|||
_DragAvatar<T> _startDrag(Offset position) { |
|||
if (this.widget.maxSimultaneousDrags != null && |
|||
this._activeCount >= this.widget.maxSimultaneousDrags) |
|||
return null; |
|||
var dragStartPoint = Offset.zero; |
|||
switch (this.widget.dragAnchor) { |
|||
case DragAnchor.child: |
|||
RenderBox renderObject = this.context.findRenderObject() as RenderBox; |
|||
dragStartPoint = renderObject.globalToLocal(position); |
|||
break; |
|||
case DragAnchor.pointer: |
|||
dragStartPoint = Offset.zero; |
|||
break; |
|||
} |
|||
|
|||
this.setState(() => { this._activeCount += 1; }); |
|||
|
|||
_DragAvatar<T> avatar = new _DragAvatar<T>( |
|||
overlayState: Overlay.of(this.context, debugRequiredFor: this.widget), |
|||
data: this.widget.data, |
|||
axis: this.widget.axis, |
|||
initialPosition: position, |
|||
dragStartPoint: dragStartPoint, |
|||
feedback: this.widget.feedback, |
|||
feedbackOffset: this.widget.feedbackOffset, |
|||
onDragEnd: (Velocity velocity, Offset offset, bool wasAccepted) => { |
|||
if (this.mounted) { |
|||
this.setState(() => { this._activeCount -= 1; }); |
|||
} |
|||
else { |
|||
this._activeCount -= 1; |
|||
this._disposeRecognizerIfInactive(); |
|||
} |
|||
|
|||
if (this.mounted && this.widget.onDragEnd != null) |
|||
this.widget.onDragEnd(new DraggableDetails( |
|||
wasAccepted: wasAccepted, |
|||
velocity: velocity, |
|||
offset: offset |
|||
)); |
|||
|
|||
if (wasAccepted && this.widget.onDragCompleted != null) |
|||
this.widget.onDragCompleted(); |
|||
if (!wasAccepted && this.widget.onDraggableCanceled != null) |
|||
this.widget.onDraggableCanceled(velocity, offset); |
|||
} |
|||
); |
|||
if (this.widget.onDragStarted != null) |
|||
this.widget.onDragStarted(); |
|||
return avatar; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(Overlay.of(context, debugRequiredFor: this.widget) != null); |
|||
bool canDrag = this.widget.maxSimultaneousDrags == null || |
|||
this._activeCount < this.widget.maxSimultaneousDrags; |
|||
|
|||
bool showChild = this._activeCount == 0 || this.widget.childWhenDragging == null; |
|||
if (canDrag) |
|||
return new Listener( |
|||
onPointerDown: this._routePointer, |
|||
child: showChild ? this.widget.child : this.widget.childWhenDragging |
|||
); |
|||
return new Listener( |
|||
child: showChild ? this.widget.child : this.widget.childWhenDragging); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class DraggableDetails { |
|||
public DraggableDetails( |
|||
Velocity velocity, |
|||
Offset offset, |
|||
bool wasAccepted = false |
|||
) { |
|||
D.assert(velocity != null); |
|||
D.assert(offset != null); |
|||
this.wasAccepted = wasAccepted; |
|||
this.velocity = velocity; |
|||
this.offset = offset; |
|||
} |
|||
|
|||
public bool wasAccepted; |
|||
|
|||
public Velocity velocity; |
|||
|
|||
public Offset offset; |
|||
} |
|||
|
|||
|
|||
public class DragTarget<T> : StatefulWidget { |
|||
public DragTarget( |
|||
DragTargetBuilder<T> builder, |
|||
Key key = null, |
|||
DragTargetWillAccept<T> onWillAccept = null, |
|||
DragTargetAccept<T> onAccept = null, |
|||
DragTargetLeave<T> onLeave = null |
|||
) : base(key) { |
|||
this.builder = builder; |
|||
this.onWillAccept = onWillAccept; |
|||
this.onAccept = onAccept; |
|||
this.onLeave = onLeave; |
|||
} |
|||
|
|||
public DragTargetBuilder<T> builder; |
|||
|
|||
public DragTargetWillAccept<T> onWillAccept; |
|||
|
|||
public DragTargetAccept<T> onAccept; |
|||
|
|||
public DragTargetLeave<T> onLeave; |
|||
|
|||
public override State createState() { |
|||
return new _DragTargetState<T>(); |
|||
} |
|||
} |
|||
|
|||
public class _DragTargetState<T> : State<DragTarget<T>> { |
|||
readonly List<_DragAvatar<T>> _candidateAvatars = new List<_DragAvatar<T>>(); |
|||
readonly List<_DragAvatar<T>> _rejectedAvatars = new List<_DragAvatar<T>>(); |
|||
|
|||
public bool didEnter(_DragAvatar<T> avatar) { |
|||
D.assert(!this._candidateAvatars.Contains(avatar)); |
|||
D.assert(!this._rejectedAvatars.Contains(avatar)); |
|||
|
|||
if (avatar.data is T && (this.widget.onWillAccept == null || this.widget.onWillAccept(avatar.data))) { |
|||
this.setState(() => { this._candidateAvatars.Add(avatar); }); |
|||
return true; |
|||
} |
|||
|
|||
this._rejectedAvatars.Add(avatar); |
|||
return false; |
|||
} |
|||
|
|||
public void didLeave(_DragAvatar<T> avatar) { |
|||
D.assert(this._candidateAvatars.Contains(avatar) || this._rejectedAvatars.Contains(avatar)); |
|||
if (!this.mounted) |
|||
return; |
|||
this.setState(() => { |
|||
this._candidateAvatars.Remove(avatar); |
|||
this._rejectedAvatars.Remove(avatar); |
|||
}); |
|||
if (this.widget.onLeave != null) |
|||
this.widget.onLeave(avatar.data); |
|||
} |
|||
|
|||
public void didDrop(_DragAvatar<T> avatar) { |
|||
D.assert(this._candidateAvatars.Contains(avatar)); |
|||
if (!this.mounted) |
|||
return; |
|||
this.setState(() => { this._candidateAvatars.Remove(avatar); }); |
|||
if (this.widget.onAccept != null) |
|||
this.widget.onAccept(avatar.data); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(this.widget.builder != null); |
|||
return new MetaData( |
|||
metaData: this, |
|||
behavior: HitTestBehavior.translucent, |
|||
child: this.widget.builder(context, DragUtils._mapAvatarsToData(this._candidateAvatars), |
|||
DragUtils._mapAvatarsToData(this._rejectedAvatars))); |
|||
} |
|||
} |
|||
|
|||
|
|||
public enum _DragEndKind { |
|||
dropped, |
|||
canceled |
|||
} |
|||
|
|||
public delegate void _OnDragEnd(Velocity velocity, Offset offset, bool wasAccepted); |
|||
|
|||
|
|||
public class _DragAvatar<T> : Drag { |
|||
public _DragAvatar( |
|||
T data, |
|||
OverlayState overlayState, |
|||
Axis? axis = null, |
|||
Offset initialPosition = null, |
|||
Offset dragStartPoint = null, |
|||
Widget feedback = null, |
|||
Offset feedbackOffset = null, |
|||
_OnDragEnd onDragEnd = null |
|||
) { |
|||
if (initialPosition == null) |
|||
initialPosition = Offset.zero; |
|||
if (dragStartPoint == null) |
|||
dragStartPoint = Offset.zero; |
|||
if (feedbackOffset == null) |
|||
feedbackOffset = Offset.zero; |
|||
|
|||
D.assert(overlayState != null); |
|||
this.overlayState = overlayState; |
|||
this.data = data; |
|||
this.axis = axis; |
|||
this.dragStartPoint = dragStartPoint; |
|||
this.feedback = feedback; |
|||
this.feedbackOffset = feedbackOffset; |
|||
this.onDragEnd = onDragEnd; |
|||
|
|||
this._entry = new OverlayEntry(this._build); |
|||
|
|||
this.overlayState.insert(this._entry); |
|||
this._position = initialPosition; |
|||
this.updateDrag(initialPosition); |
|||
} |
|||
|
|||
public readonly T data; |
|||
|
|||
readonly Axis? axis; |
|||
|
|||
readonly Offset dragStartPoint; |
|||
|
|||
readonly Widget feedback; |
|||
|
|||
readonly Offset feedbackOffset; |
|||
|
|||
readonly _OnDragEnd onDragEnd; |
|||
|
|||
readonly OverlayState overlayState; |
|||
|
|||
_DragTargetState<T> _activeTarget; |
|||
|
|||
readonly List<_DragTargetState<T>> _enteredTargets = new List<_DragTargetState<T>>(); |
|||
|
|||
OverlayEntry _entry; |
|||
|
|||
Offset _lastOffset; |
|||
|
|||
Offset _position; |
|||
|
|||
public void update(DragUpdateDetails details) { |
|||
this._position += this._restrictAxis(details.delta); |
|||
this.updateDrag(this._position); |
|||
} |
|||
|
|||
public void end(DragEndDetails details) { |
|||
this.finishDrag(_DragEndKind.dropped, this._restrictVelocityAxis(details.velocity)); |
|||
} |
|||
|
|||
public void cancel() { |
|||
this.finishDrag(_DragEndKind.canceled); |
|||
} |
|||
|
|||
void updateDrag(Offset globalPosition) { |
|||
this._lastOffset = globalPosition - this.dragStartPoint; |
|||
this._entry.markNeedsBuild(); |
|||
|
|||
HitTestResult result = new HitTestResult(); |
|||
WidgetsBinding.instance.hitTest(result, globalPosition + this.feedbackOffset); |
|||
|
|||
List<_DragTargetState<T>> targets = this._getDragTargets(result.path.ToList()); |
|||
|
|||
bool listsMatch = false; |
|||
if (targets.Count >= this._enteredTargets.Count && this._enteredTargets.isNotEmpty()) { |
|||
listsMatch = true; |
|||
List<_DragTargetState<T>>.Enumerator iterator = targets.GetEnumerator(); |
|||
for (int i = 0; i < this._enteredTargets.Count; i++) { |
|||
iterator.MoveNext(); |
|||
if (iterator.Current != this._enteredTargets[i]) { |
|||
listsMatch = false; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (listsMatch) |
|||
return; |
|||
|
|||
this._leaveAllEntered(); |
|||
|
|||
_DragTargetState<T> newTarget = null; |
|||
foreach (var target in targets) { |
|||
this._enteredTargets.Add(target); |
|||
if (target.didEnter(this)) { |
|||
newTarget = target; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
this._activeTarget = newTarget; |
|||
} |
|||
|
|||
List<_DragTargetState<T>> _getDragTargets(List<HitTestEntry> path) { |
|||
List<_DragTargetState<T>> ret = new List<_DragTargetState<T>>(); |
|||
|
|||
foreach (HitTestEntry entry in path) |
|||
if (entry.target is RenderMetaData) { |
|||
RenderMetaData renderMetaData = (RenderMetaData) entry.target; |
|||
if (renderMetaData.metaData is _DragTargetState<T>) |
|||
ret.Add((_DragTargetState<T>) renderMetaData.metaData); |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
void _leaveAllEntered() { |
|||
for (int i = 0; i < this._enteredTargets.Count; i++) |
|||
this._enteredTargets[i].didLeave(this); |
|||
this._enteredTargets.Clear(); |
|||
} |
|||
|
|||
void finishDrag(_DragEndKind endKind, Velocity velocity = null) { |
|||
bool wasAccepted = false; |
|||
if (endKind == _DragEndKind.dropped && this._activeTarget != null) { |
|||
this._activeTarget.didDrop(this); |
|||
wasAccepted = true; |
|||
this._enteredTargets.Remove(this._activeTarget); |
|||
} |
|||
|
|||
this._leaveAllEntered(); |
|||
this._activeTarget = null; |
|||
this._entry.remove(); |
|||
this._entry = null; |
|||
|
|||
if (this.onDragEnd != null) |
|||
this.onDragEnd(velocity == null ? Velocity.zero : velocity, this._lastOffset, wasAccepted); |
|||
} |
|||
|
|||
public Widget _build(BuildContext context) { |
|||
RenderBox box = (RenderBox) this.overlayState.context.findRenderObject(); |
|||
Offset overlayTopLeft = box.localToGlobal(Offset.zero); |
|||
return new Positioned( |
|||
left: this._lastOffset.dx - overlayTopLeft.dx, |
|||
top: this._lastOffset.dy - overlayTopLeft.dy, |
|||
child: new IgnorePointer( |
|||
child: this.feedback |
|||
) |
|||
); |
|||
} |
|||
|
|||
Velocity _restrictVelocityAxis(Velocity velocity) { |
|||
if (this.axis == null) { |
|||
return velocity; |
|||
} |
|||
|
|||
return new Velocity( |
|||
this._restrictAxis(velocity.pixelsPerSecond)); |
|||
} |
|||
|
|||
Offset _restrictAxis(Offset offset) { |
|||
if (this.axis == null) { |
|||
return offset; |
|||
} |
|||
|
|||
if (this.axis == Axis.horizontal) { |
|||
return new Offset(offset.dx, 0.0); |
|||
} |
|||
|
|||
return new Offset(0.0, offset.dy); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6bdb5b80d3ab24159bb25c8c2790a3a3 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.engine; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgetsSample { |
|||
public class DragDropCanvas : WidgetCanvas { |
|||
protected override Widget getWidget() { |
|||
return new DragDropApp(); |
|||
} |
|||
|
|||
class DragDropApp : StatefulWidget { |
|||
public DragDropApp(Key key = null) : base(key) { |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new DragDropState(); |
|||
} |
|||
} |
|||
|
|||
class DragTargetWidget : StatefulWidget { |
|||
public DragTargetWidget(Key key = null) : base(key) { |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new DragTargetWidgetState(); |
|||
} |
|||
} |
|||
|
|||
class DragTargetWidgetState : State<DragTargetWidget> { |
|||
int value; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new Positioned( |
|||
left: 40.0, |
|||
bottom: 40.0, |
|||
child: new DragTarget<int>( |
|||
onAccept: obj => { |
|||
Debug.Log("ON ACCEPTED ..." + obj); |
|||
this.setState(() => { this.value += obj; }); |
|||
}, |
|||
builder: (inner_context2, accepted, rejected) => { |
|||
return new Container( |
|||
width: 40.0, |
|||
height: 40.0, |
|||
constraints: BoxConstraints.tight(new Size(40, 40)), |
|||
color: AsScreenCanvas.CLColors.red, |
|||
child: new Center(child: new Text("" + this.value)) |
|||
); |
|||
} |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
class DragDropState : State<DragDropApp> { |
|||
public override Widget build(BuildContext context) { |
|||
var entries = new List<OverlayEntry>(); |
|||
|
|||
var entry_bg = new OverlayEntry( |
|||
inner_context => new Container( |
|||
color: AsScreenCanvas.CLColors.white |
|||
)); |
|||
|
|||
var entry = new OverlayEntry( |
|||
inner_context => new Positioned( |
|||
left: 0.0, |
|||
bottom: 0.0, |
|||
child: new GestureDetector( |
|||
onTap: () => { }, |
|||
child: new Draggable<int>( |
|||
5, |
|||
child: new Container( |
|||
color: AsScreenCanvas.CLColors.blue, |
|||
width: 30.0, |
|||
height: 30.0, |
|||
constraints: BoxConstraints.tight(new Size(30, 30)), |
|||
child: new Center(child: new Text("5")) |
|||
), |
|||
feedback: new Container( |
|||
color: AsScreenCanvas.CLColors.green, |
|||
width: 30.0, |
|||
height: 30.0), |
|||
//maxSimultaneousDrags: 1,
|
|||
childWhenDragging: new Container( |
|||
color: AsScreenCanvas.CLColors.black, |
|||
width: 30.0, |
|||
height: 30.0, |
|||
constraints: BoxConstraints.tight(new Size(30, 30)) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
|
|||
var entry3 = new OverlayEntry( |
|||
inner_context => new Positioned( |
|||
left: 0.0, |
|||
bottom: 40.0, |
|||
child: new GestureDetector( |
|||
onTap: () => { }, |
|||
child: |
|||
new Draggable<int>( |
|||
8, |
|||
child: new Container( |
|||
color: AsScreenCanvas.CLColors.background4, |
|||
width: 30.0, |
|||
height: 30.0, |
|||
constraints: BoxConstraints.tight(new Size(30, 30)), |
|||
child: new Center(child: new Text("8"))) |
|||
, |
|||
feedback: new Container( |
|||
color: AsScreenCanvas.CLColors.green, |
|||
width: 30.0, |
|||
height: 30.0), |
|||
maxSimultaneousDrags: 1, |
|||
childWhenDragging: new Container( |
|||
color: AsScreenCanvas.CLColors.black, |
|||
width: 30.0, |
|||
height: 30.0, |
|||
constraints: BoxConstraints.tight(new Size(30, 30)) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
|
|||
var entry2 = new OverlayEntry( |
|||
inner_context => new DragTargetWidget() |
|||
); |
|||
|
|||
entries.Add(entry_bg); |
|||
entries.Add(entry); |
|||
entries.Add(entry2); |
|||
entries.Add(entry3); |
|||
|
|||
return new Container( |
|||
color: AsScreenCanvas.CLColors.white, |
|||
child: new Overlay( |
|||
initialEntries: entries |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d01ae8029ff2f4e54a5e9c1a9672e5dc |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace Unity.UIWidgets.utils { |
|||
public class DragUtils { |
|||
public static List<T> _mapAvatarsToData<T>(List<_DragAvatar<T>> avatars) { |
|||
return avatars.Select(avatar => avatar.data).ToList(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 63d65ac8718914805bc92f8dd0b52d03 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue