diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 5953219da..2f3b32626 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -697,11 +697,8 @@ class Collection: # Browser Table ########################################################################## - def all_browser_card_columns(self) -> Sequence[BrowserColumns.Column]: - return self._backend.all_browser_card_columns() - - def all_browser_note_columns(self) -> Sequence[BrowserColumns.Column]: - return self._backend.all_browser_note_columns() + def all_browser_columns(self) -> Sequence[BrowserColumns.Column]: + return self._backend.all_browser_columns() def browser_row_for_id( self, id_: int diff --git a/qt/aqt/table.py b/qt/aqt/table.py index 76b3d2f5a..f8a83c18c 100644 --- a/qt/aqt/table.py +++ b/qt/aqt/table.py @@ -347,8 +347,8 @@ class Table: def _on_header_context(self, pos: QPoint) -> None: gpos = self._view.mapToGlobal(pos) m = QMenu() - for key, column in self._state.columns.items(): - a = m.addAction(column.label) + for key, column in self._model.columns.items(): + a = m.addAction(self._state.column_label(column)) a.setCheckable(True) a.setChecked(self._model.active_column_index(key) is not None) qconnect( @@ -522,7 +522,6 @@ class Table: class ItemState(ABC): - _columns: Dict[str, Column] _active_columns: List[str] _sort_column: str _sort_backwards: bool @@ -544,26 +543,16 @@ class ItemState(ABC): def card_ids_from_note_ids(self, items: Sequence[ItemId]) -> Sequence[CardId]: return self.col.db.list(f"select id from cards where nid in {ids2str(items)}") - def column_at(self, index: int) -> Column: - """Returns the column object corresponding to the active column at index or the default - column object if no data is associated with the active column. - """ + def column_key_at(self, index: int) -> str: + return self._active_columns[index] - key = self._active_columns[index] - try: - return self._columns[key] - except KeyError: - self._columns[key] = addon_column_fillin(key) - return self._columns[key] + def column_label(self, column: Column) -> str: + return column.notes_label if self.is_notes_mode() else column.label # Columns and sorting # abstractproperty is deprecated but used due to mypy limitations # (https://github.com/python/mypy/issues/1362) - @abstractproperty - def columns(self) -> Dict[str, Column]: - """Return all for the state available columns.""" - @abstractproperty def active_columns(self) -> List[str]: """Return the saved or default columns for the state.""" @@ -630,17 +619,12 @@ class ItemState(ABC): class CardState(ItemState): def __init__(self, col: Collection) -> None: super().__init__(col) - self._columns = dict(((c.key, c) for c in self.col.all_browser_card_columns())) self._active_columns = self.col.load_browser_card_columns() self._sort_column = self.col.get_config("sortType") self._sort_backwards = self.col.get_config_bool( Config.Bool.BROWSER_SORT_BACKWARDS ) - @property - def columns(self) -> Dict[str, Column]: - return self._columns - @property def active_columns(self) -> List[str]: return self._active_columns @@ -698,17 +682,12 @@ class CardState(ItemState): class NoteState(ItemState): def __init__(self, col: Collection) -> None: super().__init__(col) - self._columns = dict(((c.key, c) for c in self.col.all_browser_note_columns())) self._active_columns = self.col.load_browser_note_columns() self._sort_column = self.col.get_config("noteSortType") self._sort_backwards = self.col.get_config_bool( Config.Bool.BROWSER_NOTE_SORT_BACKWARDS ) - @property - def columns(self) -> Dict[str, Column]: - return self._columns - @property def active_columns(self) -> List[str]: return self._active_columns @@ -832,6 +811,9 @@ class DataModel(QAbstractTableModel): def __init__(self, col: Collection, state: ItemState) -> None: QAbstractTableModel.__init__(self) self.col: Collection = col + self.columns: Dict[str, Column] = dict( + ((c.key, c) for c in self.col.all_browser_columns()) + ) self._state: ItemState = state self._items: Sequence[ItemId] = [] self._rows: Dict[int, CellRow] = {} @@ -1002,10 +984,18 @@ class DataModel(QAbstractTableModel): # Columns def column_at(self, index: QModelIndex) -> Column: - return self._state.column_at(index.column()) + return self.column_at_section(index.column()) def column_at_section(self, section: int) -> Column: - return self._state.column_at(section) + """Returns the column object corresponding to the active column at index or the default + column object if no data is associated with the active column. + """ + key = self._state.column_key_at(section) + try: + return self.columns[key] + except KeyError: + self.columns[key] = addon_column_fillin(key) + return self.columns[key] def active_column_index(self, column: str) -> Optional[int]: return ( @@ -1056,7 +1046,7 @@ class DataModel(QAbstractTableModel): self, section: int, orientation: Qt.Orientation, role: int = 0 ) -> Optional[str]: if orientation == Qt.Horizontal and role == Qt.DisplayRole: - return self.column_at_section(section).label + return self._state.column_label(self.column_at_section(section)) return None def flags(self, index: QModelIndex) -> Qt.ItemFlags: @@ -1094,6 +1084,7 @@ def addon_column_fillin(key: str) -> Column: return Column( key=key, label=tr.browsing_addon(), + notes_label=tr.browsing_addon(), sorting=Columns.SORTING_NONE, uses_cell_font=False, alignment=Columns.ALIGNMENT_CENTER, diff --git a/rslib/backend.proto b/rslib/backend.proto index abe79cfa4..b4479baae 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -248,8 +248,7 @@ service SearchService { rpc JoinSearchNodes(JoinSearchNodesIn) returns (String); rpc ReplaceSearchNode(ReplaceSearchNodeIn) returns (String); rpc FindAndReplace(FindAndReplaceIn) returns (OpChangesWithCount); - rpc AllBrowserCardColumns(Empty) returns (BrowserColumns); - rpc AllBrowserNoteColumns(Empty) returns (BrowserColumns); + rpc AllBrowserColumns(Empty) returns (BrowserColumns); rpc BrowserRowForId(Int64) returns (BrowserRow); rpc SetDesktopBrowserCardColumns(StringList) returns (Empty); rpc SetDesktopBrowserNoteColumns(StringList) returns (Empty); @@ -1067,7 +1066,8 @@ message BrowserColumns { message Column { string key = 1; string label = 2; - Sorting sorting = 3; + string notes_label = 3; + Sorting sorting = 4; bool uses_cell_font = 5; Alignment alignment = 6; } diff --git a/rslib/src/backend/search/browser_table.rs b/rslib/src/backend/search/browser_table.rs index 8451a8695..f544c761c 100644 --- a/rslib/src/backend/search/browser_table.rs +++ b/rslib/src/backend/search/browser_table.rs @@ -3,54 +3,16 @@ use std::str::FromStr; +use strum::IntoEnumIterator; + use crate::{backend_proto as pb, browser_table, collection::Collection, i18n::I18n}; -const CARD_COLUMNS: [browser_table::Column; 15] = [ - browser_table::Column::Question, - browser_table::Column::Answer, - browser_table::Column::Deck, - browser_table::Column::Due, - browser_table::Column::Ease, - browser_table::Column::Lapses, - browser_table::Column::Interval, - browser_table::Column::CardMod, - browser_table::Column::Reps, - browser_table::Column::Cards, - browser_table::Column::NoteCreation, - browser_table::Column::SortField, - browser_table::Column::NoteMod, - browser_table::Column::Tags, - browser_table::Column::Notetype, -]; - -const NOTE_COLUMNS: [browser_table::Column; 13] = [ - browser_table::Column::Deck, - browser_table::Column::CardMod, - browser_table::Column::NoteCards, - browser_table::Column::NoteCreation, - browser_table::Column::NoteDue, - browser_table::Column::NoteEase, - browser_table::Column::SortField, - browser_table::Column::NoteInterval, - browser_table::Column::NoteLapses, - browser_table::Column::NoteMod, - browser_table::Column::NoteReps, - browser_table::Column::Tags, - browser_table::Column::Notetype, -]; - impl Collection { - pub(crate) fn all_browser_card_columns(&self) -> pb::BrowserColumns { - self.to_pb_columns(&CARD_COLUMNS) - } - - pub(crate) fn all_browser_note_columns(&self) -> pb::BrowserColumns { - self.to_pb_columns(&NOTE_COLUMNS) - } - - fn to_pb_columns(&self, columns: &[browser_table::Column]) -> pb::BrowserColumns { - let mut columns: Vec = - columns.iter().map(|c| c.to_pb_column(&self.tr)).collect(); + pub(crate) fn all_browser_columns(&self) -> pb::BrowserColumns { + let mut columns: Vec = browser_table::Column::iter() + .filter(|&c| c != browser_table::Column::Custom) + .map(|c| c.to_pb_column(&self.tr)) + .collect(); columns.sort_by(|c1, c2| c1.label.cmp(&c2.label)); pb::BrowserColumns { columns } } @@ -61,6 +23,7 @@ impl browser_table::Column { pb::browser_columns::Column { key: self.to_string(), label: self.localized_label(i18n), + notes_label: self.localized_notes_label(i18n), sorting: self.sorting() as i32, uses_cell_font: self.uses_cell_font(), alignment: self.alignment() as i32, @@ -94,28 +57,34 @@ impl browser_table::Column { fn localized_label(self, i18n: &I18n) -> String { match self { - Self::Custom => i18n.browsing_addon(), - Self::Question => i18n.browsing_question(), Self::Answer => i18n.browsing_answer(), + Self::CardMod => i18n.search_card_modified(), + Self::Cards => i18n.browsing_card(), Self::Deck => i18n.decks_deck(), Self::Due => i18n.statistics_due_date(), + Self::Custom => i18n.browsing_addon(), Self::Ease => i18n.browsing_ease(), Self::Interval => i18n.browsing_interval(), Self::Lapses => i18n.scheduling_lapses(), - Self::CardMod => i18n.search_card_modified(), - Self::Reps => i18n.scheduling_reviews(), - Self::Cards => i18n.browsing_card(), - Self::NoteCards => i18n.editing_cards(), Self::NoteCreation => i18n.browsing_created(), - Self::NoteDue => i18n.statistics_due_date(), - Self::NoteEase => i18n.browsing_average_ease(), - Self::SortField => i18n.browsing_sort_field(), - Self::NoteInterval => i18n.browsing_average_interval(), Self::NoteMod => i18n.search_note_modified(), - Self::NoteLapses => i18n.scheduling_lapses(), - Self::NoteReps => i18n.scheduling_reviews(), - Self::Tags => i18n.editing_tags(), Self::Notetype => i18n.browsing_note(), + Self::Question => i18n.browsing_question(), + Self::Reps => i18n.scheduling_reviews(), + Self::SortField => i18n.browsing_sort_field(), + Self::Tags => i18n.editing_tags(), + } + .into() + } + + fn localized_notes_label(self, i18n: &I18n) -> String { + match self { + Self::CardMod => i18n.search_card_modified(), + Self::Cards => i18n.editing_cards(), + Self::Ease => i18n.browsing_average_ease(), + Self::Interval => i18n.browsing_average_interval(), + Self::Reps => i18n.scheduling_reviews(), + _ => return self.localized_label(i18n), } .into() } diff --git a/rslib/src/backend/search/mod.rs b/rslib/src/backend/search/mod.rs index f57a10d4e..e5285d6f2 100644 --- a/rslib/src/backend/search/mod.rs +++ b/rslib/src/backend/search/mod.rs @@ -89,12 +89,8 @@ impl SearchService for Backend { }) } - fn all_browser_card_columns(&self, _input: pb::Empty) -> Result { - self.with_col(|col| Ok(col.all_browser_card_columns())) - } - - fn all_browser_note_columns(&self, _input: pb::Empty) -> Result { - self.with_col(|col| Ok(col.all_browser_note_columns())) + fn all_browser_columns(&self, _input: pb::Empty) -> Result { + self.with_col(|col| Ok(col.all_browser_columns())) } fn browser_row_for_id(&self, input: pb::Int64) -> Result { diff --git a/rslib/src/browser_table.rs b/rslib/src/browser_table.rs index 97aa53658..45ee0e9f2 100644 --- a/rslib/src/browser_table.rs +++ b/rslib/src/browser_table.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use itertools::Itertools; use serde_repr::{Deserialize_repr, Serialize_repr}; -use strum::{Display, EnumString}; +use strum::{Display, EnumIter, EnumString}; use crate::error::{AnkiError, Result}; use crate::i18n::I18n; @@ -22,14 +22,18 @@ use crate::{ timestamp::{TimestampMillis, TimestampSecs}, }; -#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy, Display, EnumString)] +#[derive( + Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy, Display, EnumString, EnumIter, +)] #[strum(serialize_all = "camelCase")] #[repr(u8)] pub enum Column { #[strum(serialize = "")] Custom, - Question, Answer, + CardMod, + #[strum(serialize = "template")] + Cards, Deck, #[strum(serialize = "cardDue")] Due, @@ -39,26 +43,17 @@ pub enum Column { Lapses, #[strum(serialize = "cardIvl")] Interval, - CardMod, - #[strum(serialize = "cardReps")] - Reps, - #[strum(serialize = "template")] - Cards, - NoteCards, #[strum(serialize = "noteCrt")] NoteCreation, - NoteDue, - NoteEase, - #[strum(serialize = "noteFld")] - SortField, - #[strum(serialize = "noteIvl")] - NoteInterval, - NoteLapses, NoteMod, - NoteReps, - Tags, #[strum(serialize = "note")] Notetype, + Question, + #[strum(serialize = "cardReps")] + Reps, + #[strum(serialize = "noteFld")] + SortField, + Tags, } impl Default for Column { @@ -295,17 +290,13 @@ impl RowContext { Column::Question => self.question_str(), Column::Answer => self.answer_str(), Column::Deck => self.deck_str(), - Column::Due | Column::NoteDue => self.due_str(), - Column::Ease | Column::NoteEase => self.ease_str(), - Column::Interval | Column::NoteInterval => self.interval_str(), - Column::Lapses | Column::NoteLapses => { - self.cards.iter().map(|c| c.lapses).sum::().to_string() - } + Column::Due => self.due_str(), + Column::Ease => self.ease_str(), + Column::Interval => self.interval_str(), + Column::Lapses => self.cards.iter().map(|c| c.lapses).sum::().to_string(), Column::CardMod => self.card_mod_str(), - Column::Reps | Column::NoteReps => { - self.cards.iter().map(|c| c.reps).sum::().to_string() - } - Column::Cards | Column::NoteCards => self.cards_str()?, + Column::Reps => self.cards.iter().map(|c| c.reps).sum::().to_string(), + Column::Cards => self.cards_str()?, Column::NoteCreation => self.note_creation_str(), Column::SortField => self.note_field_str(), Column::NoteMod => self.note.mtime.date_string(),