From 028853c9a39b8e73fa356dd562b2109a5cb5087f Mon Sep 17 00:00:00 2001 From: Rohitth Date: Wed, 9 Jun 2021 11:08:17 +0530 Subject: [PATCH 01/12] color: Add helper function to new file to add color properties. A helper function `color_properties` was used to make it easy to add color definitions with style properties. It was added into config/color.py to avoid circular imports in `themes.py`. Tests added. --- tests/config/test_color.py | 15 +++++++++++++++ zulipterminal/config/color.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/config/test_color.py create mode 100644 zulipterminal/config/color.py diff --git a/tests/config/test_color.py b/tests/config/test_color.py new file mode 100644 index 0000000000..54283cec18 --- /dev/null +++ b/tests/config/test_color.py @@ -0,0 +1,15 @@ +from enum import Enum + +from zulipterminal.config.color import color_properties + + +def test_color_properties(): + class Color(Enum): + WHITE = "wh #256 #24" + + Color = color_properties(Color, "BOLD", "ITALICS") + + assert Color.WHITE in Color + assert Color.WHITE.value == "wh #256 #24" + assert Color.WHITE__BOLD_ITALICS in Color + assert Color.WHITE__BOLD_ITALICS.value == "wh #256 #24 , bold , italics" diff --git a/zulipterminal/config/color.py b/zulipterminal/config/color.py new file mode 100644 index 0000000000..7249e85c8a --- /dev/null +++ b/zulipterminal/config/color.py @@ -0,0 +1,33 @@ +""" +COLOR +----- +Contains color definitions or functions common across all themes. +For further details on themefiles look at the theme contribution guide. +""" +from enum import Enum +from typing import Any + + +def color_properties(colors: Any, *prop: str) -> Any: + """ + Adds properties(Bold, Italics, etc...) to Enum Colors in theme files. + Useage: color_properties(Color, 'BOLD', 'ITALICS', 'STRIKETHROUGH') + + NOTE: color_properties(Color, BOLD, ITALICS) would result in only + Color.WHITE and Color.WHITE__BOLD_ITALICS + but not Color.WHITE__BOLD or Color.WHITE__ITALICS. + One would also have to do color_properties(Color, BOLD) + and color_properties(Color, ITALICS) for the others to work + >>> This function can be later extended to achieve all combinations + with one call to the function. + """ + prop_n = "_".join([p.upper() for p in prop]) + prop_v = " , ".join([p.lower() for p in prop]) + updated_colors: Any = Enum( # type: ignore # Ref: python/mypy#529, #535 and #5317 + "Color", + { + **{c.name: c.value for c in colors}, + **{c.name + f"__{prop_n}": c.value + f" , {prop_v}" for c in colors}, + }, + ) + return updated_colors From d31e7ca728f18da77f8b832ec462752bef5b1692 Mon Sep 17 00:00:00 2001 From: Rohitth Date: Sun, 27 Jun 2021 20:14:28 +0530 Subject: [PATCH 02/12] color: Add default color-scheme with bold, based on existing colors. A DefaultColor class is added to color.py which acts as the default color-scheme for ZT. The BOLD property is added to these colors using the helper function color_properties. The format: * The colors have been defined using Enum's as a single string containing 16, 256 and 24 bit codes. * Each style is defined as a tuple of 2 elements, the foreground and the background. The 24 bit colors are not defined for the default colors hence default is used. 2 missing colors in DEF, DARK_MAGENTA and LIGHT_CYAN, which were used in zt_blue were added into Color. --- zulipterminal/config/color.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/zulipterminal/config/color.py b/zulipterminal/config/color.py index 7249e85c8a..ef3132613b 100644 --- a/zulipterminal/config/color.py +++ b/zulipterminal/config/color.py @@ -8,6 +8,33 @@ from typing import Any +# fmt: off +# NOTE: The 24bit color codes use 256 color which can be +# enhanced to be truly 24bit. +# NOTE: The 256code format can be moved to h0-255 to +# make use of the complete range instead of only 216 colors. +class DefaultColor(Enum): + # color = 16code 256code 24code + DEFAULT = 'default default default' + BLACK = 'black g19 g19' + DARK_RED = 'dark_red #a00 #a00' + DARK_GREEN = 'dark_green #080 #080' + BROWN = 'brown #880 #880' + DARK_BLUE = 'dark_blue #24a #24a' + DARK_MAGENTA = 'dark_magenta h90 h90' # #870087 + DARK_CYAN = 'dark_cyan #088 #088' + DARK_GRAY = 'dark_gray #666 #666' + LIGHT_RED = 'light_red #f00 #f00' + LIGHT_GREEN = 'light_green #0f0 #0f0' + YELLOW = 'yellow #ff0 #ff0' + LIGHT_BLUE = 'light_blue #28d #28d' + LIGHT_MAGENTA = 'light_magenta #c8f #c8f' + LIGHT_CYAN = 'light_cyan h152 h152' # #afd7d7 + LIGHT_GRAY = 'light_gray #ccc #ccc' + WHITE = 'white #fff #fff' +# fmt: on + + def color_properties(colors: Any, *prop: str) -> Any: """ Adds properties(Bold, Italics, etc...) to Enum Colors in theme files. @@ -31,3 +58,6 @@ def color_properties(colors: Any, *prop: str) -> Any: }, ) return updated_colors + + +DefaultBoldColor = color_properties(DefaultColor, "BOLD") From 923f2eb472a983f7ece8882ffbd915c5e9267dec Mon Sep 17 00:00:00 2001 From: Rohitth Date: Sat, 12 Jun 2021 18:01:24 +0530 Subject: [PATCH 03/12] themes: Rename required_styles to REQUIRED_STYLES for consistency. The `required_styles` dict is renamed to REQUIRED_STYLES to maintain similarity with STYLE dict in incoming themefiles. Tests updated. --- tests/config/test_themes.py | 8 +-- zulipterminal/config/themes.py | 115 +++++++++++++++++---------------- 2 files changed, 62 insertions(+), 61 deletions(-) diff --git a/tests/config/test_themes.py b/tests/config/test_themes.py index 8053d1b535..587bd6e661 100644 --- a/tests/config/test_themes.py +++ b/tests/config/test_themes.py @@ -1,10 +1,10 @@ import pytest from zulipterminal.config.themes import ( + REQUIRED_STYLES, THEMES, all_themes, complete_and_incomplete_themes, - required_styles, theme_with_monochrome_added, ) @@ -36,8 +36,8 @@ def test_builtin_theme_completeness(theme_name): theme = THEMES[theme_name] styles_in_theme = {style[0] for style in theme} - assert len(styles_in_theme) == len(required_styles) - assert all(required_style in styles_in_theme for required_style in required_styles) + assert len(styles_in_theme) == len(REQUIRED_STYLES) + assert all(required_style in styles_in_theme for required_style in REQUIRED_STYLES) def test_complete_and_incomplete_themes(): @@ -71,5 +71,5 @@ def test_complete_and_incomplete_themes(): ], ) def test_theme_with_monochrome_added(mocker, theme, expected_new_theme, req_styles): - mocker.patch.dict("zulipterminal.config.themes.required_styles", req_styles) + mocker.patch.dict("zulipterminal.config.themes.REQUIRED_STYLES", req_styles) assert theme_with_monochrome_added(theme) == expected_new_theme diff --git a/zulipterminal/config/themes.py b/zulipterminal/config/themes.py index c8200877a6..b46d72bbb9 100644 --- a/zulipterminal/config/themes.py +++ b/zulipterminal/config/themes.py @@ -3,63 +3,64 @@ ThemeSpec = List[Tuple[Optional[str], ...]] -# The keys in required_styles specify what styles are necessary for a theme to +# fmt: off +# The keys in REQUIRED_STYLES specify what styles are necessary for a theme to # be complete, while the values are those used to style each element in # monochrome (1-bit) mode - independently of the specified theme - -required_styles = { # style-name: monochrome-bit-depth-style - None: "", - "selected": "standout", - "msg_selected": "standout", - "header": "bold", - "general_narrow": "standout", - "general_bar": "", - "name": "", - "unread": "strikethrough", - "user_active": "bold", - "user_idle": "", - "user_offline": "", - "user_inactive": "", - "title": "bold", - "column_title": "bold", - "time": "", - "bar": "standout", - "popup_contrast": "standout", - "msg_emoji": "bold", - "reaction": "bold", - "reaction_mine": "standout", - "msg_mention": "bold", - "msg_link": "", - "msg_link_index": "bold", - "msg_quote": "underline", - "msg_code": "bold", - "msg_bold": "bold", - "msg_time": "bold", - "footer": "standout", - "footer_contrast": "standout", - "starred": "bold", - "popup_category": "bold", - "unread_count": "bold", - "starred_count": "", - "table_head": "bold", - "filter_results": "bold", - "edit_topic": "standout", - "edit_tag": "standout", - "edit_author": "bold", - "edit_time": "bold", - "current_user": "", - "muted": "bold", - "popup_border": "bold", - "area:help": "standout", - "area:msg": "standout", - "area:stream": "standout", - "area:error": "standout", - "search_error": "standout", - "task:success": "standout", - "task:error": "standout", - "task:warning": "standout", +REQUIRED_STYLES = { + # style name : monochrome style + None : '', + 'selected' : 'standout', + 'msg_selected' : 'standout', + 'header' : 'bold', + 'general_narrow' : 'standout', + 'general_bar' : '', + 'name' : '', + 'unread' : 'strikethrough', + 'user_active' : 'bold', + 'user_idle' : '', + 'user_offline' : '', + 'user_inactive' : '', + 'title' : 'bold', + 'column_title' : 'bold', + 'time' : '', + 'bar' : 'standout', + 'popup_contrast' : 'standout', + 'msg_emoji' : 'bold', + 'reaction' : 'bold', + 'reaction_mine' : 'standout', + 'msg_mention' : 'bold', + 'msg_link' : '', + 'msg_link_index' : 'bold', + 'msg_quote' : 'underline', + 'msg_code' : 'bold', + 'msg_bold' : 'bold', + 'msg_time' : 'bold', + 'footer' : 'standout', + 'footer_contrast' : 'standout', + 'starred' : 'bold', + 'popup_category' : 'bold', + 'unread_count' : 'bold', + 'starred_count' : '', + 'table_head' : 'bold', + 'filter_results' : 'bold', + 'edit_topic' : 'standout', + 'edit_tag' : 'standout', + 'edit_author' : 'bold', + 'edit_time' : 'bold', + 'current_user' : '', + 'muted' : 'bold', + 'popup_border' : 'bold', + 'area:help' : 'standout', + 'area:msg' : 'standout', + 'area:stream' : 'standout', + 'area:error' : 'standout', + 'search_error' : 'standout', + 'task:success' : 'standout', + 'task:error' : 'standout', + 'task:warning' : 'standout', } - +# fmt: on # 256-color base names for default theme (bold values added below) DEF_base = dict( @@ -563,7 +564,7 @@ def complete_and_incomplete_themes() -> Tuple[List[str], List[str]]: complete = { name for name, styles in THEMES.items() - if {s[0] for s in styles} == set(required_styles) + if {s[0] for s in styles} == set(REQUIRED_STYLES) } incomplete = list(set(THEMES) - complete) return sorted(list(complete)), sorted(incomplete) @@ -573,9 +574,9 @@ def theme_with_monochrome_added(theme: ThemeSpec) -> ThemeSpec: updated_theme = [] for style in theme: style_name = style[0] - if style_name not in required_styles: # incomplete theme + if style_name not in REQUIRED_STYLES: # incomplete theme continue - mono_style = required_styles[style_name] + mono_style = REQUIRED_STYLES[style_name] if len(style) > 4: # 256 colors+ new_style = style[:3] + (mono_style,) + style[4:] elif len(style) == 4: # 16 colors + mono (overwrite mono) From 91af6d99dd56182ac573dc28c1fad5abcca8353a Mon Sep 17 00:00:00 2001 From: Rohitth Date: Mon, 21 Jun 2021 20:43:09 +0530 Subject: [PATCH 04/12] THEME_CONTRIBUTING: Add documentation for creating/adapting themes. This commit creates a Theme Contribution Guide to help with understanding the format in Themefiles and creating new themes. --- zulipterminal/themes/THEME_CONTRIBUTING.md | 100 +++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 zulipterminal/themes/THEME_CONTRIBUTING.md diff --git a/zulipterminal/themes/THEME_CONTRIBUTING.md b/zulipterminal/themes/THEME_CONTRIBUTING.md new file mode 100644 index 0000000000..553594fd14 --- /dev/null +++ b/zulipterminal/themes/THEME_CONTRIBUTING.md @@ -0,0 +1,100 @@ + +# ZT : THEME CONTRIBUTION GUIDE + +This document explains how Zulip Terminal manages themes. + +ZT uses theme-files to define a particular theme which can be found in the `zulipterminal/themes` folder. + +> **TIP**: For a theme to be accepted, it has to pass certain tests. The easiest way to make sure you have all the styles, format and spacing correct is to copy-paste an existing theme and then edit them but to understand more, read on! + +## COLOR SCHEME + +> Zulip creates some default color schemes for use in themes which can be found in `color.py`. These colors also make use of the BOLD property, more on which in the next section. Make sure if any of the existing color schemes from any of the existing themes can be useful. + +To use the default bold color scheme in a theme, paste this line at the top of the file: + +```python +from zulipterminal.config.color import DefaultBoldColor as Color +``` + +If BOLD is something that is not needed, just use: + +```python +from zulipterminal.config.color import DefaultColor as Color +``` + +Likewise, to use a color scheme from any other theme like Gruvbox, use: + +```python +from zulipterminal.themes.gruvbox import Color +``` + +--- + +If any of these are not enough for the theme that you want to create, the following format would give an idea of how to create a new color scheme ( Refer: `gruvbox.py` ). + +```python +class ThemeColor(Enum): + WHITE = '16code 256code 24code' + ... +``` + +We use **Enum** to define color constants. +Each color constant is a string containing 3 space separated +color codes: + +* **16code**: Terminal's default 16 color aliases. + * Only those used in DefaultColor are acceptable. + * Eg: `black, light_cyan, etc` +* **256code**: High color, 256 color code. + * Acceptable formats: `#000-#fff`, `h0-h255`, `g0-g100`, `g#00-g#ff` + * Eg: `#rgb or h255 or g19` +* **24code**: True color, 24 bit color code. + * Similar to HTML colors. + * Eg: `#rrggbb` + +`'default'` is a special alias which uses the default +foreground or background. + +### COLOR PROPERTIES + +The `color_properties()` function adds special properties like +Bold, Italics, Strikethrough, etc to the color scheme. + +```python +Color = color_properties(ThemeColor, 'BOLD', 'ITALICS', 'STRIKETHROUGH') +``` + +**Note**: It is advisable to only use BOLD as of now, similar to `DefaultBoldColor`, to support as many terminals as possible. + +## THEME STYLE + +To use these colors in a theme style use the dot notation: + +```python +(Color.BLACK, Color.DARK_CYAN) +``` + +Everything after a Dunder or Double Underscore +are considered as properties. + +```python +(Color.YELLOW__BOLD, Color.LIGHT_CYAN__BOLD_ITALICS) +``` + +The actual styles are defined in a dictionary called STYLES +which takes the style as the key and a tuple with foreground +and background as the value. + +```python +STYLES = { + # style_name : foreground background + 'selected' : (Color.BLACK, Color.LIGHT_GREEN) + ... +} +``` + +The REQUIRED_STYLES dictionary in `config/themes.py` shows all the required styles needed for a theme to be considered complete and pass tests but as mentioned above it is best to copy-paste an existing theme +and then edit it. + +If there are any issues, feel free to bring it up on [#zulip-terminal](https://chat.zulip.org/#narrow/stream/206-zulip-terminal) From 1873e0ef196bb6759617de6f7c466655757f74a2 Mon Sep 17 00:00:00 2001 From: Rohitth Date: Fri, 11 Jun 2021 19:59:05 +0530 Subject: [PATCH 05/12] zt_dark: Create new zt_dark theme structure. New zt_dark theme structure was created using a script and some manual editing. `zt_dark.py` can be found in the new themes folder. --- zulipterminal/themes/zt_dark.py | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 zulipterminal/themes/zt_dark.py diff --git a/zulipterminal/themes/zt_dark.py b/zulipterminal/themes/zt_dark.py new file mode 100644 index 0000000000..60097b09dc --- /dev/null +++ b/zulipterminal/themes/zt_dark.py @@ -0,0 +1,64 @@ +""" +ZT DARK +------- +This theme uses the default color scheme. +For further details on themefiles look at the theme contribution guide. +""" +from zulipterminal.config.color import DefaultBoldColor as Color + + +# fmt: off +STYLES = { + # style_name : foreground background + None : (Color.WHITE, Color.BLACK), + 'selected' : (Color.WHITE, Color.DARK_BLUE), + 'msg_selected' : (Color.WHITE, Color.DARK_BLUE), + 'header' : (Color.DARK_CYAN, Color.DARK_BLUE), + 'general_narrow' : (Color.WHITE, Color.DARK_BLUE), + 'general_bar' : (Color.WHITE, Color.BLACK), + 'name' : (Color.YELLOW__BOLD, Color.BLACK), + 'unread' : (Color.DARK_BLUE, Color.BLACK), + 'user_active' : (Color.LIGHT_GREEN, Color.BLACK), + 'user_idle' : (Color.YELLOW, Color.BLACK), + 'user_offline' : (Color.WHITE, Color.BLACK), + 'user_inactive' : (Color.WHITE, Color.BLACK), + 'title' : (Color.WHITE__BOLD, Color.BLACK), + 'column_title' : (Color.WHITE__BOLD, Color.BLACK), + 'time' : (Color.LIGHT_BLUE, Color.BLACK), + 'bar' : (Color.WHITE, Color.DARK_GRAY), + 'popup_contrast' : (Color.WHITE, Color.DARK_GRAY), + 'msg_emoji' : (Color.LIGHT_MAGENTA, Color.BLACK), + 'reaction' : (Color.LIGHT_MAGENTA__BOLD, Color.BLACK), + 'reaction_mine' : (Color.BLACK, Color.LIGHT_MAGENTA), + 'msg_mention' : (Color.LIGHT_RED__BOLD, Color.BLACK), + 'msg_link' : (Color.LIGHT_BLUE, Color.BLACK), + 'msg_link_index' : (Color.LIGHT_BLUE__BOLD, Color.BLACK), + 'msg_quote' : (Color.BROWN, Color.BLACK), + 'msg_code' : (Color.BLACK, Color.WHITE), + 'msg_bold' : (Color.WHITE__BOLD, Color.BLACK), + 'msg_time' : (Color.BLACK, Color.WHITE), + 'footer' : (Color.BLACK, Color.LIGHT_GRAY), + 'footer_contrast' : (Color.WHITE, Color.BLACK), + 'starred' : (Color.LIGHT_RED__BOLD, Color.BLACK), + 'popup_category' : (Color.LIGHT_BLUE__BOLD, Color.BLACK), + 'unread_count' : (Color.YELLOW, Color.BLACK), + 'starred_count' : (Color.LIGHT_GRAY, Color.BLACK), + 'table_head' : (Color.WHITE__BOLD, Color.BLACK), + 'filter_results' : (Color.WHITE, Color.DARK_GREEN), + 'edit_topic' : (Color.WHITE, Color.DARK_GRAY), + 'edit_tag' : (Color.WHITE, Color.DARK_GRAY), + 'edit_author' : (Color.YELLOW, Color.BLACK), + 'edit_time' : (Color.LIGHT_BLUE, Color.BLACK), + 'current_user' : (Color.WHITE, Color.BLACK), + 'muted' : (Color.LIGHT_BLUE, Color.BLACK), + 'popup_border' : (Color.WHITE, Color.BLACK), + 'area:help' : (Color.WHITE, Color.DARK_GREEN), + 'area:msg' : (Color.WHITE, Color.BROWN), + 'area:stream' : (Color.WHITE, Color.DARK_CYAN), + 'area:error' : (Color.WHITE, Color.DARK_RED), + 'search_error' : (Color.LIGHT_RED, Color.BLACK), + 'task:success' : (Color.WHITE, Color.DARK_GREEN), + 'task:error' : (Color.WHITE, Color.DARK_RED), + 'task:warning' : (Color.WHITE, Color.BROWN), +} +# fmt: on From 8b15e5fd3d81e003a68e7689cba0a3b70a7b5f37 Mon Sep 17 00:00:00 2001 From: Rohitth Date: Fri, 11 Jun 2021 20:11:15 +0530 Subject: [PATCH 06/12] zt_light: Create new zt_light theme structure. New zt_light theme structure was created using a script and some manual editing. `zt_light.py` can be found in the themes folder. --- zulipterminal/themes/zt_light.py | 64 ++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 zulipterminal/themes/zt_light.py diff --git a/zulipterminal/themes/zt_light.py b/zulipterminal/themes/zt_light.py new file mode 100644 index 0000000000..736043de7b --- /dev/null +++ b/zulipterminal/themes/zt_light.py @@ -0,0 +1,64 @@ +""" +ZT LIGHT +-------- +This theme uses the default color scheme. +For further details on themefiles look at the theme contribution guide. +""" +from zulipterminal.config.color import DefaultBoldColor as Color + + +# fmt: off +STYLES = { + # style_name : foreground background + None : (Color.BLACK, Color.WHITE), + 'selected' : (Color.BLACK, Color.LIGHT_GREEN), + 'msg_selected' : (Color.BLACK, Color.LIGHT_GREEN), + 'header' : (Color.WHITE, Color.DARK_BLUE), + 'general_narrow' : (Color.WHITE, Color.DARK_BLUE), + 'general_bar' : (Color.DARK_BLUE, Color.WHITE), + 'name' : (Color.DARK_GREEN, Color.WHITE), + 'unread' : (Color.DARK_GRAY, Color.LIGHT_GRAY), + 'user_active' : (Color.DARK_GREEN, Color.WHITE), + 'user_idle' : (Color.DARK_BLUE, Color.WHITE), + 'user_offline' : (Color.BLACK, Color.WHITE), + 'user_inactive' : (Color.BLACK, Color.WHITE), + 'title' : (Color.WHITE__BOLD, Color.DARK_GRAY), + 'column_title' : (Color.BLACK__BOLD, Color.WHITE), + 'time' : (Color.DARK_BLUE, Color.WHITE), + 'bar' : (Color.WHITE, Color.DARK_GRAY), + 'popup_contrast' : (Color.WHITE, Color.DARK_GRAY), + 'msg_emoji' : (Color.LIGHT_MAGENTA, Color.WHITE), + 'reaction' : (Color.LIGHT_MAGENTA__BOLD, Color.WHITE), + 'reaction_mine' : (Color.WHITE, Color.LIGHT_MAGENTA), + 'msg_mention' : (Color.LIGHT_RED__BOLD, Color.WHITE), + 'msg_link' : (Color.DARK_BLUE, Color.WHITE), + 'msg_link_index' : (Color.DARK_BLUE__BOLD, Color.WHITE), + 'msg_quote' : (Color.BLACK, Color.BROWN), + 'msg_code' : (Color.BLACK, Color.LIGHT_GRAY), + 'msg_bold' : (Color.WHITE__BOLD, Color.DARK_GRAY), + 'msg_time' : (Color.WHITE, Color.DARK_GRAY), + 'footer' : (Color.WHITE, Color.DARK_GRAY), + 'footer_contrast' : (Color.BLACK, Color.WHITE), + 'starred' : (Color.LIGHT_RED__BOLD, Color.WHITE), + 'popup_category' : (Color.DARK_GRAY__BOLD, Color.LIGHT_GRAY), + 'unread_count' : (Color.DARK_BLUE__BOLD, Color.WHITE), + 'starred_count' : (Color.BLACK, Color.WHITE), + 'table_head' : (Color.BLACK__BOLD, Color.WHITE), + 'filter_results' : (Color.WHITE, Color.DARK_GREEN), + 'edit_topic' : (Color.WHITE, Color.DARK_GRAY), + 'edit_tag' : (Color.WHITE, Color.DARK_GRAY), + 'edit_author' : (Color.DARK_GREEN, Color.WHITE), + 'edit_time' : (Color.DARK_BLUE, Color.WHITE), + 'current_user' : (Color.DARK_GRAY, Color.WHITE), + 'muted' : (Color.DARK_GRAY, Color.WHITE), + 'popup_border' : (Color.BLACK, Color.WHITE), + 'area:help' : (Color.BLACK, Color.LIGHT_GREEN), + 'area:stream' : (Color.BLACK, Color.LIGHT_BLUE), + 'area:msg' : (Color.BLACK, Color.YELLOW), + 'area:error' : (Color.BLACK, Color.LIGHT_RED), + 'search_error' : (Color.LIGHT_RED, Color.WHITE), + 'task:success' : (Color.BLACK, Color.DARK_GREEN), + 'task:error' : (Color.WHITE, Color.DARK_RED), + 'task:warning' : (Color.BLACK, Color.YELLOW), +} +# fmt: on From c71c172f0f9a5db1b573c9632b35e18aace2ae75 Mon Sep 17 00:00:00 2001 From: Rohitth Date: Fri, 11 Jun 2021 20:16:38 +0530 Subject: [PATCH 07/12] zt_blue: Create new zt_blue theme structure. New zt_blue theme structure was created using a script and some manual editing. `zt_blue.py` can be found in the themes folder. --- zulipterminal/themes/zt_blue.py | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 zulipterminal/themes/zt_blue.py diff --git a/zulipterminal/themes/zt_blue.py b/zulipterminal/themes/zt_blue.py new file mode 100644 index 0000000000..cec70635c5 --- /dev/null +++ b/zulipterminal/themes/zt_blue.py @@ -0,0 +1,64 @@ +""" +ZT BLUE +------- +This theme uses the default color scheme. +For further details on themefiles look at the theme contribution guide +""" +from zulipterminal.config.color import DefaultBoldColor as Color + + +# fmt: off +STYLES = { + # style_name : foreground background + None : (Color.BLACK, Color.LIGHT_BLUE), + 'selected' : (Color.BLACK, Color.LIGHT_GRAY), + 'msg_selected' : (Color.BLACK, Color.LIGHT_GRAY), + 'header' : (Color.BLACK, Color.DARK_BLUE), + 'general_narrow' : (Color.WHITE, Color.DARK_BLUE), + 'general_bar' : (Color.DARK_BLUE, Color.LIGHT_BLUE), + 'name' : (Color.DARK_RED, Color.LIGHT_BLUE), + 'unread' : (Color.LIGHT_GRAY, Color.LIGHT_BLUE), + 'user_active' : (Color.LIGHT_GREEN__BOLD, Color.LIGHT_BLUE), + 'user_idle' : (Color.DARK_GRAY, Color.LIGHT_BLUE), + 'user_offline' : (Color.BLACK, Color.LIGHT_BLUE), + 'user_inactive' : (Color.BLACK, Color.LIGHT_BLUE), + 'title' : (Color.WHITE__BOLD, Color.DARK_BLUE), + 'column_title' : (Color.BLACK__BOLD, Color.LIGHT_BLUE), + 'time' : (Color.DARK_BLUE, Color.LIGHT_BLUE), + 'bar' : (Color.WHITE, Color.DARK_BLUE), + 'popup_contrast' : (Color.WHITE, Color.DARK_BLUE), + 'msg_emoji' : (Color.DARK_MAGENTA, Color.LIGHT_BLUE), + 'reaction' : (Color.DARK_MAGENTA__BOLD, Color.LIGHT_BLUE), + 'reaction_mine' : (Color.LIGHT_BLUE, Color.DARK_MAGENTA), + 'msg_mention' : (Color.LIGHT_RED__BOLD, Color.LIGHT_BLUE), + 'msg_link' : (Color.DARK_BLUE, Color.LIGHT_GRAY), + 'msg_link_index' : (Color.DARK_BLUE__BOLD, Color.LIGHT_GRAY), + 'msg_quote' : (Color.BROWN, Color.DARK_BLUE), + 'msg_code' : (Color.DARK_BLUE, Color.WHITE), + 'msg_bold' : (Color.WHITE__BOLD, Color.DARK_BLUE), + 'msg_time' : (Color.DARK_BLUE, Color.WHITE), + 'footer' : (Color.WHITE, Color.DARK_GRAY), + 'footer_contrast' : (Color.BLACK, Color.WHITE), + 'starred' : (Color.LIGHT_RED__BOLD, Color.LIGHT_BLUE), + 'popup_category' : (Color.LIGHT_GRAY__BOLD, Color.LIGHT_BLUE), + 'unread_count' : (Color.YELLOW, Color.LIGHT_BLUE), + 'starred_count' : (Color.BLACK, Color.LIGHT_BLUE), + 'table_head' : (Color.BLACK__BOLD, Color.LIGHT_BLUE), + 'filter_results' : (Color.WHITE, Color.DARK_GREEN), + 'edit_topic' : (Color.WHITE, Color.DARK_BLUE), + 'edit_tag' : (Color.WHITE, Color.DARK_BLUE), + 'edit_author' : (Color.DARK_GRAY, Color.LIGHT_BLUE), + 'edit_time' : (Color.DARK_BLUE, Color.LIGHT_BLUE), + 'current_user' : (Color.LIGHT_GRAY, Color.LIGHT_BLUE), + 'muted' : (Color.LIGHT_GRAY, Color.LIGHT_BLUE), + 'popup_border' : (Color.WHITE, Color.LIGHT_BLUE), + 'area:help' : (Color.WHITE, Color.DARK_GREEN), + 'area:stream' : (Color.WHITE, Color.DARK_CYAN), + 'area:msg' : (Color.WHITE, Color.BROWN), + 'area:error' : (Color.WHITE, Color.DARK_RED), + 'search_error' : (Color.LIGHT_RED, Color.LIGHT_BLUE), + 'task:success' : (Color.WHITE, Color.DARK_GREEN), + 'task:error' : (Color.WHITE, Color.DARK_RED), + 'task:warning' : (Color.WHITE, Color.BROWN), +} +# fmt: on From d1ffd6392e4ff876e6f329fdc39f36e5cfb6d83a Mon Sep 17 00:00:00 2001 From: Rohitth Date: Fri, 11 Jun 2021 17:52:33 +0530 Subject: [PATCH 08/12] gruvbox: Create new gruvbox theme structure. New gruvbox theme structure was created using a script and some manual editing. `gruvbox.py` can be found in the themes folder. The names used for the color scheme is from the official gruvbox theme. --- zulipterminal/themes/gruvbox.py | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 zulipterminal/themes/gruvbox.py diff --git a/zulipterminal/themes/gruvbox.py b/zulipterminal/themes/gruvbox.py new file mode 100644 index 0000000000..b30f602457 --- /dev/null +++ b/zulipterminal/themes/gruvbox.py @@ -0,0 +1,90 @@ +""" +GRUVBOX +------- +This theme uses the official gruvbox color scheme. +For color reference see: + https://github.com/morhetz/gruvbox/blob/master/colors/gruvbox.vim + +For further details on themefiles look at the theme contribution guide +""" +from enum import Enum + +from zulipterminal.config.color import color_properties + + +# fmt: off +class GruvBoxColor(Enum): + # color = 16code 256code 24code + DEFAULT = 'default default default' + DARK0_HARD = 'black h234 #1d2021' + GRAY_244 = 'dark_gray h244 #928374' + LIGHT2 = 'white h250 #d5c4a1' + LIGHT4 = 'light_gray h248 #bdae93' + BRIGHT_BLUE = 'light_blue h109 #83a598' + BRIGHT_GREEN = 'light_green h142 #b8bb26' + BRIGHT_RED = 'light_red h167 #fb4934' + NEUTRAL_PURPLE = 'light_magenta h132 #b16286' + NEUTRAL_BLUE = 'dark_cyan h66 #458588' + NEUTRAL_YELLOW = 'yellow h172 #d79921' + FADED_BLUE = 'dark_blue h24 #076678' + FADED_YELLOW = 'brown h136 #b57614' + FADED_RED = 'dark_red h88 #9d0006' + + +Color = color_properties(GruvBoxColor, 'BOLD') + + +STYLES = { + # style_name : foreground background + None : (Color.LIGHT2, Color.DARK0_HARD), + 'selected' : (Color.DARK0_HARD, Color.LIGHT2), + 'msg_selected' : (Color.DARK0_HARD, Color.LIGHT2), + 'header' : (Color.NEUTRAL_BLUE, Color.FADED_BLUE), + 'general_narrow' : (Color.LIGHT2, Color.FADED_BLUE), + 'general_bar' : (Color.LIGHT2, Color.DARK0_HARD), + 'name' : (Color.NEUTRAL_YELLOW__BOLD, Color.DARK0_HARD), + 'unread' : (Color.NEUTRAL_PURPLE, Color.DARK0_HARD), + 'user_active' : (Color.BRIGHT_GREEN, Color.DARK0_HARD), + 'user_idle' : (Color.NEUTRAL_YELLOW, Color.DARK0_HARD), + 'user_offline' : (Color.LIGHT2, Color.DARK0_HARD), + 'user_inactive' : (Color.LIGHT2, Color.DARK0_HARD), + 'title' : (Color.LIGHT2__BOLD, Color.DARK0_HARD), + 'column_title' : (Color.LIGHT2__BOLD, Color.DARK0_HARD), + 'time' : (Color.BRIGHT_BLUE, Color.DARK0_HARD), + 'bar' : (Color.LIGHT2, Color.GRAY_244), + 'popup_contrast' : (Color.DARK0_HARD, Color.GRAY_244), + 'msg_emoji' : (Color.NEUTRAL_PURPLE, Color.DARK0_HARD), + 'reaction' : (Color.NEUTRAL_PURPLE__BOLD, Color.DARK0_HARD), + 'reaction_mine' : (Color.DARK0_HARD, Color.NEUTRAL_PURPLE), + 'msg_mention' : (Color.BRIGHT_RED__BOLD, Color.DARK0_HARD), + 'msg_link' : (Color.BRIGHT_BLUE, Color.DARK0_HARD), + 'msg_link_index' : (Color.BRIGHT_BLUE__BOLD, Color.DARK0_HARD), + 'msg_quote' : (Color.FADED_YELLOW, Color.DARK0_HARD), + 'msg_code' : (Color.DARK0_HARD, Color.LIGHT2), + 'msg_bold' : (Color.LIGHT2__BOLD, Color.DARK0_HARD), + 'msg_time' : (Color.DARK0_HARD, Color.LIGHT2), + 'footer' : (Color.DARK0_HARD, Color.LIGHT4), + 'footer_contrast' : (Color.LIGHT2, Color.DARK0_HARD), + 'starred' : (Color.BRIGHT_RED__BOLD, Color.DARK0_HARD), + 'popup_category' : (Color.BRIGHT_BLUE__BOLD, Color.DARK0_HARD), + 'unread_count' : (Color.NEUTRAL_YELLOW, Color.DARK0_HARD), + 'starred_count' : (Color.LIGHT4, Color.DARK0_HARD), + 'table_head' : (Color.LIGHT2__BOLD, Color.DARK0_HARD), + 'filter_results' : (Color.DARK0_HARD, Color.BRIGHT_GREEN), + 'edit_topic' : (Color.DARK0_HARD, Color.GRAY_244), + 'edit_tag' : (Color.DARK0_HARD, Color.GRAY_244), + 'edit_author' : (Color.NEUTRAL_YELLOW, Color.DARK0_HARD), + 'edit_time' : (Color.BRIGHT_BLUE, Color.DARK0_HARD), + 'current_user' : (Color.LIGHT2, Color.DARK0_HARD), + 'muted' : (Color.BRIGHT_BLUE, Color.DARK0_HARD), + 'popup_border' : (Color.LIGHT2, Color.DARK0_HARD), + 'area:help' : (Color.DARK0_HARD, Color.BRIGHT_GREEN), + 'area:msg' : (Color.DARK0_HARD, Color.BRIGHT_RED), + 'area:stream' : (Color.DARK0_HARD, Color.BRIGHT_BLUE), + 'area:error' : (Color.LIGHT2, Color.FADED_RED), + 'search_error' : (Color.BRIGHT_RED, Color.DARK0_HARD), + 'task:success' : (Color.DARK0_HARD, Color.BRIGHT_GREEN), + 'task:error' : (Color.LIGHT2, Color.FADED_RED), + 'task:warning' : (Color.DARK0_HARD, Color.BRIGHT_RED), +} +# fmt: on From 00a1e5d1d0e37d2256c7865d904b1052a38d8694 Mon Sep 17 00:00:00 2001 From: Rohitth Date: Wed, 23 Jun 2021 19:13:35 +0530 Subject: [PATCH 09/12] themes: Test new themefile completeness. This commit tests completeness on new themefiles in test_themes. Checks: * STYLE and REQUIRED_STYLES use the same styles * All 3 color codes are in the correct format, using regex * color used in STYLE is defined in the Color class. Run: pytest -k test_new_builtin_theme --- tests/config/test_themes.py | 67 ++++++++++++++++++++++++++++++++++ zulipterminal/config/themes.py | 10 +++++ 2 files changed, 77 insertions(+) diff --git a/tests/config/test_themes.py b/tests/config/test_themes.py index 587bd6e661..00779ba2b1 100644 --- a/tests/config/test_themes.py +++ b/tests/config/test_themes.py @@ -1,6 +1,9 @@ +import re + import pytest from zulipterminal.config.themes import ( + NEW_THEMES, REQUIRED_STYLES, THEMES, all_themes, @@ -16,12 +19,76 @@ "zt_blue", "gruvbox_dark24", } +aliases_16_color = [ + "default", + "black", + "dark red", + "dark green", + "brown", + "dark blue", + "dark magenta", + "dark cyan", + "dark gray", + "light red", + "light green", + "yellow", + "light blue", + "light magenta", + "light cyan", + "light gray", + "white", +] def test_all_themes(): assert all_themes() == list(THEMES.keys()) +# Check built-in themes are complete for quality-control purposes +@pytest.mark.parametrize( + "theme_name", + [ + theme + if theme in expected_complete_themes + else pytest.param(theme, marks=pytest.mark.xfail(reason="incomplete")) + for theme in NEW_THEMES + ], +) +def test_new_builtin_theme_completeness(theme_name): + theme = NEW_THEMES[theme_name] + theme_styles = theme.STYLES + theme_colors = theme.Color + + # Check if STYLE and REQUIRED_STYLES use the same styles. + assert len(theme_styles) == len(REQUIRED_STYLES) + assert all(required_style in theme_styles for required_style in REQUIRED_STYLES) + # Check if colors are defined with all 3 color codes. + for color in theme_colors: + if "__" in color.name: + continue + + codes = color.value.split() + assert len(codes) == 3 + # Check if 16-color alias is correct + assert codes[0].replace("_", " ") in aliases_16_color + # Check if 24-bit and 256 color is any of + # #000000-#ffffff or #000-#fff or h0-h255 or g0-g100 0r g#00-g#ff + pattern = re.compile( + "#[\\da-f]{6}|#[\\da-f]{3}|(?:h|g)([\\d]{1,3})|g#[\\da-f]{2}|default$" + ) + for code in [codes[1], codes[2]]: + code = pattern.match(code) + assert code + if code.group(1) and code.group(0).startswith("h"): + assert int(code.group(1)) < 256 + elif code.group(1) and code.group(0).startswith("g"): + assert int(code.group(1)) <= 100 + # Check if color used in STYLE exists in Color. + for style_name, style_conf in theme_styles.items(): + fg, bg = style_conf + assert fg in theme_colors and bg in theme_colors + + # Check built-in themes are complete for quality-control purposes @pytest.mark.parametrize( "theme_name", diff --git a/zulipterminal/config/themes.py b/zulipterminal/config/themes.py index b46d72bbb9..7d6bd75a81 100644 --- a/zulipterminal/config/themes.py +++ b/zulipterminal/config/themes.py @@ -1,5 +1,7 @@ from typing import Dict, List, Optional, Tuple +from zulipterminal.themes import gruvbox, zt_blue, zt_dark, zt_light + ThemeSpec = List[Tuple[Optional[str], ...]] @@ -587,3 +589,11 @@ def theme_with_monochrome_added(theme: ThemeSpec) -> ThemeSpec: new_style = style updated_theme.append(new_style) return updated_theme + + +NEW_THEMES = { + "gruvbox_dark": gruvbox, + "zt_dark": zt_dark, + "zt_light": zt_light, + "zt_blue": zt_blue, +} From 91a53750fbab81640b3feb73693fe50f2d0904b3 Mon Sep 17 00:00:00 2001 From: Rohitth Date: Fri, 11 Jun 2021 18:24:38 +0530 Subject: [PATCH 10/12] themes: Parse theme file to generate urwid theme. This commit creates the function `generate_theme` in themes.py which parses a theme file to generate a urwid compatible theme depending on the color-depth used. The theme files are accessed using NEW_THEMES which is a placeholder for the THEMES dict until migration happens. A new type hint for urwid compatible styles is created - StyleSpec. Slicing lists doesn't work with very specific type hints like StyleSpec hence Any is used, though this function will shortly be removed. Tests added. --- tests/config/test_themes.py | 51 ++++++++++++++++++++++++++++++++++ zulipterminal/config/themes.py | 50 ++++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/tests/config/test_themes.py b/tests/config/test_themes.py index 00779ba2b1..af48fc38fd 100644 --- a/tests/config/test_themes.py +++ b/tests/config/test_themes.py @@ -1,4 +1,5 @@ import re +from enum import Enum import pytest @@ -8,6 +9,7 @@ THEMES, all_themes, complete_and_incomplete_themes, + parse_themefile, theme_with_monochrome_added, ) @@ -140,3 +142,52 @@ def test_complete_and_incomplete_themes(): def test_theme_with_monochrome_added(mocker, theme, expected_new_theme, req_styles): mocker.patch.dict("zulipterminal.config.themes.REQUIRED_STYLES", req_styles) assert theme_with_monochrome_added(theme) == expected_new_theme + + +@pytest.mark.parametrize( + "color_depth, expected_urwid_theme", + [ + (1, [("s1", "", "", ""), ("s2", "", "", "bold")]), + ( + 16, + [ + ("s1", "white , bold", "dark magenta"), + ("s2", "white , bold , italics", "dark magenta"), + ], + ), + ( + 256, + [ + ("s1", "", "", "", "#fff , bold", "h90"), + ("s2", "", "", "", "#fff , bold , italics", "h90"), + ], + ), + ( + 2 ** 24, + [ + ("s1", "", "", "", "#ffffff , bold", "#870087"), + ("s2", "", "", "", "#ffffff , bold , italics", "#870087"), + ], + ), + ], + ids=[ + "mono-chrome", + "16-color", + "256-color", + "24-bit-color", + ], +) +def test_parse_themefile(mocker, color_depth, expected_urwid_theme): + class Color(Enum): + WHITE__BOLD = "white #fff #ffffff , bold" + WHITE__BOLD_ITALICS = "white #fff #ffffff , bold , italics" + DARK_MAGENTA = "dark_magenta h90 #870087" + + STYLES = { + "s1": (Color.WHITE__BOLD, Color.DARK_MAGENTA), + "s2": (Color.WHITE__BOLD_ITALICS, Color.DARK_MAGENTA), + } + + req_styles = {"s1": "", "s2": "bold"} + mocker.patch.dict("zulipterminal.config.themes.REQUIRED_STYLES", req_styles) + assert parse_themefile(STYLES, color_depth) == expected_urwid_theme diff --git a/zulipterminal/config/themes.py b/zulipterminal/config/themes.py index 7d6bd75a81..1bfa33d0f8 100644 --- a/zulipterminal/config/themes.py +++ b/zulipterminal/config/themes.py @@ -1,9 +1,14 @@ -from typing import Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, Union from zulipterminal.themes import gruvbox, zt_blue, zt_dark, zt_light -ThemeSpec = List[Tuple[Optional[str], ...]] +StyleSpec = Union[ + Tuple[Optional[str], str, str], + Tuple[Optional[str], str, str, Optional[str]], + Tuple[Optional[str], str, str, Optional[str], str, str], +] +ThemeSpec = List[StyleSpec] # fmt: off # The keys in REQUIRED_STYLES specify what styles are necessary for a theme to @@ -572,11 +577,11 @@ def complete_and_incomplete_themes() -> Tuple[List[str], List[str]]: return sorted(list(complete)), sorted(incomplete) -def theme_with_monochrome_added(theme: ThemeSpec) -> ThemeSpec: +def theme_with_monochrome_added(theme: ThemeSpec) -> Any: updated_theme = [] for style in theme: style_name = style[0] - if style_name not in REQUIRED_STYLES: # incomplete theme + if style_name not in REQUIRED_STYLES: continue mono_style = REQUIRED_STYLES[style_name] if len(style) > 4: # 256 colors+ @@ -597,3 +602,40 @@ def theme_with_monochrome_added(theme: ThemeSpec) -> ThemeSpec: "zt_light": zt_light, "zt_blue": zt_blue, } + + +def generate_theme(theme_name: str, color_depth: int) -> ThemeSpec: + theme_styles = NEW_THEMES[theme_name].STYLES + urwid_theme = parse_themefile(theme_styles, color_depth) + return urwid_theme + + +def parse_themefile( + theme_styles: Dict[Optional[str], Tuple[Any, Any]], color_depth: int +) -> ThemeSpec: + urwid_theme = [] + for style_name, (fg, bg) in theme_styles.items(): + fg_code16, fg_code256, fg_code24, *fg_props = fg.value.split() + bg_code16, bg_code256, bg_code24, *bg_props = bg.value.split() + + new_style: StyleSpec + if color_depth == 1: + new_style = (style_name, "", "", REQUIRED_STYLES[style_name]) + + elif color_depth == 16: + fg = " ".join([fg_code16] + fg_props).replace("_", " ") + bg = " ".join([bg_code16] + bg_props).replace("_", " ") + new_style = (style_name, fg, bg) + + elif color_depth == 256: + fg = " ".join([fg_code256] + fg_props) + bg = " ".join([bg_code256] + bg_props) + new_style = (style_name, "", "", "", fg, bg) + + elif color_depth == 2 ** 24: + fg = " ".join([fg_code24] + fg_props) + bg = " ".join([bg_code24] + bg_props) + new_style = (style_name, "", "", "", fg, bg) + + urwid_theme.append(new_style) + return urwid_theme From a609ec4828c12d389124922204f50e1c6c54a7e3 Mon Sep 17 00:00:00 2001 From: Rohitth Date: Fri, 11 Jun 2021 19:04:49 +0530 Subject: [PATCH 11/12] tests: theme: Test migration to new themes. A temporary test has been used to make sure all the color codes for all the color depths are correct and no errors have been made during the transition. Run: `pytest -k test_migrated_themes` --- tests/config/test_themes.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/config/test_themes.py b/tests/config/test_themes.py index af48fc38fd..16f7c43a65 100644 --- a/tests/config/test_themes.py +++ b/tests/config/test_themes.py @@ -9,6 +9,7 @@ THEMES, all_themes, complete_and_incomplete_themes, + generate_theme, parse_themefile, theme_with_monochrome_added, ) @@ -109,6 +110,42 @@ def test_builtin_theme_completeness(theme_name): assert all(required_style in styles_in_theme for required_style in REQUIRED_STYLES) +@pytest.mark.parametrize( + "theme_name, depth", + [ + ("zt_dark", 1), + ("zt_dark", 16), + ("zt_dark", 256), + ("zt_light", 16), + ("zt_blue", 16), + ("gruvbox_dark", 16), + ("gruvbox_dark", 256), + ("gruvbox_dark24", 2 ** 24), + ("gruvbox_dark24", 2 ** 24), + ], +) +def test_migrated_themes(theme_name, depth): + def split_and_strip(style): + style = style.split(",") + style = [s.strip() for s in style] + return style + + old_theme = theme_with_monochrome_added(THEMES[theme_name]) + + new_theme = generate_theme(theme_name.replace("24", ""), depth) + + for new_style, old_style in zip(new_theme, old_theme): + assert new_style[0] == old_style[0] + if depth == 1: + assert new_style[3] == old_style[3] + elif depth == 16: + assert split_and_strip(new_style[1]) == split_and_strip(old_style[1]) + assert split_and_strip(new_style[2]) == split_and_strip(old_style[2]) + else: + assert split_and_strip(new_style[4]) == split_and_strip(old_style[4]) + assert split_and_strip(new_style[5]) == split_and_strip(old_style[5]) + + def test_complete_and_incomplete_themes(): # These are sorted to ensure reproducibility result = ( From f1a3adf808bb5f9776d5f5ef54760586946f25d9 Mon Sep 17 00:00:00 2001 From: Rohitth Date: Sat, 12 Jun 2021 19:02:18 +0530 Subject: [PATCH 12/12] theme: Delete old themes and migrate to new themefiles. Old theme format used in themes.py is deleted from this commit. * Migrated to new themes in `run.py` * Mono theme generation is no longer required hence it is removed from both themes.py and run.py * NEW_THEMES is now the new THEMES dict. * `complete_and_incomplete_themes` is edited to make it use the new THEMES instead of the old one. A new `builtin_theme_completeness` test is added. Tests amended. --- tests/cli/test_run.py | 5 +- tests/config/test_themes.py | 90 +----- zulipterminal/cli/run.py | 8 +- zulipterminal/config/themes.py | 520 +-------------------------------- 4 files changed, 15 insertions(+), 608 deletions(-) diff --git a/tests/cli/test_run.py b/tests/cli/test_run.py index 935a5396b9..1527a713d9 100644 --- a/tests/cli/test_run.py +++ b/tests/cli/test_run.py @@ -5,7 +5,6 @@ import pytest from zulipterminal.cli.run import ( - THEMES, _write_zuliprc, exit_with_error, get_login_id, @@ -140,7 +139,6 @@ def test_valid_zuliprc_but_no_connection( def test_warning_regarding_incomplete_theme( capsys, mocker, - monkeypatch, minimal_zuliprc, bad_theme, server_connection_error="sce", @@ -149,13 +147,12 @@ def test_warning_regarding_incomplete_theme( "zulipterminal.core.Controller.__init__", side_effect=ServerConnectionFailure(server_connection_error), ) - - monkeypatch.setitem(THEMES, bad_theme, []) mocker.patch("zulipterminal.cli.run.all_themes", return_value=("a", "b", "c", "d")) mocker.patch( "zulipterminal.cli.run.complete_and_incomplete_themes", return_value=(["a", "b"], ["c", "d"]), ) + mocker.patch("zulipterminal.cli.run.generate_theme") with pytest.raises(SystemExit) as e: main(["-c", minimal_zuliprc, "-t", bad_theme]) diff --git a/tests/config/test_themes.py b/tests/config/test_themes.py index 16f7c43a65..ab0a711d76 100644 --- a/tests/config/test_themes.py +++ b/tests/config/test_themes.py @@ -4,14 +4,11 @@ import pytest from zulipterminal.config.themes import ( - NEW_THEMES, REQUIRED_STYLES, THEMES, all_themes, complete_and_incomplete_themes, - generate_theme, parse_themefile, - theme_with_monochrome_added, ) @@ -20,7 +17,6 @@ "gruvbox_dark", "zt_light", "zt_blue", - "gruvbox_dark24", } aliases_16_color = [ "default", @@ -54,11 +50,11 @@ def test_all_themes(): theme if theme in expected_complete_themes else pytest.param(theme, marks=pytest.mark.xfail(reason="incomplete")) - for theme in NEW_THEMES + for theme in THEMES ], ) -def test_new_builtin_theme_completeness(theme_name): - theme = NEW_THEMES[theme_name] +def test_builtin_theme_completeness(theme_name): + theme = THEMES[theme_name] theme_styles = theme.STYLES theme_colors = theme.Color @@ -92,60 +88,6 @@ def test_new_builtin_theme_completeness(theme_name): assert fg in theme_colors and bg in theme_colors -# Check built-in themes are complete for quality-control purposes -@pytest.mark.parametrize( - "theme_name", - [ - theme - if theme in expected_complete_themes - else pytest.param(theme, marks=pytest.mark.xfail(reason="incomplete")) - for theme in THEMES - ], -) -def test_builtin_theme_completeness(theme_name): - theme = THEMES[theme_name] - styles_in_theme = {style[0] for style in theme} - - assert len(styles_in_theme) == len(REQUIRED_STYLES) - assert all(required_style in styles_in_theme for required_style in REQUIRED_STYLES) - - -@pytest.mark.parametrize( - "theme_name, depth", - [ - ("zt_dark", 1), - ("zt_dark", 16), - ("zt_dark", 256), - ("zt_light", 16), - ("zt_blue", 16), - ("gruvbox_dark", 16), - ("gruvbox_dark", 256), - ("gruvbox_dark24", 2 ** 24), - ("gruvbox_dark24", 2 ** 24), - ], -) -def test_migrated_themes(theme_name, depth): - def split_and_strip(style): - style = style.split(",") - style = [s.strip() for s in style] - return style - - old_theme = theme_with_monochrome_added(THEMES[theme_name]) - - new_theme = generate_theme(theme_name.replace("24", ""), depth) - - for new_style, old_style in zip(new_theme, old_theme): - assert new_style[0] == old_style[0] - if depth == 1: - assert new_style[3] == old_style[3] - elif depth == 16: - assert split_and_strip(new_style[1]) == split_and_strip(old_style[1]) - assert split_and_strip(new_style[2]) == split_and_strip(old_style[2]) - else: - assert split_and_strip(new_style[4]) == split_and_strip(old_style[4]) - assert split_and_strip(new_style[5]) == split_and_strip(old_style[5]) - - def test_complete_and_incomplete_themes(): # These are sorted to ensure reproducibility result = ( @@ -155,32 +97,6 @@ def test_complete_and_incomplete_themes(): assert result == complete_and_incomplete_themes() -@pytest.mark.parametrize( - "theme, expected_new_theme, req_styles", - [ - ([("a", "another")], [], {}), - ([("a", "another")], [("a", "another")], {"a": ""}), - ([("a", "fg", "bg")], [("a", "fg", "bg", "x")], {"a": "x"}), - ([("a", "fg", "bg", "bold")], [("a", "fg", "bg", "x")], {"a": "x"}), - ( - [("a", "fg", "bg", "bold", "h1", "h2")], - [("a", "fg", "bg", "x", "h1", "h2")], - {"a": "x"}, - ), - ], - ids=[ - "incomplete_theme", - "one_to_one", - "16_color_add_mono", - "16_color_mono_overwrite", - "256_color", - ], -) -def test_theme_with_monochrome_added(mocker, theme, expected_new_theme, req_styles): - mocker.patch.dict("zulipterminal.config.themes.REQUIRED_STYLES", req_styles) - assert theme_with_monochrome_added(theme) == expected_new_theme - - @pytest.mark.parametrize( "color_depth, expected_urwid_theme", [ diff --git a/zulipterminal/cli/run.py b/zulipterminal/cli/run.py index c43888e5d9..14dccae4f4 100755 --- a/zulipterminal/cli/run.py +++ b/zulipterminal/cli/run.py @@ -12,11 +12,10 @@ from urwid import display_common, set_encoding from zulipterminal.config.themes import ( - THEMES, aliased_themes, all_themes, complete_and_incomplete_themes, - theme_with_monochrome_added, + generate_theme, ) from zulipterminal.core import Controller from zulipterminal.model import ServerConnectionFailure @@ -486,10 +485,7 @@ def main(options: Optional[List[str]] = None) -> None: break boolean_settings[setting] = zterm[setting][0] == valid_values[0] - if color_depth == 1: - theme_data = theme_with_monochrome_added(THEMES[theme_to_use[0]]) - else: - theme_data = THEMES[theme_to_use[0]] + theme_data = generate_theme(theme_to_use[0], color_depth) Controller( zuliprc_path, diff --git a/zulipterminal/config/themes.py b/zulipterminal/config/themes.py index 1bfa33d0f8..1131044c59 100644 --- a/zulipterminal/config/themes.py +++ b/zulipterminal/config/themes.py @@ -69,68 +69,12 @@ } # fmt: on -# 256-color base names for default theme (bold values added below) -DEF_base = dict( - dark_red="#a00", - brown="#880", - dark_blue="#24a", - dark_cyan="#088", - dark_gray="#666", - light_gray="#ccc", - light_red="#f00", - light_green="#0f0", - dark_green="#080", - yellow="#ff0", - light_blue="#28d", - light_magenta="#c8f", - white="#fff", - black="g19", -) - -DEF = dict( - DEF_base, **{f"{color}:bold": f"{code}, bold" for color, code in DEF_base.items()} -) - -# Colors used in gruvbox-256 -# See https://github.com/morhetz/gruvbox/blob/master/colors/gruvbox.vim -BLACK = "h234" # dark0_hard -WHITE = "h250" # light2 -WHITEBOLD = f"{WHITE}, bold" -DARKBLUE = "h24" # faded_blue -DARKRED = "h88" # faded_red -LIGHTBLUE = "h109" # bright_blue -LIGHTBLUEBOLD = f"{LIGHTBLUE}, bold" -YELLOW = "h172" # neutral_yellow -YELLOWBOLD = f"{YELLOW}, bold" -LIGHTGREEN = "h142" # bright_green -LIGHTRED = "h167" # bright_red -LIGHTREDBOLD = f"{LIGHTRED}, bold" -GRAY = "h244" # gray_244 -LIGHTGRAY = "h248" # light3 -LIGHTMAGENTA = "h132" # neutral_purple -LIGHTMAGENTABOLD = f"{LIGHTMAGENTA}, bold" -DARKCYAN = "h66" # neutral_blue -BROWN = "h136" # faded_yellow - -# Colors used in gruvbox_dark24 -BLACK24 = "#1d2021" # dark0_hard -WHITE24 = "#d5c4a1" # light2 -WHITEBOLD24 = f"{WHITE24}, bold" -DARKBLUE24 = "#076678" # faded_blue -DARKRED24 = "#9d0006" # faded_red -LIGHTBLUE24 = "#83a598" # bright_blue -LIGHTBLUEBOLD24 = f"{LIGHTBLUE24}, bold" -YELLOW24 = "#d79921" # neutral_yellow -YELLOWBOLD24 = f"{YELLOW24}, bold" -LIGHTGREEN24 = "#b8bb26" # bright_green -LIGHTRED24 = "#fb4934" # bright_red -LIGHTREDBOLD24 = f"{LIGHTRED24}, bold" -GRAY24 = "#928374" # gray_244 -LIGHTGRAY24 = "#bdae93" # light3 -LIGHTMAGENTA24 = "#b16286" # neutral_purple -LIGHTMAGENTABOLD24 = f"{LIGHTMAGENTA24}, bold" -DARKCYAN24 = "#458588" # neutral_blue -BROWN24 = "#b57614" # faded_yellow +THEMES = { + "gruvbox_dark": gruvbox, + "zt_dark": zt_dark, + "zt_light": zt_light, + "zt_blue": zt_blue, +} THEME_ALIASES = { "default": "zt_dark", @@ -139,425 +83,6 @@ "blue": "zt_blue", } -# fmt: off -THEMES: Dict[str, ThemeSpec] = { - 'zt_dark': [ - (None, 'white', 'black', - None, DEF['white'], DEF['black']), - ('selected', 'white', 'dark blue', - None, DEF['white'], DEF['dark_blue']), - ('msg_selected', 'white', 'dark blue', - None, DEF['white'], DEF['dark_blue']), - ('header', 'dark cyan', 'dark blue', - None, DEF['dark_cyan'], DEF['dark_blue']), - ('general_narrow', 'white', 'dark blue', - None, DEF['white'], DEF['dark_blue']), - ('general_bar', 'white', 'black', - None, DEF['white'], DEF['black']), - ('name', 'yellow, bold', 'black', - None, DEF['yellow:bold'], DEF['black']), - ('unread', 'dark blue', 'black', - None, DEF['dark_blue'], DEF['black']), - ('user_active', 'light green', 'black', - None, DEF['light_green'], DEF['black']), - ('user_idle', 'yellow', 'black', - None, DEF['yellow'], DEF['black']), - ('user_offline', 'white', 'black', - None, DEF['white'], DEF['black']), - ('user_inactive', 'white', 'black', - None, DEF['white'], DEF['black']), - ('title', 'white, bold', 'black', - None, DEF['white:bold'], DEF['black']), - ('column_title', 'white, bold', 'black', - None, DEF['white:bold'], DEF['black']), - ('time', 'light blue', 'black', - None, DEF['light_blue'], DEF['black']), - ('bar', 'white', 'dark gray', - None, DEF['white'], DEF['dark_gray']), - ('popup_contrast', 'white', 'dark gray', - None, DEF['white'], DEF['dark_gray']), - ('msg_emoji', 'light magenta', 'black', - None, DEF['light_magenta'], DEF['black']), - ('reaction', 'light magenta, bold', 'black', - None, DEF['light_magenta:bold'], DEF['black']), - ('reaction_mine', 'black', 'light magenta', - None, DEF['black'], DEF['light_magenta']), - ('msg_mention', 'light red, bold', 'black', - None, DEF['light_red:bold'], DEF['black']), - ('msg_link', 'light blue', 'black', - None, DEF['light_blue'], DEF['black']), - ('msg_link_index', 'light blue, bold', 'black', - None, DEF['light_blue:bold'], DEF['black']), - ('msg_quote', 'brown', 'black', - None, DEF['brown'], DEF['black']), - ('msg_code', 'black', 'white', - None, DEF['black'], DEF['white']), - ('msg_bold', 'white, bold', 'black', - None, DEF['white:bold'], DEF['black']), - ('msg_time', 'black', 'white', - None, DEF['black'], DEF['white']), - ('footer', 'black', 'light gray', - None, DEF['black'], DEF['light_gray']), - ('footer_contrast', 'white', 'black', - None, DEF['white'], DEF['black']), - ('starred', 'light red, bold', 'black', - None, DEF['light_red:bold'], DEF['black']), - ('popup_category', 'light blue, bold', 'black', - None, DEF['light_blue:bold'], DEF['black']), - ('unread_count', 'yellow', 'black', - None, DEF['yellow'], DEF['black']), - ('starred_count', 'light gray', 'black', - None, DEF['light_gray'], DEF['black']), - ('table_head', 'white, bold', 'black', - None, DEF['white:bold'], DEF['black']), - ('filter_results', 'white', 'dark green', - None, DEF['white'], DEF['dark_green']), - ('edit_topic', 'white', 'dark gray', - None, DEF['white'], DEF['dark_gray']), - ('edit_tag', 'white', 'dark gray', - None, DEF['white'], DEF['dark_gray']), - ('edit_author', 'yellow', 'black', - None, DEF['yellow'], DEF['black']), - ('edit_time', 'light blue', 'black', - None, DEF['light_blue'], DEF['black']), - ('current_user', 'white', 'black', - None, DEF['white'], DEF['black']), - ('muted', 'light blue', 'black', - None, DEF['light_blue'], DEF['black']), - ('popup_border', 'white', 'black', - None, DEF['white'], DEF['black']), - ('area:help', 'white', 'dark green', - None, DEF['white'], DEF['dark_green']), - ('area:msg', 'white', 'brown', - None, DEF['white'], DEF['brown']), - ('area:stream', 'white', 'dark cyan', - None, DEF['white'], DEF['dark_cyan']), - ('area:error', 'white', 'dark red', - None, DEF['white'], DEF['dark_red']), - ('search_error', 'light red', 'black', - None, DEF['light_red'], DEF['black']), - ('task:success', 'white', 'dark green', - None, DEF['white'], DEF['dark_green']), - ('task:error', 'white', 'dark red', - None, DEF['white'], DEF['dark_red']), - ('task:warning', 'white', 'brown', - None, DEF['white'], DEF['brown']), - ], - 'gruvbox_dark': [ - # default colorscheme on 16 colors, gruvbox colorscheme - # on 256 colors - (None, 'white', 'black', - None, WHITE, BLACK), - ('selected', 'black', 'white', - None, BLACK, WHITE), - ('msg_selected', 'black', 'white', - None, BLACK, WHITE), - ('header', 'dark cyan', 'dark blue', - None, DARKCYAN, DARKBLUE), - ('general_narrow', 'white', 'dark blue', - None, WHITE, DARKBLUE), - ('general_bar', 'white', 'black', - None, WHITE, BLACK), - ('name', 'yellow, bold', 'black', - None, YELLOWBOLD, BLACK), - ('unread', 'light magenta', 'black', - None, LIGHTMAGENTA, BLACK), - ('user_active', 'light green', 'black', - None, LIGHTGREEN, BLACK), - ('user_idle', 'yellow', 'black', - None, YELLOW, BLACK), - ('user_offline', 'white', 'black', - None, WHITE, BLACK), - ('user_inactive', 'white', 'black', - None, WHITE, BLACK), - ('title', 'white, bold', 'black', - None, WHITEBOLD, BLACK), - ('column_title', 'white, bold', 'black', - None, WHITEBOLD, BLACK), - ('time', 'light blue', 'black', - None, LIGHTBLUE, BLACK), - ('bar', 'white', 'dark gray', - None, WHITE, GRAY), - ('popup_contrast', 'black', 'dark gray', - None, BLACK, GRAY), - ('msg_emoji', 'light magenta', 'black', - None, LIGHTMAGENTA, BLACK), - ('reaction', 'light magenta, bold', 'black', - None, LIGHTMAGENTABOLD, BLACK), - ('reaction_mine', 'black', 'light magenta', - None, BLACK, LIGHTMAGENTA), - ('msg_mention', 'light red, bold', 'black', - None, LIGHTREDBOLD, BLACK), - ('msg_link', 'light blue', 'black', - None, LIGHTBLUE, BLACK), - ('msg_link_index', 'light blue, bold', 'black', - None, LIGHTBLUEBOLD, BLACK), - ('msg_quote', 'brown', 'black', - None, BROWN, BLACK), - ('msg_code', 'black', 'white', - None, BLACK, WHITE), - ('msg_bold', 'white, bold', 'black', - None, WHITEBOLD, BLACK), - ('msg_time', 'black', 'white', - None, BLACK, WHITE), - ('footer', 'black', 'light gray', - None, BLACK, LIGHTGRAY), - ('footer_contrast', 'white', 'black', - None, WHITE, BLACK), - ('starred', 'light red, bold', 'black', - None, LIGHTREDBOLD, BLACK), - ('popup_category', 'light blue, bold', 'black', - None, LIGHTBLUEBOLD, BLACK), - ('unread_count', 'yellow', 'black', - None, YELLOW, BLACK), - ('starred_count', 'light gray', 'black', - None, LIGHTGRAY, BLACK), - ('table_head', 'white, bold', 'black', - None, WHITEBOLD, BLACK), - ('filter_results', 'black', 'light green', - None, BLACK, LIGHTGREEN), - ('edit_topic', 'black', 'dark gray', - None, BLACK, GRAY), - ('edit_tag', 'black', 'dark gray', - None, BLACK, GRAY), - ('edit_author', 'yellow', 'black', - None, YELLOW, BLACK), - ('edit_time', 'light blue', 'black', - None, LIGHTBLUE, BLACK), - ('current_user', 'white', 'black', - None, WHITE, BLACK), - ('muted', 'light blue', 'black', - None, LIGHTBLUE, BLACK), - ('popup_border', 'white', 'black', - None, WHITE, BLACK), - ('area:help', 'black', 'light green', - None, BLACK, LIGHTGREEN), - ('area:msg', 'black', 'light red', - None, BLACK, LIGHTRED), - ('area:stream', 'black', 'light blue', - None, BLACK, LIGHTBLUE), - ('area:error', 'white', 'dark red', - None, WHITE, DARKRED), - ('search_error', 'light red', 'black', - None, LIGHTRED, BLACK), - ('task:success', 'black', 'light green', - None, BLACK, LIGHTGREEN), - ('task:error', 'white', 'dark red', - None, WHITE, DARKRED), - ('task:warning', 'black', 'light red', - None, BLACK, LIGHTRED), - ], - 'gruvbox_dark24': [ - # default colorscheme on 16 colors, gruvbox colorscheme - # on 24 bit color (true color) - (None, 'white', 'black', - None, WHITE24, BLACK24), - ('selected', 'black', 'white', - None, BLACK24, WHITE24), - ('msg_selected', 'black', 'white', - None, BLACK24, WHITE24), - ('header', 'dark cyan', 'dark blue', - None, DARKCYAN24, DARKBLUE24), - ('general_narrow', 'white', 'dark blue', - None, WHITE24, DARKBLUE24), - ('general_bar', 'white', 'black', - None, WHITE24, BLACK24), - ('name', 'yellow, bold', 'black', - None, YELLOWBOLD24, BLACK24), - ('unread', 'light magenta', 'black', - None, LIGHTMAGENTA24, BLACK24), - ('user_active', 'light green', 'black', - None, LIGHTGREEN24, BLACK24), - ('user_idle', 'yellow', 'black', - None, YELLOW24, BLACK24), - ('user_offline', 'white', 'black', - None, WHITE24, BLACK24), - ('user_inactive', 'white', 'black', - None, WHITE24, BLACK24), - ('title', 'white, bold', 'black', - None, WHITEBOLD24, BLACK24), - ('column_title', 'white, bold', 'black', - None, WHITEBOLD24, BLACK24), - ('time', 'light blue', 'black', - None, LIGHTBLUE24, BLACK24), - ('bar', 'white', 'dark gray', - None, WHITE24, GRAY24), - ('popup_contrast', 'black', 'dark gray', - None, BLACK24, GRAY24), - ('msg_emoji', 'light magenta', 'black', - None, LIGHTMAGENTA24, BLACK24), - ('reaction', 'light magenta, bold', 'black', - None, LIGHTMAGENTABOLD24, BLACK24), - ('reaction_mine', 'black', 'light magenta', - None, BLACK24, LIGHTMAGENTA24), - ('msg_mention', 'light red, bold', 'black', - None, LIGHTREDBOLD24, BLACK24), - ('msg_link', 'light blue', 'black', - None, LIGHTBLUE24, BLACK24), - ('msg_link_index', 'light blue, bold', 'black', - None, LIGHTBLUEBOLD24, BLACK24), - ('msg_quote', 'brown', 'black', - None, BROWN24, BLACK24), - ('msg_code', 'black', 'white', - None, BLACK24, WHITE24), - ('msg_bold', 'white, bold', 'black', - None, WHITEBOLD24, BLACK24), - ('msg_time', 'black', 'white', - None, BLACK24, WHITE24), - ('footer', 'black', 'light gray', - None, BLACK24, LIGHTGRAY24), - ('footer_contrast', 'white', 'black', - None, WHITE24, BLACK24), - ('starred', 'light red, bold', 'black', - None, LIGHTREDBOLD24, BLACK24), - ('popup_category', 'light blue, bold', 'black', - None, LIGHTBLUEBOLD24, BLACK24), - ('unread_count', 'yellow', 'black', - None, YELLOW24, BLACK24), - ('starred_count', 'light gray', 'black', - None, LIGHTGRAY24, BLACK24), - ('table_head', 'white, bold', 'black', - None, WHITEBOLD24, BLACK24), - ('filter_results', 'black', 'light green', - None, BLACK24, LIGHTGREEN24), - ('edit_topic', 'black', 'dark gray', - None, BLACK24, GRAY24), - ('edit_tag', 'black', 'dark gray', - None, BLACK24, GRAY24), - ('edit_author', 'yellow', 'black', - None, YELLOW24, BLACK24), - ('edit_time', 'light blue', 'black', - None, LIGHTBLUE24, BLACK24), - ('current_user', 'white', 'black', - None, WHITE24, BLACK24), - ('muted', 'light blue', 'black', - None, LIGHTBLUE24, BLACK24), - ('popup_border', 'white', 'black', - None, WHITE24, BLACK24), - ('area:help', 'black', 'light green', - None, BLACK24, LIGHTGREEN24), - ('area:msg', 'black', 'light red', - None, BLACK24, LIGHTRED24), - ('area:stream', 'black', 'light blue', - None, BLACK24, LIGHTBLUE24), - ('area:error', 'white', 'dark red', - None, WHITE24, DARKRED24), - ('search_error', 'light red', 'black', - None, LIGHTRED24, BLACK24), - ('task:success', 'black', 'light green', - None, BLACK24, LIGHTGREEN24), - ('task:error', 'white', 'dark red', - None, WHITE24, DARKRED24), - ('task:warning', 'black', 'light red', - None, BLACK24, LIGHTRED24), - ], - 'zt_light': [ - (None, 'black', 'white'), - ('selected', 'black', 'light green'), - ('msg_selected', 'black', 'light green'), - ('header', 'white', 'dark blue'), - ('general_narrow', 'white', 'dark blue'), - ('general_bar', 'dark blue', 'white'), - ('name', 'dark green', 'white'), - ('unread', 'dark gray', 'light gray'), - ('user_active', 'dark green', 'white'), - ('user_idle', 'dark blue', 'white'), - ('user_offline', 'black', 'white'), - ('user_inactive', 'black', 'white'), - ('title', 'white, bold', 'dark gray'), - ('column_title', 'black, bold', 'white'), - ('time', 'dark blue', 'white'), - ('bar', 'white', 'dark gray'), - ('popup_contrast', 'white', 'dark gray'), - ('msg_emoji', 'light magenta', 'white'), - ('reaction', 'light magenta, bold', 'white'), - ('reaction_mine', 'white', 'light magenta'), - ('msg_mention', 'light red, bold', 'white'), - ('msg_link', 'dark blue', 'white'), - ('msg_link_index', 'dark blue, bold', 'white'), - ('msg_quote', 'black', 'brown'), - ('msg_code', 'black', 'light gray'), - ('msg_bold', 'white, bold', 'dark gray'), - ('msg_time', 'white', 'dark gray'), - ('footer', 'white', 'dark gray'), - ('footer_contrast', 'black', 'white'), - ('starred', 'light red, bold', 'white'), - ('popup_category', 'dark gray, bold', 'light gray'), - ('unread_count', 'dark blue, bold', 'white'), - ('starred_count', 'black', 'white'), - ('table_head', 'black, bold', 'white'), - ('filter_results', 'white', 'dark green'), - ('edit_topic', 'white', 'dark gray'), - ('edit_tag', 'white', 'dark gray'), - ('edit_author', 'dark green', 'white'), - ('edit_time', 'dark blue', 'white'), - ('current_user', 'dark gray', 'white'), - ('muted', 'dark gray', 'white'), - ('popup_border', 'black', 'white'), - ('area:help', 'black', 'light green'), - ('area:stream', 'black', 'light blue'), - ('area:msg', 'black', 'yellow'), - ('area:error', 'black', 'light red'), - ('search_error', 'light red', 'white'), - ('task:success', 'black', 'dark green'), - ('task:error', 'white', 'dark red'), - ('task:warning', 'black', 'yellow'), - ], - 'zt_blue': [ - (None, 'black', 'light blue'), - ('selected', 'black', 'light gray'), - ('msg_selected', 'black', 'light gray'), - ('header', 'black', 'dark blue'), - ('general_narrow', 'white', 'dark blue'), - ('general_bar', 'dark blue', 'light blue'), - ('name', 'dark red', 'light blue'), - ('unread', 'light gray', 'light blue'), - ('user_active', 'light green, bold', 'light blue'), - ('user_idle', 'dark gray', 'light blue'), - ('user_offline', 'black', 'light blue'), - ('user_inactive', 'black', 'light blue'), - ('title', 'white, bold', 'dark blue'), - ('column_title', 'black, bold', 'light blue'), - ('time', 'dark blue', 'light blue'), - ('bar', 'white', 'dark blue'), - ('popup_contrast', 'white', 'dark blue'), - ('msg_emoji', 'dark magenta', 'light blue'), - ('reaction', 'dark magenta, bold', 'light blue'), - ('reaction_mine', 'light blue', 'dark magenta'), - ('msg_mention', 'light red, bold', 'light blue'), - ('msg_link', 'dark blue', 'light gray'), - ('msg_link_index', 'dark blue, bold', 'light gray'), - ('msg_quote', 'brown', 'dark blue'), - ('msg_code', 'dark blue', 'white'), - ('msg_bold', 'white, bold', 'dark blue'), - ('msg_time', 'dark blue', 'white'), - ('footer', 'white', 'dark gray'), - ('footer_contrast', 'black', 'white'), - ('starred', 'light red, bold', 'light blue'), - ('popup_category', 'light gray, bold', 'light blue'), - ('unread_count', 'yellow', 'light blue'), - ('starred_count', 'black', 'light blue'), - ('table_head', 'black, bold', 'light blue'), - ('filter_results', 'white', 'dark green'), - ('edit_topic', 'white', 'dark blue'), - ('edit_tag', 'white', 'dark blue'), - ('edit_author', 'dark gray', 'light blue'), - ('edit_time', 'dark blue', 'light blue'), - ('current_user', 'light gray', 'light blue'), - ('muted', 'light gray', 'light blue'), - ('popup_border', 'white', 'light blue'), - ('area:help', 'white', 'dark green'), - ('area:stream', 'white', 'dark cyan'), - ('area:msg', 'white', 'brown'), - ('area:error', 'white', 'dark red'), - ('search_error', 'light red', 'light blue'), - ('task:success', 'white', 'dark green'), - ('task:error', 'white', 'dark red'), - ('task:warning', 'white', 'brown'), - ] -} -# fmt: on - def all_themes() -> List[str]: return list(THEMES.keys()) @@ -570,42 +95,15 @@ def aliased_themes() -> Dict[str, str]: def complete_and_incomplete_themes() -> Tuple[List[str], List[str]]: complete = { name - for name, styles in THEMES.items() - if {s[0] for s in styles} == set(REQUIRED_STYLES) + for name, theme in THEMES.items() + if set(theme.STYLES) == set(REQUIRED_STYLES) } incomplete = list(set(THEMES) - complete) return sorted(list(complete)), sorted(incomplete) -def theme_with_monochrome_added(theme: ThemeSpec) -> Any: - updated_theme = [] - for style in theme: - style_name = style[0] - if style_name not in REQUIRED_STYLES: - continue - mono_style = REQUIRED_STYLES[style_name] - if len(style) > 4: # 256 colors+ - new_style = style[:3] + (mono_style,) + style[4:] - elif len(style) == 4: # 16 colors + mono (overwrite mono) - new_style = style[:3] + (mono_style,) - elif len(style) == 3: # 16 colors only - new_style = style[:3] + (mono_style,) - else: # 1-to-1 mapping (same as other style) - new_style = style - updated_theme.append(new_style) - return updated_theme - - -NEW_THEMES = { - "gruvbox_dark": gruvbox, - "zt_dark": zt_dark, - "zt_light": zt_light, - "zt_blue": zt_blue, -} - - def generate_theme(theme_name: str, color_depth: int) -> ThemeSpec: - theme_styles = NEW_THEMES[theme_name].STYLES + theme_styles = THEMES[theme_name].STYLES urwid_theme = parse_themefile(theme_styles, color_depth) return urwid_theme