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

Merge pull request #781 from Hixie/box_decoration

Make BoxDecoration replaceable.
parents 0fe72b17 2afa87df
...@@ -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) {
_thumbPainter = new BoxDecoration(
backgroundColor: thumbColor, backgroundColor: thumbColor,
shape: Shape.circle, shape: BoxShape.circle,
boxShadow: elevationToShadow[1] 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;
......
...@@ -8,150 +8,10 @@ import 'dart:ui' as ui; ...@@ -8,150 +8,10 @@ import 'dart:ui' as ui;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'basic_types.dart'; import 'basic_types.dart';
import 'decoration.dart';
import 'edge_dims.dart';
/// An immutable set of offsets in each of the four cardinal directions. export 'edge_dims.dart' show EdgeDims;
///
/// 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.
Size get collapsedSize => new Size(left + right, top + bottom);
Rect inflateRect(Rect rect) {
return new 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)";
}
/// A side of a border of a box /// A side of a border of a box
class BorderSide { class BorderSide {
...@@ -602,7 +462,7 @@ Iterable<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, Im ...@@ -602,7 +462,7 @@ Iterable<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, Im
} }
} }
/// Paint an image into the given rectangle in the canvas /// Paint an image into the given rectangle in the canvas.
void paintImage({ void paintImage({
Canvas canvas, Canvas canvas,
Rect rect, Rect rect,
...@@ -697,6 +557,48 @@ void paintImage({ ...@@ -697,6 +557,48 @@ void paintImage({
canvas.restore(); canvas.restore();
} }
/// An offset that's expressed as a fraction of a Size.
///
/// FractionalOffset(1.0, 0.0) represents the top right of the Size,
/// FractionalOffset(0.0, 1.0) represents the bottom left of the Size,
class FractionalOffset {
const FractionalOffset(this.x, this.y);
final double x;
final double y;
FractionalOffset operator -(FractionalOffset other) {
return new FractionalOffset(x - other.x, y - other.y);
}
FractionalOffset operator +(FractionalOffset other) {
return new FractionalOffset(x + other.x, y + other.y);
}
FractionalOffset operator *(double other) {
return new FractionalOffset(x * other, y * other);
}
bool operator ==(dynamic other) {
if (other is! FractionalOffset)
return false;
final FractionalOffset typedOther = other;
return x == typedOther.x &&
y == typedOther.y;
}
int get hashCode {
int value = 373;
value = 37 * value + x.hashCode;
value = 37 * value + y.hashCode;
return value;
}
static FractionalOffset lerp(FractionalOffset a, FractionalOffset b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return new FractionalOffset(b.x * t, b.y * t);
if (b == null)
return new FractionalOffset(b.x * (1.0 - t), b.y * (1.0 - t));
return new FractionalOffset(ui.lerpDouble(a.x, b.x, t), ui.lerpDouble(a.y, b.y, t));
}
String toString() => '$runtimeType($x, $y)';
}
/// A background image for a box. /// A background image for a box.
class BackgroundImage { class BackgroundImage {
BackgroundImage({ BackgroundImage({
...@@ -735,11 +637,10 @@ class BackgroundImage { ...@@ -735,11 +637,10 @@ class BackgroundImage {
final ImageResource _imageResource; final ImageResource _imageResource;
final List<VoidCallback> _listeners = final List<VoidCallback> _listeners = <VoidCallback>[];
new List<VoidCallback>();
/// Call listener when the background images changes (e.g., arrives from the network). /// Call listener when the background images changes (e.g., arrives from the network).
void addChangeListener(VoidCallback listener) { void _addChangeListener(VoidCallback listener) {
// We add the listener to the _imageResource first so that the first change // We add the listener to the _imageResource first so that the first change
// listener doesn't get callback synchronously if the image resource is // listener doesn't get callback synchronously if the image resource is
// already resolved. // already resolved.
...@@ -749,7 +650,7 @@ class BackgroundImage { ...@@ -749,7 +650,7 @@ class BackgroundImage {
} }
/// No longer call listener when the background image changes. /// No longer call listener when the background image changes.
void removeChangeListener(VoidCallback listener) { void _removeChangeListener(VoidCallback listener) {
_listeners.remove(listener); _listeners.remove(listener);
// We need to remove ourselves as listeners from the _imageResource so that // We need to remove ourselves as listeners from the _imageResource so that
// we're not kept alive by the image_cache. // we're not kept alive by the image_cache.
...@@ -795,18 +696,22 @@ class BackgroundImage { ...@@ -795,18 +696,22 @@ class BackgroundImage {
String toString() => 'BackgroundImage($fit, $repeat)'; String toString() => 'BackgroundImage($fit, $repeat)';
} }
// TODO(abarth): Rename to BoxShape? /// The shape to use when rendering a BoxDecoration.
/// A 2D geometrical shape enum BoxShape {
enum Shape { /// An axis-aligned, 2D rectangle. May have rounded corners. The edges of the
/// An axis-aligned, 2D rectangle /// rectangle will match the edges of the box into which the BoxDecoration is
/// painted.
rectangle, rectangle,
/// A 2D locus of points equidistant from a single point /// A circle centered in the middle of the box into which the BoxDecoration is
/// painted. The diameter of the circle is the shortest dimension of the box,
/// either the width of the height, such that the circle touches the edges of
/// the box.
circle circle
} }
/// An immutable description of how to paint a box /// An immutable description of how to paint a box
class BoxDecoration { class BoxDecoration extends Decoration {
const BoxDecoration({ const BoxDecoration({
this.backgroundColor, // null = don't draw background color this.backgroundColor, // null = don't draw background color
this.backgroundImage, // null = don't draw background image this.backgroundImage, // null = don't draw background image
...@@ -814,9 +719,15 @@ class BoxDecoration { ...@@ -814,9 +719,15 @@ class BoxDecoration {
this.borderRadius, // null = use more efficient background drawing; note that this must be null for circles this.borderRadius, // null = use more efficient background drawing; note that this must be null for circles
this.boxShadow, // null = don't draw shadows this.boxShadow, // null = don't draw shadows
this.gradient, // null = don't allocate gradient objects this.gradient, // null = don't allocate gradient objects
this.shape: Shape.rectangle this.shape: BoxShape.rectangle
}); });
bool debugAssertValid() {
assert(shape != BoxShape.circle ||
borderRadius == null); // can't have a border radius if you're a circle
return super.debugAssertValid();
}
/// The color to fill in the background of the box /// The color to fill in the background of the box
/// ///
/// The color is filled into the shape of the box (e.g., either a rectangle, /// The color is filled into the shape of the box (e.g., either a rectangle,
...@@ -841,9 +752,9 @@ class BoxDecoration { ...@@ -841,9 +752,9 @@ class BoxDecoration {
final Gradient gradient; final Gradient gradient;
/// The shape to fill the background color into and to cast as a shadow /// The shape to fill the background color into and to cast as a shadow
final Shape shape; final BoxShape shape;
/// Returns a new box decoration that is scalled by the given factor /// Returns a new box decoration that is scaled by the given factor
BoxDecoration scale(double factor) { BoxDecoration scale(double factor) {
// TODO(abarth): Scale ALL the things. // TODO(abarth): Scale ALL the things.
return new BoxDecoration( return new BoxDecoration(
...@@ -857,7 +768,33 @@ class BoxDecoration { ...@@ -857,7 +768,33 @@ class BoxDecoration {
); );
} }
/// Linearly interpolate between two box decorations double getEffectiveBorderRadius(Rect rect) {
double shortestSide = rect.shortestSide;
// In principle, we should use shortestSide / 2.0, but we don't want to
// run into floating point rounding errors. Instead, we just use
// shortestSide and let ui.Canvas do any remaining clamping.
return borderRadius > shortestSide ? shortestSide : borderRadius;
}
bool hitTest(Size size, Point position) {
assert(shape != null);
assert((Point.origin & size).contains(position));
switch (shape) {
case BoxShape.rectangle:
if (borderRadius != null) {
ui.RRect bounds = new ui.RRect.fromRectXY(Point.origin & size, borderRadius, borderRadius);
return bounds.contains(position);
}
return true;
case BoxShape.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;
}
}
/// Linearly interpolate between two box decorations.
/// ///
/// Interpolates each parameter of the box decoration separately. /// Interpolates each parameter of the box decoration separately.
static BoxDecoration lerp(BoxDecoration a, BoxDecoration b, double t) { static BoxDecoration lerp(BoxDecoration a, BoxDecoration b, double t) {
...@@ -879,6 +816,18 @@ class BoxDecoration { ...@@ -879,6 +816,18 @@ class BoxDecoration {
); );
} }
BoxDecoration lerpFrom(Decoration a, double t) {
if (a is! BoxDecoration)
return BoxDecoration.lerp(null, this, t);
return BoxDecoration.lerp(a, this, t);
}
BoxDecoration lerpTo(Decoration b, double t) {
if (b is! BoxDecoration)
return BoxDecoration.lerp(this, null, t);
return BoxDecoration.lerp(this, b, t);
}
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
if (identical(this, other)) if (identical(this, other))
return true; return true;
...@@ -920,30 +869,32 @@ class BoxDecoration { ...@@ -920,30 +869,32 @@ class BoxDecoration {
result.add('${prefix}boxShadow: ${boxShadow.map((BoxShadow shadow) => shadow.toString())}'); result.add('${prefix}boxShadow: ${boxShadow.map((BoxShadow shadow) => shadow.toString())}');
if (gradient != null) if (gradient != null)
result.add('${prefix}gradient: $gradient'); result.add('${prefix}gradient: $gradient');
if (shape != Shape.rectangle) if (shape != BoxShape.rectangle)
result.add('${prefix}shape: $shape'); result.add('${prefix}shape: $shape');
if (result.isEmpty) if (result.isEmpty)
return '$prefix<no decorations specified>'; return '$prefix<no decorations specified>';
return result.join('\n'); return result.join('\n');
} }
bool get needsListeners => backgroundImage != null;
void addChangeListener(VoidCallback listener) {
backgroundImage?._addChangeListener(listener);
}
void removeChangeListener(VoidCallback listener) {
backgroundImage?._removeChangeListener(listener);
}
_BoxDecorationPainter createBoxPainter() => new _BoxDecorationPainter(this);
} }
/// An object that paints a [BoxDecoration] into a canvas /// An object that paints a [BoxDecoration] into a canvas
class BoxPainter { class _BoxDecorationPainter extends BoxPainter {
BoxPainter(BoxDecoration decoration) : _decoration = decoration { _BoxDecorationPainter(this._decoration) {
assert(decoration != null); assert(_decoration != null);
} }
BoxDecoration _decoration; final BoxDecoration _decoration;
/// The box decoration to paint
BoxDecoration get decoration => _decoration;
void set decoration (BoxDecoration value) {
assert(value != null);
if (value == _decoration)
return;
_decoration = value;
_cachedBackgroundPaint = null;
}
Paint _cachedBackgroundPaint; Paint _cachedBackgroundPaint;
Paint get _backgroundPaint { Paint get _backgroundPaint {
...@@ -981,27 +932,19 @@ class BoxPainter { ...@@ -981,27 +932,19 @@ class BoxPainter {
return hasUniformWidth; return hasUniformWidth;
} }
double _getEffectiveBorderRadius(Rect rect) {
double shortestSide = rect.shortestSide;
// In principle, we should use shortestSide / 2.0, but we don't want to
// run into floating point rounding errors. Instead, we just use
// shortestSide and let ui.Canvas do any remaining clamping.
return _decoration.borderRadius > shortestSide ? shortestSide : _decoration.borderRadius;
}
void _paintBox(ui.Canvas canvas, Rect rect, Paint paint) { void _paintBox(ui.Canvas canvas, Rect rect, Paint paint) {
switch (_decoration.shape) { switch (_decoration.shape) {
case Shape.circle: case BoxShape.circle:
assert(_decoration.borderRadius == null); assert(_decoration.borderRadius == null);
Point center = rect.center; Point center = rect.center;
double radius = rect.shortestSide / 2.0; double radius = rect.shortestSide / 2.0;
canvas.drawCircle(center, radius, paint); canvas.drawCircle(center, radius, paint);
break; break;
case Shape.rectangle: case BoxShape.rectangle:
if (_decoration.borderRadius == null) { if (_decoration.borderRadius == null) {
canvas.drawRect(rect, paint); canvas.drawRect(rect, paint);
} else { } else {
double radius = _getEffectiveBorderRadius(rect); double radius = _decoration.getEffectiveBorderRadius(rect);
canvas.drawRRect(new ui.RRect.fromRectXY(rect, radius, radius), paint); canvas.drawRRect(new ui.RRect.fromRectXY(rect, radius, radius), paint);
} }
break; break;
...@@ -1053,14 +996,14 @@ class BoxPainter { ...@@ -1053,14 +996,14 @@ class BoxPainter {
_paintBorderWithRadius(canvas, rect); _paintBorderWithRadius(canvas, rect);
return; return;
} }
if (_decoration.shape == Shape.circle) { if (_decoration.shape == BoxShape.circle) {
_paintBorderWithCircle(canvas, rect); _paintBorderWithCircle(canvas, rect);
return; return;
} }
} }
assert(_decoration.borderRadius == null); // TODO(abarth): Support non-uniform rounded borders. assert(_decoration.borderRadius == null); // TODO(abarth): Support non-uniform rounded borders.
assert(_decoration.shape == Shape.rectangle); // TODO(ianh): Support non-uniform borders on circles. assert(_decoration.shape == BoxShape.rectangle); // TODO(ianh): Support non-uniform borders on circles.
assert(_decoration.border.top != null); assert(_decoration.border.top != null);
assert(_decoration.border.right != null); assert(_decoration.border.right != null);
...@@ -1109,10 +1052,10 @@ class BoxPainter { ...@@ -1109,10 +1052,10 @@ class BoxPainter {
void _paintBorderWithRadius(ui.Canvas canvas, Rect rect) { void _paintBorderWithRadius(ui.Canvas canvas, Rect rect) {
assert(_hasUniformBorder); assert(_hasUniformBorder);
assert(_decoration.shape == Shape.rectangle); assert(_decoration.shape == BoxShape.rectangle);
Color color = _decoration.border.top.color; Color color = _decoration.border.top.color;
double width = _decoration.border.top.width; double width = _decoration.border.top.width;
double radius = _getEffectiveBorderRadius(rect); double radius = _decoration.getEffectiveBorderRadius(rect);
ui.RRect outer = new ui.RRect.fromRectXY(rect, radius, radius); ui.RRect outer = new ui.RRect.fromRectXY(rect, radius, radius);
ui.RRect inner = new ui.RRect.fromRectXY(rect.deflate(width), radius - width, radius - width); ui.RRect inner = new ui.RRect.fromRectXY(rect.deflate(width), radius - width, radius - width);
...@@ -1121,12 +1064,11 @@ class BoxPainter { ...@@ -1121,12 +1064,11 @@ class BoxPainter {
void _paintBorderWithCircle(ui.Canvas canvas, Rect rect) { void _paintBorderWithCircle(ui.Canvas canvas, Rect rect) {
assert(_hasUniformBorder); assert(_hasUniformBorder);
assert(_decoration.shape == Shape.circle); assert(_decoration.shape == BoxShape.circle);
assert(_decoration.borderRadius == null); assert(_decoration.borderRadius == null);
double width = _decoration.border.top.width; double width = _decoration.border.top.width;
if (width <= 0.0) { if (width <= 0.0)
return; return;
}
Paint paint = new Paint() Paint paint = new Paint()
..color = _decoration.border.top.color ..color = _decoration.border.top.color
..strokeWidth = width ..strokeWidth = width
...@@ -1144,45 +1086,3 @@ class BoxPainter { ...@@ -1144,45 +1086,3 @@ class BoxPainter {
_paintBorder(canvas, rect); _paintBorder(canvas, rect);
} }
} }
/// An offset that's expressed as a fraction of a Size.
///
/// FractionalOffset(1.0, 0.0) represents the top right of the Size,
/// FractionalOffset(0.0, 1.0) represents the bottom left of the Size,
class FractionalOffset {
const FractionalOffset(this.x, this.y);
final double x;
final double y;
FractionalOffset operator -(FractionalOffset other) {
return new FractionalOffset(x - other.x, y - other.y);
}
FractionalOffset operator +(FractionalOffset other) {
return new FractionalOffset(x + other.x, y + other.y);
}
FractionalOffset operator *(double other) {
return new FractionalOffset(x * other, y * other);
}
bool operator ==(dynamic other) {
if (other is! FractionalOffset)
return false;
final FractionalOffset typedOther = other;
return x == typedOther.x &&
y == typedOther.y;
}
int get hashCode {
int value = 373;
value = 37 * value + x.hashCode;
value = 37 * value + y.hashCode;
return value;
}
static FractionalOffset lerp(FractionalOffset a, FractionalOffset b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return new FractionalOffset(b.x * t, b.y * t);
if (b == null)
return new FractionalOffset(b.x * (1.0 - t), b.y * (1.0 - t));
return new FractionalOffset(ui.lerpDouble(a.x, b.x, t), ui.lerpDouble(a.y, b.y, t));
}
String toString() => '$runtimeType($x, $y)';
}
// 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