5#include <AppKit/AppKit.h>
6#include <QuartzCore/QuartzCore.h>
20#include <QtCore/qfileinfo.h>
21#include <QtCore/private/qcore_mac_p.h>
23#include <private/qwindow_p.h>
24#include <qpa/qwindowsysteminterface.h>
25#include <qpa/qplatformscreen.h>
26#include <QtGui/private/qcoregraphics_p.h>
27#include <QtGui/private/qhighdpiscaling_p.h>
28#include <QtGui/private/qmetallayer_p.h>
35- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow;
38@implementation QNSWindowController {
39 QPointer<QCocoaWindow> m_platformWindow;
42- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow
46 if ((self = [super initWithWindowNibName:@
"QNSWindowController"]))
47 m_platformWindow = platformWindow;
54 QMacAutoReleasePool pool;
55 self.window = [m_platformWindow->createNSWindow() autorelease];
59 qCDebug(lcQpaWindow) <<
"Disposing of" << self.window <<
"for" << m_platformWindow;
69 defaultWindowWidth = 160,
70 defaultWindowHeight = 160
79 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
81 const QMetaObject *metaObject = QMetaType(qRegisterMetaType<
QCocoaWindow*>()).metaObject();
84 for (
int i = 0; i < metaObject->methodCount(); ++i) {
85 QMetaMethod method = metaObject->method(i);
86 const QString methodTag = QString::fromLatin1(method.tag());
87 if (!methodTag.startsWith(notificationHandlerPrefix))
90 const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
91 [center addObserverForName:notificationName.toNSString() object:nil queue:nil
92 usingBlock:^(NSNotification *notification) {
94 QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
95 if ([notification.object isKindOfClass:[NSWindow
class]]) {
96 NSWindow *nsWindow = notification.object;
97 for (
const QWindow *window : QGuiApplication::allWindows()) {
98 if (QCocoaWindow *cocoaWindow =
static_cast<QCocoaWindow *>(window->handle()))
99 if (cocoaWindow->nativeWindow() == nsWindow)
100 cocoaWindows += cocoaWindow;
102 }
else if ([notification.object isKindOfClass:[NSView
class]]) {
103 if (QNSView *qnsView = qnsview_cast(notification.object))
104 cocoaWindows += qnsView.platformWindow;
106 qCWarning(lcCocoaNotifications) <<
"Unhandled notification"
107 << notification.name <<
"for" << notification.object;
111 if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) {
112 QVector<QCocoaWindow *> debugWindows;
113 for (QCocoaWindow *cocoaWindow : cocoaWindows)
114 debugWindows += cocoaWindow;
115 qCDebug(lcCocoaNotifications) <<
"Forwarding" << qPrintable(notificationName) <<
116 "to" << debugWindows;
121 for (QCocoaWindow *cocoaWindow : cocoaWindows) {
122 if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
123 qCWarning(lcQpaWindow) <<
"Failed to invoke NSNotification callback for"
124 << notification.name <<
"on" << cocoaWindow;
130Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
135QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) : QPlatformWindow(win)
137 qCDebug(lcQpaWindow) <<
"QCocoaWindow::QCocoaWindow" << window();
140 m_view =
reinterpret_cast<NSView *>(nativeHandle);
147 qCDebug(lcQpaWindow) <<
"QCocoaWindow::initialize" << window();
149 QMacAutoReleasePool pool;
152 m_view = [[QNSView alloc] initWithCocoaWindow:
this];
158 auto initialGeometry = QPlatformWindow::initialGeometry(window(),
159 windowGeometry(), defaultWindowWidth, defaultWindowHeight);
165 if (QPlatformWindow::parent()) {
171 setGeometry(initialGeometry);
173 if (window()->flags() & Qt::ExpandedClientAreaHint) {
176 QRect frameGeometryWithFrame = QCocoaScreen::mapFromNative(
177 [NSWindow frameRectForContentRect:QCocoaScreen::mapToNative(initialGeometry)
178 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
179 if (qt_window_private(window())->positionPolicy == QWindowPrivate::WindowFrameExclusive)
180 initialGeometry = frameGeometryWithFrame;
182 initialGeometry.setSize(frameGeometryWithFrame.size());
190 QPlatformWindow::setGeometry(initialGeometry);
198 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
200 m_safeAreaInsetsObserver = QMacKeyValueObserver(
201 m_view, @
"safeAreaInsets", [
this] {
204 QMetaObject::invokeMethod(
this, [
this]{
205 updateSafeAreaMarginsIfNeeded();
206 }, Qt::QueuedConnection);
207 }, NSKeyValueObservingOptionNew);
213 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
223 qCDebug(lcQpaWindow) <<
"QCocoaWindow::~QCocoaWindow" << window();
225 QMacAutoReleasePool pool;
227 if (window()->flags() & Qt::ExpandedClientAreaHint) {
231 auto readjustedGeometry = geometry();
232 QRect contentRectWithFrame = QCocoaScreen::mapFromNative(
233 [NSWindow contentRectForFrameRect:QCocoaScreen::mapToNative(geometry())
234 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
235 readjustedGeometry = contentRectWithFrame;
236 setGeometry(readjustedGeometry, QWindowPrivate::PositionPolicy::WindowFrameExclusive);
241 if (!isForeignWindow() || QPlatformWindow::parent())
242 [m_view removeFromSuperview];
244 m_safeAreaInsetsObserver = {};
249 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
252 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
253 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
255 vulcanInstance->destroySurface(m_vulkanSurface);
262 [NSNotificationCenter.defaultCenter
263 postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
271 static_cast<
QCocoaScreen *>(screen())->maybeStopDisplayLink();
276 auto format = window()->requestedFormat();
277 if (
auto *view = qnsview_cast(m_view); view.colorSpace) {
278 auto colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(view.colorSpace.ICCProfileData));
279 if (!colorSpace.isValid()) {
280 qCWarning(lcQpaWindow) <<
"Failed to parse ICC profile for" << view.colorSpace
281 <<
"with ICC data" << view.colorSpace.ICCProfileData;
283 format.setColorSpace(colorSpace);
290 return ![m_view isKindOfClass:[QNSView
class]];
295 return QPlatformWindow::geometry();
302 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
305 return QCocoaScreen::mapFromNative(contentRect).toRect();
307 return QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
312
313
314
315
316
317
318
328 if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
331 return m_normalGeometry;
339 if (windowState() != Qt::WindowNoState)
342 m_normalGeometry = geometry();
348 setGeometry(rect, qt_window_private(window())->positionPolicy);
353 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setGeometry" << window() << rectIn << positionPolicy;
354 QMacAutoReleasePool pool;
357 if (positionPolicy == QWindowPrivate::WindowFrameInclusive) {
361 const QMargins margins = frameMargins();
362 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
363 qCDebug(lcQpaWindow) <<
"Adjusted content geometry to" << rect <<
"by removing frame margins" << margins;
366 QPlatformWindow::setGeometry(rect);
368 const QRect originalGeometry = actualGeometry();
376 NSRect bounds = QCocoaScreen::mapToNative(rect);
377 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
380 m_view.frame = QCocoaWindow::mapToNative(rect, m_view.superview);
383 if (actualGeometry() == originalGeometry) {
390 qCDebug(lcQpaWindow) <<
"Native geometry didn't change. Reporting geometry change manually.";
397 QMacAutoReleasePool pool;
404 QMarginsF viewSafeAreaMargins = {
405 m_view.safeAreaInsets.left,
406 m_view.safeAreaInsets.top,
407 m_view.safeAreaInsets.right,
408 m_view.safeAreaInsets.bottom
415 auto screenRect = m_view.window.screen.frame;
416 auto screenInsets = m_view.window.screen.safeAreaInsets;
417 auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect(
418 NSMinX(screenRect) + screenInsets.left,
419 NSMinY(screenRect) + screenInsets.bottom,
420 NSWidth(screenRect) - screenInsets.left - screenInsets.right,
421 NSHeight(screenRect) - screenInsets.top - screenInsets.bottom
424 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
425 [m_view.window convertRectToScreen:
426 [m_view convertRect:m_view.bounds toView:nil]]
432 QMarginsF screenSafeAreaMargins = {
433 qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left),
434 qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top),
435 qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right),
436 qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom)
439 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
444 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
445 m_lastReportedSafeAreaMargins = safeAreaMargins();
452 switch (NSApp.currentEvent.type) {
453 case NSEventTypeLeftMouseDown:
454 case NSEventTypeRightMouseDown:
455 case NSEventTypeOtherMouseDown:
456 case NSEventTypeMouseMoved:
457 case NSEventTypeLeftMouseDragged:
458 case NSEventTypeRightMouseDragged:
459 case NSEventTypeOtherMouseDragged:
462 [m_view.window performWindowDragWithEvent:NSApp.currentEvent];
471 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setVisible" << window() << visible;
484 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping setting visibility";
488 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
489 qCDebug(lcQpaWindow) <<
"No change in visible status. Ignoring.";
494 qCWarning(lcQpaWindow) <<
"Already setting window visible!";
498 QScopedValueRollback<
bool> rollback(m_inSetVisible,
true);
500 QMacAutoReleasePool pool;
502 if (window()->transientParent())
503 parentCocoaWindow =
static_cast<
QCocoaWindow *>(window()->transientParent()->handle());
505 auto eventDispatcher = [] {
506 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
519 if (parentCocoaWindow) {
522 setGeometry(windowGeometry());
524 if (window()->type() == Qt::Popup) {
527 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
528 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
529 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
530 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
531 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
540 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
544 applyWindowState(window()->windowStates());
546 if (window()->windowState() != Qt::WindowMinimized) {
547 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
549 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
550 if (!nativeParentWindow.attachedSheet)
551 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
553 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
554 }
else if (window()->modality() == Qt::ApplicationModal) {
556 eventDispatcher()->beginModalSession(window());
557 }
else if (m_view.window.canBecomeKeyWindow) {
558 bool shouldBecomeKeyNow = !NSApp.modalWindow
559 || m_view.window.worksWhenModal
560 || !NSApp.modalWindow.visible;
564 if ([m_view.window isKindOfClass:[NSPanel
class]])
565 shouldBecomeKeyNow &= !(
static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
567 if (shouldBecomeKeyNow)
568 [m_view.window makeKeyAndOrderFront:nil];
570 [m_view.window orderFront:nil];
572 [m_view.window orderFront:nil];
579 if (eventDispatcher()->hasModalSession())
580 eventDispatcher()->endModalSession(window());
581 else if ([m_view.window isSheet])
582 [m_view.window.sheetParent endSheet:m_view.window];
589 [m_view.window orderOut:nil];
591 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
596 NSWindow *mainWindow = [NSApp mainWindow];
597 if (mainWindow && [mainWindow canBecomeKeyWindow])
598 [mainWindow makeKeyWindow];
612 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
613 && m_view.nextValidKeyView.window != m_view.window) {
614 qCDebug(lcQpaWindow) <<
"Detected nextValidKeyView" << m_view.nextValidKeyView
615 <<
"in different window" << m_view.nextValidKeyView.window
616 <<
"Resetting" << m_view.window <<
"first responder to nil.";
617 [m_view.window makeFirstResponder:nil];
622 if (parentCocoaWindow && window()->type() == Qt::Popup) {
623 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
624 if (m_resizableTransientParent
625 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
627 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
632NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
634 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
636 NSInteger windowLevel = NSNormalWindowLevel;
638 if (type == Qt::Tool)
639 windowLevel = NSFloatingWindowLevel;
640 else if ((type & Qt::Popup) == Qt::Popup)
641 windowLevel = NSPopUpMenuWindowLevel;
644 if (flags & Qt::WindowStaysOnTopHint)
645 windowLevel = NSModalPanelWindowLevel;
647 if (type == Qt::ToolTip)
648 windowLevel = NSScreenSaverWindowLevel;
650 auto *transientParent = window()->transientParent();
651 if (transientParent && transientParent->handle()) {
671 auto *transientCocoaWindow =
static_cast<QCocoaWindow *>(transientParent->handle());
672 auto *nsWindow = transientCocoaWindow->nativeWindow();
677 if (type != Qt::Window)
678 windowLevel = qMax(windowLevel, nsWindow.level);
684NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
const
686 const Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
692 NSUInteger styleMask = [&]{
694 if (flags & Qt::FramelessWindowHint)
695 return NSWindowStyleMaskBorderless;
698 if (windowIsPopupType(type))
699 return NSWindowStyleMaskBorderless;
701 if (flags & Qt::CustomizeWindowHint) {
704 return flags & Qt::WindowTitleHint
705 ? NSWindowStyleMaskTitled
706 : NSWindowStyleMaskBorderless;
709 return NSWindowStyleMaskTitled;
718 styleMask |= NSWindowStyleMaskClosable
719 | NSWindowStyleMaskMiniaturizable;
721 if (type != Qt::Popup)
722 styleMask |= NSWindowStyleMaskResizable;
724 if (type == Qt::Tool)
725 styleMask |= NSWindowStyleMaskUtilityWindow;
727 if (flags & Qt::ExpandedClientAreaHint)
728 styleMask |= NSWindowStyleMaskFullSizeContentView;
731 styleMask |= (m_view.window.styleMask & (
732 NSWindowStyleMaskFullScreen
733 | NSWindowStyleMaskUnifiedTitleAndToolbar
734 | NSWindowStyleMaskDocModalWindow
735 | NSWindowStyleMaskNonactivatingPanel
736 | NSWindowStyleMaskHUDWindow));
741bool QCocoaWindow::isFixedSize()
const
743 return windowMinimumSize().isValid() && windowMaximumSize().isValid()
744 && windowMinimumSize() == windowMaximumSize();
747void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
749 if (!isContentView())
752 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
753 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
754 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
755 { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
758 bool hideButtons =
true;
759 for (
const auto &[button, buttonHint] : buttons) {
762 if (button == NSWindowMiniaturizeButton)
763 enabled = window()->type() != Qt::Dialog;
766 if (windowFlags & Qt::CustomizeWindowHint)
767 enabled = windowFlags & buttonHint;
771 if (button == NSWindowZoomButton && isFixedSize())
777 if (button == NSWindowCloseButton && enabled
778 && QWindowPrivate::get(window())->blockedByModalWindow) {
785 [m_view.window standardWindowButton:button].enabled = enabled;
786 hideButtons &= !enabled;
790 for (
const auto &[button, buttonHint] : buttons)
791 [m_view.window standardWindowButton:button].hidden = hideButtons;
794void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
800 QMacAutoReleasePool pool;
802 if (!isContentView())
805 qCDebug(lcQpaWindow) <<
"Setting" << flags <<
"for" << window();
816 QScopedValueRollback<
bool> geometryChangeBlocker(m_inSetStyleMask,
true);
818 const auto newMask = windowStyleMask(flags);
819 const bool expandedClientAreaChanged = (
820 (newMask & NSWindowStyleMaskFullSizeContentView) !=
821 (m_view.window.styleMask & NSWindowStyleMaskFullSizeContentView));
822 const auto frameGeometry = window()->frameGeometry();
824 m_view.window.styleMask = newMask;
826 if (expandedClientAreaChanged) {
828 setGeometry(frameGeometry.marginsRemoved(frameMargins()),
829 QWindowPrivate::PositionPolicy::WindowFrameExclusive);
833 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
834 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
835 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
836 const bool enableFullScreen = m_view.window.qt_fullScreen
837 || !(flags & Qt::CustomizeWindowHint)
838 || (flags & Qt::WindowFullscreenButtonHint);
839 if (enableFullScreen) {
840 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
841 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
843 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
844 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
846 m_view.window.collectionBehavior = behavior;
851 m_view.window.level =
this->windowLevel(flags);
853 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
855 if (!(flags & Qt::FramelessWindowHint))
856 setWindowTitle(window()->title());
858 updateTitleBarButtons(flags);
867 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
868 if (m_view.window.ignoresMouseEvents != ignoreMouse)
869 m_view.window.ignoresMouseEvents = ignoreMouse;
871 m_view.window.titlebarAppearsTransparent = flags & Qt::NoTitleBarBackgroundHint;
877
878
879
880
881
882
883void QCocoaWindow::setWindowState(Qt::WindowStates state)
885 if (window()->isVisible())
886 applyWindowState(state);
889void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
891 if (!isContentView())
894 const Qt::WindowState currentState = QWindowPrivate::effectiveState(windowState());
895 const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
897 if (newState == currentState)
900 qCDebug(lcQpaWindow) <<
"Applying" << newState <<
"to" << window() <<
"in" << currentState;
902 const NSSize contentSize = m_view.frame.size;
903 if (contentSize.width <= 0 || contentSize.height <= 0) {
906 qWarning(
"invalid window content view size, check your window geometry");
907 handleWindowStateChanged(HandleUnconditionally);
911 const NSWindow *nsWindow = m_view.window;
913 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
914 && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
915 qWarning() << window()->type() <<
"windows cannot be made" << newState;
916 handleWindowStateChanged(HandleUnconditionally);
920 const id sender = nsWindow;
923 switch (currentState) {
924 case Qt::WindowMinimized:
925 [nsWindow deminiaturize:sender];
930 case Qt::WindowFullScreen: {
941 if (newState == windowState())
945 case Qt::WindowFullScreen:
948 case Qt::WindowMaximized:
951 case Qt::WindowMinimized:
952 [nsWindow miniaturize:sender];
954 case Qt::WindowNoState:
955 if (windowState() == Qt::WindowMaximized)
963Qt::WindowStates QCocoaWindow::windowState()
const
965 Qt::WindowStates states = Qt::WindowNoState;
966 NSWindow *window = m_view.window;
968 if (window.miniaturized)
969 states |= Qt::WindowMinimized;
973 if (window.qt_fullScreen) {
974 states |= Qt::WindowFullScreen;
975 }
else if ((window.zoomed && !isTransitioningToFullScreen())
976 || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) {
977 states |= Qt::WindowMaximized;
986void QCocoaWindow::toggleMaximized()
988 const NSWindow *window = m_view.window;
992 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
993 window.styleMask |= NSWindowStyleMaskResizable;
995 const id sender = window;
996 [window zoom:sender];
999 window.styleMask &= ~NSWindowStyleMaskResizable;
1002void QCocoaWindow::windowWillZoom()
1004 updateNormalGeometry();
1007void QCocoaWindow::toggleFullScreen()
1009 const NSWindow *window = m_view.window;
1014 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1016 const id sender = window;
1017 [window toggleFullScreen:sender];
1020void QCocoaWindow::windowWillEnterFullScreen()
1022 if (!isContentView())
1025 updateNormalGeometry();
1030 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1033bool QCocoaWindow::isTransitioningToFullScreen()
const
1035 NSWindow *window = m_view.window;
1036 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
1039void QCocoaWindow::windowDidEnterFullScreen()
1041 if (!isContentView())
1044 Q_ASSERT_X(m_view.window.qt_fullScreen,
"QCocoaWindow",
1045 "FullScreen category processes window notifications first");
1048 setWindowFlags(window()->flags());
1050 handleWindowStateChanged();
1053void QCocoaWindow::windowWillExitFullScreen()
1055 if (!isContentView())
1060 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1063void QCocoaWindow::windowDidExitFullScreen()
1065 if (!isContentView())
1068 Q_ASSERT_X(!m_view.window.qt_fullScreen,
"QCocoaWindow",
1069 "FullScreen category processes window notifications first");
1072 setWindowFlags(window()->flags());
1074 Qt::WindowState requestedState = window()->windowState();
1077 handleWindowStateChanged();
1079 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
1082 applyWindowState(requestedState);
1086void QCocoaWindow::windowDidMiniaturize()
1088 if (!isContentView())
1091 handleWindowStateChanged();
1094void QCocoaWindow::windowDidDeminiaturize()
1096 if (!isContentView())
1099 Qt::WindowState requestedState = window()->windowState();
1101 handleWindowStateChanged();
1103 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
1106 applyWindowState(requestedState);
1110void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
1112 Qt::WindowStates currentState = windowState();
1113 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
1116 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleWindowStateChanged" <<
1117 m_lastReportedWindowState <<
"-->" << currentState;
1119 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
1120 window(), currentState, m_lastReportedWindowState);
1121 m_lastReportedWindowState = currentState;
1126void QCocoaWindow::setWindowTitle(
const QString &title)
1128 QMacAutoReleasePool pool;
1130 if (!isContentView())
1133 m_view.window.title = title.toNSString();
1135 if (title.isEmpty() && !window()->filePath().isEmpty()) {
1137 setWindowFilePath(window()->filePath());
1141void QCocoaWindow::setWindowFilePath(
const QString &filePath)
1143 QMacAutoReleasePool pool;
1145 if (!isContentView())
1148 if (window()->title().isNull())
1149 [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
1151 m_view.window.representedFilename = filePath.toNSString();
1154 setWindowIcon(window()->icon());
1157void QCocoaWindow::setWindowIcon(
const QIcon &icon)
1159 QMacAutoReleasePool pool;
1161 if (!isContentView())
1164 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
1170 if (icon.isNull()) {
1171 iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename];
1175 auto fallbackSize = QSizeF::fromCGSize(iconButton.frame.size) * qGuiApp->devicePixelRatio();
1176 iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize.toSize()];
1180void QCocoaWindow::setAlertState(
bool enabled)
1182 if (m_alertRequest == NoAlertRequest && enabled) {
1183 m_alertRequest = [NSApp requestUserAttention:NSCriticalRequest];
1184 }
else if (m_alertRequest != NoAlertRequest && !enabled) {
1185 [NSApp cancelUserAttentionRequest:m_alertRequest];
1186 m_alertRequest = NoAlertRequest;
1190bool QCocoaWindow::isAlertState()
const
1192 return m_alertRequest != NoAlertRequest;
1195void QCocoaWindow::raise()
1197 qCDebug(lcQpaWindow) <<
"QCocoaWindow::raise" << window();
1200 if (isContentView()) {
1201 if (m_view.window.visible) {
1208 QMacAutoReleasePool pool;
1209 [m_view.window orderFront:m_view.window];
1211 static bool raiseProcess = qt_mac_resolveOption(
true,
"QT_MAC_SET_RAISE_PROCESS");
1213 [NSApp activateIgnoringOtherApps:YES];
1216 [m_view.superview addSubview:m_view positioned:NSWindowAbove relativeTo:nil];
1220void QCocoaWindow::lower()
1222 qCDebug(lcQpaWindow) <<
"QCocoaWindow::lower" << window();
1224 if (isContentView()) {
1225 if (m_view.window.visible)
1226 [m_view.window orderBack:m_view.window];
1228 [m_view.superview addSubview:m_view positioned:NSWindowBelow relativeTo:nil];
1232bool QCocoaWindow::isExposed()
const
1234 return !m_exposedRect.isEmpty();
1238
1239
1240
1241
1242
1243bool QCocoaWindow::isEmbedded()
const
1249 return m_isEmbedded;
1252bool QCocoaWindow::isOpaque()
const
1256 static GLint openglSourfaceOrder = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
1258 bool translucent = window()->format().alphaBufferSize() > 0
1259 || window()->opacity() < 1
1260 || !window()->mask().isEmpty()
1261 || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
1262 return !translucent;
1265void QCocoaWindow::propagateSizeHints()
1267 QMacAutoReleasePool pool;
1268 if (!isContentView())
1271 qCDebug(lcQpaWindow) <<
"QCocoaWindow::propagateSizeHints" << window()
1272 <<
"min:" << windowMinimumSize() <<
"max:" << windowMaximumSize()
1273 <<
"increment:" << windowSizeIncrement()
1274 <<
"base:" << windowBaseSize();
1276 const NSWindow *window = m_view.window;
1279 QSize minimumSize = windowMinimumSize();
1280 if (!minimumSize.isValid())
1281 minimumSize = QSize(0, 0);
1282 window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
1285 window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
1288 updateTitleBarButtons(
this->window()->flags());
1292 QSize sizeIncrement = windowSizeIncrement();
1293 if (sizeIncrement.isEmpty())
1294 sizeIncrement = QSize(1, 1);
1295 window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
1297 QRect rect = geometry();
1298 QSize baseSize = windowBaseSize();
1299 if (!baseSize.isNull() && baseSize.isValid())
1300 [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
1303void QCocoaWindow::setOpacity(qreal level)
1305 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setOpacity" << level;
1306 if (!isContentView())
1309 m_view.window.alphaValue = level;
1312void QCocoaWindow::setMask(
const QRegion ®ion)
1314 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setMask" << window() << region;
1316 if (!region.isEmpty()) {
1317 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
1318 for (
const QRect &r : region)
1319 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
1320 CAShapeLayer *maskLayer = [CAShapeLayer layer];
1321 maskLayer.path = maskPath;
1322 m_view.layer.mask = maskLayer;
1324 m_view.layer.mask = nil;
1328bool QCocoaWindow::setKeyboardGrabEnabled(
bool)
1333bool QCocoaWindow::setMouseGrabEnabled(
bool)
1338WId QCocoaWindow::winId()
const
1343void QCocoaWindow::setParent(
const QPlatformWindow *parentWindow)
1345 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0);
1348 recreateWindowIfNeeded();
1350 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1353NSView *QCocoaWindow::view()
const
1358NSWindow *QCocoaWindow::nativeWindow()
const
1360 return m_view.window;
1363void QCocoaWindow::updateEmbeddedState()
1365 const bool wasEmbedded = m_isEmbedded;
1367 if (QPlatformWindow::parent())
1370 if (!m_view.superview)
1375 auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1376 if (m_view == qtWindowController.window.contentView)
1381 if (m_isEmbedded != wasEmbedded) {
1382 qCDebug(lcQpaWindow) <<
"Updated embedded state from"
1383 << wasEmbedded <<
"to" << m_isEmbedded;
1389void QCocoaWindow::viewDidChangeFrame()
1396 handleGeometryChange();
1400
1401
1402
1403
1404
1405
1406void QCocoaWindow::viewDidChangeGlobalFrame()
1408 [m_view setNeedsDisplay:YES];
1412
1413
1414
1415
1416
1417void QCocoaWindow::viewDidMoveToSuperview(NSView *previousSuperview)
1419 qCDebug(lcQpaWindow) <<
"Done re-parenting" << m_view
1420 <<
"from" << previousSuperview <<
"into" << m_view.superview;
1423 updateEmbeddedState();
1427 handleGeometryChange();
1429 if (m_view.superview)
1430 [m_view setNeedsDisplay:YES];
1441 if (m_view.superview && !m_view.superview.flipped && !isContentView()) {
1442 if (m_view.autoresizingMask == NSViewNotSizable) {
1443 qCDebug(lcQpaWindow) <<
"Setting auto resizing mask on" << m_view
1444 <<
"in non-flipped superview to maintain stable y-positioning";
1445 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1446 m_view.autoresizingMask = NSViewMinYMargin;
1448 }
else if (previousSuperview && !previousSuperview.flipped
1449 && m_view.autoresizingMask == NSViewMinYMargin) {
1454 qCDebug(lcQpaWindow) <<
"Clearing auto resizing mask on" << m_view
1455 <<
"as explicit stable y-positioning is no longer needed";
1456 m_view.autoresizingMask = NSViewNotSizable;
1461
1462
1463
1464
1465void QCocoaWindow::viewDidMoveToWindow(NSWindow *previousWindow)
1467 qCDebug(lcQpaWindow) <<
"Done moving" << m_view
1468 <<
"from" << previousWindow <<
"to" << m_view.window;
1470 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController)) {
1471 qCDebug(lcQpaWindow) <<
"Retaining new window controller" << qtWindowController;
1472 [qtWindowController retain];
1474 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(previousWindow.windowController)) {
1475 qCDebug(lcQpaWindow) <<
"Releasing old window controller" << qtWindowController;
1476 [qtWindowController autorelease];
1482 auto *previousScreen = previousWindow ? QCocoaScreen::get(previousWindow.screen) :
nullptr;
1483 auto *currentScreen = m_view.window ? QCocoaScreen::get(m_view.window.screen) :
nullptr;
1484 if (currentScreen && currentScreen != previousScreen)
1485 windowDidChangeScreen();
1495void QCocoaWindow::windowDidMove()
1497 if (!isContentView())
1500 handleGeometryChange();
1503 handleWindowStateChanged();
1506void QCocoaWindow::windowDidResize()
1508 if (!isContentView())
1511 handleGeometryChange();
1513 if (!m_view.inLiveResize)
1514 handleWindowStateChanged();
1517void QCocoaWindow::windowWillStartLiveResize()
1522 m_inLiveResize =
true;
1525bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1530 return !m_inLiveResize;
1533void QCocoaWindow::windowDidEndLiveResize()
1535 m_inLiveResize =
false;
1537 if (!isContentView())
1540 handleWindowStateChanged();
1543void QCocoaWindow::windowDidBecomeKey()
1547 if (m_view.window.firstResponder != m_view)
1550 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1551 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1553 if (windowIsPopupType()) {
1554 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1559 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1560 window(), Qt::ActiveWindowFocusReason);
1563void QCocoaWindow::windowDidResignKey()
1567 if (m_view.window.firstResponder != m_view)
1570 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1571 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1580 NSWindow *newKeyWindow = [NSApp keyWindow];
1581 if (newKeyWindow && newKeyWindow != m_view.window
1582 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1583 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1584 <<
"is Qt window. Deferring focus window change.";
1589 if (!windowIsPopupType()) {
1590 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1591 nullptr, Qt::ActiveWindowFocusReason);
1595void QCocoaWindow::windowDidOrderOnScreen()
1599 if (QWindowPrivate::get(window())->isPopup()) {
1600 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1601 (QGuiApplicationPrivate::currentMouseWindow);
1604 [m_view setNeedsDisplay:YES];
1607void QCocoaWindow::windowDidOrderOffScreen()
1609 handleExposeEvent(QRegion());
1612 if (window()->type() & Qt::Window) {
1613 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1614 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1615 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1616 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1618 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1619 (windowUnderMouse, windowPoint, screenPoint);
1625void QCocoaWindow::windowDidChangeOcclusionState()
1631 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1632 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1633 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1636 [m_view setNeedsDisplay:YES];
1638 handleExposeEvent(QRegion());
1641void QCocoaWindow::windowDidChangeScreen()
1647 NSScreen *nsScreen = m_view.window.screen;
1649 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1650 QCocoaScreen::updateScreens();
1652 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1653 auto *currentScreen = QCocoaScreen::get(nsScreen);
1655 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1668 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1669 window(), currentScreen ? currentScreen->screen() :
nullptr);
1671 if (currentScreen && hasPendingUpdateRequest()) {
1676 currentScreen->requestUpdate();
1681 previousScreen->maybeStopDisplayLink();
1686bool QCocoaWindow::windowShouldClose()
1688 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1700 [[m_view.window retain] autorelease];
1702 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1707void QCocoaWindow::handleGeometryChange()
1709 const QRect newGeometry = actualGeometry();
1711 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1712 <<
"current" << geometry() <<
"new" << newGeometry;
1717 if (m_inSetStyleMask && !m_view.window) {
1718 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1725 if (!m_initialized) {
1727 QPlatformWindow::setGeometry(newGeometry);
1728 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1732 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1735 updateSafeAreaMarginsIfNeeded();
1739 if (!m_inSetGeometry)
1740 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1743void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1756 if (m_view.window.visible && m_view.window.screen
1757 && !geometry().size().isEmpty() && !region.isEmpty()
1758 && !m_view.hiddenOrHasHiddenAncestor) {
1759 m_exposedRect = region.boundingRect();
1761 m_exposedRect = QRect();
1764 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1765 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1768 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1773bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1775 if (type == Qt::Widget)
1776 type = window()->type();
1777 if (type == Qt::Tool)
1780 return ((type & Qt::Popup) == Qt::Popup);
1784
1785
1786
1787
1788
1789
1790
1791
1792bool QCocoaWindow::isContentView()
const
1794 return m_view.window.contentView == m_view;
1798
1799
1800
1801
1802
1803
1804
1805void QCocoaWindow::recreateWindowIfNeeded()
1807 QMacAutoReleasePool pool;
1809 auto *parentWindow =
static_cast<QCocoaWindow *>(QPlatformWindow::parent());
1811 const bool shouldManageTopLevelWindow = !parentWindow
1812 && !((window()->type() & Qt::SubWindow) == Qt::SubWindow)
1813 && !isForeignWindow();
1815 if (shouldManageTopLevelWindow) {
1816 auto *windowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1817 const bool isControllerContentView = m_view == windowController.window.contentView;
1818 const bool windowClassIsCorrect = [m_view.window isKindOfClass:windowClass()];
1820 if (!isControllerContentView || !windowClassIsCorrect) {
1821 qCDebug(lcQpaWindow) <<
"Creating new window controller for" << m_view;
1822 windowController = [[QNSWindowController alloc] initWithCocoaWindow:
this];
1824 m_view.postsFrameChangedNotifications = NO;
1825 windowController.window.contentView = m_view;
1826 m_view.postsFrameChangedNotifications = YES;
1829 [windowController release];
1831 qCDebug(lcQpaWindow) <<
"Re-using existing window controller" << windowController;
1833 }
else if (parentWindow) {
1834 if (m_view.superview != parentWindow->view()) {
1835 qCDebug(lcQpaWindow) <<
"Reparenting view to new parent" << parentWindow;
1836 [parentWindow->view() addSubview:m_view];
1838 }
else if (qnsview_cast(m_view.superview) && !isEmbedded()) {
1839 qCDebug(lcQpaWindow) <<
"Removing view from superview" << m_view.superview;
1840 [m_view removeFromSuperview];
1844Class QCocoaWindow::windowClass()
const
1846 const auto type = window()->type();
1847 return ((type & Qt::Popup) == Qt::Popup
1848 || (type & Qt::Dialog) == Qt::Dialog) ?
1849 QNSPanel.
class : QNSWindow.
class;
1852void QCocoaWindow::requestUpdate()
1854 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1855 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1857 if (updatesWithDisplayLink()) {
1858 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1859 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1860 QPlatformWindow::requestUpdate();
1864 QPlatformWindow::requestUpdate();
1868bool QCocoaWindow::updatesWithDisplayLink()
const
1871 return format().swapInterval() != 0;
1874void QCocoaWindow::deliverUpdateRequest()
1876 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1877 QScopedValueRollback<
bool> blocker(m_deliveringUpdateRequest,
true);
1879 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1884 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1885 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1886 <<
"due to" << qtMetalLayer <<
"needing display";
1892 qtMetalLayer.displayLock.unlock();
1895 QPlatformWindow::deliverUpdateRequest();
1898void QCocoaWindow::requestActivateWindow()
1900 QMacAutoReleasePool pool;
1901 [m_view.window makeFirstResponder:m_view];
1902 [m_view.window makeKeyWindow];
1906
1907
1908void QCocoaWindow::closeAllPopups()
1910 QGuiApplicationPrivate::instance()->closeAllPopups();
1912 removePopupMonitor();
1915void QCocoaWindow::removePopupMonitor()
1917 if (s_globalMouseMonitor) {
1918 [NSEvent removeMonitor:s_globalMouseMonitor];
1919 s_globalMouseMonitor = nil;
1921 if (s_applicationActivationObserver) {
1922 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1923 s_applicationActivationObserver = nil;
1927void QCocoaWindow::setupPopupMonitor()
1934 if (!s_globalMouseMonitor) {
1936 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1937 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1938 | NSEventMaskMouseMoved;
1939 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1940 handler:^(NSEvent *e){
1941 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1942 removePopupMonitor();
1945 const auto eventType = cocoaEvent2QtMouseEvent(e);
1946 if (eventType == QEvent::MouseMove) {
1947 if (s_windowUnderMouse) {
1948 QWindow *window = s_windowUnderMouse->window();
1949 const auto button = cocoaButton2QtButton(e);
1950 const auto buttons = currentlyPressedMouseButtons();
1951 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1952 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1953 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1954 buttons, button, eventType);
1964 if (!s_applicationActivationObserver) {
1965 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1966 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1967 object:nil queue:nil
1968 usingBlock:^(NSNotification *){
1974QCocoaNSWindow *QCocoaWindow::createNSWindow()
1976 QMacAutoReleasePool pool;
1978 Qt::WindowType type = window()->type();
1979 Qt::WindowFlags flags = window()->flags();
1981 QRect rect = geometry();
1983 QScreen *targetScreen =
nullptr;
1984 for (QScreen *screen : QGuiApplication::screens()) {
1985 if (screen->geometry().contains(rect.topLeft())) {
1986 targetScreen = screen;
1991 NSWindowStyleMask styleMask = windowStyleMask(flags);
1993 if (!targetScreen) {
1994 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
1995 targetScreen = QGuiApplication::primaryScreen();
1999 styleMask = NSWindowStyleMaskBorderless;
2002 rect.translate(-targetScreen->geometry().topLeft());
2003 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
2004 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
2006 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
2011 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
2012 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
2013 <<
"Adjusting style mask during creation";
2014 styleMask = NSWindowStyleMaskBorderless;
2019 QCocoaNSWindow *nsWindow = [[windowClass() alloc] initWithContentRect:contentRect
2024 backing:NSBackingStoreBuffered defer:NO
2025 screen:targetCocoaScreen->nativeScreen()
2026 platformWindow:
this];
2030 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
2035 if (!resultingScreen)
2036 resultingScreen = targetCocoaScreen;
2038 if (resultingScreen->screen() != window()->screen()) {
2039 QWindowSystemInterface::handleWindowScreenChanged<
2040 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
2043 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
2044 [](QNSWindowDelegate *delegate) { [delegate release]; });
2045 nsWindow.delegate = sharedDelegate.get();
2051 [nsWindow setReleasedWhenClosed:NO];
2053 if (alwaysShowToolWindow()) {
2054 static dispatch_once_t onceToken;
2055 dispatch_once(&onceToken, ^{
2056 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
2057 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2058 name:NSApplicationWillResignActiveNotification object:nil];
2059 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2060 name:NSApplicationWillBecomeActiveNotification object:nil];
2064 nsWindow.restorable = NO;
2065 nsWindow.level = windowLevel(flags);
2066 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
2068 if ([nsWindow isKindOfClass:NSPanel.
class]) {
2070 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
2073 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
2074 | NSWindowCollectionBehaviorMoveToActiveSpace;
2076 if ((type & Qt::Popup) == Qt::Popup) {
2077 nsWindow.hasShadow = YES;
2078 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
2079 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
2080 setupPopupMonitor();
2091 if (
auto *qtView = qnsview_cast(m_view))
2092 nsWindow.colorSpace = qtView.colorSpace;
2097bool QCocoaWindow::alwaysShowToolWindow()
const
2099 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2102bool QCocoaWindow::setWindowModified(
bool modified)
2104 QMacAutoReleasePool pool;
2106 if (!isContentView())
2109 m_view.window.documentEdited = modified;
2113void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2118QCocoaMenuBar *QCocoaWindow::menubar()
const
2123void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2125 QMacAutoReleasePool pool;
2128 if (isForeignWindow())
2131 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2133 QNSView *view = qnsview_cast(m_view);
2134 if (cursor == view.cursor)
2137 view.cursor = cursor;
2143 [m_view.window invalidateCursorRectsForView:m_view];
2150 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2151 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2152 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2153 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2154 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2155 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2156 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2157 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2158 location:locationInWindow modifierFlags:0 timestamp:0
2159 windowNumber:m_view.window.windowNumber context:nil
2160 eventNumber:0 trackingNumber:0 userData:0]];
2164void QCocoaWindow::registerTouch(
bool enable)
2166 m_registerTouchCount += enable ? 1 : -1;
2167 if (enable && m_registerTouchCount == 1)
2168 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2169 else if (m_registerTouchCount == 0)
2170 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2173qreal QCocoaWindow::devicePixelRatio()
const
2180 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2181 return backingSize.height;
2184QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2186 QWindow *targetWindow = window();
2187 for (QObject *child : targetWindow->children())
2188 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2189 if (QPlatformWindow *handle = childWindow->handle())
2190 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2191 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2193 return targetWindow;
2196bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2201 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2211 QWindow *modalWindow =
nullptr;
2212 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2213 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2214 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2215 <<
"blocked by" << modalWindow;
2220 if (m_inSetVisible) {
2221 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2222 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2229bool QCocoaWindow::windowEvent(QEvent *event)
2231 switch (event->type()) {
2232 case QEvent::WindowBlocked:
2233 case QEvent::WindowUnblocked:
2234 updateTitleBarButtons(window()->flags());
2240 return QPlatformWindow::windowEvent(event);
2243QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2247 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2248 : NSMakePoint(0, 0);
2249 const NSRect visibleRect = [m_view visibleRect];
2251 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2254QMargins QCocoaWindow::frameMargins()
const
2256 QMacAutoReleasePool pool;
2261 if (QPlatformWindow::parent())
2267 if (m_view.window) {
2268 frameRect = m_view.window.frame;
2269 contentRect = [m_view.window contentRectForFrameRect:frameRect];
2271 contentRect = m_view.frame;
2272 frameRect = [NSWindow frameRectForContentRect:contentRect styleMask:windowStyleMask(window()->flags())];
2275 return QMargins(frameRect.origin.x - contentRect.origin.x,
2276 (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height),
2277 (frameRect.origin.x + frameRect.size.width) - (contentRect.origin.x + contentRect.size.width),
2278 contentRect.origin.y - contentRect.origin.y);
2281void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2283 m_frameStrutEventsEnabled = enabled;
2286QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2288 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2289 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2290 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2293QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2295 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2296 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2297 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2300CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2302 if (!referenceView || referenceView.flipped)
2303 return point.toCGPoint();
2305 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2308CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2310 if (!referenceView || referenceView.flipped)
2311 return rect.toCGRect();
2313 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2316QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2318 if (!referenceView || referenceView.flipped)
2319 return QPointF::fromCGPoint(point);
2321 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2324QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2326 if (!referenceView || referenceView.flipped)
2327 return QRectF::fromCGRect(rect);
2329 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2332CALayer *QCocoaWindow::contentLayer()
const
2334 auto *layer = m_view.layer;
2335 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2336 layer = containerLayer.contentLayer;
2340void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2341 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2342 NSVisualEffectState activationState)
2344 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2345 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2346 <<
"in views without a container layer";
2350 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2351 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2352 << blendMode <<
"and activation state" << activationState;
2354 NSVisualEffectView *effectView =
nullptr;
2355 if (m_effectViews.contains(identifier)) {
2356 effectView = m_effectViews.value(identifier);
2357 if (rect.isEmpty()) {
2358 [effectView removeFromSuperview];
2359 m_effectViews.remove(identifier);
2362 }
else if (!rect.isEmpty()) {
2363 effectView = [NSVisualEffectView
new];
2366 effectView.wantsLayer = YES;
2367 effectView.layer.zPosition = -FLT_MAX;
2368 [m_view addSubview:effectView];
2369 m_effectViews.insert(identifier, effectView);
2375 effectView.frame = rect.toCGRect();
2376 effectView.material = material;
2377 effectView.blendingMode = blendMode;
2378 effectView.state = activationState;
2381#ifndef QT_NO_DEBUG_STREAM
2382QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2384 QDebugStateSaver saver(debug);
2386 debug <<
"QCocoaWindow(" << (
const void *)window;
2388 debug <<
", window=" << window->window();
2396#include "moc_qcocoawindow.cpp"
void setGeometry(const QRect &rect, QWindowPrivate::PositionPolicy positionPolicy)
QMargins safeAreaMargins() const override
The safe area margins of a window represent the area that is safe to place content within,...
QRect normalGeometry() const override
the geometry of the window as it will appear when shown as a normal (not maximized or full screen) to...
bool isForeignWindow() const override
QRect geometry() const override
Returns the current geometry of a window.
void updateSafeAreaMarginsIfNeeded()
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
void updateNormalGeometry()
QRect actualGeometry() const
bool isEmbedded() const override
Returns true if the window is a child of a non-Qt window.
static QPointer< QCocoaWindow > s_windowUnderMouse
bool isContentView() const
Checks if the window is the content view of its immediate NSWindow.
void recreateWindowIfNeeded()
Recreates (or removes) the NSWindow for this QWindow, if needed.
void handleGeometryChange()
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
bool startSystemMove() override
Reimplement this method to start a system move operation if the system supports it and return true to...
static const int NoAlertRequest
void initialize() override
Called as part of QWindow::create(), after constructing the window.
QSurfaceFormat format() const override
Returns the actual surface format of the window.
QRect window() const
Returns the window rectangle.
QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QMacAccessibilityElement, NSObject -(void) invalidate;) QT_BEGIN_NAMESPACE bool QAccessibleCache
#define Q_NOTIFICATION_PREFIX
static void qRegisterNotificationCallbacks()
const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")