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")