base::lambda -> Fn (type alias for std::function). base::lambda_once -> FnMut (type alias for base::unique_function). base::lambda_guarded -> crl::guard. base::lambda_call_type_t -> crl::deduced_call_type.
		
			
				
	
	
		
			273 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			273 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
This file is part of Telegram Desktop,
 | 
						|
the official desktop application for the Telegram messaging service.
 | 
						|
 | 
						|
For license and copyright information please follow this link:
 | 
						|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
						|
*/
 | 
						|
#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, Fn<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
 |