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 @@
+
+
+
+
+
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()
+ }
+}