5#include <AppKit/AppKit.h>
6#include <QuartzCore/QuartzCore.h>
20#include <QtCore/qfileinfo.h>
21#include <QtCore/private/qcore_mac_p.h>
23#include <private/qwindow_p.h>
24#include <qpa/qwindowsysteminterface.h>
25#include <qpa/qplatformscreen.h>
26#include <QtGui/private/qcoregraphics_p.h>
27#include <QtGui/private/qhighdpiscaling_p.h>
28#include <QtGui/private/qmetallayer_p.h>
37 defaultWindowWidth = 160,
38 defaultWindowHeight = 160
47 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
49 const QMetaObject *metaObject = QMetaType(qRegisterMetaType<
QCocoaWindow*>()).metaObject();
52 for (
int i = 0; i < metaObject->methodCount(); ++i) {
53 QMetaMethod method = metaObject->method(i);
54 const QString methodTag = QString::fromLatin1(method.tag());
55 if (!methodTag.startsWith(notificationHandlerPrefix))
58 const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
59 [center addObserverForName:notificationName.toNSString() object:nil queue:nil
60 usingBlock:^(NSNotification *notification) {
62 QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
63 if ([notification.object isKindOfClass:[NSWindow
class]]) {
64 NSWindow *nsWindow = notification.object;
65 for (
const QWindow *window : QGuiApplication::allWindows()) {
66 if (QCocoaWindow *cocoaWindow =
static_cast<QCocoaWindow *>(window->handle()))
67 if (cocoaWindow->nativeWindow() == nsWindow)
68 cocoaWindows += cocoaWindow;
70 }
else if ([notification.object isKindOfClass:[NSView
class]]) {
71 if (QNSView *qnsView = qnsview_cast(notification.object))
72 cocoaWindows += qnsView.platformWindow;
74 qCWarning(lcCocoaNotifications) <<
"Unhandled notification"
75 << notification.name <<
"for" << notification.object;
79 if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) {
80 QVector<QCocoaWindow *> debugWindows;
81 for (QCocoaWindow *cocoaWindow : cocoaWindows)
82 debugWindows += cocoaWindow;
83 qCDebug(lcCocoaNotifications) <<
"Forwarding" << qPrintable(notificationName) <<
89 for (QCocoaWindow *cocoaWindow : cocoaWindows) {
90 if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
91 qCWarning(lcQpaWindow) <<
"Failed to invoke NSNotification callback for"
92 << notification.name <<
"on" << cocoaWindow;
98Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
103QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) : QPlatformWindow(win)
105 qCDebug(lcQpaWindow) <<
"QCocoaWindow::QCocoaWindow" << window();
108 m_view =
reinterpret_cast<NSView *>(nativeHandle);
115 qCDebug(lcQpaWindow) <<
"QCocoaWindow::initialize" << window();
117 QMacAutoReleasePool pool;
120 m_view = [[QNSView alloc] initWithCocoaWindow:
this];
129 auto initialGeometry = QPlatformWindow::initialGeometry(window(),
130 windowGeometry(), defaultWindowWidth, defaultWindowHeight);
131 QPlatformWindow::d_ptr->rect = QRect();
132 setGeometry(initialGeometry);
134 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
136 m_safeAreaInsetsObserver = QMacKeyValueObserver(
137 m_view, @
"safeAreaInsets", [
this] {
140 QMetaObject::invokeMethod(
this, [
this]{
141 updateSafeAreaMarginsIfNeeded();
142 }, Qt::QueuedConnection);
143 }, NSKeyValueObservingOptionNew);
147 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
159 qCDebug(lcQpaWindow) <<
"QCocoaWindow::~QCocoaWindow" << window();
161 QMacAutoReleasePool pool;
162 [m_nsWindow makeFirstResponder:nil];
163 [m_nsWindow setContentView:nil];
165 m_safeAreaInsetsObserver = {};
169 if (QPlatformWindow::parent())
170 [m_view removeFromSuperview];
175 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
178 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
179 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
181 vulcanInstance->destroySurface(m_vulkanSurface);
188 [NSNotificationCenter.defaultCenter
189 postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
193 [m_nsWindow closeAndRelease];
198 static_cast<
QCocoaScreen *>(screen())->maybeStopDisplayLink();
203 auto format = window()->requestedFormat();
204 if (
auto *view = qnsview_cast(m_view); view.colorSpace) {
205 auto colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(view.colorSpace.ICCProfileData));
206 if (!colorSpace.isValid()) {
207 qCWarning(lcQpaWindow) <<
"Failed to parse ICC profile for" << view.colorSpace
208 <<
"with ICC data" << view.colorSpace.ICCProfileData;
210 format.setColorSpace(colorSpace);
217 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setGeometry" << window() << rectIn;
224 if (qt_window_private(
const_cast<QWindow *>(window()))->positionPolicy
225 == QWindowPrivate::WindowFrameInclusive) {
226 const QMargins margins = frameMargins();
227 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
230 setCocoaGeometry(rect);
235 return ![m_view isKindOfClass:[QNSView
class]];
244 NSPoint windowPoint = [m_view convertPoint:NSMakePoint(0, 0) toView:nil];
245 NSRect screenRect = [[m_view window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)];
246 NSPoint screenPoint = screenRect.origin;
247 QPoint position = QCocoaScreen::mapFromNative(screenPoint).toPoint();
248 QSize size = QRectF::fromCGRect(NSRectToCGRect([m_view bounds])).toRect().size();
249 return QRect(position, size);
252 return QPlatformWindow::geometry();
256
257
258
259
260
261
262
272 if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
275 return m_normalGeometry;
283 if (windowState() != Qt::WindowNoState)
286 m_normalGeometry = geometry();
291 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setCocoaGeometry" << window() << rect;
292 QMacAutoReleasePool pool;
294 QPlatformWindow::setGeometry(rect);
298 [m_view setFrame:NSMakeRect(0, 0, rect.width(), rect.height())];
304 NSRect bounds = QCocoaScreen::mapToNative(rect);
305 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
307 m_view.frame = QCocoaWindow::mapToNative(rect, m_view.superview);
315 QMacAutoReleasePool pool;
322 QMarginsF viewSafeAreaMargins = {
323 m_view.safeAreaInsets.left,
324 m_view.safeAreaInsets.top,
325 m_view.safeAreaInsets.right,
326 m_view.safeAreaInsets.bottom
333 auto screenRect = m_view.window.screen.frame;
334 auto screenInsets = m_view.window.screen.safeAreaInsets;
335 auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect(
336 NSMinX(screenRect) + screenInsets.left,
337 NSMinY(screenRect) + screenInsets.bottom,
338 NSWidth(screenRect) - screenInsets.left - screenInsets.right,
339 NSHeight(screenRect) - screenInsets.top - screenInsets.bottom
342 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
343 [m_view.window convertRectToScreen:
344 [m_view convertRect:m_view.bounds toView:nil]]
350 QMarginsF screenSafeAreaMargins = {
351 qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left),
352 qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top),
353 qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right),
354 qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom)
357 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
362 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
363 m_lastReportedSafeAreaMargins = safeAreaMargins();
370 switch (NSApp.currentEvent.type) {
371 case NSEventTypeLeftMouseDown:
372 case NSEventTypeRightMouseDown:
373 case NSEventTypeOtherMouseDown:
374 case NSEventTypeMouseMoved:
375 case NSEventTypeLeftMouseDragged:
376 case NSEventTypeRightMouseDragged:
377 case NSEventTypeOtherMouseDragged:
380 [m_view.window performWindowDragWithEvent:NSApp.currentEvent];
389 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setVisible" << window() << visible;
402 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping setting visibility";
406 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
407 qCDebug(lcQpaWindow) <<
"No change in visible status. Ignoring.";
412 qCWarning(lcQpaWindow) <<
"Already setting window visible!";
416 QScopedValueRollback<
bool> rollback(m_inSetVisible,
true);
418 QMacAutoReleasePool pool;
420 if (window()->transientParent())
421 parentCocoaWindow =
static_cast<
QCocoaWindow *>(window()->transientParent()->handle());
423 auto eventDispatcher = [] {
424 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
437 if (parentCocoaWindow) {
440 setGeometry(windowGeometry());
442 if (window()->type() == Qt::Popup) {
445 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
446 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
447 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
448 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
449 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
458 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
462 applyWindowState(window()->windowStates());
464 if (window()->windowState() != Qt::WindowMinimized) {
465 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
467 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
468 if (!nativeParentWindow.attachedSheet)
469 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
471 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
472 }
else if (window()->modality() == Qt::ApplicationModal) {
474 eventDispatcher()->beginModalSession(window());
475 }
else if (m_view.window.canBecomeKeyWindow) {
476 bool shouldBecomeKeyNow = !NSApp.modalWindow
477 || m_view.window.worksWhenModal
478 || !NSApp.modalWindow.visible;
482 if ([m_view.window isKindOfClass:[NSPanel
class]])
483 shouldBecomeKeyNow &= !(
static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
485 if (shouldBecomeKeyNow)
486 [m_view.window makeKeyAndOrderFront:nil];
488 [m_view.window orderFront:nil];
490 [m_view.window orderFront:nil];
497 if (eventDispatcher()->hasModalSession())
498 eventDispatcher()->endModalSession(window());
499 else if ([m_view.window isSheet])
500 [m_view.window.sheetParent endSheet:m_view.window];
507 [m_view.window orderOut:nil];
509 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
514 NSWindow *mainWindow = [NSApp mainWindow];
515 if (mainWindow && [mainWindow canBecomeKeyWindow])
516 [mainWindow makeKeyWindow];
530 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
531 && m_view.nextValidKeyView.window != m_view.window) {
532 qCDebug(lcQpaWindow) <<
"Detected nextValidKeyView" << m_view.nextValidKeyView
533 <<
"in different window" << m_view.nextValidKeyView.window
534 <<
"Resetting" << m_view.window <<
"first responder to nil.";
535 [m_view.window makeFirstResponder:nil];
540 if (parentCocoaWindow && window()->type() == Qt::Popup) {
541 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
542 if (m_resizableTransientParent
543 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
545 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
550NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
552 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
554 NSInteger windowLevel = NSNormalWindowLevel;
556 if (type == Qt::Tool)
557 windowLevel = NSFloatingWindowLevel;
558 else if ((type & Qt::Popup) == Qt::Popup)
559 windowLevel = NSPopUpMenuWindowLevel;
562 if (flags & Qt::WindowStaysOnTopHint)
563 windowLevel = NSModalPanelWindowLevel;
565 if (type == Qt::ToolTip)
566 windowLevel = NSScreenSaverWindowLevel;
568 auto *transientParent = window()->transientParent();
569 if (transientParent && transientParent->handle()) {
589 auto *transientCocoaWindow =
static_cast<QCocoaWindow *>(transientParent->handle());
590 auto *nsWindow = transientCocoaWindow->nativeWindow();
595 if (type != Qt::Window)
596 windowLevel = qMax(windowLevel, nsWindow.level);
602NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
604 const Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
610 NSUInteger styleMask = [&]{
612 if (flags & Qt::FramelessWindowHint)
613 return NSWindowStyleMaskBorderless;
616 if (windowIsPopupType(type))
617 return NSWindowStyleMaskBorderless;
619 if (flags & Qt::CustomizeWindowHint) {
622 return flags & Qt::WindowTitleHint
623 ? NSWindowStyleMaskTitled
624 : NSWindowStyleMaskBorderless;
627 return NSWindowStyleMaskTitled;
636 styleMask |= NSWindowStyleMaskClosable
637 | NSWindowStyleMaskMiniaturizable;
639 if (type != Qt::Popup)
640 styleMask |= NSWindowStyleMaskResizable;
642 if (type == Qt::Tool)
643 styleMask |= NSWindowStyleMaskUtilityWindow;
646 if (m_drawContentBorderGradient)
647 styleMask |= QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground);
649 if (flags & Qt::ExpandedClientAreaHint)
650 styleMask |= NSWindowStyleMaskFullSizeContentView;
653 styleMask |= (m_view.window.styleMask & (
654 NSWindowStyleMaskFullScreen
655 | NSWindowStyleMaskUnifiedTitleAndToolbar
656 | NSWindowStyleMaskDocModalWindow
657 | NSWindowStyleMaskNonactivatingPanel
658 | NSWindowStyleMaskHUDWindow));
663bool QCocoaWindow::isFixedSize()
const
665 return windowMinimumSize().isValid() && windowMaximumSize().isValid()
666 && windowMinimumSize() == windowMaximumSize();
669void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
671 if (!isContentView())
674 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
675 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
676 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
677 { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
680 bool hideButtons =
true;
681 for (
const auto &[button, buttonHint] : buttons) {
684 if (button == NSWindowMiniaturizeButton)
685 enabled = window()->type() != Qt::Dialog;
688 if (windowFlags & Qt::CustomizeWindowHint)
689 enabled = windowFlags & buttonHint;
693 if (button == NSWindowZoomButton && isFixedSize())
699 if (button == NSWindowCloseButton && enabled
700 && QWindowPrivate::get(window())->blockedByModalWindow) {
707 [m_view.window standardWindowButton:button].enabled = enabled;
708 hideButtons &= !enabled;
712 for (
const auto &[button, buttonHint] : buttons)
713 [m_view.window standardWindowButton:button].hidden = hideButtons;
716void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
722 QMacAutoReleasePool pool;
724 if (!isContentView())
729 m_inSetStyleMask =
true;
730 m_view.window.styleMask = windowStyleMask(flags);
731 m_inSetStyleMask =
false;
733 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
734 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
735 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
736 const bool enableFullScreen = m_view.window.qt_fullScreen
737 || !(flags & Qt::CustomizeWindowHint)
738 || (flags & Qt::WindowFullscreenButtonHint);
739 if (enableFullScreen) {
740 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
741 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
743 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
744 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
746 m_view.window.collectionBehavior = behavior;
751 m_view.window.level =
this->windowLevel(flags);
753 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
755 if (!(flags & Qt::FramelessWindowHint))
756 setWindowTitle(window()->title());
758 updateTitleBarButtons(flags);
767 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
768 if (m_view.window.ignoresMouseEvents != ignoreMouse)
769 m_view.window.ignoresMouseEvents = ignoreMouse;
772 m_view.window.titlebarAppearsTransparent = (flags & Qt::NoTitleBarBackgroundHint)
773 || (m_view.window.styleMask & QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground));
779
780
781
782
783
784
785void QCocoaWindow::setWindowState(Qt::WindowStates state)
787 if (window()->isVisible())
788 applyWindowState(state);
791void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
793 if (!isContentView())
796 const Qt::WindowState currentState = QWindowPrivate::effectiveState(windowState());
797 const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
799 if (newState == currentState)
802 qCDebug(lcQpaWindow) <<
"Applying" << newState <<
"to" << window() <<
"in" << currentState;
804 const NSSize contentSize = m_view.frame.size;
805 if (contentSize.width <= 0 || contentSize.height <= 0) {
808 qWarning(
"invalid window content view size, check your window geometry");
809 handleWindowStateChanged(HandleUnconditionally);
813 const NSWindow *nsWindow = m_view.window;
815 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
816 && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
817 qWarning() << window()->type() <<
"windows cannot be made" << newState;
818 handleWindowStateChanged(HandleUnconditionally);
822 const id sender = nsWindow;
825 switch (currentState) {
826 case Qt::WindowMinimized:
827 [nsWindow deminiaturize:sender];
832 case Qt::WindowFullScreen: {
843 if (newState == windowState())
847 case Qt::WindowFullScreen:
850 case Qt::WindowMaximized:
853 case Qt::WindowMinimized:
854 [nsWindow miniaturize:sender];
856 case Qt::WindowNoState:
857 if (windowState() == Qt::WindowMaximized)
865Qt::WindowStates QCocoaWindow::windowState()
const
867 Qt::WindowStates states = Qt::WindowNoState;
868 NSWindow *window = m_view.window;
870 if (window.miniaturized)
871 states |= Qt::WindowMinimized;
875 if (window.qt_fullScreen) {
876 states |= Qt::WindowFullScreen;
877 }
else if ((window.zoomed && !isTransitioningToFullScreen())
878 || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) {
879 states |= Qt::WindowMaximized;
888void QCocoaWindow::toggleMaximized()
890 const NSWindow *window = m_view.window;
894 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
895 window.styleMask |= NSWindowStyleMaskResizable;
897 const id sender = window;
898 [window zoom:sender];
901 window.styleMask &= ~NSWindowStyleMaskResizable;
904void QCocoaWindow::windowWillZoom()
906 updateNormalGeometry();
909void QCocoaWindow::toggleFullScreen()
911 const NSWindow *window = m_view.window;
916 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
918 const id sender = window;
919 [window toggleFullScreen:sender];
922void QCocoaWindow::windowWillEnterFullScreen()
924 if (!isContentView())
927 updateNormalGeometry();
932 m_view.window.styleMask |= NSWindowStyleMaskResizable;
935bool QCocoaWindow::isTransitioningToFullScreen()
const
937 NSWindow *window = m_view.window;
938 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
941void QCocoaWindow::windowDidEnterFullScreen()
943 if (!isContentView())
946 Q_ASSERT_X(m_view.window.qt_fullScreen,
"QCocoaWindow",
947 "FullScreen category processes window notifications first");
950 setWindowFlags(window()->flags());
952 handleWindowStateChanged();
955void QCocoaWindow::windowWillExitFullScreen()
957 if (!isContentView())
962 m_view.window.styleMask |= NSWindowStyleMaskResizable;
965void QCocoaWindow::windowDidExitFullScreen()
967 if (!isContentView())
970 Q_ASSERT_X(!m_view.window.qt_fullScreen,
"QCocoaWindow",
971 "FullScreen category processes window notifications first");
974 setWindowFlags(window()->flags());
976 Qt::WindowState requestedState = window()->windowState();
979 handleWindowStateChanged();
981 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
984 applyWindowState(requestedState);
988void QCocoaWindow::windowDidMiniaturize()
990 if (!isContentView())
993 handleWindowStateChanged();
996void QCocoaWindow::windowDidDeminiaturize()
998 if (!isContentView())
1001 Qt::WindowState requestedState = window()->windowState();
1003 handleWindowStateChanged();
1005 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
1008 applyWindowState(requestedState);
1012void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
1014 Qt::WindowStates currentState = windowState();
1015 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
1018 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleWindowStateChanged" <<
1019 m_lastReportedWindowState <<
"-->" << currentState;
1021 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
1022 window(), currentState, m_lastReportedWindowState);
1023 m_lastReportedWindowState = currentState;
1028void QCocoaWindow::setWindowTitle(
const QString &title)
1030 QMacAutoReleasePool pool;
1032 if (!isContentView())
1035 m_view.window.title = title.toNSString();
1037 if (title.isEmpty() && !window()->filePath().isEmpty()) {
1039 setWindowFilePath(window()->filePath());
1043void QCocoaWindow::setWindowFilePath(
const QString &filePath)
1045 QMacAutoReleasePool pool;
1047 if (!isContentView())
1050 if (window()->title().isNull())
1051 [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
1053 m_view.window.representedFilename = filePath.toNSString();
1056 setWindowIcon(window()->icon());
1059void QCocoaWindow::setWindowIcon(
const QIcon &icon)
1061 QMacAutoReleasePool pool;
1063 if (!isContentView())
1066 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
1072 if (icon.isNull()) {
1073 iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename];
1077 auto fallbackSize = QSizeF::fromCGSize(iconButton.frame.size) * qGuiApp->devicePixelRatio();
1078 iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize.toSize()];
1082void QCocoaWindow::setAlertState(
bool enabled)
1084 if (m_alertRequest == NoAlertRequest && enabled) {
1085 m_alertRequest = [NSApp requestUserAttention:NSCriticalRequest];
1086 }
else if (m_alertRequest != NoAlertRequest && !enabled) {
1087 [NSApp cancelUserAttentionRequest:m_alertRequest];
1088 m_alertRequest = NoAlertRequest;
1092bool QCocoaWindow::isAlertState()
const
1094 return m_alertRequest != NoAlertRequest;
1097void QCocoaWindow::raise()
1099 qCDebug(lcQpaWindow) <<
"QCocoaWindow::raise" << window();
1102 if (isContentView()) {
1103 if (m_view.window.visible) {
1110 QMacAutoReleasePool pool;
1111 [m_view.window orderFront:m_view.window];
1113 static bool raiseProcess = qt_mac_resolveOption(
true,
"QT_MAC_SET_RAISE_PROCESS");
1115 [NSApp activateIgnoringOtherApps:YES];
1118 [m_view.superview addSubview:m_view positioned:NSWindowAbove relativeTo:nil];
1122void QCocoaWindow::lower()
1124 qCDebug(lcQpaWindow) <<
"QCocoaWindow::lower" << window();
1126 if (isContentView()) {
1127 if (m_view.window.visible)
1128 [m_view.window orderBack:m_view.window];
1130 [m_view.superview addSubview:m_view positioned:NSWindowBelow relativeTo:nil];
1134bool QCocoaWindow::isExposed()
const
1136 return !m_exposedRect.isEmpty();
1139bool QCocoaWindow::isEmbedded()
const
1142 if (window()->parent())
1147 return !([m_view.window isKindOfClass:[QNSWindow
class]] ||
1148 [m_view.window isKindOfClass:[QNSPanel
class]]);
1155bool QCocoaWindow::isOpaque()
const
1159 static GLint openglSourfaceOrder = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
1161 bool translucent = window()->format().alphaBufferSize() > 0
1162 || window()->opacity() < 1
1163 || !window()->mask().isEmpty()
1164 || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
1165 return !translucent;
1168void QCocoaWindow::propagateSizeHints()
1170 QMacAutoReleasePool pool;
1171 if (!isContentView())
1174 qCDebug(lcQpaWindow) <<
"QCocoaWindow::propagateSizeHints" << window()
1175 <<
"min:" << windowMinimumSize() <<
"max:" << windowMaximumSize()
1176 <<
"increment:" << windowSizeIncrement()
1177 <<
"base:" << windowBaseSize();
1179 const NSWindow *window = m_view.window;
1182 QSize minimumSize = windowMinimumSize();
1183 if (!minimumSize.isValid())
1184 minimumSize = QSize(0, 0);
1185 window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
1188 window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
1191 updateTitleBarButtons(
this->window()->flags());
1195 QSize sizeIncrement = windowSizeIncrement();
1196 if (sizeIncrement.isEmpty())
1197 sizeIncrement = QSize(1, 1);
1198 window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
1200 QRect rect = geometry();
1201 QSize baseSize = windowBaseSize();
1202 if (!baseSize.isNull() && baseSize.isValid())
1203 [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
1206void QCocoaWindow::setOpacity(qreal level)
1208 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setOpacity" << level;
1209 if (!isContentView())
1212 m_view.window.alphaValue = level;
1215void QCocoaWindow::setMask(
const QRegion ®ion)
1217 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setMask" << window() << region;
1219 if (!region.isEmpty()) {
1220 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
1221 for (
const QRect &r : region)
1222 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
1223 CAShapeLayer *maskLayer = [CAShapeLayer layer];
1224 maskLayer.path = maskPath;
1225 m_view.layer.mask = maskLayer;
1227 m_view.layer.mask = nil;
1231bool QCocoaWindow::setKeyboardGrabEnabled(
bool)
1236bool QCocoaWindow::setMouseGrabEnabled(
bool)
1241WId QCocoaWindow::winId()
const
1246void QCocoaWindow::setParent(
const QPlatformWindow *parentWindow)
1248 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0);
1251 recreateWindowIfNeeded();
1253 setCocoaGeometry(geometry());
1256NSView *QCocoaWindow::view()
const
1261NSWindow *QCocoaWindow::nativeWindow()
const
1263 return m_view.window;
1268void QCocoaWindow::viewDidChangeFrame()
1275 handleGeometryChange();
1279
1280
1281
1282
1283
1284
1285void QCocoaWindow::viewDidChangeGlobalFrame()
1287 [m_view setNeedsDisplay:YES];
1297void QCocoaWindow::windowDidMove()
1299 if (!isContentView())
1302 handleGeometryChange();
1305 handleWindowStateChanged();
1308void QCocoaWindow::windowDidResize()
1310 if (!isContentView())
1313 handleGeometryChange();
1315 if (!m_view.inLiveResize)
1316 handleWindowStateChanged();
1319void QCocoaWindow::windowWillStartLiveResize()
1324 m_inLiveResize =
true;
1327bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1332 return !m_inLiveResize;
1335void QCocoaWindow::windowDidEndLiveResize()
1337 m_inLiveResize =
false;
1339 if (!isContentView())
1342 handleWindowStateChanged();
1345void QCocoaWindow::windowDidBecomeKey()
1349 if (m_view.window.firstResponder != m_view)
1352 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1353 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1355 if (windowIsPopupType()) {
1356 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1361 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1362 window(), Qt::ActiveWindowFocusReason);
1365void QCocoaWindow::windowDidResignKey()
1369 if (m_view.window.firstResponder != m_view)
1372 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1373 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1382 NSWindow *newKeyWindow = [NSApp keyWindow];
1383 if (newKeyWindow && newKeyWindow != m_view.window
1384 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1385 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1386 <<
"is Qt window. Deferring focus window change.";
1391 if (!windowIsPopupType()) {
1392 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1393 nullptr, Qt::ActiveWindowFocusReason);
1397void QCocoaWindow::windowDidOrderOnScreen()
1401 if (QWindowPrivate::get(window())->isPopup()) {
1402 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1403 (QGuiApplicationPrivate::currentMouseWindow);
1406 [m_view setNeedsDisplay:YES];
1409void QCocoaWindow::windowDidOrderOffScreen()
1411 handleExposeEvent(QRegion());
1414 if (window()->type() & Qt::Window) {
1415 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1416 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1417 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1418 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1420 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1421 (windowUnderMouse, windowPoint, screenPoint);
1427void QCocoaWindow::windowDidChangeOcclusionState()
1433 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1434 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1435 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1438 [m_view setNeedsDisplay:YES];
1440 handleExposeEvent(QRegion());
1443void QCocoaWindow::windowDidChangeScreen()
1449 NSScreen *nsScreen = m_view.window.screen;
1451 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1452 QCocoaScreen::updateScreens();
1454 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1455 auto *currentScreen = QCocoaScreen::get(nsScreen);
1457 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1470 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1471 window(), currentScreen ? currentScreen->screen() :
nullptr);
1473 if (currentScreen && hasPendingUpdateRequest()) {
1478 currentScreen->requestUpdate();
1484bool QCocoaWindow::windowShouldClose()
1486 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1498 [[m_view.window retain] autorelease];
1500 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1505void QCocoaWindow::handleGeometryChange()
1508 if (isContentView() && !isEmbedded()) {
1510 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
1513 newGeometry = QCocoaScreen::mapFromNative(contentRect).toRect();
1515 newGeometry = QCocoaWindow::mapFromNative(m_view.frame, m_view.superview).toRect();
1518 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1519 <<
"current" << geometry() <<
"new" << newGeometry;
1524 if (m_inSetStyleMask && !m_view.window) {
1525 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1532 if (!m_initialized) {
1534 QPlatformWindow::setGeometry(newGeometry);
1535 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1539 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1542 updateSafeAreaMarginsIfNeeded();
1546 if (!m_inSetGeometry)
1547 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1550void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1563 if (m_view.window.visible && m_view.window.screen
1564 && !geometry().size().isEmpty() && !region.isEmpty()
1565 && !m_view.hiddenOrHasHiddenAncestor) {
1566 m_exposedRect = region.boundingRect();
1568 m_exposedRect = QRect();
1571 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1572 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1575 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1580bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1582 if (type == Qt::Widget)
1583 type = window()->type();
1584 if (type == Qt::Tool)
1587 return ((type & Qt::Popup) == Qt::Popup);
1591
1592
1593
1594
1595
1596
1597
1598
1599bool QCocoaWindow::isContentView()
const
1601 return m_view.window.contentView == m_view;
1605
1606
1607
1608
1609
1610void QCocoaWindow::recreateWindowIfNeeded()
1612 QMacAutoReleasePool pool;
1614 QPlatformWindow *parentWindow = QPlatformWindow::parent();
1615 auto *parentCocoaWindow =
static_cast<QCocoaWindow *>(parentWindow);
1617 QCocoaWindow *oldParentCocoaWindow =
nullptr;
1618 if (QNSView *qnsView = qnsview_cast(m_view.superview))
1619 oldParentCocoaWindow = qnsView.platformWindow;
1621 if (isForeignWindow()) {
1625 qCDebug(lcQpaWindow) <<
"Skipping NSWindow management for foreign window" <<
this;
1628 if (parentCocoaWindow)
1629 [parentCocoaWindow->m_view addSubview:m_view];
1630 else if (oldParentCocoaWindow)
1631 [m_view removeFromSuperview];
1636 const bool isEmbeddedView = isEmbedded();
1637 RecreationReasons recreateReason = RecreationNotNeeded;
1639 if (parentWindow != oldParentCocoaWindow)
1640 recreateReason |= ParentChanged;
1642 if (isEmbeddedView && m_nsWindow)
1643 recreateReason |= EmbeddedChanged;
1646 recreateReason |= MissingWindow;
1649 if (m_windowModality != window()->modality())
1650 recreateReason |= WindowModalityChanged;
1652 Qt::WindowType type = window()->type();
1654 const bool shouldBeContentView = !parentWindow
1655 && !((type & Qt::SubWindow) == Qt::SubWindow)
1657 if (isContentView() != shouldBeContentView)
1658 recreateReason |= ContentViewChanged;
1660 const bool isPanel = isContentView() && [m_view.window isKindOfClass:[QNSPanel
class]];
1661 const bool shouldBePanel = shouldBeContentView &&
1662 ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog);
1664 if (isPanel != shouldBePanel)
1665 recreateReason |= PanelChanged;
1667 qCDebug(lcQpaWindow) <<
"QCocoaWindow::recreateWindowIfNeeded" << window() << recreateReason;
1669 if (recreateReason == RecreationNotNeeded)
1673 if ((isContentView() && !shouldBeContentView) || (recreateReason & (PanelChanged | EmbeddedChanged))) {
1675 qCDebug(lcQpaWindow) <<
"Getting rid of existing window" << m_nsWindow;
1676 [m_nsWindow closeAndRelease];
1677 if (isContentView() && !isEmbeddedView) {
1682 m_view.window.contentView = nil;
1688 if (shouldBeContentView && !m_nsWindow) {
1690 auto *newWindow = createNSWindow(shouldBePanel);
1691 qCDebug(lcQpaWindow) <<
"Ensuring that" << m_view <<
"is content view for" << newWindow;
1692 [m_view setPostsFrameChangedNotifications:NO];
1693 [newWindow setContentView:m_view];
1694 [m_view setPostsFrameChangedNotifications:YES];
1696 m_nsWindow = newWindow;
1697 Q_ASSERT(m_view.window == m_nsWindow);
1700 if (parentCocoaWindow) {
1702 [parentCocoaWindow->m_view addSubview:m_view];
1706void QCocoaWindow::requestUpdate()
1708 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1709 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1711 if (updatesWithDisplayLink()) {
1712 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1713 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1714 QPlatformWindow::requestUpdate();
1718 QPlatformWindow::requestUpdate();
1722bool QCocoaWindow::updatesWithDisplayLink()
const
1725 return format().swapInterval() != 0;
1728void QCocoaWindow::deliverUpdateRequest()
1730 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1732 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1737 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1738 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1739 <<
"due to" << qtMetalLayer <<
"needing display";
1745 qtMetalLayer.displayLock.unlock();
1748 QPlatformWindow::deliverUpdateRequest();
1751void QCocoaWindow::requestActivateWindow()
1753 QMacAutoReleasePool pool;
1754 [m_view.window makeFirstResponder:m_view];
1755 [m_view.window makeKeyWindow];
1759
1760
1761void QCocoaWindow::closeAllPopups()
1763 QGuiApplicationPrivate::instance()->closeAllPopups();
1765 removePopupMonitor();
1768void QCocoaWindow::removePopupMonitor()
1770 if (s_globalMouseMonitor) {
1771 [NSEvent removeMonitor:s_globalMouseMonitor];
1772 s_globalMouseMonitor = nil;
1774 if (s_applicationActivationObserver) {
1775 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1776 s_applicationActivationObserver = nil;
1780void QCocoaWindow::setupPopupMonitor()
1787 if (!s_globalMouseMonitor) {
1789 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1790 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1791 | NSEventMaskMouseMoved;
1792 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1793 handler:^(NSEvent *e){
1794 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1795 removePopupMonitor();
1798 const auto eventType = cocoaEvent2QtMouseEvent(e);
1799 if (eventType == QEvent::MouseMove) {
1800 if (s_windowUnderMouse) {
1801 QWindow *window = s_windowUnderMouse->window();
1802 const auto button = cocoaButton2QtButton(e);
1803 const auto buttons = currentlyPressedMouseButtons();
1804 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1805 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1806 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1807 buttons, button, eventType);
1817 if (!s_applicationActivationObserver) {
1818 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1819 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1820 object:nil queue:nil
1821 usingBlock:^(NSNotification *){
1827QCocoaNSWindow *QCocoaWindow::createNSWindow(
bool shouldBePanel)
1829 QMacAutoReleasePool pool;
1831 Qt::WindowType type = window()->type();
1832 Qt::WindowFlags flags = window()->flags();
1834 QRect rect = geometry();
1836 QScreen *targetScreen =
nullptr;
1837 for (QScreen *screen : QGuiApplication::screens()) {
1838 if (screen->geometry().contains(rect.topLeft())) {
1839 targetScreen = screen;
1844 NSWindowStyleMask styleMask = windowStyleMask(flags);
1846 if (!targetScreen) {
1847 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
1848 targetScreen = QGuiApplication::primaryScreen();
1852 styleMask = NSWindowStyleMaskBorderless;
1855 rect.translate(-targetScreen->geometry().topLeft());
1856 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
1857 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
1859 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
1864 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
1865 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
1866 <<
"Adjusting style mask during creation";
1867 styleMask = NSWindowStyleMaskBorderless;
1872 Class windowClass = shouldBePanel ? [QNSPanel
class] : [QNSWindow
class];
1873 QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:contentRect
1878 backing:NSBackingStoreBuffered defer:NO
1879 screen:targetCocoaScreen->nativeScreen()
1880 platformWindow:
this];
1884 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
1889 if (!resultingScreen)
1890 resultingScreen = targetCocoaScreen;
1892 if (resultingScreen->screen() != window()->screen()) {
1893 QWindowSystemInterface::handleWindowScreenChanged<
1894 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
1897 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
1898 [](QNSWindowDelegate *delegate) { [delegate release]; });
1899 nsWindow.delegate = sharedDelegate.get();
1905 [nsWindow setReleasedWhenClosed:NO];
1907 if (alwaysShowToolWindow()) {
1908 static dispatch_once_t onceToken;
1909 dispatch_once(&onceToken, ^{
1910 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
1911 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
1912 name:NSApplicationWillResignActiveNotification object:nil];
1913 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
1914 name:NSApplicationWillBecomeActiveNotification object:nil];
1918 nsWindow.restorable = NO;
1919 nsWindow.level = windowLevel(flags);
1920 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
1922 if (shouldBePanel) {
1924 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
1927 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
1928 | NSWindowCollectionBehaviorMoveToActiveSpace;
1930 if ((type & Qt::Popup) == Qt::Popup) {
1931 nsWindow.hasShadow = YES;
1932 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
1933 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
1934 setupPopupMonitor();
1939 m_windowModality = QPlatformWindow::window()->modality();
1941 applyContentBorderThickness(nsWindow);
1950 if (
auto *qtView = qnsview_cast(m_view))
1951 nsWindow.colorSpace = qtView.colorSpace;
1956bool QCocoaWindow::alwaysShowToolWindow()
const
1958 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
1961bool QCocoaWindow::setWindowModified(
bool modified)
1963 QMacAutoReleasePool pool;
1965 if (!isContentView())
1968 m_view.window.documentEdited = modified;
1972void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
1977QCocoaMenuBar *QCocoaWindow::menubar()
const
1982void QCocoaWindow::setWindowCursor(NSCursor *cursor)
1984 QMacAutoReleasePool pool;
1987 if (isForeignWindow())
1990 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
1992 QNSView *view = qnsview_cast(m_view);
1993 if (cursor == view.cursor)
1996 view.cursor = cursor;
2002 [m_view.window invalidateCursorRectsForView:m_view];
2009 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2010 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2011 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2012 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2013 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2014 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2015 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2016 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2017 location:locationInWindow modifierFlags:0 timestamp:0
2018 windowNumber:m_view.window.windowNumber context:nil
2019 eventNumber:0 trackingNumber:0 userData:0]];
2023void QCocoaWindow::registerTouch(
bool enable)
2025 m_registerTouchCount += enable ? 1 : -1;
2026 if (enable && m_registerTouchCount == 1)
2027 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2028 else if (m_registerTouchCount == 0)
2029 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2032void QCocoaWindow::registerContentBorderArea(quintptr identifier,
int upper,
int lower)
2034 m_contentBorderAreas.insert(identifier, BorderRange(identifier, upper, lower));
2035 applyContentBorderThickness();
2038void QCocoaWindow::setContentBorderAreaEnabled(quintptr identifier,
bool enable)
2040 m_enabledContentBorderAreas.insert(identifier, enable);
2041 applyContentBorderThickness();
2044void QCocoaWindow::setContentBorderEnabled(
bool enable)
2046 m_drawContentBorderGradient = enable;
2047 applyContentBorderThickness();
2050void QCocoaWindow::applyContentBorderThickness(NSWindow *window)
2052 QMacAutoReleasePool pool;
2054 if (!window && isContentView())
2055 window = m_view.window;
2060 if (!m_drawContentBorderGradient) {
2062 window.styleMask = window.styleMask & QT_IGNORE_DEPRECATIONS(~NSWindowStyleMaskTexturedBackground);
2063 setWindowFlags(QPlatformWindow::window()->flags());
2064 [window.contentView.superview setNeedsDisplay:YES];
2069 std::vector<BorderRange> ranges(m_contentBorderAreas.cbegin(), m_contentBorderAreas.cend());
2070 std::sort(ranges.begin(), ranges.end());
2071 int effectiveTopContentBorderThickness = 0;
2072 for (BorderRange range : ranges) {
2074 if (!m_enabledContentBorderAreas.value(range.identifier,
false))
2080 if (range.upper <= (effectiveTopContentBorderThickness + 1))
2081 effectiveTopContentBorderThickness = qMax(effectiveTopContentBorderThickness, range.lower);
2086 int effectiveBottomContentBorderThickness = 0;
2089 [window setStyleMask:[window styleMask] | QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground)];
2090 setWindowFlags(QPlatformWindow::window()->flags());
2095 const NSRect frameRect = window.frame;
2096 const NSRect contentRect = [window contentRectForFrameRect:frameRect];
2097 const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
2098 effectiveTopContentBorderThickness += titlebarHeight;
2100 [window setContentBorderThickness:effectiveTopContentBorderThickness forEdge:NSMaxYEdge];
2101 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
2103 [window setContentBorderThickness:effectiveBottomContentBorderThickness forEdge:NSMinYEdge];
2104 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge];
2106 [[[window contentView] superview] setNeedsDisplay:YES];
2109bool QCocoaWindow::testContentBorderAreaPosition(
int position)
const
2111 if (!m_drawContentBorderGradient || !isContentView())
2117 const int contentBorderThickness = [m_view.window contentBorderThicknessForEdge:NSMaxYEdge];
2118 const NSRect frameRect = m_view.window.frame;
2119 const NSRect contentRect = [m_view.window contentRectForFrameRect:frameRect];
2120 const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
2121 return 0 <= position && position < (contentBorderThickness - titlebarHeight);
2124qreal QCocoaWindow::devicePixelRatio()
const
2131 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
2132 return backingSize.height;
2135QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
2137 QWindow *targetWindow = window();
2138 for (QObject *child : targetWindow->children())
2139 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2140 if (QPlatformWindow *handle = childWindow->handle())
2141 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2142 targetWindow =
static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2144 return targetWindow;
2147bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
2152 if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
2162 QWindow *modalWindow =
nullptr;
2163 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2164 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2165 qCDebug(lcQpaWindow) <<
"Refusing key window for" <<
this <<
"due to being"
2166 <<
"blocked by" << modalWindow;
2171 if (m_inSetVisible) {
2172 QVariant showWithoutActivating = window()->property(
"_q_showWithoutActivating");
2173 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2180bool QCocoaWindow::windowEvent(QEvent *event)
2182 switch (event->type()) {
2183 case QEvent::WindowBlocked:
2184 case QEvent::WindowUnblocked:
2185 updateTitleBarButtons(window()->flags());
2191 return QPlatformWindow::windowEvent(event);
2194QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset()
const
2198 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2199 : NSMakePoint(0, 0);
2200 const NSRect visibleRect = [m_view visibleRect];
2202 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2205QMargins QCocoaWindow::frameMargins()
const
2207 QMacAutoReleasePool pool;
2209 if (!isContentView())
2212 NSRect frameW = m_view.window.frame;
2213 NSRect frameC = [m_view.window contentRectForFrameRect:frameW];
2215 return QMargins(frameW.origin.x - frameC.origin.x,
2216 (frameW.origin.y + frameW.size.height) - (frameC.origin.y + frameC.size.height),
2217 (frameW.origin.x + frameW.size.width) - (frameC.origin.x + frameC.size.width),
2218 frameC.origin.y - frameW.origin.y);
2221void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2223 m_frameStrutEventsEnabled = enabled;
2226QPoint QCocoaWindow::mapToGlobal(
const QPoint &point)
const
2228 NSPoint windowPoint = [m_view convertPoint:point.toCGPoint() toView:nil];
2229 NSPoint screenPoint = [m_view.window convertPointToScreen:windowPoint];
2230 return QCocoaScreen::mapFromNative(screenPoint).toPoint();
2233QPoint QCocoaWindow::mapFromGlobal(
const QPoint &point)
const
2235 NSPoint screenPoint = QCocoaScreen::mapToNative(point);
2236 NSPoint windowPoint = [m_view.window convertPointFromScreen:screenPoint];
2237 return QPointF::fromCGPoint([m_view convertPoint:windowPoint fromView:nil]).toPoint();
2240CGPoint QCocoaWindow::mapToNative(
const QPointF &point, NSView *referenceView)
2242 if (!referenceView || referenceView.flipped)
2243 return point.toCGPoint();
2245 return qt_mac_flip(point, QRectF::fromCGRect(referenceView.bounds)).toCGPoint();
2248CGRect QCocoaWindow::mapToNative(
const QRectF &rect, NSView *referenceView)
2250 if (!referenceView || referenceView.flipped)
2251 return rect.toCGRect();
2253 return qt_mac_flip(rect, QRectF::fromCGRect(referenceView.bounds)).toCGRect();
2256QPointF QCocoaWindow::mapFromNative(CGPoint point, NSView *referenceView)
2258 if (!referenceView || referenceView.flipped)
2259 return QPointF::fromCGPoint(point);
2261 return qt_mac_flip(QPointF::fromCGPoint(point), QRectF::fromCGRect(referenceView.bounds));
2264QRectF QCocoaWindow::mapFromNative(CGRect rect, NSView *referenceView)
2266 if (!referenceView || referenceView.flipped)
2267 return QRectF::fromCGRect(rect);
2269 return qt_mac_flip(QRectF::fromCGRect(rect), QRectF::fromCGRect(referenceView.bounds));
2272CALayer *QCocoaWindow::contentLayer()
const
2274 auto *layer = m_view.layer;
2275 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2276 layer = containerLayer.contentLayer;
2280void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2281 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2282 NSVisualEffectState activationState)
2284 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2285 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2286 <<
"in views without a container layer";
2290 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2291 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2292 << blendMode <<
"and activation state" << activationState;
2294 NSVisualEffectView *effectView =
nullptr;
2295 if (m_effectViews.contains(identifier)) {
2296 effectView = m_effectViews.value(identifier);
2297 if (rect.isEmpty()) {
2298 [effectView removeFromSuperview];
2299 m_effectViews.remove(identifier);
2302 }
else if (!rect.isEmpty()) {
2303 effectView = [NSVisualEffectView
new];
2306 effectView.wantsLayer = YES;
2307 effectView.layer.zPosition = -FLT_MAX;
2308 [m_view addSubview:effectView];
2309 m_effectViews.insert(identifier, effectView);
2315 effectView.frame = rect.toCGRect();
2316 effectView.material = material;
2317 effectView.blendingMode = blendMode;
2318 effectView.state = activationState;
2321#ifndef QT_NO_DEBUG_STREAM
2322QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2324 QDebugStateSaver saver(debug);
2326 debug <<
"QCocoaWindow(" << (
const void *)window;
2328 debug <<
", window=" << window->window();
2336#include "moc_qcocoawindow.cpp"
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
void setCocoaGeometry(const QRect &rect)
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()
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.
#define Q_NOTIFICATION_PREFIX
static void qRegisterNotificationCallbacks()
const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")