pointer_signal_resolver.dart 4.05 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';

import 'events.dart';

/// The callback to register with a [PointerSignalResolver] to express
/// interest in a pointer signal event.
typedef PointerSignalResolvedCallback = void Function(PointerSignalEvent event);

13 14 15 16
bool _isSameEvent(PointerSignalEvent event1, PointerSignalEvent event2) {
  return (event1.original ?? event1) == (event2.original ?? event2);
}

17 18
/// Mediates disputes over which listener should handle pointer signal events
/// when multiple listeners wish to handle those events.
19
///
20 21 22 23 24 25
/// Pointer signals (such as [PointerScrollEvent]) are immediate, so unlike
/// events that participate in the gesture arena, pointer signals always
/// resolve at the end of event dispatch. Yet if objects interested in handling
/// these signal events were to handle them directly, it would cause issues
/// such as multiple [Scrollable] widgets in the widget hierarchy responding
/// to the same mouse wheel event. Using this class, these events will only
26
/// be dispatched to the first registered handler, which will in turn
27
/// correspond to the widget that's deepest in the widget hierarchy.
28
///
29 30 31 32
/// To use this class, objects should register their event handler like so:
///
/// ```dart
/// void handleSignalEvent(PointerSignalEvent event) {
33
///   GestureBinding.instance.pointerSignalResolver.register(event, (PointerSignalEvent event) {
34 35 36 37 38
///     // handle the event...
///   });
/// }
/// ```
///
39
/// {@tool dartpad}
40 41 42
/// Here is an example that demonstrates the effect of not using the resolver
/// versus using it.
///
43
/// When this example is set to _not_ use the resolver, then triggering the
44
/// mouse wheel over the outer box will cause only the outer box to change
45 46 47
/// color, but triggering the mouse wheel over the inner box will cause _both_
/// the outer and the inner boxes to change color (because they're both
/// receiving the event).
48
///
49
/// When this example is set to _use_ the resolver, then only the box located
50
/// directly under the cursor will change color when the mouse wheel is
51
/// triggered.
52
///
53
/// ** See code in examples/api/lib/gestures/pointer_signal_resolver/pointer_signal_resolver.0.dart **
54
/// {@end-tool}
55
class PointerSignalResolver {
56
  PointerSignalResolvedCallback? _firstRegisteredCallback;
57

58
  PointerSignalEvent? _currentEvent;
59 60

  /// Registers interest in handling [event].
61 62 63
  ///
  /// See the documentation for the [PointerSignalResolver] class on when and
  /// how this method should be used.
64 65 66
  void register(PointerSignalEvent event, PointerSignalResolvedCallback callback) {
    assert(event != null);
    assert(callback != null);
67
    assert(_currentEvent == null || _isSameEvent(_currentEvent!, event));
68 69 70 71 72 73 74 75 76 77
    if (_firstRegisteredCallback != null) {
      return;
    }
    _currentEvent = event;
    _firstRegisteredCallback = callback;
  }

  /// Resolves the event, calling the first registered callback if there was
  /// one.
  ///
78 79
  /// This is called by the [GestureBinding] after the framework has finished
  /// dispatching the pointer signal event.
80
  @pragma('vm:notify-debugger-on-exception')
81 82 83 84 85
  void resolve(PointerSignalEvent event) {
    if (_firstRegisteredCallback == null) {
      assert(_currentEvent == null);
      return;
    }
86
    assert(_isSameEvent(_currentEvent!, event));
87
    try {
88
      _firstRegisteredCallback!(_currentEvent!);
89
    } catch (exception, stack) {
90
      InformationCollector? collector;
91
      assert(() {
92 93 94
        collector = () => <DiagnosticsNode>[
          DiagnosticsProperty<PointerSignalEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty),
        ];
95 96
        return true;
      }());
97 98 99 100
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'gesture library',
101
        context: ErrorDescription('while resolving a PointerSignalEvent'),
102
        informationCollector: collector,
103 104 105 106 107 108
      ));
    }
    _firstRegisteredCallback = null;
    _currentEvent = null;
  }
}