From e473e4873a6c7f2f5ae61ffcc4830548abaffc39 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 5 May 2022 16:06:49 +0100 Subject: [PATCH 01/10] Add style param to Widget render method --- docs/examples/timers/clock.py | 2 +- docs/examples/widgets/custom.py | 2 +- e2e_tests/test_apps/basic.py | 12 ++++++------ examples/borders.py | 4 ++-- examples/calculator.py | 2 +- sandbox/align.py | 2 +- sandbox/basic.py | 12 ++++++------ sandbox/dev_sandbox.py | 2 +- sandbox/tabs.py | 4 ++-- src/textual/app.py | 2 +- src/textual/screen.py | 6 +++--- src/textual/scrollbar.py | 3 ++- src/textual/widget.py | 12 ++++++++---- src/textual/widgets/_button.py | 15 ++++++++------- src/textual/widgets/_footer.py | 3 ++- src/textual/widgets/_header.py | 3 ++- src/textual/widgets/_placeholder.py | 3 ++- src/textual/widgets/_static.py | 4 +++- src/textual/widgets/_tree_control.py | 2 +- src/textual/widgets/tabs.py | 2 +- tests/test_widget.py | 6 ++---- 21 files changed, 56 insertions(+), 47 deletions(-) diff --git a/docs/examples/timers/clock.py b/docs/examples/timers/clock.py index b13287748..4eb4cd4c7 100644 --- a/docs/examples/timers/clock.py +++ b/docs/examples/timers/clock.py @@ -10,7 +10,7 @@ class Clock(Widget): def on_mount(self): self.set_interval(1, self.refresh) - def render(self): + def render(self, styles: Styles): time = datetime.now().strftime("%c") return Align.center(time, vertical="middle") diff --git a/docs/examples/widgets/custom.py b/docs/examples/widgets/custom.py index 8fda42589..92baa9028 100644 --- a/docs/examples/widgets/custom.py +++ b/docs/examples/widgets/custom.py @@ -9,7 +9,7 @@ class Hover(Widget): mouse_over = Reactive(False) - def render(self) -> Panel: + def render(self, styles: Styles) -> Panel: return Panel("Hello [b]World[/b]", style=("on red" if self.mouse_over else "")) def on_enter(self) -> None: diff --git a/e2e_tests/test_apps/basic.py b/e2e_tests/test_apps/basic.py index 7a833d843..0673b22c5 100644 --- a/e2e_tests/test_apps/basic.py +++ b/e2e_tests/test_apps/basic.py @@ -53,12 +53,12 @@ lorem = Text.from_markup( class TweetHeader(Widget): - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return Text("Lorem Impsum", justify="center") class TweetBody(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return lorem @@ -67,22 +67,22 @@ class Tweet(Widget): class OptionItem(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Text("Option") class Error(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Text("This is an error message", justify="center") class Warning(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Text("This is a warning message", justify="center") class Success(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Text("This is a success message", justify="center") diff --git a/examples/borders.py b/examples/borders.py index 36e0bae47..4179d31fd 100644 --- a/examples/borders.py +++ b/examples/borders.py @@ -15,12 +15,12 @@ lorem = Text.from_markup( class Lorem(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Padding(lorem, 1) class Background(Widget): - def render(self): + def render(self, styles: Styles): return VerticalGradient("#212121", "#212121") diff --git a/examples/calculator.py b/examples/calculator.py index bbc28badf..9a15b8301 100644 --- a/examples/calculator.py +++ b/examples/calculator.py @@ -55,7 +55,7 @@ class Numbers(Widget): value = Reactive("0") - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: """Build a Rich renderable to render the calculator display.""" return Padding( Align.right(FigletText(self.value), vertical="middle"), diff --git a/sandbox/align.py b/sandbox/align.py index f3f18c93d..382621603 100644 --- a/sandbox/align.py +++ b/sandbox/align.py @@ -6,7 +6,7 @@ from textual.widgets import Static class Thing(Widget): - def render(self): + def render(self, styles: Styles): return "Hello, 3434 World.\n[b]Lorem impsum." diff --git a/sandbox/basic.py b/sandbox/basic.py index c56703c57..192d6537a 100644 --- a/sandbox/basic.py +++ b/sandbox/basic.py @@ -50,12 +50,12 @@ lorem = Text.from_markup( class TweetHeader(Widget): - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return Text("Lorem Impsum", justify="center") class TweetBody(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return lorem @@ -64,22 +64,22 @@ class Tweet(Widget): class OptionItem(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Text("Option") class Error(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Text("This is an error message", justify="center") class Warning(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Text("This is a warning message", justify="center") class Success(Widget): - def render(self) -> Text: + def render(self, styles: Styles) -> Text: return Text("This is a success message", justify="center") diff --git a/sandbox/dev_sandbox.py b/sandbox/dev_sandbox.py index 0ead807d4..5f78070cc 100644 --- a/sandbox/dev_sandbox.py +++ b/sandbox/dev_sandbox.py @@ -6,7 +6,7 @@ from textual.widget import Widget class PanelWidget(Widget): - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return Panel("hello world!", title="Title") diff --git a/sandbox/tabs.py b/sandbox/tabs.py index 6bca6162e..421ef2720 100644 --- a/sandbox/tabs.py +++ b/sandbox/tabs.py @@ -11,7 +11,7 @@ from textual.widgets.tabs import Tabs, Tab class Hr(Widget): - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return Rule() @@ -22,7 +22,7 @@ class Info(Widget): super().__init__() self.text = text - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return Padding(f"{self.text}", pad=(0, 1)) diff --git a/src/textual/app.py b/src/textual/app.py index 4ee2f63ff..a70ec0327 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -468,7 +468,7 @@ class App(Generic[ReturnType], DOMNode): self.stylesheet.update(self) self.screen.refresh(layout=True) - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return "" def query(self, selector: str | None = None) -> DOMQuery: diff --git a/src/textual/screen.py b/src/textual/screen.py index 75607ab44..8f3b8dc61 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -18,14 +18,14 @@ class Screen(Widget): """A widget for the root of the app.""" CSS = """ - + Screen { layout: dock; docks: _default=top; background: $surface; color: $text-surface; } - + """ dark = Reactive(False) @@ -38,7 +38,7 @@ class Screen(Widget): def watch_dark(self, dark: bool) -> None: pass - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return self.app.render() def get_offset(self, widget: Widget) -> Offset: diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index 5633f0d0e..bc16bf574 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -8,6 +8,7 @@ from rich.console import ConsoleOptions, RenderResult, RenderableType from rich.segment import Segment, Segments from rich.style import Style, StyleType +from textual.css.styles import Styles from textual.reactive import Reactive from . import events from ._types import MessageTarget @@ -205,7 +206,7 @@ class ScrollBar(Widget): yield "window_size", self.window_size yield "position", self.position - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: styles = self.parent.styles style = Style( bgcolor=( diff --git a/src/textual/widget.py b/src/textual/widget.py index 91bcd0005..8a123a98b 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -26,6 +26,7 @@ from .box_model import BoxModel, get_box_model from .color import Color from ._context import active_app from ._types import Lines +from .css.styles import Styles from .dom import DOMNode from .geometry import clamp, Offset, Region, Size from .message import Message @@ -156,7 +157,7 @@ class Widget(DOMNode): int: The optimal width of the content. """ console = self.app.console - renderable = self.render() + renderable = self.render(self.styles) measurement = Measurement.get(console, console.options, renderable) return measurement.maximum @@ -173,7 +174,7 @@ class Widget(DOMNode): Returns: int: The height of the content. """ - renderable = self.render() + renderable = self.render(self.styles) options = self.console.options.update_width(width) segments = self.console.render(renderable, options) # Cheaper than counting the lines returned from render_lines! @@ -461,7 +462,7 @@ class Widget(DOMNode): RenderableType: A new renderable. """ - renderable = self.render() + renderable = self.render(self.styles) styles = self.styles parent_styles = self.parent.styles @@ -661,9 +662,12 @@ class Widget(DOMNode): self.set_dirty() self.check_idle() - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: """Get renderable for widget. + Args: + styles (Styles): The Styles object for this Widget. + Returns: RenderableType: Any renderable """ diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index 46e8039dd..7e9f980bd 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -6,6 +6,7 @@ from rich.console import RenderableType from rich.text import Text from .. import events +from ..css.styles import Styles from ..message import Message from ..reactive import Reactive from ..widget import Widget @@ -15,7 +16,7 @@ class Button(Widget, can_focus=True): """A simple clickable button.""" CSS = """ - + Button { width: auto; height: 3; @@ -23,8 +24,8 @@ class Button(Widget, can_focus=True): background: $primary; color: $text-primary; content-align: center middle; - border: tall $primary-lighten-3; - + border: tall $primary-lighten-3; + margin: 1 0; text-style: bold; } @@ -32,13 +33,13 @@ class Button(Widget, can_focus=True): Button:hover { background:$primary-darken-2; color: $text-primary-darken-2; - border: tall $primary-lighten-1; + border: tall $primary-lighten-1; } App.-show-focus Button:focus { - tint: $accent 20%; + tint: $accent 20%; } - + """ class Pressed(Message, bubble=True): @@ -70,7 +71,7 @@ class Button(Widget, can_focus=True): return Text.from_markup(label) return label - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return self.label async def on_click(self, event: events.Click) -> None: diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py index fba2efe1f..50ccf1560 100644 --- a/src/textual/widgets/_footer.py +++ b/src/textual/widgets/_footer.py @@ -6,6 +6,7 @@ from rich.text import Text import rich.repr from .. import events +from ..css.styles import Styles from ..reactive import Reactive from ..widget import Widget @@ -59,7 +60,7 @@ class Footer(Widget): text.append_text(key_text) return text - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: if self._key_text is None: self._key_text = self.make_key_text() return self._key_text diff --git a/src/textual/widgets/_header.py b/src/textual/widgets/_header.py index a475e0444..826ee36ba 100644 --- a/src/textual/widgets/_header.py +++ b/src/textual/widgets/_header.py @@ -10,6 +10,7 @@ from rich.style import StyleType from rich.table import Table from .. import events +from ..css.styles import Styles from ..reactive import watch, Reactive from ..widget import Widget @@ -49,7 +50,7 @@ class Header(Widget): def get_clock(self) -> str: return datetime.now().time().strftime("%X") - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: header_table = Table.grid(padding=(0, 1), expand=True) header_table.style = self.style header_table.add_column(justify="left", ratio=0, width=8) diff --git a/src/textual/widgets/_placeholder.py b/src/textual/widgets/_placeholder.py index c43b3100d..7831f817d 100644 --- a/src/textual/widgets/_placeholder.py +++ b/src/textual/widgets/_placeholder.py @@ -10,6 +10,7 @@ import rich.repr from .. import log from .. import events +from ..css.styles import Styles from ..reactive import Reactive from ..widget import Widget @@ -26,7 +27,7 @@ class Placeholder(Widget, can_focus=True): yield "has_focus", self.has_focus, False yield "mouse_over", self.mouse_over, False - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return Panel( Align.center( Pretty(self, no_wrap=True, overflow="ellipsis"), vertical="middle" diff --git a/src/textual/widgets/_static.py b/src/textual/widgets/_static.py index 733986e2f..d6fd5d3ec 100644 --- a/src/textual/widgets/_static.py +++ b/src/textual/widgets/_static.py @@ -4,6 +4,8 @@ from rich.console import RenderableType from rich.padding import Padding, PaddingDimensions from rich.style import StyleType from rich.styled import Styled + +from ..css.styles import Styles from ..widget import Widget @@ -23,7 +25,7 @@ class Static(Widget): self.style = style self.padding = padding - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: renderable = self.renderable if self.padding: renderable = Padding(renderable, self.padding) diff --git a/src/textual/widgets/_tree_control.py b/src/textual/widgets/_tree_control.py index a76030077..b7841fde2 100644 --- a/src/textual/widgets/_tree_control.py +++ b/src/textual/widgets/_tree_control.py @@ -249,7 +249,7 @@ class TreeControl(Generic[NodeDataType], Widget): push(iter(node.children)) return None - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return self._tree def render_node(self, node: TreeNode[NodeDataType]) -> RenderableType: diff --git a/src/textual/widgets/tabs.py b/src/textual/widgets/tabs.py index 4346816f6..7623fcbaa 100644 --- a/src/textual/widgets/tabs.py +++ b/src/textual/widgets/tabs.py @@ -330,7 +330,7 @@ class Tabs(Widget): """ return next((i for i, tab in enumerate(self.tabs) if tab.name == tab_name), 0) - def render(self) -> RenderableType: + def render(self, styles: Styles) -> RenderableType: return TabsRenderable( self.tabs, tab_padding=self.tab_padding, diff --git a/tests/test_widget.py b/tests/test_widget.py index 341b7965d..5ea5131f4 100644 --- a/tests/test_widget.py +++ b/tests/test_widget.py @@ -1,10 +1,8 @@ -from contextlib import nullcontext as does_not_raise -from decimal import Decimal - import pytest from textual.app import App from textual.css.errors import StyleValueError +from textual.css.styles import Styles from textual.geometry import Size from textual.widget import Widget @@ -41,7 +39,7 @@ def test_widget_content_width(): self.text = text super().__init__(id=id) - def render(self) -> str: + def render(self, styles: Styles) -> str: return self.text widget1 = TextWidget("foo", id="widget1") From 7d7ea6b4ffbb024d14e8d5f5002e45338be928a6 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 5 May 2022 16:08:52 +0100 Subject: [PATCH 02/10] Pass missing styles into a render method in Screen --- src/textual/screen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/textual/screen.py b/src/textual/screen.py index 8f3b8dc61..024358e62 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -6,6 +6,7 @@ from rich.style import Style from . import events, messages, errors +from .css.styles import Styles from .geometry import Offset, Region from ._compositor import Compositor @@ -39,7 +40,7 @@ class Screen(Widget): pass def render(self, styles: Styles) -> RenderableType: - return self.app.render() + return self.app.render(styles) def get_offset(self, widget: Widget) -> Offset: """Get the absolute offset of a given Widget. From 5251a1a195de45dbd10821211641b1f883c9a3ad Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 5 May 2022 16:12:48 +0100 Subject: [PATCH 03/10] Add some missing Styles imports for type hinting --- src/textual/widgets/_tree_control.py | 1 + src/textual/widgets/tabs.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/textual/widgets/_tree_control.py b/src/textual/widgets/_tree_control.py index b7841fde2..5ad857733 100644 --- a/src/textual/widgets/_tree_control.py +++ b/src/textual/widgets/_tree_control.py @@ -9,6 +9,7 @@ from rich.text import Text, TextType from rich.tree import Tree from rich.padding import PaddingDimensions +from textual.css.styles import Styles from .. import log from .. import events from ..reactive import Reactive diff --git a/src/textual/widgets/tabs.py b/src/textual/widgets/tabs.py index 7623fcbaa..ebab922fc 100644 --- a/src/textual/widgets/tabs.py +++ b/src/textual/widgets/tabs.py @@ -12,6 +12,7 @@ from rich.text import Text from textual import events from textual._layout_resolve import layout_resolve, Edge +from textual.css.styles import Styles from textual.keys import Keys from textual.reactive import Reactive from textual.renderables.opacity import Opacity From 6b1dab53874d68742201d85b8da9fab784a51d13 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 5 May 2022 16:18:24 +0100 Subject: [PATCH 04/10] Add missing Styles import --- docs/examples/timers/clock.py | 1 + docs/examples/widgets/custom.py | 1 + e2e_tests/test_apps/basic.py | 2 +- examples/borders.py | 1 + examples/calculator.py | 1 + sandbox/align.py | 1 + sandbox/basic.py | 1 + sandbox/dev_sandbox.py | 1 + sandbox/tabs.py | 1 + src/textual/app.py | 1 + 10 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/examples/timers/clock.py b/docs/examples/timers/clock.py index 4eb4cd4c7..c7c568045 100644 --- a/docs/examples/timers/clock.py +++ b/docs/examples/timers/clock.py @@ -3,6 +3,7 @@ from datetime import datetime from rich.align import Align from textual.app import App +from textual.css.styles import Styles from textual.widget import Widget diff --git a/docs/examples/widgets/custom.py b/docs/examples/widgets/custom.py index 92baa9028..5a239564b 100644 --- a/docs/examples/widgets/custom.py +++ b/docs/examples/widgets/custom.py @@ -1,6 +1,7 @@ from rich.panel import Panel from textual.app import App +from textual.css.styles import Styles from textual.reactive import Reactive from textual.widget import Widget diff --git a/e2e_tests/test_apps/basic.py b/e2e_tests/test_apps/basic.py index 0673b22c5..4cdcdce14 100644 --- a/e2e_tests/test_apps/basic.py +++ b/e2e_tests/test_apps/basic.py @@ -1,11 +1,11 @@ from pathlib import Path -from rich.align import Align from rich.console import RenderableType from rich.syntax import Syntax from rich.text import Text from textual.app import App +from textual.css.styles import Styles from textual.widget import Widget from textual.widgets import Static diff --git a/examples/borders.py b/examples/borders.py index 4179d31fd..4f11d1dd6 100644 --- a/examples/borders.py +++ b/examples/borders.py @@ -3,6 +3,7 @@ from rich.padding import Padding from rich.text import Text from textual.app import App +from textual.css.styles import Styles from textual.renderables.gradient import VerticalGradient from textual import events from textual.widgets import Placeholder diff --git a/examples/calculator.py b/examples/calculator.py index 9a15b8301..5463c7765 100644 --- a/examples/calculator.py +++ b/examples/calculator.py @@ -12,6 +12,7 @@ from rich.padding import Padding from rich.text import Text from textual.app import App +from textual.css.styles import Styles from textual.reactive import Reactive from textual.views import GridView from textual.widget import Widget diff --git a/sandbox/align.py b/sandbox/align.py index 382621603..cd2929cf0 100644 --- a/sandbox/align.py +++ b/sandbox/align.py @@ -1,6 +1,7 @@ from rich.text import Text from textual.app import App, ComposeResult +from textual.css.styles import Styles from textual.widget import Widget from textual.widgets import Static diff --git a/sandbox/basic.py b/sandbox/basic.py index 192d6537a..b21495c4d 100644 --- a/sandbox/basic.py +++ b/sandbox/basic.py @@ -3,6 +3,7 @@ from rich.syntax import Syntax from rich.text import Text from textual.app import App +from textual.css.styles import Styles from textual.widget import Widget from textual.widgets import Static diff --git a/sandbox/dev_sandbox.py b/sandbox/dev_sandbox.py index 5f78070cc..d332ecca4 100644 --- a/sandbox/dev_sandbox.py +++ b/sandbox/dev_sandbox.py @@ -2,6 +2,7 @@ from rich.console import RenderableType from rich.panel import Panel from textual.app import App +from textual.css.styles import Styles from textual.widget import Widget diff --git a/sandbox/tabs.py b/sandbox/tabs.py index 421ef2720..9016ea954 100644 --- a/sandbox/tabs.py +++ b/sandbox/tabs.py @@ -6,6 +6,7 @@ from rich.rule import Rule from textual import events from textual.app import App +from textual.css.styles import Styles from textual.widget import Widget from textual.widgets.tabs import Tabs, Tab diff --git a/src/textual/app.py b/src/textual/app.py index a70ec0327..9ca074edb 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -40,6 +40,7 @@ from ._context import active_app from ._event_broker import extract_handler_actions, NoHandler from ._timer import Timer from .binding import Bindings, NoBinding +from .css.styles import Styles from .css.stylesheet import Stylesheet from .design import ColorSystem from .devtools.client import DevtoolsClient, DevtoolsConnectionError, DevtoolsLog From 43f65d73ae8174145b8a65a93a7903ef86d92610 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Fri, 6 May 2022 10:20:05 +0100 Subject: [PATCH 05/10] Pass Rich Style object into Textual render method --- docs/examples/timers/clock.py | 4 ++-- docs/examples/widgets/custom.py | 4 ++-- e2e_tests/test_apps/basic.py | 14 +++++++------- examples/borders.py | 9 +++------ examples/calculator.py | 6 +++--- sandbox/align.py | 5 ++--- sandbox/basic.py | 14 +++++++------- sandbox/dev_sandbox.py | 4 ++-- sandbox/tabs.py | 6 +++--- sandbox/uber.css | 1 + sandbox/uber.py | 11 +++++------ src/textual/app.py | 4 ++-- src/textual/screen.py | 5 ++--- src/textual/scrollbar.py | 14 ++++++-------- src/textual/widget.py | 16 +++++++++------- src/textual/widgets/_button.py | 4 ++-- src/textual/widgets/_footer.py | 3 +-- src/textual/widgets/_header.py | 5 ++--- src/textual/widgets/_placeholder.py | 13 ++++++------- src/textual/widgets/_static.py | 5 ++--- src/textual/widgets/_tree_control.py | 6 ++---- src/textual/widgets/tabs.py | 3 +-- tests/test_widget.py | 4 ++-- 23 files changed, 74 insertions(+), 86 deletions(-) diff --git a/docs/examples/timers/clock.py b/docs/examples/timers/clock.py index c7c568045..53f412da8 100644 --- a/docs/examples/timers/clock.py +++ b/docs/examples/timers/clock.py @@ -1,9 +1,9 @@ from datetime import datetime from rich.align import Align +from rich.style import Style from textual.app import App -from textual.css.styles import Styles from textual.widget import Widget @@ -11,7 +11,7 @@ class Clock(Widget): def on_mount(self): self.set_interval(1, self.refresh) - def render(self, styles: Styles): + def render(self, style: Style): time = datetime.now().strftime("%c") return Align.center(time, vertical="middle") diff --git a/docs/examples/widgets/custom.py b/docs/examples/widgets/custom.py index 5a239564b..f35e82f41 100644 --- a/docs/examples/widgets/custom.py +++ b/docs/examples/widgets/custom.py @@ -1,7 +1,7 @@ from rich.panel import Panel +from rich.style import Style from textual.app import App -from textual.css.styles import Styles from textual.reactive import Reactive from textual.widget import Widget @@ -10,7 +10,7 @@ class Hover(Widget): mouse_over = Reactive(False) - def render(self, styles: Styles) -> Panel: + def render(self, style: Style) -> Panel: return Panel("Hello [b]World[/b]", style=("on red" if self.mouse_over else "")) def on_enter(self) -> None: diff --git a/e2e_tests/test_apps/basic.py b/e2e_tests/test_apps/basic.py index 4cdcdce14..70b1b6fa0 100644 --- a/e2e_tests/test_apps/basic.py +++ b/e2e_tests/test_apps/basic.py @@ -1,11 +1,11 @@ from pathlib import Path from rich.console import RenderableType +from rich.style import Style from rich.syntax import Syntax from rich.text import Text from textual.app import App -from textual.css.styles import Styles from textual.widget import Widget from textual.widgets import Static @@ -53,12 +53,12 @@ lorem = Text.from_markup( class TweetHeader(Widget): - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: return Text("Lorem Impsum", justify="center") class TweetBody(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return lorem @@ -67,22 +67,22 @@ class Tweet(Widget): class OptionItem(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Text("Option") class Error(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Text("This is an error message", justify="center") class Warning(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Text("This is a warning message", justify="center") class Success(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Text("This is a success message", justify="center") diff --git a/examples/borders.py b/examples/borders.py index 4f11d1dd6..2154a8d4e 100644 --- a/examples/borders.py +++ b/examples/borders.py @@ -1,12 +1,9 @@ -from rich.console import Group from rich.padding import Padding +from rich.style import Style from rich.text import Text from textual.app import App -from textual.css.styles import Styles from textual.renderables.gradient import VerticalGradient -from textual import events -from textual.widgets import Placeholder from textual.widget import Widget lorem = Text.from_markup( @@ -16,12 +13,12 @@ lorem = Text.from_markup( class Lorem(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Padding(lorem, 1) class Background(Widget): - def render(self, styles: Styles): + def render(self, style: Style): return VerticalGradient("#212121", "#212121") diff --git a/examples/calculator.py b/examples/calculator.py index 5463c7765..8cb5dd3f8 100644 --- a/examples/calculator.py +++ b/examples/calculator.py @@ -9,14 +9,14 @@ from decimal import Decimal from rich.align import Align from rich.console import Console, ConsoleOptions, RenderResult, RenderableType from rich.padding import Padding +from rich.style import Style from rich.text import Text from textual.app import App -from textual.css.styles import Styles from textual.reactive import Reactive from textual.views import GridView from textual.widget import Widget -from textual.widgets import Button, ButtonPressed +from textual.widgets import Button try: from pyfiglet import Figlet @@ -56,7 +56,7 @@ class Numbers(Widget): value = Reactive("0") - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: """Build a Rich renderable to render the calculator display.""" return Padding( Align.right(FigletText(self.value), vertical="middle"), diff --git a/sandbox/align.py b/sandbox/align.py index cd2929cf0..76c3b588c 100644 --- a/sandbox/align.py +++ b/sandbox/align.py @@ -1,13 +1,12 @@ -from rich.text import Text +from rich.style import Style from textual.app import App, ComposeResult -from textual.css.styles import Styles from textual.widget import Widget from textual.widgets import Static class Thing(Widget): - def render(self, styles: Styles): + def render(self, style: Style): return "Hello, 3434 World.\n[b]Lorem impsum." diff --git a/sandbox/basic.py b/sandbox/basic.py index b21495c4d..7e9f78c28 100644 --- a/sandbox/basic.py +++ b/sandbox/basic.py @@ -1,9 +1,9 @@ from rich.console import RenderableType +from rich.style import Style from rich.syntax import Syntax from rich.text import Text from textual.app import App -from textual.css.styles import Styles from textual.widget import Widget from textual.widgets import Static @@ -51,12 +51,12 @@ lorem = Text.from_markup( class TweetHeader(Widget): - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: return Text("Lorem Impsum", justify="center") class TweetBody(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return lorem @@ -65,22 +65,22 @@ class Tweet(Widget): class OptionItem(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Text("Option") class Error(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Text("This is an error message", justify="center") class Warning(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Text("This is a warning message", justify="center") class Success(Widget): - def render(self, styles: Styles) -> Text: + def render(self, style: Style) -> Text: return Text("This is a success message", justify="center") diff --git a/sandbox/dev_sandbox.py b/sandbox/dev_sandbox.py index d332ecca4..8d3794dba 100644 --- a/sandbox/dev_sandbox.py +++ b/sandbox/dev_sandbox.py @@ -1,13 +1,13 @@ from rich.console import RenderableType from rich.panel import Panel +from rich.style import Style from textual.app import App -from textual.css.styles import Styles from textual.widget import Widget class PanelWidget(Widget): - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: return Panel("hello world!", title="Title") diff --git a/sandbox/tabs.py b/sandbox/tabs.py index 9016ea954..db12d8404 100644 --- a/sandbox/tabs.py +++ b/sandbox/tabs.py @@ -3,16 +3,16 @@ from dataclasses import dataclass from rich.console import RenderableType from rich.padding import Padding from rich.rule import Rule +from rich.style import Style from textual import events from textual.app import App -from textual.css.styles import Styles from textual.widget import Widget from textual.widgets.tabs import Tabs, Tab class Hr(Widget): - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: return Rule() @@ -23,7 +23,7 @@ class Info(Widget): super().__init__() self.text = text - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: return Padding(f"{self.text}", pad=(0, 1)) diff --git a/sandbox/uber.css b/sandbox/uber.css index 517aec8d2..51b6aa021 100644 --- a/sandbox/uber.css +++ b/sandbox/uber.css @@ -3,6 +3,7 @@ background: green; overflow: hidden auto; border: heavy white; + text-style: underline; } .list-item { diff --git a/sandbox/uber.py b/sandbox/uber.py index b8dcbffc4..4d1d9a761 100644 --- a/sandbox/uber.py +++ b/sandbox/uber.py @@ -15,14 +15,13 @@ class BasicApp(App): self.bind("d", "dump") self.bind("t", "log_tree") self.bind("p", "print") - self.bind("o", "toggle_visibility") - self.bind("p", "toggle_display") + self.bind("v", "toggle_visibility") + self.bind("x", "toggle_display") self.bind("f", "modify_focussed") self.bind("b", "toggle_border") async def on_mount(self): """Build layout here.""" - uber1 = Widget( Placeholder(id="child1", classes="list-item"), Placeholder(id="child2", classes="list-item"), @@ -32,6 +31,7 @@ class BasicApp(App): Placeholder(classes="list-item"), ) self.mount(uber1=uber1) + uber1.focus() async def on_key(self, event: events.Key) -> None: await self.dispatch_key(event) @@ -47,9 +47,8 @@ class BasicApp(App): def action_print(self): print( - "Printed using builtin [b blue]print[/] function:", - self.screen.tree, - sep=" - ", + "Focused widget is:", + self.focused, ) print(1234, 5678) sys.stdout.write("abcdef") diff --git a/src/textual/app.py b/src/textual/app.py index 9ca074edb..84b689dd0 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -28,6 +28,7 @@ from rich.measure import Measurement from rich.protocol import is_renderable from rich.screen import Screen as ScreenRenderable from rich.segment import Segments +from rich.style import Style from rich.traceback import Traceback from . import actions @@ -40,7 +41,6 @@ from ._context import active_app from ._event_broker import extract_handler_actions, NoHandler from ._timer import Timer from .binding import Bindings, NoBinding -from .css.styles import Styles from .css.stylesheet import Stylesheet from .design import ColorSystem from .devtools.client import DevtoolsClient, DevtoolsConnectionError, DevtoolsLog @@ -469,7 +469,7 @@ class App(Generic[ReturnType], DOMNode): self.stylesheet.update(self) self.screen.refresh(layout=True) - def render(self, styles: Styles) -> RenderableType: + def render(self, styles: Style) -> RenderableType: return "" def query(self, selector: str | None = None) -> DOMQuery: diff --git a/src/textual/screen.py b/src/textual/screen.py index 024358e62..f7cf288c9 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -6,7 +6,6 @@ from rich.style import Style from . import events, messages, errors -from .css.styles import Styles from .geometry import Offset, Region from ._compositor import Compositor @@ -39,8 +38,8 @@ class Screen(Widget): def watch_dark(self, dark: bool) -> None: pass - def render(self, styles: Styles) -> RenderableType: - return self.app.render(styles) + def render(self, style: Style) -> RenderableType: + return self.app.render(style) def get_offset(self, widget: Widget) -> Offset: """Get the absolute offset of a given Widget. diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index bc16bf574..2d8675da5 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -8,7 +8,6 @@ from rich.console import ConsoleOptions, RenderResult, RenderableType from rich.segment import Segment, Segments from rich.style import Style, StyleType -from textual.css.styles import Styles from textual.reactive import Reactive from . import events from ._types import MessageTarget @@ -206,18 +205,18 @@ class ScrollBar(Widget): yield "window_size", self.window_size yield "position", self.position - def render(self, styles: Styles) -> RenderableType: - styles = self.parent.styles + def render(self, style: Style) -> RenderableType: + style = self.parent.styles style = Style( bgcolor=( - styles.scrollbar_background_hover.rich_color + style.scrollbar_background_hover.rich_color if self.mouse_over - else styles.scrollbar_background.rich_color + else style.scrollbar_background.rich_color ), color=( - styles.scrollbar_color_active.rich_color + style.scrollbar_color_active.rich_color if self.grabbed - else styles.scrollbar_color.rich_color + else style.scrollbar_color.rich_color ), ) return ScrollBarRender( @@ -285,7 +284,6 @@ class ScrollBar(Widget): if __name__ == "__main__": from rich.console import Console - from rich.segment import Segments console = Console() bar = ScrollBarRender() diff --git a/src/textual/widget.py b/src/textual/widget.py index 8a123a98b..e6cb952f3 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -157,7 +157,7 @@ class Widget(DOMNode): int: The optimal width of the content. """ console = self.app.console - renderable = self.render(self.styles) + renderable = self.render(self.styles.rich_style) measurement = Measurement.get(console, console.options, renderable) return measurement.maximum @@ -174,7 +174,7 @@ class Widget(DOMNode): Returns: int: The height of the content. """ - renderable = self.render(self.styles) + renderable = self.render(self.styles.rich_style) options = self.console.options.update_width(width) segments = self.console.render(renderable, options) # Cheaper than counting the lines returned from render_lines! @@ -462,7 +462,7 @@ class Widget(DOMNode): RenderableType: A new renderable. """ - renderable = self.render(self.styles) + renderable = self.render(self.styles.rich_style) styles = self.styles parent_styles = self.parent.styles @@ -477,9 +477,11 @@ class Widget(DOMNode): renderable_text_style = parent_text_style + text_style if renderable_text_style: - renderable = Styled(renderable, renderable_text_style) + color = text_style.color + bgcolor = text_style.bgcolor + renderable = Styled(renderable, Style(color=color, bgcolor=bgcolor)) - renderable = Padding(renderable, styles.padding, style=renderable_text_style) + renderable = Padding(renderable, styles.padding) if styles.border: renderable = Border( @@ -662,11 +664,11 @@ class Widget(DOMNode): self.set_dirty() self.check_idle() - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: """Get renderable for widget. Args: - styles (Styles): The Styles object for this Widget. + style (Styles): The Styles object for this Widget. Returns: RenderableType: Any renderable diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index 7e9f980bd..d062cc21c 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -3,10 +3,10 @@ from __future__ import annotations from typing import cast from rich.console import RenderableType +from rich.style import Style from rich.text import Text from .. import events -from ..css.styles import Styles from ..message import Message from ..reactive import Reactive from ..widget import Widget @@ -71,7 +71,7 @@ class Button(Widget, can_focus=True): return Text.from_markup(label) return label - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: return self.label async def on_click(self, event: events.Click) -> None: diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py index 50ccf1560..e111f0eb3 100644 --- a/src/textual/widgets/_footer.py +++ b/src/textual/widgets/_footer.py @@ -6,7 +6,6 @@ from rich.text import Text import rich.repr from .. import events -from ..css.styles import Styles from ..reactive import Reactive from ..widget import Widget @@ -60,7 +59,7 @@ class Footer(Widget): text.append_text(key_text) return text - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: if self._key_text is None: self._key_text = self.make_key_text() return self._key_text diff --git a/src/textual/widgets/_header.py b/src/textual/widgets/_header.py index 826ee36ba..965184bdf 100644 --- a/src/textual/widgets/_header.py +++ b/src/textual/widgets/_header.py @@ -6,11 +6,10 @@ from logging import getLogger from rich.console import RenderableType from rich.panel import Panel from rich.repr import Result -from rich.style import StyleType +from rich.style import StyleType, Style from rich.table import Table from .. import events -from ..css.styles import Styles from ..reactive import watch, Reactive from ..widget import Widget @@ -50,7 +49,7 @@ class Header(Widget): def get_clock(self) -> str: return datetime.now().time().strftime("%X") - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: header_table = Table.grid(padding=(0, 1), expand=True) header_table.style = self.style header_table.add_column(justify="left", ratio=0, width=8) diff --git a/src/textual/widgets/_placeholder.py b/src/textual/widgets/_placeholder.py index 7831f817d..e5d49e755 100644 --- a/src/textual/widgets/_placeholder.py +++ b/src/textual/widgets/_placeholder.py @@ -6,11 +6,9 @@ from rich.console import RenderableType from rich.panel import Panel from rich.pretty import Pretty import rich.repr +from rich.style import Style - -from .. import log from .. import events -from ..css.styles import Styles from ..reactive import Reactive from ..widget import Widget @@ -20,22 +18,23 @@ class Placeholder(Widget, can_focus=True): has_focus: Reactive[bool] = Reactive(False) mouse_over: Reactive[bool] = Reactive(False) - style: Reactive[str] = Reactive("") def __rich_repr__(self) -> rich.repr.Result: yield from super().__rich_repr__() yield "has_focus", self.has_focus, False yield "mouse_over", self.mouse_over, False - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: + # Apply colours only inside render_styled + # Pass the full RICH style object into `render` - not the `Styles` return Panel( Align.center( - Pretty(self, no_wrap=True, overflow="ellipsis"), vertical="middle" + Pretty(self, no_wrap=True, overflow="ellipsis"), + vertical="middle", ), title=self.__class__.__name__, border_style="green" if self.mouse_over else "blue", box=box.HEAVY if self.has_focus else box.ROUNDED, - style=self.style, ) async def on_focus(self, event: events.Focus) -> None: diff --git a/src/textual/widgets/_static.py b/src/textual/widgets/_static.py index d6fd5d3ec..b467c2d96 100644 --- a/src/textual/widgets/_static.py +++ b/src/textual/widgets/_static.py @@ -2,10 +2,9 @@ from __future__ import annotations from rich.console import RenderableType from rich.padding import Padding, PaddingDimensions -from rich.style import StyleType +from rich.style import StyleType, Style from rich.styled import Styled -from ..css.styles import Styles from ..widget import Widget @@ -25,7 +24,7 @@ class Static(Widget): self.style = style self.padding = padding - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: renderable = self.renderable if self.padding: renderable = Padding(renderable, self.padding) diff --git a/src/textual/widgets/_tree_control.py b/src/textual/widgets/_tree_control.py index 5ad857733..43dd9fc12 100644 --- a/src/textual/widgets/_tree_control.py +++ b/src/textual/widgets/_tree_control.py @@ -5,13 +5,11 @@ from typing import Generic, Iterator, NewType, TypeVar import rich.repr from rich.console import RenderableType +from rich.style import Style from rich.text import Text, TextType from rich.tree import Tree from rich.padding import PaddingDimensions -from textual.css.styles import Styles -from .. import log -from .. import events from ..reactive import Reactive from .._types import MessageTarget from ..widget import Widget @@ -250,7 +248,7 @@ class TreeControl(Generic[NodeDataType], Widget): push(iter(node.children)) return None - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: return self._tree def render_node(self, node: TreeNode[NodeDataType]) -> RenderableType: diff --git a/src/textual/widgets/tabs.py b/src/textual/widgets/tabs.py index ebab922fc..8d351c510 100644 --- a/src/textual/widgets/tabs.py +++ b/src/textual/widgets/tabs.py @@ -12,7 +12,6 @@ from rich.text import Text from textual import events from textual._layout_resolve import layout_resolve, Edge -from textual.css.styles import Styles from textual.keys import Keys from textual.reactive import Reactive from textual.renderables.opacity import Opacity @@ -331,7 +330,7 @@ class Tabs(Widget): """ return next((i for i, tab in enumerate(self.tabs) if tab.name == tab_name), 0) - def render(self, styles: Styles) -> RenderableType: + def render(self, style: Style) -> RenderableType: return TabsRenderable( self.tabs, tab_padding=self.tab_padding, diff --git a/tests/test_widget.py b/tests/test_widget.py index 5ea5131f4..b1c8e9f96 100644 --- a/tests/test_widget.py +++ b/tests/test_widget.py @@ -1,8 +1,8 @@ import pytest +from rich.style import Style from textual.app import App from textual.css.errors import StyleValueError -from textual.css.styles import Styles from textual.geometry import Size from textual.widget import Widget @@ -39,7 +39,7 @@ def test_widget_content_width(): self.text = text super().__init__(id=id) - def render(self, styles: Styles) -> str: + def render(self, style: Style) -> str: return self.text widget1 = TextWidget("foo", id="widget1") From 2425ba260f9e53b20cc7c1b8a7c3273af48bef5e Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 10 May 2022 09:55:04 +0100 Subject: [PATCH 06/10] Ensure we dont overwrite style object injected into render --- sandbox/tabs.py | 3 ++- sandbox/uber.css | 5 +++++ src/textual/scrollbar.py | 14 +++++++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/sandbox/tabs.py b/sandbox/tabs.py index db12d8404..efcfcd7c8 100644 --- a/sandbox/tabs.py +++ b/sandbox/tabs.py @@ -145,4 +145,5 @@ class BasicApp(App): self.mount(example.widget) -BasicApp.run(css_path="tabs.scss", watch_css=True, log_path="textual.log") +app = BasicApp(css_path="tabs.scss", watch_css=True, log_path="textual.log") +app.run() diff --git a/sandbox/uber.css b/sandbox/uber.css index d392f2f65..e0ab19cc2 100644 --- a/sandbox/uber.css +++ b/sandbox/uber.css @@ -14,6 +14,11 @@ App.-show-focus *:focus { background: darkslateblue; } +#child2 { + text-style: underline; + background: red; +} + .list-item { height: 10; color: #12a0; diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index 2d8675da5..768720e06 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -206,17 +206,17 @@ class ScrollBar(Widget): yield "position", self.position def render(self, style: Style) -> RenderableType: - style = self.parent.styles - style = Style( + styles = self.parent.styles + scrollbar_style = Style( bgcolor=( - style.scrollbar_background_hover.rich_color + styles.scrollbar_background_hover.rich_color if self.mouse_over - else style.scrollbar_background.rich_color + else styles.scrollbar_background.rich_color ), color=( - style.scrollbar_color_active.rich_color + styles.scrollbar_color_active.rich_color if self.grabbed - else style.scrollbar_color.rich_color + else styles.scrollbar_color.rich_color ), ) return ScrollBarRender( @@ -224,7 +224,7 @@ class ScrollBar(Widget): window_size=self.window_size, position=self.position, vertical=self.vertical, - style=style, + style=scrollbar_style, ) async def on_event(self, event) -> None: From b5bcc635119e88797db0d35691a1a8c335cc755e Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 10 May 2022 09:56:47 +0100 Subject: [PATCH 07/10] Use Style.from_colors shortcut --- src/textual/widget.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/textual/widget.py b/src/textual/widget.py index b644ac3a6..92860b21a 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -480,9 +480,8 @@ class Widget(DOMNode): renderable_text_style = parent_text_style + text_style if renderable_text_style: - color = text_style.color - bgcolor = text_style.bgcolor - renderable = Styled(renderable, Style(color=color, bgcolor=bgcolor)) + style = Style.from_colors(text_style.color, text_style.bgcolor) + renderable = Styled(renderable, style) renderable = Padding(renderable, styles.padding) From 055c43660c96b511de56d81b0aa47e794bf1916a Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 10 May 2022 09:58:47 +0100 Subject: [PATCH 08/10] Fix typo --- src/textual/widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/widget.py b/src/textual/widget.py index 92860b21a..128d9245e 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -480,7 +480,7 @@ class Widget(DOMNode): renderable_text_style = parent_text_style + text_style if renderable_text_style: - style = Style.from_colors(text_style.color, text_style.bgcolor) + style = Style.from_color(text_style.color, text_style.bgcolor) renderable = Styled(renderable, style) renderable = Padding(renderable, styles.padding) From b61ee05c9852c6607d4f3bb15f4b8b4968e5fa46 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 10 May 2022 11:22:04 +0100 Subject: [PATCH 09/10] Applying styles to button --- sandbox/buttons.css | 8 ++++++++ sandbox/buttons.py | 2 +- src/textual/widget.py | 5 ++--- src/textual/widgets/_button.py | 12 ++++++++---- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/sandbox/buttons.css b/sandbox/buttons.css index e69de29bb..df19f2b8d 100644 --- a/sandbox/buttons.css +++ b/sandbox/buttons.css @@ -0,0 +1,8 @@ +#foo { + text-style: underline; + background: rebeccapurple; +} + +#foo:hover { + background: greenyellow; +} diff --git a/sandbox/buttons.py b/sandbox/buttons.py index e61a73baf..16b14bdf7 100644 --- a/sandbox/buttons.py +++ b/sandbox/buttons.py @@ -18,7 +18,7 @@ class ButtonsApp(App[str]): self.exit(event.button.id) -app = ButtonsApp(log_path="textual.log", log_verbosity=2) +app = ButtonsApp(log_path="textual.log", css_path="buttons.css", log_verbosity=2) if __name__ == "__main__": result = app.run() diff --git a/src/textual/widget.py b/src/textual/widget.py index 128d9245e..f308e62dd 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -464,7 +464,6 @@ class Widget(DOMNode): Returns: RenderableType: A new renderable. """ - renderable = self.render(self.styles.rich_style) styles = self.styles @@ -478,13 +477,13 @@ class Widget(DOMNode): horizontal, vertical = content_align renderable = Align(renderable, horizontal, vertical=vertical) + renderable = Padding(renderable, styles.padding) + renderable_text_style = parent_text_style + text_style if renderable_text_style: style = Style.from_color(text_style.color, text_style.bgcolor) renderable = Styled(renderable, style) - renderable = Padding(renderable, styles.padding) - if styles.border: renderable = Border( renderable, diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index d062cc21c..f557a7144 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -4,7 +4,7 @@ from typing import cast from rich.console import RenderableType from rich.style import Style -from rich.text import Text +from rich.text import Text, TextType from .. import events from ..message import Message @@ -49,7 +49,7 @@ class Button(Widget, can_focus=True): def __init__( self, - label: RenderableType | None = None, + label: TextType | None = None, disabled: bool = False, *, name: str | None = None, @@ -58,7 +58,11 @@ class Button(Widget, can_focus=True): ): super().__init__(name=name, id=id, classes=classes) - self.label = self.css_identifier_styled if label is None else label + if label is None: + label = self.css_identifier_styled + + self.label: Text = self.validate_label(label) + self.disabled = disabled if disabled: self.add_class("-disabled") @@ -72,7 +76,7 @@ class Button(Widget, can_focus=True): return label def render(self, style: Style) -> RenderableType: - return self.label + return Text.styled(self.label.plain, style) async def on_click(self, event: events.Click) -> None: event.stop() From 595b944e0f0dc6e82d6fc39c2afcd66a5d2fccb2 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Wed, 11 May 2022 10:23:51 +0100 Subject: [PATCH 10/10] Remove unecessary validate method call, stylize button properly --- src/textual/widgets/_button.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index f557a7144..55f25a039 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -61,7 +61,7 @@ class Button(Widget, can_focus=True): if label is None: label = self.css_identifier_styled - self.label: Text = self.validate_label(label) + self.label: Text = label self.disabled = disabled if disabled: @@ -76,7 +76,9 @@ class Button(Widget, can_focus=True): return label def render(self, style: Style) -> RenderableType: - return Text.styled(self.label.plain, style) + label = self.label.copy() + label.stylize(style) + return label async def on_click(self, event: events.Click) -> None: event.stop()