Fix floating window ordering and focus restoration

Keep minimizable macOS regular windows as independent top-level
windows instead of Cocoa child windows while preserving their logical
parent relationship.
This commit is contained in:
Andrei Gagua
2026-07-07 11:50:34 +03:00
parent 9952d6cbf8
commit 41520bb377
2 changed files with 91 additions and 27 deletions
+78 -19
View File
@@ -39,6 +39,62 @@ inline bool IsWindowInvalid(Window* win)
return !hasWindow || !win;
}
inline Window* GetEngineWindow(NSWindow* nativeWindow)
{
WindowsManager::WindowsLocker.Lock();
for (auto* window : WindowsManager::Windows)
{
if (window && window->GetNativePtr() == nativeWindow)
{
WindowsManager::WindowsLocker.Unlock();
return window;
}
}
WindowsManager::WindowsLocker.Unlock();
return nullptr;
}
inline bool ShouldAttachToParent(const CreateWindowSettings& settings)
{
// Cocoa child windows inherit parent z-order and minimize behavior.
// Minimizable regular windows need independent top-level window semantics.
return settings.Parent && (settings.Type != WindowType::Regular || !settings.AllowMinimize);
}
inline bool CanMakeKeyWindow(NSWindow* window)
{
return window && [window isVisible] && ![window isMiniaturized] && [window canBecomeKeyWindow];
}
inline bool IsRegularWindowWithParent(Window* window, Window* parent)
{
return window &&
!window->IsClosed() &&
window->GetSettings().Type == WindowType::Regular &&
window->GetSettings().Parent == parent;
}
inline void MakeNextParentedWindowKey(NSWindow* hiddenWindow, Window* fallbackParent)
{
if (!fallbackParent || IsWindowInvalid(fallbackParent))
return;
for (NSWindow* candidate in [NSApp orderedWindows])
{
if (candidate == hiddenWindow || !CanMakeKeyWindow(candidate))
continue;
Window* candidateWindow = GetEngineWindow(candidate);
if (!IsRegularWindowWithParent(candidateWindow, fallbackParent))
continue;
[candidate makeKeyAndOrderFront:nil];
return;
}
NSWindow* parent = (NSWindow*)fallbackParent->GetNativePtr();
if (parent != hiddenWindow && CanMakeKeyWindow(parent))
[parent makeKeyAndOrderFront:nil];
}
KeyboardKeys GetKey(NSEvent* event)
{
switch ([event keyCode])
@@ -963,7 +1019,7 @@ void MacWindow::Show()
// Show
NSWindow* window = (NSWindow*)_window;
if (_settings.Parent)
if (ShouldAttachToParent(_settings))
{
NSWindow* parent = (NSWindow*)_settings.Parent->GetNativePtr();
[parent addChildWindow:window ordered:NSWindowAbove];
@@ -989,18 +1045,17 @@ void MacWindow::Hide()
// Hide (order out doesn't work for miniaturized windows)
NSWindow* window = (NSWindow*)_window;
const BOOL wasKey = [window isKeyWindow];
const BOOL wasKey = [window isKeyWindow] || [NSApp keyWindow] == window;
if ([window isMiniaturized])
[window close];
else
[window orderOut:nil];
// Transfer focus back to the parent when hiding popup
if (_settings.Parent && wasKey && _settings.Type != WindowType::Popup && _settings.Type != WindowType::Tooltip)
{
NSWindow* parent = (NSWindow*)_settings.Parent->GetNativePtr();
[parent makeKeyAndOrderFront:nil];
}
const bool shouldRestoreFocus = _settings.Parent &&
_settings.Type == WindowType::Regular &&
[NSApp isActive] &&
(wasKey || [NSApp keyWindow] == nil);
if (!IsClosed() && shouldRestoreFocus)
MakeNextParentedWindowKey(window, _settings.Parent);
// Base
WindowBase::Hide();
@@ -1009,23 +1064,27 @@ void MacWindow::Hide()
void MacWindow::Close(ClosingReason reason)
{
const BOOL wasKey = _window && [(NSWindow*)_window isKeyWindow];
NSWindow* window = (NSWindow*)_window;
const BOOL shouldRestoreFocus = window &&
_settings.Parent &&
_settings.Type == WindowType::Regular &&
[NSApp isActive] &&
([window isKeyWindow] || [NSApp keyWindow] == window);
WindowBase::Close(reason);
// Closing can be cancelled by managed Window.Closing handlers.
if (!IsClosed())
return;
if (NSWindow* window = (NSWindow*)_window)
{
if (window)
[window close];
}
if (_settings.Parent && wasKey && _settings.Type != WindowType::Popup && _settings.Type != WindowType::Tooltip)
{
NSWindow* parent = (NSWindow*)_settings.Parent->GetNativePtr();
[parent makeKeyAndOrderFront:nil];
}
const bool hasNoKeyWindow = _settings.Parent &&
_settings.Type == WindowType::Regular &&
[NSApp isActive] &&
[NSApp keyWindow] == nil;
if (shouldRestoreFocus || hasNoKeyWindow)
MakeNextParentedWindowKey(window, _settings.Parent);
}
void MacWindow::Minimize()
@@ -362,6 +362,13 @@ void WindowsWindow::BringToFront(bool force)
{
ASSERT(HasHWND());
HWND hWndInsertAfter = HWND_TOP;
uint32 flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
if (_settings.IsTopmost)
{
hWndInsertAfter = HWND_TOPMOST;
}
if (_settings.Type == WindowType::Regular)
{
if (IsIconic(_handle))
@@ -372,22 +379,15 @@ void WindowsWindow::BringToFront(bool force)
{
SetActiveWindow(_handle);
}
SetWindowPos(_handle, hWndInsertAfter, 0, 0, 0, 0, flags);
}
else
{
HWND hWndInsertAfter = HWND_TOP;
uint32 flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
if (!force)
{
flags |= SWP_NOACTIVATE;
}
if (_settings.IsTopmost)
{
hWndInsertAfter = HWND_TOPMOST;
}
SetWindowPos(_handle, hWndInsertAfter, 0, 0, 0, 0, flags);
}
}
@@ -614,6 +614,7 @@ void WindowsWindow::SetOpacity(const float opacity)
void WindowsWindow::Focus()
{
ASSERT(HasHWND());
BringToFront();
if (GetFocus() != _handle)
{
SetFocus(_handle);
@@ -1130,6 +1131,10 @@ LRESULT WindowsWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
}
}
break;
case WM_MOUSEACTIVATE:
if (_settings.Type == WindowType::Regular)
BringToFront();
break;
case WM_CREATE:
return 0;
case WM_SIZE: