1/**************************************************************************** 2** 3** Copyright (C) 2019 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the plugins of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40#include "qcocoawindowmanager.h" 41#include "qcocoawindow.h" 42 43#include <QtCore/private/qcore_mac_p.h> 44 45#include <QtGui/qguiapplication.h> 46#include <QtGui/qwindow.h> 47 48QT_BEGIN_NAMESPACE 49 50QCocoaWindowManager *QCocoaWindowManager::instance() 51{ 52 static auto *instance = new QCocoaWindowManager; 53 return instance; 54} 55 56QCocoaWindowManager::QCocoaWindowManager() 57{ 58 if (NSApp) { 59 initialize(); 60 } else { 61 static auto applicationDidFinishLaunching(QMacNotificationObserver(nil, 62 NSApplicationDidFinishLaunchingNotification, [this] { initialize(); })); 63 } 64} 65 66void QCocoaWindowManager::initialize() 67{ 68 Q_ASSERT(NSApp); 69 70 // Whenever the modalWindow property of NSApplication changes we have a new 71 // modal session running. Observing the app modal window instead of our own 72 // event dispatcher sessions allows us to track session started by native 73 // APIs as well. We need to check the initial state as well, in case there 74 // is already a modal session running. 75 static auto modalSessionObserver(QMacKeyValueObserver( 76 NSApp, @"modalWindow", [this] { modalSessionChanged(); }, 77 NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)); 78} 79 80void QCocoaWindowManager::modalSessionChanged() 81{ 82 // Make sure that no window is overlapping the modal window. This can 83 // happen for e.g. splash screens, which have the NSPopUpMenuWindowLevel. 84 for (auto *window : QGuiApplication::topLevelWindows()) { 85 auto *platformWindow = static_cast<QCocoaWindow*>(window->handle()); 86 if (!platformWindow) 87 continue; 88 89 auto naturalWindowLevel = platformWindow->windowLevel(window->flags()); 90 if (naturalWindowLevel > NSModalPanelWindowLevel) { 91 NSWindow *nativeWindow = platformWindow->nativeWindow(); 92 if (NSApp.modalWindow) { 93 // Lower window to that of the modal windows, but no less 94 nativeWindow.level = NSModalPanelWindowLevel; 95 if ([nativeWindow isVisible]) 96 [nativeWindow orderBack:nil]; 97 } else { 98 // Restore window's natural window level, whatever that was 99 nativeWindow.level = naturalWindowLevel; 100 } 101 } 102 } 103 104 // Our worksWhenModal implementation is declarative and will normally be picked 105 // up by AppKit when needed, but to make sure AppKit also reflects the state 106 // in the window tag, so that the window can be ordered front by clicking it, 107 // we need to explicitly call setWorksWhenModal. 108 for (id window in NSApp.windows) { 109 if ([window isKindOfClass:[QNSPanel class]]) { 110 auto *panel = static_cast<QNSPanel *>(window); 111 // Call setter to tell AppKit that our state has changed 112 [panel setWorksWhenModal:panel.worksWhenModal]; 113 } 114 } 115} 116 117static void initializeWindowManager() { Q_UNUSED(QCocoaWindowManager::instance()); } 118Q_CONSTRUCTOR_FUNCTION(initializeWindowManager) 119 120QT_END_NAMESPACE 121 122