Commit 1a0bdb96 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add RTL support to Table (#12225)

Also, fix asserts in TableBorder#paint to match documentation.

Fixes #12009
parent fe27f864
...@@ -583,7 +583,7 @@ class DataTable extends StatelessWidget { ...@@ -583,7 +583,7 @@ class DataTable extends StatelessWidget {
return new Table( return new Table(
columnWidths: tableColumns.asMap(), columnWidths: tableColumns.asMap(),
children: tableRows children: tableRows,
); );
} }
} }
......
...@@ -298,7 +298,7 @@ class RenderStack extends RenderBox ...@@ -298,7 +298,7 @@ class RenderStack extends RenderBox
RenderStack({ RenderStack({
List<RenderBox> children, List<RenderBox> children,
FractionalOffsetGeometry alignment: FractionalOffsetDirectional.topStart, FractionalOffsetGeometry alignment: FractionalOffsetDirectional.topStart,
@required TextDirection textDirection, TextDirection textDirection,
StackFit fit: StackFit.loose, StackFit fit: StackFit.loose,
Overflow overflow: Overflow.clip, Overflow overflow: Overflow.clip,
}) : assert(alignment != null), }) : assert(alignment != null),
...@@ -569,7 +569,7 @@ class RenderIndexedStack extends RenderStack { ...@@ -569,7 +569,7 @@ class RenderIndexedStack extends RenderStack {
RenderIndexedStack({ RenderIndexedStack({
List<RenderBox> children, List<RenderBox> children,
FractionalOffsetGeometry alignment: FractionalOffsetDirectional.topStart, FractionalOffsetGeometry alignment: FractionalOffsetDirectional.topStart,
@required TextDirection textDirection, TextDirection textDirection,
int index: 0, int index: 0,
}) : _index = index, super( }) : _index = index, super(
children: children, children: children,
......
...@@ -359,6 +359,7 @@ class RenderTable extends RenderBox { ...@@ -359,6 +359,7 @@ class RenderTable extends RenderBox {
int rows, int rows,
Map<int, TableColumnWidth> columnWidths, Map<int, TableColumnWidth> columnWidths,
TableColumnWidth defaultColumnWidth: const FlexColumnWidth(1.0), TableColumnWidth defaultColumnWidth: const FlexColumnWidth(1.0),
@required TextDirection textDirection,
TableBorder border, TableBorder border,
List<Decoration> rowDecorations, List<Decoration> rowDecorations,
ImageConfiguration configuration: ImageConfiguration.empty, ImageConfiguration configuration: ImageConfiguration.empty,
...@@ -370,7 +371,9 @@ class RenderTable extends RenderBox { ...@@ -370,7 +371,9 @@ class RenderTable extends RenderBox {
assert(rows == null || rows >= 0), assert(rows == null || rows >= 0),
assert(rows == null || children == null), assert(rows == null || children == null),
assert(defaultColumnWidth != null), assert(defaultColumnWidth != null),
assert(configuration != null) { assert(textDirection != null),
assert(configuration != null),
_textDirection = textDirection {
_columns = columns ?? (children != null && children.isNotEmpty ? children.first.length : 0); _columns = columns ?? (children != null && children.isNotEmpty ? children.first.length : 0);
_rows = rows ?? 0; _rows = rows ?? 0;
_children = <RenderBox>[]..length = _columns * _rows; _children = <RenderBox>[]..length = _columns * _rows;
...@@ -489,6 +492,17 @@ class RenderTable extends RenderBox { ...@@ -489,6 +492,17 @@ class RenderTable extends RenderBox {
markNeedsLayout(); markNeedsLayout();
} }
/// The direction in which the columns are ordered.
TextDirection get textDirection => _textDirection;
TextDirection _textDirection;
set textDirection(TextDirection value) {
assert(value != null);
if (_textDirection == value)
return;
_textDirection = value;
markNeedsLayout();
}
/// The style to use when painting the boundary and interior divisions of the table. /// The style to use when painting the boundary and interior divisions of the table.
TableBorder get border => _border; TableBorder get border => _border;
TableBorder _border; TableBorder _border;
...@@ -977,6 +991,8 @@ class RenderTable extends RenderBox { ...@@ -977,6 +991,8 @@ class RenderTable extends RenderBox {
@override @override
void performLayout() { void performLayout() {
final int rows = this.rows;
final int columns = this.columns;
assert(_children.length == rows * columns); assert(_children.length == rows * columns);
if (rows * columns == 0) { if (rows * columns == 0) {
// TODO(ianh): if columns is zero, this should be zero width // TODO(ianh): if columns is zero, this should be zero width
...@@ -986,12 +1002,25 @@ class RenderTable extends RenderBox { ...@@ -986,12 +1002,25 @@ class RenderTable extends RenderBox {
} }
final List<double> widths = _computeColumnWidths(constraints); final List<double> widths = _computeColumnWidths(constraints);
final List<double> positions = new List<double>(columns); final List<double> positions = new List<double>(columns);
_rowTops.clear(); double tableWidth;
positions[0] = 0.0; switch (textDirection) {
for (int x = 1; x < columns; x += 1) case TextDirection.rtl:
positions[x] = positions[x-1] + widths[x-1]; positions[columns - 1] = 0.0;
_columnLefts = positions; for (int x = columns - 2; x >= 0; x -= 1)
positions[x] = positions[x+1] + widths[x+1];
_columnLefts = positions.reversed.toList();
tableWidth = positions.first + widths.first;
break;
case TextDirection.ltr:
positions[0] = 0.0;
for (int x = 1; x < columns; x += 1)
positions[x] = positions[x-1] + widths[x-1];
_columnLefts = positions;
tableWidth = positions.last + widths.last;
break;
}
assert(!positions.any((double value) => value == null)); assert(!positions.any((double value) => value == null));
_rowTops.clear();
_baselineDistance = null; _baselineDistance = null;
// then, lay out each row // then, lay out each row
double rowTop = 0.0; double rowTop = 0.0;
...@@ -1070,7 +1099,7 @@ class RenderTable extends RenderBox { ...@@ -1070,7 +1099,7 @@ class RenderTable extends RenderBox {
rowTop += rowHeight; rowTop += rowHeight;
} }
_rowTops.add(rowTop); _rowTops.add(rowTop);
size = constraints.constrain(new Size(positions.last + widths.last, rowTop)); size = constraints.constrain(new Size(tableWidth, rowTop));
assert(_rowTops.length == rows + 1); assert(_rowTops.length == rows + 1);
} }
...@@ -1119,7 +1148,13 @@ class RenderTable extends RenderBox { ...@@ -1119,7 +1148,13 @@ class RenderTable extends RenderBox {
} }
assert(_rows == _rowTops.length - 1); assert(_rows == _rowTops.length - 1);
assert(_columns == _columnLefts.length); assert(_columns == _columnLefts.length);
border?.paint(context.canvas, offset & size, rows: _rowTops, columns: _columnLefts); if (border != null) {
// The border rect might not fill the entire height of this render object
// if the rows underflow. We always force the columns to fill the width of
// the render object, which means the columns cannot underflow.
final Rect borderRect = new Rect.fromLTWH(offset.dx, offset.dy, size.width, _rowTops.last);
border?.paint(context.canvas, borderRect, rows: _rowTops, columns: _columnLefts);
}
} }
@override @override
......
...@@ -198,9 +198,9 @@ class TableBorder { ...@@ -198,9 +198,9 @@ class TableBorder {
assert(rows.first == 0.0); assert(rows.first == 0.0);
assert(rows.last == rect.height); assert(rows.last == rect.height);
assert(columns != null); assert(columns != null);
assert(columns.length >= 2); assert(columns.isNotEmpty);
assert(columns.first == 0.0); assert(columns.first == 0.0);
assert(columns.last == rect.width); assert(columns.last < rect.width);
final Paint paint = new Paint(); final Paint paint = new Paint();
final Path path = new Path(); final Path path = new Path();
......
...@@ -7,6 +7,7 @@ import 'dart:collection'; ...@@ -7,6 +7,7 @@ import 'dart:collection';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'debug.dart'; import 'debug.dart';
import 'framework.dart'; import 'framework.dart';
import 'image.dart'; import 'image.dart';
...@@ -97,6 +98,7 @@ class Table extends RenderObjectWidget { ...@@ -97,6 +98,7 @@ class Table extends RenderObjectWidget {
this.children: const <TableRow>[], this.children: const <TableRow>[],
this.columnWidths, this.columnWidths,
this.defaultColumnWidth: const FlexColumnWidth(1.0), this.defaultColumnWidth: const FlexColumnWidth(1.0),
this.textDirection,
this.border, this.border,
this.defaultVerticalAlignment: TableCellVerticalAlignment.top, this.defaultVerticalAlignment: TableCellVerticalAlignment.top,
this.textBaseline this.textBaseline
...@@ -179,6 +181,11 @@ class Table extends RenderObjectWidget { ...@@ -179,6 +181,11 @@ class Table extends RenderObjectWidget {
/// `columnWidths[i]` is null. /// `columnWidths[i]` is null.
final TableColumnWidth defaultColumnWidth; final TableColumnWidth defaultColumnWidth;
/// The direction in which the columns are ordered.
///
/// Defaults to the ambient [Directionality].
final TextDirection textDirection;
/// The style to use when painting the boundary and interior divisions of the table. /// The style to use when painting the boundary and interior divisions of the table.
final TableBorder border; final TableBorder border;
...@@ -200,6 +207,7 @@ class Table extends RenderObjectWidget { ...@@ -200,6 +207,7 @@ class Table extends RenderObjectWidget {
rows: children.length, rows: children.length,
columnWidths: columnWidths, columnWidths: columnWidths,
defaultColumnWidth: defaultColumnWidth, defaultColumnWidth: defaultColumnWidth,
textDirection: textDirection ?? Directionality.of(context),
border: border, border: border,
rowDecorations: _rowDecorations, rowDecorations: _rowDecorations,
configuration: createLocalImageConfiguration(context), configuration: createLocalImageConfiguration(context),
...@@ -215,6 +223,7 @@ class Table extends RenderObjectWidget { ...@@ -215,6 +223,7 @@ class Table extends RenderObjectWidget {
renderObject renderObject
..columnWidths = columnWidths ..columnWidths = columnWidths
..defaultColumnWidth = defaultColumnWidth ..defaultColumnWidth = defaultColumnWidth
..textDirection = textDirection ?? Directionality.of(context)
..border = border ..border = border
..rowDecorations = _rowDecorations ..rowDecorations = _rowDecorations
..configuration = createLocalImageConfiguration(context) ..configuration = createLocalImageConfiguration(context)
......
...@@ -16,7 +16,7 @@ RenderBox sizedBox(double width, double height) { ...@@ -16,7 +16,7 @@ RenderBox sizedBox(double width, double height) {
void main() { void main() {
test('Table control test; tight', () { test('Table control test; tight', () {
RenderTable table; RenderTable table;
layout(table = new RenderTable()); layout(table = new RenderTable(textDirection: TextDirection.ltr));
expect(table.size.width, equals(800.0)); expect(table.size.width, equals(800.0));
expect(table.size.height, equals(600.0)); expect(table.size.height, equals(600.0));
...@@ -41,7 +41,7 @@ void main() { ...@@ -41,7 +41,7 @@ void main() {
test('Table control test; loose', () { test('Table control test; loose', () {
RenderTable table; RenderTable table;
layout(new RenderPositionedBox(child: table = new RenderTable())); layout(new RenderPositionedBox(child: table = new RenderTable(textDirection: TextDirection.ltr)));
expect(table.size, equals(const Size(0.0, 0.0))); expect(table.size, equals(const Size(0.0, 0.0)));
}); });
...@@ -52,8 +52,9 @@ void main() { ...@@ -52,8 +52,9 @@ void main() {
columns: 5, columns: 5,
rows: 5, rows: 5,
defaultColumnWidth: const IntrinsicColumnWidth(), defaultColumnWidth: const IntrinsicColumnWidth(),
textDirection: TextDirection.ltr,
defaultVerticalAlignment: TableCellVerticalAlignment.baseline, defaultVerticalAlignment: TableCellVerticalAlignment.baseline,
textBaseline: TextBaseline.alphabetic textBaseline: TextBaseline.alphabetic,
))); )));
expect(table.size, equals(const Size(0.0, 0.0))); expect(table.size, equals(const Size(0.0, 0.0)));
...@@ -143,7 +144,8 @@ void main() { ...@@ -143,7 +144,8 @@ void main() {
RenderBox child; RenderBox child;
table = new RenderTable( table = new RenderTable(
columns: 5, columns: 5,
rows: 5 rows: 5,
textDirection: TextDirection.ltr,
); );
table.setChild(4, 4, child = sizedBox(10.0, 10.0)); table.setChild(4, 4, child = sizedBox(10.0, 10.0));
...@@ -159,7 +161,7 @@ void main() { ...@@ -159,7 +161,7 @@ void main() {
final RenderBox child1 = new RenderPositionedBox(); final RenderBox child1 = new RenderPositionedBox();
final RenderBox child2 = new RenderPositionedBox(); final RenderBox child2 = new RenderPositionedBox();
final RenderBox child3 = new RenderPositionedBox(); final RenderBox child3 = new RenderPositionedBox();
table = new RenderTable(); table = new RenderTable(textDirection: TextDirection.ltr);
table.setFlatChildren(3, <RenderBox>[child1, new RenderPositionedBox(), child2, table.setFlatChildren(3, <RenderBox>[child1, new RenderPositionedBox(), child2,
new RenderPositionedBox(), child3, new RenderPositionedBox()]); new RenderPositionedBox(), child3, new RenderPositionedBox()]);
expect(table.rows, equals(2)); expect(table.rows, equals(2));
......
...@@ -473,4 +473,8 @@ class WidgetController { ...@@ -473,4 +473,8 @@ class WidgetController {
assert(box != null); assert(box != null);
return box.size; return box.size;
} }
/// Returns the rect of the given widget. This is only valid once
/// the widget's render object has been laid out at least once.
Rect getRect(Finder finder) => getTopLeft(finder) & getSize(finder);
} }
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