WIP
This commit is contained in:
@@ -15,7 +15,7 @@ from ..adapters import (
|
|||||||
)
|
)
|
||||||
from ..config import AppConfiguration
|
from ..config import AppConfiguration
|
||||||
from ..ui import util
|
from ..ui import util
|
||||||
from ..ui.common import AlbumWithSongs, IconButton, LoadError, SpinnerImage
|
from ..ui.common import AlbumWithSongs, IconButton, LoadError, SpinnerImage, Sizer
|
||||||
|
|
||||||
|
|
||||||
def _to_type(query_type: AlbumSearchQuery.Type) -> str:
|
def _to_type(query_type: AlbumSearchQuery.Type) -> str:
|
||||||
@@ -46,7 +46,7 @@ def _from_str(type_str: str) -> AlbumSearchQuery.Type:
|
|||||||
}[type_str]
|
}[type_str]
|
||||||
|
|
||||||
|
|
||||||
class AlbumsPanel(Gtk.Box):
|
class AlbumsPanel(Handy.Leaflet):
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
"song-clicked": (
|
"song-clicked": (
|
||||||
GObject.SignalFlags.RUN_FIRST,
|
GObject.SignalFlags.RUN_FIRST,
|
||||||
@@ -60,18 +60,25 @@ class AlbumsPanel(Gtk.Box):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current_query: AlbumSearchQuery = AlbumSearchQuery(AlbumSearchQuery.Type.RANDOM)
|
||||||
offline_mode = False
|
offline_mode = False
|
||||||
|
provider_id: Optional[str] = None
|
||||||
|
|
||||||
populating_genre_combo = False
|
populating_genre_combo = False
|
||||||
grid_order_token: int = 0
|
|
||||||
album_sort_direction: str = "ascending"
|
album_sort_direction: str = "ascending"
|
||||||
album_page_size: int = 30
|
# album_page_size: int = 30
|
||||||
album_page: int = 0
|
album_page: int = 0
|
||||||
grid_pages_count: int = 0
|
grid_pages_count: int = 0
|
||||||
|
|
||||||
def __init__(self):
|
current_albums_result: Result = None
|
||||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
|
||||||
|
|
||||||
leaflet = Handy.Leaflet(transition_type=Handy.LeafletTransitionType.SLIDE, can_swipe_forward=False)
|
albums = []
|
||||||
|
albums_by_id = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(transition_type=Handy.LeafletTransitionType.SLIDE, can_swipe_forward=False, interpolate_size=False)
|
||||||
|
|
||||||
|
self.grid_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
actionbar = Gtk.ActionBar()
|
actionbar = Gtk.ActionBar()
|
||||||
|
|
||||||
@@ -123,6 +130,57 @@ class AlbumsPanel(Gtk.Box):
|
|||||||
self.sort_toggle.connect("clicked", self.on_sort_toggle_clicked)
|
self.sort_toggle.connect("clicked", self.on_sort_toggle_clicked)
|
||||||
actionbar.pack_start(self.sort_toggle)
|
actionbar.pack_start(self.sort_toggle)
|
||||||
|
|
||||||
|
self.refresh_button = IconButton(
|
||||||
|
"view-refresh-symbolic", "Refresh list of albums", relief=True
|
||||||
|
)
|
||||||
|
self.refresh_button.connect("clicked", self.on_refresh_clicked)
|
||||||
|
actionbar.pack_end(self.refresh_button)
|
||||||
|
|
||||||
|
# actionbar.pack_end(Gtk.Label(label="albums per page"))
|
||||||
|
# self.show_count_dropdown, _ = self.make_combobox(
|
||||||
|
# ((x, x, True) for x in ("20", "30", "40", "50")),
|
||||||
|
# self.on_show_count_dropdown_change,
|
||||||
|
# )
|
||||||
|
# actionbar.pack_end(self.show_count_dropdown)
|
||||||
|
# actionbar.pack_end(Gtk.Label(label="Show"))
|
||||||
|
|
||||||
|
self.grid_box.pack_start(actionbar, False, False, 0)
|
||||||
|
|
||||||
|
# 700 shows ~3 albums
|
||||||
|
grid_sizer = Sizer(natural_width=700)
|
||||||
|
|
||||||
|
scrolled_window = Gtk.ScrolledWindow(
|
||||||
|
hscrollbar_policy=Gtk.PolicyType.NEVER)
|
||||||
|
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
self.grid = Gtk.FlowBox(
|
||||||
|
hexpand=True,
|
||||||
|
margin_top=5,
|
||||||
|
row_spacing=5,
|
||||||
|
column_spacing=5,
|
||||||
|
homogeneous=True,
|
||||||
|
max_children_per_line=999,
|
||||||
|
valign=Gtk.Align.START,
|
||||||
|
halign=Gtk.Align.CENTER,
|
||||||
|
selection_mode=Gtk.SelectionMode.SINGLE)
|
||||||
|
self.grid.connect("child-activated", self.on_album_clicked)
|
||||||
|
|
||||||
|
# self.grid.connect(
|
||||||
|
# "song-clicked",
|
||||||
|
# lambda _, *args: self.emit("song-clicked", *args),
|
||||||
|
# )
|
||||||
|
# self.grid.connect(
|
||||||
|
# "refresh-window",
|
||||||
|
# lambda _, *args: self.emit("refresh-window", *args),
|
||||||
|
# )
|
||||||
|
# self.grid.connect("cover-clicked", self.on_album_clicked)
|
||||||
|
# self.grid.connect("num-pages-changed", self.on_grid_num_pages_changed)
|
||||||
|
|
||||||
|
box.add(self.grid)
|
||||||
|
|
||||||
|
bottom_actionbar = Gtk.ActionBar()
|
||||||
|
|
||||||
# Add the page widget.
|
# Add the page widget.
|
||||||
page_widget = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
page_widget = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||||
self.prev_page = IconButton(
|
self.prev_page = IconButton(
|
||||||
@@ -145,39 +203,46 @@ class AlbumsPanel(Gtk.Box):
|
|||||||
)
|
)
|
||||||
self.next_page.connect("clicked", self.on_next_page_clicked)
|
self.next_page.connect("clicked", self.on_next_page_clicked)
|
||||||
page_widget.add(self.next_page)
|
page_widget.add(self.next_page)
|
||||||
actionbar.set_center_widget(page_widget)
|
bottom_actionbar.set_center_widget(page_widget)
|
||||||
|
|
||||||
self.refresh_button = IconButton(
|
box.add(bottom_actionbar)
|
||||||
"view-refresh-symbolic", "Refresh list of albums", relief=True
|
|
||||||
)
|
|
||||||
self.refresh_button.connect("clicked", self.on_refresh_clicked)
|
|
||||||
actionbar.pack_end(self.refresh_button)
|
|
||||||
|
|
||||||
actionbar.pack_end(Gtk.Label(label="albums per page"))
|
scrolled_window.add(box)
|
||||||
self.show_count_dropdown, _ = self.make_combobox(
|
grid_sizer.add(scrolled_window)
|
||||||
((x, x, True) for x in ("20", "30", "40", "50")),
|
self.grid_box.pack_start(grid_sizer, True, True, 0)
|
||||||
self.on_show_count_dropdown_change,
|
|
||||||
)
|
|
||||||
actionbar.pack_end(self.show_count_dropdown)
|
|
||||||
actionbar.pack_end(Gtk.Label(label="Show"))
|
|
||||||
|
|
||||||
# self.add(actionbar)
|
self.add(self.grid_box)
|
||||||
leaflet.add(actionbar)
|
|
||||||
|
self.details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
self.details_box.get_style_context().add_class("details-panel")
|
||||||
|
|
||||||
|
details_top_bar_revealer = Gtk.Revealer(reveal_child=False)
|
||||||
|
details_top_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
||||||
|
back_button = IconButton("go-previous-symbolic")
|
||||||
|
|
||||||
|
def back_clicked(*_):
|
||||||
|
self.set_visible_child(self.grid_box)
|
||||||
|
back_button.connect("clicked", back_clicked)
|
||||||
|
|
||||||
|
details_top_bar.pack_start(back_button, False, False, 0)
|
||||||
|
|
||||||
|
details_top_bar_revealer.add(details_top_bar)
|
||||||
|
|
||||||
|
self.details_box.pack_start(details_top_bar_revealer, False, False, 0)
|
||||||
|
|
||||||
|
self.album_with_songs = AlbumWithSongs(cover_art_size=100)
|
||||||
|
self.details_box.pack_start(self.album_with_songs, True, True, 0)
|
||||||
|
|
||||||
|
self.add(self.details_box)
|
||||||
|
|
||||||
|
def folded_changed(*_):
|
||||||
|
if not self.get_folded():
|
||||||
|
self.set_visible_child(self.grid_box)
|
||||||
|
|
||||||
|
details_top_bar_revealer.set_reveal_child(self.get_folded())
|
||||||
|
self.connect("notify::folded", folded_changed)
|
||||||
|
|
||||||
scrolled_window = Gtk.ScrolledWindow()
|
|
||||||
self.grid = AlbumsGrid()
|
|
||||||
self.grid.connect(
|
|
||||||
"song-clicked",
|
|
||||||
lambda _, *args: self.emit("song-clicked", *args),
|
|
||||||
)
|
|
||||||
self.grid.connect(
|
|
||||||
"refresh-window",
|
|
||||||
lambda _, *args: self.emit("refresh-window", *args),
|
|
||||||
)
|
|
||||||
self.grid.connect("cover-clicked", self.on_grid_cover_clicked)
|
|
||||||
self.grid.connect("num-pages-changed", self.on_grid_num_pages_changed)
|
|
||||||
scrolled_window.add(self.grid)
|
|
||||||
self.add(scrolled_window)
|
|
||||||
|
|
||||||
def make_combobox(
|
def make_combobox(
|
||||||
self,
|
self,
|
||||||
@@ -234,7 +299,7 @@ class AlbumsPanel(Gtk.Box):
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.updating_query = False
|
self.updating_query = False
|
||||||
|
|
||||||
def update(self, app_config: AppConfiguration = None, force: bool = False):
|
def update(self, app_config: AppConfiguration, force: bool = False):
|
||||||
self.updating_query = True
|
self.updating_query = True
|
||||||
|
|
||||||
supported_type_strings = {
|
supported_type_strings = {
|
||||||
@@ -246,9 +311,38 @@ class AlbumsPanel(Gtk.Box):
|
|||||||
# (En|Dis)able getting genres.
|
# (En|Dis)able getting genres.
|
||||||
self.sort_type_combo_store[1][2] = AdapterManager.can_get_genres()
|
self.sort_type_combo_store[1][2] = AdapterManager.can_get_genres()
|
||||||
|
|
||||||
if app_config:
|
if (self.current_query != app_config.state.current_album_search_query
|
||||||
|
or self.offline_mode != app_config.offline_mode
|
||||||
|
or self.provider_id != app_config.current_provider_id):
|
||||||
|
|
||||||
self.current_query = app_config.state.current_album_search_query
|
self.current_query = app_config.state.current_album_search_query
|
||||||
self.offline_mode = app_config.offline_mode
|
self.offline_mode = app_config.offline_mode
|
||||||
|
self.provider_id = app_config.current_provider_id
|
||||||
|
|
||||||
|
if self.current_albums_result is not None:
|
||||||
|
self.current_albums_result.cancel()
|
||||||
|
|
||||||
|
self.current_albums_result = AdapterManager.get_albums(
|
||||||
|
self.current_query, use_ground_truth_adapter=force)
|
||||||
|
|
||||||
|
if self.current_albums_result.data_is_available:
|
||||||
|
# Don't idle add if the data is already available.
|
||||||
|
self.current_albums_result.add_done_callback(self._albums_loaded)
|
||||||
|
else:
|
||||||
|
# self.spinner.show()
|
||||||
|
self.current_albums_result.add_done_callback(
|
||||||
|
lambda f: GLib.idle_add(self._albums_received, f)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (self.album_sort_direction != app_config.state.album_sort_direction
|
||||||
|
or self.album_page != app_config.state.album_page):
|
||||||
|
|
||||||
|
self.album_sort_direction = app_config.state.album_sort_direction
|
||||||
|
self.album_page = app_config.state.album_page
|
||||||
|
|
||||||
|
self._update_albums()
|
||||||
|
# self.current_query = app_config.state.current_album_search_query
|
||||||
|
# self.offline_mode = app_config.offline_mode
|
||||||
|
|
||||||
self.alphabetical_type_combo.set_active_id(
|
self.alphabetical_type_combo.set_active_id(
|
||||||
{
|
{
|
||||||
@@ -266,7 +360,6 @@ class AlbumsPanel(Gtk.Box):
|
|||||||
# Update the page display
|
# Update the page display
|
||||||
if app_config:
|
if app_config:
|
||||||
self.album_page = app_config.state.album_page
|
self.album_page = app_config.state.album_page
|
||||||
self.album_page_size = app_config.state.album_page_size
|
|
||||||
self.refresh_button.set_sensitive(not app_config.offline_mode)
|
self.refresh_button.set_sensitive(not app_config.offline_mode)
|
||||||
|
|
||||||
self.prev_page.set_sensitive(self.album_page > 0)
|
self.prev_page.set_sensitive(self.album_page > 0)
|
||||||
@@ -309,16 +402,93 @@ class AlbumsPanel(Gtk.Box):
|
|||||||
+ self._get_opposite_sort_dir(self.album_sort_direction)
|
+ self._get_opposite_sort_dir(self.album_sort_direction)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.show_count_dropdown.set_active_id(
|
|
||||||
str(app_config.state.album_page_size)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Has to be last because it resets self.updating_query
|
# Has to be last because it resets self.updating_query
|
||||||
self.populate_genre_combo(app_config, force=force)
|
self.populate_genre_combo(app_config, force=force)
|
||||||
|
|
||||||
# At this point, the current query should be totally updated.
|
selected_album = self.albums_by_id.get(app_config.state.selected_album_id, None)
|
||||||
self.grid_order_token = self.grid.update_params(app_config)
|
self.album_with_songs.update(selected_album, app_config, force=force)
|
||||||
self.grid.update(self.grid_order_token, app_config, force=force)
|
|
||||||
|
def _albums_loaded(self, result: Result[Iterable[API.Album]]):
|
||||||
|
self.current_albums_result = None
|
||||||
|
|
||||||
|
# TODO: Error handling
|
||||||
|
self.albums = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.albums = list(result.result())
|
||||||
|
except CacheMissError as e:
|
||||||
|
print(e)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
self.albums_by_id = {album.id: album for album in self.albums}
|
||||||
|
|
||||||
|
self._update_albums()
|
||||||
|
|
||||||
|
def _update_albums(self):
|
||||||
|
# Update ordering
|
||||||
|
if self.album_sort_direction == "descending":
|
||||||
|
self.albums.reverse()
|
||||||
|
|
||||||
|
# TODO: Update instead of re-create
|
||||||
|
for child in self.grid.get_children():
|
||||||
|
self.grid.remove(child)
|
||||||
|
|
||||||
|
for album in self.albums:
|
||||||
|
self.grid.add(self._create_cover_art_widget(album))
|
||||||
|
|
||||||
|
def _make_label(self, text: str, name: str) -> Gtk.Label:
|
||||||
|
return Gtk.Label(
|
||||||
|
name=name,
|
||||||
|
label=text,
|
||||||
|
tooltip_text=text,
|
||||||
|
ellipsize=Pango.EllipsizeMode.END,
|
||||||
|
max_width_chars=22,
|
||||||
|
halign=Gtk.Align.START,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_cover_art_widget(self, album) -> Gtk.Box:
|
||||||
|
widget_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
# Cover art image
|
||||||
|
artwork = SpinnerImage(
|
||||||
|
loading=False,
|
||||||
|
image_name="grid-artwork",
|
||||||
|
spinner_name="grid-artwork-spinner",
|
||||||
|
image_size=200,
|
||||||
|
)
|
||||||
|
widget_box.pack_start(artwork, False, False, 0)
|
||||||
|
|
||||||
|
# Header for the widget
|
||||||
|
header_label = self._make_label(album.name, "grid-header-label")
|
||||||
|
widget_box.pack_start(header_label, False, False, 0)
|
||||||
|
|
||||||
|
# Extra info for the widget
|
||||||
|
info_text = util.dot_join(
|
||||||
|
album.artist.name if album.artist else "-", album.year
|
||||||
|
)
|
||||||
|
if info_text:
|
||||||
|
info_label = self._make_label(info_text, "grid-info-label")
|
||||||
|
widget_box.pack_start(info_label, False, False, 0)
|
||||||
|
|
||||||
|
# Download the cover art.
|
||||||
|
def on_artwork_downloaded(filename: Result[str]):
|
||||||
|
artwork.set_from_file(filename.result())
|
||||||
|
artwork.set_loading(False)
|
||||||
|
|
||||||
|
cover_art_filename_future = AdapterManager.get_cover_art_uri(
|
||||||
|
album.cover_art, "file"
|
||||||
|
)
|
||||||
|
if cover_art_filename_future.data_is_available:
|
||||||
|
on_artwork_downloaded(cover_art_filename_future)
|
||||||
|
else:
|
||||||
|
artwork.set_loading(True)
|
||||||
|
cover_art_filename_future.add_done_callback(
|
||||||
|
lambda f: GLib.idle_add(on_artwork_downloaded, f)
|
||||||
|
)
|
||||||
|
|
||||||
|
widget_box.show_all()
|
||||||
|
return widget_box
|
||||||
|
|
||||||
def _get_opposite_sort_dir(self, sort_dir: str) -> str:
|
def _get_opposite_sort_dir(self, sort_dir: str) -> str:
|
||||||
return ("ascending", "descending")[0 if sort_dir == "descending" else 1]
|
return ("ascending", "descending")[0 if sort_dir == "descending" else 1]
|
||||||
@@ -469,10 +639,15 @@ class AlbumsPanel(Gtk.Box):
|
|||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_grid_cover_clicked(self, grid: Any, id: str):
|
def on_album_clicked(self, _:Any, child: Gtk.FlowBoxChild):
|
||||||
|
album = self.albums[child.get_index()]
|
||||||
|
|
||||||
|
if self.get_folded() and self.get_visible_child() == self.grid_box:
|
||||||
|
self.set_visible_child(self.details_box)
|
||||||
|
|
||||||
self.emit(
|
self.emit(
|
||||||
"refresh-window",
|
"refresh-window",
|
||||||
{"selected_album_id": id},
|
{"selected_album_id": album.id},
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -339,3 +339,7 @@ entry.invalid {
|
|||||||
inset 0 -5px 5px @box_shadow_color;
|
inset 0 -5px 5px @box_shadow_color;
|
||||||
background-color: @box_shadow_color;
|
background-color: @box_shadow_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.details-panel {
|
||||||
|
box-shadow: inset 5px 0 5px @box_shadow_color;
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
from .album_with_songs import AlbumWithSongs
|
from .album_with_songs import AlbumWithSongs
|
||||||
from .icon_button import IconButton, IconMenuButton, IconToggleButton
|
from .icon_button import IconButton, IconMenuButton, IconToggleButton
|
||||||
from .load_error import LoadError
|
from .load_error import LoadError
|
||||||
|
from .sizer import Sizer
|
||||||
from .song_list_column import SongListColumn
|
from .song_list_column import SongListColumn
|
||||||
from .spinner_image import SpinnerImage
|
from .spinner_image import SpinnerImage
|
||||||
from .spinner_picture import SpinnerPicture
|
from .spinner_picture import SpinnerPicture
|
||||||
@@ -11,6 +12,7 @@ __all__ = (
|
|||||||
"IconMenuButton",
|
"IconMenuButton",
|
||||||
"IconToggleButton",
|
"IconToggleButton",
|
||||||
"LoadError",
|
"LoadError",
|
||||||
|
"Sizer",
|
||||||
"SongListColumn",
|
"SongListColumn",
|
||||||
"SpinnerImage",
|
"SpinnerImage",
|
||||||
"SpinnerPicture",
|
"SpinnerPicture",
|
||||||
|
@@ -23,55 +23,43 @@ class AlbumWithSongs(Gtk.Box):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
album = None
|
||||||
offline_mode = True
|
offline_mode = True
|
||||||
|
|
||||||
|
cover_art_result = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
album: API.Album,
|
|
||||||
cover_art_size: int = 200,
|
cover_art_size: int = 200,
|
||||||
show_artist_name: bool = True,
|
show_artist_name: bool = True,
|
||||||
):
|
):
|
||||||
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
|
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
self.album = album
|
|
||||||
|
self.show_artist_name = show_artist_name
|
||||||
|
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
artist_artwork = SpinnerImage(
|
self.artist_artwork = SpinnerImage(
|
||||||
loading=False,
|
loading=False,
|
||||||
image_name="artist-album-list-artwork",
|
image_name="artist-album-list-artwork",
|
||||||
spinner_name="artist-artwork-spinner",
|
spinner_name="artist-artwork-spinner",
|
||||||
image_size=cover_art_size,
|
image_size=cover_art_size,
|
||||||
)
|
)
|
||||||
# Account for 10px margin on all sides with "+ 20".
|
# Account for 10px margin on all sides with "+ 20".
|
||||||
# artist_artwork.set_size_request(cover_art_size + 20, cover_art_size + 20)
|
self.artist_artwork.set_size_request(cover_art_size + 20, cover_art_size + 20)
|
||||||
box.pack_start(artist_artwork, False, False, 0)
|
box.pack_start(self.artist_artwork, False, False, 0)
|
||||||
box.pack_start(Gtk.Box(), True, True, 0)
|
box.pack_start(Gtk.Box(), True, True, 0)
|
||||||
self.pack_start(box, False, False, 0)
|
self.pack_start(box, False, False, 0)
|
||||||
|
|
||||||
def cover_art_future_done(f: Result):
|
|
||||||
artist_artwork.set_from_file(f.result())
|
|
||||||
artist_artwork.set_loading(False)
|
|
||||||
|
|
||||||
cover_art_filename_future = AdapterManager.get_cover_art_uri(
|
|
||||||
album.cover_art,
|
|
||||||
"file",
|
|
||||||
before_download=lambda: artist_artwork.set_loading(True),
|
|
||||||
)
|
|
||||||
cover_art_filename_future.add_done_callback(
|
|
||||||
lambda f: GLib.idle_add(cover_art_future_done, f)
|
|
||||||
)
|
|
||||||
|
|
||||||
album_details = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
album_details = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
album_title_and_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
album_title_and_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
||||||
# TODO (#43): deal with super long-ass titles
|
# TODO (#43): deal with super long-ass titles
|
||||||
album_title_and_buttons.add(
|
self.title = Gtk.Label(
|
||||||
Gtk.Label(
|
|
||||||
label=album.name,
|
|
||||||
name="artist-album-list-album-name",
|
name="artist-album-list-album-name",
|
||||||
halign=Gtk.Align.START,
|
halign=Gtk.Align.START,
|
||||||
ellipsize=Pango.EllipsizeMode.END,
|
ellipsize=Pango.EllipsizeMode.END,
|
||||||
)
|
)
|
||||||
)
|
album_title_and_buttons.add(self.title)
|
||||||
|
|
||||||
self.play_btn = IconButton(
|
self.play_btn = IconButton(
|
||||||
"media-playback-start-symbolic",
|
"media-playback-start-symbolic",
|
||||||
@@ -113,20 +101,11 @@ class AlbumWithSongs(Gtk.Box):
|
|||||||
|
|
||||||
album_details.add(album_title_and_buttons)
|
album_details.add(album_title_and_buttons)
|
||||||
|
|
||||||
stats: List[Any] = [
|
self.stats = Gtk.Label(
|
||||||
album.artist.name if show_artist_name and album.artist else None,
|
|
||||||
album.year,
|
|
||||||
album.genre.name if album.genre else None,
|
|
||||||
util.format_sequence_duration(album.duration) if album.duration else None,
|
|
||||||
]
|
|
||||||
|
|
||||||
album_details.add(
|
|
||||||
Gtk.Label(
|
|
||||||
label=util.dot_join(*stats),
|
|
||||||
halign=Gtk.Align.START,
|
halign=Gtk.Align.START,
|
||||||
margin_left=10,
|
margin_left=10,
|
||||||
)
|
)
|
||||||
)
|
album_details.add(self.stats)
|
||||||
|
|
||||||
self.loading_indicator_container = Gtk.Box()
|
self.loading_indicator_container = Gtk.Box()
|
||||||
album_details.add(self.loading_indicator_container)
|
album_details.add(self.loading_indicator_container)
|
||||||
@@ -169,7 +148,7 @@ class AlbumWithSongs(Gtk.Box):
|
|||||||
|
|
||||||
self.pack_end(album_details, True, True, 0)
|
self.pack_end(album_details, True, True, 0)
|
||||||
|
|
||||||
self.update_album_songs(album.id)
|
# self.update_album_songs(album.id)
|
||||||
|
|
||||||
# Event Handlers
|
# Event Handlers
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
@@ -257,7 +236,9 @@ class AlbumWithSongs(Gtk.Box):
|
|||||||
def deselect_all(self):
|
def deselect_all(self):
|
||||||
self.album_songs.get_selection().unselect_all()
|
self.album_songs.get_selection().unselect_all()
|
||||||
|
|
||||||
def update(self, app_config: AppConfiguration = None, force: bool = False):
|
def update(self, album: API.Album, app_config: AppConfiguration, force: bool = False):
|
||||||
|
update_songs = False
|
||||||
|
|
||||||
if app_config:
|
if app_config:
|
||||||
# Deselect everything and reset the error container if switching between
|
# Deselect everything and reset the error container if switching between
|
||||||
# online and offline.
|
# online and offline.
|
||||||
@@ -266,8 +247,42 @@ class AlbumWithSongs(Gtk.Box):
|
|||||||
for c in self.error_container.get_children():
|
for c in self.error_container.get_children():
|
||||||
self.error_container.remove(c)
|
self.error_container.remove(c)
|
||||||
|
|
||||||
|
update_songs = True
|
||||||
|
|
||||||
self.offline_mode = app_config.offline_mode
|
self.offline_mode = app_config.offline_mode
|
||||||
|
|
||||||
|
if album != self.album:
|
||||||
|
self.album = album
|
||||||
|
|
||||||
|
self.title.set_label(album.name)
|
||||||
|
|
||||||
|
self.stats.set_label(util.dot_join(
|
||||||
|
album.artist.name if self.show_artist_name and album.artist else None,
|
||||||
|
album.year,
|
||||||
|
album.genre.name if album.genre else None,
|
||||||
|
util.format_sequence_duration(album.duration) if album.duration else None,
|
||||||
|
))
|
||||||
|
|
||||||
|
if self.cover_art_result is not None:
|
||||||
|
self.cover_art_result.cancel()
|
||||||
|
|
||||||
|
def cover_art_future_done(f: Result):
|
||||||
|
self.artist_artwork.set_from_file(f.result())
|
||||||
|
self.artist_artwork.set_loading(False)
|
||||||
|
self.cover_art_result = None
|
||||||
|
|
||||||
|
self.cover_art_result = AdapterManager.get_cover_art_uri(
|
||||||
|
album.cover_art,
|
||||||
|
"file",
|
||||||
|
before_download=lambda: self.artist_artwork.set_loading(True),
|
||||||
|
)
|
||||||
|
self.cover_art_result.add_done_callback(
|
||||||
|
lambda f: GLib.idle_add(cover_art_future_done, f)
|
||||||
|
)
|
||||||
|
|
||||||
|
update_songs = True
|
||||||
|
|
||||||
|
if update_songs:
|
||||||
self.update_album_songs(self.album.id, app_config=app_config, force=force)
|
self.update_album_songs(self.album.id, app_config=app_config, force=force)
|
||||||
|
|
||||||
def set_loading(self, loading: bool):
|
def set_loading(self, loading: bool):
|
||||||
|
44
sublime_music/ui/common/sizer.py
Normal file
44
sublime_music/ui/common/sizer.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from gi.repository import Gtk, GObject
|
||||||
|
|
||||||
|
class Sizer(Gtk.Bin):
|
||||||
|
""" A widget that lets you control the natural size like the size request """
|
||||||
|
|
||||||
|
natural_width = GObject.Property(type=int, default=0)
|
||||||
|
natural_height = GObject.Property(type=int, default=0)
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
Gtk.Bin.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
def do_get_preferred_width(self) -> (int, int):
|
||||||
|
minimum, natural = Gtk.Bin.do_get_preferred_width(self)
|
||||||
|
|
||||||
|
if self.natural_width > 0:
|
||||||
|
natural = max(minimum, self.natural_width)
|
||||||
|
|
||||||
|
return (minimum, natural)
|
||||||
|
|
||||||
|
def do_get_preferred_height(self) -> (int, int):
|
||||||
|
minimum, natural = Gtk.Bin.do_get_preferred_height(self)
|
||||||
|
|
||||||
|
if self.natural_height > 0:
|
||||||
|
natural = max(minimum, self.natural_height)
|
||||||
|
|
||||||
|
return (minimum, natural)
|
||||||
|
|
||||||
|
def do_get_preferred_width_for_height(self, height: int) -> (int, int):
|
||||||
|
minimum, natural = Gtk.Bin.do_get_preferred_width_for_height(self, height)
|
||||||
|
|
||||||
|
if self.natural_width > 0:
|
||||||
|
natural = max(minimum, self.natural_width)
|
||||||
|
|
||||||
|
return (minimum, natural)
|
||||||
|
|
||||||
|
def do_get_preferred_height_for_width(self, width: int) -> (int, int):
|
||||||
|
minimum, natural = Gtk.Bin.do_get_preferred_height_for_width(self, width)
|
||||||
|
|
||||||
|
if self.natural_height > 0:
|
||||||
|
natural = max(minimum, self.natural_height)
|
||||||
|
|
||||||
|
return (minimum, natural)
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user