diff options
21 files changed, 339 insertions, 98 deletions
diff --git a/mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in b/mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in index 18683e01e3..fe9ddaf8f1 100644 --- a/mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in +++ b/mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in @@ -6,7 +6,8 @@ xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\" xmlns:uap3=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/3\" xmlns:mobile=\"http://schemas.microsoft.com/appx/manifest/mobile/windows10\" - IgnorableNamespaces=\"uap uap3 mp mobile\"> + xmlns:iot=\"http://schemas.microsoft.com/appx/manifest/iot/windows10\" + IgnorableNamespaces=\"uap uap3 mp mobile iot\"> <Identity Name=\"$${WINRT_MANIFEST.identity}\" diff --git a/mkspecs/features/winrt/package_manifest.prf b/mkspecs/features/winrt/package_manifest.prf index 969343cfd7..22bda003fb 100644 --- a/mkspecs/features/winrt/package_manifest.prf +++ b/mkspecs/features/winrt/package_manifest.prf @@ -123,6 +123,8 @@ UAP3_CAPABILITIES += backgroundMediaPlayback remoteSystem userNotificationListener + IOT_CAPABILITIES += systemManagement + # Capabilities are given as a string list and may change with the configuration (network, sensors, etc.) WINRT_MANIFEST.capabilities = $$unique(WINRT_MANIFEST.capabilities) WINRT_MANIFEST.capabilities_device = $$unique(WINRT_MANIFEST.capabilities_device) @@ -133,6 +135,8 @@ MANIFEST_CAPABILITIES += " <uap:Capability Name=\"$$CAPABILITY\" />" else:contains(UAP3_CAPABILITIES, $$CAPABILITY): \ MANIFEST_CAPABILITIES += " <uap3:Capability Name=\"$$CAPABILITY\" />" + else:contains(IOT_CAPABILITIES, $$CAPABILITY): \ + MANIFEST_CAPABILITIES += " <iot:Capability Name=\"$$CAPABILITY\" />" else: \ MANIFEST_CAPABILITIES += " <Capability Name=\"$$CAPABILITY\" />" } diff --git a/mkspecs/wasm-emscripten/qmake.conf b/mkspecs/wasm-emscripten/qmake.conf index f4e9501415..beb08d643c 100644 --- a/mkspecs/wasm-emscripten/qmake.conf +++ b/mkspecs/wasm-emscripten/qmake.conf @@ -36,6 +36,7 @@ EMCC_COMMON_LFLAGS += \ -s NO_EXIT_RUNTIME=0 \ -s ERROR_ON_UNDEFINED_SYMBOLS=1 \ -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"UTF16ToString\",\"stringToUTF16\"] \ + -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \ --bind # The -s arguments can also be used with release builds, diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h index ddbeeb90d2..f92a68454b 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h @@ -31,7 +31,6 @@ struct TranslatedAttribute; // The order of this enum governs priority of 'getLatestBufferStorage'. enum BufferUsage { - BUFFER_USAGE_SYSTEM_MEMORY, BUFFER_USAGE_STAGING, BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK, BUFFER_USAGE_INDEX, @@ -40,6 +39,7 @@ enum BufferUsage BUFFER_USAGE_PIXEL_UNPACK, BUFFER_USAGE_PIXEL_PACK, BUFFER_USAGE_UNIFORM, + BUFFER_USAGE_SYSTEM_MEMORY, BUFFER_USAGE_EMULATED_INDEXED_VERTEX, BUFFER_USAGE_COUNT, diff --git a/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch b/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch new file mode 100644 index 0000000000..e9cda1337f --- /dev/null +++ b/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch @@ -0,0 +1,37 @@ +From b215999d63d6e6b087e53e24a47b8b60520ec9e4 Mon Sep 17 00:00:00 2001 +From: Oliver Wolff <oliver.wolff@qt.io> +Date: Wed, 11 Mar 2020 13:59:39 +0100 +Subject: [PATCH] ANGLE: Fix severe performance regression + +The changed buffer usage priority that was introduced in our ANGLE +update caused severe performance regressions for Qt applications. + +Fixes: QTBUG-73835 +Change-Id: I49839bb272cdeec0027264f2751b88bc149665ad +--- + src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h +index ddbeeb90d2..f92a68454b 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h +@@ -31,7 +31,6 @@ struct TranslatedAttribute; + // The order of this enum governs priority of 'getLatestBufferStorage'. + enum BufferUsage + { +- BUFFER_USAGE_SYSTEM_MEMORY, + BUFFER_USAGE_STAGING, + BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK, + BUFFER_USAGE_INDEX, +@@ -40,6 +39,7 @@ enum BufferUsage + BUFFER_USAGE_PIXEL_UNPACK, + BUFFER_USAGE_PIXEL_PACK, + BUFFER_USAGE_UNIFORM, ++ BUFFER_USAGE_SYSTEM_MEMORY, + BUFFER_USAGE_EMULATED_INDEXED_VERTEX, + + BUFFER_USAGE_COUNT, +-- +2.20.1.windows.1 + diff --git a/src/corelib/doc/src/containers.qdoc b/src/corelib/doc/src/containers.qdoc index d0bb251e81..4c7cb32aac 100644 --- a/src/corelib/doc/src/containers.qdoc +++ b/src/corelib/doc/src/containers.qdoc @@ -123,6 +123,10 @@ a vector can be quite slow, because it can lead to large numbers of items having to be moved by one position in memory. + \row \li \l{QVarLengthArray}<T, Prealloc> + \li This provides a low-level variable-length array. It can be used + instead of QVector in places where speed is particularly important. + \row \li \l{QStack}<T> \li This is a convenience subclass of QVector that provides "last in, first out" (LIFO) semantics. It adds the following @@ -622,15 +626,11 @@ \section1 Other Container-Like Classes - Qt includes three template classes that resemble containers in + Qt includes other template classes that resemble containers in some respects. These classes don't provide iterators and cannot be used with the \c foreach keyword. \list - \li QVarLengthArray<T, Prealloc> provides a low-level - variable-length array. It can be used instead of QVector in - places where speed is particularly important. - \li QCache<Key, T> provides a cache to store objects of a certain type T associated with keys of type Key. diff --git a/src/corelib/time/qtimezoneprivate_android.cpp b/src/corelib/time/qtimezoneprivate_android.cpp index 5cb8155dcc..fc3653752a 100644 --- a/src/corelib/time/qtimezoneprivate_android.cpp +++ b/src/corelib/time/qtimezoneprivate_android.cpp @@ -102,7 +102,7 @@ void QAndroidTimeZonePrivate::init(const QByteArray &ianaId) for (int style = 1; m_id.isEmpty() && style-- > 0;) { for (int dst = 1; m_id.isEmpty() && dst-- > 0;) { m_id = match(androidTimeZone.callObjectMethod( - "getDisplayName", "(ZI;)Ljava/lang/String;", bool(dst), style)); + "getDisplayName", "(ZI)Ljava/lang/String;", bool(dst), style)); } } } diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 27c82bc09f..2f907a7709 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -797,8 +797,12 @@ QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State if (svgIcon.isNull()) svgIcon = QIcon(filename); - // Simply reuse svg icon engine - return svgIcon.pixmap(size, mode, state); + // Bypass QIcon API, as that will scale by device pixel ratio of the + // highest DPR screen since we're not passing on any QWindow. + if (QIconEngine *engine = svgIcon.data_ptr() ? svgIcon.data_ptr()->engine : nullptr) + return engine->pixmap(size, mode, state); + + return QPixmap(); } QPixmap QIconLoaderEngine::pixmap(const QSize &size, QIcon::Mode mode, diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index a3e194f835..aaca1061b7 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -1667,7 +1667,8 @@ namespace { QFontEngine *previousGlyphFontEngine; QFixed minw; - QFixed softHyphenWidth; + QFixed currentSoftHyphenWidth; + QFixed commitedSoftHyphenWidth; QFixed rightBearing; QFixed minimumRightBearing; @@ -1681,7 +1682,7 @@ namespace { QFixed calculateNewWidth(const QScriptLine &line) const { return line.textWidth + tmpData.textWidth + spaceData.textWidth - + softHyphenWidth + negativeRightBearing(); + + (line.textWidth > 0 ? currentSoftHyphenWidth : QFixed()) + negativeRightBearing(); } inline glyph_t currentGlyph() const @@ -1755,6 +1756,7 @@ inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line) if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs)) return true; + const QFixed oldTextWidth = line.textWidth; minw = qMax(minw, tmpData.textWidth); line += tmpData; line.textWidth += spaceData.textWidth; @@ -1765,6 +1767,11 @@ inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line) spaceData.textWidth = 0; spaceData.length = 0; + if (oldTextWidth != line.textWidth || currentSoftHyphenWidth > 0) { + commitedSoftHyphenWidth = currentSoftHyphenWidth; + currentSoftHyphenWidth = 0; + } + return false; } @@ -1837,7 +1844,6 @@ void QTextLine::layout_helper(int maxGlyphs) while (newItem < eng->layoutData->items.size()) { lbh.resetRightBearing(); - lbh.softHyphenWidth = 0; if (newItem != item) { item = newItem; const QScriptItem ¤t = eng->layoutData->items.at(item); @@ -1975,9 +1981,9 @@ void QTextLine::layout_helper(int maxGlyphs) } while (lbh.currentPosition < end); lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw); - if (lbh.currentPosition > 0 && lbh.currentPosition < end - && attributes[lbh.currentPosition].lineBreak - && eng->layoutData->string.at(lbh.currentPosition - 1).unicode() == QChar::SoftHyphen) { + if (lbh.currentPosition > 0 && lbh.currentPosition <= end + && (lbh.currentPosition == end || attributes[lbh.currentPosition].lineBreak) + && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) { // if we are splitting up a word because of // a soft hyphen then we ... // @@ -1994,10 +2000,7 @@ void QTextLine::layout_helper(int maxGlyphs) // want the soft-hyphen to slip into the next line // and thus become invisible again. // - if (line.length) - lbh.softHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]]; - else if (breakany) - lbh.tmpData.textWidth += lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]]; + lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]]; } if (sb_or_ws|breakany) { @@ -2023,6 +2026,7 @@ void QTextLine::layout_helper(int maxGlyphs) lbh.calculateRightBearing(); if (lbh.checkFullOtherwiseExtend(line)) { + // We are too wide to accept the next glyph with its bearing, so we restore the // right bearing to that of the previous glyph (the one that was already accepted), // so that the bearing can be be applied to the final width of the text below. @@ -2031,9 +2035,7 @@ void QTextLine::layout_helper(int maxGlyphs) else lbh.calculateRightBearingForPreviousGlyph(); - if (!breakany) { - line.textWidth += lbh.softHyphenWidth; - } + line.textWidth += lbh.commitedSoftHyphenWidth; goto found; } @@ -2045,6 +2047,7 @@ void QTextLine::layout_helper(int maxGlyphs) } LB_DEBUG("reached end of line"); lbh.checkFullOtherwiseExtend(line); + line.textWidth += lbh.commitedSoftHyphenWidth; found: line.textAdvance = line.textWidth; diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index d31352b854..293faf8a53 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -978,6 +978,13 @@ QByteArray QWindowsContext::comErrorString(HRESULT hr) return result; } +void QWindowsContext::forceNcCalcSize(HWND hwnd) +{ + // Force WM_NCCALCSIZE to adjust margin + SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); +} + bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi) { diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 8027f09389..07398bd61c 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -246,6 +246,8 @@ public: bool asyncExpose() const; void setAsyncExpose(bool value); + static void forceNcCalcSize(HWND hwnd); + static bool systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi = 0); static bool systemParametersInfoForScreen(unsigned action, unsigned param, void *out, const QPlatformScreen *screen = nullptr); diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp index d20edd685e..221e4ff6ec 100644 --- a/src/plugins/platforms/windows/qwindowsmenu.cpp +++ b/src/plugins/platforms/windows/qwindowsmenu.cpp @@ -794,20 +794,13 @@ QWindowsMenuBar *QWindowsMenuBar::menuBarOf(const QWindow *notYetCreatedWindow) ? qobject_cast<QWindowsMenuBar *>(menuBarV.value<QObject *>()) : nullptr; } -static inline void forceNcCalcSize(HWND hwnd) -{ - // Force WM_NCCALCSIZE to adjust margin: Does not appear to work? - SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, - SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); -} - void QWindowsMenuBar::install(QWindowsWindow *window) { const HWND hwnd = window->handle(); const BOOL result = SetMenu(hwnd, m_hMenuBar); if (result) { window->setMenuBar(this); - forceNcCalcSize(hwnd); + QWindowsContext::forceNcCalcSize(hwnd); } } @@ -817,7 +810,7 @@ void QWindowsMenuBar::removeFromWindow() const HWND hwnd = window->handle(); SetMenu(hwnd, nullptr); window->setMenuBar(nullptr); - forceNcCalcSize(hwnd); + QWindowsContext::forceNcCalcSize(hwnd); } } diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index a11da598fc..04478d5f1f 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -2431,7 +2431,17 @@ void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins) void QWindowsWindow::updateFullFrameMargins() { - // Normally obtained from WM_NCCALCSIZE + // QTBUG-82580: If a native menu is present, force a WM_NCCALCSIZE. + if (GetMenu(m_data.hwnd)) + QWindowsContext::forceNcCalcSize(m_data.hwnd); + else + calculateFullFrameMargins(); +} + +void QWindowsWindow::calculateFullFrameMargins() +{ + // Normally obtained from WM_NCCALCSIZE. This calculation only works + // when no native menu is present. const auto systemMargins = testFlag(DisableNonClientScaling) ? QWindowsGeometryHint::frameOnPrimaryScreen(m_data.hwnd) : frameMargins_sys(); diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 1f8800272b..aaf02d2a81 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -370,6 +370,7 @@ private: void handleWindowStateChange(Qt::WindowStates state); inline void destroyIcon(); void fireExpose(const QRegion ®ion, bool force=false); + void calculateFullFrameMargins(); mutable QWindowsWindowData m_data; QPointer<QWindowsMenuBar> m_menuBar; diff --git a/src/plugins/platforms/winrt/qwinrtservices.cpp b/src/plugins/platforms/winrt/qwinrtservices.cpp index b27c408f40..04d7417801 100644 --- a/src/plugins/platforms/winrt/qwinrtservices.cpp +++ b/src/plugins/platforms/winrt/qwinrtservices.cpp @@ -43,6 +43,7 @@ #include <QtCore/QDir> #include <QtCore/QCoreApplication> #include <QtCore/qfunctions_winrt.h> +#include <private/qeventdispatcher_winrt_p.h> #include <wrl.h> #include <windows.foundation.h> @@ -94,13 +95,17 @@ bool QWinRTServices::openUrl(const QUrl &url) HRESULT hr = d->uriFactory->CreateUri(uriString.Get(), &uri); RETURN_FALSE_IF_FAILED("Failed to create URI from QUrl."); - ComPtr<IAsyncOperation<bool>> op; - hr = d->launcher->LaunchUriAsync(uri.Get(), &op); - RETURN_FALSE_IF_FAILED("Failed to start URI launch."); - boolean result; - hr = QWinRTFunctions::await(op, &result); - RETURN_FALSE_IF_FAILED("Failed to launch URI."); + hr = QEventDispatcherWinRT::runOnXamlThread([this, d, uri, &result]() { + ComPtr<IAsyncOperation<bool>> op; + HRESULT hr = d->launcher->LaunchUriAsync(uri.Get(), &op); + RETURN_HR_IF_FAILED("Failed to start URI launch."); + + hr = QWinRTFunctions::await(op, &result); + RETURN_HR_IF_FAILED("Failed to launch URI."); + return hr; + }); + RETURN_FALSE_IF_FAILED("Failed to launch URI from Xaml thread."); return result; } @@ -131,12 +136,16 @@ bool QWinRTServices::openDocument(const QUrl &url) boolean result; { - ComPtr<IAsyncOperation<bool>> op; - hr = d->launcher->LaunchFileAsync(file.Get(), &op); - RETURN_FALSE_IF_FAILED("Failed to start file launch."); - - hr = QWinRTFunctions::await(op, &result); - RETURN_FALSE_IF_FAILED("Failed to launch file."); + hr = QEventDispatcherWinRT::runOnXamlThread([this, d, file, &result]() { + ComPtr<IAsyncOperation<bool>> op; + HRESULT hr = d->launcher->LaunchFileAsync(file.Get(), &op); + RETURN_HR_IF_FAILED("Failed to start file launch."); + + hr = QWinRTFunctions::await(op, &result); + RETURN_HR_IF_FAILED("Failed to launch file."); + return hr; + }); + RETURN_FALSE_IF_FAILED("Failed to launch file from Xaml thread."); } return result; diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 442369c2ec..a9e6e90623 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -3755,7 +3755,8 @@ int QTreeViewPrivate::itemDecorationAt(const QPoint &pos) const bool spanned = false; if (!spanningIndexes.isEmpty()) { const QModelIndex index = q->indexAt(pos); - spanned = q->isFirstColumnSpanned(index.row(), index.parent()); + if (index.isValid()) + spanned = q->isFirstColumnSpanned(index.row(), index.parent()); } const int column = spanned ? 0 : header->logicalIndexAt(pos.x()); if (!isTreePosition(column)) diff --git a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp index 2dcca0209e..8466305832 100644 --- a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp +++ b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp @@ -62,6 +62,7 @@ private slots: void lineBreaking(); #ifdef QT_BUILD_INTERNAL void simpleBoundingRect(); + void threeLineBoundingRect_data(); void threeLineBoundingRect(); void boundingRectWithLongLineAndNoWrap(); void forcedBreaks(); @@ -140,6 +141,7 @@ private slots: void showLineAndParagraphSeparatorsCrash(); void koreanWordWrap(); void tooManyDirectionalCharctersCrash_qtbug77819(); + void softHyphens(); private: QFont testFont; @@ -315,18 +317,49 @@ void tst_QTextLayout::simpleBoundingRect() QCOMPARE(layout.boundingRect(), QRectF(0, 0, width, QFontMetrics(testFont).height())); } +void tst_QTextLayout::threeLineBoundingRect_data() +{ + QTest::addColumn<QChar>("wordBoundary1"); + QTest::addColumn<QChar>("wordBoundary2"); + QTest::newRow("2x' '") << QChar(' ') << QChar(' '); + QTest::newRow("2x'\\n'") << QChar('\n') << QChar('\n'); + QTest::newRow("' ' + '\\n'") << QChar(' ') << QChar('\n'); + QTest::newRow("'\\n' + ' '") << QChar('\n') << QChar(' '); + QTest::newRow("2x'\\t'") << QChar('\t') << QChar('\t'); + QTest::newRow("2xsoft hyphen") << QChar(0xad) << QChar(0xad); + QTest::newRow("2x'-'") << QChar('-') << QChar('-'); + QTest::newRow("2x'/'") << QChar('/') << QChar('/'); + QTest::newRow("soft hyphen + ' '") << QChar(0xad) << QChar(' '); + QTest::newRow("soft hyphen + '\\n'") << QChar(0xad) << QChar('\n'); + QTest::newRow("soft hyphen + '-'") << QChar(0xad) << QChar('-'); + QTest::newRow("' ' + soft hyphen") << QChar(' ') << QChar(0xad); + QTest::newRow("'\\n' + soft hyphen") << QChar('\n') << QChar(0xad); + QTest::newRow("'-' + soft hyphen") << QChar('-') << QChar(0xad); +} + void tst_QTextLayout::threeLineBoundingRect() { /* stricter check. break text into three lines */ + QFETCH(QChar, wordBoundary1); + QFETCH(QChar, wordBoundary2); QString firstWord("hello"); - QString secondWord("world"); - QString thirdWord("test"); - QString text(firstWord + ' ' + secondWord + ' ' + thirdWord); - - const int firstLineWidth = firstWord.length() * testFont.pixelSize(); - const int secondLineWidth = secondWord.length() * testFont.pixelSize(); - const int thirdLineWidth = thirdWord.length() * testFont.pixelSize(); + QString secondWord("test"); + QString thirdWord("world"); + QString text(firstWord + wordBoundary1 + secondWord + wordBoundary2 + thirdWord); + + int firstLineWidth = firstWord.length() * testFont.pixelSize(); + int secondLineWidth = secondWord.length() * testFont.pixelSize(); + int thirdLineWidth = thirdWord.length() * testFont.pixelSize(); + // Trailing spaces do not count to line width: + if (!wordBoundary1.isSpace()) + firstLineWidth += testFont.pixelSize(); + if (!wordBoundary2.isSpace()) + secondLineWidth += testFont.pixelSize(); + // But trailing spaces do count to line length: + const int firstLineLength = firstWord.length() + 1; + const int secondLineLength = secondWord.length() + 1; + const int thirdLineLength = thirdWord.length(); const int longestLine = qMax(firstLineWidth, qMax(secondLineWidth, thirdLineWidth)); @@ -339,8 +372,7 @@ void tst_QTextLayout::threeLineBoundingRect() line.setLineWidth(firstLineWidth); line.setPosition(QPoint(0, y)); QCOMPARE(line.textStart(), pos); - // + 1 for trailing space - QCOMPARE(line.textLength(), firstWord.length() + 1); + QCOMPARE(line.textLength(), firstLineLength); QCOMPARE(qRound(line.naturalTextWidth()), firstLineWidth); pos += line.textLength(); @@ -349,9 +381,8 @@ void tst_QTextLayout::threeLineBoundingRect() line = layout.createLine(); line.setLineWidth(secondLineWidth); line.setPosition(QPoint(0, y)); - // + 1 for trailing space QCOMPARE(line.textStart(), pos); - QCOMPARE(line.textLength(), secondWord.length() + 1); + QCOMPARE(line.textLength(), secondLineLength); QCOMPARE(qRound(line.naturalTextWidth()), secondLineWidth); pos += line.textLength(); @@ -360,9 +391,8 @@ void tst_QTextLayout::threeLineBoundingRect() line = layout.createLine(); line.setLineWidth(secondLineWidth); line.setPosition(QPoint(0, y)); - // no trailing space here! QCOMPARE(line.textStart(), pos); - QCOMPARE(line.textLength(), thirdWord.length()); + QCOMPARE(line.textLength(), thirdLineLength); QCOMPARE(qRound(line.naturalTextWidth()), thirdLineWidth); y += qRound(line.ascent() + line.descent()); @@ -2352,5 +2382,111 @@ void tst_QTextLayout::tooManyDirectionalCharctersCrash_qtbug77819() tl.endLayout(); } +void tst_QTextLayout::softHyphens() +{ + QString text = QStringLiteral("xxxx\u00ad") + QStringLiteral("xxxx\u00ad"); + + QFont font; + font.setPixelSize(14); + font.setHintingPreference(QFont::PreferNoHinting); + const float xAdvance = QFontMetricsF(font).horizontalAdvance(QChar('x')); + const float shyAdvance = QFontMetricsF(font).horizontalAdvance(QChar::SoftHyphen); + if (xAdvance < (shyAdvance + 1.0f)) + QSKIP("Default font not suitable for this test."); + QTextLayout layout(text, font); + QTextOption option; + option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + layout.setTextOption(option); + + // Loose fit + // xxxx- | + // xxxx- | + { + int pos = 0; + int y = 0; + layout.beginLayout(); + QTextLine line = layout.createLine(); + line.setLineWidth(qCeil(5 * xAdvance) + 1); + line.setPosition(QPoint(0, y)); + QCOMPARE(line.textStart(), pos); + QCOMPARE(line.textLength(), 5); + QVERIFY(qAbs(line.naturalTextWidth() - (4 * xAdvance + shyAdvance)) <= 1); + + pos += line.textLength(); + y += qRound(line.ascent() + line.descent()); + + line = layout.createLine(); + line.setLineWidth(qCeil(5 * xAdvance) + 1); + line.setPosition(QPoint(0, y)); + QCOMPARE(line.textStart(), pos); + QCOMPARE(line.textLength(), 5); + QVERIFY(qAbs(line.naturalTextWidth() - (4 * xAdvance + shyAdvance)) <= 1); + layout.endLayout(); + } + + // Tight fit + // xxxx-| + // xxxx-| + { + int pos = 0; + int y = 0; + layout.beginLayout(); + QTextLine line = layout.createLine(); + line.setLineWidth(qCeil(4 * xAdvance + shyAdvance) + 1); + line.setPosition(QPoint(0, y)); + QCOMPARE(line.textStart(), pos); + QCOMPARE(line.textLength(), 5); + QVERIFY(qAbs(line.naturalTextWidth() - (4 * xAdvance + shyAdvance)) <= 1); + + pos += line.textLength(); + y += qRound(line.ascent() + line.descent()); + + line = layout.createLine(); + line.setLineWidth(qCeil(4 * xAdvance + shyAdvance) + 1); + line.setPosition(QPoint(0, y)); + QCOMPARE(line.textStart(), pos); + QCOMPARE(line.textLength(), 5); + QVERIFY(qAbs(line.naturalTextWidth() - (4 * xAdvance + shyAdvance)) <= 1); + layout.endLayout(); + } + + // Very tight fit + // xxxx| + // xxxx| + // - | + { + int pos = 0; + int y = 0; + layout.beginLayout(); + QTextLine line = layout.createLine(); + line.setLineWidth(qCeil(4 * xAdvance) + 2); + line.setPosition(QPoint(0, y)); + QCOMPARE(line.textStart(), pos); + QCOMPARE(line.textLength(), 4); + QVERIFY(qAbs(line.naturalTextWidth() - 4 * xAdvance) <= 1); + + pos += line.textLength(); + y += qRound(line.ascent() + line.descent()); + + line = layout.createLine(); + line.setLineWidth(qCeil(4 * xAdvance) + 2); + line.setPosition(QPoint(0, y)); + QCOMPARE(line.textStart(), pos); + QCOMPARE(line.textLength(), 5); + QVERIFY(qAbs(line.naturalTextWidth() - 4 * xAdvance) <= 1); + + pos += line.textLength(); + y += qRound(line.ascent() + line.descent()); + + line = layout.createLine(); + line.setLineWidth(qCeil(4 * xAdvance) + 2); + line.setPosition(QPoint(0, y)); + QCOMPARE(line.textStart(), pos); + QCOMPARE(line.textLength(), 1); + QVERIFY(qAbs(line.naturalTextWidth() - shyAdvance) <= 1); + layout.endLayout(); + } +} + QTEST_MAIN(tst_QTextLayout) #include "tst_qtextlayout.moc" diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp index 0f419e9de4..bed8a8b129 100644 --- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp @@ -1090,12 +1090,21 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest() QProcess serverProcess; serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"), QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(serverProcess.waitForStarted(3000), - qPrintable("Failed to start subprocess: " + serverProcess.errorString())); + + const auto serverProcessCleaner = qScopeGuard([&serverProcess] { + serverProcess.kill(); + serverProcess.waitForFinished(); + }); + + if (!serverProcess.waitForStarted(3000)) + QSKIP("Failed to start server as a subprocess"); // Wait until the server has started and reports success. - while (!serverProcess.canReadLine()) - QVERIFY(serverProcess.waitForReadyRead(3000)); + while (!serverProcess.canReadLine()) { + if (!serverProcess.waitForReadyRead(3000)) + QSKIP("No output from the server process, bailing out"); + } + QByteArray serverGreeting = serverProcess.readLine(); QVERIFY(serverGreeting != QByteArray("XXX\n")); int serverPort = serverGreeting.trimmed().toInt(); @@ -1105,12 +1114,21 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest() clientProcess.start(QString::fromLatin1("clientserver/clientserver connectedclient %1 %2") .arg(QLatin1String("127.0.0.1")).arg(serverPort), QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(clientProcess.waitForStarted(3000), - qPrintable("Failed to start subprocess: " + clientProcess.errorString())); - // Wait until the server has started and reports success. - while (!clientProcess.canReadLine()) - QVERIFY(clientProcess.waitForReadyRead(3000)); + const auto clientProcessCleaner = qScopeGuard([&clientProcess] { + clientProcess.kill(); + clientProcess.waitForFinished(); + }); + + if (!clientProcess.waitForStarted(3000)) + QSKIP("Client process did not start"); + + // Wait until the client has started and reports success. + while (!clientProcess.canReadLine()) { + if (!clientProcess.waitForReadyRead(3000)) + QSKIP("No output from the client process, bailing out"); + } + QByteArray clientGreeting = clientProcess.readLine(); QCOMPARE(clientGreeting, QByteArray("ok\n")); @@ -1135,11 +1153,6 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest() QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(), sdata.mid(4).trimmed().toInt() * 2); } - - clientProcess.kill(); - QVERIFY(clientProcess.waitForFinished()); - serverProcess.kill(); - QVERIFY(serverProcess.waitForFinished()); #endif } @@ -1151,12 +1164,21 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() QProcess serverProcess; serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"), QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(serverProcess.waitForStarted(3000), - qPrintable("Failed to start subprocess: " + serverProcess.errorString())); + + const auto serverProcessCleaner = qScopeGuard([&serverProcess] { + serverProcess.kill(); + serverProcess.waitForFinished(); + }); + + if (!serverProcess.waitForStarted(3000)) + QSKIP("Failed to start the server subprocess"); // Wait until the server has started and reports success. - while (!serverProcess.canReadLine()) - QVERIFY(serverProcess.waitForReadyRead(3000)); + while (!serverProcess.canReadLine()) { + if (!serverProcess.waitForReadyRead(3000)) + QSKIP("No output from the server, probably, it is not running"); + } + QByteArray serverGreeting = serverProcess.readLine(); QVERIFY(serverGreeting != QByteArray("XXX\n")); int serverPort = serverGreeting.trimmed().toInt(); @@ -1166,12 +1188,21 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() clientProcess.start(QString::fromLatin1("clientserver/clientserver unconnectedclient %1 %2") .arg(QLatin1String("127.0.0.1")).arg(serverPort), QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(clientProcess.waitForStarted(3000), - qPrintable("Failed to start subprocess: " + clientProcess.errorString())); - // Wait until the server has started and reports success. - while (!clientProcess.canReadLine()) - QVERIFY(clientProcess.waitForReadyRead(3000)); + const auto clientProcessCleaner = qScopeGuard([&clientProcess] { + clientProcess.kill(); + clientProcess.waitForFinished(); + }); + + if (!clientProcess.waitForStarted(3000)) + QSKIP("Failed to start the client's subprocess"); + + // Wait until the client has started and reports success. + while (!clientProcess.canReadLine()) { + if (!clientProcess.waitForReadyRead(3000)) + QSKIP("The client subprocess produced not output, exiting."); + } + QByteArray clientGreeting = clientProcess.readLine(); QCOMPARE(clientGreeting, QByteArray("ok\n")); @@ -1197,11 +1228,6 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(), sdata.mid(4).trimmed().toInt() * 2); } - - clientProcess.kill(); - QVERIFY(clientProcess.waitForFinished()); - serverProcess.kill(); - QVERIFY(serverProcess.waitForFinished()); #endif } diff --git a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp index 76314564f1..543128915e 100644 --- a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp +++ b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp @@ -51,7 +51,7 @@ private slots: void about(); void detailsText(); void detailsButtonText(); - void expandDetails_QTBUG_32473(); + void expandDetailsWithoutMoving(); #ifndef Q_OS_MAC void shortcut(); @@ -499,7 +499,7 @@ void tst_QMessageBox::detailsButtonText() } } -void tst_QMessageBox::expandDetails_QTBUG_32473() +void tst_QMessageBox::expandDetailsWithoutMoving() // QTBUG-32473 { tst_ResizingMessageBox box; box.setDetailedText("bla"); @@ -516,18 +516,14 @@ void tst_QMessageBox::expandDetails_QTBUG_32473() auto moreButton = *it; QVERIFY(QTest::qWaitForWindowExposed(&box)); + QTRY_VERIFY2(!box.geometry().topLeft().isNull(), "window manager is expected to decorate and position the dialog"); QRect geom = box.geometry(); box.resized = false; + // now click the "more" button, and verify that the dialog resizes but does not move moreButton->click(); QTRY_VERIFY(box.resized); - // After we receive the expose event for a second widget, it's likely - // that the window manager is also done manipulating the first QMessageBox. - QWidget fleece; - fleece.show(); - QVERIFY(QTest::qWaitForWindowExposed(&fleece)); - if (geom.topLeft() == box.geometry().topLeft()) - QTest::qWait(500); - QCOMPARE(geom.topLeft(), box.geometry().topLeft()); + QVERIFY(box.geometry().height() > geom.height()); + QCOMPARE(box.geometry().topLeft(), geom.topLeft()); } void tst_QMessageBox::incorrectDefaultButton() diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index 0580c466cf..b26516ee6b 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -270,6 +270,12 @@ public: QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { + if (onlyValidCalls) { + Q_ASSERT(row >= 0); + Q_ASSERT(column >= 0); + Q_ASSERT(row < rows); + Q_ASSERT(column < cols); + } if (row < 0 || column < 0 || (level(parent) > levels) || column >= cols || row >= rows) { return QModelIndex(); } @@ -378,6 +384,7 @@ public: mutable bool fetched = false; bool decorationsEnabled = false; bool statusTipsEnabled = false; + bool onlyValidCalls = false; }; // Testing get/set functions @@ -2420,6 +2427,7 @@ void tst_QTreeView::hiddenItems() void tst_QTreeView::spanningItems() { QtTestModel model(10, 10); + model.onlyValidCalls = true; QTreeView view; view.setModel(&model); view.show(); @@ -2459,6 +2467,8 @@ void tst_QTreeView::spanningItems() } } QCOMPARE(view.sizeHintForColumn(0), w); + + view.repaint(); // to check that this doesn't hit any assert } void tst_QTreeView::selectionOrderTest() diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index 727d1c2a16..1fc1c65be0 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -1264,7 +1264,7 @@ void tst_QMenu::QTBUG47515_widgetActionEnterLeave() if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) QSKIP("Window activation is not supported"); if (QGuiApplication::platformName() == QLatin1String("cocoa")) - QSKIP("See QTBUG-63031"); + QSKIP("This test is meaningless on macOS, for additional info see QTBUG-63031"); const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); QRect geometry(QPoint(), availableGeometry.size() / 3); |