Only close popup in the the hierchary
Imagine following event sequences: 1. a tooltip is shown. activePopups = {tooltip} 2. user click menu bar to show the menu, QMenu::setVisible is called. now activePopups(tooltip, menu} 3. tooltip visibility changed to false. 4. closePopups() close both tooltip and menu. This is a common pattern under wayland that menu is shown as a invisible state. This patch tries to memorize the surface hierchary used to create the popup role. And only close those popups whose ancesotor is hidden. Pick-to: 6.4 Change-Id: I78aa0b4e32a5812603e003e756d8bcd202e94af4 Reviewed-by: David Edmundson <davidedmundson@kde.org>
This commit is contained in:
parent
6939f52984
commit
959134c4de
@ -174,12 +174,17 @@ QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResi
|
|||||||
| ((edges & Qt::RightEdge) ? resize_edge_right : 0));
|
| ((edges & Qt::RightEdge) ? resize_edge_right : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent,
|
QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent,
|
||||||
QtWayland::xdg_positioner *positioner)
|
QtWayland::xdg_positioner *positioner)
|
||||||
: xdg_popup(xdgSurface->get_popup(parent ? parent->object() : nullptr, positioner->object()))
|
: m_xdgSurface(xdgSurface)
|
||||||
, m_xdgSurface(xdgSurface)
|
, m_parentXdgSurface(qobject_cast<QWaylandXdgSurface *>(parent->shellSurface()))
|
||||||
, m_parent(parent)
|
, m_parent(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
init(xdgSurface->get_popup(m_parentXdgSurface ? m_parentXdgSurface->object() : nullptr, positioner->object()));
|
||||||
|
if (m_parent) {
|
||||||
|
m_parent->addChildPopup(m_xdgSurface->window());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QWaylandXdgSurface::Popup::~Popup()
|
QWaylandXdgSurface::Popup::~Popup()
|
||||||
@ -187,10 +192,14 @@ QWaylandXdgSurface::Popup::~Popup()
|
|||||||
if (isInitialized())
|
if (isInitialized())
|
||||||
destroy();
|
destroy();
|
||||||
|
|
||||||
|
if (m_parent) {
|
||||||
|
m_parent->removeChildPopup(m_xdgSurface->window());
|
||||||
|
}
|
||||||
|
|
||||||
if (m_grabbing) {
|
if (m_grabbing) {
|
||||||
auto *shell = m_xdgSurface->m_shell;
|
auto *shell = m_xdgSurface->m_shell;
|
||||||
Q_ASSERT(shell->m_topmostGrabbingPopup == this);
|
Q_ASSERT(shell->m_topmostGrabbingPopup == this);
|
||||||
shell->m_topmostGrabbingPopup = m_parent ? m_parent->m_popup : nullptr;
|
shell->m_topmostGrabbingPopup = m_parentXdgSurface ? m_parentXdgSurface->m_popup : nullptr;
|
||||||
m_grabbing = false;
|
m_grabbing = false;
|
||||||
|
|
||||||
// Synthesize Qt enter/leave events for popup
|
// Synthesize Qt enter/leave events for popup
|
||||||
@ -396,8 +405,6 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent)
|
|||||||
{
|
{
|
||||||
Q_ASSERT(!m_toplevel && !m_popup);
|
Q_ASSERT(!m_toplevel && !m_popup);
|
||||||
|
|
||||||
auto parentXdgSurface = qobject_cast<QWaylandXdgSurface *>(parent->shellSurface());
|
|
||||||
|
|
||||||
auto positioner = new QtWayland::xdg_positioner(m_shell->create_positioner());
|
auto positioner = new QtWayland::xdg_positioner(m_shell->create_positioner());
|
||||||
// set_popup expects a position relative to the parent
|
// set_popup expects a position relative to the parent
|
||||||
QPoint transientPos = m_window->geometry().topLeft(); // this is absolute
|
QPoint transientPos = m_window->geometry().topLeft(); // this is absolute
|
||||||
@ -414,8 +421,9 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent)
|
|||||||
| QtWayland::xdg_positioner::constraint_adjustment_slide_y
|
| QtWayland::xdg_positioner::constraint_adjustment_slide_y
|
||||||
| QtWayland::xdg_positioner::constraint_adjustment_flip_x
|
| QtWayland::xdg_positioner::constraint_adjustment_flip_x
|
||||||
| QtWayland::xdg_positioner::constraint_adjustment_flip_y);
|
| QtWayland::xdg_positioner::constraint_adjustment_flip_y);
|
||||||
m_popup = new Popup(this, parentXdgSurface, positioner);
|
m_popup = new Popup(this, parent, positioner);
|
||||||
positioner->destroy();
|
positioner->destroy();
|
||||||
|
|
||||||
delete positioner;
|
delete positioner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,14 +102,15 @@ private:
|
|||||||
|
|
||||||
class Popup : public QtWayland::xdg_popup {
|
class Popup : public QtWayland::xdg_popup {
|
||||||
public:
|
public:
|
||||||
Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, QtWayland::xdg_positioner *positioner);
|
Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent, QtWayland::xdg_positioner *positioner);
|
||||||
~Popup() override;
|
~Popup() override;
|
||||||
|
|
||||||
void grab(QWaylandInputDevice *seat, uint serial);
|
void grab(QWaylandInputDevice *seat, uint serial);
|
||||||
void xdg_popup_popup_done() override;
|
void xdg_popup_popup_done() override;
|
||||||
|
|
||||||
QWaylandXdgSurface *m_xdgSurface = nullptr;
|
QWaylandXdgSurface *m_xdgSurface = nullptr;
|
||||||
QWaylandXdgSurface *m_parent = nullptr;
|
QWaylandXdgSurface *m_parentXdgSurface = nullptr;
|
||||||
|
QWaylandWindow *m_parent = nullptr;
|
||||||
bool m_grabbing = false;
|
bool m_grabbing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -215,6 +215,7 @@ void QWaylandWindow::endFrame()
|
|||||||
|
|
||||||
void QWaylandWindow::reset()
|
void QWaylandWindow::reset()
|
||||||
{
|
{
|
||||||
|
closeChildPopups();
|
||||||
delete mShellSurface;
|
delete mShellSurface;
|
||||||
mShellSurface = nullptr;
|
mShellSurface = nullptr;
|
||||||
delete mSubSurfaceWindow;
|
delete mSubSurfaceWindow;
|
||||||
@ -429,20 +430,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
|
|||||||
mLastExposeGeometry = rect;
|
mLastExposeGeometry = rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QList<QPointer<QWaylandWindow>> activePopups;
|
|
||||||
|
|
||||||
void QWaylandWindow::closePopups(QWaylandWindow *parent)
|
|
||||||
{
|
|
||||||
while (!activePopups.isEmpty()) {
|
|
||||||
auto popup = activePopups.takeLast();
|
|
||||||
if (popup.isNull())
|
|
||||||
continue;
|
|
||||||
if (popup.data() == parent)
|
|
||||||
return;
|
|
||||||
popup->reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
|
QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
|
||||||
{
|
{
|
||||||
QReadLocker lock(&mSurfaceLock);
|
QReadLocker lock(&mSurfaceLock);
|
||||||
@ -462,8 +449,6 @@ void QWaylandWindow::setVisible(bool visible)
|
|||||||
lastVisible = visible;
|
lastVisible = visible;
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip)
|
|
||||||
activePopups << this;
|
|
||||||
initWindow();
|
initWindow();
|
||||||
|
|
||||||
setGeometry(windowGeometry());
|
setGeometry(windowGeometry());
|
||||||
@ -472,7 +457,6 @@ void QWaylandWindow::setVisible(bool visible)
|
|||||||
// QWaylandShmBackingStore::beginPaint().
|
// QWaylandShmBackingStore::beginPaint().
|
||||||
} else {
|
} else {
|
||||||
sendExposeEvent(QRect());
|
sendExposeEvent(QRect());
|
||||||
closePopups(this);
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1520,6 +1504,21 @@ void QWaylandWindow::setXdgActivationToken(const QString &token)
|
|||||||
{
|
{
|
||||||
mShellSurface->setXdgActivationToken(token);
|
mShellSurface->setXdgActivationToken(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QWaylandWindow::addChildPopup(QWaylandWindow *surface) {
|
||||||
|
mChildPopups.append(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWaylandWindow::removeChildPopup(QWaylandWindow *surface) {
|
||||||
|
mChildPopups.removeAll(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWaylandWindow::closeChildPopups() {
|
||||||
|
while (!mChildPopups.isEmpty()) {
|
||||||
|
auto popup = mChildPopups.takeLast();
|
||||||
|
popup->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -206,6 +206,10 @@ public:
|
|||||||
void beginFrame();
|
void beginFrame();
|
||||||
void endFrame();
|
void endFrame();
|
||||||
|
|
||||||
|
void addChildPopup(QWaylandWindow* child);
|
||||||
|
void removeChildPopup(QWaylandWindow* child);
|
||||||
|
void closeChildPopups();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void applyConfigure();
|
void applyConfigure();
|
||||||
|
|
||||||
@ -292,6 +296,8 @@ protected:
|
|||||||
|
|
||||||
QMargins mCustomMargins;
|
QMargins mCustomMargins;
|
||||||
|
|
||||||
|
QList<QPointer<QWaylandWindow>> mChildPopups;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setGeometry_helper(const QRect &rect);
|
void setGeometry_helper(const QRect &rect);
|
||||||
void initWindow();
|
void initWindow();
|
||||||
|
@ -21,6 +21,7 @@ private slots:
|
|||||||
void configureStates();
|
void configureStates();
|
||||||
void popup();
|
void popup();
|
||||||
void tooltipOnPopup();
|
void tooltipOnPopup();
|
||||||
|
void tooltipAndSiblingPopup();
|
||||||
void switchPopups();
|
void switchPopups();
|
||||||
void hidePopupParent();
|
void hidePopupParent();
|
||||||
void pongs();
|
void pongs();
|
||||||
@ -321,6 +322,92 @@ void tst_xdgshell::tooltipOnPopup()
|
|||||||
QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
|
QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_xdgshell::tooltipAndSiblingPopup()
|
||||||
|
{
|
||||||
|
class ToolTip : public QRasterWindow {
|
||||||
|
public:
|
||||||
|
explicit ToolTip(QWindow *parent) {
|
||||||
|
setTransientParent(parent);
|
||||||
|
setFlags(Qt::ToolTip);
|
||||||
|
resize(100, 100);
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
void mousePressEvent(QMouseEvent *event) override {
|
||||||
|
QRasterWindow::mousePressEvent(event);
|
||||||
|
m_popup = new QRasterWindow;
|
||||||
|
m_popup->setTransientParent(transientParent());
|
||||||
|
m_popup->setFlags(Qt::Popup);
|
||||||
|
m_popup->resize(100, 100);
|
||||||
|
m_popup->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRasterWindow *m_popup = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Window : public QRasterWindow {
|
||||||
|
public:
|
||||||
|
void mousePressEvent(QMouseEvent *event) override {
|
||||||
|
QRasterWindow::mousePressEvent(event);
|
||||||
|
m_tooltip = new ToolTip(this);
|
||||||
|
}
|
||||||
|
ToolTip *m_tooltip = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
Window window;
|
||||||
|
window.resize(200, 200);
|
||||||
|
window.show();
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
|
||||||
|
exec([=] { xdgToplevel()->sendCompleteConfigure(); });
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
|
||||||
|
|
||||||
|
exec([=] {
|
||||||
|
auto *surface = xdgToplevel()->surface();
|
||||||
|
auto *p = pointer();
|
||||||
|
auto *c = client();
|
||||||
|
p->sendEnter(surface, {100, 100});
|
||||||
|
p->sendFrame(c);
|
||||||
|
p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
|
||||||
|
p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
|
||||||
|
p->sendFrame(c);
|
||||||
|
p->sendLeave(surface);
|
||||||
|
p->sendFrame(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgPopup());
|
||||||
|
exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(!xdgPopup()->m_grabbed);
|
||||||
|
|
||||||
|
exec([=] {
|
||||||
|
auto *surface = xdgPopup()->surface();
|
||||||
|
auto *p = pointer();
|
||||||
|
auto *c = client();
|
||||||
|
p->sendEnter(surface, {100, 100});
|
||||||
|
p->sendFrame(c);
|
||||||
|
p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
|
||||||
|
p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
|
||||||
|
p->sendFrame(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgPopup(1));
|
||||||
|
exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial);
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_grabbed);
|
||||||
|
|
||||||
|
// Close the middle tooltip (it should not close the sibling popup)
|
||||||
|
window.m_tooltip->close();
|
||||||
|
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr);
|
||||||
|
// Verify the remaining xdg surface is a grab popup..
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgPopup(0));
|
||||||
|
QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)->m_grabbed);
|
||||||
|
|
||||||
|
window.m_tooltip->m_popup->close();
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr);
|
||||||
|
QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// QTBUG-65680
|
// QTBUG-65680
|
||||||
void tst_xdgshell::switchPopups()
|
void tst_xdgshell::switchPopups()
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user