2016-10-26 16:43:13 +00:00
|
|
|
/*
|
|
|
|
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
|
2017-01-11 18:31:31 +00:00
|
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
2016-10-26 16:43:13 +00:00
|
|
|
*/
|
|
|
|
#include "ui/widgets/menu.h"
|
|
|
|
|
2016-11-16 16:04:25 +00:00
|
|
|
#include "ui/effects/ripple_animation.h"
|
2017-06-29 12:29:00 +00:00
|
|
|
#include "ui/widgets/checkbox.h"
|
2016-11-16 16:04:25 +00:00
|
|
|
|
2016-10-26 16:43:13 +00:00
|
|
|
namespace Ui {
|
|
|
|
|
2017-06-29 12:29:00 +00:00
|
|
|
Menu::ActionData::~ActionData() = default;
|
|
|
|
|
2016-10-26 16:43:13 +00:00
|
|
|
Menu::Menu(QWidget *parent, const style::Menu &st) : TWidget(parent)
|
|
|
|
, _st(st)
|
|
|
|
, _itemHeight(_st.itemPadding.top() + _st.itemFont->height + _st.itemPadding.bottom())
|
|
|
|
, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom()) {
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
Menu::Menu(QWidget *parent, QMenu *menu, const style::Menu &st) : TWidget(parent)
|
|
|
|
, _st(st)
|
|
|
|
, _wappedMenu(menu)
|
|
|
|
, _itemHeight(_st.itemPadding.top() + _st.itemFont->height + _st.itemPadding.bottom())
|
|
|
|
, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom()) {
|
|
|
|
init();
|
|
|
|
|
|
|
|
_wappedMenu->setParent(this);
|
|
|
|
for (auto action : _wappedMenu->actions()) {
|
|
|
|
addAction(action);
|
|
|
|
}
|
|
|
|
_wappedMenu->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::init() {
|
2017-06-29 12:29:00 +00:00
|
|
|
resize(_forceWidth ? _forceWidth : _st.widthMin, _st.skip * 2);
|
2016-10-26 16:43:13 +00:00
|
|
|
|
|
|
|
setMouseTracking(true);
|
|
|
|
|
|
|
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
|
|
|
}
|
|
|
|
|
2016-11-01 12:46:34 +00:00
|
|
|
QAction *Menu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) {
|
2016-11-04 11:14:47 +00:00
|
|
|
auto action = addAction(new QAction(text, this), icon, iconOver);
|
2016-10-26 16:43:13 +00:00
|
|
|
connect(action, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection);
|
2016-11-04 11:14:47 +00:00
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
2017-02-26 11:32:13 +00:00
|
|
|
QAction *Menu::addAction(const QString &text, base::lambda<void()> callback, const style::icon *icon, const style::icon *iconOver) {
|
2016-11-04 11:14:47 +00:00
|
|
|
auto action = addAction(new QAction(text, this), icon, iconOver);
|
2017-08-03 13:06:29 +00:00
|
|
|
connect(action, &QAction::triggered, action, std::move(callback), Qt::QueuedConnection);
|
2016-11-04 11:14:47 +00:00
|
|
|
return action;
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
|
|
|
|
2016-11-01 12:46:34 +00:00
|
|
|
QAction *Menu::addAction(QAction *action, const style::icon *icon, const style::icon *iconOver) {
|
2016-10-26 16:43:13 +00:00
|
|
|
connect(action, SIGNAL(changed()), this, SLOT(actionChanged()));
|
|
|
|
_actions.push_back(action);
|
|
|
|
|
2017-06-29 12:29:00 +00:00
|
|
|
auto createData = [icon, iconOver, action] {
|
|
|
|
auto data = ActionData();
|
|
|
|
data.icon = icon;
|
|
|
|
data.iconOver = iconOver ? iconOver : icon;
|
|
|
|
data.hasSubmenu = (action->menu() != nullptr);
|
|
|
|
return data;
|
|
|
|
};
|
|
|
|
_actionsData.push_back(createData());
|
2016-10-26 16:43:13 +00:00
|
|
|
|
|
|
|
auto newWidth = qMax(width(), _st.widthMin);
|
|
|
|
newWidth = processAction(action, _actions.size() - 1, newWidth);
|
|
|
|
auto newHeight = height() + (action->isSeparator() ? _separatorHeight : _itemHeight);
|
2017-06-29 12:29:00 +00:00
|
|
|
resize(_forceWidth ? _forceWidth : newWidth, newHeight);
|
2016-10-26 16:43:13 +00:00
|
|
|
if (_resizedCallback) {
|
|
|
|
_resizedCallback();
|
|
|
|
}
|
2017-06-29 19:09:10 +00:00
|
|
|
updateSelected(QCursor::pos());
|
2016-10-26 16:43:13 +00:00
|
|
|
update();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
QAction *Menu::addSeparator() {
|
|
|
|
auto separator = new QAction(this);
|
|
|
|
separator->setSeparator(true);
|
|
|
|
return addAction(separator);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::clearActions() {
|
2017-06-29 19:09:10 +00:00
|
|
|
setSelected(-1);
|
|
|
|
setPressed(-1);
|
2016-10-26 16:43:13 +00:00
|
|
|
_actionsData.clear();
|
|
|
|
for (auto action : base::take(_actions)) {
|
|
|
|
if (action->parent() == this) {
|
|
|
|
delete action;
|
|
|
|
}
|
|
|
|
}
|
2017-06-29 12:29:00 +00:00
|
|
|
resize(_forceWidth ? _forceWidth : _st.widthMin, _st.skip * 2);
|
2016-10-26 16:43:13 +00:00
|
|
|
if (_resizedCallback) {
|
|
|
|
_resizedCallback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-30 19:20:40 +00:00
|
|
|
void Menu::finishAnimating() {
|
2017-06-29 19:09:10 +00:00
|
|
|
for (auto &data : _actionsData) {
|
|
|
|
if (data.ripple) {
|
|
|
|
data.ripple.reset();
|
|
|
|
}
|
|
|
|
if (data.toggle) {
|
2017-09-30 19:20:40 +00:00
|
|
|
data.toggle->finishAnimating();
|
2017-06-29 19:09:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-26 16:43:13 +00:00
|
|
|
int Menu::processAction(QAction *action, int index, int width) {
|
|
|
|
auto &data = _actionsData[index];
|
|
|
|
if (action->isSeparator() || action->text().isEmpty()) {
|
|
|
|
data.text = data.shortcut = QString();
|
|
|
|
} else {
|
|
|
|
auto actionTextParts = action->text().split('\t');
|
|
|
|
auto actionText = actionTextParts.empty() ? QString() : actionTextParts[0];
|
|
|
|
auto actionShortcut = (actionTextParts.size() > 1) ? actionTextParts[1] : QString();
|
|
|
|
int textw = _st.itemFont->width(actionText);
|
|
|
|
int goodw = _st.itemPadding.left() + textw + _st.itemPadding.right();
|
|
|
|
if (data.hasSubmenu) {
|
2017-07-18 17:10:30 +00:00
|
|
|
goodw += _st.itemPadding.right() + _st.arrow.width();
|
2016-10-26 16:43:13 +00:00
|
|
|
} else if (!actionShortcut.isEmpty()) {
|
2017-07-18 17:10:30 +00:00
|
|
|
goodw += _st.itemPadding.right() + _st.itemFont->width(actionShortcut);
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
2017-06-29 12:29:00 +00:00
|
|
|
if (action->isCheckable()) {
|
|
|
|
auto updateCallback = [this, index] { updateItem(index); };
|
|
|
|
if (data.toggle) {
|
|
|
|
data.toggle->setUpdateCallback(updateCallback);
|
2017-07-07 11:16:37 +00:00
|
|
|
data.toggle->setCheckedAnimated(action->isChecked());
|
2017-06-29 12:29:00 +00:00
|
|
|
} else {
|
|
|
|
data.toggle = std::make_unique<ToggleView>(_st.itemToggle, action->isChecked(), updateCallback);
|
|
|
|
}
|
2017-07-18 17:10:30 +00:00
|
|
|
goodw += _st.itemPadding.right() + data.toggle->getSize().width() - _st.itemToggleShift;
|
2017-06-29 12:29:00 +00:00
|
|
|
} else {
|
|
|
|
data.toggle.reset();
|
|
|
|
}
|
2016-10-26 16:43:13 +00:00
|
|
|
width = snap(goodw, width, _st.widthMax);
|
|
|
|
data.text = (width < goodw) ? _st.itemFont->elided(actionText, width - (goodw - textw)) : actionText;
|
|
|
|
data.shortcut = actionShortcut;
|
|
|
|
}
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::setShowSource(TriggeredSource source) {
|
|
|
|
_mouseSelection = (source == TriggeredSource::Mouse);
|
|
|
|
setSelected((source == TriggeredSource::Mouse || _actions.isEmpty()) ? -1 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Menu::Actions &Menu::actions() {
|
|
|
|
return _actions;
|
|
|
|
}
|
|
|
|
|
2017-06-29 12:29:00 +00:00
|
|
|
void Menu::setForceWidth(int forceWidth) {
|
|
|
|
_forceWidth = forceWidth;
|
|
|
|
resize(_forceWidth, height());
|
|
|
|
}
|
|
|
|
|
2016-10-26 16:43:13 +00:00
|
|
|
void Menu::actionChanged() {
|
2017-06-29 12:29:00 +00:00
|
|
|
auto newWidth = _st.widthMin;
|
|
|
|
for (auto i = 0, count = _actions.size(); i != count; ++i) {
|
2016-10-26 16:43:13 +00:00
|
|
|
newWidth = processAction(_actions[i], i, newWidth);
|
|
|
|
}
|
2017-06-29 12:29:00 +00:00
|
|
|
if (newWidth != width() && !_forceWidth) {
|
2016-10-26 16:43:13 +00:00
|
|
|
resize(newWidth, height());
|
|
|
|
if (_resizedCallback) {
|
|
|
|
_resizedCallback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::paintEvent(QPaintEvent *e) {
|
|
|
|
Painter p(this);
|
|
|
|
|
2016-11-16 16:04:25 +00:00
|
|
|
auto ms = getms();
|
2016-10-26 16:43:13 +00:00
|
|
|
auto clip = e->rect();
|
|
|
|
|
|
|
|
auto topskip = QRect(0, 0, width(), _st.skip);
|
|
|
|
auto bottomskip = QRect(0, height() - _st.skip, width(), _st.skip);
|
|
|
|
if (clip.intersects(topskip)) p.fillRect(clip.intersected(topskip), _st.itemBg);
|
|
|
|
if (clip.intersects(bottomskip)) p.fillRect(clip.intersected(bottomskip), _st.itemBg);
|
|
|
|
|
|
|
|
int top = _st.skip;
|
|
|
|
p.translate(0, top);
|
|
|
|
p.setFont(_st.itemFont);
|
|
|
|
for (int i = 0, count = _actions.size(); i != count; ++i) {
|
|
|
|
if (clip.top() + clip.height() <= top) break;
|
|
|
|
|
|
|
|
auto action = _actions[i];
|
|
|
|
auto &data = _actionsData[i];
|
|
|
|
auto actionHeight = action->isSeparator() ? _separatorHeight : _itemHeight;
|
|
|
|
top += actionHeight;
|
|
|
|
if (clip.top() < top) {
|
|
|
|
if (action->isSeparator()) {
|
|
|
|
p.fillRect(0, 0, width(), actionHeight, _st.itemBg);
|
|
|
|
p.fillRect(_st.separatorPadding.left(), _st.separatorPadding.top(), width() - _st.separatorPadding.left() - _st.separatorPadding.right(), _st.separatorWidth, _st.separatorFg);
|
|
|
|
} else {
|
2016-11-16 16:04:25 +00:00
|
|
|
auto enabled = action->isEnabled();
|
|
|
|
auto selected = ((i == _selected || i == _pressed) && enabled);
|
2016-10-26 16:43:13 +00:00
|
|
|
p.fillRect(0, 0, width(), actionHeight, selected ? _st.itemBgOver : _st.itemBg);
|
2016-11-16 16:04:25 +00:00
|
|
|
if (data.ripple) {
|
|
|
|
data.ripple->paint(p, 0, 0, width(), ms);
|
|
|
|
if (data.ripple->empty()) {
|
|
|
|
data.ripple.reset();
|
|
|
|
}
|
|
|
|
}
|
2016-11-01 12:46:34 +00:00
|
|
|
if (auto icon = (selected ? data.iconOver : data.icon)) {
|
|
|
|
icon->paint(p, _st.itemIconPosition, width());
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
|
|
|
p.setPen(selected ? _st.itemFgOver : (enabled ? _st.itemFg : _st.itemFgDisabled));
|
|
|
|
p.drawTextLeft(_st.itemPadding.left(), _st.itemPadding.top(), width(), data.text);
|
|
|
|
if (data.hasSubmenu) {
|
|
|
|
_st.arrow.paint(p, width() - _st.itemPadding.right() - _st.arrow.width(), (_itemHeight - _st.arrow.height()) / 2, width());
|
|
|
|
} else if (!data.shortcut.isEmpty()) {
|
|
|
|
p.setPen(selected ? _st.itemFgShortcutOver : (enabled ? _st.itemFgShortcut : _st.itemFgShortcutDisabled));
|
|
|
|
p.drawTextRight(_st.itemPadding.right(), _st.itemPadding.top(), width(), data.shortcut);
|
2017-06-29 12:29:00 +00:00
|
|
|
} else if (data.toggle) {
|
2017-07-07 11:16:37 +00:00
|
|
|
auto toggleSize = data.toggle->getSize();
|
|
|
|
data.toggle->paint(p, width() - _st.itemPadding.right() - toggleSize.width() + _st.itemToggleShift, (_itemHeight - toggleSize.height()) / 2, width(), ms);
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p.translate(0, actionHeight);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::updateSelected(QPoint globalPosition) {
|
|
|
|
if (!_mouseSelection) return;
|
|
|
|
|
|
|
|
auto p = mapFromGlobal(globalPosition) - QPoint(0, _st.skip);
|
|
|
|
auto selected = -1, top = 0;
|
|
|
|
while (top <= p.y() && ++selected < _actions.size()) {
|
|
|
|
top += _actions[selected]->isSeparator() ? _separatorHeight : _itemHeight;
|
|
|
|
}
|
|
|
|
setSelected((selected >= 0 && selected < _actions.size() && _actions[selected]->isEnabled() && !_actions[selected]->isSeparator()) ? selected : -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::itemPressed(TriggeredSource source) {
|
|
|
|
if (source == TriggeredSource::Mouse && !_mouseSelection) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_selected >= 0 && _selected < _actions.size() && _actions[_selected]->isEnabled()) {
|
2017-06-29 19:09:10 +00:00
|
|
|
setPressed(_selected);
|
2016-11-16 16:04:25 +00:00
|
|
|
if (source == TriggeredSource::Mouse) {
|
|
|
|
if (!_actionsData[_pressed].ripple) {
|
|
|
|
auto mask = RippleAnimation::rectMask(QSize(width(), _itemHeight));
|
2017-06-29 12:29:00 +00:00
|
|
|
_actionsData[_pressed].ripple = std::make_unique<RippleAnimation>(_st.ripple, std::move(mask), [this, selected = _pressed] {
|
2016-11-16 16:04:25 +00:00
|
|
|
updateItem(selected);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
_actionsData[_pressed].ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(0, itemTop(_pressed)));
|
|
|
|
} else {
|
|
|
|
itemReleased(source);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::itemReleased(TriggeredSource source) {
|
2017-06-29 19:09:10 +00:00
|
|
|
if (_pressed >= 0 && _pressed < _actions.size()) {
|
|
|
|
auto pressed = _pressed;
|
|
|
|
setPressed(-1);
|
2016-11-16 16:04:25 +00:00
|
|
|
if (source == TriggeredSource::Mouse && _actionsData[pressed].ripple) {
|
|
|
|
_actionsData[pressed].ripple->lastStop();
|
|
|
|
}
|
|
|
|
if (pressed == _selected && _triggeredCallback) {
|
|
|
|
_triggeredCallback(_actions[_selected], itemTop(_selected), source);
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::keyPressEvent(QKeyEvent *e) {
|
|
|
|
auto key = e->key();
|
|
|
|
if (!_keyPressDelegate || !_keyPressDelegate(key)) {
|
|
|
|
handleKeyPress(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::handleKeyPress(int key) {
|
|
|
|
if (key == Qt::Key_Enter || key == Qt::Key_Return) {
|
|
|
|
itemPressed(TriggeredSource::Keyboard);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (key == (rtl() ? Qt::Key_Left : Qt::Key_Right)) {
|
|
|
|
if (_selected >= 0 && _actionsData[_selected].hasSubmenu) {
|
|
|
|
itemPressed(TriggeredSource::Keyboard);
|
|
|
|
return;
|
|
|
|
} else if (_selected < 0 && !_actions.isEmpty()) {
|
|
|
|
_mouseSelection = false;
|
|
|
|
setSelected(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((key != Qt::Key_Up && key != Qt::Key_Down) || _actions.size() < 1) return;
|
|
|
|
|
|
|
|
auto delta = (key == Qt::Key_Down ? 1 : -1), start = _selected;
|
|
|
|
if (start < 0 || start >= _actions.size()) {
|
|
|
|
start = (delta > 0) ? (_actions.size() - 1) : 0;
|
|
|
|
}
|
|
|
|
auto newSelected = start;
|
|
|
|
do {
|
|
|
|
newSelected += delta;
|
|
|
|
if (newSelected < 0) {
|
|
|
|
newSelected += _actions.size();
|
|
|
|
} else if (newSelected >= _actions.size()) {
|
|
|
|
newSelected -= _actions.size();
|
|
|
|
}
|
2017-06-29 19:09:10 +00:00
|
|
|
} while (newSelected != start && (!_actions[newSelected]->isEnabled() || _actions[newSelected]->isSeparator()));
|
2016-10-26 16:43:13 +00:00
|
|
|
|
2017-06-29 19:09:10 +00:00
|
|
|
if (_actions[newSelected]->isEnabled() && !_actions[newSelected]->isSeparator()) {
|
2016-10-26 16:43:13 +00:00
|
|
|
_mouseSelection = false;
|
|
|
|
setSelected(newSelected);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::clearSelection() {
|
|
|
|
_mouseSelection = false;
|
|
|
|
setSelected(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::clearMouseSelection() {
|
|
|
|
if (_mouseSelection && !_childShown) {
|
|
|
|
clearSelection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-11 11:24:37 +00:00
|
|
|
void Menu::enterEventHook(QEvent *e) {
|
2016-10-26 16:43:13 +00:00
|
|
|
QPoint mouse = QCursor::pos();
|
|
|
|
if (!rect().marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)).contains(mapFromGlobal(mouse))) {
|
|
|
|
clearMouseSelection();
|
|
|
|
}
|
2017-02-11 11:24:37 +00:00
|
|
|
return TWidget::enterEventHook(e);
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 11:24:37 +00:00
|
|
|
void Menu::leaveEventHook(QEvent *e) {
|
2016-10-26 16:43:13 +00:00
|
|
|
clearMouseSelection();
|
2017-02-11 11:24:37 +00:00
|
|
|
return TWidget::leaveEventHook(e);
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::setSelected(int selected) {
|
|
|
|
if (selected >= _actions.size()) {
|
|
|
|
selected = -1;
|
|
|
|
}
|
|
|
|
if (_selected != selected) {
|
|
|
|
updateSelectedItem();
|
2017-06-29 12:29:00 +00:00
|
|
|
if (_selected >= 0 && _selected != _pressed && _actionsData[_selected].toggle) {
|
|
|
|
_actionsData[_selected].toggle->setStyle(_st.itemToggle);
|
|
|
|
}
|
2016-10-26 16:43:13 +00:00
|
|
|
_selected = selected;
|
2017-06-29 12:29:00 +00:00
|
|
|
if (_selected >= 0 && _actionsData[_selected].toggle && _actions[_selected]->isEnabled()) {
|
|
|
|
_actionsData[_selected].toggle->setStyle(_st.itemToggleOver);
|
|
|
|
}
|
2016-10-26 16:43:13 +00:00
|
|
|
updateSelectedItem();
|
|
|
|
if (_activatedCallback) {
|
|
|
|
auto source = _mouseSelection ? TriggeredSource::Mouse : TriggeredSource::Keyboard;
|
2016-11-16 16:04:25 +00:00
|
|
|
_activatedCallback((_selected >= 0) ? _actions[_selected] : nullptr, itemTop(_selected), source);
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-29 19:09:10 +00:00
|
|
|
void Menu::setPressed(int pressed) {
|
|
|
|
if (pressed >= _actions.size()) {
|
|
|
|
pressed = -1;
|
|
|
|
}
|
|
|
|
if (_pressed != pressed) {
|
|
|
|
if (_pressed >= 0 && _pressed != _selected && _actionsData[_pressed].toggle) {
|
|
|
|
_actionsData[_pressed].toggle->setStyle(_st.itemToggle);
|
|
|
|
}
|
|
|
|
_pressed = pressed;
|
|
|
|
if (_pressed >= 0 && _actionsData[_pressed].toggle && _actions[_pressed]->isEnabled()) {
|
|
|
|
_actionsData[_pressed].toggle->setStyle(_st.itemToggleOver);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-26 16:43:13 +00:00
|
|
|
int Menu::itemTop(int index) {
|
|
|
|
if (index > _actions.size()) {
|
|
|
|
index = _actions.size();
|
|
|
|
}
|
2016-11-16 16:04:25 +00:00
|
|
|
int top = _st.skip;
|
2016-10-26 16:43:13 +00:00
|
|
|
for (int i = 0; i < index; ++i) {
|
|
|
|
top += _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight;
|
|
|
|
}
|
|
|
|
return top;
|
|
|
|
}
|
|
|
|
|
2016-11-16 16:04:25 +00:00
|
|
|
void Menu::updateItem(int index) {
|
|
|
|
if (index >= 0 && index < _actions.size()) {
|
|
|
|
update(0, itemTop(index), width(), _actions[index]->isSeparator() ? _separatorHeight : _itemHeight);
|
2016-10-26 16:43:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-16 16:04:25 +00:00
|
|
|
void Menu::updateSelectedItem() {
|
|
|
|
updateItem(_selected);
|
|
|
|
}
|
|
|
|
|
2016-10-26 16:43:13 +00:00
|
|
|
void Menu::mouseMoveEvent(QMouseEvent *e) {
|
|
|
|
handleMouseMove(e->globalPos());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::handleMouseMove(QPoint globalPosition) {
|
|
|
|
auto inner = rect().marginsRemoved(QMargins(0, _st.skip, 0, _st.skip));
|
|
|
|
auto localPosition = mapFromGlobal(globalPosition);
|
|
|
|
if (inner.contains(localPosition)) {
|
|
|
|
_mouseSelection = true;
|
|
|
|
updateSelected(globalPosition);
|
|
|
|
} else {
|
|
|
|
clearMouseSelection();
|
|
|
|
if (_mouseMoveDelegate) {
|
|
|
|
_mouseMoveDelegate(globalPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::mousePressEvent(QMouseEvent *e) {
|
|
|
|
handleMousePress(e->globalPos());
|
|
|
|
}
|
|
|
|
|
2016-11-16 16:04:25 +00:00
|
|
|
void Menu::mouseReleaseEvent(QMouseEvent *e) {
|
|
|
|
handleMouseRelease(e->globalPos());
|
|
|
|
}
|
|
|
|
|
2016-10-26 16:43:13 +00:00
|
|
|
void Menu::handleMousePress(QPoint globalPosition) {
|
|
|
|
handleMouseMove(globalPosition);
|
|
|
|
if (rect().contains(mapFromGlobal(globalPosition))) {
|
|
|
|
itemPressed(TriggeredSource::Mouse);
|
|
|
|
} else if (_mousePressDelegate) {
|
|
|
|
_mousePressDelegate(globalPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-16 16:04:25 +00:00
|
|
|
void Menu::handleMouseRelease(QPoint globalPosition) {
|
|
|
|
handleMouseMove(globalPosition);
|
|
|
|
itemReleased(TriggeredSource::Mouse);
|
|
|
|
if (!rect().contains(mapFromGlobal(globalPosition)) && _mouseReleaseDelegate) {
|
|
|
|
_mouseReleaseDelegate(globalPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-26 16:43:13 +00:00
|
|
|
} // namespace Ui
|