[darwin] Support keybindings

This commit is contained in:
Lea Anthony 2023-09-20 20:35:35 +10:00
commit af8ee6703e
7 changed files with 221 additions and 2 deletions

View file

@ -15,7 +15,7 @@ func main() {
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
KeyBindings: map[string]func(window *application.WebviewWindow){
"CmdOrCtrl+C": func(window *application.WebviewWindow) {
"CmdOrCtrl+Shift+C": func(window *application.WebviewWindow) {
window.Center()
},
},

View file

@ -187,6 +187,13 @@ type webViewAssetRequest struct {
windowName string
}
var windowKeyEvents = make(chan *windowKeyEvent)
type windowKeyEvent struct {
windowId uint
acceleratorString string
}
func (r *webViewAssetRequest) Header() (http.Header, error) {
h, err := r.Request.Header()
if err != nil {
@ -430,6 +437,12 @@ func (a *App) Run() error {
a.handleWindowMessage(event)
}
}()
go func() {
for {
event := <-windowKeyEvents
a.handleWindowKeyEvent(event)
}
}()
go func() {
for {
dragAndDropMessage := <-windowDragAndDropBuffer
@ -742,6 +755,19 @@ func (a *App) processKeyBinding(acceleratorString string, window *WebviewWindow)
return true
}
func (a *App) handleWindowKeyEvent(event *windowKeyEvent) {
// Get window from window map
a.windowsLock.Lock()
window, ok := a.windows[event.windowId]
a.windowsLock.Unlock()
if !ok {
log.Printf("WebviewWindow #%d not found", event.windowId)
return
}
// Get callback from window
window.handleKeyEvent(event.acceleratorString)
}
func invokeSync(fn func()) {
var wg sync.WaitGroup
wg.Add(1)

View file

@ -272,6 +272,14 @@ func processURLRequest(windowID C.uint, wkUrlSchemeTask unsafe.Pointer) {
}
}
//export processWindowKeyDownEvent
func processWindowKeyDownEvent(windowID C.uint, acceleratorString *C.char) {
windowKeyEvents <- &windowKeyEvent{
windowId: uint(windowID),
acceleratorString: C.GoString(acceleratorString),
}
}
//export processDragItems
func processDragItems(windowID C.uint, arr **C.char, length C.int) {
var filenames []string

View file

@ -69,6 +69,7 @@ type (
absolutePosition() (int, int)
setAbsolutePosition(x int, y int)
flash(enabled bool)
handleKeyEvent(acceleratorString string)
}
)
@ -1065,3 +1066,12 @@ func (w *WebviewWindow) processKeyBinding(acceleratorString string) bool {
return true
}
func (w *WebviewWindow) handleKeyEvent(acceleratorString string) {
if w.impl == nil {
return
}
invokeSync(func() {
w.impl.handleKeyEvent(acceleratorString)
})
}

View file

@ -720,6 +720,16 @@ type macosWebviewWindow struct {
parent *WebviewWindow
}
func (w *macosWebviewWindow) handleKeyEvent(acceleratorString string) {
// Parse acceleratorString
accelerator, err := parseAccelerator(acceleratorString)
if err != nil {
globalApplication.error("unable to parse accelerator: %s", err.Error())
return
}
w.parent.processKeyBinding(accelerator.String())
}
func (w *macosWebviewWindow) isFocused() bool {
return bool(C.windowIsFocused(w.nsWindow))
}

View file

@ -5,6 +5,7 @@
#import "../events/events.h"
extern void processMessage(unsigned int, const char*);
extern void processURLRequest(unsigned int, void *);
extern void processWindowKeyDownEvent(unsigned int, const char*);
extern bool hasListeners(unsigned int);
@implementation WebviewWindow
- (WebviewWindow*) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation;
@ -16,8 +17,172 @@ extern bool hasListeners(unsigned int);
[self setMovableByWindowBackground:YES];
return self;
}
- (void)keyDown:(NSEvent *)event {
NSUInteger modifierFlags = event.modifierFlags;
// Create an array to hold the modifier strings
NSMutableArray *modifierStrings = [NSMutableArray array];
// Check for modifier flags and add corresponding strings to the array
if (modifierFlags & NSEventModifierFlagShift) {
[modifierStrings addObject:@"shift"];
}
if (modifierFlags & NSEventModifierFlagControl) {
[modifierStrings addObject:@"ctrl"];
}
if (modifierFlags & NSEventModifierFlagOption) {
[modifierStrings addObject:@"option"];
}
if (modifierFlags & NSEventModifierFlagCommand) {
[modifierStrings addObject:@"cmd"];
}
NSString *keyString = [self keyStringFromEvent:event];
if (keyString.length > 0) {
[modifierStrings addObject:keyString];
}
// Combine the modifier strings with the key character
NSString *keyEventString = [modifierStrings componentsJoinedByString:@"+"];
const char* utf8String = [keyEventString UTF8String];
WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)self.delegate;
processWindowKeyDownEvent(delegate.windowId, utf8String);
}
- (NSString *)keyStringFromEvent:(NSEvent *)event {
// Get the pressed key
// Check for special keys like escape and tab
NSString *characters = [event characters];
if (characters.length == 0) {
return @"";
}
if ([characters isEqualToString:@"\r"]) {
return @"enter";
}
if ([characters isEqualToString:@"\b"]) {
return @"backspace";
}
if ([characters isEqualToString:@"\e"]) {
return @"escape";
}
// page down
if ([characters isEqualToString:@"\x0B"]) {
return @"page down";
}
// page up
if ([characters isEqualToString:@"\x0E"]) {
return @"page up";
}
// home
if ([characters isEqualToString:@"\x01"]) {
return @"home";
}
// end
if ([characters isEqualToString:@"\x04"]) {
return @"end";
}
// clear
if ([characters isEqualToString:@"\x0C"]) {
return @"clear";
}
switch ([event keyCode]) {
// Function keys
case 122: return @"f1";
case 120: return @"f2";
case 99: return @"f3";
case 118: return @"f4";
case 96: return @"f5";
case 97: return @"f6";
case 98: return @"f7";
case 100: return @"f8";
case 101: return @"f9";
case 109: return @"f10";
case 103: return @"f11";
case 111: return @"f12";
case 105: return @"f13";
case 107: return @"f14";
case 113: return @"f15";
case 106: return @"f16";
case 64: return @"f17";
case 79: return @"f18";
case 80: return @"f19";
case 90: return @"f20";
// Letter keys
case 0: return @"a";
case 11: return @"b";
case 8: return @"c";
case 2: return @"d";
case 14: return @"e";
case 3: return @"f";
case 5: return @"g";
case 4: return @"h";
case 34: return @"i";
case 38: return @"j";
case 40: return @"k";
case 37: return @"l";
case 46: return @"m";
case 45: return @"n";
case 31: return @"o";
case 35: return @"p";
case 12: return @"q";
case 15: return @"r";
case 1: return @"s";
case 17: return @"t";
case 32: return @"u";
case 9: return @"v";
case 13: return @"w";
case 7: return @"x";
case 16: return @"y";
case 6: return @"z";
// Number keys
case 29: return @"0";
case 18: return @"1";
case 19: return @"2";
case 20: return @"3";
case 21: return @"4";
case 23: return @"5";
case 22: return @"6";
case 26: return @"7";
case 28: return @"8";
case 25: return @"9";
// Other special keys
case 51: return @"delete";
case 117: return @"forward delete";
case 123: return @"left";
case 124: return @"right";
case 126: return @"up";
case 125: return @"down";
case 48: return @"tab";
case 53: return @"escape";
case 49: return @"space";
// Punctuation and other keys (for a standard US layout)
case 33: return @"[";
case 30: return @"]";
case 43: return @",";
case 27: return @"-";
case 39: return @"'";
case 44: return @"/";
case 47: return @".";
case 41: return @";";
case 24: return @"=";
case 50: return @"`";
case 42: return @"\\";
default: return @"";
}
}
- (BOOL)canBecomeKeyWindow {
return YES;
}

View file

@ -1550,7 +1550,7 @@ func (w *windowsWebviewWindow) processKeyBinding(vkey uint) bool {
if len(w.parent.keyBindings) == 0 {
return false
}
// Get the keyboard state and convert to an accellerator
// Get the keyboard state and convert to an accelerator
var keyState [256]byte
if !w32.GetKeyboardState(keyState[:]) {
globalApplication.error("Error getting keyboard state")