# Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html from __future__ import annotations from typing import Any import aqt from anki.sync import SyncStatus from aqt import gui_hooks from aqt.qt import * from aqt.sync import get_sync_status from aqt.utils import tr from aqt.webview import AnkiWebView # wrapper class for set_bridge_command() class TopToolbar: def __init__(self, toolbar: Toolbar) -> None: self.toolbar = toolbar # wrapper class for set_bridge_command() class BottomToolbar: def __init__(self, toolbar: Toolbar) -> None: self.toolbar = toolbar class Toolbar: def __init__(self, mw: aqt.AnkiQt, web: AnkiWebView) -> None: self.mw = mw self.web = web self.link_handlers: dict[str, Callable] = { "study": self._studyLinkHandler, } self.web.setFixedHeight(30) self.web.requiresCol = False def draw( self, buf: str = "", web_context: Any | None = None, link_handler: Callable[[str], Any] | None = None, ) -> None: web_context = web_context or TopToolbar(self) link_handler = link_handler or self._linkHandler self.web.set_bridge_command(link_handler, web_context) self.web.stdHtml( self._body % self._centerLinks(), css=["css/toolbar.css"], js=["js/vendor/jquery.min.js", "js/toolbar.js"], context=web_context, ) self.web.adjustHeightToFit() def redraw(self) -> None: self.set_sync_active(self.mw.media_syncer.is_syncing()) self.update_sync_status() gui_hooks.top_toolbar_did_redraw(self) # Available links ###################################################################### def create_link( self, cmd: str, label: str, func: Callable, tip: str | None = None, id: str | None = None, ) -> str: """Generates HTML link element and registers link handler Arguments: cmd {str} -- Command name used for the JS → Python bridge label {str} -- Display label of the link func {Callable} -- Callable to be called on clicking the link Keyword Arguments: tip {Optional[str]} -- Optional tooltip text to show on hovering over the link (default: {None}) id: {Optional[str]} -- Optional id attribute to supply the link with (default: {None}) Returns: str -- HTML link element """ self.link_handlers[cmd] = func title_attr = f'title="{tip}"' if tip else "" id_attr = f'id="{id}"' if id else "" return ( f"""""" f"""{label}""" ) def _centerLinks(self) -> str: links = [ self.create_link( "decks", tr.actions_decks(), self._deckLinkHandler, tip=tr.actions_shortcut_key(val="D"), id="decks", ), self.create_link( "add", tr.actions_add(), self._addLinkHandler, tip=tr.actions_shortcut_key(val="A"), id="add", ), self.create_link( "browse", tr.qt_misc_browse(), self._browseLinkHandler, tip=tr.actions_shortcut_key(val="B"), id="browse", ), self.create_link( "stats", tr.qt_misc_stats(), self._statsLinkHandler, tip=tr.actions_shortcut_key(val="T"), id="stats", ), ] links.append(self._create_sync_link()) gui_hooks.top_toolbar_did_init_links(links, self) return "\n".join(links) # Sync ###################################################################### def _create_sync_link(self) -> str: name = tr.qt_misc_sync() title = tr.actions_shortcut_key(val="Y") label = "sync" self.link_handlers[label] = self._syncLinkHandler return f""" {name} """ def set_sync_active(self, active: bool) -> None: method = "add" if active else "remove" self.web.eval( f"document.getElementById('sync-spinner').classList.{method}('spin')" ) def set_sync_status(self, status: SyncStatus) -> None: self.web.eval(f"updateSyncColor({status.required})") def update_sync_status(self) -> None: get_sync_status(self.mw, self.mw.toolbar.set_sync_status) # Link handling ###################################################################### def _linkHandler(self, link: str) -> bool: if link in self.link_handlers: self.link_handlers[link]() return False def _deckLinkHandler(self) -> None: self.mw.moveToState("deckBrowser") def _studyLinkHandler(self) -> None: # if overview already shown, switch to review if self.mw.state == "overview": self.mw.col.startTimebox() self.mw.moveToState("review") else: self.mw.onOverview() def _addLinkHandler(self) -> None: self.mw.onAddCard() def _browseLinkHandler(self) -> None: self.mw.onBrowse() def _statsLinkHandler(self) -> None: self.mw.onStats() def _syncLinkHandler(self) -> None: self.mw.on_sync_button_clicked() # HTML & CSS ###################################################################### _body = """
""" # Bottom bar ###################################################################### class BottomBar(Toolbar): _centerBody = """
""" def draw( self, buf: str = "", web_context: Any | None = None, link_handler: Callable[[str], Any] | None = None, ) -> None: # note: some screens may override this web_context = web_context or BottomToolbar(self) link_handler = link_handler or self._linkHandler self.web.set_bridge_command(link_handler, web_context) self.web.stdHtml( self._centerBody % buf, css=["css/toolbar.css", "css/toolbar-bottom.css"], context=web_context, ) self.web.adjustHeightToFit()