add helpers

This commit is contained in:
grantmartin2002-oss 2026-03-02 16:20:29 -06:00
commit 58299cc293
2 changed files with 114 additions and 339 deletions

View file

@ -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;
}

View file

@ -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;