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 initialGeometry.setSize(frameGeometryWithFrame.size());
187 QPlatformWindow::setGeometry(initialGeometry);
195 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
197 m_safeAreaInsetsObserver = QMacKeyValueObserver(
198 m_view, @
"safeAreaInsets", [
this] {
201 QMetaObject::invokeMethod(
this, [
this]{
202 updateSafeAreaMarginsIfNeeded();
203 }, Qt::QueuedConnection);
204 }, NSKeyValueObservingOptionNew);
210 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
220 qCDebug(lcQpaWindow) <<
"QCocoaWindow::~QCocoaWindow" << window();
222 QMacAutoReleasePool pool;
224 if (window()->flags() & Qt::ExpandedClientAreaHint) {
228 auto readjustedGeometry = geometry();
229 QRect contentRectWithFrame = QCocoaScreen::mapFromNative(
230 [NSWindow contentRectForFrameRect:QCocoaScreen::mapToNative(geometry())
231 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
232 readjustedGeometry.setSize(contentRectWithFrame.size());
233 setGeometry(readjustedGeometry, QWindowPrivate::PositionPolicy::WindowFrameExclusive);
238 if (!isForeignWindow() || QPlatformWindow::parent())
239 [m_view removeFromSuperview];
241 m_safeAreaInsetsObserver = {};
246 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
249 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
250 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
252 vulcanInstance->destroySurface(m_vulkanSurface);
259 [NSNotificationCenter.defaultCenter
260 postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
268 static_cast<
QCocoaScreen *>(screen())->maybeStopDisplayLink();
273 auto format = window()->requestedFormat();
274 if (
auto *view = qnsview_cast(m_view); view.colorSpace) {
275 auto colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(view.colorSpace.ICCProfileData));
276 if (!colorSpace.isValid()) {
277 qCWarning(lcQpaWindow) <<
"Failed to parse ICC profile for" << view.colorSpace
278 <<
"with ICC data" << view.colorSpace.ICCProfileData;
280 format.setColorSpace(colorSpace);
287 return ![m_view isKindOfClass:[QNSView
class]];
292 return QPlatformWindow::geometry();
299 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
302 return QCocoaScreen::mapFromNative(contentRect).toRect();
304 return QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
309
310
311
312
313
314
315
325 if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
328 return m_normalGeometry;
336 if (windowState() != Qt::WindowNoState)
339 m_normalGeometry = geometry();
345 setGeometry(rect, qt_window_private(window())->positionPolicy);
350 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setGeometry" << window() << rectIn << positionPolicy;
351 QMacAutoReleasePool pool;
354 if (positionPolicy == QWindowPrivate::WindowFrameInclusive) {
358 const QMargins margins = frameMargins();
359 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
360 qCDebug(lcQpaWindow) <<
"Adjusted content geometry to" << rect <<
"by removing frame margins" << margins;
363 QPlatformWindow::setGeometry(rect);
365 const QRect originalGeometry = actualGeometry();
373 NSRect bounds = QCocoaScreen::mapToNative(rect);
374 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
377 m_view.frame = QCocoaWindow::mapToNative(rect, m_view.superview);
380 if (actualGeometry() == originalGeometry) {
387 qCDebug(lcQpaWindow) <<
"Native geometry didn't change. Reporting geometry change manually.";
394 QMacAutoReleasePool pool;
401 QMarginsF viewSafeAreaMargins = {
402 m_view.safeAreaInsets.left,
403 m_view.safeAreaInsets.top,
404 m_view.safeAreaInsets.right,
405 m_view.safeAreaInsets.bottom
412 auto screenRect = m_view.window.screen.frame;
413 auto screenInsets = m_view.window.screen.safeAreaInsets;
414 auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect(
415 NSMinX(screenRect) + screenInsets.left,
416 NSMinY(screenRect) + screenInsets.bottom,
417 NSWidth(screenRect) - screenInsets.left - screenInsets.right,
418 NSHeight(screenRect) - screenInsets.top - screenInsets.bottom
421 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
422 [m_view.window convertRectToScreen:
423 [m_view convertRect:m_view.bounds toView:nil]]
429 QMarginsF screenSafeAreaMargins = {
430 qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left),
431 qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top),
432 qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right),
433 qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom)
436 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
441 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
442 m_lastReportedSafeAreaMargins = safeAreaMargins();
449 switch (NSApp.currentEvent.type) {
450 case NSEventTypeLeftMouseDown:
451 case NSEventTypeRightMouseDown:
452 case NSEventTypeOtherMouseDown:
453 case NSEventTypeMouseMoved:
454 case NSEventTypeLeftMouseDragged:
455 case NSEventTypeRightMouseDragged:
456 case NSEventTypeOtherMouseDragged:
459 [m_view.window performWindowDragWithEvent:NSApp.currentEvent];
468 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setVisible" << window() << visible;
481 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping setting visibility";
485 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
486 qCDebug(lcQpaWindow) <<
"No change in visible status. Ignoring.";
491 qCWarning(lcQpaWindow) <<
"Already setting window visible!";
495 QScopedValueRollback<
bool> rollback(m_inSetVisible,
true);
497 QMacAutoReleasePool pool;
499 if (window()->transientParent())
500 parentCocoaWindow =
static_cast<
QCocoaWindow *>(window()->transientParent()->handle());
502 auto eventDispatcher = [] {
503 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
516 if (parentCocoaWindow) {
519 setGeometry(windowGeometry());
521 if (window()->type() == Qt::Popup) {
524 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
525 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
526 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
527 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
528 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
537 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
541 applyWindowState(window()->windowStates());
543 if (window()->windowState() != Qt::WindowMinimized) {
544 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
546 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
547 if (!nativeParentWindow.attachedSheet)
548 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
550 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
551 }
else if (window()->modality() == Qt::ApplicationModal) {
553 eventDispatcher()->beginModalSession(window());
554 }
else if (m_view.window.canBecomeKeyWindow) {
555 bool shouldBecomeKeyNow = !NSApp.modalWindow
556 || m_view.window.worksWhenModal
557 || !NSApp.modalWindow.visible;
561 if ([m_view.window isKindOfClass:[NSPanel
class]])
562 shouldBecomeKeyNow &= !(
static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
564 if (shouldBecomeKeyNow)
565 [m_view.window makeKeyAndOrderFront:nil];
567 [m_view.window orderFront:nil];
569 [m_view.window orderFront:nil];
576 if (eventDispatcher()->hasModalSession())
577 eventDispatcher()->endModalSession(window());
578 else if ([m_view.window isSheet])
579 [m_view.window.sheetParent endSheet:m_view.window];
586 [m_view.window orderOut:nil];
588 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
593 NSWindow *mainWindow = [NSApp mainWindow];
594 if (mainWindow && [mainWindow canBecomeKeyWindow])
595 [mainWindow makeKeyWindow];
609 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
610 && m_view.nextValidKeyView.window != m_view.window) {
611 qCDebug(lcQpaWindow) <<
"Detected nextValidKeyView" << m_view.nextValidKeyView
612 <<
"in different window" << m_view.nextValidKeyView.window
613 <<
"Resetting" << m_view.window <<
"first responder to nil.";
614 [m_view.window makeFirstResponder:nil];
619 if (parentCocoaWindow && window()->type() == Qt::Popup) {
620 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
621 if (m_resizableTransientParent
622 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
624 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
629NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
631 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
633 NSInteger windowLevel = NSNormalWindowLevel;
635 if (type == Qt::Tool)
636 windowLevel = NSFloatingWindowLevel;
637 else if ((type & Qt::Popup) == Qt::Popup)
638 windowLevel = NSPopUpMenuWindowLevel;
641 if (flags & Qt::WindowStaysOnTopHint)
642 windowLevel = NSModalPanelWindowLevel;
644 if (type == Qt::ToolTip)
645 windowLevel = NSScreenSaverWindowLevel;
647 auto *transientParent = window()->transientParent();
648 if (transientParent && transientParent->handle()) {
668 auto *transientCocoaWindow =
static_cast<QCocoaWindow *>(transientParent->handle());
669 auto *nsWindow = transientCocoaWindow->nativeWindow();
674 if (type != Qt::Window)
675 windowLevel = qMax(windowLevel, nsWindow.level);
681NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
const
683 const Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
689 NSUInteger styleMask = [&]{
691 if (flags & Qt::FramelessWindowHint)
692 return NSWindowStyleMaskBorderless;
695 if (windowIsPopupType(type))
696 return NSWindowStyleMaskBorderless;
698 if (flags & Qt::CustomizeWindowHint) {
701 return flags & Qt::WindowTitleHint
702 ? NSWindowStyleMaskTitled
703 : NSWindowStyleMaskBorderless;
706 return NSWindowStyleMaskTitled;
715 styleMask |= NSWindowStyleMaskClosable
716 | NSWindowStyleMaskMiniaturizable;
718 if (type != Qt::Popup)
719 styleMask |= NSWindowStyleMaskResizable;
721 if (type == Qt::Tool)
722 styleMask |= NSWindowStyleMaskUtilityWindow;
724 if (flags & Qt::ExpandedClientAreaHint)
725 styleMask |= NSWindowStyleMaskFullSizeContentView;
728 styleMask |= (m_view.window.styleMask & (
729 NSWindowStyleMaskFullScreen
730 | NSWindowStyleMaskUnifiedTitleAndToolbar
731 | NSWindowStyleMaskDocModalWindow
732 | NSWindowStyleMaskNonactivatingPanel
733 | NSWindowStyleMaskHUDWindow));
738bool QCocoaWindow::isFixedSize()
const
740 return windowMinimumSize().isValid() && windowMaximumSize().isValid()
741 && windowMinimumSize() == windowMaximumSize();
744void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
746 if (!isContentView())
749 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
750 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
751 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
752 { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
755 bool hideButtons =
true;
756 for (
const auto &[button, buttonHint] : buttons) {
759 if (button == NSWindowMiniaturizeButton)
760 enabled = window()->type() != Qt::Dialog;
763 if (windowFlags & Qt::CustomizeWindowHint)
764 enabled = windowFlags & buttonHint;
768 if (button == NSWindowZoomButton && isFixedSize())
774 if (button == NSWindowCloseButton && enabled
775 && QWindowPrivate::get(window())->blockedByModalWindow) {
782 [m_view.window standardWindowButton:button].enabled = enabled;
783 hideButtons &= !enabled;
787 for (
const auto &[button, buttonHint] : buttons)
788 [m_view.window standardWindowButton:button].hidden = hideButtons;
791void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
797 QMacAutoReleasePool pool;
799 if (!isContentView())
802 qCDebug(lcQpaWindow) <<
"Setting" << flags <<
"for" << window();
813 QScopedValueRollback<
bool> geometryChangeBlocker(m_inSetStyleMask,
true);
815 const auto newMask = windowStyleMask(flags);
816 const bool expandedClientAreaChanged = (
817 (newMask & NSWindowStyleMaskFullSizeContentView) !=
818 (m_view.window.styleMask & NSWindowStyleMaskFullSizeContentView));
819 const auto frameGeometry = window()->frameGeometry();
821 m_view.window.styleMask = newMask;
823 if (expandedClientAreaChanged) {
825 setGeometry(frameGeometry.marginsRemoved(frameMargins()),
826 QWindowPrivate::PositionPolicy::WindowFrameExclusive);
830 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
831 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
832 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
833 const bool enableFullScreen = m_view.window.qt_fullScreen
834 || !(flags & Qt::CustomizeWindowHint)
835 || (flags & Qt::WindowFullscreenButtonHint);
836 if (enableFullScreen) {
837 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
838 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
840 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
841 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
843 m_view.window.collectionBehavior = behavior;
848 m_view.window.level =
this->windowLevel(flags);
850 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
852 if (!(flags & Qt::FramelessWindowHint))
853 setWindowTitle(window()->title());
855 updateTitleBarButtons(flags);
864 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
865 if (m_view.window.ignoresMouseEvents != ignoreMouse)
866 m_view.window.ignoresMouseEvents = ignoreMouse;
868 m_view.window.titlebarAppearsTransparent = flags & Qt::NoTitleBarBackgroundHint;
874
875
876
877
878
879
880void QCocoaWindow::setWindowState(Qt::WindowStates state)
882 if (window()->isVisible())
883 applyWindowState(state);
886void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
888 if (!isContentView())
891 const Qt::WindowState currentState = QWindowPrivate::effectiveState(windowState());
892 const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
894 if (newState == currentState)
897 qCDebug(lcQpaWindow) <<
"Applying" << newState <<
"to" << window() <<
"in" << currentState;
899 const NSSize contentSize = m_view.frame.size;
900 if (contentSize.width <= 0 || contentSize.height <= 0) {
903 qWarning(
"invalid window content view size, check your window geometry");
904 handleWindowStateChanged(HandleUnconditionally);
908 const NSWindow *nsWindow = m_view.window;
910 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
911 && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
912 qWarning() << window()->type() <<
"windows cannot be made" << newState;
913 handleWindowStateChanged(HandleUnconditionally);
917 const id sender = nsWindow;
920 switch (currentState) {
921 case Qt::WindowMinimized:
922 [nsWindow deminiaturize:sender];
927 case Qt::WindowFullScreen: {
938 if (newState == windowState())
942 case Qt::WindowFullScreen:
945 case Qt::WindowMaximized:
948 case Qt::WindowMinimized:
949 [nsWindow miniaturize:sender];
951 case Qt::WindowNoState:
952 if (windowState() == Qt::WindowMaximized)
960Qt::WindowStates QCocoaWindow::windowState()
const
962 Qt::WindowStates states = Qt::WindowNoState;
963 NSWindow *window = m_view.window;
965 if (window.miniaturized)
966 states |= Qt::WindowMinimized;
970 if (window.qt_fullScreen) {
971 states |= Qt::WindowFullScreen;
972 }
else if ((window.zoomed && !isTransitioningToFullScreen())
973 || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) {
974 states |= Qt::WindowMaximized;
983void QCocoaWindow::toggleMaximized()
985 const NSWindow *window = m_view.window;
989 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
990 window.styleMask |= NSWindowStyleMaskResizable;
992 const id sender = window;
993 [window zoom:sender];
996 window.styleMask &= ~NSWindowStyleMaskResizable;
999void QCocoaWindow::windowWillZoom()
1001 updateNormalGeometry();
1004void QCocoaWindow::toggleFullScreen()
1006 const NSWindow *window = m_view.window;
1011 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1013 const id sender = window;
1014 [window toggleFullScreen:sender];
1017void QCocoaWindow::windowWillEnterFullScreen()
1019 if (!isContentView())
1022 updateNormalGeometry();
1027 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1030bool QCocoaWindow::isTransitioningToFullScreen()
const
1032 NSWindow *window = m_view.window;
1033 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
1036void QCocoaWindow::windowDidEnterFullScreen()
1038 if (!isContentView())
1041 Q_ASSERT_X(m_view.window.qt_fullScreen,
"QCocoaWindow",
1042 "FullScreen category processes window notifications first");
1045 setWindowFlags(window()->flags());
1047 handleWindowStateChanged();
1050void QCocoaWindow::windowWillExitFullScreen()
1052 if (!isContentView())
1057 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1060void QCocoaWindow::windowDidExitFullScreen()
1062 if (!isContentView())
1065 Q_ASSERT_X(!m_view.window.qt_fullScreen,
"QCocoaWindow",
1066 "FullScreen category processes window notifications first");
1069 setWindowFlags(window()->flags());
1071 Qt::WindowState requestedState = window()->windowState();
1074 handleWindowStateChanged();
1076 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
1079 applyWindowState(requestedState);
1083void QCocoaWindow::windowDidMiniaturize()
1085 if (!isContentView())
1088 handleWindowStateChanged();
1091void QCocoaWindow::windowDidDeminiaturize()
1093 if (!isContentView())
1096 Qt::WindowState requestedState = window()->windowState();
1098 handleWindowStateChanged();
1100 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
1103 applyWindowState(requestedState);
1107void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
1109 Qt::WindowStates currentState = windowState();
1110 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
1113 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleWindowStateChanged" <<
1114 m_lastReportedWindowState <<
"-->" << currentState;
1116 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
1117 window(), currentState, m_lastReportedWindowState);
1118 m_lastReportedWindowState = currentState;
1123void QCocoaWindow::setWindowTitle(
const QString &title)
1125 QMacAutoReleasePool pool;
1127 if (!isContentView())
1130 m_view.window.title = title.toNSString();
1132 if (title.isEmpty() && !window()->filePath().isEmpty()) {
1134 setWindowFilePath(window()->filePath());
1138void QCocoaWindow::setWindowFilePath(
const QString &filePath)
1140 QMacAutoReleasePool pool;
1142 if (!isContentView())
1145 if (window()->title().isNull())
1146 [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
1148 m_view.window.representedFilename = filePath.toNSString();
1151 setWindowIcon(window()->icon());
1154void QCocoaWindow::setWindowIcon(
const QIcon &icon)
1156 QMacAutoReleasePool pool;
1158 if (!isContentView())
1161 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
1167 if (icon.isNull()) {
1168 iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename];
1172 auto fallbackSize = QSizeF::fromCGSize(iconButton.frame.size) * qGuiApp->devicePixelRatio();
1173 iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize.toSize()];
1177void QCocoaWindow::setAlertState(
bool enabled)
1179 if (m_alertRequest == NoAlertRequest && enabled) {
1180 m_alertRequest = [NSApp requestUserAttention:NSCriticalRequest];
1181 }
else if (m_alertRequest != NoAlertRequest && !enabled) {
1182 [NSApp cancelUserAttentionRequest:m_alertRequest];
1183 m_alertRequest = NoAlertRequest;
1187bool QCocoaWindow::isAlertState()
const
1189 return m_alertRequest != NoAlertRequest;
1192void QCocoaWindow::raise()
1194 qCDebug(lcQpaWindow) <<
"QCocoaWindow::raise" << window();
1197 if (isContentView()) {
1198 if (m_view.window.visible) {
1205 QMacAutoReleasePool pool;
1206 [m_view.window orderFront:m_view.window];
1208 static bool raiseProcess = qt_mac_resolveOption(
true,
"QT_MAC_SET_RAISE_PROCESS");
1210 [NSApp activateIgnoringOtherApps:YES];
1213 [m_view.superview addSubview:m_view positioned:NSWindowAbove relativeTo:nil];
1217void QCocoaWindow::lower()
1219 qCDebug(lcQpaWindow) <<
"QCocoaWindow::lower" << window();
1221 if (isContentView()) {
1222 if (m_view.window.visible)
1223 [m_view.window orderBack:m_view.window];
1225 [m_view.superview addSubview:m_view positioned:NSWindowBelow relativeTo:nil];
1229bool QCocoaWindow::isExposed()
const
1231 return !m_exposedRect.isEmpty();
1235
1236
1237
1238
1239
1240bool QCocoaWindow::isEmbedded()
const
1246 return m_isEmbedded;
1249bool QCocoaWindow::isOpaque()
const
1253 static GLint openglSourfaceOrder = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
1255 bool translucent = window()->format().alphaBufferSize() > 0
1256 || window()->opacity() < 1
1257 || !window()->mask().isEmpty()
1258 || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
1259 return !translucent;
1262void QCocoaWindow::propagateSizeHints()
1264 QMacAutoReleasePool pool;
1265 if (!isContentView())
1268 qCDebug(lcQpaWindow) <<
"QCocoaWindow::propagateSizeHints" << window()
1269 <<
"min:" << windowMinimumSize() <<
"max:" << windowMaximumSize()
1270 <<
"increment:" << windowSizeIncrement()
1271 <<
"base:" << windowBaseSize();
1273 const NSWindow *window = m_view.window;
1276 QSize minimumSize = windowMinimumSize();
1277 if (!minimumSize.isValid())
1278 minimumSize = QSize(0, 0);
1279 window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
1282 window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
1285 updateTitleBarButtons(
this->window()->flags());
1289 QSize sizeIncrement = windowSizeIncrement();
1290 if (sizeIncrement.isEmpty())
1291 sizeIncrement = QSize(1, 1);
1292 window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
1294 QRect rect = geometry();
1295 QSize baseSize = windowBaseSize();
1296 if (!baseSize.isNull() && baseSize.isValid())
1297 [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
1300void QCocoaWindow::setOpacity(qreal level)
1302 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setOpacity" << level;
1303 if (!isContentView())
1306 m_view.window.alphaValue = level;
1309void QCocoaWindow::setMask(
const QRegion ®ion)
1311 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setMask" << window() << region;
1313 if (!region.isEmpty()) {
1314 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
1315 for (
const QRect &r : region)
1316 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
1317 CAShapeLayer *maskLayer = [CAShapeLayer layer];
1318 maskLayer.path = maskPath;
1319 m_view.layer.mask = maskLayer;
1321 m_view.layer.mask = nil;
1325bool QCocoaWindow::setKeyboardGrabEnabled(
bool)
1330bool QCocoaWindow::setMouseGrabEnabled(
bool)
1335WId QCocoaWindow::winId()
const
1340void QCocoaWindow::setParent(
const QPlatformWindow *parentWindow)
1342 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0);
1345 recreateWindowIfNeeded();
1347 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1350NSView *QCocoaWindow::view()
const
1355NSWindow *QCocoaWindow::nativeWindow()
const
1357 return m_view.window;
1360void QCocoaWindow::updateEmbeddedState()
1362 const bool wasEmbedded = m_isEmbedded;
1364 if (QPlatformWindow::parent())
1367 if (!m_view.superview)
1372 auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1373 if (m_view == qtWindowController.window.contentView)
1378 if (m_isEmbedded != wasEmbedded) {
1379 qCDebug(lcQpaWindow) <<
"Updated embedded state from"
1380 << wasEmbedded <<
"to" << m_isEmbedded;
1386void QCocoaWindow::viewDidChangeFrame()
1393 handleGeometryChange();
1397
1398
1399
1400
1401
1402
1403void QCocoaWindow::viewDidChangeGlobalFrame()
1405 [m_view setNeedsDisplay:YES];
1409
1410
1411
1412
1413
1414void QCocoaWindow::viewDidMoveToSuperview(NSView *previousSuperview)
1416 qCDebug(lcQpaWindow) <<
"Done re-parenting" << m_view
1417 <<
"from" << previousSuperview <<
"into" << m_view.superview;
1420 updateEmbeddedState();
1424 handleGeometryChange();
1426 if (m_view.superview)
1427 [m_view setNeedsDisplay:YES];
1438 if (m_view.superview && !m_view.superview.flipped && !isContentView()) {
1439 if (m_view.autoresizingMask == NSViewNotSizable) {
1440 qCDebug(lcQpaWindow) <<
"Setting auto resizing mask on" << m_view
1441 <<
"in non-flipped superview to maintain stable y-positioning";
1442 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1443 m_view.autoresizingMask = NSViewMinYMargin;
1445 }
else if (previousSuperview && !previousSuperview.flipped
1446 && m_view.autoresizingMask == NSViewMinYMargin) {
1451 qCDebug(lcQpaWindow) <<
"Clearing auto resizing mask on" << m_view
1452 <<
"as explicit stable y-positioning is no longer needed";
1453 m_view.autoresizingMask = NSViewNotSizable;
1458
1459
1460
1461
1462void QCocoaWindow::viewDidMoveToWindow(NSWindow *previousWindow)
1464 qCDebug(lcQpaWindow) <<
"Done moving" << m_view
1465 <<
"from" << previousWindow <<
"to" << m_view.window;
1467 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController)) {
1468 qCDebug(lcQpaWindow) <<
"Retaining new window controller" << qtWindowController;
1469 [qtWindowController retain];
1471 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(previousWindow.windowController)) {
1472 qCDebug(lcQpaWindow) <<
"Releasing old window controller" << qtWindowController;
1473 [qtWindowController autorelease];
1479 auto *previousScreen = previousWindow ? QCocoaScreen::get(previousWindow.screen) :
nullptr;
1480 auto *currentScreen = m_view.window ? QCocoaScreen::get(m_view.window.screen) :
nullptr;
1481 if (currentScreen && currentScreen != previousScreen)
1482 windowDidChangeScreen();
1492void QCocoaWindow::windowDidMove()
1494 if (!isContentView())
1497 handleGeometryChange();
1500 handleWindowStateChanged();
1503void QCocoaWindow::windowDidResize()
1505 if (!isContentView())
1508 handleGeometryChange();
1510 if (!m_view.inLiveResize)
1511 handleWindowStateChanged();
1514void QCocoaWindow::windowWillStartLiveResize()
1519 m_inLiveResize =
true;
1522bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1527 return !m_inLiveResize;
1530void QCocoaWindow::windowDidEndLiveResize()
1532 m_inLiveResize =
false;
1534 if (!isContentView())
1537 handleWindowStateChanged();
1540void QCocoaWindow::windowDidBecomeKey()
1544 if (m_view.window.firstResponder != m_view)
1547 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1548 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1550 if (windowIsPopupType()) {
1551 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1556 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1557 window(), Qt::ActiveWindowFocusReason);
1560void QCocoaWindow::windowDidResignKey()
1564 if (m_view.window.firstResponder != m_view)
1567 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1568 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1577 NSWindow *newKeyWindow = [NSApp keyWindow];
1578 if (newKeyWindow && newKeyWindow != m_view.window
1579 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1580 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1581 <<
"is Qt window. Deferring focus window change.";
1586 if (!windowIsPopupType()) {
1587 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1588 nullptr, Qt::ActiveWindowFocusReason);
1592void QCocoaWindow::windowDidOrderOnScreen()
1596 if (QWindowPrivate::get(window())->isPopup()) {
1597 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1598 (QGuiApplicationPrivate::currentMouseWindow);
1601 [m_view setNeedsDisplay:YES];
1604void QCocoaWindow::windowDidOrderOffScreen()
1606 handleExposeEvent(QRegion());
1609 if (window()->type() & Qt::Window) {
1610 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1611 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1612 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1613 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1615 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1616 (windowUnderMouse, windowPoint, screenPoint);
1622void QCocoaWindow::windowDidChangeOcclusionState()
1628 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1629 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1630 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1633 [m_view setNeedsDisplay:YES];
1635 handleExposeEvent(QRegion());
1638void QCocoaWindow::windowDidChangeScreen()
1644 NSScreen *nsScreen = m_view.window.screen;
1646 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1647 QCocoaScreen::updateScreens();
1649 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1650 auto *currentScreen = QCocoaScreen::get(nsScreen);
1652 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1665 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1666 window(), currentScreen ? currentScreen->screen() :
nullptr);
1668 if (currentScreen && hasPendingUpdateRequest()) {
1673 currentScreen->requestUpdate();
1678 previousScreen->maybeStopDisplayLink();
1683bool QCocoaWindow::windowShouldClose()
1685 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1697 [[m_view.window retain] autorelease];
1699 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1704void QCocoaWindow::handleGeometryChange()
1706 const QRect newGeometry = actualGeometry();
1708 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1709 <<
"current" << geometry() <<
"new" << newGeometry;
1714 if (m_inSetStyleMask && !m_view.window) {
1715 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1722 if (!m_initialized) {
1724 QPlatformWindow::setGeometry(newGeometry);
1725 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1729 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1732 updateSafeAreaMarginsIfNeeded();
1736 if (!m_inSetGeometry)
1737 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1740void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1753 if (m_view.window.visible && m_view.window.screen
1754 && !geometry().size().isEmpty() && !region.isEmpty()
1755 && !m_view.hiddenOrHasHiddenAncestor) {
1756 m_exposedRect = region.boundingRect();
1758 m_exposedRect = QRect();
1761 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1762 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1765 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1770bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1772 if (type == Qt::Widget)
1773 type = window()->type();
1774 if (type == Qt::Tool)
1777 return ((type & Qt::Popup) == Qt::Popup);
1781
1782
1783
1784
1785
1786
1787
1788
1789bool QCocoaWindow::isContentView()
const
1791 return m_view.window.contentView == m_view;
1795
1796
1797
1798
1799
1800
1801
1802void QCocoaWindow::recreateWindowIfNeeded()
1804 QMacAutoReleasePool pool;
1806 auto *parentWindow =
static_cast<QCocoaWindow *>(QPlatformWindow::parent());
1808 const bool shouldManageTopLevelWindow = !parentWindow
1809 && !((window()->type() & Qt::SubWindow) == Qt::SubWindow)
1810 && !isForeignWindow();
1812 if (shouldManageTopLevelWindow) {
1813 auto *windowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1814 const bool isControllerContentView = m_view == windowController.window.contentView;
1815 const bool windowClassIsCorrect = [m_view.window isKindOfClass:windowClass()];
1817 if (!isControllerContentView || !windowClassIsCorrect) {
1818 qCDebug(lcQpaWindow) <<
"Creating new window controller for" << m_view;
1819 windowController = [[QNSWindowController alloc] initWithCocoaWindow:
this];
1821 m_view.postsFrameChangedNotifications = NO;
1822 windowController.window.contentView = m_view;
1823 m_view.postsFrameChangedNotifications = YES;
1826 [windowController release];
1828 qCDebug(lcQpaWindow) <<
"Re-using existing window controller" << windowController;
1830 }
else if (parentWindow) {
1831 if (m_view.superview != parentWindow->view()) {
1832 qCDebug(lcQpaWindow) <<
"Reparenting view to new parent" << parentWindow;
1833 [parentWindow->view() addSubview:m_view];
1835 }
else if (qnsview_cast(m_view.superview) && !isEmbedded()) {
1836 qCDebug(lcQpaWindow) <<
"Removing view from superview" << m_view.superview;
1837 [m_view removeFromSuperview];
1841Class QCocoaWindow::windowClass()
const
1843 const auto type = window()->type();
1844 return ((type & Qt::Popup) == Qt::Popup
1845 || (type & Qt::Dialog) == Qt::Dialog) ?
1846 QNSPanel.
class : QNSWindow.
class;
1849void QCocoaWindow::requestUpdate()
1851 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1852 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1854 if (updatesWithDisplayLink()) {
1855 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1856 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1857 QPlatformWindow::requestUpdate();
1861 QPlatformWindow::requestUpdate();
1865bool QCocoaWindow::updatesWithDisplayLink()
const
1868 return format().swapInterval() != 0;
1871void QCocoaWindow::deliverUpdateRequest()
1873 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1874 QScopedValueRollback<
bool> blocker(m_deliveringUpdateRequest,
true);
1876 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1881 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1882 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1883 <<
"due to" << qtMetalLayer <<
"needing display";
1889 qtMetalLayer.displayLock.unlock();
1892 QPlatformWindow::deliverUpdateRequest();
1895void QCocoaWindow::requestActivateWindow()
1897 QMacAutoReleasePool pool;
1898 [m_view.window makeFirstResponder:m_view];
1899 [m_view.window makeKeyWindow];
1903
1904
1905void QCocoaWindow::closeAllPopups()
1907 QGuiApplicationPrivate::instance()->closeAllPopups();
1909 removePopupMonitor();
1912void QCocoaWindow::removePopupMonitor()
1914 if (s_globalMouseMonitor) {
1915 [NSEvent removeMonitor:s_globalMouseMonitor];
1916 s_globalMouseMonitor = nil;
1918 if (s_applicationActivationObserver) {
1919 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1920 s_applicationActivationObserver = nil;
1924void QCocoaWindow::setupPopupMonitor()
1931 if (!s_globalMouseMonitor) {
1933 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1934 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1935 | NSEventMaskMouseMoved;
1936 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1937 handler:^(NSEvent *e){
1938 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1939 removePopupMonitor();
1942 const auto eventType = cocoaEvent2QtMouseEvent(e);
1943 if (eventType == QEvent::MouseMove) {
1944 if (s_windowUnderMouse) {
1945 QWindow *window = s_windowUnderMouse->window();
1946 const auto button = cocoaButton2QtButton(e);
1947 const auto buttons = currentlyPressedMouseButtons();
1948 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1949 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1950 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1951 buttons, button, eventType);
1961 if (!s_applicationActivationObserver) {
1962 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1963 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1964 object:nil queue:nil
1965 usingBlock:^(NSNotification *){
1971QCocoaNSWindow *QCocoaWindow::createNSWindow()
1973 QMacAutoReleasePool pool;
1975 Qt::WindowType type = window()->type();
1976 Qt::WindowFlags flags = window()->flags();
1978 QRect rect = geometry();
1980 QScreen *targetScreen =
nullptr;
1981 for (QScreen *screen : QGuiApplication::screens()) {
1982 if (screen->geometry().contains(rect.topLeft())) {
1983 targetScreen = screen;
1988 NSWindowStyleMask styleMask = windowStyleMask(flags);
1990 if (!targetScreen) {
1991 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
1992 targetScreen = QGuiApplication::primaryScreen();
1996 styleMask = NSWindowStyleMaskBorderless;
1999 rect.translate(-targetScreen->geometry().topLeft());
2000 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
2001 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
2003 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
2008 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
2009 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
2010 <<
"Adjusting style mask during creation";
2011 styleMask = NSWindowStyleMaskBorderless;
2016 QCocoaNSWindow *nsWindow = [[windowClass() alloc] initWithContentRect:contentRect
2021 backing:NSBackingStoreBuffered defer:NO
2022 screen:targetCocoaScreen->nativeScreen()
2023 platformWindow:
this];
2027 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
2032 if (!resultingScreen)
2033 resultingScreen = targetCocoaScreen;
2035 if (resultingScreen->screen() != window()->screen()) {
2036 QWindowSystemInterface::handleWindowScreenChanged<
2037 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
2040 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
2041 [](QNSWindowDelegate *delegate) { [delegate release]; });
2042 nsWindow.delegate = sharedDelegate.get();
2048 [nsWindow setReleasedWhenClosed:NO];
2050 if (alwaysShowToolWindow()) {
2051 static dispatch_once_t onceToken;
2052 dispatch_once(&onceToken, ^{
2053 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
2054 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2055 name:NSApplicationWillResignActiveNotification object:nil];
2056 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2057 name:NSApplicationWillBecomeActiveNotification object:nil];
2061 nsWindow.restorable = NO;
2062 nsWindow.level = windowLevel(flags);
2063 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
2065 if ([nsWindow isKindOfClass:NSPanel.
class]) {
2067 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
2070 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
2071 | NSWindowCollectionBehaviorMoveToActiveSpace;
2073 if ((type & Qt::Popup) == Qt::Popup) {
2074 nsWindow.hasShadow = YES;
2075 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
2076 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
2077 setupPopupMonitor();
2088 if (
auto *qtView = qnsview_cast(m_view))
2089 nsWindow.colorSpace = qtView.colorSpace;
2094bool QCocoaWindow::alwaysShowToolWindow()
const
2096 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2099bool QCocoaWindow::setWindowModified(
bool modified)
2101 QMacAutoReleasePool pool;
2103 if (!isContentView())
2106 m_view.window.documentEdited = modified;
2110void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2115QCocoaMenuBar *QCocoaWindow::menubar()
const
2120void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2122 QMacAutoReleasePool pool;
2125 if (isForeignWindow())
2128 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2130 QNSView *view = qnsview_cast(m_view);
2131 if (cursor == view.cursor)
2134 view.cursor = cursor;
2140 [m_view.window invalidateCursorRectsForView:m_view];
2147 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2148 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2149 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2150 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2151 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2152 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2153 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2154 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2155 location:locationInWindow modifierFlags:0 timestamp:0
2156 windowNumber:m_view.window.windowNumber context:nil
2157 eventNumber:0 trackingNumber:0 userData:0]];
2161void QCocoaWindow::registerTouch(
bool enable)
2163 m_registerTouchCount += enable ? 1 : -1;
2164 if (enable && m_registerTouchCount == 1)
2165 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2166 else if (m_registerTouchCount == 0)
2167 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2170qreal QCocoaWindow::devicePixelRatio()
const
2177 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2178 return backingSize.height;
2181QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2183 QWindow *targetWindow = window();
2184 for (QObject *child : targetWindow->children())
2185 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2186 if (QPlatformWindow *handle = childWindow->handle())
2187 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2188 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2190 return targetWindow;
2193bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2198 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2208 QWindow *modalWindow =
nullptr;
2209 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2210 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2211 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2212 <<
"blocked by" << modalWindow;
2217 if (m_inSetVisible) {
2218 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2219 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2226bool QCocoaWindow::windowEvent(QEvent *event)
2228 switch (event->type()) {
2229 case QEvent::WindowBlocked:
2230 case QEvent::WindowUnblocked:
2231 updateTitleBarButtons(window()->flags());
2237 return QPlatformWindow::windowEvent(event);
2240QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2244 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2245 : NSMakePoint(0, 0);
2246 const NSRect visibleRect = [m_view visibleRect];
2248 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2251QMargins QCocoaWindow::frameMargins()
const
2253 QMacAutoReleasePool pool;
2258 if (QPlatformWindow::parent())
2264 if (m_view.window) {
2265 frameRect = m_view.window.frame;
2266 contentRect = [m_view.window contentRectForFrameRect:frameRect];
2268 contentRect = m_view.frame;
2269 frameRect = [NSWindow frameRectForContentRect:contentRect styleMask:windowStyleMask(window()->flags())];
2272 return QMargins(frameRect.origin.x - contentRect.origin.x,
2273 (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height),
2274 (frameRect.origin.x + frameRect.size.width) - (contentRect.origin.x + contentRect.size.width),
2275 contentRect.origin.y - contentRect.origin.y);
2278void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2280 m_frameStrutEventsEnabled = enabled;
2283QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2285 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2286 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2287 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2290QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2292 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2293 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2294 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2297CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2299 if (!referenceView || referenceView.flipped)
2300 return point.toCGPoint();
2302 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2305CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2307 if (!referenceView || referenceView.flipped)
2308 return rect.toCGRect();
2310 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2313QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2315 if (!referenceView || referenceView.flipped)
2316 return QPointF::fromCGPoint(point);
2318 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2321QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2323 if (!referenceView || referenceView.flipped)
2324 return QRectF::fromCGRect(rect);
2326 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2329CALayer *QCocoaWindow::contentLayer()
const
2331 auto *layer = m_view.layer;
2332 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2333 layer = containerLayer.contentLayer;
2337void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2338 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2339 NSVisualEffectState activationState)
2341 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2342 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2343 <<
"in views without a container layer";
2347 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2348 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2349 << blendMode <<
"and activation state" << activationState;
2351 NSVisualEffectView *effectView =
nullptr;
2352 if (m_effectViews.contains(identifier)) {
2353 effectView = m_effectViews.value(identifier);
2354 if (rect.isEmpty()) {
2355 [effectView removeFromSuperview];
2356 m_effectViews.remove(identifier);
2359 }
else if (!rect.isEmpty()) {
2360 effectView = [NSVisualEffectView
new];
2363 effectView.wantsLayer = YES;
2364 effectView.layer.zPosition = -FLT_MAX;
2365 [m_view addSubview:effectView];
2366 m_effectViews.insert(identifier, effectView);
2372 effectView.frame = rect.toCGRect();
2373 effectView.material = material;
2374 effectView.blendingMode = blendMode;
2375 effectView.state = activationState;
2378#ifndef QT_NO_DEBUG_STREAM
2379QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2381 QDebugStateSaver saver(debug);
2383 debug <<
"QCocoaWindow(" << (
const void *)window;
2385 debug <<
", window=" << window->window();
2393#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")