diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index 8b243dadc..b7fe6abfc 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -23,6 +23,72 @@ struct WebviewPreferences { extern void registerListener(unsigned int event); +// Shared helper to configure webview for a window or panel +static WKWebView* configureWebviewForWindow(NSWindow* window, NSView* view, WebviewWindowDelegate* delegate, + int width, int height, bool fraudulentWebsiteWarningEnabled, + bool enableDragAndDrop, struct WebviewPreferences preferences) { + NSRect frame = NSMakeRect(0, 0, width, height); + WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init]; + [config autorelease]; + + if (preferences.TabFocusesLinks != NULL) { + config.preferences.tabFocusesLinks = *preferences.TabFocusesLinks; + } + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110300 + if (@available(macOS 11.3, *)) { + if (preferences.TextInteractionEnabled != NULL) { + config.preferences.textInteractionEnabled = *preferences.TextInteractionEnabled; + } + } +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 120300 + if (@available(macOS 12.3, *)) { + if (preferences.FullscreenEnabled != NULL) { + config.preferences.elementFullscreenEnabled = *preferences.FullscreenEnabled; + } + } +#endif + + config.suppressesIncrementalRendering = true; + config.applicationNameForUserAgent = @"wails.io"; + [config setURLSchemeHandler:delegate forURLScheme:@"wails"]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 + if (@available(macOS 10.15, *)) { + config.preferences.fraudulentWebsiteWarningEnabled = fraudulentWebsiteWarningEnabled; + } +#endif + + WKUserContentController* userContentController = [WKUserContentController new]; + [userContentController autorelease]; + [userContentController addScriptMessageHandler:delegate name:@"external"]; + config.userContentController = userContentController; + + WKWebView* webView = [[WKWebView alloc] initWithFrame:frame configuration:config]; + [webView autorelease]; + + if (preferences.AllowsBackForwardNavigationGestures != NULL) { + webView.allowsBackForwardNavigationGestures = *preferences.AllowsBackForwardNavigationGestures; + } + + [view addSubview:webView]; + [webView setNavigationDelegate:delegate]; + [webView setUIDelegate:delegate]; + [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + if (enableDragAndDrop) { + WebviewDrag* dragView = [[WebviewDrag alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; + [dragView autorelease]; + [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [view addSubview:dragView]; + dragView.windowId = delegate.windowId; + } + + return webView; +} + // Create a new Window void* windowNew(unsigned int id, int width, int height, bool fraudulentWebsiteWarningEnabled, bool frameless, bool enableDragAndDrop, struct WebviewPreferences preferences) { NSWindowStyleMask styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; @@ -56,76 +122,9 @@ void* windowNew(unsigned int id, int width, int height, bool fraudulentWebsiteWa } [window setContentView:view]; - // Embed wkwebview in window - NSRect frame = NSMakeRect(0, 0, width, height); - WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init]; - [config autorelease]; - - // Set preferences - if (preferences.TabFocusesLinks != NULL) { - config.preferences.tabFocusesLinks = *preferences.TabFocusesLinks; - } - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110300 - if (@available(macOS 11.3, *)) { - if (preferences.TextInteractionEnabled != NULL) { - config.preferences.textInteractionEnabled = *preferences.TextInteractionEnabled; - } - } -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 120300 - if (@available(macOS 12.3, *)) { - if (preferences.FullscreenEnabled != NULL) { - config.preferences.elementFullscreenEnabled = *preferences.FullscreenEnabled; - } - } -#endif - - config.suppressesIncrementalRendering = true; - config.applicationNameForUserAgent = @"wails.io"; - [config setURLSchemeHandler:delegate forURLScheme:@"wails"]; - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 - if (@available(macOS 10.15, *)) { - config.preferences.fraudulentWebsiteWarningEnabled = fraudulentWebsiteWarningEnabled; - } -#endif - - // Setup user content controller - WKUserContentController* userContentController = [WKUserContentController new]; - [userContentController autorelease]; - - [userContentController addScriptMessageHandler:delegate name:@"external"]; - config.userContentController = userContentController; - - WKWebView* webView = [[WKWebView alloc] initWithFrame:frame configuration:config]; - [webView autorelease]; - - // Set allowsBackForwardNavigationGestures if specified - if (preferences.AllowsBackForwardNavigationGestures != NULL) { - webView.allowsBackForwardNavigationGestures = *preferences.AllowsBackForwardNavigationGestures; - } - - [view addSubview:webView]; - - // support webview events - [webView setNavigationDelegate:delegate]; - [webView setUIDelegate:delegate]; - - // Ensure webview resizes with the window - [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - if( enableDragAndDrop ) { - WebviewDrag* dragView = [[WebviewDrag alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; - [dragView autorelease]; - - [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [view addSubview:dragView]; - dragView.windowId = id; - } - - window.webView = webView; + // Configure webview using shared helper + window.webView = configureWebviewForWindow(window, view, delegate, width, height, + fraudulentWebsiteWarningEnabled, enableDragAndDrop, preferences); return window; } @@ -220,71 +219,9 @@ void* panelNew(unsigned int id, int width, int height, bool fraudulentWebsiteWar } [panel setContentView:view]; - // Embed wkwebview in panel (same as window) - NSRect frame = NSMakeRect(0, 0, width, height); - WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init]; - [config autorelease]; - - if (preferences.TabFocusesLinks != NULL) { - config.preferences.tabFocusesLinks = *preferences.TabFocusesLinks; - } - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110300 - if (@available(macOS 11.3, *)) { - if (preferences.TextInteractionEnabled != NULL) { - config.preferences.textInteractionEnabled = *preferences.TextInteractionEnabled; - } - } -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 120300 - if (@available(macOS 12.3, *)) { - if (preferences.FullscreenEnabled != NULL) { - config.preferences.elementFullscreenEnabled = *preferences.FullscreenEnabled; - } - } -#endif - - config.suppressesIncrementalRendering = true; - config.applicationNameForUserAgent = @"wails.io"; - [config setURLSchemeHandler:delegate forURLScheme:@"wails"]; - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 - if (@available(macOS 10.15, *)) { - config.preferences.fraudulentWebsiteWarningEnabled = fraudulentWebsiteWarningEnabled; - } -#endif - - WKUserContentController* userContentController = [WKUserContentController new]; - [userContentController autorelease]; - - [userContentController addScriptMessageHandler:delegate name:@"external"]; - config.userContentController = userContentController; - - WKWebView* webView = [[WKWebView alloc] initWithFrame:frame configuration:config]; - [webView autorelease]; - - if (preferences.AllowsBackForwardNavigationGestures != NULL) { - webView.allowsBackForwardNavigationGestures = *preferences.AllowsBackForwardNavigationGestures; - } - - [view addSubview:webView]; - - [webView setNavigationDelegate:delegate]; - [webView setUIDelegate:delegate]; - - [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - if( enableDragAndDrop ) { - WebviewDrag* dragView = [[WebviewDrag alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; - [dragView autorelease]; - - [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [view addSubview:dragView]; - dragView.windowId = id; - } - - panel.webView = webView; + // Configure webview using shared helper + panel.webView = configureWebviewForWindow(panel, view, delegate, width, height, + fraudulentWebsiteWarningEnabled, enableDragAndDrop, preferences); return panel; } diff --git a/v3/pkg/application/webview_window_darwin.m b/v3/pkg/application/webview_window_darwin.m index 83ed6170c..da2fd85fe 100644 --- a/v3/pkg/application/webview_window_darwin.m +++ b/v3/pkg/application/webview_window_darwin.m @@ -18,89 +18,22 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) { LiquidGlassStyleDark = 2, LiquidGlassStyleVibrant = 3 }; -@implementation WebviewWindow -- (WebviewWindow*) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; -{ - self = [super initWithContentRect:contentRect styleMask:windowStyle backing:bufferingType defer:deferCreation]; - [self setAlphaValue:1.0]; - [self setBackgroundColor:[NSColor clearColor]]; - [self setOpaque:NO]; - [self setMovableByWindowBackground:YES]; - return self; -} -// Override sendEvent to intercept key events BEFORE WKWebView consumes them -// This ensures KeyBindings work regardless of first responder state -- (void)sendEvent:(NSEvent *)event { - if (event.type == NSEventTypeKeyDown) { - [self keyDown:event]; - } - [super sendEvent:event]; -} -- (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 + +// Shared key event handling functions +static NSString* keyStringFromKeyEvent(NSEvent *event) { 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"; - } + if ([characters isEqualToString:@"\r"]) return @"enter"; + if ([characters isEqualToString:@"\b"]) return @"backspace"; + if ([characters isEqualToString:@"\e"]) return @"escape"; + if ([characters isEqualToString:@"\x0B"]) return @"page down"; + if ([characters isEqualToString:@"\x0E"]) return @"page up"; + if ([characters isEqualToString:@"\x01"]) return @"home"; + if ([characters isEqualToString:@"\x04"]) return @"end"; + if ([characters isEqualToString:@"\x0C"]) return @"clear"; switch ([event keyCode]) { - // Function keys case 122: return @"f1"; case 120: return @"f2"; case 99: return @"f3"; @@ -121,7 +54,6 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) { 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"; @@ -148,7 +80,6 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) { 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"; @@ -159,7 +90,6 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) { 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"; @@ -169,7 +99,6 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) { 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 @","; @@ -184,6 +113,44 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) { default: return @""; } } + +static void dispatchKeyDownEvent(NSEvent *event, unsigned int windowId) { + NSUInteger modifierFlags = event.modifierFlags; + NSMutableArray *modifierStrings = [NSMutableArray 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 = keyStringFromKeyEvent(event); + if (keyString.length > 0) { + [modifierStrings addObject:keyString]; + } + NSString *keyEventString = [modifierStrings componentsJoinedByString:@"+"]; + processWindowKeyDownEvent(windowId, [keyEventString UTF8String]); +} + +@implementation WebviewWindow +- (WebviewWindow*) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; +{ + self = [super initWithContentRect:contentRect styleMask:windowStyle backing:bufferingType defer:deferCreation]; + [self setAlphaValue:1.0]; + [self setBackgroundColor:[NSColor clearColor]]; + [self setOpaque:NO]; + [self setMovableByWindowBackground:YES]; + return self; +} +// Override sendEvent to intercept key events BEFORE WKWebView consumes them +// This ensures KeyBindings work regardless of first responder state +- (void)sendEvent:(NSEvent *)event { + if (event.type == NSEventTypeKeyDown) { + [self keyDown:event]; + } + [super sendEvent:event]; +} +- (void)keyDown:(NSEvent *)event { + WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)self.delegate; + dispatchKeyDownEvent(event, delegate.windowId); +} - (BOOL)canBecomeKeyWindow { return YES; } @@ -271,137 +238,8 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) { [super sendEvent:event]; } - (void)keyDown:(NSEvent *)event { - NSUInteger modifierFlags = event.modifierFlags; - NSMutableArray *modifierStrings = [NSMutableArray 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]; - } - NSString *keyEventString = [modifierStrings componentsJoinedByString:@"+"]; - const char* utf8String = [keyEventString UTF8String]; WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)self.delegate; - processWindowKeyDownEvent(delegate.windowId, utf8String); -} -- (NSString *)keyStringFromEvent:(NSEvent *)event { - 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"; - } - if ([characters isEqualToString:@"\x0B"]) { - return @"page down"; - } - if ([characters isEqualToString:@"\x0E"]) { - return @"page up"; - } - if ([characters isEqualToString:@"\x01"]) { - return @"home"; - } - if ([characters isEqualToString:@"\x04"]) { - return @"end"; - } - if ([characters isEqualToString:@"\x0C"]) { - return @"clear"; - } - switch ([event keyCode]) { - 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"; - 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"; - 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"; - 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"; - 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 @""; - } + dispatchKeyDownEvent(event, delegate.windowId); } - (BOOL)canBecomeKeyWindow { return YES;