diff --git a/examples/images/hyprspace.png b/examples/images/hyprspace.png new file mode 100644 index 0000000..f89112b Binary files /dev/null and b/examples/images/hyprspace.png differ diff --git a/examples/worf-hyprspace/Readme.md b/examples/worf-hyprspace/Readme.md index aa7864e..ac877ef 100644 --- a/examples/worf-hyprspace/Readme.md +++ b/examples/worf-hyprspace/Readme.md @@ -1 +1,15 @@ # Worf Hyprspace + +This allows to manage workspaces in hyprland using the Worf API. +Inspired by https://github.com/sslater11/hyprland-dynamic-workspaces-manager + + + +## Features +-Auto: Automatic detection of mode +-Rename: Change the name of the chosen workspace +-SwitchToWorkspace: Change the active workspace +-MoveCurrentWindowToOtherWorkspace: Move the focused window to a new workspace and follow it +-MoveCurrentWindowToOtherWorkspaceSilent: Move the focused window to a new workspace and don't follow it +-MoveAllWindowsToOtherWorkSpace: Move all windows to a new workspace +-DeleteWorkspace: Close all windows and go to another workspace diff --git a/examples/worf-hyprswitch/src/main.rs b/examples/worf-hyprswitch/src/main.rs index 100950f..0efbee3 100644 --- a/examples/worf-hyprswitch/src/main.rs +++ b/examples/worf-hyprswitch/src/main.rs @@ -157,7 +157,7 @@ fn main() -> Result<(), String> { &cache, )?)); let windows = provider.lock().unwrap().windows.clone(); - let result = gui::show(config, provider, None, None, ExpandMode::Verbatim, None) + let result = gui::show(&config, provider, None, None, ExpandMode::Verbatim, None) .map_err(|e| e.to_string())?; let update_cache = thread::spawn(move || { windows.iter().for_each(|item| { diff --git a/examples/worf-warden/src/main.rs b/examples/worf-warden/src/main.rs index 125c3aa..d2e8fd0 100644 --- a/examples/worf-warden/src/main.rs +++ b/examples/worf-warden/src/main.rs @@ -1,9 +1,19 @@ -use std::{collections::HashMap, env, process::Command, thread::sleep, time::Duration, sync::{Arc, Mutex, RwLock}}; +use std::{ + collections::HashMap, + env, + process::Command, + sync::{Arc, Mutex, RwLock}, + thread::sleep, + time::Duration, +}; use worf::{ config::{self, Config, CustomKeyHintLocation, Key}, desktop::{copy_to_clipboard, spawn_fork}, - gui::{self, CustomKeyHint, CustomKeys, ItemProvider, KeyBinding, MenuItem, Modifier, ProviderData, ExpandMode}, + gui::{ + self, CustomKeyHint, CustomKeys, ExpandMode, ItemProvider, KeyBinding, MenuItem, Modifier, + ProviderData, + }, }; #[derive(Clone)] @@ -138,7 +148,7 @@ fn rbw(cmd: &str, args: Option>) -> Result { let output = command .output() - .map_err(|e| format!("Failed to execute command: {}", e))?; + .map_err(|e| format!("Failed to execute command: {e}"))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); @@ -146,7 +156,7 @@ fn rbw(cmd: &str, args: Option>) -> Result { } let stdout = - String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output: {}", e))?; + String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output: {e}"))?; Ok(stdout.trim().to_string()) } @@ -273,7 +283,7 @@ fn key_lock() -> KeyBinding { fn show(config: Arc>, provider: Arc>) -> Result<(), String> { match gui::show( - Arc::clone(&config), + &config, provider, None, None, @@ -301,7 +311,10 @@ fn show(config: Arc>, provider: Arc>) -> Ok(selection) => { if let Some(meta) = selection.menu.data { if meta.ids.len() > 1 { - return show(config, Arc::new(Mutex::new(PasswordProvider::sub_provider(meta.ids)?))); + return show( + config, + Arc::new(Mutex::new(PasswordProvider::sub_provider(meta.ids)?)), + ); } let id = meta.ids.first().unwrap_or(&selection.menu.label); @@ -351,7 +364,9 @@ fn main() -> Result<(), String> { .init(); let args = config::parse_args(); - let config = Arc::new(RwLock::new(config::load_config(Some(&args)).unwrap_or(args))); + let config = Arc::new(RwLock::new( + config::load_config(Some(&args)).unwrap_or(args), + )); if !groups().contains("input") { log::error!( diff --git a/worf/src/lib/gui.rs b/worf/src/lib/gui.rs index a674713..51b9be0 100644 --- a/worf/src/lib/gui.rs +++ b/worf/src/lib/gui.rs @@ -75,13 +75,20 @@ pub struct DefaultItemFactory { } impl DefaultItemFactory { + #[must_use] pub fn new() -> DefaultItemFactory { DefaultItemFactory:: { - _marker: Default::default(), + _marker: PhantomData, } } } +impl Default for DefaultItemFactory { + fn default() -> Self { + Self::new() + } +} + impl ItemFactory for DefaultItemFactory { fn new_menu_item(&self, label: String) -> Option> { Some(MenuItem::new(label, None, None, vec![], None, 0.0, None)) @@ -516,8 +523,10 @@ struct UiElements { /// # Errors /// /// Will return Err when the channel between the UI and this is broken +/// # Panics +/// When failing to unwrap the arc lock pub fn show( - config: Arc>, + config: &Arc>, item_provider: ArcProvider, item_factory: Option>, search_ignored_words: Option>, @@ -550,12 +559,12 @@ where item_provider, item_factory, selected_sender: sender, - config: config.clone(), + config: Arc::clone(config), search_ignored_words, expand_mode, }); - let connect_cfg = Arc::clone(&config); + let connect_cfg = Arc::clone(config); app.connect_activate(move |app| { build_ui::(&connect_cfg, &meta, app.clone(), custom_keys.as_ref()); }); @@ -673,7 +682,7 @@ fn build_ui( let provider_elements = get_provider_elements.join().unwrap(); log::debug!("got items after {:?}", wait_for_items.elapsed()); - let cfg = config.clone(); + let cfg = Arc::clone(config); let ui = Rc::clone(&ui_elements); ui_elements.window.connect_is_active_notify(move |_| { window_show_resize(&cfg.read().unwrap(), &ui); diff --git a/worf/src/lib/modes/auto.rs b/worf/src/lib/modes/auto.rs index ffe9a41..24ee87d 100644 --- a/worf/src/lib/modes/auto.rs +++ b/worf/src/lib/modes/auto.rs @@ -1,6 +1,7 @@ use regex::Regex; use std::sync::{Arc, Mutex, RwLock}; +use crate::gui::ArcProvider; use crate::{ Error, config::Config, @@ -15,7 +16,6 @@ use crate::{ ssh::SshProvider, }, }; -use crate::gui::ArcProvider; #[derive(Debug, Clone, PartialEq)] enum AutoRunType { @@ -153,7 +153,7 @@ pub fn show(config: &Arc>) -> Result<(), Error> { loop { let selection_result = gui::show( - Arc::clone(&config), + config, Arc::clone(&arc_provider), Some(Arc::new(Mutex::new(DefaultItemFactory::new()))), Some( diff --git a/worf/src/lib/modes/dmenu.rs b/worf/src/lib/modes/dmenu.rs index 0aeff05..0e50914 100644 --- a/worf/src/lib/modes/dmenu.rs +++ b/worf/src/lib/modes/dmenu.rs @@ -52,7 +52,9 @@ impl ItemProvider for DMenuProvider { /// # Errors /// /// Forwards errors from the gui. See `gui::show` for details. -pub fn show(config: Arc>) -> Result<(), Error> { +/// # Panics +/// When failing to unwrap the arc lock +pub fn show(config: &Arc>) -> Result<(), Error> { let provider = Arc::new(Mutex::new(DMenuProvider::new( &config.read().unwrap().sort_order(), ))); diff --git a/worf/src/lib/modes/drun.rs b/worf/src/lib/modes/drun.rs index c4c0b99..66f152a 100644 --- a/worf/src/lib/modes/drun.rs +++ b/worf/src/lib/modes/drun.rs @@ -219,17 +219,12 @@ pub(crate) fn update_drun_cache_and_run( /// # Errors /// /// Will return `Err` if it was not able to spawn the process -pub fn show(config: Arc>) -> Result<(), Error> { +/// # Panics +/// When failing to unwrap the arc lock +pub fn show(config: &Arc>) -> Result<(), Error> { let provider = Arc::new(Mutex::new(DRunProvider::new((), &config.read().unwrap()))); let arc_provider = Arc::clone(&provider) as ArcProvider<()>; - let selection_result = gui::show( - config.clone(), - arc_provider, - None, - None, - ExpandMode::Verbatim, - None, - ); + let selection_result = gui::show(config, arc_provider, None, None, ExpandMode::Verbatim, None); match selection_result { Ok(s) => { let p = provider.lock().unwrap(); diff --git a/worf/src/lib/modes/emoji.rs b/worf/src/lib/modes/emoji.rs index 23802e8..69ab940 100644 --- a/worf/src/lib/modes/emoji.rs +++ b/worf/src/lib/modes/emoji.rs @@ -62,7 +62,9 @@ impl ItemProvider for EmojiProvider { /// # Errors /// /// Forwards errors from the gui. See `gui::show` for details. -pub fn show(config: Arc>) -> Result<(), Error> { +/// # Panics +/// When failing to unwrap the arc lock +pub fn show(config: &Arc>) -> Result<(), Error> { let cfg = config.read().unwrap(); let provider = Arc::new(Mutex::new(EmojiProvider::new( &cfg.sort_order(), @@ -70,14 +72,7 @@ pub fn show(config: Arc>) -> Result<(), Error> { ))); drop(cfg); - let selection_result = gui::show( - config.clone(), - provider, - None, - None, - ExpandMode::Verbatim, - None, - )?; + let selection_result = gui::show(config, provider, None, None, ExpandMode::Verbatim, None)?; match selection_result.menu.data { None => Err(Error::MissingAction), Some(action) => copy_to_clipboard(action, None), diff --git a/worf/src/lib/modes/file.rs b/worf/src/lib/modes/file.rs index c4b88d1..73f847d 100644 --- a/worf/src/lib/modes/file.rs +++ b/worf/src/lib/modes/file.rs @@ -200,7 +200,7 @@ impl ItemProvider for FileItemProvider { /// /// # Panics /// In case an internal regex does not parse anymore, this should never happen -pub fn show(config: Arc>) -> Result<(), Error> { +pub fn show(config: &Arc>) -> Result<(), Error> { let provider = Arc::new(Mutex::new(FileItemProvider::new( 0, config.read().unwrap().sort_order(), @@ -208,7 +208,7 @@ pub fn show(config: Arc>) -> Result<(), Error> { // todo ues a arc instead of cloning the config let selection_result = gui::show( - config.clone(), + config, provider, None, Some(vec![Regex::new("^\\$\\w+").unwrap()]), diff --git a/worf/src/lib/modes/math.rs b/worf/src/lib/modes/math.rs index 36aae2c..f630963 100644 --- a/worf/src/lib/modes/math.rs +++ b/worf/src/lib/modes/math.rs @@ -248,7 +248,9 @@ fn calc(input: &str) -> String { } /// Shows the math mode -pub fn show(config: Arc>) { +/// # Panics +/// When failing to unwrap the arc lock +pub fn show(config: &Arc>) { let mut calc: Vec> = vec![]; let provider = Arc::new(Mutex::new(MathProvider::new(()))); let factory: ArcFactory<()> = Arc::new(Mutex::new(DefaultItemFactory::new())); @@ -256,7 +258,7 @@ pub fn show(config: Arc>) { loop { provider.lock().unwrap().add_elements(&mut calc.clone()); let selection_result = gui::show( - config.clone(), + config, Arc::clone(&arc_provider), Some(Arc::clone(&factory)), None, diff --git a/worf/src/lib/modes/run.rs b/worf/src/lib/modes/run.rs index 7a19b84..70ad720 100644 --- a/worf/src/lib/modes/run.rs +++ b/worf/src/lib/modes/run.rs @@ -134,18 +134,13 @@ fn update_run_cache_and_run( /// # Errors /// /// Will return `Err` if it was not able to spawn the process -pub fn show(config: Arc>) -> Result<(), Error> { +/// # Panics +/// When failing to unwrap the arc lock +pub fn show(config: &Arc>) -> Result<(), Error> { let provider = Arc::new(Mutex::new(RunProvider::new(&config.read().unwrap())?)); let arc_provider = Arc::clone(&provider) as ArcProvider<()>; - let selection_result = gui::show( - config, - arc_provider, - None, - None, - ExpandMode::Verbatim, - None, - ); + let selection_result = gui::show(config, arc_provider, None, None, ExpandMode::Verbatim, None); match selection_result { Ok(s) => { let prov = provider.lock().unwrap(); diff --git a/worf/src/lib/modes/search.rs b/worf/src/lib/modes/search.rs index 35e755c..9241139 100644 --- a/worf/src/lib/modes/search.rs +++ b/worf/src/lib/modes/search.rs @@ -54,14 +54,16 @@ impl ItemProvider for SearchProvider { /// # Errors /// /// Forwards errors from the gui. See `gui::show` for details. -pub fn show(config: Arc>) -> Result<(), Error> { +/// # Panics +/// When failing to unwrap the arc lock +pub fn show(config: &Arc>) -> Result<(), Error> { let provider = Arc::new(Mutex::new(SearchProvider::new( (), config.read().unwrap().search_query(), ))); let factory: ArcFactory<()> = Arc::new(Mutex::new(DefaultItemFactory::new())); let selection_result = gui::show( - config.clone(), + config, provider, Some(factory), None, diff --git a/worf/src/lib/modes/ssh.rs b/worf/src/lib/modes/ssh.rs index 0b54d78..4ffe2a7 100644 --- a/worf/src/lib/modes/ssh.rs +++ b/worf/src/lib/modes/ssh.rs @@ -94,19 +94,14 @@ pub(crate) fn launch(menu_item: &MenuItem, config: &Config) -> Resu /// Will return `Err` /// * if it was not able to spawn the process /// * if it didn't find a terminal -pub fn show(config: Arc>) -> Result<(), Error> { +/// # Panics +/// When failing to unwrap the arc lock +pub fn show(config: &Arc>) -> Result<(), Error> { let provider = Arc::new(Mutex::new(SshProvider::new( 0, &config.read().unwrap().sort_order(), ))); - let selection_result = gui::show( - Arc::clone(&config), - provider, - None, - None, - ExpandMode::Verbatim, - None, - ); + let selection_result = gui::show(config, provider, None, None, ExpandMode::Verbatim, None); if let Ok(mi) = selection_result { launch(&mi.menu, &config.read().unwrap())?; } else { diff --git a/worf/src/main.rs b/worf/src/main.rs index 10cce09..2a686a5 100644 --- a/worf/src/main.rs +++ b/worf/src/main.rs @@ -1,13 +1,12 @@ +use clap::Parser; +use std::fmt::Display; +use std::str::FromStr; use std::{ env, sync::{Arc, RwLock}, }; -use std::fmt::Display; -use std::str::FromStr; -use clap::Parser; use worf::{Error, config, desktop::fork_if_configured, modes}; - #[derive(Clone, Debug)] pub enum Mode { /// searches `$PATH` for executables and allows them to be run by selecting them. @@ -54,7 +53,7 @@ struct MainConfig { impl Display for Mode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Mode::Run => write!(f, "run"), + Mode::Run => write!(f, "run"), Mode::Drun => write!(f, "drun"), Mode::Dmenu => write!(f, "dmenu"), Mode::Math => write!(f, "math"), @@ -93,7 +92,7 @@ fn main() { .parse_filters(&env::var("RUST_LOG").unwrap_or_else(|_| "error".to_owned())) .format_timestamp_micros() .init(); - + let mut config = MainConfig::parse(); config.worf = config::load_config(Some(&config.worf)).unwrap_or(config.worf); if config.worf.prompt().is_none() { @@ -109,18 +108,18 @@ fn main() { let cfg_arc = Arc::new(RwLock::new(config.worf)); let result = match config.show { - Mode::Run => modes::run::show(cfg_arc), - Mode::Drun => modes::drun::show(cfg_arc), - Mode::Dmenu => modes::dmenu::show(cfg_arc), - Mode::File => modes::file::show(cfg_arc), + Mode::Run => modes::run::show(&cfg_arc), + Mode::Drun => modes::drun::show(&cfg_arc), + Mode::Dmenu => modes::dmenu::show(&cfg_arc), + Mode::File => modes::file::show(&cfg_arc), Mode::Math => { - modes::math::show(cfg_arc); + modes::math::show(&cfg_arc); Ok(()) } - Mode::Ssh => modes::ssh::show(cfg_arc), - Mode::Emoji => modes::emoji::show(cfg_arc), + Mode::Ssh => modes::ssh::show(&cfg_arc), + Mode::Emoji => modes::emoji::show(&cfg_arc), Mode::Auto => modes::auto::show(&cfg_arc), - Mode::WebSearch => modes::search::show(cfg_arc), + Mode::WebSearch => modes::search::show(&cfg_arc), }; if let Err(err) = result {