1package webkit
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/bnema/dumber/internal/logging"
8 "github.com/diamondburned/gotk4/pkg/gdk/v4"
9 "github.com/diamondburned/gotk4/pkg/glib/v2"
10 "github.com/diamondburned/gotk4/pkg/gtk/v4"
11)
12
13// WindowShortcuts manages keyboard shortcuts for a window
14type WindowShortcuts struct {
15 window *Window
16 shortcuts map[string]*gtk.Shortcut
17 controller *gtk.ShortcutController
18}
19
20// NewWindowShortcuts creates a new WindowShortcuts manager
21func NewWindowShortcuts(win *Window) *WindowShortcuts {
22 controller := gtk.NewShortcutController()
23
24 // Set scope to GLOBAL so shortcuts work at the window level
25 // and aren't captured by child widgets (like the WebView)
26 controller.SetScope(gtk.ShortcutScopeGlobal)
27
28 if win != nil && win.win != nil {
29 win.win.AddController(controller)
30 }
31
32 return &WindowShortcuts{
33 window: win,
34 shortcuts: make(map[string]*gtk.Shortcut),
35 controller: controller,
36 }
37}
38
39// InitializeGlobalShortcuts initializes global keyboard shortcuts for a window
40func (w *Window) InitializeGlobalShortcuts(shortcuts map[string]func()) {
41 if w == nil || w.win == nil {
42 return
43 }
44
45 // Create event controller for keyboard events
46 controller := gtk.NewEventControllerKey()
47
48 controller.ConnectKeyPressed(func(keyval, keycode uint, state gdk.ModifierType) bool {
49 // Handle keyboard shortcuts
50 // TODO: Implement proper shortcut matching
51 return false
52 })
53
54 w.win.AddController(controller)
55}
56
57// RegisterShortcut registers a keyboard shortcut
58func (ws *WindowShortcuts) RegisterShortcut(accelerator string, handler func()) {
59 if ws == nil || ws.controller == nil {
60 return
61 }
62
63 // Convert to GTK format (e.g., "ctrl+plus" -> "<Control>plus")
64 gtkFormat := convertToGtkFormat(accelerator)
65 if gtkFormat == "" {
66 logging.Warn(fmt.Sprintf("[shortcuts] Invalid key format: %s", accelerator))
67 return
68 }
69
70 // Create a callback action that will trigger our handler
71 // ShortcutFunc signature: func(widget Widgetter, args *glib.Variant) (ok bool)
72 action := gtk.NewCallbackAction(func(widget gtk.Widgetter, args *glib.Variant) bool {
73 if handler != nil {
74 logging.Debug(fmt.Sprintf("[shortcuts] Shortcut triggered: %s", accelerator))
75 handler()
76 }
77 // Return false to allow event propagation to WebView
78 // This lets JavaScript KeyboardService also handle events if needed
79 return false
80 })
81
82 // Parse the accelerator string into a GDK key trigger
83 // GTK4 uses gtk.ShortcutTrigger for key combinations
84 trigger := gtk.NewShortcutTriggerParseString(gtkFormat)
85 if trigger == nil {
86 logging.Error(fmt.Sprintf("[shortcuts] Failed to parse trigger: %s", gtkFormat))
87 return
88 }
89
90 // Create the shortcut with trigger and action
91 shortcut := gtk.NewShortcut(trigger, action)
92
93 // Add shortcut to the shared controller
94 ws.controller.AddShortcut(shortcut)
95
96 // Store the shortcut for potential cleanup
97 ws.shortcuts[accelerator] = shortcut
98
99 logging.Debug(fmt.Sprintf("[shortcuts] Registered: %s -> %s", accelerator, gtkFormat))
100}
101
102// convertToGtkFormat converts common key formats to GTK shortcut format
103// Examples: "ctrl+plus" -> "<Control>plus", "ctrl+l" -> "<Control>l"
104func convertToGtkFormat(key string) string {
105 key = strings.ToLower(key)
106
107 // Handle cmdorctrl -> Control
108 key = strings.ReplaceAll(key, "cmdorctrl+", "ctrl+")
109 key = strings.ReplaceAll(key, "cmd+", "ctrl+")
110
111 // Split by + to get components
112 components := strings.Split(key, "+")
113 if len(components) == 0 {
114 return ""
115 }
116
117 var parts []string
118 for i, part := range components {
119 if i == len(components)-1 {
120 // Last part is the actual key
121 switch part {
122 case "f12":
123 parts = append(parts, "F12")
124 case "f5":
125 parts = append(parts, "F5")
126 case "plus":
127 parts = append(parts, "plus")
128 case "equal":
129 parts = append(parts, "equal")
130 case "minus":
131 parts = append(parts, "minus")
132 case "0":
133 parts = append(parts, "0")
134 case "left", "arrowleft":
135 parts = append(parts, "Left")
136 case "right", "arrowright":
137 parts = append(parts, "Right")
138 case "up", "arrowup":
139 parts = append(parts, "Up")
140 case "down", "arrowdown":
141 parts = append(parts, "Down")
142 case "tab":
143 parts = append(parts, "Tab")
144 case "escape":
145 parts = append(parts, "Escape")
146 case "return", "enter":
147 parts = append(parts, "Return")
148 default:
149 // Single letter keys like l, f, c, w, etc.
150 parts = append(parts, part)
151 }
152 } else {
153 // Modifier keys
154 switch part {
155 case "ctrl":
156 parts = append(parts, "<Control>")
157 case "shift":
158 parts = append(parts, "<Shift>")
159 case "alt":
160 parts = append(parts, "<Alt>")
161 case "super", "meta":
162 parts = append(parts, "<Super>")
163 }
164 }
165 }
166
167 return strings.Join(parts, "")
168}