1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! Profile keyboard shortcuts: per-profile hotkeys and shortcut string building.
use crate::app::window_state::WindowState;
use winit::event::{ElementState, KeyEvent};
use winit::keyboard::{Key, NamedKey};
impl WindowState {
/// Handle profile keyboard shortcuts (per-profile hotkeys defined in profiles.yaml)
pub(crate) fn handle_profile_shortcuts(&mut self, event: &KeyEvent) -> bool {
if event.state != ElementState::Pressed {
return false;
}
// Build shortcut string from current key event
let shortcut = self.build_shortcut_string(event);
if shortcut.is_empty() {
return false;
}
// Look up profile by shortcut
if let Some(profile) = self.overlay_ui.profile_manager.find_by_shortcut(&shortcut) {
let profile_id = profile.id;
let profile_name = profile.name.clone();
// Open the profile (creates a new tab)
self.open_profile(profile_id);
log::info!(
"Opened profile '{}' via shortcut '{}'",
profile_name,
shortcut
);
self.request_redraw();
return true;
}
false
}
/// Build a shortcut string from a key event (e.g., "Cmd+1", "Ctrl+Shift+2")
pub(crate) fn build_shortcut_string(&self, event: &KeyEvent) -> String {
let modifiers = self.input_handler.modifiers.state();
let mut parts = Vec::new();
// Add modifier keys (in canonical order).
// On macOS Cmd is reported as super_key(); on Windows/Linux the primary modifier is Ctrl.
#[cfg(target_os = "macos")]
{
if modifiers.super_key() {
parts.push("Cmd");
}
if modifiers.control_key() {
parts.push("Ctrl");
}
}
#[cfg(not(target_os = "macos"))]
{
if modifiers.control_key() {
parts.push("Ctrl");
}
}
if modifiers.alt_key() {
parts.push("Alt");
}
if modifiers.shift_key() {
parts.push("Shift");
}
// Add the key itself
let key_name = match &event.logical_key {
Key::Character(c) => {
let s = c.to_string();
if s.len() == 1 {
Some(s.to_uppercase())
} else {
None
}
}
Key::Named(named) => {
// Convert named keys to string representation
match named {
NamedKey::F1 => Some("F1".to_string()),
NamedKey::F2 => Some("F2".to_string()),
NamedKey::F3 => Some("F3".to_string()),
NamedKey::F4 => Some("F4".to_string()),
NamedKey::F5 => Some("F5".to_string()),
NamedKey::F6 => Some("F6".to_string()),
NamedKey::F7 => Some("F7".to_string()),
NamedKey::F8 => Some("F8".to_string()),
NamedKey::F9 => Some("F9".to_string()),
NamedKey::F10 => Some("F10".to_string()),
NamedKey::F11 => Some("F11".to_string()),
NamedKey::F12 => Some("F12".to_string()),
_ => None,
}
}
_ => None,
};
if let Some(key) = key_name {
parts.push(key.leak()); // Safe for short-lived strings in this context
parts.join("+")
} else {
String::new()
}
}
}