Merge pull request #2875 from Hixie/border-paint
Make the border painting code reusable.
This commit is contained in:
commit
98b1f19df8
@ -13,6 +13,18 @@ import 'edge_insets.dart';
|
|||||||
|
|
||||||
export 'edge_insets.dart' show EdgeInsets;
|
export 'edge_insets.dart' show EdgeInsets;
|
||||||
|
|
||||||
|
double _getEffectiveBorderRadius(Rect rect, double borderRadius) {
|
||||||
|
assert(rect != null);
|
||||||
|
assert(borderRadius != null);
|
||||||
|
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 Canvas do any remaining clamping.
|
||||||
|
// The right long-term fix is to do layout using fixed precision
|
||||||
|
// arithmetic. (see also "_applyFloatingPointHack")
|
||||||
|
return borderRadius > shortestSide ? shortestSide : borderRadius;
|
||||||
|
}
|
||||||
|
|
||||||
/// A side of a border of a box.
|
/// A side of a border of a box.
|
||||||
class BorderSide {
|
class BorderSide {
|
||||||
const BorderSide({
|
const BorderSide({
|
||||||
@ -151,6 +163,93 @@ class Border {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void paint(Canvas canvas, Rect rect, {
|
||||||
|
BoxShape shape: BoxShape.rectangle,
|
||||||
|
double borderRadius: null
|
||||||
|
}) {
|
||||||
|
if (isUniform) {
|
||||||
|
if (borderRadius != null) {
|
||||||
|
_paintBorderWithRadius(canvas, rect, borderRadius);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (shape == BoxShape.circle) {
|
||||||
|
_paintBorderWithCircle(canvas, rect);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(borderRadius == null); // TODO(abarth): Support non-uniform rounded borders.
|
||||||
|
assert(shape == BoxShape.rectangle); // TODO(ianh): Support non-uniform borders on circles.
|
||||||
|
|
||||||
|
assert(top != null);
|
||||||
|
assert(right != null);
|
||||||
|
assert(bottom != null);
|
||||||
|
assert(left != null);
|
||||||
|
|
||||||
|
Paint paint = new Paint();
|
||||||
|
Path path;
|
||||||
|
|
||||||
|
// TODO(ianh): Handle hairline border by drawing a single line instead of a wedge
|
||||||
|
|
||||||
|
paint.color = top.color;
|
||||||
|
path = new Path();
|
||||||
|
path.moveTo(rect.left, rect.top);
|
||||||
|
path.lineTo(rect.left + left.width, rect.top + top.width);
|
||||||
|
path.lineTo(rect.right - right.width, rect.top + top.width);
|
||||||
|
path.lineTo(rect.right, rect.top);
|
||||||
|
path.close();
|
||||||
|
canvas.drawPath(path, paint);
|
||||||
|
|
||||||
|
paint.color = right.color;
|
||||||
|
path = new Path();
|
||||||
|
path.moveTo(rect.right, rect.top);
|
||||||
|
path.lineTo(rect.right - right.width, rect.top + top.width);
|
||||||
|
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
|
||||||
|
path.lineTo(rect.right, rect.bottom);
|
||||||
|
path.close();
|
||||||
|
canvas.drawPath(path, paint);
|
||||||
|
|
||||||
|
paint.color = bottom.color;
|
||||||
|
path = new Path();
|
||||||
|
path.moveTo(rect.right, rect.bottom);
|
||||||
|
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
|
||||||
|
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
|
||||||
|
path.lineTo(rect.left, rect.bottom);
|
||||||
|
path.close();
|
||||||
|
canvas.drawPath(path, paint);
|
||||||
|
|
||||||
|
paint.color = left.color;
|
||||||
|
path = new Path();
|
||||||
|
path.moveTo(rect.left, rect.bottom);
|
||||||
|
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
|
||||||
|
path.lineTo(rect.left + left.width, rect.top + top.width);
|
||||||
|
path.lineTo(rect.left, rect.top);
|
||||||
|
path.close();
|
||||||
|
canvas.drawPath(path, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _paintBorderWithRadius(Canvas canvas, Rect rect, double borderRadius) {
|
||||||
|
assert(isUniform);
|
||||||
|
Color color = top.color;
|
||||||
|
double width = top.width;
|
||||||
|
double radius = _getEffectiveBorderRadius(rect, borderRadius);
|
||||||
|
// TODO(ianh): Handle hairline borders by just drawing an RRect instead
|
||||||
|
RRect outer = new RRect.fromRectXY(rect, radius, radius);
|
||||||
|
RRect inner = new RRect.fromRectXY(rect.deflate(width), radius - width, radius - width);
|
||||||
|
canvas.drawDRRect(outer, inner, new Paint()..color = color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _paintBorderWithCircle(Canvas canvas, Rect rect) {
|
||||||
|
assert(isUniform);
|
||||||
|
double width = top.width;
|
||||||
|
Paint paint = new Paint()
|
||||||
|
..color = top.color
|
||||||
|
..strokeWidth = width
|
||||||
|
..style = PaintingStyle.stroke;
|
||||||
|
double radius = (rect.shortestSide - width) / 2.0;
|
||||||
|
canvas.drawCircle(rect.center, radius, paint);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) {
|
bool operator ==(dynamic other) {
|
||||||
if (identical(this, other))
|
if (identical(this, other))
|
||||||
@ -981,16 +1080,6 @@ class BoxDecoration extends Decoration {
|
|||||||
backgroundImage?._removeChangeListener(listener);
|
backgroundImage?._removeChangeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 Canvas do any remaining clamping.
|
|
||||||
// The right long-term fix is to do layout using fixed precision
|
|
||||||
// arithmetic. (see also "_applyFloatingPointHack")
|
|
||||||
return borderRadius > shortestSide ? shortestSide : borderRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool hitTest(Size size, Point position) {
|
bool hitTest(Size size, Point position) {
|
||||||
assert(shape != null);
|
assert(shape != null);
|
||||||
@ -1059,7 +1148,7 @@ class _BoxDecorationPainter extends BoxPainter {
|
|||||||
if (_decoration.borderRadius == null) {
|
if (_decoration.borderRadius == null) {
|
||||||
canvas.drawRect(rect, paint);
|
canvas.drawRect(rect, paint);
|
||||||
} else {
|
} else {
|
||||||
double radius = _decoration.getEffectiveBorderRadius(rect);
|
double radius = _getEffectiveBorderRadius(rect, _decoration.borderRadius);
|
||||||
canvas.drawRRect(new RRect.fromRectXY(rect, radius, radius), paint);
|
canvas.drawRRect(new RRect.fromRectXY(rect, radius, radius), paint);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1101,103 +1190,17 @@ class _BoxDecorationPainter extends BoxPainter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _paintBorder(Canvas canvas, Rect rect) {
|
|
||||||
if (_decoration.border == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_decoration.border.isUniform) {
|
|
||||||
if (_decoration.borderRadius != null) {
|
|
||||||
_paintBorderWithRadius(canvas, rect);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_decoration.shape == BoxShape.circle) {
|
|
||||||
_paintBorderWithCircle(canvas, rect);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(_decoration.borderRadius == null); // TODO(abarth): Support non-uniform rounded borders.
|
|
||||||
assert(_decoration.shape == BoxShape.rectangle); // TODO(ianh): Support non-uniform 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 _paintBorderWithRadius(Canvas canvas, Rect rect) {
|
|
||||||
assert(_decoration.border.isUniform);
|
|
||||||
assert(_decoration.shape == BoxShape.rectangle);
|
|
||||||
Color color = _decoration.border.top.color;
|
|
||||||
double width = _decoration.border.top.width;
|
|
||||||
double radius = _decoration.getEffectiveBorderRadius(rect);
|
|
||||||
|
|
||||||
RRect outer = new RRect.fromRectXY(rect, radius, radius);
|
|
||||||
RRect inner = new RRect.fromRectXY(rect.deflate(width), radius - width, radius - width);
|
|
||||||
canvas.drawDRRect(outer, inner, new Paint()..color = color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _paintBorderWithCircle(Canvas canvas, Rect rect) {
|
|
||||||
assert(_decoration.border.isUniform);
|
|
||||||
assert(_decoration.shape == BoxShape.circle);
|
|
||||||
assert(_decoration.borderRadius == null);
|
|
||||||
double width = _decoration.border.top.width;
|
|
||||||
if (width <= 0.0)
|
|
||||||
return;
|
|
||||||
Paint paint = new Paint()
|
|
||||||
..color = _decoration.border.top.color
|
|
||||||
..strokeWidth = width
|
|
||||||
..style = PaintingStyle.stroke;
|
|
||||||
Point center = rect.center;
|
|
||||||
double radius = (rect.shortestSide - width) / 2.0;
|
|
||||||
canvas.drawCircle(center, radius, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Paint the box decoration into the given location on the given canvas
|
/// Paint the box decoration into the given location on the given canvas
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Rect rect) {
|
void paint(Canvas canvas, Rect rect) {
|
||||||
_paintShadows(canvas, rect);
|
_paintShadows(canvas, rect);
|
||||||
_paintBackgroundColor(canvas, rect);
|
_paintBackgroundColor(canvas, rect);
|
||||||
_paintBackgroundImage(canvas, rect);
|
_paintBackgroundImage(canvas, rect);
|
||||||
_paintBorder(canvas, rect);
|
_decoration.border?.paint(
|
||||||
|
canvas,
|
||||||
|
rect,
|
||||||
|
shape: _decoration.shape,
|
||||||
|
borderRadius: _decoration.borderRadius
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user