mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -04:00
fade out webview when pending updates; do some reviewer updates immediately
Issues that need fixing: - when the editor saves the note with perform_op(), if it isn't modified, no new undo entry is created, and perform_op then returns the changes made by the previous operation instead - the approach of fetching the last action in a subsequent backend method is unsound, as another queued operation may sneak in first before we have a chance to query the result - it would be better if it were returned in a single atomic action - redrawing the current card while editing is likely to make sound autoplay annoyingly, and it has an unpleasant redraw. We may be better off fading it out instead Side note: the editor cursor moves to the start of the field when the note is updated in another window - it might be nicer to have it move the cursor to the end instead.
This commit is contained in:
parent
0a5be6543e
commit
30c7cf1fdd
9 changed files with 68 additions and 61 deletions
|
@ -1246,7 +1246,6 @@ where id in %s"""
|
|||
|
||||
nids = self.selectedNotes()
|
||||
self.mw.perform_op(lambda: func(nids, tags))
|
||||
self.mw.requireReset(reason=ResetReason.BrowserAddTags, context=self)
|
||||
|
||||
def clearUnusedTags(self) -> None:
|
||||
self.editor.saveNow(self._clearUnusedTags)
|
||||
|
@ -1272,13 +1271,15 @@ where id in %s"""
|
|||
|
||||
def _suspend_selected_cards(self) -> None:
|
||||
want_suspend = not self.current_card_is_suspended()
|
||||
c = self.selectedCards()
|
||||
if want_suspend:
|
||||
self.col.sched.suspend_cards(c)
|
||||
else:
|
||||
self.col.sched.unsuspend_cards(c)
|
||||
self.model.reset()
|
||||
self.mw.requireReset(reason=ResetReason.BrowserSuspend, context=self)
|
||||
|
||||
def op() -> None:
|
||||
if want_suspend:
|
||||
self.col.sched.suspend_cards(cids)
|
||||
else:
|
||||
self.col.sched.unsuspend_cards(cids)
|
||||
|
||||
cids = self.selectedCards()
|
||||
self.mw.perform_op(op)
|
||||
|
||||
# Exporting
|
||||
######################################################################
|
||||
|
|
|
@ -62,7 +62,7 @@ class DeckBrowser:
|
|||
self.bottom = BottomBar(mw, mw.bottomWeb)
|
||||
self.scrollPos = QPoint(0, 0)
|
||||
self._v1_message_dismissed_at = 0
|
||||
self.refresh_needed = False
|
||||
self._refresh_needed = False
|
||||
|
||||
def show(self) -> None:
|
||||
av_player.stop_and_clear_queue()
|
||||
|
@ -74,19 +74,21 @@ class DeckBrowser:
|
|||
|
||||
def refresh(self) -> None:
|
||||
self._renderPage()
|
||||
self.refresh_needed = False
|
||||
self._refresh_needed = False
|
||||
|
||||
def refresh_if_needed(self) -> None:
|
||||
if self.refresh_needed:
|
||||
if self._refresh_needed:
|
||||
self.refresh()
|
||||
|
||||
def op_executed(self, op: OperationInfo, focused: bool) -> None:
|
||||
def op_executed(self, op: OperationInfo, focused: bool) -> bool:
|
||||
if self.mw.col.op_affects_study_queue(op):
|
||||
self.refresh_needed = True
|
||||
self._refresh_needed = True
|
||||
|
||||
if focused:
|
||||
self.refresh_if_needed()
|
||||
|
||||
return self._refresh_needed
|
||||
|
||||
# Event handlers
|
||||
##########################################################################
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ from anki.httpclient import HttpClient
|
|||
from anki.notes import Note
|
||||
from anki.utils import checksum, isLin, isWin, namedtmp
|
||||
from aqt import AnkiQt, colors, gui_hooks
|
||||
from aqt.main import ResetReason
|
||||
from aqt.qt import *
|
||||
from aqt.sound import av_player
|
||||
from aqt.theme import theme_manager
|
||||
|
@ -450,7 +449,6 @@ class Editor:
|
|||
|
||||
if not self.addMode:
|
||||
self._save_current_note()
|
||||
self.mw.requireReset(reason=ResetReason.EditorBridgeCmd, context=self)
|
||||
if type == "blur":
|
||||
self.currentField = None
|
||||
# run any filters
|
||||
|
@ -544,7 +542,8 @@ class Editor:
|
|||
|
||||
def _save_current_note(self) -> None:
|
||||
"Call after note is updated with data from webview."
|
||||
self.mw.col.update_note(self.note)
|
||||
note = self.note
|
||||
self.mw.perform_op(lambda: self.mw.col.update_note(note))
|
||||
|
||||
def fonts(self) -> List[Tuple[str, int, bool]]:
|
||||
return [
|
||||
|
|
|
@ -762,11 +762,16 @@ class AnkiQt(QMainWindow):
|
|||
"Notify current screen of changes."
|
||||
focused = current_top_level_widget() == self
|
||||
if self.state == "review":
|
||||
self.reviewer.op_executed(op, focused)
|
||||
dirty = self.reviewer.op_executed(op, focused)
|
||||
elif self.state == "overview":
|
||||
self.overview.op_executed(op, focused)
|
||||
dirty = self.overview.op_executed(op, focused)
|
||||
elif self.state == "deckBrowser":
|
||||
self.deckBrowser.op_executed(op, focused)
|
||||
dirty = self.deckBrowser.op_executed(op, focused)
|
||||
else:
|
||||
dirty = False
|
||||
|
||||
if not focused and dirty:
|
||||
self.fade_out_webview()
|
||||
|
||||
def on_focus_did_change(
|
||||
self, new_focus: Optional[QWidget], _old: Optional[QWidget]
|
||||
|
@ -780,6 +785,12 @@ class AnkiQt(QMainWindow):
|
|||
elif self.state == "deckBrowser":
|
||||
self.deckBrowser.refresh_if_needed()
|
||||
|
||||
def fade_out_webview(self) -> None:
|
||||
self.web.eval("document.body.style.opacity = 0.3")
|
||||
|
||||
def fade_in_webview(self) -> None:
|
||||
self.web.eval("document.body.style.opacity = 1")
|
||||
|
||||
def reset(self, unused_arg: bool = False) -> None:
|
||||
"""Legacy method of telling UI to refresh after changes made to DB.
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class Overview:
|
|||
self.mw = mw
|
||||
self.web = mw.web
|
||||
self.bottom = BottomBar(mw, mw.bottomWeb)
|
||||
self.refresh_needed = False
|
||||
self._refresh_needed = False
|
||||
|
||||
def show(self) -> None:
|
||||
av_player.stop_and_clear_queue()
|
||||
|
@ -57,19 +57,21 @@ class Overview:
|
|||
self._renderBottom()
|
||||
self.mw.web.setFocus()
|
||||
gui_hooks.overview_did_refresh(self)
|
||||
self.refresh_needed = False
|
||||
self._refresh_needed = False
|
||||
|
||||
def refresh_if_needed(self) -> None:
|
||||
if self.refresh_needed:
|
||||
if self._refresh_needed:
|
||||
self.refresh()
|
||||
|
||||
def op_executed(self, op: OperationInfo, focused: bool) -> None:
|
||||
def op_executed(self, op: OperationInfo, focused: bool) -> bool:
|
||||
if self.mw.col.op_affects_study_queue(op):
|
||||
self.refresh_needed = True
|
||||
self._refresh_needed = True
|
||||
|
||||
if focused:
|
||||
self.refresh_if_needed()
|
||||
|
||||
return self._refresh_needed
|
||||
|
||||
# Handlers
|
||||
############################################################
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import html
|
|||
import json
|
||||
import re
|
||||
import unicodedata as ucd
|
||||
from enum import Enum, auto
|
||||
from typing import Any, Callable, List, Match, Optional, Sequence, Tuple, Union
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
|
@ -15,7 +14,6 @@ from PyQt5.QtCore import Qt
|
|||
from anki import hooks
|
||||
from anki.cards import Card
|
||||
from anki.collection import Config, OperationInfo
|
||||
from anki.types import assert_exhaustive
|
||||
from anki.utils import stripHTML
|
||||
from aqt import AnkiQt, gui_hooks
|
||||
from aqt.profiles import VideoDriver
|
||||
|
@ -40,14 +38,6 @@ class ReviewerBottomBar:
|
|||
self.reviewer = reviewer
|
||||
|
||||
|
||||
class RefreshNeeded(Enum):
|
||||
NO = auto()
|
||||
NOTE_MARK = auto()
|
||||
CARD_FLAG = auto()
|
||||
QUEUE = auto()
|
||||
CARD = auto()
|
||||
|
||||
|
||||
def replay_audio(card: Card, question_side: bool) -> None:
|
||||
if question_side:
|
||||
av_player.play_tags(card.question_av_tags())
|
||||
|
@ -71,7 +61,7 @@ class Reviewer:
|
|||
self._recordedAudio: Optional[str] = None
|
||||
self.typeCorrect: str = None # web init happens before this is set
|
||||
self.state: Optional[str] = None
|
||||
self.refresh_needed = RefreshNeeded.NO
|
||||
self._refresh_needed = False
|
||||
self.bottom = BottomBar(mw, mw.bottomWeb)
|
||||
hooks.card_did_leech.append(self.onLeech)
|
||||
|
||||
|
@ -80,7 +70,7 @@ class Reviewer:
|
|||
self.web.set_bridge_command(self._linkHandler, self)
|
||||
self.bottom.web.set_bridge_command(self._linkHandler, ReviewerBottomBar(self))
|
||||
self._reps: int = None
|
||||
self.refresh_needed = RefreshNeeded.QUEUE
|
||||
self._refresh_needed = True
|
||||
self.refresh_if_needed()
|
||||
|
||||
def lastCard(self) -> Optional[Card]:
|
||||
|
@ -98,41 +88,40 @@ class Reviewer:
|
|||
self.card = None
|
||||
|
||||
def refresh_if_needed(self) -> None:
|
||||
if self.refresh_needed is RefreshNeeded.NO:
|
||||
return
|
||||
elif self.refresh_needed is RefreshNeeded.NOTE_MARK:
|
||||
if self._refresh_needed:
|
||||
self.mw.col.reset()
|
||||
self.nextCard()
|
||||
self._refresh_needed = False
|
||||
self.mw.fade_in_webview()
|
||||
|
||||
def op_executed(self, op: OperationInfo, focused: bool) -> bool:
|
||||
|
||||
if op.kind == OperationInfo.UPDATE_NOTE_TAGS:
|
||||
self.card.load()
|
||||
self._update_mark_icon()
|
||||
elif self.refresh_needed is RefreshNeeded.CARD_FLAG:
|
||||
elif op.kind == OperationInfo.SET_CARD_FLAG:
|
||||
# fixme: v3 mtime check
|
||||
self.card.load()
|
||||
self._update_flag_icon()
|
||||
elif self.refresh_needed is RefreshNeeded.QUEUE:
|
||||
self.mw.col.reset()
|
||||
self.nextCard()
|
||||
elif self.refresh_needed is RefreshNeeded.CARD:
|
||||
self.card.load()
|
||||
self._showQuestion()
|
||||
else:
|
||||
assert_exhaustive(self.refresh_needed)
|
||||
|
||||
self.refresh_needed = RefreshNeeded.NO
|
||||
|
||||
def op_executed(self, op: OperationInfo, focused: bool) -> None:
|
||||
if op.kind == OperationInfo.UPDATE_NOTE_TAGS:
|
||||
self.refresh_needed = RefreshNeeded.NOTE_MARK
|
||||
elif op.kind == OperationInfo.SET_CARD_FLAG:
|
||||
self.refresh_needed = RefreshNeeded.CARD_FLAG
|
||||
elif op.kind == OperationInfo.UPDATE_NOTE:
|
||||
self._redraw_current_card()
|
||||
elif self.mw.col.op_affects_study_queue(op):
|
||||
self.refresh_needed = RefreshNeeded.QUEUE
|
||||
self._refresh_needed = True
|
||||
elif op.changes.note or op.changes.notetype or op.changes.tag:
|
||||
self.refresh_needed = RefreshNeeded.CARD
|
||||
else:
|
||||
self.refresh_needed = RefreshNeeded.NO
|
||||
self._redraw_current_card()
|
||||
|
||||
if focused:
|
||||
if focused and self._refresh_needed:
|
||||
self.refresh_if_needed()
|
||||
|
||||
return self._refresh_needed
|
||||
|
||||
def _redraw_current_card(self) -> None:
|
||||
self.card.load()
|
||||
if self.state == "answer":
|
||||
self._showAnswer()
|
||||
else:
|
||||
self._showQuestion()
|
||||
|
||||
# Fetching a card
|
||||
##########################################################################
|
||||
|
||||
|
|
|
@ -1457,6 +1457,7 @@ message OperationInfo {
|
|||
OTHER = 0;
|
||||
UPDATE_NOTE_TAGS = 1;
|
||||
SET_CARD_FLAG = 2;
|
||||
UPDATE_NOTE = 3;
|
||||
}
|
||||
|
||||
Kind kind = 1;
|
||||
|
|
|
@ -23,6 +23,7 @@ impl From<Op> for Kind {
|
|||
match o {
|
||||
Op::SetFlag => Kind::SetCardFlag,
|
||||
Op::UpdateTag => Kind::UpdateNoteTags,
|
||||
Op::UpdateNote => Kind::UpdateNote,
|
||||
_ => Kind::Other,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ body {
|
|||
color: var(--text-fg);
|
||||
background: var(--window-bg);
|
||||
margin: 1em;
|
||||
transition: opacity 0.5s ease-out;
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
Loading…
Reference in a new issue