5#include <AppKit/AppKit.h>
6#include <QuartzCore/QuartzCore.h>
21#include <QtCore/qfileinfo.h>
22#include <QtCore/private/qcore_mac_p.h>
24#include <private/qwindow_p.h>
25#include <qpa/qwindowsysteminterface.h>
26#include <qpa/qplatformscreen.h>
27#include <QtGui/qaccessible.h>
28#include <QtGui/private/qcoregraphics_p.h>
29#include <QtGui/private/qhighdpiscaling_p.h>
30#include <QtGui/private/qmetallayer_p.h>
37- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow;
40@implementation QNSWindowController {
41 QPointer<QCocoaWindow> m_platformWindow;
44- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow
48 if ((self = [super initWithWindowNibName:@
"QNSWindowController"]))
49 m_platformWindow = platformWindow;
56 QMacAutoReleasePool pool;
57 self.window = [m_platformWindow->createNSWindow() autorelease];
61 qCDebug(lcQpaWindow) <<
"Disposing of" << self.window <<
"for" << m_platformWindow;
71 defaultWindowWidth = 160,
72 defaultWindowHeight = 160
81 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
83 const QMetaObject *metaObject = QMetaType(qRegisterMetaType<
QCocoaWindow*>()).metaObject();
86 for (
int i = 0; i < metaObject->methodCount(); ++i) {
87 QMetaMethod method = metaObject->method(i);
88 const QString methodTag = QString::fromLatin1(method.tag());
89 if (!methodTag.startsWith(notificationHandlerPrefix))
92 const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
93 [center addObserverForName:notificationName.toNSString() object:nil queue:nil
94 usingBlock:^(NSNotification *notification) {
96 QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
97 if ([notification.object isKindOfClass:[NSWindow
class]]) {
98 NSWindow *nsWindow = notification.object;
99 for (
const QWindow *window : QGuiApplication::allWindows()) {
100 if (QCocoaWindow *cocoaWindow =
static_cast<QCocoaWindow *>(window->handle()))
101 if (cocoaWindow->nativeWindow() == nsWindow)
102 cocoaWindows += cocoaWindow;
104 }
else if ([notification.object isKindOfClass:[NSView
class]]) {
105 if (QNSView *qnsView = qnsview_cast(notification.object))
106 cocoaWindows += qnsView.platformWindow;
108 qCWarning(lcCocoaNotifications) <<
"Unhandled notification"
109 << notification.name <<
"for" << notification.object;
113 if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) {
114 QVector<QCocoaWindow *> debugWindows;
115 for (QCocoaWindow *cocoaWindow : cocoaWindows)
116 debugWindows += cocoaWindow;
117 qCDebug(lcCocoaNotifications) <<
"Forwarding" << qPrintable(notificationName) <<
118 "to" << debugWindows;
123 for (QCocoaWindow *cocoaWindow : cocoaWindows) {
124 if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
125 qCWarning(lcQpaWindow) <<
"Failed to invoke NSNotification callback for"
126 << notification.name <<
"on" << cocoaWindow;
132Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
137QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) : QPlatformWindow(win)
139 qCDebug(lcQpaWindow) <<
"QCocoaWindow::QCocoaWindow" << window();
142 m_view =
reinterpret_cast<NSView *>(nativeHandle);
149 qCDebug(lcQpaWindow) <<
"QCocoaWindow::initialize" << window();
151 QMacAutoReleasePool pool;
154 m_view = [[QNSView alloc] initWithCocoaWindow:
this];
160 auto initialGeometry = QPlatformWindow::initialGeometry(window(),
161 windowGeometry(), defaultWindowWidth, defaultWindowHeight);
167 if (QPlatformWindow::parent() || isSubWindow(window())) {
173 setGeometry(initialGeometry);
175 if (window()->flags() & Qt::ExpandedClientAreaHint) {
178 QRect frameGeometryWithFrame = QCocoaScreen::mapFromNative(
179 [NSWindow frameRectForContentRect:QCocoaScreen::mapToNative(initialGeometry)
180 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
181 if (qt_window_private(window())->positionPolicy == QWindowPrivate::WindowFrameExclusive)
182 initialGeometry = frameGeometryWithFrame;
184 initialGeometry.setSize(frameGeometryWithFrame.size());
192 QPlatformWindow::setGeometry(initialGeometry);
200 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
202 m_safeAreaInsetsObserver = QMacKeyValueObserver(
203 m_view, @
"safeAreaInsets", [
this] {
206 QMetaObject::invokeMethod(
this, [
this]{
207 updateSafeAreaMarginsIfNeeded();
208 }, Qt::QueuedConnection);
209 }, NSKeyValueObservingOptionNew);
215 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
225 qCDebug(lcQpaWindow) <<
"QCocoaWindow::~QCocoaWindow" << window();
227 QMacAutoReleasePool pool;
229 if (window()->flags() & Qt::ExpandedClientAreaHint) {
233 auto readjustedGeometry = geometry();
234 QRect contentRectWithFrame = QCocoaScreen::mapFromNative(
235 [NSWindow contentRectForFrameRect:QCocoaScreen::mapToNative(geometry())
236 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
237 readjustedGeometry = contentRectWithFrame;
238 setGeometry(readjustedGeometry, QWindowPrivate::PositionPolicy::WindowFrameExclusive);
243 if (!isForeignWindow() || QPlatformWindow::parent())
244 [m_view removeFromSuperview];
246#if QT_CONFIG(accessibility)
247 if (isForeignWindow())
248 clearAccessibleParent();
251 m_safeAreaInsetsObserver = {};
256 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
259 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
260 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
262 vulcanInstance->destroySurface(m_vulkanSurface);
269 [NSNotificationCenter.defaultCenter
270 postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
278 static_cast<
QCocoaScreen *>(screen())->maybeStopDisplayLink();
283 auto format = window()->requestedFormat();
284 if (
auto *view = qnsview_cast(m_view); view.colorSpace) {
285 auto colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(view.colorSpace.ICCProfileData));
286 if (!colorSpace.isValid()) {
287 qCWarning(lcQpaWindow) <<
"Failed to parse ICC profile for" << view.colorSpace
288 <<
"with ICC data" << view.colorSpace.ICCProfileData;
290 format.setColorSpace(colorSpace);
297 return ![m_view isKindOfClass:[QNSView
class]];
302 return QPlatformWindow::geometry();
309 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
312 return QCocoaScreen::mapFromNative(contentRect).toRect();
314 return QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
319
320
321
322
323
324
325
335 if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
338 return m_normalGeometry;
346 if (windowState() != Qt::WindowNoState)
349 m_normalGeometry = geometry();
355 setGeometry(rect, qt_window_private(window())->positionPolicy);
360 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setGeometry" << window() << rectIn << positionPolicy;
361 QMacAutoReleasePool pool;
364 if (positionPolicy == QWindowPrivate::WindowFrameInclusive) {
368 const QMargins margins = frameMargins();
369 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
370 qCDebug(lcQpaWindow) <<
"Adjusted content geometry to" << rect <<
"by removing frame margins" << margins;
373 QPlatformWindow::setGeometry(rect);
375 const QRect originalGeometry = actualGeometry();
383 NSRect bounds = QCocoaScreen::mapToNative(rect);
384 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
387 m_view.frame = QCocoaWindow::mapToNative(rect, m_view.superview);
390 if (actualGeometry() == originalGeometry) {
397 qCDebug(lcQpaWindow) <<
"Native geometry didn't change. Reporting geometry change manually.";
404 QMacAutoReleasePool pool;
411 QMarginsF viewSafeAreaMargins = {
412 m_view.safeAreaInsets.left,
413 m_view.safeAreaInsets.top,
414 m_view.safeAreaInsets.right,
415 m_view.safeAreaInsets.bottom
422 auto screenRect = m_view.window.screen.frame;
423 auto screenInsets = m_view.window.screen.safeAreaInsets;
424 auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect(
425 NSMinX(screenRect) + screenInsets.left,
426 NSMinY(screenRect) + screenInsets.bottom,
427 NSWidth(screenRect) - screenInsets.left - screenInsets.right,
428 NSHeight(screenRect) - screenInsets.top - screenInsets.bottom
431 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
432 [m_view.window convertRectToScreen:
433 [m_view convertRect:m_view.bounds toView:nil]]
439 QMarginsF screenSafeAreaMargins = {
440 qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left),
441 qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top),
442 qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right),
443 qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom)
446 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
451 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
452 m_lastReportedSafeAreaMargins = safeAreaMargins();
460 if (!(
NSEvent.pressedMouseButtons == 1))
465 NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown
466 location:m_view.window.mouseLocationOutsideOfEventStream
467 modifierFlags:NSEvent.modifierFlags
468 timestamp:NSProcessInfo.processInfo.systemUptime
469 windowNumber:m_view.window.windowNumber context:nil
470 eventNumber:0 clickCount:1 pressure:1.0];
472 [m_view.window performWindowDragWithEvent:mouseEvent];
478 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setVisible" << window() << visible;
491 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping setting visibility";
495 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
496 qCDebug(lcQpaWindow) <<
"No change in visible status. Ignoring.";
501 qCWarning(lcQpaWindow) <<
"Already setting window visible!";
505 QScopedValueRollback<
bool> rollback(m_inSetVisible,
true);
507 QMacAutoReleasePool pool;
509 if (window()->transientParent())
510 parentCocoaWindow =
static_cast<
QCocoaWindow *>(window()->transientParent()->handle());
512 auto eventDispatcher = [] {
513 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
526 if (parentCocoaWindow) {
529 setGeometry(windowGeometry());
531 if (window()->type() == Qt::Popup) {
534 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
535 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
536 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
537 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
538 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
547 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
551 applyWindowState(window()->windowStates());
553 if (window()->windowState() != Qt::WindowMinimized) {
554 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
556 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
557 if (!nativeParentWindow.attachedSheet)
558 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
560 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
561 }
else if (window()->modality() == Qt::ApplicationModal) {
563 eventDispatcher()->beginModalSession(window());
564 }
else if (m_view.window.canBecomeKeyWindow) {
565 bool shouldBecomeKeyNow = !NSApp.modalWindow
566 || m_view.window.worksWhenModal
567 || !NSApp.modalWindow.visible;
571 if ([m_view.window isKindOfClass:[NSPanel
class]])
572 shouldBecomeKeyNow &= !(
static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
574 if (shouldBecomeKeyNow)
575 [m_view.window makeKeyAndOrderFront:nil];
577 [m_view.window orderFront:nil];
579 [m_view.window orderFront:nil];
586 if (eventDispatcher()->hasModalSession())
587 eventDispatcher()->endModalSession(window());
588 else if ([m_view.window isSheet])
589 [m_view.window.sheetParent endSheet:m_view.window];
596 [m_view.window orderOut:nil];
598 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
603 NSWindow *mainWindow = [NSApp mainWindow];
604 if (mainWindow && [mainWindow canBecomeKeyWindow])
605 [mainWindow makeKeyWindow];
619 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
620 && m_view.nextValidKeyView.window != m_view.window) {
621 qCDebug(lcQpaWindow) <<
"Detected nextValidKeyView" << m_view.nextValidKeyView
622 <<
"in different window" << m_view.nextValidKeyView.window
623 <<
"Resetting" << m_view.window <<
"first responder to nil.";
624 [m_view.window makeFirstResponder:nil];
629 if (parentCocoaWindow && window()->type() == Qt::Popup) {
630 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
631 if (m_resizableTransientParent
632 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
634 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
639NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
641 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
643 NSInteger windowLevel = NSNormalWindowLevel;
645 if (type == Qt::Tool)
646 windowLevel = NSFloatingWindowLevel;
647 else if ((type & Qt::Popup) == Qt::Popup)
648 windowLevel = NSPopUpMenuWindowLevel;
651 if (flags & Qt::WindowStaysOnTopHint)
652 windowLevel = NSModalPanelWindowLevel;
654 if (type == Qt::ToolTip)
655 windowLevel = NSScreenSaverWindowLevel;
657 auto *transientParent = window()->transientParent();
658 if (transientParent && transientParent->handle()) {
678 auto *transientCocoaWindow =
static_cast<QCocoaWindow *>(transientParent->handle());
679 auto *nsWindow = transientCocoaWindow->nativeWindow();
684 if (type != Qt::Window)
685 windowLevel = qMax(windowLevel, nsWindow.level);
691NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
const
693 const Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
699 NSUInteger styleMask = [&]{
701 if (flags & Qt::FramelessWindowHint)
702 return NSWindowStyleMaskBorderless;
705 if (windowIsPopupType(type))
706 return NSWindowStyleMaskBorderless;
708 if (flags & Qt::CustomizeWindowHint) {
711 return flags & Qt::WindowTitleHint
712 ? NSWindowStyleMaskTitled
713 : NSWindowStyleMaskBorderless;
716 return NSWindowStyleMaskTitled;
725 styleMask |= NSWindowStyleMaskClosable
726 | NSWindowStyleMaskMiniaturizable;
728 if (type != Qt::Popup)
729 styleMask |= NSWindowStyleMaskResizable;
731 if (type == Qt::Tool)
732 styleMask |= NSWindowStyleMaskUtilityWindow;
734 if (flags & Qt::ExpandedClientAreaHint)
735 styleMask |= NSWindowStyleMaskFullSizeContentView;
738 styleMask |= (m_view.window.styleMask & (
739 NSWindowStyleMaskFullScreen
740 | NSWindowStyleMaskUnifiedTitleAndToolbar
741 | NSWindowStyleMaskDocModalWindow
742 | NSWindowStyleMaskNonactivatingPanel
743 | NSWindowStyleMaskHUDWindow));
748bool QCocoaWindow::isFixedSize()
const
750 return windowMinimumSize().isValid() && windowMaximumSize().isValid()
751 && windowMinimumSize() == windowMaximumSize();
754void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
756 if (!isContentView())
759 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
760 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
761 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
762 { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
765 bool hideButtons =
true;
766 for (
const auto &[button, buttonHint] : buttons) {
769 if (button == NSWindowMiniaturizeButton)
770 enabled = window()->type() != Qt::Dialog;
773 if (windowFlags & Qt::CustomizeWindowHint)
774 enabled = windowFlags & buttonHint;
778 if (button == NSWindowZoomButton && isFixedSize())
784 if (button == NSWindowCloseButton && enabled
785 && QWindowPrivate::get(window())->blockedByModalWindow) {
792 [m_view.window standardWindowButton:button].enabled = enabled;
793 hideButtons &= !enabled;
797 for (
const auto &[button, buttonHint] : buttons)
798 [m_view.window standardWindowButton:button].hidden = hideButtons;
801void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
807 QMacAutoReleasePool pool;
809 if (!isContentView())
812 qCDebug(lcQpaWindow) <<
"Setting" << flags <<
"for" << window();
823 QScopedValueRollback<
bool> geometryChangeBlocker(m_inSetStyleMask,
true);
825 const auto newMask = windowStyleMask(flags);
826 const bool expandedClientAreaChanged = (
827 (newMask & NSWindowStyleMaskFullSizeContentView) !=
828 (m_view.window.styleMask & NSWindowStyleMaskFullSizeContentView));
829 const auto frameGeometry = window()->frameGeometry();
831 m_view.window.styleMask = newMask;
833 if (expandedClientAreaChanged) {
835 setGeometry(frameGeometry.marginsRemoved(frameMargins()),
836 QWindowPrivate::PositionPolicy::WindowFrameExclusive);
840 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
841 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
842 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
843 const bool enableFullScreen = m_view.window.qt_fullScreen
844 || !(flags & Qt::CustomizeWindowHint)
845 || (flags & Qt::WindowFullscreenButtonHint);
846 if (enableFullScreen) {
847 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
848 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
850 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
851 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
853 m_view.window.collectionBehavior = behavior;
858 m_view.window.level =
this->windowLevel(flags);
860 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
862 if (!(flags & Qt::FramelessWindowHint))
863 setWindowTitle(window()->title());
865 updateTitleBarButtons(flags);
874 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
875 if (m_view.window.ignoresMouseEvents != ignoreMouse)
876 m_view.window.ignoresMouseEvents = ignoreMouse;
878 m_view.window.titlebarAppearsTransparent = flags & Qt::NoTitleBarBackgroundHint;
884
885
886
887
888
889
890void QCocoaWindow::setWindowState(Qt::WindowStates state)
892 if (window()->isVisible())
893 applyWindowState(state);
896void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
898 if (!isContentView())
901 const Qt::WindowState currentState = QWindowPrivate::effectiveState(windowState());
902 const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
904 if (newState == currentState)
907 qCDebug(lcQpaWindow) <<
"Applying" << newState <<
"to" << window() <<
"in" << currentState;
909 const NSSize contentSize = m_view.frame.size;
910 if (contentSize.width <= 0 || contentSize.height <= 0) {
913 qWarning(
"invalid window content view size, check your window geometry");
914 handleWindowStateChanged(HandleUnconditionally);
918 const NSWindow *nsWindow = m_view.window;
920 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
921 && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
922 qWarning() << window()->type() <<
"windows cannot be made" << newState;
923 handleWindowStateChanged(HandleUnconditionally);
927 const id sender = nsWindow;
930 switch (currentState) {
931 case Qt::WindowMinimized:
932 [nsWindow deminiaturize:sender];
937 case Qt::WindowFullScreen: {
948 if (newState == windowState())
952 case Qt::WindowFullScreen:
955 case Qt::WindowMaximized:
958 case Qt::WindowMinimized:
959 [nsWindow miniaturize:sender];
961 case Qt::WindowNoState:
962 if (windowState() == Qt::WindowMaximized)
970Qt::WindowStates QCocoaWindow::windowState()
const
972 Qt::WindowStates states = Qt::WindowNoState;
973 NSWindow *window = m_view.window;
975 if (window.miniaturized)
976 states |= Qt::WindowMinimized;
980 if (window.qt_fullScreen) {
981 states |= Qt::WindowFullScreen;
982 }
else if ((window.zoomed && !isTransitioningToFullScreen())
983 || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) {
984 states |= Qt::WindowMaximized;
993void QCocoaWindow::toggleMaximized()
995 const NSWindow *window = m_view.window;
999 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
1000 window.styleMask |= NSWindowStyleMaskResizable;
1002 const id sender = window;
1003 [window zoom:sender];
1006 window.styleMask &= ~NSWindowStyleMaskResizable;
1009void QCocoaWindow::windowWillZoom()
1011 updateNormalGeometry();
1014void QCocoaWindow::toggleFullScreen()
1016 const NSWindow *window = m_view.window;
1021 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1023 const id sender = window;
1024 [window toggleFullScreen:sender];
1027void QCocoaWindow::windowWillEnterFullScreen()
1029 if (!isContentView())
1032 updateNormalGeometry();
1037 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1040bool QCocoaWindow::isTransitioningToFullScreen()
const
1042 NSWindow *window = m_view.window;
1043 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
1046void QCocoaWindow::windowDidEnterFullScreen()
1048 if (!isContentView())
1051 Q_ASSERT_X(m_view.window.qt_fullScreen,
"QCocoaWindow",
1052 "FullScreen category processes window notifications first");
1055 setWindowFlags(window()->flags());
1057 handleWindowStateChanged();
1060void QCocoaWindow::windowWillExitFullScreen()
1062 if (!isContentView())
1067 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1070void QCocoaWindow::windowDidExitFullScreen()
1072 if (!isContentView())
1075 Q_ASSERT_X(!m_view.window.qt_fullScreen,
"QCocoaWindow",
1076 "FullScreen category processes window notifications first");
1079 setWindowFlags(window()->flags());
1081 Qt::WindowState requestedState = window()->windowState();
1084 handleWindowStateChanged();
1086 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
1089 applyWindowState(requestedState);
1093void QCocoaWindow::windowDidMiniaturize()
1095 if (!isContentView())
1098 handleWindowStateChanged();
1101void QCocoaWindow::windowDidDeminiaturize()
1103 if (!isContentView())
1106 Qt::WindowState requestedState = window()->windowState();
1108 handleWindowStateChanged();
1110 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
1113 applyWindowState(requestedState);
1117void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
1119 Qt::WindowStates currentState = windowState();
1120 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
1123 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleWindowStateChanged" <<
1124 m_lastReportedWindowState <<
"-->" << currentState;
1126 QPointer<QCocoaWindow> destructionGuard =
this;
1127 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
1128 window(), currentState, m_lastReportedWindowState);
1129 if (!destructionGuard)
1131 m_lastReportedWindowState = currentState;
1136void QCocoaWindow::setWindowTitle(
const QString &title)
1138 QMacAutoReleasePool pool;
1140 if (!isContentView())
1143 m_view.window.title = title.toNSString();
1145 if (title.isEmpty() && !window()->filePath().isEmpty()) {
1147 setWindowFilePath(window()->filePath());
1151void QCocoaWindow::setWindowFilePath(
const QString &filePath)
1153 QMacAutoReleasePool pool;
1155 if (!isContentView())
1158 if (window()->title().isNull())
1159 [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
1161 m_view.window.representedFilename = filePath.toNSString();
1164 setWindowIcon(window()->icon());
1167void QCocoaWindow::setWindowIcon(
const QIcon &icon)
1169 QMacAutoReleasePool pool;
1171 if (!isContentView())
1174 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
1180 if (icon.isNull()) {
1181 iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename];
1185 auto fallbackSize = QSizeF::fromCGSize(iconButton.frame.size) * qGuiApp->devicePixelRatio();
1186 iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize.toSize()];
1190void QCocoaWindow::setAlertState(
bool enabled)
1192 if (m_alertRequest == NoAlertRequest && enabled) {
1193 m_alertRequest = [NSApp requestUserAttention:NSCriticalRequest];
1194 }
else if (m_alertRequest != NoAlertRequest && !enabled) {
1195 [NSApp cancelUserAttentionRequest:m_alertRequest];
1196 m_alertRequest = NoAlertRequest;
1200bool QCocoaWindow::isAlertState()
const
1202 return m_alertRequest != NoAlertRequest;
1205void QCocoaWindow::raise()
1207 qCDebug(lcQpaWindow) <<
"QCocoaWindow::raise" << window();
1210 if (isContentView()) {
1211 if (m_view.window.visible) {
1218 QMacAutoReleasePool pool;
1219 [m_view.window orderFront:m_view.window];
1221 static bool raiseProcess = qt_mac_resolveOption(
true,
"QT_MAC_SET_RAISE_PROCESS");
1223 [NSApp activateIgnoringOtherApps:YES];
1226 [m_view.superview addSubview:m_view positioned:NSWindowAbove relativeTo:nil];
1230void QCocoaWindow::lower()
1232 qCDebug(lcQpaWindow) <<
"QCocoaWindow::lower" << window();
1234 if (isContentView()) {
1235 if (m_view.window.visible)
1236 [m_view.window orderBack:m_view.window];
1238 [m_view.superview addSubview:m_view positioned:NSWindowBelow relativeTo:nil];
1242bool QCocoaWindow::isExposed()
const
1244 return !m_exposedRect.isEmpty();
1248
1249
1250
1251
1252
1253bool QCocoaWindow::isEmbedded()
const
1259 return m_isEmbedded;
1262bool QCocoaWindow::isOpaque()
const
1266 static GLint openglSourfaceOrder = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
1268 bool translucent = window()->format().alphaBufferSize() > 0
1269 || window()->opacity() < 1
1270 || !window()->mask().isEmpty()
1271 || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
1272 return !translucent;
1275void QCocoaWindow::propagateSizeHints()
1277 QMacAutoReleasePool pool;
1278 if (!isContentView())
1281 qCDebug(lcQpaWindow) <<
"QCocoaWindow::propagateSizeHints" << window()
1282 <<
"min:" << windowMinimumSize() <<
"max:" << windowMaximumSize()
1283 <<
"increment:" << windowSizeIncrement()
1284 <<
"base:" << windowBaseSize();
1286 const NSWindow *window = m_view.window;
1289 QSize minimumSize = windowMinimumSize();
1290 if (!minimumSize.isValid())
1291 minimumSize = QSize(0, 0);
1292 window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
1295 window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
1298 updateTitleBarButtons(
this->window()->flags());
1302 QSize sizeIncrement = windowSizeIncrement();
1303 if (sizeIncrement.isEmpty())
1304 sizeIncrement = QSize(1, 1);
1305 window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
1307 QRect rect = geometry();
1308 QSize baseSize = windowBaseSize();
1309 if (!baseSize.isNull() && baseSize.isValid())
1310 [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
1313void QCocoaWindow::setOpacity(qreal level)
1315 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setOpacity" << level;
1316 if (!isContentView())
1319 m_view.window.alphaValue = level;
1322void QCocoaWindow::setMask(
const QRegion ®ion)
1324 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setMask" << window() << region;
1326 if (!region.isEmpty()) {
1327 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
1328 for (
const QRect &r : region)
1329 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
1330 CAShapeLayer *maskLayer = [CAShapeLayer layer];
1331 maskLayer.path = maskPath;
1332 m_view.layer.mask = maskLayer;
1334 m_view.layer.mask = nil;
1338bool QCocoaWindow::setKeyboardGrabEnabled(
bool)
1343bool QCocoaWindow::setMouseGrabEnabled(
bool)
1348WId QCocoaWindow::winId()
const
1353void QCocoaWindow::setParent(
const QPlatformWindow *parentWindow)
1355 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0);
1357#if QT_CONFIG(accessibility)
1363 if (isForeignWindow())
1364 clearAccessibleParent();
1368 recreateWindowIfNeeded();
1370 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1373NSView *QCocoaWindow::view()
const
1378NSWindow *QCocoaWindow::nativeWindow()
const
1380 return m_view.window;
1383void QCocoaWindow::updateEmbeddedState()
1385 const bool wasEmbedded = m_isEmbedded;
1387 if (QPlatformWindow::parent())
1390 if (!m_view.superview)
1395 auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1396 if (m_view == qtWindowController.window.contentView)
1401 if (m_isEmbedded != wasEmbedded) {
1402 qCDebug(lcQpaWindow) <<
"Updated embedded state from"
1403 << wasEmbedded <<
"to" << m_isEmbedded;
1409void QCocoaWindow::viewDidChangeFrame()
1416 handleGeometryChange();
1420
1421
1422
1423
1424
1425
1426void QCocoaWindow::viewDidChangeGlobalFrame()
1428 [m_view setNeedsDisplay:YES];
1432
1433
1434
1435
1436
1437void QCocoaWindow::viewDidMoveToSuperview(NSView *previousSuperview)
1439 qCDebug(lcQpaWindow) <<
"Done re-parenting" << m_view
1440 <<
"from" << previousSuperview <<
"into" << m_view.superview;
1443 updateEmbeddedState();
1447 handleGeometryChange();
1449 if (m_view.superview)
1450 [m_view setNeedsDisplay:YES];
1461 if (m_view.superview && !m_view.superview.flipped && !isContentView()) {
1462 if (m_view.autoresizingMask == NSViewNotSizable) {
1463 qCDebug(lcQpaWindow) <<
"Setting auto resizing mask on" << m_view
1464 <<
"in non-flipped superview to maintain stable y-positioning";
1465 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1466 m_view.autoresizingMask = NSViewMinYMargin;
1468 }
else if (previousSuperview && !previousSuperview.flipped
1469 && m_view.autoresizingMask == NSViewMinYMargin) {
1474 qCDebug(lcQpaWindow) <<
"Clearing auto resizing mask on" << m_view
1475 <<
"as explicit stable y-positioning is no longer needed";
1476 m_view.autoresizingMask = NSViewNotSizable;
1481
1482
1483
1484
1485void QCocoaWindow::viewDidMoveToWindow(NSWindow *previousWindow)
1487 qCDebug(lcQpaWindow) <<
"Done moving" << m_view
1488 <<
"from" << previousWindow <<
"to" << m_view.window;
1490 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController)) {
1491 qCDebug(lcQpaWindow) <<
"Retaining new window controller" << qtWindowController;
1492 [qtWindowController retain];
1494 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(previousWindow.windowController)) {
1495 qCDebug(lcQpaWindow) <<
"Releasing old window controller" << qtWindowController;
1496 [qtWindowController autorelease];
1502 auto *previousScreen = previousWindow ? QCocoaScreen::get(previousWindow.screen) :
nullptr;
1503 auto *currentScreen = m_view.window ? QCocoaScreen::get(m_view.window.screen) :
nullptr;
1504 if (currentScreen && currentScreen != previousScreen)
1505 windowDidChangeScreen();
1515void QCocoaWindow::windowDidMove()
1517 if (!isContentView())
1520 handleGeometryChange();
1523 handleWindowStateChanged();
1526void QCocoaWindow::windowDidResize()
1528 if (!isContentView())
1531 handleGeometryChange();
1533 if (!m_view.inLiveResize)
1534 handleWindowStateChanged();
1537void QCocoaWindow::windowWillStartLiveResize()
1542 m_inLiveResize =
true;
1545bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1550 return !m_inLiveResize;
1553void QCocoaWindow::windowDidEndLiveResize()
1555 m_inLiveResize =
false;
1557 if (!isContentView())
1560 handleWindowStateChanged();
1563void QCocoaWindow::windowDidBecomeKey()
1567 if (m_view.window.firstResponder != m_view)
1570 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1571 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1573 if (windowIsPopupType()) {
1574 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1579 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1580 window(), Qt::ActiveWindowFocusReason);
1583void QCocoaWindow::windowDidResignKey()
1587 if (m_view.window.firstResponder != m_view)
1590 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1591 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1600 NSWindow *newKeyWindow = [NSApp keyWindow];
1601 if (newKeyWindow && newKeyWindow != m_view.window
1602 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1603 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1604 <<
"is Qt window. Deferring focus window change.";
1609 if (!windowIsPopupType()) {
1610 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1611 nullptr, Qt::ActiveWindowFocusReason);
1615void QCocoaWindow::windowDidOrderOnScreen()
1619 if (QWindowPrivate::get(window())->isPopup()) {
1620 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1621 (QGuiApplicationPrivate::currentMouseWindow);
1624 [m_view setNeedsDisplay:YES];
1627void QCocoaWindow::windowDidOrderOffScreen()
1629 handleExposeEvent(QRegion());
1632 if (window()->type() & Qt::Window) {
1633 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1634 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1635 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1636 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1638 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1639 (windowUnderMouse, windowPoint, screenPoint);
1645void QCocoaWindow::windowDidChangeOcclusionState()
1651 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1652 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1653 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1656 [m_view setNeedsDisplay:YES];
1658 handleExposeEvent(QRegion());
1661void QCocoaWindow::windowDidChangeScreen()
1667 NSScreen *nsScreen = m_view.window.screen;
1669 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1670 QCocoaScreen::updateScreens();
1672 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1673 auto *currentScreen = QCocoaScreen::get(nsScreen);
1675 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1688 QPointer<QCocoaWindow> destructionGuard =
this;
1689 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1690 window(), currentScreen ? currentScreen->screen() :
nullptr);
1691 if (!destructionGuard)
1694 if (currentScreen && hasPendingUpdateRequest()) {
1699 currentScreen->requestUpdate();
1704 previousScreen->maybeStopDisplayLink();
1709bool QCocoaWindow::windowShouldClose()
1711 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1723 [[m_view.window retain] autorelease];
1725 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1730void QCocoaWindow::handleGeometryChange()
1732 const QRect newGeometry = actualGeometry();
1734 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1735 <<
"current" << geometry() <<
"new" << newGeometry;
1740 if (m_inSetStyleMask && !m_view.window) {
1741 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1748 if (!m_initialized) {
1750 QPlatformWindow::setGeometry(newGeometry);
1751 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1755 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1758 updateSafeAreaMarginsIfNeeded();
1762 if (!m_inSetGeometry)
1763 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1766void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1779 if (m_view.window.visible && m_view.window.screen
1780 && !geometry().size().isEmpty() && !region.isEmpty()
1781 && !m_view.hiddenOrHasHiddenAncestor) {
1782 m_exposedRect = region.boundingRect();
1784 m_exposedRect = QRect();
1787 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1789 QPointer<QCocoaWindow> destructionGuard =
this;
1790 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1791 if (!destructionGuard)
1795 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1800bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1802 if (type == Qt::Widget)
1803 type = window()->type();
1804 if (type == Qt::Tool)
1807 return ((type & Qt::Popup) == Qt::Popup);
1811
1812
1813
1814
1815
1816
1817
1818
1819bool QCocoaWindow::isContentView()
const
1821 return m_view.window.contentView == m_view;
1825
1826
1827
1828
1829
1830
1831
1832void QCocoaWindow::recreateWindowIfNeeded()
1834 QMacAutoReleasePool pool;
1836 auto *parentWindow =
static_cast<QCocoaWindow *>(QPlatformWindow::parent());
1838 const bool shouldManageTopLevelWindow = !parentWindow
1839 && !isSubWindow(window()) && !isForeignWindow();
1841 if (shouldManageTopLevelWindow) {
1842 auto *windowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1843 const bool isControllerContentView = m_view == windowController.window.contentView;
1844 const bool windowClassIsCorrect = [m_view.window isKindOfClass:windowClass()];
1846 if (!isControllerContentView || !windowClassIsCorrect) {
1847 qCDebug(lcQpaWindow) <<
"Creating new window controller for" << m_view;
1848 windowController = [[QNSWindowController alloc] initWithCocoaWindow:
this];
1850 m_view.postsFrameChangedNotifications = NO;
1851 windowController.window.contentView = m_view;
1852 m_view.postsFrameChangedNotifications = YES;
1855 [windowController release];
1857 qCDebug(lcQpaWindow) <<
"Re-using existing window controller" << windowController;
1859 }
else if (parentWindow) {
1860 if (m_view.superview != parentWindow->view()) {
1861 qCDebug(lcQpaWindow) <<
"Reparenting view to new parent" << parentWindow;
1862 [parentWindow->view() addSubview:m_view];
1864 }
else if (qnsview_cast(m_view.superview) && !isEmbedded()) {
1865 qCDebug(lcQpaWindow) <<
"Removing view from superview" << m_view.superview;
1866 [m_view removeFromSuperview];
1870Class QCocoaWindow::windowClass()
const
1872 const auto type = window()->type();
1873 return ((type & Qt::Popup) == Qt::Popup
1874 || (type & Qt::Dialog) == Qt::Dialog) ?
1875 QNSPanel.
class : QNSWindow.
class;
1878void QCocoaWindow::requestUpdate()
1880 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1881 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1883 if (updatesWithDisplayLink()) {
1884 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1885 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1886 QPlatformWindow::requestUpdate();
1890 QPlatformWindow::requestUpdate();
1894bool QCocoaWindow::updatesWithDisplayLink()
const
1897 return format().swapInterval() != 0;
1900void QCocoaWindow::deliverUpdateRequest()
1902 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1903 QScopedValueRollback<
bool> blocker(m_deliveringUpdateRequest,
true);
1905 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1910 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1911 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1912 <<
"due to" << qtMetalLayer <<
"needing display";
1918 qtMetalLayer.displayLock.unlock();
1921 QPlatformWindow::deliverUpdateRequest();
1924void QCocoaWindow::requestActivateWindow()
1926 QMacAutoReleasePool pool;
1927 [m_view.window makeFirstResponder:m_view];
1928 [m_view.window makeKeyWindow];
1932
1933
1934void QCocoaWindow::closeAllPopups()
1936 QGuiApplicationPrivate::instance()->closeAllPopups();
1938 removePopupMonitor();
1941void QCocoaWindow::removePopupMonitor()
1943 if (s_globalMouseMonitor) {
1944 [NSEvent removeMonitor:s_globalMouseMonitor];
1945 s_globalMouseMonitor = nil;
1947 if (s_applicationActivationObserver) {
1948 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1949 s_applicationActivationObserver = nil;
1953void QCocoaWindow::setupPopupMonitor()
1960 if (!s_globalMouseMonitor) {
1962 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1963 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1964 | NSEventMaskMouseMoved;
1965 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1966 handler:^(NSEvent *e){
1967 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1968 removePopupMonitor();
1971 const auto eventType = cocoaEvent2QtMouseEvent(e);
1972 if (eventType == QEvent::MouseMove) {
1973 if (s_windowUnderMouse) {
1974 QWindow *window = s_windowUnderMouse->window();
1975 const auto button = cocoaButton2QtButton(e);
1976 const auto buttons = currentlyPressedMouseButtons();
1977 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1978 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1979 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1980 buttons, button, eventType);
1990 if (!s_applicationActivationObserver) {
1991 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1992 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1993 object:nil queue:nil
1994 usingBlock:^(NSNotification *){
2000QCocoaNSWindow *QCocoaWindow::createNSWindow()
2002 QMacAutoReleasePool pool;
2004 Qt::WindowType type = window()->type();
2005 Qt::WindowFlags flags = window()->flags();
2007 QRect rect = geometry();
2009 QScreen *targetScreen =
nullptr;
2010 for (QScreen *screen : QGuiApplication::screens()) {
2011 if (screen->geometry().contains(rect.topLeft())) {
2012 targetScreen = screen;
2017 NSWindowStyleMask styleMask = windowStyleMask(flags);
2019 if (!targetScreen) {
2020 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
2021 targetScreen = QGuiApplication::primaryScreen();
2025 styleMask = NSWindowStyleMaskBorderless;
2028 rect.translate(-targetScreen->geometry().topLeft());
2029 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
2030 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
2032 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
2037 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
2038 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
2039 <<
"Adjusting style mask during creation";
2040 styleMask = NSWindowStyleMaskBorderless;
2045 QCocoaNSWindow *nsWindow = [[windowClass() alloc] initWithContentRect:contentRect
2050 backing:NSBackingStoreBuffered defer:NO
2051 screen:targetCocoaScreen->nativeScreen()
2052 platformWindow:
this];
2056 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
2061 if (!resultingScreen)
2062 resultingScreen = targetCocoaScreen;
2064 if (resultingScreen->screen() != window()->screen()) {
2065 QWindowSystemInterface::handleWindowScreenChanged<
2066 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
2069 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
2070 [](QNSWindowDelegate *delegate) { [delegate release]; });
2071 nsWindow.delegate = sharedDelegate.get();
2077 [nsWindow setReleasedWhenClosed:NO];
2079 if (alwaysShowToolWindow()) {
2080 static dispatch_once_t onceToken;
2081 dispatch_once(&onceToken, ^{
2082 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
2083 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2084 name:NSApplicationWillResignActiveNotification object:nil];
2085 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2086 name:NSApplicationWillBecomeActiveNotification object:nil];
2090 nsWindow.restorable = NO;
2091 nsWindow.level = windowLevel(flags);
2092 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
2094 if ([nsWindow isKindOfClass:NSPanel.
class]) {
2096 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
2099 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
2100 | NSWindowCollectionBehaviorMoveToActiveSpace;
2102 if ((type & Qt::Popup) == Qt::Popup) {
2103 nsWindow.hasShadow = YES;
2104 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
2105 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
2106 setupPopupMonitor();
2117 if (
auto *qtView = qnsview_cast(m_view))
2118 nsWindow.colorSpace = qtView.colorSpace;
2123bool QCocoaWindow::alwaysShowToolWindow()
const
2125 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2128bool QCocoaWindow::setWindowModified(
bool modified)
2130 QMacAutoReleasePool pool;
2132 if (!isContentView())
2135 m_view.window.documentEdited = modified;
2139void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2144QCocoaMenuBar *QCocoaWindow::menubar()
const
2149void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2151 QMacAutoReleasePool pool;
2154 if (isForeignWindow())
2157 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2159 QNSView *view = qnsview_cast(m_view);
2160 if (cursor == view.cursor)
2163 view.cursor = cursor;
2169 [m_view.window invalidateCursorRectsForView:m_view];
2176 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2177 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2178 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2179 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2180 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2181 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2182 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2183 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2184 location:locationInWindow modifierFlags:0 timestamp:0
2185 windowNumber:m_view.window.windowNumber context:nil
2186 eventNumber:0 trackingNumber:0 userData:0]];
2190void QCocoaWindow::registerTouch(
bool enable)
2192 m_registerTouchCount += enable ? 1 : -1;
2193 if (enable && m_registerTouchCount == 1)
2194 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2195 else if (m_registerTouchCount == 0)
2196 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2199qreal QCocoaWindow::devicePixelRatio()
const
2206 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2207 return backingSize.height;
2210QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2212 QWindow *targetWindow = window();
2213 for (QObject *child : targetWindow->children())
2214 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2215 if (QPlatformWindow *handle = childWindow->handle())
2216 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2217 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2219 return targetWindow;
2222bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2227 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2237 QWindow *modalWindow =
nullptr;
2238 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2239 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2240 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2241 <<
"blocked by" << modalWindow;
2246 if (m_inSetVisible) {
2247 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2248 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2255bool QCocoaWindow::windowEvent(QEvent *event)
2257 switch (event->type()) {
2258 case QEvent::WindowBlocked:
2259 case QEvent::WindowUnblocked:
2260 updateTitleBarButtons(window()->flags());
2266 return QPlatformWindow::windowEvent(event);
2269QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2273 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2274 : NSMakePoint(0, 0);
2275 const NSRect visibleRect = [m_view visibleRect];
2277 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2280QMargins QCocoaWindow::frameMargins()
const
2282 QMacAutoReleasePool pool;
2287 if (QPlatformWindow::parent())
2293 if (m_view.window) {
2294 frameRect = m_view.window.frame;
2295 contentRect = [m_view.window contentRectForFrameRect:frameRect];
2297 contentRect = m_view.frame;
2298 frameRect = [NSWindow frameRectForContentRect:contentRect styleMask:windowStyleMask(window()->flags())];
2301 return QMargins(frameRect.origin.x - contentRect.origin.x,
2302 (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height),
2303 (frameRect.origin.x + frameRect.size.width) - (contentRect.origin.x + contentRect.size.width),
2304 contentRect.origin.y - contentRect.origin.y);
2307void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2309 m_frameStrutEventsEnabled = enabled;
2312QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2314 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2315 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2316 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2319QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2321 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2322 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2323 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2326CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2328 if (!referenceView || referenceView.flipped)
2329 return point.toCGPoint();
2331 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2334CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2336 if (!referenceView || referenceView.flipped)
2337 return rect.toCGRect();
2339 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2342QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2344 if (!referenceView || referenceView.flipped)
2345 return QPointF::fromCGPoint(point);
2347 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2350QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2352 if (!referenceView || referenceView.flipped)
2353 return QRectF::fromCGRect(rect);
2355 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2358CALayer *QCocoaWindow::contentLayer()
const
2360 auto *layer = m_view.layer;
2361 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2362 layer = containerLayer.contentLayer;
2366void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2367 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2368 NSVisualEffectState activationState)
2370 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2371 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2372 <<
"in views without a container layer";
2376 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2377 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2378 << blendMode <<
"and activation state" << activationState;
2380 NSVisualEffectView *effectView =
nullptr;
2381 if (m_effectViews.contains(identifier)) {
2382 effectView = m_effectViews.value(identifier);
2383 if (rect.isEmpty()) {
2384 [effectView removeFromSuperview];
2385 m_effectViews.remove(identifier);
2388 }
else if (!rect.isEmpty()) {
2389 effectView = [NSVisualEffectView
new];
2392 effectView.wantsLayer = YES;
2393 effectView.layer.zPosition = -FLT_MAX;
2394 [m_view addSubview:effectView];
2395 m_effectViews.insert(identifier, effectView);
2401 effectView.frame = rect.toCGRect();
2402 effectView.material = material;
2403 effectView.blendingMode = blendMode;
2404 effectView.state = activationState;
2407#ifndef QT_NO_DEBUG_STREAM
2408QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2410 QDebugStateSaver saver(debug);
2412 debug <<
"QCocoaWindow(" << (
const void *)window;
2414 debug <<
", window=" << window->window();
2422#if QT_CONFIG(accessibility)
2424static void setAccessibilityParent(NSView *view, id accessibilityParent)
2430 for (id<NSAccessibility> element in NSAccessibilityUnignoredChildrenForOnlyChild(view)) {
2432 if (![element respondsToSelector:@selector(setAccessibilityParent:)])
2435 if (element.accessibilityParent == accessibilityParent)
2438 qCDebug(lcQpaAccessibility) <<
"Setting a11y parent of" << element
2439 <<
"for" << view <<
"to" << accessibilityParent;
2440 element.accessibilityParent = accessibilityParent;
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458void QCocoaWindow::updateAccessibleParent()
2460 if (!isForeignWindow())
2463 QObject *accessibleParent = QWindowPrivate::get(window())->accessibleParent;
2464 if (QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(accessibleParent)) {
2465 setAccessibilityParent(m_view, NSAccessibilityUnignoredAncestor(
2466 [QMacAccessibilityElement elementWithInterface:iface]));
2467 m_didOverrideAccessibleParent =
true;
2469 clearAccessibleParent();
2473void QCocoaWindow::clearAccessibleParent()
2475 if (!isForeignWindow() || !m_didOverrideAccessibleParent)
2484 setAccessibilityParent(m_view, nil);
2486 m_didOverrideAccessibleParent =
false;
2495#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
Q_FORWARD_DECLARE_OBJC_CLASS(NSEvent)
#define Q_NOTIFICATION_PREFIX
static void qRegisterNotificationCallbacks()
const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")