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();
1356 auto *previousScreen = previousWindow ? QCocoaScreen::get(previousWindow.screen) :
nullptr;
1357 auto *currentScreen = m_view.window ? QCocoaScreen::get(m_view.window.screen) :
nullptr;
1358 if (currentScreen && currentScreen != previousScreen)
1359 windowDidChangeScreen();
1369void QCocoaWindow::windowDidMove()
1371 if (!isContentView())
1374 handleGeometryChange();
1377 handleWindowStateChanged();
1380void QCocoaWindow::windowDidResize()
1382 if (!isContentView())
1385 handleGeometryChange();
1387 if (!m_view.inLiveResize)
1388 handleWindowStateChanged();
1391void QCocoaWindow::windowWillStartLiveResize()
1396 m_inLiveResize =
true;
1399bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1404 return !m_inLiveResize;
1407void QCocoaWindow::windowDidEndLiveResize()
1409 m_inLiveResize =
false;
1411 if (!isContentView())
1414 handleWindowStateChanged();
1417void QCocoaWindow::windowDidBecomeKey()
1421 if (m_view.window.firstResponder != m_view)
1424 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1425 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1427 if (windowIsPopupType()) {
1428 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1433 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1434 window(), Qt::ActiveWindowFocusReason);
1437void QCocoaWindow::windowDidResignKey()
1441 if (m_view.window.firstResponder != m_view)
1444 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1445 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1454 NSWindow *newKeyWindow = [NSApp keyWindow];
1455 if (newKeyWindow && newKeyWindow != m_view.window
1456 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1457 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1458 <<
"is Qt window. Deferring focus window change.";
1463 if (!windowIsPopupType()) {
1464 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1465 nullptr, Qt::ActiveWindowFocusReason);
1469void QCocoaWindow::windowDidOrderOnScreen()
1473 if (QWindowPrivate::get(window())->isPopup()) {
1474 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1475 (QGuiApplicationPrivate::currentMouseWindow);
1478 [m_view setNeedsDisplay:YES];
1481void QCocoaWindow::windowDidOrderOffScreen()
1483 handleExposeEvent(QRegion());
1486 if (window()->type() & Qt::Window) {
1487 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1488 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1489 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1490 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1492 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1493 (windowUnderMouse, windowPoint, screenPoint);
1499void QCocoaWindow::windowDidChangeOcclusionState()
1505 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1506 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1507 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1510 [m_view setNeedsDisplay:YES];
1512 handleExposeEvent(QRegion());
1515void QCocoaWindow::windowDidChangeScreen()
1521 NSScreen *nsScreen = m_view.window.screen;
1523 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1524 QCocoaScreen::updateScreens();
1526 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1527 auto *currentScreen = QCocoaScreen::get(nsScreen);
1529 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1542 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1543 window(), currentScreen ? currentScreen->screen() :
nullptr);
1545 if (currentScreen && hasPendingUpdateRequest()) {
1550 currentScreen->requestUpdate();
1555 previousScreen->maybeStopDisplayLink();
1560bool QCocoaWindow::windowShouldClose()
1562 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1574 [[m_view.window retain] autorelease];
1576 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1581void QCocoaWindow::handleGeometryChange()
1584 if (isContentView() && !isEmbedded()) {
1586 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
1589 newGeometry = QCocoaScreen::mapFromNative(contentRect).toRect();
1591 newGeometry = QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
1594 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1595 <<
"current" << geometry() <<
"new" << newGeometry;
1600 if (m_inSetStyleMask && !m_view.window) {
1601 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1608 if (!m_initialized) {
1610 QPlatformWindow::setGeometry(newGeometry);
1611 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1615 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1618 updateSafeAreaMarginsIfNeeded();
1622 if (!m_inSetGeometry)
1623 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1626void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1639 if (m_view.window.visible && m_view.window.screen
1640 && !geometry().size().isEmpty() && !region.isEmpty()
1641 && !m_view.hiddenOrHasHiddenAncestor) {
1642 m_exposedRect = region.boundingRect();
1644 m_exposedRect = QRect();
1647 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1648 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1651 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1656bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1658 if (type == Qt::Widget)
1659 type = window()->type();
1660 if (type == Qt::Tool)
1663 return ((type & Qt::Popup) == Qt::Popup);
1667
1668
1669
1670
1671
1672
1673
1674
1675bool QCocoaWindow::isContentView()
const
1677 return m_view.window.contentView == m_view;
1681
1682
1683
1684
1685
1686void QCocoaWindow::recreateWindowIfNeeded()
1688 QMacAutoReleasePool pool;
1690 QPlatformWindow *parentWindow = QPlatformWindow::parent();
1691 auto *parentCocoaWindow =
static_cast<QCocoaWindow *>(parentWindow);
1693 QCocoaWindow *oldParentCocoaWindow =
nullptr;
1694 if (QNSView *qnsView = qnsview_cast(m_view.superview))
1695 oldParentCocoaWindow = qnsView.platformWindow;
1697 if (isForeignWindow()) {
1701 qCDebug(lcQpaWindow) <<
"Skipping NSWindow management for foreign window" <<
this;
1704 if (parentCocoaWindow)
1705 [parentCocoaWindow->m_view addSubview:m_view];
1706 else if (oldParentCocoaWindow)
1707 [m_view removeFromSuperview];
1712 const bool isEmbeddedView = isEmbedded();
1713 RecreationReasons recreateReason = RecreationNotNeeded;
1715 if (parentWindow != oldParentCocoaWindow)
1716 recreateReason |= ParentChanged;
1718 if (isEmbeddedView && m_nsWindow)
1719 recreateReason |= EmbeddedChanged;
1722 recreateReason |= MissingWindow;
1724 Qt::WindowType type = window()->type();
1726 const bool shouldBeContentView = !parentWindow
1727 && !((type & Qt::SubWindow) == Qt::SubWindow)
1729 if (isContentView() != shouldBeContentView)
1730 recreateReason |= ContentViewChanged;
1732 const bool isPanel = isContentView() && [m_view.window isKindOfClass:[QNSPanel
class]];
1733 const bool shouldBePanel = shouldBeContentView &&
1734 ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog);
1736 if (isPanel != shouldBePanel)
1737 recreateReason |= PanelChanged;
1739 qCDebug(lcQpaWindow) <<
"QCocoaWindow::recreateWindowIfNeeded" << window() << recreateReason;
1741 if (recreateReason == RecreationNotNeeded)
1745 if ((isContentView() && !shouldBeContentView) || (recreateReason & (PanelChanged | EmbeddedChanged))) {
1747 qCDebug(lcQpaWindow) <<
"Getting rid of existing window" << m_nsWindow;
1748 [m_nsWindow closeAndRelease];
1749 if (isContentView() && !isEmbeddedView) {
1754 m_view.window.contentView = nil;
1760 if (shouldBeContentView && !m_nsWindow) {
1762 auto *newWindow = createNSWindow(shouldBePanel);
1763 qCDebug(lcQpaWindow) <<
"Ensuring that" << m_view <<
"is content view for" << newWindow;
1764 [m_view setPostsFrameChangedNotifications:NO];
1765 [newWindow setContentView:m_view];
1766 [m_view setPostsFrameChangedNotifications:YES];
1768 m_nsWindow = newWindow;
1769 Q_ASSERT(m_view.window == m_nsWindow);
1772 if (parentCocoaWindow) {
1774 [parentCocoaWindow->m_view addSubview:m_view];
1778void QCocoaWindow::requestUpdate()
1780 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1781 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1783 if (updatesWithDisplayLink()) {
1784 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1785 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1786 QPlatformWindow::requestUpdate();
1790 QPlatformWindow::requestUpdate();
1794bool QCocoaWindow::updatesWithDisplayLink()
const
1797 return format().swapInterval() != 0;
1800void QCocoaWindow::deliverUpdateRequest()
1802 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1803 QScopedValueRollback<
bool> blocker(m_deliveringUpdateRequest,
true);
1805 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1810 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1811 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1812 <<
"due to" << qtMetalLayer <<
"needing display";
1818 qtMetalLayer.displayLock.unlock();
1821 QPlatformWindow::deliverUpdateRequest();
1824void QCocoaWindow::requestActivateWindow()
1826 QMacAutoReleasePool pool;
1827 [m_view.window makeFirstResponder:m_view];
1828 [m_view.window makeKeyWindow];
1832
1833
1834void QCocoaWindow::closeAllPopups()
1836 QGuiApplicationPrivate::instance()->closeAllPopups();
1838 removePopupMonitor();
1841void QCocoaWindow::removePopupMonitor()
1843 if (s_globalMouseMonitor) {
1844 [NSEvent removeMonitor:s_globalMouseMonitor];
1845 s_globalMouseMonitor = nil;
1847 if (s_applicationActivationObserver) {
1848 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1849 s_applicationActivationObserver = nil;
1853void QCocoaWindow::setupPopupMonitor()
1860 if (!s_globalMouseMonitor) {
1862 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1863 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1864 | NSEventMaskMouseMoved;
1865 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1866 handler:^(NSEvent *e){
1867 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1868 removePopupMonitor();
1871 const auto eventType = cocoaEvent2QtMouseEvent(e);
1872 if (eventType == QEvent::MouseMove) {
1873 if (s_windowUnderMouse) {
1874 QWindow *window = s_windowUnderMouse->window();
1875 const auto button = cocoaButton2QtButton(e);
1876 const auto buttons = currentlyPressedMouseButtons();
1877 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1878 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1879 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1880 buttons, button, eventType);
1890 if (!s_applicationActivationObserver) {
1891 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1892 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1893 object:nil queue:nil
1894 usingBlock:^(NSNotification *){
1900QCocoaNSWindow *QCocoaWindow::createNSWindow(
bool shouldBePanel)
1902 QMacAutoReleasePool pool;
1904 Qt::WindowType type = window()->type();
1905 Qt::WindowFlags flags = window()->flags();
1907 QRect rect = geometry();
1909 QScreen *targetScreen =
nullptr;
1910 for (QScreen *screen : QGuiApplication::screens()) {
1911 if (screen->geometry().contains(rect.topLeft())) {
1912 targetScreen = screen;
1917 NSWindowStyleMask styleMask = windowStyleMask(flags);
1919 if (!targetScreen) {
1920 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
1921 targetScreen = QGuiApplication::primaryScreen();
1925 styleMask = NSWindowStyleMaskBorderless;
1928 rect.translate(-targetScreen->geometry().topLeft());
1929 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
1930 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
1932 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
1937 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
1938 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
1939 <<
"Adjusting style mask during creation";
1940 styleMask = NSWindowStyleMaskBorderless;
1945 Class windowClass = shouldBePanel ? [QNSPanel
class] : [QNSWindow
class];
1946 QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:contentRect
1951 backing:NSBackingStoreBuffered defer:NO
1952 screen:targetCocoaScreen->nativeScreen()
1953 platformWindow:
this];
1957 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
1962 if (!resultingScreen)
1963 resultingScreen = targetCocoaScreen;
1965 if (resultingScreen->screen() != window()->screen()) {
1966 QWindowSystemInterface::handleWindowScreenChanged<
1967 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
1970 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
1971 [](QNSWindowDelegate *delegate) { [delegate release]; });
1972 nsWindow.delegate = sharedDelegate.get();
1978 [nsWindow setReleasedWhenClosed:NO];
1980 if (alwaysShowToolWindow()) {
1981 static dispatch_once_t onceToken;
1982 dispatch_once(&onceToken, ^{
1983 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
1984 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
1985 name:NSApplicationWillResignActiveNotification object:nil];
1986 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
1987 name:NSApplicationWillBecomeActiveNotification object:nil];
1991 nsWindow.restorable = NO;
1992 nsWindow.level = windowLevel(flags);
1993 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
1995 if (shouldBePanel) {
1997 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
2000 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
2001 | NSWindowCollectionBehaviorMoveToActiveSpace;
2003 if ((type & Qt::Popup) == Qt::Popup) {
2004 nsWindow.hasShadow = YES;
2005 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
2006 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
2007 setupPopupMonitor();
2011 applyContentBorderThickness(nsWindow);
2020 if (
auto *qtView = qnsview_cast(m_view))
2021 nsWindow.colorSpace = qtView.colorSpace;
2026bool QCocoaWindow::alwaysShowToolWindow()
const
2028 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2031bool QCocoaWindow::setWindowModified(
bool modified)
2033 QMacAutoReleasePool pool;
2035 if (!isContentView())
2038 m_view.window.documentEdited = modified;
2042void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2047QCocoaMenuBar *QCocoaWindow::menubar()
const
2052void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2054 QMacAutoReleasePool pool;
2057 if (isForeignWindow())
2060 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2062 QNSView *view = qnsview_cast(m_view);
2063 if (cursor == view.cursor)
2066 view.cursor = cursor;
2072 [m_view.window invalidateCursorRectsForView:m_view];
2079 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2080 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2081 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2082 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2083 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2084 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2085 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2086 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2087 location:locationInWindow modifierFlags:0 timestamp:0
2088 windowNumber:m_view.window.windowNumber context:nil
2089 eventNumber:0 trackingNumber:0 userData:0]];
2093void QCocoaWindow::registerTouch(
bool enable)
2095 m_registerTouchCount += enable ? 1 : -1;
2096 if (enable && m_registerTouchCount == 1)
2097 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2098 else if (m_registerTouchCount == 0)
2099 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2102void QCocoaWindow::registerContentBorderArea(quintptr identifier,
int upper,
int lower)
2104 m_contentBorderAreas.insert(identifier, BorderRange(identifier, upper, lower));
2105 applyContentBorderThickness();
2108void QCocoaWindow::setContentBorderAreaEnabled(quintptr identifier,
bool enable)
2110 m_enabledContentBorderAreas.insert(identifier, enable);
2111 applyContentBorderThickness();
2114void QCocoaWindow::setContentBorderEnabled(
bool enable)
2116 m_drawContentBorderGradient = enable;
2117 applyContentBorderThickness();
2120void QCocoaWindow::applyContentBorderThickness(NSWindow *window)
2122 QMacAutoReleasePool pool;
2124 if (!window && isContentView())
2125 window = m_view.window;
2130 if (!m_drawContentBorderGradient) {
2132 window.styleMask = window.styleMask & QT_IGNORE_DEPRECATIONS(~NSWindowStyleMaskTexturedBackground);
2133 setWindowFlags(QPlatformWindow::window()->flags());
2134 [window.contentView.superview setNeedsDisplay:YES];
2139 std::vector<BorderRange> ranges(m_contentBorderAreas.cbegin(), m_contentBorderAreas.cend());
2140 std::sort(ranges.begin(), ranges.end());
2141 int effectiveTopContentBorderThickness = 0;
2142 for (BorderRange range : ranges) {
2144 if (!m_enabledContentBorderAreas.value(range.identifier,
false))
2150 if (range.upper <= (effectiveTopContentBorderThickness + 1))
2151 effectiveTopContentBorderThickness = qMax(effectiveTopContentBorderThickness, range.lower);
2156 int effectiveBottomContentBorderThickness = 0;
2159 [window setStyleMask:[window styleMask] | QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground)];
2160 setWindowFlags(QPlatformWindow::window()->flags());
2165 const NSRect frameRect = window.frame;
2166 const NSRect contentRect = [window contentRectForFrameRect:frameRect];
2167 const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
2168 effectiveTopContentBorderThickness += titlebarHeight;
2170 [window setContentBorderThickness:effectiveTopContentBorderThickness forEdge:NSMaxYEdge];
2171 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
2173 [window setContentBorderThickness:effectiveBottomContentBorderThickness forEdge:NSMinYEdge];
2174 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge];
2176 [[[window contentView] superview] setNeedsDisplay:YES];
2179bool QCocoaWindow::testContentBorderAreaPosition(
int position)
const
2181 if (!m_drawContentBorderGradient || !isContentView())
2187 const int contentBorderThickness = [m_view.window contentBorderThicknessForEdge:NSMaxYEdge];
2188 const NSRect frameRect = m_view.window.frame;
2189 const NSRect contentRect = [m_view.window contentRectForFrameRect:frameRect];
2190 const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
2191 return 0 <= position && position < (contentBorderThickness - titlebarHeight);
2194qreal QCocoaWindow::devicePixelRatio()
const
2201 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2202 return backingSize.height;
2205QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2207 QWindow *targetWindow = window();
2208 for (QObject *child : targetWindow->children())
2209 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2210 if (QPlatformWindow *handle = childWindow->handle())
2211 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2212 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2214 return targetWindow;
2217bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2222 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2232 QWindow *modalWindow =
nullptr;
2233 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2234 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2235 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2236 <<
"blocked by" << modalWindow;
2241 if (m_inSetVisible) {
2242 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2243 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2250bool QCocoaWindow::windowEvent(QEvent *event)
2252 switch (event->type()) {
2253 case QEvent::WindowBlocked:
2254 case QEvent::WindowUnblocked:
2255 updateTitleBarButtons(window()->flags());
2261 return QPlatformWindow::windowEvent(event);
2264QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2268 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2269 : NSMakePoint(0, 0);
2270 const NSRect visibleRect = [m_view visibleRect];
2272 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2275QMargins QCocoaWindow::frameMargins()
const
2277 QMacAutoReleasePool pool;
2282 if (QPlatformWindow::parent())
2288 if (m_view.window) {
2289 frameRect = m_view.window.frame;
2290 contentRect = [m_view.window contentRectForFrameRect:frameRect];
2292 contentRect = m_view.frame;
2293 frameRect = [NSWindow frameRectForContentRect:contentRect styleMask:windowStyleMask(window()->flags())];
2296 return QMargins(frameRect.origin.x - contentRect.origin.x,
2297 (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height),
2298 (frameRect.origin.x + frameRect.size.width) - (contentRect.origin.x + contentRect.size.width),
2299 contentRect.origin.y - contentRect.origin.y);
2302void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2304 m_frameStrutEventsEnabled = enabled;
2307QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2309 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2310 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2311 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2314QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2316 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2317 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2318 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2321CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2323 if (!referenceView || referenceView.flipped)
2324 return point.toCGPoint();
2326 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2329CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2331 if (!referenceView || referenceView.flipped)
2332 return rect.toCGRect();
2334 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2337QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2339 if (!referenceView || referenceView.flipped)
2340 return QPointF::fromCGPoint(point);
2342 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2345QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2347 if (!referenceView || referenceView.flipped)
2348 return QRectF::fromCGRect(rect);
2350 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2353CALayer *QCocoaWindow::contentLayer()
const
2355 auto *layer = m_view.layer;
2356 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2357 layer = containerLayer.contentLayer;
2361void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2362 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2363 NSVisualEffectState activationState)
2365 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2366 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2367 <<
"in views without a container layer";
2371 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2372 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2373 << blendMode <<
"and activation state" << activationState;
2375 NSVisualEffectView *effectView =
nullptr;
2376 if (m_effectViews.contains(identifier)) {
2377 effectView = m_effectViews.value(identifier);
2378 if (rect.isEmpty()) {
2379 [effectView removeFromSuperview];
2380 m_effectViews.remove(identifier);
2383 }
else if (!rect.isEmpty()) {
2384 effectView = [NSVisualEffectView
new];
2387 effectView.wantsLayer = YES;
2388 effectView.layer.zPosition = -FLT_MAX;
2389 [m_view addSubview:effectView];
2390 m_effectViews.insert(identifier, effectView);
2396 effectView.frame = rect.toCGRect();
2397 effectView.material = material;
2398 effectView.blendingMode = blendMode;
2399 effectView.state = activationState;
2402#ifndef QT_NO_DEBUG_STREAM
2403QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2405 QDebugStateSaver saver(debug);
2407 debug <<
"QCocoaWindow(" << (
const void *)window;
2409 debug <<
", window=" << window->window();
2417#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")