fix: use GtkShortcutController for Alt+number tab switching (fixes #16)
- commit
- 5dc745a40aa0af953b5f6b7d4ef5d32a84c979d6
- author
- brice · · 2025-12-24 06:09 UTC
- committer
- brice · · 2025-12-24 16:11 UTC
- parent
- c741a39
- signature
- unsigned
4 files · +181 -13
M CHANGELOG.md +3 -0
| @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. | |||
| 4 | 4 | ||
| 5 | 5 | ## [Unreleased] | |
| 6 | 6 | ||
| 7 | + | ### Fixed | |
| 8 | + | - **Alt+number tab switching**: Fixed Alt+1-9 shortcuts not working when WebView has focus by using GtkShortcutController with global scope instead of EventControllerKey. | |
| 9 | + | ||
| 7 | 10 | ### Added | |
| 8 | 11 | - **Clean-architecture rewrite (pure Go)**: Major refactor to a ports/adapters style architecture with explicit `domain`, `application` (use cases), `infrastructure`, and `ui` layers. | |
| 9 | 12 | - **New CLI commands**: `doctor` (runtime dependency checks), `about` (build info), `setup` (desktop integration), plus expanded `purge` with an interactive TUI. | |
| @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. | |||||
| 4 | 4 | ||||
| 5 | ## [Unreleased] | 5 | ## [Unreleased] | ||
| 6 | 6 | ||||
| 7 | + | ### Fixed | |||
| 8 | + | - **Alt+number tab switching**: Fixed Alt+1-9 shortcuts not working when WebView has focus by using GtkShortcutController with global scope instead of EventControllerKey. | |||
| 9 | + | ||||
| 7 | ### Added | 10 | ### Added | ||
| 8 | - **Clean-architecture rewrite (pure Go)**: Major refactor to a ports/adapters style architecture with explicit `domain`, `application` (use cases), `infrastructure`, and `ui` layers. | 11 | - **Clean-architecture rewrite (pure Go)**: Major refactor to a ports/adapters style architecture with explicit `domain`, `application` (use cases), `infrastructure`, and `ui` layers. | ||
| 9 | - **New CLI commands**: `doctor` (runtime dependency checks), `about` (build info), `setup` (desktop integration), plus expanded `purge` with an interactive TUI. | 12 | - **New CLI commands**: `doctor` (runtime dependency checks), `about` (build info), `setup` (desktop integration), plus expanded `purge` with an interactive TUI. | ||
M internal/ui/app.go +13 -1
| @@ -61,7 +61,8 @@ type App struct { | |||
| 61 | 61 | stackedPaneMgr *component.StackedPaneManager | |
| 62 | 62 | ||
| 63 | 63 | // Input handling | |
| 64 | - | keyboardHandler *input.KeyboardHandler | |
| 64 | + | keyboardHandler *input.KeyboardHandler | |
| 65 | + | globalShortcutHandler *input.GlobalShortcutHandler | |
| 65 | 66 | ||
| 66 | 67 | // Focus and border management | |
| 67 | 68 | focusMgr *focus.Manager | |
| @@ -305,6 +306,17 @@ func (a *App) initKeyboardHandler(ctx context.Context) { | |||
| 305 | 306 | return wsView.IsOmniboxVisible() | |
| 306 | 307 | }) | |
| 307 | 308 | a.keyboardHandler.AttachTo(a.mainWindow.Window()) | |
| 309 | + | ||
| 310 | + | // Create global shortcut handler for Alt+1-9 tab switching. | |
| 311 | + | // This uses GtkShortcutController with GTK_SHORTCUT_SCOPE_GLOBAL | |
| 312 | + | // to intercept shortcuts even when WebView has focus. | |
| 313 | + | a.globalShortcutHandler = input.NewGlobalShortcutHandler( | |
| 314 | + | ctx, | |
| 315 | + | a.mainWindow.Window(), | |
| 316 | + | func(ctx context.Context, action input.Action) error { | |
| 317 | + | return a.kbDispatcher.Dispatch(ctx, action) | |
| 318 | + | }, | |
| 319 | + | ) | |
| 308 | 320 | } | |
| 309 | 321 | ||
| 310 | 322 | func (a *App) initOmniboxConfig(ctx context.Context) { | |
| @@ -61,7 +61,8 @@ type App struct { | |||||
| 61 | stackedPaneMgr *component.StackedPaneManager | 61 | stackedPaneMgr *component.StackedPaneManager | ||
| 62 | 62 | ||||
| 63 | // Input handling | 63 | // Input handling | ||
| 64 | - | keyboardHandler *input.KeyboardHandler | 64 | + | keyboardHandler *input.KeyboardHandler |
| 65 | + | globalShortcutHandler *input.GlobalShortcutHandler | |||
| 65 | 66 | ||||
| 66 | // Focus and border management | 67 | // Focus and border management | ||
| 67 | focusMgr *focus.Manager | 68 | focusMgr *focus.Manager | ||
| @@ -305,6 +306,17 @@ func (a *App) initKeyboardHandler(ctx context.Context) { | |||||
| 305 | return wsView.IsOmniboxVisible() | 306 | return wsView.IsOmniboxVisible() | ||
| 306 | }) | 307 | }) | ||
| 307 | a.keyboardHandler.AttachTo(a.mainWindow.Window()) | 308 | a.keyboardHandler.AttachTo(a.mainWindow.Window()) | ||
| 309 | + | ||||
| 310 | + | // Create global shortcut handler for Alt+1-9 tab switching. | |||
| 311 | + | // This uses GtkShortcutController with GTK_SHORTCUT_SCOPE_GLOBAL | |||
| 312 | + | // to intercept shortcuts even when WebView has focus. | |||
| 313 | + | a.globalShortcutHandler = input.NewGlobalShortcutHandler( | |||
| 314 | + | ctx, | |||
| 315 | + | a.mainWindow.Window(), | |||
| 316 | + | func(ctx context.Context, action input.Action) error { | |||
| 317 | + | return a.kbDispatcher.Dispatch(ctx, action) | |||
| 318 | + | }, | |||
| 319 | + | ) | |||
| 308 | } | 320 | } | ||
| 309 | 321 | ||||
| 310 | func (a *App) initOmniboxConfig(ctx context.Context) { | 322 | func (a *App) initOmniboxConfig(ctx context.Context) { | ||
A internal/ui/input/global_shortcuts.go +159 -0
| @@ -0,0 +1,159 @@ | |||
| 1 | + | // Package input provides keyboard event handling and modal input mode management. | |
| 2 | + | package input | |
| 3 | + | ||
| 4 | + | import ( | |
| 5 | + | "context" | |
| 6 | + | ||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |
| 8 | + | "github.com/jwijenbergh/puregotk/v4/gdk" | |
| 9 | + | "github.com/jwijenbergh/puregotk/v4/glib" | |
| 10 | + | "github.com/jwijenbergh/puregotk/v4/gtk" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | // GlobalShortcutHandler manages keyboard shortcuts that must work globally, | |
| 14 | + | // even when WebView has focus. It uses GtkShortcutController with GTK_SHORTCUT_SCOPE_GLOBAL | |
| 15 | + | // to intercept shortcuts before they reach the WebView. | |
| 16 | + | type GlobalShortcutHandler struct { | |
| 17 | + | controller *gtk.ShortcutController | |
| 18 | + | onAction ActionHandler | |
| 19 | + | ctx context.Context | |
| 20 | + | ||
| 21 | + | // Keep references to callbacks to prevent GC from collecting them | |
| 22 | + | callbacks []gtk.ShortcutFunc | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // NewGlobalShortcutHandler creates a new global shortcut handler for shortcuts | |
| 26 | + | // that need to work even when WebView has focus (like Alt+1-9 for tab switching). | |
| 27 | + | func NewGlobalShortcutHandler( | |
| 28 | + | ctx context.Context, | |
| 29 | + | window *gtk.ApplicationWindow, | |
| 30 | + | onAction ActionHandler, | |
| 31 | + | ) *GlobalShortcutHandler { | |
| 32 | + | log := logging.FromContext(ctx) | |
| 33 | + | log.Debug().Msg("creating global shortcut handler") | |
| 34 | + | ||
| 35 | + | h := &GlobalShortcutHandler{ | |
| 36 | + | controller: gtk.NewShortcutController(), | |
| 37 | + | onAction: onAction, | |
| 38 | + | ctx: ctx, | |
| 39 | + | callbacks: make([]gtk.ShortcutFunc, 0), | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | if h.controller == nil { | |
| 43 | + | log.Error().Msg("failed to create shortcut controller") | |
| 44 | + | return nil | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | // Set global scope - this is the key to making shortcuts work | |
| 48 | + | // even when WebView has focus | |
| 49 | + | h.controller.SetScope(gtk.ShortcutScopeGlobalValue) | |
| 50 | + | ||
| 51 | + | // Register Alt+1 through Alt+9 for tab switching | |
| 52 | + | tabActions := []Action{ | |
| 53 | + | ActionSwitchTabIndex1, | |
| 54 | + | ActionSwitchTabIndex2, | |
| 55 | + | ActionSwitchTabIndex3, | |
| 56 | + | ActionSwitchTabIndex4, | |
| 57 | + | ActionSwitchTabIndex5, | |
| 58 | + | ActionSwitchTabIndex6, | |
| 59 | + | ActionSwitchTabIndex7, | |
| 60 | + | ActionSwitchTabIndex8, | |
| 61 | + | ActionSwitchTabIndex9, | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | for i, action := range tabActions { | |
| 65 | + | keyval := uint(gdk.KEY_1) + uint(i) // KEY_1, KEY_2, ..., KEY_9 | |
| 66 | + | h.registerShortcut(keyval, gdk.AltMaskValue, action) | |
| 67 | + | log.Trace(). | |
| 68 | + | Uint("keyval", keyval). | |
| 69 | + | Str("action", string(action)). | |
| 70 | + | Msg("registered global shortcut") | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | // Alt+0 for tab 10 | |
| 74 | + | h.registerShortcut(uint(gdk.KEY_0), gdk.AltMaskValue, ActionSwitchTabIndex10) | |
| 75 | + | log.Trace(). | |
| 76 | + | Uint("keyval", uint(gdk.KEY_0)). | |
| 77 | + | Str("action", string(ActionSwitchTabIndex10)). | |
| 78 | + | Msg("registered global shortcut") | |
| 79 | + | ||
| 80 | + | // Alt+Tab for switching to last active tab | |
| 81 | + | h.registerShortcut(uint(gdk.KEY_Tab), gdk.AltMaskValue, ActionSwitchLastTab) | |
| 82 | + | log.Trace(). | |
| 83 | + | Uint("keyval", uint(gdk.KEY_Tab)). | |
| 84 | + | Str("action", string(ActionSwitchLastTab)). | |
| 85 | + | Msg("registered global shortcut") | |
| 86 | + | ||
| 87 | + | // Attach to window | |
| 88 | + | window.AddController(&h.controller.EventController) | |
| 89 | + | ||
| 90 | + | log.Debug(). | |
| 91 | + | Int("shortcuts", len(h.callbacks)). | |
| 92 | + | Msg("global shortcut handler created and attached") | |
| 93 | + | ||
| 94 | + | return h | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | // registerShortcut creates and registers a single shortcut with the controller. | |
| 98 | + | func (h *GlobalShortcutHandler) registerShortcut(keyval uint, modifiers gdk.ModifierType, action Action) { | |
| 99 | + | // Create trigger for this key combination | |
| 100 | + | trigger := gtk.NewKeyvalTrigger(keyval, modifiers) | |
| 101 | + | if trigger == nil { | |
| 102 | + | logging.FromContext(h.ctx).Error(). | |
| 103 | + | Uint("keyval", keyval). | |
| 104 | + | Msg("failed to create keyval trigger") | |
| 105 | + | return | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | // Create callback action | |
| 109 | + | // We need to capture the action in the closure | |
| 110 | + | actionToDispatch := action | |
| 111 | + | callback := gtk.ShortcutFunc(func(_ uintptr, _ *glib.Variant, _ uintptr) bool { | |
| 112 | + | log := logging.FromContext(h.ctx) | |
| 113 | + | log.Debug(). | |
| 114 | + | Str("action", string(actionToDispatch)). | |
| 115 | + | Msg("global shortcut triggered") | |
| 116 | + | ||
| 117 | + | if h.onAction != nil { | |
| 118 | + | if err := h.onAction(h.ctx, actionToDispatch); err != nil { | |
| 119 | + | log.Error(). | |
| 120 | + | Err(err). | |
| 121 | + | Str("action", string(actionToDispatch)). | |
| 122 | + | Msg("global shortcut action failed") | |
| 123 | + | } | |
| 124 | + | } | |
| 125 | + | return true // Event consumed | |
| 126 | + | }) | |
| 127 | + | ||
| 128 | + | // Store callback reference to prevent GC | |
| 129 | + | h.callbacks = append(h.callbacks, callback) | |
| 130 | + | ||
| 131 | + | // Create the shortcut action | |
| 132 | + | shortcutAction := gtk.NewCallbackAction(&callback, 0, nil) | |
| 133 | + | if shortcutAction == nil { | |
| 134 | + | logging.FromContext(h.ctx).Error(). | |
| 135 | + | Uint("keyval", keyval). | |
| 136 | + | Msg("failed to create callback action") | |
| 137 | + | return | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | // Create the shortcut combining trigger and action | |
| 141 | + | shortcut := gtk.NewShortcut(&trigger.ShortcutTrigger, &shortcutAction.ShortcutAction) | |
| 142 | + | if shortcut == nil { | |
| 143 | + | logging.FromContext(h.ctx).Error(). | |
| 144 | + | Uint("keyval", keyval). | |
| 145 | + | Msg("failed to create shortcut") | |
| 146 | + | return | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | // Add to controller | |
| 150 | + | h.controller.AddShortcut(shortcut) | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | // Detach removes the global shortcut handler from the window. | |
| 154 | + | // Note: GTK handles cleanup when the widget is destroyed, | |
| 155 | + | // but we clear our references here. | |
| 156 | + | func (h *GlobalShortcutHandler) Detach() { | |
| 157 | + | h.controller = nil | |
| 158 | + | h.callbacks = nil | |
| 159 | + | } | |
| @@ -0,0 +1,159 @@ | |||||
| 1 | + | // Package input provides keyboard event handling and modal input mode management. | |||
| 2 | + | package input | |||
| 3 | + | ||||
| 4 | + | import ( | |||
| 5 | + | "context" | |||
| 6 | + | ||||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |||
| 8 | + | "github.com/jwijenbergh/puregotk/v4/gdk" | |||
| 9 | + | "github.com/jwijenbergh/puregotk/v4/glib" | |||
| 10 | + | "github.com/jwijenbergh/puregotk/v4/gtk" | |||
| 11 | + | ) | |||
| 12 | + | ||||
| 13 | + | // GlobalShortcutHandler manages keyboard shortcuts that must work globally, | |||
| 14 | + | // even when WebView has focus. It uses GtkShortcutController with GTK_SHORTCUT_SCOPE_GLOBAL | |||
| 15 | + | // to intercept shortcuts before they reach the WebView. | |||
| 16 | + | type GlobalShortcutHandler struct { | |||
| 17 | + | controller *gtk.ShortcutController | |||
| 18 | + | onAction ActionHandler | |||
| 19 | + | ctx context.Context | |||
| 20 | + | ||||
| 21 | + | // Keep references to callbacks to prevent GC from collecting them | |||
| 22 | + | callbacks []gtk.ShortcutFunc | |||
| 23 | + | } | |||
| 24 | + | ||||
| 25 | + | // NewGlobalShortcutHandler creates a new global shortcut handler for shortcuts | |||
| 26 | + | // that need to work even when WebView has focus (like Alt+1-9 for tab switching). | |||
| 27 | + | func NewGlobalShortcutHandler( | |||
| 28 | + | ctx context.Context, | |||
| 29 | + | window *gtk.ApplicationWindow, | |||
| 30 | + | onAction ActionHandler, | |||
| 31 | + | ) *GlobalShortcutHandler { | |||
| 32 | + | log := logging.FromContext(ctx) | |||
| 33 | + | log.Debug().Msg("creating global shortcut handler") | |||
| 34 | + | ||||
| 35 | + | h := &GlobalShortcutHandler{ | |||
| 36 | + | controller: gtk.NewShortcutController(), | |||
| 37 | + | onAction: onAction, | |||
| 38 | + | ctx: ctx, | |||
| 39 | + | callbacks: make([]gtk.ShortcutFunc, 0), | |||
| 40 | + | } | |||
| 41 | + | ||||
| 42 | + | if h.controller == nil { | |||
| 43 | + | log.Error().Msg("failed to create shortcut controller") | |||
| 44 | + | return nil | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | // Set global scope - this is the key to making shortcuts work | |||
| 48 | + | // even when WebView has focus | |||
| 49 | + | h.controller.SetScope(gtk.ShortcutScopeGlobalValue) | |||
| 50 | + | ||||
| 51 | + | // Register Alt+1 through Alt+9 for tab switching | |||
| 52 | + | tabActions := []Action{ | |||
| 53 | + | ActionSwitchTabIndex1, | |||
| 54 | + | ActionSwitchTabIndex2, | |||
| 55 | + | ActionSwitchTabIndex3, | |||
| 56 | + | ActionSwitchTabIndex4, | |||
| 57 | + | ActionSwitchTabIndex5, | |||
| 58 | + | ActionSwitchTabIndex6, | |||
| 59 | + | ActionSwitchTabIndex7, | |||
| 60 | + | ActionSwitchTabIndex8, | |||
| 61 | + | ActionSwitchTabIndex9, | |||
| 62 | + | } | |||
| 63 | + | ||||
| 64 | + | for i, action := range tabActions { | |||
| 65 | + | keyval := uint(gdk.KEY_1) + uint(i) // KEY_1, KEY_2, ..., KEY_9 | |||
| 66 | + | h.registerShortcut(keyval, gdk.AltMaskValue, action) | |||
| 67 | + | log.Trace(). | |||
| 68 | + | Uint("keyval", keyval). | |||
| 69 | + | Str("action", string(action)). | |||
| 70 | + | Msg("registered global shortcut") | |||
| 71 | + | } | |||
| 72 | + | ||||
| 73 | + | // Alt+0 for tab 10 | |||
| 74 | + | h.registerShortcut(uint(gdk.KEY_0), gdk.AltMaskValue, ActionSwitchTabIndex10) | |||
| 75 | + | log.Trace(). | |||
| 76 | + | Uint("keyval", uint(gdk.KEY_0)). | |||
| 77 | + | Str("action", string(ActionSwitchTabIndex10)). | |||
| 78 | + | Msg("registered global shortcut") | |||
| 79 | + | ||||
| 80 | + | // Alt+Tab for switching to last active tab | |||
| 81 | + | h.registerShortcut(uint(gdk.KEY_Tab), gdk.AltMaskValue, ActionSwitchLastTab) | |||
| 82 | + | log.Trace(). | |||
| 83 | + | Uint("keyval", uint(gdk.KEY_Tab)). | |||
| 84 | + | Str("action", string(ActionSwitchLastTab)). | |||
| 85 | + | Msg("registered global shortcut") | |||
| 86 | + | ||||
| 87 | + | // Attach to window | |||
| 88 | + | window.AddController(&h.controller.EventController) | |||
| 89 | + | ||||
| 90 | + | log.Debug(). | |||
| 91 | + | Int("shortcuts", len(h.callbacks)). | |||
| 92 | + | Msg("global shortcut handler created and attached") | |||
| 93 | + | ||||
| 94 | + | return h | |||
| 95 | + | } | |||
| 96 | + | ||||
| 97 | + | // registerShortcut creates and registers a single shortcut with the controller. | |||
| 98 | + | func (h *GlobalShortcutHandler) registerShortcut(keyval uint, modifiers gdk.ModifierType, action Action) { | |||
| 99 | + | // Create trigger for this key combination | |||
| 100 | + | trigger := gtk.NewKeyvalTrigger(keyval, modifiers) | |||
| 101 | + | if trigger == nil { | |||
| 102 | + | logging.FromContext(h.ctx).Error(). | |||
| 103 | + | Uint("keyval", keyval). | |||
| 104 | + | Msg("failed to create keyval trigger") | |||
| 105 | + | return | |||
| 106 | + | } | |||
| 107 | + | ||||
| 108 | + | // Create callback action | |||
| 109 | + | // We need to capture the action in the closure | |||
| 110 | + | actionToDispatch := action | |||
| 111 | + | callback := gtk.ShortcutFunc(func(_ uintptr, _ *glib.Variant, _ uintptr) bool { | |||
| 112 | + | log := logging.FromContext(h.ctx) | |||
| 113 | + | log.Debug(). | |||
| 114 | + | Str("action", string(actionToDispatch)). | |||
| 115 | + | Msg("global shortcut triggered") | |||
| 116 | + | ||||
| 117 | + | if h.onAction != nil { | |||
| 118 | + | if err := h.onAction(h.ctx, actionToDispatch); err != nil { | |||
| 119 | + | log.Error(). | |||
| 120 | + | Err(err). | |||
| 121 | + | Str("action", string(actionToDispatch)). | |||
| 122 | + | Msg("global shortcut action failed") | |||
| 123 | + | } | |||
| 124 | + | } | |||
| 125 | + | return true // Event consumed | |||
| 126 | + | }) | |||
| 127 | + | ||||
| 128 | + | // Store callback reference to prevent GC | |||
| 129 | + | h.callbacks = append(h.callbacks, callback) | |||
| 130 | + | ||||
| 131 | + | // Create the shortcut action | |||
| 132 | + | shortcutAction := gtk.NewCallbackAction(&callback, 0, nil) | |||
| 133 | + | if shortcutAction == nil { | |||
| 134 | + | logging.FromContext(h.ctx).Error(). | |||
| 135 | + | Uint("keyval", keyval). | |||
| 136 | + | Msg("failed to create callback action") | |||
| 137 | + | return | |||
| 138 | + | } | |||
| 139 | + | ||||
| 140 | + | // Create the shortcut combining trigger and action | |||
| 141 | + | shortcut := gtk.NewShortcut(&trigger.ShortcutTrigger, &shortcutAction.ShortcutAction) | |||
| 142 | + | if shortcut == nil { | |||
| 143 | + | logging.FromContext(h.ctx).Error(). | |||
| 144 | + | Uint("keyval", keyval). | |||
| 145 | + | Msg("failed to create shortcut") | |||
| 146 | + | return | |||
| 147 | + | } | |||
| 148 | + | ||||
| 149 | + | // Add to controller | |||
| 150 | + | h.controller.AddShortcut(shortcut) | |||
| 151 | + | } | |||
| 152 | + | ||||
| 153 | + | // Detach removes the global shortcut handler from the window. | |||
| 154 | + | // Note: GTK handles cleanup when the widget is destroyed, | |||
| 155 | + | // but we clear our references here. | |||
| 156 | + | func (h *GlobalShortcutHandler) Detach() { | |||
| 157 | + | h.controller = nil | |||
| 158 | + | h.callbacks = nil | |||
| 159 | + | } | |||
M internal/ui/input/shortcuts.go +6 -12
| @@ -291,18 +291,12 @@ func (s *ShortcutSet) registerPaneNavigationShortcuts() { | |||
| 291 | 291 | } | |
| 292 | 292 | ||
| 293 | 293 | func (s *ShortcutSet) registerTabSwitchShortcuts() { | |
| 294 | - | s.Global[KeyBinding{uint(gdk.KEY_1), ModAlt}] = ActionSwitchTabIndex1 | |
| 295 | - | s.Global[KeyBinding{uint(gdk.KEY_2), ModAlt}] = ActionSwitchTabIndex2 | |
| 296 | - | s.Global[KeyBinding{uint(gdk.KEY_3), ModAlt}] = ActionSwitchTabIndex3 | |
| 297 | - | s.Global[KeyBinding{uint(gdk.KEY_4), ModAlt}] = ActionSwitchTabIndex4 | |
| 298 | - | s.Global[KeyBinding{uint(gdk.KEY_5), ModAlt}] = ActionSwitchTabIndex5 | |
| 299 | - | s.Global[KeyBinding{uint(gdk.KEY_6), ModAlt}] = ActionSwitchTabIndex6 | |
| 300 | - | s.Global[KeyBinding{uint(gdk.KEY_7), ModAlt}] = ActionSwitchTabIndex7 | |
| 301 | - | s.Global[KeyBinding{uint(gdk.KEY_8), ModAlt}] = ActionSwitchTabIndex8 | |
| 302 | - | s.Global[KeyBinding{uint(gdk.KEY_9), ModAlt}] = ActionSwitchTabIndex9 | |
| 303 | - | s.Global[KeyBinding{uint(gdk.KEY_0), ModAlt}] = ActionSwitchTabIndex10 | |
| 304 | - | ||
| 305 | - | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt}] = ActionSwitchLastTab | |
| 294 | + | // NOTE: Alt+1-9, Alt+0, and Alt+Tab are now handled by GlobalShortcutHandler | |
| 295 | + | // using GtkShortcutController with GTK_SHORTCUT_SCOPE_GLOBAL. | |
| 296 | + | // This is necessary because WebKitGTK's WebView consumes these key events | |
| 297 | + | // before they reach the EventControllerKey in capture phase. | |
| 298 | + | // | |
| 299 | + | // Only Alt+Shift+Tab remains here as a fallback binding. | |
| 306 | 300 | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt | ModShift}] = ActionSwitchLastTab | |
| 307 | 301 | } | |
| 308 | 302 | ||
| @@ -291,18 +291,12 @@ func (s *ShortcutSet) registerPaneNavigationShortcuts() { | |||||
| 291 | } | 291 | } | ||
| 292 | 292 | ||||
| 293 | func (s *ShortcutSet) registerTabSwitchShortcuts() { | 293 | func (s *ShortcutSet) registerTabSwitchShortcuts() { | ||
| 294 | - | s.Global[KeyBinding{uint(gdk.KEY_1), ModAlt}] = ActionSwitchTabIndex1 | 294 | + | // NOTE: Alt+1-9, Alt+0, and Alt+Tab are now handled by GlobalShortcutHandler |
| 295 | - | s.Global[KeyBinding{uint(gdk.KEY_2), ModAlt}] = ActionSwitchTabIndex2 | 295 | + | // using GtkShortcutController with GTK_SHORTCUT_SCOPE_GLOBAL. |
| 296 | - | s.Global[KeyBinding{uint(gdk.KEY_3), ModAlt}] = ActionSwitchTabIndex3 | 296 | + | // This is necessary because WebKitGTK's WebView consumes these key events |
| 297 | - | s.Global[KeyBinding{uint(gdk.KEY_4), ModAlt}] = ActionSwitchTabIndex4 | 297 | + | // before they reach the EventControllerKey in capture phase. |
| 298 | - | s.Global[KeyBinding{uint(gdk.KEY_5), ModAlt}] = ActionSwitchTabIndex5 | 298 | + | // |
| 299 | - | s.Global[KeyBinding{uint(gdk.KEY_6), ModAlt}] = ActionSwitchTabIndex6 | 299 | + | // Only Alt+Shift+Tab remains here as a fallback binding. |
| 300 | - | s.Global[KeyBinding{uint(gdk.KEY_7), ModAlt}] = ActionSwitchTabIndex7 | |||
| 301 | - | s.Global[KeyBinding{uint(gdk.KEY_8), ModAlt}] = ActionSwitchTabIndex8 | |||
| 302 | - | s.Global[KeyBinding{uint(gdk.KEY_9), ModAlt}] = ActionSwitchTabIndex9 | |||
| 303 | - | s.Global[KeyBinding{uint(gdk.KEY_0), ModAlt}] = ActionSwitchTabIndex10 | |||
| 304 | - | ||||
| 305 | - | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt}] = ActionSwitchLastTab | |||
| 306 | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt | ModShift}] = ActionSwitchLastTab | 300 | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt | ModShift}] = ActionSwitchLastTab | ||
| 307 | } | 301 | } | ||
| 308 | 302 | ||||