5#include <AppKit/AppKit.h>
6#include <QuartzCore/QuartzCore.h>
20#include <QtCore/qfileinfo.h>
21#include <QtCore/private/qcore_mac_p.h>
23#include <private/qwindow_p.h>
24#include <qpa/qwindowsysteminterface.h>
25#include <qpa/qplatformscreen.h>
26#include <QtGui/private/qcoregraphics_p.h>
27#include <QtGui/private/qhighdpiscaling_p.h>
28#include <QtGui/private/qmetallayer_p.h>
35- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow;
38@implementation QNSWindowController {
39 QPointer<QCocoaWindow> m_platformWindow;
42- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow
46 if ((self = [super initWithWindowNibName:@
"QNSWindowController"]))
47 m_platformWindow = platformWindow;
54 QMacAutoReleasePool pool;
55 self.window = [m_platformWindow->createNSWindow() autorelease];
59 qCDebug(lcQpaWindow) <<
"Disposing of" << self.window <<
"for" << m_platformWindow;
69 defaultWindowWidth = 160,
70 defaultWindowHeight = 160
79 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
81 const QMetaObject *metaObject = QMetaType(qRegisterMetaType<
QCocoaWindow*>()).metaObject();
84 for (
int i = 0; i < metaObject->methodCount(); ++i) {
85 QMetaMethod method = metaObject->method(i);
86 const QString methodTag = QString::fromLatin1(method.tag());
87 if (!methodTag.startsWith(notificationHandlerPrefix))
90 const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
91 [center addObserverForName:notificationName.toNSString() object:nil queue:nil
92 usingBlock:^(NSNotification *notification) {
94 QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
95 if ([notification.object isKindOfClass:[NSWindow
class]]) {
96 NSWindow *nsWindow = notification.object;
97 for (
const QWindow *window : QGuiApplication::allWindows()) {
98 if (QCocoaWindow *cocoaWindow =
static_cast<QCocoaWindow *>(window->handle()))
99 if (cocoaWindow->nativeWindow() == nsWindow)
100 cocoaWindows += cocoaWindow;
102 }
else if ([notification.object isKindOfClass:[NSView
class]]) {
103 if (QNSView *qnsView = qnsview_cast(notification.object))
104 cocoaWindows += qnsView.platformWindow;
106 qCWarning(lcCocoaNotifications) <<
"Unhandled notification"
107 << notification.name <<
"for" << notification.object;
111 if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) {
112 QVector<QCocoaWindow *> debugWindows;
113 for (QCocoaWindow *cocoaWindow : cocoaWindows)
114 debugWindows += cocoaWindow;
115 qCDebug(lcCocoaNotifications) <<
"Forwarding" << qPrintable(notificationName) <<
116 "to" << debugWindows;
121 for (QCocoaWindow *cocoaWindow : cocoaWindows) {
122 if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
123 qCWarning(lcQpaWindow) <<
"Failed to invoke NSNotification callback for"
124 << notification.name <<
"on" << cocoaWindow;
130Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
135QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) : QPlatformWindow(win)
137 qCDebug(lcQpaWindow) <<
"QCocoaWindow::QCocoaWindow" << window();
140 m_view =
reinterpret_cast<NSView *>(nativeHandle);
147 qCDebug(lcQpaWindow) <<
"QCocoaWindow::initialize" << window();
149 QMacAutoReleasePool pool;
152 m_view = [[QNSView alloc] initWithCocoaWindow:
this];
158 auto initialGeometry = QPlatformWindow::initialGeometry(window(),
159 windowGeometry(), defaultWindowWidth, defaultWindowHeight);
165 if (QPlatformWindow::parent() || isSubWindow(window())) {
171 setGeometry(initialGeometry);
173 if (window()->flags() & Qt::ExpandedClientAreaHint) {
176 QRect frameGeometryWithFrame = QCocoaScreen::mapFromNative(
177 [NSWindow frameRectForContentRect:QCocoaScreen::mapToNative(initialGeometry)
178 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
179 if (qt_window_private(window())->positionPolicy == QWindowPrivate::WindowFrameExclusive)
180 initialGeometry = frameGeometryWithFrame;
182 initialGeometry.setSize(frameGeometryWithFrame.size());
190 QPlatformWindow::setGeometry(initialGeometry);
198 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
200 m_safeAreaInsetsObserver = QMacKeyValueObserver(
201 m_view, @
"safeAreaInsets", [
this] {
204 QMetaObject::invokeMethod(
this, [
this]{
205 updateSafeAreaMarginsIfNeeded();
206 }, Qt::QueuedConnection);
207 }, NSKeyValueObservingOptionNew);
213 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
223 qCDebug(lcQpaWindow) <<
"QCocoaWindow::~QCocoaWindow" << window();
225 QMacAutoReleasePool pool;
227 if (window()->flags() & Qt::ExpandedClientAreaHint) {
231 auto readjustedGeometry = geometry();
232 QRect contentRectWithFrame = QCocoaScreen::mapFromNative(
233 [NSWindow contentRectForFrameRect:QCocoaScreen::mapToNative(geometry())
234 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
235 readjustedGeometry = contentRectWithFrame;
236 setGeometry(readjustedGeometry, QWindowPrivate::PositionPolicy::WindowFrameExclusive);
241 if (!isForeignWindow() || QPlatformWindow::parent())
242 [m_view removeFromSuperview];
244 m_safeAreaInsetsObserver = {};
249 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
252 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
253 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
255 vulcanInstance->destroySurface(m_vulkanSurface);
262 [NSNotificationCenter.defaultCenter
263 postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
271 static_cast<
QCocoaScreen *>(screen())->maybeStopDisplayLink();
276 auto format = window()->requestedFormat();
277 if (
auto *view = qnsview_cast(m_view); view.colorSpace) {
278 auto colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(view.colorSpace.ICCProfileData));
279 if (!colorSpace.isValid()) {
280 qCWarning(lcQpaWindow) <<
"Failed to parse ICC profile for" << view.colorSpace
281 <<
"with ICC data" << view.colorSpace.ICCProfileData;
283 format.setColorSpace(colorSpace);
290 return ![m_view isKindOfClass:[QNSView
class]];
295 return QPlatformWindow::geometry();
302 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
305 return QCocoaScreen::mapFromNative(contentRect).toRect();
307 return QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
312
313
314
315
316
317
318
328 if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
331 return m_normalGeometry;
339 if (windowState() != Qt::WindowNoState)
342 m_normalGeometry = geometry();
348 setGeometry(rect, qt_window_private(window())->positionPolicy);
353 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setGeometry" << window() << rectIn << positionPolicy;
354 QMacAutoReleasePool pool;
357 if (positionPolicy == QWindowPrivate::WindowFrameInclusive) {
361 const QMargins margins = frameMargins();
362 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
363 qCDebug(lcQpaWindow) <<
"Adjusted content geometry to" << rect <<
"by removing frame margins" << margins;
366 QPlatformWindow::setGeometry(rect);
368 const QRect originalGeometry = actualGeometry();
376 NSRect bounds = QCocoaScreen::mapToNative(rect);
377 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
380 m_view.frame = QCocoaWindow::mapToNative(rect, m_view.superview);
383 if (actualGeometry() == originalGeometry) {
390 qCDebug(lcQpaWindow) <<
"Native geometry didn't change. Reporting geometry change manually.";
397 QMacAutoReleasePool pool;
404 QMarginsF viewSafeAreaMargins = {
405 m_view.safeAreaInsets.left,
406 m_view.safeAreaInsets.top,
407 m_view.safeAreaInsets.right,
408 m_view.safeAreaInsets.bottom
415 auto screenRect = m_view.window.screen.frame;
416 auto screenInsets = m_view.window.screen.safeAreaInsets;
417 auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect(
418 NSMinX(screenRect) + screenInsets.left,
419 NSMinY(screenRect) + screenInsets.bottom,
420 NSWidth(screenRect) - screenInsets.left - screenInsets.right,
421 NSHeight(screenRect) - screenInsets.top - screenInsets.bottom
424 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
425 [m_view.window convertRectToScreen:
426 [m_view convertRect:m_view.bounds toView:nil]]
432 QMarginsF screenSafeAreaMargins = {
433 qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left),
434 qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top),
435 qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right),
436 qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom)
439 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
444 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
445 m_lastReportedSafeAreaMargins = safeAreaMargins();
453 if (!(
NSEvent.pressedMouseButtons == 1))
458 NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown
459 location:m_view.window.mouseLocationOutsideOfEventStream
460 modifierFlags:NSEvent.modifierFlags
461 timestamp:NSProcessInfo.processInfo.systemUptime
462 windowNumber:m_view.window.windowNumber context:nil
463 eventNumber:0 clickCount:1 pressure:1.0];
465 [m_view.window performWindowDragWithEvent:mouseEvent];
471 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setVisible" << window() << visible;
484 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping setting visibility";
488 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
489 qCDebug(lcQpaWindow) <<
"No change in visible status. Ignoring.";
494 qCWarning(lcQpaWindow) <<
"Already setting window visible!";
498 QScopedValueRollback<
bool> rollback(m_inSetVisible,
true);
500 QMacAutoReleasePool pool;
502 if (window()->transientParent())
503 parentCocoaWindow =
static_cast<
QCocoaWindow *>(window()->transientParent()->handle());
505 auto eventDispatcher = [] {
506 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
519 if (parentCocoaWindow) {
522 setGeometry(windowGeometry());
524 if (window()->type() == Qt::Popup) {
527 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
528 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
529 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
530 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
531 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
540 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
544 applyWindowState(window()->windowStates());
546 if (window()->windowState() != Qt::WindowMinimized) {
547 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
549 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
550 if (!nativeParentWindow.attachedSheet)
551 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
553 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
554 }
else if (window()->modality() == Qt::ApplicationModal) {
556 eventDispatcher()->beginModalSession(window());
557 }
else if (m_view.window.canBecomeKeyWindow) {
558 bool shouldBecomeKeyNow = !NSApp.modalWindow
559 || m_view.window.worksWhenModal
560 || !NSApp.modalWindow.visible;
564 if ([m_view.window isKindOfClass:[NSPanel
class]])
565 shouldBecomeKeyNow &= !(
static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
567 if (shouldBecomeKeyNow)
568 [m_view.window makeKeyAndOrderFront:nil];
570 [m_view.window orderFront:nil];
572 [m_view.window orderFront:nil];
579 if (eventDispatcher()->hasModalSession())
580 eventDispatcher()->endModalSession(window());
581 else if ([m_view.window isSheet])
582 [m_view.window.sheetParent endSheet:m_view.window];
589 [m_view.window orderOut:nil];
591 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
596 NSWindow *mainWindow = [NSApp mainWindow];
597 if (mainWindow && [mainWindow canBecomeKeyWindow])
598 [mainWindow makeKeyWindow];
612 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
613 && m_view.nextValidKeyView.window != m_view.window) {
614 qCDebug(lcQpaWindow) <<
"Detected nextValidKeyView" << m_view.nextValidKeyView
615 <<
"in different window" << m_view.nextValidKeyView.window
616 <<
"Resetting" << m_view.window <<
"first responder to nil.";
617 [m_view.window makeFirstResponder:nil];
622 if (parentCocoaWindow && window()->type() == Qt::Popup) {
623 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
624 if (m_resizableTransientParent
625 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
627 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
632NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
634 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
636 NSInteger windowLevel = NSNormalWindowLevel;
638 if (type == Qt::Tool)
639 windowLevel = NSFloatingWindowLevel;
640 else if ((type & Qt::Popup) == Qt::Popup)
641 windowLevel = NSPopUpMenuWindowLevel;
644 if (flags & Qt::WindowStaysOnTopHint)
645 windowLevel = NSModalPanelWindowLevel;
647 if (type == Qt::ToolTip)
648 windowLevel = NSScreenSaverWindowLevel;
650 auto *transientParent = window()->transientParent();
651 if (transientParent && transientParent->handle()) {
671 auto *transientCocoaWindow =
static_cast<QCocoaWindow *>(transientParent->handle());
672 auto *nsWindow = transientCocoaWindow->nativeWindow();
677 if (type != Qt::Window)
678 windowLevel = qMax(windowLevel, nsWindow.level);
684NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
const
686 const Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
692 NSUInteger styleMask = [&]{
694 if (flags & Qt::FramelessWindowHint)
695 return NSWindowStyleMaskBorderless;
698 if (windowIsPopupType(type))
699 return NSWindowStyleMaskBorderless;
701 if (flags & Qt::CustomizeWindowHint) {
704 return flags & Qt::WindowTitleHint
705 ? NSWindowStyleMaskTitled
706 : NSWindowStyleMaskBorderless;
709 return NSWindowStyleMaskTitled;
718 styleMask |= NSWindowStyleMaskClosable
719 | NSWindowStyleMaskMiniaturizable;
721 if (type != Qt::Popup)
722 styleMask |= NSWindowStyleMaskResizable;
724 if (type == Qt::Tool)
725 styleMask |= NSWindowStyleMaskUtilityWindow;
727 if (flags & Qt::ExpandedClientAreaHint)
728 styleMask |= NSWindowStyleMaskFullSizeContentView;
731 styleMask |= (m_view.window.styleMask & (
732 NSWindowStyleMaskFullScreen
733 | NSWindowStyleMaskUnifiedTitleAndToolbar
734 | NSWindowStyleMaskDocModalWindow
735 | NSWindowStyleMaskNonactivatingPanel
736 | NSWindowStyleMaskHUDWindow));
741bool QCocoaWindow::isFixedSize()
const
743 return windowMinimumSize().isValid() && windowMaximumSize().isValid()
744 && windowMinimumSize() == windowMaximumSize();
747void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
749 if (!isContentView())
752 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
753 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
754 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
755 { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
758 bool hideButtons =
true;
759 for (
const auto &[button, buttonHint] : buttons) {
762 if (button == NSWindowMiniaturizeButton)
763 enabled = window()->type() != Qt::Dialog;
766 if (windowFlags & Qt::CustomizeWindowHint)
767 enabled = windowFlags & buttonHint;
771 if (button == NSWindowZoomButton && isFixedSize())
777 if (button == NSWindowCloseButton && enabled
778 && QWindowPrivate::get(window())->blockedByModalWindow) {
785 [m_view.window standardWindowButton:button].enabled = enabled;
786 hideButtons &= !enabled;
790 for (
const auto &[button, buttonHint] : buttons)
791 [m_view.window standardWindowButton:button].hidden = hideButtons;
794void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
800 QMacAutoReleasePool pool;
802 if (!isContentView())
805 qCDebug(lcQpaWindow) <<
"Setting" << flags <<
"for" << window();
816 QScopedValueRollback<
bool> geometryChangeBlocker(m_inSetStyleMask,
true);
818 const auto newMask = windowStyleMask(flags);
819 const bool expandedClientAreaChanged = (
820 (newMask & NSWindowStyleMaskFullSizeContentView) !=
821 (m_view.window.styleMask & NSWindowStyleMaskFullSizeContentView));
822 const auto frameGeometry = window()->frameGeometry();
824 m_view.window.styleMask = newMask;
826 if (expandedClientAreaChanged) {
828 setGeometry(frameGeometry.marginsRemoved(frameMargins()),
829 QWindowPrivate::PositionPolicy::WindowFrameExclusive);
833 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
834 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
835 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
836 const bool enableFullScreen = m_view.window.qt_fullScreen
837 || !(flags & Qt::CustomizeWindowHint)
838 || (flags & Qt::WindowFullscreenButtonHint);
839 if (enableFullScreen) {
840 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
841 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
843 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
844 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
846 m_view.window.collectionBehavior = behavior;
851 m_view.window.level =
this->windowLevel(flags);
853 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
855 if (!(flags & Qt::FramelessWindowHint))
856 setWindowTitle(window()->title());
858 updateTitleBarButtons(flags);
867 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
868 if (m_view.window.ignoresMouseEvents != ignoreMouse)
869 m_view.window.ignoresMouseEvents = ignoreMouse;
871 m_view.window.titlebarAppearsTransparent = flags & Qt::NoTitleBarBackgroundHint;
877
878
879
880
881
882
883void QCocoaWindow::setWindowState(Qt::WindowStates state)
885 if (window()->isVisible())
886 applyWindowState(state);
889void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
891 if (!isContentView())
894 const Qt::WindowState currentState = QWindowPrivate::effectiveState(windowState());
895 const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
897 if (newState == currentState)
900 qCDebug(lcQpaWindow) <<
"Applying" << newState <<
"to" << window() <<
"in" << currentState;
902 const NSSize contentSize = m_view.frame.size;
903 if (contentSize.width <= 0 || contentSize.height <= 0) {
906 qWarning(
"invalid window content view size, check your window geometry");
907 handleWindowStateChanged(HandleUnconditionally);
911 const NSWindow *nsWindow = m_view.window;
913 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
914 && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
915 qWarning() << window()->type() <<
"windows cannot be made" << newState;
916 handleWindowStateChanged(HandleUnconditionally);
920 const id sender = nsWindow;
923 switch (currentState) {
924 case Qt::WindowMinimized:
925 [nsWindow deminiaturize:sender];
930 case Qt::WindowFullScreen: {
941 if (newState == windowState())
945 case Qt::WindowFullScreen:
948 case Qt::WindowMaximized:
951 case Qt::WindowMinimized:
952 [nsWindow miniaturize:sender];
954 case Qt::WindowNoState:
955 if (windowState() == Qt::WindowMaximized)
963Qt::WindowStates QCocoaWindow::windowState()
const
965 Qt::WindowStates states = Qt::WindowNoState;
966 NSWindow *window = m_view.window;
968 if (window.miniaturized)
969 states |= Qt::WindowMinimized;
973 if (window.qt_fullScreen) {
974 states |= Qt::WindowFullScreen;
975 }
else if ((window.zoomed && !isTransitioningToFullScreen())
976 || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) {
977 states |= Qt::WindowMaximized;
986void QCocoaWindow::toggleMaximized()
988 const NSWindow *window = m_view.window;
992 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
993 window.styleMask |= NSWindowStyleMaskResizable;
995 const id sender = window;
996 [window zoom:sender];
999 window.styleMask &= ~NSWindowStyleMaskResizable;
1002void QCocoaWindow::windowWillZoom()
1004 updateNormalGeometry();
1007void QCocoaWindow::toggleFullScreen()
1009 const NSWindow *window = m_view.window;
1014 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1016 const id sender = window;
1017 [window toggleFullScreen:sender];
1020void QCocoaWindow::windowWillEnterFullScreen()
1022 if (!isContentView())
1025 updateNormalGeometry();
1030 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1033bool QCocoaWindow::isTransitioningToFullScreen()
const
1035 NSWindow *window = m_view.window;
1036 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
1039void QCocoaWindow::windowDidEnterFullScreen()
1041 if (!isContentView())
1044 Q_ASSERT_X(m_view.window.qt_fullScreen,
"QCocoaWindow",
1045 "FullScreen category processes window notifications first");
1048 setWindowFlags(window()->flags());
1050 handleWindowStateChanged();
1053void QCocoaWindow::windowWillExitFullScreen()
1055 if (!isContentView())
1060 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1063void QCocoaWindow::windowDidExitFullScreen()
1065 if (!isContentView())
1068 Q_ASSERT_X(!m_view.window.qt_fullScreen,
"QCocoaWindow",
1069 "FullScreen category processes window notifications first");
1072 setWindowFlags(window()->flags());
1074 Qt::WindowState requestedState = window()->windowState();
1077 handleWindowStateChanged();
1079 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
1082 applyWindowState(requestedState);
1086void QCocoaWindow::windowDidMiniaturize()
1088 if (!isContentView())
1091 handleWindowStateChanged();
1094void QCocoaWindow::windowDidDeminiaturize()
1096 if (!isContentView())
1099 Qt::WindowState requestedState = window()->windowState();
1101 handleWindowStateChanged();
1103 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
1106 applyWindowState(requestedState);
1110void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
1112 Qt::WindowStates currentState = windowState();
1113 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
1116 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleWindowStateChanged" <<
1117 m_lastReportedWindowState <<
"-->" << currentState;
1119 QPointer<QCocoaWindow> destructionGuard =
this;
1120 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
1121 window(), currentState, m_lastReportedWindowState);
1122 if (!destructionGuard)
1124 m_lastReportedWindowState = currentState;
1129void QCocoaWindow::setWindowTitle(
const QString &title)
1131 QMacAutoReleasePool pool;
1133 if (!isContentView())
1136 m_view.window.title = title.toNSString();
1138 if (title.isEmpty() && !window()->filePath().isEmpty()) {
1140 setWindowFilePath(window()->filePath());
1144void QCocoaWindow::setWindowFilePath(
const QString &filePath)
1146 QMacAutoReleasePool pool;
1148 if (!isContentView())
1151 if (window()->title().isNull())
1152 [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
1154 m_view.window.representedFilename = filePath.toNSString();
1157 setWindowIcon(window()->icon());
1160void QCocoaWindow::setWindowIcon(
const QIcon &icon)
1162 QMacAutoReleasePool pool;
1164 if (!isContentView())
1167 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
1173 if (icon.isNull()) {
1174 iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename];
1178 auto fallbackSize = QSizeF::fromCGSize(iconButton.frame.size) * qGuiApp->devicePixelRatio();
1179 iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize.toSize()];
1183void QCocoaWindow::setAlertState(
bool enabled)
1185 if (m_alertRequest == NoAlertRequest && enabled) {
1186 m_alertRequest = [NSApp requestUserAttention:NSCriticalRequest];
1187 }
else if (m_alertRequest != NoAlertRequest && !enabled) {
1188 [NSApp cancelUserAttentionRequest:m_alertRequest];
1189 m_alertRequest = NoAlertRequest;
1193bool QCocoaWindow::isAlertState()
const
1195 return m_alertRequest != NoAlertRequest;
1198void QCocoaWindow::raise()
1200 qCDebug(lcQpaWindow) <<
"QCocoaWindow::raise" << window();
1203 if (isContentView()) {
1204 if (m_view.window.visible) {
1211 QMacAutoReleasePool pool;
1212 [m_view.window orderFront:m_view.window];
1214 static bool raiseProcess = qt_mac_resolveOption(
true,
"QT_MAC_SET_RAISE_PROCESS");
1216 [NSApp activateIgnoringOtherApps:YES];
1219 [m_view.superview addSubview:m_view positioned:NSWindowAbove relativeTo:nil];
1223void QCocoaWindow::lower()
1225 qCDebug(lcQpaWindow) <<
"QCocoaWindow::lower" << window();
1227 if (isContentView()) {
1228 if (m_view.window.visible)
1229 [m_view.window orderBack:m_view.window];
1231 [m_view.superview addSubview:m_view positioned:NSWindowBelow relativeTo:nil];
1235bool QCocoaWindow::isExposed()
const
1237 return !m_exposedRect.isEmpty();
1241
1242
1243
1244
1245
1246bool QCocoaWindow::isEmbedded()
const
1252 return m_isEmbedded;
1255bool QCocoaWindow::isOpaque()
const
1259 static GLint openglSourfaceOrder = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
1261 bool translucent = window()->format().alphaBufferSize() > 0
1262 || window()->opacity() < 1
1263 || !window()->mask().isEmpty()
1264 || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
1265 return !translucent;
1268void QCocoaWindow::propagateSizeHints()
1270 QMacAutoReleasePool pool;
1271 if (!isContentView())
1274 qCDebug(lcQpaWindow) <<
"QCocoaWindow::propagateSizeHints" << window()
1275 <<
"min:" << windowMinimumSize() <<
"max:" << windowMaximumSize()
1276 <<
"increment:" << windowSizeIncrement()
1277 <<
"base:" << windowBaseSize();
1279 const NSWindow *window = m_view.window;
1282 QSize minimumSize = windowMinimumSize();
1283 if (!minimumSize.isValid())
1284 minimumSize = QSize(0, 0);
1285 window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
1288 window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
1291 updateTitleBarButtons(
this->window()->flags());
1295 QSize sizeIncrement = windowSizeIncrement();
1296 if (sizeIncrement.isEmpty())
1297 sizeIncrement = QSize(1, 1);
1298 window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
1300 QRect rect = geometry();
1301 QSize baseSize = windowBaseSize();
1302 if (!baseSize.isNull() && baseSize.isValid())
1303 [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
1306void QCocoaWindow::setOpacity(qreal level)
1308 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setOpacity" << level;
1309 if (!isContentView())
1312 m_view.window.alphaValue = level;
1315void QCocoaWindow::setMask(
const QRegion ®ion)
1317 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setMask" << window() << region;
1319 if (!region.isEmpty()) {
1320 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
1321 for (
const QRect &r : region)
1322 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
1323 CAShapeLayer *maskLayer = [CAShapeLayer layer];
1324 maskLayer.path = maskPath;
1325 m_view.layer.mask = maskLayer;
1327 m_view.layer.mask = nil;
1331bool QCocoaWindow::setKeyboardGrabEnabled(
bool)
1336bool QCocoaWindow::setMouseGrabEnabled(
bool)
1341WId QCocoaWindow::winId()
const
1346void QCocoaWindow::setParent(
const QPlatformWindow *parentWindow)
1348 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0);
1351 recreateWindowIfNeeded();
1353 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1356NSView *QCocoaWindow::view()
const
1361NSWindow *QCocoaWindow::nativeWindow()
const
1363 return m_view.window;
1366void QCocoaWindow::updateEmbeddedState()
1368 const bool wasEmbedded = m_isEmbedded;
1370 if (QPlatformWindow::parent())
1373 if (!m_view.superview)
1378 auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1379 if (m_view == qtWindowController.window.contentView)
1384 if (m_isEmbedded != wasEmbedded) {
1385 qCDebug(lcQpaWindow) <<
"Updated embedded state from"
1386 << wasEmbedded <<
"to" << m_isEmbedded;
1392void QCocoaWindow::viewDidChangeFrame()
1399 handleGeometryChange();
1403
1404
1405
1406
1407
1408
1409void QCocoaWindow::viewDidChangeGlobalFrame()
1411 [m_view setNeedsDisplay:YES];
1415
1416
1417
1418
1419
1420void QCocoaWindow::viewDidMoveToSuperview(NSView *previousSuperview)
1422 qCDebug(lcQpaWindow) <<
"Done re-parenting" << m_view
1423 <<
"from" << previousSuperview <<
"into" << m_view.superview;
1426 updateEmbeddedState();
1430 handleGeometryChange();
1432 if (m_view.superview)
1433 [m_view setNeedsDisplay:YES];
1444 if (m_view.superview && !m_view.superview.flipped && !isContentView()) {
1445 if (m_view.autoresizingMask == NSViewNotSizable) {
1446 qCDebug(lcQpaWindow) <<
"Setting auto resizing mask on" << m_view
1447 <<
"in non-flipped superview to maintain stable y-positioning";
1448 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1449 m_view.autoresizingMask = NSViewMinYMargin;
1451 }
else if (previousSuperview && !previousSuperview.flipped
1452 && m_view.autoresizingMask == NSViewMinYMargin) {
1457 qCDebug(lcQpaWindow) <<
"Clearing auto resizing mask on" << m_view
1458 <<
"as explicit stable y-positioning is no longer needed";
1459 m_view.autoresizingMask = NSViewNotSizable;
1464
1465
1466
1467
1468void QCocoaWindow::viewDidMoveToWindow(NSWindow *previousWindow)
1470 qCDebug(lcQpaWindow) <<
"Done moving" << m_view
1471 <<
"from" << previousWindow <<
"to" << m_view.window;
1473 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController)) {
1474 qCDebug(lcQpaWindow) <<
"Retaining new window controller" << qtWindowController;
1475 [qtWindowController retain];
1477 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(previousWindow.windowController)) {
1478 qCDebug(lcQpaWindow) <<
"Releasing old window controller" << qtWindowController;
1479 [qtWindowController autorelease];
1485 auto *previousScreen = previousWindow ? QCocoaScreen::get(previousWindow.screen) :
nullptr;
1486 auto *currentScreen = m_view.window ? QCocoaScreen::get(m_view.window.screen) :
nullptr;
1487 if (currentScreen && currentScreen != previousScreen)
1488 windowDidChangeScreen();
1498void QCocoaWindow::windowDidMove()
1500 if (!isContentView())
1503 handleGeometryChange();
1506 handleWindowStateChanged();
1509void QCocoaWindow::windowDidResize()
1511 if (!isContentView())
1514 handleGeometryChange();
1516 if (!m_view.inLiveResize)
1517 handleWindowStateChanged();
1520void QCocoaWindow::windowWillStartLiveResize()
1525 m_inLiveResize =
true;
1528bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1533 return !m_inLiveResize;
1536void QCocoaWindow::windowDidEndLiveResize()
1538 m_inLiveResize =
false;
1540 if (!isContentView())
1543 handleWindowStateChanged();
1546void QCocoaWindow::windowDidBecomeKey()
1550 if (m_view.window.firstResponder != m_view)
1553 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1554 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1556 if (windowIsPopupType()) {
1557 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1562 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1563 window(), Qt::ActiveWindowFocusReason);
1566void QCocoaWindow::windowDidResignKey()
1570 if (m_view.window.firstResponder != m_view)
1573 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1574 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1583 NSWindow *newKeyWindow = [NSApp keyWindow];
1584 if (newKeyWindow && newKeyWindow != m_view.window
1585 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1586 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1587 <<
"is Qt window. Deferring focus window change.";
1592 if (!windowIsPopupType()) {
1593 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1594 nullptr, Qt::ActiveWindowFocusReason);
1598void QCocoaWindow::windowDidOrderOnScreen()
1602 if (QWindowPrivate::get(window())->isPopup()) {
1603 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1604 (QGuiApplicationPrivate::currentMouseWindow);
1607 [m_view setNeedsDisplay:YES];
1610void QCocoaWindow::windowDidOrderOffScreen()
1612 handleExposeEvent(QRegion());
1615 if (window()->type() & Qt::Window) {
1616 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1617 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1618 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1619 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1621 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1622 (windowUnderMouse, windowPoint, screenPoint);
1628void QCocoaWindow::windowDidChangeOcclusionState()
1634 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1635 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1636 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1639 [m_view setNeedsDisplay:YES];
1641 handleExposeEvent(QRegion());
1644void QCocoaWindow::windowDidChangeScreen()
1650 NSScreen *nsScreen = m_view.window.screen;
1652 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1653 QCocoaScreen::updateScreens();
1655 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1656 auto *currentScreen = QCocoaScreen::get(nsScreen);
1658 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1671 QPointer<QCocoaWindow> destructionGuard =
this;
1672 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1673 window(), currentScreen ? currentScreen->screen() :
nullptr);
1674 if (!destructionGuard)
1677 if (currentScreen && hasPendingUpdateRequest()) {
1682 currentScreen->requestUpdate();
1687 previousScreen->maybeStopDisplayLink();
1692bool QCocoaWindow::windowShouldClose()
1694 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1706 [[m_view.window retain] autorelease];
1708 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1713void QCocoaWindow::handleGeometryChange()
1715 const QRect newGeometry = actualGeometry();
1717 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1718 <<
"current" << geometry() <<
"new" << newGeometry;
1723 if (m_inSetStyleMask && !m_view.window) {
1724 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1731 if (!m_initialized) {
1733 QPlatformWindow::setGeometry(newGeometry);
1734 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1738 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1741 updateSafeAreaMarginsIfNeeded();
1745 if (!m_inSetGeometry)
1746 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1749void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1762 if (m_view.window.visible && m_view.window.screen
1763 && !geometry().size().isEmpty() && !region.isEmpty()
1764 && !m_view.hiddenOrHasHiddenAncestor) {
1765 m_exposedRect = region.boundingRect();
1767 m_exposedRect = QRect();
1770 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1772 QPointer<QCocoaWindow> destructionGuard =
this;
1773 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1774 if (!destructionGuard)
1778 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1783bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1785 if (type == Qt::Widget)
1786 type = window()->type();
1787 if (type == Qt::Tool)
1790 return ((type & Qt::Popup) == Qt::Popup);
1794
1795
1796
1797
1798
1799
1800
1801
1802bool QCocoaWindow::isContentView()
const
1804 return m_view.window.contentView == m_view;
1808
1809
1810
1811
1812
1813
1814
1815void QCocoaWindow::recreateWindowIfNeeded()
1817 QMacAutoReleasePool pool;
1819 auto *parentWindow =
static_cast<QCocoaWindow *>(QPlatformWindow::parent());
1821 const bool shouldManageTopLevelWindow = !parentWindow
1822 && !isSubWindow(window()) && !isForeignWindow();
1824 if (shouldManageTopLevelWindow) {
1825 auto *windowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1826 const bool isControllerContentView = m_view == windowController.window.contentView;
1827 const bool windowClassIsCorrect = [m_view.window isKindOfClass:windowClass()];
1829 if (!isControllerContentView || !windowClassIsCorrect) {
1830 qCDebug(lcQpaWindow) <<
"Creating new window controller for" << m_view;
1831 windowController = [[QNSWindowController alloc] initWithCocoaWindow:
this];
1833 m_view.postsFrameChangedNotifications = NO;
1834 windowController.window.contentView = m_view;
1835 m_view.postsFrameChangedNotifications = YES;
1838 [windowController release];
1840 qCDebug(lcQpaWindow) <<
"Re-using existing window controller" << windowController;
1842 }
else if (parentWindow) {
1843 if (m_view.superview != parentWindow->view()) {
1844 qCDebug(lcQpaWindow) <<
"Reparenting view to new parent" << parentWindow;
1845 [parentWindow->view() addSubview:m_view];
1847 }
else if (qnsview_cast(m_view.superview) && !isEmbedded()) {
1848 qCDebug(lcQpaWindow) <<
"Removing view from superview" << m_view.superview;
1849 [m_view removeFromSuperview];
1853Class QCocoaWindow::windowClass()
const
1855 const auto type = window()->type();
1856 return ((type & Qt::Popup) == Qt::Popup
1857 || (type & Qt::Dialog) == Qt::Dialog) ?
1858 QNSPanel.
class : QNSWindow.
class;
1861void QCocoaWindow::requestUpdate()
1863 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1864 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1866 if (updatesWithDisplayLink()) {
1867 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1868 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1869 QPlatformWindow::requestUpdate();
1873 QPlatformWindow::requestUpdate();
1877bool QCocoaWindow::updatesWithDisplayLink()
const
1880 return format().swapInterval() != 0;
1883void QCocoaWindow::deliverUpdateRequest()
1885 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1886 QScopedValueRollback<
bool> blocker(m_deliveringUpdateRequest,
true);
1888 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1893 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1894 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1895 <<
"due to" << qtMetalLayer <<
"needing display";
1901 qtMetalLayer.displayLock.unlock();
1904 QPlatformWindow::deliverUpdateRequest();
1907void QCocoaWindow::requestActivateWindow()
1909 QMacAutoReleasePool pool;
1910 [m_view.window makeFirstResponder:m_view];
1911 [m_view.window makeKeyWindow];
1915
1916
1917void QCocoaWindow::closeAllPopups()
1919 QGuiApplicationPrivate::instance()->closeAllPopups();
1921 removePopupMonitor();
1924void QCocoaWindow::removePopupMonitor()
1926 if (s_globalMouseMonitor) {
1927 [NSEvent removeMonitor:s_globalMouseMonitor];
1928 s_globalMouseMonitor = nil;
1930 if (s_applicationActivationObserver) {
1931 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1932 s_applicationActivationObserver = nil;
1936void QCocoaWindow::setupPopupMonitor()
1943 if (!s_globalMouseMonitor) {
1945 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1946 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1947 | NSEventMaskMouseMoved;
1948 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1949 handler:^(NSEvent *e){
1950 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1951 removePopupMonitor();
1954 const auto eventType = cocoaEvent2QtMouseEvent(e);
1955 if (eventType == QEvent::MouseMove) {
1956 if (s_windowUnderMouse) {
1957 QWindow *window = s_windowUnderMouse->window();
1958 const auto button = cocoaButton2QtButton(e);
1959 const auto buttons = currentlyPressedMouseButtons();
1960 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1961 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1962 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1963 buttons, button, eventType);
1973 if (!s_applicationActivationObserver) {
1974 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1975 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1976 object:nil queue:nil
1977 usingBlock:^(NSNotification *){
1983QCocoaNSWindow *QCocoaWindow::createNSWindow()
1985 QMacAutoReleasePool pool;
1987 Qt::WindowType type = window()->type();
1988 Qt::WindowFlags flags = window()->flags();
1990 QRect rect = geometry();
1992 QScreen *targetScreen =
nullptr;
1993 for (QScreen *screen : QGuiApplication::screens()) {
1994 if (screen->geometry().contains(rect.topLeft())) {
1995 targetScreen = screen;
2000 NSWindowStyleMask styleMask = windowStyleMask(flags);
2002 if (!targetScreen) {
2003 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
2004 targetScreen = QGuiApplication::primaryScreen();
2008 styleMask = NSWindowStyleMaskBorderless;
2011 rect.translate(-targetScreen->geometry().topLeft());
2012 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
2013 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
2015 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
2020 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
2021 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
2022 <<
"Adjusting style mask during creation";
2023 styleMask = NSWindowStyleMaskBorderless;
2028 QCocoaNSWindow *nsWindow = [[windowClass() alloc] initWithContentRect:contentRect
2033 backing:NSBackingStoreBuffered defer:NO
2034 screen:targetCocoaScreen->nativeScreen()
2035 platformWindow:
this];
2039 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
2044 if (!resultingScreen)
2045 resultingScreen = targetCocoaScreen;
2047 if (resultingScreen->screen() != window()->screen()) {
2048 QWindowSystemInterface::handleWindowScreenChanged<
2049 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
2052 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
2053 [](QNSWindowDelegate *delegate) { [delegate release]; });
2054 nsWindow.delegate = sharedDelegate.get();
2060 [nsWindow setReleasedWhenClosed:NO];
2062 if (alwaysShowToolWindow()) {
2063 static dispatch_once_t onceToken;
2064 dispatch_once(&onceToken, ^{
2065 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
2066 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2067 name:NSApplicationWillResignActiveNotification object:nil];
2068 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2069 name:NSApplicationWillBecomeActiveNotification object:nil];
2073 nsWindow.restorable = NO;
2074 nsWindow.level = windowLevel(flags);
2075 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
2077 if ([nsWindow isKindOfClass:NSPanel.
class]) {
2079 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
2082 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
2083 | NSWindowCollectionBehaviorMoveToActiveSpace;
2085 if ((type & Qt::Popup) == Qt::Popup) {
2086 nsWindow.hasShadow = YES;
2087 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
2088 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
2089 setupPopupMonitor();
2100 if (
auto *qtView = qnsview_cast(m_view))
2101 nsWindow.colorSpace = qtView.colorSpace;
2106bool QCocoaWindow::alwaysShowToolWindow()
const
2108 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2111bool QCocoaWindow::setWindowModified(
bool modified)
2113 QMacAutoReleasePool pool;
2115 if (!isContentView())
2118 m_view.window.documentEdited = modified;
2122void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2127QCocoaMenuBar *QCocoaWindow::menubar()
const
2132void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2134 QMacAutoReleasePool pool;
2137 if (isForeignWindow())
2140 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2142 QNSView *view = qnsview_cast(m_view);
2143 if (cursor == view.cursor)
2146 view.cursor = cursor;
2152 [m_view.window invalidateCursorRectsForView:m_view];
2159 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2160 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2161 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2162 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2163 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2164 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2165 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2166 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2167 location:locationInWindow modifierFlags:0 timestamp:0
2168 windowNumber:m_view.window.windowNumber context:nil
2169 eventNumber:0 trackingNumber:0 userData:0]];
2173void QCocoaWindow::registerTouch(
bool enable)
2175 m_registerTouchCount += enable ? 1 : -1;
2176 if (enable && m_registerTouchCount == 1)
2177 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2178 else if (m_registerTouchCount == 0)
2179 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2182qreal QCocoaWindow::devicePixelRatio()
const
2189 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2190 return backingSize.height;
2193QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2195 QWindow *targetWindow = window();
2196 for (QObject *child : targetWindow->children())
2197 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2198 if (QPlatformWindow *handle = childWindow->handle())
2199 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2200 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2202 return targetWindow;
2205bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2210 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2220 QWindow *modalWindow =
nullptr;
2221 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2222 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2223 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2224 <<
"blocked by" << modalWindow;
2229 if (m_inSetVisible) {
2230 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2231 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2238bool QCocoaWindow::windowEvent(QEvent *event)
2240 switch (event->type()) {
2241 case QEvent::WindowBlocked:
2242 case QEvent::WindowUnblocked:
2243 updateTitleBarButtons(window()->flags());
2249 return QPlatformWindow::windowEvent(event);
2252QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2256 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2257 : NSMakePoint(0, 0);
2258 const NSRect visibleRect = [m_view visibleRect];
2260 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2263QMargins QCocoaWindow::frameMargins()
const
2265 QMacAutoReleasePool pool;
2270 if (QPlatformWindow::parent())
2276 if (m_view.window) {
2277 frameRect = m_view.window.frame;
2278 contentRect = [m_view.window contentRectForFrameRect:frameRect];
2280 contentRect = m_view.frame;
2281 frameRect = [NSWindow frameRectForContentRect:contentRect styleMask:windowStyleMask(window()->flags())];
2284 return QMargins(frameRect.origin.x - contentRect.origin.x,
2285 (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height),
2286 (frameRect.origin.x + frameRect.size.width) - (contentRect.origin.x + contentRect.size.width),
2287 contentRect.origin.y - contentRect.origin.y);
2290void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2292 m_frameStrutEventsEnabled = enabled;
2295QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2297 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2298 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2299 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2302QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2304 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2305 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2306 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2309CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2311 if (!referenceView || referenceView.flipped)
2312 return point.toCGPoint();
2314 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2317CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2319 if (!referenceView || referenceView.flipped)
2320 return rect.toCGRect();
2322 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2325QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2327 if (!referenceView || referenceView.flipped)
2328 return QPointF::fromCGPoint(point);
2330 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2333QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2335 if (!referenceView || referenceView.flipped)
2336 return QRectF::fromCGRect(rect);
2338 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2341CALayer *QCocoaWindow::contentLayer()
const
2343 auto *layer = m_view.layer;
2344 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2345 layer = containerLayer.contentLayer;
2349void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2350 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2351 NSVisualEffectState activationState)
2353 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2354 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2355 <<
"in views without a container layer";
2359 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2360 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2361 << blendMode <<
"and activation state" << activationState;
2363 NSVisualEffectView *effectView =
nullptr;
2364 if (m_effectViews.contains(identifier)) {
2365 effectView = m_effectViews.value(identifier);
2366 if (rect.isEmpty()) {
2367 [effectView removeFromSuperview];
2368 m_effectViews.remove(identifier);
2371 }
else if (!rect.isEmpty()) {
2372 effectView = [NSVisualEffectView
new];
2375 effectView.wantsLayer = YES;
2376 effectView.layer.zPosition = -FLT_MAX;
2377 [m_view addSubview:effectView];
2378 m_effectViews.insert(identifier, effectView);
2384 effectView.frame = rect.toCGRect();
2385 effectView.material = material;
2386 effectView.blendingMode = blendMode;
2387 effectView.state = activationState;
2390#ifndef QT_NO_DEBUG_STREAM
2391QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2393 QDebugStateSaver saver(debug);
2395 debug <<
"QCocoaWindow(" << (
const void *)window;
2397 debug <<
", window=" << window->window();
2405#include "moc_qcocoawindow.cpp"
void setGeometry(const QRect &rect, QWindowPrivate::PositionPolicy positionPolicy)
QMargins safeAreaMargins() const override
The safe area margins of a window represent the area that is safe to place content within,...
QRect normalGeometry() const override
the geometry of the window as it will appear when shown as a normal (not maximized or full screen) to...
bool isForeignWindow() const override
QRect geometry() const override
Returns the current geometry of a window.
void updateSafeAreaMarginsIfNeeded()
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
void updateNormalGeometry()
QRect actualGeometry() const
bool isEmbedded() const override
Returns true if the window is a child of a non-Qt window.
static QPointer< QCocoaWindow > s_windowUnderMouse
bool isContentView() const
Checks if the window is the content view of its immediate NSWindow.
void recreateWindowIfNeeded()
Recreates (or removes) the NSWindow for this QWindow, if needed.
void handleGeometryChange()
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
bool startSystemMove() override
Reimplement this method to start a system move operation if the system supports it and return true to...
static const int NoAlertRequest
void initialize() override
Called as part of QWindow::create(), after constructing the window.
QSurfaceFormat format() const override
Returns the actual surface format of the window.
QRect window() const
Returns the window rectangle.
QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QMacAccessibilityElement, NSObject -(void) invalidate;) QT_BEGIN_NAMESPACE bool QAccessibleCache
Q_FORWARD_DECLARE_OBJC_CLASS(NSEvent)
#define Q_NOTIFICATION_PREFIX
static void qRegisterNotificationCallbacks()
const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")