// 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); } }