Commit 2afa87df authored by Ian Hickson's avatar Ian Hickson

Make BoxDecoration replaceable.

Factor out a reusable interface called Decoration from BoxDecoration.

Make all the consumers of BoxDecoration and the erstwhile BoxPainter
into consumers of Decoration.

Make a BoxPainter be something you get from a Decoration, rather than
something to which you pass a BoxDecoration.

Rename Shape to BoxShape now that it's documented specifically as
applying to boxes.

Move EdgeDims to its own file.

Move FractionalOffset up so that it's with the other helper classes in
its file rather than alone at the end.

Minor change to RenderClipOval's hit testing to avoid taking an
unnecessary square root.

Rename BoxDecorationPosition to DecorationPosition since
RenderDecoratedBox now takes any Decoration.

Implement hit testing for rounded rects.

Rename AnimatedBoxDecorationValue to AnimatedDecorationValue, and make
it support lerping across any Decoration (by deferring to the objects
involved).
parent 0fe72b17
...@@ -50,7 +50,7 @@ class Dot extends StatelessComponent { ...@@ -50,7 +50,7 @@ class Dot extends StatelessComponent {
height: size, height: size,
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: color, backgroundColor: color,
shape: Shape.circle shape: BoxShape.circle
), ),
child: child child: child
); );
......
...@@ -14,7 +14,7 @@ class Circle extends StatelessComponent { ...@@ -14,7 +14,7 @@ class Circle extends StatelessComponent {
width: 50.0, width: 50.0,
margin: margin + new EdgeDims.symmetric(horizontal: 2.0), margin: margin + new EdgeDims.symmetric(horizontal: 2.0),
decoration: new BoxDecoration( decoration: new BoxDecoration(
shape: Shape.circle, shape: BoxShape.circle,
backgroundColor: const Color(0xFF00FF00) backgroundColor: const Color(0xFF00FF00)
) )
); );
......
...@@ -15,6 +15,8 @@ library painting; ...@@ -15,6 +15,8 @@ library painting;
export 'src/painting/basic_types.dart'; export 'src/painting/basic_types.dart';
export 'src/painting/box_painter.dart'; export 'src/painting/box_painter.dart';
export 'src/painting/colors.dart'; export 'src/painting/colors.dart';
export 'src/painting/decoration.dart';
export 'src/painting/edge_dims.dart';
export 'src/painting/shadows.dart'; export 'src/painting/shadows.dart';
export 'src/painting/text_painter.dart'; export 'src/painting/text_painter.dart';
export 'src/painting/text_style.dart'; export 'src/painting/text_style.dart';
......
...@@ -35,7 +35,7 @@ class CircleAvatar extends StatelessComponent { ...@@ -35,7 +35,7 @@ class CircleAvatar extends StatelessComponent {
duration: kThemeChangeDuration, duration: kThemeChangeDuration,
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: color, backgroundColor: color,
shape: Shape.circle shape: BoxShape.circle
), ),
width: 40.0, width: 40.0,
height: 40.0, height: 40.0,
......
...@@ -226,7 +226,7 @@ class DayPicker extends StatelessComponent { ...@@ -226,7 +226,7 @@ class DayPicker extends StatelessComponent {
selectedDate.day == day) selectedDate.day == day)
decoration = new BoxDecoration( decoration = new BoxDecoration(
backgroundColor: theme.primarySwatch[100], backgroundColor: theme.primarySwatch[100],
shape: Shape.circle shape: BoxShape.circle
); );
// Use a different font color for the current day // Use a different font color for the current day
...@@ -377,7 +377,7 @@ class _YearPickerState extends ScrollableWidgetListState<YearPicker> { ...@@ -377,7 +377,7 @@ class _YearPickerState extends ScrollableWidgetListState<YearPicker> {
height: config.itemExtent, height: config.itemExtent,
decoration: year == config.selectedDate.year ? new BoxDecoration( decoration: year == config.selectedDate.year ? new BoxDecoration(
backgroundColor: Theme.of(context).primarySwatch[100], backgroundColor: Theme.of(context).primarySwatch[100],
shape: Shape.circle shape: BoxShape.circle
) : null, ) : null,
child: new Center( child: new Center(
child: new Text(label, style: style) child: new Text(label, style: style)
......
...@@ -37,11 +37,11 @@ class _DropDownMenuPainter extends CustomPainter { ...@@ -37,11 +37,11 @@ class _DropDownMenuPainter extends CustomPainter {
final RenderBox renderBox; final RenderBox renderBox;
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final BoxPainter painter = new BoxPainter(new BoxDecoration( final BoxPainter painter = new BoxDecoration(
backgroundColor: color, backgroundColor: color,
borderRadius: 2.0, borderRadius: 2.0,
boxShadow: elevationToShadow[elevation] boxShadow: elevationToShadow[elevation]
)); ).createBoxPainter();
double top = renderBox.globalToLocal(new Point(0.0, menuTop)).y; double top = renderBox.globalToLocal(new Point(0.0, menuTop)).y;
double bottom = renderBox.globalToLocal(new Point(0.0, menuBottom)).y; double bottom = renderBox.globalToLocal(new Point(0.0, menuBottom)).y;
......
...@@ -18,7 +18,7 @@ class InkResponse extends StatefulComponent { ...@@ -18,7 +18,7 @@ class InkResponse extends StatefulComponent {
this.onLongPress, this.onLongPress,
this.onHighlightChanged, this.onHighlightChanged,
this.containedInWell: false, this.containedInWell: false,
this.highlightShape: Shape.circle this.highlightShape: BoxShape.circle
}) : super(key: key); }) : super(key: key);
final Widget child; final Widget child;
...@@ -27,7 +27,7 @@ class InkResponse extends StatefulComponent { ...@@ -27,7 +27,7 @@ class InkResponse extends StatefulComponent {
final GestureLongPressCallback onLongPress; final GestureLongPressCallback onLongPress;
final ValueChanged<bool> onHighlightChanged; final ValueChanged<bool> onHighlightChanged;
final bool containedInWell; final bool containedInWell;
final Shape highlightShape; final BoxShape highlightShape;
_InkResponseState createState() => new _InkResponseState<InkResponse>(); _InkResponseState createState() => new _InkResponseState<InkResponse>();
} }
...@@ -170,6 +170,6 @@ class InkWell extends InkResponse { ...@@ -170,6 +170,6 @@ class InkWell extends InkResponse {
onLongPress: onLongPress, onLongPress: onLongPress,
onHighlightChanged: onHighlightChanged, onHighlightChanged: onHighlightChanged,
containedInWell: true, containedInWell: true,
highlightShape: Shape.rectangle highlightShape: BoxShape.rectangle
); );
} }
...@@ -65,7 +65,7 @@ abstract class MaterialInkController { ...@@ -65,7 +65,7 @@ abstract class MaterialInkController {
InkSplash splashAt({ RenderBox referenceBox, Point position, Color color, bool containedInWell, VoidCallback onRemoved }); InkSplash splashAt({ RenderBox referenceBox, Point position, Color color, bool containedInWell, VoidCallback onRemoved });
/// Begin a highlight, coincident with the referenceBox. /// Begin a highlight, coincident with the referenceBox.
InkHighlight highlightAt({ RenderBox referenceBox, Color color, Shape shape: Shape.rectangle, VoidCallback onRemoved }); InkHighlight highlightAt({ RenderBox referenceBox, Color color, BoxShape shape: BoxShape.rectangle, VoidCallback onRemoved });
/// Add an arbitrary InkFeature to this InkController. /// Add an arbitrary InkFeature to this InkController.
void addInkFeature(InkFeature feature); void addInkFeature(InkFeature feature);
...@@ -153,7 +153,7 @@ class _MaterialState extends State<Material> { ...@@ -153,7 +153,7 @@ class _MaterialState extends State<Material> {
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
borderRadius: kMaterialEdges[config.type], borderRadius: kMaterialEdges[config.type],
boxShadow: config.elevation == 0 ? null : elevationToShadow[config.elevation], boxShadow: config.elevation == 0 ? null : elevationToShadow[config.elevation],
shape: config.type == MaterialType.circle ? Shape.circle : Shape.rectangle shape: config.type == MaterialType.circle ? BoxShape.circle : BoxShape.rectangle
), ),
child: contents child: contents
); );
...@@ -217,7 +217,7 @@ class RenderInkFeatures extends RenderProxyBox implements MaterialInkController ...@@ -217,7 +217,7 @@ class RenderInkFeatures extends RenderProxyBox implements MaterialInkController
InkHighlight highlightAt({ InkHighlight highlightAt({
RenderBox referenceBox, RenderBox referenceBox,
Color color, Color color,
Shape shape: Shape.rectangle, BoxShape shape: BoxShape.rectangle,
VoidCallback onRemoved VoidCallback onRemoved
}) { }) {
_InkHighlight highlight = new _InkHighlight( _InkHighlight highlight = new _InkHighlight(
...@@ -423,7 +423,7 @@ class _InkHighlight extends InkFeature implements InkHighlight { ...@@ -423,7 +423,7 @@ class _InkHighlight extends InkFeature implements InkHighlight {
renderer.markNeedsPaint(); renderer.markNeedsPaint();
} }
final Shape shape; final BoxShape shape;
bool get active => _active; bool get active => _active;
bool _active = true; bool _active = true;
...@@ -452,7 +452,7 @@ class _InkHighlight extends InkFeature implements InkHighlight { ...@@ -452,7 +452,7 @@ class _InkHighlight extends InkFeature implements InkHighlight {
} }
void _paintHighlight(Canvas canvas, Rect rect, paint) { void _paintHighlight(Canvas canvas, Rect rect, paint) {
if (shape == Shape.rectangle) if (shape == BoxShape.rectangle)
canvas.drawRect(rect, paint); canvas.drawRect(rect, paint);
else else
canvas.drawCircle(rect.center, _kDefaultSplashRadius, paint); canvas.drawCircle(rect.center, _kDefaultSplashRadius, paint);
......
...@@ -170,7 +170,8 @@ class _RenderSwitch extends RenderToggleable { ...@@ -170,7 +170,8 @@ class _RenderSwitch extends RenderToggleable {
super.handleEvent(event, entry); super.handleEvent(event, entry);
} }
final BoxPainter _thumbPainter = new BoxPainter(const BoxDecoration()); Color _cachedThumbColor;
BoxPainter _thumbPainter;
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
final Canvas canvas = context.canvas; final Canvas canvas = context.canvas;
...@@ -200,11 +201,14 @@ class _RenderSwitch extends RenderToggleable { ...@@ -200,11 +201,14 @@ class _RenderSwitch extends RenderToggleable {
paintRadialReaction(canvas, thumbOffset); paintRadialReaction(canvas, thumbOffset);
_thumbPainter.decoration = new BoxDecoration( if (_cachedThumbColor != thumbColor) {
backgroundColor: thumbColor, _thumbPainter = new BoxDecoration(
shape: Shape.circle, backgroundColor: thumbColor,
boxShadow: elevationToShadow[1] shape: BoxShape.circle,
); boxShadow: elevationToShadow[1]
).createBoxPainter();
_cachedThumbColor = thumbColor;
}
// The thumb contracts slightly during the animation // The thumb contracts slightly during the animation
double inset = 2.0 - (position.value - 0.5).abs() * 2.0; double inset = 2.0 - (position.value - 0.5).abs() * 2.0;
......
// 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:ui' as ui;
import 'edge_dims.dart';
export 'edge_dims.dart' show EdgeDims;
// This group of classes is intended for painting in cartesian coordinates.
abstract class Decoration {
const Decoration();
bool debugAssertValid() => true;
EdgeDims get padding => null;
Decoration lerpFrom(Decoration a, double t) => this;
Decoration lerpTo(Decoration b, double t) => b;
bool hitTest(ui.Size size, ui.Point position) => true;
bool get needsListeners => false;
void addChangeListener(ui.VoidCallback listener) { assert(false); }
void removeChangeListener(ui.VoidCallback listener) { assert(false); }
BoxPainter createBoxPainter();
String toString([String prefix = '']) => '$prefix$runtimeType';
}
abstract class BoxPainter {
void paint(ui.Canvas canvas, ui.Rect rect);
}
// 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:ui' as ui;
/// An immutable set of offsets in each of the four cardinal directions.
///
/// Typically used for an offset from each of the four sides of a box. For
/// example, the padding inside a box can be represented using this class.
class EdgeDims {
/// Constructs an EdgeDims from offsets from the top, right, bottom and left.
const EdgeDims.TRBL(this.top, this.right, this.bottom, this.left);
/// Constructs an EdgeDims where all the offsets are value.
const EdgeDims.all(double value)
: top = value, right = value, bottom = value, left = value;
/// Constructs an EdgeDims with only the given values non-zero.
const EdgeDims.only({ this.top: 0.0,
this.right: 0.0,
this.bottom: 0.0,
this.left: 0.0 });
/// Constructs an EdgeDims with symmetrical vertical and horizontal offsets.
const EdgeDims.symmetric({ double vertical: 0.0,
double horizontal: 0.0 })
: top = vertical, left = horizontal, bottom = vertical, right = horizontal;
/// The offset from the top.
final double top;
/// The offset from the right.
final double right;
/// The offset from the bottom.
final double bottom;
/// The offset from the left.
final double left;
/// Whether every dimension is non-negative.
bool get isNonNegative => top >= 0.0 && right >= 0.0 && bottom >= 0.0 && left >= 0.0;
/// The size that this edge dims would occupy with an empty interior.
ui.Size get collapsedSize => new ui.Size(left + right, top + bottom);
ui.Rect inflateRect(ui.Rect rect) {
return new ui.Rect.fromLTRB(rect.left - left, rect.top - top, rect.right + right, rect.bottom + bottom);
}
EdgeDims operator -(EdgeDims other) {
return new EdgeDims.TRBL(
top - other.top,
right - other.right,
bottom - other.bottom,
left - other.left
);
}
EdgeDims operator +(EdgeDims other) {
return new EdgeDims.TRBL(
top + other.top,
right + other.right,
bottom + other.bottom,
left + other.left
);
}
EdgeDims operator *(double other) {
return new EdgeDims.TRBL(
top * other,
right * other,
bottom * other,
left * other
);
}
EdgeDims operator /(double other) {
return new EdgeDims.TRBL(
top / other,
right / other,
bottom / other,
left / other
);
}
EdgeDims operator ~/(double other) {
return new EdgeDims.TRBL(
(top ~/ other).toDouble(),
(right ~/ other).toDouble(),
(bottom ~/ other).toDouble(),
(left ~/ other).toDouble()
);
}
EdgeDims operator %(double other) {
return new EdgeDims.TRBL(
top % other,
right % other,
bottom % other,
left % other
);
}
/// Linearly interpolate between two EdgeDims.
///
/// If either is null, this function interpolates from [EdgeDims.zero].
static EdgeDims lerp(EdgeDims a, EdgeDims b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return b * t;
if (b == null)
return a * (1.0 - t);
return new EdgeDims.TRBL(
ui.lerpDouble(a.top, b.top, t),
ui.lerpDouble(a.right, b.right, t),
ui.lerpDouble(a.bottom, b.bottom, t),
ui.lerpDouble(a.left, b.left, t)
);
}
/// An EdgeDims with zero offsets in each direction.
static const EdgeDims zero = const EdgeDims.TRBL(0.0, 0.0, 0.0, 0.0);
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! EdgeDims)
return false;
final EdgeDims typedOther = other;
return top == typedOther.top &&
right == typedOther.right &&
bottom == typedOther.bottom &&
left == typedOther.left;
}
int get hashCode {
int value = 373;
value = 37 * value + top.hashCode;
value = 37 * value + left.hashCode;
value = 37 * value + bottom.hashCode;
value = 37 * value + right.hashCode;
return value;
}
String toString() => "EdgeDims($top, $right, $bottom, $left)";
}
...@@ -13,7 +13,7 @@ import 'package:vector_math/vector_math_64.dart'; ...@@ -13,7 +13,7 @@ import 'package:vector_math/vector_math_64.dart';
import 'debug.dart'; import 'debug.dart';
import 'object.dart'; import 'object.dart';
export 'package:flutter/painting.dart' show FractionalOffset, TextBaseline; export 'package:flutter/painting.dart' show EdgeDims, FractionalOffset, TextBaseline;
// This class should only be used in debug builds // This class should only be used in debug builds
class _DebugSize extends Size { class _DebugSize extends Size {
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
...@@ -13,13 +12,13 @@ import 'box.dart'; ...@@ -13,13 +12,13 @@ import 'box.dart';
import 'debug.dart'; import 'debug.dart';
import 'object.dart'; import 'object.dart';
export 'package:flutter/src/painting/box_painter.dart';
export 'package:flutter/gestures.dart' show export 'package:flutter/gestures.dart' show
PointerEvent, PointerEvent,
PointerDownEvent, PointerDownEvent,
PointerMoveEvent, PointerMoveEvent,
PointerUpEvent, PointerUpEvent,
PointerCancelEvent; PointerCancelEvent;
export 'package:flutter/painting.dart' show Decoration, BoxDecoration;
/// A base class for render objects that resemble their children. /// A base class for render objects that resemble their children.
/// ///
...@@ -704,9 +703,11 @@ class RenderClipOval extends _RenderCustomClip<Rect> { ...@@ -704,9 +703,11 @@ class RenderClipOval extends _RenderCustomClip<Rect> {
bool hitTest(HitTestResult result, { Point position }) { bool hitTest(HitTestResult result, { Point position }) {
Rect clipBounds = _clip; Rect clipBounds = _clip;
Point center = clipBounds.center; Point center = clipBounds.center;
// convert the position to an offset from the center of the unit circle
Offset offset = new Offset((position.x - center.x) / clipBounds.width, Offset offset = new Offset((position.x - center.x) / clipBounds.width,
(position.y - center.y) / clipBounds.height); (position.y - center.y) / clipBounds.height);
if (offset.distance > 0.5) // check if the point is outside the unit circle
if (offset.distanceSquared > 0.25) // x^2 + y^2 > r^2
return false; return false;
return super.hitTest(result, position: position); return super.hitTest(result, position: position);
} }
...@@ -720,7 +721,7 @@ class RenderClipOval extends _RenderCustomClip<Rect> { ...@@ -720,7 +721,7 @@ class RenderClipOval extends _RenderCustomClip<Rect> {
} }
/// Where to paint a box decoration. /// Where to paint a box decoration.
enum BoxDecorationPosition { enum DecorationPosition {
/// Paint the box decoration behind the children. /// Paint the box decoration behind the children.
background, background,
...@@ -728,97 +729,90 @@ enum BoxDecorationPosition { ...@@ -728,97 +729,90 @@ enum BoxDecorationPosition {
foreground, foreground,
} }
/// Paints a [BoxDecoration] either before or after its child paints. /// Paints a [Decoration] either before or after its child paints.
class RenderDecoratedBox extends RenderProxyBox { class RenderDecoratedBox extends RenderProxyBox {
RenderDecoratedBox({ RenderDecoratedBox({
BoxDecoration decoration, Decoration decoration,
RenderBox child, DecorationPosition position: DecorationPosition.background,
BoxDecorationPosition position: BoxDecorationPosition.background RenderBox child
}) : _painter = new BoxPainter(decoration), }) : _decoration = decoration,
_position = position, _position = position,
super(child) { super(child) {
assert(decoration != null); assert(decoration != null);
assert(position != null); assert(position != null);
} }
/// Where to paint the box decoration. BoxPainter _painter;
BoxDecorationPosition get position => _position;
BoxDecorationPosition _position;
void set position (BoxDecorationPosition newPosition) {
assert(newPosition != null);
if (newPosition == _position)
return;
markNeedsPaint();
}
/// What decoration to paint. /// What decoration to paint.
BoxDecoration get decoration => _painter.decoration; Decoration get decoration => _decoration;
void set decoration (BoxDecoration newDecoration) { Decoration _decoration;
void set decoration (Decoration newDecoration) {
assert(newDecoration != null); assert(newDecoration != null);
if (newDecoration == _painter.decoration) if (newDecoration == _decoration)
return; return;
_removeBackgroundImageListenerIfNeeded(); _removeListenerIfNeeded();
_painter.decoration = newDecoration; _painter = null;
_addBackgroundImageListenerIfNeeded(); _decoration = newDecoration;
_addListenerIfNeeded();
markNeedsPaint(); markNeedsPaint();
} }
final BoxPainter _painter; /// Where to paint the box decoration.
DecorationPosition get position => _position;
DecorationPosition _position;
void set position (DecorationPosition newPosition) {
assert(newPosition != null);
if (newPosition == _position)
return;
_position = newPosition;
markNeedsPaint();
}
bool get _needsBackgroundImageListener { bool get _needsListeners {
return attached && return attached && _decoration.needsListeners;
_painter.decoration != null &&
_painter.decoration.backgroundImage != null;
} }
void _addBackgroundImageListenerIfNeeded() { void _addListenerIfNeeded() {
if (_needsBackgroundImageListener) if (_needsListeners)
_painter.decoration.backgroundImage.addChangeListener(markNeedsPaint); _decoration.addChangeListener(markNeedsPaint);
} }
void _removeBackgroundImageListenerIfNeeded() { void _removeListenerIfNeeded() {
if (_needsBackgroundImageListener) if (_needsListeners)
_painter.decoration.backgroundImage.removeChangeListener(markNeedsPaint); _decoration.removeChangeListener(markNeedsPaint);
} }
void attach() { void attach() {
super.attach(); super.attach();
_addBackgroundImageListenerIfNeeded(); _addListenerIfNeeded();
} }
void detach() { void detach() {
_removeBackgroundImageListenerIfNeeded(); _removeListenerIfNeeded();
super.detach(); super.detach();
} }
bool hitTestSelf(Point position) { bool hitTestSelf(Point position) {
switch (_painter.decoration.shape) { return _decoration.hitTest(size, position);
case Shape.rectangle:
// TODO(abarth): We should check the border radius.
return true;
case Shape.circle:
// Circles are inscribed into our smallest dimension.
Point center = size.center(Point.origin);
double distance = (position - center).distance;
return distance <= math.min(size.width, size.height) / 2.0;
}
} }
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
assert(size.width != null); assert(size.width != null);
assert(size.height != null); assert(size.height != null);
if (position == BoxDecorationPosition.background) _painter ??= _decoration.createBoxPainter();
if (position == DecorationPosition.background)
_painter.paint(context.canvas, offset & size); _painter.paint(context.canvas, offset & size);
super.paint(context, offset); super.paint(context, offset);
if (position == BoxDecorationPosition.foreground) if (position == DecorationPosition.foreground)
_painter.paint(context.canvas, offset & size); _painter.paint(context.canvas, offset & size);
} }
void debugDescribeSettings(List<String> settings) { void debugDescribeSettings(List<String> settings) {
super.debugDescribeSettings(settings); super.debugDescribeSettings(settings);
settings.add('decoration:'); settings.add('decoration:');
settings.addAll(_painter.decoration.toString(" ").split('\n')); settings.addAll(_decoration.toString(" ").split('\n'));
} }
} }
......
...@@ -16,11 +16,17 @@ class AnimatedBoxConstraintsValue extends AnimatedValue<BoxConstraints> { ...@@ -16,11 +16,17 @@ class AnimatedBoxConstraintsValue extends AnimatedValue<BoxConstraints> {
BoxConstraints lerp(double t) => BoxConstraints.lerp(begin, end, t); BoxConstraints lerp(double t) => BoxConstraints.lerp(begin, end, t);
} }
class AnimatedBoxDecorationValue extends AnimatedValue<BoxDecoration> { class AnimatedDecorationValue extends AnimatedValue<Decoration> {
AnimatedBoxDecorationValue(BoxDecoration begin, { BoxDecoration end, Curve curve, Curve reverseCurve }) AnimatedDecorationValue(Decoration begin, { Decoration end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve); : super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
BoxDecoration lerp(double t) => BoxDecoration.lerp(begin, end, t); Decoration lerp(double t) {
if (begin == null && end == null)
return null;
if (end == null)
return begin.lerpTo(end, t);
return end.lerpFrom(begin, t);
}
} }
class AnimatedEdgeDimsValue extends AnimatedValue<EdgeDims> { class AnimatedEdgeDimsValue extends AnimatedValue<EdgeDims> {
...@@ -62,14 +68,14 @@ class AnimatedContainer extends StatefulComponent { ...@@ -62,14 +68,14 @@ class AnimatedContainer extends StatefulComponent {
assert(margin == null || margin.isNonNegative); assert(margin == null || margin.isNonNegative);
assert(padding == null || padding.isNonNegative); assert(padding == null || padding.isNonNegative);
assert(curve != null); assert(curve != null);
assert(duration != null); assert(duration != null || decoration.debugAssertValid());
} }
final Widget child; final Widget child;
final BoxConstraints constraints; final BoxConstraints constraints;
final BoxDecoration decoration; final Decoration decoration;
final BoxDecoration foregroundDecoration; final Decoration foregroundDecoration;
final EdgeDims margin; final EdgeDims margin;
final EdgeDims padding; final EdgeDims padding;
final Matrix4 transform; final Matrix4 transform;
...@@ -84,8 +90,8 @@ class AnimatedContainer extends StatefulComponent { ...@@ -84,8 +90,8 @@ class AnimatedContainer extends StatefulComponent {
class _AnimatedContainerState extends State<AnimatedContainer> { class _AnimatedContainerState extends State<AnimatedContainer> {
AnimatedBoxConstraintsValue _constraints; AnimatedBoxConstraintsValue _constraints;
AnimatedBoxDecorationValue _decoration; AnimatedDecorationValue _decoration;
AnimatedBoxDecorationValue _foregroundDecoration; AnimatedDecorationValue _foregroundDecoration;
AnimatedEdgeDimsValue _margin; AnimatedEdgeDimsValue _margin;
AnimatedEdgeDimsValue _padding; AnimatedEdgeDimsValue _padding;
AnimatedMatrix4Value _transform; AnimatedMatrix4Value _transform;
...@@ -167,7 +173,7 @@ class _AnimatedContainerState extends State<AnimatedContainer> { ...@@ -167,7 +173,7 @@ class _AnimatedContainerState extends State<AnimatedContainer> {
} }
if (config.decoration != null) { if (config.decoration != null) {
_decoration ??= new AnimatedBoxDecorationValue(config.decoration); _decoration ??= new AnimatedDecorationValue(config.decoration);
if (_configVariable(_decoration, config.decoration)) if (_configVariable(_decoration, config.decoration))
needsAnimation = true; needsAnimation = true;
} else { } else {
...@@ -175,7 +181,7 @@ class _AnimatedContainerState extends State<AnimatedContainer> { ...@@ -175,7 +181,7 @@ class _AnimatedContainerState extends State<AnimatedContainer> {
} }
if (config.foregroundDecoration != null) { if (config.foregroundDecoration != null) {
_foregroundDecoration ??= new AnimatedBoxDecorationValue(config.foregroundDecoration); _foregroundDecoration ??= new AnimatedDecorationValue(config.foregroundDecoration);
if (_configVariable(_foregroundDecoration, config.foregroundDecoration)) if (_configVariable(_foregroundDecoration, config.foregroundDecoration))
needsAnimation = true; needsAnimation = true;
} else { } else {
......
...@@ -16,13 +16,15 @@ export 'package:flutter/rendering.dart' show ...@@ -16,13 +16,15 @@ export 'package:flutter/rendering.dart' show
BorderSide, BorderSide,
BoxConstraints, BoxConstraints,
BoxDecoration, BoxDecoration,
BoxDecorationPosition,
BoxShadow, BoxShadow,
BoxShape,
Canvas, Canvas,
Color, Color,
ColorFilter, ColorFilter,
CustomClipper, CustomClipper,
CustomPainter, CustomPainter,
Decoration,
DecorationPosition,
EdgeDims, EdgeDims,
FlexAlignItems, FlexAlignItems,
FlexDirection, FlexDirection,
...@@ -51,7 +53,6 @@ export 'package:flutter/rendering.dart' show ...@@ -51,7 +53,6 @@ export 'package:flutter/rendering.dart' show
RadialGradient, RadialGradient,
Rect, Rect,
ScrollDirection, ScrollDirection,
Shape,
Size, Size,
StyledTextSpan, StyledTextSpan,
TextAlign, TextAlign,
...@@ -135,7 +136,7 @@ class DecoratedBox extends OneChildRenderObjectWidget { ...@@ -135,7 +136,7 @@ class DecoratedBox extends OneChildRenderObjectWidget {
DecoratedBox({ DecoratedBox({
Key key, Key key,
this.decoration, this.decoration,
this.position: BoxDecorationPosition.background, this.position: DecorationPosition.background,
Widget child Widget child
}) : super(key: key, child: child) { }) : super(key: key, child: child) {
assert(decoration != null); assert(decoration != null);
...@@ -143,10 +144,10 @@ class DecoratedBox extends OneChildRenderObjectWidget { ...@@ -143,10 +144,10 @@ class DecoratedBox extends OneChildRenderObjectWidget {
} }
/// What decoration to paint. /// What decoration to paint.
final BoxDecoration decoration; final Decoration decoration;
/// Where to paint the box decoration. /// Where to paint the box decoration.
final BoxDecorationPosition position; final DecorationPosition position;
RenderDecoratedBox createRenderObject() => new RenderDecoratedBox(decoration: decoration, position: position); RenderDecoratedBox createRenderObject() => new RenderDecoratedBox(decoration: decoration, position: position);
...@@ -782,26 +783,26 @@ class Container extends StatelessComponent { ...@@ -782,26 +783,26 @@ class Container extends StatelessComponent {
}) : super(key: key) { }) : super(key: key) {
assert(margin == null || margin.isNonNegative); assert(margin == null || margin.isNonNegative);
assert(padding == null || padding.isNonNegative); assert(padding == null || padding.isNonNegative);
assert(decoration == null || decoration.shape != Shape.circle || decoration.borderRadius == null); // can't have a border radius if you're a circle assert(decoration == null || decoration.debugAssertValid());
} }
final Widget child; final Widget child;
final BoxConstraints constraints; final BoxConstraints constraints;
final BoxDecoration decoration; final Decoration decoration;
final BoxDecoration foregroundDecoration; final Decoration foregroundDecoration;
final EdgeDims margin; final EdgeDims margin;
final EdgeDims padding; final EdgeDims padding;
final Matrix4 transform; final Matrix4 transform;
final double width; final double width;
final double height; final double height;
EdgeDims get _paddingIncludingBorder { EdgeDims get _paddingIncludingDecoration {
if (decoration == null || decoration.border == null) if (decoration == null || decoration.padding == null)
return padding; return padding;
EdgeDims borderPadding = decoration.border.dimensions; EdgeDims decorationPadding = decoration.padding;
if (padding == null) if (padding == null)
return borderPadding; return decorationPadding;
return padding + borderPadding; return padding + decorationPadding;
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -810,7 +811,7 @@ class Container extends StatelessComponent { ...@@ -810,7 +811,7 @@ class Container extends StatelessComponent {
if (child == null && (width == null || height == null)) if (child == null && (width == null || height == null))
current = new ConstrainedBox(constraints: const BoxConstraints.expand()); current = new ConstrainedBox(constraints: const BoxConstraints.expand());
EdgeDims effectivePadding = _paddingIncludingBorder; EdgeDims effectivePadding = _paddingIncludingDecoration;
if (effectivePadding != null) if (effectivePadding != null)
current = new Padding(padding: effectivePadding, child: current); current = new Padding(padding: effectivePadding, child: current);
...@@ -820,7 +821,7 @@ class Container extends StatelessComponent { ...@@ -820,7 +821,7 @@ class Container extends StatelessComponent {
if (foregroundDecoration != null) { if (foregroundDecoration != null) {
current = new DecoratedBox( current = new DecoratedBox(
decoration: foregroundDecoration, decoration: foregroundDecoration,
position: BoxDecorationPosition.foreground, position: DecorationPosition.foreground,
child: current child: current
); );
} }
......
...@@ -21,6 +21,8 @@ void main() { ...@@ -21,6 +21,8 @@ void main() {
backgroundColor: new Color(0xFF0000FF) backgroundColor: new Color(0xFF0000FF)
); );
BoxDecoration actualDecoration;
tester.pumpWidget( tester.pumpWidget(
new AnimatedContainer( new AnimatedContainer(
key: key, key: key,
...@@ -30,7 +32,8 @@ void main() { ...@@ -30,7 +32,8 @@ void main() {
); );
RenderDecoratedBox box = key.currentState.context.findRenderObject(); RenderDecoratedBox box = key.currentState.context.findRenderObject();
expect(box.decoration.backgroundColor, equals(decorationA.backgroundColor)); actualDecoration = box.decoration;
expect(actualDecoration.backgroundColor, equals(decorationA.backgroundColor));
tester.pumpWidget( tester.pumpWidget(
new AnimatedContainer( new AnimatedContainer(
...@@ -41,11 +44,13 @@ void main() { ...@@ -41,11 +44,13 @@ void main() {
); );
expect(key.currentState.context.findRenderObject(), equals(box)); expect(key.currentState.context.findRenderObject(), equals(box));
expect(box.decoration.backgroundColor, equals(decorationA.backgroundColor)); actualDecoration = box.decoration;
expect(actualDecoration.backgroundColor, equals(decorationA.backgroundColor));
tester.pump(const Duration(seconds: 1)); tester.pump(const Duration(seconds: 1));
expect(box.decoration.backgroundColor, equals(decorationB.backgroundColor)); actualDecoration = box.decoration;
expect(actualDecoration.backgroundColor, equals(decorationB.backgroundColor));
}); });
}); });
......
...@@ -14,7 +14,7 @@ void main() { ...@@ -14,7 +14,7 @@ void main() {
new Container( new Container(
padding: new EdgeDims.all(50.0), padding: new EdgeDims.all(50.0),
decoration: new BoxDecoration( decoration: new BoxDecoration(
shape: Shape.circle, shape: BoxShape.circle,
border: new Border.all(width: 10.0, color: const Color(0x80FF00FF)), border: new Border.all(width: 10.0, color: const Color(0x80FF00FF)),
backgroundColor: Colors.teal[600] backgroundColor: Colors.teal[600]
) )
......
...@@ -27,7 +27,7 @@ void main() { ...@@ -27,7 +27,7 @@ void main() {
expect(element.renderObject is RenderDecoratedBox, isTrue); expect(element.renderObject is RenderDecoratedBox, isTrue);
RenderDecoratedBox renderObject = element.renderObject; RenderDecoratedBox renderObject = element.renderObject;
expect(renderObject.decoration, equals(kBoxDecorationA)); expect(renderObject.decoration, equals(kBoxDecorationA));
expect(renderObject.position, equals(BoxDecorationPosition.background)); expect(renderObject.position, equals(DecorationPosition.background));
tester.pumpWidget(new DecoratedBox(decoration: kBoxDecorationB)); tester.pumpWidget(new DecoratedBox(decoration: kBoxDecorationB));
element = tester.findElement((Element element) => element is OneChildRenderObjectElement); element = tester.findElement((Element element) => element is OneChildRenderObjectElement);
...@@ -35,7 +35,7 @@ void main() { ...@@ -35,7 +35,7 @@ void main() {
expect(element.renderObject is RenderDecoratedBox, isTrue); expect(element.renderObject is RenderDecoratedBox, isTrue);
renderObject = element.renderObject; renderObject = element.renderObject;
expect(renderObject.decoration, equals(kBoxDecorationB)); expect(renderObject.decoration, equals(kBoxDecorationB));
expect(renderObject.position, equals(BoxDecorationPosition.background)); expect(renderObject.position, equals(DecorationPosition.background));
}); });
}); });
...@@ -49,12 +49,12 @@ void main() { ...@@ -49,12 +49,12 @@ void main() {
expect(element.renderObject is RenderDecoratedBox, isTrue); expect(element.renderObject is RenderDecoratedBox, isTrue);
RenderDecoratedBox renderObject = element.renderObject; RenderDecoratedBox renderObject = element.renderObject;
expect(renderObject.decoration, equals(kBoxDecorationA)); expect(renderObject.decoration, equals(kBoxDecorationA));
expect(renderObject.position, equals(BoxDecorationPosition.background)); expect(renderObject.position, equals(DecorationPosition.background));
expect(renderObject.child, isNotNull); expect(renderObject.child, isNotNull);
expect(renderObject.child is RenderDecoratedBox, isTrue); expect(renderObject.child is RenderDecoratedBox, isTrue);
RenderDecoratedBox child = renderObject.child; RenderDecoratedBox child = renderObject.child;
expect(child.decoration, equals(kBoxDecorationB)); expect(child.decoration, equals(kBoxDecorationB));
expect(child.position, equals(BoxDecorationPosition.background)); expect(child.position, equals(DecorationPosition.background));
expect(child.child, isNull); expect(child.child, isNull);
} }
...@@ -65,7 +65,7 @@ void main() { ...@@ -65,7 +65,7 @@ void main() {
expect(element.renderObject is RenderDecoratedBox, isTrue); expect(element.renderObject is RenderDecoratedBox, isTrue);
RenderDecoratedBox renderObject = element.renderObject; RenderDecoratedBox renderObject = element.renderObject;
expect(renderObject.decoration, equals(kBoxDecorationA)); expect(renderObject.decoration, equals(kBoxDecorationA));
expect(renderObject.position, equals(BoxDecorationPosition.background)); expect(renderObject.position, equals(DecorationPosition.background));
expect(renderObject.child, isNull); expect(renderObject.child, isNull);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment