Pass Rich Style object into Textual render method

This commit is contained in:
Darren Burns
2022-05-06 10:20:05 +01:00
parent 6b1dab5387
commit 43f65d73ae
23 changed files with 74 additions and 86 deletions

View File

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

View File

@@ -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:

View File

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

View File

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

View File

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

View File

@@ -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."

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@
background: green;
overflow: hidden auto;
border: heavy white;
text-style: underline;
}
.list-item {

View File

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

View File

@@ -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:

View File

@@ -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.

View File

@@ -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()

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

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

View File

@@ -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:

View File

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

View File

@@ -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:

View File

@@ -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,

View File

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