1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
import threading
from typing import Callable, Sequence
from gi.repository import Adw, Gtk
import inject
from cozy.model.book import Book
from cozy.ui.headerbar import Headerbar
from cozy.ui.widgets.book_row import BookRow
from cozy.ui.widgets.search_results import ArtistResultRow
from cozy.view_model.search_view_model import SearchViewModel
@Gtk.Template.from_resource("/com/github/geigi/cozy/search_page.ui")
class SearchView(Adw.Bin):
__gtype_name__ = "SearchView"
stack: Gtk.Stack = Gtk.Template.Child()
search_scroller: Gtk.ScrolledWindow = Gtk.Template.Child()
start_searching_page: Adw.StatusPage = Gtk.Template.Child()
nothing_found_page: Adw.StatusPage = Gtk.Template.Child()
book_result_box: Adw.PreferencesGroup = Gtk.Template.Child()
author_result_box: Adw.PreferencesGroup = Gtk.Template.Child()
reader_result_box: Adw.PreferencesGroup = Gtk.Template.Child()
book_result_list: Gtk.ListBox = Gtk.Template.Child()
author_result_list: Gtk.ListBox = Gtk.Template.Child()
reader_result_list: Gtk.ListBox = Gtk.Template.Child()
view_model = inject.attr(SearchViewModel)
main_view = inject.attr("MainWindow")
search_thread: threading.Thread
def __init__(self, main_window_builder: Gtk.Builder, headerbar: Headerbar) -> None:
super().__init__()
self.library_stack: Gtk.Stack = main_window_builder.get_object("library_stack")
self.library_stack.add_child(self)
self.split_view: Gtk.Stack = main_window_builder.get_object("split_view")
self.search_bar = headerbar.search_bar
self.entry = headerbar.search_entry
self.entry.connect("search-changed", self._on_search_changed)
self.search_thread = threading.Thread(target=self.view_model.search)
self.view_model.bind_to("close", self.close)
self.main_view.create_action("search", self.open, ["<primary>f"])
def open(self, *_) -> None:
self.library_stack.set_visible_child(self)
self.search_bar.set_search_mode(True)
self.main_view.play_pause_action.set_enabled(False)
def close(self) -> None:
self.library_stack.set_visible_child(self.split_view)
self.search_bar.set_search_mode(False)
self.main_view.play_pause_action.set_enabled(True)
def on_state_changed(self, widget: Gtk.Widget, param) -> None:
if widget.get_property(param.name):
self.open()
else:
self.close()
def _on_search_changed(self, _) -> None:
search_query = self.entry.get_text()
if not search_query:
self.stack.set_visible_child(self.start_searching_page)
return
if self.search_thread.is_alive():
self.search_thread.join(timeout=0.1)
self.search_thread = threading.Thread(
target=self.view_model.search, args=(search_query, self._display_results)
)
self.search_thread.start()
def _display_results(self, books: list[Book], authors: list[str], readers: list[str]) -> None:
if not any((books, authors, readers)):
self.stack.set_visible_child(self.nothing_found_page)
return
self.stack.set_visible_child(self.search_scroller)
self._populate_listbox(
books, self.book_result_list, self.book_result_box, self.view_model.jump_to_book
)
self._populate_listbox(
authors, self.author_result_list, self.author_result_box, self.view_model.jump_to_author
)
self._populate_listbox(
readers, self.reader_result_list, self.reader_result_box, self.view_model.jump_to_reader
)
def _populate_listbox(
self,
results: Sequence[str | Book],
listbox: Gtk.ListBox,
box: Adw.PreferencesGroup,
callback: Callable[[str | Book], None],
) -> None:
box.set_visible(False)
listbox.remove_all()
if not results:
return
if isinstance(results[0], Book):
row_type = BookRow
else:
row_type = ArtistResultRow
for result in results:
listbox.append(row_type(result, callback))
box.set_visible(True)
|