diff options
author | Thanos Apollo <[email protected]> | 2022-08-04 09:50:48 +0300 |
---|---|---|
committer | Thanos Apollo <[email protected]> | 2022-08-04 09:50:48 +0300 |
commit | 4ddb7273098bee179bb77e0937e560fc0100960c (patch) | |
tree | cebc2f9412e45910408a7885ca78d7dedb77de78 /.local/share/Anki2/addons21/anki_reworked/__init__.py | |
parent | e83759ae9d0513024e390810ddcb18ffdd84675e (diff) |
Add anki addons
Diffstat (limited to '.local/share/Anki2/addons21/anki_reworked/__init__.py')
-rw-r--r-- | .local/share/Anki2/addons21/anki_reworked/__init__.py | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/.local/share/Anki2/addons21/anki_reworked/__init__.py b/.local/share/Anki2/addons21/anki_reworked/__init__.py new file mode 100644 index 0000000..81b2627 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/__init__.py @@ -0,0 +1,705 @@ +import json +### Custom util functions +from .utils.modules import * +### Logger for debuging +from .utils.logger import logger + +from typing import Any, List, Optional +from PyQt5.QtWidgets import QWidget +from platform import system, version, release +from ctypes import * +# import the main window object (mw) from aqt +from aqt import AnkiQt, DialogManager, mw +from aqt.theme import theme_manager, colors +from aqt import gui_hooks +## QT dialog windows +# Browser import legacy check (2.1.22) +if module_exists("aqt.browser.browser"): + from aqt.browser.browser import Browser +else: + from aqt.browser import Browser +# DeckStats import legacy check +if module_has_attribute("aqt.stats", "NewDeckStats"): + from aqt.stats import DeckStats, NewDeckStats +else: + from aqt.stats import DeckStats +from aqt.addcards import AddCards +from aqt.editcurrent import EditCurrent +from aqt.about import ClosableQDialog +from aqt.preferences import Preferences +from aqt.addons import AddonsDialog +# FilteredDeckConfigDialog import non-legacy check +if module_exists("aqt.filtered_deck"): + from aqt.filtered_deck import FilteredDeckConfigDialog +# QT page views +from aqt.toolbar import Toolbar, TopToolbar +from aqt.deckbrowser import DeckBrowser, DeckBrowserBottomBar +from aqt.overview import Overview, OverviewBottomBar +from aqt.editor import Editor +from aqt.reviewer import Reviewer, ReviewerBottomBar +from aqt.webview import AnkiWebView, WebContent + +### Load config data here +from .config import config, write_config, get_config + +## Addon compatibility fixes +# More Overview Stats 2.1 addon compatibility fix +addon_more_overview_stats_fix = config['addon_more_overview_stats'] +addon_advanced_review_bottom_bar = config['addon_advanced_review_bottom_bar'] + +## Customization +theme = config['theme'] +# Init script/file path +from .utils.css_files import css_files_dir +from .utils.themes import themes, write_theme, get_theme +logger.debug(css_files_dir) +logger.debug(themes) +themes_parsed = get_theme(theme) +color_mode = 2 if theme_manager.get_night_mode() else 1 # 1 = light and 2 = dark + +dwmapi = None +## Darkmode windows titlebar thanks to miere43 +def set_dark_titlebar(window, dwmapi) -> None: + if dwmapi: + handler_window = c_void_p(int(window.winId())) + DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = c_int(19) + DWMWA_USE_IMMERSIVE_DARK_MODE = c_int(20) + windows_version = int(version().split('.')[2]) + attribute = DWMWA_USE_IMMERSIVE_DARK_MODE if windows_version >= 18985 else DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 + if windows_version >= 17763 and int(release()) >= 10: + dwmapi.DwmSetWindowAttribute(handler_window, attribute, byref(c_int(1)), c_size_t(4)) +def set_dark_titlebar_qt(obj, dwmapi, fix=True) -> None: + if dwmapi and theme_manager.get_night_mode(): + set_dark_titlebar(obj, dwmapi) + # Trick to refresh the titlebar after dark titlebar is set + if fix: + obj.showFullScreen() + obj.showNormal() +if system() == "Windows" and theme_manager.get_night_mode(): + dwmapi = WinDLL("dwmapi") + if dwmapi: + dwmapi.DwmSetWindowAttribute.argtypes = [c_void_p, c_int, c_void_p, c_size_t] + dwmapi.DwmSetWindowAttribute.restype = c_int + set_dark_titlebar(mw, dwmapi) +logger.debug(dwmapi) + +### CSS injections +def load_custom_style(): + theme_colors = "" + for color_name in themes_parsed.get("colors"): + color = themes_parsed.get("colors").get(color_name) + if color[3]: + theme_colors += f"{color[3]}: {color[color_mode]};\n " + else: + theme_colors += f"--{color_name.lower().replace('_','-')}: {color[color_mode]};\n " + custom_style = """ +<style> + :root, + :root .isMac, + :root .isWin, + :root .isLin { + %s + } + html { + font-family: %s; + font-size: %spx; + } +</style> + """ % (theme_colors, config["font"], config["font_size"]) + return custom_style + +## Adds styling on the different webview contents, before the content is set +def on_webview_will_set_content(web_content: WebContent, context: Optional[Any]) -> None: + logger.debug(context) # Logs content being loaded, find out the instance + web_content.css.append(css_files_dir['global']) # Global css + web_content.head += load_custom_style() # Custom styling + # Deckbrowser + if isinstance(context, DeckBrowser): + web_content.css.append(css_files_dir['DeckBrowser']) + # TopToolbar + elif isinstance(context, TopToolbar): + web_content.css.append(css_files_dir['TopToolbar']) + # BottomToolbar (Buttons) + elif isinstance(context, DeckBrowserBottomBar) or isinstance(context, OverviewBottomBar): + web_content.css.append(css_files_dir['BottomBar']) + # Overview + elif isinstance(context, Overview): + if addon_more_overview_stats_fix: + web_content.head += "<style>center > table tr:first-of-type {display: table-row; flex-direction: unset;}</style>" + web_content.css.append(css_files_dir['Overview']) + # Editor + elif isinstance(context, Editor): + web_content.css.append(css_files_dir['Editor']) + # Reviewer + elif isinstance(context, Reviewer): + web_content.css.append(css_files_dir['Reviewer']) + elif isinstance(context, ReviewerBottomBar): + if addon_advanced_review_bottom_bar: + #web_content.head += "<style>td.stat[align='left']:nth-of-type(2) {position: absolute; z-index: 1;}</style>" + web_content.body += "<script>const center = document.getElementById('outer');center.classList.add('arbb');</script>" + else: + web_content.css.append(css_files_dir['BottomBar']) + web_content.css.append(css_files_dir['ReviewerBottomBar']) + # Button padding bottom + web_content.body += "<div style='height: 9px; opacity: 0; pointer-events: none;'></div>" + web_content.body += "<div id='padFix' style='height: 30px; opacity: 0; pointer-events: none;'><script>const e = document.getElementById('padFix');e.parentElement.removeChild(e);</script></div>" + mw.bottomWeb.adjustHeightToFit() + # CardLayout + elif context_name_includes(context, "aqt.clayout.CardLayout"): + web_content.css.append(css_files_dir['CardLayout']) + ## Legacy webviews + # ResetRequired on card edit (legacy) + elif context_name_includes(context, "aqt.main.ResetRequired"): + web_content.css.append(css_files_dir['legacy']) +gui_hooks.webview_will_set_content.append(on_webview_will_set_content) + +# TopToolbar styling fix through height change by adding <br> tag +def redraw_toolbar() -> None: + # Reload the webview content with added <br/> tag, making the bar larger in height + mw.toolbar.web.setFixedHeight(60) + mw.toolbar.web.eval(""" + var br = document.createElement("br"); + document.body.appendChild(br); + """) + + if 'Qt6' in QPalette.ColorRole.__module__: + mw.toolbar.web.eval(""" + var div = document.createElement("div"); + div.style.width = "5px"; + div.style.height = "10px"; + document.body.appendChild(div); + """) + # Auto adjust the height, then redraw the toolbar + mw.toolbar.web.adjustHeightToFit() + mw.toolbar.redraw() + +def redraw_toolbar_legacy(links: List[str], _: Toolbar) -> None: + # Utilizing the link hook, we inject <br/> tag through javascript + inject_br = """ + <script> + while (document.body.querySelectorAll("br").length > 1) + document.body.querySelectorAll("br")[0].remove(); + var br = document.createElement("br"); + document.body.appendChild(br); + </script> + """ + mw.toolbar.web.setFixedHeight(60) + links.append(inject_br) + mw.toolbar.web.adjustHeightToFit() + +if attribute_exists(gui_hooks, "main_window_did_init"): + pass + #gui_hooks.main_window_did_init.append(redraw_toolbar) +elif attribute_exists(gui_hooks, "top_toolbar_did_init_links"): + gui_hooks.top_toolbar_did_init_links.append(redraw_toolbar_legacy) + pass + +# Dialog window styling +def on_dialog_manager_did_open_dialog(dialog_manager: DialogManager, dialog_name: str, dialog_instance: QWidget) -> None: + logger.debug(dialog_name) + dialog: AnkiQt = dialog_manager._dialogs[dialog_name][1] + # If dwmapi found and nightmode is enabled, set dark titlebar to dialog window + set_dark_titlebar_qt(dialog, dwmapi) + # AddCards + if dialog_name == "AddCards": + context: AddCards = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QAddCards'], encoding='utf-8').read()) + # Addons popup + elif dialog_name == "AddonsDialog": + context: AddonsDialog = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QAddonsDialog'], encoding='utf-8').read()) + # Browser + elif dialog_name == "Browser": + context: Browser = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QBrowser'], encoding='utf-8').read()) + pass + # EditCurrent + elif dialog_name == "EditCurrent": + context: EditCurrent = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QEditCurrent'], encoding='utf-8').read()) + # FilteredDeckConfigDialog + elif module_exists("aqt.filtered_deck") and dialog_name == "FilteredDeckConfigDialog": + context: FilteredDeckConfigDialog = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QFilteredDeckConfigDialog'], encoding='utf-8').read()) + # Statistics / NewDeckStats + elif dialog_name == "NewDeckStats": + context: NewDeckStats = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QNewDeckStats'], encoding='utf-8').read()) + # About + elif dialog_name == "About": + context: ClosableQDialog = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QAbout'], encoding='utf-8').read()) + # Preferences + elif dialog_name == "Preferences": + context: Preferences = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QPreferences'], encoding='utf-8').read()) + # sync_log - kore ha nani desu??? + elif dialog_name == "sync_log": + pass + +if attribute_exists(gui_hooks, "dialog_manager_did_open_dialog"): + gui_hooks.dialog_manager_did_open_dialog.append(on_dialog_manager_did_open_dialog) +else: + ## Legacy dialog window styling + # Sad monkey patch, instead of hooks :c + # setupDialogGC is being called on almost all dialog windows, utilizing this, the + # function is used as a type of hook to inject CSS styling on the QT instances + def monkey_setup_dialog_gc(obj: Any) -> None: + obj.finished.connect(lambda: mw.gcWindow(obj)) + logger.debug(obj) + set_dark_titlebar_qt(obj, dwmapi) + # AddCards + if isinstance(obj, AddCards): + obj.setStyleSheet(open(css_files_dir['QAddCards'], encoding='utf-8').read()) + # EditCurrent + elif isinstance(obj, EditCurrent): + obj.setStyleSheet(open(css_files_dir['QEditCurrent'], encoding='utf-8').read()) + # Statistics / DeckStats + elif isinstance(obj, DeckStats): + obj.setStyleSheet(open(css_files_dir['QNewDeckStats'], encoding='utf-8').read()) + # About + elif isinstance(obj, ClosableQDialog): + obj.setStyleSheet(open(css_files_dir['QAbout'], encoding='utf-8').read()) + # Preferences + ## Haven't found a solution for preferences yet :c + mw.setupDialogGC = monkey_setup_dialog_gc # Should be rare enough for other addons to also patch this I hope. + + # Addons popup + if attribute_exists(gui_hooks, "addons_dialog_will_show"): + def on_addons_dialog_will_show(dialog: AddonsDialog) -> None: + logger.debug(dialog) + set_dark_titlebar_qt(dialog, dwmapi) + dialog.setStyleSheet(open(css_files_dir['QAddonsDialog'], encoding='utf-8').read()) + gui_hooks.addons_dialog_will_show.append(on_addons_dialog_will_show) + # Browser + if attribute_exists(gui_hooks, "browser_will_show"): + def on_browser_will_show(browser: Browser) -> None: + logger.debug(browser) + set_dark_titlebar_qt(browser, dwmapi) + browser.setStyleSheet(open(css_files_dir['QBrowser'], encoding='utf-8').read()) + gui_hooks.browser_will_show.append(on_browser_will_show) + +## CONFIG DIALOG +from aqt import mw, gui_hooks +from aqt.qt import * +from .config import config +from aqt.utils import showInfo +from aqt.webview import AnkiWebView + +class ThemeEditor(QDialog): + def __init__(self, parent, *args, **kwargs): + super().__init__(parent=parent or mw, *args, **kwargs) + self.config_editor = parent + self.setWindowModality(Qt.ApplicationModal) + self.setWindowTitle(f'Anki-redesign Advanced Editor') + self.setSizePolicy(self.make_size_policy()) + self.setMinimumSize(420, 420) + self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) + set_dark_titlebar_qt(self, dwmapi, fix=False) + # Root layout + self.root_layout = QVBoxLayout(self) + # Main layout + self.layout = QVBoxLayout() + self.textedit = QTextEdit() + themes_plaintext = open(themes[theme], encoding='utf-8').read() + self.textedit.setPlainText(themes_plaintext) + self.layout.addWidget(self.textedit) + self.root_layout.addLayout(self.layout) + self.root_layout.addLayout(self.make_button_box()) + + def save_edit(self) -> None: + themes_parsed = json.loads(self.textedit.toPlainText()) + write_theme(themes[theme], themes_parsed) + self.config_editor.update() + self.accept() + + def make_button_box(self) -> QWidget: + def cancel(): + button = QPushButton('Cancel') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + qconnect(button.clicked, self.accept) + return button + def save(): + button = QPushButton('Save') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + button.setDefault(True) + button.setShortcut("Ctrl+Return") + button.clicked.connect(lambda _: self.save_edit()) + return button + + button_box = QHBoxLayout() + button_box.addStretch() + button_box.addWidget(cancel()) + button_box.addWidget(save()) + return button_box + + def make_size_policy(self) -> QSizePolicy: + size_policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) + return size_policy + +class ConfigDialog(QDialog): + def __init__(self, parent: QWidget, *args, **kwargs): + super().__init__(parent=parent or mw, *args, **kwargs) + self.setWindowModality(Qt.ApplicationModal) + self.setWindowTitle(f'Anki-redesign Configuration') + self.setSizePolicy(self.make_size_policy()) + self.setMinimumSize(420, 580) + self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) + set_dark_titlebar_qt(self, dwmapi, fix=False) + + # Color/theme + ## Loads theme color + self.theme_colors = themes_parsed.get("colors") + self.updates = [] + self.theme_general = ["TEXT_FG", "WINDOW_BG", "FRAME_BG", "BUTTON_BG", "TOOLTIP_BG", "BORDER", "MEDIUM_BORDER", "FAINT_BORDER", "HIGHLIGHT_BG", "HIGHLIGHT_FG" , "LINK", "DISABLED", "SLIGHTLY_GREY_TEXT", "PRIMARY_COLOR"] + self.theme_decks = ["CURRENT_DECK", "NEW_COUNT", "LEARN_COUNT", "REVIEW_COUNT", "ZERO_COUNT"] + self.theme_browse = ["BURIED_FG", "SUSPENDED_FG", "MARKED_BG", "FLAG1_BG", "FLAG1_FG", "FLAG2_BG", "FLAG2_FG", "FLAG3_BG", "FLAG3_FG", "FLAG4_BG", "FLAG4_FG", "FLAG5_BG", "FLAG5_FG", "FLAG6_BG", "FLAG6_FG", "FLAG7_BG", "FLAG7_FG"] + self.theme_extra = [] + + # Root layout + self.root_layout = QVBoxLayout(self) + # Main layout + self.layout = QVBoxLayout() + ## Initialize tab screen + self.tabs = QTabWidget(objectName="tabs") + self.tabs.setFocusPolicy(Qt.FocusPolicy.StrongFocus) + self.tab_general = QWidget(objectName="general") + self.tab_general.setLayout(self.create_color_picker_layout(self.theme_general)) + self.tab_decks = QWidget(objectName="decks") + self.tab_decks.setLayout(self.create_color_picker_layout(self.theme_decks)) + self.tab_browse = QWidget(objectName="browse") + self.tab_browse.setLayout(self.create_color_picker_layout(self.theme_browse)) + self.tab_extra = QWidget(objectName="extra") + self.tab_extra.setLayout(self.create_color_picker_layout(self.theme_extra)) + + self.tab_settings = QWidget(objectName="settings") + self.settings_layout = QFormLayout() + self.theme_label = QLabel("Theme:") + self.theme_label.setStyleSheet('QLabel { font-size: 14px; font-weight: bold }') + self.settings_layout.addRow(self.theme_label) + for key in themes: + self.radio = self.radio_button(key) + self.settings_layout.addRow(key, self.radio) + self.settings_layout.addRow(QLabel()) + + self.font_label = QLabel("Font:") + self.font_label.setStyleSheet('QLabel { font-size: 14px; font-weight: bold }') + self.settings_layout.addRow(self.font_label) + self.interface_font = QFontComboBox() + self.interface_font.setFixedWidth(200) + self.interface_font.setCurrentFont(QFont(config["font"])) + self.settings_layout.addRow(self.interface_font) + + self.font_size = QSpinBox() + self.font_size.setFixedWidth(200) + self.font_size.setValue(config["font_size"]) + self.font_size.setSuffix("px") + self.settings_layout.addRow(self.font_size) + + self.settings_layout.addRow(QLabel()) + + + self.fix_label = QLabel("Addon-Compatibility Fixes: ") + self.fix_label.setStyleSheet('QLabel { font-size: 14px; font-weight: bold }') + self.settings_layout.addRow(self.fix_label) + self.reload_theme = self.checkbox("theme_reload") + self.settings_layout.addRow("QT6 theme on-start reload fix", self.reload_theme) + self.addon_more_overview_stats_check = self.checkbox("addon_more_overview_stats") + self.settings_layout.addRow("More Overview Stats 21", self.addon_more_overview_stats_check) + self.addon_advanced_review_bottom_bar_check = self.checkbox("addon_advanced_review_bottom_bar") + self.settings_layout.addRow("Advanced Review Bottom Bar", self.addon_advanced_review_bottom_bar_check) + + self.tab_settings.setLayout(self.settings_layout) + + ## Add tabs + self.tabs.resize(300,200) + self.tabs.addTab(self.tab_settings,"Settings") + self.tabs.addTab(self.tab_general,"General") + self.tabs.addTab(self.tab_decks,"Decks") + self.tabs.addTab(self.tab_browse,"Browse") + #self.tabs.addTab(self.tab_extra,"Extra") + + ## Add tabs to widget + self.layout.addWidget(self.tabs) + + self.root_layout.addLayout(self.layout) + self.root_layout.addLayout(self.make_button_box()) + self.setLayout(self.root_layout) + self.show() + + def update(self) -> None: + global themes_parsed + themes_parsed = get_theme(theme) + self.theme_colors = themes_parsed.get("colors") + for update in self.updates: + update() + + def checkbox(self, key: str) -> QCheckBox: + checkbox = QCheckBox() + + def update() -> None: + value = config[key] + checkbox.setChecked(value) + + self.updates.append(update) + update() + return checkbox + + def radio_button(self, key: str) -> QRadioButton: + radio = QRadioButton() + + def update() -> None: + if theme == key: + radio.setChecked(True) + elif radio.isChecked(): + radio.setChecked(False) + + def toggle(checked) -> None: + global theme + if checked: + theme = key + self.update() + + self.updates.append(update) + radio.toggled.connect(lambda checked: toggle(checked)) + update() + return radio + + def color_input(self, key: str) -> QPushButton: + button = QPushButton() + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + button.setFixedWidth(25) + button.setFixedHeight(25) + + color_dialog = QColorDialog(self) + + def set_color(rgb: str) -> None: + # Check for valid color + color = QColor() + color.setNamedColor(rgb) + if not color.isValid(): + return + # Update color + color_dialog.setCurrentColor(color) + button.setStyleSheet('QPushButton{ background-color: "%s"; border: none; border-radius: 2px}' % rgb) + + def update() -> None: + rgb = self.theme_colors.get(key)[color_mode] + set_color(rgb) + + def save(color: QColor) -> None: + rgb = color.name(QColor.NameFormat.HexRgb) + self.theme_colors[key][color_mode] = rgb + set_color(rgb) + + self.updates.append(update) + color_dialog.colorSelected.connect(lambda color: save(color)) + button.clicked.connect(lambda _: color_dialog.exec()) + return button + + def create_color_picker_layout(self, colors) -> None: + layout = QFormLayout() + for key in colors: + self.test = self.color_input(key) + layout.addRow(self.theme_colors.get(key)[0], self.test) + return layout + + def theme_file_editor(self) -> None: + diag = ThemeEditor(self) + diag.show() + + def make_button_box(self) -> QWidget: + def advanced(): + button = QPushButton('Advanced') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + qconnect(button.clicked, self.theme_file_editor) + return button + def cancel(): + button = QPushButton('Cancel') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + qconnect(button.clicked, self.accept) + return button + def save(): + button = QPushButton('Save') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + button.setDefault(True) + button.setShortcut("Ctrl+Return") + button.clicked.connect(lambda _: self.save()) + return button + + button_box = QHBoxLayout() + button_box.addWidget(advanced()) + button_box.addStretch() + button_box.addWidget(cancel()) + button_box.addWidget(save()) + return button_box + + def make_size_policy(self) -> QSizePolicy: + size_policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) + return size_policy + + def save(self) -> None: + # Save settings and update config + global config, color_mode + config["font"] = self.interface_font.currentFont().family() + config["font_size"] = self.font_size.value() + config['addon_more_overview_stats'] = self.addon_more_overview_stats_check.isChecked() + config['addon_advanced_review_bottom_bar'] = self.addon_advanced_review_bottom_bar_check.isChecked() + config['theme_reload'] = self.reload_theme.isChecked() + config["theme"] = theme + write_config(config) + config = get_config() + + # Write and update theme + color_mode = 2 if theme_manager.get_night_mode() else 1 # 1 = light and 2 = dark + themes_parsed["colors"] = self.theme_colors + write_theme(themes[theme], themes_parsed) + update_theme() + + # mw.reset() + # ShowInfo for both new and legacy support + showInfo(_("Changes will take effect when you restart Anki.")) + #showInfo(tr.preferences_changes_will_take_effect_when_you()) + self.accept() + +def check_legacy_colors() -> None: + try: + _ = colors.items() + except: + return False + return True + +def refresh_all_windows() -> None: + # Redraw top toolbar + mw.toolbar.draw() + if attribute_exists(gui_hooks, "top_toolbar_did_init_links"): + gui_hooks.top_toolbar_did_init_links.append(lambda a,b: [redraw_toolbar_legacy(a,b), gui_hooks.top_toolbar_did_init_links.remove(print)]) + + # Redraw main body + if mw.state == "review": + mw.reviewer._initWeb() + if mw.reviewer.state == "question": + mw.reviewer._showQuestion() + else: + mw.reviewer._showAnswer() + elif mw.state == "overview": + mw.overview.refresh() + elif mw.state == "deckBrowser": + mw.deckBrowser.show() + + if attribute_exists(gui_hooks, "top_toolbar_did_init_links"): + gui_hooks.top_toolbar_did_init_links.remove(redraw_toolbar) + +def update_theme() -> None: + themes_parsed = get_theme(theme) + theme_colors = themes_parsed.get("colors") + # Apply theme on colors + ncolors = {} + # Legacy color check + logger.debug(dir(colors)) + legacy = check_legacy_colors() + for color_name in theme_colors: + c = theme_colors.get(color_name) + ncolors[color_name] = c[color_mode] + if legacy: + colors[f"day{c[3].replace('--','-')}"] = c[1] + colors[f"night{c[3].replace('--','-')}"] = c[2] + # Potentially add fusion fixes too? + else: + logger.debug(getattr(colors, color_name, False)) + if getattr(colors, color_name, False): + setattr(colors, color_name, (c[1], c[2])) + # Apply theme on palette + apply_theme(ncolors) + refresh_all_windows() + +def apply_theme(colors) -> None: + # Load palette + palette = QPalette() + # QT mappings + #logger.debug(QPalette.ColorRole.__dict__) + color_map = { + QPalette.ColorRole.Window: "WINDOW_BG", + QPalette.ColorRole.WindowText: "TEXT_FG", + QPalette.ColorRole.Base: "FRAME_BG", + QPalette.ColorRole.AlternateBase: "WINDOW_BG", + QPalette.ColorRole.ToolTipBase: "TOOLTIP_BG", + QPalette.ColorRole.ToolTipText: "TEXT_FG", + QPalette.ColorRole.Text: "TEXT_FG", + QPalette.ColorRole.Button: "BUTTON_BG", + QPalette.ColorRole.ButtonText: "TEXT_FG", + QPalette.ColorRole.BrightText: "HIGHLIGHT_FG", + QPalette.ColorRole.HighlightedText: "HIGHLIGHT_FG", + QPalette.ColorRole.Link: "LINK", + QPalette.ColorRole.NoRole: "WINDOW_BG", + } + for color_role in color_map: + palette.setColor(color_role, QColor(colors[color_map[color_role]])) + + highlight_bg = QColor(colors["HIGHLIGHT_BG"]) + highlight_bg.setAlpha(64) + palette.setColor(QPalette.ColorRole.Highlight, highlight_bg) + + disabled_color = QColor(colors["DISABLED"]) + palette.setColor(QPalette.ColorRole.PlaceholderText, disabled_color) + palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_color) + palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_color) + palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, disabled_color) + + # Update palette + mw.app.setPalette(palette) + theme_manager.default_palette = palette + theme_manager._apply_style(mw.app) + + # Update webview bg + AnkiWebView._getWindowColor = lambda *args: QColor(colors["WINDOW_BG"]) + AnkiWebView.get_window_bg_color = lambda *args: QColor(colors["WINDOW_BG"]) + +# Create menu actions +def create_menu_action(parent: QWidget, dialog_class: QDialog, dialog_name: str) -> QAction: + def open_dialog(): + dialog = dialog_class(mw) + return dialog.exec() + + action = QAction(dialog_name, parent) + #qconnect(action.triggered, open_dialog) + action.triggered.connect(open_dialog) + return action + +# Load in the Anki-redesign menu +if not hasattr(mw, 'anki_redesign'): + mw.anki_redesign = QMenu("&Anki-redesign", mw) + mw.form.menubar.insertMenu(mw.form.menuHelp.menuAction(), mw.anki_redesign) + + mw.anki_redesign.addAction(create_menu_action(mw.anki_redesign, ConfigDialog, "&Config")) + mw.anki_redesign.addSeparator() + mw.reset() + update_theme() + mw.reset() + + # Rereload view to fix theme change on startup + if 'Qt6' in QPalette.ColorRole.__module__: + logger.debug('QT6 DETECTED....') + config = get_config() + if config["theme_reload"]: + update_theme() + +def on_theme_did_change() -> None: + global color_mode + color_mode = 2 if theme_manager.get_night_mode() else 1 # 1 = light and 2 = dark + update_theme() + logger.debug("THEME CHANGEEEED") + refresh_all_windows() + mw.reset() + update_theme() + + +if attribute_exists(gui_hooks, "theme_did_change"): + gui_hooks.theme_did_change.append(on_theme_did_change) + logger.debug("YEP")
\ No newline at end of file |