diff --git a/CHANGELOG.md b/CHANGELOG.md index fb6eac1be6..1e8d46419b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). https://github.com/Textualize/textual/issues/1094 - Added Pilot.wait_for_animation - Added `Widget.move_child` https://github.com/Textualize/textual/issues/1121 +- Added a `Label` widget https://github.com/Textualize/textual/issues/1190 - Support lazy-instantiated Screens (callables in App.SCREENS) https://github.com/Textualize/textual/pull/1185 ### Changed diff --git a/docs/api/label.md b/docs/api/label.md new file mode 100644 index 0000000000..eee506a2c7 --- /dev/null +++ b/docs/api/label.md @@ -0,0 +1 @@ +::: textual.widgets.Label diff --git a/docs/examples/widgets/label.py b/docs/examples/widgets/label.py new file mode 100644 index 0000000000..43a50bb104 --- /dev/null +++ b/docs/examples/widgets/label.py @@ -0,0 +1,12 @@ +from textual.app import App, ComposeResult +from textual.widgets import Label + + +class LabelApp(App): + def compose(self) -> ComposeResult: + yield Label("Hello, world!") + + +if __name__ == "__main__": + app = LabelApp() + app.run() diff --git a/docs/widgets/label.md b/docs/widgets/label.md new file mode 100644 index 0000000000..96a31bcc60 --- /dev/null +++ b/docs/widgets/label.md @@ -0,0 +1,33 @@ +# Label + +A widget which displays static text, but which can also contain more complex Rich renderables. + +- [ ] Focusable +- [ ] Container + +## Example + +The example below shows how you can use a `Label` widget to display some text. + +=== "Output" + + ```{.textual path="docs/examples/widgets/label.py"} + ``` + +=== "label.py" + + ```python + --8<-- "docs/examples/widgets/label.py" + ``` + +## Reactive Attributes + +This widget has no reactive attributes. + +## Messages + +This widget sends no messages. + +## See Also + +* [Label](../api/label.md) code reference diff --git a/docs/widgets/static.md b/docs/widgets/static.md index 342e2daf7e..c8e41606f2 100644 --- a/docs/widgets/static.md +++ b/docs/widgets/static.md @@ -1,14 +1,14 @@ # Static A widget which displays static content. -Can be used for simple text labels, but can also contain more complex Rich renderables. +Can be used for Rich renderables and can also for the base for other types of widgets. - [ ] Focusable -- [x] Container +- [ ] Container ## Example -The example below shows how you can use a `Static` widget as a simple text label. +The example below shows how you can use a `Static` widget as a simple text label (but see [Label](./label.md) as a way of displaying text). === "Output" @@ -32,3 +32,4 @@ This widget sends no messages. ## See Also * [Static](../api/static.md) code reference +* [Label](./label.md) diff --git a/examples/five_by_five.py b/examples/five_by_five.py index 20bbea80f0..f4574a8a44 100644 --- a/examples/five_by_five.py +++ b/examples/five_by_five.py @@ -13,7 +13,7 @@ from textual.app import App, ComposeResult from textual.screen import Screen from textual.widget import Widget -from textual.widgets import Footer, Button, Static +from textual.widgets import Footer, Button, Label from textual.css.query import DOMQuery from textual.reactive import reactive from textual.binding import Binding @@ -33,10 +33,10 @@ def compose(self) -> ComposeResult: Returns: ComposeResult: The result of composing the help screen. """ - yield Static(Markdown(Path(__file__).with_suffix(".md").read_text())) + yield Label(Markdown(Path(__file__).with_suffix(".md").read_text())) -class WinnerMessage(Static): +class WinnerMessage(Label): """Widget to tell the user they have won.""" MIN_MOVES: Final = 14 @@ -91,9 +91,9 @@ def compose(self) -> ComposeResult: ComposeResult: The result of composing the game header. """ yield Horizontal( - Static(self.app.title, id="app-title"), - Static(id="moves"), - Static(id="progress"), + Label(self.app.title, id="app-title"), + Label(id="moves"), + Label(id="progress"), ) def watch_moves(self, moves: int): @@ -102,7 +102,7 @@ def watch_moves(self, moves: int): Args: moves (int): The number of moves made. """ - self.query_one("#moves", Static).update(f"Moves: {moves}") + self.query_one("#moves", Label).update(f"Moves: {moves}") def watch_filled(self, filled: int): """Watch the on-count reactive and update when it changes. @@ -110,7 +110,7 @@ def watch_filled(self, filled: int): Args: filled (int): The number of cells that are currently on. """ - self.query_one("#progress", Static).update(f"Filled: {filled}") + self.query_one("#progress", Label).update(f"Filled: {filled}") class GameCell(Button): diff --git a/mkdocs.yml b/mkdocs.yml index e7b4e5f9e4..332298e5d3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -96,6 +96,7 @@ nav: - "widgets/footer.md" - "widgets/header.md" - "widgets/input.md" + - "widgets/label.md" - "widgets/static.md" - "widgets/tree_control.md" - API: @@ -109,6 +110,7 @@ nav: - "api/footer.md" - "api/geometry.md" - "api/header.md" + - "api/label.md" - "api/message_pump.md" - "api/message.md" - "api/pilot.md" @@ -185,13 +187,13 @@ plugins: - blog: - rss: - match_path: blog/posts/.* + match_path: blog/posts/.* date_from_meta: as_creation: date categories: - categories - release - - tags + - tags - search: - autorefs: - mkdocstrings: @@ -215,10 +217,10 @@ extra_css: extra: social: - - icon: fontawesome/brands/twitter + - icon: fontawesome/brands/twitter link: https://twitter.com/textualizeio name: textualizeio on Twitter - - icon: fontawesome/brands/github + - icon: fontawesome/brands/github link: https://github.com/textualize/textual/ name: Textual on Github - icon: fontawesome/brands/discord diff --git a/src/textual/cli/previews/borders.py b/src/textual/cli/previews/borders.py index 6133434436..e5d453d86f 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.widgets import Button, Static +from textual.widgets import Button, Label from textual.containers import Vertical @@ -48,7 +48,7 @@ class BorderApp(App): def compose(self): yield BorderButtons() - self.text = Static(TEXT, id="text") + self.text = Label(TEXT, id="text") yield self.text def on_button_pressed(self, event: Button.Pressed) -> None: diff --git a/src/textual/cli/previews/colors.css b/src/textual/cli/previews/colors.css index 3af8eabd7e..657a8aabbd 100644 --- a/src/textual/cli/previews/colors.css +++ b/src/textual/cli/previews/colors.css @@ -68,7 +68,7 @@ ColorGroup.-active { } -ColorLabel { +Label { padding: 0 0 1 0; content-align: center middle; color: $text; diff --git a/src/textual/cli/previews/colors.py b/src/textual/cli/previews/colors.py index 56ac645c6b..e25c70d7fe 100644 --- a/src/textual/cli/previews/colors.py +++ b/src/textual/cli/previews/colors.py @@ -2,7 +2,7 @@ from textual.containers import Horizontal, Vertical from textual.design import ColorSystem from textual.widget import Widget -from textual.widgets import Button, Footer, Static +from textual.widgets import Button, Footer, Static, Label class ColorButtons(Vertical): @@ -28,10 +28,6 @@ class Content(Vertical): pass -class ColorLabel(Static): - pass - - class ColorsView(Vertical): def compose(self) -> ComposeResult: @@ -47,7 +43,7 @@ def compose(self) -> ComposeResult: for color_name in ColorSystem.COLOR_NAMES: - items: list[Widget] = [ColorLabel(f'"{color_name}"')] + items: list[Widget] = [Label(f'"{color_name}"')] for level in LEVELS: color = f"{color_name}-{level}" if level else color_name item = ColorItem( diff --git a/src/textual/cli/previews/easing.py b/src/textual/cli/previews/easing.py index 6edcdb82a1..b81290302f 100644 --- a/src/textual/cli/previews/easing.py +++ b/src/textual/cli/previews/easing.py @@ -8,7 +8,7 @@ from textual.reactive import Reactive from textual.scrollbar import ScrollBarRender from textual.widget import Widget -from textual.widgets import Button, Footer, Static, Input +from textual.widgets import Button, Footer, Label, Input VIRTUAL_SIZE = 100 WINDOW_SIZE = 10 @@ -27,7 +27,7 @@ class Bar(Widget): animation_running = Reactive(False) DEFAULT_CSS = """ - + Bar { background: $surface; color: $error; @@ -37,7 +37,7 @@ class Bar(Widget): background: $surface; color: $success; } - + """ def watch_animation_running(self, running: bool) -> None: @@ -67,14 +67,14 @@ def compose(self) -> ComposeResult: self.animated_bar.position = START_POSITION duration_input = Input("1.0", placeholder="Duration", id="duration-input") - self.opacity_widget = Static( + self.opacity_widget = Label( f"[b]Welcome to Textual![/]\n\n{TEXT}", id="opacity-widget" ) yield EasingButtons() yield Vertical( Horizontal( - Static("Animation Duration:", id="label"), duration_input, id="inputs" + Label("Animation Duration:", id="label"), duration_input, id="inputs" ), Horizontal( self.animated_bar, diff --git a/src/textual/widgets/__init__.py b/src/textual/widgets/__init__.py index c42b011a9c..8cbcb9f3b8 100644 --- a/src/textual/widgets/__init__.py +++ b/src/textual/widgets/__init__.py @@ -15,6 +15,7 @@ from ._directory_tree import DirectoryTree from ._footer import Footer from ._header import Header + from ._label import Label from ._placeholder import Placeholder from ._pretty import Pretty from ._static import Static @@ -30,6 +31,7 @@ "DirectoryTree", "Footer", "Header", + "Label", "Placeholder", "Pretty", "Static", diff --git a/src/textual/widgets/__init__.pyi b/src/textual/widgets/__init__.pyi index 5ceb018352..57f87df82e 100644 --- a/src/textual/widgets/__init__.pyi +++ b/src/textual/widgets/__init__.pyi @@ -5,6 +5,7 @@ from ._checkbox import Checkbox as Checkbox from ._directory_tree import DirectoryTree as DirectoryTree from ._footer import Footer as Footer from ._header import Header as Header +from ._label import Label as Label from ._placeholder import Placeholder as Placeholder from ._pretty import Pretty as Pretty from ._static import Static as Static diff --git a/src/textual/widgets/_label.py b/src/textual/widgets/_label.py new file mode 100644 index 0000000000..df519ae4f1 --- /dev/null +++ b/src/textual/widgets/_label.py @@ -0,0 +1,7 @@ +"""Provides a simple Label widget.""" + +from ._static import Static + + +class Label(Static): + """A simple label widget for displaying text-oriented renderables."""