From 3a5d86c34497253a92e218c035d20cc334a93cd7 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Fri, 1 Oct 2021 16:40:25 +0200 Subject: [PATCH 1/2] React to `currentChanged()` signal Decouples changes of the current element and changes of the selection. Introduces `browser.current_card` which has previously been amalgamated with the previewer card `browser.card`. --- qt/aqt/browser/browser.py | 72 ++++++++++++++++++++--------------- qt/aqt/browser/table/table.py | 35 +++++++++-------- 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index 1aad678a9..7a312b614 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -118,7 +118,9 @@ class Browser(QMainWindow): restoreState(self, "editor") restoreSplitter(self.form.splitter, "editor3") self.form.splitter.setChildrenCollapsible(False) + # set if exactly 1 row is selected; used by the previewer self.card: Optional[Card] = None + self.current_card: Optional[Card] = None self.setup_table() self.setupMenus() self.setupHooks() @@ -151,8 +153,9 @@ class Browser(QMainWindow): self.editor.set_note(note) if changes.browser_table and changes.card: - self.card = self.table.get_current_card() - self._update_context_actions() + self.card = self.table.get_single_selected_card() + self.current_card = self.table.get_current_card() + self._update_current_actions() # changes.card is required for updating flag icon if changes.note_text or changes.card: @@ -410,48 +413,45 @@ class Browser(QMainWindow): gui_hooks.editor_did_init.remove(add_preview_button) @ensure_editor_saved - def on_row_changed(self) -> None: - """Update current note and hide/show editor.""" + def on_all_or_selected_rows_changed(self) -> None: + """Called after the selected or all rows (searching, toggling mode) have + changed. Update window title, card preview, context actions, and editor. + """ if self._closeEventHasCleanedUp: return self.updateTitle() - # the current card is used for context actions - self.card = self.table.get_current_card() # if there is only one selected card, use it in the editor # it might differ from the current card - card = self.table.get_single_selected_card() - self.singleCard = bool(card) + self.card = self.table.get_single_selected_card() + self.singleCard = bool(self.card) self.form.splitter.widget(1).setVisible(self.singleCard) if self.singleCard: - self.editor.set_note(card.note(), focusTo=self.focusTo) + self.editor.set_note(self.card.note(), focusTo=self.focusTo) self.focusTo = None - self.editor.card = card + self.editor.card = self.card else: self.editor.set_note(None) self._renderPreview() - self._update_context_actions() + self._update_row_actions() + self._update_selection_actions() gui_hooks.browser_did_change_row(self) - def _update_context_actions(self) -> None: - self._update_enabled_actions() - self._update_flags_menu() - self._update_toggle_mark_action() - self._update_toggle_suspend_action() + def on_current_row_changed(self) -> None: + """Called after the row of the current element has changed.""" + if self._closeEventHasCleanedUp: + return + self.current_card = self.table.get_current_card() + self._update_current_actions() - def _update_enabled_actions(self) -> None: + def _update_row_actions(self) -> None: has_rows = bool(self.table.len()) self.form.actionSelectAll.setEnabled(has_rows) self.form.actionInvertSelection.setEnabled(has_rows) self.form.actionFirstCard.setEnabled(has_rows) self.form.actionLastCard.setEnabled(has_rows) - self.form.action_Info.setEnabled(bool(self.card)) - - self.form.actionPreviousCard.setEnabled(self.table.has_previous()) - - self.form.actionNextCard.setEnabled(self.table.has_next()) - + def _update_selection_actions(self) -> None: has_selection = bool(self.table.len_selection()) self.form.actionSelectNotes.setEnabled(has_selection) self.form.actionExport.setEnabled(has_selection) @@ -467,6 +467,14 @@ class Browser(QMainWindow): self.form.actionToggle_Suspend.setEnabled(has_selection) self.form.menuFlag.setEnabled(has_selection) + def _update_current_actions(self) -> None: + self._update_flags_menu() + self._update_toggle_mark_action() + self._update_toggle_suspend_action() + self.form.action_Info.setEnabled(self.table.has_current()) + self.form.actionPreviousCard.setEnabled(self.table.has_previous()) + self.form.actionNextCard.setEnabled(self.table.has_next()) + @ensure_editor_saved def on_table_state_changed(self, checked: bool) -> None: self.mw.progress.start() @@ -533,10 +541,10 @@ class Browser(QMainWindow): ###################################################################### def showCardInfo(self) -> None: - if not self.card: + if not self.current_card: return - CardInfoDialog(parent=self, mw=self.mw, card=self.card) + CardInfoDialog(parent=self, mw=self.mw, card=self.current_card) # Menu helpers ###################################################################### @@ -705,7 +713,9 @@ class Browser(QMainWindow): ###################################################################### def _update_toggle_suspend_action(self) -> None: - is_suspended = bool(self.card and self.card.queue == QUEUE_TYPE_SUSPENDED) + is_suspended = bool( + self.current_card and self.current_card.queue == QUEUE_TYPE_SUSPENDED + ) self.form.actionToggle_Suspend.setChecked(is_suspended) @skip_if_selection_is_empty @@ -732,11 +742,11 @@ class Browser(QMainWindow): @skip_if_selection_is_empty @ensure_editor_saved def set_flag_of_selected_cards(self, flag: int) -> None: - if not self.card: + if not self.current_card: return # flag needs toggling off? - if flag == self.card.user_flag(): + if flag == self.current_card.user_flag(): flag = 0 set_card_flag( @@ -744,7 +754,7 @@ class Browser(QMainWindow): ).run_in_background() def _update_flags_menu(self) -> None: - flag = self.card and self.card.user_flag() + flag = self.current_card and self.current_card.user_flag() flag = flag or 0 for f in self.mw.flags.all(): @@ -763,7 +773,9 @@ class Browser(QMainWindow): self.remove_tags_from_selected_notes(tags=MARKED_TAG) def _update_toggle_mark_action(self) -> None: - is_marked = bool(self.card and self.card.note().has_tag(MARKED_TAG)) + is_marked = bool( + self.current_card and self.current_card.note().has_tag(MARKED_TAG) + ) self.form.actionToggle_Mark.setChecked(is_marked) # Scheduling diff --git a/qt/aqt/browser/table/table.py b/qt/aqt/browser/table/table.py index 8bd6ea1ac..1a4c96a86 100644 --- a/qt/aqt/browser/table/table.py +++ b/qt/aqt/browser/table/table.py @@ -141,7 +141,8 @@ class Table: self._view.selectRow(row) self._scroll_to_row(row, scroll_even_if_visible=True) else: - self.browser.on_row_changed() + self.browser.on_all_or_selected_rows_changed() + self.browser.on_current_row_changed() # Reset @@ -242,7 +243,8 @@ class Table: return nids self._reset_selection() - self.browser.on_row_changed() + self.browser.on_all_or_selected_rows_changed() + self.browser.on_current_row_changed() return nids def clear_current(self) -> None: @@ -271,8 +273,8 @@ class Table: def _reset_selection(self) -> None: """Remove selection and focus without emitting signals. - If no selection change is triggerd afterwards, `browser.on_row_changed()` - must be called. + If no selection change is triggerd afterwards, `browser.on_all_or_selected_rows_changed()` + and `browser.on_current_row_changed()` must be called. """ self._view.selectionModel().reset() self._len_selection = 0 @@ -331,6 +333,7 @@ class Table: qconnect( self._view.selectionModel().selectionChanged, self._on_selection_changed ) + qconnect(self._view.selectionModel().currentChanged, self._on_current_changed) self._view.setWordWrap(False) self._view.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self._view.horizontalScrollBar().setSingleStep(10) @@ -375,6 +378,10 @@ class Table: # Slots + def _on_current_changed(self, current: QModelIndex, previous: QModelIndex) -> None: + if current.row() != previous.row(): + self.browser.on_current_row_changed() + def _on_selection_changed( self, selected: QItemSelection, deselected: QItemSelection ) -> None: @@ -391,37 +398,32 @@ class Table: # New selection is created. Usually a single row or none at all. self._len_selection = len(self._view.selectionModel().selectedRows()) self._selected_rows = None - self.browser.on_row_changed() + self.browser.on_all_or_selected_rows_changed() def _on_row_state_will_change(self, index: QModelIndex, was_restored: bool) -> None: if not was_restored: - row_changed = False if self._view.selectionModel().isSelected(index): - # calculate directly, because 'self.len_selection()' is slow and - # this method will be called a lot if a lot of rows were deleted self._len_selection -= 1 - row_changed = True self._selected_rows = None + self.browser.on_all_or_selected_rows_changed() if index.row() == self._current().row(): # avoid focus on deleted (disabled) rows self.clear_current() - row_changed = True - if row_changed: - self.browser.on_row_changed() + self.browser.on_current_row_changed() def _on_row_state_changed(self, index: QModelIndex, was_restored: bool) -> None: if was_restored: if self._view.selectionModel().isSelected(index): self._len_selection += 1 self._selected_rows = None - if not self._current().isValid() and self.len_selection() == 0: + self.browser.on_all_or_selected_rows_changed() + elif not self._current().isValid() and self.len_selection() == 0: # restore focus for convenience self._select_rows([index.row()]) self._set_current(index.row()) self._scroll_to_row(index.row()) - # row change has been triggered + # row change and redraw have been triggered return - self.browser.on_row_changed() # Workaround for a bug where the flags for the first column don't update # automatically (due to the shortcut in 'model.flags()') top_left = self._model.index(index.row(), 0) @@ -523,7 +525,8 @@ class Table: self._scroll_to_row(current) if self.len_selection() == 0: # no row change will fire - self.browser.on_row_changed() + self.browser.on_all_or_selected_rows_changed() + self.browser.on_current_row_changed() self._selected_items = [] self._current_item = None From c146f33faa04c26bfc7d6160cc4b23bee2f2e987 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Fri, 1 Oct 2021 18:36:12 +0200 Subject: [PATCH 2/2] Add legacy alias `browser.onRowChanged()` --- qt/aqt/browser/browser.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index 7a312b614..39341f84e 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -437,6 +437,10 @@ class Browser(QMainWindow): self._update_selection_actions() gui_hooks.browser_did_change_row(self) + @deprecated(info="please use on_all_or_selected_rows_changed() instead.") + def onRowChanged(self, *args: Any) -> None: + self.on_all_or_selected_rows_changed() + def on_current_row_changed(self) -> None: """Called after the row of the current element has changed.""" if self._closeEventHasCleanedUp: