Add webview_will_set_content hook & update supporting code accordingly
This commit is contained in:
parent
df2a7b06ef
commit
bbd667b0ff
@ -1650,18 +1650,21 @@ where id in %s"""
|
|||||||
|
|
||||||
def _setupPreviewWebview(self):
|
def _setupPreviewWebview(self):
|
||||||
jsinc = [
|
jsinc = [
|
||||||
"jquery.js",
|
"_anki/jquery.js",
|
||||||
"browsersel.js",
|
"_anki/browsersel.js",
|
||||||
"mathjax/conf.js",
|
"_anki/mathjax/conf.js",
|
||||||
"mathjax/MathJax.js",
|
"_anki/mathjax/MathJax.js",
|
||||||
"reviewer.js",
|
"_anki/reviewer.js",
|
||||||
]
|
]
|
||||||
|
web_context = PreviewDialog(dialog=self._previewWindow, browser=self)
|
||||||
self._previewWeb.stdHtml(
|
self._previewWeb.stdHtml(
|
||||||
self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc
|
self.mw.reviewer.revHtml(),
|
||||||
|
css=["_anki/reviewer.css"],
|
||||||
|
js=jsinc,
|
||||||
|
context=web_context,
|
||||||
)
|
)
|
||||||
self._previewWeb.set_bridge_command(
|
self._previewWeb.set_bridge_command(
|
||||||
self._on_preview_bridge_cmd,
|
self._on_preview_bridge_cmd, web_context,
|
||||||
PreviewDialog(dialog=self._previewWindow, browser=self),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _on_preview_bridge_cmd(self, cmd: str) -> Any:
|
def _on_preview_bridge_cmd(self, cmd: str) -> Any:
|
||||||
@ -2159,10 +2162,9 @@ update cards set usn=?, mod=?, did=? where id in """
|
|||||||
self._dupesButton = None
|
self._dupesButton = None
|
||||||
# links
|
# links
|
||||||
frm.webView.title = "find duplicates"
|
frm.webView.title = "find duplicates"
|
||||||
frm.webView.set_bridge_command(
|
web_context = FindDupesDialog(dialog=d, browser=self)
|
||||||
self.dupeLinkClicked, FindDupesDialog(dialog=d, browser=self)
|
frm.webView.set_bridge_command(self.dupeLinkClicked, web_context)
|
||||||
)
|
frm.webView.stdHtml("", context=web_context)
|
||||||
frm.webView.stdHtml("")
|
|
||||||
|
|
||||||
def onFin(code):
|
def onFin(code):
|
||||||
saveGeom(d, "findDupes")
|
saveGeom(d, "findDupes")
|
||||||
@ -2171,13 +2173,15 @@ update cards set usn=?, mod=?, did=? where id in """
|
|||||||
|
|
||||||
def onClick():
|
def onClick():
|
||||||
field = fields[frm.fields.currentIndex()]
|
field = fields[frm.fields.currentIndex()]
|
||||||
self.duplicatesReport(frm.webView, field, frm.search.text(), frm)
|
self.duplicatesReport(
|
||||||
|
frm.webView, field, frm.search.text(), frm, web_context
|
||||||
|
)
|
||||||
|
|
||||||
search = frm.buttonBox.addButton(_("Search"), QDialogButtonBox.ActionRole)
|
search = frm.buttonBox.addButton(_("Search"), QDialogButtonBox.ActionRole)
|
||||||
search.clicked.connect(onClick)
|
search.clicked.connect(onClick)
|
||||||
d.show()
|
d.show()
|
||||||
|
|
||||||
def duplicatesReport(self, web, fname, search, frm):
|
def duplicatesReport(self, web, fname, search, frm, web_context):
|
||||||
self.mw.progress.start()
|
self.mw.progress.start()
|
||||||
res = self.mw.col.findDupes(fname, search)
|
res = self.mw.col.findDupes(fname, search)
|
||||||
if not self._dupesButton:
|
if not self._dupesButton:
|
||||||
@ -2202,7 +2206,7 @@ update cards set usn=?, mod=?, did=? where id in """
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
t += "</ol>"
|
t += "</ol>"
|
||||||
web.stdHtml(t)
|
web.stdHtml(t, context=web_context)
|
||||||
self.mw.progress.finish()
|
self.mw.progress.finish()
|
||||||
|
|
||||||
def _onTagDupes(self, res):
|
def _onTagDupes(self, res):
|
||||||
|
@ -208,17 +208,17 @@ class CardLayout(QDialog):
|
|||||||
pform.backWeb = AnkiWebView(title="card layout back")
|
pform.backWeb = AnkiWebView(title="card layout back")
|
||||||
pform.backPrevBox.addWidget(pform.backWeb)
|
pform.backPrevBox.addWidget(pform.backWeb)
|
||||||
jsinc = [
|
jsinc = [
|
||||||
"jquery.js",
|
"_anki/jquery.js",
|
||||||
"browsersel.js",
|
"_anki/browsersel.js",
|
||||||
"mathjax/conf.js",
|
"_anki/mathjax/conf.js",
|
||||||
"mathjax/MathJax.js",
|
"_anki/mathjax/MathJax.js",
|
||||||
"reviewer.js",
|
"_anki/reviewer.js",
|
||||||
]
|
]
|
||||||
pform.frontWeb.stdHtml(
|
pform.frontWeb.stdHtml(
|
||||||
self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc
|
self.mw.reviewer.revHtml(), css=["_anki/reviewer.css"], js=jsinc, context=self
|
||||||
)
|
)
|
||||||
pform.backWeb.stdHtml(
|
pform.backWeb.stdHtml(
|
||||||
self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc
|
self.mw.reviewer.revHtml(), css=["_anki/reviewer.css"], js=jsinc, context=self
|
||||||
)
|
)
|
||||||
pform.frontWeb.set_bridge_command(self._on_bridge_cmd, self)
|
pform.frontWeb.set_bridge_command(self._on_bridge_cmd, self)
|
||||||
pform.backWeb.set_bridge_command(self._on_bridge_cmd, self)
|
pform.backWeb.set_bridge_command(self._on_bridge_cmd, self)
|
||||||
|
@ -107,8 +107,9 @@ class DeckBrowser:
|
|||||||
stats = self._renderStats()
|
stats = self._renderStats()
|
||||||
self.web.stdHtml(
|
self.web.stdHtml(
|
||||||
self._body % dict(tree=tree, stats=stats, countwarn=self._countWarn()),
|
self._body % dict(tree=tree, stats=stats, countwarn=self._countWarn()),
|
||||||
css=["deckbrowser.css"],
|
css=["_anki/deckbrowser.css"],
|
||||||
js=["jquery.js", "jquery-ui.js", "deckbrowser.js"],
|
js=["_anki/jquery.js", "_anki/jquery-ui.js", "_anki/deckbrowser.js"],
|
||||||
|
context=self,
|
||||||
)
|
)
|
||||||
self.web.key = "deckBrowser"
|
self.web.key = "deckBrowser"
|
||||||
self._drawButtons()
|
self._drawButtons()
|
||||||
@ -340,9 +341,10 @@ where id > ?""",
|
|||||||
<button title='%s' onclick='pycmd(\"%s\");'>%s</button>""" % tuple(
|
<button title='%s' onclick='pycmd(\"%s\");'>%s</button>""" % tuple(
|
||||||
b
|
b
|
||||||
)
|
)
|
||||||
self.bottom.draw(buf)
|
self.bottom.draw(
|
||||||
self.bottom.web.set_bridge_command(
|
buf=buf,
|
||||||
self._linkHandler, DeckBrowserBottomBar(self)
|
link_handler=self._linkHandler,
|
||||||
|
web_context=DeckBrowserBottomBar(self),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _onShared(self):
|
def _onShared(self):
|
||||||
|
@ -164,8 +164,9 @@ class Editor:
|
|||||||
# then load page
|
# then load page
|
||||||
self.web.stdHtml(
|
self.web.stdHtml(
|
||||||
_html % (bgcol, bgcol, topbuts, _("Show Duplicates")),
|
_html % (bgcol, bgcol, topbuts, _("Show Duplicates")),
|
||||||
css=["editor.css"],
|
css=["_anki/editor.css"],
|
||||||
js=["jquery.js", "editor.js"],
|
js=["_anki/jquery.js", "_anki/editor.js"],
|
||||||
|
context=self,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Top buttons
|
# Top buttons
|
||||||
|
@ -1129,6 +1129,56 @@ class _WebviewDidReceiveJsMessageFilter:
|
|||||||
webview_did_receive_js_message = _WebviewDidReceiveJsMessageFilter()
|
webview_did_receive_js_message = _WebviewDidReceiveJsMessageFilter()
|
||||||
|
|
||||||
|
|
||||||
|
class _WebviewWillSetContentHook:
|
||||||
|
"""Used to modify web content before it is rendered.
|
||||||
|
|
||||||
|
Web_content contains the HTML, JS, and CSS the web view will be
|
||||||
|
populated with.
|
||||||
|
|
||||||
|
Context is the instance that was passed to stdHtml().
|
||||||
|
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, do not modify content
|
||||||
|
return
|
||||||
|
|
||||||
|
web_content.js.append("my_addon.js")
|
||||||
|
web_content.css.append("my_addon.css")
|
||||||
|
web_content.head += "<script>console.log('my_addon')</script>"
|
||||||
|
web_content.body += "<div id='my-addon'></div>"
|
||||||
|
"""
|
||||||
|
|
||||||
|
_hooks: List[Callable[["aqt.webview.WebContent", Optional[Any]], None]] = []
|
||||||
|
|
||||||
|
def append(
|
||||||
|
self, cb: Callable[["aqt.webview.WebContent", Optional[Any]], None]
|
||||||
|
) -> None:
|
||||||
|
"""(web_content: aqt.webview.WebContent, context: Optional[Any])"""
|
||||||
|
self._hooks.append(cb)
|
||||||
|
|
||||||
|
def remove(
|
||||||
|
self, cb: Callable[["aqt.webview.WebContent", Optional[Any]], None]
|
||||||
|
) -> None:
|
||||||
|
if cb in self._hooks:
|
||||||
|
self._hooks.remove(cb)
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self, web_content: aqt.webview.WebContent, context: Optional[Any]
|
||||||
|
) -> None:
|
||||||
|
for hook in self._hooks:
|
||||||
|
try:
|
||||||
|
hook(web_content, context)
|
||||||
|
except:
|
||||||
|
# if the hook fails, remove it
|
||||||
|
self._hooks.remove(hook)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
webview_will_set_content = _WebviewWillSetContentHook()
|
||||||
|
|
||||||
|
|
||||||
class _WebviewWillShowContextMenuHook:
|
class _WebviewWillShowContextMenuHook:
|
||||||
_hooks: List[Callable[["aqt.webview.AnkiWebView", QMenu], None]] = []
|
_hooks: List[Callable[["aqt.webview.AnkiWebView", QMenu], None]] = []
|
||||||
|
|
||||||
|
@ -663,9 +663,8 @@ from the profile screen."
|
|||||||
if self.resetModal:
|
if self.resetModal:
|
||||||
# we don't have to change the webview, as we have a covering window
|
# we don't have to change the webview, as we have a covering window
|
||||||
return
|
return
|
||||||
self.web.set_bridge_command(
|
web_context = ResetRequired(self)
|
||||||
lambda url: self.delayedMaybeReset(), ResetRequired(self)
|
self.web.set_bridge_command(lambda url: self.delayedMaybeReset(), web_context)
|
||||||
)
|
|
||||||
i = _("Waiting for editing to finish.")
|
i = _("Waiting for editing to finish.")
|
||||||
b = self.button("refresh", _("Resume Now"), id="resume")
|
b = self.button("refresh", _("Resume Now"), id="resume")
|
||||||
self.web.stdHtml(
|
self.web.stdHtml(
|
||||||
@ -676,7 +675,8 @@ from the profile screen."
|
|||||||
%s</div></div></center>
|
%s</div></div></center>
|
||||||
<script>$('#resume').focus()</script>
|
<script>$('#resume').focus()</script>
|
||||||
"""
|
"""
|
||||||
% (i, b)
|
% (i, b),
|
||||||
|
context=web_context,
|
||||||
)
|
)
|
||||||
self.bottomWeb.hide()
|
self.bottomWeb.hide()
|
||||||
self.web.setFocus()
|
self.web.setFocus()
|
||||||
|
@ -149,8 +149,9 @@ class Overview:
|
|||||||
desc=self._desc(deck),
|
desc=self._desc(deck),
|
||||||
table=self._table(),
|
table=self._table(),
|
||||||
),
|
),
|
||||||
css=["overview.css"],
|
css=["_anki/overview.css"],
|
||||||
js=["jquery.js", "overview.js"],
|
js=["_anki/jquery.js", "_anki/overview.js"],
|
||||||
|
context=self,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _desc(self, deck):
|
def _desc(self, deck):
|
||||||
@ -243,8 +244,9 @@ to their original deck."""
|
|||||||
<button title="%s" onclick='pycmd("%s")'>%s</button>""" % tuple(
|
<button title="%s" onclick='pycmd("%s")'>%s</button>""" % tuple(
|
||||||
b
|
b
|
||||||
)
|
)
|
||||||
self.bottom.draw(buf)
|
self.bottom.draw(
|
||||||
self.bottom.web.set_bridge_command(self._linkHandler, OverviewBottomBar(self))
|
buf=buf, link_handler=self._linkHandler, web_context=OverviewBottomBar(self)
|
||||||
|
)
|
||||||
|
|
||||||
# Studying more
|
# Studying more
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -145,21 +145,23 @@ class Reviewer:
|
|||||||
# main window
|
# main window
|
||||||
self.web.stdHtml(
|
self.web.stdHtml(
|
||||||
self.revHtml(),
|
self.revHtml(),
|
||||||
css=["reviewer.css"],
|
css=["_anki/reviewer.css"],
|
||||||
js=[
|
js=[
|
||||||
"jquery.js",
|
"_anki/jquery.js",
|
||||||
"browsersel.js",
|
"_anki/browsersel.js",
|
||||||
"mathjax/conf.js",
|
"_anki/mathjax/conf.js",
|
||||||
"mathjax/MathJax.js",
|
"_anki/mathjax/MathJax.js",
|
||||||
"reviewer.js",
|
"_anki/reviewer.js",
|
||||||
],
|
],
|
||||||
|
context=self,
|
||||||
)
|
)
|
||||||
# show answer / ease buttons
|
# show answer / ease buttons
|
||||||
self.bottom.web.show()
|
self.bottom.web.show()
|
||||||
self.bottom.web.stdHtml(
|
self.bottom.web.stdHtml(
|
||||||
self._bottomHTML(),
|
self._bottomHTML(),
|
||||||
css=["toolbar-bottom.css", "reviewer-bottom.css"],
|
css=["_anki/toolbar-bottom.css", "_anki/reviewer-bottom.css"],
|
||||||
js=["jquery.js", "reviewer-bottom.js"],
|
js=["_anki/jquery.js", "_anki/reviewer-bottom.js"],
|
||||||
|
context=ReviewerBottomBar(self),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Showing the question
|
# Showing the question
|
||||||
|
@ -97,6 +97,8 @@ class DeckStats(QDialog):
|
|||||||
self.report = stats.report(type=self.period)
|
self.report = stats.report(type=self.period)
|
||||||
self.form.web.title = "deck stats"
|
self.form.web.title = "deck stats"
|
||||||
self.form.web.stdHtml(
|
self.form.web.stdHtml(
|
||||||
"<html><body>" + self.report + "</body></html>", js=["jquery.js", "plot.js"]
|
"<html><body>" + self.report + "</body></html>",
|
||||||
|
js=["_anki/jquery.js", "_anki/plot.js"],
|
||||||
|
context=self,
|
||||||
)
|
)
|
||||||
self.mw.progress.finish()
|
self.mw.progress.finish()
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional, Any
|
||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
@ -37,9 +39,18 @@ class Toolbar:
|
|||||||
self.web.setFixedHeight(30)
|
self.web.setFixedHeight(30)
|
||||||
self.web.requiresCol = False
|
self.web.requiresCol = False
|
||||||
|
|
||||||
def draw(self):
|
def draw(
|
||||||
self.web.set_bridge_command(self._linkHandler, TopToolbar(self))
|
self,
|
||||||
self.web.stdHtml(self._body % self._centerLinks(), css=["toolbar.css"])
|
buf: str = "",
|
||||||
|
web_context: Optional[Any] = None,
|
||||||
|
link_handler: Optional[Callable[[str], Any]] = None,
|
||||||
|
):
|
||||||
|
web_context = web_context or TopToolbar(self)
|
||||||
|
link_handler = link_handler or self._linkHandler
|
||||||
|
self.web.set_bridge_command(link_handler, web_context)
|
||||||
|
self.web.stdHtml(
|
||||||
|
self._body % self._centerLinks(), css=["_anki/toolbar.css"], context=web_context
|
||||||
|
)
|
||||||
self.web.adjustHeightToFit()
|
self.web.adjustHeightToFit()
|
||||||
|
|
||||||
# Available links
|
# Available links
|
||||||
@ -122,10 +133,19 @@ class BottomBar(Toolbar):
|
|||||||
%s</td></tr></table></center>
|
%s</td></tr></table></center>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def draw(self, buf):
|
def draw(
|
||||||
|
self,
|
||||||
|
buf: str = "",
|
||||||
|
web_context: Optional[Any] = None,
|
||||||
|
link_handler: Optional[Callable[[str], Any]] = None,
|
||||||
|
):
|
||||||
# note: some screens may override this
|
# note: some screens may override this
|
||||||
self.web.set_bridge_command(self._linkHandler, BottomToolbar(self))
|
web_context = web_context or BottomToolbar(self)
|
||||||
|
link_handler = link_handler or self._linkHandler
|
||||||
|
self.web.set_bridge_command(link_handler, web_context)
|
||||||
self.web.stdHtml(
|
self.web.stdHtml(
|
||||||
self._centerBody % buf, css=["toolbar.css", "toolbar-bottom.css"]
|
self._centerBody % buf,
|
||||||
|
css=["_anki/toolbar.css", "_anki/toolbar-bottom.css"],
|
||||||
|
context=web_context,
|
||||||
)
|
)
|
||||||
self.web.adjustHeightToFit()
|
self.web.adjustHeightToFit()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# Copyright: Ankitects Pty Ltd and contributors
|
# Copyright: Ankitects Pty Ltd and contributors
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
import dataclasses
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
import sys
|
import sys
|
||||||
@ -96,6 +97,64 @@ class AnkiWebPage(QWebEnginePage): # type: ignore
|
|||||||
return self._onBridgeCmd(str)
|
return self._onBridgeCmd(str)
|
||||||
|
|
||||||
|
|
||||||
|
# Add-ons
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class WebContent:
|
||||||
|
"""Stores all dynamically modified content that a particular web view will be
|
||||||
|
populated with.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
body {str} -- HTML body
|
||||||
|
head {str} -- HTML head
|
||||||
|
css {List[str]} -- List of media server subpaths, each pointing to a CSS file
|
||||||
|
js {List[str]} -- List of media server subpaths, each pointing to a JS file
|
||||||
|
|
||||||
|
Important Notes:
|
||||||
|
- When modifying the attributes specified above, please make sure your changes
|
||||||
|
only perform the minimum requried 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_webview_will_set_content(web_content: WebContent, context):
|
||||||
|
web_content.body += "<my_html>"
|
||||||
|
web_content.head += "<my_head>"
|
||||||
|
web_content.css.append("my_addon.css")
|
||||||
|
web_content.js.append("my_addon.js")
|
||||||
|
|
||||||
|
- The paths specified in `css` and `js` need to be accessible by Anki's media
|
||||||
|
server. Web components shipping with Anki are located under the `_anki`
|
||||||
|
subpath.
|
||||||
|
|
||||||
|
Add-ons may expose their own web components by utilizing
|
||||||
|
aqt.addons.AddonManager.setWebExports(). Web exports registered in this
|
||||||
|
manner may then be accessed under the `_addons` subpath.
|
||||||
|
|
||||||
|
E.g., to allow access to an `addon.js` and `addon.css` residing in a "web"
|
||||||
|
subfolder in your add-on package, first register the corresponding web export:
|
||||||
|
|
||||||
|
> from aqt import mw
|
||||||
|
> mw.addonManager.setWebExports(__name__, r"web/.*(css|js)")
|
||||||
|
|
||||||
|
Then append the subpaths to the corresponding web_content fields within a
|
||||||
|
function subscribing to gui_hooks.webview_will_set_content:
|
||||||
|
|
||||||
|
def on_webview_will_set_content(web_content: WebContent, context):
|
||||||
|
addon_package = mw.addonManager.addonFromModule(__name__)
|
||||||
|
web_content.css.append(
|
||||||
|
f"_addons/{addon_package}/web/addon.css")
|
||||||
|
web_content.js.append(
|
||||||
|
f"_addons/{addon_package}/web/addon.js")
|
||||||
|
"""
|
||||||
|
|
||||||
|
body: str = ""
|
||||||
|
head: str = ""
|
||||||
|
css: List[str] = dataclasses.field(default_factory=lambda: [])
|
||||||
|
js: List[str] = dataclasses.field(default_factory=lambda: [])
|
||||||
|
|
||||||
|
|
||||||
# Main web view
|
# Main web view
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
@ -256,11 +315,23 @@ class AnkiWebView(QWebEngineView): # type: ignore
|
|||||||
return QColor("#ececec")
|
return QColor("#ececec")
|
||||||
return self.style().standardPalette().color(QPalette.Window)
|
return self.style().standardPalette().color(QPalette.Window)
|
||||||
|
|
||||||
def stdHtml(self, body, css=None, js=None, head=""):
|
def stdHtml(
|
||||||
if css is None:
|
self,
|
||||||
css = []
|
body: str,
|
||||||
if js is None:
|
css: Optional[List[str]] = None,
|
||||||
js = ["jquery.js"]
|
js: Optional[List[str]] = None,
|
||||||
|
head: str = "",
|
||||||
|
context: Optional[Any] = None,
|
||||||
|
):
|
||||||
|
|
||||||
|
web_content = WebContent(
|
||||||
|
body=body,
|
||||||
|
head=head,
|
||||||
|
js=["_anki/webview.js"] + (["_anki/jquery.js"] if js is None else js),
|
||||||
|
css=["_anki/webview.css"] + ([] if css is None else css),
|
||||||
|
)
|
||||||
|
|
||||||
|
gui_hooks.webview_will_set_content(web_content, context)
|
||||||
|
|
||||||
palette = self.style().standardPalette()
|
palette = self.style().standardPalette()
|
||||||
color_hl = palette.color(QPalette.Highlight).name()
|
color_hl = palette.color(QPalette.Highlight).name()
|
||||||
@ -301,16 +372,12 @@ div[contenteditable="true"]:focus {
|
|||||||
"color_hl_txt": color_hl_txt,
|
"color_hl_txt": color_hl_txt,
|
||||||
}
|
}
|
||||||
|
|
||||||
csstxt = "\n".join(
|
csstxt = "\n".join(self.bundledCSS(fname) for fname in web_content.css)
|
||||||
[self.bundledCSS("webview.css")] + [self.bundledCSS(fname) for fname in css]
|
jstxt = "\n".join(self.bundledScript(fname) for fname in web_content.js)
|
||||||
)
|
|
||||||
jstxt = "\n".join(
|
|
||||||
[self.bundledScript("webview.js")]
|
|
||||||
+ [self.bundledScript(fname) for fname in js]
|
|
||||||
)
|
|
||||||
from aqt import mw
|
from aqt import mw
|
||||||
|
|
||||||
head = mw.baseHTML() + head + csstxt + jstxt
|
head = mw.baseHTML() + web_content.head + csstxt + jstxt
|
||||||
|
|
||||||
body_class = theme_manager.body_class()
|
body_class = theme_manager.body_class()
|
||||||
|
|
||||||
@ -336,20 +403,20 @@ body {{ zoom: {}; background: {}; {} }}
|
|||||||
widgetspec,
|
widgetspec,
|
||||||
head,
|
head,
|
||||||
body_class,
|
body_class,
|
||||||
body,
|
web_content.body,
|
||||||
)
|
)
|
||||||
# print(html)
|
# print(html)
|
||||||
self.setHtml(html)
|
self.setHtml(html)
|
||||||
|
|
||||||
def webBundlePath(self, path):
|
def webBundlePath(self, path: str) -> str:
|
||||||
from aqt import mw
|
from aqt import mw
|
||||||
|
|
||||||
return "http://127.0.0.1:%d/_anki/%s" % (mw.mediaServer.getPort(), path)
|
return "http://127.0.0.1:%d/%s" % (mw.mediaServer.getPort(), path)
|
||||||
|
|
||||||
def bundledScript(self, fname):
|
def bundledScript(self, fname: str) -> str:
|
||||||
return '<script src="%s"></script>' % self.webBundlePath(fname)
|
return '<script src="%s"></script>' % self.webBundlePath(fname)
|
||||||
|
|
||||||
def bundledCSS(self, fname):
|
def bundledCSS(self, fname: str) -> str:
|
||||||
return '<link rel="stylesheet" type="text/css" href="%s">' % self.webBundlePath(
|
return '<link rel="stylesheet" type="text/css" href="%s">' % self.webBundlePath(
|
||||||
fname
|
fname
|
||||||
)
|
)
|
||||||
|
@ -163,6 +163,29 @@ hooks = [
|
|||||||
return handled
|
return handled
|
||||||
""",
|
""",
|
||||||
),
|
),
|
||||||
|
Hook(
|
||||||
|
name="webview_will_set_content",
|
||||||
|
args=["web_content: aqt.webview.WebContent", "context: Optional[Any]",],
|
||||||
|
doc="""Used to modify web content before it is rendered.
|
||||||
|
|
||||||
|
Web_content contains the HTML, JS, and CSS the web view will be
|
||||||
|
populated with.
|
||||||
|
|
||||||
|
Context is the instance that was passed to stdHtml().
|
||||||
|
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, do not modify content
|
||||||
|
return
|
||||||
|
|
||||||
|
web_content.js.append("my_addon.js")
|
||||||
|
web_content.css.append("my_addon.css")
|
||||||
|
web_content.head += "<script>console.log('my_addon')</script>"
|
||||||
|
web_content.body += "<div id='my-addon'></div>"
|
||||||
|
""",
|
||||||
|
),
|
||||||
Hook(
|
Hook(
|
||||||
name="webview_will_show_context_menu",
|
name="webview_will_show_context_menu",
|
||||||
args=["webview: aqt.webview.AnkiWebView", "menu: QMenu"],
|
args=["webview: aqt.webview.AnkiWebView", "menu: QMenu"],
|
||||||
|
Loading…
Reference in New Issue
Block a user