From 6ea7ab89ff7ee9ceb03b0a42281e9738733d4ecb Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Mon, 4 Apr 2016 16:53:38 -0700 Subject: [PATCH] Add a row decoration to RenderTable This will let us draw in-row bottom borders and row-wide backgrounds, both of which are necessary for Material data tables. --- packages/flutter/lib/src/rendering/table.dart | 67 ++++++++++++++++--- packages/flutter/lib/src/widgets/table.dart | 13 +++- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/packages/flutter/lib/src/rendering/table.dart b/packages/flutter/lib/src/rendering/table.dart index 62be903b624..6193f7b4373 100644 --- a/packages/flutter/lib/src/rendering/table.dart +++ b/packages/flutter/lib/src/rendering/table.dart @@ -345,6 +345,8 @@ class RenderTable extends RenderBox { Map columnWidths, TableColumnWidth defaultColumnWidth: const FlexColumnWidth(1.0), TableBorder border, + List rowDecorations, + Decoration defaultRowDecoration, TableCellVerticalAlignment defaultVerticalAlignment: TableCellVerticalAlignment.top, TextBaseline textBaseline, List> children @@ -359,6 +361,7 @@ class RenderTable extends RenderBox { _columnWidths = columnWidths ?? new HashMap(); _defaultColumnWidth = defaultColumnWidth; _border = border; + this.rowDecorations = rowDecorations; // must use setter to initialize box painters _defaultVerticalAlignment = defaultVerticalAlignment; _textBaseline = textBaseline; if (children != null) { @@ -367,8 +370,6 @@ class RenderTable extends RenderBox { } } - // TODO(ianh): Add a 'decoration' field to the children's parent data, to paint on each cell. - // Children are stored in row-major order. // _children.length must be rows * columns List _children = const []; @@ -419,8 +420,8 @@ class RenderTable extends RenderBox { markNeedsLayout(); } - Map _columnWidths; Map get columnWidths => new Map.unmodifiable(_columnWidths); + Map _columnWidths; void set columnWidths(Map value) { value ??= new HashMap(); if (_columnWidths == value) @@ -454,6 +455,38 @@ class RenderTable extends RenderBox { _border = value; markNeedsPaint(); } + + List get rowDecorations => new List.unmodifiable(_rowDecorations ?? const []); + List _rowDecorations; + List _rowDecorationPainters; + void set rowDecorations(List value) { + if (_rowDecorations == value) + return; + _removeListenersIfNeeded(); + _rowDecorations = value; + _rowDecorationPainters = _rowDecorations != null ? new List(_rowDecorations.length) : null; + _addListenersIfNeeded(); + } + + void _removeListenersIfNeeded() { + Set visitedDecorations = new Set(); + if (_rowDecorations != null && attached) { + for (Decoration decoration in _rowDecorations) { + if (decoration != null && decoration.needsListeners && visitedDecorations.add(decoration)) + decoration.removeChangeListener(markNeedsPaint); + } + } + } + + void _addListenersIfNeeded() { + Set visitedDecorations = new Set(); + if (_rowDecorations != null && attached) { + for (Decoration decoration in _rowDecorations) { + if (decoration != null && decoration.needsListeners && visitedDecorations.add(decoration)) + decoration.addChangeListener(markNeedsPaint); + } + } + } TableCellVerticalAlignment get defaultVerticalAlignment => _defaultVerticalAlignment; TableCellVerticalAlignment _defaultVerticalAlignment; @@ -581,13 +614,15 @@ class RenderTable extends RenderBox { super.attach(owner); for (RenderBox child in _children) child?.attach(owner); + _addListenersIfNeeded(); } @override void detach() { - super.detach(); + _removeListenersIfNeeded(); for (RenderBox child in _children) child?.detach(); + super.detach(); } @override @@ -823,8 +858,9 @@ class RenderTable extends RenderBox { } rowTop += rowHeight; } + _rowTops.add(rowTop); size = constraints.constrain(new Size(positions.last + widths.last, rowTop)); - assert(_rowTops.length == rows); + assert(_rowTops.length == rows + 1); } @override @@ -845,10 +881,25 @@ class RenderTable extends RenderBox { @override void paint(PaintingContext context, Offset offset) { + Canvas canvas; assert(_children.length == rows * columns); if (rows * columns == 0) return; - assert(_rowTops.length == rows); + assert(_rowTops.length == rows + 1); + canvas = context.canvas; + if (_rowDecorations != null) { + for (int y = 0; y < rows; y += 1) { + if (_rowDecorations.length <= y) + break; + _rowDecorationPainters[y] ??= _rowDecorations[y].createBoxPainter(); + _rowDecorationPainters[y].paint(canvas, new Rect.fromLTRB( + offset.dx, + offset.dy + _rowTops[y], + offset.dx + size.width, + offset.dy + _rowTops[y+1] + )); + } + } for (int index = 0; index < _children.length; index += 1) { RenderBox child = _children[index]; if (child != null) { @@ -856,9 +907,8 @@ class RenderTable extends RenderBox { context.paintChild(child, childParentData.offset + offset); } } + canvas = context.canvas; Rect bounds = offset & size; - Canvas canvas = context.canvas; - canvas.saveLayer(bounds, new Paint()); if (border != null) { switch (border.verticalInside.style) { case BorderStyle.solid: @@ -892,7 +942,6 @@ class RenderTable extends RenderBox { } border.paint(canvas, bounds); } - canvas.restore(); } @override diff --git a/packages/flutter/lib/src/widgets/table.dart b/packages/flutter/lib/src/widgets/table.dart index 4a64ae6bdd9..b2f72651fd6 100644 --- a/packages/flutter/lib/src/widgets/table.dart +++ b/packages/flutter/lib/src/widgets/table.dart @@ -21,8 +21,9 @@ export 'package:flutter/rendering.dart' show TableColumnWidth; class TableRow { - const TableRow({ this.key, this.children }); + const TableRow({ this.key, this.decoration, this.children }); final LocalKey key; + final Decoration decoration; final List children; } @@ -39,13 +40,15 @@ class _TableElementRow { class Table extends RenderObjectWidget { Table({ Key key, - this.children: const [], + List children: const [], this.columnWidths, this.defaultColumnWidth: const FlexColumnWidth(1.0), this.border, this.defaultVerticalAlignment: TableCellVerticalAlignment.top, this.textBaseline - }) : super(key: key) { + }) : children = children, + _rowDecorations = children.map/**/((TableRow row) => row.decoration).toList(), + super(key: key) { assert(children != null); assert(defaultColumnWidth != null); assert(defaultVerticalAlignment != null); @@ -63,6 +66,8 @@ class Table extends RenderObjectWidget { final TableCellVerticalAlignment defaultVerticalAlignment; final TextBaseline textBaseline; + final List _rowDecorations; + @override _TableElement createElement() => new _TableElement(this); @@ -74,6 +79,7 @@ class Table extends RenderObjectWidget { columnWidths: columnWidths, defaultColumnWidth: defaultColumnWidth, border: border, + rowDecorations: _rowDecorations, defaultVerticalAlignment: defaultVerticalAlignment, textBaseline: textBaseline ); @@ -87,6 +93,7 @@ class Table extends RenderObjectWidget { ..columnWidths = columnWidths ..defaultColumnWidth = defaultColumnWidth ..border = border + ..rowDecorations = _rowDecorations ..defaultVerticalAlignment = defaultVerticalAlignment ..textBaseline = textBaseline; }