Commit 0eea48c8 authored by Adam Barth's avatar Adam Barth

Merge pull request #2724 from abarth/drag_down2

Tapping drawer during animation causes it to stick
parents 98636d5b a710676d
...@@ -14,13 +14,17 @@ enum DragState { ...@@ -14,13 +14,17 @@ enum DragState {
accepted accepted
} }
typedef void GestureDragDownCallback(Point globalPosition);
typedef void GestureDragStartCallback(Point globalPosition); typedef void GestureDragStartCallback(Point globalPosition);
typedef void GestureDragUpdateCallback(double delta); typedef void GestureDragUpdateCallback(double delta);
typedef void GestureDragEndCallback(Velocity velocity); typedef void GestureDragEndCallback(Velocity velocity);
typedef void GestureDragCancelCallback();
typedef void GesturePanDownCallback(Point globalPosition);
typedef void GesturePanStartCallback(Point globalPosition); typedef void GesturePanStartCallback(Point globalPosition);
typedef void GesturePanUpdateCallback(Offset delta); typedef void GesturePanUpdateCallback(Offset delta);
typedef void GesturePanEndCallback(Velocity velocity); typedef void GesturePanEndCallback(Velocity velocity);
typedef void GesturePanCancelCallback();
typedef void _GesturePolymorphicUpdateCallback<T>(T delta); typedef void _GesturePolymorphicUpdateCallback<T>(T delta);
...@@ -32,9 +36,11 @@ bool _isFlingGesture(Velocity velocity) { ...@@ -32,9 +36,11 @@ bool _isFlingGesture(Velocity velocity) {
} }
abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGestureRecognizer { abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGestureRecognizer {
GestureDragDownCallback onDown;
GestureDragStartCallback onStart; GestureDragStartCallback onStart;
_GesturePolymorphicUpdateCallback<T> onUpdate; _GesturePolymorphicUpdateCallback<T> onUpdate;
GestureDragEndCallback onEnd; GestureDragEndCallback onEnd;
GestureDragCancelCallback onCancel;
DragState _state = DragState.ready; DragState _state = DragState.ready;
Point _initialPosition; Point _initialPosition;
...@@ -54,6 +60,8 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGest ...@@ -54,6 +60,8 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGest
_state = DragState.possible; _state = DragState.possible;
_initialPosition = event.position; _initialPosition = event.position;
_pendingDragDelta = _initialPendingDragDelta; _pendingDragDelta = _initialPendingDragDelta;
if (onDown != null)
onDown(_initialPosition);
} }
} }
...@@ -90,11 +98,18 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGest ...@@ -90,11 +98,18 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGest
} }
} }
@override
void rejectGesture(int pointer) {
ensureNotTrackingPointer(pointer);
}
@override @override
void didStopTrackingLastPointer(int pointer) { void didStopTrackingLastPointer(int pointer) {
if (_state == DragState.possible) { if (_state == DragState.possible) {
resolve(GestureDisposition.rejected); resolve(GestureDisposition.rejected);
_state = DragState.ready; _state = DragState.ready;
if (onCancel != null)
onCancel();
return; return;
} }
bool wasAccepted = (_state == DragState.accepted); bool wasAccepted = (_state == DragState.accepted);
......
...@@ -99,6 +99,11 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer { ...@@ -99,6 +99,11 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
didStopTrackingLastPointer(pointer); didStopTrackingLastPointer(pointer);
} }
void ensureNotTrackingPointer(int pointer) {
if (_trackedPointers.contains(pointer))
stopTrackingPointer(pointer);
}
void stopTrackingIfPointerNoLongerDown(PointerEvent event) { void stopTrackingIfPointerNoLongerDown(PointerEvent event) {
if (event is PointerUpEvent || event is PointerCancelEvent) if (event is PointerUpEvent || event is PointerCancelEvent)
stopTrackingPointer(event.pointer); stopTrackingPointer(event.pointer);
......
...@@ -122,11 +122,21 @@ class DrawerControllerState extends State<DrawerController> { ...@@ -122,11 +122,21 @@ class DrawerControllerState extends State<DrawerController> {
AnimationController _controller; AnimationController _controller;
void _handleTapDown(Point position) { void _handleDragDown(Point position) {
_controller.stop(); _controller.stop();
_ensureHistoryEntry(); _ensureHistoryEntry();
} }
void _handleDragCancel() {
if (_controller.isDismissed || _controller.isAnimating)
return;
if (_controller.value < 0.5) {
close();
} else {
open();
}
}
double get _width { double get _width {
assert(!Scheduler.debugInFrame); // we should never try to read the tree state while building or laying out assert(!Scheduler.debugInFrame); // we should never try to read the tree state while building or laying out
RenderBox drawerBox = _drawerKey.currentContext?.findRenderObject(); RenderBox drawerBox = _drawerKey.currentContext?.findRenderObject();
...@@ -179,8 +189,10 @@ class DrawerControllerState extends State<DrawerController> { ...@@ -179,8 +189,10 @@ class DrawerControllerState extends State<DrawerController> {
} else { } else {
return new GestureDetector( return new GestureDetector(
key: _gestureDetectorKey, key: _gestureDetectorKey,
onHorizontalDragDown: _handleDragDown,
onHorizontalDragUpdate: _move, onHorizontalDragUpdate: _move,
onHorizontalDragEnd: _settle, onHorizontalDragEnd: _settle,
onHorizontalDragCancel: _handleDragCancel,
child: new RepaintBoundary( child: new RepaintBoundary(
child: new Stack( child: new Stack(
children: <Widget>[ children: <Widget>[
...@@ -195,16 +207,13 @@ class DrawerControllerState extends State<DrawerController> { ...@@ -195,16 +207,13 @@ class DrawerControllerState extends State<DrawerController> {
), ),
new Align( new Align(
alignment: const FractionalOffset(0.0, 0.5), alignment: const FractionalOffset(0.0, 0.5),
child: new GestureDetector( child: new Align(
onTapDown: _handleTapDown, alignment: const FractionalOffset(1.0, 0.5),
child: new Align( widthFactor: _controller.value,
alignment: const FractionalOffset(1.0, 0.5), child: new RepaintBoundary(
widthFactor: _controller.value, child: new Focus(
child: new RepaintBoundary( key: _drawerKey,
child: new Focus( child: config.child
key: _drawerKey,
child: config.child
)
) )
) )
) )
......
...@@ -14,15 +14,16 @@ export 'package:flutter/gestures.dart' show ...@@ -14,15 +14,16 @@ export 'package:flutter/gestures.dart' show
GestureTapCallback, GestureTapCallback,
GestureTapCancelCallback, GestureTapCancelCallback,
GestureLongPressCallback, GestureLongPressCallback,
GestureDragDownCallback,
GestureDragStartCallback, GestureDragStartCallback,
GestureDragUpdateCallback, GestureDragUpdateCallback,
GestureDragEndCallback, GestureDragEndCallback,
GestureDragStartCallback, GestureDragCancelCallback,
GestureDragUpdateCallback, GesturePanDownCallback,
GestureDragEndCallback,
GesturePanStartCallback, GesturePanStartCallback,
GesturePanUpdateCallback, GesturePanUpdateCallback,
GesturePanEndCallback, GesturePanEndCallback,
GesturePanCancelCallback,
GestureScaleStartCallback, GestureScaleStartCallback,
GestureScaleUpdateCallback, GestureScaleUpdateCallback,
GestureScaleEndCallback, GestureScaleEndCallback,
...@@ -49,15 +50,21 @@ class GestureDetector extends StatelessWidget { ...@@ -49,15 +50,21 @@ class GestureDetector extends StatelessWidget {
this.onTapCancel, this.onTapCancel,
this.onDoubleTap, this.onDoubleTap,
this.onLongPress, this.onLongPress,
this.onVerticalDragDown,
this.onVerticalDragStart, this.onVerticalDragStart,
this.onVerticalDragUpdate, this.onVerticalDragUpdate,
this.onVerticalDragEnd, this.onVerticalDragEnd,
this.onVerticalDragCancel,
this.onHorizontalDragDown,
this.onHorizontalDragStart, this.onHorizontalDragStart,
this.onHorizontalDragUpdate, this.onHorizontalDragUpdate,
this.onHorizontalDragEnd, this.onHorizontalDragEnd,
this.onHorizontalDragCancel,
this.onPanDown,
this.onPanStart, this.onPanStart,
this.onPanUpdate, this.onPanUpdate,
this.onPanEnd, this.onPanEnd,
this.onPanCancel,
this.onScaleStart, this.onScaleStart,
this.onScaleUpdate, this.onScaleUpdate,
this.onScaleEnd, this.onScaleEnd,
...@@ -116,6 +123,9 @@ class GestureDetector extends StatelessWidget { ...@@ -116,6 +123,9 @@ class GestureDetector extends StatelessWidget {
final GestureLongPressCallback onLongPress; final GestureLongPressCallback onLongPress;
/// A pointer has contacted the screen and might begin to move vertically. /// A pointer has contacted the screen and might begin to move vertically.
final GestureDragDownCallback onVerticalDragDown;
/// A pointer has contacted the screen and has begun to move vertically.
final GestureDragStartCallback onVerticalDragStart; final GestureDragStartCallback onVerticalDragStart;
/// A pointer that is in contact with the screen and moving vertically has /// A pointer that is in contact with the screen and moving vertically has
...@@ -127,7 +137,14 @@ class GestureDetector extends StatelessWidget { ...@@ -127,7 +137,14 @@ class GestureDetector extends StatelessWidget {
/// specific velocity when it stopped contacting the screen. /// specific velocity when it stopped contacting the screen.
final GestureDragEndCallback onVerticalDragEnd; final GestureDragEndCallback onVerticalDragEnd;
/// The pointer that previously triggered the [onVerticalDragDown] did not
/// end up moving vertically.
final GestureDragCancelCallback onVerticalDragCancel;
/// A pointer has contacted the screen and might begin to move horizontally. /// A pointer has contacted the screen and might begin to move horizontally.
final GestureDragDownCallback onHorizontalDragDown;
/// A pointer has contacted the screen and has begun to move horizontally.
final GestureDragStartCallback onHorizontalDragStart; final GestureDragStartCallback onHorizontalDragStart;
/// A pointer that is in contact with the screen and moving horizontally has /// A pointer that is in contact with the screen and moving horizontally has
...@@ -139,9 +156,15 @@ class GestureDetector extends StatelessWidget { ...@@ -139,9 +156,15 @@ class GestureDetector extends StatelessWidget {
/// specific velocity when it stopped contacting the screen. /// specific velocity when it stopped contacting the screen.
final GestureDragEndCallback onHorizontalDragEnd; final GestureDragEndCallback onHorizontalDragEnd;
/// The pointer that previously triggered the [onHorizontalDragDown] did not
/// end up moving horizontally.
final GestureDragCancelCallback onHorizontalDragCancel;
final GesturePanDownCallback onPanDown;
final GesturePanStartCallback onPanStart; final GesturePanStartCallback onPanStart;
final GesturePanUpdateCallback onPanUpdate; final GesturePanUpdateCallback onPanUpdate;
final GesturePanEndCallback onPanEnd; final GesturePanEndCallback onPanEnd;
final GesturePanCancelCallback onPanCancel;
final GestureScaleStartCallback onScaleStart; final GestureScaleStartCallback onScaleStart;
final GestureScaleUpdateCallback onScaleUpdate; final GestureScaleUpdateCallback onScaleUpdate;
...@@ -185,30 +208,48 @@ class GestureDetector extends StatelessWidget { ...@@ -185,30 +208,48 @@ class GestureDetector extends StatelessWidget {
}; };
} }
if (onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null) { if (onVerticalDragDown != null ||
onVerticalDragStart != null ||
onVerticalDragUpdate != null ||
onVerticalDragEnd != null ||
onVerticalDragCancel != null) {
gestures[VerticalDragGestureRecognizer] = (VerticalDragGestureRecognizer recognizer) { gestures[VerticalDragGestureRecognizer] = (VerticalDragGestureRecognizer recognizer) {
return (recognizer ??= new VerticalDragGestureRecognizer()) return (recognizer ??= new VerticalDragGestureRecognizer())
..onDown = onVerticalDragDown
..onStart = onVerticalDragStart ..onStart = onVerticalDragStart
..onUpdate = onVerticalDragUpdate ..onUpdate = onVerticalDragUpdate
..onEnd = onVerticalDragEnd; ..onEnd = onVerticalDragEnd
..onCancel = onVerticalDragCancel;
}; };
} }
if (onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null) { if (onHorizontalDragDown != null ||
onHorizontalDragStart != null ||
onHorizontalDragUpdate != null ||
onHorizontalDragEnd != null ||
onHorizontalDragCancel != null) {
gestures[HorizontalDragGestureRecognizer] = (HorizontalDragGestureRecognizer recognizer) { gestures[HorizontalDragGestureRecognizer] = (HorizontalDragGestureRecognizer recognizer) {
return (recognizer ??= new HorizontalDragGestureRecognizer()) return (recognizer ??= new HorizontalDragGestureRecognizer())
..onDown = onHorizontalDragDown
..onStart = onHorizontalDragStart ..onStart = onHorizontalDragStart
..onUpdate = onHorizontalDragUpdate ..onUpdate = onHorizontalDragUpdate
..onEnd = onHorizontalDragEnd; ..onEnd = onHorizontalDragEnd
..onCancel = onHorizontalDragCancel;
}; };
} }
if (onPanStart != null || onPanUpdate != null || onPanEnd != null) { if (onPanDown != null ||
onPanStart != null ||
onPanUpdate != null ||
onPanEnd != null ||
onPanCancel != null) {
gestures[PanGestureRecognizer] = (PanGestureRecognizer recognizer) { gestures[PanGestureRecognizer] = (PanGestureRecognizer recognizer) {
return (recognizer ??= new PanGestureRecognizer()) return (recognizer ??= new PanGestureRecognizer())
..onDown = onPanDown
..onStart = onPanStart ..onStart = onPanStart
..onUpdate = onPanUpdate ..onUpdate = onPanUpdate
..onEnd = onPanEnd; ..onEnd = onPanEnd
..onCancel = onPanCancel;
}; };
} }
......
...@@ -470,7 +470,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -470,7 +470,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
config.onScroll(_scrollOffset); config.onScroll(_scrollOffset);
} }
void _handlePointerDown(_) { void _handleDragDown(_) {
_controller.stop(); _controller.stop();
} }
...@@ -527,10 +527,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -527,10 +527,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
key: _gestureDetectorKey, key: _gestureDetectorKey,
gestures: buildGestureDetectors(), gestures: buildGestureDetectors(),
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
child: new Listener( child: buildContent(context)
child: buildContent(context),
onPointerDown: _handlePointerDown
)
); );
} }
...@@ -559,6 +556,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -559,6 +556,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return <Type, GestureRecognizerFactory>{ return <Type, GestureRecognizerFactory>{
VerticalDragGestureRecognizer: (VerticalDragGestureRecognizer recognizer) { VerticalDragGestureRecognizer: (VerticalDragGestureRecognizer recognizer) {
return (recognizer ??= new VerticalDragGestureRecognizer()) return (recognizer ??= new VerticalDragGestureRecognizer())
..onDown = _handleDragDown
..onStart = _handleDragStart ..onStart = _handleDragStart
..onUpdate = _handleDragUpdate ..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd; ..onEnd = _handleDragEnd;
...@@ -568,6 +566,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -568,6 +566,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return <Type, GestureRecognizerFactory>{ return <Type, GestureRecognizerFactory>{
HorizontalDragGestureRecognizer: (HorizontalDragGestureRecognizer recognizer) { HorizontalDragGestureRecognizer: (HorizontalDragGestureRecognizer recognizer) {
return (recognizer ??= new HorizontalDragGestureRecognizer()) return (recognizer ??= new HorizontalDragGestureRecognizer())
..onDown = _handleDragDown
..onStart = _handleDragStart ..onStart = _handleDragStart
..onUpdate = _handleDragUpdate ..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd; ..onEnd = _handleDragEnd;
......
...@@ -45,7 +45,6 @@ void main() { ...@@ -45,7 +45,6 @@ void main() {
test('Drawer tap test', () { test('Drawer tap test', () {
testWidgets((WidgetTester tester) { testWidgets((WidgetTester tester) {
GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>(); GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
tester.pumpWidget(new Container()); // throw away the old App and its Navigator
tester.pumpWidget( tester.pumpWidget(
new MaterialApp( new MaterialApp(
routes: <String, WidgetBuilder>{ routes: <String, WidgetBuilder>{
...@@ -81,4 +80,63 @@ void main() { ...@@ -81,4 +80,63 @@ void main() {
}); });
}); });
test('Drawer drag cancel resume', () {
testWidgets((WidgetTester tester) {
GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
tester.pumpWidget(
new MaterialApp(
routes: <String, WidgetBuilder>{
'/': (BuildContext context) {
return new Scaffold(
key: scaffoldKey,
drawer: new Drawer(
child: new Block(
children: <Widget>[
new Text('drawer'),
new Container(
height: 1000.0,
decoration: new BoxDecoration(
backgroundColor: Colors.blue[500]
)
),
]
)
),
body: new Container()
);
}
}
)
);
expect(tester.findText('drawer'), isNull);
scaffoldKey.currentState.openDrawer();
tester.pump(); // drawer should be starting to animate in
expect(tester.findText('drawer'), isNotNull);
tester.pump(new Duration(seconds: 1)); // animation done
expect(tester.findText('drawer'), isNotNull);
tester.tapAt(const Point(750.0, 100.0)); // on the mask
tester.pump();
tester.pump(new Duration(milliseconds: 10));
// drawer should be starting to animate away
RenderBox textBox = tester.findText('drawer').renderObject;
double textLeft = textBox.localToGlobal(Point.origin).x;
expect(textLeft, lessThan(0.0));
TestGesture gesture = tester.startGesture(new Point(100.0, 100.0));
// drawer should be stopped.
tester.pump();
tester.pump(new Duration(milliseconds: 10));
expect(textBox.localToGlobal(Point.origin).x, equals(textLeft));
gesture.moveBy(new Offset(0.0, -50.0));
// drawer should be returning to visible
tester.pump();
tester.pump(new Duration(seconds: 1));
expect(textBox.localToGlobal(Point.origin).x, equals(0.0));
gesture.up();
});
});
} }
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