mirror of
https://github.com/ankitects/anki.git
synced 2025-09-22 07:52:24 -04:00
Merge pull request #448 from glutanimate/top_toolbar_links_hook
Add a hook for adding links to the top toolbar
This commit is contained in:
commit
b3585502cd
3 changed files with 124 additions and 25 deletions
|
@ -1294,6 +1294,41 @@ class _StyleDidInitFilter:
|
||||||
style_did_init = _StyleDidInitFilter()
|
style_did_init = _StyleDidInitFilter()
|
||||||
|
|
||||||
|
|
||||||
|
class _TopToolbarDidInitLinksHook:
|
||||||
|
"""Used to modify or add links in the top toolbar of Anki's main window
|
||||||
|
|
||||||
|
'links' is a list of HTML link elements. Add-ons can generate their own links
|
||||||
|
by using aqt.toolbar.Toolbar.create_link. Links created in that way can then be
|
||||||
|
appended to the link list, e.g.:
|
||||||
|
|
||||||
|
def on_top_toolbar_did_init_links(links, toolbar):
|
||||||
|
my_link = toolbar.create_link(...)
|
||||||
|
links.append(my_link)
|
||||||
|
"""
|
||||||
|
|
||||||
|
_hooks: List[Callable[[List[str], "aqt.toolbar.Toolbar"], None]] = []
|
||||||
|
|
||||||
|
def append(self, cb: Callable[[List[str], "aqt.toolbar.Toolbar"], None]) -> None:
|
||||||
|
"""(links: List[str], top_toolbar: aqt.toolbar.Toolbar)"""
|
||||||
|
self._hooks.append(cb)
|
||||||
|
|
||||||
|
def remove(self, cb: Callable[[List[str], "aqt.toolbar.Toolbar"], None]) -> None:
|
||||||
|
if cb in self._hooks:
|
||||||
|
self._hooks.remove(cb)
|
||||||
|
|
||||||
|
def __call__(self, links: List[str], top_toolbar: aqt.toolbar.Toolbar) -> None:
|
||||||
|
for hook in self._hooks:
|
||||||
|
try:
|
||||||
|
hook(links, top_toolbar)
|
||||||
|
except:
|
||||||
|
# if the hook fails, remove it
|
||||||
|
self._hooks.remove(hook)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
top_toolbar_did_init_links = _TopToolbarDidInitLinksHook()
|
||||||
|
|
||||||
|
|
||||||
class _UndoStateDidChangeHook:
|
class _UndoStateDidChangeHook:
|
||||||
_hooks: List[Callable[[bool], None]] = []
|
_hooks: List[Callable[[bool], None]] = []
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
|
from aqt import gui_hooks
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.webview import AnkiWebView
|
from aqt.webview import AnkiWebView
|
||||||
|
|
||||||
|
@ -28,13 +29,8 @@ class Toolbar:
|
||||||
def __init__(self, mw: aqt.AnkiQt, web: AnkiWebView) -> None:
|
def __init__(self, mw: aqt.AnkiQt, web: AnkiWebView) -> None:
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.web = web
|
self.web = web
|
||||||
self.link_handlers = {
|
self.link_handlers: Dict[str, Callable] = {
|
||||||
"decks": self._deckLinkHandler,
|
|
||||||
"study": self._studyLinkHandler,
|
"study": self._studyLinkHandler,
|
||||||
"add": self._addLinkHandler,
|
|
||||||
"browse": self._browseLinkHandler,
|
|
||||||
"stats": self._statsLinkHandler,
|
|
||||||
"sync": self._syncLinkHandler,
|
|
||||||
}
|
}
|
||||||
self.web.setFixedHeight(30)
|
self.web.setFixedHeight(30)
|
||||||
self.web.requiresCol = False
|
self.web.requiresCol = False
|
||||||
|
@ -58,34 +54,88 @@ class Toolbar:
|
||||||
# Available links
|
# Available links
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
def create_link(
|
||||||
|
self,
|
||||||
|
cmd: str,
|
||||||
|
label: str,
|
||||||
|
func: Callable,
|
||||||
|
tip: Optional[str] = None,
|
||||||
|
id: Optional[str] = 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"""<a class=hitem tabindex="-1" aria-label="{label}" """
|
||||||
|
f"""{title_attr} {id_attr} href=# onclick="return pycmd('{cmd}')">"""
|
||||||
|
f"""{label}</a>"""
|
||||||
|
)
|
||||||
|
|
||||||
def _centerLinks(self):
|
def _centerLinks(self):
|
||||||
links = [
|
links = [
|
||||||
["decks", _("Decks"), _("Shortcut key: %s") % "D"],
|
self.create_link(
|
||||||
["add", _("Add"), _("Shortcut key: %s") % "A"],
|
"decks",
|
||||||
["browse", _("Browse"), _("Shortcut key: %s") % "B"],
|
_("Decks"),
|
||||||
["stats", _("Stats"), _("Shortcut key: %s") % "T"],
|
self._deckLinkHandler,
|
||||||
|
tip=_("Shortcut key: %s") % "D",
|
||||||
|
id="decks",
|
||||||
|
),
|
||||||
|
self.create_link(
|
||||||
|
"add",
|
||||||
|
_("Add"),
|
||||||
|
self._addLinkHandler,
|
||||||
|
tip=_("Shortcut key: %s") % "A",
|
||||||
|
id="add",
|
||||||
|
),
|
||||||
|
self.create_link(
|
||||||
|
"browse",
|
||||||
|
_("Browse"),
|
||||||
|
self._browseLinkHandler,
|
||||||
|
tip=_("Shortcut key: %s") % "B",
|
||||||
|
id="browse",
|
||||||
|
),
|
||||||
|
self.create_link(
|
||||||
|
"stats",
|
||||||
|
_("Stats"),
|
||||||
|
self._statsLinkHandler,
|
||||||
|
tip=_("Shortcut key: %s") % "T",
|
||||||
|
id="stats",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
return self._linkHTML(links) + self._sync_link()
|
links.append(self._create_sync_link())
|
||||||
|
|
||||||
def _linkHTML(self, links):
|
gui_hooks.top_toolbar_did_init_links(links, self)
|
||||||
buf = ""
|
|
||||||
for ln, name, title in links:
|
|
||||||
buf += """
|
|
||||||
<a class=hitem tabindex="-1" aria-label="%s" title="%s" href=# onclick="return pycmd('%s')">%s</a>""" % (
|
|
||||||
name,
|
|
||||||
title,
|
|
||||||
ln,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
return buf
|
|
||||||
|
|
||||||
def _sync_link(self) -> str:
|
return "\n".join(links)
|
||||||
|
|
||||||
|
def _create_sync_link(self) -> str:
|
||||||
name = _("Sync")
|
name = _("Sync")
|
||||||
title = _("Shortcut key: %s") % "Y"
|
title = _("Shortcut key: %s") % "Y"
|
||||||
label = "sync"
|
label = "sync"
|
||||||
|
self.link_handlers[label] = self._syncLinkHandler
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
<a class=hitem tabindex="-1" aria-label="{name}" title="{title}" href=# onclick="return pycmd('{label}')">{name}
|
<a class=hitem tabindex="-1" aria-label="{name}" title="{title}" id="{label}" href=# onclick="return pycmd('{label}')">{name}
|
||||||
<img id=sync-spinner src='/_anki/imgs/refresh.svg'>
|
<img id=sync-spinner src='/_anki/imgs/refresh.svg'>
|
||||||
</a>"""
|
</a>"""
|
||||||
|
|
||||||
|
|
|
@ -306,6 +306,20 @@ hooks = [
|
||||||
return_type="str",
|
return_type="str",
|
||||||
legacy_hook="setupStyle",
|
legacy_hook="setupStyle",
|
||||||
),
|
),
|
||||||
|
Hook(
|
||||||
|
name="top_toolbar_did_init_links",
|
||||||
|
args=["links: List[str]", "top_toolbar: aqt.toolbar.Toolbar"],
|
||||||
|
doc="""Used to modify or add links in the top toolbar of Anki's main window
|
||||||
|
|
||||||
|
'links' is a list of HTML link elements. Add-ons can generate their own links
|
||||||
|
by using aqt.toolbar.Toolbar.create_link. Links created in that way can then be
|
||||||
|
appended to the link list, e.g.:
|
||||||
|
|
||||||
|
def on_top_toolbar_did_init_links(links, toolbar):
|
||||||
|
my_link = toolbar.create_link(...)
|
||||||
|
links.append(my_link)
|
||||||
|
""",
|
||||||
|
),
|
||||||
Hook(
|
Hook(
|
||||||
name="media_sync_did_progress", args=["entry: aqt.mediasync.LogEntryWithTime"],
|
name="media_sync_did_progress", args=["entry: aqt.mediasync.LogEntryWithTime"],
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue