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);
1992 if (!s_applicationActivationObserver) {
1993 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1994 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1995 object:nil queue:nil
1996 usingBlock:^(NSNotification *){
2002QCocoaNSWindow *QCocoaWindow::createNSWindow()
2004 QMacAutoReleasePool pool;
2006 Qt::WindowType type = window()->type();
2007 Qt::WindowFlags flags = window()->flags();
2009 QRect rect = geometry();
2011 QScreen *targetScreen =
nullptr;
2012 for (QScreen *screen : QGuiApplication::screens()) {
2013 if (screen->handle()->geometry().contains(rect.topLeft())) {
2014 targetScreen = screen;
2019 NSWindowStyleMask styleMask = windowStyleMask(flags);
2021 if (!targetScreen) {
2022 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
2023 targetScreen = QGuiApplication::primaryScreen();
2027 styleMask = NSWindowStyleMaskBorderless;
2030 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
2031 rect.translate(-targetCocoaScreen->geometry().topLeft());
2032 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
2034 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
2039 if (styleMask && (contentRect.origin.y + 24 > targetCocoaScreen->geometry().width())) {
2040 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
2041 <<
"Adjusting style mask during creation";
2042 styleMask = NSWindowStyleMaskBorderless;
2047 QCocoaNSWindow *nsWindow = [[windowClass() alloc] initWithContentRect:contentRect
2052 backing:NSBackingStoreBuffered defer:NO
2053 screen:targetCocoaScreen->nativeScreen()
2054 platformWindow:
this];
2058 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
2063 if (!resultingScreen)
2064 resultingScreen = targetCocoaScreen;
2066 if (resultingScreen->screen() != window()->screen()) {
2067 QWindowSystemInterface::handleWindowScreenChanged<
2068 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
2071 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
2072 [](QNSWindowDelegate *delegate) { [delegate release]; });
2073 nsWindow.delegate = sharedDelegate.get();
2079 [nsWindow setReleasedWhenClosed:NO];
2081 if (alwaysShowToolWindow()) {
2082 static dispatch_once_t onceToken;
2083 dispatch_once(&onceToken, ^{
2084 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
2085 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2086 name:NSApplicationWillResignActiveNotification object:nil];
2087 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
2088 name:NSApplicationWillBecomeActiveNotification object:nil];
2092 nsWindow.restorable = NO;
2093 nsWindow.level = windowLevel(flags);
2094 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
2096 if ([nsWindow isKindOfClass:NSPanel.
class]) {
2098 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
2101 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
2102 | NSWindowCollectionBehaviorMoveToActiveSpace;
2104 if ((type & Qt::Popup) == Qt::Popup) {
2105 nsWindow.hasShadow = YES;
2106 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
2107 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
2108 setupPopupMonitor();
2119 if (
auto *qtView = qnsview_cast(m_view))
2120 nsWindow.colorSpace = qtView.colorSpace;
2125bool QCocoaWindow::alwaysShowToolWindow()
const
2127 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
2130bool QCocoaWindow::setWindowModified(
bool modified)
2132 QMacAutoReleasePool pool;
2134 if (!isContentView())
2137 m_view.window.documentEdited = modified;
2141void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
2146QCocoaMenuBar *QCocoaWindow::menubar()
const
2151void QCocoaWindow::setWindowCursor(NSCursor *cursor)
2153 QMacAutoReleasePool pool;
2156 if (isForeignWindow())
2159 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
2161 QNSView *view = qnsview_cast(m_view);
2162 if (cursor == view.cursor)
2165 view.cursor = cursor;
2171 [m_view.window invalidateCursorRectsForView:m_view];
2178 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2179 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2180 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2181 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2182 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2183 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2184 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2185 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2186 location:locationInWindow modifierFlags:0 timestamp:0
2187 windowNumber:m_view.window.windowNumber context:nil
2188 eventNumber:0 trackingNumber:0 userData:0]];
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202void QCocoaWindow::enableTrackpadTouchDelivery(
bool enable)
2204 m_registeredTrackpadTouchConsumers += enable ? 1 : -1;
2205 updateAllowedTouchTypes();
2208void QCocoaWindow::updateAllowedTouchTypes()
2210 bool allowingTrackpadTouch = m_view.allowedTouchTypes & NSTouchTypeMaskIndirect;
2211 if (m_registeredTrackpadTouchConsumers > 0 && !allowingTrackpadTouch) {
2212 qCDebug(lcQpaTouch) <<
"Enabling trackpad touches for" << m_view;
2213 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2214 }
else if (!m_registeredTrackpadTouchConsumers && allowingTrackpadTouch) {
2215 const auto devices = QInputDevice::devices();
2216 for (
const auto *device : devices) {
2217 if (device->type() != QInputDevice::DeviceType::TouchPad)
2219 const auto *pointingDevice =
static_cast<
const QPointingDevice *>(device);
2220 const auto *devicePriv = QPointingDevicePrivate::get(pointingDevice);
2221 for (
const auto &[pointId, point] : std::as_const(devicePriv->activePoints)) {
2222 if (QMutableEventPoint::window(point.eventPoint) == window()) {
2223 qCDebug(lcQpaTouch) <<
"Deferring allowed touch type update for"
2224 << m_view <<
"due to still active" << point.eventPoint;
2229 qCDebug(lcQpaTouch) <<
"Disabling trackpad touches for" << m_view;
2230 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2234qreal QCocoaWindow::devicePixelRatio()
const
2241 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2242 return backingSize.height;
2245QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2247 QWindow *targetWindow = window();
2248 for (QObject *child : targetWindow->children())
2249 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2250 if (QPlatformWindow *handle = childWindow->handle())
2251 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2252 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2254 return targetWindow;
2257bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2262 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2272 QWindow *modalWindow =
nullptr;
2273 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2274 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2275 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2276 <<
"blocked by" << modalWindow;
2281 if (m_inSetVisible) {
2282 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2283 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2290bool QCocoaWindow::windowEvent(QEvent *event)
2292 switch (event->type()) {
2293 case QEvent::WindowBlocked:
2294 case QEvent::WindowUnblocked:
2295 updateTitleBarButtons(window()->flags());
2301 return QPlatformWindow::windowEvent(event);
2304QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2308 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2309 : NSMakePoint(0, 0);
2310 const NSRect visibleRect = [m_view visibleRect];
2312 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2315QMargins QCocoaWindow::frameMargins()
const
2317 QMacAutoReleasePool pool;
2322 if (QPlatformWindow::parent())
2328 if (m_view.window) {
2329 frameRect = m_view.window.frame;
2330 contentRect = [m_view.window contentRectForFrameRect:frameRect];
2332 contentRect = m_view.frame;
2333 frameRect = [NSWindow frameRectForContentRect:contentRect styleMask:windowStyleMask(window()->flags())];
2336 return QMargins(frameRect.origin.x - contentRect.origin.x,
2337 (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height),
2338 (frameRect.origin.x + frameRect.size.width) - (contentRect.origin.x + contentRect.size.width),
2339 contentRect.origin.y - contentRect.origin.y);
2342void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2344 m_frameStrutEventsEnabled = enabled;
2347QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2349 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2350 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2351 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2354QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2356 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2357 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2358 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2361CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2363 if (!referenceView || referenceView.flipped)
2364 return point.toCGPoint();
2366 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2369CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2371 if (!referenceView || referenceView.flipped)
2372 return rect.toCGRect();
2374 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2377QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2379 if (!referenceView || referenceView.flipped)
2380 return QPointF::fromCGPoint(point);
2382 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2385QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2387 if (!referenceView || referenceView.flipped)
2388 return QRectF::fromCGRect(rect);
2390 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2393CALayer *QCocoaWindow::contentLayer()
const
2395 auto *layer = m_view.layer;
2396 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2397 layer = containerLayer.contentLayer;
2401void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2402 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2403 NSVisualEffectState activationState)
2405 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2406 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2407 <<
"in views without a container layer";
2411 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2412 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2413 << blendMode <<
"and activation state" << activationState;
2415 NSVisualEffectView *effectView =
nullptr;
2416 if (m_effectViews.contains(identifier)) {
2417 effectView = m_effectViews.value(identifier);
2418 if (rect.isEmpty()) {
2419 [effectView removeFromSuperview];
2420 m_effectViews.remove(identifier);
2423 }
else if (!rect.isEmpty()) {
2424 effectView = [NSVisualEffectView
new];
2427 effectView.wantsLayer = YES;
2428 effectView.layer.zPosition = -FLT_MAX;
2429 [m_view addSubview:effectView];
2430 m_effectViews.insert(identifier, effectView);
2436 effectView.frame = rect.toCGRect();
2437 effectView.material = material;
2438 effectView.blendingMode = blendMode;
2439 effectView.state = activationState;
2442#ifndef QT_NO_DEBUG_STREAM
2443QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2445 QDebugStateSaver saver(debug);
2447 debug <<
"QCocoaWindow(" << (
const void *)window;
2449 debug <<
", window=" << window->window();
2457#if QT_CONFIG(accessibility)
2459static void setAccessibilityParent(NSView *view, id accessibilityParent)
2465 for (id<NSAccessibility> element in NSAccessibilityUnignoredChildrenForOnlyChild(view)) {
2467 if (![element respondsToSelector:@selector(setAccessibilityParent:)])
2470 if (element.accessibilityParent == accessibilityParent)
2473 qCDebug(lcQpaAccessibility) <<
"Setting a11y parent of" << element
2474 <<
"for" << view <<
"to" << accessibilityParent;
2475 element.accessibilityParent = accessibilityParent;
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493void QCocoaWindow::updateAccessibleParent()
2495 if (!isForeignWindow())
2498 QObject *accessibleParent = QWindowPrivate::get(window())->accessibleParent;
2499 if (QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(accessibleParent)) {
2500 setAccessibilityParent(m_view, NSAccessibilityUnignoredAncestor(
2501 [QMacAccessibilityElement elementWithInterface:iface]));
2502 m_didOverrideAccessibleParent =
true;
2504 clearAccessibleParent();
2508void QCocoaWindow::clearAccessibleParent()
2510 if (!isForeignWindow() || !m_didOverrideAccessibleParent)
2519 setAccessibilityParent(m_view, nil);
2521 m_didOverrideAccessibleParent =
false;
2530#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")