Merge pull request #1108 from RumovZ/more-columns
Even more browser fixes and features
This commit is contained in:
commit
8449bbe469
@ -8,6 +8,7 @@ browsing-answer = Answer
|
||||
browsing-any-cards-mapped-to-nothing-will = Any cards mapped to nothing will be deleted. If a note has no remaining cards, it will be lost. Are you sure you want to continue?
|
||||
browsing-any-flag = Any Flag
|
||||
browsing-average-ease = Average Ease
|
||||
browsing-average-interval = Average Interval
|
||||
browsing-browser-appearance = Browser Appearance
|
||||
browsing-browser-options = Browser Options
|
||||
browsing-buried = Buried
|
||||
@ -100,6 +101,7 @@ browsing-suspended = Suspended
|
||||
browsing-tag-duplicates = Tag Duplicates
|
||||
browsing-tag-rename-warning-empty = You can't rename a tag that has no notes.
|
||||
browsing-target-field = Target field:
|
||||
browsing-toggle-cards-notes-mode = Toggle Cards/Notes Mode
|
||||
browsing-toggle-mark = Toggle Mark
|
||||
browsing-toggle-suspend = Toggle Suspend
|
||||
browsing-treat-input-as-regular-expression = Treat input as regular expression
|
||||
|
@ -167,9 +167,7 @@ class Browser(QMainWindow):
|
||||
lambda: self.remove_tags_from_selected_notes(),
|
||||
)
|
||||
qconnect(f.actionClear_Unused_Tags.triggered, self.clear_unused_tags)
|
||||
qconnect(
|
||||
f.actionToggle_Mark.triggered, lambda: self.toggle_mark_of_selected_notes()
|
||||
)
|
||||
qconnect(f.actionToggle_Mark.triggered, self.toggle_mark_of_selected_notes)
|
||||
qconnect(f.actionChangeModel.triggered, self.onChangeModel)
|
||||
qconnect(f.actionFindDuplicates.triggered, self.onFindDupes)
|
||||
qconnect(f.actionFindReplace.triggered, self.onFindReplace)
|
||||
@ -378,6 +376,8 @@ class Browser(QMainWindow):
|
||||
self.table.set_view(self.form.tableView)
|
||||
switch = Switch(11, tr.browsing_card_initial(), tr.browsing_note_initial())
|
||||
switch.setChecked(self.table.is_notes_mode())
|
||||
switch.setToolTip(tr.browsing_toggle_cards_notes_mode())
|
||||
qconnect(self.form.action_toggle_mode.triggered, switch.toggle)
|
||||
qconnect(switch.toggled, self.on_table_state_changed)
|
||||
self.form.gridLayout.addWidget(switch, 0, 0)
|
||||
|
||||
@ -408,7 +408,7 @@ class Browser(QMainWindow):
|
||||
|
||||
@ensure_editor_saved
|
||||
def onRowChanged(
|
||||
self, current: Optional[QItemSelection], previous: Optional[QItemSelection]
|
||||
self, _current: Optional[QItemSelection], _previous: Optional[QItemSelection]
|
||||
) -> None:
|
||||
"""Update current note and hide/show editor. """
|
||||
if self._closeEventHasCleanedUp:
|
||||
@ -428,10 +428,15 @@ class Browser(QMainWindow):
|
||||
self.editor.card = card
|
||||
else:
|
||||
self.editor.set_note(None)
|
||||
self._renderPreview()
|
||||
self._update_flags_menu()
|
||||
self._renderPreview()
|
||||
self._update_context_actions()
|
||||
gui_hooks.browser_did_change_row(self)
|
||||
|
||||
def _update_context_actions(self) -> None:
|
||||
self._update_flags_menu()
|
||||
self._update_toggle_mark_action()
|
||||
self._update_toggle_suspend_action()
|
||||
|
||||
@ensure_editor_saved
|
||||
def on_table_state_changed(self, checked: bool) -> None:
|
||||
self.mw.progress.start()
|
||||
@ -725,15 +730,14 @@ where id in %s"""
|
||||
# Suspending
|
||||
######################################################################
|
||||
|
||||
def current_card_is_suspended(self) -> bool:
|
||||
return bool(self.card and self.card.queue == QUEUE_TYPE_SUSPENDED)
|
||||
def _update_toggle_suspend_action(self) -> None:
|
||||
is_suspended = bool(self.card and self.card.queue == QUEUE_TYPE_SUSPENDED)
|
||||
self.form.actionToggle_Suspend.setChecked(is_suspended)
|
||||
|
||||
@ensure_editor_saved_on_trigger
|
||||
def suspend_selected_cards(self) -> None:
|
||||
want_suspend = not self.current_card_is_suspended()
|
||||
@ensure_editor_saved
|
||||
def suspend_selected_cards(self, checked: bool) -> None:
|
||||
cids = self.selected_cards()
|
||||
|
||||
if want_suspend:
|
||||
if checked:
|
||||
suspend_cards(mw=self.mw, card_ids=cids)
|
||||
else:
|
||||
unsuspend_cards(mw=self.mw, card_ids=cids)
|
||||
@ -776,12 +780,15 @@ where id in %s"""
|
||||
|
||||
qtMenuShortcutWorkaround(self.form.menuFlag)
|
||||
|
||||
def toggle_mark_of_selected_notes(self) -> None:
|
||||
have_mark = bool(self.card and self.card.note().has_tag(MARKED_TAG))
|
||||
if have_mark:
|
||||
self.remove_tags_from_selected_notes(tags=MARKED_TAG)
|
||||
else:
|
||||
def toggle_mark_of_selected_notes(self, checked: bool) -> None:
|
||||
if checked:
|
||||
self.add_tags_to_selected_notes(tags=MARKED_TAG)
|
||||
else:
|
||||
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))
|
||||
self.form.actionToggle_Mark.setChecked(is_marked)
|
||||
|
||||
# Scheduling
|
||||
######################################################################
|
||||
|
@ -218,6 +218,8 @@
|
||||
</property>
|
||||
<addaction name="actionUndo"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_toggle_mode"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSelectAll"/>
|
||||
<addaction name="actionSelectNotes"/>
|
||||
<addaction name="actionInvertSelection"/>
|
||||
@ -467,6 +469,9 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToggle_Suspend">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>browsing_toggle_suspend</string>
|
||||
</property>
|
||||
@ -561,6 +566,9 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToggle_Mark">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>browsing_toggle_mark</string>
|
||||
</property>
|
||||
@ -597,6 +605,14 @@
|
||||
<string>qt_accel_forget</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_toggle_mode">
|
||||
<property name="text">
|
||||
<string>browsing_toggle_cards_notes_mode</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+M</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="icons.qrc"/>
|
||||
|
@ -9,7 +9,9 @@ from aqt.theme import theme_manager
|
||||
|
||||
class Switch(QAbstractButton):
|
||||
"""A horizontal slider to toggle between two states which can be denoted by short strings.
|
||||
The left state is the default and corresponds to isChecked=False.
|
||||
|
||||
The left state is the default and corresponds to isChecked()=False.
|
||||
The suppoorted slots are toggle(), for an animated transition, and setChecked().
|
||||
"""
|
||||
|
||||
_margin: int = 2
|
||||
@ -91,7 +93,7 @@ class Switch(QAbstractButton):
|
||||
)
|
||||
|
||||
def _paint_knob(self, painter: QPainter) -> None:
|
||||
painter.setBrush(QBrush(theme_manager.qcolor(colors.HIGHLIGHT_BG)))
|
||||
painter.setBrush(QBrush(theme_manager.qcolor(colors.LINK)))
|
||||
painter.drawEllipse(self._current_knob_rectangle())
|
||||
|
||||
def _paint_label(self, painter: QPainter) -> None:
|
||||
@ -104,12 +106,20 @@ class Switch(QAbstractButton):
|
||||
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
|
||||
super().mouseReleaseEvent(event)
|
||||
if event.button() == Qt.LeftButton:
|
||||
animation = QPropertyAnimation(self, b"position", self)
|
||||
animation.setDuration(100)
|
||||
animation.setStartValue(self.start_position)
|
||||
animation.setEndValue(self.end_position)
|
||||
animation.start()
|
||||
self._animate_toggle()
|
||||
|
||||
def enterEvent(self, event: QEvent) -> None:
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
super().enterEvent(event)
|
||||
|
||||
def toggle(self) -> None:
|
||||
super().toggle()
|
||||
self._animate_toggle()
|
||||
|
||||
def _animate_toggle(self) -> None:
|
||||
animation = QPropertyAnimation(self, b"position", self)
|
||||
animation.setDuration(100)
|
||||
animation.setStartValue(self.start_position)
|
||||
animation.setEndValue(self.end_position)
|
||||
# make triggered events execute first so the animation runs smoothly afterwards
|
||||
QTimer.singleShot(50, animation.start)
|
||||
|
@ -717,8 +717,10 @@ class NoteState(ItemState):
|
||||
("note", tr.browsing_note()),
|
||||
("noteCards", tr.editing_cards()),
|
||||
("noteCrt", tr.browsing_created()),
|
||||
("noteDue", tr.statistics_due_date()),
|
||||
("noteEase", tr.browsing_average_ease()),
|
||||
("noteFld", tr.browsing_sort_field()),
|
||||
("noteIvl", tr.browsing_average_interval()),
|
||||
("noteLapses", tr.scheduling_lapses()),
|
||||
("noteMod", tr.search_note_modified()),
|
||||
("noteReps", tr.scheduling_reviews()),
|
||||
|
@ -811,21 +811,23 @@ message SortOrder {
|
||||
enum Kind {
|
||||
NOTE_CARDS = 0;
|
||||
NOTE_CREATION = 1;
|
||||
NOTE_EASE = 2;
|
||||
NOTE_FIELD = 3;
|
||||
NOTE_LAPSES = 4;
|
||||
NOTE_MOD = 5;
|
||||
NOTE_REPS = 6;
|
||||
NOTE_TAGS = 7;
|
||||
NOTETYPE = 8;
|
||||
CARD_MOD = 9;
|
||||
CARD_REPS = 10;
|
||||
CARD_DUE = 11;
|
||||
CARD_EASE = 12;
|
||||
CARD_LAPSES = 13;
|
||||
CARD_INTERVAL = 14;
|
||||
CARD_DECK = 15;
|
||||
CARD_TEMPLATE = 16;
|
||||
NOTE_DUE = 2;
|
||||
NOTE_EASE = 3;
|
||||
NOTE_FIELD = 4;
|
||||
NOTE_INTERVAL = 5;
|
||||
NOTE_LAPSES = 6;
|
||||
NOTE_MOD = 7;
|
||||
NOTE_REPS = 8;
|
||||
NOTE_TAGS = 9;
|
||||
NOTETYPE = 10;
|
||||
CARD_MOD = 11;
|
||||
CARD_REPS = 12;
|
||||
CARD_DUE = 13;
|
||||
CARD_EASE = 14;
|
||||
CARD_LAPSES = 15;
|
||||
CARD_INTERVAL = 16;
|
||||
CARD_DECK = 17;
|
||||
CARD_TEMPLATE = 18;
|
||||
}
|
||||
Kind kind = 1;
|
||||
bool reverse = 2;
|
||||
|
@ -24,8 +24,10 @@ impl From<String> for browser_table::Column {
|
||||
"template" => browser_table::Column::CardTemplate,
|
||||
"noteCards" => browser_table::Column::NoteCards,
|
||||
"noteCrt" => browser_table::Column::NoteCreation,
|
||||
"noteDue" => browser_table::Column::NoteDue,
|
||||
"noteEase" => browser_table::Column::NoteEase,
|
||||
"noteFld" => browser_table::Column::NoteField,
|
||||
"noteIvl" => browser_table::Column::NoteInterval,
|
||||
"noteLapses" => browser_table::Column::NoteLapses,
|
||||
"noteMod" => browser_table::Column::NoteMod,
|
||||
"noteReps" => browser_table::Column::NoteReps,
|
||||
|
@ -109,7 +109,9 @@ impl From<SortKindProto> for SortKind {
|
||||
match kind {
|
||||
SortKindProto::NoteCards => SortKind::NoteCards,
|
||||
SortKindProto::NoteCreation => SortKind::NoteCreation,
|
||||
SortKindProto::NoteDue => SortKind::NoteDue,
|
||||
SortKindProto::NoteEase => SortKind::NoteEase,
|
||||
SortKindProto::NoteInterval => SortKind::NoteInterval,
|
||||
SortKindProto::NoteLapses => SortKind::NoteLapses,
|
||||
SortKindProto::NoteMod => SortKind::NoteMod,
|
||||
SortKindProto::NoteField => SortKind::NoteField,
|
||||
|
@ -24,26 +24,28 @@ use crate::{
|
||||
#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum Column {
|
||||
Custom = 0,
|
||||
Question = 1,
|
||||
Answer = 2,
|
||||
CardDeck = 3,
|
||||
CardDue = 4,
|
||||
CardEase = 5,
|
||||
CardLapses = 6,
|
||||
CardInterval = 7,
|
||||
CardMod = 8,
|
||||
CardReps = 9,
|
||||
CardTemplate = 10,
|
||||
NoteCards = 11,
|
||||
NoteCreation = 12,
|
||||
NoteEase = 13,
|
||||
NoteField = 14,
|
||||
NoteLapses = 15,
|
||||
NoteMod = 16,
|
||||
NoteReps = 17,
|
||||
NoteTags = 18,
|
||||
Notetype = 19,
|
||||
Custom,
|
||||
Question,
|
||||
Answer,
|
||||
CardDeck,
|
||||
CardDue,
|
||||
CardEase,
|
||||
CardLapses,
|
||||
CardInterval,
|
||||
CardMod,
|
||||
CardReps,
|
||||
CardTemplate,
|
||||
NoteCards,
|
||||
NoteCreation,
|
||||
NoteDue,
|
||||
NoteEase,
|
||||
NoteField,
|
||||
NoteInterval,
|
||||
NoteLapses,
|
||||
NoteMod,
|
||||
NoteReps,
|
||||
NoteTags,
|
||||
Notetype,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -77,13 +79,13 @@ pub struct Font {
|
||||
}
|
||||
|
||||
trait RowContext {
|
||||
fn get_cell_text(&mut self, column: &Column) -> Result<String>;
|
||||
fn get_cell_text(&mut self, column: Column) -> Result<String>;
|
||||
fn get_row_color(&self) -> Color;
|
||||
fn get_row_font(&self) -> Result<Font>;
|
||||
fn note(&self) -> &Note;
|
||||
fn notetype(&self) -> &Notetype;
|
||||
|
||||
fn get_cell(&mut self, column: &Column) -> Result<Cell> {
|
||||
fn get_cell(&mut self, column: Column) -> Result<Cell> {
|
||||
Ok(Cell {
|
||||
text: self.get_cell_text(column)?,
|
||||
is_rtl: self.get_is_rtl(column),
|
||||
@ -101,7 +103,7 @@ trait RowContext {
|
||||
html_to_text_line(&self.note().fields()[index]).into()
|
||||
}
|
||||
|
||||
fn get_is_rtl(&self, column: &Column) -> bool {
|
||||
fn get_is_rtl(&self, column: Column) -> bool {
|
||||
match column {
|
||||
Column::NoteField => {
|
||||
let index = self.notetype().config.sort_field_idx as usize;
|
||||
@ -115,7 +117,7 @@ trait RowContext {
|
||||
Ok(Row {
|
||||
cells: columns
|
||||
.iter()
|
||||
.map(|column| self.get_cell(column))
|
||||
.map(|&column| self.get_cell(column))
|
||||
.collect::<Result<_>>()?,
|
||||
color: self.get_row_color(),
|
||||
font: self.get_row_font()?,
|
||||
@ -147,6 +149,7 @@ struct NoteRowContext<'a> {
|
||||
notetype: Arc<Notetype>,
|
||||
cards: Vec<Card>,
|
||||
tr: &'a I18n,
|
||||
timing: SchedTimingToday,
|
||||
}
|
||||
|
||||
fn card_render_required(columns: &[Column]) -> bool {
|
||||
@ -155,6 +158,49 @@ fn card_render_required(columns: &[Column]) -> bool {
|
||||
.any(|c| matches!(c, Column::Question | Column::Answer))
|
||||
}
|
||||
|
||||
impl Card {
|
||||
fn is_new_type_or_queue(&self) -> bool {
|
||||
self.queue == CardQueue::New || self.ctype == CardType::New
|
||||
}
|
||||
|
||||
fn is_filtered_deck(&self) -> bool {
|
||||
self.original_deck_id != DeckId(0)
|
||||
}
|
||||
|
||||
/// Returns true if the card can not be due as it's buried or suspended.
|
||||
fn is_undue_queue(&self) -> bool {
|
||||
(self.queue as i8) < 0
|
||||
}
|
||||
|
||||
/// Returns true if the card has a due date in terms of days.
|
||||
fn is_due_in_days(&self) -> bool {
|
||||
matches!(self.queue, CardQueue::DayLearn | CardQueue::Review)
|
||||
|| (self.ctype == CardType::Review && self.is_undue_queue())
|
||||
}
|
||||
|
||||
/// Returns the card's due date as a timestamp if it has one.
|
||||
fn due_time(&self, timing: &SchedTimingToday) -> Option<TimestampSecs> {
|
||||
if self.queue == CardQueue::Learn {
|
||||
Some(TimestampSecs(self.due as i64))
|
||||
} else if self.is_due_in_days() {
|
||||
Some(
|
||||
TimestampSecs::now()
|
||||
.adding_secs(((self.due - timing.days_elapsed as i32) * 86400) as i64),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Note {
|
||||
fn is_marked(&self) -> bool {
|
||||
self.tags
|
||||
.iter()
|
||||
.any(|tag| tag.eq_ignore_ascii_case("marked"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn browser_row_for_id(&mut self, id: i64) -> Result<Row> {
|
||||
if self.get_bool(BoolKey::BrowserTableShowNotesMode) {
|
||||
@ -297,25 +343,16 @@ impl<'a> CardRowContext<'a> {
|
||||
}
|
||||
|
||||
fn card_due_str(&mut self) -> String {
|
||||
let due = if self.card.original_deck_id != DeckId(0) {
|
||||
let due = if self.card.is_filtered_deck() {
|
||||
self.tr.browsing_filtered()
|
||||
} else if self.card.queue == CardQueue::New || self.card.ctype == CardType::New {
|
||||
} else if self.card.is_new_type_or_queue() {
|
||||
self.tr.statistics_due_for_new_card(self.card.due)
|
||||
} else if let Some(time) = self.card.due_time(&self.timing) {
|
||||
time.date_string().into()
|
||||
} else {
|
||||
let date = if self.card.queue == CardQueue::Learn {
|
||||
TimestampSecs(self.card.due as i64)
|
||||
} else if self.card.queue == CardQueue::DayLearn
|
||||
|| self.card.queue == CardQueue::Review
|
||||
|| (self.card.ctype == CardType::Review && (self.card.queue as i8) < 0)
|
||||
{
|
||||
TimestampSecs::now()
|
||||
.adding_secs(((self.card.due - self.timing.days_elapsed as i32) * 86400) as i64)
|
||||
} else {
|
||||
return "".into();
|
||||
};
|
||||
date.date_string().into()
|
||||
return "".into();
|
||||
};
|
||||
if (self.card.queue as i8) < 0 {
|
||||
if self.card.is_undue_queue() {
|
||||
format!("({})", due)
|
||||
} else {
|
||||
due.into()
|
||||
@ -360,7 +397,7 @@ impl<'a> CardRowContext<'a> {
|
||||
}
|
||||
|
||||
impl RowContext for CardRowContext<'_> {
|
||||
fn get_cell_text(&mut self, column: &Column) -> Result<String> {
|
||||
fn get_cell_text(&mut self, column: Column) -> Result<String> {
|
||||
Ok(match column {
|
||||
Column::Question => self.question_str(),
|
||||
Column::Answer => self.answer_str(),
|
||||
@ -388,12 +425,7 @@ impl RowContext for CardRowContext<'_> {
|
||||
3 => Color::FlagGreen,
|
||||
4 => Color::FlagBlue,
|
||||
_ => {
|
||||
if self
|
||||
.note
|
||||
.tags
|
||||
.iter()
|
||||
.any(|tag| tag.eq_ignore_ascii_case("marked"))
|
||||
{
|
||||
if self.note.is_marked() {
|
||||
Color::Marked
|
||||
} else if self.card.queue == CardQueue::Suspended {
|
||||
Color::Suspended
|
||||
@ -427,37 +459,74 @@ impl<'a> NoteRowContext<'a> {
|
||||
.get_notetype(note.notetype_id)?
|
||||
.ok_or(AnkiError::NotFound)?;
|
||||
let cards = col.storage.all_cards_of_note(note.id)?;
|
||||
let timing = col.timing_today()?;
|
||||
|
||||
Ok(NoteRowContext {
|
||||
note,
|
||||
notetype,
|
||||
cards,
|
||||
tr: &col.tr,
|
||||
timing,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the average ease of the non-new cards or a hint if there aren't any.
|
||||
fn note_ease_str(&self) -> String {
|
||||
let cards = self
|
||||
let eases: Vec<u16> = self
|
||||
.cards
|
||||
.iter()
|
||||
.filter(|c| c.ctype != CardType::New)
|
||||
.collect::<Vec<&Card>>();
|
||||
if cards.is_empty() {
|
||||
.map(|c| c.ease_factor)
|
||||
.collect();
|
||||
if eases.is_empty() {
|
||||
self.tr.browsing_new().into()
|
||||
} else {
|
||||
let ease = cards.iter().map(|c| c.ease_factor).sum::<u16>() / cards.len() as u16;
|
||||
format!("{}%", ease / 10)
|
||||
format!("{}%", eases.iter().sum::<u16>() / eases.len() as u16 / 10)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the due date of the next due card that is not in a filtered deck, new, suspended or
|
||||
/// buried or the empty string if there is no such card.
|
||||
fn note_due_str(&self) -> String {
|
||||
self.cards
|
||||
.iter()
|
||||
.filter(|c| !(c.is_filtered_deck() || c.is_new_type_or_queue() || c.is_undue_queue()))
|
||||
.filter_map(|c| c.due_time(&self.timing))
|
||||
.min()
|
||||
.map(|time| time.date_string())
|
||||
.unwrap_or_else(|| "".into())
|
||||
}
|
||||
|
||||
/// Returns the average interval of the review and relearn cards or the empty string if there
|
||||
/// aren't any.
|
||||
fn note_interval_str(&self) -> String {
|
||||
let intervals: Vec<u32> = self
|
||||
.cards
|
||||
.iter()
|
||||
.filter(|c| matches!(c.ctype, CardType::Review | CardType::Relearn))
|
||||
.map(|c| c.interval)
|
||||
.collect();
|
||||
if intervals.is_empty() {
|
||||
"".into()
|
||||
} else {
|
||||
time_span(
|
||||
(intervals.iter().sum::<u32>() * 86400 / (intervals.len() as u32)) as f32,
|
||||
self.tr,
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RowContext for NoteRowContext<'_> {
|
||||
fn get_cell_text(&mut self, column: &Column) -> Result<String> {
|
||||
fn get_cell_text(&mut self, column: Column) -> Result<String> {
|
||||
Ok(match column {
|
||||
Column::NoteCards => self.cards.len().to_string(),
|
||||
Column::NoteCreation => self.note_creation_str(),
|
||||
Column::NoteDue => self.note_due_str(),
|
||||
Column::NoteEase => self.note_ease_str(),
|
||||
Column::NoteField => self.note_field_str(),
|
||||
Column::NoteInterval => self.note_interval_str(),
|
||||
Column::NoteLapses => self.cards.iter().map(|c| c.lapses).sum::<u32>().to_string(),
|
||||
Column::NoteMod => self.note.mtime.date_string(),
|
||||
Column::NoteReps => self.cards.iter().map(|c| c.reps).sum::<u32>().to_string(),
|
||||
@ -468,12 +537,7 @@ impl RowContext for NoteRowContext<'_> {
|
||||
}
|
||||
|
||||
fn get_row_color(&self) -> Color {
|
||||
if self
|
||||
.note
|
||||
.tags
|
||||
.iter()
|
||||
.any(|tag| tag.eq_ignore_ascii_case("marked"))
|
||||
{
|
||||
if self.note.is_marked() {
|
||||
Color::Marked
|
||||
} else {
|
||||
Color::Default
|
||||
|
@ -271,7 +271,10 @@ pub enum SortKind {
|
||||
NoteCards,
|
||||
#[serde(rename = "noteCrt")]
|
||||
NoteCreation,
|
||||
NoteDue,
|
||||
NoteEase,
|
||||
#[serde(rename = "noteIvl")]
|
||||
NoteInterval,
|
||||
NoteLapses,
|
||||
NoteMod,
|
||||
#[serde(rename = "noteFld")]
|
||||
|
@ -90,8 +90,10 @@ impl SortKind {
|
||||
match self {
|
||||
SortKind::NoteCards
|
||||
| SortKind::NoteCreation
|
||||
| SortKind::NoteDue
|
||||
| SortKind::NoteEase
|
||||
| SortKind::NoteField
|
||||
| SortKind::NoteInterval
|
||||
| SortKind::NoteLapses
|
||||
| SortKind::NoteMod
|
||||
| SortKind::NoteReps
|
||||
@ -252,9 +254,12 @@ fn card_order_from_sortkind(kind: SortKind) -> Cow<'static, str> {
|
||||
|
||||
fn note_order_from_sortkind(kind: SortKind) -> Cow<'static, str> {
|
||||
match kind {
|
||||
SortKind::NoteCards | SortKind::NoteEase | SortKind::NoteLapses | SortKind::NoteReps => {
|
||||
"(select pos from sort_order where nid = n.id) asc".into()
|
||||
}
|
||||
SortKind::NoteCards
|
||||
| SortKind::NoteDue
|
||||
| SortKind::NoteEase
|
||||
| SortKind::NoteInterval
|
||||
| SortKind::NoteLapses
|
||||
| SortKind::NoteReps => "(select pos from sort_order where nid = n.id) asc".into(),
|
||||
SortKind::NoteCreation => "n.id asc".into(),
|
||||
SortKind::NoteField => "n.sfld collate nocase asc".into(),
|
||||
SortKind::NoteMod => "n.mod asc".into(),
|
||||
@ -270,7 +275,9 @@ fn prepare_sort(col: &mut Collection, kind: SortKind) -> Result<()> {
|
||||
CardDeck => include_str!("deck_order.sql"),
|
||||
CardTemplate => include_str!("template_order.sql"),
|
||||
NoteCards => include_str!("note_cards_order.sql"),
|
||||
NoteDue => include_str!("note_due_order.sql"),
|
||||
NoteEase => include_str!("note_ease_order.sql"),
|
||||
NoteInterval => include_str!("note_interval_order.sql"),
|
||||
NoteLapses => include_str!("note_lapses_order.sql"),
|
||||
NoteReps => include_str!("note_reps_order.sql"),
|
||||
Notetype => include_str!("notetype_order.sql"),
|
||||
|
16
rslib/src/search/note_due_order.sql
Normal file
16
rslib/src/search/note_due_order.sql
Normal file
@ -0,0 +1,16 @@
|
||||
DROP TABLE IF EXISTS sort_order;
|
||||
CREATE TEMPORARY TABLE sort_order (
|
||||
pos integer PRIMARY KEY,
|
||||
nid integer NOT NULL UNIQUE
|
||||
);
|
||||
INSERT INTO sort_order (nid)
|
||||
SELECT nid
|
||||
FROM cards
|
||||
WHERE (
|
||||
odid = 0
|
||||
AND type != 0
|
||||
AND queue > 0
|
||||
)
|
||||
GROUP BY nid
|
||||
ORDER BY MIN(type),
|
||||
MIN(due);
|
11
rslib/src/search/note_interval_order.sql
Normal file
11
rslib/src/search/note_interval_order.sql
Normal file
@ -0,0 +1,11 @@
|
||||
DROP TABLE IF EXISTS sort_order;
|
||||
CREATE TEMPORARY TABLE sort_order (
|
||||
pos integer PRIMARY KEY,
|
||||
nid integer NOT NULL UNIQUE
|
||||
);
|
||||
INSERT INTO sort_order (nid)
|
||||
SELECT nid
|
||||
FROM cards
|
||||
WHERE type IN (2, 3)
|
||||
GROUP BY nid
|
||||
ORDER BY AVG(ivl);
|
Loading…
Reference in New Issue
Block a user