diff --git a/examples/rendering/align_items.dart b/examples/rendering/align_items.dart index 817857d8da4..22145ccade2 100644 --- a/examples/rendering/align_items.dart +++ b/examples/rendering/align_items.dart @@ -9,6 +9,8 @@ import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; import 'package:sky/rendering/object.dart'; import 'package:sky/rendering/paragraph.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; import 'solid_color_box.dart'; diff --git a/examples/rendering/baseline.dart b/examples/rendering/baseline.dart index 3d610168729..568e48a4603 100644 --- a/examples/rendering/baseline.dart +++ b/examples/rendering/baseline.dart @@ -9,6 +9,8 @@ import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; import 'package:sky/rendering/object.dart'; import 'package:sky/rendering/paragraph.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; RenderBox getBox(double lh) { diff --git a/examples/rendering/borders.dart b/examples/rendering/borders.dart index c8f92cfb4a2..3b10849c9bb 100644 --- a/examples/rendering/borders.dart +++ b/examples/rendering/borders.dart @@ -6,6 +6,8 @@ import 'dart:sky' as sky; import 'package:sky/rendering/block.dart'; import 'package:sky/rendering/box.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; void main() { diff --git a/examples/rendering/flex.dart b/examples/rendering/flex.dart index 55b2460040c..e44667522e0 100644 --- a/examples/rendering/flex.dart +++ b/examples/rendering/flex.dart @@ -6,6 +6,8 @@ import 'dart:sky' as sky; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; class RenderSolidColor extends RenderDecoratedBox { diff --git a/examples/rendering/interactive_flex.dart b/examples/rendering/interactive_flex.dart index 462ea0be2b5..2a097d78498 100644 --- a/examples/rendering/interactive_flex.dart +++ b/examples/rendering/interactive_flex.dart @@ -8,10 +8,12 @@ import 'dart:math' as math; import 'package:sky/mojo/activity.dart' as activity; import 'package:sky/mojo/net/image_cache.dart' as image_cache; import 'package:sky/painting/text_style.dart'; -import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; +import 'package:sky/rendering/image.dart'; import 'package:sky/rendering/object.dart'; import 'package:sky/rendering/paragraph.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; import 'solid_color_box.dart'; diff --git a/examples/rendering/justify_content.dart b/examples/rendering/justify_content.dart index df3e1865b8d..e3f0d37b79f 100644 --- a/examples/rendering/justify_content.dart +++ b/examples/rendering/justify_content.dart @@ -5,10 +5,11 @@ import 'dart:sky'; import 'package:sky/painting/text_style.dart'; -import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; import 'package:sky/rendering/object.dart'; import 'package:sky/rendering/paragraph.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; import 'solid_color_box.dart'; diff --git a/examples/rendering/render_paragraph.dart b/examples/rendering/render_paragraph.dart index 58e390b3d2c..5eda2c504fb 100644 --- a/examples/rendering/render_paragraph.dart +++ b/examples/rendering/render_paragraph.dart @@ -5,11 +5,11 @@ import 'dart:sky'; import 'package:sky/painting/text_style.dart'; -import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; import 'package:sky/rendering/object.dart'; import 'package:sky/rendering/paragraph.dart'; import 'package:sky/rendering/sky_binding.dart'; +import 'package:sky/rendering/proxy_box.dart'; import 'solid_color_box.dart'; diff --git a/examples/rendering/sector_layout.dart b/examples/rendering/sector_layout.dart index 3d6375b6113..6141a13b5bd 100644 --- a/examples/rendering/sector_layout.dart +++ b/examples/rendering/sector_layout.dart @@ -7,6 +7,7 @@ import 'dart:sky' as sky; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/proxy_box.dart'; import 'package:sky/rendering/sky_binding.dart'; const double kTwoPi = 2 * math.PI; diff --git a/examples/rendering/shadowed_box.dart b/examples/rendering/shadowed_box.dart index 702c5f7cee4..305bcea23a0 100644 --- a/examples/rendering/shadowed_box.dart +++ b/examples/rendering/shadowed_box.dart @@ -4,7 +4,8 @@ import 'dart:sky'; -import 'package:sky/rendering/box.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; import 'package:sky/theme/colors.dart'; import 'package:sky/theme/shadows.dart'; diff --git a/examples/rendering/simple_autolayout.dart b/examples/rendering/simple_autolayout.dart index 23817fc9cf0..18e074271ce 100644 --- a/examples/rendering/simple_autolayout.dart +++ b/examples/rendering/simple_autolayout.dart @@ -3,11 +3,12 @@ // found in the LICENSE file. import 'dart:sky'; -import 'package:sky/rendering/box.dart'; -import 'package:sky/rendering/object.dart'; -import 'package:sky/rendering/sky_binding.dart'; -import 'package:sky/rendering/auto_layout.dart'; + import 'package:cassowary/cassowary.dart' as al; +import 'package:sky/rendering/auto_layout.dart'; +import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/sky_binding.dart'; void main() { RenderDecoratedBox c1 = new RenderDecoratedBox( diff --git a/examples/rendering/solid_color_box.dart b/examples/rendering/solid_color_box.dart index edca5473ee1..ad6097eb657 100644 --- a/examples/rendering/solid_color_box.dart +++ b/examples/rendering/solid_color_box.dart @@ -3,8 +3,10 @@ // found in the LICENSE file. import 'dart:sky' as sky; -import 'package:sky/rendering/object.dart'; + import 'package:sky/rendering/box.dart'; +import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/proxy_box.dart'; class RenderSolidColorBox extends RenderDecoratedBox { final Size desiredSize; diff --git a/examples/rendering/spinning_flex.dart b/examples/rendering/spinning_flex.dart index a8f9d9c573b..7ee02e85c46 100644 --- a/examples/rendering/spinning_flex.dart +++ b/examples/rendering/spinning_flex.dart @@ -5,8 +5,9 @@ import 'dart:sky' as sky; import 'package:sky/base/scheduler.dart'; -import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; import 'package:vector_math/vector_math.dart'; diff --git a/examples/rendering/transform.dart b/examples/rendering/transform.dart index bbb7b903167..fd636b4dc1b 100644 --- a/examples/rendering/transform.dart +++ b/examples/rendering/transform.dart @@ -6,6 +6,7 @@ import 'dart:sky' as sky; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; +import 'package:sky/rendering/proxy_box.dart'; import 'package:sky/rendering/sky_binding.dart'; import 'package:vector_math/vector_math.dart'; diff --git a/examples/stocks/lib/main.dart b/examples/stocks/lib/main.dart index 9111a7f85a0..9b312ba8bf6 100644 --- a/examples/stocks/lib/main.dart +++ b/examples/stocks/lib/main.dart @@ -9,7 +9,6 @@ import 'dart:sky' as sky; import 'package:sky/editing/input.dart'; import 'package:sky/painting/text_style.dart'; -import 'package:sky/rendering/box.dart'; import 'package:sky/theme/colors.dart' as colors; import 'package:sky/theme/typography.dart' as typography; import 'package:sky/widgets.dart'; diff --git a/examples/widgets/container.dart b/examples/widgets/container.dart index 836d731fa48..ea30b43c04e 100644 --- a/examples/widgets/container.dart +++ b/examples/widgets/container.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; import 'package:sky/widgets/raised_button.dart'; import 'package:sky/widgets/basic.dart'; diff --git a/examples/widgets/ensure_visible.dart b/examples/widgets/ensure_visible.dart index 8fad5d75135..280ec4822d7 100644 --- a/examples/widgets/ensure_visible.dart +++ b/examples/widgets/ensure_visible.dart @@ -8,7 +8,6 @@ import 'package:sky/animation/curves.dart'; import 'package:sky/base/lerp.dart'; import 'package:sky/painting/box_painter.dart'; import 'package:sky/painting/text_style.dart'; -import 'package:sky/rendering/box.dart'; import 'package:sky/theme/colors.dart'; import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/block_viewport.dart'; diff --git a/examples/widgets/piano.dart b/examples/widgets/piano.dart index 6625d12bca9..4458feb2b42 100644 --- a/examples/widgets/piano.dart +++ b/examples/widgets/piano.dart @@ -5,7 +5,6 @@ import 'package:mojo/mojo/url_response.mojom.dart'; import 'package:sky/mojo/net/fetch.dart'; import 'package:sky/mojo/shell.dart' as shell; -import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; import 'package:sky/theme/colors.dart' as colors; import 'package:sky/widgets/basic.dart'; diff --git a/examples/widgets/spinning_mixed.dart b/examples/widgets/spinning_mixed.dart index 2225f45871e..6e212b2f400 100644 --- a/examples/widgets/spinning_mixed.dart +++ b/examples/widgets/spinning_mixed.dart @@ -5,12 +5,13 @@ import 'dart:sky' as sky; import 'package:sky/base/scheduler.dart' as scheduler; -import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/sky_binding.dart'; import 'package:sky/widgets/basic.dart'; -import 'package:sky/widgets/raised_button.dart'; import 'package:sky/widgets/framework.dart'; +import 'package:sky/widgets/raised_button.dart'; import 'package:vector_math/vector_math.dart'; import '../rendering/solid_color_box.dart'; diff --git a/packages/flutter/lib/rendering/box.dart b/packages/flutter/lib/rendering/box.dart index fbd93deb7be..e9ce2a3572b 100644 --- a/packages/flutter/lib/rendering/box.dart +++ b/packages/flutter/lib/rendering/box.dart @@ -8,11 +8,9 @@ import 'dart:sky' as sky; import 'package:sky/base/debug.dart'; import 'package:sky/painting/box_painter.dart'; import 'package:sky/painting/text_style.dart'; -import 'package:sky/rendering/layer.dart'; import 'package:sky/rendering/object.dart'; import 'package:vector_math/vector_math.dart'; -export 'package:sky/painting/box_painter.dart'; export 'package:sky/painting/text_style.dart' show TextBaseline; // GENERIC BOX RENDERING @@ -478,1265 +476,6 @@ abstract class RenderBox extends RenderObject { String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}size: ${size}\n'; } -class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin { - - // ProxyBox assumes the child will be at 0,0 and will have the same size - - RenderProxyBox([RenderBox child = null]) { - this.child = child; - } - - double getMinIntrinsicWidth(BoxConstraints constraints) { - if (child != null) - return child.getMinIntrinsicWidth(constraints); - return super.getMinIntrinsicWidth(constraints); - } - - double getMaxIntrinsicWidth(BoxConstraints constraints) { - if (child != null) - return child.getMaxIntrinsicWidth(constraints); - return super.getMaxIntrinsicWidth(constraints); - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - if (child != null) - return child.getMinIntrinsicHeight(constraints); - return super.getMinIntrinsicHeight(constraints); - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - if (child != null) - return child.getMaxIntrinsicHeight(constraints); - return super.getMaxIntrinsicHeight(constraints); - } - - double computeDistanceToActualBaseline(TextBaseline baseline) { - if (child != null) - return child.getDistanceToActualBaseline(baseline); - return super.computeDistanceToActualBaseline(baseline); - } - - void performLayout() { - if (child != null) { - child.layout(constraints, parentUsesSize: true); - size = child.size; - } else { - performResize(); - } - } - - 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()); - } -} - -class RenderConstrainedBox extends RenderProxyBox { - RenderConstrainedBox({ - RenderBox child, - BoxConstraints additionalConstraints - }) : super(child), _additionalConstraints = additionalConstraints { - assert(additionalConstraints != null); - } - - BoxConstraints _additionalConstraints; - BoxConstraints get additionalConstraints => _additionalConstraints; - void set additionalConstraints (BoxConstraints value) { - assert(value != null); - if (_additionalConstraints == value) - return; - _additionalConstraints = value; - markNeedsLayout(); - } - - double getMinIntrinsicWidth(BoxConstraints constraints) { - if (child != null) - return child.getMinIntrinsicWidth(_additionalConstraints.apply(constraints)); - return _additionalConstraints.apply(constraints).constrainWidth(0.0); - } - - double getMaxIntrinsicWidth(BoxConstraints constraints) { - if (child != null) - return child.getMaxIntrinsicWidth(_additionalConstraints.apply(constraints)); - return _additionalConstraints.apply(constraints).constrainWidth(0.0); - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - if (child != null) - return child.getMinIntrinsicHeight(_additionalConstraints.apply(constraints)); - return _additionalConstraints.apply(constraints).constrainHeight(0.0); - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - if (child != null) - return child.getMaxIntrinsicHeight(_additionalConstraints.apply(constraints)); - return _additionalConstraints.apply(constraints).constrainHeight(0.0); - } - - void performLayout() { - if (child != null) { - child.layout(_additionalConstraints.apply(constraints), parentUsesSize: true); - size = child.size; - } else { - size = _additionalConstraints.apply(constraints).constrain(Size.zero); - } - } - - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}additionalConstraints: ${additionalConstraints}\n'; -} - -class RenderAspectRatio extends RenderProxyBox { - RenderAspectRatio({ - RenderBox child, - double aspectRatio - }) : super(child), _aspectRatio = aspectRatio { - assert(_aspectRatio != null); - } - - double _aspectRatio; - double get aspectRatio => _aspectRatio; - void set aspectRatio (double value) { - assert(value != null); - if (_aspectRatio == value) - return; - _aspectRatio = value; - markNeedsLayout(); - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - return _applyAspectRatio(constraints).height; - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - return _applyAspectRatio(constraints).height; - } - - Size _applyAspectRatio(BoxConstraints constraints) { - double width = constraints.constrainWidth(); - double height = constraints.constrainHeight(width / _aspectRatio); - return new Size(width, height); - } - - bool get sizedByParent => true; - - void performResize() { - size = _applyAspectRatio(constraints); - } - - void performLayout() { - if (child != null) - child.layout(new BoxConstraints.tight(size)); - } - - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}aspectRatio: ${aspectRatio}\n'; -} - -class RenderShrinkWrapWidth extends RenderProxyBox { - - // This class will attempt to size its child to the child's maximum - // intrinsic width, snapped to a multiple of the stepWidth, if one - // is provided, and given the provided constraints; and will then - // adopt the child's resulting dimensions. - - // Note: laying out this class is relatively expensive. Avoid using - // it where possible. - - RenderShrinkWrapWidth({ - double stepWidth, - double stepHeight, - RenderBox child - }) : _stepWidth = stepWidth, _stepHeight = stepHeight, super(child); - - double _stepWidth; - double get stepWidth => _stepWidth; - void set stepWidth(double value) { - if (value == _stepWidth) - return; - _stepWidth = value; - markNeedsLayout(); - } - - double _stepHeight; - double get stepHeight => _stepHeight; - void set stepHeight(double value) { - if (value == _stepHeight) - return; - _stepHeight = value; - markNeedsLayout(); - } - - static double applyStep(double input, double step) { - if (step == null) - return input; - return (input / step).ceil() * step; - } - - BoxConstraints _getInnerConstraints(BoxConstraints constraints) { - if (constraints.hasTightWidth) - return constraints; - double width = child.getMaxIntrinsicWidth(constraints); - assert(width == constraints.constrainWidth(width)); - return constraints.applyWidth(applyStep(width, _stepWidth)); - } - - double getMinIntrinsicWidth(BoxConstraints constraints) { - return getMaxIntrinsicWidth(constraints); - } - - double getMaxIntrinsicWidth(BoxConstraints constraints) { - if (child == null) - return constraints.constrainWidth(0.0); - double childResult = child.getMaxIntrinsicWidth(constraints); - return constraints.constrainWidth(applyStep(childResult, _stepWidth)); - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - if (child == null) - return constraints.constrainWidth(0.0); - double childResult = child.getMinIntrinsicHeight(_getInnerConstraints(constraints)); - return constraints.constrainHeight(applyStep(childResult, _stepHeight)); - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - if (child == null) - return constraints.constrainWidth(0.0); - double childResult = child.getMaxIntrinsicHeight(_getInnerConstraints(constraints)); - return constraints.constrainHeight(applyStep(childResult, _stepHeight)); - } - - void performLayout() { - if (child != null) { - BoxConstraints childConstraints = _getInnerConstraints(constraints); - if (_stepHeight != null) - childConstraints.applyHeight(getMaxIntrinsicHeight(childConstraints)); - child.layout(childConstraints, parentUsesSize: true); - size = child.size; - } else { - performResize(); - } - } - - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}stepWidth: ${stepWidth}\n${prefix}stepHeight: ${stepHeight}\n'; - -} - -class RenderOpacity extends RenderProxyBox { - RenderOpacity({ RenderBox child, double opacity }) - : this._opacity = opacity, super(child) { - assert(opacity >= 0.0 && opacity <= 1.0); - } - - double _opacity; - double get opacity => _opacity; - void set opacity (double value) { - assert(value != null); - assert(value >= 0.0 && value <= 1.0); - if (_opacity == value) - return; - _opacity = value; - _cachedPaint = null; - markNeedsPaint(); - } - - int get _alpha => (_opacity * 255).round(); - - Paint _cachedPaint; - Paint get _paint { - if (_cachedPaint == null) { - _cachedPaint = new Paint() - ..color = new Color.fromARGB(_alpha, 0, 0, 0) - ..setTransferMode(sky.TransferMode.srcOver) - ..isAntiAlias = false; - } - return _cachedPaint; - } - - void paint(PaintingContext context, Offset offset) { - if (child != null) { - int a = _alpha; - - if (a == 0) - return; - - if (a == 255) { - context.paintChild(child, offset.toPoint()); - return; - } - - context.canvas.saveLayer(null, _paint); // TODO(abarth): layerize - context.paintChild(child, offset.toPoint()); - context.canvas.restore(); - } - } -} - -class RenderColorFilter extends RenderProxyBox { - RenderColorFilter({ RenderBox child, Color color, sky.TransferMode transferMode }) - : _color = color, _transferMode = transferMode, super(child) { - } - - Color _color; - Color get color => _color; - void set color (Color value) { - assert(value != null); - if (_color == value) - return; - _color = value; - _cachedPaint = null; - markNeedsPaint(); - } - - sky.TransferMode _transferMode; - sky.TransferMode get transferMode => _transferMode; - void set transferMode (sky.TransferMode value) { - assert(value != null); - if (_transferMode == value) - return; - _transferMode = value; - _cachedPaint = null; - markNeedsPaint(); - } - - Paint _cachedPaint; - Paint get _paint { - if (_cachedPaint == null) { - _cachedPaint = new Paint() - ..setColorFilter(new sky.ColorFilter.mode(_color, _transferMode)) - ..isAntiAlias = false; - } - return _cachedPaint; - } - - void paint(PaintingContext context, Offset offset) { - if (child != null) { - context.canvas.saveLayer(offset & size, _paint); // TODO(abarth): layerize - context.paintChild(child, offset.toPoint()); - context.canvas.restore(); - } - } -} - -class RenderClipRect extends RenderProxyBox { - RenderClipRect({ RenderBox child }) : super(child); - - void paint(PaintingContext context, Offset offset) { - if (child != null) - context.paintChildWithClip(child, offset.toPoint(), Offset.zero & size); - } -} - -class RenderClipRRect extends RenderProxyBox { - RenderClipRRect({ RenderBox child, double xRadius, double yRadius }) - : _xRadius = xRadius, _yRadius = yRadius, super(child) { - assert(_xRadius != null); - assert(_yRadius != null); - } - - double _xRadius; - double get xRadius => _xRadius; - void set xRadius (double value) { - assert(value != null); - if (_xRadius == value) - return; - _xRadius = value; - markNeedsPaint(); - } - - double _yRadius; - double get yRadius => _yRadius; - void set yRadius (double value) { - assert(value != null); - if (_yRadius == value) - return; - _yRadius = value; - markNeedsPaint(); - } - - final Paint _paint = new Paint()..isAntiAlias = false; - - void paint(PaintingContext context, Offset offset) { - if (child != null) { - Rect rect = offset & size; - context.canvas.saveLayer(rect, _paint); // TODO(abarth): layerize - sky.RRect rrect = new sky.RRect()..setRectXY(rect, xRadius, yRadius); - context.canvas.clipRRect(rrect); - context.paintChild(child, offset.toPoint()); - context.canvas.restore(); - } - } -} - -class RenderClipOval extends RenderProxyBox { - RenderClipOval({ RenderBox child }) : super(child); - - final Paint _paint = new Paint()..isAntiAlias = false; - - Rect _cachedRect; - Path _cachedPath; - - Path _getPath(Rect rect) { - if (rect != _cachedRect) { - _cachedRect = rect; - _cachedPath = new Path()..addOval(_cachedRect); - } - return _cachedPath; - } - - void paint(PaintingContext context, Offset offset) { - if (child != null) { - Rect rect = offset & size; - context.canvas.saveLayer(rect, _paint); // TODO(abarth): layerize - context.canvas.clipPath(_getPath(rect)); - context.paintChild(child, offset.toPoint()); - context.canvas.restore(); - } - } -} - -abstract class RenderShiftedBox extends RenderBox with RenderObjectWithChildMixin { - - // Abstract class for one-child-layout render boxes - - RenderShiftedBox(RenderBox child) { - this.child = child; - } - - double getMinIntrinsicWidth(BoxConstraints constraints) { - if (child != null) - return child.getMinIntrinsicWidth(constraints); - return super.getMinIntrinsicWidth(constraints); - } - - double getMaxIntrinsicWidth(BoxConstraints constraints) { - if (child != null) - return child.getMaxIntrinsicWidth(constraints); - return super.getMaxIntrinsicWidth(constraints); - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - if (child != null) - return child.getMinIntrinsicHeight(constraints); - return super.getMinIntrinsicHeight(constraints); - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - if (child != null) - return child.getMaxIntrinsicHeight(constraints); - return super.getMaxIntrinsicHeight(constraints); - } - - double computeDistanceToActualBaseline(TextBaseline baseline) { - double result; - if (child != null) { - assert(!needsLayout); - result = child.getDistanceToActualBaseline(baseline); - assert(child.parentData is BoxParentData); - if (result != null) - result += child.parentData.position.y; - } else { - result = super.computeDistanceToActualBaseline(baseline); - } - return result; - } - - void paint(PaintingContext context, Offset offset) { - if (child != null) - context.paintChild(child, child.parentData.position + offset); - } - - void hitTestChildren(HitTestResult result, { Point position }) { - if (child != null) { - assert(child.parentData is BoxParentData); - child.hitTest(result, position: new Point(position.x - child.parentData.position.x, - position.y - child.parentData.position.y)); - } - } - -} - -class RenderPadding extends RenderShiftedBox { - - RenderPadding({ EdgeDims padding, RenderBox child }) : super(child) { - assert(padding != null); - this.padding = padding; - } - - EdgeDims _padding; - EdgeDims get padding => _padding; - void set padding (EdgeDims value) { - assert(value != null); - if (_padding == value) - return; - _padding = value; - markNeedsLayout(); - } - - double getMinIntrinsicWidth(BoxConstraints constraints) { - double totalPadding = padding.left + padding.right; - if (child != null) - return child.getMinIntrinsicWidth(constraints.deflate(padding)) + totalPadding; - return constraints.constrainWidth(totalPadding); - } - - double getMaxIntrinsicWidth(BoxConstraints constraints) { - double totalPadding = padding.left + padding.right; - if (child != null) - return child.getMaxIntrinsicWidth(constraints.deflate(padding)) + totalPadding; - return constraints.constrainWidth(totalPadding); - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - double totalPadding = padding.top + padding.bottom; - if (child != null) - return child.getMinIntrinsicHeight(constraints.deflate(padding)) + totalPadding; - return constraints.constrainHeight(totalPadding); - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - double totalPadding = padding.top + padding.bottom; - if (child != null) - return child.getMaxIntrinsicHeight(constraints.deflate(padding)) + totalPadding; - return constraints.constrainHeight(totalPadding); - } - - void performLayout() { - assert(padding != null); - if (child == null) { - size = constraints.constrain(new Size( - padding.left + padding.right, - padding.top + padding.bottom - )); - return; - } - BoxConstraints innerConstraints = constraints.deflate(padding); - child.layout(innerConstraints, parentUsesSize: true); - assert(child.parentData is BoxParentData); - child.parentData.position = new Point(padding.left, padding.top); - size = constraints.constrain(new Size( - padding.left + child.size.width + padding.right, - padding.top + child.size.height + padding.bottom - )); - } - - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}padding: ${padding}\n'; -} - -class RenderPositionedBox extends RenderShiftedBox { - - // This box aligns a child box within itself. It's only useful for - // children that don't always size to fit their parent. For example, - // to align a box at the bottom right, you would pass this box a - // tight constraint that is bigger than the child's natural size, - // with horizontal and vertical set to 1.0. - - RenderPositionedBox({ - RenderBox child, - double horizontal: 0.5, - double vertical: 0.5 - }) : _horizontal = horizontal, - _vertical = vertical, - super(child) { - assert(horizontal != null); - assert(vertical != null); - } - - double _horizontal; - double get horizontal => _horizontal; - void set horizontal (double value) { - assert(value != null); - if (_horizontal == value) - return; - _horizontal = value; - markNeedsLayout(); - } - - double _vertical; - double get vertical => _vertical; - void set vertical (double value) { - assert(value != null); - if (_vertical == value) - return; - _vertical = value; - markNeedsLayout(); - } - - void performLayout() { - if (child != null) { - child.layout(constraints.loosen(), parentUsesSize: true); - size = constraints.constrain(child.size); - assert(child.parentData is BoxParentData); - Offset delta = size - child.size; - child.parentData.position = (delta.scale(horizontal, vertical)).toPoint(); - } else { - performResize(); - } - } - - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}horizontal: ${horizontal}\n${prefix}vertical: ${vertical}\n'; -} - -class RenderBaseline extends RenderShiftedBox { - - RenderBaseline({ - RenderBox child, - double baseline, - TextBaseline baselineType - }) : _baseline = baseline, - _baselineType = baselineType, - super(child) { - assert(baseline != null); - assert(baselineType != null); - } - - double _baseline; - double get baseline => _baseline; - void set baseline (double value) { - assert(value != null); - if (_baseline == value) - return; - _baseline = value; - markNeedsLayout(); - } - - TextBaseline _baselineType; - TextBaseline get baselineType => _baselineType; - void set baselineType (TextBaseline value) { - assert(value != null); - if (_baselineType == value) - return; - _baselineType = value; - markNeedsLayout(); - } - - void performLayout() { - if (child != null) { - child.layout(constraints.loosen(), parentUsesSize: true); - size = constraints.constrain(child.size); - assert(child.parentData is BoxParentData); - double delta = baseline - child.getDistanceToBaseline(baselineType); - child.parentData.position = new Point(0.0, delta); - } else { - performResize(); - } - } - - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}baseline: ${baseline}\nbaselineType: ${baselineType}'; -} - -enum ScrollDirection { horizontal, vertical, both } - -class RenderViewport extends RenderBox with RenderObjectWithChildMixin { - - RenderViewport({ - RenderBox child, - Offset scrollOffset, - ScrollDirection scrollDirection: ScrollDirection.vertical - }) : _scrollOffset = scrollOffset, - _scrollDirection = scrollDirection { - assert(_offsetIsSane(scrollOffset, scrollDirection)); - this.child = child; - } - - bool _offsetIsSane(Offset offset, ScrollDirection direction) { - switch (direction) { - case ScrollDirection.both: - return true; - case ScrollDirection.horizontal: - return offset.dy == 0.0; - case ScrollDirection.vertical: - return offset.dx == 0.0; - } - } - - Offset _scrollOffset; - Offset get scrollOffset => _scrollOffset; - void set scrollOffset(Offset value) { - if (value == _scrollOffset) - return; - assert(_offsetIsSane(value, scrollDirection)); - _scrollOffset = value; - markNeedsPaint(); - } - - ScrollDirection _scrollDirection; - ScrollDirection get scrollDirection => _scrollDirection; - void set scrollDirection(ScrollDirection value) { - if (value == _scrollDirection) - return; - assert(_offsetIsSane(scrollOffset, value)); - _scrollDirection = value; - markNeedsLayout(); - } - - BoxConstraints _getInnerConstraints(BoxConstraints constraints) { - BoxConstraints innerConstraints; - switch (scrollDirection) { - case ScrollDirection.both: - innerConstraints = new BoxConstraints(); - break; - case ScrollDirection.horizontal: - innerConstraints = constraints.heightConstraints(); - break; - case ScrollDirection.vertical: - innerConstraints = constraints.widthConstraints(); - break; - } - return innerConstraints; - } - - double getMinIntrinsicWidth(BoxConstraints constraints) { - if (child != null) - return child.getMinIntrinsicWidth(_getInnerConstraints(constraints)); - return super.getMinIntrinsicWidth(constraints); - } - - double getMaxIntrinsicWidth(BoxConstraints constraints) { - if (child != null) - return child.getMaxIntrinsicWidth(_getInnerConstraints(constraints)); - return super.getMaxIntrinsicWidth(constraints); - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - if (child != null) - return child.getMinIntrinsicHeight(_getInnerConstraints(constraints)); - return super.getMinIntrinsicHeight(constraints); - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - if (child != null) - return child.getMaxIntrinsicHeight(_getInnerConstraints(constraints)); - return super.getMaxIntrinsicHeight(constraints); - } - - // We don't override computeDistanceToActualBaseline(), because we - // want the default behaviour (returning null). Otherwise, as you - // scroll the RenderViewport, it would shift in its parent if the - // parent was baseline-aligned, which makes no sense. - - void performLayout() { - if (child != null) { - child.layout(_getInnerConstraints(constraints), parentUsesSize: true); - size = constraints.constrain(child.size); - assert(child.parentData is BoxParentData); - child.parentData.position = Point.origin; - } else { - performResize(); - } - } - - Offset get _scrollOffsetRoundedToIntegerDevicePixels { - double devicePixelRatio = sky.view.devicePixelRatio; - int dxInDevicePixels = (scrollOffset.dx * devicePixelRatio).round(); - int dyInDevicePixels = (scrollOffset.dy * devicePixelRatio).round(); - return new Offset(dxInDevicePixels / devicePixelRatio, - dyInDevicePixels / devicePixelRatio); - } - - void paint(PaintingContext context, Offset offset) { - if (child != null) { - Offset roundedScrollOffset = _scrollOffsetRoundedToIntegerDevicePixels; - bool _needsClip = offset < Offset.zero || - !(offset & size).contains(((offset - roundedScrollOffset) & child.size).bottomRight); - if (_needsClip) { - context.canvas.save(); - context.canvas.clipRect(offset & size); - } - context.paintChild(child, (offset - roundedScrollOffset).toPoint()); - if (_needsClip) - context.canvas.restore(); - } - } - - void applyPaintTransform(Matrix4 transform) { - super.applyPaintTransform(transform); - transform.translate(-scrollOffset.dx, -scrollOffset.dy); - } - - void hitTestChildren(HitTestResult result, { Point position }) { - if (child != null) { - assert(child.parentData is BoxParentData); - Point transformed = position + _scrollOffsetRoundedToIntegerDevicePixels; - child.hitTest(result, position: transformed); - } - } -} - -class RenderImage extends RenderBox { - RenderImage({ - sky.Image image, - double width, - double height, - sky.ColorFilter colorFilter, - fit: ImageFit.scaleDown, - repeat: ImageRepeat.noRepeat - }) : _image = image, - _width = width, - _height = height, - _colorFilter = colorFilter, - _fit = fit, - _repeat = repeat; - - sky.Image _image; - sky.Image get image => _image; - void set image (sky.Image value) { - if (value == _image) - return; - _image = value; - markNeedsPaint(); - if (_width == null || _height == null) - markNeedsLayout(); - } - - double _width; - double get width => _width; - void set width (double value) { - if (value == _width) - return; - _width = value; - markNeedsLayout(); - } - - double _height; - double get height => _height; - void set height (double value) { - if (value == _height) - return; - _height = value; - markNeedsLayout(); - } - - sky.ColorFilter _colorFilter; - sky.ColorFilter get colorFilter => _colorFilter; - void set colorFilter (sky.ColorFilter value) { - if (value == _colorFilter) - return; - _colorFilter = value; - markNeedsPaint(); - } - - ImageFit _fit; - ImageFit get fit => _fit; - void set fit (ImageFit value) { - if (value == _fit) - return; - _fit = value; - markNeedsPaint(); - } - - ImageRepeat _repeat; - ImageRepeat get repeat => _repeat; - void set repeat (ImageRepeat value) { - if (value == _repeat) - return; - _repeat = value; - markNeedsPaint(); - } - - Size _sizeForConstraints(BoxConstraints constraints) { - // Folds the given |width| and |height| into |cosntraints| so they can all - // be treated uniformly. - constraints = new BoxConstraints.tightFor( - width: _width, - height: _height - ).apply(constraints); - - if (constraints.isTight || _image == null) - return constraints.smallest; - - // This algorithm attempts to find a size for the RenderImage that fits in - // the given constraints and preserves the image's intrinisc aspect ratio. - // Its goals as follow: - // - // - The dimensions of the RenderImage fit within the constraints. - // - The aspect ratio of the RenderImage matches the instrinsic aspect - // ratio of the image. - // - The RenderImage's dimension are maximal subject to being smaller than - // the intrinsic size of the image. - - double width = _image.width.toDouble(); - double height = _image.height.toDouble(); - assert(width > 0.0); - assert(height > 0.0); - double aspectRatio = width / height; - - if (width > constraints.maxWidth) { - width = constraints.maxWidth; - height = width / aspectRatio; - } - - if (height > constraints.maxHeight) { - height = constraints.maxHeight; - width = height * aspectRatio; - } - - if (width < constraints.minWidth) { - width = constraints.minWidth; - height = width / aspectRatio; - } - - if (height < constraints.minHeight) { - height = constraints.minHeight; - width = height * aspectRatio; - } - - return constraints.constrain(new Size(width, height)); - } - - double getMinIntrinsicWidth(BoxConstraints constraints) { - if (_width == null && _height == null) - return constraints.constrainWidth(0.0); - return _sizeForConstraints(constraints).width; - } - - double getMaxIntrinsicWidth(BoxConstraints constraints) { - return _sizeForConstraints(constraints).width; - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - if (_width == null && _height == null) - return constraints.constrainHeight(0.0); - return _sizeForConstraints(constraints).height; - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - return _sizeForConstraints(constraints).height; - } - - void performLayout() { - size = _sizeForConstraints(constraints); - } - - void paint(PaintingContext context, Offset offset) { - if (_image == null) - return; - paintImage( - canvas: context.canvas, - rect: offset & size, - image: _image, - colorFilter: _colorFilter, - fit: _fit, - repeat: _repeat - ); - } - - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}width: ${width}\n${prefix}height: ${height}\n'; -} - -class RenderDecoratedBox extends RenderProxyBox { - - RenderDecoratedBox({ - BoxDecoration decoration, - RenderBox child - }) : _painter = new BoxPainter(decoration), super(child); - - final BoxPainter _painter; - - BoxDecoration get decoration => _painter.decoration; - void set decoration (BoxDecoration value) { - assert(value != null); - if (value == _painter.decoration) - return; - _removeBackgroundImageListenerIfNeeded(); - _painter.decoration = value; - _addBackgroundImageListenerIfNeeded(); - markNeedsPaint(); - } - - bool get _needsBackgroundImageListener { - return attached && - _painter.decoration != null && - _painter.decoration.backgroundImage != null; - } - - void _addBackgroundImageListenerIfNeeded() { - if (_needsBackgroundImageListener) - _painter.decoration.backgroundImage.addChangeListener(markNeedsPaint); - } - - void _removeBackgroundImageListenerIfNeeded() { - if (_needsBackgroundImageListener) - _painter.decoration.backgroundImage.removeChangeListener(markNeedsPaint); - } - - void attach() { - super.attach(); - _addBackgroundImageListenerIfNeeded(); - } - - void detach() { - _removeBackgroundImageListenerIfNeeded(); - super.detach(); - } - - void paint(PaintingContext context, Offset offset) { - assert(size.width != null); - assert(size.height != null); - _painter.paint(context.canvas, offset & size); - super.paint(context, offset); - } - - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}decoration:\n${_painter.decoration.toString(prefix + " ")}\n'; -} - -class RenderTransform extends RenderProxyBox { - RenderTransform({ - Matrix4 transform, - RenderBox child - }) : super(child) { - assert(transform != null); - this.transform = transform; - } - - Matrix4 _transform; - - void set transform(Matrix4 value) { - assert(value != null); - if (_transform == value) - return; - _transform = new Matrix4.copy(value); - markNeedsPaint(); - } - - void setIdentity() { - _transform.setIdentity(); - markNeedsPaint(); - } - - void rotateX(double radians) { - _transform.rotateX(radians); - markNeedsPaint(); - } - - void rotateY(double radians) { - _transform.rotateY(radians); - markNeedsPaint(); - } - - void rotateZ(double radians) { - _transform.rotateZ(radians); - markNeedsPaint(); - } - - void translate(x, [double y = 0.0, double z = 0.0]) { - _transform.translate(x, y, z); - markNeedsPaint(); - } - - void scale(x, [double y, double z]) { - _transform.scale(x, y, z); - markNeedsPaint(); - } - - bool hitTest(HitTestResult result, { Point position }) { - Matrix4 inverse = new Matrix4.zero(); - // TODO(abarth): Check the determinant for degeneracy. - inverse.copyInverse(_transform); - - Vector3 position3 = new Vector3(position.x, position.y, 0.0); - Vector3 transformed3 = inverse.transform3(position3); - Point transformed = new Point(transformed3.x, transformed3.y); - return super.hitTest(result, position: transformed); - } - - void paint(PaintingContext context, Offset offset) { - context.canvas.save(); - context.canvas.translate(offset.dx, offset.dy); - context.canvas.concat(_transform.storage); - super.paint(context, Offset.zero); - context.canvas.restore(); - } - - void applyPaintTransform(Matrix4 transform) { - super.applyPaintTransform(transform); - transform.multiply(_transform); - } - - String debugDescribeSettings(String prefix) { - List result = _transform.toString().split('\n').map((s) => '$prefix $s\n').toList(); - result.removeLast(); - return '${super.debugDescribeSettings(prefix)}${prefix}transform matrix:\n${result.join()}'; - } -} - -typedef void SizeChangedCallback(Size newSize); - -class RenderSizeObserver extends RenderProxyBox { - RenderSizeObserver({ - this.callback, - RenderBox child - }) : super(child) { - assert(callback != null); - } - - SizeChangedCallback callback; - - void performLayout() { - Size oldSize = size; - - super.performLayout(); - - if (oldSize != size) - callback(size); - } -} - -typedef void CustomPaintCallback(PaintingCanvas canvas, Size size); - -class RenderCustomPaint extends RenderProxyBox { - - RenderCustomPaint({ - CustomPaintCallback callback, - RenderBox child - }) : super(child) { - assert(callback != null); - _callback = callback; - } - - CustomPaintCallback _callback; - void set callback (CustomPaintCallback value) { - assert(value != null || !attached); - if (_callback == value) - return; - _callback = value; - markNeedsPaint(); - } - - void attach() { - assert(_callback != null); - super.attach(); - } - - void paint(PaintingContext context, Offset offset) { - assert(_callback != null); - context.canvas.translate(offset.dx, offset.dy); - _callback(context.canvas, size); - // TODO(abarth): We should translate back before calling super because in - // the future, super.paint might switch our compositing layer. - super.paint(context, Offset.zero); - context.canvas.translate(-offset.dx, -offset.dy); - } -} - -// RENDER VIEW LAYOUT MANAGER - -class ViewConstraints { - const ViewConstraints({ - this.size: Size.zero, - this.orientation - }); - final Size size; - final int orientation; -} - -class RenderView extends RenderObject with RenderObjectWithChildMixin { - RenderView({ - RenderBox child, - this.timeForRotation: const Duration(microseconds: 83333) - }) { - this.child = child; - } - - Size _size = Size.zero; - Size get size => _size; - - int _orientation; // 0..3 - int get orientation => _orientation; - Duration timeForRotation; - - ViewConstraints _rootConstraints; - ViewConstraints get rootConstraints => _rootConstraints; - void set rootConstraints(ViewConstraints value) { - if (_rootConstraints == value) - return; - _rootConstraints = value; - markNeedsLayout(); - } - - ContainerLayer _rootLayer; - - // We never call layout() on this class, so this should never get - // checked. (This class is laid out using scheduleInitialLayout().) - bool debugDoesMeetConstraints() { assert(false); return false; } - - void performResize() { - assert(false); - } - - void performLayout() { - if (_rootConstraints.orientation != _orientation) { - if (_orientation != null && child != null) - child.rotate(oldAngle: _orientation, newAngle: _rootConstraints.orientation, time: timeForRotation); - _orientation = _rootConstraints.orientation; - } - _size = _rootConstraints.size; - assert(!_size.isInfinite); - - if (child != null) - child.layout(new BoxConstraints.tight(_size)); - } - - void rotate({ int oldAngle, int newAngle, Duration time }) { - assert(false); // nobody tells the screen to rotate, the whole rotate() dance is started from our performResize() - } - - bool hitTest(HitTestResult result, { Point position }) { - if (child != null) { - Rect childBounds = Point.origin & child.size; - if (childBounds.contains(position)) - child.hitTest(result, position: position); - } - result.add(new HitTestEntry(this)); - return true; - } - - void paint(PaintingContext context, Offset offset) { - if (child != null) - context.paintChild(child, offset.toPoint()); - } - - void paintFrame() { - sky.tracing.begin('RenderView.paintFrame'); - try { - final double devicePixelRatio = sky.view.devicePixelRatio; - Matrix4 transform = new Matrix4.diagonal3Values(devicePixelRatio, devicePixelRatio, 1.0); - _rootLayer = new TransformLayer(transform: transform); - PaintingContext context = new PaintingContext(Offset.zero, size); - _rootLayer.add(context.layer); - context.paintChild(child, Point.origin); - context.endRecording(); - } finally { - sky.tracing.end('RenderView.paintFrame'); - } - } - - void compositeFrame() { - sky.tracing.begin('RenderView.compositeFrame'); - try { - sky.PictureRecorder recorder = new sky.PictureRecorder(); - sky.Canvas canvas = new sky.Canvas(recorder, Point.origin & (size * sky.view.devicePixelRatio)); - _rootLayer.paint(canvas); - sky.view.picture = recorder.endRecording(); - } finally { - sky.tracing.end('RenderView.compositeFrame'); - } - } - - Rect get paintBounds => Point.origin & size; -} - -class RenderIgnorePointer extends RenderProxyBox { - RenderIgnorePointer({ RenderBox child }) : super(child); - bool hitTest(HitTestResult result, { Point position }) { - return false; - } -} // HELPER METHODS FOR RENDERBOX CONTAINERS abstract class RenderBoxContainerDefaultsMixin> implements ContainerRenderObjectMixin { diff --git a/packages/flutter/lib/rendering/image.dart b/packages/flutter/lib/rendering/image.dart new file mode 100644 index 00000000000..0d1f5182fe8 --- /dev/null +++ b/packages/flutter/lib/rendering/image.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 'dart:sky' as sky; + +import 'package:sky/painting/box_painter.dart'; +import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/box.dart'; + +class RenderImage extends RenderBox { + RenderImage({ + sky.Image image, + double width, + double height, + sky.ColorFilter colorFilter, + fit: ImageFit.scaleDown, + repeat: ImageRepeat.noRepeat + }) : _image = image, + _width = width, + _height = height, + _colorFilter = colorFilter, + _fit = fit, + _repeat = repeat; + + sky.Image _image; + sky.Image get image => _image; + void set image (sky.Image value) { + if (value == _image) + return; + _image = value; + markNeedsPaint(); + if (_width == null || _height == null) + markNeedsLayout(); + } + + double _width; + double get width => _width; + void set width (double value) { + if (value == _width) + return; + _width = value; + markNeedsLayout(); + } + + double _height; + double get height => _height; + void set height (double value) { + if (value == _height) + return; + _height = value; + markNeedsLayout(); + } + + sky.ColorFilter _colorFilter; + sky.ColorFilter get colorFilter => _colorFilter; + void set colorFilter (sky.ColorFilter value) { + if (value == _colorFilter) + return; + _colorFilter = value; + markNeedsPaint(); + } + + ImageFit _fit; + ImageFit get fit => _fit; + void set fit (ImageFit value) { + if (value == _fit) + return; + _fit = value; + markNeedsPaint(); + } + + ImageRepeat _repeat; + ImageRepeat get repeat => _repeat; + void set repeat (ImageRepeat value) { + if (value == _repeat) + return; + _repeat = value; + markNeedsPaint(); + } + + Size _sizeForConstraints(BoxConstraints constraints) { + // Folds the given |width| and |height| into |cosntraints| so they can all + // be treated uniformly. + constraints = new BoxConstraints.tightFor( + width: _width, + height: _height + ).apply(constraints); + + if (constraints.isTight || _image == null) + return constraints.smallest; + + // This algorithm attempts to find a size for the RenderImage that fits in + // the given constraints and preserves the image's intrinisc aspect ratio. + // Its goals as follow: + // + // - The dimensions of the RenderImage fit within the constraints. + // - The aspect ratio of the RenderImage matches the instrinsic aspect + // ratio of the image. + // - The RenderImage's dimension are maximal subject to being smaller than + // the intrinsic size of the image. + + double width = _image.width.toDouble(); + double height = _image.height.toDouble(); + assert(width > 0.0); + assert(height > 0.0); + double aspectRatio = width / height; + + if (width > constraints.maxWidth) { + width = constraints.maxWidth; + height = width / aspectRatio; + } + + if (height > constraints.maxHeight) { + height = constraints.maxHeight; + width = height * aspectRatio; + } + + if (width < constraints.minWidth) { + width = constraints.minWidth; + height = width / aspectRatio; + } + + if (height < constraints.minHeight) { + height = constraints.minHeight; + width = height * aspectRatio; + } + + return constraints.constrain(new Size(width, height)); + } + + double getMinIntrinsicWidth(BoxConstraints constraints) { + if (_width == null && _height == null) + return constraints.constrainWidth(0.0); + return _sizeForConstraints(constraints).width; + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + return _sizeForConstraints(constraints).width; + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + if (_width == null && _height == null) + return constraints.constrainHeight(0.0); + return _sizeForConstraints(constraints).height; + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + return _sizeForConstraints(constraints).height; + } + + void performLayout() { + size = _sizeForConstraints(constraints); + } + + void paint(PaintingContext context, Offset offset) { + if (_image == null) + return; + paintImage( + canvas: context.canvas, + rect: offset & size, + image: _image, + colorFilter: _colorFilter, + fit: _fit, + repeat: _repeat + ); + } + + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}width: ${width}\n${prefix}height: ${height}\n'; +} diff --git a/packages/flutter/lib/rendering/proxy_box.dart b/packages/flutter/lib/rendering/proxy_box.dart new file mode 100644 index 00000000000..a3813d9a240 --- /dev/null +++ b/packages/flutter/lib/rendering/proxy_box.dart @@ -0,0 +1,637 @@ +// 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 'dart:sky' as sky; + +import 'package:sky/painting/box_painter.dart'; +import 'package:sky/painting/text_style.dart'; +import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/box.dart'; +import 'package:vector_math/vector_math.dart'; + +export 'package:sky/painting/box_painter.dart'; + +class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin { + + // ProxyBox assumes the child will be at 0,0 and will have the same size + + RenderProxyBox([RenderBox child = null]) { + this.child = child; + } + + double getMinIntrinsicWidth(BoxConstraints constraints) { + if (child != null) + return child.getMinIntrinsicWidth(constraints); + return super.getMinIntrinsicWidth(constraints); + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + if (child != null) + return child.getMaxIntrinsicWidth(constraints); + return super.getMaxIntrinsicWidth(constraints); + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + if (child != null) + return child.getMinIntrinsicHeight(constraints); + return super.getMinIntrinsicHeight(constraints); + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + if (child != null) + return child.getMaxIntrinsicHeight(constraints); + return super.getMaxIntrinsicHeight(constraints); + } + + double computeDistanceToActualBaseline(TextBaseline baseline) { + if (child != null) + return child.getDistanceToActualBaseline(baseline); + return super.computeDistanceToActualBaseline(baseline); + } + + void performLayout() { + if (child != null) { + child.layout(constraints, parentUsesSize: true); + size = child.size; + } else { + performResize(); + } + } + + 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()); + } +} + +class RenderConstrainedBox extends RenderProxyBox { + RenderConstrainedBox({ + RenderBox child, + BoxConstraints additionalConstraints + }) : super(child), _additionalConstraints = additionalConstraints { + assert(additionalConstraints != null); + } + + BoxConstraints _additionalConstraints; + BoxConstraints get additionalConstraints => _additionalConstraints; + void set additionalConstraints (BoxConstraints value) { + assert(value != null); + if (_additionalConstraints == value) + return; + _additionalConstraints = value; + markNeedsLayout(); + } + + double getMinIntrinsicWidth(BoxConstraints constraints) { + if (child != null) + return child.getMinIntrinsicWidth(_additionalConstraints.apply(constraints)); + return _additionalConstraints.apply(constraints).constrainWidth(0.0); + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + if (child != null) + return child.getMaxIntrinsicWidth(_additionalConstraints.apply(constraints)); + return _additionalConstraints.apply(constraints).constrainWidth(0.0); + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + if (child != null) + return child.getMinIntrinsicHeight(_additionalConstraints.apply(constraints)); + return _additionalConstraints.apply(constraints).constrainHeight(0.0); + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + if (child != null) + return child.getMaxIntrinsicHeight(_additionalConstraints.apply(constraints)); + return _additionalConstraints.apply(constraints).constrainHeight(0.0); + } + + void performLayout() { + if (child != null) { + child.layout(_additionalConstraints.apply(constraints), parentUsesSize: true); + size = child.size; + } else { + size = _additionalConstraints.apply(constraints).constrain(Size.zero); + } + } + + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}additionalConstraints: ${additionalConstraints}\n'; +} + +class RenderAspectRatio extends RenderProxyBox { + RenderAspectRatio({ + RenderBox child, + double aspectRatio + }) : super(child), _aspectRatio = aspectRatio { + assert(_aspectRatio != null); + } + + double _aspectRatio; + double get aspectRatio => _aspectRatio; + void set aspectRatio (double value) { + assert(value != null); + if (_aspectRatio == value) + return; + _aspectRatio = value; + markNeedsLayout(); + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + return _applyAspectRatio(constraints).height; + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + return _applyAspectRatio(constraints).height; + } + + Size _applyAspectRatio(BoxConstraints constraints) { + double width = constraints.constrainWidth(); + double height = constraints.constrainHeight(width / _aspectRatio); + return new Size(width, height); + } + + bool get sizedByParent => true; + + void performResize() { + size = _applyAspectRatio(constraints); + } + + void performLayout() { + if (child != null) + child.layout(new BoxConstraints.tight(size)); + } + + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}aspectRatio: ${aspectRatio}\n'; +} + +class RenderShrinkWrapWidth extends RenderProxyBox { + + // This class will attempt to size its child to the child's maximum + // intrinsic width, snapped to a multiple of the stepWidth, if one + // is provided, and given the provided constraints; and will then + // adopt the child's resulting dimensions. + + // Note: laying out this class is relatively expensive. Avoid using + // it where possible. + + RenderShrinkWrapWidth({ + double stepWidth, + double stepHeight, + RenderBox child + }) : _stepWidth = stepWidth, _stepHeight = stepHeight, super(child); + + double _stepWidth; + double get stepWidth => _stepWidth; + void set stepWidth(double value) { + if (value == _stepWidth) + return; + _stepWidth = value; + markNeedsLayout(); + } + + double _stepHeight; + double get stepHeight => _stepHeight; + void set stepHeight(double value) { + if (value == _stepHeight) + return; + _stepHeight = value; + markNeedsLayout(); + } + + static double applyStep(double input, double step) { + if (step == null) + return input; + return (input / step).ceil() * step; + } + + BoxConstraints _getInnerConstraints(BoxConstraints constraints) { + if (constraints.hasTightWidth) + return constraints; + double width = child.getMaxIntrinsicWidth(constraints); + assert(width == constraints.constrainWidth(width)); + return constraints.applyWidth(applyStep(width, _stepWidth)); + } + + double getMinIntrinsicWidth(BoxConstraints constraints) { + return getMaxIntrinsicWidth(constraints); + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + if (child == null) + return constraints.constrainWidth(0.0); + double childResult = child.getMaxIntrinsicWidth(constraints); + return constraints.constrainWidth(applyStep(childResult, _stepWidth)); + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + if (child == null) + return constraints.constrainWidth(0.0); + double childResult = child.getMinIntrinsicHeight(_getInnerConstraints(constraints)); + return constraints.constrainHeight(applyStep(childResult, _stepHeight)); + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + if (child == null) + return constraints.constrainWidth(0.0); + double childResult = child.getMaxIntrinsicHeight(_getInnerConstraints(constraints)); + return constraints.constrainHeight(applyStep(childResult, _stepHeight)); + } + + void performLayout() { + if (child != null) { + BoxConstraints childConstraints = _getInnerConstraints(constraints); + if (_stepHeight != null) + childConstraints.applyHeight(getMaxIntrinsicHeight(childConstraints)); + child.layout(childConstraints, parentUsesSize: true); + size = child.size; + } else { + performResize(); + } + } + + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}stepWidth: ${stepWidth}\n${prefix}stepHeight: ${stepHeight}\n'; + +} + +class RenderOpacity extends RenderProxyBox { + RenderOpacity({ RenderBox child, double opacity }) + : this._opacity = opacity, super(child) { + assert(opacity >= 0.0 && opacity <= 1.0); + } + + double _opacity; + double get opacity => _opacity; + void set opacity (double value) { + assert(value != null); + assert(value >= 0.0 && value <= 1.0); + if (_opacity == value) + return; + _opacity = value; + _cachedPaint = null; + markNeedsPaint(); + } + + int get _alpha => (_opacity * 255).round(); + + Paint _cachedPaint; + Paint get _paint { + if (_cachedPaint == null) { + _cachedPaint = new Paint() + ..color = new Color.fromARGB(_alpha, 0, 0, 0) + ..setTransferMode(sky.TransferMode.srcOver) + ..isAntiAlias = false; + } + return _cachedPaint; + } + + void paint(PaintingContext context, Offset offset) { + if (child != null) { + int a = _alpha; + + if (a == 0) + return; + + if (a == 255) { + context.paintChild(child, offset.toPoint()); + return; + } + + context.canvas.saveLayer(null, _paint); // TODO(abarth): layerize + context.paintChild(child, offset.toPoint()); + context.canvas.restore(); + } + } +} + +class RenderColorFilter extends RenderProxyBox { + RenderColorFilter({ RenderBox child, Color color, sky.TransferMode transferMode }) + : _color = color, _transferMode = transferMode, super(child) { + } + + Color _color; + Color get color => _color; + void set color (Color value) { + assert(value != null); + if (_color == value) + return; + _color = value; + _cachedPaint = null; + markNeedsPaint(); + } + + sky.TransferMode _transferMode; + sky.TransferMode get transferMode => _transferMode; + void set transferMode (sky.TransferMode value) { + assert(value != null); + if (_transferMode == value) + return; + _transferMode = value; + _cachedPaint = null; + markNeedsPaint(); + } + + Paint _cachedPaint; + Paint get _paint { + if (_cachedPaint == null) { + _cachedPaint = new Paint() + ..setColorFilter(new sky.ColorFilter.mode(_color, _transferMode)) + ..isAntiAlias = false; + } + return _cachedPaint; + } + + void paint(PaintingContext context, Offset offset) { + if (child != null) { + context.canvas.saveLayer(offset & size, _paint); // TODO(abarth): layerize + context.paintChild(child, offset.toPoint()); + context.canvas.restore(); + } + } +} + +class RenderClipRect extends RenderProxyBox { + RenderClipRect({ RenderBox child }) : super(child); + + void paint(PaintingContext context, Offset offset) { + if (child != null) + context.paintChildWithClip(child, offset.toPoint(), Offset.zero & size); + } +} + +class RenderClipRRect extends RenderProxyBox { + RenderClipRRect({ RenderBox child, double xRadius, double yRadius }) + : _xRadius = xRadius, _yRadius = yRadius, super(child) { + assert(_xRadius != null); + assert(_yRadius != null); + } + + double _xRadius; + double get xRadius => _xRadius; + void set xRadius (double value) { + assert(value != null); + if (_xRadius == value) + return; + _xRadius = value; + markNeedsPaint(); + } + + double _yRadius; + double get yRadius => _yRadius; + void set yRadius (double value) { + assert(value != null); + if (_yRadius == value) + return; + _yRadius = value; + markNeedsPaint(); + } + + final Paint _paint = new Paint()..isAntiAlias = false; + + void paint(PaintingContext context, Offset offset) { + if (child != null) { + Rect rect = offset & size; + context.canvas.saveLayer(rect, _paint); // TODO(abarth): layerize + sky.RRect rrect = new sky.RRect()..setRectXY(rect, xRadius, yRadius); + context.canvas.clipRRect(rrect); + context.paintChild(child, offset.toPoint()); + context.canvas.restore(); + } + } +} + +class RenderClipOval extends RenderProxyBox { + RenderClipOval({ RenderBox child }) : super(child); + + final Paint _paint = new Paint()..isAntiAlias = false; + + Rect _cachedRect; + Path _cachedPath; + + Path _getPath(Rect rect) { + if (rect != _cachedRect) { + _cachedRect = rect; + _cachedPath = new Path()..addOval(_cachedRect); + } + return _cachedPath; + } + + void paint(PaintingContext context, Offset offset) { + if (child != null) { + Rect rect = offset & size; + context.canvas.saveLayer(rect, _paint); // TODO(abarth): layerize + context.canvas.clipPath(_getPath(rect)); + context.paintChild(child, offset.toPoint()); + context.canvas.restore(); + } + } +} + +class RenderDecoratedBox extends RenderProxyBox { + + RenderDecoratedBox({ + BoxDecoration decoration, + RenderBox child + }) : _painter = new BoxPainter(decoration), super(child); + + final BoxPainter _painter; + + BoxDecoration get decoration => _painter.decoration; + void set decoration (BoxDecoration value) { + assert(value != null); + if (value == _painter.decoration) + return; + _removeBackgroundImageListenerIfNeeded(); + _painter.decoration = value; + _addBackgroundImageListenerIfNeeded(); + markNeedsPaint(); + } + + bool get _needsBackgroundImageListener { + return attached && + _painter.decoration != null && + _painter.decoration.backgroundImage != null; + } + + void _addBackgroundImageListenerIfNeeded() { + if (_needsBackgroundImageListener) + _painter.decoration.backgroundImage.addChangeListener(markNeedsPaint); + } + + void _removeBackgroundImageListenerIfNeeded() { + if (_needsBackgroundImageListener) + _painter.decoration.backgroundImage.removeChangeListener(markNeedsPaint); + } + + void attach() { + super.attach(); + _addBackgroundImageListenerIfNeeded(); + } + + void detach() { + _removeBackgroundImageListenerIfNeeded(); + super.detach(); + } + + void paint(PaintingContext context, Offset offset) { + assert(size.width != null); + assert(size.height != null); + _painter.paint(context.canvas, offset & size); + super.paint(context, offset); + } + + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}decoration:\n${_painter.decoration.toString(prefix + " ")}\n'; +} + +class RenderTransform extends RenderProxyBox { + RenderTransform({ + Matrix4 transform, + RenderBox child + }) : super(child) { + assert(transform != null); + this.transform = transform; + } + + Matrix4 _transform; + + void set transform(Matrix4 value) { + assert(value != null); + if (_transform == value) + return; + _transform = new Matrix4.copy(value); + markNeedsPaint(); + } + + void setIdentity() { + _transform.setIdentity(); + markNeedsPaint(); + } + + void rotateX(double radians) { + _transform.rotateX(radians); + markNeedsPaint(); + } + + void rotateY(double radians) { + _transform.rotateY(radians); + markNeedsPaint(); + } + + void rotateZ(double radians) { + _transform.rotateZ(radians); + markNeedsPaint(); + } + + void translate(x, [double y = 0.0, double z = 0.0]) { + _transform.translate(x, y, z); + markNeedsPaint(); + } + + void scale(x, [double y, double z]) { + _transform.scale(x, y, z); + markNeedsPaint(); + } + + bool hitTest(HitTestResult result, { Point position }) { + Matrix4 inverse = new Matrix4.zero(); + // TODO(abarth): Check the determinant for degeneracy. + inverse.copyInverse(_transform); + + Vector3 position3 = new Vector3(position.x, position.y, 0.0); + Vector3 transformed3 = inverse.transform3(position3); + Point transformed = new Point(transformed3.x, transformed3.y); + return super.hitTest(result, position: transformed); + } + + void paint(PaintingContext context, Offset offset) { + context.canvas.save(); + context.canvas.translate(offset.dx, offset.dy); + context.canvas.concat(_transform.storage); + super.paint(context, Offset.zero); + context.canvas.restore(); + } + + void applyPaintTransform(Matrix4 transform) { + super.applyPaintTransform(transform); + transform.multiply(_transform); + } + + String debugDescribeSettings(String prefix) { + List result = _transform.toString().split('\n').map((s) => '$prefix $s\n').toList(); + result.removeLast(); + return '${super.debugDescribeSettings(prefix)}${prefix}transform matrix:\n${result.join()}'; + } +} + +typedef void SizeChangedCallback(Size newSize); + +class RenderSizeObserver extends RenderProxyBox { + RenderSizeObserver({ + this.callback, + RenderBox child + }) : super(child) { + assert(callback != null); + } + + SizeChangedCallback callback; + + void performLayout() { + Size oldSize = size; + + super.performLayout(); + + if (oldSize != size) + callback(size); + } +} + +typedef void CustomPaintCallback(PaintingCanvas canvas, Size size); + +class RenderCustomPaint extends RenderProxyBox { + + RenderCustomPaint({ + CustomPaintCallback callback, + RenderBox child + }) : super(child) { + assert(callback != null); + _callback = callback; + } + + CustomPaintCallback _callback; + void set callback (CustomPaintCallback value) { + assert(value != null || !attached); + if (_callback == value) + return; + _callback = value; + markNeedsPaint(); + } + + void attach() { + assert(_callback != null); + super.attach(); + } + + void paint(PaintingContext context, Offset offset) { + assert(_callback != null); + context.canvas.translate(offset.dx, offset.dy); + _callback(context.canvas, size); + // TODO(abarth): We should translate back before calling super because in + // the future, super.paint might switch our compositing layer. + super.paint(context, Offset.zero); + context.canvas.translate(-offset.dx, -offset.dy); + } +} + +class RenderIgnorePointer extends RenderProxyBox { + RenderIgnorePointer({ RenderBox child }) : super(child); + bool hitTest(HitTestResult result, { Point position }) { + return false; + } +} diff --git a/packages/flutter/lib/rendering/shifted_box.dart b/packages/flutter/lib/rendering/shifted_box.dart new file mode 100644 index 00000000000..22d8f4b4f1c --- /dev/null +++ b/packages/flutter/lib/rendering/shifted_box.dart @@ -0,0 +1,238 @@ +// 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 'package:sky/painting/box_painter.dart'; +import 'package:sky/painting/text_style.dart'; +import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/box.dart'; + +abstract class RenderShiftedBox extends RenderBox with RenderObjectWithChildMixin { + + // Abstract class for one-child-layout render boxes + + RenderShiftedBox(RenderBox child) { + this.child = child; + } + + double getMinIntrinsicWidth(BoxConstraints constraints) { + if (child != null) + return child.getMinIntrinsicWidth(constraints); + return super.getMinIntrinsicWidth(constraints); + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + if (child != null) + return child.getMaxIntrinsicWidth(constraints); + return super.getMaxIntrinsicWidth(constraints); + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + if (child != null) + return child.getMinIntrinsicHeight(constraints); + return super.getMinIntrinsicHeight(constraints); + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + if (child != null) + return child.getMaxIntrinsicHeight(constraints); + return super.getMaxIntrinsicHeight(constraints); + } + + double computeDistanceToActualBaseline(TextBaseline baseline) { + double result; + if (child != null) { + assert(!needsLayout); + result = child.getDistanceToActualBaseline(baseline); + assert(child.parentData is BoxParentData); + if (result != null) + result += child.parentData.position.y; + } else { + result = super.computeDistanceToActualBaseline(baseline); + } + return result; + } + + void paint(PaintingContext context, Offset offset) { + if (child != null) + context.paintChild(child, child.parentData.position + offset); + } + + void hitTestChildren(HitTestResult result, { Point position }) { + if (child != null) { + assert(child.parentData is BoxParentData); + child.hitTest(result, position: new Point(position.x - child.parentData.position.x, + position.y - child.parentData.position.y)); + } + } + +} + +class RenderPadding extends RenderShiftedBox { + + RenderPadding({ EdgeDims padding, RenderBox child }) : super(child) { + assert(padding != null); + this.padding = padding; + } + + EdgeDims _padding; + EdgeDims get padding => _padding; + void set padding (EdgeDims value) { + assert(value != null); + if (_padding == value) + return; + _padding = value; + markNeedsLayout(); + } + + double getMinIntrinsicWidth(BoxConstraints constraints) { + double totalPadding = padding.left + padding.right; + if (child != null) + return child.getMinIntrinsicWidth(constraints.deflate(padding)) + totalPadding; + return constraints.constrainWidth(totalPadding); + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + double totalPadding = padding.left + padding.right; + if (child != null) + return child.getMaxIntrinsicWidth(constraints.deflate(padding)) + totalPadding; + return constraints.constrainWidth(totalPadding); + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + double totalPadding = padding.top + padding.bottom; + if (child != null) + return child.getMinIntrinsicHeight(constraints.deflate(padding)) + totalPadding; + return constraints.constrainHeight(totalPadding); + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + double totalPadding = padding.top + padding.bottom; + if (child != null) + return child.getMaxIntrinsicHeight(constraints.deflate(padding)) + totalPadding; + return constraints.constrainHeight(totalPadding); + } + + void performLayout() { + assert(padding != null); + if (child == null) { + size = constraints.constrain(new Size( + padding.left + padding.right, + padding.top + padding.bottom + )); + return; + } + BoxConstraints innerConstraints = constraints.deflate(padding); + child.layout(innerConstraints, parentUsesSize: true); + assert(child.parentData is BoxParentData); + child.parentData.position = new Point(padding.left, padding.top); + size = constraints.constrain(new Size( + padding.left + child.size.width + padding.right, + padding.top + child.size.height + padding.bottom + )); + } + + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}padding: ${padding}\n'; +} + +class RenderPositionedBox extends RenderShiftedBox { + + // This box aligns a child box within itself. It's only useful for + // children that don't always size to fit their parent. For example, + // to align a box at the bottom right, you would pass this box a + // tight constraint that is bigger than the child's natural size, + // with horizontal and vertical set to 1.0. + + RenderPositionedBox({ + RenderBox child, + double horizontal: 0.5, + double vertical: 0.5 + }) : _horizontal = horizontal, + _vertical = vertical, + super(child) { + assert(horizontal != null); + assert(vertical != null); + } + + double _horizontal; + double get horizontal => _horizontal; + void set horizontal (double value) { + assert(value != null); + if (_horizontal == value) + return; + _horizontal = value; + markNeedsLayout(); + } + + double _vertical; + double get vertical => _vertical; + void set vertical (double value) { + assert(value != null); + if (_vertical == value) + return; + _vertical = value; + markNeedsLayout(); + } + + void performLayout() { + if (child != null) { + child.layout(constraints.loosen(), parentUsesSize: true); + size = constraints.constrain(child.size); + assert(child.parentData is BoxParentData); + Offset delta = size - child.size; + child.parentData.position = (delta.scale(horizontal, vertical)).toPoint(); + } else { + performResize(); + } + } + + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}horizontal: ${horizontal}\n${prefix}vertical: ${vertical}\n'; +} + +class RenderBaseline extends RenderShiftedBox { + + RenderBaseline({ + RenderBox child, + double baseline, + TextBaseline baselineType + }) : _baseline = baseline, + _baselineType = baselineType, + super(child) { + assert(baseline != null); + assert(baselineType != null); + } + + double _baseline; + double get baseline => _baseline; + void set baseline (double value) { + assert(value != null); + if (_baseline == value) + return; + _baseline = value; + markNeedsLayout(); + } + + TextBaseline _baselineType; + TextBaseline get baselineType => _baselineType; + void set baselineType (TextBaseline value) { + assert(value != null); + if (_baselineType == value) + return; + _baselineType = value; + markNeedsLayout(); + } + + void performLayout() { + if (child != null) { + child.layout(constraints.loosen(), parentUsesSize: true); + size = constraints.constrain(child.size); + assert(child.parentData is BoxParentData); + double delta = baseline - child.getDistanceToBaseline(baselineType); + child.parentData.position = new Point(0.0, delta); + } else { + performResize(); + } + } + + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}baseline: ${baseline}\nbaselineType: ${baselineType}'; +} diff --git a/packages/flutter/lib/rendering/sky_binding.dart b/packages/flutter/lib/rendering/sky_binding.dart index d77e5a3a664..6ad3b1e24f2 100644 --- a/packages/flutter/lib/rendering/sky_binding.dart +++ b/packages/flutter/lib/rendering/sky_binding.dart @@ -4,10 +4,11 @@ import 'dart:sky' as sky; -import 'package:sky/base/scheduler.dart' as scheduler; import 'package:sky/base/hit_test.dart'; +import 'package:sky/base/scheduler.dart' as scheduler; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/view.dart'; int _hammingWeight(int value) { if (value == 0) diff --git a/packages/flutter/lib/rendering/toggleable.dart b/packages/flutter/lib/rendering/toggleable.dart index 24450a90bfd..796f49cca75 100644 --- a/packages/flutter/lib/rendering/toggleable.dart +++ b/packages/flutter/lib/rendering/toggleable.dart @@ -9,6 +9,7 @@ import 'package:sky/animation/animation_performance.dart'; import 'package:sky/animation/curves.dart'; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/proxy_box.dart'; typedef void ValueChanged(bool value); diff --git a/packages/flutter/lib/rendering/view.dart b/packages/flutter/lib/rendering/view.dart new file mode 100644 index 00000000000..407a0638925 --- /dev/null +++ b/packages/flutter/lib/rendering/view.dart @@ -0,0 +1,115 @@ +// 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 'dart:sky' as sky; + +import 'package:sky/rendering/layer.dart'; +import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/box.dart'; +import 'package:vector_math/vector_math.dart'; + +class ViewConstraints { + const ViewConstraints({ + this.size: Size.zero, + this.orientation + }); + final Size size; + final int orientation; +} + +class RenderView extends RenderObject with RenderObjectWithChildMixin { + RenderView({ + RenderBox child, + this.timeForRotation: const Duration(microseconds: 83333) + }) { + this.child = child; + } + + Size _size = Size.zero; + Size get size => _size; + + int _orientation; // 0..3 + int get orientation => _orientation; + Duration timeForRotation; + + ViewConstraints _rootConstraints; + ViewConstraints get rootConstraints => _rootConstraints; + void set rootConstraints(ViewConstraints value) { + if (_rootConstraints == value) + return; + _rootConstraints = value; + markNeedsLayout(); + } + + ContainerLayer _rootLayer; + + // We never call layout() on this class, so this should never get + // checked. (This class is laid out using scheduleInitialLayout().) + bool debugDoesMeetConstraints() { assert(false); return false; } + + void performResize() { + assert(false); + } + + void performLayout() { + if (_rootConstraints.orientation != _orientation) { + if (_orientation != null && child != null) + child.rotate(oldAngle: _orientation, newAngle: _rootConstraints.orientation, time: timeForRotation); + _orientation = _rootConstraints.orientation; + } + _size = _rootConstraints.size; + assert(!_size.isInfinite); + + if (child != null) + child.layout(new BoxConstraints.tight(_size)); + } + + void rotate({ int oldAngle, int newAngle, Duration time }) { + assert(false); // nobody tells the screen to rotate, the whole rotate() dance is started from our performResize() + } + + bool hitTest(HitTestResult result, { Point position }) { + if (child != null) { + Rect childBounds = Point.origin & child.size; + if (childBounds.contains(position)) + child.hitTest(result, position: position); + } + result.add(new HitTestEntry(this)); + return true; + } + + void paint(PaintingContext context, Offset offset) { + if (child != null) + context.paintChild(child, offset.toPoint()); + } + + void paintFrame() { + sky.tracing.begin('RenderView.paintFrame'); + try { + final double devicePixelRatio = sky.view.devicePixelRatio; + Matrix4 transform = new Matrix4.diagonal3Values(devicePixelRatio, devicePixelRatio, 1.0); + _rootLayer = new TransformLayer(transform: transform); + PaintingContext context = new PaintingContext(Offset.zero, size); + _rootLayer.add(context.layer); + context.paintChild(child, Point.origin); + context.endRecording(); + } finally { + sky.tracing.end('RenderView.paintFrame'); + } + } + + void compositeFrame() { + sky.tracing.begin('RenderView.compositeFrame'); + try { + sky.PictureRecorder recorder = new sky.PictureRecorder(); + sky.Canvas canvas = new sky.Canvas(recorder, Point.origin & (size * sky.view.devicePixelRatio)); + _rootLayer.paint(canvas); + sky.view.picture = recorder.endRecording(); + } finally { + sky.tracing.end('RenderView.compositeFrame'); + } + } + + Rect get paintBounds => Point.origin & size; +} diff --git a/packages/flutter/lib/rendering/viewport.dart b/packages/flutter/lib/rendering/viewport.dart new file mode 100644 index 00000000000..9a182a29be8 --- /dev/null +++ b/packages/flutter/lib/rendering/viewport.dart @@ -0,0 +1,147 @@ +// 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 'dart:sky' as sky; + +import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/box.dart'; +import 'package:vector_math/vector_math.dart'; + +enum ScrollDirection { horizontal, vertical, both } + +class RenderViewport extends RenderBox with RenderObjectWithChildMixin { + + RenderViewport({ + RenderBox child, + Offset scrollOffset, + ScrollDirection scrollDirection: ScrollDirection.vertical + }) : _scrollOffset = scrollOffset, + _scrollDirection = scrollDirection { + assert(_offsetIsSane(scrollOffset, scrollDirection)); + this.child = child; + } + + bool _offsetIsSane(Offset offset, ScrollDirection direction) { + switch (direction) { + case ScrollDirection.both: + return true; + case ScrollDirection.horizontal: + return offset.dy == 0.0; + case ScrollDirection.vertical: + return offset.dx == 0.0; + } + } + + Offset _scrollOffset; + Offset get scrollOffset => _scrollOffset; + void set scrollOffset(Offset value) { + if (value == _scrollOffset) + return; + assert(_offsetIsSane(value, scrollDirection)); + _scrollOffset = value; + markNeedsPaint(); + } + + ScrollDirection _scrollDirection; + ScrollDirection get scrollDirection => _scrollDirection; + void set scrollDirection(ScrollDirection value) { + if (value == _scrollDirection) + return; + assert(_offsetIsSane(scrollOffset, value)); + _scrollDirection = value; + markNeedsLayout(); + } + + BoxConstraints _getInnerConstraints(BoxConstraints constraints) { + BoxConstraints innerConstraints; + switch (scrollDirection) { + case ScrollDirection.both: + innerConstraints = new BoxConstraints(); + break; + case ScrollDirection.horizontal: + innerConstraints = constraints.heightConstraints(); + break; + case ScrollDirection.vertical: + innerConstraints = constraints.widthConstraints(); + break; + } + return innerConstraints; + } + + double getMinIntrinsicWidth(BoxConstraints constraints) { + if (child != null) + return child.getMinIntrinsicWidth(_getInnerConstraints(constraints)); + return super.getMinIntrinsicWidth(constraints); + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + if (child != null) + return child.getMaxIntrinsicWidth(_getInnerConstraints(constraints)); + return super.getMaxIntrinsicWidth(constraints); + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + if (child != null) + return child.getMinIntrinsicHeight(_getInnerConstraints(constraints)); + return super.getMinIntrinsicHeight(constraints); + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + if (child != null) + return child.getMaxIntrinsicHeight(_getInnerConstraints(constraints)); + return super.getMaxIntrinsicHeight(constraints); + } + + // We don't override computeDistanceToActualBaseline(), because we + // want the default behaviour (returning null). Otherwise, as you + // scroll the RenderViewport, it would shift in its parent if the + // parent was baseline-aligned, which makes no sense. + + void performLayout() { + if (child != null) { + child.layout(_getInnerConstraints(constraints), parentUsesSize: true); + size = constraints.constrain(child.size); + assert(child.parentData is BoxParentData); + child.parentData.position = Point.origin; + } else { + performResize(); + } + } + + Offset get _scrollOffsetRoundedToIntegerDevicePixels { + double devicePixelRatio = sky.view.devicePixelRatio; + int dxInDevicePixels = (scrollOffset.dx * devicePixelRatio).round(); + int dyInDevicePixels = (scrollOffset.dy * devicePixelRatio).round(); + return new Offset(dxInDevicePixels / devicePixelRatio, + dyInDevicePixels / devicePixelRatio); + } + + void paint(PaintingContext context, Offset offset) { + if (child != null) { + Offset roundedScrollOffset = _scrollOffsetRoundedToIntegerDevicePixels; + bool _needsClip = offset < Offset.zero || + !(offset & size).contains(((offset - roundedScrollOffset) & child.size).bottomRight); + if (_needsClip) { + context.canvas.save(); + context.canvas.clipRect(offset & size); + } + context.paintChild(child, (offset - roundedScrollOffset).toPoint()); + if (_needsClip) + context.canvas.restore(); + } + } + + void applyPaintTransform(Matrix4 transform) { + super.applyPaintTransform(transform); + transform.translate(-scrollOffset.dx, -scrollOffset.dy); + } + + void hitTestChildren(HitTestResult result, { Point position }) { + if (child != null) { + assert(child.parentData is BoxParentData); + Point transformed = position + _scrollOffsetRoundedToIntegerDevicePixels; + child.hitTest(result, position: transformed); + } + } +} diff --git a/packages/flutter/lib/widgets/basic.dart b/packages/flutter/lib/widgets/basic.dart index 6c78f8881ab..5c6cdca20aa 100644 --- a/packages/flutter/lib/widgets/basic.dart +++ b/packages/flutter/lib/widgets/basic.dart @@ -14,18 +14,24 @@ import 'package:sky/painting/paragraph_painter.dart'; import 'package:sky/rendering/block.dart'; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/flex.dart'; +import 'package:sky/rendering/image.dart'; import 'package:sky/rendering/object.dart'; import 'package:sky/rendering/paragraph.dart'; +import 'package:sky/rendering/proxy_box.dart'; +import 'package:sky/rendering/shifted_box.dart'; import 'package:sky/rendering/stack.dart'; +import 'package:sky/rendering/viewport.dart'; import 'package:sky/widgets/default_text_style.dart'; import 'package:sky/widgets/framework.dart'; export 'package:sky/base/hit_test.dart' show EventDisposition, combineEventDispositions; export 'package:sky/rendering/block.dart' show BlockDirection; -export 'package:sky/rendering/box.dart' show BackgroundImage, BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims, ScrollDirection; +export 'package:sky/rendering/box.dart' show BoxConstraints; export 'package:sky/rendering/flex.dart' show FlexDirection, FlexJustifyContent, FlexAlignItems; export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path; +export 'package:sky/rendering/proxy_box.dart' show BackgroundImage, BoxDecoration, BoxShadow, Border, BorderSide, EdgeDims; export 'package:sky/rendering/toggleable.dart' show ValueChanged; +export 'package:sky/rendering/viewport.dart' show ScrollDirection; export 'package:sky/widgets/framework.dart' show Key, GlobalKey, Widget, Component, StatefulComponent, App, runApp, Listener, ParentDataNode; // PAINTING NODES diff --git a/packages/flutter/lib/widgets/framework.dart b/packages/flutter/lib/widgets/framework.dart index 8b15043c319..eac66d4e284 100644 --- a/packages/flutter/lib/widgets/framework.dart +++ b/packages/flutter/lib/widgets/framework.dart @@ -12,6 +12,7 @@ import 'package:sky/mojo/activity.dart' as activity; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/object.dart'; import 'package:sky/rendering/sky_binding.dart'; +import 'package:sky/rendering/view.dart'; export 'package:sky/base/hit_test.dart' show EventDisposition, combineEventDispositions; export 'package:sky/rendering/box.dart' show BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims; diff --git a/packages/flutter/lib/widgets/icon_button.dart b/packages/flutter/lib/widgets/icon_button.dart index e88f2b46fe6..fc54846d6ae 100644 --- a/packages/flutter/lib/widgets/icon_button.dart +++ b/packages/flutter/lib/widgets/icon_button.dart @@ -4,7 +4,6 @@ import 'dart:sky' as sky; -import 'package:sky/rendering/box.dart'; import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/icon.dart'; import 'package:sky/widgets/framework.dart'; diff --git a/packages/flutter/lib/widgets/ink_well.dart b/packages/flutter/lib/widgets/ink_well.dart index 6d096ae663a..6e2e22e223e 100644 --- a/packages/flutter/lib/widgets/ink_well.dart +++ b/packages/flutter/lib/widgets/ink_well.dart @@ -10,6 +10,7 @@ import 'package:sky/animation/animation_performance.dart'; import 'package:sky/animation/curves.dart'; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/proxy_box.dart'; import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/framework.dart'; diff --git a/packages/flutter/lib/widgets/scrollable.dart b/packages/flutter/lib/widgets/scrollable.dart index 596b7bb7744..1b52d4d7079 100644 --- a/packages/flutter/lib/widgets/scrollable.dart +++ b/packages/flutter/lib/widgets/scrollable.dart @@ -10,6 +10,7 @@ import 'package:sky/animation/animated_simulation.dart'; import 'package:sky/animation/animation_performance.dart'; import 'package:sky/animation/scroll_behavior.dart'; import 'package:sky/rendering/box.dart'; +import 'package:sky/rendering/viewport.dart'; import 'package:sky/theme/view_configuration.dart' as config; import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/block_viewport.dart'; diff --git a/packages/flutter/lib/widgets/tabs.dart b/packages/flutter/lib/widgets/tabs.dart index 63a469c4295..8aa3f8e4044 100644 --- a/packages/flutter/lib/widgets/tabs.dart +++ b/packages/flutter/lib/widgets/tabs.dart @@ -13,6 +13,7 @@ import 'package:sky/animation/scroll_behavior.dart'; import 'package:sky/painting/text_style.dart'; import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/object.dart'; +import 'package:sky/rendering/viewport.dart'; import 'package:sky/theme/colors.dart' as colors; import 'package:sky/theme/typography.dart' as typography; import 'package:sky/widgets/basic.dart';