First pass at support for pinch gestures; panning issues (needs testing)
Conflicts: sky/packages/sky/lib/gestures/drag.dart
This commit is contained in:
parent
f55a6ad1c1
commit
842e94e9f8
@ -17,6 +17,8 @@ const double kEdgeSlop = 12.0; // Logical pixels
|
|||||||
const double kTouchSlop = 8.0; // Logical pixels
|
const double kTouchSlop = 8.0; // Logical pixels
|
||||||
const double kDoubleTapTouchSlop = kTouchSlop; // Logical pixels
|
const double kDoubleTapTouchSlop = kTouchSlop; // Logical pixels
|
||||||
const double kPagingTouchSlop = kTouchSlop * 2.0; // Logical pixels
|
const double kPagingTouchSlop = kTouchSlop * 2.0; // Logical pixels
|
||||||
|
const double kPanSlop = kTouchSlop * 2.0; // Logical pixels
|
||||||
|
const double kPinchSlop = kTouchSlop; // Logical pixels
|
||||||
const double kDoubleTapSlop = 100.0; // Logical pixels
|
const double kDoubleTapSlop = 100.0; // Logical pixels
|
||||||
const double kWindowTouchSlop = 16.0; // Logical pixels
|
const double kWindowTouchSlop = 16.0; // Logical pixels
|
||||||
const double kMinFlingVelocity = 50.0; // Logical pixels / second
|
const double kMinFlingVelocity = 50.0; // Logical pixels / second
|
||||||
|
@ -79,7 +79,7 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz
|
|||||||
if (_state != DragState.accepted) {
|
if (_state != DragState.accepted) {
|
||||||
_state = DragState.accepted;
|
_state = DragState.accepted;
|
||||||
T delta = _pendingDragDelta;
|
T delta = _pendingDragDelta;
|
||||||
_pendingDragDelta = null;
|
_pendingDragDelta = _initialPendingDragDelta;
|
||||||
if (onStart != null)
|
if (onStart != null)
|
||||||
onStart();
|
onStart();
|
||||||
if (delta != _initialPendingDragDelta && onUpdate != null)
|
if (delta != _initialPendingDragDelta && onUpdate != null)
|
||||||
@ -149,6 +149,6 @@ class PanGestureRecognizer extends _DragGestureRecognizer<sky.Offset> {
|
|||||||
sky.Offset get _initialPendingDragDelta => sky.Offset.zero;
|
sky.Offset get _initialPendingDragDelta => sky.Offset.zero;
|
||||||
sky.Offset _getDragDelta(sky.PointerEvent event) => new sky.Offset(event.dx, event.dy);
|
sky.Offset _getDragDelta(sky.PointerEvent event) => new sky.Offset(event.dx, event.dy);
|
||||||
bool get _hasSufficientPendingDragDeltaToAccept {
|
bool get _hasSufficientPendingDragDeltaToAccept {
|
||||||
return _pendingDragDelta.dx.abs() > kTouchSlop || _pendingDragDelta.dy.abs() > kTouchSlop;
|
return _pendingDragDelta.distance > kPanSlop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
139
packages/flutter/lib/gestures/pinch.dart
Normal file
139
packages/flutter/lib/gestures/pinch.dart
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// 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:math' as math;
|
||||||
|
import 'dart:sky' as sky;
|
||||||
|
|
||||||
|
import 'package:sky/gestures/arena.dart';
|
||||||
|
import 'package:sky/gestures/recognizer.dart';
|
||||||
|
import 'package:sky/gestures/constants.dart';
|
||||||
|
|
||||||
|
enum PinchState {
|
||||||
|
ready,
|
||||||
|
possible,
|
||||||
|
started,
|
||||||
|
ended
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void GesturePinchStartCallback();
|
||||||
|
typedef void GesturePinchUpdateCallback(double scale);
|
||||||
|
typedef void GesturePinchEndCallback();
|
||||||
|
|
||||||
|
class PinchGestureRecognizer extends GestureRecognizer {
|
||||||
|
PinchGestureRecognizer({ PointerRouter router, this.onStart, this.onUpdate, this.onEnd })
|
||||||
|
: super(router: router);
|
||||||
|
|
||||||
|
GesturePinchStartCallback onStart;
|
||||||
|
GesturePinchUpdateCallback onUpdate;
|
||||||
|
GesturePinchEndCallback onEnd;
|
||||||
|
|
||||||
|
PinchState _state = PinchState.ready;
|
||||||
|
|
||||||
|
double _initialSpan;
|
||||||
|
double _currentSpan;
|
||||||
|
Map<int, sky.Point> _pointerLocations;
|
||||||
|
|
||||||
|
double get _scaleFactor => _initialSpan > 0 ? _currentSpan / _initialSpan : 1.0;
|
||||||
|
|
||||||
|
void addPointer(sky.PointerEvent event) {
|
||||||
|
startTrackingPointer(event.pointer);
|
||||||
|
if (_state == PinchState.ready) {
|
||||||
|
_state = PinchState.possible;
|
||||||
|
_initialSpan = 0.0;
|
||||||
|
_currentSpan = 0.0;
|
||||||
|
_pointerLocations = new Map<int, sky.Point>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEvent(sky.PointerEvent event) {
|
||||||
|
assert(_state != PinchState.ready);
|
||||||
|
bool configChanged = false;
|
||||||
|
switch(event.type) {
|
||||||
|
case 'pointerup':
|
||||||
|
configChanged = true;
|
||||||
|
_pointerLocations.remove(event.pointer);
|
||||||
|
break;
|
||||||
|
case 'pointerdown':
|
||||||
|
configChanged = true;
|
||||||
|
_pointerLocations[event.pointer] = new sky.Point(event.x, event.y);
|
||||||
|
break;
|
||||||
|
case 'pointermove':
|
||||||
|
_pointerLocations[event.pointer] = new sky.Point(event.x, event.y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = _pointerLocations.keys.length;
|
||||||
|
|
||||||
|
// Compute the focal point
|
||||||
|
sky.Point focalPoint = sky.Point.origin;
|
||||||
|
for (int pointer in _pointerLocations.keys)
|
||||||
|
focalPoint += _pointerLocations[pointer].toOffset();
|
||||||
|
focalPoint = new sky.Point(focalPoint.x / count, focalPoint.y / count);
|
||||||
|
|
||||||
|
// Span is the average deviation from focal point
|
||||||
|
double totalDeviation = 0.0;
|
||||||
|
for (int pointer in _pointerLocations.keys)
|
||||||
|
totalDeviation += (focalPoint - _pointerLocations[pointer]).distance;
|
||||||
|
_currentSpan = count > 0 ? totalDeviation / count : 0.0;
|
||||||
|
|
||||||
|
if (configChanged) {
|
||||||
|
_initialSpan = _currentSpan;
|
||||||
|
if (_state == PinchState.started) {
|
||||||
|
_state = PinchState.ended;
|
||||||
|
if (onEnd != null)
|
||||||
|
onEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_state == PinchState.ready)
|
||||||
|
_state = PinchState.possible;
|
||||||
|
|
||||||
|
if (_state == PinchState.possible &&
|
||||||
|
(_currentSpan - _initialSpan).abs() > kPinchSlop) {
|
||||||
|
resolve(GestureDisposition.accepted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_state == PinchState.ended && _currentSpan != _initialSpan) {
|
||||||
|
_state = PinchState.started;
|
||||||
|
if (onStart != null)
|
||||||
|
onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_state == PinchState.started && onUpdate != null)
|
||||||
|
onUpdate(_scaleFactor);
|
||||||
|
|
||||||
|
stopTrackingIfPointerNoLongerDown(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void acceptGesture(int pointer) {
|
||||||
|
if (_state != PinchState.started) {
|
||||||
|
_state = PinchState.started;
|
||||||
|
if (onStart != null)
|
||||||
|
onStart();
|
||||||
|
if (onUpdate != null)
|
||||||
|
onUpdate(_scaleFactor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void didStopTrackingLastPointer(int pointer) {
|
||||||
|
switch(_state) {
|
||||||
|
case PinchState.possible:
|
||||||
|
resolve(GestureDisposition.rejected);
|
||||||
|
break;
|
||||||
|
case PinchState.ready:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
case PinchState.started:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
case PinchState.ended:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_state = PinchState.ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import 'dart:sky' as sky;
|
|||||||
|
|
||||||
import 'package:sky/gestures/drag.dart';
|
import 'package:sky/gestures/drag.dart';
|
||||||
import 'package:sky/gestures/long_press.dart';
|
import 'package:sky/gestures/long_press.dart';
|
||||||
|
import 'package:sky/gestures/pinch.dart';
|
||||||
import 'package:sky/gestures/recognizer.dart';
|
import 'package:sky/gestures/recognizer.dart';
|
||||||
import 'package:sky/gestures/show_press.dart';
|
import 'package:sky/gestures/show_press.dart';
|
||||||
import 'package:sky/gestures/tap.dart';
|
import 'package:sky/gestures/tap.dart';
|
||||||
@ -27,7 +28,10 @@ class GestureDetector extends StatefulComponent {
|
|||||||
this.onHorizontalDragEnd,
|
this.onHorizontalDragEnd,
|
||||||
this.onPanStart,
|
this.onPanStart,
|
||||||
this.onPanUpdate,
|
this.onPanUpdate,
|
||||||
this.onPanEnd
|
this.onPanEnd,
|
||||||
|
this.onPinchStart,
|
||||||
|
this.onPinchUpdate,
|
||||||
|
this.onPinchEnd
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
Widget child;
|
Widget child;
|
||||||
@ -47,6 +51,10 @@ class GestureDetector extends StatefulComponent {
|
|||||||
GesturePanUpdateCallback onPanUpdate;
|
GesturePanUpdateCallback onPanUpdate;
|
||||||
GesturePanEndCallback onPanEnd;
|
GesturePanEndCallback onPanEnd;
|
||||||
|
|
||||||
|
GesturePinchStartCallback onPinchStart;
|
||||||
|
GesturePinchUpdateCallback onPinchUpdate;
|
||||||
|
GesturePinchEndCallback onPinchEnd;
|
||||||
|
|
||||||
void syncConstructorArguments(GestureDetector source) {
|
void syncConstructorArguments(GestureDetector source) {
|
||||||
child = source.child;
|
child = source.child;
|
||||||
onTap = source.onTap;
|
onTap = source.onTap;
|
||||||
@ -61,6 +69,9 @@ class GestureDetector extends StatefulComponent {
|
|||||||
onPanStart = source.onPanStart;
|
onPanStart = source.onPanStart;
|
||||||
onPanUpdate = source.onPanUpdate;
|
onPanUpdate = source.onPanUpdate;
|
||||||
onPanEnd = source.onPanEnd;
|
onPanEnd = source.onPanEnd;
|
||||||
|
onPinchStart = source.onPinchStart;
|
||||||
|
onPinchUpdate = source.onPinchUpdate;
|
||||||
|
onPinchEnd = source.onPinchEnd;
|
||||||
_syncGestureListeners();
|
_syncGestureListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +119,13 @@ class GestureDetector extends StatefulComponent {
|
|||||||
return _pan;
|
return _pan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PinchGestureRecognizer _pinch;
|
||||||
|
PinchGestureRecognizer _ensurePinch() {
|
||||||
|
if (_pinch == null)
|
||||||
|
_pinch = new PinchGestureRecognizer(router: _router);
|
||||||
|
return _pinch;
|
||||||
|
}
|
||||||
|
|
||||||
void didMount() {
|
void didMount() {
|
||||||
super.didMount();
|
super.didMount();
|
||||||
_syncGestureListeners();
|
_syncGestureListeners();
|
||||||
@ -121,6 +139,7 @@ class GestureDetector extends StatefulComponent {
|
|||||||
_verticalDrag = _ensureDisposed(_verticalDrag);
|
_verticalDrag = _ensureDisposed(_verticalDrag);
|
||||||
_horizontalDrag = _ensureDisposed(_horizontalDrag);
|
_horizontalDrag = _ensureDisposed(_horizontalDrag);
|
||||||
_pan = _ensureDisposed(_pan);
|
_pan = _ensureDisposed(_pan);
|
||||||
|
_pinch = _ensureDisposed(_pinch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _syncGestureListeners() {
|
void _syncGestureListeners() {
|
||||||
@ -130,6 +149,7 @@ class GestureDetector extends StatefulComponent {
|
|||||||
_syncVerticalDrag();
|
_syncVerticalDrag();
|
||||||
_syncHorizontalDrag();
|
_syncHorizontalDrag();
|
||||||
_syncPan();
|
_syncPan();
|
||||||
|
_syncPinch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _syncTap() {
|
void _syncTap() {
|
||||||
@ -186,6 +206,17 @@ class GestureDetector extends StatefulComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _syncPinch() {
|
||||||
|
if (onPinchStart == null && onPinchUpdate == null && onPinchEnd == null) {
|
||||||
|
_pinch = _ensureDisposed(_pan);
|
||||||
|
} else {
|
||||||
|
_ensurePinch()
|
||||||
|
..onStart = onPinchStart
|
||||||
|
..onUpdate = onPinchUpdate
|
||||||
|
..onEnd = onPinchEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) {
|
GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) {
|
||||||
recognizer?.dispose();
|
recognizer?.dispose();
|
||||||
return null;
|
return null;
|
||||||
@ -204,6 +235,8 @@ class GestureDetector extends StatefulComponent {
|
|||||||
_horizontalDrag.addPointer(event);
|
_horizontalDrag.addPointer(event);
|
||||||
if (_pan != null)
|
if (_pan != null)
|
||||||
_pan.addPointer(event);
|
_pan.addPointer(event);
|
||||||
|
if (_pinch != null)
|
||||||
|
_pinch.addPointer(event);
|
||||||
return EventDisposition.processed;
|
return EventDisposition.processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user