Allow adding submenu to PopupMenu on the fly.
This commit is contained in:
parent
e0339b7da1
commit
6651d9f9b6
2 changed files with 59 additions and 23 deletions
|
|
@ -36,10 +36,12 @@ PopupMenu::PopupMenu(QWidget *parent, QMenu *menu, const style::PopupMenu &st)
|
||||||
, _menu(this, menu, _st.menu) {
|
, _menu(this, menu, _st.menu) {
|
||||||
init();
|
init();
|
||||||
|
|
||||||
for (auto action : actions()) {
|
for (const auto action : actions()) {
|
||||||
if (auto submenu = action->menu()) {
|
if (const auto submenu = action->menu()) {
|
||||||
auto it = _submenus.insert(action, new PopupMenu(parentWidget(), submenu, st));
|
_submenus.emplace(
|
||||||
it.value()->deleteOnHide(false);
|
action,
|
||||||
|
base::make_unique_q<PopupMenu>(parentWidget(), submenu, st)
|
||||||
|
).first->second->deleteOnHide(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +80,27 @@ void PopupMenu::init() {
|
||||||
setAttribute(Qt::WA_TranslucentBackground, true);
|
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<PopupMenu*> PopupMenu::ensureSubmenu(not_null<QAction*> 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<PopupMenu>(parentWidget(), st())
|
||||||
|
).first->second.get();
|
||||||
|
result->deleteOnHide(false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopupMenu::removeSubmenu(not_null<QAction*> action) {
|
||||||
|
_submenus.remove(action);
|
||||||
|
}
|
||||||
|
|
||||||
void PopupMenu::handleCompositingUpdate() {
|
void PopupMenu::handleCompositingUpdate() {
|
||||||
_padding = _useTransparency ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth);
|
_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());
|
_menu->moveToLeft(_padding.left() + _st.scrollPadding.left(), _padding.top() + _st.scrollPadding.top());
|
||||||
|
|
@ -100,11 +123,16 @@ not_null<QAction*> PopupMenu::addAction(const QString &text, Fn<void()> callback
|
||||||
return _menu->addAction(text, std::move(callback), icon, iconOver);
|
return _menu->addAction(text, std::move(callback), icon, iconOver);
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<QAction*> PopupMenu::addAction(const QString &text, std::unique_ptr<PopupMenu> submenu) {
|
not_null<QAction*> PopupMenu::addAction(
|
||||||
|
const QString &text,
|
||||||
|
std::unique_ptr<PopupMenu> submenu) {
|
||||||
const auto action = _menu->addAction(text, std::make_unique<QMenu>());
|
const auto action = _menu->addAction(text, std::make_unique<QMenu>());
|
||||||
auto it = _submenus.insert(action, submenu.release());
|
const auto saved = _submenus.emplace(
|
||||||
it.value()->setParent(parentWidget());
|
action,
|
||||||
it.value()->deleteOnHide(false);
|
base::unique_qptr<PopupMenu>(submenu.release())
|
||||||
|
).first->second.get();
|
||||||
|
saved->setParent(parentWidget());
|
||||||
|
saved->deleteOnHide(false);
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,9 +141,7 @@ not_null<QAction*> PopupMenu::addSeparator() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopupMenu::clearActions() {
|
void PopupMenu::clearActions() {
|
||||||
for (const auto &submenu : base::take(_submenus)) {
|
_submenus.clear();
|
||||||
delete submenu;
|
|
||||||
}
|
|
||||||
return _menu->clearActions();
|
return _menu->clearActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,7 +190,7 @@ void PopupMenu::paintBg(QPainter &p) {
|
||||||
void PopupMenu::handleActivated(const Menu::CallbackData &data) {
|
void PopupMenu::handleActivated(const Menu::CallbackData &data) {
|
||||||
if (data.source == TriggeredSource::Mouse) {
|
if (data.source == TriggeredSource::Mouse) {
|
||||||
if (!popupSubmenuFromAction(data)) {
|
if (!popupSubmenuFromAction(data)) {
|
||||||
if (auto currentSubmenu = base::take(_activeSubmenu)) {
|
if (const auto currentSubmenu = base::take(_activeSubmenu)) {
|
||||||
currentSubmenu->hideMenu(true);
|
currentSubmenu->hideMenu(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -185,7 +211,8 @@ void PopupMenu::handleTriggered(const Menu::CallbackData &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PopupMenu::popupSubmenuFromAction(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) {
|
if (_activeSubmenu == submenu) {
|
||||||
// There is a strange problem on macOS
|
// There is a strange problem on macOS
|
||||||
// when a submenu closes arbitrarily
|
// when a submenu closes arbitrarily
|
||||||
|
|
@ -201,7 +228,7 @@ bool PopupMenu::popupSubmenuFromAction(const Menu::CallbackData &data) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopupMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source) {
|
void PopupMenu::popupSubmenu(not_null<PopupMenu*> submenu, int actionTop, TriggeredSource source) {
|
||||||
if (auto currentSubmenu = base::take(_activeSubmenu)) {
|
if (auto currentSubmenu = base::take(_activeSubmenu)) {
|
||||||
currentSubmenu->hideMenu(true);
|
currentSubmenu->hideMenu(true);
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +314,9 @@ void PopupMenu::mousePressEvent(QMouseEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopupMenu::hideMenu(bool fast) {
|
void PopupMenu::hideMenu(bool fast) {
|
||||||
if (isHidden()) return;
|
if (isHidden()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (_parent && !_a_opacity.animating()) {
|
if (_parent && !_a_opacity.animating()) {
|
||||||
_parent->childHiding(this);
|
_parent->childHiding(this);
|
||||||
}
|
}
|
||||||
|
|
@ -306,7 +335,7 @@ void PopupMenu::hideMenu(bool fast) {
|
||||||
|
|
||||||
void PopupMenu::childHiding(PopupMenu *child) {
|
void PopupMenu::childHiding(PopupMenu *child) {
|
||||||
if (_activeSubmenu && _activeSubmenu == child) {
|
if (_activeSubmenu && _activeSubmenu == child) {
|
||||||
_activeSubmenu = SubmenuPointer();
|
_activeSubmenu = nullptr;
|
||||||
}
|
}
|
||||||
if (!_hiding && !isHidden()) {
|
if (!_hiding && !isHidden()) {
|
||||||
raise();
|
raise();
|
||||||
|
|
@ -549,7 +578,7 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupMenu::~PopupMenu() {
|
PopupMenu::~PopupMenu() {
|
||||||
for (const auto &submenu : base::take(_submenus)) {
|
for (const auto &[action, submenu] : base::take(_submenus)) {
|
||||||
delete submenu;
|
delete submenu;
|
||||||
}
|
}
|
||||||
if (const auto parent = parentWidget()) {
|
if (const auto parent = parentWidget()) {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include "ui/round_rect.h"
|
#include "ui/round_rect.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "base/object_ptr.h"
|
#include "base/object_ptr.h"
|
||||||
|
#include "base/unique_qptr.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
|
|
@ -31,7 +32,10 @@ public:
|
||||||
not_null<QAction*> addSeparator();
|
not_null<QAction*> addSeparator();
|
||||||
void clearActions();
|
void clearActions();
|
||||||
|
|
||||||
const std::vector<not_null<QAction*>> &actions() const;
|
[[nodiscard]] const std::vector<not_null<QAction*>> &actions() const;
|
||||||
|
[[nodiscard]] not_null<PopupMenu*> ensureSubmenu(
|
||||||
|
not_null<QAction*> action);
|
||||||
|
void removeSubmenu(not_null<QAction*> action);
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
|
|
||||||
void deleteOnHide(bool del);
|
void deleteOnHide(bool del);
|
||||||
|
|
@ -101,9 +105,11 @@ private:
|
||||||
}
|
}
|
||||||
void handleMouseRelease(QPoint globalPosition);
|
void handleMouseRelease(QPoint globalPosition);
|
||||||
|
|
||||||
using SubmenuPointer = QPointer<PopupMenu>;
|
|
||||||
bool popupSubmenuFromAction(const Menu::CallbackData &data);
|
bool popupSubmenuFromAction(const Menu::CallbackData &data);
|
||||||
void popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source);
|
void popupSubmenu(
|
||||||
|
not_null<PopupMenu*> submenu,
|
||||||
|
int actionTop,
|
||||||
|
TriggeredSource source);
|
||||||
void showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source);
|
void showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source);
|
||||||
|
|
||||||
const style::PopupMenu &_st;
|
const style::PopupMenu &_st;
|
||||||
|
|
@ -111,15 +117,16 @@ private:
|
||||||
RoundRect _roundRect;
|
RoundRect _roundRect;
|
||||||
object_ptr<Menu::Menu> _menu;
|
object_ptr<Menu::Menu> _menu;
|
||||||
|
|
||||||
using Submenus = QMap<QAction*, SubmenuPointer>;
|
base::flat_map<
|
||||||
Submenus _submenus;
|
not_null<QAction*>,
|
||||||
|
base::unique_qptr<PopupMenu>> _submenus;
|
||||||
|
|
||||||
PopupMenu *_parent = nullptr;
|
PopupMenu *_parent = nullptr;
|
||||||
|
|
||||||
QRect _inner;
|
QRect _inner;
|
||||||
style::margins _padding;
|
style::margins _padding;
|
||||||
|
|
||||||
SubmenuPointer _activeSubmenu;
|
QPointer<PopupMenu> _activeSubmenu;
|
||||||
|
|
||||||
PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft;
|
PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft;
|
||||||
std::optional<PanelAnimation::Origin> _forcedOrigin;
|
std::optional<PanelAnimation::Origin> _forcedOrigin;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue