blob: 41955d7d02ec67d8cdf56a4c0c4a39bc508bd3d2 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 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: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 <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
#include "../shared/util.h"
#include "../shared/visualtestutil.h"
#include "../shared/qtest_quickcontrols.h"
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtQuick/qquickview.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
#include <QtQuickTemplates2/private/qquickcombobox_p.h>
#include <QtQuickTemplates2/private/qquickdialog_p.h>
#include <QtQuickTemplates2/private/qquickoverlay_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTemplates2/private/qquickslider_p.h>
#include <QtQuickTemplates2/private/qquickstackview_p.h>
using namespace QQuickVisualTestUtil;
class tst_QQuickPopup : public QQmlDataTest
{
Q_OBJECT
private slots:
void initTestCase();
void visible_data();
void visible();
void state();
void overlay_data();
void overlay();
void zOrder_data();
void zOrder();
void windowChange();
void closePolicy_data();
void closePolicy();
void activeFocusOnClose1();
void activeFocusOnClose2();
void activeFocusOnClose3();
void activeFocusOnClosingSeveralPopups();
void hover_data();
void hover();
void wheel_data();
void wheel();
void parentDestroyed();
void nested();
void grabber();
void cursorShape();
void componentComplete();
void closeOnEscapeWithNestedPopups();
void closeOnEscapeWithVisiblePopup();
void enabled();
void orientation_data();
void orientation();
void qquickview();
void disabledPalette();
void disabledParentPalette();
void countChanged();
void toolTipCrashOnClose();
void setOverlayParentToNull();
void tabFence();
void invisibleToolTipOpen();
void centerInOverlayWithinStackViewItem();
};
void tst_QQuickPopup::initTestCase()
{
QQmlDataTest::initTestCase();
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
}
void tst_QQuickPopup::visible_data()
{
QTest::addColumn<QString>("source");
QTest::newRow("Window") << "window.qml";
QTest::newRow("ApplicationWindow") << "applicationwindow.qml";
}
void tst_QQuickPopup::visible()
{
QFETCH(QString, source);
QQuickApplicationHelper helper(this, source);
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
QQuickItem *popupItem = popup->popupItem();
popup->open();
QVERIFY(popup->isVisible());
QQuickOverlay *overlay = QQuickOverlay::overlay(window);
QVERIFY(overlay);
QVERIFY(overlay->childItems().contains(popupItem));
popup->close();
QTRY_VERIFY(!popup->isVisible());
QVERIFY(!overlay->childItems().contains(popupItem));
popup->setVisible(true);
QVERIFY(popup->isVisible());
QVERIFY(overlay->childItems().contains(popupItem));
popup->setVisible(false);
QTRY_VERIFY(!popup->isVisible());
QVERIFY(!overlay->childItems().contains(popupItem));
}
void tst_QQuickPopup::state()
{
QQuickApplicationHelper helper(this, "applicationwindow.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
QCOMPARE(popup->isVisible(), false);
QSignalSpy visibleChangedSpy(popup, SIGNAL(visibleChanged()));
QSignalSpy aboutToShowSpy(popup, SIGNAL(aboutToShow()));
QSignalSpy aboutToHideSpy(popup, SIGNAL(aboutToHide()));
QSignalSpy openedSpy(popup, SIGNAL(opened()));
QSignalSpy closedSpy(popup, SIGNAL(closed()));
QVERIFY(visibleChangedSpy.isValid());
QVERIFY(aboutToShowSpy.isValid());
QVERIFY(aboutToHideSpy.isValid());
QVERIFY(openedSpy.isValid());
QVERIFY(closedSpy.isValid());
popup->open();
QCOMPARE(visibleChangedSpy.count(), 1);
QCOMPARE(aboutToShowSpy.count(), 1);
QCOMPARE(aboutToHideSpy.count(), 0);
QTRY_COMPARE(openedSpy.count(), 1);
QCOMPARE(closedSpy.count(), 0);
popup->close();
QTRY_COMPARE(visibleChangedSpy.count(), 2);
QCOMPARE(aboutToShowSpy.count(), 1);
QCOMPARE(aboutToHideSpy.count(), 1);
QCOMPARE(openedSpy.count(), 1);
QTRY_COMPARE(closedSpy.count(), 1);
}
void tst_QQuickPopup::overlay_data()
{
QTest::addColumn<QString>("source");
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_QQuickPopup::overlay()
{
QFETCH(QString, source);
QFETCH(bool, modal);
QFETCH(bool, dim);
QQuickApplicationHelper helper(this, source);
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickOverlay *overlay = QQuickOverlay::overlay(window);
QVERIFY(overlay);
QSignalSpy overlayPressedSignal(overlay, SIGNAL(pressed()));
QSignalSpy overlayReleasedSignal(overlay, SIGNAL(released()));
QVERIFY(overlayPressedSignal.isValid());
QVERIFY(overlayReleasedSignal.isValid());
QVERIFY(!overlay->isVisible()); // no popups open
QTest::mouseClick(window, Qt::LeftButton);
QCOMPARE(overlayPressedSignal.count(), 0);
QCOMPARE(overlayReleasedSignal.count(), 0);
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
QQuickOverlayAttached *overlayAttached = qobject_cast<QQuickOverlayAttached *>(qmlAttachedPropertiesObject<QQuickOverlay>(popup));
QVERIFY(overlayAttached);
QCOMPARE(overlayAttached->overlay(), overlay);
QSignalSpy overlayAttachedPressedSignal(overlayAttached, SIGNAL(pressed()));
QSignalSpy overlayAttachedReleasedSignal(overlayAttached, SIGNAL(released()));
QVERIFY(overlayAttachedPressedSignal.isValid());
QVERIFY(overlayAttachedReleasedSignal.isValid());
QQuickButton *button = window->property("button").value<QQuickButton*>();
QVERIFY(button);
int overlayPressCount = 0;
int overlayReleaseCount = 0;
popup->open();
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QTRY_VERIFY(popup->isOpened());
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayAttachedReleasedSignal.count(), overlayReleaseCount);
QTRY_VERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount); // no modal-popups open
QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayAttachedReleasedSignal.count(), overlayReleaseCount);
popup->setDim(dim);
popup->setModal(modal);
popup->setClosePolicy(QQuickPopup::CloseOnReleaseOutside);
// mouse
popup->open();
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QTRY_VERIFY(popup->isOpened());
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayAttachedReleasedSignal.count(), overlayReleaseCount);
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), ++overlayReleaseCount);
QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayAttachedReleasedSignal.count(), overlayReleaseCount);
QTRY_VERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
// touch
popup->open();
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
struct TouchDeviceDeleter
{
static inline void cleanup(QTouchDevice *device)
{
QWindowSystemInterface::unregisterTouchDevice(device);
delete device;
}
};
QScopedPointer<QTouchDevice, TouchDeviceDeleter> device(new QTouchDevice);
device->setType(QTouchDevice::TouchScreen);
QWindowSystemInterface::registerTouchDevice(device.data());
QTest::touchEvent(window, device.data()).press(0, QPoint(1, 1));
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayAttachedReleasedSignal.count(), overlayReleaseCount);
QTest::touchEvent(window, device.data()).release(0, QPoint(1, 1));
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), ++overlayReleaseCount);
QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayAttachedReleasedSignal.count(), overlayReleaseCount);
QTRY_VERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
// multi-touch
popup->open();
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QVERIFY(!button->isPressed());
QTest::touchEvent(window, device.data()).press(0, button->mapToScene(QPointF(1, 1)).toPoint());
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QCOMPARE(button->isPressed(), !modal);
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
QTest::touchEvent(window, device.data()).stationary(0).press(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
QVERIFY(popup->isVisible());
QVERIFY(overlay->isVisible());
QCOMPARE(button->isPressed(), !modal);
QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
QTest::touchEvent(window, device.data()).release(0, button->mapToScene(QPointF(1, 1)).toPoint()).stationary(1);
QTRY_VERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
QVERIFY(!button->isPressed());
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), ++overlayReleaseCount);
QTest::touchEvent(window, device.data()).release(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
QVERIFY(!popup->isVisible());
QVERIFY(!overlay->isVisible());
QVERIFY(!button->isPressed());
QCOMPARE(overlayPressedSignal.count(), overlayPressCount);
QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount);
}
void tst_QQuickPopup::zOrder_data()
{
QTest::addColumn<QString>("source");
QTest::newRow("Window") << "window.qml";
QTest::newRow("ApplicationWindow") << "applicationwindow.qml";
}
void tst_QQuickPopup::zOrder()
{
QFETCH(QString, source);
QQuickApplicationHelper helper(this, source);
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
popup->setModal(true);
QQuickPopup *popup2 = window->property("popup2").value<QQuickPopup*>();
QVERIFY(popup2);
popup2->setModal(true);
// show popups in reverse order. popup2 has higher z-order so it appears
// on top and must be closed first, even if the other popup was opened last
popup2->open();
popup->open();
QVERIFY(popup2->isVisible());
QVERIFY(popup->isVisible());
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
QTRY_VERIFY(!popup2->isVisible());
QVERIFY(popup->isVisible());
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
QVERIFY(!popup2->isVisible());
QTRY_VERIFY(!popup->isVisible());
}
void tst_QQuickPopup::windowChange()
{
QQuickPopup popup;
QSignalSpy spy(&popup, SIGNAL(windowChanged(QQuickWindow*)));
QVERIFY(spy.isValid());
QQuickItem item;
popup.setParentItem(&item);
QVERIFY(!popup.window());
QCOMPARE(spy.count(), 0);
QQuickWindow window;
item.setParentItem(window.contentItem());
QCOMPARE(popup.window(), &window);
QCOMPARE(spy.count(), 1);
item.setParentItem(nullptr);
QVERIFY(!popup.window());
QCOMPARE(spy.count(), 2);
popup.setParentItem(window.contentItem());
QCOMPARE(popup.window(), &window);
QCOMPARE(spy.count(), 3);
popup.resetParentItem();
QVERIFY(!popup.window());
QCOMPARE(spy.count(), 4);
popup.setParent(&window);
popup.resetParentItem();
QCOMPARE(popup.window(), &window);
QCOMPARE(spy.count(), 5);
popup.setParent(this);
popup.resetParentItem();
QVERIFY(!popup.window());
QCOMPARE(spy.count(), 6);
item.setParentItem(window.contentItem());
popup.setParent(&item);
popup.resetParentItem();
QCOMPARE(popup.window(), &window);
QCOMPARE(spy.count(), 7);
popup.setParent(nullptr);
}
Q_DECLARE_METATYPE(QQuickPopup::ClosePolicy)
void tst_QQuickPopup::closePolicy_data()
{
qRegisterMetaType<QQuickPopup::ClosePolicy>();
QTest::addColumn<QString>("source");
QTest::addColumn<QQuickPopup::ClosePolicy>("closePolicy");
QTest::newRow("Window:NoAutoClose") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose);
QTest::newRow("Window:CloseOnPressOutside") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside);
QTest::newRow("Window:CloseOnPressOutsideParent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent);
QTest::newRow("Window:CloseOnPressOutside|Parent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
QTest::newRow("Window:CloseOnReleaseOutside") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
QTest::newRow("Window:CloseOnReleaseOutside|Parent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
QTest::newRow("Window:CloseOnEscape") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape);
QTest::newRow("ApplicationWindow:NoAutoClose") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose);
QTest::newRow("ApplicationWindow:CloseOnPressOutside") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside);
QTest::newRow("ApplicationWindow:CloseOnPressOutsideParent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent);
QTest::newRow("ApplicationWindow:CloseOnPressOutside|Parent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
QTest::newRow("ApplicationWindow:CloseOnReleaseOutside") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
QTest::newRow("ApplicationWindow:CloseOnReleaseOutside|Parent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
QTest::newRow("ApplicationWindow:CloseOnEscape") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape);
}
void tst_QQuickPopup::closePolicy()
{
QFETCH(QString, source);
QFETCH(QQuickPopup::ClosePolicy, closePolicy);
QQuickApplicationHelper helper(this, source);
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
QQuickButton *button = window->property("button").value<QQuickButton*>();
QVERIFY(button);
popup->setModal(true);
popup->setFocus(true);
popup->setClosePolicy(closePolicy);
popup->open();
QVERIFY(popup->isVisible());
QTRY_VERIFY(popup->isOpened());
// press outside popup and its parent
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1), 50);
if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) || closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
QVERIFY(popup->isVisible());
popup->open();
QVERIFY(popup->isVisible());
QTRY_VERIFY(popup->isOpened());
// release outside popup and its parent
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside))
QTRY_VERIFY(!popup->isVisible());
else
QVERIFY(popup->isVisible());
popup->open();
QVERIFY(popup->isVisible());
QTRY_VERIFY(popup->isOpened());
// press outside popup but inside its parent
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + 1, button->y() + 1));
if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
QVERIFY(popup->isVisible());
popup->open();
QVERIFY(popup->isVisible());
QTRY_VERIFY(popup->isOpened());
// release outside popup but inside its parent
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + 1, button->y() + 1));
if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
QVERIFY(popup->isVisible());
popup->open();
QVERIFY(popup->isVisible());
QTRY_VERIFY(popup->isOpened());
// press inside and release outside
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + popup->x() + 1,
button->y() + popup->y() + 1));
QVERIFY(popup->isVisible());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
QVERIFY(popup->isVisible());
// escape
QTest::keyClick(window, Qt::Key_Escape);
if (closePolicy.testFlag(QQuickPopup::CloseOnEscape))
QTRY_VERIFY(!popup->isVisible());
else
QVERIFY(popup->isVisible());
}
void tst_QQuickPopup::activeFocusOnClose1()
{
// Test that a popup that never sets focus: true (e.g. ToolTip) doesn't affect
// the active focus item when it closes.
QQuickApplicationHelper helper(this, QStringLiteral("activeFocusOnClose1.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *focusedPopup = helper.appWindow->property("focusedPopup").value<QQuickPopup*>();
QVERIFY(focusedPopup);
QQuickPopup *nonFocusedPopup = helper.appWindow->property("nonFocusedPopup").value<QQuickPopup*>();
QVERIFY(nonFocusedPopup);
focusedPopup->open();
QVERIFY(focusedPopup->isVisible());
QTRY_VERIFY(focusedPopup->isOpened());
QVERIFY(focusedPopup->hasActiveFocus());
nonFocusedPopup->open();
QVERIFY(nonFocusedPopup->isVisible());
QTRY_VERIFY(nonFocusedPopup->isOpened());
QVERIFY(focusedPopup->hasActiveFocus());
nonFocusedPopup->close();
QTRY_VERIFY(!nonFocusedPopup->isVisible());
QVERIFY(focusedPopup->hasActiveFocus());
// QTBUG-66113: force active focus on a popup that did not request focus
nonFocusedPopup->open();
nonFocusedPopup->forceActiveFocus();
QVERIFY(nonFocusedPopup->isVisible());
QTRY_VERIFY(nonFocusedPopup->isOpened());
QVERIFY(nonFocusedPopup->hasActiveFocus());
nonFocusedPopup->close();
QTRY_VERIFY(!nonFocusedPopup->isVisible());
QVERIFY(focusedPopup->hasActiveFocus());
}
void tst_QQuickPopup::activeFocusOnClose2()
{
// Test that a popup that sets focus: true but relinquishes focus (e.g. by
// calling forceActiveFocus() on another item) before it closes doesn't
// affect the active focus item when it closes.
QQuickApplicationHelper helper(this, QStringLiteral("activeFocusOnClose2.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup1 = helper.appWindow->property("popup1").value<QQuickPopup*>();
QVERIFY(popup1);
QQuickPopup *popup2 = helper.appWindow->property("popup2").value<QQuickPopup*>();
QVERIFY(popup2);
QQuickButton *closePopup2Button = helper.appWindow->property("closePopup2Button").value<QQuickButton*>();
QVERIFY(closePopup2Button);
popup1->open();
QVERIFY(popup1->isVisible());
QTRY_VERIFY(popup1->isOpened());
QVERIFY(popup1->hasActiveFocus());
popup2->open();
QVERIFY(popup2->isVisible());
QTRY_VERIFY(popup2->isOpened());
QVERIFY(popup2->hasActiveFocus());
// Causes popup1.contentItem.forceActiveFocus() to be called, then closes popup2.
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
closePopup2Button->mapToScene(QPointF(closePopup2Button->width() / 2, closePopup2Button->height() / 2)).toPoint());
QTRY_VERIFY(!popup2->isVisible());
QVERIFY(popup1->hasActiveFocus());
}
void tst_QQuickPopup::activeFocusOnClose3()
{
// Test that a closing popup that had focus doesn't steal focus from
// another popup that the focus was transferred to.
QQuickApplicationHelper helper(this, QStringLiteral("activeFocusOnClose3.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup1 = helper.appWindow->property("popup1").value<QQuickPopup*>();
QVERIFY(popup1);
QQuickPopup *popup2 = helper.appWindow->property("popup2").value<QQuickPopup*>();
QVERIFY(popup2);
popup1->open();
QVERIFY(popup1->isVisible());
QTRY_VERIFY(popup1->hasActiveFocus());
popup2->open();
popup1->close();
QSignalSpy closedSpy(popup1, SIGNAL(closed()));
QVERIFY(closedSpy.isValid());
QVERIFY(closedSpy.wait());
QVERIFY(!popup1->isVisible());
QVERIFY(popup2->isVisible());
QTRY_VERIFY(popup2->hasActiveFocus());
}
void tst_QQuickPopup::activeFocusOnClosingSeveralPopups()
{
// Test that active focus isn't lost when multiple popup closing simultaneously
QQuickApplicationHelper helper(this, QStringLiteral("activeFocusOnClosingSeveralPopups.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickItem *button = window->property("button").value<QQuickItem *>();
QVERIFY(button);
QQuickPopup *popup1 = window->property("popup1").value<QQuickPopup *>();
QVERIFY(popup1);
QQuickPopup *popup2 = window->property("popup2").value<QQuickPopup *>();
QVERIFY(popup2);
QCOMPARE(button->hasActiveFocus(), true);
popup1->open();
QTRY_VERIFY(popup1->isOpened());
QVERIFY(popup1->hasActiveFocus());
popup2->open();
QTRY_VERIFY(popup2->isOpened());
QVERIFY(popup2->hasActiveFocus());
QTRY_COMPARE(button->hasActiveFocus(), false);
// close the unfocused popup first
popup1->close();
popup2->close();
QTRY_VERIFY(!popup1->isVisible());
QTRY_VERIFY(!popup2->isVisible());
QTRY_COMPARE(button->hasActiveFocus(), true);
popup1->open();
QTRY_VERIFY(popup1->isOpened());
QVERIFY(popup1->hasActiveFocus());
popup2->open();
QTRY_VERIFY(popup2->isOpened());
QVERIFY(popup2->hasActiveFocus());
QTRY_COMPARE(button->hasActiveFocus(), false);
// close the focused popup first
popup2->close();
popup1->close();
QTRY_VERIFY(!popup1->isVisible());
QTRY_VERIFY(!popup2->isVisible());
QTRY_COMPARE(button->hasActiveFocus(), true);
}
void tst_QQuickPopup::hover_data()
{
QTest::addColumn<QString>("source");
QTest::addColumn<bool>("modal");
QTest::newRow("Window:modal") << "window-hover.qml" << true;
QTest::newRow("Window:modeless") << "window-hover.qml" << false;
QTest::newRow("ApplicationWindow:modal") << "applicationwindow-hover.qml" << true;
QTest::newRow("ApplicationWindow:modeless") << "applicationwindow-hover.qml" << false;
}
void tst_QQuickPopup::hover()
{
QFETCH(QString, source);
QFETCH(bool, modal);
QQuickApplicationHelper helper(this, source);
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
popup->setModal(modal);
QQuickButton *parentButton = window->property("parentButton").value<QQuickButton*>();
QVERIFY(parentButton);
parentButton->setHoverEnabled(true);
QQuickButton *childButton = window->property("childButton").value<QQuickButton*>();
QVERIFY(childButton);
childButton->setHoverEnabled(true);
QSignalSpy openedSpy(popup, SIGNAL(opened()));
QVERIFY(openedSpy.isValid());
popup->open();
QVERIFY(openedSpy.count() == 1 || openedSpy.wait());
// hover the parent button outside the popup
QTest::mouseMove(window, QPoint(window->width() - 1, window->height() - 1));
QCOMPARE(parentButton->isHovered(), !modal);
QVERIFY(!childButton->isHovered());
// hover the popup background
QTest::mouseMove(window, QPoint(1, 1));
QVERIFY(!parentButton->isHovered());
QVERIFY(!childButton->isHovered());
// hover the child button in a popup
QTest::mouseMove(window, QPoint(popup->x() + popup->width() / 2, popup->y() + popup->height() / 2));
QVERIFY(!parentButton->isHovered());
QVERIFY(childButton->isHovered());
QSignalSpy closedSpy(popup, SIGNAL(closed()));
QVERIFY(closedSpy.isValid());
popup->close();
QVERIFY(closedSpy.count() == 1 || closedSpy.wait());
// hover the parent button after closing the popup
QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2));
QVERIFY(parentButton->isHovered());
}
void tst_QQuickPopup::wheel_data()
{
QTest::addColumn<QString>("source");
QTest::addColumn<bool>("modal");
QTest::newRow("Window:modal") << "window-wheel.qml" << true;
QTest::newRow("Window:modeless") << "window-wheel.qml" << false;
QTest::newRow("ApplicationWindow:modal") << "applicationwindow-wheel.qml" << true;
QTest::newRow("ApplicationWindow:modeless") << "applicationwindow-wheel.qml" << false;
}
static bool sendWheelEvent(QQuickItem *item, const QPoint &localPos, int degrees)
{
QQuickWindow *window = item->window();
QWheelEvent wheelEvent(localPos, item->window()->mapToGlobal(localPos),
QPoint(0, 0), QPoint(0, 8 * degrees),
0, Qt::Vertical, Qt::NoButton, {});
QSpontaneKeyEvent::setSpontaneous(&wheelEvent);
return qGuiApp->notify(window, &wheelEvent);
}
void tst_QQuickPopup::wheel()
{
QFETCH(QString, source);
QFETCH(bool, modal);
QQuickApplicationHelper helper(this, source);
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickSlider *contentSlider = window->property("contentSlider").value<QQuickSlider*>();
QVERIFY(contentSlider);
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup && popup->contentItem());
popup->setModal(modal);
QQuickSlider *popupSlider = window->property("popupSlider").value<QQuickSlider*>();
QVERIFY(popupSlider);
{
// wheel over the content
qreal oldContentValue = contentSlider->value();
qreal oldPopupValue = popupSlider->value();
QVERIFY(sendWheelEvent(contentSlider, QPoint(contentSlider->width() / 2, contentSlider->height() / 2), 15));
QVERIFY(!qFuzzyCompare(contentSlider->value(), oldContentValue)); // must have moved
QVERIFY(qFuzzyCompare(popupSlider->value(), oldPopupValue)); // must not have moved
}
QSignalSpy openedSpy(popup, SIGNAL(opened()));
QVERIFY(openedSpy.isValid());
popup->open();
QVERIFY(openedSpy.count() == 1 || openedSpy.wait());
{
// wheel over the popup content
qreal oldContentValue = contentSlider->value();
qreal oldPopupValue = popupSlider->value();
QVERIFY(sendWheelEvent(popupSlider, QPoint(popupSlider->width() / 2, popupSlider->height() / 2), 15));
QVERIFY(qFuzzyCompare(contentSlider->value(), oldContentValue)); // must not have moved
QVERIFY(!qFuzzyCompare(popupSlider->value(), oldPopupValue)); // must have moved
}
{
// wheel over the overlay
qreal oldContentValue = contentSlider->value();
qreal oldPopupValue = popupSlider->value();
QVERIFY(sendWheelEvent(QQuickOverlay::overlay(window), QPoint(0, 0), 15));
if (modal) {
// the content below a modal overlay must not move
QVERIFY(qFuzzyCompare(contentSlider->value(), oldContentValue));
} else {
// the content below a modeless overlay must move
QVERIFY(!qFuzzyCompare(contentSlider->value(), oldContentValue));
}
QVERIFY(qFuzzyCompare(popupSlider->value(), oldPopupValue)); // must not have moved
}
}
void tst_QQuickPopup::parentDestroyed()
{
QQuickPopup popup;
popup.setParentItem(new QQuickItem);
delete popup.parentItem();
QVERIFY(!popup.parentItem());
}
void tst_QQuickPopup::nested()
{
QQuickApplicationHelper helper(this, QStringLiteral("nested.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *modalPopup = window->property("modalPopup").value<QQuickPopup *>();
QVERIFY(modalPopup);
QQuickPopup *modelessPopup = window->property("modelessPopup").value<QQuickPopup *>();
QVERIFY(modelessPopup);
modalPopup->open();
QCOMPARE(modalPopup->isVisible(), true);
modelessPopup->open();
QCOMPARE(modelessPopup->isVisible(), true);
// click outside the modeless popup on the top, but inside the modal popup below
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(150, 150));
QTRY_COMPARE(modelessPopup->isVisible(), false);
QCOMPARE(modalPopup->isVisible(), true);
}
// QTBUG-56697
void tst_QQuickPopup::grabber()
{
QQuickApplicationHelper helper(this, QStringLiteral("grabber.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *menu = window->property("menu").value<QQuickPopup *>();
QVERIFY(menu);
QQuickPopup *popup = window->property("popup").value<QQuickPopup *>();
QVERIFY(popup);
QQuickPopup *combo = window->property("combo").value<QQuickPopup *>();
QVERIFY(combo);
menu->open();
QTRY_COMPARE(menu->isOpened(), true);
QCOMPARE(popup->isVisible(), false);
QCOMPARE(combo->isVisible(), false);
// click a menu item to open the popup
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(menu->width() / 2, menu->height() / 2));
QTRY_COMPARE(menu->isVisible(), false);
QTRY_COMPARE(popup->isOpened(), true);
QCOMPARE(combo->isVisible(), false);
combo->open();
QCOMPARE(menu->isVisible(), false);
QCOMPARE(popup->isVisible(), true);
QTRY_COMPARE(combo->isOpened(), true);
// click outside to close both the combo popup and the parent popup
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - 1, window->height() - 1));
QCOMPARE(menu->isVisible(), false);
QTRY_COMPARE(popup->isVisible(), false);
QTRY_COMPARE(combo->isVisible(), false);
menu->open();
QTRY_COMPARE(menu->isOpened(), true);
QCOMPARE(popup->isVisible(), false);
QCOMPARE(combo->isVisible(), false);
// click outside the menu to close it (QTBUG-56697)
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - 1, window->height() - 1));
QTRY_COMPARE(menu->isVisible(), false);
QCOMPARE(popup->isVisible(), false);
QCOMPARE(combo->isVisible(), false);
}
void tst_QQuickPopup::cursorShape()
{
// Ensure that the mouse cursor has the correct shape when over a popup
// which is itself over an item with a different shape.
QQuickApplicationHelper helper(this, QStringLiteral("cursor.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
centerOnScreen(window);
moveMouseAway(window);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *popup = helper.appWindow->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
popup->open();
QVERIFY(popup->isVisible());
QTRY_VERIFY(popup->isOpened());
QQuickItem *textField = helper.appWindow->property("textField").value<QQuickItem*>();
QVERIFY(textField);
// Move the mouse over the text field.
const QPoint textFieldPos(popup->x() - 10, textField->height() / 2);
QTest::mouseMove(window, textFieldPos);
QCOMPARE(window->cursor().shape(), textField->cursor().shape());
// Move the mouse over the popup where it overlaps with the text field.
const QPoint textFieldOverlapPos(popup->x() + 10, textField->height() / 2);
QTest::mouseMove(window, textFieldOverlapPos);
QCOMPARE(window->cursor().shape(), popup->popupItem()->cursor().shape());
popup->close();
QTRY_VERIFY(!popup->isVisible());
}
class FriendlyPopup : public QQuickPopup
{
friend class tst_QQuickPopup;
};
void tst_QQuickPopup::componentComplete()
{
FriendlyPopup cppPopup;
QVERIFY(cppPopup.isComponentComplete());
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick.Controls 2.2; Popup { }", QUrl());
FriendlyPopup *qmlPopup = static_cast<FriendlyPopup *>(component.beginCreate(engine.rootContext()));
QVERIFY(qmlPopup);
QVERIFY(!qmlPopup->isComponentComplete());
component.completeCreate();
QVERIFY(qmlPopup->isComponentComplete());
}
void tst_QQuickPopup::closeOnEscapeWithNestedPopups()
{
// Tests the scenario in the Gallery example, where there are nested popups that should
// close in the correct order when the Escape key is pressed.
QQuickApplicationHelper helper(this, QStringLiteral("closeOnEscapeWithNestedPopups.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
// The stack view should have two items, and it should pop the second when escape is pressed
// and it has focus.
QQuickStackView *stackView = window->findChild<QQuickStackView*>("stackView");
QVERIFY(stackView);
QCOMPARE(stackView->depth(), 2);
QQuickItem *optionsToolButton = window->findChild<QQuickItem*>("optionsToolButton");
QVERIFY(optionsToolButton);
// Click on the options tool button. The settings menu should pop up.
const QPoint optionsToolButtonCenter = optionsToolButton->mapToScene(
QPointF(optionsToolButton->width() / 2, optionsToolButton->height() / 2)).toPoint();
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, optionsToolButtonCenter);
QQuickPopup *optionsMenu = window->findChild<QQuickPopup*>("optionsMenu");
QVERIFY(optionsMenu);
QTRY_VERIFY(optionsMenu->isVisible());
QQuickItem *settingsMenuItem = window->findChild<QQuickItem*>("settingsMenuItem");
QVERIFY(settingsMenuItem);
// Click on the settings menu item. The settings dialog should pop up.
const QPoint settingsMenuItemCenter = settingsMenuItem->mapToScene(
QPointF(settingsMenuItem->width() / 2, settingsMenuItem->height() / 2)).toPoint();
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, settingsMenuItemCenter);
QQuickPopup *settingsDialog = window->contentItem()->findChild<QQuickPopup*>("settingsDialog");
QVERIFY(settingsDialog);
QTRY_VERIFY(settingsDialog->isVisible());
QQuickComboBox *comboBox = window->contentItem()->findChild<QQuickComboBox*>("comboBox");
QVERIFY(comboBox);
// Click on the combo box button. The combo box popup should pop up.
const QPoint comboBoxCenter = comboBox->mapToScene(
QPointF(comboBox->width() / 2, comboBox->height() / 2)).toPoint();
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, comboBoxCenter);
QTRY_VERIFY(comboBox->popup()->isVisible());
// Close the combo box popup with the escape key. The settings dialog should still be visible.
QTest::keyClick(window, Qt::Key_Escape);
QTRY_VERIFY(!comboBox->popup()->isVisible());
QVERIFY(settingsDialog->isVisible());
// Close the settings dialog with the escape key.
QTest::keyClick(window, Qt::Key_Escape);
QTRY_VERIFY(!settingsDialog->isVisible());
// The stack view should still have two items.
QCOMPARE(stackView->depth(), 2);
// Remove one by pressing the Escape key (the Shortcut should be activated).
QTest::keyClick(window, Qt::Key_Escape);
QCOMPARE(stackView->depth(), 1);
}
void tst_QQuickPopup::closeOnEscapeWithVisiblePopup()
{
QQuickApplicationHelper helper(this, QStringLiteral("closeOnEscapeWithVisiblePopup.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->findChild<QQuickPopup *>("popup");
QVERIFY(popup);
QTRY_VERIFY(popup->isOpened());
QTRY_VERIFY(window->activeFocusItem());
QTest::keyClick(window, Qt::Key_Escape);
QTRY_VERIFY(!popup->isVisible());
}
void tst_QQuickPopup::enabled()
{
QQuickPopup popup;
QVERIFY(popup.isEnabled());
QVERIFY(popup.popupItem()->isEnabled());
QSignalSpy enabledSpy(&popup, &QQuickPopup::enabledChanged);
QVERIFY(enabledSpy.isValid());
popup.setEnabled(false);
QVERIFY(!popup.isEnabled());
QVERIFY(!popup.popupItem()->isEnabled());
QCOMPARE(enabledSpy.count(), 1);
popup.popupItem()->setEnabled(true);
QVERIFY(popup.isEnabled());
QVERIFY(popup.popupItem()->isEnabled());
QCOMPARE(enabledSpy.count(), 2);
}
void tst_QQuickPopup::orientation_data()
{
QTest::addColumn<Qt::ScreenOrientation>("orientation");
QTest::addColumn<QPointF>("position");
QTest::newRow("Portrait") << Qt::PortraitOrientation << QPointF(330, 165);
QTest::newRow("Landscape") << Qt::LandscapeOrientation << QPointF(165, 270);
QTest::newRow("InvertedPortrait") << Qt::InvertedPortraitOrientation << QPointF(270, 135);
QTest::newRow("InvertedLandscape") << Qt::InvertedLandscapeOrientation << QPointF(135, 330);
}
void tst_QQuickPopup::orientation()
{
QFETCH(Qt::ScreenOrientation, orientation);
QFETCH(QPointF, position);
QQuickApplicationHelper helper(this, "orientation.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->reportContentOrientationChange(orientation);
window->show();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
popup->open();
QCOMPARE(popup->popupItem()->position(), position);
}
void tst_QQuickPopup::qquickview()
{
QQuickView view;
view.setObjectName("QQuickView");
view.resize(400, 400);
view.setSource(testFileUrl("dialog.qml"));
QVERIFY(view.status() != QQuickView::Error);
view.contentItem()->setObjectName("QQuickViewContentItem");
view.show();
QQuickDialog *dialog = view.rootObject()->property("dialog").value<QQuickDialog*>();
QVERIFY(dialog);
QTRY_COMPARE(dialog->property("opened").toBool(), true);
dialog->close();
QTRY_COMPARE(dialog->property("visible").toBool(), false);
// QTBUG-72746: shouldn't crash on application exit after closing a Dialog when using QQuickView.
}
// TODO: also test it out without setting enabled directly on menu, but on a parent
// QTBUG-73447
void tst_QQuickPopup::disabledPalette()
{
QQuickApplicationHelper helper(this, "disabledPalette.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
QSignalSpy popupEnabledSpy(popup, SIGNAL(enabledChanged()));
QVERIFY(popupEnabledSpy.isValid());
QSignalSpy popupPaletteSpy(popup, SIGNAL(paletteChanged()));
QVERIFY(popupPaletteSpy.isValid());
QSignalSpy popupItemEnabledSpy(popup->popupItem(), SIGNAL(enabledChanged()));
QVERIFY(popupItemEnabledSpy.isValid());
QSignalSpy popupItemPaletteSpy(popup->popupItem(), SIGNAL(paletteChanged()));
QVERIFY(popupItemPaletteSpy.isValid());
QPalette palette = popup->palette();
palette.setColor(QPalette::Active, QPalette::Base, Qt::green);
palette.setColor(QPalette::Disabled, QPalette::Base, Qt::red);
popup->setPalette(palette);
QCOMPARE(popupPaletteSpy.count(), 1);
QCOMPARE(popupItemPaletteSpy.count(), 1);
QCOMPARE(popup->background()->property("color").value<QColor>(), Qt::green);
popup->setEnabled(false);
QCOMPARE(popupEnabledSpy.count(), 1);
QCOMPARE(popupItemEnabledSpy.count(), 1);
QCOMPARE(popupPaletteSpy.count(), 2);
QCOMPARE(popupItemPaletteSpy.count(), 2);
QCOMPARE(popup->background()->property("color").value<QColor>(), Qt::red);
}
void tst_QQuickPopup::disabledParentPalette()
{
QQuickApplicationHelper helper(this, "disabledPalette.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
QSignalSpy popupEnabledSpy(popup, SIGNAL(enabledChanged()));
QVERIFY(popupEnabledSpy.isValid());
QSignalSpy popupPaletteSpy(popup, SIGNAL(paletteChanged()));
QVERIFY(popupPaletteSpy.isValid());
QSignalSpy popupItemEnabledSpy(popup->popupItem(), SIGNAL(enabledChanged()));
QVERIFY(popupItemEnabledSpy.isValid());
QSignalSpy popupItemPaletteSpy(popup->popupItem(), SIGNAL(paletteChanged()));
QVERIFY(popupItemPaletteSpy.isValid());
QPalette palette = popup->palette();
palette.setColor(QPalette::Active, QPalette::Base, Qt::green);
palette.setColor(QPalette::Disabled, QPalette::Base, Qt::red);
popup->setPalette(palette);
QCOMPARE(popupPaletteSpy.count(), 1);
QCOMPARE(popupItemPaletteSpy.count(), 1);
QCOMPARE(popup->background()->property("color").value<QColor>(), Qt::green);
// Disable the overlay (which is QQuickPopupItem's parent) to ensure that
// the palette is changed when the popup is indirectly disabled.
popup->open();
QTRY_VERIFY(popup->isOpened());
QVERIFY(QMetaObject::invokeMethod(window, "disableOverlay"));
QVERIFY(!popup->isEnabled());
QVERIFY(!popup->popupItem()->isEnabled());
QCOMPARE(popup->background()->property("color").value<QColor>(), Qt::red);
QCOMPARE(popupEnabledSpy.count(), 1);
QCOMPARE(popupItemEnabledSpy.count(), 1);
QCOMPARE(popupPaletteSpy.count(), 2);
QCOMPARE(popupItemPaletteSpy.count(), 2);
popup->close();
QTRY_VERIFY(!popup->isVisible());
}
void tst_QQuickPopup::countChanged()
{
QQuickApplicationHelper helper(this, "countChanged.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickComboBox *comboBox = window->property("comboBox").value<QQuickComboBox*>();
QVERIFY(comboBox);
QCOMPARE(window->property("count").toInt(), 1);
QVERIFY(window->setProperty("isModel1", false));
QTRY_COMPARE(window->property("count").toInt(), 2);
}
// QTBUG-73243
void tst_QQuickPopup::toolTipCrashOnClose()
{
QQuickApplicationHelper helper(this, "toolTipCrashOnClose.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
// The warning only occurs with debug builds for some reason.
// In any case, the warning is irrelevant, but using ShaderEffectSource is important, so we ignore it.
#ifdef QT_DEBUG
QTest::ignoreMessage(QtWarningMsg, "ShaderEffectSource: 'recursive' must be set to true when rendering recursively.");
#endif
QVERIFY(QTest::qWaitForWindowActive(window));
QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2));
QTRY_VERIFY(window->property("toolTipOpened").toBool());
QVERIFY(window->close());
// Shouldn't crash.
}
void tst_QQuickPopup::setOverlayParentToNull()
{
QQuickApplicationHelper helper(this, "toolTipCrashOnClose.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
centerOnScreen(window);
moveMouseAway(window);
window->show();
#ifdef QT_DEBUG
QTest::ignoreMessage(QtWarningMsg, "ShaderEffectSource: 'recursive' must be set to true when rendering recursively.");
#endif
QVERIFY(QTest::qWaitForWindowActive(window));
QVERIFY(QMetaObject::invokeMethod(window, "nullifyOverlayParent"));
QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2));
QTRY_VERIFY(window->property("toolTipOpened").toBool());
QVERIFY(window->close());
// While nullifying the overlay parent doesn't make much sense, it shouldn't crash.
}
void tst_QQuickPopup::tabFence()
{
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
QQuickApplicationHelper helper(this, "tabFence.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickPopup *popup = window->property("dialog").value<QQuickPopup*>();
QVERIFY(popup);
popup->open();
popup->setModal(true);
QQuickButton *outsideButton1 = window->property("outsideButton1").value<QQuickButton*>();
QVERIFY(outsideButton1);
QQuickButton *outsideButton2 = window->property("outsideButton2").value<QQuickButton*>();
QVERIFY(outsideButton2);
QQuickButton *dialogButton1 = window->property("dialogButton1").value<QQuickButton*>();
QVERIFY(dialogButton1);
QQuickButton *dialogButton2 = window->property("dialogButton2").value<QQuickButton*>();
QVERIFY(dialogButton2);
// When modal, focus loops between the two external buttons
outsideButton1->forceActiveFocus();
QVERIFY(outsideButton1->hasActiveFocus());
QTest::keyClick(window, Qt::Key_Tab);
QVERIFY(outsideButton2->hasActiveFocus());
QTest::keyClick(window, Qt::Key_Tab);
QVERIFY(outsideButton1->hasActiveFocus());
// Same thing for dialog's buttons
dialogButton1->forceActiveFocus();
QVERIFY(dialogButton1->hasActiveFocus());
QTest::keyClick(window, Qt::Key_Tab);
QVERIFY(dialogButton2->hasActiveFocus());
QTest::keyClick(window, Qt::Key_Tab);
QVERIFY(dialogButton1->hasActiveFocus());
popup->setModal(false);
// When not modal, focus goes in and out of the dialog
outsideButton1->forceActiveFocus();
QVERIFY(outsideButton1->hasActiveFocus());
QTest::keyClick(window, Qt::Key_Tab);
QVERIFY(outsideButton2->hasActiveFocus());
QTest::keyClick(window, Qt::Key_Tab);
QVERIFY(dialogButton1->hasActiveFocus());
QTest::keyClick(window, Qt::Key_Tab);
QVERIFY(dialogButton2->hasActiveFocus());
QTest::keyClick(window, Qt::Key_Tab);
QVERIFY(outsideButton1->hasActiveFocus());
}
void tst_QQuickPopup::invisibleToolTipOpen()
{
QQuickApplicationHelper helper(this, "invisibleToolTipOpen.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
centerOnScreen(window);
moveMouseAway(window);
window->show();
QVERIFY(QTest::qWaitForWindowActive(window));
QQuickItem *mouseArea = qvariant_cast<QQuickItem *>(window->property("mouseArea"));
QVERIFY(mouseArea);
QObject *loader = qvariant_cast<QObject *>(window->property("loader"));
QVERIFY(loader);
QTest::mouseMove(window, QPoint(mouseArea->width() / 2, mouseArea->height() / 2));
QTRY_VERIFY(mouseArea->property("isToolTipVisible").toBool());
QSignalSpy componentLoadedSpy(loader, SIGNAL(loaded()));
QVERIFY(componentLoadedSpy.isValid());
loader->setProperty("active", true);
QTRY_COMPARE(componentLoadedSpy.count(), 1);
QTRY_VERIFY(mouseArea->property("isToolTipVisible").toBool());
}
void tst_QQuickPopup::centerInOverlayWithinStackViewItem()
{
QQuickApplicationHelper helper(this, "centerInOverlayWithinStackViewItem.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
QTRY_COMPARE(popup->isVisible(), true);
// Shouldn't crash on exit.
}
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"