Component classes reactive to theme change? #5502
-
I'm trying to achieve something similar to Textual's key panel in my app, where the colors change according to the current theme. It looks like the help panel achieves this using component classes, but I can't seem to manage to replicate this in my custom widgets. Hopefully the example app below demonstrates what I mean . Thanks in advance for any help to understand what I might be missing! from rich.text import Text
from textual import on
from textual.app import App, ComposeResult
from textual.reactive import var
from textual.widgets import OptionList, Static
class MyOptionList(OptionList):
COMPONENT_CLASSES = {
"my-option-list--title",
}
DEFAULT_CSS = """
MyOptionList {
.my-option-list--title {
color: $accent;
}
}
"""
def on_mount(self) -> None:
self.add_options([self._make_entry_content(i) for i in range(20)])
self.highlighted = 0
def _make_entry_content(self, index: int) -> Text:
title_style = self.get_component_rich_style(
"my-option-list--title",
partial=True,
)
content = Text.assemble(
(f"Option {index}", title_style),
" <- This color doesn't change",
)
return content
class MyStatic(Static):
COMPONENT_CLASSES = {
"my-static--title",
}
DEFAULT_CSS = """
MyStatic {
.my-static--title {
color: $accent;
text-style: bold;
}
}
"""
index: var[int] = var(0)
def watch_index(self) -> None:
title_style = self.get_component_rich_style(
"my-static--title",
partial=True,
)
content = Text.assemble(
(f"You selected Option {self.index}!", title_style),
"\n^ This color only changes when a new option is highlighted",
)
self.update(content)
class ExampleApp(App):
CSS = """
Static {
padding: 2;
}
"""
def compose(self) -> ComposeResult:
yield Static(
"Try changing the theme using the command palette.\n"
"The key colors in the help panel change according to the theme."
)
yield MyOptionList()
yield MyStatic()
def on_mount(self) -> None:
self.action_show_help_panel()
@on(MyOptionList.OptionHighlighted)
def update_label(self, event: MyOptionList.OptionHighlighted) -> None:
self.query_one(MyStatic).index = event.option_index
if __name__ == "__main__":
app = ExampleApp()
app.run() |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
I think I understand now why this works for Textual's key panel but not the widgets in my example. After taking a closer look at the source code, I found the key panel actually constructs its content within its |
Beta Was this translation helpful? Give feedback.
-
Just an update for anyone else who might stumble on this: Textual v2.0 added Content objects, which could be a better solution for using CSS variables. Here's my updated example: from textual import on
from textual.app import App, ComposeResult
from textual.content import Content
from textual.reactive import var
from textual.widgets import OptionList, Static
class MyOptionList(OptionList):
def on_mount(self) -> None:
self.add_options([self._make_entry_content(i) for i in range(20)])
self.highlighted = 0
def _make_entry_content(self, index: int) -> Content:
return Content.from_markup(
"[$accent]Option $index[/] <- This color now changes!",
index=index,
)
class MyStatic(Static):
index: var[int] = var(0)
def watch_index(self) -> None:
content = Content.from_markup(
"[bold $accent]You selected Option $index![/]"
"\n^ This color now changes!",
index=self.index,
)
self.update(content)
class ExampleApp(App):
CSS = """
Static {
padding: 2;
}
"""
def compose(self) -> ComposeResult:
yield Static(
"Try changing the theme using the command palette.\n"
"The key colors in the help panel change according to the theme."
)
yield MyOptionList()
yield MyStatic()
def on_mount(self) -> None:
self.action_show_help_panel()
@on(MyOptionList.OptionHighlighted)
def update_label(self, event: MyOptionList.OptionHighlighted) -> None:
self.query_one(MyStatic).index = event.option_index
if __name__ == "__main__":
app = ExampleApp()
app.run() |
Beta Was this translation helpful? Give feedback.
Just an update for anyone else who might stumble on this: Textual v2.0 added Content objects, which could be a better solution for using CSS variables.
Here's my updated example: