Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fixed `TextArea` cursor display on wrapped lines https://github.com/Textualize/textual/pull/6196
- Fixed `remove_children` not refreshing layout https://github.com/Textualize/textual/pull/6206

### Added

- Added `grid_size` property to `GridLayout` https://github.com/Textualize/textual/pull/6210

## [6.5.0] - 2025-10-31

### Added
Expand Down
18 changes: 15 additions & 3 deletions src/textual/layouts/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ def __init__(self) -> None:
"""Shrink the grid to fit the container if it is larger."""
self.auto_minimum: bool = False
"""If self.shrink is `True`, auto-detect and limit the width."""
self._grid_size: tuple[int, int] | None = None
"""Grid size after last arrange call."""

@property
def grid_size(self) -> tuple[int, int] | None:
"""The grid size after the last arrange call.

Returns:
A tuple of (WIDTH, HEIGHT) or `None` prior to the first `arrange`.
Copy link
Collaborator

@TomJGooding TomJGooding Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this grid size actually columns/rows rather than width/height?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The values are the same. As for naming, my brain works best with width/height nomenclature. I have to do mental gymnastics to convert from columns/rows.

"""
return self._grid_size

def arrange(
self, parent: Widget, children: list[Widget], size: Size, greedy: bool = True
Expand Down Expand Up @@ -60,6 +71,7 @@ def arrange(
table_size_columns -= 1

table_size_rows = styles.grid_size_rows

viewport = parent.app.viewport_size
keyline_style, _keyline_color = styles.keyline
offset = (0, 0)
Expand Down Expand Up @@ -157,9 +169,9 @@ def repeat_scalars(scalars: Iterable[Scalar], count: int) -> list[Scalar]:
cell_coord = next_coord()

column_scalars = repeat_scalars(column_scalars, table_size_columns)
row_scalars = repeat_scalars(
row_scalars, table_size_rows if table_size_rows else row + 1
)
table_size_rows = table_size_rows if table_size_rows else row + 1
row_scalars = repeat_scalars(row_scalars, table_size_rows)
self._grid_size = (table_size_columns, table_size_rows)

def apply_width_limits(widget: Widget, width: int) -> int:
"""Apply min and max widths to dimension.
Expand Down
34 changes: 34 additions & 0 deletions tests/layouts/test_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from textual import containers, widgets
from textual.app import App, ComposeResult
from textual.layouts.grid import GridLayout


async def test_grid_size():
"""Test the `grid_size` property on GridLayout."""

class GridApp(App):
CSS = """
Grid {
grid-size: 3;
grid-columns: auto;
height: auto;
Label {
padding: 2 4;
border: blue;
}
}
"""

def compose(self) -> ComposeResult:
with containers.VerticalScroll():
with containers.Grid():
for _ in range(7):
yield widgets.Label("Hello, World!")

app = GridApp()
async with app.run_test() as pilot:
await pilot.pause()
await app.wait_for_refresh()
grid_layout = app.query_one(containers.Grid).layout
assert isinstance(grid_layout, GridLayout)
assert grid_layout.grid_size == (3, 3)
Loading