diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py
index 8324d9936..70df81aa1 100644
--- a/qt/aqt/addcards.py
+++ b/qt/aqt/addcards.py
@@ -7,11 +7,11 @@ import aqt.deckchooser
import aqt.editor
import aqt.forms
import aqt.modelchooser
-from anki.hooks import addHook, remHook, runHook
+from anki.hooks import addHook, remHook
from anki.lang import _
from anki.notes import Note
from anki.utils import htmlToTextLine, isMac
-from aqt import AnkiQt
+from aqt import AnkiQt, gui_hooks
from aqt.qt import *
from aqt.sound import clearAudioQueue
from aqt.utils import (
@@ -157,7 +157,7 @@ class AddCards(QDialog):
else:
a = m.addAction(_("(Note deleted)"))
a.setEnabled(False)
- runHook("AddCards.onHistory", self, m)
+ gui_hooks.add_cards_history_menu_will_show_hook(self, m)
m.exec_(self.historyButton.mapToGlobal(QPoint(0, 0)))
def editHistory(self, nid):
@@ -197,7 +197,7 @@ question on all cards."""
self.addHistory(note)
self.mw.requireReset()
self.previousNote = note
- runHook("AddCards.noteAdded", note)
+ gui_hooks.add_cards_note_did_add_hook(note)
return note
def addCards(self):
diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py
index 8944bc9da..b99d3ad24 100644
--- a/qt/aqt/deckbrowser.py
+++ b/qt/aqt/deckbrowser.py
@@ -6,9 +6,9 @@ from typing import Any
import aqt
from anki.errors import DeckRenameError
-from anki.hooks import runHook
from anki.lang import _, ngettext
from anki.utils import fmtTimeSpan, ids2str
+from aqt import gui_hooks
from aqt.qt import *
from aqt.sound import clearAudioQueue
from aqt.utils import askUser, getOnlyText, openHelp, openLink, shortcut, showWarning
@@ -245,7 +245,7 @@ where id > ?""",
a.triggered.connect(lambda b, did=did: self._export(did))
a = m.addAction(_("Delete"))
a.triggered.connect(lambda b, did=did: self._delete(did))
- runHook("showDeckOptions", m, did)
+ gui_hooks.deck_browser_options_menu_will_show_hook(m, did)
m.exec_(QCursor.pos())
def _export(self, did):
diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py
index 5a64b4c70..10cbc2a3c 100644
--- a/qt/aqt/editor.py
+++ b/qt/aqt/editor.py
@@ -19,11 +19,11 @@ from bs4 import BeautifulSoup
import aqt
import aqt.sound
-from anki.hooks import addHook, runFilter, runHook
+from anki.hooks import addHook, runFilter
from anki.lang import _
from anki.sync import AnkiRequestsClient
from anki.utils import checksum, isWin, namedtmp, stripHTMLMedia
-from aqt import AnkiQt
+from aqt import AnkiQt, gui_hooks
from aqt.qt import *
from aqt.sound import getAudio
from aqt.utils import (
@@ -141,6 +141,8 @@ class Editor:
self._addButton("more", "more"),
]
)
+ gui_hooks.editor_buttons_did_setup_hook(righttopbtns, self)
+ # legacy filter
righttopbtns = runFilter("setupEditorButtons", righttopbtns, self)
topbuts = """
@@ -283,7 +285,7 @@ class Editor:
("Ctrl+Shift+X", self.onHtmlEdit),
("Ctrl+Shift+T", self.onFocusTags, True),
]
- runHook("setupEditorShortcuts", cuts, self)
+ gui_hooks.editor_shortcuts_did_setup_hook(cuts, self)
for row in cuts:
if len(row) == 2:
keys, fn = row # pylint: disable=unbalanced-tuple-unpacking
@@ -328,7 +330,7 @@ class Editor:
######################################################################
def onBridgeCmd(self, cmd):
- if not self.note or not runHook:
+ if not self.note:
# shutdown
return
# focus lost or key/button pressed?
@@ -356,20 +358,20 @@ class Editor:
if type == "blur":
self.currentField = None
# run any filters
- if runFilter("editFocusLost", False, self.note, ord):
+ if gui_hooks.editor_field_did_lose_focus_filter(False, self.note, ord):
# something updated the note; update it after a subsequent focus
# event has had time to fire
self.mw.progress.timer(100, self.loadNoteKeepingFocus, False)
else:
self.checkValid()
else:
- runHook("editTimer", self.note)
+ gui_hooks.editor_typing_timer_did_fire_hook(self.note)
self.checkValid()
# focused into field?
elif cmd.startswith("focus"):
(type, num) = cmd.split(":", 1)
self.currentField = int(num)
- runHook("editFocusGained", self.note, self.currentField)
+ gui_hooks.editor_field_did_gain_focus_hook(self.note, self.currentField)
elif cmd in self._links:
self._links[cmd](self)
else:
@@ -414,7 +416,7 @@ class Editor:
self.checkValid()
if focusTo is not None:
self.web.setFocus()
- runHook("loadNote", self)
+ gui_hooks.editor_note_did_load_hook(self)
js = "setFields(%s); setFonts(%s); focusField(%s); setNoteId(%s)" % (
json.dumps(data),
@@ -426,7 +428,7 @@ class Editor:
def fonts(self):
return [
- (runFilter("mungeEditingFontName", f["font"]), f["size"], f["rtl"])
+ (gui_hooks.editor_font_for_field_filter(f["font"]), f["size"], f["rtl"])
for f in self.note.model()["flds"]
]
@@ -534,7 +536,7 @@ class Editor:
self.tags.setText(self.mw.col.tags.join(self.note.tags).strip())
if not self.addMode:
self.note.flush()
- runHook("tagsUpdated", self.note)
+ gui_hooks.editor_tags_did_update_hook(self.note)
def saveAddModeVars(self):
if self.addMode:
@@ -1109,7 +1111,7 @@ class EditorWebView(AnkiWebView):
a.triggered.connect(self.onCopy)
a = m.addAction(_("Paste"))
a.triggered.connect(self.onPaste)
- runHook("EditorWebView.contextMenuEvent", self, m)
+ gui_hooks.editor_context_menu_will_show_hook(self, m)
m.popup(QCursor.pos())
diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py
index c0021f396..d8f33b119 100644
--- a/qt/aqt/gui_hooks.py
+++ b/qt/aqt/gui_hooks.py
@@ -13,7 +13,7 @@ import anki
import aqt
from anki.cards import Card
from anki.hooks import runFilter, runHook
-from aqt.qt import QMenu, QShortcut
+from aqt.qt import QMenu
# New hook/filter handling
##############################################################################
@@ -376,7 +376,7 @@ class _EditorFontForFieldFilter:
editor_font_for_field_filter = _EditorFontForFieldFilter()
-class _EditorNoteDidUpdateHook:
+class _EditorNoteDidLoadHook:
_hooks: List[Callable[["aqt.editor.Editor"], None]] = []
def append(self, cb: Callable[["aqt.editor.Editor"], None]) -> None:
@@ -398,7 +398,7 @@ class _EditorNoteDidUpdateHook:
runHook("loadNote", editor)
-editor_note_did_update_hook = _EditorNoteDidUpdateHook()
+editor_note_did_load_hook = _EditorNoteDidLoadHook()
class _EditorShortcutsDidSetupHook:
@@ -476,7 +476,7 @@ class _EditorTypingTimerDidFireHook:
editor_typing_timer_did_fire_hook = _EditorTypingTimerDidFireHook()
-class _MpvIdleHook:
+class _MpvDidIdleHook:
_hooks: List[Callable[[], None]] = []
def append(self, cb: Callable[[], None]) -> None:
@@ -496,7 +496,7 @@ class _MpvIdleHook:
raise
-mpv_idle_hook = _MpvIdleHook()
+mpv_did_idle_hook = _MpvDidIdleHook()
class _MpvWillPlayHook:
@@ -674,32 +674,6 @@ class _ReviewerQuestionDidShowHook:
reviewer_question_did_show_hook = _ReviewerQuestionDidShowHook()
-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]] = []
@@ -753,6 +727,8 @@ state_did_reset_hook = _StateDidResetHook()
class _StateDidRevertHook:
+ """Called when user used the undo option to restore to an earlier database state."""
+
_hooks: List[Callable[[str], None]] = []
def append(self, cb: Callable[[str], None]) -> None:
@@ -778,16 +754,16 @@ state_did_revert_hook = _StateDidRevertHook()
class _StateShortcutsWillChangeHook:
- _hooks: List[Callable[[str, List[QShortcut]], None]] = []
+ _hooks: List[Callable[[str, List[Tuple[str, Callable]]], None]] = []
- def append(self, cb: Callable[[str, List[QShortcut]], None]) -> None:
- """(state: str, shortcuts: List[QShortcut])"""
+ def append(self, cb: Callable[[str, List[Tuple[str, Callable]]], None]) -> None:
+ """(state: str, shortcuts: List[Tuple[str, Callable]])"""
self._hooks.append(cb)
- def remove(self, cb: Callable[[str, List[QShortcut]], None]) -> None:
+ def remove(self, cb: Callable[[str, List[Tuple[str, Callable]]], None]) -> None:
self._hooks.remove(cb)
- def __call__(self, state: str, shortcuts: List[QShortcut]) -> None:
+ def __call__(self, state: str, shortcuts: List[Tuple[str, Callable]]) -> None:
for hook in self._hooks:
try:
hook(state, shortcuts)
@@ -825,6 +801,32 @@ class _StateWillChangeHook:
state_will_change_hook = _StateWillChangeHook()
+class _StyleDidSetupFilter:
+ _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
+
+
+style_did_setup_filter = _StyleDidSetupFilter()
+
+
class _UndoStateDidChangeHook:
_hooks: List[Callable[[bool], None]] = []
diff --git a/qt/aqt/main.py b/qt/aqt/main.py
index 3ede4ea24..4572cb1ad 100644
--- a/qt/aqt/main.py
+++ b/qt/aqt/main.py
@@ -26,7 +26,7 @@ import aqt.toolbar
import aqt.webview
from anki import hooks
from anki.collection import _Collection
-from anki.hooks import addHook, runFilter, runHook
+from anki.hooks import addHook, runHook
from anki.lang import _, ngettext
from anki.storage import Collection
from anki.utils import devMode, ids2str, intTime, isMac, isWin, splitFields
@@ -341,7 +341,7 @@ close the profile or restart Anki."""
else:
self.handleImport(self.pendingImport)
self.pendingImport = None
- runHook("profileLoaded")
+ gui_hooks.profile_did_open_hook()
if onsuccess:
onsuccess()
@@ -350,7 +350,7 @@ close the profile or restart Anki."""
self._unloadProfile()
onsuccess()
- runHook("unloadProfile")
+ gui_hooks.profile_will_close_hook()
self.unloadCollection(callback)
def _unloadProfile(self) -> None:
@@ -560,11 +560,11 @@ from the profile screen."
cleanup(state)
self.clearStateShortcuts()
self.state = state
- runHook("beforeStateChange", state, oldState, *args)
+ gui_hooks.state_will_change_hook(state, oldState)
getattr(self, "_" + state + "State")(oldState, *args)
if state != "resetRequired":
self.bottomWeb.show()
- runHook("afterStateChange", state, oldState, *args)
+ gui_hooks.state_did_change_hook(state, oldState)
def _deckBrowserState(self, oldState: str) -> None:
self.deckBrowser.show()
@@ -574,7 +574,7 @@ from the profile screen."
self.enableColMenuItems()
# ensure cwd is set if media dir exists
self.col.media.dir()
- runHook("colLoading", self.col)
+ gui_hooks.collection_did_load_hook(self.col)
self.moveToState("overview")
def _selectedDeck(self) -> Optional[Dict[str, Any]]:
@@ -605,7 +605,7 @@ from the profile screen."
if self.col:
if not guiOnly:
self.col.reset()
- runHook("reset")
+ gui_hooks.state_did_reset_hook()
self.maybeEnableUndo()
self.moveToState(self.state)
@@ -847,7 +847,7 @@ QTreeWidget {
"""
# allow addons to modify the styling
- buf = runFilter("setupStyle", buf)
+ buf = gui_hooks.style_did_setup_filter(buf)
# allow users to extend styling
p = os.path.join(aqt.mw.pm.base, "style.css")
@@ -884,6 +884,8 @@ QTreeWidget {
return qshortcuts
def setStateShortcuts(self, shortcuts: List[Tuple[str, Callable]]) -> None:
+ gui_hooks.state_shortcuts_will_change_hook(self.state, shortcuts)
+ # legacy hook
runHook(self.state + "StateShortcuts", shortcuts)
self.stateShortcuts = self.applyShortcuts(shortcuts)
@@ -926,22 +928,22 @@ QTreeWidget {
self.col.sched.reset()
self.reviewer.cardQueue.append(card)
self.reviewer.nextCard()
- runHook("revertedCard", cid)
+ gui_hooks.review_did_undo_hook(cid)
else:
self.reset()
tooltip(_("Reverted to state prior to '%s'.") % n.lower())
- runHook("revertedState", n)
+ gui_hooks.state_did_revert_hook(n)
self.maybeEnableUndo()
def maybeEnableUndo(self) -> None:
if self.col and self.col.undoName():
self.form.actionUndo.setText(_("Undo %s") % self.col.undoName())
self.form.actionUndo.setEnabled(True)
- runHook("undoState", True)
+ gui_hooks.undo_state_did_change_hook(True)
else:
self.form.actionUndo.setText(_("Undo"))
self.form.actionUndo.setEnabled(False)
- runHook("undoState", False)
+ gui_hooks.undo_state_did_change_hook(False)
def checkpoint(self, name):
self.col.save(name)
@@ -1156,7 +1158,7 @@ Difference to correct time: %s."""
hooks.odue_invalid_hook.append(self.onOdueInvalid)
gui_hooks.mpv_will_play_hook.append(self.on_mpv_will_play)
- gui_hooks.mpv_idle_hook.append(self.on_mpv_idle)
+ gui_hooks.mpv_did_idle_hook.append(self.on_mpv_idle)
self._activeWindowOnPlay: Optional[QWidget] = None
diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py
index 200b3ec47..42d7fc827 100644
--- a/qt/aqt/reviewer.py
+++ b/qt/aqt/reviewer.py
@@ -696,7 +696,6 @@ time = %(time)d;
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/sound.py b/qt/aqt/sound.py
index 09182c6df..fb19b18ef 100644
--- a/qt/aqt/sound.py
+++ b/qt/aqt/sound.py
@@ -173,7 +173,7 @@ class MpvManager(MPV):
self.command("seek", secs, "relative")
def on_idle(self) -> None:
- gui_hooks.mpv_idle_hook()
+ gui_hooks.mpv_did_idle_hook()
def setMpvConfigBase(base) -> None:
diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py
index 480c93bfd..82ffcdb6c 100644
--- a/qt/tools/genhooks_gui.py
+++ b/qt/tools/genhooks_gui.py
@@ -17,7 +17,7 @@ from tools.hookslib import Hook, update_file
######################################################################
hooks = [
- Hook(name="mpv_idle"),
+ Hook(name="mpv_did_idle"),
Hook(name="mpv_will_play", args=["file: str"], legacy_hook="mpvWillPlay"),
Hook(
name="reviewer_question_did_show",
@@ -83,14 +83,19 @@ hooks = [
# different sig to original
Hook(
name="state_shortcuts_will_change",
- args=["state: str", "shortcuts: List[QShortcut]"],
+ args=["state: str", "shortcuts: List[Tuple[str, Callable]]"],
),
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_revert",
+ args=["action: str"],
+ legacy_hook="revertedState",
+ doc="Called when user used the undo option to restore to an earlier database state.",
+ ),
Hook(
name="state_did_reset",
legacy_hook="reset",
@@ -101,7 +106,7 @@ hooks = [
),
Hook(name="review_did_undo", args=["card_id: int"], legacy_hook="revertedCard"),
Hook(
- name="setup_style",
+ name="style_did_setup",
args=["style: str"],
return_type="str",
legacy_hook="setupStyle",
@@ -153,7 +158,7 @@ hooks = [
legacy_hook="editFocusLost",
),
Hook(
- name="editor_note_did_update",
+ name="editor_note_did_load",
args=["editor: aqt.editor.Editor"],
legacy_hook="loadNote",
),