mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
ThemeProvider
This commit is contained in:
@@ -122,7 +122,7 @@ from textual.screen import (
|
||||
SystemModalScreen,
|
||||
)
|
||||
from textual.signal import Signal
|
||||
from textual.theme import BUILTIN_THEMES, Theme
|
||||
from textual.theme import BUILTIN_THEMES, Theme, ThemeProvider
|
||||
from textual.timer import Timer
|
||||
from textual.widget import AwaitMount, Widget
|
||||
from textual.widgets._toast import ToastRack
|
||||
@@ -1527,7 +1527,12 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
def action_change_theme(self) -> None:
|
||||
"""An [action](/guide/actions) to change the current theme."""
|
||||
self.push_screen(CommandPalette())
|
||||
self.app.push_screen(
|
||||
CommandPalette(
|
||||
[ThemeProvider],
|
||||
placeholder="Search for themes…",
|
||||
),
|
||||
)
|
||||
|
||||
def action_screenshot(
|
||||
self, filename: str | None = None, path: str | None = None
|
||||
@@ -4138,7 +4143,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
def action_command_palette(self) -> None:
|
||||
"""Show the Textual command palette."""
|
||||
if self.use_command_palette and not CommandPalette.is_open(self):
|
||||
self.push_screen(CommandPalette())
|
||||
self.push_screen(CommandPalette(id="--command-palette"))
|
||||
|
||||
def _suspend_signal(self) -> None:
|
||||
"""Signal that the application is being suspended."""
|
||||
|
||||
@@ -597,9 +597,6 @@ class CommandPalette(SystemModalScreen):
|
||||
_calling_screen: var[Screen[Any] | None] = var(None)
|
||||
"""A record of the screen that was active when we were called."""
|
||||
|
||||
_PALETTE_ID: Final[str] = "--command-palette"
|
||||
"""The internal ID for the command palette."""
|
||||
|
||||
@dataclass
|
||||
class OptionHighlighted(Message):
|
||||
"""Posted to App when an option is highlighted in the command palette."""
|
||||
@@ -618,14 +615,29 @@ class CommandPalette(SystemModalScreen):
|
||||
option_selected: bool
|
||||
"""True if an option was selected, False if the palette was closed without selecting an option."""
|
||||
|
||||
def __init__(self, providers: ProviderSource | None = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
providers: ProviderSource | None = None,
|
||||
*,
|
||||
placeholder: str = "Search for commands…",
|
||||
name: str | None = None,
|
||||
id: str | None = None,
|
||||
classes: str | None = None,
|
||||
) -> None:
|
||||
"""Initialise the command palette.
|
||||
|
||||
Args:
|
||||
providers: An optional list of providers to use. If None, the providers supplied
|
||||
in the App or Screen will be used.
|
||||
placeholder: The placeholder text for the command palette.
|
||||
"""
|
||||
super().__init__(id=self._PALETTE_ID)
|
||||
super().__init__(
|
||||
id=id,
|
||||
classes=classes,
|
||||
name=name,
|
||||
)
|
||||
self.add_class("--textual-command-palette")
|
||||
|
||||
self._selected_command: DiscoveryHit | Hit | None = None
|
||||
"""The command that was selected by the user."""
|
||||
self._busy_timer: Timer | None = None
|
||||
@@ -637,18 +649,19 @@ class CommandPalette(SystemModalScreen):
|
||||
"""List of Provider instances involved in searches."""
|
||||
self._hit_count: int = 0
|
||||
"""Number of hits displayed."""
|
||||
self._placeholder = placeholder
|
||||
|
||||
@staticmethod
|
||||
def is_open(app: App[object]) -> bool:
|
||||
"""Is the command palette current open?
|
||||
"""Is a command palette current open?
|
||||
|
||||
Args:
|
||||
app: The app to test.
|
||||
|
||||
Returns:
|
||||
`True` if the command palette is currently open, `False` if not.
|
||||
`True` if a command palette is currently open, `False` if not.
|
||||
"""
|
||||
return app.screen.id == CommandPalette._PALETTE_ID
|
||||
return app.screen.has_class("--textual-command-palette")
|
||||
|
||||
@property
|
||||
def _provider_classes(self) -> set[type[Provider]]:
|
||||
@@ -697,7 +710,7 @@ class CommandPalette(SystemModalScreen):
|
||||
with Vertical(id="--container"):
|
||||
with Horizontal(id="--input"):
|
||||
yield SearchIcon()
|
||||
yield CommandInput(placeholder="Search for commands…")
|
||||
yield CommandInput(placeholder=self._placeholder)
|
||||
if not self.run_on_select:
|
||||
yield Button("\u25b6")
|
||||
with Vertical(id="--results"):
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
from typing import Callable
|
||||
|
||||
from textual.command import DiscoveryHit, Hit, Hits, Provider
|
||||
from textual.design import ColorSystem
|
||||
|
||||
|
||||
@@ -366,3 +369,34 @@ BUILTIN_THEMES: dict[str, Theme] = {
|
||||
panel="#2A2A2A", # Dark Gray
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class ThemeProvider(Provider):
|
||||
"""A provider for themes."""
|
||||
|
||||
@property
|
||||
def commands(self) -> list[tuple[str, Callable[[], None]]]:
|
||||
themes = self.app.available_themes
|
||||
|
||||
def set_app_theme(name: str) -> None:
|
||||
self.app.theme = name
|
||||
|
||||
return [
|
||||
(theme.name, partial(set_app_theme, theme.name))
|
||||
for theme in themes.values()
|
||||
]
|
||||
|
||||
async def discover(self) -> Hits:
|
||||
for command in self.commands:
|
||||
yield DiscoveryHit(*command)
|
||||
|
||||
async def search(self, query: str) -> Hits:
|
||||
matcher = self.matcher(query)
|
||||
|
||||
for name, callback in self.commands:
|
||||
if (match := matcher.match(name)) > 0:
|
||||
yield Hit(
|
||||
match,
|
||||
matcher.highlight(name),
|
||||
callback,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user