From 6651d9f9b6fb7ebc9d18e6b3429862477d891b2e Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Sep 2021 21:18:43 +0300 Subject: [PATCH] Allow adding submenu to PopupMenu on the fly. --- ui/widgets/popup_menu.cpp | 63 ++++++++++++++++++++++++++++----------- ui/widgets/popup_menu.h | 19 ++++++++---- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/ui/widgets/popup_menu.cpp b/ui/widgets/popup_menu.cpp index 87f6ee3..2e13a6e 100644 --- a/ui/widgets/popup_menu.cpp +++ b/ui/widgets/popup_menu.cpp @@ -36,10 +36,12 @@ PopupMenu::PopupMenu(QWidget *parent, QMenu *menu, const style::PopupMenu &st) , _menu(this, menu, _st.menu) { init(); - for (auto action : actions()) { - if (auto submenu = action->menu()) { - auto it = _submenus.insert(action, new PopupMenu(parentWidget(), submenu, st)); - it.value()->deleteOnHide(false); + for (const auto action : actions()) { + if (const auto submenu = action->menu()) { + _submenus.emplace( + action, + base::make_unique_q(parentWidget(), submenu, st) + ).first->second->deleteOnHide(false); } } } @@ -78,6 +80,27 @@ void PopupMenu::init() { setAttribute(Qt::WA_TranslucentBackground, true); } +not_null PopupMenu::ensureSubmenu(not_null action) { + const auto &list = actions(); + const auto i = ranges::find(list, action); + Assert(i != end(list)); + + const auto j = _submenus.find(action); + if (j != end(_submenus)) { + return j->second.get(); + } + const auto result = _submenus.emplace( + action, + base::make_unique_q(parentWidget(), st()) + ).first->second.get(); + result->deleteOnHide(false); + return result; +} + +void PopupMenu::removeSubmenu(not_null action) { + _submenus.remove(action); +} + void PopupMenu::handleCompositingUpdate() { _padding = _useTransparency ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); _menu->moveToLeft(_padding.left() + _st.scrollPadding.left(), _padding.top() + _st.scrollPadding.top()); @@ -100,11 +123,16 @@ not_null PopupMenu::addAction(const QString &text, Fn callback return _menu->addAction(text, std::move(callback), icon, iconOver); } -not_null PopupMenu::addAction(const QString &text, std::unique_ptr submenu) { +not_null PopupMenu::addAction( + const QString &text, + std::unique_ptr submenu) { const auto action = _menu->addAction(text, std::make_unique()); - auto it = _submenus.insert(action, submenu.release()); - it.value()->setParent(parentWidget()); - it.value()->deleteOnHide(false); + const auto saved = _submenus.emplace( + action, + base::unique_qptr(submenu.release()) + ).first->second.get(); + saved->setParent(parentWidget()); + saved->deleteOnHide(false); return action; } @@ -113,9 +141,7 @@ not_null PopupMenu::addSeparator() { } void PopupMenu::clearActions() { - for (const auto &submenu : base::take(_submenus)) { - delete submenu; - } + _submenus.clear(); return _menu->clearActions(); } @@ -164,7 +190,7 @@ void PopupMenu::paintBg(QPainter &p) { void PopupMenu::handleActivated(const Menu::CallbackData &data) { if (data.source == TriggeredSource::Mouse) { if (!popupSubmenuFromAction(data)) { - if (auto currentSubmenu = base::take(_activeSubmenu)) { + if (const auto currentSubmenu = base::take(_activeSubmenu)) { currentSubmenu->hideMenu(true); } } @@ -185,7 +211,8 @@ void PopupMenu::handleTriggered(const Menu::CallbackData &data) { } bool PopupMenu::popupSubmenuFromAction(const Menu::CallbackData &data) { - if (auto submenu = _submenus.value(data.action)) { + 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 @@ -201,7 +228,7 @@ bool PopupMenu::popupSubmenuFromAction(const Menu::CallbackData &data) { return false; } -void PopupMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source) { +void PopupMenu::popupSubmenu(not_null submenu, int actionTop, TriggeredSource source) { if (auto currentSubmenu = base::take(_activeSubmenu)) { currentSubmenu->hideMenu(true); } @@ -287,7 +314,9 @@ void PopupMenu::mousePressEvent(QMouseEvent *e) { } void PopupMenu::hideMenu(bool fast) { - if (isHidden()) return; + if (isHidden()) { + return; + } if (_parent && !_a_opacity.animating()) { _parent->childHiding(this); } @@ -306,7 +335,7 @@ void PopupMenu::hideMenu(bool fast) { void PopupMenu::childHiding(PopupMenu *child) { if (_activeSubmenu && _activeSubmenu == child) { - _activeSubmenu = SubmenuPointer(); + _activeSubmenu = nullptr; } if (!_hiding && !isHidden()) { raise(); @@ -549,7 +578,7 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou } PopupMenu::~PopupMenu() { - for (const auto &submenu : base::take(_submenus)) { + for (const auto &[action, submenu] : base::take(_submenus)) { delete submenu; } if (const auto parent = parentWidget()) { diff --git a/ui/widgets/popup_menu.h b/ui/widgets/popup_menu.h index 3b86873..6665090 100644 --- a/ui/widgets/popup_menu.h +++ b/ui/widgets/popup_menu.h @@ -13,6 +13,7 @@ #include "ui/round_rect.h" #include "ui/rp_widget.h" #include "base/object_ptr.h" +#include "base/unique_qptr.h" namespace Ui { @@ -31,7 +32,10 @@ public: not_null addSeparator(); void clearActions(); - const std::vector> &actions() const; + [[nodiscard]] const std::vector> &actions() const; + [[nodiscard]] not_null ensureSubmenu( + not_null action); + void removeSubmenu(not_null action); bool empty() const; void deleteOnHide(bool del); @@ -101,9 +105,11 @@ private: } void handleMouseRelease(QPoint globalPosition); - using SubmenuPointer = QPointer; bool popupSubmenuFromAction(const Menu::CallbackData &data); - void popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source); + void popupSubmenu( + not_null submenu, + int actionTop, + TriggeredSource source); void showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source); const style::PopupMenu &_st; @@ -111,15 +117,16 @@ private: RoundRect _roundRect; object_ptr _menu; - using Submenus = QMap; - Submenus _submenus; + base::flat_map< + not_null, + base::unique_qptr> _submenus; PopupMenu *_parent = nullptr; QRect _inner; style::margins _padding; - SubmenuPointer _activeSubmenu; + QPointer _activeSubmenu; PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; std::optional _forcedOrigin;