5#include <AppKit/AppKit.h>
6#include <QuartzCore/QuartzCore.h>
20#include <QtCore/qfileinfo.h>
21#include <QtCore/private/qcore_mac_p.h>
23#include <private/qwindow_p.h>
24#include <qpa/qwindowsysteminterface.h>
25#include <qpa/qplatformscreen.h>
26#include <QtGui/private/qcoregraphics_p.h>
27#include <QtGui/private/qhighdpiscaling_p.h>
28#include <QtGui/private/qmetallayer_p.h>
35- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow;
38@implementation QNSWindowController {
39 QPointer<QCocoaWindow> m_platformWindow;
42- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow
46 if ((self = [super initWithWindowNibName:@
"QNSWindowController"]))
47 m_platformWindow = platformWindow;
54 QMacAutoReleasePool pool;
55 self.window = [m_platformWindow->createNSWindow() autorelease];
59 qCDebug(lcQpaWindow) <<
"Disposing of" << self.window <<
"for" << m_platformWindow;
69 defaultWindowWidth = 160,
70 defaultWindowHeight = 160
79 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
81 const QMetaObject *metaObject = QMetaType(qRegisterMetaType<
QCocoaWindow*>()).metaObject();
84 for (
int i = 0; i < metaObject->methodCount(); ++i) {
85 QMetaMethod method = metaObject->method(i);
86 const QString methodTag = QString::fromLatin1(method.tag());
87 if (!methodTag.startsWith(notificationHandlerPrefix))
90 const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
91 [center addObserverForName:notificationName.toNSString() object:nil queue:nil
92 usingBlock:^(NSNotification *notification) {
94 QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
95 if ([notification.object isKindOfClass:[NSWindow
class]]) {
96 NSWindow *nsWindow = notification.object;
97 for (
const QWindow *window : QGuiApplication::allWindows()) {
98 if (QCocoaWindow *cocoaWindow =
static_cast<QCocoaWindow *>(window->handle()))
99 if (cocoaWindow->nativeWindow() == nsWindow)
100 cocoaWindows += cocoaWindow;
102 }
else if ([notification.object isKindOfClass:[NSView
class]]) {
103 if (QNSView *qnsView = qnsview_cast(notification.object))
104 cocoaWindows += qnsView.platformWindow;
106 qCWarning(lcCocoaNotifications) <<
"Unhandled notification"
107 << notification.name <<
"for" << notification.object;
111 if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) {
112 QVector<QCocoaWindow *> debugWindows;
113 for (QCocoaWindow *cocoaWindow : cocoaWindows)
114 debugWindows += cocoaWindow;
115 qCDebug(lcCocoaNotifications) <<
"Forwarding" << qPrintable(notificationName) <<
116 "to" << debugWindows;
121 for (QCocoaWindow *cocoaWindow : cocoaWindows) {
122 if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
123 qCWarning(lcQpaWindow) <<
"Failed to invoke NSNotification callback for"
124 << notification.name <<
"on" << cocoaWindow;
130Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
135QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) : QPlatformWindow(win)
137 qCDebug(lcQpaWindow) <<
"QCocoaWindow::QCocoaWindow" << window();
140 m_view =
reinterpret_cast<NSView *>(nativeHandle);
147 qCDebug(lcQpaWindow) <<
"QCocoaWindow::initialize" << window();
149 QMacAutoReleasePool pool;
152 m_view = [[QNSView alloc] initWithCocoaWindow:
this];
158 auto initialGeometry = QPlatformWindow::initialGeometry(window(),
159 windowGeometry(), defaultWindowWidth, defaultWindowHeight);
165 if (QPlatformWindow::parent() || isSubWindow(window())) {
171 setGeometry(initialGeometry);
173 if (window()->flags() & Qt::ExpandedClientAreaHint) {
176 QRect frameGeometryWithFrame = QCocoaScreen::mapFromNative(
177 [NSWindow frameRectForContentRect:QCocoaScreen::mapToNative(initialGeometry)
178 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
179 if (qt_window_private(window())->positionPolicy == QWindowPrivate::WindowFrameExclusive)
180 initialGeometry = frameGeometryWithFrame;
182 initialGeometry.setSize(frameGeometryWithFrame.size());
190 QPlatformWindow::setGeometry(initialGeometry);
198 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
200 m_safeAreaInsetsObserver = QMacKeyValueObserver(
201 m_view, @
"safeAreaInsets", [
this] {
204 QMetaObject::invokeMethod(
this, [
this]{
205 updateSafeAreaMarginsIfNeeded();
206 }, Qt::QueuedConnection);
207 }, NSKeyValueObservingOptionNew);
213 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
223 qCDebug(lcQpaWindow) <<
"QCocoaWindow::~QCocoaWindow" << window();
225 QMacAutoReleasePool pool;
227 if (window()->flags() & Qt::ExpandedClientAreaHint) {
231 auto readjustedGeometry = geometry();
232 QRect contentRectWithFrame = QCocoaScreen::mapFromNative(
233 [NSWindow contentRectForFrameRect:QCocoaScreen::mapToNative(geometry())
234 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
235 readjustedGeometry = contentRectWithFrame;
236 setGeometry(readjustedGeometry, QWindowPrivate::PositionPolicy::WindowFrameExclusive);
241 if (!isForeignWindow() || QPlatformWindow::parent())
242 [m_view removeFromSuperview];
244 m_safeAreaInsetsObserver = {};
249 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
252 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
253 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
255 vulcanInstance->destroySurface(m_vulkanSurface);
262 [NSNotificationCenter.defaultCenter
263 postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
271 static_cast<
QCocoaScreen *>(screen())->maybeStopDisplayLink();
276 auto format = window()->requestedFormat();
277 if (
auto *view = qnsview_cast(m_view); view.colorSpace) {
278 auto colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(view.colorSpace.ICCProfileData));
279 if (!colorSpace.isValid()) {
280 qCWarning(lcQpaWindow) <<
"Failed to parse ICC profile for" << view.colorSpace
281 <<
"with ICC data" << view.colorSpace.ICCProfileData;
283 format.setColorSpace(colorSpace);
290 return ![m_view isKindOfClass:[QNSView
class]];
295 return QPlatformWindow::geometry();
302 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
305 return QCocoaScreen::mapFromNative(contentRect).toRect();
307 return QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
312
313
314
315
316
317
318
328 if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
331 return m_normalGeometry;
339 if (windowState() != Qt::WindowNoState)
342 m_normalGeometry = geometry();
348 setGeometry(rect, qt_window_private(window())->positionPolicy);
353 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setGeometry" << window() << rectIn << positionPolicy;
354 QMacAutoReleasePool pool;
357 if (positionPolicy == QWindowPrivate::WindowFrameInclusive) {
361 const QMargins margins = frameMargins();
362 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
363 qCDebug(lcQpaWindow) <<
"Adjusted content geometry to" << rect <<
"by removing frame margins" << margins;
366 QPlatformWindow::setGeometry(rect);
368 const QRect originalGeometry = actualGeometry();
376 NSRect bounds = QCocoaScreen::mapToNative(rect);
377 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
380 m_view.frame = QCocoaWindow::mapToNative(rect, m_view.superview);
383 if (actualGeometry() == originalGeometry) {
390 qCDebug(lcQpaWindow) <<
"Native geometry didn't change. Reporting geometry change manually.";
397 QMacAutoReleasePool pool;
404 QMarginsF viewSafeAreaMargins = {
405 m_view.safeAreaInsets.left,
406 m_view.safeAreaInsets.top,
407 m_view.safeAreaInsets.right,
408 m_view.safeAreaInsets.bottom
415 auto screenRect = m_view.window.screen.frame;
416 auto screenInsets = m_view.window.screen.safeAreaInsets;
417 auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect(
418 NSMinX(screenRect) + screenInsets.left,
419 NSMinY(screenRect) + screenInsets.bottom,
420 NSWidth(screenRect) - screenInsets.left - screenInsets.right,
421 NSHeight(screenRect) - screenInsets.top - screenInsets.bottom
424 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
425 [m_view.window convertRectToScreen:
426 [m_view convertRect:m_view.bounds toView:nil]]
432 QMarginsF screenSafeAreaMargins = {
433 qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left),
434 qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top),
435 qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right),
436 qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom)
439 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
444 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
445 m_lastReportedSafeAreaMargins = safeAreaMargins();
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 && !isSubWindow(window()) && !isForeignWindow();
1814 if (shouldManageTopLevelWindow) {
1815 auto *windowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1816 const bool isControllerContentView = m_view == windowController.window.contentView;
1817 const bool windowClassIsCorrect = [m_view.window isKindOfClass:windowClass()];
1819 if (!isControllerContentView || !windowClassIsCorrect) {
1820 qCDebug(lcQpaWindow) <<
"Creating new window controller for" << m_view;
1821 windowController = [[QNSWindowController alloc] initWithCocoaWindow:
this];
1823 m_view.postsFrameChangedNotifications = NO;
1824 windowController.window.contentView = m_view;
1825 m_view.postsFrameChangedNotifications = YES;
1828 [windowController release];
1830 qCDebug(lcQpaWindow) <<
"Re-using existing window controller" << windowController;
1832 }
else if (parentWindow) {
1833 if (m_view.superview != parentWindow->view()) {
1834 qCDebug(lcQpaWindow) <<
"Reparenting view to new parent" << parentWindow;
1835 [parentWindow->view() addSubview:m_view];
1837 }
else if (qnsview_cast(m_view.superview) && !isEmbedded()) {
1838 qCDebug(lcQpaWindow) <<
"Removing view from superview" << m_view.superview;
1839 [m_view removeFromSuperview];
1843Class QCocoaWindow::windowClass()
const
1845 const auto type = window()->type();
1846 return ((type & Qt::Popup) == Qt::Popup
1847 || (type & Qt::Dialog) == Qt::Dialog) ?
1848 QNSPanel.
class : QNSWindow.
class;
1851void QCocoaWindow::requestUpdate()
1853 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1854 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1856 if (updatesWithDisplayLink()) {
1857 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1858 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1859 QPlatformWindow::requestUpdate();
1863 QPlatformWindow::requestUpdate();
1867bool QCocoaWindow::updatesWithDisplayLink()
const
1870 return format().swapInterval() != 0;
1873void QCocoaWindow::deliverUpdateRequest()
1875 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1876 QScopedValueRollback<
bool> blocker(m_deliveringUpdateRequest,
true);
1878 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1883 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1884 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1885 <<
"due to" << qtMetalLayer <<
"needing display";
1891 qtMetalLayer.displayLock.unlock();
1894 QPlatformWindow::deliverUpdateRequest();
1897void QCocoaWindow::requestActivateWindow()
1899 QMacAutoReleasePool pool;
1900 [m_view.window makeFirstResponder:m_view];
1901 [m_view.window makeKeyWindow];
1905
1906
1907void QCocoaWindow::closeAllPopups()
1909 QGuiApplicationPrivate::instance()->closeAllPopups();
1911 removePopupMonitor();
1914void QCocoaWindow::removePopupMonitor()
1916 if (s_globalMouseMonitor) {
1917 [NSEvent removeMonitor:s_globalMouseMonitor];
1918 s_globalMouseMonitor = nil;
1920 if (s_applicationActivationObserver) {
1921 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1922 s_applicationActivationObserver = nil;
1926void QCocoaWindow::setupPopupMonitor()
1933 if (!s_globalMouseMonitor) {
1935 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1936 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1937 | NSEventMaskMouseMoved;
1938 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1939 handler:^(NSEvent *e){
1940 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1941 removePopupMonitor();
1944 const auto eventType = cocoaEvent2QtMouseEvent(e);
1945 if (eventType == QEvent::MouseMove) {
1946 if (s_windowUnderMouse) {
1947 QWindow *window = s_windowUnderMouse->window();
1948 const auto button = cocoaButton2QtButton(e);
1949 const auto buttons = currentlyPressedMouseButtons();
1950 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1951 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1952 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1953 buttons, button, eventType);
1963 if (!s_applicationActivationObserver) {
1964 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1965 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1966 object:nil queue:nil
1967 usingBlock:^(NSNotification *){
1973QCocoaNSWindow *QCocoaWindow::createNSWindow()
1975 QMacAutoReleasePool pool;
1977 Qt::WindowType type = window()->type();
1978 Qt::WindowFlags flags = window()->flags();
1980 QRect rect = geometry();
1982 QScreen *targetScreen =
nullptr;
1983 for (QScreen *screen : QGuiApplication::screens()) {
1984 if (screen->geometry().contains(rect.topLeft())) {
1985 targetScreen = screen;
1990 NSWindowStyleMask styleMask = windowStyleMask(flags);
1992 if (!targetScreen) {
1993 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
1994 targetScreen = QGuiApplication::primaryScreen();
1998 styleMask = NSWindowStyleMaskBorderless;
2001 rect.translate(-targetScreen->geometry().topLeft());
2002 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
2003 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
2005 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
2010 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
2011 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
2012 <<
"Adjusting style mask during creation";
2013 styleMask = NSWindowStyleMaskBorderless;
2018 QCocoaNSWindow *nsWindow = [[windowClass() alloc] initWithContentRect:contentRect
2023 backing:NSBackingStoreBuffered defer:NO
2024 screen:targetCocoaScreen->nativeScreen()
2025 platformWindow:
this];
2029 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
2034 if (!resultingScreen)
2035 resultingScreen = targetCocoaScreen;
2037 if (resultingScreen->screen() != window()->screen()) {
2038 QWindowSystemInterface::handleWindowScreenChanged<
2039 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
2042 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
2043 [](QNSWindowDelegate *delegate) { [delegate release]; });
2044 nsWindow.delegate = sharedDelegate.get();
2050 [nsWindow setReleasedWhenClosed:NO];
2052 if (alwaysShowToolWindow()) {
2053 static dispatch_once_t onceToken;
2054 dispatch_once(&onceToken, ^{
2055 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
2056 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2057 name:NSApplicationWillResignActiveNotification object:nil];
2058 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2059 name:NSApplicationWillBecomeActiveNotification object:nil];
2063 nsWindow.restorable = NO;
2064 nsWindow.level = windowLevel(flags);
2065 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
2067 if ([nsWindow isKindOfClass:NSPanel.
class]) {
2069 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
2072 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
2073 | NSWindowCollectionBehaviorMoveToActiveSpace;
2075 if ((type & Qt::Popup) == Qt::Popup) {
2076 nsWindow.hasShadow = YES;
2077 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
2078 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
2079 setupPopupMonitor();
2090 if (
auto *qtView = qnsview_cast(m_view))
2091 nsWindow.colorSpace = qtView.colorSpace;
2096bool QCocoaWindow::alwaysShowToolWindow()
const
2098 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2101bool QCocoaWindow::setWindowModified(
bool modified)
2103 QMacAutoReleasePool pool;
2105 if (!isContentView())
2108 m_view.window.documentEdited = modified;
2112void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2117QCocoaMenuBar *QCocoaWindow::menubar()
const
2122void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2124 QMacAutoReleasePool pool;
2127 if (isForeignWindow())
2130 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2132 QNSView *view = qnsview_cast(m_view);
2133 if (cursor == view.cursor)
2136 view.cursor = cursor;
2142 [m_view.window invalidateCursorRectsForView:m_view];
2149 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2150 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2151 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2152 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2153 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2154 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2155 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2156 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2157 location:locationInWindow modifierFlags:0 timestamp:0
2158 windowNumber:m_view.window.windowNumber context:nil
2159 eventNumber:0 trackingNumber:0 userData:0]];
2163void QCocoaWindow::registerTouch(
bool enable)
2165 m_registerTouchCount += enable ? 1 : -1;
2166 if (enable && m_registerTouchCount == 1)
2167 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2168 else if (m_registerTouchCount == 0)
2169 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2172qreal QCocoaWindow::devicePixelRatio()
const
2179 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2180 return backingSize.height;
2183QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2185 QWindow *targetWindow = window();
2186 for (QObject *child : targetWindow->children())
2187 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2188 if (QPlatformWindow *handle = childWindow->handle())
2189 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2190 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2192 return targetWindow;
2195bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2200 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2210 QWindow *modalWindow =
nullptr;
2211 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2212 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2213 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2214 <<
"blocked by" << modalWindow;
2219 if (m_inSetVisible) {
2220 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2221 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2228bool QCocoaWindow::windowEvent(QEvent *event)
2230 switch (event->type()) {
2231 case QEvent::WindowBlocked:
2232 case QEvent::WindowUnblocked:
2233 updateTitleBarButtons(window()->flags());
2239 return QPlatformWindow::windowEvent(event);
2242QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2246 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2247 : NSMakePoint(0, 0);
2248 const NSRect visibleRect = [m_view visibleRect];
2250 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2253QMargins QCocoaWindow::frameMargins()
const
2255 QMacAutoReleasePool pool;
2260 if (QPlatformWindow::parent())
2266 if (m_view.window) {
2267 frameRect = m_view.window.frame;
2268 contentRect = [m_view.window contentRectForFrameRect:frameRect];
2270 contentRect = m_view.frame;
2271 frameRect = [NSWindow frameRectForContentRect:contentRect styleMask:windowStyleMask(window()->flags())];
2274 return QMargins(frameRect.origin.x - contentRect.origin.x,
2275 (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height),
2276 (frameRect.origin.x + frameRect.size.width) - (contentRect.origin.x + contentRect.size.width),
2277 contentRect.origin.y - contentRect.origin.y);
2280void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2282 m_frameStrutEventsEnabled = enabled;
2285QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2287 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2288 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2289 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2292QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2294 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2295 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2296 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2299CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2301 if (!referenceView || referenceView.flipped)
2302 return point.toCGPoint();
2304 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2307CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2309 if (!referenceView || referenceView.flipped)
2310 return rect.toCGRect();
2312 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2315QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2317 if (!referenceView || referenceView.flipped)
2318 return QPointF::fromCGPoint(point);
2320 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2323QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2325 if (!referenceView || referenceView.flipped)
2326 return QRectF::fromCGRect(rect);
2328 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2331CALayer *QCocoaWindow::contentLayer()
const
2333 auto *layer = m_view.layer;
2334 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2335 layer = containerLayer.contentLayer;
2339void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2340 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2341 NSVisualEffectState activationState)
2343 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2344 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2345 <<
"in views without a container layer";
2349 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2350 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2351 << blendMode <<
"and activation state" << activationState;
2353 NSVisualEffectView *effectView =
nullptr;
2354 if (m_effectViews.contains(identifier)) {
2355 effectView = m_effectViews.value(identifier);
2356 if (rect.isEmpty()) {
2357 [effectView removeFromSuperview];
2358 m_effectViews.remove(identifier);
2361 }
else if (!rect.isEmpty()) {
2362 effectView = [NSVisualEffectView
new];
2365 effectView.wantsLayer = YES;
2366 effectView.layer.zPosition = -FLT_MAX;
2367 [m_view addSubview:effectView];
2368 m_effectViews.insert(identifier, effectView);
2374 effectView.frame = rect.toCGRect();
2375 effectView.material = material;
2376 effectView.blendingMode = blendMode;
2377 effectView.state = activationState;
2380#ifndef QT_NO_DEBUG_STREAM
2381QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2383 QDebugStateSaver saver(debug);
2385 debug <<
"QCocoaWindow(" << (
const void *)window;
2387 debug <<
", window=" << window->window();
2395#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")