mirror of
https://github.com/ankitects/anki.git
synced 2025-09-23 08:22:24 -04:00
remember last input for 'set due'; add string config; nest config types
This commit is contained in:
parent
b56580a8c0
commit
f434cff36f
12 changed files with 124 additions and 48 deletions
|
@ -8,7 +8,8 @@ license = "AGPL-3.0-or-later"
|
|||
members = ["rslib", "pylib/rsbridge"]
|
||||
|
||||
[lib]
|
||||
name = "dummy"
|
||||
# dummy top level for tooling
|
||||
name = "anki"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[package.metadata.raze]
|
||||
|
|
|
@ -50,7 +50,7 @@ MediaSyncProgress = _pb.MediaSyncProgress
|
|||
FullSyncProgress = _pb.FullSyncProgress
|
||||
NormalSyncProgress = _pb.NormalSyncProgress
|
||||
DatabaseCheckProgress = _pb.DatabaseCheckProgress
|
||||
ConfigBool = _pb.ConfigBool
|
||||
Config = _pb.Config
|
||||
EmptyCardsReport = _pb.EmptyCardsReport
|
||||
NoteWithEmptyCards = _pb.NoteWithEmptyCards
|
||||
GraphPreferences = _pb.GraphPreferences
|
||||
|
@ -586,13 +586,20 @@ class Collection:
|
|||
"This is a debugging aid. Prefer .get_config() when you know the key you need."
|
||||
return from_json_bytes(self._backend.get_all_config())
|
||||
|
||||
def get_config_bool(self, key: ConfigBool.Key.V) -> bool:
|
||||
def get_config_bool(self, key: Config.Bool.Key.V) -> bool:
|
||||
return self._backend.get_config_bool(key)
|
||||
|
||||
def set_config_bool(self, key: ConfigBool.Key.V, value: bool) -> None:
|
||||
def set_config_bool(self, key: Config.Bool.Key.V, value: bool) -> None:
|
||||
self.setMod()
|
||||
self._backend.set_config_bool(key=key, value=value)
|
||||
|
||||
def get_config_string(self, key: Config.String.Key.V) -> str:
|
||||
return self._backend.get_config_string(key)
|
||||
|
||||
def set_config_string(self, key: Config.String.Key.V, value: str) -> None:
|
||||
self.setMod()
|
||||
self._backend.set_config_string(key=key, value=value)
|
||||
|
||||
# Stats
|
||||
##########################################################################
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# coding: utf-8
|
||||
import pytest
|
||||
|
||||
from anki.collection import BuiltinSort, ConfigBool
|
||||
from anki.collection import BuiltinSort, Config
|
||||
from anki.consts import *
|
||||
from tests.shared import getEmptyCol, isNearCutoff
|
||||
|
||||
|
@ -121,7 +121,7 @@ def test_findCards():
|
|||
col.flush()
|
||||
assert col.findCards("", order=True)[-1] in latestCardIds
|
||||
assert col.findCards("", order=True)[0] == firstCardId
|
||||
col.set_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS, True)
|
||||
col.set_config_bool(Config.Bool.BROWSER_SORT_BACKWARDS, True)
|
||||
col.flush()
|
||||
assert col.findCards("", order=True)[0] in latestCardIds
|
||||
assert (
|
||||
|
|
|
@ -5,7 +5,9 @@ ignore = forms,hooks_gen.py
|
|||
|
||||
[TYPECHECK]
|
||||
ignored-modules=win32file,pywintypes,socket,win32pipe,winrt,pyaudio
|
||||
ignored-classes=SearchTerm,ConfigBool
|
||||
ignored-classes=
|
||||
SearchTerm,
|
||||
Config,
|
||||
|
||||
[REPORTS]
|
||||
output-format=colorized
|
||||
|
|
|
@ -13,7 +13,7 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union,
|
|||
import aqt
|
||||
import aqt.forms
|
||||
from anki.cards import Card
|
||||
from anki.collection import Collection, ConfigBool, SearchTerm
|
||||
from anki.collection import Collection, Config, SearchTerm
|
||||
from anki.consts import *
|
||||
from anki.errors import InvalidInput
|
||||
from anki.lang import without_unicode_isolation
|
||||
|
@ -852,13 +852,13 @@ QTableView {{ gridline-color: {grid} }}
|
|||
# default to descending for non-text fields
|
||||
if type == "noteFld":
|
||||
ord = not ord
|
||||
self.col.set_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS, ord)
|
||||
self.col.set_config_bool(Config.Bool.BROWSER_SORT_BACKWARDS, ord)
|
||||
self.col.setMod()
|
||||
self.col.save()
|
||||
self.search()
|
||||
else:
|
||||
if self.col.get_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS) != ord:
|
||||
self.col.set_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS, ord)
|
||||
if self.col.get_config_bool(Config.Bool.BROWSER_SORT_BACKWARDS) != ord:
|
||||
self.col.set_config_bool(Config.Bool.BROWSER_SORT_BACKWARDS, ord)
|
||||
self.col.setMod()
|
||||
self.col.save()
|
||||
self.model.reverse()
|
||||
|
@ -871,7 +871,7 @@ QTableView {{ gridline-color: {grid} }}
|
|||
hh.setSortIndicatorShown(False)
|
||||
return
|
||||
idx = self.model.activeCols.index(type)
|
||||
if self.col.get_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS):
|
||||
if self.col.get_config_bool(Config.Bool.BROWSER_SORT_BACKWARDS):
|
||||
ord = Qt.DescendingOrder
|
||||
else:
|
||||
ord = Qt.AscendingOrder
|
||||
|
@ -1399,7 +1399,7 @@ where id in %s"""
|
|||
mw=self.mw,
|
||||
parent=self,
|
||||
card_ids=self.selectedCards(),
|
||||
default="0",
|
||||
default_key=Config.String.SET_DUE_BROWSER,
|
||||
on_done=self._after_schedule,
|
||||
)
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ import time
|
|||
from typing import Any, Callable, Optional, Tuple, Union
|
||||
|
||||
from anki.cards import Card
|
||||
from anki.collection import ConfigBool
|
||||
from anki.collection import Config
|
||||
from aqt import AnkiQt, gui_hooks
|
||||
from aqt.qt import (
|
||||
QAbstractItemView,
|
||||
|
@ -94,7 +94,7 @@ class Previewer(QDialog):
|
|||
both_sides_button.setToolTip(tr(TR.ACTIONS_SHORTCUT_KEY, val="B"))
|
||||
self.bbox.addButton(both_sides_button, QDialogButtonBox.ActionRole)
|
||||
self._show_both_sides = self.mw.col.get_config_bool(
|
||||
ConfigBool.PREVIEW_BOTH_SIDES
|
||||
Config.Bool.PREVIEW_BOTH_SIDES
|
||||
)
|
||||
both_sides_button.setChecked(self._show_both_sides)
|
||||
qconnect(both_sides_button.toggled, self._on_show_both_sides)
|
||||
|
@ -221,7 +221,7 @@ class Previewer(QDialog):
|
|||
|
||||
def _on_show_both_sides(self, toggle: bool) -> None:
|
||||
self._show_both_sides = toggle
|
||||
self.mw.col.set_config_bool(ConfigBool.PREVIEW_BOTH_SIDES, toggle)
|
||||
self.mw.col.set_config_bool(Config.Bool.PREVIEW_BOTH_SIDES, toggle)
|
||||
self.mw.col.setMod()
|
||||
if self._state == "answer" and not toggle:
|
||||
self._state = "question"
|
||||
|
|
|
@ -14,6 +14,7 @@ from PyQt5.QtCore import Qt
|
|||
|
||||
from anki import hooks
|
||||
from anki.cards import Card
|
||||
from anki.collection import Config
|
||||
from anki.utils import stripHTML
|
||||
from aqt import AnkiQt, gui_hooks
|
||||
from aqt.profiles import VideoDriver
|
||||
|
@ -809,7 +810,7 @@ time = %(time)d;
|
|||
mw=self.mw,
|
||||
parent=self.mw,
|
||||
card_ids=[self.card.id],
|
||||
default="1",
|
||||
default_key=Config.String.SET_DUE_REVIEWER,
|
||||
on_done=self.mw.reset,
|
||||
)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ from concurrent.futures import Future
|
|||
from typing import List
|
||||
|
||||
import aqt
|
||||
from anki.collection import Config
|
||||
from anki.errors import InvalidInput
|
||||
from anki.lang import TR
|
||||
from aqt.qt import *
|
||||
|
@ -18,12 +19,14 @@ def set_due_date_dialog(
|
|||
mw: aqt.AnkiQt,
|
||||
parent: QDialog,
|
||||
card_ids: List[int],
|
||||
default: str,
|
||||
default_key: Config.String.Key.V,
|
||||
on_done: Callable[[], None],
|
||||
) -> None:
|
||||
if not card_ids:
|
||||
return
|
||||
|
||||
default = mw.col.get_config_string(default_key)
|
||||
|
||||
(days, success) = getText(
|
||||
prompt=tr(TR.SCHEDULING_SET_DUE_DATE_PROMPT, cards=len(card_ids)),
|
||||
parent=parent,
|
||||
|
@ -33,7 +36,12 @@ def set_due_date_dialog(
|
|||
if not success or not days.strip():
|
||||
return
|
||||
|
||||
def on_done_wrapper(fut: Future) -> None:
|
||||
def set_due() -> None:
|
||||
mw.col.sched.set_due_date(card_ids, days)
|
||||
if days != default:
|
||||
mw.col.set_config_string(default_key, days)
|
||||
|
||||
def after_set(fut: Future) -> None:
|
||||
try:
|
||||
fut.result()
|
||||
except Exception as e:
|
||||
|
@ -53,9 +61,7 @@ def set_due_date_dialog(
|
|||
on_done()
|
||||
|
||||
mw.checkpoint(tr(TR.ACTIONS_SET_DUE_DATE))
|
||||
mw.taskman.with_progress(
|
||||
lambda: mw.col.sched.set_due_date(card_ids, days), on_done_wrapper
|
||||
)
|
||||
mw.taskman.with_progress(set_due, after_set)
|
||||
|
||||
|
||||
def forget_cards(
|
||||
|
|
|
@ -9,7 +9,7 @@ from enum import Enum, auto
|
|||
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, cast
|
||||
|
||||
import aqt
|
||||
from anki.collection import ConfigBool, SearchTerm
|
||||
from anki.collection import Config, SearchTerm
|
||||
from anki.decks import DeckTreeNode
|
||||
from anki.errors import DeckRenameError, InvalidInput
|
||||
from anki.tags import TagTreeNode
|
||||
|
@ -526,7 +526,7 @@ class SidebarTreeView(QTreeView):
|
|||
root: SidebarItem,
|
||||
name: TR.V,
|
||||
icon: Union[str, ColoredIcon],
|
||||
collapse_key: ConfigBool.Key.V,
|
||||
collapse_key: Config.Bool.Key.V,
|
||||
type: Optional[SidebarItemType] = None,
|
||||
) -> SidebarItem:
|
||||
def update(expanded: bool) -> None:
|
||||
|
@ -557,7 +557,7 @@ class SidebarTreeView(QTreeView):
|
|||
root=root,
|
||||
name=TR.BROWSING_SIDEBAR_SAVED_SEARCHES,
|
||||
icon=icon,
|
||||
collapse_key=ConfigBool.COLLAPSE_SAVED_SEARCHES,
|
||||
collapse_key=Config.Bool.COLLAPSE_SAVED_SEARCHES,
|
||||
type=SidebarItemType.SAVED_SEARCH_ROOT,
|
||||
)
|
||||
|
||||
|
@ -584,7 +584,7 @@ class SidebarTreeView(QTreeView):
|
|||
root=root,
|
||||
name=TR.BROWSING_SIDEBAR_RECENT,
|
||||
icon=icon,
|
||||
collapse_key=ConfigBool.COLLAPSE_RECENT,
|
||||
collapse_key=Config.Bool.COLLAPSE_RECENT,
|
||||
type=SidebarItemType.FLAG_ROOT,
|
||||
)
|
||||
type = SidebarItemType.FLAG
|
||||
|
@ -646,7 +646,7 @@ class SidebarTreeView(QTreeView):
|
|||
root=root,
|
||||
name=TR.BROWSING_SIDEBAR_CARD_STATE,
|
||||
icon=icon,
|
||||
collapse_key=ConfigBool.COLLAPSE_CARD_STATE,
|
||||
collapse_key=Config.Bool.COLLAPSE_CARD_STATE,
|
||||
type=SidebarItemType.CARD_STATE_ROOT,
|
||||
)
|
||||
type = SidebarItemType.CARD_STATE
|
||||
|
@ -693,7 +693,7 @@ class SidebarTreeView(QTreeView):
|
|||
root=root,
|
||||
name=TR.BROWSING_SIDEBAR_FLAGS,
|
||||
icon=icon,
|
||||
collapse_key=ConfigBool.COLLAPSE_FLAGS,
|
||||
collapse_key=Config.Bool.COLLAPSE_FLAGS,
|
||||
type=SidebarItemType.FLAG_ROOT,
|
||||
)
|
||||
type = SidebarItemType.FLAG
|
||||
|
@ -771,7 +771,7 @@ class SidebarTreeView(QTreeView):
|
|||
root=root,
|
||||
name=TR.BROWSING_SIDEBAR_TAGS,
|
||||
icon=icon,
|
||||
collapse_key=ConfigBool.COLLAPSE_TAGS,
|
||||
collapse_key=Config.Bool.COLLAPSE_TAGS,
|
||||
type=SidebarItemType.TAG_ROOT,
|
||||
)
|
||||
render(root, tree.children)
|
||||
|
@ -810,7 +810,7 @@ class SidebarTreeView(QTreeView):
|
|||
root=root,
|
||||
name=TR.BROWSING_SIDEBAR_DECKS,
|
||||
icon=icon,
|
||||
collapse_key=ConfigBool.COLLAPSE_DECKS,
|
||||
collapse_key=Config.Bool.COLLAPSE_DECKS,
|
||||
type=SidebarItemType.DECK_ROOT,
|
||||
)
|
||||
render(root, tree.children)
|
||||
|
@ -824,7 +824,7 @@ class SidebarTreeView(QTreeView):
|
|||
root=root,
|
||||
name=TR.BROWSING_SIDEBAR_NOTETYPES,
|
||||
icon=icon,
|
||||
collapse_key=ConfigBool.COLLAPSE_NOTETYPES,
|
||||
collapse_key=Config.Bool.COLLAPSE_NOTETYPES,
|
||||
type=SidebarItemType.NOTETYPE_ROOT,
|
||||
)
|
||||
|
||||
|
|
|
@ -228,8 +228,10 @@ service BackendService {
|
|||
rpc SetConfigJson(SetConfigJsonIn) returns (Empty);
|
||||
rpc RemoveConfig(String) returns (Empty);
|
||||
rpc GetAllConfig(Empty) returns (Json);
|
||||
rpc GetConfigBool(ConfigBool) returns (Bool);
|
||||
rpc GetConfigBool(Config.Bool) returns (Bool);
|
||||
rpc SetConfigBool(SetConfigBoolIn) returns (Empty);
|
||||
rpc GetConfigString(Config.String) returns (String);
|
||||
rpc SetConfigString(SetConfigStringIn) returns (Empty);
|
||||
|
||||
// preferences
|
||||
|
||||
|
@ -1216,7 +1218,8 @@ message SetDeckIn {
|
|||
int64 deck_id = 2;
|
||||
}
|
||||
|
||||
message ConfigBool {
|
||||
message Config {
|
||||
message Bool {
|
||||
enum Key {
|
||||
BROWSER_SORT_BACKWARDS = 0;
|
||||
PREVIEW_BOTH_SIDES = 1;
|
||||
|
@ -1231,7 +1234,21 @@ message ConfigBool {
|
|||
Key key = 1;
|
||||
}
|
||||
|
||||
message String {
|
||||
enum Key {
|
||||
SET_DUE_BROWSER = 0;
|
||||
SET_DUE_REVIEWER = 1;
|
||||
}
|
||||
Key key = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message SetConfigBoolIn {
|
||||
ConfigBool.Key key = 1;
|
||||
Config.Bool.Key key = 1;
|
||||
bool value = 2;
|
||||
}
|
||||
|
||||
message SetConfigStringIn {
|
||||
Config.String.Key key = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
|
|
@ -1493,7 +1493,7 @@ impl BackendService for Backend {
|
|||
.map(Into::into)
|
||||
}
|
||||
|
||||
fn get_config_bool(&self, input: pb::ConfigBool) -> BackendResult<pb::Bool> {
|
||||
fn get_config_bool(&self, input: pb::config::Bool) -> BackendResult<pb::Bool> {
|
||||
self.with_col(|col| {
|
||||
Ok(pb::Bool {
|
||||
val: col.get_bool(input),
|
||||
|
@ -1505,6 +1505,19 @@ impl BackendService for Backend {
|
|||
self.with_col(|col| col.transact(None, |col| col.set_bool(input)))
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
fn get_config_string(&self, input: pb::config::String) -> BackendResult<pb::String> {
|
||||
self.with_col(|col| {
|
||||
Ok(pb::String {
|
||||
val: col.get_string(input),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn set_config_string(&self, input: pb::SetConfigStringIn) -> BackendResult<pb::Empty> {
|
||||
self.with_col(|col| col.transact(None, |col| col.set_string(input)))
|
||||
.map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
|
|
|
@ -5,7 +5,8 @@ use crate::{
|
|||
backend_proto as pb, collection::Collection, decks::DeckID, err::Result, notetype::NoteTypeID,
|
||||
timestamp::TimestampSecs,
|
||||
};
|
||||
use pb::config_bool::Key as BoolKey;
|
||||
use pb::config::bool::Key as BoolKey;
|
||||
use pb::config::string::Key as StringKey;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde_aux::field_attributes::deserialize_bool_from_anything;
|
||||
use serde_derive::Deserialize;
|
||||
|
@ -63,6 +64,8 @@ pub(crate) enum ConfigKey {
|
|||
PreviewBothSides,
|
||||
Rollover,
|
||||
SchedulerVersion,
|
||||
SetDueBrowser,
|
||||
SetDueReviewer,
|
||||
ShowDayLearningCardsFirst,
|
||||
ShowIntervalsAboveAnswerButtons,
|
||||
ShowRemainingDueCountsInStudy,
|
||||
|
@ -102,6 +105,8 @@ impl From<ConfigKey> for &'static str {
|
|||
ConfigKey::PreviewBothSides => "previewBothSides",
|
||||
ConfigKey::Rollover => "rollover",
|
||||
ConfigKey::SchedulerVersion => "schedVer",
|
||||
ConfigKey::SetDueBrowser => "setDueBrowser",
|
||||
ConfigKey::SetDueReviewer => "setDueReviewer",
|
||||
ConfigKey::ShowDayLearningCardsFirst => "dayLearnFirst",
|
||||
ConfigKey::ShowIntervalsAboveAnswerButtons => "estTimes",
|
||||
ConfigKey::ShowRemainingDueCountsInStudy => "dueCounts",
|
||||
|
@ -125,6 +130,15 @@ impl From<BoolKey> for ConfigKey {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<StringKey> for ConfigKey {
|
||||
fn from(key: StringKey) -> Self {
|
||||
match key {
|
||||
StringKey::SetDueBrowser => ConfigKey::SetDueBrowser,
|
||||
StringKey::SetDueReviewer => ConfigKey::SetDueReviewer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a workaround for old clients that used ints to represent boolean
|
||||
/// values. For new config items, prefer using a bool directly.
|
||||
#[derive(Deserialize, Default)]
|
||||
|
@ -345,7 +359,7 @@ impl Collection {
|
|||
}
|
||||
|
||||
#[allow(clippy::match_single_binding)]
|
||||
pub(crate) fn get_bool(&self, config: pb::ConfigBool) -> bool {
|
||||
pub(crate) fn get_bool(&self, config: pb::config::Bool) -> bool {
|
||||
match config.key() {
|
||||
// all options default to false at the moment
|
||||
other => self.get_config_default(ConfigKey::from(other)),
|
||||
|
@ -355,6 +369,21 @@ impl Collection {
|
|||
pub(crate) fn set_bool(&self, input: pb::SetConfigBoolIn) -> Result<()> {
|
||||
self.set_config(ConfigKey::from(input.key()), &input.value)
|
||||
}
|
||||
|
||||
pub(crate) fn get_string(&self, config: pb::config::String) -> String {
|
||||
let key = config.key();
|
||||
let default = match key {
|
||||
StringKey::SetDueBrowser => "0",
|
||||
StringKey::SetDueReviewer => "1",
|
||||
// other => "",
|
||||
};
|
||||
self.get_config_optional(ConfigKey::from(key))
|
||||
.unwrap_or_else(|| default.to_string())
|
||||
}
|
||||
|
||||
pub(crate) fn set_string(&self, input: pb::SetConfigStringIn) -> Result<()> {
|
||||
self.set_config(ConfigKey::from(input.key()), &input.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug, Clone, Copy)]
|
||||
|
|
Loading…
Reference in a new issue