Dropdown replaced by Ui::DropdownMenu. ScrolledWidget removed.

Ui::DropdownMenu is like Ui::PopupMenu, both based on Ui::Menu.
This commit is contained in:
John Preston 2016-10-26 19:43:13 +03:00
parent 140e6d6268
commit 6e33f039b2
104 changed files with 2688 additions and 2308 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -272,36 +272,10 @@ solidScroll: flatScroll {
duration: 150;
hiding: 0;
}
defaultDropdownDuration: 150;
defaultDropdownPadding: margins(10px, 10px, 10px, 10px);
defaultDropdownShadow: icon {{ "dropdown_shadow", windowShadowFg }};
defaultPopupMenu: PopupMenu {
skip: 5px;
shadow: defaultDropdownShadow;
shadowShift: 1px;
itemBg: white;
itemBgOver: overBg;
itemFg: black;
itemFgOver: black;
itemFgDisabled: #ccc;
itemFgShortcut: #999;
itemFgShortcutOver: #7c99b2;
itemFgShortcutDisabled: #ccc;
itemPadding: margins(17px, 8px, 17px, 7px);
itemFont: normalFont;
separatorPadding: margins(0px, 5px, 0px, 5px);
separatorWidth: 1px;
separatorFg: #f1f1f1;
arrow: icon {{ "dropdown_submenu_arrow", #373737 }};
duration: 120;
widthMin: 180px;
widthMax: 300px;
}
defaultDropdownShadowShift: 1px;
defaultTooltip: Tooltip {
textBg: #eef2f5;
@ -1088,54 +1062,6 @@ btnUnblock: flatButton(btnSend) {
downColor: #db6352;
}
btnAttachDocument: iconedButton(btnDefIconed) {
icon: sprite(218px, 68px, 24px, 24px);
iconPos: point(11px, 11px);
downIcon: sprite(218px, 68px, 24px, 24px);
downIconPos: point(11px, 12px);
overBgColor: btnWhiteHover;
width: 46px;
height: 46px;
}
btnAttachPhoto: iconedButton(btnAttachDocument) {
icon: sprite(118px, 0px, 24px, 24px);
downIcon: sprite(118px, 0px, 24px, 24px);
}
btnAttachEmoji: iconedButton(btnAttachDocument) {
overBgColor: white;
icon: sprite(374px, 344px, 21px, 22px);
iconPos: point(6px, 12px);
downIcon: sprite(374px, 344px, 21px, 22px);
downIconPos: point(6px, 12px);
width: 33px;
}
emojiCircle: size(19px, 19px);
emojiCirclePeriod: 1500;
emojiCircleDuration: 500;
emojiCircleTop: 13px;
emojiCircleLine: 2px;
emojiCircleFg: #b9b9b9;
emojiCirclePart: 3.5;
btnBotKbShow: iconedButton(btnAttachEmoji) {
icon: sprite(375px, 74px, 21px, 21px);
iconPos: point(6px, 12px);
downIcon: sprite(375px, 74px, 21px, 21px);
downIconPos: point(6px, 12px);
}
btnBotCmdStart: iconedButton(btnAttachEmoji) {
icon: sprite(354px, 74px, 21px, 21px);
iconPos: point(6px, 12px);
downIcon: sprite(354px, 74px, 21px, 21px);
downIconPos: point(6px, 12px);
}
btnBotKbHide: iconedButton(btnAttachEmoji) {
icon: sprite(373px, 95px, 23px, 14px);
iconPos: point(5px, 17px);
downIcon: sprite(373px, 95px, 23px, 14px);
downIconPos: point(5px, 17px);
}
silentToggle: flatCheckbox {
textColor: black;
bgColor: white;
@ -1158,15 +1084,6 @@ silentToggle: flatCheckbox {
imagePos: point(6px, 12px);
}
btnRecordAudio: sprite(379px, 390px, 16px, 24px);
btnRecordAudioActive: sprite(379px, 366px, 16px, 24px);
recordSignalColor: #f17077;
recordSignalMin: 5px;
recordSignalMax: 12px;
recordCancel: #aaa;
recordCancelActive: #ec6466;
recordFont: font(13px);
recordTextTop: 14px;
replySkip: 51px;
replyColor: #377aae;
@ -1418,68 +1335,6 @@ connectingBG: #fffe;
connectingColor: #777;
connectingPadding: margins(5px, 5px, 5px, 5px);
dropdownDef: dropdown {
border: 1px;
borderColor: #ebebeb;
padding: margins(10px, 10px, 10px, 10px);
shadow: defaultDropdownShadow;
shadowShift: 1px;
duration: 150;
width: 0px;
}
defaultInnerDropdown: InnerDropdown {
padding: margins(10px, 10px, 10px, 10px);
shadow: defaultDropdownShadow;
shadowShift: 1px;
duration: 150;
}
dropdownAttachDocument: iconedButton(btnAttachDocument) {
iconPos: point(14px, 13px);
downIconPos: point(14px, 14px);
width: 172px;
height: 49px;
color: black;
font: font(16px);
textPos: point(50px, 13px);
downTextPos: point(50px, 14px);
}
dropdownAttachPhoto: iconedButton(dropdownAttachDocument) {
icon: sprite(118px, 0px, 24px, 24px);
downIcon: sprite(118px, 0px, 24px, 24px);
}
dropdownMediaPhotos: iconedButton(dropdownAttachPhoto) {
width: 200px;
}
dropdownMediaVideos: iconedButton(dropdownMediaPhotos) {
icon: sprite(92px, 348px, 24px, 24px);
downIcon: sprite(92px, 348px, 24px, 24px);
}
dropdownMediaSongs: iconedButton(dropdownMediaPhotos) {
icon: sprite(60px, 374px, 24px, 26px);
downIcon: sprite(60px, 374px, 24px, 26px);
iconPos: point(12px, 12px);
downIconPos: point(12px, 13px);
}
dropdownMediaDocuments: iconedButton(dropdownAttachDocument) {
width: 200px;
}
dropdownMediaAudios: iconedButton(dropdownMediaDocuments) {
icon: sprite(62px, 348px, 24px, 24px);
downIcon: sprite(62px, 348px, 24px, 24px);
}
dropdownMediaLinks: iconedButton(dropdownMediaDocuments) {
icon: sprite(372px, 414px, 24px, 24px);
downIcon: sprite(372px, 414px, 24px, 24px);
}
dragFont: font(28px semibold);
dragSubfont: font(20px semibold);
dragColor: #777;
@ -1720,49 +1575,6 @@ mvControlMargin: 0px;
mvControlSize: 90px;
mvIconSize: size(60px, 56px);
mvDropdown: dropdown(dropdownDef) {
shadow: icon {};
padding: margins(11px, 12px, 11px, 12px);
border: 0px;
width: 182px;
}
mvButton: iconedButton(btnDefIconed) {
bgColor: #383838;
overBgColor: #505050;
font: font(fsize);
opacity: 1.;
overOpacity: 1.;
width: -32px;
height: 36px;
color: white;
textPos: point(16px, 9px);
downTextPos: point(16px, 10px);
duration: 0;
}
mvPopupMenu: PopupMenu(defaultPopupMenu) {
shadow: icon {};
itemBg: #383838;
itemBgOver: #505050;
itemFg: white;
itemFgOver: white;
itemFgDisabled: #999;
itemFgShortcut: #eee;
itemFgShortcutOver: #fff;
itemFgShortcutDisabled: #999;
separatorFg: #484848;
}
mvContextButton: iconedButton(mvButton) {
bgColor: #383838E6;
overBgColor: #505050E7;
}
mvWaitHide: 2000;
mvHideDuration: 1000;
mvShowDuration: 200;
@ -1818,23 +1630,6 @@ macAlwaysThisAppTop: 4;
macAppHintTop: 8;
macCautionIconSize: 16;
btnContext: iconedButton(btnDefIconed) {
bgColor: white;
overBgColor: btnWhiteHover;
font: font(14px);
opacity: 1.;
overOpacity: 1.;
width: -32px;
height: 36px;
color: black;
textPos: point(16px, 7px);
downTextPos: point(16px, 8px);
}
radialSize: size(50px, 50px);
radialLine: 3px;
radialDuration: 350;

View file

@ -227,59 +227,6 @@ switcher {
duration: int;
}
dropdown {
border: pixels;
borderColor: color;
padding: margins;
shadow: icon;
shadowShift: pixels;
duration: int;
width: pixels;
}
InnerDropdown {
padding: margins;
shadow: icon;
shadowShift: pixels;
duration: int;
width: pixels;
scrollMargin: margins;
scrollPadding: margins;
}
PopupMenu {
skip: pixels;
shadow: icon;
shadowShift: pixels;
itemBg: color;
itemBgOver: color;
itemFg: color;
itemFgOver: color;
itemFgDisabled: color;
itemFgShortcut: color;
itemFgShortcutOver: color;
itemFgShortcutDisabled: color;
itemPadding: margins;
itemFont: font;
separatorPadding: margins;
separatorWidth: pixels;
separatorFg: color;
arrow: icon;
duration: int;
widthMin: pixels;
widthMax: pixels;
}
Tooltip {
textBg: color;
textFg: color;

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

View file

@ -28,7 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "boxes/confirmbox.h"
#include "ui/filedialog.h"
#include "ui/popupmenu.h"
#include "ui/widgets/tooltip.h"
#include "langloaderplain.h"
#include "localstorage.h"
#include "autoupdater.h"
@ -926,7 +926,7 @@ void AppClass::onAppStateChanged(Qt::ApplicationState state) {
_window->updateIsActive((state == Qt::ApplicationActive) ? Global::OnlineFocusTimeout() : Global::OfflineBlurTimeout());
}
if (state != Qt::ApplicationActive) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
}
}

View file

@ -201,7 +201,7 @@ void ScrollableBox::resizeEvent(QResizeEvent *e) {
AbstractBox::resizeEvent(e);
}
void ScrollableBox::init(ScrolledWidget *inner, int bottomSkip, int topSkip) {
void ScrollableBox::init(TWidget *inner, int bottomSkip, int topSkip) {
_bottomSkip = bottomSkip;
_topSkip = topSkip;
_scroll->setOwnedWidget(inner);

View file

@ -105,7 +105,7 @@ public:
ScrollableBox(const style::flatScroll &scroll, int w = st::boxWideWidth);
protected:
void init(ScrolledWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
void init(TWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
void setScrollSkips(int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
void resizeEvent(QResizeEvent *e) override;

View file

@ -54,7 +54,7 @@ void BackgroundBox::onBackgroundChosen(int index) {
onClose();
}
BackgroundBox::Inner::Inner(QWidget *parent) : ScrolledWidget(parent)
BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent)
, _bgCount(0)
, _rows(0)
, _over(-1)

View file

@ -42,7 +42,7 @@ private:
};
// This class is hold in header because it requires Qt preprocessing.
class BackgroundBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber {
class BackgroundBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
Q_OBJECT
public:

View file

@ -555,7 +555,7 @@ ContactsBox::Inner::ContactData::ContactData(PeerData *peer, base::lambda_wrap<v
: checkbox(std_::make_unique<Ui::RoundImageCheckbox>(st::contactsPhotoCheckbox, std_::move(updateCallback), PaintUserpicCallback(peer))) {
}
ContactsBox::Inner::Inner(QWidget *parent, CreatingGroupType creating) : ScrolledWidget(parent)
ContactsBox::Inner::Inner(QWidget *parent, CreatingGroupType creating) : TWidget(parent)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _newItemHeight(creating == CreatingGroupNone ? st::contactsNewItemHeight : 0)
, _creating(creating)
@ -565,7 +565,7 @@ ContactsBox::Inner::Inner(QWidget *parent, CreatingGroupType creating) : Scrolle
init();
}
ContactsBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already) : ScrolledWidget(parent)
ContactsBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already) : TWidget(parent)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _channel(channel)
, _membersFilter(membersFilter)
@ -583,7 +583,7 @@ namespace {
}
}
ContactsBox::Inner::Inner(QWidget *parent, ChatData *chat, MembersFilter membersFilter) : ScrolledWidget(parent)
ContactsBox::Inner::Inner(QWidget *parent, ChatData *chat, MembersFilter membersFilter) : TWidget(parent)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _chat(chat)
, _membersFilter(membersFilter)
@ -615,7 +615,7 @@ void ContactsBox::Inner::addDialogsToList(FilterCallback callback) {
}
}
ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : ScrolledWidget(parent)
ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _bot(bot)
, _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox)

View file

@ -132,7 +132,7 @@ private:
};
// This class is hold in header because it requires Qt preprocessing.
class ContactsBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber {
class ContactsBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
Q_OBJECT
public:

View file

@ -97,7 +97,7 @@ void MembersBox::onAdminAdded() {
_loadTimer.start(ReloadChannelMembersTimeout);
}
MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter filter) : ScrolledWidget(parent)
MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter filter) : TWidget(parent)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _newItemHeight((channel->amCreator() && (channel->membersCount() < (channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!channel->isMegagroup() && !channel->isPublic()) || filter == MembersFilter::Admins)) ? st::contactsNewItemHeight : 0)
, _newItemSel(false)

View file

@ -61,7 +61,7 @@ private:
};
// This class is hold in header because it requires Qt preprocessing.
class MembersBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber {
class MembersBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
Q_OBJECT
public:

View file

@ -242,7 +242,7 @@ void SessionsBox::onTerminateAll() {
}
}
SessionsBox::Inner::Inner(QWidget *parent, SessionsBox::List *list, SessionsBox::Data *current) : ScrolledWidget(parent)
SessionsBox::Inner::Inner(QWidget *parent, SessionsBox::List *list, SessionsBox::Data *current) : TWidget(parent)
, _list(list)
, _current(current)
, _terminating(0)

View file

@ -72,7 +72,7 @@ private:
};
// This class is hold in header because it requires Qt preprocessing.
class SessionsBox::Inner : public ScrolledWidget, public RPCSender {
class SessionsBox::Inner : public TWidget, public RPCSender {
Q_OBJECT
public:

View file

@ -289,7 +289,7 @@ void ShareBox::onScroll() {
_inner->setVisibleTopBottom(scrollTop, scrollTop + scroll->height());
}
ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : ScrolledWidget(parent)
ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : TWidget(parent)
, _filterCallback(std_::move(filterCallback))
, _chatsIndexed(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) {
_rowsTop = st::shareRowsTop;

View file

@ -113,7 +113,7 @@ private:
};
// This class is hold in header because it requires Qt preprocessing.
class ShareBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber {
class ShareBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
Q_OBJECT
public:

View file

@ -498,7 +498,7 @@ void StickersBox::showAll() {
ItemListBox::showAll();
}
StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : ScrolledWidget(parent)
StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : TWidget(parent)
, _section(section)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _a_shifting(animation(this, &Inner::step_shifting))
@ -511,7 +511,7 @@ StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : Scrol
setup();
}
StickersBox::Inner::Inner(QWidget *parent, const Stickers::Order &archivedIds) : ScrolledWidget(parent)
StickersBox::Inner::Inner(QWidget *parent, const Stickers::Order &archivedIds) : TWidget(parent)
, _section(StickersBox::Section::ArchivedPart)
, _archivedIds(archivedIds)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())

View file

@ -103,7 +103,7 @@ private:
int32 stickerPacksCount(bool includeDisabledOfficial = false);
// This class is hold in header because it requires Qt preprocessing.
class StickersBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber {
class StickersBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
Q_OBJECT
public:

View file

@ -147,7 +147,7 @@ void StickerSetBox::resizeEvent(QResizeEvent *e) {
}
}
StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : ScrolledWidget(parent)
StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : TWidget(parent)
, _input(set) {
switch (set.type()) {
case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break;

View file

@ -65,7 +65,7 @@ private:
};
// This class is hold in header because it requires Qt preprocessing.
class StickerSetBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber {
class StickerSetBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
Q_OBJECT
public:

View file

@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "core/qthelp_regex.h"
#include "core/qthelp_url.h"
#include "localstorage.h"
#include "ui/popupmenu.h"
#include "ui/widgets/tooltip.h"
QString UrlClickHandler::copyToClipboardContextItemText() const {
return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link);
@ -64,7 +64,7 @@ QString tryConvertUrlToLocal(QString url) {
} // namespace
void UrlClickHandler::doOpen(QString url) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
if (isEmail(url)) {
QUrl u(qstr("mailto:") + url);

View file

@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "dialogs/dialogs_layout.h"
#include "styles/style_dialogs.h"
#include "ui/buttons/round_button.h"
#include "ui/popupmenu.h"
#include "ui/widgets/popup_menu.h"
#include "data/data_drafts.h"
#include "lang.h"
#include "application.h"
@ -639,7 +639,7 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) {
if (!history) return;
_menuPeer = history->peer;
_menu = new PopupMenu();
_menu = new Ui::PopupMenu();
_menu->addAction(lang((_menuPeer->isChat() || _menuPeer->isMegagroup()) ? lng_context_view_group : (_menuPeer->isUser() ? lng_context_view_profile : lng_context_view_channel)), this, SLOT(onContextProfile()))->setEnabled(true);
_menu->addAction(lang(menuPeerMuted() ? lng_enable_notifications_from_tray : lng_disable_notifications_from_tray), this, SLOT(onContextToggleNotifications()))->setEnabled(true);
_menu->addAction(lang(lng_profile_search_messages), this, SLOT(onContextSearch()))->setEnabled(true);

View file

@ -30,10 +30,10 @@ class IndexedList;
namespace Ui {
class RoundButton;
class PopupMenu;
} // namespace Ui
class MainWidget;
class PopupMenu;
enum DialogsSearchRequestType {
DialogsSearchFromStart,
@ -230,7 +230,7 @@ private:
PeerData *_menuPeer = nullptr;
PeerData *_menuActionPeer = nullptr;
PopupMenu *_menu = nullptr;
Ui::PopupMenu *_menu = nullptr;
};

View file

@ -1,456 +0,0 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "dropdown.h"
#include "styles/style_stickers.h"
#include "boxes/confirmbox.h"
#include "boxes/stickersetbox.h"
#include "inline_bots/inline_bot_result.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "dialogs/dialogs_layout.h"
#include "historywidget.h"
#include "localstorage.h"
#include "lang.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "mainwidget.h"
Dropdown::Dropdown(QWidget *parent, const style::dropdown &st) : TWidget(parent)
, _st(st)
, _width(_st.width)
, a_opacity(0)
, _a_appearance(animation(this, &Dropdown::step_appearance))
, _shadow(_st.shadow) {
resetButtons();
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
}
void Dropdown::ignoreShow(bool ignore) {
_ignore = ignore;
}
void Dropdown::onWndActiveChanged() {
if (!App::wnd()->windowHandle()->isActive() && !isHidden()) {
leaveEvent(0);
}
}
IconedButton *Dropdown::addButton(IconedButton *button) {
button->setParent(this);
int32 nw = _st.padding.left() + _st.padding.right() + button->width();
if (nw > _width) {
_width = nw;
for (int32 i = 0, l = _buttons.size(); i < l; ++i) _buttons[i]->resize(_width - _st.padding.left() - _st.padding.right(), _buttons[i]->height());
} else {
button->resize(_width - _st.padding.left() - _st.padding.right(), button->height());
}
if (!button->isHidden()) {
if (_height > _st.padding.top() + _st.padding.bottom()) {
_height += _st.border;
}
_height += button->height();
}
_buttons.push_back(button);
connect(button, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(buttonStateChanged(int, ButtonStateChangeSource)));
resize(_width, _height);
return button;
}
void Dropdown::resetButtons() {
_width = qMax(_st.padding.left() + _st.padding.right(), int(_st.width));
_height = _st.padding.top() + _st.padding.bottom();
for (int32 i = 0, l = _buttons.size(); i < l; ++i) {
delete _buttons[i];
}
_buttons.clear();
resize(_width, _height);
_selected = -1;
}
void Dropdown::updateButtons() {
int32 top = _st.padding.top(), starttop = top;
for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) {
if (!(*i)->isHidden()) {
(*i)->move(_st.padding.left(), top);
if ((*i)->width() != _width - _st.padding.left() - _st.padding.right()) {
(*i)->resize(_width - _st.padding.left() - _st.padding.right(), (*i)->height());
}
top += (*i)->height() + _st.border;
}
}
_height = top + _st.padding.bottom() - (top > starttop ? _st.border : 0);
resize(_width, _height);
}
void Dropdown::resizeEvent(QResizeEvent *e) {
int32 top = _st.padding.top();
for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) {
if (!(*i)->isHidden()) {
(*i)->move(_st.padding.left(), top);
top += (*i)->height() + _st.border;
}
}
}
void Dropdown::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
}
// draw shadow
QRect r(_st.padding.left(), _st.padding.top(), _width - _st.padding.left() - _st.padding.right(), _height - _st.padding.top() - _st.padding.bottom());
_shadow.paint(p, r, _st.shadowShift);
if (!_buttons.isEmpty() && _st.border > 0) { // paint separators
p.setPen(_st.borderColor->p);
int32 top = _st.padding.top(), i = 0, l = _buttons.size();
for (; i < l; ++i) {
if (!_buttons.at(i)->isHidden()) break;
}
if (i < l) {
top += _buttons.at(i)->height();
for (++i; i < l; ++i) {
if (!_buttons.at(i)->isHidden()) {
p.fillRect(_st.padding.left(), top, _width - _st.padding.left() - _st.padding.right(), _st.border, _st.borderColor->b);
top += _st.border + _buttons.at(i)->height();
}
}
}
}
}
void Dropdown::enterEvent(QEvent *e) {
_hideTimer.stop();
if (_hiding) showStart();
return TWidget::enterEvent(e);
}
void Dropdown::leaveEvent(QEvent *e) {
if (_a_appearance.animating()) {
hideStart();
} else {
_hideTimer.start(300);
}
return TWidget::leaveEvent(e);
}
void Dropdown::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;
}
} else if (e->key() == Qt::Key_Escape) {
hideStart();
return;
}
if ((e->key() != Qt::Key_Up && e->key() != Qt::Key_Down) || _buttons.size() < 1) return;
bool none = (_selected < 0 || _selected >= _buttons.size());
int32 delta = (e->key() == Qt::Key_Down ? 1 : -1);
int32 newSelected = none ? (e->key() == Qt::Key_Down ? 0 : _buttons.size() - 1) : (_selected + delta);
if (newSelected < 0) {
newSelected = _buttons.size() - 1;
} else if (newSelected >= _buttons.size()) {
newSelected = 0;
}
int32 startFrom = newSelected;
while (_buttons.at(newSelected)->isHidden()) {
newSelected += delta;
if (newSelected < 0) {
newSelected = _buttons.size() - 1;
} else if (newSelected >= _buttons.size()) {
newSelected = 0;
}
if (newSelected == startFrom) return;
}
if (!none) {
_buttons[_selected]->setOver(false);
}
_selected = newSelected;
_buttons[_selected]->setOver(true);
}
void Dropdown::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) {
bool found = false;
for (int32 i = 0, l = _buttons.size(); i < l; ++i) {
if (_buttons[i]->getState() & Button::StateOver) {
found = true;
if (i != _selected) {
int32 sel = _selected;
_selected = i;
if (sel >= 0 && sel < _buttons.size()) {
_buttons[sel]->setOver(false);
}
}
}
}
if (!found) {
_selected = -1;
}
}
}
void Dropdown::otherEnter() {
_hideTimer.stop();
showStart();
}
void Dropdown::otherLeave() {
if (_a_appearance.animating()) {
hideStart();
} else {
_hideTimer.start(0);
}
}
void Dropdown::fastHide() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
_hideTimer.stop();
hide();
}
void Dropdown::adjustButtons() {
for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) {
(*i)->setOpacity(a_opacity.current());
}
}
void Dropdown::hideStart() {
_hiding = true;
a_opacity.start(0);
_a_appearance.start();
}
void Dropdown::hideFinish() {
emit hiding();
hide();
for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) {
(*i)->clearState();
}
_selected = -1;
}
void Dropdown::showStart() {
if (!isHidden() && a_opacity.current() == 1) {
return;
}
_selected = -1;
_hiding = false;
show();
a_opacity.start(1);
_a_appearance.start();
}
void Dropdown::step_appearance(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
if (dt >= 1) {
_a_appearance.stop();
a_opacity.finish();
if (_hiding) {
hideFinish();
}
} else {
a_opacity.update(dt, anim::linear);
}
adjustButtons();
if (timer) update();
}
bool Dropdown::eventFilter(QObject *obj, QEvent *e) {
if (e->type() == QEvent::Enter) {
otherEnter();
} else if (e->type() == QEvent::Leave) {
otherLeave();
} else if (e->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton) {
if (isHidden() || _hiding) {
otherEnter();
} else {
otherLeave();
}
}
return false;
}
DragArea::DragArea(QWidget *parent) : TWidget(parent)
, _hiding(false)
, _in(false)
, a_opacity(0)
, a_color(st::dragColor->c)
, _a_appearance(animation(this, &DragArea::step_appearance))
, _shadow(st::boxShadow) {
setMouseTracking(true);
setAcceptDrops(true);
}
void DragArea::mouseMoveEvent(QMouseEvent *e) {
if (_hiding) return;
bool newIn = QRect(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()).contains(e->pos());
if (newIn != _in) {
_in = newIn;
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
}
void DragArea::dragMoveEvent(QDragMoveEvent *e) {
QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom());
bool newIn = r.contains(e->pos());
if (newIn != _in) {
_in = newIn;
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction);
e->accept();
}
void DragArea::setText(const QString &text, const QString &subtext) {
_text = text;
_subtext = subtext;
update();
}
void DragArea::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
}
QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom());
// draw shadow
_shadow.paint(p, r, st::boxShadowShift);
p.fillRect(r, st::white->b);
p.setPen(a_color.current());
p.setFont(st::dragFont->f);
p.drawText(QRect(0, (height() - st::dragHeight) / 2, width(), st::dragFont->height), _text, QTextOption(style::al_top));
p.setFont(st::dragSubfont->f);
p.drawText(QRect(0, (height() + st::dragHeight) / 2 - st::dragSubfont->height, width(), st::dragSubfont->height * 2), _subtext, QTextOption(style::al_top));
}
void DragArea::dragEnterEvent(QDragEnterEvent *e) {
static_cast<HistoryWidget*>(parentWidget())->dragEnterEvent(e);
e->setDropAction(Qt::IgnoreAction);
e->accept();
}
void DragArea::dragLeaveEvent(QDragLeaveEvent *e) {
static_cast<HistoryWidget*>(parentWidget())->dragLeaveEvent(e);
_in = false;
a_opacity.start(_hiding ? 0 : 1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
void DragArea::dropEvent(QDropEvent *e) {
static_cast<HistoryWidget*>(parentWidget())->dropEvent(e);
if (e->isAccepted()) {
emit dropped(e->mimeData());
}
}
void DragArea::otherEnter() {
showStart();
}
void DragArea::otherLeave() {
hideStart();
}
void DragArea::fastHide() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
hide();
}
void DragArea::hideStart() {
_hiding = true;
_in = false;
a_opacity.start(0);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
void DragArea::hideFinish() {
hide();
_in = false;
a_color = anim::cvalue(st::dragColor->c);
}
void DragArea::showStart() {
_hiding = false;
show();
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
void DragArea::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::dropdownDef.duration;
if (dt >= 1) {
a_opacity.finish();
a_color.finish();
if (_hiding) {
hideFinish();
}
_a_appearance.stop();
} else {
a_opacity.update(dt, anim::linear);
a_color.update(dt, anim::linear);
}
if (timer) update();
}

View file

@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "apiwrap.h"
#include "localstorage.h"
#include "styles/style_history.h"
FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent)
, _scroll(this, st::mentionScroll)
@ -333,7 +334,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
void FieldAutocomplete::rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const StickerPack &srows, bool resetScroll) {
if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.isEmpty()) {
if (!isHidden()) {
hideStart();
hideAnimated();
}
_mrows.clear();
_hrows.clear();
@ -354,7 +355,7 @@ void FieldAutocomplete::rowsUpdated(const internal::MentionRows &mrows, const in
update();
if (hidden) {
hide();
showStart();
showAnimated();
}
}
}
@ -394,7 +395,7 @@ void FieldAutocomplete::recount(bool resetScroll) {
if (resetScroll) _inner->clearSel();
}
void FieldAutocomplete::fastHide() {
void FieldAutocomplete::hideFast() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
@ -403,18 +404,20 @@ void FieldAutocomplete::fastHide() {
hideFinish();
}
void FieldAutocomplete::hideStart() {
if (!_hiding) {
if (_cache.isNull()) {
_scroll->show();
_cache = myGrab(this);
}
_scroll->hide();
_hiding = true;
a_opacity.start(0);
setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
void FieldAutocomplete::hideAnimated() {
if (isHidden() || _hiding) {
return;
}
if (_cache.isNull()) {
_scroll->show();
_cache = myGrab(this);
}
_scroll->hide();
_hiding = true;
a_opacity.start(0);
setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
}
void FieldAutocomplete::hideFinish() {
@ -424,7 +427,7 @@ void FieldAutocomplete::hideFinish() {
_inner->clearSel(true);
}
void FieldAutocomplete::showStart() {
void FieldAutocomplete::showAnimated() {
if (!isHidden() && a_opacity.current() == 1 && !_hiding) {
return;
}
@ -441,7 +444,7 @@ void FieldAutocomplete::showStart() {
}
void FieldAutocomplete::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::dropdownDef.duration;
float64 dt = ms / st::defaultDropdownDuration;
if (dt >= 1) {
_a_appearance.stop();
a_opacity.finish();
@ -544,7 +547,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#');
int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize;
int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right();
int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width;
int32 htagleft = st::historyAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width;
if (!_srows->isEmpty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
@ -650,7 +653,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
}
}
p.setFont(st::mentionFont->f);
p.setFont(st::mentionFont);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);

View file

@ -39,8 +39,6 @@ class FieldAutocomplete final : public TWidget {
public:
FieldAutocomplete(QWidget *parent);
void fastHide();
bool clearFilteredBotCommands();
void showFiltered(PeerData *peer, QString query, bool addInlineBots);
void showStickers(EmojiPtr emoji);
@ -75,6 +73,8 @@ public:
return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
void hideFast();
~FieldAutocomplete();
signals:
@ -86,15 +86,15 @@ signals:
void moderateKeyActivate(int key, bool *outHandled) const;
public slots:
void hideStart();
void hideFinish();
void showStart();
void showAnimated();
void hideAnimated();
protected:
void paintEvent(QPaintEvent *e) override;
private:
void hideFinish();
void updateFiltered(bool resetScroll = false);
void recount(bool resetScroll = false);

View file

@ -20,6 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
using "basic.style";
using "dialogs/dialogs.style";
using "ui/widgets/widgets.style";
historyPaddingBottom: 10px;
@ -38,16 +39,16 @@ historyToDownBadgeSize: 22px;
historyEmptyDog: icon {{ "history_empty_dog", #ffffff }};
historyEmptySize: 128px;
membersInnerScroll: flatScroll(solidScroll) {
deltat: 3px;
deltab: 3px;
round: 1px;
width: 8px;
deltax: 3px;
}
membersInnerWidth: 310px;
membersInnerHeightMax: 360px;
membersInnerDropdown: InnerDropdown(defaultInnerDropdown) {
scroll: flatScroll(solidScroll) {
deltat: 3px;
deltab: 3px;
round: 1px;
width: 8px;
deltax: 3px;
}
scrollMargin: margins(0px, 5px, 0px, 5px);
scrollPadding: margins(0px, 3px, 8px, 3px);
}
@ -138,3 +139,74 @@ historyPeer8NameFg: #ce671b; // orange
historyPeer8UserpicBg: #f7b37c;
historyPeer8UserpicFg: #de8d62;
historyPeer8UserpicPerson: icon {{ size(120px, 120px), historyPeer8UserpicBg }, { "userpic_person", historyPeer8UserpicFg }};
historyMediaTypeFile: icon {{ "media_type_file", #b3b3b3, point(2px, 2px) }};
historyMediaTypePhoto: icon {{ "media_type_photo", #bebebe, point(2px, 2px) }};
historyMediaTypeVideo: icon {{ "media_type_video", #bebebe, point(2px, 2px) }};
historyMediaTypeSong: icon {{ "media_type_song", #bebebe, point(0px, 0px) }};
historyMediaTypeVoice: icon {{ "media_type_voice", #bebebe, point(2px, 2px) }};
historyMediaTypeLink: icon {{ "media_type_link", #bebebe, point(2px, 2px) }};
historyAttachDocument: IconButton {
width: 46px;
height: 46px;
opacity: 0.78;
overOpacity: 1.;
icon: historyMediaTypeFile;
iconPosition: point(9px, 9px);
downIconPosition: point(9px, 10px);
duration: 150;
}
historyAttachPhoto: IconButton(historyAttachDocument) {
icon: historyMediaTypePhoto;
}
historyAttachEmoji: IconButton(historyAttachDocument) {
width: 33px;
icon: icon {{ "send_control_emoji", #b9b9b9 }};
iconPosition: point(12px, 16px);
downIconPosition: point(12px, 16px);
}
historyEmojiCircle: size(19px, 19px);
historyEmojiCirclePeriod: 1500;
historyEmojiCircleDuration: 500;
historyEmojiCircleTop: 13px;
historyEmojiCircleLine: 2px;
historyEmojiCircleFg: #b9b9b9;
historyEmojiCirclePart: 3.5;
historyBotKeyboardShow: IconButton(historyAttachEmoji) {
icon: icon {{ "send_control_bot_keyboard", #b3b3b3 }};
iconPosition: point(6px, 12px);
downIconPosition: point(6px, 12px);
}
historyBotKeyboardHide: IconButton(historyAttachEmoji) {
icon: icon {{ "send_control_bot_keyboard_hide", #b3b3b3 }};
iconPosition: point(5px, 17px);
downIconPosition: point(5px, 17px);
}
historyBotCommandStart: IconButton(historyBotKeyboardShow) {
icon: icon {{ "send_control_bot_command", #b3b3b3 }};
}
historyRecordVoice: icon {{ "send_control_record", #b9b9b9 }};
historyRecordVoiceActive: icon {{ "send_control_record", #58b2ed }};
historyRecordSignalColor: #f17077;
historyRecordSignalMin: 5px;
historyRecordSignalMax: 12px;
historyRecordCancel: #aaa;
historyRecordCancelActive: #ec6466;
historyRecordFont: font(13px);
historyRecordTextTop: 14px;
historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) {
menu: Menu(defaultMenu) {
skip: 5px;
itemBgOver: btnWhiteHover;
itemIconPosition: point(12px, 6px);
itemIconOpacity: 0.78;
itemIconOverOpacity: 1.;
itemPadding: margins(48px, 11px, 48px, 11px);
}
}

View file

@ -0,0 +1,175 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "history/history_drag_area.h"
#include "styles/style_stickers.h"
#include "boxes/confirmbox.h"
#include "boxes/stickersetbox.h"
#include "inline_bots/inline_bot_result.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "dialogs/dialogs_layout.h"
#include "historywidget.h"
#include "localstorage.h"
#include "lang.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "mainwidget.h"
DragArea::DragArea(QWidget *parent) : TWidget(parent)
, _hiding(false)
, _in(false)
, a_opacity(0)
, a_color(st::dragColor->c)
, _a_appearance(animation(this, &DragArea::step_appearance))
, _shadow(st::boxShadow) {
setMouseTracking(true);
setAcceptDrops(true);
}
void DragArea::mouseMoveEvent(QMouseEvent *e) {
if (_hiding) return;
bool newIn = QRect(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()).contains(e->pos());
if (newIn != _in) {
_in = newIn;
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
}
void DragArea::dragMoveEvent(QDragMoveEvent *e) {
QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom());
bool newIn = r.contains(e->pos());
if (newIn != _in) {
_in = newIn;
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction);
e->accept();
}
void DragArea::setText(const QString &text, const QString &subtext) {
_text = text;
_subtext = subtext;
update();
}
void DragArea::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
}
QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom());
// draw shadow
_shadow.paint(p, r, st::boxShadowShift);
p.fillRect(r, st::white->b);
p.setPen(a_color.current());
p.setFont(st::dragFont->f);
p.drawText(QRect(0, (height() - st::dragHeight) / 2, width(), st::dragFont->height), _text, QTextOption(style::al_top));
p.setFont(st::dragSubfont->f);
p.drawText(QRect(0, (height() + st::dragHeight) / 2 - st::dragSubfont->height, width(), st::dragSubfont->height * 2), _subtext, QTextOption(style::al_top));
}
void DragArea::dragEnterEvent(QDragEnterEvent *e) {
static_cast<HistoryWidget*>(parentWidget())->dragEnterEvent(e);
e->setDropAction(Qt::IgnoreAction);
e->accept();
}
void DragArea::dragLeaveEvent(QDragLeaveEvent *e) {
static_cast<HistoryWidget*>(parentWidget())->dragLeaveEvent(e);
_in = false;
a_opacity.start(_hiding ? 0 : 1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
void DragArea::dropEvent(QDropEvent *e) {
static_cast<HistoryWidget*>(parentWidget())->dropEvent(e);
if (e->isAccepted()) {
emit dropped(e->mimeData());
}
}
void DragArea::otherEnter() {
showStart();
}
void DragArea::otherLeave() {
hideStart();
}
void DragArea::hideFast() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
hide();
}
void DragArea::hideStart() {
_hiding = true;
_in = false;
a_opacity.start(0);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
void DragArea::hideFinish() {
hide();
_in = false;
a_color = anim::cvalue(st::dragColor->c);
}
void DragArea::showStart() {
_hiding = false;
show();
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
_a_appearance.start();
}
void DragArea::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::defaultDropdownDuration;
if (dt >= 1) {
a_opacity.finish();
a_color.finish();
if (_hiding) {
hideFinish();
}
_a_appearance.stop();
} else {
a_opacity.update(dt, anim::linear);
a_color.update(dt, anim::linear);
}
if (timer) update();
}

View file

@ -23,78 +23,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/twidget.h"
#include "ui/effects/rect_shadow.h"
class Dropdown : public TWidget {
Q_OBJECT
public:
Dropdown(QWidget *parent, const style::dropdown &st = st::dropdownDef);
IconedButton *addButton(IconedButton *button);
void resetButtons();
void updateButtons();
void resizeEvent(QResizeEvent *e);
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void keyPressEvent(QKeyEvent *e);
void otherEnter();
void otherLeave();
void fastHide();
void ignoreShow(bool ignore = true);
void step_appearance(float64 ms, bool timer);
bool eventFilter(QObject *obj, QEvent *e);
bool overlaps(const QRect &globalRect) {
if (isHidden() || _a_appearance.animating()) return false;
return QRect(_st.padding.left(),
_st.padding.top(),
_width - _st.padding.left() - _st.padding.right(),
_height - _st.padding.top() - _st.padding.bottom()
).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
signals:
void hiding();
public slots:
void hideStart();
void hideFinish();
void showStart();
void onWndActiveChanged();
void buttonStateChanged(int oldState, ButtonStateChangeSource source);
private:
void adjustButtons();
bool _ignore = false;
typedef QVector<IconedButton*> Buttons;
Buttons _buttons;
int32 _selected = -1;
const style::dropdown &_st;
int32 _width, _height;
bool _hiding = false;
anim::fvalue a_opacity;
Animation _a_appearance;
QTimer _hideTimer;
Ui::RectShadow _shadow;
};
class DragArea : public TWidget {
Q_OBJECT
@ -106,8 +34,6 @@ public:
void otherEnter();
void otherLeave();
void fastHide();
void step_appearance(float64 ms, bool timer);
bool overlaps(const QRect &globalRect) {
@ -120,6 +46,8 @@ public:
).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
void hideFast();
protected:
void paintEvent(QPaintEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;

View file

@ -29,17 +29,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/filedialog.h"
#include "ui/toast/toast.h"
#include "ui/buttons/history_down_button.h"
#include "ui/inner_dropdown.h"
#include "ui/buttons/icon_button.h"
#include "ui/widgets/inner_dropdown.h"
#include "ui/widgets/dropdown_menu.h"
#include "inline_bots/inline_bot_result.h"
#include "data/data_drafts.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "history/history_drag_area.h"
#include "profile/profile_members_widget.h"
#include "core/click_handler_types.h"
#include "stickers/emoji_pan.h"
#include "lang.h"
#include "application.h"
#include "dropdown.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "passcodewidget.h"
@ -52,6 +54,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "window/chat_background.h"
#include "observer_peer.h"
#include "core/qthelp_regex.h"
#include "ui/widgets/popup_menu.h"
namespace {
@ -1154,7 +1157,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
isUponSelected = hasSelected;
}
_menu = new PopupMenu();
_menu = new Ui::PopupMenu();
_contextMenuLnk = ClickHandler::getActive();
HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
@ -1698,7 +1701,7 @@ void HistoryInner::toggleScrollDateShown() {
_scrollDateShown = !_scrollDateShown;
auto from = _scrollDateShown ? 0. : 1.;
auto to = _scrollDateShown ? 1. : 0.;
_scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::btnAttachEmoji.duration);
_scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::historyAttachPhoto.duration);
}
void HistoryInner::repaintScrollDateCallback() {
@ -1749,7 +1752,7 @@ void HistoryInner::leaveEvent(QEvent *e) {
App::hoveredItem(nullptr);
}
ClickHandler::clearActive();
PopupTooltip::Hide();
Ui::Tooltip::Hide();
if (!ClickHandler::getPressed() && _cursor != style::cur_default) {
_cursor = style::cur_default;
setCursor(_cursor);
@ -1970,7 +1973,7 @@ void HistoryInner::onUpdateSelected() {
dragState = item->getState(m.x(), m.y(), request);
lnkhost = item;
if (!dragState.link && m.x() >= st::msgMargin.left() && m.x() < st::msgMargin.left() + st::msgPhotoSize) {
if (HistoryMessage *msg = item->toHistoryMessage()) {
if (auto msg = item->toHistoryMessage()) {
if (msg->hasFromPhoto()) {
enumerateUserpics([&dragState, &lnkhost, &point](HistoryMessage *message, int userpicTop) -> bool {
// stop enumeration if the userpic is above our point
@ -1992,10 +1995,10 @@ void HistoryInner::onUpdateSelected() {
}
bool lnkChanged = ClickHandler::setActive(dragState.link, lnkhost);
if (lnkChanged || dragState.cursor != _dragCursorState) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
}
if (dragState.link || dragState.cursor == HistoryInDateCursorState || dragState.cursor == HistoryInForwardedCursorState) {
PopupTooltip::Show(1000, this);
Ui::Tooltip::Show(1000, this);
}
Qt::CursorShape cur = style::cur_default;
@ -2611,7 +2614,7 @@ void BotKeyboard::updateStyle(int newWidth) {
void BotKeyboard::clearSelection() {
if (_impl) {
if (ClickHandler::setActive(ClickHandlerPtr(), this)) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
setCursor(style::cur_default);
}
}
@ -2629,7 +2632,7 @@ QString BotKeyboard::tooltipText() const {
}
void BotKeyboard::updateSelected() {
PopupTooltip::Show(1000, this);
Ui::Tooltip::Show(1000, this);
if (!_impl) return;
@ -2638,7 +2641,7 @@ void BotKeyboard::updateSelected() {
auto link = _impl->getState(p.x() - x, p.y() - _st->margin);
if (ClickHandler::setActive(link, this)) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
setCursor(link ? style::cur_pointer : style::cur_default);
}
}
@ -2917,19 +2920,19 @@ SilentToggle::SilentToggle(QWidget *parent) : FlatCheckbox(parent, QString(), fa
void SilentToggle::mouseMoveEvent(QMouseEvent *e) {
FlatCheckbox::mouseMoveEvent(e);
if (rect().contains(e->pos())) {
PopupTooltip::Show(1000, this);
Ui::Tooltip::Show(1000, this);
} else {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
}
}
void SilentToggle::leaveEvent(QEvent *e) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
}
void SilentToggle::mouseReleaseEvent(QMouseEvent *e) {
FlatCheckbox::mouseReleaseEvent(e);
PopupTooltip::Show(0, this);
Ui::Tooltip::Show(0, this);
PeerData *p = App::main() ? App::main()->peer() : nullptr;
if (p && p->isChannel() && p->notify != UnknownNotifySettings) {
App::main()->updateNotifySetting(p, NotifySettingDontChange, checked() ? SilentNotifiesSetSilent : SilentNotifiesSetNotify);
@ -2991,20 +2994,20 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
, _botStart(this, lang(lng_bot_start), st::btnSend)
, _joinChannel(this, lang(lng_channel_join), st::btnSend)
, _muteUnmute(this, lang(lng_channel_mute), st::btnSend)
, _attachDocument(this, st::btnAttachDocument)
, _attachPhoto(this, st::btnAttachPhoto)
, _attachEmoji(this, st::btnAttachEmoji)
, _kbShow(this, st::btnBotKbShow)
, _kbHide(this, st::btnBotKbHide)
, _cmdStart(this, st::btnBotCmdStart)
, _attachDocument(this, st::historyAttachDocument)
, _attachPhoto(this, st::historyAttachPhoto)
, _attachEmoji(this, st::historyAttachEmoji)
, _botKeyboardShow(this, st::historyBotKeyboardShow)
, _botKeyboardHide(this, st::historyBotKeyboardHide)
, _botCommandStart(this, st::historyBotCommandStart)
, _silent(this)
, _field(this, st::taMsgField, lang(lng_message_ph))
, _a_record(animation(this, &HistoryWidget::step_record))
, _a_recording(animation(this, &HistoryWidget::step_recording))
, a_recordCancel(st::recordCancel->c, st::recordCancel->c)
, _recordCancelWidth(st::recordFont->width(lang(lng_record_cancel)))
, a_recordCancel(st::historyRecordCancel->c, st::historyRecordCancel->c)
, _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel)))
, _kbScroll(this, st::botKbScroll)
, _attachType(this)
, _attachType(this, st::historyAttachDropdownMenu)
, _emojiPan(this)
, _attachDragDocument(this)
, _attachDragPhoto(this)
@ -3028,8 +3031,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
connect(&_joinChannel, SIGNAL(clicked()), this, SLOT(onJoinChannel()));
connect(&_muteUnmute, SIGNAL(clicked()), this, SLOT(onMuteUnmute()));
connect(&_silent, SIGNAL(clicked()), this, SLOT(onBroadcastSilentChange()));
connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
connect(_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
connect(_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool)));
connect(&_field, SIGNAL(cancelled()), this, SLOT(onCancel()));
connect(&_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed()));
@ -3107,24 +3110,24 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
_reportSpamPanel.move(0, 0);
_reportSpamPanel.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachEmoji.hide();
_kbShow.hide();
_kbHide.hide();
_attachDocument->hide();
_attachPhoto->hide();
_attachEmoji->hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
_silent.hide();
_cmdStart.hide();
_botCommandStart->hide();
_attachDocument.installEventFilter(_attachType);
_attachPhoto.installEventFilter(_attachType);
_attachEmoji.installEventFilter(_emojiPan);
_attachDocument->installEventFilter(_attachType);
_attachPhoto->installEventFilter(_attachType);
_attachEmoji->installEventFilter(_emojiPan);
connect(&_kbShow, SIGNAL(clicked()), this, SLOT(onKbToggle()));
connect(&_kbHide, SIGNAL(clicked()), this, SLOT(onKbToggle()));
connect(&_cmdStart, SIGNAL(clicked()), this, SLOT(onCmdStart()));
connect(_botKeyboardShow, SIGNAL(clicked()), this, SLOT(onKbToggle()));
connect(_botKeyboardHide, SIGNAL(clicked()), this, SLOT(onKbToggle()));
connect(_botCommandStart, SIGNAL(clicked()), this, SLOT(onCmdStart()));
connect(_attachType->addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
connect(_attachType->addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
_attachType->addAction(lang(lng_attach_file), this, SLOT(onDocumentSelect()), &st::historyMediaTypeFile);
_attachType->addAction(lang(lng_attach_photo), this, SLOT(onPhotoSelect()), &st::historyMediaTypePhoto);
_attachType->hide();
_emojiPan->hide();
_attachDragDocument->hide();
@ -3221,7 +3224,7 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
_emojiPan->queryInlineBot(_inlineBot, _peer, query);
}
if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->hideStart();
_fieldAutocomplete->hideAnimated();
}
} else {
clearInlineBot();
@ -3267,7 +3270,7 @@ void HistoryWidget::onTextChange() {
_a_record.stop();
_inRecord = _inField = false;
a_recordOver = a_recordDown = anim::fvalue(0, 0);
a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c);
a_recordCancel = anim::cvalue(st::historyRecordCancel->c, st::historyRecordCancel->c);
}
}
if (updateCmdStartShown()) {
@ -3541,7 +3544,7 @@ void HistoryWidget::notify_botCommandsChanged(UserData *user) {
}
void HistoryWidget::notify_inlineBotRequesting(bool requesting) {
_attachEmoji.setLoading(requesting);
_attachEmoji->setLoading(requesting);
}
void HistoryWidget::notify_replyMarkupUpdated(const HistoryItem *item) {
@ -4497,14 +4500,14 @@ void HistoryWidget::updateControlsVisibility() {
_fieldAutocomplete->hide();
_field.hide();
_fieldBarCancel.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachEmoji.hide();
_attachDocument->hide();
_attachPhoto->hide();
_attachEmoji->hide();
_silent.hide();
_historyToEnd->hide();
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
_botCommandStart->hide();
_attachType->hide();
_emojiPan->hide();
if (_pinnedBar) {
@ -4556,17 +4559,17 @@ void HistoryWidget::updateControlsVisibility() {
_send.hide();
if (_inlineBotCancel) _inlineBotCancel->hide();
_botStart.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachDocument->hide();
_attachPhoto->hide();
_silent.hide();
_kbScroll.hide();
_fieldBarCancel.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachEmoji.hide();
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachDocument->hide();
_attachPhoto->hide();
_attachEmoji->hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
_botCommandStart->hide();
_attachType->hide();
_emojiPan->hide();
if (!_field.isHidden()) {
@ -4588,12 +4591,12 @@ void HistoryWidget::updateControlsVisibility() {
_send.hide();
if (_inlineBotCancel) _inlineBotCancel->hide();
_field.hide();
_attachEmoji.hide();
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachEmoji->hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
_botCommandStart->hide();
_attachDocument->hide();
_attachPhoto->hide();
_silent.hide();
_kbScroll.hide();
_fieldBarCancel.hide();
@ -4618,12 +4621,12 @@ void HistoryWidget::updateControlsVisibility() {
}
if (_recording) {
_field.hide();
_attachEmoji.hide();
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachEmoji->hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
_botCommandStart->hide();
_attachDocument->hide();
_attachPhoto->hide();
_silent.hide();
if (_kbShown) {
_kbScroll.show();
@ -4634,38 +4637,38 @@ void HistoryWidget::updateControlsVisibility() {
_field.show();
if (_kbShown) {
_kbScroll.show();
_attachEmoji.hide();
_kbHide.show();
_kbShow.hide();
_cmdStart.hide();
_attachEmoji->hide();
_botKeyboardHide->show();
_botKeyboardShow->hide();
_botCommandStart->hide();
} else if (_kbReplyTo) {
_kbScroll.hide();
_attachEmoji.show();
_kbHide.hide();
_kbShow.hide();
_cmdStart.hide();
_attachEmoji->show();
_botKeyboardHide->hide();
_botKeyboardShow->hide();
_botCommandStart->hide();
} else {
_kbScroll.hide();
_attachEmoji.show();
_kbHide.hide();
_attachEmoji->show();
_botKeyboardHide->hide();
if (_keyboard.hasMarkup()) {
_kbShow.show();
_cmdStart.hide();
_botKeyboardShow->show();
_botCommandStart->hide();
} else {
_kbShow.hide();
_botKeyboardShow->hide();
if (_cmdStartShown) {
_cmdStart.show();
_botCommandStart->show();
} else {
_cmdStart.hide();
_botCommandStart->hide();
}
}
}
if (cDefaultAttach() == dbidaPhoto) {
_attachDocument.hide();
_attachPhoto.show();
_attachDocument->hide();
_attachPhoto->show();
} else {
_attachDocument.show();
_attachPhoto.hide();
_attachDocument->show();
_attachPhoto->hide();
}
if (hasSilentToggle()) {
_silent.show();
@ -4692,17 +4695,17 @@ void HistoryWidget::updateControlsVisibility() {
_botStart.hide();
_joinChannel.hide();
_muteUnmute.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachDocument->hide();
_attachPhoto->hide();
_silent.hide();
_kbScroll.hide();
_fieldBarCancel.hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachEmoji.hide();
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachDocument->hide();
_attachPhoto->hide();
_attachEmoji->hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
_botCommandStart->hide();
_attachType->hide();
_emojiPan->hide();
_kbScroll.hide();
@ -5219,6 +5222,12 @@ bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtp
return true;
}
void HistoryWidget::hideSelectorControlsAnimated() {
_fieldAutocomplete->hideAnimated();
_attachType->hideAnimated();
_emojiPan->hideAnimated();
}
void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
if (!_history) return;
@ -5244,9 +5253,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
_saveDraftStart = getms();
onDraftSave();
if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart();
if (!_attachType->isHidden()) _attachType->hideStart();
if (!_emojiPan->isHidden()) _emojiPan->hideStart();
hideSelectorControlsAnimated();
if (replyTo < 0) cancelReply(lastKeyboardUsed);
if (_previewData && _previewData->pendingTill) previewCancel();
@ -5448,14 +5455,14 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window:
_kbScroll.hide();
_reportSpamPanel.hide();
_historyToEnd->hide();
_attachDocument.hide();
_attachPhoto.hide();
_attachEmoji.hide();
_attachDocument->hide();
_attachPhoto->hide();
_attachEmoji->hide();
_fieldAutocomplete->hide();
_silent.hide();
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_botKeyboardShow->hide();
_botKeyboardHide->hide();
_botCommandStart->hide();
_field.hide();
_fieldBarCancel.hide();
_send.hide();
@ -5565,16 +5572,16 @@ void HistoryWidget::step_recording(float64 ms, bool timer) {
} else {
a_recordingLevel.update(dt, anim::linear);
}
if (timer) update(_attachDocument.geometry());
if (timer) update(_attachDocument->geometry());
}
void HistoryWidget::onPhotoSelect() {
if (!_history) return;
_attachDocument.clearState();
_attachDocument.hide();
_attachPhoto.show();
_attachType->fastHide();
_attachDocument->clearState();
_attachDocument->hide();
_attachPhoto->show();
_attachType->hideFast();
if (cDefaultAttach() != dbidaPhoto) {
cSetDefaultAttach(dbidaPhoto);
@ -5599,10 +5606,10 @@ void HistoryWidget::onPhotoSelect() {
void HistoryWidget::onDocumentSelect() {
if (!_history) return;
_attachPhoto.clearState();
_attachPhoto.hide();
_attachDocument.show();
_attachType->fastHide();
_attachPhoto->clearState();
_attachPhoto->hide();
_attachDocument->show();
_attachType->hideFast();
if (cDefaultAttach() != dbidaDocument) {
cSetDefaultAttach(dbidaDocument);
@ -5671,7 +5678,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) {
_inField = inField;
a_recordOver.restart();
a_recordDown.start(_inField ? 1 : 0);
a_recordCancel.start(_inField ? st::recordCancel->c : st::recordCancelActive->c);
a_recordCancel.start(_inField ? st::historyRecordCancel->c : st::historyRecordCancelActive->c);
startAnim = true;
}
if (inReplyEdit != _inReplyEdit) {
@ -5722,7 +5729,7 @@ void HistoryWidget::stopRecording(bool send) {
a_recordDown.start(0);
a_recordOver.restart();
a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c);
a_recordCancel = anim::cvalue(st::historyRecordCancel->c, st::historyRecordCancel->c);
_a_record.start();
}
@ -5960,7 +5967,7 @@ void HistoryWidget::updateDragAreas() {
case DragStateFiles:
_attachDragDocument->otherEnter();
_attachDragDocument->setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files));
_attachDragPhoto->fastHide();
_attachDragPhoto->hideFast();
break;
case DragStatePhotoFiles:
_attachDragDocument->otherEnter();
@ -5969,7 +5976,7 @@ void HistoryWidget::updateDragAreas() {
_attachDragPhoto->setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick));
break;
case DragStateImage:
_attachDragDocument->fastHide();
_attachDragDocument->hideFast();
_attachDragPhoto->otherEnter();
_attachDragPhoto->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick));
break;
@ -6127,10 +6134,10 @@ void HistoryWidget::onFilesDrop(const QMimeData *data) {
void HistoryWidget::onKbToggle(bool manual) {
auto fieldEnabled = canWriteMessage();
if (_kbShown || _kbReplyTo) {
_kbHide.hide();
_botKeyboardHide->hide();
if (_kbShown) {
if (fieldEnabled) {
_kbShow.show();
_botKeyboardShow->show();
}
if (manual && _history) {
_history->lastKeyboardHiddenId = _keyboard.forMsgId().msg;
@ -6154,10 +6161,10 @@ void HistoryWidget::onKbToggle(bool manual) {
}
}
} else if (!_keyboard.hasMarkup() && _keyboard.forceReply()) {
_kbHide.hide();
_kbShow.hide();
_botKeyboardHide->hide();
_botKeyboardShow->hide();
if (fieldEnabled) {
_cmdStart.show();
_botCommandStart->show();
}
_kbScroll.hide();
_kbShown = false;
@ -6175,8 +6182,8 @@ void HistoryWidget::onKbToggle(bool manual) {
_history->lastKeyboardHiddenId = 0;
}
} else if (fieldEnabled) {
_kbHide.show();
_kbShow.hide();
_botKeyboardHide->show();
_botKeyboardShow->hide();
_kbScroll.show();
_kbShown = true;
@ -6195,10 +6202,10 @@ void HistoryWidget::onKbToggle(bool manual) {
}
}
resizeEvent(0);
if (_kbHide.isHidden() && canWriteMessage()) {
_attachEmoji.show();
if (_botKeyboardHide->isHidden() && canWriteMessage()) {
_attachEmoji->show();
} else {
_attachEmoji.hide();
_attachEmoji->hide();
}
updateField();
}
@ -6310,13 +6317,13 @@ void HistoryWidget::setMembersShowAreaActive(bool active) {
void HistoryWidget::onMembersDropdownShow() {
if (!_membersDropdown) {
_membersDropdown.create(this, st::membersInnerDropdown, st::membersInnerScroll);
_membersDropdown.create(this, st::membersInnerDropdown);
_membersDropdown->setOwnedWidget(new Profile::MembersWidget(_membersDropdown, _peer, Profile::MembersWidget::TitleVisibility::Hidden));
_membersDropdown->resize(st::membersInnerWidth, _membersDropdown->height());
_membersDropdown->resizeToWidth(st::membersInnerWidth);
_membersDropdown->setMaxHeight(countMembersDropdownHeightMax());
_membersDropdown->moveToLeft(0, 0);
connect(_membersDropdown, SIGNAL(hidden()), this, SLOT(onMembersDropdownHidden()));
connect(_membersDropdown, SIGNAL(beforeHidden()), this, SLOT(onMembersDropdownHidden()));
}
_membersDropdown->otherEnter();
}
@ -6436,24 +6443,24 @@ void HistoryWidget::moveFieldControls() {
// (_attachDocument|_attachPhoto) _field (_silent|_cmdStart|_kbShow) (_kbHide|_attachEmoji) [_broadcast] _send
// (_botStart|_unblock|_joinChannel|_muteUnmute)
int buttonsBottom = bottom - _attachDocument.height();
_attachDocument.move(0, buttonsBottom);
_attachPhoto.move(0, buttonsBottom);
_field.move(_attachDocument.width(), bottom - _field.height() - st::sendPadding);
int buttonsBottom = bottom - _attachDocument->height();
_attachDocument->move(0, buttonsBottom);
_attachPhoto->move(0, buttonsBottom);
_field.move(_attachDocument->width(), bottom - _field.height() - st::sendPadding);
_send.move(right - _send.width(), buttonsBottom);
if (_inlineBotCancel) _inlineBotCancel->move(_send.pos());
right -= _send.width();
_attachEmoji.move(right - _attachEmoji.width(), buttonsBottom);
_kbHide.move(right - _kbHide.width(), buttonsBottom);
right -= _attachEmoji.width();
_kbShow.move(right - _kbShow.width(), buttonsBottom);
_cmdStart.move(right - _cmdStart.width(), buttonsBottom);
_attachEmoji->move(right - _attachEmoji->width(), buttonsBottom);
_botKeyboardHide->move(right - _botKeyboardHide->width(), buttonsBottom);
right -= _attachEmoji->width();
_botKeyboardShow->move(right - _botKeyboardShow->width(), buttonsBottom);
_botCommandStart->move(right - _botCommandStart->width(), buttonsBottom);
_silent.move(right - _silent.width(), buttonsBottom);
right = w;
_fieldBarCancel.move(right - _fieldBarCancel.width(), _field.y() - st::sendPadding - _fieldBarCancel.height());
_attachType->move(0, _attachDocument.y() - _attachType->height());
_emojiPan->moveBottom(_attachEmoji.y());
_attachType->move(0, _attachDocument->y() - _attachType->height());
_emojiPan->moveBottom(_attachEmoji->y());
_botStart.setGeometry(0, bottom - _botStart.height(), w, _botStart.height());
_unblock.setGeometry(0, bottom - _unblock.height(), w, _unblock.height());
@ -6463,11 +6470,11 @@ void HistoryWidget::moveFieldControls() {
void HistoryWidget::updateFieldSize() {
bool kbShowShown = _history && !_kbShown && _keyboard.hasMarkup();
int fieldWidth = width() - _attachDocument.width();
int fieldWidth = width() - _attachDocument->width();
fieldWidth -= _send.width();
fieldWidth -= _attachEmoji.width();
if (kbShowShown) fieldWidth -= _kbShow.width();
if (_cmdStartShown) fieldWidth -= _cmdStart.width();
fieldWidth -= _attachEmoji->width();
if (kbShowShown) fieldWidth -= _botKeyboardShow->width();
if (_cmdStartShown) fieldWidth -= _botCommandStart->width();
if (hasSilentToggle()) fieldWidth -= _silent.width();
if (_field.width() != fieldWidth) {
@ -6493,7 +6500,7 @@ void HistoryWidget::inlineBotChanged() {
_inlineBotCancel = std_::make_unique<IconedButton>(this, st::inlineBotCancel);
connect(_inlineBotCancel.get(), SIGNAL(clicked()), this, SLOT(onInlineBotCancel()));
_inlineBotCancel->setGeometry(_send.geometry());
_attachEmoji.raise();
_attachEmoji->raise();
updateFieldSubmitSettings();
updateControlsVisibility();
} else if (!isInlineBot && _inlineBotCancel) {
@ -7029,7 +7036,7 @@ void HistoryWidget::updateControlsGeometry() {
_historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll.y() + _scroll.height() - _historyToEnd->height() - st::historyToDownPosition.y());
_emojiPan->setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height());
_emojiPan->setMaxHeight(height() - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom() - _attachEmoji->height());
if (_membersDropdown) {
_membersDropdown->setMaxHeight(countMembersDropdownHeightMax());
}
@ -7315,15 +7322,15 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
if (!_a_show.animating()) {
if (hasMarkup) {
_kbScroll.show();
_attachEmoji.hide();
_kbHide.show();
_attachEmoji->hide();
_botKeyboardHide->show();
} else {
_kbScroll.hide();
_attachEmoji.show();
_kbHide.hide();
_attachEmoji->show();
_botKeyboardHide->hide();
}
_kbShow.hide();
_cmdStart.hide();
_botKeyboardShow->hide();
_botCommandStart->hide();
}
int32 maxh = hasMarkup ? qMin(_keyboard.height(), int(st::maxFieldHeight) - (int(st::maxFieldHeight) / 2)) : 0;
_field.setMaxHeight(st::maxFieldHeight - maxh);
@ -7338,10 +7345,10 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
} else {
if (!_a_show.animating()) {
_kbScroll.hide();
_attachEmoji.show();
_kbHide.hide();
_kbShow.show();
_cmdStart.hide();
_attachEmoji->show();
_botKeyboardHide->hide();
_botKeyboardShow->show();
_botCommandStart->hide();
}
_field.setMaxHeight(st::maxFieldHeight);
_kbShown = false;
@ -7354,10 +7361,10 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
} else {
if (!_scroll.isHidden()) {
_kbScroll.hide();
_attachEmoji.show();
_kbHide.hide();
_kbShow.hide();
_cmdStart.show();
_attachEmoji->show();
_botKeyboardHide->hide();
_botKeyboardShow->hide();
_botCommandStart->show();
}
_field.setMaxHeight(st::maxFieldHeight);
_kbShown = false;
@ -7543,9 +7550,7 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot
Local::writeRecentHashtagsAndBots();
}
if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart();
if (!_attachType->isHidden()) _attachType->hideStart();
if (!_emojiPan->isHidden()) _emojiPan->hideStart();
hideSelectorControlsAnimated();
_field.setFocus();
}
@ -7711,9 +7716,7 @@ bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti
onCloudDraftSave(); // won't be needed if SendInlineBotResult will clear the cloud draft
}
if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart();
if (!_attachType->isHidden()) _attachType->hideStart();
if (!_emojiPan->isHidden()) _emojiPan->hideStart();
hideSelectorControlsAnimated();
_field.setFocus();
return true;
@ -7758,9 +7761,7 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption)
App::historyRegRandom(randomId, newId);
if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart();
if (!_attachType->isHidden()) _attachType->hideStart();
if (!_emojiPan->isHidden()) _emojiPan->hideStart();
hideSelectorControlsAnimated();
_field.setFocus();
}
@ -8001,7 +8002,7 @@ void HistoryWidget::cancelReplyAfterMediaSend(bool lastKeyboardUsed) {
int HistoryWidget::countMembersDropdownHeightMax() const {
int result = height() - st::membersInnerDropdown.padding.top() - st::membersInnerDropdown.padding.bottom();
result -= _attachEmoji.height();
result -= _attachEmoji->height();
accumulate_min(result, st::membersInnerHeightMax);
return result;
}
@ -8221,7 +8222,7 @@ void HistoryWidget::onCancel() {
onFieldBarCancel();
}
} else if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->hideStart();
_fieldAutocomplete->hideAnimated();
} else {
App::main()->showBackFromStack();
emit cancelled();
@ -8637,36 +8638,36 @@ void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int
void HistoryWidget::drawRecordButton(Painter &p) {
if (a_recordDown.current() < 1) {
p.setOpacity(st::btnAttachEmoji.opacity * (1 - a_recordOver.current()) + st::btnAttachEmoji.overOpacity * a_recordOver.current());
p.drawSprite(_send.x() + (_send.width() - st::btnRecordAudio.pxWidth()) / 2, _send.y() + (_send.height() - st::btnRecordAudio.pxHeight()) / 2, st::btnRecordAudio);
p.setOpacity(st::historyAttachEmoji.opacity * (1 - a_recordOver.current()) + st::historyAttachEmoji.overOpacity * a_recordOver.current());
st::historyRecordVoice.paint(p, _send.x() + (_send.width() - st::historyRecordVoice.width()) / 2, _send.y() + (_send.height() - st::historyRecordVoice.height()) / 2, width());
}
if (a_recordDown.current() > 0) {
p.setOpacity(a_recordDown.current());
p.drawSprite(_send.x() + (_send.width() - st::btnRecordAudioActive.pxWidth()) / 2, _send.y() + (_send.height() - st::btnRecordAudioActive.pxHeight()) / 2, st::btnRecordAudioActive);
st::historyRecordVoiceActive.paint(p, _send.x() + (_send.width() - st::historyRecordVoiceActive.width()) / 2, _send.y() + (_send.height() - st::historyRecordVoiceActive.height()) / 2, width());
}
p.setOpacity(1);
}
void HistoryWidget::drawRecording(Painter &p) {
p.setPen(Qt::NoPen);
p.setBrush(st::recordSignalColor->b);
p.setBrush(st::historyRecordSignalColor);
p.setRenderHint(QPainter::HighQualityAntialiasing);
float64 delta = qMin(float64(a_recordingLevel.current()) / 0x4000, 1.);
int32 d = 2 * qRound(st::recordSignalMin + (delta * (st::recordSignalMax - st::recordSignalMin)));
p.drawEllipse(_attachPhoto.x() + (_attachEmoji.width() - d) / 2, _attachPhoto.y() + (_attachPhoto.height() - d) / 2, d, d);
int32 d = 2 * qRound(st::historyRecordSignalMin + (delta * (st::historyRecordSignalMax - st::historyRecordSignalMin)));
p.drawEllipse(_attachPhoto->x() + (_attachEmoji->width() - d) / 2, _attachPhoto->y() + (_attachPhoto->height() - d) / 2, d, d);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
QString duration = formatDurationText(_recordingSamples / AudioVoiceMsgFrequency);
p.setFont(st::recordFont->f);
p.setFont(st::historyRecordFont);
p.setPen(st::black->p);
p.drawText(_attachPhoto.x() + _attachEmoji.width(), _attachPhoto.y() + st::recordTextTop + st::recordFont->ascent, duration);
p.setPen(st::black);
p.drawText(_attachPhoto->x() + _attachEmoji->width(), _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration);
int32 left = _attachPhoto.x() + _attachEmoji.width() + st::recordFont->width(duration) + ((_send.width() - st::btnRecordAudio.pxWidth()) / 2);
int32 left = _attachPhoto->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send.width() - st::historyRecordVoice.width()) / 2);
int32 right = width() - _send.width();
p.setPen(a_recordCancel.current());
p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto.y() + st::recordTextTop + st::recordFont->ascent, lang(lng_record_cancel));
p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, lang(lng_record_cancel));
}
void HistoryWidget::drawPinnedBar(Painter &p) {

View file

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localimageloader.h"
#include "ui/effects/rect_shadow.h"
#include "ui/popupmenu.h"
#include "ui/widgets/tooltip.h"
#include "history/history_common.h"
#include "history/field_autocomplete.h"
#include "window/section_widget.h"
@ -36,17 +36,20 @@ class Result;
} // namespace InlineBots
namespace Ui {
class HistoryDownButton;
class InnerDropdown;
class DropdownMenu;
class PlainShadow;
class PopupMenu;
class IconButton;
class HistoryDownButton;
class EmojiButton;
} // namespace Ui
class Dropdown;
class DragArea;
class EmojiPan;
class HistoryWidget;
class HistoryInner : public TWidget, public AbstractTooltipShower, private base::Subscriber {
class HistoryInner : public TWidget, public Ui::AbstractTooltipShower, private base::Subscriber {
Q_OBJECT
public:
@ -256,7 +259,7 @@ private:
QTimer _touchScrollTimer;
// context menu
PopupMenu *_menu = nullptr;
Ui::PopupMenu *_menu = nullptr;
// save visible area coords for painting / pressing userpics
int _visibleAreaTop = 0;
@ -351,7 +354,7 @@ private:
};
class BotKeyboard : public TWidget, public AbstractTooltipShower, public ClickHandlerHost {
class BotKeyboard : public TWidget, public Ui::AbstractTooltipShower, public ClickHandlerHost {
Q_OBJECT
public:
@ -505,7 +508,7 @@ private:
};
class SilentToggle : public FlatCheckbox, public AbstractTooltipShower {
class SilentToggle : public FlatCheckbox, public Ui::AbstractTooltipShower {
public:
SilentToggle(QWidget *parent);
@ -868,6 +871,7 @@ private:
void cancelReplyAfterMediaSend(bool lastKeyboardUsed);
void hideSelectorControlsAnimated();
int countMembersDropdownHeightMax() const;
MsgId _replyToId = 0;
@ -1086,9 +1090,12 @@ private:
FlatButton _send, _unblock, _botStart, _joinChannel, _muteUnmute;
mtpRequestId _unblockRequest = 0;
mtpRequestId _reportSpamRequest = 0;
IconedButton _attachDocument, _attachPhoto;
EmojiButton _attachEmoji;
IconedButton _kbShow, _kbHide, _cmdStart;
ChildWidget<Ui::IconButton> _attachDocument;
ChildWidget<Ui::IconButton> _attachPhoto;
ChildWidget<Ui::EmojiButton> _attachEmoji;
ChildWidget<Ui::IconButton> _botKeyboardShow;
ChildWidget<Ui::IconButton> _botKeyboardHide;
ChildWidget<Ui::IconButton> _botCommandStart;
SilentToggle _silent;
bool _cmdStartShown = false;
MessageField _field;
@ -1115,7 +1122,7 @@ private:
ChildWidget<Ui::InnerDropdown> _membersDropdown = { nullptr };
QTimer _membersDropdownShowTimer;
ChildWidget<Dropdown> _attachType;
ChildWidget<Ui::DropdownMenu> _attachType;
ChildWidget<EmojiPan> _emojiPan;
DragState _attachDrag = DragStateNone;
ChildWidget<DragArea> _attachDragDocument, _attachDragPhoto;

View file

@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "styles/style_dialogs.h"
#include "styles/style_history.h"
#include "ui/buttons/peer_avatar_button.h"
#include "ui/buttons/round_button.h"
#include "ui/widgets/shadow.h"
@ -29,7 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "window/section_widget.h"
#include "window/top_bar_widget.h"
#include "data/data_drafts.h"
#include "dropdown.h"
#include "ui/widgets/dropdown_menu.h"
#include "observer_peer.h"
#include "apiwrap.h"
#include "dialogswidget.h"
@ -75,7 +76,7 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window)
, _topBar(this)
, _playerPlaylist(this, Media::Player::Panel::Layout::OnlyPlaylist)
, _playerPanel(this, Media::Player::Panel::Layout::Full)
, _mediaType(this)
, _mediaType(this, st::historyAttachDropdownMenu)
, _api(new ApiWrap(this)) {
setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight));
@ -1382,16 +1383,16 @@ void MainWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
}
}
if (mask != _mediaTypeMask) {
_mediaType->resetButtons();
_mediaType->clearActions();
for (int32 i = 0; i < OverviewCount; ++i) {
if (mask & (1 << i)) {
switch (i) {
case OverviewPhotos: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaPhotos, lang(lng_media_type_photos))), SIGNAL(clicked()), this, SLOT(onPhotosSelect())); break;
case OverviewVideos: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaVideos, lang(lng_media_type_videos))), SIGNAL(clicked()), this, SLOT(onVideosSelect())); break;
case OverviewMusicFiles: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaSongs, lang(lng_media_type_songs))), SIGNAL(clicked()), this, SLOT(onSongsSelect())); break;
case OverviewFiles: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaDocuments, lang(lng_media_type_files))), SIGNAL(clicked()), this, SLOT(onDocumentsSelect())); break;
case OverviewVoiceFiles: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaAudios, lang(lng_media_type_audios))), SIGNAL(clicked()), this, SLOT(onAudiosSelect())); break;
case OverviewLinks: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaLinks, lang(lng_media_type_links))), SIGNAL(clicked()), this, SLOT(onLinksSelect())); break;
case OverviewPhotos: _mediaType->addAction(lang(lng_media_type_photos), this, SLOT(onPhotosSelect()), &st::historyMediaTypePhoto); break;
case OverviewVideos: _mediaType->addAction(lang(lng_media_type_videos), this, SLOT(onVideosSelect()), &st::historyMediaTypeVideo); break;
case OverviewMusicFiles: _mediaType->addAction(lang(lng_media_type_songs), this, SLOT(onSongsSelect()), &st::historyMediaTypeSong); break;
case OverviewFiles: _mediaType->addAction(lang(lng_media_type_files), this, SLOT(onDocumentsSelect()), &st::historyMediaTypeFile); break;
case OverviewVoiceFiles: _mediaType->addAction(lang(lng_media_type_audios), this, SLOT(onAudiosSelect()), &st::historyMediaTypeVoice); break;
case OverviewLinks: _mediaType->addAction(lang(lng_media_type_links), this, SLOT(onLinksSelect()), &st::historyMediaTypeLink); break;
}
}
}
@ -2912,32 +2913,32 @@ void MainWidget::setMembersShowAreaActive(bool active) {
void MainWidget::onPhotosSelect() {
if (_overview) _overview->switchType(OverviewPhotos);
_mediaType->hideStart();
_mediaType->hideAnimated();
}
void MainWidget::onVideosSelect() {
if (_overview) _overview->switchType(OverviewVideos);
_mediaType->hideStart();
_mediaType->hideAnimated();
}
void MainWidget::onSongsSelect() {
if (_overview) _overview->switchType(OverviewMusicFiles);
_mediaType->hideStart();
_mediaType->hideAnimated();
}
void MainWidget::onDocumentsSelect() {
if (_overview) _overview->switchType(OverviewFiles);
_mediaType->hideStart();
_mediaType->hideAnimated();
}
void MainWidget::onAudiosSelect() {
if (_overview) _overview->switchType(OverviewVoiceFiles);
_mediaType->hideStart();
_mediaType->hideAnimated();
}
void MainWidget::onLinksSelect() {
if (_overview) _overview->switchType(OverviewLinks);
_mediaType->hideStart();
_mediaType->hideAnimated();
}
Window::TopBarWidget *MainWidget::topBar() {

View file

@ -39,6 +39,7 @@ class Panel;
namespace Ui {
class PeerAvatarButton;
class PlainShadow;
class DropdownMenu;
} // namespace Ui
namespace Window {
@ -56,7 +57,6 @@ class DialogsWidget;
class HistoryWidget;
class OverviewWidget;
class HistoryHider;
class Dropdown;
enum StackItemType {
HistoryStackItem,
@ -609,7 +609,7 @@ private:
int _playerHeight = 0;
int _contentScrollAddToY = 0;
ChildWidget<Dropdown> _mediaType;
ChildWidget<Ui::DropdownMenu> _mediaType;
int32 _mediaTypeMask = 0;
int32 updDate = 0;

View file

@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "dialogs/dialogs_layout.h"
#include "styles/style_dialogs.h"
#include "ui/popupmenu.h"
#include "ui/widgets/popup_menu.h"
#include "zip.h"
#include "lang.h"
#include "shortcuts.h"
@ -185,7 +185,7 @@ void MainWindow::onWindowActiveChanged() {
void MainWindow::firstShow() {
#ifdef Q_OS_WIN
trayIconMenu = new PopupMenu();
trayIconMenu = new Ui::PopupMenu();
trayIconMenu->deleteOnHide(false);
#else // Q_OS_WIN
trayIconMenu = new QMenu(this);

View file

@ -29,7 +29,7 @@ class Document;
namespace Media {
namespace Player {
class ListWidget : public ScrolledWidget, private base::Subscriber {
class ListWidget : public TWidget, private base::Subscriber {
public:
ListWidget();

View file

@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/player/media_player_list.h"
#include "media/player/media_player_instance.h"
#include "styles/style_overview.h"
#include "styles/style_widgets.h"
#include "styles/style_media_player.h"
#include "ui/widgets/shadow.h"
#include "mainwindow.h"
@ -34,7 +35,7 @@ namespace Player {
Panel::Panel(QWidget *parent, Layout layout) : TWidget(parent)
, _layout(layout)
, _shadow(st::defaultInnerDropdown.shadow)
, _shadow(st::defaultDropdownShadow)
, _scroll(this, st::mediaPlayerScroll) {
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart()));
@ -90,7 +91,7 @@ void Panel::updateControlsGeometry() {
if (scrollHeight > 0) {
_scroll->setGeometryToRight(contentRight(), scrollTop, width, scrollHeight);
}
if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
if (auto widget = static_cast<TWidget*>(_scroll->widget())) {
widget->resizeToWidth(width);
onScroll();
}
@ -118,7 +119,7 @@ void Panel::scrollPlaylistToCurrentTrack() {
}
void Panel::onScroll() {
if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
if (auto widget = static_cast<TWidget*>(_scroll->widget())) {
int visibleTop = _scroll->scrollTop();
int visibleBottom = visibleTop + _scroll->height();
widget->setVisibleTopBottom(visibleTop, visibleBottom);
@ -153,7 +154,7 @@ void Panel::paintEvent(QPaintEvent *e) {
if (animating) {
p.setOpacity(_a_appearance.current(_hiding ? 0. : 1.));
} else if (_hiding || isHidden()) {
hidingFinished();
hideFinished();
return;
}
p.drawPixmap(0, 0, _cache);
@ -290,7 +291,7 @@ void Panel::onShowStart() {
void Panel::hideIgnoringEnterEvents() {
_ignoringEnterEvents = true;
if (isHidden()) {
hidingFinished();
hideFinished();
} else {
onHideStart();
}
@ -317,13 +318,13 @@ void Panel::startAnimation() {
void Panel::appearanceCallback() {
if (!_a_appearance.animating() && _hiding) {
_hiding = false;
hidingFinished();
hideFinished();
} else {
update();
}
}
void Panel::hidingFinished() {
void Panel::hideFinished() {
hide();
_cache = QPixmap();
performDestroy();

View file

@ -81,7 +81,7 @@ private:
void updateSize();
void appearanceCallback();
void hidingFinished();
void hideFinished();
int contentLeft() const;
int contentTop() const;
int contentRight() const;

View file

@ -56,9 +56,6 @@ public:
bool overlaps(const QRect &globalRect);
void otherEnter();
void otherLeave();
QMargins getMargin() const;
protected:
@ -75,6 +72,9 @@ private slots:
void onWindowActiveChanged();
private:
void otherEnter();
void otherLeave();
void appearanceCallback();
void hidingFinished();
void startAnimation();

View file

@ -110,3 +110,50 @@ mediaviewFileBlue: icon {
mediaviewTransparentBg: #ffffff;
mediaviewTransparentFg: #cccccc;
mediaviewTransparentSize: 4px;
mediaviewMenu: Menu(defaultMenu) {
itemBg: #383838;
itemBgOver: #505050;
itemFg: white;
itemFgOver: white;
itemFgDisabled: #999;
itemFgShortcut: #eee;
itemFgShortcutOver: #fff;
itemFgShortcutDisabled: #999;
separatorFg: #484848;
}
mediaviewPopupMenu: PopupMenu(defaultPopupMenu) {
shadow: icon {};
menu: mediaviewMenu;
}
mediaviewDropdownMenu: DropdownMenu(defaultDropdownMenu) {
menu: mediaviewMenu;
}
/*
mvDropdown: dropdown(dropdownDef) {
shadow: icon {};
padding: margins(11px, 12px, 11px, 12px);
border: 0px;
width: 182px;
}
mvButton: iconedButton(btnDefIconed) {
bgColor: #383838;
overBgColor: #505050;
font: font(fsize);
opacity: 1.;
overOpacity: 1.;
width: -32px;
height: 36px;
color: white;
textPos: point(16px, 9px);
downTextPos: point(16px, 10px);
duration: 0;
}
*/

View file

@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "application.h"
#include "ui/filedialog.h"
#include "ui/popupmenu.h"
#include "ui/widgets/popup_menu.h"
#include "media/media_clip_reader.h"
#include "media/view/media_clip_controller.h"
#include "styles/style_mediaview.h"
@ -88,7 +88,7 @@ MediaView::MediaView() : TWidget(App::wnd())
, _radial(animation(this, &MediaView::step_radial))
, _lastAction(-st::mvDeltaFromLastAction, -st::mvDeltaFromLastAction)
, _a_state(animation(this, &MediaView::step_state))
, _dropdown(this, st::mvDropdown) {
, _dropdown(this, st::mediaviewDropdownMenu) {
TextCustomTagsMap custom;
custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink()));
_saveMsgText.setRichText(st::medviewSaveMsgFont, lang(lng_mediaview_saved), _textDlgOptions, custom);
@ -126,32 +126,14 @@ MediaView::MediaView() : TWidget(App::wnd())
_touchTimer.setSingleShot(true);
connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
_btns.push_back(_btnSaveCancel = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_cancel))));
connect(_btnSaveCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel()));
_btns.push_back(_btnToMessage = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_context_to_msg))));
connect(_btnToMessage, SIGNAL(clicked()), this, SLOT(onToMessage()));
_btns.push_back(_btnShowInFolder = _dropdown.addButton(new IconedButton(this, st::mvButton, lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder))));
connect(_btnShowInFolder, SIGNAL(clicked()), this, SLOT(onShowInFolder()));
_btns.push_back(_btnCopy = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_copy))));
connect(_btnCopy, SIGNAL(clicked()), this, SLOT(onCopy()));
_btns.push_back(_btnForward = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_forward))));
connect(_btnForward, SIGNAL(clicked()), this, SLOT(onForward()));
_btns.push_back(_btnDelete = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_delete))));
connect(_btnDelete, SIGNAL(clicked()), this, SLOT(onDelete()));
_btns.push_back(_btnSaveAs = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_save_as))));
connect(_btnSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs()));
_btns.push_back(_btnViewAll = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_photos_all))));
connect(_btnViewAll, SIGNAL(clicked()), this, SLOT(onOverview()));
_dropdown.hide();
connect(&_dropdown, SIGNAL(hiding()), this, SLOT(onDropdownHiding()));
_controlsHideTimer.setSingleShot(true);
connect(&_controlsHideTimer, SIGNAL(timeout()), this, SLOT(onHideControls()));
connect(&_docDownload, SIGNAL(clicked()), this, SLOT(onDownload()));
connect(&_docSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs()));
connect(&_docCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel()));
connect(_dropdown, SIGNAL(beforeHidden()), this, SLOT(onDropdownHidden()));
}
void MediaView::moveToScreen() {
@ -400,18 +382,31 @@ void MediaView::updateControls() {
update();
}
void MediaView::updateDropdown() {
_btnSaveCancel->setVisible(_doc && _doc->loading());
_btnToMessage->setVisible(_msgid > 0);
_btnShowInFolder->setVisible(_doc && !_doc->filepath(DocumentData::FilePathResolveChecked).isEmpty());
_btnSaveAs->setVisible(true);
_btnCopy->setVisible((_doc && fileShown()) || (_photo && _photo->loaded()));
_btnForward->setVisible(_canForward);
_btnDelete->setVisible(_canDelete || (_photo && App::self() && _user == App::self()) || (_photo && _photo->peer && _photo->peer->photoId == _photo->id && (_photo->peer->isChat() || (_photo->peer->isChannel() && _photo->peer->asChannel()->amCreator()))));
_btnViewAll->setVisible(_history && typeHasMediaOverview(_overview));
_btnViewAll->setText(lang(_doc ? lng_mediaview_files_all : lng_mediaview_photos_all));
_dropdown.updateButtons();
_dropdown.moveToRight(0, height() - _dropdown.height());
void MediaView::updateActions() {
_actions.clear();
if (_doc && _doc->loading()) {
_actions.push_back({ lang(lng_cancel), SLOT(onSaveCancel()) });
}
if (_msgid > 0) {
_actions.push_back({ lang(lng_context_to_msg), SLOT(onToMessage()) });
}
if (_doc && !_doc->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
_actions.push_back({ lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), SLOT(onShowInFolder()) });
}
if ((_doc && fileShown()) || (_photo && _photo->loaded())) {
_actions.push_back({ lang(lng_mediaview_copy), SLOT(onCopy()) });
}
if (_canForward) {
_actions.push_back({ lang(lng_mediaview_forward), SLOT(onForward()) });
}
if (_canDelete || (_photo && App::self() && _user == App::self()) || (_photo && _photo->peer && _photo->peer->photoId == _photo->id && (_photo->peer->isChat() || (_photo->peer->isChannel() && _photo->peer->asChannel()->amCreator())))) {
_actions.push_back({ lang(lng_mediaview_delete), SLOT(onDelete()) });
}
_actions.push_back({ lang(lng_mediaview_save_as), SLOT(onSaveAs()) });
if (_history && typeHasMediaOverview(_overview)) {
_actions.push_back({ lang(_doc ? lng_mediaview_files_all : lng_mediaview_photos_all), SLOT(onOverview()) });
}
}
void MediaView::step_state(uint64 ms, bool timer) {
@ -662,7 +657,7 @@ void MediaView::activateControls() {
void MediaView::onHideControls(bool force) {
if (!force) {
if (!_dropdown.isHidden()
if (!_dropdown->isHidden()
|| _menu
|| _mousePressed
|| (_fullScreenVideo && _clipController && _clipController->geometry().contains(_lastMouseMovePos))) {
@ -682,7 +677,7 @@ void MediaView::onHideControls(bool force) {
if (!_a_state.animating()) _a_state.start();
}
void MediaView::onDropdownHiding() {
void MediaView::onDropdownHidden() {
setFocus();
_ignoringDropdown = true;
_lastMouseMovePos = mapFromGlobal(QCursor::pos());
@ -961,10 +956,7 @@ void MediaView::onOverview() {
}
void MediaView::onCopy() {
if (!_dropdown.isHidden()) {
_dropdown.ignoreShow();
_dropdown.hideStart();
}
_dropdown->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow);
if (_doc) {
if (!_current.isNull()) {
QApplication::clipboard()->setPixmap(_current);
@ -2395,10 +2387,10 @@ void MediaView::contextMenuEvent(QContextMenuEvent *e) {
_menu->deleteLater();
_menu = 0;
}
_menu = new PopupMenu(st::mvPopupMenu);
updateDropdown();
for (int32 i = 0, l = _btns.size(); i < l; ++i) {
if (!_btns.at(i)->isHidden()) _menu->addAction(_btns.at(i)->getText(), _btns.at(i), SIGNAL(clicked()))->setEnabled(true);
_menu = new Ui::PopupMenu(st::mediaviewPopupMenu);
updateActions();
for_const (auto &action, _actions) {
_menu->addAction(action.text, this, action.member)->setEnabled(true);
}
connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*)));
_menu->popup(e->globalPos());
@ -2541,10 +2533,14 @@ void MediaView::receiveMouse() {
}
void MediaView::onDropdown() {
updateDropdown();
_dropdown.ignoreShow(false);
_dropdown.showStart();
_dropdown.setFocus();
updateActions();
_dropdown->clearActions();
for_const (auto &action, _actions) {
_dropdown->addAction(action.text, this, action.member);
}
_dropdown->moveToRight(0, height() - _dropdown->height());
_dropdown->showAnimated();
_dropdown->setFocus();
}
void MediaView::onCheckActive() {

View file

@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "dropdown.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/effects/radial_animation.h"
namespace Media {
@ -29,7 +29,9 @@ class Controller;
} // namespace Clip
} // namespace Media
namespace Ui {
class PopupMenu;
} // namespace Ui
struct AudioPlaybackState;
@ -60,9 +62,6 @@ public:
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
void documentUpdated(DocumentData *doc);
void changingMsgId(HistoryItem *row, MsgId newId);
void updateDocSize();
void updateControls();
void updateDropdown();
void showSaveMsgFile();
void close();
@ -81,9 +80,9 @@ public:
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
public slots:
private slots:
void onHideControls(bool force = false);
void onDropdownHiding();
void onDropdownHidden();
void onScreenResized(int screen);
@ -130,6 +129,10 @@ private slots:
void onVideoPlayProgress(const AudioMsgId &audioId);
private:
void updateDocSize();
void updateControls();
void updateActions();
void displayPhoto(PhotoData *photo, HistoryItem *item);
void displayDocument(DocumentData *doc, HistoryItem *item);
void displayFinished();
@ -304,10 +307,14 @@ private:
anim::fvalue a_cOpacity;
bool _mousePressed = false;
PopupMenu *_menu = nullptr;
Dropdown _dropdown;
IconedButton *_btnSaveCancel, *_btnToMessage, *_btnShowInFolder, *_btnSaveAs, *_btnCopy, *_btnForward, *_btnDelete, *_btnViewAll;
QList<IconedButton*> _btns;
Ui::PopupMenu *_menu = nullptr;
ChildWidget<Ui::DropdownMenu> _dropdown;
struct ActionData {
QString text;
const char *member;
};
QList<ActionData> _actions;
bool _receiveMouse = true;

View file

@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/confirmbox.h"
#include "boxes/photocropbox.h"
#include "ui/filedialog.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/tooltip.h"
#include "window/top_bar_widget.h"
#include "window/chat_background.h"
#include "lang.h"
@ -931,22 +933,22 @@ void OverviewInner::onUpdateSelected() {
Qt::CursorShape cur = style::cur_default;
bool lnkChanged = ClickHandler::setActive(lnk, lnkhost);
if (lnkChanged) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
}
App::mousedItem(item);
if (_mousedItem != oldMousedItem) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
if (oldMousedItem) repaintItem(oldMousedItem, oldMousedItemIndex);
if (item) repaintItem(item);
}
if (_cursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) {
PopupTooltip::Hide();
Ui::Tooltip::Hide();
}
if (cursorState != _cursorState) {
_cursorState = cursorState;
}
if (lnk || cursorState == HistoryInDateCursorState) {
PopupTooltip::Show(1000, this);
Ui::Tooltip::Show(1000, this);
}
fixItemIndex(_dragItemIndex, _dragItem);
@ -1183,7 +1185,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
bool lnkIsAudio = lnkDocument ? (lnkDocument->document()->voice() != nullptr) : false;
bool lnkIsSong = lnkDocument ? (lnkDocument->document()->song() != nullptr) : false;
if (lnkPhoto || lnkDocument) {
_menu = new PopupMenu();
_menu = new Ui::PopupMenu();
if (App::hoveredLinkItem()) {
_menu->addAction(lang(lng_context_to_msg), this, SLOT(goToMessage()))->setEnabled(true);
}
@ -1221,7 +1223,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
repaintItem(App::contextItem());
if (_selectedMsgId) repaintItem(_selectedMsgId, -1);
} else if (!ignoreMousedItem && App::mousedItem() && App::mousedItem()->channelId() == itemChannel(_mousedItem) && App::mousedItem()->id == itemMsgId(_mousedItem)) {
_menu = new PopupMenu();
_menu = new Ui::PopupMenu();
QString linkCopyToClipboardText = _contextMenuLnk ? _contextMenuLnk->copyToClipboardContextItemText() : QString();
if (!linkCopyToClipboardText.isEmpty()) {
_menu->addAction(linkCopyToClipboardText, this, SLOT(copyContextUrl()))->setEnabled(true);

View file

@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "window/section_widget.h"
#include "ui/popupmenu.h"
#include "ui/widgets/tooltip.h"
namespace Overview {
namespace Layout {
@ -33,10 +33,11 @@ class Date;
namespace Ui {
class PlainShadow;
class PopupMenu;
} // namespace Ui
class OverviewWidget;
class OverviewInner : public TWidget, public AbstractTooltipShower, public RPCSender, private base::Subscriber {
class OverviewInner : public TWidget, public Ui::AbstractTooltipShower, public RPCSender, private base::Subscriber {
Q_OBJECT
public:
@ -263,7 +264,7 @@ private:
uint64 _touchTime = 0;
QTimer _touchScrollTimer;
PopupMenu *_menu = nullptr;
Ui::PopupMenu *_menu = nullptr;
};
class OverviewWidget : public TWidget, public RPCSender {

View file

@ -28,7 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "application.h"
#include "lang.h"
#include "localstorage.h"
#include "ui/popupmenu.h"
#include "ui/widgets/popup_menu.h"
#include <qpa/qplatformnativeinterface.h>

View file

@ -23,7 +23,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "window/main_window.h"
#include <windows.h>
namespace Ui {
class PopupMenu;
} // namespace Ui
namespace Platform {
@ -109,7 +111,7 @@ protected:
bool posInited = false;
QSystemTrayIcon *trayIcon = nullptr;
PopupMenu *trayIconMenu = nullptr;
Ui::PopupMenu *trayIconMenu = nullptr;
QImage icon256, iconbig256;
QIcon wndIcon;

View file

@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Profile {
BlockWidget::BlockWidget(QWidget *parent, PeerData *peer, const QString &title) : ScrolledWidget(parent)
BlockWidget::BlockWidget(QWidget *parent, PeerData *peer, const QString &title) : TWidget(parent)
, _peer(peer)
, _title(title) {
}

View file

@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Profile {
class BlockWidget : public ScrolledWidget, protected base::Subscriber {
class BlockWidget : public TWidget, protected base::Subscriber {
Q_OBJECT
public:

View file

@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Settings {
BlockWidget::BlockWidget(QWidget *parent, UserData *self, const QString &title) : ScrolledWidget(parent)
BlockWidget::BlockWidget(QWidget *parent, UserData *self, const QString &title) : TWidget(parent)
, _self(self)
, _title(title) {
}

View file

@ -32,7 +32,7 @@ class WidgetSlideWrap;
namespace Settings {
class BlockWidget : public ScrolledWidget, protected base::Subscriber {
class BlockWidget : public TWidget, protected base::Subscriber {
Q_OBJECT
public:

View file

@ -42,19 +42,19 @@ EmojiColorPicker::EmojiColorPicker() : TWidget()
, _a_selected(animation(this, &EmojiColorPicker::step_selected))
, a_opacity(0)
, _a_appearance(animation(this, &EmojiColorPicker::step_appearance))
, _shadow(st::dropdownDef.shadow) {
, _shadow(st::defaultDropdownShadow) {
memset(_variants, 0, sizeof(_variants));
memset(_hovers, 0, sizeof(_hovers));
setMouseTracking(true);
setFocusPolicy(Qt::NoFocus);
int32 w = st::emojiPanSize.width() * (EmojiColorsCount + 1) + 4 * st::emojiColorsPadding + st::emojiColorsSep + st::dropdownDef.shadow.width() * 2;
int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::dropdownDef.shadow.height() * 2;
int32 w = st::emojiPanSize.width() * (EmojiColorsCount + 1) + 4 * st::emojiColorsPadding + st::emojiColorsSep + st::defaultDropdownShadow.width() * 2;
int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::defaultDropdownShadow.height() * 2;
resize(w, h);
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideAnimated()));
}
void EmojiColorPicker::showEmoji(uint32 code) {
@ -72,7 +72,7 @@ void EmojiColorPicker::showEmoji(uint32 code) {
_variants[5] = emojiGet(e, 0xD83CDFFF);
if (!_cache.isNull()) _cache = QPixmap();
showStart();
showAnimated();
}
void EmojiColorPicker::paintEvent(QPaintEvent *e) {
@ -85,12 +85,12 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) {
p.setClipRect(e->rect());
}
int32 w = st::dropdownDef.shadow.width(), h = st::dropdownDef.shadow.height();
int32 w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height();
QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h);
_shadow.paint(p, r, st::dropdownDef.shadowShift);
_shadow.paint(p, r, st::defaultDropdownShadowShift);
if (_cache.isNull()) {
p.fillRect(e->rect().intersected(r), st::white->b);
p.fillRect(e->rect().intersected(r), st::white);
int32 x = w + 2 * st::emojiColorsPadding + st::emojiPanSize.width();
if (rtl()) x = width() - x - st::emojiColorsSep;
@ -108,7 +108,7 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) {
void EmojiColorPicker::enterEvent(QEvent *e) {
_hideTimer.stop();
if (_hiding) showStart();
if (_hiding) showAnimated();
TWidget::enterEvent(e);
}
@ -135,7 +135,7 @@ void EmojiColorPicker::mouseReleaseEvent(QMouseEvent *e) {
emit emojiSelected(_variants[_selected]);
}
_ignoreShow = true;
hideStart();
hideAnimated();
}
void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) {
@ -148,7 +148,7 @@ void EmojiColorPicker::step_appearance(float64 ms, bool timer) {
_a_appearance.stop();
return;
}
float64 dt = ms / st::dropdownDef.duration;
float64 dt = ms / st::defaultDropdownDuration;
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
@ -178,34 +178,34 @@ void EmojiColorPicker::step_selected(uint64 ms, bool timer) {
_hovers[index] = (i.key() > 0) ? dt : (1 - dt);
++i;
}
toUpdate += QRect(st::dropdownDef.shadow.width() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.height() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height());
toUpdate += QRect(st::defaultDropdownShadow.width() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::defaultDropdownShadow.height() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height());
}
if (timer) rtlupdate(toUpdate.boundingRect());
if (_emojiAnimations.isEmpty()) _a_selected.stop();
}
void EmojiColorPicker::hideStart(bool fast) {
if (fast) {
clearSelection(true);
if (_a_appearance.animating()) _a_appearance.stop();
if (_a_selected.animating()) _a_selected.stop();
a_opacity = anim::fvalue(0);
_cache = QPixmap();
hide();
emit hidden();
} else {
if (_cache.isNull()) {
int32 w = st::dropdownDef.shadow.width(), h = st::dropdownDef.shadow.height();
_cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h));
clearSelection(true);
}
_hiding = true;
a_opacity.start(0);
_a_appearance.start();
}
void EmojiColorPicker::hideFast() {
clearSelection(true);
if (_a_appearance.animating()) _a_appearance.stop();
if (_a_selected.animating()) _a_selected.stop();
a_opacity = anim::fvalue(0);
_cache = QPixmap();
hide();
emit hidden();
}
void EmojiColorPicker::showStart() {
void EmojiColorPicker::hideAnimated() {
if (_cache.isNull()) {
int32 w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height();
_cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h));
clearSelection(true);
}
_hiding = true;
a_opacity.start(0);
_a_appearance.start();
}
void EmojiColorPicker::showAnimated() {
if (_ignoreShow) return;
_hiding = false;
@ -217,7 +217,7 @@ void EmojiColorPicker::showStart() {
return;
}
if (_cache.isNull()) {
int32 w = st::dropdownDef.shadow.width(), h = st::dropdownDef.shadow.height();
int32 w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height();
_cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h));
clearSelection(true);
}
@ -241,9 +241,9 @@ void EmojiColorPicker::clearSelection(bool fast) {
void EmojiColorPicker::updateSelected() {
int32 selIndex = -1;
QPoint p(mapFromGlobal(_lastMousePos));
int32 sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::dropdownDef.shadow.height() - st::emojiColorsPadding;
int32 sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::defaultDropdownShadow.height() - st::emojiColorsPadding;
if (y >= 0 && y < st::emojiPanSize.height()) {
int32 x = sx - st::dropdownDef.shadow.width() - st::emojiColorsPadding;
int32 x = sx - st::defaultDropdownShadow.width() - st::emojiColorsPadding;
if (x >= 0 && x < st::emojiPanSize.width()) {
selIndex = 0;
} else {
@ -279,7 +279,7 @@ void EmojiColorPicker::updateSelected() {
void EmojiColorPicker::drawVariant(Painter &p, int variant) {
float64 hover = _hovers[variant];
QPoint w(st::dropdownDef.shadow.width() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.height() + st::emojiColorsPadding);
QPoint w(st::defaultDropdownShadow.width() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::defaultDropdownShadow.height() + st::emojiColorsPadding);
if (hover > 0) {
p.setOpacity(hover);
QPoint tl(w);
@ -291,7 +291,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) {
p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_variants[variant]->x * esize, _variants[variant]->y * esize, esize, esize));
}
EmojiPanInner::EmojiPanInner() : ScrolledWidget()
EmojiPanInner::EmojiPanInner() : TWidget()
, _maxHeight(int(st::emojiPanMaxHeight) - st::rbEmoji.height)
, _a_selected(animation(this, &EmojiPanInner::step_selected)) {
resize(st::emojiPanWidth - st::emojiScroll.width, countHeight());
@ -404,7 +404,7 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) {
bool EmojiPanInner::checkPickerHide() {
if (!_picker.isHidden() && _selected == _pickerSel) {
_picker.hideStart();
_picker.hideAnimated();
_pickerSel = -1;
updateSelected();
return true;
@ -446,7 +446,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift;
if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) {
if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) {
_picker.hideStart();
_picker.hideAnimated();
_pickerSel = -1;
}
}
@ -576,7 +576,7 @@ void EmojiPanInner::onColorSelected(EmojiPtr emoji) {
}
}
selectEmoji(emoji);
_picker.hideStart();
_picker.hideAnimated();
}
void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) {
@ -642,7 +642,7 @@ DBIEmojiTab EmojiPanInner::currentTab(int yOffset) const {
void EmojiPanInner::hideFinish() {
if (!_picker.isHidden()) {
_picker.hideStart(true);
_picker.hideFast();
_pickerSel = -1;
clearSelection(true);
}
@ -738,9 +738,9 @@ void EmojiPanInner::updateSelected() {
setCursor((newSel >= 0) ? style::cur_pointer : style::cur_default);
if (newSel >= 0 && !_picker.isHidden()) {
if (newSel != _pickerSel) {
_picker.hideStart();
_picker.hideAnimated();
} else {
_picker.showStart();
_picker.showAnimated();
}
}
}
@ -793,7 +793,7 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) {
update();
}
StickerPanInner::StickerPanInner() : ScrolledWidget()
StickerPanInner::StickerPanInner() : TWidget()
, _a_selected(animation(this, &StickerPanInner::step_selected))
, _section(cShowingSavedGifs() ? Section::Gifs : Section::Stickers)
, _addText(lang(lng_stickers_featured_add).toUpper())
@ -2550,7 +2550,7 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent)
, _contentHeightEmoji(_contentHeight - st::rbEmoji.height)
, _contentHeightStickers(_contentHeight - st::rbEmoji.height)
, _a_appearance(animation(this, &EmojiPan::step_appearance))
, _shadow(st::dropdownDef.shadow)
, _shadow(st::defaultDropdownShadow)
, _recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent)
, _people(this , qsl("emoji_group"), dbietPeople , QString(), false, st::rbEmojiPeople)
, _nature(this , qsl("emoji_group"), dbietNature , QString(), false, st::rbEmojiNature)
@ -2573,24 +2573,24 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent)
s_scroll.setFocusPolicy(Qt::NoFocus);
s_scroll.viewport()->setFocusPolicy(Qt::NoFocus);
_width = st::dropdownDef.padding.left() + st::emojiPanWidth + st::dropdownDef.padding.right();
_height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom();
_width = st::defaultDropdownPadding.left() + st::emojiPanWidth + st::defaultDropdownPadding.right();
_height = st::defaultDropdownPadding.top() + _contentHeight + st::defaultDropdownPadding.bottom();
_bottom = 0;
resize(_width, _height);
e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji);
s_scroll.resize(st::emojiPanWidth, _contentHeightStickers);
e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top());
e_scroll.move(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top());
e_scroll.setWidget(&e_inner);
s_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top());
s_scroll.move(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top());
s_scroll.setWidget(&s_inner);
e_inner.moveToLeft(0, 0, e_scroll.width());
s_inner.moveToLeft(0, 0, s_scroll.width());
int32 left = _iconsLeft = st::dropdownDef.padding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2;
int32 top = _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height;
int32 left = _iconsLeft = st::defaultDropdownPadding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2;
int32 top = _iconsTop = st::defaultDropdownPadding.top() + _contentHeight - st::rbEmoji.height;
prepareTab(left, top, _width, _recent);
prepareTab(left, top, _width, _people);
prepareTab(left, top, _width, _nature);
@ -2603,7 +2603,7 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent)
updatePanelsPositions(e_panels, 0);
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideAnimated()));
connect(&e_inner, SIGNAL(scrollToY(int)), &e_scroll, SLOT(scrollToY(int)));
connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool)));
@ -2666,7 +2666,7 @@ void EmojiPan::updateContentHeight() {
_contentHeightEmoji = he;
_contentHeightStickers = hs;
_height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom();
_height = st::defaultDropdownPadding.top() + _contentHeight + st::defaultDropdownPadding.bottom();
resize(_width, _height);
move(x(), _bottom - height());
@ -2683,7 +2683,7 @@ void EmojiPan::updateContentHeight() {
s_scroll.resize(st::emojiPanWidth, _contentHeightStickers);
}
_iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height;
_iconsTop = st::defaultDropdownPadding.top() + _contentHeight - st::rbEmoji.height;
_recent.move(_recent.x(), _iconsTop);
_people.move(_people.x(), _iconsTop);
_nature.move(_nature.x(), _iconsTop);
@ -2742,9 +2742,9 @@ void EmojiPan::paintEvent(QPaintEvent *e) {
p.setOpacity(o = a_opacity.current());
}
QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom());
QRect r(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top(), _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom());
_shadow.paint(p, r, st::dropdownDef.shadowShift);
_shadow.paint(p, r, st::defaultDropdownShadowShift);
if (_toCache.isNull()) {
if (_cache.isNull()) {
@ -2871,7 +2871,7 @@ void EmojiPan::moveBottom(int32 bottom, bool force) {
void EmojiPan::enterEvent(QEvent *e) {
_hideTimer.stop();
if (_hiding) showStart();
if (_hiding) showAnimated();
}
bool EmojiPan::preventAutoHide() const {
@ -2881,7 +2881,7 @@ bool EmojiPan::preventAutoHide() const {
void EmojiPan::leaveEvent(QEvent *e) {
if (preventAutoHide() || s_inner.inlineResultsShown()) return;
if (_a_appearance.animating()) {
hideStart();
hideAnimated();
} else {
_hideTimer.start(300);
}
@ -2889,13 +2889,13 @@ void EmojiPan::leaveEvent(QEvent *e) {
void EmojiPan::otherEnter() {
_hideTimer.stop();
showStart();
showAnimated();
}
void EmojiPan::otherLeave() {
if (preventAutoHide() || s_inner.inlineResultsShown()) return;
if (_a_appearance.animating()) {
hideStart();
hideAnimated();
} else {
_hideTimer.start(0);
}
@ -2990,7 +2990,7 @@ bool EmojiPan::event(QEvent *e) {
return TWidget::event(e);
}
void EmojiPan::fastHide() {
void EmojiPan::hideFast() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
@ -3107,7 +3107,7 @@ void EmojiPan::updateSelected() {
void EmojiPan::updateIcons() {
if (!_stickersShown || !s_inner.showSectionIcons()) return;
QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom());
QRect r(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top(), _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom());
update(r.left(), _iconsTop, r.width(), st::rbEmoji.height);
}
@ -3177,7 +3177,7 @@ void EmojiPan::step_appearance(float64 ms, bool timer) {
return;
}
float64 dt = ms / st::dropdownDef.duration;
float64 dt = ms / st::defaultDropdownDuration;
if (dt >= 1) {
_a_appearance.stop();
a_opacity.finish();
@ -3193,10 +3193,10 @@ void EmojiPan::step_appearance(float64 ms, bool timer) {
if (timer) update();
}
void EmojiPan::hideStart() {
if (preventAutoHide() || s_inner.inlineResultsShown()) return;
void EmojiPan::hideAnimated() {
if (isHidden() || preventAutoHide() || s_inner.inlineResultsShown()) return;
hideAnimated();
startHideAnimated();
}
void EmojiPan::prepareShowHideCache() {
@ -3204,12 +3204,12 @@ void EmojiPan::prepareShowHideCache() {
QPixmap from = _fromCache, to = _toCache;
_fromCache = _toCache = QPixmap();
showAll();
_cache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding));
_cache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding));
_fromCache = from; _toCache = to;
}
}
void EmojiPan::hideAnimated() {
void EmojiPan::startHideAnimated() {
if (_hiding) return;
prepareShowHideCache();
@ -3247,7 +3247,7 @@ void EmojiPan::hideFinish() {
Notify::clipStopperHidden(ClipStopperSavedGifsPanel);
}
void EmojiPan::showStart() {
void EmojiPan::showAnimated() {
if (!isHidden() && !_hiding) {
return;
}
@ -3297,7 +3297,7 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) {
} else if (e->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton/* && !dynamic_cast<StickerPan*>(obj)*/) {
if (isHidden() || _hiding) {
_hideTimer.stop();
showStart();
showAnimated();
} else {
hideAnimated();
}
@ -3317,7 +3317,7 @@ void EmojiPan::stickersInstalled(uint64 setId) {
showAll();
s_inner.showStickerSet(setId);
updateContentHeight();
showStart();
showAnimated();
}
void EmojiPan::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) {
@ -3490,7 +3490,7 @@ void EmojiPan::validateSelectedIcon(ValidateIconAnimations animations) {
void EmojiPan::onSwitch() {
QPixmap cache = _cache;
_fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding));
_fromCache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding));
_stickersShown = !_stickersShown;
if (!_stickersShown) {
Notify::clipStopperHidden(ClipStopperSavedGifsPanel);
@ -3515,7 +3515,7 @@ void EmojiPan::onSwitch() {
_cache = QPixmap();
showAll();
_toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding));
_toCache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding));
_cache = cache;
hideAll();
@ -3804,7 +3804,7 @@ int32 EmojiPan::showInlineRows(bool newResults) {
} else {
_hideTimer.stop();
if (hidden || _hiding) {
showStart();
showAnimated();
} else if (!_stickersShown) {
onSwitch();
}

View file

@ -67,21 +67,20 @@ public:
void step_appearance(float64 ms, bool timer);
void step_selected(uint64 ms, bool timer);
void showStart();
void clearSelection(bool fast = false);
public slots:
void hideFast();
void hideStart(bool fast = false);
public slots:
void showAnimated();
void hideAnimated();
signals:
void emojiSelected(EmojiPtr emoji);
void hidden();
private:
void drawVariant(Painter &p, int variant);
void updateSelected();
@ -113,7 +112,7 @@ private:
};
class EmojiPanel;
class EmojiPanInner : public ScrolledWidget {
class EmojiPanInner : public TWidget {
Q_OBJECT
public:
@ -207,7 +206,7 @@ struct StickerIcon {
int pixh = 0;
};
class StickerPanInner : public ScrolledWidget, private base::Subscriber {
class StickerPanInner : public TWidget, private base::Subscriber {
Q_OBJECT
public:
@ -499,7 +498,7 @@ public:
bool event(QEvent *e);
void fastHide();
void hideFast();
bool hiding() const {
return _hiding || _hideTimer.isActive();
}
@ -517,10 +516,10 @@ public:
bool overlaps(const QRect &globalRect) {
if (isHidden() || !_cache.isNull()) return false;
return QRect(st::dropdownDef.padding.left(),
st::dropdownDef.padding.top(),
_width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(),
_height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()
return QRect(st::defaultDropdownPadding.left(),
st::defaultDropdownPadding.top(),
_width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(),
_height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom()
).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
@ -534,7 +533,9 @@ public:
}
public slots:
void hideStart();
void showAnimated();
void hideAnimated();
void refreshStickers();
private slots:
@ -542,7 +543,6 @@ private slots:
void hideFinish();
void showStart();
void onWndActiveChanged();
void onTabChange();
@ -590,7 +590,7 @@ private:
void updateContentHeight();
void leaveToChildEvent(QEvent *e, QWidget *child);
void hideAnimated();
void startHideAnimated();
void prepareShowHideCache();
void updateSelected();

View file

@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
HistoryDownButton::HistoryDownButton(QWidget *parent) : Button(parent)
, a_arrowOpacity(st::btnAttachEmoji.opacity, st::btnAttachEmoji.opacity)
, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity)
, _a_arrowOver(animation(this, &HistoryDownButton::step_arrowOver)) {
setCursor(style::cur_pointer);
@ -83,7 +83,7 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) {
}
void HistoryDownButton::onStateChanged(int oldState, ButtonStateChangeSource source) {
a_arrowOpacity.start((_state & (StateOver | StateDown)) ? st::btnAttachEmoji.overOpacity : st::btnAttachEmoji.opacity);
a_arrowOpacity.start((_state & (StateOver | StateDown)) ? st::historyAttachEmoji.overOpacity : st::historyAttachEmoji.opacity);
if (source == ButtonByUser || source == ButtonByPress) {
_a_arrowOver.stop();
@ -118,7 +118,7 @@ void HistoryDownButton::hideAnimated() {
void HistoryDownButton::toggleAnimated() {
_shown = !_shown;
float64 from = _shown ? 0. : 1., to = _shown ? 1. : 0.;
_a_show.start([this] { update(); }, from, to, st::btnAttachEmoji.duration);
_a_show.start([this] { update(); }, from, to, st::historyAttachEmoji.duration);
}
void HistoryDownButton::finishAnimation() {
@ -127,7 +127,7 @@ void HistoryDownButton::finishAnimation() {
}
void HistoryDownButton::step_arrowOver(float64 ms, bool timer) {
float64 dt = ms / st::btnAttachEmoji.duration;
float64 dt = ms / st::historyAttachEmoji.duration;
if (dt >= 1) {
_a_arrowOver.stop();
a_arrowOpacity.finish();
@ -137,4 +137,64 @@ void HistoryDownButton::step_arrowOver(float64 ms, bool timer) {
if (timer) update();
}
EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : Button(parent)
, _st(st)
, _a_loading(animation(this, &EmojiButton::step_loading)) {
resize(_st.width, _st.height);
setCursor(style::cur_pointer);
}
void EmojiButton::paintEvent(QPaintEvent *e) {
Painter p(this);
uint64 ms = getms();
p.fillRect(e->rect(), st::white);
auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.);
auto opacity = over * _st.overOpacity + (1. - over) * _st.opacity;
auto loading = a_loading.current(ms, _loading ? 1 : 0);
p.setOpacity(opacity * (1 - loading));
_st.icon.paint(p, (_state & StateDown) ? _st.downIconPosition : _st.iconPosition, width());
p.setOpacity(opacity);
p.setPen(QPen(st::historyEmojiCircleFg, st::historyEmojiCircleLine));
p.setBrush(Qt::NoBrush);
p.setRenderHint(QPainter::HighQualityAntialiasing);
QRect inner(QPoint((width() - st::historyEmojiCircle.width()) / 2, st::historyEmojiCircleTop), st::historyEmojiCircle);
if (loading > 0) {
int32 full = 5760;
int32 start = qRound(full * float64(ms % uint64(st::historyEmojiCirclePeriod)) / st::historyEmojiCirclePeriod), part = qRound(loading * full / st::historyEmojiCirclePart);
p.drawArc(inner, start, full - part);
} else {
p.drawEllipse(inner);
}
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
}
void EmojiButton::setLoading(bool loading) {
if (_loading != loading) {
_loading = loading;
auto from = loading ? 0. : 1., to = loading ? 1. : 0.;
a_loading.start([this] { update(); }, from, to, st::historyEmojiCircleDuration);
if (loading) {
_a_loading.start();
} else {
_a_loading.stop();
}
}
}
void EmojiButton::onStateChanged(int oldState, ButtonStateChangeSource source) {
auto over = (_state & StateOver);
if (over != (oldState & StateOver)) {
auto from = over ? 0. : 1.;
auto to = over ? 1. : 0.;
_a_over.start([this] { update(); }, from, to, _st.duration);
}
}
} // namespace Ui

View file

@ -61,4 +61,31 @@ private:
};
class EmojiButton : public Button {
public:
EmojiButton(QWidget *parent, const style::IconButton &st);
void setLoading(bool loading);
protected:
void paintEvent(QPaintEvent *e) override;
void onStateChanged(int oldState, ButtonStateChangeSource source) override;
private:
const style::IconButton &_st;
FloatAnimation _a_over;
bool _loading = false;
FloatAnimation a_loading;
Animation _a_loading;
void step_loading(uint64 ms, bool timer) {
if (timer) {
update();
}
}
};
} // namespace Ui

View file

@ -261,10 +261,8 @@ void CountrySelectBox::doSetInnerFocus() {
_select->setInnerFocus();
}
CountrySelectBox::Inner::Inner(QWidget *parent) : ScrolledWidget(parent)
, _rowHeight(st::countryRowHeight)
, _sel(0)
, _mouseSel(false) {
CountrySelectBox::Inner::Inner(QWidget *parent) : TWidget(parent)
, _rowHeight(st::countryRowHeight) {
setAttribute(Qt::WA_OpaquePaintEvent);
CountriesByISO2::const_iterator l = _countriesByISO2.constFind(lastValidISO);

View file

@ -103,7 +103,7 @@ private:
};
// This class is hold in header because it requires Qt preprocessing.
class CountrySelectBox::Inner : public ScrolledWidget {
class CountrySelectBox::Inner : public TWidget {
Q_OBJECT
public:
@ -135,11 +135,11 @@ protected:
private:
void updateSelectedRow();
int32 _rowHeight;
int _rowHeight;
int32 _sel;
int _sel = 0;
QString _filter;
bool _mouseSel;
bool _mouseSel = false;
QPoint _lastMousePos;

View file

@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "ui/flatbutton.h"
#include "styles/style_history.h"
FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatButton &st) : Button(parent)
, _text(text)
, _st(st)
@ -264,83 +266,6 @@ void IconedButton::paintEvent(QPaintEvent *e) {
}
}
MaskedButton::MaskedButton(QWidget *parent, const style::iconedButton &st, const QString &text) : IconedButton(parent, st, text) {
}
void MaskedButton::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setOpacity(_opacity);
p.setOpacity(a_opacity.current() * _opacity);
if (!_text.isEmpty()) {
p.setFont(_st.font->f);
p.setRenderHint(QPainter::TextAntialiasing);
p.setPen(a_bg.current());
const QPoint &t((_state & StateDown) ? _st.downTextPos : _st.textPos);
p.drawText(t.x(), t.y() + _st.font->ascent, _text);
}
const style::sprite &i((_state & StateDown) ? _st.downIcon : _st.icon);
if (i.pxWidth()) {
const QPoint &t((_state & StateDown) ? _st.downIconPos : _st.iconPos);
p.fillRect(QRect(t, QSize(i.pxWidth(), i.pxHeight())), a_bg.current());
p.drawSprite(t, i);
}
}
EmojiButton::EmojiButton(QWidget *parent, const style::iconedButton &st) : IconedButton(parent, st)
, _loading(false)
, _a_loading(animation(this, &EmojiButton::step_loading)) {
}
void EmojiButton::paintEvent(QPaintEvent *e) {
Painter p(this);
uint64 ms = getms();
float64 loading = a_loading.current(ms, _loading ? 1 : 0);
p.setOpacity(_opacity * (1 - loading));
p.fillRect(e->rect(), a_bg.current());
p.setOpacity(a_opacity.current() * _opacity * (1 - loading));
const style::sprite &i((_state & StateDown) ? _st.downIcon : _st.icon);
if (!i.isEmpty()) {
const QPoint &t((_state & StateDown) ? _st.downIconPos : _st.iconPos);
p.drawSprite(t, i);
}
p.setOpacity(a_opacity.current() * _opacity);
p.setPen(QPen(st::emojiCircleFg, st::emojiCircleLine));
p.setBrush(Qt::NoBrush);
p.setRenderHint(QPainter::HighQualityAntialiasing);
QRect inner(QPoint((width() - st::emojiCircle.width()) / 2, st::emojiCircleTop), st::emojiCircle);
if (loading > 0) {
int32 full = 5760;
int32 start = qRound(full * float64(ms % uint64(st::emojiCirclePeriod)) / st::emojiCirclePeriod), part = qRound(loading * full / st::emojiCirclePart);
p.drawArc(inner, start, full - part);
} else {
p.drawEllipse(inner);
}
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
}
void EmojiButton::setLoading(bool loading) {
if (_loading != loading) {
_loading = loading;
auto from = loading ? 0. : 1., to = loading ? 1. : 0.;
a_loading.start([this] { update(); }, from, to, st::emojiCircleDuration);
if (loading) {
_a_loading.start();
} else {
_a_loading.stop();
}
}
}
BoxButton::BoxButton(QWidget *parent, const QString &text, const style::RoundButton &st) : Button(parent)
, _text(text.toUpper())
, _fullText(text.toUpper())

View file

@ -28,7 +28,6 @@ class FlatButton : public Button {
Q_OBJECT
public:
FlatButton(QWidget *parent, const QString &text, const style::flatButton &st);
void step_appearance(float64 ms, bool timer);
@ -45,11 +44,9 @@ public:
}
public slots:
void onStateChange(int oldState, ButtonStateChangeSource source);
private:
QString _text, _textForAutoSize;
int32 _textWidth;
@ -91,7 +88,6 @@ class IconedButton : public Button {
Q_OBJECT
public:
IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text = QString());
void step_appearance(float64 ms, bool timer);
@ -103,11 +99,9 @@ public:
QString getText() const;
public slots:
void onStateChange(int oldState, ButtonStateChangeSource source);
protected:
QString _text;
style::iconedButton _st;
@ -120,39 +114,6 @@ protected:
float64 _opacity;
};
class MaskedButton : public IconedButton {
Q_OBJECT
public:
MaskedButton(QWidget *parent, const style::iconedButton &st, const QString &text = QString());
void paintEvent(QPaintEvent *e);
};
class EmojiButton : public IconedButton {
Q_OBJECT
public:
EmojiButton(QWidget *parent, const style::iconedButton &st);
void paintEvent(QPaintEvent *e);
void setLoading(bool loading);
private:
bool _loading;
FloatAnimation a_loading;
Animation _a_loading;
void step_loading(uint64 ms, bool timer) {
if (timer) {
update();
}
}
};
class BoxButton : public Button {
Q_OBJECT

View file

@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "ui/flatinput.h"
#include "ui/popupmenu.h"
#include "ui/widgets/popup_menu.h"
#include "mainwindow.h"
#include "countryinput.h"
#include "lang.h"
@ -244,7 +244,12 @@ void FlatInput::updatePlaceholderText() {
void FlatInput::contextMenuEvent(QContextMenuEvent *e) {
if (auto menu = createStandardContextMenu()) {
(new PopupMenu(menu))->popup(e->globalPos());
menu->addSeparator();
auto action = menu->addAction(QString("test"));
action->setMenu(new QMenu(this));
action->menu()->addAction(QString("test123"));
action->menu()->addAction(QString("test456"));
(new Ui::PopupMenu(menu))->popup(e->globalPos());
}
}
@ -1282,7 +1287,7 @@ void InputArea::Inner::paintEvent(QPaintEvent *e) {
void InputArea::Inner::contextMenuEvent(QContextMenuEvent *e) {
if (auto menu = createStandardContextMenu()) {
(new PopupMenu(menu))->popup(e->globalPos());
(new Ui::PopupMenu(menu))->popup(e->globalPos());
}
}
@ -2029,7 +2034,7 @@ void InputField::Inner::paintEvent(QPaintEvent *e) {
void InputField::Inner::contextMenuEvent(QContextMenuEvent *e) {
if (auto menu = createStandardContextMenu()) {
(new PopupMenu(menu))->popup(e->globalPos());
(new Ui::PopupMenu(menu))->popup(e->globalPos());
}
}
@ -2237,7 +2242,7 @@ void MaskedInputField::updatePlaceholderText() {
void MaskedInputField::contextMenuEvent(QContextMenuEvent *e) {
if (auto menu = createStandardContextMenu()) {
(new PopupMenu(menu))->popup(e->globalPos());
(new Ui::PopupMenu(menu))->popup(e->globalPos());
}
}

View file

@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "ui/flatlabel.h"
#include "ui/popupmenu.h"
#include "ui/widgets/popup_menu.h"
#include "mainwindow.h"
#include "lang.h"
@ -414,7 +414,7 @@ void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason)
uponSelection = hasSelection;
}
_contextMenu = new PopupMenu();
_contextMenu = new Ui::PopupMenu();
_contextMenuClickHandler = ClickHandler::getActive();

View file

@ -20,7 +20,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Ui {
class PopupMenu;
} // namespace Ui
class FlatLabel : public TWidget, public ClickHandlerHost {
Q_OBJECT
@ -137,7 +139,7 @@ private:
QPoint _trippleClickPoint;
QTimer _trippleClickTimer;
PopupMenu *_contextMenu = nullptr;
Ui::PopupMenu *_contextMenu = nullptr;
ClickHandlerPtr _contextMenuClickHandler;
QString _contextCopyText;
ExpandLinksMode _contextExpandLinksMode = ExpandLinksAll;

View file

@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "flattextarea.h"
#include "ui/popupmenu.h"
#include "ui/widgets/popup_menu.h"
#include "mainwindow.h"
QByteArray FlatTextarea::serializeTagsList(const TagList &tags) {
@ -1421,6 +1421,6 @@ void FlatTextarea::dropEvent(QDropEvent *e) {
void FlatTextarea::contextMenuEvent(QContextMenuEvent *e) {
if (auto menu = createStandardContextMenu()) {
(new PopupMenu(menu))->popup(e->globalPos());
(new Ui::PopupMenu(menu))->popup(e->globalPos());
}
}

View file

@ -1,678 +0,0 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "popupmenu.h"
#include "flatbutton.h"
#include "pspecific.h"
#include "application.h"
#include "lang.h"
PopupMenu::PopupMenu(const style::PopupMenu &st) : TWidget(nullptr)
, _st(st)
, _itemHeight(_st.itemPadding.top() + _st.itemFont->height + _st.itemPadding.bottom())
, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom())
, _shadow(_st.shadow)
, a_opacity(1)
, _a_hide(animation(this, &PopupMenu::step_hide)) {
init();
}
PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(nullptr)
, _st(st)
, _menu(menu)
, _itemHeight(_st.itemPadding.top() + _st.itemFont->height + _st.itemPadding.bottom())
, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom())
, _shadow(_st.shadow)
, a_opacity(1)
, _a_hide(animation(this, &PopupMenu::step_hide)) {
_menu->setParent(this);
_menu->hide();
init();
for (auto action : menu->actions()) {
addAction(action);
}
}
void PopupMenu::init() {
_padding = _shadow.getDimensions(_st.shadowShift);
resetActions();
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Popup | Qt::NoDropShadowWindowHint);
setMouseTracking(true);
hide();
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
}
QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member) {
QAction *a = new QAction(text, this);
connect(a, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection);
return addAction(a);
}
QAction *PopupMenu::addAction(QAction *a) {
connect(a, SIGNAL(changed()), this, SLOT(actionChanged()));
_actions.push_back(a);
if (auto submenu = a->menu()) {
_menus.push_back(new PopupMenu(submenu));
_menus.back()->deleteOnHide(false);
} else {
_menus.push_back(nullptr);
}
_texts.push_back(QString());
_shortcutTexts.push_back(QString());
int32 w = processAction(a, _actions.size() - 1, width());
resize(w, height() + (a->isSeparator() ? _separatorHeight : _itemHeight));
update();
return a;
}
QAction *PopupMenu::addSeparator() {
QAction *separator = new QAction(this);
separator->setSeparator(true);
return addAction(separator);
}
int32 PopupMenu::processAction(QAction *a, int32 index, int32 w) {
if (a->isSeparator() || a->text().isEmpty()) {
_texts[index] = _shortcutTexts[index] = QString();
} else {
QStringList texts = a->text().split('\t');
int32 textw = _st.itemFont->width(texts.at(0));
int32 goodw = _padding.left() + _st.itemPadding.left() + textw + _st.itemPadding.right() + _padding.right();
if (_menus.at(index)) {
goodw += _st.itemPadding.left() + _st.arrow.width();
} else if (texts.size() > 1) {
goodw += _st.itemPadding.left() + _st.itemFont->width(texts.at(1));
}
w = snap(goodw, w, int32(_padding.left() + _st.widthMax + _padding.right()));
_texts[index] = (w < goodw) ? _st.itemFont->elided(texts.at(0), w - (goodw - textw)) : texts.at(0);
_shortcutTexts[index] = texts.size() > 1 ? texts.at(1) : QString();
}
return w;
}
PopupMenu::Actions &PopupMenu::actions() {
return _actions;
}
void PopupMenu::actionChanged() {
int32 w = _padding.left() + _st.widthMin + _padding.right();
for (int32 i = 0, l = _actions.size(); i < l; ++i) {
w = processAction(_actions.at(i), i, w);
}
if (w != width()) {
resize(w, height());
}
update();
}
void PopupMenu::resetActions() {
clearActions();
resize(_padding.left() + _st.widthMin + _padding.right(), _padding.top() + (_st.skip * 2) + _padding.bottom());
}
void PopupMenu::clearActions(bool force) {
if (_menu && !force) return;
if (!_menu) {
for (int32 i = 0, l = _actions.size(); i < l; ++i) {
delete _actions[i];
}
}
_actions.clear();
for (int32 i = 0, l = _menus.size(); i < l; ++i) {
delete _menus[i];
}
_menus.clear();
_childMenuIndex = -1;
_selected = -1;
}
void PopupMenu::resizeEvent(QResizeEvent *e) {
_inner = QRect(_padding.left(), _padding.top(), width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom());
return TWidget::resizeEvent(e);
}
void PopupMenu::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(e->rect());
p.setClipRect(r);
QPainter::CompositionMode m = p.compositionMode();
p.setCompositionMode(QPainter::CompositionMode_Source);
if (_a_hide.animating()) {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
return;
}
p.fillRect(r, st::almostTransparent->b);
p.setCompositionMode(m);
_shadow.paint(p, _inner, _st.shadowShift);
QRect topskip(_padding.left(), _padding.top(), _inner.width(), _st.skip);
QRect bottomskip(_padding.left(), height() - _padding.bottom() - _st.skip, _inner.width(), _st.skip);
if (r.intersects(topskip)) p.fillRect(r.intersected(topskip), _st.itemBg->b);
if (r.intersects(bottomskip)) p.fillRect(r.intersected(bottomskip), _st.itemBg->b);
int32 y = _padding.top() + _st.skip;
p.translate(_padding.left(), y);
p.setFont(_st.itemFont);
for (int32 i = 0, l = _actions.size(); i < l; ++i) {
if (r.top() + r.height() <= y) break;
int32 h = _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight;
y += h;
if (r.top() < y) {
if (_actions.at(i)->isSeparator()) {
p.fillRect(0, 0, _inner.width(), h, _st.itemBg->b);
p.fillRect(_st.separatorPadding.left(), _st.separatorPadding.top(), _inner.width() - _st.separatorPadding.left() - _st.separatorPadding.right(), _st.separatorWidth, _st.separatorFg->b);
} else {
bool enabled = _actions.at(i)->isEnabled(), selected = (i == _selected && enabled);
p.fillRect(0, 0, _inner.width(), h, (selected ? _st.itemBgOver : _st.itemBg)->b);
p.setPen(selected ? _st.itemFgOver : (enabled ? _st.itemFg : _st.itemFgDisabled));
p.drawTextLeft(_st.itemPadding.left(), _st.itemPadding.top(), _inner.width(), _texts.at(i));
if (_menus.at(i)) {
_st.arrow.paint(p, _inner.width() - _st.itemPadding.right() - _st.arrow.width(), (_itemHeight - _st.arrow.height()) / 2, _inner.width());
} else if (!_shortcutTexts.at(i).isEmpty()) {
p.setPen(selected ? _st.itemFgShortcutOver : (enabled ? _st.itemFgShortcut : _st.itemFgShortcutDisabled));
p.drawTextRight(_st.itemPadding.right(), _st.itemPadding.top(), _inner.width(), _shortcutTexts.at(i));
}
}
}
p.translate(0, h);
}
}
void PopupMenu::updateSelected() {
if (!_mouseSelection) return;
QPoint p(mapFromGlobal(_mouse) - QPoint(_padding.left(), _padding.top() + _st.skip));
int32 selected = -1, y = 0;
while (y <= p.y() && ++selected < _actions.size()) {
y += _actions.at(selected)->isSeparator() ? _separatorHeight : _itemHeight;
}
setSelected((selected >= 0 && selected < _actions.size() && _actions.at(selected)->isEnabled() && !_actions.at(selected)->isSeparator()) ? selected : -1);
}
void PopupMenu::itemPressed(PressSource source) {
if (source == PressSourceMouse && !_mouseSelection) {
return;
}
if (_selected >= 0 && _selected < _actions.size() && _actions[_selected]->isEnabled()) {
if (_menus.at(_selected)) {
if (_childMenuIndex == _selected) {
_menus.at(_childMenuIndex)->hideMenu(true);
} else {
popupChildMenu(source);
}
} else {
hideMenu();
_triggering = true;
emit _actions[_selected]->trigger();
_triggering = false;
if (_deleteLater) {
_deleteLater = false;
deleteLater();
}
}
}
}
void PopupMenu::popupChildMenu(PressSource source) {
if (_childMenuIndex >= 0) {
_menus.at(_childMenuIndex)->hideMenu(true);
_childMenuIndex = -1;
}
if (_selected >= 0 && _selected < _menus.size() && _menus.at(_selected)) {
QPoint p(_inner.x() + (rtl() ? _padding.right() : _inner.width() - _padding.left()), _inner.y() + _st.skip + itemY(_selected));
_childMenuIndex = _selected;
_menus.at(_childMenuIndex)->showMenu(geometry().topLeft() + p, this, source);
}
}
void PopupMenu::keyPressEvent(QKeyEvent *e) {
if (_childMenuIndex >= 0) {
return _menus.at(_childMenuIndex)->keyPressEvent(e);
}
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
itemPressed(PressSourceKeyboard);
return;
} else if (e->key() == Qt::Key_Escape) {
hideMenu(_parent ? true : false);
return;
}
if (e->key() == (rtl() ? Qt::Key_Left : Qt::Key_Right)) {
if (_selected >= 0 && _menus.at(_selected)) {
itemPressed(PressSourceKeyboard);
return;
} else if (_selected < 0 && _parent && !_actions.isEmpty()) {
_mouseSelection = false;
setSelected(0);
}
} else if (e->key() == (rtl() ? Qt::Key_Right : Qt::Key_Left)) {
if (_parent) {
hideMenu(true);
}
}
if ((e->key() != Qt::Key_Up && e->key() != Qt::Key_Down) || _actions.size() < 1) return;
int32 delta = (e->key() == Qt::Key_Down ? 1 : -1), start = _selected;
if (start < 0 || start >= _actions.size()) {
start = (delta > 0) ? (_actions.size() - 1) : 0;
}
int32 newSelected = start;
do {
newSelected += delta;
if (newSelected < 0) {
newSelected += _actions.size();
} else if (newSelected >= _actions.size()) {
newSelected -= _actions.size();
}
} while (newSelected != start && (!_actions.at(newSelected)->isEnabled() || _actions.at(newSelected)->isSeparator()));
if (_actions.at(newSelected)->isEnabled() && !_actions.at(newSelected)->isSeparator()) {
_mouseSelection = false;
setSelected(newSelected);
}
}
void PopupMenu::enterEvent(QEvent *e) {
QPoint mouse = QCursor::pos();
if (!_inner.marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)).contains(mapFromGlobal(mouse))) {
if (_mouseSelection && _childMenuIndex < 0) {
_mouseSelection = false;
setSelected(-1);
}
}
return TWidget::enterEvent(e);
}
void PopupMenu::leaveEvent(QEvent *e) {
if (_mouseSelection && _childMenuIndex < 0) {
_mouseSelection = false;
setSelected(-1);
}
return TWidget::leaveEvent(e);
}
void PopupMenu::setSelected(int32 newSelected) {
if (newSelected >= _actions.size()) {
newSelected = -1;
}
if (newSelected != _selected) {
updateSelectedItem();
_selected = newSelected;
if (_mouseSelection) {
popupChildMenu(PressSourceMouse);
}
updateSelectedItem();
}
}
int32 PopupMenu::itemY(int32 index) {
if (index > _actions.size()) {
index = _actions.size();
}
int32 y = 0;
for (int32 i = 0; i < index; ++i) {
y += _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight;
}
return y;
}
void PopupMenu::updateSelectedItem() {
if (_selected >= 0) {
update(_padding.left(), _padding.top() + _st.skip + itemY(_selected), width() - _padding.left() - _padding.right(), _actions.at(_selected)->isSeparator() ? _separatorHeight : _itemHeight);
}
}
void PopupMenu::mouseMoveEvent(QMouseEvent *e) {
if (_inner.marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)).contains(mapFromGlobal(e->globalPos()))) {
_mouseSelection = true;
_mouse = e->globalPos();
updateSelected();
} else {
if (_mouseSelection && _childMenuIndex < 0) {
_mouseSelection = false;
setSelected(-1);
}
if (_parent) {
_parent->mouseMoveEvent(e);
}
}
}
void PopupMenu::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (_inner.contains(mapFromGlobal(e->globalPos()))) {
itemPressed(PressSourceMouse);
return;
}
if (_parent) {
_parent->mousePressEvent(e);
} else {
hideMenu();
}
}
void PopupMenu::focusOutEvent(QFocusEvent *e) {
hideMenu();
}
void PopupMenu::hideEvent(QHideEvent *e) {
if (_deleteOnHide) {
if (_triggering) {
_deleteLater = true;
} else {
deleteLater();
}
}
}
void PopupMenu::hideMenu(bool fast) {
if (isHidden()) return;
if (_parent && !_a_hide.animating()) {
_parent->childHiding(this);
}
if (fast) {
if (_a_hide.animating()) {
_a_hide.stop();
}
a_opacity = anim::fvalue(0, 0);
hideFinish();
} else {
if (!_a_hide.animating()) {
_cache = myGrab(this);
a_opacity.start(0);
_a_hide.start();
}
if (_parent) {
_parent->hideMenu();
}
}
if (_childMenuIndex >= 0) {
_menus.at(_childMenuIndex)->hideMenu(fast);
}
}
void PopupMenu::childHiding(PopupMenu *child) {
if (_childMenuIndex >= 0 && _menus.at(_childMenuIndex) == child) {
_childMenuIndex = -1;
}
}
void PopupMenu::hideFinish() {
hide();
}
void PopupMenu::step_hide(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
if (dt >= 1) {
_a_hide.stop();
a_opacity.finish();
hideFinish();
} else {
a_opacity.update(dt, anim::linear);
}
if (timer) update();
}
void PopupMenu::deleteOnHide(bool del) {
_deleteOnHide = del;
}
void PopupMenu::popup(const QPoint &p) {
showMenu(p, 0, PressSourceMouse);
}
void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, PressSource source) {
_parent = parent;
QPoint w = p - QPoint(0, _padding.top());
QRect 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);
_mouseSelection = (source == PressSourceMouse);
setSelected((source == PressSourceMouse || _actions.isEmpty()) ? -1 : 0);
psUpdateOverlayed(this);
show();
psShowOverAll(this);
windowHandle()->requestActivate();
activateWindow();
if (_a_hide.animating()) {
_a_hide.stop();
_cache = QPixmap();
}
a_opacity = anim::fvalue(1, 1);
}
PopupMenu::~PopupMenu() {
clearActions(true);
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
if (auto w = App::wnd()) {
w->onReActivate();
QTimer::singleShot(200, w, SLOT(onReActivate()));
}
#endif
}
PopupTooltip *PopupTooltipInstance = 0;
AbstractTooltipShower::~AbstractTooltipShower() {
if (PopupTooltipInstance && PopupTooltipInstance->_shower == this) {
PopupTooltipInstance->_shower = 0;
}
}
PopupTooltip::PopupTooltip() : TWidget(0)
, _shower(0)
, _st(0) {
PopupTooltipInstance = this;
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_NoSystemBackground, true);
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow()));
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
void PopupTooltip::onShow() {
if (_shower) {
QString text = (App::wnd() && App::wnd()->isActive(false)) ? _shower->tooltipText() : QString();
if (text.isEmpty()) {
Hide();
} else {
PopupTooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt());
}
}
}
void PopupTooltip::onWndActiveChanged() {
if (!App::wnd() || !App::wnd()->windowHandle() || !App::wnd()->windowHandle()->isActive()) {
PopupTooltip::Hide();
}
}
bool PopupTooltip::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::Leave) {
_hideByLeaveTimer.start(10);
} else if (e->type() == QEvent::Enter) {
_hideByLeaveTimer.stop();
} else if (e->type() == QEvent::MouseMove) {
if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) {
Hide();
}
}
return TWidget::eventFilter(o, e);
}
void PopupTooltip::onHideByLeave() {
Hide();
}
PopupTooltip::~PopupTooltip() {
if (PopupTooltipInstance == this) {
PopupTooltipInstance = 0;
}
}
void PopupTooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) {
if (!_hideByLeaveTimer.isSingleShot()) {
_hideByLeaveTimer.setSingleShot(true);
connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));
Sandbox::installEventFilter(this);
}
_point = m;
_st = st;
_text = Text(_st->textFont, text, _textPlainOptions, _st->widthMax, true);
int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right();
int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom();
// count tooltip size
QSize s(addw + _text.maxWidth(), addh + _text.minHeight());
if (s.width() > _st->widthMax) {
s.setWidth(addw + _text.countWidth(_st->widthMax - addw));
s.setHeight(addh + _text.countHeight(s.width() - addw));
}
int32 maxh = addh + (_st->linesMax * _st->textFont->height);
if (s.height() > maxh) {
s.setHeight(maxh);
}
// count tooltip position
QPoint p(m + _st->shift);
if (rtl()) {
p.setX(m.x() - s.width() - _st->shift.x());
}
if (s.width() < 2 * _st->shift.x()) {
p.setX(m.x() - (s.width() / 2));
}
// adjust tooltip position
QRect r(QApplication::desktop()->screenGeometry(m));
if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) {
p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width()));
}
if (r.x() + _st->skip > p.x() && p.x() < m.x()) {
p.setX(qMin(m.x(), r.x() + int32(_st->skip)));
}
if (r.y() + r.height() - _st->skip < p.y() + s.height()) {
p.setY(m.y() - s.height() - _st->skip);
}
if (r.y() > p.x()) {
p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height()));
}
setGeometry(QRect(p, s));
_hideByLeaveTimer.stop();
show();
}
void PopupTooltip::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(rect(), _st->textBg);
p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textFont->height);
p.setPen(_st->textFg);
_text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines);
}
void PopupTooltip::hideEvent(QHideEvent *e) {
if (PopupTooltipInstance == this) {
Hide();
}
}
void PopupTooltip::Show(int32 delay, const AbstractTooltipShower *shower) {
if (!PopupTooltipInstance) {
new PopupTooltip();
}
PopupTooltipInstance->_shower = shower;
if (delay >= 0) {
PopupTooltipInstance->_showTimer.start(delay);
} else {
PopupTooltipInstance->onShow();
}
}
void PopupTooltip::Hide() {
if (PopupTooltip *instance = PopupTooltipInstance) {
PopupTooltipInstance = 0;
instance->_showTimer.stop();
instance->_hideByLeaveTimer.stop();
instance->hide();
instance->deleteLater();
}
}

View file

@ -1,158 +0,0 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/text/text.h"
#include "ui/effects/rect_shadow.h"
class PopupMenu : public TWidget {
Q_OBJECT
public:
PopupMenu(const style::PopupMenu &st = st::defaultPopupMenu);
PopupMenu(QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu);
QAction *addAction(const QString &text, const QObject *receiver, const char* member);
QAction *addAction(QAction *a);
QAction *addSeparator();
void resetActions();
typedef QVector<QAction*> Actions;
Actions &actions();
void deleteOnHide(bool del);
void popup(const QPoint &p);
void hideMenu(bool fast = false);
~PopupMenu();
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void leaveEvent(QEvent *e) override;
void enterEvent(QEvent *e) override;
void focusOutEvent(QFocusEvent *e) override;
void hideEvent(QHideEvent *e) override;
private slots:
void actionChanged();
private:
void updateSelected();
void childHiding(PopupMenu *child);
void step_hide(float64 ms, bool timer);
void init();
void hideFinish();
enum PressSource {
PressSourceMouse,
PressSourceKeyboard,
};
void clearActions(bool force = false);
int32 processAction(QAction *a, int32 index, int32 w);
void setSelected(int32 selected);
int32 itemY(int32 index);
void updateSelectedItem();
void itemPressed(PressSource source);
void popupChildMenu(PressSource source);
void showMenu(const QPoint &p, PopupMenu *parent, PressSource source);;
const style::PopupMenu &_st;
typedef QVector<PopupMenu*> PopupMenus;
QMenu *_menu = nullptr;
Actions _actions;
PopupMenus _menus;
PopupMenu *_parent = nullptr;
QStringList _texts, _shortcutTexts;
int32 _itemHeight, _separatorHeight;
QRect _inner;
style::margins _padding;
QPoint _mouse;
bool _mouseSelection = false;
Ui::RectShadow _shadow;
int _selected = -1;
int _childMenuIndex = -1;
QPixmap _cache;
anim::fvalue a_opacity;
Animation _a_hide;
bool _deleteOnHide = true;
bool _triggering = false;
bool _deleteLater = false;
};
class AbstractTooltipShower {
public:
virtual QString tooltipText() const = 0;
virtual QPoint tooltipPos() const = 0;
virtual const style::Tooltip *tooltipSt() const {
return &st::defaultTooltip;
}
virtual ~AbstractTooltipShower();
};
class PopupTooltip : public TWidget {
Q_OBJECT
public:
bool eventFilter(QObject *o, QEvent *e);
static void Show(int32 delay, const AbstractTooltipShower *shower);
static void Hide();
~PopupTooltip();
public slots:
void onShow();
void onWndActiveChanged();
void onHideByLeave();
protected:
void paintEvent(QPaintEvent *e);
void hideEvent(QHideEvent *e);
private:
PopupTooltip();
void popup(const QPoint &p, const QString &text, const style::Tooltip *st);
friend class AbstractTooltipShower;
const AbstractTooltipShower *_shower;
QTimer _showTimer;
Text _text;
QPoint _point;
const style::Tooltip *_st;
QTimer _hideByLeaveTimer;
};

View file

@ -273,19 +273,3 @@ public:
}
void paintEvent(QPaintEvent *e);
};
class ScrolledWidget : public TWidget {
Q_OBJECT
public:
ScrolledWidget(QWidget *parent = nullptr) : TWidget(parent) {
}
// Updates the area that is visible inside the scroll container.
virtual void setVisibleTopBottom(int visibleTop, int visibleBottom) {
}
signals:
void heightUpdated();
};

View file

@ -228,6 +228,14 @@ public:
}
}
// Updates the area that is visible inside the scroll container.
virtual void setVisibleTopBottom(int visibleTop, int visibleBottom) {
}
signals:
// Child widget is responsible for emitting this signal.
void heightUpdated();
protected:
void enterEventHook(QEvent *e) {
return QWidget::enterEvent(e);

View file

@ -0,0 +1,270 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/dropdown_menu.h"
#include "application.h"
#include "lang.h"
namespace Ui {
DropdownMenu::DropdownMenu(QWidget *parent, const style::DropdownMenu &st) : InnerDropdown(parent, st.wrap)
, _st(st)
, _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(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() {
connect(this, SIGNAL(beforeHidden()), this, SLOT(onHidden()));
setOwnedWidget(_menu);
_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); });
setMouseTracking(true);
hide();
}
QAction *DropdownMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) {
return _menu->addAction(text, receiver, member, icon);
}
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::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();
}
// 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

View file

@ -0,0 +1,110 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "styles/style_widgets.h"
#include "ui/effects/rect_shadow.h"
#include "ui/widgets/inner_dropdown.h"
#include "ui/widgets/menu.h"
namespace Ui {
class DropdownMenu : public InnerDropdown {
Q_OBJECT
public:
DropdownMenu(QWidget *parent, const style::DropdownMenu &st = st::defaultDropdownMenu);
QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr);
QAction *addSeparator();
void clearActions();
using Actions = Ui::Menu::Actions;
Actions &actions();
~DropdownMenu();
protected:
void focusOutEvent(QFocusEvent *e) override;
void hideEvent(QHideEvent *e) override;
void keyPressEvent(QKeyEvent *e) override {
forwardKeyPress(e->key());
}
void mouseMoveEvent(QMouseEvent *e) override {
forwardMouseMove(e->globalPos());
}
void mousePressEvent(QMouseEvent *e) override {
forwardMousePress(e->globalPos());
}
private slots:
void onHidden() {
hideFinish();
}
private:
// Not ready with submenus yet.
DropdownMenu(QWidget *parent, QMenu *menu, const style::DropdownMenu &st = st::defaultDropdownMenu);
void deleteOnHide(bool del);
void popup(const QPoint &p);
void hideMenu(bool fast = false);
void childHiding(DropdownMenu *child);
void init();
void hideFinish();
using TriggeredSource = Ui::Menu::TriggeredSource;
void handleActivated(QAction *action, int actionTop, TriggeredSource source);
void handleTriggered(QAction *action, int actionTop, TriggeredSource source);
void forwardKeyPress(int key);
bool handleKeyPress(int key);
void forwardMouseMove(QPoint globalPosition) {
_menu->handleMouseMove(globalPosition);
}
void handleMouseMove(QPoint globalPosition);
void forwardMousePress(QPoint globalPosition) {
_menu->handleMousePress(globalPosition);
}
void handleMousePress(QPoint globalPosition);
using SubmenuPointer = QPointer<DropdownMenu>;
bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source);
void popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source);
void showMenu(const QPoint &p, DropdownMenu *parent, TriggeredSource source);
const style::DropdownMenu &_st;
ChildWidget<Ui::Menu> _menu;
// Not ready with submenus yet.
//using Submenus = QMap<QAction*, SubmenuPointer>;
//Submenus _submenus;
DropdownMenu *_parent = nullptr;
SubmenuPointer _activeSubmenu;
bool _deleteOnHide = false;
bool _triggering = false;
bool _deleteLater = false;
};
} // namespace Ui

View file

@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/inner_dropdown.h"
#include "ui/widgets/inner_dropdown.h"
#include "mainwindow.h"
#include "ui/scrollarea.h"
@ -27,12 +27,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
InnerDropdown::InnerDropdown(QWidget *parent, const style::InnerDropdown &st, const style::flatScroll &scrollSt) : TWidget(parent)
InnerDropdown::InnerDropdown(QWidget *parent, const style::InnerDropdown &st) : TWidget(parent)
, _st(st)
, _shadow(_st.shadow)
, _scroll(this, scrollSt) {
, _scroll(this, _st.scroll) {
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart()));
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideAnimated()));
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
@ -43,9 +43,9 @@ InnerDropdown::InnerDropdown(QWidget *parent, const style::InnerDropdown &st, co
hide();
}
void InnerDropdown::setOwnedWidget(ScrolledWidget *widget) {
auto container = new internal::Container(_scroll, widget, _st);
connect(container, SIGNAL(heightUpdated()), this, SLOT(onWidgetHeightUpdated()));
void InnerDropdown::setOwnedWidget(TWidget *widget) {
auto container = new Container(_scroll, widget, _st);
connect(widget, SIGNAL(heightUpdated()), this, SLOT(onWidgetHeightUpdated()));
_scroll->setOwnedWidget(container);
container->resizeToWidth(_scroll->width());
container->moveToLeft(0, 0);
@ -55,23 +55,22 @@ void InnerDropdown::setOwnedWidget(ScrolledWidget *widget) {
void InnerDropdown::setMaxHeight(int newMaxHeight) {
_maxHeight = newMaxHeight;
updateHeight();
resizeToContent();
}
void InnerDropdown::onWidgetHeightUpdated() {
updateHeight();
}
void InnerDropdown::updateHeight() {
int newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom();
if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
void InnerDropdown::resizeToContent() {
auto newWidth = _st.padding.left() + _st.scrollMargin.left() + _st.scrollMargin.right() + _st.padding.right();
auto newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom();
if (auto widget = static_cast<Container*>(_scroll->widget())) {
widget->resizeToContent();
newWidth += widget->width();
newHeight += widget->height();
}
if (_maxHeight > 0) {
accumulate_min(newHeight, _maxHeight);
}
if (newHeight != height()) {
resize(width(), newHeight);
if (newWidth != width() || newHeight != height()) {
resize(newWidth, newHeight);
}
}
@ -83,14 +82,14 @@ void InnerDropdown::onWindowActiveChanged() {
void InnerDropdown::resizeEvent(QResizeEvent *e) {
_scroll->setGeometry(rect().marginsRemoved(_st.padding).marginsRemoved(_st.scrollMargin));
if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
if (auto widget = static_cast<TWidget*>(_scroll->widget())) {
widget->resizeToWidth(_scroll->width());
onScroll();
}
}
void InnerDropdown::onScroll() {
if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
if (auto widget = static_cast<TWidget*>(_scroll->widget())) {
int visibleTop = _scroll->scrollTop();
int visibleBottom = visibleTop + _scroll->height();
widget->setVisibleTopBottom(visibleTop, visibleBottom);
@ -103,9 +102,9 @@ void InnerDropdown::paintEvent(QPaintEvent *e) {
if (!_cache.isNull()) {
bool animating = _a_appearance.animating(getms());
if (animating) {
p.setOpacity(_a_appearance.current(_hiding));
} else if (_hiding) {
hidingFinished();
p.setOpacity(_a_appearance.current(_hiding ? 0. : 1.));
} else if (_hiding || isHidden()) {
hideFinished();
return;
}
p.drawPixmap(0, 0, _cache);
@ -117,20 +116,19 @@ void InnerDropdown::paintEvent(QPaintEvent *e) {
}
// draw shadow
QRect shadowedRect = rect().marginsRemoved(_st.padding);
auto shadowedRect = rect().marginsRemoved(_st.padding);
_shadow.paint(p, shadowedRect, _st.shadowShift);
p.fillRect(shadowedRect, st::windowBg);
}
void InnerDropdown::enterEvent(QEvent *e) {
_hideTimer.stop();
if (_hiding) showingStarted();
showAnimated();
return TWidget::enterEvent(e);
}
void InnerDropdown::leaveEvent(QEvent *e) {
if (_a_appearance.animating(getms())) {
onHideStart();
hideAnimated();
} else {
_hideTimer.start(300);
}
@ -138,25 +136,43 @@ void InnerDropdown::leaveEvent(QEvent *e) {
}
void InnerDropdown::otherEnter() {
_hideTimer.stop();
showingStarted();
showAnimated();
}
void InnerDropdown::otherLeave() {
if (_a_appearance.animating(getms())) {
onHideStart();
hideAnimated();
} else {
_hideTimer.start(0);
}
}
void InnerDropdown::onHideStart() {
void InnerDropdown::showAnimated() {
_hideTimer.stop();
showStarted();
}
void InnerDropdown::hideAnimated(HideOption option) {
if (isHidden()) return;
if (option == HideOption::IgnoreShow) {
_ignoreShowEvents = true;
}
if (_hiding) return;
_hideTimer.stop();
_hiding = true;
startAnimation();
}
void InnerDropdown::hideFast() {
if (isHidden()) return;
_hideTimer.stop();
_hiding = false;
_a_appearance.finish();
hideFinished();
}
void InnerDropdown::startAnimation() {
auto from = _hiding ? 1. : 0.;
auto to = _hiding ? 0. : 1.;
@ -168,13 +184,17 @@ void InnerDropdown::startAnimation() {
_a_appearance.start([this] { repaintCallback(); }, from, to, _st.duration);
}
void InnerDropdown::hidingFinished() {
hide();
// showChildren();
emit hidden();
void InnerDropdown::hideFinished() {
_cache = QPixmap();
_ignoreShowEvents = false;
if (!isHidden()) {
emit beforeHidden();
hide();
}
}
void InnerDropdown::showingStarted() {
void InnerDropdown::showStarted() {
if (_ignoreShowEvents) return;
if (isHidden()) {
show();
} else if (!_hiding) {
@ -188,7 +208,7 @@ void InnerDropdown::repaintCallback() {
update();
if (!_a_appearance.animating() && _hiding) {
_hiding = false;
hidingFinished();
hideFinished();
}
}
@ -207,35 +227,45 @@ bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) {
return false;
}
namespace internal {
Container::Container(QWidget *parent, ScrolledWidget *child, const style::InnerDropdown &st) : ScrolledWidget(parent), _st(st) {
child->setParent(this);
child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top());
connect(child, SIGNAL(heightUpdated()), this, SLOT(onHeightUpdate()));
int InnerDropdown::resizeGetHeight(int newWidth) {
auto newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom();
if (auto widget = static_cast<TWidget*>(_scroll->widget())) {
widget->resizeToWidth(newWidth - _st.padding.left() - _st.padding.right() - _st.scrollMargin.left() - _st.scrollMargin.right());
newHeight += widget->height();
}
if (_maxHeight > 0) {
accumulate_min(newHeight, _maxHeight);
}
return newHeight;
}
void Container::setVisibleTopBottom(int visibleTop, int visibleBottom) {
if (auto child = static_cast<ScrolledWidget*>(children().front())) {
InnerDropdown::Container::Container(QWidget *parent, TWidget *child, const style::InnerDropdown &st) : TWidget(parent), _st(st) {
child->setParent(this);
child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top());
}
void InnerDropdown::Container::setVisibleTopBottom(int visibleTop, int visibleBottom) {
if (auto child = static_cast<TWidget*>(children().front())) {
child->setVisibleTopBottom(visibleTop - _st.scrollPadding.top(), visibleBottom - _st.scrollPadding.top());
}
}
void Container::onHeightUpdate() {
int newHeight = _st.scrollPadding.top() + _st.scrollPadding.bottom();
if (auto child = static_cast<ScrolledWidget*>(children().front())) {
void InnerDropdown::Container::resizeToContent() {
auto newWidth = _st.scrollPadding.top() + _st.scrollPadding.bottom();
auto newHeight = _st.scrollPadding.top() + _st.scrollPadding.bottom();
if (auto child = static_cast<TWidget*>(children().front())) {
newWidth += child->width();
newHeight += child->height();
}
if (newHeight != height()) {
resize(width(), newHeight);
emit heightUpdated();
if (newWidth != width() || newHeight != height()) {
resize(newWidth, newHeight);
}
}
int Container::resizeGetHeight(int newWidth) {
int InnerDropdown::Container::resizeGetHeight(int newWidth) {
int innerWidth = newWidth - _st.scrollPadding.left() - _st.scrollPadding.right();
int result = _st.scrollPadding.top() + _st.scrollPadding.bottom();
if (auto child = static_cast<ScrolledWidget*>(children().front())) {
if (auto child = static_cast<TWidget*>(children().front())) {
child->resizeToWidth(innerWidth);
child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top());
result += child->height();
@ -243,5 +273,4 @@ int Container::resizeGetHeight(int newWidth) {
return result;
}
} // namespace internal
} // namespace Ui

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "ui/effects/rect_shadow.h"
#include "styles/style_widgets.h"
class ScrollArea;
@ -30,9 +31,9 @@ class InnerDropdown : public TWidget {
Q_OBJECT
public:
InnerDropdown(QWidget *parent, const style::InnerDropdown &st = st::defaultInnerDropdown, const style::flatScroll &scrollSt = st::scrollDef);
InnerDropdown(QWidget *parent, const style::InnerDropdown &st = st::defaultInnerDropdown);
void setOwnedWidget(ScrolledWidget *widget);
void setOwnedWidget(TWidget *widget);
bool overlaps(const QRect &globalRect) {
if (isHidden() || _a_appearance.animating()) return false;
@ -41,32 +42,52 @@ public:
}
void setMaxHeight(int newMaxHeight);
void resizeToContent();
void otherEnter();
void otherLeave();
void showFast();
void hideFast();
bool isHiding() const {
return _hiding && _a_appearance.animating();
}
void showAnimated();
enum class HideOption {
Default,
IgnoreShow,
};
void hideAnimated(HideOption option = HideOption::Default);
signals:
void beforeHidden();
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
bool eventFilter(QObject *obj, QEvent *e) override;
signals:
void hidden();
int resizeGetHeight(int newWidth) override;
private slots:
void onHideStart();
void onHideAnimated() {
hideAnimated();
}
void onWindowActiveChanged();
void onScroll();
void onWidgetHeightUpdated();
void onWidgetHeightUpdated() {
resizeToContent();
}
private:
class Container;
void repaintCallback();
void hidingFinished();
void showingStarted();
void hideFinished();
void showStarted();
void startAnimation();
@ -80,6 +101,7 @@ private:
FloatAnimation _a_appearance;
QTimer _hideTimer;
bool _ignoreShowEvents = false;
RectShadow _shadow;
ChildWidget<ScrollArea> _scroll;
@ -88,17 +110,12 @@ private:
};
namespace internal {
class Container : public ScrolledWidget {
Q_OBJECT
class InnerDropdown::Container : public TWidget {
public:
Container(QWidget *parent, ScrolledWidget *child, const style::InnerDropdown &st);
Container(QWidget *parent, TWidget *child, const style::InnerDropdown &st);
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
private slots:
void onHeightUpdate();
void resizeToContent();
protected:
int resizeGetHeight(int newWidth) override;
@ -108,5 +125,4 @@ private:
};
} // namespace internal
} // namespace Ui

View file

@ -0,0 +1,343 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/menu.h"
namespace Ui {
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() {
resize(_st.widthMin, _st.skip * 2);
setMouseTracking(true);
setAttribute(Qt::WA_OpaquePaintEvent);
}
QAction *Menu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) {
auto action = new QAction(text, this);
connect(action, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection);
return addAction(action, icon);
}
QAction *Menu::addAction(QAction *action, const style::icon *icon) {
connect(action, SIGNAL(changed()), this, SLOT(actionChanged()));
_actions.push_back(action);
ActionData data;
data.icon = icon;
data.hasSubmenu = (action->menu() != nullptr);
_actionsData.push_back(data);
auto newWidth = qMax(width(), _st.widthMin);
newWidth = processAction(action, _actions.size() - 1, newWidth);
auto newHeight = height() + (action->isSeparator() ? _separatorHeight : _itemHeight);
resize(newWidth, newHeight);
if (_resizedCallback) {
_resizedCallback();
}
update();
return action;
}
QAction *Menu::addSeparator() {
auto separator = new QAction(this);
separator->setSeparator(true);
return addAction(separator);
}
void Menu::clearActions() {
_actionsData.clear();
for (auto action : base::take(_actions)) {
if (action->parent() == this) {
delete action;
}
}
resize(_st.widthMin, _st.skip * 2);
if (_resizedCallback) {
_resizedCallback();
}
}
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) {
goodw += _st.itemPadding.left() + _st.arrow.width();
} else if (!actionShortcut.isEmpty()) {
goodw += _st.itemPadding.left() + _st.itemFont->width(actionShortcut);
}
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;
}
void Menu::actionChanged() {
int newWidth = _st.widthMin;
for (int i = 0, count = _actions.size(); i != count; ++i) {
newWidth = processAction(_actions[i], i, newWidth);
}
if (newWidth != width()) {
resize(newWidth, height());
if (_resizedCallback) {
_resizedCallback();
}
}
update();
}
void Menu::paintEvent(QPaintEvent *e) {
Painter p(this);
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 {
auto enabled = action->isEnabled(), selected = (i == _selected && enabled);
p.fillRect(0, 0, width(), actionHeight, selected ? _st.itemBgOver : _st.itemBg);
if (data.icon) {
p.setOpacity(selected ? _st.itemIconOverOpacity : _st.itemIconOpacity);
data.icon->paint(p, _st.itemIconPosition, width());
p.setOpacity(1.);
}
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);
}
}
}
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()) {
if (_triggeredCallback) {
auto actionTop = _st.skip + itemTop(_selected);
_triggeredCallback(_actions[_selected], actionTop, source);
}
}
}
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();
}
} while (newSelected != start && (!_actions.at(newSelected)->isEnabled() || _actions.at(newSelected)->isSeparator()));
if (_actions.at(newSelected)->isEnabled() && !_actions.at(newSelected)->isSeparator()) {
_mouseSelection = false;
setSelected(newSelected);
}
}
void Menu::clearSelection() {
_mouseSelection = false;
setSelected(-1);
}
void Menu::clearMouseSelection() {
if (_mouseSelection && !_childShown) {
clearSelection();
}
}
void Menu::enterEvent(QEvent *e) {
QPoint mouse = QCursor::pos();
if (!rect().marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)).contains(mapFromGlobal(mouse))) {
clearMouseSelection();
}
return TWidget::enterEvent(e);
}
void Menu::leaveEvent(QEvent *e) {
clearMouseSelection();
return TWidget::leaveEvent(e);
}
void Menu::setSelected(int selected) {
if (selected >= _actions.size()) {
selected = -1;
}
if (_selected != selected) {
updateSelectedItem();
_selected = selected;
updateSelectedItem();
if (_activatedCallback) {
auto actionTop = _st.skip + itemTop(_selected);
auto source = _mouseSelection ? TriggeredSource::Mouse : TriggeredSource::Keyboard;
_activatedCallback((_selected >= 0) ? _actions[_selected] : nullptr, actionTop, source);
}
}
}
int Menu::itemTop(int index) {
if (index > _actions.size()) {
index = _actions.size();
}
int top = 0;
for (int i = 0; i < index; ++i) {
top += _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight;
}
return top;
}
void Menu::updateSelectedItem() {
if (_selected >= 0) {
update(0, _st.skip + itemTop(_selected), width(), _actions.at(_selected)->isSeparator() ? _separatorHeight : _itemHeight);
}
}
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());
}
void Menu::handleMousePress(QPoint globalPosition) {
handleMouseMove(globalPosition);
if (rect().contains(mapFromGlobal(globalPosition))) {
itemPressed(TriggeredSource::Mouse);
} else if (_mousePressDelegate) {
_mousePressDelegate(globalPosition);
}
}
} // namespace Ui

View file

@ -0,0 +1,134 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "styles/style_widgets.h"
namespace Ui {
class Menu : public TWidget {
Q_OBJECT
public:
Menu(QWidget *parent, const style::Menu &st = st::defaultMenu);
Menu(QWidget *parent, QMenu *menu, const style::Menu &st = st::defaultMenu);
QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr);
QAction *addSeparator();
void clearActions();
void clearSelection();
enum class TriggeredSource {
Mouse,
Keyboard,
};
void setChildShown(bool shown) {
_childShown = shown;
}
void setShowSource(TriggeredSource source);
using Actions = QList<QAction*>;
Actions &actions();
void setResizedCallback(base::lambda_unique<void()> callback) {
_resizedCallback = std_::move(callback);
}
void setActivatedCallback(base::lambda_unique<void(QAction *action, int actionTop, TriggeredSource source)> callback) {
_activatedCallback = std_::move(callback);
}
void setTriggeredCallback(base::lambda_unique<void(QAction *action, int actionTop, TriggeredSource source)> callback) {
_triggeredCallback = std_::move(callback);
}
void setKeyPressDelegate(base::lambda_unique<bool(int key)> delegate) {
_keyPressDelegate = std_::move(delegate);
}
void handleKeyPress(int key);
void setMouseMoveDelegate(base::lambda_unique<void(QPoint globalPosition)> delegate) {
_mouseMoveDelegate = std_::move(delegate);
}
void handleMouseMove(QPoint globalPosition);
void setMousePressDelegate(base::lambda_unique<void(QPoint globalPosition)> delegate) {
_mousePressDelegate = std_::move(delegate);
}
void handleMousePress(QPoint globalPosition);
protected:
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
private slots:
void actionChanged();
private:
void updateSelected(QPoint globalPosition);
void init();
// Returns the new width.
int processAction(QAction *action, int index, int width);
QAction *addAction(QAction *a, const style::icon *icon = nullptr);
void setSelected(int selected);
void clearMouseSelection();
int itemTop(int index);
void updateSelectedItem();
void itemPressed(TriggeredSource source);
const style::Menu &_st;
base::lambda_unique<void()> _resizedCallback;
base::lambda_unique<void(QAction *action, int actionTop, TriggeredSource source)> _activatedCallback;
base::lambda_unique<void(QAction *action, int actionTop, TriggeredSource source)> _triggeredCallback;
base::lambda_unique<bool(int key)> _keyPressDelegate;
base::lambda_unique<void(QPoint globalPosition)> _mouseMoveDelegate;
base::lambda_unique<void(QPoint globalPosition)> _mousePressDelegate;
struct ActionData {
bool hasSubmenu = false;
QString text;
QString shortcut;
const style::icon *icon = nullptr;
};
using ActionsData = QList<ActionData>;
QMenu *_wappedMenu = nullptr;
Actions _actions;
ActionsData _actionsData;
int _itemHeight, _separatorHeight;
bool _mouseSelection = false;
int _selected = -1;
bool _childShown = false;
};
} // namespace Ui

View file

@ -454,7 +454,7 @@ int MultiSelect::resizeGetHeight(int newWidth) {
return newHeight;
}
MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, const QString &placeholder, ScrollCallback callback) : ScrolledWidget(parent)
MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, const QString &placeholder, ScrollCallback callback) : TWidget(parent)
, _st(st)
, _scrollCallback(std_::move(callback))
, _field(this, _st.field, placeholder)

View file

@ -71,7 +71,7 @@ private:
};
// This class is hold in header because it requires Qt preprocessing.
class MultiSelect::Inner : public ScrolledWidget {
class MultiSelect::Inner : public TWidget {
Q_OBJECT
public:

View file

@ -0,0 +1,340 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/popup_menu.h"
#include "pspecific.h"
#include "application.h"
#include "lang.h"
namespace Ui {
PopupMenu::PopupMenu(const style::PopupMenu &st) : TWidget(nullptr)
, _st(st)
, _menu(this, _st.menu)
, _shadow(_st.shadow)
, a_opacity(1)
, _a_hide(animation(this, &PopupMenu::step_hide)) {
init();
}
PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(nullptr)
, _st(st)
, _menu(this, menu, _st.menu)
, _shadow(_st.shadow)
, a_opacity(1)
, _a_hide(animation(this, &PopupMenu::step_hide)) {
init();
for (auto action : actions()) {
if (auto submenu = action->menu()) {
auto it = _submenus.insert(action, new PopupMenu(submenu, st));
it.value()->deleteOnHide(false);
}
}
}
void PopupMenu::init() {
_padding = _shadow.getDimensions(_st.shadowShift);
_menu->setResizedCallback([this] { handleMenuResize(); });
_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->moveToLeft(_padding.left(), _padding.top());
handleMenuResize();
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::Popup | Qt::NoDropShadowWindowHint);
setMouseTracking(true);
hide();
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
}
void PopupMenu::handleMenuResize() {
resize(_padding.left() + _menu->width() + _padding.right(), _padding.top() + _menu->height() + _padding.bottom());
_inner = QRect(_padding.left(), _padding.top(), width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom());
}
QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) {
return _menu->addAction(text, receiver, member, icon);
}
QAction *PopupMenu::addSeparator() {
return _menu->addSeparator();
}
void PopupMenu::clearActions() {
for (auto submenu : base::take(_submenus)) {
delete submenu;
}
return _menu->clearActions();
}
PopupMenu::Actions &PopupMenu::actions() {
return _menu->actions();
}
void PopupMenu::paintEvent(QPaintEvent *e) {
Painter p(this);
auto clip = e->rect();
p.setClipRect(clip);
auto compositionMode = p.compositionMode();
p.setCompositionMode(QPainter::CompositionMode_Source);
if (_a_hide.animating()) {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
return;
}
p.fillRect(clip, st::almostTransparent);
p.setCompositionMode(compositionMode);
_shadow.paint(p, _inner, _st.shadowShift);
}
void PopupMenu::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 PopupMenu::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();
}
}
}
bool PopupMenu::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 PopupMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source) {
if (auto currentSubmenu = base::take(_activeSubmenu)) {
currentSubmenu->hideMenu(true);
}
if (submenu) {
QPoint p(_inner.x() + (rtl() ? _padding.right() : _inner.width() - _padding.left()), _inner.y() + actionTop);
_activeSubmenu = submenu;
_activeSubmenu->showMenu(geometry().topLeft() + p, this, source);
_menu->setChildShown(true);
} else {
_menu->setChildShown(false);
}
}
void PopupMenu::forwardKeyPress(int key) {
if (!handleKeyPress(key)) {
_menu->handleKeyPress(key);
}
}
bool PopupMenu::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 PopupMenu::handleMouseMove(QPoint globalPosition) {
if (_parent) {
_parent->forwardMouseMove(globalPosition);
}
}
void PopupMenu::handleMousePress(QPoint globalPosition) {
if (_parent) {
_parent->forwardMousePress(globalPosition);
} else {
hideMenu();
}
}
void PopupMenu::focusOutEvent(QFocusEvent *e) {
hideMenu();
}
void PopupMenu::hideEvent(QHideEvent *e) {
if (_deleteOnHide) {
if (_triggering) {
_deleteLater = true;
} else {
deleteLater();
}
}
}
void PopupMenu::hideMenu(bool fast) {
if (isHidden()) return;
if (_parent && !_a_hide.animating()) {
_parent->childHiding(this);
}
if (fast) {
if (_a_hide.animating()) {
_a_hide.stop();
}
a_opacity = anim::fvalue(0, 0);
hideFinish();
} else {
if (!_a_hide.animating()) {
_cache = myGrab(this);
a_opacity.start(0);
_a_hide.start();
}
if (_parent) {
_parent->hideMenu();
}
}
if (_activeSubmenu) {
_activeSubmenu->hideMenu(fast);
}
}
void PopupMenu::childHiding(PopupMenu *child) {
if (_activeSubmenu && _activeSubmenu == child) {
_activeSubmenu = SubmenuPointer();
}
}
void PopupMenu::hideFinish() {
hide();
}
void PopupMenu::step_hide(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
if (dt >= 1) {
_a_hide.stop();
a_opacity.finish();
hideFinish();
} else {
a_opacity.update(dt, anim::linear);
}
if (timer) update();
}
void PopupMenu::deleteOnHide(bool del) {
_deleteOnHide = del;
}
void PopupMenu::popup(const QPoint &p) {
showMenu(p, nullptr, TriggeredSource::Mouse);
}
void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source) {
_parent = parent;
QPoint w = p - QPoint(0, _padding.top());
QRect 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);
psUpdateOverlayed(this);
show();
psShowOverAll(this);
windowHandle()->requestActivate();
activateWindow();
if (_a_hide.animating()) {
_a_hide.stop();
_cache = QPixmap();
}
a_opacity = anim::fvalue(1, 1);
}
PopupMenu::~PopupMenu() {
for (auto submenu : base::take(_submenus)) {
delete submenu;
}
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
if (auto w = App::wnd()) {
w->onReActivate();
QTimer::singleShot(200, w, SLOT(onReActivate()));
}
#endif
}
} // namespace Ui

View file

@ -0,0 +1,112 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "styles/style_widgets.h"
#include "ui/effects/rect_shadow.h"
#include "ui/widgets/menu.h"
namespace Ui {
class PopupMenu : public TWidget {
public:
PopupMenu(const style::PopupMenu &st = st::defaultPopupMenu);
PopupMenu(QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu);
QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr);
QAction *addSeparator();
void clearActions();
using Actions = Ui::Menu::Actions;
Actions &actions();
void deleteOnHide(bool del);
void popup(const QPoint &p);
void hideMenu(bool fast = false);
~PopupMenu();
protected:
void paintEvent(QPaintEvent *e) override;
void focusOutEvent(QFocusEvent *e) override;
void hideEvent(QHideEvent *e) override;
void keyPressEvent(QKeyEvent *e) override {
forwardKeyPress(e->key());
}
void mouseMoveEvent(QMouseEvent *e) override {
forwardMouseMove(e->globalPos());
}
void mousePressEvent(QMouseEvent *e) override {
forwardMousePress(e->globalPos());
}
private:
void childHiding(PopupMenu *child);
void step_hide(float64 ms, bool timer);
void init();
void hideFinish();
using TriggeredSource = Ui::Menu::TriggeredSource;
void handleMenuResize();
void handleActivated(QAction *action, int actionTop, TriggeredSource source);
void handleTriggered(QAction *action, int actionTop, TriggeredSource source);
void forwardKeyPress(int key);
bool handleKeyPress(int key);
void forwardMouseMove(QPoint globalPosition) {
_menu->handleMouseMove(globalPosition);
}
void handleMouseMove(QPoint globalPosition);
void forwardMousePress(QPoint globalPosition) {
_menu->handleMousePress(globalPosition);
}
void handleMousePress(QPoint globalPosition);
using SubmenuPointer = QPointer<PopupMenu>;
bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source);
void popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source);
void showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source);
const style::PopupMenu &_st;
ChildWidget<Ui::Menu> _menu;
using Submenus = QMap<QAction*, SubmenuPointer>;
Submenus _submenus;
PopupMenu *_parent = nullptr;
QRect _inner;
style::margins _padding;
Ui::RectShadow _shadow;
SubmenuPointer _activeSubmenu;
QPixmap _cache;
anim::fvalue a_opacity;
Animation _a_hide;
bool _deleteOnHide = true;
bool _triggering = false;
bool _deleteLater = false;
};
} // namespace Ui

View file

@ -0,0 +1,185 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/tooltip.h"
#include "application.h"
namespace Ui {
Tooltip *TooltipInstance = nullptr;
AbstractTooltipShower::~AbstractTooltipShower() {
if (TooltipInstance && TooltipInstance->_shower == this) {
TooltipInstance->_shower = 0;
}
}
Tooltip::Tooltip() : TWidget(nullptr) {
TooltipInstance = this;
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_NoSystemBackground, true);
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow()));
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
void Tooltip::onShow() {
if (_shower) {
QString text = (App::wnd() && App::wnd()->isActive(false)) ? _shower->tooltipText() : QString();
if (text.isEmpty()) {
Hide();
} else {
TooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt());
}
}
}
void Tooltip::onWndActiveChanged() {
if (!App::wnd() || !App::wnd()->windowHandle() || !App::wnd()->windowHandle()->isActive()) {
Tooltip::Hide();
}
}
bool Tooltip::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::Leave) {
_hideByLeaveTimer.start(10);
} else if (e->type() == QEvent::Enter) {
_hideByLeaveTimer.stop();
} else if (e->type() == QEvent::MouseMove) {
if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) {
Hide();
}
}
return TWidget::eventFilter(o, e);
}
void Tooltip::onHideByLeave() {
Hide();
}
Tooltip::~Tooltip() {
if (TooltipInstance == this) {
TooltipInstance = 0;
}
}
void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) {
if (!_hideByLeaveTimer.isSingleShot()) {
_hideByLeaveTimer.setSingleShot(true);
connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));
Sandbox::installEventFilter(this);
}
_point = m;
_st = st;
_text = Text(_st->textFont, text, _textPlainOptions, _st->widthMax, true);
int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right();
int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom();
// count tooltip size
QSize s(addw + _text.maxWidth(), addh + _text.minHeight());
if (s.width() > _st->widthMax) {
s.setWidth(addw + _text.countWidth(_st->widthMax - addw));
s.setHeight(addh + _text.countHeight(s.width() - addw));
}
int32 maxh = addh + (_st->linesMax * _st->textFont->height);
if (s.height() > maxh) {
s.setHeight(maxh);
}
// count tooltip position
QPoint p(m + _st->shift);
if (rtl()) {
p.setX(m.x() - s.width() - _st->shift.x());
}
if (s.width() < 2 * _st->shift.x()) {
p.setX(m.x() - (s.width() / 2));
}
// adjust tooltip position
QRect r(QApplication::desktop()->screenGeometry(m));
if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) {
p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width()));
}
if (r.x() + _st->skip > p.x() && p.x() < m.x()) {
p.setX(qMin(m.x(), r.x() + int32(_st->skip)));
}
if (r.y() + r.height() - _st->skip < p.y() + s.height()) {
p.setY(m.y() - s.height() - _st->skip);
}
if (r.y() > p.x()) {
p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height()));
}
setGeometry(QRect(p, s));
_hideByLeaveTimer.stop();
show();
}
void Tooltip::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(rect(), _st->textBg);
p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textFont->height);
p.setPen(_st->textFg);
_text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines);
}
void Tooltip::hideEvent(QHideEvent *e) {
if (TooltipInstance == this) {
Hide();
}
}
void Tooltip::Show(int32 delay, const AbstractTooltipShower *shower) {
if (!TooltipInstance) {
new Tooltip();
}
TooltipInstance->_shower = shower;
if (delay >= 0) {
TooltipInstance->_showTimer.start(delay);
} else {
TooltipInstance->onShow();
}
}
void Tooltip::Hide() {
if (auto instance = TooltipInstance) {
TooltipInstance = nullptr;
instance->_showTimer.stop();
instance->_hideByLeaveTimer.stop();
instance->hide();
instance->deleteLater();
}
}
} // namespace Ui

View file

@ -0,0 +1,69 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Ui {
class AbstractTooltipShower {
public:
virtual QString tooltipText() const = 0;
virtual QPoint tooltipPos() const = 0;
virtual const style::Tooltip *tooltipSt() const {
return &st::defaultTooltip;
}
virtual ~AbstractTooltipShower();
};
class Tooltip : public TWidget {
Q_OBJECT
public:
static void Show(int32 delay, const AbstractTooltipShower *shower);
static void Hide();
private slots:
void onShow();
void onWndActiveChanged();
void onHideByLeave();
protected:
void paintEvent(QPaintEvent *e) override;
void hideEvent(QHideEvent *e) override;
bool eventFilter(QObject *o, QEvent *e) override;
private:
Tooltip();
~Tooltip();
void popup(const QPoint &p, const QString &text, const style::Tooltip *st);
friend class AbstractTooltipShower;
const AbstractTooltipShower *_shower = nullptr;
QTimer _showTimer;
Text _text;
QPoint _point;
const style::Tooltip *_st = nullptr;
QTimer _hideByLeaveTimer;
};
} // namespace Ui

Some files were not shown because too many files have changed in this diff Show more