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 <QtGui/private/qpointingdevice_p.h>
26#include <QtGui/private/qeventpoint_p.h>
27#include <qpa/qwindowsysteminterface.h>
28#include <qpa/qplatformscreen.h>
29#include <QtGui/qaccessible.h>
30#include <QtGui/private/qcoregraphics_p.h>
31#include <QtGui/private/qhighdpiscaling_p.h>
32#include <QtGui/private/qmetallayer_p.h>
39- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow;
42@implementation QNSWindowController {
43 QPointer<QCocoaWindow> m_platformWindow;
46- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow
50 if ((self = [super initWithWindowNibName:@
"QNSWindowController"]))
51 m_platformWindow = platformWindow;
58 QMacAutoReleasePool pool;
59 self.window = [m_platformWindow->createNSWindow() autorelease];
63 qCDebug(lcQpaWindow) <<
"Disposing of" << self.window <<
"for" << m_platformWindow;
73 defaultWindowWidth = 160,
74 defaultWindowHeight = 160
83 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
85 const QMetaObject *metaObject = QMetaType(qRegisterMetaType<
QCocoaWindow*>()).metaObject();
88 for (
int i = 0; i < metaObject->methodCount(); ++i) {
89 QMetaMethod method = metaObject->method(i);
90 const QString methodTag = QString::fromLatin1(method.tag());
91 if (!methodTag.startsWith(notificationHandlerPrefix))
94 const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
95 [center addObserverForName:notificationName.toNSString() object:nil queue:nil
96 usingBlock:^(NSNotification *notification) {
98 QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
99 if ([notification.object isKindOfClass:[NSWindow
class]]) {
100 NSWindow *nsWindow = notification.object;
101 for (
const QWindow *window : QGuiApplication::allWindows()) {
102 if (QCocoaWindow *cocoaWindow =
static_cast<QCocoaWindow *>(window->handle()))
103 if (cocoaWindow->nativeWindow() == nsWindow)
104 cocoaWindows += cocoaWindow;
106 }
else if ([notification.object isKindOfClass:[NSView
class]]) {
107 if (QNSView *qnsView = qnsview_cast(notification.object))
108 cocoaWindows += qnsView.platformWindow;
110 qCWarning(lcCocoaNotifications) <<
"Unhandled notification"
111 << notification.name <<
"for" << notification.object;
115 if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) {
116 QVector<QCocoaWindow *> debugWindows;
117 for (QCocoaWindow *cocoaWindow : cocoaWindows)
118 debugWindows += cocoaWindow;
119 qCDebug(lcCocoaNotifications) <<
"Forwarding" << qPrintable(notificationName) <<
120 "to" << debugWindows;
125 for (QCocoaWindow *cocoaWindow : cocoaWindows) {
126 if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
127 qCWarning(lcQpaWindow) <<
"Failed to invoke NSNotification callback for"
128 << notification.name <<
"on" << cocoaWindow;
134Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
139QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) : QPlatformWindow(win)
141 qCDebug(lcQpaWindow) <<
"QCocoaWindow::QCocoaWindow" << window();
144 m_view =
reinterpret_cast<NSView *>(nativeHandle);
151 qCDebug(lcQpaWindow) <<
"QCocoaWindow::initialize" << window();
153 QMacAutoReleasePool pool;
156 m_view = [[QNSView alloc] initWithCocoaWindow:
this];
162 auto initialGeometry = QPlatformWindow::initialGeometry(window(),
163 windowGeometry(), defaultWindowWidth, defaultWindowHeight);
169 if (QPlatformWindow::parent() || isSubWindow(window())) {
175 setGeometry(initialGeometry);
177 if (window()->flags() & Qt::ExpandedClientAreaHint) {
180 QRect frameGeometryWithFrame = QCocoaScreen::mapFromNative(
181 [NSWindow frameRectForContentRect:QCocoaScreen::mapToNative(initialGeometry)
182 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
183 if (qt_window_private(window())->positionPolicy == QWindowPrivate::WindowFrameExclusive)
184 initialGeometry = frameGeometryWithFrame;
186 initialGeometry.setSize(frameGeometryWithFrame.size());
194 QPlatformWindow::setGeometry(initialGeometry);
202 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
204 m_safeAreaInsetsObserver = QMacKeyValueObserver(
205 m_view, @
"safeAreaInsets", [
this] {
208 QMetaObject::invokeMethod(
this, [
this]{
209 updateSafeAreaMarginsIfNeeded();
210 }, Qt::QueuedConnection);
211 }, NSKeyValueObservingOptionNew);
217 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
227 qCDebug(lcQpaWindow) <<
"QCocoaWindow::~QCocoaWindow" << window();
229 QMacAutoReleasePool pool;
231 if (window()->flags() & Qt::ExpandedClientAreaHint) {
235 auto readjustedGeometry = geometry();
236 QRect contentRectWithFrame = QCocoaScreen::mapFromNative(
237 [NSWindow contentRectForFrameRect:QCocoaScreen::mapToNative(geometry())
238 styleMask:windowStyleMask(window()->flags() & ~Qt::ExpandedClientAreaHint)]).toRect();
239 readjustedGeometry = contentRectWithFrame;
240 setGeometry(readjustedGeometry, QWindowPrivate::PositionPolicy::WindowFrameExclusive);
245 if (!isForeignWindow() || QPlatformWindow::parent())
246 [m_view removeFromSuperview];
248#if QT_CONFIG(accessibility)
249 if (isForeignWindow())
250 clearAccessibleParent();
253 m_safeAreaInsetsObserver = {};
258 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
261 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
262 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
264 vulcanInstance->destroySurface(m_vulkanSurface);
271 [NSNotificationCenter.defaultCenter
272 postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
280 static_cast<
QCocoaScreen *>(screen())->maybeStopDisplayLink();
285 auto format = window()->requestedFormat();
286 if (
auto *view = qnsview_cast(m_view); view.colorSpace) {
287 auto colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(view.colorSpace.ICCProfileData));
288 if (!colorSpace.isValid()) {
289 qCWarning(lcQpaWindow) <<
"Failed to parse ICC profile for" << view.colorSpace
290 <<
"with ICC data" << view.colorSpace.ICCProfileData;
292 format.setColorSpace(colorSpace);
299 return ![m_view isKindOfClass:[QNSView
class]];
304 return QPlatformWindow::geometry();
311 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
314 return QCocoaScreen::mapFromNative(contentRect).toRect();
316 return QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
321
322
323
324
325
326
327
337 if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
340 return m_normalGeometry;
348 if (windowState() != Qt::WindowNoState)
351 m_normalGeometry = geometry();
357 setGeometry(rect, qt_window_private(window())->positionPolicy);
362 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setGeometry" << window() << rectIn << positionPolicy;
363 QMacAutoReleasePool pool;
366 if (positionPolicy == QWindowPrivate::WindowFrameInclusive) {
370 const QMargins margins = frameMargins();
371 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
372 qCDebug(lcQpaWindow) <<
"Adjusted content geometry to" << rect <<
"by removing frame margins" << margins;
375 QPlatformWindow::setGeometry(rect);
377 const QRect originalGeometry = actualGeometry();
385 NSRect bounds = QCocoaScreen::mapToNative(rect);
386 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
389 m_view.frame = QCocoaWindow::mapToNative(rect, m_view.superview);
392 if (actualGeometry() == originalGeometry) {
399 qCDebug(lcQpaWindow) <<
"Native geometry didn't change. Reporting geometry change manually.";
406 QMacAutoReleasePool pool;
413 QMarginsF viewSafeAreaMargins = {
414 m_view.safeAreaInsets.left,
415 m_view.safeAreaInsets.top,
416 m_view.safeAreaInsets.right,
417 m_view.safeAreaInsets.bottom
424 auto screenRect = m_view.window.screen.frame;
425 auto screenInsets = m_view.window.screen.safeAreaInsets;
426 auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect(
427 NSMinX(screenRect) + screenInsets.left,
428 NSMinY(screenRect) + screenInsets.bottom,
429 NSWidth(screenRect) - screenInsets.left - screenInsets.right,
430 NSHeight(screenRect) - screenInsets.top - screenInsets.bottom
433 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
434 [m_view.window convertRectToScreen:
435 [m_view convertRect:m_view.bounds toView:nil]]
441 QMarginsF screenSafeAreaMargins = {
442 qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left),
443 qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top),
444 qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right),
445 qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom)
448 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
453 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
454 m_lastReportedSafeAreaMargins = safeAreaMargins();
462 if (!(
NSEvent.pressedMouseButtons == 1))
467 NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown
468 location:m_view.window.mouseLocationOutsideOfEventStream
469 modifierFlags:NSEvent.modifierFlags
470 timestamp:NSProcessInfo.processInfo.systemUptime
471 windowNumber:m_view.window.windowNumber context:nil
472 eventNumber:0 clickCount:1 pressure:1.0];
474 [m_view.window performWindowDragWithEvent:mouseEvent];
480 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setVisible" << window() << visible;
493 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping setting visibility";
497 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
498 qCDebug(lcQpaWindow) <<
"No change in visible status. Ignoring.";
503 qCWarning(lcQpaWindow) <<
"Already setting window visible!";
507 QScopedValueRollback<
bool> rollback(m_inSetVisible,
true);
509 QMacAutoReleasePool pool;
511 if (window()->transientParent())
512 parentCocoaWindow =
static_cast<
QCocoaWindow *>(window()->transientParent()->handle());
514 auto eventDispatcher = [] {
515 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
528 if (parentCocoaWindow) {
531 setGeometry(windowGeometry());
533 if (window()->type() == Qt::Popup) {
536 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
537 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
538 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
539 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
540 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
549 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
553 applyWindowState(window()->windowStates());
555 if (window()->windowState() != Qt::WindowMinimized) {
556 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
558 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
559 if (!nativeParentWindow.attachedSheet)
560 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
562 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
563 }
else if (window()->modality() == Qt::ApplicationModal) {
565 eventDispatcher()->beginModalSession(window());
566 }
else if (m_view.window.canBecomeKeyWindow) {
567 bool shouldBecomeKeyNow = !NSApp.modalWindow
568 || m_view.window.worksWhenModal
569 || !NSApp.modalWindow.visible;
573 if ([m_view.window isKindOfClass:[NSPanel
class]])
574 shouldBecomeKeyNow &= !(
static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
576 if (shouldBecomeKeyNow)
577 [m_view.window makeKeyAndOrderFront:nil];
579 [m_view.window orderFront:nil];
581 [m_view.window orderFront:nil];
588 if (eventDispatcher()->hasModalSession())
589 eventDispatcher()->endModalSession(window());
590 else if ([m_view.window isSheet])
591 [m_view.window.sheetParent endSheet:m_view.window];
598 [m_view.window orderOut:nil];
600 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
605 NSWindow *mainWindow = [NSApp mainWindow];
606 if (mainWindow && [mainWindow canBecomeKeyWindow])
607 [mainWindow makeKeyWindow];
621 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
622 && m_view.nextValidKeyView.window != m_view.window) {
623 qCDebug(lcQpaWindow) <<
"Detected nextValidKeyView" << m_view.nextValidKeyView
624 <<
"in different window" << m_view.nextValidKeyView.window
625 <<
"Resetting" << m_view.window <<
"first responder to nil.";
626 [m_view.window makeFirstResponder:nil];
631 if (parentCocoaWindow && window()->type() == Qt::Popup) {
632 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
633 if (m_resizableTransientParent
634 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
636 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
641NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
643 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
645 NSInteger windowLevel = NSNormalWindowLevel;
647 if (type == Qt::Tool)
648 windowLevel = NSFloatingWindowLevel;
649 else if ((type & Qt::Popup) == Qt::Popup)
650 windowLevel = NSPopUpMenuWindowLevel;
653 if (flags & Qt::WindowStaysOnTopHint)
654 windowLevel = NSModalPanelWindowLevel;
656 if (type == Qt::ToolTip)
657 windowLevel = NSScreenSaverWindowLevel;
659 auto *transientParent = window()->transientParent();
660 if (transientParent && transientParent->handle()) {
680 auto *transientCocoaWindow =
static_cast<QCocoaWindow *>(transientParent->handle());
681 auto *nsWindow = transientCocoaWindow->nativeWindow();
686 if (type != Qt::Window)
687 windowLevel = qMax(windowLevel, nsWindow.level);
693NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
const
695 const Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
701 NSUInteger styleMask = [&]{
703 if (flags & Qt::FramelessWindowHint)
704 return NSWindowStyleMaskBorderless;
707 if (windowIsPopupType(type))
708 return NSWindowStyleMaskBorderless;
710 if (flags & Qt::CustomizeWindowHint) {
713 return flags & Qt::WindowTitleHint
714 ? NSWindowStyleMaskTitled
715 : NSWindowStyleMaskBorderless;
718 return NSWindowStyleMaskTitled;
727 styleMask |= NSWindowStyleMaskClosable
728 | NSWindowStyleMaskMiniaturizable;
730 if (type != Qt::Popup)
731 styleMask |= NSWindowStyleMaskResizable;
733 if (type == Qt::Tool)
734 styleMask |= NSWindowStyleMaskUtilityWindow;
736 if (flags & Qt::ExpandedClientAreaHint)
737 styleMask |= NSWindowStyleMaskFullSizeContentView;
740 styleMask |= (m_view.window.styleMask & (
741 NSWindowStyleMaskFullScreen
742 | NSWindowStyleMaskUnifiedTitleAndToolbar
743 | NSWindowStyleMaskDocModalWindow
744 | NSWindowStyleMaskNonactivatingPanel
745 | NSWindowStyleMaskHUDWindow));
750bool QCocoaWindow::isFixedSize()
const
752 return windowMinimumSize().isValid() && windowMaximumSize().isValid()
753 && windowMinimumSize() == windowMaximumSize();
756void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
758 if (!isContentView())
761 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
762 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
763 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
764 { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
767 bool hideButtons =
true;
768 for (
const auto &[button, buttonHint] : buttons) {
771 if (button == NSWindowMiniaturizeButton)
772 enabled = window()->type() != Qt::Dialog;
775 if (windowFlags & Qt::CustomizeWindowHint)
776 enabled = windowFlags & buttonHint;
780 if (button == NSWindowZoomButton && isFixedSize())
786 if (button == NSWindowCloseButton && enabled
787 && QWindowPrivate::get(window())->blockedByModalWindow) {
794 [m_view.window standardWindowButton:button].enabled = enabled;
795 hideButtons &= !enabled;
799 for (
const auto &[button, buttonHint] : buttons)
800 [m_view.window standardWindowButton:button].hidden = hideButtons;
803void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
809 QMacAutoReleasePool pool;
811 if (!isContentView())
814 qCDebug(lcQpaWindow) <<
"Setting" << flags <<
"for" << window();
825 QScopedValueRollback<
bool> geometryChangeBlocker(m_inSetStyleMask,
true);
827 const auto newMask = windowStyleMask(flags);
828 const bool expandedClientAreaChanged = (
829 (newMask & NSWindowStyleMaskFullSizeContentView) !=
830 (m_view.window.styleMask & NSWindowStyleMaskFullSizeContentView));
831 const QRect frameGeometry = geometry().marginsAdded(frameMargins());
833 m_view.window.styleMask = newMask;
835 if (expandedClientAreaChanged) {
837 setGeometry(frameGeometry.marginsRemoved(frameMargins()),
838 QWindowPrivate::PositionPolicy::WindowFrameExclusive);
842 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
843 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
844 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
845 const bool enableFullScreen = m_view.window.qt_fullScreen
846 || !(flags & Qt::CustomizeWindowHint)
847 || (flags & Qt::WindowFullscreenButtonHint);
848 if (enableFullScreen) {
849 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
850 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
852 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
853 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
855 m_view.window.collectionBehavior = behavior;
860 m_view.window.level =
this->windowLevel(flags);
862 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
864 if (!(flags & Qt::FramelessWindowHint))
865 setWindowTitle(window()->title());
867 updateTitleBarButtons(flags);
876 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
877 if (m_view.window.ignoresMouseEvents != ignoreMouse)
878 m_view.window.ignoresMouseEvents = ignoreMouse;
880 m_view.window.titlebarAppearsTransparent = flags & Qt::NoTitleBarBackgroundHint;
886
887
888
889
890
891
892void QCocoaWindow::setWindowState(Qt::WindowStates state)
894 if (window()->isVisible())
895 applyWindowState(state);
898void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
900 if (!isContentView())
903 const Qt::WindowState currentState = QWindowPrivate::effectiveState(windowState());
904 const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
906 if (newState == currentState)
909 qCDebug(lcQpaWindow) <<
"Applying" << newState <<
"to" << window() <<
"in" << currentState;
911 const NSSize contentSize = m_view.frame.size;
912 if (contentSize.width <= 0 || contentSize.height <= 0) {
915 qWarning(
"invalid window content view size, check your window geometry");
916 handleWindowStateChanged(HandleUnconditionally);
920 const NSWindow *nsWindow = m_view.window;
922 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
923 && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
924 qWarning() << window()->type() <<
"windows cannot be made" << newState;
925 handleWindowStateChanged(HandleUnconditionally);
929 const id sender = nsWindow;
932 switch (currentState) {
933 case Qt::WindowMinimized:
934 [nsWindow deminiaturize:sender];
939 case Qt::WindowFullScreen: {
950 if (newState == windowState())
954 case Qt::WindowFullScreen:
957 case Qt::WindowMaximized:
960 case Qt::WindowMinimized:
961 [nsWindow miniaturize:sender];
963 case Qt::WindowNoState:
964 if (windowState() == Qt::WindowMaximized)
972Qt::WindowStates QCocoaWindow::windowState()
const
974 Qt::WindowStates states = Qt::WindowNoState;
975 NSWindow *window = m_view.window;
977 if (window.miniaturized)
978 states |= Qt::WindowMinimized;
982 if (window.qt_fullScreen) {
983 states |= Qt::WindowFullScreen;
984 }
else if ((window.zoomed && !isTransitioningToFullScreen())
985 || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) {
986 states |= Qt::WindowMaximized;
995void QCocoaWindow::toggleMaximized()
997 const NSWindow *window = m_view.window;
1001 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
1002 window.styleMask |= NSWindowStyleMaskResizable;
1004 const id sender = window;
1005 [window zoom:sender];
1008 window.styleMask &= ~NSWindowStyleMaskResizable;
1011void QCocoaWindow::windowWillZoom()
1013 updateNormalGeometry();
1016void QCocoaWindow::toggleFullScreen()
1018 const NSWindow *window = m_view.window;
1023 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1025 const id sender = window;
1026 [window toggleFullScreen:sender];
1029void QCocoaWindow::windowWillEnterFullScreen()
1031 if (!isContentView())
1034 updateNormalGeometry();
1039 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1042bool QCocoaWindow::isTransitioningToFullScreen()
const
1044 NSWindow *window = m_view.window;
1045 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
1048void QCocoaWindow::windowDidEnterFullScreen()
1050 if (!isContentView())
1053 Q_ASSERT_X(m_view.window.qt_fullScreen,
"QCocoaWindow",
1054 "FullScreen category processes window notifications first");
1057 setWindowFlags(window()->flags());
1059 handleWindowStateChanged();
1062void QCocoaWindow::windowWillExitFullScreen()
1064 if (!isContentView())
1069 m_view.window.styleMask |= NSWindowStyleMaskResizable;
1072void QCocoaWindow::windowDidExitFullScreen()
1074 if (!isContentView())
1077 Q_ASSERT_X(!m_view.window.qt_fullScreen,
"QCocoaWindow",
1078 "FullScreen category processes window notifications first");
1081 setWindowFlags(window()->flags());
1083 Qt::WindowState requestedState = window()->windowState();
1086 handleWindowStateChanged();
1088 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
1091 applyWindowState(requestedState);
1095void QCocoaWindow::windowDidMiniaturize()
1097 if (!isContentView())
1100 handleWindowStateChanged();
1103void QCocoaWindow::windowDidDeminiaturize()
1105 if (!isContentView())
1108 Qt::WindowState requestedState = window()->windowState();
1110 handleWindowStateChanged();
1112 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
1115 applyWindowState(requestedState);
1119void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
1121 Qt::WindowStates currentState = windowState();
1122 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
1125 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleWindowStateChanged" <<
1126 m_lastReportedWindowState <<
"-->" << currentState;
1128 QPointer<QCocoaWindow> destructionGuard =
this;
1129 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
1130 window(), currentState, m_lastReportedWindowState);
1131 if (!destructionGuard)
1133 m_lastReportedWindowState = currentState;
1138void QCocoaWindow::setWindowTitle(
const QString &title)
1140 QMacAutoReleasePool pool;
1142 if (!isContentView())
1145 m_view.window.title = title.toNSString();
1147 if (title.isEmpty() && !window()->filePath().isEmpty()) {
1149 setWindowFilePath(window()->filePath());
1153void QCocoaWindow::setWindowFilePath(
const QString &filePath)
1155 QMacAutoReleasePool pool;
1157 if (!isContentView())
1160 if (window()->title().isNull())
1161 [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
1163 m_view.window.representedFilename = filePath.toNSString();
1166 setWindowIcon(window()->icon());
1169void QCocoaWindow::setWindowIcon(
const QIcon &icon)
1171 QMacAutoReleasePool pool;
1173 if (!isContentView())
1176 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
1182 if (icon.isNull()) {
1183 iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename];
1187 auto fallbackSize = QSizeF::fromCGSize(iconButton.frame.size) * qGuiApp->devicePixelRatio();
1188 iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize.toSize()];
1192void QCocoaWindow::setAlertState(
bool enabled)
1194 if (m_alertRequest == NoAlertRequest && enabled) {
1195 m_alertRequest = [NSApp requestUserAttention:NSCriticalRequest];
1196 }
else if (m_alertRequest != NoAlertRequest && !enabled) {
1197 [NSApp cancelUserAttentionRequest:m_alertRequest];
1198 m_alertRequest = NoAlertRequest;
1202bool QCocoaWindow::isAlertState()
const
1204 return m_alertRequest != NoAlertRequest;
1207void QCocoaWindow::raise()
1209 qCDebug(lcQpaWindow) <<
"QCocoaWindow::raise" << window();
1212 if (isContentView()) {
1213 if (m_view.window.visible) {
1220 QMacAutoReleasePool pool;
1221 [m_view.window orderFront:m_view.window];
1223 static bool raiseProcess = qt_mac_resolveOption(
true,
"QT_MAC_SET_RAISE_PROCESS");
1225 [NSApp activateIgnoringOtherApps:YES];
1228 [m_view.superview addSubview:m_view positioned:NSWindowAbove relativeTo:nil];
1232void QCocoaWindow::lower()
1234 qCDebug(lcQpaWindow) <<
"QCocoaWindow::lower" << window();
1236 if (isContentView()) {
1237 if (m_view.window.visible)
1238 [m_view.window orderBack:m_view.window];
1240 [m_view.superview addSubview:m_view positioned:NSWindowBelow relativeTo:nil];
1244bool QCocoaWindow::isExposed()
const
1246 return !m_exposedRect.isEmpty();
1250
1251
1252
1253
1254
1255bool QCocoaWindow::isEmbedded()
const
1261 return m_isEmbedded;
1264bool QCocoaWindow::isOpaque()
const
1268 static GLint openglSourfaceOrder = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
1270 bool translucent = window()->format().alphaBufferSize() > 0
1271 || window()->opacity() < 1
1272 || !window()->mask().isEmpty()
1273 || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
1274 return !translucent;
1277void QCocoaWindow::propagateSizeHints()
1279 QMacAutoReleasePool pool;
1280 if (!isContentView())
1283 qCDebug(lcQpaWindow) <<
"QCocoaWindow::propagateSizeHints" << window()
1284 <<
"min:" << windowMinimumSize() <<
"max:" << windowMaximumSize()
1285 <<
"increment:" << windowSizeIncrement()
1286 <<
"base:" << windowBaseSize();
1288 const NSWindow *window = m_view.window;
1291 QSize minimumSize = windowMinimumSize();
1292 if (!minimumSize.isValid())
1293 minimumSize = QSize(0, 0);
1294 window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
1297 window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
1300 updateTitleBarButtons(
this->window()->flags());
1304 QSize sizeIncrement = windowSizeIncrement();
1305 if (sizeIncrement.isEmpty())
1306 sizeIncrement = QSize(1, 1);
1307 window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
1309 QRect rect = geometry();
1310 QSize baseSize = windowBaseSize();
1311 if (!baseSize.isNull() && baseSize.isValid())
1312 [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
1315void QCocoaWindow::setOpacity(qreal level)
1317 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setOpacity" << level;
1318 if (!isContentView())
1321 m_view.window.alphaValue = level;
1324void QCocoaWindow::setMask(
const QRegion ®ion)
1326 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setMask" << window() << region;
1328 if (!region.isEmpty()) {
1329 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
1330 for (
const QRect &r : region)
1331 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
1332 CAShapeLayer *maskLayer = [CAShapeLayer layer];
1333 maskLayer.path = maskPath;
1334 m_view.layer.mask = maskLayer;
1336 m_view.layer.mask = nil;
1340bool QCocoaWindow::setKeyboardGrabEnabled(
bool)
1345bool QCocoaWindow::setMouseGrabEnabled(
bool)
1350WId QCocoaWindow::winId()
const
1355void QCocoaWindow::setParent(
const QPlatformWindow *parentWindow)
1357 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0);
1359#if QT_CONFIG(accessibility)
1365 if (isForeignWindow())
1366 clearAccessibleParent();
1370 recreateWindowIfNeeded();
1372 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1375NSView *QCocoaWindow::view()
const
1380NSWindow *QCocoaWindow::nativeWindow()
const
1382 return m_view.window;
1385void QCocoaWindow::updateEmbeddedState()
1387 const bool wasEmbedded = m_isEmbedded;
1389 if (QPlatformWindow::parent())
1392 if (!m_view.superview)
1397 auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1398 if (m_view == qtWindowController.window.contentView)
1403 if (m_isEmbedded != wasEmbedded) {
1404 qCDebug(lcQpaWindow) <<
"Updated embedded state from"
1405 << wasEmbedded <<
"to" << m_isEmbedded;
1411void QCocoaWindow::viewDidChangeFrame()
1418 handleGeometryChange();
1422
1423
1424
1425
1426
1427
1428void QCocoaWindow::viewDidChangeGlobalFrame()
1430 [m_view setNeedsDisplay:YES];
1434
1435
1436
1437
1438
1439void QCocoaWindow::viewDidMoveToSuperview(NSView *previousSuperview)
1441 qCDebug(lcQpaWindow) <<
"Done re-parenting" << m_view
1442 <<
"from" << previousSuperview <<
"into" << m_view.superview;
1445 updateEmbeddedState();
1449 handleGeometryChange();
1451 if (m_view.superview)
1452 [m_view setNeedsDisplay:YES];
1463 if (m_view.superview && !m_view.superview.flipped && !isContentView()) {
1464 if (m_view.autoresizingMask == NSViewNotSizable) {
1465 qCDebug(lcQpaWindow) <<
"Setting auto resizing mask on" << m_view
1466 <<
"in non-flipped superview to maintain stable y-positioning";
1467 setGeometry(geometry(), QWindowPrivate::WindowFrameExclusive);
1468 m_view.autoresizingMask = NSViewMinYMargin;
1470 }
else if (previousSuperview && !previousSuperview.flipped
1471 && m_view.autoresizingMask == NSViewMinYMargin) {
1476 qCDebug(lcQpaWindow) <<
"Clearing auto resizing mask on" << m_view
1477 <<
"as explicit stable y-positioning is no longer needed";
1478 m_view.autoresizingMask = NSViewNotSizable;
1483
1484
1485
1486
1487void QCocoaWindow::viewDidMoveToWindow(NSWindow *previousWindow)
1489 qCDebug(lcQpaWindow) <<
"Done moving" << m_view
1490 <<
"from" << previousWindow <<
"to" << m_view.window;
1492 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController)) {
1493 qCDebug(lcQpaWindow) <<
"Retaining new window controller" << qtWindowController;
1494 [qtWindowController retain];
1496 if (
auto *qtWindowController = qt_objc_cast<QNSWindowController*>(previousWindow.windowController)) {
1497 qCDebug(lcQpaWindow) <<
"Releasing old window controller" << qtWindowController;
1498 [qtWindowController autorelease];
1504 auto *previousScreen = previousWindow ? QCocoaScreen::get(previousWindow.screen) :
nullptr;
1505 auto *currentScreen = m_view.window ? QCocoaScreen::get(m_view.window.screen) :
nullptr;
1506 if (currentScreen && currentScreen != previousScreen)
1507 windowDidChangeScreen();
1517void QCocoaWindow::windowDidMove()
1519 if (!isContentView())
1522 handleGeometryChange();
1525 handleWindowStateChanged();
1528void QCocoaWindow::windowDidResize()
1530 if (!isContentView())
1533 handleGeometryChange();
1535 if (!m_view.inLiveResize)
1536 handleWindowStateChanged();
1539void QCocoaWindow::windowWillStartLiveResize()
1544 m_inLiveResize =
true;
1547bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1552 return !m_inLiveResize;
1555void QCocoaWindow::windowDidEndLiveResize()
1557 m_inLiveResize =
false;
1559 if (!isContentView())
1562 handleWindowStateChanged();
1565void QCocoaWindow::windowDidBecomeKey()
1569 if (m_view.window.firstResponder != m_view)
1572 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1573 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1575 if (windowIsPopupType()) {
1576 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1581 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1582 window(), Qt::ActiveWindowFocusReason);
1585void QCocoaWindow::windowDidResignKey()
1589 if (m_view.window.firstResponder != m_view)
1592 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1593 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1602 NSWindow *newKeyWindow = [NSApp keyWindow];
1603 if (newKeyWindow && newKeyWindow != m_view.window
1604 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1605 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1606 <<
"is Qt window. Deferring focus window change.";
1611 if (!windowIsPopupType()) {
1612 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1613 nullptr, Qt::ActiveWindowFocusReason);
1617void QCocoaWindow::windowDidOrderOnScreen()
1621 if (QWindowPrivate::get(window())->isPopup()) {
1622 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1623 (QGuiApplicationPrivate::currentMouseWindow);
1626 [m_view setNeedsDisplay:YES];
1629void QCocoaWindow::windowDidOrderOffScreen()
1631 handleExposeEvent(QRegion());
1634 if (window()->type() & Qt::Window) {
1635 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1636 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1637 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1638 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1640 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1641 (windowUnderMouse, windowPoint, screenPoint);
1647void QCocoaWindow::windowDidChangeOcclusionState()
1653 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1654 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1655 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1658 [m_view setNeedsDisplay:YES];
1660 handleExposeEvent(QRegion());
1663void QCocoaWindow::windowDidChangeScreen()
1669 NSScreen *nsScreen = m_view.window.screen;
1671 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1672 QCocoaScreen::updateScreens();
1674 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1675 auto *currentScreen = QCocoaScreen::get(nsScreen);
1677 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1690 QPointer<QCocoaWindow> destructionGuard =
this;
1691 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1692 window(), currentScreen ? currentScreen->screen() :
nullptr);
1693 if (!destructionGuard)
1696 if (currentScreen && hasPendingUpdateRequest()) {
1701 currentScreen->requestUpdate();
1706 previousScreen->maybeStopDisplayLink();
1711bool QCocoaWindow::windowShouldClose()
1713 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1725 [[m_view.window retain] autorelease];
1727 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1732void QCocoaWindow::handleGeometryChange()
1734 const QRect newGeometry = actualGeometry();
1736 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1737 <<
"current" << geometry() <<
"new" << newGeometry;
1742 if (m_inSetStyleMask && !m_view.window) {
1743 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1750 if (!m_initialized) {
1752 QPlatformWindow::setGeometry(newGeometry);
1753 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1757 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1760 updateSafeAreaMarginsIfNeeded();
1764 if (!m_inSetGeometry)
1765 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1768void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1781 if (m_view.window.visible && m_view.window.screen
1782 && !geometry().size().isEmpty() && !region.isEmpty()
1783 && !m_view.hiddenOrHasHiddenAncestor) {
1784 m_exposedRect = region.boundingRect();
1786 m_exposedRect = QRect();
1789 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1791 QPointer<QCocoaWindow> destructionGuard =
this;
1792 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1793 if (!destructionGuard)
1797 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1802bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1804 if (type == Qt::Widget)
1805 type = window()->type();
1806 if (type == Qt::Tool)
1809 return ((type & Qt::Popup) == Qt::Popup);
1813
1814
1815
1816
1817
1818
1819
1820
1821bool QCocoaWindow::isContentView()
const
1823 return m_view.window.contentView == m_view;
1827
1828
1829
1830
1831
1832
1833
1834void QCocoaWindow::recreateWindowIfNeeded()
1836 QMacAutoReleasePool pool;
1838 auto *parentWindow =
static_cast<QCocoaWindow *>(QPlatformWindow::parent());
1840 const bool shouldManageTopLevelWindow = !parentWindow
1841 && !isSubWindow(window()) && !isForeignWindow();
1843 if (shouldManageTopLevelWindow) {
1844 auto *windowController = qt_objc_cast<QNSWindowController*>(m_view.window.windowController);
1845 const bool isControllerContentView = m_view == windowController.window.contentView;
1846 const bool windowClassIsCorrect = [m_view.window isKindOfClass:windowClass()];
1848 if (!isControllerContentView || !windowClassIsCorrect) {
1849 qCDebug(lcQpaWindow) <<
"Creating new window controller for" << m_view;
1850 windowController = [[QNSWindowController alloc] initWithCocoaWindow:
this];
1852 m_view.postsFrameChangedNotifications = NO;
1853 windowController.window.contentView = m_view;
1854 m_view.postsFrameChangedNotifications = YES;
1857 [windowController release];
1859 qCDebug(lcQpaWindow) <<
"Re-using existing window controller" << windowController;
1861 }
else if (parentWindow) {
1862 if (m_view.superview != parentWindow->view()) {
1863 qCDebug(lcQpaWindow) <<
"Reparenting view to new parent" << parentWindow;
1864 [parentWindow->view() addSubview:m_view];
1866 }
else if (qnsview_cast(m_view.superview) && !isEmbedded()) {
1867 qCDebug(lcQpaWindow) <<
"Removing view from superview" << m_view.superview;
1868 [m_view removeFromSuperview];
1872Class QCocoaWindow::windowClass()
const
1874 const auto type = window()->type();
1875 return ((type & Qt::Popup) == Qt::Popup
1876 || (type & Qt::Dialog) == Qt::Dialog) ?
1877 QNSPanel.
class : QNSWindow.
class;
1880void QCocoaWindow::requestUpdate()
1882 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1883 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1885 if (updatesWithDisplayLink()) {
1886 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1887 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1888 QPlatformWindow::requestUpdate();
1892 QPlatformWindow::requestUpdate();
1896bool QCocoaWindow::updatesWithDisplayLink()
const
1899 return format().swapInterval() != 0;
1902void QCocoaWindow::deliverUpdateRequest()
1904 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1905 QScopedValueRollback<
bool> blocker(m_deliveringUpdateRequest,
true);
1907 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1912 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1913 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1914 <<
"due to" << qtMetalLayer <<
"needing display";
1920 qtMetalLayer.displayLock.unlock();
1923 QPlatformWindow::deliverUpdateRequest();
1926void QCocoaWindow::requestActivateWindow()
1928 QMacAutoReleasePool pool;
1929 [m_view.window makeFirstResponder:m_view];
1930 [m_view.window makeKeyWindow];
1934
1935
1936void QCocoaWindow::closeAllPopups()
1938 QGuiApplicationPrivate::instance()->closeAllPopups();
1940 removePopupMonitor();
1943void QCocoaWindow::removePopupMonitor()
1945 if (s_globalMouseMonitor) {
1946 [NSEvent removeMonitor:s_globalMouseMonitor];
1947 s_globalMouseMonitor = nil;
1949 if (s_applicationActivationObserver) {
1950 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1951 s_applicationActivationObserver = nil;
1955void QCocoaWindow::setupPopupMonitor()
1962 if (!s_globalMouseMonitor) {
1964 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1965 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1966 | NSEventMaskMouseMoved;
1967 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1968 handler:^(NSEvent *e){
1969 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1970 removePopupMonitor();
1973 const auto eventType = cocoaEvent2QtMouseEvent(e);
1974 if (eventType == QEvent::MouseMove) {
1975 if (s_windowUnderMouse) {
1976 QWindow *window = s_windowUnderMouse->window();
1977 const auto button = cocoaButton2QtButton(e);
1978 const auto buttons = currentlyPressedMouseButtons();
1979 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1980 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1981 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1982 buttons, button, eventType);
1995 if (!s_applicationActivationObserver) {
1996 s_applicationActivationObserver = [NSWorkspace.sharedWorkspace.notificationCenter
1997 addObserverForName:NSWorkspaceDidActivateApplicationNotification object:nil queue:NSOperationQueue.mainQueue
1998 usingBlock:^(NSNotification *notification) {
1999 NSRunningApplication *activatedApp = notification.userInfo[NSWorkspaceApplicationKey];
2000 if ([activatedApp isEqual:NSRunningApplication.currentApplication]) {
2001 [NSWorkspace.sharedWorkspace.notificationCenter
2002 removeObserver:s_applicationActivationObserver];
2003 s_applicationActivationObserver = nil;
2012QCocoaNSWindow *QCocoaWindow::createNSWindow()
2014 QMacAutoReleasePool pool;
2016 Qt::WindowType type = window()->type();
2017 Qt::WindowFlags flags = window()->flags();
2019 QRect rect = geometry();
2021 QScreen *targetScreen =
nullptr;
2022 for (QScreen *screen : QGuiApplication::screens()) {
2023 if (screen->handle()->geometry().contains(rect.topLeft())) {
2024 targetScreen = screen;
2029 NSWindowStyleMask styleMask = windowStyleMask(flags);
2031 if (!targetScreen) {
2032 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
2033 targetScreen = QGuiApplication::primaryScreen();
2037 styleMask = NSWindowStyleMaskBorderless;
2040 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
2041 rect.translate(-targetCocoaScreen->geometry().topLeft());
2042 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
2044 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
2049 if (styleMask && (contentRect.origin.y + 24 > targetCocoaScreen->geometry().width())) {
2050 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
2051 <<
"Adjusting style mask during creation";
2052 styleMask = NSWindowStyleMaskBorderless;
2057 QCocoaNSWindow *nsWindow = [[windowClass() alloc] initWithContentRect:contentRect
2062 backing:NSBackingStoreBuffered defer:NO
2063 screen:targetCocoaScreen->nativeScreen()
2064 platformWindow:
this];
2068 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
2073 if (!resultingScreen)
2074 resultingScreen = targetCocoaScreen;
2076 if (resultingScreen->screen() != window()->screen()) {
2077 QWindowSystemInterface::handleWindowScreenChanged<
2078 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
2081 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
2082 [](QNSWindowDelegate *delegate) { [delegate release]; });
2083 nsWindow.delegate = sharedDelegate.get();
2089 [nsWindow setReleasedWhenClosed:NO];
2091 if (alwaysShowToolWindow()) {
2092 static dispatch_once_t onceToken;
2093 dispatch_once(&onceToken, ^{
2094 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
2095 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2096 name:NSApplicationWillResignActiveNotification object:nil];
2097 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2098 name:NSApplicationWillBecomeActiveNotification object:nil];
2102 nsWindow.restorable = NO;
2103 nsWindow.level = windowLevel(flags);
2104 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
2106 if ([nsWindow isKindOfClass:NSPanel.
class]) {
2108 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
2111 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
2112 | NSWindowCollectionBehaviorMoveToActiveSpace;
2114 if ((type & Qt::Popup) == Qt::Popup) {
2115 nsWindow.hasShadow = YES;
2116 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
2117 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
2118 setupPopupMonitor();
2129 if (
auto *qtView = qnsview_cast(m_view))
2130 nsWindow.colorSpace = qtView.colorSpace;
2135bool QCocoaWindow::alwaysShowToolWindow()
const
2137 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2140bool QCocoaWindow::setWindowModified(
bool modified)
2142 QMacAutoReleasePool pool;
2144 if (!isContentView())
2147 m_view.window.documentEdited = modified;
2151void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2156QCocoaMenuBar *QCocoaWindow::menubar()
const
2161void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2163 QMacAutoReleasePool pool;
2166 if (isForeignWindow())
2169 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2171 QNSView *view = qnsview_cast(m_view);
2172 if (cursor == view.cursor)
2175 view.cursor = cursor;
2181 [m_view.window invalidateCursorRectsForView:m_view];
2188 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2189 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2190 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2191 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2192 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2193 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2194 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2195 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2196 location:locationInWindow modifierFlags:0 timestamp:0
2197 windowNumber:m_view.window.windowNumber context:nil
2198 eventNumber:0 trackingNumber:0 userData:0]];
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212void QCocoaWindow::enableTrackpadTouchDelivery(
bool enable)
2214 m_registeredTrackpadTouchConsumers += enable ? 1 : -1;
2215 updateAllowedTouchTypes();
2218void QCocoaWindow::updateAllowedTouchTypes()
2220 bool allowingTrackpadTouch = m_view.allowedTouchTypes & NSTouchTypeMaskIndirect;
2221 if (m_registeredTrackpadTouchConsumers > 0 && !allowingTrackpadTouch) {
2222 qCDebug(lcQpaTouch) <<
"Enabling trackpad touches for" << m_view;
2223 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2224 }
else if (!m_registeredTrackpadTouchConsumers && allowingTrackpadTouch) {
2225 const auto devices = QInputDevice::devices();
2226 for (
const auto *device : devices) {
2227 if (device->type() != QInputDevice::DeviceType::TouchPad)
2229 const auto *pointingDevice =
static_cast<
const QPointingDevice *>(device);
2230 const auto *devicePriv = QPointingDevicePrivate::get(pointingDevice);
2231 for (
const auto &[pointId, point] : std::as_const(devicePriv->activePoints)) {
2232 if (QMutableEventPoint::window(point.eventPoint) == window()) {
2233 qCDebug(lcQpaTouch) <<
"Deferring allowed touch type update for"
2234 << m_view <<
"due to still active" << point.eventPoint;
2239 qCDebug(lcQpaTouch) <<
"Disabling trackpad touches for" << m_view;
2240 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2244qreal QCocoaWindow::devicePixelRatio()
const
2251 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2252 return backingSize.height;
2255QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2257 QWindow *targetWindow = window();
2258 for (QObject *child : targetWindow->children())
2259 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2260 if (QPlatformWindow *handle = childWindow->handle())
2261 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2262 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2264 return targetWindow;
2267bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2272 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2282 QWindow *modalWindow =
nullptr;
2283 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2284 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2285 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2286 <<
"blocked by" << modalWindow;
2291 if (m_inSetVisible) {
2292 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2293 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2300bool QCocoaWindow::windowEvent(QEvent *event)
2302 switch (event->type()) {
2303 case QEvent::WindowBlocked:
2304 case QEvent::WindowUnblocked:
2305 updateTitleBarButtons(window()->flags());
2311 return QPlatformWindow::windowEvent(event);
2314QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2318 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2319 : NSMakePoint(0, 0);
2320 const NSRect visibleRect = [m_view visibleRect];
2322 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2325QMargins QCocoaWindow::frameMargins()
const
2327 QMacAutoReleasePool pool;
2332 if (QPlatformWindow::parent())
2338 if (m_view.window) {
2339 frameRect = m_view.window.frame;
2340 contentRect = [m_view.window contentRectForFrameRect:frameRect];
2342 contentRect = m_view.frame;
2343 frameRect = [NSWindow frameRectForContentRect:contentRect styleMask:windowStyleMask(window()->flags())];
2346 return QMargins(frameRect.origin.x - contentRect.origin.x,
2347 (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height),
2348 (frameRect.origin.x + frameRect.size.width) - (contentRect.origin.x + contentRect.size.width),
2349 contentRect.origin.y - contentRect.origin.y);
2352void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2354 m_frameStrutEventsEnabled = enabled;
2357QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2359 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2360 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2361 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2364QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2366 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2367 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2368 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2371CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2373 if (!referenceView || referenceView.flipped)
2374 return point.toCGPoint();
2376 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2379CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2381 if (!referenceView || referenceView.flipped)
2382 return rect.toCGRect();
2384 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2387QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2389 if (!referenceView || referenceView.flipped)
2390 return QPointF::fromCGPoint(point);
2392 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2395QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2397 if (!referenceView || referenceView.flipped)
2398 return QRectF::fromCGRect(rect);
2400 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2403CALayer *QCocoaWindow::contentLayer()
const
2405 auto *layer = m_view.layer;
2406 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2407 layer = containerLayer.contentLayer;
2411void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2412 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2413 NSVisualEffectState activationState)
2415 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2416 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2417 <<
"in views without a container layer";
2421 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2422 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2423 << blendMode <<
"and activation state" << activationState;
2425 NSVisualEffectView *effectView =
nullptr;
2426 if (m_effectViews.contains(identifier)) {
2427 effectView = m_effectViews.value(identifier);
2428 if (rect.isEmpty()) {
2429 [effectView removeFromSuperview];
2430 m_effectViews.remove(identifier);
2433 }
else if (!rect.isEmpty()) {
2434 effectView = [NSVisualEffectView
new];
2437 effectView.wantsLayer = YES;
2438 effectView.layer.zPosition = -FLT_MAX;
2439 [m_view addSubview:effectView];
2440 m_effectViews.insert(identifier, effectView);
2446 effectView.frame = rect.toCGRect();
2447 effectView.material = material;
2448 effectView.blendingMode = blendMode;
2449 effectView.state = activationState;
2452#ifndef QT_NO_DEBUG_STREAM
2453QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2455 QDebugStateSaver saver(debug);
2457 debug <<
"QCocoaWindow(" << (
const void *)window;
2459 debug <<
", window=" << window->window();
2467#if QT_CONFIG(accessibility)
2469static void setAccessibilityParent(NSView *view, id accessibilityParent)
2475 for (id<NSAccessibility> element in NSAccessibilityUnignoredChildrenForOnlyChild(view)) {
2477 if (![element respondsToSelector:@selector(setAccessibilityParent:)])
2480 if (element.accessibilityParent == accessibilityParent)
2483 qCDebug(lcQpaAccessibility) <<
"Setting a11y parent of" << element
2484 <<
"for" << view <<
"to" << accessibilityParent;
2485 element.accessibilityParent = accessibilityParent;
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503void QCocoaWindow::updateAccessibleParent()
2505 if (!isForeignWindow())
2508 QObject *accessibleParent = QWindowPrivate::get(window())->accessibleParent;
2509 if (QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(accessibleParent)) {
2510 setAccessibilityParent(m_view, NSAccessibilityUnignoredAncestor(
2511 [QMacAccessibilityElement elementWithInterface:iface]));
2512 m_didOverrideAccessibleParent =
true;
2514 clearAccessibleParent();
2518void QCocoaWindow::clearAccessibleParent()
2520 if (!isForeignWindow() || !m_didOverrideAccessibleParent)
2529 setAccessibilityParent(m_view, nil);
2531 m_didOverrideAccessibleParent =
false;
2540#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")