| Index: sky/sdk/lib/painting/box_painter.dart
|
| diff --git a/sky/sdk/lib/painting/box_painter.dart b/sky/sdk/lib/painting/box_painter.dart
|
| index e77d7b9ac3884bc60d9440cab6c42b301e957d70..6f3a9d6e4995a4720473d53c84ac01c96d20af4e 100644
|
| --- a/sky/sdk/lib/painting/box_painter.dart
|
| +++ b/sky/sdk/lib/painting/box_painter.dart
|
| @@ -2,10 +2,12 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +import 'dart:math' as math;
|
| import 'dart:sky' as sky;
|
| import 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path;
|
|
|
| import 'shadows.dart';
|
| +import 'package:sky/mojo/net/image_cache.dart' as image_cache;
|
|
|
| class BorderSide {
|
| const BorderSide({
|
| @@ -120,12 +122,60 @@ class RadialGradient extends Gradient {
|
| final sky.TileMode tileMode;
|
| }
|
|
|
| +enum BackgroundFit { fill, contain, cover, none, scaleDown }
|
| +
|
| +enum BackgroundRepeat { repeat, repeatX, repeatY, noRepeat }
|
| +
|
| +// TODO(jackson): We should abstract this out into a separate class
|
| +// that handles the image caching and so forth, which has callbacks
|
| +// for "size changed" and "image changed". This would also enable us
|
| +// to do animated images.
|
| +
|
| +class BackgroundImage {
|
| + final String src;
|
| + final BackgroundFit fit;
|
| + final BackgroundRepeat repeat;
|
| + BackgroundImage({
|
| + this.src,
|
| + this.fit: BackgroundFit.scaleDown,
|
| + this.repeat: BackgroundRepeat.noRepeat
|
| + }) {
|
| + image_cache.load(src, (image) {
|
| + if (image == null)
|
| + return;
|
| + _image = image;
|
| + _size = new Size(image.width.toDouble(), image.height.toDouble());
|
| + for (Function listener in _listeners) {
|
| + listener();
|
| + }
|
| + });
|
| + }
|
| +
|
| + sky.Image _image;
|
| + sky.Image get image => _image;
|
| +
|
| + Size _size;
|
| +
|
| + final List<Function> _listeners = new List<Function>();
|
| +
|
| + void addChangeListener(Function listener) {
|
| + _listeners.add(listener);
|
| + }
|
| +
|
| + void removeChangeListener(Function listener) {
|
| + _listeners.remove(listener);
|
| + }
|
| +
|
| + String toString() => 'BackgroundImage($src, $fit, $repeat)';
|
| +}
|
| +
|
| enum Shape { rectangle, circle }
|
|
|
| // This must be immutable, because we won't notice when it changes
|
| class BoxDecoration {
|
| const BoxDecoration({
|
| - this.backgroundColor, // null = don't draw background
|
| + this.backgroundColor, // null = don't draw background color
|
| + this.backgroundImage, // null = don't draw background image
|
| this.border, // null = don't draw border
|
| this.borderRadius, // null = use more efficient background drawing; note that this must be null for circles
|
| this.boxShadow, // null = don't draw shadows
|
| @@ -134,6 +184,7 @@ class BoxDecoration {
|
| });
|
|
|
| final Color backgroundColor;
|
| + final BackgroundImage backgroundImage;
|
| final double borderRadius;
|
| final Border border;
|
| final List<BoxShadow> boxShadow;
|
| @@ -144,6 +195,8 @@ class BoxDecoration {
|
| List<String> result = [];
|
| if (backgroundColor != null)
|
| result.add('${prefix}backgroundColor: $backgroundColor');
|
| + if (backgroundImage != null)
|
| + result.add('${prefix}backgroundImage: $backgroundImage');
|
| if (border != null)
|
| result.add('${prefix}border: $border');
|
| if (borderRadius != null)
|
| @@ -199,7 +252,7 @@ class BoxPainter {
|
| return _cachedBackgroundPaint;
|
| }
|
|
|
| - void paint(sky.Canvas canvas, Rect rect) {
|
| + void _paintBackgroundColor(sky.Canvas canvas, Rect rect) {
|
| if (_decoration.backgroundColor != null || _decoration.boxShadow != null ||
|
| _decoration.gradient != null) {
|
| switch (_decoration.shape) {
|
| @@ -217,54 +270,113 @@ class BoxPainter {
|
| break;
|
| }
|
| }
|
| + }
|
|
|
| - if (_decoration.border != null) {
|
| - assert(_decoration.borderRadius == null); // TODO(abarth): Implement borders with border radius.
|
| - assert(_decoration.shape == Shape.rectangle); // TODO(ianh): Implement borders on circles.
|
| + void _paintBackgroundImage(sky.Canvas canvas, Rect rect) {
|
| + if (_decoration.backgroundImage == null)
|
| + return;
|
| + sky.Image image = _decoration.backgroundImage.image;
|
| + if (image != null) {
|
| + Size bounds = rect.size;
|
| + Size imageSize = _decoration.backgroundImage._size;
|
| + Size src;
|
| + Size dst;
|
| + switch(_decoration.backgroundImage.fit) {
|
| + case BackgroundFit.fill:
|
| + src = imageSize;
|
| + dst = bounds;
|
| + break;
|
| + case BackgroundFit.contain:
|
| + src = imageSize;
|
| + if (bounds.width / bounds.height > src.width / src.height) {
|
| + dst = new Size(bounds.width, src.height * bounds.width / src.width);
|
| + } else {
|
| + dst = new Size(src.width * bounds.height / src.height, bounds.height);
|
| + }
|
| + break;
|
| + case BackgroundFit.cover:
|
| + if (bounds.width / bounds.height > imageSize.width / imageSize.height) {
|
| + src = new Size(imageSize.width, imageSize.width * bounds.height / bounds.width);
|
| + } else {
|
| + src = new Size(imageSize.height * bounds.width / bounds.height, imageSize.height);
|
| + }
|
| + dst = bounds;
|
| + break;
|
| + case BackgroundFit.none:
|
| + src = new Size(math.min(imageSize.width, bounds.width),
|
| + math.min(imageSize.height, bounds.height));
|
| + dst = src;
|
| + break;
|
| + case BackgroundFit.scaleDown:
|
| + src = imageSize;
|
| + dst = bounds;
|
| + if (src.height > dst.height) {
|
| + dst = new Size(src.width * dst.height / src.height, src.height);
|
| + }
|
| + if (src.width > dst.width) {
|
| + dst = new Size(dst.width, src.height * dst.width / src.width);
|
| + }
|
| + break;
|
| + }
|
| + canvas.drawImageRect(image, Point.origin & src, rect.topLeft & dst, new Paint());
|
| + }
|
| + }
|
|
|
| - assert(_decoration.border.top != null);
|
| - assert(_decoration.border.right != null);
|
| - assert(_decoration.border.bottom != null);
|
| - assert(_decoration.border.left != null);
|
| + void _paintBorder(sky.Canvas canvas, Rect rect) {
|
| + if (_decoration.border == null)
|
| + return;
|
|
|
| - Paint paint = new Paint();
|
| - Path path;
|
| -
|
| - paint.color = _decoration.border.top.color;
|
| - path = new Path();
|
| - path.moveTo(rect.left, rect.top);
|
| - path.lineTo(rect.left + _decoration.border.left.width, rect.top + _decoration.border.top.width);
|
| - path.lineTo(rect.right - _decoration.border.right.width, rect.top + _decoration.border.top.width);
|
| - path.lineTo(rect.right, rect.top);
|
| - path.close();
|
| - canvas.drawPath(path, paint);
|
| -
|
| - paint.color = _decoration.border.right.color;
|
| - path = new Path();
|
| - path.moveTo(rect.right, rect.top);
|
| - path.lineTo(rect.right - _decoration.border.right.width, rect.top + _decoration.border.top.width);
|
| - path.lineTo(rect.right - _decoration.border.right.width, rect.bottom - _decoration.border.bottom.width);
|
| - path.lineTo(rect.right, rect.bottom);
|
| - path.close();
|
| - canvas.drawPath(path, paint);
|
| -
|
| - paint.color = _decoration.border.bottom.color;
|
| - path = new Path();
|
| - path.moveTo(rect.right, rect.bottom);
|
| - path.lineTo(rect.right - _decoration.border.right.width, rect.bottom - _decoration.border.bottom.width);
|
| - path.lineTo(rect.left + _decoration.border.left.width, rect.bottom - _decoration.border.bottom.width);
|
| - path.lineTo(rect.left, rect.bottom);
|
| - path.close();
|
| - canvas.drawPath(path, paint);
|
| -
|
| - paint.color = _decoration.border.left.color;
|
| - path = new Path();
|
| - path.moveTo(rect.left, rect.bottom);
|
| - path.lineTo(rect.left + _decoration.border.left.width, rect.bottom - _decoration.border.bottom.width);
|
| - path.lineTo(rect.left + _decoration.border.left.width, rect.top + _decoration.border.top.width);
|
| - path.lineTo(rect.left, rect.top);
|
| - path.close();
|
| - canvas.drawPath(path, paint);
|
| - }
|
| + assert(_decoration.borderRadius == null); // TODO(abarth): Implement borders with border radius.
|
| + assert(_decoration.shape == Shape.rectangle); // TODO(ianh): Implement borders on circles.
|
| +
|
| + assert(_decoration.border.top != null);
|
| + assert(_decoration.border.right != null);
|
| + assert(_decoration.border.bottom != null);
|
| + assert(_decoration.border.left != null);
|
| +
|
| + Paint paint = new Paint();
|
| + Path path;
|
| +
|
| + paint.color = _decoration.border.top.color;
|
| + path = new Path();
|
| + path.moveTo(rect.left, rect.top);
|
| + path.lineTo(rect.left + _decoration.border.left.width, rect.top + _decoration.border.top.width);
|
| + path.lineTo(rect.right - _decoration.border.right.width, rect.top + _decoration.border.top.width);
|
| + path.lineTo(rect.right, rect.top);
|
| + path.close();
|
| + canvas.drawPath(path, paint);
|
| +
|
| + paint.color = _decoration.border.right.color;
|
| + path = new Path();
|
| + path.moveTo(rect.right, rect.top);
|
| + path.lineTo(rect.right - _decoration.border.right.width, rect.top + _decoration.border.top.width);
|
| + path.lineTo(rect.right - _decoration.border.right.width, rect.bottom - _decoration.border.bottom.width);
|
| + path.lineTo(rect.right, rect.bottom);
|
| + path.close();
|
| + canvas.drawPath(path, paint);
|
| +
|
| + paint.color = _decoration.border.bottom.color;
|
| + path = new Path();
|
| + path.moveTo(rect.right, rect.bottom);
|
| + path.lineTo(rect.right - _decoration.border.right.width, rect.bottom - _decoration.border.bottom.width);
|
| + path.lineTo(rect.left + _decoration.border.left.width, rect.bottom - _decoration.border.bottom.width);
|
| + path.lineTo(rect.left, rect.bottom);
|
| + path.close();
|
| + canvas.drawPath(path, paint);
|
| +
|
| + paint.color = _decoration.border.left.color;
|
| + path = new Path();
|
| + path.moveTo(rect.left, rect.bottom);
|
| + path.lineTo(rect.left + _decoration.border.left.width, rect.bottom - _decoration.border.bottom.width);
|
| + path.lineTo(rect.left + _decoration.border.left.width, rect.top + _decoration.border.top.width);
|
| + path.lineTo(rect.left, rect.top);
|
| + path.close();
|
| + canvas.drawPath(path, paint);
|
| + }
|
| +
|
| + void paint(sky.Canvas canvas, Rect rect) {
|
| + _paintBackgroundColor(canvas, rect);
|
| + _paintBackgroundImage(canvas, rect);
|
| + _paintBorder(canvas, rect);
|
| }
|
| }
|
|
|