Workaround Wayland popup menu bug.

When hiding a child popup first the app receives ApplicationDeactivate
event and in a short time (a couple of ms) ApplicationActivate.

But the first event hides all popups, so the parent popup gets closed too.

Delay handling of ApplicationDeactivate event in this specific case.
This commit is contained in:
John Preston 2023-07-12 22:03:35 +04:00
parent d04a38e15d
commit 8db6dcf125
5 changed files with 63 additions and 1 deletions

View file

@ -9,8 +9,9 @@
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_linux_glibmm_helper.h"
#include "base/platform/linux/base_linux_xdp_utilities.h"
#include "ui/platform/linux/ui_linux_wayland_integration.h"
#include "base/call_delayed.h"
#include "base/const_string.h"
#include "ui/platform/linux/ui_linux_wayland_integration.h"
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
#include "base/platform/linux/base_linux_xcb_utilities.h"
@ -26,6 +27,10 @@ namespace Platform {
namespace {
constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs;
constexpr auto kDelayDeactivateEventTimeout = crl::time(400);
bool PendingDeactivateEvent/* = false*/;
int ChildPopupsHiddenOnWayland/* = 0*/;
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
std::optional<bool> XCBWindowMapped(xcb_window_t window) {
@ -548,6 +553,34 @@ void ShowWindowMenu(not_null<QWidget*> widget, const QPoint &point) {
}
}
void RegisterChildPopupHiding() {
if (!::Platform::IsWayland()) {
return;
}
++ChildPopupsHiddenOnWayland;
base::call_delayed(kDelayDeactivateEventTimeout, [] {
if (!--ChildPopupsHiddenOnWayland) {
if (base::take(PendingDeactivateEvent)) {
// We didn't receive ApplicationActivate event in time.
QEvent appDeactivate(QEvent::ApplicationDeactivate);
QCoreApplication::sendEvent(qApp, &appDeactivate);
}
}
});
}
bool SkipApplicationDeactivateEvent() {
if (!ChildPopupsHiddenOnWayland) {
return false;
}
PendingDeactivateEvent = true;
return true;
}
void GotApplicationActivateEvent() {
PendingDeactivateEvent = false;
}
namespace internal {
TitleControls::Layout TitleControlsLayout() {

View file

@ -147,6 +147,16 @@ std::optional<bool> IsOverlapped(
return false;
}
void RegisterChildPopupHiding() {
}
bool SkipApplicationDeactivateEvent() {
return false;
}
void GotApplicationActivateEvent() {
}
namespace internal {
TitleControls::Layout TitleControlsLayout() {

View file

@ -58,6 +58,12 @@ void ShowWindowMenu(not_null<QWidget*> widget, const QPoint &point);
void FixPopupMenuNativeEmojiPopup(not_null<PopupMenu*> menu);
// Workaround for a Qt/Wayland bug that hides the parent popup when
// the child popup gets hidden, by sending Deactivate / Activate events.
void RegisterChildPopupHiding();
[[nodiscard]] bool SkipApplicationDeactivateEvent();
void GotApplicationActivateEvent();
} // namespace Ui::Platform
// Platform dependent implementations.

View file

@ -203,4 +203,14 @@ void FixPopupMenuNativeEmojiPopup(not_null<PopupMenu*> menu) {
menu->lifetime().make_state<Filter>(menu));
}
void RegisterChildPopupHiding() {
}
bool SkipApplicationDeactivateEvent() {
return false;
}
void GotApplicationActivateEvent() {
}
} // namespace Ui::Platform

View file

@ -673,6 +673,9 @@ bool PopupMenu::eventFilter(QObject *o, QEvent *e) {
}
void PopupMenu::hideMenu(bool fast) {
if (fast && _parent) {
Platform::RegisterChildPopupHiding();
}
if (isHidden() || (_hiding && !fast)) {
return;
}