From 8419a56e1049c74b24f048459d41e31611bf2c3f Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 10 Sep 2016 23:54:59 +0300 Subject: [PATCH] Emoji display added to sticker preview. Reading featured sticker sets. Reading featured sticker sets one by one while scrolling through them, only when the row was fully visible and the image was already loaded. --- Telegram/SourceFiles/boxes/boxes.style | 19 --- Telegram/SourceFiles/boxes/stickersetbox.cpp | 111 ++++++++------- Telegram/SourceFiles/boxes/stickersetbox.h | 28 ++-- Telegram/SourceFiles/historywidget.cpp | 4 +- Telegram/SourceFiles/layerwidget.cpp | 64 ++++++++- Telegram/SourceFiles/layerwidget.h | 6 +- Telegram/SourceFiles/localstorage.cpp | 14 ++ Telegram/SourceFiles/mainwidget.cpp | 24 ++-- Telegram/SourceFiles/stickers/emoji_pan.cpp | 140 +++++++++++++++---- Telegram/SourceFiles/stickers/emoji_pan.h | 57 ++++---- Telegram/SourceFiles/stickers/stickers.cpp | 66 ++++++++- Telegram/SourceFiles/stickers/stickers.h | 20 +++ Telegram/SourceFiles/stickers/stickers.style | 21 +++ Telegram/SourceFiles/structs.cpp | 4 +- Telegram/SourceFiles/structs.h | 4 +- 15 files changed, 418 insertions(+), 164 deletions(-) diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 820e9a1e8..119c3414e 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -49,25 +49,6 @@ confirmInviteUserName: flatLabel(labelDefFlat) { } confirmInviteUserNameTop: 227px; -stickersAddIcon: icon { - { "stickers_add", #ffffff }, -}; -stickersAddSize: size(30px, 24px); - -stickersFeaturedHeight: 32px; -stickersFeaturedFont: contactsNameFont; -stickersFeaturedPosition: point(16px, 6px); -stickersFeaturedBadgeFont: semiboldFont; -stickersFeaturedBadgeSize: 21px; -stickersFeaturedPen: contactsNewItemFg; -stickersFeaturedUnreadBg: msgFileInBg; -stickersFeaturedUnreadSize: 5px; -stickersFeaturedUnreadSkip: 5px; -stickersFeaturedUnreadTop: 7px; -stickersFeaturedInstalled: icon { - { "mediaview_save_check", #40ace3 } -}; - confirmPhoneAboutLabel: flatLabel(labelDefFlat) { width: 282px; } diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index f3d50db54..5f094e795 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "dialogs/dialogs_layout.h" #include "styles/style_boxes.h" +#include "styles/style_stickers.h" namespace { @@ -423,7 +424,7 @@ void StickerSetBox::resizeEvent(QResizeEvent *e) { namespace internal { -StickersInner::StickersInner(StickersBox::Section section) : TWidget() +StickersInner::StickersInner(StickersBox::Section section) : ScrolledWidget() , _section(section) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _a_shifting(animation(this, &StickersInner::step_shifting)) @@ -436,7 +437,7 @@ StickersInner::StickersInner(StickersBox::Section section) : TWidget() setup(); } -StickersInner::StickersInner(const Stickers::Order &archivedIds) : TWidget() +StickersInner::StickersInner(const Stickers::Order &archivedIds) : ScrolledWidget() , _section(StickersBox::Section::ArchivedPart) , _archivedIds(archivedIds) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) @@ -451,10 +452,15 @@ StickersInner::StickersInner(const Stickers::Order &archivedIds) : TWidget() } void StickersInner::setup() { - connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); + connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(onImageLoaded())); setMouseTracking(true); } +void StickersInner::onImageLoaded() { + update(); + readVisibleSets(); +} + void StickersInner::paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const { if (selected) { p.fillRect(0, y, width(), _buttonHeight, st::contactsBgOver); @@ -587,18 +593,18 @@ void StickersInner::paintRow(Painter &p, int32 index) { int statusx = namex; int statusy = st::contactsPadding.top() + st::contactsStatusTop; + p.setFont(st::contactsNameFont); + p.setPen(st::black); + p.drawTextLeft(namex, namey, width(), s->title, s->titleWidth); + if (s->unread) { p.setPen(Qt::NoPen); p.setBrush(st::stickersFeaturedUnreadBg); p.setRenderHint(QPainter::HighQualityAntialiasing, true); - p.drawEllipse(rtlrect(namex, namey + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); + p.drawEllipse(rtlrect(namex + s->titleWidth + st::stickersFeaturedUnreadSkip, namey + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); p.setRenderHint(QPainter::HighQualityAntialiasing, false); - namex += st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip; } - p.setFont(st::contactsNameFont); - p.setPen(st::black); - p.drawTextLeft(namex, namey, width(), s->title); p.setFont(st::contactsStatusFont); p.setPen(st::contactsStatusFg); @@ -967,19 +973,6 @@ void StickersInner::rebuild() { } App::api()->requestStickerSets(); updateSize(); - - if (_section == Section::Featured && Global::FeaturedStickerSetsUnreadCount()) { - Global::SetFeaturedStickerSetsUnreadCount(0); - QVector readIds; - readIds.reserve(Global::StickerSets().size()); - for (auto &set : Global::RefStickerSets()) { - if (set.flags & MTPDstickerSet_ClientFlag::f_unread) { - set.flags &= ~MTPDstickerSet_ClientFlag::f_unread; - readIds.push_back(MTP_long(set.id)); - } - } - MTP::send(MTPmessages_ReadFeaturedStickers(MTP_vector(readIds)), rpcDone(&StickersInner::readFeaturedDone), rpcFail(&StickersInner::readFeaturedFail)); - } } void StickersInner::updateSize() { @@ -1007,7 +1000,7 @@ void StickersInner::updateRows() { if (_section == Section::Installed) { row->disabled = false; } - row->title = fillSetTitle(set, maxNameWidth); + row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth); row->count = fillSetCount(set); } } @@ -1031,6 +1024,7 @@ int StickersInner::countMaxNameWidth() const { namew -= qMax(qMax(qMax(_returnWidth, _removeWidth), _restoreWidth), _clearWidth); } else { namew -= st::stickersAddIcon.width() - st::defaultActiveButton.width; + namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip; } return namew; } @@ -1046,10 +1040,11 @@ void StickersInner::rebuildAppendSet(const Stickers::Set &set, int maxNameWidth) int pixw = 0, pixh = 0; fillSetCover(set, &sticker, &pixw, &pixh); - QString title = fillSetTitle(set, maxNameWidth); + int titleWidth = 0; + QString title = fillSetTitle(set, maxNameWidth, &titleWidth); int count = fillSetCount(set); - _rows.push_back(new StickerSetRow(set.id, sticker, count, title, installed, official, unread, disabled, recent, pixw, pixh)); + _rows.push_back(new StickerSetRow(set.id, sticker, count, title, titleWidth, installed, official, unread, disabled, recent, pixw, pixh)); _animStartTimes.push_back(0); } @@ -1097,11 +1092,15 @@ int StickersInner::fillSetCount(const Stickers::Set &set) const { return result + added; } -QString StickersInner::fillSetTitle(const Stickers::Set &set, int maxNameWidth) const { +QString StickersInner::fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const { auto result = set.title; - int32 titleWidth = st::contactsNameFont->width(result); + int titleWidth = st::contactsNameFont->width(result); if (titleWidth > maxNameWidth) { result = st::contactsNameFont->elided(result, maxNameWidth); + titleWidth = st::contactsNameFont->width(result); + } + if (outTitleWidth) { + *outTitleWidth = titleWidth; } return result; } @@ -1117,35 +1116,11 @@ void StickersInner::fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outOfficial = (set.flags & MTPDstickerSet::Flag::f_official); *outDisabled = (set.flags & MTPDstickerSet::Flag::f_archived); if (_section == Section::Featured) { - *outUnread = _unreadSets.contains(set.id); - if (!*outUnread && (set.flags & MTPDstickerSet_ClientFlag::f_unread)) { - *outUnread = true; - _unreadSets.insert(set.id); - } + *outUnread = (set.flags & MTPDstickerSet_ClientFlag::f_unread); } } } -void StickersInner::readFeaturedDone(const MTPBool &result) { - Local::writeFeaturedStickers(); - emit App::main()->stickersUpdated(); -} - -bool StickersInner::readFeaturedFail(const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return false; - - int unreadCount = 0; - for_const (auto &set, Global::StickerSets()) { - if (!(set.flags & MTPDstickerSet::Flag::f_installed)) { - if (set.flags & MTPDstickerSet_ClientFlag::f_unread) { - ++unreadCount; - } - } - } - Global::SetFeaturedStickerSetsUnreadCount(unreadCount); - return true; -} - Stickers::Order StickersInner::getOrder() const { Stickers::Order result; result.reserve(_rows.size()); @@ -1169,6 +1144,32 @@ Stickers::Order StickersInner::getDisabledSets() const { return result; } +void StickersInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { + if (_section == Section::Featured) { + _visibleTop = visibleTop; + _visibleBottom = visibleBottom; + readVisibleSets(); + } +} + +void StickersInner::readVisibleSets() { + auto itemsVisibleTop = _visibleTop - _itemsTop; + auto itemsVisibleBottom = _visibleBottom - _itemsTop; + int rowFrom = floorclamp(itemsVisibleTop, _rowHeight, 0, _rows.size()); + int rowTo = ceilclamp(itemsVisibleBottom, _rowHeight, 0, _rows.size()); + for (int i = rowFrom; i < rowTo; ++i) { + if (!_rows[i]->unread) { + continue; + } + if (i * _rowHeight < itemsVisibleTop || (i + 1) * _rowHeight > itemsVisibleBottom) { + continue; + } + if (!_rows[i]->sticker || _rows[i]->sticker->thumb->loaded() || _rows[i]->sticker->loaded()) { + Stickers::markFeaturedAsRead(_rows[i]->id); + } + } +} + void StickersInner::setVisibleScrollbar(int32 width) { _scrollbar = width; } @@ -1329,9 +1330,16 @@ void StickersBox::setup() { } void StickersBox::onScroll() { + updateVisibleTopBottom(); checkLoadMoreArchived(); } +void StickersBox::updateVisibleTopBottom() { + auto visibleTop = scrollArea()->scrollTop(); + auto visibleBottom = visibleTop + scrollArea()->height(); + _inner->setVisibleTopBottom(visibleTop, visibleBottom); +} + void StickersBox::checkLoadMoreArchived() { if (_section != Section::Archived) return; @@ -1456,6 +1464,7 @@ 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); + updateVisibleTopBottom(); if (_topShadow) { _topShadow->setGeometry(0, st::boxTitleHeight + _aboutHeight, width(), st::lineWidth); } diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h index ef73191b2..99bc272b9 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.h +++ b/Telegram/SourceFiles/boxes/stickersetbox.h @@ -28,7 +28,6 @@ class StickerSetInner : public TWidget, public RPCSender { Q_OBJECT public: - StickerSetInner(const MTPInputStickerSet &set); void mousePressEvent(QMouseEvent *e); @@ -49,16 +48,13 @@ public: ~StickerSetInner(); public slots: - void onPreview(); signals: - void updateButtons(); void installed(uint64 id); private: - int32 stickerFromGlobalPos(const QPoint &p) const; void gotSet(const MTPmessages_StickerSet &set); @@ -169,6 +165,7 @@ private: bool reorderFail(const RPCError &result); void saveOrder(); + void updateVisibleTopBottom(); void checkLoadMoreArchived(); void getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result); @@ -198,7 +195,7 @@ int32 stickerPacksCount(bool includeDisabledOfficial = false); namespace internal { -class StickersInner : public TWidget, public RPCSender { +class StickersInner : public ScrolledWidget, public RPCSender { Q_OBJECT public: @@ -220,6 +217,7 @@ public: Stickers::Order getDisabledSets() const; void setVisibleScrollbar(int32 width); + void setVisibleTopBottom(int visibleTop, int visibleBottom) override; ~StickersInner(); @@ -239,6 +237,9 @@ public slots: void onClearRecent(); void onClearBoxDestroyed(QObject *box); +private slots: + void onImageLoaded(); + private: void setup(); void paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const; @@ -249,21 +250,22 @@ private: 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); - void readFeaturedDone(const MTPBool &result); - bool readFeaturedFail(const RPCError &error); Section _section; Stickers::Order _archivedIds; int32 _rowHeight; struct StickerSetRow { - StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, bool installed, bool official, bool unread, bool disabled, bool recent, int32 pixw, int32 pixh) : id(id) + 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) , sticker(sticker) , count(count) , title(title) + , titleWidth(titleWidth) , installed(installed) , official(official) , unread(unread) @@ -277,6 +279,7 @@ private: DocumentData *sticker; int32 count; QString title; + int titleWidth; bool installed, official, unread, disabled, recent; int32 pixw, pixh; anim::ivalue yadd; @@ -286,7 +289,7 @@ private: 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) 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); int countMaxNameWidth() const; @@ -297,7 +300,9 @@ private: anim::fvalue _aboveShadowFadeOpacity = { 0., 0. }; Animation _a_shifting; - int32 _itemsTop; + int _visibleTop = 0; + int _visibleBottom = 0; + int _itemsTop = 0; bool _saving = false; @@ -312,9 +317,6 @@ private: bool _hasFeaturedButton = false; bool _hasArchivedButton = false; - // Remember all the unread set ids to display unread dots. - OrderedSet _unreadSets; - QPoint _mouse; int _selected = -3; // -2 - featured stickers button, -1 - archived stickers button int _pressed = -2; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index f83fdb289..604c6c204 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3836,14 +3836,14 @@ void HistoryWidget::featuredStickersGot(const MTPmessages_FeaturedStickers &stic _featuredStickersUpdateRequest = 0; if (stickers.type() != mtpc_messages_featuredStickers) return; - auto &d(stickers.c_messages_featuredStickers()); + auto &d = stickers.c_messages_featuredStickers(); OrderedSet unread; for_const (auto &unreadSetId, d.vunread.c_vector().v) { unread.insert(unreadSetId.v); } - auto &d_sets(d.vsets.c_vector().v); + auto &d_sets = d.vsets.c_vector().v; auto &setsOrder = Global::RefFeaturedStickerSetsOrder(); setsOrder.clear(); diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 1d3b2082a..722a387bf 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -27,6 +27,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "mainwidget.h" #include "ui/filedialog.h" +#include "styles/style_stickers.h" + +namespace { + +constexpr int kStickerPreviewEmojiLimit = 10; + +} // namespace void LayerWidget::setInnerFocus() { auto focused = App::wnd()->focusWidget(); @@ -339,6 +346,7 @@ void LayerStackWidget::activateLayer(LayerWidget *l) { startShow(); } else { l->show(); + l->showDone(); if (App::wnd()) App::wnd()->setInnerFocus(); updateLayerBox(); } @@ -426,7 +434,8 @@ LayerStackWidget::~LayerStackWidget() { MediaPreviewWidget::MediaPreviewWidget(QWidget *parent) : TWidget(parent) , a_shown(0, 0) -, _a_shown(animation(this, &MediaPreviewWidget::step_shown)) { +, _a_shown(animation(this, &MediaPreviewWidget::step_shown)) +, _emojiSize(EmojiSizes[EIndex + 1] / cIntRetinaFactor()) { setAttribute(Qt::WA_TransparentForMouseEvents); connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); } @@ -435,8 +444,8 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) { Painter p(this); QRect r(e->rect()); - const QPixmap &draw(currentImage()); - int w = draw.width() / cIntRetinaFactor(), h = draw.height() / cIntRetinaFactor(); + auto &image = currentImage(); + int w = image.width() / cIntRetinaFactor(), h = image.height() / cIntRetinaFactor(); if (_a_shown.animating()) { float64 shown = a_shown.current(); p.setOpacity(shown); @@ -444,7 +453,17 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) { // h = qMax(qRound(h * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(h % 2), 1); } p.fillRect(r, st::stickerPreviewBg); - p.drawPixmap((width() - w) / 2, (height() - h) / 2, draw); + p.drawPixmap((width() - w) / 2, (height() - h) / 2, image); + if (!_emojiList.isEmpty()) { + int emojiCount = _emojiList.size(); + int emojiWidth = emojiCount * _emojiSize + (emojiCount - 1) * st::stickerEmojiSkip; + int emojiLeft = (width() - emojiWidth) / 2; + int esize = _emojiSize * cIntRetinaFactor(); + for_const (auto emoji, _emojiList) { + p.drawPixmapLeft(emojiLeft, (height() - h) / 2 - _emojiSize * 2, width(), App::emojiLarge(), QRect(emoji->x * esize, emoji->y * esize, esize, esize)); + emojiLeft += _emojiSize + st::stickerEmojiSkip; + } + } } void MediaPreviewWidget::resizeEvent(QResizeEvent *e) { @@ -472,6 +491,7 @@ void MediaPreviewWidget::showPreview(DocumentData *document) { startShow(); _photo = nullptr; _document = document; + fillEmojiString(); resetGifAndCache(); } @@ -510,6 +530,42 @@ void MediaPreviewWidget::hidePreview() { resetGifAndCache(); } +void MediaPreviewWidget::fillEmojiString() { + auto getStickerEmojiList = [this](uint64 setId) { + QList result; + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it == sets.cend()) { + return result; + } + for (auto i = it->emoji.cbegin(), e = it->emoji.cend(); i != e; ++i) { + for_const (auto document, *i) { + if (document == _document) { + result.append(i.key()); + if (result.size() >= kStickerPreviewEmojiLimit) { + return result; + } + } + } + } + return result; + }; + + if (auto sticker = _document->sticker()) { + auto &inputSet = sticker->set; + if (inputSet.type() == mtpc_inputStickerSetID) { + _emojiList = getStickerEmojiList(inputSet.c_inputStickerSetID().vid.v); + } else { + _emojiList.clear(); + if (auto emoji = emojiFromText(sticker->alt)) { + _emojiList.append(emoji); + } + } + } else { + _emojiList.clear(); + } +} + void MediaPreviewWidget::resetGifAndCache() { if (_gif) { if (gif()) { diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index ffba58d3f..14d1fbc84 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -133,7 +133,6 @@ class MediaPreviewWidget : public TWidget { Q_OBJECT public: - MediaPreviewWidget(QWidget *parent); void paintEvent(QPaintEvent *e); @@ -148,10 +147,10 @@ public: ~MediaPreviewWidget(); private: - QSize currentDimensions() const; QPixmap currentImage() const; void startShow(); + void fillEmojiString(); void resetGifAndCache(); anim::fvalue a_shown; @@ -163,6 +162,9 @@ private: return (!_gif || _gif == Media::Clip::BadReader) ? false : true; } + int _emojiSize; + QList _emojiList; + void clipCallback(Media::Clip::Notification notification); enum CacheStatus { diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index b662396c9..066004bc0 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -3215,6 +3215,7 @@ namespace Local { it = sets.insert(setId, Stickers::Set(setId, setAccess, setTitle, setShortName, 0, setHash, MTPDstickerSet::Flags(setFlags))); } auto &set = it.value(); + auto inputSet = MTP_inputStickerSetID(MTP_long(set.id), MTP_long(set.access)); if (scnt < 0) { // disabled not loaded set if (!set.count || set.stickers.isEmpty()) { @@ -3240,6 +3241,11 @@ namespace Local { if (fillStickers) { set.stickers.push_back(document); + if (!(set.flags & MTPDstickerSet_ClientFlag::f_special)) { + if (document->sticker()->set.type() != mtpc_inputStickerSetID) { + document->sticker()->set = inputSet; + } + } ++set.count; } } @@ -3287,6 +3293,8 @@ namespace Local { } void writeInstalledStickers() { + if (!Global::started()) return; + _writeStickerSets(_installedStickersKey, [](const Stickers::Set &set) { if (set.id == Stickers::CloudRecentSetId) { // separate file for recent return StickerSetCheckResult::Skip; @@ -3306,6 +3314,8 @@ namespace Local { } void writeFeaturedStickers() { + if (!Global::started()) return; + _writeStickerSets(_featuredStickersKey, [](const Stickers::Set &set) { if (set.id == Stickers::CloudRecentSetId) { // separate file for recent return StickerSetCheckResult::Skip; @@ -3323,6 +3333,8 @@ namespace Local { } void writeRecentStickers() { + if (!Global::started()) return; + _writeStickerSets(_recentStickersKey, [](const Stickers::Set &set) { if (set.id != Stickers::CloudRecentSetId || set.stickers.isEmpty()) { return StickerSetCheckResult::Skip; @@ -3332,6 +3344,8 @@ namespace Local { } void writeArchivedStickers() { + if (!Global::started()) return; + _writeStickerSets(_archivedStickersKey, [](const Stickers::Set &set) { if (!(set.flags & MTPDstickerSet::Flag::f_archived) || set.stickers.isEmpty()) { return StickerSetCheckResult::Skip; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 8feba33bc..cde8d0048 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -4691,15 +4691,18 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { writeArchived = true; } } - - const auto &v(set.vdocuments.c_vector().v); + auto inputSet = MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)); + auto &v = set.vdocuments.c_vector().v; it->stickers.clear(); it->stickers.reserve(v.size()); for (int32 i = 0, l = v.size(); i < l; ++i) { - DocumentData *doc = App::feedDocument(v.at(i)); + auto doc = App::feedDocument(v.at(i)); if (!doc || !doc->sticker()) continue; it->stickers.push_back(doc); + if (doc->sticker()->set.type() != mtpc_inputStickerSetID) { + doc->sticker()->set = inputSet; + } } it->emoji.clear(); auto &packs = set.vpacks.c_vector().v; @@ -4780,16 +4783,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { } break; case mtpc_updateReadFeaturedStickers: { - for (auto &set : Global::RefStickerSets()) { - if (set.flags & MTPDstickerSet_ClientFlag::f_unread) { - set.flags &= ~MTPDstickerSet_ClientFlag::f_unread; - } - } - if (Global::FeaturedStickerSetsUnreadCount()) { - Global::SetFeaturedStickerSetsUnreadCount(0); - Local::writeFeaturedStickers(); - emit stickersUpdated(); - } + // We read some of the featured stickers, perhaps not all of them. + // Here we don't know what featured sticker sets were read, so we + // request all of them once again. + Global::SetLastFeaturedStickersUpdate(0); + App::main()->updateStickers(); } break; ////// Cloud saved GIFs diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index f84cc454b..a39f1e194 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -287,7 +287,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_variants[variant]->x * esize, _variants[variant]->y * esize, esize, esize)); } -EmojiPanInner::EmojiPanInner() : TWidget() +EmojiPanInner::EmojiPanInner() : ScrolledWidget() , _maxHeight(int(st::emojiPanMaxHeight) - st::rbEmoji.height) , _a_selected(animation(this, &EmojiPanInner::step_selected)) { resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); @@ -316,8 +316,9 @@ void EmojiPanInner::setMaxHeight(int32 h) { resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); } -void EmojiPanInner::setScrollTop(int top) { - _top = top; +void EmojiPanInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { + _visibleTop = visibleTop; + _visibleBottom = visibleBottom; } int EmojiPanInner::countHeight() { @@ -519,7 +520,7 @@ void EmojiPanInner::onShowPicker() { int32 size = (c == tab) ? (sel - (sel % EmojiPanPerRow)) : _counts[c], rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); y += st::emojiPanHeader + (rows * st::emojiPanSize.height()); } - y -= _picker.height() - st::buttonRadius + _top; + y -= _picker.height() - st::buttonRadius + _visibleTop; if (y < 0) { y += _picker.height() - st::buttonRadius + st::emojiPanSize.height() - st::buttonRadius; } @@ -788,7 +789,7 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { update(); } -StickerPanInner::StickerPanInner() : TWidget() +StickerPanInner::StickerPanInner() : ScrolledWidget() , _a_selected(animation(this, &StickerPanInner::step_selected)) , _section(cShowingSavedGifs() ? Section::Gifs : Section::Stickers) , _addText(lang(lng_stickers_featured_add).toUpper()) @@ -800,7 +801,7 @@ StickerPanInner::StickerPanInner() : TWidget() setFocusPolicy(Qt::NoFocus); setAttribute(Qt::WA_OpaquePaintEvent); - connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); + connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(onImageLoaded())); connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); _previewTimer.setSingleShot(true); @@ -816,11 +817,47 @@ void StickerPanInner::setMaxHeight(int32 h) { _settings.moveToLeft((st::emojiPanWidth - _settings.width()) / 2, height() / 3); } -void StickerPanInner::setScrollTop(int top) { - if (top == _top) return; +void StickerPanInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { + _visibleBottom = visibleBottom; + if (_visibleTop != visibleTop) { + _visibleTop = visibleTop; + _lastScrolled = getms(); + } + if (_section == Section::Featured) { + readVisibleSets(); + } +} - _lastScrolled = getms(); - _top = top; +void StickerPanInner::readVisibleSets() { + auto itemsVisibleTop = _visibleTop - st::emojiPanHeader; + auto itemsVisibleBottom = _visibleBottom - st::emojiPanHeader; + auto rowHeight = featuredRowHeight(); + int rowFrom = floorclamp(itemsVisibleTop, rowHeight, 0, _featuredSets.size()); + int rowTo = ceilclamp(itemsVisibleBottom, rowHeight, 0, _featuredSets.size()); + for (int i = rowFrom; i < rowTo; ++i) { + auto &set = _featuredSets[i]; + if (!(set.flags & MTPDstickerSet_ClientFlag::f_unread)) { + continue; + } + if (i * rowHeight < itemsVisibleTop || (i + 1) * rowHeight > itemsVisibleBottom) { + continue; + } + int count = qMin(set.pack.size(), static_cast(StickerPanPerRow)); + int loaded = 0; + for (int j = 0; j < count; ++j) { + if (set.pack[j]->thumb->loaded() || set.pack[j]->loaded()) { + ++loaded; + } + } + if (loaded == count) { + Stickers::markFeaturedAsRead(set.id); + } + } +} + +void StickerPanInner::onImageLoaded() { + update(); + readVisibleSets(); } int StickerPanInner::featuredRowHeight() const { @@ -973,6 +1010,9 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) { widthForTitle -= add.width() - (st::featuredStickersAdd.width / 2); } + if (set.flags & MTPDstickerSet_ClientFlag::f_unread) { + widthForTitle -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip; + } auto titleText = set.title; auto titleWidth = st::featuredStickersHeaderFont->width(titleText); @@ -984,6 +1024,15 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) { p.setPen(st::featuredStickersHeaderFg); p.drawTextLeft(st::emojiPanHeaderLeft, y + st::featuredStickersHeaderTop, width(), titleText, titleWidth); + if (set.flags & MTPDstickerSet_ClientFlag::f_unread) { + p.setPen(Qt::NoPen); + p.setBrush(st::stickersFeaturedUnreadBg); + + p.setRenderHint(QPainter::HighQualityAntialiasing, true); + p.drawEllipse(rtlrect(st::emojiPanHeaderLeft + titleWidth + st::stickersFeaturedUnreadSkip, y + st::featuredStickersHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + } + p.setFont(st::featuredStickersSubheaderFont); p.setPen(st::featuredStickersSubheaderFg); p.drawTextLeft(st::emojiPanHeaderLeft, y + st::featuredStickersSubheaderTop, width(), lng_stickers_count(lt_count, size)); @@ -1246,7 +1295,6 @@ bool StickerPanInner::showSectionIcons() const { } void StickerPanInner::clearSelection(bool fast) { - _lastMousePos = mapToGlobal(QPoint(-10, -10)); if (fast) { if (showingInlineItems()) { if (_selected >= 0) { @@ -1287,7 +1335,10 @@ void StickerPanInner::clearSelection(bool fast) { _a_selected.stop(); update(); } else { + auto pos = _lastMousePos; + _lastMousePos = mapToGlobal(QPoint(-10, -10)); updateSelected(); + _lastMousePos = pos; } } @@ -1324,35 +1375,62 @@ void StickerPanInner::hideFinish(bool completely) { } void StickerPanInner::refreshStickers() { - clearSelection(true); + auto stickersShown = (_section == Section::Stickers || _section == Section::Featured); + if (stickersShown) { + clearSelection(true); + } _mySets.clear(); _mySets.reserve(Global::StickerSetsOrder().size() + 1); refreshRecentStickers(false); for_const (auto setId, Global::StickerSetsOrder()) { - appendSet(_mySets, setId); + appendSet(_mySets, setId, AppendSkip::Archived); } _featuredSets.clear(); _featuredSets.reserve(Global::FeaturedStickerSetsOrder().size()); for_const (auto setId, Global::FeaturedStickerSetsOrder()) { - appendSet(_featuredSets, setId); + appendSet(_featuredSets, setId, AppendSkip::Installed); } - if (_section == Section::Stickers) { + if (stickersShown) { int h = countHeight(); if (h != height()) resize(width(), h); - _settings.setVisible(_mySets.isEmpty()); + _settings.setVisible(_section == Section::Stickers && _mySets.isEmpty()); } else { _settings.hide(); } emit refreshIcons(); - updateSelected(); + // Hack: skip over animations to the very end, + // so that currently selected sticker won't get + // blinking background when refreshing stickers. + if (stickersShown) { + updateSelected(); + int sel = _selected, tab = sel / MatrixRowShift, xsel = -1; + if (sel >= 0) { + auto &sets = shownSets(); + if (tab < sets.size() && sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + sets[tab].pack.size()) { + xsel = sel; + sel -= sets[tab].pack.size(); + } + auto i = _animations.find(sel + 1); + if (i != _animations.cend()) { + i.value() = (i.value() >= st::emojiPanDuration) ? (i.value() - st::emojiPanDuration) : 0; + } + if (xsel >= 0) { + auto j = _animations.find(xsel + 1); + if (j != _animations.cend()) { + j.value() = (j.value() >= st::emojiPanDuration) ? (j.value() - st::emojiPanDuration) : 0; + } + } + step_selected(getms(), true); + } + } } bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) { @@ -1790,17 +1868,19 @@ bool StickerPanInner::ui_isInlineItemVisible(const InlineItem *layout) { top += _inlineRows.at(i).height; } - return (top < _top + _maxHeight) && (top + _inlineRows.at(row).items.at(col)->height() > _top); + return (top < _visibleTop + _maxHeight) && (top + _inlineRows[row].items[col]->height() > _visibleTop); } bool StickerPanInner::ui_isInlineItemBeingChosen() { return showingInlineItems(); } -void StickerPanInner::appendSet(Sets &to, uint64 setId) { +void StickerPanInner::appendSet(Sets &to, uint64 setId, AppendSkip skip) { auto &sets = Global::StickerSets(); auto it = sets.constFind(setId); - if (it == sets.cend() || (it->flags & MTPDstickerSet::Flag::f_archived) || it->stickers.isEmpty()) return; + if (it == sets.cend() || it->stickers.isEmpty()) return; + if ((skip == AppendSkip::Archived) && (it->flags & MTPDstickerSet::Flag::f_archived)) return; + if ((skip == AppendSkip::Installed) && (it->flags & MTPDstickerSet::Flag::f_installed) && !(it->flags & MTPDstickerSet::Flag::f_archived)) return; to.push_back(Set(it->id, it->flags, it->title, it->stickers.size() + 1, it->stickers)); } @@ -1879,7 +1959,7 @@ void StickerPanInner::fillIcons(QList &icons) { if (!cSavedGifs().isEmpty()) { icons.push_back(StickerIcon(Stickers::NoneSetId)); } - if (Global::FeaturedStickerSetsUnreadCount()) { + if (Global::FeaturedStickerSetsUnreadCount() && !_featuredSets.isEmpty()) { icons.push_back(StickerIcon(Stickers::FeaturedSetId)); } @@ -1906,7 +1986,7 @@ void StickerPanInner::fillIcons(QList &icons) { } } - if (!Global::FeaturedStickerSetsUnreadCount() && !Global::FeaturedStickerSetsOrder().empty()) { + if (!Global::FeaturedStickerSetsUnreadCount() && !_featuredSets.isEmpty()) { icons.push_back(StickerIcon(Stickers::FeaturedSetId)); } } @@ -2671,14 +2751,14 @@ void EmojiPan::paintEvent(QPaintEvent *e) { p.drawPixmapLeft(x + (st::rbEmoji.width - s.pixw) / 2, _iconsTop + (st::rbEmoji.height - s.pixh) / 2, width(), pix); x += st::rbEmoji.width; } else { - if (selxrel != x) { + if (true || selxrel != x) { p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), getSpecialSetIcon(s.setId, false)); } - if (selxrel < x + st::rbEmoji.width && selxrel > x - st::rbEmoji.width) { - p.setOpacity(1 - (qAbs(selxrel - x) / float64(st::rbEmoji.width))); - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), getSpecialSetIcon(s.setId, true)); - p.setOpacity(1); - } + //if (selxrel < x + st::rbEmoji.width && selxrel > x - st::rbEmoji.width) { + // p.setOpacity(1 - (qAbs(selxrel - x) / float64(st::rbEmoji.width))); + // p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), getSpecialSetIcon(s.setId, true)); + // p.setOpacity(1); + //} if (s.setId == Stickers::FeaturedSetId) { paintFeaturedStickerSetsBadge(p, x); } @@ -3330,7 +3410,7 @@ void EmojiPan::onScrollEmoji() { _noTabUpdate = false; } - e_inner.setScrollTop(st); + e_inner.setVisibleTopBottom(st, st + e_scroll.height()); } void EmojiPan::onScrollStickers() { @@ -3343,7 +3423,7 @@ void EmojiPan::onScrollStickers() { onInlineRequest(); } - s_inner.setScrollTop(st); + s_inner.setVisibleTopBottom(st, st + s_scroll.height()); } void EmojiPan::validateSelectedIcon(bool animated) { diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index ff6d609b5..16c8edc77 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -113,7 +113,7 @@ private: }; class EmojiPanel; -class EmojiPanInner : public TWidget { +class EmojiPanInner : public ScrolledWidget { Q_OBJECT public: @@ -122,13 +122,6 @@ public: void setMaxHeight(int32 h); void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void leaveEvent(QEvent *e) override; - void leaveToChildEvent(QEvent *e, QWidget *child) override; - void enterFromChildEvent(QEvent *e, QWidget *child) override; - void step_selected(uint64 ms, bool timer); void hideFinish(); @@ -140,13 +133,20 @@ public: void refreshRecent(); - void setScrollTop(int top); + void setVisibleTopBottom(int visibleTop, int visibleBottom) override; void fillPanels(QVector &panels); void refreshPanels(QVector &panels); -public slots: +protected: + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; + void enterFromChildEvent(QEvent *e, QWidget *child) override; +public slots: void updateSelected(); void onShowPicker(); @@ -156,7 +156,6 @@ public slots: bool checkPickerHide(); signals: - void selected(EmojiPtr emoji); void switchToStickers(); @@ -168,7 +167,6 @@ signals: void saveConfigDelayed(int32 delay); private: - int32 _maxHeight; int countHeight(); @@ -180,7 +178,9 @@ private: Animations _animations; Animation _a_selected; - int _top = 0, _counts[emojiTabCount]; + int _visibleTop = 0; + int _visibleBottom = 0; + int _counts[emojiTabCount]; QVector _emojis[emojiTabCount]; QVector _hovers[emojiTabCount]; @@ -207,23 +207,15 @@ struct StickerIcon { int pixh = 0; }; -class StickerPanInner : public TWidget { +class StickerPanInner : public ScrolledWidget { Q_OBJECT public: - StickerPanInner(); void setMaxHeight(int32 h); void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void leaveEvent(QEvent *e) override; - void leaveToChildEvent(QEvent *e, QWidget *child) override; - void enterFromChildEvent(QEvent *e, QWidget *child) override; - void step_selected(uint64 ms, bool timer); void hideFinish(bool completely); @@ -247,7 +239,7 @@ public: void fillPanels(QVector &panels); void refreshPanels(QVector &panels); - void setScrollTop(int top); + void setVisibleTopBottom(int visibleTop, int visibleBottom) override; void preloadImages(); uint64 currentSet(int yOffset) const; @@ -264,12 +256,21 @@ public: ~StickerPanInner(); +protected: + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; + void enterFromChildEvent(QEvent *e, QWidget *child) override; + private slots: void updateSelected(); void onSettings(); void onPreview(); void onUpdateInlineItems(); void onSwitchPm(); + void onImageLoaded(); signals: void selected(DocumentData *sticker); @@ -310,6 +311,7 @@ private: return const_cast(this)->shownSets(); } int featuredRowHeight() const; + void readVisibleSets(); bool showingInlineItems() const { // Gifs or Inline results return (_section == Section::Inlines) || (_section == Section::Gifs); @@ -324,7 +326,11 @@ private: void refreshSwitchPmButton(const InlineCacheEntry *entry); - void appendSet(Sets &to, uint64 setId); + enum class AppendSkip { + Archived, + Installed, + }; + void appendSet(Sets &to, uint64 setId, AppendSkip skip); void selectEmoji(EmojiPtr emoji); QRect stickerRect(int tab, int sel); @@ -335,7 +341,8 @@ private: Animations _animations; Animation _a_selected; - int _top = 0; + int _visibleTop = 0; + int _visibleBottom = 0; Sets _mySets; Sets _featuredSets; diff --git a/Telegram/SourceFiles/stickers/stickers.cpp b/Telegram/SourceFiles/stickers/stickers.cpp index 4f5412d62..266ede52d 100644 --- a/Telegram/SourceFiles/stickers/stickers.cpp +++ b/Telegram/SourceFiles/stickers/stickers.cpp @@ -19,15 +19,22 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "lang.h" +#include "stickers.h" #include "boxes/stickersetbox.h" #include "boxes/confirmbox.h" +#include "lang.h" #include "apiwrap.h" #include "localstorage.h" #include "mainwidget.h" namespace Stickers { +namespace { + +constexpr int kReadFeaturedSetsTimeoutMs = 1000; +internal::FeaturedReader *FeaturedReaderInstance = nullptr; + +} // namespace void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { auto &v = d.vsets.c_vector().v; @@ -140,4 +147,61 @@ void undoInstallLocally(uint64 setId) { Ui::showLayer(new InformBox(lang(lng_stickers_not_found)), KeepOtherLayers); } +void markFeaturedAsRead(uint64 setId) { + if (!FeaturedReaderInstance) { + if (auto main = App::main()) { + FeaturedReaderInstance = new internal::FeaturedReader(main); + } else { + return; + } + } + FeaturedReaderInstance->scheduleRead(setId); +} + +namespace internal { + +void readFeaturedDone() { + Local::writeFeaturedStickers(); + if (App::main()) { + emit App::main()->stickersUpdated(); + } +} + +FeaturedReader::FeaturedReader(QObject *parent) : QObject(parent) +, _timer(new QTimer(this)) { + _timer->setSingleShot(true); + connect(_timer, SIGNAL(timeout()), this, SLOT(onReadSets())); +} + +void FeaturedReader::scheduleRead(uint64 setId) { + if (!_setIds.contains(setId)) { + _setIds.insert(setId); + _timer->start(kReadFeaturedSetsTimeoutMs); + } +} + +void FeaturedReader::onReadSets() { + auto &sets = Global::RefStickerSets(); + auto count = Global::FeaturedStickerSetsUnreadCount(); + QVector wrappedIds; + wrappedIds.reserve(_setIds.size()); + for_const (auto setId, _setIds) { + auto it = sets.find(setId); + if (it != sets.cend()) { + it->flags &= ~MTPDstickerSet_ClientFlag::f_unread; + wrappedIds.append(MTP_long(setId)); + if (count) { + --count; + } + } + } + _setIds.clear(); + + if (!wrappedIds.empty()) { + MTP::send(MTPmessages_ReadFeaturedStickers(MTP_vector(wrappedIds)), rpcDone(&readFeaturedDone)); + Global::SetFeaturedStickerSetsUnreadCount(count); + } +} + +} // namespace internal } // namespace Stickers diff --git a/Telegram/SourceFiles/stickers/stickers.h b/Telegram/SourceFiles/stickers/stickers.h index 06df73d47..c2449c6af 100644 --- a/Telegram/SourceFiles/stickers/stickers.h +++ b/Telegram/SourceFiles/stickers/stickers.h @@ -25,5 +25,25 @@ namespace Stickers { void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d); void installLocally(uint64 setId); void undoInstallLocally(uint64 setId); +void markFeaturedAsRead(uint64 setId); +namespace internal { + +class FeaturedReader : public QObject { + Q_OBJECT + +public: + FeaturedReader(QObject *parent); + void scheduleRead(uint64 setId); + +private slots: + void onReadSets(); + +private: + QTimer *_timer; + OrderedSet _setIds; + +}; + +} // namespace internal } // namespace Stickers diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 11168295c..ae74a48f7 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -37,3 +37,24 @@ featuredStickersAdd: RoundButton(defaultActiveButton) { textTop: 4px; downTextTop: 5px; } + +stickerEmojiSkip: 5px; + +stickersAddIcon: icon { + { "stickers_add", #ffffff }, +}; +stickersAddSize: size(30px, 24px); + +stickersFeaturedHeight: 32px; +stickersFeaturedFont: contactsNameFont; +stickersFeaturedPosition: point(16px, 6px); +stickersFeaturedBadgeFont: semiboldFont; +stickersFeaturedBadgeSize: 21px; +stickersFeaturedPen: contactsNewItemFg; +stickersFeaturedUnreadBg: msgFileInBg; +stickersFeaturedUnreadSize: 5px; +stickersFeaturedUnreadSkip: 5px; +stickersFeaturedUnreadTop: 7px; +stickersFeaturedInstalled: icon { + { "mediaview_save_check", #40ace3 } +}; diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index e6098fa03..459c48387 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1155,7 +1155,9 @@ void DocumentData::setattributes(const QVector &attributes } if (sticker()) { sticker()->alt = qs(d.valt); - sticker()->set = d.vstickerset; + if (sticker()->set.type() != mtpc_inputStickerSetID || d.vstickerset.type() == mtpc_inputStickerSetID) { + sticker()->set = d.vstickerset; + } } } break; case mtpc_documentAttributeVideo: { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 3fc4cec2e..f9627501c 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1029,12 +1029,10 @@ struct DocumentAdditionalData { }; struct StickerData : public DocumentAdditionalData { - StickerData() : set(MTP_inputStickerSetEmpty()) { - } ImagePtr img; QString alt; - MTPInputStickerSet set; + MTPInputStickerSet set = MTP_inputStickerSetEmpty(); bool setInstalled() const; StorageImageLocation loc; // doc thumb location