Merge pull request #1121 from abarth/fling_at_drag_end

Fold fling gesture into onDragEnd
This commit is contained in:
Adam Barth 2015-09-10 12:46:41 -07:00
commit 888b0bfb21
8 changed files with 48 additions and 116 deletions

View File

@ -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;
}
}

View File

@ -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<GestureArenaEntry> 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;
}

View File

@ -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>(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 {
_DragGestureRecognizer({ PointerRouter router, this.onStart, this.onUpdate, this.onEnd })
: super(router: router);
@ -39,6 +51,8 @@ abstract class _DragGestureRecognizer<T extends dynamic> 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<T extends dynamic> 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<T extends dynamic> 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<T extends dynamic> 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();
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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<T> extends ScrollableList<T> {
.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),

View File

@ -25,7 +25,7 @@ void main() {
};
bool didEndPan = false;
pan.onEnd = () {
pan.onEnd = (sky.Offset velocity) {
didEndPan = true;
};

View File

@ -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()
);
}