From 8565d3cef6be856ee4ab6a4847c9a4f179c713c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Wed, 8 Mar 2023 18:31:24 +0000 Subject: [PATCH 01/16] Renamed 'Vertical' to 'VerticalScroll'. Related issues: #1957. --- CHANGELOG.md | 1 + .../blocking01.py | 6 +++--- .../blocking02.py | 6 +++--- .../nonblocking01.py | 6 +++--- docs/examples/events/dictionary.py | 7 ++++--- docs/examples/guide/layout/combining_layouts.py | 4 ++-- docs/examples/guide/layout/utility_containers.py | 6 +++--- .../guide/layout/utility_containers_using_with.py | 6 +++--- docs/examples/styles/height_comparison.py | 6 +++--- docs/examples/styles/max_width.py | 4 ++-- docs/examples/styles/min_width.css | 5 +++-- docs/examples/styles/min_width.py | 4 ++-- docs/examples/styles/overflow.css | 4 ++-- docs/examples/styles/overflow.py | 6 +++--- docs/examples/styles/scrollbar_size.py | 4 ++-- docs/examples/styles/visibility_containers.py | 4 ++-- docs/examples/widgets/button.css | 2 +- docs/examples/widgets/button.py | 6 +++--- docs/examples/widgets/checkbox.css | 2 +- docs/examples/widgets/checkbox.py | 4 ++-- docs/examples/widgets/placeholder.py | 5 ++--- docs/examples/widgets/radio_set_changed.css | 2 +- docs/examples/widgets/radio_set_changed.py | 4 ++-- docs/guide/layout.md | 6 +++--- docs/styles/height.md | 4 ++-- docs/styles/overflow.md | 2 +- examples/code_browser.py | 4 ++-- src/textual/_segment_tools.py | 3 +-- src/textual/cli/previews/borders.py | 4 ++-- src/textual/cli/previews/colors.py | 10 +++++----- src/textual/cli/previews/easing.py | 4 ++-- src/textual/containers.py | 8 ++++---- src/textual/widgets/_list_view.py | 4 ++-- src/textual/widgets/_markdown.py | 14 +++++++------- .../snapshot_apps/auto_width_input.py | 3 +-- .../snapshot_apps/disable_widgets.py | 7 +++---- .../snapshot_apps/focus_component_class.py | 4 ++-- tests/snapshot_tests/snapshot_apps/fr_units.py | 5 ++--- .../snapshot_apps/layer_order_independence.py | 7 +++---- .../snapshot_apps/line_api_scrollbars.py | 14 +++++++------- .../snapshot_apps/nested_auto_heights.py | 6 +++--- tests/snapshot_tests/snapshot_apps/offsets.py | 3 +-- tests/snapshot_tests/snapshot_apps/visibility.py | 9 ++++----- tests/test_disabled.py | 8 ++++---- tests/test_focus.py | 4 ++-- tests/test_overflow_change.py | 7 +++---- tests/test_visibility_change.py | 4 ++-- 47 files changed, 121 insertions(+), 127 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a008e826f..f665c00e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Updated styling to make it clear DataTable grows horizontally https://github.com/Textualize/textual/pull/1946 - Changed the `Checkbox` character due to issues with Windows Terminal and Windows 10 https://github.com/Textualize/textual/issues/1934 - Changed the `RadioButton` character due to issues with Windows Terminal and Windows 10 and 11 https://github.com/Textualize/textual/issues/1934 +- Renamed `Vertical` to `VerticalScroll` https://github.com/Textualize/textual/issues/1957 ### Added diff --git a/docs/blog/snippets/2022-12-07-responsive-app-background-task/blocking01.py b/docs/blog/snippets/2022-12-07-responsive-app-background-task/blocking01.py index 6e0ab0949..9773da0c9 100644 --- a/docs/blog/snippets/2022-12-07-responsive-app-background-task/blocking01.py +++ b/docs/blog/snippets/2022-12-07-responsive-app-background-task/blocking01.py @@ -1,9 +1,9 @@ -from random import randint import time +from random import randint from textual.app import App, ComposeResult from textual.color import Color -from textual.containers import Grid, Vertical +from textual.containers import Grid, VerticalScroll from textual.widget import Widget from textual.widgets import Footer, Label @@ -28,7 +28,7 @@ class MyApp(App[None]): def compose(self) -> ComposeResult: yield Grid( ColourChanger(), - Vertical(id="log"), + VerticalScroll(id="log"), ) yield Footer() diff --git a/docs/blog/snippets/2022-12-07-responsive-app-background-task/blocking02.py b/docs/blog/snippets/2022-12-07-responsive-app-background-task/blocking02.py index 693bde821..0edefbeff 100644 --- a/docs/blog/snippets/2022-12-07-responsive-app-background-task/blocking02.py +++ b/docs/blog/snippets/2022-12-07-responsive-app-background-task/blocking02.py @@ -1,10 +1,10 @@ import asyncio -from random import randint import time +from random import randint from textual.app import App, ComposeResult from textual.color import Color -from textual.containers import Grid, Vertical +from textual.containers import Grid, VerticalScroll from textual.widget import Widget from textual.widgets import Footer, Label @@ -29,7 +29,7 @@ class MyApp(App[None]): def compose(self) -> ComposeResult: yield Grid( ColourChanger(), - Vertical(id="log"), + VerticalScroll(id="log"), ) yield Footer() diff --git a/docs/blog/snippets/2022-12-07-responsive-app-background-task/nonblocking01.py b/docs/blog/snippets/2022-12-07-responsive-app-background-task/nonblocking01.py index 2c96e5ed6..20f2daba8 100644 --- a/docs/blog/snippets/2022-12-07-responsive-app-background-task/nonblocking01.py +++ b/docs/blog/snippets/2022-12-07-responsive-app-background-task/nonblocking01.py @@ -1,10 +1,10 @@ import asyncio -from random import randint import time +from random import randint from textual.app import App, ComposeResult from textual.color import Color -from textual.containers import Grid, Vertical +from textual.containers import Grid, VerticalScroll from textual.widget import Widget from textual.widgets import Footer, Label @@ -29,7 +29,7 @@ class MyApp(App[None]): def compose(self) -> ComposeResult: yield Grid( ColourChanger(), - Vertical(id="log"), + VerticalScroll(id="log"), ) yield Footer() diff --git a/docs/examples/events/dictionary.py b/docs/examples/events/dictionary.py index cf85c2bf6..51421ad39 100644 --- a/docs/examples/events/dictionary.py +++ b/docs/examples/events/dictionary.py @@ -6,9 +6,10 @@ except ImportError: raise ImportError("Please install httpx with 'pip install httpx' ") from rich.json import JSON + from textual.app import App, ComposeResult -from textual.containers import Vertical -from textual.widgets import Static, Input +from textual.containers import VerticalScroll +from textual.widgets import Input, Static class DictionaryApp(App): @@ -18,7 +19,7 @@ class DictionaryApp(App): def compose(self) -> ComposeResult: yield Input(placeholder="Search for a word") - yield Vertical(Static(id="results"), id="results-container") + yield VerticalScroll(Static(id="results"), id="results-container") async def on_input_changed(self, message: Input.Changed) -> None: """A coroutine to handle a text changed message.""" diff --git a/docs/examples/guide/layout/combining_layouts.py b/docs/examples/guide/layout/combining_layouts.py index c52ecce0d..e608152cd 100644 --- a/docs/examples/guide/layout/combining_layouts.py +++ b/docs/examples/guide/layout/combining_layouts.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Container, Horizontal, Vertical +from textual.containers import Container, Horizontal, VerticalScroll from textual.widgets import Header, Static @@ -9,7 +9,7 @@ class CombiningLayoutsExample(App): def compose(self) -> ComposeResult: yield Header() with Container(id="app-grid"): - with Vertical(id="left-pane"): + with VerticalScroll(id="left-pane"): for number in range(15): yield Static(f"Vertical layout, child {number}") with Horizontal(id="top-right"): diff --git a/docs/examples/guide/layout/utility_containers.py b/docs/examples/guide/layout/utility_containers.py index eadf58b4c..80cfada17 100644 --- a/docs/examples/guide/layout/utility_containers.py +++ b/docs/examples/guide/layout/utility_containers.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Horizontal, Vertical +from textual.containers import Horizontal, VerticalScroll from textual.widgets import Static @@ -8,12 +8,12 @@ class UtilityContainersExample(App): def compose(self) -> ComposeResult: yield Horizontal( - Vertical( + VerticalScroll( Static("One"), Static("Two"), classes="column", ), - Vertical( + VerticalScroll( Static("Three"), Static("Four"), classes="column", diff --git a/docs/examples/guide/layout/utility_containers_using_with.py b/docs/examples/guide/layout/utility_containers_using_with.py index d09a3481e..4abe3f9f5 100644 --- a/docs/examples/guide/layout/utility_containers_using_with.py +++ b/docs/examples/guide/layout/utility_containers_using_with.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Horizontal, Vertical +from textual.containers import Horizontal, VerticalScroll from textual.widgets import Static @@ -8,10 +8,10 @@ class UtilityContainersExample(App): def compose(self) -> ComposeResult: with Horizontal(): - with Vertical(classes="column"): + with VerticalScroll(classes="column"): yield Static("One") yield Static("Two") - with Vertical(classes="column"): + with VerticalScroll(classes="column"): yield Static("Three") yield Static("Four") diff --git a/docs/examples/styles/height_comparison.py b/docs/examples/styles/height_comparison.py index c679a68e6..41d8f0a76 100644 --- a/docs/examples/styles/height_comparison.py +++ b/docs/examples/styles/height_comparison.py @@ -1,6 +1,6 @@ from textual.app import App -from textual.containers import Vertical -from textual.widgets import Placeholder, Label, Static +from textual.containers import VerticalScroll +from textual.widgets import Label, Placeholder, Static class Ruler(Static): @@ -11,7 +11,7 @@ class Ruler(Static): class HeightComparisonApp(App): def compose(self): - yield Vertical( + yield VerticalScroll( Placeholder(id="cells"), # (1)! Placeholder(id="percent"), Placeholder(id="w"), diff --git a/docs/examples/styles/max_width.py b/docs/examples/styles/max_width.py index 0ee440a92..7ea482dc7 100644 --- a/docs/examples/styles/max_width.py +++ b/docs/examples/styles/max_width.py @@ -1,11 +1,11 @@ from textual.app import App -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Placeholder class MaxWidthApp(App): def compose(self): - yield Vertical( + yield VerticalScroll( Placeholder("max-width: 50h", id="p1"), Placeholder("max-width: 999", id="p2"), Placeholder("max-width: 50%", id="p3"), diff --git a/docs/examples/styles/min_width.css b/docs/examples/styles/min_width.css index 43dd8400a..ba2dd81c6 100644 --- a/docs/examples/styles/min_width.css +++ b/docs/examples/styles/min_width.css @@ -1,4 +1,4 @@ -Vertical { +VerticalScroll { height: 100%; width: 100%; overflow-x: auto; @@ -10,7 +10,8 @@ Placeholder { } #p1 { - min-width: 25%; /* (1)! */ + min-width: 25%; + /* (1)! */ } #p2 { diff --git a/docs/examples/styles/min_width.py b/docs/examples/styles/min_width.py index 25195c61d..b00881266 100644 --- a/docs/examples/styles/min_width.py +++ b/docs/examples/styles/min_width.py @@ -1,11 +1,11 @@ from textual.app import App -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Placeholder class MinWidthApp(App): def compose(self): - yield Vertical( + yield VerticalScroll( Placeholder("min-width: 25%", id="p1"), Placeholder("min-width: 75%", id="p2"), Placeholder("min-width: 100", id="p3"), diff --git a/docs/examples/styles/overflow.css b/docs/examples/styles/overflow.css index 3b68440c7..527429097 100644 --- a/docs/examples/styles/overflow.css +++ b/docs/examples/styles/overflow.css @@ -3,7 +3,7 @@ Screen { color: black; } -Vertical { +VerticalScroll { width: 1fr; } @@ -13,7 +13,7 @@ Static { border: green wide; color: white 90%; height: auto; -} +} #right { overflow-y: hidden; diff --git a/docs/examples/styles/overflow.py b/docs/examples/styles/overflow.py index b9a6c3383..debe0252d 100644 --- a/docs/examples/styles/overflow.py +++ b/docs/examples/styles/overflow.py @@ -1,6 +1,6 @@ from textual.app import App +from textual.containers import Horizontal, VerticalScroll from textual.widgets import Static -from textual.containers import Horizontal, Vertical TEXT = """I must not fear. Fear is the mind-killer. @@ -14,8 +14,8 @@ Where the fear has gone there will be nothing. Only I will remain.""" class OverflowApp(App): def compose(self): yield Horizontal( - Vertical(Static(TEXT), Static(TEXT), Static(TEXT), id="left"), - Vertical(Static(TEXT), Static(TEXT), Static(TEXT), id="right"), + VerticalScroll(Static(TEXT), Static(TEXT), Static(TEXT), id="left"), + VerticalScroll(Static(TEXT), Static(TEXT), Static(TEXT), id="right"), ) diff --git a/docs/examples/styles/scrollbar_size.py b/docs/examples/styles/scrollbar_size.py index 1bbcec572..e949aaaca 100644 --- a/docs/examples/styles/scrollbar_size.py +++ b/docs/examples/styles/scrollbar_size.py @@ -1,5 +1,5 @@ from textual.app import App -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Label TEXT = """I must not fear. @@ -14,7 +14,7 @@ Where the fear has gone there will be nothing. Only I will remain. class ScrollbarApp(App): def compose(self): - yield Vertical(Label(TEXT * 5), classes="panel") + yield VerticalScroll(Label(TEXT * 5), classes="panel") app = ScrollbarApp(css_path="scrollbar_size.css") diff --git a/docs/examples/styles/visibility_containers.py b/docs/examples/styles/visibility_containers.py index 879b916fb..a94de145d 100644 --- a/docs/examples/styles/visibility_containers.py +++ b/docs/examples/styles/visibility_containers.py @@ -1,11 +1,11 @@ from textual.app import App -from textual.containers import Horizontal, Vertical +from textual.containers import Horizontal, VerticalScroll from textual.widgets import Placeholder class VisibilityContainersApp(App): def compose(self): - yield Vertical( + yield VerticalScroll( Horizontal( Placeholder(), Placeholder(), diff --git a/docs/examples/widgets/button.css b/docs/examples/widgets/button.css index 5f1c906da..aee652f68 100644 --- a/docs/examples/widgets/button.css +++ b/docs/examples/widgets/button.css @@ -2,7 +2,7 @@ Button { margin: 1 2; } -Horizontal > Vertical { +Horizontal>VerticalScroll { width: 24; } diff --git a/docs/examples/widgets/button.py b/docs/examples/widgets/button.py index 4c3509c32..09339ccb0 100644 --- a/docs/examples/widgets/button.py +++ b/docs/examples/widgets/button.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Horizontal, Vertical +from textual.containers import Horizontal, VerticalScroll from textual.widgets import Button, Static @@ -8,7 +8,7 @@ class ButtonsApp(App[str]): def compose(self) -> ComposeResult: yield Horizontal( - Vertical( + VerticalScroll( Static("Standard Buttons", classes="header"), Button("Default"), Button("Primary!", variant="primary"), @@ -16,7 +16,7 @@ class ButtonsApp(App[str]): Button.warning("Warning!"), Button.error("Error!"), ), - Vertical( + VerticalScroll( Static("Disabled Buttons", classes="header"), Button("Default", disabled=True), Button("Primary!", variant="primary", disabled=True), diff --git a/docs/examples/widgets/checkbox.css b/docs/examples/widgets/checkbox.css index 4e80b6685..b6e17c093 100644 --- a/docs/examples/widgets/checkbox.css +++ b/docs/examples/widgets/checkbox.css @@ -2,7 +2,7 @@ Screen { align: center middle; } -Vertical { +VerticalScroll { width: auto; height: auto; border: solid $primary; diff --git a/docs/examples/widgets/checkbox.py b/docs/examples/widgets/checkbox.py index 5ad6ca1ff..75eadda0c 100644 --- a/docs/examples/widgets/checkbox.py +++ b/docs/examples/widgets/checkbox.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Checkbox @@ -7,7 +7,7 @@ class CheckboxApp(App[None]): CSS_PATH = "checkbox.css" def compose(self) -> ComposeResult: - with Vertical(): + with VerticalScroll(): yield Checkbox("Arrakis :sweat:") yield Checkbox("Caladan") yield Checkbox("Chusuk") diff --git a/docs/examples/widgets/placeholder.py b/docs/examples/widgets/placeholder.py index c09e9c412..9c7d6eb0e 100644 --- a/docs/examples/widgets/placeholder.py +++ b/docs/examples/widgets/placeholder.py @@ -1,14 +1,13 @@ from textual.app import App, ComposeResult -from textual.containers import Container, Horizontal, Vertical +from textual.containers import Container, Horizontal, VerticalScroll from textual.widgets import Placeholder class PlaceholderApp(App): - CSS_PATH = "placeholder.css" def compose(self) -> ComposeResult: - yield Vertical( + yield VerticalScroll( Container( Placeholder("This is a custom label for p1.", id="p1"), Placeholder("Placeholder p2 here!", id="p2"), diff --git a/docs/examples/widgets/radio_set_changed.css b/docs/examples/widgets/radio_set_changed.css index 47583c1d8..fc1299294 100644 --- a/docs/examples/widgets/radio_set_changed.css +++ b/docs/examples/widgets/radio_set_changed.css @@ -1,4 +1,4 @@ -Vertical { +VerticalScroll { align: center middle; } diff --git a/docs/examples/widgets/radio_set_changed.py b/docs/examples/widgets/radio_set_changed.py index c360cb868..c817b6e6f 100644 --- a/docs/examples/widgets/radio_set_changed.py +++ b/docs/examples/widgets/radio_set_changed.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Horizontal, Vertical +from textual.containers import Horizontal, VerticalScroll from textual.widgets import Label, RadioButton, RadioSet @@ -7,7 +7,7 @@ class RadioSetChangedApp(App[None]): CSS_PATH = "radio_set_changed.css" def compose(self) -> ComposeResult: - with Vertical(): + with VerticalScroll(): with Horizontal(): with RadioSet(): yield RadioButton("Battlestar Galactica") diff --git a/docs/guide/layout.md b/docs/guide/layout.md index 3fe1184e0..db8d97dfe 100644 --- a/docs/guide/layout.md +++ b/docs/guide/layout.md @@ -135,10 +135,10 @@ exceeds the available horizontal space in the parent container. ## Utility containers Textual comes with several "container" widgets. -These are [Vertical][textual.containers.Vertical], [Horizontal][textual.containers.Horizontal], and [Grid][textual.containers.Grid] which have the corresponding layout. +These are [VerticalScroll][textual.containers.VerticalScroll], [Horizontal][textual.containers.Horizontal], and [Grid][textual.containers.Grid] which have the corresponding layout. The example below shows how we can combine these containers to create a simple 2x2 grid. -Inside a single `Horizontal` container, we place two `Vertical` containers. +Inside a single `Horizontal` container, we place two `VerticalScroll` containers. In other words, we have a single row containing two columns. === "Output" @@ -163,7 +163,7 @@ However, Textual comes with a more powerful mechanism for achieving this known a ## Composing with context managers -In the previous section we've show how you add children to a container (such as `Horizontal` and `Vertical`) using positional arguments. +In the previous section we've show how you add children to a container (such as `Horizontal` and `VerticalScroll`) using positional arguments. It's fine to do it this way, but Textual offers a simplified syntax using [context managers](https://docs.python.org/3/reference/datamodel.html#context-managers) which is generally easier to write and edit. When composing a widget, you can introduce a container using Python's `with` statement. diff --git a/docs/styles/height.md b/docs/styles/height.md index 15d5399a3..bd3fe3512 100644 --- a/docs/styles/height.md +++ b/docs/styles/height.md @@ -61,8 +61,8 @@ Open the CSS file tab to see the comments that explain how each height is comput 1. This sets the height to 2 lines. 2. This sets the height to 12.5% of the space made available by the container. The container is 24 lines tall, so 12.5% of 24 is 3. - 3. This sets the height to 5% of the width of the direct container, which is the `Vertical` container. Because it expands to fit all of the terminal, the width of the `Vertical` is 80 and 5% of 80 is 4. - 4. This sets the height to 12.5% of the height of the direct container, which is the `Vertical` container. Because it expands to fit all of the terminal, the height of the `Vertical` is 24 and 12.5% of 24 is 3. + 3. This sets the height to 5% of the width of the direct container, which is the `VerticalScroll` container. Because it expands to fit all of the terminal, the width of the `VerticalScroll` is 80 and 5% of 80 is 4. + 4. This sets the height to 12.5% of the height of the direct container, which is the `VerticalScroll` container. Because it expands to fit all of the terminal, the height of the `VerticalScroll` is 24 and 12.5% of 24 is 3. 5. This sets the height to 6.25% of the viewport width, which is 80. 6.25% of 80 is 5. 6. This sets the height to 12.5% of the viewport height, which is 24. 12.5% of 24 is 3. 7. This sets the height of the placeholder to be the optimal size that fits the content without scrolling. diff --git a/docs/styles/overflow.md b/docs/styles/overflow.md index 12edaac15..d3f0ece5f 100644 --- a/docs/styles/overflow.md +++ b/docs/styles/overflow.md @@ -25,7 +25,7 @@ The default setting for containers is `overflow: auto auto`. !!! warning - Some built-in containers like `Horizontal` and `Vertical` override these defaults. + Some built-in containers like `Horizontal` and `VerticalScroll` override these defaults. ## Example diff --git a/examples/code_browser.py b/examples/code_browser.py index 4616be4f7..13fdf3c6d 100644 --- a/examples/code_browser.py +++ b/examples/code_browser.py @@ -14,7 +14,7 @@ from rich.traceback import Traceback from textual import events from textual.app import App, ComposeResult -from textual.containers import Container, Vertical +from textual.containers import Container, VerticalScroll from textual.reactive import var from textual.widgets import DirectoryTree, Footer, Header, Static @@ -40,7 +40,7 @@ class CodeBrowser(App): yield Header() with Container(): yield DirectoryTree(path, id="tree-view") - with Vertical(id="code-view"): + with VerticalScroll(id="code-view"): yield Static(id="code", expand=True) yield Footer() diff --git a/src/textual/_segment_tools.py b/src/textual/_segment_tools.py index 5f9df333b..79f3bc077 100644 --- a/src/textual/_segment_tools.py +++ b/src/textual/_segment_tools.py @@ -192,11 +192,10 @@ def align_lines( style: Background style. size: Size of container. horizontal: Horizontal alignment. - vertical: Vertical alignment + vertical: Vertical alignment. Returns: Aligned lines. - """ width, height = size diff --git a/src/textual/cli/previews/borders.py b/src/textual/cli/previews/borders.py index a33320a0a..09dbcaab4 100644 --- a/src/textual/cli/previews/borders.py +++ b/src/textual/cli/previews/borders.py @@ -1,6 +1,6 @@ from textual.app import App, ComposeResult from textual.constants import BORDERS -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Button, Label TEXT = """I must not fear. @@ -12,7 +12,7 @@ And when it has gone past, I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.""" -class BorderButtons(Vertical): +class BorderButtons(VerticalScroll): DEFAULT_CSS = """ BorderButtons { dock: left; diff --git a/src/textual/cli/previews/colors.py b/src/textual/cli/previews/colors.py index b9d8da3eb..d8028f7c5 100644 --- a/src/textual/cli/previews/colors.py +++ b/src/textual/cli/previews/colors.py @@ -1,11 +1,11 @@ from textual.app import App, ComposeResult -from textual.containers import Horizontal, Vertical +from textual.containers import Horizontal, VerticalScroll from textual.design import ColorSystem from textual.widget import Widget from textual.widgets import Button, Footer, Label, Static -class ColorButtons(Vertical): +class ColorButtons(VerticalScroll): def compose(self) -> ComposeResult: for border in ColorSystem.COLOR_NAMES: if border: @@ -20,15 +20,15 @@ class ColorItem(Horizontal): pass -class ColorGroup(Vertical): +class ColorGroup(VerticalScroll): pass -class Content(Vertical): +class Content(VerticalScroll): pass -class ColorsView(Vertical): +class ColorsView(VerticalScroll): def compose(self) -> ComposeResult: LEVELS = [ "darken-3", diff --git a/src/textual/cli/previews/easing.py b/src/textual/cli/previews/easing.py index 70dc45758..7bca58efa 100644 --- a/src/textual/cli/previews/easing.py +++ b/src/textual/cli/previews/easing.py @@ -5,7 +5,7 @@ from rich.console import RenderableType from textual._easing import EASING from textual.app import App, ComposeResult from textual.cli.previews.borders import TEXT -from textual.containers import Container, Horizontal, Vertical +from textual.containers import Container, Horizontal, VerticalScroll from textual.reactive import reactive, var from textual.scrollbar import ScrollBarRender from textual.widget import Widget @@ -73,7 +73,7 @@ class EasingApp(App): ) yield EasingButtons() - with Vertical(): + with VerticalScroll(): with Horizontal(id="inputs"): yield Label("Animation Duration:", id="label") yield duration_input diff --git a/src/textual/containers.py b/src/textual/containers.py index bbd4c13d7..37e3729e2 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -13,11 +13,11 @@ class Container(Widget): """ -class Vertical(Widget): +class VerticalScroll(Widget): """A container widget which aligns children vertically.""" DEFAULT_CSS = """ - Vertical { + VerticalScroll { height: 1fr; layout: vertical; overflow-y: auto; @@ -44,7 +44,7 @@ class Grid(Widget): Grid { height: 1fr; layout: grid; - } + } """ @@ -52,7 +52,7 @@ class Content(Widget, can_focus=True, can_focus_children=False): """A container for content such as text.""" DEFAULT_CSS = """ - Vertical { + VerticalScroll { height: 1fr; layout: vertical; overflow-y: auto; diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index 013c51ead..8f95d011d 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -4,7 +4,7 @@ from typing import ClassVar, Optional from textual.await_remove import AwaitRemove from textual.binding import Binding, BindingType -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.geometry import clamp from textual.message import Message from textual.reactive import reactive @@ -12,7 +12,7 @@ from textual.widget import AwaitMount, Widget from textual.widgets._list_item import ListItem -class ListView(Vertical, can_focus=True, can_focus_children=False): +class ListView(VerticalScroll, can_focus=True, can_focus_children=False): """A vertical list view widget. Displays a vertical list of `ListItem`s which can be highlighted and diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index f352683f7..c55fe1f94 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -10,7 +10,7 @@ from rich.text import Text from typing_extensions import TypeAlias from ..app import ComposeResult -from ..containers import Horizontal, Vertical +from ..containers import Horizontal, VerticalScroll from ..message import Message from ..reactive import reactive, var from ..widget import Widget @@ -266,7 +266,7 @@ class MarkdownBulletList(MarkdownList): width: 1fr; } - MarkdownBulletList Vertical { + MarkdownBulletList VerticalScroll { height: auto; width: 1fr; } @@ -277,7 +277,7 @@ class MarkdownBulletList(MarkdownList): if isinstance(block, MarkdownListItem): bullet = MarkdownBullet() bullet.symbol = block.bullet - yield Horizontal(bullet, Vertical(*block._blocks)) + yield Horizontal(bullet, VerticalScroll(*block._blocks)) self._blocks.clear() @@ -295,7 +295,7 @@ class MarkdownOrderedList(MarkdownList): width: 1fr; } - MarkdownOrderedList Vertical { + MarkdownOrderedList VerticalScroll { height: auto; width: 1fr; } @@ -311,7 +311,7 @@ class MarkdownOrderedList(MarkdownList): if isinstance(block, MarkdownListItem): bullet = MarkdownBullet() bullet.symbol = block.bullet.rjust(symbol_size + 1) - yield Horizontal(bullet, Vertical(*block._blocks)) + yield Horizontal(bullet, VerticalScroll(*block._blocks)) self._blocks.clear() @@ -410,7 +410,7 @@ class MarkdownListItem(MarkdownBlock): height: auto; } - MarkdownListItem > Vertical { + MarkdownListItem > VerticalScroll { width: 1fr; height: auto; } @@ -761,7 +761,7 @@ class MarkdownTableOfContents(Widget, can_focus_children=True): ) -class MarkdownViewer(Vertical, can_focus=True, can_focus_children=True): +class MarkdownViewer(VerticalScroll, can_focus=True, can_focus_children=True): """A Markdown viewer widget.""" DEFAULT_CSS = """ diff --git a/tests/snapshot_tests/snapshot_apps/auto_width_input.py b/tests/snapshot_tests/snapshot_apps/auto_width_input.py index 35cc3b2ec..e312e653e 100644 --- a/tests/snapshot_tests/snapshot_apps/auto_width_input.py +++ b/tests/snapshot_tests/snapshot_apps/auto_width_input.py @@ -1,10 +1,9 @@ from textual.app import App, ComposeResult -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Header, Footer, Label, Input class InputWidthAutoApp(App[None]): - CSS = """ Input.auto { width: auto; diff --git a/tests/snapshot_tests/snapshot_apps/disable_widgets.py b/tests/snapshot_tests/snapshot_apps/disable_widgets.py index 7a241e914..fb321b3b6 100644 --- a/tests/snapshot_tests/snapshot_apps/disable_widgets.py +++ b/tests/snapshot_tests/snapshot_apps/disable_widgets.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Vertical, Horizontal +from textual.containers import VerticalScroll, Horizontal from textual.widgets import ( Header, Footer, @@ -17,7 +17,6 @@ from textual.widgets import ( class WidgetDisableTestApp(App[None]): - CSS = """ Horizontal { height: auto; @@ -54,7 +53,7 @@ class WidgetDisableTestApp(App[None]): def compose(self) -> ComposeResult: yield Header() - yield Vertical( + yield VerticalScroll( Horizontal( Button(), Button(variant="primary"), @@ -77,7 +76,7 @@ class WidgetDisableTestApp(App[None]): def on_mount(self) -> None: self.query_one(TextLog).write("Hello, World!") - self.query_one("#test-container", Vertical).disabled = True + self.query_one("#test-container", VerticalScroll).disabled = True if __name__ == "__main__": diff --git a/tests/snapshot_tests/snapshot_apps/focus_component_class.py b/tests/snapshot_tests/snapshot_apps/focus_component_class.py index 85becd79b..77ac46212 100644 --- a/tests/snapshot_tests/snapshot_apps/focus_component_class.py +++ b/tests/snapshot_tests/snapshot_apps/focus_component_class.py @@ -1,7 +1,7 @@ from rich.text import Text from textual.app import App, ComposeResult, RenderResult -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Header, Footer from textual.widget import Widget @@ -32,7 +32,7 @@ class Tester(Widget, can_focus=True): class StyleBugApp(App[None]): def compose(self) -> ComposeResult: yield Header() - with Vertical(): + with VerticalScroll(): for n in range(40): yield Tester(n) yield Footer() diff --git a/tests/snapshot_tests/snapshot_apps/fr_units.py b/tests/snapshot_tests/snapshot_apps/fr_units.py index d9bcddeb9..1ece79ebc 100644 --- a/tests/snapshot_tests/snapshot_apps/fr_units.py +++ b/tests/snapshot_tests/snapshot_apps/fr_units.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Horizontal, Vertical +from textual.containers import Horizontal, VerticalScroll from textual.widgets import Static @@ -8,7 +8,6 @@ class StaticText(Static): class FRApp(App): - CSS = """ StaticText { height: 1fr; @@ -39,7 +38,7 @@ class FRApp(App): """ def compose(self) -> ComposeResult: - yield Vertical( + yield VerticalScroll( StaticText("HEADER", id="header"), Horizontal( StaticText("foo", id="foo"), diff --git a/tests/snapshot_tests/snapshot_apps/layer_order_independence.py b/tests/snapshot_tests/snapshot_apps/layer_order_independence.py index 2ad43e931..a3deec8ba 100644 --- a/tests/snapshot_tests/snapshot_apps/layer_order_independence.py +++ b/tests/snapshot_tests/snapshot_apps/layer_order_independence.py @@ -1,7 +1,7 @@ from textual.app import App, ComposeResult from textual.screen import Screen from textual.widgets import Header, Footer, Label -from textual.containers import Vertical, Container +from textual.containers import VerticalScroll, Container class Overlay(Container): @@ -9,12 +9,12 @@ class Overlay(Container): yield Label("This should float over the top") -class Body1(Vertical): +class Body1(VerticalScroll): def compose(self) -> ComposeResult: yield Label("My God! It's full of stars! " * 300) -class Body2(Vertical): +class Body2(VerticalScroll): def compose(self) -> ComposeResult: yield Label("My God! It's full of stars! " * 300) @@ -36,7 +36,6 @@ class Bad(Screen): class Layers(App[None]): - CSS = """ Screen { layers: base higher; diff --git a/tests/snapshot_tests/snapshot_apps/line_api_scrollbars.py b/tests/snapshot_tests/snapshot_apps/line_api_scrollbars.py index 68a16a099..26ab2c985 100644 --- a/tests/snapshot_tests/snapshot_apps/line_api_scrollbars.py +++ b/tests/snapshot_tests/snapshot_apps/line_api_scrollbars.py @@ -1,7 +1,7 @@ from rich.text import Text from textual.app import App, ComposeResult -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widget import Widget from textual.widgets import TextLog @@ -21,32 +21,32 @@ class ScrollViewApp(App): Screen { align: center middle; } - + TextLog { width:13; - height:10; + height:10; } - Vertical{ + VerticalScroll { width:13; height: 10; overflow: scroll; overflow-x: auto; } + MyWidget { width:13; height:auto; } - """ def compose(self) -> ComposeResult: yield TextLog() - yield Vertical(MyWidget()) + yield VerticalScroll(MyWidget()) def on_ready(self) -> None: self.query_one(TextLog).write("\n".join(f"{n} 0123456789" for n in range(20))) - self.query_one(Vertical).scroll_end(animate=False) + self.query_one(VerticalScroll).scroll_end(animate=False) if __name__ == "__main__": diff --git a/tests/snapshot_tests/snapshot_apps/nested_auto_heights.py b/tests/snapshot_tests/snapshot_apps/nested_auto_heights.py index 5df6c2960..4ed40f10e 100644 --- a/tests/snapshot_tests/snapshot_apps/nested_auto_heights.py +++ b/tests/snapshot_tests/snapshot_apps/nested_auto_heights.py @@ -3,7 +3,7 @@ from __future__ import annotations from textual.app import App, ComposeResult -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Static @@ -42,8 +42,8 @@ class NestedAutoApp(App[None]): def compose(self) -> ComposeResult: self._static = Static("", id="my-static") - yield Vertical( - Vertical( + yield VerticalScroll( + VerticalScroll( self._static, id="my-static-wrapper", ), diff --git a/tests/snapshot_tests/snapshot_apps/offsets.py b/tests/snapshot_tests/snapshot_apps/offsets.py index 12099b126..23d21bb38 100644 --- a/tests/snapshot_tests/snapshot_apps/offsets.py +++ b/tests/snapshot_tests/snapshot_apps/offsets.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Label, Static @@ -18,7 +18,6 @@ class Box(Static): class OffsetsApp(App): - CSS = """ #box1 { diff --git a/tests/snapshot_tests/snapshot_apps/visibility.py b/tests/snapshot_tests/snapshot_apps/visibility.py index a5ccccb7e..7dbfcf7b4 100644 --- a/tests/snapshot_tests/snapshot_apps/visibility.py +++ b/tests/snapshot_tests/snapshot_apps/visibility.py @@ -1,5 +1,5 @@ from textual.app import App -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import Static @@ -10,7 +10,7 @@ class Visibility(App): Screen { layout: horizontal; } - Vertical { + VerticalScroll { width: 1fr; border: solid red; } @@ -30,13 +30,12 @@ class Visibility(App): """ def compose(self): - - yield Vertical( + yield VerticalScroll( Static("foo"), Static("float", classes="float"), id="container1", ) - yield Vertical( + yield VerticalScroll( Static("bar"), Static("float", classes="float"), id="container2", diff --git a/tests/test_disabled.py b/tests/test_disabled.py index 850fcf7c7..bc0692ad0 100644 --- a/tests/test_disabled.py +++ b/tests/test_disabled.py @@ -1,7 +1,7 @@ """Test Widget.disabled.""" from textual.app import App, ComposeResult -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widgets import ( Button, DataTable, @@ -21,7 +21,7 @@ class DisableApp(App[None]): def compose(self) -> ComposeResult: """Compose the child widgets.""" - yield Vertical( + yield VerticalScroll( Button(), DataTable(), DirectoryTree("."), @@ -56,7 +56,7 @@ async def test_enabled_widgets_have_enabled_pseudo_class() -> None: async def test_all_individually_disabled() -> None: """Post-disable all widgets should report being disabled.""" async with DisableApp().run_test() as pilot: - for node in pilot.app.screen.query("Vertical > *"): + for node in pilot.app.screen.query("VerticalScroll > *"): node.disabled = True assert all( node.disabled for node in pilot.app.screen.query("#test-container > *") @@ -77,7 +77,7 @@ async def test_disabled_widgets_have_disabled_pseudo_class() -> None: async def test_disable_via_container() -> None: """All child widgets should appear (to CSS) as disabled by a container being disabled.""" async with DisableApp().run_test() as pilot: - pilot.app.screen.query_one("#test-container", Vertical).disabled = True + pilot.app.screen.query_one("#test-container", VerticalScroll).disabled = True assert all( node.has_pseudo_class("disabled") and not node.has_pseudo_class("enabled") for node in pilot.app.screen.query("#test-container > *") diff --git a/tests/test_focus.py b/tests/test_focus.py index b4d9ce8c5..a03b9b53c 100644 --- a/tests/test_focus.py +++ b/tests/test_focus.py @@ -151,11 +151,11 @@ def test_focus_next_and_previous_with_type_selector_without_self(): screen = app.screen - from textual.containers import Horizontal, Vertical + from textual.containers import Horizontal, VerticalScroll from textual.widgets import Button, Input, Switch screen._add_children( - Vertical( + VerticalScroll( Horizontal( Input(id="w3"), Switch(id="w4"), diff --git a/tests/test_overflow_change.py b/tests/test_overflow_change.py index 236a15f8c..7f5d7eb46 100644 --- a/tests/test_overflow_change.py +++ b/tests/test_overflow_change.py @@ -1,19 +1,18 @@ """Regression test for #1616 https://github.com/Textualize/textual/issues/1616""" -import pytest from textual.app import App -from textual.containers import Vertical +from textual.containers import VerticalScroll async def test_overflow_change_updates_virtual_size_appropriately(): class MyApp(App): def compose(self): - yield Vertical() + yield VerticalScroll() app = MyApp() async with app.run_test() as pilot: - vertical = app.query_one(Vertical) + vertical = app.query_one(VerticalScroll) height = vertical.virtual_size.height diff --git a/tests/test_visibility_change.py b/tests/test_visibility_change.py index f79f18a6f..7006827ee 100644 --- a/tests/test_visibility_change.py +++ b/tests/test_visibility_change.py @@ -1,7 +1,7 @@ """See https://github.com/Textualize/textual/issues/1355 as the motivation for these tests.""" from textual.app import App, ComposeResult -from textual.containers import Vertical +from textual.containers import VerticalScroll from textual.widget import Widget @@ -18,7 +18,7 @@ class VisibleTester(App[None]): """ def compose(self) -> ComposeResult: - yield Vertical( + yield VerticalScroll( Widget(id="keep"), Widget(id="hide-via-code"), Widget(id="hide-via-css") ) From 4ca62eee6097136b06472a91c83bbe4b3e72f123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 12:21:53 +0000 Subject: [PATCH 02/16] Update docstring. --- src/textual/containers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/containers.py b/src/textual/containers.py index 37e3729e2..0d45e8cda 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -14,7 +14,7 @@ class Container(Widget): class VerticalScroll(Widget): - """A container widget which aligns children vertically.""" + """A container widget which aligns children vertically and overflows if needed.""" DEFAULT_CSS = """ VerticalScroll { From 90dce06eaef107f0b7b274784da50af2faee7c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 14:35:49 +0000 Subject: [PATCH 03/16] Add 'HorizontalScroll'. Related issues: #1957. --- CHANGELOG.md | 1 + src/textual/containers.py | 12 ++++++++++++ tests/test_containers.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 tests/test_containers.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f665c00e4..b403661c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Breaking change: Added `toggle_button` attribute to RadioButton and Checkbox events, replaces `input` https://github.com/Textualize/textual/pull/1940 - A percentage alpha can now be applied to a border https://github.com/Textualize/textual/issues/1863 - Added `Color.multiply_alpha`. +- Added `HorizontalScroll` https://github.com/Textualize/textual/issues/1957 ### Fixed diff --git a/src/textual/containers.py b/src/textual/containers.py index 0d45e8cda..946ab52e1 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -37,6 +37,18 @@ class Horizontal(Widget): """ +class HorizontalScroll(Widget): + """A container widget which aligns children horizontally and overflows if needed.""" + + DEFAULT_CSS = """ + HorizontalScroll { + height: 1fr; + layout: horizontal; + overflow-x: auto; + } + """ + + class Grid(Widget): """A container widget with grid alignment.""" diff --git a/tests/test_containers.py b/tests/test_containers.py new file mode 100644 index 000000000..d84941082 --- /dev/null +++ b/tests/test_containers.py @@ -0,0 +1,34 @@ +"""Test basic functioning of some containers.""" + +from textual.app import App, ComposeResult +from textual.containers import Horizontal, HorizontalScroll +from textual.widgets import Label + + +async def test_horizontal_vs_horizontalscroll_scrolling(): + """Check the default scrollbar behaviours for Horizontal and HorizontalScroll.""" + + class HorizontalsApp(App[None]): + CSS = """ + Screen { + layout: vertical; + } + """ + + def compose(self) -> ComposeResult: + with Horizontal(): + for _ in range(10): + yield Label("How is life going? " * 3 + " | ") + with HorizontalScroll(): + for _ in range(10): + yield Label("How is life going? " * 3 + " | ") + + WIDTH = 80 + HEIGHT = 24 + app = HorizontalsApp() + async with app.run_test(size=(WIDTH, HEIGHT)): + horizontal = app.query_one(Horizontal) + horizontal_scroll = app.query_one(HorizontalScroll) + assert horizontal.size.height == horizontal_scroll.size.height + assert horizontal.scrollbars_enabled == (False, False) + assert horizontal_scroll.scrollbars_enabled == (False, True) From f91750ed3d45c416a6da42ddb4c2f63242c53d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:20:36 +0000 Subject: [PATCH 04/16] Add container 'Center'. This container will centre children horizontally. Related issues: #1957. --- CHANGELOG.md | 1 + src/textual/containers.py | 12 ++++++++++++ tests/test_containers.py | 19 +++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce0bf2b8a..59e064974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added `HorizontalScroll` https://github.com/Textualize/textual/issues/1957 +- Added `Center` https://github.com/Textualize/textual/issues/1957 ## [0.14.0] - 2023-03-09 diff --git a/src/textual/containers.py b/src/textual/containers.py index 946ab52e1..a1836aa85 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -49,6 +49,18 @@ class HorizontalScroll(Widget): """ +class Center(Widget): + """A container widget which centers children horizontally.""" + + DEFAULT_CSS = """ + Center { + align-horizontal: center; + width: 100%; + height: auto; + } + """ + + class Grid(Widget): """A container widget with grid alignment.""" diff --git a/tests/test_containers.py b/tests/test_containers.py index d84941082..7dde9446e 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -1,12 +1,12 @@ """Test basic functioning of some containers.""" from textual.app import App, ComposeResult -from textual.containers import Horizontal, HorizontalScroll +from textual.containers import Center, Horizontal, HorizontalScroll from textual.widgets import Label async def test_horizontal_vs_horizontalscroll_scrolling(): - """Check the default scrollbar behaviours for Horizontal and HorizontalScroll.""" + """Check the default scrollbar behaviours for `Horizontal` and `HorizontalScroll`.""" class HorizontalsApp(App[None]): CSS = """ @@ -32,3 +32,18 @@ async def test_horizontal_vs_horizontalscroll_scrolling(): assert horizontal.size.height == horizontal_scroll.size.height assert horizontal.scrollbars_enabled == (False, False) assert horizontal_scroll.scrollbars_enabled == (False, True) + + +async def test_center_container(): + """Check the size of the container `Center`.""" + + class CenterApp(App[None]): + def compose(self) -> ComposeResult: + with Center(): + yield Label("<>\n<>\n<>") + + app = CenterApp() + async with app.run_test(): + center = app.query_one(Center) + assert center.size.width == app.size.width + assert center.size.height == 3 From e111449856da0006d7b53051bad029c959da9201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:24:13 +0000 Subject: [PATCH 05/16] Add container 'Middle'. Related issues: #1957. --- CHANGELOG.md | 1 + src/textual/containers.py | 12 ++++++++++++ tests/test_containers.py | 17 ++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e064974..2670f3f32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `HorizontalScroll` https://github.com/Textualize/textual/issues/1957 - Added `Center` https://github.com/Textualize/textual/issues/1957 +- Added `Middle` https://github.com/Textualize/textual/issues/1957 ## [0.14.0] - 2023-03-09 diff --git a/src/textual/containers.py b/src/textual/containers.py index a1836aa85..156965ff2 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -61,6 +61,18 @@ class Center(Widget): """ +class Middle(Widget): + """A container widget which aligns children vertically in the middle.""" + + DEFAULT_CSS = """ + Middle { + align-vertical: middle; + height: 100%; + width: auto; + } + """ + + class Grid(Widget): """A container widget with grid alignment.""" diff --git a/tests/test_containers.py b/tests/test_containers.py index 7dde9446e..866e62800 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -1,7 +1,7 @@ """Test basic functioning of some containers.""" from textual.app import App, ComposeResult -from textual.containers import Center, Horizontal, HorizontalScroll +from textual.containers import Center, Horizontal, HorizontalScroll, Middle from textual.widgets import Label @@ -47,3 +47,18 @@ async def test_center_container(): center = app.query_one(Center) assert center.size.width == app.size.width assert center.size.height == 3 + + +async def test_middle_container(): + """Check the size of the container `Middle`.""" + + class MiddleApp(App[None]): + def compose(self) -> ComposeResult: + with Middle(): + yield Label("1234") + + app = MiddleApp() + async with app.run_test(): + middle = app.query_one(Middle) + assert middle.size.width == 4 + assert middle.size.height == app.size.height From 58ad5dfdd947de78785fe4ff6868ba2ab0026b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:26:16 +0000 Subject: [PATCH 06/16] Tweak docstrings. --- src/textual/containers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/textual/containers.py b/src/textual/containers.py index 156965ff2..25633d1df 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -14,7 +14,7 @@ class Container(Widget): class VerticalScroll(Widget): - """A container widget which aligns children vertically and overflows if needed.""" + """A container which aligns children vertically and overflows automatically.""" DEFAULT_CSS = """ VerticalScroll { @@ -26,7 +26,7 @@ class VerticalScroll(Widget): class Horizontal(Widget): - """A container widget which aligns children horizontally.""" + """A container which lays children horizontally.""" DEFAULT_CSS = """ Horizontal { @@ -38,7 +38,7 @@ class Horizontal(Widget): class HorizontalScroll(Widget): - """A container widget which aligns children horizontally and overflows if needed.""" + """A container which lays children horizontally and overflows automatically.""" DEFAULT_CSS = """ HorizontalScroll { @@ -50,7 +50,7 @@ class HorizontalScroll(Widget): class Center(Widget): - """A container widget which centers children horizontally.""" + """A container which centers children horizontally.""" DEFAULT_CSS = """ Center { @@ -62,7 +62,7 @@ class Center(Widget): class Middle(Widget): - """A container widget which aligns children vertically in the middle.""" + """A container which aligns children vertically in the middle.""" DEFAULT_CSS = """ Middle { @@ -74,7 +74,7 @@ class Middle(Widget): class Grid(Widget): - """A container widget with grid alignment.""" + """A container with grid alignment.""" DEFAULT_CSS = """ Grid { From 639d8f0250bd76b38113ed0b7c81503a0c577498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:30:44 +0000 Subject: [PATCH 07/16] Change default 'overflow' style for 'Horizontal'. Related issues: #1957. --- CHANGELOG.md | 1 + src/textual/containers.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2670f3f32..12cd78bd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Renamed `Vertical` to `VerticalScroll` https://github.com/Textualize/textual/issues/1957 +- Default `overflow` style for `Horizontal` changed to `hidden hidden` https://github.com/Textualize/textual/issues/1957 ### Added diff --git a/src/textual/containers.py b/src/textual/containers.py index 25633d1df..894912cf4 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -32,7 +32,7 @@ class Horizontal(Widget): Horizontal { height: 1fr; layout: horizontal; - overflow-x: hidden; + overflow: hidden hidden; } """ From 38c7cc1849d4856ca0e6cc7d25b3ccdeaacf0dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:36:38 +0000 Subject: [PATCH 08/16] Fix default CSS for 'VerticalScroll'. --- src/textual/containers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/containers.py b/src/textual/containers.py index 894912cf4..59a07fef4 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -18,7 +18,7 @@ class VerticalScroll(Widget): DEFAULT_CSS = """ VerticalScroll { - height: 1fr; + width: 1fr; layout: vertical; overflow-y: auto; } From 5674b4b62802043b2e171327ed9e20b414b6af51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:38:31 +0000 Subject: [PATCH 09/16] Add 'Vertical'. Related issues: #1957. --- CHANGELOG.md | 5 +++-- src/textual/containers.py | 12 ++++++++++++ tests/test_containers.py | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12cd78bd7..a6ece47c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,14 +9,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- Renamed `Vertical` to `VerticalScroll` https://github.com/Textualize/textual/issues/1957 -- Default `overflow` style for `Horizontal` changed to `hidden hidden` https://github.com/Textualize/textual/issues/1957 +- Breaking change: changed default behaviour of `Vertical` (see `VerticalScroll`) https://github.com/Textualize/textual/issues/1957 +- The default `overflow` style for `Horizontal` was changed to `hidden hidden` https://github.com/Textualize/textual/issues/1957 ### Added - Added `HorizontalScroll` https://github.com/Textualize/textual/issues/1957 - Added `Center` https://github.com/Textualize/textual/issues/1957 - Added `Middle` https://github.com/Textualize/textual/issues/1957 +- Added `VerticalScroll` (mimicking the old behaviour of `Vertical`) https://github.com/Textualize/textual/issues/1957 ## [0.14.0] - 2023-03-09 diff --git a/src/textual/containers.py b/src/textual/containers.py index 59a07fef4..e94cb19fa 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -13,6 +13,18 @@ class Container(Widget): """ +class Vertical(Widget): + """A container which lays children vertically.""" + + DEFAULT_CSS = """ + Vertical { + width: 1fr; + layout: vertical; + overflow: hidden hidden; + } + """ + + class VerticalScroll(Widget): """A container which aligns children vertically and overflows automatically.""" diff --git a/tests/test_containers.py b/tests/test_containers.py index 866e62800..22a4ad0ec 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -1,7 +1,14 @@ """Test basic functioning of some containers.""" from textual.app import App, ComposeResult -from textual.containers import Center, Horizontal, HorizontalScroll, Middle +from textual.containers import ( + Center, + Horizontal, + HorizontalScroll, + Middle, + Vertical, + VerticalScroll, +) from textual.widgets import Label @@ -34,6 +41,35 @@ async def test_horizontal_vs_horizontalscroll_scrolling(): assert horizontal_scroll.scrollbars_enabled == (False, True) +async def test_vertical_vs_verticalscroll_scrolling(): + """Check the default scrollbar behaviours for `Vertical` and `VerticalScroll`.""" + + class VerticalsApp(App[None]): + CSS = """ + Screen { + layout: horizontal; + } + """ + + def compose(self) -> ComposeResult: + with Vertical(): + for _ in range(10): + yield Label("How is life going?\n" * 3 + "\n\n") + with VerticalScroll(): + for _ in range(10): + yield Label("How is life going?\n" * 3 + "\n\n") + + WIDTH = 80 + HEIGHT = 24 + app = VerticalsApp() + async with app.run_test(size=(WIDTH, HEIGHT)): + vertical = app.query_one(Vertical) + vertical_scroll = app.query_one(VerticalScroll) + assert vertical.size.width == vertical_scroll.size.width + assert vertical.scrollbars_enabled == (False, False) + assert vertical_scroll.scrollbars_enabled == (True, False) + + async def test_center_container(): """Check the size of the container `Center`.""" From c13308a360c59a8682d9bbb1a2afae5f00062b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 9 Mar 2023 17:39:05 +0000 Subject: [PATCH 10/16] Add tests for Horizontal/Vertical(Scroll) containers. --- .../__snapshots__/test_snapshots.ambr | 166 ++++++++++++++++++ .../snapshot_apps/layout_containers.py | 56 ++++++ tests/snapshot_tests/test_snapshots.py | 13 +- 3 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 tests/snapshot_tests/snapshot_apps/layout_containers.py diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index b69597054..4f4413e48 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -14805,6 +14805,172 @@ ''' # --- +# name: test_layout_containers + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MyApp + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + AcceptDeclineAcceptDecline + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▅▅ + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + AcceptAccept + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + DeclineDecline▅▅ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + 00 + + 1000000 + + + + + ''' +# --- # name: test_line_api_scrollbars ''' diff --git a/tests/snapshot_tests/snapshot_apps/layout_containers.py b/tests/snapshot_tests/snapshot_apps/layout_containers.py new file mode 100644 index 000000000..c06b6270c --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/layout_containers.py @@ -0,0 +1,56 @@ +""" +App to test layout containers. +""" + +from typing import Iterable + +from textual.app import App, ComposeResult +from textual.containers import ( + Grid, + Horizontal, + HorizontalScroll, + Vertical, + VerticalScroll, +) +from textual.widget import Widget +from textual.widgets import Button, Input, Label + + +def sub_compose() -> Iterable[Widget]: + yield Button.success("Accept") + yield Button.error("Decline") + yield Input() + yield Label("\n\n".join([str(n * 1_000_000) for n in range(10)])) + + +class MyApp(App[None]): + CSS = """ + Grid { + grid-size: 2 2; + grid-rows: 1fr; + grid-columns: 1fr; + } + Grid > Widget { + width: 100%; + height: 100%; + } + Input { + width: 80; + } + """ + + def compose(self) -> ComposeResult: + with Grid(): + with Horizontal(): + yield from sub_compose() + with HorizontalScroll(): + yield from sub_compose() + with Vertical(): + yield from sub_compose() + with VerticalScroll(): + yield from sub_compose() + + +app = MyApp() +if __name__ == "__main__": + app.run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 7d338f1d5..c8ecb7d12 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -48,6 +48,10 @@ def test_dock_layout_sidebar(snap_compare): assert snap_compare(LAYOUT_EXAMPLES_DIR / "dock_layout2_sidebar.py") +def test_layout_containers(snap_compare): + assert snap_compare(SNAPSHOT_APPS_DIR / "layout_containers.py") + + # --- Widgets - rendering and basic interactions --- # Each widget should have a canonical example that is display in the docs. # When adding a new widget, ideally we should also create a snapshot test @@ -170,9 +174,11 @@ def test_content_switcher_example_initial(snap_compare): def test_content_switcher_example_switch(snap_compare): - assert snap_compare(WIDGET_EXAMPLES_DIR / "content_switcher.py", press=[ - "tab", "tab", "enter", "wait:500" - ], terminal_size=(50, 50)) + assert snap_compare( + WIDGET_EXAMPLES_DIR / "content_switcher.py", + press=["tab", "tab", "enter", "wait:500"], + terminal_size=(50, 50), + ) # --- CSS properties --- @@ -234,6 +240,7 @@ def test_programmatic_scrollbar_gutter_change(snap_compare): # --- CLI Preview Apps --- # For our CLI previews e.g. `textual easing`, `textual colors` etc, we have snapshots + def test_borders_preview(snap_compare): assert snap_compare(CLI_PREVIEWS_DIR / "borders.py", press=["tab", "enter"]) From bdeea9fb86e74ea3cc40ab4a5d4eb4f7e326b7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Mon, 13 Mar 2023 11:10:50 +0000 Subject: [PATCH 11/16] Tweak Center/Middle default dimensions. --- src/textual/containers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/textual/containers.py b/src/textual/containers.py index e94cb19fa..0c8d61dcb 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -67,7 +67,6 @@ class Center(Widget): DEFAULT_CSS = """ Center { align-horizontal: center; - width: 100%; height: auto; } """ @@ -79,7 +78,6 @@ class Middle(Widget): DEFAULT_CSS = """ Middle { align-vertical: middle; - height: 100%; width: auto; } """ From 48ce1a149d5ba39a6a96c4344f860d478c276fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Mon, 13 Mar 2023 11:18:33 +0000 Subject: [PATCH 12/16] Add snapshot test for Center/Middle. --- .../__snapshots__/test_snapshots.ambr | 163 ++++++++++++++++++ .../snapshot_apps/alignment_containers.py | 29 ++++ tests/snapshot_tests/test_snapshots.py | 4 + 3 files changed, 196 insertions(+) create mode 100644 tests/snapshot_tests/snapshot_apps/alignment_containers.py diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 4f4413e48..7130b1f5f 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -1,3 +1,166 @@ +# name: test_alignment_containers + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AlignContainersApp + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + center + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + middle + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + ▅▅ + + + + + + + ''' +# --- # name: test_auto_width_input ''' diff --git a/tests/snapshot_tests/snapshot_apps/alignment_containers.py b/tests/snapshot_tests/snapshot_apps/alignment_containers.py new file mode 100644 index 000000000..c419f29e9 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/alignment_containers.py @@ -0,0 +1,29 @@ +""" +App to test alignment containers. +""" + +from textual.app import App, ComposeResult +from textual.containers import Center, Middle +from textual.widgets import Button + + +class AlignContainersApp(App[None]): + CSS = """ + Center { + tint: $primary 10%; + } + Middle { + tint: $secondary 10%; + } + """ + + def compose(self) -> ComposeResult: + with Center(): + yield Button.success("center") + with Middle(): + yield Button.error("middle") + + +app = AlignContainersApp() +if __name__ == "__main__": + app.run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index c8ecb7d12..47098eb91 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -52,6 +52,10 @@ def test_layout_containers(snap_compare): assert snap_compare(SNAPSHOT_APPS_DIR / "layout_containers.py") +def test_alignment_containers(snap_compare): + assert snap_compare(SNAPSHOT_APPS_DIR / "alignment_containers.py") + + # --- Widgets - rendering and basic interactions --- # Each widget should have a canonical example that is display in the docs. # When adding a new widget, ideally we should also create a snapshot test From 0e51520a2c48be585cabae34fe039f8ddbb79eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Mon, 13 Mar 2023 11:44:47 +0000 Subject: [PATCH 13/16] Make docs comply with new containers. --- docs/examples/guide/layout/utility_containers.py | 6 +++--- .../guide/layout/utility_containers_using_with.py | 6 +++--- docs/guide/layout.md | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/examples/guide/layout/utility_containers.py b/docs/examples/guide/layout/utility_containers.py index 80cfada17..eadf58b4c 100644 --- a/docs/examples/guide/layout/utility_containers.py +++ b/docs/examples/guide/layout/utility_containers.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Horizontal, VerticalScroll +from textual.containers import Horizontal, Vertical from textual.widgets import Static @@ -8,12 +8,12 @@ class UtilityContainersExample(App): def compose(self) -> ComposeResult: yield Horizontal( - VerticalScroll( + Vertical( Static("One"), Static("Two"), classes="column", ), - VerticalScroll( + Vertical( Static("Three"), Static("Four"), classes="column", diff --git a/docs/examples/guide/layout/utility_containers_using_with.py b/docs/examples/guide/layout/utility_containers_using_with.py index 4abe3f9f5..d09a3481e 100644 --- a/docs/examples/guide/layout/utility_containers_using_with.py +++ b/docs/examples/guide/layout/utility_containers_using_with.py @@ -1,5 +1,5 @@ from textual.app import App, ComposeResult -from textual.containers import Horizontal, VerticalScroll +from textual.containers import Horizontal, Vertical from textual.widgets import Static @@ -8,10 +8,10 @@ class UtilityContainersExample(App): def compose(self) -> ComposeResult: with Horizontal(): - with VerticalScroll(classes="column"): + with Vertical(classes="column"): yield Static("One") yield Static("Two") - with VerticalScroll(classes="column"): + with Vertical(classes="column"): yield Static("Three") yield Static("Four") diff --git a/docs/guide/layout.md b/docs/guide/layout.md index db8d97dfe..a39da2f8e 100644 --- a/docs/guide/layout.md +++ b/docs/guide/layout.md @@ -134,11 +134,11 @@ exceeds the available horizontal space in the parent container. ## Utility containers -Textual comes with several "container" widgets. -These are [VerticalScroll][textual.containers.VerticalScroll], [Horizontal][textual.containers.Horizontal], and [Grid][textual.containers.Grid] which have the corresponding layout. +Textual comes with [several "container" widgets][textual.containers]. +Among them, we have [Vertical][textual.containers.Vertical], [Horizontal][textual.containers.Horizontal], and [Grid][textual.containers.Grid] which have the corresponding layout. The example below shows how we can combine these containers to create a simple 2x2 grid. -Inside a single `Horizontal` container, we place two `VerticalScroll` containers. +Inside a single `Horizontal` container, we place two `Vertical` containers. In other words, we have a single row containing two columns. === "Output" @@ -163,8 +163,8 @@ However, Textual comes with a more powerful mechanism for achieving this known a ## Composing with context managers -In the previous section we've show how you add children to a container (such as `Horizontal` and `VerticalScroll`) using positional arguments. -It's fine to do it this way, but Textual offers a simplified syntax using [context managers](https://docs.python.org/3/reference/datamodel.html#context-managers) which is generally easier to write and edit. +In the previous section, we've shown how you add children to a container (such as `Horizontal` and `Vertical`) using positional arguments. +It's fine to do it this way, but Textual offers a simplified syntax using [context managers](https://docs.python.org/3/reference/datamodel.html#context-managers), which is generally easier to write and edit. When composing a widget, you can introduce a container using Python's `with` statement. Any widgets yielded within that block are added as a child of the container. @@ -202,7 +202,7 @@ Let's update the [utility containers](#utility-containers) example to use the co ```{.textual path="docs/examples/guide/layout/utility_containers_using_with.py"} ``` -Note how the end result is the same, but the code with context managers is a little easer to read. It is up to you which method you want to use, and you can mix context managers with positional arguments if you like! +Note how the end result is the same, but the code with context managers is a little easier to read. It is up to you which method you want to use, and you can mix context managers with positional arguments if you like! ## Grid From 429c8a3649fe570fbaf495cf8fa648f443b211db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Mon, 13 Mar 2023 11:48:49 +0000 Subject: [PATCH 14/16] Tweak default Center/Middle dimensions. We set dimensions to 1fr instead of 100% (original request) or blank (previous change) for consistency with the other containers. --- src/textual/containers.py | 2 + .../__snapshots__/test_snapshots.ambr | 125 +++++++++--------- 2 files changed, 64 insertions(+), 63 deletions(-) diff --git a/src/textual/containers.py b/src/textual/containers.py index 0c8d61dcb..fbae6ecb7 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -68,6 +68,7 @@ class Center(Widget): Center { align-horizontal: center; height: auto; + width: 1fr; } """ @@ -79,6 +80,7 @@ class Middle(Widget): Middle { align-vertical: middle; width: auto; + height: 1fr; } """ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 7130b1f5f..19dfd1a6c 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -21,139 +21,138 @@ font-weight: 700; } - .terminal-258300109-matrix { + .terminal-1593336641-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-258300109-title { + .terminal-1593336641-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-258300109-r1 { fill: #c5c8c6 } - .terminal-258300109-r2 { fill: #7ae998 } - .terminal-258300109-r3 { fill: #e1e1e1 } - .terminal-258300109-r4 { fill: #0a180e;font-weight: bold } - .terminal-258300109-r5 { fill: #008139 } - .terminal-258300109-r6 { fill: #e3dbce } - .terminal-258300109-r7 { fill: #e76580 } - .terminal-258300109-r8 { fill: #f5e5e9;font-weight: bold } - .terminal-258300109-r9 { fill: #780028 } - .terminal-258300109-r10 { fill: #14191f } + .terminal-1593336641-r1 { fill: #c5c8c6 } + .terminal-1593336641-r2 { fill: #7ae998 } + .terminal-1593336641-r3 { fill: #0a180e;font-weight: bold } + .terminal-1593336641-r4 { fill: #008139 } + .terminal-1593336641-r5 { fill: #e3dbce } + .terminal-1593336641-r6 { fill: #e1e1e1 } + .terminal-1593336641-r7 { fill: #e76580 } + .terminal-1593336641-r8 { fill: #f5e5e9;font-weight: bold } + .terminal-1593336641-r9 { fill: #780028 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - AlignContainersApp + AlignContainersApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - center - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - - - - - - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - middle - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - - - - ▅▅ - + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + center + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + middle + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + From 3518d38d85afe98c586ea638c8c49b30acc5e4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:02:36 +0000 Subject: [PATCH 15/16] Update snapshot tests. --- .../__snapshots__/test_snapshots.ambr | 134 +++++++++--------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 1518261b9..4640380ec 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -15216,143 +15216,143 @@ font-weight: 700; } - .terminal-2034626251-matrix { + .terminal-4197777529-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2034626251-title { + .terminal-4197777529-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2034626251-r1 { fill: #7ae998 } - .terminal-2034626251-r2 { fill: #e76580 } - .terminal-2034626251-r3 { fill: #1e1e1e } - .terminal-2034626251-r4 { fill: #121212 } - .terminal-2034626251-r5 { fill: #e1e1e1 } - .terminal-2034626251-r6 { fill: #c5c8c6 } - .terminal-2034626251-r7 { fill: #0a180e;font-weight: bold } - .terminal-2034626251-r8 { fill: #f5e5e9;font-weight: bold } - .terminal-2034626251-r9 { fill: #e2e2e2 } - .terminal-2034626251-r10 { fill: #008139 } - .terminal-2034626251-r11 { fill: #780028 } - .terminal-2034626251-r12 { fill: #14191f } - .terminal-2034626251-r13 { fill: #23568b } + .terminal-4197777529-r1 { fill: #7ae998 } + .terminal-4197777529-r2 { fill: #e76580 } + .terminal-4197777529-r3 { fill: #1e1e1e } + .terminal-4197777529-r4 { fill: #121212 } + .terminal-4197777529-r5 { fill: #c5c8c6 } + .terminal-4197777529-r6 { fill: #0a180e;font-weight: bold } + .terminal-4197777529-r7 { fill: #f5e5e9;font-weight: bold } + .terminal-4197777529-r8 { fill: #e2e2e2 } + .terminal-4197777529-r9 { fill: #008139 } + .terminal-4197777529-r10 { fill: #780028 } + .terminal-4197777529-r11 { fill: #e1e1e1 } + .terminal-4197777529-r12 { fill: #23568b } + .terminal-4197777529-r13 { fill: #14191f } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MyApp + MyApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - AcceptDeclineAcceptDecline - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - - ▅▅ - - - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - AcceptAccept - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - DeclineDecline▅▅ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - 00 - - 1000000 + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + AcceptDeclineAcceptDecline + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + AcceptAccept + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + DeclineDecline + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▆▆ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + 00 + + 10000001000000 From d775a90fa23f44d6fb6b6c47c910d6a0b4e237ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:33:10 +0000 Subject: [PATCH 16/16] Address review comments. --- docs/examples/widgets/button.css | 2 +- src/textual/containers.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/examples/widgets/button.css b/docs/examples/widgets/button.css index aee652f68..cb07fcaa1 100644 --- a/docs/examples/widgets/button.css +++ b/docs/examples/widgets/button.css @@ -2,7 +2,7 @@ Button { margin: 1 2; } -Horizontal>VerticalScroll { +Horizontal > VerticalScroll { width: 24; } diff --git a/src/textual/containers.py b/src/textual/containers.py index fbae6ecb7..a2ddbf422 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -14,7 +14,7 @@ class Container(Widget): class Vertical(Widget): - """A container which lays children vertically.""" + """A container which arranges children vertically.""" DEFAULT_CSS = """ Vertical { @@ -26,7 +26,7 @@ class Vertical(Widget): class VerticalScroll(Widget): - """A container which aligns children vertically and overflows automatically.""" + """A container which arranges children vertically, with an automatic vertical scrollbar.""" DEFAULT_CSS = """ VerticalScroll { @@ -38,7 +38,7 @@ class VerticalScroll(Widget): class Horizontal(Widget): - """A container which lays children horizontally.""" + """A container which arranges children horizontally.""" DEFAULT_CSS = """ Horizontal { @@ -50,7 +50,7 @@ class Horizontal(Widget): class HorizontalScroll(Widget): - """A container which lays children horizontally and overflows automatically.""" + """A container which arranges children horizontally, with an automatic horizontal scrollbar.""" DEFAULT_CSS = """ HorizontalScroll {