From 5173dfa1ca6e5d4651cb41ae4c0d8e6c4ae6b75b Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 17 Mar 2023 14:21:42 +0400 Subject: [PATCH] Workaround for emoji selector in popup menu on Windows. --- ui/platform/linux/ui_utility_linux.h | 3 ++ ui/platform/mac/ui_utility_mac.h | 3 ++ ui/platform/ui_platform_utility.h | 10 +++-- ui/platform/win/ui_utility_win.cpp | 63 ++++++++++++++++++++++++++-- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/ui/platform/linux/ui_utility_linux.h b/ui/platform/linux/ui_utility_linux.h index ce28a8e..f2b58a7 100644 --- a/ui/platform/linux/ui_utility_linux.h +++ b/ui/platform/linux/ui_utility_linux.h @@ -36,5 +36,8 @@ inline constexpr bool UseMainQueueGeneric() { return true; } +inline void FixPopupMenuNativeEmojiPopup(not_null menu) { +} + } // namespace Platform } // namespace Ui diff --git a/ui/platform/mac/ui_utility_mac.h b/ui/platform/mac/ui_utility_mac.h index 57abb69..04170f3 100644 --- a/ui/platform/mac/ui_utility_mac.h +++ b/ui/platform/mac/ui_utility_mac.h @@ -40,5 +40,8 @@ inline void UnsetWindowExtents(not_null widget) { inline void ShowWindowMenu(not_null widget, const QPoint &point) { } +inline void FixPopupMenuNativeEmojiPopup(not_null menu) { +} + } // namespace Platform } // namespace Ui diff --git a/ui/platform/ui_platform_utility.h b/ui/platform/ui_platform_utility.h index 2d10077..db24bcb 100644 --- a/ui/platform/ui_platform_utility.h +++ b/ui/platform/ui_platform_utility.h @@ -13,7 +13,10 @@ class QPainter; class QPaintEvent; namespace Ui { -namespace Platform { +class PopupMenu; +} // namespace Ui + +namespace Ui::Platform { [[nodiscard]] bool IsApplicationActive(); @@ -47,8 +50,9 @@ void ShowWindowMenu(not_null widget, const QPoint &point); [[nodiscard]] rpl::producer TitleControlsLayoutChanged(); void NotifyTitleControlsLayoutChanged(); -} // namespace Platform -} // namespace Ui +void FixPopupMenuNativeEmojiPopup(not_null menu); + +} // namespace Ui::Platform // Platform dependent implementations. diff --git a/ui/platform/win/ui_utility_win.cpp b/ui/platform/win/ui_utility_win.cpp index 6c42a73..90b9108 100644 --- a/ui/platform/win/ui_utility_win.cpp +++ b/ui/platform/win/ui_utility_win.cpp @@ -7,17 +7,18 @@ #include "ui/platform/win/ui_utility_win.h" #include "base/platform/win/base_windows_h.h" +#include "ui/widgets/popup_menu.h" #include #include +#include #include #include using namespace Microsoft::WRL; -namespace Ui { -namespace Platform { +namespace Ui::Platform { bool IsApplicationActive() { return QApplication::activeWindow() != nullptr; @@ -143,5 +144,59 @@ TitleControls::Layout TitleControlsLayout() { }; } -} // namespace Platform -} // namespace Ui +void FixPopupMenuNativeEmojiPopup(not_null menu) { + // Windows native emoji selector, that can be called by Win+. shortcut, + // is behaving strangely within an input field in a popup menu. + // + // When the selector is shown and a mouse button is pressed the system + // sends two events "MousePress + MouseRelease" to the popup menu, even + // before the button is physically released. That way we hide the menu + // on this MousePress, that we shouldn't have received (in case of + // input field in the main window no such events are sent at all). + // + // To workaround this we detect a WM_MOUSELEAVE event that is sent to + // the popup menu when the selector is shown and skip all mouse press + // events while we don't receive mouse move events. If we receive mouse + // move events that means the selector was hidden and the mouse is + // captured by the popup menu again. + class Filter final : public QAbstractNativeEventFilter { + public: + explicit Filter(not_null menu) : _menu(menu) { + } + + bool nativeEventFilter( + const QByteArray &eventType, + void *message, + long *result) override { + const auto msg = static_cast(message); + switch (msg->message) { + case WM_MOUSELEAVE: if (msg->hwnd == hwnd()) { + _skipMouseDown = true; + } break; + case WM_MOUSEMOVE: if (msg->hwnd == hwnd()) { + _skipMouseDown = false; + } break; + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: if (msg->hwnd == hwnd()) { + return _skipMouseDown; + } + } + return false; + } + + private: + [[nodiscard]] HWND hwnd() const { + const auto top = _menu->window()->windowHandle(); + return top ? reinterpret_cast(top->winId()) : nullptr; + } + + not_null _menu; + bool _skipMouseDown = false; + + }; + + QGuiApplication::instance()->installNativeEventFilter( + menu->lifetime().make_state(menu)); +} + +} // namespace Ui::Platform