From eb77c97f9b902bd8a8e807ae43d704ea8f226ec5 Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Sat, 7 Nov 2020 20:07:26 +0100 Subject: [PATCH] Add player details screen --- res/musicus.gresource.xml | 1 + res/ui/player_screen.ui | 305 ++++++++++++++++++++++++++ src/dialogs/ensemble_selector.rs | 5 + src/dialogs/instrument_selector.rs | 5 + src/dialogs/part_editor.rs | 5 + src/dialogs/recording_editor.rs | 5 + src/dialogs/recording_selector.rs | 5 + src/dialogs/tracks_editor.rs | 1 + src/dialogs/work_editor.rs | 9 + src/dialogs/work_selector.rs | 10 + src/meson.build | 1 + src/player.rs | 66 +++--- src/screens/ensemble_screen.rs | 1 + src/screens/mod.rs | 3 + src/screens/person_screen.rs | 5 + src/screens/player_screen.rs | 336 +++++++++++++++++++++++++++++ src/screens/recording_screen.rs | 1 + src/screens/work_screen.rs | 1 + src/widgets/list.rs | 17 +- src/widgets/person_list.rs | 4 + src/widgets/player_bar.rs | 24 ++- src/widgets/poe_list.rs | 4 + src/widgets/selector_row.rs | 1 - src/window.rs | 20 +- 24 files changed, 792 insertions(+), 43 deletions(-) create mode 100644 res/ui/player_screen.ui create mode 100644 src/screens/player_screen.rs diff --git a/res/musicus.gresource.xml b/res/musicus.gresource.xml index b446cce..d64423c 100644 --- a/res/musicus.gresource.xml +++ b/res/musicus.gresource.xml @@ -13,6 +13,7 @@ ui/person_screen.ui ui/person_selector.ui ui/player_bar.ui + ui/player_screen.ui ui/poe_list.ui ui/preferences.ui ui/recording_editor.ui diff --git a/res/ui/player_screen.ui b/res/ui/player_screen.ui new file mode 100644 index 0000000..561f2e2 --- /dev/null +++ b/res/ui/player_screen.ui @@ -0,0 +1,305 @@ + + + + + + + True + False + media-playback-start-symbolic + + + 1 + 0.01 + 0.05 + + + True + False + vertical + + + True + False + Player + True + + + True + True + True + + + True + False + go-previous-symbolic + + + + + + + False + True + 0 + + + + + True + True + + + True + False + none + + + True + False + 12 + 12 + 12 + 12 + 18 + 12 + 800 + + + True + False + vertical + 12 + + + True + False + 12 + + + True + False + center + 6 + + + True + False + True + True + + + True + False + media-skip-backward-symbolic + + + + + False + True + 0 + + + + + True + True + True + + + True + False + media-playback-pause-symbolic + + + + + False + True + 1 + + + + + True + False + True + True + + + True + False + media-skip-forward-symbolic + + + + + False + True + 2 + + + + + False + True + 0 + + + + + True + False + vertical + + + True + False + start + Title + end + + + + + + False + True + 0 + + + + + True + False + start + Subtitle + end + + + False + True + 1 + + + + + True + True + 1 + + + + + True + True + True + center + + + True + False + media-playback-stop-symbolic + + + + + False + True + 2 + + + + + False + True + 0 + + + + + True + False + 6 + + + True + False + 0:00 + + + False + True + 0 + + + + + True + True + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + position + off + 1 + False + + + True + True + 1 + + + + + True + False + 0:00 + + + False + True + 2 + + + + + False + True + 1 + + + + + True + False + 0 + in + + + + + + + + + False + True + 2 + + + + + + + + + + + True + True + 1 + + + + diff --git a/src/dialogs/ensemble_selector.rs b/src/dialogs/ensemble_selector.rs index a96f980..0d64225 100644 --- a/src/dialogs/ensemble_selector.rs +++ b/src/dialogs/ensemble_selector.rs @@ -49,6 +49,11 @@ where for (index, ensemble) in ensembles.iter().enumerate() { let label = gtk::Label::new(Some(&ensemble.name)); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + let row = SelectorRow::new(index.try_into().unwrap(), &label); row.show_all(); clone.list.insert(&row, -1); diff --git a/src/dialogs/instrument_selector.rs b/src/dialogs/instrument_selector.rs index 9b19895..381ce23 100644 --- a/src/dialogs/instrument_selector.rs +++ b/src/dialogs/instrument_selector.rs @@ -49,6 +49,11 @@ where for (index, instrument) in instruments.iter().enumerate() { let label = gtk::Label::new(Some(&instrument.name)); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + let row = SelectorRow::new(index.try_into().unwrap(), &label); row.show_all(); clone.list.insert(&row, -1); diff --git a/src/dialogs/part_editor.rs b/src/dialogs/part_editor.rs index bd65edf..f7043d8 100644 --- a/src/dialogs/part_editor.rs +++ b/src/dialogs/part_editor.rs @@ -140,6 +140,11 @@ impl PartEditor { for (index, instrument) in self.instruments.borrow().iter().enumerate() { let label = gtk::Label::new(Some(&instrument.name)); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + let row = SelectorRow::new(index.try_into().unwrap(), &label); row.show_all(); self.instrument_list.insert(&row, -1); diff --git a/src/dialogs/recording_editor.rs b/src/dialogs/recording_editor.rs index 9b6fce1..5a73260 100644 --- a/src/dialogs/recording_editor.rs +++ b/src/dialogs/recording_editor.rs @@ -163,6 +163,11 @@ where for (index, performer) in self.performers.borrow().iter().enumerate() { let label = gtk::Label::new(Some(&performer.get_title())); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + let row = SelectorRow::new(index.try_into().unwrap(), &label); row.show_all(); self.performer_list.insert(&row, -1); diff --git a/src/dialogs/recording_selector.rs b/src/dialogs/recording_selector.rs index 7852f2b..411764d 100644 --- a/src/dialogs/recording_selector.rs +++ b/src/dialogs/recording_selector.rs @@ -110,6 +110,10 @@ impl RecordingSelectorPersonScreen { |work: &WorkDescription| { let label = gtk::Label::new(Some(&work.title)); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); label.upcast() }, |_| true, @@ -213,6 +217,7 @@ impl RecordingSelectorWorkScreen { performers_label.set_halign(gtk::Align::Start); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + vbox.set_border_width(6); vbox.add(&work_label); vbox.add(&performers_label); diff --git a/src/dialogs/tracks_editor.rs b/src/dialogs/tracks_editor.rs index 34663c7..31f0168 100644 --- a/src/dialogs/tracks_editor.rs +++ b/src/dialogs/tracks_editor.rs @@ -71,6 +71,7 @@ impl TracksEditor { file_name_label.set_halign(gtk::Align::Start); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + vbox.set_border_width(6); vbox.add(&title_label); vbox.add(&file_name_label); diff --git a/src/dialogs/work_editor.rs b/src/dialogs/work_editor.rs index 53ceaf8..3deaf02 100644 --- a/src/dialogs/work_editor.rs +++ b/src/dialogs/work_editor.rs @@ -341,6 +341,11 @@ where for (index, instrument) in self.instruments.borrow().iter().enumerate() { let label = gtk::Label::new(Some(&instrument.name)); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + let row = SelectorRow::new(index.try_into().unwrap(), &label); row.show_all(); self.instrument_list.insert(&row, -1); @@ -365,6 +370,10 @@ where for (index, part) in self.structure.borrow().iter().enumerate() { let label = gtk::Label::new(Some(&part.get_title())); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); if part.is_part() { label.set_margin_start(6); diff --git a/src/dialogs/work_selector.rs b/src/dialogs/work_selector.rs index 457b15b..3a0419f 100644 --- a/src/dialogs/work_selector.rs +++ b/src/dialogs/work_selector.rs @@ -142,6 +142,11 @@ where for (index, person) in persons.iter().enumerate() { let label = gtk::Label::new(Some(&person.name_lf())); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + let row = SelectorRow::new(index.try_into().unwrap(), &label); row.show_all(); self.person_list.insert(&row, -1); @@ -207,6 +212,11 @@ where for (index, work) in works.iter().enumerate() { let label = gtk::Label::new(Some(&work.title)); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + let row = SelectorRow::new(index.try_into().unwrap(), &label); row.show_all(); self.work_list.insert(&row, -1); diff --git a/src/meson.build b/src/meson.build index b7a72cf..2660253 100644 --- a/src/meson.build +++ b/src/meson.build @@ -57,6 +57,7 @@ sources = files( 'screens/ensemble_screen.rs', 'screens/mod.rs', 'screens/person_screen.rs', + 'screens/player_screen.rs', 'screens/recording_screen.rs', 'screens/work_screen.rs', 'widgets/list.rs', diff --git a/src/player.rs b/src/player.rs index bfb0bf9..84ca301 100644 --- a/src/player.rs +++ b/src/player.rs @@ -19,11 +19,11 @@ pub struct Player { current_item: Cell>, current_track: Cell>, playing: Cell, - playlist_cb: RefCell) -> ()>>>, - track_cb: RefCell ()>>>, - duration_cb: RefCell ()>>>, - playing_cb: RefCell ()>>>, - position_cb: RefCell ()>>>, + playlist_cbs: RefCell) -> ()>>>, + track_cbs: RefCell ()>>>, + duration_cbs: RefCell ()>>>, + playing_cbs: RefCell ()>>>, + position_cbs: RefCell ()>>>, } impl Player { @@ -42,11 +42,11 @@ impl Player { current_item: Cell::new(None), current_track: Cell::new(None), playing: Cell::new(false), - playlist_cb: RefCell::new(None), - track_cb: RefCell::new(None), - duration_cb: RefCell::new(None), - playing_cb: RefCell::new(None), - position_cb: RefCell::new(None), + playlist_cbs: RefCell::new(Vec::new()), + track_cbs: RefCell::new(Vec::new()), + duration_cbs: RefCell::new(Vec::new()), + playing_cbs: RefCell::new(Vec::new()), + position_cbs: RefCell::new(Vec::new()), }); let clone = fragile::Fragile::new(result.clone()); @@ -56,8 +56,8 @@ impl Player { clone.next().unwrap(); } else { clone.player.stop(); - - if let Some(cb) = &*clone.playing_cb.borrow() { + clone.playing.replace(false); + for cb in &*clone.playing_cbs.borrow() { cb(false); } } @@ -65,14 +65,14 @@ impl Player { let clone = fragile::Fragile::new(result.clone()); player.connect_position_updated(move |_, position| { - if let Some(cb) = &*clone.get().position_cb.borrow() { + for cb in &*clone.get().position_cbs.borrow() { cb(position.mseconds().unwrap()); } }); let clone = fragile::Fragile::new(result.clone()); player.connect_duration_changed(move |_, duration| { - if let Some(cb) = &*clone.get().duration_cb.borrow() { + for cb in &*clone.get().duration_cbs.borrow() { cb(duration.mseconds().unwrap()); } }); @@ -80,24 +80,24 @@ impl Player { result } - pub fn set_playlist_cb) -> () + 'static>(&self, cb: F) { - self.playlist_cb.replace(Some(Box::new(cb))); + pub fn add_playlist_cb) -> () + 'static>(&self, cb: F) { + self.playlist_cbs.borrow_mut().push(Box::new(cb)); } - pub fn set_track_cb () + 'static>(&self, cb: F) { - self.track_cb.replace(Some(Box::new(cb))); + pub fn add_track_cb () + 'static>(&self, cb: F) { + self.track_cbs.borrow_mut().push(Box::new(cb)); } - pub fn set_duration_cb () + 'static>(&self, cb: F) { - self.duration_cb.replace(Some(Box::new(cb))); + pub fn add_duration_cb () + 'static>(&self, cb: F) { + self.duration_cbs.borrow_mut().push(Box::new(cb)); } - pub fn set_playing_cb () + 'static>(&self, cb: F) { - self.playing_cb.replace(Some(Box::new(cb))); + pub fn add_playing_cb () + 'static>(&self, cb: F) { + self.playing_cbs.borrow_mut().push(Box::new(cb)); } - pub fn set_position_cb () + 'static>(&self, cb: F) { - self.position_cb.replace(Some(Box::new(cb))); + pub fn add_position_cb () + 'static>(&self, cb: F) { + self.position_cbs.borrow_mut().push(Box::new(cb)); } pub fn get_playlist(&self) -> Vec { @@ -135,7 +135,7 @@ impl Player { was_empty }; - if let Some(cb) = &*self.playlist_cb.borrow() { + for cb in &*self.playlist_cbs.borrow() { cb(self.playlist.borrow().clone()); } @@ -144,7 +144,7 @@ impl Player { self.player.play(); self.playing.set(true); - if let Some(cb) = &*self.playing_cb.borrow() { + for cb in &*self.playing_cbs.borrow() { cb(true); } } @@ -158,19 +158,23 @@ impl Player { self.player.pause(); self.playing.set(false); - if let Some(cb) = &*self.playing_cb.borrow() { + for cb in &*self.playing_cbs.borrow() { cb(false); } } else { self.player.play(); self.playing.set(true); - if let Some(cb) = &*self.playing_cb.borrow() { + for cb in &*self.playing_cbs.borrow() { cb(true); } } } + pub fn seek(&self, ms: u64) { + self.player.seek(gstreamer::ClockTime::from_mseconds(ms)); + } + pub fn has_previous(&self) -> bool { if let Some(current_item) = self.current_item.get() { if let Some(current_track) = self.current_track.get() { @@ -266,7 +270,7 @@ impl Player { self.current_item.set(Some(current_item)); self.current_track.set(Some(current_track)); - if let Some(cb) = &*self.track_cb.borrow() { + for cb in &*self.track_cbs.borrow() { cb(current_item, current_track); } @@ -280,11 +284,11 @@ impl Player { self.current_track.set(None); self.playlist.replace(Vec::new()); - if let Some(cb) = &*self.playing_cb.borrow() { + for cb in &*self.playing_cbs.borrow() { cb(false); } - if let Some(cb) = &*self.playlist_cb.borrow() { + for cb in &*self.playlist_cbs.borrow() { cb(Vec::new()); } } diff --git a/src/screens/ensemble_screen.rs b/src/screens/ensemble_screen.rs index 4808868..3501229 100644 --- a/src/screens/ensemble_screen.rs +++ b/src/screens/ensemble_screen.rs @@ -63,6 +63,7 @@ impl EnsembleScreen { performers_label.set_halign(gtk::Align::Start); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + vbox.set_border_width(6); vbox.add(&work_label); vbox.add(&performers_label); diff --git a/src/screens/mod.rs b/src/screens/mod.rs index 8efd741..5543659 100644 --- a/src/screens/mod.rs +++ b/src/screens/mod.rs @@ -4,6 +4,9 @@ pub use ensemble_screen::*; pub mod person_screen; pub use person_screen::*; +pub mod player_screen; +pub use player_screen::*; + pub mod work_screen; pub use work_screen::*; diff --git a/src/screens/person_screen.rs b/src/screens/person_screen.rs index dee7fb9..fd8c855 100644 --- a/src/screens/person_screen.rs +++ b/src/screens/person_screen.rs @@ -58,6 +58,10 @@ impl PersonScreen { |work: &WorkDescription| { let label = gtk::Label::new(Some(&work.title)); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); label.upcast() }, clone!(@strong search_entry => move |work: &WorkDescription| { @@ -81,6 +85,7 @@ impl PersonScreen { performers_label.set_halign(gtk::Align::Start); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + vbox.set_border_width(6); vbox.add(&work_label); vbox.add(&performers_label); diff --git a/src/screens/player_screen.rs b/src/screens/player_screen.rs new file mode 100644 index 0000000..d868716 --- /dev/null +++ b/src/screens/player_screen.rs @@ -0,0 +1,336 @@ +use crate::player::*; +use crate::widgets::*; +use gettextrs::gettext; +use glib::clone; +use gtk::prelude::*; +use gtk_macros::get_widget; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; + +struct PlaylistElement { + pub item: usize, + pub track: usize, + pub title: String, + pub subtitle: Option, + pub playable: bool, +} + +pub struct PlayerScreen { + pub widget: gtk::Box, + title_label: gtk::Label, + subtitle_label: gtk::Label, + previous_button: gtk::Button, + play_button: gtk::Button, + next_button: gtk::Button, + position_label: gtk::Label, + position: gtk::Adjustment, + duration_label: gtk::Label, + play_image: gtk::Image, + pause_image: gtk::Image, + list: Rc>, + player: Rc>>>, + seeking: Rc>, + current_item: Rc>, + current_track: Rc>, + back_cb: Rc ()>>>>, +} + +impl PlayerScreen { + pub fn new() -> Self { + let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/player_screen.ui"); + + get_widget!(builder, gtk::Box, widget); + get_widget!(builder, gtk::Button, back_button); + get_widget!(builder, gtk::Label, title_label); + get_widget!(builder, gtk::Label, subtitle_label); + get_widget!(builder, gtk::Button, previous_button); + get_widget!(builder, gtk::Button, play_button); + get_widget!(builder, gtk::Button, next_button); + get_widget!(builder, gtk::Button, stop_button); + get_widget!(builder, gtk::Label, position_label); + get_widget!(builder, gtk::Scale, position_scale); + get_widget!(builder, gtk::Adjustment, position); + get_widget!(builder, gtk::Label, duration_label); + get_widget!(builder, gtk::Image, play_image); + get_widget!(builder, gtk::Image, pause_image); + get_widget!(builder, gtk::Frame, frame); + + let back_cb = Rc::new(RefCell::new(None:: ()>>)); + + back_button.connect_clicked(clone!(@strong back_cb => move |_| { + if let Some(cb) = &*back_cb.borrow() { + cb(); + } + })); + + let player = Rc::new(RefCell::new(None::>)); + let seeking = Rc::new(Cell::new(false)); + + previous_button.connect_clicked(clone!(@strong player => move |_| { + if let Some(player) = &*player.borrow() { + player.previous().unwrap(); + } + })); + + play_button.connect_clicked(clone!(@strong player => move |_| { + if let Some(player) = &*player.borrow() { + player.play_pause(); + } + })); + + next_button.connect_clicked(clone!(@strong player => move |_| { + if let Some(player) = &*player.borrow() { + player.next().unwrap(); + } + })); + + stop_button.connect_clicked(clone!(@strong player, @strong back_cb => move |_| { + if let Some(player) = &*player.borrow() { + if let Some(cb) = &*back_cb.borrow() { + cb(); + } + + player.clear(); + } + })); + + position_scale.connect_button_press_event(clone!(@strong seeking => move |_, _| { + seeking.replace(true); + Inhibit(false) + })); + + position_scale.connect_button_release_event( + clone!(@strong seeking, @strong position, @strong player => move |_, _| { + if let Some(player) = &*player.borrow() { + player.seek(position.get_value() as u64); + } + + seeking.replace(false); + Inhibit(false) + }), + ); + + position_scale.connect_value_changed( + clone!(@strong seeking, @strong position, @strong position_label => move |_| { + if seeking.get() { + let ms = position.get_value() as u64; + let min = ms / 60000; + let sec = (ms % 60000) / 1000; + position_label.set_text(&format!("{}:{:02}", min, sec)); + } + }), + ); + + let current_item = Rc::new(Cell::::new(0)); + let current_track = Rc::new(Cell::::new(0)); + let list = List::new( + clone!( + @strong current_item, + @strong current_track + => move |element: &PlaylistElement| { + let title_label = gtk::Label::new(Some(&element.title)); + title_label.set_ellipsize(pango::EllipsizeMode::End); + title_label.set_halign(gtk::Align::Start); + let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + vbox.add(&title_label); + if let Some(subtitle) = &element.subtitle { + let subtitle_label = gtk::Label::new(Some(&subtitle)); + subtitle_label.set_ellipsize(pango::EllipsizeMode::End); + subtitle_label.set_halign(gtk::Align::Start); + subtitle_label.set_opacity(0.5); + vbox.add(&subtitle_label); + } + + let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 6); + hbox.set_border_width(6); + + if element.playable { + let image = gtk::Image::new(); + + if element.item == current_item.get() && element.track == current_track.get() { + image.set_from_icon_name( + Some("media-playback-start-symbolic"), + gtk::IconSize::Button, + ); + } + + hbox.add(&image); + } else if element.item > 0 { + hbox.set_margin_top(18); + } + hbox.add(&vbox); + hbox.upcast() + } + ), + |_| true, + "", + ); + + list.set_selected(clone!(@strong player => move |element| { + if let Some(player) = &*player.borrow() { + player.set_track(element.item, element.track).unwrap(); + } + })); + + frame.add(&list.widget); + + Self { + widget, + title_label, + subtitle_label, + previous_button, + play_button, + next_button, + position_label, + position, + duration_label, + play_image, + pause_image, + list, + player, + seeking, + current_item, + current_track, + back_cb, + } + } + + pub fn set_player(&self, player: Option>) { + self.player.replace(player.clone()); + + if let Some(player) = player { + let playlist = Rc::new(RefCell::new(Vec::::new())); + + player.add_playlist_cb(clone!( + @strong player, + @strong self.previous_button as previous_button, + @strong self.next_button as next_button, + @strong self.list as list, + @strong playlist + => move |new_playlist| { + playlist.replace(new_playlist); + previous_button.set_sensitive(player.has_previous()); + next_button.set_sensitive(player.has_next()); + + let mut elements = Vec::new(); + for (item_index, item) in playlist.borrow().iter().enumerate() { + elements.push(PlaylistElement { + item: item_index, + track: 0, + title: item.recording.work.get_title(), + subtitle: Some(item.recording.get_performers()), + playable: false, + }); + + for (track_index, track) in item.tracks.iter().enumerate() { + let mut parts = Vec::::new(); + for part in &track.work_parts { + parts.push(item.recording.work.parts[*part].title.clone()); + } + + let title = if parts.is_empty() { + gettext("Unknown") + } else { + parts.join(", ") + }; + + elements.push(PlaylistElement { + item: item_index, + track: track_index, + title: title, + subtitle: None, + playable: true, + }); + } + } + + list.show_items(elements); + } + )); + + player.add_track_cb(clone!( + @strong player, + @strong playlist, + @strong self.previous_button as previous_button, + @strong self.next_button as next_button, + @strong self.title_label as title_label, + @strong self.subtitle_label as subtitle_label, + @strong self.position_label as position_label, + @strong self.current_item as self_item, + @strong self.current_track as self_track, + @strong self.list as list + => move |current_item, current_track| { + previous_button.set_sensitive(player.has_previous()); + next_button.set_sensitive(player.has_next()); + + let item = &playlist.borrow()[current_item]; + let track = &item.tracks[current_track]; + + let mut parts = Vec::::new(); + for part in &track.work_parts { + parts.push(item.recording.work.parts[*part].title.clone()); + } + + let mut title = item.recording.work.get_title(); + if !parts.is_empty() { + title = format!("{}: {}", title, parts.join(", ")); + } + + title_label.set_text(&title); + subtitle_label.set_text(&item.recording.get_performers()); + position_label.set_text("0:00"); + + self_item.replace(current_item); + self_track.replace(current_track); + list.update(); + } + )); + + player.add_duration_cb(clone!( + @strong self.duration_label as duration_label, + @strong self.position as position + => move |ms| { + let min = ms / 60000; + let sec = (ms % 60000) / 1000; + duration_label.set_text(&format!("{}:{:02}", min, sec)); + position.set_upper(ms as f64); + } + )); + + player.add_playing_cb(clone!( + @strong self.play_button as play_button, + @strong self.play_image as play_image, + @strong self.pause_image as pause_image + => move |playing| { + if let Some(child) = play_button.get_child() { + play_button.remove( &child); + } + + play_button.add(if playing { + &pause_image + } else { + &play_image + }); + } + )); + + player.add_position_cb(clone!( + @strong self.position_label as position_label, + @strong self.position as position, + @strong self.seeking as seeking + => move |ms| { + if !seeking.get() { + let min = ms / 60000; + let sec = (ms % 60000) / 1000; + position_label.set_text(&format!("{}:{:02}", min, sec)); + position.set_value(ms as f64); + } + } + )); + } + } + + pub fn set_back_cb () + 'static>(&self, cb: F) { + self.back_cb.replace(Some(Box::new(cb))); + } +} diff --git a/src/screens/recording_screen.rs b/src/screens/recording_screen.rs index c852de9..e73fd7e 100644 --- a/src/screens/recording_screen.rs +++ b/src/screens/recording_screen.rs @@ -75,6 +75,7 @@ impl RecordingScreen { file_name_label.set_halign(gtk::Align::Start); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + vbox.set_border_width(6); vbox.add(&title_label); vbox.add(&file_name_label); diff --git a/src/screens/work_screen.rs b/src/screens/work_screen.rs index 932383c..b2b1013 100644 --- a/src/screens/work_screen.rs +++ b/src/screens/work_screen.rs @@ -64,6 +64,7 @@ impl WorkScreen { performers_label.set_halign(gtk::Align::Start); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + vbox.set_border_width(6); vbox.add(&work_label); vbox.add(&performers_label); diff --git a/src/widgets/list.rs b/src/widgets/list.rs index bed3b72..13fd74d 100644 --- a/src/widgets/list.rs +++ b/src/widgets/list.rs @@ -88,12 +88,23 @@ where } pub fn select_index(&self, index: usize) { - self.widget.select_row(self.widget.get_row_at_index(index.try_into().unwrap()).as_ref()); + self.widget.select_row( + self.widget + .get_row_at_index(index.try_into().unwrap()) + .as_ref(), + ); } pub fn show_items(&self, items: Vec) { self.items.replace(items); + self.update(); + } + pub fn invalidate_filter(&self) { + self.widget.invalidate_filter(); + } + + pub fn update(&self) { for child in self.widget.get_children() { self.widget.remove(&child); } @@ -105,10 +116,6 @@ where } } - pub fn invalidate_filter(&self) { - self.widget.invalidate_filter(); - } - pub fn clear_selection(&self) { self.widget.unselect_all(); } diff --git a/src/widgets/person_list.rs b/src/widgets/person_list.rs index 3a5bd79..df67a3d 100644 --- a/src/widgets/person_list.rs +++ b/src/widgets/person_list.rs @@ -27,6 +27,10 @@ impl PersonList { |person: &Person| { let label = gtk::Label::new(Some(&person.name_lf())); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); label.upcast() }, clone!(@strong search_entry => move |person: &Person| { diff --git a/src/widgets/player_bar.rs b/src/widgets/player_bar.rs index edd07a8..9d2cf90 100644 --- a/src/widgets/player_bar.rs +++ b/src/widgets/player_bar.rs @@ -17,6 +17,7 @@ pub struct PlayerBar { play_image: gtk::Image, pause_image: gtk::Image, player: Rc>>>, + playlist_cb: Rc ()>>>>, } impl PlayerBar { @@ -31,10 +32,12 @@ impl PlayerBar { get_widget!(builder, gtk::Button, next_button); get_widget!(builder, gtk::Label, position_label); get_widget!(builder, gtk::Label, duration_label); + get_widget!(builder, gtk::Button, playlist_button); get_widget!(builder, gtk::Image, play_image); get_widget!(builder, gtk::Image, pause_image); let player = Rc::new(RefCell::new(None::>)); + let playlist_cb = Rc::new(RefCell::new(None:: ()>>)); previous_button.connect_clicked(clone!(@strong player => move |_| { if let Some(player) = &*player.borrow() { @@ -54,6 +57,12 @@ impl PlayerBar { } })); + playlist_button.connect_clicked(clone!(@strong playlist_cb => move |_| { + if let Some(cb) = &*playlist_cb.borrow() { + cb(); + } + })); + Self { widget, title_label, @@ -66,6 +75,7 @@ impl PlayerBar { play_image, pause_image, player: player, + playlist_cb: playlist_cb, } } @@ -75,7 +85,7 @@ impl PlayerBar { if let Some(player) = player { let playlist = Rc::new(RefCell::new(Vec::::new())); - player.set_playlist_cb(clone!( + player.add_playlist_cb(clone!( @strong player, @strong self.widget as widget, @strong self.previous_button as previous_button, @@ -89,7 +99,7 @@ impl PlayerBar { } )); - player.set_track_cb(clone!( + player.add_track_cb(clone!( @strong player, @strong playlist, @strong self.previous_button as previous_button, @@ -120,7 +130,7 @@ impl PlayerBar { } )); - player.set_duration_cb(clone!( + player.add_duration_cb(clone!( @strong self.duration_label as duration_label => move |ms| { let min = ms / 60000; @@ -129,7 +139,7 @@ impl PlayerBar { } )); - player.set_playing_cb(clone!( + player.add_playing_cb(clone!( @strong self.play_button as play_button, @strong self.play_image as play_image, @strong self.pause_image as pause_image @@ -146,7 +156,7 @@ impl PlayerBar { } )); - player.set_position_cb(clone!( + player.add_position_cb(clone!( @strong self.position_label as position_label => move |ms| { let min = ms / 60000; @@ -158,4 +168,8 @@ impl PlayerBar { self.widget.set_reveal_child(false); } } + + pub fn set_playlist_cb () + 'static>(&self, cb: F) { + self.playlist_cb.replace(Some(Box::new(cb))); + } } diff --git a/src/widgets/poe_list.rs b/src/widgets/poe_list.rs index 317eda2..53b72bc 100644 --- a/src/widgets/poe_list.rs +++ b/src/widgets/poe_list.rs @@ -42,6 +42,10 @@ impl PoeList { |poe: &PersonOrEnsemble| { let label = gtk::Label::new(Some(&poe.get_title())); label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); label.upcast() }, clone!(@strong search_entry => move |poe: &PersonOrEnsemble| { diff --git a/src/widgets/selector_row.rs b/src/widgets/selector_row.rs index 41d3b39..a00f5c7 100644 --- a/src/widgets/selector_row.rs +++ b/src/widgets/selector_row.rs @@ -90,7 +90,6 @@ impl ObjectImpl for SelectorRowPriv { self.parent_constructed(object); let row = object.downcast_ref::().unwrap(); - row.set_border_width(6); let child = self.child.borrow(); match child.as_ref() { diff --git a/src/window.rs b/src/window.rs index b433d74..a5b9bd2 100644 --- a/src/window.rs +++ b/src/window.rs @@ -20,6 +20,7 @@ pub struct Window { poe_list: Rc, navigator: Rc, player_bar: PlayerBar, + player_screen: PlayerScreen, } impl Window { @@ -38,6 +39,9 @@ impl Window { let backend = Rc::new(Backend::new()); backend.clone().init(); + let player_screen = PlayerScreen::new(); + stack.add_named(&player_screen.widget, "player_screen"); + let poe_list = PoeList::new(backend.clone()); let navigator = Navigator::new(&empty_screen); navigator.set_back_cb(clone!(@strong leaflet, @strong sidebar_box => move || { @@ -56,6 +60,7 @@ impl Window { poe_list, navigator, player_bar, + player_screen, }); result.window.set_application(Some(app)); @@ -85,6 +90,18 @@ impl Window { })).show(); })); + result + .player_bar + .set_playlist_cb(clone!(@strong result => move || { + result.stack.set_visible_child_name("player_screen"); + })); + + result + .player_screen + .set_back_cb(clone!(@strong result => move || { + result.stack.set_visible_child_name("content"); + })); + action!( result.window, "preferences", @@ -298,7 +315,8 @@ impl Window { clone.poe_list.clone().reload(); let player = clone.backend.get_player().unwrap(); - clone.player_bar.set_player(Some(player)); + clone.player_bar.set_player(Some(player.clone())); + clone.player_screen.set_player(Some(player)); } } }