Merge pull request #1692 from Hixie/overflow-and-offstage

OffStage
diff --git a/sky/packages/sky/lib/rendering.dart b/sky/packages/sky/lib/rendering.dart
index 24e7b46..52a57e8 100644
--- a/sky/packages/sky/lib/rendering.dart
+++ b/sky/packages/sky/lib/rendering.dart
@@ -20,6 +20,7 @@
 export 'src/rendering/layer.dart';
 export 'src/rendering/node.dart';
 export 'src/rendering/object.dart';
+export 'src/rendering/overflow.dart';
 export 'src/rendering/paragraph.dart';
 export 'src/rendering/proxy_box.dart';
 export 'src/rendering/shifted_box.dart';
diff --git a/sky/packages/sky/lib/src/rendering/overflow.dart b/sky/packages/sky/lib/src/rendering/overflow.dart
new file mode 100644
index 0000000..7090f44
--- /dev/null
+++ b/sky/packages/sky/lib/src/rendering/overflow.dart
@@ -0,0 +1,170 @@
+// 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) {
+    return constraints.constrainWidth();
+  }
+
+  double getMaxIntrinsicWidth(BoxConstraints constraints) {
+    return constraints.constrainWidth();
+  }
+
+  double getMinIntrinsicHeight(BoxConstraints constraints) {
+    return constraints.constrainHeight();
+  }
+
+  double getMaxIntrinsicHeight(BoxConstraints constraints) {
+    return constraints.constrainHeight();
+  }
+
+  bool get sizedByParent => true;
+
+  void performResize() {
+    size = constraints.biggest;
+  }
+
+  void performLayout() {
+    if (child != null)
+      child.layout(_getInnerConstraints(constraints));
+  }
+
+  void hitTestChildren(HitTestResult result, { Point position }) {
+    if (child != null)
+      child.hitTest(result, position: position);
+    else
+      super.hitTestChildren(result, position: position);
+  }
+
+  void paint(PaintingContext context, Offset offset) {
+    if (child != null)
+      context.paintChild(child, offset.toPoint());
+  }
+
+  String debugDescribeSettings(String prefix) {
+    return '${super.debugDescribeSettings(prefix)}' +
+           '${prefix}minWidth: ${minWidth ?? "use parent minWidth constraint"}\n' +
+           '${prefix}maxWidth: ${maxWidth ?? "use parent maxWidth constraint"}\n' +
+           '${prefix}minHeight: ${minHeight ?? "use parent minHeight constraint"}\n' +
+           '${prefix}maxHeight: ${maxHeight ?? "use parent maxHeight constraint"}\n';
+  }
+}
+
+
+/// 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) { }
+}
diff --git a/sky/packages/sky/lib/src/rendering/proxy_box.dart b/sky/packages/sky/lib/src/rendering/proxy_box.dart
index a0456d5..b98ba83 100644
--- a/sky/packages/sky/lib/src/rendering/proxy_box.dart
+++ b/sky/packages/sky/lib/src/rendering/proxy_box.dart
@@ -239,125 +239,6 @@
   }
 }
 
-/// 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 RenderProxyBox {
-  RenderOverflowBox({
-    RenderBox child,
-    double minWidth,
-    double maxWidth,
-    double minHeight,
-    double maxHeight
-  }) : _minWidth = minWidth, _maxWidth = maxWidth, _minHeight = minHeight, _maxHeight = maxHeight, super(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) {
-    return constraints.constrainWidth();
-  }
-
-  double getMaxIntrinsicWidth(BoxConstraints constraints) {
-    return constraints.constrainWidth();
-  }
-
-  double getMinIntrinsicHeight(BoxConstraints constraints) {
-    return constraints.constrainHeight();
-  }
-
-  double getMaxIntrinsicHeight(BoxConstraints constraints) {
-    return constraints.constrainHeight();
-  }
-
-  bool get sizedByParent => true;
-
-  void performResize() {
-    size = constraints.biggest;
-  }
-
-  void performLayout() {
-    if (child != null)
-      child.layout(_getInnerConstraints(constraints));
-  }
-
-  String debugDescribeSettings(String prefix) {
-    return '${super.debugDescribeSettings(prefix)}' +
-           '${prefix}minWidth: ${minWidth ?? "use parent minWidth constraint"}\n' +
-           '${prefix}maxWidth: ${maxWidth ?? "use parent maxWidth constraint"}\n' +
-           '${prefix}minHeight: ${minHeight ?? "use parent minHeight constraint"}\n' +
-           '${prefix}maxHeight: ${maxHeight ?? "use parent maxHeight constraint"}\n';
-  }
-}
-
 /// Forces child to layout at a specific aspect ratio
 ///
 /// The width of this render object is the largest width permited by the layout
diff --git a/sky/packages/sky/lib/src/widgets/basic.dart b/sky/packages/sky/lib/src/widgets/basic.dart
index f048dfe..8e21acc 100644
--- a/sky/packages/sky/lib/src/widgets/basic.dart
+++ b/sky/packages/sky/lib/src/widgets/basic.dart
@@ -346,6 +346,13 @@
   }
 }
 
+class OffStage extends OneChildRenderObjectWidget {
+  OffStage({ Key key, Widget child })
+    : super(key: key, child: child);
+
+  RenderOffStage createRenderObject() => new RenderOffStage();
+}
+
 class AspectRatio extends OneChildRenderObjectWidget {
   AspectRatio({ Key key, this.aspectRatio, Widget child })
     : super(key: key, child: child) {