Popup: fix fading in and out of the dimmer

When showing and hiding the overlay we go through QQmlProperty to enable
behaviors, e.g. for fading the dimmer in and out, but since we already
set the opacity to 1.0 when creating the dimmer any fade-in behavior is
ignored (as the property already has the target value).

Fix this by creating the dimmer with opacity 0, and calling showOverlay
to make it visible if the popup is already visible. In addition, store
the target opacity of the dimmer component, and set opacity to that
value instead of 1.0 to respect an explicitly set opacity on the modal
or modeless item.

Add a test to verify that we respect the opacity that is set on the
attached modal or modeless dimmer item, and that we correctly trigger
behaviors on the dimmer item. For that to work, the exit transition of
the popup itself needs to be at least as long as the behavior's
duration.

Fixes: QTBUG-118461
Pick-to: 6.6
Change-Id: I5259ea54f7e4a98bf671e8b52c26676d591a0176
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Reviewed-by: Hatem ElKharashy <hatem.elkharashy@qt.io>
This commit is contained in:
Volker Hilsheimer 2023-11-07 16:00:27 +01:00
parent 9ca4969307
commit ef8bde838e
4 changed files with 93 additions and 3 deletions

View File

@ -841,7 +841,6 @@ static QQuickItem *createDimmer(QQmlComponent *component, QQuickPopup *popup, QQ
item = new QQuickItem;
if (item) {
item->setOpacity(popup->isVisible() ? 1.0 : 0.0);
item->setParentItem(parent);
item->stackBefore(popup->popupItem());
item->setZ(popup->z());
@ -883,8 +882,18 @@ void QQuickPopupPrivate::createOverlay()
if (!component)
component = modal ? overlay->modal() : overlay->modeless();
if (!dimmer)
if (!dimmer) {
dimmer = createDimmer(component, q, overlay);
// We cannot update explicitDimmerOpacity when dimmer's opacity changes,
// as it is expected to do so when we fade the dimmer in and out in
// show/hideOverlay, and any binding of the dimmer's opacity will be
// implicitly broken anyway.
explicitDimmerOpacity = dimmer->opacity();
// initially fully transparent, showOverlay fades the dimmer in.
dimmer->setOpacity(0);
if (q->isVisible())
showOverlay();
}
resizeOverlay();
}
@ -922,7 +931,7 @@ void QQuickPopupPrivate::showOverlay()
{
// use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors
if (dim && dimmer)
QQmlProperty::write(dimmer, QStringLiteral("opacity"), 1.0);
QQmlProperty::write(dimmer, QStringLiteral("opacity"), explicitDimmerOpacity);
}
void QQuickPopupPrivate::hideOverlay()

View File

@ -181,6 +181,7 @@ public:
QList<QQuickStateAction> exitActions;
QQuickPopupTransitionManager transitionManager;
QQuickPopupAnchors *anchors = nullptr;
qreal explicitDimmerOpacity = 0;
qreal prevOpacity = 0;
qreal prevScale = 0;

View File

@ -0,0 +1,39 @@
import QtQuick
import QtQuick.Controls
Window {
id: window
Text {text: "Hello"}
Popup {
id: popup
anchors.centerIn: parent
dim: true
property double dimmerOpacity: 0.5
Overlay.modeless: Rectangle {
color: "blue"
opacity: popup.dimmerOpacity
Behavior on opacity {
NumberAnimation { from: 0; to: popup.dimmerOpacity; duration: 250 }
}
}
Overlay.modal: Rectangle {
color: "blue"
opacity: popup.dimmerOpacity
Behavior on opacity {
NumberAnimation { from: 0; to: popup.dimmerOpacity; duration: 250 }
}
}
enter: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 250 }
}
exit: Transition {
NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 250 }
}
}
}

View File

@ -102,6 +102,8 @@ private slots:
void focusMultiplePopup();
void contentChildrenChange();
void doubleClickInMouseArea();
void fadeDimmer_data();
void fadeDimmer();
private:
static bool hasWindowActivation();
@ -2287,6 +2289,45 @@ void tst_QQuickPopup::doubleClickInMouseArea()
QCOMPARE(longPressSpy.count(), 0);
}
void tst_QQuickPopup::fadeDimmer_data()
{
QTest::addColumn<bool>("modality");
QTest::addRow("modal") << true;
QTest::addRow("modeless") << true;
}
void tst_QQuickPopup::fadeDimmer()
{
QFETCH(const bool, modality);
QQuickApplicationHelper helper(this, "fadeDimmer.qml");
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
auto *popup = window->contentItem()->findChild<QQuickPopup *>();
QVERIFY(popup);
popup->setModal(modality);
popup->open();
auto dimmer = QQuickPopupPrivate::get(popup)->dimmer;
QVERIFY(dimmer);
int opacityChangeCount = 0;
connect(dimmer, &QQuickItem::opacityChanged, this, [&opacityChangeCount]{
++opacityChangeCount;
});
QTRY_VERIFY(popup->isOpened());
QTRY_COMPARE(dimmer->opacity(), popup->property("dimmerOpacity").toDouble());
QCOMPARE_GT(opacityChangeCount, 2);
opacityChangeCount = 0;
popup->setVisible(false);
QTRY_VERIFY(!popup->isVisible());
QCOMPARE_GT(opacityChangeCount, 2);
}
QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
#include "tst_qquickpopup.moc"