From 9507add6a4bd3600f75998c54cb97c6a54458bbf Mon Sep 17 00:00:00 2001 From: Henrik Friedrichsen Date: Mon, 4 Mar 2019 00:56:34 +0100 Subject: [PATCH] more refined queue events + playlist delete binding - move from listview to linearlayout + scrollview - doesn't redraw the whole view on queue changes anymore - uses new cursive functions for linearlayout (needs cursive git) closes #3 --- Cargo.toml | 4 +- src/events.rs | 3 +- src/main.rs | 12 +++--- src/queue.rs | 19 ++++++--- src/ui/queue.rs | 109 +++++++++++++++++++++++++++++++++++------------- 5 files changed, 105 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 01d1ff2..8d14bb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ travis-ci = { repository = "hrkfdn/ncspot", branch = "master" } maintenance = { status = "experimental" } [dependencies] -cursive = "0.10" crossbeam-channel = "0.3.8" env_logger = "0.6.0" failure = "0.1.3" @@ -31,6 +30,9 @@ git = "https://github.com/librespot-org/librespot.git" rev = "daeeeaa122fc2d71edf11e562e23038db4210b39" default-features = false +[dependencies.cursive] +git = "https://github.com/gyscos/Cursive.git" + [features] pulseaudio_backend = ["librespot/pulseaudio-backend"] portaudio_backend = ["librespot/portaudio-backend"] diff --git a/src/events.rs b/src/events.rs index a8781a8..85cee2b 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,10 +1,11 @@ use crossbeam_channel::{unbounded, Receiver, Sender, TryIter}; use cursive::{CbFunc, Cursive}; +use queue::QueueChange; use spotify::PlayerState; pub enum Event { - QueueUpdate, + Queue(QueueChange), Player(PlayerState), } diff --git a/src/main.rs b/src/main.rs index dbca4c3..771c316 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,7 @@ mod theme; mod ui; use events::{Event, EventManager}; +use queue::QueueChange; fn init_logger(content: TextContent) { let mut builder = env_logger::Builder::from_default_env(); @@ -100,6 +101,7 @@ fn main() { cursive.add_global_callback('q', |s| s.quit()); cursive.set_theme(theme::default()); + cursive.set_autorefresh(true); let queue = Arc::new(Mutex::new(queue::Queue::new(event_manager.clone()))); @@ -131,8 +133,8 @@ fn main() { cursive.add_fullscreen_layer(search.view); let queuescreen = cursive.add_active_screen(); - let mut queue = ui::queue::QueueView::new(queue.clone(), spotify.clone()); - cursive.add_fullscreen_layer(queue.view.take().unwrap()); + let mut queueview = ui::queue::QueueView::new(queue.clone(), spotify.clone()); + cursive.add_fullscreen_layer(queueview.view.take().unwrap()); let logscreen = cursive.add_active_screen(); let logview_scroller = ScrollView::new(logview).scroll_strategy(ScrollStrategy::StickToBottom); @@ -144,10 +146,10 @@ fn main() { }); { - let event_manager = event_manager.clone(); + let ev = event_manager.clone(); cursive.add_global_callback(Key::F2, move |s| { s.set_screen(queuescreen); - event_manager.clone().send(Event::QueueUpdate); + ev.send(Event::Queue(QueueChange::Show)); }); } @@ -161,7 +163,7 @@ fn main() { for event in event_manager.msg_iter() { trace!("event received"); match event { - Event::QueueUpdate => queue.redraw(&mut cursive), + Event::Queue(ev) => queueview.handle_ev(&mut cursive, ev), Event::Player(state) => spotify.updatestate(state), } } diff --git a/src/queue.rs b/src/queue.rs index 1694ae1..75d07bf 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -10,6 +10,13 @@ pub struct Queue { ev: EventManager, } +pub enum QueueChange { + Dequeue, + Enqueue, + Remove(usize), + Show, +} + impl Queue { pub fn new(ev: EventManager) -> Queue { Queue { @@ -17,14 +24,11 @@ impl Queue { ev: ev, } } - fn send_event(&self) { - self.ev.send(Event::QueueUpdate); - } pub fn remove(&mut self, index: usize) -> Option { match self.queue.remove(index) { Some(track) => { debug!("Removed from queue: {}", &track.name); - self.send_event(); + self.ev.send(Event::Queue(QueueChange::Remove(index))); Some(track) } None => None, @@ -33,18 +37,21 @@ impl Queue { pub fn enqueue(&mut self, track: FullTrack) { debug!("Queued: {}", &track.name); self.queue.push_back(track); - self.send_event(); + self.ev.send(Event::Queue(QueueChange::Enqueue)); } pub fn dequeue(&mut self) -> Option { match self.queue.pop_front() { Some(track) => { debug!("Dequeued : {}", track.name); - self.send_event(); + self.ev.send(Event::Queue(QueueChange::Dequeue)); Some(track) } None => None, } } + pub fn peek(&self) -> Option<&FullTrack> { + self.queue.get(0) + } pub fn iter(&self) -> Iter { self.queue.iter() } diff --git a/src/ui/queue.rs b/src/ui/queue.rs index 1f4d7c7..670423c 100644 --- a/src/ui/queue.rs +++ b/src/ui/queue.rs @@ -9,23 +9,23 @@ use std::sync::Arc; use std::sync::Mutex; use librespot::core::spotify_id::SpotifyId; +use rspotify::spotify::model::track::FullTrack; -use queue::Queue; +use queue::{Queue, QueueChange}; use spotify::Spotify; use ui::trackbutton::TrackButton; pub struct QueueView { - pub view: Option>, + pub view: Option>>>>>, // FIXME: wow queue: Arc>, spotify: Arc, } impl QueueView { pub fn new(queue: Arc>, spotify: Arc) -> QueueView { - let queuelist = ListView::new().with_id("queue_list").full_width(); + let queuelist = LinearLayout::new(Orientation::Vertical).with_id("queue_list"); let scrollable = ScrollView::new(queuelist).full_width().full_height(); - let layout = LinearLayout::new(Orientation::Vertical).child(scrollable); - let panel = Panel::new(layout).title("Queue"); + let panel = Panel::new(scrollable).title("Queue"); QueueView { view: Some(panel), @@ -34,32 +34,83 @@ impl QueueView { } } - pub fn redraw(&self, s: &mut Cursive) { - let view_ref: Option> = s.find_id("queue_list"); + fn cb_delete(cursive: &mut Cursive, queue: &mut Queue) { + let view_ref: Option> = cursive.find_id("queue_list"); + if let Some(queuelist) = view_ref { + let index = queuelist.get_focus_index(); + queue.remove(index); + } + } + + fn cb_play(cursive: &mut Cursive, queue: &mut Queue, spotify: &Spotify) { + let view_ref: Option> = cursive.find_id("queue_list"); + if let Some(queuelist) = view_ref { + let index = queuelist.get_focus_index(); + let track = queue.remove(index).expect("could not dequeue track"); + let trackid = SpotifyId::from_base62(&track.id).expect("could not load track"); + spotify.load(trackid); + spotify.play(); + } + } + + pub fn handle_ev(&self, cursive: &mut Cursive, ev: QueueChange) { + let view_ref: Option> = cursive.find_id("queue_list"); if let Some(mut queuelist) = view_ref { - queuelist.clear(); - - let queue_ref = self.queue.clone(); - let queue = self.queue.lock().unwrap(); - for (index, track) in queue.iter().enumerate() { - let mut button = TrackButton::new(&track); - let spotify = self.spotify.clone(); - - // dequeues the selected track - let queue_ref = queue_ref.clone(); - button.add_callback(Key::Enter, move |_cursive| { - let track = queue_ref - .lock() - .unwrap() - .remove(index) - .expect("could not dequeue track"); - let trackid = SpotifyId::from_base62(&track.id).expect("could not load track"); - spotify.load(trackid); - spotify.play(); - }); - - queuelist.add_child("", button); + match ev { + QueueChange::Enqueue => { + let queue = self.queue.lock().expect("could not lock queue"); + let track = queue.peek().expect("queue is empty"); + let button = self.create_button(&track); + queuelist.insert_child(0, button); + } + QueueChange::Dequeue => { + queuelist.remove_child(0); + } + QueueChange::Remove(index) => { + queuelist.remove_child(index); + } + QueueChange::Show => self.populate(&mut queuelist), } } } + + fn create_button(&self, track: &FullTrack) -> TrackButton { + let mut button = TrackButton::new(&track); + // 'd' deletes the selected track + { + let queue_ref = self.queue.clone(); + button.add_callback('d', move |cursive| { + Self::cb_delete( + cursive, + &mut queue_ref.lock().expect("could not lock queue"), + ); + }); + } + + // dequeues the selected track + { + let queue_ref = self.queue.clone(); + let spotify = self.spotify.clone(); + button.add_callback(Key::Enter, move |cursive| { + Self::cb_play( + cursive, + &mut queue_ref.lock().expect("could not lock queue"), + &spotify, + ); + }); + } + button + } + + pub fn populate(&self, queuelist: &mut LinearLayout) { + while queuelist.len() > 0 { + queuelist.remove_child(0); + } + + let queue = self.queue.lock().expect("could not lock queue"); + for track in queue.iter() { + let button = self.create_button(&track); + queuelist.add_child(button); + } + } }