Commit a5ee6e11 authored by Andrew Wilson's avatar Andrew Wilson

Merge pull request #2919 from apwilson/drop_through

Allow Draggables to drop through DragTargets that don't accept them to one that does.
parents c9c84946 8ff19827
......@@ -391,7 +391,7 @@ class _DragAvatar<T> extends Drag {
final _OnDragEnd onDragEnd;
_DragTargetState<T> _activeTarget;
bool _activeTargetWillAcceptDrop = false;
List<_DragTargetState<T>> _lastTargets = <_DragTargetState<T>>[];
Point _position;
Offset _lastOffset;
OverlayEntry _entry;
......@@ -418,32 +418,58 @@ class _DragAvatar<T> extends Drag {
_entry.markNeedsBuild();
HitTestResult result = new HitTestResult();
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;
if (_activeTarget != null)
_activeTarget.didLeave(data);
_activeTarget = target;
_activeTargetWillAcceptDrop = _activeTarget != null && _activeTarget.didEnter(data);
// Leave old targets.
for (int i = 0; i < _lastTargets.length; i += 1)
_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) {
// Look for the RenderBox that corresponds to the hit target (the hit target
// widget builds a RenderMetadata box for us for this purpose).
Iterable<_DragTargetState<T>> _getDragTargets(List<HitTestEntry> path) sync* {
// Look for the RenderBoxes that corresponds to the hit target (the hit target
// widgets build RenderMetadata boxes for us for this purpose).
for (HitTestEntry entry in path) {
if (entry.target is RenderMetaData) {
RenderMetaData renderMetaData = entry.target;
if (renderMetaData.metaData is _DragTargetState<T>)
return renderMetaData.metaData;
yield renderMetaData.metaData;
}
}
return null;
}
void finish(_DragEndKind endKind, [Velocity velocity]) {
bool wasAccepted = false;
if (_activeTarget != null) {
if (endKind == _DragEndKind.dropped && _activeTargetWillAcceptDrop) {
if (endKind == _DragEndKind.dropped && _activeTarget != null) {
_activeTarget.didDrop(data);
wasAccepted = true;
} else {
......@@ -451,7 +477,6 @@ class _DragAvatar<T> extends Drag {
}
}
_activeTarget = null;
_activeTargetWillAcceptDrop = false;
_entry.remove();
_entry = null;
// TODO(ianh): consider passing _entry as well so the client can perform an animation.
......
......@@ -753,4 +753,123 @@ void main() {
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