1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 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:collection';
import 'package:flutter/foundation.dart';
import 'events.dart';
/// A callback that receives a [PointerEvent]
typedef void PointerRoute(PointerEvent event);
/// A routing table for [PointerEvent] events.
class PointerRouter {
final Map<int, LinkedHashSet<PointerRoute>> _routeMap = new Map<int, LinkedHashSet<PointerRoute>>();
final LinkedHashSet<PointerRoute> _globalRoutes = new LinkedHashSet<PointerRoute>();
/// Adds a route to the routing table.
///
/// Whenever this object routes a [PointerEvent] corresponding to
/// pointer, call route.
///
/// Routes added reentrantly within [PointerRouter.route] will take effect when
/// routing the next event.
void addRoute(int pointer, PointerRoute route) {
LinkedHashSet<PointerRoute> routes = _routeMap.putIfAbsent(pointer, () => new LinkedHashSet<PointerRoute>());
assert(!routes.contains(route));
routes.add(route);
}
/// Removes a route from the routing table.
///
/// No longer call route when routing a [PointerEvent] corresponding to
/// pointer. Requires that this route was previously added to the router.
///
/// Routes removed reentrantly within [PointerRouter.route] will take effect
/// immediately.
void removeRoute(int pointer, PointerRoute route) {
assert(_routeMap.containsKey(pointer));
LinkedHashSet<PointerRoute> routes = _routeMap[pointer];
assert(routes.contains(route));
routes.remove(route);
if (routes.isEmpty)
_routeMap.remove(pointer);
}
/// Adds a route to the global entry in the routing table.
///
/// Whenever this object routes a [PointerEvent], call route.
///
/// Routes added reentrantly within [PointerRouter.route] will take effect when
/// routing the next event.
void addGlobalRoute(PointerRoute route) {
assert(!_globalRoutes.contains(route));
_globalRoutes.add(route);
}
/// Removes a route from the global entry in the routing table.
///
/// No longer call route when routing a [PointerEvent]. Requires that this
/// route was previously added via [addGlobalRoute].
///
/// Routes removed reentrantly within [PointerRouter.route] will take effect
/// immediately.
void removeGlobalRoute(PointerRoute route) {
assert(_globalRoutes.contains(route));
_globalRoutes.remove(route);
}
void _dispatch(PointerEvent event, PointerRoute route) {
try {
route(event);
} catch (exception, stack) {
FlutterError.reportError(new FlutterErrorDetailsForPointerRouter(
exception: exception,
stack: stack,
library: 'gesture library',
context: 'while routing a pointer event',
router: this,
route: route,
event: event,
informationCollector: (StringBuffer information) {
information.writeln('Event:');
information.write(' $event');
}
));
}
}
/// Calls the routes registered for this pointer event.
///
/// Routes are called in the order in which they were added to the
/// PointerRouter object.
void route(PointerEvent event) {
LinkedHashSet<PointerRoute> routes = _routeMap[event.pointer];
List<PointerRoute> globalRoutes = new List<PointerRoute>.from(_globalRoutes);
if (routes != null) {
for (PointerRoute route in new List<PointerRoute>.from(routes)) {
if (routes.contains(route))
_dispatch(event, route);
}
}
for (PointerRoute route in globalRoutes) {
if (_globalRoutes.contains(route))
_dispatch(event, route);
}
}
}
/// Variant of [FlutterErrorDetails] with extra fields for the gestures
/// library's pointer router ([PointerRouter]).
///
/// See also [FlutterErrorDetailsForPointerEventDispatcher], which is also used
/// by the gestures library.
class FlutterErrorDetailsForPointerRouter extends FlutterErrorDetails {
/// Creates a [FlutterErrorDetailsForPointerRouter] object with the given
/// arguments setting the object's properties.
///
/// The gestures library calls this constructor when catching an exception
/// that will subsequently be reported using [FlutterError.onError].
const FlutterErrorDetailsForPointerRouter({
dynamic exception,
StackTrace stack,
String library,
String context,
this.router,
this.route,
this.event,
InformationCollector informationCollector,
bool silent: false
}) : super(
exception: exception,
stack: stack,
library: library,
context: context,
informationCollector: informationCollector,
silent: silent
);
/// The pointer router that caught the exception.
///
/// In a typical application, this is the value of [GestureBinding.pointerRouter] on
/// the binding ([GestureBinding.instance]).
final PointerRouter router;
/// The callback that threw the exception.
final PointerRoute route;
/// The pointer event that was being routed when the exception was raised.
final PointerEvent event;
}