Skip to content

Commit

Permalink
Fix focus traversal regions to account for transforms. (#55600)
Browse files Browse the repository at this point in the history
  • Loading branch information
gspencergoog committed Apr 27, 2020
1 parent a60e4d1 commit 7ab8767
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 14 deletions.
28 changes: 14 additions & 14 deletions packages/flutter/lib/src/widgets/focus_manager.dart
Expand Up @@ -640,38 +640,38 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {

/// Returns the size of the attached widget's [RenderObject], in logical
/// units.
Size get size {
assert(
context != null,
"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;
}
///
/// Size is the size of the transformed widget in global coordinates.
Size get size => rect.size;

/// Returns the global offset to the upper left corner of the attached
/// widget's [RenderObject], in logical units.
///
/// Offset is the offset of the transformed widget in global coordinates.
Offset get offset {
assert(
context != null,
"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 '
'is typically done with the attach method.');
'The context needs to be set before trying to evaluate traversal policies. '
'Setting the context is typically done with the attach method.');
final RenderObject object = context.findRenderObject();
return MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
}

/// Returns the global rectangle of the attached widget's [RenderObject], in
/// logical units.
///
/// Rect is the rectangle of the transformed widget in global coordinates.
Rect get rect {
assert(
context != null,
"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 '
'is typically done with the attach method.');
'The context needs to be set before trying to evaluate traversal policies. '
'Setting the context is typically done with the attach method.');
final RenderObject object = context.findRenderObject();
final Offset globalOffset = MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
return globalOffset & object.semanticBounds.size;
final Offset topLeft = MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
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.
Expand Down
35 changes: 35 additions & 0 deletions packages/flutter/test/widgets/focus_manager_test.dart
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:math' as math;

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -62,6 +64,39 @@ void main() {
expect(child2.parent, isNull);
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 {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
FocusNode(
Expand Down

0 comments on commit 7ab8767

Please sign in to comment.