v0.9.0 636c44c

window_shortcuts.go RawHistoryFolder

1//go:build webkit_cgo
2
3package webkit
4
5import (
6	"log"
7	"strings"
8	"unsafe"
9)
10
11// No C imports needed - using Go wrapper functions from shortcuts_cgo.go
12
13// WindowShortcuts manages global shortcuts at the window level
14type WindowShortcuts struct {
15	window     *Window
16	controller uintptr // *C.GtkShortcutController
17	callbacks  map[string]uintptr
18}
19
20// InitializeGlobalShortcuts sets up the GTK4 global shortcut controller
21func (w *Window) InitializeGlobalShortcuts() *WindowShortcuts {
22	if w.win == nil {
23		log.Printf("[window-shortcuts] Cannot initialize - invalid window")
24		return nil
25	}
26
27	controller := CreateGlobalShortcutController(uintptr(unsafe.Pointer(w.win)))
28	if controller == 0 {
29		log.Printf("[window-shortcuts] Failed to create shortcut controller")
30		return nil
31	}
32
33	ws := &WindowShortcuts{
34		window:     w,
35		controller: controller,
36		callbacks:  make(map[string]uintptr),
37	}
38
39	log.Printf("[window-shortcuts] Global shortcut controller initialized")
40	return ws
41}
42
43// RegisterGlobalShortcut registers a shortcut at the window level with global scope
44func (ws *WindowShortcuts) RegisterGlobalShortcut(key string, callback func()) error {
45	if ws == nil || ws.controller == 0 {
46		return ErrNotImplemented
47	}
48
49	// Convert key format (e.g., "ctrl+l" -> "<Control>l")
50	gtkKey := convertToGtkFormat(key)
51	if gtkKey == "" {
52		log.Printf("[window-shortcuts] Invalid key format: %s", key)
53		return ErrNotImplemented
54	}
55
56	// Register callback and get handle
57	handle := registerWindowShortcutCallback(callback)
58	if handle == 0 {
59		log.Printf("[window-shortcuts] Failed to register callback for %s", key)
60		return ErrNotImplemented
61	}
62
63	// Store handle for cleanup
64	ws.callbacks[key] = handle
65
66	// Register with GTK using Go wrapper function
67	if err := AddShortcutToController(ws.controller, gtkKey, handle); err != nil {
68		log.Printf("[window-shortcuts] Failed to add shortcut to controller: %v", err)
69		unregisterWindowShortcutCallback(handle)
70		delete(ws.callbacks, key)
71		return err
72	}
73
74	log.Printf("[window-shortcuts] Registered global shortcut: %s -> %s", key, gtkKey)
75	return nil
76}
77
78// Cleanup unregisters all shortcuts and cleans up resources
79func (ws *WindowShortcuts) Cleanup() {
80	if ws == nil {
81		return
82	}
83
84	// Unregister all callbacks
85	for key, handle := range ws.callbacks {
86		unregisterWindowShortcutCallback(handle)
87		log.Printf("[window-shortcuts] Unregistered shortcut: %s", key)
88	}
89
90	ws.callbacks = nil
91	ws.controller = 0
92}
93
94// convertToGtkFormat converts common key formats to GTK shortcut format
95func convertToGtkFormat(key string) string {
96	// Convert from electron/webkit format to GTK format
97	key = strings.ToLower(key)
98
99	// Handle cmdorctrl -> Control
100	key = strings.ReplaceAll(key, "cmdorctrl+", "ctrl+")
101	key = strings.ReplaceAll(key, "cmd+", "ctrl+")
102
103	// Build GTK format
104	var parts []string
105	components := strings.Split(key, "+")
106
107	for i, part := range components {
108		if i == len(components)-1 {
109			// Last part is the key
110			switch part {
111			case "f12":
112				parts = append(parts, "F12")
113			case "f5":
114				parts = append(parts, "F5")
115			case "l":
116				parts = append(parts, "l")
117			case "f":
118				parts = append(parts, "f")
119			case "r":
120				parts = append(parts, "r")
121			case "c":
122				parts = append(parts, "c")
123			case "left", "arrowleft":
124				parts = append(parts, "Left")
125			case "right", "arrowright":
126				parts = append(parts, "Right")
127			case "up", "arrowup":
128				parts = append(parts, "Up")
129			case "down", "arrowdown":
130				parts = append(parts, "Down")
131			default:
132				parts = append(parts, part)
133			}
134		} else {
135			// Modifier
136			switch part {
137			case "ctrl":
138				parts = append(parts, "<Control>")
139			case "shift":
140				parts = append(parts, "<Shift>")
141			case "alt":
142				parts = append(parts, "<Alt>")
143			case "super":
144				parts = append(parts, "<Super>")
145			}
146		}
147	}
148
149	return strings.Join(parts, "")
150}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close