Commit 8ff19827 authored by Andrew Wilson's avatar Andrew Wilson Committed by Andrew P. Wilson

Allow Draggables to drop through DragTargets that don't accept them to one that does.

parent d5deea49
...@@ -391,7 +391,7 @@ class _DragAvatar<T> extends Drag { ...@@ -391,7 +391,7 @@ class _DragAvatar<T> extends Drag {
final _OnDragEnd onDragEnd; final _OnDragEnd onDragEnd;
_DragTargetState<T> _activeTarget; _DragTargetState<T> _activeTarget;
bool _activeTargetWillAcceptDrop = false; List<_DragTargetState<T>> _lastTargets = <_DragTargetState<T>>[];
Point _position; Point _position;
Offset _lastOffset; Offset _lastOffset;
OverlayEntry _entry; OverlayEntry _entry;
...@@ -418,32 +418,58 @@ class _DragAvatar<T> extends Drag { ...@@ -418,32 +418,58 @@ class _DragAvatar<T> extends Drag {
_entry.markNeedsBuild(); _entry.markNeedsBuild();
HitTestResult result = new HitTestResult(); HitTestResult result = new HitTestResult();
WidgetFlutterBinding.instance.hitTest(result, globalPosition + feedbackOffset); WidgetFlutterBinding.instance.hitTest(result, globalPosition + feedbackOffset);
_DragTargetState<T> target = _getDragTarget(result.path);
if (target == _activeTarget) List<_DragTargetState<T>> targets = _getDragTargets(result.path).toList();
bool listsMatch = false;
if (targets.length >= _lastTargets.length && _lastTargets.isNotEmpty) {
listsMatch = true;
Iterator<_DragTargetState<T>> iterator = targets.iterator;
for (int i = 0; i < _lastTargets.length; i += 1) {
iterator.moveNext();
if (iterator.current != _lastTargets[i]) {
listsMatch = false;
break;
}
}
}
// If everything's the same, bail early.
if (listsMatch)
return; return;
if (_activeTarget != null)
_activeTarget.didLeave(data); // Leave old targets.
_activeTarget = target; for (int i = 0; i < _lastTargets.length; i += 1)
_activeTargetWillAcceptDrop = _activeTarget != null && _activeTarget.didEnter(data); _lastTargets[i].didLeave(data);
_lastTargets.clear();
// Enter new targets.
_DragTargetState<T> newTarget = targets.firstWhere((_DragTargetState<T> target) {
_lastTargets.add(target);
return target.didEnter(data);
},
orElse: () => null
);
_activeTarget = newTarget;
} }
_DragTargetState<T> _getDragTarget(List<HitTestEntry> path) { Iterable<_DragTargetState<T>> _getDragTargets(List<HitTestEntry> path) sync* {
// Look for the RenderBox that corresponds to the hit target (the hit target // Look for the RenderBoxes that corresponds to the hit target (the hit target
// widget builds a RenderMetadata box for us for this purpose). // widgets build RenderMetadata boxes for us for this purpose).
for (HitTestEntry entry in path) { for (HitTestEntry entry in path) {
if (entry.target is RenderMetaData) { if (entry.target is RenderMetaData) {
RenderMetaData renderMetaData = entry.target; RenderMetaData renderMetaData = entry.target;
if (renderMetaData.metaData is _DragTargetState<T>) if (renderMetaData.metaData is _DragTargetState<T>)
return renderMetaData.metaData; yield renderMetaData.metaData;
} }
} }
return null;
} }
void finish(_DragEndKind endKind, [Velocity velocity]) { void finish(_DragEndKind endKind, [Velocity velocity]) {
bool wasAccepted = false; bool wasAccepted = false;
if (_activeTarget != null) { if (_activeTarget != null) {
if (endKind == _DragEndKind.dropped && _activeTargetWillAcceptDrop) { if (endKind == _DragEndKind.dropped && _activeTarget != null) {
_activeTarget.didDrop(data); _activeTarget.didDrop(data);
wasAccepted = true; wasAccepted = true;
} else { } else {
...@@ -451,7 +477,6 @@ class _DragAvatar<T> extends Drag { ...@@ -451,7 +477,6 @@ class _DragAvatar<T> extends Drag {
} }
} }
_activeTarget = null; _activeTarget = null;
_activeTargetWillAcceptDrop = false;
_entry.remove(); _entry.remove();
_entry = null; _entry = null;
// TODO(ianh): consider passing _entry as well so the client can perform an animation. // TODO(ianh): consider passing _entry as well so the client can perform an animation.
......
...@@ -753,4 +753,123 @@ void main() { ...@@ -753,4 +753,123 @@ void main() {
expect(onDraggableCanceledOffset, equals(new Offset(flingStart.x, flingStart.y) + new Offset(0.0, 100.0))); expect(onDraggableCanceledOffset, equals(new Offset(flingStart.x, flingStart.y) + new Offset(0.0, 100.0)));
}); });
}); });
test('Drag and drop - allow pass thru of unaccepted data test', () {
testWidgets((WidgetTester tester) {
List<int> acceptedInts = <int>[];
List<double> acceptedDoubles = <double>[];
tester.pumpWidget(new MaterialApp(
routes: <String, WidgetBuilder>{
'/': (BuildContext context) { return new Column(
children: <Widget>[
new Draggable<int>(
data: 1,
child: new Text('IntSource'),
feedback: new Text('IntDragging')
),
new Draggable<double>(
data: 1.0,
child: new Text('DoubleSource'),
feedback: new Text('DoubleDragging')
),
new Stack(children:[
new DragTarget<int>(
builder: (BuildContext context, List<int> data, List<dynamic> rejects) {
return new IgnorePointer(
child: new Container(
height: 100.0,
child: new Text('Target1')
)
);
},
onAccept: (int data) {
acceptedInts.add(data);
}
),
new DragTarget<double>(
builder: (BuildContext context, List<double> data, List<dynamic> rejects) {
return new IgnorePointer(
child: new Container(
height: 100.0,
child: new Text('Target2')
)
);
},
onAccept: (double data) {
acceptedDoubles.add(data);
}
),
])
]);
},
}
));
expect(acceptedInts, isEmpty);
expect(acceptedDoubles, isEmpty);
expect(tester.findText('IntSource'), isNotNull);
expect(tester.findText('IntDragging'), isNull);
expect(tester.findText('DoubleSource'), isNotNull);
expect(tester.findText('DoubleDragging'), isNull);
expect(tester.findText('Target1'), isNotNull);
expect(tester.findText('Target2'), isNotNull);
Point intLocation = tester.getCenter(tester.findText('IntSource'));
Point doubleLocation = tester.getCenter(tester.findText('DoubleSource'));
Point targetLocation = tester.getCenter(tester.findText('Target1'));
// Drag the double draggable.
TestGesture doubleGesture = tester.startGesture(doubleLocation, pointer: 7);
tester.pump();
expect(acceptedInts, isEmpty);
expect(acceptedDoubles, isEmpty);
expect(tester.findText('IntDragging'), isNull);
expect(tester.findText('DoubleDragging'), isNotNull);
doubleGesture.moveTo(targetLocation);
tester.pump();
expect(acceptedInts, isEmpty);
expect(acceptedDoubles, isEmpty);
expect(tester.findText('IntDragging'), isNull);
expect(tester.findText('DoubleDragging'), isNotNull);
doubleGesture.up();
tester.pump();
expect(acceptedInts, isEmpty);
expect(acceptedDoubles, equals(<double>[1.0]));
expect(tester.findText('IntDragging'), isNull);
expect(tester.findText('DoubleDragging'), isNull);
acceptedDoubles.clear();
// Drag the int draggable.
TestGesture intGesture = tester.startGesture(intLocation, pointer: 7);
tester.pump();
expect(acceptedInts, isEmpty);
expect(acceptedDoubles, isEmpty);
expect(tester.findText('IntDragging'), isNotNull);
expect(tester.findText('DoubleDragging'), isNull);
intGesture.moveTo(targetLocation);
tester.pump();
expect(acceptedInts, isEmpty);
expect(acceptedDoubles, isEmpty);
expect(tester.findText('IntDragging'), isNotNull);
expect(tester.findText('DoubleDragging'), isNull);
intGesture.up();
tester.pump();
expect(acceptedInts, equals(<int>[1]));
expect(acceptedDoubles, isEmpty);
expect(tester.findText('IntDragging'), isNull);
expect(tester.findText('DoubleDragging'), isNull);
});
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment