Commit 07a9cc75 authored by Adam Barth's avatar Adam Barth

DoubleTap gesture asserts when rejected

The pointer router was using an iteration pattern that always delivers
handleEvent calls even if you remove a route during the iteration.
That's awkward to program against and causes trouble for the double-tap
gesture.

This patch switches PointerRouter to using a re-entrant iteration
pattern that supports removing routes (but not adding routes) during the
iteration.
parent 17ae19e8
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:collection';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'events.dart'; import 'events.dart';
...@@ -13,14 +15,14 @@ typedef void PointerExceptionHandler(PointerRouter source, PointerEvent event, P ...@@ -13,14 +15,14 @@ typedef void PointerExceptionHandler(PointerRouter source, PointerEvent event, P
/// A routing table for [PointerEvent] events. /// A routing table for [PointerEvent] events.
class PointerRouter { class PointerRouter {
final Map<int, List<PointerRoute>> _routeMap = new Map<int, List<PointerRoute>>(); final Map<int, LinkedHashSet<PointerRoute>> _routeMap = new Map<int, LinkedHashSet<PointerRoute>>();
/// Adds a route to the routing table. /// Adds a route to the routing table.
/// ///
/// Whenever this object routes a [PointerEvent] corresponding to /// Whenever this object routes a [PointerEvent] corresponding to
/// pointer, call route. /// pointer, call route.
void addRoute(int pointer, PointerRoute route) { void addRoute(int pointer, PointerRoute route) {
List<PointerRoute> routes = _routeMap.putIfAbsent(pointer, () => new List<PointerRoute>()); LinkedHashSet<PointerRoute> routes = _routeMap.putIfAbsent(pointer, () => new LinkedHashSet<PointerRoute>());
assert(!routes.contains(route)); assert(!routes.contains(route));
routes.add(route); routes.add(route);
} }
...@@ -31,7 +33,7 @@ class PointerRouter { ...@@ -31,7 +33,7 @@ class PointerRouter {
/// pointer. Requires that this route was previously added to the router. /// pointer. Requires that this route was previously added to the router.
void removeRoute(int pointer, PointerRoute route) { void removeRoute(int pointer, PointerRoute route) {
assert(_routeMap.containsKey(pointer)); assert(_routeMap.containsKey(pointer));
List<PointerRoute> routes = _routeMap[pointer]; LinkedHashSet<PointerRoute> routes = _routeMap[pointer];
assert(routes.contains(route)); assert(routes.contains(route));
routes.remove(route); routes.remove(route);
if (routes.isEmpty) if (routes.isEmpty)
...@@ -53,10 +55,12 @@ class PointerRouter { ...@@ -53,10 +55,12 @@ class PointerRouter {
/// Routes are called in the order in which they were added to the /// Routes are called in the order in which they were added to the
/// PointerRouter object. /// PointerRouter object.
void route(PointerEvent event) { void route(PointerEvent event) {
List<PointerRoute> routes = _routeMap[event.pointer]; LinkedHashSet<PointerRoute> routes = _routeMap[event.pointer];
if (routes == null) if (routes == null)
return; return;
for (PointerRoute route in new List<PointerRoute>.from(routes)) { for (PointerRoute route in new List<PointerRoute>.from(routes)) {
if (!routes.contains(route))
continue;
try { try {
route(event); route(event);
} catch (exception, stack) { } catch (exception, stack) {
......
...@@ -27,4 +27,19 @@ void main() { ...@@ -27,4 +27,19 @@ void main() {
router.route(pointer3.up()); router.route(pointer3.up());
expect(callbackRan, isFalse); expect(callbackRan, isFalse);
}); });
test('Supports re-entrant cancellation', () {
bool callbackRan = false;
void callback(PointerEvent event) {
callbackRan = true;
}
PointerRouter router = new PointerRouter();
router.addRoute(2, (PointerEvent event) {
router.removeRoute(2, callback);
});
router.addRoute(2, callback);
TestPointer pointer2 = new TestPointer(2);
router.route(pointer2.down(Point.origin));
expect(callbackRan, isFalse);
});
} }
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