diff --git a/README.md b/README.md
index 0b1161d..a8c7b2f 100644
--- a/README.md
+++ b/README.md
@@ -65,6 +65,9 @@ Styling names and classes are inspired by wofi, so most of the documentation and
| `row` | Row containing the entry, used to control hover effects. |
| `custom-key-label-text` | The label for custom keys |
| `custom-key-label-box` | Box containing the label, can be used for borders etc. |
+| `custom-key-hint-text` | The label for custom keys hint |
+| `custom-key-hint-box` | Box containing the hint, can be used for borders etc. |
+
Checkout more showcases in the [styles directory of this repo](styles).
diff --git a/examples/worf-warden/src/main.rs b/examples/worf-warden/src/main.rs
index fbc64b0..c2b07ae 100644
--- a/examples/worf-warden/src/main.rs
+++ b/examples/worf-warden/src/main.rs
@@ -3,9 +3,9 @@ use std::env;
use std::process::Command;
use std::thread::sleep;
use std::time::Duration;
-use worf_lib::config::Config;
+use worf_lib::config::{Config, CustomKeyHintLocation};
use worf_lib::desktop::{copy_to_clipboard, spawn_fork};
-use worf_lib::gui::{ItemProvider, Key, KeyBinding, MenuItem, Modifier};
+use worf_lib::gui::{CustomKeyHint, CustomKeys, ItemProvider, Key, KeyBinding, MenuItem, Modifier};
use worf_lib::{config, gui};
#[derive(Clone)]
@@ -189,6 +189,16 @@ fn key_type_all() -> KeyBinding {
key: Key::Num1,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "Alt+1 Type All".to_string(),
+ visible: true,
+ }
+}
+
+fn key_type_all_and_enter() -> KeyBinding {
+ KeyBinding {
+ key: Key::Exclamation,
+ modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
+ label: String::new(),
+ visible: false,
}
}
@@ -197,6 +207,16 @@ fn key_type_user() -> KeyBinding {
key: Key::Num2,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "Alt+2 Type User".to_string(),
+ visible: true,
+ }
+}
+
+fn key_type_user_and_enter() -> KeyBinding {
+ KeyBinding {
+ key: Key::At,
+ modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
+ label: String::new(),
+ visible: false,
}
}
@@ -205,6 +225,7 @@ fn key_type_password() -> KeyBinding {
key: Key::Num3,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "Alt+3 Type Password".to_string(),
+ visible: true,
}
}
@@ -212,7 +233,8 @@ fn key_type_password_and_enter() -> KeyBinding {
KeyBinding {
key: Key::Hash,
modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
- label: "Alt+Shift+3 Type Password + Enter".to_string(),
+ label: String::new(),
+ visible: false,
}
}
@@ -221,6 +243,16 @@ fn key_type_totp() -> KeyBinding {
key: Key::Num4,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "Alt+4 Type Totp".to_string(),
+ visible: true,
+ }
+}
+
+fn key_type_totp_and_enter() -> KeyBinding {
+ KeyBinding {
+ key: Key::Dollar,
+ modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
+ label: String::new(),
+ visible: false,
}
}
@@ -229,6 +261,7 @@ fn key_sync() -> KeyBinding {
key: Key::R,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "Alt+r Sync".to_string(),
+ visible: true,
}
}
@@ -238,6 +271,7 @@ fn key_totp() -> KeyBinding {
key: Key::T,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "Alt+t Totp".to_string(),
+ visible: true,
}
}
@@ -246,6 +280,7 @@ fn key_lock() -> KeyBinding {
key: Key::L,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "Alt+l Lock".to_string(),
+ visible: true,
}
}
@@ -255,16 +290,25 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {
provider,
false,
None,
- Some(vec![
- key_type_all(),
- key_type_user(),
- key_type_password(),
- key_type_password_and_enter(),
- key_type_totp(),
- key_sync(),
- key_totp(),
- key_lock(),
- ]),
+ Some(CustomKeys {
+ bindings: vec![
+ key_type_all(),
+ key_type_all_and_enter(),
+ key_type_user(),
+ key_type_user_and_enter(),
+ key_type_password(),
+ key_type_password_and_enter(),
+ key_type_totp(),
+ key_type_totp_and_enter(),
+ key_sync(),
+ key_totp(),
+ key_lock(),
+ ],
+ hint: Some(CustomKeyHint {
+ label: "Use Shift as additional modifier to send enter".to_string(),
+ location: CustomKeyHintLocation::Top,
+ }),
+ }),
) {
Ok(selection) => {
if let Some(meta) = selection.menu.data {
@@ -276,18 +320,15 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {
sleep(Duration::from_millis(500));
if let Some(key) = selection.custom_key {
- if key == key_type_all() {
+ if key == key_type_all() || key == key_type_all_and_enter() {
keyboard_type(&rbw_get_user(id, false)?);
keyboard_tab();
keyboard_type(&rbw_get_password(id, false)?);
- } else if key == key_type_user() {
+ } else if key == key_type_user() || key == key_type_user_and_enter() {
keyboard_type(&rbw_get_user(id, false)?);
- } else if key == key_type_password() {
+ } else if key == key_type_password() || key == key_type_password_and_enter() {
keyboard_type(&rbw_get_password(id, false)?);
- } else if key == key_type_password_and_enter() {
- keyboard_type(&rbw_get_password(id, false)?);
- keyboard_return();
- } else if key == key_type_totp() {
+ } else if key == key_type_totp() || key == key_type_totp_and_enter() {
keyboard_type(&rbw_get_totp(id, false)?);
} else if key == key_lock() {
rbw("lock", None)?;
@@ -296,6 +337,10 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {
} else if key == key_totp() {
rbw_get_totp(id, true)?;
}
+
+ if key.modifiers.contains(&Modifier::Shift) {
+ keyboard_return();
+ }
} else {
let pw = rbw_get_password(id, true)?;
if let Err(e) = copy_to_clipboard(pw, None) {
diff --git a/styles/compact/style.css b/styles/compact/style.css
index 81837dd..83d3a16 100644
--- a/styles/compact/style.css
+++ b/styles/compact/style.css
@@ -74,8 +74,15 @@ font-family: DejaVu;
margin-bottom: 0.25em;
border-right: 1px solid rgba(214, 174, 0, 1);
border-left: 1px solid rgba(214, 174, 0, 1);
+ border-bottom: 1px solid rgba(214, 174, 0, 1);
+ padding-left: 1em;
+}
+
+#custom-key-hint-box {
+ margin-top: 0.25em;
+ margin-bottom: 0.25em;
padding-left: 1em;
}
#custom-key-label-text {
- }
+}
diff --git a/worf/src/lib/config.rs b/worf/src/lib/config.rs
index edc4f7b..4a05a6d 100644
--- a/worf/src/lib/config.rs
+++ b/worf/src/lib/config.rs
@@ -49,6 +49,12 @@ pub enum SortOrder {
Alphabetical,
}
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
+pub enum CustomKeyHintLocation {
+ Top,
+ Bottom,
+}
+
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Mode {
/// searches `$PATH` for executables and allows them to be run by selecting them.
diff --git a/worf/src/lib/gui.rs b/worf/src/lib/gui.rs
index cbbe9e1..4442f18 100644
--- a/worf/src/lib/gui.rs
+++ b/worf/src/lib/gui.rs
@@ -25,7 +25,7 @@ use gtk4_layer_shell::{Edge, KeyboardMode, LayerShell};
use log;
use regex::Regex;
-use crate::config::{Anchor, Config, MatchMethod, SortOrder, WrapMode};
+use crate::config::{Anchor, Config, CustomKeyHintLocation, MatchMethod, SortOrder, WrapMode};
use crate::desktop::known_image_extension_regex_pattern;
use crate::{Error, config, desktop};
@@ -83,17 +83,11 @@ impl From for Align {
}
}
-fn into_core_order(gtk_order: &Ordering) -> core::cmp::Ordering {
+fn into_core_order(gtk_order: Ordering) -> core::cmp::Ordering {
match gtk_order {
- Ordering::Smaller => {
- core::cmp::Ordering::Less
- }
- Ordering::Larger => {
- core::cmp::Ordering::Greater
- }
- _ => {
- core::cmp::Ordering::Equal
- }
+ Ordering::Smaller => core::cmp::Ordering::Less,
+ Ordering::Larger => core::cmp::Ordering::Greater,
+ _ => core::cmp::Ordering::Equal,
}
}
@@ -386,6 +380,19 @@ pub struct KeyBinding {
pub key: Key,
pub modifiers: HashSet,
pub label: String,
+ pub visible: bool,
+}
+
+#[derive(Clone, PartialEq, Debug)]
+pub struct CustomKeyHint {
+ pub label: String,
+ pub location: CustomKeyHintLocation,
+}
+
+#[derive(Clone, PartialEq, Debug)]
+pub struct CustomKeys {
+ pub bindings: Vec,
+ pub hint: Option,
}
impl MenuItem {
@@ -445,7 +452,7 @@ pub fn show(
item_provider: P,
new_on_empty: bool,
search_ignored_words: Option>,
- custom_keys: Option>,
+ custom_keys: Option,
) -> Result, Error>
where
T: Clone + 'static + Send,
@@ -503,7 +510,7 @@ fn build_ui(
app: Application,
new_on_empty: bool,
search_ignored_words: Option>,
- custom_keys: Option<&Vec>,
+ custom_keys: Option<&CustomKeys>,
) where
T: Clone + 'static + Send,
P: ItemProvider + 'static + Send,
@@ -569,7 +576,9 @@ fn build_ui(
let outer_box = gtk4::Box::new(config.orientation().into(), 0);
outer_box.set_widget_name("outer-box");
outer_box.append(&ui_elements.search);
- build_custom_key_view(custom_keys, &outer_box);
+ if let Some(custom_keys) = custom_keys {
+ build_custom_key_view(custom_keys, &outer_box);
+ }
ui_elements.window.set_child(Some(&outer_box));
@@ -595,15 +604,11 @@ fn build_ui(
log::debug!("got items after {:?}", wait_for_items.elapsed());
let active_cfg = config.clone();
- //let map_cfg = config.clone();
let animate_window = ui_elements.window.clone();
animate_window.connect_is_active_notify(move |w| {
window_show_resize(&active_cfg.clone(), w);
});
- // animate_window.connect_map(move |w| {
- // window_show_resize(&map_cfg.clone(), w);
- // });
build_ui_from_menu_items(&ui_elements, &meta, provider_elements);
@@ -626,7 +631,6 @@ fn build_main_box(config: &Config, ui_elements: &Rc(
}
}
-fn build_custom_key_view(custom_keys: Option<&Vec>, outer_box: >k4::Box) {
- let inner_box = FlowBox::new();
+fn build_custom_key_view(custom_keys: &CustomKeys, outer_box: >k4::Box) {
+ fn create_label(inner_box: &FlowBox, text: &str, label_css: &str, box_css: &str) {
+ let label_box = FlowBoxChild::new();
+ label_box.set_halign(Align::Fill);
+ inner_box.set_valign(Align::Start);
+ label_box.set_widget_name(box_css);
+ inner_box.append(&label_box);
+ inner_box.set_vexpand(false);
+ inner_box.set_hexpand(false);
+ let label = Label::new(Some(text));
+ label.set_halign(Align::Fill);
+ label.set_valign(Align::Start);
+ label.set_use_markup(true);
+ label.set_hexpand(true);
+ label.set_vexpand(false);
+ label.set_widget_name(label_css);
+ label.set_wrap(false);
+ label.set_xalign(0.0);
+ label_box.set_child(Some(&label));
+ }
+
+ let inner_box = gtk4::Box::new(Orientation::Vertical, 0);
inner_box.set_halign(Align::Fill);
- inner_box.set_widget_name("custom-key-box");
- if let Some(custom_keys) = custom_keys {
- for key in custom_keys {
- let label_box = FlowBoxChild::new();
- label_box.set_halign(Align::Fill);
- inner_box.set_valign(Align::Start);
- label_box.set_widget_name("custom-key-label-box");
- inner_box.append(&label_box);
- inner_box.set_vexpand(false);
- inner_box.set_hexpand(false);
- let label = Label::new(Some(&key.label));
- label.set_halign(Align::Fill);
- label.set_valign(Align::Start);
- label.set_use_markup(true);
- label.set_hexpand(true);
- label.set_vexpand(false);
- label.set_widget_name("custom-key-label-text");
- label.set_wrap(false);
- label.set_xalign(0.0);
- label_box.set_child(Some(&label));
+
+ let hint_box = FlowBox::new();
+ hint_box.set_halign(Align::Fill);
+ hint_box.set_widget_name("custom-key-box");
+
+ let custom_key_box = FlowBox::new();
+ custom_key_box.set_halign(Align::Fill);
+ custom_key_box.set_widget_name("custom-key-box");
+ inner_box.append(&custom_key_box);
+
+ let make_key_labels = || {
+ for key in custom_keys.bindings.iter().filter(|key| key.visible) {
+ create_label(
+ &custom_key_box,
+ key.label.as_ref(),
+ "custom-key-label-text",
+ "custom-key-label-box",
+ );
+ }
+ };
+
+ if let Some(hint) = custom_keys.hint.as_ref() {
+ match hint.location {
+ CustomKeyHintLocation::Top => {
+ inner_box.append(&hint_box);
+ create_label(
+ &hint_box,
+ &hint.label,
+ "custom-key-hint-text",
+ "custom-key-hint-box",
+ );
+ make_key_labels();
+ } // todo this surely can be done better
+ CustomKeyHintLocation::Bottom => {
+ make_key_labels();
+ create_label(
+ &hint_box,
+ &hint.label,
+ "custom-key-hint-text",
+ "custom-key-hint-box",
+ );
+ inner_box.append(&hint_box);
+ }
}
}
+
outer_box.append(&inner_box);
}
@@ -771,7 +819,7 @@ fn build_ui_from_menu_items(
fn setup_key_event_handler(
ui: &Rc>,
meta: &Rc>,
- custom_keys: Option<&Vec>,
+ custom_keys: Option<&CustomKeys>,
) {
let key_controller = EventControllerKey::new();
@@ -797,7 +845,7 @@ fn handle_key_press(
meta: &Rc>,
keyboard_key: gdk4::Key,
modifier_type: gdk4::ModifierType,
- custom_keys: Option<&Vec>,
+ custom_keys: Option<&CustomKeys>,
) -> Propagation {
let update_view = |query: &String| {
let mut lock = ui.menu_rows.write().unwrap();
@@ -820,7 +868,7 @@ fn handle_key_press(
if let Some(custom_keys) = custom_keys {
let mods = modifiers_from_mask(modifier_type);
- for custom_key in custom_keys {
+ for custom_key in &custom_keys.bindings {
log::debug!(
"comparing custom key {custom_key:?} to mask {mods:?} and key {keyboard_key}"
);
@@ -935,7 +983,10 @@ fn sort_flow_box_childs(
sort_menu_items_by_score(m1, m2)
}
-fn sort_menu_items_by_score(m1: Option<&MenuItem>, m2: Option<&MenuItem>) -> Ordering {
+fn sort_menu_items_by_score(
+ m1: Option<&MenuItem>,
+ m2: Option<&MenuItem>,
+) -> Ordering {
match (m1, m2) {
(Some(menu1), Some(menu2)) => {
fn compare(a: f64, b: f64) -> Ordering {
@@ -1376,7 +1427,7 @@ pub fn apply_sort(items: &mut [MenuItem], order: &SortOrder) {
}
}
- items.sort_by(|l, r| into_core_order(&sort_menu_items_by_score(Some(l), Some(r))));
+ items.sort_by(|l, r| into_core_order(sort_menu_items_by_score(Some(l), Some(r))));
}
}
}