Skip to content

Commit

Permalink
Port all code from PR #561 to current dev branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Zaedus committed Jun 2, 2023
1 parent 1bf74ce commit 2c5df0b
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 9 deletions.
38 changes: 29 additions & 9 deletions src/app/components/details/album_header.blp
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,38 @@ template $AlbumHeaderWidget : Box {
}
}

Button like_button {
receives-default: true;
halign: center;
valign: center;
tooltip-text: "Add to Library";
Box {
orientation: horizontal;

styles [
"circular",
"like__button",
]
spacing: 8;

Button play_button {
receives-default: true;
halign: center;
valign: center;
tooltip-text: "Play";
icon-name: "media-playback-start-symbolic";

styles [
"circular",
"play__button",
]
}

Button like_button {
receives-default: true;
halign: center;
valign: center;
tooltip-text: "Add to Library";

styles [
"circular",
"like__button",
]
}
}


styles [
"album__header",
]
Expand Down
30 changes: 30 additions & 0 deletions src/app/components/details/album_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::app::components::display_add_css_provider;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::{glib, CompositeTemplate};
use gettextrs::gettext;

mod imp {

Expand All @@ -19,6 +20,9 @@ mod imp {
#[template_child]
pub like_button: TemplateChild<gtk::Button>,

#[template_child]
pub play_button: TemplateChild<gtk::Button>,

#[template_child]
pub info_button: TemplateChild<gtk::Button>,

Expand Down Expand Up @@ -65,6 +69,13 @@ impl AlbumHeaderWidget {
glib::Object::new()
}

pub fn connect_play<F>(&self, f: F)
where
F: Fn() + 'static,
{
self.imp().play_button.connect_clicked(move |_| f());
}

pub fn connect_liked<F>(&self, f: F)
where
F: Fn() + 'static,
Expand Down Expand Up @@ -97,6 +108,25 @@ impl AlbumHeaderWidget {
});
}

pub fn set_playing(&self, is_playing: bool) {
let playback_icon = if is_playing {
"media-playback-pause-symbolic"
} else {
"media-playback-start-symbolic"
};

let translated_tooltip = if is_playing {
gettext("Pause")
} else {
gettext("Play")
};
let tooltip_text = Some(translated_tooltip.as_str());
let playback_control = imp::AlbumHeaderWidget::from_obj(self);

playback_control.play_button.set_icon_name(playback_icon);
playback_control.play_button.set_tooltip_text(tooltip_text);
}

pub fn set_artwork(&self, art: &gdk_pixbuf::Pixbuf) {
self.imp().album_art.set_from_pixbuf(Some(art));
}
Expand Down
30 changes: 30 additions & 0 deletions src/app/components/details/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ use super::DetailsModel;

use crate::app::components::{
Component, EventListener, HeaderBarComponent, HeaderBarWidget, Playlist, ScrollingHeaderWidget,
playlist::PlaylistModel
};
use crate::app::dispatch::Worker;
use crate::app::loader::ImageLoader;
use crate::app::state::PlaybackEvent;
use crate::app::{AppEvent, BrowserEvent};

mod imp {
Expand Down Expand Up @@ -123,6 +125,13 @@ impl AlbumDetailsWidget {
self.imp().header_mobile.connect_liked(f);
}

fn connect_play<F>(&self, f: F)
where
F: Fn() + Clone + 'static,
{
self.imp().header_widget.connect_play(f);
}

fn connect_info<F>(&self, f: F)
where
F: Fn() + Clone + 'static,
Expand All @@ -136,6 +145,10 @@ impl AlbumDetailsWidget {
self.imp().header_mobile.set_liked(is_liked);
}

fn set_playing(&self, is_playing: bool) {
self.imp().header_widget.set_playing(is_playing);
}

fn set_album_and_artist_and_year(&self, album: &str, artist: &str, year: Option<u32>) {
self.imp()
.header_widget
Expand Down Expand Up @@ -193,6 +206,8 @@ impl Details {

widget.connect_liked(clone!(@weak model => move || model.toggle_save_album()));

widget.connect_play(clone!(@weak model => move || model.toggle_play_album()));

widget.connect_header();

widget.connect_bottom_edge(clone!(@weak model => move || {
Expand Down Expand Up @@ -228,6 +243,14 @@ impl Details {
}
}

fn update_playing(&self, is_playing: bool) {
if !self.model.playlist_is_playing() {
self.widget.set_playing(false);
return;
}
self.widget.set_playing(is_playing);
}

fn update_details(&mut self) {
if let Some(album) = self.model.get_album_info() {
let details = &album.release_details;
Expand Down Expand Up @@ -290,13 +313,20 @@ impl EventListener for Details {
if id == &self.model.id =>
{
self.update_details();
self.update_playing(true);
}
AppEvent::BrowserEvent(BrowserEvent::AlbumSaved(id))
| AppEvent::BrowserEvent(BrowserEvent::AlbumUnsaved(id))
if id == &self.model.id =>
{
self.update_liked();
}
AppEvent::PlaybackEvent(PlaybackEvent::PlaybackPaused) => {
self.update_playing(false);
}
AppEvent::PlaybackEvent(PlaybackEvent::PlaybackResumed) => {
self.update_playing(true);
}
_ => {}
}
self.broadcast_event(event);
Expand Down
74 changes: 74 additions & 0 deletions src/app/components/details/details_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@ impl DetailsModel {
}
}

pub fn toggle_play_album(&self) {
if let Some(album) = self.get_album_description() {
if !self.playlist_is_playing() {
if self.state().playback.is_shuffled() {
self.dispatcher
.dispatch(AppAction::PlaybackAction(PlaybackAction::ToggleShuffle));
}
let id_of_first_song = album.songs.songs[0].id.as_str();
self.play_song_at(0, id_of_first_song);
return;
}
if self.state().playback.is_playing() {
self.dispatcher
.dispatch(AppAction::PlaybackAction(PlaybackAction::Pause));
} else {
self.dispatcher
.dispatch(AppAction::PlaybackAction(PlaybackAction::Play));
}
}
}

pub fn load_more(&self) -> Option<()> {
let last_batch = self.song_list_model().last_batch()?;
let query = BatchQuery {
Expand Down Expand Up @@ -176,6 +197,27 @@ impl PlaylistModel for DetailsModel {
self.state().playback.current_song_id()
}

fn playlist_song_ids(&self) -> Option<Vec<String>> {
if let Some(album) = self.get_album_description() {
let playlist_ids = album
.songs
.songs
.iter()
.map(|song| song.id.clone())
.collect::<Vec<_>>();
return Some(playlist_ids);
}
None
}

fn playlist_is_playing(&self) -> bool {
let current_song_id = self.state().playback.current_song_id();
if current_song_id.is_none() || self.playlist_song_ids().is_none() {
return false;
}
self.playlist_song_ids().unwrap().contains(&current_song_id.unwrap())
}

fn play_song_at(&self, pos: usize, id: &str) {
let source = SongsSource::Album(self.id.clone());
let batch = self.song_list_model().song_batch_for(pos);
Expand Down Expand Up @@ -218,6 +260,38 @@ impl PlaylistModel for DetailsModel {
menu.append(Some(&*labels::ADD_TO_QUEUE), Some("song.queue"));
Some(menu.upcast())
}

fn autoscroll_to_playing(&self) -> bool {
true
}

fn is_selection_enabled(&self) -> bool {
self.selection()
.map(|s| s.is_selection_enabled())
.unwrap_or(false)
}

fn song_state(&self, id: &str) -> SongState {
let is_playing = self.current_song_id().map(|s| s.eq(id)).unwrap_or(false);
let is_selected = self
.selection()
.map(|s| s.is_song_selected(id))
.unwrap_or(false);
SongState {
is_selected,
is_playing,
}
}

fn toggle_select(&self, id: &str) {
if let Some(selection) = self.selection() {
if selection.is_song_selected(id) {
self.deselect_song(id)
} else {
self.select_song(id);
}
}
}
}

impl SimpleHeaderBarModel for DetailsModel {
Expand Down
8 changes: 8 additions & 0 deletions src/app/components/playlist/playlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ pub trait PlaylistModel {

fn current_song_id(&self) -> Option<String>;

fn playlist_song_ids(&self) -> Option<Vec<String>> {
None
}

fn playlist_is_playing(&self) -> bool {
false
}

fn play_song_at(&self, pos: usize, id: &str);

fn autoscroll_to_playing(&self) -> bool {
Expand Down
21 changes: 21 additions & 0 deletions src/app/components/playlist_details/playlist_details_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,27 @@ impl PlaylistModel for PlaylistDetailsModel {
self.app_model.get_state().playback.current_song_id()
}

fn playlist_song_ids(&self) -> Option<Vec<String>> {
if let Some(playlist) = self.get_playlist_info() {
let playlist_ids = playlist
.songs
.songs
.iter()
.map(|song| song.id.clone())
.collect::<Vec<_>>();
return Some(playlist_ids);
}
None
}

fn playlist_is_playing(&self) -> bool {
let current_song_id = self.app_model.get_state().playback.current_song_id();
if current_song_id.is_none() || self.playlist_song_ids().is_none() {
return false;
}
self.playlist_song_ids().unwrap().contains(&current_song_id.unwrap())
}

fn play_song_at(&self, pos: usize, id: &str) {
let source = SongsSource::Playlist(self.id.clone());
let batch = self.song_list_model().song_batch_for(pos);
Expand Down

0 comments on commit 2c5df0b

Please sign in to comment.