WIP
This commit is contained in:
@@ -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",
|
||||
|
@@ -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()
|
||||
|
||||
|
@@ -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()
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user