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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! Keyboard shortcut for triggering actions.
//!
//! Wraps [`QShortcut`](https://doc.qt.io/qt-6/qshortcut.html).
//!
//! # Note
//!
//! `QShortcut` is a `QObject` subclass, **not** a `QWidget`. It does **not**
//! implement [`AsWidget`](crate::widget::AsWidget). Signal closures are always
//! reclaimed on [`Drop`]; the C++ object is always deleted.
use cxx::let_cxx_string;
use crate::ffi;
use crate::signal::{self, SignalHandle};
/// A keyboard shortcut that triggers a callback when pressed.
///
/// `Shortcut` uses a **builder pattern**: call [`Shortcut::new`] to obtain
/// a [`Builder`], chain configuration, then call `.build()`.
///
/// # Signals
///
/// | Method | Qt signal | Callback receives |
/// |---|---|---|
/// | [`Builder::on_activated`] / [`Shortcut::connect_activated`] | `QShortcut::activated` | `()` |
///
/// # Example
///
/// ```no_run
/// use qtrs::Shortcut;
///
/// let sc = Shortcut::new("Ctrl+O")
/// .on_activated(|| println!("opened!"))
/// .build();
/// ```
pub struct Shortcut {
ptr: *mut ffi::QShortcut,
signal_handles: Vec<SignalHandle>,
}
impl Shortcut {
/// Start building a new shortcut with the given key sequence.
pub fn new(key: impl Into<String>) -> Builder {
Builder::new(key.into())
}
// --- Properties ---
/// Set the keyboard shortcut key.
pub fn set_key(&self, key: &str) {
debug_assert!(!self.ptr.is_null());
let_cxx_string!(c_key = key);
unsafe { ffi::QShortcut_setKey(self.ptr, &c_key); }
}
/// Enable or disable the shortcut.
pub fn set_enabled(&self, enabled: bool) {
debug_assert!(!self.ptr.is_null());
unsafe { ffi::QShortcut_setEnabled(self.ptr, enabled); }
}
/// Set whether the shortcut auto-repeats when held.
pub fn set_auto_repeat(&self, repeat: bool) {
debug_assert!(!self.ptr.is_null());
unsafe { ffi::QShortcut_setAutoRepeat(self.ptr, repeat); }
}
// --- Runtime signal connections ---
/// Connect a callback when the shortcut is activated.
pub fn connect_activated<F: Fn() + 'static>(&mut self, f: F) {
debug_assert!(!self.ptr.is_null());
let handle = signal::leak_void(f);
unsafe { ffi::QShortcut_onActivated(self.ptr, handle.token); }
self.signal_handles.push(handle);
}
}
impl Drop for Shortcut {
fn drop(&mut self) {
if self.ptr.is_null() { return; }
for h in self.signal_handles.drain(..) {
unsafe { h.reclaim(); }
}
unsafe { ffi::QShortcut_delete(self.ptr); }
self.ptr = std::ptr::null_mut();
}
}
/// Builder for [`Shortcut`].
pub struct Builder {
key: String,
enabled: Option<bool>,
auto_repeat: Option<bool>,
on_activated: Option<Box<dyn Fn()>>,
}
impl Builder {
fn new(key: String) -> Self {
Self {
key,
enabled: None,
auto_repeat: None,
on_activated: None,
}
}
/// Enable or disable the shortcut.
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = Some(enabled);
self
}
/// Set whether the shortcut auto-repeats when held.
pub fn auto_repeat(mut self, repeat: bool) -> Self {
self.auto_repeat = Some(repeat);
self
}
/// Called when the shortcut is activated.
pub fn on_activated<F: Fn() + 'static>(mut self, f: F) -> Self {
self.on_activated = Some(Box::new(f));
self
}
/// Create the C++ `QShortcut` and return the Rust wrapper.
pub fn build(self) -> Shortcut {
let_cxx_string!(c_key = &self.key);
let ptr = unsafe {
ffi::QShortcut_new(&c_key, std::ptr::null_mut())
};
debug_assert!(!ptr.is_null());
let mut sc = Shortcut { ptr, signal_handles: Vec::new() };
if let Some(enabled) = self.enabled {
unsafe { ffi::QShortcut_setEnabled(ptr, enabled); }
}
if let Some(repeat) = self.auto_repeat {
unsafe { ffi::QShortcut_setAutoRepeat(ptr, repeat); }
}
if let Some(f) = self.on_activated {
let h = signal::leak_void(f);
unsafe { ffi::QShortcut_onActivated(ptr, h.token); }
sc.signal_handles.push(h);
}
sc
}
}