Commit 99876e38 authored by Hans Muller's avatar Hans Muller

Merge pull request #1938 from HansMuller/grid_layout

Added Grid row and column spacing, changed padding interpretation

Grid padding now defines the distance the overall grid is inset. The grid rowSpacing and columnSpacing attributes define the space between rows and columns respectively.
parents 5960a134 ba3930cc
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'dart:typed_data'; import 'dart:typed_data';
import 'box.dart'; import 'box.dart';
...@@ -25,8 +26,8 @@ bool _debugIsMonotonic(List<double> offsets) { ...@@ -25,8 +26,8 @@ bool _debugIsMonotonic(List<double> offsets) {
} }
List<double> _generateRegularOffsets(int count, double size) { List<double> _generateRegularOffsets(int count, double size) {
int length = count + 1; final int length = count + 1;
List<double> result = new Float64List(length); final List<double> result = new Float64List(length);
for (int i = 0; i < length; ++i) for (int i = 0; i < length; ++i)
result[i] = i * size; result[i] = i * size;
return result; return result;
...@@ -37,11 +38,15 @@ class GridSpecification { ...@@ -37,11 +38,15 @@ class GridSpecification {
GridSpecification.fromOffsets({ GridSpecification.fromOffsets({
this.columnOffsets, this.columnOffsets,
this.rowOffsets, this.rowOffsets,
this.columnSpacing: 0.0,
this.rowSpacing: 0.0,
this.padding: EdgeDims.zero this.padding: EdgeDims.zero
}) { }) {
assert(_debugIsMonotonic(columnOffsets)); assert(_debugIsMonotonic(columnOffsets));
assert(_debugIsMonotonic(rowOffsets)); assert(_debugIsMonotonic(rowOffsets));
assert(padding != null); assert(columnSpacing != null && columnSpacing >= 0.0);
assert(rowSpacing != null && rowSpacing >= 0.0);
assert(padding != null && padding.isNonNegative);
} }
/// Creates a grid specification containing a certain number of equally sized tiles. /// Creates a grid specification containing a certain number of equally sized tiles.
...@@ -50,42 +55,50 @@ class GridSpecification { ...@@ -50,42 +55,50 @@ class GridSpecification {
double tileHeight, double tileHeight,
int columnCount, int columnCount,
int rowCount, int rowCount,
this.rowSpacing: 0.0,
this.columnSpacing: 0.0,
this.padding: EdgeDims.zero this.padding: EdgeDims.zero
}) : columnOffsets = _generateRegularOffsets(columnCount, tileWidth), }) : columnOffsets = _generateRegularOffsets(columnCount, tileWidth),
rowOffsets = _generateRegularOffsets(rowCount, tileHeight) { rowOffsets = _generateRegularOffsets(rowCount, tileHeight) {
assert(_debugIsMonotonic(columnOffsets)); assert(_debugIsMonotonic(columnOffsets));
assert(_debugIsMonotonic(rowOffsets)); assert(_debugIsMonotonic(rowOffsets));
assert(padding != null); assert(padding != null && padding.isNonNegative);
} }
/// The offsets of the column boundaries in the grid. /// The offsets of the column boundaries in the grid.
/// ///
/// The first offset is the offset of the left edge of the left-most column /// The first offset is the offset of the left edge of the left-most column
/// from the left edge of the interior of the grid's padding (usually 0.0). /// from the left edge of the interior of the grid's padding (0.0 if the padding
/// The last offset is the offset of the right edge of the right-most column /// is EdgeOffsets.zero). The last offset is the offset of the right edge of
/// from the left edge of the interior of the grid's padding. /// the right-most column from the left edge of the interior of the grid's padding.
/// ///
/// If there are n columns in the grid, there should be n + 1 entries in this /// If there are n columns in the grid, there should be n + 1 entries in this
/// list (because there's an entry before the first column and after the last /// list. The right edge of the last column is defined as columnOffsets(n), i.e.
/// column). /// the left edge of an extra column.
final List<double> columnOffsets; final List<double> columnOffsets;
/// The offsets of the row boundaries in the grid. /// The offsets of the row boundaries in the grid.
/// ///
/// The first offset is the offset of the top edge of the top-most row from /// The first offset is the offset of the top edge of the top-most row from
/// the top edge of the interior of the grid's padding (usually 0.0). The /// the top edge of the interior of the grid's padding (usually if the padding
/// last offset is the offset of the bottom edge of the bottom-most column /// is EdgeOffsets.zero). The last offset is the offset of the bottom edge of
/// from the top edge of the interior of the grid's padding. /// the bottom-most column from the top edge of the interior of the grid's padding.
/// ///
/// If there are n rows in the grid, there should be n + 1 entries in this /// If there are n rows in the grid, there should be n + 1 entries in this
/// list (because there's an entry before the first row and after the last /// list. The bottom edge of the last row is defined as rowOffsets(n), i.e.
/// row). /// the top edge of an extra row.
final List<double> rowOffsets; final List<double> rowOffsets;
/// The vertical distance between rows.
final double rowSpacing;
/// The horizontal distance between columns.
final double columnSpacing;
/// The interior padding of the grid. /// The interior padding of the grid.
/// ///
/// The grid's size encloses the rows and columns and is then inflated by the /// The grid's size encloses the spaced rows and columns and is then inflated
/// padding. /// by the padding.
final EdgeDims padding; final EdgeDims padding;
/// The size of the grid. /// The size of the grid.
...@@ -104,14 +117,12 @@ class GridChildPlacement { ...@@ -104,14 +117,12 @@ class GridChildPlacement {
this.column, this.column,
this.row, this.row,
this.columnSpan: 1, this.columnSpan: 1,
this.rowSpan: 1, this.rowSpan: 1
this.padding: EdgeDims.zero
}) { }) {
assert(column != null); assert(column != null && column >= 0);
assert(row != null); assert(row != null && row >= 0);
assert(columnSpan != null); assert(columnSpan != null && columnSpan > 0);
assert(rowSpan != null); assert(rowSpan != null && rowSpan > 0);
assert(padding != null);
} }
/// The column in which to place the child. /// The column in which to place the child.
...@@ -125,9 +136,6 @@ class GridChildPlacement { ...@@ -125,9 +136,6 @@ class GridChildPlacement {
/// How many rows the child should span. /// How many rows the child should span.
final int rowSpan; final int rowSpan;
/// How much the child should be inset from the column and row boundaries.
final EdgeDims padding;
} }
/// An abstract interface to control the layout of a [RenderGrid]. /// An abstract interface to control the layout of a [RenderGrid].
...@@ -172,32 +180,53 @@ abstract class GridDelegate { ...@@ -172,32 +180,53 @@ abstract class GridDelegate {
/// A [GridDelegate] the places its children in order throughout the grid. /// A [GridDelegate] the places its children in order throughout the grid.
abstract class GridDelegateWithInOrderChildPlacement extends GridDelegate { abstract class GridDelegateWithInOrderChildPlacement extends GridDelegate {
GridDelegateWithInOrderChildPlacement({ this.padding: EdgeDims.zero }); GridDelegateWithInOrderChildPlacement({
this.columnSpacing: 0.0,
this.rowSpacing: 0.0,
this.padding: EdgeDims.zero
}) {
assert(columnSpacing != null && columnSpacing >= 0.0);
assert(rowSpacing != null && rowSpacing >= 0.0);
assert(padding != null && padding.isNonNegative);
}
/// The horizontal distance between columns.
final double columnSpacing;
/// The vertical distance between rows.
final double rowSpacing;
/// The amount of padding to apply to each child. // Insets for the entire grid.
final EdgeDims padding; final EdgeDims padding;
GridChildPlacement getChildPlacement(GridSpecification specification, int index, Object placementData) { GridChildPlacement getChildPlacement(GridSpecification specification, int index, Object placementData) {
int columnCount = specification.columnOffsets.length - 1; final int columnCount = specification.columnOffsets.length - 1;
return new GridChildPlacement( return new GridChildPlacement(
column: index % columnCount, column: index % columnCount,
row: index ~/ columnCount, row: index ~/ columnCount
padding: padding
); );
} }
bool shouldRelayout(GridDelegateWithInOrderChildPlacement oldDelegate) { bool shouldRelayout(GridDelegateWithInOrderChildPlacement oldDelegate) {
return padding != oldDelegate.padding; return columnSpacing != oldDelegate.columnSpacing
|| rowSpacing != oldDelegate.rowSpacing
|| padding != oldDelegate.padding;
} }
} }
/// A [GridDelegate] that divides the grid's width evenly amount a fixed number of columns. /// A [GridDelegate] that divides the grid's width evenly amount a fixed number of columns.
class FixedColumnCountGridDelegate extends GridDelegateWithInOrderChildPlacement { class FixedColumnCountGridDelegate extends GridDelegateWithInOrderChildPlacement {
FixedColumnCountGridDelegate({ FixedColumnCountGridDelegate({
this.columnCount, this.columnCount,
this.tileAspectRatio: 1.0, double columnSpacing: 0.0,
EdgeDims padding: EdgeDims.zero double rowSpacing: 0.0,
}) : super(padding: padding); EdgeDims padding: EdgeDims.zero,
this.tileAspectRatio: 1.0
}) : super(columnSpacing: columnSpacing, rowSpacing: rowSpacing, padding: padding) {
assert(columnCount != null && columnCount >= 0);
assert(tileAspectRatio != null && tileAspectRatio > 0.0);
}
/// The number of columns in the grid. /// The number of columns in the grid.
final int columnCount; final int columnCount;
...@@ -208,14 +237,16 @@ class FixedColumnCountGridDelegate extends GridDelegateWithInOrderChildPlacement ...@@ -208,14 +237,16 @@ class FixedColumnCountGridDelegate extends GridDelegateWithInOrderChildPlacement
GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) { GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) {
assert(constraints.maxWidth < double.INFINITY); assert(constraints.maxWidth < double.INFINITY);
int rowCount = (childCount / columnCount).ceil(); int rowCount = (childCount / columnCount).ceil();
double tileWidth = constraints.maxWidth / columnCount; double tileWidth = math.max(0.0, constraints.maxWidth - padding.horizontal + columnSpacing) / columnCount;
double tileHeight = tileWidth / tileAspectRatio; double tileHeight = tileWidth / tileAspectRatio;
return new GridSpecification.fromRegularTiles( return new GridSpecification.fromRegularTiles(
tileWidth: tileWidth, tileWidth: tileWidth,
tileHeight: tileHeight, tileHeight: tileHeight,
columnCount: columnCount, columnCount: columnCount,
rowCount: rowCount, rowCount: rowCount,
padding: padding.flipped columnSpacing: columnSpacing,
rowSpacing: rowSpacing,
padding: padding
); );
} }
...@@ -246,8 +277,13 @@ class MaxTileWidthGridDelegate extends GridDelegateWithInOrderChildPlacement { ...@@ -246,8 +277,13 @@ class MaxTileWidthGridDelegate extends GridDelegateWithInOrderChildPlacement {
MaxTileWidthGridDelegate({ MaxTileWidthGridDelegate({
this.maxTileWidth, this.maxTileWidth,
this.tileAspectRatio: 1.0, this.tileAspectRatio: 1.0,
double columnSpacing: 0.0,
double rowSpacing: 0.0,
EdgeDims padding: EdgeDims.zero EdgeDims padding: EdgeDims.zero
}) : super(padding: padding); }) : super(columnSpacing: columnSpacing, rowSpacing: rowSpacing, padding: padding) {
assert(maxTileWidth != null && maxTileWidth >= 0.0);
assert(tileAspectRatio != null && tileAspectRatio > 0.0);
}
/// The maximum width of a tile in the grid. /// The maximum width of a tile in the grid.
final double maxTileWidth; final double maxTileWidth;
...@@ -257,7 +293,7 @@ class MaxTileWidthGridDelegate extends GridDelegateWithInOrderChildPlacement { ...@@ -257,7 +293,7 @@ class MaxTileWidthGridDelegate extends GridDelegateWithInOrderChildPlacement {
GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) { GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) {
assert(constraints.maxWidth < double.INFINITY); assert(constraints.maxWidth < double.INFINITY);
double gridWidth = constraints.maxWidth; final double gridWidth = math.max(0.0, constraints.maxWidth - padding.horizontal);
int columnCount = (gridWidth / maxTileWidth).ceil(); int columnCount = (gridWidth / maxTileWidth).ceil();
int rowCount = (childCount / columnCount).ceil(); int rowCount = (childCount / columnCount).ceil();
double tileWidth = gridWidth / columnCount; double tileWidth = gridWidth / columnCount;
...@@ -267,7 +303,9 @@ class MaxTileWidthGridDelegate extends GridDelegateWithInOrderChildPlacement { ...@@ -267,7 +303,9 @@ class MaxTileWidthGridDelegate extends GridDelegateWithInOrderChildPlacement {
tileHeight: tileHeight, tileHeight: tileHeight,
columnCount: columnCount, columnCount: columnCount,
rowCount: rowCount, rowCount: rowCount,
padding: padding.flipped columnSpacing: columnSpacing,
rowSpacing: rowSpacing,
padding: padding
); );
} }
...@@ -411,14 +449,14 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> { ...@@ -411,14 +449,14 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> {
void performLayout() { void performLayout() {
_updateGridSpecification(); _updateGridSpecification();
Size gridSize = _specification.gridSize; final Size gridSize = _specification.gridSize;
size = constraints.constrain(gridSize); size = constraints.constrain(gridSize);
if (callback != null) if (callback != null)
invokeLayoutCallback(callback); invokeLayoutCallback(callback);
double gridTopPadding = _specification.padding.top; final double gridTopPadding = _specification.padding.top;
double gridLeftPadding = _specification.padding.left; final double gridLeftPadding = _specification.padding.left;
int childIndex = virtualChildBase; int childIndex = virtualChildBase;
RenderBox child = firstChild; RenderBox child = firstChild;
while (child != null) { while (child != null) {
...@@ -435,8 +473,8 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> { ...@@ -435,8 +473,8 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> {
double tileTop = _specification.rowOffsets[placement.row] + gridTopPadding; double tileTop = _specification.rowOffsets[placement.row] + gridTopPadding;
double tileBottom = _specification.rowOffsets[placement.row + placement.rowSpan] + gridTopPadding; double tileBottom = _specification.rowOffsets[placement.row + placement.rowSpan] + gridTopPadding;
double childWidth = tileRight - tileLeft - placement.padding.horizontal; double childWidth = math.max(0.0, tileRight - tileLeft - _specification.columnSpacing);
double childHeight = tileBottom - tileTop - placement.padding.vertical; double childHeight = math.max(0.0, tileBottom - tileTop - _specification.rowSpacing);
child.layout(new BoxConstraints( child.layout(new BoxConstraints(
minWidth: childWidth, minWidth: childWidth,
...@@ -445,11 +483,7 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> { ...@@ -445,11 +483,7 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> {
maxHeight: childHeight maxHeight: childHeight
)); ));
childParentData.offset = new Offset( childParentData.offset = new Offset(tileLeft, tileTop);
tileLeft + placement.padding.left,
tileTop + placement.padding.top
);
childIndex += 1; childIndex += 1;
assert(child.parentData == childParentData); assert(child.parentData == childParentData);
......
...@@ -22,6 +22,7 @@ export 'package:flutter/rendering.dart' show ...@@ -22,6 +22,7 @@ export 'package:flutter/rendering.dart' show
FlexJustifyContent, FlexJustifyContent,
FractionalOffsetTween, FractionalOffsetTween,
GridDelegate, GridDelegate,
GridSpecification,
HitTestBehavior, HitTestBehavior,
MaxTileWidthGridDelegate, MaxTileWidthGridDelegate,
MultiChildLayoutDelegate, MultiChildLayoutDelegate,
...@@ -1192,6 +1193,8 @@ class FixedColumnCountGrid extends GridRenderObjectWidgetBase { ...@@ -1192,6 +1193,8 @@ class FixedColumnCountGrid extends GridRenderObjectWidgetBase {
Key key, Key key,
List<Widget> children: _emptyWidgetList, List<Widget> children: _emptyWidgetList,
this.columnCount, this.columnCount,
this.columnSpacing,
this.rowSpacing,
this.tileAspectRatio: 1.0, this.tileAspectRatio: 1.0,
this.padding: EdgeDims.zero this.padding: EdgeDims.zero
}) : super(key: key, children: children) { }) : super(key: key, children: children) {
...@@ -1201,6 +1204,12 @@ class FixedColumnCountGrid extends GridRenderObjectWidgetBase { ...@@ -1201,6 +1204,12 @@ class FixedColumnCountGrid extends GridRenderObjectWidgetBase {
/// The number of columns in the grid. /// The number of columns in the grid.
final int columnCount; final int columnCount;
/// The horizontal distance between columns.
final double columnSpacing;
/// The vertical distance between rows.
final double rowSpacing;
/// The ratio of the width to the height of each tile in the grid. /// The ratio of the width to the height of each tile in the grid.
final double tileAspectRatio; final double tileAspectRatio;
...@@ -1210,6 +1219,8 @@ class FixedColumnCountGrid extends GridRenderObjectWidgetBase { ...@@ -1210,6 +1219,8 @@ class FixedColumnCountGrid extends GridRenderObjectWidgetBase {
FixedColumnCountGridDelegate createDelegate() { FixedColumnCountGridDelegate createDelegate() {
return new FixedColumnCountGridDelegate( return new FixedColumnCountGridDelegate(
columnCount: columnCount, columnCount: columnCount,
columnSpacing: columnSpacing,
rowSpacing: rowSpacing,
tileAspectRatio: tileAspectRatio, tileAspectRatio: tileAspectRatio,
padding: padding padding: padding
); );
...@@ -1224,6 +1235,8 @@ class MaxTileWidthGrid extends GridRenderObjectWidgetBase { ...@@ -1224,6 +1235,8 @@ class MaxTileWidthGrid extends GridRenderObjectWidgetBase {
Key key, Key key,
List<Widget> children: _emptyWidgetList, List<Widget> children: _emptyWidgetList,
this.maxTileWidth, this.maxTileWidth,
this.columnSpacing,
this.rowSpacing,
this.tileAspectRatio: 1.0, this.tileAspectRatio: 1.0,
this.padding: EdgeDims.zero this.padding: EdgeDims.zero
}) : super(key: key, children: children) { }) : super(key: key, children: children) {
...@@ -1236,6 +1249,12 @@ class MaxTileWidthGrid extends GridRenderObjectWidgetBase { ...@@ -1236,6 +1249,12 @@ class MaxTileWidthGrid extends GridRenderObjectWidgetBase {
/// The ratio of the width to the height of each tile in the grid. /// The ratio of the width to the height of each tile in the grid.
final double tileAspectRatio; final double tileAspectRatio;
/// The horizontal distance between columns.
final double columnSpacing;
/// The vertical distance between rows.
final double rowSpacing;
/// The amount of padding to apply to each child. /// The amount of padding to apply to each child.
final EdgeDims padding; final EdgeDims padding;
...@@ -1243,6 +1262,8 @@ class MaxTileWidthGrid extends GridRenderObjectWidgetBase { ...@@ -1243,6 +1262,8 @@ class MaxTileWidthGrid extends GridRenderObjectWidgetBase {
return new MaxTileWidthGridDelegate( return new MaxTileWidthGridDelegate(
maxTileWidth: maxTileWidth, maxTileWidth: maxTileWidth,
tileAspectRatio: tileAspectRatio, tileAspectRatio: tileAspectRatio,
columnSpacing: columnSpacing,
rowSpacing: rowSpacing,
padding: padding padding: padding
); );
} }
......
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