diff --git a/musicus/res/musicus.gresource.xml b/musicus/res/musicus.gresource.xml index 1cf3500..1811484 100644 --- a/musicus/res/musicus.gresource.xml +++ b/musicus/res/musicus.gresource.xml @@ -17,6 +17,7 @@ ui/selector.ui ui/source_selector.ui ui/track_editor.ui + ui/track_row.ui ui/track_selector.ui ui/track_set_editor.ui ui/work_editor.ui diff --git a/musicus/res/ui/track_row.ui b/musicus/res/ui/track_row.ui new file mode 100644 index 0000000..1ca859b --- /dev/null +++ b/musicus/res/ui/track_row.ui @@ -0,0 +1,65 @@ + + + + + + + horizontal + 12 + 6 + 6 + + + + + media-playback-start-symbolic + 6 + 12 + 18 + start + + + + + + + vertical + true + + + vertical + false + 12 + + + true + 0.0 + + + + + + + + true + 0.0 + + + + + + + + true + 0.0 + 12 + + + + + + + + diff --git a/musicus/src/screens/player.rs b/musicus/src/screens/player.rs index 07e97b5..acc5075 100644 --- a/musicus/src/screens/player.rs +++ b/musicus/src/screens/player.rs @@ -1,7 +1,6 @@ use crate::navigator::{NavigationHandle, Screen}; -use crate::widgets::{List, Widget}; +use crate::widgets::{List, TrackRow, Widget}; use adw::prelude::*; -use gettextrs::gettext; use glib::clone; use gtk_macros::get_widget; use musicus_backend::db::Track; @@ -9,6 +8,7 @@ use std::cell::{Cell, RefCell}; use std::rc::Rc; /// Elements for visually representing the playlist. +#[derive(Clone)] enum ListItem { /// A playable track. Track { @@ -207,63 +207,35 @@ impl Screen<(), ()> for PlayerScreen { } })); - this.list.set_make_widget_cb(clone!(@weak this => @default-panic, move |index| { - let widget = match this.items.borrow()[index] { - ListItem::Track {index, first, playing} => { - let track = &this.playlist.borrow()[index]; - - let mut parts = Vec::::new(); - for part in &track.work_parts { - parts.push(track.recording.work.parts[*part].title.clone()); + this.list + .set_make_widget_cb(clone!(@weak this => @default-panic, move |index| { + let widget = match this.items.borrow()[index] { + ListItem::Track {index, first, playing} => { + let track = &this.playlist.borrow()[index]; + TrackRow::new(track, first, playing).get_widget() } - - let title = if first { - track.recording.work.get_title() - } else if parts.is_empty() { - gettext("Unknown") - } else { - parts.join(", ") - }; - - let row = adw::ActionRowBuilder::new() - .selectable(false) - .activatable(true) - .title(&title) - .build(); - - if first { - let subtitle = if !parts.is_empty() { - format!("{}\n{}", track.recording.get_performers(), parts.join(", ")) - } else { - track.recording.get_performers() - }; - - row.set_subtitle(&subtitle); + ListItem::Separator => { + gtk::ListBoxRowBuilder::new() + .selectable(false) + .activatable(false) + .child(>k::Separator::new(gtk::Orientation::Horizontal)) + .build() + .upcast() } + }; - row.connect_activated(clone!(@weak this => move |_| { - this.handle.backend.pl().set_track(index).unwrap(); - })); + widget + })); - let icon = if playing { - Some("media-playback-start-symbolic") - } else { - None - }; - - let image = gtk::Image::from_icon_name(icon); - row.add_prefix(&image); - - row.upcast() - } - ListItem::Separator => { - let separator = gtk::Separator::new(gtk::Orientation::Horizontal); - separator.upcast() - } - }; - - widget - })); + this.list + .widget + .connect_row_activated(clone!(@weak this => move |_, row| { + let list_index = row.index(); + let list_item = this.items.borrow()[list_index as usize].clone(); + if let ListItem::Track {index, ..} = list_item { + this.handle.backend.pl().set_track(index).unwrap(); + }; + })); player.send_data(); diff --git a/musicus/src/widgets/mod.rs b/musicus/src/widgets/mod.rs index c23c8ad..f11bb20 100644 --- a/musicus/src/widgets/mod.rs +++ b/musicus/src/widgets/mod.rs @@ -21,6 +21,9 @@ pub use screen::*; pub mod section; pub use section::*; +pub mod track_row; +pub use track_row::TrackRow; + mod indexed_list_model; /// Something that can be represented as a GTK widget. diff --git a/musicus/src/widgets/track_row.rs b/musicus/src/widgets/track_row.rs new file mode 100644 index 0000000..3df9b6b --- /dev/null +++ b/musicus/src/widgets/track_row.rs @@ -0,0 +1,54 @@ +use super::Widget; +use gtk::prelude::*; +use gtk_macros::get_widget; +use musicus_backend::db::Track; + +/// A widget for showing a single track in a list. +pub struct TrackRow { + /// The actual GTK widget. + pub widget: gtk::ListBoxRow, +} + +impl TrackRow { + /// Create a new track row. + pub fn new(track: &Track, show_header: bool, playing: bool) -> Self { + let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_row.ui"); + + get_widget!(builder, gtk::ListBoxRow, widget); + get_widget!(builder, gtk::Revealer, playing_revealer); + get_widget!(builder, gtk::Image, playing_image); + get_widget!(builder, gtk::Box, header_box); + get_widget!(builder, gtk::Label, work_title_label); + get_widget!(builder, gtk::Label, performances_label); + get_widget!(builder, gtk::Label, track_title_label); + + playing_revealer.set_reveal_child(playing); + + let mut parts = Vec::<&str>::new(); + for part in &track.work_parts { + parts.push(&track.recording.work.parts[*part].title); + } + + if parts.is_empty() || show_header { + work_title_label.set_text(&track.recording.work.get_title()); + performances_label.set_text(&track.recording.get_performers()); + header_box.show(); + } else { + playing_image.set_margin_top(0); + } + + if !parts.is_empty() { + track_title_label.set_text(&parts.join(", ")); + } else { + track_title_label.hide(); + } + + Self { widget } + } +} + +impl Widget for TrackRow { + fn get_widget(&self) -> gtk::Widget { + self.widget.clone().upcast() + } +}