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.
This commit is contained in:
John Preston 2016-09-10 23:54:59 +03:00
parent ff657347b8
commit 8419a56e10
15 changed files with 418 additions and 164 deletions

View File

@ -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;
}

View File

@ -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<MTPlong> 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<MTPlong>(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);
}

View File

@ -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<uint64> _unreadSets;
QPoint _mouse;
int _selected = -3; // -2 - featured stickers button, -1 - archived stickers button
int _pressed = -2;

View File

@ -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<uint64> 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();

View File

@ -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<EmojiPtr> 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()) {

View File

@ -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<EmojiPtr> _emojiList;
void clipCallback(Media::Clip::Notification notification);
enum CacheStatus {

View File

@ -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;

View File

@ -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

View File

@ -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<int>(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<StickerIcon> &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<StickerIcon> &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) {

View File

@ -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<EmojiPanel*> &panels);
void refreshPanels(QVector<EmojiPanel*> &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<EmojiPtr> _emojis[emojiTabCount];
QVector<float64> _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<EmojiPanel*> &panels);
void refreshPanels(QVector<EmojiPanel*> &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<StickerPanInner*>(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;

View File

@ -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<MTPlong> 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<MTPlong>(wrappedIds)), rpcDone(&readFeaturedDone));
Global::SetFeaturedStickerSetsUnreadCount(count);
}
}
} // namespace internal
} // namespace Stickers

View File

@ -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<uint64> _setIds;
};
} // namespace internal
} // namespace Stickers

View File

@ -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 }
};

View File

@ -1155,7 +1155,9 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &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: {

View File

@ -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