From 06ed7b8eaf86f960efe776986015ef90686cc341 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 5 Dec 2016 14:01:08 +0300 Subject: [PATCH] Some more ripple animations. Now only anim::value (float64). Also each FloatAnimation now stops MTP responses. Also slide animations done by FloatAnimation. Closed beta 10019012. --- Telegram/Resources/langs/lang.strings | 12 +- Telegram/Resources/winrc/Telegram.rc | 8 +- Telegram/Resources/winrc/Updater.rc | 8 +- Telegram/SourceFiles/app.cpp | 2 +- Telegram/SourceFiles/application.cpp | 15 - Telegram/SourceFiles/application.h | 8 - Telegram/SourceFiles/boxes/addcontactbox.cpp | 2 +- Telegram/SourceFiles/boxes/addcontactbox.h | 2 +- Telegram/SourceFiles/boxes/boxes.style | 17 +- Telegram/SourceFiles/boxes/confirmbox.cpp | 2 +- Telegram/SourceFiles/boxes/confirmbox.h | 2 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 409 +++--- Telegram/SourceFiles/boxes/contactsbox.h | 24 +- Telegram/SourceFiles/boxes/members_box.cpp | 4 +- Telegram/SourceFiles/boxes/sessionsbox.cpp | 2 +- Telegram/SourceFiles/boxes/sharebox.cpp | 2 +- Telegram/SourceFiles/boxes/sharebox.h | 2 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 24 +- Telegram/SourceFiles/boxes/stickers_box.h | 4 +- Telegram/SourceFiles/core/stl_subset.h | 2 + Telegram/SourceFiles/core/version.h | 2 +- Telegram/SourceFiles/dialogs/dialogs.style | 4 + .../SourceFiles/dialogs/dialogs_layout.cpp | 45 +- Telegram/SourceFiles/dialogs/dialogs_layout.h | 6 +- Telegram/SourceFiles/dialogs/dialogs_list.cpp | 4 +- Telegram/SourceFiles/dialogs/dialogs_row.cpp | 28 + Telegram/SourceFiles/dialogs/dialogs_row.h | 23 +- Telegram/SourceFiles/dialogswidget.cpp | 1168 ++++++++++------- Telegram/SourceFiles/dialogswidget.h | 178 ++- .../history/field_autocomplete.cpp | 2 +- .../SourceFiles/history/field_autocomplete.h | 2 +- Telegram/SourceFiles/history/history.style | 9 +- .../SourceFiles/history/history_drag_area.cpp | 6 +- .../SourceFiles/history/history_drag_area.h | 4 +- Telegram/SourceFiles/history/history_item.cpp | 2 +- .../history/history_media_types.cpp | 2 +- .../SourceFiles/history/history_media_types.h | 4 +- .../SourceFiles/history/history_message.cpp | 2 +- Telegram/SourceFiles/historywidget.cpp | 188 ++- Telegram/SourceFiles/historywidget.h | 29 +- .../inline_bot_layout_internal.cpp | 2 +- .../inline_bots/inline_bot_layout_internal.h | 2 +- Telegram/SourceFiles/intro/intro.style | 4 +- Telegram/SourceFiles/intro/introwidget.cpp | 67 +- Telegram/SourceFiles/intro/introwidget.h | 10 +- Telegram/SourceFiles/layerwidget.cpp | 37 +- Telegram/SourceFiles/layerwidget.h | 6 +- Telegram/SourceFiles/mainwidget.cpp | 80 +- Telegram/SourceFiles/mainwidget.h | 12 +- Telegram/SourceFiles/mainwindow.cpp | 10 +- Telegram/SourceFiles/media/media_audio.cpp | 2 +- Telegram/SourceFiles/media/media_audio.h | 2 +- .../media/player/media_player.style | 50 +- .../media/player/media_player_button.cpp | 2 + .../media/player/media_player_cover.cpp | 8 +- .../media/player/media_player_widget.cpp | 32 +- .../media/view/media_clip_controller.cpp | 4 +- Telegram/SourceFiles/mediaview.cpp | 6 +- Telegram/SourceFiles/mediaview.h | 6 +- Telegram/SourceFiles/mtproto/facade.cpp | 30 +- Telegram/SourceFiles/mtproto/facade.h | 54 +- .../SourceFiles/overview/overview_layout.h | 2 +- Telegram/SourceFiles/overviewwidget.cpp | 85 +- Telegram/SourceFiles/overviewwidget.h | 8 +- Telegram/SourceFiles/passcodewidget.cpp | 63 +- Telegram/SourceFiles/passcodewidget.h | 11 +- .../platform/win/window_title_win.cpp | 4 +- Telegram/SourceFiles/profile/profile.style | 2 +- .../profile/profile_block_common_groups.cpp | 2 +- .../profile/profile_block_common_groups.h | 2 +- .../profile/profile_block_info.cpp | 17 +- .../SourceFiles/profile/profile_block_info.h | 3 +- .../profile/profile_block_peer_list.cpp | 103 +- .../profile/profile_block_peer_list.h | 14 +- .../SourceFiles/profile/profile_fixed_bar.cpp | 4 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 67 +- Telegram/SourceFiles/stickers/emoji_pan.h | 6 +- Telegram/SourceFiles/stickers/stickers.style | 6 + Telegram/SourceFiles/ui/abstract_button.cpp | 71 +- Telegram/SourceFiles/ui/abstract_button.h | 60 +- Telegram/SourceFiles/ui/animation.h | 59 +- .../ui/buttons/history_down_button.cpp | 24 +- .../ui/buttons/history_down_button.h | 9 +- .../ui/buttons/peer_avatar_button.cpp | 2 +- Telegram/SourceFiles/ui/countryinput.cpp | 113 +- Telegram/SourceFiles/ui/countryinput.h | 19 +- .../ui/effects/radial_animation.cpp | 24 +- .../SourceFiles/ui/effects/radial_animation.h | 3 +- .../ui/effects/ripple_animation.cpp | 4 +- .../ui/effects/widget_slide_wrap.cpp | 10 +- .../ui/effects/widget_slide_wrap.h | 2 +- Telegram/SourceFiles/ui/widgets/buttons.cpp | 63 +- Telegram/SourceFiles/ui/widgets/buttons.h | 18 +- Telegram/SourceFiles/ui/widgets/checkbox.cpp | 20 +- Telegram/SourceFiles/ui/widgets/checkbox.h | 4 +- .../ui/widgets/continuous_sliders.cpp | 4 +- .../ui/widgets/continuous_sliders.h | 2 +- .../SourceFiles/ui/widgets/input_fields.cpp | 221 +--- .../SourceFiles/ui/widgets/input_fields.h | 52 +- .../SourceFiles/ui/widgets/multi_select.cpp | 6 +- .../SourceFiles/ui/widgets/multi_select.h | 2 +- Telegram/SourceFiles/ui/widgets/scroll_area.h | 14 +- Telegram/SourceFiles/ui/widgets/widgets.style | 17 +- .../window/notifications_manager_default.cpp | 2 +- .../window/notifications_manager_default.h | 4 +- .../SourceFiles/window/player_wrap_widget.h | 2 +- .../SourceFiles/window/section_widget.cpp | 2 - Telegram/SourceFiles/window/window.style | 6 + Telegram/build/version | 2 +- 109 files changed, 2129 insertions(+), 1796 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 85ce5990a..18968c74c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -475,17 +475,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_profile_shared_media" = "Shared media"; "lng_profile_no_media" = "No media in this conversation."; "lng_profile_photos" = "{count:_not_used_|# photo|# photos}"; -"lng_profile_photos_header" = "Photos overview"; +"lng_profile_photos_header" = "Photos"; "lng_profile_videos" = "{count:_not_used_|# video|# videos}"; -"lng_profile_videos_header" = "Videos overview"; +"lng_profile_videos_header" = "Videos"; "lng_profile_songs" = "{count:_not_used_|# audio file|# audio files}"; -"lng_profile_songs_header" = "Audio files overview"; +"lng_profile_songs_header" = "Audio files"; "lng_profile_files" = "{count:_not_used_|# file|# files}"; -"lng_profile_files_header" = "Files overview"; +"lng_profile_files_header" = "Files"; "lng_profile_audios" = "{count:_not_used_|# voice message|# voice messages}"; -"lng_profile_audios_header" = "Voice messages overview"; +"lng_profile_audios_header" = "Voice messages"; "lng_profile_shared_links" = "{count:_not_used_|# shared link|# shared links}"; -"lng_profile_shared_links_header" = "Shared links overview"; +"lng_profile_shared_links_header" = "Shared links"; "lng_profile_copy_phone" = "Copy Phone Number"; "lng_profile_copy_fullname" = "Copy Name"; "lng_profile_drop_area_title" = "Drop your image here"; diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index c1afe54b0..7f8a306fb 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,11 - PRODUCTVERSION 0,10,19,11 + FILEVERSION 0,10,19,12 + PRODUCTVERSION 0,10,19,12 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.19.11" + VALUE "FileVersion", "0.10.19.12" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.11" + VALUE "ProductVersion", "0.10.19.12" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 5ccb3c6cb..de7a9f48a 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,11 - PRODUCTVERSION 0,10,19,11 + FILEVERSION 0,10,19,12 + PRODUCTVERSION 0,10,19,12 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.19.11" + VALUE "FileVersion", "0.10.19.12" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.11" + VALUE "ProductVersion", "0.10.19.12" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index b7d2aa87c..8937b9e54 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1246,7 +1246,7 @@ namespace { if (auto history = App::historyLoaded(peer)) { history->outboxRead(upTo); if (history->lastMsg && history->lastMsg->out() && history->lastMsg->id <= upTo) { - if (App::main()) App::main()->dlgUpdated(history, history->lastMsg->id); + if (App::main()) App::main()->dlgUpdated(history->peer, history->lastMsg->id); } history->updateChatListEntry(); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 11ad708df..401ffc65e 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -711,8 +711,6 @@ AppClass::AppClass() : QObject() { cChangeTimeFormat(QLocale::system().timeFormat(QLocale::ShortFormat)); - connect(&_mtpUnpauseTimer, SIGNAL(timeout()), this, SLOT(doMtpUnpause())); - connect(&killDownloadSessionsTimer, SIGNAL(timeout()), this, SLOT(killDownloadSessions())); DEBUG_LOG(("Application Info: starting app...")); @@ -832,19 +830,6 @@ void AppClass::cancelPhotoUpdate(const PeerId &peer) { } } -void AppClass::mtpPause() { - MTP::pause(); - _mtpUnpauseTimer.start(st::slideDuration * 2); -} - -void AppClass::mtpUnpause() { - _mtpUnpauseTimer.start(1); -} - -void AppClass::doMtpUnpause() { - MTP::unpause(); -} - void AppClass::selfPhotoCleared(const MTPUserProfilePhoto &result) { if (!App::self()) return; App::self()->setPhoto(result); diff --git a/Telegram/SourceFiles/application.h b/Telegram/SourceFiles/application.h index f838eae9b..6dd3b6e5c 100644 --- a/Telegram/SourceFiles/application.h +++ b/Telegram/SourceFiles/application.h @@ -159,9 +159,6 @@ public: bool isPhotoUpdating(const PeerId &peer); void cancelPhotoUpdate(const PeerId &peer); - void mtpPause(); - void mtpUnpause(); - void selfPhotoCleared(const MTPUserProfilePhoto &result); void chatPhotoCleared(PeerId peer, const MTPUpdates &updates); void selfPhotoDone(const MTPphotos_Photo &result); @@ -185,9 +182,6 @@ signals: void adjustSingleTimers(); public slots: - - void doMtpUnpause(); - void photoUpdated(const FullMsgId &msgId, bool silent, const MTPInputFile &file); void onSwitchDebugMode(); @@ -217,6 +211,4 @@ private: FileUploader *_uploader = nullptr; Translator *_translator = nullptr; - SingleTimer _mtpUnpauseTimer; - }; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 5d7f74bcf..26775ab7e 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -557,7 +557,7 @@ void SetupChannelBox::mousePressEvent(QMouseEvent *e) { if (_linkOver) { Application::clipboard()->setText(_channel->inviteLink()); _goodTextLink = lang(lng_create_channel_link_copied); - a_goodOpacity = anim::fvalue(1, 0); + a_goodOpacity = anim::value(1, 0); _a_goodFade.start(); } } diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index ad66f4369..4129218e2 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -190,7 +190,7 @@ private: QString _sentUsername, _checkUsername, _errorText, _goodText; QString _goodTextLink; - anim::fvalue a_goodOpacity; + anim::value a_goodOpacity; Animation _a_goodFade; QTimer _checkTimer; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index bef2e0784..b38e20780 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -332,6 +332,7 @@ contactsPhotoCheckbox: RoundImageCheckbox { } contactsPhotoDisabledCheckFg: #bbbbbb; contactsNameCheckedFg: #2b88b8; +contactsRipple: defaultRippleAnimation; localStorageBoxSkip: 10px; @@ -381,15 +382,21 @@ sessionWhenFont: msgDateFont; sessionWhenFg: #aaaaaa; sessionInfoFont: msgFont; sessionInfoFg: #888888; -sessionTerminateTop: 30px; -sessionTerminateSkip: 18px; +sessionTerminateTop: 28px; +sessionTerminateSkip: 22px; sessionTerminate: IconButton { - width: 16px; - height: 16px; + width: 20px; + height: 20px; icon: simpleCloseIcon; iconOver: simpleCloseIconOver; - iconPosition: point(3px, 3px); + iconPosition: point(5px, 5px); + + rippleAreaPosition: point(0px, 0px); + rippleAreaSize: 20px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } } sessionTerminateAllButton: LinkButton(boxLinkButton) { color: #d15948; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 464b0073a..2fd812469 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -226,7 +226,7 @@ void MaxInviteBox::mousePressEvent(QMouseEvent *e) { if (_linkOver) { Application::clipboard()->setText(_link); _goodTextLink = lang(lng_create_channel_link_copied); - a_goodOpacity = anim::fvalue(1, 0); + a_goodOpacity = anim::value(1, 0); _a_good.start(); } } diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index b0a7fc8cf..5e42b5a94 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -178,7 +178,7 @@ private: QPoint _lastMousePos; QString _goodTextLink; - anim::fvalue a_goodOpacity; + anim::value a_goodOpacity; Animation _a_good; }; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 9ac1c37f5..9d2c00e39 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -37,6 +37,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/multi_select.h" #include "ui/widgets/scroll_area.h" #include "ui/effects/widget_slide_wrap.h" +#include "ui/effects/ripple_animation.h" #include "boxes/photocropbox.h" #include "boxes/confirmbox.h" #include "observer_peer.h" @@ -546,10 +547,14 @@ bool ContactsBox::creationFail(const RPCError &error) { return false; } +ContactsBox::Inner::ContactData::ContactData() = default; + ContactsBox::Inner::ContactData::ContactData(PeerData *peer, const base::lambda_copy &updateCallback) : checkbox(std_::make_unique(st::contactsPhotoCheckbox, updateCallback, PaintUserpicCallback(peer))) { } +ContactsBox::Inner::ContactData::~ContactData() = default; + ContactsBox::Inner::Inner(QWidget *parent, CreatingGroupType creating) : TWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _creating(creating) @@ -924,16 +929,16 @@ ContactsBox::Inner::ContactData *ContactsBox::Inner::contactData(Dialogs::Row *r return data; } -void ContactsBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, ContactData *data, bool sel) { - UserData *user = peer->asUser(); +void ContactsBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, ContactData *data, bool selected) { + auto user = peer->asUser(); if (_chat && _membersFilter == MembersFilter::Admins) { if (_allAdmins->checked() || peer->id == peerFromUser(_chat->creator) || _saving) { - sel = false; + selected = false; } } else { if (data->disabledChecked || selectedCount() >= Global::MegagroupSizeMax()) { - sel = false; + selected = false; } } @@ -945,7 +950,13 @@ void ContactsBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, Cont } auto checkedRatio = 0.; - p.fillRect(0, 0, width(), _rowHeight, sel ? st::contactsBgOver : st::contactsBg); + p.fillRect(0, 0, width(), _rowHeight, selected ? st::contactsBgOver : st::contactsBg); + if (data->ripple) { + data->ripple->paint(p, 0, 0, width(), ms); + if (data->ripple->empty()) { + data->ripple.reset(); + } + } if (paintDisabledCheck) { paintDisabledCheckUserpic(p, peer, st::contactsPadding.left(), st::contactsPadding.top(), width()); } else if (usingMultiSelect()) { @@ -979,14 +990,14 @@ void ContactsBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, Cont int32 secondw = st::contactsStatusFont->width(second); p.setPen(st::contactsStatusFgOnline); p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width() - secondw, first); - p.setPen(sel ? st::contactsStatusFgOver : st::contactsStatusFg); + p.setPen(selected ? st::contactsStatusFgOver : st::contactsStatusFg); p.drawTextLeft(namex + w, st::contactsPadding.top() + st::contactsStatusTop, width() + w, second); } } else { if ((user && (uname || data->statusHasOnlineColor)) || (peer->isChannel() && uname)) { p.setPen(st::contactsStatusFgOnline); } else { - p.setPen(sel ? st::contactsStatusFgOver : st::contactsStatusFg); + p.setPen(selected ? st::contactsStatusFgOver : st::contactsStatusFg); } p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), data->statusText); } @@ -1064,7 +1075,8 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { if ((*i)->pos() * _rowHeight >= yTo) { break; } - paintDialog(p, ms, (*i)->history()->peer, contactData(*i), (*i == _sel)); + auto selected = _pressed ? (*i == _pressed) : (*i == _selected); + paintDialog(p, ms, (*i)->history()->peer, contactData(*i), selected); p.translate(0, _rowHeight); } yFrom -= _contacts->size() * _rowHeight; @@ -1080,11 +1092,12 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { yTo -= st::searchedBarHeight; p.translate(0, st::searchedBarHeight); - int32 from = floorclamp(yFrom, _rowHeight, 0, _byUsername.size()); - int32 to = ceilclamp(yTo, _rowHeight, 0, _byUsername.size()); + auto from = floorclamp(yFrom, _rowHeight, 0, _byUsername.size()); + auto to = ceilclamp(yTo, _rowHeight, 0, _byUsername.size()); p.translate(0, from * _rowHeight); for (; from < to; ++from) { - paintDialog(p, ms, _byUsername[from], d_byUsername[from], (_byUsernameSel == from)); + auto selected = (_searchedPressed >= 0) ? (_searchedPressed == from) : (_searchedSelected == from); + paintDialog(p, ms, _byUsername[from], d_byUsername[from], selected); p.translate(0, _rowHeight); } } @@ -1130,7 +1143,8 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { int32 to = ceilclamp(yTo, _rowHeight, 0, _filtered.size()); p.translate(0, from * _rowHeight); for (; from < to; ++from) { - paintDialog(p, ms, _filtered[from]->history()->peer, contactData(_filtered[from]), (_filteredSel == from)); + auto selected = (_filteredPressed >= 0) ? (_filteredPressed == from) : (_filteredSelected == from); + paintDialog(p, ms, _filtered[from]->history()->peer, contactData(_filtered[from]), selected); p.translate(0, _rowHeight); } } @@ -1147,7 +1161,8 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { int32 to = ceilclamp(yTo, _rowHeight, 0, _byUsernameFiltered.size()); p.translate(0, from * _rowHeight); for (; from < to; ++from) { - paintDialog(p, ms, _byUsernameFiltered[from], d_byUsernameFiltered[from], (_byUsernameSel == from)); + auto selected = (_searchedPressed >= 0) ? (_searchedPressed == from) : (_searchedSelected == from); + paintDialog(p, ms, _byUsernameFiltered[from], d_byUsernameFiltered[from], selected); p.translate(0, _rowHeight); } } @@ -1161,16 +1176,16 @@ void ContactsBox::Inner::enterEvent(QEvent *e) { int ContactsBox::Inner::getSelectedRowTop() const { if (_filter.isEmpty()) { - if (_sel) { - return _aboutHeight + (_sel->pos() * _rowHeight); - } else if (_byUsernameSel >= 0) { - return _aboutHeight + (_contacts->size() * _rowHeight) + st::searchedBarHeight + (_byUsernameSel * _rowHeight); + if (_selected) { + return _aboutHeight + (_selected->pos() * _rowHeight); + } else if (_searchedSelected >= 0) { + return _aboutHeight + (_contacts->size() * _rowHeight) + st::searchedBarHeight + (_searchedSelected * _rowHeight); } } else { - if (_filteredSel >= 0) { - return (_filteredSel * _rowHeight); - } else if (_byUsernameSel >= 0) { - return (_filtered.size() * _rowHeight + st::searchedBarHeight + _byUsernameSel * _rowHeight); + if (_filteredSelected >= 0) { + return (_filteredSelected * _rowHeight); + } else if (_searchedSelected >= 0) { + return (_filtered.size() * _rowHeight + st::searchedBarHeight + _searchedSelected * _rowHeight); } } return -1; @@ -1222,53 +1237,130 @@ void ContactsBox::Inner::updateRowWithPeer(PeerData *peer) { } void ContactsBox::Inner::leaveEvent(QEvent *e) { - _mouseSel = false; + _mouseSelection = false; setMouseTracking(false); - if (_sel || _filteredSel >= 0 || _byUsernameSel >= 0) { + if (_selected || _filteredSelected >= 0 || _searchedSelected >= 0) { updateSelectedRow(); - _sel = 0; - _filteredSel = _byUsernameSel = -1; + _selected = nullptr; + _filteredSelected = _searchedSelected = -1; } } void ContactsBox::Inner::mouseMoveEvent(QMouseEvent *e) { - _mouseSel = true; + _mouseSelection = true; _lastMousePos = e->globalPos(); updateSelection(); } void ContactsBox::Inner::mousePressEvent(QMouseEvent *e) { - _mouseSel = true; + _mouseSelection = true; _lastMousePos = e->globalPos(); updateSelection(); - if (e->button() == Qt::LeftButton) { - chooseParticipant(); + + setPressed(_selected); + setFilteredPressed(_filteredSelected); + setSearchedPressed(_searchedSelected); + if (_pressed) { + addRipple(contactData(_selected)); + } else if (_filteredSelected >= 0 && _filteredSelected < _filtered.size()) { + addRipple(contactData(_filtered[_filteredSelected])); + } else if (_searchedSelected >= 0) { + if (_filter.isEmpty() && _searchedSelected < d_byUsername.size()) { + addRipple(d_byUsername[_searchedSelected]); + } else if (!_filter.isEmpty() && _searchedSelected < d_byUsernameFiltered.size()) { + addRipple(d_byUsernameFiltered[_searchedSelected]); + } } } +void ContactsBox::Inner::mouseReleaseEvent(QMouseEvent *e) { + auto pressed = _pressed; + setPressed(nullptr); + auto filteredPressed = _filteredPressed; + setFilteredPressed(-1); + auto searchedPressed = _searchedPressed; + setSearchedPressed(-1); + updateSelectedRow(); + if (e->button() == Qt::LeftButton) { + if (pressed && pressed == _selected) { + chooseParticipant(); + } else if (filteredPressed >= 0 && filteredPressed == _filteredSelected) { + chooseParticipant(); + } else if (searchedPressed >= 0 && searchedPressed == _searchedSelected) { + chooseParticipant(); + } + } +} + +void ContactsBox::Inner::addRipple(ContactData *data) { + auto rowTop = getSelectedRowTop(); + if (!data->ripple) { + auto mask = Ui::RippleAnimation::rectMask(QSize(width(), _rowHeight)); + data->ripple = std_::make_unique(st::contactsRipple, std_::move(mask), [this, data] { + updateRowWithTop(data->rippleRowTop); + }); + } + data->rippleRowTop = rowTop; + data->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(0, rowTop)); +} + +void ContactsBox::Inner::stopLastRipple(ContactData *data) { + if (data->ripple) { + data->ripple->lastStop(); + } +} + +void ContactsBox::Inner::setPressed(Dialogs::Row *pressed) { + if (_pressed != pressed) { + if (_pressed) { + stopLastRipple(contactData(_pressed)); + } + _pressed = pressed; + } +} + +void ContactsBox::Inner::setFilteredPressed(int pressed) { + if (_filteredPressed >= 0 && _filteredPressed < _filtered.size()) { + stopLastRipple(contactData(_filtered[_filteredPressed])); + } + _filteredPressed = pressed; +} + +void ContactsBox::Inner::setSearchedPressed(int pressed) { + if (_searchedPressed >= 0) { + if (_searchedPressed < d_byUsername.size()) { + stopLastRipple(d_byUsername[_searchedPressed]); + } + if (_searchedPressed < d_byUsernameFiltered.size()) { + stopLastRipple(d_byUsernameFiltered[_searchedPressed]); + } + } + _searchedPressed = pressed; +} + void ContactsBox::Inner::chooseParticipant() { if (_saving) return; bool addingAdmin = (_channel && _membersFilter == MembersFilter::Admins); if (!addingAdmin && usingMultiSelect()) { _time = unixtime(); if (_filter.isEmpty()) { - if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) { - auto data = d_byUsername[_byUsernameSel]; - auto peer = _byUsername[_byUsernameSel]; + if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) { + auto data = d_byUsername[_searchedSelected]; + auto peer = _byUsername[_searchedSelected]; if (data->disabledChecked) return; changeCheckState(data, peer); - } else if (_sel) { - auto data = contactData(_sel); - auto peer = _sel->history()->peer; + } else if (_selected) { + auto data = contactData(_selected); + auto peer = _selected->history()->peer; if (data->disabledChecked) return; - changeCheckState(_sel); + changeCheckState(_selected); } } else { - if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) { - auto data = d_byUsernameFiltered[_byUsernameSel]; - auto peer = _byUsernameFiltered[_byUsernameSel]; + if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) { + auto data = d_byUsernameFiltered[_searchedSelected]; + auto peer = _byUsernameFiltered[_searchedSelected]; if (data->disabledChecked) return; int i = 0, l = d_byUsername.size(); @@ -1291,9 +1383,9 @@ void ContactsBox::Inner::chooseParticipant() { } changeCheckState(data, peer); - } else if (_filteredSel >= 0 && _filteredSel < _filtered.size()) { - auto data = contactData(_filtered[_filteredSel]); - auto peer = _filtered[_filteredSel]->history()->peer; + } else if (_filteredSelected >= 0 && _filteredSelected < _filtered.size()) { + auto data = contactData(_filtered[_filteredSelected]); + auto peer = _filtered[_filteredSelected]->history()->peer; if (data->disabledChecked) return; changeCheckState(data, peer); @@ -1302,17 +1394,17 @@ void ContactsBox::Inner::chooseParticipant() { } else { PeerData *peer = 0; if (_filter.isEmpty()) { - if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) { - peer = _byUsername[_byUsernameSel]; - } else if (_sel) { - peer = _sel->history()->peer; + if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) { + peer = _byUsername[_searchedSelected]; + } else if (_selected) { + peer = _selected->history()->peer; } } else { - if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) { - peer = _byUsernameFiltered[_byUsernameSel]; + if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) { + peer = _byUsernameFiltered[_searchedSelected]; } else { - if (_filteredSel < 0 || _filteredSel >= _filtered.size()) return; - peer = _filtered[_filteredSel]->history()->peer; + if (_filteredSelected < 0 || _filteredSelected >= _filtered.size()) return; + peer = _filtered[_filteredSelected]->history()->peer; } } if (peer) { @@ -1408,31 +1500,35 @@ int32 ContactsBox::Inner::selectedCount() const { } void ContactsBox::Inner::updateSelection() { - if (!_mouseSel) return; + if (!_mouseSelection) return; - QPoint p(mapFromGlobal(_lastMousePos)); + auto p = mapFromGlobal(_lastMousePos); bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); if (_filter.isEmpty()) { + _filteredSelected = -1; + setFilteredPressed(-1); if (_aboutHeight) { p.setY(p.y() - _aboutHeight); } - Dialogs::Row *newSel = (in && (p.y() >= 0) && (p.y() < _contacts->size() * _rowHeight)) ? _contacts->rowAtY(p.y(), _rowHeight) : nullptr; - int32 byUsernameSel = (in && p.y() >= _contacts->size() * _rowHeight + st::searchedBarHeight) ? ((p.y() - _contacts->size() * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1; - if (byUsernameSel >= _byUsername.size()) byUsernameSel = -1; - if (newSel != _sel || byUsernameSel != _byUsernameSel) { + auto selected = (in && (p.y() >= 0) && (p.y() < _contacts->size() * _rowHeight)) ? _contacts->rowAtY(p.y(), _rowHeight) : nullptr; + auto searchedSelected = (in && p.y() >= _contacts->size() * _rowHeight + st::searchedBarHeight) ? ((p.y() - _contacts->size() * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1; + if (searchedSelected >= _byUsername.size()) searchedSelected = -1; + if (_selected != selected || _searchedSelected != searchedSelected) { updateSelectedRow(); - _sel = newSel; - _byUsernameSel = byUsernameSel; + _selected = selected; + _searchedSelected = searchedSelected; updateSelectedRow(); } } else { - int32 newFilteredSel = (in && p.y() >= 0 && p.y() < _filtered.size() * _rowHeight) ? (p.y() / _rowHeight) : -1; - int32 byUsernameSel = (in && p.y() >= _filtered.size() * _rowHeight + st::searchedBarHeight) ? ((p.y() - _filtered.size() * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1; - if (byUsernameSel >= _byUsernameFiltered.size()) byUsernameSel = -1; - if (newFilteredSel != _filteredSel || byUsernameSel != _byUsernameSel) { + _selected = nullptr; + setPressed(nullptr); + auto filteredSelected = (in && p.y() >= 0 && p.y() < _filtered.size() * _rowHeight) ? (p.y() / _rowHeight) : -1; + auto searchedSelected = (in && p.y() >= _filtered.size() * _rowHeight + st::searchedBarHeight) ? ((p.y() - _filtered.size() * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1; + if (searchedSelected >= _byUsernameFiltered.size()) searchedSelected = -1; + if (_filteredSelected != filteredSelected || _searchedSelected != searchedSelected) { updateSelectedRow(); - _filteredSel = newFilteredSel; - _byUsernameSel = byUsernameSel; + _filteredSelected = filteredSelected; + _searchedSelected = searchedSelected; updateSelectedRow(); } } @@ -1461,13 +1557,15 @@ void ContactsBox::Inner::updateFilter(QString filter) { _byUsernameFiltered.clear(); d_byUsernameFiltered.clear(); - for (int i = 0, l = _byUsernameDatas.size(); i < l; ++i) { - delete _byUsernameDatas[i]; - } - _byUsernameDatas.clear(); + clearSearchedContactDatas(); + _selected = nullptr; + setPressed(nullptr); + _filteredSelected = -1; + setFilteredPressed(-1); + _searchedSelected = -1; + setSearchedPressed(-1); if (_filter.isEmpty()) { - _sel = 0; refresh(); } else { if (!_addContactLnk->isHidden()) _addContactLnk->hide(); @@ -1534,21 +1632,19 @@ void ContactsBox::Inner::updateFilter(QString filter) { } } } - _filteredSel = -1; if (!_filtered.isEmpty()) { - for (_filteredSel = 0; (_filteredSel < _filtered.size()) && contactData(_filtered[_filteredSel])->disabledChecked;) { - ++_filteredSel; + for (_filteredSelected = 0; (_filteredSelected < _filtered.size()) && contactData(_filtered[_filteredSelected])->disabledChecked;) { + ++_filteredSelected; } - if (_filteredSel == _filtered.size()) _filteredSel = -1; + if (_filteredSelected == _filtered.size()) _filteredSelected = -1; } - _byUsernameSel = -1; - if (_filteredSel < 0 && !_byUsernameFiltered.isEmpty()) { - for (_byUsernameSel = 0; (_byUsernameSel < _byUsernameFiltered.size()) && d_byUsernameFiltered[_byUsernameSel]->disabledChecked;) { - ++_byUsernameSel; + if (_filteredSelected < 0 && !_byUsernameFiltered.isEmpty()) { + for (_searchedSelected = 0; (_searchedSelected < _byUsernameFiltered.size()) && d_byUsernameFiltered[_searchedSelected]->disabledChecked;) { + ++_searchedSelected; } - if (_byUsernameSel == _byUsernameFiltered.size()) _byUsernameSel = -1; + if (_searchedSelected == _byUsernameFiltered.size()) _searchedSelected = -1; } - _mouseSel = false; + _mouseSelection = false; refresh(); if ((!bot() || sharingBotGame()) && (!_chat || _membersFilter != MembersFilter::Admins)) { @@ -1561,9 +1657,15 @@ void ContactsBox::Inner::updateFilter(QString filter) { } } +void ContactsBox::Inner::clearSearchedContactDatas() { + for (auto data : base::take(_byUsernameDatas)) { + delete data; + } +} + void ContactsBox::Inner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) { if (!_filter.isEmpty()) { - for (FilteredDialogs::iterator i = _filtered.begin(), e = _filtered.end(); i != e;) { + for (auto i = _filtered.begin(), e = _filtered.end(); i != e;) { if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts! if (newRow) { *i = newRow; @@ -1575,18 +1677,21 @@ void ContactsBox::Inner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row ++i; } } - if (_filteredSel >= _filtered.size()) { - _filteredSel = -1; + if (_filteredSelected >= _filtered.size()) { + _filteredSelected = -1; + } + if (_filteredPressed >= _filtered.size()) { + _filteredPressed = -1; } } else { - if (_sel == oldRow) { - _sel = newRow; + if (_selected == oldRow) { + _selected = newRow; + } + if (_pressed == oldRow) { + setPressed(newRow); } } - _mouseSel = false; - int cnt = (_filter.isEmpty() ? _contacts->size() : _filtered.size()); - int newh = cnt ? (cnt * _rowHeight) : st::noContactsHeight; - resize(width(), newh); + refresh(); } void ContactsBox::Inner::peopleReceived(const QString &query, const QVector &people) { @@ -1707,6 +1812,10 @@ ContactsBox::Inner::~Inner() { for (auto contactData : base::take(_contactsData)) { delete contactData; } + clearSearchedContactDatas(); + for (auto data : base::take(d_byUsername)) { + delete data; + } if (_bot) { if (auto &info = _bot->botInfo) { info->startGroupToken = QString(); @@ -1722,127 +1831,127 @@ void ContactsBox::Inner::resizeEvent(QResizeEvent *e) { void ContactsBox::Inner::selectSkip(int32 dir) { _time = unixtime(); - _mouseSel = false; + _mouseSelection = false; if (_filter.isEmpty()) { int cur = 0; - if (_sel) { - for (auto i = _contacts->cbegin(); *i != _sel; ++i) { + if (_selected) { + for (auto i = _contacts->cbegin(); *i != _selected; ++i) { ++cur; } - } else if (_byUsernameSel >= 0) { - cur = (_contacts->size() + _byUsernameSel); + } else if (_searchedSelected >= 0) { + cur = (_contacts->size() + _searchedSelected); } else { cur = -1; } cur += dir; if (cur <= 0) { - _sel = (!_contacts->isEmpty()) ? *_contacts->cbegin() : nullptr; - _byUsernameSel = (_contacts->isEmpty() && !_byUsername.isEmpty()) ? 0 : -1; + _selected = (!_contacts->isEmpty()) ? *_contacts->cbegin() : nullptr; + _searchedSelected = (_contacts->isEmpty() && !_byUsername.isEmpty()) ? 0 : -1; } else if (cur >= _contacts->size()) { if (_byUsername.isEmpty()) { - _sel = _contacts->isEmpty() ? nullptr : *(_contacts->cend() - 1); - _byUsernameSel = -1; + _selected = _contacts->isEmpty() ? nullptr : *(_contacts->cend() - 1); + _searchedSelected = -1; } else { - _sel = nullptr; - _byUsernameSel = cur - _contacts->size(); - if (_byUsernameSel >= _byUsername.size()) _byUsernameSel = _byUsername.size() - 1; + _selected = nullptr; + _searchedSelected = cur - _contacts->size(); + if (_searchedSelected >= _byUsername.size()) _searchedSelected = _byUsername.size() - 1; } } else { for (auto i = _contacts->cbegin(); ; ++i) { - _sel = *i; + _selected = *i; if (!cur) { break; } else { --cur; } } - _byUsernameSel = -1; + _searchedSelected = -1; } if (dir > 0) { - for (auto i = _contacts->cfind(_sel), end = _contacts->cend(); i != end && contactData(*i)->disabledChecked; ++i) { - _sel = *i; + for (auto i = _contacts->cfind(_selected), end = _contacts->cend(); i != end && contactData(*i)->disabledChecked; ++i) { + _selected = *i; } - if (_sel && contactData(_sel)->disabledChecked) { - _sel = nullptr; + if (_selected && contactData(_selected)->disabledChecked) { + _selected = nullptr; } - if (!_sel) { + if (!_selected) { if (!_byUsername.isEmpty()) { - if (_byUsernameSel < 0) _byUsernameSel = 0; - for (; _byUsernameSel < _byUsername.size() && d_byUsername[_byUsernameSel]->disabledChecked;) { - ++_byUsernameSel; + if (_searchedSelected < 0) _searchedSelected = 0; + for (; _searchedSelected < _byUsername.size() && d_byUsername[_searchedSelected]->disabledChecked;) { + ++_searchedSelected; } - if (_byUsernameSel == _byUsername.size()) _byUsernameSel = -1; + if (_searchedSelected == _byUsername.size()) _searchedSelected = -1; } } } else { - while (_byUsernameSel >= 0 && d_byUsername[_byUsernameSel]->disabledChecked) { - --_byUsernameSel; + while (_searchedSelected >= 0 && d_byUsername[_searchedSelected]->disabledChecked) { + --_searchedSelected; } - if (_byUsernameSel < 0) { + if (_searchedSelected < 0) { if (!_contacts->isEmpty()) { - if (!_sel) _sel = *(_contacts->cend() - 1); - if (_sel) { - for (auto i = _contacts->cfind(_sel), b = _contacts->cbegin(); i != b && contactData(*i)->disabledChecked; --i) { - _sel = *i; + if (!_selected) _selected = *(_contacts->cend() - 1); + if (_selected) { + for (auto i = _contacts->cfind(_selected), b = _contacts->cbegin(); i != b && contactData(*i)->disabledChecked; --i) { + _selected = *i; } - if (contactData(_sel)->disabledChecked) { - _sel = nullptr; + if (contactData(_selected)->disabledChecked) { + _selected = nullptr; } } } } } - if (_sel) { - emit mustScrollTo(_aboutHeight + _sel->pos() * _rowHeight, _aboutHeight + (_sel->pos() + 1) * _rowHeight); - } else if (_byUsernameSel >= 0) { - emit mustScrollTo(_aboutHeight + (_contacts->size() + _byUsernameSel) * _rowHeight + st::searchedBarHeight, _aboutHeight + (_contacts->size() + _byUsernameSel + 1) * _rowHeight + st::searchedBarHeight); + if (_selected) { + emit mustScrollTo(_aboutHeight + _selected->pos() * _rowHeight, _aboutHeight + (_selected->pos() + 1) * _rowHeight); + } else if (_searchedSelected >= 0) { + emit mustScrollTo(_aboutHeight + (_contacts->size() + _searchedSelected) * _rowHeight + st::searchedBarHeight, _aboutHeight + (_contacts->size() + _searchedSelected + 1) * _rowHeight + st::searchedBarHeight); } } else { - int cur = (_filteredSel >= 0) ? _filteredSel : ((_byUsernameSel >= 0) ? (_filtered.size() + _byUsernameSel) : -1); + int cur = (_filteredSelected >= 0) ? _filteredSelected : ((_searchedSelected >= 0) ? (_filtered.size() + _searchedSelected) : -1); cur += dir; if (cur <= 0) { - _filteredSel = _filtered.isEmpty() ? -1 : 0; - _byUsernameSel = (_filtered.isEmpty() && !_byUsernameFiltered.isEmpty()) ? 0 : -1; + _filteredSelected = _filtered.isEmpty() ? -1 : 0; + _searchedSelected = (_filtered.isEmpty() && !_byUsernameFiltered.isEmpty()) ? 0 : -1; } else if (cur >= _filtered.size()) { - _filteredSel = -1; - _byUsernameSel = cur - _filtered.size(); - if (_byUsernameSel >= _byUsernameFiltered.size()) _byUsernameSel = _byUsernameFiltered.size() - 1; + _filteredSelected = -1; + _searchedSelected = cur - _filtered.size(); + if (_searchedSelected >= _byUsernameFiltered.size()) _searchedSelected = _byUsernameFiltered.size() - 1; } else { - _filteredSel = cur; - _byUsernameSel = -1; + _filteredSelected = cur; + _searchedSelected = -1; } if (dir > 0) { - while (_filteredSel >= 0 && _filteredSel < _filtered.size() && contactData(_filtered[_filteredSel])->disabledChecked) { - ++_filteredSel; + while (_filteredSelected >= 0 && _filteredSelected < _filtered.size() && contactData(_filtered[_filteredSelected])->disabledChecked) { + ++_filteredSelected; } - if (_filteredSel < 0 || _filteredSel >= _filtered.size()) { - _filteredSel = -1; + if (_filteredSelected < 0 || _filteredSelected >= _filtered.size()) { + _filteredSelected = -1; if (!_byUsernameFiltered.isEmpty()) { - if (_byUsernameSel < 0) _byUsernameSel = 0; - for (; _byUsernameSel < _byUsernameFiltered.size() && d_byUsernameFiltered[_byUsernameSel]->disabledChecked;) { - ++_byUsernameSel; + if (_searchedSelected < 0) _searchedSelected = 0; + for (; _searchedSelected < _byUsernameFiltered.size() && d_byUsernameFiltered[_searchedSelected]->disabledChecked;) { + ++_searchedSelected; } - if (_byUsernameSel == _byUsernameFiltered.size()) _byUsernameSel = -1; + if (_searchedSelected == _byUsernameFiltered.size()) _searchedSelected = -1; } } } else { - while (_byUsernameSel >= 0 && d_byUsernameFiltered[_byUsernameSel]->disabledChecked) { - --_byUsernameSel; + while (_searchedSelected >= 0 && d_byUsernameFiltered[_searchedSelected]->disabledChecked) { + --_searchedSelected; } - if (_byUsernameSel < 0) { + if (_searchedSelected < 0) { if (!_filtered.isEmpty()) { - if (_filteredSel < 0) _filteredSel = _filtered.size() - 1; - for (; _filteredSel >= 0 && contactData(_filtered[_filteredSel])->disabledChecked;) { - --_filteredSel; + if (_filteredSelected < 0) _filteredSelected = _filtered.size() - 1; + for (; _filteredSelected >= 0 && contactData(_filtered[_filteredSelected])->disabledChecked;) { + --_filteredSelected; } } } } - if (_filteredSel >= 0) { - emit mustScrollTo(_filteredSel * _rowHeight, (_filteredSel + 1) * _rowHeight); - } else if (_byUsernameSel >= 0) { + if (_filteredSelected >= 0) { + emit mustScrollTo(_filteredSelected * _rowHeight, (_filteredSelected + 1) * _rowHeight); + } else if (_searchedSelected >= 0) { int skip = _filtered.size() * _rowHeight + st::searchedBarHeight; - emit mustScrollTo(skip + _byUsernameSel * _rowHeight, skip + (_byUsernameSel + 1) * _rowHeight); + emit mustScrollTo(skip + _searchedSelected * _rowHeight, skip + (_searchedSelected + 1) * _rowHeight); } } update(); diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index eaf98c941..d5dd70c93 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -31,6 +31,7 @@ class IndexedList; } // namespace Dialogs namespace Ui { +class RippleAnimation; class RoundButton; class LinkButton; class Checkbox; @@ -208,19 +209,29 @@ protected: void leaveEvent(QEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; void resizeEvent(QResizeEvent *e) override; private: struct ContactData { - ContactData() = default; + ContactData(); ContactData(PeerData *peer, const base::lambda_copy &updateCallback); + ~ContactData(); std_::unique_ptr checkbox; + std_::unique_ptr ripple; + int rippleRowTop = 0; Text name; QString statusText; bool statusHasOnlineColor = false; bool disabledChecked = false; }; + void addRipple(ContactData *data); + void stopLastRipple(ContactData *data); + void setPressed(Dialogs::Row *pressed); + void setFilteredPressed(int pressed); + void setSearchedPressed(int pressed); + void clearSearchedContactDatas(); void init(); void initList(); @@ -277,12 +288,14 @@ private: std_::unique_ptr _customList; Dialogs::IndexedList *_contacts = nullptr; - Dialogs::Row *_sel = nullptr; + Dialogs::Row *_selected = nullptr; + Dialogs::Row *_pressed = nullptr; QString _filter; using FilteredDialogs = QVector; FilteredDialogs _filtered; - int _filteredSel = -1; - bool _mouseSel = false; + int _filteredSelected = -1; + int _filteredPressed = -1; + bool _mouseSelection = false; using ContactsData = QMap; ContactsData _contactsData; @@ -298,7 +311,8 @@ private: ByUsernameRows _byUsername, _byUsernameFiltered; ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas ByUsernameDatas _byUsernameDatas; - int _byUsernameSel = -1; + int _searchedSelected = -1; + int _searchedPressed = -1; QPoint _lastMousePos; ChildWidget _addContactLnk; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 1fac7d7b7..275286afc 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -44,8 +44,8 @@ void MembersAddButton::paintEvent(QPaintEvent *e) { Painter p(this); auto ms = getms(); - auto over = (_state & StateOver); - auto down = (_state & StateDown); + auto over = isOver(); + auto down = isDown(); ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width()); paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms); diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index 5f9491a7d..4d9f45834 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -311,7 +311,7 @@ void SessionsBox::Inner::paintEvent(QPaintEvent *e) { void SessionsBox::Inner::onTerminate() { for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) { - if (i.value()->getState() & Ui::AbstractButton::StateOver) { + if (i.value()->isOver()) { _terminating = i.key(); if (_terminateBox) _terminateBox->deleteLater(); diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index cbc0c7f5d..734a814d8 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -274,7 +274,7 @@ void ShareBox::onMustScrollTo(int top, int bottom) { } if (from != to) { _scrollAnimation.start([this]() { - scrollArea()->scrollToY(_scrollAnimation.current(scrollArea()->scrollTop())); + scrollArea()->scrollToY(qRound(_scrollAnimation.current(scrollArea()->scrollTop()))); }, from, to, st::shareScrollDuration, anim::sineInOut); } } diff --git a/Telegram/SourceFiles/boxes/sharebox.h b/Telegram/SourceFiles/boxes/sharebox.h index 35fa69d5d..ec284510f 100644 --- a/Telegram/SourceFiles/boxes/sharebox.h +++ b/Telegram/SourceFiles/boxes/sharebox.h @@ -107,7 +107,7 @@ private: using PeopleQueries = QMap; PeopleQueries _peopleQueries; - IntAnimation _scrollAnimation; + FloatAnimation _scrollAnimation; }; diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 00516b519..834af932b 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -663,7 +663,7 @@ QRect StickersBox::Inner::relativeButtonRect(bool removeButton) const { void StickersBox::Inner::paintRow(Painter &p, int index, TimeMs ms) { auto s = _rows.at(index); - auto xadd = 0, yadd = s->yadd.current(); + auto xadd = 0, yadd = qRound(s->yadd.current()); if (xadd || yadd) p.translate(xadd, yadd); if (_section == Section::Installed) { @@ -673,7 +673,7 @@ void StickersBox::Inner::paintRow(Painter &p, int index, TimeMs ms) { if (_started >= 0) { float64 o = aboveShadowOpacity(); if (o > current) { - _aboveShadowFadeOpacity = anim::fvalue(o, o); + _aboveShadowFadeOpacity = anim::value(o, o); current = o; } } @@ -858,14 +858,14 @@ void StickersBox::Inner::onUpdateSelected() { shift = -floorclamp(_dragStart.y() - local.y() + (_rowHeight / 2), _rowHeight, 0, _dragging - firstSetIndex); for (int32 from = _dragging, to = _dragging + shift; from > to; --from) { qSwap(_rows[from], _rows[from - 1]); - _rows.at(from)->yadd = anim::ivalue(_rows.at(from)->yadd.current() - _rowHeight, 0); + _rows[from]->yadd = anim::value(_rows[from]->yadd.current() - _rowHeight, 0); _animStartTimes[from] = ms; } } else if (_dragStart.y() < local.y() && _dragging + 1 < _rows.size()) { shift = floorclamp(local.y() - _dragStart.y() + (_rowHeight / 2), _rowHeight, 0, _rows.size() - _dragging - 1); for (int32 from = _dragging, to = _dragging + shift; from < to; ++from) { qSwap(_rows[from], _rows[from + 1]); - _rows.at(from)->yadd = anim::ivalue(_rows.at(from)->yadd.current() + _rowHeight, 0); + _rows[from]->yadd = anim::value(_rows[from]->yadd.current() + _rowHeight, 0); _animStartTimes[from] = ms; } } @@ -877,7 +877,7 @@ void StickersBox::Inner::onUpdateSelected() { _a_shifting.start(); } } - _rows.at(_dragging)->yadd = anim::ivalue(local.y() - _dragStart.y(), local.y() - _dragStart.y()); + _rows[_dragging]->yadd = anim::value(local.y() - _dragStart.y(), local.y() - _dragStart.y()); _animStartTimes[_dragging] = 0; _a_shifting.step(getms(), true); @@ -926,8 +926,8 @@ void StickersBox::Inner::onUpdateSelected() { float64 StickersBox::Inner::aboveShadowOpacity() const { if (_above < 0) return 0; - int32 dx = 0; - int32 dy = qAbs(_above * _rowHeight + _rows.at(_above)->yadd.current() - _started * _rowHeight); + auto dx = 0; + auto dy = qAbs(_above * _rowHeight + qRound(_rows[_above]->yadd.current()) - _started * _rowHeight); return qMin((dx + dy) * 2. / _rowHeight, 1.); } @@ -948,9 +948,9 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { } } else if (_dragging >= 0) { QPoint local(mapFromGlobal(_mouse)); - _rows[_dragging]->yadd.start(0); + _rows[_dragging]->yadd.start(0.); _aboveShadowFadeStart = _animStartTimes[_dragging] = getms(); - _aboveShadowFadeOpacity = anim::fvalue(aboveShadowOpacity(), 0); + _aboveShadowFadeOpacity = anim::value(aboveShadowOpacity(), 0); if (!_a_shifting.animating()) { _a_shifting.start(); } @@ -994,10 +994,10 @@ void StickersBox::Inner::step_shifting(TimeMs ms, bool timer) { if (updateMin < 0) updateMin = i; updateMax = i; if (start + st::stickersRowDuration > ms && ms >= start) { - _rows.at(i)->yadd.update(float64(ms - start) / st::stickersRowDuration, anim::sineInOut); + _rows[i]->yadd.update(float64(ms - start) / st::stickersRowDuration, anim::sineInOut); animating = true; } else { - _rows.at(i)->yadd.finish(); + _rows[i]->yadd.finish(); _animStartTimes[i] = 0; } } @@ -1035,7 +1035,7 @@ void StickersBox::Inner::clear() { _rows.clear(); _animStartTimes.clear(); _aboveShadowFadeStart = 0; - _aboveShadowFadeOpacity = anim::fvalue(0, 0); + _aboveShadowFadeOpacity = anim::value(); _a_shifting.stop(); _above = _dragging = _started = -1; _selected = -1; diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h index 3f21d2fec..8b97c3333 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.h +++ b/Telegram/SourceFiles/boxes/stickers_box.h @@ -229,7 +229,7 @@ private: int titleWidth; bool installed, official, unread, archived, removed; int32 pixw, pixh; - anim::ivalue yadd; + anim::value yadd; QSharedPointer ripple; }; using Rows = QList; @@ -245,7 +245,7 @@ private: Rows _rows; QList _animStartTimes; TimeMs _aboveShadowFadeStart = 0; - anim::fvalue _aboveShadowFadeOpacity = { 0., 0. }; + anim::value _aboveShadowFadeOpacity; Animation _a_shifting; base::lambda _installSetCallback; diff --git a/Telegram/SourceFiles/core/stl_subset.h b/Telegram/SourceFiles/core/stl_subset.h index c87ccc604..8053e4165 100644 --- a/Telegram/SourceFiles/core/stl_subset.h +++ b/Telegram/SourceFiles/core/stl_subset.h @@ -200,6 +200,7 @@ public: } ~unique_ptr() noexcept { if (_p) { + static_assert(sizeof(T) > 0, "can't delete an incomplete type"); delete _p; _p = nullptr; } @@ -228,6 +229,7 @@ public: auto old = _p; _p = p; if (old) { + static_assert(sizeof(T) > 0, "can't delete an incomplete type"); delete old; } } diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 5fedef043..a0cbf9997 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019011ULL) +#define BETA_VERSION_MACRO (10019012ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 46b688e8c..e93fc4537 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -27,6 +27,10 @@ dialogsUnreadFont: font(12px bold); dialogsUnreadHeight: 19px; dialogsUnreadPadding: 5px; +dialogsRipple: defaultRippleAnimation; +dialogsRippleBg: windowBgRipple; +dialogsRippleBgActive: activeButtonBgRipple; + dialogsTextFont: font(fsize); dialogsDateFont: font(13px); dialogsDateSkip: 5px; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index a1daae5f2..b4b2bf3cb 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -59,20 +59,21 @@ void paintRowDate(Painter &p, const QDateTime &date, QRect &rectForName, bool ac } template -void paintRow(Painter &p, History *history, HistoryItem *item, Data::Draft *draft, QDateTime date, int w, bool active, bool selected, bool onlyBackground, TimeMs ms, PaintItemCallback paintItemCallback) { - QRect fullRect(0, 0, w, st::dialogsRowHeight); +void paintRow(Painter &p, const RippleRow *row, History *history, HistoryItem *item, Data::Draft *draft, QDateTime date, int fullWidth, bool active, bool selected, bool onlyBackground, TimeMs ms, PaintItemCallback paintItemCallback) { + QRect fullRect(0, 0, fullWidth, st::dialogsRowHeight); p.fillRect(fullRect, active ? st::dialogsBgActive : (selected ? st::dialogsBgOver : st::dialogsBg)); + row->paintRipple(p, 0, 0, fullWidth, ms, &(active ? st::dialogsRippleBgActive : st::dialogsRippleBg)->c); if (onlyBackground) return; - PeerData *userpicPeer = (history->peer->migrateTo() ? history->peer->migrateTo() : history->peer); - userpicPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), w); + auto userpicPeer = (history->peer->migrateTo() ? history->peer->migrateTo() : history->peer); + userpicPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), fullWidth); - int32 nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; - int32 namewidth = w - nameleft - st::dialogsPadding.x(); + auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; + auto namewidth = fullWidth - nameleft - st::dialogsPadding.x(); QRect rectForName(nameleft, st::dialogsPadding.y() + st::dialogsNameTop, namewidth, st::msgNameFont->height); if (auto chatTypeIcon = ChatTypeIcon(history->peer, active, selected)) { - chatTypeIcon->paint(p, rectForName.topLeft(), w); + chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); } @@ -82,7 +83,7 @@ void paintRow(Painter &p, History *history, HistoryItem *item, Data::Draft *draf p.setFont(st::dialogsTextFont); auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService); - if (!history->paintSendAction(p, nameleft, texttop, namewidth, w, color, ms)) { + if (!history->paintSendAction(p, nameleft, texttop, namewidth, fullWidth, color, ms)) { if (history->cloudDraftTextCache.isEmpty()) { auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft))); auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, textClean(draft->textWithTags.text)); @@ -96,7 +97,7 @@ void paintRow(Painter &p, History *history, HistoryItem *item, Data::Draft *draf } else if (!item) { auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService); p.setFont(st::dialogsTextFont); - if (!history->paintSendAction(p, nameleft, texttop, namewidth, w, color, ms)) { + if (!history->paintSendAction(p, nameleft, texttop, namewidth, fullWidth, color, ms)) { p.setPen(color); p.drawText(nameleft, texttop + st::msgNameFont->ascent, lang(lng_empty_history)); } @@ -123,13 +124,13 @@ void paintRow(Painter &p, History *history, HistoryItem *item, Data::Draft *draf })(); if (sendStateIcon) { rectForName.setWidth(rectForName.width() - st::dialogsSendStateSkip); - sendStateIcon->paint(p, rectForName.topLeft() + QPoint(rectForName.width(), 0), w); + sendStateIcon->paint(p, rectForName.topLeft() + QPoint(rectForName.width(), 0), fullWidth); } if (history->peer->isUser() && history->peer->isVerified()) { auto icon = &(active ? st::dialogsVerifiedIconActive : (selected ? st::dialogsVerifiedIconOver : st::dialogsVerifiedIcon)); rectForName.setWidth(rectForName.width() - icon->width()); - icon->paint(p, rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0), w); + icon->paint(p, rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0), fullWidth); } p.setPen(active ? st::dialogsNameFgActive : (selected ? st::dialogsNameFgOver : st::dialogsNameFg)); @@ -241,7 +242,7 @@ void paintUnreadCount(Painter &p, const QString &text, int x, int y, const Unrea p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + textTop + st.font->ascent, text); } -void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground, TimeMs ms) { +void RowPainter::paint(Painter &p, const Row *row, int fullWidth, bool active, bool selected, bool onlyBackground, TimeMs ms) { auto history = row->history(); auto item = history->lastMsg; auto cloudDraft = history->cloudDraft(); @@ -267,15 +268,15 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele if (item && cloudDraft && unreadCount > 0) { cloudDraft = nullptr; // Draw item, if draft is older. } - paintRow(p, history, item, cloudDraft, displayDate(), w, active, selected, onlyBackground, ms, [&p, w, active, selected, ms, history, unreadCount](int nameleft, int namewidth, HistoryItem *item) { + paintRow(p, row, history, item, cloudDraft, displayDate(), fullWidth, active, selected, onlyBackground, ms, [&p, fullWidth, active, selected, ms, history, unreadCount](int nameleft, int namewidth, HistoryItem *item) { int availableWidth = namewidth; int texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; if (unreadCount) { auto counter = QString::number(unreadCount); auto mutedCounter = history->mute(); - int unreadRight = w - st::dialogsPadding.x(); - int unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; - int unreadWidth = 0; + auto unreadRight = fullWidth - st::dialogsPadding.x(); + auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; + auto unreadWidth = 0; UnreadBadgeStyle st; st.active = active; @@ -284,16 +285,16 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele availableWidth -= unreadWidth + st.padding; } auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService); - if (!history->paintSendAction(p, nameleft, texttop, availableWidth, w, color, ms)) { + if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) { item->drawInDialog(p, QRect(nameleft, texttop, availableWidth, st::dialogsTextFont->height), active, selected, history->textCachedFor, history->lastItemTextCache); } }); } -void RowPainter::paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground, TimeMs ms) { +void RowPainter::paint(Painter &p, const FakeRow *row, int fullWidth, bool active, bool selected, bool onlyBackground, TimeMs ms) { auto item = row->item(); auto history = item->history(); - paintRow(p, history, item, nullptr, item->date, w, active, selected, onlyBackground, ms, [&p, row, active, selected](int nameleft, int namewidth, HistoryItem *item) { + paintRow(p, row, history, item, nullptr, item->date, fullWidth, active, selected, onlyBackground, ms, [&p, row, active, selected](int nameleft, int namewidth, HistoryItem *item) { int lastWidth = namewidth, texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; item->drawInDialog(p, QRect(nameleft, texttop, lastWidth, st::dialogsTextFont->height), active, selected, row->_cacheFor, row->_cache); }); @@ -306,8 +307,8 @@ QRect RowPainter::sendActionAnimationRect(int animationWidth, int animationHeigh return QRect(nameleft, texttop, textUpdated ? namewidth : animationWidth, animationHeight); } -void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool onlyBackground) { - p.fillRect(0, 0, w, st::dialogsImportantBarHeight, selected ? st::dialogsBgOver : st::dialogsBg); +void paintImportantSwitch(Painter &p, Mode current, int fullWidth, bool selected, bool onlyBackground) { + p.fillRect(0, 0, fullWidth, st::dialogsImportantBarHeight, selected ? st::dialogsBgOver : st::dialogsBg); if (onlyBackground) { return; } @@ -323,7 +324,7 @@ void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool o if (mutedHidden) { if (int32 unread = App::histories().unreadMutedCount()) { - int unreadRight = w - st::dialogsPadding.x(); + int unreadRight = fullWidth - st::dialogsPadding.x(); UnreadBadgeStyle st; st.muted = true; paintUnreadCount(p, QString::number(unread), unreadRight, unreadTop, st, nullptr); diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h index f84ee6409..4e5bc6cb0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.h +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -31,13 +31,13 @@ const style::icon *ChatTypeIcon(PeerData *peer, bool active, bool selected); class RowPainter { public: - static void paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground, TimeMs ms); - static void paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground, TimeMs ms); + static void paint(Painter &p, const Row *row, int fullWidth, bool active, bool selected, bool onlyBackground, TimeMs ms); + static void paint(Painter &p, const FakeRow *row, int fullWidth, bool active, bool selected, bool onlyBackground, TimeMs ms); static QRect sendActionAnimationRect(int animationWidth, int animationHeight, int fullWidth, bool textUpdated); }; -void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool onlyBackground); +void paintImportantSwitch(Painter &p, Mode current, int fullWidth, bool selected, bool onlyBackground); enum UnreadBadgeSize { UnreadBadgeInDialogs = 0, diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_list.cpp index b0ed915a4..f9347e5e3 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_list.cpp @@ -201,7 +201,7 @@ bool List::del(PeerId peerId, Row *replacedBy) { auto i = _rowByPeer.find(peerId); if (i == _rowByPeer.cend()) return false; - Row *row = i.value(); + auto row = i.value(); if (App::main()) { emit App::main()->dialogRowReplaced(row, replacedBy); } @@ -209,7 +209,7 @@ bool List::del(PeerId peerId, Row *replacedBy) { if (row == _current) { _current = row->_next; } - for (Row *change = row->_next; change != _end; change = change->_next) { + for (auto change = row->_next; change != _end; change = change->_next) { --change->_pos; } --_end->_pos; diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.cpp b/Telegram/SourceFiles/dialogs/dialogs_row.cpp index 74de183a6..88311ed15 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_row.cpp @@ -22,9 +22,37 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_row.h" #include "styles/style_dialogs.h" +#include "ui/effects/ripple_animation.h" +#include "mainwidget.h" namespace Dialogs { +RippleRow::RippleRow() = default; +RippleRow::~RippleRow() = default; + +void RippleRow::addRipple(QPoint origin, QSize size, base::lambda_copy &&updateCallback) { + if (!_ripple) { + auto mask = Ui::RippleAnimation::rectMask(size); + _ripple = std_::make_unique(st::dialogsRipple, std_::move(mask), std_::move(updateCallback)); + } + _ripple->add(origin); +} + +void RippleRow::stopLastRipple() { + if (_ripple) { + _ripple->lastStop(); + } +} + +void RippleRow::paintRipple(Painter &p, int x, int y, int outerWidth, TimeMs ms, const QColor *colorOverride) const { + if (_ripple) { + _ripple->paint(p, x, y, outerWidth, ms, colorOverride); + if (_ripple->empty()) { + _ripple.reset(); + } + } +} + FakeRow::FakeRow(HistoryItem *item) : _item(item), _cache(st::dialogsTextWidthMin) { } diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h index 7327f094e..48426c5c3 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.h +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -25,13 +25,32 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class History; class HistoryItem; +namespace Ui { +class RippleAnimation; +} // namespace Ui + namespace Dialogs { namespace Layout { class RowPainter; } // namespace Layout +class RippleRow { +public: + RippleRow(); + ~RippleRow(); + + void addRipple(QPoint origin, QSize size, base::lambda_copy &&updateCallback); + void stopLastRipple(); + + void paintRipple(Painter &p, int x, int y, int outerWidth, TimeMs ms, const QColor *colorOverride = nullptr) const; + +private: + mutable std_::unique_ptr _ripple; + +}; + class List; -class Row { +class Row : public RippleRow { public: Row(History *history, Row *prev, Row *next, int pos) : _history(history) @@ -57,7 +76,7 @@ private: }; -class FakeRow { +class FakeRow : public RippleRow { public: FakeRow(HistoryItem *item); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 41d9283a1..18a65942d 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -44,14 +44,39 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/input_fields.h" #include "autoupdater.h" +namespace { + +constexpr auto kHashtagResultsLimit = 5; + +} // namespace + +struct DialogsInner::ImportantSwitch { + Dialogs::RippleRow row; +}; + +struct DialogsInner::HashtagResult { + HashtagResult(const QString &tag) : tag(tag) { + } + QString tag; + Dialogs::RippleRow row; +}; + +struct DialogsInner::PeerSearchResult { + PeerSearchResult(PeerData *peer) : peer(peer) { + } + PeerData *peer; + Dialogs::RippleRow row; +}; + DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(parent) -, dialogs(std_::make_unique(Dialogs::SortMode::Date)) -, contactsNoDialogs(std_::make_unique(Dialogs::SortMode::Name)) -, contacts(std_::make_unique(Dialogs::SortMode::Name)) +, _dialogs(std_::make_unique(Dialogs::SortMode::Date)) +, _contactsNoDialogs(std_::make_unique(Dialogs::SortMode::Name)) +, _contacts(std_::make_unique(Dialogs::SortMode::Name)) , _addContactLnk(this, lang(lng_add_contact_button)) , _cancelSearchInPeer(this, st::dialogsCancelSearchInPeer) { if (Global::DialogsModeEnabled()) { - importantDialogs = std_::make_unique(Dialogs::SortMode::Date); + _dialogsImportant = std_::make_unique(Dialogs::SortMode::Date); + _importantSwitch = std_::make_unique(); } connect(main, SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&))); connect(main, SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(onPeerPhotoChanged(PeerData*))); @@ -65,27 +90,27 @@ DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(pare itemRemoved(item); }); subscribe(App::histories().sendActionAnimationUpdated(), [this](const Histories::SendActionAnimationUpdate &update) { - auto updateRect = Dialogs::Layout::RowPainter::sendActionAnimationRect(update.width, update.height, fullWidth(), update.textUpdated); - updateDialogRow(update.history, MsgId(0), updateRect, UpdateRowSection::Default | UpdateRowSection::Filtered); + auto updateRect = Dialogs::Layout::RowPainter::sendActionAnimationRect(update.width, update.height, getFullWidth(), update.textUpdated); + updateDialogRow(update.history->peer, MsgId(0), updateRect, UpdateRowSection::Default | UpdateRowSection::Filtered); }); refresh(); } int DialogsInner::dialogsOffset() const { - return importantDialogs ? st::dialogsImportantBarHeight : 0; + return _dialogsImportant ? st::dialogsImportantBarHeight : 0; } int DialogsInner::filteredOffset() const { return _hashtagResults.size() * st::mentionHeight; } -int DialogsInner::peopleOffset() const { +int DialogsInner::peerSearchOffset() const { return filteredOffset() + (_filterResults.size() * st::dialogsRowHeight) + st::searchedBarHeight; } int DialogsInner::searchedOffset() const { - int result = peopleOffset() + (_peopleResults.isEmpty() ? 0 : ((_peopleResults.size() * st::dialogsRowHeight) + st::searchedBarHeight)); + int result = peerSearchOffset() + (_peerSearchResults.isEmpty() ? 0 : ((_peerSearchResults.size() * st::dialogsRowHeight) + st::searchedBarHeight)); if (_searchInPeer) result += st::dialogsRowHeight; return result; } @@ -96,29 +121,32 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO if (!App::main()) return; - QRect r(region.boundingRect()); + auto r = region.boundingRect(); if (!paintingOther) { p.setClipRect(r); } - auto ms = paintingOther ? TimeMs(0) : getms(); + auto fullWidth = getFullWidth(); + auto ms = getms(); if (_state == DefaultState) { QRect dialogsClip = r; - if (importantDialogs) { - Dialogs::Layout::paintImportantSwitch(p, Global::DialogsMode(), fullWidth(), _importantSwitchSel, paintingOther); + if (_dialogsImportant) { + auto selected = isPressed() ? _importantSwitchPressed : _importantSwitchSelected; + Dialogs::Layout::paintImportantSwitch(p, Global::DialogsMode(), fullWidth, selected, paintingOther); dialogsClip.translate(0, -st::dialogsImportantBarHeight); p.translate(0, st::dialogsImportantBarHeight); } - int32 otherStart = shownDialogs()->size() * st::dialogsRowHeight; - auto active = App::main()->activePeer(), selected = _menuPeer ? _menuPeer : (_sel ? _sel->history()->peer : nullptr); + auto otherStart = shownDialogs()->size() * st::dialogsRowHeight; + auto active = App::main()->activePeer(); + auto selected = _menuPeer ? _menuPeer : (isPressed() ? (_pressed ? _pressed->history()->peer : nullptr) : (_selected ? _selected->history()->peer : nullptr)); if (otherStart) { - shownDialogs()->all().paint(p, fullWidth(), dialogsClip.top(), dialogsClip.top() + dialogsClip.height(), active, selected, paintingOther, ms); + shownDialogs()->all().paint(p, fullWidth, dialogsClip.top(), dialogsClip.top() + dialogsClip.height(), active, selected, paintingOther, ms); } if (!otherStart) { p.fillRect(dialogsClip, st::dialogsBg); if (!paintingOther) { p.setFont(st::noContactsFont); p.setPen(st::noContactsColor); - p.drawText(QRect(0, 0, fullWidth(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_chats : lng_contacts_loading), style::al_center); + p.drawText(QRect(0, 0, fullWidth, st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_chats : lng_contacts_loading), style::al_center); } } } else if (_state == FilteredState || _state == SearchedState) { @@ -127,19 +155,24 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO int32 to = ceilclamp(r.y() + r.height(), st::mentionHeight, 0, _hashtagResults.size()); p.translate(0, from * st::mentionHeight); if (from < _hashtagResults.size()) { - int32 w = fullWidth(), htagwidth = w - st::dialogsPadding.x() * 2; + auto htagwidth = fullWidth - st::dialogsPadding.x() * 2; p.setFont(st::mentionFont); for (; from < to; ++from) { - bool selected = (from == _hashtagSel); - p.fillRect(0, 0, w, st::mentionHeight, selected ? st::mentionBgOver : st::dialogsBg); + auto &result = _hashtagResults[from]; + bool selected = (from == (isPressed() ? _hashtagPressed : _hashtagSelected)); + p.fillRect(0, 0, fullWidth, st::mentionHeight, selected ? st::mentionBgOver : st::dialogsBg); + result->row.paintRipple(p, 0, 0, fullWidth, ms); if (!paintingOther) { + auto &tag = result->tag; if (selected) { int skip = (st::mentionHeight - st::simpleCloseIconOver.height()) / 2; - st::simpleCloseIconOver.paint(p, QPoint(w - st::simpleCloseIconOver.width() - skip, skip), width()); + st::simpleCloseIconOver.paint(p, QPoint(fullWidth - st::simpleCloseIconOver.width() - skip, skip), width()); } - QString first = (_hashtagFilter.size() < 2) ? QString() : ('#' + _hashtagResults.at(from).mid(0, _hashtagFilter.size() - 1)), second = (_hashtagFilter.size() < 2) ? ('#' + _hashtagResults.at(from)) : _hashtagResults.at(from).mid(_hashtagFilter.size() - 1); - int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second); + auto first = (_hashtagFilter.size() < 2) ? QString() : ('#' + tag.mid(0, _hashtagFilter.size() - 1)); + auto second = (_hashtagFilter.size() < 2) ? ('#' + tag) : tag.mid(_hashtagFilter.size() - 1); + auto firstwidth = st::mentionFont->width(first); + auto secondwidth = st::mentionFont->width(second); if (htagwidth < firstwidth + secondwidth) { if (htagwidth < firstwidth + st::mentionFont->elidew) { first = st::mentionFont->elided(first + second, htagwidth); @@ -149,13 +182,13 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } } - p.setFont(st::mentionFont->f); + p.setFont(st::mentionFont); if (!first.isEmpty()) { - p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); + p.setPen(selected ? st::mentionFgOverActive : st::mentionFgActive); p.drawText(st::dialogsPadding.x(), st::mentionTop + st::mentionFont->ascent, first); } if (!second.isEmpty()) { - p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p); + p.setPen(selected ? st::mentionFgOver : st::mentionFg); p.drawText(st::dialogsPadding.x() + firstwidth, st::mentionTop + st::mentionFont->ascent, second); } } @@ -164,25 +197,26 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } } if (!_filterResults.isEmpty()) { - int32 skip = filteredOffset(); - int32 from = floorclamp(r.y() - skip, st::dialogsRowHeight, 0, _filterResults.size()); - int32 to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _filterResults.size()); + auto skip = filteredOffset(); + auto from = floorclamp(r.y() - skip, st::dialogsRowHeight, 0, _filterResults.size()); + auto to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _filterResults.size()); p.translate(0, from * st::dialogsRowHeight); if (from < _filterResults.size()) { - int32 w = fullWidth(); - PeerData *act = App::main()->activePeer(); - MsgId actId = App::main()->activeMsgId(); + auto activePeer = App::main()->activePeer(); + auto activeMsgId = App::main()->activeMsgId(); for (; from < to; ++from) { - bool active = ((_filterResults[from]->history()->peer == act) || (_filterResults[from]->history()->peer->migrateTo() && _filterResults[from]->history()->peer->migrateTo() == act)) && !actId; - bool selected = (from == _filteredSel) || (_filterResults[from]->history()->peer == _menuPeer); - Dialogs::Layout::RowPainter::paint(p, _filterResults[from], w, active, selected, paintingOther, ms); + auto row = _filterResults[from]; + auto peer = row->history()->peer; + auto active = ((peer == activePeer) || (peer->migrateTo() && peer->migrateTo() == activePeer)) && !activeMsgId; + auto selected = _menuPeer ? (peer == _menuPeer) : (from == (isPressed() ? _filteredPressed : _filteredSelected)); + Dialogs::Layout::RowPainter::paint(p, _filterResults[from], fullWidth, active, selected, paintingOther, ms); p.translate(0, st::dialogsRowHeight); } } } - if (!_peopleResults.isEmpty()) { - p.fillRect(0, 0, fullWidth(), st::searchedBarHeight, st::searchedBarBg); + if (!_peerSearchResults.isEmpty()) { + p.fillRect(0, 0, fullWidth, st::searchedBarHeight, st::searchedBarBg); if (!paintingOther) { p.setFont(st::searchedBarFont); p.setPen(st::searchedBarFg); @@ -190,28 +224,29 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } p.translate(0, st::searchedBarHeight); - int32 skip = peopleOffset(); - int32 from = floorclamp(r.y() - skip, st::dialogsRowHeight, 0, _peopleResults.size()); - int32 to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _peopleResults.size()); + auto skip = peerSearchOffset(); + auto from = floorclamp(r.y() - skip, st::dialogsRowHeight, 0, _peerSearchResults.size()); + auto to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _peerSearchResults.size()); p.translate(0, from * st::dialogsRowHeight); - if (from < _peopleResults.size()) { - int32 w = fullWidth(); - PeerData *act = App::main()->activePeer(); - MsgId actId = App::main()->activeMsgId(); + if (from < _peerSearchResults.size()) { + auto activePeer = App::main()->activePeer(); + auto activeMsgId = App::main()->activeMsgId(); for (; from < to; ++from) { - bool active = ((_peopleResults[from] == act) || (_peopleResults[from]->migrateTo() && _peopleResults[from]->migrateTo() == act)) && !actId; - bool selected = (from == _peopleSel); - peopleResultPaint(_peopleResults[from], p, w, active, selected, paintingOther); + auto &result = _peerSearchResults[from]; + auto peer = result->peer; + auto active = ((peer == activePeer) || (peer->migrateTo() && peer->migrateTo() == activePeer)) && !activeMsgId; + auto selected = false ? (peer == _menuPeer) : (from == (isPressed() ? _peerSearchPressed : _peerSearchSelected)); + paintPeerSearchResult(p, result.get(), fullWidth, active, selected, paintingOther, ms); p.translate(0, st::dialogsRowHeight); } } } if (_searchInPeer) { - searchInPeerPaint(p, fullWidth(), paintingOther); + paintSearchInPeer(p, fullWidth, paintingOther); p.translate(0, st::dialogsRowHeight); if (_state == FilteredState && _searchResults.isEmpty()) { - p.fillRect(0, 0, fullWidth(), st::searchedBarHeight, st::searchedBarBg); + p.fillRect(0, 0, fullWidth, st::searchedBarHeight, st::searchedBarBg); if (!paintingOther) { p.setFont(st::searchedBarFont); p.setPen(st::searchedBarFg); @@ -223,7 +258,7 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO if (_state == SearchedState || !_searchResults.isEmpty()) { QString text = lng_search_found_results(lt_count, _searchResults.isEmpty() ? 0 : (_searchedMigratedCount + _searchedCount)); - p.fillRect(0, 0, fullWidth(), st::searchedBarHeight, st::searchedBarBg); + p.fillRect(0, 0, fullWidth, st::searchedBarHeight, st::searchedBarBg); if (!paintingOther) { p.setFont(st::searchedBarFont); p.setPen(st::searchedBarFg); @@ -231,21 +266,20 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } p.translate(0, st::searchedBarHeight); - int32 skip = searchedOffset(); - int32 from = floorclamp(r.y() - skip, st::dialogsRowHeight, 0, _searchResults.size()); - int32 to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _searchResults.size()); + auto skip = searchedOffset(); + auto from = floorclamp(r.y() - skip, st::dialogsRowHeight, 0, _searchResults.size()); + auto to = ceilclamp(r.y() + r.height() - skip, st::dialogsRowHeight, 0, _searchResults.size()); p.translate(0, from * st::dialogsRowHeight); if (from < _searchResults.size()) { - int32 w = fullWidth(); - PeerData *act = App::main()->activePeer(); - MsgId actId = App::main()->activeMsgId(); + auto activePeer = App::main()->activePeer(); + auto activeMsgId = App::main()->activeMsgId(); for (; from < to; ++from) { - auto result = _searchResults[from]; + auto &result = _searchResults[from]; auto item = result->item(); - auto history = item->history(); - bool active = (history->peer == act && item->id == actId) || (history->peer->migrateTo() && history->peer->migrateTo() == act && item->id == -actId); - bool selected = (from == _searchedSel); - Dialogs::Layout::RowPainter::paint(p, result, w, active, selected, paintingOther, ms); + auto peer = item->history()->peer; + auto active = (peer == activePeer && item->id == activeMsgId) || (peer->migrateTo() && peer->migrateTo() == activePeer && item->id == -activeMsgId); + auto selected = false ? (peer == _menuPeer) : (from == (isPressed() ? _searchedPressed : _searchedSelected)); + Dialogs::Layout::RowPainter::paint(p, result.get(), fullWidth, active, selected, paintingOther, ms); p.translate(0, st::dialogsRowHeight); } } @@ -253,35 +287,40 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } } -void DialogsInner::peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool active, bool selected, bool onlyBackground) const { - QRect fullRect(0, 0, w, st::dialogsRowHeight); +void DialogsInner::paintPeerSearchResult(Painter &p, const PeerSearchResult *result, int fullWidth, bool active, bool selected, bool onlyBackground, TimeMs ms) const { + QRect fullRect(0, 0, fullWidth, st::dialogsRowHeight); p.fillRect(fullRect, active ? st::dialogsBgActive : (selected ? st::dialogsBgOver : st::dialogsBg)); + if (!active) { + result->row.paintRipple(p, 0, 0, fullWidth, ms); + } if (onlyBackground) return; - PeerData *userpicPeer = (peer->migrateTo() ? peer->migrateTo() : peer); - userpicPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), fullWidth()); + auto peer = result->peer; + auto userpicPeer = (peer->migrateTo() ? peer->migrateTo() : peer); + userpicPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), getFullWidth()); - int32 nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; - int32 namewidth = w - nameleft - st::dialogsPadding.x(); + auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; + auto namewidth = fullWidth - nameleft - st::dialogsPadding.x(); QRect rectForName(nameleft, st::dialogsPadding.y() + st::dialogsNameTop, namewidth, st::msgNameFont->height); // draw chat icon if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(peer, active, selected)) { - chatTypeIcon->paint(p, rectForName.topLeft(), w); + chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); } if (peer->isVerified()) { auto icon = &(active ? st::dialogsVerifiedIconActive : (selected ? st::dialogsVerifiedIconOver : st::dialogsVerifiedIcon)); rectForName.setWidth(rectForName.width() - icon->width()); - icon->paint(p, rectForName.topLeft() + QPoint(qMin(peer->dialogName().maxWidth(), rectForName.width()), 0), w); + icon->paint(p, rectForName.topLeft() + QPoint(qMin(peer->dialogName().maxWidth(), rectForName.width()), 0), fullWidth); } QRect tr(nameleft, st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip, namewidth, st::dialogsTextFont->height); p.setFont(st::dialogsTextFont); QString username = peer->userName(); - if (!active && username.toLower().startsWith(_peopleQuery)) { - QString first = '@' + username.mid(0, _peopleQuery.size()), second = username.mid(_peopleQuery.size()); - int32 w = st::dialogsTextFont->width(first); + if (!active && username.toLower().startsWith(_peerSearchQuery)) { + auto first = '@' + username.mid(0, _peerSearchQuery.size()); + auto second = username.mid(_peerSearchQuery.size()); + auto w = st::dialogsTextFont->width(first); if (w >= tr.width()) { p.setPen(st::dialogsTextFgService); p.drawText(tr.left(), tr.top() + st::dialogsTextFont->ascent, st::dialogsTextFont->elided(first, tr.width())); @@ -300,19 +339,19 @@ void DialogsInner::peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool a peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } -void DialogsInner::searchInPeerPaint(Painter &p, int32 w, bool onlyBackground) const { - QRect fullRect(0, 0, w, st::dialogsRowHeight); +void DialogsInner::paintSearchInPeer(Painter &p, int fullWidth, bool onlyBackground) const { + QRect fullRect(0, 0, fullWidth, st::dialogsRowHeight); p.fillRect(fullRect, st::dialogsBg); if (onlyBackground) return; - _searchInPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), fullWidth()); + _searchInPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), getFullWidth()); - int32 nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; - int32 namewidth = w - nameleft - st::dialogsPadding.x() * 2 - st::dialogsCancelSearch.width; + auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; + auto namewidth = fullWidth - nameleft - st::dialogsPadding.x() * 2 - st::dialogsCancelSearch.width; QRect rectForName(nameleft, st::dialogsPadding.y() + st::dialogsNameTop, namewidth, st::msgNameFont->height); if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(_searchInPeer, false, false)) { - chatTypeIcon->paint(p, rectForName.topLeft(), w); + chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); } @@ -329,92 +368,237 @@ void DialogsInner::activate() { } void DialogsInner::mouseMoveEvent(QMouseEvent *e) { - lastMousePos = mapToGlobal(e->pos()); - _selByMouse = true; - onUpdateSelected(true); + _mouseSelection = true; + updateSelected(e->pos()); } -void DialogsInner::onUpdateSelected(bool force) { - QPoint mouse(mapFromGlobal(lastMousePos)); - if ((!force && !rect().contains(mouse)) || !_selByMouse) return; - - int w = width(), mouseY = mouse.y(); - _overDelete = false; +void DialogsInner::clearIrrelevantState() { if (_state == DefaultState) { - auto newImportantSwitchSel = (importantDialogs && mouseY >= 0 && mouseY < dialogsOffset()); + _hashtagSelected = -1; + setHashtagPressed(-1); + _hashtagDeleteSelected = _hashtagDeletePressed = false; + _filteredSelected = -1; + setFilteredPressed(-1); + _peerSearchSelected = -1; + setPeerSearchPressed(-1); + _searchedSelected = -1; + setSearchedPressed(-1); + } else if (_state == FilteredState || _state == SearchedState) { + _importantSwitchSelected = false; + setImportantSwitchPressed(false); + _selected = nullptr; + setPressed(nullptr); + } +} + +void DialogsInner::updateSelected(QPoint localPos) { + if (!_mouseSelection) return; + + int w = width(), mouseY = localPos.y(); + clearIrrelevantState(); + if (_state == DefaultState) { + auto importantSwitchSelected = (_dialogsImportant && mouseY >= 0 && mouseY < dialogsOffset()); mouseY -= dialogsOffset(); - auto newSel = newImportantSwitchSel ? nullptr : shownDialogs()->rowAtY(mouseY, st::dialogsRowHeight); - if (newSel != _sel || newImportantSwitchSel != _importantSwitchSel) { + auto selected = importantSwitchSelected ? nullptr : shownDialogs()->rowAtY(mouseY, st::dialogsRowHeight); + if (_selected != selected || _importantSwitchSelected != importantSwitchSelected) { updateSelectedRow(); - _sel = newSel; - _importantSwitchSel = newImportantSwitchSel; + _selected = selected; + _importantSwitchSelected = importantSwitchSelected; updateSelectedRow(); - setCursor(_sel ? style::cur_pointer : style::cur_default); + setCursor((_selected || _importantSwitchSelected) ? style::cur_pointer : style::cur_default); } } else if (_state == FilteredState || _state == SearchedState) { - if (!_hashtagResults.isEmpty()) { - int32 skip = 0, newHashtagSel = (mouseY >= skip) ? ((mouseY - skip) / int32(st::mentionHeight)) : -1; - if (newHashtagSel < 0 || newHashtagSel >= _hashtagResults.size()) { - newHashtagSel = -1; + auto wasSelected = isSelected(); + if (_hashtagResults.isEmpty()) { + _hashtagSelected = -1; + _hashtagDeleteSelected = false; + } else { + auto skip = 0; + auto hashtagSelected = (mouseY >= skip) ? ((mouseY - skip) / st::mentionHeight) : -1; + if (hashtagSelected < 0 || hashtagSelected >= _hashtagResults.size()) { + hashtagSelected = -1; } - if (newHashtagSel != _hashtagSel) { + if (_hashtagSelected != hashtagSelected) { updateSelectedRow(); - _hashtagSel = newHashtagSel; + _hashtagSelected = hashtagSelected; updateSelectedRow(); - setCursor((_hashtagSel >= 0) ? style::cur_pointer : style::cur_default); - } - if (_hashtagSel >= 0) { - _overDelete = (mouse.x() >= w - st::mentionHeight); } + _hashtagDeleteSelected = (_hashtagSelected >= 0) && (localPos.x() >= w - st::mentionHeight); } if (!_filterResults.isEmpty()) { - int32 skip = filteredOffset(), newFilteredSel = (mouseY >= skip) ? ((mouseY - skip) / int32(st::dialogsRowHeight)) : -1; - if (newFilteredSel < 0 || newFilteredSel >= _filterResults.size()) { - newFilteredSel = -1; + auto skip = filteredOffset(); + auto filteredSelected = (mouseY >= skip) ? ((mouseY - skip) / st::dialogsRowHeight) : -1; + if (filteredSelected < 0 || filteredSelected >= _filterResults.size()) { + filteredSelected = -1; } - if (newFilteredSel != _filteredSel) { + if (_filteredSelected != filteredSelected) { updateSelectedRow(); - _filteredSel = newFilteredSel; + _filteredSelected = filteredSelected; updateSelectedRow(); - setCursor((_filteredSel >= 0) ? style::cur_pointer : style::cur_default); } } - if (!_peopleResults.isEmpty()) { - int32 skip = peopleOffset(), newPeopleSel = (mouseY >= skip) ? ((mouseY - skip) / int32(st::dialogsRowHeight)) : -1; - if (newPeopleSel < 0 || newPeopleSel >= _peopleResults.size()) { - newPeopleSel = -1; + if (!_peerSearchResults.isEmpty()) { + auto skip = peerSearchOffset(); + auto peerSearchSelected = (mouseY >= skip) ? ((mouseY - skip) / st::dialogsRowHeight) : -1; + if (peerSearchSelected < 0 || peerSearchSelected >= _peerSearchResults.size()) { + peerSearchSelected = -1; } - if (newPeopleSel != _peopleSel) { + if (_peerSearchSelected != peerSearchSelected) { updateSelectedRow(); - _peopleSel = newPeopleSel; + _peerSearchSelected = peerSearchSelected; updateSelectedRow(); - setCursor((_peopleSel >= 0) ? style::cur_pointer : style::cur_default); } } if (_state == SearchedState && !_searchResults.isEmpty()) { - int32 skip = searchedOffset(), newSearchedSel = (mouseY >= skip) ? ((mouseY - skip) / int32(st::dialogsRowHeight)) : -1; - if (newSearchedSel < 0 || newSearchedSel >= _searchResults.size()) { - newSearchedSel = -1; + auto skip = searchedOffset(); + auto searchedSelected = (mouseY >= skip) ? ((mouseY - skip) / st::dialogsRowHeight) : -1; + if (searchedSelected < 0 || searchedSelected >= _searchResults.size()) { + searchedSelected = -1; } - if (newSearchedSel != _searchedSel) { + if (_searchedSelected != searchedSelected) { updateSelectedRow(); - _searchedSel = newSearchedSel; + _searchedSelected = searchedSelected; updateSelectedRow(); - setCursor((_searchedSel >= 0) ? style::cur_pointer : style::cur_default); } } + if (wasSelected != isSelected()) { + setCursor(wasSelected ? style::cur_default : style::cur_pointer); + } } } void DialogsInner::mousePressEvent(QMouseEvent *e) { - lastMousePos = mapToGlobal(e->pos()); - _selByMouse = true; - onUpdateSelected(true); - if (e->button() == Qt::LeftButton) { - choosePeer(); + _mouseSelection = true; + updateSelected(e->pos()); + + _pressButton = e->button(); + setPressed(_selected); + setImportantSwitchPressed(_importantSwitchSelected); + setHashtagPressed(_hashtagSelected); + _hashtagDeletePressed = _hashtagDeleteSelected; + setFilteredPressed(_filteredSelected); + setPeerSearchPressed(_peerSearchPressed); + setSearchedPressed(_searchedSelected); + if (_importantSwitchPressed) { + _importantSwitch->row.addRipple(e->pos(), QSize(getFullWidth(), st::dialogsImportantBarHeight), [this] { + update(0, 0, getFullWidth(), st::dialogsImportantBarHeight); + }); + } else if (_pressed) { + auto row = _pressed; + row->addRipple(e->pos() - QPoint(0, dialogsOffset() + _pressed->pos() * st::dialogsRowHeight), QSize(getFullWidth(), st::dialogsRowHeight), [row] { + row->history()->updateChatListEntry(); + }); + } else if (_hashtagPressed >= 0 && _hashtagPressed < _hashtagResults.size() && !_hashtagDeletePressed) { + auto row = &_hashtagResults[_hashtagPressed]->row; + row->addRipple(e->pos(), QSize(getFullWidth(), st::mentionHeight), [this, index = _hashtagPressed] { + update(0, index * st::mentionHeight, getFullWidth(), st::mentionHeight); + }); + } else if (_filteredPressed >= 0 && _filteredPressed < _filterResults.size()) { + auto row = _filterResults[_filteredPressed]; + row->addRipple(e->pos() - QPoint(0, filteredOffset() + _filteredPressed * st::dialogsRowHeight), QSize(getFullWidth(), st::dialogsRowHeight), [row] { + if (auto main = App::main()) { + main->dlgUpdated(row->history()->peer, 0); + } + }); + } else if (_peerSearchPressed >= 0 && _peerSearchPressed < _peerSearchResults.size()) { + auto &result = _peerSearchResults[_peerSearchPressed]; + auto row = &result->row; + row->addRipple(e->pos() - QPoint(0, peerSearchOffset() + _peerSearchPressed * st::dialogsRowHeight), QSize(getFullWidth(), st::dialogsRowHeight), [peer = result->peer] { + if (auto main = App::main()) { + main->dlgUpdated(peer, 0); + } + }); + } else if (_searchedPressed >= 0 && _searchedPressed < _searchResults.size()) { + auto &row = _searchResults[_searchedPressed]; + row->addRipple(e->pos() - QPoint(0, searchedOffset() + _searchedPressed * st::dialogsRowHeight), QSize(getFullWidth(), st::dialogsRowHeight), [this, index = _searchedPressed] { + rtlupdate(0, searchedOffset() + index * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); + }); } } +void DialogsInner::mouseReleaseEvent(QMouseEvent *e) { + mousePressReleased(e->button()); +} + +void DialogsInner::mousePressReleased(Qt::MouseButton button) { + auto importantSwitchPressed = _importantSwitchPressed; + setImportantSwitchPressed(false); + auto pressed = _pressed; + setPressed(nullptr); + auto hashtagPressed = _hashtagPressed; + setHashtagPressed(-1); + auto hashtagDeletePressed = _hashtagDeletePressed; + _hashtagDeletePressed = false; + auto filteredPressed = _filteredPressed; + setFilteredPressed(-1); + auto peerSearchPressed = _peerSearchPressed; + setPeerSearchPressed(-1); + auto searchedPressed = _searchedPressed; + setSearchedPressed(-1); + updateSelectedRow(); + if (button == Qt::LeftButton) { + if (importantSwitchPressed && importantSwitchPressed == _importantSwitchSelected) { + choosePeer(); + } else if (pressed && pressed == _selected) { + choosePeer(); + } else if (hashtagPressed >= 0 && hashtagPressed == _hashtagSelected && hashtagDeletePressed == _hashtagDeleteSelected) { + choosePeer(); + } else if (filteredPressed >= 0 && filteredPressed == _filteredSelected) { + choosePeer(); + } else if (peerSearchPressed >= 0 && peerSearchPressed == _peerSearchSelected) { + choosePeer(); + } else if (searchedPressed >= 0 && searchedPressed == _searchedSelected) { + choosePeer(); + } + } +} + +void DialogsInner::setImportantSwitchPressed(bool pressed) { + if (_importantSwitchPressed != pressed) { + if (_importantSwitchPressed) { + _importantSwitch->row.stopLastRipple(); + } + _importantSwitchPressed = pressed; + } +} + +void DialogsInner::setPressed(Dialogs::Row *pressed) { + if (_pressed != pressed) { + if (_pressed) { + _pressed->stopLastRipple(); + } + _pressed = pressed; + } +} + +void DialogsInner::setHashtagPressed(int pressed) { + if (_hashtagPressed >= 0 && _hashtagPressed < _hashtagResults.size()) { + _hashtagResults[_hashtagPressed]->row.stopLastRipple(); + } + _hashtagPressed = pressed; +} + +void DialogsInner::setFilteredPressed(int pressed) { + if (_filteredPressed >= 0 && _filteredPressed < _filterResults.size()) { + _filterResults[_filteredPressed]->stopLastRipple(); + } + _filteredPressed = pressed; +} + +void DialogsInner::setPeerSearchPressed(int pressed) { + if (_peerSearchPressed >= 0 && _peerSearchPressed < _peerSearchResults.size()) { + _peerSearchResults[_peerSearchPressed]->row.stopLastRipple(); + } + _peerSearchPressed = pressed; +} + +void DialogsInner::setSearchedPressed(int pressed) { + if (_searchedPressed >= 0 && _searchedPressed < _searchResults.size()) { + _searchResults[_searchedPressed]->stopLastRipple(); + } + _searchedPressed = pressed; +} + void DialogsInner::resizeEvent(QResizeEvent *e) { _addContactLnk->move((width() - _addContactLnk->width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); _cancelSearchInPeer->moveToRight(st::dialogsFilterSkip + st::dialogsFilterPadding.x() - otherWidth(), (st::dialogsRowHeight - st::dialogsCancelSearchInPeer.height) / 2); @@ -435,8 +619,11 @@ void DialogsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRo } } } - if (_sel == oldRow) { - _sel = newRow; + if (_selected == oldRow) { + _selected = newRow; + } + if (_pressed == oldRow) { + setPressed(newRow); } } @@ -448,25 +635,25 @@ void DialogsInner::createDialog(History *history) { bool creating = !history->inChatList(Dialogs::Mode::All); if (creating) { - auto mainRow = history->addToChatList(Dialogs::Mode::All, dialogs.get()); - contactsNoDialogs->del(history->peer, mainRow); + auto mainRow = history->addToChatList(Dialogs::Mode::All, _dialogs.get()); + _contactsNoDialogs->del(history->peer, mainRow); } - if (importantDialogs && !history->inChatList(Dialogs::Mode::Important) && !history->mute()) { + if (_dialogsImportant && !history->inChatList(Dialogs::Mode::Important) && !history->mute()) { if (Global::DialogsMode() == Dialogs::Mode::Important) { creating = true; } - history->addToChatList(Dialogs::Mode::Important, importantDialogs.get()); + history->addToChatList(Dialogs::Mode::Important, _dialogsImportant.get()); } - auto changed = history->adjustByPosInChatList(Dialogs::Mode::All, dialogs.get()); + auto changed = history->adjustByPosInChatList(Dialogs::Mode::All, _dialogs.get()); - if (importantDialogs) { + if (_dialogsImportant) { if (history->mute()) { if (Global::DialogsMode() == Dialogs::Mode::Important) { return; } } else { - auto importantChanged = history->adjustByPosInChatList(Dialogs::Mode::Important, importantDialogs.get()); + auto importantChanged = history->adjustByPosInChatList(Dialogs::Mode::Important, _dialogsImportant.get()); if (Global::DialogsMode() == Dialogs::Mode::Important) { changed = importantChanged; } @@ -480,7 +667,7 @@ void DialogsInner::createDialog(History *history) { if (creating) { refresh(); } else if (_state == DefaultState && changed.movedFrom != changed.movedTo) { - update(0, qMin(from, to), fullWidth(), qAbs(from - to) + st::dialogsRowHeight); + update(0, qMin(from, to), getFullWidth(), qAbs(from - to) + st::dialogsRowHeight); } } @@ -489,17 +676,20 @@ void DialogsInner::removeDialog(History *history) { if (history->peer == _menuPeer && _menu) { _menu->deleteLater(); } - if (_sel && _sel->history() == history) { - _sel = nullptr; + if (_selected && _selected->history() == history) { + _selected = nullptr; } - history->removeFromChatList(Dialogs::Mode::All, dialogs.get()); - if (importantDialogs) { - history->removeFromChatList(Dialogs::Mode::Important, importantDialogs.get()); + if (_pressed && _pressed->history() == history) { + setPressed(nullptr); + } + history->removeFromChatList(Dialogs::Mode::All, _dialogs.get()); + if (_dialogsImportant) { + history->removeFromChatList(Dialogs::Mode::Important, _dialogsImportant.get()); } if (App::wnd()) App::wnd()->notifyClear(history); - if (contacts->contains(history->peer->id)) { - if (!contactsNoDialogs->contains(history->peer->id)) { - contactsNoDialogs->addByName(history); + if (_contacts->contains(history->peer->id)) { + if (!_contactsNoDialogs->contains(history->peer->id)) { + _contactsNoDialogs->addByName(history); } } @@ -513,13 +703,13 @@ void DialogsInner::removeDialog(History *history) { void DialogsInner::dlgUpdated(Dialogs::Mode list, Dialogs::Row *row) { if (_state == DefaultState) { if (Global::DialogsMode() == list) { - update(0, dialogsOffset() + row->pos() * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight); + update(0, dialogsOffset() + row->pos() * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); } } else if (_state == FilteredState || _state == SearchedState) { if (list == Dialogs::Mode::All) { for (int32 i = 0, l = _filterResults.size(); i < l; ++i) { if (_filterResults.at(i)->history() == row->history()) { - update(0, i * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight); + update(0, filteredOffset() + i * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); break; } } @@ -527,17 +717,17 @@ void DialogsInner::dlgUpdated(Dialogs::Mode list, Dialogs::Row *row) { } } -void DialogsInner::dlgUpdated(History *history, MsgId msgId) { - updateDialogRow(history, msgId, QRect(0, 0, fullWidth(), st::dialogsRowHeight)); +void DialogsInner::dlgUpdated(PeerData *peer, MsgId msgId) { + updateDialogRow(peer, msgId, QRect(0, 0, getFullWidth(), st::dialogsRowHeight)); } -void DialogsInner::updateDialogRow(History *history, MsgId msgId, QRect updateRect, UpdateRowSections sections) { +void DialogsInner::updateDialogRow(PeerData *peer, MsgId msgId, QRect updateRect, UpdateRowSections sections) { auto updateRow = [this, updateRect](int rowTop) { rtlupdate(updateRect.x(), rowTop + updateRect.y(), updateRect.width(), updateRect.height()); }; if (_state == DefaultState) { if (sections & UpdateRowSection::Default) { - if (auto row = shownDialogs()->getRow(history->peer->id)) { + if (auto row = shownDialogs()->getRow(peer->id)) { updateRow(dialogsOffset() + row->pos() * st::dialogsRowHeight); } } @@ -545,17 +735,17 @@ void DialogsInner::updateDialogRow(History *history, MsgId msgId, QRect updateRe if ((sections & UpdateRowSection::Filtered) && !_filterResults.isEmpty()) { auto index = 0, add = filteredOffset(); for_const (auto row, _filterResults) { - if (row->history() == history) { + if (row->history()->peer == peer) { updateRow(add + index * st::dialogsRowHeight); break; } ++index; } } - if ((sections & UpdateRowSection::GlobalSearch) && !_peopleResults.isEmpty()) { - auto index = 0, add = peopleOffset(); - for_const (auto peer, _peopleResults) { - if (peer == history->peer) { + if ((sections & UpdateRowSection::PeerSearch) && !_peerSearchResults.isEmpty()) { + auto index = 0, add = peerSearchOffset(); + for_const (auto &result, _peerSearchResults) { + if (result->peer == peer) { updateRow(add + index * st::dialogsRowHeight); break; } @@ -564,8 +754,9 @@ void DialogsInner::updateDialogRow(History *history, MsgId msgId, QRect updateRe } if ((sections & UpdateRowSection::MessageSearch) && !_searchResults.isEmpty()) { auto index = 0, add = searchedOffset(); - for_const (auto fakeRow, _searchResults) { - if (fakeRow->item()->history() == history && fakeRow->item()->id == msgId) { + for_const (auto &result, _searchResults) { + auto item = result->item(); + if (item->history()->peer == peer && item->id == msgId) { updateRow(add + index * st::dialogsRowHeight); break; } @@ -577,8 +768,7 @@ void DialogsInner::updateDialogRow(History *history, MsgId msgId, QRect updateRe void DialogsInner::enterEvent(QEvent *e) { setMouseTracking(true); - lastMousePos = QCursor::pos(); - onUpdateSelected(true); + updateSelected(); } void DialogsInner::updateSelectedRow(PeerData *peer) { @@ -586,30 +776,30 @@ void DialogsInner::updateSelectedRow(PeerData *peer) { if (peer) { if (History *h = App::historyLoaded(peer->id)) { if (h->inChatList(Global::DialogsMode())) { - update(0, dialogsOffset() + h->posInChatList(Global::DialogsMode()) * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight); + update(0, dialogsOffset() + h->posInChatList(Global::DialogsMode()) * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); } } - } else if (_sel) { - update(0, dialogsOffset() + _sel->pos() * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight); - } else if (_importantSwitchSel) { - update(0, 0, fullWidth(), st::dialogsImportantBarHeight); + } else if (_selected) { + update(0, dialogsOffset() + _selected->pos() * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); + } else if (_importantSwitchSelected) { + update(0, 0, getFullWidth(), st::dialogsImportantBarHeight); } } else if (_state == FilteredState || _state == SearchedState) { if (peer) { for (int32 i = 0, l = _filterResults.size(); i != l; ++i) { if (_filterResults.at(i)->history()->peer == peer) { - update(0, filteredOffset() + i * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight); + update(0, filteredOffset() + i * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); break; } } - } else if (_hashtagSel >= 0) { - update(0, _hashtagSel * st::mentionHeight, fullWidth(), st::mentionHeight); - } else if (_filteredSel >= 0) { - update(0, filteredOffset() + _filteredSel * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight); - } else if (_peopleSel >= 0) { - update(0, peopleOffset() + _peopleSel * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight); - } else if (_searchedSel >= 0) { - update(0, searchedOffset() + _searchedSel * st::dialogsRowHeight, fullWidth(), st::dialogsRowHeight); + } else if (_hashtagSelected >= 0) { + update(0, _hashtagSelected * st::mentionHeight, getFullWidth(), st::mentionHeight); + } else if (_filteredSelected >= 0) { + update(0, filteredOffset() + _filteredSelected * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); + } else if (_peerSearchSelected >= 0) { + update(0, peerSearchOffset() + _peerSearchSelected * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); + } else if (_searchedSelected >= 0) { + update(0, searchedOffset() + _searchedSelected * st::dialogsRowHeight, getFullWidth(), st::dialogsRowHeight); } } @@ -626,12 +816,12 @@ void DialogsInner::dragLeft() { } void DialogsInner::clearSelection() { - _selByMouse = false; - if (_importantSwitchSel || _sel || _filteredSel >= 0 || _hashtagSel >= 0 || _searchedSel >= 0 || _peopleSel >= 0) { + _mouseSelection = false; + if (_importantSwitchSelected || _selected || _filteredSelected >= 0 || _hashtagSelected >= 0 || _peerSearchSelected >= 0 || _searchedSelected >= 0) { updateSelectedRow(); - _sel = nullptr; - _importantSwitchSel = false; - _filteredSel = _searchedSel = _peopleSel = _hashtagSel = -1; + _importantSwitchSelected = false; + _selected = nullptr; + _filteredSelected = _searchedSelected = _peerSearchSelected = _hashtagSelected = -1; setCursor(style::cur_default); } } @@ -647,22 +837,25 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) { } if (e->reason() == QContextMenuEvent::Mouse) { - lastMousePos = e->globalPos(); - _selByMouse = true; - onUpdateSelected(true); + _mouseSelection = true; + updateSelected(); } History *history = nullptr; if (_state == DefaultState) { - if (_sel) history = _sel->history(); + if (_selected) history = _selected->history(); } else if (_state == FilteredState || _state == SearchedState) { - if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) { - history = _filterResults[_filteredSel]->history(); + if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) { + history = _filterResults[_filteredSelected]->history(); } } if (!history) return; _menuPeer = history->peer; + if (_pressButton != Qt::LeftButton) { + mousePressReleased(_pressButton); + } + _menu = new Ui::PopupMenu(); App::main()->fillPeerMenu(_menuPeer, [this](const QString &text, base::lambda &&callback) { return _menu->addAction(text, std_::move(callback)); @@ -678,30 +871,30 @@ void DialogsInner::onMenuDestroyed(QObject *obj) { if (_menuPeer) { updateSelectedRow(base::take(_menuPeer)); } - lastMousePos = QCursor::pos(); - if (rect().contains(mapFromGlobal(lastMousePos))) { - _selByMouse = true; + auto localPos = mapFromGlobal(QCursor::pos()); + if (rect().contains(localPos)) { + _mouseSelection = true; setMouseTracking(true); - onUpdateSelected(true); + updateSelected(localPos); } } } void DialogsInner::onParentGeometryChanged() { - lastMousePos = QCursor::pos(); - if (rect().contains(mapFromGlobal(lastMousePos))) { + auto localPos = mapFromGlobal(QCursor::pos()); + if (rect().contains(localPos)) { setMouseTracking(true); - onUpdateSelected(true); + updateSelected(localPos); } } void DialogsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { - dialogs->peerNameChanged(Dialogs::Mode::All, peer, oldNames, oldChars); - if (importantDialogs) { - importantDialogs->peerNameChanged(Dialogs::Mode::Important, peer, oldNames, oldChars); + _dialogs->peerNameChanged(Dialogs::Mode::All, peer, oldNames, oldChars); + if (_dialogsImportant) { + _dialogsImportant->peerNameChanged(Dialogs::Mode::Important, peer, oldNames, oldChars); } - contactsNoDialogs->peerNameChanged(peer, oldNames, oldChars); - contacts->peerNameChanged(peer, oldNames, oldChars); + _contactsNoDialogs->peerNameChanged(peer, oldNames, oldChars); + _contacts->peerNameChanged(peer, oldNames, oldChars); update(); } @@ -731,7 +924,7 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) { _state = DefaultState; _hashtagResults.clear(); _filterResults.clear(); - _peopleResults.clear(); + _peerSearchResults.clear(); _searchResults.clear(); _lastSearchDate = 0; _lastSearchPeer = 0; @@ -743,9 +936,9 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) { _filterResults.clear(); if (!_searchInPeer && !f.isEmpty()) { const Dialogs::List *toFilter = nullptr; - if (!dialogs->isEmpty()) { + if (!_dialogs->isEmpty()) { for (fi = fb; fi != fe; ++fi) { - auto found = dialogs->filtered(fi->at(0)); + auto found = _dialogs->filtered(fi->at(0)); if (found->isEmpty()) { toFilter = nullptr; break; @@ -756,9 +949,9 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) { } } const Dialogs::List *toFilterContacts = nullptr; - if (!contactsNoDialogs->isEmpty()) { + if (!_contactsNoDialogs->isEmpty()) { for (fi = fb; fi != fe; ++fi) { - auto found = contactsNoDialogs->filtered(fi->at(0)); + auto found = _contactsNoDialogs->filtered(fi->at(0)); if (found->isEmpty()) { toFilterContacts = nullptr; break; @@ -813,7 +1006,7 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) { } } refresh(true); - setMouseSel(false, true); + setMouseSelection(false, true); } if (_state != DefaultState) { emit searchMessages(); @@ -826,7 +1019,7 @@ void DialogsInner::onHashtagFilterUpdate(QStringRef newFilter) { if (!_hashtagResults.isEmpty()) { _hashtagResults.clear(); refresh(true); - setMouseSel(false, true); + setMouseSelection(false, true); } return; } @@ -834,33 +1027,28 @@ void DialogsInner::onHashtagFilterUpdate(QStringRef newFilter) { if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) { Local::readRecentHashtagsAndBots(); } - const RecentHashtagPack &recent(cRecentSearchHashtags()); + auto &recent = cRecentSearchHashtags(); _hashtagResults.clear(); if (!recent.isEmpty()) { - _hashtagResults.reserve(qMin(recent.size(), 5)); - for (RecentHashtagPack::const_iterator i = recent.cbegin(), e = recent.cend(); i != e; ++i) { + _hashtagResults.reserve(qMin(recent.size(), kHashtagResultsLimit)); + for (auto i = recent.cbegin(), e = recent.cend(); i != e; ++i) { if (i->first.startsWith(_hashtagFilter.midRef(1), Qt::CaseInsensitive) && i->first.size() + 1 != newFilter.size()) { - _hashtagResults.push_back(i->first); - if (_hashtagResults.size() == 5) break; + _hashtagResults.push_back(std_::make_unique(i->first)); + if (_hashtagResults.size() == kHashtagResultsLimit) break; } } } refresh(true); - setMouseSel(false, true); + setMouseSelection(false, true); } DialogsInner::~DialogsInner() { clearSearchResults(); } -void DialogsInner::clearSearchResults(bool clearPeople) { - if (clearPeople) _peopleResults.clear(); - if (!_searchResults.isEmpty()) { - for (SearchResults::const_iterator i = _searchResults.cbegin(), e = _searchResults.cend(); i != e; ++i) { - delete *i; - } - _searchResults.clear(); - } +void DialogsInner::clearSearchResults(bool clearPeerSearchResults) { + if (clearPeerSearchResults) _peerSearchResults.clear(); + _searchResults.clear(); _searchedCount = _searchedMigratedCount = 0; _lastSearchDate = 0; _lastSearchPeer = 0; @@ -868,28 +1056,37 @@ void DialogsInner::clearSearchResults(bool clearPeople) { } PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) { - lastMousePos = globalPos; - _selByMouse = true; - onUpdateSelected(true); + _mouseSelection = true; + updateSelected(mapFromGlobal(globalPos)); if (_state == DefaultState) { - if (_sel) return _sel->history()->peer; + if (_selected) return _selected->history()->peer; } else if (_state == FilteredState || _state == SearchedState) { - if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) { - return _filterResults[_filteredSel]->history()->peer; - } else if (_peopleSel >= 0 && _peopleSel < _peopleResults.size()) { - return _peopleResults[_peopleSel]; - } else if (_searchedSel >= 0 && _searchedSel < _searchResults.size()) { - return _searchResults[_searchedSel]->item()->history()->peer; + if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) { + return _filterResults[_filteredSelected]->history()->peer; + } else if (_peerSearchSelected >= 0 && _peerSearchSelected < _peerSearchResults.size()) { + return _peerSearchResults[_peerSearchSelected]->peer; + } else if (_searchedSelected >= 0 && _searchedSelected < _searchResults.size()) { + return _searchResults[_searchedSelected]->item()->history()->peer; + } + } + return nullptr; +} + +void DialogsInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { + _visibleAreaHeight = visibleBottom - visibleTop; + loadPeerPhotos(visibleTop); + if (visibleTop + PreloadHeightsCount * (visibleBottom - visibleTop) >= height()) { + if (_loadMoreCallback) { + _loadMoreCallback(); } } - return 0; } void DialogsInner::itemRemoved(HistoryItem *item) { int wasCount = _searchResults.size(); - for (int i = 0; i < _searchResults.size();) { - if (_searchResults[i]->item() == item) { - _searchResults.remove(i); + for (auto i = _searchResults.begin(); i != _searchResults.end();) { + if ((*i)->item() == item) { + i = _searchResults.erase(i); if (item->history()->peer == _searchInMigrated) { if (_searchedMigratedCount > 0) --_searchedMigratedCount; } else { @@ -935,7 +1132,7 @@ void DialogsInner::dialogsReceived(const QVector &added) { if (!history->lastMsgDate.isNull()) { addSavedPeersAfter(history->lastMsgDate); } - contactsNoDialogs->del(peer); + _contactsNoDialogs->del(peer); if (peer->migrateFrom()) { removeDialog(App::historyLoaded(peer->migrateFrom()->id)); } else if (peer->migrateTo() && peer->migrateTo()->amIn()) { @@ -956,7 +1153,7 @@ void DialogsInner::addSavedPeersAfter(const QDateTime &date) { while (!saved.isEmpty() && (date.isNull() || date < saved.lastKey())) { History *history = App::history(saved.last()->id); history->setChatsListDate(saved.lastKey()); - contactsNoDialogs->del(history->peer); + _contactsNoDialogs->del(history->peer); saved.remove(saved.lastKey(), saved.last()); } } @@ -980,7 +1177,7 @@ bool DialogsInner::searchReceived(const QVector &messages, DialogsSe if (auto peer = App::peerLoaded(peerId)) { if (lastDate) { auto item = App::histories().addNewMessage(message, NewMessageExisting); - _searchResults.push_back(new Dialogs::FakeRow(item)); + _searchResults.push_back(std_::make_unique(item)); lastDateFound = lastDate; if (isGlobalSearch) { _lastSearchDate = lastDateFound; @@ -1013,11 +1210,11 @@ bool DialogsInner::searchReceived(const QVector &messages, DialogsSe return lastDateFound != 0; } -void DialogsInner::peopleReceived(const QString &query, const QVector &people) { - _peopleQuery = query.toLower().trimmed(); - _peopleResults.clear(); - _peopleResults.reserve(people.size()); - for (auto i = people.cbegin(), e = people.cend(); i != e; ++i) { +void DialogsInner::peerSearchReceived(const QString &query, const QVector &result) { + _peerSearchQuery = query.toLower().trimmed(); + _peerSearchResults.clear(); + _peerSearchResults.reserve(result.size()); + for (auto i = result.cbegin(), e = result.cend(); i != e; ++i) { auto peerId = peerFromMTP(*i); if (auto history = App::historyLoaded(peerId)) { if (history->inChatList(Dialogs::Mode::All)) { @@ -1025,7 +1222,7 @@ void DialogsInner::peopleReceived(const QString &query, const QVector & } } if (auto peer = App::peerLoaded(peerId)) { - _peopleResults.push_back(App::peer(peerId)); + _peerSearchResults.push_back(std_::make_unique(App::peer(peerId))); } else { LOG(("API Error: user %1 was not loaded in DialogsInner::peopleReceived()").arg(peerId)); } @@ -1033,10 +1230,12 @@ void DialogsInner::peopleReceived(const QString &query, const QVector & refresh(); } -void DialogsInner::contactsReceived(const QVector &contacts) { - for (QVector::const_iterator i = contacts.cbegin(), e = contacts.cend(); i != e; ++i) { - int32 uid = i->c_contact().vuser_id.v; - if (uid == MTP::authedId() && App::self()) { +void DialogsInner::contactsReceived(const QVector &result) { + for_const (auto contact, result) { + if (contact.type() != mtpc_contact) continue; + + auto userId = contact.c_contact().vuser_id.v; + if (userId == MTP::authedId() && App::self()) { if (App::self()->contact < 1) { App::self()->contact = 1; Notify::userIsContactChanged(App::self()); @@ -1053,33 +1252,41 @@ void DialogsInner::notify_userIsContactChanged(UserData *user, bool fromThisApp) } if (user->contact > 0) { auto history = App::history(user->id); - contacts->addByName(history); + _contacts->addByName(history); if (auto row = shownDialogs()->getRow(user->id)) { if (fromThisApp) { - _sel = row; - _importantSwitchSel = false; + _selected = row; + _importantSwitchSelected = false; } - } else if (!dialogs->contains(user->id)) { - contactsNoDialogs->addByName(history); + } else if (!_dialogs->contains(user->id)) { + _contactsNoDialogs->addByName(history); } } else { - if (_sel && _sel->history()->peer == user) { - _sel = nullptr; + if (_selected && _selected->history()->peer == user) { + _selected = nullptr; } - contactsNoDialogs->del(user); - contacts->del(user); + if (_pressed && _pressed->history()->peer == user) { + setPressed(nullptr); + } + _contactsNoDialogs->del(user); + _contacts->del(user); } refresh(); } void DialogsInner::notify_historyMuteUpdated(History *history) { - if (!importantDialogs || !history->inChatList(Dialogs::Mode::All)) return; + if (!_dialogsImportant || !history->inChatList(Dialogs::Mode::All)) return; if (history->mute()) { - if (_sel && _sel->history() == history && Global::DialogsMode() == Dialogs::Mode::Important) { - _sel = nullptr; + if (Global::DialogsMode() == Dialogs::Mode::Important) { + if (_selected && _selected->history() == history) { + _selected = nullptr; + } + if (_pressed && _pressed->history() == history) { + setPressed(nullptr); + } } - history->removeFromChatList(Dialogs::Mode::Important, importantDialogs.get()); + history->removeFromChatList(Dialogs::Mode::Important, _dialogsImportant.get()); if (Global::DialogsMode() != Dialogs::Mode::Important) { return; } @@ -1087,10 +1294,10 @@ void DialogsInner::notify_historyMuteUpdated(History *history) { } else { bool creating = !history->inChatList(Dialogs::Mode::Important); if (creating) { - history->addToChatList(Dialogs::Mode::Important, importantDialogs.get()); + history->addToChatList(Dialogs::Mode::Important, _dialogsImportant.get()); } - auto changed = history->adjustByPosInChatList(Dialogs::Mode::All, dialogs.get()); + auto changed = history->adjustByPosInChatList(Dialogs::Mode::All, _dialogs.get()); if (Global::DialogsMode() != Dialogs::Mode::Important) { return; @@ -1103,7 +1310,7 @@ void DialogsInner::notify_historyMuteUpdated(History *history) { if (creating) { refresh(); } else if (_state == DefaultState && changed.movedFrom != changed.movedTo) { - update(0, qMin(from, to), fullWidth(), qAbs(from - to) + st::dialogsRowHeight); + update(0, qMin(from, to), getFullWidth(), qAbs(from - to) + st::dialogsRowHeight); } } } @@ -1125,9 +1332,9 @@ void DialogsInner::refresh(bool toTop) { } else { if (!_addContactLnk->isHidden()) _addContactLnk->hide(); if (_state == FilteredState) { - h = searchedOffset() + (_searchResults.count() * st::dialogsRowHeight) + ((_searchResults.isEmpty() && !_searchInPeer) ? -st::searchedBarHeight : 0); + h = searchedOffset() + (_searchResults.size() * st::dialogsRowHeight) + ((_searchResults.isEmpty() && !_searchInPeer) ? -st::searchedBarHeight : 0); } else if (_state == SearchedState) { - h = searchedOffset() + (_searchResults.count() * st::dialogsRowHeight); + h = searchedOffset() + (_searchResults.size() * st::dialogsRowHeight); } } setHeight(h); @@ -1138,14 +1345,14 @@ void DialogsInner::refresh(bool toTop) { update(); } -void DialogsInner::setMouseSel(bool msel, bool toTop) { - _selByMouse = msel; - if (!_selByMouse && toTop) { +void DialogsInner::setMouseSelection(bool mouseSelection, bool toTop) { + _mouseSelection = mouseSelection; + if (!_mouseSelection && toTop) { if (_state == DefaultState) { - _sel = nullptr; - _importantSwitchSel = false; + _selected = nullptr; + _importantSwitchSelected = false; } else if (_state == FilteredState || _state == SearchedState) { // don't select first elem in search - _filteredSel = _peopleSel = _searchedSel = _hashtagSel = -1; + _filteredSelected = _peerSearchSelected = _searchedSelected = _hashtagSelected = -1; setCursor(style::cur_default); } } @@ -1153,14 +1360,14 @@ void DialogsInner::setMouseSel(bool msel, bool toTop) { void DialogsInner::setState(State newState) { _state = newState; + clearIrrelevantState(); if (_state == DefaultState) { clearSearchResults(); - _searchedSel = _peopleSel = _filteredSel = _hashtagSel = -1; - } else if (_state == DefaultState || _state == SearchedState) { + } else if (_state == FilteredState || _state == SearchedState) { _hashtagResults.clear(); - _hashtagSel = -1; + _hashtagSelected = -1; _filterResults.clear(); - _filteredSel = -1; + _filteredSelected = -1; } onFilterUpdate(_filter, true); } @@ -1193,7 +1400,7 @@ void DialogsInner::clearFilter() { } _hashtagResults.clear(); _filterResults.clear(); - _peopleResults.clear(); + _peerSearchResults.clear(); _searchResults.clear(); _lastSearchDate = 0; _lastSearchPeer = 0; @@ -1205,79 +1412,79 @@ void DialogsInner::clearFilter() { void DialogsInner::selectSkip(int32 direction) { if (_state == DefaultState) { - if (_importantSwitchSel) { + if (_importantSwitchSelected) { if (!shownDialogs()->isEmpty() && direction > 0) { - _sel = *shownDialogs()->cbegin(); - _importantSwitchSel = false; + _selected = *shownDialogs()->cbegin(); + _importantSwitchSelected = false; } else { return; } - } else if (!_sel) { - if (importantDialogs) { - _importantSwitchSel = true; + } else if (!_selected) { + if (_dialogsImportant) { + _importantSwitchSelected = true; } else if (!shownDialogs()->isEmpty() && direction > 0) { - _sel = *shownDialogs()->cbegin(); + _selected = *shownDialogs()->cbegin(); } else { return; } } else if (direction > 0) { - auto next = shownDialogs()->cfind(_sel); + auto next = shownDialogs()->cfind(_selected); if (++next != shownDialogs()->cend()) { - _sel = *next; + _selected = *next; } } else { - auto prev = shownDialogs()->cfind(_sel); + auto prev = shownDialogs()->cfind(_selected); if (prev != shownDialogs()->cbegin()) { - _sel = *(--prev); - } else if (importantDialogs) { - _importantSwitchSel = true; - _sel = nullptr; + _selected = *(--prev); + } else if (_dialogsImportant) { + _importantSwitchSelected = true; + _selected = nullptr; } } - if (_importantSwitchSel || _sel) { - int fromY = _importantSwitchSel ? 0 : (dialogsOffset() + _sel->pos() * st::dialogsRowHeight); + if (_importantSwitchSelected || _selected) { + int fromY = _importantSwitchSelected ? 0 : (dialogsOffset() + _selected->pos() * st::dialogsRowHeight); emit mustScrollTo(fromY, fromY + st::dialogsRowHeight); } } else if (_state == FilteredState || _state == SearchedState) { - if (_hashtagResults.isEmpty() && _filterResults.isEmpty() && _peopleResults.isEmpty() && _searchResults.isEmpty()) return; - if ((_hashtagSel < 0 || _hashtagSel >= _hashtagResults.size()) && - (_filteredSel < 0 || _filteredSel >= _filterResults.size()) && - (_peopleSel < 0 || _peopleSel >= _peopleResults.size()) && - (_searchedSel < 0 || _searchedSel >= _searchResults.size())) { - if (_hashtagResults.isEmpty() && _filterResults.isEmpty() && _peopleResults.isEmpty()) { - _searchedSel = 0; + if (_hashtagResults.isEmpty() && _filterResults.isEmpty() && _peerSearchResults.isEmpty() && _searchResults.isEmpty()) return; + if ((_hashtagSelected < 0 || _hashtagSelected >= _hashtagResults.size()) && + (_filteredSelected < 0 || _filteredSelected >= _filterResults.size()) && + (_peerSearchSelected < 0 || _peerSearchSelected >= _peerSearchResults.size()) && + (_searchedSelected < 0 || _searchedSelected >= _searchResults.size())) { + if (_hashtagResults.isEmpty() && _filterResults.isEmpty() && _peerSearchResults.isEmpty()) { + _searchedSelected = 0; } else if (_hashtagResults.isEmpty() && _filterResults.isEmpty()) { - _peopleSel = 0; + _peerSearchSelected = 0; } else if (_hashtagResults.isEmpty()) { - _filteredSel = 0; + _filteredSelected = 0; } else { - _hashtagSel = 0; + _hashtagSelected = 0; } } else { - int32 cur = (_hashtagSel >= 0 && _hashtagSel < _hashtagResults.size()) ? _hashtagSel : ((_filteredSel >= 0 && _filteredSel < _filterResults.size()) ? (_hashtagResults.size() + _filteredSel) : ((_peopleSel >= 0 && _peopleSel < _peopleResults.size()) ? (_peopleSel + _filterResults.size() + _hashtagResults.size()) : (_searchedSel + _peopleResults.size() + _filterResults.size() + _hashtagResults.size()))); - cur = snap(cur + direction, 0, _hashtagResults.size() + _filterResults.size() + _peopleResults.size() + _searchResults.size() - 1); + int32 cur = (_hashtagSelected >= 0 && _hashtagSelected < _hashtagResults.size()) ? _hashtagSelected : ((_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) ? (_hashtagResults.size() + _filteredSelected) : ((_peerSearchSelected >= 0 && _peerSearchSelected < _peerSearchResults.size()) ? (_peerSearchSelected + _filterResults.size() + _hashtagResults.size()) : (_searchedSelected + _peerSearchResults.size() + _filterResults.size() + _hashtagResults.size()))); + cur = snap(cur + direction, 0, _hashtagResults.size() + _filterResults.size() + _peerSearchResults.size() + _searchResults.size() - 1); if (cur < _hashtagResults.size()) { - _hashtagSel = cur; - _filteredSel = _peopleSel = _searchedSel = -1; + _hashtagSelected = cur; + _filteredSelected = _peerSearchSelected = _searchedSelected = -1; } else if (cur < _hashtagResults.size() + _filterResults.size()) { - _filteredSel = cur - _hashtagResults.size(); - _hashtagSel = _peopleSel = _searchedSel = -1; - } else if (cur < _hashtagResults.size() + _filterResults.size() + _peopleResults.size()) { - _peopleSel = cur - _hashtagResults.size() - _filterResults.size(); - _hashtagSel = _filteredSel = _searchedSel = -1; + _filteredSelected = cur - _hashtagResults.size(); + _hashtagSelected = _peerSearchSelected = _searchedSelected = -1; + } else if (cur < _hashtagResults.size() + _filterResults.size() + _peerSearchResults.size()) { + _peerSearchSelected = cur - _hashtagResults.size() - _filterResults.size(); + _hashtagSelected = _filteredSelected = _searchedSelected = -1; } else { - _hashtagSel = _filteredSel = _peopleSel = -1; - _searchedSel = cur - _hashtagResults.size() - _filterResults.size() - _peopleResults.size(); + _hashtagSelected = _filteredSelected = _peerSearchSelected = -1; + _searchedSelected = cur - _hashtagResults.size() - _filterResults.size() - _peerSearchResults.size(); } } - if (_hashtagSel >= 0 && _hashtagSel < _hashtagResults.size()) { - emit mustScrollTo(_hashtagSel * st::mentionHeight, (_hashtagSel + 1) * st::mentionHeight); - } else if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) { - emit mustScrollTo(filteredOffset() + _filteredSel * st::dialogsRowHeight, filteredOffset() + (_filteredSel + 1) * st::dialogsRowHeight); - } else if (_peopleSel >= 0 && _peopleSel < _peopleResults.size()) { - emit mustScrollTo(peopleOffset() + _peopleSel * st::dialogsRowHeight + (_peopleSel ? 0 : -st::searchedBarHeight), peopleOffset() + (_peopleSel + 1) * st::dialogsRowHeight); + if (_hashtagSelected >= 0 && _hashtagSelected < _hashtagResults.size()) { + emit mustScrollTo(_hashtagSelected * st::mentionHeight, (_hashtagSelected + 1) * st::mentionHeight); + } else if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) { + emit mustScrollTo(filteredOffset() + _filteredSelected * st::dialogsRowHeight, filteredOffset() + (_filteredSelected + 1) * st::dialogsRowHeight); + } else if (_peerSearchSelected >= 0 && _peerSearchSelected < _peerSearchResults.size()) { + emit mustScrollTo(peerSearchOffset() + _peerSearchSelected * st::dialogsRowHeight + (_peerSearchSelected ? 0 : -st::searchedBarHeight), peerSearchOffset() + (_peerSearchSelected + 1) * st::dialogsRowHeight); } else { - emit mustScrollTo(searchedOffset() + _searchedSel * st::dialogsRowHeight + (_searchedSel ? 0 : -st::searchedBarHeight), searchedOffset() + (_searchedSel + 1) * st::dialogsRowHeight); + emit mustScrollTo(searchedOffset() + _searchedSelected * st::dialogsRowHeight + (_searchedSelected ? 0 : -st::searchedBarHeight), searchedOffset() + (_searchedSelected + 1) * st::dialogsRowHeight); } } update(); @@ -1315,29 +1522,29 @@ void DialogsInner::scrollToPeer(const PeerId &peer, MsgId msgId) { void DialogsInner::selectSkipPage(int32 pixels, int32 direction) { int toSkip = pixels / int(st::dialogsRowHeight); if (_state == DefaultState) { - if (!_sel) { + if (!_selected) { if (direction > 0 && !shownDialogs()->isEmpty()) { - _sel = *shownDialogs()->cbegin(); - _importantSwitchSel = false; + _selected = *shownDialogs()->cbegin(); + _importantSwitchSelected = false; } else { return; } } if (direction > 0) { - for (auto i = shownDialogs()->cfind(_sel), end = shownDialogs()->cend(); i != end && (toSkip--); ++i) { - _sel = *i; + for (auto i = shownDialogs()->cfind(_selected), end = shownDialogs()->cend(); i != end && (toSkip--); ++i) { + _selected = *i; } } else { - for (auto i = shownDialogs()->cfind(_sel), b = shownDialogs()->cbegin(); i != b && (toSkip--);) { - _sel = *(--i); + for (auto i = shownDialogs()->cfind(_selected), b = shownDialogs()->cbegin(); i != b && (toSkip--);) { + _selected = *(--i); } - if (toSkip && importantDialogs) { - _importantSwitchSel = true; - _sel = nullptr; + if (toSkip && _dialogsImportant) { + _importantSwitchSelected = true; + _selected = nullptr; } } - if (_importantSwitchSel || _sel) { - int fromY = (_importantSwitchSel ? 0 : (dialogsOffset() + _sel->pos() * st::dialogsRowHeight)); + if (_importantSwitchSelected || _selected) { + int fromY = (_importantSwitchSelected ? 0 : (dialogsOffset() + _selected->pos() * st::dialogsRowHeight)); emit mustScrollTo(fromY, fromY + st::dialogsRowHeight); } } else { @@ -1346,13 +1553,14 @@ void DialogsInner::selectSkipPage(int32 pixels, int32 direction) { update(); } -void DialogsInner::loadPeerPhotos(int32 yFrom) { +void DialogsInner::loadPeerPhotos(int visibleTop) { if (!parentWidget()) return; - int32 yTo = yFrom + parentWidget()->height() * 5; + auto yFrom = visibleTop; + auto yTo = visibleTop + _visibleAreaHeight * (PreloadHeightsCount + 1); MTP::clearLoaderPriorities(); if (_state == DefaultState) { - int32 otherStart = shownDialogs()->size() * st::dialogsRowHeight; + auto otherStart = shownDialogs()->size() * st::dialogsRowHeight; if (yFrom < otherStart) { for (auto i = shownDialogs()->cfind(yFrom, st::dialogsRowHeight), end = shownDialogs()->cend(); i != end; ++i) { if (((*i)->pos() * st::dialogsRowHeight) >= yTo) { @@ -1379,18 +1587,18 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) { from = (yFrom > filteredOffset() + st::searchedBarHeight ? ((yFrom - filteredOffset() - st::searchedBarHeight) / int32(st::dialogsRowHeight)) : 0) - _filterResults.size(); if (from < 0) from = 0; - if (from < _peopleResults.size()) { + if (from < _peerSearchResults.size()) { int32 to = (yTo > filteredOffset() + st::searchedBarHeight ? ((yTo - filteredOffset() - st::searchedBarHeight) / int32(st::dialogsRowHeight)) : 0) - _filterResults.size() + 1, w = width(); - if (to > _peopleResults.size()) to = _peopleResults.size(); + if (to > _peerSearchResults.size()) to = _peerSearchResults.size(); for (; from < to; ++from) { - _peopleResults[from]->loadUserpic(); + _peerSearchResults[from]->peer->loadUserpic(); } } - from = (yFrom > filteredOffset() + ((_peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight) ? ((yFrom - filteredOffset() - (_peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dialogsRowHeight)) : 0) - _filterResults.size() - _peopleResults.size(); + from = (yFrom > filteredOffset() + ((_peerSearchResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight) ? ((yFrom - filteredOffset() - (_peerSearchResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dialogsRowHeight)) : 0) - _filterResults.size() - _peerSearchResults.size(); if (from < 0) from = 0; if (from < _searchResults.size()) { - int32 to = (yTo > filteredOffset() + (_peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight ? ((yTo - filteredOffset() - (_peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dialogsRowHeight)) : 0) - _filterResults.size() - _peopleResults.size() + 1, w = width(); + int32 to = (yTo > filteredOffset() + (_peerSearchResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight ? ((yTo - filteredOffset() - (_peerSearchResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dialogsRowHeight)) : 0) - _filterResults.size() - _peerSearchResults.size() + 1, w = width(); if (to > _searchResults.size()) to = _searchResults.size(); for (; from < to; ++from) { @@ -1404,7 +1612,7 @@ bool DialogsInner::choosePeer() { History *history = nullptr; MsgId msgId = ShowAtUnreadMsgId; if (_state == DefaultState) { - if (_importantSwitchSel && importantDialogs) { + if (_importantSwitchSelected && _dialogsImportant) { clearSelection(); if (Global::DialogsMode() == Dialogs::Mode::All) { Global::SetDialogsMode(Dialogs::Mode::Important); @@ -1413,20 +1621,18 @@ bool DialogsInner::choosePeer() { } Local::writeUserSettings(); refresh(); - _importantSwitchSel = true; + _importantSwitchSelected = true; return true; - } else if (_sel) { - history = _sel->history(); + } else if (_selected) { + history = _selected->history(); } } else if (_state == FilteredState || _state == SearchedState) { - if (_hashtagSel >= 0 && _hashtagSel < _hashtagResults.size()) { - QString hashtag = _hashtagResults.at(_hashtagSel); - if (_overDelete) { - lastMousePos = QCursor::pos(); - + if (_hashtagSelected >= 0 && _hashtagSelected < _hashtagResults.size()) { + auto &hashtag = _hashtagResults[_hashtagSelected]; + if (_hashtagDeleteSelected) { RecentHashtagPack recent(cRecentSearchHashtags()); for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) { - if (i->first == hashtag) { + if (i->first == hashtag->tag) { i = recent.erase(i); } else { ++i; @@ -1436,35 +1642,35 @@ bool DialogsInner::choosePeer() { Local::writeRecentHashtagsAndBots(); emit refreshHashtags(); - _selByMouse = true; - onUpdateSelected(true); + _mouseSelection = true; + updateSelected(); } else { - saveRecentHashtags('#' + hashtag); - emit completeHashtag(hashtag); + saveRecentHashtags('#' + hashtag->tag); + emit completeHashtag(hashtag->tag); } return true; } - if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) { - history = _filterResults[_filteredSel]->history(); - } else if (_peopleSel >= 0 && _peopleSel < _peopleResults.size()) { - history = App::history(_peopleResults[_peopleSel]->id); - } else if (_searchedSel >= 0 && _searchedSel < _searchResults.size()) { - history = _searchResults[_searchedSel]->item()->history(); - msgId = _searchResults[_searchedSel]->item()->id; + if (_filteredSelected >= 0 && _filteredSelected < _filterResults.size()) { + history = _filterResults[_filteredSelected]->history(); + } else if (_peerSearchSelected >= 0 && _peerSearchSelected < _peerSearchResults.size()) { + history = App::history(_peerSearchResults[_peerSearchSelected]->peer->id); + } else if (_searchedSelected >= 0 && _searchedSelected < _searchResults.size()) { + history = _searchResults[_searchedSelected]->item()->history(); + msgId = _searchResults[_searchedSelected]->item()->id; } } if (history) { if (msgId > 0) { saveRecentHashtags(_filter); } - bool chosen = (!App::main()->selectingPeer(true) && (_state == FilteredState || _state == SearchedState) && _filteredSel >= 0 && _filteredSel < _filterResults.size()); + bool chosen = (!App::main()->selectingPeer(true) && (_state == FilteredState || _state == SearchedState) && _filteredSelected >= 0 && _filteredSelected < _filterResults.size()); App::main()->choosePeer(history->peer->id, msgId); if (chosen) { emit searchResultChosen(); } updateSelectedRow(); - _sel = nullptr; - _filteredSel = _peopleSel = _searchedSel = _hashtagSel = -1; + _selected = nullptr; + _hashtagSelected = _filteredSelected = _peerSearchSelected = _searchedSelected = -1; return true; } return false; @@ -1499,19 +1705,19 @@ void DialogsInner::saveRecentHashtags(const QString &text) { } void DialogsInner::destroyData() { - _sel = nullptr; - _hashtagSel = -1; + _selected = nullptr; + _hashtagSelected = -1; _hashtagResults.clear(); - _filteredSel = -1; + _filteredSelected = -1; _filterResults.clear(); _filter.clear(); - _searchedSel = _peopleSel = -1; + _searchedSelected = _peerSearchSelected = -1; clearSearchResults(); - contacts = nullptr; - contactsNoDialogs = nullptr; - dialogs = nullptr; - if (importantDialogs) { - importantDialogs = nullptr; + _contacts = nullptr; + _contactsNoDialogs = nullptr; + _dialogs = nullptr; + if (_dialogsImportant) { + _dialogsImportant = nullptr; } } @@ -1535,9 +1741,9 @@ void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&ou return; } else if (_state == FilteredState || _state == SearchedState) { if (inMsg && !_searchResults.isEmpty()) { - for (SearchResults::const_iterator b = _searchResults.cbegin(), i = b + 1, e = _searchResults.cend(); i != e; ++i) { + for (auto b = _searchResults.cbegin(), i = b + 1, e = _searchResults.cend(); i != e; ++i) { if ((*i)->item()->history()->peer == inPeer && (*i)->item()->id == inMsg) { - SearchResults::const_iterator j = i - 1; + auto j = i - 1; outPeer = (*j)->item()->history()->peer; outMsg = (*j)->item()->id; return; @@ -1545,27 +1751,27 @@ void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&ou } if (_searchResults.at(0)->item()->history()->peer == inPeer && _searchResults.at(0)->item()->id == inMsg) { outMsg = ShowAtUnreadMsgId; - if (_peopleResults.isEmpty()) { + if (_peerSearchResults.isEmpty()) { if (_filterResults.isEmpty()) { outPeer = nullptr; } else { outPeer = _filterResults.back()->history()->peer; } } else { - outPeer = _peopleResults.back(); + outPeer = _peerSearchResults.back()->peer; } return; } } - if (!_peopleResults.isEmpty() && _peopleResults.at(0) == inPeer) { + if (!_peerSearchResults.isEmpty() && _peerSearchResults[0]->peer == inPeer) { outPeer = _filterResults.isEmpty() ? 0 : _filterResults.back()->history()->peer; outMsg = ShowAtUnreadMsgId; return; } - if (!_peopleResults.isEmpty()) { - for (PeopleResults::const_iterator b = _peopleResults.cbegin(), i = b + 1, e = _peopleResults.cend(); i != e; ++i) { - if ((*i) == inPeer) { - outPeer = (*(i - 1)); + if (!_peerSearchResults.isEmpty()) { + for (auto b = _peerSearchResults.cbegin(), i = b + 1, e = _peerSearchResults.cend(); i != e; ++i) { + if ((*i)->peer == inPeer) { + outPeer = (*(i - 1))->peer; outMsg = ShowAtUnreadMsgId; return; } @@ -1577,7 +1783,7 @@ void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&ou return; } - for (FilteredDialogs::const_iterator b = _filterResults.cbegin(), i = b + 1, e = _filterResults.cend(); i != e; ++i) { + for (auto b = _filterResults.cbegin(), i = b + 1, e = _filterResults.cend(); i != e; ++i) { if ((*i)->history()->peer == inPeer) { outPeer = (*(i - 1))->history()->peer; outMsg = ShowAtUnreadMsgId; @@ -1609,7 +1815,7 @@ void DialogsInner::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&out return; } else if (_state == FilteredState || _state == SearchedState) { if (inMsg) { - for (SearchResults::const_iterator i = _searchResults.cbegin(), e = _searchResults.cend(); i != e; ++i) { + for (auto i = _searchResults.cbegin(), e = _searchResults.cend(); i != e; ++i) { if ((*i)->item()->history()->peer == inPeer && (*i)->item()->id == inMsg) { ++i; outPeer = (i == e) ? nullptr : (*i)->item()->history()->peer; @@ -1618,14 +1824,14 @@ void DialogsInner::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&out } } } - for (PeopleResults::const_iterator i = _peopleResults.cbegin(), e = _peopleResults.cend(); i != e; ++i) { - if ((*i) == inPeer) { + for (auto i = _peerSearchResults.cbegin(), e = _peerSearchResults.cend(); i != e; ++i) { + if ((*i)->peer == inPeer) { ++i; if (i == e && !_searchResults.isEmpty()) { outPeer = _searchResults.front()->item()->history()->peer; outMsg = _searchResults.front()->item()->id; } else { - outPeer = (i == e) ? nullptr : (*i); + outPeer = (i == e) ? nullptr : (*i)->peer; outMsg = ShowAtUnreadMsgId; } return; @@ -1634,8 +1840,8 @@ void DialogsInner::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&out for (FilteredDialogs::const_iterator i = _filterResults.cbegin(), e = _filterResults.cend(); i != e; ++i) { if ((*i)->history()->peer == inPeer) { ++i; - if (i == e && !_peopleResults.isEmpty()) { - outPeer = _peopleResults.front(); + if (i == e && !_peerSearchResults.isEmpty()) { + outPeer = _peerSearchResults.front()->peer; outMsg = ShowAtUnreadMsgId; } else if (i == e && !_searchResults.isEmpty()) { outPeer = _searchResults.front()->item()->history()->peer; @@ -1653,23 +1859,11 @@ void DialogsInner::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&out } Dialogs::IndexedList *DialogsInner::contactsList() { - return contacts.get(); + return _contacts.get(); } Dialogs::IndexedList *DialogsInner::dialogsList() { - return dialogs.get(); -} - -DialogsInner::FilteredDialogs &DialogsInner::filteredList() { - return _filterResults; -} - -DialogsInner::PeopleResults &DialogsInner::peopleList() { - return _peopleResults; -} - -DialogsInner::SearchResults &DialogsInner::searchList() { - return _searchResults; + return _dialogs.get(); } int32 DialogsInner::lastSearchDate() const { @@ -1694,8 +1888,7 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) , _cancelSearch(this, st::dialogsCancelSearch) , _lockUnlock(this, st::dialogsLock) , _scroll(this, st::dialogsScroll) -, _inner(this, parent) -, _a_show(animation(this, &DialogsWidget::step_show)) { +, _inner(this, parent) { _scroll->setOwnedWidget(_inner); connect(_inner, SIGNAL(mustScrollTo(int,int)), _scroll, SLOT(scrollToY(int,int))); connect(_inner, SIGNAL(dialogMoved(int,int)), this, SLOT(onDialogMoved(int,int))); @@ -1723,9 +1916,9 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) _lockUnlock->setVisible(Global::LocalPasscode()); subscribe(Global::RefLocalPasscodeChanged(), [this] { updateLockUnlockVisibility(); }); _lockUnlock->setClickedCallback([this] { - _lockUnlock->setIcon(&st::dialogsUnlockIcon, &st::dialogsUnlockIconOver); + _lockUnlock->setIconOverride(&st::dialogsUnlockIcon, &st::dialogsUnlockIconOver); App::wnd()->setupPasscode(); - _lockUnlock->setIcon(nullptr); + _lockUnlock->setIconOverride(nullptr); }); _mainMenuToggle->setClickedCallback([this] { showMainMenu(); }); @@ -1737,6 +1930,14 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) _searchTimer.setSingleShot(true); connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchMessages())); + _inner->setLoadMoreCallback([this] { + if (_inner->state() == DialogsInner::SearchedState || (_inner->state() == DialogsInner::FilteredState && _searchInMigrated && _searchFull && !_searchFullMigrated)) { + onSearchMore(); + } else { + loadDialogs(); + } + }); + _filter->setFocusPolicy(Qt::StrongFocus); _filter->customUpDown(true); } @@ -1780,8 +1981,8 @@ void DialogsWidget::dlgUpdated(Dialogs::Mode list, Dialogs::Row *row) { _inner->dlgUpdated(list, row); } -void DialogsWidget::dlgUpdated(History *row, MsgId msgId) { - _inner->dlgUpdated(row, msgId); +void DialogsWidget::dlgUpdated(PeerData *peer, MsgId msgId) { + _inner->dlgUpdated(peer, msgId); } void DialogsWidget::dialogsToUp() { @@ -1796,15 +1997,15 @@ void DialogsWidget::showFast() { } void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms) { - if (App::app()) App::app()->mtpPause(); + _showDirection = direction; + + _a_show.finish(); _cacheUnder = params.oldContentCache; show(); updateForwardBar(); _cacheOver = App::main()->grabForShowAnimation(params); - _a_show.stop(); - _scroll->hide(); _mainMenuToggle->hide(); if (_forwardCancel) _forwardCancel->hide(); @@ -1813,28 +2014,15 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window: _lockUnlock->hide(); int delta = st::slideShift; - if (direction == Window::SlideDirection::FromLeft) { - a_progress = anim::fvalue(1, 0); + if (_showDirection == Window::SlideDirection::FromLeft) { std::swap(_cacheUnder, _cacheOver); - a_coordUnder = anim::ivalue(-delta, 0); - a_coordOver = anim::ivalue(0, width()); - } else { - a_progress = anim::fvalue(0, 1); - a_coordUnder = anim::ivalue(0, -delta); - a_coordOver = anim::ivalue(width(), 0); } - _a_show.start(); + _a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition()); } -void DialogsWidget::step_show(float64 ms, bool timer) { - float64 dt = ms / st::slideDuration; - if (dt >= 1) { - _a_show.stop(); - - a_coordUnder.finish(); - a_coordOver.finish(); - a_progress.finish(); - +void DialogsWidget::animationCallback() { + update(); + if (!_a_show.animating()) { _cacheUnder = _cacheOver = QPixmap(); _scroll->show(); @@ -1842,18 +2030,10 @@ void DialogsWidget::step_show(float64 ms, bool timer) { if (_forwardCancel) _forwardCancel->show(); _filter->show(); updateLockUnlockVisibility(); - _a_show.stop(); onFilterUpdate(); if (App::wnd()) App::wnd()->setInnerFocus(); - - if (App::app()) App::app()->mtpUnpause(); - } else { - a_coordUnder.update(dt, Window::SlideAnimation::transition()); - a_coordOver.update(dt, Window::SlideAnimation::transition()); - a_progress.update(dt, Window::SlideAnimation::transition()); } - if (timer) update(); } void DialogsWidget::onCancel() { @@ -1967,14 +2147,8 @@ bool DialogsWidget::dialogsFailed(const RPCError &error, mtpRequestId req) { bool DialogsWidget::onSearchMessages(bool searchCache) { QString q = _filter->getLastText().trimmed(); if (q.isEmpty()) { - if (_searchRequest) { - MTP::cancel(_searchRequest); - _searchRequest = 0; - } - if (_peopleRequest) { - MTP::cancel(_peopleRequest); - _peopleRequest = 0; - } + MTP::cancel(base::take(_searchRequest)); + MTP::cancel(base::take(_peerSearchRequest)); return true; } if (searchCache) { @@ -1982,19 +2156,14 @@ bool DialogsWidget::onSearchMessages(bool searchCache) { if (i != _searchCache.cend()) { _searchQuery = q; _searchFull = _searchFullMigrated = false; - if (_searchRequest) { - MTP::cancel(_searchRequest); - _searchRequest = 0; - } + MTP::cancel(base::take(_searchRequest)); searchReceived(_searchInPeer ? DialogsSearchPeerFromStart : DialogsSearchFromStart, i.value(), 0); return true; } } else if (_searchQuery != q) { _searchQuery = q; _searchFull = _searchFullMigrated = false; - if (_searchRequest) { - MTP::cancel(_searchRequest); - } + MTP::cancel(base::take(_searchRequest)); if (_searchInPeer) { MTPmessages_Search::Flags flags = 0; _searchRequest = MTP::send(MTPmessages_Search(MTP_flags(flags), _searchInPeer->input, MTP_string(_searchQuery), MTP_inputMessagesFilterEmpty(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(SearchPerPage)), rpcDone(&DialogsWidget::searchReceived, DialogsSearchPeerFromStart), rpcFail(&DialogsWidget::searchFailed, DialogsSearchPeerFromStart)); @@ -2005,18 +2174,18 @@ bool DialogsWidget::onSearchMessages(bool searchCache) { } if (!_searchInPeer && q.size() >= MinUsernameLength) { if (searchCache) { - auto i = _peopleCache.constFind(q); - if (i != _peopleCache.cend()) { - _peopleQuery = q; - _peopleRequest = 0; - peopleReceived(i.value(), 0); + auto i = _peerSearchCache.constFind(q); + if (i != _peerSearchCache.cend()) { + _peerSearchQuery = q; + _peerSearchRequest = 0; + peerSearchReceived(i.value(), 0); return true; } - } else if (_peopleQuery != q) { - _peopleQuery = q; - _peopleFull = false; - _peopleRequest = MTP::send(MTPcontacts_Search(MTP_string(_peopleQuery), MTP_int(SearchPeopleLimit)), rpcDone(&DialogsWidget::peopleReceived), rpcFail(&DialogsWidget::peopleFailed)); - _peopleQueries.insert(_peopleRequest, _peopleQuery); + } else if (_peerSearchQuery != q) { + _peerSearchQuery = q; + _peerSearchFull = false; + _peerSearchRequest = MTP::send(MTPcontacts_Search(MTP_string(_peerSearchQuery), MTP_int(SearchPeopleLimit)), rpcDone(&DialogsWidget::peerSearchReceived), rpcFail(&DialogsWidget::peopleFailed)); + _peerSearchQueries.insert(_peerSearchRequest, _peerSearchQuery); } } return false; @@ -2087,10 +2256,10 @@ void DialogsWidget::loadDialogs() { _dialogsRequest = MTP::send(MTPmessages_GetDialogs(MTP_int(_dialogsOffsetDate), MTP_int(_dialogsOffsetId), _dialogsOffsetPeer ? _dialogsOffsetPeer->input : MTP_inputPeerEmpty(), MTP_int(loadCount)), rpcDone(&DialogsWidget::dialogsReceived), rpcFail(&DialogsWidget::dialogsFailed)); } -void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &contacts) { +void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &result) { cSetContactsReceived(true); - if (contacts.type() == mtpc_contacts_contacts) { - const auto &d(contacts.c_contacts_contacts()); + if (result.type() == mtpc_contacts_contacts) { + auto &d = result.c_contacts_contacts(); App::feedUsers(d.vusers); _inner->contactsReceived(d.vcontacts.c_vector().v); } @@ -2170,27 +2339,27 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa } } -void DialogsWidget::peopleReceived(const MTPcontacts_Found &result, mtpRequestId req) { - auto q = _peopleQuery; +void DialogsWidget::peerSearchReceived(const MTPcontacts_Found &result, mtpRequestId req) { + auto q = _peerSearchQuery; if (_inner->state() == DialogsInner::FilteredState || _inner->state() == DialogsInner::SearchedState) { - auto i = _peopleQueries.find(req); - if (i != _peopleQueries.cend()) { + auto i = _peerSearchQueries.find(req); + if (i != _peerSearchQueries.cend()) { q = i.value(); - _peopleCache[q] = result; - _peopleQueries.erase(i); + _peerSearchCache[q] = result; + _peerSearchQueries.erase(i); } } - if (_peopleRequest == req) { + if (_peerSearchRequest == req) { switch (result.type()) { case mtpc_contacts_found: { auto &d = result.c_contacts_found(); App::feedUsers(d.vusers); App::feedChats(d.vchats); - _inner->peopleReceived(q, d.vresults.c_vector().v); + _inner->peerSearchReceived(q, d.vresults.c_vector().v); } break; } - _peopleRequest = 0; + _peerSearchRequest = 0; onListScroll(); } } @@ -2212,9 +2381,9 @@ bool DialogsWidget::searchFailed(DialogsSearchRequestType type, const RPCError & bool DialogsWidget::peopleFailed(const RPCError &error, mtpRequestId req) { if (MTP::isDefaultHandledError(error)) return false; - if (_peopleRequest == req) { - _peopleRequest = 0; - _peopleFull = true; + if (_peerSearchRequest == req) { + _peerSearchRequest = 0; + _peerSearchFull = true; } return true; } @@ -2292,16 +2461,8 @@ void DialogsWidget::dropEvent(QDropEvent *e) { } void DialogsWidget::onListScroll() { -// if (!App::self()) return; - - _inner->loadPeerPhotos(_scroll->scrollTop()); - if (_inner->state() == DialogsInner::SearchedState || (_inner->state() == DialogsInner::FilteredState && _searchInMigrated && _searchFull && !_searchFullMigrated)) { - if (_scroll->scrollTop() > (_inner->searchList().size() + _inner->filteredList().size() + _inner->peopleList().size()) * st::dialogsRowHeight - PreloadHeightsCount * _scroll->height()) { - onSearchMore(); - } - } else if (_scroll->scrollTop() > _inner->dialogsList()->size() * st::dialogsRowHeight - PreloadHeightsCount * _scroll->height()) { - loadDialogs(); - } + auto scrollTop = _scroll->scrollTop(); + _inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); } void DialogsWidget::onFilterUpdate(bool force) { @@ -2318,9 +2479,9 @@ void DialogsWidget::onFilterUpdate(bool force) { _cancelSearch->showAnimated(); } if (filterText.size() < MinUsernameLength) { - _peopleCache.clear(); - _peopleQueries.clear(); - _peopleQuery = QString(); + _peerSearchCache.clear(); + _peerSearchQueries.clear(); + _peerSearchQuery = QString(); } } @@ -2459,16 +2620,16 @@ void DialogsWidget::keyPressEvent(QKeyEvent *e) { } } } else if (e->key() == Qt::Key_Down) { - _inner->setMouseSel(false); + _inner->setMouseSelection(false); _inner->selectSkip(1); } else if (e->key() == Qt::Key_Up) { - _inner->setMouseSel(false); + _inner->setMouseSelection(false); _inner->selectSkip(-1); } else if (e->key() == Qt::Key_PageDown) { - _inner->setMouseSel(false); + _inner->setMouseSelection(false); _inner->selectSkipPage(_scroll->height(), 1); } else if (e->key() == Qt::Key_PageUp) { - _inner->setMouseSel(false); + _inner->setMouseSelection(false); _inner->selectSkipPage(_scroll->height(), -1); } else { e->ignore(); @@ -2483,17 +2644,22 @@ void DialogsWidget::paintEvent(QPaintEvent *e) { if (r != rect()) { p.setClipRect(r); } + auto progress = _a_show.current(getms(), 1.); if (_a_show.animating()) { - int retina = cIntRetinaFactor(); - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, _cacheUnder.height())); - p.setOpacity(a_progress.current()); - p.fillRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina, st::slideFadeOutBg); + auto retina = cIntRetinaFactor(); + auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft); + auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); + auto coordOver = fromLeft ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto shadow = fromLeft ? (1. - progress) : progress; + if (coordOver > 0) { + p.drawPixmap(QRect(0, 0, coordOver, _cacheUnder.height() / retina), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, _cacheUnder.height())); + p.setOpacity(shadow); + p.fillRect(0, 0, coordOver, _cacheUnder.height() / retina, st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, _cacheOver.height() / retina), _cacheOver, QRect(0, 0, _cacheOver.width(), _cacheOver.height())); - p.setOpacity(a_progress.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), _cacheOver.height() / retina)); + p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, _cacheOver.height() / retina), _cacheOver, QRect(0, 0, _cacheOver.width(), _cacheOver.height())); + p.setOpacity(shadow); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), _cacheOver.height() / retina)); return; } auto aboveTop = 0; diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 4abaaa03b..e3cce1c17 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -57,25 +57,24 @@ public: void dialogsReceived(const QVector &dialogs); void addSavedPeersAfter(const QDateTime &date); void addAllSavedPeers(); - bool searchReceived(const QVector &messages, DialogsSearchRequestType type, int32 fullCount); - void peopleReceived(const QString &query, const QVector &people); + bool searchReceived(const QVector &result, DialogsSearchRequestType type, int32 fullCount); + void peerSearchReceived(const QString &query, const QVector &result); void showMore(int32 pixels); void activate(); - void contactsReceived(const QVector &contacts); + void contactsReceived(const QVector &result); void selectSkip(int32 direction); void selectSkipPage(int32 pixels, int32 direction); void createDialog(History *history); void dlgUpdated(Dialogs::Mode list, Dialogs::Row *row); - void dlgUpdated(History *row, MsgId msgId); + void dlgUpdated(PeerData *peer, MsgId msgId); void removeDialog(History *history); void dragLeft(); - void loadPeerPhotos(int32 yFrom); void clearFilter(); void refresh(bool toTop = false); @@ -88,21 +87,14 @@ public: void peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const; void scrollToPeer(const PeerId &peer, MsgId msgId); - typedef QVector FilteredDialogs; - typedef QVector PeopleResults; - typedef QVector SearchResults; - Dialogs::IndexedList *contactsList(); Dialogs::IndexedList *dialogsList(); - FilteredDialogs &filteredList(); - PeopleResults &peopleList(); - SearchResults &searchList(); int32 lastSearchDate() const; PeerData *lastSearchPeer() const; MsgId lastSearchId() const; MsgId lastSearchMigratedId() const; - void setMouseSel(bool msel, bool toTop = false); + void setMouseSelection(bool mouseSelection, bool toTop = false); enum State { DefaultState = 0, @@ -120,7 +112,10 @@ public: PeerData *updateFromParentDrag(QPoint globalPos); - void updateNotifySettings(PeerData *peer); + void setLoadMoreCallback(base::lambda &&callback) { + _loadMoreCallback = std_::move(callback); + } + void setVisibleTopBottom(int visibleTop, int visibleBottom) override; void notify_userIsContactChanged(UserData *user, bool fromThisApp); void notify_historyMuteUpdated(History *history); @@ -128,7 +123,6 @@ public: ~DialogsInner(); public slots: - void onUpdateSelected(bool force = false); void onParentGeometryChanged(); void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); void onPeerPhotoChanged(PeerData *peer); @@ -149,67 +143,109 @@ protected: void paintRegion(Painter &p, const QRegion ®ion, bool paintingOther) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; void resizeEvent(QResizeEvent *e) override; void enterEvent(QEvent *e) override; void leaveEvent(QEvent *e) override; void contextMenuEvent(QContextMenuEvent *e) override; private: + struct ImportantSwitch; + using DialogsList = std_::unique_ptr; + using FilteredDialogs = QVector; + using SearchResults = std_::vector_of_moveable>; + struct HashtagResult; + using HashtagResults = std_::vector_of_moveable>; + struct PeerSearchResult; + using PeerSearchResults = std_::vector_of_moveable>; + + void mousePressReleased(Qt::MouseButton button); + void clearIrrelevantState(); + void updateSelected() { + updateSelected(mapFromGlobal(QCursor::pos())); + } + void updateSelected(QPoint localPos); + void loadPeerPhotos(int visibleTop); + void setImportantSwitchPressed(bool pressed); + void setPressed(Dialogs::Row *pressed); + void setHashtagPressed(int pressed); + void setFilteredPressed(int pressed); + void setPeerSearchPressed(int pressed); + void setSearchedPressed(int pressed); + bool isPressed() const { + return _importantSwitchPressed || _pressed || (_hashtagPressed >= 0) || (_filteredPressed >= 0) || (_peerSearchPressed >= 0) || (_searchedPressed >= 0); + } + bool isSelected() const { + return _importantSwitchSelected || _selected || (_hashtagSelected >= 0) || (_filteredSelected>= 0) || (_peerSearchSelected >= 0) || (_searchedSelected >= 0); + } + void itemRemoved(HistoryItem *item); enum class UpdateRowSection { Default = 0x01, Filtered = 0x02, - GlobalSearch = 0x04, + PeerSearch = 0x04, MessageSearch = 0x08, All = 0x0F, }; Q_DECLARE_FLAGS(UpdateRowSections, UpdateRowSection); Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(UpdateRowSections); - void updateDialogRow(History *history, MsgId msgId, QRect updateRect, UpdateRowSections sections = UpdateRowSection::All); + void updateDialogRow(PeerData *peer, MsgId msgId, QRect updateRect, UpdateRowSections sections = UpdateRowSection::All); int dialogsOffset() const; int filteredOffset() const; - int peopleOffset() const; + int peerSearchOffset() const; int searchedOffset() const; - void peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool active, bool selected, bool onlyBackground) const; - void searchInPeerPaint(Painter &p, int32 w, bool onlyBackground) const; + void paintDialog(QPainter &p, Dialogs::Row *dialog); + void paintPeerSearchResult(Painter &p, const PeerSearchResult *result, int32 w, bool active, bool selected, bool onlyBackground, TimeMs ms) const; + void paintSearchInPeer(Painter &p, int32 w, bool onlyBackground) const; void clearSelection(); - void clearSearchResults(bool clearPeople = true); + void clearSearchResults(bool clearPeerSearchResults = true); void updateSelectedRow(PeerData *peer = 0); Dialogs::IndexedList *shownDialogs() const { - return (Global::DialogsMode() == Dialogs::Mode::Important) ? importantDialogs.get() : dialogs.get(); + return (Global::DialogsMode() == Dialogs::Mode::Important) ? _dialogsImportant.get() : _dialogs.get(); } - using DialogsList = std_::unique_ptr; - DialogsList dialogs; - DialogsList importantDialogs; + DialogsList _dialogs; + DialogsList _dialogsImportant; - DialogsList contactsNoDialogs; - DialogsList contacts; + DialogsList _contactsNoDialogs; + DialogsList _contacts; - bool _importantSwitchSel = false; - Dialogs::Row *_sel = nullptr; - bool _selByMouse = false; + bool _mouseSelection = false; + Qt::MouseButton _pressButton = Qt::LeftButton; + std_::unique_ptr _importantSwitch; + bool _importantSwitchSelected = false; + bool _importantSwitchPressed = false; + Dialogs::Row *_selected = nullptr; + Dialogs::Row *_pressed = nullptr; + + int _visibleAreaHeight = 0; QString _filter, _hashtagFilter; - QStringList _hashtagResults; - int _hashtagSel = -1; + HashtagResults _hashtagResults; + int _hashtagSelected = -1; + int _hashtagPressed = -1; + bool _hashtagDeleteSelected = false; + bool _hashtagDeletePressed = false; FilteredDialogs _filterResults; - int _filteredSel = -1; + int _filteredSelected = -1; + int _filteredPressed = -1; + + QString _peerSearchQuery; + PeerSearchResults _peerSearchResults; + int _peerSearchSelected = -1; + int _peerSearchPressed = -1; SearchResults _searchResults; int _searchedCount = 0; int _searchedMigratedCount = 0; - int _searchedSel = -1; - - QString _peopleQuery; - PeopleResults _peopleResults; - int _peopleSel = -1; + int _searchedSelected = -1; + int _searchedPressed = -1; int _lastSearchDate = 0; PeerData *_lastSearchPeer = nullptr; @@ -218,21 +254,17 @@ private: State _state = DefaultState; - QPoint lastMousePos; - - void paintDialog(QPainter &p, Dialogs::Row *dialog); - ChildWidget _addContactLnk; ChildWidget _cancelSearchInPeer; - bool _overDelete = false; - PeerData *_searchInPeer = nullptr; PeerData *_searchInMigrated = nullptr; PeerData *_menuPeer = nullptr; Ui::PopupMenu *_menu = nullptr; + base::lambda _loadMoreCallback; + }; Q_DECLARE_OPERATORS_FOR_FLAGS(DialogsInner::UpdateRowSections); @@ -243,27 +275,14 @@ class DialogsWidget : public TWidget, public RPCSender, private base::Subscriber public: DialogsWidget(QWidget *parent); - void dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpRequestId req); - void contactsReceived(const MTPcontacts_Contacts &contacts); - void searchReceived(DialogsSearchRequestType type, const MTPmessages_Messages &result, mtpRequestId req); - void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req); - - void dragEnterEvent(QDragEnterEvent *e) override; - void dragMoveEvent(QDragMoveEvent *e) override; - void dragLeaveEvent(QDragLeaveEvent *e) override; - void dropEvent(QDropEvent *e) override; void updateDragInScroll(bool inScroll); - void resizeEvent(QResizeEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void searchInPeer(PeerData *peer); void loadDialogs(); void createDialog(History *history); void dlgUpdated(Dialogs::Mode list, Dialogs::Row *row); - void dlgUpdated(History *row, MsgId msgId); + void dlgUpdated(PeerData *peer, MsgId msgId); void dialogsToUp(); @@ -272,7 +291,6 @@ public: } void showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms); void showFast(); - void step_show(float64 ms, bool timer); void destroyData(); @@ -321,7 +339,22 @@ private slots: void onCheckUpdateStatus(); #endif // TDESKTOP_DISABLE_AUTOUPDATE +protected: + void dragEnterEvent(QDragEnterEvent *e) override; + void dragMoveEvent(QDragMoveEvent *e) override; + void dragLeaveEvent(QDragLeaveEvent *e) override; + void dropEvent(QDropEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void keyPressEvent(QKeyEvent *e) override; + void paintEvent(QPaintEvent *e) override; + private: + void animationCallback(); + void dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpRequestId req); + void contactsReceived(const MTPcontacts_Contacts &result); + void searchReceived(DialogsSearchRequestType type, const MTPmessages_Messages &result, mtpRequestId req); + void peerSearchReceived(const MTPcontacts_Found &result, mtpRequestId req); + void setSearchInPeer(PeerData *peer); void showMainMenu(); void updateLockUnlockVisibility(); @@ -354,31 +387,34 @@ private: ChildWidget _inner; ChildWidget _updateTelegram = { nullptr }; - Animation _a_show; + FloatAnimation _a_show; + Window::SlideDirection _showDirection; QPixmap _cacheUnder, _cacheOver; - anim::ivalue a_coordUnder, a_coordOver; - anim::fvalue a_progress; PeerData *_searchInPeer = nullptr; PeerData *_searchInMigrated = nullptr; QTimer _searchTimer; - QString _searchQuery, _peopleQuery; + + QString _peerSearchQuery; + bool _peerSearchFull = false; + mtpRequestId _peerSearchRequest = 0; + + QString _searchQuery; bool _searchFull = false; bool _searchFullMigrated = false; - bool _peopleFull = false; - mtpRequestId _searchRequest, _peopleRequest; + mtpRequestId _searchRequest = 0; - typedef QMap SearchCache; + using SearchCache = QMap; SearchCache _searchCache; - typedef QMap SearchQueries; + using SearchQueries = QMap; SearchQueries _searchQueries; - typedef QMap PeopleCache; - PeopleCache _peopleCache; + using PeerSearchCache = QMap; + PeerSearchCache _peerSearchCache; - typedef QMap PeopleQueries; - PeopleQueries _peopleQueries; + using PeerSearchQueries = QMap; + PeerSearchQueries _peerSearchQueries; }; diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index 3b902cb48..236a2df47 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -395,7 +395,7 @@ void FieldAutocomplete::hideFast() { if (_a_appearance.animating()) { _a_appearance.stop(); } - a_opacity = anim::fvalue(0, 0); + a_opacity = anim::value(); hideFinish(); } diff --git a/Telegram/SourceFiles/history/field_autocomplete.h b/Telegram/SourceFiles/history/field_autocomplete.h index 7f8faab8f..ba8df4f92 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.h +++ b/Telegram/SourceFiles/history/field_autocomplete.h @@ -131,7 +131,7 @@ private: int32 _width, _height; bool _hiding = false; - anim::fvalue a_opacity; + anim::value a_opacity; Animation _a_appearance; friend class internal::FieldAutocompleteInner; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 161c7437a..146100c8a 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -240,8 +240,6 @@ historyAttachEmoji: IconButton(historyAttach) { icon: icon {{ "send_control_emoji", historyComposeIconFg }}; iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }}; iconPosition: point(15px, 15px); - - ripple: emptyRippleAnimation; } historyEmojiCircle: size(20px, 20px); historyEmojiCirclePeriod: 1500; @@ -251,16 +249,16 @@ historyEmojiCircleLine: 2px; historyEmojiCircleFg: historyComposeIconFg; historyEmojiCircleFgOver: historyComposeIconFgOver; historyEmojiCirclePart: 3.5; -historyBotKeyboardShow: IconButton(historySend) { +historyBotKeyboardShow: IconButton(historyAttach) { icon: icon {{ "send_control_bot_keyboard", historyComposeIconFg }}; iconOver: icon {{ "send_control_bot_keyboard", historyComposeIconFgOver }}; } -historyBotKeyboardHide: IconButton(historySend) { +historyBotKeyboardHide: IconButton(historyAttach) { icon: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFg }}; iconOver: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFgOver }}; iconPosition: point(11px, 16px); } -historyBotCommandStart: IconButton(historySend) { +historyBotCommandStart: IconButton(historyAttach) { icon: icon {{ "send_control_bot_command", historyComposeIconFg }}; iconOver: icon {{ "send_control_bot_command", historyComposeIconFgOver }}; } @@ -270,6 +268,7 @@ historyRecordVoiceFgActive: windowBgActive; historyRecordVoice: icon {{ "send_control_record", historyRecordVoiceFg }}; historyRecordVoiceOver: icon {{ "send_control_record", historyRecordVoiceFgOver }}; historyRecordVoiceActive: icon {{ "send_control_record", historyRecordVoiceFgActive }}; +historyRecordVoiceRippleBgActive: lightButtonBgOver; historyRecordSignalColor: #f17077; historyRecordSignalMin: 5px; historyRecordSignalMax: 12px; diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp index 00c69cb07..f14df11e7 100644 --- a/Telegram/SourceFiles/history/history_drag_area.cpp +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -133,14 +133,14 @@ void DragArea::hideFast() { if (_a_appearance.animating()) { _a_appearance.stop(); } - a_opacity = anim::fvalue(0, 0); + a_opacity = anim::value(); hide(); } void DragArea::hideStart() { _hiding = true; _in = false; - a_opacity.start(0); + a_opacity.start(0.); a_colorDrop.start(_in ? 1. : 0.); _a_appearance.start(); } @@ -148,7 +148,7 @@ void DragArea::hideStart() { void DragArea::hideFinish() { hide(); _in = false; - a_colorDrop = anim::fvalue(0.); + a_colorDrop = anim::value(); } void DragArea::showStart() { diff --git a/Telegram/SourceFiles/history/history_drag_area.h b/Telegram/SourceFiles/history/history_drag_area.h index fc4f11afd..ff4706566 100644 --- a/Telegram/SourceFiles/history/history_drag_area.h +++ b/Telegram/SourceFiles/history/history_drag_area.h @@ -70,8 +70,8 @@ private: bool _hiding, _in; base::lambda _droppedCallback; - anim::fvalue a_opacity; - anim::fvalue a_colorDrop; + anim::value a_opacity; + anim::value a_colorDrop; Animation _a_appearance; Ui::RectShadow _shadow; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index cbb9a6827..f85ee2cea 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -584,7 +584,7 @@ void HistoryItem::finishCreate() { void HistoryItem::finishEdition(int oldKeyboardTop) { setPendingInitDimensions(); if (App::main()) { - App::main()->dlgUpdated(history(), id); + App::main()->dlgUpdated(history()->peer, id); } // invalidate cache for drawInDialog diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index b5822174d..d74135ca6 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -1456,7 +1456,7 @@ bool HistoryDocument::updateStatusText() const { if (voice->_playback->_position < playbackState.position) { voice->_playback->a_progress.start(prg); } else { - voice->_playback->a_progress = anim::fvalue(0., prg); + voice->_playback->a_progress = anim::value(0., prg); } voice->_playback->_position = playbackState.position; voice->_playback->_a_progress.start(); diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 51e8524cb..a7340ace9 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -99,7 +99,7 @@ protected: , _a_thumbOver(std_::move(thumbOverCallbacks)) , radial(std_::move(radialCallbacks)) { } - anim::fvalue a_thumbOver; + anim::value a_thumbOver; Animation _a_thumbOver; Ui::RadialAnimation radial; @@ -306,7 +306,7 @@ struct HistoryDocumentVoicePlayback { HistoryDocumentVoicePlayback(const HistoryDocument *that); int32 _position; - anim::fvalue a_progress; + anim::value a_progress; Animation _a_progress; }; struct HistoryDocumentVoice : public RuntimeComponent { diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 9248ffcc6..ddd616344 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -2329,7 +2329,7 @@ bool HistoryService::updateDependentText() { history()->textCachedFor = 0; } if (App::main()) { - App::main()->dlgUpdated(history(), id); + App::main()->dlgUpdated(history()->peer, id); } App::historyUpdateDependent(this); return result; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index e36736e3b..81c3a44f6 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -34,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/buttons.h" #include "ui/widgets/inner_dropdown.h" #include "ui/widgets/dropdown_menu.h" +#include "ui/effects/ripple_animation.h" #include "inline_bots/inline_bot_result.h" #include "data/data_drafts.h" #include "history/history_service_layout.h" @@ -2971,7 +2972,7 @@ void SilentToggle::mouseMoveEvent(QMouseEvent *e) { void SilentToggle::setChecked(bool checked) { if (_checked != checked) { _checked = checked; - setIcon(_checked ? &st::historySilentToggleOn : nullptr, _checked ? &st::historySilentToggleOnOver : nullptr); + setIconOverride(_checked ? &st::historySilentToggleOn : nullptr, _checked ? &st::historySilentToggleOnOver : nullptr); } } @@ -3051,9 +3052,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _botCommandStart(this, st::historyBotCommandStart) , _silent(this) , _field(this, st::historyComposeField, lang(lng_message_ph)) -, _a_record(animation(this, &HistoryWidget::step_record)) , _a_recording(animation(this, &HistoryWidget::step_recording)) -, a_recordCancelActive(0, 0) , _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel))) , _kbScroll(this, st::botKbScroll) , _keyboard(this) @@ -3061,7 +3060,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _attachDragDocument(this) , _attachDragPhoto(this) , _fileLoader(this, FileLoaderQueueStopTimeout) -, _a_show(animation(this, &HistoryWidget::step_show)) , _topShadow(this, st::shadowColor) { setAcceptDrops(true); @@ -3318,10 +3316,8 @@ void HistoryWidget::onTextChange() { _send->show(); } updateMouseTracking(); - _a_record.stop(); + _a_recordActive.finish(); _inRecord = _inField = false; - a_recordDown = anim::fvalue(0, 0); - a_recordCancelActive = anim::fvalue(0, 0); } } if (updateCmdStartShown()) { @@ -4197,7 +4193,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re historyLoaded(); } - App::main()->dlgUpdated(wasHistory, wasMsgId); + App::main()->dlgUpdated(wasHistory ? wasHistory->peer : nullptr, wasMsgId); emit historyShown(_history, _showAtMsgId); App::main()->topBar()->update(); @@ -4358,7 +4354,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re if (App::wnd()) QTimer::singleShot(0, App::wnd(), SLOT(setInnerFocus())); - App::main()->dlgUpdated(wasHistory, wasMsgId); + App::main()->dlgUpdated(wasHistory ? wasHistory->peer : nullptr, wasMsgId); emit historyShown(_history, _showAtMsgId); App::main()->historyPeerChanged().notify(_peer, true); @@ -4650,7 +4646,7 @@ void HistoryWidget::updateControlsVisibility() { } else { _send->show(); } - _a_record.stop(); + _a_recordActive.finish(); _inRecord = _inField = false; } if (_recording) { @@ -5441,9 +5437,9 @@ PeerData *HistoryWidget::peer() const { void HistoryWidget::setMsgId(MsgId showAtMsgId) { // sometimes _showAtMsgId is set directly if (_showAtMsgId != showAtMsgId) { - MsgId wasMsgId = _showAtMsgId; + auto wasMsgId = _showAtMsgId; _showAtMsgId = showAtMsgId; - App::main()->dlgUpdated(_history, wasMsgId); + App::main()->dlgUpdated(_history ? _history->peer : nullptr, wasMsgId); emit historyShown(_history, _showAtMsgId); } } @@ -5453,7 +5449,9 @@ MsgId HistoryWidget::msgId() const { } void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms) { - if (App::app()) App::app()->mtpPause(); + _showDirection = direction; + + _a_show.finish(); _cacheUnder = params.oldContentCache; show(); @@ -5487,47 +5485,26 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: _pinnedBar->cancel->hide(); } - int delta = st::slideShift; - if (direction == Window::SlideDirection::FromLeft) { - a_progress = anim::fvalue(1, 0); + if (_showDirection == Window::SlideDirection::FromLeft) { std::swap(_cacheUnder, _cacheOver); - a_coordUnder = anim::ivalue(-delta, 0); - a_coordOver = anim::ivalue(0, width()); - } else { - a_progress = anim::fvalue(0, 1); - a_coordUnder = anim::ivalue(0, -delta); - a_coordOver = anim::ivalue(width(), 0); } - _a_show.start(); + _a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition()); App::main()->topBar()->update(); activate(); } -void HistoryWidget::step_show(float64 ms, bool timer) { - float64 dt = ms / st::slideDuration; - if (dt >= 1) { - _a_show.stop(); +void HistoryWidget::animationCallback() { + update(); + App::main()->topBar()->update(); + if (!_a_show.animating()) { _topShadow->setVisible(_peer ? true : false); _historyToEnd->finishAnimation(); - a_coordUnder.finish(); - a_coordOver.finish(); - a_progress.finish(); _cacheUnder = _cacheOver = QPixmap(); App::main()->topBar()->stopAnim(); doneShow(); - - if (App::app()) App::app()->mtpUnpause(); - } else { - a_coordUnder.update(dt, Window::SlideAnimation::transition()); - a_coordOver.update(dt, Window::SlideAnimation::transition()); - a_progress.update(dt, Window::SlideAnimation::transition()); - } - if (timer) { - update(); - App::main()->topBar()->update(); } } @@ -5547,29 +5524,21 @@ void HistoryWidget::doneShow() { } } -void HistoryWidget::animStop() { +void HistoryWidget::finishAnimation() { if (!_a_show.animating()) return; - _a_show.stop(); + _a_show.finish(); _topShadow->setVisible(_peer ? true : false); _historyToEnd->finishAnimation(); } -void HistoryWidget::step_record(float64 ms, bool timer) { - float64 dt = ms / st::historyComposeButton.duration; - if (dt >= 1 || !_send->isHidden() || (_inlineBotCancel && !_inlineBotCancel->isHidden()) || isBotStart() || isBlocked()) { - _a_record.stop(); - a_recordDown.finish(); - a_recordCancelActive.finish(); +void HistoryWidget::recordActiveCallback() { + if (_recording) { + updateField(); } else { - a_recordDown.update(dt, anim::linear); - a_recordCancelActive.update(dt, anim::linear); + update(_send->geometry()); } - if (timer) { - if (_recording) { - updateField(); - } else { - update(_send->geometry()); - } + if (!_send->isHidden() || (_inlineBotCancel && !_inlineBotCancel->isHidden()) || isBotStart() || isBlocked()) { + _a_recordActive.finish(); } } @@ -5665,9 +5634,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { } if (inField != _inField && _recording) { _inField = inField; - a_recordDown.start(_inField ? 1 : 0); - a_recordCancelActive.start(_inField ? 0. : 1.); - _a_record.start(); + _a_recordActive.start([this] { recordActiveCallback(); }, _inField ? 0. : 1., _inField ? 1. : 0., st::historyComposeButton.duration); } _inReplyEdit = inReplyEdit; _inPinnedMsg = inPinnedMsg; @@ -5698,7 +5665,7 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) { void HistoryWidget::stopRecording(bool send) { emit audioCapture()->stop(send); - a_recordingLevel = anim::ivalue(0, 0); + a_recordingLevel = anim::value(); _a_recording.stop(); _recording = false; @@ -5712,9 +5679,13 @@ void HistoryWidget::stopRecording(bool send) { updateField(); - a_recordDown.start(0); - a_recordCancelActive = anim::fvalue(0., 0.); - _a_record.start(); + if (_inField) { + _a_recordActive.start([this] { recordActiveCallback(); }, 1., 0., st::historyComposeButton.duration); + } + + if (_recordRipple) { + _recordRipple->lastStop(); + } } void HistoryWidget::sendBotCommand(PeerData *peer, UserData *bot, const QString &cmd, MsgId replyTo) { // replyTo != 0 from ReplyKeyboardMarkup, == 0 from cmd links @@ -6202,16 +6173,21 @@ void HistoryWidget::onForwardHere() { bool HistoryWidget::paintTopBar(Painter &p, int decreaseWidth, TimeMs ms) { if (_a_show.animating()) { - int retina = cIntRetinaFactor(); - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), st::topBarHeight), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, st::topBarHeight * retina)); - p.setOpacity(a_progress.current()); - p.fillRect(0, 0, a_coordOver.current(), st::topBarHeight, st::slideFadeOutBg); + auto progress = _a_show.current(1.); + auto retina = cIntRetinaFactor(); + auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft); + auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); + auto coordOver = fromLeft ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto shadow = fromLeft ? (1. - progress) : progress; + if (coordOver > 0) { + p.drawPixmap(QRect(0, 0, coordOver, st::topBarHeight), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, st::topBarHeight * retina)); + p.setOpacity(shadow); + p.fillRect(0, 0, coordOver, st::topBarHeight, st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina)); - p.setOpacity(a_progress.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), st::topBarHeight)); + p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina)); + p.setOpacity(shadow); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), st::topBarHeight)); return false; } @@ -6481,14 +6457,14 @@ void HistoryWidget::onCheckFieldAutocomplete() { void HistoryWidget::updateFieldPlaceholder() { if (_editMsgId) { _field->setPlaceholder(lang(lng_edit_message_text)); - _send->setIcon(&st::historyEditSaveIcon, &st::historyEditSaveIconOver); + _send->setIconOverride(&st::historyEditSaveIcon, &st::historyEditSaveIconOver); } else { if (_inlineBot && _inlineBot != Ui::LookingUpInlineBot) { _field->setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); } else { _field->setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_silent->checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_message_ph)); } - _send->setIcon(nullptr); + _send->setIconOverride(nullptr); } } @@ -7511,8 +7487,13 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { updateField(); - a_recordDown.start(1); - _a_record.start(); + _a_recordActive.start([this] { recordActiveCallback(); }, 0., 1., st::historyComposeButton.duration); + + if (!_recordRipple) { + auto mask = Ui::RippleAnimation::ellipseMask(QSize(st::historyAttachEmoji.rippleAreaSize, st::historyAttachEmoji.rippleAreaSize)); + _recordRipple = std_::make_unique(st::historyAttachEmoji.ripple, std_::move(mask), [this] { update(_send->geometry()); }); + } + _recordRipple->add(mapFromGlobal(QCursor::pos()) - QPoint(_send->x() + (_send->width() - st::historyAttachEmoji.rippleAreaSize) / 2, _send->y() + st::historyAttachEmoji.rippleAreaPosition.y())); } else if (_inReplyEdit) { Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId()); } else if (_inPinnedMsg) { @@ -8582,7 +8563,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { Text *from = 0, *text = 0; bool serviceColor = false, hasForward = readyToForward(); ImagePtr preview; - HistoryItem *drawMsgText = (_editMsgId || _replyToId) ? _replyEditMsg : _kbReplyTo; + auto drawMsgText = (_editMsgId || _replyToId) ? _replyEditMsg : _kbReplyTo; if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { if (!_editMsgId && drawMsgText && drawMsgText->author()->nameVersion > _replyToNameVersion) { updateReplyToName(); @@ -8718,10 +8699,16 @@ void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int } } -void HistoryWidget::drawRecordButton(Painter &p) { - auto down = a_recordDown.current(); - auto fastIcon = [down, this] { - if (down == 1.) { +void HistoryWidget::drawRecordButton(Painter &p, float64 recordActive, TimeMs ms) { + if (_recordRipple) { + auto rippleColor = anim::color(st::historyAttachEmoji.ripple.color, st::historyRecordVoiceRippleBgActive, recordActive); + _recordRipple->paint(p, _send->x() + (_send->width() - st::historyAttachEmoji.rippleAreaSize) / 2, _send->y() + st::historyAttachEmoji.rippleAreaPosition.y(), width(), ms, &rippleColor); + if (_recordRipple->empty()) { + _recordRipple.reset(); + } + } + auto fastIcon = [recordActive, this] { + if (recordActive == 1.) { return &st::historyRecordVoiceActive; } else if (_inRecord) { return &st::historyRecordVoiceOver; @@ -8729,19 +8716,19 @@ void HistoryWidget::drawRecordButton(Painter &p) { return &st::historyRecordVoice; }; fastIcon()->paintInCenter(p, _send->geometry()); - if (down > 0. && down < 1.) { - p.setOpacity(down); + if (recordActive > 0. && recordActive < 1.) { + p.setOpacity(recordActive); st::historyRecordVoiceActive.paintInCenter(p, _send->geometry()); p.setOpacity(1.); } } -void HistoryWidget::drawRecording(Painter &p) { +void HistoryWidget::drawRecording(Painter &p, float64 recordActive) { p.setPen(Qt::NoPen); p.setBrush(st::historyRecordSignalColor); - float64 delta = qMin(float64(a_recordingLevel.current()) / 0x4000, 1.); - int32 d = 2 * qRound(st::historyRecordSignalMin + (delta * (st::historyRecordSignalMax - st::historyRecordSignalMin))); + auto delta = qMin(a_recordingLevel.current() / 0x4000, 1.); + auto d = 2 * qRound(st::historyRecordSignalMin + (delta * (st::historyRecordSignalMax - st::historyRecordSignalMin))); { PainterHighQualityEnabler hq(p); p.drawEllipse(_attachToggle->x() + (_attachEmoji->width() - d) / 2, _attachToggle->y() + (_attachToggle->height() - d) / 2, d, d); @@ -8756,7 +8743,7 @@ void HistoryWidget::drawRecording(Painter &p) { int32 left = _attachToggle->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send->width() - st::historyRecordVoice.width()) / 2); int32 right = width() - _send->width(); - p.setPen(anim::pen(st::historyRecordCancel, st::historyRecordCancelActive, a_recordCancelActive.current())); + p.setPen(anim::pen(st::historyRecordCancel, st::historyRecordCancelActive, 1. - recordActive)); p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachToggle->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, lang(lng_record_cancel)); } @@ -8809,18 +8796,24 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { } bool hasTopBar = !App::main()->topBar()->isHidden(); + auto ms = getms(); + auto progress = _a_show.current(ms, 1.); if (_a_show.animating()) { - int retina = cIntRetinaFactor(); - int inCacheTop = hasTopBar ? st::topBarHeight : 0; - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * retina, inCacheTop * retina, a_coordOver.current() * retina, height() * retina)); - p.setOpacity(a_progress.current()); - p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); + auto retina = cIntRetinaFactor(); + auto inCacheTop = hasTopBar ? st::topBarHeight : 0; + auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft); + auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); + auto coordOver = fromLeft ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto shadow = fromLeft ? (1. - progress) : progress; + if (coordOver > 0) { + p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * retina, inCacheTop * retina, coordOver * retina, height() * retina)); + p.setOpacity(shadow); + p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina)); - p.setOpacity(a_progress.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), height())); + p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina)); + p.setOpacity(shadow); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height())); return; } @@ -8854,8 +8847,9 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { if (!_field->isHidden() || _recording) { drawField(p, r); if (_send->isHidden() && (!_inlineBotCancel || _inlineBotCancel->isHidden())) { - drawRecordButton(p); - if (_recording) drawRecording(p); + auto recordActive = _a_recordActive.current(ms, _inField ? 1. : 0.); + drawRecordButton(p, recordActive, ms); + if (_recording) drawRecording(p, recordActive); } } if (_pinnedBar && !_pinnedBar->cancel->isHidden()) { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 26b486eaa..14e381b8a 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -510,7 +510,7 @@ private: ChildWidget _cancel; PeerData *_offered = nullptr; - anim::fvalue a_opacity; + anim::value a_opacity; Animation _a_appearance; QRect _box; @@ -613,8 +613,7 @@ public: return peer() != nullptr; } void showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms); - void step_show(float64 ms, bool timer); - void animStop(); + void finishAnimation(); void doneShow(); @@ -650,7 +649,6 @@ public: void updatePreview(); void previewCancel(); - void step_record(float64 ms, bool timer); void step_recording(float64 ms, bool timer); void stopRecording(bool send); @@ -849,6 +847,8 @@ private slots: void updateField(); private: + void animationCallback(); + void recordActiveCallback(); void chooseAttach(); void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update); struct SendingFilesLists { @@ -931,8 +931,8 @@ private: void drawField(Painter &p, const QRect &rect); void paintEditHeader(Painter &p, const QRect &rect, int left, int top) const; - void drawRecordButton(Painter &p); - void drawRecording(Painter &p); + void drawRecordButton(Painter &p, float64 recordActive, TimeMs ms); + void drawRecording(Painter &p, float64 recordActive); void drawPinnedBar(Painter &p); void updateMouseTracking(); @@ -1122,18 +1122,18 @@ private: ChildWidget _silent; bool _cmdStartShown = false; ChildWidget _field; - Animation _a_record, _a_recording; + Animation _a_recording; bool _recording = false; bool _inRecord = false; bool _inField = false; bool _inReplyEdit = false; bool _inPinnedMsg = false; bool _inClickable = false; - anim::ivalue a_recordingLevel = { 0, 0 }; - int32 _recordingSamples = 0; - anim::fvalue a_recordDown = { 0, 0 }; - anim::fvalue a_recordCancelActive; - int32 _recordCancelWidth; + anim::value a_recordingLevel; + int _recordingSamples = 0; + FloatAnimation _a_recordActive; + std_::unique_ptr _recordRipple; + int _recordCancelWidth; FileDialog::QueryId _attachFilesQueryId = 0; @@ -1163,10 +1163,9 @@ private: bool _titlePeerTextOnline = false; int _titlePeerTextWidth = 0; - Animation _a_show; + FloatAnimation _a_show; + Window::SlideDirection _showDirection; QPixmap _cacheUnder, _cacheOver; - anim::ivalue a_coordUnder, a_coordOver; - anim::fvalue a_progress; QTimer _scrollTimer; int32 _scrollDelta = 0; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index ff36b1191..26f724b21 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -786,7 +786,7 @@ void File::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { } else { if (!_animation) { ensureAnimation(); - _animation->a_thumbOver = anim::fvalue(1, 1); + _animation->a_thumbOver = anim::value(1, 1); } _animation->a_thumbOver.start(0); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h index f9170280a..c04c4ee76 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h @@ -267,7 +267,7 @@ private: , _a_thumbOver(std_::move(thumbOverCallbacks)) , radial(std_::move(radialCallbacks)) { } - anim::fvalue a_thumbOver; + anim::value a_thumbOver; Animation _a_thumbOver; Ui::RadialAnimation radial; diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 7c59f82a1..fbe54e65e 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org using "basic.style"; using "ui/widgets/widgets.style"; +countryRipple: defaultRippleAnimation; + introCoverHeight: 208px; introCoverTopBg: #0f89d0; introCoverBottomBg: #39b0f0; @@ -84,7 +86,7 @@ introStepHeight: 256px; introStepHeightAdd: 30px; introStepHeightFull: 580px; introSlideDuration: 200; -introCoverDuration: 200; +introCoverDuration: 300; introNextButton: RoundButton(defaultActiveButton) { width: 300px; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 61202fef1..c479cd8e8 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -47,7 +47,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Intro { Widget::Widget(QWidget *parent) : TWidget(parent) -, _a_show(animation(this, &Widget::step_show)) , _back(this, new Ui::IconButton(this, st::introBackButton), base::lambda(), st::introSlideDuration) , _settings(this, new Ui::RoundButton(this, lang(lng_menu_settings), st::defaultBoxButton), base::lambda(), st::introCoverDuration) , _next(this, QString(), st::introNextButton) { @@ -143,7 +142,7 @@ void Widget::historyMove(Direction direction) { if (wasStep->hasCover() != getStep()->hasCover()) { _nextTopFrom = wasStep->contentTop() + st::introStepHeight; _controlsTopFrom = wasStep->hasCover() ? st::introCoverHeight : 0; - _coverShownAnimation.start([this] { updateControlsGeometry(); }, 0., 1., st::introCoverDuration, anim::easeOutCirc); + _coverShownAnimation.start([this] { updateControlsGeometry(); }, 0., 1., st::introCoverDuration, wasStep->hasCover() ? anim::linear : anim::easeOutCirc); } if (direction == Direction::Forward || direction == Direction::Replace) { @@ -302,45 +301,29 @@ void Widget::hideControls() { _back->hideFast(); } -void Widget::animShow(const QPixmap &bgAnimCache, bool back) { - if (App::app()) App::app()->mtpPause(); +void Widget::showAnimated(const QPixmap &bgAnimCache, bool back) { + _showBack = back; - (back ? _cacheOver : _cacheUnder) = bgAnimCache; + (_showBack ? _cacheOver : _cacheUnder) = bgAnimCache; - _a_show.stop(); + _a_show.finish(); showControls(); - (back ? _cacheUnder : _cacheOver) = myGrab(this); + (_showBack ? _cacheUnder : _cacheOver) = myGrab(this); hideControls(); - a_coordUnder = back ? anim::ivalue(-st::slideShift, 0) : anim::ivalue(0, -st::slideShift); - a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0); - a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1); - _a_show.start(); + _a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition()); show(); } -void Widget::step_show(float64 ms, bool timer) { - float64 dt = ms / st::slideDuration; - if (dt >= 1) { - _a_show.stop(); - - a_coordUnder.finish(); - a_coordOver.finish(); - a_shadow.finish(); - +void Widget::animationCallback() { + update(); + if (!_a_show.animating()) { _cacheUnder = _cacheOver = QPixmap(); showControls(); getStep()->activate(); - - if (App::app()) App::app()->mtpUnpause(); - } else { - a_coordUnder.update(dt, Window::SlideAnimation::transition()); - a_coordOver.update(dt, Window::SlideAnimation::transition()); - a_shadow.update(dt, Window::SlideAnimation::transition()); } - if (timer) update(); } void Widget::paintEvent(QPaintEvent *e) { @@ -356,16 +339,20 @@ void Widget::paintEvent(QPaintEvent *e) { p.setClipRect(e->rect()); } p.fillRect(e->rect(), st::windowBg); + auto progress = _a_show.current(getms(), 1.); if (_a_show.animating()) { - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor())); - p.setOpacity(a_shadow.current()); - p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); + auto coordUnder = _showBack ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); + auto coordOver = _showBack ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto shadow = _showBack ? (1. - progress) : progress; + if (coordOver > 0) { + p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * cRetinaFactor(), 0, coordOver * cRetinaFactor(), height() * cRetinaFactor())); + p.setOpacity(shadow); + p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(a_coordOver.current(), 0, _cacheOver); - p.setOpacity(a_shadow.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), height())); + p.drawPixmap(coordOver, 0, _cacheOver); + p.setOpacity(shadow); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height())); } } @@ -491,7 +478,6 @@ void Widget::Step::showFinished() { _slideAnimation.reset(); prepareCoverMask(); activate(); - if (App::app()) App::app()->mtpUnpause(); } bool Widget::Step::paintAnimated(Painter &p, QRect clip) { @@ -519,11 +505,11 @@ bool Widget::Step::paintAnimated(Painter &p, QRect clip) { return false; } - auto easeOut = anim::easeOutCirc(1., dt); - auto arrivingAlpha = easeOut; - auto departingAlpha = 1. - easeOut; - auto showCoverMethod = easeOut; - auto hideCoverMethod = easeOut; + auto progress = (hasCover() ? anim::easeOutCirc(1., dt) : anim::linear(1., dt)); + auto arrivingAlpha = progress; + auto departingAlpha = 1. - progress; + auto showCoverMethod = progress; + auto hideCoverMethod = progress; auto coverTop = (hasCover() ? anim::interpolate(-st::introCoverHeight, 0, showCoverMethod) : anim::interpolate(0, -st::introCoverHeight, hideCoverMethod)); paintCover(p, coverTop); @@ -718,7 +704,6 @@ QPixmap Widget::Step::prepareSlideAnimation() { void Widget::Step::showAnimated(Direction direction) { show(); - if (App::app()) App::app()->mtpPause(); hideChildren(); if (_slideAnimation) { auto slideLeft = (direction == Direction::Back); diff --git a/Telegram/SourceFiles/intro/introwidget.h b/Telegram/SourceFiles/intro/introwidget.h index 5707268f0..0baddc61f 100644 --- a/Telegram/SourceFiles/intro/introwidget.h +++ b/Telegram/SourceFiles/intro/introwidget.h @@ -39,7 +39,8 @@ class Widget : public TWidget, public RPCSender { public: Widget(QWidget *parent); - void animShow(const QPixmap &bgAnimCache, bool back = false); + void showAnimated(const QPixmap &bgAnimCache, bool back = false); + void setInnerFocus(); ~Widget(); @@ -205,7 +206,7 @@ public: }; private: - void step_show(float64 ms, bool timer); + void animationCallback(); void changeLanguage(int32 languageId); void updateControlsGeometry(); @@ -225,10 +226,9 @@ private: void resetDone(const MTPBool &result); bool resetFail(const RPCError &error); - Animation _a_show; + FloatAnimation _a_show; + bool _showBack = false; QPixmap _cacheUnder, _cacheOver; - anim::ivalue a_coordUnder, a_coordOver; - anim::fvalue a_shadow; QVector _stepHistory; Step *getStep(int skip = 0) { diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 58235b06a..5c71b328a 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -453,9 +453,6 @@ void LayerStackWidget::prepareForAnimation() { if (auto layer = currentLayer()) { layer->hide(); } - if (auto app = App::app()) { - app->mtpPause(); - } } void LayerStackWidget::animationDone() { @@ -477,9 +474,6 @@ void LayerStackWidget::animationDone() { } else { showFinished(); } - if (auto app = App::app()) { - app->mtpUnpause(); - } setAttribute(Qt::WA_OpaquePaintEvent, false); } @@ -613,8 +607,6 @@ LayerStackWidget::~LayerStackWidget() { } MediaPreviewWidget::MediaPreviewWidget(QWidget *parent) : TWidget(parent) -, a_shown(0, 0) -, _a_shown(animation(this, &MediaPreviewWidget::step_shown)) , _emojiSize(EmojiSizes[EIndex + 1] / cIntRetinaFactor()) { setAttribute(Qt::WA_TransparentForMouseEvents); subscribe(FileDownload::ImageLoaded(), [this] { update(); }); @@ -626,8 +618,13 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) { auto image = currentImage(); int w = image.width() / cIntRetinaFactor(), h = image.height() / cIntRetinaFactor(); - if (_a_shown.animating()) { - float64 shown = a_shown.current(); + auto shown = _a_shown.current(getms(), _hiding ? 0. : 1.); + if (!_a_shown.animating()) { + if (_hiding) { + hide(); + return; + } + } else { p.setOpacity(shown); // w = qMax(qRound(w * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(w % 2), 1); // h = qMax(qRound(h * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(h % 2), 1); @@ -650,18 +647,6 @@ void MediaPreviewWidget::resizeEvent(QResizeEvent *e) { update(); } -void MediaPreviewWidget::step_shown(float64 ms, bool timer) { - float64 dt = ms / st::stickerPreviewDuration; - if (dt >= 1) { - _a_shown.stop(); - a_shown.finish(); - if (a_shown.current() < 0.5) hide(); - } else { - a_shown.update(dt, anim::linear); - } - if (timer) update(); -} - void MediaPreviewWidget::showPreview(DocumentData *document) { if (!document || (!document->isAnimation() && !document->sticker())) { hidePreview(); @@ -692,8 +677,8 @@ void MediaPreviewWidget::startShow() { _cache = QPixmap(); if (isHidden() || _a_shown.animating()) { if (isHidden()) show(); - a_shown.start(1); - _a_shown.start(); + _hiding = false; + _a_shown.start([this] { update(); }, 0., 1., st::stickerPreviewDuration); } else { update(); } @@ -704,8 +689,8 @@ void MediaPreviewWidget::hidePreview() { return; } if (_gif) _cache = currentImage(); - a_shown.start(0); - _a_shown.start(); + _hiding = true; + _a_shown.start([this] { update(); }, 1., 0., st::stickerPreviewDuration); _photo = nullptr; _document = nullptr; resetGifAndCache(); diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index 6834bbc0a..498608974 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -144,8 +144,6 @@ public: void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *e); - void step_shown(float64 ms, bool timer); - void showPreview(DocumentData *document); void showPreview(PhotoData *photo); void hidePreview(); @@ -159,8 +157,8 @@ private: void fillEmojiString(); void resetGifAndCache(); - anim::fvalue a_shown; - Animation _a_shown; + FloatAnimation _a_shown; + bool _hiding = false; DocumentData *_document = nullptr; PhotoData *_photo = nullptr; Media::Clip::ReaderPointer _gif; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index ba9be8c6e..4909737d3 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -69,7 +69,6 @@ StackItemSection::~StackItemSection() { } MainWidget::MainWidget(QWidget *parent) : TWidget(parent) -, _a_show(animation(this, &MainWidget::step_show)) , _dialogsWidth(st::dialogsWidthMin) , _sideShadow(this, st::shadowColor) , _dialogs(this) @@ -2231,7 +2230,7 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show } Window::SectionSlideParams animationParams; - if (!_a_show.animating() && ((_history->isHidden() && (_wideSection || _overview)) || (Adaptive::OneColumn() && (_history->isHidden() || !peerId)) || back || (way == Ui::ShowWay::Forward))) { + if (!_a_show.animating() && !App::passcoded() && ((_history->isHidden() && (_wideSection || _overview)) || (Adaptive::OneColumn() && (_history->isHidden() || !peerId)) || back || (way == Ui::ShowWay::Forward))) { animationParams = prepareHistoryAnimation(peerId); } if (_history->peer() && _history->peer()->id != peerId && way != Ui::ShowWay::Forward) { @@ -2258,7 +2257,7 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show _topBar->hide(); _history->hide(); if (!_a_show.animating()) { - if (!animationParams.oldContentCache.isNull() && !App::passcoded()) { + if (!animationParams.oldContentCache.isNull()) { _dialogs->showAnimated(back ? Window::SlideDirection::FromLeft : Window::SlideDirection::FromRight, animationParams); } else { _dialogs->showFast(); @@ -2408,7 +2407,7 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool } else { _overview->fastShow(); } - _history->animStop(); + _history->finishAnimation(); if (back) { clearBotStartToken(_history->peer()); } @@ -2537,7 +2536,7 @@ void MainWidget::showWideSectionAnimated(const Window::SectionMemento *memento, resizeEvent(0); auto direction = back ? Window::SlideDirection::FromLeft : Window::SlideDirection::FromRight; _wideSection->showAnimated(direction, animationParams); - _history->animStop(); + _history->finishAnimation(); _history->showHistory(0, 0); _history->hide(); if (Adaptive::OneColumn()) _dialogs->hide(); @@ -2651,7 +2650,7 @@ QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶m void MainWidget::dlgUpdated() { if (_peerInStack) { - _dialogs->dlgUpdated(App::history(_peerInStack->id), _msgIdInStack); + _dialogs->dlgUpdated(_peerInStack, _msgIdInStack); } } @@ -2661,12 +2660,12 @@ void MainWidget::dlgUpdated(Dialogs::Mode list, Dialogs::Row *row) { } } -void MainWidget::dlgUpdated(History *row, MsgId msgId) { - if (!row) return; - if (msgId < 0 && -msgId < ServerMaxMsgId && row->peer->migrateFrom()) { - _dialogs->dlgUpdated(App::history(row->peer->migrateFrom()->id), -msgId); +void MainWidget::dlgUpdated(PeerData *peer, MsgId msgId) { + if (!peer) return; + if (msgId < 0 && -msgId < ServerMaxMsgId && peer->migrateFrom()) { + _dialogs->dlgUpdated(peer->migrateFrom(), -msgId); } else { - _dialogs->dlgUpdated(row, msgId); + _dialogs->dlgUpdated(peer, msgId); } } @@ -2719,66 +2718,49 @@ void MainWidget::historyCleared(History *history) { _history->historyCleared(history); } -void MainWidget::animShow(const QPixmap &bgAnimCache, bool back) { - if (App::app()) App::app()->mtpPause(); +void MainWidget::showAnimated(const QPixmap &bgAnimCache, bool back) { + _showBack = back; + (_showBack ? _cacheOver : _cacheUnder) = bgAnimCache; - (back ? _cacheOver : _cacheUnder) = bgAnimCache; - - _a_show.stop(); + _a_show.finish(); showAll(); - (back ? _cacheUnder : _cacheOver) = myGrab(this); + (_showBack ? _cacheUnder : _cacheOver) = myGrab(this); hideAll(); - a_coordUnder = back ? anim::ivalue(-st::slideShift, 0) : anim::ivalue(0, -st::slideShift); - a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0); - a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1); - _a_show.start(); + _a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition()); show(); } -void MainWidget::step_show(float64 ms, bool timer) { - float64 dt = ms / st::slideDuration; - if (dt >= 1) { - _a_show.stop(); - - a_coordUnder.finish(); - a_coordOver.finish(); - a_shadow.finish(); - +void MainWidget::animationCallback() { + update(); + if (!_a_show.animating()) { _cacheUnder = _cacheOver = QPixmap(); showAll(); activate(); - - if (App::app()) App::app()->mtpUnpause(); - } else { - a_coordUnder.update(dt, Window::SlideAnimation::transition()); - a_coordOver.update(dt, Window::SlideAnimation::transition()); - a_shadow.update(dt, Window::SlideAnimation::transition()); } - if (timer) update(); -} - -void MainWidget::animStop_show() { - _a_show.stop(); } void MainWidget::paintEvent(QPaintEvent *e) { if (_background) checkChatBackground(); Painter p(this); + auto progress = _a_show.current(getms(), 1.); if (_a_show.animating()) { - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor())); - p.setOpacity(a_shadow.current()); - p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); + auto coordUnder = _showBack ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); + auto coordOver = _showBack ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto shadow = _showBack ? (1. - progress) : progress; + if (coordOver > 0) { + p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * cRetinaFactor(), 0, coordOver * cRetinaFactor(), height() * cRetinaFactor())); + p.setOpacity(shadow); + p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(a_coordOver.current(), 0, _cacheOver); - p.setOpacity(a_shadow.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), height())); + p.drawPixmap(coordOver, 0, _cacheOver); + p.setOpacity(shadow); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height())); } } @@ -3048,7 +3030,7 @@ void MainWidget::onHistoryShown(History *history, MsgId atMsgId) { _topBar->hide(); } - dlgUpdated(history, atMsgId); + dlgUpdated(history ? history->peer : nullptr, atMsgId); } void MainWidget::searchInPeer(PeerData *peer) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index c6e7a0046..cc0c75728 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -152,9 +152,7 @@ public: int contentScrollAddToY() const; - void animShow(const QPixmap &bgAnimCache, bool back = false); - void step_show(float64 ms, bool timer); - void animStop_show(); + void showAnimated(const QPixmap &bgAnimCache, bool back = false); void start(const MTPUser &user); @@ -178,7 +176,7 @@ public: void removeDialog(History *history); void dlgUpdated(); void dlgUpdated(Dialogs::Mode list, Dialogs::Row *row); - void dlgUpdated(History *row, MsgId msgId); + void dlgUpdated(PeerData *peer, MsgId msgId); void windowShown(); @@ -483,6 +481,7 @@ protected: void keyPressEvent(QKeyEvent *e) override; private: + void animationCallback(); void updateAdaptiveLayout(); void handleAudioUpdate(const AudioMsgId &audioId); void updateMediaPlayerPosition(); @@ -587,10 +586,9 @@ private: base::Observable _searchInPeerChanged; base::Observable _historyPeerChanged; - Animation _a_show; + FloatAnimation _a_show; + bool _showBack = false; QPixmap _cacheUnder, _cacheOver; - anim::ivalue a_coordUnder, a_coordOver; - anim::fvalue a_shadow; int _dialogsWidth; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index dbcfbc3aa..35ad19798 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -241,9 +241,9 @@ void MainWindow::clearPasscode() { _passcode.destroy(); if (_intro) { - _intro->animShow(bg, true); + _intro->showAnimated(bg, true); } else { - _main->animShow(bg, true); + _main->showAnimated(bg, true); } notifyUpdateAll(); updateGlobalMenu(); @@ -265,7 +265,7 @@ void MainWindow::setupPasscode() { } if (_intro) _intro->hide(); if (animated) { - _passcode->animShow(bg); + _passcode->showAnimated(bg); } else { setInnerFocus(); } @@ -313,7 +313,7 @@ void MainWindow::setupIntro() { updateControlsGeometry(); if (animated) { - _intro->animShow(bg); + _intro->showAnimated(bg); } else { setInnerFocus(); } @@ -367,7 +367,7 @@ void MainWindow::setupMain(const MTPUser *self) { updateControlsGeometry(); if (animated) { - _main->animShow(bg); + _main->showAnimated(bg); } else { _main->activate(); } diff --git a/Telegram/SourceFiles/media/media_audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp index eb8a71309..2de874a60 100644 --- a/Telegram/SourceFiles/media/media_audio.cpp +++ b/Telegram/SourceFiles/media/media_audio.cpp @@ -991,7 +991,7 @@ void AudioPlayerFader::onTimer() { float64 wasAudio = suppressAllGain; if (ms >= _suppressAllStart + notifyLengthMs || ms < _suppressAllStart) { _suppressAll = _suppressAllAnim = false; - _suppressAllGain = anim::fvalue(1., 1.); + _suppressAllGain = anim::value(1., 1.); } else if (ms > _suppressAllStart + notifyLengthMs - AudioFadeDuration) { if (_suppressAllGain.to() != 1.) _suppressAllGain.start(1.); _suppressAllGain.update(1. - ((_suppressAllStart + notifyLengthMs - ms) / float64(AudioFadeDuration)), anim::linear); diff --git a/Telegram/SourceFiles/media/media_audio.h b/Telegram/SourceFiles/media/media_audio.h index 46046c51f..6efcd3a92 100644 --- a/Telegram/SourceFiles/media/media_audio.h +++ b/Telegram/SourceFiles/media/media_audio.h @@ -257,7 +257,7 @@ private: bool _suppressSong = false; bool _suppressSongAnim = false; bool _songVolumeChanged, _videoVolumeChanged; - anim::fvalue _suppressAllGain, _suppressSongGain; + anim::value _suppressAllGain, _suppressSongGain; TimeMs _suppressAllStart = 0; TimeMs _suppressSongStart = 0; diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 460f81502..f2ae6476a 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -23,17 +23,7 @@ using "basic.style"; using "ui/widgets/widgets.style"; using "overview/overview.style"; -MediaPlayerButton { - playPosition: point; - playOuter: size; - pausePosition: point; - pauseOuter: size; - pauseStroke: pixels; - cancelPosition: point; - cancelOuter: size; - cancelStroke: pixels; -} - +mediaPlayerBg: windowBg; mediaPlayerActiveFg: windowBgActive; mediaPlayerInactiveFg: #dfebf2; @@ -46,6 +36,12 @@ mediaPlayerButton: MediaPlayerButton { cancelPosition: point(1px, 1px); cancelOuter: size(15px, 15px); cancelStroke: 3px; + + rippleAreaPosition: point(0px, 5px); + rippleAreaSize: 25px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: lightButtonBgOver; + } } mediaPlayerButtonSize: size(25px, 30px); @@ -76,10 +72,20 @@ mediaPlayerRepeatButton: IconButton { { "player_repeat", mediaPlayerActiveFg, point(9px, 11px) } }; iconPosition: point(0px, 0px); + + rippleAreaPosition: point(3px, 5px); + rippleAreaSize: 25px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: lightButtonBgOver; + } } mediaPlayerRepeatDisabledIcon: icon { - { "player_repeat", #c8c8c8, point(9px, 11px)} + { "player_repeat", menuIconFg, point(9px, 11px)} }; +mediaPlayerRepeatDisabledIconOver: icon { + { "player_repeat", menuIconFgOver, point(9px, 11px)} +}; +mediaPlayerRepeatDisabledRippleBg: windowBgOver; mediaPlayerRepeatInactiveIcon: icon { { "player_repeat", mediaPlayerInactiveFg, point(9px, 11px)} }; @@ -102,6 +108,12 @@ mediaPlayerVolumeToggle: IconButton { icon: mediaPlayerVolumeIcon0; iconPosition: point(8px, 11px); + + rippleAreaPosition: point(3px, 5px); + rippleAreaSize: 25px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: lightButtonBgOver; + } } mediaPlayerVolumeMargin: 10px; mediaPlayerVolumeSize: size(27px, 100px); @@ -117,6 +129,8 @@ mediaPlayerNextButton: IconButton(mediaPlayerRepeatButton) { icon: icon { { "player_next", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, }; + + rippleAreaPosition: point(0px, 5px); } mediaPlayerNextDisabledIcon: icon { { "player_next", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, @@ -125,13 +139,20 @@ mediaPlayerPreviousButton: IconButton(mediaPlayerNextButton) { icon: icon { { "player_next-flip_horizontal", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, }; + rippleAreaPosition: point(1px, 5px); } mediaPlayerPreviousDisabledIcon: icon { { "player_next-flip_horizontal", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, }; mediaPlayerClose: IconButton(mediaPlayerRepeatButton) { width: 37px; - icon: icon {{ "player_close", #c8c8c8, point(10px, 12px) }}; + icon: icon {{ "player_close", menuIconFg, point(10px, 12px) }}; + iconOver: icon {{ "player_close", menuIconFgOver, point(10px, 12px) }}; + + rippleAreaPosition: point(3px, 5px); + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } } mediaPlayerPlayback: FilledSlider { fullWidth: 6px; @@ -165,7 +186,8 @@ mediaPlayerCoverHeight: 102px; mediaPlayerPanelClose: IconButton(mediaPlayerClose) { width: 43px; height: 28px; - icon: icon {{ "player_close", #c8c8c8, point(16px, 14px) }}; + icon: icon {{ "player_close", menuIconFg, point(16px, 14px) }}; + iconOver: icon {{ "player_close", menuIconFgOver, point(16px, 14px) }}; } mediaPlayerPanelNextButton: IconButton(mediaPlayerRepeatButton) { diff --git a/Telegram/SourceFiles/media/player/media_player_button.cpp b/Telegram/SourceFiles/media/player/media_player_button.cpp index e51de5b6e..e79ec7974 100644 --- a/Telegram/SourceFiles/media/player/media_player_button.cpp +++ b/Telegram/SourceFiles/media/player/media_player_button.cpp @@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "media/player/media_player_button.h" +#include "styles/style_widgets.h" + namespace Media { namespace Player { namespace { diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp index 8a45f3df6..6e7efdb8f 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.cpp +++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp @@ -234,7 +234,7 @@ void CoverWidget::updateLabelPositions() { } void CoverWidget::updateRepeatTrackIcon() { - _repeatTrack->setIcon(instance()->repeatEnabled() ? nullptr : &st::mediaPlayerRepeatInactiveIcon); + _repeatTrack->setIconOverride(instance()->repeatEnabled() ? nullptr : &st::mediaPlayerRepeatInactiveIcon); } void CoverWidget::handleSongUpdate(const UpdatedEvent &e) { @@ -338,9 +338,9 @@ void CoverWidget::handlePlaylistUpdate() { createPrevNextButtons(); auto previousEnabled = (index > 0); auto nextEnabled = (index + 1 < playlist.size()); - _previousTrack->setIcon(previousEnabled ? nullptr : &st::mediaPlayerPanelPreviousDisabledIcon); + _previousTrack->setIconOverride(previousEnabled ? nullptr : &st::mediaPlayerPanelPreviousDisabledIcon); _previousTrack->setCursor(previousEnabled ? style::cur_pointer : style::cur_default); - _nextTrack->setIcon(nextEnabled ? nullptr : &st::mediaPlayerPanelNextDisabledIcon); + _nextTrack->setIconOverride(nextEnabled ? nullptr : &st::mediaPlayerPanelNextDisabledIcon); _nextTrack->setCursor(nextEnabled ? style::cur_pointer : style::cur_default); } } @@ -386,7 +386,7 @@ void CoverWidget::updateVolumeToggleIcon() { } return nullptr; }; - _volumeToggle->setIcon(icon()); + _volumeToggle->setIconOverride(icon()); } } // namespace Player diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 23bc0c0c7..e507cd784 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/continuous_sliders.h" #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" +#include "ui/effects/ripple_animation.h" #include "media/media_audio.h" #include "media/view/media_clip_playback.h" #include "media/player/media_player_button.h" @@ -38,7 +39,7 @@ namespace Player { using State = PlayButtonLayout::State; -class Widget::PlayButton : public Ui::AbstractButton { +class Widget::PlayButton : public Ui::RippleButton { public: PlayButton(QWidget *parent); @@ -52,12 +53,15 @@ public: protected: void paintEvent(QPaintEvent *e) override; + QImage prepareRippleMask() const override; + QPoint prepareRippleStartPosition() const override; + private: PlayButtonLayout _layout; }; -Widget::PlayButton::PlayButton(QWidget *parent) : Ui::AbstractButton(parent) +Widget::PlayButton::PlayButton(QWidget *parent) : Ui::RippleButton(parent, st::mediaPlayerButton.ripple) , _layout(st::mediaPlayerButton, [this] { update(); }) { resize(st::mediaPlayerButtonSize); setCursor(style::cur_pointer); @@ -66,10 +70,20 @@ Widget::PlayButton::PlayButton(QWidget *parent) : Ui::AbstractButton(parent) void Widget::PlayButton::paintEvent(QPaintEvent *e) { Painter p(this); + paintRipple(p, st::mediaPlayerButton.rippleAreaPosition.x(), st::mediaPlayerButton.rippleAreaPosition.y(), getms()); p.translate(st::mediaPlayerButtonPosition.x(), st::mediaPlayerButtonPosition.y()); _layout.paint(p, st::mediaPlayerActiveFg); } +QImage Widget::PlayButton::prepareRippleMask() const { + auto size = QSize(st::mediaPlayerButton.rippleAreaSize, st::mediaPlayerButton.rippleAreaSize); + return Ui::RippleAnimation::ellipseMask(size); +} + +QPoint Widget::PlayButton::prepareRippleStartPosition() const { + return QPoint(mapFromGlobal(QCursor::pos()) - st::mediaPlayerButton.rippleAreaPosition); +} + Widget::Widget(QWidget *parent) : TWidget(parent) , _nameLabel(this, st::mediaPlayerName) , _timeLabel(this, st::mediaPlayerTime) @@ -146,7 +160,7 @@ void Widget::updateVolumeToggleIcon() { } return nullptr; }; - _volumeToggle->setIcon(icon()); + _volumeToggle->setIconOverride(icon()); } void Widget::setCloseCallback(CloseCallback &&callback) { @@ -223,7 +237,7 @@ void Widget::paintEvent(QPaintEvent *e) { Painter p(this); auto fill = e->rect().intersected(QRect(0, 0, width(), st::mediaPlayerHeight)); if (!fill.isEmpty()) { - p.fillRect(fill, st::windowBg); + p.fillRect(fill, st::mediaPlayerBg); } } @@ -290,7 +304,9 @@ void Widget::updateLabelsGeometry() { } void Widget::updateRepeatTrackIcon() { - _repeatTrack->setIcon(instance()->repeatEnabled() ? nullptr : &st::mediaPlayerRepeatDisabledIcon); + auto repeating = instance()->repeatEnabled(); + _repeatTrack->setIconOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledIcon, repeating ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); + _repeatTrack->setRippleColorOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); } void Widget::handleSongUpdate(const UpdatedEvent &e) { @@ -393,9 +409,11 @@ void Widget::handlePlaylistUpdate() { createPrevNextButtons(); auto previousEnabled = (index > 0); auto nextEnabled = (index + 1 < playlist.size()); - _previousTrack->setIcon(previousEnabled ? nullptr : &st::mediaPlayerPreviousDisabledIcon); + _previousTrack->setIconOverride(previousEnabled ? nullptr : &st::mediaPlayerPreviousDisabledIcon); + _previousTrack->setRippleColorOverride(previousEnabled ? nullptr : &st::mediaPlayerBg); _previousTrack->setCursor(previousEnabled ? style::cur_pointer : style::cur_default); - _nextTrack->setIcon(nextEnabled ? nullptr : &st::mediaPlayerNextDisabledIcon); + _nextTrack->setIconOverride(nextEnabled ? nullptr : &st::mediaPlayerNextDisabledIcon); + _nextTrack->setRippleColorOverride(nextEnabled ? nullptr : &st::mediaPlayerBg); _nextTrack->setCursor(nextEnabled ? style::cur_pointer : style::cur_default); } } diff --git a/Telegram/SourceFiles/media/view/media_clip_controller.cpp b/Telegram/SourceFiles/media/view/media_clip_controller.cpp index 4bb35f49a..e3af74d29 100644 --- a/Telegram/SourceFiles/media/view/media_clip_controller.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_controller.cpp @@ -118,7 +118,7 @@ void Controller::updatePlayPauseResumeState(const AudioPlaybackState &playbackSt _showPause = showPause; connect(_playPauseResume, SIGNAL(clicked()), this, _showPause ? SIGNAL(pausePressed()) : SIGNAL(playPressed())); - _playPauseResume->setIcon(_showPause ? &st::mediaviewPauseIcon : nullptr, _showPause ? &st::mediaviewPauseIconOver : nullptr); + _playPauseResume->setIconOverride(_showPause ? &st::mediaviewPauseIcon : nullptr, _showPause ? &st::mediaviewPauseIconOver : nullptr); } } @@ -169,7 +169,7 @@ void Controller::refreshTimeTexts() { } void Controller::setInFullScreen(bool inFullScreen) { - _fullScreenToggle->setIcon(inFullScreen ? &st::mediaviewFullScreenOutIcon : nullptr, inFullScreen ? &st::mediaviewFullScreenOutIconOver : nullptr); + _fullScreenToggle->setIconOverride(inFullScreen ? &st::mediaviewFullScreenOutIcon : nullptr, inFullScreen ? &st::mediaviewFullScreenOutIconOver : nullptr); disconnect(_fullScreenToggle, SIGNAL(clicked()), this, SIGNAL(toFullScreenPressed())); disconnect(_fullScreenToggle, SIGNAL(clicked()), this, SIGNAL(fromFullScreenPressed())); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 181693324..e6422fc4c 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -2241,7 +2241,7 @@ bool MediaView::updateOverState(OverState newState) { if (i != _animOpacities.end()) { i->start(0); } else { - _animOpacities.insert(_over, anim::fvalue(1, 0)); + _animOpacities.insert(_over, anim::value(1, 0)); } if (!_a_state.animating()) _a_state.start(); } else { @@ -2254,7 +2254,7 @@ bool MediaView::updateOverState(OverState newState) { if (i != _animOpacities.end()) { i->start(1); } else { - _animOpacities.insert(_over, anim::fvalue(0, 1)); + _animOpacities.insert(_over, anim::value()); } if (!_a_state.animating()) _a_state.start(); } @@ -2492,7 +2492,7 @@ void MediaView::setVisible(bool visible) { if (!visible) { _controlsHideTimer.stop(); _controlsState = ControlsShown; - a_cOpacity = anim::fvalue(1, 1); + a_cOpacity = anim::value(1, 1); } TWidget::setVisible(visible); if (visible) { diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 8cc2368fe..3fd288f91 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -307,7 +307,7 @@ private: ControlsState _controlsState = ControlsShown; TimeMs _controlsAnimStarted = 0; QTimer _controlsHideTimer; - anim::fvalue a_cOpacity; + anim::value a_cOpacity; bool _mousePressed = false; Ui::PopupMenu *_menu = nullptr; @@ -328,14 +328,14 @@ private: QString _saveMsgFilename; TimeMs _saveMsgStarted = 0; - anim::fvalue _saveMsgOpacity = { 0 }; + anim::value _saveMsgOpacity; QRect _saveMsg; QTimer _saveMsgUpdater; Text _saveMsgText; typedef QMap Showing; Showing _animations; - typedef QMap ShowingOpacities; + typedef QMap ShowingOpacities; ShowingOpacities _animOpacities; int _verticalWheelDelta = 0; diff --git a/Telegram/SourceFiles/mtproto/facade.cpp b/Telegram/SourceFiles/mtproto/facade.cpp index a025225a3..bc3e3fff3 100644 --- a/Telegram/SourceFiles/mtproto/facade.cpp +++ b/Telegram/SourceFiles/mtproto/facade.cpp @@ -358,7 +358,7 @@ namespace { return false; } - bool _paused = false; +int PauseLevel = 0; } // namespace @@ -379,7 +379,20 @@ Session *getSession(ShiftedDcId shiftedDcId) { } bool paused() { - return _paused; + return PauseLevel > 0; +} + +void pause() { + ++PauseLevel; +} + +void unpause() { + --PauseLevel; + if (_started) { + for_const (auto session, sessions) { + session->unpaused(); + } + } } void registerRequest(mtpRequestId requestId, int32 dcWithShift) { @@ -685,19 +698,6 @@ void restart(int32 dcMask) { } } -void pause() { - if (!_started) return; - _paused = true; -} - -void unpause() { - if (!_started) return; - _paused = false; - for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) { - i.value()->unpaused(); - } -} - void configure(int32 dc, int32 user) { if (_started) return; internal::setDC(dc); diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index fced81484..3ae6e6da6 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -25,12 +25,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/single_timer.h" namespace MTP { - namespace internal { Session *getSession(ShiftedDcId shiftedDcId); // 0 - current set dc bool paused(); +void pause(); +void unpause(); void registerRequest(mtpRequestId requestId, int32 dc); void unregisterRequest(mtpRequestId requestId); @@ -96,27 +97,32 @@ constexpr ShiftedDcId lgtDcId(DcId dcId) { } namespace internal { - constexpr ShiftedDcId downloadDcId(DcId dcId, int index) { - static_assert(MTPDownloadSessionsCount < 0x10, "Too large MTPDownloadSessionsCount!"); - return shiftDcId(dcId, 0x10 + index); - }; -} + +constexpr ShiftedDcId downloadDcId(DcId dcId, int index) { + static_assert(MTPDownloadSessionsCount < 0x10, "Too large MTPDownloadSessionsCount!"); + return shiftDcId(dcId, 0x10 + index); +}; + +} // namespace internal // send(req, callbacks, MTP::dldDcId(dc, index)) - for download shifted dc id inline ShiftedDcId dldDcId(DcId dcId, int index) { t_assert(index >= 0 && index < MTPDownloadSessionsCount); return internal::downloadDcId(dcId, index); } + constexpr bool isDldDcId(ShiftedDcId shiftedDcId) { return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, MTPDownloadSessionsCount - 1) + DCShift); } namespace internal { - constexpr ShiftedDcId uploadDcId(DcId dcId, int index) { - static_assert(MTPUploadSessionsCount < 0x10, "Too large MTPUploadSessionsCount!"); - return shiftDcId(dcId, 0x20 + index); - }; -} + +constexpr ShiftedDcId uploadDcId(DcId dcId, int index) { + static_assert(MTPUploadSessionsCount < 0x10, "Too large MTPUploadSessionsCount!"); + return shiftDcId(dcId, 0x20 + index); +}; + +} // namespace internal // send(req, callbacks, MTP::uplDcId(index)) - for upload shifted dc id // uploading always to the main dc so bareDcId == 0 @@ -124,6 +130,7 @@ inline ShiftedDcId uplDcId(int index) { t_assert(index >= 0 && index < MTPUploadSessionsCount); return internal::uploadDcId(0, index); }; + constexpr bool isUplDcId(ShiftedDcId shiftedDcId) { return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, MTPUploadSessionsCount - 1) + DCShift); } @@ -133,8 +140,29 @@ bool started(); void restart(); void restart(int32 dcMask); -void pause(); -void unpause(); +class PauseHolder { +public: + PauseHolder() { + restart(); + } + void restart() { + if (!base::take(_paused, true)) { + internal::pause(); + } + } + void release() { + if (base::take(_paused)) { + internal::unpause(); + } + } + ~PauseHolder() { + release(); + } + +private: + bool _paused = false; + +}; void configure(int32 dc, int32 user); diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index 4356c94b4..ca6a7857c 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -132,7 +132,7 @@ protected: } Ui::RadialAnimation *_radial; - anim::fvalue a_iconOver; + anim::value a_iconOver; Animation _a_iconOver; }; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index e01fc4711..5f1510c7b 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1878,7 +1878,6 @@ OverviewInner::~OverviewInner() { OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewType type) : TWidget(parent) , _scroll(this, st::settingsScroll, false) , _inner(this, _scroll, peer, type) -, _a_show(animation(this, &OverviewWidget::step_show)) , _topShadow(this, st::shadowColor) { _scroll->setOwnedWidget(_inner); _scroll->move(0, 0); @@ -1936,18 +1935,23 @@ void OverviewWidget::paintEvent(QPaintEvent *e) { if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; Painter p(this); + auto progress = _a_show.current(getms(), 1.); if (_a_show.animating()) { - int retina = cIntRetinaFactor(); - int inCacheTop = st::topBarHeight; - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * retina, inCacheTop * retina, a_coordOver.current() * retina, height() * retina)); - p.setOpacity(a_progress.current()); - p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); + auto retina = cIntRetinaFactor(); + auto inCacheTop = st::topBarHeight; + auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft); + auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); + auto coordOver = fromLeft ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto shadow = fromLeft ? (1. - progress) : progress; + if (coordOver > 0) { + p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * retina, inCacheTop * retina, coordOver * retina, height() * retina)); + p.setOpacity(shadow); + p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina)); - p.setOpacity(a_progress.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), height())); + p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina)); + p.setOpacity(shadow); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height())); return; } @@ -1972,16 +1976,21 @@ void OverviewWidget::scrollReset() { bool OverviewWidget::paintTopBar(Painter &p, int decreaseWidth) { if (_a_show.animating()) { - int retina = cIntRetinaFactor(); - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), st::topBarHeight), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, st::topBarHeight * retina)); - p.setOpacity(a_progress.current()); - p.fillRect(0, 0, a_coordOver.current(), st::topBarHeight, st::slideFadeOutBg); + auto progress = _a_show.current(1.); + auto retina = cIntRetinaFactor(); + auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft); + auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); + auto coordOver = fromLeft ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto shadow = fromLeft ? (1. - progress) : progress; + if (coordOver > 0) { + p.drawPixmap(QRect(0, 0, coordOver, st::topBarHeight), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, st::topBarHeight * retina)); + p.setOpacity(shadow); + p.fillRect(0, 0, coordOver, st::topBarHeight, st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina)); - p.setOpacity(a_progress.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), st::topBarHeight)); + p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina)); + p.setOpacity(shadow); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), st::topBarHeight)); return false; } st::topBarBack.paint(p, (st::topBarArrowPadding.left() - st::topBarBack.width()) / 2, (st::topBarHeight - st::topBarBack.height()) / 2, width()); @@ -2080,8 +2089,6 @@ void OverviewWidget::fastShow(bool back, int32 lastScrollTop) { show(); _inner->activate(); doneShow(); - - if (App::app()) App::app()->mtpUnpause(); } void OverviewWidget::setLastScrollTop(int lastScrollTop) { @@ -2090,10 +2097,11 @@ void OverviewWidget::setLastScrollTop(int lastScrollTop) { } void OverviewWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms) { - if (App::app()) App::app()->mtpPause(); - + _showDirection = direction; resizeEvent(0); + _a_show.finish(); + _cacheUnder = params.oldContentCache; show(); _topShadow->setVisible(params.withTopBarShadow ? false : true); @@ -2104,47 +2112,26 @@ void OverviewWidget::showAnimated(Window::SlideDirection direction, const Window _scrollSetAfterShow = _scroll->scrollTop(); _scroll->hide(); - int delta = st::slideShift; - if (direction == Window::SlideDirection::FromLeft) { - a_progress = anim::fvalue(1, 0); + if (_showDirection == Window::SlideDirection::FromLeft) { std::swap(_cacheUnder, _cacheOver); - a_coordUnder = anim::ivalue(-delta, 0); - a_coordOver = anim::ivalue(0, width()); - } else { - a_progress = anim::fvalue(0, 1); - a_coordUnder = anim::ivalue(0, -delta); - a_coordOver = anim::ivalue(width(), 0); } - _a_show.start(); + _a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition()); App::main()->topBar()->update(); activate(); } -void OverviewWidget::step_show(float64 ms, bool timer) { - float64 dt = ms / st::slideDuration; - if (dt >= 1) { - _a_show.stop(); +void OverviewWidget::animationCallback() { + update(); + App::main()->topBar()->update(); + if (!_a_show.animating()) { _topShadow->show(); - a_coordUnder.finish(); - a_coordOver.finish(); - a_progress.finish(); _cacheUnder = _cacheOver = QPixmap(); App::main()->topBar()->stopAnim(); doneShow(); - - if (App::app()) App::app()->mtpUnpause(); - } else { - a_coordUnder.update(dt, Window::SlideAnimation::transition()); - a_coordOver.update(dt, Window::SlideAnimation::transition()); - a_progress.update(dt, Window::SlideAnimation::transition()); - } - if (timer) { - update(); - App::main()->topBar()->update(); } } diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 0e7acdd8d..79f272c6c 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -301,7 +301,6 @@ public: } void setLastScrollTop(int lastScrollTop); void showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms); - void step_show(float64 ms, bool timer); void doneShow(); @@ -355,16 +354,17 @@ public slots: void onClearSelected(); private: + void animationCallback(); + ChildWidget _scroll; ChildWidget _inner; bool _noDropResizeIndex = false; QString _header; - Animation _a_show; + FloatAnimation _a_show; + Window::SlideDirection _showDirection; QPixmap _cacheUnder, _cacheOver; - anim::ivalue a_coordUnder, a_coordOver; - anim::fvalue a_progress; int32 _scrollSetAfterShow = 0; diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index f508036f2..06a4166b1 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -32,7 +32,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/window_slide_animation.h" PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) -, _a_show(animation(this, &PasscodeWidget::step_show)) , _passcode(this, st::passcodeInput) , _submit(this, lang(lng_passcode_submit), st::passcodeSubmit) , _logout(this, lang(lng_passcode_logout)) { @@ -104,52 +103,30 @@ void PasscodeWidget::onChanged() { } } -void PasscodeWidget::animShow(const QPixmap &bgAnimCache, bool back) { - if (App::app()) App::app()->mtpPause(); +void PasscodeWidget::showAnimated(const QPixmap &bgAnimCache, bool back) { + _showBack = back; + (_showBack ? _cacheOver : _cacheUnder) = bgAnimCache; - (back ? _cacheOver : _cacheUnder) = bgAnimCache; - - _a_show.stop(); + _a_show.finish(); showAll(); - (back ? _cacheUnder : _cacheOver) = myGrab(this); + (_showBack ? _cacheUnder : _cacheOver) = myGrab(this); hideAll(); - a_coordUnder = back ? anim::ivalue(-st::slideShift, 0) : anim::ivalue(0, -st::slideShift); - a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0); - a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1); - _a_show.start(); - + _a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition()); show(); } -void PasscodeWidget::step_show(float64 ms, bool timer) { - float64 dt = ms / st::slideDuration; - if (dt >= 1) { - _a_show.stop(); - - a_coordUnder.finish(); - a_coordOver.finish(); - a_shadow.finish(); - - _cacheUnder = _cacheOver = QPixmap(); - +void PasscodeWidget::animationCallback() { + update(); + if (!_a_show.animating()) { showAll(); if (App::wnd()) App::wnd()->setInnerFocus(); - if (App::app()) App::app()->mtpUnpause(); - Ui::showChatsList(); - } else { - a_coordUnder.update(dt, Window::SlideAnimation::transition()); - a_coordOver.update(dt, Window::SlideAnimation::transition()); - a_shadow.update(dt, Window::SlideAnimation::transition()); - } - if (timer) update(); -} -void PasscodeWidget::stop_show() { - _a_show.stop(); + _cacheUnder = _cacheOver = QPixmap(); + } } void PasscodeWidget::showAll() { @@ -173,16 +150,20 @@ void PasscodeWidget::paintEvent(QPaintEvent *e) { p.setClipRect(e->rect()); } + auto progress = _a_show.current(getms(), 1.); if (_a_show.animating()) { - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor())); - p.setOpacity(a_shadow.current()); - p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); + auto coordUnder = _showBack ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress); + auto coordOver = _showBack ? anim::interpolate(0, width(), progress) : anim::interpolate(width(), 0, progress); + auto shadow = _showBack ? (1. - progress) : progress; + if (coordOver > 0) { + p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * cRetinaFactor(), 0, coordOver * cRetinaFactor(), height() * cRetinaFactor())); + p.setOpacity(shadow); + p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(a_coordOver.current(), 0, _cacheOver); - p.setOpacity(a_shadow.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), height())); + p.drawPixmap(coordOver, 0, _cacheOver); + p.setOpacity(shadow); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height())); } else { p.fillRect(rect(), st::windowBg); diff --git a/Telegram/SourceFiles/passcodewidget.h b/Telegram/SourceFiles/passcodewidget.h index 79a789e44..aca9921b7 100644 --- a/Telegram/SourceFiles/passcodewidget.h +++ b/Telegram/SourceFiles/passcodewidget.h @@ -34,9 +34,7 @@ public: void setInnerFocus(); - void animShow(const QPixmap &bgAnimCache, bool back = false); - void step_show(float64 ms, bool timer); - void stop_show(); + void showAnimated(const QPixmap &bgAnimCache, bool back = false); protected: void paintEvent(QPaintEvent *e) override; @@ -48,13 +46,14 @@ public slots: void onSubmit(); private: + void animationCallback(); + void showAll(); void hideAll(); - Animation _a_show; + FloatAnimation _a_show; + bool _showBack = false; QPixmap _cacheUnder, _cacheOver; - anim::ivalue a_coordUnder, a_coordOver; - anim::fvalue a_shadow; ChildWidget _passcode; ChildWidget _submit; diff --git a/Telegram/SourceFiles/platform/win/window_title_win.cpp b/Telegram/SourceFiles/platform/win/window_title_win.cpp index e948614b9..30f6d44a8 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.cpp +++ b/Telegram/SourceFiles/platform/win/window_title_win.cpp @@ -89,9 +89,9 @@ void TitleWidget::onWindowStateChanged(Qt::WindowState state) { void TitleWidget::updateMaximizeRestoreButton() { if (_maximized) { - _maximizeRestore->setIcon(&st::titleButtonRestoreIcon, &st::titleButtonRestoreIconOver); + _maximizeRestore->setIconOverride(&st::titleButtonRestoreIcon, &st::titleButtonRestoreIconOver); } else { - _maximizeRestore->setIcon(nullptr, nullptr); + _maximizeRestore->setIconOverride(nullptr, nullptr); } } diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index 3cffea908..58f24b1c2 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -123,7 +123,7 @@ profileMemberNamePosition: point(68px, 11px); profileMemberNameFg: #222222; profileMemberStatusPosition: point(68px, 31px); profileMemberStatusFg: windowSubTextFg; -profileMemberStatusFgOver: windowSubTextFgOver; +profileMemberStatusFgOver: #7c99b2; profileMemberStatusFgActive: windowActiveTextFg; profileMemberAdminIcon: icon {{ "profile_admin_star", windowBgActive, point(4px, 3px) }}; profileLimitReachedLabel: FlatLabel(defaultFlatLabel) { diff --git a/Telegram/SourceFiles/profile/profile_block_common_groups.cpp b/Telegram/SourceFiles/profile/profile_block_common_groups.cpp index c694d0cb1..b77f993f6 100644 --- a/Telegram/SourceFiles/profile/profile_block_common_groups.cpp +++ b/Telegram/SourceFiles/profile/profile_block_common_groups.cpp @@ -63,7 +63,7 @@ void CommonGroupsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) { int CommonGroupsWidget::resizeGetHeight(int newWidth) { auto result = PeerListWidget::resizeGetHeight(newWidth); - return _height.animating() ? _height.current() : result; + return qRound(_height.current(result)); } void CommonGroupsWidget::paintContents(Painter &p) { diff --git a/Telegram/SourceFiles/profile/profile_block_common_groups.h b/Telegram/SourceFiles/profile/profile_block_common_groups.h index 1524e935a..55d8d54a9 100644 --- a/Telegram/SourceFiles/profile/profile_block_common_groups.h +++ b/Telegram/SourceFiles/profile/profile_block_common_groups.h @@ -65,7 +65,7 @@ private: Item *computeItem(PeerData *group); QMap _dataMap; - IntAnimation _height; + FloatAnimation _height; int32 _preloadGroupId = 0; mtpRequestId _preloadRequestId = 0; diff --git a/Telegram/SourceFiles/profile/profile_block_info.cpp b/Telegram/SourceFiles/profile/profile_block_info.cpp index bbe5d7227..01f032d7b 100644 --- a/Telegram/SourceFiles/profile/profile_block_info.cpp +++ b/Telegram/SourceFiles/profile/profile_block_info.cpp @@ -58,13 +58,17 @@ InfoWidget::InfoWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, pe void InfoWidget::showFinished() { _showFinished = true; if (_commonGroups && _commonGroups->isHidden() && getCommonGroupsCount() > 0) { - _commonGroups->show(); - refreshVisibility(); - _height.start([this] { contentSizeUpdated(); }, isHidden() ? 0 : height(), resizeGetHeight(width()), st::widgetSlideDuration); - contentSizeUpdated(); + slideCommonGroupsDown(); } } +void InfoWidget::slideCommonGroupsDown() { + _commonGroups->show(); + refreshVisibility(); + _height.start([this] { contentSizeUpdated(); }, isHidden() ? 0 : height(), resizeGetHeight(width()), st::widgetSlideDuration); + contentSizeUpdated(); +} + void InfoWidget::restoreState(const SectionMemento *memento) { if (!memento->getCommonGroups().isEmpty()) { onForceHideCommonGroups(); @@ -152,7 +156,7 @@ int InfoWidget::resizeGetHeight(int newWidth) { } newHeight += st::profileBlockMarginBottom; - return _height.animating() ? _height.current() : newHeight; + return qRound(_height.current(newHeight)); } void InfoWidget::leaveEvent(QEvent *e) { @@ -258,8 +262,7 @@ void InfoWidget::refreshCommonGroups() { _commonGroups->setClickedCallback([this] { onShowCommonGroups(); }); _commonGroups->hide(); if (_showFinished) { - _height.start([this] { contentSizeUpdated(); }, isHidden() ? 0 : height(), resizeGetHeight(width()), st::widgetSlideDuration); - contentSizeUpdated(); + slideCommonGroupsDown(); } } } else if (_commonGroups) { diff --git a/Telegram/SourceFiles/profile/profile_block_info.h b/Telegram/SourceFiles/profile/profile_block_info.h index bf1574281..04bee1181 100644 --- a/Telegram/SourceFiles/profile/profile_block_info.h +++ b/Telegram/SourceFiles/profile/profile_block_info.h @@ -68,6 +68,7 @@ private: int getCommonGroupsCount() const; void onForceHideCommonGroups(); void onShowCommonGroups(); + void slideCommonGroupsDown(); // labelWidget may be nullptr. void setLabeledText(ChildWidget *labelWidget, const QString &label, @@ -83,7 +84,7 @@ private: ChildWidget _username = { nullptr }; ChildWidget _commonGroups = { nullptr }; - IntAnimation _height; + FloatAnimation _height; bool _showFinished = false; bool _forceHiddenCommonGroups = false; diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp index 587b5b757..062d4a530 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp @@ -21,11 +21,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "profile/profile_block_peer_list.h" +#include "ui/effects/ripple_animation.h" #include "styles/style_profile.h" #include "styles/style_widgets.h" namespace Profile { +PeerListWidget::Item::Item(PeerData *peer) : peer(peer) { +} + +PeerListWidget::Item::~Item() = default; + PeerListWidget::PeerListWidget(QWidget *parent, PeerData *peer, const QString &title, const QString &removeText) : BlockWidget(parent, peer, title) , _removeText(removeText) @@ -54,38 +60,39 @@ void PeerListWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) { } void PeerListWidget::paintContents(Painter &p) { - int left = getListLeft(); - int top = getListTop(); - int memberRowWidth = width() - left; - accumulate_min(memberRowWidth, st::profileBlockWideWidthMax); + auto ms = getms(); + auto left = getListLeft(); + auto top = getListTop(); + auto memberRowWidth = rowWidth(); - int from = floorclamp(_visibleTop - top, st::profileMemberHeight, 0, _items.size()); - int to = ceilclamp(_visibleBottom - top, st::profileMemberHeight, 0, _items.size()); - for (int i = from; i < to; ++i) { - int y = top + i * st::profileMemberHeight; - bool selected = (i == _selected); - bool selectedRemove = selected && _selectedRemove; - if (_pressed >= 0) { - if (_pressed != _selected) { - selected = selectedRemove = false; - } else if (!_pressedRemove) { - _selectedRemove = false; - } + auto from = floorclamp(_visibleTop - top, st::profileMemberHeight, 0, _items.size()); + auto to = ceilclamp(_visibleBottom - top, st::profileMemberHeight, 0, _items.size()); + for (auto i = from; i < to; ++i) { + auto y = top + i * st::profileMemberHeight; + auto selected = (_pressed >= 0) ? (i == _pressed) : (i == _selected); + auto selectedRemove = selected && _selectedRemove; + if (_pressed >= 0 && !_pressedRemove) { + selectedRemove = false; } - paintItem(p, left, y, _items[i], selected, selectedRemove); + paintItem(p, left, y, _items[i], selected, selectedRemove, ms); } } -void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool selected, bool selectedKick) { +void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool selected, bool selectedKick, TimeMs ms) { if (_updateItemCallback) { _updateItemCallback(item); } - int memberRowWidth = width() - x; - accumulate_min(memberRowWidth, st::profileBlockWideWidthMax); + auto memberRowWidth = rowWidth(); if (selected) { paintOutlinedRect(p, x, y, memberRowWidth, st::profileMemberHeight); } + if (auto &ripple = item->ripple) { + ripple->paint(p, x + st::defaultLeftOutlineButton.outlineWidth, y, width(), ms); + if (ripple->empty()) { + ripple.reset(); + } + } int skip = st::profileMemberPhotoPosition.x(); item->peer->paintUserpicLeft(p, st::profileMemberPhotoSize, x + st::profileMemberPhotoPosition.x(), y + st::profileMemberPhotoPosition.y(), width()); @@ -136,19 +143,36 @@ void PeerListWidget::mousePressEvent(QMouseEvent *e) { _pressed = _selected; _pressedRemove = _selectedRemove; + if (_pressed >= 0 && !_pressedRemove) { + auto item = _items[_pressed]; + if (!item->ripple) { + auto memberRowWidth = rowWidth(); + auto mask = Ui::RippleAnimation::rectMask(QSize(memberRowWidth - st::defaultLeftOutlineButton.outlineWidth, st::profileMemberHeight)); + item->ripple = std_::make_unique(st::defaultLeftOutlineButton.ripple, std_::move(mask), [this, index = _pressed] { + repaintRow(index); + }); + } + auto left = getListLeft() + st::defaultLeftOutlineButton.outlineWidth; + auto top = getListTop() + st::profileMemberHeight * _pressed; + item->ripple->add(e->pos() - QPoint(left, top)); + } } void PeerListWidget::mouseReleaseEvent(QMouseEvent *e) { _mousePosition = e->globalPos(); updateSelection(); - auto pressed = _pressed; - auto pressedRemove = _pressedRemove; - _pressed = -1; - _pressedRemove = false; - if (pressed >= 0 && pressed < _items.size() && pressed == _selected && pressedRemove == _selectedRemove) { - if (auto &callback = (pressedRemove ? _removedCallback : _selectedCallback)) { - callback(_items[pressed]->peer); + repaintRow(_pressed); + auto pressed = base::take(_pressed, -1); + auto pressedRemove = base::take(_pressedRemove); + if (pressed >= 0 && pressed < _items.size()) { + if (auto &ripple = _items[pressed]->ripple) { + ripple->lastStop(); + } + if (pressed == _selected && pressedRemove == _selectedRemove) { + if (auto &callback = (pressedRemove ? _removedCallback : _selectedCallback)) { + callback(_items[pressed]->peer); + } } } setCursor(_selectedRemove ? style::cur_pointer : style::cur_default); @@ -166,15 +190,14 @@ void PeerListWidget::leaveEvent(QEvent *e) { } void PeerListWidget::updateSelection() { - int selected = -1; - bool selectedKick = false; + auto selected = -1; + auto selectedKick = false; auto mouse = mapFromGlobal(_mousePosition); if (rtl()) mouse.setX(width() - mouse.x()); - int left = getListLeft(); - int top = getListTop(); - int memberRowWidth = width() - left; - accumulate_min(memberRowWidth, st::profileBlockWideWidthMax); + auto left = getListLeft(); + auto top = getListTop(); + auto memberRowWidth = rowWidth(); if (mouse.x() >= left && mouse.x() < left + memberRowWidth && mouse.y() >= top) { selected = (mouse.y() - top) / st::profileMemberHeight; if (selected >= _items.size()) { @@ -214,9 +237,13 @@ void PeerListWidget::setSelected(int selected, bool selectedRemove) { } void PeerListWidget::repaintSelectedRow() { - if (_selected >= 0) { - int left = getListLeft(); - rtlupdate(left, getListTop() + _selected * st::profileMemberHeight, width() - left, st::profileMemberHeight); + repaintRow(_selected); +} + +void PeerListWidget::repaintRow(int index) { + if (index >= 0) { + auto left = getListLeft(); + rtlupdate(left, getListTop() + index * st::profileMemberHeight, width() - left, st::profileMemberHeight); } } @@ -224,6 +251,10 @@ int PeerListWidget::getListLeft() const { return st::profileBlockTitlePosition.x() - st::profileMemberPaddingLeft; } +int PeerListWidget::rowWidth() const { + return qMin(width() - getListLeft(), st::profileBlockWideWidthMax); +} + void PeerListWidget::preloadPhotos() { int top = getListTop(); int preloadFor = (_visibleBottom - _visibleTop) * PreloadHeightsCount; diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.h b/Telegram/SourceFiles/profile/profile_block_peer_list.h index a83a4bb79..c88dbb3c2 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.h +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.h @@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_block_widget.h" +namespace Ui { +class RippleAnimation; +} // namespace Ui + namespace Notify { struct PeerUpdate; } // namespace Notify @@ -35,14 +39,16 @@ public: void setVisibleTopBottom(int visibleTop, int visibleBottom) override; struct Item { - explicit Item(PeerData *peer) : peer(peer) { - } + explicit Item(PeerData *peer); + ~Item(); + PeerData * const peer; Text name; QString statusText; bool statusHasOnlineColor = false; bool hasAdminStar = false; bool hasRemoveLink = false; + std_::unique_ptr ripple; }; virtual int getListTop() const { return contentTop(); @@ -111,9 +117,11 @@ private: void updateSelection(); void setSelected(int selected, bool selectedRemove); void repaintSelectedRow(); + void repaintRow(int index); void preloadPhotos(); + int rowWidth() const; - void paintItem(Painter &p, int x, int y, Item *item, bool selected, bool selectedRemove); + void paintItem(Painter &p, int x, int y, Item *item, bool selected, bool selectedRemove, TimeMs ms); base::lambda _preloadMoreCallback; base::lambda _selectedCallback; diff --git a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp index 919ea3f73..77f73b739 100644 --- a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp +++ b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp @@ -60,8 +60,8 @@ protected: Window::TopBarWidget::paintUnreadCounter(p, width()); } - void onStateChanged(int oldState, StateChangeSource source) override { - if ((_state & StateDown) && !(oldState & StateDown)) { + void onStateChanged(State was, StateChangeSource source) override { + if (isDown() && !(was & StateFlag::Down)) { emit clicked(); } } diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 20f880cd4..fe3a17ce8 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -177,7 +177,7 @@ void EmojiColorPicker::step_appearance(float64 ms, bool timer) { void EmojiColorPicker::hideFast() { clearSelection(); if (_a_appearance.animating()) _a_appearance.stop(); - a_opacity = anim::fvalue(0); + a_opacity = anim::value(); _cache = QPixmap(); hide(); emit hidden(); @@ -2899,7 +2899,9 @@ void EmojiPan::paintContent(Painter &p) { paintStickerSettingsIcon(p); if (!_icons.isEmpty()) { - int x = _iconsLeft, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current(); + auto x = _iconsLeft; + auto selxrel = _iconsLeft + qRound(_iconSelX.current()); + auto selx = selxrel - qRound(_iconsX.current()); QRect clip(x, _iconsTop, _iconsLeft + 7 * st::emojiCategory.width - x, st::emojiCategory.height); if (rtl()) clip.moveLeft(width() - x - clip.width()); @@ -2915,9 +2917,10 @@ void EmojiPan::paintContent(Painter &p) { }; int i = 0; - i += _iconsX.current() / int(st::emojiCategory.width); - x -= _iconsX.current() % int(st::emojiCategory.width); - selxrel -= _iconsX.current(); + auto iconsX = qRound(_iconsX.current()); + i += iconsX / int(st::emojiCategory.width); + x -= iconsX % int(st::emojiCategory.width); + selxrel -= iconsX; for (int l = qMin(_icons.size(), i + 8); i < l; ++i) { auto &s = _icons.at(i); if (s.sticker) { @@ -2938,13 +2941,13 @@ void EmojiPan::paintContent(Painter &p) { if (rtl()) selx = width() - selx - st::emojiCategory.width; p.fillRect(selx, _iconsTop + st::emojiCategory.height - st::stickerIconPadding, st::emojiCategory.width, st::stickerIconSel, st::stickerIconSelColor); - float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.width(), 0., 1.); + auto o_left = snap(_iconsX.current() / st::stickerIconLeft.width(), 0., 1.); if (o_left > 0) { p.setOpacity(o_left); st::stickerIconLeft.fill(p, rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiCategory.height, width())); p.setOpacity(1.); } - float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), 0., 1.); + auto o_right = snap((_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), 0., 1.); if (o_right > 0) { p.setOpacity(o_right); st::stickerIconRight.fill(p, rtlrect(_iconsLeft + 7 * st::emojiCategory.width - st::stickerIconRight.width(), _iconsTop, st::stickerIconRight.width(), st::emojiCategory.height, width())); @@ -3019,7 +3022,7 @@ void EmojiPan::mousePressEvent(QMouseEvent *e) { } else { _iconDown = _iconOver; _iconsMouseDown = _iconsMousePos; - _iconsStartX = _iconsX.current(); + _iconsStartX = qRound(_iconsX.current()); } } @@ -3034,9 +3037,9 @@ void EmojiPan::mouseMoveEvent(QMouseEvent *e) { } } if (_iconsDragging) { - int32 newX = snap(_iconsStartX + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()), 0, _iconsMax); - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); + auto newX = snap(_iconsStartX + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()), 0, _iconsMax); + if (newX != qRound(_iconsX.current())) { + _iconsX = anim::value(newX, newX); _iconsStartAnim = 0; _a_icons.stop(); updateIcons(); @@ -3052,9 +3055,9 @@ void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { _iconsMousePos = e ? e->globalPos() : QCursor::pos(); if (_iconsDragging) { - int32 newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax); - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); + auto newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax); + if (newX != qRound(_iconsX.current())) { + _iconsX = anim::value(newX, newX); _iconsStartAnim = 0; _a_icons.stop(); updateIcons(); @@ -3065,7 +3068,7 @@ void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { updateSelected(); if (wasDown == _iconOver && _iconOver >= 0 && _iconOver < _icons.size()) { - _iconSelX = anim::ivalue(_iconOver * st::emojiCategory.width, _iconOver * st::emojiCategory.width); + _iconSelX = anim::value(_iconOver * st::emojiCategory.width, _iconOver * st::emojiCategory.width); s_inner->showStickerSet(_icons.at(_iconOver).setId); } } @@ -3080,14 +3083,14 @@ bool EmojiPan::event(QEvent *e) { bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal); bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical); if (hor) _horizontal = true; - int32 newX = _iconsX.current(); + auto newX = qRound(_iconsX.current()); if (/*_horizontal && */hor) { newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax); } else if (/*!_horizontal && */ver) { newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax); } - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); + if (newX != qRound(_iconsX.current())) { + _iconsX = anim::value(newX, newX); _iconsStartAnim = 0; _a_icons.stop(); updateSelected(); @@ -3138,7 +3141,7 @@ void EmojiPan::onRefreshIcons(bool scrollAnimation) { _iconsMax = qMax(int((_icons.size() - 7) * st::emojiCategory.width), 0); } if (_iconsX.current() > _iconsMax) { - _iconsX = anim::ivalue(_iconsMax, _iconsMax); + _iconsX = anim::value(_iconsMax, _iconsMax); } updatePanelsPositions(s_panels, s_scroll->scrollTop()); updateSelected(); @@ -3178,7 +3181,7 @@ void EmojiPan::updateSelected() { newOver = _icons.size(); } else if (!_icons.isEmpty()) { if (y >= _iconsTop && y < _iconsTop + st::emojiCategory.height && x >= 0 && x < 7 * st::emojiCategory.width && x < _icons.size() * st::emojiCategory.width) { - x += _iconsX.current(); + x += qRound(_iconsX.current()); newOver = qFloor(x / st::emojiCategory.width); } } @@ -3329,8 +3332,8 @@ void EmojiPan::hideFinished() { s_scroll->scrollToY(0); _iconOver = _iconDown = -1; _iconSel = 0; - _iconsX = anim::ivalue(0, 0); - _iconSelX = anim::ivalue(0, 0); + _iconsX = anim::value(); + _iconSelX = anim::value(); _iconsStartAnim = 0; _a_icons.stop(); @@ -3512,14 +3515,14 @@ void EmojiPan::onScrollEmoji() { } void EmojiPan::setCurrentTabIcon(DBIEmojiTab tab) { - _recent->setIcon((tab == dbietRecent) ? &st::emojiRecentActive : nullptr); - _people->setIcon((tab == dbietPeople) ? &st::emojiPeopleActive : nullptr); - _nature->setIcon((tab == dbietNature) ? &st::emojiNatureActive : nullptr); - _food->setIcon((tab == dbietFood) ? &st::emojiFoodActive : nullptr); - _activity->setIcon((tab == dbietActivity) ? &st::emojiActivityActive : nullptr); - _travel->setIcon((tab == dbietTravel) ? &st::emojiTravelActive : nullptr); - _objects->setIcon((tab == dbietObjects) ? &st::emojiObjectsActive : nullptr); - _symbols->setIcon((tab == dbietSymbols) ? &st::emojiSymbolsActive : nullptr); + _recent->setIconOverride((tab == dbietRecent) ? &st::emojiRecentActive : nullptr); + _people->setIconOverride((tab == dbietPeople) ? &st::emojiPeopleActive : nullptr); + _nature->setIconOverride((tab == dbietNature) ? &st::emojiNatureActive : nullptr); + _food->setIconOverride((tab == dbietFood) ? &st::emojiFoodActive : nullptr); + _activity->setIconOverride((tab == dbietActivity) ? &st::emojiActivityActive : nullptr); + _travel->setIconOverride((tab == dbietTravel) ? &st::emojiTravelActive : nullptr); + _objects->setIconOverride((tab == dbietObjects) ? &st::emojiObjectsActive : nullptr); + _symbols->setIconOverride((tab == dbietSymbols) ? &st::emojiSymbolsActive : nullptr); } void EmojiPan::onScrollStickers() { @@ -3550,11 +3553,11 @@ void EmojiPan::validateSelectedIcon(ValidateIconAnimations animations) { if (animations == ValidateIconAnimations::Full) { _iconSelX.start(iconSelXFinal); } else { - _iconSelX = anim::ivalue(iconSelXFinal, iconSelXFinal); + _iconSelX = anim::value(iconSelXFinal, iconSelXFinal); } auto iconsXFinal = snap((2 * newSel - 7) * int(st::emojiCategory.width) / 2, 0, _iconsMax); if (animations == ValidateIconAnimations::None) { - _iconsX = anim::ivalue(iconsXFinal, iconsXFinal); + _iconsX = anim::value(iconsXFinal, iconsXFinal); _a_icons.stop(); } else { _iconsX.start(iconsXFinal); diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index 5e63c00fa..463f43fab 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -108,7 +108,7 @@ private: bool _hiding = false; QPixmap _cache; - anim::fvalue a_opacity; + anim::value a_opacity; Animation _a_appearance; QTimer _hideTimer; @@ -661,8 +661,8 @@ private: int _iconsTop = 0; int _iconsStartX = 0; int _iconsMax = 0; - anim::ivalue _iconsX = { 0, 0 }; - anim::ivalue _iconSelX = { 0, 0 }; + anim::value _iconsX; + anim::value _iconSelX; TimeMs _iconsStartAnim = 0; bool _emojiShown = true; diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index b1c2850cb..98a201c9f 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -190,4 +190,10 @@ hashtagClose: IconButton { icon: simpleCloseIcon; iconOver: simpleCloseIconOver; iconPosition: point(10px, 10px); + + rippleAreaPosition: point(5px, 5px); + rippleAreaSize: 20px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } } diff --git a/Telegram/SourceFiles/ui/abstract_button.cpp b/Telegram/SourceFiles/ui/abstract_button.cpp index 22313bb27..ca61f508c 100644 --- a/Telegram/SourceFiles/ui/abstract_button.cpp +++ b/Telegram/SourceFiles/ui/abstract_button.cpp @@ -24,15 +24,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { void AbstractButton::leaveEvent(QEvent *e) { - if (_state & StateDown) return; + if (_state & StateFlag::Down) return; setOver(false, StateChangeSource::ByHover); return TWidget::leaveEvent(e); } void AbstractButton::enterEvent(QEvent *e) { - auto over = rect().marginsRemoved(getMargins()).contains(mapFromGlobal(QCursor::pos())); - setOver(over, StateChangeSource::ByHover); + checkIfOver(mapFromGlobal(QCursor::pos())); return TWidget::enterEvent(e); } @@ -40,12 +39,18 @@ void AbstractButton::setAcceptBoth(bool acceptBoth) { _acceptBoth = acceptBoth; } +void AbstractButton::checkIfOver(QPoint localPos) { + auto over = rect().marginsRemoved(getMargins()).contains(localPos); + setOver(over, StateChangeSource::ByHover); +} + void AbstractButton::mousePressEvent(QMouseEvent *e) { + checkIfOver(e->pos()); if (_acceptBoth || (e->buttons() & Qt::LeftButton)) { - if ((_state & StateOver) && !(_state & StateDown)) { - int oldState = _state; - _state |= StateDown; - onStateChanged(oldState, StateChangeSource::ByPress); + if ((_state & StateFlag::Over) && !(_state & StateFlag::Down)) { + auto was = _state; + _state |= StateFlag::Down; + onStateChanged(was, StateChangeSource::ByPress); e->accept(); } @@ -61,11 +66,11 @@ void AbstractButton::mouseMoveEvent(QMouseEvent *e) { } void AbstractButton::mouseReleaseEvent(QMouseEvent *e) { - if (_state & StateDown) { - int oldState = _state; - _state &= ~StateDown; - onStateChanged(oldState, StateChangeSource::ByPress); - if (oldState & StateOver) { + if (_state & StateFlag::Down) { + auto was = _state; + _state &= ~State(StateFlag::Down); + onStateChanged(was, StateChangeSource::ByPress); + if (was & StateFlag::Over) { _modifiers = e->modifiers(); if (_clickedCallback) { _clickedCallback(); @@ -73,43 +78,39 @@ void AbstractButton::mouseReleaseEvent(QMouseEvent *e) { emit clicked(); } } else { - leaveEvent(e); + setOver(false, StateChangeSource::ByHover); } } } void AbstractButton::setOver(bool over, StateChangeSource source) { setCursor(over ? style::cur_pointer : style::cur_default); - if (over && !(_state & StateOver)) { - int oldState = _state; - _state |= StateOver; - onStateChanged(oldState, source); - } else if (!over && (_state & StateOver)) { - int oldState = _state; - _state &= ~StateOver; - onStateChanged(oldState, source); + if (over && !(_state & StateFlag::Over)) { + auto was = _state; + _state |= StateFlag::Over; + onStateChanged(was, source); + } else if (!over && (_state & StateFlag::Over)) { + auto was = _state; + _state &= ~State(StateFlag::Over); + onStateChanged(was, source); } } void AbstractButton::setDisabled(bool disabled) { - int oldState = _state; - if (disabled && !(_state & StateDisabled)) { - _state |= StateDisabled; - onStateChanged(oldState, StateChangeSource::ByUser); - } else if (!disabled && (_state & StateDisabled)) { - _state &= ~StateDisabled; - onStateChanged(oldState, StateChangeSource::ByUser); + auto was = _state; + if (disabled && !(_state & StateFlag::Disabled)) { + _state |= StateFlag::Disabled; + onStateChanged(was, StateChangeSource::ByUser); + } else if (!disabled && (_state & StateFlag::Disabled)) { + _state &= ~State(StateFlag::Disabled); + onStateChanged(was, StateChangeSource::ByUser); } } void AbstractButton::clearState() { - int oldState = _state; - _state = StateNone; - onStateChanged(oldState, StateChangeSource::ByUser); -} - -int AbstractButton::getState() const { - return _state; + auto was = _state; + _state = StateFlag::None; + onStateChanged(was, StateChangeSource::ByUser); } } // namespace Ui diff --git a/Telegram/SourceFiles/ui/abstract_button.h b/Telegram/SourceFiles/ui/abstract_button.h index 961a9c81d..97fe8e0be 100644 --- a/Telegram/SourceFiles/ui/abstract_button.h +++ b/Telegram/SourceFiles/ui/abstract_button.h @@ -28,34 +28,24 @@ class AbstractButton : public TWidget { Q_OBJECT public: - enum class StateChangeSource { - ByUser = 0x00, - ByPress = 0x01, - ByHover = 0x02, - }; - AbstractButton(QWidget *parent) : TWidget(parent) { setMouseTracking(true); } - enum { - StateNone = 0x00, - StateOver = 0x01, - StateDown = 0x02, - StateDisabled = 0x04, - }; - Qt::KeyboardModifiers clickModifiers() const { return _modifiers; } - void clearState(); - int getState() const; - void setDisabled(bool disabled = true); - void setOver(bool over, StateChangeSource source = StateChangeSource::ByUser); - bool disabled() const { - return (_state & StateDisabled); + void clearState(); + bool isOver() const { + return _state & StateFlag::Over; + } + bool isDown() const { + return _state & StateFlag::Down; + } + bool isDisabled() { + return _state & StateFlag::Disabled; } void setAcceptBoth(bool acceptBoth = true); @@ -75,15 +65,41 @@ signals: void clicked(); protected: - virtual void onStateChanged(int oldState, StateChangeSource source) { + enum class StateFlag { + None = 0x00, + Over = 0x01, + Down = 0x02, + Disabled = 0x04, + }; + Q_DECLARE_FLAGS(State, StateFlag); + Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(State); + + State state() const { + return _state; } - Qt::KeyboardModifiers _modifiers; - int _state = StateNone; + enum class StateChangeSource { + ByUser = 0x00, + ByPress = 0x01, + ByHover = 0x02, + }; + void setOver(bool over, StateChangeSource source = StateChangeSource::ByUser); + + virtual void onStateChanged(State was, StateChangeSource source) { + } + +private: + void checkIfOver(QPoint localPos); + + State _state = StateFlag::None; + bool _acceptBoth = false; + Qt::KeyboardModifiers _modifiers; base::lambda _clickedCallback; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractButton::State); + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/animation.h b/Telegram/SourceFiles/ui/animation.h index 7b1f005ce..0c7c2ac97 100644 --- a/Telegram/SourceFiles/ui/animation.h +++ b/Telegram/SourceFiles/ui/animation.h @@ -158,56 +158,6 @@ private: }; -using fvalue = value; - -class ivalue { // int animated value -public: - using ValueType = int; - - ivalue() = default; - ivalue(int from) : _cur(from), _from(float64(from)) { - } - ivalue(int from, int to) : _cur(from), _from(float64(from)), _delta(float64(to - from)) { - } - void start(int32 to) { - _from = float64(_cur); - _delta = float64(to) - _from; - } - void restart() { - _delta = _from + _delta - float64(_cur); - _from = float64(_cur); - } - - int from() const { - return _from; - } - int current() const { - return _cur; - } - int to() const { - return qRound(_from + _delta); - } - void add(int delta) { - _from += delta; - _cur += delta; - } - ivalue &update(float64 dt, const transition &func) { - _cur = qRound(_from + func(_delta, dt)); - return *this; - } - void finish() { - _cur = qRound(_from + _delta); - _from = _cur; - _delta = 0; - } - -private: - int _cur = 0; - float64 _from = 0.; - float64 _delta = 0.; - -}; - void startManager(); void stopManager(); void registerClipManager(Media::Clip::Manager *manager); @@ -639,7 +589,9 @@ public: template void start(Lambda &&updateCallback, const ValueType &from, const ValueType &to, float64 duration, const anim::transition &transition = anim::linear) { - if (!_data) { + if (_data) { + _data->pause.restart(); + } else { _data = std_::make_unique(from, std_::forward(updateCallback)); } _data->value.start(to); @@ -674,6 +626,7 @@ private: if (dt >= 1) { value.finish(); a_animation.stop(); + pause.release(); } else { value.update(dt, transition); } @@ -685,13 +638,13 @@ private: Callback updateCallback; float64 duration = 0.; anim::transition transition = anim::linear; + MTP::PauseHolder pause; }; mutable std_::unique_ptr _data; }; -using FloatAnimation = SimpleAnimation; -using IntAnimation = SimpleAnimation; +using FloatAnimation = SimpleAnimation; class AnimationManager : public QObject { Q_OBJECT diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp index a3de96871..1706d1b8d 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp @@ -55,8 +55,8 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) { return; } p.setOpacity(opacity); - auto over = (_state & StateOver); - auto down = (_state & StateDown); + auto over = isOver(); + auto down = isDown(); ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width()); paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms); ((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width()); @@ -118,7 +118,7 @@ void HistoryDownButton::step_arrowOver(float64 ms, bool timer) { if (timer) update(); } -EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : AbstractButton(parent) +EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : RippleButton(parent, st.ripple) , _st(st) , _a_loading(animation(this, &EmojiButton::step_loading)) { resize(_st.width, _st.height); @@ -131,11 +131,12 @@ void EmojiButton::paintEvent(QPaintEvent *e) { auto ms = getms(); p.fillRect(e->rect(), st::historyComposeAreaBg); + paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms); auto loading = a_loading.current(ms, _loading ? 1 : 0); p.setOpacity(1 - loading); - auto over = (_state & StateOver); + auto over = isOver(); auto icon = &(over ? _st.iconOver : _st.icon); icon->paint(p, _st.iconPosition, width()); @@ -170,11 +171,20 @@ void EmojiButton::setLoading(bool loading) { } } -void EmojiButton::onStateChanged(int oldState, StateChangeSource source) { - auto over = (_state & StateOver); - if (over != (oldState & StateOver)) { +void EmojiButton::onStateChanged(State was, StateChangeSource source) { + RippleButton::onStateChanged(was, source); + auto wasOver = static_cast(was & StateFlag::Over); + if (isOver() != wasOver) { update(); } } +QPoint EmojiButton::prepareRippleStartPosition() const { + return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition; +} + +QImage EmojiButton::prepareRippleMask() const { + return RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize)); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.h b/Telegram/SourceFiles/ui/buttons/history_down_button.h index 8c427b067..ab009c1d1 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.h +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.h @@ -55,7 +55,7 @@ private: bool _shown = false; - anim::fvalue a_arrowOpacity; + anim::value a_arrowOpacity; Animation _a_arrowOver; FloatAnimation _a_show; @@ -64,7 +64,7 @@ private: }; -class EmojiButton : public AbstractButton { +class EmojiButton : public RippleButton { public: EmojiButton(QWidget *parent, const style::IconButton &st); @@ -72,7 +72,10 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, StateChangeSource source) override; + void onStateChanged(State was, StateChangeSource source) override; + + QImage prepareRippleMask() const override; + QPoint prepareRippleStartPosition() const override; private: const style::IconButton &_st; diff --git a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp index fd62244f6..136c04817 100644 --- a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp @@ -54,7 +54,7 @@ void NewAvatarButton::paintEvent(QPaintEvent *e) { } p.setPen(Qt::NoPen); - p.setBrush((_state & StateOver) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg); + p.setBrush(isOver() ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg); { PainterHighQualityEnabler hq(p); p.drawEllipse(rect()); diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index 12870b966..bf8c3cfe1 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/multi_select.h" +#include "ui/effects/ripple_animation.h" #include "boxes/contactsbox.h" #include "countries.h" #include "styles/style_boxes.h" @@ -283,6 +284,7 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); p.setClipRect(r); + auto ms = getms(); int l = countriesNow->size(); if (l) { if (r.intersects(QRect(0, 0, width(), st::countriesSkip))) { @@ -291,10 +293,16 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) { int32 from = floorclamp(r.y() - st::countriesSkip, _rowHeight, 0, l); int32 to = ceilclamp(r.y() + r.height() - st::countriesSkip, _rowHeight, 0, l); for (int32 i = from; i < to; ++i) { - bool sel = (i == _sel); - int32 y = st::countriesSkip + i * _rowHeight; + auto selected = (i == (_pressed >= 0 ? _pressed : _selected)); + auto y = st::countriesSkip + i * _rowHeight; - p.fillRect(0, y, width(), _rowHeight, sel ? st::countryRowBgOver : st::countryRowBg); + p.fillRect(0, y, width(), _rowHeight, selected ? st::countryRowBgOver : st::countryRowBg); + if (_ripples.size() > i && _ripples[i]) { + _ripples[i]->paint(p, 0, y, width(), ms); + if (_ripples[i]->empty()) { + _ripples[i].reset(); + } + } QString code = QString("+") + (*countriesNow)[i]->code; int32 codeWidth = st::countryRowCodeFont->width(code); @@ -312,7 +320,7 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) { p.drawTextLeft(st::countryRowPadding.left(), y + st::countryRowPadding.top(), width(), name); p.setFont(st::countryRowCodeFont); - p.setPen(sel ? st::countryRowCodeFgOver : st::countryRowCodeFg); + p.setPen(selected ? st::countryRowCodeFgOver : st::countryRowCodeFg); p.drawTextLeft(st::countryRowPadding.left() + nameWidth + st::countryRowPadding.right(), y + st::countryRowPadding.top(), width(), code); } } else { @@ -328,26 +336,49 @@ void CountrySelectBox::Inner::enterEvent(QEvent *e) { } void CountrySelectBox::Inner::leaveEvent(QEvent *e) { - _mouseSel = false; + _mouseSelection = false; setMouseTracking(false); - if (_sel >= 0) { + if (_selected >= 0) { updateSelectedRow(); - _sel = -1; + _selected = -1; } } void CountrySelectBox::Inner::mouseMoveEvent(QMouseEvent *e) { - _mouseSel = true; - _lastMousePos = e->globalPos(); - updateSel(); + _mouseSelection = true; + updateSelected(e->pos()); } void CountrySelectBox::Inner::mousePressEvent(QMouseEvent *e) { - _mouseSel = true; - _lastMousePos = e->globalPos(); - updateSel(); + _mouseSelection = true; + updateSelected(e->pos()); + + setPressed(_selected); + if (_pressed >= 0 && _pressed < countriesNow->size()) { + if (_ripples.size() <= _pressed) { + _ripples.reserve(_pressed + 1); + while (_ripples.size() <= _pressed) { + _ripples.push_back(std_::unique_ptr()); + } + } + if (!_ripples[_pressed]) { + auto mask = Ui::RippleAnimation::rectMask(QSize(width(), _rowHeight)); + _ripples[_pressed] = std_::make_unique(st::countryRipple, std_::move(mask), [this, index = _pressed] { + updateRow(index); + }); + _ripples[_pressed]->add(e->pos() - QPoint(0, st::countriesSkip + _pressed * _rowHeight)); + } + } +} + +void CountrySelectBox::Inner::mouseReleaseEvent(QMouseEvent *e) { + auto pressed = _pressed; + setPressed(-1); + updateSelectedRow(); if (e->button() == Qt::LeftButton) { - chooseCountry(); + if ((pressed >= 0) && pressed == _selected) { + chooseCountry(); + } } } @@ -401,25 +432,25 @@ void CountrySelectBox::Inner::updateFilter(QString filter) { countriesNow = &countriesFiltered; } refresh(); - _sel = countriesNow->isEmpty() ? -1 : 0; + _selected = countriesNow->isEmpty() ? -1 : 0; update(); } } void CountrySelectBox::Inner::selectSkip(int32 dir) { - _mouseSel = false; + _mouseSelection = false; - int cur = (_sel >= 0) ? _sel : -1; + int cur = (_selected >= 0) ? _selected : -1; cur += dir; if (cur <= 0) { - _sel = countriesNow->isEmpty() ? -1 : 0; + _selected = countriesNow->isEmpty() ? -1 : 0; } else if (cur >= countriesNow->size()) { - _sel = -1; + _selected = -1; } else { - _sel = cur; + _selected = cur; } - if (_sel >= 0) { - emit mustScrollTo(st::countriesSkip + _sel * _rowHeight, st::countriesSkip + (_sel + 1) * _rowHeight); + if (_selected >= 0) { + emit mustScrollTo(st::countriesSkip + _selected * _rowHeight, st::countriesSkip + (_selected + 1) * _rowHeight); } update(); } @@ -433,12 +464,12 @@ void CountrySelectBox::Inner::selectSkipPage(int32 h, int32 dir) { void CountrySelectBox::Inner::chooseCountry() { QString result; if (_filter.isEmpty()) { - if (_sel >= 0 && _sel < countriesAll.size()) { - result = countriesAll[_sel]->iso2; + if (_selected >= 0 && _selected < countriesAll.size()) { + result = countriesAll[_selected]->iso2; } } else { - if (_sel >= 0 && _sel < countriesFiltered.size()) { - result = countriesFiltered[_sel]->iso2; + if (_selected >= 0 && _selected < countriesFiltered.size()) { + result = countriesFiltered[_selected]->iso2; } } emit countryChosen(result); @@ -448,22 +479,34 @@ void CountrySelectBox::Inner::refresh() { resize(width(), countriesNow->length() ? (countriesNow->length() * _rowHeight + st::countriesSkip) : st::noContactsHeight); } -void CountrySelectBox::Inner::updateSel() { - if (!_mouseSel) return; +void CountrySelectBox::Inner::updateSelected(QPoint localPos) { + if (!_mouseSelection) return; - QPoint p(mapFromGlobal(_lastMousePos)); - bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); + auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(QCursor::pos())); - int32 newSel = (in && p.y() >= st::countriesSkip && p.y() < st::countriesSkip + countriesNow->size() * _rowHeight) ? ((p.y() - st::countriesSkip) / _rowHeight) : -1; - if (newSel != _sel) { + auto selected = (in && localPos.y() >= st::countriesSkip && localPos.y() < st::countriesSkip + countriesNow->size() * _rowHeight) ? ((localPos.y() - st::countriesSkip) / _rowHeight) : -1; + if (_selected != selected) { updateSelectedRow(); - _sel = newSel; + _selected = selected; updateSelectedRow(); } } void CountrySelectBox::Inner::updateSelectedRow() { - if (_sel >= 0) { - update(0, st::countriesSkip + _sel * _rowHeight, width(), _rowHeight); + updateRow(_selected); +} + +void CountrySelectBox::Inner::updateRow(int index) { + if (index >= 0) { + update(0, st::countriesSkip + index * _rowHeight, width(), _rowHeight); } } + +void CountrySelectBox::Inner::setPressed(int pressed) { + if (_pressed >= 0 && _pressed < _ripples.size() && _ripples[_pressed]) { + _ripples[_pressed]->lastStop(); + } + _pressed = pressed; +} + +CountrySelectBox::Inner::~Inner() = default; diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index ae3b03bbc..014b872e8 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -108,29 +108,36 @@ public: void refresh(); + ~Inner(); + signals: void countryChosen(const QString &iso); void mustScrollTo(int ymin, int ymax); -public slots: - void updateSel(); - protected: void paintEvent(QPaintEvent *e) override; void enterEvent(QEvent *e) override; void leaveEvent(QEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; private: + void updateSelected() { + updateSelected(mapFromGlobal(QCursor::pos())); + } + void updateSelected(QPoint localPos); void updateSelectedRow(); + void updateRow(int index); + void setPressed(int pressed); int _rowHeight; - int _sel = 0; + int _selected = -1; + int _pressed = -1; QString _filter; - bool _mouseSel = false; + bool _mouseSelection = false; - QPoint _lastMousePos; + std_::vector_of_moveable> _ripples; }; diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.cpp b/Telegram/SourceFiles/ui/effects/radial_animation.cpp index 801dc48bc..80cb84b96 100644 --- a/Telegram/SourceFiles/ui/effects/radial_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/radial_animation.cpp @@ -24,45 +24,45 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks) - : a_arcEnd(0, 0) - , a_arcStart(0, FullArcLength) + : a_arcStart(0, FullArcLength) , _animation(std_::move(callbacks)) { } void RadialAnimation::start(float64 prg) { _firstStart = _lastStart = _lastTime = getms(); int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength), iprgstrict = qRound(prg * AlmostFullArcLength); - a_arcEnd = anim::ivalue(iprgstrict, iprg); + a_arcEnd = anim::value(iprgstrict, iprg); _animation.start(); } void RadialAnimation::update(float64 prg, bool finished, TimeMs ms) { - int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength); - if (iprg != a_arcEnd.to()) { + auto iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength); + if (iprg != qRound(a_arcEnd.to())) { a_arcEnd.start(iprg); _lastStart = _lastTime; } _lastTime = ms; - float64 dt = float64(ms - _lastStart), fulldt = float64(ms - _firstStart); + auto dt = float64(ms - _lastStart); + auto fulldt = float64(ms - _firstStart); _opacity = qMin(fulldt / st::radialDuration, 1.); if (!finished) { a_arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear); } else if (dt >= st::radialDuration) { - a_arcEnd.update(1, anim::linear); + a_arcEnd.update(1., anim::linear); stop(); } else { - float64 r = dt / st::radialDuration; + auto r = dt / st::radialDuration; a_arcEnd.update(r, anim::linear); _opacity *= 1 - r; } - float64 fromstart = fulldt / st::radialPeriod; + auto fromstart = fulldt / st::radialPeriod; a_arcStart.update(fromstart - std::floor(fromstart), anim::linear); } void RadialAnimation::stop() { _firstStart = _lastStart = _lastTime = 0; - a_arcEnd = anim::ivalue(0, 0); + a_arcEnd = anim::value(); _animation.stop(); } @@ -79,8 +79,8 @@ void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, cons pen.setCapStyle(Qt::RoundCap); p.setPen(pen); - int32 len = MinArcLength + a_arcEnd.current(); - int32 from = QuarterArcLength - a_arcStart.current() - len; + auto len = MinArcLength + qRound(a_arcEnd.current()); + auto from = QuarterArcLength - qRound(a_arcStart.current()) - len; if (rtl()) { from = QuarterArcLength - (from - QuarterArcLength) - len; if (from < 0) from += FullArcLength; diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.h b/Telegram/SourceFiles/ui/effects/radial_animation.h index dcc1130b9..c648f01bb 100644 --- a/Telegram/SourceFiles/ui/effects/radial_animation.h +++ b/Telegram/SourceFiles/ui/effects/radial_animation.h @@ -49,7 +49,8 @@ private: TimeMs _lastStart = 0; TimeMs _lastTime = 0; float64 _opacity = 0.; - anim::ivalue a_arcEnd, a_arcStart; + anim::value a_arcEnd; + anim::value a_arcStart; Animation _animation; }; diff --git a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp index 865e2829f..e77e8b71b 100644 --- a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp @@ -92,7 +92,7 @@ void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, TimeMs ms, return; } - if (_cache.isNull()) { + if (_cache.isNull() || colorOverride != nullptr) { auto radius = anim::interpolate(_radiusFrom, _radiusTo, _show.current(ms, 1.)); _frame.fill(Qt::transparent); { @@ -110,7 +110,7 @@ void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, TimeMs ms, p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.drawPixmap(0, 0, mask); } - if (radius == _radiusTo) { + if (radius == _radiusTo && colorOverride == nullptr) { _cache = App::pixmapFromImageInPlace(std_::move(_frame)); } } diff --git a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.cpp b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.cpp index 786c71945..353636c8e 100644 --- a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.cpp +++ b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.cpp @@ -51,9 +51,9 @@ void WidgetSlideWrap::slideUp() { if (_a_height.animating()) { if (_hiding) return; } else { - a_height = anim::ivalue(_realSize.height()); + a_height = anim::value(_realSize.height()); } - a_height.start(0); + a_height.start(0.); _hiding = true; _a_height.start(); } @@ -70,7 +70,7 @@ void WidgetSlideWrap::slideDown() { if (!_hiding) return; } a_height.start(_realSize.height()); - _forceHeight = a_height.current(); + _forceHeight = qRound(a_height.current()); _hiding = false; _a_height.start(); } @@ -87,7 +87,7 @@ void WidgetSlideWrap::showFast() { void WidgetSlideWrap::hideFast() { _a_height.stop(); - a_height = anim::ivalue(0); + a_height = anim::value(); _forceHeight = 0; resizeToWidth(_realSize.width()); hide(); @@ -144,7 +144,7 @@ void WidgetSlideWrap::step_height(float64 ms, bool timer) { if (_hiding) hide(); } else { a_height.update(dt, anim::linear); - _forceHeight = a_height.current(); + _forceHeight = qRound(a_height.current()); } resizeToWidth(_realSize.width()); if (_updateCallback) { diff --git a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h index fb859c12b..dbbe697d2 100644 --- a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h +++ b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h @@ -66,7 +66,7 @@ private: style::size _realSize; int _forceHeight = -1; - anim::ivalue a_height; + anim::value a_height; Animation _a_height; bool _hiding = false; diff --git a/Telegram/SourceFiles/ui/widgets/buttons.cpp b/Telegram/SourceFiles/ui/widgets/buttons.cpp index b41231948..5c77c8bdc 100644 --- a/Telegram/SourceFiles/ui/widgets/buttons.cpp +++ b/Telegram/SourceFiles/ui/widgets/buttons.cpp @@ -40,8 +40,8 @@ int LinkButton::naturalWidth() const { void LinkButton::paintEvent(QPaintEvent *e) { Painter p(this); - auto &font = ((_state & StateOver) ? _st.overFont : _st.font); - auto &pen = ((_state & StateDown) ? _st.downColor : ((_state & StateOver) ? _st.overColor : _st.color)); + auto &font = (isOver() ? _st.overFont : _st.font); + auto &pen = (isDown() ? _st.downColor : (isOver() ? _st.overColor : _st.color)); p.setFont(font); p.setPen(pen); if (_textWidth > width()) { @@ -58,7 +58,7 @@ void LinkButton::setText(const QString &text) { update(); } -void LinkButton::onStateChanged(int oldState, StateChangeSource source) { +void LinkButton::onStateChanged(State was, StateChangeSource source) { update(); } @@ -95,11 +95,11 @@ void RippleButton::paintRipple(QPainter &p, int x, int y, TimeMs ms, const QColo } } -void RippleButton::onStateChanged(int oldState, StateChangeSource source) { +void RippleButton::onStateChanged(State was, StateChangeSource source) { update(); - auto wasDown = (oldState & StateDown); - auto down = (_state & StateDown); + auto wasDown = static_cast(was & StateFlag::Down); + auto down = isDown(); if (!_st.showDuration || down == wasDown || _forceRippled) { return; } @@ -187,10 +187,10 @@ void FlatButton::step_appearance(float64 ms, bool timer) { if (timer) update(); } -void FlatButton::onStateChanged(int oldState, StateChangeSource source) { - RippleButton::onStateChanged(oldState, source); +void FlatButton::onStateChanged(State was, StateChangeSource source) { + RippleButton::onStateChanged(was, source); - a_over.start((_state & StateOver) ? 1. : 0.); + a_over.start(isOver() ? 1. : 0.); if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) { _a_appearance.stop(); a_over.finish(); @@ -211,7 +211,7 @@ void FlatButton::paintEvent(QPaintEvent *e) { auto ms = getms(); paintRipple(p, 0, 0, ms); - p.setFont((_state & StateOver) ? _st.overFont : _st.font); + p.setFont(isOver() ? _st.overFont : _st.font); p.setRenderHint(QPainter::TextAntialiasing); p.setPen(anim::pen(_st.color, _st.overColor, a_over.current())); @@ -294,8 +294,8 @@ void RoundButton::paintEvent(QPaintEvent *e) { } App::roundRect(p, myrtlrect(rounded), _st.textBg, ImageRoundRadius::Small); - auto over = (_state & StateOver); - auto down = (_state & StateDown); + auto over = isOver(); + auto down = isDown(); if (over || down) { App::roundRect(p, myrtlrect(rounded), _st.textBgOver, ImageRoundRadius::Small); } @@ -340,21 +340,25 @@ IconButton::IconButton(QWidget *parent, const style::IconButton &st) : RippleBut setCursor(style::cur_pointer); } -void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) { - _iconOverride = icon; - _iconOverrideOver = iconOver; +void IconButton::setIconOverride(const style::icon *iconOverride, const style::icon *iconOverOverride) { + _iconOverride = iconOverride; + _iconOverrideOver = iconOverOverride; update(); } +void IconButton::setRippleColorOverride(const style::color *colorOverride) { + _rippleColorOverride = colorOverride; +} + void IconButton::paintEvent(QPaintEvent *e) { Painter p(this); auto ms = getms(); - paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms); + paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms, _rippleColorOverride ? &(*_rippleColorOverride)->c : nullptr); - auto down = (_state & StateDown); - auto overIconOpacity = (down || forceRippled()) ? 1. : _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); + auto down = isDown(); + auto overIconOpacity = (down || forceRippled()) ? 1. : _a_over.current(getms(), isOver() ? 1. : 0.); auto overIcon = [this] { if (_iconOverrideOver) { return _iconOverrideOver; @@ -389,11 +393,12 @@ void IconButton::paintEvent(QPaintEvent *e) { } } -void IconButton::onStateChanged(int oldState, StateChangeSource source) { - RippleButton::onStateChanged(oldState, source); +void IconButton::onStateChanged(State was, StateChangeSource source) { + RippleButton::onStateChanged(was, source); - auto over = (_state & StateOver); - if (over != (oldState & StateOver)) { + auto over = isOver(); + auto wasOver = static_cast(was & StateFlag::Over); + if (over != wasOver) { if (_st.duration) { auto from = over ? 0. : 1.; auto to = over ? 1. : 0.; @@ -443,7 +448,8 @@ int LeftOutlineButton::resizeGetHeight(int newWidth) { void LeftOutlineButton::paintEvent(QPaintEvent *e) { Painter p(this); - bool over = (_state & StateOver), down = (_state & StateDown); + auto over = isOver(); + auto down = isDown(); if (width() > _st.outlineWidth) { p.fillRect(rtlrect(_st.outlineWidth, 0, width() - _st.outlineWidth, height(), width()), (over || down) ? _st.textBgOver : _st.textBg); paintRipple(p, 0, 0, getms()); @@ -492,7 +498,7 @@ void CrossButton::paintEvent(QPaintEvent *e) { Painter p(this); auto ms = getms(); - auto over = (_state & StateOver); + auto over = isOver(); auto shown = _a_show.current(ms, _shown ? 1. : 0.); p.setOpacity(shown); @@ -501,11 +507,12 @@ void CrossButton::paintEvent(QPaintEvent *e) { CrossAnimation::paint(p, _st.cross, over ? _st.crossFgOver : _st.crossFg, _st.crossPosition.x(), _st.crossPosition.y(), width(), shown); } -void CrossButton::onStateChanged(int oldState, StateChangeSource source) { - RippleButton::onStateChanged(oldState, source); +void CrossButton::onStateChanged(State was, StateChangeSource source) { + RippleButton::onStateChanged(was, source); - auto over = (_state & StateOver); - if (over != (oldState & StateOver)) { + auto over = isOver(); + auto wasOver = static_cast(was & StateFlag::Over); + if (over != wasOver) { update(); } } diff --git a/Telegram/SourceFiles/ui/widgets/buttons.h b/Telegram/SourceFiles/ui/widgets/buttons.h index 7459739d6..43f6a5199 100644 --- a/Telegram/SourceFiles/ui/widgets/buttons.h +++ b/Telegram/SourceFiles/ui/widgets/buttons.h @@ -23,6 +23,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/abstract_button.h" #include "styles/style_widgets.h" +#include + namespace Ui { class RippleAnimation; @@ -38,7 +40,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, StateChangeSource source) override; + void onStateChanged(State was, StateChangeSource source) override; private: QString _text; @@ -66,7 +68,7 @@ public: protected: void paintRipple(QPainter &p, int x, int y, TimeMs ms, const QColor *colorOverride = nullptr); - void onStateChanged(int oldState, StateChangeSource source) override; + void onStateChanged(State was, StateChangeSource source) override; virtual QImage prepareRippleMask() const; virtual QPoint prepareRippleStartPosition() const; @@ -98,7 +100,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, StateChangeSource source) override; + void onStateChanged(State was, StateChangeSource source) override; private: void setOpacity(float64 o); @@ -109,7 +111,7 @@ private: const style::FlatButton &_st; - anim::fvalue a_over; + anim::value a_over; Animation _a_appearance; float64 _opacity = 1.; @@ -162,12 +164,13 @@ public: IconButton(QWidget *parent, const style::IconButton &st); // Pass nullptr to restore the default icon. - void setIcon(const style::icon *icon, const style::icon *iconOver = nullptr); + void setIconOverride(const style::icon *iconOverride, const style::icon *iconOverOverride = nullptr); + void setRippleColorOverride(const style::color *colorOverride); protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, StateChangeSource source) override; + void onStateChanged(State was, StateChangeSource source) override; QImage prepareRippleMask() const override; QPoint prepareRippleStartPosition() const override; @@ -176,6 +179,7 @@ private: const style::IconButton &_st; const style::icon *_iconOverride = nullptr; const style::icon *_iconOverrideOver = nullptr; + const style::color *_rippleColorOverride = nullptr; FloatAnimation _a_over; @@ -215,7 +219,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, StateChangeSource source) override; + void onStateChanged(State was, StateChangeSource source) override; QImage prepareRippleMask() const override; QPoint prepareRippleStartPosition() const override; diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.cpp b/Telegram/SourceFiles/ui/widgets/checkbox.cpp index eaf17f1ae..12c8a5470 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.cpp +++ b/Telegram/SourceFiles/ui/widgets/checkbox.cpp @@ -163,16 +163,16 @@ void Checkbox::paintEvent(QPaintEvent *e) { } void Checkbox::onClicked() { - if (_state & StateDisabled) return; + if (isDisabled()) return; setChecked(!checked()); } -void Checkbox::onStateChanged(int oldState, StateChangeSource source) { - RippleButton::onStateChanged(oldState, source); +void Checkbox::onStateChanged(State was, StateChangeSource source) { + RippleButton::onStateChanged(was, source); - if ((_state & StateDisabled) && !(oldState & StateDisabled)) { + if (isDisabled() && !(was & StateFlag::Disabled)) { setCursor(style::cur_default); - } else if (!(_state & StateDisabled) && (oldState & StateDisabled)) { + } else if (!isDisabled() && (was & StateFlag::Disabled)) { setCursor(style::cur_pointer); } } @@ -284,16 +284,16 @@ void Radiobutton::paintEvent(QPaintEvent *e) { } void Radiobutton::onClicked() { - if (_state & StateDisabled) return; + if (isDisabled()) return; setChecked(!checked()); } -void Radiobutton::onStateChanged(int oldState, StateChangeSource source) { - RippleButton::onStateChanged(oldState, source); +void Radiobutton::onStateChanged(State was, StateChangeSource source) { + RippleButton::onStateChanged(was, source); - if ((_state & StateDisabled) && !(oldState & StateDisabled)) { + if (isDisabled() && !(was & StateFlag::Disabled)) { setCursor(style::cur_default); - } else if (!(_state & StateDisabled) && (oldState & StateDisabled)) { + } else if (!isDisabled() && (was & StateFlag::Disabled)) { setCursor(style::cur_pointer); } } diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.h b/Telegram/SourceFiles/ui/widgets/checkbox.h index 221ed21dd..ccc4c41a1 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.h +++ b/Telegram/SourceFiles/ui/widgets/checkbox.h @@ -48,7 +48,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, StateChangeSource source) override; + void onStateChanged(State was, StateChangeSource source) override; int resizeGetHeight(int newWidth) override; QImage prepareRippleMask() const override; @@ -95,7 +95,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, StateChangeSource source) override; + void onStateChanged(State was, StateChangeSource source) override; int resizeGetHeight(int newWidth) override; QImage prepareRippleMask() const override; diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp index 8ceb21c4b..727ee7326 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp @@ -65,7 +65,7 @@ void ContinuousSlider::setValue(float64 value, bool animated) { a_value.start(value); _a_value.start(); } else { - a_value = anim::fvalue(value, value); + a_value = anim::value(value, value); _a_value.stop(); } update(); @@ -116,7 +116,7 @@ void ContinuousSlider::mouseReleaseEvent(QMouseEvent *e) { if (_changeFinishedCallback) { _changeFinishedCallback(_downValue); } - a_value = anim::fvalue(_downValue, _downValue); + a_value = anim::value(_downValue, _downValue); _a_value.stop(); update(); } diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.h b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h index 539a251a6..0bb4af4bb 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.h +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h @@ -107,7 +107,7 @@ private: bool _over = false; FloatAnimation _a_over; - anim::fvalue a_value = { 0., 0. }; + anim::value a_value; Animation _a_value; bool _mouseDown = false; diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.cpp b/Telegram/SourceFiles/ui/widgets/input_fields.cpp index 717406f45..e8c750881 100644 --- a/Telegram/SourceFiles/ui/widgets/input_fields.cpp +++ b/Telegram/SourceFiles/ui/widgets/input_fields.cpp @@ -1716,11 +1716,6 @@ InputArea::InputArea(QWidget *parent, const style::InputArea &st, const QString , _placeholderFull(ph) , _placeholderVisible(val.isEmpty()) -, a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift) -, a_placeholderOpacity(_placeholderVisible ? 1 : 0) -, a_placeholderFgActive(0) -, _a_placeholderFg(animation(this, &InputArea::step_placeholderFg)) -, _a_placeholderShift(animation(this, &InputArea::step_placeholderShift)) , a_borderOpacityActive(0) , a_borderFgActive(0) @@ -1878,21 +1873,21 @@ void InputArea::paintEvent(QPaintEvent *e) { p.setOpacity(1); } - bool drawPlaceholder = _placeholderVisible; - if (_a_placeholderShift.animating()) { - p.setOpacity(a_placeholderOpacity.current()); - drawPlaceholder = true; - } - if (drawPlaceholder) { + auto ms = getms(); + auto placeholderOpacity = _a_placeholderVisible.current(ms, _placeholderVisible ? 1. : 0.); + if (placeholderOpacity > 0.) { + p.setOpacity(placeholderOpacity); p.save(); p.setClipRect(r); + auto placeholderLeft = anim::interpolate(_st.placeholderShift, 0, placeholderOpacity); + QRect r(rect().marginsRemoved(_st.textMargins + _st.placeholderMargins)); - r.moveLeft(r.left() + a_placeholderLeft.current()); + r.moveLeft(r.left() + placeholderLeft); if (rtl()) r.moveLeft(width() - r.left() - r.width()); p.setFont(_st.font); - p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, a_placeholderFgActive.current())); + p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, _a_placeholderFocused.current(ms, hasFocus() ? 1. : 0.))); p.drawText(r, _placeholder, _st.placeholderAlign); p.restore(); @@ -1929,8 +1924,7 @@ void InputArea::focusInInner() { if (!_focused) { _focused = true; - a_placeholderFgActive.start(1.); - _a_placeholderFg.start(); + _a_placeholderFocused.start([this] { update(); }, 0., 1., _st.duration); startBorderAnimation(); } @@ -1946,8 +1940,7 @@ void InputArea::focusOutInner() { if (_focused) { _focused = false; - a_placeholderFgActive.start(0.); - _a_placeholderFg.start(); + _a_placeholderFocused.start([this] { update(); }, 1., 0., _st.duration); startBorderAnimation(); } @@ -2273,30 +2266,6 @@ void InputArea::onRedoAvailable(bool avail) { if (App::wnd()) App::wnd()->updateGlobalMenu(); } -void InputArea::step_placeholderFg(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_placeholderFg.stop(); - a_placeholderFgActive.finish(); - } else { - a_placeholderFgActive.update(dt, anim::linear); - } - if (timer) update(); -} - -void InputArea::step_placeholderShift(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_placeholderShift.stop(); - a_placeholderLeft.finish(); - a_placeholderOpacity.finish(); - } else { - a_placeholderLeft.update(dt, anim::linear); - a_placeholderOpacity.update(dt, anim::linear); - } - if (timer) update(); -} - void InputArea::step_border(float64 ms, bool timer) { float64 dt = ms / _st.duration; bool res = true; @@ -2314,13 +2283,10 @@ void InputArea::step_border(float64 ms, bool timer) { } void InputArea::updatePlaceholder() { - bool placeholderVisible = _oldtext.isEmpty(); - if (placeholderVisible != _placeholderVisible) { + auto placeholderVisible = _oldtext.isEmpty(); + if (_placeholderVisible != placeholderVisible) { _placeholderVisible = placeholderVisible; - - a_placeholderLeft.start(_placeholderVisible ? 0 : _st.placeholderShift); - a_placeholderOpacity.start(_placeholderVisible ? 1 : 0); - _a_placeholderShift.start(); + _a_placeholderVisible.start([this] { update(); }, _placeholderVisible ? 0. : 1., _placeholderVisible ? 1. : 0., _st.duration); } } @@ -2445,11 +2411,6 @@ InputField::InputField(QWidget *parent, const style::InputField &st, const QStri , _placeholderFull(ph) , _placeholderVisible(val.isEmpty()) -, a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift) -, a_placeholderOpacity(_placeholderVisible ? 1 : 0) -, a_placeholderFgActive(0) -, _a_placeholderFg(animation(this, &InputField::step_placeholderFg)) -, _a_placeholderShift(animation(this, &InputField::step_placeholderShift)) , a_borderOpacityActive(0) , a_borderFgActive(0) @@ -2571,13 +2532,6 @@ void InputField::paintEvent(QPaintEvent *e) { Painter p(this); auto ms = getms(); - if (_a_placeholderShift.animating()) { - _a_placeholderShift.step(ms); - } - if (_a_placeholderFg.animating()) { - _a_placeholderFg.step(ms); - } - QRect r(rect().intersected(e->rect())); if (_st.textBg->c.alphaF() > 0.) { p.fillRect(r, _st.textBg); @@ -2593,21 +2547,21 @@ void InputField::paintEvent(QPaintEvent *e) { p.setOpacity(1); } - bool drawPlaceholder = _placeholderVisible; - if (_a_placeholderShift.animating()) { - p.setOpacity(a_placeholderOpacity.current()); - drawPlaceholder = true; - } - if (drawPlaceholder) { + auto placeholderOpacity = _a_placeholderVisible.current(ms, _placeholderVisible ? 1. : 0.); + if (placeholderOpacity > 0.) { + p.setOpacity(placeholderOpacity); p.save(); p.setClipRect(r); + auto placeholderLeft = anim::interpolate(_st.placeholderShift, 0, placeholderOpacity); + + QRect r(rect().marginsRemoved(_st.textMargins + _st.placeholderMargins)); - r.moveLeft(r.left() + a_placeholderLeft.current()); + r.moveLeft(r.left() + placeholderLeft); if (rtl()) r.moveLeft(width() - r.left() - r.width()); p.setFont(_st.font); - p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, a_placeholderFgActive.current())); + p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, _a_placeholderFocused.current(ms, hasFocus() ? 1. : 0.))); p.drawText(r, _placeholder, _st.placeholderAlign); p.restore(); @@ -2644,8 +2598,7 @@ void InputField::focusInInner() { if (!_focused) { _focused = true; - a_placeholderFgActive.start(1.); - _a_placeholderFg.start(); + _a_placeholderFocused.start([this] { update(); }, 0., 1., _st.duration); startBorderAnimation(); } @@ -2661,8 +2614,7 @@ void InputField::focusOutInner() { if (_focused) { _focused = false; - a_placeholderFgActive.start(0.); - _a_placeholderFg.start(); + _a_placeholderFocused.start([this] { update(); }, 1., 0., _st.duration); startBorderAnimation(); } @@ -3019,32 +2971,9 @@ void InputField::selectAll() { _inner.setTextCursor(c); } -void InputField::step_placeholderFg(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_placeholderFg.stop(); - a_placeholderFgActive.finish(); - } else { - a_placeholderFgActive.update(dt, anim::linear); - } - if (timer) update(); -} - -void InputField::step_placeholderShift(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - finishPlaceholderAnimation(); - } else { - a_placeholderLeft.update(dt, anim::linear); - a_placeholderOpacity.update(dt, anim::linear); - } - if (timer) update(); -} - void InputField::finishPlaceholderAnimation() { - _a_placeholderShift.stop(); - a_placeholderLeft.finish(); - a_placeholderOpacity.finish(); + _a_placeholderVisible.finish(); + _a_placeholderFocused.finish(); update(); } @@ -3064,13 +2993,10 @@ void InputField::step_border(float64 ms, bool timer) { } void InputField::updatePlaceholder() { - bool placeholderVisible = _oldtext.isEmpty() && !_forcePlaceholderHidden; - if (placeholderVisible != _placeholderVisible) { + auto placeholderVisible = _oldtext.isEmpty(); + if (_placeholderVisible != placeholderVisible) { _placeholderVisible = placeholderVisible; - - a_placeholderLeft.start(_placeholderVisible ? 0 : _st.placeholderShift); - a_placeholderOpacity.start(_placeholderVisible ? 1 : 0); - _a_placeholderShift.start(); + _a_placeholderVisible.start([this] { update(); }, _placeholderVisible ? 0. : 1., _placeholderVisible ? 1. : 0., _st.duration); } } @@ -3198,11 +3124,6 @@ MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st, , _placeholderFull(placeholder) , _placeholderVisible(val.isEmpty()) , _placeholderFast(false) -, a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift) -, a_placeholderOpacity(_placeholderVisible ? 1 : 0) -, a_placeholderFgActive(0) -, _a_placeholderFg(animation(this, &MaskedInputField::step_placeholderFg)) -, _a_placeholderShift(animation(this, &MaskedInputField::step_placeholderShift)) , a_borderOpacityActive(0) , a_borderFgActive(0) @@ -3345,7 +3266,7 @@ void MaskedInputField::paintEvent(QPaintEvent *e) { } p.setClipRect(r); - paintPlaceholder(p); + paintPlaceholder(p, getms()); QLineEdit::paintEvent(e); } @@ -3361,8 +3282,7 @@ void MaskedInputField::focusInEvent(QFocusEvent *e) { if (!_focused) { _focused = true; - a_placeholderFgActive.start(1.); - _a_placeholderFg.start(); + _a_placeholderFocused.start([this] { update(); }, 0., 1., _st.duration); startBorderAnimation(); } @@ -3374,8 +3294,7 @@ void MaskedInputField::focusOutEvent(QFocusEvent *e) { if (_focused) { _focused = false; - a_placeholderFgActive.start(0.); - _a_placeholderFg.start(); + _a_placeholderFocused.start([this] { update(); }, 1., 0., _st.duration); startBorderAnimation(); } @@ -3416,30 +3335,6 @@ QSize MaskedInputField::minimumSizeHint() const { return geometry().size(); } -void MaskedInputField::step_placeholderFg(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_placeholderFg.stop(); - a_placeholderFgActive.finish(); - } else { - a_placeholderFgActive.update(dt, anim::linear); - } - if (timer) update(); -} - -void MaskedInputField::step_placeholderShift(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_placeholderShift.stop(); - a_placeholderLeft.finish(); - a_placeholderOpacity.finish(); - } else { - a_placeholderLeft.update(dt, anim::linear); - a_placeholderOpacity.update(dt, anim::linear); - } - if (timer) update(); -} - void MaskedInputField::step_border(float64 ms, bool timer) { float64 dt = ms / _st.duration; if (dt >= 1) { @@ -3467,25 +3362,18 @@ bool MaskedInputField::setPlaceholder(const QString &placeholder) { void MaskedInputField::setPlaceholderFast(bool fast) { _placeholderFast = fast; if (_placeholderFast) { - a_placeholderLeft = anim::ivalue(_placeholderVisible ? 0 : _st.placeholderShift, _placeholderVisible ? 0 : _st.placeholderShift); - a_placeholderOpacity = anim::fvalue(_placeholderVisible ? 1 : 0, _placeholderVisible ? 1 : 0); + _a_placeholderVisible.finish(); update(); } } void MaskedInputField::updatePlaceholder() { - bool placeholderVisible = _oldtext.isEmpty(); - if (placeholderVisible != _placeholderVisible) { + auto placeholderVisible = _oldtext.isEmpty(); + if (_placeholderVisible != placeholderVisible) { _placeholderVisible = placeholderVisible; - + _a_placeholderVisible.start([this] { update(); }, _placeholderVisible ? 0. : 1., _placeholderVisible ? 1. : 0., _st.duration); if (_placeholderFast) { - a_placeholderLeft = anim::ivalue(_placeholderVisible ? 0 : _st.placeholderShift, _placeholderVisible ? 0 : _st.placeholderShift); - a_placeholderOpacity = anim::fvalue(_placeholderVisible ? 1 : 0, _placeholderVisible ? 1 : 0); - update(); - } else { - a_placeholderLeft.start(_placeholderVisible ? 0 : _st.placeholderShift); - a_placeholderOpacity.start(_placeholderVisible ? 1 : 0); - _a_placeholderShift.start(); + _a_placeholderVisible.finish(); } } } @@ -3498,29 +3386,28 @@ QRect MaskedInputField::placeholderRect() const { return rect().marginsRemoved(_st.textMargins + _st.placeholderMargins); } -void MaskedInputField::paintPlaceholder(Painter &p) { - bool drawPlaceholder = _placeholderVisible; - if (_a_placeholderShift.animating()) { - p.setOpacity(a_placeholderOpacity.current()); - drawPlaceholder = true; - } - if (drawPlaceholder) { +void MaskedInputField::paintPlaceholder(Painter &p, TimeMs ms) { + auto placeholderOpacity = _a_placeholderVisible.current(ms, _placeholderVisible ? 1. : 0.); + if (placeholderOpacity > 0.) { + p.setOpacity(placeholderOpacity); p.save(); + auto placeholderLeft = anim::interpolate(_st.placeholderShift, 0, placeholderOpacity); + QRect phRect(placeholderRect()); - phRect.moveLeft(phRect.left() + a_placeholderLeft.current()); + phRect.moveLeft(phRect.left() + placeholderLeft); if (rtl()) phRect.moveLeft(width() - phRect.left() - phRect.width()); - placeholderPreparePaint(p); + placeholderPreparePaint(p, ms); p.drawText(phRect, _placeholder, _st.placeholderAlign); p.restore(); } } -void MaskedInputField::placeholderPreparePaint(Painter &p) { +void MaskedInputField::placeholderPreparePaint(Painter &p, TimeMs ms) { p.setFont(_st.font); - p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, a_placeholderFgActive.current())); + p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, _a_placeholderFocused.current(ms, hasFocus() ? 1. : 0.))); } void MaskedInputField::keyPressEvent(QKeyEvent *e) { @@ -3648,7 +3535,7 @@ void CountryCodeInput::correctValue(const QString &was, int32 wasCursor, QString PhonePartInput::PhonePartInput(QWidget *parent, const style::InputField &st) : MaskedInputField(parent, st, lang(lng_phone_ph)) { } -void PhonePartInput::paintPlaceholder(Painter &p) { +void PhonePartInput::paintPlaceholder(Painter &p, TimeMs ms) { auto t = getLastText(); if (!_pattern.isEmpty() && !t.isEmpty()) { auto ph = placeholder().mid(t.size()); @@ -3658,12 +3545,12 @@ void PhonePartInput::paintPlaceholder(Painter &p) { int tw = phFont()->width(t); if (tw < phRect.width()) { phRect.setLeft(phRect.left() + tw); - placeholderPreparePaint(p); + placeholderPreparePaint(p, ms); p.drawText(phRect, ph, style::al_topleft); } } } else { - MaskedInputField::paintPlaceholder(p); + MaskedInputField::paintPlaceholder(p, ms); } } @@ -3812,9 +3699,9 @@ _linkPlaceholder(isLink ? qsl("telegram.me/") : QString()) { } } -void UsernameInput::paintPlaceholder(Painter &p) { +void UsernameInput::paintPlaceholder(Painter &p, TimeMs ms) { if (_linkPlaceholder.isEmpty()) { - MaskedInputField::paintPlaceholder(p); + MaskedInputField::paintPlaceholder(p, ms); } else { p.setFont(_st.font); p.setPen(_st.placeholderFg); @@ -3872,7 +3759,7 @@ void PhoneInput::clearText() { correctValue(QString(), 0, phone, pos); } -void PhoneInput::paintPlaceholder(Painter &p) { +void PhoneInput::paintPlaceholder(Painter &p, TimeMs ms) { auto t = getLastText(); if (!_pattern.isEmpty() && !t.isEmpty()) { auto ph = placeholder().mid(t.size()); @@ -3882,12 +3769,12 @@ void PhoneInput::paintPlaceholder(Painter &p) { int tw = phFont()->width(t); if (tw < phRect.width()) { phRect.setLeft(phRect.left() + tw); - placeholderPreparePaint(p); + placeholderPreparePaint(p, ms); p.drawText(phRect, ph, style::al_topleft); } } } else { - MaskedInputField::paintPlaceholder(p); + MaskedInputField::paintPlaceholder(p, ms); } } diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.h b/Telegram/SourceFiles/ui/widgets/input_fields.h index a823c7c39..bbede945c 100644 --- a/Telegram/SourceFiles/ui/widgets/input_fields.h +++ b/Telegram/SourceFiles/ui/widgets/input_fields.h @@ -347,8 +347,6 @@ public: } void updatePlaceholder(); - void step_placeholderFg(float64 ms, bool timer); - void step_placeholderShift(float64 ms, bool timer); void step_border(float64 ms, bool timer); QSize sizeHint() const override; @@ -470,14 +468,12 @@ private: QString _placeholder, _placeholderFull; bool _placeholderVisible; - anim::ivalue a_placeholderLeft; - anim::fvalue a_placeholderOpacity; - anim::fvalue a_placeholderFgActive; - Animation _a_placeholderFg, _a_placeholderShift; + FloatAnimation _a_placeholderFocused; + FloatAnimation _a_placeholderVisible; - anim::fvalue a_borderOpacityActive; - anim::fvalue a_borderFgActive; - anim::fvalue a_borderFgError; + anim::value a_borderOpacityActive; + anim::value a_borderFgActive; + anim::value a_borderFgError; Animation _a_border; bool _focused, _error; @@ -511,8 +507,6 @@ public: void setPlaceholderHidden(bool forcePlaceholderHidden); void finishPlaceholderAnimation(); - void step_placeholderFg(float64 ms, bool timer); - void step_placeholderShift(float64 ms, bool timer); void step_border(float64 ms, bool timer); QSize sizeHint() const override; @@ -640,14 +634,12 @@ private: QString _placeholder, _placeholderFull; bool _placeholderVisible; - anim::ivalue a_placeholderLeft; - anim::fvalue a_placeholderOpacity; - anim::fvalue a_placeholderFgActive; - Animation _a_placeholderFg, _a_placeholderShift; + FloatAnimation _a_placeholderFocused; + FloatAnimation _a_placeholderVisible; - anim::fvalue a_borderOpacityActive; - anim::fvalue a_borderFgActive; - anim::fvalue a_borderFgError; + anim::value a_borderOpacityActive; + anim::value a_borderFgActive; + anim::value a_borderFgError; Animation _a_border; bool _focused, _error; @@ -676,8 +668,6 @@ public: QRect getTextRect() const; - void step_placeholderFg(float64 ms, bool timer); - void step_placeholderShift(float64 ms, bool timer); void step_border(float64 ms, bool timer); QSize sizeHint() const override; @@ -736,13 +726,13 @@ protected: } void setCorrectedText(QString &now, int &nowCursor, const QString &newText, int newPos); - virtual void paintPlaceholder(Painter &p); + virtual void paintPlaceholder(Painter &p, TimeMs ms); style::font phFont() { return _st.font; } - void placeholderPreparePaint(Painter &p); + void placeholderPreparePaint(Painter &p, TimeMs ms); const QString &placeholder() const; QRect placeholderRect() const; @@ -764,14 +754,12 @@ private: QString _placeholder, _placeholderFull; bool _placeholderVisible, _placeholderFast; - anim::ivalue a_placeholderLeft; - anim::fvalue a_placeholderOpacity; - anim::fvalue a_placeholderFgActive; - Animation _a_placeholderFg, _a_placeholderShift; + FloatAnimation _a_placeholderFocused; + FloatAnimation _a_placeholderVisible; - anim::fvalue a_borderOpacityActive; - anim::fvalue a_borderFgActive; - anim::fvalue a_borderFgError; + anim::value a_borderOpacityActive; + anim::value a_borderFgActive; + anim::value a_borderFgError; Animation _a_border; bool _focused, _error; @@ -822,7 +810,7 @@ protected: void keyPressEvent(QKeyEvent *e) override; void correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) override; - void paintPlaceholder(Painter &p) override; + void paintPlaceholder(Painter &p, TimeMs ms) override; private: QVector _pattern; @@ -850,7 +838,7 @@ public: protected: void correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) override; - void paintPlaceholder(Painter &p) override; + void paintPlaceholder(Painter &p, TimeMs ms) override; private: QString _linkPlaceholder; @@ -867,7 +855,7 @@ protected: void focusInEvent(QFocusEvent *e) override; void correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) override; - void paintPlaceholder(Painter &p) override; + void paintPlaceholder(Painter &p, TimeMs ms) override; private: QString _defaultPlaceholder; diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index ce8a7bd47..c5a49273a 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -95,7 +95,7 @@ private: , y(y) { x.start(updateCallback, fromX, toX, duration); } - IntAnimation x; + FloatAnimation x; int fromX, toX; int y; }; @@ -143,7 +143,7 @@ void MultiSelect::Inner::Item::paint(Painter &p, int outerWidth, TimeMs ms) { paintOnce(p, _x, _y, outerWidth, ms); } else { for (auto i = _copies.begin(), e = _copies.end(); i != e;) { - auto x = i->x.current(getms(), _x); + auto x = qRound(i->x.current(getms(), _x)); auto y = i->y; auto animating = i->x.animating(); if (animating || (y == _y)) { @@ -748,7 +748,7 @@ void MultiSelect::Inner::updateItemsGeometry() { } void MultiSelect::Inner::updateHeightStep() { - auto newHeight = _height.current(_newHeight); + auto newHeight = qRound(_height.current(_newHeight)); if (auto heightDelta = newHeight - height()) { resize(width(), newHeight); if (_resizedCallback) { diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.h b/Telegram/SourceFiles/ui/widgets/multi_select.h index 3f448cd4c..b535c141e 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.h +++ b/Telegram/SourceFiles/ui/widgets/multi_select.h @@ -157,7 +157,7 @@ private: ChildWidget _cancel; int _newHeight = 0; - IntAnimation _height; + FloatAnimation _height; base::lambda _queryChangedCallback; base::lambda _submittedCallback; diff --git a/Telegram/SourceFiles/ui/widgets/scroll_area.h b/Telegram/SourceFiles/ui/widgets/scroll_area.h index b6bb137c1..16e25ef60 100644 --- a/Telegram/SourceFiles/ui/widgets/scroll_area.h +++ b/Telegram/SourceFiles/ui/widgets/scroll_area.h @@ -100,7 +100,7 @@ private: TimeMs _hideIn; QTimer _hideTimer; - anim::fvalue a_bgOver, a_barOver, a_fullOpacity; + anim::value a_bgOver, a_barOver, a_fullOpacity; Animation _a_appearance; QRect _bar; @@ -131,7 +131,7 @@ public: public slots: void update() { - update(0, 0, fullWidth(), height()); + update(0, 0, getFullWidth(), height()); } signals: @@ -142,20 +142,20 @@ signals: protected: void paintEvent(QPaintEvent *e) override; // paintEvent done through paintRegion - int32 otherWidth() const { + int otherWidth() const { return _otherWidth; } - int32 fullWidth() const { + int getFullWidth() const { return width() + otherWidth(); } virtual void paintRegion(Painter &p, const QRegion ®ion, bool paintingOther) = 0; private: - int32 _otherWidth = 0; - void setOtherWidth(int32 otherWidth) { + int _otherWidth = 0; + void setOtherWidth(int otherWidth) { _otherWidth = otherWidth; } - void resize(int32 w, int32 h) { + void resize(int w, int h) { TWidget::resize(w, h); } friend class ScrollArea; diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index bb04fb471..2b3b0c013 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -878,7 +878,7 @@ historySendActionTypingDuration: 800; historySendActionTypingHalfPeriod: 320; historySendActionTypingDeltaTime: 150; historySendActionTypingPosition: point(4px, -4px); -historySendActionTypingDelta: 7px; +historySendActionTypingDelta: 6px; historySendActionTypingLargeNumerator: 28px; historySendActionTypingSmallNumerator: 16px; historySendActionTypingDenominator: 12.; @@ -895,3 +895,18 @@ historySendActionUploadDelta: 5px; historySendActionUploadStrokeNumerator: 12px; historySendActionUploadSizeNumerator: 28px; historySendActionUploadDenominator: 8.; + +MediaPlayerButton { + playPosition: point; + playOuter: size; + pausePosition: point; + pauseOuter: size; + pauseStroke: pixels; + cancelPosition: point; + cancelOuter: size; + cancelStroke: pixels; + + rippleAreaPosition: point; + rippleAreaSize: pixels; + ripple: RippleAnimation; +} diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 93648bb6f..2ce657b27 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -466,7 +466,7 @@ void Widget::moveByShift() { } QPoint Widget::computePosition(int height) const { - auto realShift = a_shift.current(); + auto realShift = qRound(a_shift.current()); if (_direction == Direction::Up) { realShift = -realShift - height; } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index 8002e9eda..170dc4b52 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -159,13 +159,13 @@ private: bool _hiding = false; bool _deleted = false; float64 _opacityDuration; - anim::fvalue a_opacity; + anim::value a_opacity; anim::transition a_func; Animation _a_opacity; QPoint _startPosition; Direction _direction; - anim::ivalue a_shift; + anim::value a_shift; Animation _a_shift; }; diff --git a/Telegram/SourceFiles/window/player_wrap_widget.h b/Telegram/SourceFiles/window/player_wrap_widget.h index 44719f54c..00a6d2ebf 100644 --- a/Telegram/SourceFiles/window/player_wrap_widget.h +++ b/Telegram/SourceFiles/window/player_wrap_widget.h @@ -25,7 +25,7 @@ public: entity()->hideShadow(); } int contentHeight() const { - return height() - st::lineWidth; + return qMax(height() - st::lineWidth, 0); } protected: diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index a1f13eed2..c5b41b79b 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -72,8 +72,6 @@ void SectionWidget::showFinished() { _showAnimation.reset(); if (isHidden()) return; - App::app()->mtpUnpause(); - showChildren(); showFinishedHook(); diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index d5b78511e..d08c7e3a7 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -47,6 +47,12 @@ notifyClose: IconButton { iconOver: simpleCloseIconOver; iconPosition: point(10px, 10px); + + rippleAreaPosition: point(5px, 5px); + rippleAreaSize: 20px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } } notifyItemTop: 12px; notifyTextLeft: 12px; diff --git a/Telegram/build/version b/Telegram/build/version index 1daa28362..da285d56d 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019011 +BetaVersion 10019012