Remove LiveTestRenderView (#127882)
In the multi view world, `RenderViews` are created by the `View` widget and no longer owned by the binding. Prior to this change, the `LiveTestWidgetsFlutterBinding` owned and managed a special subclass of `RenderView`, the `_LiveTestRenderView`. In the new world, where `RenderView`s can be created anywhere in the widget tree where a `View` widget is used, this setup is no longer feasible. This change removes this special `_LiveTestRenderView` and instead adds debug hocks to `RenderView` to allow the `LiveTestWidgetsFlutterBinding` to draw a debug overlay on top of the content of any `RenderView`.
This commit is contained in:
parent
6f8ef1a197
commit
4cf89cc278
@ -33,6 +33,10 @@ class ViewConfiguration {
|
||||
final double devicePixelRatio;
|
||||
|
||||
/// Creates a transformation matrix that applies the [devicePixelRatio].
|
||||
///
|
||||
/// The matrix translates points from the local coordinate system of the
|
||||
/// app (in logical pixels) to the global coordinate system of the
|
||||
/// [FlutterView] (in physical pixels).
|
||||
Matrix4 toMatrix() {
|
||||
return Matrix4.diagonal3Values(devicePixelRatio, devicePixelRatio, 1.0);
|
||||
}
|
||||
@ -212,6 +216,15 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
|
||||
if (child != null) {
|
||||
context.paintChild(child!, offset);
|
||||
}
|
||||
assert(() {
|
||||
final List<DebugPaintCallback> localCallbacks = _debugPaintCallbacks.toList();
|
||||
for (final DebugPaintCallback paintCallback in localCallbacks) {
|
||||
if (_debugPaintCallbacks.contains(paintCallback)) {
|
||||
paintCallback(context, offset, this);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
|
||||
@override
|
||||
@ -381,4 +394,47 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
|
||||
properties.add(DiagnosticsNode.message('semantics enabled'));
|
||||
}
|
||||
}
|
||||
|
||||
static final List<DebugPaintCallback> _debugPaintCallbacks = <DebugPaintCallback>[];
|
||||
|
||||
/// Registers a [DebugPaintCallback] that is called every time a [RenderView]
|
||||
/// repaints in debug mode.
|
||||
///
|
||||
/// The callback may paint a debug overlay on top of the content of the
|
||||
/// [RenderView] provided to the callback. Callbacks are invoked in the
|
||||
/// order they were registered in.
|
||||
///
|
||||
/// Neither registering a callback nor the continued presence of a callback
|
||||
/// changes how often [RenderView]s are repainted. It is up to the owner of
|
||||
/// the callback to call [markNeedsPaint] on any [RenderView] for which it
|
||||
/// wants to update the painted overlay.
|
||||
///
|
||||
/// Does nothing in release mode.
|
||||
static void debugAddPaintCallback(DebugPaintCallback callback) {
|
||||
assert(() {
|
||||
_debugPaintCallbacks.add(callback);
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
|
||||
/// Removes a callback registered with [debugAddPaintCallback].
|
||||
///
|
||||
/// It does not schedule a frame to repaint the [RenderView]s without the
|
||||
/// overlay painted by the removed callback. It is up to the owner of the
|
||||
/// callback to call [markNeedsPaint] on the relevant [RenderView]s to
|
||||
/// repaint them without the overlay.
|
||||
///
|
||||
/// Does nothing in release mode.
|
||||
static void debugRemovePaintCallback(DebugPaintCallback callback) {
|
||||
assert(() {
|
||||
_debugPaintCallbacks.remove(callback);
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
}
|
||||
|
||||
/// A callback for painting a debug overlay on top of the provided [RenderView].
|
||||
///
|
||||
/// Used by [RenderView.debugAddPaintCallback] and
|
||||
/// [RenderView.debugRemovePaintCallback].
|
||||
typedef DebugPaintCallback = void Function(PaintingContext context, Offset offset, RenderView renderView);
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'mock_canvas.dart';
|
||||
import 'rendering_tester.dart';
|
||||
|
||||
void main() {
|
||||
@ -69,4 +70,76 @@ void main() {
|
||||
expect(viewConfigurationA.hashCode, viewConfigurationB.hashCode);
|
||||
expect(viewConfigurationA.hashCode != viewConfigurationC.hashCode, true);
|
||||
});
|
||||
|
||||
test('invokes DebugPaintCallback', () {
|
||||
final PaintPattern paintsOrangeRect = paints..rect(
|
||||
color: orange,
|
||||
rect: orangeRect,
|
||||
);
|
||||
final PaintPattern paintsGreenRect = paints..rect(
|
||||
color: green,
|
||||
rect: greenRect,
|
||||
);
|
||||
final PaintPattern paintOrangeAndGreenRect = paints
|
||||
..rect(
|
||||
color: orange,
|
||||
rect: orangeRect,
|
||||
)
|
||||
..rect(
|
||||
color: green,
|
||||
rect: greenRect,
|
||||
);
|
||||
void paintCallback(PaintingContext context, Offset offset, RenderView renderView) {
|
||||
context.canvas.drawRect(
|
||||
greenRect,
|
||||
Paint()..color = green,
|
||||
);
|
||||
}
|
||||
|
||||
layout(TestRenderObject());
|
||||
expect(
|
||||
TestRenderingFlutterBinding.instance.renderView,
|
||||
paintsOrangeRect,
|
||||
);
|
||||
expect(
|
||||
TestRenderingFlutterBinding.instance.renderView,
|
||||
isNot(paintsGreenRect),
|
||||
);
|
||||
|
||||
RenderView.debugAddPaintCallback(paintCallback);
|
||||
expect(
|
||||
TestRenderingFlutterBinding.instance.renderView,
|
||||
paintOrangeAndGreenRect,
|
||||
);
|
||||
|
||||
RenderView.debugRemovePaintCallback(paintCallback);
|
||||
expect(
|
||||
TestRenderingFlutterBinding.instance.renderView,
|
||||
paintsOrangeRect,
|
||||
);
|
||||
expect(
|
||||
TestRenderingFlutterBinding.instance.renderView,
|
||||
isNot(paintsGreenRect),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const Color orange = Color(0xFFFF9000);
|
||||
const Color green = Color(0xFF0FF900);
|
||||
const Rect orangeRect = Rect.fromLTWH(10, 10, 50, 75);
|
||||
const Rect greenRect = Rect.fromLTWH(20, 20, 100, 150);
|
||||
|
||||
class TestRenderObject extends RenderBox {
|
||||
@override
|
||||
void performLayout() {
|
||||
size = constraints.biggest;
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
context.canvas.drawRect(
|
||||
orangeRect,
|
||||
Paint()..color = orange,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -563,17 +563,24 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
});
|
||||
}
|
||||
|
||||
/// Convert the given point from the global coordinate space to the local
|
||||
/// one.
|
||||
/// Convert the given point from the global coordinate space of the provided
|
||||
/// [RenderView] to its local one.
|
||||
///
|
||||
/// This method operates in logical pixels for both coordinate spaces. It does
|
||||
/// not apply the device pixel ratio (used to translate to/from physical
|
||||
/// pixels).
|
||||
///
|
||||
/// For definitions for coordinate spaces, see [TestWidgetsFlutterBinding].
|
||||
Offset globalToLocal(Offset point) => point;
|
||||
Offset globalToLocal(Offset point, RenderView view) => point;
|
||||
|
||||
/// Convert the given point from the local coordinate space to the global
|
||||
/// one.
|
||||
/// coordinate space of the [RenderView].
|
||||
///
|
||||
/// This method operates in logical pixels for both coordinate spaces. It does
|
||||
/// not apply the device pixel ratio to translate to physical pixels.
|
||||
///
|
||||
/// For definitions for coordinate spaces, see [TestWidgetsFlutterBinding].
|
||||
Offset localToGlobal(Offset point) => point;
|
||||
Offset localToGlobal(Offset point, RenderView view) => point;
|
||||
|
||||
/// The source of the current pointer event.
|
||||
///
|
||||
@ -1654,6 +1661,8 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
void initInstances() {
|
||||
super.initInstances();
|
||||
_instance = this;
|
||||
|
||||
RenderView.debugAddPaintCallback(_handleRenderViewPaint);
|
||||
}
|
||||
|
||||
/// The current [LiveTestWidgetsFlutterBinding], if one has been created.
|
||||
@ -1782,23 +1791,70 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initRenderView() {
|
||||
renderView = _LiveTestRenderView(
|
||||
configuration: createViewConfiguration(),
|
||||
onNeedPaint: _handleViewNeedsPaint,
|
||||
view: platformDispatcher.implicitView!,
|
||||
);
|
||||
renderView.prepareInitialFrame();
|
||||
}
|
||||
|
||||
_LiveTestRenderView get _liveTestRenderView => super.renderView as _LiveTestRenderView;
|
||||
|
||||
void _handleViewNeedsPaint() {
|
||||
void _markViewNeedsPaint() {
|
||||
_viewNeedsPaint = true;
|
||||
renderView.markNeedsPaint();
|
||||
}
|
||||
|
||||
TextPainter? _label;
|
||||
static const TextStyle _labelStyle = TextStyle(
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 10.0,
|
||||
);
|
||||
|
||||
void _setDescription(String value) {
|
||||
if (value.isEmpty) {
|
||||
_label = null;
|
||||
return;
|
||||
}
|
||||
// TODO(ianh): Figure out if the test name is actually RTL.
|
||||
_label ??= TextPainter(textAlign: TextAlign.left, textDirection: TextDirection.ltr);
|
||||
_label!.text = TextSpan(text: value, style: _labelStyle);
|
||||
_label!.layout();
|
||||
_markViewNeedsPaint();
|
||||
}
|
||||
|
||||
final Map<int, _LiveTestPointerRecord> _pointerIdToPointerRecord = <int, _LiveTestPointerRecord>{};
|
||||
|
||||
void _handleRenderViewPaint(PaintingContext context, Offset offset, RenderView renderView) {
|
||||
assert(offset == Offset.zero);
|
||||
|
||||
if (_pointerIdToPointerRecord.isNotEmpty) {
|
||||
final double radius = renderView.configuration.size.shortestSide * 0.05;
|
||||
final Path path = Path()
|
||||
..addOval(Rect.fromCircle(center: Offset.zero, radius: radius))
|
||||
..moveTo(0.0, -radius * 2.0)
|
||||
..lineTo(0.0, radius * 2.0)
|
||||
..moveTo(-radius * 2.0, 0.0)
|
||||
..lineTo(radius * 2.0, 0.0);
|
||||
final Canvas canvas = context.canvas;
|
||||
final Paint paint = Paint()
|
||||
..strokeWidth = radius / 10.0
|
||||
..style = PaintingStyle.stroke;
|
||||
bool dirty = false;
|
||||
for (final _LiveTestPointerRecord record in _pointerIdToPointerRecord.values) {
|
||||
paint.color = record.color.withOpacity(record.decay < 0 ? (record.decay / (_kPointerDecay - 1)) : 1.0);
|
||||
canvas.drawPath(path.shift(record.position), paint);
|
||||
if (record.decay < 0) {
|
||||
dirty = true;
|
||||
}
|
||||
record.decay += 1;
|
||||
}
|
||||
_pointerIdToPointerRecord
|
||||
.keys
|
||||
.where((int pointer) => _pointerIdToPointerRecord[pointer]!.decay == 0)
|
||||
.toList()
|
||||
.forEach(_pointerIdToPointerRecord.remove);
|
||||
if (dirty) {
|
||||
scheduleMicrotask(() {
|
||||
_markViewNeedsPaint();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_label?.paint(context.canvas, offset - const Offset(0.0, 10.0));
|
||||
}
|
||||
|
||||
/// An object to which real device events should be routed.
|
||||
///
|
||||
/// Normally, device events are silently dropped. However, if this property is
|
||||
@ -1822,19 +1878,19 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
void handlePointerEvent(PointerEvent event) {
|
||||
switch (pointerEventSource) {
|
||||
case TestBindingEventSource.test:
|
||||
final _LiveTestPointerRecord? record = _liveTestRenderView._pointers[event.pointer];
|
||||
final _LiveTestPointerRecord? record = _pointerIdToPointerRecord[event.pointer];
|
||||
if (record != null) {
|
||||
record.position = event.position;
|
||||
if (!event.down) {
|
||||
record.decay = _kPointerDecay;
|
||||
}
|
||||
_handleViewNeedsPaint();
|
||||
_markViewNeedsPaint();
|
||||
} else if (event.down) {
|
||||
_liveTestRenderView._pointers[event.pointer] = _LiveTestPointerRecord(
|
||||
_pointerIdToPointerRecord[event.pointer] = _LiveTestPointerRecord(
|
||||
event.pointer,
|
||||
event.position,
|
||||
);
|
||||
_handleViewNeedsPaint();
|
||||
_markViewNeedsPaint();
|
||||
}
|
||||
super.handlePointerEvent(event);
|
||||
case TestBindingEventSource.device:
|
||||
@ -1846,7 +1902,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
// The pointer events received with this source has a global position
|
||||
// (see [handlePointerEventForSource]). Transform it to the local
|
||||
// coordinate space used by the testing widgets.
|
||||
final PointerEvent localEvent = event.copyWith(position: globalToLocal(event.position));
|
||||
final PointerEvent localEvent = event.copyWith(position: globalToLocal(event.position, renderView));
|
||||
withPointerEventSource(TestBindingEventSource.device,
|
||||
() => super.handlePointerEvent(localEvent)
|
||||
);
|
||||
@ -1945,7 +2001,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
}) {
|
||||
assert(!inTest);
|
||||
_inTest = true;
|
||||
_liveTestRenderView._setDescription(description);
|
||||
_setDescription(description);
|
||||
return _runTest(testBody, invariantTester, description);
|
||||
}
|
||||
|
||||
@ -1981,24 +2037,42 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
}
|
||||
|
||||
@override
|
||||
Offset globalToLocal(Offset point) {
|
||||
final Matrix4 transform = _liveTestRenderView.configuration.toHitTestMatrix();
|
||||
Offset globalToLocal(Offset point, RenderView view) {
|
||||
// The method is expected to translate the given point expressed in logical
|
||||
// pixels in the global coordinate space to the local coordinate space (also
|
||||
// expressed in logical pixels).
|
||||
// The inverted transform translates from the global coordinate space in
|
||||
// physical pixels to the local coordinate space in logical pixels.
|
||||
final Matrix4 transform = view.configuration.toMatrix();
|
||||
final double det = transform.invert();
|
||||
assert(det != 0.0);
|
||||
final Offset result = MatrixUtils.transformPoint(transform, point);
|
||||
return result;
|
||||
// In order to use the transform, we need to translate the point first into
|
||||
// the physical coordinate space by applying the device pixel ratio.
|
||||
return MatrixUtils.transformPoint(
|
||||
transform,
|
||||
point * view.configuration.devicePixelRatio,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Offset localToGlobal(Offset point) {
|
||||
final Matrix4 transform = _liveTestRenderView.configuration.toHitTestMatrix();
|
||||
return MatrixUtils.transformPoint(transform, point);
|
||||
Offset localToGlobal(Offset point, RenderView view) {
|
||||
// The method is expected to translate the given point expressed in logical
|
||||
// pixels in the local coordinate space to the global coordinate space (also
|
||||
// expressed in logical pixels).
|
||||
// The transform translates from the local coordinate space in logical
|
||||
// pixels to the global coordinate space in physical pixels.
|
||||
final Matrix4 transform = view.configuration.toMatrix();
|
||||
final Offset pointInPhysicalPixels = MatrixUtils.transformPoint(transform, point);
|
||||
// We need to apply the device pixel ratio to get back to logical pixels.
|
||||
return pointInPhysicalPixels / view.configuration.devicePixelRatio;
|
||||
}
|
||||
}
|
||||
|
||||
/// A [ViewConfiguration] that pretends the display is of a particular size. The
|
||||
/// size is in logical pixels. The resulting ViewConfiguration maps the given
|
||||
/// size onto the actual display using the [BoxFit.contain] algorithm.
|
||||
/// A [ViewConfiguration] that pretends the display is of a particular size (in
|
||||
/// logical pixels).
|
||||
///
|
||||
/// The resulting ViewConfiguration maps the given size onto the actual display
|
||||
/// using the [BoxFit.contain] algorithm.
|
||||
class TestViewConfiguration extends ViewConfiguration {
|
||||
/// Deprecated. Will be removed in a future version of Flutter.
|
||||
///
|
||||
@ -2023,7 +2097,6 @@ class TestViewConfiguration extends ViewConfiguration {
|
||||
/// The [size] defaults to 800x600.
|
||||
TestViewConfiguration.fromView({required ui.FlutterView view, super.size = _kDefaultTestViewportSize})
|
||||
: _paintMatrix = _getMatrix(size, view.devicePixelRatio, view),
|
||||
_hitTestMatrix = _getMatrix(size, 1.0, view),
|
||||
super(devicePixelRatio: view.devicePixelRatio);
|
||||
|
||||
static Matrix4 _getMatrix(Size size, double devicePixelRatio, ui.FlutterView window) {
|
||||
@ -2051,21 +2124,10 @@ class TestViewConfiguration extends ViewConfiguration {
|
||||
}
|
||||
|
||||
final Matrix4 _paintMatrix;
|
||||
final Matrix4 _hitTestMatrix;
|
||||
|
||||
@override
|
||||
Matrix4 toMatrix() => _paintMatrix.clone();
|
||||
|
||||
/// Provides the transformation matrix that converts coordinates in the test
|
||||
/// coordinate space to coordinates in logical pixels on the real display.
|
||||
///
|
||||
/// This is essentially the same as [toMatrix] but ignoring the device pixel
|
||||
/// ratio.
|
||||
///
|
||||
/// This is useful because pointers are described in logical pixels, as
|
||||
/// opposed to graphics which are expressed in physical pixels.
|
||||
Matrix4 toHitTestMatrix() => _hitTestMatrix.clone();
|
||||
|
||||
@override
|
||||
String toString() => 'TestViewConfiguration';
|
||||
}
|
||||
@ -2083,75 +2145,3 @@ class _LiveTestPointerRecord {
|
||||
Offset position;
|
||||
int decay; // >0 means down, <0 means up, increases by one each time, removed at 0
|
||||
}
|
||||
|
||||
class _LiveTestRenderView extends RenderView {
|
||||
_LiveTestRenderView({
|
||||
required super.configuration,
|
||||
required this.onNeedPaint,
|
||||
required super.view,
|
||||
});
|
||||
|
||||
@override
|
||||
TestViewConfiguration get configuration => super.configuration as TestViewConfiguration;
|
||||
@override
|
||||
set configuration(covariant TestViewConfiguration value) { super.configuration = value; }
|
||||
|
||||
final VoidCallback onNeedPaint;
|
||||
|
||||
final Map<int, _LiveTestPointerRecord> _pointers = <int, _LiveTestPointerRecord>{};
|
||||
|
||||
TextPainter? _label;
|
||||
static const TextStyle _labelStyle = TextStyle(
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 10.0,
|
||||
);
|
||||
void _setDescription(String value) {
|
||||
if (value.isEmpty) {
|
||||
_label = null;
|
||||
return;
|
||||
}
|
||||
// TODO(ianh): Figure out if the test name is actually RTL.
|
||||
_label ??= TextPainter(textAlign: TextAlign.left, textDirection: TextDirection.ltr);
|
||||
_label!.text = TextSpan(text: value, style: _labelStyle);
|
||||
_label!.layout();
|
||||
onNeedPaint();
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
assert(offset == Offset.zero);
|
||||
super.paint(context, offset);
|
||||
if (_pointers.isNotEmpty) {
|
||||
final double radius = configuration.size.shortestSide * 0.05;
|
||||
final Path path = Path()
|
||||
..addOval(Rect.fromCircle(center: Offset.zero, radius: radius))
|
||||
..moveTo(0.0, -radius * 2.0)
|
||||
..lineTo(0.0, radius * 2.0)
|
||||
..moveTo(-radius * 2.0, 0.0)
|
||||
..lineTo(radius * 2.0, 0.0);
|
||||
final Canvas canvas = context.canvas;
|
||||
final Paint paint = Paint()
|
||||
..strokeWidth = radius / 10.0
|
||||
..style = PaintingStyle.stroke;
|
||||
bool dirty = false;
|
||||
for (final int pointer in _pointers.keys) {
|
||||
final _LiveTestPointerRecord record = _pointers[pointer]!;
|
||||
paint.color = record.color.withOpacity(record.decay < 0 ? (record.decay / (_kPointerDecay - 1)) : 1.0);
|
||||
canvas.drawPath(path.shift(record.position), paint);
|
||||
if (record.decay < 0) {
|
||||
dirty = true;
|
||||
}
|
||||
record.decay += 1;
|
||||
}
|
||||
_pointers
|
||||
.keys
|
||||
.where((int pointer) => _pointers[pointer]!.decay == 0)
|
||||
.toList()
|
||||
.forEach(_pointers.remove);
|
||||
if (dirty) {
|
||||
scheduleMicrotask(onNeedPaint);
|
||||
}
|
||||
}
|
||||
_label?.paint(context.canvas, offset - const Offset(0.0, 10.0));
|
||||
}
|
||||
}
|
||||
|
@ -834,7 +834,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
|
||||
|
||||
@override
|
||||
HitTestResult hitTestOnBinding(Offset location) {
|
||||
location = binding.localToGlobal(location);
|
||||
location = binding.localToGlobal(location, binding.renderView);
|
||||
return super.hitTestOnBinding(location);
|
||||
}
|
||||
|
||||
|
70
packages/flutter_test/test/coordinate_translation_test.dart
Normal file
70
packages/flutter_test/test/coordinate_translation_test.dart
Normal file
@ -0,0 +1,70 @@
|
||||
// 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 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
final LiveTestWidgetsFlutterBinding binding = LiveTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
testWidgets('localToGlobal and globalToLocal calculate correct results', (WidgetTester tester) async {
|
||||
tester.view.physicalSize = const Size(2400, 1800);
|
||||
tester.view.devicePixelRatio = 3.0;
|
||||
final RenderView renderView = RenderView(
|
||||
view: tester.view,
|
||||
configuration: TestViewConfiguration.fromView(
|
||||
view: tester.view,
|
||||
size: const Size(400, 200),
|
||||
)
|
||||
);
|
||||
|
||||
// The configuration above defines a view with a resolution of 2400x1800
|
||||
// physical pixels. With a device pixel ratio of 3x, this yields a
|
||||
// resolution of 800x600 logical pixels. In this view, a RenderView sized
|
||||
// 400x200 (in logical pixels) is fitted using the BoxFit.contain
|
||||
// algorithm (as documented on TestViewConfiguration. To fit 400x200 into
|
||||
// 800x600 the RenderView is scaled up by 2 to fill the full width and then
|
||||
// vertically positioned in the middle. The origin of the RenderView is
|
||||
// located at (0, 100) in the logical coordinate space of the view:
|
||||
//
|
||||
// View: 800 logical pixels wide (or 2400 physical pixels)
|
||||
// +---------------------------------------+
|
||||
// | |
|
||||
// | 100px |
|
||||
// | |
|
||||
// +---------------------------------------+
|
||||
// | |
|
||||
// | RenderView (400x200px) |
|
||||
// | 400px scaled to 800x400px | View: 600 logical pixels high (or 1800 physical pixels)
|
||||
// | |
|
||||
// | |
|
||||
// +---------------------------------------+
|
||||
// | |
|
||||
// | 200px |
|
||||
// | |
|
||||
// +---------------------------------------+
|
||||
//
|
||||
// All values in logical pixels until otherwise noted.
|
||||
//
|
||||
// A point can be translated from the local coordinate space of the
|
||||
// RenderView (in logical pixels) to the global coordinate space of the View
|
||||
// (in logical pixels) by multiplying each coordinate by 2 and adding 100 to
|
||||
// the y coordinate. This is what the localToGlobal/globalToLocal methods
|
||||
// do:
|
||||
|
||||
expect(binding.localToGlobal(const Offset(0, -50), renderView), Offset.zero);
|
||||
expect(binding.localToGlobal(Offset.zero, renderView), const Offset(0, 100));
|
||||
expect(binding.localToGlobal(const Offset(200, 100), renderView), const Offset(400, 300));
|
||||
expect(binding.localToGlobal(const Offset(150, 75), renderView), const Offset(300, 250));
|
||||
expect(binding.localToGlobal(const Offset(400, 200), renderView), const Offset(800, 500));
|
||||
expect(binding.localToGlobal(const Offset(400, 400), renderView), const Offset(800, 900));
|
||||
|
||||
expect(binding.globalToLocal(Offset.zero, renderView), const Offset(0, -50));
|
||||
expect(binding.globalToLocal(const Offset(0, 100), renderView), Offset.zero);
|
||||
expect(binding.globalToLocal(const Offset(400, 300), renderView), const Offset(200, 100));
|
||||
expect(binding.globalToLocal(const Offset(300, 250), renderView), const Offset(150, 75));
|
||||
expect(binding.globalToLocal(const Offset(800, 500), renderView), const Offset(400, 200));
|
||||
expect(binding.globalToLocal(const Offset(800, 900), renderView), const Offset(400, 400));
|
||||
});
|
||||
}
|
@ -126,7 +126,7 @@ class _MockLiveTestWidgetsFlutterBinding extends LiveTestWidgetsFlutterBinding {
|
||||
// real devices touches sends event in the global coordinate system.
|
||||
// See the documentation of [handlePointerEventForSource] for details.
|
||||
if (source == TestBindingEventSource.test) {
|
||||
final PointerEvent globalEvent = event.copyWith(position: localToGlobal(event.position));
|
||||
final PointerEvent globalEvent = event.copyWith(position: localToGlobal(event.position, renderView));
|
||||
return super.handlePointerEventForSource(globalEvent);
|
||||
}
|
||||
return super.handlePointerEventForSource(event, source: source);
|
||||
|
Loading…
x
Reference in New Issue
Block a user