mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -2,7 +2,7 @@ Button {
|
||||
margin: 1 2;
|
||||
}
|
||||
|
||||
Horizontal > Vertical {
|
||||
Horizontal>VerticalScroll {
|
||||
width: 24;
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -2,7 +2,7 @@ Screen {
|
||||
align: center middle;
|
||||
}
|
||||
|
||||
Vertical {
|
||||
VerticalScroll {
|
||||
width: auto;
|
||||
height: auto;
|
||||
border: solid $primary;
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Vertical {
|
||||
VerticalScroll {
|
||||
align: center middle;
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = """
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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",
|
||||
),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 > *")
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user