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>
37 defaultWindowWidth = 160,
38 defaultWindowHeight = 160
47 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
49 const QMetaObject *metaObject = QMetaType(qRegisterMetaType<
QCocoaWindow*>()).metaObject();
52 for (
int i = 0; i < metaObject->methodCount(); ++i) {
53 QMetaMethod method = metaObject->method(i);
54 const QString methodTag = QString::fromLatin1(method.tag());
55 if (!methodTag.startsWith(notificationHandlerPrefix))
58 const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
59 [center addObserverForName:notificationName.toNSString() object:nil queue:nil
60 usingBlock:^(NSNotification *notification) {
62 QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
63 if ([notification.object isKindOfClass:[NSWindow
class]]) {
64 NSWindow *nsWindow = notification.object;
65 for (
const QWindow *window : QGuiApplication::allWindows()) {
66 if (QCocoaWindow *cocoaWindow =
static_cast<QCocoaWindow *>(window->handle()))
67 if (cocoaWindow->nativeWindow() == nsWindow)
68 cocoaWindows += cocoaWindow;
70 }
else if ([notification.object isKindOfClass:[NSView
class]]) {
71 if (QNSView *qnsView = qnsview_cast(notification.object))
72 cocoaWindows += qnsView.platformWindow;
74 qCWarning(lcCocoaNotifications) <<
"Unhandled notification"
75 << notification.name <<
"for" << notification.object;
79 if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) {
80 QVector<QCocoaWindow *> debugWindows;
81 for (QCocoaWindow *cocoaWindow : cocoaWindows)
82 debugWindows += cocoaWindow;
83 qCDebug(lcCocoaNotifications) <<
"Forwarding" << qPrintable(notificationName) <<
89 for (QCocoaWindow *cocoaWindow : cocoaWindows) {
90 if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
91 qCWarning(lcQpaWindow) <<
"Failed to invoke NSNotification callback for"
92 << notification.name <<
"on" << cocoaWindow;
98Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
103QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) : QPlatformWindow(win)
105 qCDebug(lcQpaWindow) <<
"QCocoaWindow::QCocoaWindow" << window();
108 m_view =
reinterpret_cast<NSView *>(nativeHandle);
115 qCDebug(lcQpaWindow) <<
"QCocoaWindow::initialize" << window();
117 QMacAutoReleasePool pool;
120 m_view = [[QNSView alloc] initWithCocoaWindow:
this];
126 auto initialGeometry = QPlatformWindow::initialGeometry(window(),
127 windowGeometry(), defaultWindowWidth, defaultWindowHeight);
133 if (QPlatformWindow::parent()) {
139 setGeometry(initialGeometry);
146 QPlatformWindow::setGeometry(initialGeometry);
154 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
156 m_safeAreaInsetsObserver = QMacKeyValueObserver(
157 m_view, @
"safeAreaInsets", [
this] {
160 QMetaObject::invokeMethod(
this, [
this]{
161 updateSafeAreaMarginsIfNeeded();
162 }, Qt::QueuedConnection);
163 }, NSKeyValueObservingOptionNew);
169 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
179 qCDebug(lcQpaWindow) <<
"QCocoaWindow::~QCocoaWindow" << window();
181 QMacAutoReleasePool pool;
182 [m_nsWindow makeFirstResponder:nil];
183 [m_nsWindow setContentView:nil];
185 m_safeAreaInsetsObserver = {};
189 if (QPlatformWindow::parent())
190 [m_view removeFromSuperview];
195 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
198 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
199 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
201 vulcanInstance->destroySurface(m_vulkanSurface);
208 [NSNotificationCenter.defaultCenter
209 postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
213 [m_nsWindow closeAndRelease];
218 static_cast<
QCocoaScreen *>(screen())->maybeStopDisplayLink();
223 auto format = window()->requestedFormat();
224 if (
auto *view = qnsview_cast(m_view); view.colorSpace) {
225 auto colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(view.colorSpace.ICCProfileData));
226 if (!colorSpace.isValid()) {
227 qCWarning(lcQpaWindow) <<
"Failed to parse ICC profile for" << view.colorSpace
228 <<
"with ICC data" << view.colorSpace.ICCProfileData;
230 format.setColorSpace(colorSpace);
237 return ![m_view isKindOfClass:[QNSView
class]];
242 return QPlatformWindow::geometry();
246
247
248
249
250
251
252
262 if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
265 return m_normalGeometry;
273 if (windowState() != Qt::WindowNoState)
276 m_normalGeometry = geometry();
282 setGeometry(rect, qt_window_private(window())->positionPolicy);
287 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setGeometry" << window() << rectIn << positionPolicy;
288 QMacAutoReleasePool pool;
291 if (positionPolicy == QWindowPrivate::WindowFrameInclusive) {
295 const QMargins margins = frameMargins();
296 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
297 qCDebug(lcQpaWindow) <<
"Adjusted content geometry to" << rect <<
"by removing frame margins" << margins;
300 QPlatformWindow::setGeometry(rect);
308 NSRect bounds = QCocoaScreen::mapToNative(rect);
309 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
312 m_view.frame = QCocoaWindow::mapToNative(rect, m_view.superview);
318 QMacAutoReleasePool pool;
325 QMarginsF viewSafeAreaMargins = {
326 m_view.safeAreaInsets.left,
327 m_view.safeAreaInsets.top,
328 m_view.safeAreaInsets.right,
329 m_view.safeAreaInsets.bottom
336 auto screenRect = m_view.window.screen.frame;
337 auto screenInsets = m_view.window.screen.safeAreaInsets;
338 auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect(
339 NSMinX(screenRect) + screenInsets.left,
340 NSMinY(screenRect) + screenInsets.bottom,
341 NSWidth(screenRect) - screenInsets.left - screenInsets.right,
342 NSHeight(screenRect) - screenInsets.top - screenInsets.bottom
345 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
346 [m_view.window convertRectToScreen:
347 [m_view convertRect:m_view.bounds toView:nil]]
353 QMarginsF screenSafeAreaMargins = {
354 qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left),
355 qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top),
356 qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right),
357 qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom)
360 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
365 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
366 m_lastReportedSafeAreaMargins = safeAreaMargins();
373 switch (NSApp.currentEvent.type) {
374 case NSEventTypeLeftMouseDown:
375 case NSEventTypeRightMouseDown:
376 case NSEventTypeOtherMouseDown:
377 case NSEventTypeMouseMoved:
378 case NSEventTypeLeftMouseDragged:
379 case NSEventTypeRightMouseDragged:
380 case NSEventTypeOtherMouseDragged:
383 [m_view.window performWindowDragWithEvent:NSApp.currentEvent];
392 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setVisible" << window() << visible;
405 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping setting visibility";
409 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
410 qCDebug(lcQpaWindow) <<
"No change in visible status. Ignoring.";
415 qCWarning(lcQpaWindow) <<
"Already setting window visible!";
419 QScopedValueRollback<
bool> rollback(m_inSetVisible,
true);
421 QMacAutoReleasePool pool;
423 if (window()->transientParent())
424 parentCocoaWindow =
static_cast<
QCocoaWindow *>(window()->transientParent()->handle());
426 auto eventDispatcher = [] {
427 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
440 if (parentCocoaWindow) {
443 setGeometry(windowGeometry());
445 if (window()->type() == Qt::Popup) {
448 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
449 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
450 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
451 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
452 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
461 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
465 applyWindowState(window()->windowStates());
467 if (window()->windowState() != Qt::WindowMinimized) {
468 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
470 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
471 if (!nativeParentWindow.attachedSheet)
472 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
474 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
475 }
else if (window()->modality() == Qt::ApplicationModal) {
477 eventDispatcher()->beginModalSession(window());
478 }
else if (m_view.window.canBecomeKeyWindow) {
479 bool shouldBecomeKeyNow = !NSApp.modalWindow
480 || m_view.window.worksWhenModal
481 || !NSApp.modalWindow.visible;
485 if ([m_view.window isKindOfClass:[NSPanel
class]])
486 shouldBecomeKeyNow &= !(
static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
488 if (shouldBecomeKeyNow)
489 [m_view.window makeKeyAndOrderFront:nil];
491 [m_view.window orderFront:nil];
493 [m_view.window orderFront:nil];
500 if (eventDispatcher()->hasModalSession())
501 eventDispatcher()->endModalSession(window());
502 else if ([m_view.window isSheet])
503 [m_view.window.sheetParent endSheet:m_view.window];
510 [m_view.window orderOut:nil];
512 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
517 NSWindow *mainWindow = [NSApp mainWindow];
518 if (mainWindow && [mainWindow canBecomeKeyWindow])
519 [mainWindow makeKeyWindow];
533 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
534 && m_view.nextValidKeyView.window != m_view.window) {
535 qCDebug(lcQpaWindow) <<
"Detected nextValidKeyView" << m_view.nextValidKeyView
536 <<
"in different window" << m_view.nextValidKeyView.window
537 <<
"Resetting" << m_view.window <<
"first responder to nil.";
538 [m_view.window makeFirstResponder:nil];
543 if (parentCocoaWindow && window()->type() == Qt::Popup) {
544 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
545 if (m_resizableTransientParent
546 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
548 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
553NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
555 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
557 NSInteger windowLevel = NSNormalWindowLevel;
559 if (type == Qt::Tool)
560 windowLevel = NSFloatingWindowLevel;
561 else if ((type & Qt::Popup) == Qt::Popup)
562 windowLevel = NSPopUpMenuWindowLevel;
565 if (flags & Qt::WindowStaysOnTopHint)
566 windowLevel = NSModalPanelWindowLevel;
568 if (type == Qt::ToolTip)
569 windowLevel = NSScreenSaverWindowLevel;
571 auto *transientParent = window()->transientParent();
572 if (transientParent && transientParent->handle()) {
592 auto *transientCocoaWindow =
static_cast<QCocoaWindow *>(transientParent->handle());
593 auto *nsWindow = transientCocoaWindow->nativeWindow();
598 if (type != Qt::Window)
599 windowLevel = qMax(windowLevel, nsWindow.level);
605NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
const
607 const Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
613 NSUInteger styleMask = [&]{
615 if (flags & Qt::FramelessWindowHint)
616 return NSWindowStyleMaskBorderless;
619 if (windowIsPopupType(type))
620 return NSWindowStyleMaskBorderless;
622 if (flags & Qt::CustomizeWindowHint) {
625 return flags & Qt::WindowTitleHint
626 ? NSWindowStyleMaskTitled
627 : NSWindowStyleMaskBorderless;
630 return NSWindowStyleMaskTitled;
639 styleMask |= NSWindowStyleMaskClosable
640 | NSWindowStyleMaskMiniaturizable;
642 if (type != Qt::Popup)
643 styleMask |= NSWindowStyleMaskResizable;
645 if (type == Qt::Tool)
646 styleMask |= NSWindowStyleMaskUtilityWindow;
649 if (m_drawContentBorderGradient)
650 styleMask |= QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground);
652 if (flags & Qt::ExpandedClientAreaHint)
653 styleMask |= NSWindowStyleMaskFullSizeContentView;
656 styleMask |= (m_view.window.styleMask & (
657 NSWindowStyleMaskFullScreen
658 | NSWindowStyleMaskUnifiedTitleAndToolbar
659 | NSWindowStyleMaskDocModalWindow
660 | NSWindowStyleMaskNonactivatingPanel
661 | NSWindowStyleMaskHUDWindow));
666bool QCocoaWindow::isFixedSize()
const
668 return windowMinimumSize().isValid() && windowMaximumSize().isValid()
669 && windowMinimumSize() == windowMaximumSize();
672void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
674 if (!isContentView())
677 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
678 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
679 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
680 { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
683 bool hideButtons =
true;
684 for (
const auto &[button, buttonHint] : buttons) {
687 if (button == NSWindowMiniaturizeButton)
688 enabled = window()->type() != Qt::Dialog;
691 if (windowFlags & Qt::CustomizeWindowHint)
692 enabled = windowFlags & buttonHint;
696 if (button == NSWindowZoomButton && isFixedSize())
702 if (button == NSWindowCloseButton && enabled
703 && QWindowPrivate::get(window())->blockedByModalWindow) {
710 [m_view.window standardWindowButton:button].enabled = enabled;
711 hideButtons &= !enabled;
715 for (
const auto &[button, buttonHint] : buttons)
716 [m_view.window standardWindowButton:button].hidden = hideButtons;
719void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
725 QMacAutoReleasePool pool;
727 if (!isContentView())
732 m_inSetStyleMask =
true;
733 m_view.window.styleMask = windowStyleMask(flags);
734 m_inSetStyleMask =
false;
736 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
737 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
738 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
739 const bool enableFullScreen = m_view.window.qt_fullScreen
740 || !(flags & Qt::CustomizeWindowHint)
741 || (flags & Qt::WindowFullscreenButtonHint);
742 if (enableFullScreen) {
743 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
744 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
746 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
747 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
749 m_view.window.collectionBehavior = behavior;
754 m_view.window.level =
this->windowLevel(flags);
756 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
758 if (!(flags & Qt::FramelessWindowHint))
759 setWindowTitle(window()->title());
761 updateTitleBarButtons(flags);
770 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
771 if (m_view.window.ignoresMouseEvents != ignoreMouse)
772 m_view.window.ignoresMouseEvents = ignoreMouse;
775 m_view.window.titlebarAppearsTransparent = (flags & Qt::NoTitleBarBackgroundHint)
776 || (m_view.window.styleMask & QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground));
782
783
784
785
786
787
788void QCocoaWindow::setWindowState(Qt::WindowStates state)
790 if (window()->isVisible())
791 applyWindowState(state);
794void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
796 if (!isContentView())
799 const Qt::WindowState currentState = QWindowPrivate::effectiveState(windowState());
800 const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
802 if (newState == currentState)
805 qCDebug(lcQpaWindow) <<
"Applying" << newState <<
"to" << window() <<
"in" << currentState;
807 const NSSize contentSize = m_view.frame.size;
808 if (contentSize.width <= 0 || contentSize.height <= 0) {
811 qWarning(
"invalid window content view size, check your window geometry");
812 handleWindowStateChanged(HandleUnconditionally);
816 const NSWindow *nsWindow = m_view.window;
818 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
819 && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
820 qWarning() << window()->type() <<
"windows cannot be made" << newState;
821 handleWindowStateChanged(HandleUnconditionally);
825 const id sender = nsWindow;
828 switch (currentState) {
829 case Qt::WindowMinimized:
830 [nsWindow deminiaturize:sender];
835 case Qt::WindowFullScreen: {
846 if (newState == windowState())
850 case Qt::WindowFullScreen:
853 case Qt::WindowMaximized:
856 case Qt::WindowMinimized:
857 [nsWindow miniaturize:sender];
859 case Qt::WindowNoState:
860 if (windowState() == Qt::WindowMaximized)
868Qt::WindowStates QCocoaWindow::windowState()
const
870 Qt::WindowStates states = Qt::WindowNoState;
871 NSWindow *window = m_view.window;
873 if (window.miniaturized)
874 states |= Qt::WindowMinimized;
878 if (window.qt_fullScreen) {
879 states |= Qt::WindowFullScreen;
880 }
else if ((window.zoomed && !isTransitioningToFullScreen())
881 || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) {
882 states |= Qt::WindowMaximized;
891void QCocoaWindow::toggleMaximized()
893 const NSWindow *window = m_view.window;
897 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
898 window.styleMask |= NSWindowStyleMaskResizable;
900 const id sender = window;
901 [window zoom:sender];
904 window.styleMask &= ~NSWindowStyleMaskResizable;
907void QCocoaWindow::windowWillZoom()
909 updateNormalGeometry();
912void QCocoaWindow::toggleFullScreen()
914 const NSWindow *window = m_view.window;
919 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
921 const id sender = window;
922 [window toggleFullScreen:sender];
925void QCocoaWindow::windowWillEnterFullScreen()
927 if (!isContentView())
930 updateNormalGeometry();
935 m_view.window.styleMask |= NSWindowStyleMaskResizable;
938bool QCocoaWindow::isTransitioningToFullScreen()
const
940 NSWindow *window = m_view.window;
941 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
944void QCocoaWindow::windowDidEnterFullScreen()
946 if (!isContentView())
949 Q_ASSERT_X(m_view.window.qt_fullScreen,
"QCocoaWindow",
950 "FullScreen category processes window notifications first");
953 setWindowFlags(window()->flags());
955 handleWindowStateChanged();
958void QCocoaWindow::windowWillExitFullScreen()
960 if (!isContentView())
965 m_view.window.styleMask |= NSWindowStyleMaskResizable;
968void QCocoaWindow::windowDidExitFullScreen()
970 if (!isContentView())
973 Q_ASSERT_X(!m_view.window.qt_fullScreen,
"QCocoaWindow",
974 "FullScreen category processes window notifications first");
977 setWindowFlags(window()->flags());
979 Qt::WindowState requestedState = window()->windowState();
982 handleWindowStateChanged();
984 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
987 applyWindowState(requestedState);
991void QCocoaWindow::windowDidMiniaturize()
993 if (!isContentView())
996 handleWindowStateChanged();
999void QCocoaWindow::windowDidDeminiaturize()
1001 if (!isContentView())
1004 Qt::WindowState requestedState = window()->windowState();
1006 handleWindowStateChanged();
1008 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
1011 applyWindowState(requestedState);
1015void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
1017 Qt::WindowStates currentState = windowState();
1018 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
1021 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleWindowStateChanged" <<
1022 m_lastReportedWindowState <<
"-->" << currentState;
1024 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
1025 window(), currentState, m_lastReportedWindowState);
1026 m_lastReportedWindowState = currentState;
1031void QCocoaWindow::setWindowTitle(
const QString &title)
1033 QMacAutoReleasePool pool;
1035 if (!isContentView())
1038 m_view.window.title = title.toNSString();
1040 if (title.isEmpty() && !window()->filePath().isEmpty()) {
1042 setWindowFilePath(window()->filePath());
1046void QCocoaWindow::setWindowFilePath(
const QString &filePath)
1048 QMacAutoReleasePool pool;
1050 if (!isContentView())
1053 if (window()->title().isNull())
1054 [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
1056 m_view.window.representedFilename = filePath.toNSString();
1059 setWindowIcon(window()->icon());
1062void QCocoaWindow::setWindowIcon(
const QIcon &icon)
1064 QMacAutoReleasePool pool;
1066 if (!isContentView())
1069 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
1075 if (icon.isNull()) {
1076 iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename];
1080 auto fallbackSize = QSizeF::fromCGSize(iconButton.frame.size) * qGuiApp->devicePixelRatio();
1081 iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize.toSize()];
1085void QCocoaWindow::setAlertState(
bool enabled)
1087 if (m_alertRequest == NoAlertRequest && enabled) {
1088 m_alertRequest = [NSApp requestUserAttention:NSCriticalRequest];
1089 }
else if (m_alertRequest != NoAlertRequest && !enabled) {
1090 [NSApp cancelUserAttentionRequest:m_alertRequest];
1091 m_alertRequest = NoAlertRequest;
1095bool QCocoaWindow::isAlertState()
const
1097 return m_alertRequest != NoAlertRequest;
1100void QCocoaWindow::raise()
1102 qCDebug(lcQpaWindow) <<
"QCocoaWindow::raise" << window();
1105 if (isContentView()) {
1106 if (m_view.window.visible) {
1113 QMacAutoReleasePool pool;
1114 [m_view.window orderFront:m_view.window];
1116 static bool raiseProcess = qt_mac_resolveOption(
true,
"QT_MAC_SET_RAISE_PROCESS");
1118 [NSApp activateIgnoringOtherApps:YES];
1121 [m_view.superview addSubview:m_view positioned:NSWindowAbove relativeTo:nil];
1125void QCocoaWindow::lower()
1127 qCDebug(lcQpaWindow) <<
"QCocoaWindow::lower" << window();
1129 if (isContentView()) {
1130 if (m_view.window.visible)
1131 [m_view.window orderBack:m_view.window];
1133 [m_view.superview addSubview:m_view positioned:NSWindowBelow relativeTo:nil];
1137bool QCocoaWindow::isExposed()
const
1139 return !m_exposedRect.isEmpty();
1142bool QCocoaWindow::isEmbedded()
const
1145 if (window()->parent())
1150 return !([m_view.window isKindOfClass:[QNSWindow
class]] ||
1151 [m_view.window isKindOfClass:[QNSPanel
class]]);
1158bool QCocoaWindow::isOpaque()
const
1162 static GLint openglSourfaceOrder = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
1164 bool translucent = window()->format().alphaBufferSize() > 0
1165 || window()->opacity() < 1
1166 || !window()->mask().isEmpty()
1167 || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
1168 return !translucent;
1171void QCocoaWindow::propagateSizeHints()
1173 QMacAutoReleasePool pool;
1174 if (!isContentView())
1177 qCDebug(lcQpaWindow) <<
"QCocoaWindow::propagateSizeHints" << window()
1178 <<
"min:" << windowMinimumSize() <<
"max:" << windowMaximumSize()
1179 <<
"increment:" << windowSizeIncrement()
1180 <<
"base:" << windowBaseSize();
1182 const NSWindow *window = m_view.window;
1185 QSize minimumSize = windowMinimumSize();
1186 if (!minimumSize.isValid())
1187 minimumSize = QSize(0, 0);
1188 window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
1191 window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
1194 updateTitleBarButtons(
this->window()->flags());
1198 QSize sizeIncrement = windowSizeIncrement();
1199 if (sizeIncrement.isEmpty())
1200 sizeIncrement = QSize(1, 1);
1201 window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
1203 QRect rect = geometry();
1204 QSize baseSize = windowBaseSize();
1205 if (!baseSize.isNull() && baseSize.isValid())
1206 [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
1209void QCocoaWindow::setOpacity(qreal level)
1211 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setOpacity" << level;
1212 if (!isContentView())
1215 m_view.window.alphaValue = level;
1218void QCocoaWindow::setMask(
const QRegion ®ion)
1220 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setMask" << window() << region;
1222 if (!region.isEmpty()) {
1223 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
1224 for (
const QRect &r : region)
1225 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
1226 CAShapeLayer *maskLayer = [CAShapeLayer layer];
1227 maskLayer.path = maskPath;
1228 m_view.layer.mask = maskLayer;
1230 m_view.layer.mask = nil;
1234bool QCocoaWindow::setKeyboardGrabEnabled(
bool)
1239bool QCocoaWindow::setMouseGrabEnabled(
bool)
1244WId QCocoaWindow::winId()
const
1249void QCocoaWindow::setParent(
const QPlatformWindow *parentWindow)
1251 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0);
1254 recreateWindowIfNeeded();
1256 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1259NSView *QCocoaWindow::view()
const
1264NSWindow *QCocoaWindow::nativeWindow()
const
1266 return m_view.window;
1271void QCocoaWindow::viewDidChangeFrame()
1278 handleGeometryChange();
1282
1283
1284
1285
1286
1287
1288void QCocoaWindow::viewDidChangeGlobalFrame()
1290 [m_view setNeedsDisplay:YES];
1294
1295
1296
1297
1298
1299void QCocoaWindow::viewDidMoveToSuperview(NSView *previousSuperview)
1301 qCDebug(lcQpaWindow) <<
"Done re-parenting" << m_view
1302 <<
"from" << previousSuperview <<
"into" << m_view.superview;
1306 handleGeometryChange();
1308 if (m_view.superview)
1309 [m_view setNeedsDisplay:YES];
1320 if (m_view.superview && !m_view.superview.flipped && !isContentView()) {
1321 if (m_view.autoresizingMask == NSViewNotSizable) {
1322 qCDebug(lcQpaWindow) <<
"Setting auto resizing mask on" << m_view
1323 <<
"in non-flipped superview to maintain stable y-positioning";
1324 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1325 m_view.autoresizingMask = NSViewMinYMargin;
1327 }
else if (previousSuperview && !previousSuperview.flipped
1328 && m_view.autoresizingMask == NSViewMinYMargin) {
1333 qCDebug(lcQpaWindow) <<
"Clearing auto resizing mask on" << m_view
1334 <<
"as explicit stable y-positioning is no longer needed";
1335 m_view.autoresizingMask = NSViewNotSizable;
1340
1341
1342
1343
1344void QCocoaWindow::viewDidMoveToWindow(NSWindow *previousWindow)
1346 qCDebug(lcQpaWindow) <<
"Done moving" << m_view
1347 <<
"from" << previousWindow <<
"to" << m_view.window;
1351 recreateWindowIfNeeded();
1361void QCocoaWindow::windowDidMove()
1363 if (!isContentView())
1366 handleGeometryChange();
1369 handleWindowStateChanged();
1372void QCocoaWindow::windowDidResize()
1374 if (!isContentView())
1377 handleGeometryChange();
1379 if (!m_view.inLiveResize)
1380 handleWindowStateChanged();
1383void QCocoaWindow::windowWillStartLiveResize()
1388 m_inLiveResize =
true;
1391bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1396 return !m_inLiveResize;
1399void QCocoaWindow::windowDidEndLiveResize()
1401 m_inLiveResize =
false;
1403 if (!isContentView())
1406 handleWindowStateChanged();
1409void QCocoaWindow::windowDidBecomeKey()
1413 if (m_view.window.firstResponder != m_view)
1416 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1417 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1419 if (windowIsPopupType()) {
1420 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1425 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1426 window(), Qt::ActiveWindowFocusReason);
1429void QCocoaWindow::windowDidResignKey()
1433 if (m_view.window.firstResponder != m_view)
1436 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1437 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1446 NSWindow *newKeyWindow = [NSApp keyWindow];
1447 if (newKeyWindow && newKeyWindow != m_view.window
1448 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1449 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1450 <<
"is Qt window. Deferring focus window change.";
1455 if (!windowIsPopupType()) {
1456 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1457 nullptr, Qt::ActiveWindowFocusReason);
1461void QCocoaWindow::windowDidOrderOnScreen()
1465 if (QWindowPrivate::get(window())->isPopup()) {
1466 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1467 (QGuiApplicationPrivate::currentMouseWindow);
1470 [m_view setNeedsDisplay:YES];
1473void QCocoaWindow::windowDidOrderOffScreen()
1475 handleExposeEvent(QRegion());
1478 if (window()->type() & Qt::Window) {
1479 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1480 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1481 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1482 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1484 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1485 (windowUnderMouse, windowPoint, screenPoint);
1491void QCocoaWindow::windowDidChangeOcclusionState()
1497 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1498 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1499 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1502 [m_view setNeedsDisplay:YES];
1504 handleExposeEvent(QRegion());
1507void QCocoaWindow::windowDidChangeScreen()
1513 NSScreen *nsScreen = m_view.window.screen;
1515 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1516 QCocoaScreen::updateScreens();
1518 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1519 auto *currentScreen = QCocoaScreen::get(nsScreen);
1521 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1534 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1535 window(), currentScreen ? currentScreen->screen() :
nullptr);
1537 if (currentScreen && hasPendingUpdateRequest()) {
1542 currentScreen->requestUpdate();
1548bool QCocoaWindow::windowShouldClose()
1550 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1562 [[m_view.window retain] autorelease];
1564 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1569void QCocoaWindow::handleGeometryChange()
1572 if (isContentView() && !isEmbedded()) {
1574 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
1577 newGeometry = QCocoaScreen::mapFromNative(contentRect).toRect();
1579 newGeometry = QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
1582 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1583 <<
"current" << geometry() <<
"new" << newGeometry;
1588 if (m_inSetStyleMask && !m_view.window) {
1589 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1596 if (!m_initialized) {
1598 QPlatformWindow::setGeometry(newGeometry);
1599 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1603 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1606 updateSafeAreaMarginsIfNeeded();
1610 if (!m_inSetGeometry)
1611 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1614void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1627 if (m_view.window.visible && m_view.window.screen
1628 && !geometry().size().isEmpty() && !region.isEmpty()
1629 && !m_view.hiddenOrHasHiddenAncestor) {
1630 m_exposedRect = region.boundingRect();
1632 m_exposedRect = QRect();
1635 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1636 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1639 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1644bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1646 if (type == Qt::Widget)
1647 type = window()->type();
1648 if (type == Qt::Tool)
1651 return ((type & Qt::Popup) == Qt::Popup);
1655
1656
1657
1658
1659
1660
1661
1662
1663bool QCocoaWindow::isContentView()
const
1665 return m_view.window.contentView == m_view;
1669
1670
1671
1672
1673
1674void QCocoaWindow::recreateWindowIfNeeded()
1676 QMacAutoReleasePool pool;
1678 QPlatformWindow *parentWindow = QPlatformWindow::parent();
1679 auto *parentCocoaWindow =
static_cast<QCocoaWindow *>(parentWindow);
1681 QCocoaWindow *oldParentCocoaWindow =
nullptr;
1682 if (QNSView *qnsView = qnsview_cast(m_view.superview))
1683 oldParentCocoaWindow = qnsView.platformWindow;
1685 if (isForeignWindow()) {
1689 qCDebug(lcQpaWindow) <<
"Skipping NSWindow management for foreign window" <<
this;
1692 if (parentCocoaWindow)
1693 [parentCocoaWindow->m_view addSubview:m_view];
1694 else if (oldParentCocoaWindow)
1695 [m_view removeFromSuperview];
1700 const bool isEmbeddedView = isEmbedded();
1701 RecreationReasons recreateReason = RecreationNotNeeded;
1703 if (parentWindow != oldParentCocoaWindow)
1704 recreateReason |= ParentChanged;
1706 if (isEmbeddedView && m_nsWindow)
1707 recreateReason |= EmbeddedChanged;
1710 recreateReason |= MissingWindow;
1712 Qt::WindowType type = window()->type();
1714 const bool shouldBeContentView = !parentWindow
1715 && !((type & Qt::SubWindow) == Qt::SubWindow)
1717 if (isContentView() != shouldBeContentView)
1718 recreateReason |= ContentViewChanged;
1720 const bool isPanel = isContentView() && [m_view.window isKindOfClass:[QNSPanel
class]];
1721 const bool shouldBePanel = shouldBeContentView &&
1722 ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog);
1724 if (isPanel != shouldBePanel)
1725 recreateReason |= PanelChanged;
1727 qCDebug(lcQpaWindow) <<
"QCocoaWindow::recreateWindowIfNeeded" << window() << recreateReason;
1729 if (recreateReason == RecreationNotNeeded)
1733 if ((isContentView() && !shouldBeContentView) || (recreateReason & (PanelChanged | EmbeddedChanged))) {
1735 qCDebug(lcQpaWindow) <<
"Getting rid of existing window" << m_nsWindow;
1736 [m_nsWindow closeAndRelease];
1737 if (isContentView() && !isEmbeddedView) {
1742 m_view.window.contentView = nil;
1748 if (shouldBeContentView && !m_nsWindow) {
1750 auto *newWindow = createNSWindow(shouldBePanel);
1751 qCDebug(lcQpaWindow) <<
"Ensuring that" << m_view <<
"is content view for" << newWindow;
1752 [m_view setPostsFrameChangedNotifications:NO];
1753 [newWindow setContentView:m_view];
1754 [m_view setPostsFrameChangedNotifications:YES];
1756 m_nsWindow = newWindow;
1757 Q_ASSERT(m_view.window == m_nsWindow);
1760 if (parentCocoaWindow) {
1762 [parentCocoaWindow->m_view addSubview:m_view];
1766void QCocoaWindow::requestUpdate()
1768 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1769 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1771 if (updatesWithDisplayLink()) {
1772 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1773 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1774 QPlatformWindow::requestUpdate();
1778 QPlatformWindow::requestUpdate();
1782bool QCocoaWindow::updatesWithDisplayLink()
const
1785 return format().swapInterval() != 0;
1788void QCocoaWindow::deliverUpdateRequest()
1790 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1791 QScopedValueRollback<
bool> blocker(m_deliveringUpdateRequest,
true);
1793 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1798 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1799 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1800 <<
"due to" << qtMetalLayer <<
"needing display";
1806 qtMetalLayer.displayLock.unlock();
1809 QPlatformWindow::deliverUpdateRequest();
1812void QCocoaWindow::requestActivateWindow()
1814 QMacAutoReleasePool pool;
1815 [m_view.window makeFirstResponder:m_view];
1816 [m_view.window makeKeyWindow];
1820
1821
1822void QCocoaWindow::closeAllPopups()
1824 QGuiApplicationPrivate::instance()->closeAllPopups();
1826 removePopupMonitor();
1829void QCocoaWindow::removePopupMonitor()
1831 if (s_globalMouseMonitor) {
1832 [NSEvent removeMonitor:s_globalMouseMonitor];
1833 s_globalMouseMonitor = nil;
1835 if (s_applicationActivationObserver) {
1836 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1837 s_applicationActivationObserver = nil;
1841void QCocoaWindow::setupPopupMonitor()
1848 if (!s_globalMouseMonitor) {
1850 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1851 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1852 | NSEventMaskMouseMoved;
1853 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1854 handler:^(NSEvent *e){
1855 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1856 removePopupMonitor();
1859 const auto eventType = cocoaEvent2QtMouseEvent(e);
1860 if (eventType == QEvent::MouseMove) {
1861 if (s_windowUnderMouse) {
1862 QWindow *window = s_windowUnderMouse->window();
1863 const auto button = cocoaButton2QtButton(e);
1864 const auto buttons = currentlyPressedMouseButtons();
1865 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1866 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1867 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1868 buttons, button, eventType);
1878 if (!s_applicationActivationObserver) {
1879 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1880 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1881 object:nil queue:nil
1882 usingBlock:^(NSNotification *){
1888QCocoaNSWindow *QCocoaWindow::createNSWindow(
bool shouldBePanel)
1890 QMacAutoReleasePool pool;
1892 Qt::WindowType type = window()->type();
1893 Qt::WindowFlags flags = window()->flags();
1895 QRect rect = geometry();
1897 QScreen *targetScreen =
nullptr;
1898 for (QScreen *screen : QGuiApplication::screens()) {
1899 if (screen->geometry().contains(rect.topLeft())) {
1900 targetScreen = screen;
1905 NSWindowStyleMask styleMask = windowStyleMask(flags);
1907 if (!targetScreen) {
1908 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
1909 targetScreen = QGuiApplication::primaryScreen();
1913 styleMask = NSWindowStyleMaskBorderless;
1916 rect.translate(-targetScreen->geometry().topLeft());
1917 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
1918 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
1920 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
1925 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
1926 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
1927 <<
"Adjusting style mask during creation";
1928 styleMask = NSWindowStyleMaskBorderless;
1933 Class windowClass = shouldBePanel ? [QNSPanel
class] : [QNSWindow
class];
1934 QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:contentRect
1939 backing:NSBackingStoreBuffered defer:NO
1940 screen:targetCocoaScreen->nativeScreen()
1941 platformWindow:
this];
1945 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
1950 if (!resultingScreen)
1951 resultingScreen = targetCocoaScreen;
1953 if (resultingScreen->screen() != window()->screen()) {
1954 QWindowSystemInterface::handleWindowScreenChanged<
1955 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
1958 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
1959 [](QNSWindowDelegate *delegate) { [delegate release]; });
1960 nsWindow.delegate = sharedDelegate.get();
1966 [nsWindow setReleasedWhenClosed:NO];
1968 if (alwaysShowToolWindow()) {
1969 static dispatch_once_t onceToken;
1970 dispatch_once(&onceToken, ^{
1971 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
1972 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
1973 name:NSApplicationWillResignActiveNotification object:nil];
1974 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
1975 name:NSApplicationWillBecomeActiveNotification object:nil];
1979 nsWindow.restorable = NO;
1980 nsWindow.level = windowLevel(flags);
1981 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
1983 if (shouldBePanel) {
1985 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
1988 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
1989 | NSWindowCollectionBehaviorMoveToActiveSpace;
1991 if ((type & Qt::Popup) == Qt::Popup) {
1992 nsWindow.hasShadow = YES;
1993 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
1994 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
1995 setupPopupMonitor();
1999 applyContentBorderThickness(nsWindow);
2008 if (
auto *qtView = qnsview_cast(m_view))
2009 nsWindow.colorSpace = qtView.colorSpace;
2014bool QCocoaWindow::alwaysShowToolWindow()
const
2016 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2019bool QCocoaWindow::setWindowModified(
bool modified)
2021 QMacAutoReleasePool pool;
2023 if (!isContentView())
2026 m_view.window.documentEdited = modified;
2030void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2035QCocoaMenuBar *QCocoaWindow::menubar()
const
2040void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2042 QMacAutoReleasePool pool;
2045 if (isForeignWindow())
2048 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2050 QNSView *view = qnsview_cast(m_view);
2051 if (cursor == view.cursor)
2054 view.cursor = cursor;
2060 [m_view.window invalidateCursorRectsForView:m_view];
2067 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2068 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2069 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2070 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2071 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2072 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2073 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2074 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2075 location:locationInWindow modifierFlags:0 timestamp:0
2076 windowNumber:m_view.window.windowNumber context:nil
2077 eventNumber:0 trackingNumber:0 userData:0]];
2081void QCocoaWindow::registerTouch(
bool enable)
2083 m_registerTouchCount += enable ? 1 : -1;
2084 if (enable && m_registerTouchCount == 1)
2085 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2086 else if (m_registerTouchCount == 0)
2087 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2090void QCocoaWindow::registerContentBorderArea(quintptr identifier,
int upper,
int lower)
2092 m_contentBorderAreas.insert(identifier, BorderRange(identifier, upper, lower));
2093 applyContentBorderThickness();
2096void QCocoaWindow::setContentBorderAreaEnabled(quintptr identifier,
bool enable)
2098 m_enabledContentBorderAreas.insert(identifier, enable);
2099 applyContentBorderThickness();
2102void QCocoaWindow::setContentBorderEnabled(
bool enable)
2104 m_drawContentBorderGradient = enable;
2105 applyContentBorderThickness();
2108void QCocoaWindow::applyContentBorderThickness(NSWindow *window)
2110 QMacAutoReleasePool pool;
2112 if (!window && isContentView())
2113 window = m_view.window;
2118 if (!m_drawContentBorderGradient) {
2120 window.styleMask = window.styleMask & QT_IGNORE_DEPRECATIONS(~NSWindowStyleMaskTexturedBackground);
2121 setWindowFlags(QPlatformWindow::window()->flags());
2122 [window.contentView.superview setNeedsDisplay:YES];
2127 std::vector<BorderRange> ranges(m_contentBorderAreas.cbegin(), m_contentBorderAreas.cend());
2128 std::sort(ranges.begin(), ranges.end());
2129 int effectiveTopContentBorderThickness = 0;
2130 for (BorderRange range : ranges) {
2132 if (!m_enabledContentBorderAreas.value(range.identifier,
false))
2138 if (range.upper <= (effectiveTopContentBorderThickness + 1))
2139 effectiveTopContentBorderThickness = qMax(effectiveTopContentBorderThickness, range.lower);
2144 int effectiveBottomContentBorderThickness = 0;
2147 [window setStyleMask:[window styleMask] | QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground)];
2148 setWindowFlags(QPlatformWindow::window()->flags());
2153 const NSRect frameRect = window.frame;
2154 const NSRect contentRect = [window contentRectForFrameRect:frameRect];
2155 const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
2156 effectiveTopContentBorderThickness += titlebarHeight;
2158 [window setContentBorderThickness:effectiveTopContentBorderThickness forEdge:NSMaxYEdge];
2159 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
2161 [window setContentBorderThickness:effectiveBottomContentBorderThickness forEdge:NSMinYEdge];
2162 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge];
2164 [[[window contentView] superview] setNeedsDisplay:YES];
2167bool QCocoaWindow::testContentBorderAreaPosition(
int position)
const
2169 if (!m_drawContentBorderGradient || !isContentView())
2175 const int contentBorderThickness = [m_view.window contentBorderThicknessForEdge:NSMaxYEdge];
2176 const NSRect frameRect = m_view.window.frame;
2177 const NSRect contentRect = [m_view.window contentRectForFrameRect:frameRect];
2178 const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
2179 return 0 <= position && position < (contentBorderThickness - titlebarHeight);
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()
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.
#define Q_NOTIFICATION_PREFIX
static void qRegisterNotificationCallbacks()
const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")