mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Implement custom hotkey overlay titles
This commit is contained in:
+20
-2
@@ -1334,6 +1334,7 @@ pub struct Bind {
|
|||||||
pub cooldown: Option<Duration>,
|
pub cooldown: Option<Duration>,
|
||||||
pub allow_when_locked: bool,
|
pub allow_when_locked: bool,
|
||||||
pub allow_inhibiting: bool,
|
pub allow_inhibiting: bool,
|
||||||
|
pub hotkey_overlay_title: Option<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
@@ -3232,6 +3233,7 @@ where
|
|||||||
let mut allow_when_locked = false;
|
let mut allow_when_locked = false;
|
||||||
let mut allow_when_locked_node = None;
|
let mut allow_when_locked_node = None;
|
||||||
let mut allow_inhibiting = true;
|
let mut allow_inhibiting = true;
|
||||||
|
let mut hotkey_overlay_title = None;
|
||||||
for (name, val) in &node.properties {
|
for (name, val) in &node.properties {
|
||||||
match &***name {
|
match &***name {
|
||||||
"repeat" => {
|
"repeat" => {
|
||||||
@@ -3249,6 +3251,9 @@ where
|
|||||||
"allow-inhibiting" => {
|
"allow-inhibiting" => {
|
||||||
allow_inhibiting = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
allow_inhibiting = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
||||||
}
|
}
|
||||||
|
"hotkey-overlay-title" => {
|
||||||
|
hotkey_overlay_title = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);
|
||||||
|
}
|
||||||
name_str => {
|
name_str => {
|
||||||
ctx.emit_error(DecodeError::unexpected(
|
ctx.emit_error(DecodeError::unexpected(
|
||||||
name,
|
name,
|
||||||
@@ -3271,6 +3276,7 @@ where
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(child) = children.next() {
|
if let Some(child) = children.next() {
|
||||||
@@ -3306,6 +3312,7 @@ where
|
|||||||
cooldown,
|
cooldown,
|
||||||
allow_when_locked,
|
allow_when_locked,
|
||||||
allow_inhibiting,
|
allow_inhibiting,
|
||||||
|
hotkey_overlay_title,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -3707,10 +3714,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binds {
|
binds {
|
||||||
Mod+Escape { toggle-keyboard-shortcuts-inhibit; }
|
Mod+Escape hotkey-overlay-title="Inhibit" { toggle-keyboard-shortcuts-inhibit; }
|
||||||
Mod+Shift+Escape allow-inhibiting=true { toggle-keyboard-shortcuts-inhibit; }
|
Mod+Shift+Escape allow-inhibiting=true { toggle-keyboard-shortcuts-inhibit; }
|
||||||
Mod+T allow-when-locked=true { spawn "alacritty"; }
|
Mod+T allow-when-locked=true { spawn "alacritty"; }
|
||||||
Mod+Q { close-window; }
|
Mod+Q hotkey-overlay-title=null { close-window; }
|
||||||
Mod+Shift+H { focus-monitor-left; }
|
Mod+Shift+H { focus-monitor-left; }
|
||||||
Mod+Ctrl+Shift+L { move-window-to-monitor-right; }
|
Mod+Ctrl+Shift+L { move-window-to-monitor-right; }
|
||||||
Mod+Comma { consume-window-into-column; }
|
Mod+Comma { consume-window-into-column; }
|
||||||
@@ -4055,6 +4062,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: false,
|
allow_inhibiting: false,
|
||||||
|
hotkey_overlay_title: Some(Some("Inhibit".to_owned())),
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4066,6 +4074,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: false,
|
allow_inhibiting: false,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4077,6 +4086,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: true,
|
allow_when_locked: true,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4088,6 +4098,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: Some(None),
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4099,6 +4110,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4110,6 +4122,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4121,6 +4134,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4132,6 +4146,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4145,6 +4160,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4156,6 +4172,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: false,
|
allow_inhibiting: false,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -4167,6 +4184,7 @@ mod tests {
|
|||||||
cooldown: Some(Duration::from_millis(150)),
|
cooldown: Some(Duration::from_millis(150)),
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
switch_events: SwitchBinds {
|
switch_events: SwitchBinds {
|
||||||
|
|||||||
@@ -3009,6 +3009,7 @@ fn should_intercept_key(
|
|||||||
// But logically, nothing can inhibit its actions. Only opening it can be
|
// But logically, nothing can inhibit its actions. Only opening it can be
|
||||||
// inhibited.
|
// inhibited.
|
||||||
allow_inhibiting: false,
|
allow_inhibiting: false,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3074,6 +3075,7 @@ fn find_bind(
|
|||||||
// It also makes no sense to inhibit the default power key handling.
|
// It also makes no sense to inhibit the default power key handling.
|
||||||
// Hardcoded binds must never be inhibited.
|
// Hardcoded binds must never be inhibited.
|
||||||
allow_inhibiting: false,
|
allow_inhibiting: false,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3541,6 +3543,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
let comp_mod = CompositorMod::Super;
|
let comp_mod = CompositorMod::Super;
|
||||||
@@ -3726,6 +3729,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -3737,6 +3741,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -3748,6 +3753,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -3759,6 +3765,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -3770,6 +3777,7 @@ mod tests {
|
|||||||
cooldown: None,
|
cooldown: None,
|
||||||
allow_when_locked: false,
|
allow_when_locked: false,
|
||||||
allow_inhibiting: true,
|
allow_inhibiting: true,
|
||||||
|
hotkey_overlay_title: None,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
+175
-18
@@ -4,7 +4,7 @@ use std::collections::HashMap;
|
|||||||
use std::iter::zip;
|
use std::iter::zip;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use niri_config::{Action, Config, Key, Modifiers, Trigger};
|
use niri_config::{Action, Bind, Config, Key, Modifiers, Trigger};
|
||||||
use pangocairo::cairo::{self, ImageSurface};
|
use pangocairo::cairo::{self, ImageSurface};
|
||||||
use pangocairo::pango::{AttrColor, AttrInt, AttrList, AttrString, FontDescription, Weight};
|
use pangocairo::pango::{AttrColor, AttrInt, AttrList, AttrString, FontDescription, Weight};
|
||||||
use smithay::backend::renderer::element::Kind;
|
use smithay::backend::renderer::element::Kind;
|
||||||
@@ -125,6 +125,52 @@ impl HotkeyOverlay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_bind(
|
||||||
|
binds: &[Bind],
|
||||||
|
comp_mod: CompositorMod,
|
||||||
|
action: &Action,
|
||||||
|
) -> Option<(String, String)> {
|
||||||
|
let mut bind_with_non_null = None;
|
||||||
|
let mut bind_with_custom_title = None;
|
||||||
|
let mut found_null_title = false;
|
||||||
|
|
||||||
|
for bind in binds {
|
||||||
|
if bind.action != *action {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &bind.hotkey_overlay_title {
|
||||||
|
Some(Some(_)) => {
|
||||||
|
bind_with_custom_title.get_or_insert(bind);
|
||||||
|
}
|
||||||
|
Some(None) => {
|
||||||
|
found_null_title = true;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
bind_with_non_null.get_or_insert(bind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bind_with_custom_title.is_none() && found_null_title {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut title = None;
|
||||||
|
let key = if let Some(bind) = bind_with_custom_title.or(bind_with_non_null) {
|
||||||
|
if let Some(Some(custom)) = &bind.hotkey_overlay_title {
|
||||||
|
title = Some(custom.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
key_name(comp_mod, &bind.key)
|
||||||
|
} else {
|
||||||
|
String::from("(not bound)")
|
||||||
|
};
|
||||||
|
let title = title.unwrap_or_else(|| action_name(action));
|
||||||
|
|
||||||
|
Some((format!(" {key} "), title))
|
||||||
|
}
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
renderer: &mut GlesRenderer,
|
renderer: &mut GlesRenderer,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
@@ -212,8 +258,17 @@ fn render(
|
|||||||
actions.push(&Action::Screenshot);
|
actions.push(&Action::Screenshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add actions with a custom hotkey-overlay-title.
|
||||||
|
for bind in binds {
|
||||||
|
if matches!(bind.hotkey_overlay_title, Some(Some(_))) {
|
||||||
|
// Avoid duplicate actions.
|
||||||
|
if !actions.contains(&&bind.action) {
|
||||||
|
actions.push(&bind.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add the spawn actions.
|
// Add the spawn actions.
|
||||||
let mut spawn_actions = Vec::new();
|
|
||||||
for bind in binds.iter().filter(|bind| {
|
for bind in binds.iter().filter(|bind| {
|
||||||
matches!(bind.action, Action::Spawn(_))
|
matches!(bind.action, Action::Spawn(_))
|
||||||
// Only show binds with Mod or Super to filter out stuff like volume up/down.
|
// Only show binds with Mod or Super to filter out stuff like volume up/down.
|
||||||
@@ -225,25 +280,14 @@ fn render(
|
|||||||
let action = &bind.action;
|
let action = &bind.action;
|
||||||
|
|
||||||
// We only show one bind for each action, so we need to deduplicate the Spawn actions.
|
// We only show one bind for each action, so we need to deduplicate the Spawn actions.
|
||||||
if !spawn_actions.contains(&action) {
|
if !actions.contains(&action) {
|
||||||
spawn_actions.push(action);
|
actions.push(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actions.extend(spawn_actions);
|
|
||||||
|
|
||||||
let strings = actions
|
let strings = actions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|action| {
|
.filter_map(|action| format_bind(binds, comp_mod, action))
|
||||||
let key = config
|
|
||||||
.binds
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.find(|bind| bind.action == *action)
|
|
||||||
.map(|bind| key_name(comp_mod, &bind.key))
|
|
||||||
.unwrap_or_else(|| String::from("(not bound)"));
|
|
||||||
|
|
||||||
(format!(" {key} "), action_name(action))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut font = FontDescription::from_string(FONT);
|
let mut font = FontDescription::from_string(FONT);
|
||||||
@@ -323,8 +367,16 @@ fn render(
|
|||||||
|
|
||||||
cr.rel_move_to((key_width + padding).into(), 0.);
|
cr.rel_move_to((key_width + padding).into(), 0.);
|
||||||
|
|
||||||
layout.set_attributes(None);
|
let (attrs, text) = match pango::parse_markup(action, '\0') {
|
||||||
layout.set_markup(action);
|
Ok((attrs, text, _accel)) => (Some(attrs), text),
|
||||||
|
Err(err) => {
|
||||||
|
warn!("error parsing markup for key {key}: {err}");
|
||||||
|
(None, action.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
layout.set_attributes(attrs.as_ref());
|
||||||
|
layout.set_text(&text);
|
||||||
pangocairo::functions::show_layout(&cr, &layout);
|
pangocairo::functions::show_layout(&cr, &layout);
|
||||||
|
|
||||||
cr.rel_move_to(
|
cr.rel_move_to(
|
||||||
@@ -473,3 +525,108 @@ fn prettify_keysym_name(name: &str) -> String {
|
|||||||
name.into()
|
name.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use insta::assert_snapshot;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn check(config: &str, action: Action) -> String {
|
||||||
|
let config = Config::parse("test.kdl", config).unwrap();
|
||||||
|
if let Some((key, title)) = format_bind(&config.binds.0, CompositorMod::Super, &action) {
|
||||||
|
format!("{key}: {title}")
|
||||||
|
} else {
|
||||||
|
String::from("None")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_bind() {
|
||||||
|
// Not bound.
|
||||||
|
assert_snapshot!(check("", Action::Screenshot), @" (not bound) : Take a Screenshot");
|
||||||
|
|
||||||
|
// Bound with a default title.
|
||||||
|
assert_snapshot!(
|
||||||
|
check(
|
||||||
|
r#"binds {
|
||||||
|
Mod+P { screenshot; }
|
||||||
|
}"#,
|
||||||
|
Action::Screenshot,
|
||||||
|
),
|
||||||
|
@" Super + P : Take a Screenshot"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Custom title.
|
||||||
|
assert_snapshot!(
|
||||||
|
check(
|
||||||
|
r#"binds {
|
||||||
|
Mod+P hotkey-overlay-title="Hello" { screenshot; }
|
||||||
|
}"#,
|
||||||
|
Action::Screenshot,
|
||||||
|
),
|
||||||
|
@" Super + P : Hello"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prefer first bind.
|
||||||
|
assert_snapshot!(
|
||||||
|
check(
|
||||||
|
r#"binds {
|
||||||
|
Mod+P { screenshot; }
|
||||||
|
Print { screenshot; }
|
||||||
|
}"#,
|
||||||
|
Action::Screenshot,
|
||||||
|
),
|
||||||
|
@" Super + P : Take a Screenshot"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prefer bind with custom title.
|
||||||
|
assert_snapshot!(
|
||||||
|
check(
|
||||||
|
r#"binds {
|
||||||
|
Mod+P { screenshot; }
|
||||||
|
Print hotkey-overlay-title="My Cool Bind" { screenshot; }
|
||||||
|
}"#,
|
||||||
|
Action::Screenshot,
|
||||||
|
),
|
||||||
|
@" PrtSc : My Cool Bind"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prefer first bind with custom title.
|
||||||
|
assert_snapshot!(
|
||||||
|
check(
|
||||||
|
r#"binds {
|
||||||
|
Mod+P hotkey-overlay-title="First" { screenshot; }
|
||||||
|
Print hotkey-overlay-title="My Cool Bind" { screenshot; }
|
||||||
|
}"#,
|
||||||
|
Action::Screenshot,
|
||||||
|
),
|
||||||
|
@" Super + P : First"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Any bind with null title hides it.
|
||||||
|
assert_snapshot!(
|
||||||
|
check(
|
||||||
|
r#"binds {
|
||||||
|
Mod+P { screenshot; }
|
||||||
|
Print hotkey-overlay-title=null { screenshot; }
|
||||||
|
}"#,
|
||||||
|
Action::Screenshot,
|
||||||
|
),
|
||||||
|
@"None"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Custom title takes preference over null.
|
||||||
|
assert_snapshot!(
|
||||||
|
check(
|
||||||
|
r#"binds {
|
||||||
|
Mod+P hotkey-overlay-title="Hello" { screenshot; }
|
||||||
|
Print hotkey-overlay-title=null { screenshot; }
|
||||||
|
}"#,
|
||||||
|
Action::Screenshot,
|
||||||
|
),
|
||||||
|
@" Super + P : Hello"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -119,6 +119,45 @@ Mouse clicks operate on the window that was focused at the time of the click, no
|
|||||||
|
|
||||||
Note that binding `Mod+MouseLeft` or `Mod+MouseRight` will override the corresponding gesture (moving or resizing the window).
|
Note that binding `Mod+MouseLeft` or `Mod+MouseRight` will override the corresponding gesture (moving or resizing the window).
|
||||||
|
|
||||||
|
### Custom Hotkey Overlay Titles
|
||||||
|
|
||||||
|
<sup>Since: next release</sup>
|
||||||
|
|
||||||
|
The hotkey overlay (the Important Hotkeys dialog) shows a hardcoded list of binds.
|
||||||
|
You can customize this list using the `hotkey-overlay-title` property.
|
||||||
|
|
||||||
|
To add a bind to the hotkey overlay, set the property to the title that you want to show:
|
||||||
|
```kdl
|
||||||
|
binds {
|
||||||
|
Mod+Shift+S hotkey-overlay-title="Toggle Dark/Light Style" { spawn "some-script.sh"; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Binds with custom titles are listed after the hardcoded binds and before non-customized Spawn binds.
|
||||||
|
|
||||||
|
To remove a hardcoded bind from the hotkey overlay, set the property to null:
|
||||||
|
```kdl
|
||||||
|
binds {
|
||||||
|
Mod+Q hotkey-overlay-title=null { close-window; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> When multiple key combinations are bound to the same action:
|
||||||
|
> - If any of the binds has a custom hotkey overlay title, niri will show that bind.
|
||||||
|
> - Otherwise, if any of the binds has a null title, niri will hide the bind.
|
||||||
|
> - Otherwise, niri will show the first key combination.
|
||||||
|
|
||||||
|
Custom titles support [Pango markup](https://docs.gtk.org/Pango/pango_markup.html):
|
||||||
|
|
||||||
|
```kdl
|
||||||
|
binds {
|
||||||
|
Mod+Shift+S hotkey-overlay-title="<b>Toggle</b> <span foreground='red'>Dark</span>/Light Style" { spawn "some-script.sh"; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### Actions
|
### Actions
|
||||||
|
|
||||||
Every action that you can bind is also available for programmatic invocation via `niri msg action`.
|
Every action that you can bind is also available for programmatic invocation via `niri msg action`.
|
||||||
|
|||||||
@@ -166,3 +166,6 @@ hotkey-overlay {
|
|||||||
skip-at-startup
|
skip-at-startup
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can customize which binds the hotkey overlay shows using the `hotkey-overlay-title` property.
|
||||||
|
Check the [key bindings](./Configuration:-Key-Bindings.md) wiki page for details.
|
||||||
|
|||||||
Reference in New Issue
Block a user