Details
-
Bug
-
Resolution: Unresolved
-
P3: Somewhat important
-
None
-
5.15.1
-
None
-
Mac OS 11.0.1 ARM, XPC, Motion 5.5/Final Cut 10.5
Description
I am implementing a Qt based UI for an XPC service called by another application (Motion, Final Cut).
The main window of my app has ApplicatonModal flag set.
I observe that all transients: Menus, Popup menus, Tooltips еtc. end up behind the main window OR behind any other modal window opened on top of the main window.
Even though the root cause of this behaviour is still not clear to me, while searching for the solution I noticed that Qt internally has a notion of transientParent that it tracks. I also noticed that all transients are created as top-level cocoa windows and they are not connected to the windows hierarchy. This fact can add to the problem.
Cocoa has an NSWindow::addChildWindow:ordered: interface that can be used to establish the parent-child relationship between windows. For some reason unclear to me, Qt does not use this interface and resorts to all top-level windows instead. I am no expert in Qt, so you guys might shed some light on the idea behind this design decision.
Anyways, to workaround the problem that I'm fighting with, I came up with this:
--- qcocoawindow.mm.bak Mon May 11 18:45:08 2020 +++ qcocoawindow.mm Thu Feb 04 20:22:32 2021 @@ -192,6 +192,14 @@ qCDebug(lcQpaWindow) << "QCocoaWindow::~QCocoaWindow" << window(); QMacAutoReleasePool pool; + + if (window()->transientParent()) + { + QCocoaWindow *parentCocoaWindow = parentCocoaWindow = static_cast<QCocoaWindow *>(window()->transientParent()->handle()); + [parentCocoaWindow->nativeWindow() removeChildWindow:nativeWindow()]; + } + + [m_nsWindow makeFirstResponder:nil]; [m_nsWindow setContentView:nil]; if ([m_view superview]) @@ -324,7 +332,10 @@ QMacAutoReleasePool pool; QCocoaWindow *parentCocoaWindow = nullptr; if (window()->transientParent()) + { parentCocoaWindow = static_cast<QCocoaWindow *>(window()->transientParent()->handle()); + [parentCocoaWindow->nativeWindow() addChildWindow:nativeWindow() ordered:NSWindowAbove]; + } auto eventDispatcher = [] { return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher())); }}
This patch is pretty dirty. I would rather channel the call to setTransientParent() through QWindow to QPlatformWindow to QCocoaWindow. I don't want to complicate things until it is clear enough as I may be completely off and should look elsewhere, hope you guys can point me to the right direction.