From 0112dcc63e4c80a3591ab8c6bf3078d49b9f396a Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 10 Sep 2015 12:38:11 -0700 Subject: [PATCH] Fold fling gesture into onDragEnd That way the fling engages in the same direction as the scroll. For example, if you have a horizontal scroll nested inside a vertical scroll, the fling will take place in the same direction as the scroll. --- sky/packages/sky/lib/gestures/fling.dart | 63 ------------------- sky/packages/sky/lib/gestures/recognizer.dart | 6 +- sky/packages/sky/lib/gestures/scroll.dart | 37 +++++++++-- .../sky/lib/src/widgets/dismissable.dart | 2 +- .../sky/lib/src/widgets/gesture_detector.dart | 25 +------- .../sky/lib/src/widgets/scrollable.dart | 23 +++---- sky/unit/test/gestures/scroll_test.dart | 2 +- .../test/widget/gesture_detector_test.dart | 6 +- 8 files changed, 48 insertions(+), 116 deletions(-) delete mode 100644 sky/packages/sky/lib/gestures/fling.dart diff --git a/sky/packages/sky/lib/gestures/fling.dart b/sky/packages/sky/lib/gestures/fling.dart deleted file mode 100644 index 618e40b02b9..00000000000 --- a/sky/packages/sky/lib/gestures/fling.dart +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:sky' as sky; - -import 'package:sky/gestures/arena.dart'; -import 'package:sky/gestures/recognizer.dart'; -import 'package:sky/gestures/constants.dart'; - -// Fling velocities are logical pixels per second. -typedef void GestureFlingCallback(sky.Offset velocity); - -int _eventTime(sky.PointerEvent event) => (event.timeStamp * 1000.0).toInt(); // microseconds - -bool _isFlingGesture(sky.GestureVelocity velocity) { - double velocitySquared = velocity.x * velocity.x + velocity.y * velocity.y; - return velocity.isValid && - velocitySquared > kMinFlingVelocity * kMinFlingVelocity && - velocitySquared < kMaxFlingVelocity * kMaxFlingVelocity; -} - -class FlingGestureRecognizer extends GestureRecognizer { - FlingGestureRecognizer({ PointerRouter router, this.onFling }) - : super(router: router); - - GestureFlingCallback onFling; - sky.VelocityTracker _velocityTracker = new sky.VelocityTracker(); - int _primaryPointer; - - void addPointer(sky.PointerEvent event) { - startTrackingPointer(event.pointer); - if (_primaryPointer == null) - _primaryPointer = event.pointer; - } - - void handleEvent(sky.PointerEvent event) { - if (event.pointer == _primaryPointer) { - if (event.type == 'pointermove') { - _velocityTracker.addPosition(_eventTime(event), _primaryPointer, event.x, event.y); - } else if (event.type == 'pointerup') { - sky.GestureVelocity velocity = _velocityTracker.getVelocity(_primaryPointer); - if (_isFlingGesture(velocity)) { - resolve(GestureDisposition.accepted); - if (onFling != null) - onFling(new sky.Offset(velocity.x, velocity.y)); - } - } - } - stopTrackingIfPointerNoLongerDown(event); - } - - void didStopTrackingLastPointer() { - _primaryPointer = null; - _velocityTracker.reset(); - } - - void dispose() { - super.dispose(); - _velocityTracker.reset(); - _primaryPointer = null; - } -} diff --git a/sky/packages/sky/lib/gestures/recognizer.dart b/sky/packages/sky/lib/gestures/recognizer.dart index 68345525669..9fd068d825c 100644 --- a/sky/packages/sky/lib/gestures/recognizer.dart +++ b/sky/packages/sky/lib/gestures/recognizer.dart @@ -25,7 +25,7 @@ abstract class GestureRecognizer extends GestureArenaMember { void handleEvent(sky.PointerEvent event); void acceptGesture(int pointer) { } void rejectGesture(int pointer) { } - void didStopTrackingLastPointer(); + void didStopTrackingLastPointer(int pointer); void resolve(GestureDisposition disposition) { List localEntries = new List.from(_entries); @@ -53,7 +53,7 @@ abstract class GestureRecognizer extends GestureArenaMember { _router.removeRoute(pointer, handleEvent); _trackedPointers.remove(pointer); if (_trackedPointers.isEmpty) - didStopTrackingLastPointer(); + didStopTrackingLastPointer(pointer); } void stopTrackingIfPointerNoLongerDown(sky.PointerEvent event) { @@ -124,7 +124,7 @@ abstract class PrimaryPointerGestureRecognizer extends GestureRecognizer { } } - void didStopTrackingLastPointer() { + void didStopTrackingLastPointer(int pointer) { _stopTimer(); state = GestureRecognizerState.ready; } diff --git a/sky/packages/sky/lib/gestures/scroll.dart b/sky/packages/sky/lib/gestures/scroll.dart index 26d11c39581..7f65132efd0 100644 --- a/sky/packages/sky/lib/gestures/scroll.dart +++ b/sky/packages/sky/lib/gestures/scroll.dart @@ -16,14 +16,26 @@ enum DragState { typedef void GestureDragStartCallback(); typedef void GestureDragUpdateCallback(double scrollDelta); -typedef void GestureDragEndCallback(); +typedef void GestureDragEndCallback(sky.Offset velocity); typedef void GesturePanStartCallback(); typedef void GesturePanUpdateCallback(sky.Offset scrollDelta); -typedef void GesturePanEndCallback(); +typedef void GesturePanEndCallback(sky.Offset velocity); typedef void _GesturePolymorphicUpdateCallback(T scrollDelta); +// Fling velocities are logical pixels per second. +typedef void GestureFlingCallback(sky.Offset velocity); + +int _eventTime(sky.PointerEvent event) => (event.timeStamp * 1000.0).toInt(); // microseconds + +bool _isFlingGesture(sky.GestureVelocity velocity) { + double velocitySquared = velocity.x * velocity.x + velocity.y * velocity.y; + return velocity.isValid && + velocitySquared > kMinFlingVelocity * kMinFlingVelocity && + velocitySquared < kMaxFlingVelocity * kMaxFlingVelocity; +} + abstract class _DragGestureRecognizer extends GestureRecognizer { _DragGestureRecognizer({ PointerRouter router, this.onStart, this.onUpdate, this.onEnd }) : super(router: router); @@ -39,6 +51,8 @@ abstract class _DragGestureRecognizer extends GestureRecogniz T _getDragDelta(sky.PointerEvent event); bool get _hasSufficientPendingDragDeltaToAccept; + final sky.VelocityTracker _velocityTracker = new sky.VelocityTracker(); + void addPointer(sky.PointerEvent event) { startTrackingPointer(event.pointer); if (_state == DragState.ready) { @@ -50,6 +64,7 @@ abstract class _DragGestureRecognizer extends GestureRecogniz void handleEvent(sky.PointerEvent event) { assert(_state != DragState.ready); if (event.type == 'pointermove') { + _velocityTracker.addPosition(_eventTime(event), event.pointer, event.x, event.y); T delta = _getDragDelta(event); if (_state == DragState.accepted) { if (onUpdate != null) @@ -75,7 +90,7 @@ abstract class _DragGestureRecognizer extends GestureRecogniz } } - void didStopTrackingLastPointer() { + void didStopTrackingLastPointer(int pointer) { if (_state == DragState.possible) { resolve(GestureDisposition.rejected); _state = DragState.ready; @@ -83,8 +98,20 @@ abstract class _DragGestureRecognizer extends GestureRecogniz } bool wasAccepted = (_state == DragState.accepted); _state = DragState.ready; - if (wasAccepted && onEnd != null) - onEnd(); + if (wasAccepted && onEnd != null) { + sky.GestureVelocity gestureVelocity = _velocityTracker.getVelocity(pointer); + sky.Offset velocity = sky.Offset.zero; + if (_isFlingGesture(gestureVelocity)) + velocity = new sky.Offset(gestureVelocity.x, gestureVelocity.y); + resolve(GestureDisposition.accepted); + onEnd(velocity); + } + _velocityTracker.reset(); + } + + void dispose() { + _velocityTracker.reset(); + super.dispose(); } } diff --git a/sky/packages/sky/lib/src/widgets/dismissable.dart b/sky/packages/sky/lib/src/widgets/dismissable.dart index 7425cdb0a9a..e895ef20758 100644 --- a/sky/packages/sky/lib/src/widgets/dismissable.dart +++ b/sky/packages/sky/lib/src/widgets/dismissable.dart @@ -155,7 +155,7 @@ class Dismissable extends StatefulComponent { _fadePerformance.progress = _dragExtent.abs() / (_size.width * _kDismissCardThreshold); } - _handleDragEnd() { + void _handleDragEnd(Offset velocity) { if (!_isActive || _fadePerformance.isAnimating) return; _dragUnderway = false; diff --git a/sky/packages/sky/lib/src/widgets/gesture_detector.dart b/sky/packages/sky/lib/src/widgets/gesture_detector.dart index ddc2295f4a3..15436c9a926 100644 --- a/sky/packages/sky/lib/src/widgets/gesture_detector.dart +++ b/sky/packages/sky/lib/src/widgets/gesture_detector.dart @@ -4,7 +4,6 @@ import 'dart:sky' as sky; -import 'package:sky/gestures/fling.dart'; import 'package:sky/gestures/long_press.dart'; import 'package:sky/gestures/recognizer.dart'; import 'package:sky/gestures/scroll.dart'; @@ -28,8 +27,7 @@ class GestureDetector extends StatefulComponent { this.onHorizontalDragEnd, this.onPanStart, this.onPanUpdate, - this.onPanEnd, - this.onFling + this.onPanEnd }) : super(key: key); Widget child; @@ -49,8 +47,6 @@ class GestureDetector extends StatefulComponent { GesturePanUpdateCallback onPanUpdate; GesturePanEndCallback onPanEnd; - GestureFlingCallback onFling; - void syncConstructorArguments(GestureDetector source) { child = source.child; onTap = source.onTap; @@ -65,7 +61,6 @@ class GestureDetector extends StatefulComponent { onPanStart = source.onPanStart; onPanUpdate = source.onPanUpdate; onPanEnd = source.onPanEnd; - onFling = source.onFling; _syncGestureListeners(); } @@ -113,13 +108,6 @@ class GestureDetector extends StatefulComponent { return _pan; } - FlingGestureRecognizer _fling; - FlingGestureRecognizer _ensureFling() { - if (_fling == null) - _fling = new FlingGestureRecognizer(router: _router); - return _fling; - } - void didMount() { super.didMount(); _syncGestureListeners(); @@ -133,7 +121,6 @@ class GestureDetector extends StatefulComponent { _verticalDrag = _ensureDisposed(_verticalDrag); _horizontalDrag = _ensureDisposed(_horizontalDrag); _pan = _ensureDisposed(_pan); - _fling = _ensureDisposed(_fling); } void _syncGestureListeners() { @@ -143,7 +130,6 @@ class GestureDetector extends StatefulComponent { _syncVerticalDrag(); _syncHorizontalDrag(); _syncPan(); - _syncFling(); } void _syncTap() { @@ -200,13 +186,6 @@ class GestureDetector extends StatefulComponent { } } - void _syncFling() { - if (onFling == null) - _fling = _ensureDisposed(_fling); - else - _ensureFling().onFling = onFling; - } - GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) { recognizer?.dispose(); return null; @@ -225,8 +204,6 @@ class GestureDetector extends StatefulComponent { _horizontalDrag.addPointer(event); if (_pan != null) _pan.addPointer(event); - if (_fling != null) - _fling.addPointer(event); return EventDisposition.processed; } diff --git a/sky/packages/sky/lib/src/widgets/scrollable.dart b/sky/packages/sky/lib/src/widgets/scrollable.dart index 2617f3505bf..217a4d48963 100644 --- a/sky/packages/sky/lib/src/widgets/scrollable.dart +++ b/sky/packages/sky/lib/src/widgets/scrollable.dart @@ -89,7 +89,7 @@ abstract class Scrollable extends StatefulComponent { GestureDragEndCallback _getDragEndHandler(ScrollDirection direction) { if (scrollDirection != direction || !scrollBehavior.isScrollable) return null; - return _maybeSettleScrollOffset; + return _handleDragEnd; } Widget build() { @@ -98,11 +98,9 @@ abstract class Scrollable extends StatefulComponent { onVerticalDragEnd: _getDragEndHandler(ScrollDirection.vertical), onHorizontalDragUpdate: _getDragUpdateHandler(ScrollDirection.horizontal), onHorizontalDragEnd: _getDragEndHandler(ScrollDirection.horizontal), - onFling: scrollBehavior.isScrollable ? _handleFling : null, child: new Listener( child: buildContent(), onPointerDown: _handlePointerDown, - onGestureFlingCancel: _handleFlingCancel, onWheel: _handleWheel ) ); @@ -183,19 +181,12 @@ abstract class Scrollable extends StatefulComponent { return EventDisposition.processed; } - void _handleFling(Offset velocity) { - _startToEndAnimation(velocity: _scrollVelocity(velocity)); - } - - void _maybeSettleScrollOffset() { - if (!_toEndAnimation.isAnimating && - (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating)) + void _handleDragEnd(Offset velocity) { + if (velocity != Offset.zero) { + _startToEndAnimation(velocity: _scrollVelocity(velocity)); + } else if (!_toEndAnimation.isAnimating && (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating)) { settleScrollOffset(); - } - - EventDisposition _handleFlingCancel(sky.GestureEvent event) { - _maybeSettleScrollOffset(); - return EventDisposition.processed; + } } EventDisposition _handleWheel(sky.WheelEvent event) { @@ -570,7 +561,7 @@ class PageableList extends ScrollableList { .clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset); } - void _handleFling(sky.Offset velocity) { + void _handleDragEnd(sky.Offset velocity) { double scrollVelocity = _scrollVelocity(velocity); double newScrollOffset = _snapScrollOffset(scrollOffset + scrollVelocity.sign * itemExtent) .clamp(_snapScrollOffset(scrollOffset - itemExtent / 2.0), diff --git a/sky/unit/test/gestures/scroll_test.dart b/sky/unit/test/gestures/scroll_test.dart index 793dabb45ae..60c8538a086 100644 --- a/sky/unit/test/gestures/scroll_test.dart +++ b/sky/unit/test/gestures/scroll_test.dart @@ -25,7 +25,7 @@ void main() { }; bool didEndPan = false; - pan.onEnd = () { + pan.onEnd = (sky.Offset velocity) { didEndPan = true; }; diff --git a/sky/unit/test/widget/gesture_detector_test.dart b/sky/unit/test/widget/gesture_detector_test.dart index b07f374f549..29029144d1d 100644 --- a/sky/unit/test/widget/gesture_detector_test.dart +++ b/sky/unit/test/widget/gesture_detector_test.dart @@ -21,7 +21,7 @@ void main() { onVerticalDragUpdate: (double scrollDelta) { updatedDragDelta = scrollDelta; }, - onVerticalDragEnd: () { + onVerticalDragEnd: (Offset velocity) { didEndDrag = true; }, child: new Container() @@ -69,9 +69,9 @@ void main() { Widget builder() { return new GestureDetector( onVerticalDragUpdate: (double delta) { dragDistance += delta; }, - onVerticalDragEnd: () { gestureCount += 1; }, + onVerticalDragEnd: (Offset velocity) { gestureCount += 1; }, onHorizontalDragUpdate: (_) { fail("gesture should not match"); }, - onHorizontalDragEnd: () { fail("gesture should not match"); }, + onHorizontalDragEnd: (Offset velocity) { fail("gesture should not match"); }, child: new Container() ); }