From 4f71b2489bf8a61bec9c40bf1a2579c639eaff94 Mon Sep 17 00:00:00 2001 From: Henrik Friedrichsen Date: Wed, 12 Aug 2020 01:12:08 +0200 Subject: [PATCH] implement command to reload config (#243) * implement command to reload themes * refresh keybindings after config reload --- README.md | 7 ++++-- src/command.rs | 3 +++ src/commands.rs | 65 ++++++++++++++++++++++++++++++++++--------------- src/config.rs | 5 ++++ src/main.rs | 33 +++++++++++++------------ 5 files changed, 76 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 79d2f92..6ce39f5 100644 --- a/README.md +++ b/README.md @@ -117,8 +117,11 @@ the search view. ## Configuration -Configuration is saved to `~/.config/ncspot/config.toml`. Possible configuration -values are: +Configuration is saved to `~/.config/ncspot/config.toml`. To reload the +configuration during runtime use the `reload` statement in the command prompt +`:reload`. + +Possible configuration values are: * `use_nerdfont`: Turn nerdfont glyphs on/off * `theme`: Set a custom color palette (see below) diff --git a/src/command.rs b/src/command.rs index 43da6a5..e5b7f4b 100644 --- a/src/command.rs +++ b/src/command.rs @@ -102,6 +102,7 @@ pub enum Command { Shift(ShiftMode, Option), Search(String), Help, + ReloadConfig, Noop, } @@ -155,6 +156,7 @@ impl fmt::Display for Command { Command::Shift(mode, amount) => format!("shift {} {}", mode, amount.unwrap_or(1)), Command::Search(term) => format!("search {}", term), Command::Help => "help".to_string(), + Command::ReloadConfig => "reload".to_string(), }; write!(f, "{}", repr) } @@ -329,6 +331,7 @@ pub fn parse(input: &str) -> Option { "volup" => Some(Command::VolumeUp), "voldown" => Some(Command::VolumeDown), "help" => Some(Command::Help), + "reload" => Some(Command::ReloadConfig), "noop" => Some(Command::Noop), _ => None, } diff --git a/src/commands.rs b/src/commands.rs index f0c2283..4ffb5bb 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -12,10 +12,12 @@ use crate::spotify::{Spotify, VOLUME_PERCENT}; use crate::traits::ViewExt; use crate::ui::help::HelpView; use crate::ui::layout::Layout; +use crate::UserData; use cursive::event::{Event, Key}; use cursive::traits::View; use cursive::views::ViewRef; use cursive::Cursive; +use std::cell::RefCell; pub enum CommandResult { Consumed(Option), @@ -26,7 +28,7 @@ pub enum CommandResult { pub struct CommandManager { aliases: HashMap, - bindings: HashMap, + bindings: RefCell>, spotify: Arc, queue: Arc, library: Arc, @@ -39,6 +41,16 @@ impl CommandManager { library: Arc, config: &Config, ) -> CommandManager { + CommandManager { + aliases: HashMap::new(), + bindings: RefCell::new(Self::get_bindings(config)), + spotify, + queue, + library, + } + } + + pub fn get_bindings(config: &Config) -> HashMap { let mut kb = if config.default_keybindings.unwrap_or(true) { Self::default_keybindings() } else { @@ -55,13 +67,7 @@ impl CommandManager { } } - CommandManager { - aliases: HashMap::new(), - bindings: kb, - spotify, - queue, - library, - } + kb } pub fn register_aliases>(&mut self, name: S, aliases: Vec) { @@ -150,10 +156,23 @@ impl CommandManager { Ok(None) } Command::Help => { - let view = Box::new(HelpView::new(self.keybindings().clone())); + let view = Box::new(HelpView::new(self.bindings.borrow().clone())); s.call_on_name("main", move |v: &mut Layout| v.push_view(view)); Ok(None) } + Command::ReloadConfig => { + let cfg = crate::config::load()?; + + // update theme + let theme = crate::theme::load(&cfg); + s.set_theme(theme); + + // update bindings + self.unregister_keybindings(s); + self.bindings.replace(Self::get_bindings(&cfg)); + self.register_keybindings(s); + Ok(None) + } Command::Search(_) | Command::Move(_, _) | Command::Shift(_, _) @@ -201,30 +220,38 @@ impl CommandManager { } pub fn register_keybinding>( - this: Arc, + &self, cursive: &mut Cursive, event: E, command: Command, ) { cursive.add_global_callback(event, move |s| { - this.handle(s, command.clone()); + if let Some(data) = s.user_data::().cloned() { + data.cmd.handle(s, command.clone()); + } }); } - pub fn register_keybindings(this: Arc, cursive: &mut Cursive) { - let kb = this.keybindings(); + pub fn unregister_keybindings(&self, cursive: &mut Cursive) { + let kb = self.bindings.borrow(); - for (k, v) in kb { + for (k, _v) in kb.iter() { if let Some(binding) = Self::parse_keybinding(&k) { - Self::register_keybinding(this.clone(), cursive, binding, v.clone()); - } else { - error!("Could not parse keybinding: \"{}\"", &k); + cursive.clear_global_callbacks(binding); } } } - pub fn keybindings(&self) -> &HashMap { - &self.bindings + pub fn register_keybindings(&self, cursive: &mut Cursive) { + let kb = self.bindings.borrow(); + + for (k, v) in kb.iter() { + if let Some(binding) = Self::parse_keybinding(&k) { + self.register_keybinding(cursive, binding, v.clone()); + } else { + error!("Could not parse keybinding: \"{}\"", &k); + } + } } fn default_keybindings() -> HashMap { diff --git a/src/config.rs b/src/config.rs index 68d6955..3a5187a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -53,6 +53,11 @@ lazy_static! { pub static ref BASE_PATH: RwLock> = RwLock::new(None); } +pub fn load() -> Result { + let path = config_path("config.toml"); + load_or_generate_default(path, |_| Ok(Config::default()), false) +} + fn proj_dirs() -> ProjectDirs { match *BASE_PATH.read().expect("can't readlock BASE_PATH") { Some(ref basepath) => ProjectDirs::from_path(basepath.clone()).expect("invalid basepath"), diff --git a/src/main.rs b/src/main.rs index 3ae0d01..402edde 100644 --- a/src/main.rs +++ b/src/main.rs @@ -135,6 +135,11 @@ fn credentials_prompt(reset: bool, error_message: Option) -> Credentials creds } +type UserData = Arc; +struct UserDataInner { + pub cmd: CommandManager, +} + fn main() { let backends = { let backends: Vec<&str> = audio_backend::BACKENDS.iter().map(|b| b.0).collect(); @@ -177,18 +182,10 @@ fn main() { // Things here may cause the process to abort; we must do them before creating curses windows // otherwise the error message will not be seen by a user - let cfg: crate::config::Config = { - let path = config::config_path("config.toml"); - crate::config::load_or_generate_default( - path, - |_| Ok(crate::config::Config::default()), - false, - ) - .unwrap_or_else(|e| { - eprintln!("{}", e); - process::exit(1); - }) - }; + let cfg: crate::config::Config = config::load().unwrap_or_else(|e| { + eprintln!("{}", e); + process::exit(1); + }); let cache = Cache::new(config::cache_path("librespot"), true); let mut credentials = { @@ -240,10 +237,12 @@ fn main() { let mut cmd_manager = CommandManager::new(spotify.clone(), queue.clone(), library.clone(), &cfg); - cmd_manager.register_all(); - let cmd_manager = Arc::new(cmd_manager); - CommandManager::register_keybindings(cmd_manager.clone(), &mut cursive); + cmd_manager.register_all(); + cmd_manager.register_keybindings(&mut cursive); + + let user_data: UserData = Arc::new(UserDataInner { cmd: cmd_manager }); + cursive.set_user_data(user_data); let search = ui::search::SearchView::new( event_manager.clone(), @@ -291,7 +290,9 @@ fn main() { let c = &cmd[1..]; let parsed = command::parse(c); if let Some(parsed) = parsed { - cmd_manager.handle(s, parsed); + if let Some(data) = s.user_data::().cloned() { + data.cmd.handle(s, parsed) + } } ev.trigger(); });