mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Rework & unify webview identification and title setting (#2366)
* Create common web view registry and unify title setting * Consistently use space-separated naming for webview titles None of the modified titles seem to be in use by add-ons, so we are not bound to the current naming. The old naming was likely following camelCase as the name was also acting as a key for saveGeom, which is no longer the case. * Update webview_did_inject_style_into_page example * Add docstring to addon-targeted method * Change AnkiWebView.origin to property * Fix dupe enum value * Tweak method name * Add semicolon * Rename `AnkiWebViewOrigin` to `AnkiWebViewKind`
This commit is contained in:
parent
f616bea580
commit
0f86c9fd11
15 changed files with 84 additions and 37 deletions
|
@ -19,7 +19,7 @@ from aqt.utils import (
|
|||
setWindowIcon,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
|
||||
class CardInfoDialog(QDialog):
|
||||
|
@ -52,7 +52,7 @@ class CardInfoDialog(QDialog):
|
|||
addCloseShortcut(self)
|
||||
setWindowIcon(self)
|
||||
|
||||
self.web = AnkiWebView(title=self.TITLE)
|
||||
self.web = AnkiWebView(kind=AnkiWebViewKind.BROWSER_CARD_INFO)
|
||||
self.web.setVisible(False)
|
||||
self.web.load_ts_page("card-info")
|
||||
layout = QVBoxLayout()
|
||||
|
|
|
@ -13,6 +13,7 @@ import aqt.forms
|
|||
from anki.collection import SearchNode
|
||||
from anki.notes import NoteId
|
||||
from aqt.qt import *
|
||||
from aqt.webview import AnkiWebViewKind
|
||||
|
||||
from ..operations import QueryOp
|
||||
from ..operations.tag import add_tags_to_notes
|
||||
|
@ -50,7 +51,7 @@ class FindDuplicatesDialog(QDialog):
|
|||
self._dupes: list[tuple[str, list[NoteId]]] = []
|
||||
|
||||
# links
|
||||
form.webView.set_title("find duplicates")
|
||||
form.webView.set_kind(AnkiWebViewKind.FIND_DUPLICATES)
|
||||
form.webView.set_bridge_command(self._on_duplicate_clicked, context=self)
|
||||
form.webView.stdHtml("", context=self)
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ from aqt.reviewer import replay_audio
|
|||
from aqt.sound import av_player, play_clicked_audio
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.utils import disable_help_button, restoreGeom, saveGeom, setWindowIcon, tr
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
LastStateAndMod = tuple[str, int, int]
|
||||
|
||||
|
@ -78,7 +78,7 @@ class Previewer(QDialog):
|
|||
self.silentlyClose = True
|
||||
self.vbox = QVBoxLayout()
|
||||
self.vbox.setContentsMargins(0, 0, 0, 0)
|
||||
self._web = AnkiWebView(title="previewer")
|
||||
self._web = AnkiWebView(kind=AnkiWebViewKind.PREVIEWER)
|
||||
self.vbox.addWidget(self._web)
|
||||
self.bbox = QDialogButtonBox()
|
||||
self.bbox.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
|
||||
|
|
|
@ -23,7 +23,7 @@ from aqt.utils import (
|
|||
tooltip,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
|
||||
class ChangeNotetypeDialog(QDialog):
|
||||
|
@ -52,7 +52,7 @@ class ChangeNotetypeDialog(QDialog):
|
|||
restoreGeom(self, self.TITLE, default_size=(800, 800))
|
||||
addCloseShortcut(self)
|
||||
|
||||
self.web = AnkiWebView(title=self.TITLE)
|
||||
self.web = AnkiWebView(kind=AnkiWebViewKind.CHANGE_NOTETYPE)
|
||||
self.web.setVisible(False)
|
||||
self.web.load_ts_page("change-notetype")
|
||||
layout = QVBoxLayout()
|
||||
|
|
|
@ -35,7 +35,7 @@ from aqt.utils import (
|
|||
tooltip,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
|
||||
class CardLayout(QDialog):
|
||||
|
@ -334,7 +334,7 @@ class CardLayout(QDialog):
|
|||
|
||||
def setup_preview(self) -> None:
|
||||
pform = self.pform
|
||||
self.preview_web = AnkiWebView(title="card layout")
|
||||
self.preview_web = AnkiWebView(kind=AnkiWebViewKind.CARD_LAYOUT)
|
||||
pform.verticalLayout.addWidget(self.preview_web)
|
||||
pform.verticalLayout.setStretch(1, 99)
|
||||
pform.preview_front.isChecked()
|
||||
|
|
|
@ -19,7 +19,7 @@ from aqt.utils import (
|
|||
saveGeom,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
|
||||
class DeckOptionsDialog(QDialog):
|
||||
|
@ -42,7 +42,7 @@ class DeckOptionsDialog(QDialog):
|
|||
restoreGeom(self, self.TITLE, default_size=(800, 800))
|
||||
addCloseShortcut(self)
|
||||
|
||||
self.web = AnkiWebView(title=self.TITLE)
|
||||
self.web = AnkiWebView(kind=AnkiWebViewKind.DECK_OPTIONS)
|
||||
self.web.load_ts_page("deck-options")
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
|
|
@ -55,7 +55,7 @@ from aqt.utils import (
|
|||
tooltip,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
pics = ("jpg", "jpeg", "png", "tif", "tiff", "gif", "svg", "webp", "ico")
|
||||
audio = (
|
||||
|
@ -1211,7 +1211,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
|
|||
|
||||
class EditorWebView(AnkiWebView):
|
||||
def __init__(self, parent: QWidget, editor: Editor) -> None:
|
||||
AnkiWebView.__init__(self, title="editor")
|
||||
AnkiWebView.__init__(self, kind=AnkiWebViewKind.EDITOR)
|
||||
self.editor = editor
|
||||
self.setAcceptDrops(True)
|
||||
self._markInternal = False
|
||||
|
|
|
@ -15,6 +15,7 @@ from anki.collection import EmptyCardsReport
|
|||
from aqt import gui_hooks
|
||||
from aqt.qt import QDialog, QDialogButtonBox, qconnect
|
||||
from aqt.utils import disable_help_button, restoreGeom, saveGeom, tooltip, tr
|
||||
from aqt.webview import AnkiWebViewKind
|
||||
|
||||
|
||||
def show_empty_cards(mw: aqt.main.AnkiQt) -> None:
|
||||
|
@ -46,7 +47,7 @@ class EmptyCardsDialog(QDialog):
|
|||
self.setWindowTitle(tr.empty_cards_window_title())
|
||||
disable_help_button(self)
|
||||
self.form.keep_notes.setText(tr.empty_cards_preserve_notes_checkbox())
|
||||
self.form.webview.set_title("empty cards")
|
||||
self.form.webview.set_kind(AnkiWebViewKind.EMPTY_CARDS)
|
||||
self.form.webview.set_bridge_command(self._on_note_link_clicked, self)
|
||||
|
||||
gui_hooks.empty_cards_will_show(self)
|
||||
|
|
|
@ -27,6 +27,7 @@ from aqt.utils import (
|
|||
tooltip,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebViewKind
|
||||
|
||||
|
||||
class FieldDialog(QDialog):
|
||||
|
@ -55,7 +56,7 @@ class FieldDialog(QDialog):
|
|||
form.setupUi(self)
|
||||
|
||||
self.webview = form.webview
|
||||
self.webview.set_title("fields")
|
||||
self.webview.set_kind(AnkiWebViewKind.FIELDS)
|
||||
|
||||
self.show()
|
||||
self.refresh()
|
||||
|
|
|
@ -10,7 +10,7 @@ import aqt.operations
|
|||
from anki.collection import ImportCsvRequest
|
||||
from aqt.qt import *
|
||||
from aqt.utils import addCloseShortcut, disable_help_button, restoreGeom, saveGeom, tr
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
|
||||
class ImportCsvDialog(QDialog):
|
||||
|
@ -38,7 +38,7 @@ class ImportCsvDialog(QDialog):
|
|||
restoreGeom(self, self.TITLE, default_size=(800, 800))
|
||||
addCloseShortcut(self)
|
||||
|
||||
self.web = AnkiWebView(title=self.TITLE)
|
||||
self.web = AnkiWebView(kind=AnkiWebViewKind.IMPORT_CSV)
|
||||
self.web.setVisible(False)
|
||||
self.web.load_ts_page("import-csv")
|
||||
layout = QVBoxLayout()
|
||||
|
|
|
@ -91,7 +91,7 @@ from aqt.utils import (
|
|||
tooltip,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
install_pylib_legacy()
|
||||
|
||||
|
@ -105,7 +105,7 @@ T = TypeVar("T")
|
|||
|
||||
class MainWebView(AnkiWebView):
|
||||
def __init__(self, mw: AnkiQt) -> None:
|
||||
AnkiWebView.__init__(self, title="main webview")
|
||||
AnkiWebView.__init__(self, kind=AnkiWebViewKind.MAIN)
|
||||
self.mw = mw
|
||||
self.setFocusPolicy(Qt.FocusPolicy.WheelFocus)
|
||||
self.setMinimumWidth(400)
|
||||
|
@ -879,12 +879,12 @@ title="{}" {}>{}</button>""".format(
|
|||
self.form = aqt.forms.main.Ui_MainWindow()
|
||||
self.form.setupUi(self)
|
||||
# toolbar
|
||||
tweb = self.toolbarWeb = TopWebView(self, title="top toolbar")
|
||||
tweb = self.toolbarWeb = TopWebView(self)
|
||||
self.toolbar = Toolbar(self, tweb)
|
||||
# main area
|
||||
self.web = MainWebView(self)
|
||||
# bottom area
|
||||
sweb = self.bottomWeb = BottomWebView(self, title="bottom toolbar")
|
||||
sweb = self.bottomWeb = BottomWebView(self)
|
||||
sweb.setFocusPolicy(Qt.FocusPolicy.WheelFocus)
|
||||
sweb.disable_zoom()
|
||||
# add in a layout
|
||||
|
|
|
@ -21,6 +21,7 @@ from aqt.utils import (
|
|||
tooltip,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebViewKind
|
||||
|
||||
|
||||
class NewDeckStats(QDialog):
|
||||
|
@ -50,6 +51,7 @@ class NewDeckStats(QDialog):
|
|||
maybeHideClose(self.form.buttonBox)
|
||||
addCloseShortcut(self)
|
||||
gui_hooks.stats_dialog_will_show(self)
|
||||
self.form.web.set_kind(AnkiWebViewKind.DECK_STATS)
|
||||
self.form.web.hide_while_preserving_layout()
|
||||
self.show()
|
||||
self.refresh()
|
||||
|
@ -198,7 +200,7 @@ class DeckStats(QDialog):
|
|||
stats = self.mw.col.stats()
|
||||
stats.wholeCollection = self.wholeCollection
|
||||
self.report = stats.report(type=self.period)
|
||||
self.form.web.set_title("deck stats")
|
||||
self.form.web.set_kind(AnkiWebViewKind.LEGACY_DECK_STATS)
|
||||
self.form.web.stdHtml(
|
||||
f"<html><body>{self.report}</body></html>",
|
||||
js=["js/vendor/jquery.min.js", "js/vendor/plot.js"],
|
||||
|
|
|
@ -13,7 +13,7 @@ from aqt.qt import *
|
|||
from aqt.sync import get_sync_status
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.utils import tr
|
||||
from aqt.webview import AnkiWebView
|
||||
from aqt.webview import AnkiWebView, AnkiWebViewKind
|
||||
|
||||
|
||||
class HideMode(enum.IntEnum):
|
||||
|
@ -36,8 +36,8 @@ class BottomToolbar:
|
|||
class ToolbarWebView(AnkiWebView):
|
||||
hide_condition: Callable[..., bool]
|
||||
|
||||
def __init__(self, mw: aqt.AnkiQt, title: str) -> None:
|
||||
AnkiWebView.__init__(self, mw, title=title)
|
||||
def __init__(self, mw: aqt.AnkiQt, kind: AnkiWebViewKind | None = None) -> None:
|
||||
AnkiWebView.__init__(self, mw, kind=kind)
|
||||
self.mw = mw
|
||||
self.setFocusPolicy(Qt.FocusPolicy.WheelFocus)
|
||||
self.disable_zoom()
|
||||
|
@ -58,8 +58,8 @@ class ToolbarWebView(AnkiWebView):
|
|||
|
||||
|
||||
class TopWebView(ToolbarWebView):
|
||||
def __init__(self, mw: aqt.AnkiQt, title: str) -> None:
|
||||
super().__init__(mw, title=title)
|
||||
def __init__(self, mw: aqt.AnkiQt) -> None:
|
||||
super().__init__(mw, kind=AnkiWebViewKind.TOP_TOOLBAR)
|
||||
self.web_height = 0
|
||||
qconnect(self.hide_timer.timeout, self.hide_if_allowed)
|
||||
|
||||
|
@ -179,8 +179,8 @@ class TopWebView(ToolbarWebView):
|
|||
|
||||
|
||||
class BottomWebView(ToolbarWebView):
|
||||
def __init__(self, mw: aqt.AnkiQt, title: str) -> None:
|
||||
super().__init__(mw, title=title)
|
||||
def __init__(self, mw: aqt.AnkiQt) -> None:
|
||||
super().__init__(mw, kind=AnkiWebViewKind.BOTTOM_TOOLBAR)
|
||||
qconnect(self.hide_timer.timeout, self.hide_if_allowed)
|
||||
|
||||
def eventFilter(self, obj, evt):
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
# 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 dataclasses
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from enum import Enum
|
||||
from typing import Any, Callable, Optional, Sequence, cast
|
||||
|
||||
import anki
|
||||
|
@ -222,16 +225,46 @@ class WebContent:
|
|||
##########################################################################
|
||||
|
||||
|
||||
class AnkiWebViewKind(Enum):
|
||||
"""Enum registry of all web views managed by Anki
|
||||
|
||||
The value of each entry corresponds to the web view's title.
|
||||
|
||||
When introducing a new web view, please add it to the registry below.
|
||||
"""
|
||||
|
||||
MAIN = "main webview"
|
||||
TOP_TOOLBAR = "top toolbar"
|
||||
BOTTOM_TOOLBAR = "bottom toolbar"
|
||||
DECK_OPTIONS = "deck options"
|
||||
EDITOR = "editor"
|
||||
LEGACY_DECK_STATS = "legacy deck stats"
|
||||
DECK_STATS = "deck stats"
|
||||
PREVIEWER = "previewer"
|
||||
CHANGE_NOTETYPE = "change notetype"
|
||||
CARD_LAYOUT = "card layout"
|
||||
BROWSER_CARD_INFO = "browser card info"
|
||||
IMPORT_CSV = "csv import"
|
||||
EMPTY_CARDS = "empty cards"
|
||||
FIND_DUPLICATES = "find duplicates"
|
||||
FIELDS = "fields"
|
||||
|
||||
|
||||
class AnkiWebView(QWebEngineView):
|
||||
allow_drops = False
|
||||
_kind: AnkiWebViewKind | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: Optional[QWidget] = None,
|
||||
parent: QWidget | None = None,
|
||||
title: str = "default",
|
||||
kind: AnkiWebViewKind | None = None,
|
||||
) -> None:
|
||||
QWebEngineView.__init__(self, parent=parent)
|
||||
self.set_title(title)
|
||||
if kind:
|
||||
self.set_kind(kind)
|
||||
else:
|
||||
self.set_title(title)
|
||||
self._page = AnkiWebPage(self._onBridgeCmd)
|
||||
# reduce flicker
|
||||
self._page.setBackgroundColor(theme_manager.qcolor(colors.CANVAS))
|
||||
|
@ -263,6 +296,15 @@ class AnkiWebView(QWebEngineView):
|
|||
"""
|
||||
)
|
||||
|
||||
def set_kind(self, kind: AnkiWebViewKind) -> None:
|
||||
self._kind = kind
|
||||
self.set_title(kind.value)
|
||||
|
||||
@property
|
||||
def kind(self) -> AnkiWebViewKind | None:
|
||||
"""Used by add-ons to identify the webview kind"""
|
||||
return self._kind
|
||||
|
||||
def set_title(self, title: str) -> None:
|
||||
self.title = title # type: ignore[assignment]
|
||||
|
||||
|
@ -654,8 +696,8 @@ html {{ {font} }}
|
|||
self.setSizePolicy(sp)
|
||||
self.hide()
|
||||
|
||||
def add_dynamic_css_and_classes_then_show(self) -> None:
|
||||
"Add dynamic styling, set platform-specific body classes and reveal."
|
||||
def add_dynamic_styling_and_props_then_show(self) -> None:
|
||||
"Add dynamic styling, title, set platform-specific body classes and reveal."
|
||||
css = self.standard_css()
|
||||
body_classes = theme_manager.body_class().split(" ")
|
||||
|
||||
|
@ -670,6 +712,7 @@ html {{ {font} }}
|
|||
self.evalWithCallback(
|
||||
f"""
|
||||
(function(){{
|
||||
document.title = `{self.title}`;
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `{css}`;
|
||||
document.head.appendChild(style);
|
||||
|
@ -689,7 +732,7 @@ html {{ {font} }}
|
|||
else:
|
||||
extra = ""
|
||||
self.load_url(QUrl(f"{mw.serverURL()}_anki/pages/{name}.html{extra}"))
|
||||
self.add_dynamic_css_and_classes_then_show()
|
||||
self.add_dynamic_styling_and_props_then_show()
|
||||
|
||||
def force_load_hack(self) -> None:
|
||||
"""Force process to initialize.
|
||||
|
|
|
@ -705,9 +705,8 @@ mutate the DOM before the page is revealed.
|
|||
|
||||
For example:
|
||||
|
||||
def mytest(web: AnkiWebView):
|
||||
page = os.path.basename(web.page().url().path())
|
||||
if page != "graphs.html":
|
||||
def mytest(webview: AnkiWebView):
|
||||
if webview.kind != AnkiWebViewKind.DECK_STATS:
|
||||
return
|
||||
web.eval(
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue