From 50bfdedbf46b34b96a95ee69bd03eb668aed5fed Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 18 Aug 2015 20:55:32 -0700 Subject: [PATCH] Split box.dart into many files Sadly, box.dart has grown much longer than 1000 lines. This patch splits it up into several files based on the class hierarchy. Fortunately, many of these classes are loosely coupled to each other. --- examples/rendering/align_items.dart | 2 + examples/rendering/baseline.dart | 2 + examples/rendering/borders.dart | 2 + examples/rendering/flex.dart | 2 + examples/rendering/interactive_flex.dart | 4 +- examples/rendering/justify_content.dart | 3 +- examples/rendering/render_paragraph.dart | 2 +- examples/rendering/sector_layout.dart | 1 + examples/rendering/shadowed_box.dart | 3 +- examples/rendering/simple_autolayout.dart | 9 +- examples/rendering/solid_color_box.dart | 4 +- examples/rendering/spinning_flex.dart | 3 +- examples/rendering/transform.dart | 1 + examples/stocks/lib/main.dart | 1 - examples/widgets/container.dart | 1 - examples/widgets/ensure_visible.dart | 1 - examples/widgets/piano.dart | 1 - examples/widgets/spinning_mixed.dart | 5 +- packages/flutter/lib/rendering/box.dart | 1261 ----------------- packages/flutter/lib/rendering/image.dart | 170 +++ packages/flutter/lib/rendering/proxy_box.dart | 637 +++++++++ .../flutter/lib/rendering/shifted_box.dart | 238 ++++ .../flutter/lib/rendering/sky_binding.dart | 3 +- .../flutter/lib/rendering/toggleable.dart | 1 + packages/flutter/lib/rendering/view.dart | 115 ++ packages/flutter/lib/rendering/viewport.dart | 147 ++ packages/flutter/lib/widgets/basic.dart | 8 +- packages/flutter/lib/widgets/framework.dart | 1 + packages/flutter/lib/widgets/icon_button.dart | 1 - packages/flutter/lib/widgets/ink_well.dart | 1 + packages/flutter/lib/widgets/scrollable.dart | 1 + packages/flutter/lib/widgets/tabs.dart | 1 + 32 files changed, 1352 insertions(+), 1280 deletions(-) create mode 100644 packages/flutter/lib/rendering/image.dart create mode 100644 packages/flutter/lib/rendering/proxy_box.dart create mode 100644 packages/flutter/lib/rendering/shifted_box.dart create mode 100644 packages/flutter/lib/rendering/view.dart create mode 100644 packages/flutter/lib/rendering/viewport.dart diff --git a/examples/rendering/align_items.dart b/examples/rendering/align_items.dart index 817857d8da..22145ccade 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 3d61016872..568e48a460 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 c8f92cfb4a..3b10849c9b 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 55b2460040..e44667522e 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 462ea0be2b..2a097d7849 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 df3e1865b8..e3f0d37b79 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 58e390b3d2..5eda2c504f 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 3d6375b611..6141a13b5b 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 702c5f7cee..305bcea23a 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 23817fc9cf..18e074271c 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 edca5473ee..ad6097eb65 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 a8f9d9c573..7ee02e85c4 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 bbb7b90316..fd636b4dc1 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 9111a7f85a..9b312ba8bf 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 836d731fa4..ea30b43c04 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 8fad5d7513..280ec4822d 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 6625d12bca..4458feb2b4 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 2225f45871..6e212b2f40 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 fbd93deb7b..e9ce2a3572 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 0000000000..0d1f5182fe --- /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 0000000000..a3813d9a24 --- /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 0000000000..22d8f4b4f1 --- /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 d77e5a3a66..6ad3b1e24f 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 24450a90bf..796f49cca7 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 0000000000..407a063892 --- /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 0000000000..9a182a29be --- /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 6c78f8881a..5c6cdca20a 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 8b15043c31..eac66d4e28 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 e88f2b46fe..fc54846d6a 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 6d096ae663..6e2e22e223 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 596b7bb774..1b52d4d707 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 63a469c429..8aa3f8e404 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';