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.
This commit is contained in:
LGUG2Z
2025-05-03 17:37:02 -07:00
parent 4ea835fa59
commit 70f561e6ac
13 changed files with 256 additions and 15 deletions

73
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"
]
]

View File

@@ -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",
]

View File

@@ -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",

View File

@@ -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 }

View File

@@ -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

View File

@@ -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"]

View File

@@ -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 }

View File

@@ -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<Whkdrc>,
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")
}

View File

@@ -160,7 +160,14 @@ lazy_static! {
})
]));
static ref SESSION_FLOATING_APPLICATIONS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(Vec::new()));
static ref FLOATING_APPLICATIONS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(Vec::new()));
static ref FLOATING_APPLICATIONS: Arc<Mutex<Vec<MatchingRule>>> = 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<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"Chrome_RenderWidgetHostHWND".to_string(),
]));

View File

@@ -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)'] }

View File

@@ -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);
}

View File

@@ -101,6 +101,9 @@
<Component Id='binary4' Guid='*'>
<File Id='exe4' Name='komorebi-bar.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebi-bar.exe' KeyPath='yes' />
</Component>
<Component Id='binary5' Guid='*'>
<File Id='exe5' Name='komorebi-shortcuts.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebi-shortcuts.exe' KeyPath='yes' />
</Component>
</Directory>
</Directory>
</Directory>
@@ -123,6 +126,8 @@
<ComponentRef Id='binary4' />
<ComponentRef Id='binary5' />
<Feature Id='Environment' Title='PATH Environment Variable' Description='Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.' Level='1' Absent='allow'>
<ComponentRef Id='Path' />
</Feature>