diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index f365c6a89..97174c2fa 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -133,7 +133,7 @@ class Browser(QMainWindow): self.table.op_executed(changes, meta, focused) self.sidebar.op_executed(changes, meta, focused) if changes.note or changes.notetype: - if meta.handled_by is not self.editor: + if meta.handler is not self.editor: # fixme: this will leave the splitter shown, but with no current # note being edited note = self.editor.note diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index cde0b0f1f..3a3a67a63 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -32,21 +32,17 @@ class EditCurrent(QDialog): self.show() def on_operation_did_execute(self, changes: OpChanges, meta: OpMeta) -> None: - if not (changes.note or changes.notetype): - return - if meta.handled_by is self.editor: - return + if changes.editor and meta.handler is not self.editor: + # reload note + note = self.editor.note + try: + note.load() + except NotFoundError: + # note's been deleted + self.cleanup_and_close() + return - # reload note - note = self.editor.note - try: - note.load() - except NotFoundError: - # note's been deleted - self.cleanup_and_close() - return - - self.editor.set_note(note) + self.editor.set_note(note) def cleanup_and_close(self) -> None: gui_hooks.operation_did_execute.remove(self.on_operation_did_execute) diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index c1cea825e..82f795994 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -100,7 +100,7 @@ class Editor: redrawing. The editor will cause that hook to be fired when it saves changes. To avoid - an unwanted refresh, the parent widget should check if meta.handled_by + an unwanted refresh, the parent widget should check if meta.handler corresponds to this editor instance, and ignore the change if it does. """ @@ -558,7 +558,7 @@ class Editor: def _save_current_note(self) -> None: "Call after note is updated with data from webview." - update_note(mw=self.mw, note=self.note, handled_by=self) + update_note(mw=self.mw, note=self.note, handler=self) def fonts(self) -> List[Tuple[str, int, bool]]: return [ diff --git a/qt/aqt/operations/__init__.py b/qt/aqt/operations/__init__.py index 49e7bdc55..dd3d825c6 100644 --- a/qt/aqt/operations/__init__.py +++ b/qt/aqt/operations/__init__.py @@ -9,8 +9,8 @@ from typing import Optional class OpMeta: """Metadata associated with an operation. - The `handled_by` field can be used by screens to ignore change + The `handler` field can be used by screens to ignore change events they initiated themselves, if they have already made the required changes.""" - handled_by: Optional[object] = None + handler: Optional[object] = None diff --git a/qt/aqt/operations/deck.py b/qt/aqt/operations/deck.py index ff934c908..e32e69b0c 100644 --- a/qt/aqt/operations/deck.py +++ b/qt/aqt/operations/deck.py @@ -77,11 +77,11 @@ def set_deck_collapsed( deck_id: DeckId, collapsed: bool, scope: DeckCollapseScope.V, - handled_by: Optional[object] = None, + handler: Optional[object] = None, ) -> None: mw.perform_op( lambda: mw.col.decks.set_collapsed( deck_id=deck_id, collapsed=collapsed, scope=scope ), - meta=OpMeta(handled_by=handled_by), + meta=OpMeta(handler=handler), ) diff --git a/qt/aqt/operations/note.py b/qt/aqt/operations/note.py index 1e3bb41d0..4c0d8c3d2 100644 --- a/qt/aqt/operations/note.py +++ b/qt/aqt/operations/note.py @@ -22,10 +22,10 @@ def add_note( mw.perform_op(lambda: mw.col.add_note(note, target_deck_id), success=success) -def update_note(*, mw: AnkiQt, note: Note, handled_by: Optional[object]) -> None: +def update_note(*, mw: AnkiQt, note: Note, handler: Optional[object]) -> None: mw.perform_op( lambda: mw.col.update_note(note), - meta=OpMeta(handled_by=handled_by), + meta=OpMeta(handler=handler), ) diff --git a/qt/aqt/sidebar.py b/qt/aqt/sidebar.py index f362474a0..f8d83c354 100644 --- a/qt/aqt/sidebar.py +++ b/qt/aqt/sidebar.py @@ -420,11 +420,8 @@ class SidebarTreeView(QTreeView): # Refreshing ########################### - def op_executed(self, op: OpChanges, meta: OpMeta, focused: bool) -> None: - if meta.handled_by is self: - return - - if op.tag or op.notetype or op.deck: + def op_executed(self, changes: OpChanges, meta: OpMeta, focused: bool) -> None: + if changes.browser_sidebar and not meta.handler is self: self._refresh_needed = True if focused: self.refresh_if_needed() @@ -984,7 +981,7 @@ class SidebarTreeView(QTreeView): deck_id=DeckId(node.deck_id), collapsed=not expanded, scope=DeckCollapseScope.BROWSER, - handled_by=self, + handler=self, ) for node in nodes: diff --git a/qt/aqt/table.py b/qt/aqt/table.py index dd6983b10..3fab969c2 100644 --- a/qt/aqt/table.py +++ b/qt/aqt/table.py @@ -180,9 +180,8 @@ class Table: def redraw_cells(self) -> None: self._model.redraw_cells() - def op_executed(self, op: OpChanges, meta: OpMeta, focused: bool) -> None: - print("op executed") - if op.card or op.note or op.deck or op.notetype: + def op_executed(self, changes: OpChanges, meta: OpMeta, focused: bool) -> None: + if changes.browser_table: self._model.empty_cache() if focused: self.redraw_cells() diff --git a/rslib/backend.proto b/rslib/backend.proto index d6652587f..06fe920e1 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -1513,6 +1513,11 @@ message OpChanges { bool tag = 5; bool notetype = 6; bool preference = 7; + + bool browser_table = 8; + bool browser_sidebar = 9; + bool editor = 10; + bool study_queues = 11; } message UndoStatus { diff --git a/rslib/src/backend/ops.rs b/rslib/src/backend/ops.rs index 6ddad03a6..d634093ab 100644 --- a/rslib/src/backend/ops.rs +++ b/rslib/src/backend/ops.rs @@ -31,6 +31,10 @@ impl From for pb::OpChanges { tag: c.changes.tag, notetype: c.changes.notetype, preference: c.changes.preference, + browser_table: c.requires_browser_table_redraw(), + browser_sidebar: c.requires_browser_sidebar_redraw(), + editor: c.requires_editor_redraw(), + study_queues: c.requires_study_queue_rebuild(), } } } diff --git a/rslib/src/ops.rs b/rslib/src/ops.rs index 6e7503e48..90b568ced 100644 --- a/rslib/src/ops.rs +++ b/rslib/src/ops.rs @@ -105,3 +105,36 @@ impl OpOutput { } } } + +impl OpChanges { + // These routines should return true even if the GUI may have + // special handling for an action, since we need to do the right + // thing when undoing, and if multiple windows of the same type are + // open. For example, while toggling the expand/collapse state + // in the sidebar will not normally trigger a full sidebar refresh, + // requires_browser_sidebar_redraw() should still return true. + + pub fn requires_browser_table_redraw(&self) -> bool { + let c = &self.changes; + c.card + || c.notetype + || (c.note && self.op != Op::AddNote) + || (c.deck && self.op != Op::ExpandCollapse) + } + + pub fn requires_browser_sidebar_redraw(&self) -> bool { + let c = &self.changes; + c.tag || c.deck || c.notetype + } + + pub fn requires_editor_redraw(&self) -> bool { + let c = &self.changes; + c.note || c.notetype + } + + pub fn requires_study_queue_rebuild(&self) -> bool { + let c = &self.changes; + !matches!(self.op, Op::AnswerCard | Op::ExpandCollapse) + && (c.card || c.deck || c.preference) + } +} diff --git a/rslib/src/scheduler/queue/mod.rs b/rslib/src/scheduler/queue/mod.rs index 7fa4d66e4..f45fa8a3a 100644 --- a/rslib/src/scheduler/queue/mod.rs +++ b/rslib/src/scheduler/queue/mod.rs @@ -140,8 +140,7 @@ impl Collection { } pub(crate) fn maybe_clear_study_queues_after_op(&mut self, op: OpChanges) { - if op.op != Op::AnswerCard && (op.changes.card || op.changes.deck || op.changes.preference) - { + if op.requires_study_queue_rebuild() { self.state.card_queues = None; } }