Move to enum-based commands

This commit is contained in:
Rasmus Larsen
2019-05-20 22:15:12 +02:00
parent 4b4a027c3c
commit 1671db14c1
15 changed files with 343 additions and 317 deletions

64
src/command.rs Normal file
View File

@@ -0,0 +1,64 @@
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum PlaylistCommands {
Update,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum SeekInterval {
Forward,
Backwards,
Custom(usize),
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum TargetMode {
Current,
Selected,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum MoveMode {
Up,
Down,
Left,
Right,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum ShiftMode {
Up,
Down,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum GotoMode {
Album,
Artist,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum Command {
Quit,
TogglePlay,
Playlists(PlaylistCommands),
Stop,
Previous,
Next,
Clear,
Queue,
Play,
Save,
SaveQueue,
Delete,
Focus(String),
Seek(SeekInterval),
Repeat,
Shuffle,
Share(TargetMode),
Back,
Open,
Goto(GotoMode),
Move(MoveMode, Option<usize>),
Shift(ShiftMode, Option<usize>),
Search(String),
}

View File

@@ -6,6 +6,16 @@ use cursive::event::{Event, Key};
use cursive::views::ViewRef; use cursive::views::ViewRef;
use cursive::Cursive; use cursive::Cursive;
use command::Command::{
Back, Clear, Delete, Focus, Goto, Move, Next, Open, Play, Playlists, Previous, Quit, Repeat,
Save, SaveQueue, Seek, Share, Shift, Shuffle, Stop, TogglePlay,
};
use command::GotoMode::{Album, Artist};
use command::MoveMode::{Down, Left, Right, Up};
use command::PlaylistCommands::Update;
use command::SeekInterval::{Backwards, Forward};
use command::TargetMode::{Current, Selected};
use command::{Command, ShiftMode};
use library::Library; use library::Library;
use queue::{Queue, RepeatSetting}; use queue::{Queue, RepeatSetting};
use spotify::Spotify; use spotify::Spotify;
@@ -227,15 +237,10 @@ impl CommandManager {
} }
} }
fn handle_callbacks( fn handle_callbacks(&self, s: &mut Cursive, cmd: &Command) -> Result<Option<String>, String> {
&self,
s: &mut Cursive,
cmd: &str,
args: &[String],
) -> Result<Option<String>, String> {
let local = { let local = {
let mut main: ViewRef<Layout> = s.find_id("main").unwrap(); let mut main: ViewRef<Layout> = s.find_id("main").unwrap();
main.on_command(s, cmd, args)? main.on_command(s, cmd)?
}; };
if let CommandResult::Consumed(output) = local { if let CommandResult::Consumed(output) = local {
@@ -246,24 +251,18 @@ impl CommandManager {
}); });
Ok(None) Ok(None)
} else if let Some(callback) = self.callbacks.get(cmd) { }
/* handle default commands
else if let Some(callback) = self.callbacks.get(cmd) {
callback.as_ref().map(|cb| cb(s, args)).unwrap_or(Ok(None)) callback.as_ref().map(|cb| cb(s, args)).unwrap_or(Ok(None))
} else { } */
else {
Err("Unknown command.".to_string()) Err("Unknown command.".to_string())
} }
} }
pub fn handle(&self, s: &mut Cursive, cmd: String) { pub fn handle(&self, s: &mut Cursive, cmd: Command) {
let components: Vec<String> = cmd let result = self.handle_callbacks(s, &cmd);
.trim()
.split(' ')
.map(std::string::ToString::to_string)
.collect();
let cmd = self.handle_aliases(&components[0]);
let args = components[1..].to_vec();
let result = self.handle_callbacks(s, &cmd, &args);
s.call_on_id("main", |v: &mut Layout| { s.call_on_id("main", |v: &mut Layout| {
v.set_result(result); v.set_result(result);
@@ -272,22 +271,21 @@ impl CommandManager {
s.on_event(Event::Refresh); s.on_event(Event::Refresh);
} }
pub fn register_keybinding<E: Into<cursive::event::Event>, S: Into<String>>( pub fn register_keybinding<E: Into<cursive::event::Event>>(
this: Arc<Self>, this: Arc<Self>,
cursive: &mut Cursive, cursive: &mut Cursive,
event: E, event: E,
command: S, command: Command,
) { ) {
let cmd = command.into();
cursive.add_global_callback(event, move |s| { cursive.add_global_callback(event, move |s| {
this.handle(s, cmd.clone()); this.handle(s, command.clone());
}); });
} }
pub fn register_keybindings( pub fn register_keybindings(
this: Arc<Self>, this: Arc<Self>,
cursive: &mut Cursive, cursive: &mut Cursive,
keybindings: Option<HashMap<String, String>>, keybindings: Option<HashMap<String, Command>>,
) { ) {
let mut kb = Self::default_keybindings(); let mut kb = Self::default_keybindings();
kb.extend(keybindings.unwrap_or_default()); kb.extend(keybindings.unwrap_or_default());
@@ -301,51 +299,51 @@ impl CommandManager {
} }
} }
fn default_keybindings() -> HashMap<String, String> { fn default_keybindings() -> HashMap<String, Command> {
let mut kb = HashMap::new(); let mut kb = HashMap::new();
kb.insert("q".into(), "quit".into()); kb.insert("q".into(), Quit);
kb.insert("P".into(), "toggleplay".into()); kb.insert("P".into(), TogglePlay);
kb.insert("R".into(), "playlists update".into()); kb.insert("R".into(), Playlists(Update));
kb.insert("S".into(), "stop".into()); kb.insert("S".into(), Stop);
kb.insert("<".into(), "previous".into()); kb.insert("<".into(), Previous);
kb.insert(">".into(), "next".into()); kb.insert(">".into(), Next);
kb.insert("c".into(), "clear".into()); kb.insert("c".into(), Clear);
kb.insert(" ".into(), "queue".into()); kb.insert(" ".into(), Command::Queue);
kb.insert("Enter".into(), "play".into()); kb.insert("Enter".into(), Play);
kb.insert("s".into(), "save".into()); kb.insert("s".into(), Save);
kb.insert("Ctrl+s".into(), "save queue".into()); kb.insert("Ctrl+s".into(), SaveQueue);
kb.insert("d".into(), "delete".into()); kb.insert("d".into(), Delete);
kb.insert("/".into(), "focus search".into()); kb.insert("/".into(), Focus("search".into()));
kb.insert(".".into(), "seek +500".into()); kb.insert(".".into(), Seek(Forward));
kb.insert(",".into(), "seek -500".into()); kb.insert(",".into(), Seek(Backwards));
kb.insert("r".into(), "repeat".into()); kb.insert("r".into(), Repeat);
kb.insert("z".into(), "shuffle".into()); kb.insert("z".into(), Shuffle);
kb.insert("x".into(), "share current".into()); kb.insert("x".into(), Share(Current));
kb.insert("Shift+x".into(), "share selected".into()); kb.insert("Shift+x".into(), Share(Selected));
kb.insert("F1".into(), "focus queue".into()); kb.insert("F1".into(), Focus("queue".into()));
kb.insert("F2".into(), "focus search".into()); kb.insert("F2".into(), Focus("search".into()));
kb.insert("F3".into(), "focus library".into()); kb.insert("F3".into(), Focus("library".into()));
kb.insert("Backspace".into(), "back".into()); kb.insert("Backspace".into(), Back);
kb.insert("o".into(), "open".into()); kb.insert("o".into(), Open);
kb.insert("a".into(), "goto album".into()); kb.insert("a".into(), Goto(Album));
kb.insert("A".into(), "goto artist".into()); kb.insert("A".into(), Goto(Artist));
kb.insert("Up".into(), "move up".into()); kb.insert("Up".into(), Move(Up, None));
kb.insert("Down".into(), "move down".into()); kb.insert("Down".into(), Move(Down, None));
kb.insert("Left".into(), "move left".into()); kb.insert("Left".into(), Move(Left, None));
kb.insert("Right".into(), "move right".into()); kb.insert("Right".into(), Move(Right, None));
kb.insert("PageUp".into(), "move up 5".into()); kb.insert("PageUp".into(), Move(Up, Some(5)));
kb.insert("PageDown".into(), "move down 5".into()); kb.insert("PageDown".into(), Move(Down, Some(5)));
kb.insert("k".into(), "move up".into()); kb.insert("k".into(), Move(Up, None));
kb.insert("j".into(), "move down".into()); kb.insert("j".into(), Move(Down, None));
kb.insert("h".into(), "move left".into()); kb.insert("h".into(), Move(Left, None));
kb.insert("l".into(), "move right".into()); kb.insert("l".into(), Move(Right, None));
kb.insert("Shift+Up".into(), "shift up".into()); kb.insert("Shift+Up".into(), Shift(ShiftMode::Up, None));
kb.insert("Shift+Down".into(), "shift down".into()); kb.insert("Shift+Down".into(), Shift(ShiftMode::Down, None));
kb kb
} }

View File

@@ -3,13 +3,14 @@ use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::RwLock; use std::sync::RwLock;
use command::Command;
use directories::ProjectDirs; use directories::ProjectDirs;
pub const CLIENT_ID: &str = "d420a117a32841c2b3474932e49fb54b"; pub const CLIENT_ID: &str = "d420a117a32841c2b3474932e49fb54b";
#[derive(Serialize, Deserialize, Debug, Default)] #[derive(Serialize, Deserialize, Debug, Default)]
pub struct Config { pub struct Config {
pub keybindings: Option<HashMap<String, String>>, pub keybindings: Option<HashMap<String, Command>>,
pub theme: Option<ConfigTheme>, pub theme: Option<ConfigTheme>,
pub use_nerdfont: Option<bool>, pub use_nerdfont: Option<bool>,
} }

View File

@@ -46,6 +46,7 @@ use librespot::core::authentication::Credentials;
mod album; mod album;
mod artist; mod artist;
mod authentication; mod authentication;
mod command;
mod commands; mod commands;
mod config; mod config;
mod events; mod events;
@@ -232,6 +233,8 @@ fn main() {
}); });
}); });
/*
TODO: Write parser for commands
{ {
let ev = event_manager.clone(); let ev = event_manager.clone();
let cmd_manager = cmd_manager.clone(); let cmd_manager = cmd_manager.clone();
@@ -244,6 +247,7 @@ fn main() {
ev.trigger(); ev.trigger();
}); });
} }
*/
cursive.add_fullscreen_layer(layout.with_id("main")); cursive.add_fullscreen_layer(layout.with_id("main"));

View File

@@ -6,6 +6,7 @@ use cursive::Cursive;
use album::Album; use album::Album;
use artist::Artist; use artist::Artist;
use command::Command;
use commands::CommandResult; use commands::CommandResult;
use library::Library; use library::Library;
use queue::Queue; use queue::Queue;
@@ -34,25 +35,14 @@ pub trait ViewExt: View {
"".into() "".into()
} }
fn on_command( fn on_command(&mut self, _s: &mut Cursive, _cmd: &Command) -> Result<CommandResult, String> {
&mut self,
_s: &mut Cursive,
_cmd: &str,
_args: &[String],
) -> Result<CommandResult, String> {
Ok(CommandResult::Ignored) Ok(CommandResult::Ignored)
} }
} }
impl<V: ViewExt> ViewExt for IdView<V> { impl<V: ViewExt> ViewExt for IdView<V> {
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, self.with_view_mut(move |v| v.on_command(s, cmd)).unwrap()
s: &mut Cursive,
cmd: &str,
args: &[String],
) -> Result<CommandResult, String> {
self.with_view_mut(move |v| v.on_command(s, cmd, args))
.unwrap()
} }
} }

View File

@@ -5,6 +5,7 @@ use cursive::Cursive;
use album::Album; use album::Album;
use artist::Artist; use artist::Artist;
use command::Command;
use commands::CommandResult; use commands::CommandResult;
use library::Library; use library::Library;
use queue::Queue; use queue::Queue;
@@ -69,12 +70,7 @@ impl ViewExt for AlbumView {
format!("{} ({})", self.album.title, self.album.year) format!("{} ({})", self.album.title, self.album.year)
} }
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, self.tabs.on_command(s, cmd)
s: &mut Cursive,
cmd: &str,
args: &[String],
) -> Result<CommandResult, String> {
self.tabs.on_command(s, cmd, args)
} }
} }

View File

@@ -5,6 +5,7 @@ use cursive::view::ViewWrapper;
use cursive::Cursive; use cursive::Cursive;
use artist::Artist; use artist::Artist;
use command::Command;
use commands::CommandResult; use commands::CommandResult;
use library::Library; use library::Library;
use queue::Queue; use queue::Queue;
@@ -110,12 +111,7 @@ impl ViewExt for ArtistView {
self.artist.name.clone() self.artist.name.clone()
} }
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, self.tabs.on_command(s, cmd)
s: &mut Cursive,
cmd: &str,
args: &[String],
) -> Result<CommandResult, String> {
self.tabs.on_command(s, cmd, args)
} }
} }

View File

@@ -12,6 +12,8 @@ use cursive::views::EditView;
use cursive::{Cursive, Printer}; use cursive::{Cursive, Printer};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use command::Command;
use command::Command::Focus;
use commands::CommandResult; use commands::CommandResult;
use events; use events;
use traits::{IntoBoxedViewExt, ViewExt}; use traits::{IntoBoxedViewExt, ViewExt};
@@ -283,29 +285,28 @@ impl View for Layout {
} }
impl ViewExt for Layout { impl ViewExt for Layout {
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, match cmd {
s: &mut Cursive, Command::Focus(view) => {
cmd: &str,
args: &[String],
) -> Result<CommandResult, String> {
if cmd == "focus" {
if let Some(view) = args.get(0) {
if self.views.keys().any(|k| k == view) { if self.views.keys().any(|k| k == view) {
self.set_view(view.clone()); self.set_view(view.clone());
let screen = self.views.get_mut(view).unwrap(); let screen = self.views.get_mut(view).unwrap();
screen.view.on_command(s, cmd, args)?; screen.view.on_command(s, cmd)?;
}
Ok(CommandResult::Consumed(None))
}
Command::Back => {
self.pop_view();
Ok(CommandResult::Consumed(None))
}
_ => {
if let Some(screen) = self.get_current_screen_mut() {
screen.view.on_command(s, cmd)
} else {
Ok(CommandResult::Ignored)
} }
} }
Ok(CommandResult::Consumed(None))
} else if cmd == "back" {
self.pop_view();
Ok(CommandResult::Consumed(None))
} else if let Some(screen) = self.get_current_screen_mut() {
screen.view.on_command(s, cmd, args)
} else {
Ok(CommandResult::Ignored)
} }
} }
} }

View File

@@ -3,6 +3,7 @@ use std::sync::Arc;
use cursive::view::ViewWrapper; use cursive::view::ViewWrapper;
use cursive::Cursive; use cursive::Cursive;
use command::Command;
use commands::CommandResult; use commands::CommandResult;
use library::Library; use library::Library;
use queue::Queue; use queue::Queue;
@@ -48,12 +49,7 @@ impl ViewWrapper for LibraryView {
} }
impl ViewExt for LibraryView { impl ViewExt for LibraryView {
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, self.tabs.on_command(s, cmd)
s: &mut Cursive,
cmd: &str,
args: &[String],
) -> Result<CommandResult, String> {
self.tabs.on_command(s, cmd, args)
} }
} }

View File

@@ -10,6 +10,7 @@ use cursive::{Cursive, Printer, Rect, Vec2};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use clipboard::{ClipboardContext, ClipboardProvider}; use clipboard::{ClipboardContext, ClipboardProvider};
use command::{Command, GotoMode, MoveMode, TargetMode};
use commands::CommandResult; use commands::CommandResult;
use library::Library; use library::Library;
use queue::Queue; use queue::Queue;
@@ -277,123 +278,116 @@ impl<I: ListItem> View for ListView<I> {
} }
impl<I: ListItem + Clone> ViewExt for ListView<I> { impl<I: ListItem + Clone> ViewExt for ListView<I> {
fn on_command( fn on_command(&mut self, _s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, match cmd {
_s: &mut Cursive, Command::Play => {
cmd: &str, self.queue.clear();
args: &[String],
) -> Result<CommandResult, String> {
if cmd == "play" {
self.queue.clear();
if !self.attempt_play_all_tracks() { if !self.attempt_play_all_tracks() {
let mut content = self.content.write().unwrap();
if let Some(item) = content.get_mut(self.selected) {
item.play(self.queue.clone());
}
}
return Ok(CommandResult::Consumed(None));
}
Command::Queue => {
let mut content = self.content.write().unwrap(); let mut content = self.content.write().unwrap();
if let Some(item) = content.get_mut(self.selected) { if let Some(item) = content.get_mut(self.selected) {
item.play(self.queue.clone()); item.queue(self.queue.clone());
}
return Ok(CommandResult::Consumed(None));
}
Command::Save => {
let mut item = {
let content = self.content.read().unwrap();
content.get(self.selected).cloned()
};
if let Some(item) = item.as_mut() {
item.toggle_saved(self.library.clone());
} }
} }
return Ok(CommandResult::Consumed(None)); Command::Share(mode) => {
} let url = match mode {
TargetMode::Selected => self.content.read().ok().and_then(|content| {
if cmd == "queue" {
let mut content = self.content.write().unwrap();
if let Some(item) = content.get_mut(self.selected) {
item.queue(self.queue.clone());
}
return Ok(CommandResult::Consumed(None));
}
if cmd == "save" {
let mut item = {
let content = self.content.read().unwrap();
content.get(self.selected).cloned()
};
if let Some(item) = item.as_mut() {
item.toggle_saved(self.library.clone());
}
}
if cmd == "share" {
let source = args.get(0);
let url =
source.and_then(|source| match source.as_str() {
"selected" => self.content.read().ok().and_then(|content| {
content.get(self.selected).and_then(ListItem::share_url) content.get(self.selected).and_then(ListItem::share_url)
}), }),
"current" => self.queue.get_current().and_then(|t| t.share_url()), TargetMode::Current => self.queue.get_current().and_then(|t| t.share_url()),
_ => None, };
});
if let Some(url) = url { if let Some(url) = url {
ClipboardProvider::new() ClipboardProvider::new()
.and_then(|mut ctx: ClipboardContext| ctx.set_contents(url)) .and_then(|mut ctx: ClipboardContext| ctx.set_contents(url))
.ok(); .ok();
}; }
return Ok(CommandResult::Consumed(None)); return Ok(CommandResult::Consumed(None));
} }
Command::Move(mode, amount) => {
if cmd == "move" { let amount = match amount {
if let Some(dir) = args.get(0) { Some(amount) => *amount,
let amount: usize = args _ => 1,
.get(1) };
.unwrap_or(&"1".to_string())
.parse()
.map_err(|e| format!("{:?}", e))?;
let len = self.content.read().unwrap().len(); let len = self.content.read().unwrap().len();
if dir == "up" && self.selected > 0 { match mode {
self.move_focus(-(amount as i32)); MoveMode::Up if self.selected > 0 => {
return Ok(CommandResult::Consumed(None)); self.move_focus(-(amount as i32));
} return Ok(CommandResult::Consumed(None));
}
if dir == "down" { MoveMode::Down if self.selected < len.saturating_sub(1) => {
if self.selected < len.saturating_sub(1) {
self.move_focus(amount as i32); self.move_focus(amount as i32);
return Ok(CommandResult::Consumed(None)); return Ok(CommandResult::Consumed(None));
} else if self.selected == len.saturating_sub(1) && self.can_paginate() { }
MoveMode::Down
if self.selected == len.saturating_sub(1) && self.can_paginate() =>
{
self.pagination.call(&self.content); self.pagination.call(&self.content);
} }
_ => {}
} }
} }
} Command::Open => {
let mut content = self.content.write().unwrap();
if cmd == "open" { if let Some(item) = content.get_mut(self.selected) {
let mut content = self.content.write().unwrap(); let queue = self.queue.clone();
if let Some(item) = content.get_mut(self.selected) { let library = self.library.clone();
let queue = self.queue.clone(); if let Some(view) = item.open(queue, library) {
let library = self.library.clone();
if let Some(view) = item.open(queue, library) {
return Ok(CommandResult::View(view));
}
}
}
if cmd == "goto" {
let mut content = self.content.write().unwrap();
if let Some(item) = content.get_mut(self.selected) {
let queue = self.queue.clone();
let library = self.library.clone();
let arg = args.get(0).cloned().unwrap_or_default();
if arg == "album" {
if let Some(album) = item.album(queue.clone()) {
let view = AlbumView::new(queue, library, &album).as_boxed_view_ext();
return Ok(CommandResult::View(view));
}
}
if arg == "artist" {
if let Some(artist) = item.artist() {
let view = ArtistView::new(queue, library, &artist).as_boxed_view_ext();
return Ok(CommandResult::View(view)); return Ok(CommandResult::View(view));
} }
} }
} }
} Command::Goto(mode) => {
let mut content = self.content.write().unwrap();
if let Some(item) = content.get_mut(self.selected) {
let queue = self.queue.clone();
let library = self.library.clone();
match mode {
GotoMode::Album => {
if let Some(album) = item.album(queue.clone()) {
let view =
AlbumView::new(queue, library, &album).as_boxed_view_ext();
return Ok(CommandResult::View(view));
}
}
GotoMode::Artist => {
if let Some(artist) = item.artist() {
let view =
ArtistView::new(queue, library, &artist).as_boxed_view_ext();
return Ok(CommandResult::View(view));
}
}
}
}
}
_ => {}
};
Ok(CommandResult::Ignored) Ok(CommandResult::Ignored)
} }

View File

@@ -3,6 +3,7 @@ use std::sync::{Arc, RwLock};
use cursive::view::ViewWrapper; use cursive::view::ViewWrapper;
use cursive::Cursive; use cursive::Cursive;
use command::Command;
use commands::CommandResult; use commands::CommandResult;
use library::Library; use library::Library;
use playlist::Playlist; use playlist::Playlist;
@@ -43,12 +44,7 @@ impl ViewExt for PlaylistView {
self.playlist.name.clone() self.playlist.name.clone()
} }
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, self.list.on_command(s, cmd)
s: &mut Cursive,
cmd: &str,
args: &[String],
) -> Result<CommandResult, String> {
self.list.on_command(s, cmd, args)
} }
} }

View File

@@ -4,6 +4,7 @@ use cursive::view::ViewWrapper;
use cursive::views::Dialog; use cursive::views::Dialog;
use cursive::Cursive; use cursive::Cursive;
use command::Command;
use commands::CommandResult; use commands::CommandResult;
use library::Library; use library::Library;
use playlist::Playlist; use playlist::Playlist;
@@ -52,19 +53,17 @@ impl ViewWrapper for PlaylistsView {
} }
impl ViewExt for PlaylistsView { impl ViewExt for PlaylistsView {
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, match cmd {
s: &mut Cursive, Command::Delete => {
cmd: &str, if let Some(dialog) = self.delete_dialog() {
args: &[String], s.add_layer(dialog);
) -> Result<CommandResult, String> { }
if cmd == "delete" { return Ok(CommandResult::Consumed(None));
if let Some(dialog) = self.delete_dialog() {
s.add_layer(dialog);
} }
return Ok(CommandResult::Consumed(None)); _ => {}
} }
self.list.on_command(s, cmd, args) self.list.on_command(s, cmd)
} }
} }

View File

@@ -6,6 +6,7 @@ use cursive::Cursive;
use std::cmp::min; use std::cmp::min;
use std::sync::Arc; use std::sync::Arc;
use command::{Command, ShiftMode};
use commands::CommandResult; use commands::CommandResult;
use library::Library; use library::Library;
use queue::Queue; use queue::Queue;
@@ -88,55 +89,51 @@ impl ViewWrapper for QueueView {
} }
impl ViewExt for QueueView { impl ViewExt for QueueView {
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, match cmd {
s: &mut Cursive, Command::Play => {
cmd: &str, self.queue.play(self.list.get_selected_index(), true);
args: &[String], return Ok(CommandResult::Consumed(None));
) -> Result<CommandResult, String> { }
if cmd == "play" { Command::Queue => {
self.queue.play(self.list.get_selected_index(), true); return Ok(CommandResult::Ignored);
return Ok(CommandResult::Consumed(None)); }
} Command::Delete => {
self.queue.remove(self.list.get_selected_index());
return Ok(CommandResult::Consumed(None));
}
Command::Shift(mode, amount) => {
let amount = match amount {
Some(amount) => *amount,
_ => 1,
};
if cmd == "queue" {
return Ok(CommandResult::Ignored);
}
if cmd == "delete" {
self.queue.remove(self.list.get_selected_index());
return Ok(CommandResult::Consumed(None));
}
if cmd == "shift" {
if let Some(dir) = args.get(0) {
let amount: usize = args
.get(1)
.unwrap_or(&"1".to_string())
.parse()
.map_err(|e| format!("{:?}", e))?;
let selected = self.list.get_selected_index(); let selected = self.list.get_selected_index();
let len = self.queue.len(); let len = self.queue.len();
if dir == "up" && selected > 0 {
self.queue.shift(selected, selected.saturating_sub(amount)); match mode {
self.list.move_focus(-(amount as i32)); ShiftMode::Up if selected > 0 => {
return Ok(CommandResult::Consumed(None)); self.queue.shift(selected, selected.saturating_sub(amount));
} else if dir == "down" && selected < len.saturating_sub(1) { self.list.move_focus(-(amount as i32));
self.queue return Ok(CommandResult::Consumed(None));
.shift(selected, min(selected + amount as usize, len - 1)); }
self.list.move_focus(amount as i32); ShiftMode::Down if selected < len.saturating_sub(1) => {
return Ok(CommandResult::Consumed(None)); self.queue
.shift(selected, min(selected + amount as usize, len - 1));
self.list.move_focus(amount as i32);
return Ok(CommandResult::Consumed(None));
}
_ => {}
} }
} }
Command::SaveQueue => {
let dialog = Self::save_dialog(self.queue.clone(), self.library.clone());
s.add_layer(dialog);
return Ok(CommandResult::Consumed(None));
}
_ => {}
} }
if cmd == "save" && args.get(0).unwrap_or(&"".to_string()) == "queue" { self.with_view_mut(move |v| v.on_command(s, cmd)).unwrap()
let dialog = Self::save_dialog(self.queue.clone(), self.library.clone());
s.add_layer(dialog);
return Ok(CommandResult::Consumed(None));
}
self.with_view_mut(move |v| v.on_command(s, cmd, args))
.unwrap()
} }
} }

View File

@@ -11,6 +11,7 @@ use std::sync::{Arc, Mutex, RwLock};
use album::Album; use album::Album;
use artist::Artist; use artist::Artist;
use command::{Command, MoveMode};
use commands::CommandResult; use commands::CommandResult;
use events::EventManager; use events::EventManager;
use library::Library; use library::Library;
@@ -435,42 +436,37 @@ impl View for SearchView {
} }
impl ViewExt for SearchView { impl ViewExt for SearchView {
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, match cmd {
s: &mut Cursive, Command::Search(query) => self.run_search(query.to_string()),
cmd: &str, Command::Focus(view) => {
args: &[String], self.edit_focused = true;
) -> Result<CommandResult, String> { self.clear();
if cmd == "search" && !args.is_empty() { return Ok(CommandResult::Consumed(None));
self.run_search(args.join(" ")); }
return Ok(CommandResult::Consumed(None)); _ => {}
}
if cmd == "focus" {
self.edit_focused = true;
self.clear();
return Ok(CommandResult::Consumed(None));
} }
let result = if !self.edit_focused { let result = if !self.edit_focused {
self.tabs.on_command(s, cmd, args)? self.tabs.on_command(s, cmd)?
} else { } else {
CommandResult::Ignored CommandResult::Ignored
}; };
if let CommandResult::Ignored = result { if let CommandResult::Ignored = result {
if cmd == "move" { match cmd {
if let Some(dir) = args.get(0) { Command::Move(mode, amount) => match mode {
if dir == "up" && !self.edit_focused { MoveMode::Up if !self.edit_focused => {
self.edit_focused = true; self.edit_focused = true;
return Ok(CommandResult::Consumed(None)); return Ok(CommandResult::Consumed(None));
} }
MoveMode::Down if self.edit_focused => {
if dir == "down" && self.edit_focused {
self.edit_focused = false; self.edit_focused = false;
return Ok(CommandResult::Consumed(None)); return Ok(CommandResult::Consumed(None));
} }
} _ => {}
},
_ => {}
} }
} }

View File

@@ -7,6 +7,7 @@ use cursive::traits::View;
use cursive::{Cursive, Printer, Vec2}; use cursive::{Cursive, Printer, Vec2};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use command::{Command, MoveMode};
use commands::CommandResult; use commands::CommandResult;
use traits::{IntoBoxedViewExt, ViewExt}; use traits::{IntoBoxedViewExt, ViewExt};
@@ -102,36 +103,33 @@ impl View for TabView {
} }
impl ViewExt for TabView { impl ViewExt for TabView {
fn on_command( fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
&mut self, match cmd {
s: &mut Cursive, Command::Move(mode, amount) => {
cmd: &str, let amount = match amount {
args: &[String], Some(amount) => *amount,
) -> Result<CommandResult, String> { _ => 1,
if cmd == "move" { };
if let Some(dir) = args.get(0) {
let amount: i32 = args
.get(1)
.unwrap_or(&"1".to_string())
.parse()
.map_err(|e| format!("{:?}", e))?;
let len = self.tabs.len(); let len = self.tabs.len();
if dir == "left" && self.selected > 0 { match mode {
self.move_focus(-amount); MoveMode::Left if self.selected > 0 => {
return Ok(CommandResult::Consumed(None)); self.move_focus(-(amount as i32));
} return Ok(CommandResult::Consumed(None));
}
if dir == "right" && self.selected < len - 1 { MoveMode::Right if self.selected < len - 1 => {
self.move_focus(amount); self.move_focus(amount as i32);
return Ok(CommandResult::Consumed(None)); return Ok(CommandResult::Consumed(None));
}
_ => {}
} }
} }
_ => {}
} }
if let Some(tab) = self.tabs.get_mut(self.selected) { if let Some(tab) = self.tabs.get_mut(self.selected) {
tab.view.on_command(s, cmd, args) tab.view.on_command(s, cmd)
} else { } else {
Ok(CommandResult::Ignored) Ok(CommandResult::Ignored)
} }