pointer_router.dart 4.33 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Adam Barth's avatar
Adam Barth committed
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5

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

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

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

Ian Hickson's avatar
Ian Hickson committed
14
/// A routing table for [PointerEvent] events.
15
class PointerRouter {
16 17
  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
18

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

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

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

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

74
  void _dispatch(PointerEvent event, PointerRoute route, Matrix4? transform) {
75
    try {
76 77
      event = event.transformed(transform);
      route(event);
78
    } catch (exception, stack) {
79
      InformationCollector? collector;
80 81
      assert(() {
        collector = () sync* {
82 83 84
          yield DiagnosticsProperty<PointerRouter>('router', this, level: DiagnosticLevel.debug);
          yield DiagnosticsProperty<PointerRoute>('route', route, level: DiagnosticLevel.debug);
          yield DiagnosticsProperty<PointerEvent>('event', event, level: DiagnosticLevel.debug);
85 86 87
        };
        return true;
      }());
88
      FlutterError.reportError(FlutterErrorDetails(
89 90 91
        exception: exception,
        stack: stack,
        library: 'gesture library',
92
        context: ErrorDescription('while routing a pointer event'),
93
        informationCollector: collector
94 95 96 97
      ));
    }
  }

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

  void _dispatchEventToRoutes(
    PointerEvent event,
117 118
    Map<PointerRoute, Matrix4?> referenceRoutes,
    Map<PointerRoute, Matrix4?> copiedRoutes,
119
  ) {
120
    copiedRoutes.forEach((PointerRoute route, Matrix4? transform) {
121 122 123 124
      if (referenceRoutes.containsKey(route)) {
        _dispatch(event, route, transform);
      }
    });
Adam Barth's avatar
Adam Barth committed
125 126
  }
}