mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-06 03:18:13 +00:00
macOS child window fixes
- Fix places working with window coordinates that need to call SDL_RelativeToGlobalForWindow or SDL_GlobalToRelativeForWindow - Remove NSScreen param from ConvertNSRect(). Reflecting the Y coordinate is done relative to the main screen height (which ConvertNSRect was already doing) so the explicit screen isn't needed. - Refactor NSScreen lookups for point/rect and fix getting the screen for Cocoa_SetWindowPosition() to get the screen for the new position and not the window's current screen (which may not exist if the window is off-screen). - Fix re-associating the popup and parent window when the child window is shown. Hiding a child window removes it from the window hierarchy and so must be added when the window is shown again. - Allow popup windows that are not tooltips to gain key focus.
This commit is contained in:
@@ -116,7 +116,7 @@
|
|||||||
- (BOOL)canBecomeKeyWindow
|
- (BOOL)canBecomeKeyWindow
|
||||||
{
|
{
|
||||||
SDL_Window *window = [self findSDLWindow];
|
SDL_Window *window = [self findSDLWindow];
|
||||||
if (window && !SDL_WINDOW_IS_POPUP(window)) {
|
if (window && !(window->flags & SDL_WINDOW_TOOLTIP)) {
|
||||||
return YES;
|
return YES;
|
||||||
} else {
|
} else {
|
||||||
return NO;
|
return NO;
|
||||||
@@ -279,7 +279,63 @@
|
|||||||
|
|
||||||
static Uint64 s_moveHack;
|
static Uint64 s_moveHack;
|
||||||
|
|
||||||
static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r)
|
static CGFloat SqDistanceToRect(const NSPoint *point, const NSRect *rect)
|
||||||
|
{
|
||||||
|
NSPoint edge = *point;
|
||||||
|
CGFloat left = NSMinX(*rect), right = NSMaxX(*rect);
|
||||||
|
CGFloat bottom = NSMinX(*rect), top = NSMaxY(*rect);
|
||||||
|
NSPoint delta;
|
||||||
|
|
||||||
|
if (point->x < left) {
|
||||||
|
edge.x = left;
|
||||||
|
} else if (point->x > right) {
|
||||||
|
edge.x = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point->y < bottom) {
|
||||||
|
edge.y = bottom;
|
||||||
|
} else if (point->y > top) {
|
||||||
|
edge.y = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = NSMakePoint(edge.x - point->x, edge.y - point->y);
|
||||||
|
return delta.x * delta.x + delta.y * delta.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSScreen *ScreenForPoint(const NSPoint *point) {
|
||||||
|
NSScreen *screen;
|
||||||
|
|
||||||
|
/* Do a quick check first to see if the point lies on a specific screen*/
|
||||||
|
for (NSScreen *candidate in [NSScreen screens]) {
|
||||||
|
if (NSPointInRect(*point, [candidate frame])) {
|
||||||
|
screen = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the screen the point is closest to */
|
||||||
|
if (!screen) {
|
||||||
|
CGFloat closest = MAXFLOAT;
|
||||||
|
for (NSScreen *candidate in [NSScreen screens]) {
|
||||||
|
NSRect screenRect = [candidate frame];
|
||||||
|
|
||||||
|
CGFloat sqdist = SqDistanceToRect(point, &screenRect);
|
||||||
|
if (sqdist < closest) {
|
||||||
|
screen = candidate;
|
||||||
|
closest = sqdist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSScreen *ScreenForRect(const NSRect *rect) {
|
||||||
|
NSPoint center = NSMakePoint(NSMidX(*rect), NSMidY(*rect));
|
||||||
|
return ScreenForPoint(¢er);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ConvertNSRect(BOOL fullscreen, NSRect *r)
|
||||||
{
|
{
|
||||||
r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
|
r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
|
||||||
}
|
}
|
||||||
@@ -787,7 +843,7 @@ static void Cocoa_SetKeyboardFocus(SDL_Window *window)
|
|||||||
NSWindow *nswindow = _data.nswindow;
|
NSWindow *nswindow = _data.nswindow;
|
||||||
BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
||||||
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
|
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
|
||||||
ConvertNSRect([nswindow screen], fullscreen, &rect);
|
ConvertNSRect(fullscreen, &rect);
|
||||||
|
|
||||||
if (inFullscreenTransition) {
|
if (inFullscreenTransition) {
|
||||||
/* We'll take care of this at the end of the transition */
|
/* We'll take care of this at the end of the transition */
|
||||||
@@ -801,9 +857,10 @@ static void Cocoa_SetKeyboardFocus(SDL_Window *window)
|
|||||||
|
|
||||||
if (blockMove) {
|
if (blockMove) {
|
||||||
/* Cocoa is adjusting the window in response to a mode change */
|
/* Cocoa is adjusting the window in response to a mode change */
|
||||||
rect.origin.x = window->x;
|
SDL_RelativeToGlobalForWindow(window, window->x, window->y, &x, &y );
|
||||||
rect.origin.y = window->y;
|
rect.origin.x = x;
|
||||||
ConvertNSRect([nswindow screen], fullscreen, &rect);
|
rect.origin.y = y;
|
||||||
|
ConvertNSRect(fullscreen, &rect);
|
||||||
[nswindow setFrameOrigin:rect.origin];
|
[nswindow setFrameOrigin:rect.origin];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -841,7 +898,7 @@ static void Cocoa_SetKeyboardFocus(SDL_Window *window)
|
|||||||
nswindow = _data.nswindow;
|
nswindow = _data.nswindow;
|
||||||
rect = [nswindow contentRectForFrameRect:[nswindow frame]];
|
rect = [nswindow contentRectForFrameRect:[nswindow frame]];
|
||||||
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
||||||
ConvertNSRect([nswindow screen], fullscreen, &rect);
|
ConvertNSRect(fullscreen, &rect);
|
||||||
x = (int)rect.origin.x;
|
x = (int)rect.origin.x;
|
||||||
y = (int)rect.origin.y;
|
y = (int)rect.origin.y;
|
||||||
w = (int)rect.size.width;
|
w = (int)rect.size.width;
|
||||||
@@ -1114,12 +1171,14 @@ static void Cocoa_SetKeyboardFocus(SDL_Window *window)
|
|||||||
*/
|
*/
|
||||||
/* Restore windowed size and position in case it changed while fullscreen */
|
/* Restore windowed size and position in case it changed while fullscreen */
|
||||||
{
|
{
|
||||||
|
int x, y;
|
||||||
NSRect rect;
|
NSRect rect;
|
||||||
rect.origin.x = window->windowed.x;
|
SDL_RelativeToGlobalForWindow(window, window->windowed.x, window->windowed.y, x, y);
|
||||||
rect.origin.y = window->windowed.y;
|
rect.origin.x = x;
|
||||||
|
rect.origin.y = y;
|
||||||
rect.size.width = window->windowed.w;
|
rect.size.width = window->windowed.w;
|
||||||
rect.size.height = window->windowed.h;
|
rect.size.height = window->windowed.h;
|
||||||
ConvertNSRect([nswindow screen], NO, &rect);
|
ConvertNSRect(NO, &rect);
|
||||||
|
|
||||||
s_moveHack = 0;
|
s_moveHack = 0;
|
||||||
[nswindow setContentSize:rect.size];
|
[nswindow setContentSize:rect.size];
|
||||||
@@ -1682,13 +1741,12 @@ static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow
|
|||||||
int x, y;
|
int x, y;
|
||||||
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
|
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
|
||||||
BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
||||||
ConvertNSRect([nswindow screen], fullscreen, &rect);
|
ConvertNSRect(fullscreen, &rect);
|
||||||
x = (int)rect.origin.x;
|
SDL_GlobalToRelativeForWindow(window, (int)rect.origin.x, (int)rect.origin.y, &x, &y);
|
||||||
y = (int)rect.origin.y;
|
window->x = x;
|
||||||
|
window->y = y;
|
||||||
window->w = (int)rect.size.width;
|
window->w = (int)rect.size.width;
|
||||||
window->h = (int)rect.size.height;
|
window->h = (int)rect.size.height;
|
||||||
|
|
||||||
SDL_GlobalToRelativeForWindow(window, x, y, &window->x, &window->y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up the listener after we create the view */
|
/* Set up the listener after we create the view */
|
||||||
@@ -1778,11 +1836,10 @@ int Cocoa_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||||||
SDL_CocoaVideoData *videodata = (__bridge SDL_CocoaVideoData *)_this->driverdata;
|
SDL_CocoaVideoData *videodata = (__bridge SDL_CocoaVideoData *)_this->driverdata;
|
||||||
NSWindow *nswindow;
|
NSWindow *nswindow;
|
||||||
int x, y;
|
int x, y;
|
||||||
NSRect rect;
|
NSScreen *screen;
|
||||||
|
NSRect rect, screenRect;
|
||||||
BOOL fullscreen;
|
BOOL fullscreen;
|
||||||
NSUInteger style;
|
NSUInteger style;
|
||||||
NSArray *screens = [NSScreen screens];
|
|
||||||
NSScreen *screen = nil;
|
|
||||||
SDLView *contentView;
|
SDLView *contentView;
|
||||||
BOOL highdpi;
|
BOOL highdpi;
|
||||||
|
|
||||||
@@ -1792,38 +1849,28 @@ int Cocoa_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||||||
rect.size.width = window->w;
|
rect.size.width = window->w;
|
||||||
rect.size.height = window->h;
|
rect.size.height = window->h;
|
||||||
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
||||||
|
ConvertNSRect(fullscreen, &rect);
|
||||||
|
|
||||||
style = GetWindowStyle(window);
|
style = GetWindowStyle(window);
|
||||||
|
|
||||||
/* Figure out which screen to place this window */
|
/* Figure out which screen to place this window */
|
||||||
for (NSScreen *candidate in screens) {
|
screen = ScreenForRect(&rect);
|
||||||
NSRect screenRect = [candidate frame];
|
screenRect = [screen frame];
|
||||||
if (rect.origin.x >= screenRect.origin.x &&
|
|
||||||
rect.origin.x < screenRect.origin.x + screenRect.size.width &&
|
|
||||||
rect.origin.y >= screenRect.origin.y &&
|
|
||||||
rect.origin.y < screenRect.origin.y + screenRect.size.height) {
|
|
||||||
screen = candidate;
|
|
||||||
rect.origin.x -= screenRect.origin.x;
|
rect.origin.x -= screenRect.origin.x;
|
||||||
rect.origin.y -= screenRect.origin.y;
|
rect.origin.y -= screenRect.origin.y;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Constrain the popup */
|
/* Constrain the popup */
|
||||||
if (SDL_WINDOW_IS_POPUP(window)) {
|
if (SDL_WINDOW_IS_POPUP(window)) {
|
||||||
NSRect bounds = [screen frame];
|
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
|
||||||
|
rect.origin.x -= (rect.origin.x + rect.size.width) - (screenRect.origin.x + screenRect.size.width);
|
||||||
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) {
|
if (rect.origin.y + rect.size.height > screenRect.origin.y + screenRect.size.height) {
|
||||||
rect.origin.y -= (rect.origin.y + rect.size.height) - (bounds.origin.y + bounds.size.height);
|
rect.origin.y -= (rect.origin.y + rect.size.height) - (screenRect.origin.y + screenRect.size.height);
|
||||||
}
|
}
|
||||||
rect.origin.x = SDL_max(rect.origin.x, bounds.origin.x);
|
rect.origin.x = SDL_max(rect.origin.x, screenRect.origin.x);
|
||||||
rect.origin.y = SDL_max(rect.origin.y, bounds.origin.y);
|
rect.origin.y = SDL_max(rect.origin.y, screenRect.origin.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConvertNSRect([screens objectAtIndex:0], fullscreen, &rect);
|
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
|
nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
|
||||||
}
|
}
|
||||||
@@ -1988,9 +2035,8 @@ int Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
|
|||||||
{
|
{
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
|
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
|
||||||
NSRect bounds;
|
|
||||||
NSWindow *nswindow = windata.nswindow;
|
NSWindow *nswindow = windata.nswindow;
|
||||||
NSRect rect;
|
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
|
||||||
BOOL fullscreen;
|
BOOL fullscreen;
|
||||||
Uint64 moveHack;
|
Uint64 moveHack;
|
||||||
int x, y;
|
int x, y;
|
||||||
@@ -1998,26 +2044,23 @@ int Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
|
|||||||
SDL_RelativeToGlobalForWindow(window, window->x, window->y, &x, &y);
|
SDL_RelativeToGlobalForWindow(window, window->x, window->y, &x, &y);
|
||||||
rect.origin.x = x;
|
rect.origin.x = x;
|
||||||
rect.origin.y = y;
|
rect.origin.y = y;
|
||||||
rect.size.width = window->w;
|
|
||||||
rect.size.height = window->h;
|
|
||||||
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
||||||
|
ConvertNSRect(fullscreen, &rect);
|
||||||
|
|
||||||
/* Position and constrain the popup */
|
/* Position and constrain the popup */
|
||||||
if (SDL_WINDOW_IS_POPUP(window)) {
|
if (SDL_WINDOW_IS_POPUP(window)) {
|
||||||
bounds = [[nswindow screen] frame];
|
NSRect screenRect = [ScreenForRect(&rect) frame];
|
||||||
|
|
||||||
if (rect.origin.x + rect.size.width > bounds.origin.x + bounds.size.width) {
|
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
|
||||||
rect.origin.x -= (rect.origin.x + rect.size.width) - (bounds.origin.x + bounds.size.width);
|
rect.origin.x -= (rect.origin.x + rect.size.width) - (screenRect.origin.x + screenRect.size.width);
|
||||||
}
|
}
|
||||||
if (rect.origin.y + rect.size.height > bounds.origin.y + bounds.size.height) {
|
if (rect.origin.y + rect.size.height > screenRect.origin.y + screenRect.size.height) {
|
||||||
rect.origin.y -= (rect.origin.y + rect.size.height) - (bounds.origin.y + bounds.size.height);
|
rect.origin.y -= (rect.origin.y + rect.size.height) - (screenRect.origin.y + screenRect.size.height);
|
||||||
}
|
}
|
||||||
rect.origin.x = SDL_max(rect.origin.x, bounds.origin.x);
|
rect.origin.x = SDL_max(rect.origin.x, screenRect.origin.x);
|
||||||
rect.origin.y = SDL_max(rect.origin.y, bounds.origin.y);
|
rect.origin.y = SDL_max(rect.origin.y, screenRect.origin.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConvertNSRect([nswindow screen], fullscreen, &rect);
|
|
||||||
|
|
||||||
moveHack = s_moveHack;
|
moveHack = s_moveHack;
|
||||||
s_moveHack = 0;
|
s_moveHack = 0;
|
||||||
[nswindow setFrameOrigin:rect.origin];
|
[nswindow setFrameOrigin:rect.origin];
|
||||||
@@ -2033,20 +2076,13 @@ void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
|
|||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
|
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
|
||||||
NSWindow *nswindow = windata.nswindow;
|
NSWindow *nswindow = windata.nswindow;
|
||||||
NSRect rect;
|
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
|
||||||
BOOL fullscreen;
|
BOOL fullscreen;
|
||||||
Uint64 moveHack;
|
Uint64 moveHack;
|
||||||
|
|
||||||
/* Cocoa will resize the window from the bottom-left rather than the
|
|
||||||
* top-left when -[nswindow setContentSize:] is used, so we must set the
|
|
||||||
* entire frame based on the new size, in order to preserve the position.
|
|
||||||
*/
|
|
||||||
rect.origin.x = window->x;
|
|
||||||
rect.origin.y = window->y;
|
|
||||||
rect.size.width = window->w;
|
rect.size.width = window->w;
|
||||||
rect.size.height = window->h;
|
rect.size.height = window->h;
|
||||||
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
|
||||||
ConvertNSRect([nswindow screen], fullscreen, &rect);
|
|
||||||
|
|
||||||
moveHack = s_moveHack;
|
moveHack = s_moveHack;
|
||||||
s_moveHack = 0;
|
s_moveHack = 0;
|
||||||
@@ -2108,6 +2144,10 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||||||
|
|
||||||
if (![nswindow isMiniaturized]) {
|
if (![nswindow isMiniaturized]) {
|
||||||
[windowData.listener pauseVisibleObservation];
|
[windowData.listener pauseVisibleObservation];
|
||||||
|
if (SDL_WINDOW_IS_POPUP(window)) {
|
||||||
|
NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata).nswindow;
|
||||||
|
[nsparent addChildWindow:nswindow ordered:NSWindowAbove];
|
||||||
|
}
|
||||||
[nswindow makeKeyAndOrderFront:nil];
|
[nswindow makeKeyAndOrderFront:nil];
|
||||||
[windowData.listener resumeVisibleObservation];
|
[windowData.listener resumeVisibleObservation];
|
||||||
}
|
}
|
||||||
@@ -2149,6 +2189,10 @@ void Cocoa_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||||||
[windowData.listener pauseVisibleObservation];
|
[windowData.listener pauseVisibleObservation];
|
||||||
if (![nswindow isMiniaturized] && [nswindow isVisible]) {
|
if (![nswindow isMiniaturized] && [nswindow isVisible]) {
|
||||||
[NSApp activateIgnoringOtherApps:YES];
|
[NSApp activateIgnoringOtherApps:YES];
|
||||||
|
if (SDL_WINDOW_IS_POPUP(window)) {
|
||||||
|
NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata).nswindow;
|
||||||
|
[nsparent addChildWindow:nswindow ordered:NSWindowAbove];
|
||||||
|
}
|
||||||
[nswindow makeKeyAndOrderFront:nil];
|
[nswindow makeKeyAndOrderFront:nil];
|
||||||
}
|
}
|
||||||
[windowData.listener resumeVisibleObservation];
|
[windowData.listener resumeVisibleObservation];
|
||||||
@@ -2261,7 +2305,7 @@ void Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_V
|
|||||||
rect.origin.y = bounds.y;
|
rect.origin.y = bounds.y;
|
||||||
rect.size.width = bounds.w;
|
rect.size.width = bounds.w;
|
||||||
rect.size.height = bounds.h;
|
rect.size.height = bounds.h;
|
||||||
ConvertNSRect([nswindow screen], fullscreen, &rect);
|
ConvertNSRect(fullscreen, &rect);
|
||||||
|
|
||||||
/* Hack to fix origin on macOS 10.4
|
/* Hack to fix origin on macOS 10.4
|
||||||
This is no longer needed as of macOS 10.15, according to bug 4822.
|
This is no longer needed as of macOS 10.15, according to bug 4822.
|
||||||
@@ -2280,7 +2324,7 @@ void Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_V
|
|||||||
rect.origin.y = window->windowed.y;
|
rect.origin.y = window->windowed.y;
|
||||||
rect.size.width = window->windowed.w;
|
rect.size.width = window->windowed.w;
|
||||||
rect.size.height = window->windowed.h;
|
rect.size.height = window->windowed.h;
|
||||||
ConvertNSRect([nswindow screen], fullscreen, &rect);
|
ConvertNSRect(fullscreen, &rect);
|
||||||
|
|
||||||
/* The window is not meant to be fullscreen, but its flags might have a
|
/* The window is not meant to be fullscreen, but its flags might have a
|
||||||
* fullscreen bit set if it's scheduled to go fullscreen immediately
|
* fullscreen bit set if it's scheduled to go fullscreen immediately
|
||||||
|
Reference in New Issue
Block a user