Commit d24a7d3f authored by Adam Barth's avatar Adam Barth

Merge pull request #2607 from abarth/rotated_box

Add RotatedBox which applies a rotation before layout
parents 9319ac5e e59b25b2
......@@ -26,6 +26,7 @@ export 'src/rendering/overflow.dart';
export 'src/rendering/paragraph.dart';
export 'src/rendering/performance_overlay.dart';
export 'src/rendering/proxy_box.dart';
export 'src/rendering/rotated_box.dart';
export 'src/rendering/semantics.dart';
export 'src/rendering/shifted_box.dart';
export 'src/rendering/stack.dart';
......
......@@ -154,6 +154,16 @@ class BoxConstraints extends Constraints {
maxHeight: height == null ? maxHeight : height.clamp(minHeight, maxHeight));
}
/// A box constraints with the width and height constraints flipped.
BoxConstraints get flipped {
return new BoxConstraints(
minWidth: minHeight,
maxWidth: maxHeight,
minHeight: minWidth,
maxHeight: maxWidth
);
}
/// Returns box constraints with the same width constraints but with
/// unconstrainted height.
BoxConstraints widthConstraints() => new BoxConstraints(minWidth: minWidth, maxWidth: maxWidth);
......
// Copyright 2016 The Chromium 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:math' as math;
import 'package:flutter/gestures.dart';
import 'package:vector_math/vector_math_64.dart';
import 'box.dart';
import 'object.dart';
const double _kQuarterTurnsInRadians = math.PI / 2.0;
/// Rotates its child by a integral number of quarter turns.
///
/// Unlike [RenderTransform], which applies a transform just prior to painting,
/// this object applies its rotation prior to layout, which means the entire
/// rotated box consumes only as much space as required by the rotated child.
class RenderRotatedBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
RenderRotatedBox({
int quarterTurns,
RenderBox child
}) : _quarterTurns = quarterTurns {
assert(quarterTurns != null);
this.child = child;
}
/// The number of clockwise quarter turns the child should be rotated.
int get quarterTurns => _quarterTurns;
int _quarterTurns;
void set quarterTurns(int value) {
assert(value != null);
if (_quarterTurns == value)
return;
_quarterTurns = value;
markNeedsLayout();
}
bool get _isVertical => quarterTurns % 2 == 1;
double getMinIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
if (child != null)
return _isVertical ? child.getMinIntrinsicHeight(constraints.flipped) : child.getMinIntrinsicWidth(constraints);
return super.getMinIntrinsicWidth(constraints);
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
if (child != null)
return _isVertical ? child.getMaxIntrinsicHeight(constraints.flipped) : child.getMaxIntrinsicWidth(constraints);
return super.getMaxIntrinsicWidth(constraints);
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
if (child != null)
return _isVertical ? child.getMinIntrinsicWidth(constraints.flipped) : child.getMinIntrinsicHeight(constraints);
return super.getMinIntrinsicHeight(constraints);
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
if (child != null)
return _isVertical ? child.getMaxIntrinsicWidth(constraints.flipped) : child.getMaxIntrinsicHeight(constraints);
return super.getMaxIntrinsicHeight(constraints);
}
Matrix4 _paintTransform;
void performLayout() {
_paintTransform = null;
if (child != null) {
child.layout(_isVertical ? constraints.flipped : constraints, parentUsesSize: true);
size = _isVertical ? new Size(child.size.height, child.size.width) : child.size;
_paintTransform = new Matrix4.identity()
..translate(size.width / 2.0, size.height / 2.0)
..rotateZ(_kQuarterTurnsInRadians * (quarterTurns % 4))
..translate(-child.size.width / 2.0, -child.size.height / 2.0);
} else {
performResize();
}
}
bool hitTestChildren(HitTestResult result, { Point position }) {
assert(_paintTransform != null || needsLayout || child == null);
if (child == null || _paintTransform == null)
return false;
Matrix4 inverse = new Matrix4.inverted(_paintTransform);
Vector3 position3 = new Vector3(position.x, position.y, 0.0);
Vector3 transformed3 = inverse.transform3(position3);
return child.hitTest(result, position: new Point(transformed3.x, transformed3.y));
}
void _paintChild(PaintingContext context, Offset offset) {
context.paintChild(child, offset);
}
void paint(PaintingContext context, Offset offset) {
if (child != null)
context.pushTransform(needsCompositing, offset, _paintTransform, _paintChild);
}
void applyPaintTransform(RenderBox child, Matrix4 transform) {
if (_paintTransform != null)
transform.multiply(_paintTransform);
super.applyPaintTransform(child, transform);
}
}
......@@ -320,6 +320,27 @@ class FractionalTranslation extends OneChildRenderObjectWidget {
}
}
/// Rotates its child by a integral number of quarter turns.
///
/// Unlike [Transform], which applies a transform just prior to painting,
/// this object applies its rotation prior to layout, which means the entire
/// rotated box consumes only as much space as required by the rotated child.
class RotatedBox extends OneChildRenderObjectWidget {
RotatedBox({ Key key, this.quarterTurns, Widget child })
: super(key: key, child: child) {
assert(quarterTurns != null);
}
/// The number of clockwise quarter turns the child should be rotated.
final int quarterTurns;
RenderRotatedBox createRenderObject(BuildContext context) => new RenderRotatedBox(quarterTurns: quarterTurns);
void updateRenderObject(BuildContext context, RenderRotatedBox renderObject) {
renderObject.quarterTurns = quarterTurns;
}
}
/// Insets its child by the given padding.
///
/// When passing layout constraints to its child, padding shrinks the
......
// Copyright 2015 The Chromium 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_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:test/test.dart';
void main() {
test('Rotated box control test', () {
testWidgets((WidgetTester tester) {
List<String> log = <String>[];
Key rotatedBoxKey = new UniqueKey();
tester.pumpWidget(
new Center(
child: new RotatedBox(
key: rotatedBoxKey,
quarterTurns: 1,
child: new Row(
justifyContent: FlexJustifyContent.collapse,
children: <Widget>[
new GestureDetector(
onTap: () { log.add('left'); },
child: new Container(
width: 100.0,
height: 40.0,
decoration: new BoxDecoration(backgroundColor: Colors.blue[500])
)
),
new GestureDetector(
onTap: () { log.add('right'); },
child: new Container(
width: 75.0,
height: 65.0,
decoration: new BoxDecoration(backgroundColor: Colors.blue[500])
)
),
]
)
)
)
);
RenderBox box = tester.findElementByKey(rotatedBoxKey).renderObject;
expect(box.size.width, equals(65.0));
expect(box.size.height, equals(175.0));
tester.tapAt(new Point(420.0, 280.0));
expect(log, equals(['left']));
log.clear();
tester.tapAt(new Point(380.0, 320.0));
expect(log, equals(['right']));
log.clear();
});
});
}
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