fix(shortcuts): plain , and ; bindings follow the typed character on AZERTY (#7521)

* fix(shortcuts): plain , and ; bindings follow the typed character on AZERTY

The positional match for , and . (so Shift+Cmd+. works on Nordic/German layouts)
applied to every shortcut. On AZERTY, ; sits on the US comma key, so ; fired the
command bound to , (brush hardness up/down did the same thing). Only shortcuts
with Cmd now follow the key position.

* contributors: credit Byxis by their GitHub profile name
This commit is contained in:
Alexis authored and GitHub committed 2026-10-18 16:56:58 -04:00
1 parent 5095babf89
commit 449b99a74e
3 files changed
+27 -2

No files matched your search

+3
View File
@@ -169,3 +169,6 @@ sync_github_name = true
[people.Silvertree2010]
display_name = "Netter"
sync_github_name = false
[people.Byxis]
sync_github_name = true
+4 -2
View File
@@ -215,7 +215,9 @@ fn key_owner(app: &PhotocraftApp, ctx: &egui::Context) -> (bool, usize, bool, bo
}
/// Keys whose shortcuts follow the key's position, as in Photoshop: ⇧⌘. is ⇧⌘> on a US layout,
/// but types `:` on Nordic and German ones, so its character would never match.
/// but types `:` on Nordic and German ones, so its character would never match. Only ⌘ shortcuts
/// follow the position: a plain `,` or `;` binding (brush hardness on AZERTY, where `;` sits on the
/// US comma key) must match the character typed.
const POSITIONAL: [Key; 2] = [Key::Period, Key::Comma];
/// Dispatch this frame's shortcut presses in the order they arrived (⌘Z then ⌘S undoes, then
@@ -236,7 +238,7 @@ pub fn dispatch_pressed(app: &mut PhotocraftApp, ctx: &egui::Context, focus: Foc
egui::Event::Key { key, physical_key, pressed: true, modifiers, .. } => {
let by_position = physical_key
.filter(|p| POSITIONAL.contains(p))
.and_then(|p| table.iter().find(|(_, sc)| sc.logical_key == p && key_matches(sc, p, *modifiers)));
.and_then(|p| table.iter().find(|(_, sc)| sc.modifiers.command && sc.logical_key == p && key_matches(sc, p, *modifiers)));
by_position.or_else(|| table.iter().find(|(_, sc)| key_matches(sc, *key, *modifiers))).map(|(id, _)| (id.clone(), *key == Key::Tab))
}
_ => None,
@@ -804,3 +804,23 @@ fn type_size_keys_follow_key_position_and_the_typed_selection() {
assert_eq!(logged(&h), ["type.decreaseSize"]);
assert_eq!(sizes(&h), [(2, base), (3, base + 2.0)], "only the selected \"He\" shrinks");
}
/// AZERTY: `;` sits on the US comma key and `,` on the US M key. Plain `,` / `;` bindings (brush
/// hardness) must match the character typed, not the key's position, or `;` fires `,`'s command.
#[test]
fn plain_comma_and_semicolon_bindings_follow_the_typed_character_on_azerty() {
let mut h = harness();
h.state_mut().session.edit_prefs(|p| {
p.shortcuts.insert("tools.decreaseBrushHardness".into(), ",".into());
p.shortcuts.insert("tools.increaseBrushHardness".into(), ";".into());
});
let tap = |h: &mut Harness<'_, PhotocraftApp>, key, physical| {
h.event(egui::Event::Key { key, physical_key: Some(physical), pressed: true, repeat: false, modifiers: Modifiers::NONE });
h.event(egui::Event::Key { key, physical_key: Some(physical), pressed: false, repeat: false, modifiers: Modifiers::NONE });
h.run_steps(2);
};
tap(&mut h, egui::Key::Semicolon, egui::Key::Comma);
assert_eq!(logged(&h), ["tools.increaseBrushHardness"]);
tap(&mut h, egui::Key::Comma, egui::Key::M);
assert_eq!(logged(&h), ["tools.decreaseBrushHardness"]);
}