more words

This commit is contained in:
Will McGugan
2025-06-25 17:22:16 +01:00
parent 0a2710f96b
commit 74a4fddef4
5 changed files with 113 additions and 36 deletions

View File

@@ -5,12 +5,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
### Unreleased
## Added
- Added `CenterMiddle` container
## [3.5.0] - 2025-06-20
### Changed

View File

@@ -24,10 +24,10 @@ class ContainerApp(App):
"""
def compose(self) -> ComposeResult:
yield Box("Box 1")
with Center(classes="with-border"):
yield Box("Box 1") # (1)!
with Center(classes="with-border"): # (2)!
yield Box("Box 2")
with Right(classes="with-border"):
with Right(classes="with-border"): # (3)!
yield Box("Box 3")

View File

@@ -1,5 +1,5 @@
from textual.app import App, ComposeResult
from textual.containers import HorizontalGroup, Middle
from textual.containers import Middle
from textual.widgets import Placeholder
@@ -24,11 +24,10 @@ class ContainerApp(App):
"""
def compose(self) -> ComposeResult:
with HorizontalGroup():
yield Box("Box 1")
with Middle(classes="with-border"):
yield Box("Box 2")
yield Box("Box 3")
with Middle(classes="with-border"): # (1)!
yield Box("Box 1.")
yield Box("Box 2.")
yield Box("Box 3.")
if __name__ == "__main__":

View File

@@ -1,6 +1,11 @@
# Work with containers
# Save time with Textual containers
Textual's [containers][textual.containers] provide a convenient way of arranging your widgets. Let's look at them in a little detail.
!!! info "Are you in the right place?"
We are talking about Textual container widgets here. Not to be confused with [containerization](https://en.wikipedia.org/wiki/Containerization_(computing))—which is something else entirely!
Textual's [containers][textual.containers] provide a convenient way of arranging your widgets. Let's look at them in a little detail!
## What are containers?
@@ -30,7 +35,7 @@ Before I describe some of the other containers, I would like to show how contain
The following is the actual source of the `Horizontal` widget:
```python
class Horizontal(Widget, inherit_bindings=False):
class Horizontal(Widget):
"""An expanding container with horizontal layout and no scrollbars."""
DEFAULT_CSS = """
@@ -47,8 +52,6 @@ That's it!
A simple widget with a few preset styles.
The other containers are just as simple.
You can customize the container with TCSS in the same way as other widgets.
## Horizontal and Vertical
We've seen the `Horizontal` container in action.
@@ -69,6 +72,10 @@ And here's the output:
Three boxes, vertically stacked.
!!! tip "Styling layout"
You can set the layout of a compound widget with the [layout](../styles/layout.md) rule.
### Size behavior
Something to keep in mind when using `Horizontal` or `Vertical` is that they will consume the remaining space in the screen. Let's look at an example to illustrate that.
@@ -100,7 +107,7 @@ And here's the result:
```
Two horizontal containers divide the remaining screen space in two.
If you were to add another horizontal it would divide the screen space in to thirds--and so on.
If you were to add another horizontal it would divide the screen space in to thirds—and so on.
This makes `Horizontal` and `Vertical` excellent for designing the macro layout of your app's interface, but not for making tightly packed rows or columns. For that you need the *group* containers which I'll cover next.
@@ -132,7 +139,7 @@ We can see that the widgets are arranged horizontally as before, but they only u
Something to watch out for regarding the previous containers we have discussed, is that they don't scroll by default.
Let's see what happens if we add more boxes than could fit on the screen.
In the following example, we will add boxes:
In the following example, we will add 10 boxes:
```python hl_lines="28 29"
--8<-- "docs/examples/how-to/containers06.py"
@@ -161,7 +168,84 @@ Here's the output:
We now have a scrollbar we can click and drag to see all the boxes.
!!! tip "Automatic scrollbars"
!!! tip "Automatic scrollbars"
You can implement automatic scrollbars with the [overflow](../styles/overflow.md) style.
You can also implement automatic scrollbars with the [overflow](../styles/overflow.md) style.
## Center, Right, and Middle
The [Center][textual.containers.Center], [Right][textual.containers.Right], and [Middle][textual.containers.Middle] containers are handy for setting the alignment of select widgets.
First lets look at `Center` and `Right` which align their children on the horizontal axis (there is no `Left` container, as this is the default).
Here's an example:
```python hl_lines="2 28 30"
--8<-- "docs/examples/how-to/containers08.py"
```
1. The default is to align widgets to the left.
2. Align the child to the center.
3. Align the child to the right edge.
Here's the output:
```{.textual path="docs/examples/how-to/containers08.py"}
```
Note how `Center` and `Right` expand to fill the horizontal dimension, but are only as tall as they need to be.
!!! tip "Alignment in TCSS"
You can set alignment in TCSS with the [align](../styles/align.md) rule.
The [Middle][textual.containers.Middle] container aligns its children to the center of the *vertical* axis.
Let's look at an example.
The following code aligns three boxes on the vertical axis:
```python hl_lines="2 27"
--8<-- "docs/examples/how-to/containers09.py"
```
1. Align children to the center of the vertical axis.
Here's the output:
```{.textual path="docs/examples/how-to/containers09.py"}
```
Note how the container expands on the vertical axis, but fits on the horizontal axis.
## Other containers
This how-to covers the most common widgets, but isn't exhausted.
Be sure to visit the [container reference][textual.containers] for the full list.
There may be new containers added in future versions of Textual.
## Custom containers
The builtin [containers][textual.containers] cover a number of common layout patterns, but are unlikely to cover every possible requirement.
Fortunately, creating your own is easy.
Just like the builtin containers, you can create a container by extending Widget and adding little TCSS.
Here's a template for a custom container:
```python
class MyContainer(Widget):
"""My custom container."""
DEFAULT_CSS = """
MyContainer {
# Your rules here
}
"""
```
## Summary
- Containers are compound widgets with preset styles for arranging their children.
- [`Horizontal`][textual.containers.Horizontal] and [`Vertical`][textual.containers.Vertical] containers stretch to fill available space.
- [`HorizontalGroup`][textual.containers.HorizontalGroup] and [`VerticalGroup`][textual.containers.VerticalGroup] fit to the height of their contents.
- [`HorizontalScroll`][textual.containers.HorizontalScroll] and [`VerticalScroll`][textual.containers.VerticalScroll] add automatic scrollbars.
- [`Center`][textual.containers.Center], [`Right`][textual.containers.Right], and [`Middle`][textual.containers.Middle] set alignment.
- Custom containers are trivial to create.

View File

@@ -29,7 +29,7 @@ class Container(Widget):
"""
class ScrollableContainer(Widget, can_focus=True, inherit_bindings=False):
class ScrollableContainer(Widget, can_focus=True):
"""A scrollable container with vertical layout, and auto scrollbars on both axis."""
# We don't typically want to maximize scrollable containers,
@@ -118,7 +118,7 @@ class ScrollableContainer(Widget, can_focus=True, inherit_bindings=False):
return self.can_maximize
class Vertical(Widget, inherit_bindings=False):
class Vertical(Widget):
"""An expanding container with vertical layout and no scrollbars."""
DEFAULT_CSS = """
@@ -131,7 +131,7 @@ class Vertical(Widget, inherit_bindings=False):
"""
class VerticalGroup(Widget, inherit_bindings=False):
class VerticalGroup(Widget):
"""A non-expanding container with vertical layout and no scrollbars."""
DEFAULT_CSS = """
@@ -156,7 +156,7 @@ class VerticalScroll(ScrollableContainer):
"""
class Horizontal(Widget, inherit_bindings=False):
class Horizontal(Widget):
"""An expanding container with horizontal layout and no scrollbars."""
DEFAULT_CSS = """
@@ -169,7 +169,7 @@ class Horizontal(Widget, inherit_bindings=False):
"""
class HorizontalGroup(Widget, inherit_bindings=False):
class HorizontalGroup(Widget):
"""A non-expanding container with horizontal layout and no scrollbars."""
DEFAULT_CSS = """
@@ -194,7 +194,7 @@ class HorizontalScroll(ScrollableContainer):
"""
class Center(Widget, inherit_bindings=False):
class Center(Widget):
"""A container which aligns children on the X axis."""
DEFAULT_CSS = """
@@ -206,7 +206,7 @@ class Center(Widget, inherit_bindings=False):
"""
class Right(Widget, inherit_bindings=False):
class Right(Widget):
"""A container which aligns children on the X axis."""
DEFAULT_CSS = """
@@ -218,7 +218,7 @@ class Right(Widget, inherit_bindings=False):
"""
class Middle(Widget, inherit_bindings=False):
class Middle(Widget):
"""A container which aligns children on the Y axis."""
DEFAULT_CSS = """
@@ -230,7 +230,7 @@ class Middle(Widget, inherit_bindings=False):
"""
class CenterMiddle(Widget, inherit_bindings=False):
class CenterMiddle(Widget):
"""A container which aligns its children on both axis."""
DEFAULT_CSS = """
@@ -242,7 +242,7 @@ class CenterMiddle(Widget, inherit_bindings=False):
"""
class Grid(Widget, inherit_bindings=False):
class Grid(Widget):
"""A container with grid layout."""
DEFAULT_CSS = """
@@ -254,7 +254,7 @@ class Grid(Widget, inherit_bindings=False):
"""
class ItemGrid(Widget, inherit_bindings=False):
class ItemGrid(Widget):
"""A container with grid layout and automatic columns."""
DEFAULT_CSS = """