Commit 67410a5a authored by Hans Muller's avatar Hans Muller

Initial support for the fling gesture detector

Added FlingGestureRecognizer and exposed it in the GestureDetector class. FlingGestureRecognizer is based on the Android/Chromium VelocityTracker class which computes a velocity vector for for a list of X,Y,Time tuples.

The Scrollable classes now use the FlingGestureRecognizer. Dismissable and Drawer still need to be updated
parent c5edfa19
......@@ -19,5 +19,5 @@ const double kDoubleTapTouchSlop = kTouchSlop; // Logical pixels
const double kPagingTouchSlop = kTouchSlop * 2.0; // Logical pixels
const double kDoubleTapSlop = 100.0; // Logical pixels
const double kWindowTouchSlop = 16.0; // Logical pixels
const double kMinFlingVelocity = 50.0; // TODO(abarth): Which units is this in?
const double kMaxFlingVelocity = 8000.0; // TODO(abarth): Which units is this in?
const double kMinFlingVelocity = 50.0; // Logical pixels / second
const double kMaxFlingVelocity = 8000.0; // Logical pixels / second
// 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;
}
}
......@@ -4,6 +4,7 @@
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';
......@@ -27,7 +28,8 @@ class GestureDetector extends StatefulComponent {
this.onHorizontalDragEnd,
this.onPanStart,
this.onPanUpdate,
this.onPanEnd
this.onPanEnd,
this.onFling
}) : super(key: key);
Widget child;
......@@ -47,6 +49,8 @@ class GestureDetector extends StatefulComponent {
GesturePanUpdateCallback onPanUpdate;
GesturePanEndCallback onPanEnd;
GestureFlingCallback onFling;
void syncConstructorArguments(GestureDetector source) {
child = source.child;
onTap = source.onTap;
......@@ -61,6 +65,7 @@ class GestureDetector extends StatefulComponent {
onPanStart = source.onPanStart;
onPanUpdate = source.onPanUpdate;
onPanEnd = source.onPanEnd;
onFling = source.onFling;
_syncGestureListeners();
}
......@@ -108,6 +113,13 @@ 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();
......@@ -121,6 +133,7 @@ class GestureDetector extends StatefulComponent {
_verticalDrag = _ensureDisposed(_verticalDrag);
_horizontalDrag = _ensureDisposed(_horizontalDrag);
_pan = _ensureDisposed(_pan);
_fling = _ensureDisposed(_fling);
}
void _syncGestureListeners() {
......@@ -130,6 +143,7 @@ class GestureDetector extends StatefulComponent {
_syncVerticalDrag();
_syncHorizontalDrag();
_syncPan();
_syncFling();
}
void _syncTap() {
......@@ -186,6 +200,13 @@ class GestureDetector extends StatefulComponent {
}
}
void _syncFling() {
if (onFling == null)
_fling = _ensureDisposed(_fling);
else
_ensureFling().onFling = onFling;
}
GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) {
recognizer?.dispose();
return null;
......@@ -204,6 +225,8 @@ class GestureDetector extends StatefulComponent {
_horizontalDrag.addPointer(event);
if (_pan != null)
_pan.addPointer(event);
if (_fling != null)
_fling.addPointer(event);
return EventDisposition.processed;
}
......
......@@ -85,10 +85,10 @@ abstract class Scrollable extends StatefulComponent {
onVerticalDragEnd: scrollDirection == ScrollDirection.vertical ? _maybeSettleScrollOffset : null,
onHorizontalDragUpdate: scrollDirection == ScrollDirection.horizontal ? scrollBy : null,
onHorizontalDragEnd: scrollDirection == ScrollDirection.horizontal ? _maybeSettleScrollOffset : null,
onFling: _handleFling,
child: new Listener(
child: buildContent(),
onPointerDown: _handlePointerDown,
onGestureFlingStart: _handleFlingStart,
onGestureFlingCancel: _handleFlingCancel,
onWheel: _handleWheel
)
......@@ -158,12 +158,11 @@ abstract class Scrollable extends StatefulComponent {
_startToEndAnimation();
}
// Return the event's velocity in pixels/second.
double _eventVelocity(sky.GestureEvent event) {
double velocity = scrollDirection == ScrollDirection.horizontal
? -event.velocityX
: -event.velocityY;
return velocity.clamp(_kMinFlingVelocity, _kMaxFlingVelocity) / _kMillisecondsPerSecond;
double _scrollVelocity(sky.Offset velocity) {
double scrollVelocity = scrollDirection == ScrollDirection.horizontal
? -velocity.dx
: -velocity.dy;
return scrollVelocity.clamp(_kMinFlingVelocity, _kMaxFlingVelocity) / _kMillisecondsPerSecond;
}
EventDisposition _handlePointerDown(_) {
......@@ -171,9 +170,8 @@ abstract class Scrollable extends StatefulComponent {
return EventDisposition.processed;
}
EventDisposition _handleFlingStart(sky.GestureEvent event) {
_startToEndAnimation(velocity: _eventVelocity(event));
return EventDisposition.processed;
void _handleFling(Offset velocity) {
_startToEndAnimation(velocity: _scrollVelocity(velocity));
}
void _maybeSettleScrollOffset() {
......@@ -559,13 +557,12 @@ class PageableList<T> extends ScrollableList<T> {
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
}
EventDisposition _handleFlingStart(sky.GestureEvent event) {
double velocity = _eventVelocity(event);
double newScrollOffset = _snapScrollOffset(scrollOffset + velocity.sign * itemExtent)
void _handleFling(sky.Offset velocity) {
double scrollVelocity = _scrollVelocity(velocity);
double newScrollOffset = _snapScrollOffset(scrollOffset + scrollVelocity.sign * itemExtent)
.clamp(_snapScrollOffset(scrollOffset - itemExtent / 2.0),
_snapScrollOffset(scrollOffset + itemExtent / 2.0));
scrollTo(newScrollOffset, duration: duration, curve: curve).then(_notifyPageChanged);
return EventDisposition.processed;
}
int get currentPage => (scrollOffset / itemExtent).floor() % itemCount;
......
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