Unverified Commit 7ab8767a authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Fix focus traversal regions to account for transforms. (#55600)

parent a60e4d1f
...@@ -640,38 +640,38 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier { ...@@ -640,38 +640,38 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
/// Returns the size of the attached widget's [RenderObject], in logical /// Returns the size of the attached widget's [RenderObject], in logical
/// units. /// units.
Size get size { ///
assert( /// Size is the size of the transformed widget in global coordinates.
context != null, Size get size => rect.size;
"Tried to get the size of a focus node that didn't have its context set yet.\n"
'The context needs to be set before trying to evaluate traversal policies. This '
'is typically done with the attach method.');
return context.findRenderObject().semanticBounds.size;
}
/// Returns the global offset to the upper left corner of the attached /// Returns the global offset to the upper left corner of the attached
/// widget's [RenderObject], in logical units. /// widget's [RenderObject], in logical units.
///
/// Offset is the offset of the transformed widget in global coordinates.
Offset get offset { Offset get offset {
assert( assert(
context != null, context != null,
"Tried to get the offset of a focus node that didn't have its context set yet.\n" "Tried to get the offset of a focus node that didn't have its context set yet.\n"
'The context needs to be set before trying to evaluate traversal policies. This ' 'The context needs to be set before trying to evaluate traversal policies. '
'is typically done with the attach method.'); 'Setting the context is typically done with the attach method.');
final RenderObject object = context.findRenderObject(); final RenderObject object = context.findRenderObject();
return MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft); return MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
} }
/// Returns the global rectangle of the attached widget's [RenderObject], in /// Returns the global rectangle of the attached widget's [RenderObject], in
/// logical units. /// logical units.
///
/// Rect is the rectangle of the transformed widget in global coordinates.
Rect get rect { Rect get rect {
assert( assert(
context != null, context != null,
"Tried to get the bounds of a focus node that didn't have its context set yet.\n" "Tried to get the bounds of a focus node that didn't have its context set yet.\n"
'The context needs to be set before trying to evaluate traversal policies. This ' 'The context needs to be set before trying to evaluate traversal policies. '
'is typically done with the attach method.'); 'Setting the context is typically done with the attach method.');
final RenderObject object = context.findRenderObject(); final RenderObject object = context.findRenderObject();
final Offset globalOffset = MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft); final Offset topLeft = MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
return globalOffset & object.semanticBounds.size; final Offset bottomRight = MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.bottomRight);
return Rect.fromLTRB(topLeft.dx, topLeft.dy, bottomRight.dx, bottomRight.dy);
} }
/// Removes the focus on this node by moving the primary focus to another node. /// Removes the focus on this node by moving the primary focus to another node.
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
...@@ -62,6 +64,39 @@ void main() { ...@@ -62,6 +64,39 @@ void main() {
expect(child2.parent, isNull); expect(child2.parent, isNull);
expect(parent.children, isEmpty); expect(parent.children, isEmpty);
}); });
testWidgets('Geometry is transformed properly.', (WidgetTester tester) async {
final FocusNode focusNode1 = FocusNode(debugLabel: 'Test Node 1');
final FocusNode focusNode2 = FocusNode(debugLabel: 'Test Node 2');
await tester.pumpWidget(
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Focus(focusNode: focusNode1, child: Container(width: 200, height: 100),),
Transform.translate(
offset: const Offset(10, 20),
child: Transform.scale(
scale: 0.33,
child: Transform.rotate(
angle: math.pi,
child: Focus(focusNode: focusNode2, child: Container(width: 200, height: 100)),
),
),
),
],
),
),
);
focusNode2.requestFocus();
await tester.pump();
expect(focusNode1.rect, equals(const Rect.fromLTRB(300.0, 8.0, 500.0, 108.0)));
expect(focusNode2.rect, equals(const Rect.fromLTRB(443.0, 194.5, 377.0, 161.5)));
expect(focusNode1.size, equals(const Size(200.0, 100.0)));
expect(focusNode2.size, equals(const Size(-66.0, -33.0)));
expect(focusNode1.offset, equals(const Offset(300.0, 8.0)));
expect(focusNode2.offset, equals(const Offset(443.0, 194.5)));
});
testWidgets('implements debugFillProperties', (WidgetTester tester) async { testWidgets('implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
FocusNode( FocusNode(
......
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