aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-10-05 03:03:27 +0200
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-10-05 03:03:27 +0200
commita4ecc76df944cce5152cdfc8fd27436390a4fa62 (patch)
treec840006f08a790bf4c9ce362e98d826009f520fc
parent472750be915b7cab88c5daaca1246f58c6bd0f72 (diff)
parenta392194a575653dff3cd21227e6a1a2902b8399f (diff)
Merge remote-tracking branch 'origin/5.14' into 5.15
-rw-r--r--src/quicktemplates2/qquickabstractbutton.cpp4
-rw-r--r--src/quicktemplates2/qquickcombobox.cpp119
-rw-r--r--src/quicktemplates2/qquickcombobox_p.h4
-rw-r--r--src/quicktemplates2/qquickpopup_p_p.h2
-rw-r--r--src/quicktemplates2/qquicktooltip.cpp3
-rw-r--r--src/quicktemplates2/qquicktumbler.cpp17
-rw-r--r--tests/auto/controls/data/tst_tooltip.qml9
-rw-r--r--tests/auto/controls/data/tst_tumbler.qml27
-rw-r--r--tests/auto/qquickpopup/data/toolTipCrashOnClose.qml94
-rw-r--r--tests/auto/qquickpopup/tst_qquickpopup.cpp42
10 files changed, 287 insertions, 34 deletions
diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp
index 983f2651..2099f2db 100644
--- a/src/quicktemplates2/qquickabstractbutton.cpp
+++ b/src/quicktemplates2/qquickabstractbutton.cpp
@@ -105,6 +105,7 @@ QT_BEGIN_NAMESPACE
\qmlsignal QtQuick.Controls::AbstractButton::pressAndHold()
This signal is emitted when the button is interactively pressed and held down by the user via touch or mouse.
+ It is not emitted when \l autoRepeat is enabled.
*/
/*!
@@ -658,6 +659,9 @@ void QQuickAbstractButton::setAutoExclusive(bool exclusive)
This property holds whether the button repeats \l pressed(), \l released()
and \l clicked() signals while the button is pressed and held down.
+ If this property is set to \c true, the \l pressAndHold() signal will not
+ be emitted.
+
The default value is \c false.
The initial delay and the repetition interval are defined in milliseconds
diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp
index 60526a0a..21eecfe1 100644
--- a/src/quicktemplates2/qquickcombobox.cpp
+++ b/src/quicktemplates2/qquickcombobox.cpp
@@ -43,6 +43,7 @@
#include <QtCore/qregularexpression.h>
#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qglobal.h>
#include <QtGui/qinputmethod.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpa/qplatformtheme.h>
@@ -231,6 +232,13 @@ public:
void updateEditText();
void updateCurrentText();
void updateCurrentValue();
+ void updateCurrentText(bool hasDelegateModelObject);
+ void updateCurrentValue(bool hasDelegateModelObject);
+ void updateCurrentTextAndValue();
+
+ bool isValidIndex(int index) const;
+ QString fastTextAt(int index) const;
+ QVariant fastValueAt(int index) const;
void acceptInput();
QString tryComplete(const QString &inputText);
@@ -433,10 +441,34 @@ void QQuickComboBoxPrivate::updateEditText()
q->setEditText(text);
}
+// We have these two rather than just using default arguments
+// because QObjectPrivate::connect() doesn't accept lambdas.
void QQuickComboBoxPrivate::updateCurrentText()
{
+ updateCurrentText(false);
+}
+
+void QQuickComboBoxPrivate::updateCurrentValue()
+{
+ updateCurrentValue(false);
+}
+
+void QQuickComboBoxPrivate::updateCurrentText(bool hasDelegateModelObject)
+{
Q_Q(QQuickComboBox);
- QString text = q->textAt(currentIndex);
+ QString text;
+ // If a delegate model object was passed in, it means the calling code
+ // has decided to reuse it for several function calls to speed things up.
+ // So, use the faster (private) version in that case.
+ // For other cases, we use the version that creates the delegate model object
+ // itself in order to have neater, more convenient calling code.
+ if (isValidIndex(currentIndex)) {
+ if (hasDelegateModelObject)
+ text = fastTextAt(currentIndex);
+ else
+ text = q->textAt(currentIndex);
+ }
+
if (currentText != text) {
currentText = text;
if (!hasDisplayText)
@@ -451,10 +483,19 @@ void QQuickComboBoxPrivate::updateCurrentText()
q->setEditText(currentText);
}
-void QQuickComboBoxPrivate::updateCurrentValue()
+void QQuickComboBoxPrivate::updateCurrentValue(bool hasDelegateModelObject)
{
Q_Q(QQuickComboBox);
- const QVariant value = q->valueAt(currentIndex);
+ QVariant value;
+ // If a delegate model object was passed in, it means the calling code
+ // has decided to reuse it for several function calls to speed things up.
+ // So, use the faster (private) version in that case.
+ if (isValidIndex(currentIndex)) {
+ if (hasDelegateModelObject)
+ value = fastValueAt(currentIndex);
+ else
+ value = q->valueAt(currentIndex);
+ }
if (currentValue == value)
return;
@@ -462,6 +503,38 @@ void QQuickComboBoxPrivate::updateCurrentValue()
emit q->currentValueChanged();
}
+void QQuickComboBoxPrivate::updateCurrentTextAndValue()
+{
+ QObject *object = nullptr;
+ // For performance reasons, we reuse the same delegate model object: QTBUG-76029.
+ if (isValidIndex(currentIndex))
+ object = delegateModel->object(currentIndex);
+ const bool hasDelegateModelObject = object != nullptr;
+ updateCurrentText(hasDelegateModelObject);
+ updateCurrentValue(hasDelegateModelObject);
+ if (object)
+ delegateModel->release(object);
+}
+
+bool QQuickComboBoxPrivate::isValidIndex(int index) const
+{
+ return delegateModel && index >= 0 && index < delegateModel->count();
+}
+
+// For performance reasons (QTBUG-76029), both this and valueAt assume that
+// the index is valid and delegateModel->object(index) has been called.
+QString QQuickComboBoxPrivate::fastTextAt(int index) const
+{
+ const QString effectiveTextRole = textRole.isEmpty() ? QStringLiteral("modelData") : textRole;
+ return delegateModel->stringValue(index, effectiveTextRole);
+}
+
+QVariant QQuickComboBoxPrivate::fastValueAt(int index) const
+{
+ const QString effectiveValueRole = valueRole.isEmpty() ? QStringLiteral("modelData") : valueRole;
+ return delegateModel->variantValue(index, effectiveValueRole);
+}
+
void QQuickComboBoxPrivate::acceptInput()
{
Q_Q(QQuickComboBox);
@@ -508,10 +581,8 @@ void QQuickComboBoxPrivate::setCurrentIndex(int index, Activation activate)
currentIndex = index;
emit q->currentIndexChanged();
- if (componentComplete) {
- updateCurrentText();
- updateCurrentValue();
- }
+ if (componentComplete)
+ updateCurrentTextAndValue();
if (activate)
emit q->activated(index);
@@ -832,10 +903,14 @@ void QQuickComboBox::setModel(const QVariant& m)
if (d->model == model)
return;
- if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(d->model))
- QObjectPrivate::disconnect(aim, &QAbstractItemModel::dataChanged, d, &QQuickComboBoxPrivate::updateCurrentText);
- if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(model))
- QObjectPrivate::connect(aim, &QAbstractItemModel::dataChanged, d, &QQuickComboBoxPrivate::updateCurrentText);
+ if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(d->model)) {
+ QObjectPrivate::disconnect(aim, &QAbstractItemModel::dataChanged,
+ d, QOverload<>::of(&QQuickComboBoxPrivate::updateCurrentText));
+ }
+ if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(model)) {
+ QObjectPrivate::connect(aim, &QAbstractItemModel::dataChanged,
+ d, QOverload<>::of(&QQuickComboBoxPrivate::updateCurrentText));
+ }
d->model = model;
d->createDelegateModel();
@@ -1505,15 +1580,13 @@ QVariant QQuickComboBox::currentValue() const
QVariant QQuickComboBox::valueAt(int index) const
{
Q_D(const QQuickComboBox);
- if (!d->delegateModel || index < 0 || index >= d->delegateModel->count())
+ if (!d->isValidIndex(index))
return QVariant();
- // We use QVariant because the model API uses QVariant.
- QVariant value;
QObject *object = d->delegateModel->object(index);
+ QVariant value;
if (object) {
- const QString role = d->valueRole.isEmpty() ? QStringLiteral("modelData") : d->valueRole;
- value = d->delegateModel->variantValue(index, role);
+ value = d->fastValueAt(index);
d->delegateModel->release(object);
}
return value;
@@ -1550,13 +1623,13 @@ int QQuickComboBox::indexOfValue(const QVariant &value) const
QString QQuickComboBox::textAt(int index) const
{
Q_D(const QQuickComboBox);
- if (!d->delegateModel || index < 0 || index >= d->delegateModel->count())
+ if (!d->isValidIndex(index))
return QString();
- QString text;
QObject *object = d->delegateModel->object(index);
+ QString text;
if (object) {
- text = d->delegateModel->stringValue(index, d->textRole.isEmpty() ? QStringLiteral("modelData") : d->textRole);
+ text = d->fastTextAt(index);
d->delegateModel->release(object);
}
return text;
@@ -1823,12 +1896,10 @@ void QQuickComboBox::componentComplete()
static_cast<QQmlDelegateModel *>(d->delegateModel)->componentComplete();
if (count() > 0) {
- if (!d->hasCurrentIndex && d->currentIndex == -1) {
+ if (!d->hasCurrentIndex && d->currentIndex == -1)
setCurrentIndex(0);
- } else {
- d->updateCurrentText();
- d->updateCurrentValue();
- }
+ else
+ d->updateCurrentTextAndValue();
}
}
diff --git a/src/quicktemplates2/qquickcombobox_p.h b/src/quicktemplates2/qquickcombobox_p.h
index a55541d4..c9063d6a 100644
--- a/src/quicktemplates2/qquickcombobox_p.h
+++ b/src/quicktemplates2/qquickcombobox_p.h
@@ -163,8 +163,8 @@ public:
// 2.14 (Qt 5.14)
QVariant currentValue() const;
- Q_INVOKABLE QVariant valueAt(int index) const;
- Q_INVOKABLE int indexOfValue(const QVariant &value) const;
+ Q_REVISION(14) Q_INVOKABLE QVariant valueAt(int index) const;
+ Q_REVISION(14) Q_INVOKABLE int indexOfValue(const QVariant &value) const;
public Q_SLOTS:
void incrementCurrentIndex();
diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h
index e32fdb28..8a85f914 100644
--- a/src/quicktemplates2/qquickpopup_p_p.h
+++ b/src/quicktemplates2/qquickpopup_p_p.h
@@ -187,7 +187,7 @@ public:
QQuickPopup::ClosePolicy closePolicy = DefaultClosePolicy;
QQuickItem *parentItem = nullptr;
QQuickItem *dimmer = nullptr;
- QQuickWindow *window = nullptr;
+ QPointer<QQuickWindow> window;
QQuickTransition *enter = nullptr;
QQuickTransition *exit = nullptr;
QQuickPopupItem *popupItem = nullptr;
diff --git a/src/quicktemplates2/qquicktooltip.cpp b/src/quicktemplates2/qquicktooltip.cpp
index c1271dab..0a36e0c7 100644
--- a/src/quicktemplates2/qquicktooltip.cpp
+++ b/src/quicktemplates2/qquicktooltip.cpp
@@ -238,12 +238,13 @@ void QQuickToolTip::setTimeout(int timeout)
if (d->timeout == timeout)
return;
+ d->timeout = timeout;
+
if (timeout <= 0)
d->stopTimeout();
else if (isVisible())
d->startTimeout();
- d->timeout = timeout;
emit timeoutChanged();
}
diff --git a/src/quicktemplates2/qquicktumbler.cpp b/src/quicktemplates2/qquicktumbler.cpp
index 8b702c60..85c70b1b 100644
--- a/src/quicktemplates2/qquicktumbler.cpp
+++ b/src/quicktemplates2/qquicktumbler.cpp
@@ -949,9 +949,20 @@ void QQuickTumblerAttachedPrivate::calculateDisplacement()
const qreal contentY = tumblerPrivate->viewContentY;
const qreal delegateH = delegateHeight(tumbler);
const qreal preferredHighlightBegin = tumblerPrivate->view->property("preferredHighlightBegin").toReal();
- // Tumbler's displacement goes from negative at the top to positive towards the bottom, so we must switch this around.
- const qreal reverseDisplacement = (contentY + preferredHighlightBegin) / delegateH;
- displacement = reverseDisplacement - index;
+ const qreal itemY = qobject_cast<QQuickItem*>(parent)->y();
+ qreal currentItemY = 0;
+ auto currentItem = tumblerPrivate->view->property("currentItem").value<QQuickItem*>();
+ if (currentItem)
+ currentItemY = currentItem->y();
+ // Start from the y position of the current item.
+ const qreal topOfCurrentItemInViewport = currentItemY - contentY;
+ // Then, calculate the distance between it and the preferredHighlightBegin.
+ const qreal relativePositionToPreferredHighlightBegin = topOfCurrentItemInViewport - preferredHighlightBegin;
+ // Next, calculate the distance between us and the current item.
+ const qreal distanceFromCurrentItem = currentItemY - itemY;
+ const qreal displacementInPixels = distanceFromCurrentItem - relativePositionToPreferredHighlightBegin;
+ // Convert it from pixels to a floating point index.
+ displacement = displacementInPixels / delegateH;
}
emitIfDisplacementChanged(previousDisplacement, displacement);
diff --git a/tests/auto/controls/data/tst_tooltip.qml b/tests/auto/controls/data/tst_tooltip.qml
index 18911895..70579c70 100644
--- a/tests/auto/controls/data/tst_tooltip.qml
+++ b/tests/auto/controls/data/tst_tooltip.qml
@@ -205,6 +205,15 @@ TestCase {
else
control.visible = true
compare(control.visible, true)
+ // wait a bit to make sure that it's still visible
+ wait(50)
+ compare(control.visible, true)
+ // re-arm for another 200 ms
+ control.timeout = 200
+ compare(control.visible, true)
+ // ensure that it's still visible after 150 ms (where old timeout < 150 < new timeout)
+ wait(150)
+ compare(control.visible, true)
tryCompare(control, "visible", false)
}
diff --git a/tests/auto/controls/data/tst_tumbler.qml b/tests/auto/controls/data/tst_tumbler.qml
index c9cc10d7..5b3ef6e3 100644
--- a/tests/auto/controls/data/tst_tumbler.qml
+++ b/tests/auto/controls/data/tst_tumbler.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -54,8 +54,8 @@ import QtQuick.Controls 2.12
TestCase {
id: testCase
- width: 200
- height: 200
+ width: 300
+ height: 300
visible: true
when: windowShown
name: "Tumbler"
@@ -513,6 +513,7 @@ TestCase {
Text {
text: parent.displacement.toFixed(2)
anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
}
property real displacement: Tumbler.displacement
@@ -1236,4 +1237,24 @@ TestCase {
// 5 - 2 = 3
compare(tumbler.currentIndex, 3);
}
+
+ function test_displacementAfterResizing() {
+ createTumbler({
+ width: 200,
+ wrap: false,
+ delegate: displacementDelegate,
+ model: 30,
+ visibleItemCount: 7,
+ currentIndex: 15
+ })
+
+ var delegate = findChild(tumblerView, "delegate15")
+ verify(delegate)
+
+ tryCompare(delegate, "displacement", 0)
+
+ // Resizing the Tumbler shouldn't affect the displacement.
+ tumbler.height *= 1.4
+ tryCompare(delegate, "displacement", 0)
+ }
}
diff --git a/tests/auto/qquickpopup/data/toolTipCrashOnClose.qml b/tests/auto/qquickpopup/data/toolTipCrashOnClose.qml
new file mode 100644
index 00000000..8de14f4c
--- /dev/null
+++ b/tests/auto/qquickpopup/data/toolTipCrashOnClose.qml
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.13
+import QtQuick.Window 2.13
+import QtQuick.Controls 2.13
+import QtGraphicalEffects 1.13
+
+Window {
+ width: 640
+ height: 480
+
+ readonly property bool toolTipOpened: mouseArea.ToolTip.toolTip.opened
+
+ Component.onCompleted: contentItem.objectName = "windowContentItem"
+
+ // For the setOverlayParentToNull test.
+ function nullifyOverlayParent() {
+ Overlay.overlay.parent = null
+ }
+
+ Item {
+ objectName: "outerItem"
+ anchors.fill: parent
+
+ Item {
+ objectName: "innerItem"
+ anchors.fill: parent
+
+ ColorOverlay {
+ objectName: "colorOverlay"
+ source: parent
+ anchors.fill: parent
+ }
+
+ MouseArea {
+ id: mouseArea
+ objectName: "mouseArea"
+ anchors.fill: parent
+ hoverEnabled: true
+
+ ToolTip.visible: containsMouse
+ ToolTip.text: "ToolTip text"
+ }
+ }
+ }
+}
diff --git a/tests/auto/qquickpopup/tst_qquickpopup.cpp b/tests/auto/qquickpopup/tst_qquickpopup.cpp
index 636b2b21..4d238663 100644
--- a/tests/auto/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/qquickpopup/tst_qquickpopup.cpp
@@ -89,6 +89,8 @@ private slots:
void disabledPalette();
void disabledParentPalette();
void countChanged();
+ void toolTipCrashOnClose();
+ void setOverlayParentToNull();
};
void tst_QQuickPopup::initTestCase()
@@ -1187,6 +1189,46 @@ void tst_QQuickPopup::countChanged()
QVERIFY(window->setProperty("isModel1", false));
QTRY_COMPARE(window->property("count").toInt(), 2);
}
+
+// QTBUG-73243
+void tst_QQuickPopup::toolTipCrashOnClose()
+{
+ QQuickApplicationHelper helper(this, "toolTipCrashOnClose.qml");
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ // TODO: Using ignoreMessage() fails in CI with macOS for release builds,
+ // so for now we let the warning through.
+// QTest::ignoreMessage(QtWarningMsg, "ShaderEffectSource: 'recursive' must be set to true when rendering recursively.");
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2));
+ QTRY_VERIFY(window->property("toolTipOpened").toBool());
+
+ QVERIFY(window->close());
+ // Shouldn't crash.
+}
+
+void tst_QQuickPopup::setOverlayParentToNull()
+{
+ QQuickApplicationHelper helper(this, "toolTipCrashOnClose.qml");
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ // TODO: Using ignoreMessage() fails in CI with macOS for release builds,
+ // so for now we let the warning through.
+// QTest::ignoreMessage(QtWarningMsg, "ShaderEffectSource: 'recursive' must be set to true when rendering recursively.");
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ QVERIFY(QMetaObject::invokeMethod(window, "nullifyOverlayParent"));
+
+ QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2));
+ QTRY_VERIFY(window->property("toolTipOpened").toBool());
+
+ QVERIFY(window->close());
+ // While nullifying the overlay parent doesn't make much sense, it shouldn't crash.
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"