diff --git a/packages/flutter/lib/rendering/proxy_box.dart b/packages/flutter/lib/rendering/proxy_box.dart index 418d59a80f..6dec1dfdc7 100644 --- a/packages/flutter/lib/rendering/proxy_box.dart +++ b/packages/flutter/lib/rendering/proxy_box.dart @@ -287,13 +287,13 @@ class RenderShrinkWrapHeight extends RenderProxyBox { double getMinIntrinsicWidth(BoxConstraints constraints) { if (child == null) return constraints.constrainWidth(0.0); - return child.getMinIntrinsicWidth(_getInnerConstraints(constraints)); + return child.getMinIntrinsicWidth(_getInnerConstraints(constraints)); } double getMaxIntrinsicWidth(BoxConstraints constraints) { if (child == null) return constraints.constrainWidth(0.0); - return child.getMaxIntrinsicWidth(_getInnerConstraints(constraints)); + return child.getMaxIntrinsicWidth(_getInnerConstraints(constraints)); } double getMinIntrinsicHeight(BoxConstraints constraints) { @@ -516,13 +516,24 @@ class RenderDecoratedBox extends RenderProxyBox { class RenderTransform extends RenderProxyBox { RenderTransform({ Matrix4 transform, + Offset origin, RenderBox child }) : super(child) { assert(transform != null); this.transform = transform; + this.origin = origin; } Matrix4 _transform; + Offset _origin; + + Offset get origin => _origin; + void set origin (Offset newOrigin) { + if (_origin == newOrigin) + return; + _origin = newOrigin; + markNeedsPaint(); + } void set transform(Matrix4 newTransform) { assert(newTransform != null); @@ -562,10 +573,20 @@ class RenderTransform extends RenderProxyBox { markNeedsPaint(); } + Matrix4 get _effectiveTransform { + if (_origin == null) + return _transform; + return new Matrix4 + .identity() + .translate(_origin.dx, _origin.dy) + .multiply(_transform) + .translate(-_origin.dx, -_origin.dy); + } + bool hitTest(HitTestResult result, { Point position }) { Matrix4 inverse = new Matrix4.zero(); // TODO(abarth): Check the determinant for degeneracy. - inverse.copyInverse(_transform); + inverse.copyInverse(_effectiveTransform); Vector3 position3 = new Vector3(position.x, position.y, 0.0); Vector3 transformed3 = inverse.transform3(position3); @@ -575,18 +596,18 @@ class RenderTransform extends RenderProxyBox { void paint(PaintingContext context, Offset offset) { if (child != null) - context.paintChildWithTransform(child, offset.toPoint(), _transform); + context.paintChildWithTransform(child, offset.toPoint(), _effectiveTransform); } void applyPaintTransform(Matrix4 transform) { super.applyPaintTransform(transform); - transform.multiply(_transform); + transform.multiply(_effectiveTransform); } String debugDescribeSettings(String prefix) { List result = _transform.toString().split('\n').map((s) => '$prefix $s\n').toList(); result.removeLast(); - return '${super.debugDescribeSettings(prefix)}${prefix}transform matrix:\n${result.join()}'; + return '${super.debugDescribeSettings(prefix)}${prefix}transform matrix:\n${result.join()}\n${prefix}origin: ${origin}\n'; } } diff --git a/packages/flutter/lib/widgets/basic.dart b/packages/flutter/lib/widgets/basic.dart index f4a84bcb50..b06bd8155b 100644 --- a/packages/flutter/lib/widgets/basic.dart +++ b/packages/flutter/lib/widgets/basic.dart @@ -154,17 +154,19 @@ class ClipOval extends OneChildRenderObjectWrapper { // POSITIONING AND SIZING NODES class Transform extends OneChildRenderObjectWrapper { - Transform({ Key key, this.transform, Widget child }) + Transform({ Key key, this.transform, this.origin, Widget child }) : super(key: key, child: child); final Matrix4 transform; + final Offset origin; - RenderTransform createNode() => new RenderTransform(transform: transform); + RenderTransform createNode() => new RenderTransform(transform: transform, origin: origin); RenderTransform get renderObject => super.renderObject; void syncRenderObject(Transform old) { super.syncRenderObject(old); renderObject.transform = transform; + renderObject.origin = origin; } } diff --git a/packages/unit/test/widget/transform_test.dart b/packages/unit/test/widget/transform_test.dart new file mode 100644 index 0000000000..cf824b0d37 --- /dev/null +++ b/packages/unit/test/widget/transform_test.dart @@ -0,0 +1,51 @@ +import 'package:sky/widgets.dart'; +import 'package:test/test.dart'; + +import 'widget_tester.dart'; + +void main() { + test('Transform origin', () { + WidgetTester tester = new WidgetTester(); + + bool didReceiveTap = false; + tester.pumpFrame(() { + return new Stack([ + new Positioned( + top: 100.0, + left: 100.0, + child: new Container( + width: 100.0, + height: 100.0, + decoration: new BoxDecoration( + backgroundColor: new Color(0xFF0000FF) + ) + ) + ), + new Positioned( + top: 100.0, + left: 100.0, + child: new Container( + width: 100.0, + height: 100.0, + child: new Transform( + transform: new Matrix4.identity().scale(0.5, 0.5), + origin: new Offset(100.0, 50.0), + child: new GestureDetector( + onTap: () { + didReceiveTap = true; + }, + child: new Container() + ) + ) + ) + ) + ]); + }); + + expect(didReceiveTap, isFalse); + tester.tapAt(new Point(110.0, 110.0)); + expect(didReceiveTap, isFalse); + tester.tapAt(new Point(190.0, 150.0)); + expect(didReceiveTap, isTrue); + }); +} diff --git a/packages/unit/test/widget/widget_tester.dart b/packages/unit/test/widget/widget_tester.dart index 187a360d32..a1ace1ef95 100644 --- a/packages/unit/test/widget/widget_tester.dart +++ b/packages/unit/test/widget/widget_tester.dart @@ -90,7 +90,10 @@ class WidgetTester { } void tap(Widget widget, { int pointer: 1 }) { - Point location = getCenter(widget); + tapAt(getCenter(widget), pointer: pointer); + } + + void tapAt(Point location, { int pointer: 1 }) { HitTestResult result = _hitTest(location); TestPointer p = new TestPointer(pointer); _dispatchEvent(p.down(location), result);