Commit bef55951 authored by Adam Barth's avatar Adam Barth

Scrolls should start immediately when possible

If there are no other gestures in the arena, we should kick off the scroll
gesture right away. This change pulled a refactoring of how we dispatch events
to Widgets. Now we dispatch events to Widgets interleaved with their associated
RenderObjects. (Previously we dispatched to all of the RenderObjects first.)
parent 4adf7021
...@@ -28,7 +28,11 @@ class HitTestEntry { ...@@ -28,7 +28,11 @@ class HitTestEntry {
} }
class HitTestResult { class HitTestResult {
final List<HitTestEntry> path = new List<HitTestEntry>(); HitTestResult({ List<HitTestEntry> path })
: path = path != null ? path : new List<HitTestEntry>();
final List<HitTestEntry> path;
void add(HitTestEntry data) { void add(HitTestEntry data) {
path.add(data); path.add(data);
} }
......
...@@ -4,11 +4,9 @@ ...@@ -4,11 +4,9 @@
import 'dart:sky' as sky; import 'dart:sky' as sky;
import 'package:sky/base/hit_test.dart';
typedef void _Route(sky.PointerEvent event); typedef void _Route(sky.PointerEvent event);
class PointerRouter extends HitTestTarget { class PointerRouter {
final Map<int, List<_Route>> _routeMap = new Map<int, List<_Route>>(); final Map<int, List<_Route>> _routeMap = new Map<int, List<_Route>>();
void addRoute(int pointer, _Route route) { void addRoute(int pointer, _Route route) {
...@@ -26,15 +24,11 @@ class PointerRouter extends HitTestTarget { ...@@ -26,15 +24,11 @@ class PointerRouter extends HitTestTarget {
_routeMap.remove(pointer); _routeMap.remove(pointer);
} }
EventDisposition handleEvent(sky.Event e, HitTestEntry entry) { void route(sky.PointerEvent event) {
if (e is! sky.PointerEvent)
return EventDisposition.ignored;
sky.PointerEvent event = e;
List<_Route> routes = _routeMap[event.pointer]; List<_Route> routes = _routeMap[event.pointer];
if (routes == null) if (routes == null)
return EventDisposition.ignored; return;
for (_Route route in new List<_Route>.from(routes)) for (_Route route in new List<_Route>.from(routes))
route(event); route(event);
return EventDisposition.processed;
} }
} }
...@@ -42,37 +42,61 @@ class GestureArenaEntry { ...@@ -42,37 +42,61 @@ class GestureArenaEntry {
} }
} }
class _GestureArenaState {
final List<GestureArenaMember> members = new List<GestureArenaMember>();
bool isOpen = true;
void add(GestureArenaMember member) {
assert(isOpen);
members.add(member);
}
}
/// The first member to accept or the last member to not to reject wins. /// The first member to accept or the last member to not to reject wins.
class GestureArena { class GestureArena {
final Map<Object, List<GestureArenaMember>> _arenas = new Map<Object, List<GestureArenaMember>>(); final Map<Object, _GestureArenaState> _arenas = new Map<Object, _GestureArenaState>();
static final GestureArena instance = new GestureArena(); static final GestureArena instance = new GestureArena();
GestureArenaEntry add(Object key, GestureArenaMember member) { GestureArenaEntry add(Object key, GestureArenaMember member) {
List<GestureArenaMember> members = _arenas.putIfAbsent(key, () => new List<GestureArenaMember>()); _GestureArenaState state = _arenas.putIfAbsent(key, () => new _GestureArenaState());
members.add(member); state.add(member);
return new GestureArenaEntry._(this, key, member); return new GestureArenaEntry._(this, key, member);
} }
void close(Object key) {
_GestureArenaState state = _arenas[key];
if (state == null)
return; // This arena either never existed or has been resolved.
state.isOpen = false;
_tryToResolveArena(key, state);
}
void _tryToResolveArena(Object key, _GestureArenaState state) {
assert(_arenas[key] == state);
assert(!state.isOpen);
if (state.members.length == 1) {
_arenas.remove(key);
state.members.first.acceptGesture(key);
} else if (state.members.isEmpty) {
_arenas.remove(key);
}
}
void _resolve(Object key, GestureArenaMember member, GestureDisposition disposition) { void _resolve(Object key, GestureArenaMember member, GestureDisposition disposition) {
List<GestureArenaMember> members = _arenas[key]; _GestureArenaState state = _arenas[key];
if (members == null) if (state == null)
return; // This arena has already resolved. return; // This arena has already resolved.
assert(members != null); assert(!state.isOpen);
assert(members.contains(member)); assert(state.members.contains(member));
if (disposition == GestureDisposition.rejected) { if (disposition == GestureDisposition.rejected) {
members.remove(member); state.members.remove(member);
member.rejectGesture(key); member.rejectGesture(key);
if (members.length == 1) { _tryToResolveArena(key, state);
_arenas.remove(key);
members.first.acceptGesture(key);
} else if (members.isEmpty) {
_arenas.remove(key);
}
} else { } else {
assert(disposition == GestureDisposition.accepted); assert(disposition == GestureDisposition.accepted);
_arenas.remove(key); _arenas.remove(key);
for (GestureArenaMember rejectedMember in members) { for (GestureArenaMember rejectedMember in state.members) {
if (rejectedMember != member) if (rejectedMember != member)
rejectedMember.rejectGesture(key); rejectedMember.rejectGesture(key);
} }
......
...@@ -118,12 +118,14 @@ abstract class PrimaryPointerGestureRecognizer extends GestureRecognizer { ...@@ -118,12 +118,14 @@ abstract class PrimaryPointerGestureRecognizer extends GestureRecognizer {
} }
void rejectGesture(int pointer) { void rejectGesture(int pointer) {
_stopTimer(); if (pointer == primaryPointer) {
if (pointer == primaryPointer) _stopTimer();
state = GestureRecognizerState.defunct; state = GestureRecognizerState.defunct;
}
} }
void didStopTrackingLastPointer() { void didStopTrackingLastPointer() {
_stopTimer();
state = GestureRecognizerState.ready; state = GestureRecognizerState.ready;
} }
......
...@@ -7,6 +7,7 @@ import 'dart:sky' as sky; ...@@ -7,6 +7,7 @@ import 'dart:sky' as sky;
import 'package:sky/base/pointer_router.dart'; import 'package:sky/base/pointer_router.dart';
import 'package:sky/base/hit_test.dart'; import 'package:sky/base/hit_test.dart';
import 'package:sky/base/scheduler.dart' as scheduler; import 'package:sky/base/scheduler.dart' as scheduler;
import 'package:sky/gestures/arena.dart';
import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart'; import 'package:sky/rendering/object.dart';
import 'package:sky/rendering/view.dart'; import 'package:sky/rendering/view.dart';
...@@ -30,7 +31,12 @@ class PointerState { ...@@ -30,7 +31,12 @@ class PointerState {
typedef void EventListener(sky.Event event); typedef void EventListener(sky.Event event);
class SkyBinding { class BindingHitTestEntry extends HitTestEntry {
const BindingHitTestEntry(HitTestTarget target, this.result) : super(target);
final HitTestResult result;
}
class SkyBinding extends HitTestTarget {
SkyBinding({ RenderBox root: null, RenderView renderViewOverride }) { SkyBinding({ RenderBox root: null, RenderView renderViewOverride }) {
assert(_instance == null); assert(_instance == null);
...@@ -88,8 +94,8 @@ class SkyBinding { ...@@ -88,8 +94,8 @@ class SkyBinding {
} else if (event is sky.GestureEvent) { } else if (event is sky.GestureEvent) {
dispatchEvent(event, hitTest(new Point(event.x, event.y))); dispatchEvent(event, hitTest(new Point(event.x, event.y)));
} else { } else {
for (EventListener e in _eventListeners) for (EventListener listener in _eventListeners)
e(event); listener(event);
} }
} }
...@@ -130,7 +136,7 @@ class SkyBinding { ...@@ -130,7 +136,7 @@ class SkyBinding {
HitTestResult hitTest(Point position) { HitTestResult hitTest(Point position) {
HitTestResult result = new HitTestResult(); HitTestResult result = new HitTestResult();
result.add(new HitTestEntry(pointerRouter)); result.add(new BindingHitTestEntry(this, result));
_renderView.hitTest(result, position: position); _renderView.hitTest(result, position: position);
return result; return result;
} }
...@@ -148,6 +154,16 @@ class SkyBinding { ...@@ -148,6 +154,16 @@ class SkyBinding {
return disposition; return disposition;
} }
EventDisposition handleEvent(sky.Event e, BindingHitTestEntry entry) {
if (e is! sky.PointerEvent)
return EventDisposition.ignored;
sky.PointerEvent event = e;
pointerRouter.route(event);
if (event.type == 'pointerdown')
GestureArena.instance.close(event.pointer);
return EventDisposition.processed;
}
String toString() => 'Render Tree:\n${_renderView}'; String toString() => 'Render Tree:\n${_renderView}';
void debugDumpRenderTree() { void debugDumpRenderTree() {
......
...@@ -1273,12 +1273,9 @@ class WidgetSkyBinding extends SkyBinding { ...@@ -1273,12 +1273,9 @@ class WidgetSkyBinding extends SkyBinding {
assert(SkyBinding.instance is WidgetSkyBinding); assert(SkyBinding.instance is WidgetSkyBinding);
} }
EventDisposition dispatchEvent(sky.Event event, HitTestResult result) { EventDisposition handleEvent(sky.Event event, BindingHitTestEntry entry) {
assert(SkyBinding.instance == this); EventDisposition disposition = EventDisposition.ignored;
EventDisposition disposition = super.dispatchEvent(event, result); for (HitTestEntry entry in entry.result.path.reversed) {
if (disposition == EventDisposition.consumed)
return EventDisposition.consumed;
for (HitTestEntry entry in result.path.reversed) {
if (entry.target is! RenderObject) if (entry.target is! RenderObject)
continue; continue;
for (Widget target in RenderObjectWrapper.getWidgetsForRenderObject(entry.target)) { for (Widget target in RenderObjectWrapper.getWidgetsForRenderObject(entry.target)) {
...@@ -1293,7 +1290,7 @@ class WidgetSkyBinding extends SkyBinding { ...@@ -1293,7 +1290,7 @@ class WidgetSkyBinding extends SkyBinding {
target = target._parent; target = target._parent;
} }
} }
return disposition; return combineEventDispositions(disposition, super.handleEvent(event, entry));
} }
void beginFrame(double timeStamp) { void beginFrame(double timeStamp) {
......
import 'dart:sky' as sky; import 'dart:sky' as sky;
import 'package:sky/base/hit_test.dart';
import 'package:sky/base/pointer_router.dart'; import 'package:sky/base/pointer_router.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
...@@ -13,15 +12,18 @@ void main() { ...@@ -13,15 +12,18 @@ void main() {
callbackRan = true; callbackRan = true;
} }
TestPointer pointer2 = new TestPointer(2);
TestPointer pointer3 = new TestPointer(3);
PointerRouter router = new PointerRouter(); PointerRouter router = new PointerRouter();
router.addRoute(3, callback); router.addRoute(3, callback);
expect(router.handleEvent(new TestPointerEvent(pointer: 2), null), equals(EventDisposition.ignored)); router.route(pointer2.down());
expect(callbackRan, isFalse); expect(callbackRan, isFalse);
expect(router.handleEvent(new TestPointerEvent(pointer: 3), null), equals(EventDisposition.processed)); router.route(pointer3.down());
expect(callbackRan, isTrue); expect(callbackRan, isTrue);
callbackRan = false; callbackRan = false;
router.removeRoute(3, callback); router.removeRoute(3, callback);
expect(router.handleEvent(new TestPointerEvent(pointer: 3), null), equals(EventDisposition.ignored)); router.route(pointer3.up());
expect(callbackRan, isFalse); expect(callbackRan, isFalse);
}); });
} }
import 'dart:sky' as sky; import 'dart:sky' as sky;
export 'dart:sky' show Point;
class TestPointerEvent extends sky.PointerEvent { class TestPointerEvent extends sky.PointerEvent {
TestPointerEvent({ TestPointerEvent({
this.type, this.type,
...@@ -79,3 +81,60 @@ class TestGestureEvent extends sky.GestureEvent { ...@@ -79,3 +81,60 @@ class TestGestureEvent extends sky.GestureEvent {
double velocityX; double velocityX;
double velocityY; double velocityY;
} }
class TestPointer {
TestPointer([ this.pointer = 1 ]);
int pointer;
bool isDown = false;
sky.Point location;
sky.PointerEvent down([sky.Point newLocation = sky.Point.origin ]) {
assert(!isDown);
isDown = true;
location = newLocation;
return new TestPointerEvent(
type: 'pointerdown',
pointer: pointer,
x: location.x,
y: location.y
);
}
sky.PointerEvent move([sky.Point newLocation = sky.Point.origin ]) {
assert(isDown);
sky.Offset delta = newLocation - location;
location = newLocation;
return new TestPointerEvent(
type: 'pointermove',
pointer: pointer,
x: newLocation.x,
y: newLocation.y,
dx: delta.dx,
dy: delta.dy
);
}
sky.PointerEvent up() {
assert(isDown);
isDown = false;
return new TestPointerEvent(
type: 'pointerup',
pointer: pointer,
x: location.x,
y: location.y
);
}
sky.PointerEvent cancel() {
assert(isDown);
isDown = false;
return new TestPointerEvent(
type: 'pointercancel',
pointer: pointer,
x: location.x,
y: location.y
);
}
}
...@@ -52,6 +52,7 @@ void main() { ...@@ -52,6 +52,7 @@ void main() {
GestureArenaEntry firstEntry = arena.add(primaryKey, first); GestureArenaEntry firstEntry = arena.add(primaryKey, first);
arena.add(primaryKey, second); arena.add(primaryKey, second);
arena.close(primaryKey);
expect(firstAcceptRan, isFalse); expect(firstAcceptRan, isFalse);
expect(firstRejectRan, isFalse); expect(firstRejectRan, isFalse);
......
import 'package:quiver/testing/async.dart'; import 'package:quiver/testing/async.dart';
import 'package:sky/base/pointer_router.dart'; import 'package:sky/base/pointer_router.dart';
import 'package:sky/gestures/arena.dart';
import 'package:sky/gestures/long_press.dart'; import 'package:sky/gestures/long_press.dart';
import 'package:sky/gestures/show_press.dart'; import 'package:sky/gestures/show_press.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
...@@ -32,8 +33,9 @@ void main() { ...@@ -32,8 +33,9 @@ void main() {
new FakeAsync().run((async) { new FakeAsync().run((async) {
longPress.addPointer(down); longPress.addPointer(down);
GestureArena.instance.close(5);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
router.handleEvent(down, null); router.route(down);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 300)); async.elapse(new Duration(milliseconds: 300));
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
...@@ -55,12 +57,13 @@ void main() { ...@@ -55,12 +57,13 @@ void main() {
new FakeAsync().run((async) { new FakeAsync().run((async) {
longPress.addPointer(down); longPress.addPointer(down);
GestureArena.instance.close(5);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
router.handleEvent(down, null); router.route(down);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 300)); async.elapse(new Duration(milliseconds: 300));
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
router.handleEvent(up, null); router.route(up);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
async.elapse(new Duration(seconds: 1)); async.elapse(new Duration(seconds: 1));
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
...@@ -87,9 +90,10 @@ void main() { ...@@ -87,9 +90,10 @@ void main() {
new FakeAsync().run((async) { new FakeAsync().run((async) {
showPress.addPointer(down); showPress.addPointer(down);
longPress.addPointer(down); longPress.addPointer(down);
GestureArena.instance.close(5);
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
router.handleEvent(down, null); router.route(down);
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 300)); async.elapse(new Duration(milliseconds: 300));
......
import 'dart:sky' as sky; import 'dart:sky' as sky;
import 'package:sky/base/pointer_router.dart'; import 'package:sky/base/pointer_router.dart';
import 'package:sky/gestures/arena.dart';
import 'package:sky/gestures/scroll.dart'; import 'package:sky/gestures/scroll.dart';
import 'package:sky/gestures/tap.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../engine/mock_events.dart'; import '../engine/mock_events.dart';
TestPointerEvent down = new TestPointerEvent(
pointer: 5,
type: 'pointerdown',
x: 10.0,
y: 10.0
);
TestPointerEvent move1 = new TestPointerEvent(
pointer: 5,
type: 'pointermove',
x: 20.0,
y: 20.0,
dx: 10.0,
dy: 10.0
);
TestPointerEvent move2 = new TestPointerEvent(
pointer: 5,
type: 'pointermove',
x: 20.0,
y: 25.0,
dx: 0.0,
dy: 5.0
);
TestPointerEvent up = new TestPointerEvent(
pointer: 5,
type: 'pointerup',
x: 20.0,
y: 25.0
);
void main() { void main() {
test('Should recognize scroll', () { test('Should recognize pan', () {
PointerRouter router = new PointerRouter(); PointerRouter router = new PointerRouter();
PanGestureRecognizer scroll = new PanGestureRecognizer(router: router); PanGestureRecognizer pan = new PanGestureRecognizer(router: router);
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
bool didStartScroll = false; bool didStartPan = false;
scroll.onStart = () { pan.onStart = () {
didStartScroll = true; didStartPan = true;
}; };
sky.Offset updateOffset; sky.Offset updatedScrollDelta;
scroll.onUpdate = (sky.Offset offset) { pan.onUpdate = (sky.Offset offset) {
updateOffset = offset; updatedScrollDelta = offset;
}; };
bool didEndScroll = false; bool didEndPan = false;
scroll.onEnd = () { pan.onEnd = () {
didEndScroll = true; didEndPan = true;
}; };
scroll.addPointer(down); bool didTap = false;
expect(didStartScroll, isFalse); tap.onTap = () {
expect(updateOffset, isNull); didTap = true;
expect(didEndScroll, isFalse); };
router.handleEvent(down, null);
expect(didStartScroll, isFalse);
expect(updateOffset, isNull);
expect(didEndScroll, isFalse);
router.handleEvent(move1, null);
expect(didStartScroll, isTrue);
didStartScroll = false;
expect(updateOffset, new sky.Offset(10.0, -10.0));
updateOffset = null;
expect(didEndScroll, isFalse);
router.handleEvent(move2, null);
expect(didStartScroll, isFalse);
expect(updateOffset, new sky.Offset(0.0, -5.0));
updateOffset = null;
expect(didEndScroll, isFalse);
router.handleEvent(up, null);
expect(didStartScroll, isFalse);
expect(updateOffset, isNull);
expect(didEndScroll, isTrue);
didEndScroll = false;
scroll.dispose(); TestPointer pointer = new TestPointer(5);
sky.PointerEvent down = pointer.down(new Point(10.0, 10.0));
pan.addPointer(down);
tap.addPointer(down);
GestureArena.instance.close(5);
expect(didStartPan, isFalse);
expect(updatedScrollDelta, isNull);
expect(didEndPan, isFalse);
expect(didTap, isFalse);
router.route(down);
expect(didStartPan, isFalse);
expect(updatedScrollDelta, isNull);
expect(didEndPan, isFalse);
expect(didTap, isFalse);
router.route(pointer.move(new Point(20.0, 20.0)));
expect(didStartPan, isTrue);
didStartPan = false;
expect(updatedScrollDelta, new sky.Offset(10.0, -10.0));
updatedScrollDelta = null;
expect(didEndPan, isFalse);
expect(didTap, isFalse);
router.route(pointer.move(new Point(20.0, 25.0)));
expect(didStartPan, isFalse);
expect(updatedScrollDelta, new sky.Offset(0.0, -5.0));
updatedScrollDelta = null;
expect(didEndPan, isFalse);
expect(didTap, isFalse);
router.route(pointer.up());
expect(didStartPan, isFalse);
expect(updatedScrollDelta, isNull);
expect(didEndPan, isTrue);
didEndPan = false;
expect(didTap, isFalse);
pan.dispose();
tap.dispose();
}); });
} }
import 'package:quiver/testing/async.dart'; import 'package:quiver/testing/async.dart';
import 'package:sky/base/pointer_router.dart'; import 'package:sky/base/pointer_router.dart';
import 'package:sky/gestures/arena.dart';
import 'package:sky/gestures/show_press.dart'; import 'package:sky/gestures/show_press.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
...@@ -31,8 +32,9 @@ void main() { ...@@ -31,8 +32,9 @@ void main() {
new FakeAsync().run((async) { new FakeAsync().run((async) {
showPress.addPointer(down); showPress.addPointer(down);
GestureArena.instance.close(5);
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
router.handleEvent(down, null); router.route(down);
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 300)); async.elapse(new Duration(milliseconds: 300));
expect(showPressRecognized, isTrue); expect(showPressRecognized, isTrue);
...@@ -52,12 +54,13 @@ void main() { ...@@ -52,12 +54,13 @@ void main() {
new FakeAsync().run((async) { new FakeAsync().run((async) {
showPress.addPointer(down); showPress.addPointer(down);
GestureArena.instance.close(5);
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
router.handleEvent(down, null); router.route(down);
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 50)); async.elapse(new Duration(milliseconds: 50));
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
router.handleEvent(up, null); router.route(up);
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
async.elapse(new Duration(seconds: 1)); async.elapse(new Duration(seconds: 1));
expect(showPressRecognized, isFalse); expect(showPressRecognized, isFalse);
......
import 'package:sky/base/pointer_router.dart'; import 'package:sky/base/pointer_router.dart';
import 'package:sky/gestures/arena.dart';
import 'package:sky/gestures/tap.dart'; import 'package:sky/gestures/tap.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
...@@ -22,8 +23,9 @@ void main() { ...@@ -22,8 +23,9 @@ void main() {
); );
tap.addPointer(down); tap.addPointer(down);
GestureArena.instance.close(5);
expect(tapRecognized, isFalse); expect(tapRecognized, isFalse);
router.handleEvent(down, null); router.route(down);
expect(tapRecognized, isFalse); expect(tapRecognized, isFalse);
TestPointerEvent up = new TestPointerEvent( TestPointerEvent up = new TestPointerEvent(
...@@ -33,7 +35,7 @@ void main() { ...@@ -33,7 +35,7 @@ void main() {
y: 9.0 y: 9.0
); );
router.handleEvent(up, null); router.route(up);
expect(tapRecognized, isTrue); expect(tapRecognized, isTrue);
tap.dispose(); tap.dispose();
......
import 'package:sky/widgets.dart';
import 'package:test/test.dart';
import '../engine/mock_events.dart';
import 'widget_tester.dart';
void main() {
test('Uncontested scrolls start immediately', () {
WidgetTester tester = new WidgetTester();
TestPointer pointer = new TestPointer(7);
bool didStartScroll = false;
double updatedScrollDelta;
bool didEndScroll = false;
Widget builder() {
return new GestureDetector(
onVerticalScrollStart: () {
didStartScroll = true;
},
onVerticalScrollUpdate: (double scrollDelta) {
updatedScrollDelta = scrollDelta;
},
onVerticalScrollEnd: () {
didEndScroll = true;
},
child: new Container()
);
}
tester.pumpFrame(builder);
expect(didStartScroll, isFalse);
expect(updatedScrollDelta, isNull);
expect(didEndScroll, isFalse);
Point firstLocation = new Point(10.0, 10.0);
tester.dispatchEvent(pointer.down(firstLocation), firstLocation);
expect(didStartScroll, isTrue);
didStartScroll = false;
expect(updatedScrollDelta, isNull);
expect(didEndScroll, isFalse);
Point secondLocation = new Point(10.0, 9.0);
tester.dispatchEvent(pointer.move(secondLocation), secondLocation);
expect(didStartScroll, isFalse);
expect(updatedScrollDelta, 1.0);
updatedScrollDelta = null;
expect(didEndScroll, isFalse);
tester.dispatchEvent(pointer.up(), secondLocation);
expect(didStartScroll, isFalse);
expect(updatedScrollDelta, isNull);
expect(didEndScroll, isTrue);
didEndScroll = false;
tester.pumpFrame(() => new Container());
});
}
...@@ -89,29 +89,22 @@ class WidgetTester { ...@@ -89,29 +89,22 @@ class WidgetTester {
return SkyBinding.instance.dispatchEvent(event, result); return SkyBinding.instance.dispatchEvent(event, result);
} }
void tap(Widget widget) { void tap(Widget widget, { int pointer: 1 }) {
Point location = getCenter(widget); Point location = getCenter(widget);
HitTestResult result = _hitTest(location); HitTestResult result = _hitTest(location);
_dispatchEvent(new TestPointerEvent(type: 'pointerdown', x: location.x, y: location.y), result); TestPointer p = new TestPointer(pointer);
_dispatchEvent(new TestPointerEvent(type: 'pointerup', x: location.x, y: location.y), result); _dispatchEvent(p.down(location), result);
_dispatchEvent(p.up(), result);
} }
void scroll(Widget widget, Offset offset) { void scroll(Widget widget, Offset offset, { int pointer: 1 }) {
Point startLocation = getCenter(widget); Point startLocation = getCenter(widget);
HitTestResult result = _hitTest(startLocation);
_dispatchEvent(new TestPointerEvent(type: 'pointerdown', x: startLocation.x, y: startLocation.y), result);
Point endLocation = startLocation + offset; Point endLocation = startLocation + offset;
_dispatchEvent( HitTestResult result = _hitTest(startLocation);
new TestPointerEvent( TestPointer p = new TestPointer(pointer);
type: 'pointermove', _dispatchEvent(p.down(startLocation), result);
x: endLocation.x, _dispatchEvent(p.move(endLocation), result);
y: endLocation.y, _dispatchEvent(p.up(), result);
dx: offset.dx,
dy: offset.dy
),
result
);
_dispatchEvent(new TestPointerEvent(type: 'pointerup', x: endLocation.x, y: endLocation.y), result);
} }
void dispatchEvent(sky.Event event, Point location) { void dispatchEvent(sky.Event event, Point location) {
......
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