1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'package:flutter/widgets.dart';
6///
7/// @docImport 'binding.dart';
8library;
9
10import 'package:flutter/foundation.dart';
11
12import 'events.dart';
13
14export 'events.dart' show PointerSignalEvent;
15
16/// The callback to register with a [PointerSignalResolver] to express
17/// interest in a pointer signal event.
18typedef PointerSignalResolvedCallback = void Function(PointerSignalEvent event);
19
20bool _isSameEvent(PointerSignalEvent event1, PointerSignalEvent event2) {
21 return (event1.original ?? event1) == (event2.original ?? event2);
22}
23
24/// Mediates disputes over which listener should handle pointer signal events
25/// when multiple listeners wish to handle those events.
26///
27/// Pointer signals (such as [PointerScrollEvent]) are immediate, so unlike
28/// events that participate in the gesture arena, pointer signals always
29/// resolve at the end of event dispatch. Yet if objects interested in handling
30/// these signal events were to handle them directly, it would cause issues
31/// such as multiple [Scrollable] widgets in the widget hierarchy responding
32/// to the same mouse wheel event. Using this class, these events will only
33/// be dispatched to the first registered handler, which will in turn
34/// correspond to the widget that's deepest in the widget hierarchy.
35///
36/// To use this class, objects should register their event handler like so:
37///
38/// ```dart
39/// void handleSignalEvent(PointerSignalEvent event) {
40/// GestureBinding.instance.pointerSignalResolver.register(event, (PointerSignalEvent event) {
41/// // handle the event...
42/// });
43/// }
44/// ```
45///
46/// {@tool dartpad}
47/// Here is an example that demonstrates the effect of not using the resolver
48/// versus using it.
49///
50/// When this example is set to _not_ use the resolver, then triggering the
51/// mouse wheel over the outer box will cause only the outer box to change
52/// color, but triggering the mouse wheel over the inner box will cause _both_
53/// the outer and the inner boxes to change color (because they're both
54/// receiving the event).
55///
56/// When this example is set to _use_ the resolver, then only the box located
57/// directly under the cursor will change color when the mouse wheel is
58/// triggered.
59///
60/// ** See code in examples/api/lib/gestures/pointer_signal_resolver/pointer_signal_resolver.0.dart **
61/// {@end-tool}
62class PointerSignalResolver {
63 PointerSignalResolvedCallback? _firstRegisteredCallback;
64
65 PointerSignalEvent? _currentEvent;
66
67 /// Registers interest in handling [event].
68 ///
69 /// This method may be called multiple times (typically from different parts
70 /// of the widget hierarchy) for the same `event`, with different `callback`s,
71 /// as the event is being dispatched across the tree. Once the dispatching is
72 /// complete, the [GestureBinding] calls [resolve], and the first registered
73 /// callback is called.
74 ///
75 /// The `callback` is invoked with one argument, the `event`.
76 ///
77 /// Once the [register] method has been called with a particular `event`, it
78 /// must not be called for other `event`s until after [resolve] has been
79 /// called. Only one event disambiguation can be in flight at a time. In
80 /// normal use this is achieved by only registering callbacks for an event as
81 /// it is actively being dispatched (for example, in
82 /// [Listener.onPointerSignal]).
83 ///
84 /// See the documentation for the [PointerSignalResolver] class for an example
85 /// of using this method.
86 void register(PointerSignalEvent event, PointerSignalResolvedCallback callback) {
87 assert(_currentEvent == null || _isSameEvent(_currentEvent!, event));
88 if (_firstRegisteredCallback != null) {
89 return;
90 }
91 _currentEvent = event;
92 _firstRegisteredCallback = callback;
93 }
94
95 /// Resolves the event, calling the first registered callback if there was
96 /// one.
97 ///
98 /// This is called by the [GestureBinding] after the framework has finished
99 /// dispatching the pointer signal event.
100 @pragma('vm:notify-debugger-on-exception')
101 void resolve(PointerSignalEvent event) {
102 if (_firstRegisteredCallback == null) {
103 assert(_currentEvent == null);
104 // Nothing in the framework/app wants to handle the `event`. Allow the
105 // platform to trigger any default native actions.
106 event.respond(allowPlatformDefault: true);
107 return;
108 }
109 assert(_isSameEvent(_currentEvent!, event));
110 try {
111 _firstRegisteredCallback!(_currentEvent!);
112 } catch (exception, stack) {
113 InformationCollector? collector;
114 assert(() {
115 collector =
116 () => <DiagnosticsNode>[
117 DiagnosticsProperty<PointerSignalEvent>(
118 'Event',
119 event,
120 style: DiagnosticsTreeStyle.errorProperty,
121 ),
122 ];
123 return true;
124 }());
125 FlutterError.reportError(
126 FlutterErrorDetails(
127 exception: exception,
128 stack: stack,
129 library: 'gesture library',
130 context: ErrorDescription('while resolving a PointerSignalEvent'),
131 informationCollector: collector,
132 ),
133 );
134 }
135 _firstRegisteredCallback = null;
136 _currentEvent = null;
137 }
138}
139

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com