diff options
author | J-P Nurmi <jpnurmi@theqtcompany.com> | 2016-04-13 15:59:53 +0200 |
---|---|---|
committer | J-P Nurmi <jpnurmi@theqtcompany.com> | 2016-04-13 15:28:11 +0000 |
commit | a60c8e60d508117ddf48876b44d31e6d5ab98da6 (patch) | |
tree | 8bf008a1bf8dc34338450eee86d2b55e84cba36c /src/quicktemplates2/qquickdrawer.cpp | |
parent | e719087dce50d739c723c5ef9af2a9226318aa02 (diff) |
Templates: rename the C++ module to qtquicktemplates2
Change-Id: I146da903b46f5c2caf865e37291c25376b49021a
Reviewed-by: J-P Nurmi <jpnurmi@theqtcompany.com>
Diffstat (limited to 'src/quicktemplates2/qquickdrawer.cpp')
-rw-r--r-- | src/quicktemplates2/qquickdrawer.cpp | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp new file mode 100644 index 00000000..8d8d2d78 --- /dev/null +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -0,0 +1,460 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Labs Templates module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdrawer_p.h" +#include "qquickpopup_p_p.h" +#include "qquickvelocitycalculator_p_p.h" + +#include <QtGui/qstylehints.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtQuick/private/qquickwindow_p.h> +#include <QtQuick/private/qquickanimation_p.h> +#include <QtQuick/private/qquicktransition_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Drawer + \inherits Popup + \instantiates QQuickDrawer + \inqmlmodule Qt.labs.controls + \ingroup qtquickcontrols2-navigation + \ingroup qtquickcontrols2-containers + \brief Provides a swipe-based side panel. + + Drawer provides a swipe-based side panel, similar to those often used in + touch interfaces to provide a central location for navigation. + + \table + \row + \li \image qtquickcontrols-drawer-wireframe.png + Drawer can be positioned at any of the four edges of the content item. \br + In this image, it is against the left edge of the window. + + \li \image qtquickcontrols-drawer-expanded-wireframe.png + The drawer is then opened by \e "dragging" it out from the left edge \br + of the window. + \endtable + + In the image above, the application's contents are \e "pushed" across the + screen. This is achieved by applying a translation to the contents: + + \code + transform: Translate { + x: (1.0 - drawer.position) * listview.width + } + \endcode + + If you would like the application's contents to stay where they are when + the drawer is opened, don't apply a translation. + + \labs + + \sa SwipeView, {Customizing Drawer}, {Navigation Controls}, {Container Controls} +*/ + +class QQuickDrawerPrivate : public QQuickPopupPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickDrawer) + +public: + QQuickDrawerPrivate() : edge(Qt::LeftEdge), offset(0), position(0) { } + + qreal positionAt(const QPointF &point) const; + void reposition() override; + + bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); + bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); + bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event); + + void prepareEnterTransition(bool notify = true) override; + void prepareExitTransition() override; + void finalizeEnterTransition() override; + void finalizeExitTransition(bool hide = true) override; + + Qt::Edge edge; + qreal offset; + qreal position; + QPointF pressPoint; + QQuickVelocityCalculator velocityCalculator; +}; + +qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const +{ + Q_Q(const QQuickDrawer); + switch (edge) { + case Qt::TopEdge: + return point.y() / q->height(); + case Qt::LeftEdge: + return point.x() / q->width(); + case Qt::RightEdge: + return (q->width() - point.x()) / popupItem->width(); + case Qt::BottomEdge: + return (q->height() - point.y()) / popupItem->height(); + default: + return 0; + } +} + +void QQuickDrawerPrivate::reposition() +{ + Q_Q(QQuickDrawer); + QQuickWindow *window = q->window(); + if (!window) + return; + + switch (edge) { + case Qt::LeftEdge: + popupItem->setX((position - 1.0) * popupItem->width()); + break; + case Qt::RightEdge: + popupItem->setX(window->width() - position * popupItem->width()); + break; + case Qt::TopEdge: + popupItem->setY((position - 1.0) * popupItem->height()); + break; + case Qt::BottomEdge: + popupItem->setY(window->height() - position * popupItem->height()); + break; + } +} + +static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int threshold = -1) +{ + return QQuickWindowPrivate::dragOverThreshold(d, axis, event, threshold); +} + +bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) +{ + pressPoint = event->windowPos(); + offset = 0; + + QQuickWindow *window = item->window(); + if (!window) + return false; + + if (qFuzzyIsNull(position)) { + // only accept pressing at drag margins when fully closed + switch (edge) { + case Qt::LeftEdge: + event->setAccepted(!dragOverThreshold(event->windowPos().x(), Qt::XAxis, event)); + break; + case Qt::RightEdge: + event->setAccepted(!dragOverThreshold(window->width() - event->windowPos().x(), Qt::XAxis, event)); + break; + case Qt::TopEdge: + event->setAccepted(!dragOverThreshold(event->windowPos().y(), Qt::YAxis, event)); + break; + case Qt::BottomEdge: + event->setAccepted(!dragOverThreshold(window->height() - event->windowPos().y(), Qt::YAxis, event)); + break; + } + } else { + event->setAccepted(item->isAncestorOf(popupItem)); + } + + velocityCalculator.startMeasuring(pressPoint, event->timestamp()); + + return event->isAccepted(); +} + +bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) +{ + Q_Q(QQuickDrawer); + QQuickWindow *window = item->window(); + if (!window) + return false; + + QPointF movePoint = event->windowPos(); + + if (!popupItem->keepMouseGrab()) { + // 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) + int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5); + bool overThreshold = false; + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, event, threshold); + else + overThreshold = dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, event, threshold); + + if (overThreshold) { + QQuickItem *grabber = window->mouseGrabberItem(); + if (!grabber || !grabber->keepMouseGrab()) { + popupItem->grabMouse(); + popupItem->setKeepMouseGrab(overThreshold); + offset = qMin<qreal>(0.0, positionAt(movePoint) - position); + } + } + } + + if (popupItem->keepMouseGrab()) + q->setPosition(positionAt(movePoint) - offset); + event->accept(); + + return popupItem->keepMouseGrab(); +} + +static const qreal openCloseVelocityThreshold = 300; + +bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event) +{ + Q_UNUSED(item); + + bool wasGrabbed = popupItem->keepMouseGrab(); + if (wasGrabbed) { + velocityCalculator.stopMeasuring(event->pos(), event->timestamp()); + const qreal velocity = velocityCalculator.velocity().x(); + + if (position > 0.7 || velocity > openCloseVelocityThreshold) { + transitionManager.transitionEnter(); + } else if (position < 0.3 || velocity < -openCloseVelocityThreshold) { + transitionManager.transitionExit(); + } else { + switch (edge) { + case Qt::LeftEdge: + if (event->x() - pressPoint.x() > 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::RightEdge: + if (event->x() - pressPoint.x() < 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::TopEdge: + if (event->y() - pressPoint.y() > 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + case Qt::BottomEdge: + if (event->y() - pressPoint.y() < 0) + transitionManager.transitionEnter(); + else + transitionManager.transitionExit(); + break; + } + } + popupItem->setKeepMouseGrab(false); + } + pressPoint = QPoint(); + event->accept(); + return wasGrabbed; +} + +static QList<QQuickStateAction> prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to) +{ + QList<QQuickStateAction> actions; + if (!transition) + return actions; + + qmlExecuteDeferred(transition); + + QQmlProperty defaultTarget(drawer, QLatin1String("position")); + QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations(); + int count = animations.count(&animations); + for (int i = 0; i < count; ++i) { + QQuickAbstractAnimation *anim = animations.at(&animations, i); + anim->setDefaultTarget(defaultTarget); + } + + actions << QQuickStateAction(drawer, QLatin1String("position"), to); + return actions; +} + +void QQuickDrawerPrivate::prepareEnterTransition(bool notify) +{ + Q_Q(QQuickDrawer); + enterActions = prepareTransition(q, enter, 1.0); + QQuickPopupPrivate::prepareEnterTransition(notify); +} + +void QQuickDrawerPrivate::prepareExitTransition() +{ + Q_Q(QQuickDrawer); + exitActions = prepareTransition(q, exit, 0.0); + QQuickPopupPrivate::prepareExitTransition(); +} + +void QQuickDrawerPrivate::finalizeEnterTransition() +{ + QQuickPopupPrivate::finalizeEnterTransition(); +} + +void QQuickDrawerPrivate::finalizeExitTransition(bool hide) +{ + QQuickPopupPrivate::finalizeExitTransition(hide = false); +} + +QQuickDrawer::QQuickDrawer(QObject *parent) : + QQuickPopup(*(new QQuickDrawerPrivate), parent) +{ + setFocus(true); + setModal(true); + setFiltersChildMouseEvents(true); + setClosePolicy(OnEscape | OnReleaseOutside); +} + +/*! + \qmlproperty enumeration Qt.labs.controls::Drawer::edge + + This property holds the edge of the content item at which the drawer will + open from. The acceptable values are: + + \value Qt.TopEdge The top edge of the content item. + \value Qt.LeftEdge The left edge of the content item (default). + \value Qt.RightEdge The right edge of the content item. + \value Qt.BottomEdge The bottom edge of the content item. +*/ +Qt::Edge QQuickDrawer::edge() const +{ + Q_D(const QQuickDrawer); + return d->edge; +} + +void QQuickDrawer::setEdge(Qt::Edge edge) +{ + Q_D(QQuickDrawer); + if (d->edge == edge) + return; + + d->edge = edge; + if (isComponentComplete()) + d->reposition(); + emit edgeChanged(); +} + +/*! + \qmlproperty real Qt.labs.controls::Drawer::position + + This property holds the position of the drawer relative to its final + destination. That is, the position will be \c 0 when the drawer + is fully closed, and \c 1 when fully open. +*/ +qreal QQuickDrawer::position() const +{ + Q_D(const QQuickDrawer); + return d->position; +} + +void QQuickDrawer::setPosition(qreal position) +{ + Q_D(QQuickDrawer); + position = qBound<qreal>(0.0, position, 1.0); + if (qFuzzyCompare(d->position, position)) + return; + + d->position = position; + if (isComponentComplete()) + d->reposition(); + emit positionChanged(); +} + +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::MouseMove: + return d->handleMouseMoveEvent(child, static_cast<QMouseEvent *>(event)); + case QEvent::MouseButtonRelease: + return d->handleMouseReleaseEvent(child, static_cast<QMouseEvent *>(event)); + default: + return false; + } +} + +void QQuickDrawer::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickDrawer); + QQuickPopup::mousePressEvent(event); + d->handleMousePressEvent(d->popupItem, event); +} + +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(); +} + +bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) +{ + Q_D(QQuickDrawer); + switch (event->type()) { + case QEvent::MouseButtonPress: + d->tryClose(item, static_cast<QMouseEvent *>(event)); + return d->handleMousePressEvent(item, static_cast<QMouseEvent *>(event)); + case QEvent::MouseMove: + return d->handleMouseMoveEvent(item, static_cast<QMouseEvent *>(event)); + case QEvent::MouseButtonRelease: + d->tryClose(item, static_cast<QMouseEvent *>(event)); + return d->handleMouseReleaseEvent(item, static_cast<QMouseEvent *>(event)); + default: + return false; + } +} + +void QQuickDrawer::componentComplete() +{ + Q_D(QQuickDrawer); + QQuickPopup::componentComplete(); + bool notify = false; + d->prepareEnterTransition(notify); + d->reposition(); +} + +QT_END_NAMESPACE |