aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dist/changes-5.9.463
-rw-r--r--src/imports/controls/Tumbler.qml2
-rw-r--r--src/imports/controls/doc/snippets/qtquickcontrols2-tumbler-custom.qml2
-rw-r--r--src/imports/controls/material/Tumbler.qml2
-rw-r--r--src/imports/controls/universal/Tumbler.qml2
-rw-r--r--src/quickcontrols2/qquicktumblerview.cpp9
-rw-r--r--src/quicktemplates2/qquicktumbler.cpp91
-rw-r--r--src/quicktemplates2/qquicktumbler_p.h1
-rw-r--r--src/quicktemplates2/qquicktumbler_p_p.h1
-rw-r--r--tests/auto/controls/data/TumblerListView.qml12
-rw-r--r--tests/auto/controls/data/TumblerPathView.qml11
-rw-r--r--tests/auto/controls/data/tst_tumbler.qml21
-rw-r--r--tests/auto/customization/tst_customization.cpp2
13 files changed, 177 insertions, 42 deletions
diff --git a/dist/changes-5.9.4 b/dist/changes-5.9.4
new file mode 100644
index 00000000..3ac6f4a5
--- /dev/null
+++ b/dist/changes-5.9.4
@@ -0,0 +1,63 @@
+Qt 5.9.4 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.9.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+http://doc.qt.io/qt-5/index.html
+
+The Qt version 5.9 series is binary compatible with the 5.8.x series.
+Applications compiled for 5.8 will continue to run with 5.9.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Important Behavior Changes *
+****************************************************************************
+
+ - As a fix to QTBUG-50992, execution of delegates, such as contentItem and
+ background, is deferred until the construction of a control is complete,
+ or a delegate is accessed. Consequently, if a delegate is replaced with
+ a custom delegate, the default delegate is not created. Previously, when
+ a default delegate was replaced by a custom delegate, the default
+ delegate was nevertheless created and destroyed during the construction
+ of the control.
+
+****************************************************************************
+* Controls *
+****************************************************************************
+
+ - [QTBUG-64548] Fixed shortcuts in QQuickWidget
+
+ - [QTBUG-65016] Fixed QQuickStyle::setFallbackStyle() in static builds
+
+ - [QTBUG-50992] Fixed a bug which caused delegates of customized controls
+ to often be "destroyed during incubation" when asynchronously incubated
+ by item views or asynchronous loaders.
+
+ - BusyIndicator:
+ * [QTBUG-62874] Fixed busy animation in QQuickWidget
+
+ - ProgressBar:
+ * [QTBUG-62874] Fixed indeterminate animation in QQuickWidget
+
+ - StackView:
+ * [QTBUG-65084] Fixed to not block touch events.
+
+ - SwipeView:
+ * [QTBUG-62110] Fixed slow swiping in large views
+
+ - ToolButton:
+ * [QTBUG-65052] Fixed round ripple for square buttons in the Material style
+
+ - Tumbler:
+ * [QTBUG-65165] Made currentIndex animations take a constant amount of
+ time (1 second) regardless of how many items are in the model. This
+ prevents Tumblers with large amounts of items from scrolling too
+ slowly when changing the currentIndex.
diff --git a/src/imports/controls/Tumbler.qml b/src/imports/controls/Tumbler.qml
index 752d5834..eb912553 100644
--- a/src/imports/controls/Tumbler.qml
+++ b/src/imports/controls/Tumbler.qml
@@ -48,7 +48,7 @@ T.Tumbler {
text: modelData
color: control.visualFocus ? control.palette.highlight : control.palette.text
font: control.font
- opacity: 1.0 - Math.abs(Tumbler.displacement) / (visibleItemCount / 2)
+ opacity: 1.0 - Math.abs(Tumbler.displacement) / (control.visibleItemCount / 2)
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-tumbler-custom.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-tumbler-custom.qml
index b3645587..cc37d541 100644
--- a/src/imports/controls/doc/snippets/qtquickcontrols2-tumbler-custom.qml
+++ b/src/imports/controls/doc/snippets/qtquickcontrols2-tumbler-custom.qml
@@ -56,7 +56,7 @@ Tumbler {
font: control.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
- opacity: 1.0 - Math.abs(Tumbler.displacement) / (visibleItemCount / 2)
+ opacity: 1.0 - Math.abs(Tumbler.displacement) / (control.visibleItemCount / 2)
}
Rectangle {
diff --git a/src/imports/controls/material/Tumbler.qml b/src/imports/controls/material/Tumbler.qml
index 8d58257f..4364581d 100644
--- a/src/imports/controls/material/Tumbler.qml
+++ b/src/imports/controls/material/Tumbler.qml
@@ -49,7 +49,7 @@ T.Tumbler {
text: modelData
color: control.Material.foreground
font: control.font
- opacity: (1.0 - Math.abs(Tumbler.displacement) / (visibleItemCount / 2)) * (control.enabled ? 1 : 0.6)
+ opacity: (1.0 - Math.abs(Tumbler.displacement) / (control.visibleItemCount / 2)) * (control.enabled ? 1 : 0.6)
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/imports/controls/universal/Tumbler.qml b/src/imports/controls/universal/Tumbler.qml
index 70e80354..30b3b785 100644
--- a/src/imports/controls/universal/Tumbler.qml
+++ b/src/imports/controls/universal/Tumbler.qml
@@ -50,7 +50,7 @@ T.Tumbler {
text: modelData
font: control.font
color: control.Universal.foreground
- opacity: (1.0 - Math.abs(Tumbler.displacement) / (visibleItemCount / 2)) * (control.enabled ? 1 : 0.6)
+ opacity: (1.0 - Math.abs(Tumbler.displacement) / (control.visibleItemCount / 2)) * (control.enabled ? 1 : 0.6)
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
diff --git a/src/quickcontrols2/qquicktumblerview.cpp b/src/quickcontrols2/qquicktumblerview.cpp
index e8f0c364..4469810c 100644
--- a/src/quickcontrols2/qquicktumblerview.cpp
+++ b/src/quickcontrols2/qquicktumblerview.cpp
@@ -173,14 +173,19 @@ void QQuickTumblerView::createView()
m_listView->setParentItem(this);
m_listView->setSnapMode(QQuickListView::SnapToItem);
m_listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
- m_listView->setHighlightMoveDuration(1000);
m_listView->setClip(true);
- m_listView->setDelegate(m_delegate);
// Give the view a size.
updateView();
// Set the model.
updateModel();
+
+ // Set these after the model is set so that the currentItem animation
+ // happens instantly on startup/after switching models. If we set them too early,
+ // the view animates any potential currentIndex change over one second,
+ // which we don't want when the contentItem has just been created.
+ m_listView->setDelegate(m_delegate);
+ m_listView->setHighlightMoveDuration(1000);
}
}
}
diff --git a/src/quicktemplates2/qquicktumbler.cpp b/src/quicktemplates2/qquicktumbler.cpp
index bed3e400..efa61dad 100644
--- a/src/quicktemplates2/qquicktumbler.cpp
+++ b/src/quicktemplates2/qquicktumbler.cpp
@@ -120,17 +120,24 @@ namespace {
*/
QQuickItem *QQuickTumblerPrivate::determineViewType(QQuickItem *contentItem)
{
+ if (!contentItem) {
+ resetViewData();
+ return nullptr;
+ }
+
if (contentItem->inherits("QQuickPathView")) {
view = contentItem;
viewContentItem = contentItem;
viewContentItemType = PathViewContentItem;
viewOffset = 0;
+
return contentItem;
} else if (contentItem->inherits("QQuickListView")) {
view = contentItem;
viewContentItem = qobject_cast<QQuickFlickable*>(contentItem)->contentItem();
viewContentItemType = ListViewContentItem;
viewContentY = 0;
+
return contentItem;
} else {
const auto childItems = contentItem->childItems();
@@ -142,6 +149,7 @@ QQuickItem *QQuickTumblerPrivate::determineViewType(QQuickItem *contentItem)
}
resetViewData();
+ viewContentItemType = UnsupportedContentItemType;
return nullptr;
}
@@ -153,7 +161,7 @@ void QQuickTumblerPrivate::resetViewData()
viewOffset = 0;
else if (viewContentItemType == ListViewContentItem)
viewContentY = 0;
- viewContentItemType = UnsupportedContentItemType;
+ viewContentItemType = NoContentItem;
}
QList<QQuickItem *> QQuickTumblerPrivate::viewContentItemChildItems() const
@@ -257,24 +265,16 @@ void QQuickTumblerPrivate::calculateDisplacements()
}
}
-void QQuickTumblerPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
+void QQuickTumblerPrivate::itemChildAdded(QQuickItem *, QQuickItem *)
{
_q_updateItemWidths();
_q_updateItemHeights();
-
- QQuickTumblerAttached *attached = qobject_cast<QQuickTumblerAttached *>(qmlAttachedPropertiesObject<QQuickTumbler>(child, false));
- if (attached)
- QQuickTumblerAttachedPrivate::get(attached)->calculateDisplacement();
}
-void QQuickTumblerPrivate::itemChildRemoved(QQuickItem *, QQuickItem *child)
+void QQuickTumblerPrivate::itemChildRemoved(QQuickItem *, QQuickItem *)
{
_q_updateItemWidths();
_q_updateItemHeights();
-
- QQuickTumblerAttached *attached = qobject_cast<QQuickTumblerAttached *>(qmlAttachedPropertiesObject<QQuickTumbler>(child, false));
- if (attached)
- QQuickTumblerAttachedPrivate::get(attached)->calculateDisplacement();
}
void QQuickTumblerPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &)
@@ -527,8 +527,6 @@ void QQuickTumbler::componentComplete()
{
Q_D(QQuickTumbler);
QQuickControl::componentComplete();
- d->_q_updateItemHeights();
- d->_q_updateItemWidths();
if (!d->view) {
// Force the view to be created.
@@ -536,6 +534,17 @@ void QQuickTumbler::componentComplete()
// Determine the type of view for attached properties, etc.
d->setupViewData(d->contentItem);
}
+
+ // If there was no contentItem or it was of an unsupported type,
+ // we don't have anything else to do.
+ if (!d->view)
+ return;
+
+ // Update item heights after we've populated the model,
+ // otherwise ignoreSignals will cause these functions to return early.
+ d->_q_updateItemHeights();
+ d->_q_updateItemWidths();
+ d->_q_onViewCountChanged();
}
void QQuickTumbler::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
@@ -554,6 +563,9 @@ void QQuickTumbler::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
// Make sure we use the new content item and not the current one, as that won't
// be changed until after contentItemChange() has finished.
d->setupViewData(newItem);
+
+ d->_q_updateItemHeights();
+ d->_q_updateItemWidths();
}
}
}
@@ -593,6 +605,9 @@ void QQuickTumblerPrivate::setupViewData(QQuickItem *newControlContentItem)
determineViewType(newControlContentItem);
+ if (viewContentItemType == QQuickTumblerPrivate::NoContentItem)
+ return;
+
if (viewContentItemType == QQuickTumblerPrivate::UnsupportedContentItemType) {
qWarning() << "Tumbler: contentItem must contain either a PathView or a ListView";
return;
@@ -617,6 +632,8 @@ void QQuickTumblerPrivate::setupViewData(QQuickItem *newControlContentItem)
// Sync the view's currentIndex with ours.
syncCurrentIndex();
+
+ calculateDisplacements();
}
void QQuickTumblerPrivate::syncCurrentIndex()
@@ -624,17 +641,27 @@ void QQuickTumblerPrivate::syncCurrentIndex()
const int actualViewIndex = view->property("currentIndex").toInt();
Q_Q(QQuickTumbler);
+ const bool isPendingCurrentIndex = pendingCurrentIndex != -1;
+ const int indexToSet = isPendingCurrentIndex ? pendingCurrentIndex : currentIndex;
+
// Nothing to do.
- if (actualViewIndex == currentIndex)
+ if (actualViewIndex == indexToSet) {
+ pendingCurrentIndex = -1;
return;
+ }
// PathView likes to use 0 as currentIndex for empty models, but we use -1 for that.
if (q->count() == 0 && actualViewIndex == 0)
return;
ignoreCurrentIndexChanges = true;
- view->setProperty("currentIndex", currentIndex);
+ view->setProperty("currentIndex", QVariant(indexToSet));
ignoreCurrentIndexChanges = false;
+
+ if (view->property("currentIndex").toInt() == indexToSet)
+ pendingCurrentIndex = -1;
+ else if (isPendingCurrentIndex)
+ q->polish();
}
void QQuickTumblerPrivate::setCount(int newCount)
@@ -685,11 +712,17 @@ void QQuickTumblerPrivate::setWrap(bool shouldWrap, bool isExplicit)
ignoreCurrentIndexChanges = false;
- // The view should have been created now, so we can start determining its type, etc.
- // If the delegates use attached properties, this will have already been called,
- // in which case it will return early. If the delegate doesn't use attached properties,
- // we need to call it here.
- setupViewData(contentItem);
+ // If isComponentComplete() is true, we require a contentItem. If it's not
+ // true, it might not have been created yet, so we wait until
+ // componentComplete() is called.
+ //
+ // When the contentItem (usually QQuickTumblerView) has been created, we
+ // can start determining its type, etc. If the delegates use attached
+ // properties, this will have already been called, in which case it will
+ // return early. If the delegate doesn't use attached properties, we need
+ // to call it here.
+ if (q->isComponentComplete() || contentItem)
+ setupViewData(contentItem);
q->setCurrentIndex(oldCurrentIndex);
}
@@ -724,6 +757,10 @@ void QQuickTumbler::updatePolish()
{
Q_D(QQuickTumbler);
if (d->pendingCurrentIndex != -1) {
+ // Update our count, as ignoreSignals might have been true
+ // when _q_onViewCountChanged() was last called.
+ d->setCount(d->view->property("count").toInt());
+
// If the count is still 0, it's not going to happen.
if (d->count == 0) {
d->pendingCurrentIndex = -1;
@@ -789,9 +826,9 @@ void QQuickTumblerAttachedPrivate::calculateDisplacement()
const int previousDisplacement = displacement;
displacement = 0;
- // Can happen if the attached properties are accessed on the wrong type of item or the tumbler was destroyed.
if (!tumbler) {
- emitIfDisplacementChanged(previousDisplacement, displacement);
+ // Can happen if the attached properties are accessed on the wrong type of item or the tumbler was destroyed.
+ // We don't want to emit the change signal though, as this could cause warnings about Tumbler.tumbler being null.
return;
}
@@ -860,10 +897,12 @@ QQuickTumblerAttached::QQuickTumblerAttached(QObject *parent)
QQuickTumblerPrivate *tumblerPrivate = QQuickTumblerPrivate::get(d->tumbler);
tumblerPrivate->setupViewData(tumblerPrivate->contentItem);
- if (!tumblerPrivate->viewContentItem)
- return;
-
- d->calculateDisplacement();
+ if (delegateItem->parentItem() == tumblerPrivate->viewContentItem) {
+ // This item belongs to the "new" view, meaning that the tumbler's contentItem
+ // was probably assigned declaratively. If they're not equal, calling
+ // calculateDisplacement() would use the old contentItem data, which is bad.
+ d->calculateDisplacement();
+ }
}
}
diff --git a/src/quicktemplates2/qquicktumbler_p.h b/src/quicktemplates2/qquicktumbler_p.h
index c388c28d..5d4df4a7 100644
--- a/src/quicktemplates2/qquicktumbler_p.h
+++ b/src/quicktemplates2/qquicktumbler_p.h
@@ -70,7 +70,6 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumbler : public QQuickControl
Q_PROPERTY(bool wrap READ wrap WRITE setWrap RESET resetWrap NOTIFY wrapChanged FINAL REVISION 1)
// 2.2 (Qt 5.9)
Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged FINAL REVISION 2)
- Q_CLASSINFO("DeferredPropertyNames", "background")
public:
explicit QQuickTumbler(QQuickItem *parent = nullptr);
diff --git a/src/quicktemplates2/qquicktumbler_p_p.h b/src/quicktemplates2/qquicktumbler_p_p.h
index fe8a4ad2..75b1a396 100644
--- a/src/quicktemplates2/qquicktumbler_p_p.h
+++ b/src/quicktemplates2/qquicktumbler_p_p.h
@@ -63,6 +63,7 @@ public:
~QQuickTumblerPrivate();
enum ContentItemType {
+ NoContentItem,
UnsupportedContentItemType,
PathViewContentItem,
ListViewContentItem
diff --git a/tests/auto/controls/data/TumblerListView.qml b/tests/auto/controls/data/TumblerListView.qml
index 4e71f471..1248bec0 100644
--- a/tests/auto/controls/data/TumblerListView.qml
+++ b/tests/auto/controls/data/TumblerListView.qml
@@ -49,15 +49,21 @@
****************************************************************************/
import QtQuick 2.6
+import QtQuick.Controls 2.2
ListView {
anchors.fill: parent
- model: parent.model
- delegate: parent.delegate
-
snapMode: ListView.SnapToItem
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: height / 2 - (height / parent.visibleItemCount / 2)
preferredHighlightEnd: height / 2 + (height / parent.visibleItemCount / 2)
clip: true
+ model: parent.model
+ delegate: Text {
+ objectName: text
+ text: "Custom" + modelData
+ opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
}
diff --git a/tests/auto/controls/data/TumblerPathView.qml b/tests/auto/controls/data/TumblerPathView.qml
index 4f6f653f..7b7cd5f4 100644
--- a/tests/auto/controls/data/TumblerPathView.qml
+++ b/tests/auto/controls/data/TumblerPathView.qml
@@ -49,16 +49,23 @@
****************************************************************************/
import QtQuick 2.6
+import QtQuick.Controls 2.2
PathView {
id: pathView
- model: parent.model
- delegate: parent.delegate
clip: true
pathItemCount: parent.visibleItemCount + 1
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
dragMargin: width / 2
+ model: parent.model
+ delegate: Text {
+ objectName: text
+ text: "Custom" + modelData
+ opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
path: Path {
startX: pathView.width / 2
diff --git a/tests/auto/controls/data/tst_tumbler.qml b/tests/auto/controls/data/tst_tumbler.qml
index fbcb2c40..03908566 100644
--- a/tests/auto/controls/data/tst_tumbler.qml
+++ b/tests/auto/controls/data/tst_tumbler.qml
@@ -289,11 +289,10 @@ TestCase {
compare(tumblerView.currentIndex, data.currentIndex);
compare(tumblerView.currentItem.text, data.currentIndex.toString());
- var fuzz = 1;
if (data.wrap) {
- fuzzyCompare(tumblerView.offset, data.currentIndex > 0 ? tumblerView.count - data.currentIndex : 0, fuzz);
+ tryCompare(tumblerView, "offset", data.currentIndex > 0 ? tumblerView.count - data.currentIndex : 0);
} else {
- fuzzyCompare(tumblerView.contentY, tumblerDelegateHeight * data.currentIndex - tumblerView.preferredHighlightBegin, fuzz);
+ tryCompare(tumblerView, "contentY", tumblerDelegateHeight * data.currentIndex - tumblerView.preferredHighlightBegin);
}
}
@@ -682,6 +681,17 @@ TestCase {
compare(tumbler.currentIndex, 3);
}
+ function findFirstDelegateWithText(view, text) {
+ var delegate = null;
+ var contentItem = view.hasOwnProperty("contentItem") ? view.contentItem : view;
+ for (var i = 0; i < contentItem.children.length && !delegate; ++i) {
+ var child = contentItem.children[i];
+ if (child.hasOwnProperty("text") && child.text === text)
+ delegate = child;
+ }
+ return delegate;
+ }
+
function test_customContentItemAfterConstruction_data() {
return [
{ tag: "ListView", componentPath: "TumblerListView.qml" },
@@ -707,6 +717,11 @@ TestCase {
tumblerView = findView(tumbler);
compare(tumblerView.currentIndex, 2);
+ var delegate = findFirstDelegateWithText(tumblerView, "Custom2");
+ verify(delegate);
+ compare(delegate.height, defaultImplicitDelegateHeight);
+ tryCompare(delegate.Tumbler, "displacement", 0);
+
tumblerView.incrementCurrentIndex();
compare(tumblerView.currentIndex, 3);
compare(tumbler.currentIndex, 3);
diff --git a/tests/auto/customization/tst_customization.cpp b/tests/auto/customization/tst_customization.cpp
index cd939bcc..91b5706c 100644
--- a/tests/auto/customization/tst_customization.cpp
+++ b/tests/auto/customization/tst_customization.cpp
@@ -103,7 +103,7 @@ static const ControlInfo ControlInfos[] = {
{ "ToolButton", QStringList() << "background" << "contentItem" },
{ "ToolSeparator", QStringList() << "background" << "contentItem" },
{ "ToolTip", QStringList() << "background" << "contentItem" },
- // { "Tumbler", QStringList() << "background" << "contentItem" } ### TODO: fix and enable deferred execution
+ { "Tumbler", QStringList() << "background" << "contentItem" }
};
class tst_customization : public QQmlDataTest