pointer_router.dart 5.49 KB
Newer Older
Adam Barth's avatar
Adam Barth committed
1 2 3 4
// 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.

5
import 'package:flutter/foundation.dart';
6
import 'package:vector_math/vector_math_64.dart';
7

Kris Giesing's avatar
Kris Giesing committed
8
import 'events.dart';
Adam Barth's avatar
Adam Barth committed
9

Ian Hickson's avatar
Ian Hickson committed
10
/// A callback that receives a [PointerEvent]
11
typedef PointerRoute = void Function(PointerEvent event);
Adam Barth's avatar
Adam Barth committed
12

Ian Hickson's avatar
Ian Hickson committed
13
/// A routing table for [PointerEvent] events.
14
class PointerRouter {
15 16
  final Map<int, Map<PointerRoute, Matrix4>> _routeMap = <int, Map<PointerRoute, Matrix4>>{};
  final Map<PointerRoute, Matrix4> _globalRoutes = <PointerRoute, Matrix4>{};
Adam Barth's avatar
Adam Barth committed
17

Ian Hickson's avatar
Ian Hickson committed
18
  /// Adds a route to the routing table.
Adam Barth's avatar
Adam Barth committed
19
  ///
Ian Hickson's avatar
Ian Hickson committed
20
  /// Whenever this object routes a [PointerEvent] corresponding to
Adam Barth's avatar
Adam Barth committed
21
  /// pointer, call route.
22 23 24
  ///
  /// Routes added reentrantly within [PointerRouter.route] will take effect when
  /// routing the next event.
25
  void addRoute(int pointer, PointerRoute route, [Matrix4 transform]) {
26 27 28 29 30 31
    final Map<PointerRoute, Matrix4> routes = _routeMap.putIfAbsent(
      pointer,
      () => <PointerRoute, Matrix4>{},
    );
    assert(!routes.containsKey(route));
    routes[route] = transform;
Adam Barth's avatar
Adam Barth committed
32 33
  }

Ian Hickson's avatar
Ian Hickson committed
34
  /// Removes a route from the routing table.
Adam Barth's avatar
Adam Barth committed
35
  ///
Ian Hickson's avatar
Ian Hickson committed
36
  /// No longer call route when routing a [PointerEvent] corresponding to
Adam Barth's avatar
Adam Barth committed
37
  /// pointer. Requires that this route was previously added to the router.
38 39 40
  ///
  /// Routes removed reentrantly within [PointerRouter.route] will take effect
  /// immediately.
Adam Barth's avatar
Adam Barth committed
41
  void removeRoute(int pointer, PointerRoute route) {
Adam Barth's avatar
Adam Barth committed
42
    assert(_routeMap.containsKey(pointer));
43 44 45
    final Map<PointerRoute, Matrix4> routes = _routeMap[pointer];
    assert(routes.containsKey(route));
    routes.remove(route);
Adam Barth's avatar
Adam Barth committed
46 47 48 49
    if (routes.isEmpty)
      _routeMap.remove(pointer);
  }

50 51 52 53 54 55
  /// 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.
56
  void addGlobalRoute(PointerRoute route, [Matrix4 transform]) {
57 58
    assert(!_globalRoutes.containsKey(route));
    _globalRoutes[route] = transform;
59 60 61 62 63 64 65 66 67 68
  }

  /// 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) {
69 70
    assert(_globalRoutes.containsKey(route));
    _globalRoutes.remove(route);
71 72
  }

73
  void _dispatch(PointerEvent event, PointerRoute route, Matrix4 transform) {
74
    try {
75 76
      event = event.transformed(transform);
      route(event);
77
    } catch (exception, stack) {
78
      FlutterError.reportError(FlutterErrorDetailsForPointerRouter(
79 80 81
        exception: exception,
        stack: stack,
        library: 'gesture library',
82
        context: ErrorDescription('while routing a pointer event'),
83
        router: this,
84
        route: route,
85
        event: event,
86 87
        informationCollector: () sync* {
          yield DiagnosticsProperty<PointerEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty);
88
        },
89 90 91 92
      ));
    }
  }

93
  /// Calls the routes registered for this pointer event.
Adam Barth's avatar
Adam Barth committed
94
  ///
95 96
  /// Routes are called in the order in which they were added to the
  /// PointerRouter object.
Ian Hickson's avatar
Ian Hickson committed
97
  void route(PointerEvent event) {
98 99
    final Map<PointerRoute, Matrix4> routes = _routeMap[event.pointer];
    final Map<PointerRoute, Matrix4> copiedGlobalRoutes = Map<PointerRoute, Matrix4>.from(_globalRoutes);
100
    if (routes != null) {
101 102 103 104 105
      _dispatchEventToRoutes(
        event,
        routes,
        Map<PointerRoute, Matrix4>.from(routes),
      );
106
    }
107 108 109 110 111 112 113 114 115 116 117 118 119
    _dispatchEventToRoutes(event, _globalRoutes, copiedGlobalRoutes);
  }

  void _dispatchEventToRoutes(
    PointerEvent event,
    Map<PointerRoute, Matrix4> referenceRoutes,
    Map<PointerRoute, Matrix4> copiedRoutes,
  ) {
    copiedRoutes.forEach((PointerRoute route, Matrix4 transform) {
      if (referenceRoutes.containsKey(route)) {
        _dispatch(event, route, transform);
      }
    });
Adam Barth's avatar
Adam Barth committed
120 121
  }
}
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

/// 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,
138
    DiagnosticsNode context,
139 140 141
    this.router,
    this.route,
    this.event,
142
    InformationCollector informationCollector,
143
    bool silent = false,
144 145 146 147 148 149
  }) : super(
    exception: exception,
    stack: stack,
    library: library,
    context: context,
    informationCollector: informationCollector,
150
    silent: silent,
151 152 153
  );

  /// The pointer router that caught the exception.
154 155 156
  ///
  /// In a typical application, this is the value of [GestureBinding.pointerRouter] on
  /// the binding ([GestureBinding.instance]).
157 158 159 160 161 162 163 164
  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;
}