Merge remote-tracking branch 'origin/5.9' into dev

Conflicts:
	.qmake.conf
	tests/auto/controls/data/tst_scrollindicator.qml

Change-Id: I1f5581ae7814c0d4152e4c9b79a30a8af5a3a17b
This commit is contained in:
J-P Nurmi 2017-07-11 12:06:15 +02:00
commit d2d0e08e58
46 changed files with 563 additions and 149 deletions

75
dist/changes-5.9.1 vendored Normal file
View File

@ -0,0 +1,75 @@
Qt 5.9.1 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.
****************************************************************************
* General *
****************************************************************************
- [QTBUG-58571] Enabled the use of QML caching at build time.
- [QTBUG-61144] Added a configure feature for disabling multi- touch
support (configure -no-feature-quicktemplates2-multitouch).
****************************************************************************
* Controls *
****************************************************************************
- ApplicationWindow:
* [QTBUG-60893] Fixed access to revisioned members in base classes.
- ComboBox:
* [QTBUG-60684] Fixed an empty popup being shown after model is cleared.
- Container:
* [QTBUG-61310] Fixed a crash that occurred with Repeater under certain
circumstances.
- Menu:
* Fixed key navigation to skip separators.
- Page:
* [QTBUG-61109] Fixed the initial content layouting of dynamically created
Page instances.
- Popup:
* [QTBUG-61114] Fixed font inheritance for popups.
- SpinBox:
* [QTBUG-61426] Fixed valueModified() to get emitted on repeated value
changes during long press.
- StackView:
* Fixed clear() to not emit depthChanged() when the view is empty.
****************************************************************************
* Styles *
****************************************************************************
- QQuickStyle:
* [QTBUG-60973] Fixed availableStyles() to exclude debug symbol folders
(.dSYM) on macOS.
Default
--------
- RangeSlider:
* Fixed the second handle to visualize its pressed state.
Material & Universal
--------------------
- ScrollBar:
* Fixed flashing when calling decrease() or increase() for key navigation.

View File

@ -83,7 +83,7 @@ ApplicationWindow {
Image {
id: avatar
source: "qrc:/" + modelData + ".png"
source: "qrc:/" + modelData.replace(" ", "_") + ".png"
}
}
}

View File

@ -80,7 +80,7 @@ Page {
Image {
id: avatar
source: "qrc:/" + modelData + ".png"
source: "qrc:/" + modelData.replace(" ", "_") + ".png"
}
}
}

View File

@ -82,7 +82,7 @@ Page {
Image {
id: avatar
source: "qrc:/" + model.display + ".png"
source: "qrc:/" + model.display.replace(" ", "_") + ".png"
}
}
}

View File

@ -104,7 +104,7 @@ Page {
Image {
id: avatar
source: !sentByMe ? "qrc:/" + model.author + ".png" : ""
source: !sentByMe ? "qrc:/" + model.author.replace(" ", "_") + ".png" : ""
}
Rectangle {

View File

@ -82,7 +82,7 @@ Page {
Image {
id: avatar
source: "qrc:/" + model.display + ".png"
source: "qrc:/" + model.display.replace(" ", "_") + ".png"
}
}
}

View File

@ -104,7 +104,7 @@ Page {
Image {
id: avatar
source: !sentByMe ? "qrc:/" + model.author + ".png" : ""
source: !sentByMe ? "qrc:/" + model.author.replace(" ", "_") + ".png" : ""
}
Rectangle {

View File

@ -149,7 +149,7 @@ how to navigate between several pages.
\printuntil }
We replace the default \c{MainForm {...}} code block with a Page, which is
sized to occupy all the space on the window using the \l{anchors.fill}
sized to occupy all the space on the window using the \l {Item::}{anchors.fill}
property.
Then, we assign a \l Label to its \l {Page::}{header} property. Label extends
@ -245,7 +245,7 @@ Here is our ListView:
\section2 Sizing and Positioning
The first thing we do is set a size for the view. It should fill the available
space on the page, so we use \l {Item::anchors}{anchors.fill}. Note that
space on the page, so we use \l {Item::}{anchors.fill}. Note that
Page ensures that its header and footer have enough of their own space
reserved, so the view in this case will sit below the header, for example.

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,16 +1,16 @@
<RCC>
<qresource prefix="/">
<file>Albert Einstein.png</file>
<file>Albert Einstein@2x.png</file>
<file>Albert Einstein@3x.png</file>
<file>Albert Einstein@4x.png</file>
<file>Ernest Hemingway.png</file>
<file>Ernest Hemingway@2x.png</file>
<file>Ernest Hemingway@3x.png</file>
<file>Ernest Hemingway@4x.png</file>
<file>Hans Gude.png</file>
<file>Hans Gude@2x.png</file>
<file>Hans Gude@3x.png</file>
<file>Hans Gude@4x.png</file>
<file>Albert_Einstein.png</file>
<file>Albert_Einstein@2x.png</file>
<file>Albert_Einstein@3x.png</file>
<file>Albert_Einstein@4x.png</file>
<file>Ernest_Hemingway.png</file>
<file>Ernest_Hemingway@2x.png</file>
<file>Ernest_Hemingway@3x.png</file>
<file>Ernest_Hemingway@4x.png</file>
<file>Hans_Gude.png</file>
<file>Hans_Gude@2x.png</file>
<file>Hans_Gude@3x.png</file>
<file>Hans_Gude@4x.png</file>
</qresource>
</RCC>

View File

@ -48,6 +48,30 @@ Column {
showVerticalAlignment: true
}
Section {
anchors.left: parent.left
anchors.right: parent.right
caption: qsTr("Text Color")
ColorEditor {
caption: qsTr("Text Color")
backendValue: backendValues.color
supportGradient: false
}
}
Section {
anchors.left: parent.left
anchors.right: parent.right
caption: qsTr("Style Color")
ColorEditor {
caption: qsTr("Style Color")
backendValue: backendValues.styleColor
supportGradient: false
}
}
FontSection {
width: parent.width
}

View File

@ -36,7 +36,6 @@
#include "qquicktumblerview_p.h"
#include <QtQml/private/qqmldelegatemodel_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquickpathview_p.h>
@ -126,7 +125,17 @@ void QQuickTumblerView::createView()
// the count yet, because we rely on the view to tell us the count.
if (m_tumbler->wrap()) {
if (m_listView) {
delete m_listView;
// It's necessary to call deleteLater() rather than delete,
// as this code is most likely being run in rensponse to a signal
// emission somewhere in the list view's internals, so we need to
// wait until that has finished.
m_listView->deleteLater();
QQml_setParent_noEvent(m_listView, nullptr);
// The auto tests pass with unparenting the list view alone, but
// just to be sure, we unset some other things as well.
m_listView->setParentItem(nullptr);
m_listView->setVisible(false);
m_listView->setModel(QVariant());
m_listView = nullptr;
}
@ -143,12 +152,16 @@ void QQuickTumblerView::createView()
// Give the view a size.
updateView();
// Ensure that the model is set eventually.
polish();
// Set the model.
updateModel();
}
} else {
if (m_pathView) {
delete m_pathView;
m_pathView->deleteLater();
QQml_setParent_noEvent(m_pathView, nullptr);
m_pathView->setParentItem(nullptr);
m_pathView->setVisible(false);
m_pathView->setModel(QVariant());
m_pathView = nullptr;
}
@ -164,8 +177,8 @@ void QQuickTumblerView::createView()
// Give the view a size.
updateView();
// Ensure that the model is set eventually.
polish();
// Set the model.
updateModel();
}
}
}
@ -193,6 +206,56 @@ void QQuickTumblerView::updateView()
}
}
void QQuickTumblerView::updateModel()
{
if (m_pathView && !m_pathView->model().isValid() && m_model.isValid()) {
// QQuickPathView::setPathItemCount() resets the offset animation,
// so we just skip the animation while constructing the view.
const int oldHighlightMoveDuration = m_pathView->highlightMoveDuration();
m_pathView->setHighlightMoveDuration(0);
// Setting model can change the count, which can affect the wrap, which can cause
// the current view to be deleted before setModel() is finished, which causes a crash.
// Since QQuickTumbler can't know about QQuickTumblerView, we use its private API to
// inform it that it should delay setting wrap.
QQuickTumblerPrivate *tumblerPrivate = QQuickTumblerPrivate::get(m_tumbler);
tumblerPrivate->lockWrap();
m_pathView->setModel(m_model);
tumblerPrivate->unlockWrap();
// The count-depends-on-wrap behavior could cause wrap to change after
// the call above, so we must check that we're still using a PathView.
if (m_pathView)
m_pathView->setHighlightMoveDuration(oldHighlightMoveDuration);
} else if (m_listView && !m_listView->model().isValid() && m_model.isValid()) {
const int currentIndex = m_tumbler->currentIndex();
QQuickTumblerPrivate *tumblerPrivate = QQuickTumblerPrivate::get(m_tumbler);
// setModel() causes QQuickTumblerPrivate::_q_onViewCountChanged() to
// be called called, which calls QQuickTumbler::setCurrentIndex(),
// which results in QQuickItemViewPrivate::createHighlightItem() being
// called. When the highlight item is created,
// QQuickTumblerPrivate::itemChildAdded() is notified and
// QQuickTumblerPrivate::_q_updateItemHeights() is called, which causes
// a geometry change in the item and createHighlight() is called again.
// However, since the highlight item hadn't been assigned yet in the
// previous call frame, the "if (highlight) { delete highlight; }"
// check doesn't succeed, so the item is never deleted.
//
// To avoid this, we tell QQuickTumblerPrivate to ignore signals while
// setting the model, and manually call _q_onViewCountChanged() to
// ensure the correct sequence of calls happens (_q_onViewCountChanged()
// has to be within the ignoreSignals scope, because it also generates
// recursion otherwise).
tumblerPrivate->ignoreSignals = true;
m_listView->setModel(m_model);
m_listView->setCurrentIndex(currentIndex);
tumblerPrivate->_q_onViewCountChanged();
tumblerPrivate->ignoreSignals = false;
}
}
void QQuickTumblerView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
QQuickItem::geometryChanged(newGeometry, oldGeometry);
@ -223,42 +286,6 @@ void QQuickTumblerView::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
}
void QQuickTumblerView::updatePolish()
{
// There are certain cases where model count changes can potentially cause problems.
// An example of this is a ListModel that appends items in a for loop in Component.onCompleted.
// If we didn't delay assignment of the model, the PathView/ListView would be deleted in
// response to it emitting countChanged(), causing a crash. To avoid this issue,
// and to avoid the overhead of count affecting the wrap property, which in turn may
// unnecessarily create delegates that are never seen, we delay setting the model. This ensures that
// Component.onCompleted would have been finished, for example.
if (m_pathView && !m_pathView->model().isValid() && m_model.isValid()) {
// QQuickPathView::setPathItemCount() resets the offset animation,
// so we just skip the animation while constructing the view.
const int oldHighlightMoveDuration = m_pathView->highlightMoveDuration();
m_pathView->setHighlightMoveDuration(0);
// Setting model can change the count, which can affect the wrap, which can cause
// the current view to be deleted before setModel() is finished, which causes a crash.
// Since QQuickTumbler can't know about QQuickTumblerView, we use its private API to
// inform it that it should delay setting wrap.
QQuickTumblerPrivate *tumblerPrivate = QQuickTumblerPrivate::get(m_tumbler);
tumblerPrivate->lockWrap();
m_pathView->setModel(m_model);
tumblerPrivate->unlockWrap();
// The count-depends-on-wrap behavior could cause wrap to change after
// the call above, so we must check that we're still using a PathView.
if (m_pathView)
m_pathView->setHighlightMoveDuration(oldHighlightMoveDuration);
} else if (m_listView && !m_listView->model().isValid() && m_model.isValid()) {
// Usually we'd do this in QQuickTumbler::setWrap(), but that will be too early for polishes.
const int currentIndex = m_tumbler->currentIndex();
m_listView->setModel(m_model);
m_listView->setCurrentIndex(currentIndex);
}
}
QQuickItem *QQuickTumblerView::view()
{
if (!m_tumbler)

View File

@ -87,12 +87,12 @@ protected:
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void componentComplete() override;
void itemChange(ItemChange change, const ItemChangeData &data) override;
void updatePolish() override;
private:
QQuickItem *view();
void createView();
void updateView();
void updateModel();
void wrapChange();

View File

@ -115,6 +115,13 @@ void QQuickBusyIndicator::setRunning(bool running)
emit runningChanged();
}
#if QT_CONFIG(quicktemplates2_multitouch)
void QQuickBusyIndicator::touchEvent(QTouchEvent *event)
{
event->ignore(); // QTBUG-61785
}
#endif
#if QT_CONFIG(accessibility)
QAccessible::Role QQuickBusyIndicator::accessibleRole() const
{

View File

@ -69,6 +69,10 @@ Q_SIGNALS:
void runningChanged();
protected:
#if QT_CONFIG(quicktemplates2_multitouch)
void touchEvent(QTouchEvent *event) override;
#endif
#if QT_CONFIG(accessibility)
QAccessible::Role accessibleRole() const override;
#endif

View File

@ -1482,13 +1482,8 @@ void QQuickControl::touchEvent(QTouchEvent *event)
Q_D(QQuickControl);
switch (event->type()) {
case QEvent::TouchBegin:
for (const QTouchEvent::TouchPoint &point : event->touchPoints()) {
if (d->acceptTouch(point))
d->handlePress(point.pos());
}
break;
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
for (const QTouchEvent::TouchPoint &point : event->touchPoints()) {
if (!d->acceptTouch(point))
continue;
@ -1509,13 +1504,6 @@ void QQuickControl::touchEvent(QTouchEvent *event)
}
break;
case QEvent::TouchEnd:
for (const QTouchEvent::TouchPoint &point : event->touchPoints()) {
if (d->acceptTouch(point))
d->handleRelease(point.pos());
}
break;
case QEvent::TouchCancel:
d->handleUngrab();
break;

View File

@ -40,6 +40,7 @@
#include <QtGui/qstylehints.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtQml/qqmlinfo.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickanimation_p.h>
#include <QtQuick/private/qquicktransition_p.h>
@ -540,20 +541,32 @@ bool QQuickDrawerPrivate::prepareExitTransition()
return QQuickPopupPrivate::prepareExitTransition();
}
void QQuickDrawerPrivate::setEdge(Qt::Edge e)
bool QQuickDrawerPrivate::setEdge(Qt::Edge e)
{
edge = e;
if (edge == Qt::LeftEdge || edge == Qt::RightEdge) {
Q_Q(QQuickDrawer);
switch (e) {
case Qt::LeftEdge:
case Qt::RightEdge:
allowVerticalMove = true;
allowVerticalResize = true;
allowHorizontalMove = false;
allowHorizontalResize = false;
} else {
break;
case Qt::TopEdge:
case Qt::BottomEdge:
allowVerticalMove = false;
allowVerticalResize = false;
allowHorizontalMove = true;
allowHorizontalResize = true;
break;
default:
qmlWarning(q) << "invalid edge value - valid values are: "
<< "Qt.TopEdge, Qt.LeftEdge, Qt.RightEdge, Qt.BottomEdge";
return false;
}
edge = e;
return true;
}
QQuickDrawer::QQuickDrawer(QObject *parent)
@ -588,7 +601,9 @@ void QQuickDrawer::setEdge(Qt::Edge edge)
if (d->edge == edge)
return;
d->setEdge(edge);
if (!d->setEdge(edge))
return;
if (isComponentComplete())
d->reposition();
emit edgeChanged();

View File

@ -88,7 +88,7 @@ public:
bool prepareEnterTransition() override;
bool prepareExitTransition() override;
void setEdge(Qt::Edge edge);
bool setEdge(Qt::Edge edge);
Qt::Edge edge;
qreal offset;

View File

@ -150,6 +150,11 @@ bool QQuickOverlayPrivate::startDrag(QEvent *event, const QPointF &pos)
return false;
}
static bool isTouchEvent(QEvent *event)
{
return event->type() == QEvent::TouchBegin || event->type() == QEvent::TouchUpdate || event->type() == QEvent::TouchEnd;
}
bool QQuickOverlayPrivate::handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target)
{
if (target) {
@ -158,7 +163,7 @@ bool QQuickOverlayPrivate::handlePress(QQuickItem *source, QEvent *event, QQuick
return true;
}
return false;
} else if (!mouseGrabberPopup) {
} else if (!mouseGrabberPopup || isTouchEvent(event)) {
// allow non-modal popups to close themselves,
// and non-dimming modal popups to block the event
const auto popups = stackingOrderPopups();

View File

@ -98,11 +98,16 @@ public:
{
}
QQuickItem *itemAt(const QPoint &pos) const;
void updatePressed(bool pressed, const QPoint &pos = QPoint());
void handlePress(const QPointF &point) override;
void handleMove(const QPointF &point) override;
void handleRelease(const QPointF &point) override;
void handleUngrab() override;
QQuickItem *itemAt(const QPointF &pos) const;
void updatePressed(bool pressed, const QPointF &pos = QPointF());
void setContextProperty(QQuickItem *item, const QString &name, const QVariant &value);
void itemChildAdded(QQuickItem *, QQuickItem *child);
void itemChildAdded(QQuickItem *, QQuickItem *child) override;
int count;
int currentIndex;
@ -111,7 +116,39 @@ public:
QQuickItem *pressedItem;
};
QQuickItem *QQuickPageIndicatorPrivate::itemAt(const QPoint &pos) const
void QQuickPageIndicatorPrivate::handlePress(const QPointF &point)
{
QQuickControlPrivate::handlePress(point);
if (interactive)
updatePressed(true, point);
}
void QQuickPageIndicatorPrivate::handleMove(const QPointF &point)
{
QQuickControlPrivate::handleMove(point);
if (interactive)
updatePressed(true, point);
}
void QQuickPageIndicatorPrivate::handleRelease(const QPointF &point)
{
Q_Q(QQuickPageIndicator);
QQuickControlPrivate::handleRelease(point);
if (interactive) {
if (pressedItem && contentItem)
q->setCurrentIndex(contentItem->childItems().indexOf(pressedItem));
updatePressed(false);
}
}
void QQuickPageIndicatorPrivate::handleUngrab()
{
QQuickControlPrivate::handleUngrab();
if (interactive)
updatePressed(false);
}
QQuickItem *QQuickPageIndicatorPrivate::itemAt(const QPointF &pos) const
{
Q_Q(const QQuickPageIndicator);
if (!contentItem || !q->contains(pos))
@ -144,7 +181,7 @@ QQuickItem *QQuickPageIndicatorPrivate::itemAt(const QPoint &pos) const
return nearest;
}
void QQuickPageIndicatorPrivate::updatePressed(bool pressed, const QPoint &pos)
void QQuickPageIndicatorPrivate::updatePressed(bool pressed, const QPointF &pos)
{
QQuickItem *prevItem = pressedItem;
pressedItem = pressed ? itemAt(pos) : nullptr;
@ -298,41 +335,16 @@ void QQuickPageIndicator::contentItemChange(QQuickItem *newItem, QQuickItem *old
QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Children);
}
void QQuickPageIndicator::mousePressEvent(QMouseEvent *event)
{
Q_D(QQuickPageIndicator);
if (d->interactive) {
d->updatePressed(true, event->pos());
event->accept();
}
}
void QQuickPageIndicator::mouseMoveEvent(QMouseEvent *event)
{
Q_D(QQuickPageIndicator);
if (d->interactive) {
d->updatePressed(true, event->pos());
event->accept();
}
}
void QQuickPageIndicator::mouseReleaseEvent(QMouseEvent *event)
{
Q_D(QQuickPageIndicator);
if (d->interactive) {
if (d->pressedItem)
setCurrentIndex(d->contentItem->childItems().indexOf(d->pressedItem));
d->updatePressed(false);
event->accept();
}
}
void QQuickPageIndicator::mouseUngrabEvent()
#if QT_CONFIG(quicktemplates2_multitouch)
void QQuickPageIndicator::touchEvent(QTouchEvent *event)
{
Q_D(QQuickPageIndicator);
if (d->interactive)
d->updatePressed(false);
QQuickControl::touchEvent(event);
else
event->ignore(); // QTBUG-61785
}
#endif
#if QT_CONFIG(accessibility)
QAccessible::Role QQuickPageIndicator::accessibleRole() const

View File

@ -87,10 +87,9 @@ Q_SIGNALS:
protected:
void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseUngrabEvent() override;
#if QT_CONFIG(quicktemplates2_multitouch)
void touchEvent(QTouchEvent *event) override;
#endif
#if QT_CONFIG(accessibility)
QAccessible::Role accessibleRole() const override;

View File

@ -384,15 +384,10 @@ bool QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event)
{
switch (event->type()) {
case QEvent::TouchBegin:
for (const QTouchEvent::TouchPoint &point : event->touchPoints()) {
if (acceptTouch(point))
return handlePress(item, item->mapToScene(point.pos()), event->timestamp());
}
break;
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
for (const QTouchEvent::TouchPoint &point : event->touchPoints()) {
if (!acceptTouch(point))
if (!acceptTouch(point) && !blockInput(item, point.pos()))
continue;
switch (point.state()) {
@ -408,13 +403,6 @@ bool QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event)
}
break;
case QEvent::TouchEnd:
for (const QTouchEvent::TouchPoint &point : event->touchPoints()) {
if (acceptTouch(point))
return handleRelease(item, item->mapToScene(point.pos()), event->timestamp());
}
break;
case QEvent::TouchCancel:
handleUngrab();
break;
@ -493,7 +481,13 @@ void QQuickPopupPrivate::finalizeExitTransition()
destroyOverlay();
if (hadActiveFocusBeforeExitTransition && window) {
if (!qobject_cast<QQuickPopupItem *>(window->activeFocusItem())) {
// restore focus to the next popup in chain, or to the window content if there are no other popups open
QQuickPopup *popup = nullptr;
if (QQuickOverlay *overlay = QQuickOverlay::overlay(window))
popup = QQuickOverlayPrivate::get(overlay)->stackingOrderPopups().value(0);
if (popup && popup->hasFocus()) {
popup->forceActiveFocus();
} else {
QQuickApplicationWindow *applicationWindow = qobject_cast<QQuickApplicationWindow*>(window);
if (applicationWindow)
applicationWindow->contentItem()->setFocus(true);

View File

@ -567,6 +567,13 @@ void QQuickScrollIndicatorAttached::setVertical(QQuickScrollIndicator *vertical)
emit verticalChanged();
}
#if QT_CONFIG(quicktemplates2_multitouch)
void QQuickScrollIndicator::touchEvent(QTouchEvent *event)
{
event->ignore(); // QTBUG-61785
}
#endif
#if QT_CONFIG(accessibility)
QAccessible::Role QQuickScrollIndicator::accessibleRole() const
{

View File

@ -94,6 +94,10 @@ Q_SIGNALS:
void orientationChanged();
protected:
#if QT_CONFIG(quicktemplates2_multitouch)
void touchEvent(QTouchEvent *event) override;
#endif
#if QT_CONFIG(accessibility)
QAccessible::Role accessibleRole() const override;
#endif

View File

@ -97,7 +97,8 @@ QQuickTumblerPrivate::QQuickTumblerPrivate()
currentIndex(-1),
pendingCurrentIndex(-1),
ignoreCurrentIndexChanges(false),
count(0)
count(0),
ignoreSignals(false)
{
}
@ -163,6 +164,9 @@ QQuickTumblerPrivate *QQuickTumblerPrivate::get(QQuickTumbler *tumbler)
void QQuickTumblerPrivate::_q_updateItemHeights()
{
if (ignoreSignals)
return;
// Can't use our own private padding members here, as the padding property might be set,
// which doesn't affect them, only their getters.
Q_Q(const QQuickTumbler);
@ -174,6 +178,9 @@ void QQuickTumblerPrivate::_q_updateItemHeights()
void QQuickTumblerPrivate::_q_updateItemWidths()
{
if (ignoreSignals)
return;
Q_Q(const QQuickTumbler);
const qreal availableWidth = q->availableWidth();
const auto items = viewContentItemChildItems();
@ -195,6 +202,8 @@ void QQuickTumblerPrivate::_q_onViewCurrentIndexChanged()
void QQuickTumblerPrivate::_q_onViewCountChanged()
{
Q_Q(QQuickTumbler);
if (ignoreSignals)
return;
setCount(view->property("count").toInt());
@ -336,7 +345,9 @@ void QQuickTumbler::setCurrentIndex(int currentIndex)
couldSet = true;
} else {
d->ignoreCurrentIndexChanges = true;
d->ignoreSignals = true;
d->view->setProperty("currentIndex", currentIndex);
d->ignoreSignals = false;
d->ignoreCurrentIndexChanges = false;
couldSet = d->view->property("currentIndex").toInt() == currentIndex;

View File

@ -88,6 +88,7 @@ public:
int pendingCurrentIndex;
bool ignoreCurrentIndexChanges;
int count;
bool ignoreSignals;
void _q_updateItemHeights();
void _q_updateItemWidths();

View File

@ -57,9 +57,12 @@ ApplicationWindow {
visible: true
signal focusScopeKeyPressed
signal focusPopupKeyPressed
property alias fileMenu: fileMenu
property alias toolButton: toolButton
property alias focusScope: focusScope
property alias focusPopup: focusPopup
header: ToolBar {
ToolButton {
@ -92,5 +95,17 @@ ApplicationWindow {
Keys.onSpacePressed: focusScopeKeyPressed()
}
Popup {
id: focusPopup
focus: true
width: parent.width
height: parent.height
Item {
focus: true
Keys.onSpacePressed: focusPopupKeyPressed()
}
}
}

View File

@ -46,6 +46,8 @@
#include <QtQuickTemplates2/private/qquickoverlay_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
#include <QtQuickTemplates2/private/qquicklabel_p.h>
#include <QtQuickTemplates2/private/qquickmenu_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p.h>
#include <QtQuickTemplates2/private/qquicktextarea_p.h>
#include <QtQuickTemplates2/private/qquicktextfield_p.h>
#include <QtQuickControls2/private/qquickproxytheme_p.h>
@ -713,9 +715,9 @@ void tst_applicationwindow::focusAfterPopupClosed()
QVERIFY(focusScope);
QVERIFY(focusScope->hasActiveFocus());
QSignalSpy spy(window.data(), SIGNAL(focusScopeKeyPressed()));
QSignalSpy focusScopeSpy(window.data(), SIGNAL(focusScopeKeyPressed()));
QTest::keyClick(window.data(), Qt::Key_Space);
QCOMPARE(spy.count(), 1);
QCOMPARE(focusScopeSpy.count(), 1);
// Open the menu.
QQuickItem* toolButton = window->property("toolButton").value<QQuickItem*>();
@ -726,14 +728,48 @@ void tst_applicationwindow::focusAfterPopupClosed()
// The FocusScope shouldn't receive any key events while the menu is open.
QTest::keyClick(window.data(), Qt::Key_Space);
QCOMPARE(spy.count(), 1);
QCOMPARE(focusScopeSpy.count(), 1);
// Close the menu. The FocusScope should regain focus.
QTest::keyClick(window.data(), Qt::Key_Escape);
QVERIFY(focusScope->hasActiveFocus());
QTest::keyClick(window.data(), Qt::Key_Space);
QCOMPARE(spy.count(), 2);
QCOMPARE(focusScopeSpy.count(), 2);
QQuickPopup *focusPopup = window->property("focusPopup").value<QQuickPopup*>();
QVERIFY(focusPopup);
QVERIFY(!focusPopup->hasActiveFocus());
focusPopup->open();
QVERIFY(focusPopup->isVisible());
QSignalSpy focusPopupSpy(window.data(), SIGNAL(focusPopupKeyPressed()));
QTest::keyClick(window.data(), Qt::Key_Space);
QCOMPARE(focusPopupSpy.count(), 1);
QQuickMenu *fileMenu = window->property("fileMenu").value<QQuickMenu*>();
QVERIFY(fileMenu);
fileMenu->open();
QVERIFY(fileMenu->isVisible());
// The Popup shouldn't receive any key events while the menu is open.
QTest::keyClick(window.data(), Qt::Key_Space);
QCOMPARE(focusPopupSpy.count(), 1);
// Close the menu. The Popup should regain focus.
QTest::keyClick(window.data(), Qt::Key_Escape);
QVERIFY(focusPopup->hasActiveFocus());
QTest::keyClick(window.data(), Qt::Key_Space);
QCOMPARE(focusPopupSpy.count(), 2);
// Close the popup. The FocusScope should regain focus.
QTest::keyClick(window.data(), Qt::Key_Escape);
QVERIFY(focusScope->hasActiveFocus());
QTest::keyClick(window.data(), Qt::Key_Space);
QCOMPARE(focusScopeSpy.count(), 3);
}
void tst_applicationwindow::clearFocusOnDestruction()

View File

@ -87,6 +87,7 @@ Row {
id: yearTumbler
objectName: "yearTumbler"
model: ListModel {
objectName: "yearTumblerListModel"
Component.onCompleted: {
for (var i = 2000; i < 2100; ++i) {
append({value: i.toString()});

View File

@ -65,6 +65,11 @@ TestCase {
BusyIndicator { }
}
Component {
id: mouseArea
MouseArea { }
}
function test_running() {
var control = createTemporaryObject(busyIndicator, testCase)
verify(control)
@ -73,4 +78,26 @@ TestCase {
control.running = false
compare(control.running, false)
}
// QTBUG-61785
function test_mouseArea() {
var ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
verify(ma)
var control = busyIndicator.createObject(ma, {width: testCase.width, height: testCase.height})
verify(control)
mousePress(control)
verify(ma.pressed)
mouseRelease(control)
verify(!ma.pressed)
var touch = touchEvent(control)
touch.press(0, control).commit()
verify(ma.pressed)
touch.release(0, control).commit()
verify(!ma.pressed)
}
}

View File

@ -73,6 +73,16 @@ TestCase {
compare(control.parent, Overlay.overlay)
}
function test_invalidEdge() {
var control = createTemporaryObject(drawer, testCase)
compare(control.edge, Qt.LeftEdge)
// Test an invalid value - it should warn and ignore it.
ignoreWarning(Qt.resolvedUrl("tst_drawer.qml") + ":65:9: QML Drawer: invalid edge value - valid values are: Qt.TopEdge, Qt.LeftEdge, Qt.RightEdge, Qt.BottomEdge")
control.edge = Drawer.Right
compare(control.edge, Qt.LeftEdge)
}
Component {
id: rectDrawer

View File

@ -65,6 +65,11 @@ TestCase {
PageIndicator { }
}
Component {
id: mouseArea
MouseArea { }
}
function test_count() {
var control = createTemporaryObject(pageIndicator, testCase)
verify(control)
@ -83,20 +88,35 @@ TestCase {
compare(control.currentIndex, 5)
}
function test_interactive() {
function test_interactive_data() {
return [
{ tag: "mouse", touch: false },
{ tag: "touch", touch: true }
]
}
function test_interactive(data) {
var control = createTemporaryObject(pageIndicator, testCase, {count: 5, spacing: 10, padding: 10})
verify(control)
verify(!control.interactive)
compare(control.currentIndex, 0)
mouseClick(control, control.width / 2, control.height / 2, Qt.LeftButton)
var touch = touchEvent(control)
if (data.touch)
touch.press(0, control).commit().release(0, control).commit()
else
mouseClick(control, control.width / 2, control.height / 2, Qt.LeftButton)
compare(control.currentIndex, 0)
control.interactive = true
verify(control.interactive)
mouseClick(control, control.width / 2, control.height / 2, Qt.LeftButton)
if (data.touch)
touch.press(0, control).commit().release(0, control).commit()
else
mouseClick(control, control.width / 2, control.height / 2, Qt.LeftButton)
compare(control.currentIndex, 2)
// test also clicking outside delegates => the nearest should be selected
@ -108,10 +128,44 @@ TestCase {
compare(control.currentIndex, -1)
var pos = control.mapFromItem(child, x, y)
mouseClick(control, pos.x, pos.y, Qt.LeftButton)
if (data.touch)
touch.press(0, control, pos.x, pos.y).commit().release(0, control, pos.x, pos.y).commit()
else
mouseClick(control, pos.x, pos.y, Qt.LeftButton)
compare(control.currentIndex, i)
}
}
}
}
function test_mouseArea_data() {
return [
{ tag: "interactive", interactive: true },
{ tag: "non-interactive", interactive: false }
]
}
// QTBUG-61785
function test_mouseArea(data) {
var ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
verify(ma)
var control = pageIndicator.createObject(ma, {count: 5, interactive: data.interactive, width: testCase.width, height: testCase.height})
verify(control)
compare(control.interactive, data.interactive)
mousePress(control)
compare(ma.pressed, !data.interactive)
mouseRelease(control)
verify(!ma.pressed)
var touch = touchEvent(control)
touch.press(0, control).commit()
compare(ma.pressed, !data.interactive)
touch.release(0, control).commit()
verify(!ma.pressed)
}
}

View File

@ -65,6 +65,11 @@ TestCase {
ScrollIndicator { }
}
Component {
id: mouseArea
MouseArea { }
}
Component {
id: flickable
Flickable {
@ -231,4 +236,26 @@ TestCase {
compare(control.horizontal, true)
compare(control.vertical, false)
}
// QTBUG-61785
function test_mouseArea() {
var ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
verify(ma)
var control = scrollIndicator.createObject(ma, {active: true, size: 0.9, width: testCase.width, height: testCase.height})
verify(control)
mousePress(control)
verify(ma.pressed)
mouseRelease(control)
verify(!ma.pressed)
var touch = touchEvent(control)
touch.press(0, control).commit()
verify(ma.pressed)
touch.release(0, control).commit()
verify(!ma.pressed)
}
}

View File

@ -389,7 +389,7 @@ TestCase {
compare(tumbler.monthTumbler.currentIndex, 0);
compare(tumbler.monthTumbler.count, 12);
compare(tumbler.yearTumbler.currentIndex, 0);
compare(tumbler.yearTumbler.count, 100);
tryCompare(tumbler.yearTumbler, "count", 100);
verify(findView(tumbler.dayTumbler).children.length >= tumbler.dayTumbler.visibleItemCount);
verify(findView(tumbler.monthTumbler).children.length >= tumbler.monthTumbler.visibleItemCount);
@ -1057,4 +1057,41 @@ TestCase {
compare(tumbler.moving, true)
tryCompare(tumbler, "moving", false)
}
Component {
id: qtbug61374Component
Row {
property alias tumbler: tumbler
property alias label: label
Component.onCompleted: {
tumbler.currentIndex = 2
}
Tumbler {
id: tumbler
model: 5
// ...
}
Label {
id: label
text: tumbler.currentItem.text
}
}
}
function test_qtbug61374() {
var row = createTemporaryObject(qtbug61374Component, testCase);
verify(row);
var tumbler = row.tumbler;
tryCompare(tumbler, "currentIndex", 2);
tumblerView = findView(tumbler);
var label = row.label;
compare(label.text, "2");
}
}

View File

@ -307,6 +307,40 @@ void tst_popup::overlay()
QVERIFY(!popup->isVisible());
QCOMPARE(overlay->isVisible(), popup->isVisible());
// multi-touch
popup->open();
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QVERIFY(!button->isPressed());
QTest::touchEvent(window, device.data()).press(0, button->mapToScene(QPointF(1, 1)).toPoint());
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QCOMPARE(button->isPressed(), !modal);
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
QTest::touchEvent(window, device.data()).stationary(0).press(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QCOMPARE(button->isPressed(), !modal);
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
QTest::touchEvent(window, device.data()).release(0, button->mapToScene(QPointF(1, 1)).toPoint()).stationary(1);
QVERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
QVERIFY(!button->isPressed());
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), ++overlayReleaseCount);
QTest::touchEvent(window, device.data()).release(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
QVERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
QVERIFY(!button->isPressed());
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
}
void tst_popup::zOrder_data()