From 2a134167441f42edeb53788e8b2fcddf29bdede7 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 17 Oct 2024 16:13:07 +0100 Subject: [PATCH] Overriding generated theme variables --- src/textual/command.py | 1 - src/textual/design.py | 100 +++++++++++++++++++++++++++-------------- src/textual/theme.py | 7 +++ 3 files changed, 74 insertions(+), 34 deletions(-) diff --git a/src/textual/command.py b/src/textual/command.py index 41bd4ad086..151b404354 100644 --- a/src/textual/command.py +++ b/src/textual/command.py @@ -227,7 +227,6 @@ def app(self) -> App[object]: @property def match_style(self) -> Style | None: """The preferred style to use when highlighting matching portions of the [`match_display`][textual.command.Hit.match_display].""" - print("inside property match_style", self.__match_style) return self.__match_style def matcher(self, user_input: str, case_sensitive: bool = False) -> Matcher: diff --git a/src/textual/design.py b/src/textual/design.py index 5938a1ffcd..590301c7cb 100644 --- a/src/textual/design.py +++ b/src/textual/design.py @@ -65,6 +65,7 @@ def __init__( dark: bool = False, luminosity_spread: float = 0.15, text_alpha: float = 0.95, + variables: dict[str, str] | None = None, ): def parse(color: str | None) -> Color | None: if color is None: @@ -85,6 +86,8 @@ def parse(color: str | None) -> Color | None: self.dark = dark self.luminosity_spread = luminosity_spread self.text_alpha = text_alpha + self.variables = variables or {} + """Overrides for specific variables.""" @property def shades(self) -> Iterable[str]: @@ -98,6 +101,10 @@ def shades(self) -> Iterable[str]: else: yield color + def get_or_default(self, name: str, default: str) -> str: + """Get the value of a color variable, or the default value if not set.""" + return self.variables.get(name, default) + def generate(self) -> dict[str, str]: """Generate a mapping of color name on to a CSS color. @@ -170,63 +177,90 @@ def luminosity_range(spread: float) -> Iterable[tuple[str, float]]: # Colors names that have a dark variant DARK_SHADES = {"primary-background", "secondary-background"} + get = self.get_or_default + for name, color in COLORS: is_dark_shade = dark and name in DARK_SHADES spread = luminosity_spread for shade_name, luminosity_delta in luminosity_range(spread): + key = f"{name}{shade_name}" if color.ansi is not None: - colors[f"{name}{shade_name}"] = color.hex + colors[key] = color.hex elif is_dark_shade: dark_background = background.blend(color, 0.15, alpha=1.0) - shade_color = dark_background.blend( - WHITE, spread + luminosity_delta, alpha=1.0 - ).clamped - colors[f"{name}{shade_name}"] = shade_color.hex + if key not in self.variables: + shade_color = dark_background.blend( + WHITE, spread + luminosity_delta, alpha=1.0 + ).clamped + colors[key] = shade_color.hex + else: + colors[key] = self.variables[key] else: - shade_color = color.lighten(luminosity_delta) - colors[f"{name}{shade_name}"] = shade_color.hex + colors[key] = get(key, color.lighten(luminosity_delta).hex) if foreground.ansi is None: - colors["text"] = "auto 87%" - colors["text-muted"] = "auto 60%" - colors["text-disabled"] = "auto 38%" + colors["text"] = get("text", "auto 87%") + colors["text-muted"] = get("text-muted", "auto 60%") + colors["text-disabled"] = get("text-disabled", "auto 38%") else: colors["text"] = "ansi_default" colors["text-muted"] = "ansi_default" colors["text-disabled"] = "ansi_default" # The cursor color for widgets such as OptionList, DataTable, etc. - colors["block-cursor-foreground"] = colors["text"] - colors["block-cursor-background"] = secondary.hex - colors["block-cursor-text-style"] = "bold" - colors["block-cursor-blurred-foreground"] = colors["text"] - colors["block-cursor-blurred-background"] = secondary.with_alpha(0.3).hex - colors["block-cursor-blurred-text-style"] = "" - colors["block-hover-background"] = boost.with_alpha(0.05).hex + colors["block-cursor-foreground"] = get( + "block-cursor-foreground", colors["text"] + ) + colors["block-cursor-background"] = get( + "block-cursor-background", secondary.hex + ) + colors["block-cursor-text-style"] = get("block-cursor-text-style", "bold") + colors["block-cursor-blurred-foreground"] = get( + "block-cursor-blurred-foreground", colors["text"] + ) + colors["block-cursor-blurred-background"] = get( + "block-cursor-blurred-background", secondary.with_alpha(0.3).hex + ) + colors["block-cursor-blurred-text-style"] = get( + "block-cursor-blurred-text-style", "none" + ) + colors["block-hover-background"] = get( + "block-hover-background", boost.with_alpha(0.05).hex + ) # The border color for focused widgets which have a border. - colors["border"] = secondary.hex - colors["border-blurred"] = surface.darken(0.025).hex + colors["border"] = get("border", secondary.hex) + colors["border-blurred"] = get("border-blurred", surface.darken(0.025).hex) # The surface color for builtin focused widgets - colors["surface-active"] = surface.lighten(self.luminosity_spread / 2.5).hex + colors["surface-active"] = get( + "surface-active", surface.lighten(self.luminosity_spread / 2.5).hex + ) # The scrollbar colors - colors["scrollbar"] = panel.hex - colors["scrollbar-hover"] = colors["panel-lighten-1"] - colors["scrollbar-active"] = colors["panel-lighten-2"] - colors["scrollbar-background"] = colors["background-darken-1"] - colors["scrollbar-corner-color"] = colors["background"] - colors["scrollbar-background-hover"] = colors["scrollbar-background"] - colors["scrollbar-background-active"] = colors["scrollbar-background"] + colors["scrollbar"] = get("scrollbar", panel.hex) + colors["scrollbar-hover"] = get("scrollbar-hover", colors["panel-lighten-1"]) + colors["scrollbar-active"] = get("scrollbar-active", colors["panel-lighten-2"]) + colors["scrollbar-background"] = get( + "scrollbar-background", colors["background-darken-1"] + ) + colors["scrollbar-corner-color"] = get( + "scrollbar-corner-color", colors["background"] + ) + colors["scrollbar-background-hover"] = get( + "scrollbar-background-hover", colors["scrollbar-background"] + ) + colors["scrollbar-background-active"] = get( + "scrollbar-background-active", colors["scrollbar-background"] + ) # Links - colors["link-background"] = "initial" - colors["link-background-hover"] = colors["secondary"] - colors["link-color"] = colors["text"] - colors["link-style"] = "underline" - colors["link-color-hover"] = colors["text"] - colors["link-style-hover"] = "bold not underline" + colors["link-background"] = get("link-background", "initial") + colors["link-background-hover"] = get("link-background-hover", secondary.hex) + colors["link-color"] = get("link-color", colors["text"]) + colors["link-style"] = get("link-style", "underline") + colors["link-color-hover"] = get("link-color-hover", colors["text"]) + colors["link-style-hover"] = get("link-style-hover", "bold not underline") return colors diff --git a/src/textual/theme.py b/src/textual/theme.py index 2783948a15..1c9ce6dba3 100644 --- a/src/textual/theme.py +++ b/src/textual/theme.py @@ -34,6 +34,7 @@ class Theme: dark: bool = False luminosity_spread: float = 0.15 text_alpha: float = 0.95 + variables: dict[str, str] | None = None def to_color_system(self) -> ColorSystem: """ @@ -57,6 +58,7 @@ def to_color_system(self) -> ColorSystem: dark=self.dark, luminosity_spread=self.luminosity_spread, text_alpha=self.text_alpha, + variables=self.variables, ) @@ -94,6 +96,11 @@ def to_color_system(self) -> ColorSystem: panel="#434C5E", # Nord2 - Polar Night foreground="#D8DEE9", # Nord4 - Snow Storm dark=True, + variables={ + "block-cursor-background": "#88C0D0", + "block-cursor-foreground": "#2E3440", + "block-cursor-text-style": "bold", + }, ), "gruvbox": Theme( name="gruvbox",