Skip to content

Commit

Permalink
cocoa: Implement popup windows
Browse files Browse the repository at this point in the history
  • Loading branch information
Kontrabant authored and slouken committed Mar 10, 2023
1 parent f41d393 commit 77dc1a9
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 22 deletions.
2 changes: 2 additions & 0 deletions src/video/cocoa/SDL_cocoavideo.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ static void Cocoa_DeleteDevice(SDL_VideoDevice *device)

device->free = Cocoa_DeleteDevice;

device->quirk_flags = VIDEO_DEVICE_QUIRK_HAS_POPUP_WINDOW_SUPPORT;

return device;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/video/cocoa/SDL_cocoawindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ typedef enum
@property(nonatomic) SDL_bool inWindowFullscreenTransition;
@property(nonatomic) NSInteger window_number;
@property(nonatomic) NSInteger flash_request;
@property(nonatomic) SDL_Window *keyboard_focus;
@property(nonatomic) Cocoa_WindowListener *listener;
@property(nonatomic) SDL_CocoaVideoData *videodata;
#if SDL_VIDEO_OPENGL_EGL
Expand Down
138 changes: 116 additions & 22 deletions src/video/cocoa/SDL_cocoawindow.m
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,22 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem

- (BOOL)canBecomeKeyWindow
{
return YES;
SDL_Window *window = [self findSDLWindow];
if (window && !SDL_WINDOW_IS_POPUP(window)) {
return YES;
} else {
return NO;
}
}

- (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
Expand Down Expand Up @@ -324,13 +334,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;
}
Expand Down Expand Up @@ -477,6 +491,21 @@ static void Cocoa_UpdateClipCursor(SDL_Window *window)
}
}

static void Cocoa_SetKeyboardFocus(SDL_Window *window)
{
SDL_Window *topmost = window;
SDL_CocoaWindowData* topmost_data;

/* Find the topmost parent */
while (topmost->parent != NULL) {
topmost = topmost->parent;
}

topmost_data = (__bridge SDL_CocoaWindowData *)topmost->driverdata;
topmost_data.keyboard_focus = window;
SDL_SetKeyboardFocus(window);
}

@implementation Cocoa_WindowListener

- (void)listen:(SDL_CocoaWindowData *)data
Expand Down Expand Up @@ -785,6 +814,8 @@ - (void)windowDidMove:(NSNotification *)aNotification

ScheduleContextUpdates(_data);

/* Get the parent-relative coordinates for child windows. */
SDL_GlobalToRelativeForWindow(window, x, y, &x, &y);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
}

Expand Down Expand Up @@ -820,6 +851,7 @@ - (void)windowDidResize:(NSNotification *)aNotification

/* The window can move during a resize event, such as when maximizing
or resizing from a corner */
SDL_GlobalToRelativeForWindow(window, x, y, &x, &y);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, w, h);

Expand Down Expand Up @@ -857,7 +889,7 @@ - (void)windowDidBecomeKey:(NSNotification *)aNotification

/* We're going to get keyboard events, since we're key. */
/* This needs to be done before restoring the relative mouse mode. */
SDL_SetKeyboardFocus(window);
Cocoa_SetKeyboardFocus(_data.keyboard_focus ? _data.keyboard_focus : window);

if (mouse->relative_mode && !mouse->relative_mode_warp && ![self isMovingOrFocusClickPending]) {
mouse->SetRelativeMouseMode(SDL_TRUE);
Expand Down Expand Up @@ -1642,13 +1674,16 @@ static int SetupWindowData(_THIS, SDL_Window *window, NSWindow *nswindow, NSView

/* Fill in the SDL window with the window data */
{
int x, y;
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
ConvertNSRect([nswindow screen], fullscreen, &rect);
window->x = (int)rect.origin.x;
window->y = (int)rect.origin.y;
x = (int)rect.origin.x;
y = (int)rect.origin.y;
window->w = (int)rect.size.width;
window->h = (int)rect.size.height;

SDL_GlobalToRelativeForWindow(window, x, y, &window->x, &window->y);
}

/* Set up the listener after we create the view */
Expand Down Expand Up @@ -1691,9 +1726,22 @@ static int SetupWindowData(_THIS, SDL_Window *window, NSWindow *nswindow, NSView
window->flags &= ~SDL_WINDOW_MINIMIZED;
}

if ([nswindow isKeyWindow]) {
window->flags |= SDL_WINDOW_INPUT_FOCUS;
SDL_SetKeyboardFocus(data.window);
if (!SDL_WINDOW_IS_POPUP(window)) {
if ([nswindow isKeyWindow]) {
window->flags |= SDL_WINDOW_INPUT_FOCUS;
Cocoa_SetKeyboardFocus(data.window);
}
} else {
NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata).nswindow;
[nsparent addChildWindow:nswindow ordered:NSWindowAbove];

if (window->flags & SDL_WINDOW_TOOLTIP) {
[nswindow setIgnoresMouseEvents:YES];
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
if (window->parent == SDL_GetKeyboardFocus()) {
Cocoa_SetKeyboardFocus(window);
}
}
}

/* SDL_CocoaWindowData will be holding a strong reference to the NSWindow, and
Expand All @@ -1718,23 +1766,21 @@ int Cocoa_CreateWindow(_THIS, SDL_Window *window)
@autoreleasepool {
SDL_CocoaVideoData *videodata = (__bridge SDL_CocoaVideoData *)_this->driverdata;
NSWindow *nswindow;
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
int x, y;
NSRect rect;
BOOL fullscreen;
SDL_Rect bounds;
NSUInteger style;
NSArray *screens = [NSScreen screens];
NSScreen *screen = nil;
SDLView *contentView;
BOOL highdpi;

Cocoa_GetDisplayBounds(_this, display, &bounds);
rect.origin.x = window->x;
rect.origin.y = window->y;
SDL_RelativeToGlobalForWindow(window, window->x, window->y, &x, &y);
rect.origin.x = x;
rect.origin.y = y;
rect.size.width = window->w;
rect.size.height = window->h;
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
ConvertNSRect([screens objectAtIndex:0], fullscreen, &rect);

style = GetWindowStyle(window);

Expand All @@ -1751,6 +1797,22 @@ int Cocoa_CreateWindow(_THIS, SDL_Window *window)
}
}

/* Constrain the popup */
if (SDL_WINDOW_IS_POPUP(window)) {
NSRect bounds = [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([screens objectAtIndex:0], fullscreen, &rect);

@try {
nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
}
Expand Down Expand Up @@ -1910,16 +1972,34 @@ void Cocoa_SetWindowPosition(_THIS, SDL_Window *window)
{
@autoreleasepool {
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
NSRect bounds;
NSWindow *nswindow = windata.nswindow;
NSRect rect;
BOOL fullscreen;
Uint64 moveHack;
int x, y;

rect.origin.x = window->x;
rect.origin.y = window->y;
SDL_RelativeToGlobalForWindow(window, window->x, window->y, &x, &y);
rect.origin.x = x;
rect.origin.y = y;
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)) {
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;
Expand Down Expand Up @@ -2023,6 +2103,20 @@ void Cocoa_HideWindow(_THIS, SDL_Window *window)
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->driverdata).nswindow;

[nswindow orderOut:nil];

/* Transfer keyboard focus back to the parent */
if (window->flags & SDL_WINDOW_POPUP_MENU) {
if (window == SDL_GetKeyboardFocus()) {
SDL_Window *new_focus = window->parent;

/* Find the highest level window that isn't being hidden or destroyed. */
while (new_focus->parent != NULL && (new_focus->is_hiding || new_focus->is_destroying)) {
new_focus = new_focus->parent;
}

Cocoa_SetKeyboardFocus(new_focus);
}
}
}
}

Expand Down

0 comments on commit 77dc1a9

Please sign in to comment.