diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-05-04 11:33:10 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2020-05-04 17:38:40 +0200 |
commit | 0f7987f0c934b311bd39a5a496ffb0c6128eb8df (patch) | |
tree | dea392deb3c43e377a224271c4f450dcff3fc76f /src | |
parent | ea7d85457d30e915ad470919d2e74867cba9cad8 (diff) | |
parent | f0ea852d4dd6b3139869a952ee92e74cd367866d (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts:
src/corelib/text/qlocale.cpp
src/network/access/qnetworkaccessmanager.cpp
Regenerated tests/auto/testlib/selftests/float/CMakeLists.txt
Change-Id: I5a8ae42511380ca49a38b13c6fa8a3c5df8bed01
Diffstat (limited to 'src')
40 files changed, 501 insertions, 195 deletions
diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index a3f93e5eed..7d6782cd7f 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -788,17 +788,34 @@ static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d) // The data is supposed to be US-ASCII. If it isn't (contains UTF-8), // QDateTime::fromString will fail anyway. dt = QDateTime::fromString(b->asLatin1(), Qt::ISODateWithMs); - } else if (tag == qint64(QCborKnownTags::UnixTime_t) && e.type == QCborValue::Integer) { - dt = QDateTime::fromSecsSinceEpoch(e.value, Qt::UTC); - } else if (tag == qint64(QCborKnownTags::UnixTime_t) && e.type == QCborValue::Double) { - dt = QDateTime::fromMSecsSinceEpoch(qint64(e.fpvalue() * 1000), Qt::UTC); + } else if (tag == qint64(QCborKnownTags::UnixTime_t)) { + qint64 msecs; + bool ok = false; + if (e.type == QCborValue::Integer) { +#if QT_POINTER_SIZE == 8 + // we don't have a fast 64-bit mul_overflow implementation on + // 32-bit architectures. + ok = !mul_overflow(e.value, qint64(1000), &msecs); +#else + static const qint64 Limit = std::numeric_limits<qint64>::max() / 1000; + ok = (e.value > -Limit && e.value < Limit); + if (ok) + msecs = e.value * 1000; +#endif + } else if (e.type == QCborValue::Double) { + ok = convertDoubleTo(round(e.fpvalue() * 1000), &msecs); + } + if (ok) + dt = QDateTime::fromMSecsSinceEpoch(msecs, Qt::UTC); } if (dt.isValid()) { QByteArray text = dt.toString(Qt::ISODateWithMs).toLatin1(); - replaceByteData(text, text.size(), Element::StringIsAscii); - e.type = QCborValue::String; - d->elements[0].value = qint64(QCborKnownTags::DateTimeString); - return QCborValue::DateTime; + if (!text.isEmpty()) { + replaceByteData(text, text.size(), Element::StringIsAscii); + e.type = QCborValue::String; + d->elements[0].value = qint64(QCborKnownTags::DateTimeString); + return QCborValue::DateTime; + } } break; } @@ -810,9 +827,11 @@ static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d) // normalize to a short (decoded) form, so as to save space QUrl url(e.flags & Element::StringIsUtf16 ? b->asQStringRaw() : - b->toUtf8String()); - QByteArray encoded = url.toString(QUrl::DecodeReserved).toUtf8(); - replaceByteData(encoded, encoded.size(), {}); + b->toUtf8String(), QUrl::StrictMode); + if (url.isValid()) { + QByteArray encoded = url.toString(QUrl::DecodeReserved).toUtf8(); + replaceByteData(encoded, encoded.size(), {}); + } } return QCborValue::Url; } @@ -1561,8 +1580,6 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) if (newSize > MaxByteArraySize) return -1; - // since usedData <= data.size(), this can't overflow - usedData += increment; data.resize(newSize); return offset; }; @@ -1635,7 +1652,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) } // update size - if (e.flags & Element::HasByteData) { + if (r.status == QCborStreamReader::EndOfString && e.flags & Element::HasByteData) { auto b = new (dataPtr() + e.value) ByteData; b->len = data.size() - e.value - int(sizeof(*b)); usedData += b->len; diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp index a03855d4a3..bf2bdb957d 100644 --- a/src/corelib/serialization/qjsonobject.cpp +++ b/src/corelib/serialization/qjsonobject.cpp @@ -577,7 +577,7 @@ void QJsonObject::removeImpl(T key) if (!keyExists) return; - removeAt(index); + removeAt(index / 2); } #if QT_STRINGVIEW_LEVEL < 2 @@ -629,7 +629,7 @@ QJsonValue QJsonObject::takeImpl(T key) return QJsonValue(QJsonValue::Undefined); const QJsonValue v = QJsonPrivate::Value::fromTrustedCbor(o->extractAt(index + 1)); - removeAt(index); + removeAt(index / 2); return v; } @@ -1486,8 +1486,8 @@ void QJsonObject::setValueAt(int i, const QJsonValue &val) void QJsonObject::removeAt(int index) { detach2(); - o->removeAt(index + 1); - o->removeAt(index); + o->removeAt(2 * index + 1); + o->removeAt(2 * index); } size_t qHash(const QJsonObject &object, size_t seed) diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 6eb84f6c99..f87cf41bf1 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -1781,57 +1781,39 @@ const QString::Null QString::null = { }; /*! \typedef QString::const_iterator - This typedef provides an STL-style const iterator for QString. - \sa QString::iterator */ /*! \typedef QString::iterator - The QString::iterator typedef provides an STL-style non-const - iterator for QString. - \sa QString::const_iterator */ /*! \typedef QString::const_reverse_iterator \since 5.6 - This typedef provides an STL-style const reverse iterator for QString. - \sa QString::reverse_iterator, QString::const_iterator */ /*! \typedef QString::reverse_iterator \since 5.6 - This typedef provides an STL-style non-const reverse iterator for QString. - \sa QString::const_reverse_iterator, QString::iterator */ /*! \typedef QString::size_type - - The QString::size_type typedef provides an STL-style type for sizes (int). */ /*! \typedef QString::difference_type - - The QString::size_type typedef provides an STL-style type for difference between pointers. */ /*! \typedef QString::const_reference - - This typedef provides an STL-style const reference for a QString element (QChar). */ /*! \typedef QString::reference - - This typedef provides an STL-style - reference for a QString element (QChar). */ /*! @@ -1849,8 +1831,6 @@ const QString::Null QString::null = { }; /*! \typedef QString::value_type - - This typedef provides an STL-style value type for QString. */ /*! \fn QString::iterator QString::begin() @@ -9397,8 +9377,6 @@ QString &QString::setRawData(const QChar *unicode, int size) \typedef QLatin1String::iterator \since 5.10 - This typedef provides an STL-style const iterator for QLatin1String. - QLatin1String does not support mutable iterators, so this is the same as const_iterator. @@ -9409,8 +9387,6 @@ QString &QString::setRawData(const QChar *unicode, int size) \typedef QLatin1String::const_iterator \since 5.10 - This typedef provides an STL-style const iterator for QLatin1String. - \sa iterator, const_reverse_iterator */ @@ -9418,8 +9394,6 @@ QString &QString::setRawData(const QChar *unicode, int size) \typedef QLatin1String::reverse_iterator \since 5.10 - This typedef provides an STL-style const reverse iterator for QLatin1String. - QLatin1String does not support mutable reverse iterators, so this is the same as const_reverse_iterator. @@ -9430,8 +9404,6 @@ QString &QString::setRawData(const QChar *unicode, int size) \typedef QLatin1String::const_reverse_iterator \since 5.10 - This typedef provides an STL-style const reverse iterator for QLatin1String. - \sa reverse_iterator, const_iterator */ @@ -10332,8 +10304,6 @@ QDataStream &operator>>(QDataStream &in, QString &str) \typedef QStringRef::const_iterator \since 5.4 - This typedef provides an STL-style const iterator for QStringRef. - \sa QStringRef::const_reverse_iterator */ @@ -10341,8 +10311,6 @@ QDataStream &operator>>(QDataStream &in, QString &str) \typedef QStringRef::const_reverse_iterator \since 5.7 - This typedef provides an STL-style const reverse iterator for QStringRef. - \sa QStringRef::const_iterator */ diff --git a/src/corelib/tools/qcommandlineparser.cpp b/src/corelib/tools/qcommandlineparser.cpp index 48501f5271..a3d2a2f7c0 100644 --- a/src/corelib/tools/qcommandlineparser.cpp +++ b/src/corelib/tools/qcommandlineparser.cpp @@ -1063,18 +1063,23 @@ QString QCommandLineParser::helpText() const return d->helpText(false); } -static QString wrapText(const QString &names, int longestOptionNameString, const QString &description) +static QString wrapText(const QString &names, int optionNameMaxWidth, const QString &description) { const QLatin1Char nl('\n'); const QLatin1String indentation(" "); - if (description.isEmpty()) - return indentation + names + nl; - QString text = indentation + names.leftJustified(longestOptionNameString) + QLatin1Char(' '); - const int indent = text.length(); + // In case the list of option names is very long, wrap it as well + int nameIndex = 0; + auto nextNameSection = [&]() { + QString section = names.mid(nameIndex, optionNameMaxWidth); + nameIndex += section.size(); + return section; + }; + + QString text; int lineStart = 0; int lastBreakable = -1; - const int max = 79 - indent; + const int max = 79 - (indentation.size() + optionNameMaxWidth + 1); int x = 0; const int len = description.length(); @@ -1103,8 +1108,7 @@ static QString wrapText(const QString &names, int longestOptionNameString, const if (breakAt != -1) { const int numChars = breakAt - lineStart; //qDebug() << "breakAt=" << description.at(breakAt) << "breakAtSpace=" << breakAtSpace << lineStart << "to" << breakAt << description.mid(lineStart, numChars); - if (lineStart > 0) - text += QString(indent, QLatin1Char(' ')); + text += indentation + nextNameSection().leftJustified(optionNameMaxWidth) + QLatin1Char(' '); text += description.midRef(lineStart, numChars) + nl; x = 0; lastBreakable = -1; @@ -1115,6 +1119,10 @@ static QString wrapText(const QString &names, int longestOptionNameString, const } } + while (nameIndex < names.size()) { + text += indentation + nextNameSection() + nl; + } + return text; } @@ -1158,11 +1166,12 @@ QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const longestOptionNameString = qMax(longestOptionNameString, optionNamesString.length()); } ++longestOptionNameString; + const int optionNameMaxWidth = qMin(50, longestOptionNameString); auto optionNameIterator = optionNameList.cbegin(); for (const QCommandLineOption &option : qAsConst(options)) { if (option.flags() & QCommandLineOption::HiddenFromHelp) continue; - text += wrapText(*optionNameIterator, longestOptionNameString, option.description()); + text += wrapText(*optionNameIterator, optionNameMaxWidth, option.description()); ++optionNameIterator; } if (!positionalArgumentDefinitions.isEmpty()) { @@ -1170,7 +1179,7 @@ QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const text += nl; text += QCommandLineParser::tr("Arguments:") + nl; for (const PositionalArgumentDefinition &arg : positionalArgumentDefinitions) - text += wrapText(arg.name, longestOptionNameString, arg.description); + text += wrapText(arg.name, optionNameMaxWidth, arg.description); } return text; } diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 34d2b8d8c7..506ebc797f 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -2016,6 +2016,21 @@ static void convert_Mono_to_Indexed8(QImageData *dest, const QImageData *src, Qt } } +static void copy_8bit_pixels(QImageData *dest, const QImageData *src) +{ + if (src->bytes_per_line == dest->bytes_per_line) { + memcpy(dest->data, src->data, src->bytes_per_line * src->height); + } else { + const uchar *sdata = src->data; + uchar *ddata = dest->data; + for (int y = 0; y < src->height; ++y) { + memcpy(ddata, sdata, src->width); + sdata += src->bytes_per_line; + ddata += dest->bytes_per_line; + } + } +} + static void convert_Indexed8_to_Alpha8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(src->format == QImage::Format_Indexed8); @@ -2031,11 +2046,15 @@ static void convert_Indexed8_to_Alpha8(QImageData *dest, const QImageData *src, } if (simpleCase) - memcpy(dest->data, src->data, src->bytes_per_line * src->height); + copy_8bit_pixels(dest, src); else { - qsizetype size = src->bytes_per_line * src->height; - for (qsizetype i = 0; i < size; ++i) { - dest->data[i] = translate[src->data[i]]; + const uchar *sdata = src->data; + uchar *ddata = dest->data; + for (int y = 0; y < src->height; ++y) { + for (int x = 0; x < src->width; ++x) + ddata[x] = translate[sdata[x]]; + sdata += src->bytes_per_line; + ddata += dest->bytes_per_line; } } } @@ -2055,11 +2074,15 @@ static void convert_Indexed8_to_Grayscale8(QImageData *dest, const QImageData *s } if (simpleCase) - memcpy(dest->data, src->data, src->bytes_per_line * src->height); + copy_8bit_pixels(dest, src); else { - qsizetype size = src->bytes_per_line * src->height; - for (qsizetype i = 0; i < size; ++i) { - dest->data[i] = translate[src->data[i]]; + const uchar *sdata = src->data; + uchar *ddata = dest->data; + for (int y = 0; y < src->height; ++y) { + for (int x = 0; x < src->width; ++x) + ddata[x] = translate[sdata[x]]; + sdata += src->bytes_per_line; + ddata += dest->bytes_per_line; } } } @@ -2107,7 +2130,7 @@ static void convert_Alpha8_to_Indexed8(QImageData *dest, const QImageData *src, Q_ASSERT(src->format == QImage::Format_Alpha8); Q_ASSERT(dest->format == QImage::Format_Indexed8); - memcpy(dest->data, src->data, src->bytes_per_line * src->height); + copy_8bit_pixels(dest, src); dest->colortable = defaultColorTables->alpha; } @@ -2117,8 +2140,7 @@ static void convert_Grayscale8_to_Indexed8(QImageData *dest, const QImageData *s Q_ASSERT(src->format == QImage::Format_Grayscale8); Q_ASSERT(dest->format == QImage::Format_Indexed8); - memcpy(dest->data, src->data, src->bytes_per_line * src->height); - + copy_8bit_pixels(dest, src); dest->colortable = defaultColorTables->gray; } diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 834f969680..13a5bbb009 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -951,6 +951,7 @@ QWheelEvent::~QWheelEvent() /*! \fn QPoint QWheelEvent::position() const + \since 5.14 Returns the position of the mouse cursor relative to the widget that received the event. diff --git a/src/gui/painting/qcoregraphics.mm b/src/gui/painting/qcoregraphics.mm index 1125a9183c..0030e8e29c 100644 --- a/src/gui/painting/qcoregraphics.mm +++ b/src/gui/painting/qcoregraphics.mm @@ -162,12 +162,12 @@ QT_END_NAMESPACE if (icon.isNull()) return nil; - auto nsImage = [[NSImage alloc] initWithSize:NSZeroSize]; - auto availableSizes = icon.availableSizes(); if (availableSizes.isEmpty() && size > 0) availableSizes << QSize(size, size); + auto nsImage = [[[NSImage alloc] initWithSize:NSZeroSize] autorelease]; + for (QSize size : qAsConst(availableSizes)) { QImage image = icon.pixmap(size).toImage(); if (image.isNull()) @@ -182,12 +182,15 @@ QT_END_NAMESPACE [nsImage addRepresentation:[imageRep autorelease]]; } + if (!nsImage.representations.count) + return nil; + [nsImage setTemplate:icon.isMask()]; if (size) nsImage.size = CGSizeMake(size, size); - return [nsImage autorelease]; + return nsImage; } @end diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 959566d4c1..cb87cfa09b 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -420,7 +420,9 @@ QFontEngineData::~QFontEngineData() be removed with removeSubstitutions(). Use substitute() to retrieve a family's first substitute, or the family name itself if it has no substitutes. Use substitutes() to retrieve a list of a family's - substitutes (which may be empty). + substitutes (which may be empty). After substituting a font, you must + trigger the updating of the font by destroying and re-creating all + QFont objects. Every QFont has a key() which you can use, for example, as the key in a cache or dictionary. If you want to store a user's font @@ -1864,6 +1866,9 @@ QStringList QFont::substitutes(const QString &familyName) Inserts \a substituteName into the substitution table for the family \a familyName. + After substituting a font, trigger the updating of the font by destroying + and re-creating all QFont objects. + \sa insertSubstitutions(), removeSubstitutions(), substitutions(), substitute(), substitutes() */ void QFont::insertSubstitution(const QString &familyName, @@ -1882,6 +1887,10 @@ void QFont::insertSubstitution(const QString &familyName, Inserts the list of families \a substituteNames into the substitution list for \a familyName. + After substituting a font, trigger the updating of the font by destroying + and re-creating all QFont objects. + + \sa insertSubstitution(), removeSubstitutions(), substitutions(), substitute() */ void QFont::insertSubstitutions(const QString &familyName, diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp index 22fdc5bb0b..4986b36ab1 100644 --- a/src/network/access/qnetworkaccesscachebackend.cpp +++ b/src/network/access/qnetworkaccesscachebackend.cpp @@ -123,6 +123,12 @@ bool QNetworkAccessCacheBackend::sendCacheContents() return true; } +bool QNetworkAccessCacheBackend::start() +{ + open(); + return true; +} + void QNetworkAccessCacheBackend::closeDownstreamChannel() { } diff --git a/src/network/access/qnetworkaccesscachebackend_p.h b/src/network/access/qnetworkaccesscachebackend_p.h index dfb0ce84d9..ceb02946dc 100644 --- a/src/network/access/qnetworkaccesscachebackend_p.h +++ b/src/network/access/qnetworkaccesscachebackend_p.h @@ -68,6 +68,7 @@ public: void open() override; void closeDownstreamChannel() override; void closeUpstreamChannel(); + bool start() override; void upstreamReadyRead(); void downstreamReadyWrite() override; diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index e5414665de..1c5e29f8ac 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp index af8b39bab6..5b84014789 100644 --- a/src/network/access/qnetworkreplywasmimpl.cpp +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -245,7 +245,7 @@ void QNetworkReplyWasmImplPrivate::doSendRequest() attr.password = request.url().password().toUtf8(); } - attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY | EMSCRIPTEN_FETCH_PERSIST_FILE | EMSCRIPTEN_FETCH_REPLACE; + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; QNetworkRequest::CacheLoadControl CacheLoadControlAttribute = (QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(); diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index c95b1280b0..3ac54605e4 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -71,6 +71,7 @@ QT_BEGIN_NAMESPACE static QByteArray qNtlmPhase1(); static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data); #if QT_CONFIG(sspi) // SSPI +static bool q_SSPI_library_load(); static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method, const QString& host); static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method, @@ -503,8 +504,13 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet if (challenge.isEmpty()) { #if QT_CONFIG(sspi) // SSPI QByteArray phase1Token; - if (user.isEmpty()) // Only pull from system if no user was specified in authenticator + if (user.isEmpty()) { // Only pull from system if no user was specified in authenticator phase1Token = qSspiStartup(this, method, host); + } else if (!q_SSPI_library_load()) { + // Since we're not running qSspiStartup we have to make sure the library is loaded + qWarning("Failed to load the SSPI libraries"); + return ""; + } if (!phase1Token.isEmpty()) { response = phase1Token.toBase64(); phase = Phase2; diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp index 6558643386..5634332a67 100644 --- a/src/network/ssl/qasn1element.cpp +++ b/src/network/ssl/qasn1element.cpp @@ -45,6 +45,7 @@ #include <QtCore/qvector.h> #include <QDebug> +#include <limits> #include <locale> QT_BEGIN_NAMESPACE @@ -120,7 +121,7 @@ bool QAsn1Element::read(QDataStream &stream) return false; // length - qint64 length = 0; + quint64 length = 0; quint8 first; stream >> first; if (first & 0x80) { @@ -139,11 +140,13 @@ bool QAsn1Element::read(QDataStream &stream) length = (first & 0x7f); } + if (length > quint64(std::numeric_limits<int>::max())) + return false; // value QByteArray tmpValue; tmpValue.resize(length); int count = stream.readRawData(tmpValue.data(), tmpValue.size()); - if (count != length) + if (count != int(length)) return false; mType = tmpType; diff --git a/src/network/ssl/qdtls.cpp b/src/network/ssl/qdtls.cpp index a2280a7d10..74fb691dde 100644 --- a/src/network/ssl/qdtls.cpp +++ b/src/network/ssl/qdtls.cpp @@ -278,8 +278,6 @@ /*! \typedef QDtls::GeneratorParameters - - This is a synonym for QDtlsClientVerifier::GeneratorParameters. */ /*! diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index e0dd103fbc..2d19809435 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -481,7 +481,9 @@ QList<QCocoaWindow *> *QCocoaIntegration::popupWindowStack() void QCocoaIntegration::setApplicationIcon(const QIcon &icon) const { - NSApp.applicationIconImage = [NSImage imageFromQIcon:icon]; + // Fall back to a size that looks good on the highest resolution screen available + auto fallbackSize = NSApp.dockTile.size.width * qGuiApp->devicePixelRatio(); + NSApp.applicationIconImage = [NSImage imageFromQIcon:icon withSize:fallbackSize]; } void QCocoaIntegration::beep() const diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index e4dd4cf6c6..6a3172fb19 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -614,7 +614,11 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) QRect windowRect; for (uint i = 0; i < displayCount; ++i) { QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(displays[i])).toRect(); - windowRect = windowRect.united(displayBounds); + // Only include the screen if it is positioned past the x/y position + if ((displayBounds.x() >= x || displayBounds.right() > x) && + (displayBounds.y() >= y || displayBounds.bottom() > y)) { + windowRect = windowRect.united(displayBounds); + } } if (grabRect.width() < 0) grabRect.setWidth(windowRect.width()); @@ -631,6 +635,11 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) auto display = displays[i]; QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(display)).toRect(); QRect grabBounds = displayBounds.intersected(grabRect); + if (grabBounds.isNull()) { + destinations.append(QRect()); + images.append(QImage()); + continue; + } QRect displayLocalGrabBounds = QRect(QPoint(grabBounds.topLeft() - displayBounds.topLeft()), grabBounds.size()); QImage displayImage = qt_mac_toQImage(QCFType<CGImageRef>(CGDisplayCreateImageForRect(display, displayLocalGrabBounds.toCGRect()))); displayImage.setDevicePixelRatio(displayImage.size().width() / displayLocalGrabBounds.size().width()); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index e471d2af28..14833267d2 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -368,8 +368,18 @@ void QCocoaWindow::setVisible(bool visible) } else if (window()->modality() == Qt::ApplicationModal) { // Show the window as application modal eventDispatcher()->beginModalSession(window()); - } else if (m_view.window.canBecomeKeyWindow && !eventDispatcher()->hasModalSession()) { - [m_view.window makeKeyAndOrderFront:nil]; + } else if (m_view.window.canBecomeKeyWindow) { + bool shouldBecomeKeyNow = !NSApp.modalWindow || m_view.window.worksWhenModal; + + // Panels with becomesKeyOnlyIfNeeded set should not activate until a view + // with needsPanelToBecomeKey, for example a line edit, is clicked. + if ([m_view.window isKindOfClass:[NSPanel class]]) + shouldBecomeKeyNow &= !(static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded); + + if (shouldBecomeKeyNow) + [m_view.window makeKeyAndOrderFront:nil]; + else + [m_view.window orderFront:nil]; } else { [m_view.window orderFront:nil]; } @@ -891,10 +901,13 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon) QMacAutoReleasePool pool; - if (icon.isNull()) + if (icon.isNull()) { iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename]; - else - iconButton.image = [NSImage imageFromQIcon:icon]; + } else { + // Fall back to a size that looks good on the highest resolution screen available + auto fallbackSize = iconButton.frame.size.height * qGuiApp->devicePixelRatio(); + iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize]; + } } void QCocoaWindow::setAlertState(bool enabled) @@ -1814,8 +1827,17 @@ void QCocoaWindow::updateNSToolbar() bool QCocoaWindow::testContentBorderAreaPosition(int position) const { - return isContentView() && m_drawContentBorderGradient && - 0 <= position && position < [m_view.window contentBorderThicknessForEdge:NSMaxYEdge]; + if (!m_drawContentBorderGradient || !isContentView()) + return false; + + // Determine if the given y postion (relative to the content area) is inside the + // unified toolbar area. Note that the value returned by contentBorderThicknessForEdge + // includes the title bar height; subtract it. + const int contentBorderThickness = [m_view.window contentBorderThicknessForEdge:NSMaxYEdge]; + const NSRect frameRect = m_view.window.frame; + const NSRect contentRect = [m_view.window contentRectForFrameRect:frameRect]; + const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height; + return 0 <= position && position < (contentBorderThickness - titlebarHeight); } qreal QCocoaWindow::devicePixelRatio() const diff --git a/src/plugins/platforms/cocoa/qcocoawindowmanager.mm b/src/plugins/platforms/cocoa/qcocoawindowmanager.mm index 9c45d8c7fc..5e218157c2 100644 --- a/src/plugins/platforms/cocoa/qcocoawindowmanager.mm +++ b/src/plugins/platforms/cocoa/qcocoawindowmanager.mm @@ -100,6 +100,18 @@ void QCocoaWindowManager::modalSessionChanged() } } } + + // Our worksWhenModal implementation is declarative and will normally be picked + // up by AppKit when needed, but to make sure AppKit also reflects the state + // in the window tag, so that the window can be ordered front by clicking it, + // we need to explicitly call setWorksWhenModal. + for (id window in NSApp.windows) { + if ([window isKindOfClass:[QNSPanel class]]) { + auto *panel = static_cast<QNSPanel *>(window); + // Call setter to tell AppKit that our state has changed + [panel setWorksWhenModal:panel.worksWhenModal]; + } + } } static void initializeWindowManager() { Q_UNUSED(QCocoaWindowManager::instance()); } diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac.mm b/src/plugins/platforms/cocoa/qmultitouch_mac.mm index 95256657fe..ac2317b217 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac.mm +++ b/src/plugins/platforms/cocoa/qmultitouch_mac.mm @@ -184,7 +184,10 @@ QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch) if (_touchCount != _currentTouches.size()) { // Remove all instances, and basically start from scratch: touchPoints.clear(); - for (QCocoaTouch *qcocoaTouch : _currentTouches) { + // Deleting touch points will remove them from current touches, + // so we make a copy of the touches before iterating them. + const auto currentTouchesSnapshot = _currentTouches; + for (QCocoaTouch *qcocoaTouch : currentTouchesSnapshot) { if (!_updateInternalStateOnly) { qcocoaTouch->_touchPoint.state = Qt::TouchPointReleased; touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint); diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm index 6b4e110af2..311c291252 100644 --- a/src/plugins/platforms/cocoa/qnswindow.mm +++ b/src/plugins/platforms/cocoa/qnswindow.mm @@ -158,7 +158,78 @@ static bool isMouseEvent(NSEvent *ev) #define QNSWINDOW_PROTOCOL_IMPLMENTATION 1 #include "qnswindow.mm" #undef QNSWINDOW_PROTOCOL_IMPLMENTATION + +- (BOOL)worksWhenModal +{ + if (!m_platformWindow) + return NO; + + // Conceptually there are two sets of windows we need consider: + // + // - windows 'lower' in the modal session stack + // - windows 'within' the current modal session + // + // The first set of windows should always be blocked by the current + // modal session, regardless of window type. The latter set may contain + // windows with a transient parent, which from Qt's point of view makes + // them 'child' windows, so we treat them as operable within the current + // modal session. + + if (!NSApp.modalWindow) + return NO; + + // If the current modal window (top level modal session) is not a Qt window we + // have no way of knowing if this window is transient child of the modal window. + if (![NSApp.modalWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) + return NO; + + if (auto *modalWindow = static_cast<QCocoaNSWindow *>(NSApp.modalWindow).platformWindow) { + if (modalWindow->window()->isAncestorOf(m_platformWindow->window(), QWindow::IncludeTransients)) + return YES; + } + + return NO; +} +@end + +#if !defined(QT_APPLE_NO_PRIVATE_APIS) +// When creating an NSWindow the worksWhenModal function is queried, +// and the resulting state is used to set the corresponding window tag, +// which the window server uses to determine whether or not the window +// should be allowed to activate via mouse clicks in the title-bar. +// Unfortunately, prior to macOS 10.15, this window tag was never +// updated after the initial assignment in [NSWindow _commonAwake], +// which meant that windows that dynamically change their worksWhenModal +// state will behave as if they were never allowed to work when modal. +// We work around this by manually updating the window tag when needed. + +typedef uint32_t CGSConnectionID; +typedef uint32_t CGSWindowID; + +extern "C" { +CGSConnectionID CGSMainConnectionID() __attribute__((weak_import)); +OSStatus CGSSetWindowTags(const CGSConnectionID, const CGSWindowID, int *, int) __attribute__((weak_import)); +OSStatus CGSClearWindowTags(const CGSConnectionID, const CGSWindowID, int *, int) __attribute__((weak_import)); +} + +@interface QNSPanel (WorksWhenModalWindowTagWorkaround) @end +@implementation QNSPanel (WorksWhenModalWindowTagWorkaround) +- (void)setWorksWhenModal:(BOOL)worksWhenModal +{ + [super setWorksWhenModal:worksWhenModal]; + + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSCatalina) { + if (CGSMainConnectionID && CGSSetWindowTags && CGSClearWindowTags) { + static int kWorksWhenModalWindowTag = 0x40; + auto *function = worksWhenModal ? CGSSetWindowTags : CGSClearWindowTags; + function(CGSMainConnectionID(), self.windowNumber, &kWorksWhenModalWindowTag, 64); + } else { + qWarning() << "Missing APIs for window tag handling, can not update worksWhenModal state"; + } + } +} @end +#endif // QT_APPLE_NO_PRIVATE_APIS #else // QNSWINDOW_PROTOCOL_IMPLMENTATION @@ -237,17 +308,6 @@ static bool isMouseEvent(NSEvent *ev) return canBecomeMain; } -- (BOOL)worksWhenModal -{ - if (m_platformWindow && [self isKindOfClass:[QNSPanel class]]) { - Qt::WindowType type = m_platformWindow->window()->type(); - if (type == Qt::Popup || type == Qt::Dialog || type == Qt::Tool) - return YES; - } - - return [super worksWhenModal]; -} - - (BOOL)isOpaque { return m_platformWindow ? m_platformWindow->isOpaque() : [super isOpaque]; diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index b0e60d76fc..4dbb0a4cdf 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -45,6 +45,7 @@ #include "qiosapplicationdelegate.h" #include "qiosviewcontroller.h" #include "quiview.h" +#include "qiostheme.h" #include <QtCore/private/qcore_mac_p.h> @@ -208,6 +209,18 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) [super sendEvent:event]; } +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + [super traitCollectionDidChange:previousTraitCollection]; + + if (self.screen == UIScreen.mainScreen) { + if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) { + QIOSTheme::initializeSystemPalette(); + QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>(nullptr); + } + } +} + @end // ------------------------------------------------------------------------- diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index a3350bda87..19e476a064 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -416,6 +416,17 @@ if (unknownAction) return [super canPerformAction:action withSender:sender]; + + QObject *focusObject = QGuiApplication::focusObject(); + if (focusObject && focusObject->property("qt_im_readonly").toBool()) { + // exceptional menu items for read-only views: do include Copy, do not include Paste etc. + if (action == @selector(cut:) + || action == @selector(paste:) + || action == @selector(delete:)) + return NO; + if (action == @selector(copy:)) + return YES; + } return (hasSelection && isEditAction) || (!hasSelection && isSelectAction); } diff --git a/src/plugins/platforms/ios/qiostheme.h b/src/plugins/platforms/ios/qiostheme.h index c917679a91..c9d833713d 100644 --- a/src/plugins/platforms/ios/qiostheme.h +++ b/src/plugins/platforms/ios/qiostheme.h @@ -65,9 +65,12 @@ public: static const char *name; + static void initializeSystemPalette(); + private: mutable QHash<QPlatformTheme::Font, QFont *> m_fonts; - QPalette m_systemPalette; + + static QPalette s_systemPalette; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm index 5534264a60..626fbb66fe 100644 --- a/src/plugins/platforms/ios/qiostheme.mm +++ b/src/plugins/platforms/ios/qiostheme.mm @@ -44,6 +44,7 @@ #include <QtCore/private/qcore_mac_p.h> #include <QtGui/QFont> +#include <QtGui/private/qcoregraphics_p.h> #include <QtFontDatabaseSupport/private/qcoretextfontdatabase_p.h> #include <QtGui/private/qguiapplication_p.h> @@ -63,10 +64,8 @@ QT_BEGIN_NAMESPACE const char *QIOSTheme::name = "ios"; QIOSTheme::QIOSTheme() - : m_systemPalette(*QPlatformTheme::palette(QPlatformTheme::SystemPalette)) { - m_systemPalette.setBrush(QPalette::Highlight, QColor(204, 221, 237)); - m_systemPalette.setBrush(QPalette::HighlightedText, Qt::black); + initializeSystemPalette(); } QIOSTheme::~QIOSTheme() @@ -74,10 +73,41 @@ QIOSTheme::~QIOSTheme() qDeleteAll(m_fonts); } +QPalette QIOSTheme::s_systemPalette; + +void QIOSTheme::initializeSystemPalette() +{ + Q_DECL_IMPORT QPalette qt_fusionPalette(void); + s_systemPalette = qt_fusionPalette(); + + if (@available(ios 13.0, *)) { + s_systemPalette.setBrush(QPalette::Window, qt_mac_toQBrush(UIColor.systemGroupedBackgroundColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::WindowText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); + + s_systemPalette.setBrush(QPalette::Base, qt_mac_toQBrush(UIColor.secondarySystemGroupedBackgroundColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::Text, qt_mac_toQBrush(UIColor.labelColor.CGColor)); + + s_systemPalette.setBrush(QPalette::Button, qt_mac_toQBrush(UIColor.secondarySystemBackgroundColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::ButtonText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); + + s_systemPalette.setBrush(QPalette::Active, QPalette::BrightText, qt_mac_toQBrush(UIColor.lightTextColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::PlaceholderText, qt_mac_toQBrush(UIColor.placeholderTextColor.CGColor)); + + s_systemPalette.setBrush(QPalette::Active, QPalette::Link, qt_mac_toQBrush(UIColor.linkColor.CGColor)); + s_systemPalette.setBrush(QPalette::Active, QPalette::LinkVisited, qt_mac_toQBrush(UIColor.linkColor.CGColor)); + + s_systemPalette.setBrush(QPalette::Highlight, QColor(11, 70, 150, 60)); + s_systemPalette.setBrush(QPalette::HighlightedText, qt_mac_toQBrush(UIColor.labelColor.CGColor)); + } else { + s_systemPalette.setBrush(QPalette::Highlight, QColor(204, 221, 237)); + s_systemPalette.setBrush(QPalette::HighlightedText, Qt::black); + } +} + const QPalette *QIOSTheme::palette(QPlatformTheme::Palette type) const { if (type == QPlatformTheme::SystemPalette) - return &m_systemPalette; + return &s_systemPalette; return 0; } diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index c08e5b0cf1..871f4b8fb0 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -3964,13 +3964,19 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // which makes no sense on tabs. NSPopUpArrowPosition oldPosition = NSPopUpArrowAtCenter; NSPopUpButtonCell *pbCell = nil; - if (isPopupButton) { + auto rAdjusted = r; + if (isPopupButton && tp == QStyleOptionTab::OnlyOneTab) { pbCell = static_cast<NSPopUpButtonCell *>(pb.cell); oldPosition = pbCell.arrowPosition; pbCell.arrowPosition = NSPopUpNoArrow; + if (pb.state == NSOffState) { + // NSPopUpButton in this state is smaller. + rAdjusted.origin.x -= 3; + rAdjusted.size.width += 6; + } } - [pb.cell drawBezelWithFrame:r inView:pb.superview]; + [pb.cell drawBezelWithFrame:rAdjusted inView:pb.superview]; if (pbCell) // Restore, we may reuse it for a ComboBox. pbCell.arrowPosition = oldPosition; diff --git a/src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp b/src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp index eda0f430a1..cfa999a6f4 100644 --- a/src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp +++ b/src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp @@ -84,7 +84,7 @@ class TestQString : public QObject void wrapInFunction() { //! [1] -QVERIFY2(qIsNaN(0.0 / 0.0), "Ill-defined division produced unambiguous result."); +QVERIFY2(QFileInfo("file.txt").exists(), "file.txt does not exist."); //! [1] //! [2] diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 8b5df06d82..e1da9e617c 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -2541,16 +2541,21 @@ bool QTest::compare_helper(bool success, const char *failureMsg, } template <typename T> -static bool floatingCompare(const T &t1, const T &t2) +static bool floatingCompare(const T &actual, const T &expected) { - switch (qFpClassify(t1)) + switch (qFpClassify(expected)) { case FP_INFINITE: - return (t1 < 0) == (t2 < 0) && qFpClassify(t2) == FP_INFINITE; + return (expected < 0) == (actual < 0) && qFpClassify(actual) == FP_INFINITE; case FP_NAN: - return qFpClassify(t2) == FP_NAN; + return qFpClassify(actual) == FP_NAN; default: - return qFuzzyCompare(t1, t2); + if (!qFuzzyIsNull(expected)) + return qFuzzyCompare(actual, expected); + Q_FALLTHROUGH(); + case FP_SUBNORMAL: // subnormal is always fuzzily null + case FP_ZERO: + return qFuzzyIsNull(actual); } } diff --git a/src/testlib/qtestcase.qdoc b/src/testlib/qtestcase.qdoc index e4e1825bb5..72f8cdaf8c 100644 --- a/src/testlib/qtestcase.qdoc +++ b/src/testlib/qtestcase.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the documentation of the Qt Toolkit. @@ -109,19 +109,18 @@ continues. If not, a failure is recorded in the test log and the test function returns without attempting any later checks. - Always respect QCOMPARE() parameter semantics. The first parameter passed to it - should always be the actual value produced by the code-under-test, while the - second parameter should always be the expected value. When the values don't - match, QCOMPARE() prints them with the labels \e Actual and \e Expected. - If the parameter order is swapped, debugging a failing test can be confusing. + Always respect QCOMPARE() parameter semantics. The first parameter passed to + it should always be the actual value produced by the code-under-test, while + the second parameter should always be the expected value. When the values + don't match, QCOMPARE() prints them with the labels \e Actual and \e + Expected. If the parameter order is swapped, debugging a failing test can be + confusing and tests expecting zero may fail due to rounding errors. When comparing floating-point types (\c float, \c double, and \c qfloat16), - \l qFuzzyCompare() is used for finite values. Infinities match if they have + \l qFuzzyCompare() is used for finite values. If qFuzzyIsNull() is true for + both values, they are also considered equal. Infinities match if they have the same sign, and any NaN as actual value matches with any NaN as expected - value (even though NaN != NaN, even when they're identical). This means that - expecting 0 can fail when the actual value may be affected by rounding errors. - One solution to this is to offset both actual and expected values by adding - some suitable constant (such as 1). + value (even though NaN != NaN, even when they're identical). QCOMPARE() tries to output the contents of the values if the comparison fails, so it is visible from the test log why the comparison failed. diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index 5b4d861b8d..5b890e0f8d 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -1378,6 +1378,9 @@ QStringList qt_make_filter_list(const QString &filter) \snippet code/src_gui_dialogs_qfiledialog.cpp 6 + \note This is not supported on Android's native file dialog. Use + \l{setMimeTypeFilters()} instead. + \sa setMimeTypeFilters(), setNameFilters() */ void QFileDialog::setNameFilter(const QString &filter) @@ -1428,6 +1431,9 @@ QStringList qt_strip_filters(const QStringList &filters) filters for each file type. For example, JPEG images have three possible extensions; if your application can open such files, selecting the \c image/jpeg mime type as a filter will allow you to open all of them. + + \note This is not supported on Android's native file dialog. Use + \l{setMimeTypeFilters()} instead. */ void QFileDialog::setNameFilters(const QStringList &filters) { diff --git a/src/widgets/doc/snippets/customviewstyle.cpp b/src/widgets/doc/snippets/customviewstyle.cpp index b9c10cb31d..1e4cf2b711 100644 --- a/src/widgets/doc/snippets/customviewstyle.cpp +++ b/src/widgets/doc/snippets/customviewstyle.cpp @@ -50,11 +50,10 @@ #include <QtWidgets> -#include "customviewstyle.h" +#include "./customstyle/customstyle.h" - - -void CustomViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const +void CustomStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const { //![0] diff --git a/src/widgets/doc/snippets/filedialogurls.cpp b/src/widgets/doc/snippets/filedialogurls.cpp index cd91e797e7..4b73a6c818 100644 --- a/src/widgets/doc/snippets/filedialogurls.cpp +++ b/src/widgets/doc/snippets/filedialogurls.cpp @@ -49,11 +49,10 @@ ****************************************************************************/ #include <QtGui> +#include <QFileDialog> -int main(int argv, char **args) +int loadFileDialog() { - QApplication app(argv, args); - //![0] QList<QUrl> urls; urls << QUrl::fromLocalFile("/Users/foo/Code/qt5") @@ -66,6 +65,5 @@ int main(int argv, char **args) // ... } //![0] - - return app.exec(); + return 1; } diff --git a/src/widgets/doc/snippets/graphicssceneadditemsnippet.cpp b/src/widgets/doc/snippets/graphicssceneadditemsnippet.cpp index 96e6bd650c..0ce135dc63 100644 --- a/src/widgets/doc/snippets/graphicssceneadditemsnippet.cpp +++ b/src/widgets/doc/snippets/graphicssceneadditemsnippet.cpp @@ -49,6 +49,9 @@ ****************************************************************************/ #include <QtGui> +#include <QGraphicsScene> +#include <QGraphicsEllipseItem> +#include <QStyleOptionGraphicsItem> class CustomScene : public QGraphicsScene { @@ -70,21 +73,9 @@ void CustomScene::drawItems(QPainter *painter, int numItems, for (int i = 0; i < numItems; ++i) { // Draw the item painter->save(); - painter->setMatrix(items[i]->sceneMatrix(), true); + painter->setTransform(items[i]->sceneTransform(), true); items[i]->paint(painter, &options[i], widget); painter->restore(); } } //! [0] - -int main(int argv, char **args) -{ - QApplication app(argv, args); - - CustomScene scene; - QGraphicsView view(&scene); - - view.show(); - - return app.exec(); -} diff --git a/src/widgets/doc/snippets/graphicsview.cpp b/src/widgets/doc/snippets/graphicsview.cpp index 9578f91eec..6262137c90 100644 --- a/src/widgets/doc/snippets/graphicsview.cpp +++ b/src/widgets/doc/snippets/graphicsview.cpp @@ -47,53 +47,70 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - +#include <QGraphicsView> +#include <QOpenGLWidget> +#include <QPrinter> +#include <QPrintDialog> +#include <QStandardItem> +#include <QMimeData> +#include <QDrag> +#include <QGraphicsSceneMouseEvent> + +int main() +{ //! [0] QGraphicsScene scene; QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); -QGraphicsItem *item = scene.itemAt(50, 50); -// item == rect +QGraphicsItem *item = scene.itemAt(50, 50, QTransform()); //! [0] +Q_UNUSED(rect); +Q_UNUSED(item); +} +void myPopulateScene(QGraphicsScene *) +{ + // Intentionally left empty +} +void snippetThatUsesMyPopulateScene() +{ //! [1] QGraphicsScene scene; myPopulateScene(&scene); - QGraphicsView view(&scene); view.show(); //! [1] +} - -//! [2] -class View : public QGraphicsView +class CustomItem : public QStandardItem { -Q_OBJECT - ... -public slots: - void zoomIn() { scale(1.2, 1.2); } - void zoomOut() { scale(1 / 1.2, 1 / 1.2); } - void rotateLeft() { rotate(-10); } - void rotateRight() { rotate(10); } - ... +public: + using QStandardItem::QStandardItem; + + int type() const override { return UserType; } + void mousePressEvent(QGraphicsSceneMouseEvent *event); + QStandardItem *clone() const override { return new CustomItem; } }; -//! [2] +void printScene() +{ //! [3] QGraphicsScene scene; +QPrinter printer; scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green)); -QPrinter printer; if (QPrintDialog(&printer).exec() == QDialog::Accepted) { QPainter painter(&printer); painter.setRenderHint(QPainter::Antialiasing); scene.render(&painter); } //! [3] +} - +void pixmapScene() +{ //! [4] QGraphicsScene scene; scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green)); @@ -106,21 +123,21 @@ painter.end(); pixmap.save("scene.png"); //! [4] - +} //! [5] void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { QMimeData *data = new QMimeData; - data->setColor(Qt::green); - QDrag *drag = new QDrag(event->widget()); drag->setMimeData(data); - drag->start(); + drag->exec(); } //! [5] - +void viewScene() +{ +QGraphicsScene scene; //! [6] QGraphicsView view(&scene); QOpenGLWidget *gl = new QOpenGLWidget(); @@ -129,3 +146,4 @@ format.setSamples(4); gl->setFormat(format); view.setViewport(gl); //! [6] +} diff --git a/src/widgets/doc/snippets/graphicsview_snippet.cpp b/src/widgets/doc/snippets/graphicsview_snippet.cpp new file mode 100644 index 0000000000..9d058d0f6a --- /dev/null +++ b/src/widgets/doc/snippets/graphicsview_snippet.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [2] +class View : public QGraphicsView +{ +Q_OBJECT + ... +public slots: + void zoomIn() { scale(1.2, 1.2); } + void zoomOut() { scale(1 / 1.2, 1 / 1.2); } + void rotateLeft() { rotate(-10); } + void rotateRight() { rotate(10); } + ... +}; +//! [2] diff --git a/src/widgets/doc/snippets/mdiareasnippets.cpp b/src/widgets/doc/snippets/mdiareasnippets.cpp index dec7aaa1e7..c1e2d37ccb 100644 --- a/src/widgets/doc/snippets/mdiareasnippets.cpp +++ b/src/widgets/doc/snippets/mdiareasnippets.cpp @@ -85,6 +85,7 @@ void addingSubWindowsExample() mdiArea.show(); } +/* int main(int argv, char **args) { QApplication app(argv, args); @@ -103,5 +104,5 @@ int main(int argv, char **args) return app.exec(); } - +*/ diff --git a/src/widgets/doc/snippets/myscrollarea.cpp b/src/widgets/doc/snippets/myscrollarea.cpp index dbf8da1603..8afe4a6834 100644 --- a/src/widgets/doc/snippets/myscrollarea.cpp +++ b/src/widgets/doc/snippets/myscrollarea.cpp @@ -48,7 +48,7 @@ ** ****************************************************************************/ -#include <QtGui> +#include <QtWidgets> class MyScrollArea : public QAbstractScrollArea { @@ -97,8 +97,10 @@ void MyScrollArea::updateWidgetPosition() //! [0] } -void MyScrollArea::scrollContentsBy(int /*dx*/, int /*dy*/) +void MyScrollArea::scrollContentsBy(int dx, int dy) { + Q_UNUSED(dx); + Q_UNUSED(dy); updateWidgetPosition(); } @@ -118,21 +120,6 @@ void MyScrollArea::updateArea() void MyScrollArea::resizeEvent(QResizeEvent *event) { + Q_UNUSED(event); updateArea(); } - -int main(int argv, char **args) -{ - QApplication app(argv, args); - - QPixmap pixmap("mypixmap.png"); - QLabel label; - label.setPixmap(pixmap); - MyScrollArea area(&label); - area.resize(300, 300); - area.show(); - - area.setWidget(&label); - - return app.exec(); -} diff --git a/src/widgets/doc/snippets/snippets.pro b/src/widgets/doc/snippets/snippets.pro new file mode 100644 index 0000000000..f1d3596f9d --- /dev/null +++ b/src/widgets/doc/snippets/snippets.pro @@ -0,0 +1,16 @@ +requires(qtHaveModule(widgets)) +requires(qtHaveModule(printsupport)) + +TEMPLATE = app + +TARGET = widgets_snippets + +QT += widgets printsupport + +SOURCES += customviewstyle.cpp \ + filedialogurls.cpp \ + graphicssceneadditemsnippet.cpp \ + graphicsview.cpp \ + mdiareasnippets.cpp \ + myscrollarea.cpp + diff --git a/src/widgets/doc/src/graphicsview.qdoc b/src/widgets/doc/src/graphicsview.qdoc index 0489203e40..2dfad442a3 100644 --- a/src/widgets/doc/src/graphicsview.qdoc +++ b/src/widgets/doc/src/graphicsview.qdoc @@ -327,7 +327,7 @@ Here is an example of how to implement zoom and rotate slots in a subclass of QGraphicsView: - \snippet graphicsview.cpp 2 + \snippet graphicsview_snippet.cpp 2 The slots could be connected to \l{QToolButton}{QToolButtons} with \l{QAbstractButton::autoRepeat}{autoRepeat} enabled. diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 64cd87df38..fe5c52ee93 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -40,6 +40,7 @@ #include "qmenu.h" #include <QtWidgets/private/qtwidgetsglobal_p.h> +#include <QtWidgets/private/qwidgetwindow_p.h> #include "qactiongroup.h" #include "qdebug.h" @@ -2353,15 +2354,23 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po // Use d->popupScreen to remember, because initialScreenIndex will be reset after the first showing. // However if eventLoop exists, then exec() already did this by calling createWinId(); so leave it alone. (QTBUG-76162) if (!eventLoop) { + bool screenSet = false; const int screenIndex = topData()->initialScreenIndex; if (screenIndex >= 0) popupScreen = screenIndex; if (auto s = QGuiApplication::screens().value(popupScreen)) { if (setScreen(s)) itemsDirty = true; - } else if (setScreenForPoint(p)) { - itemsDirty = true; + screenSet = true; + } else if (QMenu *parentMenu = qobject_cast<QMenu *>(parent)) { + // a submenu is always opened from an open parent menu, + // so show it on the same screen where the parent is. (QTBUG-76162) + if (setScreen(QMenuPrivate::get(parentMenu)->windowHandle()->screen())) + itemsDirty = true; + screenSet = true; } + if (!screenSet && setScreenForPoint(p)) + itemsDirty = true; } const bool contextMenu = isContextMenu(); |