diff --git a/docs/src/content/docs/features/windows/options.mdx b/docs/src/content/docs/features/windows/options.mdx index 9b38d5e2b..1f22d52f2 100644 --- a/docs/src/content/docs/features/windows/options.mdx +++ b/docs/src/content/docs/features/windows/options.mdx @@ -828,8 +828,9 @@ Controls how the window behaves across macOS Spaces and fullscreen. These are bi - `MacWindowCollectionBehaviorFullScreenAllowsTiling` - Allows side-by-side tiling (macOS 10.11+) - `MacWindowCollectionBehaviorFullScreenDisallowsTiling` - Prevents tiling (macOS 10.11+) -**Example - Spotlight-like window:** +**Note:** MacWindowClassWindow fullscreen overlay requires `application.Options{ Mac: application.MacOptions{ ActivationPolicy: application.ActivationPolicyAccessory } }` +**Example - Spotlight-like window:** ```go // Window that appears on all Spaces AND can overlay fullscreen apps Mac: application.MacWindow{ @@ -848,6 +849,32 @@ Mac: application.MacWindow{ }, ``` +**WindowClass** (`MacWindowClass`) +- `MacWindowClassWindow` - Standard window (default) +- `MacWindowClassPanel` - Auxiliary window that can float above other windows and receive input without activating the application + +**PanelPreferences** (`MacPanelPreferences`) + +Preferences for MacWindowClassPanel windows (only applies when `WindowClass` is `MacWindowClassPanel`): +- `FloatingPanel` - Panel floats above other windows +- `BecomesKeyOnlyIfNeeded` - Panel becomes key only when needed +- `NonactivatingPanel` - Panel receives input without activating the application +- `UtilityWindow` - Panel uses utility window style + +**Note:** Unlike MacWindowClassWindow, MacWindowClassPanel fullscreen overlay does NOT require `application.Options{ Mac: application.MacOptions{ ActivationPolicy: application.ActivationPolicyAccessory } }` + +**Example - Spotlight-like panel:** +```go +Mac: application.MacWindow{ + WindowClass: application.MacWindowClassPanel, + PanelPreferences: application.MacPanelPreferences{ + NonactivatingPanel: true, + }, + CollectionBehavior: application.MacWindowCollectionBehaviorCanJoinAllSpaces | + application.MacWindowCollectionBehaviorFullScreenAuxiliary, +}, +``` + ### Windows Options ```go diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index 14fef21da..2730eb3ee 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -21,114 +21,134 @@ struct WebviewPreferences { bool *AllowsBackForwardNavigationGestures; }; +struct PanelPreferences { + bool FloatingPanel; + bool BecomesKeyOnlyIfNeeded; + bool NonactivatingPanel; + bool UtilityWindow; +}; + extern void registerListener(unsigned int event); -// 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; - if (frameless) { - styleMask = NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable; - } - WebviewWindow* window = [[WebviewWindow alloc] initWithContentRect:NSMakeRect(0, 0, width-1, height-1) - styleMask:styleMask - backing:NSBackingStoreBuffered - defer:NO]; - - // Note: collectionBehavior is set later via windowSetCollectionBehavior() - // to allow user configuration of Space and fullscreen behavior - - // Create delegate - WebviewWindowDelegate* delegate = [[WebviewWindowDelegate alloc] init]; - [delegate autorelease]; - - // Set delegate - [window setDelegate:delegate]; - delegate.windowId = id; - - // Add NSView to window - NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; - [view autorelease]; - - [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - if( frameless ) { - [view setWantsLayer:YES]; - view.layer.cornerRadius = 8.0; - } - [window setContentView:view]; - - // Embed wkwebview in window +// 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 webviewPreferences) { 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 (webviewPreferences.TabFocusesLinks != NULL) { + config.preferences.tabFocusesLinks = *webviewPreferences.TabFocusesLinks; } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 110300 if (@available(macOS 11.3, *)) { - if (preferences.TextInteractionEnabled != NULL) { - config.preferences.textInteractionEnabled = *preferences.TextInteractionEnabled; + if (webviewPreferences.TextInteractionEnabled != NULL) { + config.preferences.textInteractionEnabled = *webviewPreferences.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; + if (webviewPreferences.FullscreenEnabled != NULL) { + config.preferences.elementFullscreenEnabled = *webviewPreferences.FullscreenEnabled; + } } #endif - // Setup user content controller - WKUserContentController* userContentController = [WKUserContentController new]; - [userContentController autorelease]; + config.suppressesIncrementalRendering = true; + config.applicationNameForUserAgent = @"wails.io"; + [config setURLSchemeHandler:delegate forURLScheme:@"wails"]; - [userContentController addScriptMessageHandler:delegate name:@"external"]; - config.userContentController = userContentController; +#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]; - // 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; + if (webviewPreferences.AllowsBackForwardNavigationGestures != NULL) { + webView.allowsBackForwardNavigationGestures = *webviewPreferences.AllowsBackForwardNavigationGestures; } - window.webView = webView; - return window; + [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 webviewPreferences, bool isPanel, struct PanelPreferences panelPreferences) { + NSWindowStyleMask styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; + if (frameless) { + styleMask = NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable; + } + + NSWindow* window; + if (isPanel) { + if (panelPreferences.NonactivatingPanel) { + styleMask |= NSWindowStyleMaskNonactivatingPanel; + } + if (panelPreferences.UtilityWindow) { + styleMask |= NSWindowStyleMaskUtilityWindow; + } + WebviewPanel* panel = [[WebviewPanel alloc] initWithContentRect:NSMakeRect(0, 0, width-1, height-1) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + [panel setFloatingPanel:panelPreferences.FloatingPanel]; + [panel setBecomesKeyOnlyIfNeeded:panelPreferences.BecomesKeyOnlyIfNeeded]; + window = panel; + } else { + window = [[WebviewWindow alloc] initWithContentRect:NSMakeRect(0, 0, width-1, height-1) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + } + + WebviewWindowDelegate* delegate = [[WebviewWindowDelegate alloc] init]; + [delegate autorelease]; + [window setDelegate:delegate]; + delegate.windowId = id; + + NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; + [view autorelease]; + [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + if (frameless) { + [view setWantsLayer:YES]; + view.layer.cornerRadius = 8.0; + } + [window setContentView:view]; + + WKWebView* webView = configureWebviewForWindow(window, view, delegate, width, height, + fraudulentWebsiteWarningEnabled, enableDragAndDrop, webviewPreferences); + if (isPanel) { + ((WebviewPanel*)window).webView = webView; + } else { + ((WebviewWindow*)window).webView = webView; + } + return window; +} void printWindowStyle(void *window) { WebviewWindow* nsWindow = (WebviewWindow*)window; @@ -1276,6 +1296,17 @@ func (w *macosWebviewWindow) getWebviewPreferences() C.struct_WebviewPreferences return result } +func (w *macosWebviewWindow) getPanelPreferences() C.struct_PanelPreferences { + panelPrefs := w.parent.options.Mac.PanelPreferences + + return C.struct_PanelPreferences{ + FloatingPanel: C.bool(panelPrefs.FloatingPanel), + BecomesKeyOnlyIfNeeded: C.bool(panelPrefs.BecomesKeyOnlyIfNeeded), + NonactivatingPanel: C.bool(panelPrefs.NonactivatingPanel), + UtilityWindow: C.bool(panelPrefs.UtilityWindow), + } +} + func (w *macosWebviewWindow) run() { for eventId := range w.parent.eventListeners { w.on(eventId) @@ -1291,6 +1322,8 @@ func (w *macosWebviewWindow) run() { C.bool(options.Frameless), C.bool(options.EnableFileDrop), w.getWebviewPreferences(), + C.bool(macOptions.WindowClass == MacWindowClassPanel), + w.getPanelPreferences(), ) w.setTitle(options.Title) w.setResizable(!options.DisableResize) @@ -1320,10 +1353,13 @@ func (w *macosWebviewWindow) run() { case MacBackdropNormal: } - if macOptions.WindowLevel == "" { - macOptions.WindowLevel = MacWindowLevelNormal + // Only set window level if explicitly specified, or if not a floating panel + // (setFloatingPanel:YES already sets NSFloatingWindowLevel, so don't override it) + if macOptions.WindowLevel != "" { + w.setWindowLevel(macOptions.WindowLevel) + } else if !(macOptions.WindowClass == MacWindowClassPanel && macOptions.PanelPreferences.FloatingPanel) { + w.setWindowLevel(MacWindowLevelNormal) } - w.setWindowLevel(macOptions.WindowLevel) // Set collection behavior (defaults to FullScreenPrimary for backwards compatibility) w.setCollectionBehavior(macOptions.CollectionBehavior) diff --git a/v3/pkg/application/webview_window_darwin.h b/v3/pkg/application/webview_window_darwin.h index 489ff582e..4e129f2fc 100644 --- a/v3/pkg/application/webview_window_darwin.h +++ b/v3/pkg/application/webview_window_darwin.h @@ -18,6 +18,18 @@ @end +@interface WebviewPanel : NSPanel +- (BOOL) canBecomeKeyWindow; +- (BOOL) canBecomeMainWindow; +- (BOOL) acceptsFirstResponder; +- (BOOL) becomeFirstResponder; +- (BOOL) resignFirstResponder; +- (WebviewPanel*) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; + +@property (assign) WKWebView* webView; + +@end + @interface WebviewWindowDelegate : NSObject @property unsigned int windowId; diff --git a/v3/pkg/application/webview_window_darwin.m b/v3/pkg/application/webview_window_darwin.m index 48130a562..6662bdedf 100644 --- a/v3/pkg/application/webview_window_darwin.m +++ b/v3/pkg/application/webview_window_darwin.m @@ -18,81 +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; -} -- (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"; @@ -113,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"; @@ -140,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"; @@ -151,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"; @@ -161,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 @","; @@ -176,6 +113,36 @@ 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; +} +- (void)keyDown:(NSEvent *)event { + WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)self.delegate; + dispatchKeyDownEvent(event, delegate.windowId); +} - (BOOL)canBecomeKeyWindow { return YES; } @@ -243,6 +210,59 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) { } } @end + +@implementation WebviewPanel +- (WebviewPanel*) 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 +- (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; +} +- (BOOL) canBecomeMainWindow { + return NO; // Panels typically don't become main window +} +- (BOOL) acceptsFirstResponder { + return YES; +} +- (BOOL) becomeFirstResponder { + return YES; +} +- (BOOL) resignFirstResponder { + return YES; +} +- (void) setDelegate:(id) delegate { + [delegate retain]; + [super setDelegate: delegate]; + if ([delegate isKindOfClass:[WebviewWindowDelegate class]]) { + [self registerForDraggedTypes:@[NSFilenamesPboardType]]; + } +} +- (void) dealloc { + [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"external"]; + if (self.delegate) { + [self.delegate release]; + } + [super dealloc]; +} +@end + @implementation WebviewWindowDelegate - (NSDragOperation)draggingEntered:(id)sender { NSPasteboard *pasteboard = [sender draggingPasteboard]; diff --git a/v3/pkg/application/webview_window_options.go b/v3/pkg/application/webview_window_options.go index c1b1bf164..10189c481 100644 --- a/v3/pkg/application/webview_window_options.go +++ b/v3/pkg/application/webview_window_options.go @@ -485,6 +485,34 @@ type MacWindow struct { // LiquidGlass contains configuration for the Liquid Glass effect LiquidGlass MacLiquidGlass + + // WindowClass is the window class for the window + WindowClass MacWindowClass + + // PanelPreferences contains configuration for panel windows + PanelPreferences MacPanelPreferences +} + +// MacWindowClass is the window class for macOS +type MacWindowClass int + +const ( + // MacWindowClassWindow - The default value. A window that an app displays on the screen. + MacWindowClassWindow MacWindowClass = iota + // MacWindowClassPanel - A special kind of window that typically performs a function that is auxiliary to the main window + MacWindowClassPanel +) + +// MacPanelPreferences contains options for MacWindowClassPanel windows +type MacPanelPreferences struct { + // FloatingPanel will make the panel float above other windows + FloatingPanel bool + // BecomesKeyOnlyIfNeeded will make the panel become key only when needed + BecomesKeyOnlyIfNeeded bool + // NonactivatingPanel will apply the NSWindowStyleMaskNonactivatingPanel style + NonactivatingPanel bool + // UtilityWindow will apply the NSWindowStyleMaskUtilityWindow style + UtilityWindow bool } type MacWindowLevel string diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index c5f51220e..ac7e7804e 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Added Panel support for macOS. Panels serve as auxiliary windows and can appear over fullscreen apps without activating the application using `NSWindowStyleMaskNonactivatingPanel`. Configure via `Mac.WindowClass` and `Mac.PanelPreferences` in window options. Added by [@Grantmartin2002](https://github.com/Grantmartin2002) in [PR](https://github.com/wailsapp/wails/pull/5024) + ### Fixed - Fixed locking issue on Windows when multiselect dialog returns an error. Fixed in [PR](https://github.com/wailsapp/wails/pull/4156) by @johannes-luebke