diff --git a/packages/flutter/lib/rendering.dart b/packages/flutter/lib/rendering.dart index d579bdf0183..d09b04129a5 100644 --- a/packages/flutter/lib/rendering.dart +++ b/packages/flutter/lib/rendering.dart @@ -47,6 +47,7 @@ export 'src/rendering/shifted_box.dart'; export 'src/rendering/sliver.dart'; export 'src/rendering/sliver_app_bar.dart'; export 'src/rendering/sliver_block.dart'; +export 'src/rendering/sliver_padding.dart'; export 'src/rendering/stack.dart'; export 'src/rendering/table.dart'; export 'src/rendering/tweens.dart'; diff --git a/packages/flutter/lib/src/rendering/debug.dart b/packages/flutter/lib/src/rendering/debug.dart index c815f9cabe9..ab843f64b85 100644 --- a/packages/flutter/lib/src/rendering/debug.dart +++ b/packages/flutter/lib/src/rendering/debug.dart @@ -14,7 +14,8 @@ const Color _kDebugPaintSizeColor = const Color(0xFF00FFFF); const Color _kDebugPaintSpacingColor = const Color(0x90909090); const Color _kDebugPaintPaddingColor = const Color(0x900090FF); const Color _kDebugPaintPaddingInnerEdgeColor = const Color(0xFF0090FF); -const Color _kDebugPaintArrowColor = const Color(0xFFFFFF00); +const Color _kDebugPaintBoxArrowColor = const Color(0xFFFFFF00); +const Color _kDebugPaintSliverArrowColor = const Color(0xFF33CC33); const Color _kDebugPaintAlphabeticBaselineColor = const Color(0xFF00FF00); const Color _kDebugPaintIdeographicBaselineColor = const Color(0xFFFFD000); const Color _kDebugPaintLayerBordersColor = const Color(0xFFFF9800); @@ -23,34 +24,37 @@ const HSVColor _kDebugCurrentRepaintColor = const HSVColor.fromAHSV(0.4, 60.0, 1 const double _kDebugRepaintRainbowHueIncrement = 2.0; /// Causes each RenderBox to paint a box around its bounds, and some extra -/// boxes, such as RenderPadding, to draw construction lines. +/// boxes, such as [RenderPadding], to draw construction lines. bool debugPaintSizeEnabled = false; /// The color to use when painting RenderObject bounds. Color debugPaintSizeColor = _kDebugPaintSizeColor; /// The color to use when painting some boxes that just add space (e.g. an empty -/// RenderConstrainedBox or RenderPadding). +/// RenderConstrainedBox or [RenderPadding]). /// /// Used by, among other methods, [debugPaintPadding], which is called by /// [RenderPadding.debugPaintSize] when [debugPaintSizeEnabled] is true. Color debugPaintSpacingColor = _kDebugPaintSpacingColor; -/// The color to use when painting RenderPadding edges. +/// The color to use when painting [RenderPadding] edges. /// /// Used by, among other methods, [debugPaintPadding], which is called by /// [RenderPadding.debugPaintSize] when [debugPaintSizeEnabled] is true. Color debugPaintPaddingColor = _kDebugPaintPaddingColor; -/// The color to use when painting RenderPadding edges. This color is painted on +/// The color to use when painting [RenderPadding] edges. This color is painted on /// top of [debugPaintPaddingColor]. /// /// Used by, among other methods, [debugPaintPadding], which is called by /// [RenderPadding.debugPaintSize] when [debugPaintSizeEnabled] is true. Color debugPaintPaddingInnerEdgeColor = _kDebugPaintPaddingInnerEdgeColor; -/// The color to use when painting the arrows used to show RenderPositionedBox alignment. -Color debugPaintArrowColor = _kDebugPaintArrowColor; +/// The color to use when painting the arrows used to show [RenderPositionedBox] alignment. +Color debugPaintBoxArrowColor = _kDebugPaintBoxArrowColor; + +/// The color to use when painting the arrows used to show [RenderSliver] alignment. +Color debugPaintSliverArrowColor = _kDebugPaintSliverArrowColor; /// Causes each RenderBox to paint a line at each of its baselines. bool debugPaintBaselinesEnabled = false; @@ -166,7 +170,8 @@ bool debugAssertAllRenderVarsUnset(String reason) { debugPaintSpacingColor != _kDebugPaintSpacingColor || debugPaintPaddingColor != _kDebugPaintPaddingColor || debugPaintPaddingInnerEdgeColor != _kDebugPaintPaddingInnerEdgeColor || - debugPaintArrowColor != _kDebugPaintArrowColor || + debugPaintBoxArrowColor != _kDebugPaintBoxArrowColor || + debugPaintSliverArrowColor != _kDebugPaintSliverArrowColor || debugPaintAlphabeticBaselineColor != _kDebugPaintAlphabeticBaselineColor || debugPaintIdeographicBaselineColor != _kDebugPaintIdeographicBaselineColor || debugPaintLayerBordersColor != _kDebugPaintLayerBordersColor || diff --git a/packages/flutter/lib/src/rendering/shifted_box.dart b/packages/flutter/lib/src/rendering/shifted_box.dart index 265f3658637..26306235092 100644 --- a/packages/flutter/lib/src/rendering/shifted_box.dart +++ b/packages/flutter/lib/src/rendering/shifted_box.dart @@ -320,7 +320,7 @@ class RenderPositionedBox extends RenderAligningShiftedBox { paint = new Paint() ..style = PaintingStyle.stroke ..strokeWidth = 1.0 - ..color = debugPaintArrowColor; + ..color = debugPaintBoxArrowColor; path = new Path(); final BoxParentData childParentData = child.parentData; if (childParentData.offset.dy > 0.0) { diff --git a/packages/flutter/lib/src/rendering/sliver.dart b/packages/flutter/lib/src/rendering/sliver.dart index 99fb57c78eb..15d77085618 100644 --- a/packages/flutter/lib/src/rendering/sliver.dart +++ b/packages/flutter/lib/src/rendering/sliver.dart @@ -329,7 +329,7 @@ class SliverConstraints extends Constraints { return false; final SliverConstraints typedOther = other; assert(typedOther.debugAssertIsValid()); - return axis == typedOther.axis && + return axisDirection == typedOther.axisDirection && growthDirection == typedOther.growthDirection && scrollOffset == typedOther.scrollOffset && overlap == typedOther.overlap && @@ -339,7 +339,7 @@ class SliverConstraints extends Constraints { @override int get hashCode { - return hashValues(axis, growthDirection, scrollOffset, overlap, remainingPaintExtent, crossAxisExtent); + return hashValues(axisDirection, growthDirection, scrollOffset, overlap, remainingPaintExtent, crossAxisExtent); } @override @@ -684,8 +684,6 @@ abstract class RenderSliver extends RenderObject { /// [RenderViewport2.axisDirection], so values will typically be positive. double get centerOffsetAdjustment => 0.0; - void didScroll(double delta, Point focus) { } - /// Determines the set of render objects located at the given position. /// /// Returns true if the given point is contained in this render object or one @@ -820,10 +818,30 @@ abstract class RenderSliver extends RenderObject { }); } + /// This returns a [Size] with dimensions relative to the leading edge of the + /// sliver, specifically the same offset that is given to the [paint] method. + /// This means that the dimensions may be negative. + @protected + Size getAbsoluteSizeRelativeToOrigin() { + assert(geometry != null); + assert(!needsLayout); + switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) { + case AxisDirection.up: + return new Size(constraints.crossAxisExtent, -geometry.paintExtent); + case AxisDirection.right: + return new Size(geometry.paintExtent, constraints.crossAxisExtent); + case AxisDirection.down: + return new Size(constraints.crossAxisExtent, geometry.paintExtent); + case AxisDirection.left: + return new Size(-geometry.paintExtent, constraints.crossAxisExtent); + } + return null; + } + void _debugDrawArrow(Canvas canvas, Paint paint, Point p0, Point p1, GrowthDirection direction) { assert(() { if (p0 == p1) - return; + return true; assert(p0.x == p1.x || p0.y == p1.y); // must be axis-aligned final double d = (p1 - p0).distance * 0.2; Point temp; @@ -853,6 +871,7 @@ abstract class RenderSliver extends RenderObject { ..lineTo(p1.x - dx2, p1.y - dy2), paint ); + return true; }); } @@ -862,7 +881,7 @@ abstract class RenderSliver extends RenderObject { if (debugPaintSizeEnabled) { final double strokeWidth = math.min(4.0, geometry.paintExtent / 30.0); final Paint paint = new Paint() - ..color = const Color(0xFF33CC33) + ..color = debugPaintSliverArrowColor ..strokeWidth = strokeWidth ..style = PaintingStyle.stroke ..maskFilter = new MaskFilter.blur(BlurStyle.solid, strokeWidth); diff --git a/packages/flutter/lib/src/rendering/sliver_padding.dart b/packages/flutter/lib/src/rendering/sliver_padding.dart new file mode 100644 index 00000000000..a85b1d6684d --- /dev/null +++ b/packages/flutter/lib/src/rendering/sliver_padding.dart @@ -0,0 +1,312 @@ +// Copyright 2016 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:math' as math; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:meta/meta.dart'; +import 'package:vector_math/vector_math_64.dart'; + +import 'binding.dart'; +import 'debug.dart'; +import 'object.dart'; +import 'sliver.dart'; + +/// Inset a [RenderSliver], applying padding on each side. +/// +/// A [RenderSliverPadding] object wraps the [SliverGeometry.layoutExtent] of +/// its child. Any incoming [SliverConstraints.overlap] is ignored and not +/// passed on to the child. +/// +/// Applying this to anything but the most mundane sliver is likely to have +/// undesired effects. For example, wrapping a [RenderSliverPinnedAppBar] will +/// cause the app bar to overlap earlier slivers (contrary to the normal +/// behavior of pinned app bars), and while the app bar is pinned, the padding +/// will scroll away. +class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin { + /// Creates a render object that insets its child in a viewport. + /// + /// The [padding] argument must not be null and must have non-negative insets. + RenderSliverPadding({ + EdgeInsets padding, + RenderSliver child, + }) : _padding = padding { + assert(padding != null); + assert(padding.isNonNegative); + this.child = child; + } + + /// The amount to pad the child in each dimension. + EdgeInsets get padding => _padding; + EdgeInsets _padding; + set padding (EdgeInsets value) { + assert(value != null); + assert(value.isNonNegative); + if (_padding == value) + return; + _padding = value; + markNeedsLayout(); + } + + /// The padding in the scroll direction on the side nearest the 0.0 scroll direction. + /// + /// Only valid after layout has started, since before layout the render object + /// doesn't know what direction it will be laid out in. + double get beforePadding { + assert(constraints != null); + assert(constraints.axisDirection != null); + assert(constraints.growthDirection != null); + switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) { + case AxisDirection.up: + return padding.bottom; + case AxisDirection.right: + return padding.left; + case AxisDirection.down: + return padding.top; + case AxisDirection.left: + return padding.right; + } + return null; + } + + /// The padding in the scroll direction on the side furthest from the 0.0 scroll offset. + /// + /// Only valid after layout has started, since before layout the render object + /// doesn't know what direction it will be laid out in. + double get afterPadding { + assert(constraints != null); + assert(constraints.axisDirection != null); + assert(constraints.growthDirection != null); + switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) { + case AxisDirection.up: + return padding.top; + case AxisDirection.right: + return padding.right; + case AxisDirection.down: + return padding.bottom; + case AxisDirection.left: + return padding.left; + } + return null; + } + + /// The padding in the cross-axis direction on the side to the right when + /// facing away from the zero scroll offset in the scroll axis direction. (In + /// other words, for a vertical downwards-growing list, the padding on the + /// left.) + /// + /// Only valid after layout has started, since before layout the render object + /// doesn't know what direction it will be laid out in. + double get startPadding { + assert(constraints != null); + assert(constraints.axisDirection != null); + assert(constraints.growthDirection != null); + switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) { + case AxisDirection.up: + return padding.right; + case AxisDirection.right: + return padding.top; + case AxisDirection.down: + return padding.left; + case AxisDirection.left: + return padding.bottom; + } + return null; + } + + /// The padding in the cross-axis direction on the side to the left when + /// facing away from the zero scroll offset in the scroll axis direction. (In + /// other words, for a vertical downwards-growing list, the padding on the + /// right.) + /// + /// Only valid after layout has started, since before layout the render object + /// doesn't know what direction it will be laid out in. + double get endPadding { + assert(constraints != null); + assert(constraints.axisDirection != null); + assert(constraints.growthDirection != null); + switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) { + case AxisDirection.up: + return padding.left; + case AxisDirection.right: + return padding.bottom; + case AxisDirection.down: + return padding.right; + case AxisDirection.left: + return padding.top; + } + return null; + } + + /// The total padding in the [constraints.axisDirection]. (In other words, for + /// a vertical downwards-growing list, the sum of the padding on the top and + /// bottom.) + /// + /// Only valid after layout has started, since before layout the render object + /// doesn't know what direction it will be laid out in. + double get mainAxisPadding { + assert(constraints != null); + assert(constraints.axis != null); + switch (constraints.axis) { + case Axis.horizontal: + return padding.left + padding.right; + case Axis.vertical: + return padding.top + padding.bottom; + } + return null; + } + + /// The total padding in the cross-axis direction. (In other words, for a + /// vertical downwards-growing list, the sum of the padding on the left and + /// right.) + /// + /// Only valid after layout has started, since before layout the render object + /// doesn't know what direction it will be laid out in. + double get crossAxisPadding { + assert(constraints != null); + assert(constraints.axis != null); + switch (constraints.axis) { + case Axis.horizontal: + return padding.top + padding.bottom; + case Axis.vertical: + return padding.left + padding.right; + } + return null; + } + + @override + void setupParentData(RenderObject child) { + if (child.parentData is! SliverPhysicalParentData) + child.parentData = new SliverPhysicalParentData(); + } + + @override + void performLayout() { + final double beforePadding = this.beforePadding; + final double afterPadding = this.afterPadding; + final double mainAxisPadding = this.mainAxisPadding; + final double crossAxisPadding = this.crossAxisPadding; + if (child == null) { + geometry = new SliverGeometry( + scrollExtent: mainAxisPadding, + paintExtent: math.min(mainAxisPadding, constraints.remainingPaintExtent), + maxPaintExtent: mainAxisPadding, + ); + return; + } + child.layout( + constraints.copyWith( + scrollOffset: math.max(0.0, constraints.scrollOffset - beforePadding), + overlap: 0.0, + remainingPaintExtent: constraints.remainingPaintExtent - calculatePaintOffset(constraints, from: 0.0, to: beforePadding), + crossAxisExtent: constraints.crossAxisExtent - crossAxisPadding, + ), + parentUsesSize: true, + ); + final SliverGeometry childLayoutGeometry = child.geometry; + final double beforePaddingPaintExtent = calculatePaintOffset( + constraints, + from: 0.0, + to: beforePadding, + ); + final double afterPaddingPaintExtent = calculatePaintOffset( + constraints, + from: beforePadding + childLayoutGeometry.scrollExtent, + to: mainAxisPadding + childLayoutGeometry.scrollExtent, + ); + final double mainAxisPaddingPaintExtent = beforePaddingPaintExtent + afterPaddingPaintExtent; + geometry = new SliverGeometry( + scrollExtent: mainAxisPadding + childLayoutGeometry.scrollExtent, + paintExtent: math.min( + beforePaddingPaintExtent + math.max(childLayoutGeometry.paintExtent, childLayoutGeometry.layoutExtent + afterPaddingPaintExtent), + constraints.remainingPaintExtent, + ), + layoutExtent: math.min( + mainAxisPaddingPaintExtent + childLayoutGeometry.layoutExtent, + constraints.remainingPaintExtent, + ), + maxPaintExtent: mainAxisPadding + childLayoutGeometry.maxPaintExtent, + hitTestExtent: math.max( + mainAxisPaddingPaintExtent + childLayoutGeometry.paintExtent, + beforePaddingPaintExtent + childLayoutGeometry.hitTestExtent, + ), + ); + + final SliverPhysicalParentData childParentData = child.parentData; + assert(constraints.axisDirection != null); + assert(constraints.growthDirection != null); + switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) { + case AxisDirection.up: + childParentData.paintOffset = new Offset(padding.left, calculatePaintOffset(constraints, from: padding.bottom + childLayoutGeometry.scrollExtent, to: padding.bottom + childLayoutGeometry.scrollExtent + padding.top)); + break; + case AxisDirection.right: + childParentData.paintOffset = new Offset(calculatePaintOffset(constraints, from: 0.0, to: padding.left), padding.top); + break; + case AxisDirection.down: + childParentData.paintOffset = new Offset(padding.left, calculatePaintOffset(constraints, from: 0.0, to: padding.top)); + break; + case AxisDirection.left: + childParentData.paintOffset = new Offset(calculatePaintOffset(constraints, from: padding.right + childLayoutGeometry.scrollExtent, to: padding.right + childLayoutGeometry.scrollExtent + padding.left), padding.top); + break; + } + assert(childParentData.paintOffset != null); + assert(beforePadding == this.beforePadding); + assert(afterPadding == this.afterPadding); + assert(mainAxisPadding == this.mainAxisPadding); + assert(crossAxisPadding == this.crossAxisPadding); + } + + @override + bool hitTestChildren(HitTestResult result, { @required double mainAxisPosition, @required double crossAxisPosition }) { + if (child.geometry.hitTestExtent > 0.0) + return child.hitTest(result, mainAxisPosition: mainAxisPosition - childPosition(child), crossAxisPosition: crossAxisPosition - startPadding); + return false; + } + + @override + double childPosition(RenderSliver child) { + assert(child != null); + assert(child == this.child); + return calculatePaintOffset(constraints, from: 0.0, to: beforePadding); + } + + @override + void applyPaintTransform(RenderObject child, Matrix4 transform) { + assert(child != null); + assert(child == this.child); + final SliverPhysicalParentData childParentData = child.parentData; + childParentData.applyPaintTransform(transform); + } + + @override + void paint(PaintingContext context, Offset offset) { + if (child != null && child.geometry.visible) { + final SliverPhysicalParentData childParentData = child.parentData; + context.paintChild(child, offset + childParentData.paintOffset); + } + } + + @override + void debugPaint(PaintingContext context, Offset offset) { + super.debugPaint(context, offset); + assert(() { + if (debugPaintSizeEnabled) { + final Size parentSize = getAbsoluteSizeRelativeToOrigin(); + final Rect outerRect = offset & parentSize; + Size childSize; + Rect innerRect; + if (child != null) { + childSize = child.getAbsoluteSizeRelativeToOrigin(); + final SliverPhysicalParentData childParentData = child.parentData; + innerRect = (offset + childParentData.paintOffset) & childSize; + assert(outerRect.contains(innerRect.topLeft)); + assert(outerRect.contains(innerRect.bottomRight)); + } + debugPaintPadding(context.canvas, outerRect, innerRect); + } + return true; + }); + } +} diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index bdbeec99848..306c551e901 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -1439,6 +1439,45 @@ class Viewport extends SingleChildRenderObjectWidget { } +// SLIVERS + +class SliverToBoxAdapter extends SingleChildRenderObjectWidget { + SliverToBoxAdapter({ + Key key, + Widget child, + }) : super(key: key, child: child); + + @override + RenderSliverToBoxAdapter createRenderObject(BuildContext context) => new RenderSliverToBoxAdapter(); +} + +class SliverPadding extends SingleChildRenderObjectWidget { + SliverPadding({ + Key key, + @required this.padding, + Widget child, + }) : super(key: key, child: child) { + assert(padding != null); + } + + final EdgeInsets padding; + + @override + RenderSliverPadding createRenderObject(BuildContext context) => new RenderSliverPadding(padding: padding); + + @override + void updateRenderObject(BuildContext context, RenderSliverPadding renderObject) { + renderObject.padding = padding; + } + + @override + void debugFillDescription(List description) { + super.debugFillDescription(description); + description.add('padding: $padding'); + } +} + + // LAYOUT NODES /// A widget that uses the block layout algorithm for its children. @@ -2703,16 +2742,6 @@ class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget { } } -class SliverToBoxAdapter extends SingleChildRenderObjectWidget { - SliverToBoxAdapter({ - Key key, - Widget child, - }) : super(key: key, child: child); - - @override - RenderSliverToBoxAdapter createRenderObject(BuildContext context) => new RenderSliverToBoxAdapter(); -} - // EVENT HANDLING diff --git a/packages/flutter/test/rendering/debug_test.dart b/packages/flutter/test/rendering/debug_test.dart index f810ab9b5b9..56359c22091 100644 --- a/packages/flutter/test/rendering/debug_test.dart +++ b/packages/flutter/test/rendering/debug_test.dart @@ -6,6 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:test/test.dart'; import 'package:vector_math/vector_math_64.dart'; +import 'rendering_tester.dart'; import 'mock_canvas.dart'; void main() { @@ -31,4 +32,53 @@ void main() { debugPaintPadding(canvas, new Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), new Rect.fromLTRB(15.0, 15.0, 15.0, 15.0)); }, paints..rect(rect: new Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), color: debugPaintSpacingColor)); }); + + test('debugPaintPadding from render objects', () { + debugPaintSizeEnabled = true; + RenderSliver s; + RenderBox b; + RenderViewport2 root = new RenderViewport2( + offset: new ViewportOffset.zero(), + children: [ + s = new RenderSliverPadding( + padding: new EdgeInsets.all(10.0), + child: new RenderSliverToBoxAdapter( + child: b = new RenderPadding( + padding: new EdgeInsets.all(10.0), + ), + ), + ), + ], + ); + layout(root); + expect(b.debugPaint, paints..rect(color: debugPaintSizeColor)..rect(color: debugPaintSpacingColor)); + expect(b.debugPaint, isNot(paints..path())); + expect(s.debugPaint, paints..circle(hasMaskFilter: true)..line(hasMaskFilter: true)..path(hasMaskFilter: true)..path(hasMaskFilter: true) + ..path(color: debugPaintPaddingColor)..path(color: debugPaintPaddingInnerEdgeColor)); + expect(s.debugPaint, isNot(paints..rect())); + debugPaintSizeEnabled = false; + }); + + test('debugPaintPadding from render objects', () { + debugPaintSizeEnabled = true; + RenderSliver s; + RenderBox b = new RenderPadding( + padding: new EdgeInsets.all(10.0), + child: new RenderViewport2( + offset: new ViewportOffset.zero(), + children: [ + s = new RenderSliverPadding( + padding: new EdgeInsets.all(10.0), + ), + ], + ), + ); + layout(b); + expect(s.debugPaint, paints..rect(color: debugPaintSpacingColor)); + expect(s.debugPaint, isNot(paints..circle(hasMaskFilter: true)..line(hasMaskFilter: true)..path(hasMaskFilter: true)..path(hasMaskFilter: true) + ..path(color: debugPaintPaddingColor)..path(color: debugPaintPaddingInnerEdgeColor))); + expect(b.debugPaint, paints..rect(color: debugPaintSizeColor)..path(color: debugPaintPaddingColor)..path(color: debugPaintPaddingInnerEdgeColor)); + expect(b.debugPaint, isNot(paints..rect(color: debugPaintSpacingColor))); + debugPaintSizeEnabled = false; + }); } diff --git a/packages/flutter/test/rendering/mock_canvas.dart b/packages/flutter/test/rendering/mock_canvas.dart index 503b190c968..f3c512c5911 100644 --- a/packages/flutter/test/rendering/mock_canvas.dart +++ b/packages/flutter/test/rendering/mock_canvas.dart @@ -158,6 +158,18 @@ abstract class PaintPattern { /// [Canvas.drawPath] call are ignored. void path({ Color color, bool hasMaskFilter, PaintingStyle style }); + /// Indicates that a line is expected next. + /// + /// The next line is examined. Any arguments that are passed to this method + /// are compared to the actual [Canvas.drawLine] call's `paint` argument, and + /// any mismatches result in failure. + /// + /// If no call to [Canvas.drawLine] was made, then this results in failure. + /// + /// Any calls made between the last matched call (if any) and the + /// [Canvas.drawLine] call are ignored. + void line({ Color color, bool hasMaskFilter, PaintingStyle style }); + /// Provides a custom matcher. /// /// Each method call after the last matched call (if any) will be passed to @@ -235,6 +247,11 @@ class _TestRecordingCanvasPatternMatcher extends Matcher implements PaintPattern _predicates.add(new _PathPaintPredicate(color: color, hasMaskFilter: hasMaskFilter, style: style)); } + @override + void line({ Color color, bool hasMaskFilter, PaintingStyle style }) { + _predicates.add(new _LinePaintPredicate(color: color, hasMaskFilter: hasMaskFilter, style: style)); + } + @override void something(PaintPatternPredicate predicate) { _predicates.add(new _SomethingPaintPredicate(predicate)); @@ -573,6 +590,13 @@ class _PathPaintPredicate extends _DrawCommandPaintPredicate { ); } +// TODO(ianh): add arguments to test the points, length, angle, that kind of thing +class _LinePaintPredicate extends _DrawCommandPaintPredicate { + _LinePaintPredicate({ Color color, bool hasMaskFilter, PaintingStyle style }) : super( + #drawLine, 'a line', 3, 2, color: color, hasMaskFilter: hasMaskFilter, style: style + ); +} + class _SomethingPaintPredicate extends _PaintPredicate { _SomethingPaintPredicate(this.predicate); diff --git a/packages/flutter/test/widgets/slivers_block_global_key_test.dart b/packages/flutter/test/widgets/slivers_block_global_key_test.dart index a2b63b808e4..871a1fe62e2 100644 --- a/packages/flutter/test/widgets/slivers_block_global_key_test.dart +++ b/packages/flutter/test/widgets/slivers_block_global_key_test.dart @@ -37,13 +37,13 @@ Future test(WidgetTester tester, double offset, List keys) { } void verify(WidgetTester tester, List answerKey, String text) { - List testAnswers = tester.renderObjectList/**/(find.byType(SizedBox)).map/**/( + List testAnswers = tester.renderObjectList(find.byType(SizedBox)).map( (RenderBox target) => target.localToGlobal(const Point(0.0, 0.0)) ).toList(); expect(testAnswers, equals(answerKey)); final String foundText = - tester.widgetList/**/(find.byType(Text)) - .map/**/((Text widget) => widget.data) + tester.widgetList(find.byType(Text)) + .map((Text widget) => widget.data) .reduce((String value, String element) => value + element); expect(foundText, equals(text)); } diff --git a/packages/flutter/test/widgets/slivers_block_test.dart b/packages/flutter/test/widgets/slivers_block_test.dart index d6355a6b93e..a3792498484 100644 --- a/packages/flutter/test/widgets/slivers_block_test.dart +++ b/packages/flutter/test/widgets/slivers_block_test.dart @@ -24,13 +24,13 @@ Future test(WidgetTester tester, double offset) { } void verify(WidgetTester tester, List answerKey, String text) { - List testAnswers = tester.renderObjectList/**/(find.byType(SizedBox)).map/**/( + List testAnswers = tester.renderObjectList(find.byType(SizedBox)).map( (RenderBox target) => target.localToGlobal(const Point(0.0, 0.0)) ).toList(); expect(testAnswers, equals(answerKey)); final String foundText = - tester.widgetList/**/(find.byType(Text)) - .map/**/((Text widget) => widget.data) + tester.widgetList(find.byType(Text)) + .map((Text widget) => widget.data) .reduce((String value, String element) => value + element); expect(foundText, equals(text)); } @@ -38,7 +38,7 @@ void verify(WidgetTester tester, List answerKey, String text) { void main() { testWidgets('Viewport2+SliverBlock basic test', (WidgetTester tester) async { await test(tester, 0.0); - expect(tester.renderObject/**/(find.byType(Viewport2)).size, equals(const Size(800.0, 600.0))); + expect(tester.renderObject(find.byType(Viewport2)).size, equals(const Size(800.0, 600.0))); verify(tester, [ const Point(0.0, 0.0), const Point(0.0, 400.0), diff --git a/packages/flutter/test/widgets/slivers_padding_test.dart b/packages/flutter/test/widgets/slivers_padding_test.dart new file mode 100644 index 00000000000..844357e53b4 --- /dev/null +++ b/packages/flutter/test/widgets/slivers_padding_test.dart @@ -0,0 +1,240 @@ +// Copyright 2016 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:flutter_test/flutter_test.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; + +Future test(WidgetTester tester, double offset, EdgeInsets padding, AxisDirection axisDirection) { + return tester.pumpWidget(new Viewport2( + offset: new ViewportOffset.fixed(offset), + axisDirection: axisDirection, + children: [ + new SliverToBoxAdapter(child: new SizedBox(width: 400.0, height: 400.0, child: new Text('before'))), + new SliverPadding( + padding: padding, + child: new SliverToBoxAdapter(child: new SizedBox(width: 400.0, height: 400.0, child: new Text('padded'))), + ), + new SliverToBoxAdapter(child: new SizedBox(width: 400.0, height: 400.0, child: new Text('after'))), + ], + )); +} + +void verify(WidgetTester tester, List answerKey) { + List testAnswers = tester.renderObjectList(find.byType(SizedBox)).map( + (RenderBox target) { + Point topLeft = target.localToGlobal(Point.origin); + Point bottomRight = target.localToGlobal(target.size.bottomRight(Point.origin)); + return new Rect.fromPoints(topLeft, bottomRight); + } + ).toList(); + expect(testAnswers, equals(answerKey)); +} + +void main() { + testWidgets('Viewport2+SliverPadding basic test', (WidgetTester tester) async { + EdgeInsets padding = const EdgeInsets.fromLTRB(25.0, 20.0, 15.0, 35.0); + await test(tester, 0.0, padding, AxisDirection.down); + expect(tester.renderObject(find.byType(Viewport2)).size, equals(const Size(800.0, 600.0))); + verify(tester, [ + new Rect.fromLTWH(0.0, 0.0, 800.0, 400.0), + new Rect.fromLTWH(25.0, 420.0, 760.0, 400.0), + new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0), + ]); + + await test(tester, 200.0, padding, AxisDirection.down); + verify(tester, [ + new Rect.fromLTWH(0.0, -200.0, 800.0, 400.0), + new Rect.fromLTWH(25.0, 220.0, 760.0, 400.0), + new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0), + ]); + + await test(tester, 390.0, padding, AxisDirection.down); + verify(tester, [ + new Rect.fromLTWH(0.0, -390.0, 800.0, 400.0), + new Rect.fromLTWH(25.0, 30.0, 760.0, 400.0), + new Rect.fromLTWH(0.0, 465.0, 800.0, 400.0), + ]); + + await test(tester, 490.0, padding, AxisDirection.down); + verify(tester, [ + new Rect.fromLTWH(0.0, -490.0, 800.0, 400.0), + new Rect.fromLTWH(25.0, -70.0, 760.0, 400.0), + new Rect.fromLTWH(0.0, 365.0, 800.0, 400.0), + ]); + + await test(tester, 10000.0, padding, AxisDirection.down); + verify(tester, [ + new Rect.fromLTWH(0.0, -10000.0, 800.0, 400.0), + new Rect.fromLTWH(25.0, -9580.0, 760.0, 400.0), + new Rect.fromLTWH(0.0, -9145.0, 800.0, 400.0), + ]); + }); + + testWidgets('Viewport2+SliverPadding hit testing', (WidgetTester tester) async { + EdgeInsets padding = const EdgeInsets.all(30.0); + await test(tester, 350.0, padding, AxisDirection.down); + expect(tester.renderObject(find.byType(Viewport2)).size, equals(const Size(800.0, 600.0))); + verify(tester, [ + new Rect.fromLTWH(0.0, -350.0, 800.0, 400.0), + new Rect.fromLTWH(30.0, 80.0, 740.0, 400.0), + new Rect.fromLTWH(0.0, 510.0, 800.0, 400.0), + ]); + HitTestResult result; + result = tester.hitTestOnBinding(const Point(10.0, 10.0)); + expect(result.path.first.target, tester.firstRenderObject(find.byType(Text))); + result = tester.hitTestOnBinding(const Point(10.0, 60.0)); + expect(result.path.first.target, new isInstanceOf()); + result = tester.hitTestOnBinding(const Point(100.0, 100.0)); + expect(result.path.first.target, tester.renderObjectList(find.byType(Text)).skip(1).first); + result = tester.hitTestOnBinding(const Point(100.0, 490.0)); + expect(result.path.first.target, new isInstanceOf()); + result = tester.hitTestOnBinding(const Point(10.0, 520.0)); + expect(result.path.first.target, tester.renderObjectList(find.byType(Text)).last); + }); + + testWidgets('Viewport2+SliverPadding hit testing up', (WidgetTester tester) async { + EdgeInsets padding = const EdgeInsets.all(30.0); + await test(tester, 350.0, padding, AxisDirection.up); + expect(tester.renderObject(find.byType(Viewport2)).size, equals(const Size(800.0, 600.0))); + verify(tester, [ + new Rect.fromLTWH(0.0, 600.0+350.0-400.0, 800.0, 400.0), + new Rect.fromLTWH(30.0, 600.0-80.0-400.0, 740.0, 400.0), + new Rect.fromLTWH(0.0, 600.0-510.0-400.0, 800.0, 400.0), + ]); + HitTestResult result; + result = tester.hitTestOnBinding(const Point(10.0, 600.0-10.0)); + expect(result.path.first.target, tester.firstRenderObject(find.byType(Text))); + result = tester.hitTestOnBinding(const Point(10.0, 600.0-60.0)); + expect(result.path.first.target, new isInstanceOf()); + result = tester.hitTestOnBinding(const Point(100.0, 600.0-100.0)); + expect(result.path.first.target, tester.renderObjectList(find.byType(Text)).skip(1).first); + result = tester.hitTestOnBinding(const Point(100.0, 600.0-490.0)); + expect(result.path.first.target, new isInstanceOf()); + result = tester.hitTestOnBinding(const Point(10.0, 600.0-520.0)); + expect(result.path.first.target, tester.renderObjectList(find.byType(Text)).last); + }); + + testWidgets('Viewport2+SliverPadding hit testing left', (WidgetTester tester) async { + EdgeInsets padding = const EdgeInsets.all(30.0); + await test(tester, 350.0, padding, AxisDirection.left); + expect(tester.renderObject(find.byType(Viewport2)).size, equals(const Size(800.0, 600.0))); + verify(tester, [ + new Rect.fromLTWH(800.0+350.0-400.0, 0.0, 400.0, 600.0), + new Rect.fromLTWH(800.0-80.0-400.0, 30.0, 400.0, 540.0), + new Rect.fromLTWH(800.0-510.0-400.0, 0.0, 400.0, 600.0), + ]); + HitTestResult result; + result = tester.hitTestOnBinding(const Point(800.0-10.0, 10.0)); + expect(result.path.first.target, tester.firstRenderObject(find.byType(Text))); + result = tester.hitTestOnBinding(const Point(800.0-60.0, 10.0)); + expect(result.path.first.target, new isInstanceOf()); + result = tester.hitTestOnBinding(const Point(800.0-100.0, 100.0)); + expect(result.path.first.target, tester.renderObjectList(find.byType(Text)).skip(1).first); + result = tester.hitTestOnBinding(const Point(800.0-490.0, 100.0)); + expect(result.path.first.target, new isInstanceOf()); + result = tester.hitTestOnBinding(const Point(800.0-520.0, 10.0)); + expect(result.path.first.target, tester.renderObjectList(find.byType(Text)).last); + }); + + testWidgets('Viewport2+SliverPadding hit testing right', (WidgetTester tester) async { + EdgeInsets padding = const EdgeInsets.all(30.0); + await test(tester, 350.0, padding, AxisDirection.right); + expect(tester.renderObject(find.byType(Viewport2)).size, equals(const Size(800.0, 600.0))); + verify(tester, [ + new Rect.fromLTWH(-350.0, 0.0, 400.0, 600.0), + new Rect.fromLTWH(80.0, 30.0, 400.0, 540.0), + new Rect.fromLTWH(510.0, 0.0, 400.0, 600.0), + ]); + HitTestResult result; + result = tester.hitTestOnBinding(const Point(10.0, 10.0)); + expect(result.path.first.target, tester.firstRenderObject(find.byType(Text))); + result = tester.hitTestOnBinding(const Point(60.0, 10.0)); + expect(result.path.first.target, new isInstanceOf()); + result = tester.hitTestOnBinding(const Point(100.0, 100.0)); + expect(result.path.first.target, tester.renderObjectList(find.byType(Text)).skip(1).first); + result = tester.hitTestOnBinding(const Point(490.0, 100.0)); + expect(result.path.first.target, new isInstanceOf()); + result = tester.hitTestOnBinding(const Point(520.0, 10.0)); + expect(result.path.first.target, tester.renderObjectList(find.byType(Text)).last); + }); + + testWidgets('Viewport2+SliverPadding no child', (WidgetTester tester) async { + await tester.pumpWidget(new Viewport2( + offset: new ViewportOffset.fixed(0.0), + children: [ + new SliverPadding(padding: new EdgeInsets.all(100.0)), + new SliverToBoxAdapter(child: new SizedBox(width: 400.0, height: 400.0, child: new Text('x'))), + ], + )); + expect(tester.renderObject(find.text('x')).localToGlobal(Point.origin), const Point(0.0, 200.0)); + expect(tester.renderObject(find.byType(SliverPadding)).endPadding, 100.0); + }); + + testWidgets('Viewport2+SliverPadding changing padding', (WidgetTester tester) async { + await tester.pumpWidget(new Viewport2( + axisDirection: AxisDirection.left, + offset: new ViewportOffset.fixed(0.0), + children: [ + new SliverPadding(padding: new EdgeInsets.fromLTRB(90.0, 1.0, 110.0, 2.0)), + new SliverToBoxAdapter(child: new SizedBox(width: 201.0, child: new Text('x'))), + ], + )); + expect(tester.renderObject(find.text('x')).localToGlobal(Point.origin), const Point(399.0, 0.0)); + expect(tester.renderObject(find.byType(SliverPadding)).endPadding, 1.0); + await tester.pumpWidget(new Viewport2( + axisDirection: AxisDirection.left, + offset: new ViewportOffset.fixed(0.0), + children: [ + new SliverPadding(padding: new EdgeInsets.fromLTRB(110.0, 1.0, 80.0, 2.0)), + new SliverToBoxAdapter(child: new SizedBox(width: 201.0, child: new Text('x'))), + ], + )); + expect(tester.renderObject(find.text('x')).localToGlobal(Point.origin), const Point(409.0, 0.0)); + expect(tester.renderObject(find.byType(SliverPadding)).endPadding, 1.0); + }); + + testWidgets('Viewport2+SliverPadding changing direction', (WidgetTester tester) async { + await tester.pumpWidget(new Viewport2( + axisDirection: AxisDirection.up, + offset: new ViewportOffset.fixed(0.0), + children: [ + new SliverPadding(padding: new EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)), + ], + )); + expect(tester.renderObject(find.byType(SliverPadding)).endPadding, 1.0); + await tester.pumpWidget(new Viewport2( + axisDirection: AxisDirection.down, + offset: new ViewportOffset.fixed(0.0), + children: [ + new SliverPadding(padding: new EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)), + ], + )); + expect(tester.renderObject(find.byType(SliverPadding)).endPadding, 4.0); + await tester.pumpWidget(new Viewport2( + axisDirection: AxisDirection.right, + offset: new ViewportOffset.fixed(0.0), + children: [ + new SliverPadding(padding: new EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)), + ], + )); + expect(tester.renderObject(find.byType(SliverPadding)).endPadding, 8.0); + await tester.pumpWidget(new Viewport2( + axisDirection: AxisDirection.left, + offset: new ViewportOffset.fixed(0.0), + children: [ + new SliverPadding(padding: new EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)), + ], + )); + expect(tester.renderObject(find.byType(SliverPadding)).endPadding, 2.0); + await tester.pumpWidget(new Viewport2( + axisDirection: AxisDirection.left, + offset: new ViewportOffset.fixed(99999.9), + children: [ + new SliverPadding(padding: new EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)), + ], + )); + expect(tester.renderObject(find.byType(SliverPadding)).endPadding, 2.0); + }); +}