From 5520562a183c7c958e3c4e352e7906e2537e350d Mon Sep 17 00:00:00 2001 From: Alan Date: Mon, 21 Jul 2025 00:19:58 +0300 Subject: [PATCH] feat: support glob patterns for keyboard layout icons (#949) * Simplistic globbing for matching keyboard layout icons Update the logic for determining the display text for the current keyboard layout. Instead of a direct map lookup, iterate through the layout map to support wildcard matching. Patterns ending with `*` will match any language string starting with the characters before the `*`. This allows grouping similar layouts (e.g., `English`, `English (Colemak-DH ISO)`) under a single pattern like `English*`. * Use `IndexMap` instead of `HashMap` for keyboard layout icons map This enables users to choose which globs to prioritize via ordering in the config * Enable feature `serde` for `indexmap` * Document wildcard matching for keyboard layouts * Enable `indexmap2` feature flag for `schemars` * Add missing period * use string slices * Fix formatting --- Cargo.lock | 1 + Cargo.toml | 5 +++-- docs/modules/Keyboard.md | 2 +- src/modules/keyboard.rs | 21 ++++++++++++++++----- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 700ac5a..4543f1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2969,6 +2969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", + "indexmap", "ref-cast", "schemars_derive", "serde", diff --git a/Cargo.toml b/Cargo.toml index a85aada..c38bf0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,7 +132,7 @@ tracing-appender = "0.2.3" strip-ansi-escapes = "0.2.0" color-eyre = "0.6.5" serde = { version = "1.0.219", features = ["derive"] } -indexmap = "2.10.0" +indexmap = { version = "2.10.0", features = ["serde"] } dirs = "6.0.0" walkdir = "2.5.0" notify = { version = "8.1.0", default-features = false } @@ -191,7 +191,8 @@ rustix = { version = "1.0.7", default-features = false, features = ["std", "fs", serde_json = { version = "1.0.140", optional = true } # ipc, niri # schema -schemars = { version = "1.0.4", optional = true } + +schemars = { version = "1.0.4", optional = true, features = ["indexmap2"] } [build-dependencies] clap = { version = "4.5.41", features = ["derive"] } diff --git a/docs/modules/Keyboard.md b/docs/modules/Keyboard.md index 3dbdebf..5afe07d 100644 --- a/docs/modules/Keyboard.md +++ b/docs/modules/Keyboard.md @@ -25,7 +25,7 @@ Displays the toggle state of the capslock, num lock and scroll lock keys, and th | `icons.num_off` | `string` or [image](images) | `''` | Icon to show for disabled num lock indicator. | | `icons.scroll_on` | `string` or [image](images) | `` | Icon to show for enabled scroll lock indicator. | | `icons.scroll_off` | `string` or [image](images) | `''` | Icon to show for disabled scroll lock indicator. | -| `icons.layout_map` | `Map` | `{}` | Map of icons or labels to show for a particular keyboard layout. Layouts use their actual name if not present in the map. | +| `icons.layout_map` | `Map` | `{}` | Map of icons or labels to show for a particular keyboard layout. Layouts use their actual name if not present in the map. Layouts are matched in the order they appear in the map. If a pattern to match ends with a `*`, it acts as a wildcard, matching any layout name that begins with the part before the `*`. | | `seat` | `string` | `seat0` | ID of the Wayland seat to attach to. |
diff --git a/src/modules/keyboard.rs b/src/modules/keyboard.rs index 145422f..2f1971f 100644 --- a/src/modules/keyboard.rs +++ b/src/modules/keyboard.rs @@ -1,8 +1,7 @@ -use std::collections::HashMap; - use color_eyre::Result; use color_eyre::eyre::Report; use gtk::prelude::*; +use indexmap::IndexMap; use serde::Deserialize; use tokio::sync::mpsc; use tracing::{debug, trace}; @@ -129,7 +128,7 @@ struct Icons { /// } /// ``` #[serde(default)] - layout_map: HashMap, + layout_map: IndexMap, } impl Default for Icons { @@ -141,7 +140,7 @@ impl Default for Icons { num_off: String::new(), scroll_on: default_icon_scroll(), scroll_off: String::new(), - layout_map: HashMap::new(), + layout_map: IndexMap::new(), } } } @@ -338,7 +337,19 @@ impl Module for KeyboardModule { } } KeyboardUpdate::Layout(KeyboardLayoutUpdate(language)) => { - let text = icons.layout_map.get(&language).unwrap_or(&language); + let text = icons + .layout_map + .iter() + .find_map(|(pattern, display_text)| { + let is_match = if pattern.ends_with("*") { + language.starts_with(&pattern[..pattern.len() - 1]) + } else { + pattern == &language + }; + + is_match.then(|| display_text) + }) + .unwrap_or(&language); layout_button.set_label(text); } });