overflow.dart 7.96 KB
Newer Older
Hixie's avatar
Hixie committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
// 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 'box.dart';
import 'object.dart';

export 'package:flutter/src/painting/box_painter.dart';

/// A render object that imposes different constraints on its child than it gets
/// from its parent, possibly allowing the child to overflow the parent.
///
/// A render overflow box proxies most functions in the render box protocol to
/// its child, except that when laying out its child, it passes constraints
/// based on the minWidth, maxWidth, minHeight, and maxHeight fields instead of
/// just passing the parent's constraints in. Specifically, it overrides any of
/// the equivalent fields on the constraints given by the parent with the
/// constraints given by these fields for each such field that is not null. It
/// then sizes itself based on the parent's constraints' maxWidth and maxHeight,
/// ignoring the child's dimensions.
///
/// For example, if you wanted a box to always render 50 pixels high, regardless
/// of where it was rendered, you would wrap it in a RenderOverflow with
/// minHeight and maxHeight set to 50.0. Generally speaking, to avoid confusing
/// behaviour around hit testing, a RenderOverflowBox should usually be wrapped
/// in a RenderClipRect.
///
/// The child is positioned at the top left of the box. To position a smaller
/// child inside a larger parent, use [RenderPositionedBox] and
/// [RenderConstrainedBox] rather than RenderOverflowBox.
class RenderOverflowBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
  RenderOverflowBox({
    RenderBox child,
    double minWidth,
    double maxWidth,
    double minHeight,
    double maxHeight
  }) : _minWidth = minWidth, _maxWidth = maxWidth, _minHeight = minHeight, _maxHeight = maxHeight {
    this.child = child;
  }

  /// The minimum width constraint to give the child. Set this to null (the
  /// default) to use the constraint from the parent instead.
  double get minWidth => _minWidth;
  double _minWidth;
  void set minWidth (double value) {
    if (_minWidth == value)
      return;
    _minWidth = value;
    markNeedsLayout();
  }

  /// The maximum width constraint to give the child. Set this to null (the
  /// default) to use the constraint from the parent instead.
  double get maxWidth => _maxWidth;
  double _maxWidth;
  void set maxWidth (double value) {
    if (_maxWidth == value)
      return;
    _maxWidth = value;
    markNeedsLayout();
  }

  /// The minimum height constraint to give the child. Set this to null (the
  /// default) to use the constraint from the parent instead.
  double get minHeight => _minHeight;
  double _minHeight;
  void set minHeight (double value) {
    if (_minHeight == value)
      return;
    _minHeight = value;
    markNeedsLayout();
  }

  /// The maximum height constraint to give the child. Set this to null (the
  /// default) to use the constraint from the parent instead.
  double get maxHeight => _maxHeight;
  double _maxHeight;
  void set maxHeight (double value) {
    if (_maxHeight == value)
      return;
    _maxHeight = value;
    markNeedsLayout();
  }

  BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
    return new BoxConstraints(
      minWidth: _minWidth ?? constraints.minWidth,
      maxWidth: _maxWidth ?? constraints.maxWidth,
      minHeight: _minHeight ?? constraints.minHeight,
      maxHeight: _maxHeight ?? constraints.maxHeight
    );
  }

  double getMinIntrinsicWidth(BoxConstraints constraints) {
96
    assert(constraints.isNormalized);
Hixie's avatar
Hixie committed
97 98 99 100
    return constraints.constrainWidth();
  }

  double getMaxIntrinsicWidth(BoxConstraints constraints) {
101
    assert(constraints.isNormalized);
Hixie's avatar
Hixie committed
102 103 104 105
    return constraints.constrainWidth();
  }

  double getMinIntrinsicHeight(BoxConstraints constraints) {
106
    assert(constraints.isNormalized);
Hixie's avatar
Hixie committed
107 108 109 110
    return constraints.constrainHeight();
  }

  double getMaxIntrinsicHeight(BoxConstraints constraints) {
111
    assert(constraints.isNormalized);
Hixie's avatar
Hixie committed
112 113 114
    return constraints.constrainHeight();
  }

115 116 117 118 119 120
  double computeDistanceToActualBaseline(TextBaseline baseline) {
    if (child != null)
      return child.getDistanceToActualBaseline(baseline);
    return super.computeDistanceToActualBaseline(baseline);
  }

Hixie's avatar
Hixie committed
121 122 123 124 125 126 127 128 129 130 131
  bool get sizedByParent => true;

  void performResize() {
    size = constraints.biggest;
  }

  void performLayout() {
    if (child != null)
      child.layout(_getInnerConstraints(constraints));
  }

Adam Barth's avatar
Adam Barth committed
132 133
  bool hitTestChildren(HitTestResult result, { Point position }) {
    return child?.hitTest(result, position: position) ?? false;
Hixie's avatar
Hixie committed
134 135 136 137
  }

  void paint(PaintingContext context, Offset offset) {
    if (child != null)
Adam Barth's avatar
Adam Barth committed
138
      context.paintChild(child, offset);
Hixie's avatar
Hixie committed
139 140
  }

141 142 143 144 145 146
  void debugDescribeSettings(List<String> settings) {
    super.debugDescribeSettings(settings);
    settings.add('minWidth: ${minWidth ?? "use parent minWidth constraint"}');
    settings.add('maxWidth: ${maxWidth ?? "use parent maxWidth constraint"}');
    settings.add('minHeight: ${minHeight ?? "use parent minHeight constraint"}');
    settings.add('maxHeight: ${maxHeight ?? "use parent maxHeight constraint"}');
Hixie's avatar
Hixie committed
147 148 149
  }
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
/// A render box that's a specific size but passes its original constraints through to its child, which will probably overflow
class RenderSizedOverflowBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
  RenderSizedOverflowBox({
    RenderBox child,
    Size requestedSize
  }) : _requestedSize = requestedSize {
    assert(requestedSize != null);
    this.child = child;
  }

  /// The size this render box should attempt to be.
  Size get requestedSize => _requestedSize;
  Size _requestedSize;
  void set requestedSize (Size value) {
    assert(value != null);
    if (_requestedSize == value)
      return;
    _requestedSize = value;
    markNeedsLayout();
  }

  double getMinIntrinsicWidth(BoxConstraints constraints) {
172
    assert(constraints.isNormalized);
173 174 175 176
    return constraints.constrainWidth(_requestedSize.width);
  }

  double getMaxIntrinsicWidth(BoxConstraints constraints) {
177
    assert(constraints.isNormalized);
178 179 180 181
    return constraints.constrainWidth(_requestedSize.width);
  }

  double getMinIntrinsicHeight(BoxConstraints constraints) {
182
    assert(constraints.isNormalized);
183 184 185 186
    return constraints.constrainWidth(_requestedSize.height);
  }

  double getMaxIntrinsicHeight(BoxConstraints constraints) {
187
    assert(constraints.isNormalized);
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    return constraints.constrainWidth(_requestedSize.height);
  }

  double computeDistanceToActualBaseline(TextBaseline baseline) {
    if (child != null)
      return child.getDistanceToActualBaseline(baseline);
    return super.computeDistanceToActualBaseline(baseline);
  }

  void performLayout() {
    size = constraints.constrain(_requestedSize);
    if (child != null)
      child.layout(constraints);
  }

Adam Barth's avatar
Adam Barth committed
203 204
  bool hitTestChildren(HitTestResult result, { Point position }) {
    return child?.hitTest(result, position: position) ?? false;
205 206 207 208
  }

  void paint(PaintingContext context, Offset offset) {
    if (child != null)
Adam Barth's avatar
Adam Barth committed
209
      context.paintChild(child, offset);
210 211
  }
}
Hixie's avatar
Hixie committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239

/// Lays the child out as if it was in the tree, but without painting anything,
/// without making the child available for hit testing, and without taking any
/// room in the parent.
class RenderOffStage extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
  RenderOffStage({ RenderBox child }) {
    this.child = child;
  }

  double getMinIntrinsicWidth(BoxConstraints constraints) => constraints.minWidth;
  double getMaxIntrinsicWidth(BoxConstraints constraints) => constraints.minWidth;
  double getMinIntrinsicHeight(BoxConstraints constraints) => constraints.minHeight;
  double getMaxIntrinsicHeight(BoxConstraints constraints) => constraints.minHeight;

  bool get sizedByParent => true;

  void performResize() {
    size = constraints.smallest;
  }

  void performLayout() {
    if (child != null)
      child.layout(constraints);
  }

  bool hitTest(HitTestResult result, { Point position }) => false;
  void paint(PaintingContext context, Offset offset) { }
}