tdesktop/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp

284 lines
8.0 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "ui/widgets/dropdown_menu.h"
#include "application.h"
#include "lang/lang_keys.h"
namespace Ui {
DropdownMenu::DropdownMenu(QWidget *parent, const style::DropdownMenu &st) : InnerDropdown(parent, st.wrap)
, _st(st) {
_menu = setOwnedWidget(object_ptr<Ui::Menu>(this, _st.menu));
init();
}
// Not ready with submenus yet.
//DropdownMenu::DropdownMenu(QWidget *parent, QMenu *menu, const style::DropdownMenu &st) : InnerDropdown(parent, st.wrap)
//, _st(st) {
// _menu = setOwnedWidget(object_ptr<Ui::Menu>(this, menu, _st.menu));
// init();
//
// for (auto action : actions()) {
// if (auto submenu = action->menu()) {
// auto it = _submenus.insert(action, new DropdownMenu(submenu, st));
// it.value()->deleteOnHide(false);
// }
// }
//}
void DropdownMenu::init() {
InnerDropdown::setHiddenCallback([this] { hideFinish(); });
_menu->setResizedCallback([this] { resizeToContent(); });
_menu->setActivatedCallback([this](QAction *action, int actionTop, TriggeredSource source) {
handleActivated(action, actionTop, source);
});
_menu->setTriggeredCallback([this](QAction *action, int actionTop, TriggeredSource source) {
handleTriggered(action, actionTop, source);
});
_menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); });
_menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); });
_menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); });
_menu->setMouseReleaseDelegate([this](QPoint globalPosition) { handleMouseRelease(globalPosition); });
setMouseTracking(true);
hide();
}
QAction *DropdownMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) {
return _menu->addAction(text, receiver, member, icon, iconOver);
}
QAction *DropdownMenu::addAction(const QString &text, base::lambda<void()> callback, const style::icon *icon, const style::icon *iconOver) {
return _menu->addAction(text, std::move(callback), icon, iconOver);
}
QAction *DropdownMenu::addSeparator() {
return _menu->addSeparator();
}
void DropdownMenu::clearActions() {
//for (auto submenu : base::take(_submenus)) {
// delete submenu;
//}
return _menu->clearActions();
}
DropdownMenu::Actions &DropdownMenu::actions() {
return _menu->actions();
}
void DropdownMenu::handleActivated(QAction *action, int actionTop, TriggeredSource source) {
if (source == TriggeredSource::Mouse) {
if (!popupSubmenuFromAction(action, actionTop, source)) {
if (auto currentSubmenu = base::take(_activeSubmenu)) {
currentSubmenu->hideMenu(true);
}
}
}
}
void DropdownMenu::handleTriggered(QAction *action, int actionTop, TriggeredSource source) {
if (!popupSubmenuFromAction(action, actionTop, source)) {
hideMenu();
_triggering = true;
emit action->trigger();
_triggering = false;
if (_deleteLater) {
_deleteLater = false;
deleteLater();
}
}
}
// Not ready with submenus yet.
bool DropdownMenu::popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source) {
//if (auto submenu = _submenus.value(action)) {
// if (_activeSubmenu == submenu) {
// submenu->hideMenu(true);
// } else {
// popupSubmenu(submenu, actionTop, source);
// }
// return true;
//}
return false;
}
//void DropdownMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source) {
// if (auto currentSubmenu = base::take(_activeSubmenu)) {
// currentSubmenu->hideMenu(true);
// }
// if (submenu) {
// auto menuTopLeft = mapFromGlobal(_menu->mapToGlobal(QPoint(0, 0)));
// auto menuBottomRight = mapFromGlobal(_menu->mapToGlobal(QPoint(_menu->width(), _menu->height())));
// QPoint p(menuTopLeft.x() + (rtl() ? (width() - menuBottomRight.x()) : menuBottomRight.x()), menuTopLeft.y() + actionTop);
// _activeSubmenu = submenu;
// _activeSubmenu->showMenu(geometry().topLeft() + p, this, source);
//
// _menu->setChildShown(true);
// } else {
// _menu->setChildShown(false);
// }
//}
void DropdownMenu::forwardKeyPress(int key) {
if (!handleKeyPress(key)) {
_menu->handleKeyPress(key);
}
}
bool DropdownMenu::handleKeyPress(int key) {
if (_activeSubmenu) {
_activeSubmenu->handleKeyPress(key);
return true;
} else if (key == Qt::Key_Escape) {
hideMenu(_parent ? true : false);
return true;
} else if (key == (rtl() ? Qt::Key_Right : Qt::Key_Left)) {
if (_parent) {
hideMenu(true);
return true;
}
}
return false;
}
void DropdownMenu::handleMouseMove(QPoint globalPosition) {
if (_parent) {
_parent->forwardMouseMove(globalPosition);
}
}
void DropdownMenu::handleMousePress(QPoint globalPosition) {
if (_parent) {
_parent->forwardMousePress(globalPosition);
} else {
hideMenu();
}
}
void DropdownMenu::handleMouseRelease(QPoint globalPosition) {
if (_parent) {
_parent->forwardMouseRelease(globalPosition);
} else {
hideMenu();
}
}
void DropdownMenu::focusOutEvent(QFocusEvent *e) {
hideMenu();
}
void DropdownMenu::hideEvent(QHideEvent *e) {
if (_deleteOnHide) {
if (_triggering) {
_deleteLater = true;
} else {
deleteLater();
}
}
}
void DropdownMenu::hideMenu(bool fast) {
if (isHidden()) return;
if (_parent && !isHiding()) {
_parent->childHiding(this);
}
if (fast) {
hideFast();
} else {
hideAnimated();
if (_parent) {
_parent->hideMenu();
}
}
if (_activeSubmenu) {
_activeSubmenu->hideMenu(fast);
}
}
void DropdownMenu::childHiding(DropdownMenu *child) {
if (_activeSubmenu && _activeSubmenu == child) {
_activeSubmenu = SubmenuPointer();
}
}
void DropdownMenu::hideFinish() {
_menu->clearSelection();
if (_hiddenCallback) {
_hiddenCallback();
}
}
// Not ready with submenus yet.
//void DropdownMenu::deleteOnHide(bool del) {
// _deleteOnHide = del;
//}
//void DropdownMenu::popup(const QPoint &p) {
// showMenu(p, nullptr, TriggeredSource::Mouse);
//}
//
//void DropdownMenu::showMenu(const QPoint &p, DropdownMenu *parent, TriggeredSource source) {
// _parent = parent;
//
// auto menuTopLeft = mapFromGlobal(_menu->mapToGlobal(QPoint(0, 0)));
// auto w = p - QPoint(0, menuTopLeft.y());
// auto r = Sandbox::screenGeometry(p);
// if (rtl()) {
// if (w.x() - width() < r.x() - _padding.left()) {
// if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) {
// w.setX(w.x() + _parent->width() - _padding.left() - _padding.right());
// } else {
// w.setX(r.x() - _padding.left());
// }
// } else {
// w.setX(w.x() - width());
// }
// } else {
// if (w.x() + width() - _padding.right() > r.x() + r.width()) {
// if (_parent && w.x() - _parent->width() + _padding.left() + _padding.right() - width() + _padding.right() >= r.x() - _padding.left()) {
// w.setX(w.x() + _padding.left() + _padding.right() - _parent->width() - width() + _padding.left() + _padding.right());
// } else {
// w.setX(r.x() + r.width() - width() + _padding.right());
// }
// }
// }
// if (w.y() + height() - _padding.bottom() > r.y() + r.height()) {
// if (_parent) {
// w.setY(r.y() + r.height() - height() + _padding.bottom());
// } else {
// w.setY(p.y() - height() + _padding.bottom());
// }
// }
// if (w.y() < r.y()) {
// w.setY(r.y());
// }
// move(w);
//
// _menu->setShowSource(source);
//}
DropdownMenu::~DropdownMenu() {
clearActions();
}
} // namespace Ui