diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-05-04 11:33:10 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2020-05-04 17:38:40 +0200 |
commit | 0f7987f0c934b311bd39a5a496ffb0c6128eb8df (patch) | |
tree | dea392deb3c43e377a224271c4f450dcff3fc76f /src/plugins/platforms/cocoa | |
parent | ea7d85457d30e915ad470919d2e74867cba9cad8 (diff) | |
parent | f0ea852d4dd6b3139869a952ee92e74cd367866d (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts:
src/corelib/text/qlocale.cpp
src/network/access/qnetworkaccessmanager.cpp
Regenerated tests/auto/testlib/selftests/float/CMakeLists.txt
Change-Id: I5a8ae42511380ca49a38b13c6fa8a3c5df8bed01
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaintegration.mm | 4 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoascreen.mm | 11 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindow.mm | 36 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindowmanager.mm | 12 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qmultitouch_mac.mm | 5 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnswindow.mm | 82 |
6 files changed, 129 insertions, 21 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index e0dd103fbc..2d19809435 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -481,7 +481,9 @@ QList<QCocoaWindow *> *QCocoaIntegration::popupWindowStack() void QCocoaIntegration::setApplicationIcon(const QIcon &icon) const { - NSApp.applicationIconImage = [NSImage imageFromQIcon:icon]; + // Fall back to a size that looks good on the highest resolution screen available + auto fallbackSize = NSApp.dockTile.size.width * qGuiApp->devicePixelRatio(); + NSApp.applicationIconImage = [NSImage imageFromQIcon:icon withSize:fallbackSize]; } void QCocoaIntegration::beep() const diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index e4dd4cf6c6..6a3172fb19 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -614,7 +614,11 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) QRect windowRect; for (uint i = 0; i < displayCount; ++i) { QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(displays[i])).toRect(); - windowRect = windowRect.united(displayBounds); + // Only include the screen if it is positioned past the x/y position + if ((displayBounds.x() >= x || displayBounds.right() > x) && + (displayBounds.y() >= y || displayBounds.bottom() > y)) { + windowRect = windowRect.united(displayBounds); + } } if (grabRect.width() < 0) grabRect.setWidth(windowRect.width()); @@ -631,6 +635,11 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) auto display = displays[i]; QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(display)).toRect(); QRect grabBounds = displayBounds.intersected(grabRect); + if (grabBounds.isNull()) { + destinations.append(QRect()); + images.append(QImage()); + continue; + } QRect displayLocalGrabBounds = QRect(QPoint(grabBounds.topLeft() - displayBounds.topLeft()), grabBounds.size()); QImage displayImage = qt_mac_toQImage(QCFType<CGImageRef>(CGDisplayCreateImageForRect(display, displayLocalGrabBounds.toCGRect()))); displayImage.setDevicePixelRatio(displayImage.size().width() / displayLocalGrabBounds.size().width()); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index e471d2af28..14833267d2 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -368,8 +368,18 @@ void QCocoaWindow::setVisible(bool visible) } else if (window()->modality() == Qt::ApplicationModal) { // Show the window as application modal eventDispatcher()->beginModalSession(window()); - } else if (m_view.window.canBecomeKeyWindow && !eventDispatcher()->hasModalSession()) { - [m_view.window makeKeyAndOrderFront:nil]; + } else if (m_view.window.canBecomeKeyWindow) { + bool shouldBecomeKeyNow = !NSApp.modalWindow || m_view.window.worksWhenModal; + + // Panels with becomesKeyOnlyIfNeeded set should not activate until a view + // with needsPanelToBecomeKey, for example a line edit, is clicked. + if ([m_view.window isKindOfClass:[NSPanel class]]) + shouldBecomeKeyNow &= !(static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded); + + if (shouldBecomeKeyNow) + [m_view.window makeKeyAndOrderFront:nil]; + else + [m_view.window orderFront:nil]; } else { [m_view.window orderFront:nil]; } @@ -891,10 +901,13 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon) QMacAutoReleasePool pool; - if (icon.isNull()) + if (icon.isNull()) { iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename]; - else - iconButton.image = [NSImage imageFromQIcon:icon]; + } else { + // Fall back to a size that looks good on the highest resolution screen available + auto fallbackSize = iconButton.frame.size.height * qGuiApp->devicePixelRatio(); + iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize]; + } } void QCocoaWindow::setAlertState(bool enabled) @@ -1814,8 +1827,17 @@ void QCocoaWindow::updateNSToolbar() bool QCocoaWindow::testContentBorderAreaPosition(int position) const { - return isContentView() && m_drawContentBorderGradient && - 0 <= position && position < [m_view.window contentBorderThicknessForEdge:NSMaxYEdge]; + if (!m_drawContentBorderGradient || !isContentView()) + return false; + + // Determine if the given y postion (relative to the content area) is inside the + // unified toolbar area. Note that the value returned by contentBorderThicknessForEdge + // includes the title bar height; subtract it. + const int contentBorderThickness = [m_view.window contentBorderThicknessForEdge:NSMaxYEdge]; + const NSRect frameRect = m_view.window.frame; + const NSRect contentRect = [m_view.window contentRectForFrameRect:frameRect]; + const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height; + return 0 <= position && position < (contentBorderThickness - titlebarHeight); } qreal QCocoaWindow::devicePixelRatio() const diff --git a/src/plugins/platforms/cocoa/qcocoawindowmanager.mm b/src/plugins/platforms/cocoa/qcocoawindowmanager.mm index 9c45d8c7fc..5e218157c2 100644 --- a/src/plugins/platforms/cocoa/qcocoawindowmanager.mm +++ b/src/plugins/platforms/cocoa/qcocoawindowmanager.mm @@ -100,6 +100,18 @@ void QCocoaWindowManager::modalSessionChanged() } } } + + // Our worksWhenModal implementation is declarative and will normally be picked + // up by AppKit when needed, but to make sure AppKit also reflects the state + // in the window tag, so that the window can be ordered front by clicking it, + // we need to explicitly call setWorksWhenModal. + for (id window in NSApp.windows) { + if ([window isKindOfClass:[QNSPanel class]]) { + auto *panel = static_cast<QNSPanel *>(window); + // Call setter to tell AppKit that our state has changed + [panel setWorksWhenModal:panel.worksWhenModal]; + } + } } static void initializeWindowManager() { Q_UNUSED(QCocoaWindowManager::instance()); } diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac.mm b/src/plugins/platforms/cocoa/qmultitouch_mac.mm index 95256657fe..ac2317b217 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac.mm +++ b/src/plugins/platforms/cocoa/qmultitouch_mac.mm @@ -184,7 +184,10 @@ QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch) if (_touchCount != _currentTouches.size()) { // Remove all instances, and basically start from scratch: touchPoints.clear(); - for (QCocoaTouch *qcocoaTouch : _currentTouches) { + // Deleting touch points will remove them from current touches, + // so we make a copy of the touches before iterating them. + const auto currentTouchesSnapshot = _currentTouches; + for (QCocoaTouch *qcocoaTouch : currentTouchesSnapshot) { if (!_updateInternalStateOnly) { qcocoaTouch->_touchPoint.state = Qt::TouchPointReleased; touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint); diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm index 6b4e110af2..311c291252 100644 --- a/src/plugins/platforms/cocoa/qnswindow.mm +++ b/src/plugins/platforms/cocoa/qnswindow.mm @@ -158,7 +158,78 @@ static bool isMouseEvent(NSEvent *ev) #define QNSWINDOW_PROTOCOL_IMPLMENTATION 1 #include "qnswindow.mm" #undef QNSWINDOW_PROTOCOL_IMPLMENTATION + +- (BOOL)worksWhenModal +{ + if (!m_platformWindow) + return NO; + + // Conceptually there are two sets of windows we need consider: + // + // - windows 'lower' in the modal session stack + // - windows 'within' the current modal session + // + // The first set of windows should always be blocked by the current + // modal session, regardless of window type. The latter set may contain + // windows with a transient parent, which from Qt's point of view makes + // them 'child' windows, so we treat them as operable within the current + // modal session. + + if (!NSApp.modalWindow) + return NO; + + // If the current modal window (top level modal session) is not a Qt window we + // have no way of knowing if this window is transient child of the modal window. + if (![NSApp.modalWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) + return NO; + + if (auto *modalWindow = static_cast<QCocoaNSWindow *>(NSApp.modalWindow).platformWindow) { + if (modalWindow->window()->isAncestorOf(m_platformWindow->window(), QWindow::IncludeTransients)) + return YES; + } + + return NO; +} +@end + +#if !defined(QT_APPLE_NO_PRIVATE_APIS) +// When creating an NSWindow the worksWhenModal function is queried, +// and the resulting state is used to set the corresponding window tag, +// which the window server uses to determine whether or not the window +// should be allowed to activate via mouse clicks in the title-bar. +// Unfortunately, prior to macOS 10.15, this window tag was never +// updated after the initial assignment in [NSWindow _commonAwake], +// which meant that windows that dynamically change their worksWhenModal +// state will behave as if they were never allowed to work when modal. +// We work around this by manually updating the window tag when needed. + +typedef uint32_t CGSConnectionID; +typedef uint32_t CGSWindowID; + +extern "C" { +CGSConnectionID CGSMainConnectionID() __attribute__((weak_import)); +OSStatus CGSSetWindowTags(const CGSConnectionID, const CGSWindowID, int *, int) __attribute__((weak_import)); +OSStatus CGSClearWindowTags(const CGSConnectionID, const CGSWindowID, int *, int) __attribute__((weak_import)); +} + +@interface QNSPanel (WorksWhenModalWindowTagWorkaround) @end +@implementation QNSPanel (WorksWhenModalWindowTagWorkaround) +- (void)setWorksWhenModal:(BOOL)worksWhenModal +{ + [super setWorksWhenModal:worksWhenModal]; + + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSCatalina) { + if (CGSMainConnectionID && CGSSetWindowTags && CGSClearWindowTags) { + static int kWorksWhenModalWindowTag = 0x40; + auto *function = worksWhenModal ? CGSSetWindowTags : CGSClearWindowTags; + function(CGSMainConnectionID(), self.windowNumber, &kWorksWhenModalWindowTag, 64); + } else { + qWarning() << "Missing APIs for window tag handling, can not update worksWhenModal state"; + } + } +} @end +#endif // QT_APPLE_NO_PRIVATE_APIS #else // QNSWINDOW_PROTOCOL_IMPLMENTATION @@ -237,17 +308,6 @@ static bool isMouseEvent(NSEvent *ev) return canBecomeMain; } -- (BOOL)worksWhenModal -{ - if (m_platformWindow && [self isKindOfClass:[QNSPanel class]]) { - Qt::WindowType type = m_platformWindow->window()->type(); - if (type == Qt::Popup || type == Qt::Dialog || type == Qt::Tool) - return YES; - } - - return [super worksWhenModal]; -} - - (BOOL)isOpaque { return m_platformWindow ? m_platformWindow->isOpaque() : [super isOpaque]; |