mirror of
https://github.com/imgurbot12/rmenu.git
synced 2025-02-12 13:15:07 +01:00
feat: allow plugins to have their own config overrides
This commit is contained in:
parent
19901cbe34
commit
7d1a12c05d
8 changed files with 121 additions and 84 deletions
|
@ -171,6 +171,9 @@ struct OptionArgs {
|
||||||
/// Override Window Fullscreen Settings
|
/// Override Window Fullscreen Settings
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub fullscreen: Option<bool>,
|
pub fullscreen: Option<bool>,
|
||||||
|
/// Override Window Tranparent Settings
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub transparent: Option<bool>,
|
||||||
/// Override Window Width
|
/// Override Window Width
|
||||||
#[arg(short = 'w', long)]
|
#[arg(short = 'w', long)]
|
||||||
pub window_width: Option<f64>,
|
pub window_width: Option<f64>,
|
||||||
|
@ -198,6 +201,7 @@ impl Into<Options> for OptionArgs {
|
||||||
title: self.title,
|
title: self.title,
|
||||||
decorate: self.deocorate,
|
decorate: self.deocorate,
|
||||||
fullscreen: self.fullscreen,
|
fullscreen: self.fullscreen,
|
||||||
|
transparent: self.transparent,
|
||||||
window_width: self.window_width,
|
window_width: self.window_width,
|
||||||
window_height: self.window_height,
|
window_height: self.window_height,
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,8 @@ pub struct Options {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub decorate: Option<bool>,
|
pub decorate: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub transparent: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub fullscreen: Option<bool>,
|
pub fullscreen: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub window_width: Option<f64>,
|
pub window_width: Option<f64>,
|
||||||
|
|
|
@ -25,6 +25,15 @@ plugins:
|
||||||
drun:
|
drun:
|
||||||
exec: ["~/.config/rmenu/rmenu-desktop"]
|
exec: ["~/.config/rmenu/rmenu-desktop"]
|
||||||
cache: onlogin
|
cache: onlogin
|
||||||
|
options:
|
||||||
|
css: ~/Code/rust/rmenu2/themes/fullscreen.css
|
||||||
|
transparent: true
|
||||||
|
window_width: 1200
|
||||||
|
window_height: 800
|
||||||
|
key_move_prev: ["Arrow-Left"]
|
||||||
|
key_move_next: ["Arrow-Right"]
|
||||||
|
key_jump_prev: ["Arrow-Up"]
|
||||||
|
key_jump_next: ["Arrow-Down"]
|
||||||
network:
|
network:
|
||||||
exec: ["~/.config/rmenu/rmenu-network"]
|
exec: ["~/.config/rmenu/rmenu-network"]
|
||||||
cache: false
|
cache: false
|
||||||
|
|
|
@ -78,7 +78,7 @@ pub fn write_cache(name: &str, cfg: &PluginConfig, entries: &Vec<Entry>) -> Resu
|
||||||
match cfg.cache {
|
match cfg.cache {
|
||||||
CacheSetting::NoCache => {}
|
CacheSetting::NoCache => {}
|
||||||
_ => {
|
_ => {
|
||||||
println!("writing {} entries", entries.len());
|
log::debug!("{name:?} writing {} entries", entries.len());
|
||||||
let path = cache_file(name);
|
let path = cache_file(name);
|
||||||
let f = fs::File::create(path)?;
|
let f = fs::File::create(path)?;
|
||||||
serde_json::to_writer(f, entries)?;
|
serde_json::to_writer(f, entries)?;
|
||||||
|
|
114
rmenu/src/cli.rs
114
rmenu/src/cli.rs
|
@ -8,7 +8,7 @@ use clap::Parser;
|
||||||
use rmenu_plugin::{Entry, Message};
|
use rmenu_plugin::{Entry, Message};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::config::{Config, Keybind};
|
use crate::config::{cfg_replace, Config, Keybind};
|
||||||
use crate::{DEFAULT_CONFIG, DEFAULT_CSS};
|
use crate::{DEFAULT_CONFIG, DEFAULT_CSS};
|
||||||
|
|
||||||
/// Allowed Formats for Entry Ingestion
|
/// Allowed Formats for Entry Ingestion
|
||||||
|
@ -177,33 +177,6 @@ pub enum RMenuError {
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, RMenuError>;
|
pub type Result<T> = std::result::Result<T, RMenuError>;
|
||||||
|
|
||||||
macro_rules! cli_replace {
|
|
||||||
($key:expr, $repl:expr) => {
|
|
||||||
if $repl.is_some() {
|
|
||||||
$key = $repl.clone();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($key:expr, $repl:expr, true) => {
|
|
||||||
if let Some(value) = $repl.as_ref() {
|
|
||||||
$key = value.to_owned();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! cli_keybind {
|
|
||||||
($key:expr, $repl:expr) => {
|
|
||||||
if let Some(bind_strings) = $repl.as_ref() {
|
|
||||||
let mut keybinds = vec![];
|
|
||||||
for bind_str in bind_strings.iter() {
|
|
||||||
let bind =
|
|
||||||
Keybind::from_str(bind_str).map_err(|e| RMenuError::InvalidKeybind(e))?;
|
|
||||||
keybinds.push(bind);
|
|
||||||
}
|
|
||||||
$key = keybinds;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
/// Load Configuration File
|
/// Load Configuration File
|
||||||
pub fn get_config(&self) -> Result<Config> {
|
pub fn get_config(&self) -> Result<Config> {
|
||||||
|
@ -233,32 +206,32 @@ impl Args {
|
||||||
config.use_icons = self.use_icons.unwrap_or(config.use_icons);
|
config.use_icons = self.use_icons.unwrap_or(config.use_icons);
|
||||||
config.use_comments = self.use_icons.unwrap_or(config.use_comments);
|
config.use_comments = self.use_icons.unwrap_or(config.use_comments);
|
||||||
// override search settings
|
// override search settings
|
||||||
cli_replace!(config.search.restrict, self.search_restrict);
|
cfg_replace!(config.search.restrict, self.search_restrict);
|
||||||
cli_replace!(config.search.min_length, self.search_min_length);
|
cfg_replace!(config.search.min_length, self.search_min_length);
|
||||||
cli_replace!(config.search.max_length, self.search_max_length);
|
cfg_replace!(config.search.max_length, self.search_max_length);
|
||||||
cli_replace!(config.search.use_regex, self.search_regex, true);
|
cfg_replace!(config.search.use_regex, self.search_regex, true);
|
||||||
cli_replace!(config.search.ignore_case, self.ignore_case, true);
|
cfg_replace!(config.search.ignore_case, self.ignore_case, true);
|
||||||
cli_replace!(config.search.placeholder, self.placeholder);
|
cfg_replace!(config.search.placeholder, self.placeholder);
|
||||||
// override keybind settings
|
// override keybind settings
|
||||||
cli_replace!(config.keybinds.exec, self.key_exec, true);
|
cfg_replace!(config.keybinds.exec, self.key_exec, true);
|
||||||
cli_replace!(config.keybinds.exit, self.key_exit, true);
|
cfg_replace!(config.keybinds.exit, self.key_exit, true);
|
||||||
cli_replace!(config.keybinds.move_next, self.key_move_next, true);
|
cfg_replace!(config.keybinds.move_next, self.key_move_next, true);
|
||||||
cli_replace!(config.keybinds.move_prev, self.key_move_prev, true);
|
cfg_replace!(config.keybinds.move_prev, self.key_move_prev, true);
|
||||||
cli_replace!(config.keybinds.open_menu, self.key_open_menu, true);
|
cfg_replace!(config.keybinds.open_menu, self.key_open_menu, true);
|
||||||
cli_replace!(config.keybinds.close_menu, self.key_close_menu, true);
|
cfg_replace!(config.keybinds.close_menu, self.key_close_menu, true);
|
||||||
cli_replace!(config.keybinds.jump_next, self.key_jump_next, true);
|
cfg_replace!(config.keybinds.jump_next, self.key_jump_next, true);
|
||||||
cli_replace!(config.keybinds.jump_prev, self.key_jump_prev, true);
|
cfg_replace!(config.keybinds.jump_prev, self.key_jump_prev, true);
|
||||||
// override window settings
|
// override window settings
|
||||||
cli_replace!(config.window.title, self.title, true);
|
cfg_replace!(config.window.title, self.title, true);
|
||||||
cli_replace!(config.window.size.width, self.width, true);
|
cfg_replace!(config.window.size.width, self.width, true);
|
||||||
cli_replace!(config.window.size.height, self.height, true);
|
cfg_replace!(config.window.size.height, self.height, true);
|
||||||
cli_replace!(config.window.position.x, self.xpos, true);
|
cfg_replace!(config.window.position.x, self.xpos, true);
|
||||||
cli_replace!(config.window.position.y, self.ypos, true);
|
cfg_replace!(config.window.position.y, self.ypos, true);
|
||||||
cli_replace!(config.window.focus, self.focus, true);
|
cfg_replace!(config.window.focus, self.focus, true);
|
||||||
cli_replace!(config.window.decorate, self.decorate, true);
|
cfg_replace!(config.window.decorate, self.decorate, true);
|
||||||
cli_replace!(config.window.transparent, self.transparent, true);
|
cfg_replace!(config.window.transparent, self.transparent, true);
|
||||||
cli_replace!(config.window.always_top, self.always_top, true);
|
cfg_replace!(config.window.always_top, self.always_top, true);
|
||||||
cli_replace!(config.window.fullscreen, self.fullscreen);
|
cfg_replace!(config.window.fullscreen, self.fullscreen);
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,8 +248,8 @@ impl Args {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load Additional CSS or Default
|
/// Load Additional CSS or Default
|
||||||
pub fn get_css(&self) -> String {
|
pub fn get_css(&self, c: &Config) -> String {
|
||||||
if let Some(css) = self.css.as_ref() {
|
if let Some(css) = self.css.as_ref().or(c.css.as_ref()) {
|
||||||
let path = shellexpand::tilde(&css).to_string();
|
let path = shellexpand::tilde(&css).to_string();
|
||||||
match read_to_string(&path) {
|
match read_to_string(&path) {
|
||||||
Ok(theme) => return theme,
|
Ok(theme) => return theme,
|
||||||
|
@ -299,30 +272,9 @@ impl Args {
|
||||||
let msg: Message = serde_json::from_str(&line)?;
|
let msg: Message = serde_json::from_str(&line)?;
|
||||||
match msg {
|
match msg {
|
||||||
Message::Entry(entry) => v.push(entry),
|
Message::Entry(entry) => v.push(entry),
|
||||||
Message::Options(options) => {
|
Message::Options(options) => c
|
||||||
// base settings
|
.update(&options)
|
||||||
self.css = self.css.clone().or(options.css);
|
.map_err(|s| RMenuError::InvalidKeybind(s))?,
|
||||||
// search settings
|
|
||||||
cli_replace!(c.search.placeholder, options.placeholder);
|
|
||||||
cli_replace!(c.search.restrict, options.search_restrict);
|
|
||||||
cli_replace!(c.search.min_length, options.search_min_length);
|
|
||||||
cli_replace!(c.search.max_length, options.search_max_length);
|
|
||||||
// keybind settings
|
|
||||||
cli_keybind!(c.keybinds.exec, options.key_exec);
|
|
||||||
cli_keybind!(c.keybinds.exec, options.key_exec);
|
|
||||||
cli_keybind!(c.keybinds.exit, options.key_exit);
|
|
||||||
cli_keybind!(c.keybinds.move_next, options.key_move_next);
|
|
||||||
cli_keybind!(c.keybinds.move_prev, options.key_move_prev);
|
|
||||||
cli_keybind!(c.keybinds.open_menu, options.key_open_menu);
|
|
||||||
cli_keybind!(c.keybinds.close_menu, options.key_close_menu);
|
|
||||||
cli_keybind!(c.keybinds.jump_next, options.key_jump_next);
|
|
||||||
cli_keybind!(c.keybinds.jump_prev, options.key_jump_prev);
|
|
||||||
// window settings
|
|
||||||
cli_replace!(c.window.title, options.title, true);
|
|
||||||
cli_replace!(c.window.decorate, options.decorate, true);
|
|
||||||
cli_replace!(c.window.size.width, options.window_width, true);
|
|
||||||
cli_replace!(c.window.size.height, options.window_height, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,6 +307,12 @@ impl Args {
|
||||||
.get(&name)
|
.get(&name)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| RMenuError::NoSuchPlugin(name.to_owned()))?;
|
.ok_or_else(|| RMenuError::NoSuchPlugin(name.to_owned()))?;
|
||||||
|
// update config w/ plugin options when available
|
||||||
|
if let Some(options) = plugin.options.as_ref() {
|
||||||
|
config
|
||||||
|
.update(options)
|
||||||
|
.map_err(|e| RMenuError::InvalidKeybind(e))?;
|
||||||
|
}
|
||||||
// read cache when available
|
// read cache when available
|
||||||
match crate::cache::read_cache(&name, &plugin) {
|
match crate::cache::read_cache(&name, &plugin) {
|
||||||
Err(err) => log::error!("cache read failed: {err:?}"),
|
Err(err) => log::error!("cache read failed: {err:?}"),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! RMENU Configuration Implementations
|
//! RMENU Configuration Implementations
|
||||||
use heck::AsPascalCase;
|
use heck::AsPascalCase;
|
||||||
use keyboard_types::{Code, Modifiers};
|
use keyboard_types::{Code, Modifiers};
|
||||||
|
use rmenu_plugin::Options;
|
||||||
use serde::{de::Error, Deserialize};
|
use serde::{de::Error, Deserialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -201,6 +202,8 @@ pub struct PluginConfig {
|
||||||
pub cache: CacheSetting,
|
pub cache: CacheSetting,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub placeholder: Option<String>,
|
pub placeholder: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub options: Option<Options>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -249,6 +252,7 @@ pub struct Config {
|
||||||
pub plugins: BTreeMap<String, PluginConfig>,
|
pub plugins: BTreeMap<String, PluginConfig>,
|
||||||
pub keybinds: KeyConfig,
|
pub keybinds: KeyConfig,
|
||||||
pub window: WindowConfig,
|
pub window: WindowConfig,
|
||||||
|
pub css: Option<String>,
|
||||||
pub terminal: Option<String>,
|
pub terminal: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +268,67 @@ impl Default for Config {
|
||||||
plugins: Default::default(),
|
plugins: Default::default(),
|
||||||
keybinds: Default::default(),
|
keybinds: Default::default(),
|
||||||
window: Default::default(),
|
window: Default::default(),
|
||||||
terminal: Default::default(),
|
css: None,
|
||||||
|
terminal: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! cfg_replace {
|
||||||
|
($key:expr, $repl:expr) => {
|
||||||
|
if $repl.is_some() {
|
||||||
|
$key = $repl.clone();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($key:expr, $repl:expr, true) => {
|
||||||
|
if let Some(value) = $repl.as_ref() {
|
||||||
|
$key = value.to_owned();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cfg_keybind {
|
||||||
|
($key:expr, $repl:expr) => {
|
||||||
|
if let Some(bind_strings) = $repl.as_ref() {
|
||||||
|
let mut keybinds = vec![];
|
||||||
|
for bind_str in bind_strings.iter() {
|
||||||
|
let bind = Keybind::from_str(bind_str)?;
|
||||||
|
keybinds.push(bind);
|
||||||
|
}
|
||||||
|
$key = keybinds;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use cfg_keybind;
|
||||||
|
pub(crate) use cfg_replace;
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// Update Configuration from Options Object
|
||||||
|
pub fn update(&mut self, options: &Options) -> Result<(), String> {
|
||||||
|
cfg_replace!(self.css, options.css);
|
||||||
|
// search settings
|
||||||
|
cfg_replace!(self.search.placeholder, options.placeholder);
|
||||||
|
cfg_replace!(self.search.restrict, options.search_restrict);
|
||||||
|
cfg_replace!(self.search.min_length, options.search_min_length);
|
||||||
|
cfg_replace!(self.search.max_length, options.search_max_length);
|
||||||
|
// keybind settings
|
||||||
|
cfg_keybind!(self.keybinds.exec, options.key_exec);
|
||||||
|
cfg_keybind!(self.keybinds.exec, options.key_exec);
|
||||||
|
cfg_keybind!(self.keybinds.exit, options.key_exit);
|
||||||
|
cfg_keybind!(self.keybinds.move_next, options.key_move_next);
|
||||||
|
cfg_keybind!(self.keybinds.move_prev, options.key_move_prev);
|
||||||
|
cfg_keybind!(self.keybinds.open_menu, options.key_open_menu);
|
||||||
|
cfg_keybind!(self.keybinds.close_menu, options.key_close_menu);
|
||||||
|
cfg_keybind!(self.keybinds.jump_next, options.key_jump_next);
|
||||||
|
cfg_keybind!(self.keybinds.jump_prev, options.key_jump_prev);
|
||||||
|
// window settings
|
||||||
|
cfg_replace!(self.window.title, options.title, true);
|
||||||
|
cfg_replace!(self.window.decorate, options.decorate, true);
|
||||||
|
cfg_replace!(self.window.fullscreen, options.fullscreen);
|
||||||
|
cfg_replace!(self.window.transparent, options.transparent, true);
|
||||||
|
cfg_replace!(self.window.size.width, options.window_width, true);
|
||||||
|
cfg_replace!(self.window.size.height, options.window_height, true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//! GUI Image Processing
|
//! GUI Image Processing
|
||||||
use std::fs::{create_dir_all, read_to_string, write};
|
use std::fs::{create_dir_all, write};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
@ -37,9 +37,9 @@ fn make_temp() -> Result<(), io::Error> {
|
||||||
/// Convert SVG to PNG Image
|
/// Convert SVG to PNG Image
|
||||||
fn svg_to_png(path: &str, dest: &PathBuf, pixels: u32) -> Result<(), SvgError> {
|
fn svg_to_png(path: &str, dest: &PathBuf, pixels: u32) -> Result<(), SvgError> {
|
||||||
// read and convert to resvg document tree
|
// read and convert to resvg document tree
|
||||||
let xml = read_to_string(path)?;
|
let xml = std::fs::read(path)?;
|
||||||
let opt = resvg::usvg::Options::default();
|
let opt = resvg::usvg::Options::default();
|
||||||
let tree = resvg::usvg::Tree::from_str(&xml, &opt)?;
|
let tree = resvg::usvg::Tree::from_data(&xml, &opt)?;
|
||||||
let rtree = resvg::Tree::from_usvg(&tree);
|
let rtree = resvg::Tree::from_usvg(&tree);
|
||||||
// generate pixel-buffer and scale according to size preference
|
// generate pixel-buffer and scale according to size preference
|
||||||
let size = rtree.size.to_int_size();
|
let size = rtree.size.to_int_size();
|
||||||
|
|
|
@ -45,7 +45,7 @@ fn main() -> cli::Result<()> {
|
||||||
let mut cli = cli::Args::parse();
|
let mut cli = cli::Args::parse();
|
||||||
let mut config = cli.get_config()?;
|
let mut config = cli.get_config()?;
|
||||||
let entries = cli.get_entries(&mut config)?;
|
let entries = cli.get_entries(&mut config)?;
|
||||||
let css = cli.get_css();
|
let css = cli.get_css(&config);
|
||||||
let theme = cli.get_theme();
|
let theme = cli.get_theme();
|
||||||
|
|
||||||
// update config based on cli-settings and entries
|
// update config based on cli-settings and entries
|
||||||
|
|
Loading…
Reference in a new issue