undoable ops now return changes directly; add new *_ops.py files
- Introduced a new transact() method that wraps the return value
in a separate struct that describes the changes that were made.
- Changes are now gathered from the undo log, so we don't need to
guess at what was changed - eg if update_note() is called with identical
note contents, no changes are returned. Card changes will only be set
if cards were actually generated by the update_note() call, and tag
will only be set if a new tag was added.
- mw.perform_op() has been updated to expect the op to return the changes,
or a structure with the changes in it, and it will use them to fire the
change hook, instead of fetching the changes from undo_status(), so there
is no risk of race conditions.
- the various calls to mw.perform_op() have been split into separate
files like card_ops.py. Aside from making the code cleaner, this works
around a rather annoying issue with mypy. Because we run it with
no_strict_optional, mypy is happy to accept an operation that returns None,
despite the type signature saying it requires changes to be returned.
Turning no_strict_optional on for the whole codebase is not practical
at the moment, but we can enable it for individual files.
Still todo:
- The cursor keeps moving back to the start of a field when typing -
we need to ignore the refresh hook when we are the initiator.
- The busy cursor icon should probably be delayed a few hundreds ms.
- Still need to think about a nicer way of handling saveNow()
- op_made_changes(), op_affects_study_queue() might be better embedded
as properties in the object instead
2021-03-16 05:26:42 +01:00
|
|
|
# Copyright: Ankitects Pty Ltd and contributors
|
|
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
2021-04-06 03:18:13 +02:00
|
|
|
from typing import Optional, Sequence
|
undoable ops now return changes directly; add new *_ops.py files
- Introduced a new transact() method that wraps the return value
in a separate struct that describes the changes that were made.
- Changes are now gathered from the undo log, so we don't need to
guess at what was changed - eg if update_note() is called with identical
note contents, no changes are returned. Card changes will only be set
if cards were actually generated by the update_note() call, and tag
will only be set if a new tag was added.
- mw.perform_op() has been updated to expect the op to return the changes,
or a structure with the changes in it, and it will use them to fire the
change hook, instead of fetching the changes from undo_status(), so there
is no risk of race conditions.
- the various calls to mw.perform_op() have been split into separate
files like card_ops.py. Aside from making the code cleaner, this works
around a rather annoying issue with mypy. Because we run it with
no_strict_optional, mypy is happy to accept an operation that returns None,
despite the type signature saying it requires changes to be returned.
Turning no_strict_optional on for the whole codebase is not practical
at the moment, but we can enable it for individual files.
Still todo:
- The cursor keeps moving back to the start of a field when typing -
we need to ignore the refresh hook when we are the initiator.
- The busy cursor icon should probably be delayed a few hundreds ms.
- Still need to think about a nicer way of handling saveNow()
- op_made_changes(), op_affects_study_queue() might be better embedded
as properties in the object instead
2021-03-16 05:26:42 +01:00
|
|
|
|
2021-04-06 06:36:13 +02:00
|
|
|
from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithId
|
2021-04-05 02:21:50 +02:00
|
|
|
from anki.decks import DeckCollapseScope, DeckId
|
2021-04-06 06:36:13 +02:00
|
|
|
from aqt import QWidget
|
|
|
|
from aqt.operations import CollectionOp
|
2021-03-22 14:17:07 +01:00
|
|
|
from aqt.utils import getOnlyText, tooltip, tr
|
undoable ops now return changes directly; add new *_ops.py files
- Introduced a new transact() method that wraps the return value
in a separate struct that describes the changes that were made.
- Changes are now gathered from the undo log, so we don't need to
guess at what was changed - eg if update_note() is called with identical
note contents, no changes are returned. Card changes will only be set
if cards were actually generated by the update_note() call, and tag
will only be set if a new tag was added.
- mw.perform_op() has been updated to expect the op to return the changes,
or a structure with the changes in it, and it will use them to fire the
change hook, instead of fetching the changes from undo_status(), so there
is no risk of race conditions.
- the various calls to mw.perform_op() have been split into separate
files like card_ops.py. Aside from making the code cleaner, this works
around a rather annoying issue with mypy. Because we run it with
no_strict_optional, mypy is happy to accept an operation that returns None,
despite the type signature saying it requires changes to be returned.
Turning no_strict_optional on for the whole codebase is not practical
at the moment, but we can enable it for individual files.
Still todo:
- The cursor keeps moving back to the start of a field when typing -
we need to ignore the refresh hook when we are the initiator.
- The busy cursor icon should probably be delayed a few hundreds ms.
- Still need to think about a nicer way of handling saveNow()
- op_made_changes(), op_affects_study_queue() might be better embedded
as properties in the object instead
2021-03-16 05:26:42 +01:00
|
|
|
|
|
|
|
|
|
|
|
def remove_decks(
|
|
|
|
*,
|
2021-03-17 05:51:59 +01:00
|
|
|
parent: QWidget,
|
2021-03-27 12:38:20 +01:00
|
|
|
deck_ids: Sequence[DeckId],
|
2021-04-06 06:36:13 +02:00
|
|
|
) -> CollectionOp[OpChangesWithCount]:
|
|
|
|
return CollectionOp(parent, lambda col: col.decks.remove(deck_ids)).success(
|
|
|
|
lambda out: tooltip(tr.browsing_cards_deleted(count=out.count), parent=parent)
|
undoable ops now return changes directly; add new *_ops.py files
- Introduced a new transact() method that wraps the return value
in a separate struct that describes the changes that were made.
- Changes are now gathered from the undo log, so we don't need to
guess at what was changed - eg if update_note() is called with identical
note contents, no changes are returned. Card changes will only be set
if cards were actually generated by the update_note() call, and tag
will only be set if a new tag was added.
- mw.perform_op() has been updated to expect the op to return the changes,
or a structure with the changes in it, and it will use them to fire the
change hook, instead of fetching the changes from undo_status(), so there
is no risk of race conditions.
- the various calls to mw.perform_op() have been split into separate
files like card_ops.py. Aside from making the code cleaner, this works
around a rather annoying issue with mypy. Because we run it with
no_strict_optional, mypy is happy to accept an operation that returns None,
despite the type signature saying it requires changes to be returned.
Turning no_strict_optional on for the whole codebase is not practical
at the moment, but we can enable it for individual files.
Still todo:
- The cursor keeps moving back to the start of a field when typing -
we need to ignore the refresh hook when we are the initiator.
- The busy cursor icon should probably be delayed a few hundreds ms.
- Still need to think about a nicer way of handling saveNow()
- op_made_changes(), op_affects_study_queue() might be better embedded
as properties in the object instead
2021-03-16 05:26:42 +01:00
|
|
|
)
|
2021-03-22 09:23:56 +01:00
|
|
|
|
|
|
|
|
|
|
|
def reparent_decks(
|
2021-04-06 06:36:13 +02:00
|
|
|
*, parent: QWidget, deck_ids: Sequence[DeckId], new_parent: DeckId
|
|
|
|
) -> CollectionOp[OpChangesWithCount]:
|
|
|
|
return CollectionOp(
|
|
|
|
parent, lambda col: col.decks.reparent(deck_ids=deck_ids, new_parent=new_parent)
|
|
|
|
).success(
|
|
|
|
lambda out: tooltip(
|
2021-03-26 05:21:04 +01:00
|
|
|
tr.browsing_reparented_decks(count=out.count), parent=parent
|
2021-04-06 06:36:13 +02:00
|
|
|
)
|
2021-03-22 09:23:56 +01:00
|
|
|
)
|
2021-03-22 11:38:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
def rename_deck(
|
|
|
|
*,
|
2021-04-06 06:36:13 +02:00
|
|
|
parent: QWidget,
|
2021-03-27 12:38:20 +01:00
|
|
|
deck_id: DeckId,
|
2021-03-22 11:38:51 +01:00
|
|
|
new_name: str,
|
2021-04-06 06:36:13 +02:00
|
|
|
) -> CollectionOp[OpChanges]:
|
|
|
|
return CollectionOp(
|
|
|
|
parent,
|
|
|
|
lambda col: col.decks.rename(deck_id, new_name),
|
2021-03-22 11:38:51 +01:00
|
|
|
)
|
2021-03-22 14:17:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
def add_deck_dialog(
|
|
|
|
*,
|
|
|
|
parent: QWidget,
|
|
|
|
default_text: str = "",
|
2021-04-06 06:36:13 +02:00
|
|
|
) -> Optional[CollectionOp[OpChangesWithId]]:
|
2021-03-22 14:17:07 +01:00
|
|
|
if name := getOnlyText(
|
2021-03-26 04:48:26 +01:00
|
|
|
tr.decks_new_deck_name(), default=default_text, parent=parent
|
2021-03-22 14:17:07 +01:00
|
|
|
).strip():
|
2021-04-06 06:36:13 +02:00
|
|
|
return add_deck(parent=parent, name=name)
|
|
|
|
else:
|
|
|
|
return None
|
2021-03-22 14:17:07 +01:00
|
|
|
|
|
|
|
|
2021-04-06 06:36:13 +02:00
|
|
|
def add_deck(*, parent: QWidget, name: str) -> CollectionOp[OpChangesWithId]:
|
|
|
|
return CollectionOp(parent, lambda col: col.decks.add_normal_deck_with_name(name))
|
2021-04-05 02:21:50 +02:00
|
|
|
|
|
|
|
|
|
|
|
def set_deck_collapsed(
|
2021-04-05 05:43:09 +02:00
|
|
|
*,
|
2021-04-06 06:36:13 +02:00
|
|
|
parent: QWidget,
|
2021-04-05 05:43:09 +02:00
|
|
|
deck_id: DeckId,
|
|
|
|
collapsed: bool,
|
|
|
|
scope: DeckCollapseScope.V,
|
2021-04-06 06:36:13 +02:00
|
|
|
) -> CollectionOp[OpChanges]:
|
|
|
|
return CollectionOp(
|
|
|
|
parent,
|
|
|
|
lambda col: col.decks.set_collapsed(
|
2021-04-05 02:21:50 +02:00
|
|
|
deck_id=deck_id, collapsed=collapsed, scope=scope
|
2021-04-05 05:43:09 +02:00
|
|
|
),
|
2021-04-05 02:21:50 +02:00
|
|
|
)
|
2021-04-06 13:37:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
def set_current_deck(*, parent: QWidget, deck_id: DeckId) -> CollectionOp[OpChanges]:
|
|
|
|
return CollectionOp(parent, lambda col: col.decks.set_current(deck_id))
|