| // 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/foundation.dart'; |
| import 'package:flutter/gestures.dart'; |
| import 'package:flutter/painting.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> { |
| /// Creates a rotated render box. |
| /// |
| /// The [quarterTurns] argument must not be null. |
| RenderRotatedBox({ |
| @required int quarterTurns, |
| RenderBox child |
| }) : assert(quarterTurns != null), |
| _quarterTurns = quarterTurns { |
| this.child = child; |
| } |
| |
| /// The number of clockwise quarter turns the child should be rotated. |
| int get quarterTurns => _quarterTurns; |
| int _quarterTurns; |
| set quarterTurns(int value) { |
| assert(value != null); |
| if (_quarterTurns == value) |
| return; |
| _quarterTurns = value; |
| markNeedsLayout(); |
| } |
| |
| bool get _isVertical => quarterTurns % 2 == 1; |
| |
| @override |
| double computeMinIntrinsicWidth(double height) { |
| if (child == null) |
| return 0.0; |
| return _isVertical ? child.getMinIntrinsicHeight(height) : child.getMinIntrinsicWidth(height); |
| } |
| |
| @override |
| double computeMaxIntrinsicWidth(double height) { |
| if (child == null) |
| return 0.0; |
| return _isVertical ? child.getMaxIntrinsicHeight(height) : child.getMaxIntrinsicWidth(height); |
| } |
| |
| @override |
| double computeMinIntrinsicHeight(double width) { |
| if (child == null) |
| return 0.0; |
| return _isVertical ? child.getMinIntrinsicWidth(width) : child.getMinIntrinsicHeight(width); |
| } |
| |
| @override |
| double computeMaxIntrinsicHeight(double width) { |
| if (child == null) |
| return 0.0; |
| return _isVertical ? child.getMaxIntrinsicWidth(width) : child.getMaxIntrinsicHeight(width); |
| } |
| |
| Matrix4 _paintTransform; |
| |
| @override |
| 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(); |
| } |
| } |
| |
| @override |
| bool hitTestChildren(HitTestResult result, { Offset position }) { |
| assert(_paintTransform != null || debugNeedsLayout || child == null); |
| if (child == null || _paintTransform == null) |
| return false; |
| final Matrix4 inverse = new Matrix4.inverted(_paintTransform); |
| return child.hitTest(result, position: MatrixUtils.transformPoint(inverse, position)); |
| } |
| |
| void _paintChild(PaintingContext context, Offset offset) { |
| context.paintChild(child, offset); |
| } |
| |
| @override |
| void paint(PaintingContext context, Offset offset) { |
| if (child != null) |
| context.pushTransform(needsCompositing, offset, _paintTransform, _paintChild); |
| } |
| |
| @override |
| void applyPaintTransform(RenderBox child, Matrix4 transform) { |
| if (_paintTransform != null) |
| transform.multiply(_paintTransform); |
| super.applyPaintTransform(child, transform); |
| } |
| } |