From f6517a56a6e4d92468dfdda1656e80d57f302816 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 20 Jul 2026 16:15:08 +0300 Subject: shortcuts: GLOBAL scope so t/Esc reach window actions (2u0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GtkShortcutController on the window used MANAGED scope, so the viewer's own GtkEventControllerKey consumed key events before the window-level win.* shortcuts were consulted — t (toggle-view) and Esc (back) never fired. Switch to GLOBAL scope, which registers the shortcuts with the toplevel's global shortcut manager consulted before child key controllers. Comment notes the future-text-entry caveat. New integration test tests/test_shortcut.c (3 subtests): - controller_scope: asserts the window's shortcut controller is GLOBAL (and is ours, not GtkApplicationWindow's mnemonic controller). - keypath_toggle_and_back: verifies the t→win.toggle-view and Escape→win.back bindings (bidirectional keyval/action check) and dispatches them via gtk_shortcut_action_activate (the controller's own dispatch primitive), asserting the stack flips large↔grid. GTK 4.22 exposes no public GdkKeyEvent synthesis API, so this is the most faithful exercise of the keyboard shortcut path available. - full_table_registered: asserts all 16 win.* actions and all 20 shortcut rows are registered, guarding against table regressions. Notes a pre-existing, out-of-scope keybinding conflict: GTK4 normalizes GDK_KEY_G/0 to GDK_KEY_g/0 in GtkKeyvalTrigger, so win.last (G) collides with win.first (g); left unfixed per task's 'preserve existing behavior'. --- src/shortcuts.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/shortcuts.c b/src/shortcuts.c index a23cfe2..0c737bd 100644 --- a/src/shortcuts.c +++ b/src/shortcuts.c @@ -47,8 +47,16 @@ void shortcuts_install(GtkWidget *p_widget) { g_return_if_fail(GTK_IS_WIDGET(p_widget)); GtkEventController *p_ctrl = gtk_shortcut_controller_new(); + /* GLOBAL scope: the viewer installs its own GtkEventControllerKey that + * consumes key events before a MANAGED-scope window controller would see + * them. GLOBAL-scope shortcuts are consulted for every key event at the + * toplevel first, so the win.* bindings fire regardless of which child has + * focus. Note: this is the right scope while the app has no text-entry + * widgets; if a search entry / settings text field is added later, bare + * letter shortcuts (h/l/g/o/d/u/t/f/i/...) would intercept typing, and the + * dispatch will need to skip editable/IM-context focus or revisit scope. */ gtk_shortcut_controller_set_scope(GTK_SHORTCUT_CONTROLLER(p_ctrl), - GTK_SHORTCUT_SCOPE_MANAGED); + GTK_SHORTCUT_SCOPE_GLOBAL); for (gsize u_i = 0; u_i < G_N_ELEMENTS(SHORTCUTS); u_i++) { GtkShortcut *p_s = gtk_shortcut_new(GTK_SHORTCUT_TRIGGER(gtk_keyval_trigger_new( -- cgit v1.2.3