Drop workaround to turn popups into main windows

On Wayland there are additional requirements when making popups to Qt.

In regular cases we can guess what the popup parent should be based on
which window last received input, but as it's a heuristic with obscure
enough timing events this can sometimes fail.

In this situation the current code creates a toplevel instead of a
regular popup, having a popup take focus with a window decoration is
rarely a good UX. Rather than making an incorrect window, the new
approach is to not create a popup over the wire and synthesise an
immediate close event. Application code should be able to handle this as
it's the same path if the popup was denied server side.

Pick-to: 6.9
Task-number: QTBUG-136343
Change-Id: Ia76c36a047e93f2469a8bf0f4b94642f05179124
Reviewed-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
This commit is contained in:
David Edmundson 2025-04-24 18:07:51 +02:00
parent 991c83cc96
commit 8c0dd12f4b
2 changed files with 54 additions and 4 deletions

View File

@ -314,10 +314,20 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *s
Qt::WindowType type = window->window()->type();
auto *transientParent = window->transientParent();
if (type == Qt::ToolTip && transientParent) {
setPopup(transientParent);
} else if (type == Qt::Popup && transientParent && display->lastInputDevice()) {
setGrabPopup(transientParent, display->lastInputDevice(), display->lastInputSerial());
if (type == Qt::ToolTip) {
if (transientParent) {
setPopup(transientParent);
} else {
qCWarning(lcQpaWayland) << "Failed to create popup. Ensure popup " << window->window() << "has a transientParent set.";
QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::AsynchronousDelivery>(m_window->window());
}
} else if (type == Qt::Popup ) {
if (transientParent && display->lastInputDevice()) {
setGrabPopup(transientParent, display->lastInputDevice(), display->lastInputSerial());
} else {
qCWarning(lcQpaWayland) << "Failed to create grabbing popup. Ensure popup " << window->window() << "has a transientParent set and that parent window has received input.";
QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::AsynchronousDelivery>(m_window->window());
}
} else {
setToplevel();
}

View File

@ -26,6 +26,7 @@ private slots:
void tooltipAndSiblingPopup();
void switchPopups();
void hidePopupParent();
void popupsWithoutParent();
void pongs();
void minMaxSize_data();
void minMaxSize();
@ -608,6 +609,45 @@ void tst_xdgshell::hidePopupParent()
QCOMPOSITOR_TRY_VERIFY(!xdgToplevel());
}
void tst_xdgshell::popupsWithoutParent()
{
QRasterWindow popup;
QSignalSpy popupDoneSpy(&popup, &QWindow::visibilityChanged);
popup.setFlags(Qt::Popup);
popup.resize(100, 100);
popup.show();
QVERIFY(popup.isVisible());
// popup cannot be created within the spec, so it gets auto-dismissed
QVERIFY(popupDoneSpy.wait());
QVERIFY(!popup.isVisible());
QCOMPOSITOR_VERIFY(!xdgToplevel());
// now make a normal window with an input event
QRasterWindow window;
window.setTitle("main window");
window.resize(200, 200);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
exec([&] { xdgToplevel()->sendCompleteConfigure(); });
QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
exec([&] {
keyboard()->sendEnter(xdgToplevel()->surface());
keyboard()->sendKey(client(), 72, Keyboard::key_state_pressed); // related with native scan code
keyboard()->sendKey(client(), 72, Keyboard::key_state_released); // related with native scan code
});
QTRY_COMPARE(qGuiApp->focusWindow(), &window);
// now re-show our popup, it should be able to guess a transient this time
// and correctly show as a popup
popup.show();
QCOMPOSITOR_TRY_VERIFY(xdgPopup());
exec([&] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
}
void tst_xdgshell::pongs()
{
// Create and show a window to trigger shell integration initialzation,