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 setFrame:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())];
320 QMarginsF viewSafeAreaMargins = {
321 m_view.safeAreaInsets.left,
322 m_view.safeAreaInsets.top,
323 m_view.safeAreaInsets.right,
324 m_view.safeAreaInsets.bottom
331 auto screenRect = m_view.window.screen.frame;
332 auto screenInsets = m_view.window.screen.safeAreaInsets;
333 auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
334 [m_view.window convertRectToScreen:
335 [m_view convertRect:m_view.bounds toView:nil]]
341 QMarginsF screenSafeAreaMargins = {
343 qMax(0.0f, screenInsets.left - screenRelativeViewBounds.left())
346 qMax(0.0f, screenInsets.top - screenRelativeViewBounds.top())
349 qMax(0.0f, screenInsets.right
350 - (screenRect.size.width - screenRelativeViewBounds.right()))
352 screenInsets.bottom ?
353 qMax(0.0f, screenInsets.bottom
354 - (screenRect.size.height - screenRelativeViewBounds.bottom()))
358 return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
363 if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
364 m_lastReportedSafeAreaMargins = safeAreaMargins();
371 switch (NSApp.currentEvent.type) {
372 case NSEventTypeLeftMouseDown:
373 case NSEventTypeRightMouseDown:
374 case NSEventTypeOtherMouseDown:
375 case NSEventTypeMouseMoved:
376 case NSEventTypeLeftMouseDragged:
377 case NSEventTypeRightMouseDragged:
378 case NSEventTypeOtherMouseDragged:
381 [m_view.window performWindowDragWithEvent:NSApp.currentEvent];
390 qCDebug(lcQpaWindow) <<
"QCocoaWindow::setVisible" << window() << visible;
403 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping setting visibility";
407 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
408 qCDebug(lcQpaWindow) <<
"No change in visible status. Ignoring.";
413 qCWarning(lcQpaWindow) <<
"Already setting window visible!";
417 QScopedValueRollback<
bool> rollback(m_inSetVisible,
true);
419 QMacAutoReleasePool pool;
421 if (window()->transientParent())
422 parentCocoaWindow =
static_cast<
QCocoaWindow *>(window()->transientParent()->handle());
424 auto eventDispatcher = [] {
425 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
438 if (parentCocoaWindow) {
441 setGeometry(windowGeometry());
443 if (window()->type() == Qt::Popup) {
446 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
447 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
448 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
449 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
450 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
459 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
463 applyWindowState(window()->windowStates());
465 if (window()->windowState() != Qt::WindowMinimized) {
466 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
468 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
469 if (!nativeParentWindow.attachedSheet)
470 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
472 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
473 }
else if (window()->modality() == Qt::ApplicationModal) {
475 eventDispatcher()->beginModalSession(window());
476 }
else if (m_view.window.canBecomeKeyWindow) {
477 bool shouldBecomeKeyNow = !NSApp.modalWindow
478 || m_view.window.worksWhenModal
479 || !NSApp.modalWindow.visible;
483 if ([m_view.window isKindOfClass:[NSPanel
class]])
484 shouldBecomeKeyNow &= !(
static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
486 if (shouldBecomeKeyNow)
487 [m_view.window makeKeyAndOrderFront:nil];
489 [m_view.window orderFront:nil];
491 [m_view.window orderFront:nil];
498 if (eventDispatcher()->hasModalSession())
499 eventDispatcher()->endModalSession(window());
500 else if ([m_view.window isSheet])
501 [m_view.window.sheetParent endSheet:m_view.window];
508 [m_view.window orderOut:nil];
510 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
515 NSWindow *mainWindow = [NSApp mainWindow];
516 if (mainWindow && [mainWindow canBecomeKeyWindow])
517 [mainWindow makeKeyWindow];
531 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
532 && m_view.nextValidKeyView.window != m_view.window) {
533 qCDebug(lcQpaWindow) <<
"Detected nextValidKeyView" << m_view.nextValidKeyView
534 <<
"in different window" << m_view.nextValidKeyView.window
535 <<
"Resetting" << m_view.window <<
"first responder to nil.";
536 [m_view.window makeFirstResponder:nil];
541 if (parentCocoaWindow && window()->type() == Qt::Popup) {
542 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
543 if (m_resizableTransientParent
544 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
546 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
551NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
553 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
555 NSInteger windowLevel = NSNormalWindowLevel;
557 if (type == Qt::Tool)
558 windowLevel = NSFloatingWindowLevel;
559 else if ((type & Qt::Popup) == Qt::Popup)
560 windowLevel = NSPopUpMenuWindowLevel;
563 if (flags & Qt::WindowStaysOnTopHint)
564 windowLevel = NSModalPanelWindowLevel;
566 if (type == Qt::ToolTip)
567 windowLevel = NSScreenSaverWindowLevel;
569 auto *transientParent = window()->transientParent();
570 if (transientParent && transientParent->handle()) {
590 auto *transientCocoaWindow =
static_cast<QCocoaWindow *>(transientParent->handle());
591 auto *nsWindow = transientCocoaWindow->nativeWindow();
596 if (type != Qt::Window)
597 windowLevel = qMax(windowLevel, nsWindow.level);
603NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
605 const Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
611 NSUInteger styleMask = [&]{
613 if (flags & Qt::FramelessWindowHint)
614 return NSWindowStyleMaskBorderless;
617 if (windowIsPopupType(type))
618 return NSWindowStyleMaskBorderless;
620 if (flags & Qt::CustomizeWindowHint) {
623 return flags & Qt::WindowTitleHint
624 ? NSWindowStyleMaskTitled
625 : NSWindowStyleMaskBorderless;
628 return NSWindowStyleMaskTitled;
637 styleMask |= NSWindowStyleMaskClosable
638 | NSWindowStyleMaskMiniaturizable;
640 if (type != Qt::Popup)
641 styleMask |= NSWindowStyleMaskResizable;
643 if (type == Qt::Tool)
644 styleMask |= NSWindowStyleMaskUtilityWindow;
647 if (m_drawContentBorderGradient)
648 styleMask |= QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground);
650 if (flags & Qt::ExpandedClientAreaHint)
651 styleMask |= NSWindowStyleMaskFullSizeContentView;
654 styleMask |= (m_view.window.styleMask & (
655 NSWindowStyleMaskFullScreen
656 | NSWindowStyleMaskUnifiedTitleAndToolbar
657 | NSWindowStyleMaskDocModalWindow
658 | NSWindowStyleMaskNonactivatingPanel
659 | NSWindowStyleMaskHUDWindow));
664bool QCocoaWindow::isFixedSize()
const
666 return windowMinimumSize().isValid() && windowMaximumSize().isValid()
667 && windowMinimumSize() == windowMaximumSize();
670void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
672 if (!isContentView())
675 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
676 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
677 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
678 { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
681 bool hideButtons =
true;
682 for (
const auto &[button, buttonHint] : buttons) {
685 if (button == NSWindowMiniaturizeButton)
686 enabled = window()->type() != Qt::Dialog;
689 if (windowFlags & Qt::CustomizeWindowHint)
690 enabled = windowFlags & buttonHint;
694 if (button == NSWindowZoomButton && isFixedSize())
700 if (button == NSWindowCloseButton && enabled
701 && QWindowPrivate::get(window())->blockedByModalWindow) {
708 [m_view.window standardWindowButton:button].enabled = enabled;
709 hideButtons &= !enabled;
713 for (
const auto &[button, buttonHint] : buttons)
714 [m_view.window standardWindowButton:button].hidden = hideButtons;
717void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
723 QMacAutoReleasePool pool;
725 if (!isContentView())
730 m_inSetStyleMask =
true;
731 m_view.window.styleMask = windowStyleMask(flags);
732 m_inSetStyleMask =
false;
734 Qt::WindowType type =
static_cast<Qt::WindowType>(
int(flags & Qt::WindowType_Mask));
735 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
736 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
737 const bool enableFullScreen = m_view.window.qt_fullScreen
738 || !(flags & Qt::CustomizeWindowHint)
739 || (flags & Qt::WindowFullscreenButtonHint);
740 if (enableFullScreen) {
741 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
742 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
744 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
745 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
747 m_view.window.collectionBehavior = behavior;
752 m_view.window.level =
this->windowLevel(flags);
754 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
756 if (!(flags & Qt::FramelessWindowHint))
757 setWindowTitle(window()->title());
759 updateTitleBarButtons(flags);
768 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
769 if (m_view.window.ignoresMouseEvents != ignoreMouse)
770 m_view.window.ignoresMouseEvents = ignoreMouse;
773 m_view.window.titlebarAppearsTransparent = (flags & Qt::NoTitleBarBackgroundHint)
774 || (m_view.window.styleMask & QT_IGNORE_DEPRECATIONS(NSWindowStyleMaskTexturedBackground));
780
781
782
783
784
785
786void QCocoaWindow::setWindowState(Qt::WindowStates state)
788 if (window()->isVisible())
789 applyWindowState(state);
792void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
794 if (!isContentView())
797 const Qt::WindowState currentState = QWindowPrivate::effectiveState(windowState());
798 const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
800 if (newState == currentState)
803 qCDebug(lcQpaWindow) <<
"Applying" << newState <<
"to" << window() <<
"in" << currentState;
805 const NSSize contentSize = m_view.frame.size;
806 if (contentSize.width <= 0 || contentSize.height <= 0) {
809 qWarning(
"invalid window content view size, check your window geometry");
810 handleWindowStateChanged(HandleUnconditionally);
814 const NSWindow *nsWindow = m_view.window;
816 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
817 && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
818 qWarning() << window()->type() <<
"windows cannot be made" << newState;
819 handleWindowStateChanged(HandleUnconditionally);
823 const id sender = nsWindow;
826 switch (currentState) {
827 case Qt::WindowMinimized:
828 [nsWindow deminiaturize:sender];
833 case Qt::WindowFullScreen: {
844 if (newState == windowState())
848 case Qt::WindowFullScreen:
851 case Qt::WindowMaximized:
854 case Qt::WindowMinimized:
855 [nsWindow miniaturize:sender];
857 case Qt::WindowNoState:
858 if (windowState() == Qt::WindowMaximized)
866Qt::WindowStates QCocoaWindow::windowState()
const
868 Qt::WindowStates states = Qt::WindowNoState;
869 NSWindow *window = m_view.window;
871 if (window.miniaturized)
872 states |= Qt::WindowMinimized;
876 if (window.qt_fullScreen) {
877 states |= Qt::WindowFullScreen;
878 }
else if ((window.zoomed && !isTransitioningToFullScreen())
879 || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) {
880 states |= Qt::WindowMaximized;
889void QCocoaWindow::toggleMaximized()
891 const NSWindow *window = m_view.window;
895 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
896 window.styleMask |= NSWindowStyleMaskResizable;
898 const id sender = window;
899 [window zoom:sender];
902 window.styleMask &= ~NSWindowStyleMaskResizable;
905void QCocoaWindow::windowWillZoom()
907 updateNormalGeometry();
910void QCocoaWindow::toggleFullScreen()
912 const NSWindow *window = m_view.window;
917 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
919 const id sender = window;
920 [window toggleFullScreen:sender];
923void QCocoaWindow::windowWillEnterFullScreen()
925 if (!isContentView())
928 updateNormalGeometry();
933 m_view.window.styleMask |= NSWindowStyleMaskResizable;
936bool QCocoaWindow::isTransitioningToFullScreen()
const
938 NSWindow *window = m_view.window;
939 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
942void QCocoaWindow::windowDidEnterFullScreen()
944 if (!isContentView())
947 Q_ASSERT_X(m_view.window.qt_fullScreen,
"QCocoaWindow",
948 "FullScreen category processes window notifications first");
951 setWindowFlags(window()->flags());
953 handleWindowStateChanged();
956void QCocoaWindow::windowWillExitFullScreen()
958 if (!isContentView())
963 m_view.window.styleMask |= NSWindowStyleMaskResizable;
966void QCocoaWindow::windowDidExitFullScreen()
968 if (!isContentView())
971 Q_ASSERT_X(!m_view.window.qt_fullScreen,
"QCocoaWindow",
972 "FullScreen category processes window notifications first");
975 setWindowFlags(window()->flags());
977 Qt::WindowState requestedState = window()->windowState();
980 handleWindowStateChanged();
982 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
985 applyWindowState(requestedState);
989void QCocoaWindow::windowDidMiniaturize()
991 if (!isContentView())
994 handleWindowStateChanged();
997void QCocoaWindow::windowDidDeminiaturize()
999 if (!isContentView())
1002 Qt::WindowState requestedState = window()->windowState();
1004 handleWindowStateChanged();
1006 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
1009 applyWindowState(requestedState);
1013void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
1015 Qt::WindowStates currentState = windowState();
1016 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
1019 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleWindowStateChanged" <<
1020 m_lastReportedWindowState <<
"-->" << currentState;
1022 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
1023 window(), currentState, m_lastReportedWindowState);
1024 m_lastReportedWindowState = currentState;
1029void QCocoaWindow::setWindowTitle(
const QString &title)
1031 if (!isContentView())
1034 QMacAutoReleasePool pool;
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 if (!isContentView())
1048 QMacAutoReleasePool pool;
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 if (!isContentView())
1064 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
1070 QMacAutoReleasePool pool;
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;
1266void QCocoaWindow::setEmbeddedInForeignView()
1269 [m_nsWindow closeAndRelease];
1275void QCocoaWindow::viewDidChangeFrame()
1282 handleGeometryChange();
1286
1287
1288
1289
1290
1291
1292void QCocoaWindow::viewDidChangeGlobalFrame()
1294 [m_view setNeedsDisplay:YES];
1304void QCocoaWindow::windowDidMove()
1306 if (!isContentView())
1309 handleGeometryChange();
1312 handleWindowStateChanged();
1315void QCocoaWindow::windowDidResize()
1317 if (!isContentView())
1320 handleGeometryChange();
1322 if (!m_view.inLiveResize)
1323 handleWindowStateChanged();
1326void QCocoaWindow::windowWillStartLiveResize()
1331 m_inLiveResize =
true;
1334bool QCocoaWindow::allowsIndependentThreadedRendering()
const
1339 return !m_inLiveResize;
1342void QCocoaWindow::windowDidEndLiveResize()
1344 m_inLiveResize =
false;
1346 if (!isContentView())
1349 handleWindowStateChanged();
1352void QCocoaWindow::windowDidBecomeKey()
1356 if (m_view.window.firstResponder != m_view)
1359 qCDebug(lcQpaWindow) << m_view.window <<
"became key window."
1360 <<
"Updating focus window to" <<
this <<
"with view" << m_view;
1362 if (windowIsPopupType()) {
1363 qCDebug(lcQpaWindow) <<
"Window is popup. Skipping focus window change.";
1368 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1369 window(), Qt::ActiveWindowFocusReason);
1372void QCocoaWindow::windowDidResignKey()
1376 if (m_view.window.firstResponder != m_view)
1379 qCDebug(lcQpaWindow) << m_view.window <<
"resigned key window."
1380 <<
"Clearing focus window" <<
this <<
"with view" << m_view;
1389 NSWindow *newKeyWindow = [NSApp keyWindow];
1390 if (newKeyWindow && newKeyWindow != m_view.window
1391 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1392 qCDebug(lcQpaWindow) <<
"New key window" << newKeyWindow
1393 <<
"is Qt window. Deferring focus window change.";
1398 if (!windowIsPopupType()) {
1399 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1400 nullptr, Qt::ActiveWindowFocusReason);
1404void QCocoaWindow::windowDidOrderOnScreen()
1408 if (QWindowPrivate::get(window())->isPopup()) {
1409 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1410 (QGuiApplicationPrivate::currentMouseWindow);
1413 [m_view setNeedsDisplay:YES];
1416void QCocoaWindow::windowDidOrderOffScreen()
1418 handleExposeEvent(QRegion());
1421 if (window()->type() & Qt::Window) {
1422 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1423 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1424 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1425 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1427 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1428 (windowUnderMouse, windowPoint, screenPoint);
1434void QCocoaWindow::windowDidChangeOcclusionState()
1440 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1441 qCDebug(lcQpaWindow) <<
"Occlusion state of" << m_view.window <<
"for"
1442 << window() <<
"changed to" << (visible ?
"visible" :
"occluded");
1445 [m_view setNeedsDisplay:YES];
1447 handleExposeEvent(QRegion());
1450void QCocoaWindow::windowDidChangeScreen()
1456 NSScreen *nsScreen = m_view.window.screen;
1458 qCDebug(lcQpaWindow) << window() <<
"did change" << nsScreen;
1459 QCocoaScreen::updateScreens();
1461 auto *previousScreen =
static_cast<QCocoaScreen*>(screen());
1462 auto *currentScreen = QCocoaScreen::get(nsScreen);
1464 qCDebug(lcQpaWindow) <<
"Screen changed for" << window() <<
"from" << previousScreen <<
"to" << currentScreen;
1477 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1478 window(), currentScreen ? currentScreen->screen() :
nullptr);
1480 if (currentScreen && hasPendingUpdateRequest()) {
1485 currentScreen->requestUpdate();
1491bool QCocoaWindow::windowShouldClose()
1493 qCDebug(lcQpaWindow) <<
"QCocoaWindow::windowShouldClose" << window();
1505 [[m_view.window retain] autorelease];
1507 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1512void QCocoaWindow::handleGeometryChange()
1515 if (isContentView() && !isEmbedded()) {
1517 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
1520 newGeometry = QCocoaScreen::mapFromNative(contentRect).toRect();
1523 newGeometry = QRectF::fromCGRect(m_view.frame).toRect();
1526 qCDebug(lcQpaWindow) <<
"QCocoaWindow::handleGeometryChange" << window()
1527 <<
"current" << geometry() <<
"new" << newGeometry;
1532 if (m_inSetStyleMask && !m_view.window) {
1533 qCDebug(lcQpaWindow) <<
"Lacking window during style mask update, ignoring geometry change";
1540 if (!m_initialized) {
1542 QPlatformWindow::setGeometry(newGeometry);
1543 qCDebug(lcQpaWindow) <<
"Window still initializing, skipping event";
1547 QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
1550 updateSafeAreaMarginsIfNeeded();
1554 if (!m_inSetGeometry)
1555 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
1558void QCocoaWindow::handleExposeEvent(
const QRegion ®ion)
1571 if (m_view.window.visible && m_view.window.screen
1572 && !geometry().size().isEmpty() && !region.isEmpty()
1573 && !m_view.hiddenOrHasHiddenAncestor) {
1574 m_exposedRect = region.boundingRect();
1576 m_exposedRect = QRect();
1579 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::handleExposeEvent" << window() << region <<
"isExposed" << isExposed();
1580 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1583 static_cast<QCocoaScreen *>(screen())->maybeStopDisplayLink();
1588bool QCocoaWindow::windowIsPopupType(Qt::WindowType type)
const
1590 if (type == Qt::Widget)
1591 type = window()->type();
1592 if (type == Qt::Tool)
1595 return ((type & Qt::Popup) == Qt::Popup);
1599
1600
1601
1602
1603
1604
1605
1606
1607bool QCocoaWindow::isContentView()
const
1609 return m_view.window.contentView == m_view;
1613
1614
1615
1616
1617
1618void QCocoaWindow::recreateWindowIfNeeded()
1620 QMacAutoReleasePool pool;
1622 QPlatformWindow *parentWindow = QPlatformWindow::parent();
1623 auto *parentCocoaWindow =
static_cast<QCocoaWindow *>(parentWindow);
1625 QCocoaWindow *oldParentCocoaWindow =
nullptr;
1626 if (QNSView *qnsView = qnsview_cast(m_view.superview))
1627 oldParentCocoaWindow = qnsView.platformWindow;
1629 if (isForeignWindow()) {
1633 qCDebug(lcQpaWindow) <<
"Skipping NSWindow management for foreign window" <<
this;
1636 if (parentCocoaWindow)
1637 [parentCocoaWindow->m_view addSubview:m_view];
1638 else if (oldParentCocoaWindow)
1639 [m_view removeFromSuperview];
1644 const bool isEmbeddedView = isEmbedded();
1645 RecreationReasons recreateReason = RecreationNotNeeded;
1647 if (parentWindow != oldParentCocoaWindow)
1648 recreateReason |= ParentChanged;
1651 recreateReason |= MissingWindow;
1654 if (m_windowModality != window()->modality())
1655 recreateReason |= WindowModalityChanged;
1657 Qt::WindowType type = window()->type();
1659 const bool shouldBeContentView = !parentWindow
1660 && !((type & Qt::SubWindow) == Qt::SubWindow)
1662 if (isContentView() != shouldBeContentView)
1663 recreateReason |= ContentViewChanged;
1665 const bool isPanel = isContentView() && [m_view.window isKindOfClass:[QNSPanel
class]];
1666 const bool shouldBePanel = shouldBeContentView &&
1667 ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog);
1669 if (isPanel != shouldBePanel)
1670 recreateReason |= PanelChanged;
1672 qCDebug(lcQpaWindow) <<
"QCocoaWindow::recreateWindowIfNeeded" << window() << recreateReason;
1674 if (recreateReason == RecreationNotNeeded)
1678 if ((isContentView() && !shouldBeContentView) || (recreateReason & PanelChanged)) {
1680 qCDebug(lcQpaWindow) <<
"Getting rid of existing window" << m_nsWindow;
1681 [m_nsWindow closeAndRelease];
1682 if (isContentView() && !isEmbeddedView) {
1687 m_view.window.contentView = nil;
1693 if (shouldBeContentView && !m_nsWindow) {
1695 if (
auto *newWindow = createNSWindow(shouldBePanel)) {
1696 qCDebug(lcQpaWindow) <<
"Ensuring that" << m_view <<
"is content view for" << newWindow;
1697 [m_view setPostsFrameChangedNotifications:NO];
1698 [newWindow setContentView:m_view];
1699 [m_view setPostsFrameChangedNotifications:YES];
1701 m_nsWindow = newWindow;
1702 Q_ASSERT(m_view.window == m_nsWindow);
1706 if (parentCocoaWindow) {
1708 [parentCocoaWindow->m_view addSubview:m_view];
1712void QCocoaWindow::requestUpdate()
1714 qCDebug(lcQpaDrawing) <<
"QCocoaWindow::requestUpdate" << window()
1715 <<
"using" << (updatesWithDisplayLink() ?
"display-link" :
"timer");
1717 if (updatesWithDisplayLink()) {
1718 if (!
static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1719 qCDebug(lcQpaDrawing) <<
"Falling back to timer-based update request";
1720 QPlatformWindow::requestUpdate();
1724 QPlatformWindow::requestUpdate();
1728bool QCocoaWindow::updatesWithDisplayLink()
const
1731 return format().swapInterval() != 0;
1734void QCocoaWindow::deliverUpdateRequest()
1736 qCDebug(lcQpaDrawing) <<
"Delivering update request to" << window();
1738 if (
auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(contentLayer())) {
1743 if (!qtMetalLayer.displayLock.tryLockForRead()) {
1744 qCDebug(lcQpaDrawing) <<
"Deferring update request"
1745 <<
"due to" << qtMetalLayer <<
"needing display";
1751 qtMetalLayer.displayLock.unlock();
1754 QPlatformWindow::deliverUpdateRequest();
1757void QCocoaWindow::requestActivateWindow()
1759 QMacAutoReleasePool pool;
1760 [m_view.window makeFirstResponder:m_view];
1761 [m_view.window makeKeyWindow];
1765
1766
1767void QCocoaWindow::closeAllPopups()
1769 QGuiApplicationPrivate::instance()->closeAllPopups();
1771 removePopupMonitor();
1774void QCocoaWindow::removePopupMonitor()
1776 if (s_globalMouseMonitor) {
1777 [NSEvent removeMonitor:s_globalMouseMonitor];
1778 s_globalMouseMonitor = nil;
1780 if (s_applicationActivationObserver) {
1781 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1782 s_applicationActivationObserver = nil;
1786void QCocoaWindow::setupPopupMonitor()
1793 if (!s_globalMouseMonitor) {
1795 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1796 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1797 | NSEventMaskMouseMoved;
1798 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1799 handler:^(NSEvent *e){
1800 if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
1801 removePopupMonitor();
1804 const auto eventType = cocoaEvent2QtMouseEvent(e);
1805 if (eventType == QEvent::MouseMove) {
1806 if (s_windowUnderMouse) {
1807 QWindow *window = s_windowUnderMouse->window();
1808 const auto button = cocoaButton2QtButton(e);
1809 const auto buttons = currentlyPressedMouseButtons();
1810 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1811 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1812 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1813 buttons, button, eventType);
1823 if (!s_applicationActivationObserver) {
1824 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1825 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1826 object:nil queue:nil
1827 usingBlock:^(NSNotification *){
1833QCocoaNSWindow *QCocoaWindow::createNSWindow(
bool shouldBePanel)
1835 QMacAutoReleasePool pool;
1837 Qt::WindowType type = window()->type();
1838 Qt::WindowFlags flags = window()->flags();
1840 QRect rect = geometry();
1842 QScreen *targetScreen =
nullptr;
1843 for (QScreen *screen : QGuiApplication::screens()) {
1844 if (screen->geometry().contains(rect.topLeft())) {
1845 targetScreen = screen;
1850 NSWindowStyleMask styleMask = windowStyleMask(flags);
1852 if (!targetScreen) {
1853 qCWarning(lcQpaWindow) <<
"Window position" << rect <<
"outside any known screen, using primary screen";
1854 targetScreen = QGuiApplication::primaryScreen();
1858 styleMask = NSWindowStyleMaskBorderless;
1861 rect.translate(-targetScreen->geometry().topLeft());
1862 auto *targetCocoaScreen =
static_cast<QCocoaScreen *>(targetScreen->handle());
1863 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
1865 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
1870 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
1871 qCDebug(lcQpaWindow) <<
"Window positioned on portrait screen."
1872 <<
"Adjusting style mask during creation";
1873 styleMask = NSWindowStyleMaskBorderless;
1878 Class windowClass = shouldBePanel ? [QNSPanel
class] : [QNSWindow
class];
1879 QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:contentRect
1884 backing:NSBackingStoreBuffered defer:NO
1885 screen:targetCocoaScreen->nativeScreen()
1886 platformWindow:
this];
1890 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
1895 if (!resultingScreen)
1896 resultingScreen = targetCocoaScreen;
1898 if (resultingScreen->screen() != window()->screen()) {
1899 QWindowSystemInterface::handleWindowScreenChanged<
1900 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
1903 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
1904 [](QNSWindowDelegate *delegate) { [delegate release]; });
1905 nsWindow.delegate = sharedDelegate.get();
1911 [nsWindow setReleasedWhenClosed:NO];
1913 if (alwaysShowToolWindow()) {
1914 static dispatch_once_t onceToken;
1915 dispatch_once(&onceToken, ^{
1916 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
1917 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
1918 name:NSApplicationWillResignActiveNotification object:nil];
1919 [center addObserver:[QNSWindow
class] selector:@selector(applicationActivationChanged:)
1920 name:NSApplicationWillBecomeActiveNotification object:nil];
1924 nsWindow.restorable = NO;
1925 nsWindow.level = windowLevel(flags);
1926 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
1928 if (shouldBePanel) {
1930 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
1933 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
1934 | NSWindowCollectionBehaviorMoveToActiveSpace;
1936 if ((type & Qt::Popup) == Qt::Popup) {
1937 nsWindow.hasShadow = YES;
1938 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
1939 if (QGuiApplication::applicationState() != Qt::ApplicationActive)
1940 setupPopupMonitor();
1945 m_windowModality = QPlatformWindow::window()->modality();
1947 applyContentBorderThickness(nsWindow);
1956 if (
auto *qtView = qnsview_cast(m_view))
1957 nsWindow.colorSpace = qtView.colorSpace;
1962bool QCocoaWindow::alwaysShowToolWindow()
const
1964 return qt_mac_resolveOption(
false, window(),
"_q_macAlwaysShowToolWindow",
"");
1967bool QCocoaWindow::setWindowModified(
bool modified)
1969 if (!isContentView())
1972 m_view.window.documentEdited = modified;
1976void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
1981QCocoaMenuBar *QCocoaWindow::menubar()
const
1986void QCocoaWindow::setWindowCursor(NSCursor *cursor)
1989 if (isForeignWindow())
1992 qCInfo(lcQpaMouse) <<
"Setting" <<
this <<
"cursor to" << cursor;
1994 QNSView *view = qnsview_cast(m_view);
1995 if (cursor == view.cursor)
1998 view.cursor = cursor;
2004 [m_view.window invalidateCursorRectsForView:m_view];
2011 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
2012 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
2013 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
2014 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
2015 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
2016 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
2017 qCDebug(lcQpaMouse) <<
"Synthesizing cursor update";
2018 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
2019 location:locationInWindow modifierFlags:0 timestamp:0
2020 windowNumber:m_view.window.windowNumber context:nil
2021 eventNumber:0 trackingNumber:0 userData:0]];
2025void QCocoaWindow::registerTouch(
bool enable)
2027 m_registerTouchCount += enable ? 1 : -1;
2028 if (enable && m_registerTouchCount == 1)
2029 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
2030 else if (m_registerTouchCount == 0)
2031 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
2034void QCocoaWindow::registerContentBorderArea(quintptr identifier,
int upper,
int lower)
2036 m_contentBorderAreas.insert(identifier, BorderRange(identifier, upper, lower));
2037 applyContentBorderThickness();
2040void QCocoaWindow::setContentBorderAreaEnabled(quintptr identifier,
bool enable)
2042 m_enabledContentBorderAreas.insert(identifier, enable);
2043 applyContentBorderThickness();
2046void QCocoaWindow::setContentBorderEnabled(
bool enable)
2048 m_drawContentBorderGradient = enable;
2049 applyContentBorderThickness();
2052void QCocoaWindow::applyContentBorderThickness(NSWindow *window)
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 if (!isContentView())
2210 NSRect frameW = m_view.window.frame;
2211 NSRect frameC = [m_view.window contentRectForFrameRect:frameW];
2213 return QMargins(frameW.origin.x - frameC.origin.x,
2214 (frameW.origin.y + frameW.size.height) - (frameC.origin.y + frameC.size.height),
2215 (frameW.origin.x + frameW.size.width) - (frameC.origin.x + frameC.size.width),
2216 frameC.origin.y - frameW.origin.y);
2219void QCocoaWindow::setFrameStrutEventsEnabled(
bool enabled)
2221 m_frameStrutEventsEnabled = enabled;
2224CALayer *QCocoaWindow::contentLayer()
const
2226 auto *layer = m_view.layer;
2227 if (
auto *containerLayer = qt_objc_cast<QContainerLayer*>(layer))
2228 layer = containerLayer.contentLayer;
2232void QCocoaWindow::manageVisualEffectArea(quintptr identifier,
const QRect &rect,
2233 NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode,
2234 NSVisualEffectState activationState)
2236 if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) {
2237 qCWarning(lcQpaWindow) <<
"Can not manage visual effect areas"
2238 <<
"in views without a container layer";
2242 qCDebug(lcQpaWindow) <<
"Updating visual effect area" << identifier
2243 <<
"to" << rect <<
"with material" << material <<
"blend mode"
2244 << blendMode <<
"and activation state" << activationState;
2246 NSVisualEffectView *effectView =
nullptr;
2247 if (m_effectViews.contains(identifier)) {
2248 effectView = m_effectViews.value(identifier);
2249 if (rect.isEmpty()) {
2250 [effectView removeFromSuperview];
2251 m_effectViews.remove(identifier);
2254 }
else if (!rect.isEmpty()) {
2255 effectView = [NSVisualEffectView
new];
2258 effectView.wantsLayer = YES;
2259 effectView.layer.zPosition = -FLT_MAX;
2260 [m_view addSubview:effectView];
2261 m_effectViews.insert(identifier, effectView);
2267 effectView.frame = rect.toCGRect();
2268 effectView.material = material;
2269 effectView.blendingMode = blendMode;
2270 effectView.state = activationState;
2273#ifndef QT_NO_DEBUG_STREAM
2274QDebug operator<<(QDebug debug,
const QCocoaWindow *window)
2276 QDebugStateSaver saver(debug);
2278 debug <<
"QCocoaWindow(" << (
const void *)window;
2280 debug <<
", window=" << window->window();
2288#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")