rotated_box.dart 4.31 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9
// 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';
10
import 'layer.dart';
11 12
import 'object.dart';

13
const double _kQuarterTurnsInRadians = math.pi / 2.0;
14 15 16 17 18 19 20

/// 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> {
21 22 23
  /// Creates a rotated render box.
  ///
  /// The [quarterTurns] argument must not be null.
24
  RenderRotatedBox({
25 26
    required int quarterTurns,
    RenderBox? child,
27 28
  }) : assert(quarterTurns != null),
       _quarterTurns = quarterTurns {
29 30 31 32 33 34
    this.child = child;
  }

  /// The number of clockwise quarter turns the child should be rotated.
  int get quarterTurns => _quarterTurns;
  int _quarterTurns;
35
  set quarterTurns(int value) {
36 37 38 39 40 41 42
    assert(value != null);
    if (_quarterTurns == value)
      return;
    _quarterTurns = value;
    markNeedsLayout();
  }

43
  bool get _isVertical => quarterTurns.isOdd;
44

45
  @override
46
  double computeMinIntrinsicWidth(double height) {
47 48
    if (child == null)
      return 0.0;
49
    return _isVertical ? child!.getMinIntrinsicHeight(height) : child!.getMinIntrinsicWidth(height);
50 51
  }

52
  @override
53
  double computeMaxIntrinsicWidth(double height) {
54 55
    if (child == null)
      return 0.0;
56
    return _isVertical ? child!.getMaxIntrinsicHeight(height) : child!.getMaxIntrinsicWidth(height);
57 58
  }

59
  @override
60
  double computeMinIntrinsicHeight(double width) {
61 62
    if (child == null)
      return 0.0;
63
    return _isVertical ? child!.getMinIntrinsicWidth(width) : child!.getMinIntrinsicHeight(width);
64 65
  }

66
  @override
67
  double computeMaxIntrinsicHeight(double width) {
68 69
    if (child == null)
      return 0.0;
70
    return _isVertical ? child!.getMaxIntrinsicWidth(width) : child!.getMaxIntrinsicHeight(width);
71 72
  }

73
  Matrix4? _paintTransform;
74

75 76 77 78 79 80 81 82 83
  @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;
  }

84
  @override
85 86 87
  void performLayout() {
    _paintTransform = null;
    if (child != null) {
88 89
      child!.layout(_isVertical ? constraints.flipped : constraints, parentUsesSize: true);
      size = _isVertical ? Size(child!.size.height, child!.size.width) : child!.size;
90
      _paintTransform = Matrix4.identity()
91 92
        ..translate(size.width / 2.0, size.height / 2.0)
        ..rotateZ(_kQuarterTurnsInRadians * (quarterTurns % 4))
93
        ..translate(-child!.size.width / 2.0, -child!.size.height / 2.0);
94
    } else {
95
      size = constraints.smallest;
96 97 98
    }
  }

99
  @override
100
  bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
101
    assert(_paintTransform != null || debugNeedsLayout || child == null);
102 103
    if (child == null || _paintTransform == null)
      return false;
104 105 106
    return result.addWithPaintTransform(
      transform: _paintTransform,
      position: position,
107 108
      hitTest: (BoxHitTestResult result, Offset position) {
        return child!.hitTest(result, position: position);
109 110
      },
    );
111 112 113
  }

  void _paintChild(PaintingContext context, Offset offset) {
114
    context.paintChild(child!, offset);
115 116
  }

117
  @override
118
  void paint(PaintingContext context, Offset offset) {
119
    if (child != null) {
120
      _transformLayer.layer = context.pushTransform(
121 122 123 124
        needsCompositing,
        offset,
        _paintTransform!,
        _paintChild,
125
        oldLayer: _transformLayer.layer,
126
      );
127
    } else {
128
      _transformLayer.layer = null;
129
    }
130 131
  }

132 133 134 135 136 137 138
  final LayerHandle<TransformLayer> _transformLayer = LayerHandle<TransformLayer>();

  @override
  void dispose() {
    _transformLayer.layer = null;
    super.dispose();
  }
139

140
  @override
141 142
  void applyPaintTransform(RenderBox child, Matrix4 transform) {
    if (_paintTransform != null)
143
      transform.multiply(_paintTransform!);
144 145 146
    super.applyPaintTransform(child, transform);
  }
}