diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index d28c8295c..9c5babb43 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -5,6 +5,7 @@ from __future__ import annotations from copy import deepcopy +from dataclasses import dataclass from typing import Any import aqt @@ -32,6 +33,22 @@ class DeckBrowserBottomBar: self.deck_browser = deck_browser +@dataclass +class DeckBrowserContent: + """Stores sections of HTML content that the deck browser will be + populated with. + + Attributes: + tree {str} -- HTML of the deck tree section + stats {str} -- HTML of the stats section + countwarn {str} -- HTML of the deck count warning section + """ + + tree: str + stats: str + countwarn: str + + class DeckBrowser: _dueTree: Any @@ -112,10 +129,14 @@ class DeckBrowser: gui_hooks.deck_browser_did_render(self) def __renderPage(self, offset): - tree = self._renderDeckTree(self._dueTree) - stats = self._renderStats() + content = DeckBrowserContent( + tree=self._renderDeckTree(self._dueTree), + stats=self._renderStats(), + countwarn=self._countWarn(), + ) + gui_hooks.deck_browser_will_render_content(self, content) self.web.stdHtml( - self._body % dict(tree=tree, stats=stats, countwarn=self._countWarn()), + self._body % content.__dict__, css=["deckbrowser.css"], js=["jquery.js", "jquery-ui.js", "deckbrowser.js"], context=self, diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index b9fc915b4..26d6de196 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -435,6 +435,63 @@ class _DeckBrowserDidRenderHook: deck_browser_did_render = _DeckBrowserDidRenderHook() +class _DeckBrowserWillRenderContentHook: + """Used to modify HTML content sections in the deck browser body + + 'content' contains the sections of HTML content the deck browser body + will be updated with. + + When modifying the content of a particular section, please make sure your + changes only perform the minimum required edits to make your add-on work. + You should avoid overwriting or interfering with existing data as much + as possible, instead opting to append your own changes, e.g.: + + def on_deck_browser_will_render_content(deck_browser, content): + content.stats += " +
my html
" + """ + + _hooks: List[ + Callable[ + ["aqt.deckbrowser.DeckBrowser", "aqt.deckbrowser.DeckBrowserContent"], None + ] + ] = [] + + def append( + self, + cb: Callable[ + ["aqt.deckbrowser.DeckBrowser", "aqt.deckbrowser.DeckBrowserContent"], None + ], + ) -> None: + """(deck_browser: aqt.deckbrowser.DeckBrowser, content: aqt.deckbrowser.DeckBrowserContent)""" + self._hooks.append(cb) + + def remove( + self, + cb: Callable[ + ["aqt.deckbrowser.DeckBrowser", "aqt.deckbrowser.DeckBrowserContent"], None + ], + ) -> None: + if cb in self._hooks: + self._hooks.remove(cb) + + def __call__( + self, + deck_browser: aqt.deckbrowser.DeckBrowser, + content: aqt.deckbrowser.DeckBrowserContent, + ) -> None: + for hook in self._hooks: + try: + hook(deck_browser, content) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + + +deck_browser_will_render_content = _DeckBrowserWillRenderContentHook() + + class _DeckBrowserWillShowOptionsMenuHook: _hooks: List[Callable[[QMenu, int], None]] = [] @@ -772,6 +829,55 @@ class _OverviewDidRefreshHook: overview_did_refresh = _OverviewDidRefreshHook() +class _OverviewWillRenderContentHook: + """Used to modify HTML content sections in the overview body + + 'content' contains the sections of HTML content the overview body + will be updated with. + + When modifying the content of a particular section, please make sure your + changes only perform the minimum required edits to make your add-on work. + You should avoid overwriting or interfering with existing data as much + as possible, instead opting to append your own changes, e.g.: + + def on_overview_will_render_content(overview, content): + content.table += " +
my html
" + """ + + _hooks: List[ + Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None] + ] = [] + + def append( + self, + cb: Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None], + ) -> None: + """(overview: aqt.overview.Overview, content: aqt.overview.OverviewContent)""" + self._hooks.append(cb) + + def remove( + self, + cb: Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None], + ) -> None: + if cb in self._hooks: + self._hooks.remove(cb) + + def __call__( + self, overview: aqt.overview.Overview, content: aqt.overview.OverviewContent + ) -> None: + for hook in self._hooks: + try: + hook(overview, content) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + + +overview_will_render_content = _OverviewWillRenderContentHook() + + class _ProfileDidOpenHook: _hooks: List[Callable[[], None]] = [] diff --git a/qt/aqt/overview.py b/qt/aqt/overview.py index e94b69953..4184660e1 100644 --- a/qt/aqt/overview.py +++ b/qt/aqt/overview.py @@ -4,6 +4,8 @@ from __future__ import annotations +from dataclasses import dataclass + import aqt from anki.lang import _ from aqt import gui_hooks @@ -17,6 +19,24 @@ class OverviewBottomBar: self.overview = overview +@dataclass +class OverviewContent: + """Stores sections of HTML content that the overview will be + populated with. + + Attributes: + deck {str} -- Plain text deck name + shareLink {str} -- HTML of the share link section + desc {str} -- HTML of the deck description section + table {str} -- HTML of the deck stats table section + """ + + deck: str + shareLink: str + desc: str + table: str + + class Overview: "Deck overview." @@ -141,14 +161,15 @@ class Overview: shareLink = 'Reviews and Updates' else: shareLink = "" + content = OverviewContent( + deck=deck["name"], + shareLink=shareLink, + desc=self._desc(deck), + table=self._table(), + ) + gui_hooks.overview_will_render_content(self, content) self.web.stdHtml( - self._body - % dict( - deck=deck["name"], - shareLink=shareLink, - desc=self._desc(deck), - table=self._table(), - ), + self._body % content.__dict__, css=["overview.css"], js=["jquery.js", "overview.js"], context=self, diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index cd2c8d471..c71917407 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -25,11 +25,51 @@ hooks = [ doc="""Allow to update the overview window. E.g. add the deck name in the title.""", ), + Hook( + name="overview_will_render_content", + args=[ + "overview: aqt.overview.Overview", + "content: aqt.overview.OverviewContent", + ], + doc="""Used to modify HTML content sections in the overview body + + 'content' contains the sections of HTML content the overview body + will be updated with. + + When modifying the content of a particular section, please make sure your + changes only perform the minimum required edits to make your add-on work. + You should avoid overwriting or interfering with existing data as much + as possible, instead opting to append your own changes, e.g.: + + def on_overview_will_render_content(overview, content): + content.table += "\n
my html
" + """, + ), Hook( name="deck_browser_did_render", args=["deck_browser: aqt.deckbrowser.DeckBrowser"], doc="""Allow to update the deck browser window. E.g. change its title.""", ), + Hook( + name="deck_browser_will_render_content", + args=[ + "deck_browser: aqt.deckbrowser.DeckBrowser", + "content: aqt.deckbrowser.DeckBrowserContent", + ], + doc="""Used to modify HTML content sections in the deck browser body + + 'content' contains the sections of HTML content the deck browser body + will be updated with. + + When modifying the content of a particular section, please make sure your + changes only perform the minimum required edits to make your add-on work. + You should avoid overwriting or interfering with existing data as much + as possible, instead opting to append your own changes, e.g.: + + def on_deck_browser_will_render_content(deck_browser, content): + content.stats += "\n
my html
" + """, + ), Hook( name="reviewer_did_show_question", args=["card: Card"],