diff options
Diffstat (limited to 'src/plugins')
117 files changed, 2563 insertions, 841 deletions
diff --git a/src/plugins/bearer/generic/qgenericengine.cpp b/src/plugins/bearer/generic/qgenericengine.cpp index 7472758418..b1f28849a7 100644 --- a/src/plugins/bearer/generic/qgenericengine.cpp +++ b/src/plugins/bearer/generic/qgenericengine.cpp @@ -300,7 +300,7 @@ void QGenericEngine::doRequestUpdate() if (interface.flags() & QNetworkInterface::IsLoopBack) continue; -#ifndef Q_OS_WINRT +#ifndef Q_OS_WIN // ignore WLAN interface handled in separate engine if (qGetInterfaceType(interface.name()) == QNetworkConfiguration::BearerWLAN) continue; diff --git a/src/plugins/bearer/nla/nla.pro b/src/plugins/bearer/nla/nla.pro index 113d0667d2..76f3279d25 100644 --- a/src/plugins/bearer/nla/nla.pro +++ b/src/plugins/bearer/nla/nla.pro @@ -2,7 +2,7 @@ TARGET = qnlabearer QT = core core-private network network-private -LIBS += -lws2_32 +QMAKE_USE_PRIVATE += ws2_32 HEADERS += qnlaengine.h \ ../platformdefs_win.h \ diff --git a/src/plugins/bearer/nla/qnlaengine.cpp b/src/plugins/bearer/nla/qnlaengine.cpp index 726e1efb92..e1e60389f1 100644 --- a/src/plugins/bearer/nla/qnlaengine.cpp +++ b/src/plugins/bearer/nla/qnlaengine.cpp @@ -75,38 +75,38 @@ QWindowsSockInit2::~QWindowsSockInit2() #ifdef BEARER_MANAGEMENT_DEBUG static void printBlob(NLA_BLOB *blob) { - qDebug() << "==== BEGIN NLA_BLOB ====" << endl + qDebug() << "==== BEGIN NLA_BLOB ====" << Qt::endl - << "type:" << blob->header.type << endl - << "size:" << blob->header.dwSize << endl + << "type:" << blob->header.type << Qt::endl + << "size:" << blob->header.dwSize << Qt::endl << "next offset:" << blob->header.nextOffset; switch (blob->header.type) { case NLA_RAW_DATA: - qDebug() << "Raw Data" << endl + qDebug() << "Raw Data" << Qt::endl << '\t' << blob->data.rawData; break; case NLA_INTERFACE: - qDebug() << "Interface" << endl - << "\ttype:" << blob->data.interfaceData.dwType << endl - << "\tspeed:" << blob->data.interfaceData.dwSpeed << endl + qDebug() << "Interface" << Qt::endl + << "\ttype:" << blob->data.interfaceData.dwType << Qt::endl + << "\tspeed:" << blob->data.interfaceData.dwSpeed << Qt::endl << "\tadapter:" << blob->data.interfaceData.adapterName; break; case NLA_802_1X_LOCATION: - qDebug() << "802.1x Location" << endl + qDebug() << "802.1x Location" << Qt::endl << '\t' << blob->data.locationData.information; break; case NLA_CONNECTIVITY: - qDebug() << "Connectivity" << endl - << "\ttype:" << blob->data.connectivity.type << endl + qDebug() << "Connectivity" << Qt::endl + << "\ttype:" << blob->data.connectivity.type << Qt::endl << "\tinternet:" << blob->data.connectivity.internet; break; case NLA_ICS: - qDebug() << "ICS" << endl - << "\tspeed:" << blob->data.ICS.remote.speed << endl - << "\ttype:" << blob->data.ICS.remote.type << endl - << "\tstate:" << blob->data.ICS.remote.state << endl - << "\tmachine name:" << blob->data.ICS.remote.machineName << endl + qDebug() << "ICS" << Qt::endl + << "\tspeed:" << blob->data.ICS.remote.speed << Qt::endl + << "\ttype:" << blob->data.ICS.remote.type << Qt::endl + << "\tstate:" << blob->data.ICS.remote.state << Qt::endl + << "\tmachine name:" << blob->data.ICS.remote.machineName << Qt::endl << "\tshared adapter name:" << blob->data.ICS.remote.sharedAdapterName; break; default: @@ -119,9 +119,6 @@ static void printBlob(NLA_BLOB *blob) static QNetworkConfiguration::BearerType qGetInterfaceType(const QString &interface) { -#ifdef Q_OS_WINCE - Q_UNUSED(interface) -#else unsigned long oid; DWORD bytesWritten; @@ -177,8 +174,6 @@ static QNetworkConfiguration::BearerType qGetInterfaceType(const QString &interf qDebug() << medium << physicalMedium; #endif -#endif - return QNetworkConfiguration::BearerUnknown; } @@ -307,16 +302,12 @@ void QNlaThread::run() break; } -#ifndef Q_OS_WINCE // Not interested in unrelated IO completion events // although we also don't want to block them while (WaitForSingleObjectEx(changeEvent, WSA_INFINITE, true) != WAIT_IO_COMPLETION && handle) { } -#else - WaitForSingleObject(changeEvent, WSA_INFINITE); -#endif mutex.lock(); if (handle) { diff --git a/src/plugins/imageformats/ico/qicohandler.cpp b/src/plugins/imageformats/ico/qicohandler.cpp index 30935cacda..4908850cc5 100644 --- a/src/plugins/imageformats/ico/qicohandler.cpp +++ b/src/plugins/imageformats/ico/qicohandler.cpp @@ -523,17 +523,21 @@ QImage ICOReader::iconAt(int index) if (!image.isNull()) { readBMP(image); if (!image.isNull()) { - QImage mask(image.width(), image.height(), QImage::Format_Mono); - if (!mask.isNull()) { - mask.setColorCount(2); - mask.setColor(0, qRgba(255,255,255,0xff)); - mask.setColor(1, qRgba(0 ,0 ,0 ,0xff)); - read1BitBMP(mask); + if (icoAttrib.depth == 32) { + img = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); + } else { + QImage mask(image.width(), image.height(), QImage::Format_Mono); if (!mask.isNull()) { - img = image; - img.setAlphaChannel(mask); - // (Luckily, it seems that setAlphaChannel() does not ruin the alpha values - // of partially transparent pixels in those icons that have that) + mask.setColorCount(2); + mask.setColor(0, qRgba(255,255,255,0xff)); + mask.setColor(1, qRgba(0 ,0 ,0 ,0xff)); + read1BitBMP(mask); + if (!mask.isNull()) { + img = image; + img.setAlphaChannel(mask); + // (Luckily, it seems that setAlphaChannel() does not ruin the alpha values + // of partially transparent pixels in those icons that have that) + } } } } diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp index 57fe7c2fa2..4e9828663f 100644 --- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp @@ -40,6 +40,7 @@ #include <QtCore/QCoreApplication> #include <QtGui/QKeyEvent> +#include <QtGui/QGuiApplication> #include <locale.h> @@ -121,7 +122,14 @@ bool QComposeInputContext::filterEvent(const QEvent *event) QInputMethodEvent event; event.setCommitString(composedText); - QCoreApplication::sendEvent(m_focusObject, &event); + + if (!m_focusObject && qApp) + m_focusObject = qApp->focusObject(); + + if (m_focusObject) + QCoreApplication::sendEvent(m_focusObject, &event); + else + qCWarning(lcXkbCompose, "no focus object"); reset(); return true; diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 6ae429b24e..70dde46ffa 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -485,7 +485,7 @@ static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring pa } if (Q_UNLIKELY(!m_main)) { - qCritical() << "dlsym failed:" << dlerror() << endl + qCritical() << "dlsym failed:" << dlerror() << Qt::endl << "Could not find main method"; return false; } diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index c31e43e0bb..db40c30d7d 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -791,7 +791,7 @@ void QAndroidInputContext::longPress(int x, int y) return; } QList<QInputMethodEvent::Attribute> imAttributes; - imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, 0, QVariant())); + imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant())); QInputMethodEvent event(QString(), imAttributes); QGuiApplication::sendEvent(m_focusObject, &event); @@ -978,15 +978,25 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right m_composingText.clear(); m_composingTextStart = -1; - QString text = query->value(Qt::ImSurroundingText).toString(); - if (text.isEmpty()) - return JNI_TRUE; - if (leftLength < 0) { rightLength += -leftLength; leftLength = 0; } + QVariant textBeforeCursor = query->value(Qt::ImTextBeforeCursor); + QVariant textAfterCursor = query->value(Qt::ImTextAfterCursor); + if (textBeforeCursor.isValid() && textAfterCursor.isValid()) { + leftLength = qMin(leftLength, textBeforeCursor.toString().length()); + rightLength = qMin(rightLength, textAfterCursor.toString().length()); + } else { + int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + leftLength = qMin(leftLength, cursorPos); + rightLength = qMin(rightLength, query->value(Qt::ImSurroundingText).toString().length() - cursorPos); + } + + if (leftLength == 0 && rightLength == 0) + return JNI_TRUE; + QInputMethodEvent event; event.setCommitString(QString(), -leftLength, leftLength+rightLength); sendInputMethodEvent(&event); @@ -1075,6 +1085,14 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex int cpos = localPos + composeLength; //actual cursor pos relative to the current block int localOffset = 0; // start of extracted text relative to the current block + if (blockPos > 0) { + QString prevBlockEnding = query->value(Qt::ImTextBeforeCursor).toString(); + prevBlockEnding.chop(localPos); + if (prevBlockEnding.endsWith(QLatin1Char('\n'))) { + localOffset = -qMin(20, prevBlockEnding.length()); + blockText = prevBlockEnding.right(-localOffset) + blockText; + } + } // It is documented that we should try to return hintMaxChars // characters, but that's not what the standard Android controls do, and @@ -1111,46 +1129,63 @@ QString QAndroidInputContext::getSelectedText(jint /*flags*/) QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) { - //### the preedit text could theoretically be after the cursor - QVariant textAfter = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, QVariant(length)); - if (textAfter.isValid()) { - return textAfter.toString().left(length); - } - - //compatibility code for old controls that do not implement the new API - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); - if (query.isNull()) + if (length <= 0) return QString(); - QString text = query->value(Qt::ImSurroundingText).toString(); - if (!text.length()) - return text; + QString text; + + QVariant reportedTextAfter = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, length); + if (reportedTextAfter.isValid()) { + text = reportedTextAfter.toString(); + } else { + // Compatibility code for old controls that do not implement the new API + QSharedPointer<QInputMethodQueryEvent> query = + focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText); + if (query) { + const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + text = query->value(Qt::ImSurroundingText).toString().mid(cursorPos); + } + } + + // Controls do not report preedit text, so we have to add it + if (!m_composingText.isEmpty()) { + const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart; + text = m_composingText.midRef(cursorPosInsidePreedit) + text; + } - int cursorPos = query->value(Qt::ImCursorPosition).toInt(); - return text.mid(cursorPos, length); + text.truncate(length); + return text; } QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) { - QVariant textBefore = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, QVariant(length)); - if (textBefore.isValid()) - return textBefore.toString().rightRef(length) + m_composingText; - - //compatibility code for old controls that do not implement the new API - QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); - if (query.isNull()) + if (length <= 0) return QString(); - int cursorPos = query->value(Qt::ImCursorPosition).toInt(); - QString text = query->value(Qt::ImSurroundingText).toString(); - if (!text.length()) - return text; + QString text; - //### the preedit text does not need to be immediately before the cursor - if (cursorPos <= length) - return text.leftRef(cursorPos) + m_composingText; - else - return text.midRef(cursorPos - length, length) + m_composingText; + QVariant reportedTextBefore = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, length); + if (reportedTextBefore.isValid()) { + text = reportedTextBefore.toString(); + } else { + // Compatibility code for old controls that do not implement the new API + QSharedPointer<QInputMethodQueryEvent> query = + focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText); + if (query) { + const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + text = query->value(Qt::ImSurroundingText).toString().left(cursorPos); + } + } + + // Controls do not report preedit text, so we have to add it + if (!m_composingText.isEmpty()) { + const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart; + text += m_composingText.leftRef(cursorPosInsidePreedit); + } + + if (text.length() > length) + text = text.right(length); + return text; } /* @@ -1213,6 +1248,8 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) if (query.isNull()) return JNI_FALSE; + if (start == end) + return JNI_TRUE; if (start > end) qSwap(start, end); diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 368cf56c80..db4ec251ae 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -177,7 +177,7 @@ NSString *macRole(QAccessibleInterface *interface) if (roleMap.isEmpty()) populateRoleMap(); - // MAC_ACCESSIBILTY_DEBUG() << "role for" << interface.object() << "interface role" << hex << qtRole; + // MAC_ACCESSIBILTY_DEBUG() << "role for" << interface.object() << "interface role" << Qt::hex << qtRole; if (roleMap.contains(qtRole)) { // MAC_ACCESSIBILTY_DEBUG() << "return" << roleMap[qtRole]; diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h index 914aaa2b1b..7fbe729381 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h @@ -52,7 +52,7 @@ @class QT_MANGLE_NAMESPACE(QMacAccessibilityElement); -@interface QT_MANGLE_NAMESPACE(QMacAccessibilityElement) : NSObject +@interface QT_MANGLE_NAMESPACE(QMacAccessibilityElement) : NSObject <NSAccessibilityElement> - (instancetype)initWithId:(QAccessible::Id)anId; + (instancetype)elementWithId:(QAccessible::Id)anId; diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index f0ef70e3a3..3560c9d9b5 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -174,6 +174,17 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of // accessibility protocol // +- (BOOL)isAccessibilityFocused +{ + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) { + return false; + } + // Just check if the app thinks we're focused. + id focusedElement = NSApp.accessibilityApplicationFocusedUIElement; + return [focusedElement isEqual:self]; +} + // attributes + (id) lineNumberForIndex: (int)index forText:(const QString &)text @@ -187,54 +198,58 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of return YES; } -- (NSArray<NSString *> *)accessibilityAttributeNames { - static NSArray<NSString *> *defaultAttributes = [@[ - NSAccessibilityRoleAttribute, - NSAccessibilityRoleDescriptionAttribute, - NSAccessibilitySubroleAttribute, - NSAccessibilityChildrenAttribute, - NSAccessibilityFocusedAttribute, - NSAccessibilityParentAttribute, - NSAccessibilityWindowAttribute, - NSAccessibilityTopLevelUIElementAttribute, - NSAccessibilityPositionAttribute, - NSAccessibilitySizeAttribute, - NSAccessibilityTitleAttribute, - NSAccessibilityDescriptionAttribute, - NSAccessibilityEnabledAttribute - ] retain]; +- (NSString *) accessibilityRole { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return NSAccessibilityUnknownRole; + return QCocoaAccessible::macRole(iface); +} +- (NSString *) accessibilitySubRole { QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); if (!iface || !iface->isValid()) - return defaultAttributes; + return NSAccessibilityUnknownRole; + return QCocoaAccessible::macSubrole(iface); +} - NSMutableArray<NSString *> *attributes = [[NSMutableArray<NSString *> alloc] initWithCapacity:defaultAttributes.count]; - [attributes addObjectsFromArray:defaultAttributes]; +- (NSString *) accessibilityRoleDescription { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return NSAccessibilityUnknownRole; + return NSAccessibilityRoleDescription(self.accessibilityRole, self.accessibilitySubRole); +} - if (QCocoaAccessible::hasValueAttribute(iface)) { - [attributes addObject:NSAccessibilityValueAttribute]; - } +- (NSArray *) accessibilityChildren { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return nil; + return QCocoaAccessible::unignoredChildren(iface); +} - if (iface->textInterface()) { - [attributes addObjectsFromArray:@[ - NSAccessibilityNumberOfCharactersAttribute, - NSAccessibilitySelectedTextAttribute, - NSAccessibilitySelectedTextRangeAttribute, - NSAccessibilityVisibleCharacterRangeAttribute, - NSAccessibilityInsertionPointLineNumberAttribute - ]]; - -// TODO: multi-selection: NSAccessibilitySelectedTextRangesAttribute, - } +- (id) accessibilityWindow { + // We're in the same window as our parent. + return [self.accessibilityParent accessibilityWindow]; +} - if (iface->valueInterface()) { - [attributes addObjectsFromArray:@[ - NSAccessibilityMinValueAttribute, - NSAccessibilityMaxValueAttribute - ]]; - } +- (id) accessibilityTopLevelUIElementAttribute { + // We're in the same top level element as our parent. + return [self.accessibilityParent accessibilityTopLevelUIElementAttribute]; +} - return [attributes autorelease]; +- (NSString *) accessibilityTitle { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return nil; + if (iface->role() == QAccessible::StaticText) + return nil; + return iface->text(QAccessible::Name).toNSString(); +} + +- (BOOL) accessibilityEnabledAttribute { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return false; + return !iface->state().disabled; } - (id)accessibilityParent { @@ -271,112 +286,117 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of return QCocoaScreen::mapToNative(iface->rect()); } -- (id) minValueAttribute:(QAccessibleInterface*)iface { +- (NSString*)accessibilityLabel { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) { + qWarning() << "Called accessibilityLabel on invalid object: " << axid; + return nil; + } + return iface->text(QAccessible::Description).toNSString(); +} + +- (void)setAccessibilityLabel:(NSString*)label{ + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return; + iface->setText(QAccessible::Description, QString::fromNSString(label)); +} + +- (id) accessibilityMinValue:(QAccessibleInterface*)iface { if (QAccessibleValueInterface *val = iface->valueInterface()) return @(val->minimumValue().toDouble()); return nil; } -- (id) maxValueAttribute:(QAccessibleInterface*)iface { +- (id) accessibilityMaxValue:(QAccessibleInterface*)iface { if (QAccessibleValueInterface *val = iface->valueInterface()) return @(val->maximumValue().toDouble()); return nil; } -- (id)accessibilityAttributeValue:(NSString *)attribute { +- (id) accessibilityValue { QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); - if (!iface || !iface->isValid()) { - qWarning() << "Called attribute on invalid object: " << axid; + if (!iface || !iface->isValid()) return nil; - } - if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { - return QCocoaAccessible::macRole(iface); - } else if ([attribute isEqualToString:NSAccessibilitySubroleAttribute]) { - return QCocoaAccessible::macSubrole(iface); - } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { - return NSAccessibilityRoleDescription(QCocoaAccessible::macRole(iface), - [self accessibilityAttributeValue:NSAccessibilitySubroleAttribute]); - } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { - return QCocoaAccessible::unignoredChildren(iface); - } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { - // Just check if the app thinks we're focused. - id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute]; - return @([focusedElement isEqual:self]); - } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) { - return self.accessibilityParent; - } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) { - // We're in the same window as our parent. - return [[self accessibilityParent] accessibilityAttributeValue:NSAccessibilityWindowAttribute]; - } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) { - // We're in the same top level element as our parent. - return [[self accessibilityParent] accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; - } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) { - // The position in points of the element's lower-left corner in screen-relative coordinates - QPointF qtPosition = QRectF(iface->rect()).bottomLeft(); - return [NSValue valueWithPoint:QCocoaScreen::mapToNative(qtPosition)]; - } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) { - QSize qtSize = iface->rect().size(); - return [NSValue valueWithSize: NSMakeSize(qtSize.width(), qtSize.height())]; - } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) { - if (iface->role() == QAccessible::StaticText) - return nil; - return iface->text(QAccessible::Name).toNSString(); - } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { - return iface->text(QAccessible::Description).toNSString(); - } else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) { - return @(!iface->state().disabled); - } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { - // VoiceOver asks for the value attribute for all elements. Return nil - // if we don't want the element to have a value attribute. - if (!QCocoaAccessible::hasValueAttribute(iface)) - return nil; + // VoiceOver asks for the value attribute for all elements. Return nil + // if we don't want the element to have a value attribute. + if (!QCocoaAccessible::hasValueAttribute(iface)) + return nil; + + return QCocoaAccessible::getValueAttribute(iface); +} - return QCocoaAccessible::getValueAttribute(iface); +- (NSInteger) accessibilityNumberOfCharacters { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return 0; + if (QAccessibleTextInterface *text = iface->textInterface()) + return text->characterCount(); + return 0; +} - } else if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute]) { - if (QAccessibleTextInterface *text = iface->textInterface()) - return @(text->characterCount()); +- (NSString *) accessibilitySelectedText { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) return nil; - } else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { - if (QAccessibleTextInterface *text = iface->textInterface()) { - int start = 0; - int end = 0; + if (QAccessibleTextInterface *text = iface->textInterface()) { + int start = 0; + int end = 0; + text->selection(0, &start, &end); + return text->text(start, end).toNSString(); + } + return nil; +} + +- (NSRange) accessibilitySelectedTextRange { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return NSRange(); + if (QAccessibleTextInterface *text = iface->textInterface()) { + int start = 0; + int end = 0; + if (text->selectionCount() > 0) { text->selection(0, &start, &end); - return text->text(start, end).toNSString(); - } - return nil; - } else if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { - if (QAccessibleTextInterface *text = iface->textInterface()) { - int start = 0; - int end = 0; - if (text->selectionCount() > 0) { - text->selection(0, &start, &end); - } else { - start = text->cursorPosition(); - end = start; - } - return [NSValue valueWithRange:NSMakeRange(quint32(start), quint32(end - start))]; - } - return [NSValue valueWithRange: NSMakeRange(0, 0)]; - } else if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) { - // FIXME This is not correct and may impact performance for big texts - if (QAccessibleTextInterface *text = iface->textInterface()) - return [NSValue valueWithRange: NSMakeRange(0, text->characterCount())]; - return [NSValue valueWithRange: NSMakeRange(0, iface->text(QAccessible::Name).length())]; - } else if ([attribute isEqualToString:NSAccessibilityInsertionPointLineNumberAttribute]) { - if (QAccessibleTextInterface *text = iface->textInterface()) { - int position = text->cursorPosition(); - return [self accessibilityAttributeValue:NSAccessibilityLineForIndexParameterizedAttribute forParameter:@(position)]; + } else { + start = text->cursorPosition(); + end = start; } - return nil; - } else if ([attribute isEqualToString:NSAccessibilityMinValueAttribute]) { - return [self minValueAttribute:iface]; - } else if ([attribute isEqualToString:NSAccessibilityMaxValueAttribute]) { - return [self maxValueAttribute:iface]; + return NSMakeRange(quint32(start), quint32(end - start)); } + return NSMakeRange(0, 0); +} - return nil; +- (NSInteger)accessibilityLineForIndex:(NSInteger)index { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return 0; + if (QAccessibleTextInterface *text = iface->textInterface()) { + QString textToPos = text->text(0, index); + return textToPos.count('\n'); + } + return 0; +} + +- (NSRange)accessibilityVisibleCharacterRange { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return NSRange(); + // FIXME This is not correct and may impact performance for big texts + if (QAccessibleTextInterface *text = iface->textInterface()) + return NSMakeRange(0, static_cast<uint>(text->characterCount())); + return NSMakeRange(0, static_cast<uint>(iface->text(QAccessible::Name).length())); +} + +- (NSInteger) accessibilityInsertionPointLineNumber { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return 0; + if (QAccessibleTextInterface *text = iface->textInterface()) { + int position = text->cursorPosition(); + return [self accessibilityLineForIndex:position]; + } + return 0; } - (NSArray *)accessibilityParameterizedAttributeNames { diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 508f24d578..acddc3ecc8 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -76,9 +76,12 @@ public: void endPaint() override; void flush(QWindow *, const QRegion &, const QPoint &) override; +#ifndef QT_NO_OPENGL void composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) override; +#endif + QImage toImage() const override; QPlatformGraphicsBuffer *graphicsBuffer() const override; private: @@ -93,6 +96,7 @@ private: QRegion dirtyRegion; // In unscaled coordinates QImage *asImage(); + qreal devicePixelRatio() const { return m_devicePixelRatio; } private: qreal m_devicePixelRatio; diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 8e4e928bc5..c381f87844 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -273,18 +273,6 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const // ---------------------------------------------------------------------------- -// https://stackoverflow.com/a/52722575/2761869 -template<class R> -struct backwards_t { - R r; - constexpr auto begin() const { using std::rbegin; return rbegin(r); } - constexpr auto begin() { using std::rbegin; return rbegin(r); } - constexpr auto end() const { using std::rend; return rend(r); } - constexpr auto end() { using std::rend; return rend(r); } -}; -template<class R> -constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; } - QCALayerBackingStore::QCALayerBackingStore(QWindow *window) : QPlatformBackingStore(window) { @@ -460,12 +448,29 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view(); + // If the backingstore is just flushed, without being painted to first, then we may + // end in a situation where the backingstore is flushed to a layer with a different + // scale factor than the one it was created for in beginPaint. This is the client's + // fault in not picking up the change in scale factor of the window and re-painting + // the backingstore accordingly. To smoothing things out, we warn about this situation, + // and change the layer's contentsScale to match the scale of the back buffer, so that + // we at least cover the whole layer. This is necessary since we set the view's + // contents placement policy to NSViewLayerContentsPlacementTopLeft, which means + // AppKit will not do any scaling on our behalf. + if (m_buffers.back()->devicePixelRatio() != flushedView.layer.contentsScale) { + qCWarning(lcQpaBackingStore) << "Back buffer dpr of" << m_buffers.back()->devicePixelRatio() + << "doesn't match" << flushedView.layer << "contents scale of" << flushedView.layer.contentsScale + << "- updating layer to match."; + flushedView.layer.contentsScale = m_buffers.back()->devicePixelRatio(); + } + id backBufferSurface = (__bridge id)m_buffers.back()->surface(); if (flushedView.layer.contents == backBufferSurface) { // We've managed to paint to the back buffer again before Core Animation had time - // to flush the transaction and persist the layer changes to the window server. - // The layer already knows about the back buffer, and we don't need to re-apply - // it to pick up the surface changes, so bail out early. + // to flush the transaction and persist the layer changes to the window server, or + // we've been asked to flush without painting anything. The layer already knows about + // the back buffer, and we don't need to re-apply it to pick up any possible surface + // changes, so bail out early. qCInfo(lcQpaBackingStore).nospace() << "Skipping flush of " << flushedView << ", layer already reflects back buffer"; return; @@ -506,6 +511,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // the window server. } +#ifndef QT_NO_OPENGL void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) { @@ -514,6 +520,22 @@ void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®io QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground); } +#endif + +QImage QCALayerBackingStore::toImage() const +{ + if (!const_cast<QCALayerBackingStore*>(this)->prepareForFlush()) + return QImage(); + + // We need to make a copy here, as the returned image could be used just + // for reading, in which case it won't detach, and then the underlying + // image data might change under the feet of the client when we re-use + // the buffer at a later point. + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); + QImage imageCopy = m_buffers.back()->asImage()->copy(); + m_buffers.back()->unlock(); + return imageCopy; +} QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const { diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index d1695ea860..5f32400af0 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -51,7 +51,6 @@ #include "qt_mac_p.h" #include "qcocoahelpers.h" #include "qcocoaeventdispatcher.h" -#include <qregexp.h> #include <qbuffer.h> #include <qdebug.h> #include <qstringlist.h> diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 69aa7937b6..69a1854598 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -176,6 +176,18 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro return fallback; } +// https://stackoverflow.com/a/52722575/2761869 +template<class R> +struct backwards_t { + R r; + constexpr auto begin() const { using std::rbegin; return rbegin(r); } + constexpr auto begin() { using std::rbegin; return rbegin(r); } + constexpr auto end() const { using std::rend; return rend(r); } + constexpr auto end() { using std::rend; return rend(r); } +}; +template<class R> +constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; } + // ------------------------------------------------------------------------- #if !defined(Q_PROCESSOR_X86_64) diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 9c705616ba..1b184cd60f 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing"); Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg); -Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen"); +Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg); // // Conversion Functions diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 04cb4e1226..bfc3bfe9de 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -61,8 +61,6 @@ QT_BEGIN_NAMESPACE -class QCocoaScreen; - class QCocoaIntegration : public QObject, public QPlatformIntegration { Q_OBJECT @@ -113,9 +111,6 @@ public: Qt::KeyboardModifiers queryKeyboardModifiers() const override; QList<int> possibleKeys(const QKeyEvent *event) const override; - void updateScreens(); - QCocoaScreen *screenForNSScreen(NSScreen *nsScreen); - void setToolbar(QWindow *window, NSToolbar *toolbar); NSToolbar *toolbar(QWindow *window) const; void clearToolbars(); @@ -143,8 +138,6 @@ private: QScopedPointer<QCocoaAccessibility> mAccessibility; #endif QScopedPointer<QPlatformTheme> mPlatformTheme; - QList<QCocoaScreen *> mScreens; - QMacScopedObserver m_screensObserver; #ifndef QT_NO_CLIPBOARD QCocoaClipboard *mCocoaClipboard; #endif diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index fb3d05d3e4..300082d694 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -207,9 +207,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) // which will resolve to an actual value and result in screen invalidation. cocoaApplication.presentationOptions = NSApplicationPresentationDefault; - m_screensObserver = QMacScopedObserver([NSApplication sharedApplication], - NSApplicationDidChangeScreenParametersNotification, [&]() { updateScreens(); }); - updateScreens(); + QCocoaScreen::initializeScreens(); QMacInternalPasteboardMime::initializeMimeTypes(); QCocoaMimeTypes::initializeMimeTypes(); @@ -242,10 +240,7 @@ QCocoaIntegration::~QCocoaIntegration() QMacInternalPasteboardMime::destroyMimeTypes(); #endif - // Delete screens in reverse order to avoid crash in case of multiple screens - while (!mScreens.isEmpty()) { - QWindowSystemInterface::handleScreenRemoved(mScreens.takeLast()); - } + QCocoaScreen::cleanupScreens(); clearToolbars(); } @@ -260,88 +255,6 @@ QCocoaIntegration::Options QCocoaIntegration::options() const return mOptions; } -/*! - \brief Synchronizes the screen list, adds new screens, removes deleted ones -*/ -void QCocoaIntegration::updateScreens() -{ - NSArray<NSScreen *> *scrs = [NSScreen screens]; - NSMutableArray<NSScreen *> *screens = [NSMutableArray<NSScreen *> arrayWithArray:scrs]; - if ([screens count] == 0) - if ([NSScreen mainScreen]) - [screens addObject:[NSScreen mainScreen]]; - if ([screens count] == 0) - return; - QSet<QCocoaScreen*> remainingScreens = QSet<QCocoaScreen*>::fromList(mScreens); - QList<QPlatformScreen *> siblings; - uint screenCount = [screens count]; - for (uint i = 0; i < screenCount; i++) { - NSScreen* scr = [screens objectAtIndex:i]; - CGDirectDisplayID dpy = scr.qt_displayId; - // If this screen is a mirror and is not the primary one of the mirror set, ignore it. - // Exception: The NSScreen API has been observed to a return a screen list with one - // mirrored, non-primary screen when Qt is running as a startup item. Always use the - // screen if there's only one screen in the list. - if (screenCount > 1 && CGDisplayIsInMirrorSet(dpy)) { - CGDirectDisplayID primary = CGDisplayMirrorsDisplay(dpy); - if (primary != kCGNullDirectDisplay && primary != dpy) - continue; - } - QCocoaScreen* screen = nullptr; - foreach (QCocoaScreen* existingScr, mScreens) { - // NSScreen documentation says do not cache the array returned from [NSScreen screens]. - // However in practice, we can identify a screen by its pointer: if resolution changes, - // the NSScreen object will be the same instance, just with different values. - if (existingScr->nativeScreen() == scr) { - screen = existingScr; - break; - } - } - if (screen) { - remainingScreens.remove(screen); - screen->updateProperties(); - } else { - screen = new QCocoaScreen(i); - mScreens.append(screen); - qCDebug(lcQpaScreen) << "Adding" << screen; - QWindowSystemInterface::handleScreenAdded(screen); - } - siblings << screen; - } - - // Set virtual siblings list. All screens in mScreens are siblings, because we ignored the - // mirrors. Note that some of the screens we update the siblings list for here may be deleted - // below, but update anyway to keep the to-be-deleted screens out of the siblings list. - foreach (QCocoaScreen* screen, mScreens) - screen->setVirtualSiblings(siblings); - - // Now the leftovers in remainingScreens are no longer current, so we can delete them. - foreach (QCocoaScreen* screen, remainingScreens) { - mScreens.removeOne(screen); - // Prevent stale references to NSScreen during destroy - screen->m_screenIndex = -1; - qCDebug(lcQpaScreen) << "Removing" << screen; - QWindowSystemInterface::handleScreenRemoved(screen); - } -} - -QCocoaScreen *QCocoaIntegration::screenForNSScreen(NSScreen *nsScreen) -{ - NSUInteger index = [[NSScreen screens] indexOfObject:nsScreen]; - if (index == NSNotFound) - return nullptr; - - if (index >= unsigned(mScreens.count())) - updateScreens(); - - for (QCocoaScreen *screen : mScreens) { - if (screen->nativeScreen() == nsScreen) - return screen; - } - - return nullptr; -} - bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { @@ -490,8 +403,13 @@ QCocoaServices *QCocoaIntegration::services() const QVariant QCocoaIntegration::styleHint(StyleHint hint) const { - if (hint == QPlatformIntegration::FontSmoothingGamma) + switch (hint) { + case FontSmoothingGamma: return QCoreTextFontEngine::fontSmoothingGamma(); + case ShowShortcutsInContextMenus: + return QVariant(false); + default: break; + } return QPlatformIntegration::styleHint(hint); } diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index e54b6284e5..32fc3ab660 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -53,6 +53,7 @@ #include <QtGui/private/qcoregraphics_p.h> #include <QtCore/QDebug> +#include <QtCore/QRegExp> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h index 9ded98df32..491af2fe9c 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.h +++ b/src/plugins/platforms/cocoa/qcocoascreen.h @@ -48,10 +48,14 @@ QT_BEGIN_NAMESPACE +class QCocoaIntegration; + class QCocoaScreen : public QPlatformScreen { public: - QCocoaScreen(int screenIndex); + static void initializeScreens(); + static void cleanupScreens(); + ~QCocoaScreen(); // ---------------------------------------------------- @@ -61,19 +65,18 @@ public: QRect availableGeometry() const override { return m_availableGeometry; } int depth() const override { return m_depth; } QImage::Format format() const override { return m_format; } - qreal devicePixelRatio() const override; + qreal devicePixelRatio() const override { return m_devicePixelRatio; } QSizeF physicalSize() const override { return m_physicalSize; } QDpi logicalDpi() const override { return m_logicalDpi; } qreal refreshRate() const override { return m_refreshRate; } QString name() const override { return m_name; } QPlatformCursor *cursor() const override { return m_cursor; } QWindow *topLevelAt(const QPoint &point) const override; - QList<QPlatformScreen *> virtualSiblings() const override { return m_siblings; } + QList<QPlatformScreen *> virtualSiblings() const override; QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override; // ---------------------------------------------------- - // Additional methods - void setVirtualSiblings(const QList<QPlatformScreen *> &siblings) { m_siblings = siblings; } + NSScreen *nativeScreen() const; void updateProperties(); @@ -82,14 +85,21 @@ public: bool isRunningDisplayLink() const; static QCocoaScreen *primaryScreen(); + static QCocoaScreen *get(NSScreen *nsScreen); + static QCocoaScreen *get(CGDirectDisplayID displayId); static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); -public: - int m_screenIndex; +private: + QCocoaScreen(CGDirectDisplayID displayId); + static void add(CGDirectDisplayID displayId); + void remove(); + + CGDirectDisplayID m_displayId = 0; + QRect m_geometry; QRect m_availableGeometry; QDpi m_logicalDpi; @@ -99,11 +109,13 @@ public: QImage::Format m_format; QSizeF m_physicalSize; QCocoaCursor *m_cursor; - QList<QPlatformScreen *> m_siblings; + qreal m_devicePixelRatio; CVDisplayLinkRef m_displayLink = nullptr; dispatch_source_t m_displayLinkSource = nullptr; QAtomicInt m_pendingUpdates; + + friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen); }; #ifndef QT_NO_DEBUG_STREAM @@ -116,5 +128,4 @@ QT_END_NAMESPACE @property(readonly) CGDirectDisplayID qt_displayId; @end -#endif - +#endif // QCOCOASCREEN_H diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 6a5b0e6e3e..392099d083 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -41,6 +41,7 @@ #include "qcocoawindow.h" #include "qcocoahelpers.h" +#include "qcocoaintegration.h" #include <QtCore/qcoreapplication.h> #include <QtGui/private/qcoregraphics_p.h> @@ -53,34 +54,104 @@ QT_BEGIN_NAMESPACE -class QCoreTextFontEngine; -class QFontEngineFT; +void QCocoaScreen::initializeScreens() +{ + uint32_t displayCount = 0; + if (CGGetActiveDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess) + qFatal("Failed to get number of active displays"); + + CGDirectDisplayID activeDisplays[displayCount]; + if (CGGetActiveDisplayList(displayCount, &activeDisplays[0], &displayCount) != kCGErrorSuccess) + qFatal("Failed to get active displays"); + + for (CGDirectDisplayID displayId : activeDisplays) + QCocoaScreen::add(displayId); + + CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) { + if (flags & kCGDisplayBeginConfigurationFlag) + return; // Wait for changes to apply + + Q_UNUSED(userInfo); + + QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId); + + if ((flags & kCGDisplayAddFlag) || !cocoaScreen) { + if (!CGDisplayIsActive(displayId)) { + qCDebug(lcQpaScreen) << "Not adding inactive display" << displayId; + return; // Will be added when activated + } + QCocoaScreen::add(displayId); + } else if ((flags & kCGDisplayRemoveFlag) || !CGDisplayIsActive(displayId)) { + cocoaScreen->remove(); + } else { + // Detect changes to the primary screen immediately, instead of + // waiting for a display reconfigure with kCGDisplaySetMainFlag. + // This ensures that any property updates to the other screens + // will be in reference to the correct primary screen. + QCocoaScreen *mainDisplay = QCocoaScreen::get(CGMainDisplayID()); + if (QGuiApplication::primaryScreen()->handle() != mainDisplay) { + mainDisplay->updateProperties(); + qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay; + QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay); + } -QCocoaScreen::QCocoaScreen(int screenIndex) - : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0) + if (cocoaScreen == mainDisplay) + return; // Already reconfigured + + cocoaScreen->updateProperties(); + qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen; + } + }, nullptr); +} + +void QCocoaScreen::add(CGDirectDisplayID displayId) +{ + QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId); + qCInfo(lcQpaScreen) << "Adding" << cocoaScreen; + QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId)); +} + +QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId) + : QPlatformScreen(), m_displayId(displayId) { updateProperties(); m_cursor = new QCocoaCursor; } -QCocoaScreen::~QCocoaScreen() +void QCocoaScreen::cleanupScreens() { - delete m_cursor; + // Remove screens in reverse order to avoid crash in case of multiple screens + for (QScreen *screen : backwards(QGuiApplication::screens())) + static_cast<QCocoaScreen*>(screen->handle())->remove(); +} - CVDisplayLinkRelease(m_displayLink); - if (m_displayLinkSource) - dispatch_release(m_displayLinkSource); +void QCocoaScreen::remove() +{ + m_displayId = 0; // Prevent stale references during removal + + // This may result in the application responding to QGuiApplication::screenRemoved + // by moving the window to another screen, either by setGeometry, or by setScreen. + // If the window isn't moved by the application, Qt will as a fallback move it to + // the primary screen via setScreen. Due to the way setScreen works, this won't + // actually recreate the window on the new screen, it will just assign the new + // QScreen to the window. The associated NSWindow will have an NSScreen determined + // by AppKit. AppKit will then move the window to another screen by changing the + // geometry, and we will get a callback in QCocoaWindow::windowDidMove and then + // QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have + // already changed its screen, but that's only true if comparing the Qt screens, + // not when comparing the NSScreens. + QWindowSystemInterface::handleScreenRemoved(this); } -NSScreen *QCocoaScreen::nativeScreen() const +QCocoaScreen::~QCocoaScreen() { - NSArray<NSScreen *> *screens = [NSScreen screens]; + Q_ASSERT_X(!screen(), "QCocoaScreen", "QScreen should be deleted first"); - // Stale reference, screen configuration has changed - if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count]) - return nil; + delete m_cursor; - return [screens objectAtIndex:m_screenIndex]; + CVDisplayLinkRelease(m_displayLink); + if (m_displayLinkSource) + dispatch_release(m_displayLinkSource); } static QString displayName(CGDirectDisplayID displayID) @@ -117,35 +188,37 @@ static QString displayName(CGDirectDisplayID displayID) void QCocoaScreen::updateProperties() { - NSScreen *nsScreen = nativeScreen(); - if (!nsScreen) - return; + Q_ASSERT(m_displayId); const QRect previousGeometry = m_geometry; const QRect previousAvailableGeometry = m_availableGeometry; const QDpi previousLogicalDpi = m_logicalDpi; const qreal previousRefreshRate = m_refreshRate; + // Some properties are only available via NSScreen + NSScreen *nsScreen = nativeScreen(); + Q_ASSERT(nsScreen); + // The reference screen for the geometry is always the primary screen - QRectF primaryScreenGeometry = QRectF::fromCGRect([[NSScreen screens] firstObject].frame); + QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID())); m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect(); m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect(); + m_devicePixelRatio = nsScreen.backingScaleFactor; + m_format = QImage::Format_RGB32; - m_depth = NSBitsPerPixelFromDepth([nsScreen depth]); + m_depth = NSBitsPerPixelFromDepth(nsScreen.depth); - CGDirectDisplayID dpy = nsScreen.qt_displayId; - CGSize size = CGDisplayScreenSize(dpy); + CGSize size = CGDisplayScreenSize(m_displayId); m_physicalSize = QSizeF(size.width, size.height); m_logicalDpi.first = 72; m_logicalDpi.second = 72; - CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy); + + QCFType<CGDisplayModeRef> displayMode = CGDisplayCopyDisplayMode(m_displayId); float refresh = CGDisplayModeGetRefreshRate(displayMode); - CGDisplayModeRelease(displayMode); - if (refresh > 0) - m_refreshRate = refresh; + m_refreshRate = refresh > 0 ? refresh : 60.0; - m_name = displayName(dpy); + m_name = displayName(m_displayId); const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry; @@ -155,24 +228,6 @@ void QCocoaScreen::updateProperties() QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second); if (m_refreshRate != previousRefreshRate) QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate); - - qCDebug(lcQpaScreen) << "Updated properties for" << this; - - if (didChangeGeometry) { - // When a screen changes its geometry, AppKit will send us a NSWindowDidMoveNotification - // for each window, resulting in calls to handleGeometryChange(), but this happens before - // the NSApplicationDidChangeScreenParametersNotification, so when we map the new geometry - // (which is correct at that point) to the screen using QCocoaScreen::mapFromNative(), we - // end up using the stale screen geometry, and the new window geometry we report is wrong. - // To make sure we finally report the correct window geometry, we need to do another pass - // of geometry reporting, now that the screen properties have been updates. FIXME: Ideally - // this would be solved by not caching the screen properties in QCocoaScreen, but that - // requires more research. - for (QWindow *window : windows()) { - if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow*>(window->handle())) - cocoaWindow->handleGeometryChange(); - } - } } // ----------------------- Display link ----------------------- @@ -181,8 +236,10 @@ Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg); void QCocoaScreen::requestUpdate() { + Q_ASSERT(m_displayId); + if (!m_displayLink) { - CVDisplayLinkCreateWithCGDisplay(nativeScreen().qt_displayId, &m_displayLink); + CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink); CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int { // FIXME: It would be nice if update requests would include timing info @@ -269,6 +326,9 @@ struct DeferredDebugHelper void QCocoaScreen::deliverUpdateRequests() { + if (!m_displayId) + return; // Screen removed + QMacAutoReleasePool pool; // The CVDisplayLink callback is a notification that it's a good time to produce a new frame. @@ -283,7 +343,7 @@ void QCocoaScreen::deliverUpdateRequests() const int pendingUpdates = ++m_pendingUpdates; DeferredDebugHelper screenUpdates(lcQpaScreenUpdates()); - qDeferredDebug(screenUpdates) << "display link callback for screen " << m_screenIndex; + qDeferredDebug(screenUpdates) << "display link callback for screen " << m_displayId; if (const int framesAheadOfDelivery = pendingUpdates - 1) { // If we have more than one update pending it means that a previous display link callback @@ -370,13 +430,6 @@ bool QCocoaScreen::isRunningDisplayLink() const // ----------------------------------------------------------- -qreal QCocoaScreen::devicePixelRatio() const -{ - QMacAutoReleasePool pool; - NSScreen *nsScreen = nativeScreen(); - return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0); -} - QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const { QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint(); @@ -430,7 +483,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) { // Determine the grab rect. FIXME: The rect should be bounded by the view's // geometry, but note that for the pixeltool use case that window will be the - // desktop widgets's view, which currently gets resized to fit one screen + // desktop widget's view, which currently gets resized to fit one screen // only, since its NSWindow has the NSWindowStyleMaskTitled flag set. Q_UNUSED(view); QRect grabRect = QRect(x, y, width, height); @@ -482,7 +535,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) for (uint i = 0; i < displayCount; ++i) dpr = qMax(dpr, images.at(i).devicePixelRatio()); - // Alocate target pixmap and draw each screen's content + // Allocate target pixmap and draw each screen's content qCDebug(lcQpaScreen) << "Create grap pixmap" << grabRect.size() << "at devicePixelRatio" << dpr; QPixmap windowPixmap(grabRect.size() * dpr); windowPixmap.setDevicePixelRatio(dpr); @@ -499,7 +552,57 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) */ QCocoaScreen *QCocoaScreen::primaryScreen() { - return static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle()); + auto screen = static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle()); + Q_ASSERT_X(screen == get(CGMainDisplayID()), "QCocoaScreen", + "The application's primary screen should always be in sync with the main display"); + return screen; +} + +QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const +{ + QList<QPlatformScreen*> siblings; + + // Screens on macOS are always part of the same virtual desktop + for (QScreen *screen : QGuiApplication::screens()) + siblings << screen->handle(); + + return siblings; +} + +QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen) +{ + return get(nsScreen.qt_displayId); +} + +QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId) +{ + for (QScreen *screen : QGuiApplication::screens()) { + QCocoaScreen *cocoaScreen = static_cast<QCocoaScreen*>(screen->handle()); + if (cocoaScreen->m_displayId == displayId) + return cocoaScreen; + } + + return nullptr; +} + +NSScreen *QCocoaScreen::nativeScreen() const +{ + if (!m_displayId) + return nil; // The display has been disconnected + + // A single display may have different displayIds depending on + // which GPU is in use or which physical port the display is + // connected to. By comparing UUIDs instead of display IDs we + // ensure that we always pick up the appropriate NSScreen. + QCFType<CFUUIDRef> uuid = CGDisplayCreateUUIDFromDisplayID(m_displayId); + + for (NSScreen *screen in [NSScreen screens]) { + if (CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId) == uuid) + return screen; + } + + qCWarning(lcQpaScreen) << "Could not find NSScreen for display ID" << m_displayId; + return nil; } CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen) @@ -533,11 +636,10 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen) debug.nospace(); debug << "QCocoaScreen(" << (const void *)screen; if (screen) { - debug << ", index=" << screen->m_screenIndex; - debug << ", native=" << screen->nativeScreen(); debug << ", geometry=" << screen->geometry(); debug << ", dpr=" << screen->devicePixelRatio(); debug << ", name=" << screen->name(); + debug << ", native=" << screen->nativeScreen(); } debug << ')'; return debug; diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h index d831612c22..6779bda491 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h @@ -42,8 +42,9 @@ #define QCOCOASYSTEMTRAYICON_P_H #include <QtCore/qglobal.h> +#include <QtGui/qtguiglobal.h> -#ifndef QT_NO_SYSTEMTRAYICON +#if QT_CONFIG(systemtrayicon) #include "QtCore/qstring.h" #include "QtGui/qpa/qplatformsystemtrayicon.h" diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index 4982f5ee05..de5cf85854 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -383,9 +383,9 @@ QT_END_NAMESPACE } - (QRectF)geometry { - if (NSWindow *window = [[item view] window]) { - if (QCocoaScreen *screen = QCocoaIntegration::instance()->screenForNSScreen([window screen])) - return screen->mapFromNative([window frame]); + if (NSWindow *window = item.view.window) { + if (QCocoaScreen *screen = QCocoaScreen::get(window.screen)) + return screen->mapFromNative(window.frame); } return QRectF(); } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 298d11fe08..67b0cbdfe9 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -1086,9 +1086,11 @@ void QCocoaWindow::setEmbeddedInForeignView() void QCocoaWindow::viewDidChangeFrame() { - if (isContentView()) - return; // Handled below - + // Note: When the view is the content view, it would seem redundant + // to deliver geometry changes both from windowDidResize and this + // callback, but in some cases such as when macOS native tabbed + // windows are enabled we may end up with the wrong geometry in + // the initial windowDidResize callback when a new tab is created. handleGeometryChange(); } @@ -1208,23 +1210,34 @@ void QCocoaWindow::windowDidChangeScreen() if (!window()) return; - const bool wasRunningDisplayLink = static_cast<QCocoaScreen *>(screen())->isRunningDisplayLink(); - - if (QCocoaScreen *newScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen)) { - if (newScreen == screen()) { - // Screen properties have changed. Will be handled by - // NSApplicationDidChangeScreenParametersNotification - // in QCocoaIntegration::updateScreens(). - return; - } - - qCDebug(lcQpaWindow) << window() << "moved to" << newScreen; - QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(window(), newScreen->screen()); - - if (hasPendingUpdateRequest() && wasRunningDisplayLink) - requestUpdate(); // Restart display-link on new screen - } else { - qCWarning(lcQpaWindow) << "Failed to get QCocoaScreen for" << m_view.window.screen; + // Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil + auto *currentScreen = QCocoaScreen::get(m_view.window.screen); + auto *previousScreen = static_cast<QCocoaScreen*>(screen()); + + Q_ASSERT_X(!m_view.window.screen || currentScreen, + "QCocoaWindow", "Failed to get QCocoaScreen for NSScreen"); + + // Note: The previous screen may be the same as the current screen, either because + // a) the screen was just reconfigured, which still results in AppKit sending an + // NSWindowDidChangeScreenNotification, b) because the previous screen was removed, + // and we ended up calling QWindow::setScreen to move the window, which doesn't + // actually move the window to the new screen, or c) because we've delivered the + // screen change to the top level window, which will make all the child windows + // of that window report the new screen when requested via QWindow::screen(). + // We still need to deliver the screen change in all these cases, as the + // device-pixel ratio may have changed, and needs to be delivered to all + // windows, both top level and child windows. + + qCDebug(lcQpaWindow) << "Screen changed for" << window() << "from" << previousScreen << "to" << currentScreen; + QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>( + window(), currentScreen ? currentScreen->screen() : nullptr); + + if (currentScreen && hasPendingUpdateRequest()) { + // Restart display-link on new screen. We need to do this unconditionally, + // since we can't rely on the previousScreen reflecting whether or not the + // window actually moved from one screen to another, or just stayed on the + // same screen. + currentScreen->requestUpdate(); } } diff --git a/src/plugins/platforms/cocoa/qmacclipboard.mm b/src/plugins/platforms/cocoa/qmacclipboard.mm index ba6cfca219..358a6b49fd 100644 --- a/src/plugins/platforms/cocoa/qmacclipboard.mm +++ b/src/plugins/platforms/cocoa/qmacclipboard.mm @@ -489,19 +489,16 @@ QMacPasteboard::retrieveData(const QString &format, QVariant::Type) const QMacInternalPasteboardMime *c = mimes.at(mime); QString c_flavor = c->flavorFor(format); if (!c_flavor.isEmpty()) { - // Handle text/plain a little differently. Try handling Unicode first. - bool checkForUtf16 = (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text") - || c_flavor == QLatin1String("public.utf8-plain-text")); - if (checkForUtf16 || c_flavor == QLatin1String("public.utf16-plain-text")) { - // Try to get the NSStringPboardType from NSPasteboard, newlines are mapped - // correctly (as '\n') in this data. The 'public.utf16-plain-text' type - // usually maps newlines to '\r' instead. + // Converting via PasteboardCopyItemFlavorData below will for some UITs result + // in newlines mapping to '\r' instead of '\n'. To work around this we shortcut + // the conversion via NSPasteboard's NSStringPboardType if possible. + if (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text") + || c_flavor == QLatin1String("public.utf8-plain-text") + || c_flavor == QLatin1String("public.utf16-plain-text")) { QString str = qt_mac_get_pasteboardString(paste); if (!str.isEmpty()) return str; } - if (checkForUtf16 && hasFlavor(QLatin1String("public.utf16-plain-text"))) - c_flavor = QLatin1String("public.utf16-plain-text"); QVariant ret; QList<QByteArray> retList; diff --git a/src/plugins/platforms/cocoa/qnsview_dragging.mm b/src/plugins/platforms/cocoa/qnsview_dragging.mm index 002cb3279e..37e972dba9 100644 --- a/src/plugins/platforms/cocoa/qnsview_dragging.mm +++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm @@ -293,7 +293,26 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation)); + // Qt starts drag-and-drop on a mouse button press event. Cococa in + // this case won't send the matching release event, so we have to + // synthesize it here. m_buttons = currentlyPressedMouseButtons(); + const auto modifiers = [QNSView convertKeyModifiers:NSApp.currentEvent.modifierFlags]; + + NSPoint windowPoint = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 1, 1)].origin; + NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; + + QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y); + QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint(); + + QWindowSystemInterface::handleMouseEvent( + target, + mapWindowCoordinates(m_platformWindow->window(), target, qtWindowPoint), + qtScreenPoint, + m_buttons, + Qt::NoButton, + QEvent::MouseButtonRelease, + modifiers); qCDebug(lcQpaMouse) << "Drag session" << session << "ended, with" << m_buttons; } diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm index cb1799b039..d2e6f848a0 100644 --- a/src/plugins/platforms/cocoa/qnsview_drawing.mm +++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm @@ -154,6 +154,7 @@ << "due to being" << ([self layerExplicitlyRequested] ? "explicitly requested" : [self shouldUseMetalLayer] ? "needed by surface type" : "enabled by macOS"); [super setLayer:layer]; + layer.delegate = self; } - (NSViewLayerContentsRedrawPolicy)layerContentsRedrawPolicy @@ -188,6 +189,15 @@ - (void)displayLayer:(CALayer *)layer { + if (!NSThread.isMainThread) { + // Qt is calling AppKit APIs such as -[NSOpenGLContext setView:] on secondary threads, + // which we shouldn't do. This may result in AppKit (wrongly) triggering a display on + // the thread where we made the call, so block it here and defer to the main thread. + qCWarning(lcQpaDrawing) << "Display non non-main thread! Deferring to main thread"; + dispatch_async(dispatch_get_main_queue(), ^{ self.needsDisplay = YES; }); + return; + } + Q_ASSERT(layer == self.layer); if (!m_platformWindow) diff --git a/src/plugins/platforms/cocoa/qnsview_gestures.mm b/src/plugins/platforms/cocoa/qnsview_gestures.mm index 61d551ee0e..f6cd3af4da 100644 --- a/src/plugins/platforms/cocoa/qnsview_gestures.mm +++ b/src/plugins/platforms/cocoa/qnsview_gestures.mm @@ -70,7 +70,7 @@ Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") if ([self handleGestureAsBeginEnd:event]) return; - qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification] << "from device" << hex << [event deviceID]; + qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification] << "from device" << Qt::hex << [event deviceID]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; @@ -85,7 +85,7 @@ Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") return; static bool zoomIn = true; - qCDebug(lcQpaGestures) << "smartMagnifyWithEvent" << zoomIn << "from device" << hex << [event deviceID]; + qCDebug(lcQpaGestures) << "smartMagnifyWithEvent" << zoomIn << "from device" << Qt::hex << [event deviceID]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; @@ -116,7 +116,7 @@ Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") if (!m_platformWindow) return; - qCDebug(lcQpaGestures) << "swipeWithEvent" << [event deltaX] << [event deltaY] << "from device" << hex << [event deviceID]; + qCDebug(lcQpaGestures) << "swipeWithEvent" << [event deltaX] << [event deltaY] << "from device" << Qt::hex << [event deviceID]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; @@ -145,7 +145,7 @@ Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - qCDebug(lcQpaGestures) << "beginGestureWithEvent @" << windowPoint << "from device" << hex << [event deviceID]; + qCDebug(lcQpaGestures) << "beginGestureWithEvent @" << windowPoint << "from device" << Qt::hex << [event deviceID]; QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::BeginNativeGesture, windowPoint, screenPoint); } @@ -155,7 +155,7 @@ Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") if (!m_platformWindow) return; - qCDebug(lcQpaGestures) << "endGestureWithEvent" << "from device" << hex << [event deviceID]; + qCDebug(lcQpaGestures) << "endGestureWithEvent" << "from device" << Qt::hex << [event deviceID]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm index 0ab09b97d3..4408385aa8 100644 --- a/src/plugins/platforms/cocoa/qnsview_mouse.mm +++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm @@ -147,9 +147,34 @@ ulong timestamp = [theEvent timestamp] * 1000; - auto eventType = cocoaEvent2QtMouseEvent(theEvent); - qCInfo(lcQpaMouse) << "Frame-strut" << eventType << "at" << qtWindowPoint << "with" << m_frameStrutButtons << "in" << self.window; - QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons); + const auto button = cocoaButton2QtButton(theEvent); + auto eventType = [&]() { + switch (theEvent.type) { + case NSEventTypeLeftMouseDown: + case NSEventTypeRightMouseDown: + case NSEventTypeOtherMouseDown: + return QEvent::NonClientAreaMouseButtonPress; + + case NSEventTypeLeftMouseUp: + case NSEventTypeRightMouseUp: + case NSEventTypeOtherMouseUp: + return QEvent::NonClientAreaMouseButtonRelease; + + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeOtherMouseDragged: + return QEvent::NonClientAreaMouseMove; + + default: + break; + } + + return QEvent::None; + }(); + + qCInfo(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << m_frameStrutButtons << "in" << self.window; + QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), + timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons, button, eventType); } @end @@ -477,12 +502,15 @@ // uses the legacy cursorRect API, so the cursor is reset to the arrow // cursor. See rdar://34183708 - if (self.cursor && self.cursor != NSCursor.currentCursor) { - qCInfo(lcQpaMouse) << "Updating cursor for" << self << "to" << self.cursor; + auto previousCursor = NSCursor.currentCursor; + + if (self.cursor) [self.cursor set]; - } else { + else [super cursorUpdate:theEvent]; - } + + if (NSCursor.currentCursor != previousCursor) + qCInfo(lcQpaMouse) << "Cursor update for" << self << "resulted in new cursor" << NSCursor.currentCursor; } - (void)mouseMovedImpl:(NSEvent *)theEvent diff --git a/src/plugins/platforms/cocoa/qnsview_touch.mm b/src/plugins/platforms/cocoa/qnsview_touch.mm index e789213f70..9330844aec 100644 --- a/src/plugins/platforms/cocoa/qnsview_touch.mm +++ b/src/plugins/platforms/cocoa/qnsview_touch.mm @@ -60,7 +60,7 @@ Q_LOGGING_CATEGORY(lcQpaTouch, "qt.qpa.input.touch") const NSTimeInterval timestamp = [event timestamp]; const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); - qCDebug(lcQpaTouch) << "touchesBeganWithEvent" << points << "from device" << hex << [event deviceID]; + qCDebug(lcQpaTouch) << "touchesBeganWithEvent" << points << "from device" << Qt::hex << [event deviceID]; QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); } @@ -71,7 +71,7 @@ Q_LOGGING_CATEGORY(lcQpaTouch, "qt.qpa.input.touch") const NSTimeInterval timestamp = [event timestamp]; const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); - qCDebug(lcQpaTouch) << "touchesMovedWithEvent" << points << "from device" << hex << [event deviceID]; + qCDebug(lcQpaTouch) << "touchesMovedWithEvent" << points << "from device" << Qt::hex << [event deviceID]; QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); } @@ -82,7 +82,7 @@ Q_LOGGING_CATEGORY(lcQpaTouch, "qt.qpa.input.touch") const NSTimeInterval timestamp = [event timestamp]; const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); - qCDebug(lcQpaTouch) << "touchesEndedWithEvent" << points << "from device" << hex << [event deviceID]; + qCDebug(lcQpaTouch) << "touchesEndedWithEvent" << points << "from device" << Qt::hex << [event deviceID]; QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); } @@ -93,7 +93,7 @@ Q_LOGGING_CATEGORY(lcQpaTouch, "qt.qpa.input.touch") const NSTimeInterval timestamp = [event timestamp]; const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); - qCDebug(lcQpaTouch) << "touchesCancelledWithEvent" << points << "from device" << hex << [event deviceID]; + qCDebug(lcQpaTouch) << "touchesCancelledWithEvent" << points << "from device" << Qt::hex << [event deviceID]; QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); } diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm index 087cb3651f..9502a315d8 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm @@ -51,7 +51,9 @@ static QRegExp whitespaceRegex = QRegExp(QStringLiteral("\\s*")); static QCocoaWindow *toPlatformWindow(NSWindow *window) { - return qnsview_cast(window.contentView).platformWindow; + if ([window conformsToProtocol:@protocol(QNSWindowProtocol)]) + return static_cast<QCocoaNSWindow *>(window).platformWindow; + return nullptr; } @implementation QNSWindowDelegate diff --git a/src/plugins/platforms/direct2d/direct2d.pro b/src/plugins/platforms/direct2d/direct2d.pro index 9764272632..6e73bd14f9 100644 --- a/src/plugins/platforms/direct2d/direct2d.pro +++ b/src/plugins/platforms/direct2d/direct2d.pro @@ -8,8 +8,8 @@ QT += \ qtConfig(accessibility): QT += accessibility_support-private qtConfig(vulkan): QT += vulkan_support-private -LIBS += -ldwmapi -lversion -lgdi32 -QMAKE_USE_PRIVATE += dwrite_1 d2d1_1 d3d11_1 dxgi1_2 +LIBS += -ldwmapi -lversion +QMAKE_USE_PRIVATE += gdi32 dwrite_1 d2d1_1 d3d11_1 dxgi1_2 include(../windows/windows.pri) diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp index 48469b0f8c..c8a1ddf9b9 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp @@ -199,6 +199,10 @@ QPlatformWindow *QEglFSIntegration::createPlatformWindow(QWindow *window) const QEglFSWindow *w = qt_egl_device_integration()->createWindow(window); w->create(); + const auto showWithoutActivating = window->property("_q_showWithoutActivating"); + if (showWithoutActivating.isValid() && showWithoutActivating.toBool()) + return w; + // Activate only the window for the primary screen to make input work if (window->type() != Qt::ToolTip && window->screen() == QGuiApplication::primaryScreen()) w->requestActivateWindow(); diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp index 98e9ee4728..c1d5af47aa 100644 --- a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp @@ -167,6 +167,9 @@ void QEglFSWindow::create() void QEglFSWindow::destroy() { + if (!m_flags.testFlag(Created)) + return; // already destroyed + #ifndef QT_NO_OPENGL QOpenGLCompositor::instance()->removeWindow(this); #endif diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp index 24051c352e..09a10bcc9c 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp @@ -157,7 +157,7 @@ gbm_surface *QEglFSKmsGbmScreen::createSurface(EGLConfig eglConfig) const auto gbmDevice = static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice(); EGLint native_format = -1; EGLBoolean success = eglGetConfigAttrib(display(), eglConfig, EGL_NATIVE_VISUAL_ID, &native_format); - qCDebug(qLcEglfsKmsDebug) << "Got native format" << hex << native_format << dec << "from eglGetConfigAttrib() with return code" << bool(success); + qCDebug(qLcEglfsKmsDebug) << "Got native format" << Qt::hex << native_format << Qt::dec << "from eglGetConfigAttrib() with return code" << bool(success); if (success) m_gbm_surface = gbm_surface_create(gbmDevice, diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h index a19cf7e8bc..ee4b7978f1 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h @@ -55,6 +55,9 @@ public: : QEglFSWindow(w), m_integration(integration) { } + + ~QEglFSKmsGbmWindow() { destroy(); } + void resetSurface() override; void invalidateSurface() override; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp index ef732f9832..2c6b4245b7 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp @@ -117,6 +117,8 @@ public: , m_framePending(false) { } + ~QEglFSKmsEglDeviceWindow() { destroy(); } + void invalidateSurface() override; void resetSurface() override; void flip(); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp index 0d9b6b6290..d1250ec9bf 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp @@ -205,6 +205,9 @@ public: : QEglFSWindow(w) , m_integration(integration) {} + + ~QEglFSKmsVsp2Window() { destroy(); } + void resetSurface() override; void invalidateSurface() override; const QEglFSKmsVsp2Integration *m_integration; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp index 738c30c65a..b8f04cf978 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp @@ -221,7 +221,7 @@ EGLNativeWindowType QEglFSOpenWFDIntegration::createNativeWindow(QPlatformWindow QSurfaceFormat QEglFSOpenWFDIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const { - QSurfaceFormat format; + QSurfaceFormat format = inputFormat; format.setRedBufferSize(8); format.setGreenBufferSize(8); format.setBlueBufferSize(8); diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm index e5419b1766..0561a826c6 100644 --- a/src/plugins/platforms/ios/qiostextinputoverlay.mm +++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm @@ -834,9 +834,14 @@ static void executeBlockWithoutAnimation(Block block) - (void)updateSelection { if (!hasSelection()) { - _cursorLayer.visible = NO; - _anchorLayer.visible = NO; - QIOSTextInputOverlay::s_editMenu.visible = NO; + if (_cursorLayer.visible) { + _cursorLayer.visible = NO; + _anchorLayer.visible = NO; + // Only hide the edit menu if we had a selection from before, since + // the edit menu can also be used for other purposes by others (in + // which case we try our best not to interfere). + QIOSTextInputOverlay::s_editMenu.visible = NO; + } return; } diff --git a/src/plugins/platforms/openwfd/qopenwfdscreen.h b/src/plugins/platforms/openwfd/qopenwfdscreen.h index dede0025a9..dec51d306b 100644 --- a/src/plugins/platforms/openwfd/qopenwfdscreen.h +++ b/src/plugins/platforms/openwfd/qopenwfdscreen.h @@ -48,7 +48,6 @@ #include <WF/wfd.h> #include <QtCore/QVarLengthArray> -#include <QtCore/QLinkedList> #define BUFFER_NUM 4 diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index 96bfa1dd19..bfd56e8d13 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -33,6 +33,7 @@ QT += \ SOURCES = main.cpp \ qqnxbuffer.cpp \ + qqnxforeignwindow.cpp \ qqnxintegration.cpp \ qqnxscreen.cpp \ qqnxwindow.cpp \ @@ -50,6 +51,7 @@ SOURCES = main.cpp \ HEADERS = main.h \ qqnxbuffer.h \ + qqnxforeignwindow.h \ qqnxkeytranslator.h \ qqnxintegration.h \ qqnxscreen.h \ diff --git a/src/plugins/platforms/qnx/qqnxforeignwindow.cpp b/src/plugins/platforms/qnx/qqnxforeignwindow.cpp new file mode 100644 index 0000000000..94608215dc --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxforeignwindow.cpp @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** Copyright (C) 2018 QNX Software Systems. All rights reserved. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqnxforeignwindow.h" +#include "qqnxintegration.h" + +QT_BEGIN_NAMESPACE + +QQnxForeignWindow::QQnxForeignWindow(QWindow *window, + screen_context_t context, + screen_window_t screenWindow) + : QQnxWindow(window, context, screenWindow) +{ + initWindow(); +} + +bool QQnxForeignWindow::isForeignWindow() const +{ + return true; +} + +int QQnxForeignWindow::pixelFormat() const +{ + int result = SCREEN_FORMAT_RGBA8888; + screen_get_window_property_iv(nativeHandle(), SCREEN_PROPERTY_FORMAT, &result); + return result; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxforeignwindow.h b/src/plugins/platforms/qnx/qqnxforeignwindow.h new file mode 100644 index 0000000000..22dde643e4 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxforeignwindow.h @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** Copyright (C) 2018 QNX Software Systems. All rights reserved. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQNXFOREIGNWINDOW_H +#define QQNXFOREIGNWINDOW_H + +#include "qqnxwindow.h" + +QT_BEGIN_NAMESPACE + +class QQnxForeignWindow : public QQnxWindow +{ +public: + QQnxForeignWindow(QWindow *window, + screen_context_t context, + screen_window_t screenWindow); + + bool isForeignWindow() const override; + int pixelFormat() const override; + void resetBuffers() override {} +}; + +QT_END_NAMESPACE + +#endif // QQNXFOREIGNWINDOW_H diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index a45dcabeb7..f479e94988 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -51,6 +51,7 @@ #include "qqnxabstractvirtualkeyboard.h" #include "qqnxservices.h" +#include "qqnxforeignwindow.h" #include "qqnxrasterwindow.h" #if !defined(QT_NO_OPENGL) #include "qqnxeglwindow.h" @@ -147,6 +148,7 @@ static inline int getContextCapabilities(const QStringList ¶mList) QQnxIntegration::QQnxIntegration(const QStringList ¶mList) : QPlatformIntegration() + , m_screenContextId(256, 0) , m_screenEventThread(0) , m_navigatorEventHandler(new QQnxNavigatorEventHandler()) , m_virtualKeyboard(0) @@ -178,6 +180,11 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) qFatal("%s - Screen: Failed to create screen context - Error: %s (%i)", Q_FUNC_INFO, strerror(errno), errno); } + screen_get_context_property_cv(m_screenContext, + SCREEN_PROPERTY_ID, + m_screenContextId.size(), + m_screenContextId.data()); + m_screenContextId.resize(strlen(m_screenContextId.constData())); #if QT_CONFIG(qqnx_pps) // Create/start navigator event notifier @@ -310,6 +317,7 @@ bool QQnxIntegration::hasCapability(QPlatformIntegration::Capability cap) const qIntegrationDebug(); switch (cap) { case MultipleWindows: + case ForeignWindows: case ThreadedPixmaps: return true; #if !defined(QT_NO_OPENGL) @@ -323,6 +331,18 @@ bool QQnxIntegration::hasCapability(QPlatformIntegration::Capability cap) const } } +QPlatformWindow *QQnxIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const +{ + screen_window_t screenWindow = reinterpret_cast<screen_window_t>(nativeHandle); + if (this->window(screenWindow)) { + qWarning() << "QWindow already created for foreign window" + << screenWindow; + return nullptr; + } + + return new QQnxForeignWindow(window, m_screenContext, screenWindow); +} + QPlatformWindow *QQnxIntegration::createPlatformWindow(QWindow *window) const { qIntegrationDebug(); @@ -478,7 +498,7 @@ QPlatformServices * QQnxIntegration::services() const return m_services; } -QWindow *QQnxIntegration::window(screen_window_t qnxWindow) +QWindow *QQnxIntegration::window(screen_window_t qnxWindow) const { qIntegrationDebug(); QMutexLocker locker(&m_windowMapperMutex); @@ -587,12 +607,11 @@ QList<screen_display_t *> QQnxIntegration::sortDisplays(screen_display_t *availa // Move all displays with matching ID from the intermediate list // to the beginning of the ordered list - QMutableListIterator<screen_display_t *> iter(allDisplays); - while (iter.hasNext()) { - screen_display_t *display = iter.next(); + for (auto it = allDisplays.begin(), end = allDisplays.end(); it != end; ++it) { + screen_display_t *display = *it; if (getIdOfDisplay(*display) == requestedValue) { orderedDisplays.append(display); - iter.remove(); + allDisplays.erase(it); break; } } @@ -706,6 +725,11 @@ screen_context_t QQnxIntegration::screenContext() return m_screenContext; } +QByteArray QQnxIntegration::screenContextId() +{ + return m_screenContextId; +} + QQnxNavigatorEventHandler *QQnxIntegration::navigatorEventHandler() { return m_navigatorEventHandler; diff --git a/src/plugins/platforms/qnx/qqnxintegration.h b/src/plugins/platforms/qnx/qqnxintegration.h index 366556dc4b..0bf37880d1 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.h +++ b/src/plugins/platforms/qnx/qqnxintegration.h @@ -92,6 +92,7 @@ public: bool hasCapability(QPlatformIntegration::Capability cap) const override; + QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override; QPlatformWindow *createPlatformWindow(QWindow *window) const override; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; @@ -123,7 +124,7 @@ public: QPlatformServices *services() const override; - QWindow *window(screen_window_t qnxWindow); + QWindow *window(screen_window_t qnxWindow) const; QQnxScreen *screenForNative(screen_display_t qnxScreen) const; @@ -132,6 +133,7 @@ public: QQnxScreen *primaryDisplay() const; Options options() const; screen_context_t screenContext(); + QByteArray screenContextId(); QQnxNavigatorEventHandler *navigatorEventHandler(); @@ -145,6 +147,7 @@ private: int displayCount); screen_context_t m_screenContext; + QByteArray m_screenContextId; QQnxScreenEventThread *m_screenEventThread; QQnxNavigatorEventHandler *m_navigatorEventHandler; QQnxAbstractVirtualKeyboard *m_virtualKeyboard; @@ -168,7 +171,7 @@ private: QSimpleDrag *m_drag; #endif QQnxWindowMapper m_windowMapper; - QMutex m_windowMapperMutex; + mutable QMutex m_windowMapperMutex; Options m_options; diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index c2471751f5..56131dcc48 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -45,6 +45,7 @@ #include "qqnxkeytranslator.h" #include "qqnxscreen.h" #include "qqnxscreeneventfilter.h" +#include "qqnxscreentraits.h" #include <QDebug> #include <QGuiApplication> @@ -89,6 +90,51 @@ static QString capKeyString(int cap, int modifiers, int key) return QString(); } +template <typename T> +static void finishCloseEvent(screen_event_t event) +{ + T t; + screen_get_event_property_pv(event, + screen_traits<T>::propertyName, + reinterpret_cast<void**>(&t)); + screen_traits<T>::destroy(t); +} + +static void finishCloseEvent(screen_event_t event) +{ + // Let libscreen know that we're finished with anything that may have been acquired. + int objectType = SCREEN_OBJECT_TYPE_CONTEXT; + screen_get_event_property_iv(event, SCREEN_PROPERTY_OBJECT_TYPE, &objectType); + switch (objectType) { + case SCREEN_OBJECT_TYPE_CONTEXT: + finishCloseEvent<screen_context_t>(event); + break; + case SCREEN_OBJECT_TYPE_DEVICE: + finishCloseEvent<screen_device_t>(event); + break; + case SCREEN_OBJECT_TYPE_DISPLAY: + // no screen_destroy_display + break; + case SCREEN_OBJECT_TYPE_GROUP: + finishCloseEvent<screen_group_t>(event); + break; + case SCREEN_OBJECT_TYPE_PIXMAP: + finishCloseEvent<screen_pixmap_t>(event); + break; + case SCREEN_OBJECT_TYPE_SESSION: + finishCloseEvent<screen_session_t>(event); + break; +#if _SCREEN_VERSION >= _SCREEN_MAKE_VERSION(2, 0, 0) + case SCREEN_OBJECT_TYPE_STREAM: + finishCloseEvent<screen_stream_t>(event); + break; +#endif + case SCREEN_OBJECT_TYPE_WINDOW: + finishCloseEvent<screen_window_t>(event); + break; + } +} + QT_BEGIN_NAMESPACE QQnxScreenEventHandler::QQnxScreenEventHandler(QQnxIntegration *integration) @@ -251,6 +297,9 @@ void QQnxScreenEventHandler::processEvents() bool handled = dispatcher && dispatcher->filterNativeEvent(QByteArrayLiteral("screen_event_t"), event, &result); if (!handled) handleEvent(event); + + if (type == SCREEN_EVENT_CLOSE) + finishCloseEvent(event); } m_eventThread->armEventsPending(count); diff --git a/src/plugins/platforms/qnx/qqnxscreentraits.h b/src/plugins/platforms/qnx/qqnxscreentraits.h new file mode 100644 index 0000000000..ebd74141f2 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxscreentraits.h @@ -0,0 +1,127 @@ +/*************************************************************************** +** +** Copyright (C) 2018 QNX Software Systems. All rights reserved. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQNXSCREENTRAITS_H +#define QQNXSCREENTRAITS_H + +#include <screen/screen.h> + +QT_BEGIN_NAMESPACE + +template <typename T> +class screen_traits +{ +}; + +template <> +class screen_traits<screen_context_t> +{ +public: + typedef screen_context_t screen_type; + static const int propertyName = SCREEN_PROPERTY_CONTEXT; + static int destroy(screen_context_t context) { return screen_destroy_context(context); } +}; + +template <> +class screen_traits<screen_device_t> +{ +public: + typedef screen_device_t screen_type; + static const int propertyName = SCREEN_PROPERTY_DEVICE; + static int destroy(screen_device_t device) { return screen_destroy_device(device); } +}; + +template <> +class screen_traits<screen_display_t> +{ +public: + typedef screen_display_t screen_type; + static const int propertyName = SCREEN_PROPERTY_DISPLAY; +}; + +template <> +class screen_traits<screen_group_t> +{ +public: + typedef screen_group_t screen_type; + static const int propertyName = SCREEN_PROPERTY_GROUP; + static int destroy(screen_group_t group) { return screen_destroy_group(group); } +}; + +template <> +class screen_traits<screen_pixmap_t> +{ +public: + typedef screen_pixmap_t screen_type; + static const int propertyName = SCREEN_PROPERTY_PIXMAP; + static int destroy(screen_pixmap_t pixmap) { return screen_destroy_pixmap(pixmap); } +}; + +template <> +class screen_traits<screen_session_t> +{ +public: + typedef screen_session_t screen_type; + static const int propertyName = SCREEN_PROPERTY_SESSION; + static int destroy(screen_session_t session) { return screen_destroy_session(session); } +}; + +#if _SCREEN_VERSION >= _SCREEN_MAKE_VERSION(2, 0, 0) +template <> +class screen_traits<screen_stream_t> +{ +public: + typedef screen_stream_t screen_type; + static const int propertyName = SCREEN_PROPERTY_STREAM; + static int destroy(screen_stream_t stream) { return screen_destroy_stream(stream); } +}; +#endif + +template <> +class screen_traits<screen_window_t> +{ +public: + typedef screen_window_t screen_type; + static const int propertyName = SCREEN_PROPERTY_WINDOW; + static int destroy(screen_window_t window) { return screen_destroy_window(window); } +}; + +QT_END_NAMESPACE + +#endif // QQNXSCREENTRAITS_H diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index 7644e28b44..1d3d609017 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -155,6 +155,7 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW m_parentWindow(0), m_visible(false), m_exposed(true), + m_foreign(false), m_windowState(Qt::WindowNoState), m_mmRendererWindow(0), m_firstActivateHandled(false) @@ -254,6 +255,39 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW } } +QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, screen_window_t screenWindow) + : QPlatformWindow(window) + , m_screenContext(context) + , m_window(screenWindow) + , m_screen(0) + , m_parentWindow(0) + , m_visible(false) + , m_exposed(true) + , m_foreign(true) + , m_windowState(Qt::WindowNoState) + , m_mmRendererWindow(0) + , m_parentGroupName(256, 0) + , m_isTopLevel(false) +{ + qWindowDebug() << "window =" << window << ", size =" << window->size(); + + collectWindowGroup(); + + screen_get_window_property_cv(m_window, + SCREEN_PROPERTY_PARENT, + m_parentGroupName.size(), + m_parentGroupName.data()); + m_parentGroupName.resize(strlen(m_parentGroupName.constData())); + + // If a window group has been provided join it now. If it's an empty string that's OK too, + // it'll cause us not to join a group (the app will presumably join at some future time). + QVariant parentGroup = window->property("qnxInitialWindowGroup"); + if (!parentGroup.isValid()) + parentGroup = window->property("_q_platform_qnxParentGroup"); + if (parentGroup.isValid() && parentGroup.canConvert<QByteArray>()) + joinWindowGroup(parentGroup.toByteArray()); +} + QQnxWindow::~QQnxWindow() { qWindowDebug() << "window =" << window(); @@ -270,7 +304,11 @@ QQnxWindow::~QQnxWindow() m_screen->updateHierarchy(); // Cleanup QNX window and its buffers - screen_destroy_window(m_window); + // Foreign windows are cleaned up externally after the CLOSE event has been handled. + if (m_foreign) + removeContextPermission(); + else + screen_destroy_window(m_window); } void QQnxWindow::setGeometry(const QRect &rect) @@ -793,14 +831,24 @@ void QQnxWindow::initWindow() setGeometryHelper(shouldMakeFullScreen() ? screen()->geometry() : window()->geometry()); } -void QQnxWindow::createWindowGroup() +void QQnxWindow::collectWindowGroup() { - // Generate a random window group name - m_windowGroupName = QUuid::createUuid().toByteArray(); + QByteArray groupName(256, 0); + Q_SCREEN_CHECKERROR(screen_get_window_property_cv(m_window, + SCREEN_PROPERTY_GROUP, + groupName.size(), + groupName.data()), + "Failed to retrieve window group"); + groupName.resize(strlen(groupName.constData())); + m_windowGroupName = groupName; +} - // Create window group so child windows can be parented by container window - Q_SCREEN_CHECKERROR(screen_create_window_group(m_window, m_windowGroupName.constData()), +void QQnxWindow::createWindowGroup() +{ + Q_SCREEN_CHECKERROR(screen_create_window_group(m_window, nullptr), "Failed to create window group"); + + collectWindowGroup(); } void QQnxWindow::joinWindowGroup(const QByteArray &groupName) @@ -809,6 +857,17 @@ void QQnxWindow::joinWindowGroup(const QByteArray &groupName) qWindowDebug() << "group:" << groupName; + // screen has this annoying habit of generating a CLOSE/CREATE when the owner context of + // the parent group moves a foreign window to another group that it also owns. The + // CLOSE/CREATE changes the identity of the foreign window. Usually, this is undesirable. + // To prevent this CLOSE/CREATE when changing the parent group, we temporarily add a + // context permission for the Qt context. screen won't send a CLOSE/CREATE when the + // context has some permission other than the PARENT permission. If there isn't a new + // group (the window has no parent), this context permission is left in place. + + if (m_foreign && !m_parentGroupName.isEmpty())\ + addContextPermission(); + if (!groupName.isEmpty()) { if (groupName != m_parentGroupName) { screen_join_window_group(m_window, groupName); @@ -827,6 +886,9 @@ void QQnxWindow::joinWindowGroup(const QByteArray &groupName) m_parentGroupName = ""; } + if (m_foreign && !groupName.isEmpty()) + removeContextPermission(); + if (changed) screen_flush_context(m_screenContext, 0); } @@ -899,4 +961,26 @@ bool QQnxWindow::focusable() const return (window()->flags() & Qt::WindowDoesNotAcceptFocus) != Qt::WindowDoesNotAcceptFocus; } +void QQnxWindow::addContextPermission() +{ + QByteArray grantString("context:"); + grantString.append(QQnxIntegration::instance()->screenContextId()); + grantString.append(":rw-"); + screen_set_window_property_cv(m_window, + SCREEN_PROPERTY_PERMISSIONS, + grantString.length(), + grantString.data()); +} + +void QQnxWindow::removeContextPermission() +{ + QByteArray revokeString("context:"); + revokeString.append(QQnxIntegration::instance()->screenContextId()); + revokeString.append(":---"); + screen_set_window_property_cv(m_window, + SCREEN_PROPERTY_PERMISSIONS, + revokeString.length(), + revokeString.data()); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h index 20c38cb4b7..9040619c41 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.h +++ b/src/plugins/platforms/qnx/qqnxwindow.h @@ -64,7 +64,8 @@ class QQnxWindow : public QPlatformWindow { friend class QQnxScreen; public: - QQnxWindow(QWindow *window, screen_context_t context, bool needRootWindow); + explicit QQnxWindow(QWindow *window, screen_context_t context, bool needRootWindow); + explicit QQnxWindow(QWindow *window, screen_context_t context, screen_window_t screenWindow); virtual ~QQnxWindow(); void setGeometry(const QRect &rect) override; @@ -124,6 +125,7 @@ protected: screen_context_t m_screenContext; private: + void collectWindowGroup(); void createWindowGroup(); void setGeometryHelper(const QRect &rect); void removeFromParent(); @@ -135,6 +137,9 @@ private: bool showWithoutActivating() const; bool focusable() const; + void addContextPermission(); + void removeContextPermission(); + screen_window_t m_window; QSize m_bufferSize; @@ -144,6 +149,7 @@ private: QScopedPointer<QQnxAbstractCover> m_cover; bool m_visible; bool m_exposed; + bool m_foreign; QRect m_unmaximizedGeometry; Qt::WindowStates m_windowState; QString m_mmRendererWindowName; diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js index 2db7723ae2..ef4a6ec2b9 100644 --- a/src/plugins/platforms/wasm/qtloader.js +++ b/src/plugins/platforms/wasm/qtloader.js @@ -124,6 +124,8 @@ // Remove canvas at run-time. Removes the corresponding QScreen. // resizeCanvasElement // Signals to the application that a canvas has been resized. +// setFontDpi +// Sets the logical font dpi for the application. var Module = {} @@ -237,6 +239,8 @@ function QtLoader(config) publicAPI.addCanvasElement = addCanvasElement; publicAPI.removeCanvasElement = removeCanvasElement; publicAPI.resizeCanvasElement = resizeCanvasElement; + publicAPI.setFontDpi = setFontDpi; + publicAPI.fontDpi = fontDpi; restartCount = 0; @@ -404,7 +408,7 @@ function QtLoader(config) Module.preRun = Module.preRun || [] Module.preRun.push(function() { for (var [key, value] of Object.entries(config.environment)) { - Module.ENV[key.toUpperCase()] = value; + ENV[key.toUpperCase()] = value; } }); @@ -557,6 +561,16 @@ function QtLoader(config) Module.qtResizeCanvasElement(element); } + function setFontDpi(dpi) { + Module.qtFontDpi = dpi; + if (publicAPI.status == "Running") + Module.qtSetFontDpi(dpi); + } + + function fontDpi() { + return Module.qtFontDpi; + } + setStatus("Created"); return publicAPI; diff --git a/src/plugins/platforms/wasm/qtlogo.svg b/src/plugins/platforms/wasm/qtlogo.svg index cb8989bb79..ad7c7776bf 100644 --- a/src/plugins/platforms/wasm/qtlogo.svg +++ b/src/plugins/platforms/wasm/qtlogo.svg @@ -5,15 +5,10 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="462pt" height="339pt" viewBox="0 0 462 339" - version="1.1" - id="svg2" - inkscape:version="0.91 r13725" - sodipodi:docname="TheQtCompany_logo_2.svg"> + version="1.1"> <metadata id="metadata20"> <rdf:RDF> @@ -26,28 +21,6 @@ </cc:Work> </rdf:RDF> </metadata> - <defs - id="defs18" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1536" - inkscape:window-height="801" - id="namedview16" - showgrid="false" - inkscape:zoom="1.1138643" - inkscape:cx="270.58047" - inkscape:cy="174.65092" - inkscape:window-x="-8" - inkscape:window-y="-8" - inkscape:window-maximized="1" - inkscape:current-layer="svg2" /> <path fill="#41cd52" d=" M 63.50 0.00 L 462.00 0.00 L 462.00 274.79 C 440.60 296.26 419.13 317.66 397.61 339.00 L 0.00 339.00 L 0.00 63.39 C 21.08 42.18 42.34 21.13 63.50 0.00 Z" diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index f4ca49997a..6a02a457a0 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -568,11 +568,8 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven switch (eventType) { case EMSCRIPTEN_EVENT_MOUSEDOWN: { - if (window2) { - window2->raise(); - if (!window2->isActive()) - window2->requestActivate(); - } + if (window2) + window2->requestActivate(); pressedButtons.setFlag(button); @@ -776,7 +773,7 @@ int QWasmEventTranslator::handleTouch(int eventType, const EmscriptenTouchEvent QWindowSystemInterface::handleTouchCancelEvent(window2, getTimestamp(), touchDevice, keyModifier); QWasmEventDispatcher::maintainTimers(); - return 0; + return 1; } quint64 QWasmEventTranslator::getTimestamp() diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp index 0c72dfddc4..dc6bb5847e 100644 --- a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp +++ b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp @@ -38,9 +38,9 @@ void QWasmFontDatabase::populateFontDatabase() // Load font file from resources. Currently // all fonts needs to be bundled with the nexe // as Qt resources. - QStringList fontFileNames = QStringList() << QStringLiteral(":/fonts/Vera.ttf") + QStringList fontFileNames = QStringList() << QStringLiteral(":/fonts/DejaVuSansMono.ttf") + << QStringLiteral(":/fonts/Vera.ttf") << QStringLiteral(":/fonts/DejaVuSans.ttf"); - foreach (const QString &fontFileName, fontFileNames) { QFile theFont(fontFileName); if (!theFont.open(QIODevice::ReadOnly)) @@ -82,5 +82,9 @@ void QWasmFontDatabase::releaseHandle(void *handle) QFreeTypeFontDatabase::releaseHandle(handle); } +QFont QWasmFontDatabase::defaultFont() const +{ + return QFont(QLatin1String("Bitstream Vera Sans")); +} QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.h b/src/plugins/platforms/wasm/qwasmfontdatabase.h index 891f12859e..cbd187a022 100644 --- a/src/plugins/platforms/wasm/qwasmfontdatabase.h +++ b/src/plugins/platforms/wasm/qwasmfontdatabase.h @@ -44,6 +44,7 @@ public: QChar::Script script) const override; QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) override; void releaseHandle(void *handle) override; + QFont defaultFont() const override; }; QT_END_NAMESPACE #endif diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index e601d553f2..31b9104de1 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -48,8 +48,10 @@ #include <QtGui/qscreen.h> #include <qpa/qwindowsysteminterface.h> #include <QtCore/qcoreapplication.h> +#include <qpa/qplatforminputcontextfactory_p.h> #include <emscripten/bind.h> +#include <emscripten/val.h> // this is where EGL headers are pulled in, make sure it is last #include "qwasmscreen.h" @@ -80,12 +82,18 @@ static void resizeCanvasElement(emscripten::val canvas) QWasmIntegration::get()->resizeScreen(canvasId); } +static void qtUpdateDpi() +{ + QWasmIntegration::get()->updateDpi(); +} + EMSCRIPTEN_BINDINGS(qtQWasmIntegraton) { function("qtBrowserBeforeUnload", &browserBeforeUnload); function("qtAddCanvasElement", &addCanvasElement); function("qtRemoveCanvasElement", &removeCanvasElement); function("qtResizeCanvasElement", &resizeCanvasElement); + function("qtUpdateDpi", &qtUpdateDpi); } QWasmIntegration *QWasmIntegration::s_instance; @@ -173,6 +181,18 @@ QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLCon } #endif +void QWasmIntegration::initialize() +{ + QString icStr = QPlatformInputContextFactory::requested(); + if (!icStr.isNull()) + m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); +} + +QPlatformInputContext *QWasmIntegration::inputContext() const +{ + return m_inputContext.data(); +} + QPlatformFontDatabase *QWasmIntegration::fontDatabase() const { if (m_fontDb == nullptr) @@ -245,4 +265,14 @@ void QWasmIntegration::resizeScreen(const QString &canvasId) m_screens.value(canvasId)->updateQScreenAndCanvasRenderSize(); } +void QWasmIntegration::updateDpi() +{ + emscripten::val dpi = emscripten::val::module_property("qtFontDpi"); + if (dpi.isUndefined()) + return; + qreal dpiValue = dpi.as<qreal>(); + for (QWasmScreen *screen : m_screens) + QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), dpiValue, dpiValue); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index 11d8d0f7f5..c04c0eaecc 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -34,6 +34,7 @@ #include <qpa/qplatformintegration.h> #include <qpa/qplatformscreen.h> +#include <qpa/qplatforminputcontext.h> #include <QtCore/qhash.h> @@ -73,6 +74,9 @@ public: QPlatformTheme *createPlatformTheme(const QString &name) const override; QPlatformServices *services() const override; QPlatformClipboard *clipboard() const override; + void initialize() override; + QPlatformInputContext *inputContext() const override; + QWasmClipboard *getWasmClipboard() { return m_clipboard; } static QWasmIntegration *get() { return s_instance; } @@ -81,6 +85,7 @@ public: void addScreen(const QString &canvasId); void removeScreen(const QString &canvasId); void resizeScreen(const QString &canvasId); + void updateDpi(); private: mutable QWasmFontDatabase *m_fontDb; @@ -89,6 +94,8 @@ private: QHash<QString, QWasmScreen *> m_screens; mutable QWasmClipboard *m_clipboard; + qreal m_fontDpi = -1; + mutable QScopedPointer<QPlatformInputContext> m_inputContext; static QWasmIntegration *s_instance; }; diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index ae43e2ebf0..1658f32f9e 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -37,6 +37,14 @@ QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format) : m_requestedFormat(format) { m_requestedFormat.setRenderableType(QSurfaceFormat::OpenGLES); + + // if we set one, we need to set the other as well since in webgl, these are tied together + if (format.depthBufferSize() < 0 && format.stencilBufferSize() > 0) + m_requestedFormat.setDepthBufferSize(16); + + if (format.stencilBufferSize() < 0 && format.depthBufferSize() > 0) + m_requestedFormat.setStencilBufferSize(8); + } QWasmOpenGLContext::~QWasmOpenGLContext() @@ -91,10 +99,14 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons attributes.majorVersion = 2; } + // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT + // we need both or none + bool useDepthStencil = (format.depthBufferSize() > 0 || format.stencilBufferSize() > 0); + // WebGL offers enable/disable control but not size control for these attributes.alpha = format.alphaBufferSize() > 0; - attributes.depth = format.depthBufferSize() > 0; - attributes.stencil = format.stencilBufferSize() > 0; + attributes.depth = useDepthStencil; + attributes.stencil = useDepthStencil; EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId.toLocal8Bit().constData(), &attributes); diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index a26cafa900..af50ce7440 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -31,6 +31,7 @@ #include "qwasmwindow.h" #include "qwasmeventtranslator.h" #include "qwasmcompositor.h" +#include "qwasmintegration.h" #include <emscripten/bind.h> #include <emscripten/val.h> @@ -100,6 +101,17 @@ QImage::Format QWasmScreen::format() const return m_format; } +QDpi QWasmScreen::logicalDpi() const +{ + emscripten::val dpi = emscripten::val::module_property("qtFontDpi"); + if (!dpi.isUndefined()) { + qreal dpiValue = dpi.as<qreal>(); + return QDpi(dpiValue, dpiValue); + } + const qreal defaultDpi = 96; + return QDpi(defaultDpi, defaultDpi); +} + qreal QWasmScreen::devicePixelRatio() const { // FIXME: The effective device pixel ratio may be different from the diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h index 82d2a83edb..8d0d15f245 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -63,6 +63,7 @@ public: QRect geometry() const override; int depth() const override; QImage::Format format() const override; + QDpi logicalDpi() const override; qreal devicePixelRatio() const override; QString name() const override; QPlatformCursor *cursor() const override; diff --git a/src/plugins/platforms/wasm/qwasmtheme.cpp b/src/plugins/platforms/wasm/qwasmtheme.cpp index a7f2db3bd3..978d60d686 100644 --- a/src/plugins/platforms/wasm/qwasmtheme.cpp +++ b/src/plugins/platforms/wasm/qwasmtheme.cpp @@ -29,15 +29,22 @@ #include "qwasmtheme.h" #include <QtCore/qvariant.h> +#include <QFontDatabase> QT_BEGIN_NAMESPACE QWasmTheme::QWasmTheme() { + QFontDatabase fdb; + for (auto family : fdb.families()) + if (fdb.isFixedPitch(family)) + fixedFont = new QFont(family); } QWasmTheme::~QWasmTheme() { + if (fixedFont) + delete fixedFont; } QVariant QWasmTheme::themeHint(ThemeHint hint) const @@ -47,4 +54,12 @@ QVariant QWasmTheme::themeHint(ThemeHint hint) const return QPlatformTheme::themeHint(hint); } +const QFont *QWasmTheme::font(Font type) const +{ + if (type == QPlatformTheme::FixedFont) { + return fixedFont; + } + return nullptr; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmtheme.h b/src/plugins/platforms/wasm/qwasmtheme.h index e4cc06e049..7123a1f3d4 100644 --- a/src/plugins/platforms/wasm/qwasmtheme.h +++ b/src/plugins/platforms/wasm/qwasmtheme.h @@ -31,6 +31,7 @@ #define QWASMTHEME_H #include <qpa/qplatformtheme.h> +#include <QtGui/QFont> QT_BEGIN_NAMESPACE @@ -49,6 +50,8 @@ public: ~QWasmTheme(); QVariant themeHint(ThemeHint hint) const override; + const QFont *font(Font type) const override; + QFont *fixedFont = nullptr; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/wasm.pro b/src/plugins/platforms/wasm/wasm.pro index 9b98445c68..e8728d9dba 100644 --- a/src/plugins/platforms/wasm/wasm.pro +++ b/src/plugins/platforms/wasm/wasm.pro @@ -39,7 +39,8 @@ HEADERS = \ wasmfonts.files = \ ../../../3rdparty/wasm/Vera.ttf \ - ../../../3rdparty/wasm/DejaVuSans.ttf + ../../../3rdparty/wasm/DejaVuSans.ttf \ + ../../../3rdparty/wasm/DejaVuSansMono.ttf wasmfonts.prefix = /fonts wasmfonts.base = ../../../3rdparty/wasm RESOURCES += wasmfonts diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html index f7c856d63d..a118c217f3 100644 --- a/src/plugins/platforms/wasm/wasm_shell.html +++ b/src/plugins/platforms/wasm/wasm_shell.html @@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - <title>APPNAME</title> + <title>@APPNAME@</title> <style> html, body { padding: 0; margin : 0; overflow:hidden; height: 100% } /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ @@ -18,7 +18,7 @@ <figure style="overflow:visible;" id="qtspinner"> <center style="margin-top:1.5em; line-height:150%"> <img src="qtlogo.svg"; width=320; height=200; style="display:block"> </img> - <strong>Qt for WebAssembly: APPNAME</strong> + <strong>Qt for WebAssembly: @APPNAME@</strong> <div id="qtstatus"></div> <noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript> </center> @@ -57,7 +57,7 @@ canvas.style.display = 'block'; }, }); - qtLoader.loadEmscriptenModule("APPNAME"); + qtLoader.loadEmscriptenModule("@APPNAME@"); } </script> <script type="text/javascript" src="qtloader.js"></script> diff --git a/src/plugins/platforms/windows/.prev_CMakeLists.txt b/src/plugins/platforms/windows/.prev_CMakeLists.txt new file mode 100644 index 0000000000..b53fed4b46 --- /dev/null +++ b/src/plugins/platforms/windows/.prev_CMakeLists.txt @@ -0,0 +1,226 @@ +# Generated from windows.pro. + +##################################################################### +## qwindows Plugin: +##################################################################### + +add_qt_plugin(qwindows + TYPE platforms + SOURCES + main.cpp + qtwindowsglobal.h + qwin10helpers.cpp qwin10helpers.h + qwindowsbackingstore.cpp qwindowsbackingstore.h + qwindowscombase.h + qwindowscontext.cpp qwindowscontext.h + qwindowscursor.cpp qwindowscursor.h + qwindowsdialoghelpers.cpp qwindowsdialoghelpers.h + qwindowsdropdataobject.cpp qwindowsdropdataobject.h + qwindowsgdiintegration.cpp qwindowsgdiintegration.h + qwindowsgdinativeinterface.cpp qwindowsgdinativeinterface.h + qwindowsinputcontext.cpp qwindowsinputcontext.h + qwindowsintegration.cpp qwindowsintegration.h + qwindowsinternalmimedata.cpp qwindowsinternalmimedata.h + qwindowskeymapper.cpp qwindowskeymapper.h + qwindowsmenu.cpp qwindowsmenu.h + qwindowsmime.cpp qwindowsmime.h + qwindowsmousehandler.cpp qwindowsmousehandler.h + qwindowsnativeinterface.cpp qwindowsnativeinterface.h + qwindowsole.cpp qwindowsole.h + qwindowsopengltester.cpp qwindowsopengltester.h + qwindowspointerhandler.cpp qwindowspointerhandler.h + qwindowsscreen.cpp qwindowsscreen.h + qwindowsservices.cpp qwindowsservices.h + qwindowstheme.cpp qwindowstheme.h + qwindowsthreadpoolrunner.h + qwindowswindow.cpp qwindowswindow.h + DEFINES + QT_NO_CAST_FROM_ASCII + QT_NO_FOREACH + INCLUDE_DIRECTORIES + . + LIBRARIES + Qt::CorePrivate + Qt::EventDispatcherSupportPrivate + Qt::FontDatabaseSupportPrivate + Qt::GuiPrivate + Qt::ThemeSupportPrivate + advapi32 + d3d9 + gdi32 + ole32 + shell32 + user32 + winmm + PUBLIC_LIBRARIES + Qt::Core + Qt::EventDispatcherSupport + Qt::FontDatabaseSupport + Qt::Gui + Qt::ThemeSupport + dwmapi + imm32 + oleaut32 + shlwapi + winspool + wtsapi32 +) + +# Resources: +set_source_files_properties("openglblacklists/default.json" + PROPERTIES alias "default.json") +add_qt_resource(qwindows "openglblacklists" PREFIX "/qt-project.org/windows/openglblacklists" FILES + openglblacklists/default.json) + + +#### Keys ignored in scope 1:.:.:windows.pro:<TRUE>: +# OTHER_FILES = "windows.json" +# PLUGIN_CLASS_NAME = "QWindowsIntegrationPlugin" +# _LOADED = "qt_plugin" + +## Scopes: +##################################################################### + +extend_target(qwindows CONDITION QT_FEATURE_accessibility + SOURCES + uiautomation/qwindowsuiaaccessibility.cpp uiautomation/qwindowsuiaaccessibility.h + uiautomation/qwindowsuiabaseprovider.cpp uiautomation/qwindowsuiabaseprovider.h + uiautomation/qwindowsuiagriditemprovider.cpp uiautomation/qwindowsuiagriditemprovider.h + uiautomation/qwindowsuiagridprovider.cpp uiautomation/qwindowsuiagridprovider.h + uiautomation/qwindowsuiainvokeprovider.cpp uiautomation/qwindowsuiainvokeprovider.h + uiautomation/qwindowsuiamainprovider.cpp uiautomation/qwindowsuiamainprovider.h + uiautomation/qwindowsuiaprovidercache.cpp uiautomation/qwindowsuiaprovidercache.h + uiautomation/qwindowsuiarangevalueprovider.cpp uiautomation/qwindowsuiarangevalueprovider.h + uiautomation/qwindowsuiaselectionitemprovider.cpp uiautomation/qwindowsuiaselectionitemprovider.h + uiautomation/qwindowsuiaselectionprovider.cpp uiautomation/qwindowsuiaselectionprovider.h + uiautomation/qwindowsuiatableitemprovider.cpp uiautomation/qwindowsuiatableitemprovider.h + uiautomation/qwindowsuiatableprovider.cpp uiautomation/qwindowsuiatableprovider.h + uiautomation/qwindowsuiatextprovider.cpp uiautomation/qwindowsuiatextprovider.h + uiautomation/qwindowsuiatextrangeprovider.cpp uiautomation/qwindowsuiatextrangeprovider.h + uiautomation/qwindowsuiatoggleprovider.cpp uiautomation/qwindowsuiatoggleprovider.h + uiautomation/qwindowsuiautils.cpp uiautomation/qwindowsuiautils.h + uiautomation/qwindowsuiavalueprovider.cpp uiautomation/qwindowsuiavalueprovider.h + uiautomation/qwindowsuiawindowprovider.cpp uiautomation/qwindowsuiawindowprovider.h + LIBRARIES + Qt::AccessibilitySupportPrivate + PUBLIC_LIBRARIES + Qt::AccessibilitySupport +) + +extend_target(qwindows CONDITION QT_FEATURE_vulkan + SOURCES + qwindowsvulkaninstance.cpp qwindowsvulkaninstance.h + LIBRARIES + Qt::VulkanSupportPrivate + PUBLIC_LIBRARIES + Qt::VulkanSupport +) + +#### Keys ignored in scope 4:.:.:windows.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: +# PLUGIN_EXTENDS = "-" + +extend_target(qwindows CONDITION QT_FEATURE_opengl AND NOT QT_FEATURE_dynamicgl AND NOT QT_FEATURE_opengles2 + PUBLIC_LIBRARIES + opengl32 +) + +extend_target(qwindows CONDITION mingw + PUBLIC_LIBRARIES + uuid +) + +extend_target(qwindows CONDITION QT_FEATURE_opengl + SOURCES + qwindowsopenglcontext.h +) + +extend_target(qwindows CONDITION QT_FEATURE_opengles2 + SOURCES + qwindowseglcontext.cpp qwindowseglcontext.h +) + +extend_target(qwindows CONDITION QT_FEATURE_opengl AND NOT QT_FEATURE_opengles2 + SOURCES + qwindowsglcontext.cpp qwindowsglcontext.h +) + +extend_target(qwindows CONDITION QT_FEATURE_dynamicgl + SOURCES + qwindowseglcontext.cpp qwindowseglcontext.h +) + +extend_target(qwindows CONDITION QT_FEATURE_systemtrayicon + SOURCES + qwindowssystemtrayicon.cpp qwindowssystemtrayicon.h +) + +extend_target(qwindows CONDITION QT_FEATURE_clipboard + SOURCES + qwindowsclipboard.cpp qwindowsclipboard.h +) + +extend_target(qwindows CONDITION QT_FEATURE_clipboard AND QT_FEATURE_draganddrop + SOURCES + qwindowsdrag.cpp qwindowsdrag.h +) + +extend_target(qwindows CONDITION QT_FEATURE_tabletevent + SOURCES + qwindowstabletsupport.cpp qwindowstabletsupport.h + INCLUDE_DIRECTORIES + ${PROJECT_SOURCE_DIR}/src/3rdparty/wintab +) + +extend_target(qwindows CONDITION QT_FEATURE_sessionmanager + SOURCES + qwindowssessionmanager.cpp qwindowssessionmanager.h +) + +# Resources: +add_qt_resource(qwindows "cursors" PREFIX "/qt-project.org/windows/cursors" FILES + images/closedhandcursor_32.png + images/closedhandcursor_48.png + images/closedhandcursor_64.png + images/dragcopycursor_32.png + images/dragcopycursor_48.png + images/dragcopycursor_64.png + images/draglinkcursor_32.png + images/draglinkcursor_48.png + images/draglinkcursor_64.png + images/dragmovecursor_32.png + images/dragmovecursor_48.png + images/dragmovecursor_64.png + images/openhandcursor_32.png + images/openhandcursor_48.png + images/openhandcursor_64.png + images/splithcursor_32.png + images/splithcursor_48.png + images/splithcursor_64.png + images/splitvcursor_32.png + images/splitvcursor_48.png + images/splitvcursor_64.png) + + +extend_target(qwindows CONDITION (QT_FEATURE_accessibility) AND (TARGET Qt::WindowsUIAutomationSupportPrivate) + LIBRARIES + Qt::WindowsUIAutomationSupportPrivate + PUBLIC_LIBRARIES + Qt::WindowsUIAutomationSupport +) + +extend_target(qwindows CONDITION QT_FEATURE_accessibility AND mingw + PUBLIC_LIBRARIES + uuid +) + +extend_target(qwindows CONDITION QT_FEATURE_combined_angle_lib + DEFINES + LIBEGL_NAME= + LIBGLESV2_NAME= +) + +extend_target(qwindows CONDITION NOT QT_FEATURE_combined_angle_lib + DEFINES + LIBEGL_NAME= + LIBGLESV2_NAME= +) diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt index c665d75af7..8b7ba7e9be 100644 --- a/src/plugins/platforms/windows/CMakeLists.txt +++ b/src/plugins/platforms/windows/CMakeLists.txt @@ -37,6 +37,8 @@ add_qt_plugin(qwindows DEFINES QT_NO_CAST_FROM_ASCII QT_NO_FOREACH + INCLUDE_DIRECTORIES + . LIBRARIES Qt::CorePrivate Qt::EventDispatcherSupportPrivate @@ -44,15 +46,22 @@ add_qt_plugin(qwindows Qt::GuiPrivate Qt::ThemeSupportPrivate advapi32 - dwmapi + d3d9 gdi32 - imm32 ole32 - oleaut32 shell32 - shlwapi user32 winmm + PUBLIC_LIBRARIES + Qt::Core + Qt::EventDispatcherSupport + Qt::FontDatabaseSupport + Qt::Gui + Qt::ThemeSupport + dwmapi + imm32 + oleaut32 + shlwapi winspool wtsapi32 ) @@ -64,10 +73,9 @@ add_qt_resource(qwindows "openglblacklists" PREFIX "/qt-project.org/windows/open openglblacklists/default.json) -#### Keys ignored in scope 1:.:windows.pro:<NONE>: +#### Keys ignored in scope 1:.:.:windows.pro:<TRUE>: # OTHER_FILES = "windows.json" # PLUGIN_CLASS_NAME = "QWindowsIntegrationPlugin" -# QT_FOR_CONFIG = "gui" # _LOADED = "qt_plugin" ## Scopes: @@ -92,8 +100,11 @@ extend_target(qwindows CONDITION QT_FEATURE_accessibility uiautomation/qwindowsuiatoggleprovider.cpp uiautomation/qwindowsuiatoggleprovider.h uiautomation/qwindowsuiautils.cpp uiautomation/qwindowsuiautils.h uiautomation/qwindowsuiavalueprovider.cpp uiautomation/qwindowsuiavalueprovider.h + uiautomation/qwindowsuiawindowprovider.cpp uiautomation/qwindowsuiawindowprovider.h LIBRARIES Qt::AccessibilitySupportPrivate + PUBLIC_LIBRARIES + Qt::AccessibilitySupport ) extend_target(qwindows CONDITION QT_FEATURE_vulkan @@ -101,18 +112,20 @@ extend_target(qwindows CONDITION QT_FEATURE_vulkan qwindowsvulkaninstance.cpp qwindowsvulkaninstance.h LIBRARIES Qt::VulkanSupportPrivate + PUBLIC_LIBRARIES + Qt::VulkanSupport ) -#### Keys ignored in scope 4:.:windows.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: +#### Keys ignored in scope 4:.:.:windows.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: # PLUGIN_EXTENDS = "-" extend_target(qwindows CONDITION QT_FEATURE_opengl AND NOT QT_FEATURE_dynamicgl AND NOT QT_FEATURE_opengles2 - LIBRARIES + PUBLIC_LIBRARIES opengl32 ) extend_target(qwindows CONDITION mingw - LIBRARIES + PUBLIC_LIBRARIES uuid ) @@ -188,13 +201,15 @@ add_qt_resource(qwindows "cursors" PREFIX "/qt-project.org/windows/cursors" FILE images/splitvcursor_64.png) -extend_target(qwindows CONDITION (QT_FEATURE_accessibility) AND (TARGET WindowsUIAutomationSupportPrivate) +extend_target(qwindows CONDITION (QT_FEATURE_accessibility) AND (TARGET Qt::WindowsUIAutomationSupportPrivate) LIBRARIES Qt::WindowsUIAutomationSupportPrivate + PUBLIC_LIBRARIES + Qt::WindowsUIAutomationSupport ) extend_target(qwindows CONDITION QT_FEATURE_accessibility AND mingw - LIBRARIES + PUBLIC_LIBRARIES uuid ) diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index e14a0c1984..e36a285aa2 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -210,6 +210,7 @@ void QWindowsUser32DLL::init() if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) { + adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi"); enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling"); getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext"); getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext"); @@ -590,7 +591,7 @@ QString QWindowsContext::registerWindowClass(QString cname, d->m_registeredWindowClassNames.insert(cname); qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << cname - << " style=0x" << hex << style << dec + << " style=0x" << Qt::hex << style << Qt::dec << " brush=" << brush << " icon=" << icon << " atom=" << atom; return cname; } @@ -712,7 +713,7 @@ static inline bool findPlatformWindowHelper(const POINT &screenPoint, unsigned c HWND *hwnd, QWindowsWindow **result) { POINT point = screenPoint; - ScreenToClient(*hwnd, &point); + screenToClient(*hwnd, &point); // Returns parent if inside & none matched. const HWND child = ChildWindowFromPointEx(*hwnd, point, cwexFlags); if (!child || child == *hwnd) @@ -977,7 +978,7 @@ static inline bool resizeOnDpiChanged(const QWindow *w) return result; } -static bool shouldHaveNonClientDpiScaling(const QWindow *window) +bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window) { return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 && window->isTopLevel() @@ -1042,7 +1043,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, // For non-client-area messages, these are screen coordinates (as expected // in the MSG structure), otherwise they are client coordinates. if (!(et & QtWindows::NonClientEventFlag)) { - ClientToScreen(msg.hwnd, &msg.pt); + clientToScreen(msg.hwnd, &msg.pt); } } else { GetCursorPos(&msg.pt); @@ -1133,13 +1134,11 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::QuerySizeHints: d->m_creationContext->applyToMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam)); return true; - case QtWindows::ResizeEvent: { - const QSize size(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) - d->m_creationContext->menuHeight); - d->m_creationContext->obtainedGeometry.setSize(size); - } + case QtWindows::ResizeEvent: + d->m_creationContext->obtainedSize = QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return true; case QtWindows::MoveEvent: - d->m_creationContext->obtainedGeometry.moveTo(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return true; case QtWindows::NonClientCreate: if (shouldHaveNonClientDpiScaling(d->m_creationContext->window)) @@ -1321,15 +1320,24 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, #endif } break; case QtWindows::DpiChangedEvent: { - if (!resizeOnDpiChanged(platformWindow->window())) - return false; - platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); - const RECT *prcNewWindow = reinterpret_cast<RECT *>(lParam); - SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, - prcNewWindow->right - prcNewWindow->left, - prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); - platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); - return true; + // Try to apply the suggested size first and then notify ScreenChanged + // so that the resize event sent from QGuiApplication incorporates it + // WM_DPICHANGED is sent with a size that avoids resize loops (by + // snapping back to the previous screen, see QTBUG-65580). + const bool doResize = resizeOnDpiChanged(platformWindow->window()); + if (doResize) { + platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); + platformWindow->updateFullFrameMargins(); + const auto prcNewWindow = reinterpret_cast<RECT *>(lParam); + qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED" + << platformWindow->window() << *prcNewWindow; + SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); + platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); + } + platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange); + return doResize; } #if QT_CONFIG(sessionmanager) case QtWindows::QueryEndSessionApplicationEvent: { @@ -1478,6 +1486,10 @@ void QWindowsContext::handleExitSizeMove(QWindow *window) keyboardModifiers); } } + if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) + d->m_pointerHandler.clearEvents(); + else + d->m_mouseHandler.clearEvents(); } bool QWindowsContext::asyncExpose() const @@ -1570,7 +1582,7 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR if (QWindowsContext::verbose > 1 && lcQpaEvents().isDebugEnabled()) { if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) { qCDebug(lcQpaEvents).nospace() << "EVENT: hwd=" << hwnd << ' ' << eventName - << " msg=0x" << hex << message << " et=0x" << et << dec << " wp=" + << " msg=0x" << Qt::hex << message << " et=0x" << et << Qt::dec << " wp=" << int(wParam) << " at " << GET_X_LPARAM(lParam) << ',' << GET_Y_LPARAM(lParam) << " handled=" << handled; } @@ -1587,6 +1599,7 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0)); if (margins.left() >= 0) { if (platformWindow) { + qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins; platformWindow->setFullFrameMargins(margins); } else { const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext(); diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index fd6c72668c..4908f14629 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -102,6 +102,7 @@ struct QWindowsUser32DLL typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND); typedef BOOL (WINAPI *GetDisplayAutoRotationPreferences)(DWORD *); typedef BOOL (WINAPI *SetDisplayAutoRotationPreferences)(DWORD); + typedef BOOL (WINAPI *AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND); typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND); typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int); @@ -131,6 +132,7 @@ struct QWindowsUser32DLL GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr; SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr; + AdjustWindowRectExForDpi adjustWindowRectExForDpi = nullptr; EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr; GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr; GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr; @@ -201,6 +203,8 @@ public: QWindowsWindow *findPlatformWindowAt(HWND parent, const QPoint &screenPoint, unsigned cwex_flags) const; + static bool shouldHaveNonClientDpiScaling(const QWindow *window); + QWindow *windowUnderMouse() const; void clearWindowUnderMouse(); diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index 00d011ccec..20a8117304 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -184,9 +184,11 @@ static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1) return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm); } -static QSize systemCursorSize(const QPlatformScreen *screen = nullptr) +static QSize systemCursorSize() { return QSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR)); } + +static QSize screenCursorSize(const QPlatformScreen *screen = nullptr) { - const QSize primaryScreenCursorSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR)); + const QSize primaryScreenCursorSize = systemCursorSize(); if (screen) { // Correct the size if the DPI value of the screen differs from // that of the primary screen. @@ -212,7 +214,7 @@ static inline QSize standardCursorSize() { return QSize(32, 32); } // createBitmapCursor() only work for standard sizes (32,48,64...), which does // not work when scaling the 16x16 openhand cursor bitmaps to 150% (resulting // in a non-standard 24x24 size). -static QWindowsCursor::PixmapCursor createPixmapCursorFromData(const QSize &systemCursorSize, +static QWindowsCursor::PixmapCursor createPixmapCursorFromData(const QSize &screenCursorSize, // The cursor size the bitmap is targeted for const QSize &bitmapTargetCursorSize, // The actual size of the bitmap data @@ -222,7 +224,7 @@ static QWindowsCursor::PixmapCursor createPixmapCursorFromData(const QSize &syst QPixmap rawImage = QPixmap::fromImage(QBitmap::fromData(QSize(bitmapSize, bitmapSize), bits).toImage()); rawImage.setMask(QBitmap::fromData(QSize(bitmapSize, bitmapSize), maskBits)); - const qreal factor = qreal(systemCursorSize.width()) / qreal(bitmapTargetCursorSize.width()); + const qreal factor = qreal(screenCursorSize.width()) / qreal(bitmapTargetCursorSize.width()); // Scale images if the cursor size is significantly different, starting with 150% where the system cursor // size is 48. if (qAbs(factor - 1.0) > 0.4) { @@ -402,13 +404,13 @@ QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursor switch (cursorShape) { case Qt::SplitVCursor: - return createPixmapCursorFromData(systemCursorSize(screen), standardCursorSize(), 32, vsplit_bits, vsplitm_bits); + return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 32, vsplit_bits, vsplitm_bits); case Qt::SplitHCursor: - return createPixmapCursorFromData(systemCursorSize(screen), standardCursorSize(), 32, hsplit_bits, hsplitm_bits); + return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 32, hsplit_bits, hsplitm_bits); case Qt::OpenHandCursor: - return createPixmapCursorFromData(systemCursorSize(screen), standardCursorSize(), 16, openhand_bits, openhandm_bits); + return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 16, openhand_bits, openhandm_bits); case Qt::ClosedHandCursor: - return createPixmapCursorFromData(systemCursorSize(screen), standardCursorSize(), 16, closedhand_bits, closedhandm_bits); + return createPixmapCursorFromData(screenCursorSize(screen), standardCursorSize(), 16, closedhand_bits, closedhandm_bits); case Qt::DragCopyCursor: return QWindowsCursor::PixmapCursor(QPixmap(copyDragCursorXpmC), QPoint(0, 0)); case Qt::DragMoveCursor: @@ -454,7 +456,7 @@ QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursor { Qt::DragLinkCursor, 64, "draglinkcursor_64.png", 0, 0 } }; - const QSize cursorSize = systemCursorSize(screen); + const QSize cursorSize = screenCursorSize(screen); const QWindowsCustomPngCursor *sEnd = pngCursors + sizeof(pngCursors) / sizeof(pngCursors[0]); const QWindowsCustomPngCursor *bestFit = nullptr; int sizeDelta = INT_MAX; @@ -507,7 +509,7 @@ HCURSOR QWindowsCursor::createCursorFromShape(Qt::CursorShape cursorShape, const switch (cursorShape) { case Qt::BlankCursor: { - QImage blank = QImage(systemCursorSize(screen), QImage::Format_Mono); + QImage blank = QImage(systemCursorSize(), QImage::Format_Mono); blank.fill(0); // ignore color table return createBitmapCursor(blank, blank); } diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 9de3268fc8..e0bd38c951 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -736,7 +736,7 @@ QString QWindowsShellItem::libraryItemDefaultSaveFolder(IShellItem *item) #ifndef QT_NO_DEBUG_STREAM void QWindowsShellItem::format(QDebug &d) const { - d << "attributes=0x" << hex << attributes() << dec; + d << "attributes=0x" << Qt::hex << attributes() << Qt::dec; if (isFileSystem()) d << " [filesys]"; if (isDir()) @@ -972,7 +972,7 @@ void QWindowsNativeFileDialogBase::doExec(HWND owner) // gets a WM_CLOSE or the parent window is destroyed. const HRESULT hr = m_fileDialog->Show(owner); QWindowsDialogs::eatMouseMove(); - qCDebug(lcQpaDialogs) << '<' << __FUNCTION__ << " returns " << hex << hr; + qCDebug(lcQpaDialogs) << '<' << __FUNCTION__ << " returns " << Qt::hex << hr; // Emit accepted() only if there is a result as otherwise UI hangs occur. // For example, typing in invalid URLs results in empty result lists. if (hr == S_OK && !m_data.selectedFiles().isEmpty()) { @@ -1013,7 +1013,7 @@ void QWindowsNativeFileDialogBase::setMode(QFileDialogOptions::FileMode mode, } qCDebug(lcQpaDialogs) << __FUNCTION__ << "mode=" << mode << "acceptMode=" << acceptMode << "options=" << options - << "results in" << showbase << hex << flags; + << "results in" << Qt::showbase << Qt::hex << flags; if (FAILED(m_fileDialog->SetOptions(flags))) qErrnoWarning("%s: SetOptions() failed", __FUNCTION__); diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 322865b0f3..502c92ef59 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -428,7 +428,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) if (QWindowsContext::verbose > 1 || result != S_OK) { qCDebug(lcQpaMime) << __FUNCTION__ << "fEscapePressed=" << fEscapePressed << "grfKeyState=" << grfKeyState << "buttons" << m_currentButtons - << "returns 0x" << hex << int(result) << dec; + << "returns 0x" << Qt::hex << int(result) << Qt::dec; } return ResultFromScode(result); } @@ -710,7 +710,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) const Qt::DropActions possibleActions = drag->supportedActions(); const DWORD allowedEffects = translateToWinDragEffects(possibleActions); qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x" - << hex << int(possibleActions) << "effects=0x" << allowedEffects << dec; + << Qt::hex << int(possibleActions) << "effects=0x" << allowedEffects << Qt::dec; // Indicate message handlers we are in DoDragDrop() event loop. QWindowsDrag::m_dragging = true; const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); @@ -734,9 +734,9 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) dropDataObject->releaseQt(); dropDataObject->Release(); // Will delete obj if refcount becomes 0 windowDropSource->Release(); // Will delete src if refcount becomes 0 - qCDebug(lcQpaMime) << '<' << __FUNCTION__ << hex << "allowedEffects=0x" << allowedEffects + qCDebug(lcQpaMime) << '<' << __FUNCTION__ << Qt::hex << "allowedEffects=0x" << allowedEffects << "reportedPerformedEffect=0x" << reportedPerformedEffect - << " resultEffect=0x" << resultEffect << "hr=0x" << int(r) << dec << "dropAction=" << dragResult; + << " resultEffect=0x" << resultEffect << "hr=0x" << int(r) << Qt::dec << "dropAction=" << dragResult; return dragResult; } diff --git a/src/plugins/platforms/windows/qwindowsdropdataobject.cpp b/src/plugins/platforms/windows/qwindowsdropdataobject.cpp index 229ff92894..e1a41c0ede 100644 --- a/src/plugins/platforms/windows/qwindowsdropdataobject.cpp +++ b/src/plugins/platforms/windows/qwindowsdropdataobject.cpp @@ -41,6 +41,7 @@ #include <QtCore/qurl.h> #include <QtCore/qmimedata.h> +#include "qwindowsmime.h" QT_BEGIN_NAMESPACE @@ -48,8 +49,9 @@ QT_BEGIN_NAMESPACE \class QWindowsDropDataObject \brief QWindowsOleDataObject subclass specialized for handling Drag&Drop. - Only allows "text/uri-list" data to be exported as CF_HDROP, to allow dropped - files to be attached to Office applications (instead of adding an URL link). + Prevents "text/uri-list" data for local files from being exported as text + or URLs, to allow dropped files to be attached to Office applications + (instead of creating local hyperlinks). \internal \ingroup qt-lighthouse-win @@ -80,14 +82,22 @@ QWindowsDropDataObject::QueryGetData(LPFORMATETC pformatetc) return QWindowsOleDataObject::QueryGetData(pformatetc); } -// If the data is text/uri-list for local files, tell we can only export it as CF_HDROP. +// If the data is "text/uri-list" only, and all URIs are for local files, +// we prevent it from being exported as text or URLs, to make target applications +// like MS Office attach or open the files instead of creating local hyperlinks. bool QWindowsDropDataObject::shouldIgnore(LPFORMATETC pformatetc) const { QMimeData *dropData = mimeData(); - if (dropData && dropData->hasFormat(QStringLiteral("text/uri-list")) && (pformatetc->cfFormat != CF_HDROP)) { - QList<QUrl> urls = dropData->urls(); - return std::any_of(urls.cbegin(), urls.cend(), [] (const QUrl &u) { return u.isLocalFile(); }); + if (dropData && dropData->formats().size() == 1 && dropData->hasUrls()) { + QString formatName = QWindowsMimeConverter::clipboardFormatName(pformatetc->cfFormat); + if (pformatetc->cfFormat == CF_UNICODETEXT + || pformatetc->cfFormat == CF_TEXT + || formatName == QStringLiteral("UniformResourceLocator") + || formatName == QStringLiteral("UniformResourceLocatorW")) { + QList<QUrl> urls = dropData->urls(); + return std::all_of(urls.cbegin(), urls.cend(), [] (const QUrl &u) { return u.isLocalFile(); }); + } } return false; diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index e95eaef420..d534ce87cd 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -243,7 +243,7 @@ QDebug operator<<(QDebug d, const PIXELFORMATDESCRIPTOR &pd) QDebugStateSaver saver(d); d.nospace(); d << "PIXELFORMATDESCRIPTOR " - << "dwFlags=" << hex << showbase << pd.dwFlags << dec << noshowbase; + << "dwFlags=" << Qt::hex << Qt::showbase << pd.dwFlags << Qt::dec << Qt::noshowbase; if (pd.dwFlags & PFD_DRAW_TO_WINDOW) d << " PFD_DRAW_TO_WINDOW"; if (pd.dwFlags & PFD_DRAW_TO_BITMAP) d << " PFD_DRAW_TO_BITMAP"; if (pd.dwFlags & PFD_SUPPORT_GDI) d << " PFD_SUPPORT_GDI"; @@ -631,10 +631,10 @@ static int choosePixelFormat(HDC hdc, nsp << __FUNCTION__; if (sampleBuffersRequested) nsp << " samples=" << iAttributes[samplesValuePosition]; - nsp << " Attributes: " << hex << showbase; + nsp << " Attributes: " << Qt::hex << Qt::showbase; for (int ii = 0; ii < i; ++ii) nsp << iAttributes[ii] << ','; - nsp << noshowbase << dec << "\n obtained px #" << pixelFormat + nsp << Qt::noshowbase << Qt::dec << "\n obtained px #" << pixelFormat << " of " << numFormats << "\n " << *obtainedPfd; qCDebug(lcQpaGl) << message; } // Debug @@ -784,7 +784,7 @@ static HGLRC createContext(const QOpenGLStaticContext &staticContext, if (!result) { QString message; QDebug(&message).nospace() << __FUNCTION__ << ": wglCreateContextAttribsARB() failed (GL error code: 0x" - << hex << staticContext.opengl32.glGetError() << dec << ") for format: " << format << ", shared context: " << shared; + << Qt::hex << staticContext.opengl32.glGetError() << Qt::dec << ") for format: " << format << ", shared context: " << shared; qErrnoWarning("%s", qPrintable(message)); } return result; diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp index 878f55e56b..71ed33f85b 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp @@ -657,9 +657,9 @@ void QWindowsInputContext::handleInputLanguageChanged(WPARAM wparam, LPARAM lpar m_locale = qt_localeFromLCID(m_languageId); emitLocaleChanged(); - qCDebug(lcQpaInputMethods) << __FUNCTION__ << hex << showbase + qCDebug(lcQpaInputMethods) << __FUNCTION__ << Qt::hex << Qt::showbase << oldLanguageId << "->" << newLanguageId << "Character set:" - << DWORD(wparam) << dec << noshowbase << m_locale; + << DWORD(wparam) << Qt::dec << Qt::noshowbase << m_locale; } /*! diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 2c90b0484e..8dd3810463 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -217,6 +217,8 @@ static inline unsigned parseOptions(const QStringList ¶mList, options |= QWindowsIntegration::NoNativeMenus; } else if (param == QLatin1String("nowmpointer")) { options |= QWindowsIntegration::DontUseWMPointer; + } else if (param == QLatin1String("reverse")) { + options |= QWindowsIntegration::RtlEnabled; } else { qWarning() << "Unknown option" << param; } @@ -324,7 +326,7 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons if (window->type() == Qt::Desktop) { QWindowsDesktopWindow *result = new QWindowsDesktopWindow(window); qCDebug(lcQpaWindows) << "Desktop window:" << window - << showbase << hex << result->winId() << noshowbase << dec << result->geometry(); + << Qt::showbase << Qt::hex << result->winId() << Qt::noshowbase << Qt::dec << result->geometry(); return result; } @@ -353,6 +355,9 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons QWindowsWindow *result = createPlatformWindowHelper(window, obtained); Q_ASSERT(result); + if (window->isTopLevel() && !QWindowsContext::shouldHaveNonClientDpiScaling(window)) + result->setFlag(QWindowsWindow::DisableNonClientScaling); + if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window)) menuBarToBeInstalled->install(result); @@ -373,8 +378,8 @@ QPlatformWindow *QWindowsIntegration::createForeignWindow(QWindow *window, WId n screen = pScreen->screen(); if (screen && screen != window->screen()) window->setScreen(screen); - qCDebug(lcQpaWindows) << "Foreign window:" << window << showbase << hex - << result->winId() << noshowbase << dec << obtainedGeometry << screen; + qCDebug(lcQpaWindows) << "Foreign window:" << window << Qt::showbase << Qt::hex + << result->winId() << Qt::noshowbase << Qt::dec << obtainedGeometry << screen; return result; } diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index e28b2c2fb3..015cf79b6c 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -69,7 +69,8 @@ public: AlwaysUseNativeMenus = 0x100, NoNativeMenus = 0x200, DontUseWMPointer = 0x400, - DetectAltGrModifier = 0x800 + DetectAltGrModifier = 0x800, + RtlEnabled = 0x1000 }; explicit QWindowsIntegration(const QStringList ¶mList); diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index c050369801..44668cde78 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -554,7 +554,7 @@ QDebug operator<<(QDebug d, const KeyboardLayoutItem &k) if (const quint32 qtKey = k.qtKey[i]) { d << '[' << i << ' '; QtDebugUtils::formatQFlags(d, ModsTbl[i]); - d << ' ' << hex << showbase << qtKey << dec << noshowbase << ' '; + d << ' ' << Qt::hex << Qt::showbase << qtKey << Qt::dec << Qt::noshowbase << ' '; QtDebugUtils::formatQEnum(d, Qt::Key(qtKey)); if (qtKey >= 32 && qtKey < 128) d << " '" << char(qtKey) << '\''; @@ -776,7 +776,7 @@ void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 ::ToAscii(vk_key, scancode, kbdBuffer, reinterpret_cast<LPWORD>(&buffer), 0); } qCDebug(lcQpaEvents) << __FUNCTION__ << "for virtual key=" - << hex << showbase << vk_key << dec << noshowbase << keyLayout[vk_key]; + << Qt::hex << Qt::showbase << vk_key << Qt::dec << Qt::noshowbase << keyLayout[vk_key]; } static inline QString messageKeyText(const MSG &msg) @@ -879,21 +879,16 @@ bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, con #if defined(WM_APPCOMMAND) const int cmd = GET_APPCOMMAND_LPARAM(msg.lParam); // QTBUG-57198, do not send mouse-synthesized commands as key events in addition + bool skipPressRelease = false; switch (GET_DEVICE_LPARAM(msg.lParam)) { case FAPPCOMMAND_MOUSE: return false; case FAPPCOMMAND_KEY: - // QTBUG-62838, swallow WM_KEYDOWN, WM_KEYUP for commands that are - // reflected in VK(s) like VK_MEDIA_NEXT_TRACK. Don't do that for - // APPCOMMAND_BROWSER_HOME as that one does not trigger two events - if (cmd != APPCOMMAND_BROWSER_HOME) { - MSG peekedMsg; - if (PeekMessage(&peekedMsg, msg.hwnd, 0, 0, PM_NOREMOVE) - && peekedMsg.message == WM_KEYDOWN) { - PeekMessage(&peekedMsg, msg.hwnd, 0, 0, PM_REMOVE); - PeekMessage(&peekedMsg, msg.hwnd, 0, 0, PM_REMOVE); - } - } + // QTBUG-62838, use WM_KEYDOWN/WM_KEYUP for commands that are reflected + // in VK(s) like VK_MEDIA_NEXT_TRACK, to get correct codes and autorepeat. + // Don't do that for APPCOMMAND_BROWSER_HOME as that one does not trigger two events. + if (cmd != APPCOMMAND_BROWSER_HOME) + skipPressRelease = true; break; } @@ -908,7 +903,8 @@ bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, con return false; const int qtKey = int(CmdTbl[cmd]); - sendExtendedPressRelease(receiver, qtKey, Qt::KeyboardModifier(state), 0, 0, 0); + if (!skipPressRelease) + sendExtendedPressRelease(receiver, qtKey, Qt::KeyboardModifier(state), 0, 0, 0); // QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise, // the keys are not passed to the active media player. # if QT_CONFIG(shortcut) @@ -1388,7 +1384,7 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const } } qCDebug(lcQpaEvents) << __FUNCTION__ << e << "nativeVirtualKey=" - << showbase << hex << e->nativeVirtualKey() << dec << noshowbase + << Qt::showbase << Qt::hex << e->nativeVirtualKey() << Qt::dec << Qt::noshowbase << e->modifiers() << kbItem << "\n returns" << formatKeys(result); return result; } diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp index 17a1b94101..e55e283fe1 100644 --- a/src/plugins/platforms/windows/qwindowsmenu.cpp +++ b/src/plugins/platforms/windows/qwindowsmenu.cpp @@ -896,8 +896,8 @@ void QWindowsMenuItem::formatDebug(QDebug &d) const d << ", parentMenu=" << static_cast<const void *>(m_parentMenu); if (m_subMenu) d << ", subMenu=" << static_cast<const void *>(m_subMenu); - d << ", tag=" << showbase << hex - << tag() << noshowbase << dec << ", id=" << m_id; + d << ", tag=" << Qt::showbase << Qt::hex + << tag() << Qt::noshowbase << Qt::dec << ", id=" << m_id; #if QT_CONFIG(shortcut) if (!m_shortcut.isEmpty()) d << ", shortcut=" << m_shortcut; @@ -933,7 +933,7 @@ void QWindowsMenu::formatDebug(QDebug &d) const if (m_parentMenu != nullptr) d << " [on menu]"; if (tag()) - d << ", tag=" << showbase << hex << tag() << noshowbase << dec; + d << ", tag=" << Qt::showbase << Qt::hex << tag() << Qt::noshowbase << Qt::dec; if (m_visible) d << " [visible]"; if (m_enabled) diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index 96e34fb44c..030d8d1e0f 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -1087,7 +1087,10 @@ bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMi const QImage image = qvariant_cast<QImage>(mimeData->imageData()); if (image.isNull()) return false; - return cf == CF_DIBV5 || (cf == CF_DIB) || cf == int(CF_PNG); + // QTBUG-64322: Use PNG only for transparent images as otherwise MS PowerPoint + // cannot handle it. + return cf == CF_DIBV5 || cf == CF_DIB + || (cf == int(CF_PNG) && image.hasAlphaChannel()); } bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index 737fd1d2a9..97e1319e8d 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -106,7 +106,7 @@ static inline void compressMouseMove(MSG *msg) // Extract the x,y coordinates from the lParam as we do in the WndProc msg->pt.x = GET_X_LPARAM(mouseMsg.lParam); msg->pt.y = GET_Y_LPARAM(mouseMsg.lParam); - ClientToScreen(msg->hwnd, &(msg->pt)); + clientToScreen(msg->hwnd, &(msg->pt)); // Remove the mouse move message PeekMessage(&mouseMsg, msg->hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE); @@ -124,8 +124,8 @@ static inline QTouchDevice *createTouchDevice() return nullptr; const int tabletPc = GetSystemMetrics(SM_TABLETPC); const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES); - qCDebug(lcQpaEvents) << "Digitizers:" << hex << showbase << (digitizers & ~NID_READY) - << "Ready:" << (digitizers & NID_READY) << dec << noshowbase + qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << (digitizers & ~NID_READY) + << "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase << "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints; QTouchDevice *result = new QTouchDevice; result->setType(digitizers & NID_INTEGRATED_TOUCH @@ -157,6 +157,12 @@ QTouchDevice *QWindowsMouseHandler::ensureTouchDevice() return m_touchDevice; } +void QWindowsMouseHandler::clearEvents() +{ + m_lastEventType = QEvent::None; + m_lastEventButton = Qt::NoButton; +} + Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons() { Qt::MouseButtons result = nullptr; @@ -262,7 +268,13 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, if (et == QtWindows::MouseWheelEvent) return translateMouseWheelEvent(window, hwnd, msg, result); - const QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); + QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); + if ((et & QtWindows::NonClientEventFlag) == 0 && QWindowsBaseWindow::isRtlLayout(hwnd)) { + RECT clientArea; + GetClientRect(hwnd, &clientArea); + winEventPosition.setX(clientArea.right - winEventPosition.x()); + } + QPoint clientPosition; QPoint globalPosition; if (et & QtWindows::NonClientEventFlag) { @@ -287,8 +299,6 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; - const MouseEvent mouseEvent = eventFromMsg(msg); - // Check for events synthesized from touch. Lower byte is touch index, 0 means pen. static const bool passSynthesizedMouseEvents = !(QWindowsIntegration::instance()->options() & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch); @@ -305,13 +315,40 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } } + const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const MouseEvent mouseEvent = eventFromMsg(msg); + Qt::MouseButtons buttons; + + if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) + buttons = queryMouseButtons(); + else + buttons = keyStateToMouseButtons(msg.wParam); + + // When the left/right mouse buttons are pressed over the window title bar + // WM_NCLBUTTONDOWN/WM_NCRBUTTONDOWN messages are received. But no UP + // messages are received on release, only WM_NCMOUSEMOVE/WM_MOUSEMOVE. + // We detect it and generate the missing release events here. (QTBUG-75678) + // The last event vars are cleared on QWindowsContext::handleExitSizeMove() + // to avoid generating duplicated release events. + if (m_lastEventType == QEvent::NonClientAreaMouseButtonPress + && (mouseEvent.type == QEvent::NonClientAreaMouseMove || mouseEvent.type == QEvent::MouseMove) + && (m_lastEventButton & buttons) == 0) { + if (mouseEvent.type == QEvent::NonClientAreaMouseMove) { + QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition, globalPosition, buttons, m_lastEventButton, + QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source); + } else { + QWindowSystemInterface::handleMouseEvent(window, clientPosition, globalPosition, buttons, m_lastEventButton, + QEvent::MouseButtonRelease, keyModifiers, source); + } + } + m_lastEventType = mouseEvent.type; + m_lastEventButton = mouseEvent.button; + if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) { - const Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons(); QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition, globalPosition, buttons, mouseEvent.button, mouseEvent.type, - QWindowsKeyMapper::queryKeyboardModifiers(), - source); + keyModifiers, source); return false; // Allow further event processing (dragging of windows). } @@ -334,7 +371,6 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle()); - const Qt::MouseButtons buttons = keyStateToMouseButtons(int(msg.wParam)); // If the window was recently resized via mouse doubleclick on the frame or title bar, // we don't get WM_LBUTTONDOWN or WM_LBUTTONDBLCLK for the second click, @@ -461,8 +497,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, if (!discardEvent && mouseEvent.type != QEvent::None) { QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons, mouseEvent.button, mouseEvent.type, - QWindowsKeyMapper::queryKeyboardModifiers(), - source); + keyModifiers, source); } m_previousCaptureWindow = hasCapture ? window : nullptr; // QTBUG-48117, force synchronous handling for the extra buttons so that WM_APPCOMMAND diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.h b/src/plugins/platforms/windows/qwindowsmousehandler.h index 480662c9bf..5fe4b09c1e 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.h +++ b/src/plugins/platforms/windows/qwindowsmousehandler.h @@ -45,6 +45,7 @@ #include <QtCore/qpointer.h> #include <QtCore/qhash.h> +#include <QtGui/qevent.h> QT_BEGIN_NAMESPACE @@ -72,13 +73,14 @@ public: bool translateScrollEvent(QWindow *window, HWND hwnd, MSG msg, LRESULT *result); - static inline Qt::MouseButtons keyStateToMouseButtons(int); + static inline Qt::MouseButtons keyStateToMouseButtons(WPARAM); static inline Qt::KeyboardModifiers keyStateToModifiers(int); static inline int mouseButtonsToKeyState(Qt::MouseButtons); static Qt::MouseButtons queryMouseButtons(); QWindow *windowUnderMouse() const { return m_windowUnderMouse.data(); } void clearWindowUnderMouse() { m_windowUnderMouse = 0; } + void clearEvents(); private: inline bool translateMouseWheelEvent(QWindow *window, HWND hwnd, @@ -91,9 +93,11 @@ private: QTouchDevice *m_touchDevice = nullptr; bool m_leftButtonDown = false; QWindow *m_previousCaptureWindow = nullptr; + QEvent::Type m_lastEventType = QEvent::None; + Qt::MouseButton m_lastEventButton = Qt::NoButton; }; -Qt::MouseButtons QWindowsMouseHandler::keyStateToMouseButtons(int wParam) +Qt::MouseButtons QWindowsMouseHandler::keyStateToMouseButtons(WPARAM wParam) { Qt::MouseButtons mb(Qt::NoButton); if (wParam & MK_LBUTTON) diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp index b8ab7f8779..e581b30ced 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp @@ -40,6 +40,7 @@ #include "qwindowsnativeinterface.h" #include "qwindowsclipboard.h" #include "qwindowswindow.h" +#include "qwindowsscreen.h" #include "qwindowscontext.h" #include "qwindowscursor.h" #include "qwindowsopenglcontext.h" @@ -124,6 +125,21 @@ void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resourc return nullptr; } +void *QWindowsNativeInterface::nativeResourceForScreen(const QByteArray &resource, QScreen *screen) +{ + if (!screen || !screen->handle()) { + qWarning("%s: '%s' requested for null screen or screen without handle.", __FUNCTION__, resource.constData()); + return nullptr; + } + QWindowsScreen *bs = static_cast<QWindowsScreen *>(screen->handle()); + int type = resourceType(resource); + if (type == HandleType) + return bs->handle(); + + qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData()); + return nullptr; +} + #ifndef QT_NO_CURSOR void *QWindowsNativeInterface::nativeResourceForCursor(const QByteArray &resource, const QCursor &cursor) { diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.h b/src/plugins/platforms/windows/qwindowsnativeinterface.h index e6f8aae8fb..ce395dc5a4 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.h +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.h @@ -74,6 +74,7 @@ public: void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override; #endif void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override; + void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override; #ifndef QT_NO_CURSOR void *nativeResourceForCursor(const QByteArray &resource, const QCursor &cursor) override; #endif diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp index e9c3f2cbf6..fb6a74581a 100644 --- a/src/plugins/platforms/windows/qwindowsole.cpp +++ b/src/plugins/platforms/windows/qwindowsole.cpp @@ -110,7 +110,7 @@ QWindowsOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) } if (QWindowsContext::verbose > 1 && lcQpaMime().isDebugEnabled()) - qCDebug(lcQpaMime) <<__FUNCTION__ << *pformatetc << "returns" << hex << showbase << quint64(hr); + qCDebug(lcQpaMime) <<__FUNCTION__ << *pformatetc << "returns" << Qt::hex << Qt::showbase << quint64(hr); return hr; } @@ -135,7 +135,7 @@ QWindowsOleDataObject::QueryGetData(LPFORMATETC pformatetc) ResultFromScode(S_OK) : ResultFromScode(S_FALSE); } if (QWindowsContext::verbose > 1) - qCDebug(lcQpaMime) << __FUNCTION__ << " returns 0x" << hex << int(hr); + qCDebug(lcQpaMime) << __FUNCTION__ << " returns 0x" << Qt::hex << int(hr); return hr; } @@ -163,7 +163,7 @@ QWindowsOleDataObject::SetData(LPFORMATETC pFormatetc, STGMEDIUM *pMedium, BOOL hr = ResultFromScode(S_OK); } if (QWindowsContext::verbose > 1) - qCDebug(lcQpaMime) << __FUNCTION__ << " returns 0x" << hex << int(hr); + qCDebug(lcQpaMime) << __FUNCTION__ << " returns 0x" << Qt::hex << int(hr); return hr; } diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp index 840a3a11c4..35418a18e7 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.cpp +++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp @@ -188,9 +188,9 @@ QDebug operator<<(QDebug d, const GpuDescription &gd) { QDebugStateSaver s(d); d.nospace(); - d << hex << showbase << "GpuDescription(vendorId=" << gd.vendorId + d << Qt::hex << Qt::showbase << "GpuDescription(vendorId=" << gd.vendorId << ", deviceId=" << gd.deviceId << ", subSysId=" << gd.subSysId - << dec << noshowbase << ", revision=" << gd.revision + << Qt::dec << Qt::noshowbase << ", revision=" << gd.revision << ", driver: " << gd.driverName << ", version=" << gd.driverVersion << ", " << gd.description << gd.gpuSuitableScreen << ')'; @@ -207,11 +207,11 @@ QString GpuDescription::toString() const << "\n Driver Name : " << driverName << "\n Driver Version : " << driverVersion.toString() << "\n Vendor ID : 0x" << qSetPadChar(QLatin1Char('0')) - << uppercasedigits << hex << qSetFieldWidth(4) << vendorId + << Qt::uppercasedigits << Qt::hex << qSetFieldWidth(4) << vendorId << "\n Device ID : 0x" << qSetFieldWidth(4) << deviceId << "\n SubSys ID : 0x" << qSetFieldWidth(8) << subSysId << "\n Revision ID : 0x" << qSetFieldWidth(4) << revision - << dec; + << Qt::dec; if (!gpuSuitableScreen.isEmpty()) str << "\nGL windows forced to screen: " << gpuSuitableScreen; return result; diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index f1960f1585..778170d563 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -250,6 +250,23 @@ static Qt::MouseButtons mouseButtonsFromKeyState(WPARAM keyState) return result; } +static Qt::MouseButtons queryMouseButtons() +{ + Qt::MouseButtons result = Qt::NoButton; + const bool mouseSwapped = GetSystemMetrics(SM_SWAPBUTTON); + if (GetAsyncKeyState(VK_LBUTTON) < 0) + result |= mouseSwapped ? Qt::RightButton: Qt::LeftButton; + if (GetAsyncKeyState(VK_RBUTTON) < 0) + result |= mouseSwapped ? Qt::LeftButton : Qt::RightButton; + if (GetAsyncKeyState(VK_MBUTTON) < 0) + result |= Qt::MidButton; + if (GetAsyncKeyState(VK_XBUTTON1) < 0) + result |= Qt::XButton1; + if (GetAsyncKeyState(VK_XBUTTON2) < 0) + result |= Qt::XButton2; + return result; +} + static QWindow *getWindowUnderPointer(QWindow *window, QPoint globalPos) { QWindow *currentWindowUnderPointer = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT); @@ -298,8 +315,8 @@ static QTouchDevice *createTouchDevice() return nullptr; const int tabletPc = GetSystemMetrics(SM_TABLETPC); const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES); - qCDebug(lcQpaEvents) << "Digitizers:" << hex << showbase << (digitizers & ~NID_READY) - << "Ready:" << (digitizers & NID_READY) << dec << noshowbase + qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << (digitizers & ~NID_READY) + << "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase << "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints; QTouchDevice *result = new QTouchDevice; result->setType(digitizers & NID_INTEGRATED_TOUCH @@ -319,6 +336,12 @@ QTouchDevice *QWindowsPointerHandler::ensureTouchDevice() return m_touchDevice; } +void QWindowsPointerHandler::clearEvents() +{ + m_lastEventType = QEvent::None; + m_lastEventButton = Qt::NoButton; +} + void QWindowsPointerHandler::handleCaptureRelease(QWindow *window, QWindow *currentWindowUnderPointer, HWND hwnd, @@ -452,19 +475,19 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, QList<QWindowSystemInterface::TouchPoint> touchPoints; if (QWindowsContext::verbose > 1) - qCDebug(lcQpaEvents).noquote().nospace() << showbase + qCDebug(lcQpaEvents).noquote().nospace() << Qt::showbase << __FUNCTION__ - << " message=" << hex << msg.message - << " count=" << dec << count; + << " message=" << Qt::hex << msg.message + << " count=" << Qt::dec << count; Qt::TouchPointStates allStates = 0; for (quint32 i = 0; i < count; ++i) { if (QWindowsContext::verbose > 1) - qCDebug(lcQpaEvents).noquote().nospace() << showbase + qCDebug(lcQpaEvents).noquote().nospace() << Qt::showbase << " TouchPoint id=" << touchInfo[i].pointerInfo.pointerId << " frame=" << touchInfo[i].pointerInfo.frameId - << " flags=" << hex << touchInfo[i].pointerInfo.pointerFlags; + << " flags=" << Qt::hex << touchInfo[i].pointerInfo.pointerFlags; QWindowSystemInterface::TouchPoint touchPoint; const quint32 pointerId = touchInfo[i].pointerInfo.pointerId; @@ -531,7 +554,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect)) return false; - const quint32 pointerId = penInfo->pointerInfo.pointerId; + const qint64 sourceDevice = (qint64)penInfo->pointerInfo.sourceDevice; const QPoint globalPos = QPoint(penInfo->pointerInfo.ptPixelLocation.x, penInfo->pointerInfo.ptPixelLocation.y); const QPoint localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPos); const QPointF hiResGlobalPos = QPointF(dRect.left + qreal(penInfo->pointerInfo.ptHimetricLocation.x - pRect.left) @@ -546,11 +569,11 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin const int z = 0; if (QWindowsContext::verbose > 1) - qCDebug(lcQpaEvents).noquote().nospace() << showbase - << __FUNCTION__ << " pointerId=" << pointerId + qCDebug(lcQpaEvents).noquote().nospace() << Qt::showbase + << __FUNCTION__ << " sourceDevice=" << sourceDevice << " globalPos=" << globalPos << " localPos=" << localPos << " hiResGlobalPos=" << hiResGlobalPos - << " message=" << hex << msg.message - << " flags=" << hex << penInfo->pointerInfo.pointerFlags; + << " message=" << Qt::hex << msg.message + << " flags=" << Qt::hex << penInfo->pointerInfo.pointerFlags; const QTabletEvent::TabletDevice device = QTabletEvent::Stylus; QTabletEvent::PointerType type; @@ -570,7 +593,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin switch (msg.message) { case WM_POINTERENTER: { - QWindowSystemInterface::handleTabletEnterProximityEvent(device, type, pointerId); + QWindowSystemInterface::handleTabletEnterProximityEvent(device, type, sourceDevice); m_windowUnderPointer = window; // The local coordinates may fall outside the window. // Wait until the next update to send the enter event. @@ -583,12 +606,12 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin m_windowUnderPointer = nullptr; m_currentWindow = nullptr; } - QWindowSystemInterface::handleTabletLeaveProximityEvent(device, type, pointerId); + QWindowSystemInterface::handleTabletLeaveProximityEvent(device, type, sourceDevice); break; case WM_POINTERDOWN: case WM_POINTERUP: case WM_POINTERUPDATE: { - QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(pointerId).target; // Pass to window that grabbed it. + QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(sourceDevice).target; // Pass to window that grabbed it. if (!target && m_windowUnderPointer) target = m_windowUnderPointer; if (!target) @@ -597,6 +620,9 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin if (m_needsEnterOnPointerUpdate) { m_needsEnterOnPointerUpdate = false; if (window != m_currentWindow) { + // make sure we subscribe to leave events for this window + trackLeave(hwnd); + QWindowSystemInterface::handleEnterEvent(window, localPos, globalPos); m_currentWindow = window; if (QWindowsWindow *wumPlatformWindow = QWindowsWindow::windowsWindowOf(target)) @@ -607,7 +633,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin QWindowSystemInterface::handleTabletEvent(target, localPos, hiResGlobalPos, device, type, mouseButtons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, - pointerId, keyModifiers); + sourceDevice, keyModifiers); return false; // Allow mouse messages to be generated. } } @@ -668,7 +694,13 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, { *result = 0; - const QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); + QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); + if ((et & QtWindows::NonClientEventFlag) == 0 && QWindowsBaseWindow::isRtlLayout(hwnd)) { + RECT clientArea; + GetClientRect(hwnd, &clientArea); + eventPos.setX(clientArea.right - eventPos.x()); + } + QPoint localPos; QPoint globalPos; @@ -681,7 +713,6 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, } const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); - const Qt::MouseButtons mouseButtons = mouseButtonsFromKeyState(msg.wParam); QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos); if (et == QtWindows::MouseWheelEvent) @@ -707,6 +738,32 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, } const MouseEvent mouseEvent = eventFromMsg(msg); + Qt::MouseButtons mouseButtons; + + if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) + mouseButtons = queryMouseButtons(); + else + mouseButtons = mouseButtonsFromKeyState(msg.wParam); + + // When the left/right mouse buttons are pressed over the window title bar + // WM_NCLBUTTONDOWN/WM_NCRBUTTONDOWN messages are received. But no UP + // messages are received on release, only WM_NCMOUSEMOVE/WM_MOUSEMOVE. + // We detect it and generate the missing release events here. (QTBUG-75678) + // The last event vars are cleared on QWindowsContext::handleExitSizeMove() + // to avoid generating duplicated release events. + if (m_lastEventType == QEvent::NonClientAreaMouseButtonPress + && (mouseEvent.type == QEvent::NonClientAreaMouseMove || mouseEvent.type == QEvent::MouseMove) + && (m_lastEventButton & mouseButtons) == 0) { + if (mouseEvent.type == QEvent::NonClientAreaMouseMove) { + QWindowSystemInterface::handleFrameStrutMouseEvent(window, localPos, globalPos, mouseButtons, m_lastEventButton, + QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source); + } else { + QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, mouseButtons, m_lastEventButton, + QEvent::MouseButtonRelease, keyModifiers, source); + } + } + m_lastEventType = mouseEvent.type; + m_lastEventButton = mouseEvent.button; if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) { QWindowSystemInterface::handleFrameStrutMouseEvent(window, localPos, globalPos, mouseButtons, diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.h b/src/plugins/platforms/windows/qwindowspointerhandler.h index aebef062bc..ccbb1d3939 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.h +++ b/src/plugins/platforms/windows/qwindowspointerhandler.h @@ -46,7 +46,7 @@ #include <QtCore/qpointer.h> #include <QtCore/qscopedpointer.h> #include <QtCore/qhash.h> -#include <qpa/qwindowsysteminterface.h> +#include <QtGui/qevent.h> QT_BEGIN_NAMESPACE @@ -64,6 +64,7 @@ public: QTouchDevice *ensureTouchDevice(); QWindow *windowUnderMouse() const { return m_windowUnderPointer.data(); } void clearWindowUnderMouse() { m_windowUnderPointer = nullptr; } + void clearEvents(); private: bool translateTouchEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vTouchInfo, unsigned int count); @@ -79,6 +80,8 @@ private: QPointer<QWindow> m_currentWindow; QWindow *m_previousCaptureWindow = nullptr; bool m_needsEnterOnPointerUpdate = false; + QEvent::Type m_lastEventType = QEvent::None; + Qt::MouseButton m_lastEventButton = Qt::NoButton; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 0520f88935..2f8850cbe0 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -240,7 +240,8 @@ QWindow *QWindowsScreen::topLevelAt(const QPoint &point) const QWindow *result = nullptr; if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE)) result = QWindowsWindow::topLevelOf(child); - qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result; + if (QWindowsContext::verbose > 1) + qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result; return result; } @@ -250,7 +251,8 @@ QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags) if (QPlatformWindow *bw = QWindowsContext::instance()-> findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags)) result = bw->window(); - qCDebug(lcQpaWindows) <<__FUNCTION__ << screenPoint << " returns " << result; + if (QWindowsContext::verbose > 1) + qCDebug(lcQpaWindows) <<__FUNCTION__ << screenPoint << " returns " << result; return result; } @@ -321,6 +323,11 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData) } } +HMONITOR QWindowsScreen::handle() const +{ + return m_data.hMonitor; +} + QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScreen::virtualGeometry() { QRect result; @@ -433,6 +440,12 @@ QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTy QWindowsScreenManager::QWindowsScreenManager() = default; + +bool QWindowsScreenManager::isSingleScreen() +{ + return QWindowsContext::instance()->screenManager().screens().size() < 2; +} + /*! \brief Triggers synchronization of screens (WM_DISPLAYCHANGE). diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 824bcb1ad6..3eb2d35b27 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -104,6 +104,8 @@ public: inline void handleChanges(const QWindowsScreenData &newData); + HMONITOR handle() const; + #ifndef QT_NO_CURSOR QPlatformCursor *cursor() const override { return m_cursor.data(); } const CursorPtr &cursorPtr() const { return m_cursor; } @@ -138,6 +140,8 @@ public: const QWindowsScreen *screenAtDp(const QPoint &p) const; const QWindowsScreen *screenForHwnd(HWND hwnd) const; + static bool isSingleScreen(); + private: void removeScreen(int index); diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp index fa209f09c4..cd5a78abb6 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp @@ -146,13 +146,13 @@ QDebug operator<<(QDebug d, const LOGCONTEXT &lc) QDebugStateSaver saver(d); d.nospace(); d << "LOGCONTEXT(\"" << QString::fromWCharArray(lc.lcName) << "\", options=0x" - << hex << lc.lcOptions << dec; + << Qt::hex << lc.lcOptions << Qt::dec; formatOptions(d, lc.lcOptions); - d << ", status=0x" << hex << lc.lcStatus << ", device=0x" << lc.lcDevice - << dec << ", PktRate=" << lc.lcPktRate + d << ", status=0x" << Qt::hex << lc.lcStatus << ", device=0x" << lc.lcDevice + << Qt::dec << ", PktRate=" << lc.lcPktRate << ", PktData=" << lc.lcPktData << ", PktMode=" << lc.lcPktMode - << ", MoveMask=0x" << hex << lc.lcMoveMask << ", BtnDnMask=0x" << lc.lcBtnDnMask - << ", BtnUpMask=0x" << lc.lcBtnUpMask << dec << ", SysMode=" << lc.lcSysMode + << ", MoveMask=0x" << Qt::hex << lc.lcMoveMask << ", BtnDnMask=0x" << lc.lcBtnDnMask + << ", BtnUpMask=0x" << lc.lcBtnUpMask << Qt::dec << ", SysMode=" << lc.lcSysMode << ", InOrg=(" << lc.lcInOrgX << ", " << lc.lcInOrgY << ", " << lc.lcInOrgZ << "), InExt=(" << lc.lcInExtX << ", " << lc.lcInExtY << ", " << lc.lcInExtZ << ") OutOrg=(" << lc.lcOutOrgX << ", " << lc.lcOutOrgY << ", " @@ -305,7 +305,7 @@ QString QWindowsTabletSupport::description() const << '.' << (specificationVersion & 0xFF) << " implementation: v" << (implementationVersion >> 8) << '.' << (implementationVersion & 0xFF) << ' ' << devices << " device(s), " << cursors << " cursor(s), " - << extensions << " extensions" << ", options: 0x" << hex << opts << dec; + << extensions << " extensions" << ", options: 0x" << Qt::hex << opts << Qt::dec; formatOptions(str, opts); if (m_tiltSupport) str << " tilt"; @@ -435,6 +435,27 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L m_currentDevice = m_devices.size(); m_devices.push_back(tabletInit(uniqueId, cursorType)); } + + /** + * We should check button map for changes on every proximity event, not + * only during initialization phase. + * + * WARNING: in 2016 there were some Wacom table drivers, which could mess up + * button mapping if the remapped button was pressed, while the + * application **didn't have input focus**. This bug is somehow + * related to the fact that Wacom drivers allow user to configure + * per-application button-mappings. If the bug shows up again, + * just move this button-map fetching into initialization block. + * + * See https://bugs.kde.org/show_bug.cgi?id=359561 + */ + BYTE logicalButtons[32]; + memset(logicalButtons, 0, 32); + m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons); + m_devices[m_currentDevice].buttonsMap[0x1] = logicalButtons[0]; + m_devices[m_currentDevice].buttonsMap[0x2] = logicalButtons[1]; + m_devices[m_currentDevice].buttonsMap[0x4] = logicalButtons[2]; + m_devices[m_currentDevice].currentPointerType = pointerType(currentCursor); m_state = PenProximity; qCDebug(lcQpaTablet) << "enter proximity for device #" @@ -446,6 +467,52 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L return true; } +Qt::MouseButton buttonValueToEnum(DWORD button, + const QWindowsTabletDeviceData &tdd) { + + enum : unsigned { + leftButtonValue = 0x1, + middleButtonValue = 0x2, + rightButtonValue = 0x4, + doubleClickButtonValue = 0x7 + }; + + button = tdd.buttonsMap.value(button); + + return button == leftButtonValue ? Qt::LeftButton : + button == rightButtonValue ? Qt::RightButton : + button == doubleClickButtonValue ? Qt::MiddleButton : + button == middleButtonValue ? Qt::MiddleButton : + button ? Qt::LeftButton /* fallback item */ : + Qt::NoButton; +} + +Qt::MouseButtons convertTabletButtons(DWORD btnNew, + const QWindowsTabletDeviceData &tdd) { + + Qt::MouseButtons buttons = Qt::NoButton; + for (unsigned int i = 0; i < 3; i++) { + unsigned int btn = 0x1 << i; + + if (btn & btnNew) { + Qt::MouseButton convertedButton = + buttonValueToEnum(btn, tdd); + + buttons |= convertedButton; + + /** + * If a button that is present in hardware input is + * mapped to a Qt::NoButton, it means that it is going + * to be eaten by the driver, for example by its + * "Pan/Scroll" feature. Therefore we shouldn't handle + * any of the events associated to it. We'll just return + * Qt::NoButtons here. + */ + } + } + return buttons; +} + bool QWindowsTabletSupport::translateTabletPacketEvent() { static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue. @@ -552,9 +619,12 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() << tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation; } + Qt::MouseButtons buttons = + convertTabletButtons(packet.pkButtons, m_devices.at(m_currentDevice)); + QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, QPointF(localPos), globalPosF, currentDevice, currentPointer, - static_cast<Qt::MouseButtons>(packet.pkButtons), + buttons, pressureNew, tiltX, tiltY, tangentialPressure, rotation, z, uniqueId, diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.h b/src/plugins/platforms/windows/qwindowstabletsupport.h index d91701d6a5..8f97982308 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.h +++ b/src/plugins/platforms/windows/qwindowstabletsupport.h @@ -45,6 +45,7 @@ #include <QtCore/qvector.h> #include <QtCore/qpoint.h> +#include <QtCore/qhash.h> #include <wintab.h> @@ -100,6 +101,7 @@ struct QWindowsTabletDeviceData qint64 uniqueId = 0; int currentDevice = 0; int currentPointerType = 0; + QHash<quint8, quint8> buttonsMap; }; #ifndef QT_NO_DEBUG_STREAM diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index a6b9781252..b75c64c40e 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -170,15 +170,9 @@ public: if (m_params) { const QString fileName = m_params->fileName; SHFILEINFO info; -#ifndef Q_OS_WINCE - const UINT oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); -#endif const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(fileName.utf16()), m_params->attributes, &info, sizeof(SHFILEINFO), m_params->flags); -#ifndef Q_OS_WINCE - SetErrorMode(oldErrorMode); -#endif m_doneMutex.lock(); if (!m_cancelled.load()) { *m_params->result = result; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 338e594c7b..e700e6cff4 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -134,6 +134,10 @@ static QByteArray debugWinExStyle(DWORD exStyle) rc += " WS_EX_LAYERED"; if (exStyle & WS_EX_DLGMODALFRAME) rc += " WS_EX_DLGMODALFRAME"; + if (exStyle & WS_EX_LAYOUTRTL) + rc += " WS_EX_LAYOUTRTL"; + if (exStyle & WS_EX_NOINHERITLAYOUT) + rc += " WS_EX_NOINHERITLAYOUT"; return rc; } @@ -184,6 +188,7 @@ static inline RECT RECTfromQRect(const QRect &rect) return result; } + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const RECT &r) { @@ -238,7 +243,7 @@ QDebug operator<<(QDebug d, const WINDOWPLACEMENT &wp) QDebugStateSaver saver(d); d.nospace(); d.noquote(); - d << "WINDOWPLACEMENT(flags=0x" << hex << wp.flags << dec << ", showCmd=" + d << "WINDOWPLACEMENT(flags=0x" << Qt::hex << wp.flags << Qt::dec << ", showCmd=" << wp.showCmd << ", ptMinPosition=" << wp.ptMinPosition << ", ptMaxPosition=" << wp.ptMaxPosition << ", rcNormalPosition=" << wp.rcNormalPosition; return d; @@ -248,7 +253,7 @@ QDebug operator<<(QDebug d, const GUID &guid) { QDebugStateSaver saver(d); d.nospace(); - d << '{' << hex << uppercasedigits << qSetPadChar(QLatin1Char('0')) + d << '{' << Qt::hex << Qt::uppercasedigits << qSetPadChar(QLatin1Char('0')) << qSetFieldWidth(8) << guid.Data1 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4) << guid.Data2 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4) @@ -262,6 +267,16 @@ QDebug operator<<(QDebug d, const GUID &guid) } #endif // !QT_NO_DEBUG_STREAM +static void formatBriefRectangle(QDebug &d, const QRect &r) +{ + d << r.width() << 'x' << r.height() << forcesign << r.x() << r.y() << noforcesign; +} + +static void formatBriefMargins(QDebug &d, const QMargins &m) +{ + d << m.left() << ", " << m.top() << ", " << m.right() << ", " << m.bottom(); +} + // QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT // is in workspace/available area coordinates. static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point) @@ -296,7 +311,7 @@ static inline QRect frameGeometry(HWND hwnd, bool topLevel) const int width = rect.right - rect.left; const int height = rect.bottom - rect.top; POINT leftTop = { rect.left, rect.top }; - ScreenToClient(parent, &leftTop); + screenToClient(parent, &leftTop); rect.left = leftTop.x; rect.top = leftTop.y; rect.right = leftTop.x + width; @@ -656,6 +671,17 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag if ((flags & Qt::MSWindowsFixedSizeDialogHint)) dialog = true; + // This causes the title bar to drawn RTL and the close button + // to be left. Note that this causes: + // - All DCs created on the Window to have RTL layout (see SetLayout) + // - ClientToScreen() and ScreenToClient() to work in reverse as well. + // - Mouse event coordinates to be mirrored. + // - Positioning of child Windows. + if (QGuiApplication::layoutDirection() == Qt::RightToLeft + && (QWindowsIntegration::instance()->options() & QWindowsIntegration::RtlEnabled) != 0) { + exStyle |= WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT; + } + // Parent: Use transient parent for top levels. if (popup) { flags |= Qt::WindowStaysOnTopHint; // a popup stays on top, no parent. @@ -761,6 +787,16 @@ QWindowsWindowData QPoint pos = calcPosition(w, context, invMargins); + // Mirror the position when creating on a parent in RTL mode, ditto for the obtained geometry. + int mirrorParentWidth = 0; + if (!w->isTopLevel() && QWindowsBaseWindow::isRtlLayout(parentHandle)) { + RECT rect; + GetClientRect(parentHandle, &rect); + mirrorParentWidth = rect.right; + } + if (mirrorParentWidth != 0 && pos.x() != CW_USEDEFAULT && context->frameWidth != CW_USEDEFAULT) + pos.setX(mirrorParentWidth - context->frameWidth - pos.x()); + result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16, style, pos.x(), pos.y(), @@ -768,14 +804,21 @@ QWindowsWindowData parentHandle, nullptr, appinst, nullptr); qCDebug(lcQpaWindows).nospace() << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: " - << context->obtainedGeometry << ' ' << context->margins; + << context->obtainedPos << context->obtainedSize << ' ' << context->margins; if (!result.hwnd) { qErrnoWarning("%s: CreateWindowEx failed", __FUNCTION__); return result; } - result.geometry = context->obtainedGeometry; + if (mirrorParentWidth != 0) { + context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width() + - context->obtainedPos.x()); + } + + QRect obtainedGeometry(context->obtainedPos, context->obtainedSize); + + result.geometry = obtainedGeometry; result.fullFrameMargins = context->margins; result.embedded = embedded; result.hasFrame = hasFrame; @@ -859,35 +902,78 @@ static QSize toNativeSizeConstrained(QSize dip, const QWindow *w) \ingroup qt-lighthouse-win */ -QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) : - minimumSize(toNativeSizeConstrained(w->minimumSize(), w)), - maximumSize(toNativeSizeConstrained(w->maximumSize(), w)), - customMargins(cm) +QMargins QWindowsGeometryHint::frameOnPrimaryScreen(DWORD style, DWORD exStyle) { + RECT rect = {0,0,0,0}; + style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. + if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE) + qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); + const QMargins result(qAbs(rect.left), qAbs(rect.top), + qAbs(rect.right), qAbs(rect.bottom)); + qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" + << showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase + << ' ' << rect << ' ' << result; + return result; } -bool QWindowsGeometryHint::validSize(const QSize &s) const +QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd) { - const int width = s.width(); - const int height = s.height(); - return width >= minimumSize.width() && width <= maximumSize.width() - && height >= minimumSize.height() && height <= maximumSize.height(); + return frameOnPrimaryScreen(DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)), + DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE))); } -QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle) +QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle, qreal dpi) { + if (QWindowsContext::user32dll.adjustWindowRectExForDpi == nullptr) + return frameOnPrimaryScreen(style, exStyle); RECT rect = {0,0,0,0}; - style &= ~(WS_OVERLAPPED); // Not permitted, see docs. - if (!AdjustWindowRectEx(&rect, style, FALSE, exStyle)) - qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); + style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. + if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle, + unsigned(qRound(dpi))) == FALSE) { + qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__); + } const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" - << showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase + << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase + << " dpi=" << dpi << ' ' << rect << ' ' << result; return result; } +QMargins QWindowsGeometryHint::frame(HWND hwnd, DWORD style, DWORD exStyle) +{ + if (QWindowsScreenManager::isSingleScreen()) + return frameOnPrimaryScreen(style, exStyle); + auto screenManager = QWindowsContext::instance()->screenManager(); + auto screen = screenManager.screenForHwnd(hwnd); + if (!screen) + screen = screenManager.screens().value(0); + const auto dpi = screen ? screen->logicalDpi().first : qreal(96); + return frame(style, exStyle, dpi); +} + +// For newly created windows. +QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry, + DWORD style, DWORD exStyle) +{ + if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint)) + return {}; + if (!QWindowsContext::user32dll.adjustWindowRectExForDpi + || QWindowsScreenManager::isSingleScreen() + || !QWindowsContext::shouldHaveNonClientDpiScaling(w)) { + return frameOnPrimaryScreen(style, exStyle); + } + qreal dpi = 96; + auto screenManager = QWindowsContext::instance()->screenManager(); + auto screen = screenManager.screenAtDp(geometry.center()); + if (!screen) + screen = screenManager.screens().value(0); + if (screen) + dpi = screen->logicalDpi().first; + return QWindowsGeometryHint::frame(style, exStyle, dpi); +} + bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result) { // NCCALCSIZE_PARAMS structure if wParam==TRUE @@ -907,36 +993,50 @@ bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, co return true; } -void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const +void QWindowsGeometryHint::frameSizeConstraints(const QWindow *w, const QMargins &margins, + QSize *minimumSize, QSize *maximumSize) { - return applyToMinMaxInfo(DWORD(GetWindowLong(hwnd, GWL_STYLE)), - DWORD(GetWindowLong(hwnd, GWL_EXSTYLE)), mmi); + *minimumSize = toNativeSizeConstrained(w->minimumSize(), w); + *maximumSize = toNativeSizeConstrained(w->maximumSize(), w); + + const int maximumWidth = qMax(maximumSize->width(), minimumSize->width()); + const int maximumHeight = qMax(maximumSize->height(), minimumSize->height()); + const int frameWidth = margins.left() + margins.right(); + const int frameHeight = margins.top() + margins.bottom(); + + if (minimumSize->width() > 0) + minimumSize->rwidth() += frameWidth; + if (minimumSize->height() > 0) + minimumSize->rheight() += frameHeight; + if (maximumWidth < QWINDOWSIZE_MAX) + maximumSize->setWidth(maximumWidth + frameWidth); + if (maximumHeight < QWINDOWSIZE_MAX) + maximumSize->setHeight(maximumHeight + frameHeight); } -void QWindowsGeometryHint::applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const +void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w, + const QMargins &margins, + MINMAXINFO *mmi) { + QSize minimumSize; + QSize maximumSize; + frameSizeConstraints(w, margins, &minimumSize, &maximumSize); qCDebug(lcQpaWindows).nospace() << '>' << __FUNCTION__ << '<' << " min=" << minimumSize.width() << ',' << minimumSize.height() << " max=" << maximumSize.width() << ',' << maximumSize.height() + << " margins=" << margins << " in " << *mmi; - const QMargins margins = QWindowsGeometryHint::frame(style, exStyle); - const int frameWidth = margins.left() + margins.right() + customMargins.left() + customMargins.right(); - const int frameHeight = margins.top() + margins.bottom() + customMargins.top() + customMargins.bottom(); if (minimumSize.width() > 0) - mmi->ptMinTrackSize.x = minimumSize.width() + frameWidth; + mmi->ptMinTrackSize.x = minimumSize.width(); if (minimumSize.height() > 0) - mmi->ptMinTrackSize.y = minimumSize.height() + frameHeight; + mmi->ptMinTrackSize.y = minimumSize.height(); - const int maximumWidth = qMax(maximumSize.width(), minimumSize.width()); - const int maximumHeight = qMax(maximumSize.height(), minimumSize.height()); - if (maximumWidth < QWINDOWSIZE_MAX) - mmi->ptMaxTrackSize.x = maximumWidth + frameWidth; - if (maximumHeight < QWINDOWSIZE_MAX) - mmi->ptMaxTrackSize.y = maximumHeight + frameHeight; - qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ - << " frame=" << margins << ' ' << frameWidth << ',' << frameHeight - << " out " << *mmi; + if (maximumSize.width() < QWINDOWSIZE_MAX) + mmi->ptMaxTrackSize.x = maximumSize.width(); + if (maximumSize.height() < QWINDOWSIZE_MAX) + mmi->ptMaxTrackSize.y = maximumSize.height(); + qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ << " out " << *mmi; } bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) @@ -963,6 +1063,11 @@ bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) \ingroup qt-lighthouse-win */ +bool QWindowsBaseWindow::isRtlLayout(HWND hwnd) +{ + return (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0; +} + QWindowsBaseWindow *QWindowsBaseWindow::baseWindowOf(const QWindow *w) { if (w) { @@ -996,7 +1101,7 @@ QRect QWindowsBaseWindow::geometry_sys() const QMargins QWindowsBaseWindow::frameMargins_sys() const { - return QWindowsGeometryHint::frame(style(), exStyle()); + return QWindowsGeometryHint::frame(handle(), style(), exStyle()); } void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other windows. @@ -1122,11 +1227,14 @@ void QWindowsForeignWindow::setVisible(bool visible) QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QRect &geometryIn, const QRect &geometry, const QMargins &cm, - DWORD style_, DWORD exStyle_) : - geometryHint(w, cm), window(w), style(style_), exStyle(exStyle_), + DWORD style, DWORD exStyle) : + window(w), requestedGeometryIn(geometryIn), - requestedGeometry(geometry), obtainedGeometry(geometry), - margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm) + requestedGeometry(geometry), + obtainedPos(geometryIn.topLeft()), + obtainedSize(geometryIn.size()), + margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)), + customMargins(cm) { // Geometry of toplevels does not consider window frames. // TODO: No concept of WA_wasMoved yet that would indicate a @@ -1155,8 +1263,12 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w) << " frame=" << frameWidth << 'x' << frameHeight << '+' << frameX << '+' << frameY - << " min=" << geometryHint.minimumSize << " max=" << geometryHint.maximumSize - << " custom margins=" << customMargins; + << " margins=" << margins << " custom margins=" << customMargins; +} + +void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const +{ + QWindowsGeometryHint::applyToMinMaxInfo(window, margins + customMargins, mmi); } /*! @@ -1248,11 +1360,12 @@ void QWindowsWindow::initialize() // will send the message) and screen change signals of QWindow. if (w->type() != Qt::Desktop) { const Qt::WindowState state = w->windowState(); + const QRect obtainedGeometry(creationContext->obtainedPos, creationContext->obtainedSize); if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen - && creationContext->requestedGeometryIn != creationContext->obtainedGeometry) { - QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, creationContext->obtainedGeometry); + && creationContext->requestedGeometryIn != obtainedGeometry) { + QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry); } - QPlatformScreen *obtainedScreen = screenForGeometry(creationContext->obtainedGeometry); + QPlatformScreen *obtainedScreen = screenForGeometry(obtainedGeometry); if (obtainedScreen && screen() != obtainedScreen) QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen()); } @@ -1672,10 +1785,57 @@ QRect QWindowsWindow::normalGeometry() const const bool fakeFullScreen = m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen); const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd); - const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : fullFrameMargins(); + const QMargins margins = fakeFullScreen + ? QWindowsGeometryHint::frame(handle(), m_savedStyle, 0) + : fullFrameMargins(); return frame.isValid() ? frame.marginsRemoved(margins) : frame; } +static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow, + const QRect &requestedRect, + const QRect &obtainedRect, + const QMargins &fullMargins, + const QMargins &customMargins) +{ + QString result; + QDebug debug(&result); + debug.nospace(); + debug.noquote(); + const auto window = platformWindow->window(); + debug << "Unable to set geometry "; + formatBriefRectangle(debug, requestedRect); + debug << " (frame: "; + formatBriefRectangle(debug, requestedRect + fullMargins); + debug << ") on " << window->metaObject()->className() << "/\"" + << window->objectName() << "\" on \"" << window->screen()->name() + << "\". Resulting geometry: "; + formatBriefRectangle(debug, obtainedRect); + debug << " (frame: "; + formatBriefRectangle(debug, obtainedRect + fullMargins); + debug << ") margins: "; + formatBriefMargins(debug, fullMargins); + if (!customMargins.isNull()) { + debug << " custom margin: "; + formatBriefMargins(debug, customMargins); + } + const auto minimumSize = window->minimumSize(); + const bool hasMinimumSize = !minimumSize.isEmpty(); + if (hasMinimumSize) + debug << " minimum size: " << minimumSize.width() << 'x' << minimumSize.height(); + const auto maximumSize = window->maximumSize(); + const bool hasMaximumSize = maximumSize.width() != QWINDOWSIZE_MAX || maximumSize.height() != QWINDOWSIZE_MAX; + if (hasMaximumSize) + debug << " maximum size: " << maximumSize.width() << 'x' << maximumSize.height(); + if (hasMinimumSize || hasMaximumSize) { + MINMAXINFO minmaxInfo; + memset(&minmaxInfo, 0, sizeof(minmaxInfo)); + platformWindow->getSizeHints(&minmaxInfo); + debug << ' ' << minmaxInfo; + } + debug << ')'; + return result; +} + void QWindowsWindow::setGeometry(const QRect &rectIn) { QRect rect = rectIn; @@ -1695,21 +1855,10 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) setGeometry_sys(rect); clearFlag(WithinSetGeometry); if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) { - qWarning("%s: Unable to set geometry %dx%d+%d+%d on %s/'%s'." - " Resulting geometry: %dx%d+%d+%d " - "(frame: %d, %d, %d, %d, custom margin: %d, %d, %d, %d" - ", minimum size: %dx%d, maximum size: %dx%d).", - __FUNCTION__, - rect.width(), rect.height(), rect.x(), rect.y(), - window()->metaObject()->className(), qPrintable(window()->objectName()), - m_data.geometry.width(), m_data.geometry.height(), - m_data.geometry.x(), m_data.geometry.y(), - m_data.fullFrameMargins.left(), m_data.fullFrameMargins.top(), - m_data.fullFrameMargins.right(), m_data.fullFrameMargins.bottom(), - m_data.customMargins.left(), m_data.customMargins.top(), - m_data.customMargins.right(), m_data.customMargins.bottom(), - window()->minimumWidth(), window()->minimumHeight(), - window()->maximumWidth(), window()->maximumHeight()); + const auto warning = + msgUnableToSetGeometry(this, rectIn, m_data.geometry, + m_data.fullFrameMargins, m_data.customMargins); + qWarning("%s: %s", __FUNCTION__, qPrintable(warning)); } } else { QPlatformWindow::setGeometry(rect); @@ -1753,27 +1902,41 @@ void QWindowsWindow::handleResized(int wParam) } } -void QWindowsWindow::checkForScreenChanged() +static inline bool equalDpi(const QDpi &d1, const QDpi &d2) +{ + return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second); +} + +void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode) { - if (parent()) + if (parent() || QWindowsScreenManager::isSingleScreen()) return; QPlatformScreen *currentScreen = screen(); - const auto &screenManager = QWindowsContext::instance()->screenManager(); - const QWindowsScreen *newScreen = screenManager.screenForHwnd(m_data.hwnd); - if (newScreen != nullptr && newScreen != currentScreen) { - qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ - << ' ' << window() << " \"" << currentScreen->name() - << "\"->\"" << newScreen->name() << '"'; - setFlag(SynchronousGeometryChangeEvent); - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); + const QWindowsScreen *newScreen = + QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd); + if (newScreen == nullptr || newScreen == currentScreen) + return; + // For screens with different DPI: postpone until WM_DPICHANGE + if (mode == FromGeometryChange + && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) { + return; } + qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ + << ' ' << window() << " \"" << currentScreen->name() + << "\"->\"" << newScreen->name() << '"'; + if (mode == FromGeometryChange) + setFlag(SynchronousGeometryChangeEvent); + updateFullFrameMargins(); + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); } void QWindowsWindow::handleGeometryChange() { const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); + if (testFlag(WithinDpiChanged)) + return; // QGuiApplication will send resize QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive // expose events when shrinking, synthesize. @@ -1819,7 +1982,16 @@ void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const windowPlacement.showCmd = windowPlacement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWMINIMIZED : SW_HIDE; result = SetWindowPlacement(hwnd, &windowPlacement); } else { - result = MoveWindow(hwnd, frameGeometry.x(), frameGeometry.y(), + int x = frameGeometry.x(); + if (!window()->isTopLevel()) { + const HWND parentHandle = GetParent(hwnd); + if (isRtlLayout(parentHandle)) { + RECT rect; + GetClientRect(parentHandle, &rect); + x = rect.right - frameGeometry.width() - x; + } + } + result = MoveWindow(hwnd, x, frameGeometry.y(), frameGeometry.width(), frameGeometry.height(), true); } qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << window() @@ -1835,8 +2007,11 @@ void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const HDC QWindowsWindow::getDC() { - if (!m_hdc) + if (!m_hdc) { m_hdc = GetDC(handle()); + if (QGuiApplication::layoutDirection() == Qt::RightToLeft) + SetLayout(m_hdc, 0); // Clear RTL layout + } return m_hdc; } @@ -1876,6 +2051,9 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, { if (message == WM_ERASEBKGND) // Backing store - ignored. return true; + // QTBUG-75455: Suppress WM_PAINT sent to invisible windows when setting WS_EX_LAYERED + if (!window()->isVisible() && (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0) + return false; // Ignore invalid update bounding rectangles RECT updateRect; if (!GetUpdateRect(m_data.hwnd, &updateRect, FALSE)) @@ -2233,6 +2411,15 @@ void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins) } } +void QWindowsWindow::updateFullFrameMargins() +{ + // Normally obtained from WM_NCCALCSIZE + const auto systemMargins = testFlag(DisableNonClientScaling) + ? QWindowsGeometryHint::frameOnPrimaryScreen(m_data.hwnd) + : frameMargins_sys(); + setFullFrameMargins(systemMargins + m_data.customMargins); +} + QMargins QWindowsWindow::frameMargins() const { QMargins result = fullFrameMargins(); @@ -2443,10 +2630,8 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const { // We don't apply the min/max size hint as we change the dpi, because we did not adjust the // QScreen of the window yet so we don't have the min/max with the right ratio - if (!testFlag(QWindowsWindow::WithinDpiChanged)) { - const QWindowsGeometryHint hint(window(), m_data.customMargins); - hint.applyToMinMaxInfo(m_data.hwnd, mmi); - } + if (!testFlag(QWindowsWindow::WithinDpiChanged)) + QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi); // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the // taskbar when maximized diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 0d8096ddfa..1abe1e3531 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -59,24 +59,23 @@ class QDebug; struct QWindowsGeometryHint { - QWindowsGeometryHint() = default; - explicit QWindowsGeometryHint(const QWindow *w, const QMargins &customMargins); - static QMargins frame(DWORD style, DWORD exStyle); + static QMargins frameOnPrimaryScreen(DWORD style, DWORD exStyle); + static QMargins frameOnPrimaryScreen(HWND hwnd); + static QMargins frame(DWORD style, DWORD exStyle, qreal dpi); + static QMargins frame(HWND hwnd, DWORD style, DWORD exStyle); + static QMargins frame(const QWindow *w, const QRect &geometry, + DWORD style, DWORD exStyle); static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result); - void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const; - void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const; - bool validSize(const QSize &s) const; - + static void applyToMinMaxInfo(const QWindow *w, const QMargins &margins, + MINMAXINFO *mmi); + static void frameSizeConstraints(const QWindow *w, const QMargins &margins, + QSize *minimumSize, QSize *maximumSize); static inline QPoint mapToGlobal(HWND hwnd, const QPoint &); static inline QPoint mapToGlobal(const QWindow *w, const QPoint &); static inline QPoint mapFromGlobal(const HWND hwnd, const QPoint &); static inline QPoint mapFromGlobal(const QWindow *w, const QPoint &); static bool positionIncludesFrame(const QWindow *w); - - QSize minimumSize; - QSize maximumSize; - QMargins customMargins; }; struct QWindowCreationContext @@ -85,16 +84,13 @@ struct QWindowCreationContext const QRect &geometryIn, const QRect &geometry, const QMargins &customMargins, DWORD style, DWORD exStyle); - void applyToMinMaxInfo(MINMAXINFO *mmi) const - { geometryHint.applyToMinMaxInfo(style, exStyle, mmi); } + void applyToMinMaxInfo(MINMAXINFO *mmi) const; - QWindowsGeometryHint geometryHint; const QWindow *window; - DWORD style; - DWORD exStyle; QRect requestedGeometryIn; // QWindow scaled QRect requestedGeometry; // after QPlatformWindow::initialGeometry() - QRect obtainedGeometry; + QPoint obtainedPos; + QSize obtainedSize; QMargins margins; QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE int frameX = CW_USEDEFAULT; // Passed on to CreateWindowEx(), including frame. @@ -139,6 +135,7 @@ public: unsigned style() const { return GetWindowLongPtr(handle(), GWL_STYLE); } unsigned exStyle() const { return GetWindowLongPtr(handle(), GWL_EXSTYLE); } + static bool isRtlLayout(HWND hwnd); static QWindowsBaseWindow *baseWindowOf(const QWindow *w); static HWND handleOf(const QWindow *w); @@ -221,7 +218,8 @@ public: HasBorderInFullScreen = 0x200000, WithinDpiChanged = 0x400000, VulkanSurface = 0x800000, - ResizeMoveActive = 0x1000000 + ResizeMoveActive = 0x1000000, + DisableNonClientScaling = 0x2000000 }; QWindowsWindow(QWindow *window, const QWindowsWindowData &data); @@ -262,6 +260,7 @@ public: QMargins frameMargins() const override; QMargins fullFrameMargins() const override; void setFullFrameMargins(const QMargins &newMargins); + void updateFullFrameMargins(); void setOpacity(qreal level) override; void setMask(const QRegion ®ion) override; @@ -337,7 +336,8 @@ public: void alertWindow(int durationMs = 0); void stopAlertWindow(); - void checkForScreenChanged(); + enum ScreenChangeMode { FromGeometryChange, FromDpiChange }; + void checkForScreenChanged(ScreenChangeMode mode = FromGeometryChange); static void setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes); void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch); @@ -401,18 +401,38 @@ QDebug operator<<(QDebug d, const WINDOWPOS &); QDebug operator<<(QDebug d, const GUID &guid); #endif // !QT_NO_DEBUG_STREAM +static inline void clientToScreen(HWND hwnd, POINT *wP) +{ + if (QWindowsBaseWindow::isRtlLayout(hwnd)) { + RECT clientArea; + GetClientRect(hwnd, &clientArea); + wP->x = clientArea.right - wP->x; + } + ClientToScreen(hwnd, wP); +} + +static inline void screenToClient(HWND hwnd, POINT *wP) +{ + ScreenToClient(hwnd, wP); + if (QWindowsBaseWindow::isRtlLayout(hwnd)) { + RECT clientArea; + GetClientRect(hwnd, &clientArea); + wP->x = clientArea.right - wP->x; + } +} + // ---------- QWindowsGeometryHint inline functions. QPoint QWindowsGeometryHint::mapToGlobal(HWND hwnd, const QPoint &qp) { POINT p = { qp.x(), qp.y() }; - ClientToScreen(hwnd, &p); + clientToScreen(hwnd, &p); return QPoint(p.x, p.y); } QPoint QWindowsGeometryHint::mapFromGlobal(const HWND hwnd, const QPoint &qp) { POINT p = { qp.x(), qp.y() }; - ScreenToClient(hwnd, &p); + screenToClient(hwnd, &p); return QPoint(p.x, p.y); } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp index 85a931e015..c7c0deab3f 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp @@ -113,6 +113,9 @@ void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event case QAccessible::ValueChanged: QWindowsUiaMainProvider::notifyValueChange(static_cast<QAccessibleValueChangeEvent *>(event)); break; + case QAccessible::SelectionAdd: + QWindowsUiaMainProvider::notifySelectionChange(event); + break; case QAccessible::TextAttributeChanged: case QAccessible::TextColumnChanged: case QAccessible::TextInserted: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index fad83fb165..a427e553f0 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -52,6 +52,7 @@ #include "qwindowsuiatableitemprovider.h" #include "qwindowsuiagridprovider.h" #include "qwindowsuiagriditemprovider.h" +#include "qwindowsuiawindowprovider.h" #include "qwindowscombase.h" #include "qwindowscontext.h" #include "qwindowsuiautils.h" @@ -146,9 +147,33 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *event) { if (QAccessibleInterface *accessible = event->accessibleInterface()) { - if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) { - // Notifies changes in values of controls supporting the value interface. + if (accessible->role() == QAccessible::ComboBox && accessible->childCount() > 0) { + QAccessibleInterface *listacc = accessible->child(0); + if (listacc && listacc->role() == QAccessible::List) { + int count = listacc->childCount(); + for (int i = 0; i < count; ++i) { + QAccessibleInterface *item = listacc->child(i); + if (item && item->text(QAccessible::Name) == event->value()) { + if (!item->state().selected) { + if (QAccessibleActionInterface *actionInterface = item->actionInterface()) + actionInterface->doAction(QAccessibleActionInterface::toggleAction()); + } + break; + } + } + } + } + if (event->value().type() == QVariant::String) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { + // Notifies changes in string values. + VARIANT oldVal, newVal; + clearVariant(&oldVal); + setVariantString(event->value().toString(), &newVal); + QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); + } + } else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) { + if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { + // Notifies changes in values of controls supporting the value interface. VARIANT oldVal, newVal; clearVariant(&oldVal); setVariantDouble(valueInterface->currentValue().toDouble(), &newVal); @@ -158,6 +183,15 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve } } +void QWindowsUiaMainProvider::notifySelectionChange(QAccessibleEvent *event) +{ + if (QAccessibleInterface *accessible = event->accessibleInterface()) { + if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { + QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_SelectionItem_ElementSelectedEventId); + } + } +} + // Notifies changes in text content and selection state of text controls. void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event) { @@ -230,6 +264,11 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow return UIA_E_ELEMENTNOTAVAILABLE; switch (idPattern) { + case UIA_WindowPatternId: + if (accessible->parent() && (accessible->parent()->role() == QAccessible::Application)) { + *pRetVal = new QWindowsUiaWindowProvider(id()); + } + break; case UIA_TextPatternId: case UIA_TextPattern2Id: // All text controls. @@ -319,8 +358,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; - bool clientTopLevel = (accessible->role() == QAccessible::Client) - && accessible->parent() && (accessible->parent()->role() == QAccessible::Application); + bool topLevelWindow = accessible->parent() && (accessible->parent()->role() == QAccessible::Application); switch (idProp) { case UIA_ProcessIdPropertyId: @@ -346,7 +384,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR setVariantString(QStringLiteral("Qt"), pRetVal); break; case UIA_ControlTypePropertyId: - if (clientTopLevel) { + if (topLevelWindow) { // Reports a top-level widget as a window, instead of "custom". setVariantI4(UIA_WindowControlTypeId, pRetVal); } else { @@ -358,10 +396,20 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR setVariantString(accessible->text(QAccessible::Help), pRetVal); break; case UIA_HasKeyboardFocusPropertyId: - setVariantBool(accessible->state().focused, pRetVal); + if (topLevelWindow) { + // Windows set the active state to true when they are focused + setVariantBool(accessible->state().active, pRetVal); + } else { + setVariantBool(accessible->state().focused, pRetVal); + } break; case UIA_IsKeyboardFocusablePropertyId: - setVariantBool(accessible->state().focusable, pRetVal); + if (topLevelWindow) { + // Windows should always be focusable + setVariantBool(true, pRetVal); + } else { + setVariantBool(accessible->state().focusable, pRetVal); + } break; case UIA_IsOffscreenPropertyId: setVariantBool(accessible->state().offscreen, pRetVal); @@ -391,7 +439,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR break; case UIA_NamePropertyId: { QString name = accessible->text(QAccessible::Name); - if (name.isEmpty() && clientTopLevel) + if (name.isEmpty() && topLevelWindow) name = QCoreApplication::applicationName(); setVariantString(name, pRetVal); break; diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h index 325d5b3de4..df0d60f9c9 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h @@ -68,6 +68,7 @@ public: static void notifyFocusChange(QAccessibleEvent *event); static void notifyStateChange(QAccessibleStateChangeEvent *event); static void notifyValueChange(QAccessibleValueChangeEvent *event); + static void notifySelectionChange(QAccessibleEvent *event); static void notifyTextChange(QAccessibleEvent *event); // IUnknown diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp new file mode 100644 index 0000000000..3738aa72ff --- /dev/null +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qtguiglobal.h> +#if QT_CONFIG(accessibility) + +#include "qwindowsuiawindowprovider.h" +#include "qwindowsuiautils.h" +#include "qwindowscontext.h" + +#include <QtGui/qaccessible.h> +#include <QtGui/private/qwindow_p.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +using namespace QWindowsUiAutomation; + + +QWindowsUiaWindowProvider::QWindowsUiaWindowProvider(QAccessible::Id id) : + QWindowsUiaBaseProvider(id) +{ +} + +QWindowsUiaWindowProvider::~QWindowsUiaWindowProvider() +{ +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::SetVisualState(WindowVisualState state) { + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible || !accessible->window()) + return UIA_E_ELEMENTNOTAVAILABLE; + auto window = accessible->window(); + switch (state) { + case WindowVisualState_Normal: + window->showNormal(); + break; + case WindowVisualState_Maximized: + window->showMaximized(); + break; + case WindowVisualState_Minimized: + window->showMinimized(); + break; + } + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::Close() { + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible || !accessible->window()) + return UIA_E_ELEMENTNOTAVAILABLE; + accessible->window()->close(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::WaitForInputIdle(int milliseconds, __RPC__out BOOL *pRetVal) { + Q_UNUSED(milliseconds); + Q_UNUSED(pRetVal); + return UIA_E_NOTSUPPORTED; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_CanMaximize(__RPC__out BOOL *pRetVal) { + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible || !accessible->window()) + return UIA_E_ELEMENTNOTAVAILABLE; + + auto window = accessible->window(); + auto flags = window->flags(); + + *pRetVal = (!(flags & Qt::MSWindowsFixedSizeDialogHint) + && (flags & Qt::WindowMaximizeButtonHint) + && ((flags & Qt::CustomizeWindowHint) + || window->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX))); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_CanMinimize(__RPC__out BOOL *pRetVal) { + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible || !accessible->window()) + return UIA_E_ELEMENTNOTAVAILABLE; + *pRetVal = accessible->window()->flags() & Qt::WindowMinimizeButtonHint; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_IsModal(__RPC__out BOOL *pRetVal) { + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible || !accessible->window()) + return UIA_E_ELEMENTNOTAVAILABLE; + *pRetVal = accessible->window()->isModal(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_WindowVisualState(__RPC__out enum WindowVisualState *pRetVal) { + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible || !accessible->window()) + return UIA_E_ELEMENTNOTAVAILABLE; + auto visibility = accessible->window()->visibility(); + switch (visibility) { + case QWindow::FullScreen: + case QWindow::Maximized: + *pRetVal = WindowVisualState_Maximized; + break; + case QWindow::Minimized: + *pRetVal = WindowVisualState_Minimized; + break; + default: + *pRetVal = WindowVisualState_Normal; + break; + } + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_WindowInteractionState(__RPC__out enum WindowInteractionState *pRetVal) { + Q_UNUSED(pRetVal); + return UIA_E_NOTSUPPORTED; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_IsTopmost(__RPC__out BOOL *pRetVal) { + Q_UNUSED(pRetVal); + return UIA_E_NOTSUPPORTED; +} + +QT_END_NAMESPACE + +#endif // QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h new file mode 100644 index 0000000000..343fb275f7 --- /dev/null +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSUIAWINDOWPROVIDER_H +#define QWINDOWSUIAWINDOWPROVIDER_H + +#include <QtGui/qtguiglobal.h> +#if QT_CONFIG(accessibility) + +#include "qwindowsuiabaseprovider.h" + +QT_BEGIN_NAMESPACE + +class QWindowsUiaWindowProvider : public QWindowsUiaBaseProvider, + public QWindowsComBase<IWindowProvider> +{ + Q_DISABLE_COPY(QWindowsUiaWindowProvider) +public: + explicit QWindowsUiaWindowProvider(QAccessible::Id id); + ~QWindowsUiaWindowProvider() override; + + HRESULT STDMETHODCALLTYPE SetVisualState(WindowVisualState state) override; + HRESULT STDMETHODCALLTYPE Close( void) override; + HRESULT STDMETHODCALLTYPE WaitForInputIdle(int milliseconds, __RPC__out BOOL *pRetVal) override; + HRESULT STDMETHODCALLTYPE get_CanMaximize(__RPC__out BOOL *pRetVal) override; + HRESULT STDMETHODCALLTYPE get_CanMinimize(__RPC__out BOOL *pRetVal) override; + HRESULT STDMETHODCALLTYPE get_IsModal(__RPC__out BOOL *pRetVal) override; + HRESULT STDMETHODCALLTYPE get_WindowVisualState(__RPC__out WindowVisualState *pRetVal) override; + HRESULT STDMETHODCALLTYPE get_WindowInteractionState(__RPC__out WindowInteractionState *pRetVal) override; + HRESULT STDMETHODCALLTYPE get_IsTopmost(__RPC__out BOOL *pRetVal) override; +}; + +QT_END_NAMESPACE + +#endif // QT_CONFIG(accessibility) + +#endif // QWINDOWSUIAWINDOWPROVIDER_H diff --git a/src/plugins/platforms/windows/uiautomation/uiautomation.pri b/src/plugins/platforms/windows/uiautomation/uiautomation.pri index e3071766d9..ee9332e7ea 100644 --- a/src/plugins/platforms/windows/uiautomation/uiautomation.pri +++ b/src/plugins/platforms/windows/uiautomation/uiautomation.pri @@ -18,6 +18,7 @@ SOURCES += \ $$PWD/qwindowsuiatableitemprovider.cpp \ $$PWD/qwindowsuiagridprovider.cpp \ $$PWD/qwindowsuiagriditemprovider.cpp \ + $$PWD/qwindowsuiawindowprovider.cpp \ $$PWD/qwindowsuiautils.cpp HEADERS += \ @@ -37,7 +38,7 @@ HEADERS += \ $$PWD/qwindowsuiatableitemprovider.h \ $$PWD/qwindowsuiagridprovider.h \ $$PWD/qwindowsuiagriditemprovider.h \ + $$PWD/qwindowsuiawindowprovider.h \ $$PWD/qwindowsuiautils.h -mingw: LIBS *= -luuid - +mingw: QMAKE_USE *= uuid diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri index 7004d7e854..95ba961df1 100644 --- a/src/plugins/platforms/windows/windows.pri +++ b/src/plugins/platforms/windows/windows.pri @@ -1,15 +1,21 @@ # Note: OpenGL32 must precede Gdi32 as it overwrites some functions. -LIBS += -lole32 -luser32 -lwinspool -limm32 -lwinmm -loleaut32 +LIBS += -lwinspool -limm32 -loleaut32 QT_FOR_CONFIG += gui qtConfig(opengl):!qtConfig(opengles2):!qtConfig(dynamicgl): LIBS *= -lopengl32 -mingw: LIBS *= -luuid +mingw: QMAKE_USE *= uuid # For the dialog helpers: -LIBS += -lshlwapi -lshell32 -ladvapi32 -lwtsapi32 - -QMAKE_USE_PRIVATE += d3d9/nolink +LIBS += -lshlwapi -lwtsapi32 + +QMAKE_USE_PRIVATE += \ + advapi32 \ + d3d9/nolink \ + ole32 \ + shell32 \ + user32 \ + winmm DEFINES *= QT_NO_CAST_FROM_ASCII QT_NO_FOREACH diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro index 174bc7b609..50a3bb41a9 100644 --- a/src/plugins/platforms/windows/windows.pro +++ b/src/plugins/platforms/windows/windows.pro @@ -8,7 +8,8 @@ QT += \ qtConfig(accessibility): QT += accessibility_support-private qtConfig(vulkan): QT += vulkan_support-private -LIBS += -lgdi32 -ldwmapi +LIBS += -ldwmapi +QMAKE_USE_PRIVATE += gdi32 include(windows.pri) diff --git a/src/plugins/platforms/winrt/qwinrttheme.cpp b/src/plugins/platforms/winrt/qwinrttheme.cpp index 283825a880..0e1504b1c1 100644 --- a/src/plugins/platforms/winrt/qwinrttheme.cpp +++ b/src/plugins/platforms/winrt/qwinrttheme.cpp @@ -44,6 +44,8 @@ #include <QtCore/qfunctions_winrt.h> #include <QtGui/QPalette> +#include <QtFontDatabaseSupport/private/qwinrtfontdatabase_p.h> + #include <wrl.h> #include <windows.ui.h> #include <windows.ui.viewmanagement.h> @@ -96,7 +98,13 @@ static IUISettings *uiSettings() class QWinRTThemePrivate { public: + QWinRTThemePrivate() + : monospaceFont(QWinRTFontDatabase::familyForStyleHint(QFont::Monospace)) + { + } + QPalette palette; + QFont monospaceFont; }; static inline QColor fromColor(const Color &color) @@ -321,4 +329,14 @@ const QPalette *QWinRTTheme::palette(Palette type) const return QPlatformTheme::palette(type); } +const QFont *QWinRTTheme::font(QPlatformTheme::Font type) const +{ + Q_D(const QWinRTTheme); + qCDebug(lcQpaTheme) << __FUNCTION__ << type; + if (type == QPlatformTheme::FixedFont) + return &d->monospaceFont; + + return QPlatformTheme::font(type); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrttheme.h b/src/plugins/platforms/winrt/qwinrttheme.h index cc5fc851e7..acf5a54a94 100644 --- a/src/plugins/platforms/winrt/qwinrttheme.h +++ b/src/plugins/platforms/winrt/qwinrttheme.h @@ -58,6 +58,7 @@ public: QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override; const QPalette *palette(Palette type = SystemPalette) const override; + const QFont *font(Font type = SystemFont) const override; static QVariant styleHint(QPlatformIntegration::StyleHint hint); QVariant themeHint(ThemeHint hint) const override; diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 43132a1a76..43dc8f074c 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -8,8 +8,7 @@ QT += \ DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ -LIBS += -lws2_32 -QMAKE_USE_PRIVATE += d3d11 +QMAKE_USE_PRIVATE += d3d11 ws2_32 SOURCES = \ main.cpp \ diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp index 476de6d1e5..4adf662152 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp @@ -270,7 +270,9 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) // ES does not support any format option m_format.setOptions(QSurfaceFormat::FormatOptions()); } - + // Robustness must match that of the shared context. + if (share && share->format().testOption(QSurfaceFormat::ResetNotification)) + m_format.setOption(QSurfaceFormat::ResetNotification); Q_ASSERT(glVersions.count() > 0); for (int i = 0; !m_context && i < glVersions.count(); i++) { diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp index b1ce39f363..f86bedbdcd 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp @@ -2017,7 +2017,7 @@ QImage QX11PlatformPixmap::toImage(const QXImageWrapper &xiWrapper, const QRect } } else if (xi->bits_per_pixel == d) { // compatible depth char *xidata = xi->data; // copy each scanline - int bpl = qMin(image.bytesPerLine(),xi->bytes_per_line); + int bpl = qMin(int(image.bytesPerLine()),xi->bytes_per_line); for (int y=0; y<xi->height; y++) { memcpy(image.scanLine(y), xidata, bpl); xidata += xi->bytes_per_line; diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index e4da207b00..bc09fe2f91 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -240,6 +240,10 @@ void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting) } else if (name.contains("uc-logic") && isTablet) { tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); + } else if (name.contains("ugee")) { + isTablet = true; + tabletData.pointerType = QTabletEvent::Pen; + dbgType = QLatin1String("pen"); } else { isTablet = false; } diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index aa329d8cb7..1ce947165d 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -202,7 +202,7 @@ void QXcbDrag::startDrag() if (connection()->mouseGrabber() == nullptr) shapedPixmapWindow()->setMouseGrabEnabled(true); - auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), initiatorWindow); + auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), initiatorWindow.data()); move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); } diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index a70c7db923..95ca40fc95 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -550,6 +550,7 @@ void QXcbIntegration::beep() const return; xcb_connection_t *connection = static_cast<QXcbScreen *>(screen)->xcb_connection(); xcb_bell(connection, 0); + xcb_flush(connection); } bool QXcbIntegration::nativePaintingEnabled() const diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 899081e752..81b889a80f 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -636,13 +636,13 @@ static void dumpNativeWindowsRecursion(const QXcbConnection *connection, xcb_win const QChar oldPadChar =str.padChar(); str.setFieldWidth(8); str.setPadChar(QLatin1Char('0')); - str << hex << window; + str << Qt::hex << window; str.setFieldWidth(oldFieldWidth); str.setPadChar(oldPadChar); - str << dec << " \"" + str << Qt::dec << " \"" << QXcbWindow::windowTitle(connection, window) << "\" " - << geom.width() << 'x' << geom.height() << forcesign << geom.x() << geom.y() - << noforcesign << '\n'; + << geom.width() << 'x' << geom.height() << Qt::forcesign << geom.x() << geom.y() + << Qt::noforcesign << '\n'; auto reply = Q_XCB_REPLY(xcb_query_tree, conn, window); if (reply) { diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 0fa0e8cd7b..bfc105a040 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -915,7 +915,7 @@ QByteArray QXcbScreen::getEdid() const static inline void formatRect(QDebug &debug, const QRect r) { debug << r.width() << 'x' << r.height() - << forcesign << r.x() << r.y() << noforcesign; + << Qt::forcesign << r.x() << r.y() << Qt::noforcesign; } static inline void formatSizeF(QDebug &debug, const QSizeF s) @@ -929,7 +929,7 @@ QDebug operator<<(QDebug debug, const QXcbScreen *screen) debug.nospace(); debug << "QXcbScreen(" << (const void *)screen; if (screen) { - debug << fixed << qSetRealNumberPrecision(1); + debug << Qt::fixed << qSetRealNumberPrecision(1); debug << ", name=" << screen->name(); debug << ", geometry="; formatRect(debug, screen->geometry()); @@ -947,7 +947,7 @@ QDebug operator<<(QDebug debug, const QXcbScreen *screen) debug << "), orientation=" << screen->orientation(); debug << ", depth=" << screen->depth(); debug << ", refreshRate=" << screen->refreshRate(); - debug << ", root=" << hex << screen->root(); + debug << ", root=" << Qt::hex << screen->root(); debug << ", windowManagerName=" << screen->windowManagerName(); } debug << ')'; diff --git a/src/plugins/printsupport/windows/windows.pro b/src/plugins/printsupport/windows/windows.pro index 06694fb7fe..6ca601b2a4 100644 --- a/src/plugins/printsupport/windows/windows.pro +++ b/src/plugins/printsupport/windows/windows.pro @@ -18,7 +18,8 @@ HEADERS += \ OTHER_FILES += windows.json -LIBS += -lwinspool -lcomdlg32 -lgdi32 -luser32 +LIBS += -lwinspool -lcomdlg32 +QMAKE_USE_PRIVATE += user32 gdi32 PLUGIN_TYPE = printsupport PLUGIN_CLASS_NAME = QWindowsPrinterSupportPlugin diff --git a/src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp b/src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp index 390f05c7aa..b7bcd044ab 100644 --- a/src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp +++ b/src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp @@ -43,7 +43,6 @@ #include <qvariant.h> #include <qdatetime.h> #include <qfile.h> -#include <qregexp.h> #include <qsqlerror.h> #include <qsqlfield.h> #include <qsqlindex.h> diff --git a/src/plugins/sqldrivers/sqlite2/smain.cpp b/src/plugins/sqldrivers/sqlite2/smain.cpp index 3a5734f8c9..7d971d6e5a 100644 --- a/src/plugins/sqldrivers/sqlite2/smain.cpp +++ b/src/plugins/sqldrivers/sqlite2/smain.cpp @@ -43,6 +43,7 @@ QT_BEGIN_NAMESPACE +// ### Qt6: remove, obsolete since 5.14 class QSQLite2DriverPlugin : public QSqlDriverPlugin { Q_OBJECT diff --git a/src/plugins/sqldrivers/tds/main.cpp b/src/plugins/sqldrivers/tds/main.cpp index 4aa1444608..18efb22ea4 100644 --- a/src/plugins/sqldrivers/tds/main.cpp +++ b/src/plugins/sqldrivers/tds/main.cpp @@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE +// ### Qt6: remove, obsolete since 4.7 class QTDSDriverPlugin : public QSqlDriverPlugin { Q_OBJECT diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index d0e05c1e20..392368a40b 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -150,6 +150,16 @@ static QWindow *qt_getWindow(const QWidget *widget) QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver); @implementation NotificationReceiver +{ + QMacStylePrivate *privateStyle; +} + +- (instancetype)initWithPrivateStyle:(QMacStylePrivate *)style +{ + if (self = [super init]) + privateStyle = style; + return self; +} - (void)scrollBarStyleDidChange:(NSNotification *)notification { @@ -162,6 +172,23 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver); for (const auto &o : QMacStylePrivate::scrollBars) QCoreApplication::sendEvent(o, &event); } + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context +{ + Q_UNUSED(keyPath); + Q_UNUSED(object); + Q_UNUSED(change); + Q_UNUSED(context); + + Q_ASSERT([keyPath isEqualToString:@"effectiveAppearance"]); + Q_ASSERT(object == NSApp); + + for (NSView *b : privateStyle->cocoaControls) + [b release]; + privateStyle->cocoaControls.clear(); +} + @end @interface QT_MANGLE_NAMESPACE(QIndeterminateProgressIndicator) : NSProgressIndicator @@ -447,6 +474,42 @@ static const int toolButtonArrowMargin = 2; static const qreal focusRingWidth = 3.5; +// An application can force 'Aqua' theme while the system theme is one of +// the 'Dark' variants. Since in Qt we sometimes use NSControls and even +// NSCells directly without attaching them to any view hierarchy, we have +// to set NSAppearance.currentAppearance to 'Aqua' manually, to make sure +// the correct rendering path is triggered. Apple recommends us to un-set +// the current appearance back after we finished with drawing. This is what +// AppearanceSync is for. + +class AppearanceSync { +public: + AppearanceSync() + { +#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave + && !qt_mac_applicationIsInDarkMode()) { + auto requiredAppearanceName = NSApplication.sharedApplication.effectiveAppearance.name; + if (![NSAppearance.currentAppearance.name isEqualToString:requiredAppearanceName]) { + previous = NSAppearance.currentAppearance; + NSAppearance.currentAppearance = [NSAppearance appearanceNamed:requiredAppearanceName]; + } + } +#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + } + + ~AppearanceSync() + { + if (previous) + NSAppearance.currentAppearance = previous; + } + +private: + NSAppearance *previous = nil; + + Q_DISABLE_COPY(AppearanceSync) +}; + static bool setupScroller(NSScroller *scroller, const QStyleOptionSlider *sb) { const qreal length = sb->maximum - sb->minimum + sb->pageStep; @@ -1156,66 +1219,6 @@ static QStyleHelper::WidgetSizePolicy qt_aqua_guess_size(const QWidget *widg, QS } #endif -static NSColor *qt_convertColorForContext(CGContextRef context, NSColor *color) -{ - Q_ASSERT(color); - Q_ASSERT(context); - - CGColorSpaceRef targetCGColorSpace = CGBitmapContextGetColorSpace(context); - NSColorSpace *targetNSColorSpace = [[NSColorSpace alloc] initWithCGColorSpace:targetCGColorSpace]; - NSColor *adjusted = [color colorUsingColorSpace:targetNSColorSpace]; - [targetNSColorSpace release]; - - return adjusted; -} - -static NSColor *qt_colorForContext(CGContextRef context, const CGFloat (&rgba)[4]) -{ - Q_ASSERT(context); - - auto colorSpace = CGBitmapContextGetColorSpace(context); - if (!colorSpace) - return nil; - - return qt_convertColorForContext(context, [NSColor colorWithSRGBRed:rgba[0] green:rgba[1] blue:rgba[2] alpha:rgba[3]]); -} - -static void qt_drawDisclosureButton(CGContextRef context, NSInteger state, bool selected, CGRect rect) -{ - Q_ASSERT(context); - - static const CGFloat gray[] = {0.55, 0.55, 0.55, 0.97}; - static const CGFloat white[] = {1.0, 1.0, 1.0, 0.9}; - - NSColor *fillColor = qt_colorForContext(context, selected ? white : gray); - [fillColor setFill]; - - if (state == NSOffState) { - static NSBezierPath *triangle = [[NSBezierPath alloc] init]; - [triangle removeAllPoints]; - // In off state, a disclosure button is an equilateral triangle - // ('pointing' to the right) with a bound rect that can be described - // as NSMakeRect(0, 0, 8, 9). Inside the 'rect' it's translated by - // (2, 4). - [triangle moveToPoint:NSMakePoint(rect.origin.x + 2, rect.origin.y + 4)]; - [triangle lineToPoint:NSMakePoint(rect.origin.x + 2, rect.origin.y + 4 + 9)]; - [triangle lineToPoint:NSMakePoint(rect.origin.x + 2 + 8, rect.origin.y + 4 + 4.5)]; - [triangle closePath]; - [triangle fill]; - } else { - static NSBezierPath *openTriangle = [[NSBezierPath alloc] init]; - [openTriangle removeAllPoints]; - // In 'on' state, the button is an equilateral triangle (looking down) - // with the bounding rect NSMakeRect(0, 0, 9, 8). Inside the 'rect' - // it's translated by (1, 4). - [openTriangle moveToPoint:NSMakePoint(rect.origin.x + 1, rect.origin.y + 4 + 8)]; - [openTriangle lineToPoint:NSMakePoint(rect.origin.x + 1 + 9, rect.origin.y + 4 + 8)]; - [openTriangle lineToPoint:NSMakePoint(rect.origin.x + 1 + 4.5, rect.origin.y + 4)]; - [openTriangle closePath]; - [openTriangle fill]; - } -} - void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const { QPainterPath focusRingPath; @@ -2092,11 +2095,17 @@ QMacStyle::QMacStyle() Q_D(QMacStyle); QMacAutoReleasePool pool; - d->receiver = [[NotificationReceiver alloc] init]; + d->receiver = [[NotificationReceiver alloc] initWithPrivateStyle:d]; [[NSNotificationCenter defaultCenter] addObserver:d->receiver selector:@selector(scrollBarStyleDidChange:) name:NSPreferredScrollerStyleDidChangeNotification object:nil]; +#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) { + [NSApplication.sharedApplication addObserver:d->receiver forKeyPath:@"effectiveAppearance" + options:NSKeyValueObservingOptionNew context:nullptr]; + } +#endif } QMacStyle::~QMacStyle() @@ -2105,6 +2114,10 @@ QMacStyle::~QMacStyle() QMacAutoReleasePool pool; [[NSNotificationCenter defaultCenter] removeObserver:d->receiver]; +#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) + [NSApplication.sharedApplication removeObserver:d->receiver forKeyPath:@"effectiveAppearance"]; +#endif [d->receiver release]; } @@ -2978,6 +2991,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai const QWidget *w) const { Q_D(const QMacStyle); + const AppearanceSync appSync; QMacCGContext cg(p); QWindow *window = w && w->window() ? w->window()->windowHandle() : nullptr; d->resolveCurrentNSView(window); @@ -3301,15 +3315,8 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai CGContextScaleCTM(cg, 1, -1); CGContextTranslateCTM(cg, -rect.origin.x, -rect.origin.y); - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave && !qt_mac_applicationIsInDarkMode()) { - // When the real system theme is one of the 'Dark' themes, and an application forces the 'Aqua' theme, - // under some conditions (see QTBUG-74515 for more details) NSButtonCell seems to select the 'Dark' - // code path and is becoming transparent, thus 'invisible' on the white background. To workaround this, - // we draw the disclose triangle manually: - qt_drawDisclosureButton(cg, triangleCell.state, (opt->state & State_Selected) && viewHasFocus, rect); - } else { - [triangleCell drawBezelWithFrame:NSRectFromCGRect(rect) inView:[triangleCell controlView]]; - } + [triangleCell drawBezelWithFrame:NSRectFromCGRect(rect) inView:[triangleCell controlView]]; + d->restoreNSGraphicsContext(cg); break; } @@ -3510,6 +3517,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const QWidget *w) const { Q_D(const QMacStyle); + const AppearanceSync sync; QMacCGContext cg(p); QWindow *window = w && w->window() ? w->window()->windowHandle() : nullptr; d->resolveCurrentNSView(window); @@ -4319,12 +4327,15 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter alpha:pc.alphaF()]; s = qt_mac_removeMnemonics(s); - const auto textRect = CGRectMake(xpos, yPos, mi->rect.width() - xm - tabwidth + 1, mi->rect.height()); QMacCGContext cgCtx(p); d->setupNSGraphicsContext(cgCtx, YES); - [s.toNSString() drawInRect:textRect + // Draw at point instead of in rect, as the rect we've computed for the menu item + // is based on the font metrics we got from HarfBuzz, so we may risk having CoreText + // line-break the string if it doesn't fit the given rect. It's better to draw outside + // the rect and possibly overlap something than to have part of the text disappear. + [s.toNSString() drawAtPoint:CGPointMake(xpos, yPos) withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c, NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0]}]; @@ -5100,6 +5111,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const QWidget *widget) const { Q_D(const QMacStyle); + const AppearanceSync sync; QMacCGContext cg(p); QWindow *window = widget && widget->window() ? widget->window()->windowHandle() : nullptr; d->resolveCurrentNSView(window); @@ -6154,8 +6166,9 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, switch (ct) { #if QT_CONFIG(spinbox) case CT_SpinBox: - if (qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { - const int buttonWidth = 20; // FIXME Use subControlRect() + if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + const bool hasButtons = (vopt->buttonSymbols != QAbstractSpinBox::NoButtons); + const int buttonWidth = hasButtons ? proxy()->subControlRect(CC_SpinBox, vopt, SC_SpinBoxUp, widget).width() : 0; sz += QSize(buttonWidth, 0); } break; diff --git a/src/plugins/styles/windowsvista/windowsvista.pro b/src/plugins/styles/windowsvista/windowsvista.pro index f82bcfc91b..c08db7f533 100644 --- a/src/plugins/styles/windowsvista/windowsvista.pro +++ b/src/plugins/styles/windowsvista/windowsvista.pro @@ -10,7 +10,7 @@ SOURCES += qwindowsvistastyle.cpp HEADERS += qwindowsxpstyle_p.h qwindowsxpstyle_p_p.h SOURCES += qwindowsxpstyle.cpp -LIBS_PRIVATE += -lgdi32 -luser32 +QMAKE_USE_PRIVATE += user32 gdi32 # DEFINES/LIBS needed for qwizard_win.cpp and the styles include(../../../widgets/kernel/win.pri) |