2020-04-03 01:05:32 +02:00
|
|
|
# Copyright: Ankitects Pty Ltd and contributors
|
|
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
2021-03-17 05:51:59 +01:00
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
2020-03-29 21:10:30 +02:00
|
|
|
import json
|
|
|
|
import re
|
|
|
|
import time
|
2021-10-03 10:59:42 +02:00
|
|
|
from typing import Any, Callable
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-03-17 05:51:59 +01:00
|
|
|
import aqt.browser
|
2020-03-30 15:40:19 +02:00
|
|
|
from anki.cards import Card
|
2021-02-08 05:10:05 +01:00
|
|
|
from anki.collection import Config
|
2021-07-26 07:28:38 +02:00
|
|
|
from anki.tags import MARKED_TAG
|
2020-03-29 21:10:30 +02:00
|
|
|
from aqt import AnkiQt, gui_hooks
|
|
|
|
from aqt.qt import (
|
|
|
|
QCheckBox,
|
|
|
|
QDialog,
|
|
|
|
QDialogButtonBox,
|
2020-05-20 05:26:47 +02:00
|
|
|
QIcon,
|
2020-03-29 21:10:30 +02:00
|
|
|
QKeySequence,
|
2020-05-20 05:26:47 +02:00
|
|
|
QPixmap,
|
2021-01-10 01:34:59 +01:00
|
|
|
QShortcut,
|
2020-03-29 21:10:30 +02:00
|
|
|
Qt,
|
2021-02-01 13:08:56 +01:00
|
|
|
QTimer,
|
2020-03-29 21:10:30 +02:00
|
|
|
QVBoxLayout,
|
|
|
|
QWidget,
|
2020-04-03 01:13:33 +02:00
|
|
|
qconnect,
|
2020-03-29 21:10:30 +02:00
|
|
|
)
|
2020-04-13 00:59:36 +02:00
|
|
|
from aqt.reviewer import replay_audio
|
2020-03-29 21:10:30 +02:00
|
|
|
from aqt.sound import av_player, play_clicked_audio
|
|
|
|
from aqt.theme import theme_manager
|
2021-03-26 05:21:04 +01:00
|
|
|
from aqt.utils import disable_help_button, restoreGeom, saveGeom, tr
|
2020-03-29 21:10:30 +02:00
|
|
|
from aqt.webview import AnkiWebView
|
|
|
|
|
2021-10-03 10:59:42 +02:00
|
|
|
LastStateAndMod = tuple[str, int, int]
|
2021-02-01 13:08:56 +01:00
|
|
|
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2020-04-02 10:02:54 +02:00
|
|
|
class Previewer(QDialog):
|
2021-10-03 10:59:42 +02:00
|
|
|
_last_state: LastStateAndMod | None = None
|
2020-04-02 17:34:53 +02:00
|
|
|
_card_changed = False
|
2021-10-03 10:59:42 +02:00
|
|
|
_last_render: int | float = 0
|
|
|
|
_timer: QTimer | None = None
|
2020-04-03 00:27:26 +02:00
|
|
|
_show_both_sides = False
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-02-01 14:28:21 +01:00
|
|
|
def __init__(
|
|
|
|
self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None]
|
|
|
|
) -> None:
|
2021-10-05 05:53:01 +02:00
|
|
|
super().__init__(None, Qt.WindowType.Window)
|
2021-09-22 10:21:08 +02:00
|
|
|
mw.garbage_collect_on_dialog_finish(self)
|
2020-04-02 10:02:54 +02:00
|
|
|
self._open = True
|
2020-04-02 10:31:29 +02:00
|
|
|
self._parent = parent
|
2020-04-08 08:19:59 +02:00
|
|
|
self._close_callback = on_close
|
2020-03-29 21:10:30 +02:00
|
|
|
self.mw = mw
|
2020-05-20 05:26:47 +02:00
|
|
|
icon = QIcon()
|
2021-10-05 05:53:01 +02:00
|
|
|
icon.addPixmap(QPixmap("icons:anki.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
2021-01-07 05:24:49 +01:00
|
|
|
disable_help_button(self)
|
2020-05-20 00:42:35 +02:00
|
|
|
self.setWindowIcon(icon)
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-10-03 10:59:42 +02:00
|
|
|
def card(self) -> Card | None:
|
2020-03-29 22:32:24 +02:00
|
|
|
raise NotImplementedError
|
2020-03-30 15:40:19 +02:00
|
|
|
|
2020-07-24 03:57:22 +02:00
|
|
|
def card_changed(self) -> bool:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def open(self) -> None:
|
2020-04-02 09:48:25 +02:00
|
|
|
self._state = "question"
|
2020-04-02 17:34:53 +02:00
|
|
|
self._last_state = None
|
2020-03-29 22:27:55 +02:00
|
|
|
self._create_gui()
|
2020-04-02 17:34:53 +02:00
|
|
|
self._setup_web_view()
|
2020-07-24 03:57:22 +02:00
|
|
|
self.render_card()
|
2020-04-02 10:02:54 +02:00
|
|
|
self.show()
|
2020-03-29 22:27:55 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _create_gui(self) -> None:
|
2021-03-26 04:48:26 +01:00
|
|
|
self.setWindowTitle(tr.actions_preview())
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-01-10 01:34:59 +01:00
|
|
|
self.close_shortcut = QShortcut(QKeySequence("Ctrl+Shift+P"), self)
|
|
|
|
qconnect(self.close_shortcut.activated, self.close)
|
|
|
|
|
2020-04-03 01:13:33 +02:00
|
|
|
qconnect(self.finished, self._on_finished)
|
2020-04-02 10:02:54 +02:00
|
|
|
self.silentlyClose = True
|
2020-03-29 22:28:47 +02:00
|
|
|
self.vbox = QVBoxLayout()
|
|
|
|
self.vbox.setContentsMargins(0, 0, 0, 0)
|
2020-04-02 09:48:25 +02:00
|
|
|
self._web = AnkiWebView(title="previewer")
|
|
|
|
self.vbox.addWidget(self._web)
|
2020-03-29 22:28:47 +02:00
|
|
|
self.bbox = QDialogButtonBox()
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2020-04-02 09:48:25 +02:00
|
|
|
self._replay = self.bbox.addButton(
|
2021-10-05 05:53:01 +02:00
|
|
|
tr.actions_replay_audio(), QDialogButtonBox.ButtonRole.ActionRole
|
2020-03-29 21:10:30 +02:00
|
|
|
)
|
2020-04-02 09:48:25 +02:00
|
|
|
self._replay.setAutoDefault(False)
|
|
|
|
self._replay.setShortcut(QKeySequence("R"))
|
2021-03-26 05:21:04 +01:00
|
|
|
self._replay.setToolTip(tr.actions_shortcut_key(val="R"))
|
2020-04-03 01:13:33 +02:00
|
|
|
qconnect(self._replay.clicked, self._on_replay_audio)
|
2020-04-02 09:48:25 +02:00
|
|
|
|
2021-03-26 04:48:26 +01:00
|
|
|
both_sides_button = QCheckBox(tr.qt_misc_back_side_only())
|
2020-04-03 00:27:26 +02:00
|
|
|
both_sides_button.setShortcut(QKeySequence("B"))
|
2021-03-26 05:21:04 +01:00
|
|
|
both_sides_button.setToolTip(tr.actions_shortcut_key(val="B"))
|
2021-10-05 05:53:01 +02:00
|
|
|
self.bbox.addButton(both_sides_button, QDialogButtonBox.ButtonRole.ActionRole)
|
2021-01-29 12:03:19 +01:00
|
|
|
self._show_both_sides = self.mw.col.get_config_bool(
|
2021-02-08 05:10:05 +01:00
|
|
|
Config.Bool.PREVIEW_BOTH_SIDES
|
2021-01-29 12:03:19 +01:00
|
|
|
)
|
2020-04-03 00:27:26 +02:00
|
|
|
both_sides_button.setChecked(self._show_both_sides)
|
2020-04-03 01:13:33 +02:00
|
|
|
qconnect(both_sides_button.toggled, self._on_show_both_sides)
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2020-03-29 22:28:47 +02:00
|
|
|
self.vbox.addWidget(self.bbox)
|
2020-04-02 10:02:54 +02:00
|
|
|
self.setLayout(self.vbox)
|
|
|
|
restoreGeom(self, "preview")
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _on_finished(self, ok: int) -> None:
|
2020-04-02 10:02:54 +02:00
|
|
|
saveGeom(self, "preview")
|
2020-04-02 17:34:53 +02:00
|
|
|
self.mw.progress.timer(100, self._on_close, False)
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _on_replay_audio(self) -> None:
|
2020-04-13 00:59:36 +02:00
|
|
|
if self._state == "question":
|
|
|
|
replay_audio(self.card(), True)
|
|
|
|
elif self._state == "answer":
|
|
|
|
replay_audio(self.card(), False)
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def close(self) -> None:
|
2020-04-08 08:19:59 +02:00
|
|
|
self._on_close()
|
|
|
|
super().close()
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _on_close(self) -> None:
|
2020-04-02 10:02:54 +02:00
|
|
|
self._open = False
|
2020-05-19 23:46:50 +02:00
|
|
|
self._close_callback()
|
2021-09-22 10:21:08 +02:00
|
|
|
self._web = None
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _setup_web_view(self) -> None:
|
2020-04-03 01:00:08 +02:00
|
|
|
self._web.stdHtml(
|
2020-08-31 05:29:28 +02:00
|
|
|
self.mw.reviewer.revHtml(),
|
2020-11-01 05:26:58 +01:00
|
|
|
css=["css/reviewer.css"],
|
2021-07-16 16:33:12 +02:00
|
|
|
js=[
|
|
|
|
"js/mathjax.js",
|
|
|
|
"js/vendor/mathjax/tex-chtml.js",
|
|
|
|
"js/reviewer.js",
|
|
|
|
],
|
2020-08-31 05:29:28 +02:00
|
|
|
context=self,
|
2020-03-29 21:10:30 +02:00
|
|
|
)
|
2020-04-02 09:48:25 +02:00
|
|
|
self._web.set_bridge_command(self._on_bridge_cmd, self)
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2020-04-02 09:48:25 +02:00
|
|
|
def _on_bridge_cmd(self, cmd: str) -> Any:
|
2020-03-29 21:10:30 +02:00
|
|
|
if cmd.startswith("play:"):
|
2020-03-30 15:40:19 +02:00
|
|
|
play_clicked_audio(cmd, self.card())
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-10-03 10:59:42 +02:00
|
|
|
def _update_flag_and_mark_icons(self, card: Card | None) -> None:
|
2021-07-26 07:28:38 +02:00
|
|
|
if card:
|
|
|
|
flag = card.user_flag()
|
|
|
|
marked = card.note(reload=True).has_tag(MARKED_TAG)
|
|
|
|
else:
|
|
|
|
flag = 0
|
|
|
|
marked = False
|
|
|
|
self._web.eval(f"_drawFlag({flag}); _drawMark({json.dumps(marked)});")
|
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def render_card(self) -> None:
|
2020-04-02 17:34:53 +02:00
|
|
|
self.cancel_timer()
|
2020-04-02 09:35:51 +02:00
|
|
|
# Keep track of whether render() has ever been called
|
2020-03-29 21:10:30 +02:00
|
|
|
# with cardChanged=True since the last successful render
|
2020-07-24 03:57:22 +02:00
|
|
|
self._card_changed |= self.card_changed()
|
2020-03-29 21:10:30 +02:00
|
|
|
# avoid rendering in quick succession
|
2020-04-03 00:27:26 +02:00
|
|
|
elap_ms = int((time.time() - self._last_render) * 1000)
|
2020-03-29 21:10:30 +02:00
|
|
|
delay = 300
|
2020-04-03 00:27:26 +02:00
|
|
|
if elap_ms < delay:
|
2020-04-02 09:48:25 +02:00
|
|
|
self._timer = self.mw.progress.timer(
|
2020-04-03 00:27:26 +02:00
|
|
|
delay - elap_ms, self._render_scheduled, False
|
2020-03-29 21:10:30 +02:00
|
|
|
)
|
|
|
|
else:
|
2020-04-02 17:34:53 +02:00
|
|
|
self._render_scheduled()
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def cancel_timer(self) -> None:
|
2020-04-02 09:48:25 +02:00
|
|
|
if self._timer:
|
|
|
|
self._timer.stop()
|
|
|
|
self._timer = None
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2020-04-02 17:34:53 +02:00
|
|
|
def _render_scheduled(self) -> None:
|
|
|
|
self.cancel_timer()
|
|
|
|
self._last_render = time.time()
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2020-04-02 10:02:54 +02:00
|
|
|
if not self._open:
|
2020-03-29 21:10:30 +02:00
|
|
|
return
|
2020-03-30 15:40:19 +02:00
|
|
|
c = self.card()
|
2021-07-26 07:28:38 +02:00
|
|
|
self._update_flag_and_mark_icons(c)
|
2021-07-16 16:57:49 +02:00
|
|
|
func = "_showQuestion"
|
2021-07-01 22:45:19 +02:00
|
|
|
ans_txt = ""
|
2020-03-29 22:32:24 +02:00
|
|
|
if not c:
|
2021-03-26 04:48:26 +01:00
|
|
|
txt = tr.qt_misc_please_select_1_card()
|
2020-03-29 21:10:30 +02:00
|
|
|
bodyclass = ""
|
2020-04-02 17:34:53 +02:00
|
|
|
self._last_state = None
|
2020-03-29 21:10:30 +02:00
|
|
|
else:
|
2020-04-03 00:27:26 +02:00
|
|
|
if self._show_both_sides:
|
2020-04-02 09:48:25 +02:00
|
|
|
self._state = "answer"
|
2020-04-02 17:34:53 +02:00
|
|
|
elif self._card_changed:
|
2020-04-02 09:48:25 +02:00
|
|
|
self._state = "question"
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2020-04-02 17:34:53 +02:00
|
|
|
currentState = self._state_and_mod()
|
|
|
|
if currentState == self._last_state:
|
2020-03-29 21:10:30 +02:00
|
|
|
# nothing has changed, avoid refreshing
|
|
|
|
return
|
|
|
|
|
|
|
|
# need to force reload even if answer
|
2021-06-27 04:12:23 +02:00
|
|
|
txt = c.question(reload=True)
|
2021-07-01 22:45:19 +02:00
|
|
|
ans_txt = c.answer()
|
2021-07-16 16:57:49 +02:00
|
|
|
|
2020-04-02 09:48:25 +02:00
|
|
|
if self._state == "answer":
|
2021-07-16 16:57:49 +02:00
|
|
|
func = "_showAnswer"
|
2021-07-01 22:45:19 +02:00
|
|
|
txt = ans_txt
|
2020-03-29 21:10:30 +02:00
|
|
|
txt = re.sub(r"\[\[type:[^]]+\]\]", "", txt)
|
|
|
|
|
|
|
|
bodyclass = theme_manager.body_classes_for_card_ord(c.ord)
|
|
|
|
|
2020-04-13 01:04:30 +02:00
|
|
|
if c.autoplay():
|
2020-04-03 00:27:26 +02:00
|
|
|
if self._show_both_sides:
|
2020-03-29 21:10:30 +02:00
|
|
|
# if we're showing both sides at once, remove any audio
|
|
|
|
# from the answer that's appeared on the question already
|
|
|
|
question_audio = c.question_av_tags()
|
|
|
|
only_on_answer_audio = [
|
|
|
|
x for x in c.answer_av_tags() if x not in question_audio
|
|
|
|
]
|
|
|
|
audio = question_audio + only_on_answer_audio
|
2020-04-02 09:48:25 +02:00
|
|
|
elif self._state == "question":
|
2020-03-29 21:10:30 +02:00
|
|
|
audio = c.question_av_tags()
|
|
|
|
else:
|
|
|
|
audio = c.answer_av_tags()
|
|
|
|
av_player.play_tags(audio)
|
|
|
|
else:
|
|
|
|
av_player.clear_queue_and_maybe_interrupt()
|
|
|
|
|
|
|
|
txt = self.mw.prepare_card_text_for_display(txt)
|
2021-02-11 01:09:06 +01:00
|
|
|
txt = gui_hooks.card_will_show(txt, c, f"preview{self._state.capitalize()}")
|
2020-04-02 17:34:53 +02:00
|
|
|
self._last_state = self._state_and_mod()
|
2021-07-01 22:45:19 +02:00
|
|
|
|
2021-07-16 16:57:49 +02:00
|
|
|
js: str
|
2021-07-01 22:45:19 +02:00
|
|
|
if self._state == "question":
|
2021-07-16 16:57:49 +02:00
|
|
|
ans_txt = self.mw.col.media.escape_media_filenames(ans_txt)
|
|
|
|
js = f"{func}({json.dumps(txt)}, {json.dumps(ans_txt)}, '{bodyclass}');"
|
2021-07-01 22:45:19 +02:00
|
|
|
else:
|
2021-07-16 16:57:49 +02:00
|
|
|
js = f"{func}({json.dumps(txt)}, '{bodyclass}');"
|
2021-07-01 22:45:19 +02:00
|
|
|
self._web.eval(js)
|
2020-04-02 17:34:53 +02:00
|
|
|
self._card_changed = False
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _on_show_both_sides(self, toggle: bool) -> None:
|
2020-04-03 00:27:26 +02:00
|
|
|
self._show_both_sides = toggle
|
2021-02-08 05:10:05 +01:00
|
|
|
self.mw.col.set_config_bool(Config.Bool.PREVIEW_BOTH_SIDES, toggle)
|
2020-04-02 09:48:25 +02:00
|
|
|
if self._state == "answer" and not toggle:
|
|
|
|
self._state = "question"
|
2020-04-03 00:29:35 +02:00
|
|
|
self.render_card()
|
2020-03-29 21:10:30 +02:00
|
|
|
|
2021-10-03 10:59:42 +02:00
|
|
|
def _state_and_mod(self) -> tuple[str, int, int]:
|
2020-03-30 15:40:19 +02:00
|
|
|
c = self.card()
|
2020-03-29 21:10:30 +02:00
|
|
|
n = c.note()
|
|
|
|
n.load()
|
2020-04-02 09:48:25 +02:00
|
|
|
return (self._state, c.id, n.mod)
|
2020-03-29 22:32:24 +02:00
|
|
|
|
2020-04-03 01:00:08 +02:00
|
|
|
def state(self) -> str:
|
|
|
|
return self._state
|
|
|
|
|
2020-03-29 22:32:24 +02:00
|
|
|
|
2020-04-03 00:27:26 +02:00
|
|
|
class MultiCardPreviewer(Previewer):
|
2021-10-03 10:59:42 +02:00
|
|
|
def card(self) -> Card | None:
|
2020-03-30 10:13:38 +02:00
|
|
|
# need to state explicitly it's not implement to avoid W0223
|
|
|
|
raise NotImplementedError
|
2020-03-29 22:32:24 +02:00
|
|
|
|
2020-07-24 04:16:13 +02:00
|
|
|
def card_changed(self) -> bool:
|
|
|
|
# need to state explicitly it's not implement to avoid W0223
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _create_gui(self) -> None:
|
2020-03-29 22:32:24 +02:00
|
|
|
super()._create_gui()
|
2021-10-05 05:53:01 +02:00
|
|
|
self._prev = self.bbox.addButton("<", QDialogButtonBox.ButtonRole.ActionRole)
|
2020-04-02 09:48:25 +02:00
|
|
|
self._prev.setAutoDefault(False)
|
|
|
|
self._prev.setShortcut(QKeySequence("Left"))
|
2021-03-26 04:48:26 +01:00
|
|
|
self._prev.setToolTip(tr.qt_misc_shortcut_key_left_arrow())
|
2020-04-02 09:48:25 +02:00
|
|
|
|
2021-10-05 05:53:01 +02:00
|
|
|
self._next = self.bbox.addButton(">", QDialogButtonBox.ButtonRole.ActionRole)
|
2020-04-02 09:48:25 +02:00
|
|
|
self._next.setAutoDefault(True)
|
|
|
|
self._next.setShortcut(QKeySequence("Right"))
|
2021-03-26 04:48:26 +01:00
|
|
|
self._next.setToolTip(tr.qt_misc_shortcut_key_right_arrow_or_enter())
|
2020-04-02 09:48:25 +02:00
|
|
|
|
2020-04-03 01:13:33 +02:00
|
|
|
qconnect(self._prev.clicked, self._on_prev)
|
|
|
|
qconnect(self._next.clicked, self._on_next)
|
2020-04-02 09:48:25 +02:00
|
|
|
|
2021-02-01 14:28:21 +01:00
|
|
|
def _on_prev(self) -> None:
|
2020-04-03 00:27:26 +02:00
|
|
|
if self._state == "answer" and not self._show_both_sides:
|
2020-04-02 09:48:25 +02:00
|
|
|
self._state = "question"
|
2020-04-03 00:29:35 +02:00
|
|
|
self.render_card()
|
2020-03-29 22:32:24 +02:00
|
|
|
else:
|
2020-04-02 17:34:53 +02:00
|
|
|
self._on_prev_card()
|
2020-03-30 09:58:51 +02:00
|
|
|
|
2021-02-01 14:28:21 +01:00
|
|
|
def _on_prev_card(self) -> None:
|
2020-04-03 00:27:56 +02:00
|
|
|
pass
|
2020-03-29 22:32:24 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _on_next(self) -> None:
|
2020-04-02 09:48:25 +02:00
|
|
|
if self._state == "question":
|
|
|
|
self._state = "answer"
|
2020-04-03 00:29:35 +02:00
|
|
|
self.render_card()
|
2020-03-29 22:32:24 +02:00
|
|
|
else:
|
2020-04-02 17:34:53 +02:00
|
|
|
self._on_next_card()
|
2020-03-30 09:58:51 +02:00
|
|
|
|
2021-02-01 14:28:21 +01:00
|
|
|
def _on_next_card(self) -> None:
|
2020-04-03 00:27:56 +02:00
|
|
|
pass
|
2020-03-29 22:32:24 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _updateButtons(self) -> None:
|
2020-04-02 10:02:54 +02:00
|
|
|
if not self._open:
|
2020-03-29 22:32:24 +02:00
|
|
|
return
|
2020-04-02 09:48:25 +02:00
|
|
|
self._prev.setEnabled(self._should_enable_prev())
|
|
|
|
self._next.setEnabled(self._should_enable_next())
|
2020-03-30 09:56:46 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _should_enable_prev(self) -> bool:
|
2020-04-03 00:27:26 +02:00
|
|
|
return self._state == "answer" and not self._show_both_sides
|
2020-03-30 10:13:38 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _should_enable_next(self) -> bool:
|
2020-04-02 09:48:25 +02:00
|
|
|
return self._state == "question"
|
2020-03-30 10:13:38 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _on_close(self) -> None:
|
2020-04-02 17:34:53 +02:00
|
|
|
super()._on_close()
|
2020-04-02 09:48:25 +02:00
|
|
|
self._prev = None
|
|
|
|
self._next = None
|
2020-03-30 10:13:38 +02:00
|
|
|
|
|
|
|
|
2020-04-03 00:27:26 +02:00
|
|
|
class BrowserPreviewer(MultiCardPreviewer):
|
2020-07-24 03:57:22 +02:00
|
|
|
_last_card_id = 0
|
2021-10-03 10:59:42 +02:00
|
|
|
_parent: aqt.browser.Browser | None
|
2021-03-17 05:51:59 +01:00
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self, parent: aqt.browser.Browser, mw: AnkiQt, on_close: Callable[[], None]
|
|
|
|
) -> None:
|
|
|
|
super().__init__(parent=parent, mw=mw, on_close=on_close)
|
2020-07-24 03:57:22 +02:00
|
|
|
|
2021-10-03 10:59:42 +02:00
|
|
|
def card(self) -> Card | None:
|
2020-04-02 10:31:29 +02:00
|
|
|
if self._parent.singleCard:
|
|
|
|
return self._parent.card
|
2020-03-30 10:13:38 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2020-07-24 03:57:22 +02:00
|
|
|
def card_changed(self) -> bool:
|
|
|
|
c = self.card()
|
|
|
|
if not c:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
changed = c.id != self._last_card_id
|
|
|
|
self._last_card_id = c.id
|
|
|
|
return changed
|
|
|
|
|
2021-02-01 14:28:21 +01:00
|
|
|
def _on_prev_card(self) -> None:
|
2021-03-29 08:12:26 +02:00
|
|
|
self._parent.onPreviousCard()
|
2020-03-30 10:13:38 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _on_next_card(self) -> None:
|
2021-03-29 08:12:26 +02:00
|
|
|
self._parent.onNextCard()
|
2020-03-30 10:13:38 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _should_enable_prev(self) -> bool:
|
2021-03-29 08:12:26 +02:00
|
|
|
return super()._should_enable_prev() or self._parent.has_previous_card()
|
2020-03-30 09:56:46 +02:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _should_enable_next(self) -> bool:
|
2021-03-29 08:12:26 +02:00
|
|
|
return super()._should_enable_next() or self._parent.has_next_card()
|
2020-03-29 22:32:24 +02:00
|
|
|
|
2020-04-02 17:34:53 +02:00
|
|
|
def _render_scheduled(self) -> None:
|
|
|
|
super()._render_scheduled()
|
2020-04-02 09:48:25 +02:00
|
|
|
self._updateButtons()
|