summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2020-05-04 11:33:10 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2020-05-04 17:38:40 +0200
commit0f7987f0c934b311bd39a5a496ffb0c6128eb8df (patch)
treedea392deb3c43e377a224271c4f450dcff3fc76f /src/plugins/platforms/cocoa
parentea7d85457d30e915ad470919d2e74867cba9cad8 (diff)
parentf0ea852d4dd6b3139869a952ee92e74cd367866d (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.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm11
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm36
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindowmanager.mm12
-rw-r--r--src/plugins/platforms/cocoa/qmultitouch_mac.mm5
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm82
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];