mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
283 lines
9.5 KiB
Dart
283 lines
9.5 KiB
Dart
// 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 'package:flutter/foundation.dart';
|
|
import 'package:flutter/painting.dart' hide Border;
|
|
|
|
/// Border specification for [Table] widgets.
|
|
///
|
|
/// This is like [Border], with the addition of two sides: the inner horizontal
|
|
/// borders between rows and the inner vertical borders between columns.
|
|
///
|
|
/// The sides are represented by [BorderSide] objects.
|
|
@immutable
|
|
class TableBorder {
|
|
/// Creates a border for a table.
|
|
///
|
|
/// All the sides of the border default to [BorderSide.none].
|
|
const TableBorder({
|
|
this.top = BorderSide.none,
|
|
this.right = BorderSide.none,
|
|
this.bottom = BorderSide.none,
|
|
this.left = BorderSide.none,
|
|
this.horizontalInside = BorderSide.none,
|
|
this.verticalInside = BorderSide.none,
|
|
});
|
|
|
|
/// A uniform border with all sides the same color and width.
|
|
///
|
|
/// The sides default to black solid borders, one logical pixel wide.
|
|
factory TableBorder.all({
|
|
Color color = const Color(0xFF000000),
|
|
double width = 1.0,
|
|
BorderStyle style = BorderStyle.solid,
|
|
}) {
|
|
final BorderSide side = BorderSide(color: color, width: width, style: style);
|
|
return TableBorder(top: side, right: side, bottom: side, left: side, horizontalInside: side, verticalInside: side);
|
|
}
|
|
|
|
/// Creates a border for a table where all the interior sides use the same
|
|
/// styling and all the exterior sides use the same styling.
|
|
factory TableBorder.symmetric({
|
|
BorderSide inside = BorderSide.none,
|
|
BorderSide outside = BorderSide.none,
|
|
}) {
|
|
return TableBorder(
|
|
top: outside,
|
|
right: outside,
|
|
bottom: outside,
|
|
left: outside,
|
|
horizontalInside: inside,
|
|
verticalInside: inside,
|
|
);
|
|
}
|
|
|
|
/// The top side of this border.
|
|
final BorderSide top;
|
|
|
|
/// The right side of this border.
|
|
final BorderSide right;
|
|
|
|
/// The bottom side of this border.
|
|
final BorderSide bottom;
|
|
|
|
/// The left side of this border.
|
|
final BorderSide left;
|
|
|
|
/// The horizontal interior sides of this border.
|
|
final BorderSide horizontalInside;
|
|
|
|
/// The vertical interior sides of this border.
|
|
final BorderSide verticalInside;
|
|
|
|
/// The widths of the sides of this border represented as an [EdgeInsets].
|
|
///
|
|
/// This can be used, for example, with a [Padding] widget to inset a box by
|
|
/// the size of these borders.
|
|
EdgeInsets get dimensions {
|
|
return EdgeInsets.fromLTRB(left.width, top.width, right.width, bottom.width);
|
|
}
|
|
|
|
/// Whether all the sides of the border (outside and inside) are identical.
|
|
/// Uniform borders are typically more efficient to paint.
|
|
bool get isUniform {
|
|
assert(top != null);
|
|
assert(right != null);
|
|
assert(bottom != null);
|
|
assert(left != null);
|
|
assert(horizontalInside != null);
|
|
assert(verticalInside != null);
|
|
|
|
final Color topColor = top.color;
|
|
if (right.color != topColor ||
|
|
bottom.color != topColor ||
|
|
left.color != topColor ||
|
|
horizontalInside.color != topColor ||
|
|
verticalInside.color != topColor)
|
|
return false;
|
|
|
|
final double topWidth = top.width;
|
|
if (right.width != topWidth ||
|
|
bottom.width != topWidth ||
|
|
left.width != topWidth ||
|
|
horizontalInside.width != topWidth ||
|
|
verticalInside.width != topWidth)
|
|
return false;
|
|
|
|
final BorderStyle topStyle = top.style;
|
|
if (right.style != topStyle ||
|
|
bottom.style != topStyle ||
|
|
left.style != topStyle ||
|
|
horizontalInside.style != topStyle ||
|
|
verticalInside.style != topStyle)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Creates a copy of this border but with the widths scaled by the factor `t`.
|
|
///
|
|
/// The `t` argument represents the multiplicand, or the position on the
|
|
/// timeline for an interpolation from nothing to `this`, with 0.0 meaning
|
|
/// that the object returned should be the nil variant of this object, 1.0
|
|
/// meaning that no change should be applied, returning `this` (or something
|
|
/// equivalent to `this`), and other values meaning that the object should be
|
|
/// multiplied by `t`. Negative values are treated like zero.
|
|
///
|
|
/// Values for `t` are usually obtained from an [Animation<double>], such as
|
|
/// an [AnimationController].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [BorderSide.scale], which is used to implement this method.
|
|
TableBorder scale(double t) {
|
|
return TableBorder(
|
|
top: top.scale(t),
|
|
right: right.scale(t),
|
|
bottom: bottom.scale(t),
|
|
left: left.scale(t),
|
|
horizontalInside: horizontalInside.scale(t),
|
|
verticalInside: verticalInside.scale(t),
|
|
);
|
|
}
|
|
|
|
/// Linearly interpolate between two table borders.
|
|
///
|
|
/// If a border is null, it is treated as having only [BorderSide.none]
|
|
/// borders.
|
|
///
|
|
/// {@macro dart.ui.shadow.lerp}
|
|
static TableBorder lerp(TableBorder a, TableBorder b, double t) {
|
|
assert(t != null);
|
|
if (a == null && b == null)
|
|
return null;
|
|
if (a == null)
|
|
return b.scale(t);
|
|
if (b == null)
|
|
return a.scale(1.0 - t);
|
|
return TableBorder(
|
|
top: BorderSide.lerp(a.top, b.top, t),
|
|
right: BorderSide.lerp(a.right, b.right, t),
|
|
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
|
|
left: BorderSide.lerp(a.left, b.left, t),
|
|
horizontalInside: BorderSide.lerp(a.horizontalInside, b.horizontalInside, t),
|
|
verticalInside: BorderSide.lerp(a.verticalInside, b.verticalInside, t),
|
|
);
|
|
}
|
|
|
|
/// Paints the border around the given [Rect] on the given [Canvas], with the
|
|
/// given rows and columns.
|
|
///
|
|
/// Uniform borders are more efficient to paint than more complex borders.
|
|
///
|
|
/// The `rows` argument specifies the vertical positions between the rows,
|
|
/// relative to the given rectangle. For example, if the table contained two
|
|
/// rows of height 100.0 each, then `rows` would contain a single value,
|
|
/// 100.0, which is the vertical position between the two rows (relative to
|
|
/// the top edge of `rect`).
|
|
///
|
|
/// The `columns` argument specifies the horizontal positions between the
|
|
/// columns, relative to the given rectangle. For example, if the table
|
|
/// contained two columns of height 100.0 each, then `columns` would contain a
|
|
/// single value, 100.0, which is the vertical position between the two
|
|
/// columns (relative to the left edge of `rect`).
|
|
///
|
|
/// The [verticalInside] border is only drawn if there are at least two
|
|
/// columns. The [horizontalInside] border is only drawn if there are at least
|
|
/// two rows. The horizontal borders are drawn after the vertical borders.
|
|
///
|
|
/// The outer borders (in the order [top], [right], [bottom], [left], with
|
|
/// [left] above the others) are painted after the inner borders.
|
|
///
|
|
/// The paint order is particularly notable in the case of
|
|
/// partially-transparent borders.
|
|
void paint(
|
|
Canvas canvas,
|
|
Rect rect, {
|
|
@required Iterable<double> rows,
|
|
@required Iterable<double> columns,
|
|
}) {
|
|
// properties can't be null
|
|
assert(top != null);
|
|
assert(right != null);
|
|
assert(bottom != null);
|
|
assert(left != null);
|
|
assert(horizontalInside != null);
|
|
assert(verticalInside != null);
|
|
|
|
// arguments can't be null
|
|
assert(canvas != null);
|
|
assert(rect != null);
|
|
assert(rows != null);
|
|
assert(rows.isEmpty || (rows.first >= 0.0 && rows.last <= rect.height));
|
|
assert(columns != null);
|
|
assert(columns.isEmpty || (columns.first >= 0.0 && columns.last <= rect.width));
|
|
|
|
if (columns.isNotEmpty || rows.isNotEmpty) {
|
|
final Paint paint = Paint();
|
|
final Path path = Path();
|
|
|
|
if (columns.isNotEmpty) {
|
|
switch (verticalInside.style) {
|
|
case BorderStyle.solid:
|
|
paint
|
|
..color = verticalInside.color
|
|
..strokeWidth = verticalInside.width
|
|
..style = PaintingStyle.stroke;
|
|
path.reset();
|
|
for (double x in columns) {
|
|
path.moveTo(rect.left + x, rect.top);
|
|
path.lineTo(rect.left + x, rect.bottom);
|
|
}
|
|
canvas.drawPath(path, paint);
|
|
break;
|
|
case BorderStyle.none:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rows.isNotEmpty) {
|
|
switch (horizontalInside.style) {
|
|
case BorderStyle.solid:
|
|
paint
|
|
..color = horizontalInside.color
|
|
..strokeWidth = horizontalInside.width
|
|
..style = PaintingStyle.stroke;
|
|
path.reset();
|
|
for (double y in rows) {
|
|
path.moveTo(rect.left, rect.top + y);
|
|
path.lineTo(rect.right, rect.top + y);
|
|
}
|
|
canvas.drawPath(path, paint);
|
|
break;
|
|
case BorderStyle.none:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
paintBorder(canvas, rect, top: top, right: right, bottom: bottom, left: left);
|
|
}
|
|
|
|
@override
|
|
bool operator ==(dynamic other) {
|
|
if (identical(this, other))
|
|
return true;
|
|
if (runtimeType != other.runtimeType)
|
|
return false;
|
|
final TableBorder typedOther = other;
|
|
return top == typedOther.top
|
|
&& right == typedOther.right
|
|
&& bottom == typedOther.bottom
|
|
&& left == typedOther.left
|
|
&& horizontalInside == typedOther.horizontalInside
|
|
&& verticalInside == typedOther.verticalInside;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => hashValues(top, right, bottom, left, horizontalInside, verticalInside);
|
|
|
|
@override
|
|
String toString() => 'TableBorder($top, $right, $bottom, $left, $horizontalInside, $verticalInside)';
|
|
}
|