diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2016-12-19 11:49:36 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2016-12-19 11:49:51 +0100 |
commit | 25277124d8b7581a9390702ad3d4bcd4be8dedd7 (patch) | |
tree | 92ea6d78df705f9c01747905a760dc8dd308d51d | |
parent | 6812d1921161612788ac2f7c49decce77c8e0ccd (diff) | |
parent | c4eefa4a8d6d3e95062deb78229940460a7ef605 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/scenegraphng
Change-Id: Id7c17b390a8360b4324afa040774dba8e8fc891f
-rw-r--r-- | src/quick/items/qquickitem.cpp | 66 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 4 | ||||
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 2 | ||||
-rw-r--r-- | src/quick/items/qquickmousearea.cpp | 47 | ||||
-rw-r--r-- | src/quick/items/qquickmousearea_p.h | 6 | ||||
-rw-r--r-- | src/quick/items/qquickmousearea_p_p.h | 1 | ||||
-rw-r--r-- | src/quick/util/qquickshortcut.cpp | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquickitem/data/shortcutOverride.qml | 65 | ||||
-rw-r--r-- | tests/auto/quick/qquickitem/tst_qquickitem.cpp | 35 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/data/pressAndHold.qml | 12 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 45 |
11 files changed, 282 insertions, 3 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index ca8ef7ba2e..504446a8be 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -333,6 +333,12 @@ QVariant QQuickItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const } #endif // im +void QQuickItemKeyFilter::shortcutOverride(QKeyEvent *event) +{ + if (m_next) + m_next->shortcutOverride(event); +} + void QQuickItemKeyFilter::componentComplete() { if (m_next) m_next->componentComplete(); @@ -935,6 +941,46 @@ bool QQuickKeysAttached::isConnected(const char *signalName) const */ /*! + \qmlsignal QtQuick::Keys::shortcutOverride(KeyEvent event) + \since 5.9 + + This signal is emitted when a key has been pressed that could potentially + be used as a shortcut. The \a event parameter provides information about + the event. + + Set \c event.accepted to \c true if you wish to prevent the pressed key + from being used as a shortcut by other types, such as \l Shortcut. For + example: + + \code + Item { + id: escapeItem + focus: true + + // Ensure that we get escape key press events first. + Keys.onShortcutOverride: event.accepted = (event.key === Qt.Key_Escape) + + Keys.onEscapePressed: { + console.log("escapeItem is handling escape"); + event.accepted = true; + } + } + + Shortcut { + sequence: "Escape" + onActivated: console.log("Shortcut is handling escape") + } + \endcode + + As with the other signals, \c shortcutOverride will only be emitted for an + item if that item has \l {Item::}{activeFocus}. + + The corresponding handler is \c onShortcutOverride. + + \sa Shortcut +*/ + +/*! \qmlsignal QtQuick::Keys::digit0Pressed(KeyEvent event) This signal is emitted when the digit '0' has been pressed. The \a event @@ -1426,6 +1472,16 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const } #endif // im +void QQuickKeysAttached::shortcutOverride(QKeyEvent *event) +{ + Q_D(QQuickKeysAttached); + QQuickKeyEvent &keyEvent = d->theKeyEvent; + keyEvent.reset(*event); + emit shortcutOverride(&keyEvent); + + event->setAccepted(keyEvent.isAccepted()); +} + QQuickKeysAttached *QQuickKeysAttached::qmlAttachedProperties(QObject *obj) { return new QQuickKeysAttached(obj); @@ -5019,6 +5075,13 @@ void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e) } #endif // im +void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event) +{ + if (extra.isAllocated() && extra->keyHandler) { + extra->keyHandler->shortcutOverride(event); + } +} + /*! Called when \a change occurs for this item. @@ -7652,6 +7715,9 @@ bool QQuickItem::event(QEvent *ev) case QEvent::KeyRelease: d->deliverKeyEvent(static_cast<QKeyEvent*>(ev)); break; + case QEvent::ShortcutOverride: + d->deliverShortcutOverrideEvent(static_cast<QKeyEvent*>(ev)); + break; case QEvent::FocusIn: focusInEvent(static_cast<QFocusEvent*>(ev)); break; diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 3002b73a8c..c0c9bd46bd 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -559,6 +559,7 @@ public: #if QT_CONFIG(im) void deliverInputMethodEvent(QInputMethodEvent *); #endif + void deliverShortcutOverrideEvent(QKeyEvent *); bool isTransparentForPositioner() const; void setTransparentForPositioner(bool trans); @@ -622,6 +623,7 @@ public: virtual void inputMethodEvent(QInputMethodEvent *event, bool post); virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; #endif + virtual void shortcutOverride(QKeyEvent *event); virtual void componentComplete(); bool m_processPost; @@ -813,6 +815,7 @@ Q_SIGNALS: void priorityChanged(); void pressed(QQuickKeyEvent *event); void released(QQuickKeyEvent *event); + void shortcutOverride(QQuickKeyEvent *event); void digit0Pressed(QQuickKeyEvent *event); void digit1Pressed(QQuickKeyEvent *event); void digit2Pressed(QQuickKeyEvent *event); @@ -861,6 +864,7 @@ private: void inputMethodEvent(QInputMethodEvent *, bool post) Q_DECL_OVERRIDE; QVariant inputMethodQuery(Qt::InputMethodQuery query) const Q_DECL_OVERRIDE; #endif + void shortcutOverride(QKeyEvent *event) override; static QByteArray keyToSignal(int key); bool isConnected(const char *signalName) const; diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 8dd4a96b62..dbe30fbc83 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -368,6 +368,8 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #if QT_CONFIG(quick_shadereffect) qmlRegisterType<QQuickBorderImageMesh>("QtQuick", 2, 8, "BorderImageMesh"); #endif + + qmlRegisterType<QQuickMouseArea, 9>(uri, 2, 9, "MouseArea"); } static void initResources() diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 5e30bf9e0a..79c957832a 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -60,7 +60,8 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) QQuickMouseAreaPrivate::QQuickMouseAreaPrivate() : enabled(true), scrollGestureEnabled(true), hovered(false), longPress(false), moved(false), stealMouse(false), doubleClick(false), preventStealing(false), - propagateComposedEvents(false), overThreshold(false), pressed(0) + propagateComposedEvents(false), overThreshold(false), pressed(0), + pressAndHoldInterval(-1) #if QT_CONFIG(draganddrop) , drag(0) #endif @@ -685,7 +686,7 @@ void QQuickMouseArea::mousePressEvent(QMouseEvent *event) #endif setHovered(true); d->startScene = event->windowPos(); - d->pressAndHoldTimer.start(QGuiApplication::styleHints()->mousePressAndHoldInterval(), this); + d->pressAndHoldTimer.start(pressAndHoldInterval(), this); setKeepMouseGrab(d->stealMouse); event->setAccepted(setPressed(event->button(), true, event->source())); } @@ -1294,6 +1295,48 @@ void QQuickMouseArea::setCursorShape(Qt::CursorShape shape) #endif + +/*! + \qmlproperty int QtQuick::MouseArea::pressAndHoldInterval + \since 5.9 + + This property overrides the elapsed time in milliseconds before + \c pressAndHold is emitted. + + If not explicitly set -- or after reset -- the value follows + \c QStyleHints::mousePressAndHoldInterval. + + Typically it's sufficient to set this property globally using the + application style hint. This property should be used when varying intervals + are needed for certain MouseAreas. + + \sa pressAndHold +*/ +int QQuickMouseArea::pressAndHoldInterval() const +{ + Q_D(const QQuickMouseArea); + return d->pressAndHoldInterval > -1 ? + d->pressAndHoldInterval : QGuiApplication::styleHints()->mousePressAndHoldInterval(); +} + +void QQuickMouseArea::setPressAndHoldInterval(int interval) +{ + Q_D(QQuickMouseArea); + if (interval != d->pressAndHoldInterval) { + d->pressAndHoldInterval = interval; + emit pressAndHoldIntervalChanged(); + } +} + +void QQuickMouseArea::resetPressAndHoldInterval() +{ + Q_D(QQuickMouseArea); + if (d->pressAndHoldInterval > -1) { + d->pressAndHoldInterval = -1; + emit pressAndHoldIntervalChanged(); + } +} + /*! \qmlpropertygroup QtQuick::MouseArea::drag \qmlproperty Item QtQuick::MouseArea::drag.target diff --git a/src/quick/items/qquickmousearea_p.h b/src/quick/items/qquickmousearea_p.h index d90c8e1baa..ee166a2082 100644 --- a/src/quick/items/qquickmousearea_p.h +++ b/src/quick/items/qquickmousearea_p.h @@ -84,6 +84,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseArea : public QQuickItem Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape RESET unsetCursor NOTIFY cursorShapeChanged) #endif Q_PROPERTY(bool containsPress READ containsPress NOTIFY containsPressChanged REVISION 1) + Q_PROPERTY(int pressAndHoldInterval READ pressAndHoldInterval WRITE setPressAndHoldInterval NOTIFY pressAndHoldIntervalChanged RESET resetPressAndHoldInterval REVISION 9) public: QQuickMouseArea(QQuickItem *parent=0); @@ -125,6 +126,10 @@ public: void setCursorShape(Qt::CursorShape shape); #endif + int pressAndHoldInterval() const; + void setPressAndHoldInterval(int interval); + void resetPressAndHoldInterval(); + Q_SIGNALS: void hoveredChanged(); void pressedChanged(); @@ -152,6 +157,7 @@ Q_SIGNALS: void exited(); void canceled(); Q_REVISION(1) void containsPressChanged(); + Q_REVISION(9) void pressAndHoldIntervalChanged(); protected: void setHovered(bool); diff --git a/src/quick/items/qquickmousearea_p_p.h b/src/quick/items/qquickmousearea_p_p.h index 456b1866a3..2fa5f7cd44 100644 --- a/src/quick/items/qquickmousearea_p_p.h +++ b/src/quick/items/qquickmousearea_p_p.h @@ -95,6 +95,7 @@ public: bool propagateComposedEvents : 1; bool overThreshold : 1; Qt::MouseButtons pressed; + int pressAndHoldInterval; #if QT_CONFIG(draganddrop) QQuickDrag *drag; #endif diff --git a/src/quick/util/qquickshortcut.cpp b/src/quick/util/qquickshortcut.cpp index a0a58f2e02..72d9c889e3 100644 --- a/src/quick/util/qquickshortcut.cpp +++ b/src/quick/util/qquickshortcut.cpp @@ -73,7 +73,7 @@ It is also possible to set multiple shortcut \l sequences, so that the shortcut can be \l activated via several different sequences of key presses. - \sa Keys + \sa Keys, {Keys::}{shortcutOverride()} */ /*! \qmlsignal QtQuick::Shortcut::activated() diff --git a/tests/auto/quick/qquickitem/data/shortcutOverride.qml b/tests/auto/quick/qquickitem/data/shortcutOverride.qml new file mode 100644 index 0000000000..fab9175c17 --- /dev/null +++ b/tests/auto/quick/qquickitem/data/shortcutOverride.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** 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:LGPL21$ +** 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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import QtQuick.Window 2.1 + +Item { + property int escapeHandlerActivationCount: 0 + property int shortcutActivationCount: 0 + property alias escapeItem: escapeItem + + Item { + id: escapeItem + objectName: "escapeItem" + focus: true + + // By accepting shortcut override events when the key is Qt.Key_Escape, + // we can ensure that our Keys.onEscapePressed handler (below) will be called. + Keys.onShortcutOverride: event.accepted = (event.key === Qt.Key_Escape) + + Keys.onEscapePressed: { + // Pretend that we just did some really important stuff that was triggered + // by the escape key (like might occur in a popup that has a keyboard shortcut editor, for example). + // Now that we're done, we no longer need focus, so we won't accept future shorcut override events. + focus = false; + event.accepted = true; + ++escapeHandlerActivationCount; + } + } + + Shortcut { + sequence: "Escape" + onActivated: ++shortcutActivationCount + } +} diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index d0139b6cdf..8d974f4d17 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -172,6 +172,8 @@ private slots: void ignoreButtonPressNotInAcceptedMouseButtons(); + void shortcutOverride(); + private: enum PaintOrderOp { @@ -2036,6 +2038,39 @@ void tst_qquickitem::ignoreButtonPressNotInAcceptedMouseButtons() QCOMPARE(item.releaseCount, 1); } +void tst_qquickitem::shortcutOverride() +{ + QQuickView view; + view.setSource(testFileUrl("shortcutOverride.qml")); + ensureFocus(&view); + + QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 0); + QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0); + + QQuickItem *escapeItem = view.rootObject()->property("escapeItem").value<QQuickItem*>(); + QVERIFY(escapeItem); + QVERIFY(escapeItem->hasActiveFocus()); + + // escapeItem's onEscapePressed handler should accept the first escape press event. + QTest::keyPress(&view, Qt::Key_Escape); + QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1); + QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0); + // Now it shouldn't have focus, so it can't handle the next escape press event. + QVERIFY(!escapeItem->hasActiveFocus()); + + QTest::keyRelease(&view, Qt::Key_Escape); + QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1); + QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0); + + QTest::keyPress(&view, Qt::Key_Escape); + QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1); + QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 1); + + QTest::keyRelease(&view, Qt::Key_Escape); + QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1); + QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 1); +} + QTEST_MAIN(tst_qquickitem) #include "tst_qquickitem.moc" diff --git a/tests/auto/quick/qquickmousearea/data/pressAndHold.qml b/tests/auto/quick/qquickmousearea/data/pressAndHold.qml new file mode 100644 index 0000000000..bde195965e --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/pressAndHold.qml @@ -0,0 +1,12 @@ +import QtQuick 2.9 + +Item { + width: 100 + height: 100 + + MouseArea { + id: mouseArea + objectName: "mouseArea" + anchors.fill: parent + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index e1f903123b..c8351b9e18 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -127,6 +127,8 @@ private slots: void containsPress(); void ignoreBySource(); void notPressedAfterStolenGrab(); + void pressAndHold_data(); + void pressAndHold(); private: int startDragDistance() const { @@ -2098,6 +2100,49 @@ void tst_QQuickMouseArea::notPressedAfterStolenGrab() QVERIFY(!ma->pressed()); } +void tst_QQuickMouseArea::pressAndHold_data() +{ + QTest::addColumn<int>("pressAndHoldInterval"); + QTest::addColumn<int>("waitTime"); + + QTest::newRow("default") << -1 << QGuiApplication::styleHints()->mousePressAndHoldInterval(); + QTest::newRow("short") << 500 << 500; + QTest::newRow("long") << 1000 << 1000; +} + +void tst_QQuickMouseArea::pressAndHold() +{ + QFETCH(int, pressAndHoldInterval); + QFETCH(int, waitTime); + + QQuickView window; + QByteArray errorMessage; + QVERIFY2(initView(window, testFileUrl("pressAndHold.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QQuickItem *root = window.rootObject(); + QVERIFY(root != 0); + + QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>("mouseArea"); + QVERIFY(mouseArea != 0); + + QSignalSpy pressAndHoldSpy(mouseArea, &QQuickMouseArea::pressAndHold); + + if (pressAndHoldInterval > -1) + mouseArea->setPressAndHoldInterval(pressAndHoldInterval); + else + mouseArea->resetPressAndHoldInterval(); + + QElapsedTimer t; + t.start(); + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50)); + QVERIFY(pressAndHoldSpy.wait()); + // should be off by no more than 20% of waitTime + QVERIFY(qAbs(t.elapsed() - waitTime) < (waitTime * 0.2)); + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50)); +} + QTEST_MAIN(tst_QQuickMouseArea) #include "tst_qquickmousearea.moc" |