From 84c17e1f022b274162c8495f474565ca2a6e7f08 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 15 Jan 2020 12:16:54 +1000 Subject: [PATCH] add more hooks, tweak wording for consistency --- qt/aqt/browser.py | 6 +- qt/aqt/clayout.py | 6 +- qt/aqt/gui_hooks.py | 684 ++++++++++++++++++++++++++++++++++++--- qt/aqt/modelchooser.py | 2 +- qt/aqt/reviewer.py | 11 +- qt/aqt/webview.py | 2 +- qt/tools/genhooks_gui.py | 123 ++++++- 7 files changed, 774 insertions(+), 60 deletions(-) diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index 961a445d4..642f6df7d 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -639,7 +639,7 @@ class Browser(QMainWindow): self.pgDownCut = QShortcut(QKeySequence("Shift+End"), self) self.pgDownCut.activated.connect(self.onLastCard) # add-on hook - gui_hooks.browser_setup_menus_hook(self) + gui_hooks.browser_menus_did_setup_hook(self) self.mw.maybeHideAccelerators(self) # context menu @@ -653,7 +653,7 @@ class Browser(QMainWindow): m.addSeparator() for act in self.form.menu_Notes.actions(): m.addAction(act) - gui_hooks.browser_context_menu_hook(self, m) + gui_hooks.browser_context_menu_will_show_hook(self, m) qtMenuShortcutWorkaround(m) m.exec_(QCursor.pos()) @@ -844,7 +844,7 @@ class Browser(QMainWindow): self.editor.card = self.card self.singleCard = True self._updateFlagsMenu() - gui_hooks.browser_row_changed_hook(self) + gui_hooks.browser_row_did_change_hook(self) self._renderPreview(True) def refreshCurrentCard(self, note): diff --git a/qt/aqt/clayout.py b/qt/aqt/clayout.py index 9b44c0d69..38b51530a 100644 --- a/qt/aqt/clayout.py +++ b/qt/aqt/clayout.py @@ -8,9 +8,9 @@ import re import aqt from anki.consts import * -from anki.hooks import runFilter from anki.lang import _, ngettext from anki.utils import bodyClass, isMac, isWin, joinFields +from aqt import gui_hooks from aqt.qt import * from aqt.sound import clearAudioQueue, playFromText from aqt.utils import ( @@ -335,10 +335,10 @@ Please create a new card type first.""" bodyclass = bodyClass(self.mw.col, c) q = ti(mungeQA(self.mw.col, c.q(reload=True))) - q = runFilter("prepareQA", q, c, "clayoutQuestion") + q = gui_hooks.card_text_filter(q, c, "clayoutQuestion") a = ti(mungeQA(self.mw.col, c.a()), type="a") - a = runFilter("prepareQA", a, c, "clayoutAnswer") + a = gui_hooks.card_text_filter(a, c, "clayoutAnswer") # use _showAnswer to avoid the longer delay self.pform.frontWeb.eval("_showAnswer(%s,'%s');" % (json.dumps(q), bodyclass)) diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index c16ff5c50..c0021f396 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -7,12 +7,13 @@ See pylib/anki/hooks.py from __future__ import annotations -from typing import Any, Callable, Dict, List # pylint: disable=unused-import +from typing import Any, Callable, Dict, List, Tuple +import anki import aqt from anki.cards import Card -from anki.hooks import runFilter, runHook # pylint: disable=unused-import -from aqt.qt import QMenu +from anki.hooks import runFilter, runHook +from aqt.qt import QMenu, QShortcut # New hook/filter handling ############################################################################## @@ -22,7 +23,57 @@ from aqt.qt import QMenu # @@AUTOGEN@@ -class _BrowserContextMenuHook: +class _AddCardsHistoryMenuWillShowHook: + _hooks: List[Callable[["aqt.addcards.AddCards", QMenu], None]] = [] + + def append(self, cb: Callable[["aqt.addcards.AddCards", QMenu], None]) -> None: + """(addcards: aqt.addcards.AddCards, menu: QMenu)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["aqt.addcards.AddCards", QMenu], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, addcards: aqt.addcards.AddCards, menu: QMenu) -> None: + for hook in self._hooks: + try: + hook(addcards, menu) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("AddCards.onHistory", addcards, menu) + + +add_cards_history_menu_will_show_hook = _AddCardsHistoryMenuWillShowHook() + + +class _AddCardsNoteDidAddHook: + _hooks: List[Callable[["anki.notes.Note"], None]] = [] + + def append(self, cb: Callable[["anki.notes.Note"], None]) -> None: + """(note: anki.notes.Note)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["anki.notes.Note"], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, note: anki.notes.Note) -> None: + for hook in self._hooks: + try: + hook(note) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("AddCards.noteAdded", note) + + +add_cards_note_did_add_hook = _AddCardsNoteDidAddHook() + + +class _BrowserContextMenuWillShowHook: _hooks: List[Callable[["aqt.browser.Browser", QMenu], None]] = [] def append(self, cb: Callable[["aqt.browser.Browser", QMenu], None]) -> None: @@ -44,35 +95,10 @@ class _BrowserContextMenuHook: runHook("browser.onContextMenu", browser, menu) -browser_context_menu_hook = _BrowserContextMenuHook() +browser_context_menu_will_show_hook = _BrowserContextMenuWillShowHook() -class _BrowserRowChangedHook: - _hooks: List[Callable[["aqt.browser.Browser"], None]] = [] - - def append(self, cb: Callable[["aqt.browser.Browser"], None]) -> None: - """(browser: aqt.browser.Browser)""" - self._hooks.append(cb) - - def remove(self, cb: Callable[["aqt.browser.Browser"], None]) -> None: - self._hooks.remove(cb) - - def __call__(self, browser: aqt.browser.Browser) -> None: - for hook in self._hooks: - try: - hook(browser) - except: - # if the hook fails, remove it - self._hooks.remove(hook) - raise - # legacy support - runHook("browser.rowChanged", browser) - - -browser_row_changed_hook = _BrowserRowChangedHook() - - -class _BrowserSetupMenusHook: +class _BrowserMenusDidSetupHook: _hooks: List[Callable[["aqt.browser.Browser"], None]] = [] def append(self, cb: Callable[["aqt.browser.Browser"], None]) -> None: @@ -94,7 +120,32 @@ class _BrowserSetupMenusHook: runHook("browser.setupMenus", browser) -browser_setup_menus_hook = _BrowserSetupMenusHook() +browser_menus_did_setup_hook = _BrowserMenusDidSetupHook() + + +class _BrowserRowDidChangeHook: + _hooks: List[Callable[["aqt.browser.Browser"], None]] = [] + + def append(self, cb: Callable[["aqt.browser.Browser"], None]) -> None: + """(browser: aqt.browser.Browser)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["aqt.browser.Browser"], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, browser: aqt.browser.Browser) -> None: + for hook in self._hooks: + try: + hook(browser) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("browser.rowChanged", browser) + + +browser_row_did_change_hook = _BrowserRowDidChangeHook() class _CardTextFilter: @@ -123,7 +174,32 @@ class _CardTextFilter: card_text_filter = _CardTextFilter() -class _CurrentNoteTypeChangedHook: +class _CollectionDidLoadHook: + _hooks: List[Callable[["anki.storage._Collection"], None]] = [] + + def append(self, cb: Callable[["anki.storage._Collection"], None]) -> None: + """(col: anki.storage._Collection)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["anki.storage._Collection"], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, col: anki.storage._Collection) -> None: + for hook in self._hooks: + try: + hook(col) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("colLoading", col) + + +collection_did_load_hook = _CollectionDidLoadHook() + + +class _CurrentNoteTypeDidChangeHook: _hooks: List[Callable[[Dict[str, Any]], None]] = [] def append(self, cb: Callable[[Dict[str, Any]], None]) -> None: @@ -145,7 +221,259 @@ class _CurrentNoteTypeChangedHook: runHook("currentModelChanged") -current_note_type_changed_hook = _CurrentNoteTypeChangedHook() +current_note_type_did_change_hook = _CurrentNoteTypeDidChangeHook() + + +class _DeckBrowserOptionsMenuWillShowHook: + _hooks: List[Callable[[QMenu, int], None]] = [] + + def append(self, cb: Callable[[QMenu, int], None]) -> None: + """(menu: QMenu, deck_id: int)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[QMenu, int], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, menu: QMenu, deck_id: int) -> None: + for hook in self._hooks: + try: + hook(menu, deck_id) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("showDeckOptions", menu, deck_id) + + +deck_browser_options_menu_will_show_hook = _DeckBrowserOptionsMenuWillShowHook() + + +class _EditorButtonsDidSetupHook: + _hooks: List[Callable[[List, "aqt.editor.Editor"], None]] = [] + + def append(self, cb: Callable[[List, "aqt.editor.Editor"], None]) -> None: + """(buttons: List, editor: aqt.editor.Editor)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[List, "aqt.editor.Editor"], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, buttons: List, editor: aqt.editor.Editor) -> None: + for hook in self._hooks: + try: + hook(buttons, editor) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + + +editor_buttons_did_setup_hook = _EditorButtonsDidSetupHook() + + +class _EditorContextMenuWillShowHook: + _hooks: List[Callable[["aqt.editor.EditorWebView", QMenu], None]] = [] + + def append(self, cb: Callable[["aqt.editor.EditorWebView", QMenu], None]) -> None: + """(editor_webview: aqt.editor.EditorWebView, menu: QMenu)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["aqt.editor.EditorWebView", QMenu], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, editor_webview: aqt.editor.EditorWebView, menu: QMenu) -> None: + for hook in self._hooks: + try: + hook(editor_webview, menu) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("EditorWebView.contextMenuEvent", editor_webview, menu) + + +editor_context_menu_will_show_hook = _EditorContextMenuWillShowHook() + + +class _EditorFieldDidGainFocusHook: + _hooks: List[Callable[["anki.notes.Note", int], None]] = [] + + def append(self, cb: Callable[["anki.notes.Note", int], None]) -> None: + """(note: anki.notes.Note, current_field_idx: int)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["anki.notes.Note", int], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, note: anki.notes.Note, current_field_idx: int) -> None: + for hook in self._hooks: + try: + hook(note, current_field_idx) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("editFocusGained", note, current_field_idx) + + +editor_field_did_gain_focus_hook = _EditorFieldDidGainFocusHook() + + +class _EditorFieldDidLoseFocusFilter: + _hooks: List[Callable[[bool, "anki.notes.Note", int], bool]] = [] + + def append(self, cb: Callable[[bool, "anki.notes.Note", int], bool]) -> None: + """(changed: bool, note: anki.notes.Note, current_field_idx: int)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[bool, "anki.notes.Note", int], bool]) -> None: + self._hooks.remove(cb) + + def __call__( + self, changed: bool, note: anki.notes.Note, current_field_idx: int + ) -> bool: + for filter in self._hooks: + try: + changed = filter(changed, note, current_field_idx) + except: + # if the hook fails, remove it + self._hooks.remove(filter) + raise + # legacy support + runFilter("editFocusLost", changed, note, current_field_idx) + return changed + + +editor_field_did_lose_focus_filter = _EditorFieldDidLoseFocusFilter() + + +class _EditorFontForFieldFilter: + _hooks: List[Callable[[str], str]] = [] + + def append(self, cb: Callable[[str], str]) -> None: + """(font: str)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[str], str]) -> None: + self._hooks.remove(cb) + + def __call__(self, font: str) -> str: + for filter in self._hooks: + try: + font = filter(font) + except: + # if the hook fails, remove it + self._hooks.remove(filter) + raise + # legacy support + runFilter("mungeEditingFontName", font) + return font + + +editor_font_for_field_filter = _EditorFontForFieldFilter() + + +class _EditorNoteDidUpdateHook: + _hooks: List[Callable[["aqt.editor.Editor"], None]] = [] + + def append(self, cb: Callable[["aqt.editor.Editor"], None]) -> None: + """(editor: aqt.editor.Editor)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["aqt.editor.Editor"], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, editor: aqt.editor.Editor) -> None: + for hook in self._hooks: + try: + hook(editor) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("loadNote", editor) + + +editor_note_did_update_hook = _EditorNoteDidUpdateHook() + + +class _EditorShortcutsDidSetupHook: + _hooks: List[Callable[[List[Tuple], "aqt.editor.Editor"], None]] = [] + + def append(self, cb: Callable[[List[Tuple], "aqt.editor.Editor"], None]) -> None: + """(shortcuts: List[Tuple], editor: aqt.editor.Editor)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[List[Tuple], "aqt.editor.Editor"], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, shortcuts: List[Tuple], editor: aqt.editor.Editor) -> None: + for hook in self._hooks: + try: + hook(shortcuts, editor) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("setupEditorShortcuts", shortcuts, editor) + + +editor_shortcuts_did_setup_hook = _EditorShortcutsDidSetupHook() + + +class _EditorTagsDidUpdateHook: + _hooks: List[Callable[["anki.notes.Note"], None]] = [] + + def append(self, cb: Callable[["anki.notes.Note"], None]) -> None: + """(note: anki.notes.Note)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["anki.notes.Note"], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, note: anki.notes.Note) -> None: + for hook in self._hooks: + try: + hook(note) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("tagsUpdated", note) + + +editor_tags_did_update_hook = _EditorTagsDidUpdateHook() + + +class _EditorTypingTimerDidFireHook: + _hooks: List[Callable[["anki.notes.Note"], None]] = [] + + def append(self, cb: Callable[["anki.notes.Note"], None]) -> None: + """(note: anki.notes.Note)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["anki.notes.Note"], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, note: anki.notes.Note) -> None: + for hook in self._hooks: + try: + hook(note) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("editTimer", note) + + +editor_typing_timer_did_fire_hook = _EditorTypingTimerDidFireHook() class _MpvIdleHook: @@ -196,7 +524,82 @@ class _MpvWillPlayHook: mpv_will_play_hook = _MpvWillPlayHook() -class _ReviewerShowingAnswerHook: +class _ProfileDidOpenHook: + _hooks: List[Callable[[], None]] = [] + + def append(self, cb: Callable[[], None]) -> None: + """()""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[], None]) -> None: + self._hooks.remove(cb) + + def __call__(self) -> None: + for hook in self._hooks: + try: + hook() + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("profileLoaded") + + +profile_did_open_hook = _ProfileDidOpenHook() + + +class _ProfileWillCloseHook: + _hooks: List[Callable[[], None]] = [] + + def append(self, cb: Callable[[], None]) -> None: + """()""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[], None]) -> None: + self._hooks.remove(cb) + + def __call__(self) -> None: + for hook in self._hooks: + try: + hook() + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("unloadProfile") + + +profile_will_close_hook = _ProfileWillCloseHook() + + +class _ReviewDidUndoHook: + _hooks: List[Callable[[int], None]] = [] + + def append(self, cb: Callable[[int], None]) -> None: + """(card_id: int)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[int], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, card_id: int) -> None: + for hook in self._hooks: + try: + hook(card_id) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("revertedCard", card_id) + + +review_did_undo_hook = _ReviewDidUndoHook() + + +class _ReviewerAnswerDidShowHook: _hooks: List[Callable[[Card], None]] = [] def append(self, cb: Callable[[Card], None]) -> None: @@ -218,10 +621,35 @@ class _ReviewerShowingAnswerHook: runHook("showAnswer") -reviewer_showing_answer_hook = _ReviewerShowingAnswerHook() +reviewer_answer_did_show_hook = _ReviewerAnswerDidShowHook() -class _ReviewerShowingQuestionHook: +class _ReviewerContextMenuWillShowHook: + _hooks: List[Callable[["aqt.reviewer.Reviewer", QMenu], None]] = [] + + def append(self, cb: Callable[["aqt.reviewer.Reviewer", QMenu], None]) -> None: + """(reviewer: aqt.reviewer.Reviewer, menu: QMenu)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[["aqt.reviewer.Reviewer", QMenu], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, reviewer: aqt.reviewer.Reviewer, menu: QMenu) -> None: + for hook in self._hooks: + try: + hook(reviewer, menu) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("Reviewer.contextMenuEvent", reviewer, menu) + + +reviewer_context_menu_will_show_hook = _ReviewerContextMenuWillShowHook() + + +class _ReviewerQuestionDidShowHook: _hooks: List[Callable[[Card], None]] = [] def append(self, cb: Callable[[Card], None]) -> None: @@ -243,10 +671,186 @@ class _ReviewerShowingQuestionHook: runHook("showQuestion") -reviewer_showing_question_hook = _ReviewerShowingQuestionHook() +reviewer_question_did_show_hook = _ReviewerQuestionDidShowHook() -class _WebviewContextMenuHook: +class _SetupStyleFilter: + _hooks: List[Callable[[str], str]] = [] + + def append(self, cb: Callable[[str], str]) -> None: + """(style: str)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[str], str]) -> None: + self._hooks.remove(cb) + + def __call__(self, style: str) -> str: + for filter in self._hooks: + try: + style = filter(style) + except: + # if the hook fails, remove it + self._hooks.remove(filter) + raise + # legacy support + runFilter("setupStyle", style) + return style + + +setup_style_filter = _SetupStyleFilter() + + +class _StateDidChangeHook: + _hooks: List[Callable[[str, str], None]] = [] + + def append(self, cb: Callable[[str, str], None]) -> None: + """(new_state: str, old_state: str)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[str, str], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, new_state: str, old_state: str) -> None: + for hook in self._hooks: + try: + hook(new_state, old_state) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("afterStateChange", new_state, old_state) + + +state_did_change_hook = _StateDidChangeHook() + + +class _StateDidResetHook: + """Called when the interface needs to be redisplayed after non-trivial changes have been made.""" + + _hooks: List[Callable[[], None]] = [] + + def append(self, cb: Callable[[], None]) -> None: + """()""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[], None]) -> None: + self._hooks.remove(cb) + + def __call__(self) -> None: + for hook in self._hooks: + try: + hook() + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("reset") + + +state_did_reset_hook = _StateDidResetHook() + + +class _StateDidRevertHook: + _hooks: List[Callable[[str], None]] = [] + + def append(self, cb: Callable[[str], None]) -> None: + """(action: str)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[str], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, action: str) -> None: + for hook in self._hooks: + try: + hook(action) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("revertedState", action) + + +state_did_revert_hook = _StateDidRevertHook() + + +class _StateShortcutsWillChangeHook: + _hooks: List[Callable[[str, List[QShortcut]], None]] = [] + + def append(self, cb: Callable[[str, List[QShortcut]], None]) -> None: + """(state: str, shortcuts: List[QShortcut])""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[str, List[QShortcut]], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, state: str, shortcuts: List[QShortcut]) -> None: + for hook in self._hooks: + try: + hook(state, shortcuts) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + + +state_shortcuts_will_change_hook = _StateShortcutsWillChangeHook() + + +class _StateWillChangeHook: + _hooks: List[Callable[[str, str], None]] = [] + + def append(self, cb: Callable[[str, str], None]) -> None: + """(new_state: str, old_state: str)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[str, str], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, new_state: str, old_state: str) -> None: + for hook in self._hooks: + try: + hook(new_state, old_state) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("beforeStateChange", new_state, old_state) + + +state_will_change_hook = _StateWillChangeHook() + + +class _UndoStateDidChangeHook: + _hooks: List[Callable[[bool], None]] = [] + + def append(self, cb: Callable[[bool], None]) -> None: + """(can_undo: bool)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[bool], None]) -> None: + self._hooks.remove(cb) + + def __call__(self, can_undo: bool) -> None: + for hook in self._hooks: + try: + hook(can_undo) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + # legacy support + runHook("undoState", can_undo) + + +undo_state_did_change_hook = _UndoStateDidChangeHook() + + +class _WebviewContextMenuWillShowHook: _hooks: List[Callable[["aqt.webview.AnkiWebView", QMenu], None]] = [] def append(self, cb: Callable[["aqt.webview.AnkiWebView", QMenu], None]) -> None: @@ -268,5 +872,5 @@ class _WebviewContextMenuHook: runHook("AnkiWebView.contextMenuEvent", webview, menu) -webview_context_menu_hook = _WebviewContextMenuHook() +webview_context_menu_will_show_hook = _WebviewContextMenuWillShowHook() # @@AUTOGEN@@ diff --git a/qt/aqt/modelchooser.py b/qt/aqt/modelchooser.py index 212361b83..fb0e91e83 100644 --- a/qt/aqt/modelchooser.py +++ b/qt/aqt/modelchooser.py @@ -87,7 +87,7 @@ class ModelChooser(QHBoxLayout): cdeck = self.deck.decks.current() cdeck["mid"] = m["id"] self.deck.decks.save(cdeck) - gui_hooks.current_note_type_changed_hook(current) + gui_hooks.current_note_type_did_change_hook(current) self.mw.reset() def updateModels(self): diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index 074c5e94d..200b3ec47 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -13,7 +13,7 @@ from typing import List import aqt from anki import hooks from anki.cards import Card -from anki.hooks import runFilter, runHook +from anki.hooks import runHook from anki.lang import _, ngettext from anki.utils import bodyClass, stripHTML from aqt import AnkiQt, gui_hooks @@ -188,7 +188,7 @@ The front of this card is empty. Please run Tools>Empty Cards.""" playFromText(q) # render & update bottom q = self._mungeQA(q) - q = runFilter("prepareQA", q, c, "reviewQuestion") + q = gui_hooks.card_text_filter(q, c, "reviewQuestion") bodyclass = bodyClass(self.mw.col, c) @@ -200,7 +200,7 @@ The front of this card is empty. Please run Tools>Empty Cards.""" if self.typeCorrect: self.mw.web.setFocus() # user hook - gui_hooks.reviewer_showing_question_hook(c) + gui_hooks.reviewer_question_did_show_hook(c) def autoplay(self, card): return self.mw.col.decks.confForDid(card.odid or card.did)["autoplay"] @@ -230,12 +230,12 @@ The front of this card is empty. Please run Tools>Empty Cards.""" if self.autoplay(c): playFromText(a) a = self._mungeQA(a) - a = runFilter("prepareQA", a, c, "reviewAnswer") + a = gui_hooks.card_text_filter(a, c, "reviewAnswer") # render and update bottom self.web.eval("_showAnswer(%s);" % json.dumps(a)) self._showEaseButtons() # user hook - gui_hooks.reviewer_showing_answer_hook(c) + gui_hooks.reviewer_answer_did_show_hook(c) # Answering a card ############################################################ @@ -695,6 +695,7 @@ time = %(time)d; m = QMenu(self.mw) self._addMenuItems(m, opts) + gui_hooks.reviewer_context_menu_will_show_hook(self, m) runHook("Reviewer.contextMenuEvent", self, m) qtMenuShortcutWorkaround(m) m.exec_(QCursor.pos()) diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index 8cda6dc1f..247e0a149 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -182,7 +182,7 @@ class AnkiWebView(QWebEngineView): # type: ignore m = QMenu(self) a = m.addAction(_("Copy")) a.triggered.connect(self.onCopy) - gui_hooks.webview_context_menu_hook(self, m) + gui_hooks.webview_context_menu_will_show_hook(self, m) m.popup(QCursor.pos()) def dropEvent(self, evt): diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index 6bdfa9d3d..480c93bfd 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -20,35 +20,35 @@ hooks = [ Hook(name="mpv_idle"), Hook(name="mpv_will_play", args=["file: str"], legacy_hook="mpvWillPlay"), Hook( - name="reviewer_showing_question", + name="reviewer_question_did_show", args=["card: Card"], legacy_hook="showQuestion", legacy_no_args=True, ), Hook( - name="reviewer_showing_answer", + name="reviewer_answer_did_show", args=["card: Card"], legacy_hook="showAnswer", legacy_no_args=True, ), Hook( - name="current_note_type_changed", + name="current_note_type_did_change", args=["notetype: Dict[str, Any]"], legacy_hook="currentModelChanged", legacy_no_args=True, ), Hook( - name="browser_setup_menus", + name="browser_menus_did_setup", args=["browser: aqt.browser.Browser"], legacy_hook="browser.setupMenus", ), Hook( - name="browser_context_menu", + name="browser_context_menu_will_show", args=["browser: aqt.browser.Browser", "menu: QMenu"], legacy_hook="browser.onContextMenu", ), Hook( - name="browser_row_changed", + name="browser_row_did_change", args=["browser: aqt.browser.Browser"], legacy_hook="browser.rowChanged", ), @@ -59,10 +59,119 @@ hooks = [ legacy_hook="prepareQA", ), Hook( - name="webview_context_menu", + name="webview_context_menu_will_show", args=["webview: aqt.webview.AnkiWebView", "menu: QMenu"], legacy_hook="AnkiWebView.contextMenuEvent", ), + Hook( + name="reviewer_context_menu_will_show", + args=["reviewer: aqt.reviewer.Reviewer", "menu: QMenu"], + legacy_hook="Reviewer.contextMenuEvent", + ), + Hook(name="profile_did_open", legacy_hook="profileLoaded"), + Hook(name="profile_will_close", legacy_hook="unloadProfile"), + Hook( + name="state_will_change", + args=["new_state: str", "old_state: str"], + legacy_hook="beforeStateChange", + ), + Hook( + name="state_did_change", + args=["new_state: str", "old_state: str"], + legacy_hook="afterStateChange", + ), + # different sig to original + Hook( + name="state_shortcuts_will_change", + args=["state: str", "shortcuts: List[QShortcut]"], + ), + Hook( + name="collection_did_load", + args=["col: anki.storage._Collection"], + legacy_hook="colLoading", + ), + Hook(name="state_did_revert", args=["action: str"], legacy_hook="revertedState"), + Hook( + name="state_did_reset", + legacy_hook="reset", + doc="Called when the interface needs to be redisplayed after non-trivial changes have been made.", + ), + Hook( + name="undo_state_did_change", args=["can_undo: bool"], legacy_hook="undoState" + ), + Hook(name="review_did_undo", args=["card_id: int"], legacy_hook="revertedCard"), + Hook( + name="setup_style", + args=["style: str"], + return_type="str", + legacy_hook="setupStyle", + ), + Hook( + name="add_cards_history_menu_will_show", + args=["addcards: aqt.addcards.AddCards", "menu: QMenu"], + legacy_hook="AddCards.onHistory", + ), + Hook( + name="add_cards_note_did_add", + args=["note: anki.notes.Note"], + legacy_hook="AddCards.noteAdded", + ), + Hook( + name="deck_browser_options_menu_will_show", + args=["menu: QMenu", "deck_id: int"], + legacy_hook="showDeckOptions", + ), + # no return like setupEditorButtons + Hook( + name="editor_buttons_did_setup", + args=["buttons: List", "editor: aqt.editor.Editor"], + ), + Hook( + name="editor_shortcuts_did_setup", + args=["shortcuts: List[Tuple]", "editor: aqt.editor.Editor"], + legacy_hook="setupEditorShortcuts", + ), + Hook( + name="editor_context_menu_will_show", + args=["editor_webview: aqt.editor.EditorWebView", "menu: QMenu"], + legacy_hook="EditorWebView.contextMenuEvent", + ), + Hook( + name="editor_typing_timer_did_fire", + args=["note: anki.notes.Note"], + legacy_hook="editTimer", + ), + Hook( + name="editor_field_did_gain_focus", + args=["note: anki.notes.Note", "current_field_idx: int"], + legacy_hook="editFocusGained", + ), + Hook( + name="editor_field_did_lose_focus", + args=["changed: bool", "note: anki.notes.Note", "current_field_idx: int"], + return_type="bool", + legacy_hook="editFocusLost", + ), + Hook( + name="editor_note_did_update", + args=["editor: aqt.editor.Editor"], + legacy_hook="loadNote", + ), + Hook( + name="editor_tags_did_update", + args=["note: anki.notes.Note"], + legacy_hook="tagsUpdated", + ), + Hook( + name="editor_font_for_field", + args=["font: str"], + return_type="str", + legacy_hook="mungeEditingFontName", + ), + # + # aqt/reviewer.py + # 66: runHook("reviewCleanup") + # ] if __name__ == "__main__":