| // Copyright 2014 The Flutter 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:vector_math/vector_math_64.dart'; |
| |
| import 'box.dart'; |
| import 'layer.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.isOdd; |
| |
| @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 |
| Size computeDryLayout(BoxConstraints constraints) { |
| if (child == null) { |
| return constraints.smallest; |
| } |
| final Size childSize = child!.getDryLayout(_isVertical ? constraints.flipped : constraints); |
| return _isVertical ? Size(childSize.height, childSize.width) : childSize; |
| } |
| |
| @override |
| void performLayout() { |
| _paintTransform = null; |
| if (child != null) { |
| child!.layout(_isVertical ? constraints.flipped : constraints, parentUsesSize: true); |
| size = _isVertical ? Size(child!.size.height, child!.size.width) : child!.size; |
| _paintTransform = 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 { |
| size = constraints.smallest; |
| } |
| } |
| |
| @override |
| bool hitTestChildren(BoxHitTestResult result, { required Offset position }) { |
| assert(_paintTransform != null || debugNeedsLayout || child == null); |
| if (child == null || _paintTransform == null) |
| return false; |
| return result.addWithPaintTransform( |
| transform: _paintTransform, |
| position: position, |
| hitTest: (BoxHitTestResult result, Offset position) { |
| return child!.hitTest(result, position: position); |
| }, |
| ); |
| } |
| |
| void _paintChild(PaintingContext context, Offset offset) { |
| context.paintChild(child!, offset); |
| } |
| |
| @override |
| void paint(PaintingContext context, Offset offset) { |
| if (child != null) { |
| _transformLayer.layer = context.pushTransform( |
| needsCompositing, |
| offset, |
| _paintTransform!, |
| _paintChild, |
| oldLayer: _transformLayer.layer, |
| ); |
| } else { |
| _transformLayer.layer = null; |
| } |
| } |
| |
| final LayerHandle<TransformLayer> _transformLayer = LayerHandle<TransformLayer>(); |
| |
| @override |
| void dispose() { |
| _transformLayer.layer = null; |
| super.dispose(); |
| } |
| |
| @override |
| void applyPaintTransform(RenderBox child, Matrix4 transform) { |
| if (_paintTransform != null) |
| transform.multiply(_paintTransform!); |
| super.applyPaintTransform(child, transform); |
| } |
| } |