From 70f561e6acf6a7e8953491edaf7e15cddfdde08a Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sat, 3 May 2025 17:37:02 -0700 Subject: [PATCH] feat(shortcuts): add helper written in egui This commit adds a simple egui helper application which shows a list of shortcuts defined in a user's whkdrc file. Parsing AHK files is not supported. In addition to listing out shortcuts defined in the whkdrc file, the top line allows users to add filter a filter to narrow down the list of commands and key bindings to the ones they are interested in. A new komorebic command "toggle-shortcuts" has been introduced which will first attempt to kill "komorebi-shortcuts.exe", and then exit if the kill signal was successful (ie. a process was closed), or proceed to open "komorebi-shortcuts.exe" if the kill signal was not successful (ie. no process was closed, so we should open one). "komorebi-shortcuts.exe" has been added as a floating application in lib.rs to allow for users to use the "komorebic move" command to manipulate its position via their existing keyboard bindings. --- Cargo.lock | 73 +++++++++++++++++++++++ Cargo.toml | 5 +- deny.toml | 32 +++++++--- dependencies.json | 9 +++ docs/whkdrc.sample | 2 + justfile | 4 +- komorebi-bar/Cargo.toml | 2 +- komorebi-shortcuts/Cargo.toml | 11 ++++ komorebi-shortcuts/src/main.rs | 106 +++++++++++++++++++++++++++++++++ komorebi/src/lib.rs | 9 ++- komorebic/Cargo.toml | 2 +- komorebic/src/main.rs | 11 ++++ wix/main.wxs | 5 ++ 13 files changed, 256 insertions(+), 15 deletions(-) create mode 100644 komorebi-shortcuts/Cargo.toml create mode 100644 komorebi-shortcuts/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 5b691b90..c79f4393 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,6 +180,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.0" @@ -932,6 +938,16 @@ dependencies = [ "phf_codegen 0.11.3", ] +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown 0.14.5", + "stacker", +] + [[package]] name = "clap" version = "4.5.37" @@ -2320,6 +2336,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -3027,6 +3053,16 @@ dependencies = [ "windows-core 0.61.0", ] +[[package]] +name = "komorebi-shortcuts" +version = "0.1.0" +dependencies = [ + "dirs 6.0.0", + "eframe", + "whkd-core", + "whkd-parser", +] + [[package]] name = "komorebi-themes" version = "0.1.37" @@ -4617,6 +4653,15 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "psm" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +dependencies = [ + "cc", +] + [[package]] name = "qoi" version = "0.4.1" @@ -5564,6 +5609,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "windows-sys 0.52.0", +] + [[package]] name = "starship-battery" version = "0.10.1" @@ -6752,6 +6810,21 @@ dependencies = [ "winsafe", ] +[[package]] +name = "whkd-core" +version = "0.2.9" +source = "git+https://github.com/LGUG2Z/whkd?rev=29df24ff2dd715655b0366bd2a598837c699a8e9#29df24ff2dd715655b0366bd2a598837c699a8e9" + +[[package]] +name = "whkd-parser" +version = "0.2.9" +source = "git+https://github.com/LGUG2Z/whkd?rev=29df24ff2dd715655b0366bd2a598837c699a8e9#29df24ff2dd715655b0366bd2a598837c699a8e9" +dependencies = [ + "chumsky", + "thiserror 2.0.12", + "whkd-core", +] + [[package]] name = "win32-display-data" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index fc5b5d12..3447ec46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,8 @@ members = [ "komorebic", "komorebic-no-console", "komorebi-bar", - "komorebi-themes" + "komorebi-themes", + "komorebi-shortcuts" ] [workspace.dependencies] @@ -72,4 +73,4 @@ features = [ "Win32_System_WindowsProgramming", "Media", "Media_Control" -] \ No newline at end of file +] diff --git a/deny.toml b/deny.toml index b0cccf23..3144b003 100644 --- a/deny.toml +++ b/deny.toml @@ -34,43 +34,58 @@ allow = [ "Ubuntu-font-1.0", "Unicode-3.0", "Zlib", - "LicenseRef-Komorebi-1.0" + "LicenseRef-Komorebi-2.0" ] confidence-threshold = 0.8 [[licenses.clarify]] crate = "komorebi" -expression = "LicenseRef-Komorebi-1.0" +expression = "LicenseRef-Komorebi-2.0" license-files = [] [[licenses.clarify]] crate = "komorebi-client" -expression = "LicenseRef-Komorebi-1.0" +expression = "LicenseRef-Komorebi-2.0" license-files = [] [[licenses.clarify]] crate = "komorebic" -expression = "LicenseRef-Komorebi-1.0" +expression = "LicenseRef-Komorebi-2.0" license-files = [] [[licenses.clarify]] crate = "komorebic-no-console" -expression = "LicenseRef-Komorebi-1.0" +expression = "LicenseRef-Komorebi-2.0" license-files = [] [[licenses.clarify]] crate = "komorebi-themes" -expression = "LicenseRef-Komorebi-1.0" +expression = "LicenseRef-Komorebi-2.0" license-files = [] [[licenses.clarify]] crate = "komorebi-gui" -expression = "LicenseRef-Komorebi-1.0" +expression = "LicenseRef-Komorebi-2.0" license-files = [] [[licenses.clarify]] crate = "komorebi-bar" -expression = "LicenseRef-Komorebi-1.0" +expression = "LicenseRef-Komorebi-2.0" +license-files = [] + +[[licenses.clarify]] +crate = "komorebi-shortcuts" +expression = "LicenseRef-Komorebi-2.0" +license-files = [] + +[[licenses.clarify]] +crate = "whkd-core" +expression = "LicenseRef-Komorebi-2.0" +license-files = [] + +[[licenses.clarify]] +crate = "whkd-parser" +expression = "LicenseRef-Komorebi-2.0" license-files = [] [[licenses.clarify]] @@ -96,4 +111,5 @@ allow-git = [ "https://github.com/LGUG2Z/win32-display-data", "https://github.com/LGUG2Z/flavours", "https://github.com/LGUG2Z/base16_color_scheme", + "https://github.com/LGUG2Z/whkd", ] diff --git a/dependencies.json b/dependencies.json index dee36180..10692e7d 100644 --- a/dependencies.json +++ b/dependencies.json @@ -20,6 +20,7 @@ "adler 1.0.2 registry+https://github.com/rust-lang/crates.io-index", "adler2 2.0.0 registry+https://github.com/rust-lang/crates.io-index", "ahash 0.8.11 registry+https://github.com/rust-lang/crates.io-index", + "allocator-api2 0.2.21 registry+https://github.com/rust-lang/crates.io-index", "anstream 0.6.18 registry+https://github.com/rust-lang/crates.io-index", "anstyle 1.0.10 registry+https://github.com/rust-lang/crates.io-index", "anstyle-parse 0.2.6 registry+https://github.com/rust-lang/crates.io-index", @@ -120,6 +121,7 @@ "glutin_wgl_sys 0.6.1 registry+https://github.com/rust-lang/crates.io-index", "half 2.6.0 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index", + "hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.15.2 registry+https://github.com/rust-lang/crates.io-index", "heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index", "hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index", @@ -201,6 +203,7 @@ "proc-macro2 1.0.95 registry+https://github.com/rust-lang/crates.io-index", "profiling 1.0.16 registry+https://github.com/rust-lang/crates.io-index", "profiling-procmacros 1.0.16 registry+https://github.com/rust-lang/crates.io-index", + "psm 0.1.26 registry+https://github.com/rust-lang/crates.io-index", "qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index", "quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index", "quote 1.0.40 registry+https://github.com/rust-lang/crates.io-index", @@ -247,6 +250,7 @@ "smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "socket2 0.5.9 registry+https://github.com/rust-lang/crates.io-index", "stable_deref_trait 1.2.0 registry+https://github.com/rust-lang/crates.io-index", + "stacker 0.1.21 registry+https://github.com/rust-lang/crates.io-index", "static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "supports-color 3.0.2 registry+https://github.com/rust-lang/crates.io-index", "supports-hyperlinks 3.1.0 registry+https://github.com/rust-lang/crates.io-index", @@ -411,6 +415,7 @@ "ahash 0.8.11 registry+https://github.com/rust-lang/crates.io-index", "aho-corasick 1.1.3 registry+https://github.com/rust-lang/crates.io-index", "aligned-vec 0.5.0 registry+https://github.com/rust-lang/crates.io-index", + "allocator-api2 0.2.21 registry+https://github.com/rust-lang/crates.io-index", "anstream 0.6.18 registry+https://github.com/rust-lang/crates.io-index", "anstyle 1.0.10 registry+https://github.com/rust-lang/crates.io-index", "anstyle-parse 0.2.6 registry+https://github.com/rust-lang/crates.io-index", @@ -449,6 +454,7 @@ "chrono 0.4.40 registry+https://github.com/rust-lang/crates.io-index", "chrono-tz 0.10.3 registry+https://github.com/rust-lang/crates.io-index", "chrono-tz-build 0.4.1 registry+https://github.com/rust-lang/crates.io-index", + "chumsky 0.9.3 registry+https://github.com/rust-lang/crates.io-index", "clap 4.5.37 registry+https://github.com/rust-lang/crates.io-index", "clap_builder 4.5.37 registry+https://github.com/rust-lang/crates.io-index", "clap_derive 4.5.32 registry+https://github.com/rust-lang/crates.io-index", @@ -526,6 +532,7 @@ "h2 0.4.9 registry+https://github.com/rust-lang/crates.io-index", "half 2.6.0 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index", + "hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.15.2 registry+https://github.com/rust-lang/crates.io-index", "heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index", "hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index", @@ -635,6 +642,7 @@ "proc-macro2 1.0.95 registry+https://github.com/rust-lang/crates.io-index", "profiling 1.0.16 registry+https://github.com/rust-lang/crates.io-index", "profiling-procmacros 1.0.16 registry+https://github.com/rust-lang/crates.io-index", + "psm 0.1.26 registry+https://github.com/rust-lang/crates.io-index", "qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index", "quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index", "quote 1.0.40 registry+https://github.com/rust-lang/crates.io-index", @@ -692,6 +700,7 @@ "smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "socket2 0.5.9 registry+https://github.com/rust-lang/crates.io-index", "stable_deref_trait 1.2.0 registry+https://github.com/rust-lang/crates.io-index", + "stacker 0.1.21 registry+https://github.com/rust-lang/crates.io-index", "static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "strsim 0.11.1 registry+https://github.com/rust-lang/crates.io-index", "strum 0.27.1 registry+https://github.com/rust-lang/crates.io-index", diff --git a/docs/whkdrc.sample b/docs/whkdrc.sample index ad985546..4516a5de 100644 --- a/docs/whkdrc.sample +++ b/docs/whkdrc.sample @@ -5,6 +5,8 @@ alt + o : taskkill /f /im whkd.exe; Start-Process whkd -WindowStyle hidden # if shell is pwsh / powershell alt + shift + o : komorebic reload-configuration +alt + i : komorebic toggle-shortcuts + # App shortcuts - these require shell to be pwsh / powershell # The apps will be focused if open, or launched if not open # alt + f : if ($wshell.AppActivate('Firefox') -eq $False) { start firefox } diff --git a/justfile b/justfile index baec5423..b272f8cb 100644 --- a/justfile +++ b/justfile @@ -31,7 +31,7 @@ install: just install-targets komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui install-with-jsonschema: - just install-targets-with-jsonschema komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui + just install-targets-with-jsonschema komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui komorebi-shortcuts build-targets *targets: "{{ targets }}" -split ' ' | ForEach-Object { just build-target $_ } @@ -40,7 +40,7 @@ build-target target: cargo +stable build --package {{ target }} --locked --release --no-default-features build: - just build-targets komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui + just build-targets komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui komorebi-shortcuts copy-target target: cp .\target\release\{{ target }}.exe $Env:USERPROFILE\.cargo\bin diff --git a/komorebi-bar/Cargo.toml b/komorebi-bar/Cargo.toml index d71e41e5..eaaf59e1 100644 --- a/komorebi-bar/Cargo.toml +++ b/komorebi-bar/Cargo.toml @@ -43,4 +43,4 @@ windows-icons-fallback = { package = "windows-icons", git = "https://github.com/ [features] default = ["schemars"] -schemars = ["dep:schemars"] +schemars = ["dep:schemars", "komorebi-client/schemars", "komorebi-themes/schemars"] diff --git a/komorebi-shortcuts/Cargo.toml b/komorebi-shortcuts/Cargo.toml new file mode 100644 index 00000000..a5492833 --- /dev/null +++ b/komorebi-shortcuts/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "komorebi-shortcuts" +version = "0.1.0" +edition = "2024" + +[dependencies] +whkd-parser = { git = "https://github.com/LGUG2Z/whkd", rev = "29df24ff2dd715655b0366bd2a598837c699a8e9" } +whkd-core = { git = "https://github.com/LGUG2Z/whkd", rev = "29df24ff2dd715655b0366bd2a598837c699a8e9" } + +eframe = { workspace = true } +dirs = { workspace = true } \ No newline at end of file diff --git a/komorebi-shortcuts/src/main.rs b/komorebi-shortcuts/src/main.rs new file mode 100644 index 00000000..c7fe0cea --- /dev/null +++ b/komorebi-shortcuts/src/main.rs @@ -0,0 +1,106 @@ +use eframe::egui::ViewportBuilder; +use std::path::PathBuf; +use whkd_core::HotkeyBinding; +use whkd_core::Whkdrc; + +#[derive(Default)] +struct Quicklook { + whkdrc: Option, + filter: String, +} + +impl Quicklook { + fn new(_cc: &eframe::CreationContext<'_>) -> Self { + // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals. + // Restore app state using cc.storage (requires the "persistence" feature). + // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use + // for e.g. egui::PaintCallback. + let mut home = std::env::var("WHKD_CONFIG_HOME").map_or_else( + |_| { + dirs::home_dir() + .expect("no home directory found") + .join(".config") + }, + |home_path| { + let home = PathBuf::from(&home_path); + + if home.as_path().is_dir() { + home + } else { + panic!( + "$Env:WHKD_CONFIG_HOME is set to '{home_path}', which is not a valid directory", + ); + } + }, + ); + home.push("whkdrc"); + + Self { + whkdrc: whkd_parser::load(&home).ok(), + filter: String::new(), + } + } +} + +impl eframe::App for Quicklook { + fn update(&mut self, ctx: &eframe::egui::Context, _frame: &mut eframe::Frame) { + eframe::egui::CentralPanel::default().show(ctx, |ui| { + ui.set_max_width(ui.available_width()); + ui.set_max_height(ui.available_height()); + eframe::egui::ScrollArea::vertical().show(ui, |ui| { + eframe::egui::Grid::new("grid") + .num_columns(2) + .striped(true) + .spacing([40.0, 4.0]) + .min_col_width(ui.available_width() / 2.0 - 20.0) + .show(ui, |ui| { + if let Some(whkdrc) = &self.whkdrc { + ui.label("Filter"); + ui.add( + eframe::egui::text_edit::TextEdit::singleline(&mut self.filter) + .background_color(ctx.style().visuals.faint_bg_color), + ); + ui.end_row(); + ui.end_row(); + + for binding in &whkdrc.bindings { + if is_komorebic_binding(binding) { + let keys = binding.keys.join(" + "); + if self.filter.is_empty() + || binding.command.contains(&self.filter) + { + ui.label(keys); + ui.label(&binding.command); + ui.end_row(); + } + } + } + } + }); + }); + }); + } +} + +fn main() { + let viewport_builder = ViewportBuilder::default() + .with_resizable(true) + .with_decorations(false); + + let native_options = eframe::NativeOptions { + viewport: viewport_builder, + centered: true, + ..Default::default() + }; + + eframe::run_native( + "komorebi-shortcuts", + native_options, + Box::new(|cc| Ok(Box::new(Quicklook::new(cc)))), + ) + .unwrap(); +} + +fn is_komorebic_binding(binding: &HotkeyBinding) -> bool { + binding.command.starts_with("komorebic") +} diff --git a/komorebi/src/lib.rs b/komorebi/src/lib.rs index 0bac3780..323d1eef 100644 --- a/komorebi/src/lib.rs +++ b/komorebi/src/lib.rs @@ -160,7 +160,14 @@ lazy_static! { }) ])); static ref SESSION_FLOATING_APPLICATIONS: Arc>> = Arc::new(Mutex::new(Vec::new())); - static ref FLOATING_APPLICATIONS: Arc>> = Arc::new(Mutex::new(Vec::new())); + static ref FLOATING_APPLICATIONS: Arc>> = Arc::new(Mutex::new(vec![ + MatchingRule::Simple(IdWithIdentifier { + kind: ApplicationIdentifier::Exe, + id: String::from("komorebi-shortcuts.exe"), + matching_strategy: Option::from(MatchingStrategy::Equals), + }) + + ])); static ref PERMAIGNORE_CLASSES: Arc>> = Arc::new(Mutex::new(vec![ "Chrome_RenderWidgetHostHWND".to_string(), ])); diff --git a/komorebic/Cargo.toml b/komorebic/Cargo.toml index fb4457be..9e1faa16 100644 --- a/komorebic/Cargo.toml +++ b/komorebic/Cargo.toml @@ -36,7 +36,7 @@ shadow-rs = { workspace = true } [features] default = ["schemars"] -schemars = ["dep:schemars"] +schemars = ["dep:schemars", "komorebi-client/schemars"] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(FALSE)'] } \ No newline at end of file diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 5f728182..aef6545e 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -1003,6 +1003,8 @@ enum SubCommand { GlobalState, /// Launch the komorebi-gui debugging tool Gui, + /// Toggle the komorebi-shortcuts helper + ToggleShortcuts, /// Show a JSON representation of visible windows VisibleWindows, /// Show information about connected monitors @@ -2713,6 +2715,15 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) { SubCommand::Gui => { Command::new("komorebi-gui").spawn()?; } + SubCommand::ToggleShortcuts => { + let output = Command::new("taskkill") + .args(["/F", "/IM", "komorebi-shortcuts.exe"]) + .output()?; + + if !output.status.success() { + Command::new("komorebi-shortcuts.exe").spawn()?; + } + } SubCommand::VisibleWindows => { print_query(&SocketMessage::VisibleWindows); } diff --git a/wix/main.wxs b/wix/main.wxs index 991c202c..60f3deee 100644 --- a/wix/main.wxs +++ b/wix/main.wxs @@ -101,6 +101,9 @@ + + + @@ -123,6 +126,8 @@ + +