Skip to content

Commit

Permalink
Further removal of App.dark from various places, swapping in App.them…
Browse files Browse the repository at this point in the history
…e equivalents
  • Loading branch information
darrenburns committed Oct 24, 2024
1 parent f8af200 commit 0c5f611
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 55 deletions.
4 changes: 2 additions & 2 deletions docs/guide/CSS.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ The `background: green` is only applied to the Button underneath the mouse curso
Here are some other pseudo classes:

- `:blur` Matches widgets which *do not* have input focus.
- `:dark` Matches widgets in dark mode (where `App.dark == True`).
- `:dark` Matches widgets in dark themes (where `App.theme.dark == True`).
- `:disabled` Matches widgets which are in a disabled state.
- `:enabled` Matches widgets which are in an enabled state.
- `:even` Matches a widget at an evenly numbered position within its siblings.
Expand All @@ -336,7 +336,7 @@ Here are some other pseudo classes:
- `:focus` Matches widgets which have input focus.
- `:inline` Matches widgets when the app is running in inline mode.
- `:last-of-type` Matches a widget that is the last of its type amongst its siblings.
- `:light` Matches widgets in dark mode (where `App.dark == False`).
- `:light` Matches widgets in light themes (where `App.theme.dark == False`).
- `:odd` Matches a widget at an oddly numbered position within its siblings.

## Combinators
Expand Down
2 changes: 1 addition & 1 deletion examples/theme_sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def watch_theme(self, theme_name: str) -> None:

def compose(self) -> ComposeResult:
with Grid(id="palette"):
theme = self.get_theme(self.theme)
theme = self.current_theme
for variable, value in vars(theme).items():
if variable not in {
"name",
Expand Down
3 changes: 1 addition & 2 deletions src/textual/_text_area_theme.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ def apply_css(self, text_area: TextArea) -> None:
if self.base_style.color is None:
self.base_style = Style(color="#f3f3f3", bgcolor=self.base_style.bgcolor)

app = text_area.app
app_theme = app.get_theme(app.theme)
app_theme = text_area.app.current_theme

if self.base_style.bgcolor is None:
self.base_style = Style(
Expand Down
39 changes: 11 additions & 28 deletions src/textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,9 @@ def __init__(
This excludes the built-in themes."""

ansi_theme = self.ansi_theme_dark if self.dark else self.ansi_theme_light
ansi_theme = (
self.ansi_theme_dark if self.current_theme.dark else self.ansi_theme_light
)
self.set_reactive(App.ansi_color, ansi_color)
self._filters: list[LineFilter] = [
ANSIToTruecolor(ansi_theme, enabled=not ansi_color)
Expand Down Expand Up @@ -764,8 +766,8 @@ def __init__(
perform work after the app has resumed.
"""

self.set_class(self.dark, "-dark-mode")
self.set_class(not self.dark, "-light-mode")
self.set_class(self.current_theme.dark, "-dark-mode")
self.set_class(not self.current_theme.dark, "-light-mode")

self.animation_level: AnimationLevel = constants.TEXTUAL_ANIMATIONS
"""Determines what type of animations the app will display.
Expand Down Expand Up @@ -1183,11 +1185,7 @@ def get_css_variables(self) -> dict[str, str]:
Returns:
A mapping of variable name to value.
"""
theme = self.get_theme(self.theme)
if theme is None:
# This should never happen thanks to the `App.theme` validator.
return {}

theme = self.current_theme
# Build the Textual color system from the theme.
# This will contain $secondary, $primary, $background, etc.
variables = theme.to_color_system().generate()
Expand Down Expand Up @@ -1265,28 +1263,13 @@ def _watch_theme(self, theme_name: str) -> None:
self.call_next(self.refresh_css)
self.call_next(self.theme_changed_signal.publish, theme)

def watch_dark(self, dark: bool) -> None:
"""Watches the dark bool.
This method handles the transition between light and dark mode when you
change the [dark][textual.app.App.dark] attribute.
"""
self.set_class(dark, "-dark-mode", update=False)
self.set_class(not dark, "-light-mode", update=False)
self._refresh_truecolor_filter(self.ansi_theme)
self.call_next(self.refresh_css)

def watch_ansi_theme_dark(self, theme: TerminalTheme) -> None:
app_theme = self.get_theme(self.theme)
assert app_theme is not None # validated by _validate_theme
if app_theme.dark:
if self.current_theme.dark:
self._refresh_truecolor_filter(theme)
self.call_next(self.refresh_css)

def watch_ansi_theme_light(self, theme: TerminalTheme) -> None:
app_theme = self.get_theme(self.theme)
assert app_theme is not None # validated by _validate_theme
if not app_theme.dark:
if not self.current_theme.dark:
self._refresh_truecolor_filter(theme)
self.call_next(self.refresh_css)

Expand All @@ -1297,9 +1280,9 @@ def ansi_theme(self) -> TerminalTheme:
Defines how colors defined as ANSI (e.g. `magenta`) inside Rich renderables
are mapped to hex codes.
"""
app_theme = self.get_theme(self.theme)
assert app_theme is not None # validated by _validate_theme
return self.ansi_theme_dark if app_theme.dark else self.ansi_theme_light
return (
self.ansi_theme_dark if self.current_theme.dark else self.ansi_theme_light
)

def _refresh_truecolor_filter(self, theme: TerminalTheme) -> None:
"""Update the ANSI to Truecolor filter, if available, with a new theme mapping.
Expand Down
21 changes: 10 additions & 11 deletions src/textual/dom.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def set_reactive(
Example:
```python
self.set_reactive(App.dark_mode, True)
self.set_reactive(App.theme, "textual-light")
```
Args:
Expand All @@ -247,15 +247,14 @@ def set_reactive(
Raises:
AttributeError: If the first argument is not a reactive.
"""
name = reactive.name
if not isinstance(reactive, Reactive):
raise TypeError(
"A Reactive class is required; for example: MyApp.dark_mode"
)
if reactive.name not in self._reactives:
raise TypeError("A Reactive class is required; for example: MyApp.theme")
if name not in self._reactives:
raise AttributeError(
"No reactive called {name!r}; Have you called super().__init__(...) in the {self.__class__.__name__} constructor?"
f"No reactive called {name!r}; Have you called super().__init__(...) in the {self.__class__.__name__} constructor?"
)
setattr(self, f"_reactive_{reactive.name}", value)
setattr(self, f"_reactive_{name}", value)

def mutate_reactive(self, reactive: Reactive[ReactiveType]) -> None:
"""Force an update to a mutable reactive.
Expand Down Expand Up @@ -1218,11 +1217,11 @@ def watch(
Example:
```python
def on_dark_change(old_value:bool, new_value:bool) -> None:
# Called when app.dark changes.
print("App.dark went from {old_value} to {new_value}")
def on_theme_change(old_value:str, new_value:str) -> None:
# Called when app.theme changes.
print(f"App.theme went from {old_value} to {new_value}")
self.watch(self.app, "dark", self.on_dark_change, init=False)
self.watch(self.app, "theme", self.on_theme_change, init=False)
```
Args:
Expand Down
4 changes: 2 additions & 2 deletions src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ class Widget(DOMNode):
"can-focus": lambda widget: widget.can_focus,
"disabled": lambda widget: widget.is_disabled,
"enabled": lambda widget: not widget.is_disabled,
"dark": lambda widget: widget.app.dark,
"light": lambda widget: not widget.app.dark,
"dark": lambda widget: widget.app.current_theme.dark,
"light": lambda widget: not widget.app.current_theme.dark,
"focus-within": lambda widget: widget.has_focus_within,
"inline": lambda widget: widget.app.is_inline,
"ansi": lambda widget: widget.app.ansi_color,
Expand Down
10 changes: 4 additions & 6 deletions src/textual/widgets/_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,10 +619,9 @@ def __init__(self, markdown: Markdown, code: str, lexer: str) -> None:
super().__init__(markdown)
self.code = code
self.lexer = lexer
app_theme = self.app.get_theme(self.app.theme)
self.theme = (
self._markdown.code_dark_theme
if app_theme.dark
if self.app.current_theme.dark
else self._markdown.code_light_theme
)

Expand All @@ -642,10 +641,9 @@ def _on_mount(self, _: Mount) -> None:

def _retheme(self) -> None:
"""Rerender when the theme changes."""
app_theme = self.app.get_theme(self.app.theme)
self.theme = (
self._markdown.code_dark_theme
if app_theme.dark
if self.app.current_theme.dark
else self._markdown.code_light_theme
)
self.get_child_by_type(Static).update(self._block())
Expand Down Expand Up @@ -807,13 +805,13 @@ async def _on_mount(self, _: Mount) -> None:

def _watch_code_dark_theme(self) -> None:
"""React to the dark theme being changed."""
if self.app.dark:
if self.app.current_theme.dark:
for block in self.query(MarkdownFence):
block._retheme()

def _watch_code_light_theme(self) -> None:
"""React to the light theme being changed."""
if not self.app.dark:
if not self.app.current_theme.dark:
for block in self.query(MarkdownFence):
block._retheme()

Expand Down
6 changes: 3 additions & 3 deletions src/textual/widgets/_text_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ def _watch_theme(self, theme: str) -> None:
if padding is applied, the colors match."""
self._set_theme(theme)

def _app_dark_toggled(self) -> None:
def _app_theme_changed(self) -> None:
self._set_theme(self._theme.name)

def _set_theme(self, theme: str) -> None:
Expand Down Expand Up @@ -1521,8 +1521,8 @@ def gutter_width(self) -> int:
return gutter_width

def _on_mount(self, event: events.Mount) -> None:
# When `app.dark` is toggled, reset the theme (since it caches values).
self.watch(self.app, "dark", self._app_dark_toggled, init=False)
# When `app.theme` reactive is changed, reset the theme to clear cached styles.
self.watch(self.app, "theme", self._app_theme_changed, init=False)

self.blink_timer = self.set_interval(
0.5,
Expand Down

0 comments on commit 0c5f611

Please sign in to comment.