mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 14:32:22 -04:00
If deck options are modified, ask before closing (#3410)
* If deck options are modified, ask before closing This imitates the way the note editor behaves. If a user assumes by error that chanhges are automatically saved, it ensures they won't lose them. Also, this will eventually allows to have the same feature on AnkiDroid. While, currently, we always ask the user whether they want to close the deck options, even when there are no modification, which seems to regularly frustate users (including myself). I'm new to Svelte, please let me know whether there is a better way to obtain the information from Svelte state that I missed. Note that I ensured that only a boolean can be obtained. I didn't cause the whole state to be accessible. May be useful for some add-ons, I guess, but risks breaking too much things. Regarding the deckoptions.py, I tried to imitate addcards.py way to check whether the add card view can be closed. Reusing the same function and variable name when possible. * Update qt/aqt/deckoptions.py (dae)
This commit is contained in:
parent
163c10191f
commit
b08e454f57
3 changed files with 56 additions and 0 deletions
|
@ -14,6 +14,7 @@ from aqt.qt import *
|
||||||
from aqt.utils import (
|
from aqt.utils import (
|
||||||
KeyboardModifiersPressed,
|
KeyboardModifiersPressed,
|
||||||
addCloseShortcut,
|
addCloseShortcut,
|
||||||
|
ask_user_dialog,
|
||||||
disable_help_button,
|
disable_help_button,
|
||||||
restoreGeom,
|
restoreGeom,
|
||||||
saveGeom,
|
saveGeom,
|
||||||
|
@ -33,6 +34,8 @@ class DeckOptionsDialog(QDialog):
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
self._deck = deck
|
self._deck = deck
|
||||||
self._setup_ui()
|
self._setup_ui()
|
||||||
|
self._close_event_has_cleaned_up = False
|
||||||
|
self._ready = False
|
||||||
|
|
||||||
def _setup_ui(self) -> None:
|
def _setup_ui(self) -> None:
|
||||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||||
|
@ -57,8 +60,44 @@ class DeckOptionsDialog(QDialog):
|
||||||
|
|
||||||
def _on_bridge_cmd(self, cmd: str) -> None:
|
def _on_bridge_cmd(self, cmd: str) -> None:
|
||||||
if cmd == "deckOptionsReady":
|
if cmd == "deckOptionsReady":
|
||||||
|
self.ready = True
|
||||||
gui_hooks.deck_options_did_load(self)
|
gui_hooks.deck_options_did_load(self)
|
||||||
|
|
||||||
|
def closeEvent(self, evt: QCloseEvent) -> None:
|
||||||
|
if self._close_event_has_cleaned_up:
|
||||||
|
evt.accept()
|
||||||
|
return
|
||||||
|
evt.ignore()
|
||||||
|
self.if_can_close()
|
||||||
|
|
||||||
|
def _close(self):
|
||||||
|
"""Close. Ensure the closeEvent is not ignored."""
|
||||||
|
self._close_event_has_cleaned_up = True
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def if_can_close(self):
|
||||||
|
"""Close if there was no modification. Otherwise ask for confirmation first."""
|
||||||
|
|
||||||
|
def callbackWithUserChoice(choice: int):
|
||||||
|
if choice == 0:
|
||||||
|
# The user accepted to discard current input.
|
||||||
|
self._close()
|
||||||
|
|
||||||
|
def if_can_close_callback_with_data_information(has_modified_dataData: bool):
|
||||||
|
if has_modified_dataData:
|
||||||
|
ask_user_dialog(
|
||||||
|
tr.card_templates_discard_changes(),
|
||||||
|
callback=callbackWithUserChoice,
|
||||||
|
buttons=[
|
||||||
|
QMessageBox.StandardButton.Discard,
|
||||||
|
(tr.adding_keep_editing(), QMessageBox.ButtonRole.RejectRole),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._close()
|
||||||
|
|
||||||
|
self.has_modified_data(if_can_close_callback_with_data_information)
|
||||||
|
|
||||||
def reject(self) -> None:
|
def reject(self) -> None:
|
||||||
self.mw.col.set_wants_abort()
|
self.mw.col.set_wants_abort()
|
||||||
self.web.cleanup()
|
self.web.cleanup()
|
||||||
|
@ -66,6 +105,15 @@ class DeckOptionsDialog(QDialog):
|
||||||
saveGeom(self, self.TITLE)
|
saveGeom(self, self.TITLE)
|
||||||
QDialog.reject(self)
|
QDialog.reject(self)
|
||||||
|
|
||||||
|
def has_modified_data(self, callback: Callable[[bool], None]):
|
||||||
|
"""Calls `callback` with the information of whether any deck options are modified."""
|
||||||
|
if self.ready:
|
||||||
|
self.web.evalWithCallback(
|
||||||
|
"anki.deckOptionsPendingChanges()", callback
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
callback(False)
|
||||||
|
|
||||||
|
|
||||||
def confirm_deck_then_display_options(active_card: Card | None = None) -> None:
|
def confirm_deck_then_display_options(active_card: Card | None = None) -> None:
|
||||||
decks = [aqt.mw.col.decks.current()]
|
decks = [aqt.mw.col.decks.current()]
|
||||||
|
|
|
@ -11,6 +11,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
let page: DeckOptionsPage;
|
let page: DeckOptionsPage;
|
||||||
|
|
||||||
|
globalThis.anki ||= {};
|
||||||
|
globalThis.anki.deckOptionsPendingChanges = () => {
|
||||||
|
return data.state.isModified();
|
||||||
|
};
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
globalThis.$deckOptions = new Promise((resolve, _reject) => {
|
globalThis.$deckOptions = new Promise((resolve, _reject) => {
|
||||||
resolve(page);
|
resolve(page);
|
||||||
|
|
|
@ -292,6 +292,10 @@ export class DeckOptionsState {
|
||||||
});
|
});
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isModified(): boolean {
|
||||||
|
return this.removedConfigs.length > 0 || this.modifiedConfigs.size > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bytesToObject(bytes: Uint8Array): Record<string, unknown> {
|
function bytesToObject(bytes: Uint8Array): Record<string, unknown> {
|
||||||
|
|
Loading…
Reference in a new issue