From 205ae7ec9f2e0abbf93f1b9a97593d43a11c73c1 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Thu, 2 Mar 2023 12:25:43 -0500
Subject: [PATCH] cocoa: Implement Cocoa popup windows
---
src/video/cocoa/SDL_cocoawindow.m | 115 ++++++++++++++++++++++++++++--
1 file changed, 108 insertions(+), 7 deletions(-)
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index b14cd3bbf623..0f159a67f602 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -119,7 +119,12 @@ - (BOOL)canBecomeKeyWindow
- (BOOL)canBecomeMainWindow
{
- return YES;
+ SDL_Window *window = [self findSDLWindow];
+ if (window && !SDL_WINDOW_IS_POPUP(window)) {
+ return YES;
+ } else {
+ return NO;
+ }
}
- (void)sendEvent:(NSEvent *)event
@@ -308,13 +313,17 @@ the NSWindowStyleMaskBorderless comments in SetupWindowData()! */
minimize the window, whether there's a title bar or not */
NSUInteger style = NSWindowStyleMaskMiniaturizable;
- if (window->flags & SDL_WINDOW_BORDERLESS) {
- style |= NSWindowStyleMaskBorderless;
+ if (!SDL_WINDOW_IS_POPUP(window)) {
+ if (window->flags & SDL_WINDOW_BORDERLESS) {
+ style |= NSWindowStyleMaskBorderless;
+ } else {
+ style |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable);
+ }
+ if (window->flags & SDL_WINDOW_RESIZABLE) {
+ style |= NSWindowStyleMaskResizable;
+ }
} else {
- style |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable);
- }
- if (window->flags & SDL_WINDOW_RESIZABLE) {
- style |= NSWindowStyleMaskResizable;
+ style |= NSWindowStyleMaskBorderless;
}
return style;
}
@@ -461,6 +470,26 @@ static void Cocoa_UpdateClipCursor(SDL_Window *window)
}
}
+static void Cocoa_RestoreChildParameters(SDL_Window *window)
+{
+ if (SDL_WINDOW_IS_POPUP(window)) {
+ SDL_CocoaWindowData *windowData, *parentData;
+ SDL_Window *w;
+
+ windowData = (__bridge SDL_CocoaWindowData *)window->driverdata;
+ parentData = (__bridge SDL_CocoaWindowData *)window->parent->driverdata;
+ [parentData.nswindow addChildWindow:windowData.nswindow ordered:NSWindowAbove];
+
+ if (window->flags & SDL_WINDOW_TOOLTIP) {
+ [windowData.nswindow setIgnoresMouseEvents:YES];
+ }
+
+ for (w = window->first_child; w != NULL; w = w->next_sibling) {
+ Cocoa_RestoreChildParameters(w);
+ }
+ }
+}
+
@implementation Cocoa_WindowListener
- (void)listen:(SDL_CocoaWindowData *)data
@@ -738,6 +767,7 @@ - (void)windowWillMove:(NSNotification *)aNotification
- (void)windowDidMove:(NSNotification *)aNotification
{
int x, y;
+ SDL_Window *w;
SDL_Window *window = _data.window;
NSWindow *nswindow = _data.nswindow;
BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
@@ -766,6 +796,12 @@ - (void)windowDidMove:(NSNotification *)aNotification
x = (int)rect.origin.x;
y = (int)rect.origin.y;
+
+ /* Get the parent-relative coordinates for child windows. */
+ for (w = window->parent; w != NULL; w = w->parent) {
+ x -= w->x;
+ y -= w->y;
+ }
ScheduleContextUpdates(_data);
@@ -1700,6 +1736,8 @@ int Cocoa_CreateWindow(_THIS, SDL_Window *window)
{
@autoreleasepool {
SDL_CocoaVideoData *videodata = (__bridge SDL_CocoaVideoData *)_this->driverdata;
+ SDL_CocoaWindowData *windata;
+ SDL_Window *w;
NSWindow *nswindow;
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
NSRect rect;
@@ -1717,6 +1755,13 @@ int Cocoa_CreateWindow(_THIS, SDL_Window *window)
rect.size.width = window->w;
rect.size.height = window->h;
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
+
+ if (window->parent) {
+ w = window->parent;
+ rect.origin.x += w->x;
+ rect.origin.y += w->y;
+ }
+
ConvertNSRect([screens objectAtIndex:0], fullscreen, &rect);
style = GetWindowStyle(window);
@@ -1740,6 +1785,18 @@ int Cocoa_CreateWindow(_THIS, SDL_Window *window)
@catch (NSException *e) {
return SDL_SetError("%s", [[e reason] UTF8String]);
}
+
+ if (SDL_WINDOW_IS_POPUP(window)) {
+ windata = (__bridge SDL_CocoaWindowData *)window->parent->driverdata;
+
+ [windata.nswindow addChildWindow:nswindow ordered:NSWindowAbove];
+
+ if (window->flags & SDL_WINDOW_TOOLTIP) {
+ [nswindow setIgnoresMouseEvents:YES];
+ } else {
+ [nswindow makeKeyWindow];
+ }
+ }
[nswindow setColorSpace:[NSColorSpace sRGBColorSpace]];
@@ -1893,7 +1950,9 @@ void Cocoa_SetWindowPosition(_THIS, SDL_Window *window)
{
@autoreleasepool {
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
+ NSRect bounds;
NSWindow *nswindow = windata.nswindow;
+ SDL_Window *w;
NSRect rect;
BOOL fullscreen;
Uint64 moveHack;
@@ -1903,6 +1962,26 @@ void Cocoa_SetWindowPosition(_THIS, SDL_Window *window)
rect.size.width = window->w;
rect.size.height = window->h;
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
+
+ /* Position and constrain the popup */
+ if (SDL_WINDOW_IS_POPUP(window)) {
+ for (w = window->parent; w != NULL; w = w->parent) {
+ rect.origin.x += w->x;
+ rect.origin.y += w->y;
+ }
+
+ bounds = [[nswindow screen] frame];
+
+ if (rect.origin.x + rect.size.width > bounds.origin.x + bounds.size.width) {
+ rect.origin.x -= (rect.origin.x + rect.size.width) - (bounds.origin.x + bounds.size.width);
+ }
+ if (rect.origin.y + rect.size.height > bounds.origin.y + bounds.size.height) {
+ rect.origin.y -= (rect.origin.y + rect.size.height) - (bounds.origin.y + bounds.size.height);
+ }
+ rect.origin.x = SDL_max(rect.origin.x, bounds.origin.x);
+ rect.origin.y = SDL_max(rect.origin.y, bounds.origin.y);
+ }
+
ConvertNSRect([nswindow screen], fullscreen, &rect);
moveHack = s_moveHack;
@@ -1997,6 +2076,28 @@ void Cocoa_ShowWindow(_THIS, SDL_Window *window)
[nswindow makeKeyAndOrderFront:nil];
[windowData.listener resumeVisibleObservation];
}
+
+ if (SDL_WINDOW_IS_POPUP(window)) {
+ SDL_Window *w;
+
+ windowData = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata);
+ [windowData.nswindow addChildWindow:nswindow ordered:NSWindowAbove];
+
+ if (window->flags & SDL_WINDOW_TOOLTIP) {
+ [nswindow setIgnoresMouseEvents:YES];
+
+ for (w = window->parent; w->parent != NULL; w = w->parent) {
+ if (!(w->flags & SDL_WINDOW_TOOLTIP)) {
+ break;
+ }
+ }
+
+ windowData = ((__bridge SDL_CocoaWindowData *)w->driverdata);
+ [windowData.nswindow makeKeyWindow];
+ } else {
+ [nswindow makeKeyWindow];
+ }
+ }
}
}