implement command to reload config (#243)
* implement command to reload themes * refresh keybindings after config reload
This commit is contained in:
committed by
GitHub
parent
9e5e3f3089
commit
4f71b2489b
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
33
src/main.rs
33
src/main.rs
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user