Skip to content

Commit

Permalink
[two_dimensional_scrollables] Add TableSpanPadding (#5039)
Browse files Browse the repository at this point in the history
---

Fixes flutter/flutter#134453
Also related: flutter/flutter#134655

This adds padding to TableSpans with TableSpanPadding. This affords folks a leading and trailing offset to pad rows and columns by.
  • Loading branch information
Piinks authored Oct 26, 2023
1 parent d7dc0a0 commit 2af6954
Show file tree
Hide file tree
Showing 6 changed files with 501 additions and 39 deletions.
4 changes: 4 additions & 0 deletions packages/two_dimensional_scrollables/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.0.4

* Adds TableSpanPadding, TableSpan.padding, and TableSpanDecoration.consumeSpanPadding.

## 0.0.3

* Fixes paint issue when axes are reversed and TableView has pinned rows and columns.
Expand Down
114 changes: 83 additions & 31 deletions packages/two_dimensional_scrollables/lib/src/table_view/table.dart
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,13 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
);
}

// TODO(Piinks): Pinned rows/cols do not account for what is visible on the
// screen. Ostensibly, we would not want to have pinned rows/columns that
// extend beyond the viewport, we would never see them as they would never
// scroll into view. So this currently implementation is fairly assuming
// we will never have rows/cols that are outside of the viewport. We should
// maybe add an assertion for this during layout.
// https://github.com/flutter/flutter/issues/136833
int? get _lastPinnedRow =>
delegate.pinnedRowCount > 0 ? delegate.pinnedRowCount - 1 : null;
int? get _lastPinnedColumn =>
Expand Down Expand Up @@ -667,12 +674,17 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
}) {
// TODO(Piinks): Assert here or somewhere else merged cells cannot span
// pinned and unpinned cells (for merged cell follow-up), https://github.com/flutter/flutter/issues/131224
_Span colSpan, rowSpan;
double yPaintOffset = -offset.dy;
for (int row = start.row; row <= end.row; row += 1) {
double xPaintOffset = -offset.dx;
final double rowHeight = _rowMetrics[row]!.extent;
rowSpan = _rowMetrics[row]!;
final double rowHeight = rowSpan.extent;
yPaintOffset += rowSpan.configuration.padding.leading;
for (int column = start.column; column <= end.column; column += 1) {
final double columnWidth = _columnMetrics[column]!.extent;
colSpan = _columnMetrics[column]!;
final double columnWidth = colSpan.extent;
xPaintOffset += colSpan.configuration.padding.leading;

final TableVicinity vicinity = TableVicinity(column: column, row: row);
// TODO(Piinks): Add back merged cells, https://github.com/flutter/flutter/issues/131224
Expand All @@ -689,9 +701,11 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
cell.layout(cellConstraints);
cellParentData.layoutOffset = Offset(xPaintOffset, yPaintOffset);
}
xPaintOffset += columnWidth;
xPaintOffset += columnWidth +
_columnMetrics[column]!.configuration.padding.trailing;
}
yPaintOffset += rowHeight;
yPaintOffset +=
rowHeight + _rowMetrics[row]!.configuration.padding.trailing;
}
}

Expand Down Expand Up @@ -836,29 +850,45 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
final LinkedHashMap<Rect, TableSpanDecoration> backgroundColumns =
LinkedHashMap<Rect, TableSpanDecoration>();

final TableSpan rowSpan = _rowMetrics[leading.row]!.configuration;
for (int column = leading.column; column <= trailing.column; column++) {
final _Span span = _columnMetrics[column]!;
if (span.configuration.backgroundDecoration != null ||
span.configuration.foregroundDecoration != null) {
final TableSpan columnSpan = _columnMetrics[column]!.configuration;
if (columnSpan.backgroundDecoration != null ||
columnSpan.foregroundDecoration != null) {
final RenderBox leadingCell = getChildFor(
TableVicinity(column: column, row: leading.row),
)!;
final RenderBox trailingCell = getChildFor(
TableVicinity(column: column, row: trailing.row),
)!;

final Rect rect = Rect.fromPoints(
parentDataOf(leadingCell).paintOffset! + offset,
parentDataOf(trailingCell).paintOffset! +
Offset(trailingCell.size.width, trailingCell.size.height) +
offset,
);
Rect getColumnRect(bool consumePadding) {
return Rect.fromPoints(
parentDataOf(leadingCell).paintOffset! +
offset -
Offset(
consumePadding ? columnSpan.padding.leading : 0.0,
rowSpan.padding.leading,
),
parentDataOf(trailingCell).paintOffset! +
offset +
Offset(trailingCell.size.width, trailingCell.size.height) +
Offset(
consumePadding ? columnSpan.padding.trailing : 0.0,
rowSpan.padding.trailing,
),
);
}

if (span.configuration.backgroundDecoration != null) {
backgroundColumns[rect] = span.configuration.backgroundDecoration!;
if (columnSpan.backgroundDecoration != null) {
final Rect rect = getColumnRect(
columnSpan.backgroundDecoration!.consumeSpanPadding);
backgroundColumns[rect] = columnSpan.backgroundDecoration!;
}
if (span.configuration.foregroundDecoration != null) {
foregroundColumns[rect] = span.configuration.foregroundDecoration!;
if (columnSpan.foregroundDecoration != null) {
final Rect rect = getColumnRect(
columnSpan.foregroundDecoration!.consumeSpanPadding);
foregroundColumns[rect] = columnSpan.foregroundDecoration!;
}
}
}
Expand All @@ -869,28 +899,45 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
final LinkedHashMap<Rect, TableSpanDecoration> backgroundRows =
LinkedHashMap<Rect, TableSpanDecoration>();

final TableSpan columnSpan = _columnMetrics[leading.column]!.configuration;
for (int row = leading.row; row <= trailing.row; row++) {
final _Span span = _rowMetrics[row]!;
if (span.configuration.backgroundDecoration != null ||
span.configuration.foregroundDecoration != null) {
final TableSpan rowSpan = _rowMetrics[row]!.configuration;
if (rowSpan.backgroundDecoration != null ||
rowSpan.foregroundDecoration != null) {
final RenderBox leadingCell = getChildFor(
TableVicinity(column: leading.column, row: row),
)!;
final RenderBox trailingCell = getChildFor(
TableVicinity(column: trailing.column, row: row),
)!;

final Rect rect = Rect.fromPoints(
parentDataOf(leadingCell).paintOffset! + offset,
parentDataOf(trailingCell).paintOffset! +
Offset(trailingCell.size.width, trailingCell.size.height) +
offset,
);
if (span.configuration.backgroundDecoration != null) {
backgroundRows[rect] = span.configuration.backgroundDecoration!;
Rect getRowRect(bool consumePadding) {
return Rect.fromPoints(
parentDataOf(leadingCell).paintOffset! +
offset -
Offset(
columnSpan.padding.leading,
consumePadding ? rowSpan.padding.leading : 0.0,
),
parentDataOf(trailingCell).paintOffset! +
offset +
Offset(trailingCell.size.width, trailingCell.size.height) +
Offset(
columnSpan.padding.leading,
consumePadding ? rowSpan.padding.trailing : 0.0,
),
);
}

if (rowSpan.backgroundDecoration != null) {
final Rect rect =
getRowRect(rowSpan.backgroundDecoration!.consumeSpanPadding);
backgroundRows[rect] = rowSpan.backgroundDecoration!;
}
if (span.configuration.foregroundDecoration != null) {
foregroundRows[rect] = span.configuration.foregroundDecoration!;
if (rowSpan.foregroundDecoration != null) {
final Rect rect =
getRowRect(rowSpan.foregroundDecoration!.consumeSpanPadding);
foregroundRows[rect] = rowSpan.foregroundDecoration!;
}
}
}
Expand Down Expand Up @@ -1028,7 +1075,12 @@ class _Span
bool get isPinned => _isPinned;
late bool _isPinned;

double get trailingOffset => leadingOffset + extent;
double get trailingOffset {
return leadingOffset +
extent +
configuration.padding.leading +
configuration.padding.trailing;
}

// ---- Span Management ----

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,42 @@ import 'package:flutter/widgets.dart';

import 'table.dart';

/// Defines the leading and trailing padding values of a [TableSpan].
class TableSpanPadding {
/// Creates a padding configuration for a [TableSpan].
const TableSpanPadding({
this.leading = 0.0,
this.trailing = 0.0,
});

/// Creates padding where both the [leading] and [trailing] are `value`.
const TableSpanPadding.all(double value)
: leading = value,
trailing = value;

/// The leading amount of pixels to pad a [TableSpan] by.
///
/// If the [TableSpan] is a row and the vertical [Axis] is not reversed, this
/// offset will be applied above the row. If the vertical [Axis] is reversed,
/// this will be applied below the row.
///
/// If the [TableSpan] is a column and the horizontal [Axis] is not reversed,
/// this offset will be applied to the left the column. If the horizontal
/// [Axis] is reversed, this will be applied to the right of the column.
final double leading;

/// The trailing amount of pixels to pad a [TableSpan] by.
///
/// If the [TableSpan] is a row and the vertical [Axis] is not reversed, this
/// offset will be applied below the row. If the vertical [Axis] is reversed,
/// this will be applied above the row.
///
/// If the [TableSpan] is a column and the horizontal [Axis] is not reversed,
/// this offset will be applied to the right the column. If the horizontal
/// [Axis] is reversed, this will be applied to the left of the column.
final double trailing;
}

/// Defines the extent, visual appearance, and gesture handling of a row or
/// column in a [TableView].
///
Expand All @@ -20,20 +56,26 @@ class TableSpan {
/// The [extent] argument must be provided.
const TableSpan({
required this.extent,
TableSpanPadding? padding,
this.recognizerFactories = const <Type, GestureRecognizerFactory>{},
this.onEnter,
this.onExit,
this.cursor = MouseCursor.defer,
this.backgroundDecoration,
this.foregroundDecoration,
});
}) : padding = padding ?? const TableSpanPadding();

/// Defines the extent of the span.
///
/// If the span represents a row, this is the height of the row. If it
/// represents a column, this is the width of the column.
final TableSpanExtent extent;

/// Defines the leading and or trailing extent to pad the row or column by.
///
/// Defaults to no padding.
final TableSpanPadding padding;

/// Factory for creating [GestureRecognizer]s that want to compete for
/// gestures within the [extent] of the span.
///
Expand Down Expand Up @@ -251,14 +293,63 @@ class MinTableSpanExtent extends CombiningTableSpanExtent {
/// A decoration for a [TableSpan].
class TableSpanDecoration {
/// Creates a [TableSpanDecoration].
const TableSpanDecoration({this.border, this.color});
const TableSpanDecoration({
this.border,
this.color,
this.consumeSpanPadding = true,
});

/// The border drawn around the span.
final TableSpanBorder? border;

/// The color to fill the bounds of the span with.
final Color? color;

/// Whether or not the decoration should extend to fill the space created by
/// the [TableSpanPadding].
///
/// Defaults to true, meaning if a [TableSpan] is a row, the decoration will
/// apply to the full [TableSpanExtent], including the
/// [TableSpanPadding.leading] and [TableSpanPadding.trailing] for the row.
/// This same row decoration will consume any padding from the column spans so
/// as to decorate the row as one continuous span.
///
/// {@tool snippet}
/// This example illustrates how [consumeSpanPadding] affects
/// [TableSpanDecoration.color]. By default, the color of the decoration
/// consumes the padding, coloring the row fully by including the padding
/// around the row. When [consumeSpanPadding] is false, the padded area of
/// the row is not decorated.
///
/// ```dart
/// TableView.builder(
/// rowCount: 4,
/// columnCount: 4,
/// columnBuilder: (int index) => TableSpan(
/// extent: const FixedTableSpanExtent(150.0),
/// padding: const TableSpanPadding(trailing: 10),
/// ),
/// rowBuilder: (int index) => TableSpan(
/// extent: const FixedTableSpanExtent(150.0),
/// padding: TableSpanPadding(leading: 10, trailing: 10),
/// backgroundDecoration: TableSpanDecoration(
/// color: index.isOdd ? Colors.blue : Colors.green,
/// // The background color will not be applied to the padded area.
/// consumeSpanPadding: false,
/// ),
/// ),
/// cellBuilder: (_, TableVicinity vicinity) {
/// return Container(
/// height: 150,
/// width: 150,
/// child: const Center(child: FlutterLogo()),
/// );
/// },
/// );
/// ```
/// {@end-tool}
final bool consumeSpanPadding;

/// Called to draw the decoration around a span.
///
/// The provided [TableSpanDecorationPaintDetails] describes the bounds and
Expand Down
2 changes: 1 addition & 1 deletion packages/two_dimensional_scrollables/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: two_dimensional_scrollables
description: Widgets that scroll using the two dimensional scrolling foundation.
version: 0.0.3
version: 0.0.4
repository: https://github.com/flutter/packages/tree/main/packages/two_dimensional_scrollables
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+two_dimensional_scrollables%22+

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2af6954

Please sign in to comment.