提交 3a25de84 编写于 作者: H Hans Muller

Merge pull request #974 from HansMuller/dismissable-uses-gestures

Convert Dismissable to use gestures

Convert Dismissable to use the ScrollStart, ScrollUpdate, and ScrollEnd gestures. Support for fling gestures is TBD.

Included a basic unit test that checks that one item can be dismissed with a press-drag-release gesture.

Fixed the scroll gesture recognizer: if the last pointer goes up and candidate recognizers still exist, then reject the gesture.
......@@ -76,6 +76,10 @@ abstract class _ScrollGestureRecognizer<T extends dynamic> extends GestureRecogn
}
void didStopTrackingLastPointer() {
if (_state == ScrollState.possible) {
resolve(GestureDisposition.rejected);
return;
}
bool wasAccepted = (_state == ScrollState.accepted);
_state = ScrollState.ready;
if (wasAccepted && onEnd != null)
......
......@@ -10,6 +10,7 @@ import 'package:sky/animation/curves.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/transitions.dart';
import 'package:sky/widgets/framework.dart';
import 'package:sky/widgets/gesture_detector.dart';
const Duration _kCardDismissFadeout = const Duration(milliseconds: 200);
const Duration _kCardDismissResize = const Duration(milliseconds: 300);
......@@ -99,45 +100,33 @@ class Dismissable extends StatefulComponent {
_maybeCallOnResized();
}
EventDisposition _handlePointerDown(sky.PointerEvent event) {
void _handleScrollStart() {
if (_fadePerformance.isAnimating)
return EventDisposition.processed;
return;
_dragUnderway = true;
_dragX = 0.0;
_fadePerformance.progress = 0.0;
return EventDisposition.processed;
}
EventDisposition _handlePointerMove(sky.PointerEvent event) {
if (!_isActive)
return EventDisposition.ignored;
if (_fadePerformance.isAnimating)
return EventDisposition.processed;
void _handleScrollUpdate(double scrollOffset) {
if (!_isActive || _fadePerformance.isAnimating)
return;
double oldDragX = _dragX;
_dragX += event.dx;
_dragX -= scrollOffset;
if (oldDragX.sign != _dragX.sign)
setState(() {}); // Rebuild to update the new drag endpoint.
if (!_fadePerformance.isAnimating)
_fadePerformance.progress = _dragX.abs() / (_size.width * _kDismissCardThreshold);
return EventDisposition.processed;
}
EventDisposition _handlePointerUpOrCancel(_) {
if (!_isActive)
return EventDisposition.ignored;
if (_fadePerformance.isAnimating)
return EventDisposition.processed;
_handleScrollEnd() {
if (!_isActive || _fadePerformance.isAnimating)
return;
_dragUnderway = false;
if (_fadePerformance.isCompleted)
_startResizePerformance();
else if (!_fadePerformance.isAnimating)
_fadePerformance.reverse();
return EventDisposition.processed;
}
bool _isHorizontalFlingGesture(sky.GestureEvent event) {
......@@ -185,22 +174,23 @@ class Dismissable extends StatefulComponent {
height: dismissHeight);
}
return new Listener(
onPointerDown: _handlePointerDown,
onPointerMove: _handlePointerMove,
onPointerUp: _handlePointerUpOrCancel,
onPointerCancel: _handlePointerUpOrCancel,
onGestureFlingStart: _handleFlingStart,
child: new SizeObserver(
callback: _handleSizeChanged,
child: new FadeTransition(
performance: _fadePerformance,
onCompleted: _handleFadeCompleted,
opacity: new AnimatedValue<double>(1.0, end: 0.0),
child: new SlideTransition(
return new GestureDetector(
onHorizontalScrollStart: _handleScrollStart,
onHorizontalScrollUpdate: _handleScrollUpdate,
onHorizontalScrollEnd: _handleScrollEnd,
child: new Listener(
onGestureFlingStart: _handleFlingStart,
child: new SizeObserver(
callback: _handleSizeChanged,
child: new FadeTransition(
performance: _fadePerformance,
position: new AnimatedValue<Point>(Point.origin, end: _activeCardDragEndPoint),
child: child
onCompleted: _handleFadeCompleted,
opacity: new AnimatedValue<double>(1.0, end: 0.0),
child: new SlideTransition(
performance: _fadePerformance,
position: new AnimatedValue<Point>(Point.origin, end: _activeCardDragEndPoint),
child: child
)
)
)
)
......
import 'package:quiver/testing/async.dart';
import 'package:sky/widgets.dart';
import 'package:test/test.dart';
import '../engine/mock_events.dart';
import 'widget_tester.dart';
void main() {
test('Horizontal drag triggers dismiss', () {
WidgetTester tester = new WidgetTester();
TestPointer pointer = new TestPointer(5);
const double itemHeight = 50.0;
List<int> dismissedItems = [];
void handleOnResized(item) {
expect(dismissedItems.contains(item), isFalse);
}
void handleOnDismissed(item) {
expect(dismissedItems.contains(item), isFalse);
dismissedItems.add(item);
}
Widget buildDismissableItem(int item) {
return new Dismissable(
key: new ValueKey<int>(item),
onDismissed: () { handleOnDismissed(item); },
onResized: () { handleOnResized(item); },
child: new Container(
height: itemHeight,
child: new Text(item.toString())
)
);
}
Widget builder() {
return new Container(
padding: const EdgeDims.all(10.0),
child: new ScrollableList<int>(
items: [0, 1, 2, 3, 4, 5],
itemBuilder: buildDismissableItem,
scrollDirection: ScrollDirection.vertical,
itemExtent: itemHeight
)
);
}
tester.pumpFrame(builder);
Widget item3 = tester.findText("3");
expect(item3, isNotNull);
expect(dismissedItems, isEmpty);
// Gesture: press-drag-release from the Dismissable's top-left corner
// to its top-right corner. Triggers the resize animation which concludes
// by calling onDismissed().
Point downLocation = tester.getTopLeft(item3);
Point upLocation = tester.getTopRight(item3);
tester.dispatchEvent(pointer.down(downLocation), downLocation);
tester.dispatchEvent(pointer.move(upLocation), upLocation);
tester.dispatchEvent(pointer.up(), upLocation);
new FakeAsync().run((async) {
tester.pumpFrame(builder); // start the resize animation
tester.pumpFrame(builder, 1000.0); // finish the resize animation
async.elapse(new Duration(seconds: 1));
tester.pumpFrame(builder, 2000.0); // dismiss
async.elapse(new Duration(seconds: 1));
expect(dismissedItems, equals([3]));
expect(tester.findText("3"), isNull);
});
});
}
......@@ -83,6 +83,20 @@ class WidgetTester {
return box.localToGlobal(box.size.center(Point.origin));
}
Point getTopLeft(Widget widget) {
assert(widget != null);
RenderBox box = widget.renderObject as RenderBox;
assert(box != null);
return box.localToGlobal(Point.origin);
}
Point getTopRight(Widget widget) {
assert(widget != null);
RenderBox box = widget.renderObject as RenderBox;
assert(box != null);
return box.localToGlobal(box.size.topRight(Point.origin));
}
HitTestResult _hitTest(Point location) => SkyBinding.instance.hitTest(location);
EventDisposition _dispatchEvent(sky.Event event, HitTestResult result) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册