This commit is contained in:
Benjamin Schaaf
2021-02-09 01:24:04 +11:00
parent 56ae24b479
commit 4ba2e09cf1
5 changed files with 302 additions and 271 deletions

View File

@@ -99,30 +99,37 @@ class AlbumsPanel(Handy.Leaflet):
)
actionbar.pack_start(self.sort_type_combo)
self.filter_stack = Gtk.Stack(no_show_all=True)
self.alphabetical_type_combo, _ = self.make_combobox(
(("by_name", "by album name", True), ("by_artist", "by artist name", True)),
self.on_alphabetical_type_change,
)
actionbar.pack_start(self.alphabetical_type_combo)
self.filter_stack.add(self.alphabetical_type_combo)
self.genre_combo, self.genre_combo_store = self.make_combobox(
(), self.on_genre_change
)
actionbar.pack_start(self.genre_combo)
self.filter_stack.add(self.genre_combo)
filter_time_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
next_decade = (datetime.datetime.now().year // 10) * 10 + 10
self.from_year_label = Gtk.Label(label="from")
actionbar.pack_start(self.from_year_label)
filter_time_box.add(self.from_year_label)
self.from_year_spin_button = Gtk.SpinButton.new_with_range(0, next_decade, 1)
self.from_year_spin_button.connect("value-changed", self.on_year_changed)
actionbar.pack_start(self.from_year_spin_button)
filter_time_box.add(self.from_year_spin_button)
self.to_year_label = Gtk.Label(label="to")
actionbar.pack_start(self.to_year_label)
filter_time_box.add(self.to_year_label)
self.to_year_spin_button = Gtk.SpinButton.new_with_range(0, next_decade, 1)
self.to_year_spin_button.connect("value-changed", self.on_year_changed)
actionbar.pack_start(self.to_year_spin_button)
filter_time_box.add(self.to_year_spin_button)
self.filter_stack.add(filter_time_box)
actionbar.pack_start(self.filter_stack)
self.sort_toggle = IconButton(
"view-sort-descending-symbolic", "Sort descending", relief=True
@@ -213,34 +220,25 @@ class AlbumsPanel(Handy.Leaflet):
self.add(self.grid_box)
self.details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.details_box.get_style_context().add_class("details-panel")
self.album_container = Sizer(natural_width=500)
details_top_bar_revealer = Gtk.Revealer(reveal_child=False)
details_top_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.album_with_songs = AlbumWithSongs(scroll_contents=True)
self.album_with_songs.get_style_context().add_class("details-panel")
back_button = IconButton("go-previous-symbolic")
self.album_with_songs.connect("song-clicked", lambda _, *args: self.emit("song-clicked", *args))
def back_clicked(*_):
def back_clicked(_):
self.set_visible_child(self.grid_box)
back_button.connect("clicked", back_clicked)
self.album_with_songs.connect("back-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)
self.album_container.add(self.album_with_songs)
self.add(self.album_container)
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.album_with_songs.show_back_button = self.get_folded()
self.connect("notify::folded", folded_changed)
@@ -643,7 +641,7 @@ class AlbumsPanel(Handy.Leaflet):
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.set_visible_child(self.album_container)
self.emit(
"refresh-window",

View File

@@ -5,7 +5,7 @@ from typing import cast, List, Sequence
import bleach
from gi.repository import Gio, GLib, GObject, Gtk, Pango
from gi.repository import Gio, GLib, GObject, Gtk, Pango, Handy
from ..adapters import (
AdapterManager,
@@ -15,10 +15,10 @@ from ..adapters import (
)
from ..config import AppConfiguration
from ..ui import util
from ..ui.common import AlbumWithSongs, IconButton, LoadError, SpinnerImage
from ..ui.common import AlbumWithSongs, IconButton, IconToggleButton, LoadError, SpinnerImage, Sizer
class ArtistsPanel(Gtk.Paned):
class ArtistsPanel(Handy.Leaflet):
"""Defines the arist panel."""
__gsignals__ = {
@@ -35,11 +35,14 @@ class ArtistsPanel(Gtk.Paned):
}
def __init__(self, *args, **kwargs):
Gtk.Paned.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
super().__init__(transition_type=Handy.LeafletTransitionType.SLIDE, can_swipe_forward=False, interpolate_size=False)
list_sizer = Sizer(natural_width=400)
self.artist_list = ArtistList()
self.pack1(self.artist_list, False, False)
list_sizer.add(self.artist_list)
self.add(list_sizer)
details_sizer = Sizer(hexpand=True, natural_width=800)
self.artist_detail_panel = ArtistDetailPanel()
self.artist_detail_panel.connect(
"song-clicked",
@@ -49,7 +52,24 @@ class ArtistsPanel(Gtk.Paned):
"refresh-window",
lambda _, *args: self.emit("refresh-window", *args),
)
self.pack2(self.artist_detail_panel, True, False)
details_sizer.add(self.artist_detail_panel)
self.add(details_sizer)
def artist_clicked(_):
if self.get_folded():
self.set_visible_child(details_sizer)
self.artist_list.connect("artist-clicked", artist_clicked)
def back_clicked(_):
self.set_visible_child(list_sizer)
self.artist_detail_panel.connect("back-clicked", back_clicked)
def folded_changed(*_):
if not self.get_folded():
self.set_visible_child(list_sizer)
self.artist_detail_panel.show_mobile = self.get_folded()
self.connect("notify::folded", folded_changed)
def update(self, app_config: AppConfiguration, force: bool = False):
self.artist_list.update(app_config=app_config)
@@ -69,6 +89,10 @@ class _ArtistModel(GObject.GObject):
class ArtistList(Gtk.Box):
__gsignals__ = {
"artist-clicked": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ()),
}
def __init__(self):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
@@ -119,8 +143,9 @@ class ArtistList(Gtk.Box):
return row
self.artists_store = Gio.ListStore()
self.list = Gtk.ListBox(name="artist-list")
self.list = Gtk.ListBox(name="artist-list", selection_mode=Gtk.SelectionMode.BROWSE)
self.list.bind_model(self.artists_store, create_artist_row)
self.list.connect("row-selected", lambda *_: self.emit("artist-clicked"))
list_scroll_window.add(self.list)
self.pack_start(list_scroll_window, True, True, 0)
@@ -184,6 +209,7 @@ class ArtistDetailPanel(Gtk.Box):
"""Defines the artists list."""
__gsignals__ = {
"back-clicked": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ()),
"song-clicked": (
GObject.SignalFlags.RUN_FIRST,
GObject.TYPE_NONE,
@@ -196,8 +222,9 @@ class ArtistDetailPanel(Gtk.Box):
),
}
show_mobile = GObject.Property(type=bool, default=False)
update_order_token = 0
artist_details_expanded = False
def __init__(self, *args, **kwargs):
super().__init__(
@@ -206,134 +233,136 @@ class ArtistDetailPanel(Gtk.Box):
orientation=Gtk.Orientation.VERTICAL,
**kwargs,
)
self.connect("notify::show-mobile", self.on_show_mobile_changed)
self.albums: Sequence[API.Album] = []
self.artist_id = None
# Artist info panel
self.big_info_panel = Gtk.Box(
orientation=Gtk.Orientation.HORIZONTAL, name="artist-info-panel"
)
action_bar = Gtk.ActionBar()
self.artist_artwork = SpinnerImage(
loading=False,
image_name="artist-album-artwork",
spinner_name="artist-artwork-spinner",
image_size=300,
)
self.big_info_panel.pack_start(self.artist_artwork, False, False, 0)
back_button_revealer = Gtk.Revealer(transition_type=Gtk.RevealerTransitionType.CROSSFADE)
self.bind_property("show-mobile", back_button_revealer, "reveal-child", GObject.BindingFlags.SYNC_CREATE)
# Action buttons, name, comment, number of songs, etc.
artist_details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
back_button = IconButton("go-previous-symbolic")
back_button.connect("clicked", lambda *_: self.emit("back-clicked"))
back_button_revealer.add(back_button)
artist_details_box.pack_start(Gtk.Box(), True, False, 0)
action_bar.pack_start(back_button_revealer)
self.artist_indicator = self.make_label(name="artist-indicator")
artist_details_box.add(self.artist_indicator)
self.artist_name = self.make_label(
name="artist-name", ellipsize=Pango.EllipsizeMode.END
)
artist_details_box.add(self.artist_name)
self.artist_bio = self.make_label(
name="artist-bio", justify=Gtk.Justification.LEFT
)
self.artist_bio.set_line_wrap(True)
artist_details_box.add(self.artist_bio)
self.similar_artists_scrolledwindow = Gtk.ScrolledWindow()
similar_artists_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.similar_artists_label = self.make_label(name="similar-artists")
similar_artists_box.add(self.similar_artists_label)
self.similar_artists_button_box = Gtk.Box(
orientation=Gtk.Orientation.HORIZONTAL
)
similar_artists_box.add(self.similar_artists_button_box)
self.similar_artists_scrolledwindow.add(similar_artists_box)
artist_details_box.add(self.similar_artists_scrolledwindow)
self.artist_stats = self.make_label(name="artist-stats")
artist_details_box.add(self.artist_stats)
self.play_shuffle_buttons = Gtk.Box(
orientation=Gtk.Orientation.HORIZONTAL,
name="playlist-play-shuffle-buttons",
)
self.play_button = IconButton(
"media-playback-start-symbolic", label="Play All", relief=True
)
self.play_button.connect("clicked", self.on_play_all_clicked)
self.play_shuffle_buttons.pack_start(self.play_button, False, False, 0)
self.shuffle_button = IconButton(
"media-playlist-shuffle-symbolic", label="Shuffle All", relief=True
)
self.shuffle_button.connect("clicked", self.on_shuffle_all_button)
self.play_shuffle_buttons.pack_start(self.shuffle_button, False, False, 5)
artist_details_box.add(self.play_shuffle_buttons)
self.big_info_panel.pack_start(artist_details_box, True, True, 0)
# Action buttons
action_buttons_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.artist_action_buttons = Gtk.Box(
orientation=Gtk.Orientation.HORIZONTAL, spacing=10
)
self.refresh_button = IconButton("view-refresh-symbolic", "Refresh artist info")
self.refresh_button.connect("clicked", self.on_view_refresh_click)
action_bar.pack_end(self.refresh_button)
self.download_all_button = IconButton(
"folder-download-symbolic", "Download all songs by this artist"
)
self.download_all_button.connect("clicked", self.on_download_all_click)
self.artist_action_buttons.add(self.download_all_button)
action_bar.pack_end(self.download_all_button)
self.refresh_button = IconButton("view-refresh-symbolic", "Refresh artist info")
self.refresh_button.connect("clicked", self.on_view_refresh_click)
self.artist_action_buttons.add(self.refresh_button)
self.shuffle_button = IconButton("media-playlist-shuffle-symbolic")
self.shuffle_button.connect("clicked", self.on_shuffle_all_button)
action_bar.pack_end(self.shuffle_button)
action_buttons_container.pack_start(
self.artist_action_buttons, False, False, 10
self.play_button = IconButton("media-playback-start-symbolic")
self.play_button.connect("clicked", self.on_play_all_clicked)
action_bar.pack_end(self.play_button)
self.pack_start(action_bar, False, False, 0)
self.pack_start(Gtk.Separator(), False, False, 0)
self.scrolled_window = Gtk.ScrolledWindow(hscrollbar_policy=Gtk.PolicyType.NEVER)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
# Artist info panel
info_panel = Gtk.Box(
orientation=Gtk.Orientation.HORIZONTAL, name="artist-info-panel"
)
action_buttons_container.pack_start(Gtk.Box(), True, True, 0)
expand_button_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.expand_collapse_button = IconButton(
"pan-up-symbolic", "Expand playlist details"
self.artist_artwork = SpinnerImage(
loading=False,
image_size=120,
valign=Gtk.Align.START,
)
self.expand_collapse_button.connect("clicked", self.on_expand_collapse_click)
expand_button_container.pack_end(self.expand_collapse_button, False, False, 0)
action_buttons_container.add(expand_button_container)
info_panel.pack_start(self.artist_artwork, False, False, 10)
self.big_info_panel.pack_start(action_buttons_container, False, False, 5)
# Action buttons, name, comment, number of songs, etc.
details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.pack_start(self.big_info_panel, False, True, 0)
self.artist_name = self.make_label(
name="artist-name", ellipsize=Pango.EllipsizeMode.END
)
details_box.add(self.artist_name)
self.artist_bio_revealer = Gtk.Revealer(transition_type=Gtk.RevealerTransitionType.SLIDE_DOWN, reveal_child=True)
self.artist_bio = self.make_label(
name="artist-bio", justify=Gtk.Justification.LEFT
)
self.artist_bio.set_line_wrap(True)
self.artist_bio_revealer.add(self.artist_bio)
details_box.add(self.artist_bio_revealer)
details_bottom_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
artist_stats_squeezer = Handy.Squeezer(homogeneous=False)
self.artist_stats_long = self.make_label(name="artist-stats")
artist_stats_squeezer.add(self.artist_stats_long)
self.artist_stats_short = self.make_label(name="artist-stats")
artist_stats_squeezer.add(self.artist_stats_short)
details_bottom_box.pack_start(artist_stats_squeezer, False, False, 0)
self.expand_button_revealer = Gtk.Revealer(transition_type=Gtk.RevealerTransitionType.CROSSFADE, margin_left=10)
self.expand_button = IconToggleButton(
"pan-down-symbolic", "Expand"
)
self.expand_button.bind_property("active", self.artist_bio_revealer, "reveal-child")
self.expand_button.connect("clicked", self.on_expand_button_clicked)
self.expand_button_revealer.add(self.expand_button)
details_bottom_box.pack_end(self.expand_button_revealer, False, False, 0)
details_box.pack_end(details_bottom_box, False, False, 0)
info_panel.pack_start(details_box, True, True, 0)
box.pack_start(info_panel, False, False, 0)
self.error_container = Gtk.Box()
self.add(self.error_container)
# self.add(self.error_container)
self.album_list_scrolledwindow = Gtk.ScrolledWindow()
self.albums_list = AlbumsListWithSongs()
self.albums_list.connect(
"song-clicked",
lambda _, *args: self.emit("song-clicked", *args),
)
self.album_list_scrolledwindow.add(self.albums_list)
self.pack_start(self.album_list_scrolledwindow, True, True, 0)
box.pack_start(self.albums_list, True, True, 0)
self.scrolled_window.add(box)
self.pack_start(self.scrolled_window, True, True, 0)
def on_show_mobile_changed(self, *_):
self.expand_button.set_active(not self.show_mobile)
self.artist_bio_revealer.set_reveal_child(not self.show_mobile)
self.expand_button_revealer.set_reveal_child(self.show_mobile)
def on_expand_button_clicked(self, *_):
up_down = "up" if self.expand_button.get_active() else "down"
self.expand_button.set_icon(f"pan-{up_down}-symbolic")
self.expand_button.set_tooltip_text(
"Collapse" if self.expand_button.get_active() else "Expand"
)
def update(self, app_config: AppConfiguration):
self.artist_id = app_config.state.selected_artist_id
self.offline_mode = app_config.offline_mode
if app_config.state.selected_artist_id is None:
self.big_info_panel.hide()
self.album_list_scrolledwindow.hide()
self.play_shuffle_buttons.hide()
self.shuffle_button.set_sensitive(False)
self.play_button.set_sensitive(False)
else:
self.update_order_token += 1
self.album_list_scrolledwindow.show()
self.update_artist_view(
app_config.state.selected_artist_id,
app_config=app_config,
@@ -358,58 +387,27 @@ class ArtistDetailPanel(Gtk.Box):
if order_token != self.update_order_token:
return
self.big_info_panel.show_all()
if app_config:
self.artist_details_expanded = app_config.state.artist_details_expanded
up_down = "up" if self.artist_details_expanded else "down"
self.expand_collapse_button.set_icon(f"pan-{up_down}-symbolic")
self.expand_collapse_button.set_tooltip_text(
"Collapse" if self.artist_details_expanded else "Expand"
)
# Scroll to top
self.scrolled_window.get_vadjustment().set_value(0)
self.artist_name.set_markup(bleach.clean(f"<b>{artist.name}</b>"))
self.artist_name.set_tooltip_text(artist.name)
if self.artist_details_expanded:
self.artist_artwork.get_style_context().remove_class("collapsed")
self.artist_name.get_style_context().remove_class("collapsed")
self.artist_indicator.set_text("ARTIST")
self.artist_stats.set_markup(self.format_stats(artist))
self.artist_stats_long.set_markup(self.format_stats(artist, short=False))
self.artist_stats_short.set_markup(self.format_stats(artist, short=True))
if artist.biography:
self.artist_bio.set_markup(bleach.clean(artist.biography))
self.artist_bio.show()
else:
self.artist_bio.hide()
biography = ""
if artist.biography:
biography += util.esc(artist.biography)
if len(artist.similar_artists or []) > 0:
self.similar_artists_label.set_markup("<b>Similar Artists:</b> ")
for c in self.similar_artists_button_box.get_children():
self.similar_artists_button_box.remove(c)
if artist.similar_artists:
biography += "\n\n<b>Similar Artists:</b> "
for similar_artist in (artist.similar_artists or [])[:5]:
self.similar_artists_button_box.add(
Gtk.LinkButton(
label=similar_artist.name,
name="similar-artist-button",
action_name="app.go-to-artist",
action_target=GLib.Variant("s", similar_artist.id),
)
)
self.similar_artists_scrolledwindow.show_all()
else:
self.similar_artists_scrolledwindow.hide()
else:
self.artist_artwork.get_style_context().add_class("collapsed")
self.artist_name.get_style_context().add_class("collapsed")
self.artist_indicator.hide()
self.artist_stats.hide()
self.artist_bio.hide()
self.similar_artists_scrolledwindow.hide()
# TODO: Make links work
biography += ", ".join(f"<a href=\"{a.id}\">{bleach.clean(a.name)}</a>" for a in artist.similar_artists[:6])
self.play_shuffle_buttons.show_all()
self.artist_bio.set_markup(biography)
self.expand_button.set_sensitive(bool(biography))
self.update_artist_artwork(
artist.artist_image_url,
@@ -429,11 +427,8 @@ class ArtistDetailPanel(Gtk.Box):
)
self.error_container.pack_start(load_error, True, True, 0)
self.error_container.show_all()
if not has_data:
self.album_list_scrolledwindow.hide()
else:
self.error_container.hide()
self.album_list_scrolledwindow.show()
self.albums = artist.albums or []
@@ -486,10 +481,10 @@ class ArtistDetailPanel(Gtk.Box):
self.artist_artwork.set_from_file(cover_art_filename)
self.artist_artwork.set_loading(False)
if self.artist_details_expanded:
self.artist_artwork.set_image_size(300)
else:
self.artist_artwork.set_image_size(70)
# if self.artist_details_expanded:
# self.artist_artwork.set_image_size(300)
# else:
# self.artist_artwork.set_image_size(70)
# Event Handlers
# =========================================================================
@@ -531,13 +526,6 @@ class ArtistDetailPanel(Gtk.Box):
{"force_shuffle_state": True},
)
def on_expand_collapse_click(self, _):
self.emit(
"refresh-window",
{"artist_details_expanded": not self.artist_details_expanded},
False,
)
# Helper Methods
# =========================================================================
def set_all_loading(self, loading_state: bool):
@@ -554,7 +542,7 @@ class ArtistDetailPanel(Gtk.Box):
label=text, name=name, halign=Gtk.Align.START, xalign=0, **params
)
def format_stats(self, artist: API.Artist) -> str:
def format_stats(self, artist: API.Artist, short=False) -> str:
album_count = artist.album_count or len(artist.albums or [])
song_count, duration = 0, timedelta(0)
for album in artist.albums or []:
@@ -564,7 +552,8 @@ class ArtistDetailPanel(Gtk.Box):
return util.dot_join(
"{} {}".format(album_count, util.pluralize("album", album_count)),
"{} {}".format(song_count, util.pluralize("song", song_count)),
util.format_sequence_duration(duration),
util.format_song_duration(duration)
if short else util.format_sequence_duration(duration),
)
def get_artist_song_ids(self) -> List[str]:
@@ -633,8 +622,8 @@ class AlbumsListWithSongs(Gtk.Overlay):
if self.albums == new_albums:
# Just go through all of the colidren and update them.
for c in self.box.get_children():
c.update(app_config=app_config, force=force)
for c, album in zip(self.box.get_children(), self.albums):
c.update(album, app_config=app_config, force=force)
self.spinner.hide()
return
@@ -644,19 +633,20 @@ class AlbumsListWithSongs(Gtk.Overlay):
remove_all()
for album in self.albums:
album_with_songs = AlbumWithSongs(album, show_artist_name=False)
album_with_songs = AlbumWithSongs(show_artist_name=False)
album_with_songs.update(album, app_config, force=force)
album_with_songs.connect(
"song-clicked",
lambda _, *args: self.emit("song-clicked", *args),
)
album_with_songs.connect("song-selected", self.on_song_selected)
# album_with_songs.connect("song-selected", self.on_song_selected)
album_with_songs.show_all()
self.box.add(album_with_songs)
# Update everything (no force to ensure that if we are online, then everything
# is clickable)
for c in self.box.get_children():
c.update(app_config=app_config)
# for c in self.box.get_children():
# c.update(app_config=app_config)
self.spinner.hide()

View File

@@ -1,7 +1,7 @@
from random import randint
from typing import Any, cast, List
from gi.repository import Gdk, GLib, GObject, Gtk, Pango
from gi.repository import Gdk, GLib, GObject, Gtk, Pango, Handy
from sublime_music.adapters import AdapterManager, api_objects as API, Result
from sublime_music.config import AppConfiguration
@@ -15,81 +15,37 @@ from .spinner_image import SpinnerImage
class AlbumWithSongs(Gtk.Box):
__gsignals__ = {
"song-selected": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ()),
# "song-selected": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ()),
"song-clicked": (
GObject.SignalFlags.RUN_FIRST,
GObject.TYPE_NONE,
(int, object, object),
),
"back-clicked": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ()),
}
show_back_button = GObject.Property(type=bool, default=False)
album = None
offline_mode = True
cover_art_result = None
def __init__(
self,
cover_art_size: int = 200,
show_artist_name: bool = True,
):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
def __init__(self, show_artist_name: bool = True, scroll_contents: bool = False, **kwargs):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
self.show_artist_name = show_artist_name
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.artist_artwork = SpinnerImage(
loading=False,
image_name="artist-album-list-artwork",
spinner_name="artist-artwork-spinner",
image_size=cover_art_size,
)
# Account for 10px margin on all sides with "+ 20".
self.artist_artwork.set_size_request(cover_art_size + 20, cover_art_size + 20)
box.pack_start(self.artist_artwork, False, False, 0)
box.pack_start(Gtk.Box(), True, True, 0)
self.pack_start(box, False, False, 0)
action_bar = Gtk.ActionBar()
album_details = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
album_title_and_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
back_button_revealer = Gtk.Revealer(transition_type=Gtk.RevealerTransitionType.CROSSFADE)
self.bind_property("show-back-button", back_button_revealer, "reveal-child", GObject.BindingFlags.SYNC_CREATE)
# TODO (#43): deal with super long-ass titles
self.title = Gtk.Label(
name="artist-album-list-album-name",
halign=Gtk.Align.START,
ellipsize=Pango.EllipsizeMode.END,
)
album_title_and_buttons.add(self.title)
back_button = IconButton("go-previous-symbolic")
back_button.connect("clicked", lambda *_: self.emit("back-clicked"))
back_button_revealer.add(back_button)
self.play_btn = IconButton(
"media-playback-start-symbolic",
"Play all songs in this album",
sensitive=False,
)
self.play_btn.connect("clicked", self.play_btn_clicked)
album_title_and_buttons.pack_start(self.play_btn, False, False, 5)
self.shuffle_btn = IconButton(
"media-playlist-shuffle-symbolic",
"Shuffle all songs in this album",
sensitive=False,
)
self.shuffle_btn.connect("clicked", self.shuffle_btn_clicked)
album_title_and_buttons.pack_start(self.shuffle_btn, False, False, 5)
self.play_next_btn = IconButton(
"queue-front-symbolic",
"Play all of the songs in this album next",
sensitive=False,
)
album_title_and_buttons.pack_start(self.play_next_btn, False, False, 5)
self.add_to_queue_btn = IconButton(
"queue-back-symbolic",
"Add all the songs in this album to the end of the play queue",
sensitive=False,
)
album_title_and_buttons.pack_start(self.add_to_queue_btn, False, False, 5)
action_bar.pack_start(back_button_revealer)
self.download_all_btn = IconButton(
"folder-download-symbolic",
@@ -97,21 +53,105 @@ class AlbumWithSongs(Gtk.Box):
sensitive=False,
)
self.download_all_btn.connect("clicked", self.on_download_all_click)
album_title_and_buttons.pack_end(self.download_all_btn, False, False, 5)
action_bar.pack_end(self.download_all_btn)
album_details.add(album_title_and_buttons)
self.add_to_queue_btn = IconButton(
"queue-back-symbolic",
"Add all the songs in this album to the end of the play queue",
sensitive=False,
)
action_bar.pack_end(self.add_to_queue_btn)
self.stats = Gtk.Label(
self.play_next_btn = IconButton(
"queue-front-symbolic",
"Play all of the songs in this album next",
sensitive=False,
)
action_bar.pack_end(self.play_next_btn)
self.shuffle_btn = IconButton(
"media-playlist-shuffle-symbolic",
"Shuffle all songs in this album",
sensitive=False,
)
self.shuffle_btn.connect("clicked", self.shuffle_btn_clicked)
action_bar.pack_end(self.shuffle_btn)
self.play_btn = IconButton(
"media-playback-start-symbolic",
"Play all songs in this album",
sensitive=False,
)
self.play_btn.connect("clicked", self.play_btn_clicked)
action_bar.pack_end(self.play_btn)
self.pack_start(action_bar, False, False, 0)
if scroll_contents:
self.pack_start(Gtk.Separator(), False, False, 0)
scrolled_window = Gtk.ScrolledWindow(hscrollbar_policy=Gtk.PolicyType.NEVER)
self.pack_start(scrolled_window, True, True, 0)
contents_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
scrolled_window.add(contents_box)
else:
contents_box = self
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.artist_artwork = SpinnerImage(
loading=False,
image_name="artist-album-list-artwork",
spinner_name="artist-artwork-spinner",
image_size=80,
)
# Account for 10px margin on all sides with "+ 20".
# self.artist_artwork.set_size_request(cover_art_size + 20, cover_art_size + 20)
box.pack_start(self.artist_artwork, False, False, 0)
album_details = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
# TODO (#43): deal with super long-ass titles
self.title = Gtk.Label(
name="artist-album-list-album-name",
halign=Gtk.Align.START,
ellipsize=Pango.EllipsizeMode.END,
)
album_details.pack_start(self.title, False, False, 0)
self.artist_and_year = Gtk.Label(
halign=Gtk.Align.START,
margin_left=10,
margin_right=10,
ellipsize=Pango.EllipsizeMode.END,
)
album_details.add(self.stats)
album_details.pack_start(self.artist_and_year, False, False, 0)
details_bottom_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.genre_and_song_count = Gtk.Label()
details_bottom_box.pack_start(self.genre_and_song_count, False, False, 10)
squeezer = Handy.Squeezer(homogeneous=False)
self.song_duration_long = Gtk.Label(halign=Gtk.Align.END)
squeezer.add(self.song_duration_long)
self.song_duration_short = Gtk.Label(halign=Gtk.Align.END)
squeezer.add(self.song_duration_short)
details_bottom_box.pack_end(squeezer, False, False, 20)
album_details.pack_start(details_bottom_box, False, False, 0)
self.loading_indicator_container = Gtk.Box()
album_details.add(self.loading_indicator_container)
album_details.pack_start(self.loading_indicator_container, False, False, 0)
self.error_container = Gtk.Box()
album_details.add(self.error_container)
album_details.pack_start(self.error_container, False, False, 0)
box.pack_start(album_details, True, True, 0)
contents_box.pack_start(box, False, False, 0)
# clickable, cache status, title, duration, song ID
self.album_song_store = Gtk.ListStore(bool, str, str, str, str)
@@ -144,11 +184,7 @@ class AlbumWithSongs(Gtk.Box):
self.album_songs.get_selection().connect(
"changed", self.on_song_selection_change
)
album_details.add(self.album_songs)
self.pack_end(album_details, True, True, 0)
# self.update_album_songs(album.id)
contents_box.pack_start(self.album_songs, True, True, 0)
# Event Handlers
# =========================================================================
@@ -256,12 +292,18 @@ class AlbumWithSongs(Gtk.Box):
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,
))
artist = album.artist.name if self.show_artist_name and album.artist else None
self.artist_and_year.set_label(util.dot_join(artist, album.year))
self.genre_and_song_count.set_label(util.dot_join(
f"{album.song_count} " + util.pluralize("song", album.song_count),
album.genre.name if album.genre else None))
self.song_duration_long.set_label(
util.format_sequence_duration(album.duration) if album.duration else "")
self.song_duration_short.set_label(
util.format_song_duration(album.duration) if album.duration else "")
if self.cover_art_result is not None:
self.cover_art_result.cancel()

View File

@@ -74,8 +74,8 @@ class MainWindow(Gtk.ApplicationWindow):
self.stack = self._create_stack(
Albums=self.albums_panel,
Artists=self.artists_panel,
Browse=self.browse_panel,
Playlists=self.playlists_panel,
# Browse=self.browse_panel,
# Playlists=self.playlists_panel,
)
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
@@ -84,6 +84,7 @@ class MainWindow(Gtk.ApplicationWindow):
fold_policy=Handy.FlapFoldPolicy.ALWAYS,
transition_type=Handy.FlapTransitionType.OVER,
modal=True)
self.stack.connect("notify::visible-child", lambda *_: self.sidebar_flap.set_reveal_flap(False))
self.titlebar = self._create_headerbar(self.stack)
self.set_titlebar(self.titlebar)

View File

@@ -3,7 +3,7 @@ from datetime import timedelta
from typing import Any, Optional, Callable, Dict, Set, Tuple
from functools import partial
from gi.repository import GObject, Gtk
from gi.repository import GObject, Gtk, GLib
from .. import util
from ...adapters import AdapterManager, Result