diff options
author | J-P Nurmi <jpnurmi@qt.io> | 2016-09-15 10:09:36 +0200 |
---|---|---|
committer | J-P Nurmi <jpnurmi@qt.io> | 2016-09-21 21:39:47 +0000 |
commit | 244356ba182c2807ef9b15eb71ac16a568d65642 (patch) | |
tree | 15d6590511f983e98c909763a274bead9de9ee8b | |
parent | 175d0ea3f80af0ca32baec489d8ea66dd4ee3418 (diff) |
QQuickDrawer: allow resizing and positioning
Make QQuickDrawer re-use QQuickPopup's reposition() implementation.
This way QQuickDrawer gains support for proper positioning and margins
"for free". Now it is possible to place Drawer below the window header,
for instance:
import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
visible: true
header: ToolBar { }
Drawer {
y: header.height
width: window.width * 0.6
height: window.height - header.height
}
}
[ChangeLog][Controls][Drawer] Made it possible to control the vertical
position of a horizontal drawer, and vice versa. This allows placing
a drawer below a header/toolbar, for instance.
Task-number: QTBUG-55360
Change-Id: I63621195efeefa2ea88935d676771b392e0a4030
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r-- | src/quicktemplates2/qquickdrawer.cpp | 73 | ||||
-rw-r--r-- | src/quicktemplates2/qquickdrawer_p.h | 2 | ||||
-rw-r--r-- | src/quicktemplates2/qquickdrawer_p_p.h | 3 | ||||
-rw-r--r-- | src/quicktemplates2/qquickpopup.cpp | 81 | ||||
-rw-r--r-- | src/quicktemplates2/qquickpopup_p_p.h | 4 | ||||
-rw-r--r-- | tests/auto/drawer/data/reposition.qml | 58 | ||||
-rw-r--r-- | tests/auto/drawer/tst_drawer.cpp | 47 |
7 files changed, 228 insertions, 40 deletions
diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index 4c850473..80cd95c9 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -91,6 +91,32 @@ QT_BEGIN_NAMESPACE \l {Popup::}{parent} to something else to make the drawer operate in a specific coordinate space. + Drawer can be configured to cover only part of its window edge. The following example + illustrates how Drawer can be positioned to appear below a window header: + + \code + import QtQuick 2.7 + import QtQuick.Controls 2.0 + + ApplicationWindow { + id: window + visible: true + + header: ToolBar { } + + Drawer { + y: header.height + width: window.width * 0.6 + height: window.height - header.height + } + } + \endcode + + The \l position property determines how much of the drawer is visible, as + a value between \c 0.0 and \c 1.0. It is not possible to set the x-coordinate + (or horizontal margins) of a drawer at the left or right window edge, or the + y-coordinate (or vertical margins) of a drawer at the top or bottom window edge. + In the image above, the application's contents are \e "pushed" across the screen. This is achieved by applying a translation to the contents: @@ -139,6 +165,7 @@ QQuickDrawerPrivate::QQuickDrawerPrivate() : edge(Qt::LeftEdge), offset(0), position(0), dragMargin(QGuiApplication::styleHints()->startDragDistance()) { + setEdge(Qt::LeftEdge); } qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const @@ -183,6 +210,27 @@ void QQuickDrawerPrivate::reposition() popupItem->setY(window->height() - position * popupItem->height()); break; } + + QQuickPopupPrivate::reposition(); +} + +void QQuickDrawerPrivate::resizeOverlay() +{ + if (!dimmer || !window) + return; + + QRectF geometry(0, 0, window->width(), window->height()); + + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) { + geometry.setY(popupItem->y()); + geometry.setHeight(popupItem->height()); + } else { + geometry.setX(popupItem->x()); + geometry.setWidth(popupItem->width()); + } + + dimmer->setPosition(geometry.topLeft()); + dimmer->setSize(geometry.size()); } static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int threshold = -1) @@ -402,6 +450,22 @@ bool QQuickDrawerPrivate::prepareExitTransition() return QQuickPopupPrivate::prepareExitTransition(); } +void QQuickDrawerPrivate::setEdge(Qt::Edge e) +{ + edge = e; + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) { + allowVerticalMove = true; + allowVerticalResize = true; + allowHorizontalMove = false; + allowHorizontalResize = false; + } else { + allowVerticalMove = false; + allowVerticalResize = false; + allowHorizontalMove = true; + allowHorizontalResize = true; + } +} + QQuickDrawer::QQuickDrawer(QObject *parent) : QQuickPopup(*(new QQuickDrawerPrivate), parent) { @@ -434,7 +498,7 @@ void QQuickDrawer::setEdge(Qt::Edge edge) if (d->edge == edge) return; - d->edge = edge; + d->setEdge(edge); if (isComponentComplete()) d->reposition(); emit edgeChanged(); @@ -559,4 +623,11 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) } } +void QQuickDrawer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickDrawer); + QQuickPopup::geometryChanged(newGeometry, oldGeometry); + d->resizeOverlay(); +} + QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index e694e27b..bada1344 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -87,6 +87,8 @@ protected: void mouseUngrabEvent() override; bool overlayEvent(QQuickItem *item, QEvent *event) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + private: Q_DISABLE_COPY(QQuickDrawer) Q_DECLARE_PRIVATE(QQuickDrawer) diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h index f14c36dd..b7555c3e 100644 --- a/src/quicktemplates2/qquickdrawer_p_p.h +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -68,6 +68,7 @@ public: qreal positionAt(const QPointF &point) const; void reposition() override; + void resizeOverlay() override; bool startDrag(QQuickWindow *window, QMouseEvent *event); bool grabMouse(QMouseEvent *event); @@ -80,6 +81,8 @@ public: bool prepareEnterTransition() override; bool prepareExitTransition() override; + void setEdge(Qt::Edge edge); + Qt::Edge edge; qreal offset; qreal position; diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index 22b399b1..6deda711 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -133,6 +133,10 @@ QQuickPopupPrivate::QQuickPopupPrivate() , hasBottomMargin(false) , allowVerticalFlip(false) , allowHorizontalFlip(false) + , allowVerticalMove(true) + , allowHorizontalMove(true) + , allowVerticalResize(true) + , allowHorizontalResize(true) , hadActiveFocusBeforeExitTransition(false) , x(0) , y(0) @@ -589,7 +593,10 @@ void QQuickPopupPrivate::reposition() bool widthAdjusted = false; bool heightAdjusted = false; - QRectF rect(x, y, !hasWidth && iw > 0 ? iw : w, !hasHeight && ih > 0 ? ih : h); + QRectF rect(allowHorizontalMove ? x : popupItem->x(), + allowVerticalMove ? y : popupItem->y(), + !hasWidth && iw > 0 ? iw : w, + !hasHeight && ih > 0 ? ih : h); if (parentItem) { rect = parentItem->mapRectToScene(rect); @@ -615,31 +622,39 @@ void QQuickPopupPrivate::reposition() } // push inside the margins if specified - if (margins.top() >= 0 && rect.top() < bounds.top()) - rect.moveTop(margins.top()); - if (margins.bottom() >= 0 && rect.bottom() > bounds.bottom()) - rect.moveBottom(bounds.bottom()); - if (margins.left() >= 0 && rect.left() < bounds.left()) - rect.moveLeft(margins.left()); - if (margins.right() >= 0 && rect.right() > bounds.right()) - rect.moveRight(bounds.right()); + if (allowVerticalMove) { + if (margins.top() >= 0 && rect.top() < bounds.top()) + rect.moveTop(margins.top()); + if (margins.bottom() >= 0 && rect.bottom() > bounds.bottom()) + rect.moveBottom(bounds.bottom()); + } + if (allowHorizontalMove) { + if (margins.left() >= 0 && rect.left() < bounds.left()) + rect.moveLeft(margins.left()); + if (margins.right() >= 0 && rect.right() > bounds.right()) + rect.moveRight(bounds.right()); + } if (iw > 0 && (rect.left() < bounds.left() || rect.right() > bounds.right())) { // neither the flipped or pushed geometry fits inside the window, choose // whichever side (left vs. right) fits larger part of the popup - if (rect.left() < bounds.left() && bounds.left() + rect.width() <= bounds.right()) - rect.moveLeft(bounds.left()); - else if (rect.right() > bounds.right() && bounds.right() - rect.width() >= bounds.left()) - rect.moveRight(bounds.right()); + if (allowHorizontalMove && allowHorizontalFlip) { + if (rect.left() < bounds.left() && bounds.left() + rect.width() <= bounds.right()) + rect.moveLeft(bounds.left()); + else if (rect.right() > bounds.right() && bounds.right() - rect.width() >= bounds.left()) + rect.moveRight(bounds.right()); + } // as a last resort, adjust the width to fit the window - if (rect.left() < bounds.left()) { - rect.setLeft(bounds.left()); - widthAdjusted = true; - } - if (rect.right() > bounds.right()) { - rect.setRight(bounds.right()); - widthAdjusted = true; + if (allowHorizontalResize) { + if (rect.left() < bounds.left()) { + rect.setLeft(bounds.left()); + widthAdjusted = true; + } + if (rect.right() > bounds.right()) { + rect.setRight(bounds.right()); + widthAdjusted = true; + } } } else if (iw > 0 && rect.left() >= bounds.left() && rect.right() <= bounds.right() && iw != w) { @@ -651,19 +666,23 @@ void QQuickPopupPrivate::reposition() if (ih > 0 && (rect.top() < bounds.top() || rect.bottom() > bounds.bottom())) { // neither the flipped or pushed geometry fits inside the window, choose // whichever side (above vs. below) fits larger part of the popup - if (rect.top() < bounds.top() && bounds.top() + rect.height() <= bounds.bottom()) - rect.moveTop(bounds.top()); - else if (rect.bottom() > bounds.bottom() && bounds.bottom() - rect.height() >= bounds.top()) - rect.moveBottom(bounds.bottom()); + if (allowVerticalMove && allowVerticalFlip) { + if (rect.top() < bounds.top() && bounds.top() + rect.height() <= bounds.bottom()) + rect.moveTop(bounds.top()); + else if (rect.bottom() > bounds.bottom() && bounds.bottom() - rect.height() >= bounds.top()) + rect.moveBottom(bounds.bottom()); + } // as a last resort, adjust the height to fit the window - if (rect.top() < bounds.top()) { - rect.setTop(bounds.top()); - heightAdjusted = true; - } - if (rect.bottom() > bounds.bottom()) { - rect.setBottom(bounds.bottom()); - heightAdjusted = true; + if (allowVerticalResize) { + if (rect.top() < bounds.top()) { + rect.setTop(bounds.top()); + heightAdjusted = true; + } + if (rect.bottom() > bounds.bottom()) { + rect.setBottom(bounds.bottom()); + heightAdjusted = true; + } } } else if (ih > 0 && rect.top() >= bounds.top() && rect.bottom() <= bounds.bottom() && ih != h) { diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h index 321adaf5..c9f5e264 100644 --- a/src/quicktemplates2/qquickpopup_p_p.h +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -192,6 +192,10 @@ public: bool hasBottomMargin; bool allowVerticalFlip; bool allowHorizontalFlip; + bool allowVerticalMove; + bool allowHorizontalMove; + bool allowVerticalResize; + bool allowHorizontalResize; bool hadActiveFocusBeforeExitTransition; qreal x; qreal y; diff --git a/tests/auto/drawer/data/reposition.qml b/tests/auto/drawer/data/reposition.qml new file mode 100644 index 00000000..abaec5ae --- /dev/null +++ b/tests/auto/drawer/data/reposition.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +ApplicationWindow { + width: 400 + height: 400 + + property alias drawer: drawer + + header: Item { implicitHeight: 50 } + footer: Item { implicitHeight: 50 } + + Drawer { + id: drawer + width: parent.width / 2 + implicitHeight: parent.height + } +} diff --git a/tests/auto/drawer/tst_drawer.cpp b/tests/auto/drawer/tst_drawer.cpp index f29abf21..19a66326 100644 --- a/tests/auto/drawer/tst_drawer.cpp +++ b/tests/auto/drawer/tst_drawer.cpp @@ -46,6 +46,7 @@ #include <QtQuick/private/qquickwindow_p.h> #include <QtQuickTemplates2/private/qquickapplicationwindow_p.h> #include <QtQuickTemplates2/private/qquickoverlay_p.h> +#include <QtQuickTemplates2/private/qquickpopup_p_p.h> #include <QtQuickTemplates2/private/qquickdrawer_p.h> #include <QtQuickTemplates2/private/qquickbutton_p.h> #include <QtQuickTemplates2/private/qquickslider_p.h> @@ -330,27 +331,57 @@ void tst_Drawer::dragMargin() QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDistance, drawer->height() / 2)); } +static QRectF geometry(const QQuickItem *item) +{ + return QRectF(item->x(), item->y(), item->width(), item->height()); +} + void tst_Drawer::reposition() { - QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); + QQuickApplicationHelper helper(this, QStringLiteral("reposition.qml")); QQuickApplicationWindow *window = helper.appWindow; window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowExposed(window)); - QQuickDrawer *drawer = helper.appWindow->property("drawer").value<QQuickDrawer*>(); + QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer*>(); QVERIFY(drawer); - drawer->setEdge(Qt::RightEdge); + QQuickItem *popupItem = drawer->popupItem(); + QVERIFY(popupItem); drawer->open(); - QTRY_COMPARE(drawer->popupItem()->x(), window->width() - drawer->width()); + QQuickItem *dimmer = QQuickPopupPrivate::get(drawer)->dimmer; + QVERIFY(dimmer); + + QCOMPARE(geometry(dimmer), QRectF(0, 0, window->width(), window->height())); + QTRY_COMPARE(geometry(popupItem), QRectF(0, 0, window->width() / 2, window->height())); + + drawer->setY(100); + QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height() - 100)); + QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height() - 100)); + + drawer->setHeight(window->height()); + QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height())); + QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height())); + + drawer->resetHeight(); + QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height() - 100)); + QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height() - 100)); + + drawer->setParentItem(window->contentItem()); + QCOMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150)); + QCOMPARE(geometry(popupItem), QRectF(0, 150, window->width() / 2, window->height() - 150)); + + drawer->setEdge(Qt::RightEdge); + QCOMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150)); + QTRY_COMPARE(geometry(popupItem), QRectF(window->width() - drawer->width(), 150, window->width() / 2, window->height() - 150)); window->setWidth(window->width() + 100); - QTRY_COMPARE(drawer->popupItem()->x(), window->width() - drawer->width()); + QTRY_COMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150)); + QCOMPARE(geometry(popupItem), QRectF(window->width() - drawer->width(), 150, window->width() / 2, window->height() - 150)); drawer->close(); - QTRY_COMPARE(drawer->popupItem()->x(), static_cast<qreal>(window->width())); + QTRY_COMPARE(geometry(popupItem), QRectF(window->width(), 150, window->width() / 2, window->height() - 150)); } void tst_Drawer::header() |