implement command to reload config (#243)

* implement command to reload themes

* refresh keybindings after config reload
This commit is contained in:
Henrik Friedrichsen
2020-08-12 01:12:08 +02:00
committed by GitHub
parent 9e5e3f3089
commit 4f71b2489b
5 changed files with 76 additions and 37 deletions

View File

@@ -117,8 +117,11 @@ the search view.
## Configuration ## Configuration
Configuration is saved to `~/.config/ncspot/config.toml`. Possible configuration Configuration is saved to `~/.config/ncspot/config.toml`. To reload the
values are: configuration during runtime use the `reload` statement in the command prompt
`:reload`.
Possible configuration values are:
* `use_nerdfont`: Turn nerdfont glyphs on/off <true/false> * `use_nerdfont`: Turn nerdfont glyphs on/off <true/false>
* `theme`: Set a custom color palette (see below) * `theme`: Set a custom color palette (see below)

View File

@@ -102,6 +102,7 @@ pub enum Command {
Shift(ShiftMode, Option<i32>), Shift(ShiftMode, Option<i32>),
Search(String), Search(String),
Help, Help,
ReloadConfig,
Noop, Noop,
} }
@@ -155,6 +156,7 @@ impl fmt::Display for Command {
Command::Shift(mode, amount) => format!("shift {} {}", mode, amount.unwrap_or(1)), Command::Shift(mode, amount) => format!("shift {} {}", mode, amount.unwrap_or(1)),
Command::Search(term) => format!("search {}", term), Command::Search(term) => format!("search {}", term),
Command::Help => "help".to_string(), Command::Help => "help".to_string(),
Command::ReloadConfig => "reload".to_string(),
}; };
write!(f, "{}", repr) write!(f, "{}", repr)
} }
@@ -329,6 +331,7 @@ pub fn parse(input: &str) -> Option<Command> {
"volup" => Some(Command::VolumeUp), "volup" => Some(Command::VolumeUp),
"voldown" => Some(Command::VolumeDown), "voldown" => Some(Command::VolumeDown),
"help" => Some(Command::Help), "help" => Some(Command::Help),
"reload" => Some(Command::ReloadConfig),
"noop" => Some(Command::Noop), "noop" => Some(Command::Noop),
_ => None, _ => None,
} }

View File

@@ -12,10 +12,12 @@ use crate::spotify::{Spotify, VOLUME_PERCENT};
use crate::traits::ViewExt; use crate::traits::ViewExt;
use crate::ui::help::HelpView; use crate::ui::help::HelpView;
use crate::ui::layout::Layout; use crate::ui::layout::Layout;
use crate::UserData;
use cursive::event::{Event, Key}; use cursive::event::{Event, Key};
use cursive::traits::View; use cursive::traits::View;
use cursive::views::ViewRef; use cursive::views::ViewRef;
use cursive::Cursive; use cursive::Cursive;
use std::cell::RefCell;
pub enum CommandResult { pub enum CommandResult {
Consumed(Option<String>), Consumed(Option<String>),
@@ -26,7 +28,7 @@ pub enum CommandResult {
pub struct CommandManager { pub struct CommandManager {
aliases: HashMap<String, String>, aliases: HashMap<String, String>,
bindings: HashMap<String, Command>, bindings: RefCell<HashMap<String, Command>>,
spotify: Arc<Spotify>, spotify: Arc<Spotify>,
queue: Arc<Queue>, queue: Arc<Queue>,
library: Arc<Library>, library: Arc<Library>,
@@ -39,6 +41,16 @@ impl CommandManager {
library: Arc<Library>, library: Arc<Library>,
config: &Config, config: &Config,
) -> CommandManager { ) -> CommandManager {
CommandManager {
aliases: HashMap::new(),
bindings: RefCell::new(Self::get_bindings(config)),
spotify,
queue,
library,
}
}
pub fn get_bindings(config: &Config) -> HashMap<String, Command> {
let mut kb = if config.default_keybindings.unwrap_or(true) { let mut kb = if config.default_keybindings.unwrap_or(true) {
Self::default_keybindings() Self::default_keybindings()
} else { } else {
@@ -55,13 +67,7 @@ impl CommandManager {
} }
} }
CommandManager { kb
aliases: HashMap::new(),
bindings: kb,
spotify,
queue,
library,
}
} }
pub fn register_aliases<S: Into<String>>(&mut self, name: S, aliases: Vec<S>) { pub fn register_aliases<S: Into<String>>(&mut self, name: S, aliases: Vec<S>) {
@@ -150,10 +156,23 @@ impl CommandManager {
Ok(None) Ok(None)
} }
Command::Help => { 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)); s.call_on_name("main", move |v: &mut Layout| v.push_view(view));
Ok(None) 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::Search(_)
| Command::Move(_, _) | Command::Move(_, _)
| Command::Shift(_, _) | Command::Shift(_, _)
@@ -201,30 +220,38 @@ impl CommandManager {
} }
pub fn register_keybinding<E: Into<cursive::event::Event>>( pub fn register_keybinding<E: Into<cursive::event::Event>>(
this: Arc<Self>, &self,
cursive: &mut Cursive, cursive: &mut Cursive,
event: E, event: E,
command: Command, command: Command,
) { ) {
cursive.add_global_callback(event, move |s| { cursive.add_global_callback(event, move |s| {
this.handle(s, command.clone()); if let Some(data) = s.user_data::<UserData>().cloned() {
data.cmd.handle(s, command.clone());
}
}); });
} }
pub fn register_keybindings(this: Arc<Self>, cursive: &mut Cursive) { pub fn unregister_keybindings(&self, cursive: &mut Cursive) {
let kb = this.keybindings(); let kb = self.bindings.borrow();
for (k, v) in kb { for (k, _v) in kb.iter() {
if let Some(binding) = Self::parse_keybinding(&k) { if let Some(binding) = Self::parse_keybinding(&k) {
Self::register_keybinding(this.clone(), cursive, binding, v.clone()); cursive.clear_global_callbacks(binding);
} else {
error!("Could not parse keybinding: \"{}\"", &k);
} }
} }
} }
pub fn keybindings(&self) -> &HashMap<String, Command> { pub fn register_keybindings(&self, cursive: &mut Cursive) {
&self.bindings 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<String, Command> { fn default_keybindings() -> HashMap<String, Command> {

View File

@@ -53,6 +53,11 @@ lazy_static! {
pub static ref BASE_PATH: RwLock<Option<PathBuf>> = RwLock::new(None); pub static ref BASE_PATH: RwLock<Option<PathBuf>> = RwLock::new(None);
} }
pub fn load() -> Result<Config, String> {
let path = config_path("config.toml");
load_or_generate_default(path, |_| Ok(Config::default()), false)
}
fn proj_dirs() -> ProjectDirs { fn proj_dirs() -> ProjectDirs {
match *BASE_PATH.read().expect("can't readlock BASE_PATH") { match *BASE_PATH.read().expect("can't readlock BASE_PATH") {
Some(ref basepath) => ProjectDirs::from_path(basepath.clone()).expect("invalid basepath"), Some(ref basepath) => ProjectDirs::from_path(basepath.clone()).expect("invalid basepath"),

View File

@@ -135,6 +135,11 @@ fn credentials_prompt(reset: bool, error_message: Option<String>) -> Credentials
creds creds
} }
type UserData = Arc<UserDataInner>;
struct UserDataInner {
pub cmd: CommandManager,
}
fn main() { fn main() {
let backends = { let backends = {
let backends: Vec<&str> = audio_backend::BACKENDS.iter().map(|b| b.0).collect(); 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 // 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 // otherwise the error message will not be seen by a user
let cfg: crate::config::Config = { let cfg: crate::config::Config = config::load().unwrap_or_else(|e| {
let path = config::config_path("config.toml"); eprintln!("{}", e);
crate::config::load_or_generate_default( process::exit(1);
path, });
|_| Ok(crate::config::Config::default()),
false,
)
.unwrap_or_else(|e| {
eprintln!("{}", e);
process::exit(1);
})
};
let cache = Cache::new(config::cache_path("librespot"), true); let cache = Cache::new(config::cache_path("librespot"), true);
let mut credentials = { let mut credentials = {
@@ -240,10 +237,12 @@ fn main() {
let mut cmd_manager = let mut cmd_manager =
CommandManager::new(spotify.clone(), queue.clone(), library.clone(), &cfg); CommandManager::new(spotify.clone(), queue.clone(), library.clone(), &cfg);
cmd_manager.register_all();
let cmd_manager = Arc::new(cmd_manager); cmd_manager.register_all();
CommandManager::register_keybindings(cmd_manager.clone(), &mut cursive); 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( let search = ui::search::SearchView::new(
event_manager.clone(), event_manager.clone(),
@@ -291,7 +290,9 @@ fn main() {
let c = &cmd[1..]; let c = &cmd[1..];
let parsed = command::parse(c); let parsed = command::parse(c);
if let Some(parsed) = parsed { if let Some(parsed) = parsed {
cmd_manager.handle(s, parsed); if let Some(data) = s.user_data::<UserData>().cloned() {
data.cmd.handle(s, parsed)
}
} }
ev.trigger(); ev.trigger();
}); });