Edit dialog: make browser button show all history
Before, pressing the Browse button would only show browser with the cards or notes corresponding to the currently edited note. Now, it shows all cards or notes from the dialog history, in reverse order (last seen on top), with the currently edited note or its cards selected.
This commit is contained in:
parent
849ab43be7
commit
c688895c0e
@ -1629,7 +1629,7 @@ class AnkiConnect:
|
|||||||
# when run inside Anki, `__name__` would be either numeric,
|
# when run inside Anki, `__name__` would be either numeric,
|
||||||
# or, if installed via `link.sh`, `AnkiConnectDev`
|
# or, if installed via `link.sh`, `AnkiConnectDev`
|
||||||
if __name__ != "plugin":
|
if __name__ != "plugin":
|
||||||
Edit.register_with_dialog_manager()
|
Edit.register_with_anki()
|
||||||
|
|
||||||
ac = AnkiConnect()
|
ac = AnkiConnect()
|
||||||
ac.initLogging()
|
ac.initLogging()
|
||||||
|
@ -2,7 +2,6 @@ import aqt
|
|||||||
import aqt.editor
|
import aqt.editor
|
||||||
from aqt import gui_hooks
|
from aqt import gui_hooks
|
||||||
from aqt.qt import QDialog, Qt, QKeySequence, QShortcut
|
from aqt.qt import QDialog, Qt, QKeySequence, QShortcut
|
||||||
from aqt.browser.previewer import MultiCardPreviewer
|
|
||||||
from aqt.utils import disable_help_button, restoreGeom, saveGeom, tooltip
|
from aqt.utils import disable_help_button, restoreGeom, saveGeom, tooltip
|
||||||
from anki.errors import NotFoundError
|
from anki.errors import NotFoundError
|
||||||
from anki.consts import QUEUE_TYPE_SUSPENDED
|
from anki.consts import QUEUE_TYPE_SUSPENDED
|
||||||
@ -11,13 +10,13 @@ from anki.utils import ids2str
|
|||||||
|
|
||||||
# Edit dialog. Like Edit Current, but:
|
# Edit dialog. Like Edit Current, but:
|
||||||
# * has a Preview button to preview the cards for the note
|
# * has a Preview button to preview the cards for the note
|
||||||
# * has a Browse button to open the browser with these cards
|
|
||||||
# * has Previous/Back buttons to navigate the history of the dialog
|
# * has Previous/Back buttons to navigate the history of the dialog
|
||||||
|
# * has a Browse button to open the history in the Browser
|
||||||
# * has no bar with the Close button
|
# * has no bar with the Close button
|
||||||
#
|
#
|
||||||
# To register in Anki's dialog system:
|
# To register in Anki's dialog system:
|
||||||
# > from .edit import Edit
|
# > from .edit import Edit
|
||||||
# > Edit.register_with_dialog_manager()
|
# > Edit.register_with_anki()
|
||||||
#
|
#
|
||||||
# To (re)open (note_id is an integer):
|
# To (re)open (note_id is an integer):
|
||||||
# > Edit.open_dialog_and_show_note_with_id(note_id)
|
# > Edit.open_dialog_and_show_note_with_id(note_id)
|
||||||
@ -87,7 +86,7 @@ class DecentPreviewer(aqt.browser.previewer.MultiCardPreviewer):
|
|||||||
self.adapter.can_select_next_card()
|
self.adapter.can_select_next_card()
|
||||||
|
|
||||||
def _render_scheduled(self):
|
def _render_scheduled(self):
|
||||||
super()._render_scheduled()
|
super()._render_scheduled() # noqa
|
||||||
self._updateButtons()
|
self._updateButtons()
|
||||||
|
|
||||||
def showing_answer_and_can_show_question(self):
|
def showing_answer_and_can_show_question(self):
|
||||||
@ -157,6 +156,21 @@ class History:
|
|||||||
history = History()
|
history = History()
|
||||||
|
|
||||||
|
|
||||||
|
# see method `find_cards` of `collection.py`
|
||||||
|
def trigger_search_for_dialog_history_notes(search_context, use_history_order):
|
||||||
|
search_context.search = " or ".join(
|
||||||
|
f"nid:{note_id}" for note_id in history.note_ids
|
||||||
|
)
|
||||||
|
|
||||||
|
if use_history_order:
|
||||||
|
search_context.order = f"""case c.nid {
|
||||||
|
" ".join(
|
||||||
|
f"when {note_id} then {n}"
|
||||||
|
for (n, note_id) in enumerate(reversed(history.note_ids))
|
||||||
|
)
|
||||||
|
} end asc"""
|
||||||
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
||||||
@ -164,6 +178,7 @@ history = History()
|
|||||||
class Edit(aqt.editcurrent.EditCurrent):
|
class Edit(aqt.editcurrent.EditCurrent):
|
||||||
dialog_geometry_tag = DOMAIN_PREFIX + "edit"
|
dialog_geometry_tag = DOMAIN_PREFIX + "edit"
|
||||||
dialog_registry_tag = DOMAIN_PREFIX + "Edit"
|
dialog_registry_tag = DOMAIN_PREFIX + "Edit"
|
||||||
|
dialog_search_tag = DOMAIN_PREFIX + "edit.history"
|
||||||
|
|
||||||
# depending on whether the dialog already exists,
|
# depending on whether the dialog already exists,
|
||||||
# upon a request to open the dialog via `aqt.dialogs.open()`,
|
# upon a request to open the dialog via `aqt.dialogs.open()`,
|
||||||
@ -245,13 +260,26 @@ class Edit(aqt.editcurrent.EditCurrent):
|
|||||||
|
|
||||||
################################################################## actions
|
################################################################## actions
|
||||||
|
|
||||||
|
# search two times, one is to select the current note or its cards,
|
||||||
|
# and another to show the whole history, while keeping the above selection
|
||||||
|
# set sort column to our search tag, which:
|
||||||
|
# * prevents the column sort indicator from being shown
|
||||||
|
# * serves as a hint for us to show notes or cards in history order
|
||||||
|
# (user can then click on any of the column names
|
||||||
|
# to show history cards in the order of their choosing)
|
||||||
def show_browser(self, *_):
|
def show_browser(self, *_):
|
||||||
def search_input_select_all(browser, *_):
|
def search_input_select_all(hook_browser, *_):
|
||||||
browser.form.searchEdit.lineEdit().selectAll()
|
hook_browser.form.searchEdit.lineEdit().selectAll()
|
||||||
gui_hooks.browser_did_change_row.remove(search_input_select_all)
|
gui_hooks.browser_did_change_row.remove(search_input_select_all)
|
||||||
|
|
||||||
gui_hooks.browser_did_change_row.append(search_input_select_all)
|
gui_hooks.browser_did_change_row.append(search_input_select_all)
|
||||||
aqt.dialogs.open("Browser", aqt.mw, search=(f"nid:{self.note.id}",))
|
|
||||||
|
browser = aqt.dialogs.open("Browser", aqt.mw)
|
||||||
|
browser.table._state.sort_column = self.dialog_search_tag # noqa
|
||||||
|
browser.table._set_sort_indicator() # noqa
|
||||||
|
|
||||||
|
browser.search_for(f"nid:{self.note.id}")
|
||||||
|
browser.table.select_all()
|
||||||
|
browser.search_for(self.dialog_search_tag)
|
||||||
|
|
||||||
def show_preview(self, *_):
|
def show_preview(self, *_):
|
||||||
if cards := self.note.cards():
|
if cards := self.note.cards():
|
||||||
@ -339,8 +367,19 @@ class Edit(aqt.editcurrent.EditCurrent):
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_with_dialog_manager(cls):
|
def browser_will_search(cls, search_context):
|
||||||
|
if search_context.search == cls.dialog_search_tag:
|
||||||
|
trigger_search_for_dialog_history_notes(
|
||||||
|
search_context=search_context,
|
||||||
|
use_history_order=cls.dialog_search_tag ==
|
||||||
|
search_context.browser.table._state.sort_column # noqa
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register_with_anki(cls):
|
||||||
|
if cls.dialog_registry_tag not in aqt.dialogs._dialogs: # noqa
|
||||||
aqt.dialogs.register_dialog(cls.dialog_registry_tag, cls)
|
aqt.dialogs.register_dialog(cls.dialog_registry_tag, cls)
|
||||||
|
gui_hooks.browser_will_search.append(cls.browser_will_search)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def open_dialog_and_show_note_with_id(cls, note_id): # raises NotFoundError
|
def open_dialog_and_show_note_with_id(cls, note_id): # raises NotFoundError
|
||||||
|
@ -44,6 +44,10 @@ def close_all_dialogs_and_wait_for_them_to_run_closing_callbacks():
|
|||||||
wait_until(aqt.dialogs.allClosed)
|
wait_until(aqt.dialogs.allClosed)
|
||||||
|
|
||||||
|
|
||||||
|
def get_dialog_instance(name):
|
||||||
|
return aqt.dialogs._dialogs[name][1] # noqa
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def empty_anki_session_started():
|
def empty_anki_session_started():
|
||||||
with anki_running(
|
with anki_running(
|
||||||
@ -98,6 +102,8 @@ class Setup:
|
|||||||
deck_id: int
|
deck_id: int
|
||||||
note1_id: int
|
note1_id: int
|
||||||
note2_id: int
|
note2_id: int
|
||||||
|
note1_card_ids: "list[int]"
|
||||||
|
note2_card_ids: "list[int]"
|
||||||
card_ids: "list[int]"
|
card_ids: "list[int]"
|
||||||
|
|
||||||
|
|
||||||
@ -128,12 +134,16 @@ def set_up_test_deck_and_test_model_and_two_notes():
|
|||||||
tags={"tag2"},
|
tags={"tag2"},
|
||||||
))
|
))
|
||||||
|
|
||||||
|
note1_card_ids = ac.findCards(query=f"nid:{note1_id}")
|
||||||
|
note2_card_ids = ac.findCards(query=f"nid:{note2_id}")
|
||||||
card_ids = ac.findCards(query="deck:test_deck")
|
card_ids = ac.findCards(query="deck:test_deck")
|
||||||
|
|
||||||
return Setup(
|
return Setup(
|
||||||
deck_id=deck_id,
|
deck_id=deck_id,
|
||||||
note1_id=note1_id,
|
note1_id=note1_id,
|
||||||
note2_id=note2_id,
|
note2_id=note2_id,
|
||||||
|
note1_card_ids=note1_card_ids,
|
||||||
|
note2_card_ids=note2_card_ids,
|
||||||
card_ids=card_ids,
|
card_ids=card_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -239,6 +249,6 @@ def setup(session_with_profile_loaded):
|
|||||||
* Edit dialog is registered with dialog manager
|
* Edit dialog is registered with dialog manager
|
||||||
* Any dialogs, if open, are safely closed on exit
|
* Any dialogs, if open, are safely closed on exit
|
||||||
"""
|
"""
|
||||||
Edit.register_with_dialog_manager()
|
Edit.register_with_anki()
|
||||||
yield set_up_test_deck_and_test_model_and_two_notes()
|
yield set_up_test_deck_and_test_model_and_two_notes()
|
||||||
close_all_dialogs_and_wait_for_them_to_run_closing_callbacks()
|
close_all_dialogs_and_wait_for_them_to_run_closing_callbacks()
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
|
||||||
import aqt.operations.note
|
import aqt.operations.note
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from conftest import get_dialog_instance
|
||||||
from plugin.edit import Edit, DecentPreviewer, history
|
from plugin.edit import Edit, DecentPreviewer, history
|
||||||
|
|
||||||
|
|
||||||
@ -18,10 +20,40 @@ def test_edit_dialog_fails_to_open_with_invalid_note(setup):
|
|||||||
Edit.open_dialog_and_show_note_with_id(123)
|
Edit.open_dialog_and_show_note_with_id(123)
|
||||||
|
|
||||||
|
|
||||||
def test_browser_dialog_opens(setup):
|
class TestBrowser:
|
||||||
|
@staticmethod
|
||||||
|
def get_selected_card_ids():
|
||||||
|
return get_dialog_instance("Browser").table.get_selected_card_ids()
|
||||||
|
|
||||||
|
def test_dialog_opens(self, setup):
|
||||||
dialog = Edit.open_dialog_and_show_note_with_id(setup.note1_id)
|
dialog = Edit.open_dialog_and_show_note_with_id(setup.note1_id)
|
||||||
dialog.show_browser()
|
dialog.show_browser()
|
||||||
|
|
||||||
|
def test_selects_cards_of_last_note(self, setup):
|
||||||
|
Edit.open_dialog_and_show_note_with_id(setup.note1_id)
|
||||||
|
Edit.open_dialog_and_show_note_with_id(setup.note2_id).show_browser()
|
||||||
|
|
||||||
|
assert {*self.get_selected_card_ids()} == {*setup.note2_card_ids}
|
||||||
|
|
||||||
|
def test_selects_cards_of_note_before_last_after_previous_button_pressed(self, setup):
|
||||||
|
Edit.open_dialog_and_show_note_with_id(setup.note1_id)
|
||||||
|
dialog = Edit.open_dialog_and_show_note_with_id(setup.note2_id)
|
||||||
|
|
||||||
|
def verify_that_the_table_shows_note2_cards_then_note1_cards():
|
||||||
|
get_dialog_instance("Browser").table.select_all()
|
||||||
|
assert {*self.get_selected_card_ids()[:2]} == {*setup.note2_card_ids}
|
||||||
|
assert {*self.get_selected_card_ids()[2:]} == {*setup.note1_card_ids}
|
||||||
|
|
||||||
|
dialog.show_previous()
|
||||||
|
dialog.show_browser()
|
||||||
|
assert {*self.get_selected_card_ids()} == {*setup.note1_card_ids}
|
||||||
|
verify_that_the_table_shows_note2_cards_then_note1_cards()
|
||||||
|
|
||||||
|
dialog.show_next()
|
||||||
|
dialog.show_browser()
|
||||||
|
assert {*self.get_selected_card_ids()} == {*setup.note2_card_ids}
|
||||||
|
verify_that_the_table_shows_note2_cards_then_note1_cards()
|
||||||
|
|
||||||
|
|
||||||
class TestPreviewDialog:
|
class TestPreviewDialog:
|
||||||
def test_opens(self, setup):
|
def test_opens(self, setup):
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import aqt
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from conftest import ac, wait, wait_until, \
|
from conftest import ac, wait_until, \
|
||||||
close_all_dialogs_and_wait_for_them_to_run_closing_callbacks
|
close_all_dialogs_and_wait_for_them_to_run_closing_callbacks, \
|
||||||
|
get_dialog_instance
|
||||||
|
|
||||||
|
|
||||||
def test_guiBrowse(setup):
|
def test_guiBrowse(setup):
|
||||||
@ -52,7 +52,7 @@ class TestAddCards:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def click_on_add_card_dialog_save_button():
|
def click_on_add_card_dialog_save_button():
|
||||||
dialog = aqt.dialogs._dialogs["AddCards"][1]
|
dialog = get_dialog_instance("AddCards")
|
||||||
dialog.addButton.click()
|
dialog.addButton.click()
|
||||||
|
|
||||||
# todo previously, these tests were verifying
|
# todo previously, these tests were verifying
|
||||||
|
Loading…
Reference in New Issue
Block a user