Unverified Commit 9e2e0ef3 authored by Ming Lyu (CareF)'s avatar Ming Lyu (CareF) Committed by GitHub

implement handlePointerEventRecord for LiveWidgetController (#61266)

parent e91b8339
......@@ -4,6 +4,7 @@
import 'dart:async';
import 'package:clock/clock.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
......@@ -718,8 +719,86 @@ class LiveWidgetController extends WidgetController {
@override
Future<List<Duration>> handlePointerEventRecord(List<PointerEventRecord> records) {
// TODO(CareF): This will be implemented after we decide what should be the
// correct pumping strategy.
throw UnimplementedError;
assert(records != null);
assert(records.isNotEmpty);
return TestAsyncUtils.guard<List<Duration>>(() async {
// hitTestHistory is an equivalence of _hitTests in [GestureBinding],
// used as state for all pointers which are currently down.
final Map<int, HitTestResult> hitTestHistory = <int, HitTestResult>{};
final List<Duration> handleTimeStampDiff = <Duration>[];
DateTime startTime;
for (final PointerEventRecord record in records) {
final DateTime now = clock.now();
startTime ??= now;
// So that the first event is promised to receive a zero timeDiff
final Duration timeDiff = record.timeDelay - now.difference(startTime);
if (timeDiff.isNegative) {
// This happens when something (e.g. GC) takes a long time during the
// processing of the events.
// Flush all past events
handleTimeStampDiff.add(-timeDiff);
for (final PointerEvent event in record.events) {
_handlePointerEvent(event, hitTestHistory);
}
} else {
await Future<void>.delayed(timeDiff);
handleTimeStampDiff.add(
// Recalculating the time diff for getting exact time when the event
// packet is sent. For a perfect Future.delayed like the one in a
// fake async this new diff should be zero.
clock.now().difference(startTime) - record.timeDelay,
);
for (final PointerEvent event in record.events) {
_handlePointerEvent(event, hitTestHistory);
}
}
}
// This makes sure that a gesture is completed, with no more pointers
// active.
assert(hitTestHistory.isEmpty);
return handleTimeStampDiff;
});
}
// This method is almost identical to [GestureBinding._handlePointerEvent]
// to replicate the bahavior of the real binding.
void _handlePointerEvent(
PointerEvent event,
Map<int, HitTestResult> _hitTests
) {
HitTestResult hitTestResult;
if (event is PointerDownEvent || event is PointerSignalEvent) {
assert(!_hitTests.containsKey(event.pointer));
hitTestResult = HitTestResult();
binding.hitTest(hitTestResult, event.position);
if (event is PointerDownEvent) {
_hitTests[event.pointer] = hitTestResult;
}
assert(() {
if (debugPrintHitTestResults)
debugPrint('$event: $hitTestResult');
return true;
}());
} else if (event is PointerUpEvent || event is PointerCancelEvent) {
hitTestResult = _hitTests.remove(event.pointer);
} else if (event.down) {
// Because events that occur with the pointer down (like
// PointerMoveEvents) should be dispatched to the same place that their
// initial PointerDownEvent was, we want to re-use the path we found when
// the pointer went down, rather than do hit detection each time we get
// such an event.
hitTestResult = _hitTests[event.pointer];
}
assert(() {
if (debugPrintMouseHoverEvents && event is PointerHoverEvent)
debugPrint('$event');
return true;
}());
if (hitTestResult != null ||
event is PointerHoverEvent ||
event is PointerAddedEvent ||
event is PointerRemovedEvent) {
binding.dispatchEvent(event, hitTestResult);
}
}
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';
Future<void> main() async {
test('Input event array on LiveWidgetController', () async {
final List<String> logs = <String>[];
runApp(
MaterialApp(
home: Listener(
onPointerDown: (PointerDownEvent event) => logs.add('down ${event.buttons}'),
onPointerMove: (PointerMoveEvent event) => logs.add('move ${event.buttons}'),
onPointerUp: (PointerUpEvent event) => logs.add('up ${event.buttons}'),
child: const Text('test'),
),
),
);
await SchedulerBinding.instance.endOfFrame;
final WidgetController controller =
LiveWidgetController(WidgetsBinding.instance);
final Offset location = controller.getCenter(find.text('test'));
final List<PointerEventRecord> records = <PointerEventRecord>[
PointerEventRecord(Duration.zero, <PointerEvent>[
// Typically PointerAddedEvent is not used in testers, but for records
// captured on a device it is usually what start a gesture.
PointerAddedEvent(
timeStamp: Duration.zero,
position: location,
),
PointerDownEvent(
timeStamp: Duration.zero,
position: location,
buttons: kSecondaryMouseButton,
pointer: 1,
),
]),
...<PointerEventRecord>[
for (Duration t = const Duration(milliseconds: 5);
t < const Duration(milliseconds: 80);
t += const Duration(milliseconds: 16))
PointerEventRecord(t, <PointerEvent>[
PointerMoveEvent(
timeStamp: t - const Duration(milliseconds: 1),
position: location,
buttons: kSecondaryMouseButton,
pointer: 1,
)
])
],
PointerEventRecord(const Duration(milliseconds: 80), <PointerEvent>[
PointerUpEvent(
timeStamp: const Duration(milliseconds: 79),
position: location,
buttons: kSecondaryMouseButton,
pointer: 1,
)
])
];
final List<Duration> timeDiffs =
await controller.handlePointerEventRecord(records);
expect(timeDiffs.length, records.length);
for (final Duration diff in timeDiffs) {
// Allow some freedom of time delay in real world.
assert(diff.inMilliseconds > -1);
}
const String b = '$kSecondaryMouseButton';
expect(logs.first, 'down $b');
for (int i = 1; i < logs.length - 1; i++) {
expect(logs[i], 'move $b');
}
expect(logs.last, 'up $b');
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment