diff --git a/sublime_music/ui/albums.py b/sublime_music/ui/albums.py
index a6f094e..273a6af 100644
--- a/sublime_music/ui/albums.py
+++ b/sublime_music/ui/albums.py
@@ -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",
diff --git a/sublime_music/ui/artists.py b/sublime_music/ui/artists.py
index 32a54de..ba7e6c6 100644
--- a/sublime_music/ui/artists.py
+++ b/sublime_music/ui/artists.py
@@ -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"{artist.name}"))
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("Similar Artists: ")
- for c in self.similar_artists_button_box.get_children():
- self.similar_artists_button_box.remove(c)
+ if artist.similar_artists:
+ biography += "\n\nSimilar Artists: "
- 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"{bleach.clean(a.name)}" 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()
diff --git a/sublime_music/ui/common/album_with_songs.py b/sublime_music/ui/common/album_with_songs.py
index 092f8d2..c067646 100644
--- a/sublime_music/ui/common/album_with_songs.py
+++ b/sublime_music/ui/common/album_with_songs.py
@@ -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()
diff --git a/sublime_music/ui/main.py b/sublime_music/ui/main.py
index 9430f92..c62dcf8 100644
--- a/sublime_music/ui/main.py
+++ b/sublime_music/ui/main.py
@@ -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)
diff --git a/sublime_music/ui/player_controls/manager.py b/sublime_music/ui/player_controls/manager.py
index 27cdfdb..b1ddf88 100644
--- a/sublime_music/ui/player_controls/manager.py
+++ b/sublime_music/ui/player_controls/manager.py
@@ -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