feat: support more dynamic configuration via env & xdg

This commit is contained in:
imgurbot12 2024-03-28 18:21:41 -07:00
parent aa1b2f6db9
commit 188f542ffb
5 changed files with 91 additions and 66 deletions

View File

@ -1,5 +1,4 @@
RMenu ## RMenu
------
<!-- <!--
TODO: improve documentation: TODO: improve documentation:
@ -11,14 +10,22 @@ Another customizable Application-Launcher written in Rust
### Features ### Features
* Blazingly Fast 🔥 - Blazingly Fast 🔥
* Simple and Easy to Use - Simple and Easy to Use
* Customizable (Configuration and CSS-Styling) - Customizable (Configuration and CSS-Styling)
* Plugin Support - Plugin Support
* Dmenu-Like Stdin Menu Generation - Dmenu-Like Stdin Menu Generation
### Installation ### Installation
Install Dependencies (Ubuntu)
```bash
sudo apt install pkg-config libglib2.0-dev libghc-gi-gdk-dev libsoup-3.0-dev libjavascriptcoregtk-4.1-dev libwebkit2gtk-4.1-dev libnm-dev
```
Compile and Install Binaries/Config-Files
```bash ```bash
$ make install $ make install
``` ```
@ -33,13 +40,13 @@ $ rmenu --help
RMenu Comes with a few default plugins. RMenu Comes with a few default plugins.
| Name | Description | | Name | Description |
| :-------: | ------------------------------------------------------- | | :-------: | ------------------------------------------------------- |
| run | Execute a program in $PATH | | run | Execute a program in $PATH |
| drun | Run a Configured Free-Desktop Application | | drun | Run a Configured Free-Desktop Application |
| audio | Select and Set-Default PulseAudio Sink using `pactl` | | audio | Select and Set-Default PulseAudio Sink using `pactl` |
| network | Wi-Fi Login/Connection Tool using Network-Manager | | network | Wi-Fi Login/Connection Tool using Network-Manager |
| window | Simple Window Switcher (Currently Only Support Sway) | | window | Simple Window Switcher (Currently Only Support Sway) |
| powermenu | Simple Power/Logout Tool (Currently Only Supports Sway) | | powermenu | Simple Power/Logout Tool (Currently Only Supports Sway) |
Run a plugin by passing the `-r` flag like one of the following: Run a plugin by passing the `-r` flag like one of the following:
@ -56,16 +63,16 @@ Or even run plugins in combination if you'd like:
$ rmenu -r run -r drun $ rmenu -r run -r drun
``` ```
Custom Menus can also be passed much like Dmenu by passing items via Custom Menus can also be passed much like Dmenu by passing items via an input.
an input. The schema follows a standard as defined in [rmenu-plugin](./rmenu-plugin) The schema follows a standard as defined in [rmenu-plugin](./rmenu-plugin)
```bash ```bash
$ generate-my-menu.sh > input.json $ generate-my-menu.sh > input.json
$ rmenu -i input.json $ rmenu -i input.json
``` ```
When neither a plugin nor an input are specified, rmenu defaults to When neither a plugin nor an input are specified, rmenu defaults to reading from
reading from stdin. stdin.
```bash ```bash
$ generate-my-menu.sh | rmenu $ generate-my-menu.sh | rmenu
@ -73,20 +80,23 @@ $ generate-my-menu.sh | rmenu
### Configuration ### Configuration
Customize RMenu Behavior and Appearal in a [single config](./rmenu/public/config.yaml) Customize RMenu Behavior and Appearal in a
[single config](./rmenu/public/config.yaml)
Customize the entire app's appearance with CSS. A few [Example Themes](./themes/) Customize the entire app's appearance with CSS. A few
are available as reference. To try them out use: `rmenu --css <my-css-theme>` [Example Themes](./themes/) are available as reference. To try them out use:
or move the css file to `$HOME/.config/rmenu/style.css` `rmenu --css <my-css-theme>` or move the css file to
`$HOME/.config/rmenu/style.css`
### Scripting ### Scripting
RMenu plugins and imports communicate using JSON messages defined in `rmenu-plugin`. RMenu plugins and imports communicate using JSON messages defined in
Writing JSON in shell is painful, so rmenu provides another cli-tool to help build `rmenu-plugin`. Writing JSON in shell is painful, so rmenu provides another
messages quickly and easily while still retaining the flexibility of JSON. cli-tool to help build messages quickly and easily while still retaining the
flexibility of JSON.
After Installing. Use the following command, and look at [other-plugins](./other-plugins) After Installing. Use the following command, and look at
for example uses. [other-plugins](./other-plugins) for example uses.
``` ```
$ rmenu-build --help $ rmenu-build --help
@ -95,16 +105,21 @@ $ rmenu-build --help
### Example Screenshots ### Example Screenshots
#### Launchpad #### Launchpad
![launchpad](./screenshots/launchpad.png) ![launchpad](./screenshots/launchpad.png)
#### Nord #### Nord
![nord](./screenshots/nord.png) ![nord](./screenshots/nord.png)
#### Dark #### Dark
![dark](./screenshots/dark.png) ![dark](./screenshots/dark.png)
#### Solarized #### Solarized
![solzarized](./screenshots/solarized.png) ![solzarized](./screenshots/solarized.png)
#### PowerMenu #### PowerMenu
![powermenu](./screenshots/powermenu.png) ![powermenu](./screenshots/powermenu.png)

View File

@ -7,7 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
cached = "0.44.0" cached = "0.44.0"
clap = { version = "4.3.15", features = ["derive"] } clap = { version = "4.3.15", features = ["derive", "env"] }
dioxus = "0.4.3" dioxus = "0.4.3"
dioxus-desktop = "0.4.3" dioxus-desktop = "0.4.3"
dioxus-html = "0.4.3" dioxus-html = "0.4.3"

View File

@ -1,5 +1,6 @@
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Read}; use std::io::{BufRead, BufReader, Read};
use std::path::PathBuf;
use std::process::{Command, ExitStatus, Stdio}; use std::process::{Command, ExitStatus, Stdio};
use std::str::FromStr; use std::str::FromStr;
use std::{fmt::Display, fs::read_to_string}; use std::{fmt::Display, fs::read_to_string};
@ -9,7 +10,7 @@ use rmenu_plugin::{Entry, Message};
use thiserror::Error; use thiserror::Error;
use crate::config::{cfg_replace, Config, Keybind}; use crate::config::{cfg_replace, Config, Keybind};
use crate::{DEFAULT_CONFIG, DEFAULT_CSS}; use crate::{CONFIG_DIR, DEFAULT_CONFIG, DEFAULT_THEME};
/// Allowed Formats for Entry Ingestion /// Allowed Formats for Entry Ingestion
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -52,18 +53,18 @@ pub struct Args {
#[arg(short, long)] #[arg(short, long)]
run: Vec<String>, run: Vec<String>,
/// Override default configuration path /// Override default configuration path
#[arg(short, long)] #[arg(short, long, env = "RMENU_CONFIG")]
config: Option<String>, config: Option<PathBuf>,
/// Override base css theme styling /// Override base css theme styling
#[arg(long, default_value_t=String::from(DEFAULT_CSS))] #[arg(long, env = "RMENU_THEME")]
theme: String, theme: Option<PathBuf>,
/// Include additional css settings /// Include additional css settings
#[arg(long)] #[arg(long, env = "RMENU_CSS")]
css: Option<String>, css: Option<PathBuf>,
// root config settings // root config settings
/// Override terminal command /// Override terminal command
#[arg(long)] #[arg(long, env = "RMENU_TERMINAL")]
terminal: Option<String>, terminal: Option<String>,
/// Number of results to include for each page /// Number of results to include for each page
#[arg(long)] #[arg(long)]
@ -178,15 +179,18 @@ pub enum RMenuError {
pub type Result<T> = std::result::Result<T, RMenuError>; pub type Result<T> = std::result::Result<T, RMenuError>;
impl Args { impl Args {
/// Find Configuration Path
pub fn find_config(&self) -> PathBuf {
self.config.clone().unwrap_or_else(|| {
let cfgdir = std::env::var("XDG_CONFIG_DIR").unwrap_or_else(|_| CONFIG_DIR.to_string());
PathBuf::from(cfgdir).join(DEFAULT_CONFIG)
})
}
/// Load Configuration File /// Load Configuration File
pub fn get_config(&self) -> Result<Config> { pub fn get_config(&self, path: &PathBuf) -> Result<Config> {
// read configuration let path = path.to_string_lossy().to_string();
let path = self let path = shellexpand::tilde(&path).to_string();
.config
.as_ref()
.map(|v| v.as_str())
.unwrap_or(DEFAULT_CONFIG);
let path = shellexpand::tilde(path).to_string();
let config: Config = match read_to_string(path) { let config: Config = match read_to_string(path) {
Ok(content) => serde_yaml::from_str(&content), Ok(content) => serde_yaml::from_str(&content),
Err(err) => { Err(err) => {
@ -236,23 +240,30 @@ impl Args {
} }
/// Load CSS Theme or Default /// Load CSS Theme or Default
pub fn get_theme(&self) -> String { pub fn get_theme(&self, cfgdir: &PathBuf) -> String {
let path = shellexpand::tilde(&self.theme).to_string(); let theme = self.theme.clone().or(Some(cfgdir.join(DEFAULT_THEME)));
match read_to_string(&path) { if let Some(theme) = theme {
Ok(css) => css, let path = theme.to_string_lossy().to_string();
Err(err) => { let path = shellexpand::tilde(&path).to_string();
log::error!("Failed to load CSS: {err:?}"); match read_to_string(&path) {
String::new() Ok(css) => return css,
Err(err) => log::error!("Failed to load CSS: {err:?}"),
} }
} }
String::new()
} }
/// Load Additional CSS or Default /// Load Additional CSS or Default
pub fn get_css(&self, c: &Config) -> String { pub fn get_css(&self, c: &Config) -> String {
if let Some(css) = self.css.as_ref().or(c.css.as_ref()) { let css = self
let path = shellexpand::tilde(&css).to_string(); .css
.clone()
.or(c.css.as_ref().map(|s| PathBuf::from(s)));
if let Some(css) = css {
let path = css.to_string_lossy().to_string();
let path = shellexpand::tilde(&path).to_string();
match read_to_string(&path) { match read_to_string(&path) {
Ok(theme) => return theme, Ok(css) => return css,
Err(err) => log::error!("Failed to load Theme: {err:?}"), Err(err) => log::error!("Failed to load Theme: {err:?}"),
} }
} }

View File

@ -1,7 +1,4 @@
//! RMENU Configuration Implementations //! RMENU Configuration Implementations
use heck::AsPascalCase;
use rmenu_plugin::Options;
use serde::{de::Error, Deserialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::str::FromStr; use std::str::FromStr;
@ -10,6 +7,9 @@ use dioxus_desktop::tao::{
window::Fullscreen, window::Fullscreen,
}; };
use dioxus_html::input_data::keyboard_types::{Code, Modifiers}; use dioxus_html::input_data::keyboard_types::{Code, Modifiers};
use heck::AsPascalCase;
use rmenu_plugin::Options;
use serde::{de::Error, Deserialize};
// parse supported modifiers from string // parse supported modifiers from string
fn mod_from_str(s: &str) -> Option<Modifiers> { fn mod_from_str(s: &str) -> Option<Modifiers> {

View File

@ -11,8 +11,8 @@ use clap::Parser;
use rmenu_plugin::{self_exe, Entry}; use rmenu_plugin::{self_exe, Entry};
static CONFIG_DIR: &'static str = "~/.config/rmenu/"; static CONFIG_DIR: &'static str = "~/.config/rmenu/";
static DEFAULT_CSS: &'static str = "~/.config/rmenu/style.css"; static DEFAULT_THEME: &'static str = "style.css";
static DEFAULT_CONFIG: &'static str = "~/.config/rmenu/config.yaml"; static DEFAULT_CONFIG: &'static str = "config.yaml";
static DEFAULT_CSS_CONTENT: &'static str = include_str!("../public/default.css"); static DEFAULT_CSS_CONTENT: &'static str = include_str!("../public/default.css");
/// Application State for GUI /// Application State for GUI
@ -43,10 +43,9 @@ fn main() -> cli::Result<()> {
// parse cli and retrieve values for app // parse cli and retrieve values for app
let mut cli = cli::Args::parse(); let mut cli = cli::Args::parse();
let mut config = cli.get_config()?; let mut cfgpath = cli.find_config();
let mut config = cli.get_config(&cfgpath)?;
let entries = cli.get_entries(&mut config)?; let entries = cli.get_entries(&mut config)?;
let css = cli.get_css(&config);
let theme = cli.get_theme();
// update config based on cli-settings and entries // update config based on cli-settings and entries
config = cli.update_config(config); config = cli.update_config(config);
@ -56,11 +55,11 @@ fn main() -> cli::Result<()> {
.any(|e| e.icon.is_some() || e.icon_alt.is_some()); .any(|e| e.icon.is_some() || e.icon_alt.is_some());
config.use_comments = config.use_comments && entries.iter().any(|e| e.comment.is_some()); config.use_comments = config.use_comments && entries.iter().any(|e| e.comment.is_some());
// change directory to config folder // retrieve cfgdir and get theme/css
let cfgdir = shellexpand::tilde(CONFIG_DIR).to_string(); cfgpath.pop();
if let Err(err) = std::env::set_current_dir(&cfgdir) { let cfgdir = cfgpath;
log::error!("failed to change directory: {err:?}"); let theme = cli.get_theme(&cfgdir);
} let css = cli.get_css(&config);
// genrate app context and run gui // genrate app context and run gui
gui::run(App { gui::run(App {