From 992be7c50207b057fa9b7f8df5456680fe3ede65 Mon Sep 17 00:00:00 2001 From: KoffeinFlummi Date: Mon, 25 Mar 2019 18:21:08 +0100 Subject: [PATCH 1/5] Reintroduce pulseaudio backend Rodio seems to have issues with some PulseAudio setups, so keep this option around. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 644b84f..9f5c470 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ version = "0.11.1" default-features = false [features] +pulseaudio_backend = ["librespot/pulseaudio-backend"] rodio_backend = ["librespot/rodio-backend"] portaudio_backend = ["librespot/portaudio-backend"] termion_backend = ["cursive/termion-backend"] From e6a7d8a693a060a6ee46a30756926e563b98608a Mon Sep 17 00:00:00 2001 From: KoffeinFlummi Date: Mon, 25 Mar 2019 18:29:00 +0100 Subject: [PATCH 2/5] Add seeking --- src/commands.rs | 26 ++++++++++++++++++++++- src/mpris.rs | 2 +- src/spotify.rs | 21 +++++++++++++++++++ src/track.rs | 6 +++--- src/ui/layout.rs | 32 ++++++++++++++++++++++++++++- src/ui/statusbar.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 130 insertions(+), 7 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 402e6c3..c13725d 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -297,6 +297,28 @@ impl CommandManager { }), ); } + + { + let spotify = spotify.clone(); + self.register( + "seek", + Vec::new(), + Box::new(move |_s, args| { + if let Some(arg) = args.get(0) { + match arg.chars().next().unwrap() { + '+' | '-' => { + spotify.seek_relative(arg.parse::().unwrap_or(0)); + }, + _ => { + spotify.seek(arg.parse::().unwrap_or(0)); + } + } + } + + Ok(None) + }), + ); + } } fn handle_aliases(&self, name: &String) -> String { @@ -308,7 +330,7 @@ impl CommandManager { } pub fn handle(&self, s: &mut Cursive, cmd: String) { - let components: Vec = cmd.split(' ').map(|s| s.to_string()).collect(); + let components: Vec = cmd.trim().split(' ').map(|s| s.to_string()).collect(); let result = if let Some(cb) = self.commands.get(&self.handle_aliases(&components[0])) { cb(s, components[1..].to_vec()) @@ -363,6 +385,8 @@ impl CommandManager { kb.insert("Enter".into(), "play selected".into()); kb.insert("d".into(), "delete selected".into()); kb.insert("/".into(), "search".into()); + kb.insert(".".into(), "seek +500".into()); + kb.insert(",".into(), "seek -500".into()); kb.insert("F1".into(), "queue".into()); kb.insert("F2".into(), "search".into()); diff --git a/src/mpris.rs b/src/mpris.rs index 6e0357f..cfdd80f 100644 --- a/src/mpris.rs +++ b/src/mpris.rs @@ -35,7 +35,7 @@ fn get_metadata(queue: Arc) -> HashMap>> { ); hm.insert( "mpris:length".to_string(), - Variant(Box::new(track.map(|t| t.duration * 1_000_000).unwrap_or(0))), + Variant(Box::new(track.map(|t| t.duration * 1_000).unwrap_or(0))), ); hm.insert( "mpris:artUrl".to_string(), diff --git a/src/spotify.rs b/src/spotify.rs index d4bf59e..cfc27c7 100644 --- a/src/spotify.rs +++ b/src/spotify.rs @@ -40,6 +40,7 @@ enum WorkerCommand { Play, Pause, Stop, + Seek(u32), RequestToken(oneshot::Sender), } @@ -135,6 +136,9 @@ impl futures::Future for Worker { self.events.send(Event::Player(PlayerEvent::Stopped)); self.active = false; } + WorkerCommand::Seek(pos) => { + self.player.seek(pos); + } WorkerCommand::RequestToken(sender) => { self.token_task = Spotify::get_token(&self.session, sender); progress = true; @@ -499,4 +503,21 @@ impl Spotify { info!("stop()"); self.channel.unbounded_send(WorkerCommand::Stop).unwrap(); } + + pub fn seek(&self, position_ms: u32) { + self.set_elapsed(Some(Duration::from_millis(position_ms.into()))); + self.set_since(if self.get_current_status() == PlayerEvent::Playing { + Some(SystemTime::now()) + } else { + None + }); + + self.channel.unbounded_send(WorkerCommand::Seek(position_ms)).unwrap(); + } + + pub fn seek_relative(&self, delta: i32) { + let progress = self.get_current_progress(); + let new = (progress.as_secs() * 1000) as i32 + progress.subsec_millis() as i32 + delta; + self.seek(std::cmp::max(0, new) as u32); + } } diff --git a/src/track.rs b/src/track.rs index c337505..e96dbfe 100644 --- a/src/track.rs +++ b/src/track.rs @@ -44,7 +44,7 @@ impl Track { title: track.name.clone(), track_number: track.track_number, disc_number: track.disc_number, - duration: track.duration_ms / 1000, + duration: track.duration_ms, artists: artists, album: track.album.name.clone(), album_artists: album_artists, @@ -54,8 +54,8 @@ impl Track { } pub fn duration_str(&self) -> String { - let minutes = self.duration / 60; - let seconds = self.duration % 60; + let minutes = self.duration / 60_000; + let seconds = (self.duration / 1000) % 60; format!("{:02}:{:02}", minutes, seconds) } } diff --git a/src/ui/layout.rs b/src/ui/layout.rs index f85979f..caec4b8 100644 --- a/src/ui/layout.rs +++ b/src/ui/layout.rs @@ -29,6 +29,7 @@ pub struct Layout { error: Option, error_time: Option, screenchange: bool, + last_size: Vec2, ev: events::EventManager, theme: Theme, } @@ -49,8 +50,9 @@ impl Layout { cmdline_focus: false, error: None, error_time: None, - ev: ev.clone(), screenchange: true, + last_size: Vec2::new(0, 0), + ev: ev.clone(), theme: theme, } } @@ -166,6 +168,30 @@ impl View for Layout { } fn on_event(&mut self, event: Event) -> EventResult { + if let Event::Mouse { + position, + .. + } = event { + let error = self.get_error(); + + let cmdline_visible = self.cmdline.get_content().len() > 0; + let mut cmdline_height = if cmdline_visible { 1 } else { 0 }; + if error.is_some() { + cmdline_height += 1; + } + + if position.y < self.last_size.y - 2 - cmdline_height { + if let Some(ref id) = self.focus { + let screen = self.views.get_mut(id).unwrap(); + screen.view.on_event(event.clone()); + } + } else if position.y < self.last_size.y - cmdline_height { + self.statusbar.on_event(event.relativized(Vec2::new(0, self.last_size.y - 2 - cmdline_height))); + } + + return EventResult::Consumed(None); + } + if self.cmdline_focus { return self.cmdline.on_event(event); } @@ -179,6 +205,10 @@ impl View for Layout { } fn layout(&mut self, size: Vec2) { + self.last_size = size; + + self.statusbar.layout(Vec2::new(size.x, 2)); + self.cmdline.layout(Vec2::new(size.x, 1)); if let Some(ref id) = self.focus { diff --git a/src/ui/statusbar.rs b/src/ui/statusbar.rs index 2e2854d..755718e 100644 --- a/src/ui/statusbar.rs +++ b/src/ui/statusbar.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use cursive::align::HAlign; +use cursive::event::{Event, EventResult, MouseButton, MouseEvent}; use cursive::theme::{ColorStyle, ColorType, PaletteColor}; use cursive::traits::View; use cursive::vec::Vec2; @@ -13,6 +14,7 @@ use spotify::{PlayerEvent, Spotify}; pub struct StatusBar { queue: Arc, spotify: Arc, + last_size: Vec2, } impl StatusBar { @@ -20,6 +22,7 @@ impl StatusBar { StatusBar { queue: queue, spotify: spotify, + last_size: Vec2::new(0, 0), } } } @@ -63,6 +66,8 @@ impl View for StatusBar { if let Some(ref t) = self.queue.get_current() { let elapsed = self.spotify.get_current_progress(); + let elapsed_ms = elapsed.as_secs() as u32 * 1000 + elapsed.subsec_millis(); + let formatted_elapsed = format!( "{:02}:{:02}", elapsed.as_secs() / 60, @@ -80,7 +85,7 @@ impl View for StatusBar { printer.with_color(style_bar, |printer| { printer.print((0, 0), &"—".repeat(printer.size.x)); let duration_width = - (((printer.size.x as u32) * (elapsed.as_secs() as u32)) / t.duration) as usize; + (((printer.size.x as u32) * elapsed_ms) / t.duration) as usize; printer.print((0, 0), &format!("{}{}", "=".repeat(duration_width), ">")); }); } else { @@ -90,7 +95,50 @@ impl View for StatusBar { } } + fn layout(&mut self, size: Vec2) { + self.last_size = size; + } + fn required_size(&mut self, constraint: Vec2) -> Vec2 { Vec2::new(constraint.x, 2) } + + fn on_event(&mut self, event: Event) -> EventResult { + if let Event::Mouse { + offset, + position, + event + } = event { + let position = position - offset; + + if position.y == 0 { + if event == MouseEvent::WheelUp { + self.spotify.seek_relative(-500); + } + + if event == MouseEvent::WheelDown { + self.spotify.seek_relative(500); + } + + if event == MouseEvent::Press(MouseButton::Left) || + event == MouseEvent::Hold(MouseButton::Left) + { + if let Some(ref t) = self.queue.get_current() { + let f: f32 = position.x as f32 / self.last_size.x as f32; + let new = t.duration as f32 * f; + self.spotify.seek(new as u32); + } + + } + } else { + if event == MouseEvent::Press(MouseButton::Left) { + self.queue.toggleplayback(); + } + } + + EventResult::Consumed(None) + } else { + EventResult::Ignored + } + } } From 45062bd89e695beea44f9b76abf4162dd4cdc1eb Mon Sep 17 00:00:00 2001 From: KoffeinFlummi Date: Mon, 25 Mar 2019 18:31:39 +0100 Subject: [PATCH 3/5] Add shuffle and repeat --- Cargo.toml | 1 + src/commands.rs | 70 ++++++++++++++++++--- src/config.rs | 1 + src/main.rs | 10 +-- src/mpris.rs | 33 +++++++--- src/queue.rs | 147 ++++++++++++++++++++++++++++++++++++++------ src/ui/statusbar.rs | 58 ++++++++++++++--- 7 files changed, 270 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9f5c470..595eecc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ tokio-core = "0.1" tokio-timer = "0.2" unicode-width = "0.1.5" dbus = { version = "0.6.4", optional = true } +rand = "0.6.5" [dependencies.librespot] git = "https://github.com/librespot-org/librespot.git" diff --git a/src/commands.rs b/src/commands.rs index c13725d..4978803 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -5,7 +5,7 @@ use cursive::event::{Event, Key}; use cursive::Cursive; use playlists::{Playlist, Playlists}; -use queue::Queue; +use queue::{Queue, RepeatSetting}; use spotify::Spotify; use track::Track; use ui::layout::Layout; @@ -84,7 +84,7 @@ impl CommandManager { "next", Vec::new(), Box::new(move |_s, _args| { - queue.next(); + queue.next(true); Ok(None) }), ); @@ -240,7 +240,7 @@ impl CommandManager { { let queue = queue.clone(); s.call_on_id("queue_list", |v: &mut ListView| { - queue.play(v.get_selected_index()); + queue.play(v.get_selected_index(), true); }); } @@ -248,8 +248,8 @@ impl CommandManager { let queue = queue.clone(); s.call_on_id("list", |v: &mut ListView| { v.with_selected(Box::new(move |t| { - let index = queue.append_next(t); - queue.play(index); + let index = queue.append_next(vec![t]); + queue.play(index, true); })); }); } @@ -258,11 +258,8 @@ impl CommandManager { let queue = queue.clone(); s.call_on_id("list", |v: &mut ListView| { v.with_selected(Box::new(move |pl| { - let indices: Vec = - pl.tracks.iter().map(|t| queue.append_next(t)).collect(); - if let Some(i) = indices.get(0) { - queue.play(*i) - } + let index = queue.append_next(pl.tracks.iter().collect()); + queue.play(index, true); })); }); } @@ -298,6 +295,57 @@ impl CommandManager { ); } + { + let queue = queue.clone(); + self.register( + "shuffle", + Vec::new(), + Box::new(move |_s, args| { + if let Some(arg) = args.get(0) { + queue.set_shuffle(match arg.as_ref() { + "on" => true, + "off" => false, + _ => { + return Err("Unknown shuffle setting.".to_string()); + } + }); + } else { + queue.set_shuffle(!queue.get_shuffle()); + } + + Ok(None) + }), + ); + } + + { + let queue = queue.clone(); + self.register( + "repeat", + vec!["loop"], + Box::new(move |_s, args| { + if let Some(arg) = args.get(0) { + queue.set_repeat(match arg.as_ref() { + "list" | "playlist" | "queue" => RepeatSetting::RepeatPlaylist, + "track" | "once" => RepeatSetting::RepeatTrack, + "none" | "off" => RepeatSetting::None, + _ => { + return Err("Unknown loop setting.".to_string()); + } + }); + } else { + queue.set_repeat(match queue.get_repeat() { + RepeatSetting::None => RepeatSetting::RepeatPlaylist, + RepeatSetting::RepeatPlaylist => RepeatSetting::RepeatTrack, + RepeatSetting::RepeatTrack => RepeatSetting::None, + }); + } + + Ok(None) + }), + ); + } + { let spotify = spotify.clone(); self.register( @@ -387,6 +435,8 @@ impl CommandManager { kb.insert("/".into(), "search".into()); kb.insert(".".into(), "seek +500".into()); kb.insert(",".into(), "seek -500".into()); + kb.insert("r".into(), "repeat".into()); + kb.insert("z".into(), "shuffle".into()); kb.insert("F1".into(), "queue".into()); kb.insert("F2".into(), "search".into()); diff --git a/src/config.rs b/src/config.rs index 9971966..1fe93ba 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,6 +12,7 @@ pub struct Config { pub password: String, pub keybindings: Option>, pub theme: Option, + pub use_nerdfont: Option, } #[derive(Serialize, Deserialize, Debug, Default, Clone)] diff --git a/src/main.rs b/src/main.rs index 5c72131..b37b729 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,8 @@ extern crate log; extern crate chrono; extern crate fern; +extern crate rand; + use std::process; use std::sync::Arc; use std::thread; @@ -122,8 +124,8 @@ fn main() { let spotify = Arc::new(spotify::Spotify::new( event_manager.clone(), - cfg.username, - cfg.password, + cfg.username.clone(), + cfg.password.clone(), )); let queue = Arc::new(queue::Queue::new(spotify.clone())); @@ -154,7 +156,7 @@ fn main() { let queueview = ui::queue::QueueView::new(queue.clone(), playlists.clone()); - let status = ui::statusbar::StatusBar::new(queue.clone(), spotify.clone()); + let status = ui::statusbar::StatusBar::new(queue.clone(), spotify.clone(), &cfg); let mut layout = ui::layout::Layout::new(status, &event_manager, theme) .view("search", search.with_id("search"), "Search") @@ -196,7 +198,7 @@ fn main() { match event { Event::Player(state) => { if state == PlayerEvent::FinishedTrack { - queue.next(); + queue.next(false); } spotify.update_status(state); diff --git a/src/mpris.rs b/src/mpris.rs index cfdd80f..0495adf 100644 --- a/src/mpris.rs +++ b/src/mpris.rs @@ -7,7 +7,7 @@ use dbus::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged; use dbus::tree::{Access, Factory}; use dbus::{Path, SignalArgs}; -use queue::Queue; +use queue::{Queue, RepeatSetting}; use spotify::{PlayerEvent, Spotify}; fn get_playbackstatus(spotify: Arc) -> String { @@ -176,13 +176,19 @@ fn run_dbus_server(spotify: Arc, queue: Arc, rx: mpsc::Receiver< }) }; - let property_loopstatus = f - .property::("LoopStatus", ()) + let property_loopstatus = { + let queue = queue.clone(); + f.property::("LoopStatus", ()) .access(Access::Read) - .on_get(|iter, _| { - iter.append("None".to_string()); // TODO + .on_get(move |iter, _| { + iter.append(match queue.get_repeat() { + RepeatSetting::None => "None", + RepeatSetting::RepeatTrack => "Track", + RepeatSetting::RepeatPlaylist => "Playlist", + }.to_string()); Ok(()) - }); + }) + }; let property_metadata = { let queue = queue.clone(); @@ -287,6 +293,16 @@ fn run_dbus_server(spotify: Arc, queue: Arc, rx: mpsc::Receiver< Ok(()) }); + let property_shuffle = { + let queue = queue.clone(); + f.property::("Shuffle", ()) + .access(Access::Read) + .on_get(move |iter, _| { + iter.append(queue.get_shuffle()); + Ok(()) + }) + }; + let method_playpause = { let spotify = spotify.clone(); f.method("PlayPause", (), move |m| { @@ -322,7 +338,7 @@ fn run_dbus_server(spotify: Arc, queue: Arc, rx: mpsc::Receiver< let method_next = { let queue = queue.clone(); f.method("Next", (), move |m| { - queue.next(); + queue.next(true); Ok(vec![m.msg.method_return()]) }) }; @@ -335,7 +351,7 @@ fn run_dbus_server(spotify: Arc, queue: Arc, rx: mpsc::Receiver< }) }; - // TODO: Seek, SetPosition, Shuffle, OpenUri (?) + // TODO: Seek, SetPosition, OpenUri (?) // https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html let interface_player = f @@ -354,6 +370,7 @@ fn run_dbus_server(spotify: Arc, queue: Arc, rx: mpsc::Receiver< .add_p(property_cancontrol) .add_p(property_cangonext) .add_p(property_cangoprevious) + .add_p(property_shuffle) .add_m(method_playpause) .add_m(method_play) .add_m(method_pause) diff --git a/src/queue.rs b/src/queue.rs index 57912b8..9231123 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,11 +1,22 @@ use std::sync::{Arc, RwLock}; +use rand::prelude::*; + use spotify::Spotify; use track::Track; +#[derive(Clone, Copy, PartialEq)] +pub enum RepeatSetting { + None, + RepeatPlaylist, + RepeatTrack, +} + pub struct Queue { pub queue: Arc>>, + random_order: RwLock>>, current_track: RwLock>, + repeat: RwLock, spotify: Arc, } @@ -14,15 +25,26 @@ impl Queue { Queue { queue: Arc::new(RwLock::new(Vec::new())), current_track: RwLock::new(None), + repeat: RwLock::new(RepeatSetting::None), + random_order: RwLock::new(None), spotify: spotify, } } pub fn next_index(&self) -> Option { match *self.current_track.read().unwrap() { - Some(index) => { - let next_index = index + 1; + Some(mut index) => { + let random_order = self.random_order.read().unwrap(); + if let Some(order) = random_order.as_ref() { + index = order.iter().position(|&i| i == index).unwrap(); + } + + let mut next_index = index + 1; if next_index < self.queue.read().unwrap().len() { + if let Some(order) = random_order.as_ref() { + next_index = order[next_index]; + } + Some(next_index) } else { None @@ -34,9 +56,19 @@ impl Queue { pub fn previous_index(&self) -> Option { match *self.current_track.read().unwrap() { - Some(index) => { + Some(mut index) => { + let random_order = self.random_order.read().unwrap(); + if let Some(order) = random_order.as_ref() { + index = order.iter().position(|&i| i == index).unwrap(); + } + if index > 0 { - Some(index - 1) + let mut next_index = index - 1; + if let Some(order) = random_order.as_ref() { + next_index = order[next_index]; + } + + Some(next_index) } else { None } @@ -53,21 +85,38 @@ impl Queue { } pub fn append(&self, track: &Track) { + let mut random_order = self.random_order.write().unwrap(); + if let Some(order) = random_order.as_mut() { + let index = order.len() - 1; + order.push(index); + } + let mut q = self.queue.write().unwrap(); q.push(track.clone()); } - pub fn append_next(&self, track: &Track) -> usize { - let next = self.next_index(); + pub fn append_next(&self, tracks: Vec<&Track>) -> usize { let mut q = self.queue.write().unwrap(); - if let Some(next_index) = next { - q.insert(next_index, track.clone()); - next_index - } else { - q.push(track.clone()); - q.len() - 1 + { + let mut random_order = self.random_order.write().unwrap(); + if let Some(order) = random_order.as_mut() { + order.extend((q.len() - 1)..(q.len() + tracks.len())); + } } + + let first = match *self.current_track.read().unwrap() { + Some(index) => index + 1, + None => q.len() + }; + + let mut i = first; + for track in tracks { + q.insert(i, track.clone()); + i += 1; + } + + first } pub fn remove(&self, index: usize) { @@ -90,12 +139,14 @@ impl Queue { let current = *self.current_track.read().unwrap(); if let Some(current_track) = current { if index == current_track { - self.play(index); + self.play(index, false); } else if index < current_track { let mut current = self.current_track.write().unwrap(); current.replace(current_track - 1); } } + + self.generate_random_order(); } pub fn clear(&self) { @@ -103,9 +154,12 @@ impl Queue { let mut q = self.queue.write().unwrap(); q.clear(); + + let mut random_order = self.random_order.write().unwrap(); + random_order.as_mut().map(|o| o.clear()); } - pub fn play(&self, index: usize) { + pub fn play(&self, index: usize, reshuffle: bool) { if let Some(track) = &self.queue.read().unwrap().get(index) { self.spotify.load(&track); let mut current = self.current_track.write().unwrap(); @@ -113,6 +167,10 @@ impl Queue { self.spotify.play(); self.spotify.update_track(); } + + if reshuffle { + self.generate_random_order() + } } pub fn toggleplayback(&self) { @@ -125,9 +183,20 @@ impl Queue { self.spotify.stop(); } - pub fn next(&self) { - if let Some(index) = self.next_index() { - self.play(index); + pub fn next(&self, manual: bool) { + let q = self.queue.read().unwrap(); + let current = *self.current_track.read().unwrap(); + let repeat = *self.repeat.read().unwrap(); + + if repeat == RepeatSetting::RepeatTrack && !manual { + if let Some(index) = current { + self.play(index, false); + } + } else if let Some(index) = self.next_index() { + self.play(index, false); + } else if repeat == RepeatSetting::RepeatPlaylist && q.len() > 0 { + let random_order = self.random_order.read().unwrap(); + self.play(random_order.as_ref().map(|o| o[0]).unwrap_or(0), false); } else { self.spotify.stop(); } @@ -135,9 +204,51 @@ impl Queue { pub fn previous(&self) { if let Some(index) = self.previous_index() { - self.play(index); + self.play(index, false); } else { self.spotify.stop(); } } + + pub fn get_repeat(&self) -> RepeatSetting { + let repeat = self.repeat.read().unwrap(); + *repeat + } + + pub fn set_repeat(&self, new: RepeatSetting) { + let mut repeat = self.repeat.write().unwrap(); + *repeat = new; + } + + pub fn get_shuffle(&self) -> bool { + let random_order = self.random_order.read().unwrap(); + random_order.is_some() + } + + fn generate_random_order(&self) { + let q = self.queue.read().unwrap(); + let mut order: Vec = Vec::with_capacity(q.len()); + let mut random: Vec = (0..q.len()).collect(); + + if let Some(current) = *self.current_track.read().unwrap() { + order.push(current); + random.remove(current); + } + + let mut rng = rand::thread_rng(); + random.shuffle(&mut rng); + order.extend(random); + + let mut random_order = self.random_order.write().unwrap(); + *random_order = Some(order); + } + + pub fn set_shuffle(&self, new: bool) { + if new { + self.generate_random_order(); + } else { + let mut random_order = self.random_order.write().unwrap(); + *random_order = None; + } + } } diff --git a/src/ui/statusbar.rs b/src/ui/statusbar.rs index 755718e..c9a7c2f 100644 --- a/src/ui/statusbar.rs +++ b/src/ui/statusbar.rs @@ -8,21 +8,24 @@ use cursive::vec::Vec2; use cursive::Printer; use unicode_width::UnicodeWidthStr; -use queue::Queue; +use config::Config; +use queue::{Queue, RepeatSetting}; use spotify::{PlayerEvent, Spotify}; pub struct StatusBar { queue: Arc, spotify: Arc, last_size: Vec2, + use_nerdfont: bool, } impl StatusBar { - pub fn new(queue: Arc, spotify: Arc) -> StatusBar { + pub fn new(queue: Arc, spotify: Arc, cfg: &Config) -> StatusBar { StatusBar { queue: queue, spotify: spotify, last_size: Vec2::new(0, 0), + use_nerdfont: cfg.use_nerdfont.unwrap_or(false) } } } @@ -53,17 +56,45 @@ impl View for StatusBar { ); }); - let state_icon = match self.spotify.get_current_status() { - PlayerEvent::Playing => "▶ ", - PlayerEvent::Paused => "▮▮", - PlayerEvent::Stopped | PlayerEvent::FinishedTrack => "◼ ", + let state_icon = if self.use_nerdfont { + match self.spotify.get_current_status() { + PlayerEvent::Playing => "\u{f909} ", + PlayerEvent::Paused => "\u{f8e3} ", + PlayerEvent::Stopped | PlayerEvent::FinishedTrack => "\u{f9da} ", + } + } else { + match self.spotify.get_current_status() { + PlayerEvent::Playing => "▶ ", + PlayerEvent::Paused => "▮▮", + PlayerEvent::Stopped | PlayerEvent::FinishedTrack => "◼ ", + } } .to_string(); printer.with_color(style, |printer| { - printer.print((0, 1), &state_icon); + printer.print((1, 1), &state_icon); }); + let repeat = if self.use_nerdfont { + match self.queue.get_repeat() { + RepeatSetting::None => "", + RepeatSetting::RepeatPlaylist => "\u{f955} ", + RepeatSetting::RepeatTrack => "\u{f957} ", + } + } else { + match self.queue.get_repeat() { + RepeatSetting::None => "", + RepeatSetting::RepeatPlaylist => "[R] ", + RepeatSetting::RepeatTrack => "[R1] ", + } + }.to_string(); + + let shuffle = if self.use_nerdfont { + if self.queue.get_shuffle() { "\u{f99c} " } else { "" } + } else { + if self.queue.get_shuffle() { "[Z] " } else { "" } + }.to_string(); + if let Some(ref t) = self.queue.get_current() { let elapsed = self.spotify.get_current_progress(); let elapsed_ms = elapsed.as_secs() as u32 * 1000 + elapsed.subsec_millis(); @@ -74,12 +105,12 @@ impl View for StatusBar { elapsed.as_secs() % 60 ); - let duration = format!("{} / {} ", formatted_elapsed, t.duration_str()); - let offset = HAlign::Right.get_offset(duration.width(), printer.size.x); + let right = repeat + &shuffle + &format!("{} / {} ", formatted_elapsed, t.duration_str()); + let offset = HAlign::Right.get_offset(right.width(), printer.size.x); printer.with_color(style, |printer| { printer.print((4, 1), &t.to_string()); - printer.print((offset, 1), &duration); + printer.print((offset, 1), &right); }); printer.with_color(style_bar, |printer| { @@ -89,6 +120,13 @@ impl View for StatusBar { printer.print((0, 0), &format!("{}{}", "=".repeat(duration_width), ">")); }); } else { + let right = repeat + &shuffle; + let offset = HAlign::Right.get_offset(right.width(), printer.size.x); + + printer.with_color(style, |printer| { + printer.print((offset, 1), &right); + }); + printer.with_color(style_bar, |printer| { printer.print((0, 0), &"—".repeat(printer.size.x)); }); From 2b75e985adc597a310bd1d5c969cf4b0e987a364 Mon Sep 17 00:00:00 2001 From: KoffeinFlummi Date: Mon, 25 Mar 2019 18:32:19 +0100 Subject: [PATCH 4/5] Properly clear cmdline when deleting last char --- src/main.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.rs b/src/main.rs index b37b729..661753f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -172,6 +172,14 @@ fn main() { }); }); + layout.cmdline.set_on_edit(move |s, cmd, _| { + s.call_on_id("main", |v: &mut ui::layout::Layout| { + if cmd.len() == 0 { + v.clear_cmdline(); + } + }); + }); + { let ev = event_manager.clone(); layout.cmdline.set_on_submit(move |s, cmd| { From 9cf1e9945e458675611657a3bf45452e57ec12db Mon Sep 17 00:00:00 2001 From: KoffeinFlummi Date: Mon, 25 Mar 2019 20:07:38 +0100 Subject: [PATCH 5/5] Only regenerate random order if shuffle is enabled --- src/queue.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/queue.rs b/src/queue.rs index 9231123..9c9d718 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -146,7 +146,9 @@ impl Queue { } } - self.generate_random_order(); + if self.get_shuffle() { + self.generate_random_order(); + } } pub fn clear(&self) { @@ -168,7 +170,7 @@ impl Queue { self.spotify.update_track(); } - if reshuffle { + if reshuffle && self.get_shuffle() { self.generate_random_order() } }