diff --git a/ftl/core/deck-config.ftl b/ftl/core/deck-config.ftl index 480f95c97..107532f7a 100644 --- a/ftl/core/deck-config.ftl +++ b/ftl/core/deck-config.ftl @@ -213,3 +213,7 @@ deck-config-reviews-too-low = deck-config-learning-step-above-graduating-interval = The graduating interval should be at least as long as your final learning step. deck-config-good-above-easy = The easy interval should be at least as long as the graduating interval. deck-config-relearning-steps-above-minimum-interval = The minimum lapse interval should be at least as long as your final relearning step. + +## Selecting a deck + +deck-config-which-deck = Which deck would you like? diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index 31f6a5e3f..eb1af4b56 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -12,6 +12,7 @@ from anki.collection import OpChanges from anki.decks import Deck, DeckCollapseScope, DeckId, DeckTreeNode from anki.utils import intTime from aqt import AnkiQt, gui_hooks +from aqt.deckoptions import display_options_for_deck_id from aqt.operations import QueryOp from aqt.operations.deck import ( add_deck_dialog, @@ -295,10 +296,7 @@ class DeckBrowser: ).run_in_background() def _options(self, did: DeckId) -> None: - # select the deck first, because the dyn deck conf assumes the deck - # we're editing is the current one - self.mw.col.decks.select(did) - self.mw.onDeckConf() + display_options_for_deck_id(did) def _collapse(self, did: DeckId) -> None: node = self.mw.col.decks.find_deck_in_tree(self._dueTree, did) diff --git a/qt/aqt/deckconf.py b/qt/aqt/deckconf.py index d8028737c..6a5b93329 100644 --- a/qt/aqt/deckconf.py +++ b/qt/aqt/deckconf.py @@ -1,5 +1,8 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +from __future__ import annotations + from operator import itemgetter from typing import Any, Dict, List, Optional diff --git a/qt/aqt/deckoptions.py b/qt/aqt/deckoptions.py index efba64762..33079efa0 100644 --- a/qt/aqt/deckoptions.py +++ b/qt/aqt/deckoptions.py @@ -3,11 +3,23 @@ from __future__ import annotations +from typing import List, Optional + import aqt +import aqt.deckconf +from anki.cards import Card +from anki.decks import Deck, DeckId from anki.lang import without_unicode_isolation from aqt import gui_hooks from aqt.qt import * -from aqt.utils import addCloseShortcut, disable_help_button, restoreGeom, saveGeom, tr +from aqt.utils import ( + KeyboardModifiersPressed, + addCloseShortcut, + disable_help_button, + restoreGeom, + saveGeom, + tr, +) from aqt.webview import AnkiWebView @@ -17,9 +29,10 @@ class DeckOptionsDialog(QDialog): TITLE = "deckOptions" silentlyClose = True - def __init__(self, mw: aqt.main.AnkiQt) -> None: + def __init__(self, mw: aqt.main.AnkiQt, deck: Deck) -> None: QDialog.__init__(self, mw, Qt.Window) self.mw = mw + self._deck = deck self._setup_ui() self.show() @@ -39,13 +52,12 @@ class DeckOptionsDialog(QDialog): layout.addWidget(self.web) self.setLayout(layout) - deck = self.mw.col.decks.get_current() self.web.eval( f"""const $deckOptions = anki.deckOptions( - document.getElementById('main'), {deck.id});""" + document.getElementById('main'), {self._deck.id});""" ) self.setWindowTitle( - without_unicode_isolation(tr.actions_options_for(val=deck.name)) + without_unicode_isolation(tr.actions_options_for(val=self._deck.name)) ) gui_hooks.deck_options_did_load(self) @@ -53,3 +65,51 @@ class DeckOptionsDialog(QDialog): self.web = None saveGeom(self, self.TITLE) QDialog.reject(self) + + +def confirm_deck_then_display_options(active_card: Optional[Card] = None) -> None: + decks = [aqt.mw.col.decks.get_current()] + if card := active_card: + if card.odid and card.odid != decks[0].id: + decks.append(aqt.mw.col.get_deck(card.odid)) + + if not any(d.id == card.did for d in decks): + decks.append(aqt.mw.col.get_deck(card.odid)) + + if len(decks) == 1: + display_options_for_deck(decks[0]) + else: + decks.sort(key=lambda x: x.WhichOneof("kind") == "filtered") + _deck_prompt_dialog(decks) + + +def _deck_prompt_dialog(decks: List[Deck]) -> None: + diag = QDialog(aqt.mw.app.activeWindow()) + diag.setWindowTitle("Anki") + box = QVBoxLayout() + box.addWidget(QLabel(tr.deck_config_which_deck())) + for deck in decks: + button = QPushButton(deck.name) + qconnect(button.clicked, lambda _, deck=deck: display_options_for_deck(deck)) + qconnect(button.clicked, diag.close) + box.addWidget(button) + button = QPushButton(tr.actions_cancel()) + qconnect(button.clicked, diag.close) + box.addWidget(button) + diag.setLayout(box) + diag.exec_() + + +def display_options_for_deck_id(deck_id: DeckId) -> None: + display_options_for_deck(aqt.mw.col.get_deck(deck_id)) + + +def display_options_for_deck(deck: Deck) -> None: + if deck.WhichOneof("kind") == "normal": + if KeyboardModifiersPressed().shift or aqt.mw.col.schedVer() == 1: + deck_legacy = aqt.mw.col.decks.get(DeckId(deck.id)) + aqt.deckconf.DeckConf(aqt.mw, deck_legacy) + else: + DeckOptionsDialog(aqt.mw, deck) + else: + aqt.dialogs.open("FilteredDeckConfigDialog", aqt.mw, deck_id=deck.id) diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 3faa3feff..b433cb0ca 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -48,7 +48,6 @@ from anki.utils import devMode, ids2str, intTime, isMac, isWin, splitFields from aqt import gui_hooks from aqt.addons import DownloadLogEntry, check_and_prompt_for_updates, show_log_to_user from aqt.dbcheck import check_db -from aqt.deckoptions import DeckOptionsDialog from aqt.emptycards import show_empty_cards from aqt.legacy import install_pylib_legacy from aqt.mediacheck import check_media_db @@ -1120,19 +1119,6 @@ title="%s" %s>%s""" % ( def onEditCurrent(self) -> None: aqt.dialogs.open("EditCurrent", self) - def onDeckConf(self, deck: Optional[DeckDict] = None) -> None: - import aqt.deckconf - - if not deck: - deck = self.col.decks.current() - if deck["dyn"]: - aqt.dialogs.open("FilteredDeckConfigDialog", self, deck_id=deck["id"]) - else: - if KeyboardModifiersPressed().shift or self.col.schedVer() == 1: - aqt.deckconf.DeckConf(self, deck) - else: - DeckOptionsDialog(self) - def onOverview(self) -> None: self.col.reset() self.moveToState("overview") @@ -1164,6 +1150,11 @@ title="%s" %s>%s""" % ( def onDocumentation(self) -> None: openHelp(HelpPage.INDEX) + # legacy + + def onDeckConf(self, deck: Optional[DeckDict] = None) -> None: + pass + # Importing & exporting ########################################################################## diff --git a/qt/aqt/overview.py b/qt/aqt/overview.py index bda4f5632..117ffaa10 100644 --- a/qt/aqt/overview.py +++ b/qt/aqt/overview.py @@ -10,6 +10,7 @@ from anki.collection import OpChanges from anki.scheduler import UnburyDeck from aqt import gui_hooks from aqt.deckdescription import DeckDescriptionDialog +from aqt.deckoptions import display_options_for_deck from aqt.operations.scheduling import ( empty_filtered_deck, rebuild_filtered_deck, @@ -93,7 +94,7 @@ class Overview: elif url == "anki": print("anki menu") elif url == "opts": - self.mw.onDeckConf() + display_options_for_deck(self.mw.col.decks.get_current()) elif url == "cram": aqt.dialogs.open("FilteredDeckConfigDialog", self.mw) elif url == "refresh": @@ -116,7 +117,7 @@ class Overview: def _shortcutKeys(self) -> List[Tuple[str, Callable]]: return [ - ("o", self.mw.onDeckConf), + ("o", lambda: display_options_for_deck(self.mw.col.decks.get_current())), ("r", self.rebuild_current_filtered_deck), ("e", self.empty_current_filtered_deck), ("c", self.onCustomStudyKey), diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index ffca8ebf9..ce6d93dbc 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -23,6 +23,7 @@ from anki.scheduler.v3 import Scheduler as V3Scheduler from anki.tags import MARKED_TAG from anki.utils import stripHTML from aqt import AnkiQt, gui_hooks +from aqt.deckoptions import confirm_deck_then_display_options from aqt.flags import load_flags from aqt.operations.card import set_card_flag from aqt.operations.note import remove_notes @@ -953,7 +954,7 @@ time = %(time)d; qconnect(a.triggered, func) def onOptions(self) -> None: - self.mw.onDeckConf(self.mw.col.decks.get(self.card.current_deck_id())) + confirm_deck_then_display_options(self.card) def set_flag_on_current_card(self, desired_flag: int) -> None: def redraw_flag(out: OpChangesWithCount) -> None: