diff options
author | J-P Nurmi <jpnurmi@qt.io> | 2017-04-27 21:51:56 +0300 |
---|---|---|
committer | J-P Nurmi <jpnurmi@qt.io> | 2017-04-27 21:52:01 +0300 |
commit | ea470e8d612e5aa8c12eb3393baf057b8e8149e4 (patch) | |
tree | b5b77618de709c2cd818970b42f138d8e4b7c390 | |
parent | 806b1f7c4ed218d4df67428277f06bdbb1c71e0e (diff) | |
parent | f9552a96a7c6f8ea58d8ea90cf3027cc0636bf64 (diff) |
Merge remote-tracking branch 'origin/5.9' into dev
Change-Id: I47fbec425594386f529623f2dcbddd7ec7a09ee7
22 files changed, 700 insertions, 393 deletions
diff --git a/src/imports/controls/material/RoundButton.qml b/src/imports/controls/material/RoundButton.qml index c8d4f9a3..ac5949d9 100644 --- a/src/imports/controls/material/RoundButton.qml +++ b/src/imports/controls/material/RoundButton.qml @@ -59,7 +59,7 @@ T.RoundButton { icon.color: enabled ? undefined : Material.hintTextColor Material.elevation: flat ? control.down || control.hovered ? 2 : 0 - : control.down ? 8 : 2 + : control.down ? 12 : 6 Material.background: flat ? "transparent" : undefined contentItem: IconLabel { diff --git a/src/quicktemplates2/qquickcontainer.cpp b/src/quicktemplates2/qquickcontainer.cpp index 6777fa5d..2bc37e9c 100644 --- a/src/quicktemplates2/qquickcontainer.cpp +++ b/src/quicktemplates2/qquickcontainer.cpp @@ -96,6 +96,54 @@ QT_BEGIN_NAMESPACE } \endcode + \section2 Managing the Current Index + + When using multiple containers, such as \l TabBar and \l SwipeView, together, + their \l currentIndex properties can be bound to each other to keep them in + sync. When the user interacts with either container, its current index changes + automatically propagate to the other container. + + Notice, however, that assigning a \c currentIndex value in JavaScript removes + the respective binding. In order to retain the bindings, use the following + methods to alter the current index: + + \list + \li \l incrementCurrentIndex() + \li \l decrementCurrentIndex() + \li \l setCurrentIndex(int index) + \endlist + + \code + TabBar { + id: tabBar + currentIndex: swipeView.currentIndex + } + + SwipeView { + id: swipeView + currentIndex: tabBar.currentIndex + } + + Button { + text: qsTr("Home") + onClicked: swipeView.setCurrentIndex(0) + enabled: swipeView.currentIndex != 0 + } + + Button { + text: qsTr("Previous") + onClicked: swipeView.decrementCurrentIndex() + enabled: swipeView.currentIndex > 0 + } + + Button { + text: qsTr("Next") + onClicked: swipeView.incrementCurrentIndex() + enabled: swipeView.currentIndex < swipeView.count - 1 + } + \endcode + + \section2 Implementing Containers Container does not provide any default visualization. It is used to implement @@ -551,7 +599,7 @@ QQmlListProperty<QQuickItem> QQuickContainer::contentChildren() This property holds the index of the current item. - \sa currentItem, incrementCurrentIndex(), decrementCurrentIndex() + \sa currentItem, {Managing the Current Index} */ int QQuickContainer::currentIndex() const { @@ -559,6 +607,16 @@ int QQuickContainer::currentIndex() const return d->currentIndex; } +/*! + \qmlmethod void QtQuick.Controls::Container::setCurrentIndex(int index) + + Sets the current index of the container. + + This method can be called to set a specific current index without breaking + existing \c currentIndex bindings. + + \sa currentIndex, {Managing the Current Index} +*/ void QQuickContainer::setCurrentIndex(int index) { Q_D(QQuickContainer); @@ -576,7 +634,10 @@ void QQuickContainer::setCurrentIndex(int index) Increments the current index of the container. - \sa currentIndex + This method can be called to alter the current index without breaking + existing \c currentIndex bindings. + + \sa currentIndex, {Managing the Current Index} */ void QQuickContainer::incrementCurrentIndex() { @@ -591,7 +652,10 @@ void QQuickContainer::incrementCurrentIndex() Decrements the current index of the container. - \sa currentIndex + This method can be called to alter the current index without breaking + existing \c currentIndex bindings. + + \sa currentIndex, {Managing the Current Index} */ void QQuickContainer::decrementCurrentIndex() { diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 0c53093b..76a09d11 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -234,35 +234,17 @@ void QQuickDrawerPrivate::resizeOverlay() dimmer->setSize(geometry.size()); } -static bool mouseDragOverThreshold(QQuickDrawer *drawer, QMouseEvent *event) +static bool isWithinDragMargin(QQuickDrawer *drawer, const QPointF &pos) { switch (drawer->edge()) { case Qt::LeftEdge: - return !QQuickWindowPrivate::dragOverThreshold(event->windowPos().x(), Qt::XAxis, event, drawer->dragMargin()); + return pos.x() <= drawer->dragMargin(); case Qt::RightEdge: - return !QQuickWindowPrivate::dragOverThreshold(drawer->window()->width() - event->windowPos().x(), Qt::XAxis, event, drawer->dragMargin()); + return pos.x() >= drawer->window()->width() - drawer->dragMargin(); case Qt::TopEdge: - return !QQuickWindowPrivate::dragOverThreshold(event->windowPos().y(), Qt::YAxis, event, drawer->dragMargin()); + return pos.y() <= drawer->dragMargin(); case Qt::BottomEdge: - return !QQuickWindowPrivate::dragOverThreshold(drawer->window()->height() - event->windowPos().y(), Qt::YAxis, event, drawer->dragMargin()); - default: - Q_UNREACHABLE(); - break; - } - return false; -} - -static bool touchDragOverThreshold(QQuickDrawer *drawer, const QTouchEvent::TouchPoint &point) -{ - switch (drawer->edge()) { - case Qt::LeftEdge: - return !QQuickWindowPrivate::dragOverThreshold(point.scenePos().x(), Qt::XAxis, &point, drawer->dragMargin()); - case Qt::RightEdge: - return !QQuickWindowPrivate::dragOverThreshold(drawer->window()->width() - point.scenePos().x(), Qt::XAxis, &point, drawer->dragMargin()); - case Qt::TopEdge: - return !QQuickWindowPrivate::dragOverThreshold(point.scenePos().y(), Qt::YAxis, &point, drawer->dragMargin()); - case Qt::BottomEdge: - return !QQuickWindowPrivate::dragOverThreshold(drawer->window()->height() - point.scenePos().y(), Qt::YAxis, &point, drawer->dragMargin()); + return pos.y() >= drawer->window()->height() - drawer->dragMargin(); default: Q_UNREACHABLE(); break; @@ -276,33 +258,38 @@ bool QQuickDrawerPrivate::startDrag(QEvent *event) if (!window || !interactive || dragMargin < 0.0 || qFuzzyIsNull(dragMargin)) return false; - bool overThreshold = false; - bool mouse = event->type() == QEvent::MouseButtonPress; - if (mouse) { - overThreshold = mouseDragOverThreshold(q, static_cast<QMouseEvent *>(event)); - } else { + switch (event->type()) { + case QEvent::MouseButtonPress: + if (isWithinDragMargin(q, static_cast<QMouseEvent *>(event)->windowPos())) { + prepareEnterTransition(); + reposition(); + return handleMouseEvent(window->contentItem(), static_cast<QMouseEvent *>(event)); + } + break; + + case QEvent::TouchBegin: + case QEvent::TouchUpdate: for (const QTouchEvent::TouchPoint &point : static_cast<QTouchEvent *>(event)->touchPoints()) { - if (touchDragOverThreshold(q, point)) { - overThreshold = true; - break; + if (point.state() == Qt::TouchPointPressed && isWithinDragMargin(q, point.scenePos())) { + prepareEnterTransition(); + reposition(); + return handleTouchEvent(window->contentItem(), static_cast<QTouchEvent *>(event)); } } - } - if (!overThreshold) - return false; + break; - prepareEnterTransition(); - reposition(); - if (mouse) { - handleMousePressEvent(window->contentItem(), static_cast<QMouseEvent *>(event)); - return true; + default: + break; } + return false; } -bool QQuickDrawerPrivate::grabMouse(QMouseEvent *event) +bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event) { Q_Q(QQuickDrawer); + handleMouseEvent(item, event); + if (!window || !interactive || popupItem->keepMouseGrab()) return false; @@ -328,125 +315,174 @@ bool QQuickDrawerPrivate::grabMouse(QMouseEvent *event) overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin; } + if (overThreshold) { + QQuickItem *grabber = window->mouseGrabberItem(); + if (!grabber || !grabber->keepMouseGrab()) { + popupItem->grabMouse(); + popupItem->setKeepMouseGrab(true); + offset = positionAt(movePoint) - position; + + // don't jump when dragged open + if (offset > 0 && position > 0 && !popupItem->contains(popupItem->mapFromScene(movePoint))) + offset = 0; + } + } + return overThreshold; } -static const qreal openCloseVelocityThreshold = 300; - -bool QQuickDrawerPrivate::ungrabMouse(QMouseEvent *event) +bool QQuickDrawerPrivate::grabTouch(QQuickItem *item, QTouchEvent *event) { - bool wasGrabbed = popupItem->keepMouseGrab(); - if (wasGrabbed) { - const QPointF releasePoint = event->windowPos(); - velocityCalculator.stopMeasuring(releasePoint, event->timestamp()); + Q_Q(QQuickDrawer); + handleTouchEvent(item, event); - qreal velocity = 0; - if (edge == Qt::LeftEdge || edge == Qt::RightEdge) - velocity = velocityCalculator.velocity().x(); - else - velocity = velocityCalculator.velocity().y(); - - // the velocity is calculated so that swipes from left to right - // and top to bottom have positive velocity, and swipes from right - // to left and bottom to top have negative velocity. - // - // - top/left edge: positive velocity opens, negative velocity closes - // - bottom/right edge: negative velocity opens, positive velocity closes - // - // => invert the velocity for bottom and right edges, for the threshold comparison below - if (edge == Qt::RightEdge || edge == Qt::BottomEdge) - velocity = -velocity; - - if (position > 0.7 || velocity > openCloseVelocityThreshold) { - transitionManager.transitionEnter(); - } else if (position < 0.3 || velocity < -openCloseVelocityThreshold) { - transitionManager.transitionExit(); - } else { - switch (edge) { - case Qt::LeftEdge: - if (releasePoint.x() - pressPoint.x() > 0) - transitionManager.transitionEnter(); - else - transitionManager.transitionExit(); - break; - case Qt::RightEdge: - if (releasePoint.x() - pressPoint.x() < 0) - transitionManager.transitionEnter(); - else - transitionManager.transitionExit(); - break; - case Qt::TopEdge: - if (releasePoint.y() - pressPoint.y() > 0) - transitionManager.transitionEnter(); - else - transitionManager.transitionExit(); - break; - case Qt::BottomEdge: - if (releasePoint.y() - pressPoint.y() < 0) - transitionManager.transitionEnter(); - else - transitionManager.transitionExit(); - break; - } + if (!window || !interactive || popupItem->keepTouchGrab() || !event->touchPointStates().testFlag(Qt::TouchPointMoved)) + return false; + + bool overThreshold = false; + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (!acceptTouch(point) || point.state() != Qt::TouchPointMoved) + continue; + + const QPointF movePoint = point.scenePos(); + + // Flickable uses a hard-coded threshold of 15 for flicking, and + // QStyleHints::startDragDistance for dragging. Drawer uses a bit + // larger threshold to avoid being too eager to steal touch (QTBUG-50045) + const int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5); + if (position > 0 || dragMargin > 0) { + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, &point, threshold); + else + overThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, &point, threshold); + } + + // Don't be too eager to steal presses outside the drawer (QTBUG-53929) + if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !popupItem->contains(popupItem->mapFromScene(movePoint))) { + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin; + else + overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin; + } + + if (overThreshold) { + popupItem->setKeepTouchGrab(true); + offset = positionAt(movePoint) - position; + + // don't jump when dragged open + if (offset > 0 && position > 0 && !popupItem->contains(popupItem->mapFromScene(movePoint))) + offset = 0; } } - return wasGrabbed; + + return overThreshold; } -bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) +static const qreal openCloseVelocityThreshold = 300; + +bool QQuickDrawerPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp) { + if (!QQuickPopupPrivate::handlePress(item, point, timestamp)) + return false; + offset = 0; - pressPoint = event->windowPos(); - velocityCalculator.startMeasuring(pressPoint, event->timestamp()); - - // don't block press events - // a) outside a non-modal drawer, - // b) to drawer children, or - // c) outside a modal drawer's background dimming - event->setAccepted(modal && !popupItem->isAncestorOf(item) && (!dimmer || dimmer->contains(dimmer->mapFromScene(pressPoint)))); - return event->isAccepted(); + pressPoint = point; + velocityCalculator.startMeasuring(point, timestamp); + + return true; } -bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) +bool QQuickDrawerPrivate::handleMove(QQuickItem *item, const QPointF &point, ulong timestamp) { Q_Q(QQuickDrawer); - Q_UNUSED(item); + if (!QQuickPopupPrivate::handleMove(item, point, timestamp)) + return false; - // Don't react to synthesized mouse move events at INF,INF coordinates. - // QQuickWindowPrivate::translateTouchToMouse() uses them to clear hover - // on touch release (QTBUG-55995). - if (qIsInf(event->screenPos().x()) || qIsInf(event->screenPos().y())) - return true; + // limit/reset the offset to the edge of the drawer when pushed from the outside + if (qFuzzyCompare(position, 1.0) && !popupItem->contains(popupItem->mapFromScene(point))) + offset = 0; - const QPointF movePoint = event->windowPos(); + bool isGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab(); + if (isGrabbed) + q->setPosition(positionAt(point) - offset); - if (grabMouse(event)) { - QQuickItem *grabber = window->mouseGrabberItem(); - if (!grabber || !grabber->keepMouseGrab()) { - popupItem->grabMouse(); - popupItem->setKeepMouseGrab(true); - offset = qMin<qreal>(0.0, positionAt(movePoint) - position); + return isGrabbed; +} + +bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point, ulong timestamp) +{ + pressPoint = QPointF(); + + if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab()) { + velocityCalculator.reset(); + return QQuickPopupPrivate::handleRelease(item, point, timestamp); + } + + velocityCalculator.stopMeasuring(point, timestamp); + + qreal velocity = 0; + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + velocity = velocityCalculator.velocity().x(); + else + velocity = velocityCalculator.velocity().y(); + + // the velocity is calculated so that swipes from left to right + // and top to bottom have positive velocity, and swipes from right + // to left and bottom to top have negative velocity. + // + // - top/left edge: positive velocity opens, negative velocity closes + // - bottom/right edge: negative velocity opens, positive velocity closes + // + // => invert the velocity for bottom and right edges, for the threshold comparison below + if (edge == Qt::RightEdge || edge == Qt::BottomEdge) + velocity = -velocity; + + if (position > 0.7 || velocity > openCloseVelocityThreshold) { + transitionManager.transitionEnter(); + } else if (position < 0.3 || velocity < -openCloseVelocityThreshold) { + transitionManager.transitionExit(); + } else { + switch (edge) { + case Qt::LeftEdge: + if (point.x() - pressPoint.x() > 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::RightEdge: + if (point.x() - pressPoint.x() < 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::TopEdge: + if (point.y() - pressPoint.y() > 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::BottomEdge: + if (point.y() - pressPoint.y() < 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; } } - if (popupItem->keepMouseGrab()) - q->setPosition(positionAt(movePoint) - offset); - event->accept(); + bool wasGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab(); + popupItem->setKeepMouseGrab(false); + popupItem->setKeepTouchGrab(false); - return popupItem->keepMouseGrab(); + return wasGrabbed; } -bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event) +void QQuickDrawerPrivate::handleUngrab() { - Q_UNUSED(item); + QQuickPopupPrivate::handleUngrab(); - const bool wasGrabbed = ungrabMouse(event); - - popupItem->setKeepMouseGrab(false); pressPoint = QPoint(); - event->accept(); - - return wasGrabbed; + velocityCalculator.reset(); } static QList<QQuickStateAction> prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to) @@ -629,69 +665,43 @@ bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) { Q_D(QQuickDrawer); switch (event->type()) { - case QEvent::MouseButtonPress: - return d->handleMousePressEvent(child, static_cast<QMouseEvent *>(event)); + case QEvent::TouchUpdate: + return d->grabTouch(child, static_cast<QTouchEvent *>(event)); case QEvent::MouseMove: - return d->handleMouseMoveEvent(child, static_cast<QMouseEvent *>(event)); + return d->grabMouse(child, static_cast<QMouseEvent *>(event)); + case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: - return d->handleMouseReleaseEvent(child, static_cast<QMouseEvent *>(event)); + return d->handleMouseEvent(child, static_cast<QMouseEvent *>(event)); default: - return false; + break; } -} - -void QQuickDrawer::mousePressEvent(QMouseEvent *event) -{ - Q_D(QQuickDrawer); - QQuickPopup::mousePressEvent(event); - d->handleMousePressEvent(d->popupItem, event); + return false; } void QQuickDrawer::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickDrawer); - QQuickPopup::mouseMoveEvent(event); - d->handleMouseMoveEvent(d->popupItem, event); -} - -void QQuickDrawer::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QQuickDrawer); - QQuickPopup::mouseReleaseEvent(event); - d->handleMouseReleaseEvent(d->popupItem, event); -} - -void QQuickDrawer::mouseUngrabEvent() -{ - Q_D(QQuickDrawer); - QQuickPopup::mouseUngrabEvent(); - d->pressPoint = QPoint(); - d->velocityCalculator.reset(); + d->grabMouse(d->popupItem, event); } bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) { Q_D(QQuickDrawer); switch (event->type()) { - case QEvent::TouchBegin: case QEvent::TouchUpdate: - case QEvent::TouchEnd: - case QEvent::TouchCancel: - // TODO: QQuickDrawer still relies on synthesized mouse events - event->ignore(); - return false; - - case QEvent::MouseButtonPress: - d->tryClose(item, event); - return d->handleMousePressEvent(item, static_cast<QMouseEvent *>(event)); + return d->grabTouch(item, static_cast<QTouchEvent *>(event)); case QEvent::MouseMove: - return d->handleMouseMoveEvent(item, static_cast<QMouseEvent *>(event)); - case QEvent::MouseButtonRelease: - d->tryClose(item, event); - return d->handleMouseReleaseEvent(item, static_cast<QMouseEvent *>(event)); + return d->grabMouse(item, static_cast<QMouseEvent *>(event)); default: - return QQuickPopup::overlayEvent(item, event); + break; } + return QQuickPopup::overlayEvent(item, event); +} + +void QQuickDrawer::touchEvent(QTouchEvent *event) +{ + Q_D(QQuickDrawer); + d->grabTouch(d->popupItem, event); } void QQuickDrawer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index 68b58362..9ab1c4ef 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -86,11 +86,9 @@ Q_SIGNALS: protected: bool childMouseEventFilter(QQuickItem *child, QEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseUngrabEvent() override; bool overlayEvent(QQuickItem *item, QEvent *event) override; + void touchEvent(QTouchEvent *event) override; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index c2c5beaf..dba3032e 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -71,12 +71,13 @@ public: void resizeOverlay() override; bool startDrag(QEvent *event); - bool grabMouse(QMouseEvent *event); - bool ungrabMouse(QMouseEvent *event); + bool grabMouse(QQuickItem *item, QMouseEvent *event); + bool grabTouch(QQuickItem *item, QTouchEvent *event); - bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); - bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); - bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event); + bool handlePress(QQuickItem* item, const QPointF &point, ulong timestamp) override; + bool handleMove(QQuickItem* item, const QPointF &point, ulong timestamp) override; + bool handleRelease(QQuickItem* item, const QPointF &point, ulong timestamp) override; + void handleUngrab() override; bool prepareEnterTransition() override; bool prepareExitTransition() override; diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index 58ec5582..308eda86 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -181,57 +181,136 @@ QQuickOverlayPrivate::QQuickOverlayPrivate() { } -void QQuickOverlayPrivate::handlePress(QEvent *event) +bool QQuickOverlayPrivate::startDrag(QEvent *event) +{ + if (allDrawers.isEmpty()) + return false; + + const QVector<QQuickDrawer *> drawers = stackingOrderDrawers(); + for (QQuickDrawer *drawer : drawers) { + QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer); + if (p->startDrag(event)) { + setMouseGrabberPopup(drawer); + return true; + } + } + + return false; +} + +bool QQuickOverlayPrivate::handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target) { Q_Q(QQuickOverlay); emit q->pressed(); - if (!allDrawers.isEmpty()) { - // the overlay background was pressed, so there are no modal popups open. - // test if the press point lands on any drawer's drag margin - - const QVector<QQuickDrawer *> drawers = stackingOrderDrawers(); - for (QQuickDrawer *drawer : drawers) { - QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer); - if (p->startDrag(event)) { - setMouseGrabberPopup(drawer); - return; - } + if (target) { + if (target->overlayEvent(source, event)) { + setMouseGrabberPopup(target); + return true; } - } - - if (!mouseGrabberPopup) { - // allow non-modal popups to close themselves + return false; + } else if (!mouseGrabberPopup) { + // allow non-modal popups to close themselves, + // and non-dimming modal popups to block the event const auto popups = stackingOrderPopups(); - for (QQuickPopup *popup : popups) - popup->overlayEvent(q, event); + for (QQuickPopup *popup : popups) { + if (popup->overlayEvent(source, event)) { + setMouseGrabberPopup(popup); + return true; + } + } } event->ignore(); + return false; } -void QQuickOverlayPrivate::handleMove(QEvent *event) +bool QQuickOverlayPrivate::handleMove(QQuickItem *source, QEvent *event, QQuickPopup *target) { - Q_Q(QQuickOverlay); - if (mouseGrabberPopup) - mouseGrabberPopup->overlayEvent(q, event); + if (target) + return target->overlayEvent(source, event); + return false; } -void QQuickOverlayPrivate::handleRelease(QEvent *event) +bool QQuickOverlayPrivate::handleRelease(QQuickItem *source, QEvent *event, QQuickPopup *target) { Q_Q(QQuickOverlay); emit q->released(); - if (mouseGrabberPopup) { - mouseGrabberPopup->overlayEvent(q, event); + if (target) { setMouseGrabberPopup(nullptr); + if (target->overlayEvent(source, event)) { + setMouseGrabberPopup(nullptr); + return true; + } } else { const auto popups = stackingOrderPopups(); for (QQuickPopup *popup : popups) { - if (popup->overlayEvent(q, event)) + if (popup->overlayEvent(source, event)) + return true; + } + } + return false; +} + +bool QQuickOverlayPrivate::handleMouseEvent(QQuickItem *source, QMouseEvent *event, QQuickPopup *target) +{ + switch (event->type()) { + case QEvent::MouseButtonPress: + if (!target && startDrag(event)) + return true; + return handlePress(source, event, target); + case QEvent::MouseMove: + return handleMove(source, event, target ? target : mouseGrabberPopup.data()); + case QEvent::MouseButtonRelease: + return handleRelease(source, event, target ? target : mouseGrabberPopup.data()); + default: + break; + } + return false; +} + +bool QQuickOverlayPrivate::handleTouchEvent(QQuickItem *source, QTouchEvent *event, QQuickPopup *target) +{ + bool handled = false; + switch (event->type()) { + case QEvent::TouchBegin: + if (!target && startDrag(event)) + handled = true; + else + handled = handlePress(source, event, target); + break; + + case QEvent::TouchUpdate: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + switch (point.state()) { + case Qt::TouchPointPressed: + if (!target && startDrag(event)) + handled = true; + else + handled |= handlePress(source, event, target); + break; + case Qt::TouchPointMoved: + handled |= handleMove(source, event, target ? target : mouseGrabberPopup.data()); + break; + case Qt::TouchPointReleased: + handled |= handleRelease(source, event, target ? target : mouseGrabberPopup.data()); break; + default: + break; + } } + break; + + case QEvent::TouchEnd: + handled = handleRelease(source, event, target ? target : mouseGrabberPopup.data()); + break; + + default: + break; } + + return handled; } void QQuickOverlayPrivate::addPopup(QQuickPopup *popup) @@ -382,55 +461,25 @@ void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &old void QQuickOverlay::mousePressEvent(QMouseEvent *event) { Q_D(QQuickOverlay); - d->handlePress(event); + d->handleMouseEvent(this, event); } void QQuickOverlay::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickOverlay); - d->handleMove(event); + d->handleMouseEvent(this, event); } void QQuickOverlay::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickOverlay); - d->handleRelease(event); + d->handleMouseEvent(this, event); } void QQuickOverlay::touchEvent(QTouchEvent *event) { Q_D(QQuickOverlay); - switch (event->type()) { - case QEvent::TouchBegin: - d->handlePress(event); - break; - - case QEvent::TouchUpdate: - for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { - switch (point.state()) { - case Qt::TouchPointPressed: - d->handlePress(event); - break; - case Qt::TouchPointMoved: - d->handleMove(event); - break; - case Qt::TouchPointReleased: - d->handleRelease(event); - break; - default: - break; - } - } - break; - - case QEvent::TouchEnd: - d->handleRelease(event); - break; - - default: - QQuickItem::touchEvent(event); - break; - } + d->handleTouchEvent(this, event); } #if QT_CONFIG(wheelevent) @@ -468,19 +517,16 @@ bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) // does not have background dimming. if (item == p->dimmer || !p->popupItem->isAncestorOf(item)) { switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + return d->handleTouchEvent(item, static_cast<QTouchEvent *>(event), popup); + case QEvent::MouseButtonPress: - emit pressed(); - if (popup->overlayEvent(item, event)) { - d->setMouseGrabberPopup(popup); - return true; - } - break; case QEvent::MouseMove: - return popup->overlayEvent(item, event); case QEvent::MouseButtonRelease: - emit released(); - d->setMouseGrabberPopup(nullptr); - return popup->overlayEvent(item, event); + return d->handleMouseEvent(item, static_cast<QMouseEvent *>(event), popup); + default: break; } diff --git a/src/quicktemplates2/qquickoverlay_p_p.h b/src/quicktemplates2/qquickoverlay_p_p.h index c9d887bd..de8539c9 100644 --- a/src/quicktemplates2/qquickoverlay_p_p.h +++ b/src/quicktemplates2/qquickoverlay_p_p.h @@ -70,9 +70,13 @@ public: return overlay->d_func(); } - void handlePress(QEvent *event); - void handleMove(QEvent *event); - void handleRelease(QEvent *event); + bool startDrag(QEvent *event); + bool handlePress(QQuickItem *source, QEvent *event, QQuickPopup *target); + bool handleMove(QQuickItem *source, QEvent *event, QQuickPopup *target); + bool handleRelease(QQuickItem *source, QEvent *event, QQuickPopup *target); + + bool handleMouseEvent(QQuickItem *source, QMouseEvent *event, QQuickPopup *target = nullptr); + bool handleTouchEvent(QQuickItem *source, QTouchEvent *event, QQuickPopup *target = nullptr); void addPopup(QQuickPopup *popup); void removePopup(QQuickPopup *popup); diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index a0302417..775d4e6a 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -44,7 +44,6 @@ #include "qquickdialog_p.h" #include <QtQml/qqmlinfo.h> -#include <QtQml/private/qqmlnullablevalue_p.h> #include <QtQuick/qquickitem.h> #include <QtQuick/private/qquicktransition_p.h> #include <QtQuick/private/qquickitem_p.h> @@ -274,46 +273,19 @@ void QQuickPopupPrivate::closeOrReject() q->close(); } -bool QQuickPopupPrivate::tryClose(QQuickItem *item, QEvent *event) +bool QQuickPopupPrivate::tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags) { if (!interactive) return false; - bool isPress = false; - QQmlNullableValue<QPointF> pos; + static const QQuickPopup::ClosePolicy outsideFlags = QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnReleaseOutside; + static const QQuickPopup::ClosePolicy outsideParentFlags = QQuickPopup::CloseOnPressOutsideParent | QQuickPopup::CloseOnReleaseOutsideParent; - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - pos = static_cast<QMouseEvent *>(event)->pos(); - isPress = event->type() == QEvent::MouseButtonPress; - break; - - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - for (const QTouchEvent::TouchPoint &point : static_cast<QTouchEvent *>(event)->touchPoints()) { - if (point.state() == Qt::TouchPointMoved) - continue; - - pos = point.pos(); - isPress = point.state() == Qt::TouchPointPressed; - break; // TODO: multiple touch points - } - break; - - default: - break; - } - - if (pos.isNull) // ignore touch moves - return false; - - const bool onOutside = closePolicy.testFlag(isPress ? QQuickPopup::CloseOnPressOutside : QQuickPopup::CloseOnReleaseOutside); - const bool onOutsideParent = closePolicy.testFlag(isPress ? QQuickPopup::CloseOnPressOutsideParent : QQuickPopup::CloseOnReleaseOutsideParent); + const bool onOutside = closePolicy & (flags & outsideFlags); + const bool onOutsideParent = closePolicy & (flags & outsideParentFlags); if (onOutside || onOutsideParent) { - if (!popupItem->contains(item->mapToItem(popupItem, pos))) { - if (!onOutsideParent || !parentItem || !parentItem->contains(item->mapToItem(parentItem, pos))) { + if (!popupItem->contains(popupItem->mapFromScene(pos))) { + if (!onOutsideParent || !parentItem || !parentItem->contains(parentItem->mapFromScene(pos))) { closeOrReject(); return true; } @@ -335,17 +307,34 @@ bool QQuickPopupPrivate::acceptTouch(const QTouchEvent::TouchPoint &point) return false; } -void QQuickPopupPrivate::handlePress(const QPointF &) +bool QQuickPopupPrivate::blockInput(QQuickItem *item, const QPointF &point) const { + // don't block presses and releases + // a) outside a non-modal popup, + // b) to popup children/content, or + // b) outside a modal popups's background dimming + return modal && !popupItem->isAncestorOf(item) && (!dimmer || dimmer->contains(dimmer->mapFromScene(point))); } -void QQuickPopupPrivate::handleMove(const QPointF &) +bool QQuickPopupPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp) { + Q_UNUSED(timestamp); + tryClose(point, QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); + return blockInput(item, point); } -void QQuickPopupPrivate::handleRelease(const QPointF &) +bool QQuickPopupPrivate::handleMove(QQuickItem *item, const QPointF &point, ulong timestamp) { + Q_UNUSED(timestamp); + return blockInput(item, point); +} + +bool QQuickPopupPrivate::handleRelease(QQuickItem *item, const QPointF &point, ulong timestamp) +{ + Q_UNUSED(timestamp); + tryClose(point, QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); touchId = -1; + return blockInput(item, point); } void QQuickPopupPrivate::handleUngrab() @@ -360,6 +349,68 @@ void QQuickPopupPrivate::handleUngrab() touchId = -1; } +bool QQuickPopupPrivate::handleMouseEvent(QQuickItem *item, QMouseEvent *event) +{ + QPointF pos = item->mapToScene(event->localPos()); + switch (event->type()) { + case QEvent::MouseButtonPress: + return handlePress(item, pos, event->timestamp()); + case QEvent::MouseMove: + return handleMove(item, pos, event->timestamp()); + case QEvent::MouseButtonRelease: + return handleRelease(item, pos, event->timestamp()); + default: + Q_UNREACHABLE(); + return false; + } +} + +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: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (!acceptTouch(point)) + continue; + + switch (point.state()) { + case Qt::TouchPointPressed: + return handlePress(item, item->mapToScene(point.pos()), event->timestamp()); + case Qt::TouchPointMoved: + return handleMove(item, item->mapToScene(point.pos()), event->timestamp()); + case Qt::TouchPointReleased: + return handleRelease(item, item->mapToScene(point.pos()), event->timestamp()); + default: + break; + } + } + 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; + + default: + break; + } + + return false; +} + bool QQuickPopupPrivate::prepareEnterTransition() { Q_Q(QQuickPopup); @@ -1873,21 +1924,21 @@ void QQuickPopup::keyReleaseEvent(QKeyEvent *event) void QQuickPopup::mousePressEvent(QMouseEvent *event) { Q_D(QQuickPopup); - d->handlePress(event->localPos()); + d->handleMouseEvent(d->popupItem, event); event->accept(); } void QQuickPopup::mouseMoveEvent(QMouseEvent *event) { Q_D(QQuickPopup); - d->handleMove(event->localPos()); + d->handleMouseEvent(d->popupItem, event); event->accept(); } void QQuickPopup::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickPopup); - d->handleRelease(event->localPos()); + d->handleMouseEvent(d->popupItem, event); event->accept(); } @@ -1917,12 +1968,11 @@ bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event) case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: + return d->handleTouchEvent(item, static_cast<QTouchEvent *>(event)); + case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: - if (d->modal) - event->accept(); - d->tryClose(item, event); - return d->modal; + return d->handleMouseEvent(item, static_cast<QMouseEvent *>(event)); default: return false; @@ -1932,52 +1982,7 @@ bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event) void QQuickPopup::touchEvent(QTouchEvent *event) { Q_D(QQuickPopup); - 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: - for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { - if (!d->acceptTouch(point)) - continue; - - switch (point.state()) { - case Qt::TouchPointPressed: - d->handlePress(point.pos()); - break; - case Qt::TouchPointMoved: - d->handleMove(point.pos()); - break; - case Qt::TouchPointReleased: - d->handleRelease(point.pos()); - break; - default: - break; - } - } - 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; - - default: - break; - } - - // TODO: QQuickPopup still relies on synthesized mouse events - event->ignore(); + d->handleTouchEvent(d->popupItem, event); } void QQuickPopup::touchUngrabEvent() diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h index 51e9cf2f..476e691d 100644 --- a/src/quicktemplates2/qquickpopup_p_p.h +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -95,14 +95,19 @@ public: void init(); void closeOrReject(); - bool tryClose(QQuickItem *item, QEvent *event); + bool tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags); virtual bool acceptTouch(const QTouchEvent::TouchPoint &point); - virtual void handlePress(const QPointF &point); - virtual void handleMove(const QPointF &point); - virtual void handleRelease(const QPointF &point); + virtual bool blockInput(QQuickItem *item, const QPointF &point) const; + + virtual bool handlePress(QQuickItem* item, const QPointF &point, ulong timestamp); + virtual bool handleMove(QQuickItem* item, const QPointF &point, ulong timestamp); + virtual bool handleRelease(QQuickItem* item, const QPointF &point, ulong timestamp); virtual void handleUngrab(); + bool handleMouseEvent(QQuickItem *item, QMouseEvent *event); + bool handleTouchEvent(QQuickItem *item, QTouchEvent *event); + virtual void reposition(); virtual void resizeOverlay(); diff --git a/src/quicktemplates2/qquickrangeslider.cpp b/src/quicktemplates2/qquickrangeslider.cpp index 1468d358..f1e8a01e 100644 --- a/src/quicktemplates2/qquickrangeslider.cpp +++ b/src/quicktemplates2/qquickrangeslider.cpp @@ -435,16 +435,15 @@ void QQuickRangeSliderPrivate::handlePress(const QPointF &point) otherNode = first; } else { // find the nearest - const qreal firstDistance = QLineF(firstHandle->boundingRect().center(), - q->mapToItem(firstHandle, point)).length(); - const qreal secondDistance = QLineF(secondHandle->boundingRect().center(), - q->mapToItem(secondHandle, point)).length(); + const qreal firstPos = positionAt(q, firstHandle, point); + const qreal secondPos = positionAt(q, secondHandle, point); + const qreal firstDistance = qAbs(firstPos - first->position()); + const qreal secondDistance = qAbs(secondPos - second->position()); if (qFuzzyCompare(firstDistance, secondDistance)) { // same distance => choose the one that can be moved towards the press position const bool inverted = from > to; - const qreal pos = positionAt(q, firstHandle, point); - if ((!inverted && pos < first->position()) || (inverted && pos > first->position())) { + if ((!inverted && firstPos < first->position()) || (inverted && firstPos > first->position())) { hitNode = first; otherNode = second; } else { @@ -462,11 +461,14 @@ void QQuickRangeSliderPrivate::handlePress(const QPointF &point) if (hitNode) { hitNode->setPressed(true); - hitNode->handle()->setZ(1); + if (QQuickItem *handle = hitNode->handle()) + handle->setZ(1); QQuickRangeSliderNodePrivate::get(hitNode)->touchId = touchId; } - if (otherNode) - otherNode->handle()->setZ(0); + if (otherNode) { + if (QQuickItem *handle = otherNode->handle()) + handle->setZ(0); + } } void QQuickRangeSliderPrivate::handleMove(const QPointF &point) diff --git a/src/quicktemplates2/qquickspinbox.cpp b/src/quicktemplates2/qquickspinbox.cpp index b6da0c9f..07bf4121 100644 --- a/src/quicktemplates2/qquickspinbox.cpp +++ b/src/quicktemplates2/qquickspinbox.cpp @@ -766,6 +766,16 @@ void QQuickSpinBox::decrease() d->stepBy(-d->effectiveStepSize()); } +void QQuickSpinBox::focusInEvent(QFocusEvent *event) +{ + Q_D(QQuickSpinBox); + QQuickControl::focusInEvent(event); + + // When an editable SpinBox gets focus, it must pass on the focus to its editor. + if (d->editable && d->contentItem && !d->contentItem->hasActiveFocus()) + d->contentItem->forceActiveFocus(event->reason()); +} + void QQuickSpinBox::hoverEnterEvent(QHoverEvent *event) { Q_D(QQuickSpinBox); diff --git a/src/quicktemplates2/qquickspinbox_p.h b/src/quicktemplates2/qquickspinbox_p.h index d1b42497..27956d7c 100644 --- a/src/quicktemplates2/qquickspinbox_p.h +++ b/src/quicktemplates2/qquickspinbox_p.h @@ -132,6 +132,7 @@ Q_SIGNALS: Q_REVISION(3) void wrapChanged(); protected: + void focusInEvent(QFocusEvent *event) override; void hoverEnterEvent(QHoverEvent *event) override; void hoverMoveEvent(QHoverEvent *event) override; void hoverLeaveEvent(QHoverEvent *event) override; diff --git a/tests/auto/controls/data/tst_container.qml b/tests/auto/controls/data/tst_container.qml index 684f0931..049982fb 100644 --- a/tests/auto/controls/data/tst_container.qml +++ b/tests/auto/controls/data/tst_container.qml @@ -90,4 +90,40 @@ TestCase { compare(control.implicitWidth, 210) compare(control.implicitHeight, 220) } + + function test_currentIndex() { + var control1 = createTemporaryObject(container, testCase) + verify(control1) + + var control2 = createTemporaryObject(container, testCase) + verify(control2) + + compare(control1.currentIndex, -1) + compare(control2.currentIndex, -1) + + for (var i = 0; i < 3; ++i) { + control1.addItem(rectangle.createObject(control1)) + control2.addItem(rectangle.createObject(control2)) + } + + compare(control1.count, 3) + compare(control2.count, 3) + compare(control1.currentIndex, 0) + compare(control2.currentIndex, 0) + + control1.currentIndex = Qt.binding(function() { return control2.currentIndex }) + control2.currentIndex = Qt.binding(function() { return control1.currentIndex }) + + control1.setCurrentIndex(1) + compare(control1.currentIndex, 1) + compare(control2.currentIndex, 1) + + control1.incrementCurrentIndex() + compare(control1.currentIndex, 2) + compare(control2.currentIndex, 2) + + control2.decrementCurrentIndex() + compare(control1.currentIndex, 1) + compare(control2.currentIndex, 1) + } } diff --git a/tests/auto/controls/data/tst_dial.qml b/tests/auto/controls/data/tst_dial.qml index 3955e60d..33b0dbea 100644 --- a/tests/auto/controls/data/tst_dial.qml +++ b/tests/auto/controls/data/tst_dial.qml @@ -579,4 +579,17 @@ TestCase { compare(control.value, 2.5) compare(control.position, 0.25) } + + function test_nullHandle() { + var control = createTemporaryObject(dialComponent, testCase) + verify(control) + + control.handle = null + + mousePress(control) + verify(control.pressed, true) + + mouseRelease(control) + compare(control.pressed, false) + } } diff --git a/tests/auto/controls/data/tst_dialog.qml b/tests/auto/controls/data/tst_dialog.qml index 6b18a323..2f3d2a6b 100644 --- a/tests/auto/controls/data/tst_dialog.qml +++ b/tests/auto/controls/data/tst_dialog.qml @@ -87,8 +87,12 @@ TestCase { function test_accept() { var control = createTemporaryObject(dialog, testCase) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) verify(control.visible) var acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"}) @@ -102,8 +106,12 @@ TestCase { function test_reject() { var control = createTemporaryObject(dialog, testCase) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) verify(control.visible) var rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"}) @@ -226,8 +234,12 @@ TestCase { var control = createTemporaryObject(dialog, testCase, {width: 100, height: 100}) verify(control) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) verify(control.visible) compare(control.width, 100) @@ -315,8 +327,12 @@ TestCase { var control = createTemporaryObject(dialog, testCase, {spacing: 20, width: 100, height: 100}) verify(control) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) verify(control.visible) control.contentItem.visible = data.content diff --git a/tests/auto/controls/data/tst_popup.qml b/tests/auto/controls/data/tst_popup.qml index 24465db1..beae39d1 100644 --- a/tests/auto/controls/data/tst_popup.qml +++ b/tests/auto/controls/data/tst_popup.qml @@ -936,8 +936,13 @@ TestCase { var control = createTemporaryObject(popupControl, testCase) verify(control) + var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + verify(openedSpy.valid) + control.open() - waitForRendering(control.contentItem) + openedSpy.wait() + compare(openedSpy.count, 1) + verify(control.visible) // implicit size of the content control.contentItem.implicitWidth = 10 diff --git a/tests/auto/controls/data/tst_rangeslider.qml b/tests/auto/controls/data/tst_rangeslider.qml index 3d197f83..01ac1b8f 100644 --- a/tests/auto/controls/data/tst_rangeslider.qml +++ b/tests/auto/controls/data/tst_rangeslider.qml @@ -903,4 +903,31 @@ TestCase { mouseMove(control, node.handle.x - 1, node.handle.y - 1) compare(node.hovered, false) } + + function test_nullHandles() { + var control = createTemporaryObject(sliderComponent, testCase, {"second.value": 1}) + verify(control) + + verify(control.first.handle) + verify(control.second.handle) + + control.first.handle = null + control.second.handle = null + + mousePress(control, control.leftPadding, control.height / 2) + verify(control.first.pressed, true) + compare(control.second.pressed, false) + + mouseRelease(control, control.leftPadding, control.height / 2) + compare(control.first.pressed, false) + compare(control.second.pressed, false) + + mousePress(control, control.width - control.rightPadding, control.height / 2) + compare(control.first.pressed, false) + compare(control.second.pressed, true) + + mouseRelease(control, control.width - control.rightPadding, control.height / 2) + compare(control.first.pressed, false) + compare(control.second.pressed, false) + } } diff --git a/tests/auto/controls/data/tst_slider.qml b/tests/auto/controls/data/tst_slider.qml index 826a17d2..b70aeaab 100644 --- a/tests/auto/controls/data/tst_slider.qml +++ b/tests/auto/controls/data/tst_slider.qml @@ -789,4 +789,17 @@ TestCase { compare(control.valueAt(0.5), data.values[2]) compare(control.valueAt(1.0), data.values[3]) } + + function test_nullHandle() { + var control = createTemporaryObject(slider, testCase) + verify(control) + + control.handle = null + + mousePress(control) + verify(control.pressed, true) + + mouseRelease(control) + compare(control.pressed, false) + } } diff --git a/tests/auto/controls/data/tst_spinbox.qml b/tests/auto/controls/data/tst_spinbox.qml index 6c4f15c5..17e4b592 100644 --- a/tests/auto/controls/data/tst_spinbox.qml +++ b/tests/auto/controls/data/tst_spinbox.qml @@ -51,6 +51,7 @@ import QtQuick 2.2 import QtTest 1.0 import QtQuick.Controls 2.2 +import QtQuick.Window 2.3 TestCase { id: testCase @@ -399,6 +400,16 @@ TestCase { compare(control.value, 100) } + function test_initialFocus() { + var window = testCase.Window.window + verify(window) + compare(window.activeFocusItem, window.contentItem) + + var control = createTemporaryObject(spinBox, testCase, { editable: true, focus: true }) + verify(control) + tryCompare(control.contentItem, "activeFocus", true) + } + function test_editable() { var control = createTemporaryObject(spinBox, testCase) verify(control) diff --git a/tests/auto/drawer/tst_drawer.cpp b/tests/auto/drawer/tst_drawer.cpp index 251f5567..7fd4f0d9 100644 --- a/tests/auto/drawer/tst_drawer.cpp +++ b/tests/auto/drawer/tst_drawer.cpp @@ -58,6 +58,8 @@ class tst_Drawer : public QQmlDataTest Q_OBJECT private slots: + void initTestCase(); + void visible_data(); void visible(); @@ -89,6 +91,13 @@ private slots: void interactive(); }; + +void tst_Drawer::initTestCase() +{ + QQmlDataTest::initTestCase(); + qputenv("QML_NO_TOUCH_COMPRESSION", "1"); +} + void tst_Drawer::visible_data() { QTest::addColumn<QString>("source"); @@ -240,19 +249,21 @@ void tst_Drawer::state() void tst_Drawer::position_data() { QTest::addColumn<Qt::Edge>("edge"); + QTest::addColumn<QPoint>("press"); QTest::addColumn<QPoint>("from"); QTest::addColumn<QPoint>("to"); QTest::addColumn<qreal>("position"); - QTest::newRow("top") << Qt::TopEdge << QPoint(100, 0) << QPoint(100, 100) << qreal(0.5); - QTest::newRow("left") << Qt::LeftEdge << QPoint(0, 100) << QPoint(100, 100) << qreal(0.5); - QTest::newRow("right") << Qt::RightEdge << QPoint(399, 100) << QPoint(300, 100) << qreal(0.5); - QTest::newRow("bottom") << Qt::BottomEdge << QPoint(100, 399) << QPoint(100, 300) << qreal(0.5); + QTest::newRow("top") << Qt::TopEdge << QPoint(100, 0) << QPoint(100, 50) << QPoint(100, 150) << qreal(0.5); + QTest::newRow("left") << Qt::LeftEdge << QPoint(0, 100) << QPoint(50, 100) << QPoint(150, 100) << qreal(0.5); + QTest::newRow("right") << Qt::RightEdge << QPoint(399, 100) << QPoint(350, 100) << QPoint(250, 100) << qreal(0.5); + QTest::newRow("bottom") << Qt::BottomEdge << QPoint(100, 399) << QPoint(100, 350) << QPoint(150, 250) << qreal(0.5); } void tst_Drawer::position() { QFETCH(Qt::Edge, edge); + QFETCH(QPoint, press); QFETCH(QPoint, from); QFETCH(QPoint, to); QFETCH(qreal, position); @@ -268,7 +279,8 @@ void tst_Drawer::position() QVERIFY(drawer); drawer->setEdge(edge); - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, from); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, press); + QTest::mouseMove(window, from); QTest::mouseMove(window, to); QCOMPARE(drawer->position(), position); @@ -314,26 +326,30 @@ void tst_Drawer::dragMargin() drawer->setEdge(edge); drawer->setDragMargin(dragMargin); + const int startDragDistance = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5) + 1; + // drag from the left int leftX = qMax<int>(0, dragMargin); - int leftDistance = drawer->width() * 0.45; + int leftDistance = startDragDistance + drawer->width() * 0.45; QVERIFY(leftDistance > QGuiApplication::styleHints()->startDragDistance()); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftX, drawer->height() / 2)); - QTest::mouseMove(window, QPoint(leftDistance, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(leftX + startDragDistance, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(leftX + leftDistance, drawer->height() / 2)); QCOMPARE(drawer->position(), dragFromLeft); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftDistance, drawer->height() / 2)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftX + leftDistance, drawer->height() / 2)); drawer->close(); QTRY_COMPARE(drawer->position(), qreal(0.0)); // drag from the right int rightX = qMin<int>(window->width() - 1, window->width() - dragMargin); - int rightDistance = drawer->width() * 0.75; + int rightDistance = startDragDistance + drawer->width() * 0.75; QVERIFY(rightDistance > QGuiApplication::styleHints()->startDragDistance()); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(rightX, drawer->height() / 2)); - QTest::mouseMove(window, QPoint(window->width() - rightDistance, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(rightX - startDragDistance, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(rightX - rightDistance, drawer->height() / 2)); QCOMPARE(drawer->position(), dragFromRight); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDistance, drawer->height() / 2)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(rightX - rightDistance, drawer->height() / 2)); } static QRectF geometry(const QQuickItem *item) @@ -620,7 +636,8 @@ void tst_Drawer::multiple() // drag the left drawer open QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(0, window->height() / 2)); - QTest::mouseMove(window, QPoint(leftDrawer->width() / 2, window->height() / 2)); + QTest::mouseMove(window, QPoint(leftDrawer->width() / 4, window->height() / 2)); + QTest::mouseMove(window, QPoint(leftDrawer->width() / 4 * 3, window->height() / 2)); QCOMPARE(leftDrawer->position(), 0.5); QCOMPARE(rightDrawer->position(), 0.0); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftDrawer->width() / 2, window->height() / 2)); @@ -699,7 +716,8 @@ void tst_Drawer::multiple() // drag the right drawer open QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - 1, window->height() / 2)); - QTest::mouseMove(window, QPoint(window->width() - rightDrawer->width() / 2, window->height() / 2)); + QTest::mouseMove(window, QPoint(window->width() - rightDrawer->width() / 4, window->height() / 2)); + QTest::mouseMove(window, QPoint(window->width() - rightDrawer->width() / 4 * 3, window->height() / 2)); QCOMPARE(rightDrawer->position(), 0.5); QCOMPARE(leftDrawer->position(), 0.0); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDrawer->width() / 2, window->height() / 2)); @@ -746,19 +764,18 @@ void tst_Drawer::touch() // drag to open QTest::touchEvent(window, device.data()).press(0, QPoint(0, 100)); - QTest::touchEvent(window, device.data()).move(0, QPoint(100, 100)); + QTest::touchEvent(window, device.data()).move(0, QPoint(50, 100)); + QTest::touchEvent(window, device.data()).move(0, QPoint(150, 100)); QTRY_COMPARE(drawer->position(), 0.5); - QTest::touchEvent(window, device.data()).release(0, QPoint(100, 100)); + QTest::touchEvent(window, device.data()).release(0, QPoint(150, 100)); QVERIFY(drawerOpenedSpy.wait()); QCOMPARE(drawer->position(), 1.0); // drag to close QTest::touchEvent(window, device.data()).press(0, QPoint(300, 100)); QTest::touchEvent(window, device.data()).move(0, QPoint(300 - drawer->dragMargin(), 100)); - for (int x = 300; x > 100; x -= 10) { + for (int x = 300; x > 100; x -= 10) QTest::touchEvent(window, device.data()).move(0, QPoint(x, 100)); - QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); - } QTest::touchEvent(window, device.data()).move(0, QPoint(100, 100)); QTRY_COMPARE(drawer->position(), 0.5); QTest::touchEvent(window, device.data()).release(0, QPoint(100, 100)); diff --git a/tests/auto/focus/data/keyNavigation.qml b/tests/auto/focus/data/keyNavigation.qml index 98a58ab5..ba7d5807 100644 --- a/tests/auto/focus/data/keyNavigation.qml +++ b/tests/auto/focus/data/keyNavigation.qml @@ -191,7 +191,6 @@ Item { SpinBox { id: spinbox objectName: "spinbox" - editable: true value: 50 KeyNavigation.left: radiobutton2 KeyNavigation.right: swtich diff --git a/tests/auto/popup/tst_popup.cpp b/tests/auto/popup/tst_popup.cpp index a6de0c03..0e9b55f7 100644 --- a/tests/auto/popup/tst_popup.cpp +++ b/tests/auto/popup/tst_popup.cpp @@ -160,13 +160,26 @@ void tst_popup::state() void tst_popup::overlay_data() { QTest::addColumn<QString>("source"); - QTest::newRow("Window") << "window.qml"; - QTest::newRow("ApplicationWindow") << "applicationwindow.qml"; + QTest::addColumn<bool>("modal"); + QTest::addColumn<bool>("dim"); + + QTest::newRow("Window") << "window.qml" << false << false; + QTest::newRow("Window,dim") << "window.qml" << false << true; + QTest::newRow("Window,modal") << "window.qml" << true << false; + QTest::newRow("Window,modal,dim") << "window.qml" << true << true; + + QTest::newRow("ApplicationWindow") << "applicationwindow.qml" << false << false; + QTest::newRow("ApplicationWindow,dim") << "applicationwindow.qml" << false << true; + QTest::newRow("ApplicationWindow,modal") << "applicationwindow.qml" << true << false; + QTest::newRow("ApplicationWindow,modal,dim") << "applicationwindow.qml" << true << true; } void tst_popup::overlay() { QFETCH(QString, source); + QFETCH(bool, modal); + QFETCH(bool, dim); + QQuickApplicationHelper helper(this, source); QQuickWindow *window = helper.window; @@ -210,7 +223,8 @@ void tst_popup::overlay() QVERIFY(!popup->isVisible()); QVERIFY(!overlay->isVisible()); - popup->setModal(true); + popup->setDim(dim); + popup->setModal(modal); popup->setClosePolicy(QQuickPopup::CloseOnReleaseOutside); popup->open(); @@ -223,10 +237,20 @@ void tst_popup::overlay() QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); QCOMPARE(overlayPressedSignal.count(), 2); + + #define comment "Non-modal popups do not yet support CloseOnReleaseXxx" + #define QEXPECT_NON_MODAL_POPUP_FAILS() \ + QEXPECT_FAIL("Window", comment, Continue); \ + QEXPECT_FAIL("Window,dim", comment, Continue); \ + QEXPECT_FAIL("ApplicationWindow", comment, Continue); \ + QEXPECT_FAIL("ApplicationWindow,dim", comment, Continue); + + QEXPECT_NON_MODAL_POPUP_FAILS() QCOMPARE(overlayReleasedSignal.count(), 1); + QEXPECT_NON_MODAL_POPUP_FAILS() QVERIFY(!popup->isVisible()); - QVERIFY(!overlay->isVisible()); + QCOMPARE(overlay->isVisible(), popup->isVisible()); } void tst_popup::zOrder_data() |