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 {
return new Table(
columnWidths: tableColumns.asMap(),
children: tableRows
children: tableRows,
);
}
}
......
......@@ -298,7 +298,7 @@ class RenderStack extends RenderBox
RenderStack({
List<RenderBox> children,
FractionalOffsetGeometry alignment: FractionalOffsetDirectional.topStart,
@required TextDirection textDirection,
TextDirection textDirection,
StackFit fit: StackFit.loose,
Overflow overflow: Overflow.clip,
}) : assert(alignment != null),
......@@ -569,7 +569,7 @@ class RenderIndexedStack extends RenderStack {
RenderIndexedStack({
List<RenderBox> children,
FractionalOffsetGeometry alignment: FractionalOffsetDirectional.topStart,
@required TextDirection textDirection,
TextDirection textDirection,
int index: 0,
}) : _index = index, super(
children: children,
......
......@@ -359,6 +359,7 @@ class RenderTable extends RenderBox {
int rows,
Map<int, TableColumnWidth> columnWidths,
TableColumnWidth defaultColumnWidth: const FlexColumnWidth(1.0),
@required TextDirection textDirection,
TableBorder border,
List<Decoration> rowDecorations,
ImageConfiguration configuration: ImageConfiguration.empty,
......@@ -370,7 +371,9 @@ class RenderTable extends RenderBox {
assert(rows == null || rows >= 0),
assert(rows == null || children == 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);
_rows = rows ?? 0;
_children = <RenderBox>[]..length = _columns * _rows;
......@@ -489,6 +492,17 @@ class RenderTable extends RenderBox {
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.
TableBorder get border => _border;
TableBorder _border;
......@@ -977,6 +991,8 @@ class RenderTable extends RenderBox {
@override
void performLayout() {
final int rows = this.rows;
final int columns = this.columns;
assert(_children.length == rows * columns);
if (rows * columns == 0) {
// TODO(ianh): if columns is zero, this should be zero width
......@@ -986,12 +1002,25 @@ class RenderTable extends RenderBox {
}
final List<double> widths = _computeColumnWidths(constraints);
final List<double> positions = new List<double>(columns);
_rowTops.clear();
positions[0] = 0.0;
for (int x = 1; x < columns; x += 1)
positions[x] = positions[x-1] + widths[x-1];
_columnLefts = positions;
double tableWidth;
switch (textDirection) {
case TextDirection.rtl:
positions[columns - 1] = 0.0;
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));
_rowTops.clear();
_baselineDistance = null;
// then, lay out each row
double rowTop = 0.0;
......@@ -1070,7 +1099,7 @@ class RenderTable extends RenderBox {
rowTop += rowHeight;
}
_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);
}
......@@ -1119,7 +1148,13 @@ class RenderTable extends RenderBox {
}
assert(_rows == _rowTops.length - 1);
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
......
......@@ -198,9 +198,9 @@ class TableBorder {
assert(rows.first == 0.0);
assert(rows.last == rect.height);
assert(columns != null);
assert(columns.length >= 2);
assert(columns.isNotEmpty);
assert(columns.first == 0.0);
assert(columns.last == rect.width);
assert(columns.last < rect.width);
final Paint paint = new Paint();
final Path path = new Path();
......
......@@ -7,6 +7,7 @@ import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'debug.dart';
import 'framework.dart';
import 'image.dart';
......@@ -97,6 +98,7 @@ class Table extends RenderObjectWidget {
this.children: const <TableRow>[],
this.columnWidths,
this.defaultColumnWidth: const FlexColumnWidth(1.0),
this.textDirection,
this.border,
this.defaultVerticalAlignment: TableCellVerticalAlignment.top,
this.textBaseline
......@@ -179,6 +181,11 @@ class Table extends RenderObjectWidget {
/// `columnWidths[i]` is null.
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.
final TableBorder border;
......@@ -200,6 +207,7 @@ class Table extends RenderObjectWidget {
rows: children.length,
columnWidths: columnWidths,
defaultColumnWidth: defaultColumnWidth,
textDirection: textDirection ?? Directionality.of(context),
border: border,
rowDecorations: _rowDecorations,
configuration: createLocalImageConfiguration(context),
......@@ -215,6 +223,7 @@ class Table extends RenderObjectWidget {
renderObject
..columnWidths = columnWidths
..defaultColumnWidth = defaultColumnWidth
..textDirection = textDirection ?? Directionality.of(context)
..border = border
..rowDecorations = _rowDecorations
..configuration = createLocalImageConfiguration(context)
......
......@@ -16,7 +16,7 @@ RenderBox sizedBox(double width, double height) {
void main() {
test('Table control test; tight', () {
RenderTable table;
layout(table = new RenderTable());
layout(table = new RenderTable(textDirection: TextDirection.ltr));
expect(table.size.width, equals(800.0));
expect(table.size.height, equals(600.0));
......@@ -41,7 +41,7 @@ void main() {
test('Table control test; loose', () {
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)));
});
......@@ -52,8 +52,9 @@ void main() {
columns: 5,
rows: 5,
defaultColumnWidth: const IntrinsicColumnWidth(),
textDirection: TextDirection.ltr,
defaultVerticalAlignment: TableCellVerticalAlignment.baseline,
textBaseline: TextBaseline.alphabetic
textBaseline: TextBaseline.alphabetic,
)));
expect(table.size, equals(const Size(0.0, 0.0)));
......@@ -143,7 +144,8 @@ void main() {
RenderBox child;
table = new RenderTable(
columns: 5,
rows: 5
rows: 5,
textDirection: TextDirection.ltr,
);
table.setChild(4, 4, child = sizedBox(10.0, 10.0));
......@@ -159,7 +161,7 @@ void main() {
final RenderBox child1 = new RenderPositionedBox();
final RenderBox child2 = new RenderPositionedBox();
final RenderBox child3 = new RenderPositionedBox();
table = new RenderTable();
table = new RenderTable(textDirection: TextDirection.ltr);
table.setFlatChildren(3, <RenderBox>[child1, new RenderPositionedBox(), child2,
new RenderPositionedBox(), child3, new RenderPositionedBox()]);
expect(table.rows, equals(2));
......
......@@ -473,4 +473,8 @@ class WidgetController {
assert(box != null);
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