Commit 47e882a5 authored by Ian Hickson's avatar Ian Hickson

Merge pull request #2878 from Hixie/border-style-none

Support hairline borders
parents a3b17838 9fc29dbb
......@@ -64,7 +64,7 @@ class DotState extends State<Dot> {
height: config.size,
decoration: new BoxDecoration(
backgroundColor: config.color,
border: new Border.all(color: const Color(0xFF000000), width: taps.toDouble()),
border: new Border.all(width: taps.toDouble()),
shape: BoxShape.circle
),
child: config.child
......
......@@ -134,7 +134,7 @@ class SectorAppState extends State<SectorApp> {
child: new Container(
margin: new EdgeInsets.all(8.0),
decoration: new BoxDecoration(
border: new Border.all(color: new Color(0xFF000000))
border: new Border.all()
),
padding: new EdgeInsets.all(8.0),
child: new WidgetToRenderBoxAdapter(
......
......@@ -42,7 +42,7 @@ class ListDemoState extends State<ListDemo> {
_bottomSheet = scaffoldKey.currentState.showBottomSheet((BuildContext bottomSheetContext) {
return new Container(
decoration: new BoxDecoration(
border: new Border(top: new BorderSide(color: Colors.black26, width: 1.0))
border: new Border(top: new BorderSide(color: Colors.black26))
),
child: new Column(
mainAxisAlignment: MainAxisAlignment.collapse,
......
......@@ -16,7 +16,7 @@ class PersistentBottomSheetDemo extends StatelessWidget {
Scaffold.of(context).showBottomSheet((_) {
return new Container(
decoration: new BoxDecoration(
border: new Border(top: new BorderSide(color: Colors.black26, width: 1.0))
border: new Border(top: new BorderSide(color: Colors.black26))
),
child: new Padding(
padding: const EdgeInsets.all(32.0),
......
......@@ -100,7 +100,7 @@ class StockSymbolBottomSheet extends StatelessWidget {
return new Container(
padding: new EdgeInsets.all(10.0),
decoration: new BoxDecoration(
border: new Border(top: new BorderSide(color: Colors.black26, width: 1.0))
border: new Border(top: new BorderSide(color: Colors.black26))
),
child: new StockSymbolView(stock: stock)
);
......
......@@ -25,30 +25,52 @@ double _getEffectiveBorderRadius(Rect rect, double borderRadius) {
return borderRadius > shortestSide ? shortestSide : borderRadius;
}
/// The style of line to draw for a [BorderSide] in a [Border].
enum BorderStyle {
/// Skip the border.
none,
/// Draw the border as a solid line.
solid,
// if you add more, think about how they will lerp
}
/// A side of a border of a box.
class BorderSide {
const BorderSide({
this.color: const Color(0xFF000000),
this.width: 1.0
this.width: 1.0,
this.style: BorderStyle.solid
});
/// The color of this side of the border.
final Color color;
/// The width of this side of the border.
/// The width of this side of the border, in logical pixels. A
/// zero-width border is a hairline border. To omit the border
/// entirely, set the [style] to [BorderStyle.none].
final double width;
/// A black border side of zero width.
static const BorderSide none = const BorderSide(width: 0.0);
/// The style of this side of the border.
///
/// To omit a side, set [style] to [BorderStyle.none]. This skips
/// painting the border, but the border still has a [width].
final BorderStyle style;
/// A hairline black border that is not rendered.
static const BorderSide none = const BorderSide(width: 0.0, style: BorderStyle.none);
/// Creates a copy of this border but with the given fields replaced with the new values.
BorderSide copyWith({
Color color,
double width
double width,
BorderStyle style
}) {
return new BorderSide(
color: color ?? this.color,
width: width ?? this.width
width: width ?? this.width,
style: style ?? this.style
);
}
......@@ -56,9 +78,38 @@ class BorderSide {
static BorderSide lerp(BorderSide a, BorderSide b, double t) {
assert(a != null);
assert(b != null);
if (t == 0.0)
return a;
if (t == 1.0)
return b;
if (a.style == b.style) {
return new BorderSide(
color: Color.lerp(a.color, b.color, t),
width: ui.lerpDouble(a.width, b.width, t),
style: a.style // == b.style
);
}
Color colorA, colorB;
switch (a.style) {
case BorderStyle.solid:
colorA = a.color;
break;
case BorderStyle.none:
colorA = a.color.withAlpha(0x00);
break;
}
switch (b.style) {
case BorderStyle.solid:
colorB = b.color;
break;
case BorderStyle.none:
colorB = b.color.withAlpha(0x00);
break;
}
return new BorderSide(
color: Color.lerp(a.color, b.color, t),
width: ui.lerpDouble(a.width, b.width, t)
color: Color.lerp(colorA, colorB, t),
width: ui.lerpDouble(a.width, b.width, t),
style: BorderStyle.solid
);
}
......@@ -70,14 +121,15 @@ class BorderSide {
return false;
final BorderSide typedOther = other;
return color == typedOther.color &&
width == typedOther.width;
width == typedOther.width &&
style == typedOther.style;
}
@override
int get hashCode => hashValues(color, width);
int get hashCode => hashValues(color, width, style);
@override
String toString() => 'BorderSide($color, $width)';
String toString() => 'BorderSide($color, $width, $style)';
}
/// A border of a box, comprised of four sides.
......@@ -92,9 +144,10 @@ class Border {
/// A uniform border with all sides the same color and width.
factory Border.all({
Color color: const Color(0xFF000000),
double width: 1.0
double width: 1.0,
BorderStyle style: BorderStyle.solid
}) {
final BorderSide side = new BorderSide(color: color, width: width);
final BorderSide side = new BorderSide(color: color, width: width, style: style);
return new Border(top: side, right: side, bottom: side, left: side);
}
......@@ -134,6 +187,12 @@ class Border {
left.width != topWidth)
return false;
final BorderStyle topStyle = top.style;
if (right.style != topStyle ||
bottom.style != topStyle ||
left.style != topStyle)
return false;
return true;
}
......@@ -186,57 +245,99 @@ class Border {
assert(bottom != null);
assert(left != null);
Paint paint = new Paint();
Paint paint = new Paint()
..strokeWidth = 0.0; // used for hairline borders
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);
switch (top.style) {
case BorderStyle.solid:
paint.color = top.color;
path = new Path();
path.moveTo(rect.left, rect.top);
path.lineTo(rect.right, rect.top);
if (top.width == 0.0) {
paint.style = PaintingStyle.stroke;
} else {
paint.style = PaintingStyle.fill;
path.lineTo(rect.right - right.width, rect.top + top.width);
path.lineTo(rect.left + left.width, rect.top + top.width);
}
canvas.drawPath(path, paint);
break;
case BorderStyle.none: ;
}
switch (right.style) {
case BorderStyle.solid:
paint.color = right.color;
path = new Path();
path.moveTo(rect.right, rect.top);
path.lineTo(rect.right, rect.bottom);
if (right.width == 0.0) {
paint.style = PaintingStyle.stroke;
} else {
paint.style = PaintingStyle.fill;
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
path.lineTo(rect.right - right.width, rect.top + top.width);
}
canvas.drawPath(path, paint);
break;
case BorderStyle.none: ;
}
switch (bottom.style) {
case BorderStyle.solid:
paint.color = bottom.color;
path = new Path();
path.moveTo(rect.right, rect.bottom);
path.lineTo(rect.left, rect.bottom);
if (bottom.width == 0.0) {
paint.style = PaintingStyle.stroke;
} else {
paint.style = PaintingStyle.fill;
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
}
canvas.drawPath(path, paint);
break;
case BorderStyle.none: ;
}
switch (left.style) {
case BorderStyle.solid:
paint.color = left.color;
path = new Path();
path.moveTo(rect.left, rect.bottom);
path.lineTo(rect.left, rect.top);
if (right.width == 0.0) {
paint.style = PaintingStyle.stroke;
} else {
paint.style = PaintingStyle.fill;
path.lineTo(rect.left + left.width, rect.top + top.width);
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
}
canvas.drawPath(path, paint);
break;
case BorderStyle.none: ;
}
}
void _paintBorderWithRadius(Canvas canvas, Rect rect, double borderRadius) {
assert(isUniform);
Color color = top.color;
double width = top.width;
Paint paint = new Paint()
..color = top.color;
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);
double width = top.width;
if (width == 0.0) {
paint
..style = PaintingStyle.stroke
..strokeWidth = 0.0;
canvas.drawRRect(outer, paint);
} else {
RRect inner = new RRect.fromRectXY(rect.deflate(width), radius - width, radius - width);
canvas.drawDRRect(outer, inner, paint);
}
}
void _paintBorderWithCircle(Canvas canvas, Rect rect) {
......
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