diff --git a/Telegram/Resources/art/sprite.png b/Telegram/Resources/art/sprite.png index 809d0201a..932c1d990 100644 Binary files a/Telegram/Resources/art/sprite.png and b/Telegram/Resources/art/sprite.png differ diff --git a/Telegram/Resources/art/sprite_200x.png b/Telegram/Resources/art/sprite_200x.png index 2c1270a99..dcad41196 100644 Binary files a/Telegram/Resources/art/sprite_200x.png and b/Telegram/Resources/art/sprite_200x.png differ diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 36cd767e9..ca5150dfd 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -1838,6 +1838,20 @@ stickersReorderFg: #777; stickersRowDisabledOpacity: 0.4; stickersRowDuration: 200; +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 } +}; + emojiScroll: flatScroll(solidScroll) { deltat: 48px; } @@ -1861,6 +1875,9 @@ stickersSettings: sprite(140px, 124px, 21px, 22px); savedGifsOver: sprite(329px, 286px, 21px, 22px); savedGifsActive: sprite(350px, 286px, 21px, 22px); +stickersSettingsUnreadSize: 17px; +stickersSettingsUnreadPosition: point(4px, 5px); + emojiPanCategories: #f7f7f7; rbEmoji: flatCheckbox { @@ -2133,7 +2150,9 @@ mvCaptionRadius: 2px; mvCaptionBg: #11111180; mvCaptionFont: font(fsize); -medviewSaveMsgCheck: sprite(311px, 309px, 22px, 18px); +medviewSaveMsgCheck: icon { + { "mediaview_save_check", #ffffff } +}; medviewSaveMsgFont: font(16px); medviewSaveMsgPadding: margins(55px, 19px, 29px, 20px); medviewSaveMsgCheckPos: point(23px, 21px); diff --git a/Telegram/Resources/icons/mediaview_save_check.png b/Telegram/Resources/icons/mediaview_save_check.png new file mode 100644 index 000000000..33be1af78 Binary files /dev/null and b/Telegram/Resources/icons/mediaview_save_check.png differ diff --git a/Telegram/Resources/icons/mediaview_save_check@2x.png b/Telegram/Resources/icons/mediaview_save_check@2x.png new file mode 100644 index 000000000..d9d3bd85b Binary files /dev/null and b/Telegram/Resources/icons/mediaview_save_check@2x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 9f87cedca..789bb5709 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -685,9 +685,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "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" = "Featured Stickers"; "lng_stickers_remove" = "Delete"; "lng_stickers_return" = "Undo"; "lng_stickers_restore" = "Restore"; +"lng_stickers_add" = "Add"; "lng_stickers_count" = "{count:Loading...|# sticker|# stickers}"; "lng_in_dlg_photo" = "Photo"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index e0dc51135..e6c12b071 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -924,10 +924,10 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) _stickerSetRequests.remove(setId); if (result.type() != mtpc_messages_stickerSet) return; - const auto &d(result.c_messages_stickerSet()); + auto &d(result.c_messages_stickerSet()); if (d.vset.type() != mtpc_stickerSet) return; - const auto &s(d.vset.c_stickerSet()); + auto &s(d.vset.c_stickerSet()); auto &sets = Global::RefStickerSets(); auto it = sets.find(setId); @@ -937,7 +937,7 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) it->hash = s.vhash.v; it->shortName = qs(s.vshort_name); it->title = stickerSetTitle(s); - auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded); + auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded); it->flags = s.vflags.v | clientFlags; it->flags &= ~MTPDstickerSet_ClientFlag::f_not_loaded; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 256421f30..094759909 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1979,6 +1979,9 @@ namespace { Global::SetStickerSets(Stickers::Sets()); Global::SetStickerSetsOrder(Stickers::Order()); Global::SetLastStickersUpdate(0); + Global::SetFeaturedStickerSetsOrder(Stickers::Order()); + Global::SetFeaturedStickerSetsUnreadCount(0); + Global::SetLastFeaturedStickersUpdate(0); cSetSavedGifs(SavedGifs()); cSetLastSavedGifsUpdate(0); cSetReportSpamStatuses(ReportSpamStatuses()); diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 0f23423d5..f87cf0a9b 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "apiwrap.h" #include "localstorage.h" +#include "dialogs/dialogs_layout.h" StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : TWidget() , _input(set) { @@ -47,21 +48,21 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) { _pack.clear(); _emoji.clear(); if (set.type() == mtpc_messages_stickerSet) { - const auto &d(set.c_messages_stickerSet()); - const auto &v(d.vdocuments.c_vector().v); + auto &d(set.c_messages_stickerSet()); + auto &v(d.vdocuments.c_vector().v); _pack.reserve(v.size()); - for (int32 i = 0, l = v.size(); i < l; ++i) { - DocumentData *doc = App::feedDocument(v.at(i)); + for (int i = 0, l = v.size(); i < l; ++i) { + auto doc = App::feedDocument(v.at(i)); if (!doc || !doc->sticker()) continue; _pack.push_back(doc); } - const auto &packs(d.vpacks.c_vector().v); - for (int32 i = 0, l = packs.size(); i < l; ++i) { + auto &packs(d.vpacks.c_vector().v); + for (int i = 0, l = packs.size(); i < l; ++i) { if (packs.at(i).type() != mtpc_stickerPack) continue; - const auto &pack(packs.at(i).c_stickerPack()); - if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) { - const auto &stickers(pack.vdocuments.c_vector().v); + auto &pack(packs.at(i).c_stickerPack()); + if (auto e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) { + auto &stickers(pack.vdocuments.c_vector().v); StickerPack p; p.reserve(stickers.size()); for (int32 j = 0, c = stickers.size(); j < c; ++j) { @@ -74,7 +75,7 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) { } } if (d.vset.type() == mtpc_stickerSet) { - const auto &s(d.vset.c_stickerSet()); + auto &s(d.vset.c_stickerSet()); _setTitle = stickerSetTitle(s); _title = st::boxTitleFont->elided(_setTitle, width() - st::boxTitlePosition.x() - st::boxTitleHeight); _setShortName = qs(s.vshort_name); @@ -83,6 +84,15 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) { _setCount = s.vcount.v; _setHash = s.vhash.v; _setFlags = s.vflags.v; + auto &sets = Global::RefStickerSets(); + auto it = sets.find(_setId); + if (it != sets.cend()) { + auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_unread); + _setFlags |= clientFlags; + it->flags = _setFlags; + it->stickers = _pack; + it->emoji = _emoji; + } } } @@ -116,13 +126,13 @@ void StickerSetInner::installDone(const MTPBool &result) { if (it == sets.cend()) { it = sets.insert(_setId, Stickers::Set(_setId, _setAccess, _setTitle, _setShortName, _setCount, _setHash, _setFlags)); } else { - it.value().flags = _setFlags; + it->flags = _setFlags; } - it.value().stickers = _pack; - it.value().emoji = _emoji; + it->stickers = _pack; + it->emoji = _emoji; auto &order = Global::RefStickerSetsOrder(); - int32 insertAtIndex = 0, currentIndex = order.indexOf(_setId); + int insertAtIndex = 0, currentIndex = order.indexOf(_setId); if (currentIndex != insertAtIndex) { if (currentIndex > 0) { order.removeAt(currentIndex); @@ -132,8 +142,8 @@ void StickerSetInner::installDone(const MTPBool &result) { auto custom = sets.find(Stickers::CustomSetId); if (custom != sets.cend()) { - for (int32 i = 0, l = _pack.size(); i < l; ++i) { - int32 removeIndex = custom->stickers.indexOf(_pack.at(i)); + for_const (auto sticker, _pack) { + int removeIndex = custom->stickers.indexOf(sticker); if (removeIndex >= 0) custom->stickers.removeAt(removeIndex); } if (custom->stickers.isEmpty()) { @@ -141,8 +151,8 @@ void StickerSetInner::installDone(const MTPBool &result) { } } Local::writeStickers(); + emit App::main()->stickersUpdated(); emit installed(_setId); - Ui::hideLayer(); } bool StickerSetInner::installFailed(const RPCError &error) { @@ -295,7 +305,7 @@ StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st:: connect(&_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons())); connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); - connect(&_inner, SIGNAL(installed(uint64)), this, SIGNAL(installed(uint64))); + connect(&_inner, SIGNAL(installed(uint64)), this, SLOT(onInstalled(uint64))); onStickersUpdated(); @@ -304,6 +314,11 @@ StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st:: prepare(); } +void StickerSetBox::onInstalled(uint64 setId) { + emit installed(setId); + onClose(); +} + void StickerSetBox::onStickersUpdated() { showAll(); } @@ -392,28 +407,44 @@ void StickerSetBox::resizeEvent(QResizeEvent *e) { } } -StickersInner::StickersInner() : TWidget() +namespace internal { + +StickersInner::StickersInner(StickersBox::Section section) : TWidget() +, _section(section) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _aboveShadowFadeStart(0) -, _aboveShadowFadeOpacity(0, 0) , _a_shifting(animation(this, &StickersInner::step_shifting)) , _itemsTop(st::membersPadding.top()) -, _saving(false) -, _removeSel(-1) -, _removeDown(-1) , _removeWidth(st::normalFont->width(lang(lng_stickers_remove))) , _returnWidth(st::normalFont->width(lang(lng_stickers_return))) , _restoreWidth(st::normalFont->width(lang(lng_stickers_restore))) -, _selected(-1) -, _started(-1) -, _dragging(-1) -, _above(-1) -, _aboveShadow(st::boxShadow) -, _scrollbar(0) { +, _addText(lang(lng_stickers_add).toUpper()) +, _addWidth(st::defaultActiveButton.font->width(_addText)) +, _aboveShadow(st::boxShadow) { connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); setMouseTracking(true); } +void StickersInner::paintFeaturedButton(Painter &p) const { + if (!_featuredHeight) return; + + if (_selected == -1) { + p.fillRect(0, st::membersPadding.top(), width(), _featuredHeight, st::contactsBgOver); + } + p.setFont(st::stickersFeaturedFont); + p.setPen(st::stickersFeaturedPen); + p.drawTextLeft(st::stickersFeaturedPosition.x(), st::membersPadding.top() + st::stickersFeaturedPosition.y(), width(), lang(lng_stickers_featured)); + + if (auto unread = Global::FeaturedStickerSetsUnreadCount()) { + 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 = st::membersPadding.top() + (_featuredHeight - st::stickersFeaturedBadgeSize) / 2; + Dialogs::Layout::paintUnreadCount(p, QString::number(unread), unreadRight, unreadTop, unreadSt); + } +} + void StickersInner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); Painter p(this); @@ -422,10 +453,13 @@ void StickersInner::paintEvent(QPaintEvent *e) { p.fillRect(r, st::white); p.setClipRect(r); + + paintFeaturedButton(p); + if (_rows.isEmpty()) { - p.setFont(st::noContactsFont->f); - p.setPen(st::noContactsColor->p); - p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); + p.setFont(st::noContactsFont); + p.setPen(st::noContactsColor); + p.drawText(QRect(0, _featuredHeight, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); } else { p.translate(0, _itemsTop); @@ -452,33 +486,50 @@ void StickersInner::paintRow(Painter &p, int32 index) { int32 xadd = 0, yadd = s->yadd.current(); if (xadd || yadd) p.translate(xadd, yadd); - bool removeSel = (index == _removeSel && (_removeDown < 0 || index == _removeDown)); - bool removeDown = removeSel && (index == _removeDown); + if (_section == Section::Installed) { + bool removeSel = (index == _actionSel && (_actionDown < 0 || index == _actionDown)); + bool removeDown = removeSel && (index == _actionDown); - p.setFont((removeSel ? st::linkOverFont : st::linkFont)->f); - if (removeDown) { - p.setPen(st::btnDefLink.downColor->p); - } else { - p.setPen(st::btnDefLink.color->p); - } - int32 remWidth = s->disabled ? (s->official ? _restoreWidth : _returnWidth) : _removeWidth; - QString remText = lang(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); - - if (index == _above) { - float64 current = _aboveShadowFadeOpacity.current(); - if (_started >= 0) { - float64 o = aboveShadowOpacity(); - if (o > current) { - _aboveShadowFadeOpacity = anim::fvalue(o, o); - current = o; - } + p.setFont(removeSel ? st::linkOverFont : st::linkFont); + if (removeDown) { + p.setPen(st::btnDefLink.downColor); + } else { + p.setPen(st::btnDefLink.color); } - p.setOpacity(current); - QRect row(myrtlrect(_aboveShadow.getDimensions(st::boxShadowShift).left(), st::contactsPadding.top() / 2, width() - (st::contactsPadding.left() / 2) - _scrollbar - _aboveShadow.getDimensions(st::boxShadowShift).right(), _rowHeight - ((st::contactsPadding.top() + st::contactsPadding.bottom()) / 2))); - _aboveShadow.paint(p, row, st::boxShadowShift); - p.fillRect(row, st::white); - p.setOpacity(1); + int32 remWidth = s->disabled ? (s->official ? _restoreWidth : _returnWidth) : _removeWidth; + QString remText = lang(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); + + if (index == _above) { + float64 current = _aboveShadowFadeOpacity.current(); + if (_started >= 0) { + float64 o = aboveShadowOpacity(); + if (o > current) { + _aboveShadowFadeOpacity = anim::fvalue(o, o); + current = o; + } + } + p.setOpacity(current); + QRect row(myrtlrect(_aboveShadow.getDimensions(st::boxShadowShift).left(), st::contactsPadding.top() / 2, width() - (st::contactsPadding.left() / 2) - _scrollbar - _aboveShadow.getDimensions(st::boxShadowShift).right(), _rowHeight - ((st::contactsPadding.top() + st::contactsPadding.bottom()) / 2))); + _aboveShadow.paint(p, row, st::boxShadowShift); + p.fillRect(row, st::white); + p.setOpacity(1); + } + } else if (s->installed) { + int addw = _addWidth - st::defaultActiveButton.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()); + } else { + int addw = _addWidth - st::defaultActiveButton.width; + int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; + int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::defaultActiveButton.height) / 2; + QRect add(myrtlrect(addx, addy, addw, st::defaultActiveButton.height)); + + App::roundRect(p, add, st::defaultActiveButton.textBgOver); + p.setFont(st::defaultActiveButton.font); + p.setPen(st::defaultActiveButton.textFg); + p.drawTextLeft(addx - st::defaultActiveButton.width / 2, addy + st::defaultActiveButton.textTop, width(), _addText, _addWidth); } if (s->disabled) p.setOpacity(st::stickersRowDisabledOpacity); @@ -487,15 +538,28 @@ void StickersInner::paintRow(Painter &p, int32 index) { 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); } + + int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); + int namey = st::contactsPadding.top() + st::contactsNameTop; + int statusx = namex; + int statusy = st::contactsPadding.top() + st::contactsStatusTop; + + 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.setRenderHint(QPainter::HighQualityAntialiasing, false); + namex += st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip; + } p.setFont(st::contactsNameFont); p.setPen(st::black); - - int32 namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); - p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsNameTop, width(), s->title); + p.drawTextLeft(namex, namey, width(), s->title); p.setFont(st::contactsStatusFont); p.setPen(st::contactsStatusFg); - p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), lng_stickers_count(lt_count, s->count)); + p.drawTextLeft(statusx, statusy, width(), lng_stickers_count(lt_count, s->count)); p.setOpacity(1); if (xadd || yadd) p.translate(-xadd, -yadd); @@ -506,10 +570,12 @@ void StickersInner::mousePressEvent(QMouseEvent *e) { if (_dragging >= 0) mouseReleaseEvent(e); _mouse = e->globalPos(); onUpdateSelected(); - if (_removeSel >= 0) { - _removeDown = _removeSel; - update(0, _itemsTop + _removeSel * _rowHeight, width(), _rowHeight); - } else if (_selected >= 0) { + + _pressed = _selected; + if (_actionSel >= 0) { + _actionDown = _actionSel; + update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); + } else if (_selected >= 0 && _section == Section::Installed) { _above = _dragging = _started = _selected; _dragStart = mapFromGlobal(_mouse); } @@ -557,15 +623,39 @@ void StickersInner::onUpdateSelected() { emit checkDraggingScroll(local.y()); } else { bool in = rect().marginsRemoved(QMargins(0, _itemsTop, 0, st::membersPadding.bottom())).contains(local); - _selected = in ? floorclamp(local.y() - _itemsTop, _rowHeight, 0, _rows.size() - 1) : -1; - int32 removeSel = -1; + int selected = -2; + int actionSel = -1; + if (in) { + selected = floorclamp(local.y() - _itemsTop, _rowHeight, 0, _rows.size() - 1); - if (_selected >= 0) { - int32 remw = _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)); - removeSel = rem.contains(local.x(), local.y() - _itemsTop - _selected * _rowHeight) ? _selected : -1; + if (_section == Section::Installed) { + int remw = _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) { + actionSel = -1; + } else { + int addw = _addWidth - st::defaultActiveButton.width; + int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; + int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::defaultActiveButton.height) / 2; + QRect add(myrtlrect(addx, addy, addw, st::defaultActiveButton.height)); + actionSel = add.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; + } + } else if (_featuredHeight && QRect(0, st::membersPadding.top(), width(), _featuredHeight).contains(local)) { + selected = -1; + } else { + selected = -2; } - setRemoveSel(removeSel); + if (_selected != selected) { + if ((_selected == -1) != (selected == -1)) { + update(); + } + if (_section == Section::Featured && ((_selected >= 0 || _pressed >= 0) != (selected >= 0 || _pressed >= 0))) { + setCursor((selected >= 0 || _pressed >= 0) ? style::cur_pointer : style::cur_default); + } + _selected = selected; + } + setActionSel(actionSel); emit noDraggingScroll(); } } @@ -579,11 +669,23 @@ float64 StickersInner::aboveShadowOpacity() const { } void StickersInner::mouseReleaseEvent(QMouseEvent *e) { + auto pressed = _pressed; + _pressed = -2; + + if (_section == Section::Featured && _selected < 0 && pressed >= 0) { + setCursor(style::cur_default); + } + if (_saving) return; + _mouse = e->globalPos(); onUpdateSelected(); - if (_removeDown == _removeSel && _removeSel >= 0) { - _rows[_removeDown]->disabled = !_rows[_removeDown]->disabled; + if (_actionDown == _actionSel && _actionSel >= 0) { + if (_section == Section::Installed) { + _rows[_actionDown]->disabled = !_rows[_actionDown]->disabled; + } else { + installSet(_rows[_actionDown]->id); + } } else if (_dragging >= 0) { QPoint local(mapFromGlobal(_mouse)); _rows[_dragging]->yadd.start(0); @@ -594,13 +696,66 @@ void StickersInner::mouseReleaseEvent(QMouseEvent *e) { } _dragging = _started = -1; + } else if (pressed == _selected) { + if (_selected == -1) { + _selected = -2; + Ui::showLayer(new StickersBox(Section::Featured), KeepOtherLayers); + } else if (_selected >= 0 && _section == Section::Featured) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(_rows.at(pressed)->id); + if (it != sets.cend()) { + _selected = -2; + Ui::showLayer(new StickerSetBox(Stickers::inputSetId(*it)), KeepOtherLayers); + } + } } - if (_removeDown >= 0) { - update(0, _itemsTop + _removeDown * _rowHeight, width(), _rowHeight); - _removeDown = -1; + if (_actionDown >= 0) { + update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight); + _actionDown = -1; } } +void StickersInner::leaveEvent(QEvent *e) { + _mouse = QPoint(-1, -1); + onUpdateSelected(); +} + +void StickersInner::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())); + + it->flags &= ~(MTPDstickerSet::Flag::f_disabled | MTPDstickerSet_ClientFlag::f_unread); + it->flags |= MTPDstickerSet::Flag::f_installed; + + auto &order = Global::RefStickerSetsOrder(); + int insertAtIndex = 0, currentIndex = order.indexOf(setId); + if (currentIndex != insertAtIndex) { + if (currentIndex > 0) { + order.removeAt(currentIndex); + } + order.insert(insertAtIndex, setId); + } + + auto custom = sets.find(Stickers::CustomSetId); + if (custom != sets.cend()) { + for_const (auto sticker, it->stickers) { + int removeIndex = custom->stickers.indexOf(sticker); + if (removeIndex >= 0) custom->stickers.removeAt(removeIndex); + } + if (custom->stickers.isEmpty()) { + sets.erase(custom); + } + } + Local::writeStickers(); + emit App::main()->stickersUpdated(); +} + void StickersInner::step_shifting(uint64 ms, bool timer) { bool animating = false; int32 updateMin = -1, updateMax = 0; @@ -653,72 +808,123 @@ void StickersInner::clear() { _aboveShadowFadeStart = 0; _aboveShadowFadeOpacity = anim::fvalue(0, 0); _a_shifting.stop(); - _above = _dragging = _started = -1; - _selected = -1; - _removeDown = -1; - setRemoveSel(-1); - update(); +_above = _dragging = _started = -1; +_selected = -2; +_pressed = -2; +_actionDown = -1; +setActionSel(-1); +update(); } -void StickersInner::setRemoveSel(int32 removeSel) { - if (removeSel != _removeSel) { - if (_removeSel >= 0) update(0, _itemsTop + _removeSel * _rowHeight, width(), _rowHeight); - _removeSel = removeSel; - if (_removeSel >= 0) update(0, _itemsTop + _removeSel * _rowHeight, width(), _rowHeight); - setCursor((_removeSel >= 0 && (_removeDown < 0 || _removeDown == _removeSel)) ? style::cur_pointer : style::cur_default); +void StickersInner::setActionSel(int32 actionSel) { + if (actionSel != _actionSel) { + if (_actionSel >= 0) update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); + _actionSel = actionSel; + if (_actionSel >= 0) update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); + if (_section == Section::Installed) { + setCursor((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel)) ? style::cur_pointer : style::cur_default); + } } } void StickersInner::rebuild() { QList rows, rowsDisabled; - int32 namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); - int32 namew = st::boxWideWidth - namex - st::contactsPadding.right() - st::contactsCheckPosition.x() - qMax(qMax(_returnWidth, _removeWidth), _restoreWidth); + _itemsTop = st::membersPadding.top(); + _featuredHeight = 0; + if (_section == Section::Installed && !Global::FeaturedStickerSetsOrder().isEmpty()) { + _featuredHeight = st::stickersFeaturedHeight; + _itemsTop += _featuredHeight + st::membersPadding.top(); + } + + 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(_returnWidth, _removeWidth), _restoreWidth); + } else { + namew -= _addWidth - st::defaultActiveButton.width; + } clear(); - auto &order = Global::StickerSetsOrder(); + auto &order = (_section == Section::Installed) ? Global::StickerSetsOrder() : Global::FeaturedStickerSetsOrder(); _animStartTimes.reserve(order.size()); auto &sets = Global::StickerSets(); - for (int i = 0, l = order.size(); i < l; ++i) { - auto it = sets.constFind(order.at(i)); - if (it != sets.cend()) { - bool disabled = (it->flags & MTPDstickerSet::Flag::f_disabled); + for_const (auto setId, order) { + auto it = sets.constFind(setId); + if (it == sets.cend()) { + continue; + } - DocumentData *sticker = it->stickers.isEmpty() ? 0 : it->stickers.at(0); - int32 pixw = 0, pixh = 0; - if (sticker) { - pixw = sticker->thumb->width(); - pixh = sticker->thumb->height(); - if (pixw > st::contactsPhotoSize) { - if (pixw > pixh) { - pixh = (pixh * st::contactsPhotoSize) / pixw; - pixw = st::contactsPhotoSize; - } else { - pixw = (pixw * st::contactsPhotoSize) / pixh; - pixh = st::contactsPhotoSize; - } - } else if (pixh > st::contactsPhotoSize) { + bool installed = (it->flags & MTPDstickerSet::Flag::f_installed); + bool disabled = (_section == Section::Installed) && (it->flags & MTPDstickerSet::Flag::f_disabled); + bool official = (it->flags & MTPDstickerSet::Flag::f_official); + bool unread = (_section == Section::Featured) && _unreadSets.contains(it->id); + if (!unread && _section == Section::Featured && (it->flags & MTPDstickerSet_ClientFlag::f_unread)) { + unread = true; + _unreadSets.insert(it->id); + } + + DocumentData *sticker = it->stickers.isEmpty() ? 0 : it->stickers.at(0); + int32 pixw = 0, pixh = 0; + if (sticker) { + pixw = sticker->thumb->width(); + pixh = sticker->thumb->height(); + if (pixw > st::contactsPhotoSize) { + if (pixw > pixh) { + pixh = (pixh * st::contactsPhotoSize) / pixw; + pixw = st::contactsPhotoSize; + } else { pixw = (pixw * st::contactsPhotoSize) / pixh; pixh = st::contactsPhotoSize; } + } else if (pixh > st::contactsPhotoSize) { + pixw = (pixw * st::contactsPhotoSize) / pixh; + pixh = st::contactsPhotoSize; } - QString title = it->title; - int32 titleWidth = st::contactsNameFont->width(title); - if (titleWidth > namew) { - title = st::contactsNameFont->elided(title, namew); - } - bool official = (it->flags & MTPDstickerSet::Flag::f_official); - (disabled ? rowsDisabled : rows).push_back(new StickerSetRow(it->id, sticker, it->stickers.size(), title, official, disabled, pixw, pixh)); - _animStartTimes.push_back(0); - if (it->stickers.isEmpty() || (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { - App::api()->scheduleStickerSetRequest(it->id, it->access); - } + } + QString title = it->title; + int32 titleWidth = st::contactsNameFont->width(title); + if (titleWidth > namew) { + title = st::contactsNameFont->elided(title, namew); + } + (disabled ? rowsDisabled : rows).push_back(new StickerSetRow(it->id, sticker, it->stickers.size(), title, installed, official, unread, disabled, pixw, pixh)); + _animStartTimes.push_back(0); + if (it->stickers.isEmpty() || (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { + App::api()->scheduleStickerSetRequest(it->id, it->access); } } App::api()->requestStickerSets(); _rows = rows + rowsDisabled; resize(width(), _itemsTop + _rows.size() * _rowHeight + st::membersPadding.bottom()); + + if (_section == Section::Featured && Global::FeaturedStickerSetsUnreadCount()) { + Global::SetFeaturedStickerSetsUnreadCount(0); + for (auto &set : Global::RefStickerSets()) { + set.flags &= ~MTPDstickerSet_ClientFlag::f_unread; + } + MTP::send(MTPmessages_ReadFeaturedStickers(), rpcDone(&StickersInner::readFeaturedDone), rpcFail(&StickersInner::readFeaturedFail)); + } +} + +void StickersInner::readFeaturedDone(const MTPBool &result) { + Local::writeStickers(); + 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; } QVector StickersInner::getOrder() const { @@ -755,27 +961,38 @@ StickersInner::~StickersInner() { clear(); } -StickersBox::StickersBox() : ItemListBox(st::boxScroll) -, _save(this, lang(lng_settings_save), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +} // namespace internal + +StickersBox::StickersBox(Section section) : ItemListBox(st::boxScroll) +, _section(section) +, _inner(section) , _reorderRequest(0) , _topShadow(this, st::contactsAboutShadow) -, _bottomShadow(this) , _scrollDelta(0) , _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.left()) -, _about(st::boxTextFont, lang(lng_stickers_reorder), _defaultOptions, _aboutWidth) -, _aboutHeight(st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom()) { - ItemListBox::init(&_inner, st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(), st::boxTitleHeight + _aboutHeight); +, _about(st::boxTextFont, lang(lng_stickers_reorder), _defaultOptions, _aboutWidth) { + + int bottomSkip = st::boxPadding.bottom(); + if (_section == Section::Installed) { + _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); + + _save = new BoxButton(this, lang(lng_settings_save), st::defaultBoxButton); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + + _cancel = new BoxButton(this, lang(lng_cancel), st::cancelBoxButton); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + + _bottomShadow = new ScrollableBoxShadow(this); + bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); + } + ItemListBox::init(_inner, bottomSkip, st::boxTitleHeight + _aboutHeight); setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); App::main()->updateStickers(); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - - connect(&_inner, SIGNAL(checkDraggingScroll(int)), this, SLOT(onCheckDraggingScroll(int))); - connect(&_inner, SIGNAL(noDraggingScroll()), this, SLOT(onNoDraggingScroll())); + connect(_inner, SIGNAL(checkDraggingScroll(int)), this, SLOT(onCheckDraggingScroll(int))); + connect(_inner, SIGNAL(noDraggingScroll()), this, SLOT(onNoDraggingScroll())); connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); _scrollTimer.setSingleShot(false); @@ -785,7 +1002,11 @@ StickersBox::StickersBox() : ItemListBox(st::boxScroll) } int32 StickersBox::countHeight() const { - return st::boxTitleHeight + _aboutHeight + _inner.height() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(); + int bottomSkip = st::boxPadding.bottom(); + if (_section == Section::Installed) { + bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); + } + return st::boxTitleHeight + _aboutHeight + _inner->height() + bottomSkip; } void StickersBox::disenableDone(const MTPBool & result, mtpRequestId req) { @@ -805,7 +1026,7 @@ bool StickersBox::disenableFail(const RPCError &error, mtpRequestId req) { } void StickersBox::saveOrder() { - QVector order = _inner.getOrder(); + auto order = _inner->getOrder(); if (order.size() > 1) { QVector mtpOrder; mtpOrder.reserve(order.size()); @@ -839,9 +1060,11 @@ void StickersBox::paintEvent(QPaintEvent *e) { paintTitle(p, lang(lng_stickers_packs)); p.translate(0, st::boxTitleHeight); - p.fillRect(0, 0, width(), _aboutHeight, st::contactsAboutBg); - p.setPen(st::stickersReorderFg); - _about.draw(p, st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center); + if (_aboutHeight > 0) { + p.fillRect(0, 0, width(), _aboutHeight, st::contactsAboutBg); + p.setPen(st::stickersReorderFg); + _about.draw(p, st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center); + } } void StickersBox::closePressed() { @@ -862,18 +1085,20 @@ void StickersBox::closePressed() { void StickersBox::resizeEvent(QResizeEvent *e) { ItemListBox::resizeEvent(e); - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); - _inner.resize(width(), _inner.height()); + _inner->resize(width(), _inner->height()); _topShadow.setGeometry(0, st::boxTitleHeight + _aboutHeight, width(), st::lineWidth); - _bottomShadow.setGeometry(0, height() - st::boxButtonPadding.bottom() - _save.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); - _inner.setVisibleScrollbar((_scroll.scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); + _inner->setVisibleScrollbar((_scroll.scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); + if (_save) { + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _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); + } } void StickersBox::onStickersUpdated() { - _inner.rebuild(); + _inner->rebuild(); setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); - _inner.setVisibleScrollbar((_scroll.scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); + _inner->setVisibleScrollbar((_scroll.scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); } void StickersBox::onCheckDraggingScroll(int localY) { @@ -901,7 +1126,7 @@ void StickersBox::onScrollTimer() { } void StickersBox::onSave() { - if (!_inner.savingStart()) { + if (!_inner->savingStart()) { return; } @@ -909,7 +1134,7 @@ void StickersBox::onSave() { RecentStickerPack &recent(cGetRecentStickers()); auto &sets = Global::RefStickerSets(); - QVector reorder = _inner.getOrder(), disabled = _inner.getDisabledSets(); + QVector 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()) { @@ -932,12 +1157,20 @@ void StickersBox::onSave() { if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured)) { sets.erase(it); + } else { + it->flags &= ~(MTPDstickerSet::Flag::f_installed | MTPDstickerSet::Flag::f_disabled); } } } } } - Stickers::Order &order(Global::RefStickerSetsOrder()); + + // Clear all installed flags, set only for sets from order. + for (auto &set : sets) { + 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)); @@ -948,13 +1181,14 @@ void StickersBox::onSave() { it->flags &= ~MTPDstickerSet::Flag::f_disabled; } order.push_back(reorder.at(i)); + it->flags |= MTPDstickerSet::Flag::f_installed; } } for (auto it = sets.begin(); it != sets.cend();) { if (it->id == Stickers::CustomSetId || it->id == Stickers::RecentSetId || (it->flags & MTPDstickerSet_ClientFlag::f_featured) - || order.contains(it->id)) { + || (it->flags & MTPDstickerSet::Flag::f_installed)) { ++it; } else { it = sets.erase(it); @@ -973,18 +1207,22 @@ void StickersBox::onSave() { } void StickersBox::hideAll() { - _save.hide(); - _cancel.hide(); _topShadow.hide(); - _bottomShadow.hide(); + if (_save) { + _save->hide(); + _cancel->hide(); + _bottomShadow->hide(); + } ItemListBox::hideAll(); } void StickersBox::showAll() { - _save.show(); - _cancel.show(); _topShadow.show(); - _bottomShadow.show(); + if (_save) { + _save->show(); + _cancel->show(); + _bottomShadow->show(); + } ItemListBox::showAll(); } diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h index 9b6cd9891..1c8e9590d 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.h +++ b/Telegram/SourceFiles/boxes/stickersetbox.h @@ -95,7 +95,6 @@ public: void resizeEvent(QResizeEvent *e); public slots: - void onStickersUpdated(); void onAddStickers(); void onShareStickers(); @@ -103,115 +102,38 @@ public slots: void onScroll(); -signals: +private slots: + void onInstalled(uint64 id); +signals: void installed(uint64 id); protected: - void hideAll(); void showAll(); private: - StickerSetInner _inner; ScrollableBoxShadow _shadow; BoxButton _add, _share, _cancel, _done; QString _title; + }; -class StickersInner : public TWidget { - Q_OBJECT - -public: - - StickersInner(); - - void paintEvent(QPaintEvent *e); - void mousePressEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - - void rebuild(); - bool savingStart() { - if (_saving) return false; - _saving = true; - return true; - } - - QVector getOrder() const; - QVector getDisabledSets() const; - - void setVisibleScrollbar(int32 width); - - ~StickersInner(); - -signals: - - void checkDraggingScroll(int localY); - void noDraggingScroll(); - -public slots: - - void onUpdateSelected(); - -private: - - void step_shifting(uint64 ms, bool timer); - void paintRow(Painter &p, int32 index); - void clear(); - void setRemoveSel(int32 removeSel); - float64 aboveShadowOpacity() const; - - int32 _rowHeight; - struct StickerSetRow { - StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, bool official, bool disabled, int32 pixw, int32 pixh) : id(id) - , sticker(sticker) - , count(count) - , title(title) - , official(official) - , disabled(disabled) - , pixw(pixw) - , pixh(pixh) - , yadd(0, 0) { - } - uint64 id; - DocumentData *sticker; - int32 count; - QString title; - bool official, disabled; - int32 pixw, pixh; - anim::ivalue yadd; - }; - typedef QList StickerSetRows; - StickerSetRows _rows; - QList _animStartTimes; - uint64 _aboveShadowFadeStart; - anim::fvalue _aboveShadowFadeOpacity; - Animation _a_shifting; - - int32 _itemsTop; - - bool _saving; - - int32 _removeSel, _removeDown, _removeWidth, _returnWidth, _restoreWidth; - - QPoint _mouse; - int32 _selected; - QPoint _dragStart; - int32 _started, _dragging, _above; - - BoxShadow _aboveShadow; - - int32 _scrollbar; -}; +namespace internal { +class StickersInner; +} // namespace internal class StickersBox : public ItemListBox, public RPCSender { Q_OBJECT public: - StickersBox(); + enum class Section { + Installed, + Featured, + }; + StickersBox(Section section = Section::Installed); void resizeEvent(QResizeEvent *e); void paintEvent(QPaintEvent *e); @@ -242,20 +164,135 @@ private: bool reorderFail(const RPCError &result); void saveOrder(); - StickersInner _inner; - BoxButton _save, _cancel; + Section _section; + + ChildWidget _inner; + ChildWidget _save = { nullptr }; + ChildWidget _cancel = { nullptr }; QMap _disenableRequests; mtpRequestId _reorderRequest; PlainShadow _topShadow; - ScrollableBoxShadow _bottomShadow; + ChildWidget _bottomShadow = { nullptr }; QTimer _scrollTimer; int32 _scrollDelta; - int32 _aboutWidth; + int _aboutWidth = 0; Text _about; - int32 _aboutHeight; + int _aboutHeight = 0; }; int32 stickerPacksCount(bool includeDisabledOfficial = false); + +namespace internal { + +class StickersInner : public TWidget, public RPCSender { + Q_OBJECT + +public: + using Section = StickersBox::Section; + StickersInner(Section section); + + void rebuild(); + bool savingStart() { + if (_saving) return false; + _saving = true; + return true; + } + + QVector getOrder() const; + QVector getDisabledSets() const; + + void setVisibleScrollbar(int32 width); + + ~StickersInner(); + +protected: + void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + +signals: + void checkDraggingScroll(int localY); + void noDraggingScroll(); + +public slots: + void onUpdateSelected(); + +private: + void paintFeaturedButton(Painter &p) const; + + void step_shifting(uint64 ms, bool timer); + void paintRow(Painter &p, int32 index); + void clear(); + void setActionSel(int32 actionSel); + float64 aboveShadowOpacity() const; + + void installSet(uint64 setId); + void readFeaturedDone(const MTPBool &result); + bool readFeaturedFail(const RPCError &error); + + Section _section; + + int32 _rowHeight; + struct StickerSetRow { + StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, bool installed, bool official, bool unread, bool disabled, int32 pixw, int32 pixh) : id(id) + , sticker(sticker) + , count(count) + , title(title) + , installed(installed) + , official(official) + , unread(unread) + , disabled(disabled) + , pixw(pixw) + , pixh(pixh) + , yadd(0, 0) { + } + uint64 id; + DocumentData *sticker; + int32 count; + QString title; + bool installed, official, unread, disabled; + int32 pixw, pixh; + anim::ivalue yadd; + }; + typedef QList StickerSetRows; + StickerSetRows _rows; + QList _animStartTimes; + uint64 _aboveShadowFadeStart = 0; + anim::fvalue _aboveShadowFadeOpacity = { 0., 0. }; + Animation _a_shifting; + + int32 _itemsTop; + + bool _saving = false; + + int _actionSel = -1; + int _actionDown = -1; + + int _removeWidth, _returnWidth, _restoreWidth; + + QString _addText; + int _addWidth; + + int _featuredHeight = 0; + // Remember all the unread set ids to display unread dots. + OrderedSet _unreadSets; + + QPoint _mouse; + int _selected = -2; // -1 - featured stickers button + int _pressed = -2; + QPoint _dragStart; + int _started = -1; + int _dragging = -1; + int _above = -1; + + BoxShadow _aboveShadow; + + int32 _scrollbar = 0; +}; + +} // namespace internal diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index cc9afe6d6..ef1c72478 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -29,7 +29,6 @@ dialogsUnreadBgActive: #ffffff; dialogsUnreadBgMutedActive: #d3e2ee; dialogsUnreadFont: font(12px bold); dialogsUnreadHeight: 19px; -dialogsUnreadTop: 1px; dialogsUnreadPadding: 5px; dialogsBg: windowBg; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 8591ea74e..ff7043963 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -222,7 +222,7 @@ void paintUnreadCount(Painter &p, const QString &text, int x, int y, const Unrea p.setFont(st.font); p.setPen(st.active ? st::dialogsUnreadFgActive : st::dialogsUnreadFg); - p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + st::dialogsUnreadTop + st.font->ascent, text); + p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + (unreadRectHeight - st.font->height) / 2 + st.font->ascent, text); } void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground) { @@ -258,7 +258,7 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele auto counter = QString::number(unreadCount); auto mutedCounter = history->mute(); int unreadRight = w - st::dialogsPadding.x(); - int unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - st::dialogsUnreadTop; + int unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; int unreadWidth = 0; UnreadBadgeStyle st; @@ -297,7 +297,7 @@ void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool o int unreadTop = (st::dialogsImportantBarHeight - st::dialogsUnreadHeight) / 2; bool mutedHidden = (current == Dialogs::Mode::Important); QString text = mutedHidden ? qsl("Show all chats") : qsl("Hide muted chats"); - int textBaseline = unreadTop + st::dialogsUnreadTop + st::dialogsUnreadFont->ascent; + int textBaseline = unreadTop + (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2 + st::dialogsUnreadFont->ascent; p.drawText(st::dialogsPadding.x(), textBaseline, text); if (mutedHidden) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h index b0e5bd8b1..f083f062f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.h +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -38,6 +38,8 @@ void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool o enum UnreadBadgeSize { UnreadBadgeInDialogs = 0, UnreadBadgeInHistoryToDown, + UnreadBadgeInStickersPanel, + UnreadBadgeInStickersBox, UnreadBadgeSizesCount }; diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index d4db768d2..fd31b7c85 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/stickersetbox.h" #include "inline_bots/inline_bot_result.h" #include "inline_bots/inline_bot_layout_item.h" +#include "dialogs/dialogs_layout.h" #include "historywidget.h" #include "localstorage.h" #include "lang.h" @@ -2829,6 +2830,20 @@ void EmojiPan::onSaveConfigDelayed(int32 delay) { _saveConfigTimer.start(delay); } +void EmojiPan::paintStickerSettingsIcon(Painter &p) const { + int settingsLeft = _iconsLeft + 7 * st::rbEmoji.width; + p.drawSpriteLeft(settingsLeft + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings); + if (auto unread = Global::FeaturedStickerSetsUnreadCount()) { + Dialogs::Layout::UnreadBadgeStyle unreadSt; + unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersPanel; + unreadSt.size = st::stickersSettingsUnreadSize; + int unreadRight = settingsLeft + st::rbEmoji.width - st::stickersSettingsUnreadPosition.x(); + if (rtl()) unreadRight = width() - unreadRight; + int unreadTop = _iconsTop + st::stickersSettingsUnreadPosition.y(); + Dialogs::Layout::paintUnreadCount(p, QString::number(unread), unreadRight, unreadTop, unreadSt); + } +} + void EmojiPan::paintEvent(QPaintEvent *e) { Painter p(this); @@ -2846,7 +2861,7 @@ void EmojiPan::paintEvent(QPaintEvent *e) { p.fillRect(myrtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height()), st::white->b); if (_stickersShown && s_inner.showSectionIcons()) { p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories); - p.drawSpriteLeft(_iconsLeft + 7 * st::rbEmoji.width + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings); + paintStickerSettingsIcon(p); if (!_icons.isEmpty()) { int32 x = _iconsLeft, i = 0, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current(); @@ -3093,6 +3108,7 @@ void EmojiPan::refreshStickers() { if (!_stickersShown) { s_inner.preloadImages(); } + update(); } void EmojiPan::refreshSavedGifs() { diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index 32faf3568..dfd81a7cf 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -656,6 +656,7 @@ signals: void updateStickers(); private: + void paintStickerSettingsIcon(Painter &p) const; void validateSelectedIcon(bool animated = false); diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 3e1a18143..58175e636 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -561,7 +561,7 @@ struct Data { Stickers::Order StickerSetsOrder; uint64 LastStickersUpdate = 0; Stickers::Order FeaturedStickerSetsOrder; - Stickers::UnreadMap FeaturedUnreadSets; + int FeaturedStickerSetsUnreadCount = 0; uint64 LastFeaturedStickersUpdate = 0; MTP::DcOptions DcOptions; @@ -630,7 +630,7 @@ DefineVar(Global, Stickers::Sets, StickerSets); DefineVar(Global, Stickers::Order, StickerSetsOrder); DefineVar(Global, uint64, LastStickersUpdate); DefineVar(Global, Stickers::Order, FeaturedStickerSetsOrder); -DefineVar(Global, Stickers::UnreadMap, FeaturedUnreadSets); +DefineVar(Global, int, FeaturedStickerSetsUnreadCount); DefineVar(Global, uint64, LastFeaturedStickersUpdate); DefineVar(Global, MTP::DcOptions, DcOptions); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index f8831e798..71cd432e5 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -199,7 +199,13 @@ struct Set { }; using Sets = QMap; using Order = QList; -using UnreadMap = OrderedSet; + +inline MTPInputStickerSet inputSetId(const Set &set) { + if (set.id && set.access) { + return MTP_inputStickerSetID(MTP_long(set.id), MTP_long(set.access)); + } + return MTP_inputStickerSetShortName(MTP_string(set.shortName)); +} } // namespace Stickers @@ -250,7 +256,7 @@ DeclareVar(Stickers::Sets, StickerSets); DeclareVar(Stickers::Order, StickerSetsOrder); DeclareVar(uint64, LastStickersUpdate); DeclareVar(Stickers::Order, FeaturedStickerSetsOrder); -DeclareVar(Stickers::UnreadMap, FeaturedUnreadSets); +DeclareVar(int, FeaturedStickerSetsUnreadCount); DeclareVar(uint64, LastFeaturedStickersUpdate); DeclareVar(MTP::DcOptions, DcOptions); diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index 67995b523..9bd12b065 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -196,14 +196,14 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { } return true; }; - auto filterNotPassedByName = [this](UserData *user) -> bool { + auto filterNotPassedByName = [this, &filterNotPassedByUsername](UserData *user) -> bool { for_const (auto &namePart, user->names) { if (namePart.startsWith(_filter, Qt::CaseInsensitive)) { bool exactUsername = (user->username.compare(_filter, Qt::CaseInsensitive) == 0); return exactUsername; } } - return true; + return filterNotPassedByUsername(user); }; bool listAllSuggestions = _filter.isEmpty(); diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 59a20ef43..5f0bd07d1 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -29,6 +29,8 @@ historyToDownArrow: icon { { "history_down_arrow", #b9b9b9, point(14px, 19px) }, }; historyToDownPaddingTop: 10px; +historyToDownBadgeFont: semiboldFont; +historyToDownBadgeSize: 22px; membersInnerScroll: flatScroll(solidScroll) { deltat: 3px; @@ -42,5 +44,3 @@ membersInnerDropdown: InnerDropdown(defaultInnerDropdown) { scrollMargin: margins(0px, 5px, 0px, 5px); scrollPadding: margins(0px, 3px, 8px, 3px); } -historyToDownBadgeFont: semiboldFont; -historyToDownBadgeSize: 22px; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 3bdd9da63..47a8fe198 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3667,7 +3667,7 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) { it->access = set.vaccess_hash.v; it->title = title; it->shortName = qs(set.vshort_name); - auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded); + auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded); it->flags = set.vflags.v | clientFlags; if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) { it->count = set.vcount.v; @@ -3739,6 +3739,11 @@ void HistoryWidget::featuredStickersGot(const MTPmessages_FeaturedStickers &stic if (stickers.type() != mtpc_messages_featuredStickers) return; 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 &setsOrder = Global::RefFeaturedStickerSetsOrder(); @@ -3755,13 +3760,23 @@ void HistoryWidget::featuredStickersGot(const MTPmessages_FeaturedStickers &stic auto it = sets.find(set.vid.v); QString title = stickerSetTitle(set); if (it == sets.cend()) { - it = sets.insert(set.vid.v, Stickers::Set(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded)); + auto clientFlags = MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded; + if (unread.contains(set.vid.v)) { + clientFlags |= MTPDstickerSet_ClientFlag::f_unread; + } + it = sets.insert(set.vid.v, Stickers::Set(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | clientFlags)); } else { it->access = set.vaccess_hash.v; it->title = title; it->shortName = qs(set.vshort_name); - auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded); - it->flags = set.vflags.v | clientFlags | MTPDstickerSet_ClientFlag::f_featured; + auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded); + it->flags = set.vflags.v | clientFlags; + it->flags |= MTPDstickerSet_ClientFlag::f_featured; + if (unread.contains(it->id)) { + it->flags |= MTPDstickerSet_ClientFlag::f_unread; + } else { + it->flags &= ~MTPDstickerSet_ClientFlag::f_unread; + } if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) { it->count = set.vcount.v; it->hash = set.vhash.v; @@ -3774,21 +3789,21 @@ void HistoryWidget::featuredStickersGot(const MTPmessages_FeaturedStickers &stic } } } - for (Stickers::Sets::iterator it = sets.begin(), e = sets.end(); it != e;) { + + int unreadCount = 0; + for (auto it = sets.begin(), e = sets.end(); it != e;) { bool installed = (it->flags & MTPDstickerSet::Flag::f_installed); bool featured = (it->flags & MTPDstickerSet_ClientFlag::f_featured); if (installed || featured) { + if (featured && (it->flags & MTPDstickerSet_ClientFlag::f_unread)) { + ++unreadCount; + } ++it; } else { it = sets.erase(it); } } - - auto &unreadFeatured = Global::RefFeaturedUnreadSets(); - unreadFeatured.clear(); - for_const (auto &unreadSetId, d.vunread.c_vector().v) { - unreadFeatured.insert(unreadSetId.v); - } + Global::SetFeaturedStickerSetsUnreadCount(unreadCount); 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/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 3c2ab76d4..aeec35846 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -3079,7 +3079,6 @@ namespace Local { } size += sizeof(qint32) + (Global::StickerSetsOrder().size() * sizeof(quint64)); size += sizeof(qint32) + (Global::FeaturedStickerSetsOrder().size() * sizeof(quint64)); - size += sizeof(qint32) + (Global::FeaturedUnreadSets().size() * sizeof(quint64)); if (!_stickersKey) { _stickersKey = genKey(); @@ -3094,11 +3093,6 @@ namespace Local { data.stream << Global::StickerSetsOrder(); data.stream << Global::FeaturedStickerSetsOrder(); - data.stream << qint32(Global::FeaturedUnreadSets().size()); - for_const (auto setId, Global::FeaturedUnreadSets()) { - data.stream << quint64(setId); - } - FileWriteDescriptor file(_stickersKey); file.writeEncrypted(data); } @@ -3200,9 +3194,6 @@ namespace Local { auto &featuredOrder = Global::RefFeaturedStickerSetsOrder(); featuredOrder.clear(); - auto &unreadFeatured = Global::RefFeaturedUnreadSets(); - unreadFeatured.clear(); - quint32 cnt; QByteArray hash; stickers.stream >> cnt >> hash; // ignore hash, it is counted @@ -3244,6 +3235,8 @@ namespace Local { } auto &set = sets.insert(setId, Stickers::Set(setId, setAccess, setTitle, setShortName, 0, setHash, MTPDstickerSet::Flags(setFlags))).value(); + // We will set this flags from order lists below. + set.flags &= ~(MTPDstickerSet::Flag::f_installed | MTPDstickerSet_ClientFlag::f_featured); if (scnt < 0) { // disabled not loaded set set.count = -scnt; continue; @@ -3293,23 +3286,32 @@ namespace Local { stickers.stream >> order; stickers.stream >> featuredOrder; - qint32 unreadCount = 0; - stickers.stream >> unreadCount; - for (int i = 0; i < unreadCount; ++i) { - quint64 setId = 0; - stickers.stream >> setId; - if (setId) { - unreadFeatured.insert(setId); + // Set flags and count unread featured sets. + for_const (auto setId, order) { + auto it = sets.find(setId); + if (it != sets.cend()) { + it->flags |= MTPDstickerSet::Flag::f_installed; } } + int unreadCount = 0; + for_const (auto setId, featuredOrder) { + auto it = sets.find(setId); + if (it != sets.cend()) { + it->flags |= MTPDstickerSet_ClientFlag::f_featured; + if (it->flags & MTPDstickerSet_ClientFlag::f_unread) { + ++unreadCount; + } + } + } + Global::SetFeaturedStickerSetsUnreadCount(unreadCount); } } int32 countStickersHash(bool checkOfficial) { uint32 acc = 0; bool foundOfficial = false, foundBad = false;; - const Stickers::Sets &sets(Global::StickerSets()); - const Stickers::Order &order(Global::StickerSetsOrder()); + auto &sets = Global::StickerSets(); + auto &order = Global::StickerSetsOrder(); for (auto i = order.cbegin(), e = order.cend(); i != e; ++i) { auto j = sets.constFind(*i); if (j != sets.cend()) { @@ -3328,11 +3330,14 @@ namespace Local { int32 countFeaturedStickersHash() { uint32 acc = 0; - auto &featured(Global::FeaturedStickerSetsOrder()); + auto &sets = Global::StickerSets(); + auto &featured = Global::FeaturedStickerSetsOrder(); for_const (auto setId, featured) { acc = (acc * 20261) + uint32(setId >> 32); acc = (acc * 20261) + uint32(setId & 0xFFFFFFFF); - if (Global::FeaturedUnreadSets().contains(setId)) { + + auto it = sets.constFind(setId); + if (it != sets.cend() && (it->flags & MTPDstickerSet_ClientFlag::f_unread)) { acc = (acc * 20261) + 1U; } } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 6cb95b58c..f5a74cc59 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3378,7 +3378,6 @@ void MainWidget::stickersBox(const MTPInputStickerSet &set) { } void MainWidget::onStickersInstalled(uint64 setId) { - emit stickersUpdated(); _history->stickersInstalled(setId); } @@ -4731,9 +4730,16 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { } break; case mtpc_updateReadFeaturedStickers: { - Global::RefFeaturedUnreadSets().clear(); - Local::writeStickers(); - emit stickersUpdated(); + 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::writeStickers(); + emit stickersUpdated(); + } } break; ////// Cloud saved GIFs diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 17669f57b..4b4d05a88 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -1291,7 +1291,7 @@ void MediaView::paintEvent(QPaintEvent *e) { if (_saveMsgOpacity.current() > 0) { p.setOpacity(_saveMsgOpacity.current()); App::roundRect(p, _saveMsg, st::medviewSaveMsg, MediaviewSaveCorners); - p.drawSprite(_saveMsg.topLeft() + st::medviewSaveMsgCheckPos, st::medviewSaveMsgCheck); + st::medviewSaveMsgCheck.paint(p, _saveMsg.topLeft() + st::medviewSaveMsgCheckPos, width()); p.setPen(st::white->p); textstyleSet(&st::medviewSaveAsTextStyle); diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index 96eebe2c3..1438fe212 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -1063,8 +1063,11 @@ enum class MTPDstickerSet_ClientFlag : int32 { // sticker set is one of featured (should be saved locally) f_featured = (1 << 29), + // sticker set is an unread featured set + f_unread = (1 << 28), + // update this when adding new client side flags - MIN_FIELD = (1 << 29), + MIN_FIELD = (1 << 28), }; DEFINE_MTP_CLIENT_FLAGS(MTPDstickerSet)