From 6e0394dd427772952298b4ecf2774863c3f2b310 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 22 Nov 2016 12:48:13 +0300 Subject: [PATCH] Stickers box redesigned. --- Telegram/Resources/icons/stickers_remove.png | Bin 0 -> 168 bytes .../Resources/icons/stickers_remove@2x.png | Bin 0 -> 260 bytes Telegram/Resources/icons/stickers_reorder.png | Bin 0 -> 134 bytes .../Resources/icons/stickers_reorder@2x.png | Bin 0 -> 183 bytes Telegram/Resources/langs/lang.strings | 10 +- Telegram/SourceFiles/apiwrap.cpp | 157 +++ Telegram/SourceFiles/apiwrap.h | 17 +- Telegram/SourceFiles/app.cpp | 5 +- Telegram/SourceFiles/boxes/aboutbox.cpp | 2 +- Telegram/SourceFiles/boxes/abstractbox.cpp | 25 +- Telegram/SourceFiles/boxes/abstractbox.h | 6 +- Telegram/SourceFiles/boxes/boxes.style | 2 +- .../SourceFiles/boxes/notifications_box.cpp | 2 +- .../SourceFiles/boxes/notifications_box.h | 4 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 1127 +++++++++-------- Telegram/SourceFiles/boxes/stickers_box.h | 145 ++- Telegram/SourceFiles/dialogs/dialogs.style | 1 - .../SourceFiles/dialogs/dialogs_layout.cpp | 8 +- Telegram/SourceFiles/dialogs/dialogs_layout.h | 2 + Telegram/SourceFiles/facades.cpp | 2 + Telegram/SourceFiles/facades.h | 1 + Telegram/SourceFiles/history/history_item.cpp | 8 +- Telegram/SourceFiles/historywidget.cpp | 5 +- Telegram/SourceFiles/intro/intro.style | 2 +- Telegram/SourceFiles/localstorage.cpp | 9 +- .../media/player/media_player_cover.cpp | 2 +- .../player/media_player_volume_controller.cpp | 2 +- .../media/player/media_player_widget.cpp | 2 +- .../media/view/media_clip_controller.cpp | 2 +- .../media/view/media_clip_playback.h | 2 +- Telegram/SourceFiles/overview/overview.style | 3 +- .../settings/settings_scale_widget.cpp | 14 +- .../settings/settings_scale_widget.h | 7 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 1 - Telegram/SourceFiles/stickers/stickers.cpp | 25 +- Telegram/SourceFiles/stickers/stickers.h | 1 + Telegram/SourceFiles/stickers/stickers.style | 39 +- Telegram/SourceFiles/structs.h | 2 +- .../ui/widgets/continuous_slider.cpp | 175 --- .../ui/widgets/continuous_sliders.cpp | 293 +++++ ...ntinuous_slider.h => continuous_sliders.h} | 38 + ...screte_slider.cpp => discrete_sliders.cpp} | 188 +-- .../{discrete_slider.h => discrete_sliders.h} | 60 +- .../SourceFiles/ui/widgets/filled_slider.cpp | 73 -- .../SourceFiles/ui/widgets/filled_slider.h | 43 - .../SourceFiles/ui/widgets/media_slider.cpp | 101 -- .../SourceFiles/ui/widgets/media_slider.h | 49 - Telegram/SourceFiles/ui/widgets/widgets.style | 55 +- Telegram/gyp/Telegram.gyp | 12 +- 49 files changed, 1525 insertions(+), 1204 deletions(-) create mode 100644 Telegram/Resources/icons/stickers_remove.png create mode 100644 Telegram/Resources/icons/stickers_remove@2x.png create mode 100644 Telegram/Resources/icons/stickers_reorder.png create mode 100644 Telegram/Resources/icons/stickers_reorder@2x.png delete mode 100644 Telegram/SourceFiles/ui/widgets/continuous_slider.cpp create mode 100644 Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp rename Telegram/SourceFiles/ui/widgets/{continuous_slider.h => continuous_sliders.h} (79%) rename Telegram/SourceFiles/ui/widgets/{discrete_slider.cpp => discrete_sliders.cpp} (52%) rename Telegram/SourceFiles/ui/widgets/{discrete_slider.h => discrete_sliders.h} (67%) delete mode 100644 Telegram/SourceFiles/ui/widgets/filled_slider.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/filled_slider.h delete mode 100644 Telegram/SourceFiles/ui/widgets/media_slider.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/media_slider.h diff --git a/Telegram/Resources/icons/stickers_remove.png b/Telegram/Resources/icons/stickers_remove.png new file mode 100644 index 0000000000000000000000000000000000000000..8a98e3293853aaa55050b802390f2858d98e26ab GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;AzNd?0h(+()DTaIp6nL2DuHrx0 zJmJUxDW)CnJG2!9bQeia+oN*oWSkO1NAE5-?$FkmjVY6AL=?`^KjLw z7VDDj&FiXzBl_v z2gbFN0ylY?ow``q7NBqX<-(sC)3@;L;jb~=^i@$sUWUO!>e+}%Y40n{u;6_)!vTxF(4VWcBDB9t2A`W^-6yo+QuDk0xn~6qUH|*6I-p5swe1bA zv*88bW%pFgy1s1tg&J1X6H1O^oSQr*xHjnsrA$=lW{FgR2;GSd&>#KHUiG%EE4a}KZ3k;tXC%GSByzn+tH?uJ`m67qt+r>N$XSO5=Zq%}N|9O)4 hzI@)(zYkj^*)fx%UZ3il9n`htv}$62znA<8nG(SlN%AF2`BQ*|eG*gbo94|DSK<@- ffF=pPuwppTCi{GTQu-vIOBg&|{an^LB{Ts5Zg)uT literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 37c46db99f..9afd94ed42 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -708,7 +708,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_box_remove" = "Remove"; -"lng_custom_stickers" = "Custom stickers"; +"lng_stickers_installed_tab" = "Stickers"; +"lng_stickers_featured_tab" = "Trending"; +"lng_stickers_archived_tab" = "Archived"; "lng_stickers_remove_pack" = "Remove «{sticker_pack}»?"; "lng_stickers_add_pack" = "Add stickers"; "lng_stickers_share_pack" = "Share Stickers"; @@ -718,14 +720,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_stickers_copied" = "Sticker pack link copied to clipboard."; "lng_stickers_default_set" = "Great Minds"; "lng_stickers_you_have" = "Manage and reorder sticker packs"; -"lng_stickers_packs" = "Sticker Packs"; -"lng_stickers_reorder" = "Click and drag to reorder sticker packs"; "lng_stickers_featured" = "Trending Stickers"; -"lng_stickers_clear_recent" = "Clear"; -"lng_stickers_clear_recent_sure" = "Are you sure you want to clear your frequently used stickers list?"; -"lng_stickers_remove" = "Delete"; "lng_stickers_return" = "Undo"; -"lng_stickers_restore" = "Restore"; "lng_stickers_count" = "{count:Loading...|# sticker|# stickers}"; "lng_stickers_masks_pack" = "This is a pack of mask stickers. You can use them in the photo editor on our mobile apps."; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 119b4aa7f8..7184472f37 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -692,6 +692,109 @@ void ApiWrap::requestStickerSets() { } } +void ApiWrap::saveStickerSets(const Stickers::Order &localOrder, const Stickers::Order &localRemoved) { + for (auto requestId : base::take(_stickerSetDisenableRequests)) { + MTP::cancel(requestId); + } + MTP::cancel(base::take(_stickersReorderRequestId)); + MTP::cancel(base::take(_stickersClearRecentRequestId)); + + auto writeInstalled = true, writeRecent = false, writeCloudRecent = false, writeArchived = false; + auto &recent = cGetRecentStickers(); + auto &sets = Global::RefStickerSets(); + + _stickersOrder = localOrder; + for_const (auto removedSetId, localRemoved) { + if (removedSetId == Stickers::CloudRecentSetId) { + if (sets.remove(Stickers::CloudRecentSetId) != 0) { + writeCloudRecent = true; + } + if (sets.remove(Stickers::CustomSetId)) { + writeInstalled = true; + } + if (!recent.isEmpty()) { + recent.clear(); + writeRecent = true; + } + + MTPmessages_ClearRecentStickers::Flags flags = 0; + _stickersClearRecentRequestId = MTP::send(MTPmessages_ClearRecentStickers(MTP_flags(flags)), rpcDone(&ApiWrap::stickersClearRecentDone), rpcFail(&ApiWrap::stickersClearRecentFail)); + continue; + } + + auto it = sets.find(removedSetId); + if (it != sets.cend()) { + for (auto i = recent.begin(); i != recent.cend();) { + if (it->stickers.indexOf(i->first) >= 0) { + i = recent.erase(i); + writeRecent = true; + } else { + ++i; + } + } + if (!(it->flags & MTPDstickerSet::Flag::f_archived)) { + MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); + _stickerSetDisenableRequests.insert(MTP::send(MTPmessages_UninstallStickerSet(setId), rpcDone(&ApiWrap::stickerSetDisenableDone), rpcFail(&ApiWrap::stickerSetDisenableFail), 0, 5)); + int removeIndex = Global::StickerSetsOrder().indexOf(it->id); + if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); + if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) { + sets.erase(it); + } else { + if (it->flags & MTPDstickerSet::Flag::f_archived) { + writeArchived = true; + } + it->flags &= ~(MTPDstickerSet::Flag::f_installed | MTPDstickerSet::Flag::f_archived); + } + } + } + } + + // Clear all installed flags, set only for sets from order. + for (auto &set : sets) { + if (!(set.flags & MTPDstickerSet::Flag::f_archived)) { + set.flags &= ~MTPDstickerSet::Flag::f_installed; + } + } + + auto &order(Global::RefStickerSetsOrder()); + order.clear(); + for_const (auto setId, _stickersOrder) { + auto it = sets.find(setId); + if (it != sets.cend()) { + if ((it->flags & MTPDstickerSet::Flag::f_archived) && !localRemoved.contains(it->id)) { + MTPInputStickerSet mtpSetId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); + _stickerSetDisenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(mtpSetId, MTP_boolFalse()), rpcDone(&ApiWrap::stickerSetDisenableDone), rpcFail(&ApiWrap::stickerSetDisenableFail), 0, 5)); + it->flags &= ~MTPDstickerSet::Flag::f_archived; + writeArchived = true; + } + order.push_back(setId); + it->flags |= MTPDstickerSet::Flag::f_installed; + } + } + for (auto it = sets.begin(); it != sets.cend();) { + if ((it->flags & MTPDstickerSet_ClientFlag::f_featured) + || (it->flags & MTPDstickerSet::Flag::f_installed) + || (it->flags & MTPDstickerSet::Flag::f_archived) + || (it->flags & MTPDstickerSet_ClientFlag::f_special)) { + ++it; + } else { + it = sets.erase(it); + } + } + + if (writeInstalled) Local::writeInstalledStickers(); + if (writeRecent) Local::writeUserSettings(); + if (writeArchived) Local::writeArchivedStickers(); + if (writeCloudRecent) Local::writeRecentStickers(); + emit App::main()->stickersUpdated(); + + if (_stickerSetDisenableRequests.isEmpty()) { + stickersSaveOrder(); + } else { + MTP::sendAnything(); + } +} + void ApiWrap::joinChannel(ChannelData *channel) { if (channel->amIn()) { channelAmInUpdated(channel); @@ -1180,3 +1283,57 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs ApiWrap::~ApiWrap() { App::clearHistories(); } + + +void ApiWrap::stickerSetDisenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req) { + _stickerSetDisenableRequests.remove(req); + if (_stickerSetDisenableRequests.isEmpty()) { + stickersSaveOrder(); + } +} + +bool ApiWrap::stickerSetDisenableFail(const RPCError &error, mtpRequestId req) { + if (MTP::isDefaultHandledError(error)) return false; + _stickerSetDisenableRequests.remove(req); + if (_stickerSetDisenableRequests.isEmpty()) { + stickersSaveOrder(); + } + return true; +} + +void ApiWrap::stickersSaveOrder() { + if (_stickersOrder.size() > 1) { + QVector mtpOrder; + mtpOrder.reserve(_stickersOrder.size()); + for_const (auto setId, _stickersOrder) { + mtpOrder.push_back(MTP_long(setId)); + } + + MTPmessages_ReorderStickerSets::Flags flags = 0; + _stickersReorderRequestId = MTP::send(MTPmessages_ReorderStickerSets(MTP_flags(flags), MTP_vector(mtpOrder)), rpcDone(&ApiWrap::stickersReorderDone), rpcFail(&ApiWrap::stickersReorderFail)); + } else { + stickersReorderDone(MTP_boolTrue()); + } +} + +void ApiWrap::stickersReorderDone(const MTPBool &result) { + _stickersReorderRequestId = 0; +} + +bool ApiWrap::stickersReorderFail(const RPCError &result) { + if (MTP::isDefaultHandledError(result)) return false; + _stickersReorderRequestId = 0; + Global::SetLastStickersUpdate(0); + App::main()->updateStickers(); + return true; +} + +void ApiWrap::stickersClearRecentDone(const MTPBool &result) { + _stickersClearRecentRequestId = 0; +} + +bool ApiWrap::stickersClearRecentFail(const RPCError &result) { + if (MTP::isDefaultHandledError(result)) return false; + _stickersClearRecentRequestId = 0; + return true; +} diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 964404fe05..2d5c6529e8 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -26,7 +26,6 @@ class ApiWrap : public QObject, public RPCSender { Q_OBJECT public: - ApiWrap(QObject *parent); void init(); @@ -51,6 +50,7 @@ public: void scheduleStickerSetRequest(uint64 setId, uint64 access); void requestStickerSets(); + void saveStickerSets(const Stickers::Order &localOrder, const Stickers::Order &localRemoved); void joinChannel(ChannelData *channel); void leaveChannel(ChannelData *channel); @@ -67,11 +67,9 @@ public: ~ApiWrap(); signals: - void fullPeerUpdated(PeerData *peer); public slots: - void resolveMessageDatas(); void resolveWebPages(); @@ -79,7 +77,6 @@ public slots: void saveDraftsToCloud(); private: - void updatesReceived(const MTPUpdates &updates); void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req); @@ -158,4 +155,16 @@ private: void saveCloudDraftDone(History *history, const MTPBool &result, mtpRequestId requestId); bool saveCloudDraftFail(History *history, const RPCError &error, mtpRequestId requestId); + OrderedSet _stickerSetDisenableRequests; + void stickerSetDisenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req); + bool stickerSetDisenableFail(const RPCError &error, mtpRequestId req); + Stickers::Order _stickersOrder; + mtpRequestId _stickersReorderRequestId = 0; + void stickersSaveOrder(); + void stickersReorderDone(const MTPBool &result); + bool stickersReorderFail(const RPCError &result); + mtpRequestId _stickersClearRecentRequestId = 0; + void stickersClearRecentDone(const MTPBool &result); + bool stickersClearRecentFail(const RPCError &result); + }; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index c0f83f16a3..a7eb94ef2c 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2093,7 +2093,10 @@ namespace { Global::SetLastStickersUpdate(0); Global::SetLastRecentStickersUpdate(0); Global::SetFeaturedStickerSetsOrder(Stickers::Order()); - Global::SetFeaturedStickerSetsUnreadCount(0); + if (Global::FeaturedStickerSetsUnreadCount() != 0) { + Global::SetFeaturedStickerSetsUnreadCount(0); + Global::RefFeaturedStickerSetsUnreadCountChanged().notify(); + } Global::SetLastFeaturedStickersUpdate(0); Global::SetArchivedStickerSetsOrder(Stickers::Order()); cSetSavedGifs(SavedGifs()); diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp index 5f0fb5baff..b216b6ece2 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.cpp +++ b/Telegram/SourceFiles/boxes/aboutbox.cpp @@ -60,7 +60,7 @@ void AboutBox::resizeEvent(QResizeEvent *e) { void AboutBox::onVersion() { if (cRealBetaVersion()) { - QString url = qsl("https://tdesktop.com/"); + auto url = qsl("https://tdesktop.com/"); switch (cPlatform()) { case dbipWindows: url += qsl("win/%1.zip"); break; case dbipMac: url += qsl("mac/%1.zip"); break; diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index 005b1a4493..b92660e9b1 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -58,13 +58,17 @@ void AbstractBox::keyPressEvent(QKeyEvent *e) { } void AbstractBox::resizeEvent(QResizeEvent *e) { + updateBlockTitleGeometry(); + LayerWidget::resizeEvent(e); +} + +void AbstractBox::updateBlockTitleGeometry() { if (_blockClose) { _blockClose->moveToRight(0, 0); } if (_blockShadow) { _blockShadow->setGeometry(0, st::boxBlockTitleHeight, width(), st::boxBlockTitleShadow.height()); } - LayerWidget::resizeEvent(e); } void AbstractBox::parentResized() { @@ -143,11 +147,22 @@ void AbstractBox::onClose() { emit closed(this); } -void AbstractBox::setBlockTitle(bool block) { +void AbstractBox::setBlockTitle(bool block, bool withClose, bool withShadow) { _blockTitle = block; - _blockShadow.create(this, st::boxBlockTitleShadow); - _blockClose.create(this, st::boxBlockTitleClose); - _blockClose->setClickedCallback([this] { onClose(); }); + if (withClose) { + _blockClose.create(this, st::boxBlockTitleClose); + _blockClose->setClickedCallback([this] { onClose(); }); + _blockClose->show(); + } else { + _blockClose.destroy(); + } + if (withShadow) { + _blockShadow.create(this, st::boxBlockTitleShadow); + _blockShadow->show(); + } else { + _blockShadow.destroy(); + } + updateBlockTitleGeometry(); } void AbstractBox::raiseShadow() { diff --git a/Telegram/SourceFiles/boxes/abstractbox.h b/Telegram/SourceFiles/boxes/abstractbox.h index 6db669f16b..73c01882c8 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.h +++ b/Telegram/SourceFiles/boxes/abstractbox.h @@ -38,7 +38,7 @@ public: void setTitleText(const QString &title); void setAdditionalTitle(const QString &additionalTitle); - void setBlockTitle(bool block); + void setBlockTitle(bool block, bool withClose = true, bool withShadow = true); void raiseShadow(); public slots: @@ -59,9 +59,11 @@ protected: } private: - int _maxHeight = 0; + void updateBlockTitleGeometry(); int countHeight() const; + int _maxHeight = 0; + bool _closed = false; QString _title; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index f57102f6ed..0ad1d63d1e 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -470,7 +470,7 @@ confirmCaptionArea: InputArea(defaultInputArea) { textMargins: margins(1px, 6px, 1px, 4px); heightMax: 56px; } -confirmBg: #f2f2f2; +confirmBg: windowBgOver; confirmMaxHeight: 245px; confirmCompressedSkip: 10px; diff --git a/Telegram/SourceFiles/boxes/notifications_box.cpp b/Telegram/SourceFiles/boxes/notifications_box.cpp index f0bd270d82..a665c40c1c 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.cpp +++ b/Telegram/SourceFiles/boxes/notifications_box.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "ui/widgets/buttons.h" -#include "ui/widgets/discrete_slider.h" +#include "ui/widgets/discrete_sliders.h" #include "styles/style_boxes.h" #include "styles/style_dialogs.h" #include "styles/style_window.h" diff --git a/Telegram/SourceFiles/boxes/notifications_box.h b/Telegram/SourceFiles/boxes/notifications_box.h index a6c948819c..14d8ecc8ae 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.h +++ b/Telegram/SourceFiles/boxes/notifications_box.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class RoundButton; class LinkButton; -class DiscreteSlider; +class SettingsSlider; } // namespace Ui class NotificationsBox : public AbstractBox { @@ -70,7 +70,7 @@ private: ScreenCorner _downCorner = ScreenCorner::TopLeft; int _oldCount; - ChildWidget _countSlider; + ChildWidget _countSlider; ChildWidget _done; QVector _cornerSamples[4]; diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 75bf12c055..bbe6c7530e 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -34,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/effects/ripple_animation.h" +#include "ui/widgets/discrete_sliders.h" namespace { @@ -42,14 +43,14 @@ constexpr int kArchivedLimitPerPage = 30; } // namespace -int32 stickerPacksCount(bool includeDisabledOfficial) { - int32 result = 0; +int stickerPacksCount(bool includeArchivedOfficial) { + auto result = 0; auto &order = Global::StickerSetsOrder(); auto &sets = Global::StickerSets(); - for (int i = 0, l = order.size(); i < l; ++i) { + for (auto i = 0, l = order.size(); i < l; ++i) { auto it = sets.constFind(order.at(i)); if (it != sets.cend()) { - if (!(it->flags & MTPDstickerSet::Flag::f_archived) || ((it->flags & MTPDstickerSet::Flag::f_official) && includeDisabledOfficial)) { + if (!(it->flags & MTPDstickerSet::Flag::f_archived) || ((it->flags & MTPDstickerSet::Flag::f_official) && includeArchivedOfficial)) { ++result; } } @@ -57,17 +58,78 @@ int32 stickerPacksCount(bool includeDisabledOfficial) { return result; } +class StickersBox::CounterWidget : public TWidget, private base::Subscriber { +public: + CounterWidget(QWidget *parent); + + void setCounter(int counter); + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + void updateCounter(); + + QString _text; + Dialogs::Layout::UnreadBadgeStyle _st; + +}; + +StickersBox::CounterWidget::CounterWidget(QWidget *parent) : TWidget(parent) { + setAttribute(Qt::WA_TransparentForMouseEvents); + + _st.sizeId = Dialogs::Layout::UnreadBadgeInStickersBox; + _st.textTop = st::stickersFeaturedBadgeTextTop; + _st.size = st::stickersFeaturedBadgeSize; + _st.padding = st::stickersFeaturedBadgePadding; + _st.font = st::stickersFeaturedBadgeFont; + + subscribe(Global::RefFeaturedStickerSetsUnreadCountChanged(), [this] { updateCounter(); }); + updateCounter(); +} + + +void StickersBox::CounterWidget::setCounter(int counter) { + _text = (counter > 0) ? QString::number(counter) : QString(); + auto dummy = QImage(1, 1, QImage::Format_ARGB32_Premultiplied); + Painter p(&dummy); + + auto newWidth = 0; + Dialogs::Layout::paintUnreadCount(p, _text, 0, 0, _st, &newWidth); + + resize(newWidth, st::stickersFeaturedBadgeSize); +} + +void StickersBox::CounterWidget::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_text.isEmpty()) { + auto unreadRight = rtl() ? 0 : width(); + auto unreadTop = 0; + Dialogs::Layout::paintUnreadCount(p, _text, unreadRight, unreadTop, _st); + } +} + +void StickersBox::CounterWidget::updateCounter() { + setCounter(Global::FeaturedStickerSetsUnreadCount()); + update(); +} + StickersBox::StickersBox(Section section) : ItemListBox(st::boxScroll) +, _topShadow(this, st::shadowColor) +, _tabs(this, st::defaultTabsSlider) +, _unreadBadge(this) , _section(section) -, _inner(this, section) -, _aboutWidth(st::boxWideWidth - 2 * st::stickersReorderPadding.top()) -, _about(st::boxTextFont, lang((section == Section::Archived) ? lng_stickers_packs_archived : lng_stickers_reorder), _defaultOptions, _aboutWidth) { +, _installed(0, this, Section::Installed) +, _featured(1, this, Section::Featured) +, _archived(2, this, Section::Archived) { setup(); } StickersBox::StickersBox(const Stickers::Order &archivedIds) : ItemListBox(st::boxScroll) +, _topShadow(this, st::shadowColor) , _section(Section::ArchivedPart) -, _inner(this, archivedIds) +, _archived(0, this, archivedIds) , _aboutWidth(st::boxWideWidth - 2 * st::stickersReorderPadding.top()) , _about(st::boxTextFont, lang(lng_stickers_packs_archived), _defaultOptions, _aboutWidth) { setup(); @@ -75,6 +137,7 @@ StickersBox::StickersBox(const Stickers::Order &archivedIds) : ItemListBox(st::b void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result) { _archivedRequestId = 0; + _archivedLoaded = true; if (result.type() != mtpc_messages_archivedStickers) { return; } @@ -118,28 +181,25 @@ void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedSti } archived.push_back(set->id); } - if (_section == Section::Archived) { - if (_inner->appendSet(*set)) { - addedSet = true; - if (set->stickers.isEmpty() || (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { - App::api()->scheduleStickerSetRequest(set->id, set->access); - } + if (_archived.widget->appendSet(*set)) { + addedSet = true; + if (set->stickers.isEmpty() || (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { + App::api()->scheduleStickerSetRequest(set->id, set->access); } } } } - if (_section == Section::Installed && !archived.isEmpty()) { - Local::writeArchivedStickers(); - rebuildList(); - } else if (_section == Section::Archived) { - if (addedSet) { - _inner->updateSize(); - setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); - _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); - App::api()->requestStickerSets(); - } else { - _allArchivedLoaded = v.isEmpty() || (offsetId != 0); - } + if (addedSet) { + _archived.widget->updateSize(); + _archived.widget->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); + } else { + _allArchivedLoaded = v.isEmpty() || (offsetId != 0); + } + + refreshTabs(); + _someArchivedLoaded = true; + if (_section == Section::Archived && addedSet) { + App::api()->requestStickerSets(); } checkLoadMoreArchived(); } @@ -147,62 +207,57 @@ void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedSti void StickersBox::setup() { if (_section == Section::Installed) { Local::readArchivedStickers(); - if (Global::ArchivedStickerSetsOrder().isEmpty()) { - MTPmessages_GetArchivedStickers::Flags flags = 0; - _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(0), MTP_int(kArchivedLimitFirstRequest)), rpcDone(&StickersBox::getArchivedDone, 0ULL)); - } - setTitleText(lang(lng_stickers_packs)); } else if (_section == Section::Archived) { - // Reload the archived list. - MTPmessages_GetArchivedStickers::Flags flags = 0; - _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(0), MTP_int(kArchivedLimitFirstRequest)), rpcDone(&StickersBox::getArchivedDone, 0ULL)); - - auto &sets = Global::StickerSets(); - for_const (auto setId, Global::ArchivedStickerSetsOrder()) { - auto it = sets.constFind(setId); - if (it != sets.cend()) { - if (it->stickers.isEmpty() && (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { - App::api()->scheduleStickerSetRequest(setId, it->access); - } - } - } - App::api()->requestStickerSets(); + requestArchivedSets(); + } else if (_section == Section::ArchivedPart) { setTitleText(lang(lng_stickers_archived)); - } else { - setTitleText(lang(lng_stickers_featured)); } + if (Global::ArchivedStickerSetsOrder().isEmpty()) { + preloadArchivedSets(); + } + if (_tabs) { + _tabs->setSelectOnPress(false); + _tabs->setSectionActivatedCallback([this] { + switchTab(); + }); + refreshTabs(); + } + if (_installed.widget && _section != Section::Installed) _installed.widget->hide(); + if (_featured.widget && _section != Section::Featured) _featured.widget->hide(); + if (_section != Section::Archived && _section != Section::ArchivedPart) _archived.widget->hide(); - int bottomSkip = st::boxPadding.bottom(); + if (_featured.widget) { + _featured.widget->setInstallSetCallback([this](uint64 setId) { installSet(setId); }); + } + _archived.widget->setInstallSetCallback([this](uint64 setId) { installSet(setId); }); + + _done.create(this, lang(lng_about_done), st::defaultBoxButton); + connect(_done, SIGNAL(clicked()), this, SLOT(onClose())); + + auto bottomSkip = st::boxButtonPadding.top() + _done->height() + st::boxButtonPadding.bottom(); if (_section == Section::Installed) { - _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); - _topShadow.create(this, st::contactsAboutShadow); - - _save.create(this, lang(lng_settings_save), st::defaultBoxButton); - connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); - - _cancel.create(this, lang(lng_cancel), st::cancelBoxButton); - connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - _bottomShadow.create(this); - bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); + _tab = &_installed; } else if (_section == Section::ArchivedPart) { _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); - _topShadow.create(this, st::contactsAboutShadow); - - _save.create(this, lang(lng_box_ok), st::defaultBoxButton); - connect(_save, SIGNAL(clicked()), this, SLOT(onClose())); + _tab = &_archived; } else if (_section == Section::Archived) { - _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); - _topShadow.create(this, st::contactsAboutShadow); + _bottomShadow.create(this); + _tab = &_archived; + } else { // _section == Section::Featured + _bottomShadow.create(this); + _tab = &_featured; } - ItemListBox::init(_inner, bottomSkip, titleHeight() + _aboutHeight); - setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); + ItemListBox::init(_tab->widget, bottomSkip, titleHeight() + _aboutHeight); + setMaxHeight((_section == Section::ArchivedPart) ? st::sessionsHeight : st::boxMaxListHeight); connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); App::main()->updateStickers(); - connect(_inner, SIGNAL(checkDraggingScroll(int)), this, SLOT(onCheckDraggingScroll(int))); - connect(_inner, SIGNAL(noDraggingScroll()), this, SLOT(onNoDraggingScroll())); + if (_installed.widget) { + connect(_installed.widget, SIGNAL(checkDraggingScroll(int)), this, SLOT(onCheckDraggingScroll(int))); + connect(_installed.widget, SIGNAL(noDraggingScroll()), this, SLOT(onNoDraggingScroll())); + } connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); _scrollTimer.setSingleShot(false); @@ -212,6 +267,30 @@ void StickersBox::setup() { prepare(); } +void StickersBox::refreshTabs() { + if (!_tabs) return; + + _tabIndices.clear(); + auto sections = QStringList(); + sections.push_back(lang(lng_stickers_installed_tab).toUpper()); + _tabIndices.push_back(Section::Installed); + if (!Global::FeaturedStickerSetsOrder().isEmpty()) { + sections.push_back(lang(lng_stickers_featured_tab).toUpper()); + _tabIndices.push_back(Section::Featured); + } + if (!Global::ArchivedStickerSetsOrder().isEmpty()) { + sections.push_back(lang(lng_stickers_archived_tab).toUpper()); + _tabIndices.push_back(Section::Archived); + } + _tabs->setSections(sections); + if ((_tab == &_archived && !_tabIndices.contains(Section::Archived)) + || (_tab == &_featured && !_tabIndices.contains(Section::Featured))) { + switchTab(); + } + updateTabsGeometry(); + setBlockTitle(true, (sections.size() < 3), false); +} + void StickersBox::onScroll() { updateVisibleTopBottom(); checkLoadMoreArchived(); @@ -220,7 +299,7 @@ void StickersBox::onScroll() { void StickersBox::updateVisibleTopBottom() { auto visibleTop = scrollArea()->scrollTop(); auto visibleBottom = visibleTop + scrollArea()->height(); - _inner->setVisibleTopBottom(visibleTop, visibleBottom); + _tab->widget->setVisibleTopBottom(visibleTop, visibleBottom); } void StickersBox::checkLoadMoreArchived() { @@ -246,60 +325,6 @@ void StickersBox::checkLoadMoreArchived() { } } -int32 StickersBox::countHeight() const { - int bottomSkip = st::boxPadding.bottom(); - if (_section == Section::Installed) { - bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); - } - return titleHeight() + _aboutHeight + _inner->height() + bottomSkip; -} - -void StickersBox::disenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req) { - _disenableRequests.remove(req); - if (_disenableRequests.isEmpty()) { - saveOrder(); - } -} - -bool StickersBox::disenableFail(const RPCError &error, mtpRequestId req) { - if (MTP::isDefaultHandledError(error)) return false; - _disenableRequests.remove(req); - if (_disenableRequests.isEmpty()) { - saveOrder(); - } - return true; -} - -void StickersBox::saveOrder() { - auto order = _inner->getOrder(); - if (order.size() > 1) { - QVector mtpOrder; - mtpOrder.reserve(order.size()); - for (int i = 0, l = order.size(); i < l; ++i) { - mtpOrder.push_back(MTP_long(order.at(i))); - } - - MTPmessages_ReorderStickerSets::Flags flags = 0; - _reorderRequest = MTP::send(MTPmessages_ReorderStickerSets(MTP_flags(flags), MTP_vector(mtpOrder)), rpcDone(&StickersBox::reorderDone), rpcFail(&StickersBox::reorderFail)); - } else { - reorderDone(MTP_boolTrue()); - } -} - -void StickersBox::reorderDone(const MTPBool &result) { - _reorderRequest = 0; - onClose(); -} - -bool StickersBox::reorderFail(const RPCError &result) { - if (MTP::isDefaultHandledError(result)) return false; - _reorderRequest = 0; - Global::SetLastStickersUpdate(0); - App::main()->updateStickers(); - onClose(); - return true; -} - void StickersBox::paintEvent(QPaintEvent *e) { AbstractBox::paintEvent(e); @@ -312,44 +337,206 @@ void StickersBox::paintEvent(QPaintEvent *e) { p.setPen(st::stickersReorderFg); _about.draw(p, st::stickersReorderPadding.top(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center); } -} -void StickersBox::closePressed() { - if (!_disenableRequests.isEmpty()) { - for_const (auto requestId, _disenableRequests) { - MTP::cancel(requestId); + if (!_leftCache.isNull()) { + auto slide = _a_slide.current(getms(), _slideLeft ? 0. : 1.); + if (!_a_slide.animating()) { + _leftCache = _rightCache = QPixmap(); + scrollArea()->show(); + update(); + } else { + auto easeOut = anim::easeOutCirc(1., slide); + auto easeIn = anim::easeInCirc(1., slide); + auto cacheWidth = (_leftCache.width() / cIntRetinaFactor()); + auto arrivingCoord = anim::interpolate(cacheWidth, 0, easeOut); + auto departingCoord = anim::interpolate(0, cacheWidth, easeIn); + auto arrivingAlpha = easeIn; + auto departingAlpha = 1. - easeOut; + auto leftCoord = (_slideLeft ? arrivingCoord : departingCoord) * -1; + auto leftAlpha = (_slideLeft ? arrivingAlpha : departingAlpha); + auto rightCoord = (_slideLeft ? departingCoord : arrivingCoord); + auto rightAlpha = (_slideLeft ? departingAlpha : arrivingAlpha); + + auto x = scrollArea()->x(); + auto y = scrollArea()->y() - titleHeight(); + auto leftWidth = (cacheWidth + leftCoord); + if (leftWidth > 0) { + p.setOpacity(leftAlpha); + p.drawPixmap(x, y, leftWidth, _leftCache.height() / cIntRetinaFactor(), _leftCache, (_leftCache.width() - leftWidth * cIntRetinaFactor()), 0, leftWidth * cIntRetinaFactor(), _leftCache.height()); + } + auto rightWidth = cacheWidth - rightCoord; + if (rightWidth > 0) { + p.setOpacity(rightAlpha); + p.drawPixmap(x + rightCoord, y, _rightCache, 0, 0, rightWidth * cIntRetinaFactor(), _rightCache.height()); + } } - _disenableRequests.clear(); - Global::SetLastStickersUpdate(0); - App::main()->updateStickers(); - } else if (_reorderRequest) { - MTP::cancel(_reorderRequest); - _reorderRequest = 0; - Global::SetLastStickersUpdate(0); - App::main()->updateStickers(); } } -StickersBox::~StickersBox() { - if (_section == Section::Archived) { - Local::writeArchivedStickers(); +void StickersBox::updateTabsGeometry() { + if (!_tabs) return; + + _tabs->resizeToWidth(_tabIndices.size() * width() / 3); + _unreadBadge->setVisible(_tabIndices.contains(Section::Featured)); + + auto featuredLeft = width() / 3; + auto featuredRight = 2 * width() / 3; + auto featuredTextWidth = st::defaultTabsSlider.labelFont->width(lang(lng_stickers_featured_tab).toUpper()); + auto featuredTextRight = featuredLeft + (featuredRight - featuredLeft - featuredTextWidth) / 2 + featuredTextWidth; + auto unreadBadgeLeft = featuredTextRight - st::stickersFeaturedBadgeSkip; + auto unreadBadgeTop = st::stickersFeaturedBadgeTop; + if (unreadBadgeLeft + _unreadBadge->width() > featuredRight) { + unreadBadgeLeft = featuredRight - _unreadBadge->width(); } + _unreadBadge->moveToLeft(unreadBadgeLeft, unreadBadgeTop); + + _tabs->moveToLeft(0, 0); +} + +void StickersBox::switchTab() { + if (!_tabs) return; + + auto tab = _tabs->activeSection(); + t_assert(tab >= 0 && tab < _tabIndices.size()); + auto newSection = _tabIndices[tab]; + + auto newTab = _tab; + if (newSection == Section::Installed) { + newTab = &_installed; + } else if (newSection == Section::Featured) { + newTab = &_featured; + } else if (newSection == Section::Archived) { + newTab = &_archived; + requestArchivedSets(); + } + if (_tab != newTab) { + if (_tab == &_installed) { + _localOrder = _tab->widget->getFullOrder(); + _localRemoved = _tab->widget->getRemovedSets(); + } + auto scroll = scrollArea(); + auto wasCache = grabContentCache(); + auto wasIndex = _tab->index; + _tab->scrollTop = scroll->scrollTop(); + auto widget = scroll->takeWidget(); + widget->setParent(this); + widget->hide(); + _tab = newTab; + _section = newSection; + scroll->setOwnedWidget(newTab->widget); + _tab->widget->show(); + rebuildList(); + scroll->scrollToY(_tab->scrollTop); + auto nowCache = grabContentCache(); + auto nowIndex = _tab->index; + + _leftCache = std_::move(wasCache); + _rightCache = std_::move(nowCache); + _slideLeft = (wasIndex > nowIndex); + if (_slideLeft) { + std_::swap_moveable(_leftCache, _rightCache); + } + scrollArea()->hide(); + _a_slide.start([this] { update(); }, 0., 1., st::slideDuration); + update(); + } +} + +QPixmap StickersBox::grabContentCache() { + _topShadow->hide(); + _bottomShadow->hide(); + _tabs->hide(); + auto result = myGrab(this, scrollArea()->geometry()); + _topShadow->show(); + _bottomShadow->show(); + _tabs->show(); + return std_::move(result); +} + +void StickersBox::installSet(uint64 setId) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(setId); + if (it == sets.cend()) { + rebuildList(); + return; + } + + if (_localRemoved.contains(setId)) { + _localRemoved.removeOne(setId); + if (_installed.widget) _installed.widget->setRemovedSets(_localRemoved); + if (_featured.widget) _featured.widget->setRemovedSets(_localRemoved); + _archived.widget->setRemovedSets(_localRemoved); + } + if (!(it->flags & MTPDstickerSet::Flag::f_installed) || (it->flags & MTPDstickerSet::Flag::f_archived)) { + MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_boolFalse()), rpcDone(&StickersBox::installDone), rpcFail(&StickersBox::installFail, setId)); + + Stickers::installLocally(setId); + } +} + +void StickersBox::installDone(const MTPmessages_StickerSetInstallResult &result) { + if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { + Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); + } +} + +bool StickersBox::installFail(uint64 setId, const RPCError &error) { + if (MTP::isDefaultHandledError(error)) return false; + + auto &sets = Global::RefStickerSets(); + auto it = sets.find(setId); + if (it == sets.cend()) { + rebuildList(); + return true; + } + + Stickers::undoInstallLocally(setId); + return true; +} + +void StickersBox::preloadArchivedSets() { + if (!_archivedRequestId) { + MTPmessages_GetArchivedStickers::Flags flags = 0; + _archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(flags), MTP_long(0), MTP_int(kArchivedLimitFirstRequest)), rpcDone(&StickersBox::getArchivedDone, 0ULL)); + } +} + +void StickersBox::requestArchivedSets() { + // Reload the archived list. + if (!_archivedLoaded) { + preloadArchivedSets(); + } + + auto &sets = Global::StickerSets(); + for_const (auto setId, Global::ArchivedStickerSetsOrder()) { + auto it = sets.constFind(setId); + if (it != sets.cend()) { + if (it->stickers.isEmpty() && (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { + App::api()->scheduleStickerSetRequest(setId, it->access); + } + } + } + App::api()->requestStickerSets(); } void StickersBox::resizeEvent(QResizeEvent *e) { ItemListBox::resizeEvent(e); - _inner->resize(width(), _inner->height()); - _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); + if (_tabs) { + updateTabsGeometry(); + _topShadow->setGeometryToLeft(0, _tabs->height() - st::lineWidth, width(), st::lineWidth); + } else { + _topShadow->setGeometryToLeft(0, scrollArea()->y(), width(), st::lineWidth); + } + if (_installed.widget) _installed.widget->resize(width(), _installed.widget->height()); + if (_featured.widget) _featured.widget->resize(width(), _featured.widget->height()); + _archived.widget->resize(width(), _archived.widget->height()); + _tab->widget->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); updateVisibleTopBottom(); - if (_topShadow) { - _topShadow->setGeometry(0, titleHeight() + _aboutHeight, width(), st::lineWidth); + if (_done) { + _done->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _done->height()); } - if (_save) { - _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); - } - if (_cancel) { - _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); - _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _save->height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); + if (_bottomShadow) { + _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _done->height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); } } @@ -357,14 +544,26 @@ void StickersBox::onStickersUpdated() { if (_section == Section::Installed || _section == Section::Featured) { rebuildList(); } else { - _inner->updateRows(); + _tab->widget->updateRows(); + } + if (Global::ArchivedStickerSetsOrder().isEmpty()) { + preloadArchivedSets(); + } else { + refreshTabs(); } } void StickersBox::rebuildList() { - _inner->rebuild(); - setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); - _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); + if (_tab == &_installed) { + _localOrder = _tab->widget->getFullOrder(); + _localRemoved = _tab->widget->getRemovedSets(); + } + _tab->widget->rebuild(); + if (_tab == &_installed) { + _tab->widget->setFullOrder(_localOrder); + } + _tab->widget->setRemovedSets(_localRemoved); + _tab->widget->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); } void StickersBox::onCheckDraggingScroll(int localY) { @@ -391,96 +590,15 @@ void StickersBox::onScrollTimer() { scrollArea()->scrollToY(scrollArea()->scrollTop() + d); } -void StickersBox::onSave() { - if (!_inner->savingStart()) { +void StickersBox::closePressed() { + if (!_installed.widget) { return; } - - bool writeRecent = false, writeArchived = false; - auto &recent = cGetRecentStickers(); - auto &sets = Global::RefStickerSets(); - - auto reorder = _inner->getOrder(), disabled = _inner->getDisabledSets(); - for (int32 i = 0, l = disabled.size(); i < l; ++i) { - auto it = sets.find(disabled.at(i)); - if (it != sets.cend()) { - for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { - if (it->stickers.indexOf(i->first) >= 0) { - i = recent.erase(i); - writeRecent = true; - } else { - ++i; - } - } - if (!(it->flags & MTPDstickerSet::Flag::f_archived)) { - MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); - if (it->flags & MTPDstickerSet::Flag::f_official) { - _disenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(setId, MTP_boolTrue()), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); - it->flags |= MTPDstickerSet::Flag::f_archived; - auto index = Global::RefArchivedStickerSetsOrder().indexOf(it->id); - if (index < 0) { - Global::RefArchivedStickerSetsOrder().push_front(it->id); - writeArchived = true; - } - } else { - _disenableRequests.insert(MTP::send(MTPmessages_UninstallStickerSet(setId), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); - int removeIndex = Global::StickerSetsOrder().indexOf(it->id); - if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); - if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) { - sets.erase(it); - } else { - if (it->flags & MTPDstickerSet::Flag::f_archived) { - writeArchived = true; - } - it->flags &= ~(MTPDstickerSet::Flag::f_installed | MTPDstickerSet::Flag::f_archived); - } - } - } - } + if (_someArchivedLoaded) { + Local::writeArchivedStickers(); } - - // Clear all installed flags, set only for sets from order. - for (auto &set : sets) { - if (!(set.flags & MTPDstickerSet::Flag::f_archived)) { - set.flags &= ~MTPDstickerSet::Flag::f_installed; - } - } - - auto &order(Global::RefStickerSetsOrder()); - order.clear(); - for (int i = 0, l = reorder.size(); i < l; ++i) { - auto it = sets.find(reorder.at(i)); - if (it != sets.cend()) { - if ((it->flags & MTPDstickerSet::Flag::f_archived) && !disabled.contains(it->id)) { - MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); - _disenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(setId, MTP_boolFalse()), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5)); - it->flags &= ~MTPDstickerSet::Flag::f_archived; - writeArchived = true; - } - order.push_back(reorder.at(i)); - it->flags |= MTPDstickerSet::Flag::f_installed; - } - } - for (auto it = sets.begin(); it != sets.cend();) { - if ((it->flags & MTPDstickerSet_ClientFlag::f_featured) - || (it->flags & MTPDstickerSet::Flag::f_installed) - || (it->flags & MTPDstickerSet::Flag::f_archived) - || (it->flags & MTPDstickerSet_ClientFlag::f_special)) { - ++it; - } else { - it = sets.erase(it); - } - } - - Local::writeInstalledStickers(); - if (writeRecent) Local::writeUserSettings(); - if (writeArchived) Local::writeArchivedStickers(); - emit App::main()->stickersUpdated(); - - if (_disenableRequests.isEmpty()) { - saveOrder(); - } else { - MTP::sendAnything(); + if (auto api = App::api()) { + api->saveStickerSets(_installed.widget->getOrder(), _installed.widget->getRemovedSets()); } } @@ -489,10 +607,10 @@ StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : TWidg , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _a_shifting(animation(this, &Inner::step_shifting)) , _itemsTop(st::membersPadding.top()) -, _clearWidth(st::normalFont->width(lang(lng_stickers_clear_recent))) -, _removeWidth(st::normalFont->width(lang(lng_stickers_remove))) -, _returnWidth(st::normalFont->width(lang(lng_stickers_return))) -, _restoreWidth(st::normalFont->width(lang(lng_stickers_restore))) +, _addText(lang(lng_stickers_featured_add).toUpper()) +, _addWidth(st::stickersTrendingAdd.font->width(_addText)) +, _undoText(lang(lng_stickers_return).toUpper()) +, _undoWidth(st::stickersUndoRemove.font->width(_undoText)) , _aboveShadow(st::boxShadow) { setup(); } @@ -503,49 +621,20 @@ StickersBox::Inner::Inner(QWidget *parent, const Stickers::Order &archivedIds) : , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _a_shifting(animation(this, &Inner::step_shifting)) , _itemsTop(st::membersPadding.top()) -, _clearWidth(st::normalFont->width(lang(lng_stickers_clear_recent))) -, _removeWidth(st::normalFont->width(lang(lng_stickers_remove))) -, _returnWidth(st::normalFont->width(lang(lng_stickers_return))) -, _restoreWidth(st::normalFont->width(lang(lng_stickers_restore))) +, _addText(lang(lng_stickers_featured_add).toUpper()) +, _addWidth(st::stickersTrendingAdd.font->width(_addText)) , _aboveShadow(st::boxShadow) { setup(); } void StickersBox::Inner::setup() { - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(FileDownload::ImageLoaded(), [this] { + update(); + readVisibleSets(); + }); setMouseTracking(true); } -void StickersBox::Inner::onImageLoaded() { - update(); - readVisibleSets(); -} - -void StickersBox::Inner::paintButton(Painter &p, int y, bool selected, std_::unique_ptr &ripple, const QString &text, int badgeCounter) const { - if (selected) { - p.fillRect(0, y, width(), _buttonHeight, st::contactsBgOver); - } - if (ripple) { - ripple->paint(p, 0, y, width(), getms()); - if (ripple->empty()) { - ripple.reset(); - } - } - p.setFont(st::stickersFeaturedFont); - p.setPen(st::stickersFeaturedPen); - p.drawTextLeft(st::stickersFeaturedPosition.x(), y + st::stickersFeaturedPosition.y(), width(), text); - - if (badgeCounter) { - Dialogs::Layout::UnreadBadgeStyle unreadSt; - unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersBox; - unreadSt.size = st::stickersFeaturedBadgeSize; - int unreadRight = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x()); - if (rtl()) unreadRight = width() - unreadRight; - int unreadTop = y + (_buttonHeight - st::stickersFeaturedBadgeSize) / 2; - Dialogs::Layout::paintUnreadCount(p, QString::number(badgeCounter), unreadRight, unreadTop, unreadSt); - } -} - void StickersBox::Inner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); Painter p(this); @@ -557,17 +646,6 @@ void StickersBox::Inner::paintEvent(QPaintEvent *e) { p.setClipRect(r); int y = st::membersPadding.top(); - if (_hasFeaturedButton) { - auto selected = (_selected == -2); - paintButton(p, y, selected, _featuredRipple, lang(lng_stickers_featured), Global::FeaturedStickerSetsUnreadCount()); - y += _buttonHeight; - } - if (_hasArchivedButton) { - auto selected = (_selected == -1); - paintButton(p, y, selected, _archivedRipple, lang(lng_stickers_archived), 0); - y += _buttonHeight; - } - if (_rows.isEmpty()) { p.setFont(st::noContactsFont); p.setPen(st::noContactsColor); @@ -592,30 +670,30 @@ void StickersBox::Inner::paintEvent(QPaintEvent *e) { } } -QRect StickersBox::Inner::relativeAddButtonRect() const { - int addw = st::stickersAddSize.width(); - int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; - int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; - return QRect(addx, addy, addw, st::stickersAddSize.height()); +QRect StickersBox::Inner::relativeButtonRect(bool removeButton) const { + auto buttonw = st::stickersRemove.width; + auto buttonh = st::stickersRemove.height; + auto buttonshift = st::stickersRemoveSkip; + if (!removeButton) { + auto &st = (_section == Section::Installed) ? st::stickersUndoRemove : st::stickersTrendingAdd; + auto textWidth = (_section == Section::Installed) ? _undoWidth : _addWidth; + buttonw = textWidth - st.width; + buttonh = st.height; + buttonshift = 0; + } + auto buttonx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - buttonw + buttonshift; + auto buttony = st::contactsPadding.top() + (st::contactsPhotoSize - buttonh) / 2; + return QRect(buttonx, buttony, buttonw, buttonh); } -void StickersBox::Inner::paintRow(Painter &p, int32 index, uint64 ms) { +void StickersBox::Inner::paintRow(Painter &p, int index, uint64 ms) { auto s = _rows.at(index); - int32 xadd = 0, yadd = s->yadd.current(); + auto xadd = 0, yadd = s->yadd.current(); if (xadd || yadd) p.translate(xadd, yadd); if (_section == Section::Installed) { - bool removeSel = (index == _actionSel && (_actionDown < 0 || index == _actionDown)); - bool removeDown = removeSel && (index == _actionDown); - - p.setFont(removeSel ? st::linkOverFont : st::linkFont); - p.setPen(removeDown ? st::defaultLinkButton.downColor : st::defaultLinkButton.color); - - int32 remWidth = s->recent ? _clearWidth : (s->disabled ? (s->official ? _restoreWidth : _returnWidth) : _removeWidth); - QString remText = lang(s->recent ? lng_stickers_clear_recent : (s->disabled ? (s->official ? lng_stickers_restore : lng_stickers_return) : lng_stickers_remove)); - p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), remText, remWidth); - + paintFakeButton(p, index, ms); if (index == _above) { float64 current = _aboveShadowFadeOpacity.current(); if (_started >= 0) { @@ -631,42 +709,32 @@ void StickersBox::Inner::paintRow(Painter &p, int32 index, uint64 ms) { p.fillRect(row, st::boxBg); p.setOpacity(1); } - } else if (s->installed && !s->disabled) { - int addw = st::stickersAddSize.width(); - int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (addw + st::stickersFeaturedInstalled.width()) / 2); - int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2; - st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width()); - if (s->ripple) { - s->ripple.reset(); - } } else { - auto relativeAdd = relativeAddButtonRect(); - auto add = myrtlrect(relativeAdd); - - auto &textBg = (_actionSel == index || _actionDown == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg; - App::roundRect(p, add, textBg, ImageRoundRadius::Small); - if (s->ripple) { - s->ripple->paint(p, relativeAdd.x(), relativeAdd.y(), width(), ms); - if (s->ripple->empty()) { - s->ripple.reset(); - } - } - int iconx = relativeAdd.x() + (st::stickersAddSize.width() - st::stickersAddIcon.width()) / 2; - int icony = relativeAdd.y() + (st::stickersAddSize.height() - st::stickersAddIcon.height()) / 2; - st::stickersAddIcon.paint(p, QPoint(iconx, icony), width()); + paintFakeButton(p, index, ms); } - if (s->disabled && _section == Section::Installed) { + if (s->removed && _section == Section::Installed) { p.setOpacity(st::stickersRowDisabledOpacity); } + + auto stickerx = st::contactsPadding.left(); + + if (_section == Section::Installed) { + stickerx += st::stickersReorderIcon.width() + st::stickersReorderSkip; + if (!s->isRecentSet()) { + st::stickersReorderIcon.paint(p, st::contactsPadding.left(), (_rowHeight - st::stickersReorderIcon.height()) / 2, width()); + } + } + if (s->sticker) { s->sticker->thumb->load(); QPixmap pix(s->sticker->thumb->pix(s->pixw, s->pixh)); - p.drawPixmapLeft(st::contactsPadding.left() + (st::contactsPhotoSize - s->pixw) / 2, st::contactsPadding.top() + (st::contactsPhotoSize - s->pixh) / 2, width(), pix); + p.drawPixmapLeft(stickerx + (st::contactsPhotoSize - s->pixw) / 2, st::contactsPadding.top() + (st::contactsPhotoSize - s->pixh) / 2, width(), pix); } - int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); + int namex = stickerx + st::contactsPhotoSize + st::contactsPadding.left(); int namey = st::contactsPadding.top() + st::contactsNameTop; + int statusx = namex; int statusy = st::contactsPadding.top() + st::contactsStatusTop; @@ -691,17 +759,61 @@ void StickersBox::Inner::paintRow(Painter &p, int32 index, uint64 ms) { if (xadd || yadd) p.translate(-xadd, -yadd); } +void StickersBox::Inner::paintFakeButton(Painter &p, int index, uint64 ms) { + auto set = _rows[index]; + auto removeButton = (_section == Section::Installed && !set->removed); + auto rect = relativeButtonRect(removeButton); + if (_section != Section::Installed && set->installed && !set->archived && !set->removed) { + // Checkbox after installed from Trending or Archived. + int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (rect.width() + st::stickersFeaturedInstalled.width()) / 2); + int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2; + st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width()); + } else { + auto selected = (index == _actionSel && _actionDown < 0) || (index == _actionDown); + if (removeButton) { + // Trash icon button when not disabled in Installed. + if (set->ripple) { + set->ripple->paint(p, rect.x(), rect.y(), width(), ms); + if (set->ripple->empty()) { + set->ripple.reset(); + } + } + auto &icon = selected ? st::stickersRemove.iconOver : st::stickersRemove.icon; + auto position = st::stickersRemove.iconPosition; + if (position.x() < 0) position.setX((rect.width() - icon.width()) / 2); + if (position.y() < 0) position.setY((rect.height() - icon.height()) / 2); + icon.paint(p, rect.topLeft() + position, ms); + } else { + // Round button ADD when not installed from Trending or Archived. + // Or round button UNDO after disabled from Installed. + auto &st = (_section == Section::Installed) ? st::stickersUndoRemove : st::stickersTrendingAdd; + auto textWidth = (_section == Section::Installed) ? _undoWidth : _addWidth; + auto &text = (_section == Section::Installed) ? _undoText : _addText; + auto &textBg = selected ? st.textBgOver : st.textBg; + App::roundRect(p, myrtlrect(rect), textBg, ImageRoundRadius::Small); + if (set->ripple) { + set->ripple->paint(p, rect.x(), rect.y(), width(), ms); + if (set->ripple->empty()) { + set->ripple.reset(); + } + } + p.setFont(st.font); + p.setPen(selected ? st.textFgOver : st.textFg); + p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth); + } + } +} + void StickersBox::Inner::mousePressEvent(QMouseEvent *e) { - if (_saving) return; if (_dragging >= 0) mouseReleaseEvent(e); _mouse = e->globalPos(); onUpdateSelected(); - setPressed(_selected); + _pressed = _selected; if (_actionSel >= 0) { setActionDown(_actionSel); update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); - } else if (_selected >= 0 && _section == Section::Installed && !_rows.at(_selected)->recent) { + } else if (_selected >= 0 && _section == Section::Installed && !_rows.at(_selected)->isRecentSet() && _inDragArea) { _above = _dragging = _started = _selected; _dragStart = mapFromGlobal(_mouse); } @@ -722,32 +834,49 @@ void StickersBox::Inner::setActionDown(int newActionDown) { if (_actionDown >= 0 && _actionDown < _rows.size()) { update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight); auto set = _rows[_actionDown]; + auto removeButton = (_section == Section::Installed && !set->removed); if (!set->ripple) { - auto mask = Ui::RippleAnimation::roundRectMask(st::stickersAddSize, st::buttonRadius); - set->ripple = MakeShared(st::defaultActiveButton.ripple, std_::move(mask), [this, index = _actionDown] { - update(0, _itemsTop + index * _rowHeight, width(), _rowHeight); - }); + if (_section == Section::Installed) { + if (set->removed) { + auto rippleSize = QSize(_undoWidth - st::stickersUndoRemove.width, st::stickersUndoRemove.height); + auto rippleMask = Ui::RippleAnimation::roundRectMask(rippleSize, st::buttonRadius); + ensureRipple(st::stickersUndoRemove.ripple, std_::move(rippleMask), removeButton); + } else { + auto rippleSize = st::stickersRemove.rippleAreaSize; + auto rippleMask = Ui::RippleAnimation::ellipseMask(QSize(rippleSize, rippleSize)); + ensureRipple(st::stickersRemove.ripple, std_::move(rippleMask), removeButton); + } + } else if (!set->installed || set->archived || set->removed) { + auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height); + auto rippleMask = Ui::RippleAnimation::roundRectMask(rippleSize, st::buttonRadius); + ensureRipple(st::stickersTrendingAdd.ripple, std_::move(rippleMask), removeButton); + } + } + if (set->ripple) { + auto rect = relativeButtonRect(removeButton); + set->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(myrtlrect(rect).x(), _itemsTop + _actionDown * _rowHeight + rect.y())); } - auto relativeAdd = relativeAddButtonRect(); - auto add = myrtlrect(relativeAdd); - set->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(add.x(), _itemsTop + _actionDown * _rowHeight + add.y())); } } +void StickersBox::Inner::ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton) { + _rows[_actionDown]->ripple = MakeShared(st, std_::move(mask), [this, index = _actionDown, removeButton] { + update(myrtlrect(relativeButtonRect(removeButton).translated(0, _itemsTop + index * _rowHeight))); + }); +} + void StickersBox::Inner::mouseMoveEvent(QMouseEvent *e) { - if (_saving) return; _mouse = e->globalPos(); onUpdateSelected(); } void StickersBox::Inner::onUpdateSelected() { - if (_saving) return; QPoint local(mapFromGlobal(_mouse)); if (_dragging >= 0) { int32 shift = 0; uint64 ms = getms(); int firstSetIndex = 0; - if (_rows.at(firstSetIndex)->recent) { + if (_rows.at(firstSetIndex)->isRecentSet()) { ++firstSetIndex; } if (_dragStart.y() > local.y() && _dragging > 0) { @@ -780,73 +909,45 @@ void StickersBox::Inner::onUpdateSelected() { emit checkDraggingScroll(local.y()); } else { bool in = rect().marginsRemoved(QMargins(0, _itemsTop, 0, st::membersPadding.bottom())).contains(local); - int selected = -2; - int actionSel = -1; - if (in) { + auto selected = -1; + auto actionSel = -1; + auto inDragArea = false; + if (in && !_rows.isEmpty()) { selected = floorclamp(local.y() - _itemsTop, _rowHeight, 0, _rows.size() - 1); - - if (_section == Section::Installed) { - int remw = _rows.at(selected)->recent ? _clearWidth : (_rows.at(selected)->disabled ? (_rows.at(selected)->official ? _restoreWidth : _returnWidth) : _removeWidth); - QRect rem(myrtlrect(width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - remw, st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, remw, st::normalFont->height)); - actionSel = rem.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; - } else if (_rows.at(selected)->installed && !_rows.at(selected)->disabled) { - actionSel = -1; + local.setY(local.y() - _itemsTop - selected * _rowHeight); + auto set = _rows[selected]; + if (_section == Section::Installed || !set->installed || set->archived || set->removed) { + auto removeButton = (_section == Section::Installed && !set->removed); + auto rect = myrtlrect(relativeButtonRect(removeButton)); + actionSel = rect.contains(local) ? selected : -1; } else { - auto relativeAdd = relativeAddButtonRect(); - auto add = myrtlrect(relativeAdd); - actionSel = add.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; + actionSel = -1; + } + if (_section == Section::Installed && !set->isRecentSet()) { + auto dragAreaWidth = st::contactsPadding.left() + st::stickersReorderIcon.width() + st::stickersReorderSkip; + auto dragArea = myrtlrect(0, 0, dragAreaWidth, _rowHeight); + inDragArea = dragArea.contains(local); } - } else if (_hasFeaturedButton && QRect(0, st::membersPadding.top(), width(), _buttonHeight).contains(local)) { - selected = -2; - } else if (_hasArchivedButton && QRect(0, st::membersPadding.top() + (_hasFeaturedButton ? _buttonHeight : 0), width(), _buttonHeight).contains(local)) { - selected = -1; } else { - selected = -3; + selected = -1; } if (_selected != selected) { - if (((_selected == -1) != (selected == -1)) || ((_selected == -2) != (selected == -2))) { - update(); - } if (_section != Section::Installed && ((_selected >= 0 || _pressed >= 0) != (selected >= 0 || _pressed >= 0))) { - setCursor((selected >= 0 || _pressed >= 0) ? style::cur_pointer : style::cur_default); + if (!inDragArea) { + setCursor((selected >= 0 || _pressed >= 0) ? style::cur_pointer : style::cur_default); + } } _selected = selected; } + if (_inDragArea != inDragArea) { + _inDragArea = inDragArea; + setCursor(_inDragArea ? style::cur_sizeall : (_selected >= 0 || _pressed >= 0) ? style::cur_pointer : style::cur_default); + } setActionSel(actionSel); emit noDraggingScroll(); } } -void StickersBox::Inner::onClearRecent() { - if (_clearBox) { - _clearBox->onClose(); - } - - auto &sets = Global::RefStickerSets(); - bool removedCloud = (sets.remove(Stickers::CloudRecentSetId) != 0); - bool removedCustom = (sets.remove(Stickers::CustomSetId) != 0); - - auto &recent = cGetRecentStickers(); - if (!recent.isEmpty()) { - recent.clear(); - Local::writeUserSettings(); - } - - if (removedCustom) Local::writeInstalledStickers(); - if (removedCloud) Local::writeRecentStickers(); - emit App::main()->updateStickers(); - rebuild(); - - MTPmessages_ClearRecentStickers::Flags flags = 0; - MTP::send(MTPmessages_ClearRecentStickers(MTP_flags(flags))); -} - -void StickersBox::Inner::onClearBoxDestroyed(QObject *box) { - if (box == _clearBox) { - _clearBox = nullptr; - } -} - float64 StickersBox::Inner::aboveShadowOpacity() const { if (_above < 0) return 0; @@ -855,53 +956,20 @@ float64 StickersBox::Inner::aboveShadowOpacity() const { return qMin((dx + dy) * 2. / _rowHeight, 1.); } -void StickersBox::Inner::setPressed(int newPressed) { - if (auto ripple = (_pressed == -1) ? &_archivedRipple : (_pressed == -2) ? &_featuredRipple : nullptr) { - if (*ripple) { - (*ripple)->lastStop(); - } - } - _pressed = newPressed; - if (auto ripple = (_pressed == -1) ? &_archivedRipple : (_pressed == -2) ? &_featuredRipple : nullptr) { - if (!*ripple) { - auto mask = Ui::RippleAnimation::rectMask(QSize(width(), _buttonHeight)); - auto updateCallback = [this, index = _pressed] { - auto y = st::membersPadding.top(); - if (index == -1 && _hasFeaturedButton) { - y += _buttonHeight; - } - update(0, y, width(), _buttonHeight); - }; - *ripple = std_::make_unique(st::defaultRippleAnimation, std_::move(mask), std_::move(updateCallback)); - } - (*ripple)->add(mapFromGlobal(QCursor::pos())); - } -} - void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { - auto pressed = _pressed; - setPressed(-3); + auto pressed = base::take(_pressed, -1); if (_section != Section::Installed && _selected < 0 && pressed >= 0) { setCursor(style::cur_default); } - if (_saving) return; - _mouse = e->globalPos(); onUpdateSelected(); if (_actionDown == _actionSel && _actionSel >= 0) { if (_section == Section::Installed) { - if (_rows[_actionDown]->recent) { - _clearBox = new ConfirmBox(lang(lng_stickers_clear_recent_sure), lang(lng_stickers_clear_recent)); - connect(_clearBox, SIGNAL(confirmed()), this, SLOT(onClearRecent())); - connect(_clearBox, SIGNAL(destroyed(QObject*)), this, SLOT(onClearBoxDestroyed(QObject*))); - Ui::showLayer(_clearBox, KeepOtherLayers); - } else { - _rows[_actionDown]->disabled = !_rows[_actionDown]->disabled; - } - } else { - installSet(_rows[_actionDown]->id); + setRowRemoved(_actionDown, !_rows[_actionDown]->removed); + } else if (_installSetCallback) { + _installSetCallback(_rows[_actionDown]->id); } } else if (_dragging >= 0) { QPoint local(mapFromGlobal(_mouse)); @@ -914,17 +982,11 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { _dragging = _started = -1; } else if (pressed == _selected && _actionSel < 0 && _actionDown < 0) { - if (_selected == -2) { - _selected = -3; - Ui::showLayer(new StickersBox(Section::Featured), KeepOtherLayers); - } else if (_selected == -1) { - _selected = -3; - Ui::showLayer(new StickersBox(Section::Archived), KeepOtherLayers); - } else if (_selected >= 0 && _section != Section::Installed) { + if (_selected >= 0 && !_inDragArea) { auto &sets = Global::RefStickerSets(); auto it = sets.find(_rows.at(pressed)->id); if (it != sets.cend()) { - _selected = -3; + _selected = -1; Ui::showLayer(new StickerSetBox(Stickers::inputSetId(*it)), KeepOtherLayers); } } @@ -932,44 +994,21 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { setActionDown(-1); } +void StickersBox::Inner::setRowRemoved(int index, bool removed) { + auto row = _rows[index]; + if (row->removed != removed) { + row->removed = removed; + row->ripple.reset(); + update(0, _itemsTop + index * _rowHeight, width(), _rowHeight); + onUpdateSelected(); + } +} + void StickersBox::Inner::leaveEvent(QEvent *e) { _mouse = QPoint(-1, -1); onUpdateSelected(); } -void StickersBox::Inner::installSet(uint64 setId) { - auto &sets = Global::RefStickerSets(); - auto it = sets.find(setId); - if (it == sets.cend()) { - rebuild(); - return; - } - - MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_boolFalse()), rpcDone(&Inner::installDone), rpcFail(&Inner::installFail, setId)); - - Stickers::installLocally(setId); -} - -void StickersBox::Inner::installDone(const MTPmessages_StickerSetInstallResult &result) { - if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { - Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); - } -} - -bool StickersBox::Inner::installFail(uint64 setId, const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return false; - - auto &sets = Global::RefStickerSets(); - auto it = sets.find(setId); - if (it == sets.cend()) { - rebuild(); - return true; - } - - Stickers::undoInstallLocally(setId); - return true; -} - void StickersBox::Inner::step_shifting(uint64 ms, bool timer) { bool animating = false; int32 updateMin = -1, updateMax = 0; @@ -1023,8 +1062,8 @@ void StickersBox::Inner::clear() { _aboveShadowFadeOpacity = anim::fvalue(0, 0); _a_shifting.stop(); _above = _dragging = _started = -1; - _selected = -3; - _pressed = -3; + _selected = -1; + _pressed = -1; _actionDown = -1; setActionSel(-1); update(); @@ -1042,22 +1081,7 @@ void StickersBox::Inner::setActionSel(int32 actionSel) { } void StickersBox::Inner::rebuild() { - _hasFeaturedButton = _hasArchivedButton = false; _itemsTop = st::membersPadding.top(); - _buttonHeight = st::stickersFeaturedHeight; - if (_section == Section::Installed) { - if (!Global::FeaturedStickerSetsOrder().isEmpty()) { - _itemsTop += _buttonHeight; - _hasFeaturedButton = true; - } - if (!Global::ArchivedStickerSetsOrder().isEmpty()) { - _itemsTop += _buttonHeight; - _hasArchivedButton = true; - } - if (_itemsTop > st::membersPadding.top()) { - _itemsTop += st::membersPadding.top(); - } - } int maxNameWidth = countMaxNameWidth(); @@ -1119,9 +1143,16 @@ void StickersBox::Inner::updateRows() { row->pixh = pixh; } } - fillSetFlags(set, &row->recent, &row->installed, &row->official, &row->unread, &row->disabled); - if (_section == Section::Installed) { - row->disabled = false; + if (!row->isRecentSet()) { + auto wasInstalled = row->installed; + auto wasArchived = row->archived; + fillSetFlags(set, &row->installed, &row->official, &row->unread, &row->archived); + if (_section == Section::Installed) { + row->archived = false; + } + if (row->installed != wasInstalled || row->archived != wasArchived) { + row->ripple.reset(); + } } row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth); row->count = fillSetCount(set); @@ -1144,18 +1175,22 @@ int StickersBox::Inner::countMaxNameWidth() const { int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); int namew = st::boxWideWidth - namex - st::contactsPadding.right() - st::contactsCheckPosition.x(); if (_section == Section::Installed) { - namew -= qMax(qMax(qMax(_returnWidth, _removeWidth), _restoreWidth), _clearWidth); + namew -= _undoWidth - st::stickersUndoRemove.width; } else { - namew -= st::stickersAddIcon.width() - st::defaultActiveButton.width; - namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip; + namew -= _addWidth - st::stickersTrendingAdd.width; + if (_section == Section::Featured) { + namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip; + } } return namew; } void StickersBox::Inner::rebuildAppendSet(const Stickers::Set &set, int maxNameWidth) { - bool recent = false, installed = false, official = false, unread = false, disabled = false; - fillSetFlags(set, &recent, &installed, &official, &unread, &disabled); - if (_section == Section::Installed && disabled) { + bool installed = true, official = true, unread = false, archived = false, removed = false; + if (set.id != Stickers::CloudRecentSetId) { + fillSetFlags(set, &installed, &official, &unread, &archived); + } + if (_section == Section::Installed && archived) { return; } @@ -1167,7 +1202,7 @@ void StickersBox::Inner::rebuildAppendSet(const Stickers::Set &set, int maxNameW QString title = fillSetTitle(set, maxNameWidth, &titleWidth); int count = fillSetCount(set); - _rows.push_back(new StickerSetRow(set.id, sticker, count, title, titleWidth, installed, official, unread, disabled, recent, pixw, pixh)); + _rows.push_back(new Row(set.id, sticker, count, title, titleWidth, installed, official, unread, archived, removed, pixw, pixh)); _animStartTimes.push_back(0); } @@ -1228,43 +1263,75 @@ QString StickersBox::Inner::fillSetTitle(const Stickers::Set &set, int maxNameWi return result; } -void StickersBox::Inner::fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outDisabled) { - *outRecent = (set.id == Stickers::CloudRecentSetId); - *outInstalled = true; - *outOfficial = true; - *outUnread = false; - *outDisabled = false; - if (!*outRecent) { - *outInstalled = (set.flags & MTPDstickerSet::Flag::f_installed); - *outOfficial = (set.flags & MTPDstickerSet::Flag::f_official); - *outDisabled = (set.flags & MTPDstickerSet::Flag::f_archived); - if (_section == Section::Featured) { - *outUnread = (set.flags & MTPDstickerSet_ClientFlag::f_unread); +void StickersBox::Inner::fillSetFlags(const Stickers::Set &set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived) { + *outInstalled = (set.flags & MTPDstickerSet::Flag::f_installed); + *outOfficial = (set.flags & MTPDstickerSet::Flag::f_official); + *outArchived = (set.flags & MTPDstickerSet::Flag::f_archived); + if (_section == Section::Featured) { + *outUnread = (set.flags & MTPDstickerSet_ClientFlag::f_unread); + } else { + *outUnread = false; + } +} + +template +Stickers::Order StickersBox::Inner::collectSets(Check check) const { + Stickers::Order result; + result.reserve(_rows.size()); + for_const (auto row, _rows) { + if (check(row)) { + result.push_back(row->id); } } + return result; } Stickers::Order StickersBox::Inner::getOrder() const { - Stickers::Order result; - result.reserve(_rows.size()); - for (int32 i = 0, l = _rows.size(); i < l; ++i) { - if (_rows.at(i)->disabled || _rows.at(i)->recent) { - continue; - } - result.push_back(_rows.at(i)->id); - } - return result; + return collectSets([](Row *row) { + return !row->archived && !row->removed && !row->isRecentSet(); + }); } -Stickers::Order StickersBox::Inner::getDisabledSets() const { - Stickers::Order result; - result.reserve(_rows.size()); - for (int32 i = 0, l = _rows.size(); i < l; ++i) { - if (_rows.at(i)->disabled) { - result.push_back(_rows.at(i)->id); +Stickers::Order StickersBox::Inner::getFullOrder() const { + return collectSets([](Row *row) { + return !row->isRecentSet(); + }); +} + +Stickers::Order StickersBox::Inner::getRemovedSets() const { + return collectSets([](Row *row) { + return row->removed; + }); +} + +int StickersBox::Inner::getRowIndex(uint64 setId) const { + for (auto i = 0, count = _rows.size(); i != count; ++i) { + auto row = _rows[i]; + if (row->id == setId) { + return i; } } - return result; + return -1; +} + +void StickersBox::Inner::setFullOrder(const Stickers::Order &order) { + for_const (auto setId, order) { + auto index = getRowIndex(setId); + if (index >= 0) { + auto row = _rows[index]; + auto count = _rows.size(); + for (auto i = index + 1; i != count; ++i) { + _rows[i - 1] = _rows[i]; + } + _rows[count - 1] = row; + } + } +} + +void StickersBox::Inner::setRemovedSets(const Stickers::Order &removed) { + for (auto i = 0, count = _rows.size(); i != count; ++i) { + setRowRemoved(i, removed.contains(_rows[i]->id)); + } } void StickersBox::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) { diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h index a63a5b57d8..1e60821eb7 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.h +++ b/Telegram/SourceFiles/boxes/stickers_box.h @@ -24,10 +24,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class ConfirmBox; +namespace style { +struct RippleAnimation; +} // namespace style + namespace Ui { class PlainShadow; class RoundButton; class RippleAnimation; +class SettingsSlider; } // namespace Ui class StickersBox : public ItemListBox, public RPCSender { @@ -43,8 +48,6 @@ public: StickersBox(Section section = Section::Installed); StickersBox(const Stickers::Order &archivedIds); - ~StickersBox(); - public slots: void onStickersUpdated(); @@ -52,8 +55,6 @@ public slots: void onNoDraggingScroll(); void onScrollTimer(); - void onSave(); - private slots: void onScroll(); @@ -64,31 +65,56 @@ protected: void closePressed() override; private: + void refreshTabs(); void setup(); - int32 countHeight() const; void rebuildList(); + void updateTabsGeometry(); + void switchTab(); + void installSet(uint64 setId); - void disenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req); - bool disenableFail(const RPCError &error, mtpRequestId req); - void reorderDone(const MTPBool &result); - bool reorderFail(const RPCError &result); - void saveOrder(); + QPixmap grabContentCache(); + + void installDone(const MTPmessages_StickerSetInstallResult &result); + bool installFail(uint64 setId, const RPCError &error); void updateVisibleTopBottom(); + void preloadArchivedSets(); + void requestArchivedSets(); void checkLoadMoreArchived(); void getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result); + ChildWidget _topShadow; + ChildWidget _tabs = { nullptr }; + QList
_tabIndices; + + class CounterWidget; + ChildWidget _unreadBadge = { nullptr }; + Section _section; class Inner; - ChildWidget _inner; - ChildWidget _save = { nullptr }; - ChildWidget _cancel = { nullptr }; - OrderedSet _disenableRequests; - mtpRequestId _reorderRequest = 0; - ChildWidget _topShadow = { nullptr }; + struct Tab { + Tab() : widget(nullptr) { + } + template + Tab(int index, Args&&... args) : index(index), widget(std_::forward(args)...) { + } + + int index = 0; + ChildWidget widget = { nullptr }; + int scrollTop = 0; + }; + Tab _installed; + Tab _featured; + Tab _archived; + Tab *_tab = nullptr; + ChildWidget _done = { nullptr }; ChildWidget _bottomShadow = { nullptr }; + FloatAnimation _a_slide; + bool _slideLeft = false; + QPixmap _leftCache, _rightCache; + QTimer _scrollTimer; int32 _scrollDelta = 0; @@ -97,14 +123,19 @@ private: int _aboutHeight = 0; mtpRequestId _archivedRequestId = 0; + bool _archivedLoaded = false; bool _allArchivedLoaded = false; + bool _someArchivedLoaded = false; + + Stickers::Order _localOrder; + Stickers::Order _localRemoved; }; -int32 stickerPacksCount(bool includeDisabledOfficial = false); +int stickerPacksCount(bool includeArchivedOfficial = false); // This class is hold in header because it requires Qt preprocessing. -class StickersBox::Inner : public TWidget, public RPCSender, private base::Subscriber { +class StickersBox::Inner : public TWidget, private base::Subscriber { Q_OBJECT public: @@ -116,14 +147,17 @@ public: void updateSize(); void updateRows(); // refresh only pack cover stickers bool appendSet(const Stickers::Set &set); - bool savingStart() { - if (_saving) return false; - _saving = true; - return true; - } Stickers::Order getOrder() const; - Stickers::Order getDisabledSets() const; + Stickers::Order getFullOrder() const; + Stickers::Order getRemovedSets() const; + + void setFullOrder(const Stickers::Order &order); + void setRemovedSets(const Stickers::Order &removed); + + void setInstallSetCallback(base::lambda &&callback) { + _installSetCallback = std_::move(callback); + } void setVisibleScrollbar(int32 width); void setVisibleTopBottom(int visibleTop, int visibleBottom) override; @@ -143,37 +177,34 @@ signals: public slots: void onUpdateSelected(); - void onClearRecent(); - void onClearBoxDestroyed(QObject *box); - -private slots: - void onImageLoaded(); private: + template + Stickers::Order collectSets(Check check) const; + + int getRowIndex(uint64 setId) const; + void setRowRemoved(int index, bool removed); + void setActionDown(int newActionDown); - void setPressed(int newPressed); void setup(); - QRect relativeAddButtonRect() const; - void paintButton(Painter &p, int y, bool selected, std_::unique_ptr &ripple, const QString &text, int badgeCounter) const; + QRect relativeButtonRect(bool removeButton) const; + void ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton); void step_shifting(uint64 ms, bool timer); - void paintRow(Painter &p, int32 index, uint64 ms); + void paintRow(Painter &p, int index, uint64 ms); + void paintFakeButton(Painter &p, int index, uint64 ms); void clear(); void setActionSel(int32 actionSel); float64 aboveShadowOpacity() const; void readVisibleSets(); - void installSet(uint64 setId); - void installDone(const MTPmessages_StickerSetInstallResult &result); - bool installFail(uint64 setId, const RPCError &error); - Section _section; Stickers::Order _archivedIds; int32 _rowHeight; - struct StickerSetRow { - StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, int titleWidth, bool installed, bool official, bool unread, bool disabled, bool recent, int32 pixw, int32 pixh) : id(id) + struct Row { + Row(uint64 id, DocumentData *sticker, int32 count, const QString &title, int titleWidth, bool installed, bool official, bool unread, bool archived, bool removed, int32 pixw, int32 pixh) : id(id) , sticker(sticker) , count(count) , title(title) @@ -181,66 +212,66 @@ private: , installed(installed) , official(official) , unread(unread) - , disabled(disabled) - , recent(recent) + , archived(archived) + , removed(removed) , pixw(pixw) , pixh(pixh) , yadd(0, 0) { } + bool isRecentSet() const { + return (id == Stickers::CloudRecentSetId); + } uint64 id; DocumentData *sticker; int32 count; QString title; int titleWidth; - bool installed, official, unread, disabled, recent; + bool installed, official, unread, archived, removed; int32 pixw, pixh; anim::ivalue yadd; QSharedPointer ripple; }; - using StickerSetRows = QList; + using Rows = QList; void rebuildAppendSet(const Stickers::Set &set, int maxNameWidth); void fillSetCover(const Stickers::Set &set, DocumentData **outSticker, int *outWidth, int *outHeight) const; int fillSetCount(const Stickers::Set &set) const; QString fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const; - void fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outDisabled); + void fillSetFlags(const Stickers::Set &set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived); int countMaxNameWidth() const; - StickerSetRows _rows; + Rows _rows; QList _animStartTimes; uint64 _aboveShadowFadeStart = 0; anim::fvalue _aboveShadowFadeOpacity = { 0., 0. }; Animation _a_shifting; + base::lambda _installSetCallback; + int _visibleTop = 0; int _visibleBottom = 0; int _itemsTop = 0; - bool _saving = false; - int _actionSel = -1; int _actionDown = -1; - int _clearWidth, _removeWidth, _returnWidth, _restoreWidth; - - ConfirmBox *_clearBox = nullptr; + QString _addText; + int _addWidth = 0; + QString _undoText; + int _undoWidth = 0; int _buttonHeight = 0; - bool _hasFeaturedButton = false; - bool _hasArchivedButton = false; QPoint _mouse; - int _selected = -3; // -2 - featured stickers button, -1 - archived stickers button - int _pressed = -2; + bool _inDragArea = false; + int _selected = -1; + int _pressed = -1; QPoint _dragStart; int _started = -1; int _dragging = -1; int _above = -1; - std_::unique_ptr _archivedRipple; - std_::unique_ptr _featuredRipple; - Ui::RectShadow _aboveShadow; int _scrollbar = 0; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 2170c498c5..46b688e8c2 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -100,7 +100,6 @@ dialogsUnlockIconOver: icon {{ "dialogs_unlock", dialogsMenuIconFgOver }}; dialogsFilter: FlatInput(defaultFlatInput) { font: font(fsize); - bgColor: #f2f2f2; phColor: #949494; phFocusColor: #a4a4a4; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index fc3dc862bc..bf1a8dbec8 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -214,13 +214,14 @@ UnreadBadgeStyle::UnreadBadgeStyle() , selected(false) , muted(false) , size(st::dialogsUnreadHeight) +, padding(st::dialogsUnreadPadding) , sizeId(UnreadBadgeInDialogs) , font(st::dialogsUnreadFont) { } void paintUnreadCount(Painter &p, const QString &text, int x, int y, const UnreadBadgeStyle &st, int *outUnreadWidth) { int unreadWidth = st.font->width(text); - int unreadRectWidth = unreadWidth + 2 * st::dialogsUnreadPadding; + int unreadRectWidth = unreadWidth + 2 * st.padding; int unreadRectHeight = st.size; accumulate_max(unreadRectWidth, unreadRectHeight); @@ -237,9 +238,10 @@ void paintUnreadCount(Painter &p, const QString &text, int x, int y, const Unrea paintUnreadBadge(p, QRect(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight), st); + auto textTop = st.textTop ? st.textTop : (unreadRectHeight - st.font->height) / 2; p.setFont(st.font); p.setPen(st.active ? st::dialogsUnreadFgActive : (st.selected ? st::dialogsUnreadFgOver : st::dialogsUnreadFg)); - p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + (unreadRectHeight - st.font->height) / 2 + st.font->ascent, text); + 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) { @@ -282,7 +284,7 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele st.active = active; st.muted = history->mute(); paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); - availableWidth -= unreadWidth + st::dialogsUnreadPadding; + availableWidth -= unreadWidth + st.padding; } if (history->typing.isEmpty() && history->sendActions.isEmpty()) { item->drawInDialog(p, QRect(nameleft, texttop, availableWidth, st::dialogsTextFont->height), active, selected, history->textCachedFor, history->lastItemTextCache); diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h index 4ec145ecd2..37ce9c6dc6 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.h +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -52,7 +52,9 @@ struct UnreadBadgeStyle { bool active; bool selected; bool muted; + int textTop = 0; int size; + int padding; UnreadBadgeSize sizeId; style::font font; }; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 583a4c1fe9..2303eb2376 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -637,6 +637,7 @@ struct Data { uint64 LastRecentStickersUpdate = 0; Stickers::Order FeaturedStickerSetsOrder; int FeaturedStickerSetsUnreadCount = 0; + base::Observable FeaturedStickerSetsUnreadCountChanged; uint64 LastFeaturedStickersUpdate = 0; Stickers::Order ArchivedStickerSetsOrder; @@ -754,6 +755,7 @@ DefineVar(Global, uint64, LastStickersUpdate); DefineVar(Global, uint64, LastRecentStickersUpdate); DefineVar(Global, Stickers::Order, FeaturedStickerSetsOrder); DefineVar(Global, int, FeaturedStickerSetsUnreadCount); +DefineRefVar(Global, base::Observable, FeaturedStickerSetsUnreadCountChanged); DefineVar(Global, uint64, LastFeaturedStickersUpdate); DefineVar(Global, Stickers::Order, ArchivedStickerSetsOrder); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index b95f128f47..17a2af68f4 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -317,6 +317,7 @@ DeclareVar(uint64, LastStickersUpdate); DeclareVar(uint64, LastRecentStickersUpdate); DeclareVar(Stickers::Order, FeaturedStickerSetsOrder); DeclareVar(int, FeaturedStickerSetsUnreadCount); +DeclareRefVar(base::Observable, FeaturedStickerSetsUnreadCountChanged); DeclareVar(uint64, LastFeaturedStickersUpdate); DeclareVar(Stickers::Order, ArchivedStickerSetsOrder); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 2111f7b863..747bcb2acf 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -701,10 +701,10 @@ void HistoryItem::nextItemChanged() { void HistoryItem::recountAttachToPrevious() { bool attach = false; if (auto previous = previousItem()) { - if (!isPost() && !Has() && !Has()) { - attach = !previous->isPost() - && !previous->serviceMsg() - && !previous->isEmpty() + if (!Has() && !Has()) { + attach = !isPost() && !previous->isPost() + && !serviceMsg() && !previous->serviceMsg() + && !isEmpty() && !previous->isEmpty() && previous->from() == from() && (qAbs(previous->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta); } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 41f14aab79..861e502729 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3992,7 +3992,10 @@ void HistoryWidget::featuredStickersGot(const MTPmessages_FeaturedStickers &stic it = sets.erase(it); } } - Global::SetFeaturedStickerSetsUnreadCount(unreadCount); + if (Global::FeaturedStickerSetsUnreadCount() != unreadCount) { + Global::SetFeaturedStickerSetsUnreadCount(unreadCount); + Global::RefFeaturedStickerSetsUnreadCountChanged().notify(); + } if (Local::countFeaturedStickersHash() != d.vhash.v) { LOG(("API Error: received featured stickers hash %1 while counted hash is %2").arg(d.vhash.v).arg(Local::countFeaturedStickersHash())); diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 663f87e5ba..35f5b16768 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -36,7 +36,7 @@ introCountry: countryInput { width: 300px; height: 41px; top: 33px; - bgColor: #f2f2f2; + bgColor: windowBgOver; ptrSize: size(15px, 8px); textMrg: margins(16px, 5px, 16px, 15px); font: defaultInputFont; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index e9f776bc64..c7ccf41e0e 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -3224,7 +3224,7 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr, outOrder->push_front(setId); } } else if (setId == Stickers::CustomSetId) { - setTitle = lang(lng_custom_stickers); + setTitle = qsl("Custom stickers"); setFlags |= qFlags(MTPDstickerSet_ClientFlag::f_special); } else if (setId == Stickers::CloudRecentSetId) { setTitle = lang(lng_recent_stickers); @@ -3404,7 +3404,7 @@ void importOldRecentStickers() { recent.clear(); auto &def = sets.insert(Stickers::DefaultSetId, Stickers::Set(Stickers::DefaultSetId, 0, lang(lng_stickers_default_set), QString(), 0, 0, MTPDstickerSet::Flag::f_official | MTPDstickerSet::Flag::f_installed | MTPDstickerSet_ClientFlag::f_special)).value(); - auto &custom = sets.insert(Stickers::CustomSetId, Stickers::Set(Stickers::CustomSetId, 0, lang(lng_custom_stickers), QString(), 0, 0, MTPDstickerSet::Flag::f_installed | MTPDstickerSet_ClientFlag::f_special)).value(); + auto &custom = sets.insert(Stickers::CustomSetId, Stickers::Set(Stickers::CustomSetId, 0, qsl("Custom stickers"), QString(), 0, 0, MTPDstickerSet::Flag::f_installed | MTPDstickerSet_ClientFlag::f_special)).value(); QMap read; while (!stickers.stream.atEnd()) { @@ -3480,7 +3480,10 @@ void readFeaturedStickers() { ++unreadCount; } } - Global::SetFeaturedStickerSetsUnreadCount(unreadCount); + if (Global::FeaturedStickerSetsUnreadCount() != unreadCount) { + Global::SetFeaturedStickerSetsUnreadCount(unreadCount); + Global::RefFeaturedStickerSetsUnreadCountChanged().notify(); + } } void readRecentStickers() { diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp index 0af858d499..89a694f4a1 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.cpp +++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/player/media_player_cover.h" #include "ui/widgets/labels.h" -#include "ui/widgets/media_slider.h" +#include "ui/widgets/continuous_sliders.h" #include "ui/widgets/buttons.h" #include "media/media_audio.h" #include "media/view/media_clip_playback.h" diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp index 062032e71b..edf91ba27c 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/media_audio.h" #include "ui/widgets/buttons.h" -#include "ui/widgets/media_slider.h" +#include "ui/widgets/continuous_sliders.h" #include "styles/style_media_player.h" #include "styles/style_widgets.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index b443635765..7695ecddef 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/player/media_player_widget.h" #include "ui/widgets/labels.h" -#include "ui/widgets/filled_slider.h" +#include "ui/widgets/continuous_sliders.h" #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" #include "media/media_audio.h" diff --git a/Telegram/SourceFiles/media/view/media_clip_controller.cpp b/Telegram/SourceFiles/media/view/media_clip_controller.cpp index f695c26c33..fe73580ea8 100644 --- a/Telegram/SourceFiles/media/view/media_clip_controller.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_controller.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/view/media_clip_volume_controller.h" #include "styles/style_mediaview.h" #include "ui/widgets/labels.h" -#include "ui/widgets/media_slider.h" +#include "ui/widgets/continuous_sliders.h" #include "ui/effects/widget_fade_wrap.h" #include "ui/widgets/buttons.h" #include "media/media_audio.h" diff --git a/Telegram/SourceFiles/media/view/media_clip_playback.h b/Telegram/SourceFiles/media/view/media_clip_playback.h index 04689f733f..b1f3804e2e 100644 --- a/Telegram/SourceFiles/media/view/media_clip_playback.h +++ b/Telegram/SourceFiles/media/view/media_clip_playback.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/widgets/continuous_slider.h" +#include "ui/widgets/continuous_sliders.h" struct AudioPlaybackState; diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index c53500f9fe..614b4ed654 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -94,7 +94,7 @@ overviewLoader: size(34px, 14px); overviewLoaderPoint: size(4px, 4px); overviewLoaderSkip: 4px; -playlistHoverBg: #f2f2f2; +playlistHoverBg: windowBgOver; playlistPadding: 10px; linksSearchMargin: margins(20px, 20px, 20px, 0px); @@ -121,7 +121,6 @@ overviewLinksChecked: icon { overviewFilter: FlatInput(defaultFlatInput) { font: font(fsize); - bgColor: #f2f2f2; phColor: #949494; phFocusColor: #a4a4a4; icon: fieldSearchIcon; diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp index d16c9af9d8..172d05f958 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp @@ -28,7 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "boxes/confirmbox.h" #include "application.h" -#include "ui/widgets/discrete_slider.h" +#include "ui/widgets/discrete_sliders.h" namespace Settings { namespace { @@ -52,7 +52,7 @@ ScaleWidget::ScaleWidget(QWidget *parent, UserData *self) : BlockWidget(parent, void ScaleWidget::createControls() { style::margins margin(0, 0, 0, st::settingsSmallSkip); - addChildRow(_auto, margin, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), SLOT(onAutoChosen()), (cConfigScale() == dbisAuto)); + addChildRow(_auto, margin, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), SLOT(onAutoChanged()), (cConfigScale() == dbisAuto)); addChildRow(_scale, style::margins(0, 0, 0, 0)); _scale->addSection(scaleLabel(dbisOne)); @@ -63,7 +63,7 @@ void ScaleWidget::createControls() { _scale->setSectionActivatedCallback([this] { scaleChanged(); }); } -void ScaleWidget::onAutoChosen() { +void ScaleWidget::onAutoChanged() { auto newScale = _auto->checked() ? dbisAuto : cEvalScale(cConfigScale()); if (newScale == cScreenScale()) { if (newScale != cScale()) { @@ -81,6 +81,11 @@ void ScaleWidget::onAutoChosen() { } void ScaleWidget::setScale(DBIScale newScale) { + if (_inSetScale) return; + _inSetScale = true; + auto guard = base::scope_guard([this] { _inSetScale = false; }); + + if (newScale == cScreenScale()) newScale = dbisAuto; if (newScale == dbisAuto && !_auto->checked()) { _auto->setChecked(true); } else if (newScale != dbisAuto && _auto->checked()) { @@ -111,9 +116,6 @@ void ScaleWidget::scaleChanged() { case 2: newScale = dbisOneAndHalf; break; case 3: newScale = dbisTwo; break; } - if (newScale == cScreenScale()) { - newScale = dbisAuto; - } setScale(newScale); } diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.h b/Telegram/SourceFiles/settings/settings_scale_widget.h index c75cacf6f7..541339b69d 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.h +++ b/Telegram/SourceFiles/settings/settings_scale_widget.h @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Checkbox; -class DiscreteSlider; +class SettingsSlider; } // namespace Ui namespace Settings { @@ -36,7 +36,7 @@ public: ScaleWidget(QWidget *parent, UserData *self); private slots: - void onAutoChosen(); + void onAutoChanged(); void onRestartNow(); void onCancel(); @@ -46,9 +46,10 @@ private: void setScale(DBIScale newScale); ChildWidget _auto = { nullptr }; - ChildWidget _scale = { nullptr }; + ChildWidget _scale = { nullptr }; DBIScale _newScale = dbisAuto; + bool _inSetScale = false; }; diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index cdddb80f55..a93bc885bb 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -2530,7 +2530,6 @@ void EmojiPan::SlideAnimation::paintFrame(QPainter &p, float64 dt, float64 opaci t_assert(started()); t_assert(dt >= 0.); - auto &transition = anim::easeOutCirc; _frameAlpha = anim::interpolate(1, 256, opacity); auto frameInts = _frameInts + _innerLeft + _innerTop * _frameIntsPerLine; diff --git a/Telegram/SourceFiles/stickers/stickers.cpp b/Telegram/SourceFiles/stickers/stickers.cpp index 0a47e900d6..bd65640165 100644 --- a/Telegram/SourceFiles/stickers/stickers.cpp +++ b/Telegram/SourceFiles/stickers/stickers.cpp @@ -83,6 +83,26 @@ void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { emit App::main()->stickersUpdated(); } +// For testing: Just apply random subset or your sticker sets as archived. +bool applyArchivedResultFake() { + if (rand_value() % 128 < 64) { + return false; + } + auto sets = QVector(); + for (auto &set : Global::RefStickerSets()) { + if ((set.flags & MTPDstickerSet::Flag::f_installed) && !(set.flags & MTPDstickerSet_ClientFlag::f_special)) { + if (rand_value() % 128 < 64) { + auto data = MTP_stickerSet(MTP_flags(set.flags | MTPDstickerSet::Flag::f_archived), MTP_long(set.id), MTP_long(set.access), MTP_string(set.title), MTP_string(set.shortName), MTP_int(set.count), MTP_int(set.hash)); + sets.push_back(MTP_stickerSetCovered(data, MTP_documentEmpty(MTP_long(0)))); + } + } + } + if (sets.size() > 3) sets = sets.mid(0, 3); + auto fakeResult = MTP_messages_stickerSetInstallResultArchive(MTP_vector(sets)); + applyArchivedResult(fakeResult.c_messages_stickerSetInstallResultArchive()); + return true; +} + void installLocally(uint64 setId) { auto &sets = Global::RefStickerSets(); auto it = sets.find(setId); @@ -199,7 +219,10 @@ void FeaturedReader::onReadSets() { if (!wrappedIds.empty()) { MTP::send(MTPmessages_ReadFeaturedStickers(MTP_vector(wrappedIds)), rpcDone(&readFeaturedDone)); - Global::SetFeaturedStickerSetsUnreadCount(count); + if (Global::FeaturedStickerSetsUnreadCount() != count) { + Global::SetFeaturedStickerSetsUnreadCount(count); + Global::RefFeaturedStickerSetsUnreadCountChanged().notify(); + } } } diff --git a/Telegram/SourceFiles/stickers/stickers.h b/Telegram/SourceFiles/stickers/stickers.h index c2449c6af4..a1ceedf9d8 100644 --- a/Telegram/SourceFiles/stickers/stickers.h +++ b/Telegram/SourceFiles/stickers/stickers.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Stickers { void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d); +bool applyArchivedResultFake(); // For testing. void installLocally(uint64 setId); void undoInstallLocally(uint64 setId); void markFeaturedAsRead(uint64 setId); diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 2c732ff78d..e4efb2a694 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -41,27 +41,46 @@ stickersTrendingSubheaderTop: 20px; stickersTrendingAddTop: 3px; stickersTrendingAdd: RoundButton(defaultActiveButton) { - width: -17px; + width: -16px; height: 26px; textTop: 4px; } +stickersRemove: IconButton(defaultIconButton) { + width: 40px; + height: 40px; + + icon: icon {{ "stickers_remove", menuIconFg }}; + iconOver: icon {{ "stickers_remove", menuIconFgOver }}; + + rippleAreaSize: 40px; + rippleAreaPosition: point(0px, 0px); + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } +} +stickersUndoRemove: RoundButton(defaultLightButton) { + width: -16px; + height: 26px; + textTop: 4px; +} +stickersRemoveSkip: 4px; +stickersReorderIcon: icon {{ "stickers_reorder", menuIconFg }}; +stickersReorderSkip: 13px; stickerEmojiSkip: 5px; -stickersAddIcon: icon {{ "stickers_add", #ffffff }}; -stickersAddSize: size(30px, 24px); - -stickersFeaturedHeight: 32px; -stickersFeaturedFont: contactsNameFont; -stickersFeaturedPosition: point(16px, 6px); -stickersFeaturedBadgeFont: semiboldFont; -stickersFeaturedBadgeSize: 21px; +stickersFeaturedBadgeFont: font(12px bold); +stickersFeaturedBadgeSize: 15px; +stickersFeaturedBadgeTextTop: -1px; +stickersFeaturedBadgePadding: 4px; +stickersFeaturedBadgeSkip: 4px; +stickersFeaturedBadgeTop: 9px; stickersFeaturedPen: lightButtonFg; stickersFeaturedUnreadBg: msgFileInBg; stickersFeaturedUnreadSize: 5px; stickersFeaturedUnreadSkip: 5px; stickersFeaturedUnreadTop: 7px; -stickersFeaturedInstalled: icon {{ "mediaview_save_check", lightButtonFg }}; +stickersFeaturedInstalled: icon {{ "send_control_save", lightButtonFg }}; stickersMaxHeight: 440px; stickersPadding: margins(19px, 17px, 19px, 17px); diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 34e7a75c55..64519e6201 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -744,7 +744,7 @@ public: return flags & MTPDchannel::Flag::f_verified; } bool canAddMembers() const { - return amCreator() || amEditor() || (flags & MTPDchannel::Flag::f_democracy); + return amCreator() || amEditor() || (amIn() && (flags & MTPDchannel::Flag::f_democracy)); } bool canEditPhoto() const { return amCreator() || (amEditor() && isMegagroup()); diff --git a/Telegram/SourceFiles/ui/widgets/continuous_slider.cpp b/Telegram/SourceFiles/ui/widgets/continuous_slider.cpp deleted file mode 100644 index 1a3c6082c8..0000000000 --- a/Telegram/SourceFiles/ui/widgets/continuous_slider.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop version of Telegram messaging app, see https://telegram.org - -Telegram Desktop is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -It is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the copyright holders give permission -to link the code of portions of this program with the OpenSSL library. - -Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE -Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org -*/ -#include "stdafx.h" -#include "ui/widgets/continuous_slider.h" - -namespace Ui { -namespace { - -constexpr auto kByWheelFinishedTimeout = 1000; - -} // namespace - -ContinuousSlider::ContinuousSlider(QWidget *parent) : TWidget(parent) -, _a_value(animation(this, &ContinuousSlider::step_value)) { - setCursor(style::cur_pointer); -} - -float64 ContinuousSlider::value() const { - return a_value.current(); -} - -void ContinuousSlider::setDisabled(bool disabled) { - if (_disabled != disabled) { - _disabled = disabled; - setCursor(_disabled ? style::cur_default : style::cur_pointer); - update(); - } -} - -void ContinuousSlider::setMoveByWheel(bool move) { - if (move != moveByWheel()) { - if (move) { - _byWheelFinished = std_::make_unique(); - _byWheelFinished->setTimeoutHandler([this] { - if (_changeFinishedCallback) { - _changeFinishedCallback(getCurrentValue(getms())); - } - }); - } else { - _byWheelFinished.reset(); - } - } -} - -void ContinuousSlider::setValue(float64 value, bool animated) { - if (animated) { - a_value.start(value); - _a_value.start(); - } else { - a_value = anim::fvalue(value, value); - _a_value.stop(); - } - update(); -} - -void ContinuousSlider::setFadeOpacity(float64 opacity) { - _fadeOpacity = opacity; - update(); -} - -void ContinuousSlider::step_value(float64 ms, bool timer) { - float64 dt = ms / (2 * AudioVoiceMsgUpdateView); - if (dt >= 1) { - _a_value.stop(); - a_value.finish(); - } else { - a_value.update(qMin(dt, 1.), anim::linear); - } - if (timer) update(); -} - -void ContinuousSlider::mouseMoveEvent(QMouseEvent *e) { - if (_mouseDown) { - updateDownValueFromPos(e->pos()); - } -} - -float64 ContinuousSlider::computeValue(const QPoint &pos) const { - auto seekRect = myrtlrect(getSeekRect()); - auto result = isHorizontal() ? - (pos.x() - seekRect.x()) / float64(seekRect.width()) : - (1. - (pos.y() - seekRect.y()) / float64(seekRect.height())); - return snap(result, 0., 1.); -} - -void ContinuousSlider::mousePressEvent(QMouseEvent *e) { - _mouseDown = true; - _downValue = computeValue(e->pos()); - update(); - if (_changeProgressCallback) { - _changeProgressCallback(_downValue); - } -} - -void ContinuousSlider::mouseReleaseEvent(QMouseEvent *e) { - if (_mouseDown) { - _mouseDown = false; - if (_changeFinishedCallback) { - _changeFinishedCallback(_downValue); - } - a_value = anim::fvalue(_downValue, _downValue); - _a_value.stop(); - update(); - } -} - -void ContinuousSlider::wheelEvent(QWheelEvent *e) { - if (_mouseDown || !moveByWheel()) { - return; - } -#ifdef OS_MAC_OLD - constexpr auto step = 120; -#else // OS_MAC_OLD - constexpr auto step = static_cast(QWheelEvent::DefaultDeltasPerStep); -#endif // OS_MAC_OLD - constexpr auto coef = 1. / (step * 10.); - - auto deltaX = e->angleDelta().x(), deltaY = e->angleDelta().y(); - if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { - deltaY *= -1; - } else { - deltaX *= -1; - } - auto delta = (qAbs(deltaX) > qAbs(deltaY)) ? deltaX : deltaY; - auto finalValue = snap(a_value.to() + delta * coef, 0., 1.); - setValue(finalValue, false); - if (_changeProgressCallback) { - _changeProgressCallback(finalValue); - } - _byWheelFinished->start(kByWheelFinishedTimeout); -} - -void ContinuousSlider::updateDownValueFromPos(const QPoint &pos) { - _downValue = computeValue(pos); - update(); - if (_changeProgressCallback) { - _changeProgressCallback(_downValue); - } -} - -void ContinuousSlider::enterEvent(QEvent *e) { - setOver(true); -} - -void ContinuousSlider::leaveEvent(QEvent *e) { - setOver(false); -} - -void ContinuousSlider::setOver(bool over) { - if (_over == over) return; - - _over = over; - auto from = _over ? 0. : 1., to = _over ? 1. : 0.; - _a_over.start([this] { update(); }, from, to, getOverDuration()); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp new file mode 100644 index 0000000000..967643ae9d --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp @@ -0,0 +1,293 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "ui/widgets/continuous_sliders.h" + +namespace Ui { +namespace { + +constexpr auto kByWheelFinishedTimeout = 1000; + +} // namespace + +ContinuousSlider::ContinuousSlider(QWidget *parent) : TWidget(parent) +, _a_value(animation(this, &ContinuousSlider::step_value)) { + setCursor(style::cur_pointer); +} + +float64 ContinuousSlider::value() const { + return a_value.current(); +} + +void ContinuousSlider::setDisabled(bool disabled) { + if (_disabled != disabled) { + _disabled = disabled; + setCursor(_disabled ? style::cur_default : style::cur_pointer); + update(); + } +} + +void ContinuousSlider::setMoveByWheel(bool move) { + if (move != moveByWheel()) { + if (move) { + _byWheelFinished = std_::make_unique(); + _byWheelFinished->setTimeoutHandler([this] { + if (_changeFinishedCallback) { + _changeFinishedCallback(getCurrentValue(getms())); + } + }); + } else { + _byWheelFinished.reset(); + } + } +} + +void ContinuousSlider::setValue(float64 value, bool animated) { + if (animated) { + a_value.start(value); + _a_value.start(); + } else { + a_value = anim::fvalue(value, value); + _a_value.stop(); + } + update(); +} + +void ContinuousSlider::setFadeOpacity(float64 opacity) { + _fadeOpacity = opacity; + update(); +} + +void ContinuousSlider::step_value(float64 ms, bool timer) { + float64 dt = ms / (2 * AudioVoiceMsgUpdateView); + if (dt >= 1) { + _a_value.stop(); + a_value.finish(); + } else { + a_value.update(qMin(dt, 1.), anim::linear); + } + if (timer) update(); +} + +void ContinuousSlider::mouseMoveEvent(QMouseEvent *e) { + if (_mouseDown) { + updateDownValueFromPos(e->pos()); + } +} + +float64 ContinuousSlider::computeValue(const QPoint &pos) const { + auto seekRect = myrtlrect(getSeekRect()); + auto result = isHorizontal() ? + (pos.x() - seekRect.x()) / float64(seekRect.width()) : + (1. - (pos.y() - seekRect.y()) / float64(seekRect.height())); + return snap(result, 0., 1.); +} + +void ContinuousSlider::mousePressEvent(QMouseEvent *e) { + _mouseDown = true; + _downValue = computeValue(e->pos()); + update(); + if (_changeProgressCallback) { + _changeProgressCallback(_downValue); + } +} + +void ContinuousSlider::mouseReleaseEvent(QMouseEvent *e) { + if (_mouseDown) { + _mouseDown = false; + if (_changeFinishedCallback) { + _changeFinishedCallback(_downValue); + } + a_value = anim::fvalue(_downValue, _downValue); + _a_value.stop(); + update(); + } +} + +void ContinuousSlider::wheelEvent(QWheelEvent *e) { + if (_mouseDown || !moveByWheel()) { + return; + } +#ifdef OS_MAC_OLD + constexpr auto step = 120; +#else // OS_MAC_OLD + constexpr auto step = static_cast(QWheelEvent::DefaultDeltasPerStep); +#endif // OS_MAC_OLD + constexpr auto coef = 1. / (step * 10.); + + auto deltaX = e->angleDelta().x(), deltaY = e->angleDelta().y(); + if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { + deltaY *= -1; + } else { + deltaX *= -1; + } + auto delta = (qAbs(deltaX) > qAbs(deltaY)) ? deltaX : deltaY; + auto finalValue = snap(a_value.to() + delta * coef, 0., 1.); + setValue(finalValue, false); + if (_changeProgressCallback) { + _changeProgressCallback(finalValue); + } + _byWheelFinished->start(kByWheelFinishedTimeout); +} + +void ContinuousSlider::updateDownValueFromPos(const QPoint &pos) { + _downValue = computeValue(pos); + update(); + if (_changeProgressCallback) { + _changeProgressCallback(_downValue); + } +} + +void ContinuousSlider::enterEvent(QEvent *e) { + setOver(true); +} + +void ContinuousSlider::leaveEvent(QEvent *e) { + setOver(false); +} + +void ContinuousSlider::setOver(bool over) { + if (_over == over) return; + + _over = over; + auto from = _over ? 0. : 1., to = _over ? 1. : 0.; + _a_over.start([this] { update(); }, from, to, getOverDuration()); +} + +FilledSlider::FilledSlider(QWidget *parent, const style::FilledSlider &st) : ContinuousSlider(parent) +, _st(st) { +} + +QRect FilledSlider::getSeekRect() const { + return QRect(0, 0, width(), height()); +} + +float64 FilledSlider::getOverDuration() const { + return _st.duration; +} + +void FilledSlider::paintEvent(QPaintEvent *e) { + Painter p(this); + p.setPen(Qt::NoPen); + p.setRenderHint(QPainter::HighQualityAntialiasing); + + auto masterOpacity = fadeOpacity(); + auto ms = getms(); + auto disabled = isDisabled(); + auto over = getCurrentOverFactor(ms); + auto lineWidth = _st.lineWidth + ((_st.fullWidth - _st.lineWidth) * over); + auto lineWidthRounded = qFloor(lineWidth); + auto lineWidthPartial = lineWidth - lineWidthRounded; + auto seekRect = getSeekRect(); + auto value = getCurrentValue(ms); + auto from = seekRect.x(), mid = qRound(from + value * seekRect.width()), end = from + seekRect.width(); + if (mid > from) { + p.setOpacity(masterOpacity); + p.fillRect(from, height() - lineWidthRounded, (mid - from), lineWidthRounded, disabled ? _st.disabledFg : _st.activeFg); + if (lineWidthPartial > 0.01) { + p.setOpacity(masterOpacity * lineWidthPartial); + p.fillRect(from, height() - lineWidthRounded - 1, (mid - from), 1, disabled ? _st.disabledFg : _st.activeFg); + } + } + if (end > mid && over > 0) { + p.setOpacity(masterOpacity * over); + p.fillRect(mid, height() - lineWidthRounded, (end - mid), lineWidthRounded, _st.inactiveFg); + if (lineWidthPartial > 0.01) { + p.setOpacity(masterOpacity * over * lineWidthPartial); + p.fillRect(mid, height() - lineWidthRounded - 1, (end - mid), 1, _st.inactiveFg); + } + } +} + +MediaSlider::MediaSlider(QWidget *parent, const style::MediaSlider &st) : ContinuousSlider(parent) +, _st(st) { +} + +QRect MediaSlider::getSeekRect() const { + return isHorizontal() + ? QRect(_st.seekSize.width() / 2, 0, width() - _st.seekSize.width(), height()) + : QRect(0, _st.seekSize.height() / 2, width(), height() - _st.seekSize.width()); +} + +float64 MediaSlider::getOverDuration() const { + return _st.duration; +} + +void MediaSlider::paintEvent(QPaintEvent *e) { + Painter p(this); + p.setPen(Qt::NoPen); + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.setOpacity(fadeOpacity()); + + auto horizontal = isHorizontal(); + auto ms = getms(); + auto radius = _st.width / 2; + auto disabled = isDisabled(); + auto over = getCurrentOverFactor(ms); + auto seekRect = getSeekRect(); + auto value = getCurrentValue(ms); + + // invert colors and value for vertical + if (!horizontal) value = 1. - value; + + auto markerFrom = (horizontal ? seekRect.x() : seekRect.y()); + auto markerLength = (horizontal ? seekRect.width() : seekRect.height()); + auto from = _alwaysDisplayMarker ? 0 : markerFrom; + auto length = _alwaysDisplayMarker ? (horizontal ? width() : height()) : markerLength; + auto mid = qRound(from + value * length); + auto end = from + length; + auto activeFg = disabled ? _st.activeFgDisabled : anim::brush(_st.activeFg, _st.activeFgOver, over); + auto inactiveFg = disabled ? _st.inactiveFgDisabled : anim::brush(_st.inactiveFg, _st.inactiveFgOver, over); + if (mid > from) { + auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid); + auto fromRect = horizontal + ? QRect(from, (height() - _st.width) / 2, mid + radius - from, _st.width) + : QRect((width() - _st.width) / 2, from, _st.width, mid + radius - from); + p.setClipRect(fromClipRect); + p.setBrush(horizontal ? activeFg : inactiveFg); + p.drawRoundedRect(fromRect, radius, radius); + } + if (end > mid) { + auto endClipRect = horizontal ? QRect(mid, 0, width() - mid, height()) : QRect(0, mid, width(), height() - mid); + auto endRect = horizontal + ? QRect(mid - radius, (height() - _st.width) / 2, end - (mid - radius), _st.width) + : QRect((width() - _st.width) / 2, mid - radius, _st.width, end - (mid - radius)); + p.setClipRect(endClipRect); + p.setBrush(horizontal ? inactiveFg : activeFg); + p.drawRoundedRect(endRect, radius, radius); + } + auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over); + if (markerSizeRatio > 0) { + auto position = qRound(markerFrom + value * markerLength) - (horizontal ? seekRect.x() : seekRect.y()); + auto seekButton = horizontal + ? QRect(position, (height() - _st.seekSize.height()) / 2, _st.seekSize.width(), _st.seekSize.height()) + : QRect((width() - _st.seekSize.width()) / 2, position, _st.seekSize.width(), _st.seekSize.height()); + auto size = horizontal ? _st.seekSize.width() : _st.seekSize.height(); + auto remove = static_cast(((1. - markerSizeRatio) * size) / 2.); + if (remove * 2 < size) { + p.setClipRect(rect()); + p.setBrush(activeFg); + p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove))); + } + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/continuous_slider.h b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h similarity index 79% rename from Telegram/SourceFiles/ui/widgets/continuous_slider.h rename to Telegram/SourceFiles/ui/widgets/continuous_sliders.h index 0ea00061f2..9e7b1a74f6 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_slider.h +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include "styles/style_widgets.h" + namespace Ui { class ContinuousSlider : public TWidget { @@ -115,4 +117,40 @@ private: }; +class FilledSlider : public ContinuousSlider { +public: + FilledSlider(QWidget *parent, const style::FilledSlider &st); + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + QRect getSeekRect() const override; + float64 getOverDuration() const override; + + const style::FilledSlider &_st; + +}; + +class MediaSlider : public ContinuousSlider { +public: + MediaSlider(QWidget *parent, const style::MediaSlider &st); + + void setAlwaysDisplayMarker(bool alwaysDisplayMarker) { + _alwaysDisplayMarker = alwaysDisplayMarker; + update(); + } + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + QRect getSeekRect() const override; + float64 getOverDuration() const override; + + const style::MediaSlider &_st; + bool _alwaysDisplayMarker = false; + +}; + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/discrete_slider.cpp b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp similarity index 52% rename from Telegram/SourceFiles/ui/widgets/discrete_slider.cpp rename to Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp index b572e5c128..9d79360506 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_slider.cpp +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp @@ -19,14 +19,13 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "ui/widgets/discrete_slider.h" +#include "ui/widgets/discrete_sliders.h" #include "styles/style_widgets.h" namespace Ui { -DiscreteSlider::DiscreteSlider(QWidget *parent) : TWidget(parent) -, _a_left(animation(this, &DiscreteSlider::step_left)) { +DiscreteSlider::DiscreteSlider(QWidget *parent) : TWidget(parent) { setCursor(style::cur_pointer); } @@ -35,54 +34,70 @@ void DiscreteSlider::setSectionActivatedCallback(SectionActivatedCallback &&call } void DiscreteSlider::setActiveSection(int index) { - setSelectedSection(index); if (_activeIndex != index) { _activeIndex = index; if (_callback) { _callback(); } } + setSelectedSection(index); } void DiscreteSlider::setActiveSectionFast(int index) { setActiveSection(index); - a_left.finish(); - _a_left.stop(); + _a_left.finish(); update(); } -void DiscreteSlider::addSection(const QString &label) { - auto section = Section(label); - _sections.push_back(section); +void DiscreteSlider::setSelectOnPress(bool selectOnPress) { + _selectOnPress = selectOnPress; } -void DiscreteSlider::resizeSections(int newWidth) { - auto count = _sections.size(); - if (!count) return; +void DiscreteSlider::addSection(const QString &label) { + _sections.push_back(Section(label, getLabelFont())); + resizeToWidth(width()); +} - auto skips = count - 1; - auto sectionsWidth = newWidth - skips * st::discreteSliderSkip; - auto sectionWidth = sectionsWidth / float64(count); - auto x = 0.; - for (int i = 0; i != count; ++i) { - auto §ion = _sections[i]; - auto skip = i * st::discreteSliderThickness; - section.left = qFloor(x) + skip; - x += sectionWidth; - section.width = qRound(x) - (section.left - skip); +void DiscreteSlider::setSections(const QStringList &labels) { + t_assert(!labels.isEmpty()); + + _sections.clear(); + for_const (auto &label, labels) { + _sections.push_back(Section(label, getLabelFont())); + } + stopAnimation(); + if (_activeIndex >= _sections.size()) { + _activeIndex = 0; + } + if (_selected >= _sections.size()) { + _selected = 0; + } + resizeToWidth(width()); +} + +int DiscreteSlider::getCurrentActiveLeft(uint64 ms) { + return _a_left.current(ms, _sections.isEmpty() ? 0 : _sections[_selected].left); +} + +template +void DiscreteSlider::enumerateSections(Lambda callback) { + for (auto §ion : _sections) { + callback(section); } - a_left = anim::ivalue(_sections[_activeIndex].left, _sections[_activeIndex].left); - _a_left.stop(); } void DiscreteSlider::mousePressEvent(QMouseEvent *e) { - setSelectedSection(getIndexFromPosition(e->pos())); + if (_selectOnPress) { + setSelectedSection(getIndexFromPosition(e->pos())); + } _pressed = true; } void DiscreteSlider::mouseMoveEvent(QMouseEvent *e) { if (!_pressed) return; - setSelectedSection(getIndexFromPosition(e->pos())); + if (_selectOnPress) { + setSelectedSection(getIndexFromPosition(e->pos())); + } } void DiscreteSlider::mouseReleaseEvent(QMouseEvent *e) { @@ -92,50 +107,16 @@ void DiscreteSlider::mouseReleaseEvent(QMouseEvent *e) { } void DiscreteSlider::setSelectedSection(int index) { - if (index < 0) return; + if (index < 0 || index >= _sections.size()) return; if (_selected != index) { + auto from = _sections[_selected].left; _selected = index; - a_left.start(_sections[_selected].left); - _a_left.start(); + auto to = _sections[_selected].left; + _a_left.start([this] { update(); }, from, to, getAnimationDuration()); } } -void DiscreteSlider::paintEvent(QPaintEvent *e) { - Painter p(this); - - int activeLeft = a_left.current(); - - p.setFont(st::discreteSliderLabelFont); - p.setPen(st::discreteSliderLabelFg); - for (int i = 0, count = _sections.size(); i != count; ++i) { - auto §ion = _sections.at(i); - auto from = section.left, tofill = section.width; - if (activeLeft > from) { - auto fill = qMin(tofill, activeLeft - from); - p.fillRect(myrtlrect(from, st::discreteSliderTop, fill, st::discreteSliderThickness), st::discreteSliderInactiveFg); - from += fill; - tofill -= fill; - } - if (activeLeft + section.width > from) { - if (auto fill = qMin(tofill, activeLeft + section.width - from)) { - p.fillRect(myrtlrect(from, st::discreteSliderTop, fill, st::discreteSliderThickness), st::discreteSliderActiveFg); - from += fill; - tofill -= fill; - } - } - if (tofill) { - p.fillRect(myrtlrect(from, st::discreteSliderTop, tofill, st::discreteSliderThickness), st::discreteSliderInactiveFg); - } - p.drawTextLeft(section.left + (section.width - section.labelWidth) / 2, st::discreteSliderLabelTop, width(), section.label, section.labelWidth); - } -} - -int DiscreteSlider::resizeGetHeight(int newWidth) { - resizeSections(newWidth); - return st::discreteSliderHeight; -} - int DiscreteSlider::getIndexFromPosition(QPoint pos) { int count = _sections.size(); for (int i = 0; i != count; ++i) { @@ -146,22 +127,73 @@ int DiscreteSlider::getIndexFromPosition(QPoint pos) { return count - 1; } -void DiscreteSlider::step_left(float64 ms, bool timer) { - auto dt = ms / st::discreteSliderDuration; - if (dt >= 1) { - a_left.finish(); - _a_left.stop(); - } else { - a_left.update(dt, anim::linear); - } - if (timer) { - update(); - } +DiscreteSlider::Section::Section(const QString &label, const style::font &font) +: label(label) +, labelWidth(font->width(label)) { } -DiscreteSlider::Section::Section(const QString &label) -: label(label) -, labelWidth(st::discreteSliderLabelFont->width(label)) { +SettingsSlider::SettingsSlider(QWidget *parent, const style::SettingsSlider &st) : DiscreteSlider(parent) +, _st(st) { +} + +const style::font &SettingsSlider::getLabelFont() const { + return _st.labelFont; +} + +int SettingsSlider::getAnimationDuration() const { + return _st.duration; +} + +void SettingsSlider::resizeSections(int newWidth) { + auto count = getSectionsCount(); + if (!count) return; + + auto sectionsWidth = newWidth - (count - 1) * _st.barSkip; + auto sectionWidth = sectionsWidth / float64(count); + auto skip = 0; + auto x = 0.; + enumerateSections([this, &x, &skip, sectionWidth](Section §ion) { + section.left = qFloor(x) + skip; + x += sectionWidth; + section.width = qRound(x) - (section.left - skip); + skip += _st.barSkip; + }); + stopAnimation(); +} + +int SettingsSlider::resizeGetHeight(int newWidth) { + resizeSections(newWidth); + return _st.height; +} + +void SettingsSlider::paintEvent(QPaintEvent *e) { + Painter p(this); + + auto activeLeft = getCurrentActiveLeft(getms()); + + p.setFont(_st.labelFont); + enumerateSections([this, &p, activeLeft](Section §ion) { + auto from = section.left, tofill = section.width; + if (activeLeft > from) { + auto fill = qMin(tofill, activeLeft - from); + p.fillRect(myrtlrect(from, _st.barTop, fill, _st.barStroke), _st.barFg); + from += fill; + tofill -= fill; + } + auto active = 1. - snap(qAbs(activeLeft - section.left) / float64(section.width), 0., 1.); + if (activeLeft + section.width > from) { + if (auto fill = qMin(tofill, activeLeft + section.width - from)) { + p.fillRect(myrtlrect(from, _st.barTop, fill, _st.barStroke), _st.barFgActive); + from += fill; + tofill -= fill; + } + } + if (tofill) { + p.fillRect(myrtlrect(from, _st.barTop, tofill, _st.barStroke), _st.barFg); + } + p.setPen(anim::pen(_st.labelFg, _st.labelFgActive, active)); + p.drawTextLeft(section.left + (section.width - section.labelWidth) / 2, _st.labelTop, width(), section.label, section.labelWidth); + }); } } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/discrete_slider.h b/Telegram/SourceFiles/ui/widgets/discrete_sliders.h similarity index 67% rename from Telegram/SourceFiles/ui/widgets/discrete_slider.h rename to Telegram/SourceFiles/ui/widgets/discrete_sliders.h index 0e1e783fab..056e3e6e4a 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_slider.h +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include "styles/style_widgets.h" + namespace Ui { class DiscreteSlider : public TWidget { @@ -27,46 +29,80 @@ public: DiscreteSlider(QWidget *parent); void addSection(const QString &label); - + void setSections(const QStringList &labels); int activeSection() const { return _activeIndex; } void setActiveSection(int index); void setActiveSectionFast(int index); + void setSelectOnPress(bool selectOnPress); using SectionActivatedCallback = base::lambda; void setSectionActivatedCallback(SectionActivatedCallback &&callback); protected: - void paintEvent(QPaintEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; - int resizeGetHeight(int newWidth) override; - -private: - void resizeSections(int newWidth); - int getIndexFromPosition(QPoint pos); - void setSelectedSection(int index); - void step_left(float64 ms, bool timer); + int resizeGetHeight(int newWidth) override = 0; struct Section { - Section(const QString &label); + Section(const QString &label, const style::font &font); int left, width; QString label; int labelWidth; }; + + int getCurrentActiveLeft(uint64 ms); + + int getSectionsCount() const { + return _sections.size(); + } + + template + void enumerateSections(Lambda callback); + + void stopAnimation() { + _a_left.finish(); + } + +private: + virtual const style::font &getLabelFont() const = 0; + virtual int getAnimationDuration() const = 0; + + int getIndexFromPosition(QPoint pos); + void setSelectedSection(int index); + QList
_sections; int _activeIndex = 0; + bool _selectOnPress = true; SectionActivatedCallback _callback; bool _pressed = false; int _selected = 0; - anim::ivalue a_left = { 0 }; - Animation _a_left; + FloatAnimation _a_left; + +}; + +class SettingsSlider : public DiscreteSlider { +public: + SettingsSlider(QWidget *parent, const style::SettingsSlider &st = st::defaultSettingsSlider); + +protected: + void paintEvent(QPaintEvent *e) override; + + int resizeGetHeight(int newWidth) override; + +private: + const style::font &getLabelFont() const override; + int getAnimationDuration() const override; + + void resizeSections(int newWidth); + + const style::SettingsSlider &_st; }; diff --git a/Telegram/SourceFiles/ui/widgets/filled_slider.cpp b/Telegram/SourceFiles/ui/widgets/filled_slider.cpp deleted file mode 100644 index c6326c722f..0000000000 --- a/Telegram/SourceFiles/ui/widgets/filled_slider.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop version of Telegram messaging app, see https://telegram.org - -Telegram Desktop is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -It is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the copyright holders give permission -to link the code of portions of this program with the OpenSSL library. - -Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE -Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org -*/ -#include "stdafx.h" -#include "ui/widgets/filled_slider.h" - -#include "styles/style_widgets.h" - -namespace Ui { - -FilledSlider::FilledSlider(QWidget *parent, const style::FilledSlider &st) : ContinuousSlider(parent) -, _st(st) { -} - -QRect FilledSlider::getSeekRect() const { - return QRect(0, 0, width(), height()); -} - -float64 FilledSlider::getOverDuration() const { - return _st.duration; -} - -void FilledSlider::paintEvent(QPaintEvent *e) { - Painter p(this); - p.setPen(Qt::NoPen); - p.setRenderHint(QPainter::HighQualityAntialiasing); - - auto masterOpacity = fadeOpacity(); - auto ms = getms(); - auto disabled = isDisabled(); - auto over = getCurrentOverFactor(ms); - auto lineWidth = _st.lineWidth + ((_st.fullWidth - _st.lineWidth) * over); - auto lineWidthRounded = qFloor(lineWidth); - auto lineWidthPartial = lineWidth - lineWidthRounded; - auto seekRect = getSeekRect(); - auto value = getCurrentValue(ms); - auto from = seekRect.x(), mid = qRound(from + value * seekRect.width()), end = from + seekRect.width(); - if (mid > from) { - p.setOpacity(masterOpacity); - p.fillRect(from, height() - lineWidthRounded, (mid - from), lineWidthRounded, disabled ? _st.disabledFg : _st.activeFg); - if (lineWidthPartial > 0.01) { - p.setOpacity(masterOpacity * lineWidthPartial); - p.fillRect(from, height() - lineWidthRounded - 1, (mid - from), 1, disabled ? _st.disabledFg : _st.activeFg); - } - } - if (end > mid && over > 0) { - p.setOpacity(masterOpacity * over); - p.fillRect(mid, height() - lineWidthRounded, (end - mid), lineWidthRounded, _st.inactiveFg); - if (lineWidthPartial > 0.01) { - p.setOpacity(masterOpacity * over * lineWidthPartial); - p.fillRect(mid, height() - lineWidthRounded - 1, (end - mid), 1, _st.inactiveFg); - } - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/filled_slider.h b/Telegram/SourceFiles/ui/widgets/filled_slider.h deleted file mode 100644 index 87bb6e7ed6..0000000000 --- a/Telegram/SourceFiles/ui/widgets/filled_slider.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop version of Telegram messaging app, see https://telegram.org - -Telegram Desktop is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -It is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the copyright holders give permission -to link the code of portions of this program with the OpenSSL library. - -Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE -Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org -*/ -#pragma once - -#include "ui/widgets/continuous_slider.h" -#include "styles/style_widgets.h" - -namespace Ui { - -class FilledSlider : public ContinuousSlider { -public: - FilledSlider(QWidget *parent, const style::FilledSlider &st); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - QRect getSeekRect() const override; - float64 getOverDuration() const override; - - const style::FilledSlider &_st; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/media_slider.cpp b/Telegram/SourceFiles/ui/widgets/media_slider.cpp deleted file mode 100644 index c3a7000e63..0000000000 --- a/Telegram/SourceFiles/ui/widgets/media_slider.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop version of Telegram messaging app, see https://telegram.org - -Telegram Desktop is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -It is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the copyright holders give permission -to link the code of portions of this program with the OpenSSL library. - -Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE -Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org -*/ -#include "stdafx.h" -#include "ui/widgets/media_slider.h" - -#include "styles/style_widgets.h" - -namespace Ui { - -MediaSlider::MediaSlider(QWidget *parent, const style::MediaSlider &st) : ContinuousSlider(parent) -, _st(st) { -} - -QRect MediaSlider::getSeekRect() const { - return isHorizontal() - ? QRect(_st.seekSize.width() / 2, 0, width() - _st.seekSize.width(), height()) - : QRect(0, _st.seekSize.height() / 2, width(), height() - _st.seekSize.width()); -} - -float64 MediaSlider::getOverDuration() const { - return _st.duration; -} - -void MediaSlider::paintEvent(QPaintEvent *e) { - Painter p(this); - p.setPen(Qt::NoPen); - p.setRenderHint(QPainter::HighQualityAntialiasing); - p.setOpacity(fadeOpacity()); - - auto horizontal = isHorizontal(); - auto ms = getms(); - auto radius = _st.width / 2; - auto disabled = isDisabled(); - auto over = getCurrentOverFactor(ms); - auto seekRect = getSeekRect(); - auto value = getCurrentValue(ms); - - // invert colors and value for vertical - if (!horizontal) value = 1. - value; - - auto markerFrom = (horizontal ? seekRect.x() : seekRect.y()); - auto markerLength = (horizontal ? seekRect.width() : seekRect.height()); - auto from = _alwaysDisplayMarker ? 0 : markerFrom; - auto length = _alwaysDisplayMarker ? (horizontal ? width() : height()) : markerLength; - auto mid = qRound(from + value * length); - auto end = from + length; - auto activeFg = disabled ? _st.activeFgDisabled : anim::brush(_st.activeFg, _st.activeFgOver, over); - auto inactiveFg = disabled ? _st.inactiveFgDisabled : anim::brush(_st.inactiveFg, _st.inactiveFgOver, over); - if (mid > from) { - auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid); - auto fromRect = horizontal - ? QRect(from, (height() - _st.width) / 2, mid + radius - from, _st.width) - : QRect((width() - _st.width) / 2, from, _st.width, mid + radius - from); - p.setClipRect(fromClipRect); - p.setBrush(horizontal ? activeFg : inactiveFg); - p.drawRoundedRect(fromRect, radius, radius); - } - if (end > mid) { - auto endClipRect = horizontal ? QRect(mid, 0, width() - mid, height()) : QRect(0, mid, width(), height() - mid); - auto endRect = horizontal - ? QRect(mid - radius, (height() - _st.width) / 2, end - (mid - radius), _st.width) - : QRect((width() - _st.width) / 2, mid - radius, _st.width, end - (mid - radius)); - p.setClipRect(endClipRect); - p.setBrush(horizontal ? inactiveFg : activeFg); - p.drawRoundedRect(endRect, radius, radius); - } - auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over); - if (markerSizeRatio > 0) { - auto position = qRound(markerFrom + value * markerLength) - (horizontal ? seekRect.x() : seekRect.y()); - auto seekButton = horizontal - ? QRect(position, (height() - _st.seekSize.height()) / 2, _st.seekSize.width(), _st.seekSize.height()) - : QRect((width() - _st.seekSize.width()) / 2, position, _st.seekSize.width(), _st.seekSize.height()); - auto size = horizontal ? _st.seekSize.width() : _st.seekSize.height(); - auto remove = static_cast(((1. - markerSizeRatio) * size) / 2.); - if (remove * 2 < size) { - p.setClipRect(rect()); - p.setBrush(activeFg); - p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove))); - } - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/media_slider.h b/Telegram/SourceFiles/ui/widgets/media_slider.h deleted file mode 100644 index e01e29af56..0000000000 --- a/Telegram/SourceFiles/ui/widgets/media_slider.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop version of Telegram messaging app, see https://telegram.org - -Telegram Desktop is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -It is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the copyright holders give permission -to link the code of portions of this program with the OpenSSL library. - -Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE -Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org -*/ -#pragma once - -#include "ui/widgets/continuous_slider.h" -#include "styles/style_widgets.h" - -namespace Ui { - -class MediaSlider : public ContinuousSlider { -public: - MediaSlider(QWidget *parent, const style::MediaSlider &st); - - void setAlwaysDisplayMarker(bool alwaysDisplayMarker) { - _alwaysDisplayMarker = alwaysDisplayMarker; - update(); - } - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - QRect getSeekRect() const override; - float64 getOverDuration() const override; - - const style::MediaSlider &_st; - bool _alwaysDisplayMarker = false; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index e2dae5388e..5c75e3a209 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -566,7 +566,7 @@ defaultSolidScroll: FlatScroll(defaultFlatScroll) { defaultInputFont: font(17px); defaultFlatInput: FlatInput { textColor: #000000; - bgColor: #f2f2f2; + bgColor: windowBgOver; bgActive: #ffffff; width: 210px; height: 40px; @@ -575,7 +575,7 @@ defaultFlatInput: FlatInput { font: defaultInputFont; borderWidth: 2px; - borderColor: #f2f2f2; + borderColor: windowBgOver; borderActive: #54c3f3; borderError: #ed8080; @@ -724,16 +724,47 @@ widgetFadeDuration: 200; fieldSearchIcon: icon {{ "box_search", #aaaaaa, point(9px, 8px) }}; boxFieldSearchIcon: icon {{ "box_search", #aaaaaa, point(10px, 9px) }}; -discreteSliderHeight: 39px; -discreteSliderTop: 5px; -discreteSliderSkip: 3px; -discreteSliderThickness: 3px; -discreteSliderActiveFg: #4bb5e7; -discreteSliderInactiveFg: #e1eaef; -discreteSliderLabelTop: 17px; -discreteSliderLabelFont: normalFont; -discreteSliderLabelFg: #1485c2; -discreteSliderDuration: 200; +SettingsSlider { + height: pixels; + barTop: pixels; + barSkip: pixels; + barStroke: pixels; + barFg: color; + barFgActive: color; + labelTop: pixels; + labelFont: font; + labelFg: color; + labelFgActive: color; + duration: int; +} + +defaultSettingsSlider: SettingsSlider { + height: 39px; + barTop: 5px; + barSkip: 3px; + barStroke: 3px; + barFg: #e1eaef; + barFgActive: windowBgActive; + labelTop: 17px; + labelFont: semiboldFont; +// labelFont: normalFont; + labelFg: #999999; + labelFgActive: lightButtonFg; +// labelFg: #1485c2; +// labelFgActive: #1485c2; + duration: 150; +} + +defaultTabsSlider: SettingsSlider(defaultSettingsSlider) { + height: 49px; + barTop: 46px; + barSkip: 0px; + barFg: transparent; + labelTop: 16px; + labelFont: semiboldFont; + labelFg: #999999; + labelFgActive: lightButtonFg; +} defaultRoundShadow: Shadow { left: icon {{ "round_shadow_left", windowShadowFg }}; diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 521c8f4154..4747387c0d 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -496,22 +496,18 @@ '<(src_loc)/ui/widgets/buttons.h', '<(src_loc)/ui/widgets/checkbox.cpp', '<(src_loc)/ui/widgets/checkbox.h', - '<(src_loc)/ui/widgets/continuous_slider.cpp', - '<(src_loc)/ui/widgets/continuous_slider.h', - '<(src_loc)/ui/widgets/discrete_slider.cpp', - '<(src_loc)/ui/widgets/discrete_slider.h', + '<(src_loc)/ui/widgets/continuous_sliders.cpp', + '<(src_loc)/ui/widgets/continuous_sliders.h', + '<(src_loc)/ui/widgets/discrete_sliders.cpp', + '<(src_loc)/ui/widgets/discrete_sliders.h', '<(src_loc)/ui/widgets/dropdown_menu.cpp', '<(src_loc)/ui/widgets/dropdown_menu.h', - '<(src_loc)/ui/widgets/filled_slider.cpp', - '<(src_loc)/ui/widgets/filled_slider.h', '<(src_loc)/ui/widgets/inner_dropdown.cpp', '<(src_loc)/ui/widgets/inner_dropdown.h', '<(src_loc)/ui/widgets/input_fields.cpp', '<(src_loc)/ui/widgets/input_fields.h', '<(src_loc)/ui/widgets/labels.cpp', '<(src_loc)/ui/widgets/labels.h', - '<(src_loc)/ui/widgets/media_slider.cpp', - '<(src_loc)/ui/widgets/media_slider.h', '<(src_loc)/ui/widgets/menu.cpp', '<(src_loc)/ui/widgets/menu.h', '<(src_loc)/ui/widgets/multi_select.cpp',