From a3043346285f18baec1c1a8ce253ca0fadc5d924 Mon Sep 17 00:00:00 2001 From: Olivier Philippon Date: Thu, 28 Apr 2022 14:21:02 +0100 Subject: [PATCH] [colors] Add management of named Web colors to our CSS processing, prefix ANSI ones with `ansi_` --- src/textual/_color_constants.py | 151 ++++++++++++++++++++++++++++++++ src/textual/color.py | 13 ++- src/textual/css/styles.py | 6 +- src/textual/scrollbar.py | 6 +- tests/css/test_stylesheet.py | 18 ++-- 5 files changed, 178 insertions(+), 16 deletions(-) diff --git a/src/textual/_color_constants.py b/src/textual/_color_constants.py index 4b1a1e89b..dc12cf873 100644 --- a/src/textual/_color_constants.py +++ b/src/textual/_color_constants.py @@ -237,3 +237,154 @@ ANSI_COLOR_TO_RGB: dict[str, tuple[int, int, int]] = { "grey93": (238, 238, 238), "gray93": (238, 238, 238), } + +WEB_COLOR_TO_RGB: dict[str, tuple[int, int, int]] = { + "black": (0, 0, 0), + "silver": (192, 192, 192), + "gray": (128, 128, 128), + "white": (255, 255, 255), + "maroon": (128, 0, 0), + "red": (255, 0, 0), + "purple": (128, 0, 128), + "fuchsia": (255, 0, 255), + "green": (0, 128, 0), + "lime": (0, 255, 0), + "olive": (128, 128, 0), + "yellow": (255, 255, 0), + "navy": (0, 0, 128), + "blue": (0, 0, 255), + "teal": (0, 128, 128), + "aqua": (0, 255, 255), + "orange": (255, 165, 0), + "aliceblue": (240, 248, 255), + "antiquewhite": (250, 235, 215), + "aquamarine": (127, 255, 212), + "azure": (240, 255, 255), + "beige": (245, 245, 220), + "bisque": (255, 228, 196), + "blanchedalmond": (255, 235, 205), + "blueviolet": (138, 43, 226), + "brown": (165, 42, 42), + "burlywood": (222, 184, 135), + "cadetblue": (95, 158, 160), + "chartreuse": (127, 255, 0), + "chocolate": (210, 105, 30), + "coral": (255, 127, 80), + "cornflowerblue": (100, 149, 237), + "cornsilk": (255, 248, 220), + "crimson": (220, 20, 60), + "cyan": (0, 255, 255), + "darkblue": (0, 0, 139), + "darkcyan": (0, 139, 139), + "darkgoldenrod": (184, 134, 11), + "darkgray": (169, 169, 169), + "darkgreen": (0, 100, 0), + "darkgrey": (169, 169, 169), + "darkkhaki": (189, 183, 107), + "darkmagenta": (139, 0, 139), + "darkolivegreen": (85, 107, 47), + "darkorange": (255, 140, 0), + "darkorchid": (153, 50, 204), + "darkred": (139, 0, 0), + "darksalmon": (233, 150, 122), + "darkseagreen": (143, 188, 143), + "darkslateblue": (72, 61, 139), + "darkslategray": (47, 79, 79), + "darkslategrey": (47, 79, 79), + "darkturquoise": (0, 206, 209), + "darkviolet": (148, 0, 211), + "deeppink": (255, 20, 147), + "deepskyblue": (0, 191, 255), + "dimgray": (105, 105, 105), + "dimgrey": (105, 105, 105), + "dodgerblue": (30, 144, 255), + "firebrick": (178, 34, 34), + "floralwhite": (255, 250, 240), + "forestgreen": (34, 139, 34), + "gainsboro": (220, 220, 220), + "ghostwhite": (248, 248, 255), + "gold": (255, 215, 0), + "goldenrod": (218, 165, 32), + "greenyellow": (173, 255, 47), + "grey": (128, 128, 128), + "honeydew": (240, 255, 240), + "hotpink": (255, 105, 180), + "indianred": (205, 92, 92), + "indigo": (75, 0, 130), + "ivory": (255, 255, 240), + "khaki": (240, 230, 140), + "lavender": (230, 230, 250), + "lavenderblush": (255, 240, 245), + "lawngreen": (124, 252, 0), + "lemonchiffon": (255, 250, 205), + "lightblue": (173, 216, 230), + "lightcoral": (240, 128, 128), + "lightcyan": (224, 255, 255), + "lightgoldenrodyellow": (250, 250, 210), + "lightgray": (211, 211, 211), + "lightgreen": (144, 238, 144), + "lightgrey": (211, 211, 211), + "lightpink": (255, 182, 193), + "lightsalmon": (255, 160, 122), + "lightseagreen": (32, 178, 170), + "lightskyblue": (135, 206, 250), + "lightslategray": (119, 136, 153), + "lightslategrey": (119, 136, 153), + "lightsteelblue": (176, 196, 222), + "lightyellow": (255, 255, 224), + "limegreen": (50, 205, 50), + "linen": (250, 240, 230), + "magenta": (255, 0, 255), + "mediumaquamarine": (102, 205, 170), + "mediumblue": (0, 0, 205), + "mediumorchid": (186, 85, 211), + "mediumpurple": (147, 112, 219), + "mediumseagreen": (60, 179, 113), + "mediumslateblue": (123, 104, 238), + "mediumspringgreen": (0, 250, 154), + "mediumturquoise": (72, 209, 204), + "mediumvioletred": (199, 21, 133), + "midnightblue": (25, 25, 112), + "mintcream": (245, 255, 250), + "mistyrose": (255, 228, 225), + "moccasin": (255, 228, 181), + "navajowhite": (255, 222, 173), + "oldlace": (253, 245, 230), + "olivedrab": (107, 142, 35), + "orangered": (255, 69, 0), + "orchid": (218, 112, 214), + "palegoldenrod": (238, 232, 170), + "palegreen": (152, 251, 152), + "paleturquoise": (175, 238, 238), + "palevioletred": (219, 112, 147), + "papayawhip": (255, 239, 213), + "peachpuff": (255, 218, 185), + "peru": (205, 133, 63), + "pink": (255, 192, 203), + "plum": (221, 160, 221), + "powderblue": (176, 224, 230), + "rosybrown": (188, 143, 143), + "royalblue": (65, 105, 225), + "saddlebrown": (139, 69, 19), + "salmon": (250, 128, 114), + "sandybrown": (244, 164, 96), + "seagreen": (46, 139, 87), + "seashell": (255, 245, 238), + "sienna": (160, 82, 45), + "skyblue": (135, 206, 235), + "slateblue": (106, 90, 205), + "slategray": (112, 128, 144), + "slategrey": (112, 128, 144), + "snow": (255, 250, 250), + "springgreen": (0, 255, 127), + "steelblue": (70, 130, 180), + "tan": (210, 180, 140), + "thistle": (216, 191, 216), + "tomato": (255, 99, 71), + "turquoise": (64, 224, 208), + "violet": (238, 130, 238), + "wheat": (245, 222, 179), + "whitesmoke": (245, 245, 245), + "yellowgreen": (154, 205, 50), + "rebeccapurple": (102, 51, 153), +} diff --git a/src/textual/color.py b/src/textual/color.py index 22949cf8c..58d87a783 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -24,7 +24,7 @@ from rich.style import Style from rich.text import Text -from ._color_constants import ANSI_COLOR_TO_RGB +from ._color_constants import ANSI_COLOR_TO_RGB, WEB_COLOR_TO_RGB from .geometry import clamp @@ -253,9 +253,14 @@ class Color(NamedTuple): """ if isinstance(color_text, Color): return color_text - ansi_color = ANSI_COLOR_TO_RGB.get(color_text) - if ansi_color is not None: - return cls(*ansi_color) + if color_text.startswith("ansi_"): + ansi_color = ANSI_COLOR_TO_RGB.get(color_text[5:]) + if ansi_color is not None: + return cls(*ansi_color) + else: + web_color = WEB_COLOR_TO_RGB.get(color_text) + if web_color is not None: + return cls(*web_color) color_match = RE_COLOR.match(color_text) if color_match is None: raise ColorParseError(f"failed to parse {color_text!r} as a color") diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index d1ecbdd91..73da37f64 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -211,9 +211,9 @@ class StylesBase(ABC): rich_style = StyleProperty() - scrollbar_color = ColorProperty("bright_magenta") - scrollbar_color_hover = ColorProperty("yellow") - scrollbar_color_active = ColorProperty("bright_yellow") + scrollbar_color = ColorProperty("ansi_bright_magenta") + scrollbar_color_hover = ColorProperty("ansi_yellow") + scrollbar_color_active = ColorProperty("ansi_bright_yellow") scrollbar_background = ColorProperty("#555555") scrollbar_background_hover = ColorProperty("#444444") diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index 5633f0d0e..62f05185c 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -69,7 +69,7 @@ class ScrollBarRender: position: float = 0, thickness: int = 1, vertical: bool = True, - style: StyleType = "bright_magenta on #555555", + style: StyleType = "ansi_bright_magenta on #555555", ) -> None: self.virtual_size = virtual_size self.window_size = window_size @@ -89,7 +89,7 @@ class ScrollBarRender: thickness: int = 1, vertical: bool = True, back_color: Color = Color.parse("#555555"), - bar_color: Color = Color.parse("bright_magenta"), + bar_color: Color = Color.parse("ansi_bright_magenta"), ) -> Segments: if vertical: @@ -181,7 +181,7 @@ class ScrollBarRender: vertical=self.vertical, thickness=thickness, back_color=_style.bgcolor or Color.parse("#555555"), - bar_color=_style.color or Color.parse("bright_magenta"), + bar_color=_style.color or Color.parse("ansi_bright_magenta"), ) yield bar diff --git a/tests/css/test_stylesheet.py b/tests/css/test_stylesheet.py index 87f27845d..d1da10662 100644 --- a/tests/css/test_stylesheet.py +++ b/tests/css/test_stylesheet.py @@ -10,18 +10,24 @@ from textual.css.tokenizer import TokenizeError "css_value,expectation,expected_color", [ # Valid values: - ["red", does_not_raise(), Color(128, 0, 0)], - ["dark_cyan", does_not_raise(), Color(0, 175, 135)], - ["medium_turquoise", does_not_raise(), Color(95, 215, 215)], - ["turquoise4", does_not_raise(), Color(0, 135, 135)], + ["ansi_red", does_not_raise(), Color(128, 0, 0)], + ["ansi_dark_cyan", does_not_raise(), Color(0, 175, 135)], + ["ansi_medium_turquoise", does_not_raise(), Color(95, 215, 215)], + ["ansi_turquoise4", does_not_raise(), Color(0, 135, 135)], + ["red", does_not_raise(), Color(255, 0, 0)], + ["lime", does_not_raise(), Color(0, 255, 0)], + ["coral", does_not_raise(), Color(255, 127, 80)], + ["aqua", does_not_raise(), Color(0, 255, 255)], + ["deepskyblue", does_not_raise(), Color(0, 191, 255)], + ["rebeccapurple", does_not_raise(), Color(102, 51, 153)], ["#ffcc00", does_not_raise(), Color(255, 204, 0)], ["#ffcc0033", does_not_raise(), Color(255, 204, 0, 0.2)], ["rgb(200,90,30)", does_not_raise(), Color(200, 90, 30)], ["rgba(200,90,30,0.3)", does_not_raise(), Color(200, 90, 30, 0.3)], # Some invalid ones: ["coffee", pytest.raises(StylesheetParseError), None], # invalid color name - ["turquoise10", pytest.raises(StylesheetParseError), None], - ["turquoise 4", pytest.raises(StylesheetParseError), None], # space in it + ["ansi_turquoise10", pytest.raises(StylesheetParseError), None], + ["ansi_turquoise 4", pytest.raises(StylesheetParseError), None], # space in it ["1", pytest.raises(StylesheetParseError), None], # invalid value ["()", pytest.raises(TokenizeError), None], # invalid tokens # TODO: implement hex colors with 3 chars? @link https://devdocs.io/css/color_value