From f4cf3094c29e445256bd1c198323d393be4405c1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Sep 2021 13:46:34 +0300 Subject: [PATCH] Force PopupMenu item selection when submenu is shown. --- ui/widgets/menu/menu.cpp | 7 ++++- ui/widgets/menu/menu.h | 10 +++---- ui/widgets/menu/menu_action.cpp | 6 ---- ui/widgets/popup_menu.cpp | 52 +++++++++++++++++++++++---------- ui/widgets/popup_menu.h | 2 ++ 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/ui/widgets/menu/menu.cpp b/ui/widgets/menu/menu.cpp index a87a642..1b34316 100644 --- a/ui/widgets/menu/menu.cpp +++ b/ui/widgets/menu/menu.cpp @@ -107,6 +107,11 @@ not_null Menu::addAction(base::unique_qptr widget) { widget->selects( ) | rpl::start_with_next([=](const CallbackData &data) { if (!data.selected) { + if (!findSelectedAction() + && data.index < _actionWidgets.size() + && _childShownAction == data.action) { + _actionWidgets[data.index]->setSelected(true); + } return; } for (auto i = 0; i < _actionWidgets.size(); i++) { @@ -282,7 +287,7 @@ void Menu::clearMouseSelection() { const auto mouseSelection = selected ? (selected->lastTriggeredSource() == TriggeredSource::Mouse) : false; - if (mouseSelection && !_childShown) { + if (mouseSelection && !_childShownAction) { clearSelection(); } } diff --git a/ui/widgets/menu/menu.h b/ui/widgets/menu/menu.h index 19b40fb..c28acec 100644 --- a/ui/widgets/menu/menu.h +++ b/ui/widgets/menu/menu.h @@ -45,8 +45,8 @@ public: void clearSelection(); - void setChildShown(bool shown) { - _childShown = shown; + void setChildShownAction(QAction *action) { + _childShownAction = action; } void setShowSource(TriggeredSource source); void setForceWidth(int forceWidth); @@ -60,6 +60,8 @@ public: _triggeredCallback = std::move(callback); } + [[nodiscard]] ItemBase *findSelectedAction() const; + void setKeyPressDelegate(Fn delegate) { _keyPressDelegate = std::move(delegate); } @@ -102,8 +104,6 @@ private: void itemPressed(TriggeredSource source); - ItemBase *findSelectedAction() const; - void resizeFromInner(int w, int h); const style::Menu &_st; @@ -121,7 +121,7 @@ private: int _forceWidth = 0; - bool _childShown = false; + QPointer _childShownAction; rpl::event_stream<> _resizesFromInner; diff --git a/ui/widgets/menu/menu_action.cpp b/ui/widgets/menu/menu_action.cpp index 2f1234d..2c3601d 100644 --- a/ui/widgets/menu/menu_action.cpp +++ b/ui/widgets/menu/menu_action.cpp @@ -189,12 +189,6 @@ void Action::handleKeyPress(not_null e) { setClicked(TriggeredSource::Keyboard); return; } - if (key == (style::RightToLeft() ? Qt::Key_Left : Qt::Key_Right)) { - if (hasSubmenu()) { - setClicked(TriggeredSource::Keyboard); - return; - } - } } } // namespace Ui::Menu diff --git a/ui/widgets/popup_menu.cpp b/ui/widgets/popup_menu.cpp index 2e13a6e..23c1a50 100644 --- a/ui/widgets/popup_menu.cpp +++ b/ui/widgets/popup_menu.cpp @@ -9,6 +9,7 @@ #include "ui/widgets/shadow.h" #include "ui/image/image_prepare.h" #include "ui/platform/ui_platform_utility.h" +#include "ui/widgets/menu//menu_item_base.h" #include "ui/ui_utility.h" #include "ui/delayed_activation.h" #include "base/platform/base_platform_info.h" @@ -98,7 +99,22 @@ not_null PopupMenu::ensureSubmenu(not_null action) { } void PopupMenu::removeSubmenu(not_null action) { - _submenus.remove(action); + const auto menu = _submenus.take(action); + if (menu && menu->get() == _activeSubmenu) { + base::take(_activeSubmenu)->hideMenu(true); + } +} + +void PopupMenu::checkSubmenuShow() { + if (_activeSubmenu) { + return; + } else if (const auto item = _menu->findSelectedAction()) { + if (item->lastTriggeredSource() == Menu::TriggeredSource::Mouse) { + if (_submenus.contains(item->action())) { + item->setClicked(Menu::TriggeredSource::Mouse); + } + } + } } void PopupMenu::handleCompositingUpdate() { @@ -211,24 +227,24 @@ void PopupMenu::handleTriggered(const Menu::CallbackData &data) { } bool PopupMenu::popupSubmenuFromAction(const Menu::CallbackData &data) { + if (!data.action) { + return false; + } if (const auto i = _submenus.find(data.action); i != end(_submenus)) { const auto submenu = i->second.get(); - if (_activeSubmenu == submenu) { - // There is a strange problem on macOS - // when a submenu closes arbitrarily - // if we try to move the cursor to it. -#ifndef Q_OS_MAC - submenu->hideMenu(true); -#endif - } else { - popupSubmenu(submenu, data.actionTop, data.source); + if (_activeSubmenu != submenu) { + popupSubmenu(data.action, submenu, data.actionTop, data.source); } return true; } return false; } -void PopupMenu::popupSubmenu(not_null submenu, int actionTop, TriggeredSource source) { +void PopupMenu::popupSubmenu( + not_null action, + not_null submenu, + int actionTop, + TriggeredSource source) { if (auto currentSubmenu = base::take(_activeSubmenu)) { currentSubmenu->hideMenu(true); } @@ -236,10 +252,7 @@ void PopupMenu::popupSubmenu(not_null submenu, int actionTop, Trigge QPoint p(_inner.x() + (style::RightToLeft() ? _padding.right() : _inner.width() - _padding.left()), _inner.y() + actionTop); _activeSubmenu = submenu; _activeSubmenu->showMenu(geometry().topLeft() + p, this, source); - - _menu->setChildShown(true); - } else { - _menu->setChildShown(false); + _menu->setChildShownAction(action); } } @@ -261,6 +274,12 @@ bool PopupMenu::handleKeyPress(int key) { hideMenu(true); return true; } + } else if (key == (style::RightToLeft() ? Qt::Key_Left : Qt::Key_Right)) { + if (const auto item = _menu->findSelectedAction()) { + if (_submenus.contains(item->action())) { + item->setClicked(Menu::TriggeredSource::Keyboard); + } + } } return false; } @@ -337,6 +356,9 @@ void PopupMenu::childHiding(PopupMenu *child) { if (_activeSubmenu && _activeSubmenu == child) { _activeSubmenu = nullptr; } + if (!_activeSubmenu) { + _menu->setChildShownAction(nullptr); + } if (!_hiding && !isHidden()) { raise(); activateWindow(); diff --git a/ui/widgets/popup_menu.h b/ui/widgets/popup_menu.h index 6665090..934d243 100644 --- a/ui/widgets/popup_menu.h +++ b/ui/widgets/popup_menu.h @@ -36,6 +36,7 @@ public: [[nodiscard]] not_null ensureSubmenu( not_null action); void removeSubmenu(not_null action); + void checkSubmenuShow(); bool empty() const; void deleteOnHide(bool del); @@ -107,6 +108,7 @@ private: bool popupSubmenuFromAction(const Menu::CallbackData &data); void popupSubmenu( + not_null action, not_null submenu, int actionTop, TriggeredSource source);