mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
pass instance to webview_did_receive_js_message instead of string
This commit is contained in:
parent
5bd67509ae
commit
7fcb6b5672
11 changed files with 129 additions and 41 deletions
|
@ -49,6 +49,15 @@ from aqt.utils import (
|
|||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
|
||||
|
||||
class PreviewDialog(QDialog):
|
||||
pass
|
||||
|
||||
|
||||
class FindDupesDialog(QDialog):
|
||||
pass
|
||||
|
||||
|
||||
# Data model
|
||||
##########################################################################
|
||||
|
||||
|
@ -1536,7 +1545,7 @@ where id in %s"""
|
|||
def _openPreview(self):
|
||||
self._previewState = "question"
|
||||
self._lastPreviewState = None
|
||||
self._previewWindow = QDialog(None, Qt.Window)
|
||||
self._previewWindow = PreviewDialog(None, Qt.Window)
|
||||
self._previewWindow.setWindowTitle(_("Preview"))
|
||||
|
||||
self._previewWindow.finished.connect(self._onPreviewFinished)
|
||||
|
@ -1641,7 +1650,9 @@ where id in %s"""
|
|||
self._previewWeb.stdHtml(
|
||||
self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc
|
||||
)
|
||||
self._previewWeb.set_bridge_command(self._on_preview_bridge_cmd, "preview")
|
||||
self._previewWeb.set_bridge_command(
|
||||
self._on_preview_bridge_cmd, self._previewWindow
|
||||
)
|
||||
|
||||
def _on_preview_bridge_cmd(self, cmd: str) -> Any:
|
||||
if cmd.startswith("play:"):
|
||||
|
@ -2118,7 +2129,7 @@ update cards set usn=?, mod=?, did=? where id in """
|
|||
self.editor.saveNow(self._onFindDupes)
|
||||
|
||||
def _onFindDupes(self):
|
||||
d = QDialog(self)
|
||||
d = FindDupesDialog(self)
|
||||
self.mw.setupDialogGC(d)
|
||||
frm = aqt.forms.finddupes.Ui_Dialog()
|
||||
frm.setupUi(d)
|
||||
|
@ -2129,7 +2140,7 @@ update cards set usn=?, mod=?, did=? where id in """
|
|||
frm.fields.addItems(fields)
|
||||
self._dupesButton = None
|
||||
# links
|
||||
frm.webView.set_bridge_command(self.dupeLinkClicked, "find_dupes")
|
||||
frm.webView.set_bridge_command(self.dupeLinkClicked, d)
|
||||
|
||||
def onFin(code):
|
||||
saveGeom(d, "findDupes")
|
||||
|
|
|
@ -220,8 +220,8 @@ class CardLayout(QDialog):
|
|||
pform.backWeb.stdHtml(
|
||||
self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc
|
||||
)
|
||||
pform.frontWeb.set_bridge_command(self._on_bridge_cmd, "card_layout")
|
||||
pform.backWeb.set_bridge_command(self._on_bridge_cmd, "card_layout")
|
||||
pform.frontWeb.set_bridge_command(self._on_bridge_cmd, self)
|
||||
pform.backWeb.set_bridge_command(self._on_bridge_cmd, self)
|
||||
|
||||
def _on_bridge_cmd(self, cmd: str) -> Any:
|
||||
if cmd.startswith("play:"):
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# -*- coding: utf-8 -*-
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
from typing import Any
|
||||
|
||||
|
@ -15,6 +18,11 @@ from aqt.toolbar import BottomBar
|
|||
from aqt.utils import askUser, getOnlyText, openHelp, openLink, shortcut, showWarning
|
||||
|
||||
|
||||
class DeckBrowserBottomBar:
|
||||
def __init__(self, deck_browser: DeckBrowser):
|
||||
self.deck_browser = deck_browser
|
||||
|
||||
|
||||
class DeckBrowser:
|
||||
_dueTree: Any
|
||||
|
||||
|
@ -26,7 +34,7 @@ class DeckBrowser:
|
|||
|
||||
def show(self):
|
||||
av_player.stop_and_clear_queue()
|
||||
self.web.set_bridge_command(self._linkHandler, "deck_browser")
|
||||
self.web.set_bridge_command(self._linkHandler, self)
|
||||
self._renderPage()
|
||||
# redraw top bar for theme change
|
||||
self.mw.toolbar.draw()
|
||||
|
@ -333,7 +341,9 @@ where id > ?""",
|
|||
b
|
||||
)
|
||||
self.bottom.draw(buf)
|
||||
self.bottom.web.set_bridge_command(self._linkHandler, "deck_browser_bottom_bar")
|
||||
self.bottom.web.set_bridge_command(
|
||||
self._linkHandler, DeckBrowserBottomBar(self)
|
||||
)
|
||||
|
||||
def _onShared(self):
|
||||
openLink(aqt.appShared + "decks/")
|
||||
|
|
|
@ -97,7 +97,7 @@ class Editor:
|
|||
self.web = EditorWebView(self.widget, self)
|
||||
self.web.title = "editor"
|
||||
self.web.allowDrops = True
|
||||
self.web.set_bridge_command(self.onBridgeCmd, "editor")
|
||||
self.web.set_bridge_command(self.onBridgeCmd, self)
|
||||
self.outerLayout.addWidget(self.web, 1)
|
||||
|
||||
righttopbtns: List[str] = [
|
||||
|
|
|
@ -1070,33 +1070,51 @@ undo_state_did_change = _UndoStateDidChangeHook()
|
|||
class _WebviewDidReceiveJsMessageFilter:
|
||||
"""Used to handle pycmd() messages sent from Javascript.
|
||||
|
||||
Message is the string passed to pycmd(). Context is what was
|
||||
passed to set_bridge_command(), such as 'editor' or 'reviewer'.
|
||||
|
||||
For messages you don't want to handle, return handled unchanged.
|
||||
Message is the string passed to pycmd().
|
||||
|
||||
For messages you don't want to handle, return 'handled' unchanged.
|
||||
|
||||
If you handle a message and don't want it passed to the original
|
||||
bridge command handler, return (True, None).
|
||||
|
||||
|
||||
If you want to pass a value to pycmd's result callback, you can
|
||||
return it with (True, some_value)."""
|
||||
return it with (True, some_value).
|
||||
|
||||
Context is the instance that was passed to set_bridge_command().
|
||||
It can be inspected to check which screen this hook is firing
|
||||
in, and to get a reference to the screen. For example, if your
|
||||
code wishes to function only in the review screen, you could do:
|
||||
|
||||
_hooks: List[Callable[[Tuple[bool, Any], str, str], Tuple[bool, Any]]] = []
|
||||
if not isinstance(context, aqt.reviewer.Reviewer):
|
||||
# not reviewer, pass on message
|
||||
return handled
|
||||
|
||||
if message == "my-mark-action":
|
||||
# our message, call onMark() on the reviewer instance
|
||||
context.onMark()
|
||||
# and don't pass message to other handlers
|
||||
return (True, None)
|
||||
else:
|
||||
# some other command, pass it on
|
||||
return handled
|
||||
"""
|
||||
|
||||
_hooks: List[Callable[[Tuple[bool, Any], str, Any], Tuple[bool, Any]]] = []
|
||||
|
||||
def append(
|
||||
self, cb: Callable[[Tuple[bool, Any], str, str], Tuple[bool, Any]]
|
||||
self, cb: Callable[[Tuple[bool, Any], str, Any], Tuple[bool, Any]]
|
||||
) -> None:
|
||||
"""(handled: Tuple[bool, Any], message: str, context: str)"""
|
||||
"""(handled: Tuple[bool, Any], message: str, context: Any)"""
|
||||
self._hooks.append(cb)
|
||||
|
||||
def remove(
|
||||
self, cb: Callable[[Tuple[bool, Any], str, str], Tuple[bool, Any]]
|
||||
self, cb: Callable[[Tuple[bool, Any], str, Any], Tuple[bool, Any]]
|
||||
) -> None:
|
||||
if cb in self._hooks:
|
||||
self._hooks.remove(cb)
|
||||
|
||||
def __call__(
|
||||
self, handled: Tuple[bool, Any], message: str, context: str
|
||||
self, handled: Tuple[bool, Any], message: str, context: Any
|
||||
) -> Tuple[bool, Any]:
|
||||
for filter in self._hooks:
|
||||
try:
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import faulthandler
|
||||
import gc
|
||||
import os
|
||||
|
@ -58,6 +60,11 @@ from aqt.utils import (
|
|||
install_pylib_legacy()
|
||||
|
||||
|
||||
class ResetRequired:
|
||||
def __init__(self, mw: AnkiQt):
|
||||
self.mw = mw
|
||||
|
||||
|
||||
class AnkiQt(QMainWindow):
|
||||
col: _Collection
|
||||
pm: ProfileManagerType
|
||||
|
@ -647,14 +654,14 @@ from the profile screen."
|
|||
# windows
|
||||
self.progress.timer(100, self.maybeReset, False)
|
||||
|
||||
def _resetRequiredState(self, oldState):
|
||||
def _resetRequiredState(self, oldState: str) -> None:
|
||||
if oldState != "resetRequired":
|
||||
self.returnState = oldState
|
||||
if self.resetModal:
|
||||
# we don't have to change the webview, as we have a covering window
|
||||
return
|
||||
self.web.set_bridge_command(
|
||||
lambda url: self.delayedMaybeReset(), "reset_required"
|
||||
lambda url: self.delayedMaybeReset(), ResetRequired(self)
|
||||
)
|
||||
i = _("Waiting for editing to finish.")
|
||||
b = self.button("refresh", _("Resume Now"), id="resume")
|
||||
|
|
|
@ -12,6 +12,11 @@ from aqt.toolbar import BottomBar
|
|||
from aqt.utils import askUserDialog, openLink, shortcut, tooltip
|
||||
|
||||
|
||||
class OverviewBottomBar:
|
||||
def __init__(self, overview: Overview):
|
||||
self.overview = overview
|
||||
|
||||
|
||||
class Overview:
|
||||
"Deck overview."
|
||||
|
||||
|
@ -22,7 +27,7 @@ class Overview:
|
|||
|
||||
def show(self):
|
||||
av_player.stop_and_clear_queue()
|
||||
self.web.set_bridge_command(self._linkHandler, "overview")
|
||||
self.web.set_bridge_command(self._linkHandler, self)
|
||||
self.mw.setStateShortcuts(self._shortcutKeys())
|
||||
self.refresh()
|
||||
|
||||
|
@ -239,7 +244,7 @@ to their original deck."""
|
|||
b
|
||||
)
|
||||
self.bottom.draw(buf)
|
||||
self.bottom.web.set_bridge_command(self._linkHandler, "overview_bottom")
|
||||
self.bottom.web.set_bridge_command(self._linkHandler, OverviewBottomBar(self))
|
||||
|
||||
# Studying more
|
||||
######################################################################
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import difflib
|
||||
import html
|
||||
import html.parser
|
||||
|
@ -22,6 +24,11 @@ from aqt.toolbar import BottomBar
|
|||
from aqt.utils import askUserDialog, downArrow, qtMenuShortcutWorkaround, tooltip
|
||||
|
||||
|
||||
class ReviewerBottomBar:
|
||||
def __init__(self, reviewer: Reviewer):
|
||||
self.reviewer = reviewer
|
||||
|
||||
|
||||
class Reviewer:
|
||||
"Manage reviews. Maintains a separate state."
|
||||
|
||||
|
@ -41,8 +48,8 @@ class Reviewer:
|
|||
def show(self):
|
||||
self.mw.col.reset()
|
||||
self.mw.setStateShortcuts(self._shortcutKeys())
|
||||
self.web.set_bridge_command(self._linkHandler, "reviewer")
|
||||
self.bottom.web.set_bridge_command(self._linkHandler, "reviewer_bottom")
|
||||
self.web.set_bridge_command(self._linkHandler, self)
|
||||
self.bottom.web.set_bridge_command(self._linkHandler, ReviewerBottomBar(self))
|
||||
self._reps = None
|
||||
self.nextCard()
|
||||
|
||||
|
|
|
@ -10,6 +10,18 @@ from aqt.qt import *
|
|||
from aqt.webview import AnkiWebView
|
||||
|
||||
|
||||
# wrapper class for set_bridge_command()
|
||||
class TopToolbar:
|
||||
def __init__(self, toolbar: Toolbar):
|
||||
self.toolbar = toolbar
|
||||
|
||||
|
||||
# wrapper class for set_bridge_command()
|
||||
class BottomToolbar:
|
||||
def __init__(self, toolbar: Toolbar):
|
||||
self.toolbar = toolbar
|
||||
|
||||
|
||||
class Toolbar:
|
||||
def __init__(self, mw: aqt.AnkiQt, web: AnkiWebView) -> None:
|
||||
self.mw = mw
|
||||
|
@ -26,7 +38,7 @@ class Toolbar:
|
|||
self.web.requiresCol = False
|
||||
|
||||
def draw(self):
|
||||
self.web.set_bridge_command(self._linkHandler, "top_toolbar")
|
||||
self.web.set_bridge_command(self._linkHandler, TopToolbar(self))
|
||||
self.web.stdHtml(self._body % self._centerLinks(), css=["toolbar.css"])
|
||||
self.web.adjustHeightToFit()
|
||||
|
||||
|
@ -112,7 +124,7 @@ class BottomBar(Toolbar):
|
|||
|
||||
def draw(self, buf):
|
||||
# note: some screens may override this
|
||||
self.web.set_bridge_command(self._linkHandler, "bottom_toolbar")
|
||||
self.web.set_bridge_command(self._linkHandler, BottomToolbar(self))
|
||||
self.web.stdHtml(
|
||||
self._centerBody % buf, css=["toolbar.css", "toolbar-bottom.css"]
|
||||
)
|
||||
|
|
|
@ -414,7 +414,7 @@ body {{ zoom: {}; background: {}; {} }}
|
|||
self._maybeRunActions()
|
||||
else:
|
||||
handled, result = gui_hooks.webview_did_receive_js_message(
|
||||
(False, None), cmd, self._bridge_command_name
|
||||
(False, None), cmd, self._bridge_context
|
||||
)
|
||||
if handled:
|
||||
return result
|
||||
|
@ -427,7 +427,7 @@ body {{ zoom: {}; background: {}; {} }}
|
|||
# legacy
|
||||
def resetHandlers(self):
|
||||
self.onBridgeCmd = self.defaultOnBridgeCmd
|
||||
self._bridge_command_name = "unknown"
|
||||
self._bridge_context = None
|
||||
|
||||
def adjustHeightToFit(self):
|
||||
self.evalWithCallback("$(document.body).height()", self._onHeight)
|
||||
|
@ -447,10 +447,10 @@ body {{ zoom: {}; background: {}; {} }}
|
|||
height = math.ceil(qvar * scaleFactor)
|
||||
self.setFixedHeight(height)
|
||||
|
||||
def set_bridge_command(self, func: Callable[[str], Any], context: str) -> None:
|
||||
def set_bridge_command(self, func: Callable[[str], Any], context: Any) -> None:
|
||||
"""Set a handler for pycmd() messages received from Javascript.
|
||||
|
||||
Context is a human readable name that is provided to the
|
||||
webview_did_receive_js_message hook."""
|
||||
Context is the object calling this routine, eg an instance of
|
||||
aqt.reviewer.Reviewer or aqt.deckbrowser.DeckBrowser."""
|
||||
self.onBridgeCmd = func
|
||||
self._bridge_command_name = context
|
||||
self._bridge_context = context
|
||||
|
|
|
@ -130,20 +130,38 @@ hooks = [
|
|||
###################
|
||||
Hook(
|
||||
name="webview_did_receive_js_message",
|
||||
args=["handled: Tuple[bool, Any]", "message: str", "context: str"],
|
||||
args=["handled: Tuple[bool, Any]", "message: str", "context: Any"],
|
||||
return_type="Tuple[bool, Any]",
|
||||
doc="""Used to handle pycmd() messages sent from Javascript.
|
||||
|
||||
Message is the string passed to pycmd(). Context is what was
|
||||
passed to set_bridge_command(), such as 'editor' or 'reviewer'.
|
||||
|
||||
For messages you don't want to handle, return handled unchanged.
|
||||
Message is the string passed to pycmd().
|
||||
|
||||
For messages you don't want to handle, return 'handled' unchanged.
|
||||
|
||||
If you handle a message and don't want it passed to the original
|
||||
bridge command handler, return (True, None).
|
||||
|
||||
|
||||
If you want to pass a value to pycmd's result callback, you can
|
||||
return it with (True, some_value).""",
|
||||
return it with (True, some_value).
|
||||
|
||||
Context is the instance that was passed to set_bridge_command().
|
||||
It can be inspected to check which screen this hook is firing
|
||||
in, and to get a reference to the screen. For example, if your
|
||||
code wishes to function only in the review screen, you could do:
|
||||
|
||||
if not isinstance(context, aqt.reviewer.Reviewer):
|
||||
# not reviewer, pass on message
|
||||
return handled
|
||||
|
||||
if message == "my-mark-action":
|
||||
# our message, call onMark() on the reviewer instance
|
||||
context.onMark()
|
||||
# and don't pass message to other handlers
|
||||
return (True, None)
|
||||
else:
|
||||
# some other command, pass it on
|
||||
return handled
|
||||
""",
|
||||
),
|
||||
Hook(
|
||||
name="webview_will_show_context_menu",
|
||||
|
|
Loading…
Reference in a new issue