2021-01-29 14:42:59 +01:00
|
|
|
use super::source::Source;
|
2021-01-13 19:27:22 +01:00
|
|
|
use crate::widgets::{Navigator, NavigatorScreen};
|
|
|
|
|
use glib::clone;
|
|
|
|
|
use gtk::prelude::*;
|
|
|
|
|
use gtk_macros::get_widget;
|
2021-01-29 15:17:27 +01:00
|
|
|
use libadwaita::prelude::*;
|
2021-01-13 19:27:22 +01:00
|
|
|
use std::cell::RefCell;
|
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
2021-01-29 14:42:59 +01:00
|
|
|
/// A screen for selecting tracks from a source.
|
2021-01-13 19:27:22 +01:00
|
|
|
pub struct TrackSelector {
|
2021-01-29 14:42:59 +01:00
|
|
|
source: Rc<Box<dyn Source>>,
|
2021-01-13 19:27:22 +01:00
|
|
|
widget: gtk::Box,
|
|
|
|
|
select_button: gtk::Button,
|
|
|
|
|
selection: RefCell<Vec<usize>>,
|
|
|
|
|
selected_cb: RefCell<Option<Box<dyn Fn(Vec<usize>)>>>,
|
|
|
|
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TrackSelector {
|
|
|
|
|
/// Create a new track selector.
|
2021-01-29 14:42:59 +01:00
|
|
|
pub fn new(source: Rc<Box<dyn Source>>) -> Rc<Self> {
|
2021-01-13 19:27:22 +01:00
|
|
|
// Create UI
|
|
|
|
|
|
|
|
|
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_selector.ui");
|
|
|
|
|
|
|
|
|
|
get_widget!(builder, gtk::Box, widget);
|
|
|
|
|
get_widget!(builder, gtk::Button, back_button);
|
|
|
|
|
get_widget!(builder, gtk::Button, select_button);
|
|
|
|
|
get_widget!(builder, gtk::Frame, tracks_frame);
|
|
|
|
|
|
|
|
|
|
let track_list = gtk::ListBox::new();
|
|
|
|
|
track_list.set_selection_mode(gtk::SelectionMode::None);
|
|
|
|
|
track_list.set_vexpand(false);
|
|
|
|
|
track_list.show();
|
2021-01-25 14:00:57 +01:00
|
|
|
tracks_frame.set_child(Some(&track_list));
|
2021-01-13 19:27:22 +01:00
|
|
|
|
|
|
|
|
let this = Rc::new(Self {
|
|
|
|
|
source,
|
|
|
|
|
widget,
|
|
|
|
|
select_button,
|
|
|
|
|
selection: RefCell::new(Vec::new()),
|
|
|
|
|
selected_cb: RefCell::new(None),
|
|
|
|
|
navigator: RefCell::new(None),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Connect signals and callbacks
|
|
|
|
|
|
|
|
|
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
|
|
|
|
let navigator = this.navigator.borrow().clone();
|
|
|
|
|
if let Some(navigator) = navigator {
|
|
|
|
|
navigator.pop();
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
this.select_button.connect_clicked(clone!(@strong this => move |_| {
|
|
|
|
|
let navigator = this.navigator.borrow().clone();
|
|
|
|
|
if let Some(navigator) = navigator {
|
|
|
|
|
navigator.pop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
|
|
|
let selection = this.selection.borrow().clone();
|
|
|
|
|
cb(selection);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
2021-01-29 14:42:59 +01:00
|
|
|
let tracks = this.source.tracks().unwrap();
|
|
|
|
|
|
|
|
|
|
for (index, track) in tracks.iter().enumerate() {
|
2021-01-13 19:27:22 +01:00
|
|
|
let check = gtk::CheckButton::new();
|
|
|
|
|
|
|
|
|
|
check.connect_toggled(clone!(@strong this => move |check| {
|
|
|
|
|
let mut selection = this.selection.borrow_mut();
|
|
|
|
|
if check.get_active() {
|
|
|
|
|
selection.push(index);
|
|
|
|
|
} else {
|
|
|
|
|
if let Some(pos) = selection.iter().position(|part| *part == index) {
|
|
|
|
|
selection.remove(pos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if selection.is_empty() {
|
|
|
|
|
this.select_button.set_sensitive(false);
|
|
|
|
|
} else {
|
|
|
|
|
this.select_button.set_sensitive(true);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
2021-01-29 15:17:27 +01:00
|
|
|
let row = libadwaita::ActionRow::new();
|
2021-01-13 19:27:22 +01:00
|
|
|
row.add_prefix(&check);
|
|
|
|
|
row.set_activatable_widget(Some(&check));
|
2021-01-25 14:00:57 +01:00
|
|
|
row.set_activatable(true);
|
2021-01-29 17:51:44 +01:00
|
|
|
row.set_title(Some(&track.name));
|
2021-01-13 19:27:22 +01:00
|
|
|
|
2021-01-25 14:00:57 +01:00
|
|
|
track_list.append(&row);
|
2021-01-13 19:27:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set the closure to be called when the user has selected tracks. The
|
|
|
|
|
/// closure will be called with the indices of the selected tracks as its
|
|
|
|
|
/// argument.
|
|
|
|
|
pub fn set_selected_cb<F: Fn(Vec<usize>) + 'static>(&self, cb: F) {
|
|
|
|
|
self.selected_cb.replace(Some(Box::new(cb)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl NavigatorScreen for TrackSelector {
|
|
|
|
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
|
|
|
self.navigator.replace(Some(navigator));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_widget(&self) -> gtk::Widget {
|
|
|
|
|
self.widget.clone().upcast()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn detach_navigator(&self) {
|
|
|
|
|
self.navigator.replace(None);
|
|
|
|
|
}
|
|
|
|
|
}
|