Commit 63101e49 authored by Adam Barth's avatar Adam Barth

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.
parent d76f5652
// 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;
}
}
...@@ -25,7 +25,7 @@ abstract class GestureRecognizer extends GestureArenaMember { ...@@ -25,7 +25,7 @@ abstract class GestureRecognizer extends GestureArenaMember {
void handleEvent(sky.PointerEvent event); void handleEvent(sky.PointerEvent event);
void acceptGesture(int pointer) { } void acceptGesture(int pointer) { }
void rejectGesture(int pointer) { } void rejectGesture(int pointer) { }
void didStopTrackingLastPointer(); void didStopTrackingLastPointer(int pointer);
void resolve(GestureDisposition disposition) { void resolve(GestureDisposition disposition) {
List<GestureArenaEntry> localEntries = new List.from(_entries); List<GestureArenaEntry> localEntries = new List.from(_entries);
...@@ -53,7 +53,7 @@ abstract class GestureRecognizer extends GestureArenaMember { ...@@ -53,7 +53,7 @@ abstract class GestureRecognizer extends GestureArenaMember {
_router.removeRoute(pointer, handleEvent); _router.removeRoute(pointer, handleEvent);
_trackedPointers.remove(pointer); _trackedPointers.remove(pointer);
if (_trackedPointers.isEmpty) if (_trackedPointers.isEmpty)
didStopTrackingLastPointer(); didStopTrackingLastPointer(pointer);
} }
void stopTrackingIfPointerNoLongerDown(sky.PointerEvent event) { void stopTrackingIfPointerNoLongerDown(sky.PointerEvent event) {
...@@ -124,7 +124,7 @@ abstract class PrimaryPointerGestureRecognizer extends GestureRecognizer { ...@@ -124,7 +124,7 @@ abstract class PrimaryPointerGestureRecognizer extends GestureRecognizer {
} }
} }
void didStopTrackingLastPointer() { void didStopTrackingLastPointer(int pointer) {
_stopTimer(); _stopTimer();
state = GestureRecognizerState.ready; state = GestureRecognizerState.ready;
} }
......
...@@ -16,14 +16,26 @@ enum DragState { ...@@ -16,14 +16,26 @@ enum DragState {
typedef void GestureDragStartCallback(); typedef void GestureDragStartCallback();
typedef void GestureDragUpdateCallback(double scrollDelta); typedef void GestureDragUpdateCallback(double scrollDelta);
typedef void GestureDragEndCallback(); typedef void GestureDragEndCallback(sky.Offset velocity);
typedef void GesturePanStartCallback(); typedef void GesturePanStartCallback();
typedef void GesturePanUpdateCallback(sky.Offset scrollDelta); typedef void GesturePanUpdateCallback(sky.Offset scrollDelta);
typedef void GesturePanEndCallback(); typedef void GesturePanEndCallback(sky.Offset velocity);
typedef void _GesturePolymorphicUpdateCallback<T>(T scrollDelta); typedef void _GesturePolymorphicUpdateCallback<T>(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<T extends dynamic> extends GestureRecognizer { abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecognizer {
_DragGestureRecognizer({ PointerRouter router, this.onStart, this.onUpdate, this.onEnd }) _DragGestureRecognizer({ PointerRouter router, this.onStart, this.onUpdate, this.onEnd })
: super(router: router); : super(router: router);
...@@ -39,6 +51,8 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz ...@@ -39,6 +51,8 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz
T _getDragDelta(sky.PointerEvent event); T _getDragDelta(sky.PointerEvent event);
bool get _hasSufficientPendingDragDeltaToAccept; bool get _hasSufficientPendingDragDeltaToAccept;
final sky.VelocityTracker _velocityTracker = new sky.VelocityTracker();
void addPointer(sky.PointerEvent event) { void addPointer(sky.PointerEvent event) {
startTrackingPointer(event.pointer); startTrackingPointer(event.pointer);
if (_state == DragState.ready) { if (_state == DragState.ready) {
...@@ -50,6 +64,7 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz ...@@ -50,6 +64,7 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz
void handleEvent(sky.PointerEvent event) { void handleEvent(sky.PointerEvent event) {
assert(_state != DragState.ready); assert(_state != DragState.ready);
if (event.type == 'pointermove') { if (event.type == 'pointermove') {
_velocityTracker.addPosition(_eventTime(event), event.pointer, event.x, event.y);
T delta = _getDragDelta(event); T delta = _getDragDelta(event);
if (_state == DragState.accepted) { if (_state == DragState.accepted) {
if (onUpdate != null) if (onUpdate != null)
...@@ -75,7 +90,7 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz ...@@ -75,7 +90,7 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz
} }
} }
void didStopTrackingLastPointer() { void didStopTrackingLastPointer(int pointer) {
if (_state == DragState.possible) { if (_state == DragState.possible) {
resolve(GestureDisposition.rejected); resolve(GestureDisposition.rejected);
_state = DragState.ready; _state = DragState.ready;
...@@ -83,8 +98,20 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz ...@@ -83,8 +98,20 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz
} }
bool wasAccepted = (_state == DragState.accepted); bool wasAccepted = (_state == DragState.accepted);
_state = DragState.ready; _state = DragState.ready;
if (wasAccepted && onEnd != null) if (wasAccepted && onEnd != null) {
onEnd(); 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();
} }
} }
......
...@@ -155,7 +155,7 @@ class Dismissable extends StatefulComponent { ...@@ -155,7 +155,7 @@ class Dismissable extends StatefulComponent {
_fadePerformance.progress = _dragExtent.abs() / (_size.width * _kDismissCardThreshold); _fadePerformance.progress = _dragExtent.abs() / (_size.width * _kDismissCardThreshold);
} }
_handleDragEnd() { void _handleDragEnd(Offset velocity) {
if (!_isActive || _fadePerformance.isAnimating) if (!_isActive || _fadePerformance.isAnimating)
return; return;
_dragUnderway = false; _dragUnderway = false;
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
import 'dart:sky' as sky; import 'dart:sky' as sky;
import 'package:sky/gestures/fling.dart';
import 'package:sky/gestures/long_press.dart'; import 'package:sky/gestures/long_press.dart';
import 'package:sky/gestures/recognizer.dart'; import 'package:sky/gestures/recognizer.dart';
import 'package:sky/gestures/scroll.dart'; import 'package:sky/gestures/scroll.dart';
...@@ -28,8 +27,7 @@ class GestureDetector extends StatefulComponent { ...@@ -28,8 +27,7 @@ class GestureDetector extends StatefulComponent {
this.onHorizontalDragEnd, this.onHorizontalDragEnd,
this.onPanStart, this.onPanStart,
this.onPanUpdate, this.onPanUpdate,
this.onPanEnd, this.onPanEnd
this.onFling
}) : super(key: key); }) : super(key: key);
Widget child; Widget child;
...@@ -49,8 +47,6 @@ class GestureDetector extends StatefulComponent { ...@@ -49,8 +47,6 @@ class GestureDetector extends StatefulComponent {
GesturePanUpdateCallback onPanUpdate; GesturePanUpdateCallback onPanUpdate;
GesturePanEndCallback onPanEnd; GesturePanEndCallback onPanEnd;
GestureFlingCallback onFling;
void syncConstructorArguments(GestureDetector source) { void syncConstructorArguments(GestureDetector source) {
child = source.child; child = source.child;
onTap = source.onTap; onTap = source.onTap;
...@@ -65,7 +61,6 @@ class GestureDetector extends StatefulComponent { ...@@ -65,7 +61,6 @@ class GestureDetector extends StatefulComponent {
onPanStart = source.onPanStart; onPanStart = source.onPanStart;
onPanUpdate = source.onPanUpdate; onPanUpdate = source.onPanUpdate;
onPanEnd = source.onPanEnd; onPanEnd = source.onPanEnd;
onFling = source.onFling;
_syncGestureListeners(); _syncGestureListeners();
} }
...@@ -113,13 +108,6 @@ class GestureDetector extends StatefulComponent { ...@@ -113,13 +108,6 @@ class GestureDetector extends StatefulComponent {
return _pan; return _pan;
} }
FlingGestureRecognizer _fling;
FlingGestureRecognizer _ensureFling() {
if (_fling == null)
_fling = new FlingGestureRecognizer(router: _router);
return _fling;
}
void didMount() { void didMount() {
super.didMount(); super.didMount();
_syncGestureListeners(); _syncGestureListeners();
...@@ -133,7 +121,6 @@ class GestureDetector extends StatefulComponent { ...@@ -133,7 +121,6 @@ class GestureDetector extends StatefulComponent {
_verticalDrag = _ensureDisposed(_verticalDrag); _verticalDrag = _ensureDisposed(_verticalDrag);
_horizontalDrag = _ensureDisposed(_horizontalDrag); _horizontalDrag = _ensureDisposed(_horizontalDrag);
_pan = _ensureDisposed(_pan); _pan = _ensureDisposed(_pan);
_fling = _ensureDisposed(_fling);
} }
void _syncGestureListeners() { void _syncGestureListeners() {
...@@ -143,7 +130,6 @@ class GestureDetector extends StatefulComponent { ...@@ -143,7 +130,6 @@ class GestureDetector extends StatefulComponent {
_syncVerticalDrag(); _syncVerticalDrag();
_syncHorizontalDrag(); _syncHorizontalDrag();
_syncPan(); _syncPan();
_syncFling();
} }
void _syncTap() { void _syncTap() {
...@@ -200,13 +186,6 @@ class GestureDetector extends StatefulComponent { ...@@ -200,13 +186,6 @@ class GestureDetector extends StatefulComponent {
} }
} }
void _syncFling() {
if (onFling == null)
_fling = _ensureDisposed(_fling);
else
_ensureFling().onFling = onFling;
}
GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) { GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) {
recognizer?.dispose(); recognizer?.dispose();
return null; return null;
...@@ -225,8 +204,6 @@ class GestureDetector extends StatefulComponent { ...@@ -225,8 +204,6 @@ class GestureDetector extends StatefulComponent {
_horizontalDrag.addPointer(event); _horizontalDrag.addPointer(event);
if (_pan != null) if (_pan != null)
_pan.addPointer(event); _pan.addPointer(event);
if (_fling != null)
_fling.addPointer(event);
return EventDisposition.processed; return EventDisposition.processed;
} }
......
...@@ -89,7 +89,7 @@ abstract class Scrollable extends StatefulComponent { ...@@ -89,7 +89,7 @@ abstract class Scrollable extends StatefulComponent {
GestureDragEndCallback _getDragEndHandler(ScrollDirection direction) { GestureDragEndCallback _getDragEndHandler(ScrollDirection direction) {
if (scrollDirection != direction || !scrollBehavior.isScrollable) if (scrollDirection != direction || !scrollBehavior.isScrollable)
return null; return null;
return _maybeSettleScrollOffset; return _handleDragEnd;
} }
Widget build() { Widget build() {
...@@ -98,11 +98,9 @@ abstract class Scrollable extends StatefulComponent { ...@@ -98,11 +98,9 @@ abstract class Scrollable extends StatefulComponent {
onVerticalDragEnd: _getDragEndHandler(ScrollDirection.vertical), onVerticalDragEnd: _getDragEndHandler(ScrollDirection.vertical),
onHorizontalDragUpdate: _getDragUpdateHandler(ScrollDirection.horizontal), onHorizontalDragUpdate: _getDragUpdateHandler(ScrollDirection.horizontal),
onHorizontalDragEnd: _getDragEndHandler(ScrollDirection.horizontal), onHorizontalDragEnd: _getDragEndHandler(ScrollDirection.horizontal),
onFling: scrollBehavior.isScrollable ? _handleFling : null,
child: new Listener( child: new Listener(
child: buildContent(), child: buildContent(),
onPointerDown: _handlePointerDown, onPointerDown: _handlePointerDown,
onGestureFlingCancel: _handleFlingCancel,
onWheel: _handleWheel onWheel: _handleWheel
) )
); );
...@@ -183,19 +181,12 @@ abstract class Scrollable extends StatefulComponent { ...@@ -183,19 +181,12 @@ abstract class Scrollable extends StatefulComponent {
return EventDisposition.processed; return EventDisposition.processed;
} }
void _handleFling(Offset velocity) { void _handleDragEnd(Offset velocity) {
if (velocity != Offset.zero) {
_startToEndAnimation(velocity: _scrollVelocity(velocity)); _startToEndAnimation(velocity: _scrollVelocity(velocity));
} } else if (!_toEndAnimation.isAnimating && (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating)) {
void _maybeSettleScrollOffset() {
if (!_toEndAnimation.isAnimating &&
(_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating))
settleScrollOffset(); settleScrollOffset();
} }
EventDisposition _handleFlingCancel(sky.GestureEvent event) {
_maybeSettleScrollOffset();
return EventDisposition.processed;
} }
EventDisposition _handleWheel(sky.WheelEvent event) { EventDisposition _handleWheel(sky.WheelEvent event) {
...@@ -570,7 +561,7 @@ class PageableList<T> extends ScrollableList<T> { ...@@ -570,7 +561,7 @@ class PageableList<T> extends ScrollableList<T> {
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset); .clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
} }
void _handleFling(sky.Offset velocity) { void _handleDragEnd(sky.Offset velocity) {
double scrollVelocity = _scrollVelocity(velocity); double scrollVelocity = _scrollVelocity(velocity);
double newScrollOffset = _snapScrollOffset(scrollOffset + scrollVelocity.sign * itemExtent) double newScrollOffset = _snapScrollOffset(scrollOffset + scrollVelocity.sign * itemExtent)
.clamp(_snapScrollOffset(scrollOffset - itemExtent / 2.0), .clamp(_snapScrollOffset(scrollOffset - itemExtent / 2.0),
......
...@@ -25,7 +25,7 @@ void main() { ...@@ -25,7 +25,7 @@ void main() {
}; };
bool didEndPan = false; bool didEndPan = false;
pan.onEnd = () { pan.onEnd = (sky.Offset velocity) {
didEndPan = true; didEndPan = true;
}; };
......
...@@ -21,7 +21,7 @@ void main() { ...@@ -21,7 +21,7 @@ void main() {
onVerticalDragUpdate: (double scrollDelta) { onVerticalDragUpdate: (double scrollDelta) {
updatedDragDelta = scrollDelta; updatedDragDelta = scrollDelta;
}, },
onVerticalDragEnd: () { onVerticalDragEnd: (Offset velocity) {
didEndDrag = true; didEndDrag = true;
}, },
child: new Container() child: new Container()
...@@ -69,9 +69,9 @@ void main() { ...@@ -69,9 +69,9 @@ void main() {
Widget builder() { Widget builder() {
return new GestureDetector( return new GestureDetector(
onVerticalDragUpdate: (double delta) { dragDistance += delta; }, onVerticalDragUpdate: (double delta) { dragDistance += delta; },
onVerticalDragEnd: () { gestureCount += 1; }, onVerticalDragEnd: (Offset velocity) { gestureCount += 1; },
onHorizontalDragUpdate: (_) { fail("gesture should not match"); }, onHorizontalDragUpdate: (_) { fail("gesture should not match"); },
onHorizontalDragEnd: () { fail("gesture should not match"); }, onHorizontalDragEnd: (Offset velocity) { fail("gesture should not match"); },
child: new Container() child: new Container()
); );
} }
......
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