Improve documentation for PointerSignalResolver class (#78738)
This commit is contained in:
parent
cf903d7392
commit
351e319a47
@ -12,6 +12,15 @@ import 'package:process/process.dart';
|
|||||||
import '../test.dart';
|
import '../test.dart';
|
||||||
import 'common.dart';
|
import 'common.dart';
|
||||||
|
|
||||||
|
/// Fails a test if the exit code of `result` is not the expected value. This
|
||||||
|
/// is favored over `expect(result.exitCode, expectedExitCode)` because this
|
||||||
|
/// will include the process result's stdio in the failure message.
|
||||||
|
void expectExitCode(ProcessResult result, int expectedExitCode) {
|
||||||
|
if (result.exitCode != expectedExitCode) {
|
||||||
|
fail('Failure due to exit code ${result.exitCode}\nSTDOUT:\n${result.stdout}\nSTDERR:\n${result.stderr}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('verifyVersion()', () {
|
group('verifyVersion()', () {
|
||||||
MemoryFileSystem fileSystem;
|
MemoryFileSystem fileSystem;
|
||||||
@ -103,14 +112,14 @@ void main() {
|
|||||||
ProcessResult result = await runScript(
|
ProcessResult result = await runScript(
|
||||||
<String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '1_3'},
|
<String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '1_3'},
|
||||||
);
|
);
|
||||||
expect(result.exitCode, 0);
|
expectExitCode(result, 0);
|
||||||
// There are currently 6 smoke tests. This shard should contain test 1 and 2.
|
// There are currently 6 smoke tests. This shard should contain test 1 and 2.
|
||||||
expect(result.stdout, contains('Selecting subshard 1 of 3 (range 1-2 of 6)'));
|
expect(result.stdout, contains('Selecting subshard 1 of 3 (range 1-2 of 6)'));
|
||||||
|
|
||||||
result = await runScript(
|
result = await runScript(
|
||||||
<String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '5_6'},
|
<String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '5_6'},
|
||||||
);
|
);
|
||||||
expect(result.exitCode, 0);
|
expectExitCode(result, 0);
|
||||||
// This shard should contain only test 5.
|
// This shard should contain only test 5.
|
||||||
expect(result.stdout, contains('Selecting subshard 5 of 6 (range 5-5 of 6)'));
|
expect(result.stdout, contains('Selecting subshard 5 of 6 (range 5-5 of 6)'));
|
||||||
});
|
});
|
||||||
@ -119,7 +128,7 @@ void main() {
|
|||||||
final ProcessResult result = await runScript(
|
final ProcessResult result = await runScript(
|
||||||
<String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '100_99'},
|
<String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '100_99'},
|
||||||
);
|
);
|
||||||
expect(result.exitCode, 1);
|
expectExitCode(result, 1);
|
||||||
expect(result.stdout, contains('Invalid subshard name'));
|
expect(result.stdout, contains('Invalid subshard name'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1847,6 +1847,8 @@ class _TransformedPointerUpEvent extends _TransformedPointerEvent with _CopyPoin
|
|||||||
///
|
///
|
||||||
/// * [Listener.onPointerSignal], which allows callers to be notified of these
|
/// * [Listener.onPointerSignal], which allows callers to be notified of these
|
||||||
/// events in a widget tree.
|
/// events in a widget tree.
|
||||||
|
/// * [PointerSignalResolver], which provides an opt-in mechanism whereby
|
||||||
|
/// participating agents may disambiguate an event's target.
|
||||||
abstract class PointerSignalEvent extends PointerEvent {
|
abstract class PointerSignalEvent extends PointerEvent {
|
||||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||||
/// const constructors so that they can be used in const expressions.
|
/// const constructors so that they can be used in const expressions.
|
||||||
@ -1916,6 +1918,8 @@ mixin _CopyPointerScrollEvent on PointerEvent {
|
|||||||
///
|
///
|
||||||
/// * [Listener.onPointerSignal], which allows callers to be notified of these
|
/// * [Listener.onPointerSignal], which allows callers to be notified of these
|
||||||
/// events in a widget tree.
|
/// events in a widget tree.
|
||||||
|
/// * [PointerSignalResolver], which provides an opt-in mechanism whereby
|
||||||
|
/// participating agents may disambiguate an event's target.
|
||||||
class PointerScrollEvent extends PointerSignalEvent with _PointerEventDescription, _CopyPointerScrollEvent {
|
class PointerScrollEvent extends PointerSignalEvent with _PointerEventDescription, _CopyPointerScrollEvent {
|
||||||
/// Creates a pointer scroll event.
|
/// Creates a pointer scroll event.
|
||||||
///
|
///
|
||||||
|
@ -15,22 +15,145 @@ bool _isSameEvent(PointerSignalEvent event1, PointerSignalEvent event2) {
|
|||||||
return (event1.original ?? event1) == (event2.original ?? event2);
|
return (event1.original ?? event1) == (event2.original ?? event2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An resolver for pointer signal events.
|
/// Mediates disputes over which listener should handle pointer signal events
|
||||||
|
/// when multiple listeners wish to handle those events.
|
||||||
///
|
///
|
||||||
/// Objects interested in a [PointerSignalEvent] should register a callback to
|
/// Pointer signals (such as [PointerScrollEvent]) are immediate, so unlike
|
||||||
/// be called if they should handle the event. The resolver's purpose is to
|
/// events that participate in the gesture arena, pointer signals always
|
||||||
/// ensure that the same pointer signal is not handled by multiple objects in
|
/// resolve at the end of event dispatch. Yet if objects interested in handling
|
||||||
/// a hierarchy.
|
/// 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
|
||||||
|
/// be dispatched to the the first registered handler, which will in turn
|
||||||
|
/// correspond to the widget that's deepest in the widget hierarchy.
|
||||||
///
|
///
|
||||||
/// Pointer signals are immediate, so unlike a gesture arena it always resolves
|
/// To use this class, objects should register their event handler like so:
|
||||||
/// at the end of event dispatch. The first callback registered will be the one
|
///
|
||||||
/// that is called.
|
/// {@tool snippet}
|
||||||
|
/// ```dart
|
||||||
|
/// void handleSignalEvent(PointerSignalEvent event) {
|
||||||
|
/// GestureBinding.instance!.pointerSignalResolver.register(event, (PointerSignalEvent event) {
|
||||||
|
/// // handle the event...
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// {@end-tool}
|
||||||
|
///
|
||||||
|
/// {@tool dartpad --template=stateful_widget_material}
|
||||||
|
/// Here is an example that demonstrates the effect of not using the resolver
|
||||||
|
/// versus using it.
|
||||||
|
///
|
||||||
|
/// When this example is set to _not_ use the resolver, then scrolling the
|
||||||
|
/// mouse wheel over the outer box will cause only the outer box to change
|
||||||
|
/// color, but scrolling the mouse wheel over inner box will cause _both_ the
|
||||||
|
/// outer and the inner boxes to change color (because they're both receiving
|
||||||
|
/// the scroll event).
|
||||||
|
///
|
||||||
|
/// When this excample is set to _use_ the resolver, then only the box located
|
||||||
|
/// directly under the cursor will change color when the mouse wheel is
|
||||||
|
/// scrolled.
|
||||||
|
///
|
||||||
|
/// ```dart imports
|
||||||
|
/// import 'package:flutter/gestures.dart';
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// HSVColor outerColor = const HSVColor.fromAHSV(0.2, 120.0, 1, 1);
|
||||||
|
/// HSVColor innerColor = const HSVColor.fromAHSV(1, 60.0, 1, 1);
|
||||||
|
/// bool useResolver = false;
|
||||||
|
///
|
||||||
|
/// void rotateOuterColor() {
|
||||||
|
/// setState(() {
|
||||||
|
/// outerColor = outerColor.withHue((outerColor.hue + 6) % 360.0);
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// void rotateInnerColor() {
|
||||||
|
/// setState(() {
|
||||||
|
/// innerColor = innerColor.withHue((innerColor.hue + 6) % 360.0);
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// Widget build(BuildContext context) {
|
||||||
|
/// return Material(
|
||||||
|
/// child: Stack(
|
||||||
|
/// fit: StackFit.expand,
|
||||||
|
/// children: <Widget>[
|
||||||
|
/// Listener(
|
||||||
|
/// onPointerSignal: (PointerSignalEvent event) {
|
||||||
|
/// if (useResolver) {
|
||||||
|
/// GestureBinding.instance!.pointerSignalResolver.register(event, (PointerSignalEvent event) {
|
||||||
|
/// rotateOuterColor();
|
||||||
|
/// });
|
||||||
|
/// } else {
|
||||||
|
/// rotateOuterColor();
|
||||||
|
/// }
|
||||||
|
/// },
|
||||||
|
/// child: DecoratedBox(
|
||||||
|
/// decoration: BoxDecoration(
|
||||||
|
/// border: const Border.fromBorderSide(BorderSide()),
|
||||||
|
/// color: outerColor.toColor(),
|
||||||
|
/// ),
|
||||||
|
/// child: FractionallySizedBox(
|
||||||
|
/// widthFactor: 0.5,
|
||||||
|
/// heightFactor: 0.5,
|
||||||
|
/// child: DecoratedBox(
|
||||||
|
/// decoration: BoxDecoration(
|
||||||
|
/// border: const Border.fromBorderSide(BorderSide()),
|
||||||
|
/// color: innerColor.toColor(),
|
||||||
|
/// ),
|
||||||
|
/// child: Listener(
|
||||||
|
/// onPointerSignal: (PointerSignalEvent event) {
|
||||||
|
/// if (useResolver) {
|
||||||
|
/// GestureBinding.instance!.pointerSignalResolver.register(event, (PointerSignalEvent event) {
|
||||||
|
/// rotateInnerColor();
|
||||||
|
/// });
|
||||||
|
/// } else {
|
||||||
|
/// rotateInnerColor();
|
||||||
|
/// }
|
||||||
|
/// },
|
||||||
|
/// child: const AbsorbPointer(),
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// Align(
|
||||||
|
/// alignment: Alignment.topLeft,
|
||||||
|
/// child: Row(
|
||||||
|
/// crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
/// children: <Widget>[
|
||||||
|
/// Switch(
|
||||||
|
/// value: useResolver,
|
||||||
|
/// onChanged: (bool value) {
|
||||||
|
/// setState(() {
|
||||||
|
/// useResolver = value;
|
||||||
|
/// });
|
||||||
|
/// },
|
||||||
|
/// ),
|
||||||
|
/// Text(
|
||||||
|
/// 'Use the PointerSignalResolver?',
|
||||||
|
/// style: DefaultTextStyle.of(context).style.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
/// ),
|
||||||
|
/// ],
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// ],
|
||||||
|
/// ),
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// {@end-tool}
|
||||||
class PointerSignalResolver {
|
class PointerSignalResolver {
|
||||||
PointerSignalResolvedCallback? _firstRegisteredCallback;
|
PointerSignalResolvedCallback? _firstRegisteredCallback;
|
||||||
|
|
||||||
PointerSignalEvent? _currentEvent;
|
PointerSignalEvent? _currentEvent;
|
||||||
|
|
||||||
/// Registers interest in handling [event].
|
/// Registers interest in handling [event].
|
||||||
|
///
|
||||||
|
/// See the documentation for the [PointerSignalResolver] class on when and
|
||||||
|
/// how this method should be used.
|
||||||
void register(PointerSignalEvent event, PointerSignalResolvedCallback callback) {
|
void register(PointerSignalEvent event, PointerSignalResolvedCallback callback) {
|
||||||
assert(event != null);
|
assert(event != null);
|
||||||
assert(callback != null);
|
assert(callback != null);
|
||||||
@ -45,8 +168,8 @@ class PointerSignalResolver {
|
|||||||
/// Resolves the event, calling the first registered callback if there was
|
/// Resolves the event, calling the first registered callback if there was
|
||||||
/// one.
|
/// one.
|
||||||
///
|
///
|
||||||
/// Called after the framework has finished dispatching the pointer signal
|
/// This is called by the [GestureBinding] after the framework has finished
|
||||||
/// event.
|
/// dispatching the pointer signal event.
|
||||||
void resolve(PointerSignalEvent event) {
|
void resolve(PointerSignalEvent event) {
|
||||||
if (_firstRegisteredCallback == null) {
|
if (_firstRegisteredCallback == null) {
|
||||||
assert(_currentEvent == null);
|
assert(_currentEvent == null);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user