auto grid docs and test

This commit is contained in:
Will McGugan
2023-08-17 10:46:57 +01:00
parent c1b611bac9
commit dda2cb2be2
6 changed files with 191 additions and 66 deletions

View File

@@ -0,0 +1,11 @@
Screen {
layout: grid;
grid-size: 3;
grid-columns: auto 1fr 1fr;
grid-rows: 25% 75%;
}
.box {
height: 100%;
border: solid green;
}

View File

@@ -0,0 +1,19 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class GridLayoutExample(App):
CSS_PATH = "grid_layout_auto.css"
def compose(self) -> ComposeResult:
yield Static("First column", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
yield Static("Four", classes="box")
yield Static("Five", classes="box")
yield Static("Six", classes="box")
if __name__ == "__main__":
app = GridLayoutExample()
app.run()

View File

@@ -326,6 +326,33 @@ If you don't specify enough values in a `grid-columns` or `grid-rows` declaratio
For example, if your grid has four columns (i.e. `grid-size: 4;`), then `grid-columns: 2 4;` is equivalent to `grid-columns: 2 4 2 4;`.
If it instead had three columns, then `grid-columns: 2 4;` would be equivalent to `grid-columns: 2 4 2;`.
#### Auto rows / columns
The `grid-columns` and `grid-rows` rules can both accept a value of "auto" in place of any of the dimensions, which tells Textual to calculate an optimal size based on the content.
Let's modify the previous example to make the first column an `auto` column.
=== "Output"
```{.textual path="docs/examples/guide/layout/grid_layout_auto.py"}
```
=== "grid_layout_auto.py"
```python hl_lines="6 9"
--8<-- "docs/examples/guide/layout/grid_layout_auto.py"
```
=== "grid_layout_auto.css"
```sass hl_lines="4"
--8<-- "docs/examples/guide/layout/grid_layout_auto.css"
```
Notice how the first column is just wide enough to fit the content of each cell.
The layout will adjust accordingly if you update the content for any widget in that column.
### Cell spans
Cells may _span_ multiple rows or columns, to create more interesting grid arrangements.

View File

@@ -114,6 +114,52 @@ class GridLayout(Layout):
row_scalars, table_size_rows if table_size_rows else row + 1
)
def apply_width_limits(widget: Widget, width: int) -> int:
"""Apply min and max widths to dimension.
Args:
widget: A Widget.
width: A width.
Returns:
New width.
"""
styles = widget.styles
if styles.min_width is not None:
width = max(
width,
int(styles.min_width.resolve(size, viewport, Fraction(width))),
)
if styles.max_width is not None:
width = min(
width,
int(styles.max_width.resolve(size, viewport, Fraction(width))),
)
return width
def apply_height_limits(widget: Widget, height: int) -> int:
"""Apply min and max height to a dimension.
Args:
widget: A widget.
height: A height.
Returns:
New height
"""
styles = widget.styles
if styles.min_height is not None:
height = max(
height,
int(styles.min_height.resolve(size, viewport, Fraction(height))),
)
if styles.max_height is not None:
height = min(
height,
int(styles.max_height.resolve(size, viewport, Fraction(height))),
)
return height
# Handle any auto columns
for column, scalar in enumerate(column_scalars):
if scalar.is_auto:
@@ -129,8 +175,11 @@ class GridLayout(Layout):
continue
width = max(
width,
widget.get_content_width(size, viewport)
+ widget.styles.gutter.width,
apply_width_limits(
widget,
widget.get_content_width(size, viewport)
+ widget.styles.gutter.width,
),
)
column_scalars[column] = Scalar.from_number(width)
@@ -150,13 +199,14 @@ class GridLayout(Layout):
if widget.styles.row_span != 1:
continue
column_width = columns[column][1]
widget_height = (
widget_height = apply_height_limits(
widget,
widget.get_content_height(
size,
viewport,
column_width - parent.styles.grid_gutter_vertical,
)
+ widget.styles.gutter.height
+ widget.styles.gutter.height,
)
height = max(height, widget_height)
row_scalars[row] = Scalar.from_number(height)

File diff suppressed because one or more lines are too long

View File

@@ -16,15 +16,33 @@ class GridApp(App):
height:auto;
border: solid green;
}
#c2 Label {
min-width: 20;
}
#c3 Label {
max-width: 30;
}
"""
def compose(self) -> ComposeResult:
with Container():
with Container(id="c1"):
yield Label("foo")
yield Input()
yield Label("Longer label")
yield Input()
with Container(id="c2"):
yield Label("foo")
yield Input()
yield Label("Longer label")
yield Input()
with Container(id="c3"):
yield Label("foo bar " * 10)
yield Input()
yield Label("Longer label")
yield Input()
if __name__ == "__main__":