diff --git a/qt/BUILD.bazel b/qt/BUILD.bazel index be49bc6a6..3c1db25f5 100644 --- a/qt/BUILD.bazel +++ b/qt/BUILD.bazel @@ -16,9 +16,9 @@ py_binary( ) py_binary( - name = "extract_sass_colors", + name = "extract_sass_vars", srcs = [ - "tools/extract_sass_colors.py", + "tools/extract_sass_vars.py", ], imports = ["."], visibility = [":__subpackages__"], diff --git a/qt/aqt/BUILD.bazel b/qt/aqt/BUILD.bazel index 9248b51af..65fba5a32 100644 --- a/qt/aqt/BUILD.bazel +++ b/qt/aqt/BUILD.bazel @@ -11,14 +11,15 @@ genrule( ) genrule( - name = "extract_sass_colors", + name = "extract_sass_vars", srcs = [ + "//sass:_colors.scss", "//sass:_vars.scss", ], - outs = ["colors.py"], - cmd = "$(location //qt:extract_sass_colors) $< $@", + outs = ["colors.py", "props.py"], + cmd = "$(location //qt:extract_sass_vars) $(SRCS) $(OUTS)", tools = [ - "//qt:extract_sass_colors", + "//qt:extract_sass_vars", ], ) diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index c1049a9ad..d70acb870 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -162,7 +162,7 @@ class Editor: context=self, default_css=False, ) - self.web._fix_editor_background_color_and_show() + self.web.show() lefttopbtns: list[str] = [] gui_hooks.editor_did_init_left_buttons(lefttopbtns, self) diff --git a/qt/aqt/theme.py b/qt/aqt/theme.py index 6a943e042..f10eea944 100644 --- a/qt/aqt/theme.py +++ b/qt/aqt/theme.py @@ -251,6 +251,9 @@ QTabWidget {{ background-color: {}; }} if not self.night_mode: app.setStyle(QStyleFactory.create(self._default_style)) # type: ignore + self.default_palette.setColor( + QPalette.ColorRole.Window, self.qcolor(colors.WINDOW_BG) + ) app.setPalette(self.default_palette) return diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index f68b58926..11d2eaf66 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -233,9 +233,7 @@ class AnkiWebView(QWebEngineView): self.set_title(title) self._page = AnkiWebPage(self._onBridgeCmd) # reduce flicker - self._page.setBackgroundColor( - self.get_window_bg_color(theme_manager.night_mode) - ) + self._page.setBackgroundColor(QColor(theme_manager.color(colors.WINDOW_BG))) # in new code, use .set_bridge_command() instead of setting this directly self.onBridgeCmd: Callable[[str], Any] = self.defaultOnBridgeCmd @@ -404,15 +402,6 @@ class AnkiWebView(QWebEngineView): else: return 3 - def get_window_bg_color(self, night_mode: bool) -> QColor: - if night_mode: - return QColor(colors.WINDOW_BG[1]) - elif is_mac: - # standard palette does not return correct window color on macOS - return QColor("#ececec") - else: - return theme_manager.default_palette.color(QPalette.ColorRole.Window) - def standard_css(self) -> str: palette = theme_manager.default_palette color_hl = palette.color(QPalette.ColorRole.Highlight).name() @@ -459,15 +448,12 @@ div[contenteditable="true"]:focus {{ zoom = self.app_zoom_factor() - window_bg_day = self.get_window_bg_color(False).name() - window_bg_night = self.get_window_bg_color(True).name() - return f""" body {{ zoom: {zoom}; background-color: var(--window-bg); }} html {{ {font} }} {button_style} -:root {{ --window-bg: {window_bg_day} }} -:root[class*=night-mode] {{ --window-bg: {window_bg_night} }} +:root {{ --window-bg: {colors.WINDOW_BG[0]} }} +:root[class*=night-mode] {{ --window-bg: {colors.WINDOW_BG[1]} }} """ def stdHtml( @@ -712,7 +698,9 @@ html {{ {font} }} def on_theme_did_change(self) -> None: # avoid flashes if page reloaded self._page.setBackgroundColor( - self.get_window_bg_color(theme_manager.night_mode) + QColor(colors.WINDOW_BG[1]) + if theme_manager.night_mode + else QColor(colors.WINDOW_BG[0]) ) # update night-mode class, and legacy nightMode/night-mode body classes self.eval( @@ -732,30 +720,3 @@ html {{ {font} }} }})(); """ ) - - def _fix_editor_background_color_and_show(self) -> None: - # The editor does not use our standard CSS, which takes care of matching the background - # colour of the webview to the window we're showing it in. This causes a difference in - # shades on Windows/Linux in day mode, that we need to work around. This is a temporary - # fix before the 2.1.50 release; with more time there may be a better way to do this. - - if theme_manager.night_mode: - # The styling changes are not required for night mode, and hiding+showing the - # webview causes a flash of black. - return - - self.hide() - - window_bg_day = self.get_window_bg_color(False).name() - css = f":root {{ --window-bg: {window_bg_day} }}" - self.evalWithCallback( - f""" -(function(){{ - const style = document.createElement('style'); - style.innerHTML = `{css}`; - document.head.appendChild(style); -}})(); -""", - # avoids FOUC - lambda _: self.show(), - ) diff --git a/qt/tools/extract_sass_colors.py b/qt/tools/extract_sass_colors.py deleted file mode 100644 index 219fd6a26..000000000 --- a/qt/tools/extract_sass_colors.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -import json -import re -import sys - -input_scss = sys.argv[1] -output_py = sys.argv[2] - -colors = {} - -for line in open(input_scss): - line = line.strip() - if not line: - continue - m = re.match(r"--(.+): (.+);$", line) - if not m: - if ( - line != "}" - and not ":root" in line - and "Copyright" not in line - and "License" not in line - and "color-scheme" not in line - ): - print("failed to match", line) - continue - - var = m.group(1) - val = m.group(2) - - colors.setdefault(var, []).append(val) - -with open(output_py, "w") as buf: - buf.write( - """\ -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -""" - ) - buf.write("# this file is auto-generated from _vars.scss\n") - for color, (day, night) in colors.items(): - color = color.replace("-", "_").upper() - buf.write(f'{color} = ("{day}", "{night}")\n') diff --git a/qt/tools/extract_sass_vars.py b/qt/tools/extract_sass_vars.py new file mode 100644 index 000000000..d1dc72a41 --- /dev/null +++ b/qt/tools/extract_sass_vars.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +import json +import re +import sys + +# bazel genrule "srcs" +colors_scss = sys.argv[1] +vars_scss = sys.argv[2] + +# bazel genrule "outs" +colors_py = sys.argv[3] +props_py = sys.argv[4] + +palette = {} +colors = {} +props = {} + +color = "" + + +for line in open(colors_scss): + line = line.strip() + if not line: + continue + + if m := re.match(r"^([a-z]+): \($", line): + color = m.group(1) + palette[color] = {} + + elif m := re.match(r"(\d): (.+),$", line): + palette[color][m.group(1)] = m.group(2) + + +# TODO: recursive extraction of arbitrarily nested Sass maps? +reached_colors = False + +for line in open(vars_scss): + line = line.strip() + if not line: + continue + + if line == "themes: (": + reached_colors = True + continue + + if reached_colors: + line = re.sub( + r"get\(\$color, (.+), (\d)\)", + lambda m: palette[m.group(1)][m.group(2)], + line, + ) + + if m := re.match(r"^(.+): ([^\(]+),$", line): + var = m.group(1) + val = m.group(2) + + if reached_colors: + colors.setdefault(var, []).append(val) + else: + props.setdefault(var, []).append(val) + + +copyright_notice = """\ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +""" + +with open(colors_py, "w") as buf: + buf.write(copyright_notice) + buf.write("# this file is auto-generated from _vars.scss and _colors.scss\n") + + for color, (day, night) in colors.items(): + color = color.replace("-", "_").upper() + buf.write(f'{color} = ("{day}", "{night}")\n') + + +with open(props_py, "w") as buf: + buf.write(copyright_notice) + buf.write("# this file is auto-generated from _vars.scss\n") + + for prop, val in props.items(): + day = val[0] + night = val[1] if len(val) > 1 else day + + prop = prop.replace("-", "_").upper() + buf.write(f'{prop} = ("{day}", "{night}")\n') diff --git a/sass/BUILD.bazel b/sass/BUILD.bazel index 2e5ac043c..19885ed97 100644 --- a/sass/BUILD.bazel +++ b/sass/BUILD.bazel @@ -84,6 +84,6 @@ sass_library( ) exports_files( - ["_vars.scss"], + ["_colors.scss", "_vars.scss"], visibility = ["//visibility:public"], ) diff --git a/sass/_colors.scss b/sass/_colors.scss new file mode 100644 index 000000000..001b7b19b --- /dev/null +++ b/sass/_colors.scss @@ -0,0 +1,203 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later, http://www.gnu.org/licenses/agpl.html */ + +/* + * Anki Color Palette + * custom gray, rest from Tailwind CSS v3 palette + * + * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ + +$color: ( + gray: ( + 0: #fcfdfe, + 1: #f2f5f8, + 2: #dde1e7, + 3: #aeb5be, + 4: #7a818a, + 5: #4a5058, + 6: #2d3138, + 7: #181b1f, + 8: #101215, + 9: #0b0c0e, + ), + red: ( + 0: #fef2f2, + 1: #fee2e2, + 2: #fecaca, + 3: #fca5a5, + 4: #f87171, + 5: #ef4444, + 6: #dc2626, + 7: #b91c1c, + 8: #991b1b, + 9: #7f1d1d, + ), + orange: ( + 0: #fff7ed, + 1: #ffedd5, + 2: #fed7aa, + 3: #fdba74, + 4: #fb923c, + 5: #f97316, + 6: #ea580c, + 7: #c2410c, + 8: #9a3412, + 9: #7c2d12, + ), + amber: ( + 0: #fffbeb, + 1: #fef3c7, + 2: #fde68a, + 3: #fcd34d, + 4: #fbbf24, + 5: #f59e0b, + 6: #d97706, + 7: #b45309, + 8: #92400e, + 9: #78350f, + ), + yellow: ( + 0: #fefce8, + 1: #fef9c3, + 2: #fef08a, + 3: #fde047, + 4: #facc15, + 5: #eab308, + 6: #ca8a04, + 7: #a16207, + 8: #854d0e, + 9: #713f12, + ), + lime: ( + 0: #f7fee7, + 1: #ecfccb, + 2: #d9f99d, + 3: #bef264, + 4: #a3e635, + 5: #84cc16, + 6: #65a30d, + 7: #4d7c0f, + 8: #3f6212, + 9: #365314, + ), + green: ( + 0: #f0fdf4, + 1: #dcfce7, + 2: #bbf7d0, + 3: #86efac, + 4: #4ade80, + 5: #22c55e, + 6: #16a34a, + 7: #15803d, + 8: #166534, + 9: #14532d, + ), + teal: ( + 0: #f0fdfa, + 1: #ccfbf1, + 2: #99f6e4, + 3: #5eead4, + 4: #2dd4bf, + 5: #14b8a6, + 6: #0d9488, + 7: #0f766e, + 8: #115e59, + 9: #134e4a, + ), + cyan: ( + 0: #ecfeff, + 1: #cffafe, + 2: #a5f3fc, + 3: #67e8f9, + 4: #22d3ee, + 5: #06b6d4, + 6: #0891b2, + 7: #0e7490, + 8: #155e75, + 9: #164e63, + ), + sky: ( + 0: #f0f9ff, + 1: #e0f2fe, + 2: #bae6fd, + 3: #7dd3fc, + 4: #38bdf8, + 5: #0ea5e9, + 6: #0284c7, + 7: #0369a1, + 8: #075985, + 9: #0c4a6e, + ), + blue: ( + 0: #eff6ff, + 1: #dbeafe, + 2: #bfdbfe, + 3: #93c5fd, + 4: #60a5fa, + 5: #3b82f6, + 6: #2563eb, + 7: #1d4ed8, + 8: #1e40af, + 9: #1e3a8a, + ), + indigo: ( + 0: #eef2ff, + 1: #e0e7ff, + 2: #c7d2fe, + 3: #a5b4fc, + 4: #818cf8, + 5: #6366f1, + 6: #4f46e5, + 7: #4338ca, + 8: #3730a3, + 9: #312e81, + ), + violet: ( + 0: #f5f3ff, + 1: #ede9fe, + 2: #ddd6fe, + 3: #c4b5fd, + 4: #a78bfa, + 5: #8b5cf6, + 6: #7c3aed, + 7: #6d28d9, + 8: #5b21b6, + 9: #4c1d95, + ), + purple: ( + 0: #faf5ff, + 1: #f3e8ff, + 2: #e9d5ff, + 3: #d8b4fe, + 4: #c084fc, + 5: #a855f7, + 6: #9333ea, + 7: #7e22ce, + 8: #6b21a8, + 9: #581c87, + ), + fuchsia: ( + 0: #fdf4ff, + 1: #fae8ff, + 2: #f5d0fe, + 3: #f0abfc, + 4: #e879f9, + 5: #d946ef, + 6: #c026d3, + 7: #a21caf, + 8: #86198f, + 9: #701a75, + ), + pink: ( + 0: #fdf2f8, + 1: #fce7f3, + 2: #fbcfe8, + 3: #f9a8d4, + 4: #f472b6, + 5: #ec4899, + 6: #db2777, + 7: #be185d, + 8: #9d174d, + 9: #831843, + ), +); diff --git a/sass/_vars.scss b/sass/_vars.scss index 2e584d1da..481298fa8 100644 --- a/sass/_vars.scss +++ b/sass/_vars.scss @@ -1,81 +1,110 @@ /* Copyright: Ankitects Pty Ltd and contributors - * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ + * License: GNU AGPL, version 3 or later, http://www.gnu.org/licenses/agpl.html */ + +@use "colors" as *; + +@function get($map, $keys...) { + @each $key in $keys { + $map: map-get($map, $key); + } + @return $map; +} + +$vars: ( + props: ( + border-radius-default: 5px, + ), + themes: ( + light: ( + text-fg: get($color, gray, 9), + window-bg: get($color, gray, 1), + frame-bg: white, + border: get($color, gray, 4), + faint-border: get($color, gray, 2), + link: get($color, blue, 9), + review-count: #0a0, + new-count: get($color, blue, 9), + learn-count: get($color, orange, 7), + zero-count: get($color, gray, 2), + slightly-grey-text: get($color, gray, 8), + highlight-bg: get($color, cyan, 2), + highlight-fg: black, + disabled: get($color, gray, 5), + flag1-fg: get($color, red, 6), + flag2-fg: get($color, orange, 5), + flag3-fg: get($color, green, 5), + flag4-fg: get($color, blue, 5), + flag5-fg: get($color, fuchsia, 4), + flag6-fg: get($color, teal, 5), + flag7-fg: get($color, purple, 6), + flag1-bg: get($color, red, 5), + flag2-bg: get($color, orange, 3), + flag3-bg: get($color, green, 3), + flag4-bg: get($color, blue, 3), + flag5-bg: get($color, fuchsia, 2), + flag6-bg: get($color, teal, 4), + flag7-bg: get($color, purple, 4), + buried-fg: get($color, amber, 5), + suspended-fg: get($color, yellow, 4), + suspended-bg: get($color, yellow, 1), + marked-bg: get($color, indigo, 2), + tooltip-bg: get($color, gray, 0), + focus-border: get($color, indigo, 6), + ), + dark: ( + text-fg: get($color, gray, 0), + window-bg: get($color, gray, 7), + frame-bg: get($color, gray, 6), + border: get($color, gray, 9), + faint-border: get($color, gray, 8), + link: get($color, blue, 2), + review-count: get($color, green, 4), + new-count: get($color, blue, 2), + learn-count: get($color, red, 4), + zero-count: get($color, gray, 7), + slightly-grey-text: get($color, gray, 4), + highlight-bg: get($color, cyan, 2), + highlight-fg: white, + disabled: get($color, gray, 6), + flag1-fg: get($color, red, 4), + flag2-fg: get($color, orange, 4), + flag3-fg: get($color, green, 4), + flag4-fg: get($color, blue, 4), + flag5-fg: get($color, fuchsia, 3), + flag6-fg: get($color, teal, 4), + flag7-fg: get($color, purple, 5), + flag1-bg: get($color, red, 6), + flag2-bg: get($color, orange, 5), + flag3-bg: get($color, green, 5), + flag4-bg: get($color, blue, 5), + flag5-bg: get($color, fuchsia, 5), + flag6-bg: get($color, teal, 5), + flag7-bg: get($color, purple, 7), + buried-fg: get($color, amber, 8), + suspended-fg: get($color, yellow, 1), + suspended-bg: get($color, yellow, 5), + marked-bg: get($color, purple, 5), + tooltip-bg: get($color, gray, 8), + focus-border: get($color, indigo, 5), + ), + ), +); :root { - --text-fg: black; - --window-bg: #ececec; - --frame-bg: white; - --border: #aaa; - --medium-border: #b6b6b6; - --faint-border: #e7e7e7; - --link: #00a; - --review-count: #0a0; - --new-count: #00a; - --learn-count: #c35617; - --zero-count: #ddd; - --slightly-grey-text: #333; - --highlight-bg: #77ccff; - --highlight-fg: black; - --disabled: #777; - --flag1-fg: #e25252; - --flag2-fg: #ffb347; - --flag3-fg: #54c414; - --flag4-fg: #578cff; - --flag5-fg: #ff82ee; - --flag6-fg: #00d1b5; - --flag7-fg: #9649dd; - --flag1-bg: #ff9b9b; - --flag2-bg: #ffb347; - --flag3-bg: #93e066; - --flag4-bg: #9dbcff; - --flag5-bg: #f5a8eb; - --flag6-bg: #7edbd7; - --flag7-bg: #cca3f1; - --buried-fg: #aaaa33; - --suspended-fg: #dd0; - --suspended-bg: #ffffb2; - --marked-bg: #cce; - --tooltip-bg: #fcfcfc; - --focus-border: #0969da; - --focus-shadow: rgba(9 105 218 / 0.3); + @each $name, $value in get($vars, themes, light) { + --#{$name}: #{$value}; + } + &.night-mode { + @each $name, $value in get($vars, themes, dark) { + --#{$name}: #{$value}; + } + color-scheme: dark; + } } -:root[class*="night-mode"] { - --text-fg: white; - --window-bg: #2f2f31; - --frame-bg: #3a3a3a; - --border: #777; - --medium-border: #444; - --faint-border: #29292b; - --link: #77ccff; - --review-count: #5ccc00; - --new-count: #77ccff; - --learn-count: #ff935b; - --zero-count: #444; - --slightly-grey-text: #ccc; - --highlight-bg: #77ccff; - --highlight-fg: white; - --disabled: #777; - --flag1-fg: #ff7b7b; - --flag2-fg: #f5aa41; - --flag3-fg: #86ce5d; - --flag4-fg: #6f9dff; - --flag5-fg: #f097e4; - --flag6-fg: #5ccfca; - --flag7-fg: #9f63d3; - --flag1-bg: #aa5555; - --flag2-bg: #ac653a; - --flag3-bg: #559238; - --flag4-bg: #506aa3; - --flag5-bg: #975d8f; - --flag6-bg: #399185; - --flag7-bg: #624b77; - --buried-fg: #777733; - --suspended-fg: #ffffb2; - --suspended-bg: #aaaa33; - --marked-bg: #77c; - --tooltip-bg: #272727; - --focus-border: #316dca; - --focus-shadow: #194380; - color-scheme: dark; +/* separate rule for cleaner dev tools appearance */ +:root { + @each $name, $value in get($vars, props) { + --#{$name}: #{$value}; + } }