Skip to content

Commit d92f81d

Browse files
committed
Disable shortcuts in terminal, add back Ctrl+L and Ctrl+D
1 parent d0a9b1c commit d92f81d

4 files changed

Lines changed: 148 additions & 11 deletions

File tree

src/application.rs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,6 @@ mod imp {
8181
self.parent_constructed();
8282
let obj = self.obj();
8383
obj.setup_gactions();
84-
obj.set_accels_for_action("app.quit", &["<primary>q"]);
85-
obj.set_accels_for_action("app.shortcuts", &["<primary>question"]);
86-
obj.set_accels_for_action("win.refresh", &["F5"]);
87-
obj.set_accels_for_action("win.upgrade-container", &["<primary>u"]);
88-
obj.set_accels_for_action("win.upgrade-all", &["<primary><shift>u"]);
89-
obj.set_accels_for_action("win.install-package", &["<primary>i"]);
90-
obj.set_accels_for_action("win.preferences", &["<primary>comma"]);
91-
obj.set_accels_for_action("win.open-terminal", &["<primary>period"]);
92-
obj.set_accels_for_action("win.view-exportable-apps", &["<primary>e"]);
93-
obj.set_accels_for_action("win.delete-container", &["<primary>Delete"]);
94-
obj.set_accels_for_action("win.stop-container", &["<primary>s"]);
9584
}
9685
}
9786

@@ -254,6 +243,7 @@ impl DistroShelfApplication {
254243
root_store.start_background_tasks();
255244

256245
self.set_root_store(root_store);
246+
self.bind_shortcuts_model();
257247
let window =
258248
DistroShelfWindow::new(self.upcast_ref::<adw::Application>(), self.root_store());
259249
window.upcast()
@@ -269,6 +259,38 @@ impl DistroShelfApplication {
269259
self.add_action_entries([quit_action, about_action]);
270260
}
271261

262+
fn bind_shortcuts_model(&self) {
263+
let shortcuts = self.root_store().shortcuts_model();
264+
let app = self.clone();
265+
shortcuts.connect_items_changed(move |_, _, _, _| {
266+
app.sync_shortcut_accels();
267+
});
268+
self.sync_shortcut_accels();
269+
}
270+
271+
fn sync_shortcut_accels(&self) {
272+
let shortcuts = self.root_store().shortcuts_model();
273+
274+
for action in self.root_store().shortcut_action_names() {
275+
self.set_accels_for_action(action, &[]);
276+
}
277+
278+
for shortcut in shortcuts.iter::<gtk::Shortcut>() {
279+
let Ok(shortcut) = shortcut else {
280+
continue;
281+
};
282+
283+
let Some(named_action) = shortcut.action().and_downcast::<gtk::NamedAction>() else {
284+
continue;
285+
};
286+
let Some(trigger) = shortcut.trigger() else {
287+
continue;
288+
};
289+
290+
self.set_accels_for_action(&named_action.action_name(), &[&trigger.to_string()]);
291+
}
292+
}
293+
272294
fn show_about(&self) {
273295
let window = self.active_window().unwrap();
274296
let about =

src/models/root_store.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ use crate::query::Query;
3030

3131
use serde::Deserialize;
3232

33+
const SHORTCUT_DEFINITIONS: [(&str, &str); 13] = [
34+
("<primary>q", "app.quit"),
35+
("<primary>question", "app.shortcuts"),
36+
("F5", "win.refresh"),
37+
("<primary>u", "win.upgrade-container"),
38+
("<primary><shift>u", "win.upgrade-all"),
39+
("<primary>i", "win.install-package"),
40+
("<primary>comma", "win.preferences"),
41+
("<primary>period", "win.open-terminal"),
42+
("<primary>e", "win.view-exportable-apps"),
43+
("<primary>Delete", "win.delete-container"),
44+
("<primary>s", "win.stop-container"),
45+
("<primary>l", "win.command-log"),
46+
("<primary>d", "win.delete-container"),
47+
];
48+
3349
#[derive(Debug, Clone, Deserialize, Hash, Eq, PartialEq)]
3450
#[serde(rename_all = "PascalCase")]
3551
pub struct Image {
@@ -69,6 +85,9 @@ mod imp {
6985
#[property(get)]
7086
pub settings: gio::Settings,
7187

88+
pub shortcuts: gio::ListStore,
89+
pub shortcuts_enabled: std::cell::Cell<bool>,
90+
7291
#[property(get, set, builder(ViewType::default()))]
7392
current_view: RefCell<ViewType>,
7493
#[property(get, set, builder(DialogType::default()))]
@@ -109,6 +128,8 @@ mod imp {
109128
selected_task: Default::default(),
110129
bundled_update_available: std::cell::Cell::new(false),
111130
settings: gio::Settings::new("com.ranfdev.DistroShelf"),
131+
shortcuts: gio::ListStore::new::<gtk::Shortcut>(),
132+
shortcuts_enabled: std::cell::Cell::new(false),
112133
}
113134
}
114135
}
@@ -312,9 +333,57 @@ impl RootStore {
312333
);
313334
});
314335

336+
this.enable_shortcuts();
337+
315338
this
316339
}
317340

341+
fn build_shortcut(trigger: &str, action: &str) -> Option<gtk::Shortcut> {
342+
let trigger =
343+
gtk::ShortcutTrigger::parse_string(trigger).expect("Invalid shortcut trigger");
344+
let action = gtk::NamedAction::new(action);
345+
Some(gtk::Shortcut::new(Some(trigger), Some(action)))
346+
}
347+
348+
fn rebuild_shortcuts_model(&self) {
349+
let shortcuts = &self.imp().shortcuts;
350+
shortcuts.remove_all();
351+
for (trigger, action) in SHORTCUT_DEFINITIONS {
352+
if let Some(shortcut) = Self::build_shortcut(trigger, action) {
353+
shortcuts.append(&shortcut);
354+
}
355+
}
356+
}
357+
358+
pub fn shortcuts_model(&self) -> gio::ListStore {
359+
self.imp().shortcuts.clone()
360+
}
361+
362+
pub fn shortcut_action_names(&self) -> Vec<&'static str> {
363+
SHORTCUT_DEFINITIONS
364+
.iter()
365+
.map(|(_, action)| *action)
366+
.collect::<Vec<_>>()
367+
}
368+
369+
pub fn enable_shortcuts(&self) {
370+
if self.imp().shortcuts_enabled.get() {
371+
return;
372+
}
373+
374+
self.rebuild_shortcuts_model();
375+
self.imp().shortcuts_enabled.set(true);
376+
}
377+
378+
pub fn disable_shortcuts(&self) {
379+
if !self.imp().shortcuts_enabled.get() {
380+
return;
381+
}
382+
383+
self.imp().shortcuts.remove_all();
384+
self.imp().shortcuts_enabled.set(false);
385+
}
386+
318387
pub fn start_background_tasks(&self) {
319388
self.distrobox_version().refetch();
320389
self.container_runtime().refetch();
@@ -1179,4 +1248,32 @@ mod tests {
11791248
assert_eq!(returned_task, existing_task);
11801249
assert_eq!(store.tasks().iter().count(), 1);
11811250
}
1251+
1252+
#[gtk::test]
1253+
fn test_shortcuts_toggle_is_idempotent() {
1254+
let store = RootStore::new(NullCommandRunnerBuilder::new().build());
1255+
1256+
assert_eq!(
1257+
store.shortcuts_model().n_items(),
1258+
SHORTCUT_DEFINITIONS.len() as u32
1259+
);
1260+
1261+
store.enable_shortcuts();
1262+
assert_eq!(
1263+
store.shortcuts_model().n_items(),
1264+
SHORTCUT_DEFINITIONS.len() as u32
1265+
);
1266+
1267+
store.disable_shortcuts();
1268+
assert_eq!(store.shortcuts_model().n_items(), 0);
1269+
1270+
store.disable_shortcuts();
1271+
assert_eq!(store.shortcuts_model().n_items(), 0);
1272+
1273+
store.enable_shortcuts();
1274+
assert_eq!(
1275+
store.shortcuts_model().n_items(),
1276+
SHORTCUT_DEFINITIONS.len() as u32
1277+
);
1278+
}
11821279
}

src/widgets/integrated_terminal.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ impl IntegratedTerminal {
139139
}
140140
));
141141

142+
terminal.connect_notify_local(
143+
Some("has-focus"),
144+
clone!(
145+
#[weak(rename_to=this)]
146+
self,
147+
move |terminal, _| {
148+
let root_store = this.container().root_store();
149+
if terminal.has_focus() {
150+
root_store.disable_shortcuts();
151+
} else {
152+
root_store.enable_shortcuts();
153+
}
154+
}
155+
),
156+
);
157+
142158
// Reload button click handler
143159
reload_button.connect_clicked(clone!(
144160
#[weak(rename_to=this)]

src/widgets/window.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ impl DistroShelfWindow {
209209
.as_ref()
210210
{
211211
terminal.spawn_terminal();
212+
} else {
213+
this_clone.root_store().enable_shortcuts();
212214
}
213215
});
214216

0 commit comments

Comments
 (0)