From 6f7d3b5ad711aa7df62ca6b3fca5cd638dcec665 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 13 Sep 2022 10:53:22 +0100 Subject: [PATCH] text color --- examples/calculator.css | 2 +- examples/code_browser.css | 9 ++++-- examples/code_browser.py | 4 +-- sandbox/will/basic.css | 39 +++++++++++--------------- sandbox/will/calculator.css | 2 +- sandbox/will/center2.py | 2 +- sandbox/will/design.css | 15 ++++++---- sandbox/will/design.py | 3 +- src/textual/_styles_cache.py | 14 +++++---- src/textual/app.py | 5 +++- src/textual/cli/previews/borders.py | 2 +- src/textual/cli/previews/easing.css | 3 +- src/textual/color.py | 21 +++++++++++--- src/textual/css/_styles_builder.py | 7 +++-- src/textual/design.py | 34 +++++++++++----------- src/textual/screen.py | 1 + src/textual/scrollbar.py | 2 +- src/textual/widgets/_button.py | 22 +++++++-------- src/textual/widgets/_data_table.py | 8 +++--- src/textual/widgets/_directory_tree.py | 1 - src/textual/widgets/_footer.py | 11 +++----- src/textual/widgets/_header.py | 4 +-- src/textual/widgets/_tree_control.py | 9 +++--- 23 files changed, 118 insertions(+), 102 deletions(-) diff --git a/examples/calculator.css b/examples/calculator.css index 4a1d15663..ce9458a10 100644 --- a/examples/calculator.css +++ b/examples/calculator.css @@ -24,7 +24,7 @@ Button { padding: 0 1; height: 100%; background: $primary-lighten-2; - color: $text-primary-lighten-2; + color: $text; } #number-0 { diff --git a/examples/code_browser.css b/examples/code_browser.css index 00f62979d..ac6d8c35c 100644 --- a/examples/code_browser.css +++ b/examples/code_browser.css @@ -1,3 +1,7 @@ +Screen { + background: $surface-darken-1; +} + #tree-view { display: none; scrollbar-gutter: stable; @@ -9,7 +13,7 @@ CodeBrowser.-show-tree #tree-view { dock: left; height: 100%; max-width: 50%; - background: $surface; + background: $surface; } CodeBrowser{ @@ -17,8 +21,7 @@ CodeBrowser{ } DirectoryTree { - padding-right: 1; - + padding-right: 1; } #code { diff --git a/examples/code_browser.py b/examples/code_browser.py index ca2dac027..3cb625655 100644 --- a/examples/code_browser.py +++ b/examples/code_browser.py @@ -42,10 +42,10 @@ class CodeBrowser(App): line_numbers=True, word_wrap=True, indent_guides=True, - theme="monokai", + theme="github-dark", ) except Exception: - code_view.update(Traceback(theme="monokai", width=None)) + code_view.update(Traceback(theme="github-dark", width=None)) self.sub_title = "ERROR" else: code_view.update(syntax) diff --git a/sandbox/will/basic.css b/sandbox/will/basic.css index 7c2ad4c39..88de0d535 100644 --- a/sandbox/will/basic.css +++ b/sandbox/will/basic.css @@ -14,16 +14,11 @@ App > Screen { - background: $surface; - color: $text-surface; - layers: base sidebar; - - color: $text-background; background: $background; + color: $text; + layers: base sidebar; layout: vertical; - overflow: hidden; - } #tree-container { @@ -52,9 +47,9 @@ DataTable { height: 24; } -#sidebar { - color: $text-panel; +#sidebar { background: $panel; + color: $text; dock: left; width: 30; margin-bottom: 1; @@ -71,7 +66,7 @@ DataTable { #sidebar .title { height: 1; background: $primary-background-darken-1; - color: $text-primary-background-darken-1; + color: $text; border-right: wide $background; content-align: center middle; } @@ -79,14 +74,14 @@ DataTable { #sidebar .user { height: 8; background: $panel-darken-1; - color: $text-panel-darken-1; + color: $text; border-right: wide $background; content-align: center middle; } #sidebar .content { background: $panel-darken-2; - color: $text-surface; + color: $text; border-right: wide $background; content-align: center middle; } @@ -101,7 +96,7 @@ Tweet { margin:0 2; background: $panel; - color: $text-panel; + color: $text; layout: vertical; /* border: outer $primary; */ padding: 1; @@ -132,13 +127,13 @@ Tweet { TweetHeader { height:1; background: $accent; - color: $text-accent + color: $text; } TweetBody { width: 100%; background: $panel; - color: $text-panel; + color: $text; height: auto; padding: 0 1 0 0; } @@ -149,7 +144,7 @@ Tweet.scroll-horizontal TweetBody { .button { background: $accent; - color: $text-accent; + color: $text; width:20; height: 3; /* border-top: hidden $accent-darken-3; */ @@ -165,7 +160,7 @@ Tweet.scroll-horizontal TweetBody { .button:hover { background: $accent-lighten-1; - color: $text-accent-lighten-1; + color: $text; width: 20; height: 3; border: tall $accent-darken-1; @@ -177,7 +172,7 @@ Tweet.scroll-horizontal TweetBody { } #footer { - color: $text-accent; + color: $text; background: $accent; height: 1; @@ -200,7 +195,7 @@ OptionItem { OptionItem:hover { height: 3; - color: $text-primary; + color: $text; background: $primary-darken-1; /* border-top: hkey $accent2-darken-3; border-bottom: hkey $accent2-darken-3; */ @@ -212,7 +207,7 @@ Error { width: 100%; height:3; background: $error; - color: $text-error; + color: $text; border-top: tall $error-darken-2; border-bottom: tall $error-darken-2; @@ -225,7 +220,7 @@ Warning { width: 100%; height:3; background: $warning; - color: $text-warning-fade-1; + color: $text; border-top: tall $warning-darken-2; border-bottom: tall $warning-darken-2; @@ -239,7 +234,7 @@ Success { height:auto; box-sizing: border-box; background: $success; - color: $text-success-fade-1; + color: $text; border-top: hkey $success-darken-2; border-bottom: hkey $success-darken-2; diff --git a/sandbox/will/calculator.css b/sandbox/will/calculator.css index 4a1d15663..ce9458a10 100644 --- a/sandbox/will/calculator.css +++ b/sandbox/will/calculator.css @@ -24,7 +24,7 @@ Button { padding: 0 1; height: 100%; background: $primary-lighten-2; - color: $text-primary-lighten-2; + color: $text; } #number-0 { diff --git a/sandbox/will/center2.py b/sandbox/will/center2.py index d0191bf48..0ec20f61c 100644 --- a/sandbox/will/center2.py +++ b/sandbox/will/center2.py @@ -33,7 +33,7 @@ class CenterApp(App): Static { background: $panel; - color: $text-panel; + color: $text; content-align: center middle; } diff --git a/sandbox/will/design.css b/sandbox/will/design.css index a41cef0b6..323044432 100644 --- a/sandbox/will/design.css +++ b/sandbox/will/design.css @@ -1,19 +1,22 @@ +Screen { + background: $surface; +} + Container { height: auto; - background: $surface; - margin: 1 2; - + background: $boost; + } Panel { height: auto; - background: $surface; + background: $boost; margin: 1 2; - padding: 1 2; + } Content { - background: $surface; + background: $boost; padding: 1 2; margin: 1 2; color: auto 95%; diff --git a/sandbox/will/design.py b/sandbox/will/design.py index 488b3d8b4..48cb7e3ae 100644 --- a/sandbox/will/design.py +++ b/sandbox/will/design.py @@ -21,14 +21,13 @@ class DesignApp(App): def compose(self): yield Header() yield Footer() - yield Panel( + yield Container( Content("content"), Panel( Content("more content"), Content("more content"), ), ) - yield Panel(Static("Hello World")) app = DesignApp(css_path="design.css") diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 2c61d556e..ee7cc6d99 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -250,7 +250,7 @@ class StylesCache: line: Iterable[Segment] # Draw top or bottom borders (A) if (border_top and y == 0) or (border_bottom and y == height - 1): - border_color = background + ( + border_color = base_background + ( border_top_color if y == 0 else border_bottom_color ) box_segments = get_box( @@ -296,9 +296,13 @@ class StylesCache: if border_left or border_right: # Add left / right border - left_style = from_color((background + border_left_color).rich_color) + left_style = from_color( + (base_background + border_left_color).rich_color + ) left = get_box(border_left, inner, outer, left_style)[1][0] - right_style = from_color((background + border_right_color).rich_color) + right_style = from_color( + (base_background + border_right_color).rich_color + ) right = get_box(border_right, inner, outer, right_style)[1][2] if border_left and border_right: @@ -327,9 +331,9 @@ class StylesCache: elif outline_left or outline_right: # Lines in side outline - left_style = from_color((background + outline_left_color).rich_color) + left_style = from_color((base_background + outline_left_color).rich_color) left = get_box(outline_left, inner, outer, left_style)[1][0] - right_style = from_color((background + outline_right_color).rich_color) + right_style = from_color((base_background + outline_right_color).rich_color) right = get_box(outline_right, inner, outer, right_style)[1][2] line = line_trim(list(line), outline_left != "", outline_right != "") if outline_left and outline_right: diff --git a/src/textual/app.py b/src/textual/app.py index 58657f719..d318ca4a8 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -146,7 +146,7 @@ class App(Generic[ReturnType], DOMNode): DEFAULT_CSS = """ App { background: $background; - color: $text-background; + color: $text; } """ @@ -541,6 +541,9 @@ class App(Generic[ReturnType], DOMNode): except Exception as error: self._handle_exception(error) + def action_toggle_dark(self) -> None: + self.dark = not self.dark + def action_screenshot(self, path: str | None = None) -> None: """Action to save a screenshot.""" self.save_screenshot(path) diff --git a/src/textual/cli/previews/borders.py b/src/textual/cli/previews/borders.py index 376bd79d4..78a132c2b 100644 --- a/src/textual/cli/previews/borders.py +++ b/src/textual/cli/previews/borders.py @@ -41,7 +41,7 @@ class BorderApp(App): border: solid $primary; height: auto; background: $panel; - color: $text-panel; + color: $text; } """ diff --git a/src/textual/cli/previews/easing.css b/src/textual/cli/previews/easing.css index 7840c5590..ccc3dcc4a 100644 --- a/src/textual/cli/previews/easing.css +++ b/src/textual/cli/previews/easing.css @@ -19,6 +19,7 @@ EasingButtons { padding: 1; height: auto; dock: top; + background: $boost; } Bar { @@ -36,6 +37,6 @@ Bar { #opacity-widget { padding: 1; background: $warning; - color: $text-warning; + color: $text; border: wide $background; } diff --git a/src/textual/color.py b/src/textual/color.py index 9f36ad8ee..4fc9186b5 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -249,6 +249,17 @@ class Color(NamedTuple): else f"#{r:02X}{g:02X}{b:02X}{int(a*255):02X}" ) + @property + def hex6(self) -> str: + """The color in CSS hex form, with 6 digits for RGB. Alpha is ignored. + + Returns: + str: A CSS hex-style color, e.g. "#46b3de" + + """ + r, g, b, a = self.clamped + return f"#{r:02X}{g:02X}{b:02X}" + @property def css(self) -> str: """The color in CSS rgb or rgba form. @@ -397,29 +408,31 @@ class Color(NamedTuple): return color @lru_cache(maxsize=1024) - def darken(self, amount: float) -> Color: + def darken(self, amount: float, alpha: float | None = None) -> Color: """Darken the color by a given amount. Args: amount (float): Value between 0-1 to reduce luminance by. + alpha (float | None, optional): Alpha component for new color or None to copy alpha. Defaults to None. Returns: Color: New color. """ l, a, b = rgb_to_lab(self) l -= amount * 100 - return lab_to_rgb(Lab(l, a, b), self.a).clamped + return lab_to_rgb(Lab(l, a, b), self.a if alpha is None else alpha).clamped - def lighten(self, amount: float) -> Color: + def lighten(self, amount: float, alpha: float | None = None) -> Color: """Lighten the color by a given amount. Args: amount (float): Value between 0-1 to increase luminance by. + alpha (float | None, optional): Alpha component for new color or None to copy alpha. Defaults to None. Returns: Color: New color. """ - return self.darken(-amount) + return self.darken(-amount, alpha) @lru_cache(maxsize=1024) def get_contrast_text(self, alpha=0.95) -> Color: diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index 868429662..284d70cc7 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -580,7 +580,7 @@ class StylesBuilder: alpha: float | None = None for token in tokens: - if token.name == "token" and token.value == "auto": + if name == "color" and token.name == "token" and token.value == "auto": self.styles._rules["auto_color"] = True elif token.name == "scalar": alpha_scalar = Scalar.parse(token.value) @@ -600,9 +600,10 @@ class StylesBuilder: else: self.error(name, token, color_property_help_text(name, context="css")) - if color is not None: + if color is not None or alpha is not None: if alpha is not None: - color = color.with_alpha(alpha) + + color = (color or Color(255, 255, 255)).with_alpha(alpha) self.styles._rules[name] = color process_tint = process_color diff --git a/src/textual/design.py b/src/textual/design.py index b09fcabb4..96b9ea5e5 100644 --- a/src/textual/design.py +++ b/src/textual/design.py @@ -15,7 +15,7 @@ NUMBER_OF_SHADES = 3 # Where no content exists DEFAULT_DARK_BACKGROUND = "#000000" # What text usually goes on top off -DEFAULT_DARK_SURFACE = "#292929" +DEFAULT_DARK_SURFACE = "#1e1e1e" DEFAULT_LIGHT_SURFACE = "#f5f5f5" DEFAULT_LIGHT_BACKGROUND = "#efefef" @@ -38,6 +38,7 @@ class ColorSystem: "secondary-background", "surface", "panel", + "boost", "warning", "error", "success", @@ -55,6 +56,7 @@ class ColorSystem: background: str | None = None, surface: str | None = None, panel: str | None = None, + boost: str | None = None, dark: bool = False, luminosity_spread: float = 0.15, text_alpha: float = 0.95, @@ -73,6 +75,7 @@ class ColorSystem: self.background = parse(background) self.surface = parse(surface) self.panel = parse(panel) + self.boost = parse(boost) self._dark = dark self._luminosity_spread = luminosity_spread self._text_alpha = text_alpha @@ -126,6 +129,8 @@ class ColorSystem: else: panel = self.panel + boost = self.boost or background.get_contrast_text(1.0).with_alpha(0.07) + colors: dict[str, str] = {} def luminosity_range(spread) -> Iterable[tuple[str, float]]: @@ -153,6 +158,7 @@ class ColorSystem: ("secondary-background", secondary), ("background", background), ("panel", panel), + ("boost", boost), ("surface", surface), ("warning", warning), ("error", error), @@ -178,14 +184,10 @@ class ColorSystem: else: shade_color = color.lighten(luminosity_delta) colors[f"{name}{shade_name}"] = shade_color.hex - for fade in range(3): - text_color = shade_color.get_contrast_text(text_alpha) - if fade > 0: - text_color = text_color.blend(shade_color, fade * 0.1 + 0.15) - on_name = f"text-{name}{shade_name}-fade-{fade}" - else: - on_name = f"text-{name}{shade_name}" - colors[on_name] = text_color.hex + + colors["text"] = "auto 95%" + colors["text-muted"] = "auto 80%" + colors["text-disabled"] = "auto 60%" return colors @@ -206,16 +208,12 @@ def show_design(light: ColorSystem, dark: ColorSystem) -> Table: def make_shades(system: ColorSystem): colors = system.generate() for name in system.shades: - background = colors[name] - foreground = colors[f"text-{name}"] - text = Text(f"{background} ", style=f"{foreground} on {background}") - for fade in range(3): - foreground = colors[ - f"text-{name}-fade-{fade}" if fade else f"text-{name}" - ] - text.append(f"{name} ", style=f"{foreground} on {background}") + background = Color.parse(colors[name]).with_alpha(1.0) + foreground = background + background.get_contrast_text(0.9) - yield Padding(text, 1, style=f"{foreground} on {background}") + text = Text(name) + + yield Padding(text, 1, style=f"{foreground.hex6} on {background.hex6}") table = Table(box=None, expand=True) table.add_column("Light", justify="center") diff --git a/src/textual/screen.py b/src/textual/screen.py index 9714cf6f0..33674cd13 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -33,6 +33,7 @@ class Screen(Widget): Screen { layout: vertical; overflow-y: auto; + background: $surface; } """ diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index c223ca73b..b78ba5434 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -213,7 +213,7 @@ class ScrollBar(Widget): def render(self) -> RenderableType: styles = self.parent.styles - background = ( + background = self.parent.background_colors[1] + ( styles.scrollbar_background_hover if self.mouse_over else styles.scrollbar_background diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index 3b406be1b..3209ea894 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -36,7 +36,7 @@ class Button(Widget, can_focus=True): width: auto; height: 3; background: $panel; - color: $text-panel; + color: auto; border: none; border-top: tall $panel-lighten-2; border-bottom: tall $panel-darken-3; @@ -56,7 +56,7 @@ class Button(Widget, can_focus=True): Button:hover { border-top: tall $panel-lighten-1; background: $panel-darken-2; - color: $text-panel-darken-2; + color: $text; } Button.-active { @@ -69,7 +69,7 @@ class Button(Widget, can_focus=True): /* Primary variant */ Button.-primary { background: $primary; - color: $text-primary; + color: $text; border-top: tall $primary-lighten-3; border-bottom: tall $primary-darken-3; @@ -77,8 +77,8 @@ class Button(Widget, can_focus=True): Button.-primary:hover { background: $primary-darken-2; - color: $text-primary-darken-2; - + color: $text; + border-top: tall $primary-lighten-2; } Button.-primary.-active { @@ -91,14 +91,14 @@ class Button(Widget, can_focus=True): /* Success variant */ Button.-success { background: $success; - color: $text-success; + color: $text; border-top: tall $success-lighten-2; border-bottom: tall $success-darken-3; } Button.-success:hover { background: $success-darken-2; - color: $text-success-darken-2; + color: $text; } Button.-success.-active { @@ -111,14 +111,14 @@ class Button(Widget, can_focus=True): /* Warning variant */ Button.-warning { background: $warning; - color: $text-warning; + color: $text; border-top: tall $warning-lighten-2; border-bottom: tall $warning-darken-3; } Button.-warning:hover { background: $warning-darken-2; - color: $text-warning-darken-1; + color: $text; } @@ -132,7 +132,7 @@ class Button(Widget, can_focus=True): /* Error variant */ Button.-error { background: $error; - color: $text-error; + color: $text; border-top: tall $error-lighten-2; border-bottom: tall $error-darken-3; @@ -140,7 +140,7 @@ class Button(Widget, can_focus=True): Button.-error:hover { background: $error-darken-1; - color: $text-error-darken-2; + color: $text; } diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py index 71a667c08..57d7fb27b 100644 --- a/src/textual/widgets/_data_table.py +++ b/src/textual/widgets/_data_table.py @@ -109,17 +109,17 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True): DEFAULT_CSS = """ DataTable { background: $surface; - color: $text-surface; + color: $text; } DataTable > .datatable--header { text-style: bold; background: $primary; - color: $text-primary; + color: $text; } DataTable > .datatable--fixed { text-style: bold; background: $primary; - color: $text-primary; + color: $text; } DataTable > .datatable--odd-row { @@ -132,7 +132,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True): DataTable > .datatable--cursor { background: $secondary; - color: $text-secondary; + color: $text; } .-dark-mode DataTable > .datatable--even-row { diff --git a/src/textual/widgets/_directory_tree.py b/src/textual/widgets/_directory_tree.py index 9512641f1..3486e39c6 100644 --- a/src/textual/widgets/_directory_tree.py +++ b/src/textual/widgets/_directory_tree.py @@ -74,7 +74,6 @@ class DirectoryTree(TreeControl[DirEntry]): label.stylize("bold") icon = "📂" if expanded else "📁" else: - icon = "📄" label.highlight_regex(r"\..*$", "italic") diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py index 4cdd3390c..182782c13 100644 --- a/src/textual/widgets/_footer.py +++ b/src/textual/widgets/_footer.py @@ -16,25 +16,22 @@ class Footer(Widget): DEFAULT_CSS = """ Footer { background: $accent; - color: $text-accent; + color: $text; dock: bottom; height: 1; } Footer > .footer--highlight { - background: $accent-darken-1; - color: $text-accent-darken-1; + background: $accent-darken-1; } Footer > .footer--highlight-key { - background: $secondary; - color: $text-secondary; + background: $secondary; text-style: bold; } Footer > .footer--key { text-style: bold; - background: $accent-darken-2; - color: $text-accent-darken-2; + background: $accent-darken-2; } """ diff --git a/src/textual/widgets/_header.py b/src/textual/widgets/_header.py index 6f4ba044e..34c99629d 100644 --- a/src/textual/widgets/_header.py +++ b/src/textual/widgets/_header.py @@ -34,7 +34,7 @@ class HeaderClock(Widget): width: 10; padding: 0 1; background: $secondary-background-lighten-1; - color: $text-secondary-background; + color: $text; text-opacity: 85%; content-align: center middle; } @@ -76,7 +76,7 @@ class Header(Widget): dock: top; width: 100%; background: $secondary-background; - color: $text-secondary-background; + color: $text; height: 1; } Header.tall { diff --git a/src/textual/widgets/_tree_control.py b/src/textual/widgets/_tree_control.py index c2b163d28..e7acede74 100644 --- a/src/textual/widgets/_tree_control.py +++ b/src/textual/widgets/_tree_control.py @@ -165,7 +165,7 @@ class TreeControl(Generic[NodeDataType], Static, can_focus=True): DEFAULT_CSS = """ TreeControl { background: $surface; - color: $text-surface; + color: $text; height: auto; width: 100%; } @@ -176,8 +176,7 @@ class TreeControl(Generic[NodeDataType], Static, can_focus=True): TreeControl > .tree--guides-highlight { color: $success; - text-style: uu; - + text-style: uu; } TreeControl > .tree--guides-cursor { @@ -186,12 +185,12 @@ class TreeControl(Generic[NodeDataType], Static, can_focus=True): } TreeControl > .tree--labels { - color: $text-panel; + color: $text; } TreeControl > .tree--cursor { background: $secondary; - color: $text-secondary; + color: $text; } """