Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions glide.default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ group_bars.vertical_placement = "right"
# Enables the status icon.
status_icon.enable = true

# Whether to include Glide's default key bindings.
#
# Note: Default keys are always included if there is no keys section of the
# config. You can disable this by adding an empty `[keys]` section to your
# config file.
#
# Individual key bindings can be disabled by setting the key to "disabled".
default_keys = false

[keys]
# Note: Modifier and key names must be capitalized.

Expand Down
5 changes: 3 additions & 2 deletions src/actor/mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ impl Mouse {
state.screens = frames;
state.converter = converter;
}
Request::ConfigUpdated(config) => {
*self.config.borrow_mut() = config;
Request::ConfigUpdated(new_config) => {
drop(config);
*self.config.borrow_mut() = new_config;
self.apply_config();
}
}
Expand Down
115 changes: 113 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,20 @@ pub struct Config {
#[serde(default)]
struct ConfigPartial {
settings: SettingsPartial,
keys: Option<FxHashMap<String, WmCommand>>,
keys: Option<FxHashMap<String, WmCommandOrDisable>>,
}

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum WmCommandOrDisable {
WmCommand(WmCommand),
Disable(Disabled),
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum Disabled {
Disable,
}

#[derive(PartialConfig!)]
Expand All @@ -66,6 +79,7 @@ pub struct Settings {
pub focus_follows_mouse: bool,
pub outer_gap: f64,
pub inner_gap: f64,
pub default_keys: bool,
#[derive_args(GroupBarsPartial)]
pub group_bars: GroupBars,
#[derive_args(StatusIconPartial)]
Expand Down Expand Up @@ -143,6 +157,10 @@ impl ConfigPartial {
fn validate(self) -> Result<Config, SpannedError> {
let mut keys = Vec::new();
for (key, cmd) in self.keys.unwrap_or_default() {
let cmd = match cmd {
WmCommandOrDisable::WmCommand(wm_command) => wm_command,
WmCommandOrDisable::Disable(_) => continue,
};
let Ok(key) = Hotkey::from_str(&key) else {
return Err(SpannedError {
message: format!("Could not parse hotkey: {key}"),
Expand All @@ -158,9 +176,16 @@ impl ConfigPartial {
}

fn merge(low: Self, high: Self) -> Self {
let mut keys =
if high.settings.default_keys.unwrap_or(Config::default().settings.default_keys) {
low.keys.unwrap_or_default()
} else {
Default::default()
};
keys.extend(high.keys.unwrap_or_default());
Self {
settings: SettingsPartial::merge(low.settings, high.settings),
keys: high.keys.or(low.keys),
keys: Some(keys),
}
}
}
Expand Down Expand Up @@ -247,4 +272,90 @@ mod tests {
fn default_settings_match_unspecified_setting_values() {
assert_eq!(Config::default().settings, Config::parse("").unwrap().settings);
}

#[test]
fn default_keys_false_excludes_default_bindings() {
let config = Config::parse(
r#"
[settings]
default_keys = false

[keys]
"Alt + Q" = "debug"
"#,
)
.unwrap();

// Should only have our custom key, not the defaults
assert_eq!(config.keys.len(), 1);
let (hotkey, _cmd) = &config.keys[0];
assert_eq!(hotkey.to_string(), "Alt + KeyQ");
}

#[test]
fn default_keys_true_includes_default_bindings() {
let config = Config::parse(
r#"
[settings]
default_keys = true

[keys]
"Alt + Q" = "debug"
"#,
)
.unwrap();

// Should have default keys plus our custom key
let default_key_count = Config::default().keys.len();
assert_eq!(config.keys.len(), default_key_count + 1);

// Our custom key should be present
assert!(config.keys.iter().any(|(hk, _)| hk.to_string() == "Alt + KeyQ"));
}

#[test]
fn disable_removes_key_binding() {
let config = Config::parse(
r#"
[settings]
default_keys = false

[keys]
"Alt + Q" = "debug"
"Alt + W" = "disable"
"#,
)
.unwrap();

// "disable" key should not appear in final config
assert_eq!(config.keys.len(), 1);
assert!(config.keys.iter().any(|(hk, _)| hk.to_string() == "Alt + KeyQ"));
assert!(!config.keys.iter().any(|(hk, _)| hk.to_string() == "Alt + KeyW"));
}

#[test]
fn disable_can_override_default_key() {
// First verify Alt+H exists in defaults
let default_config = Config::default();
assert!(
default_config.keys.iter().any(|(hk, _)| hk.to_string() == "Alt + KeyH"),
"Alt+H should be a default key binding"
);

let config = Config::parse(
r#"
[settings]
default_keys = true

[keys]
"Alt + H" = "disable"
"#,
)
.unwrap();

// Alt+H should be removed even though it's in defaults
assert!(!config.keys.iter().any(|(hk, _)| hk.to_string() == "Alt + KeyH"));
// But other default keys should still be present
assert!(config.keys.iter().any(|(hk, _)| hk.to_string() == "Alt + KeyJ"));
}
}