From f6e0992ccd7d15456d528b9ee4e54ac6794e5d63 Mon Sep 17 00:00:00 2001 From: David Redondo Date: Mon, 19 Jul 2021 10:06:17 +0200 Subject: [PATCH 1/5] Unset mouseGrabberPopup if it's removed from children The mouseGrabberPopup is supposed to be unset in handleRelease, however when the exit transition of the mouseGrabberPopup (that closed itself on button press) finishes before the release event is delivered, it unparents itself from the overlay (see QQuickPopupPrivate::finalizeExitTransition) and the overlay sets itself invisible if there is nothing else visible in it. Because the overlay is not visible it handles no events anymore and the release is missed and the grabber is never unset. When opening another non-modal popup the overlay then will continue forwarding the events to now invisible popup. So when the overlay loses the currently grabbing popup as a child we need to reset mouseGrabberPopup. Fixes: QTBUG-95259 Change-Id: I3c832d47f3cee216b81ef1b5cb7dd77bf4149991 Reviewed-by: Mitch Curtis (adapted from commit d07ee1345acd8100fa5cbb7f05c0aaf5f87f4cae) (cherry picked from commit 1a59ef4218658ffc476909ef4fca13d6cf86d04b) --- src/quicktemplates2/qquickoverlay.cpp | 5 +- .../data/releaseAfterExitTransition.qml | 78 +++++++++++++++++++ tests/auto/qquickpopup/tst_qquickpopup.cpp | 29 +++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qquickpopup/data/releaseAfterExitTransition.qml diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index 91bd59184..0ce518f84 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -399,8 +399,11 @@ void QQuickOverlay::itemChange(ItemChange change, const ItemChangeData &data) Q_D(QQuickOverlay); QQuickItem::itemChange(change, data); - if (change == ItemChildAddedChange || change == ItemChildRemovedChange) + if (change == ItemChildAddedChange || change == ItemChildRemovedChange) { setVisible(!d->allDrawers.isEmpty() || !childItems().isEmpty()); + if (data.item->parent() == d->mouseGrabberPopup) + d->setMouseGrabberPopup(nullptr); + } } void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) diff --git a/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml b/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml new file mode 100644 index 000000000..9e4598b9f --- /dev/null +++ b/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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.15 +import QtQuick.Controls 2.15 + +ApplicationWindow { + id: window + width: 400 + height: 400 + title: "releaseAfterExitTransition" + + property alias popup: popup + property alias modalPopup: modalPopup + + Popup { + id: popup + y: parent.height - height + width: 50 + height: 50 + } + + Popup { + id: modalPopup + modal: true + y: parent.height - height + width: 50 + height: 50 + exit: Transition { PauseAnimation { duration: 100 } } + } +} diff --git a/tests/auto/qquickpopup/tst_qquickpopup.cpp b/tests/auto/qquickpopup/tst_qquickpopup.cpp index 54952d128..3d50e2dd4 100644 --- a/tests/auto/qquickpopup/tst_qquickpopup.cpp +++ b/tests/auto/qquickpopup/tst_qquickpopup.cpp @@ -100,6 +100,7 @@ private slots: void invisibleToolTipOpen(); void centerInOverlayWithinStackViewItem(); void destroyDuringExitTransition(); + void releaseAfterExitTransition(); }; void tst_QQuickPopup::initTestCase() @@ -1575,6 +1576,34 @@ void tst_QQuickPopup::destroyDuringExitTransition() QVERIFY(!button->isDown()); } +void tst_QQuickPopup::releaseAfterExitTransition() +{ + QQuickApplicationHelper helper(this, "releaseAfterExitTransition.qml"); + QVERIFY2(helper.ready, helper.failureMessage()); + + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window)); + + QQuickOverlay *overlay = QQuickOverlay::overlay(window); + QQuickPopup *modalPopup = window->property("modalPopup").value(); + QQuickPopup *popup = window->property("popup").value(); + + modalPopup->open(); + QTRY_VERIFY(modalPopup->isOpened()); + + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); + // wait until the transition is finished and the overlay hides itself + QTRY_VERIFY(!overlay->isVisible()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); + + popup->open(); + QTRY_VERIFY(popup->isOpened()); + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); + QTRY_VERIFY(!popup->isOpened()); +} + + QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup) #include "tst_qquickpopup.moc" -- 2.49.0 From dec090837242230f6bec4be9a09221ae773f01d8 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Tue, 4 Jan 2022 16:34:16 +0100 Subject: [PATCH 2/5] Ensure we don't crash when changing sizes after cleanup This addresses the problems I've seen during destruction. Only encountered it when using complex layouts on a DialogButtonBox. Pick-to: 6.2 6.3 Change-Id: I54528c8a2b57b4798d90f7e2021e3127f8404762 (cherry picked from commit 8b24d2bf1655e8491bdd74013579e09cd009e8fc in qtdeclarative) --- src/quicktemplates2/qquickcontainer.cpp | 5 +++-- src/quicktemplates2/qquickdialogbuttonbox.cpp | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/quicktemplates2/qquickcontainer.cpp b/src/quicktemplates2/qquickcontainer.cpp index f38c2b09c..6eed2a024 100644 --- a/src/quicktemplates2/qquickcontainer.cpp +++ b/src/quicktemplates2/qquickcontainer.cpp @@ -225,6 +225,7 @@ void QQuickContainerPrivate::cleanup() QObject::disconnect(contentModel, &QQmlObjectModel::countChanged, q, &QQuickContainer::countChanged); QObject::disconnect(contentModel, &QQmlObjectModel::childrenChanged, q, &QQuickContainer::contentChildrenChanged); delete contentModel; + contentModel = nullptr; } QQuickItem *QQuickContainerPrivate::itemAt(int index) const @@ -436,7 +437,7 @@ void QQuickContainerPrivate::contentChildren_clear(QQmlListProperty void QQuickContainerPrivate::updateContentWidth() { Q_Q(QQuickContainer); - if (hasContentWidth || qFuzzyCompare(contentWidth, implicitContentWidth)) + if (hasContentWidth || qFuzzyCompare(contentWidth, implicitContentWidth) || !contentModel) return; contentWidth = implicitContentWidth; @@ -446,7 +447,7 @@ void QQuickContainerPrivate::updateContentWidth() void QQuickContainerPrivate::updateContentHeight() { Q_Q(QQuickContainer); - if (hasContentHeight || qFuzzyCompare(contentHeight, implicitContentHeight)) + if (hasContentHeight || qFuzzyCompare(contentHeight, implicitContentHeight) || !contentModel) return; contentHeight = implicitContentHeight; diff --git a/src/quicktemplates2/qquickdialogbuttonbox.cpp b/src/quicktemplates2/qquickdialogbuttonbox.cpp index e33c5c934..9afabd18a 100644 --- a/src/quicktemplates2/qquickdialogbuttonbox.cpp +++ b/src/quicktemplates2/qquickdialogbuttonbox.cpp @@ -237,7 +237,7 @@ static QRectF alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment void QQuickDialogButtonBoxPrivate::resizeContent() { Q_Q(QQuickDialogButtonBox); - if (!contentItem) + if (!contentItem || !contentModel) return; QRectF geometry = q->boundingRect().adjusted(q->leftPadding(), q->topPadding(), -q->rightPadding(), -q->bottomPadding()); @@ -322,6 +322,9 @@ void QQuickDialogButtonBoxPrivate::updateLayout() qreal QQuickDialogButtonBoxPrivate::getContentWidth() const { Q_Q(const QQuickDialogButtonBox); + if (!contentModel) + return 0; + const int count = contentModel->count(); const qreal totalSpacing = qMax(0, count - 1) * spacing; qreal totalWidth = totalSpacing; @@ -341,6 +344,9 @@ qreal QQuickDialogButtonBoxPrivate::getContentWidth() const qreal QQuickDialogButtonBoxPrivate::getContentHeight() const { Q_Q(const QQuickDialogButtonBox); + if (!contentModel) + return 0; + const int count = contentModel->count(); qreal maxHeight = 0; for (int i = 0; i < count; ++i) { -- 2.49.0 From bc0ca722f27658c6f95b88a92e58fc1139620cb3 Mon Sep 17 00:00:00 2001 From: Harald Sitter Date: Wed, 2 Nov 2022 12:39:11 +0100 Subject: [PATCH 3/5] implement a11y pressing of qquickabstractbutton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this adds a pressAction default implementation that simply calls trigger (which in turn either triggers the action or emits a click), allowing accessibility tools to issue a button press via a11y api. Change-Id: I75b4fb8680835093b1135fdbf4329aaa85dc3243 Reviewed-by: Arjen Hiemstra Reviewed-by: Aleix Pol Gonzalez Reviewed-by: Jan Arve Sæther (cherry picked from commit 705659eaaf47af72eeb5f5c742e18a5c665a76eb in qtdeclarative) --- src/quicktemplates2/qquickabstractbutton.cpp | 6 ++++++ src/quicktemplates2/qquickabstractbutton_p.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp index 20cf59c1a..43af47a94 100644 --- a/src/quicktemplates2/qquickabstractbutton.cpp +++ b/src/quicktemplates2/qquickabstractbutton.cpp @@ -1201,6 +1201,12 @@ QAccessible::Role QQuickAbstractButton::accessibleRole() const } return QAccessible::Button; } + +void QQuickAbstractButton::accessiblePressAction() +{ + Q_D(QQuickAbstractButton); + d->trigger(); +} #endif QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickabstractbutton_p.h b/src/quicktemplates2/qquickabstractbutton_p.h index 0fa48980e..ab66220d0 100644 --- a/src/quicktemplates2/qquickabstractbutton_p.h +++ b/src/quicktemplates2/qquickabstractbutton_p.h @@ -209,6 +209,7 @@ protected: #if QT_CONFIG(accessibility) void accessibilityActiveChanged(bool active) override; QAccessible::Role accessibleRole() const override; + Q_INVOKABLE void accessiblePressAction(); #endif private: -- 2.49.0 From 33c61400f55eadbef023043742c720969f419872 Mon Sep 17 00:00:00 2001 From: Inho Lee Date: Mon, 22 Aug 2022 21:05:00 +0800 Subject: [PATCH 4/5] Fix the popup position of a Menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QPA code should operate in native coord. Treat QWidgetPlatformMenu::showPopup's input as native coord. Fixes: QTBUG-94619 Fixes: QTBUG-94783 Change-Id: Iaa030c96d84e4a588e625fe191e4324f70be961f Reviewed-by: Morten Johan Sørvig (cherry picked from commit f8cf17166c9af147f0b8fea72f5b4a8a6098a5d7 in qtdeclarative) --- src/imports/platform/widgets/qwidgetplatformmenu.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/imports/platform/widgets/qwidgetplatformmenu.cpp b/src/imports/platform/widgets/qwidgetplatformmenu.cpp index e5fe734f7..e36922775 100644 --- a/src/imports/platform/widgets/qwidgetplatformmenu.cpp +++ b/src/imports/platform/widgets/qwidgetplatformmenu.cpp @@ -38,6 +38,7 @@ #include "qwidgetplatformmenuitem_p.h" #include +#include #include #include @@ -145,7 +146,7 @@ void QWidgetPlatformMenu::showPopup(const QWindow *window, const QRect &targetRe QPoint targetPos = targetRect.bottomLeft(); if (window) - targetPos = window->mapToGlobal(targetPos); + targetPos = window->mapToGlobal(QHighDpi::fromNativeLocalPosition(targetPos, window)); const QWidgetPlatformMenuItem *widgetItem = qobject_cast(item); m_menu->popup(targetPos, widgetItem ? widgetItem->action() : nullptr); -- 2.49.0 From 9bdcebd1c093b6f0886e3a739711470cc37b1adb Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 18 Apr 2023 22:05:36 +0200 Subject: [PATCH 5/5] Accessibility: respect value in attached Accessible in controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QQuickItemPrivate::accessibleRole is virtual and called by the framework to determine the role of an item. The default implementation checks and respects a possible Accessible attached object. However, subclasses that override the virtual don't, so the attached properties are ignored, and the class-specific implementation wins. This makes it impossible to change the role of e.g. a checkable button. To fix that, move the code respecting the attached object into a non- virtual function that the framework calls instead, and only call the virtual member if there is no attached object, or if that object is not initialized with a role. Replace calls to the virtual from the framework with calls to the non-virtual wrapper. Do this for both QQuickItem and for QQuickPopup, and adjust the logic in QQuickControl types that create an attached object and initialize it's role when accessibility becomes active. Use the non-overridable effective role value for that as well. Add a test case, and to avoid any new framework calls to the virtual, make it private. Fixes: QTBUG-110114 Pick-to: 6.5 6.2 Change-Id: Ia709cecbd181b6d8ee3297a4af60c1e7db9a2c51 Reviewed-by: Qt CI Bot Reviewed-by: Jan Arve Sæther (cherry picked from commit 3c08d08ae2bbd449cc0579a1b3cb499383c7a60c) --- src/quicktemplates2/qquickpopup.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index 3f8691e2c..6b1ca32b3 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include #include -- 2.49.0