blob: 1f8775a30d79eb367e211ee522a7e20cdde52ff6 [file] [log] [blame]
// 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:async';
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'recorder.dart';
/// Creates a grid of mouse regions, then continuously scrolls them up and down.
///
/// Measures our ability to render mouse regions.
class BenchMouseRegionGridScroll extends WidgetRecorder {
BenchMouseRegionGridScroll() : super(name: benchmarkName);
static const String benchmarkName = 'bench_mouse_region_grid_scroll';
final _Tester _tester = _Tester();
// Use a non-trivial border to force Web to switch painter
Border _getBorder(int columnIndex, int rowIndex) {
const BorderSide defaultBorderSide = BorderSide();
return Border(
left: columnIndex == 0 ? defaultBorderSide : BorderSide.none,
top: rowIndex == 0 ? defaultBorderSide : BorderSide.none,
right: defaultBorderSide,
bottom: defaultBorderSide,
);
}
bool started = false;
@override
void frameDidDraw() {
if (!started) {
started = true;
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) async {
_tester.start();
registerDidStop(_tester.stop);
});
}
super.frameDidDraw();
}
@override
Widget createWidget() {
const int rowsCount = 60;
const int columnsCount = 20;
const double containerSize = 20;
return Directionality(
textDirection: TextDirection.ltr,
child: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 400,
height: 400,
child: ListView.builder(
itemCount: rowsCount,
cacheExtent: rowsCount * containerSize,
physics: const ClampingScrollPhysics(),
itemBuilder: (BuildContext context, int rowIndex) => Row(
children: List<Widget>.generate(
columnsCount,
(int columnIndex) => MouseRegion(
onEnter: (_) {},
child: Container(
decoration: BoxDecoration(
border: _getBorder(columnIndex, rowIndex),
color: Color.fromARGB(255, rowIndex * 20 % 256, 127, 127),
),
width: containerSize,
height: containerSize,
),
),
),
),
),
),
),
);
}
}
class _UntilNextFrame {
_UntilNextFrame._();
static Completer<void>? _completer;
static Future<void> wait() {
if (_UntilNextFrame._completer == null) {
_UntilNextFrame._completer = Completer<void>();
SchedulerBinding.instance.addPostFrameCallback((_) {
_UntilNextFrame._completer!.complete(null);
_UntilNextFrame._completer = null;
});
}
return _UntilNextFrame._completer!.future;
}
}
class _Tester {
static const int scrollFrequency = 60;
static const Offset dragStartLocation = Offset(200, 200);
static const Offset dragUpOffset = Offset(0, 200);
static const Offset dragDownOffset = Offset(0, -200);
static const Duration dragDuration = Duration(milliseconds: 200);
bool _stopped = false;
TestGesture get gesture {
return _gesture ??= TestGesture(
dispatcher: (PointerEvent event) async {
RendererBinding.instance.handlePointerEvent(event);
},
kind: PointerDeviceKind.mouse,
);
}
TestGesture? _gesture;
Duration currentTime = Duration.zero;
Future<void> _scroll(Offset start, Offset offset, Duration duration) async {
final int durationMs = duration.inMilliseconds;
final Duration fullFrameDuration = const Duration(seconds: 1) ~/ scrollFrequency;
final int frameDurationMs = fullFrameDuration.inMilliseconds;
final int fullFrames = duration.inMilliseconds ~/ frameDurationMs;
final Offset fullFrameOffset = offset * ((frameDurationMs as double) / durationMs);
final Duration finalFrameDuration = duration - fullFrameDuration * fullFrames;
final Offset finalFrameOffset = offset - fullFrameOffset * (fullFrames as double);
await gesture.down(start, timeStamp: currentTime);
for (int frame = 0; frame < fullFrames; frame += 1) {
currentTime += fullFrameDuration;
await gesture.moveBy(fullFrameOffset, timeStamp: currentTime);
await _UntilNextFrame.wait();
}
if (finalFrameOffset != Offset.zero) {
currentTime += finalFrameDuration;
await gesture.moveBy(finalFrameOffset, timeStamp: currentTime);
await _UntilNextFrame.wait();
}
await gesture.up(timeStamp: currentTime);
}
Future<void> start() async {
await Future<void>.delayed(Duration.zero);
while (!_stopped) {
await _scroll(dragStartLocation, dragUpOffset, dragDuration);
await _scroll(dragStartLocation, dragDownOffset, dragDuration);
}
}
void stop() {
_stopped = true;
}
}