mirror of
https://github.com/imgurbot12/rmenu.git
synced 2025-01-27 21:38:14 +01:00
feat: better cfg defaults, navigation fixes, css uses one file w/ builtin backup.
This commit is contained in:
parent
25ee2f32c4
commit
82897da0e2
6 changed files with 42 additions and 49 deletions
|
@ -9,7 +9,7 @@ edition = "2021"
|
||||||
clap = { version = "4.3.15", features = ["derive"] }
|
clap = { version = "4.3.15", features = ["derive"] }
|
||||||
dioxus = "0.3.2"
|
dioxus = "0.3.2"
|
||||||
dioxus-desktop = "0.3.0"
|
dioxus-desktop = "0.3.0"
|
||||||
dirs = "5.0.1"
|
env_logger = "0.10.0"
|
||||||
heck = "0.4.1"
|
heck = "0.4.1"
|
||||||
keyboard-types = "0.6.2"
|
keyboard-types = "0.6.2"
|
||||||
log = "0.4.19"
|
log = "0.4.19"
|
||||||
|
|
|
@ -74,14 +74,13 @@ impl<'de> Deserialize<'de> for Keybind {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
pub struct KeyConfig {
|
pub struct KeyConfig {
|
||||||
pub exec: Vec<Keybind>,
|
pub exec: Vec<Keybind>,
|
||||||
pub exit: Vec<Keybind>,
|
pub exit: Vec<Keybind>,
|
||||||
pub move_up: Vec<Keybind>,
|
pub move_up: Vec<Keybind>,
|
||||||
pub move_down: Vec<Keybind>,
|
pub move_down: Vec<Keybind>,
|
||||||
#[serde(default)]
|
|
||||||
pub open_menu: Vec<Keybind>,
|
pub open_menu: Vec<Keybind>,
|
||||||
#[serde(default)]
|
|
||||||
pub close_menu: Vec<Keybind>,
|
pub close_menu: Vec<Keybind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,23 +128,19 @@ impl Default for WindowConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub css: Vec<String>,
|
|
||||||
pub use_icons: bool,
|
pub use_icons: bool,
|
||||||
pub search_regex: bool,
|
pub search_regex: bool,
|
||||||
pub ignore_case: bool,
|
pub ignore_case: bool,
|
||||||
#[serde(default)]
|
|
||||||
pub plugins: BTreeMap<String, Vec<String>>,
|
pub plugins: BTreeMap<String, Vec<String>>,
|
||||||
#[serde(default)]
|
|
||||||
pub keybinds: KeyConfig,
|
pub keybinds: KeyConfig,
|
||||||
#[serde(default)]
|
|
||||||
pub window: WindowConfig,
|
pub window: WindowConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
css: vec![],
|
|
||||||
use_icons: true,
|
use_icons: true,
|
||||||
search_regex: false,
|
search_regex: false,
|
||||||
ignore_case: true,
|
ignore_case: true,
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::process::Command;
|
||||||
use rmenu_plugin::Action;
|
use rmenu_plugin::Action;
|
||||||
|
|
||||||
pub fn execute(action: &Action) {
|
pub fn execute(action: &Action) {
|
||||||
|
log::info!("executing: {} {:?}", action.name, action.exec);
|
||||||
let args = match shell_words::split(&action.exec) {
|
let args = match shell_words::split(&action.exec) {
|
||||||
Ok(args) => args,
|
Ok(args) => args,
|
||||||
Err(err) => panic!("{:?} invalid command {err}", action.exec),
|
Err(err) => panic!("{:?} invalid command {err}", action.exec),
|
||||||
|
|
|
@ -95,13 +95,8 @@ fn TableEntry<'a>(cx: Scope<'a, GEntry<'a>>) -> Element<'a> {
|
||||||
ondblclick: |_| {
|
ondblclick: |_| {
|
||||||
let action = match cx.props.entry.actions.get(0) {
|
let action = match cx.props.entry.actions.get(0) {
|
||||||
Some(action) => action,
|
Some(action) => action,
|
||||||
None => {
|
None => panic!("No Action Configured"),
|
||||||
let name = &cx.props.entry.name;
|
|
||||||
log::warn!("no action to execute on {:?}", name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
log::info!("executing: {:?}", action.exec);
|
|
||||||
execute(action);
|
execute(action);
|
||||||
},
|
},
|
||||||
if cx.props.config.use_icons {
|
if cx.props.config.use_icons {
|
||||||
|
@ -168,7 +163,7 @@ fn App(cx: Scope<App>) -> Element {
|
||||||
// retrieve results build and build position-tracker
|
// retrieve results build and build position-tracker
|
||||||
let tracker = PosTracker::new(cx, results.clone());
|
let tracker = PosTracker::new(cx, results.clone());
|
||||||
let (pos, subpos) = tracker.position();
|
let (pos, subpos) = tracker.position();
|
||||||
log::debug!("pos: {pos}, {subpos}");
|
log::debug!("search: {search:?}, pos: {pos}, {subpos}");
|
||||||
|
|
||||||
// keyboard events
|
// keyboard events
|
||||||
let keybinds = &cx.props.config.keybinds;
|
let keybinds = &cx.props.config.keybinds;
|
||||||
|
|
|
@ -15,6 +15,11 @@ use clap::Parser;
|
||||||
use rmenu_plugin::Entry;
|
use rmenu_plugin::Entry;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
static CONFIG_DIR: &'static str = "~/.config/rmenu/";
|
||||||
|
static DEFAULT_CSS: &'static str = "~/.config/rmenu/style.css";
|
||||||
|
static DEFAULT_CONFIG: &'static str = "~/.config/rmenu/config.yaml";
|
||||||
|
static DEFAULT_CSS_CONTENT: &'static str = include_str!("../public/default.css");
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Format {
|
pub enum Format {
|
||||||
Json,
|
Json,
|
||||||
|
@ -80,7 +85,7 @@ pub struct Args {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
config: Option<String>,
|
config: Option<String>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
css: Vec<String>,
|
css: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
|
@ -88,16 +93,7 @@ impl Args {
|
||||||
fn config(&self) -> Result<config::Config, RMenuError> {
|
fn config(&self) -> Result<config::Config, RMenuError> {
|
||||||
let path = match &self.config {
|
let path = match &self.config {
|
||||||
Some(path) => path.to_owned(),
|
Some(path) => path.to_owned(),
|
||||||
None => match dirs::config_dir() {
|
None => shellexpand::tilde(DEFAULT_CONFIG).to_string(),
|
||||||
Some(mut dir) => {
|
|
||||||
dir.push("rmenu");
|
|
||||||
dir.push("config.yaml");
|
|
||||||
dir.to_string_lossy().to_string()
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return Err(RMenuError::HomeNotFound);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
log::debug!("loading config from {path:?}");
|
log::debug!("loading config from {path:?}");
|
||||||
let cfg = match read_to_string(path) {
|
let cfg = match read_to_string(path) {
|
||||||
|
@ -140,7 +136,7 @@ impl Args {
|
||||||
|
|
||||||
/// Load Entries From Specified Sources
|
/// Load Entries From Specified Sources
|
||||||
fn load_sources(&self, cfg: &config::Config) -> Result<Vec<Entry>, RMenuError> {
|
fn load_sources(&self, cfg: &config::Config) -> Result<Vec<Entry>, RMenuError> {
|
||||||
println!("{cfg:?}");
|
log::debug!("config: {cfg:?}");
|
||||||
// execute commands to get a list of entries
|
// execute commands to get a list of entries
|
||||||
let mut entries = vec![];
|
let mut entries = vec![];
|
||||||
for plugin in self.run.iter() {
|
for plugin in self.run.iter() {
|
||||||
|
@ -188,15 +184,17 @@ impl Args {
|
||||||
/// Load Application
|
/// Load Application
|
||||||
pub fn parse_app() -> Result<App, RMenuError> {
|
pub fn parse_app() -> Result<App, RMenuError> {
|
||||||
let args = Self::parse();
|
let args = Self::parse();
|
||||||
let mut config = args.config()?;
|
let config = args.config()?;
|
||||||
// load css files from settings
|
// load css files from settings
|
||||||
config.css.extend(args.css.clone());
|
let csspath = args.css.clone().unwrap_or_else(|| DEFAULT_CSS.to_owned());
|
||||||
let mut css = vec![];
|
let csspath = shellexpand::tilde(&csspath).to_string();
|
||||||
for path in config.css.iter() {
|
let css = match read_to_string(csspath) {
|
||||||
let path = shellexpand::tilde(path).to_string();
|
Ok(css) => css,
|
||||||
let src = read_to_string(path)?;
|
Err(err) => {
|
||||||
css.push(src);
|
log::error!("failed to load css: {err:?}");
|
||||||
}
|
DEFAULT_CSS_CONTENT.to_owned()
|
||||||
|
}
|
||||||
|
};
|
||||||
// load entries from configured sources
|
// load entries from configured sources
|
||||||
let entries = match args.run.len() > 0 {
|
let entries = match args.run.len() > 0 {
|
||||||
true => args.load_sources(&config)?,
|
true => args.load_sources(&config)?,
|
||||||
|
@ -204,7 +202,7 @@ impl Args {
|
||||||
};
|
};
|
||||||
// generate app object
|
// generate app object
|
||||||
return Ok(App {
|
return Ok(App {
|
||||||
css: css.join("\n"),
|
css,
|
||||||
name: "rmenu".to_owned(),
|
name: "rmenu".to_owned(),
|
||||||
entries,
|
entries,
|
||||||
config,
|
config,
|
||||||
|
@ -215,13 +213,16 @@ impl Args {
|
||||||
//TODO: improve search w/ modes?
|
//TODO: improve search w/ modes?
|
||||||
//TODO: improve looks and css
|
//TODO: improve looks and css
|
||||||
|
|
||||||
//TODO: config
|
|
||||||
// - default and cli accessable modules (instead of piped in)
|
|
||||||
// - should resolve arguments/paths with home expansion
|
|
||||||
|
|
||||||
//TODO: add exit key (Esc by default?) - part of keybindings
|
|
||||||
|
|
||||||
fn main() -> Result<(), RMenuError> {
|
fn main() -> Result<(), RMenuError> {
|
||||||
|
// enable log if env-var is present
|
||||||
|
if std::env::var("RUST_LOG").is_ok() {
|
||||||
|
env_logger::init();
|
||||||
|
}
|
||||||
|
// change directory to configuration dir
|
||||||
|
let cfgdir = shellexpand::tilde(CONFIG_DIR).to_string();
|
||||||
|
if let Err(err) = std::env::set_current_dir(&cfgdir) {
|
||||||
|
log::error!("failed to change directory: {err:?}");
|
||||||
|
}
|
||||||
// parse cli / config / application-settings
|
// parse cli / config / application-settings
|
||||||
let app = Args::parse_app()?;
|
let app = Args::parse_app()?;
|
||||||
gui::run(app);
|
gui::run(app);
|
||||||
|
|
|
@ -28,9 +28,9 @@ impl<'a> PosTracker<'a> {
|
||||||
}
|
}
|
||||||
/// Move X Primary Results Downwards
|
/// Move X Primary Results Downwards
|
||||||
pub fn move_down(&self, x: usize) {
|
pub fn move_down(&self, x: usize) {
|
||||||
|
let max = std::cmp::max(self.results.len(), 1);
|
||||||
self.subpos.set(0);
|
self.subpos.set(0);
|
||||||
self.pos
|
self.pos.modify(|v| std::cmp::min(v + x, max - 1))
|
||||||
.modify(|v| std::cmp::min(v + x, self.results.len() - 1))
|
|
||||||
}
|
}
|
||||||
/// Get Current Position/SubPosition
|
/// Get Current Position/SubPosition
|
||||||
pub fn position(&self) -> (usize, usize) {
|
pub fn position(&self) -> (usize, usize) {
|
||||||
|
@ -64,11 +64,12 @@ impl<'a> PosTracker<'a> {
|
||||||
/// Move Down Once With Context of SubMenu
|
/// Move Down Once With Context of SubMenu
|
||||||
pub fn shift_down(&self) {
|
pub fn shift_down(&self) {
|
||||||
let index = *self.pos.get();
|
let index = *self.pos.get();
|
||||||
let result = &self.results[index];
|
if let Some(result) = &self.results.get(index) {
|
||||||
let subpos = *self.subpos.get();
|
let subpos = *self.subpos.get();
|
||||||
if subpos > 0 && subpos < result.actions.len() - 1 {
|
if subpos > 0 && subpos < result.actions.len() - 1 {
|
||||||
self.subpos.modify(|v| v + 1);
|
self.subpos.modify(|v| v + 1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.move_down(1)
|
self.move_down(1)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue