From 0d85f914537fd2fc6527ace388eddb5a0dcb1fc4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 28 Sep 2014 19:47:30 -0700 Subject: [PATCH] custom context menus (including tray menu for windows) done --- Telegram/Resources/style.txt | 17 +++ Telegram/SourceFiles/application.cpp | 2 +- Telegram/SourceFiles/config.h | 4 +- Telegram/SourceFiles/gui/button.cpp | 36 ++--- Telegram/SourceFiles/gui/button.h | 1 + Telegram/SourceFiles/gui/contextmenu.cpp | 183 ++++++++++++++++++----- Telegram/SourceFiles/gui/contextmenu.h | 39 ++++- Telegram/SourceFiles/gui/flatbutton.cpp | 5 + Telegram/SourceFiles/gui/flatbutton.h | 2 + Telegram/SourceFiles/gui/twidget.h | 4 + Telegram/SourceFiles/historywidget.cpp | 20 +-- Telegram/SourceFiles/historywidget.h | 2 +- Telegram/SourceFiles/mediaview.cpp | 17 +-- Telegram/SourceFiles/mediaview.h | 4 +- Telegram/SourceFiles/overviewwidget.cpp | 4 +- Telegram/SourceFiles/overviewwidget.h | 2 +- Telegram/SourceFiles/profilewidget.cpp | 4 +- Telegram/SourceFiles/profilewidget.h | 2 +- Telegram/SourceFiles/pspecific_linux.cpp | 6 + Telegram/SourceFiles/pspecific_linux.h | 3 + Telegram/SourceFiles/pspecific_mac.cpp | 13 +- Telegram/SourceFiles/pspecific_mac.h | 3 + Telegram/SourceFiles/pspecific_wnd.cpp | 22 ++- Telegram/SourceFiles/pspecific_wnd.h | 5 +- Telegram/SourceFiles/window.cpp | 56 ++++--- Telegram/SourceFiles/window.h | 3 +- Telegram/Telegram.vcxproj | 27 ++++ Telegram/Telegram.vcxproj.filters | 15 ++ 28 files changed, 375 insertions(+), 126 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 23416f6a3..b01c9fac2 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1514,3 +1514,20 @@ macSelectorTop: 6; macAlwaysThisAppTop: 4; macAppHintTop: 8; macCautionIconSize: size(16, 16); + +btnContext: iconedButton(btnDefIconed) { + bgColor: white; + overBgColor: btnWhiteHover; + font: font(14px); + + opacity: 1; + overOpacity: 1; + + width: 172px; + height: 36px; + + color: black; + + textPos: point(16px, 7px); + downTextPos: point(16px, 8px); +} diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 2016f7b7c..f601d9574 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -596,7 +596,7 @@ void Application::startApp() { window->setupIntro(false); } - window->psFirstShow(); + window->firstShow(); if (cStartToSettings()) { window->showSettings(); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index e1b2adfae..d34e8a9e3 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -118,9 +118,9 @@ struct BuiltInDc { static const BuiltInDc _builtInDcs[] = { { 1, "173.240.5.1", 443 }, - { 2, "149.154.167.50", 443 }, + { 2, "149.154.167.51", 443 }, { 3, "174.140.142.6", 443 }, - { 4, "149.154.167.90", 443 }, + { 4, "149.154.167.91", 443 }, { 5, "149.154.171.5", 443 } }; diff --git a/Telegram/SourceFiles/gui/button.cpp b/Telegram/SourceFiles/gui/button.cpp index 5d87a716d..2e816f4fc 100644 --- a/Telegram/SourceFiles/gui/button.cpp +++ b/Telegram/SourceFiles/gui/button.cpp @@ -24,21 +24,13 @@ Button::Button(QWidget *parent) : TWidget(parent), _state(StateNone), _acceptBot void Button::leaveEvent(QEvent *e) { if (_state & StateDown) return; - if (_state & StateOver) { - int oldState = _state; - _state &= ~StateOver; - emit stateChanged(oldState, ButtonByHover); - } + setOver(false, ButtonByHover); setMouseTracking(false); return TWidget::leaveEvent(e); } void Button::enterEvent(QEvent *e) { - if (!(_state & StateOver)) { - int oldState = _state; - _state |= StateOver; - emit stateChanged(oldState, ButtonByHover); - } + setOver(true, ButtonByHover); setMouseTracking(true); return TWidget::enterEvent(e); } @@ -64,17 +56,9 @@ void Button::mousePressEvent(QMouseEvent *e) { void Button::mouseMoveEvent(QMouseEvent *e) { if (rect().contains(e->pos())) { - if (!(_state & StateOver)) { - int oldState = _state; - _state |= StateOver; - emit stateChanged(oldState, ButtonByHover); - } + setOver(true, ButtonByHover); } else { - if (_state & StateOver) { - int oldState = _state; - _state &= ~StateOver; - emit stateChanged(oldState, ButtonByHover); - } + setOver(false, ButtonByHover); } } @@ -91,6 +75,18 @@ void Button::mouseReleaseEvent(QMouseEvent *e) { } } +void Button::setOver(bool over, ButtonStateChangeSource source) { + if (over && !(_state & StateOver)) { + int oldState = _state; + _state |= StateOver; + emit stateChanged(oldState, source); + } else if (!over && (_state & StateOver)) { + int oldState = _state; + _state &= ~StateOver; + emit stateChanged(oldState, source); + } +} + void Button::setDisabled(bool disabled) { int oldState = _state; if (disabled && !(_state & StateDisabled)) { diff --git a/Telegram/SourceFiles/gui/button.h b/Telegram/SourceFiles/gui/button.h index 51843613e..d55e1ce81 100644 --- a/Telegram/SourceFiles/gui/button.h +++ b/Telegram/SourceFiles/gui/button.h @@ -50,6 +50,7 @@ public: int getState() const; void setDisabled(bool disabled = true); + void setOver(bool over, ButtonStateChangeSource source = ButtonByUser); bool disabled() const { return (_state & StateDisabled); } diff --git a/Telegram/SourceFiles/gui/contextmenu.cpp b/Telegram/SourceFiles/gui/contextmenu.cpp index b6fa308fc..c71137e95 100644 --- a/Telegram/SourceFiles/gui/contextmenu.cpp +++ b/Telegram/SourceFiles/gui/contextmenu.cpp @@ -19,48 +19,108 @@ #include "contextmenu.h" #include "flatbutton.h" +#include "pspecific.h" #include "lang.h" -ContextMenu::ContextMenu(QWidget *parent) : QWidget(parent), -_hiding(false), a_opacity(0) { - resetButtons(); +ContextMenu::ContextMenu(QWidget *parent, const style::iconedButton &st) : TWidget(parent), +_hiding(false), _buttonStyle(st), _shadow(st::dropdownShadow), _selected(-1), a_opacity(0), _deleteOnHide(false) { + resetActions(); - setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint); + setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint | Qt::WindowStaysOnTopHint); hide(); + + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TranslucentBackground, true); } -FlatButton *ContextMenu::addButton(FlatButton *button) { - button->setParent(this); +QAction *ContextMenu::addAction(const QString &text, const QObject *receiver, const char* member) { + QAction *a = 0; + _actions.push_back(a = new QAction(text, this)); + connect(a, SIGNAL(triggered(bool)), receiver, member); + connect(a, SIGNAL(changed()), this, SLOT(actionChanged())); - _width = qMax(_width, int(2 * st::dropdownBorder + button->width())); - if (!_buttons.isEmpty()) { - _height += st::dropdownBorder; + IconedButton *b = 0; + _buttons.push_back(b = new IconedButton(this, _buttonStyle, a->text())); + connect(b, SIGNAL(clicked()), this, SLOT(hideStart())); + connect(b, SIGNAL(clicked()), a, SIGNAL(triggered())); + connect(b, SIGNAL(stateChanged(int,ButtonStateChangeSource)), this, SLOT(buttonStateChanged(int,ButtonStateChangeSource))); + + _width = qMax(_width, int(st::dropdownPadding.left() + st::dropdownPadding.right() + b->width())); + _height += b->height(); + + resize(_width, _height); + + return a; +} + +ContextMenu::Actions &ContextMenu::actions() { + return _actions; +} + +void ContextMenu::actionChanged() { + for (int32 i = 0, l = _actions.size(); i < l; ++i) { + _buttons[i]->setText(_actions[i]->text()); } - _height += button->height(); - - _buttons.push_back(button); - - resize(_width, _height); - - return button; } -void ContextMenu::resetButtons() { - _width = 2 * st::dropdownBorder; - _height = 2 * st::dropdownBorder; +void ContextMenu::onActiveChanged() { + if (!windowHandle()->isActive()) { + hideStart(); + } +} + +void ContextMenu::buttonStateChanged(int oldState, ButtonStateChangeSource source) { + if (source == ButtonByUser) { + for (int32 i = 0, l = _buttons.size(); i < l; ++i) { + if (_buttons[i]->getState() & Button::StateOver) { + if (i != _selected) { + _buttons[i]->setOver(false); + } + } + } + } else if (source == ButtonByHover) { + for (int32 i = 0, l = _buttons.size(); i < l; ++i) { + if (_buttons[i]->getState() & Button::StateOver) { + if (i != _selected) { + int32 sel = _selected; + _selected = i; + if (sel >= 0 && sel < _buttons.size()) { + _buttons[sel]->setOver(false); + } + } + } + } + } +} + +void ContextMenu::resetActions() { + _width = st::dropdownPadding.left() + st::dropdownPadding.right(); + _height = st::dropdownPadding.top() + st::dropdownPadding.bottom(); resize(_width, _height); + + clearActions(); +} + +void ContextMenu::clearActions() { for (int32 i = 0, l = _buttons.size(); i < l; ++i) { delete _buttons[i]; } _buttons.clear(); + + for (int32 i = 0, l = _actions.size(); i < l; ++i) { + delete _actions[i]; + } + _actions.clear(); + + _selected = -1; } void ContextMenu::resizeEvent(QResizeEvent *e) { - int32 top = st::dropdownBorder; + int32 top = st::dropdownPadding.top(); for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) { - (*i)->move(st::dropdownBorder, top); - top += st::dropdownBorder + (*i)->height(); + (*i)->move(st::dropdownPadding.left(), top); + top += (*i)->height(); } } @@ -71,20 +131,37 @@ void ContextMenu::paintEvent(QPaintEvent *e) { p.setOpacity(a_opacity.current()); } - // paint window border - p.fillRect(QRect(0, 0, _width - st::dropdownBorder, st::dropdownBorder), st::dropdownBorderColor->b); - p.fillRect(QRect(_width - st::dropdownBorder, 0, st::dropdownBorder, _height - st::dropdownBorder), st::dropdownBorderColor->b); - p.fillRect(QRect(st::dropdownBorder, _height - st::dropdownBorder, _width - st::dropdownBorder, st::dropdownBorder), st::dropdownBorderColor->b); - p.fillRect(QRect(0, st::dropdownBorder, st::dropdownBorder, _height - st::dropdownBorder), st::dropdownBorderColor->b); + QRect r(st::dropdownPadding.left(), st::dropdownPadding.top(), _width - st::dropdownPadding.left() - st::dropdownPadding.right(), _height - st::dropdownPadding.top() - st::dropdownPadding.bottom()); + // draw shadow + _shadow.paint(p, r); +} - if (!_buttons.isEmpty()) { // paint separators - int32 top = st::dropdownBorder + _buttons.front()->height(); - p.setPen(st::dropdownBorderColor->p); - for (int32 i = 1, s = _buttons.size(); i < s; ++i) { - p.fillRect(st::dropdownBorder, top, _width - 2 * st::dropdownBorder, st::dropdownBorder, st::dropdownBorderColor->b); - top += st::dropdownBorder + _buttons[i]->height(); +void ContextMenu::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { + if (_selected >= 0 && _selected < _buttons.size()) { + emit _buttons[_selected]->clicked(); + return; } } + if ((e->key() != Qt::Key_Up && e->key() != Qt::Key_Down) || _buttons.size() < 1) return; + + int32 newSelected = _selected + (e->key() == Qt::Key_Down ? 1 : -1); + if (_selected < 0 || _selected >= _buttons.size()) { + newSelected = e->key() == Qt::Key_Down ? 0 : (_buttons.size() - 1); + } else { + if (newSelected < 0) { + newSelected = _buttons.size() - 1; + } else if (newSelected >= _buttons.size()) { + newSelected = 0; + } + _buttons[_selected]->setOver(false); + } + _selected = newSelected; + _buttons[_selected]->setOver(true); +} + +void ContextMenu::focusOutEvent(QFocusEvent *e) { + if (!_hiding) hideStart(); } void ContextMenu::fastHide() { @@ -92,7 +169,7 @@ void ContextMenu::fastHide() { anim::stop(this); } a_opacity = anim::fvalue(0, 0); - hide(); + hideFinish(); } void ContextMenu::adjustButtons() { @@ -102,6 +179,8 @@ void ContextMenu::adjustButtons() { } void ContextMenu::hideStart() { + if (isHidden()) return; + _hiding = true; a_opacity.start(0); anim::start(this); @@ -109,16 +188,26 @@ void ContextMenu::hideStart() { void ContextMenu::hideFinish() { hide(); + if (_deleteOnHide) { + deleteLater(); + } } void ContextMenu::showStart() { if (!isHidden() && a_opacity.current() == 1) { return; } + _selected = -1; _hiding = false; - show(); a_opacity.start(1); anim::start(this); + animStep(0); + setParent(0); + psUpdateOverlayed(this); + show(); + windowHandle()->requestActivate(); + activateWindow(); + setFocus(); } bool ContextMenu::animStep(float64 ms) { @@ -137,3 +226,27 @@ bool ContextMenu::animStep(float64 ms) { update(); return res; } + +void ContextMenu::deleteOnHide() { + _deleteOnHide = true; +} + +void ContextMenu::popup(const QPoint &p) { + QPoint w = p - QPoint(st::dropdownPadding.left(), st::dropdownPadding.top()); + QRect r = QDesktopWidget().screenGeometry(p); + if (w.x() + width() - st::dropdownPadding.right() > r.x() + r.width()) { + w.setX(r.x() + r.width() - width() + st::dropdownPadding.right()); + } + if (w.y() + height() - st::dropdownPadding.bottom() > r.y() + r.height()) { + w.setY(p.y() - height() + st::dropdownPadding.bottom()); + } + if (w.y() < 0) { + w.setY(0); + } + move(w); + showStart(); +} + +ContextMenu::~ContextMenu() { + clearActions(); +} diff --git a/Telegram/SourceFiles/gui/contextmenu.h b/Telegram/SourceFiles/gui/contextmenu.h index a869404a3..5b5c7102e 100644 --- a/Telegram/SourceFiles/gui/contextmenu.h +++ b/Telegram/SourceFiles/gui/contextmenu.h @@ -18,25 +18,35 @@ #pragma once #include +#include "gui/boxshadow.h" -class FlatButton; - -class ContextMenu : public QWidget, public Animated { +class ContextMenu : public TWidget, public Animated { Q_OBJECT public: - ContextMenu(QWidget *parent); - FlatButton *addButton(FlatButton *button); - void resetButtons(); + ContextMenu(QWidget *parent, const style::iconedButton &st = st::btnContext); + QAction *addAction(const QString &text, const QObject *receiver, const char* member); + void resetActions(); + + typedef QVector Actions; + Actions &actions(); void resizeEvent(QResizeEvent *e); void paintEvent(QPaintEvent *e); + void keyPressEvent(QKeyEvent *e); + + void focusOutEvent(QFocusEvent *e); void fastHide(); bool animStep(float64 ms); + void deleteOnHide(); + void popup(const QPoint &p); + + ~ContextMenu(); + public slots: void hideStart(); @@ -44,16 +54,31 @@ public slots: void showStart(); + void actionChanged(); + + void onActiveChanged(); + void buttonStateChanged(int oldState, ButtonStateChangeSource source); + private: + void clearActions(); void adjustButtons(); - typedef QVector Buttons; + typedef QVector Buttons; Buttons _buttons; + Actions _actions; + int32 _width, _height; bool _hiding; + const style::iconedButton &_buttonStyle; + + BoxShadow _shadow; + int32 _selected; + anim::fvalue a_opacity; + bool _deleteOnHide; + }; \ No newline at end of file diff --git a/Telegram/SourceFiles/gui/flatbutton.cpp b/Telegram/SourceFiles/gui/flatbutton.cpp index fb4c31281..902b5a015 100644 --- a/Telegram/SourceFiles/gui/flatbutton.cpp +++ b/Telegram/SourceFiles/gui/flatbutton.cpp @@ -150,6 +150,11 @@ void IconedButton::setOpacity(float64 opacity) { update(); } +void IconedButton::setText(const QString &text) { + _text = text; + update(); +} + bool IconedButton::animStep(float64 ms) { float64 dt = ms / _st.duration; bool res = true; diff --git a/Telegram/SourceFiles/gui/flatbutton.h b/Telegram/SourceFiles/gui/flatbutton.h index 0adffe355..cced05903 100644 --- a/Telegram/SourceFiles/gui/flatbutton.h +++ b/Telegram/SourceFiles/gui/flatbutton.h @@ -97,6 +97,8 @@ public: void setOpacity(float64 o); + void setText(const QString &text); + public slots: void onStateChange(int oldState, ButtonStateChangeSource source); diff --git a/Telegram/SourceFiles/gui/twidget.h b/Telegram/SourceFiles/gui/twidget.h index 5fbf1600c..e5ef14b03 100644 --- a/Telegram/SourceFiles/gui/twidget.h +++ b/Telegram/SourceFiles/gui/twidget.h @@ -34,6 +34,10 @@ public: virtual void leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget } + bool event(QEvent *e) { + return QWidget::event(e); + } + protected: void enterEvent(QEvent *e) { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 803472527..af7e7b336 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -617,21 +617,21 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _contextMenuLnk = textlnkOver(); if (_contextMenuLnk && dynamic_cast(_contextMenuLnk.data())) { - _menu = new QMenu(historyWidget); + _menu = new ContextMenu(historyWidget); if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); } _menu->addAction(lang(lng_context_open_link), this, SLOT(openContextUrl()))->setEnabled(true); _menu->addAction(lang(lng_context_copy_link), this, SLOT(copyContextUrl()))->setEnabled(true); } else if (_contextMenuLnk && dynamic_cast(_contextMenuLnk.data())) { - _menu = new QMenu(historyWidget); + _menu = new ContextMenu(historyWidget); if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); } _menu->addAction(lang(lng_context_open_email), this, SLOT(openContextUrl()))->setEnabled(true); _menu->addAction(lang(lng_context_copy_email), this, SLOT(copyContextUrl()))->setEnabled(true); } else if (_contextMenuLnk && dynamic_cast(_contextMenuLnk.data())) { - _menu = new QMenu(historyWidget); + _menu = new ContextMenu(historyWidget); if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); } @@ -643,7 +643,7 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data()); DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data()); if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument) { - _menu = new QMenu(historyWidget); + _menu = new ContextMenu(historyWidget); if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); } @@ -682,29 +682,29 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { HistoryServiceMsg *srv = dynamic_cast(item); if (isUponSelected > 0) { - if (!_menu) _menu = new QMenu(this); + if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); } else if (item && !isUponSelected && !_contextMenuLnk) { QString contextMenuText = item->selectedText(FullItemSel); if (!contextMenuText.isEmpty()) { - if (!_menu) _menu = new QMenu(this); + if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true); } } if (isUponSelected > 1) { - if (!_menu) _menu = new QMenu(this); + if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_forward_selected), historyWidget, SLOT(onForwardSelected())); _menu->addAction(lang(lng_context_delete_selected), historyWidget, SLOT(onDeleteSelected())); _menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected())); } else if (isUponSelected != -2) { if (canForward) { - if (!_menu) _menu = new QMenu(this); + if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true); } if (canDelete) { - if (!_menu) _menu = new QMenu(this); + if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang((msg && msg->uploading()) ? lng_context_cancel_upload : lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true); } } @@ -712,7 +712,7 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } if (_menu) { - _menu->setAttribute(Qt::WA_DeleteOnClose); + _menu->deleteOnHide(); connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); _menu->popup(e->globalPos()); e->accept(); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 958506bf0..b5508125c 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -155,7 +155,7 @@ private: uint64 _touchSpeedTime, _touchAccelerationTime, _touchTime; QTimer _touchScrollTimer; - QMenu *_menu; + ContextMenu *_menu; }; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index def61dde2..4cdd9387a 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -24,7 +24,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com #include "application.h" #include "gui/filedialog.h" -MediaView::MediaView() : QWidget(App::wnd()), +MediaView::MediaView() : TWidget(App::wnd()), _photo(0), _leftNavVisible(false), _rightNavVisible(false), _maxWidth(0), _maxHeight(0), _x(0), _y(0), _w(0), _full(-1), _history(0), _peer(0), _user(0), _from(0), _index(-1), _msgid(0), _loadRequest(0), _over(OverNone), _down(OverNone), _lastAction(-st::medviewDeltaFromLastAction, -st::medviewDeltaFromLastAction), _close(this, lang(lng_mediaview_close), st::medviewButton), @@ -327,16 +327,7 @@ void MediaView::showPhoto(PhotoData *photo) { _from = App::user(_photo->user); updateControls(); if (isHidden()) { -#ifdef Q_OS_WIN - bool wm = testAttribute(Qt::WA_Mapped), wv = testAttribute(Qt::WA_WState_Visible); - if (!wm) setAttribute(Qt::WA_Mapped, true); - if (!wv) setAttribute(Qt::WA_WState_Visible, true); - update(); - QEvent e(QEvent::UpdateRequest); - event(&e); - if (!wm) setAttribute(Qt::WA_Mapped, false); - if (!wv) setAttribute(Qt::WA_WState_Visible, false); -#endif + psUpdateOverlayed(this); show(); } } @@ -647,7 +638,7 @@ void MediaView::contextMenuEvent(QContextMenuEvent *e) { _menu->deleteLater(); _menu = 0; } - _menu = new QMenu(this); + _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_save_image), this, SLOT(onSave()))->setEnabled(true); _menu->addAction(lang(lng_context_copy_image), this, SLOT(onCopy()))->setEnabled(true); _menu->addAction(lang(lng_context_close_image), this, SLOT(onClose()))->setEnabled(true); @@ -657,7 +648,7 @@ void MediaView::contextMenuEvent(QContextMenuEvent *e) { } else if ((App::self() && App::self()->photoId == _photo->id) || (_photo->chat && _photo->chat->photoId == _photo->id)) { _menu->addAction(lang(lng_context_delete_image), this, SLOT(onDelete()))->setEnabled(true); } - _menu->setAttribute(Qt::WA_DeleteOnClose); + _menu->deleteOnHide(); connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); _menu->popup(e->globalPos()); e->accept(); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 20082fdf0..2a948d1a5 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -17,7 +17,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com */ #pragma once -class MediaView : public QWidget, public RPCSender, public Animated { +class MediaView : public TWidget, public RPCSender, public Animated { Q_OBJECT public: @@ -107,7 +107,7 @@ private: QPoint _lastAction; FlatButton _close, _save, _forward, _delete; - QMenu *_menu; + ContextMenu *_menu; bool _receiveMouse; bool _touchPress, _touchMove, _touchRightButton; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index aab819cc3..4faa78a78 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1038,7 +1038,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data()); DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data()); if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument) { - _menu = new QMenu(_overview); + _menu = new ContextMenu(_overview); if (App::hoveredLinkItem()) { _menu->addAction(lang(lng_context_to_msg), this, SLOT(goToMessage()))->setEnabled(true); } @@ -1068,7 +1068,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { App::contextItem(App::hoveredLinkItem()); } if (_menu) { - _menu->setAttribute(Qt::WA_DeleteOnClose); + _menu->deleteOnHide(); connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); _menu->popup(e->globalPos()); e->accept(); diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index bbe3084bd..900a541d2 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -178,7 +178,7 @@ private: uint64 _touchSpeedTime, _touchAccelerationTime, _touchTime; QTimer _touchScrollTimer; - QMenu *_menu; + ContextMenu *_menu; }; class OverviewWidget : public QWidget, public RPCSender, public Animated { diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index f0aa0916e..3e87fa013 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -710,9 +710,9 @@ void ProfileInner::contextMenuEvent(QContextMenuEvent *e) { if (!_phoneText.isEmpty()) { QRect info(_left + st::profilePhotoSize + st::profilePhoneLeft, st::profilePadding.top(), _width - st::profilePhotoSize - st::profilePhoneLeft, st::profilePhotoSize); if (info.contains(mapFromGlobal(e->globalPos()))) { - _menu = new QMenu(this); + _menu = new ContextMenu(this); _menu->addAction(lang(lng_profile_copy_phone), this, SLOT(onCopyPhone()))->setEnabled(true); - _menu->setAttribute(Qt::WA_DeleteOnClose); + _menu->deleteOnHide(); connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); _menu->popup(e->globalPos()); e->accept(); diff --git a/Telegram/SourceFiles/profilewidget.h b/Telegram/SourceFiles/profilewidget.h index 2d80cad51..9bf249d46 100644 --- a/Telegram/SourceFiles/profilewidget.h +++ b/Telegram/SourceFiles/profilewidget.h @@ -152,7 +152,7 @@ private: QPoint _lastPos; QString _onlineText; - QMenu *_menu; + ContextMenu *_menu; }; diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp index 7a90afe92..02f7eca14 100644 --- a/Telegram/SourceFiles/pspecific_linux.cpp +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -75,6 +75,9 @@ void PsMainWindow::psIdleTimeout() { } } +void PsMainWindow::psShowTrayMenu() { +} + bool PsMainWindow::psIsOnline(int state) const { if (state < 0) state = this->windowState(); if (state & Qt::WindowMinimized) { @@ -1010,3 +1013,6 @@ void psAutoStart(bool start, bool silent) { void psSendToMenu(bool send, bool silent) { } + +void psUpdateOverlayed(QWidget *widget) { +} diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h index 230479084..a671e5f0e 100644 --- a/Telegram/SourceFiles/pspecific_linux.h +++ b/Telegram/SourceFiles/pspecific_linux.h @@ -78,6 +78,7 @@ public slots: void psUpdateDelegate(); void psSavePosition(Qt::WindowState state = Qt::WindowActive); void psIdleTimeout(); + void psShowTrayMenu(); protected: @@ -181,3 +182,5 @@ void psOpenFile(const QString &name, bool openWith = false); void psShowInFolder(const QString &name); void psStart(); void psFinish(); + +void psUpdateOverlayed(QWidget *widget); diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index 6ade07aed..86d3dd74a 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -96,6 +96,9 @@ void PsMainWindow::psIdleTimeout() { } } +void PsMainWindow::psShowTrayMenu() { +} + bool PsMainWindow::psIsOnline(int state) const { if (state < 0) state = this->windowState(); if (state & Qt::WindowMinimized) { @@ -137,9 +140,10 @@ void PsMainWindow::psUpdateWorkmode() { } break; case dbiwmWindowOnly: { - if (trayIconMenu) trayIconMenu->deleteLater(); - trayIconMenu = 0; - if (trayIcon) trayIcon->deleteLater(); + if (trayIcon) { + trayIcon->setContextMenu(0); + trayIcon->deleteLater(); + } trayIcon = 0; } break; } @@ -972,3 +976,6 @@ void psAutoStart(bool start, bool silent) { void psSendToMenu(bool send, bool silent) { } + +void psUpdateOverlayed(QWidget *widget) { +} diff --git a/Telegram/SourceFiles/pspecific_mac.h b/Telegram/SourceFiles/pspecific_mac.h index 68dc30104..303b79d99 100644 --- a/Telegram/SourceFiles/pspecific_mac.h +++ b/Telegram/SourceFiles/pspecific_mac.h @@ -88,6 +88,7 @@ public slots: void psUpdateDelegate(); void psSavePosition(Qt::WindowState state = Qt::WindowActive); void psIdleTimeout(); + void psShowTrayMenu(); protected: @@ -197,3 +198,5 @@ void psOpenFile(const QString &name, bool openWith = false); void psShowInFolder(const QString &name); void psStart(); void psFinish(); + +void psUpdateOverlayed(QWidget *widget); diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index 646b67f97..c22d68e4a 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -891,6 +891,10 @@ void PsMainWindow::psIdleTimeout() { } } +void PsMainWindow::psShowTrayMenu() { + trayIconMenu->popup(QCursor::pos()); +} + bool PsMainWindow::psIsActive(int state) const { if (state < 0) state = this->windowState(); return isActiveWindow() && isVisible() && !(state & Qt::WindowMinimized) && !psIdle; @@ -956,9 +960,10 @@ void PsMainWindow::psUpdateWorkmode() { } break; case dbiwmWindowOnly: { - if (trayIconMenu) trayIconMenu->deleteLater(); - trayIconMenu = 0; - if (trayIcon) trayIcon->deleteLater(); + if (trayIcon) { + trayIcon->setContextMenu(0); + trayIcon->deleteLater(); + } trayIcon = 0; HWND psOwner = (HWND)GetWindowLong(ps_hWnd, GWL_HWNDPARENT); @@ -2200,6 +2205,17 @@ void psSendToMenu(bool send, bool silent) { _manageAppLnk(send, silent, CSIDL_SENDTO, L"-sendpath", L"Telegram send to link.\nYou can disable send to menu item in Telegram settings."); } +void psUpdateOverlayed(TWidget *widget) { + bool wm = widget->testAttribute(Qt::WA_Mapped), wv = widget->testAttribute(Qt::WA_WState_Visible); + if (!wm) widget->setAttribute(Qt::WA_Mapped, true); + if (!wv) widget->setAttribute(Qt::WA_WState_Visible, true); + widget->update(); + QEvent e(QEvent::UpdateRequest); + widget->event(&e); + if (!wm) widget->setAttribute(Qt::WA_Mapped, false); + if (!wv) widget->setAttribute(Qt::WA_WState_Visible, false); +} + #ifdef _NEED_WIN_GENERATE_DUMP static const WCHAR *_programName = AppName; // folder in APPDATA, if current path is unavailable for writing static const WCHAR *_exeName = L"Telegram.exe"; diff --git a/Telegram/SourceFiles/pspecific_wnd.h b/Telegram/SourceFiles/pspecific_wnd.h index 46284bee6..df075f433 100644 --- a/Telegram/SourceFiles/pspecific_wnd.h +++ b/Telegram/SourceFiles/pspecific_wnd.h @@ -77,6 +77,7 @@ public slots: void psUpdateDelegate(); void psSavePosition(Qt::WindowState state = Qt::WindowActive); void psIdleTimeout(); + void psShowTrayMenu(); protected: @@ -84,7 +85,7 @@ protected: bool posInited; QSystemTrayIcon *trayIcon; - QMenu *trayIconMenu; + ContextMenu *trayIconMenu; QImage icon256; virtual void setupTrayIcon() = 0; @@ -191,3 +192,5 @@ void psOpenFile(const QString &name, bool openWith = false); void psShowInFolder(const QString &name); void psStart(); void psFinish(); + +void psUpdateOverlayed(TWidget *widget); diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 3fd52fed3..1e6bc6a0e 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -335,16 +335,17 @@ NotifyWindow::~NotifyWindow() { if (App::wnd()) App::wnd()->notifyShowNext(this); } -Window::Window(QWidget *parent) : PsMainWindow(parent), - intro(0), main(0), settings(0), layerBG(0), _topWidget(0), - _connecting(0), _tempDeleter(0), _tempDeleterThread(0), myIcon(QPixmap::fromImage(icon256)), dragging(false), _inactivePress(false), _mediaView(0) { +Window::Window(QWidget *parent) : PsMainWindow(parent), +intro(0), main(0), settings(0), layerBG(0), _topWidget(0), +_connecting(0), _tempDeleter(0), _tempDeleterThread(0), myIcon(QPixmap::fromImage(icon256)), dragging(false), _inactivePress(false), _mediaView(0) { icon16 = icon256.scaledToWidth(16, Qt::SmoothTransformation); icon32 = icon256.scaledToWidth(32, Qt::SmoothTransformation); icon64 = icon256.scaledToWidth(64, Qt::SmoothTransformation); - if (objectName().isEmpty()) + if (objectName().isEmpty()) { setObjectName(qsl("MainWindow")); + } resize(st::wndDefWidth, st::wndDefHeight); setWindowOpacity(1); setLocale(QLocale(QLocale::English, QLocale::UnitedStates)); @@ -395,6 +396,21 @@ void Window::init() { psUpdateWorkmode(); } +void Window::firstShow() { +#ifdef Q_OS_WIN + trayIconMenu = new ContextMenu(this); +#else + trayIconMenu = new QMenu(this); + trayIconMenu->setFont(QFont("Tahoma")); +#endif + trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray()))->setEnabled(true); + trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()))->setEnabled(true); + + psFirstShow(); + + updateTrayMenu(); +} + QWidget *Window::filedialogParent() { return (_mediaView && _mediaView->isVisible()) ? (QWidget*)_mediaView : (QWidget*)this; } @@ -770,22 +786,17 @@ void Window::setupTrayIcon() { psUpdateDelegate(); } -void Window::updateTrayMenu(int windowState) { - bool active = psIsActive(windowState); - if (trayIconMenu) { - trayIconMenu->deleteLater(); - trayIconMenu = 0; - } - if (active || cPlatform() != dbipMac) { - trayIconMenu = new QMenu(this); - trayIconMenu->setFont(QFont("Tahoma")); - QAction *a; - a = trayIconMenu->addAction(lang(active ? lng_minimize_to_tray : lng_open_from_tray), this, active ? SLOT(minimizeToTray()) : SLOT(showFromTray())); - a->setEnabled(true); - a = trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray())); - a->setEnabled(true); - } - trayIcon->setContextMenu(trayIconMenu); +void Window::updateTrayMenu(bool force) { + if (!trayIconMenu || cPlatform() == dbipWindows && !force) return; + + bool active = psIsActive(); + QAction *first = trayIconMenu->actions().at(0); + first->setText(lang(active ? lng_minimize_to_tray : lng_open_from_tray)); + disconnect(first, SIGNAL(triggered(bool)), 0, 0); + connect(first, SIGNAL(triggered(bool)), this, active ? SLOT(minimizeToTray()) : SLOT(showFromTray())); +#ifndef Q_OS_WIN + trayIcon->setContextMenu((active || cPlatform() != dbipMac) ? trayIconMenu : 0); +#endif } void Window::quitFromTray() { @@ -859,7 +870,10 @@ void Window::showFromTray(QSystemTrayIcon::ActivationReason reason) { void Window::toggleTray(QSystemTrayIcon::ActivationReason reason) { if (trayIconMenu && cPlatform() == dbipMac) return; - if (reason != QSystemTrayIcon::Context) { + if (reason == QSystemTrayIcon::Context) { + updateTrayMenu(true); + QTimer::singleShot(1, this, SLOT(psShowTrayMenu())); + } else { if (psIsActive()) { minimizeToTray(); } else { diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index 0e90971eb..12b7ed42c 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -133,6 +133,7 @@ public: ~Window(); void init(); + void firstShow(); QWidget *filedialogParent(); @@ -244,7 +245,7 @@ public slots: void onTempDirClearFailed(); void notifyFire(); - void updateTrayMenu(int windowState = -1); + void updateTrayMenu(bool force = false); signals: diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 96581f127..4109d09de 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -186,6 +186,10 @@ true true + + true + true + true true @@ -386,6 +390,10 @@ true true + + true + true + true true @@ -595,6 +603,10 @@ true true + + true + true + true true @@ -776,6 +788,7 @@ + @@ -1214,6 +1227,20 @@ $(QTDIR)\bin\moc.exe;%(FullPath) + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing contextmenu.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/gui/contextmenu.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.3.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.3.1\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing contextmenu.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/gui/contextmenu.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.3.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.3.1\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing contextmenu.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/gui/contextmenu.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.3.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.3.1\QtGui" + Moc%27ing flatcheckbox.h... diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index d66a69c0b..f95130947 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -704,6 +704,18 @@ Generated Files\Release + + gui + + + Generated Files\Deploy + + + Generated Files\Debug + + + Generated Files\Release + @@ -951,6 +963,9 @@ Source Files + + gui +