diff --git a/Telegram/Resources/art/sprite.png b/Telegram/Resources/art/sprite.png index 932c1d990..bb18b32ea 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 dcad41196..95f408612 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 570cc6603..55c6121ca 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -1866,6 +1866,8 @@ emojiSymbolsActive: sprite(287px, 286px, 21px, 22px); stickersSettings: sprite(140px, 124px, 21px, 22px); savedGifsOver: sprite(329px, 286px, 21px, 22px); savedGifsActive: sprite(350px, 286px, 21px, 22px); +featuredStickersOver: sprite(329px, 264px, 21px, 22px); +featuredStickersActive: sprite(350px, 264px, 21px, 22px); stickersSettingsUnreadSize: 17px; stickersSettingsUnreadPosition: point(4px, 5px); diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ee4bbf2b9..773677775 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -684,6 +684,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_switch_stickers" = "Stickers"; "lng_switch_stickers_gifs" = "GIFs & Stickers"; "lng_switch_emoji" = "Emoji"; +"lng_stickers_featured_add" = "Add"; "lng_saved_gifs" = "Saved GIFs"; "lng_inline_bot_results" = "Results from {inline_bot}"; diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index fea12175a..b7d7095c3 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -959,17 +959,17 @@ void AppClass::killDownloadSessions() { void AppClass::photoUpdated(const FullMsgId &msgId, bool silent, const MTPInputFile &file) { if (!App::self()) return; - QMap::iterator i = photoUpdates.find(msgId); + auto i = photoUpdates.find(msgId); if (i != photoUpdates.end()) { - PeerId id = i.value(); + auto id = i.value(); if (MTP::authedId() && peerToUser(id) == MTP::authedId()) { MTP::send(MTPphotos_UploadProfilePhoto(file, MTP_string(""), MTP_inputGeoPointEmpty(), MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100))), rpcDone(&AppClass::selfPhotoDone), rpcFail(&AppClass::peerPhotoFail, id)); } else if (peerIsChat(id)) { - History *hist = App::history(id); - hist->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(hist->peer->asChat()->inputChat, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, hist->sendRequestId); + auto history = App::history(id); + history->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(history->peer->asChat()->inputChat, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, history->sendRequestId); } else if (peerIsChannel(id)) { - History *hist = App::history(id); - hist->sendRequestId = MTP::send(MTPchannels_EditPhoto(hist->peer->asChannel()->inputChannel, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, hist->sendRequestId); + auto history = App::history(id); + history->sendRequestId = MTP::send(MTPchannels_EditPhoto(history->peer->asChannel()->inputChannel, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, history->sendRequestId); } } } diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 848bd9955..f3d50db54 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stickersetbox.h" #include "mainwidget.h" #include "mainwindow.h" +#include "stickers/stickers.h" #include "boxes/confirmbox.h" #include "apiwrap.h" #include "localstorage.h" @@ -37,54 +38,6 @@ constexpr int kArchivedLimitPerPage = 30; } // namespace -namespace Stickers { - -void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { - auto &v = d.vsets.c_vector().v; - auto &order = Global::RefStickerSetsOrder(); - Stickers::Order archived; - archived.reserve(v.size()); - QMap setsToRequest; - for_const (auto &stickerSet, v) { - const MTPDstickerSet *setData = nullptr; - switch (stickerSet.type()) { - case mtpc_stickerSetCovered: { - auto &d = stickerSet.c_stickerSetCovered(); - if (d.vset.type() == mtpc_stickerSet) { - setData = &d.vset.c_stickerSet(); - } - } break; - case mtpc_stickerSetMultiCovered: { - auto &d = stickerSet.c_stickerSetMultiCovered(); - if (d.vset.type() == mtpc_stickerSet) { - setData = &d.vset.c_stickerSet(); - } - } break; - } - if (setData) { - auto set = Stickers::feedSet(*setData); - if (set->stickers.isEmpty()) { - setsToRequest.insert(set->id, set->access); - } - auto index = order.indexOf(set->id); - if (index >= 0) { - order.removeAt(index); - } - archived.push_back(set->id); - } - } - if (!setsToRequest.isEmpty()) { - for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) { - App::api()->scheduleStickerSetRequest(i.key(), i.value()); - } - App::api()->requestStickerSets(); - } - Local::writeArchivedStickers(); - Ui::showLayer(new StickersBox(archived), KeepOtherLayers); -} - -} // namespace Stickers - StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : TWidget() , _input(set) { connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); @@ -215,12 +168,13 @@ void StickerSetInner::installDone(const MTPmessages_StickerSetInstallResult &res if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); - } else if (wasArchived) { - Local::writeArchivedStickers(); + } else { + if (wasArchived) { + Local::writeArchivedStickers(); + } + Local::writeInstalledStickers(); + emit App::main()->stickersUpdated(); } - - Local::writeInstalledStickers(); - emit App::main()->stickersUpdated(); emit installed(_setId); } @@ -864,59 +818,13 @@ void StickersInner::installSet(uint64 setId) { MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_boolFalse()), rpcDone(&StickersInner::installDone), rpcFail(&StickersInner::installFail, setId)); - auto flags = it->flags; - it->flags &= ~(MTPDstickerSet::Flag::f_archived | MTPDstickerSet_ClientFlag::f_unread); - it->flags |= MTPDstickerSet::Flag::f_installed; - auto changedFlags = flags ^ it->flags; - - 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::writeInstalledStickers(); - if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) Local::writeFeaturedStickers(); - if (changedFlags & MTPDstickerSet::Flag::f_archived) { - auto index = Global::RefArchivedStickerSetsOrder().indexOf(setId); - if (index >= 0) { - Global::RefArchivedStickerSetsOrder().removeAt(index); - Local::writeArchivedStickers(); - } - } - emit App::main()->stickersUpdated(); + Stickers::installLocally(setId); } void StickersInner::installDone(const MTPmessages_StickerSetInstallResult &result) { if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); - Local::writeInstalledStickers(); - Local::writeArchivedStickers(); - emit App::main()->stickersUpdated(); } - - // TEST DATA ONLY - //MTPVector v = MTP_vector(0); - //for (auto &set : Global::RefStickerSets()) { - // if (rand() < RAND_MAX / 2) { - // set.flags |= MTPDstickerSet::Flag::f_archived; - // v._vector().v.push_back(MTP_stickerSet(MTP_flags(set.flags), MTP_long(set.id), MTP_long(set.access), MTP_string(set.title), MTP_string(set.shortName), MTP_int(set.count), MTP_int(set.hash))); - // } - //} - //Stickers::applyArchivedResult(MTP_messages_stickerSetInstallResultArchive(v).c_messages_stickerSetInstallResultArchive()); } bool StickersInner::installFail(uint64 setId, const RPCError &error) { @@ -929,19 +837,7 @@ bool StickersInner::installFail(uint64 setId, const RPCError &error) { return true; } - it->flags &= ~MTPDstickerSet::Flag::f_installed; - - auto &order = Global::RefStickerSetsOrder(); - int currentIndex = order.indexOf(setId); - if (currentIndex >= 0) { - order.removeAt(currentIndex); - } - - Local::writeInstalledStickers(); - emit App::main()->stickersUpdated(); - - Ui::showLayer(new InformBox(lang(lng_stickers_not_found)), KeepOtherLayers); - + Stickers::undoInstallLocally(setId); return true; } diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 9ba408f63..1045d9bb0 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "dropdown.h" +#include "styles/style_stickers.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" #include "inline_bots/inline_bot_result.h" @@ -34,11 +35,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" Dropdown::Dropdown(QWidget *parent, const style::dropdown &st) : TWidget(parent) -, _ignore(false) -, _selected(-1) , _st(st) , _width(_st.width) -, _hiding(false) , a_opacity(0) , _a_appearance(animation(this, &Dropdown::step_appearance)) , _shadow(_st.shadow) { @@ -456,3446 +454,3 @@ void DragArea::step_appearance(float64 ms, bool timer) { } if (timer) update(); } - -namespace internal { - -EmojiColorPicker::EmojiColorPicker() : TWidget() -, _ignoreShow(false) -, _a_selected(animation(this, &EmojiColorPicker::step_selected)) -, _selected(-1) -, _pressedSel(-1) -, _hiding(false) -, a_opacity(0) -, _a_appearance(animation(this, &EmojiColorPicker::step_appearance)) -, _shadow(st::dropdownDef.shadow) { - memset(_variants, 0, sizeof(_variants)); - memset(_hovers, 0, sizeof(_hovers)); - - setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); - - int32 w = st::emojiPanSize.width() * (EmojiColorsCount + 1) + 4 * st::emojiColorsPadding + st::emojiColorsSep + st::dropdownDef.shadow.pxWidth() * 2; - int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::dropdownDef.shadow.pxHeight() * 2; - resize(w, h); - - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); -} - -void EmojiColorPicker::showEmoji(uint32 code) { - EmojiPtr e = emojiGet(code); - if (!e || e == TwoSymbolEmoji || !e->color) { - return; - } - _ignoreShow = false; - - _variants[0] = e; - _variants[1] = emojiGet(e, 0xD83CDFFB); - _variants[2] = emojiGet(e, 0xD83CDFFC); - _variants[3] = emojiGet(e, 0xD83CDFFD); - _variants[4] = emojiGet(e, 0xD83CDFFE); - _variants[5] = emojiGet(e, 0xD83CDFFF); - - if (!_cache.isNull()) _cache = QPixmap(); - showStart(); -} - -void EmojiColorPicker::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (!_cache.isNull()) { - p.setOpacity(a_opacity.current()); - } - if (e->rect() != rect()) { - p.setClipRect(e->rect()); - } - - int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); - QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h); - _shadow.paint(p, r, st::dropdownDef.shadowShift); - - if (_cache.isNull()) { - p.fillRect(e->rect().intersected(r), st::white->b); - - int32 x = w + 2 * st::emojiColorsPadding + st::emojiPanSize.width(); - if (rtl()) x = width() - x - st::emojiColorsSep; - p.fillRect(x, h + st::emojiColorsPadding, st::emojiColorsSep, r.height() - st::emojiColorsPadding * 2, st::emojiColorsSepColor->b); - - if (!_variants[0]) return; - for (int i = 0; i < EmojiColorsCount + 1; ++i) { - drawVariant(p, i); - } - } else { - p.drawPixmap(r.left(), r.top(), _cache); - } - -} - -void EmojiColorPicker::enterEvent(QEvent *e) { - _hideTimer.stop(); - if (_hiding) showStart(); - TWidget::enterEvent(e); -} - -void EmojiColorPicker::leaveEvent(QEvent *e) { - TWidget::leaveEvent(e); -} - -void EmojiColorPicker::mousePressEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - updateSelected(); - _pressedSel = _selected; -} - -void EmojiColorPicker::mouseReleaseEvent(QMouseEvent *e) { - _lastMousePos = e ? e->globalPos() : QCursor::pos(); - int32 pressed = _pressedSel; - _pressedSel = -1; - - updateSelected(); - if (_selected >= 0 && (pressed < 0 || _selected == pressed)) { - emit emojiSelected(_variants[_selected]); - } - _ignoreShow = true; - hideStart(); -} - -void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) { - _lastMousePos = e ? e->globalPos() : QCursor::pos(); - updateSelected(); -} - -void EmojiColorPicker::step_appearance(float64 ms, bool timer) { - if (_cache.isNull()) { - _a_appearance.stop(); - return; - } - float64 dt = ms / st::dropdownDef.duration; - if (dt >= 1) { - a_opacity.finish(); - _cache = QPixmap(); - if (_hiding) { - hide(); - emit hidden(); - } else { - _lastMousePos = QCursor::pos(); - updateSelected(); - } - _a_appearance.stop(); - } else { - a_opacity.update(dt, anim::linear); - } - if (timer) update(); -} - -void EmojiColorPicker::step_selected(uint64 ms, bool timer) { - QRegion toUpdate; - for (EmojiAnimations::iterator i = _emojiAnimations.begin(); i != _emojiAnimations.end();) { - int index = qAbs(i.key()) - 1; - float64 dt = float64(ms - i.value()) / st::emojiPanDuration; - if (dt >= 1) { - _hovers[index] = (i.key() > 0) ? 1 : 0; - i = _emojiAnimations.erase(i); - } else { - _hovers[index] = (i.key() > 0) ? dt : (1 - dt); - ++i; - } - toUpdate += QRect(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height()); - } - if (timer) rtlupdate(toUpdate.boundingRect()); - if (_emojiAnimations.isEmpty()) _a_selected.stop(); -} - -void EmojiColorPicker::hideStart(bool fast) { - if (fast) { - clearSelection(true); - if (_a_appearance.animating()) _a_appearance.stop(); - if (_a_selected.animating()) _a_selected.stop(); - a_opacity = anim::fvalue(0); - _cache = QPixmap(); - hide(); - emit hidden(); - } else { - if (_cache.isNull()) { - int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); - _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); - clearSelection(true); - } - _hiding = true; - a_opacity.start(0); - _a_appearance.start(); - } -} - -void EmojiColorPicker::showStart() { - if (_ignoreShow) return; - - _hiding = false; - if (!isHidden() && a_opacity.current() == 1) { - if (_a_appearance.animating()) { - _a_appearance.stop(); - _cache = QPixmap(); - } - return; - } - if (_cache.isNull()) { - int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); - _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); - clearSelection(true); - } - show(); - a_opacity.start(1); - _a_appearance.start(); -} - -void EmojiColorPicker::clearSelection(bool fast) { - _pressedSel = -1; - _lastMousePos = mapToGlobal(QPoint(-10, -10)); - if (fast) { - _selected = -1; - memset(_hovers, 0, sizeof(_hovers)); - _emojiAnimations.clear(); - } else { - updateSelected(); - } -} - -void EmojiColorPicker::updateSelected() { - int32 selIndex = -1; - QPoint p(mapFromGlobal(_lastMousePos)); - int32 sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::dropdownDef.shadow.pxHeight() - st::emojiColorsPadding; - if (y >= 0 && y < st::emojiPanSize.height()) { - int32 x = sx - st::dropdownDef.shadow.pxWidth() - st::emojiColorsPadding; - if (x >= 0 && x < st::emojiPanSize.width()) { - selIndex = 0; - } else { - x -= st::emojiPanSize.width() + 2 * st::emojiColorsPadding + st::emojiColorsSep; - if (x >= 0 && x < st::emojiPanSize.width() * EmojiColorsCount) { - selIndex = (x / st::emojiPanSize.width()) + 1; - } - } - } - - bool startanim = false; - if (selIndex != _selected) { - if (_selected >= 0) { - _emojiAnimations.remove(_selected + 1); - if (_emojiAnimations.find(-_selected - 1) == _emojiAnimations.end()) { - if (_emojiAnimations.isEmpty()) startanim = true; - _emojiAnimations.insert(-_selected - 1, getms()); - } - } - _selected = selIndex; - if (_selected >= 0) { - _emojiAnimations.remove(-_selected - 1); - if (_emojiAnimations.find(_selected + 1) == _emojiAnimations.end()) { - if (_emojiAnimations.isEmpty()) startanim = true; - _emojiAnimations.insert(_selected + 1, getms()); - } - } - setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default); - } - if (startanim && !_a_selected.animating()) _a_selected.start(); -} - -void EmojiColorPicker::drawVariant(Painter &p, int variant) { - float64 hover = _hovers[variant]; - - QPoint w(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding); - if (hover > 0) { - p.setOpacity(hover); - QPoint tl(w); - if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); - App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); - p.setOpacity(1); - } - int esize = EmojiSizes[EIndex + 1]; - 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() -, _maxHeight(int(st::emojiPanMaxHeight) - st::rbEmoji.height) -, _a_selected(animation(this, &EmojiPanInner::step_selected)) -, _top(0) -, _selected(-1) -, _pressedSel(-1) -, _pickerSel(-1) { - resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); - - setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); - setAttribute(Qt::WA_OpaquePaintEvent); - - _picker.hide(); - - _esize = EmojiSizes[EIndex + 1]; - - for (int32 i = 0; i < emojiTabCount; ++i) { - _counts[i] = emojiPackCount(emojiTabAtIndex(i)); - _hovers[i] = QVector(_counts[i], 0); - } - - _showPickerTimer.setSingleShot(true); - connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker())); - connect(&_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr))); - connect(&_picker, SIGNAL(hidden()), this, SLOT(onPickerHidden())); -} - -void EmojiPanInner::setMaxHeight(int32 h) { - _maxHeight = h; - resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); -} - -void EmojiPanInner::setScrollTop(int top) { - _top = top; -} - -int EmojiPanInner::countHeight() { - int result = 0; - for (int i = 0; i < emojiTabCount; ++i) { - int cnt = emojiPackCount(emojiTabAtIndex(i)), rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); - result += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } - - return result + st::emojiPanPadding; -} - -void EmojiPanInner::paintEvent(QPaintEvent *e) { - Painter p(this); - QRect r = e ? e->rect() : rect(); - if (r != rect()) { - p.setClipRect(r); - } - p.fillRect(r, st::white->b); - - int32 fromcol = floorclamp(r.x() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); - int32 tocol = ceilclamp(r.x() + r.width() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); - if (rtl()) { - qSwap(fromcol, tocol); - fromcol = EmojiPanPerRow - fromcol; - tocol = EmojiPanPerRow - tocol; - } - - int32 y, tilly = 0; - for (int c = 0; c < emojiTabCount; ++c) { - y = tilly; - int32 size = _counts[c]; - int32 rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); - tilly = y + st::emojiPanHeader + (rows * st::emojiPanSize.height()); - if (r.top() >= tilly) continue; - - y += st::emojiPanHeader; - if (_emojis[c].isEmpty()) { - _emojis[c] = emojiPack(emojiTabAtIndex(c)); - if (emojiTabAtIndex(c) != dbietRecent) { - for (EmojiPack::iterator i = _emojis[c].begin(), e = _emojis[c].end(); i != e; ++i) { - if ((*i)->color) { - EmojiColorVariants::const_iterator j = cEmojiVariants().constFind((*i)->code); - if (j != cEmojiVariants().cend()) { - EmojiPtr replace = emojiFromKey(j.value()); - if (replace) { - if (replace != TwoSymbolEmoji && replace->code == (*i)->code && replace->code2 == (*i)->code2) { - *i = replace; - } - } - } - } - } - } - } - - int32 fromrow = floorclamp(r.y() - y, st::emojiPanSize.height(), 0, rows); - int32 torow = ceilclamp(r.y() + r.height() - y, st::emojiPanSize.height(), 0, rows); - for (int32 i = fromrow; i < torow; ++i) { - for (int32 j = fromcol; j < tocol; ++j) { - int32 index = i * EmojiPanPerRow + j; - if (index >= size) break; - - float64 hover = (!_picker.isHidden() && c * MatrixRowShift + index == _pickerSel) ? 1 : _hovers[c][index]; - - QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height()); - if (hover > 0) { - p.setOpacity(hover); - QPoint tl(w); - if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); - App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); - p.setOpacity(1); - } - p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (_esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (_esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_emojis[c][index]->x * _esize, _emojis[c][index]->y * _esize, _esize, _esize)); - } - } - } -} - -bool EmojiPanInner::checkPickerHide() { - if (!_picker.isHidden() && _selected == _pickerSel) { - _picker.hideStart(); - _pickerSel = -1; - updateSelected(); - return true; - } - return false; -} - -void EmojiPanInner::mousePressEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - updateSelected(); - if (checkPickerHide()) { - return; - } - _pressedSel = _selected; - - if (_selected >= 0) { - int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; - if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { - _pickerSel = _selected; - setCursor(style::cur_default); - if (cEmojiVariants().constFind(_emojis[tab][sel]->code) == cEmojiVariants().cend()) { - onShowPicker(); - } else { - _showPickerTimer.start(500); - } - } - } -} - -void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { - int32 pressed = _pressedSel; - _pressedSel = -1; - - _lastMousePos = e->globalPos(); - if (!_picker.isHidden()) { - if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { - return _picker.mouseReleaseEvent(0); - } else if (_pickerSel >= 0) { - int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; - if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { - if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) { - _picker.hideStart(); - _pickerSel = -1; - } - } - } - } - updateSelected(); - - if (_showPickerTimer.isActive()) { - _showPickerTimer.stop(); - _pickerSel = -1; - _picker.hide(); - } - - if (_selected < 0 || _selected != pressed) return; - - if (_selected >= emojiTabCount * MatrixRowShift) { - return; - } - - int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; - if (sel < _emojis[tab].size()) { - EmojiPtr emoji(_emojis[tab][sel]); - if (emoji->color && !_picker.isHidden()) return; - - selectEmoji(emoji); - } -} - -void EmojiPanInner::selectEmoji(EmojiPtr emoji) { - RecentEmojiPack &recent(cGetRecentEmojis()); - RecentEmojiPack::iterator i = recent.begin(), e = recent.end(); - for (; i != e; ++i) { - if (i->first == emoji) { - ++i->second; - if (i->second > 0x8000) { - for (RecentEmojiPack::iterator j = recent.begin(); j != e; ++j) { - if (j->second > 1) { - j->second /= 2; - } else { - j->second = 1; - } - } - } - for (; i != recent.begin(); --i) { - if ((i - 1)->second > i->second) { - break; - } - qSwap(*i, *(i - 1)); - } - break; - } - } - if (i == e) { - while (recent.size() >= EmojiPanPerRow * EmojiPanRowsPerPage) recent.pop_back(); - recent.push_back(qMakePair(emoji, 1)); - for (i = recent.end() - 1; i != recent.begin(); --i) { - if ((i - 1)->second > i->second) { - break; - } - qSwap(*i, *(i - 1)); - } - } - emit saveConfigDelayed(SaveRecentEmojisTimeout); - - emit selected(emoji); -} - -void EmojiPanInner::onShowPicker() { - if (_pickerSel < 0) return; - - int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; - if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { - int32 y = 0; - for (int c = 0; c <= tab; ++c) { - 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; - if (y < 0) { - y += _picker.height() - st::buttonRadius + st::emojiPanSize.height() - st::buttonRadius; - } - int xmax = width() - _picker.width(); - float64 coef = float64(sel % EmojiPanPerRow) / float64(EmojiPanPerRow - 1); - if (rtl()) coef = 1. - coef; - _picker.move(qRound(xmax * coef), y); - - _picker.showEmoji(_emojis[tab][sel]->code); - emit disableScroll(true); - } -} - -void EmojiPanInner::onPickerHidden() { - _pickerSel = -1; - update(); - emit disableScroll(false); - - _lastMousePos = QCursor::pos(); - updateSelected(); -} - -QRect EmojiPanInner::emojiRect(int tab, int sel) { - int x = 0, y = 0; - for (int i = 0; i < emojiTabCount; ++i) { - if (i == tab) { - int rows = (sel / EmojiPanPerRow); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - x = st::emojiPanPadding + ((sel % EmojiPanPerRow) * st::emojiPanSize.width()); - break; - } else { - int cnt = _counts[i]; - int rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } - } - return QRect(x, y, st::emojiPanSize.width(), st::emojiPanSize.height()); -} - -void EmojiPanInner::onColorSelected(EmojiPtr emoji) { - if (emoji->color) { - cRefEmojiVariants().insert(emoji->code, emojiKey(emoji)); - } - if (_pickerSel >= 0) { - int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; - if (tab >= 0 && tab < emojiTabCount) { - _emojis[tab][sel] = emoji; - rtlupdate(emojiRect(tab, sel)); - } - } - selectEmoji(emoji); - _picker.hideStart(); -} - -void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - if (!_picker.isHidden()) { - if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { - return _picker.mouseMoveEvent(0); - } else { - _picker.clearSelection(); - } - } - updateSelected(); -} - -void EmojiPanInner::leaveEvent(QEvent *e) { - clearSelection(); -} - -void EmojiPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { - clearSelection(); -} - -void EmojiPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { - _lastMousePos = QCursor::pos(); - updateSelected(); -} - -void EmojiPanInner::clearSelection(bool fast) { - _lastMousePos = mapToGlobal(QPoint(-10, -10)); - if (fast) { - for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { - int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - _hovers[tab][sel] = 0; - } - _animations.clear(); - if (_selected >= 0) { - int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - _hovers[tab][sel] = 0; - } - if (_pressedSel >= 0) { - int index = qAbs(_pressedSel), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - _hovers[tab][sel] = 0; - } - _selected = _pressedSel = -1; - _a_selected.stop(); - } else { - updateSelected(); - } -} - -DBIEmojiTab EmojiPanInner::currentTab(int yOffset) const { - int y, ytill = 0; - for (int c = 0; c < emojiTabCount; ++c) { - int cnt = _counts[c]; - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); - if (yOffset < ytill) { - return emojiTabAtIndex(c); - } - } - return emojiTabAtIndex(emojiTabCount - 1); -} - -void EmojiPanInner::hideFinish() { - if (!_picker.isHidden()) { - _picker.hideStart(true); - _pickerSel = -1; - clearSelection(true); - } -} - -void EmojiPanInner::refreshRecent() { - clearSelection(true); - _counts[0] = emojiPackCount(dbietRecent); - if (_hovers[0].size() != _counts[0]) _hovers[0] = QVector(_counts[0], 0); - _emojis[0] = emojiPack(dbietRecent); - int32 h = countHeight(); - if (h != height()) { - resize(width(), h); - emit needRefreshPanels(); - } -} - -void EmojiPanInner::fillPanels(QVector &panels) { - if (_picker.parentWidget() != parentWidget()) { - _picker.setParent(parentWidget()); - } - for (int32 i = 0; i < panels.size(); ++i) { - panels.at(i)->hide(); - panels.at(i)->deleteLater(); - } - panels.clear(); - - int y = 0; - panels.reserve(emojiTabCount); - for (int c = 0; c < emojiTabCount; ++c) { - panels.push_back(new EmojiPanel(parentWidget(), lang(LangKey(lng_emoji_category0 + c)), Stickers::NoneSetId, true, y)); - connect(panels.back(), SIGNAL(mousePressed()), this, SLOT(checkPickerHide())); - int cnt = _counts[c], rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); - panels.back()->show(); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } - _picker.raise(); -} - -void EmojiPanInner::refreshPanels(QVector &panels) { - if (panels.size() != emojiTabCount) return fillPanels(panels); - - int32 y = 0; - for (int c = 0; c < emojiTabCount; ++c) { - panels.at(c)->setWantedY(y); - int cnt = _counts[c], rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } -} - -void EmojiPanInner::updateSelected() { - if (_pressedSel >= 0 || _pickerSel >= 0) return; - - int32 selIndex = -1; - QPoint p(mapFromGlobal(_lastMousePos)); - int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::emojiPanPadding; - for (int c = 0; c < emojiTabCount; ++c) { - int cnt = _counts[c]; - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); - if (p.y() >= y && p.y() < ytill) { - y += st::emojiPanHeader; - if (p.y() >= y && sx >= 0 && sx < EmojiPanPerRow * st::emojiPanSize.width()) { - selIndex = qFloor((p.y() - y) / st::emojiPanSize.height()) * EmojiPanPerRow + qFloor(sx / st::emojiPanSize.width()); - if (selIndex >= _emojis[c].size()) { - selIndex = -1; - } else { - selIndex += c * MatrixRowShift; - } - } - break; - } - } - - bool startanim = false; - int oldSel = _selected, newSel = selIndex; - - if (newSel != oldSel) { - if (oldSel >= 0) { - _animations.remove(oldSel + 1); - if (_animations.find(-oldSel - 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(-oldSel - 1, getms()); - } - } - if (newSel >= 0) { - _animations.remove(-newSel - 1); - if (_animations.find(newSel + 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(newSel + 1, getms()); - } - } - setCursor((newSel >= 0) ? style::cur_pointer : style::cur_default); - if (newSel >= 0 && !_picker.isHidden()) { - if (newSel != _pickerSel) { - _picker.hideStart(); - } else { - _picker.showStart(); - } - } - } - - _selected = selIndex; - if (startanim && !_a_selected.animating()) _a_selected.start(); -} - -void EmojiPanInner::step_selected(uint64 ms, bool timer) { - QRegion toUpdate; - for (Animations::iterator i = _animations.begin(); i != _animations.end();) { - int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - float64 dt = float64(ms - i.value()) / st::emojiPanDuration; - if (dt >= 1) { - _hovers[tab][sel] = (i.key() > 0) ? 1 : 0; - i = _animations.erase(i); - } else { - _hovers[tab][sel] = (i.key() > 0) ? dt : (1 - dt); - ++i; - } - toUpdate += emojiRect(tab, sel); - } - if (timer) rtlupdate(toUpdate.boundingRect()); - if (_animations.isEmpty()) _a_selected.stop(); -} - -void InlineCacheEntry::clearResults() { - for_const (const InlineBots::Result *result, results) { - delete result; - } - results.clear(); -} - -void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { - clearSelection(true); - - refreshRecent(); - - int32 y = 0; - for (int c = 0; c < emojiTabCount; ++c) { - if (emojiTabAtIndex(c) == packIndex) break; - int rows = (_counts[c] / EmojiPanPerRow) + ((_counts[c] % EmojiPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } - - emit scrollToY(y); - - _lastMousePos = QCursor::pos(); - - update(); -} - -StickerPanInner::StickerPanInner() : TWidget() -, _a_selected(animation(this, &StickerPanInner::step_selected)) -, _top(0) -, _showingSavedGifs(cShowingSavedGifs()) -, _showingInlineItems(_showingSavedGifs) -, _setGifCommand(false) -, _lastScrolled(0) -, _inlineWithThumb(false) -, _selected(-1) -, _pressedSel(-1) -, _settings(this, lang(lng_stickers_you_have)) -, _previewShown(false) { - setMaxHeight(st::emojiPanMaxHeight - st::rbEmoji.height); - - setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); - setAttribute(Qt::WA_OpaquePaintEvent); - - connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); - connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); - - _previewTimer.setSingleShot(true); - connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); - - _updateInlineItems.setSingleShot(true); - connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems())); -} - -void StickerPanInner::setMaxHeight(int32 h) { - _maxHeight = h; - resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); - _settings.moveToLeft((st::emojiPanWidth - _settings.width()) / 2, height() / 3); -} - -void StickerPanInner::setScrollTop(int top) { - if (top == _top) return; - - _lastScrolled = getms(); - _top = top; -} - -int32 StickerPanInner::countHeight(bool plain) { - int result = 0, minLastH = plain ? 0 : (_maxHeight - st::stickerPanPadding); - if (_showingInlineItems) { - result = st::emojiPanHeader; - if (_switchPmButton) { - result += _switchPmButton->height() + st::inlineResultsSkip; - } - for (int i = 0, l = _inlineRows.count(); i < l; ++i) { - result += _inlineRows.at(i).height; - } - } else { - for (int i = 0; i < _sets.size(); ++i) { - int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); - if (i == _sets.size() - 1 && h < minLastH) h = minLastH; - result += h; - } - } - return qMax(minLastH, result) + st::stickerPanPadding; -} - -StickerPanInner::~StickerPanInner() { - clearInlineRows(true); - deleteUnusedGifLayouts(); - deleteUnusedInlineLayouts(); -} - -QRect StickerPanInner::stickerRect(int tab, int sel) { - int x = 0, y = 0; - for (int i = 0; i < _sets.size(); ++i) { - if (i == tab) { - int rows = (((sel >= _sets.at(i).pack.size()) ? (sel - _sets.at(i).pack.size()) : sel) / StickerPanPerRow); - y += st::emojiPanHeader + rows * st::stickerPanSize.height(); - x = st::stickerPanPadding + ((sel % StickerPanPerRow) * st::stickerPanSize.width()); - break; - } else { - int cnt = _sets.at(i).pack.size(); - int rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::stickerPanSize.height(); - } - } - return QRect(x, y, st::stickerPanSize.width(), st::stickerPanSize.height()); -} - -void StickerPanInner::paintEvent(QPaintEvent *e) { - Painter p(this); - QRect r = e ? e->rect() : rect(); - if (r != rect()) { - p.setClipRect(r); - } - p.fillRect(r, st::white); - - if (_showingInlineItems) { - paintInlineItems(p, r); - } else { - paintStickers(p, r); - } -} - -void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) { - if (_inlineRows.isEmpty()) { - p.setFont(st::normalFont); - p.setPen(st::noContactsColor); - p.drawText(QRect(0, 0, width(), (height() / 3) * 2 + st::normalFont->height), lang(lng_inline_bot_no_results), style::al_center); - return; - } - InlineBots::Layout::PaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false); - - int top = st::emojiPanHeader; - if (_switchPmButton) { - top += _switchPmButton->height() + st::inlineResultsSkip; - } - - int fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width()); - for (int row = 0, rows = _inlineRows.size(); row < rows; ++row) { - const InlineRow &inlineRow(_inlineRows.at(row)); - if (top >= r.top() + r.height()) break; - if (top + inlineRow.height > r.top()) { - int left = st::inlineResultsLeft; - if (row == rows - 1) context.lastRow = true; - for (int col = 0, cols = inlineRow.items.size(); col < cols; ++col) { - if (left >= tox) break; - - const InlineItem *item = inlineRow.items.at(col); - int w = item->width(); - if (left + w > fromx) { - p.translate(left, top); - item->paint(p, r.translated(-left, -top), &context); - p.translate(-left, -top); - } - left += w; - if (item->hasRightSkip()) { - left += st::inlineResultsSkip; - } - } - } - top += inlineRow.height; - } -} - -void StickerPanInner::paintStickers(Painter &p, const QRect &r) { - int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); - int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); - if (rtl()) { - qSwap(fromcol, tocol); - fromcol = StickerPanPerRow - fromcol; - tocol = StickerPanPerRow - tocol; - } - - int32 y, tilly = 0; - for (int c = 0, l = _sets.size(); c < l; ++c) { - y = tilly; - int32 size = _sets.at(c).pack.size(); - int32 rows = (size / StickerPanPerRow) + ((size % StickerPanPerRow) ? 1 : 0); - tilly = y + st::emojiPanHeader + (rows * st::stickerPanSize.height()); - if (r.top() >= tilly) continue; - - bool special = (_sets[c].flags & MTPDstickerSet::Flag::f_official); - y += st::emojiPanHeader; - - int32 fromrow = floorclamp(r.y() - y, st::stickerPanSize.height(), 0, rows); - int32 torow = ceilclamp(r.y() + r.height() - y, st::stickerPanSize.height(), 0, rows); - for (int32 i = fromrow; i < torow; ++i) { - for (int32 j = fromcol; j < tocol; ++j) { - int32 index = i * StickerPanPerRow + j; - if (index >= size) break; - - float64 hover = _sets[c].hovers[index]; - - DocumentData *sticker = _sets[c].pack[index]; - if (!sticker->sticker()) continue; - - QPoint pos(st::stickerPanPadding + j * st::stickerPanSize.width(), y + i * st::stickerPanSize.height()); - if (hover > 0) { - p.setOpacity(hover); - QPoint tl(pos); - if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width()); - App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); - p.setOpacity(1); - } - - bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); - if (goodThumb) { - sticker->thumb->load(); - } else { - sticker->checkSticker(); - } - - float64 coef = qMin((st::stickerPanSize.width() - st::buttonRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::buttonRadius * 2) / float64(sticker->dimensions.height())); - if (coef > 1) coef = 1; - int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height()); - if (w < 1) w = 1; - if (h < 1) h = 1; - QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); - if (goodThumb) { - p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h)); - } else if (!sticker->sticker()->img->isNull()) { - p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h)); - } - - if (hover > 0 && _sets[c].id == Stickers::RecentSetId && _custom.at(index)) { - float64 xHover = _sets[c].hovers[_sets[c].pack.size() + index]; - - QPoint xPos = pos + QPoint(st::stickerPanSize.width() - st::stickerPanDelete.pxWidth(), 0); - p.setOpacity(hover * (xHover + (1 - xHover) * st::stickerPanDeleteOpacity)); - p.drawSpriteLeft(xPos, width(), st::stickerPanDelete); - p.setOpacity(1); - } - } - } - } -} - -void StickerPanInner::mousePressEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - updateSelected(); - - _pressedSel = _selected; - ClickHandler::pressed(); - _previewTimer.start(QApplication::startDragTime()); -} - -void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { - _previewTimer.stop(); - - int32 pressed = _pressedSel; - _pressedSel = -1; - - ClickHandlerPtr activated = ClickHandler::unpressed(); - - _lastMousePos = e->globalPos(); - updateSelected(); - - if (_previewShown) { - _previewShown = false; - return; - } - - if (_selected < 0 || _selected != pressed || (_showingInlineItems && !activated)) return; - if (_showingInlineItems) { - if (!dynamic_cast(activated.data())) { - App::activateClickHandler(activated, e->button()); - return; - } - int row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; - if (row >= _inlineRows.size() || col >= _inlineRows.at(row).items.size()) { - return; - } - - InlineItem *item = _inlineRows.at(row).items.at(col); - if (PhotoData *photo = item->getPhoto()) { - if (photo->medium->loaded() || photo->thumb->loaded()) { - emit selected(photo); - } else if (!photo->medium->loading()) { - photo->thumb->loadEvenCancelled(); - photo->medium->loadEvenCancelled(); - } - } else if (DocumentData *document = item->getDocument()) { - if (document->loaded()) { - emit selected(document); - } else if (document->loading()) { - document->cancel(); - } else { - DocumentOpenClickHandler::doOpen(document, ActionOnLoadNone); - } - } else if (InlineResult *inlineResult = item->getResult()) { - if (inlineResult->onChoose(item)) { - emit selected(inlineResult, _inlineBot); - } - } - return; - } - if (_selected >= MatrixRowShift * _sets.size()) { - return; - } - - int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; - if (_sets[tab].id == Stickers::RecentSetId && sel >= _sets[tab].pack.size() && sel < _sets[tab].pack.size() * 2 && _custom.at(sel - _sets[tab].pack.size())) { - clearSelection(true); - bool refresh = false; - DocumentData *sticker = _sets[tab].pack.at(sel - _sets[tab].pack.size()); - RecentStickerPack &recent(cGetRecentStickers()); - for (int32 i = 0, l = recent.size(); i < l; ++i) { - if (recent.at(i).first == sticker) { - recent.removeAt(i); - Local::writeUserSettings(); - refresh = true; - break; - } - } - auto &sets = Global::RefStickerSets(); - auto it = sets.find(Stickers::CustomSetId); - if (it != sets.cend()) { - for (int32 i = 0, l = it->stickers.size(); i < l; ++i) { - if (it->stickers.at(i) == sticker) { - it->stickers.removeAt(i); - if (it->stickers.isEmpty()) { - sets.erase(it); - } - Local::writeInstalledStickers(); - refresh = true; - break; - } - } - } - if (refresh) { - refreshRecentStickers(); - updateSelected(); - update(); - } - return; - } - if (sel < _sets[tab].pack.size()) { - emit selected(_sets[tab].pack[sel]); - } -} - -void StickerPanInner::mouseMoveEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - updateSelected(); -} - -void StickerPanInner::leaveEvent(QEvent *e) { - clearSelection(); -} - -void StickerPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { - clearSelection(); -} - -void StickerPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { - _lastMousePos = QCursor::pos(); - updateSelected(); -} - -bool StickerPanInner::showSectionIcons() const { - return !inlineResultsShown(); -} - -void StickerPanInner::clearSelection(bool fast) { - _lastMousePos = mapToGlobal(QPoint(-10, -10)); - if (fast) { - if (_showingInlineItems) { - if (_selected >= 0) { - int32 srow = _selected / MatrixRowShift, scol = _selected % MatrixRowShift; - t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); - ClickHandler::clearActive(_inlineRows.at(srow).items.at(scol)); - setCursor(style::cur_default); - } - _selected = _pressedSel = -1; - return; - } - for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { - int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - _sets[tab].hovers[sel] = 0; - } - _animations.clear(); - if (_selected >= 0) { - int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - if (index >= 0 && tab < _sets.size() && _sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + _sets[tab].pack.size()) { - _sets[tab].hovers[sel] = 0; - sel -= _sets[tab].pack.size(); - } - _sets[tab].hovers[sel] = 0; - } - if (_pressedSel >= 0) { - int index = qAbs(_pressedSel), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - if (index >= 0 && tab < _sets.size() && _sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + _sets[tab].pack.size()) { - _sets[tab].hovers[sel] = 0; - sel -= _sets[tab].pack.size(); - } - _sets[tab].hovers[sel] = 0; - } - _selected = _pressedSel = -1; - _a_selected.stop(); - } else { - updateSelected(); - } -} - -void StickerPanInner::hideFinish(bool completely) { - if (completely) { - auto itemForget = [](const InlineItem *item) { - if (DocumentData *document = item->getDocument()) { - document->forget(); - } - if (PhotoData *photo = item->getPhoto()) { - photo->forget(); - } - if (InlineResult *result = item->getResult()) { - result->forget(); - } - }; - clearInlineRows(false); - for_const (InlineItem *item, _gifLayouts) { - itemForget(item); - } - for_const (InlineItem *item, _inlineLayouts) { - itemForget(item); - } - } - if (_setGifCommand && _showingSavedGifs) { - App::insertBotCommand(qsl(""), true); - } - _setGifCommand = false; -} - -void StickerPanInner::refreshStickers() { - clearSelection(true); - - _sets.clear(); - _sets.reserve(Global::StickerSetsOrder().size() + 1); - - refreshRecentStickers(false); - for (auto i = Global::StickerSetsOrder().cbegin(), e = Global::StickerSetsOrder().cend(); i != e; ++i) { - appendSet(*i); - } - - if (_showingInlineItems) { - _settings.hide(); - } else { - int32 h = countHeight(); - if (h != height()) resize(width(), h); - - _settings.setVisible(_sets.isEmpty()); - } - - - emit refreshIcons(); - - updateSelected(); -} - -bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) { - InlineItem *layout = nullptr; - if (savedGif) { - layout = layoutPrepareSavedGif(savedGif, (_inlineRows.size() * MatrixRowShift) + row.items.size()); - } else if (result) { - layout = layoutPrepareInlineResult(result, (_inlineRows.size() * MatrixRowShift) + row.items.size()); - } - if (!layout) return false; - - layout->preload(); - if (inlineRowFinalize(row, sumWidth, layout->isFullLine())) { - layout->setPosition(_inlineRows.size() * MatrixRowShift); - } - - sumWidth += layout->maxWidth(); - if (!row.items.isEmpty() && row.items.back()->hasRightSkip()) { - sumWidth += st::inlineResultsSkip; - } - - row.items.push_back(layout); - return true; -} - -bool StickerPanInner::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force) { - if (row.items.isEmpty()) return false; - - bool full = (row.items.size() >= InlineItemsMaxPerRow); - bool big = (sumWidth >= st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft); - if (full || big || force) { - _inlineRows.push_back(layoutInlineRow(row, (full || big) ? sumWidth : 0)); - row = InlineRow(); - row.items.reserve(InlineItemsMaxPerRow); - sumWidth = 0; - return true; - } - return false; -} - -void StickerPanInner::refreshSavedGifs() { - if (_showingSavedGifs) { - _settings.hide(); - clearInlineRows(false); - if (_showingInlineItems) { - const SavedGifs &saved(cSavedGifs()); - if (saved.isEmpty()) { - showStickerSet(Stickers::RecentSetId); - return; - } else { - _inlineRows.reserve(saved.size()); - InlineRow row; - row.items.reserve(InlineItemsMaxPerRow); - int32 sumWidth = 0; - for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { - inlineRowsAddItem(*i, 0, row, sumWidth); - } - inlineRowFinalize(row, sumWidth, true); - } - deleteUnusedGifLayouts(); - - int32 h = countHeight(); - if (h != height()) resize(width(), h); - - update(); - } - } - emit refreshIcons(); - - updateSelected(); -} - -void StickerPanInner::inlineBotChanged() { - _setGifCommand = false; - refreshInlineRows(nullptr, nullptr, true); -} - -void StickerPanInner::clearInlineRows(bool resultsDeleted) { - if (resultsDeleted) { - if (_showingInlineItems) { - _selected = _pressedSel = -1; - } - } else { - if (_showingInlineItems) { - clearSelection(true); - } - for (InlineRows::const_iterator i = _inlineRows.cbegin(), e = _inlineRows.cend(); i != e; ++i) { - for (InlineItems::const_iterator j = i->items.cbegin(), end = i->items.cend(); j != end; ++j) { - (*j)->setPosition(-1); - } - } - } - _inlineRows.clear(); -} - -InlineItem *StickerPanInner::layoutPrepareSavedGif(DocumentData *doc, int32 position) { - auto i = _gifLayouts.constFind(doc); - if (i == _gifLayouts.cend()) { - if (auto layout = InlineItem::createLayoutGif(doc)) { - i = _gifLayouts.insert(doc, layout.release()); - i.value()->initDimensions(); - } else { - return nullptr; - } - } - if (!i.value()->maxWidth()) return nullptr; - - i.value()->setPosition(position); - return i.value(); -} - -InlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *result, int32 position) { - auto i = _inlineLayouts.constFind(result); - if (i == _inlineLayouts.cend()) { - if (auto layout = InlineItem::createLayout(result, _inlineWithThumb)) { - i = _inlineLayouts.insert(result, layout.release()); - i.value()->initDimensions(); - } else { - return nullptr; - } - } - if (!i.value()->maxWidth()) return nullptr; - - i.value()->setPosition(position); - return i.value(); -} - -void StickerPanInner::deleteUnusedGifLayouts() { - if (_inlineRows.isEmpty() || !_showingSavedGifs) { // delete all - for_const (const InlineItem *item, _gifLayouts) { - delete item; - } - _gifLayouts.clear(); - } else { - for (GifLayouts::iterator i = _gifLayouts.begin(); i != _gifLayouts.cend();) { - if (i.value()->position() < 0) { - delete i.value(); - i = _gifLayouts.erase(i); - } else { - ++i; - } - } - } -} - -void StickerPanInner::deleteUnusedInlineLayouts() { - if (_inlineRows.isEmpty() || _showingSavedGifs) { // delete all - for_const (const InlineItem *item, _inlineLayouts) { - delete item; - } - _inlineLayouts.clear(); - } else { - for (InlineLayouts::iterator i = _inlineLayouts.begin(); i != _inlineLayouts.cend();) { - if (i.value()->position() < 0) { - delete i.value(); - i = _inlineLayouts.erase(i); - } else { - ++i; - } - } - } -} - -StickerPanInner::InlineRow &StickerPanInner::layoutInlineRow(InlineRow &row, int32 sumWidth) { - int32 count = row.items.size(); - t_assert(count <= InlineItemsMaxPerRow); - - // enumerate items in the order of growing maxWidth() - // for that sort item indices by maxWidth() - int indices[InlineItemsMaxPerRow]; - for (int i = 0; i < count; ++i) { - indices[i] = i; - } - std::sort(indices, indices + count, [&row](int a, int b) -> bool { - return row.items.at(a)->maxWidth() < row.items.at(b)->maxWidth(); - }); - - row.height = 0; - int availw = width() - st::inlineResultsLeft; - for (int i = 0; i < count; ++i) { - int index = indices[i]; - int w = sumWidth ? (row.items.at(index)->maxWidth() * availw / sumWidth) : row.items.at(index)->maxWidth(); - int actualw = qMax(w, int(st::inlineResultsMinWidth)); - row.height = qMax(row.height, row.items.at(index)->resizeGetHeight(actualw)); - if (sumWidth) { - availw -= actualw; - sumWidth -= row.items.at(index)->maxWidth(); - if (index > 0 && row.items.at(index - 1)->hasRightSkip()) { - availw -= st::inlineResultsSkip; - sumWidth -= st::inlineResultsSkip; - } - } - } - return row; -} - -void StickerPanInner::preloadImages() { - if (_showingInlineItems) { - for (int32 row = 0, rows = _inlineRows.size(); row < rows; ++row) { - for (int32 col = 0, cols = _inlineRows.at(row).items.size(); col < cols; ++col) { - _inlineRows.at(row).items.at(col)->preload(); - } - } - return; - } - - for (int32 i = 0, l = _sets.size(), k = 0; i < l; ++i) { - for (int32 j = 0, n = _sets.at(i).pack.size(); j < n; ++j) { - if (++k > StickerPanPerRow * (StickerPanPerRow + 1)) break; - - DocumentData *sticker = _sets.at(i).pack.at(j); - if (!sticker || !sticker->sticker()) continue; - - bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); - if (goodThumb) { - sticker->thumb->load(); - } else { - sticker->automaticLoad(0); - } - } - if (k > StickerPanPerRow * (StickerPanPerRow + 1)) break; - } -} - -uint64 StickerPanInner::currentSet(int yOffset) const { - if (_showingInlineItems) return Stickers::NoneSetId; - - int y, ytill = 0; - for (int i = 0, l = _sets.size(); i < l; ++i) { - int cnt = _sets.at(i).pack.size(); - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); - if (yOffset < ytill) { - return _sets.at(i).id; - } - } - return _sets.isEmpty() ? Stickers::RecentSetId : _sets.back().id; -} - -void StickerPanInner::hideInlineRowsPanel() { - clearInlineRows(false); - if (_showingInlineItems) { - _showingSavedGifs = cShowingSavedGifs(); - if (_showingSavedGifs) { - refreshSavedGifs(); - emit scrollToY(0); - emit scrollUpdated(); - } else { - showStickerSet(Stickers::RecentSetId); - } - } -} - -void StickerPanInner::clearInlineRowsPanel() { - clearInlineRows(false); -} - -void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) { - if (!entry || entry->switchPmText.isEmpty()) { - _switchPmButton.reset(); - _switchPmStartToken.clear(); - } else { - if (!_switchPmButton) { - _switchPmButton = std_::make_unique(this, QString(), st::switchPmButton); - _switchPmButton->show(); - _switchPmButton->move(st::inlineResultsLeft, st::emojiPanHeader); - connect(_switchPmButton.get(), SIGNAL(clicked()), this, SLOT(onSwitchPm())); - } - _switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper() - _switchPmStartToken = entry->switchPmStartToken; - } - update(); -} - -int StickerPanInner::refreshInlineRows(UserData *bot, const InlineCacheEntry *entry, bool resultsDeleted) { - _inlineBot = bot; - refreshSwitchPmButton(entry); - auto clearResults = [this, entry]() { - if (!entry) { - return true; - } - if (entry->results.isEmpty() && entry->switchPmText.isEmpty()) { - if (!_inlineBot || _inlineBot->username != cInlineGifBotUsername()) { - return true; - } - } - return false; - }; - if (clearResults()) { - if (resultsDeleted) { - clearInlineRows(true); - deleteUnusedInlineLayouts(); - } - emit emptyInlineRows(); - return 0; - } - - clearSelection(true); - - t_assert(_inlineBot != 0); - _inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username)); - - _showingInlineItems = true; - _showingSavedGifs = false; - _settings.hide(); - - int32 count = entry->results.size(), from = validateExistingInlineRows(entry->results), added = 0; - - if (count) { - _inlineRows.reserve(count); - InlineRow row; - row.items.reserve(InlineItemsMaxPerRow); - int32 sumWidth = 0; - for (int32 i = from; i < count; ++i) { - if (inlineRowsAddItem(0, entry->results.at(i), row, sumWidth)) { - ++added; - } - } - inlineRowFinalize(row, sumWidth, true); - } - - int32 h = countHeight(); - if (h != height()) resize(width(), h); - update(); - - emit refreshIcons(); - - _lastMousePos = QCursor::pos(); - updateSelected(); - - return added; -} - -int32 StickerPanInner::validateExistingInlineRows(const InlineResults &results) { - int32 count = results.size(), until = 0, untilrow = 0, untilcol = 0; - for (; until < count;) { - if (untilrow >= _inlineRows.size() || _inlineRows.at(untilrow).items.at(untilcol)->getResult() != results.at(until)) { - break; - } - ++until; - if (++untilcol == _inlineRows.at(untilrow).items.size()) { - ++untilrow; - untilcol = 0; - } - } - if (until == count) { // all items are layed out - if (untilrow == _inlineRows.size()) { // nothing changed - return until; - } - - for (int32 i = untilrow, l = _inlineRows.size(), skip = untilcol; i < l; ++i) { - for (int32 j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) { - if (skip) { - --skip; - } else { - _inlineRows.at(i).items.at(j)->setPosition(-1); - } - } - } - if (!untilcol) { // all good rows are filled - _inlineRows.resize(untilrow); - return until; - } - _inlineRows.resize(untilrow + 1); - _inlineRows[untilrow].items.resize(untilcol); - _inlineRows[untilrow] = layoutInlineRow(_inlineRows[untilrow]); - return until; - } - if (untilrow && !untilcol) { // remove last row, maybe it is not full - --untilrow; - untilcol = _inlineRows.at(untilrow).items.size(); - } - until -= untilcol; - - for (int32 i = untilrow, l = _inlineRows.size(); i < l; ++i) { - for (int32 j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) { - _inlineRows.at(i).items.at(j)->setPosition(-1); - } - } - _inlineRows.resize(untilrow); - - if (_inlineRows.isEmpty()) { - _inlineWithThumb = false; - for (int32 i = until; i < count; ++i) { - if (results.at(i)->hasThumbDisplay()) { - _inlineWithThumb = true; - break; - } - } - } - return until; -} - -void StickerPanInner::notify_inlineItemLayoutChanged(const InlineItem *layout) { - if (_selected < 0 || !_showingInlineItems) { - return; - } - - int32 row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; - if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { - if (layout == _inlineRows.at(row).items.at(col)) { - updateSelected(); - } - } -} - -void StickerPanInner::ui_repaintInlineItem(const InlineItem *layout) { - uint64 ms = getms(); - if (_lastScrolled + 100 <= ms) { - update(); - } else { - _updateInlineItems.start(_lastScrolled + 100 - ms); - } -} - -bool StickerPanInner::ui_isInlineItemVisible(const InlineItem *layout) { - int32 position = layout->position(); - if (!_showingInlineItems || position < 0) return false; - - int32 row = position / MatrixRowShift, col = position % MatrixRowShift; - t_assert((row < _inlineRows.size()) && (col < _inlineRows.at(row).items.size())); - - const InlineItems &inlineItems(_inlineRows.at(row).items); - int32 top = st::emojiPanHeader; - for (int32 i = 0; i < row; ++i) { - top += _inlineRows.at(i).height; - } - - return (top < _top + _maxHeight) && (top + _inlineRows.at(row).items.at(col)->height() > _top); -} - -bool StickerPanInner::ui_isInlineItemBeingChosen() { - return _showingInlineItems; -} - -void StickerPanInner::appendSet(uint64 setId) { - auto &sets = Global::StickerSets(); - auto it = sets.constFind(setId); - if (it == sets.cend() || (it->flags & MTPDstickerSet::Flag::f_archived) || it->stickers.isEmpty()) return; - - StickerPack pack; - pack.reserve(it->stickers.size()); - for (int32 i = 0, l = it->stickers.size(); i < l; ++i) { - pack.push_back(it->stickers.at(i)); - } - _sets.push_back(DisplayedSet(it->id, it->flags, it->title, pack.size() + 1, pack)); -} - -void StickerPanInner::refreshRecent() { - if (_showingInlineItems) { - if (_showingSavedGifs) { - refreshSavedGifs(); - } - } else { - refreshRecentStickers(); - } -} - -void StickerPanInner::refreshRecentStickers(bool performResize) { - _custom.clear(); - clearSelection(true); - auto &sets = Global::StickerSets(); - auto &recent = cGetRecentStickers(); - auto customIt = sets.constFind(Stickers::CustomSetId); - auto cloudIt = sets.constFind(Stickers::CloudRecentSetId); - if (recent.isEmpty() - && (customIt == sets.cend() || customIt->stickers.isEmpty()) - && (cloudIt == sets.cend() || cloudIt->stickers.isEmpty())) { - if (!_sets.isEmpty() && _sets.at(0).id == Stickers::RecentSetId) { - _sets.pop_front(); - } - } else { - StickerPack recentPack; - int customCnt = (customIt == sets.cend()) ? 0 : customIt->stickers.size(); - int cloudCnt = (cloudIt == sets.cend()) ? 0 : cloudIt->stickers.size(); - recentPack.reserve(cloudCnt + recent.size() + customCnt); - _custom.reserve(cloudCnt + recent.size() + customCnt); - if (cloudCnt > 0) { - for_const (auto sticker, cloudIt->stickers) { - recentPack.push_back(sticker); - _custom.push_back(false); - } - } - for_const (auto &recentSticker, recent) { - auto sticker = recentSticker.first; - recentPack.push_back(sticker); - _custom.push_back(false); - } - if (customCnt > 0) { - for_const (auto &sticker, customIt->stickers) { - auto index = recentPack.indexOf(sticker); - if (index >= cloudCnt) { - _custom[index] = true; // mark stickers from recent as custom - } else { - recentPack.push_back(sticker); - _custom.push_back(true); - } - } - } - if (_sets.isEmpty() || _sets.at(0).id != Stickers::RecentSetId) { - _sets.push_back(DisplayedSet(Stickers::RecentSetId, MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special, lang(lng_recent_stickers), recentPack.size() * 2, recentPack)); - } else { - _sets[0].pack = recentPack; - _sets[0].hovers.resize(recentPack.size() * 2); - } - } - - if (performResize && !_showingInlineItems) { - int32 h = countHeight(); - if (h != height()) { - resize(width(), h); - emit needRefreshPanels(); - } - - updateSelected(); - } -} - -void StickerPanInner::fillIcons(QList &icons) { - icons.clear(); - icons.reserve(_sets.size() + 1); - if (!cSavedGifs().isEmpty()) icons.push_back(StickerIcon(Stickers::NoneSetId)); - - if (_sets.isEmpty()) return; - int32 i = 0; - if (_sets.at(0).id == Stickers::RecentSetId) ++i; - if (i > 0) icons.push_back(StickerIcon(Stickers::RecentSetId)); - for (int32 l = _sets.size(); i < l; ++i) { - DocumentData *s = _sets.at(i).pack.at(0); - int32 availw = st::rbEmoji.width - 2 * st::stickerIconPadding, availh = st::rbEmoji.height - 2 * st::stickerIconPadding; - int32 thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1; - if (availw * thumbh > availh * thumbw) { - pixh = availh; - pixw = (pixh * thumbw) / thumbh; - } else { - pixw = availw; - pixh = thumbw ? ((pixw * thumbh) / thumbw) : 1; - } - if (pixw < 1) pixw = 1; - if (pixh < 1) pixh = 1; - icons.push_back(StickerIcon(_sets.at(i).id, s, pixw, pixh)); - } -} - -void StickerPanInner::fillPanels(QVector &panels) { - for (int32 i = 0; i < panels.size(); ++i) { - panels.at(i)->hide(); - panels.at(i)->deleteLater(); - } - panels.clear(); - - if (_showingInlineItems) { - panels.push_back(new EmojiPanel(parentWidget(), _showingSavedGifs ? lang(lng_saved_gifs) : _inlineBotTitle, Stickers::NoneSetId, true, 0)); - panels.back()->show(); - return; - } - - if (_sets.isEmpty()) return; - - int y = 0; - panels.reserve(_sets.size()); - for (int32 i = 0, l = _sets.size(); i < l; ++i) { - bool special = (_sets.at(i).flags & MTPDstickerSet::Flag::f_official); - panels.push_back(new EmojiPanel(parentWidget(), _sets.at(i).title, _sets.at(i).id, special, y)); - panels.back()->show(); - connect(panels.back(), SIGNAL(deleteClicked(quint64)), this, SIGNAL(removing(quint64))); - int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); - y += h; - } -} - -void StickerPanInner::refreshPanels(QVector &panels) { - if (_showingInlineItems) return; - - if (panels.size() != _sets.size()) return fillPanels(panels); - - int32 y = 0; - for (int32 i = 0, l = _sets.size(); i < l; ++i) { - panels.at(i)->setWantedY(y); - int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); - y += h; - } -} - -void StickerPanInner::updateSelected() { - if (_pressedSel >= 0 && !_previewShown) { - return; - } - - int32 selIndex = -1; - QPoint p(mapFromGlobal(_lastMousePos)); - - if (_showingInlineItems) { - int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft; - int sy = p.y() - st::emojiPanHeader; - if (_switchPmButton) { - sy -= _switchPmButton->height() + st::inlineResultsSkip; - } - int row = -1, col = -1, sel = -1; - ClickHandlerPtr lnk; - ClickHandlerHost *lnkhost = nullptr; - HistoryCursorState cursor = HistoryDefaultCursorState; - if (sy >= 0) { - row = 0; - for (int rows = _inlineRows.size(); row < rows; ++row) { - if (sy < _inlineRows.at(row).height) { - break; - } - sy -= _inlineRows.at(row).height; - } - } - if (sx >= 0 && row >= 0 && row < _inlineRows.size()) { - const InlineItems &inlineItems(_inlineRows.at(row).items); - col = 0; - for (int32 cols = inlineItems.size(); col < cols; ++col) { - int32 width = inlineItems.at(col)->width(); - if (sx < width) { - break; - } - sx -= width; - if (inlineItems.at(col)->hasRightSkip()) { - sx -= st::inlineResultsSkip; - } - } - if (col < inlineItems.size()) { - sel = row * MatrixRowShift + col; - inlineItems.at(col)->getState(lnk, cursor, sx, sy); - lnkhost = inlineItems.at(col); - } else { - row = col = -1; - } - } else { - row = col = -1; - } - int32 srow = (_selected >= 0) ? (_selected / MatrixRowShift) : -1; - int32 scol = (_selected >= 0) ? (_selected % MatrixRowShift) : -1; - if (_selected != sel) { - if (srow >= 0 && scol >= 0) { - t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); - Ui::repaintInlineItem(_inlineRows.at(srow).items.at(scol)); - } - _selected = sel; - if (row >= 0 && col >= 0) { - t_assert(row >= 0 && row < _inlineRows.size() && col >= 0 && col < _inlineRows.at(row).items.size()); - Ui::repaintInlineItem(_inlineRows.at(row).items.at(col)); - } - if (_pressedSel >= 0 && _selected >= 0 && _pressedSel != _selected) { - _pressedSel = _selected; - if (row >= 0 && col >= 0) { - auto layout = _inlineRows.at(row).items.at(col); - if (auto previewDocument = layout->getPreviewDocument()) { - Ui::showMediaPreview(previewDocument); - } else if (auto previewPhoto = layout->getPreviewPhoto()) { - Ui::showMediaPreview(previewPhoto); - } - } - } - } - if (ClickHandler::setActive(lnk, lnkhost)) { - setCursor(lnk ? style::cur_pointer : style::cur_default); - } - return; - } - - int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding; - for (int c = 0, l = _sets.size(); c < l; ++c) { - const DisplayedSet &set(_sets.at(c)); - int cnt = set.pack.size(); - bool special = (set.flags & MTPDstickerSet::Flag::f_official); - - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); - if (p.y() >= y && p.y() < ytill) { - y += st::emojiPanHeader; - if (p.y() >= y && sx >= 0 && sx < StickerPanPerRow * st::stickerPanSize.width()) { - selIndex = qFloor((p.y() - y) / st::stickerPanSize.height()) * StickerPanPerRow + qFloor(sx / st::stickerPanSize.width()); - if (selIndex >= set.pack.size()) { - selIndex = -1; - } else { - if (set.id == Stickers::RecentSetId && _custom[selIndex]) { - int32 inx = sx - (selIndex % StickerPanPerRow) * st::stickerPanSize.width(), iny = p.y() - y - ((selIndex / StickerPanPerRow) * st::stickerPanSize.height()); - if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.pxWidth() && iny < st::stickerPanDelete.pxHeight()) { - selIndex += set.pack.size(); - } - } - selIndex += c * MatrixRowShift; - } - } - break; - } - } - - bool startanim = false; - int oldSel = _selected, oldSelTab = oldSel / MatrixRowShift, xOldSel = -1, newSel = selIndex, newSelTab = newSel / MatrixRowShift, xNewSel = -1; - if (oldSel >= 0 && oldSelTab < _sets.size() && _sets[oldSelTab].id == Stickers::RecentSetId && oldSel >= oldSelTab * MatrixRowShift + _sets[oldSelTab].pack.size()) { - xOldSel = oldSel; - oldSel -= _sets[oldSelTab].pack.size(); - } - if (newSel >= 0 && newSelTab < _sets.size() && _sets[newSelTab].id == Stickers::RecentSetId && newSel >= newSelTab * MatrixRowShift + _sets[newSelTab].pack.size()) { - xNewSel = newSel; - newSel -= _sets[newSelTab].pack.size(); - } - if (newSel != oldSel) { - if (oldSel >= 0) { - _animations.remove(oldSel + 1); - if (_animations.find(-oldSel - 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(-oldSel - 1, getms()); - } - } - if (newSel >= 0) { - _animations.remove(-newSel - 1); - if (_animations.find(newSel + 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(newSel + 1, getms()); - } - } - setCursor((newSel >= 0) ? style::cur_pointer : style::cur_default); - } - if (xNewSel != xOldSel) { - if (xOldSel >= 0) { - _animations.remove(xOldSel + 1); - if (_animations.find(-xOldSel - 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(-xOldSel - 1, getms()); - } - } - if (xNewSel >= 0) { - _animations.remove(-xNewSel - 1); - if (_animations.find(xNewSel + 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(xNewSel + 1, getms()); - } - } - } - _selected = selIndex; - if (_pressedSel >= 0 && _selected >= 0 && _pressedSel != _selected) { - _pressedSel = _selected; - if (newSel >= 0 && xNewSel < 0) { - Ui::showMediaPreview(_sets.at(newSelTab).pack.at(newSel % MatrixRowShift)); - } - } - if (startanim && !_a_selected.animating()) _a_selected.start(); -} - -void StickerPanInner::onSettings() { - Ui::showLayer(new StickersBox()); -} - -void StickerPanInner::onPreview() { - if (_pressedSel < 0) return; - if (_showingInlineItems) { - int32 row = _pressedSel / MatrixRowShift, col = _pressedSel % MatrixRowShift; - if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { - auto layout = _inlineRows.at(row).items.at(col); - if (auto previewDocument = layout->getPreviewDocument()) { - Ui::showMediaPreview(previewDocument); - _previewShown = true; - } else if (auto previewPhoto = layout->getPreviewPhoto()) { - Ui::showMediaPreview(previewPhoto); - _previewShown = true; - } - } - } else if (_pressedSel < MatrixRowShift * _sets.size()) { - int tab = (_pressedSel / MatrixRowShift), sel = _pressedSel % MatrixRowShift; - if (sel < _sets.at(tab).pack.size()) { - Ui::showMediaPreview(_sets.at(tab).pack.at(sel)); - _previewShown = true; - } - } -} - -void StickerPanInner::onUpdateInlineItems() { - if (!_showingInlineItems) return; - - uint64 ms = getms(); - if (_lastScrolled + 100 <= ms) { - update(); - } else { - _updateInlineItems.start(_lastScrolled + 100 - ms); - } -} - -void StickerPanInner::onSwitchPm() { - if (_inlineBot && _inlineBot->botInfo) { - _inlineBot->botInfo->startToken = _switchPmStartToken; - Ui::showPeerHistory(_inlineBot, ShowAndStartBotMsgId); - } -} - -void StickerPanInner::step_selected(uint64 ms, bool timer) { - QRegion toUpdate; - for (Animations::iterator i = _animations.begin(); i != _animations.end();) { - int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - float64 dt = float64(ms - i.value()) / st::emojiPanDuration; - if (dt >= 1) { - _sets[tab].hovers[sel] = (i.key() > 0) ? 1 : 0; - i = _animations.erase(i); - } else { - _sets[tab].hovers[sel] = (i.key() > 0) ? dt : (1 - dt); - ++i; - } - toUpdate += stickerRect(tab, sel); - } - if (timer) rtlupdate(toUpdate.boundingRect()); - if (_animations.isEmpty()) _a_selected.stop(); -} - -void StickerPanInner::showStickerSet(uint64 setId) { - clearSelection(true); - - if (setId == Stickers::NoneSetId) { - bool wasNotShowingGifs = !_showingInlineItems; - if (wasNotShowingGifs) { - _showingInlineItems = true; - _showingSavedGifs = true; - cSetShowingSavedGifs(true); - emit saveConfigDelayed(SaveRecentEmojisTimeout); - } - refreshSavedGifs(); - emit scrollToY(0); - emit scrollUpdated(); - showFinish(); - return; - } - - if (_showingInlineItems) { - if (_setGifCommand && _showingSavedGifs) { - App::insertBotCommand(qsl(""), true); - } - _setGifCommand = false; - - _showingInlineItems = false; - cSetShowingSavedGifs(false); - emit saveConfigDelayed(SaveRecentEmojisTimeout); - - Notify::clipStopperHidden(ClipStopperSavedGifsPanel); - - refreshRecentStickers(true); - emit refreshIcons(); - } - - int32 y = 0; - for (int c = 0; c < _sets.size(); ++c) { - if (_sets.at(c).id == setId) break; - int rows = (_sets[c].pack.size() / StickerPanPerRow) + ((_sets[c].pack.size() % StickerPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::stickerPanSize.height(); - } - - emit scrollToY(y); - emit scrollUpdated(); - - _lastMousePos = QCursor::pos(); - - update(); -} - -void StickerPanInner::updateShowingSavedGifs() { - if (cShowingSavedGifs()) { - if (!_showingInlineItems) { - clearSelection(true); - _showingSavedGifs = _showingInlineItems = true; - if (_inlineRows.isEmpty()) refreshSavedGifs(); - } - } else if (!_showingInlineItems) { - clearSelection(true); - _showingSavedGifs = false; - } -} - -void StickerPanInner::showFinish() { - if (_showingInlineItems && _showingSavedGifs) { - _setGifCommand = App::insertBotCommand('@' + cInlineGifBotUsername(), true); - } -} - -EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent) -, _wantedY(wantedY) -, _setId(setId) -, _special(special) -, _deleteVisible(false) -, _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // Stickers::NoneSetId if in emoji - resize(st::emojiPanWidth, st::emojiPanHeader); - setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); - setText(text); - if (_delete) { - _delete->hide(); - _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::notifyClose.icon.pxWidth()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); - connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); - } -} - -void EmojiPanel::onDelete() { - emit deleteClicked(_setId); -} - -void EmojiPanel::setText(const QString &text) { - _fullText = text; - updateText(); -} - -void EmojiPanel::updateText() { - int32 availw = st::emojiPanWidth - st::emojiPanHeaderLeft * 2; - if (_deleteVisible) { - if (!_special && _setId != Stickers::NoneSetId) { - availw -= st::notifyClose.icon.pxWidth() + st::emojiPanHeaderLeft; - } - } else { - auto switchText = ([this]() { - if (_setId != Stickers::NoneSetId) { - return lang(lng_switch_emoji); - } - if (cSavedGifs().isEmpty()) { - return lang(lng_switch_stickers); - } - return lang(lng_switch_stickers_gifs); - })(); - availw -= st::emojiSwitchSkip + st::emojiPanHeaderFont->width(switchText); - } - _text = st::emojiPanHeaderFont->elided(_fullText, availw); - update(); -} - -void EmojiPanel::setDeleteVisible(bool isVisible) { - if (_deleteVisible != isVisible) { - _deleteVisible = isVisible; - updateText(); - if (_delete) { - _delete->setVisible(_deleteVisible); - } - } -} - -void EmojiPanel::mousePressEvent(QMouseEvent *e) { - emit mousePressed(); -} - -void EmojiPanel::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (!_deleteVisible) { - p.fillRect(0, 0, width(), st::emojiPanHeader, st::emojiPanHeaderBg->b); - } - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiPanHeaderColor->p); - p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); -} - -EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent) -, _toStickers(toStickers) { - setCursor(style::cur_pointer); - updateText(); -} - -void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { - if (_toStickers) { - if (inlineBotUsername.isEmpty()) { - _text = lang(cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs); - } else { - _text = '@' + inlineBotUsername; - } - } else { - _text = lang(lng_switch_emoji); - } - _textWidth = st::emojiPanHeaderFont->width(_text); - if (_toStickers && !inlineBotUsername.isEmpty()) { - int32 maxw = 0; - for (int c = 0; c < emojiTabCount; ++c) { - maxw = qMax(maxw, st::emojiPanHeaderFont->width(lang(LangKey(lng_emoji_category0 + c)))); - } - maxw += st::emojiPanHeaderLeft + st::emojiSwitchSkip + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - if (_textWidth > st::emojiPanWidth - maxw) { - _text = st::emojiPanHeaderFont->elided(_text, st::emojiPanWidth - maxw); - _textWidth = st::emojiPanHeaderFont->width(_text); - } - } - - int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - resize(w, st::emojiPanHeader); -} - -void EmojiSwitchButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiSwitchColor->p); - if (_toStickers) { - p.drawTextRight(st::emojiSwitchSkip, st::emojiPanHeaderTop, width(), _text, _textWidth); - p.drawSpriteRight(QPoint(st::emojiSwitchImgSkip - st::emojiSwitchStickers.pxWidth(), (st::emojiPanHeader - st::emojiSwitchStickers.pxHeight()) / 2), width(), st::emojiSwitchStickers); - } else { - p.drawTextRight(st::emojiSwitchImgSkip - st::emojiSwitchEmoji.pxWidth(), st::emojiPanHeaderTop, width(), lang(lng_switch_emoji), _textWidth); - p.drawSpriteRight(QPoint(st::emojiSwitchSkip + _textWidth - st::emojiSwitchEmoji.pxWidth(), (st::emojiPanHeader - st::emojiSwitchEmoji.pxHeight()) / 2), width(), st::emojiSwitchEmoji); - } -} - -} // namespace internal - -EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) -, _maxHeight(st::emojiPanMaxHeight) -, _contentMaxHeight(st::emojiPanMaxHeight) -, _contentHeight(_contentMaxHeight) -, _contentHeightEmoji(_contentHeight - st::rbEmoji.height) -, _contentHeightStickers(_contentHeight - st::rbEmoji.height) -, _horizontal(false) -, _noTabUpdate(false) -, _hiding(false) -, a_opacity(0) -, _a_appearance(animation(this, &EmojiPan::step_appearance)) -, _shadow(st::dropdownDef.shadow) -, _recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent) -, _people(this , qsl("emoji_group"), dbietPeople , QString(), false, st::rbEmojiPeople) -, _nature(this , qsl("emoji_group"), dbietNature , QString(), false, st::rbEmojiNature) -, _food(this , qsl("emoji_group"), dbietFood , QString(), false, st::rbEmojiFood) -, _activity(this, qsl("emoji_group"), dbietActivity, QString(), false, st::rbEmojiActivity) -, _travel(this , qsl("emoji_group"), dbietTravel , QString(), false, st::rbEmojiTravel) -, _objects(this , qsl("emoji_group"), dbietObjects , QString(), false, st::rbEmojiObjects) -, _symbols(this , qsl("emoji_group"), dbietSymbols , QString(), false, st::rbEmojiSymbols) -, _iconOver(-1) -, _iconSel(0) -, _iconDown(-1) -, _iconsDragging(false) -, _a_icons(animation(this, &EmojiPan::step_icons)) -, _iconsLeft(0) -, _iconsTop(0) -, _iconsStartX(0) -, _iconsMax(0) -, _iconsX(0, 0) -, _iconSelX(0, 0) -, _iconsStartAnim(0) -, _stickersShown(false) -, _shownFromInlineQuery(false) -, _a_slide(animation(this, &EmojiPan::step_slide)) -, e_scroll(this, st::emojiScroll) -, e_inner() -, e_switch(&e_scroll, true) -, s_scroll(this, st::emojiScroll) -, s_inner() -, s_switch(&s_scroll, false) -, _removingSetId(0) -, _inlineBot(0) -, _inlineRequestId(0) { - setFocusPolicy(Qt::NoFocus); - e_scroll.setFocusPolicy(Qt::NoFocus); - e_scroll.viewport()->setFocusPolicy(Qt::NoFocus); - s_scroll.setFocusPolicy(Qt::NoFocus); - s_scroll.viewport()->setFocusPolicy(Qt::NoFocus); - - _width = st::dropdownDef.padding.left() + st::emojiPanWidth + st::dropdownDef.padding.right(); - _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); - _bottom = 0; - resize(_width, _height); - - e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); - - e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); - e_scroll.setWidget(&e_inner); - s_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); - s_scroll.setWidget(&s_inner); - - e_inner.moveToLeft(0, 0, e_scroll.width()); - s_inner.moveToLeft(0, 0, s_scroll.width()); - - int32 left = _iconsLeft = st::dropdownDef.padding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2; - int32 top = _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; - prepareTab(left, top, _width, _recent); - prepareTab(left, top, _width, _people); - prepareTab(left, top, _width, _nature); - prepareTab(left, top, _width, _food); - prepareTab(left, top, _width, _activity); - prepareTab(left, top, _width, _travel); - prepareTab(left, top, _width, _objects); - prepareTab(left, top, _width, _symbols); - e_inner.fillPanels(e_panels); - updatePanelsPositions(e_panels, 0); - - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); - - connect(&e_inner, SIGNAL(scrollToY(int)), &e_scroll, SLOT(scrollToY(int))); - connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool))); - - connect(&s_inner, SIGNAL(scrollToY(int)), &s_scroll, SLOT(scrollToY(int))); - connect(&s_inner, SIGNAL(scrollUpdated()), this, SLOT(onScrollStickers())); - - connect(&e_scroll, SIGNAL(scrolled()), this, SLOT(onScrollEmoji())); - connect(&s_scroll, SIGNAL(scrolled()), this, SLOT(onScrollStickers())); - - connect(&e_inner, SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr))); - connect(&s_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*))); - connect(&s_inner, SIGNAL(selected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*))); - connect(&s_inner, SIGNAL(selected(InlineBots::Result*,UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*))); - - connect(&s_inner, SIGNAL(emptyInlineRows()), this, SLOT(onEmptyInlineRows())); - - connect(&s_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); - connect(&e_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); - s_switch.moveToRight(0, 0, st::emojiPanWidth); - e_switch.moveToRight(0, 0, st::emojiPanWidth); - - connect(&s_inner, SIGNAL(removing(quint64)), this, SLOT(onRemoveSet(quint64))); - connect(&s_inner, SIGNAL(refreshIcons()), this, SLOT(onRefreshIcons())); - connect(&e_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); - connect(&s_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); - - _saveConfigTimer.setSingleShot(true); - connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig())); - connect(&e_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); - connect(&s_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); - - // inline bots - _inlineRequestTimer.setSingleShot(true); - connect(&_inlineRequestTimer, SIGNAL(timeout()), this, SLOT(onInlineRequest())); - - if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { - connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); - } - - setMouseTracking(true); -// setAttribute(Qt::WA_AcceptTouchEvents); -} - -void EmojiPan::setMaxHeight(int32 h) { - _maxHeight = h; - updateContentHeight(); -} - -void EmojiPan::updateContentHeight() { - int32 h = qMin(_contentMaxHeight, _maxHeight); - int32 he = h - st::rbEmoji.height; - int32 hs = h - (s_inner.showSectionIcons() ? st::rbEmoji.height : 0); - if (h == _contentHeight && he == _contentHeightEmoji && hs == _contentHeightStickers) return; - - int32 was = _contentHeight, wase = _contentHeightEmoji, wass = _contentHeightStickers; - _contentHeight = h; - _contentHeightEmoji = he; - _contentHeightStickers = hs; - - _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); - - resize(_width, _height); - move(x(), _bottom - height()); - - if (was > _contentHeight || (was == _contentHeight && wass > _contentHeightStickers)) { - e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); - s_inner.setMaxHeight(_contentHeightStickers); - e_inner.setMaxHeight(_contentHeightEmoji); - } else { - s_inner.setMaxHeight(_contentHeightStickers); - e_inner.setMaxHeight(_contentHeightEmoji); - e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); - } - - _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; - _recent.move(_recent.x(), _iconsTop); - _people.move(_people.x(), _iconsTop); - _nature.move(_nature.x(), _iconsTop); - _food.move(_food.x(), _iconsTop); - _activity.move(_activity.x(), _iconsTop); - _travel.move(_travel.x(), _iconsTop); - _objects.move(_objects.x(), _iconsTop); - _symbols.move(_symbols.x(), _iconsTop); - - update(); -} - -void EmojiPan::prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab) { - tab.moveToLeft(left, top, _width); - left += tab.width(); - tab.setAttribute(Qt::WA_OpaquePaintEvent); - connect(&tab, SIGNAL(changed()), this, SLOT(onTabChange())); -} - -void EmojiPan::onWndActiveChanged() { - if (!App::wnd()->windowHandle()->isActive() && !isHidden()) { - leaveEvent(0); - } -} - -void EmojiPan::onSaveConfig() { - Local::writeUserSettings(); -} - -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); - - float64 o = 1; - if (!_cache.isNull()) { - p.setOpacity(o = a_opacity.current()); - } - - QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); - - _shadow.paint(p, r, st::dropdownDef.shadowShift); - - if (_toCache.isNull()) { - if (_cache.isNull()) { - 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); - paintStickerSettingsIcon(p); - - if (!_icons.isEmpty()) { - int32 x = _iconsLeft, i = 0, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current(); - for (int32 l = _icons.size(); i < l && !_icons.at(i).sticker; ++i) { - bool gifs = (_icons.at(i).setId == Stickers::NoneSetId); - if (selxrel != x) { - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), gifs ? st::savedGifsOver : st::rbEmojiRecent.imageRect); - } - if (selxrel < x + st::rbEmoji.width && selxrel > x - st::rbEmoji.width) { - p.setOpacity(1 - (qAbs(selxrel - x) / st::rbEmoji.width)); - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), gifs ? st::savedGifsActive : st::rbEmojiRecent.chkImageRect); - p.setOpacity(1); - } - x += st::rbEmoji.width; - } - int32 skip = i; - - QRect clip(x, _iconsTop, _iconsLeft + 7 * st::rbEmoji.width - x, st::rbEmoji.height); - if (rtl()) clip.moveLeft(width() - x - clip.width()); - p.setClipRect(clip); - - i += _iconsX.current() / int(st::rbEmoji.width); - x -= _iconsX.current() % int(st::rbEmoji.width); - for (int32 l = qMin(_icons.size(), i + 8 - skip); i < l; ++i) { - const internal::StickerIcon &s(_icons.at(i)); - s.sticker->thumb->load(); - QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh)); - - p.drawPixmapLeft(x + (st::rbEmoji.width - s.pixw) / 2, _iconsTop + (st::rbEmoji.height - s.pixh) / 2, width(), pix); - x += st::rbEmoji.width; - } - - if (rtl()) selx = width() - selx - st::rbEmoji.width; - p.setOpacity(skip ? qMax(1., selx / float64(skip * st::rbEmoji.width)) : 1.); - p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor); - - float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.); - if (o_left > 0) { - p.setOpacity(o_left); - p.drawSpriteLeft(QRect(_iconsLeft + skip * st::rbEmoji.width, _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft); - } - float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.pxWidth(), 0., 1.); - if (o_right > 0) { - p.setOpacity(o_right); - p.drawSpriteRight(QRect(width() - _iconsLeft - 7 * st::rbEmoji.width, _iconsTop, st::stickerIconRight.pxWidth(), st::rbEmoji.height), width(), st::stickerIconRight); - } - } - } else if (_stickersShown) { - int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width()); - p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::white); - } else { - p.fillRect(r.left(), _recent.y(), (rtl() ? _objects.x() : _recent.x() - r.left()), st::rbEmoji.height, st::emojiPanCategories); - int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width()); - p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::emojiPanCategories); - } - } else { - p.fillRect(r, st::white->b); - p.drawPixmap(r.left(), r.top(), _cache); - } - } else { - p.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::rbEmoji.height), st::white->b); - p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height), st::emojiPanCategories->b); - p.setOpacity(o * a_fromAlpha.current()); - QRect fromDst = QRect(r.left() + a_fromCoord.current(), r.top(), _fromCache.width() / cIntRetinaFactor(), _fromCache.height() / cIntRetinaFactor()); - QRect fromSrc = QRect(0, 0, _fromCache.width(), _fromCache.height()); - if (fromDst.x() < r.left() + r.width() && fromDst.x() + fromDst.width() > r.left()) { - if (fromDst.x() < r.left()) { - fromSrc.setX((r.left() - fromDst.x()) * cIntRetinaFactor()); - fromDst.setX(r.left()); - } else if (fromDst.x() + fromDst.width() > r.left() + r.width()) { - fromSrc.setWidth((r.left() + r.width() - fromDst.x()) * cIntRetinaFactor()); - fromDst.setWidth(r.left() + r.width() - fromDst.x()); - } - p.drawPixmap(fromDst, _fromCache, fromSrc); - } - p.setOpacity(o * a_toAlpha.current()); - QRect toDst = QRect(r.left() + a_toCoord.current(), r.top(), _toCache.width() / cIntRetinaFactor(), _toCache.height() / cIntRetinaFactor()); - QRect toSrc = QRect(0, 0, _toCache.width(), _toCache.height()); - if (toDst.x() < r.left() + r.width() && toDst.x() + toDst.width() > r.left()) { - if (toDst.x() < r.left()) { - toSrc.setX((r.left() - toDst.x()) * cIntRetinaFactor()); - toDst.setX(r.left()); - } else if (toDst.x() + toDst.width() > r.left() + r.width()) { - toSrc.setWidth((r.left() + r.width() - toDst.x()) * cIntRetinaFactor()); - toDst.setWidth(r.left() + r.width() - toDst.x()); - } - p.drawPixmap(toDst, _toCache, toSrc); - } - } -} - -void EmojiPan::moveBottom(int32 bottom, bool force) { - _bottom = bottom; - if (isHidden() && !force) { - move(x(), _bottom - height()); - return; - } - if (_stickersShown && s_inner.inlineResultsShown()) { - moveToLeft(0, _bottom - height()); - } else { - moveToRight(0, _bottom - height()); - } -} - -void EmojiPan::enterEvent(QEvent *e) { - _hideTimer.stop(); - if (_hiding) showStart(); -} - -void EmojiPan::leaveEvent(QEvent *e) { - if (_removingSetId || s_inner.inlineResultsShown()) return; - if (_a_appearance.animating()) { - hideStart(); - } else { - _hideTimer.start(300); - } -} - -void EmojiPan::otherEnter() { - _hideTimer.stop(); - showStart(); -} - -void EmojiPan::otherLeave() { - if (_removingSetId || s_inner.inlineResultsShown()) return; - if (_a_appearance.animating()) { - hideStart(); - } else { - _hideTimer.start(0); - } -} - -void EmojiPan::mousePressEvent(QMouseEvent *e) { - if (!_stickersShown) return; - _iconsMousePos = e ? e->globalPos() : QCursor::pos(); - updateSelected(); - - if (_iconOver == _icons.size()) { - Ui::showLayer(new StickersBox()); - } else { - _iconDown = _iconOver; - _iconsMouseDown = _iconsMousePos; - _iconsStartX = _iconsX.current(); - } -} - -void EmojiPan::mouseMoveEvent(QMouseEvent *e) { - if (!_stickersShown) return; - _iconsMousePos = e ? e->globalPos() : QCursor::pos(); - updateSelected(); - - int32 skip = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).sticker) break; - ++skip; - } - if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= skip) { - if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) { - _iconsDragging = true; - } - } - if (_iconsDragging) { - int32 newX = snap(_iconsStartX + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()), 0, _iconsMax); - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); - _iconsStartAnim = 0; - if (_iconAnimations.isEmpty()) _a_icons.stop(); - updateIcons(); - } - } -} - -void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { - if (!_stickersShown || _icons.isEmpty()) return; - - int32 wasDown = _iconDown; - _iconDown = -1; - - _iconsMousePos = e ? e->globalPos() : QCursor::pos(); - if (_iconsDragging) { - int32 newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax); - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); - _iconsStartAnim = 0; - if (_iconAnimations.isEmpty()) _a_icons.stop(); - updateIcons(); - } - _iconsDragging = false; - updateSelected(); - } else { - updateSelected(); - - if (wasDown == _iconOver && _iconOver >= 0 && _iconOver < _icons.size()) { - _iconSelX = anim::ivalue(_iconOver * st::rbEmoji.width, _iconOver * st::rbEmoji.width); - s_inner.showStickerSet(_icons.at(_iconOver).setId); - } - } -} - -bool EmojiPan::event(QEvent *e) { - if (e->type() == QEvent::TouchBegin) { - - } else if (e->type() == QEvent::Wheel) { - int32 skip = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).sticker) break; - ++skip; - } - if (!_icons.isEmpty() && _iconOver >= skip && _iconOver < _icons.size() && _iconDown < 0) { - QWheelEvent *ev = static_cast(e); - bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal); - bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical); - if (hor) _horizontal = true; - int32 newX = _iconsX.current(); - if (/*_horizontal && */hor) { - newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax); - } else if (/*!_horizontal && */ver) { - newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax); - } - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); - _iconsStartAnim = 0; - if (_iconAnimations.isEmpty()) _a_icons.stop(); - updateSelected(); - updateIcons(); - } - } - } - return TWidget::event(e); -} - -void EmojiPan::fastHide() { - if (_a_appearance.animating()) { - _a_appearance.stop(); - } - a_opacity = anim::fvalue(0, 0); - _hideTimer.stop(); - hide(); - _cache = QPixmap(); -} - -void EmojiPan::refreshStickers() { - s_inner.refreshStickers(); - if (!_stickersShown) { - s_inner.preloadImages(); - } - update(); -} - -void EmojiPan::refreshSavedGifs() { - e_switch.updateText(); - e_switch.moveToRight(0, 0, st::emojiPanWidth); - s_inner.refreshSavedGifs(); - if (!_stickersShown) { - s_inner.preloadImages(); - } -} - -void EmojiPan::onRefreshIcons() { - _iconOver = -1; - _iconHovers.clear(); - _iconAnimations.clear(); - s_inner.fillIcons(_icons); - s_inner.fillPanels(s_panels); - _iconsX = anim::ivalue(0, 0); - _iconSelX.finish(); - _iconsStartAnim = 0; - _a_icons.stop(); - if (_icons.isEmpty()) { - _iconsMax = 0; - } else { - _iconHovers = QVector(_icons.size(), 0); - _iconsMax = qMax(int((_icons.size() - 7) * st::rbEmoji.width), 0); - } - updatePanelsPositions(s_panels, s_scroll.scrollTop()); - updateSelected(); - if (_stickersShown) { - validateSelectedIcon(); - updateContentHeight(); - } - updateIcons(); -} - -void EmojiPan::onRefreshPanels() { - s_inner.refreshPanels(s_panels); - e_inner.refreshPanels(e_panels); - if (_stickersShown) { - updatePanelsPositions(s_panels, s_scroll.scrollTop()); - } else { - updatePanelsPositions(e_panels, e_scroll.scrollTop()); - } -} - -void EmojiPan::leaveToChildEvent(QEvent *e, QWidget *child) { - if (!_stickersShown) return; - _iconsMousePos = QCursor::pos(); - updateSelected(); -} - -void EmojiPan::updateSelected() { - if (_iconDown >= 0) { - return; - } - - QPoint p(mapFromGlobal(_iconsMousePos)); - int32 x = p.x(), y = p.y(), newOver = -1; - if (rtl()) x = width() - x; - x -= _iconsLeft; - if (x >= st::rbEmoji.width * 7 && x < st::rbEmoji.width * 8 && y >= _iconsTop && y < _iconsTop + st::rbEmoji.height) { - newOver = _icons.size(); - } else if (!_icons.isEmpty()) { - if (y >= _iconsTop && y < _iconsTop + st::rbEmoji.height && x >= 0 && x < 7 * st::rbEmoji.width && x < _icons.size() * st::rbEmoji.width) { - int32 skip = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).sticker) break; - if (x < st::rbEmoji.width) { - newOver = i; - break; - } - x -= st::rbEmoji.width; - ++skip; - } - if (newOver < 0) { - x += _iconsX.current(); - newOver = qFloor(x / st::rbEmoji.width) + skip; - } - } - } - if (newOver != _iconOver) { - if (newOver < 0) { - setCursor(style::cur_default); - } else if (_iconOver < 0) { - setCursor(style::cur_pointer); - } - bool startanim = false; - if (_iconOver >= 0 && _iconOver < _icons.size()) { - _iconAnimations.remove(_iconOver + 1); - if (_iconAnimations.find(-_iconOver - 1) == _iconAnimations.end()) { - if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true; - _iconAnimations.insert(-_iconOver - 1, getms()); - } - } - _iconOver = newOver; - if (_iconOver >= 0 && _iconOver < _icons.size()) { - _iconAnimations.remove(-_iconOver - 1); - if (_iconAnimations.find(_iconOver + 1) == _iconAnimations.end()) { - if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true; - _iconAnimations.insert(_iconOver + 1, getms()); - } - } - if (startanim && !_a_icons.animating()) _a_icons.start(); - } -} - -void EmojiPan::updateIcons() { - if (!_stickersShown || !s_inner.showSectionIcons()) return; - - QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); - update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); -} - -void EmojiPan::step_icons(uint64 ms, bool timer) { - if (!_stickersShown) { - _a_icons.stop(); - return; - } - - for (Animations::iterator i = _iconAnimations.begin(); i != _iconAnimations.end();) { - int index = qAbs(i.key()) - 1; - float64 dt = float64(ms - i.value()) / st::emojiPanDuration; - if (index >= _iconHovers.size()) { - i = _iconAnimations.erase(i); - } else if (dt >= 1) { - _iconHovers[index] = (i.key() > 0) ? 1 : 0; - i = _iconAnimations.erase(i); - } else { - _iconHovers[index] = (i.key() > 0) ? dt : (1 - dt); - ++i; - } - } - - if (_iconsStartAnim) { - float64 dt = (ms - _iconsStartAnim) / float64(st::stickerIconMove); - if (dt >= 1) { - _iconsStartAnim = 0; - _iconsX.finish(); - _iconSelX.finish(); - } else { - _iconsX.update(dt, anim::linear); - _iconSelX.update(dt, anim::linear); - } - if (timer) updateSelected(); - } - - if (timer) updateIcons(); - - if (_iconAnimations.isEmpty() && !_iconsStartAnim) { - _a_icons.stop(); - } -} - -void EmojiPan::step_slide(float64 ms, bool timer) { - float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration; - float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0; - if (dt2 >= 1) { - _a_slide.stop(); - a_fromCoord.finish(); - a_fromAlpha.finish(); - a_toCoord.finish(); - a_toAlpha.finish(); - _fromCache = _toCache = QPixmap(); - if (_cache.isNull()) showAll(); - } else { - a_fromCoord.update(dt1, st::introHideFunc); - a_fromAlpha.update(dt1, st::introAlphaHideFunc); - a_toCoord.update(dt2, st::introShowFunc); - a_toAlpha.update(dt2, st::introAlphaShowFunc); - } - if (timer) update(); -} - -void EmojiPan::step_appearance(float64 ms, bool timer) { - if (_cache.isNull()) { - _a_appearance.stop(); - return; - } - - float64 dt = ms / st::dropdownDef.duration; - if (dt >= 1) { - _a_appearance.stop(); - a_opacity.finish(); - if (_hiding) { - hideFinish(); - } else { - _cache = QPixmap(); - if (_toCache.isNull()) showAll(); - } - } else { - a_opacity.update(dt, anim::linear); - } - if (timer) update(); -} - -void EmojiPan::hideStart() { - if (_removingSetId || s_inner.inlineResultsShown()) return; - - hideAnimated(); -} - -void EmojiPan::prepareShowHideCache() { - if (_cache.isNull()) { - QPixmap from = _fromCache, to = _toCache; - _fromCache = _toCache = QPixmap(); - showAll(); - _cache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); - _fromCache = from; _toCache = to; - } -} - -void EmojiPan::hideAnimated() { - if (_hiding) return; - - prepareShowHideCache(); - hideAll(); - _hiding = true; - a_opacity.start(0); - _a_appearance.start(); -} - -void EmojiPan::hideFinish() { - hide(); - e_inner.hideFinish(); - s_inner.hideFinish(true); - _cache = _toCache = _fromCache = QPixmap(); - _a_slide.stop(); - _horizontal = false; - _hiding = false; - - e_scroll.scrollToY(0); - if (!_recent.checked()) { - _noTabUpdate = true; - _recent.setChecked(true); - _noTabUpdate = false; - } - s_scroll.scrollToY(0); - _iconOver = _iconDown = -1; - _iconSel = 0; - _iconsX = anim::ivalue(0, 0); - _iconSelX = anim::ivalue(0, 0); - _iconsStartAnim = 0; - _a_icons.stop(); - _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); - _iconAnimations.clear(); - - Notify::clipStopperHidden(ClipStopperSavedGifsPanel); -} - -void EmojiPan::showStart() { - if (!isHidden() && !_hiding) { - return; - } - if (isHidden()) { - e_inner.refreshRecent(); - if (s_inner.inlineResultsShown() && refreshInlineRows()) { - _stickersShown = true; - _shownFromInlineQuery = true; - } else { - s_inner.refreshRecent(); - _stickersShown = false; - _shownFromInlineQuery = false; - _cache = QPixmap(); // clear after refreshInlineRows() - } - recountContentMaxHeight(); - s_inner.preloadImages(); - _fromCache = _toCache = QPixmap(); - _a_slide.stop(); - moveBottom(y() + height(), true); - } else if (_hiding) { - if (s_inner.inlineResultsShown() && refreshInlineRows()) { - onSwitch(); - } - } - prepareShowHideCache(); - hideAll(); - _hiding = false; - show(); - a_opacity.start(1); - _a_appearance.start(); - emit updateStickers(); -} - -bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { - if (e->type() == QEvent::Enter) { - //if (dynamic_cast(obj)) { - // enterEvent(e); - //} else { - otherEnter(); - //} - } else if (e->type() == QEvent::Leave) { - //if (dynamic_cast(obj)) { - // leaveEvent(e); - //} else { - otherLeave(); - //} - } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) { - if (isHidden() || _hiding) { - _hideTimer.stop(); - showStart(); - } else { - hideAnimated(); - } - } - return false; -} - -void EmojiPan::stickersInstalled(uint64 setId) { - _stickersShown = true; - if (isHidden()) { - moveBottom(y() + height(), true); - show(); - a_opacity = anim::fvalue(0, 1); - a_opacity.update(0, anim::linear); - _cache = _fromCache = _toCache = QPixmap(); - } - showAll(); - s_inner.showStickerSet(setId); - updateContentHeight(); - showStart(); -} - -void EmojiPan::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { - s_inner.notify_inlineItemLayoutChanged(layout); - } -} - -void EmojiPan::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { - s_inner.ui_repaintInlineItem(layout); - } -} - -bool EmojiPan::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { - return s_inner.ui_isInlineItemVisible(layout); - } - return false; -} - -bool EmojiPan::ui_isInlineItemBeingChosen() { - if (_stickersShown && !isHidden()) { - return s_inner.ui_isInlineItemBeingChosen(); - } - return false; -} - -void EmojiPan::showAll() { - if (_stickersShown) { - s_scroll.show(); - _recent.hide(); - _people.hide(); - _nature.hide(); - _food.hide(); - _activity.hide(); - _travel.hide(); - _objects.hide(); - _symbols.hide(); - e_scroll.hide(); - } else { - s_scroll.hide(); - _recent.show(); - _people.show(); - _nature.show(); - _food.show(); - _activity.show(); - _travel.show(); - _objects.show(); - _symbols.show(); - e_scroll.show(); - } -} - -void EmojiPan::hideAll() { - _recent.hide(); - _people.hide(); - _nature.hide(); - _food.hide(); - _activity.hide(); - _travel.hide(); - _objects.hide(); - _symbols.hide(); - e_scroll.hide(); - s_scroll.hide(); - e_inner.clearSelection(true); - s_inner.clearSelection(true); -} - -void EmojiPan::onTabChange() { - if (_noTabUpdate) return; - DBIEmojiTab newTab = dbietRecent; - if (_people.checked()) newTab = dbietPeople; - else if (_nature.checked()) newTab = dbietNature; - else if (_food.checked()) newTab = dbietFood; - else if (_activity.checked()) newTab = dbietActivity; - else if (_travel.checked()) newTab = dbietTravel; - else if (_objects.checked()) newTab = dbietObjects; - else if (_symbols.checked()) newTab = dbietSymbols; - e_inner.showEmojiPack(newTab); -} - -void EmojiPan::updatePanelsPositions(const QVector &panels, int32 st) { - for (int32 i = 0, l = panels.size(); i < l; ++i) { - int32 y = panels.at(i)->wantedY() - st; - if (y < 0) { - y = (i + 1 < l) ? qMin(panels.at(i + 1)->wantedY() - st - int(st::emojiPanHeader), 0) : 0; - } - panels.at(i)->move(0, y); - panels.at(i)->setDeleteVisible(y >= st::emojiPanHeader); - } -} - -void EmojiPan::onScrollEmoji() { - auto st = e_scroll.scrollTop(); - - updatePanelsPositions(e_panels, st); - - auto tab = e_inner.currentTab(st); - FlatRadiobutton *check = nullptr; - switch (tab) { - case dbietRecent: check = &_recent; break; - case dbietPeople: check = &_people; break; - case dbietNature: check = &_nature; break; - case dbietFood: check = &_food; break; - case dbietActivity: check = &_activity; break; - case dbietTravel: check = &_travel; break; - case dbietObjects: check = &_objects; break; - case dbietSymbols: check = &_symbols; break; - } - if (check && !check->checked()) { - _noTabUpdate = true; - check->setChecked(true); - _noTabUpdate = false; - } - - e_inner.setScrollTop(st); -} - -void EmojiPan::onScrollStickers() { - auto st = s_scroll.scrollTop(); - - updatePanelsPositions(s_panels, st); - - validateSelectedIcon(true); - if (st + s_scroll.height() > s_scroll.scrollTopMax()) { - onInlineRequest(); - } - - s_inner.setScrollTop(st); -} - -void EmojiPan::validateSelectedIcon(bool animated) { - uint64 setId = s_inner.currentSet(s_scroll.scrollTop()); - int32 newSel = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).setId == setId) { - newSel = i; - break; - } - } - if (newSel != _iconSel) { - _iconSel = newSel; - int32 skip = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).sticker) break; - ++skip; - } - if (animated) { - _iconSelX.start(newSel * st::rbEmoji.width); - } else { - _iconSelX = anim::ivalue(newSel * st::rbEmoji.width, newSel * st::rbEmoji.width); - } - _iconsX.start(snap((2 * newSel - 7 - skip) * int(st::rbEmoji.width) / 2, 0, _iconsMax)); - _iconsStartAnim = getms(); - _a_icons.start(); - updateSelected(); - updateIcons(); - } -} - -void EmojiPan::onSwitch() { - QPixmap cache = _cache; - _fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); - _stickersShown = !_stickersShown; - if (!_stickersShown) { - Notify::clipStopperHidden(ClipStopperSavedGifsPanel); - } else { - if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { - s_inner.showStickerSet(Stickers::DefaultSetId); - } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && Global::StickerSetsOrder().isEmpty()) { - s_inner.showStickerSet(Stickers::NoneSetId); - } else { - s_inner.updateShowingSavedGifs(); - } - if (cShowingSavedGifs()) { - s_inner.showFinish(); - } - validateSelectedIcon(); - updateContentHeight(); - } - _iconOver = -1; - _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); - _iconAnimations.clear(); - _a_icons.stop(); - - _cache = QPixmap(); - showAll(); - _toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); - _cache = cache; - - hideAll(); - - if (_stickersShown) { - e_inner.hideFinish(); - } else { - s_inner.hideFinish(false); - } - - a_toCoord = (_stickersShown != rtl()) ? anim::ivalue(st::emojiPanWidth, 0) : anim::ivalue(-st::emojiPanWidth, 0); - a_toAlpha = anim::fvalue(0, 1); - a_fromCoord = (_stickersShown != rtl()) ? anim::ivalue(0, -st::emojiPanWidth) : anim::ivalue(0, st::emojiPanWidth); - a_fromAlpha = anim::fvalue(1, 0); - - _a_slide.start(); - update(); -} - -void EmojiPan::onRemoveSet(quint64 setId) { - auto &sets = Global::StickerSets(); - auto it = sets.constFind(setId); - if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { - _removingSetId = it->id; - ConfirmBox *box = new ConfirmBox(lng_stickers_remove_pack(lt_sticker_pack, it->title), lang(lng_box_remove)); - connect(box, SIGNAL(confirmed()), this, SLOT(onRemoveSetSure())); - connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onDelayedHide())); - Ui::showLayer(box); - } -} - -void EmojiPan::onRemoveSetSure() { - Ui::hideLayer(); - auto &sets = Global::RefStickerSets(); - auto it = sets.find(_removingSetId); - if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { - if (it->id && it->access) { - MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)))); - } else if (!it->shortName.isEmpty()) { - MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetShortName(MTP_string(it->shortName)))); - } - bool writeRecent = false; - RecentStickerPack &recent(cGetRecentStickers()); - for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { - if (it->stickers.indexOf(i->first) >= 0) { - i = recent.erase(i); - writeRecent = true; - } else { - ++i; - } - } - it->flags &= ~MTPDstickerSet::Flag::f_installed; - if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) { - sets.erase(it); - } - int removeIndex = Global::StickerSetsOrder().indexOf(_removingSetId); - if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); - refreshStickers(); - Local::writeInstalledStickers(); - if (writeRecent) Local::writeUserSettings(); - } - _removingSetId = 0; -} - -void EmojiPan::onDelayedHide() { - if (!rect().contains(mapFromGlobal(QCursor::pos()))) { - _hideTimer.start(3000); - } - _removingSetId = 0; -} - -void EmojiPan::clearInlineBot() { - inlineBotChanged(); - e_switch.updateText(); - e_switch.moveToRight(0, 0, st::emojiPanWidth); -} - -bool EmojiPan::hideOnNoInlineResults() { - return _inlineBot && _stickersShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername()); -} - -void EmojiPan::inlineBotChanged() { - if (!_inlineBot) return; - - if (!isHidden() && !_hiding) { - if (hideOnNoInlineResults() || !rect().contains(mapFromGlobal(QCursor::pos()))) { - hideAnimated(); - } - } - - if (_inlineRequestId) MTP::cancel(_inlineRequestId); - _inlineRequestId = 0; - _inlineQuery = _inlineNextQuery = _inlineNextOffset = QString(); - _inlineBot = nullptr; - for (InlineCache::const_iterator i = _inlineCache.cbegin(), e = _inlineCache.cend(); i != e; ++i) { - delete i.value(); - } - _inlineCache.clear(); - s_inner.inlineBotChanged(); - s_inner.hideInlineRowsPanel(); - - Notify::inlineBotRequesting(false); -} - -void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) { - _inlineRequestId = 0; - Notify::inlineBotRequesting(false); - - auto it = _inlineCache.find(_inlineQuery); - - bool adding = (it != _inlineCache.cend()); - if (result.type() == mtpc_messages_botResults) { - const auto &d(result.c_messages_botResults()); - const auto &v(d.vresults.c_vector().v); - uint64 queryId(d.vquery_id.v); - - if (!adding) { - it = _inlineCache.insert(_inlineQuery, new internal::InlineCacheEntry()); - } - it.value()->nextOffset = qs(d.vnext_offset); - if (d.has_switch_pm() && d.vswitch_pm.type() == mtpc_inlineBotSwitchPM) { - const auto &switchPm = d.vswitch_pm.c_inlineBotSwitchPM(); - it.value()->switchPmText = qs(switchPm.vtext); - it.value()->switchPmStartToken = qs(switchPm.vstart_param); - } - - if (int count = v.size()) { - it.value()->results.reserve(it.value()->results.size() + count); - } - int added = 0; - for_const (const auto &res, v) { - if (auto result = InlineBots::Result::create(queryId, res)) { - ++added; - it.value()->results.push_back(result.release()); - } - } - - if (!added) { - it.value()->nextOffset = QString(); - } - } else if (adding) { - it.value()->nextOffset = QString(); - } - - if (!showInlineRows(!adding)) { - it.value()->nextOffset = QString(); - } - onScrollStickers(); -} - -bool EmojiPan::inlineResultsFail(const RPCError &error) { - // show error? - Notify::inlineBotRequesting(false); - _inlineRequestId = 0; - return true; -} - -void EmojiPan::queryInlineBot(UserData *bot, PeerData *peer, QString query) { - bool force = false; - _inlineQueryPeer = peer; - if (bot != _inlineBot) { - inlineBotChanged(); - _inlineBot = bot; - force = true; - //if (_inlineBot->isBotInlineGeo()) { - // Ui::showLayer(new InformBox(lang(lng_bot_inline_geo_unavailable))); - //} - } - //if (_inlineBot && _inlineBot->isBotInlineGeo()) { - // return; - //} - - if (_inlineQuery != query || force) { - if (_inlineRequestId) { - MTP::cancel(_inlineRequestId); - _inlineRequestId = 0; - Notify::inlineBotRequesting(false); - } - if (_inlineCache.contains(query)) { - _inlineRequestTimer.stop(); - _inlineQuery = _inlineNextQuery = query; - showInlineRows(true); - } else { - _inlineNextQuery = query; - _inlineRequestTimer.start(InlineBotRequestDelay); - } - } -} - -void EmojiPan::onInlineRequest() { - if (_inlineRequestId || !_inlineBot || !_inlineQueryPeer) return; - _inlineQuery = _inlineNextQuery; - - QString nextOffset; - InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery); - if (i != _inlineCache.cend()) { - nextOffset = i.value()->nextOffset; - if (nextOffset.isEmpty()) return; - } - Notify::inlineBotRequesting(true); - MTPmessages_GetInlineBotResults::Flags flags = 0; - _inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(MTP_flags(flags), _inlineBot->inputUser, _inlineQueryPeer->input, MTPInputGeoPoint(), MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail)); -} - -void EmojiPan::onEmptyInlineRows() { - if (_shownFromInlineQuery || hideOnNoInlineResults()) { - hideAnimated(); - s_inner.clearInlineRowsPanel(); - } else if (!_inlineBot) { - s_inner.hideInlineRowsPanel(); - } else { - s_inner.clearInlineRowsPanel(); - } -} - -bool EmojiPan::refreshInlineRows(int32 *added) { - auto i = _inlineCache.constFind(_inlineQuery); - const internal::InlineCacheEntry *entry = nullptr; - if (i != _inlineCache.cend()) { - if (!i.value()->results.isEmpty() || !i.value()->switchPmText.isEmpty()) { - entry = i.value(); - } - _inlineNextOffset = i.value()->nextOffset; - } - if (!entry) prepareShowHideCache(); - int32 result = s_inner.refreshInlineRows(_inlineBot, entry, false); - if (added) *added = result; - return (entry != nullptr); -} - -int32 EmojiPan::showInlineRows(bool newResults) { - int32 added = 0; - bool clear = !refreshInlineRows(&added); - if (newResults) s_scroll.scrollToY(0); - - e_switch.updateText(s_inner.inlineResultsShown() ? _inlineBot->username : QString()); - e_switch.moveToRight(0, 0, st::emojiPanWidth); - - bool hidden = isHidden(); - if (!hidden && !clear) { - recountContentMaxHeight(); - } - if (clear) { - if (!hidden && hideOnNoInlineResults()) { - hideAnimated(); - } else if (!_hiding) { - _cache = QPixmap(); // clear after refreshInlineRows() - } - } else { - _hideTimer.stop(); - if (hidden || _hiding) { - showStart(); - } else if (!_stickersShown) { - onSwitch(); - } - } - - return added; -} - -void EmojiPan::recountContentMaxHeight() { - if (_shownFromInlineQuery) { - _contentMaxHeight = qMin(s_inner.countHeight(true), int(st::emojiPanMaxHeight)); - } else { - _contentMaxHeight = st::emojiPanMaxHeight; - } - updateContentHeight(); -} diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index dfd81a7cf..836b2a531 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -27,7 +27,6 @@ class Dropdown : public TWidget { Q_OBJECT public: - Dropdown(QWidget *parent, const style::dropdown &st = st::dropdownDef); IconedButton *addButton(IconedButton *button); @@ -61,11 +60,9 @@ public: } signals: - void hiding(); public slots: - void hideStart(); void hideFinish(); @@ -75,20 +72,19 @@ public slots: void buttonStateChanged(int oldState, ButtonStateChangeSource source); private: - void adjustButtons(); - bool _ignore; + bool _ignore = false; typedef QVector Buttons; Buttons _buttons; - int32 _selected; + int32 _selected = -1; const style::dropdown &_st; int32 _width, _height; - bool _hiding; + bool _hiding = false; anim::fvalue a_opacity; Animation _a_appearance; @@ -103,7 +99,6 @@ class DragArea : public TWidget { Q_OBJECT public: - DragArea(QWidget *parent); void paintEvent(QPaintEvent *e); @@ -133,18 +128,15 @@ public: } signals: - void dropped(const QMimeData *data); public slots: - void hideStart(); void hideFinish(); void showStart(); private: - bool _hiding, _in; anim::fvalue a_opacity; @@ -156,588 +148,3 @@ private: QString _text, _subtext; }; - -namespace InlineBots { -namespace Layout { -class ItemBase; -} // namespace Layout -class Result; -} // namespace InlineBots - -namespace internal { - -constexpr int InlineItemsMaxPerRow = 5; -constexpr int EmojiColorsCount = 5; - -using InlineResult = InlineBots::Result; -using InlineResults = QList; -using InlineItem = InlineBots::Layout::ItemBase; - -struct InlineCacheEntry { - ~InlineCacheEntry() { - clearResults(); - } - QString nextOffset; - QString switchPmText, switchPmStartToken; - InlineResults results; // owns this results list - void clearResults(); -}; - -class EmojiColorPicker : public TWidget { - Q_OBJECT - -public: - - EmojiColorPicker(); - - void showEmoji(uint32 code); - - void paintEvent(QPaintEvent *e); - void enterEvent(QEvent *e); - void leaveEvent(QEvent *e); - void mousePressEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - - void step_appearance(float64 ms, bool timer); - void step_selected(uint64 ms, bool timer); - void showStart(); - - void clearSelection(bool fast = false); - -public slots: - - void hideStart(bool fast = false); - -signals: - - void emojiSelected(EmojiPtr emoji); - void hidden(); - -private: - - void drawVariant(Painter &p, int variant); - - void updateSelected(); - - bool _ignoreShow; - - EmojiPtr _variants[EmojiColorsCount + 1]; - - typedef QMap EmojiAnimations; // index - showing, -index - hiding - EmojiAnimations _emojiAnimations; - Animation _a_selected; - - float64 _hovers[EmojiColorsCount + 1]; - - int32 _selected, _pressedSel; - QPoint _lastMousePos; - - bool _hiding; - QPixmap _cache; - - anim::fvalue a_opacity; - Animation _a_appearance; - - QTimer _hideTimer; - - BoxShadow _shadow; - -}; - -class EmojiPanel; -class EmojiPanInner : public TWidget { - Q_OBJECT - -public: - - EmojiPanInner(); - - 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(); - - void showEmojiPack(DBIEmojiTab packIndex); - - void clearSelection(bool fast = false); - - DBIEmojiTab currentTab(int yOffset) const; - - void refreshRecent(); - - void setScrollTop(int top); - - void fillPanels(QVector &panels); - void refreshPanels(QVector &panels); - -public slots: - - void updateSelected(); - - void onShowPicker(); - void onPickerHidden(); - void onColorSelected(EmojiPtr emoji); - - bool checkPickerHide(); - -signals: - - void selected(EmojiPtr emoji); - - void switchToStickers(); - - void scrollToY(int y); - void disableScroll(bool dis); - - void needRefreshPanels(); - void saveConfigDelayed(int32 delay); - -private: - - int32 _maxHeight; - - int32 countHeight(); - void selectEmoji(EmojiPtr emoji); - - QRect emojiRect(int tab, int sel); - - typedef QMap Animations; // index - showing, -index - hiding - Animations _animations; - Animation _a_selected; - - int32 _top, _counts[emojiTabCount]; - - QVector _emojis[emojiTabCount]; - QVector _hovers[emojiTabCount]; - - int32 _esize; - - int32 _selected, _pressedSel, _pickerSel; - QPoint _lastMousePos; - - EmojiColorPicker _picker; - QTimer _showPickerTimer; -}; - -struct StickerIcon { - StickerIcon(uint64 setId) : setId(setId), sticker(0), pixw(0), pixh(0) { - } - StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) { - } - uint64 setId; - DocumentData *sticker; - int32 pixw, pixh; -}; - -class StickerPanInner : public TWidget { - 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); - void showFinish(); - void showStickerSet(uint64 setId); - void updateShowingSavedGifs(); - - bool showSectionIcons() const; - void clearSelection(bool fast = false); - - void refreshStickers(); - void refreshRecentStickers(bool resize = true); - void refreshSavedGifs(); - int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted); - void refreshRecent(); - void inlineBotChanged(); - void hideInlineRowsPanel(); - void clearInlineRowsPanel(); - - void fillIcons(QList &icons); - void fillPanels(QVector &panels); - void refreshPanels(QVector &panels); - - void setScrollTop(int top); - void preloadImages(); - - uint64 currentSet(int yOffset) const; - - void notify_inlineItemLayoutChanged(const InlineItem *layout); - void ui_repaintInlineItem(const InlineItem *layout); - bool ui_isInlineItemVisible(const InlineItem *layout); - bool ui_isInlineItemBeingChosen(); - - bool inlineResultsShown() const { - return _showingInlineItems && !_showingSavedGifs; - } - int32 countHeight(bool plain = false); - - ~StickerPanInner(); - -private slots: - - void updateSelected(); - void onSettings(); - void onPreview(); - void onUpdateInlineItems(); - void onSwitchPm(); - -signals: - - void selected(DocumentData *sticker); - void selected(PhotoData *photo); - void selected(InlineBots::Result *result, UserData *bot); - - void removing(quint64 setId); - - void refreshIcons(); - void emptyInlineRows(); - - void switchToEmoji(); - - void scrollToY(int y); - void scrollUpdated(); - void disableScroll(bool dis); - void needRefreshPanels(); - - void saveConfigDelayed(int32 delay); - -private: - - void paintInlineItems(Painter &p, const QRect &r); - void paintStickers(Painter &p, const QRect &r); - - void refreshSwitchPmButton(const InlineCacheEntry *entry); - - void appendSet(uint64 setId); - - void selectEmoji(EmojiPtr emoji); - QRect stickerRect(int tab, int sel); - - int32 _maxHeight; - - typedef QMap Animations; // index - showing, -index - hiding - Animations _animations; - Animation _a_selected; - - int32 _top; - - struct DisplayedSet { - DisplayedSet(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), hovers(hoversSize, 0), pack(pack) { - } - uint64 id; - MTPDstickerSet::Flags flags; - QString title; - QVector hovers; - StickerPack pack; - }; - QList _sets; - QList _custom; - - bool _showingSavedGifs, _showingInlineItems; - bool _setGifCommand; - UserData *_inlineBot; - QString _inlineBotTitle; - uint64 _lastScrolled; - QTimer _updateInlineItems; - bool _inlineWithThumb; - - std_::unique_ptr _switchPmButton; - QString _switchPmStartToken; - - typedef QVector InlineItems; - struct InlineRow { - InlineRow() : height(0) { - } - int32 height; - InlineItems items; - }; - typedef QVector InlineRows; - InlineRows _inlineRows; - void clearInlineRows(bool resultsDeleted); - - using GifLayouts = QMap; - GifLayouts _gifLayouts; - InlineItem *layoutPrepareSavedGif(DocumentData *doc, int32 position); - - using InlineLayouts = QMap; - InlineLayouts _inlineLayouts; - InlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position); - - bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth); - bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false); - - InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0); - void deleteUnusedGifLayouts(); - - void deleteUnusedInlineLayouts(); - - int32 validateExistingInlineRows(const InlineResults &results); - int32 _selected, _pressedSel; - QPoint _lastMousePos; - - LinkButton _settings; - - QTimer _previewTimer; - bool _previewShown; -}; - -class EmojiPanel : public TWidget { - Q_OBJECT - -public: - - EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY); // Stickers::NoneSetId if in emoji - void setText(const QString &text); - void setDeleteVisible(bool isVisible); - - void paintEvent(QPaintEvent *e); - void mousePressEvent(QMouseEvent *e); - - int32 wantedY() const { - return _wantedY; - } - void setWantedY(int32 y) { - _wantedY = y; - } - -signals: - - void deleteClicked(quint64 setId); - void mousePressed(); - -public slots: - - void onDelete(); - -private: - - void updateText(); - - int32 _wantedY; - QString _text, _fullText; - uint64 _setId; - bool _special, _deleteVisible; - IconedButton *_delete; - -}; - -class EmojiSwitchButton : public Button { -public: - - EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji - void paintEvent(QPaintEvent *e); - void updateText(const QString &inlineBotUsername = QString()); - -protected: - - bool _toStickers; - QString _text; - int32 _textWidth; - -}; - -} // namespace internal - -class EmojiPan : public TWidget, public RPCSender { - Q_OBJECT - -public: - - EmojiPan(QWidget *parent); - - void setMaxHeight(int32 h); - void paintEvent(QPaintEvent *e); - - void moveBottom(int32 bottom, bool force = false); - - void enterEvent(QEvent *e); - void leaveEvent(QEvent *e); - void otherEnter(); - void otherLeave(); - - void mousePressEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - - bool event(QEvent *e); - - void fastHide(); - bool hiding() const { - return _hiding || _hideTimer.isActive(); - } - - void step_appearance(float64 ms, bool timer); - void step_slide(float64 ms, bool timer); - void step_icons(uint64 ms, bool timer); - - bool eventFilter(QObject *obj, QEvent *e); - void stickersInstalled(uint64 setId); - - void queryInlineBot(UserData *bot, PeerData *peer, QString query); - void clearInlineBot(); - - bool overlaps(const QRect &globalRect) { - if (isHidden() || !_cache.isNull()) return false; - - return QRect(st::dropdownDef.padding.left(), - st::dropdownDef.padding.top(), - _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), - _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); - } - - void notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout); - void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout); - bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout); - bool ui_isInlineItemBeingChosen(); - - bool inlineResultsShown() const { - return s_inner.inlineResultsShown(); - } - -public slots: - - void refreshStickers(); - void refreshSavedGifs(); - - void hideStart(); - void hideFinish(); - - void showStart(); - void onWndActiveChanged(); - - void onTabChange(); - void onScrollEmoji(); - void onScrollStickers(); - void onSwitch(); - - void onRemoveSet(quint64 setId); - void onRemoveSetSure(); - void onDelayedHide(); - - void onRefreshIcons(); - void onRefreshPanels(); - - void onSaveConfig(); - void onSaveConfigDelayed(int32 delay); - - void onInlineRequest(); - void onEmptyInlineRows(); - -signals: - - void emojiSelected(EmojiPtr emoji); - void stickerSelected(DocumentData *sticker); - void photoSelected(PhotoData *photo); - void inlineResultSelected(InlineBots::Result *result, UserData *bot); - - void updateStickers(); - -private: - void paintStickerSettingsIcon(Painter &p) const; - - void validateSelectedIcon(bool animated = false); - - int32 _maxHeight, _contentMaxHeight, _contentHeight, _contentHeightEmoji, _contentHeightStickers; - bool _horizontal; - void updateContentHeight(); - - void leaveToChildEvent(QEvent *e, QWidget *child); - void hideAnimated(); - void prepareShowHideCache(); - - void updateSelected(); - void updateIcons(); - - void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab); - void updatePanelsPositions(const QVector &panels, int32 st); - - void showAll(); - void hideAll(); - - bool _noTabUpdate; - - int32 _width, _height, _bottom; - bool _hiding; - QPixmap _cache; - - anim::fvalue a_opacity; - Animation _a_appearance; - - QTimer _hideTimer; - - BoxShadow _shadow; - - FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols; - QList _icons; - QVector _iconHovers; - int32 _iconOver, _iconSel, _iconDown; - bool _iconsDragging; - typedef QMap Animations; // index - showing, -index - hiding - Animations _iconAnimations; - Animation _a_icons; - QPoint _iconsMousePos, _iconsMouseDown; - int32 _iconsLeft, _iconsTop; - int32 _iconsStartX, _iconsMax; - anim::ivalue _iconsX, _iconSelX; - uint64 _iconsStartAnim; - - bool _stickersShown, _shownFromInlineQuery; - QPixmap _fromCache, _toCache; - anim::ivalue a_fromCoord, a_toCoord; - anim::fvalue a_fromAlpha, a_toAlpha; - Animation _a_slide; - - ScrollArea e_scroll; - internal::EmojiPanInner e_inner; - QVector e_panels; - internal::EmojiSwitchButton e_switch; - ScrollArea s_scroll; - internal::StickerPanInner s_inner; - QVector s_panels; - internal::EmojiSwitchButton s_switch; - - uint64 _removingSetId; - - QTimer _saveConfigTimer; - - // inline bots - typedef QMap InlineCache; - InlineCache _inlineCache; - QTimer _inlineRequestTimer; - - void inlineBotChanged(); - int32 showInlineRows(bool newResults); - bool hideOnNoInlineResults(); - void recountContentMaxHeight(); - bool refreshInlineRows(int32 *added = 0); - UserData *_inlineBot; - PeerData *_inlineQueryPeer = nullptr; - QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; - mtpRequestId _inlineRequestId; - void inlineResultsDone(const MTPmessages_BotResults &result); - bool inlineResultsFail(const RPCError &error); - -}; diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 3150fd647..cbe01c49a 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -193,11 +193,12 @@ enum Flags { namespace Stickers { -static const uint64 DefaultSetId = 0; // for backward compatibility -static const uint64 CustomSetId = 0xFFFFFFFFFFFFFFFFULL; -static const uint64 RecentSetId = 0xFFFFFFFFFFFFFFFEULL; // for emoji/stickers panel, should not appear in Sets -static const uint64 NoneSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel, should not appear in Sets -static const uint64 CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; // for cloud-stored recent stickers +constexpr uint64 DefaultSetId = 0; // for backward compatibility +constexpr uint64 CustomSetId = 0xFFFFFFFFFFFFFFFFULL; +constexpr uint64 RecentSetId = 0xFFFFFFFFFFFFFFFEULL; // for emoji/stickers panel, should not appear in Sets +constexpr uint64 NoneSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel, should not appear in Sets +constexpr uint64 CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; // for cloud-stored recent stickers +constexpr uint64 FeaturedSetId = 0xFFFFFFFFFFFFFFFBULL; // for emoji/stickers panel, should not appear in Sets struct Set { Set(uint64 id, uint64 access, const QString &title, const QString &shortName, int32 count, int32 hash, MTPDstickerSet::Flags flags) : id(id) diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index a3a57fb2c..f83fdb289 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -35,8 +35,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "history/history_service_layout.h" #include "profile/profile_members_widget.h" #include "core/click_handler_types.h" +#include "stickers/emoji_pan.h" #include "lang.h" #include "application.h" +#include "dropdown.h" #include "mainwidget.h" #include "mainwindow.h" #include "passcodewidget.h" @@ -3055,11 +3057,11 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_field, SIGNAL(linksChanged()), this, SLOT(onPreviewCheck())); connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onWindowVisibleChanged())); connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); - connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr))); - connect(&_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*))); - connect(&_emojiPan, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*))); - connect(&_emojiPan, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*))); - connect(&_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers())); + connect(_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr))); + connect(_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*))); + connect(_emojiPan, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*))); + connect(_emojiPan, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*))); + connect(_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers())); connect(&_sendActionStopTimer, SIGNAL(timeout()), this, SLOT(onCancelSendAction())); connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreviewTimeout())); if (audioCapture()) { @@ -3130,25 +3132,25 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _silent.hide(); _cmdStart.hide(); - _attachDocument.installEventFilter(&_attachType); - _attachPhoto.installEventFilter(&_attachType); - _attachEmoji.installEventFilter(&_emojiPan); + _attachDocument.installEventFilter(_attachType); + _attachPhoto.installEventFilter(_attachType); + _attachEmoji.installEventFilter(_emojiPan); connect(&_kbShow, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(&_kbHide, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(&_cmdStart, SIGNAL(clicked()), this, SLOT(onCmdStart())); - connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect())); - connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect())); - _attachType.hide(); - _emojiPan.hide(); - _attachDragDocument.hide(); - _attachDragPhoto.hide(); + connect(_attachType->addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect())); + connect(_attachType->addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect())); + _attachType->hide(); + _emojiPan->hide(); + _attachDragDocument->hide(); + _attachDragPhoto->hide(); _topShadow.hide(); - connect(&_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*))); - connect(&_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*))); + connect(_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*))); + connect(_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*))); connect(&_updateEditTimeLeftDisplay, SIGNAL(timeout()), this, SLOT(updateField())); @@ -3157,7 +3159,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) void HistoryWidget::start() { connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); - connect(App::main(), SIGNAL(savedGifsUpdated()), &_emojiPan, SLOT(refreshSavedGifs())); + connect(App::main(), SIGNAL(savedGifsUpdated()), _emojiPan, SLOT(refreshSavedGifs())); updateRecentStickers(); if (App::main()) emit App::main()->savedGifsUpdated(); @@ -3166,7 +3168,7 @@ void HistoryWidget::start() { } void HistoryWidget::onStickersUpdated() { - _emojiPan.refreshStickers(); + _emojiPan->refreshStickers(); updateStickersByEmoji(); } @@ -3228,9 +3230,9 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) { inlineBotChanged(); } if (_inlineBot->username == cInlineGifBotUsername() && query.isEmpty()) { - _emojiPan.clearInlineBot(); + _emojiPan->clearInlineBot(); } else { - _emojiPan.queryInlineBot(_inlineBot, _peer, query); + _emojiPan->queryInlineBot(_inlineBot, _peer, query); } if (!_fieldAutocomplete->isHidden()) { _fieldAutocomplete->hideStart(); @@ -3450,11 +3452,11 @@ void HistoryWidget::updateSendAction(History *history, SendActionType type, int3 } void HistoryWidget::updateRecentStickers() { - _emojiPan.refreshStickers(); + _emojiPan->refreshStickers(); } void HistoryWidget::stickersInstalled(uint64 setId) { - _emojiPan.stickersInstalled(setId); + _emojiPan->stickersInstalled(setId); } void HistoryWidget::sendActionDone(const MTPBool &result, mtpRequestId req) { @@ -4380,11 +4382,11 @@ void HistoryWidget::updateNotifySettings() { } bool HistoryWidget::contentOverlapped(const QRect &globalRect) { - return (_attachDragDocument.overlaps(globalRect) || - _attachDragPhoto.overlaps(globalRect) || - _attachType.overlaps(globalRect) || + return (_attachDragDocument->overlaps(globalRect) || + _attachDragPhoto->overlaps(globalRect) || + _attachType->overlaps(globalRect) || _fieldAutocomplete->overlaps(globalRect) || - _emojiPan.overlaps(globalRect)); + _emojiPan->overlaps(globalRect)); } void HistoryWidget::updateReportSpamStatus() { @@ -4516,8 +4518,8 @@ void HistoryWidget::updateControlsVisibility() { _kbShow.hide(); _kbHide.hide(); _cmdStart.hide(); - _attachType.hide(); - _emojiPan.hide(); + _attachType->hide(); + _emojiPan->hide(); if (_pinnedBar) { _pinnedBar->cancel.hide(); _pinnedBar->shadow.hide(); @@ -4579,8 +4581,8 @@ void HistoryWidget::updateControlsVisibility() { _kbShow.hide(); _kbHide.hide(); _cmdStart.hide(); - _attachType.hide(); - _emojiPan.hide(); + _attachType->hide(); + _emojiPan->hide(); if (!_field.isHidden()) { _field.hide(); resizeEvent(0); @@ -4715,8 +4717,8 @@ void HistoryWidget::updateControlsVisibility() { _kbShow.hide(); _kbHide.hide(); _cmdStart.hide(); - _attachType.hide(); - _emojiPan.hide(); + _attachType->hide(); + _emojiPan->hide(); _kbScroll.hide(); if (!_field.isHidden()) { _field.hide(); @@ -5257,8 +5259,8 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { onDraftSave(); if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType.isHidden()) _attachType.hideStart(); - if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + if (!_attachType->isHidden()) _attachType->hideStart(); + if (!_emojiPan->isHidden()) _emojiPan->hideStart(); if (replyTo < 0) cancelReply(lastKeyboardUsed); if (_previewData && _previewData->pendingTill) previewCancel(); @@ -5586,7 +5588,7 @@ void HistoryWidget::onPhotoSelect() { _attachDocument.clearState(); _attachDocument.hide(); _attachPhoto.show(); - _attachType.fastHide(); + _attachType->fastHide(); if (cDefaultAttach() != dbidaPhoto) { cSetDefaultAttach(dbidaPhoto); @@ -5614,7 +5616,7 @@ void HistoryWidget::onDocumentSelect() { _attachPhoto.clearState(); _attachPhoto.hide(); _attachDocument.show(); - _attachType.fastHide(); + _attachType->fastHide(); if (cDefaultAttach() != dbidaDocument) { cSetDefaultAttach(dbidaDocument); @@ -5651,14 +5653,14 @@ void HistoryWidget::dragEnterEvent(QDragEnterEvent *e) { } void HistoryWidget::dragLeaveEvent(QDragLeaveEvent *e) { - if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) { + if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; updateDragAreas(); } } void HistoryWidget::leaveEvent(QEvent *e) { - if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) { + if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; updateDragAreas(); } @@ -5706,7 +5708,7 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) { _replyForwardPressed = false; update(0, _field.y() - st::sendPadding - st::replyHeight, width(), st::replyHeight); } - if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) { + if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; updateDragAreas(); } @@ -5961,24 +5963,24 @@ void HistoryWidget::updateDragAreas() { _field.setAcceptDrops(!_attachDrag); switch (_attachDrag) { case DragStateNone: - _attachDragDocument.otherLeave(); - _attachDragPhoto.otherLeave(); + _attachDragDocument->otherLeave(); + _attachDragPhoto->otherLeave(); break; case DragStateFiles: - _attachDragDocument.otherEnter(); - _attachDragDocument.setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files)); - _attachDragPhoto.fastHide(); + _attachDragDocument->otherEnter(); + _attachDragDocument->setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files)); + _attachDragPhoto->fastHide(); break; case DragStatePhotoFiles: - _attachDragDocument.otherEnter(); - _attachDragDocument.setText(lang(lng_drag_images_here), lang(lng_drag_to_send_no_compression)); - _attachDragPhoto.otherEnter(); - _attachDragPhoto.setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick)); + _attachDragDocument->otherEnter(); + _attachDragDocument->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_no_compression)); + _attachDragPhoto->otherEnter(); + _attachDragPhoto->setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick)); break; case DragStateImage: - _attachDragDocument.fastHide(); - _attachDragPhoto.otherEnter(); - _attachDragPhoto.setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick)); + _attachDragDocument->fastHide(); + _attachDragPhoto->otherEnter(); + _attachDragPhoto->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick)); break; }; resizeEvent(0); @@ -6460,8 +6462,8 @@ void HistoryWidget::moveFieldControls() { right = w; _fieldBarCancel.move(right - _fieldBarCancel.width(), _field.y() - st::sendPadding - _fieldBarCancel.height()); - _attachType.move(0, _attachDocument.y() - _attachType.height()); - _emojiPan.moveBottom(_attachEmoji.y()); + _attachType->move(0, _attachDocument.y() - _attachType->height()); + _emojiPan->moveBottom(_attachEmoji.y()); _botStart.setGeometry(0, bottom - _botStart.height(), w, _botStart.height()); _unblock.setGeometry(0, bottom - _unblock.height(), w, _unblock.height()); @@ -6491,7 +6493,7 @@ void HistoryWidget::clearInlineBot() { inlineBotChanged(); _field.finishPlaceholder(); } - _emojiPan.clearInlineBot(); + _emojiPan->clearInlineBot(); onCheckFieldAutocomplete(); } @@ -6958,15 +6960,15 @@ void HistoryWidget::onUpdateHistoryItems() { } void HistoryWidget::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) { - _emojiPan.ui_repaintInlineItem(layout); + _emojiPan->ui_repaintInlineItem(layout); } bool HistoryWidget::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) { - return _emojiPan.ui_isInlineItemVisible(layout); + return _emojiPan->ui_isInlineItemVisible(layout); } bool HistoryWidget::ui_isInlineItemBeingChosen() { - return _emojiPan.ui_isInlineItemBeingChosen(); + return _emojiPan->ui_isInlineItemBeingChosen(); } PeerData *HistoryWidget::ui_getPeerForMouseAction() { @@ -6980,7 +6982,7 @@ void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { } void HistoryWidget::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { - _emojiPan.notify_inlineItemLayoutChanged(layout); + _emojiPan->notify_inlineItemLayoutChanged(layout); } void HistoryWidget::notify_handlePendingHistoryUpdate() { @@ -7015,25 +7017,25 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { _historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll.y() + _scroll.height() - _historyToEnd->height() - st::historyToDownPosition.y()); - _emojiPan.setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height()); + _emojiPan->setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height()); if (_membersDropdown) { _membersDropdown->setMaxHeight(countMembersDropdownHeightMax()); } switch (_attachDrag) { case DragStateFiles: - _attachDragDocument.resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); - _attachDragDocument.move(st::dragMargin.left(), st::dragMargin.top()); + _attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); + _attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top()); break; case DragStatePhotoFiles: - _attachDragDocument.resize(width() - st::dragMargin.left() - st::dragMargin.right(), (height() - st::dragMargin.top() - st::dragMargin.bottom()) / 2); - _attachDragDocument.move(st::dragMargin.left(), st::dragMargin.top()); - _attachDragPhoto.resize(_attachDragDocument.width(), _attachDragDocument.height()); - _attachDragPhoto.move(st::dragMargin.left(), height() - _attachDragPhoto.height() - st::dragMargin.bottom()); + _attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), (height() - st::dragMargin.top() - st::dragMargin.bottom()) / 2); + _attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top()); + _attachDragPhoto->resize(_attachDragDocument->width(), _attachDragDocument->height()); + _attachDragPhoto->move(st::dragMargin.left(), height() - _attachDragPhoto->height() - st::dragMargin.bottom()); break; case DragStateImage: - _attachDragPhoto.resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); - _attachDragPhoto.move(st::dragMargin.left(), st::dragMargin.top()); + _attachDragPhoto->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); + _attachDragPhoto->move(st::dragMargin.left(), st::dragMargin.top()); break; } @@ -7530,8 +7532,8 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot } if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType.isHidden()) _attachType.hideStart(); - if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + if (!_attachType->isHidden()) _attachType->hideStart(); + if (!_emojiPan->isHidden()) _emojiPan->hideStart(); _field.setFocus(); } @@ -7600,10 +7602,10 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { if (_membersDropdown) { _membersDropdown->raise(); } - _attachType.raise(); - _emojiPan.raise(); - _attachDragDocument.raise(); - _attachDragPhoto.raise(); + _attachType->raise(); + _emojiPan->raise(); + _attachDragDocument->raise(); + _attachDragPhoto->raise(); updatePinnedBar(); result = true; @@ -7701,8 +7703,8 @@ void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti } if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType.isHidden()) _attachType.hideStart(); - if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + if (!_attachType->isHidden()) _attachType->hideStart(); + if (!_emojiPan->isHidden()) _emojiPan->hideStart(); _field.setFocus(); } @@ -7747,8 +7749,8 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption) App::historyRegRandom(randomId, newId); if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType.isHidden()) _attachType.hideStart(); - if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + if (!_attachType->isHidden()) _attachType->hideStart(); + if (!_emojiPan->isHidden()) _emojiPan->hideStart(); _field.setFocus(); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index e73e2b87d..7d4133487 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localimageloader.h" #include "ui/boxshadow.h" -#include "dropdown.h" #include "history/history_common.h" #include "history/field_autocomplete.h" #include "window/section_widget.h" @@ -39,6 +38,10 @@ class HistoryDownButton; class InnerDropdown; } // namespace Ui +class Dropdown; +class DragArea; +class EmojiPan; + class HistoryWidget; class HistoryInner : public TWidget, public AbstractTooltipShower { Q_OBJECT @@ -1124,10 +1127,10 @@ private: ChildWidget _membersDropdown = { nullptr }; QTimer _membersDropdownShowTimer; - Dropdown _attachType; - EmojiPan _emojiPan; + ChildWidget _attachType; + ChildWidget _emojiPan; DragState _attachDrag = DragStateNone; - DragArea _attachDragDocument, _attachDragPhoto; + ChildWidget _attachDragDocument, _attachDragPhoto; int32 _selCount; // < 0 - text selected, focus list, not _field diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index af1ca0e98..8feba33bc 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/section_widget.h" #include "window/top_bar_widget.h" #include "data/data_drafts.h" +#include "dropdown.h" #include "observer_peer.h" #include "apiwrap.h" #include "dialogswidget.h" diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp new file mode 100644 index 000000000..f84cc454b --- /dev/null +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -0,0 +1,3704 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "stickers/emoji_pan.h" + +#include "styles/style_stickers.h" +#include "boxes/confirmbox.h" +#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 "stickers/stickers.h" +#include "historywidget.h" +#include "localstorage.h" +#include "lang.h" +#include "mainwindow.h" +#include "apiwrap.h" +#include "mainwidget.h" + +namespace internal { + +EmojiColorPicker::EmojiColorPicker() : TWidget() +, _a_selected(animation(this, &EmojiColorPicker::step_selected)) +, a_opacity(0) +, _a_appearance(animation(this, &EmojiColorPicker::step_appearance)) +, _shadow(st::dropdownDef.shadow) { + memset(_variants, 0, sizeof(_variants)); + memset(_hovers, 0, sizeof(_hovers)); + + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + + int32 w = st::emojiPanSize.width() * (EmojiColorsCount + 1) + 4 * st::emojiColorsPadding + st::emojiColorsSep + st::dropdownDef.shadow.pxWidth() * 2; + int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::dropdownDef.shadow.pxHeight() * 2; + resize(w, h); + + _hideTimer.setSingleShot(true); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); +} + +void EmojiColorPicker::showEmoji(uint32 code) { + EmojiPtr e = emojiGet(code); + if (!e || e == TwoSymbolEmoji || !e->color) { + return; + } + _ignoreShow = false; + + _variants[0] = e; + _variants[1] = emojiGet(e, 0xD83CDFFB); + _variants[2] = emojiGet(e, 0xD83CDFFC); + _variants[3] = emojiGet(e, 0xD83CDFFD); + _variants[4] = emojiGet(e, 0xD83CDFFE); + _variants[5] = emojiGet(e, 0xD83CDFFF); + + if (!_cache.isNull()) _cache = QPixmap(); + showStart(); +} + +void EmojiColorPicker::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_cache.isNull()) { + p.setOpacity(a_opacity.current()); + } + if (e->rect() != rect()) { + p.setClipRect(e->rect()); + } + + int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); + QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h); + _shadow.paint(p, r, st::dropdownDef.shadowShift); + + if (_cache.isNull()) { + p.fillRect(e->rect().intersected(r), st::white->b); + + int32 x = w + 2 * st::emojiColorsPadding + st::emojiPanSize.width(); + if (rtl()) x = width() - x - st::emojiColorsSep; + p.fillRect(x, h + st::emojiColorsPadding, st::emojiColorsSep, r.height() - st::emojiColorsPadding * 2, st::emojiColorsSepColor->b); + + if (!_variants[0]) return; + for (int i = 0; i < EmojiColorsCount + 1; ++i) { + drawVariant(p, i); + } + } else { + p.drawPixmap(r.left(), r.top(), _cache); + } + +} + +void EmojiColorPicker::enterEvent(QEvent *e) { + _hideTimer.stop(); + if (_hiding) showStart(); + TWidget::enterEvent(e); +} + +void EmojiColorPicker::leaveEvent(QEvent *e) { + TWidget::leaveEvent(e); +} + +void EmojiColorPicker::mousePressEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + updateSelected(); + _pressedSel = _selected; +} + +void EmojiColorPicker::mouseReleaseEvent(QMouseEvent *e) { + _lastMousePos = e ? e->globalPos() : QCursor::pos(); + int32 pressed = _pressedSel; + _pressedSel = -1; + + updateSelected(); + if (_selected >= 0 && (pressed < 0 || _selected == pressed)) { + emit emojiSelected(_variants[_selected]); + } + _ignoreShow = true; + hideStart(); +} + +void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) { + _lastMousePos = e ? e->globalPos() : QCursor::pos(); + updateSelected(); +} + +void EmojiColorPicker::step_appearance(float64 ms, bool timer) { + if (_cache.isNull()) { + _a_appearance.stop(); + return; + } + float64 dt = ms / st::dropdownDef.duration; + if (dt >= 1) { + a_opacity.finish(); + _cache = QPixmap(); + if (_hiding) { + hide(); + emit hidden(); + } else { + _lastMousePos = QCursor::pos(); + updateSelected(); + } + _a_appearance.stop(); + } else { + a_opacity.update(dt, anim::linear); + } + if (timer) update(); +} + +void EmojiColorPicker::step_selected(uint64 ms, bool timer) { + QRegion toUpdate; + for (EmojiAnimations::iterator i = _emojiAnimations.begin(); i != _emojiAnimations.end();) { + int index = qAbs(i.key()) - 1; + float64 dt = float64(ms - i.value()) / st::emojiPanDuration; + if (dt >= 1) { + _hovers[index] = (i.key() > 0) ? 1 : 0; + i = _emojiAnimations.erase(i); + } else { + _hovers[index] = (i.key() > 0) ? dt : (1 - dt); + ++i; + } + toUpdate += QRect(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height()); + } + if (timer) rtlupdate(toUpdate.boundingRect()); + if (_emojiAnimations.isEmpty()) _a_selected.stop(); +} + +void EmojiColorPicker::hideStart(bool fast) { + if (fast) { + clearSelection(true); + if (_a_appearance.animating()) _a_appearance.stop(); + if (_a_selected.animating()) _a_selected.stop(); + a_opacity = anim::fvalue(0); + _cache = QPixmap(); + hide(); + emit hidden(); + } else { + if (_cache.isNull()) { + int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); + _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); + clearSelection(true); + } + _hiding = true; + a_opacity.start(0); + _a_appearance.start(); + } +} + +void EmojiColorPicker::showStart() { + if (_ignoreShow) return; + + _hiding = false; + if (!isHidden() && a_opacity.current() == 1) { + if (_a_appearance.animating()) { + _a_appearance.stop(); + _cache = QPixmap(); + } + return; + } + if (_cache.isNull()) { + int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); + _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); + clearSelection(true); + } + show(); + a_opacity.start(1); + _a_appearance.start(); +} + +void EmojiColorPicker::clearSelection(bool fast) { + _pressedSel = -1; + _lastMousePos = mapToGlobal(QPoint(-10, -10)); + if (fast) { + _selected = -1; + memset(_hovers, 0, sizeof(_hovers)); + _emojiAnimations.clear(); + } else { + updateSelected(); + } +} + +void EmojiColorPicker::updateSelected() { + int32 selIndex = -1; + QPoint p(mapFromGlobal(_lastMousePos)); + int32 sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::dropdownDef.shadow.pxHeight() - st::emojiColorsPadding; + if (y >= 0 && y < st::emojiPanSize.height()) { + int32 x = sx - st::dropdownDef.shadow.pxWidth() - st::emojiColorsPadding; + if (x >= 0 && x < st::emojiPanSize.width()) { + selIndex = 0; + } else { + x -= st::emojiPanSize.width() + 2 * st::emojiColorsPadding + st::emojiColorsSep; + if (x >= 0 && x < st::emojiPanSize.width() * EmojiColorsCount) { + selIndex = (x / st::emojiPanSize.width()) + 1; + } + } + } + + bool startanim = false; + if (selIndex != _selected) { + if (_selected >= 0) { + _emojiAnimations.remove(_selected + 1); + if (_emojiAnimations.find(-_selected - 1) == _emojiAnimations.end()) { + if (_emojiAnimations.isEmpty()) startanim = true; + _emojiAnimations.insert(-_selected - 1, getms()); + } + } + _selected = selIndex; + if (_selected >= 0) { + _emojiAnimations.remove(-_selected - 1); + if (_emojiAnimations.find(_selected + 1) == _emojiAnimations.end()) { + if (_emojiAnimations.isEmpty()) startanim = true; + _emojiAnimations.insert(_selected + 1, getms()); + } + } + setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default); + } + if (startanim && !_a_selected.animating()) _a_selected.start(); +} + +void EmojiColorPicker::drawVariant(Painter &p, int variant) { + float64 hover = _hovers[variant]; + + QPoint w(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding); + if (hover > 0) { + p.setOpacity(hover); + QPoint tl(w); + if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); + App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); + p.setOpacity(1); + } + int esize = EmojiSizes[EIndex + 1]; + 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() +, _maxHeight(int(st::emojiPanMaxHeight) - st::rbEmoji.height) +, _a_selected(animation(this, &EmojiPanInner::step_selected)) { + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); + + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + setAttribute(Qt::WA_OpaquePaintEvent); + + _picker.hide(); + + _esize = EmojiSizes[EIndex + 1]; + + for (int32 i = 0; i < emojiTabCount; ++i) { + _counts[i] = emojiPackCount(emojiTabAtIndex(i)); + _hovers[i] = QVector(_counts[i], 0); + } + + _showPickerTimer.setSingleShot(true); + connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker())); + connect(&_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr))); + connect(&_picker, SIGNAL(hidden()), this, SLOT(onPickerHidden())); +} + +void EmojiPanInner::setMaxHeight(int32 h) { + _maxHeight = h; + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); +} + +void EmojiPanInner::setScrollTop(int top) { + _top = top; +} + +int EmojiPanInner::countHeight() { + int result = 0; + for (int i = 0; i < emojiTabCount; ++i) { + int cnt = emojiPackCount(emojiTabAtIndex(i)), rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + result += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + + return result + st::emojiPanPadding; +} + +void EmojiPanInner::paintEvent(QPaintEvent *e) { + Painter p(this); + QRect r = e ? e->rect() : rect(); + if (r != rect()) { + p.setClipRect(r); + } + p.fillRect(r, st::white->b); + + int32 fromcol = floorclamp(r.x() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); + int32 tocol = ceilclamp(r.x() + r.width() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); + if (rtl()) { + qSwap(fromcol, tocol); + fromcol = EmojiPanPerRow - fromcol; + tocol = EmojiPanPerRow - tocol; + } + + int32 y, tilly = 0; + for (int c = 0; c < emojiTabCount; ++c) { + y = tilly; + int32 size = _counts[c]; + int32 rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); + tilly = y + st::emojiPanHeader + (rows * st::emojiPanSize.height()); + if (r.top() >= tilly) continue; + + y += st::emojiPanHeader; + if (_emojis[c].isEmpty()) { + _emojis[c] = emojiPack(emojiTabAtIndex(c)); + if (emojiTabAtIndex(c) != dbietRecent) { + for (EmojiPack::iterator i = _emojis[c].begin(), e = _emojis[c].end(); i != e; ++i) { + if ((*i)->color) { + EmojiColorVariants::const_iterator j = cEmojiVariants().constFind((*i)->code); + if (j != cEmojiVariants().cend()) { + EmojiPtr replace = emojiFromKey(j.value()); + if (replace) { + if (replace != TwoSymbolEmoji && replace->code == (*i)->code && replace->code2 == (*i)->code2) { + *i = replace; + } + } + } + } + } + } + } + + int32 fromrow = floorclamp(r.y() - y, st::emojiPanSize.height(), 0, rows); + int32 torow = ceilclamp(r.y() + r.height() - y, st::emojiPanSize.height(), 0, rows); + for (int32 i = fromrow; i < torow; ++i) { + for (int32 j = fromcol; j < tocol; ++j) { + int32 index = i * EmojiPanPerRow + j; + if (index >= size) break; + + float64 hover = (!_picker.isHidden() && c * MatrixRowShift + index == _pickerSel) ? 1 : _hovers[c][index]; + + QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height()); + if (hover > 0) { + p.setOpacity(hover); + QPoint tl(w); + if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); + App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); + p.setOpacity(1); + } + p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (_esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (_esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_emojis[c][index]->x * _esize, _emojis[c][index]->y * _esize, _esize, _esize)); + } + } + } +} + +bool EmojiPanInner::checkPickerHide() { + if (!_picker.isHidden() && _selected == _pickerSel) { + _picker.hideStart(); + _pickerSel = -1; + updateSelected(); + return true; + } + return false; +} + +void EmojiPanInner::mousePressEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + updateSelected(); + if (checkPickerHide()) { + return; + } + _pressedSel = _selected; + + if (_selected >= 0) { + int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; + if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { + _pickerSel = _selected; + setCursor(style::cur_default); + if (cEmojiVariants().constFind(_emojis[tab][sel]->code) == cEmojiVariants().cend()) { + onShowPicker(); + } else { + _showPickerTimer.start(500); + } + } + } +} + +void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { + int32 pressed = _pressedSel; + _pressedSel = -1; + + _lastMousePos = e->globalPos(); + if (!_picker.isHidden()) { + if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { + return _picker.mouseReleaseEvent(0); + } else if (_pickerSel >= 0) { + int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; + if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { + if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) { + _picker.hideStart(); + _pickerSel = -1; + } + } + } + } + updateSelected(); + + if (_showPickerTimer.isActive()) { + _showPickerTimer.stop(); + _pickerSel = -1; + _picker.hide(); + } + + if (_selected < 0 || _selected != pressed) return; + + if (_selected >= emojiTabCount * MatrixRowShift) { + return; + } + + int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; + if (sel < _emojis[tab].size()) { + EmojiPtr emoji(_emojis[tab][sel]); + if (emoji->color && !_picker.isHidden()) return; + + selectEmoji(emoji); + } +} + +void EmojiPanInner::selectEmoji(EmojiPtr emoji) { + RecentEmojiPack &recent(cGetRecentEmojis()); + RecentEmojiPack::iterator i = recent.begin(), e = recent.end(); + for (; i != e; ++i) { + if (i->first == emoji) { + ++i->second; + if (i->second > 0x8000) { + for (RecentEmojiPack::iterator j = recent.begin(); j != e; ++j) { + if (j->second > 1) { + j->second /= 2; + } else { + j->second = 1; + } + } + } + for (; i != recent.begin(); --i) { + if ((i - 1)->second > i->second) { + break; + } + qSwap(*i, *(i - 1)); + } + break; + } + } + if (i == e) { + while (recent.size() >= EmojiPanPerRow * EmojiPanRowsPerPage) recent.pop_back(); + recent.push_back(qMakePair(emoji, 1)); + for (i = recent.end() - 1; i != recent.begin(); --i) { + if ((i - 1)->second > i->second) { + break; + } + qSwap(*i, *(i - 1)); + } + } + emit saveConfigDelayed(SaveRecentEmojisTimeout); + + emit selected(emoji); +} + +void EmojiPanInner::onShowPicker() { + if (_pickerSel < 0) return; + + int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; + if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { + int32 y = 0; + for (int c = 0; c <= tab; ++c) { + 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; + if (y < 0) { + y += _picker.height() - st::buttonRadius + st::emojiPanSize.height() - st::buttonRadius; + } + int xmax = width() - _picker.width(); + float64 coef = float64(sel % EmojiPanPerRow) / float64(EmojiPanPerRow - 1); + if (rtl()) coef = 1. - coef; + _picker.move(qRound(xmax * coef), y); + + _picker.showEmoji(_emojis[tab][sel]->code); + emit disableScroll(true); + } +} + +void EmojiPanInner::onPickerHidden() { + _pickerSel = -1; + update(); + emit disableScroll(false); + + _lastMousePos = QCursor::pos(); + updateSelected(); +} + +QRect EmojiPanInner::emojiRect(int tab, int sel) { + int x = 0, y = 0; + for (int i = 0; i < emojiTabCount; ++i) { + if (i == tab) { + int rows = (sel / EmojiPanPerRow); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + x = st::emojiPanPadding + ((sel % EmojiPanPerRow) * st::emojiPanSize.width()); + break; + } else { + int cnt = _counts[i]; + int rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + } + return QRect(x, y, st::emojiPanSize.width(), st::emojiPanSize.height()); +} + +void EmojiPanInner::onColorSelected(EmojiPtr emoji) { + if (emoji->color) { + cRefEmojiVariants().insert(emoji->code, emojiKey(emoji)); + } + if (_pickerSel >= 0) { + int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; + if (tab >= 0 && tab < emojiTabCount) { + _emojis[tab][sel] = emoji; + rtlupdate(emojiRect(tab, sel)); + } + } + selectEmoji(emoji); + _picker.hideStart(); +} + +void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + if (!_picker.isHidden()) { + if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { + return _picker.mouseMoveEvent(0); + } else { + _picker.clearSelection(); + } + } + updateSelected(); +} + +void EmojiPanInner::leaveEvent(QEvent *e) { + clearSelection(); +} + +void EmojiPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { + clearSelection(); +} + +void EmojiPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { + _lastMousePos = QCursor::pos(); + updateSelected(); +} + +void EmojiPanInner::clearSelection(bool fast) { + _lastMousePos = mapToGlobal(QPoint(-10, -10)); + if (fast) { + for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { + int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + _hovers[tab][sel] = 0; + } + _animations.clear(); + if (_selected >= 0) { + int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + _hovers[tab][sel] = 0; + } + if (_pressedSel >= 0) { + int index = qAbs(_pressedSel), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + _hovers[tab][sel] = 0; + } + _selected = _pressedSel = -1; + _a_selected.stop(); + } else { + updateSelected(); + } +} + +DBIEmojiTab EmojiPanInner::currentTab(int yOffset) const { + int y, ytill = 0; + for (int c = 0; c < emojiTabCount; ++c) { + int cnt = _counts[c]; + y = ytill; + ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); + if (yOffset < ytill) { + return emojiTabAtIndex(c); + } + } + return emojiTabAtIndex(emojiTabCount - 1); +} + +void EmojiPanInner::hideFinish() { + if (!_picker.isHidden()) { + _picker.hideStart(true); + _pickerSel = -1; + clearSelection(true); + } +} + +void EmojiPanInner::refreshRecent() { + clearSelection(true); + _counts[0] = emojiPackCount(dbietRecent); + if (_hovers[0].size() != _counts[0]) _hovers[0] = QVector(_counts[0], 0); + _emojis[0] = emojiPack(dbietRecent); + int32 h = countHeight(); + if (h != height()) { + resize(width(), h); + emit needRefreshPanels(); + } +} + +void EmojiPanInner::fillPanels(QVector &panels) { + if (_picker.parentWidget() != parentWidget()) { + _picker.setParent(parentWidget()); + } + for (int32 i = 0; i < panels.size(); ++i) { + panels.at(i)->hide(); + panels.at(i)->deleteLater(); + } + panels.clear(); + + int y = 0; + panels.reserve(emojiTabCount); + for (int c = 0; c < emojiTabCount; ++c) { + panels.push_back(new EmojiPanel(parentWidget(), lang(LangKey(lng_emoji_category0 + c)), Stickers::NoneSetId, true, y)); + connect(panels.back(), SIGNAL(mousePressed()), this, SLOT(checkPickerHide())); + int cnt = _counts[c], rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + panels.back()->show(); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + _picker.raise(); +} + +void EmojiPanInner::refreshPanels(QVector &panels) { + if (panels.size() != emojiTabCount) return fillPanels(panels); + + int32 y = 0; + for (int c = 0; c < emojiTabCount; ++c) { + panels.at(c)->setWantedY(y); + int cnt = _counts[c], rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } +} + +void EmojiPanInner::updateSelected() { + if (_pressedSel >= 0 || _pickerSel >= 0) return; + + int32 selIndex = -1; + QPoint p(mapFromGlobal(_lastMousePos)); + int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::emojiPanPadding; + for (int c = 0; c < emojiTabCount; ++c) { + int cnt = _counts[c]; + y = ytill; + ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); + if (p.y() >= y && p.y() < ytill) { + y += st::emojiPanHeader; + if (p.y() >= y && sx >= 0 && sx < EmojiPanPerRow * st::emojiPanSize.width()) { + selIndex = qFloor((p.y() - y) / st::emojiPanSize.height()) * EmojiPanPerRow + qFloor(sx / st::emojiPanSize.width()); + if (selIndex >= _emojis[c].size()) { + selIndex = -1; + } else { + selIndex += c * MatrixRowShift; + } + } + break; + } + } + + bool startanim = false; + int oldSel = _selected, newSel = selIndex; + + if (newSel != oldSel) { + if (oldSel >= 0) { + _animations.remove(oldSel + 1); + if (_animations.find(-oldSel - 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(-oldSel - 1, getms()); + } + } + if (newSel >= 0) { + _animations.remove(-newSel - 1); + if (_animations.find(newSel + 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(newSel + 1, getms()); + } + } + setCursor((newSel >= 0) ? style::cur_pointer : style::cur_default); + if (newSel >= 0 && !_picker.isHidden()) { + if (newSel != _pickerSel) { + _picker.hideStart(); + } else { + _picker.showStart(); + } + } + } + + _selected = selIndex; + if (startanim && !_a_selected.animating()) _a_selected.start(); +} + +void EmojiPanInner::step_selected(uint64 ms, bool timer) { + QRegion toUpdate; + for (Animations::iterator i = _animations.begin(); i != _animations.end();) { + int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + float64 dt = float64(ms - i.value()) / st::emojiPanDuration; + if (dt >= 1) { + _hovers[tab][sel] = (i.key() > 0) ? 1 : 0; + i = _animations.erase(i); + } else { + _hovers[tab][sel] = (i.key() > 0) ? dt : (1 - dt); + ++i; + } + toUpdate += emojiRect(tab, sel); + } + if (timer) rtlupdate(toUpdate.boundingRect()); + if (_animations.isEmpty()) _a_selected.stop(); +} + +void InlineCacheEntry::clearResults() { + for_const (const InlineBots::Result *result, results) { + delete result; + } + results.clear(); +} + +void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { + clearSelection(true); + + refreshRecent(); + + int32 y = 0; + for (int c = 0; c < emojiTabCount; ++c) { + if (emojiTabAtIndex(c) == packIndex) break; + int rows = (_counts[c] / EmojiPanPerRow) + ((_counts[c] % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + + emit scrollToY(y); + + _lastMousePos = QCursor::pos(); + + update(); +} + +StickerPanInner::StickerPanInner() : TWidget() +, _a_selected(animation(this, &StickerPanInner::step_selected)) +, _section(cShowingSavedGifs() ? Section::Gifs : Section::Stickers) +, _addText(lang(lng_stickers_featured_add).toUpper()) +, _addWidth(st::featuredStickersAdd.font->width(_addText)) +, _settings(this, lang(lng_stickers_you_have)) { + setMaxHeight(st::emojiPanMaxHeight - st::rbEmoji.height); + + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + setAttribute(Qt::WA_OpaquePaintEvent); + + connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); + connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); + + _previewTimer.setSingleShot(true); + connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); + + _updateInlineItems.setSingleShot(true); + connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems())); +} + +void StickerPanInner::setMaxHeight(int32 h) { + _maxHeight = h; + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); + _settings.moveToLeft((st::emojiPanWidth - _settings.width()) / 2, height() / 3); +} + +void StickerPanInner::setScrollTop(int top) { + if (top == _top) return; + + _lastScrolled = getms(); + _top = top; +} + +int StickerPanInner::featuredRowHeight() const { + return st::featuredStickersHeader + st::stickerPanSize.height() + st::featuredStickersSkip; +} + +int StickerPanInner::countHeight(bool plain) { + int result = 0, minLastH = plain ? 0 : (_maxHeight - st::stickerPanPadding); + if (showingInlineItems()) { + result = st::emojiPanHeader; + if (_switchPmButton) { + result += _switchPmButton->height() + st::inlineResultsSkip; + } + for (int i = 0, l = _inlineRows.count(); i < l; ++i) { + result += _inlineRows[i].height; + } + } else if (_section == Section::Featured) { + result = st::emojiPanHeader + shownSets().size() * featuredRowHeight(); + } else { + auto &sets = shownSets(); + for (int i = 0; i < sets.size(); ++i) { + int cnt = sets[i].pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + if (i == sets.size() - 1 && h < minLastH) h = minLastH; + result += h; + } + } + return qMax(minLastH, result) + st::stickerPanPadding; +} + +StickerPanInner::~StickerPanInner() { + clearInlineRows(true); + deleteUnusedGifLayouts(); + deleteUnusedInlineLayouts(); +} + +QRect StickerPanInner::stickerRect(int tab, int sel) { + int x = 0, y = 0; + if (_section == Section::Featured) { + y += st::emojiPanHeader + (tab * featuredRowHeight()) + st::featuredStickersHeader; + x = st::stickerPanPadding + (sel * st::stickerPanSize.width()); + } else { + auto &sets = shownSets(); + for (int i = 0; i < sets.size(); ++i) { + if (i == tab) { + int rows = (((sel >= sets[i].pack.size()) ? (sel - sets[i].pack.size()) : sel) / StickerPanPerRow); + y += st::emojiPanHeader + rows * st::stickerPanSize.height(); + x = st::stickerPanPadding + ((sel % StickerPanPerRow) * st::stickerPanSize.width()); + break; + } else { + int cnt = sets[i].pack.size(); + int rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::stickerPanSize.height(); + } + } + } + return QRect(x, y, st::stickerPanSize.width(), st::stickerPanSize.height()); +} + +void StickerPanInner::paintEvent(QPaintEvent *e) { + Painter p(this); + QRect r = e ? e->rect() : rect(); + if (r != rect()) { + p.setClipRect(r); + } + p.fillRect(r, st::white); + + if (showingInlineItems()) { + paintInlineItems(p, r); + } else { + paintStickers(p, r); + } +} + +void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) { + if (_inlineRows.isEmpty()) { + p.setFont(st::normalFont); + p.setPen(st::noContactsColor); + p.drawText(QRect(0, 0, width(), (height() / 3) * 2 + st::normalFont->height), lang(lng_inline_bot_no_results), style::al_center); + return; + } + InlineBots::Layout::PaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false); + + int top = st::emojiPanHeader; + if (_switchPmButton) { + top += _switchPmButton->height() + st::inlineResultsSkip; + } + + int fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width()); + for (int row = 0, rows = _inlineRows.size(); row < rows; ++row) { + auto &inlineRow = _inlineRows[row]; + if (top >= r.top() + r.height()) break; + if (top + inlineRow.height > r.top()) { + int left = st::inlineResultsLeft; + if (row == rows - 1) context.lastRow = true; + for (int col = 0, cols = inlineRow.items.size(); col < cols; ++col) { + if (left >= tox) break; + + const InlineItem *item = inlineRow.items.at(col); + int w = item->width(); + if (left + w > fromx) { + p.translate(left, top); + item->paint(p, r.translated(-left, -top), &context); + p.translate(-left, -top); + } + left += w; + if (item->hasRightSkip()) { + left += st::inlineResultsSkip; + } + } + } + top += inlineRow.height; + } +} + +void StickerPanInner::paintStickers(Painter &p, const QRect &r) { + int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); + int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); + if (rtl()) { + qSwap(fromcol, tocol); + fromcol = StickerPanPerRow - fromcol; + tocol = StickerPanPerRow - tocol; + } + + int y, tilly = 0; + + auto &sets = shownSets(); + if (_section == Section::Featured) { + tilly += st::emojiPanHeader; + for (int c = 0, l = sets.size(); c < l; ++c) { + y = tilly; + auto &set = sets[c]; + tilly = y + featuredRowHeight(); + if (r.top() >= tilly) continue; + if (y >= r.y() + r.height()) break; + + int size = set.pack.size(); + + int widthForTitle = featuredContentWidth() - st::emojiPanHeaderLeft; + if (featuredHasAddButton(c)) { + auto add = featuredAddRect(c); + auto selected = (_selectedFeaturedSetAdd == c); + auto textBg = selected ? st::featuredStickersAdd.textBgOver : st::featuredStickersAdd.textBg; + auto textTop = (selected && _selectedFeaturedSetAdd == _pressedFeaturedSetAdd) ? st::featuredStickersAdd.downTextTop : st::featuredStickersAdd.textTop; + + App::roundRect(p, myrtlrect(add), textBg, ImageRoundRadius::Small); + p.setFont(st::featuredStickersAdd.font); + p.setPen(selected ? st::featuredStickersAdd.textFgOver : st::featuredStickersAdd.textFg); + p.drawTextLeft(add.x() - (st::featuredStickersAdd.width / 2), add.y() + textTop, width(), _addText, _addWidth); + + widthForTitle -= add.width() - (st::featuredStickersAdd.width / 2); + } + + auto titleText = set.title; + auto titleWidth = st::featuredStickersHeaderFont->width(titleText); + if (titleWidth > widthForTitle) { + titleText = st::featuredStickersHeaderFont->elided(titleText, widthForTitle); + titleWidth = st::featuredStickersHeaderFont->width(titleText); + } + p.setFont(st::featuredStickersHeaderFont); + p.setPen(st::featuredStickersHeaderFg); + p.drawTextLeft(st::emojiPanHeaderLeft, y + st::featuredStickersHeaderTop, width(), titleText, titleWidth); + + p.setFont(st::featuredStickersSubheaderFont); + p.setPen(st::featuredStickersSubheaderFg); + p.drawTextLeft(st::emojiPanHeaderLeft, y + st::featuredStickersSubheaderTop, width(), lng_stickers_count(lt_count, size)); + + y += st::featuredStickersHeader; + if (y >= r.y() + r.height()) break; + + for (int j = fromcol; j < tocol; ++j) { + int index = j; + if (index >= size) break; + + paintSticker(p, set, y, index); + } + } + } else { + for (int c = 0, l = sets.size(); c < l; ++c) { + y = tilly; + auto &set = sets[c]; + int32 size = set.pack.size(); + int32 rows = (size / StickerPanPerRow) + ((size % StickerPanPerRow) ? 1 : 0); + tilly = y + st::emojiPanHeader + (rows * st::stickerPanSize.height()); + if (r.y() >= tilly) continue; + + bool special = (set.flags & MTPDstickerSet::Flag::f_official); + y += st::emojiPanHeader; + if (y >= r.y() + r.height()) break; + + int fromrow = floorclamp(r.y() - y, st::stickerPanSize.height(), 0, rows); + int torow = ceilclamp(r.y() + r.height() - y, st::stickerPanSize.height(), 0, rows); + for (int i = fromrow; i < torow; ++i) { + for (int j = fromcol; j < tocol; ++j) { + int index = i * StickerPanPerRow + j; + if (index >= size) break; + + paintSticker(p, set, y, index); + } + } + } + } +} + +void StickerPanInner::paintSticker(Painter &p, Set &set, int y, int index) { + float64 hover = set.hovers[index]; + + auto sticker = set.pack[index]; + if (!sticker->sticker()) return; + + int row = (index / StickerPanPerRow), col = (index % StickerPanPerRow); + + QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), y + row * st::stickerPanSize.height()); + if (hover > 0) { + p.setOpacity(hover); + QPoint tl(pos); + if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width()); + App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); + p.setOpacity(1); + } + + bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); + if (goodThumb) { + sticker->thumb->load(); + } else { + sticker->checkSticker(); + } + + float64 coef = qMin((st::stickerPanSize.width() - st::buttonRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::buttonRadius * 2) / float64(sticker->dimensions.height())); + if (coef > 1) coef = 1; + int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height()); + if (w < 1) w = 1; + if (h < 1) h = 1; + QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); + if (goodThumb) { + p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h)); + } else if (!sticker->sticker()->img->isNull()) { + p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h)); + } + + if (hover > 0 && set.id == Stickers::RecentSetId && _custom.at(index)) { + float64 xHover = set.hovers[set.pack.size() + index]; + + QPoint xPos = pos + QPoint(st::stickerPanSize.width() - st::stickerPanDelete.pxWidth(), 0); + p.setOpacity(hover * (xHover + (1 - xHover) * st::stickerPanDeleteOpacity)); + p.drawSpriteLeft(xPos, width(), st::stickerPanDelete); + p.setOpacity(1); + } +} + +bool StickerPanInner::featuredHasAddButton(int index) const { + if (index < 0 || index >= _featuredSets.size()) { + return false; + } + auto flags = _featuredSets[index].flags; + return !(flags & MTPDstickerSet::Flag::f_installed) || (flags & MTPDstickerSet::Flag::f_archived); +} + +int StickerPanInner::featuredContentWidth() const { + return st::stickerPanPadding + (StickerPanPerRow * st::stickerPanSize.width()); +} + +QRect StickerPanInner::featuredAddRect(int index) const { + int addw = _addWidth - st::featuredStickersAdd.width; + int addh = st::featuredStickersAdd.height; + int addx = featuredContentWidth() - addw; + int addy = st::emojiPanHeader + index * featuredRowHeight() + st::featuredStickersAddTop; + return QRect(addx, addy, addw, addh); +} + +void StickerPanInner::mousePressEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + updateSelected(); + + _pressed = _selected; + _pressedFeaturedSet = _selectedFeaturedSet; + _pressedFeaturedSetAdd = _selectedFeaturedSetAdd; + ClickHandler::pressed(); + _previewTimer.start(QApplication::startDragTime()); +} + +void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { + _previewTimer.stop(); + + auto pressed = _pressed; + _pressed = -1; + auto pressedFeaturedSet = _pressedFeaturedSet; + _pressedFeaturedSet = -1; + auto pressedFeaturedSetAdd = _pressedFeaturedSetAdd; + if (_pressedFeaturedSetAdd != _selectedFeaturedSetAdd) { + update(); + } + _pressedFeaturedSetAdd = -1; + + ClickHandlerPtr activated = ClickHandler::unpressed(); + + _lastMousePos = e->globalPos(); + updateSelected(); + + if (_previewShown) { + _previewShown = false; + return; + } + + if (showingInlineItems()) { + if (_selected < 0 || _selected != pressed || !activated) { + return; + } + + if (dynamic_cast(activated.data())) { + int row = _selected / MatrixRowShift, column = _selected % MatrixRowShift; + selectInlineResult(row, column); + } else { + App::activateClickHandler(activated, e->button()); + } + return; + } + + auto &sets = shownSets(); + if (_selected >= 0 && _selected < MatrixRowShift * sets.size() && _selected == pressed) { + int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; + if (sets[tab].id == Stickers::RecentSetId && sel >= sets[tab].pack.size() && sel < sets[tab].pack.size() * 2 && _custom.at(sel - sets[tab].pack.size())) { + removeRecentSticker(tab, sel - sets[tab].pack.size()); + return; + } + if (sel < sets[tab].pack.size()) { + emit selected(sets[tab].pack[sel]); + } + } else if (_selectedFeaturedSet >= 0 && _selectedFeaturedSet < sets.size() && _selectedFeaturedSet == pressedFeaturedSet) { + emit displaySet(sets[_selectedFeaturedSet].id); + } else if (_selectedFeaturedSetAdd >= 0 && _selectedFeaturedSetAdd < sets.size() && _selectedFeaturedSetAdd == pressedFeaturedSetAdd) { + emit installSet(sets[_selectedFeaturedSetAdd].id); + } +} + +void StickerPanInner::selectInlineResult(int row, int column) { + if (row >= _inlineRows.size() || column >= _inlineRows.at(row).items.size()) { + return; + } + + auto item = _inlineRows[row].items[column]; + if (auto photo = item->getPhoto()) { + if (photo->medium->loaded() || photo->thumb->loaded()) { + emit selected(photo); + } else if (!photo->medium->loading()) { + photo->thumb->loadEvenCancelled(); + photo->medium->loadEvenCancelled(); + } + } else if (auto document = item->getDocument()) { + if (document->loaded()) { + emit selected(document); + } else if (document->loading()) { + document->cancel(); + } else { + DocumentOpenClickHandler::doOpen(document, ActionOnLoadNone); + } + } else if (auto inlineResult = item->getResult()) { + if (inlineResult->onChoose(item)) { + emit selected(inlineResult, _inlineBot); + } + } +} + +void StickerPanInner::removeRecentSticker(int tab, int index) { + if (_section != Section::Stickers || tab >= _mySets.size() || _mySets[tab].id != Stickers::RecentSetId) { + return; + } + + clearSelection(true); + bool refresh = false; + auto sticker = _mySets[tab].pack[index]; + auto &recent = cGetRecentStickers(); + for (int32 i = 0, l = recent.size(); i < l; ++i) { + if (recent.at(i).first == sticker) { + recent.removeAt(i); + Local::writeUserSettings(); + refresh = true; + break; + } + } + auto &sets = Global::RefStickerSets(); + auto it = sets.find(Stickers::CustomSetId); + if (it != sets.cend()) { + for (int i = 0, l = it->stickers.size(); i < l; ++i) { + if (it->stickers.at(i) == sticker) { + it->stickers.removeAt(i); + if (it->stickers.isEmpty()) { + sets.erase(it); + } + Local::writeInstalledStickers(); + refresh = true; + break; + } + } + } + if (refresh) { + refreshRecentStickers(); + updateSelected(); + update(); + } +} + +void StickerPanInner::mouseMoveEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + updateSelected(); +} + +void StickerPanInner::leaveEvent(QEvent *e) { + clearSelection(); +} + +void StickerPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { + clearSelection(); +} + +void StickerPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { + _lastMousePos = QCursor::pos(); + updateSelected(); +} + +bool StickerPanInner::showSectionIcons() const { + return !inlineResultsShown(); +} + +void StickerPanInner::clearSelection(bool fast) { + _lastMousePos = mapToGlobal(QPoint(-10, -10)); + if (fast) { + if (showingInlineItems()) { + if (_selected >= 0) { + int srow = _selected / MatrixRowShift, scol = _selected % MatrixRowShift; + t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); + ClickHandler::clearActive(_inlineRows.at(srow).items.at(scol)); + setCursor(style::cur_default); + } + _selected = _pressed = -1; + return; + } + + auto &sets = shownSets(); + for (auto i = _animations.cbegin(); i != _animations.cend(); ++i) { + int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + sets[tab].hovers[sel] = 0; + } + _animations.clear(); + if (_selected >= 0) { + int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + if (index >= 0 && tab < sets.size() && sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + sets[tab].pack.size()) { + sets[tab].hovers[sel] = 0; + sel -= sets[tab].pack.size(); + } + sets[tab].hovers[sel] = 0; + } + if (_pressed >= 0) { + int index = qAbs(_pressed), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + if (index >= 0 && tab < sets.size() && sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + sets[tab].pack.size()) { + sets[tab].hovers[sel] = 0; + sel -= sets[tab].pack.size(); + } + sets[tab].hovers[sel] = 0; + } + _selected = _pressed = -1; + _selectedFeaturedSet = _pressedFeaturedSet = -1; + _selectedFeaturedSetAdd = _pressedFeaturedSetAdd = -1; + _a_selected.stop(); + update(); + } else { + updateSelected(); + } +} + +void StickerPanInner::hideFinish(bool completely) { + if (completely) { + auto itemForget = [](const InlineItem *item) { + if (auto document = item->getDocument()) { + document->forget(); + } + if (auto photo = item->getPhoto()) { + photo->forget(); + } + if (auto result = item->getResult()) { + result->forget(); + } + }; + clearInlineRows(false); + for_const (auto item, _gifLayouts) { + itemForget(item); + } + for_const (auto item, _inlineLayouts) { + itemForget(item); + } + } + if (_setGifCommand && _section == Section::Gifs) { + App::insertBotCommand(qsl(""), true); + } + _setGifCommand = false; + + // Reset to the recent stickers section. + if (_section == Section::Featured) { + _section = Section::Stickers; + } +} + +void StickerPanInner::refreshStickers() { + clearSelection(true); + + _mySets.clear(); + _mySets.reserve(Global::StickerSetsOrder().size() + 1); + + refreshRecentStickers(false); + for_const (auto setId, Global::StickerSetsOrder()) { + appendSet(_mySets, setId); + } + + _featuredSets.clear(); + _featuredSets.reserve(Global::FeaturedStickerSetsOrder().size()); + + for_const (auto setId, Global::FeaturedStickerSetsOrder()) { + appendSet(_featuredSets, setId); + } + + if (_section == Section::Stickers) { + int h = countHeight(); + if (h != height()) resize(width(), h); + + _settings.setVisible(_mySets.isEmpty()); + } else { + _settings.hide(); + } + + emit refreshIcons(); + + updateSelected(); +} + +bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) { + InlineItem *layout = nullptr; + if (savedGif) { + layout = layoutPrepareSavedGif(savedGif, (_inlineRows.size() * MatrixRowShift) + row.items.size()); + } else if (result) { + layout = layoutPrepareInlineResult(result, (_inlineRows.size() * MatrixRowShift) + row.items.size()); + } + if (!layout) return false; + + layout->preload(); + if (inlineRowFinalize(row, sumWidth, layout->isFullLine())) { + layout->setPosition(_inlineRows.size() * MatrixRowShift); + } + + sumWidth += layout->maxWidth(); + if (!row.items.isEmpty() && row.items.back()->hasRightSkip()) { + sumWidth += st::inlineResultsSkip; + } + + row.items.push_back(layout); + return true; +} + +bool StickerPanInner::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force) { + if (row.items.isEmpty()) return false; + + bool full = (row.items.size() >= InlineItemsMaxPerRow); + bool big = (sumWidth >= st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft); + if (full || big || force) { + _inlineRows.push_back(layoutInlineRow(row, (full || big) ? sumWidth : 0)); + row = InlineRow(); + row.items.reserve(InlineItemsMaxPerRow); + sumWidth = 0; + return true; + } + return false; +} + +void StickerPanInner::refreshSavedGifs() { + if (_section == Section::Gifs) { + _settings.hide(); + clearInlineRows(false); + + auto &saved = cSavedGifs(); + if (saved.isEmpty()) { + showStickerSet(Stickers::RecentSetId); + return; + } else { + _inlineRows.reserve(saved.size()); + InlineRow row; + row.items.reserve(InlineItemsMaxPerRow); + int sumWidth = 0; + for_const (auto &gif, saved) { + inlineRowsAddItem(gif, 0, row, sumWidth); + } + inlineRowFinalize(row, sumWidth, true); + } + deleteUnusedGifLayouts(); + + int32 h = countHeight(); + if (h != height()) resize(width(), h); + + update(); + } + emit refreshIcons(); + + updateSelected(); +} + +void StickerPanInner::inlineBotChanged() { + _setGifCommand = false; + refreshInlineRows(nullptr, nullptr, true); +} + +void StickerPanInner::clearInlineRows(bool resultsDeleted) { + if (resultsDeleted) { + if (showingInlineItems()) { + _selected = _pressed = -1; + } + } else { + if (showingInlineItems()) { + clearSelection(true); + } + for_const (auto &row, _inlineRows) { + for_const (auto &item, row.items) { + item->setPosition(-1); + } + } + } + _inlineRows.clear(); +} + +InlineItem *StickerPanInner::layoutPrepareSavedGif(DocumentData *doc, int32 position) { + auto i = _gifLayouts.constFind(doc); + if (i == _gifLayouts.cend()) { + if (auto layout = InlineItem::createLayoutGif(doc)) { + i = _gifLayouts.insert(doc, layout.release()); + i.value()->initDimensions(); + } else { + return nullptr; + } + } + if (!i.value()->maxWidth()) return nullptr; + + i.value()->setPosition(position); + return i.value(); +} + +InlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *result, int32 position) { + auto i = _inlineLayouts.constFind(result); + if (i == _inlineLayouts.cend()) { + if (auto layout = InlineItem::createLayout(result, _inlineWithThumb)) { + i = _inlineLayouts.insert(result, layout.release()); + i.value()->initDimensions(); + } else { + return nullptr; + } + } + if (!i.value()->maxWidth()) return nullptr; + + i.value()->setPosition(position); + return i.value(); +} + +void StickerPanInner::deleteUnusedGifLayouts() { + if (_inlineRows.isEmpty() || _section != Section::Gifs) { // delete all + for_const (auto item, _gifLayouts) { + delete item; + } + _gifLayouts.clear(); + } else { + for (auto i = _gifLayouts.begin(); i != _gifLayouts.cend();) { + if (i.value()->position() < 0) { + delete i.value(); + i = _gifLayouts.erase(i); + } else { + ++i; + } + } + } +} + +void StickerPanInner::deleteUnusedInlineLayouts() { + if (_inlineRows.isEmpty() || _section == Section::Gifs) { // delete all + for_const (auto item, _inlineLayouts) { + delete item; + } + _inlineLayouts.clear(); + } else { + for (auto i = _inlineLayouts.begin(); i != _inlineLayouts.cend();) { + if (i.value()->position() < 0) { + delete i.value(); + i = _inlineLayouts.erase(i); + } else { + ++i; + } + } + } +} + +StickerPanInner::InlineRow &StickerPanInner::layoutInlineRow(InlineRow &row, int32 sumWidth) { + int32 count = row.items.size(); + t_assert(count <= InlineItemsMaxPerRow); + + // enumerate items in the order of growing maxWidth() + // for that sort item indices by maxWidth() + int indices[InlineItemsMaxPerRow]; + for (int i = 0; i < count; ++i) { + indices[i] = i; + } + std::sort(indices, indices + count, [&row](int a, int b) -> bool { + return row.items.at(a)->maxWidth() < row.items.at(b)->maxWidth(); + }); + + row.height = 0; + int availw = width() - st::inlineResultsLeft; + for (int i = 0; i < count; ++i) { + int index = indices[i]; + int w = sumWidth ? (row.items.at(index)->maxWidth() * availw / sumWidth) : row.items.at(index)->maxWidth(); + int actualw = qMax(w, int(st::inlineResultsMinWidth)); + row.height = qMax(row.height, row.items.at(index)->resizeGetHeight(actualw)); + if (sumWidth) { + availw -= actualw; + sumWidth -= row.items.at(index)->maxWidth(); + if (index > 0 && row.items.at(index - 1)->hasRightSkip()) { + availw -= st::inlineResultsSkip; + sumWidth -= st::inlineResultsSkip; + } + } + } + return row; +} + +void StickerPanInner::preloadImages() { + if (showingInlineItems()) { + for (int32 row = 0, rows = _inlineRows.size(); row < rows; ++row) { + for (int32 col = 0, cols = _inlineRows.at(row).items.size(); col < cols; ++col) { + _inlineRows.at(row).items.at(col)->preload(); + } + } + return; + } + + auto &sets = shownSets(); + for (int i = 0, l = sets.size(), k = 0; i < l; ++i) { + int count = sets[i].pack.size(); + if (_section == Section::Featured) { + accumulate_min(count, static_cast(StickerPanPerRow)); + } + for (int j = 0; j != count; ++j) { + if (++k > StickerPanPerRow * (StickerPanPerRow + 1)) break; + + auto sticker = sets.at(i).pack.at(j); + if (!sticker || !sticker->sticker()) continue; + + bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); + if (goodThumb) { + sticker->thumb->load(); + } else { + sticker->automaticLoad(0); + } + } + if (k > StickerPanPerRow * (StickerPanPerRow + 1)) break; + } +} + +uint64 StickerPanInner::currentSet(int yOffset) const { + if (showingInlineItems()) { + return Stickers::NoneSetId; + } else if (_section == Section::Featured) { + return Stickers::FeaturedSetId; + } + + int y, ytill = 0; + for (int i = 0, l = _mySets.size(); i < l; ++i) { + int cnt = _mySets[i].pack.size(); + y = ytill; + ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); + if (yOffset < ytill) { + return _mySets[i].id; + } + } + return _mySets.isEmpty() ? Stickers::RecentSetId : _mySets.back().id; +} + +void StickerPanInner::hideInlineRowsPanel() { + clearInlineRows(false); + if (showingInlineItems()) { + _section = cShowingSavedGifs() ? Section::Gifs : Section::Inlines; + if (_section == Section::Gifs) { + refreshSavedGifs(); + emit scrollToY(0); + emit scrollUpdated(); + } else { + showStickerSet(Stickers::RecentSetId); + } + } +} + +void StickerPanInner::clearInlineRowsPanel() { + clearInlineRows(false); +} + +void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) { + if (!entry || entry->switchPmText.isEmpty()) { + _switchPmButton.reset(); + _switchPmStartToken.clear(); + } else { + if (!_switchPmButton) { + _switchPmButton = std_::make_unique(this, QString(), st::switchPmButton); + _switchPmButton->show(); + _switchPmButton->move(st::inlineResultsLeft, st::emojiPanHeader); + connect(_switchPmButton.get(), SIGNAL(clicked()), this, SLOT(onSwitchPm())); + } + _switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper() + _switchPmStartToken = entry->switchPmStartToken; + } + update(); +} + +int StickerPanInner::refreshInlineRows(UserData *bot, const InlineCacheEntry *entry, bool resultsDeleted) { + _inlineBot = bot; + refreshSwitchPmButton(entry); + auto clearResults = [this, entry]() { + if (!entry) { + return true; + } + if (entry->results.isEmpty() && entry->switchPmText.isEmpty()) { + if (!_inlineBot || _inlineBot->username != cInlineGifBotUsername()) { + return true; + } + } + return false; + }; + if (clearResults()) { + if (resultsDeleted) { + clearInlineRows(true); + deleteUnusedInlineLayouts(); + } + emit emptyInlineRows(); + return 0; + } + + clearSelection(true); + + t_assert(_inlineBot != 0); + _inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username)); + + _section = Section::Inlines; + _settings.hide(); + + int32 count = entry->results.size(), from = validateExistingInlineRows(entry->results), added = 0; + + if (count) { + _inlineRows.reserve(count); + InlineRow row; + row.items.reserve(InlineItemsMaxPerRow); + int32 sumWidth = 0; + for (int32 i = from; i < count; ++i) { + if (inlineRowsAddItem(0, entry->results.at(i), row, sumWidth)) { + ++added; + } + } + inlineRowFinalize(row, sumWidth, true); + } + + int32 h = countHeight(); + if (h != height()) resize(width(), h); + update(); + + emit refreshIcons(); + + _lastMousePos = QCursor::pos(); + updateSelected(); + + return added; +} + +int StickerPanInner::validateExistingInlineRows(const InlineResults &results) { + int count = results.size(), until = 0, untilrow = 0, untilcol = 0; + for (; until < count;) { + if (untilrow >= _inlineRows.size() || _inlineRows.at(untilrow).items.at(untilcol)->getResult() != results.at(until)) { + break; + } + ++until; + if (++untilcol == _inlineRows.at(untilrow).items.size()) { + ++untilrow; + untilcol = 0; + } + } + if (until == count) { // all items are layed out + if (untilrow == _inlineRows.size()) { // nothing changed + return until; + } + + for (int i = untilrow, l = _inlineRows.size(), skip = untilcol; i < l; ++i) { + for (int j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) { + if (skip) { + --skip; + } else { + _inlineRows.at(i).items.at(j)->setPosition(-1); + } + } + } + if (!untilcol) { // all good rows are filled + _inlineRows.resize(untilrow); + return until; + } + _inlineRows.resize(untilrow + 1); + _inlineRows[untilrow].items.resize(untilcol); + _inlineRows[untilrow] = layoutInlineRow(_inlineRows[untilrow]); + return until; + } + if (untilrow && !untilcol) { // remove last row, maybe it is not full + --untilrow; + untilcol = _inlineRows.at(untilrow).items.size(); + } + until -= untilcol; + + for (int i = untilrow, l = _inlineRows.size(); i < l; ++i) { + for (int j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) { + _inlineRows.at(i).items.at(j)->setPosition(-1); + } + } + _inlineRows.resize(untilrow); + + if (_inlineRows.isEmpty()) { + _inlineWithThumb = false; + for (int i = until; i < count; ++i) { + if (results.at(i)->hasThumbDisplay()) { + _inlineWithThumb = true; + break; + } + } + } + return until; +} + +void StickerPanInner::notify_inlineItemLayoutChanged(const InlineItem *layout) { + if (_selected < 0 || !showingInlineItems()) { + return; + } + + int row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; + if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { + if (layout == _inlineRows.at(row).items.at(col)) { + updateSelected(); + } + } +} + +void StickerPanInner::ui_repaintInlineItem(const InlineItem *layout) { + uint64 ms = getms(); + if (_lastScrolled + 100 <= ms) { + update(); + } else { + _updateInlineItems.start(_lastScrolled + 100 - ms); + } +} + +bool StickerPanInner::ui_isInlineItemVisible(const InlineItem *layout) { + int32 position = layout->position(); + if (!showingInlineItems() || position < 0) { + return false; + } + + int row = position / MatrixRowShift, col = position % MatrixRowShift; + t_assert((row < _inlineRows.size()) && (col < _inlineRows[row].items.size())); + + auto &inlineItems = _inlineRows[row].items; + int top = st::emojiPanHeader; + for (int32 i = 0; i < row; ++i) { + top += _inlineRows.at(i).height; + } + + return (top < _top + _maxHeight) && (top + _inlineRows.at(row).items.at(col)->height() > _top); +} + +bool StickerPanInner::ui_isInlineItemBeingChosen() { + return showingInlineItems(); +} + +void StickerPanInner::appendSet(Sets &to, uint64 setId) { + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it == sets.cend() || (it->flags & MTPDstickerSet::Flag::f_archived) || it->stickers.isEmpty()) return; + + to.push_back(Set(it->id, it->flags, it->title, it->stickers.size() + 1, it->stickers)); +} + +void StickerPanInner::refreshRecent() { + if (_section == Section::Gifs) { + refreshSavedGifs(); + } else if (_section == Section::Stickers) { + refreshRecentStickers(); + } +} + +void StickerPanInner::refreshRecentStickers(bool performResize) { + _custom.clear(); + clearSelection(true); + auto &sets = Global::StickerSets(); + auto &recent = cGetRecentStickers(); + auto customIt = sets.constFind(Stickers::CustomSetId); + auto cloudIt = sets.constFind(Stickers::CloudRecentSetId); + if (recent.isEmpty() + && (customIt == sets.cend() || customIt->stickers.isEmpty()) + && (cloudIt == sets.cend() || cloudIt->stickers.isEmpty())) { + if (!_mySets.isEmpty() && _mySets.at(0).id == Stickers::RecentSetId) { + _mySets.pop_front(); + } + } else { + StickerPack recentPack; + int customCnt = (customIt == sets.cend()) ? 0 : customIt->stickers.size(); + int cloudCnt = (cloudIt == sets.cend()) ? 0 : cloudIt->stickers.size(); + recentPack.reserve(cloudCnt + recent.size() + customCnt); + _custom.reserve(cloudCnt + recent.size() + customCnt); + if (cloudCnt > 0) { + for_const (auto sticker, cloudIt->stickers) { + recentPack.push_back(sticker); + _custom.push_back(false); + } + } + for_const (auto &recentSticker, recent) { + auto sticker = recentSticker.first; + recentPack.push_back(sticker); + _custom.push_back(false); + } + if (customCnt > 0) { + for_const (auto &sticker, customIt->stickers) { + auto index = recentPack.indexOf(sticker); + if (index >= cloudCnt) { + _custom[index] = true; // mark stickers from recent as custom + } else { + recentPack.push_back(sticker); + _custom.push_back(true); + } + } + } + if (_mySets.isEmpty() || _mySets.at(0).id != Stickers::RecentSetId) { + _mySets.push_back(Set(Stickers::RecentSetId, MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special, lang(lng_recent_stickers), recentPack.size() * 2, recentPack)); + } else { + _mySets[0].pack = recentPack; + _mySets[0].hovers.resize(recentPack.size() * 2); + } + } + + if (performResize && (_section == Section::Stickers || _section == Section::Featured)) { + int32 h = countHeight(); + if (h != height()) { + resize(width(), h); + emit needRefreshPanels(); + } + + updateSelected(); + } +} + +void StickerPanInner::fillIcons(QList &icons) { + icons.clear(); + icons.reserve(_mySets.size() + 1); + if (!cSavedGifs().isEmpty()) { + icons.push_back(StickerIcon(Stickers::NoneSetId)); + } + if (Global::FeaturedStickerSetsUnreadCount()) { + icons.push_back(StickerIcon(Stickers::FeaturedSetId)); + } + + if (!_mySets.isEmpty()) { + int i = 0; + if (_mySets[0].id == Stickers::RecentSetId) { + ++i; + icons.push_back(StickerIcon(Stickers::RecentSetId)); + } + for (int l = _mySets.size(); i < l; ++i) { + auto s = _mySets[i].pack[0]; + int32 availw = st::rbEmoji.width - 2 * st::stickerIconPadding, availh = st::rbEmoji.height - 2 * st::stickerIconPadding; + int32 thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1; + if (availw * thumbh > availh * thumbw) { + pixh = availh; + pixw = (pixh * thumbw) / thumbh; + } else { + pixw = availw; + pixh = thumbw ? ((pixw * thumbh) / thumbw) : 1; + } + if (pixw < 1) pixw = 1; + if (pixh < 1) pixh = 1; + icons.push_back(StickerIcon(_mySets[i].id, s, pixw, pixh)); + } + } + + if (!Global::FeaturedStickerSetsUnreadCount() && !Global::FeaturedStickerSetsOrder().empty()) { + icons.push_back(StickerIcon(Stickers::FeaturedSetId)); + } +} + +void StickerPanInner::fillPanels(QVector &panels) { + for (int32 i = 0; i < panels.size(); ++i) { + panels.at(i)->hide(); + panels.at(i)->deleteLater(); + } + panels.clear(); + + if (_section != Section::Stickers) { + auto title = [this]() -> QString { + if (_section == Section::Gifs) { + return lang(lng_saved_gifs); + } else if (_section == Section::Inlines) { + return _inlineBotTitle; + } + return lang(lng_stickers_featured); + }; + panels.push_back(new EmojiPanel(parentWidget(), title(), Stickers::NoneSetId, true, 0)); + panels.back()->show(); + return; + } + + if (_mySets.isEmpty()) return; + + int y = 0; + panels.reserve(_mySets.size()); + for (int32 i = 0, l = _mySets.size(); i < l; ++i) { + bool special = (_mySets[i].flags & MTPDstickerSet::Flag::f_official); + panels.push_back(new EmojiPanel(parentWidget(), _mySets[i].title, _mySets[i].id, special, y)); + panels.back()->show(); + connect(panels.back(), SIGNAL(deleteClicked(quint64)), this, SIGNAL(removeSet(quint64))); + int cnt = _mySets[i].pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + y += h; + } +} + +void StickerPanInner::refreshPanels(QVector &panels) { + if (_section != Section::Stickers) return; + + if (panels.size() != _mySets.size()) { + return fillPanels(panels); + } + + int y = 0; + for (int i = 0, l = _mySets.size(); i < l; ++i) { + panels.at(i)->setWantedY(y); + int cnt = _mySets[i].pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + y += h; + } +} + +void StickerPanInner::updateSelected() { + if (_pressed >= 0 && !_previewShown) { + return; + } + + int selIndex = -1; + auto p = mapFromGlobal(_lastMousePos); + + if (showingInlineItems()) { + int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft; + int sy = p.y() - st::emojiPanHeader; + if (_switchPmButton) { + sy -= _switchPmButton->height() + st::inlineResultsSkip; + } + int row = -1, col = -1, sel = -1; + ClickHandlerPtr lnk; + ClickHandlerHost *lnkhost = nullptr; + HistoryCursorState cursor = HistoryDefaultCursorState; + if (sy >= 0) { + row = 0; + for (int rows = _inlineRows.size(); row < rows; ++row) { + if (sy < _inlineRows.at(row).height) { + break; + } + sy -= _inlineRows.at(row).height; + } + } + if (sx >= 0 && row >= 0 && row < _inlineRows.size()) { + auto &inlineItems = _inlineRows[row].items; + col = 0; + for (int cols = inlineItems.size(); col < cols; ++col) { + int width = inlineItems.at(col)->width(); + if (sx < width) { + break; + } + sx -= width; + if (inlineItems.at(col)->hasRightSkip()) { + sx -= st::inlineResultsSkip; + } + } + if (col < inlineItems.size()) { + sel = row * MatrixRowShift + col; + inlineItems.at(col)->getState(lnk, cursor, sx, sy); + lnkhost = inlineItems.at(col); + } else { + row = col = -1; + } + } else { + row = col = -1; + } + int srow = (_selected >= 0) ? (_selected / MatrixRowShift) : -1; + int scol = (_selected >= 0) ? (_selected % MatrixRowShift) : -1; + if (_selected != sel) { + if (srow >= 0 && scol >= 0) { + t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); + Ui::repaintInlineItem(_inlineRows.at(srow).items.at(scol)); + } + _selected = sel; + if (row >= 0 && col >= 0) { + t_assert(row >= 0 && row < _inlineRows.size() && col >= 0 && col < _inlineRows.at(row).items.size()); + Ui::repaintInlineItem(_inlineRows.at(row).items.at(col)); + } + if (_pressed >= 0 && _selected >= 0 && _pressed != _selected) { + _pressed = _selected; + if (row >= 0 && col >= 0) { + auto layout = _inlineRows.at(row).items.at(col); + if (auto previewDocument = layout->getPreviewDocument()) { + Ui::showMediaPreview(previewDocument); + } else if (auto previewPhoto = layout->getPreviewPhoto()) { + Ui::showMediaPreview(previewPhoto); + } + } + } + } + if (ClickHandler::setActive(lnk, lnkhost)) { + setCursor(lnk ? style::cur_pointer : style::cur_default); + } + return; + } + + int selectedFeaturedSet = -1; + int selectedFeaturedSetAdd = -1; + auto featured = (_section == Section::Featured); + auto &sets = shownSets(); + int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding; + if (featured) { + ytill += st::emojiPanHeader; + } + for (int c = 0, l = sets.size(); c < l; ++c) { + auto &set = sets[c]; + bool special = featured ? false : (set.flags & MTPDstickerSet::Flag::f_official); + + y = ytill; + if (featured) { + ytill = y + featuredRowHeight(); + } else { + int cnt = set.pack.size(); + ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); + } + if (p.y() >= y && p.y() < ytill) { + if (featured) { + if (p.y() < y + st::featuredStickersHeader) { + if (featuredHasAddButton(c) && myrtlrect(featuredAddRect(c)).contains(p.x(), p.y())) { + selectedFeaturedSetAdd = c; + } else { + selectedFeaturedSet = c; + } + break; + } + y += st::featuredStickersHeader; + } else { + y += st::emojiPanHeader; + } + if (p.y() >= y && sx >= 0 && sx < StickerPanPerRow * st::stickerPanSize.width()) { + auto rowIndex = qFloor((p.y() - y) / st::stickerPanSize.height()); + if (!featured || !rowIndex) { + selIndex = rowIndex * StickerPanPerRow + qFloor(sx / st::stickerPanSize.width()); + if (selIndex >= set.pack.size()) { + selIndex = -1; + } else { + if (set.id == Stickers::RecentSetId && _custom[selIndex]) { + int inx = sx - (selIndex % StickerPanPerRow) * st::stickerPanSize.width(), iny = p.y() - y - ((selIndex / StickerPanPerRow) * st::stickerPanSize.height()); + if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.pxWidth() && iny < st::stickerPanDelete.pxHeight()) { + selIndex += set.pack.size(); + } + } + selIndex += c * MatrixRowShift; + } + } + } + break; + } + } + + bool startanim = false; + int oldSel = _selected, oldSelTab = oldSel / MatrixRowShift, xOldSel = -1, newSel = selIndex, newSelTab = newSel / MatrixRowShift, xNewSel = -1; + if (oldSel >= 0 && oldSelTab < sets.size() && sets[oldSelTab].id == Stickers::RecentSetId && oldSel >= oldSelTab * MatrixRowShift + sets[oldSelTab].pack.size()) { + xOldSel = oldSel; + oldSel -= sets[oldSelTab].pack.size(); + } + if (newSel >= 0 && newSelTab < sets.size() && sets[newSelTab].id == Stickers::RecentSetId && newSel >= newSelTab * MatrixRowShift + sets[newSelTab].pack.size()) { + xNewSel = newSel; + newSel -= sets[newSelTab].pack.size(); + } + if (newSel != oldSel || selectedFeaturedSet != _selectedFeaturedSet || selectedFeaturedSetAdd != _selectedFeaturedSetAdd) { + setCursor((newSel >= 0 || selectedFeaturedSet >= 0 || selectedFeaturedSetAdd >= 0) ? style::cur_pointer : style::cur_default); + } + if (newSel != oldSel) { + if (oldSel >= 0) { + _animations.remove(oldSel + 1); + if (_animations.find(-oldSel - 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(-oldSel - 1, getms()); + } + } + if (newSel >= 0) { + _animations.remove(-newSel - 1); + if (_animations.find(newSel + 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(newSel + 1, getms()); + } + } + } + if (selectedFeaturedSet != _selectedFeaturedSet) { + _selectedFeaturedSet = selectedFeaturedSet; + } + if (selectedFeaturedSetAdd != _selectedFeaturedSetAdd) { + _selectedFeaturedSetAdd = selectedFeaturedSetAdd; + update(); + } + if (xNewSel != xOldSel) { + if (xOldSel >= 0) { + _animations.remove(xOldSel + 1); + if (_animations.find(-xOldSel - 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(-xOldSel - 1, getms()); + } + } + if (xNewSel >= 0) { + _animations.remove(-xNewSel - 1); + if (_animations.find(xNewSel + 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(xNewSel + 1, getms()); + } + } + } + _selected = selIndex; + if (_pressed >= 0 && _selected >= 0 && _pressed != _selected) { + _pressed = _selected; + if (newSel >= 0 && xNewSel < 0) { + Ui::showMediaPreview(sets.at(newSelTab).pack.at(newSel % MatrixRowShift)); + } + } + if (startanim && !_a_selected.animating()) _a_selected.start(); +} + +void StickerPanInner::onSettings() { + Ui::showLayer(new StickersBox()); +} + +void StickerPanInner::onPreview() { + if (_pressed < 0) return; + if (showingInlineItems()) { + int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift; + if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { + auto layout = _inlineRows.at(row).items.at(col); + if (auto previewDocument = layout->getPreviewDocument()) { + Ui::showMediaPreview(previewDocument); + _previewShown = true; + } else if (auto previewPhoto = layout->getPreviewPhoto()) { + Ui::showMediaPreview(previewPhoto); + _previewShown = true; + } + } + } else { + auto &sets = shownSets(); + if (_pressed < MatrixRowShift * sets.size()) { + int tab = (_pressed / MatrixRowShift), sel = _pressed % MatrixRowShift; + if (sel < sets[tab].pack.size()) { + Ui::showMediaPreview(sets[tab].pack[sel]); + _previewShown = true; + } + } + } +} + +void StickerPanInner::onUpdateInlineItems() { + if (!showingInlineItems()) return; + + uint64 ms = getms(); + if (_lastScrolled + 100 <= ms) { + update(); + } else { + _updateInlineItems.start(_lastScrolled + 100 - ms); + } +} + +void StickerPanInner::onSwitchPm() { + if (_inlineBot && _inlineBot->botInfo) { + _inlineBot->botInfo->startToken = _switchPmStartToken; + Ui::showPeerHistory(_inlineBot, ShowAndStartBotMsgId); + } +} + +void StickerPanInner::step_selected(uint64 ms, bool timer) { + QRegion toUpdate; + auto &sets = shownSets(); + for (auto i = _animations.begin(); i != _animations.end();) { + int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + float64 dt = float64(ms - i.value()) / st::emojiPanDuration; + if (dt >= 1) { + sets[tab].hovers[sel] = (i.key() > 0) ? 1 : 0; + i = _animations.erase(i); + } else { + sets[tab].hovers[sel] = (i.key() > 0) ? dt : (1 - dt); + ++i; + } + toUpdate += stickerRect(tab, sel); + } + if (timer) rtlupdate(toUpdate.boundingRect()); + if (_animations.isEmpty()) _a_selected.stop(); +} + +void StickerPanInner::showStickerSet(uint64 setId) { + clearSelection(true); + + if (setId == Stickers::NoneSetId) { + if (!showingInlineItems()) { + _section = Section::Gifs; + cSetShowingSavedGifs(true); + emit saveConfigDelayed(SaveRecentEmojisTimeout); + } + refreshSavedGifs(); + emit scrollToY(0); + emit scrollUpdated(); + showFinish(); + return; + } + + if (showingInlineItems()) { + if (_setGifCommand && _section == Section::Gifs) { + App::insertBotCommand(qsl(""), true); + } + _setGifCommand = false; + + cSetShowingSavedGifs(false); + emit saveConfigDelayed(SaveRecentEmojisTimeout); + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); + } + + if (setId == Stickers::FeaturedSetId) { + if (_section != Section::Featured) { + _section = Section::Featured; + + refreshRecentStickers(true); + emit refreshIcons(); + update(); + } + + emit scrollToY(0); + emit scrollUpdated(); + return; + } + + bool needRefresh = (_section != Section::Stickers); + if (needRefresh) { + _section = Section::Stickers; + refreshRecentStickers(true); + } + + int32 y = 0; + for (int c = 0; c < _mySets.size(); ++c) { + if (_mySets.at(c).id == setId) break; + int rows = (_mySets[c].pack.size() / StickerPanPerRow) + ((_mySets[c].pack.size() % StickerPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::stickerPanSize.height(); + } + + emit scrollToY(y); + emit scrollUpdated(); + + if (needRefresh) { + emit refreshIcons(); + } + + _lastMousePos = QCursor::pos(); + + update(); +} + +void StickerPanInner::updateShowingSavedGifs() { + if (cShowingSavedGifs()) { + if (!showingInlineItems()) { + clearSelection(true); + _section = Section::Gifs; + if (_inlineRows.isEmpty()) refreshSavedGifs(); + } + } else if (!showingInlineItems()) { + clearSelection(true); + } +} + +void StickerPanInner::showFinish() { + if (_section == Section::Gifs) { + _setGifCommand = App::insertBotCommand('@' + cInlineGifBotUsername(), true); + } +} + +EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent) +, _wantedY(wantedY) +, _setId(setId) +, _special(special) +, _deleteVisible(false) +, _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // Stickers::NoneSetId if in emoji + resize(st::emojiPanWidth, st::emojiPanHeader); + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + setText(text); + if (_delete) { + _delete->hide(); + _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::notifyClose.icon.pxWidth()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); + connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); + } +} + +void EmojiPanel::onDelete() { + emit deleteClicked(_setId); +} + +void EmojiPanel::setText(const QString &text) { + _fullText = text; + updateText(); +} + +void EmojiPanel::updateText() { + int32 availw = st::emojiPanWidth - st::emojiPanHeaderLeft * 2; + if (_deleteVisible) { + if (!_special && _setId != Stickers::NoneSetId) { + availw -= st::notifyClose.icon.pxWidth() + st::emojiPanHeaderLeft; + } + } else { + auto switchText = ([this]() { + if (_setId != Stickers::NoneSetId) { + return lang(lng_switch_emoji); + } + if (cSavedGifs().isEmpty()) { + return lang(lng_switch_stickers); + } + return lang(lng_switch_stickers_gifs); + })(); + availw -= st::emojiSwitchSkip + st::emojiPanHeaderFont->width(switchText); + } + _text = st::emojiPanHeaderFont->elided(_fullText, availw); + update(); +} + +void EmojiPanel::setDeleteVisible(bool isVisible) { + if (_deleteVisible != isVisible) { + _deleteVisible = isVisible; + updateText(); + if (_delete) { + _delete->setVisible(_deleteVisible); + } + } +} + +void EmojiPanel::mousePressEvent(QMouseEvent *e) { + emit mousePressed(); +} + +void EmojiPanel::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_deleteVisible) { + p.fillRect(0, 0, width(), st::emojiPanHeader, st::emojiPanHeaderBg->b); + } + p.setFont(st::emojiPanHeaderFont); + p.setPen(st::emojiPanHeaderColor); + p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); +} + +EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent) +, _toStickers(toStickers) { + setCursor(style::cur_pointer); + updateText(); +} + +void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { + if (_toStickers) { + if (inlineBotUsername.isEmpty()) { + _text = lang(cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs); + } else { + _text = '@' + inlineBotUsername; + } + } else { + _text = lang(lng_switch_emoji); + } + _textWidth = st::emojiPanHeaderFont->width(_text); + if (_toStickers && !inlineBotUsername.isEmpty()) { + int32 maxw = 0; + for (int c = 0; c < emojiTabCount; ++c) { + maxw = qMax(maxw, st::emojiPanHeaderFont->width(lang(LangKey(lng_emoji_category0 + c)))); + } + maxw += st::emojiPanHeaderLeft + st::emojiSwitchSkip + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); + if (_textWidth > st::emojiPanWidth - maxw) { + _text = st::emojiPanHeaderFont->elided(_text, st::emojiPanWidth - maxw); + _textWidth = st::emojiPanHeaderFont->width(_text); + } + } + + int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); + resize(w, st::emojiPanHeader); +} + +void EmojiSwitchButton::paintEvent(QPaintEvent *e) { + Painter p(this); + + p.setFont(st::emojiPanHeaderFont->f); + p.setPen(st::emojiSwitchColor->p); + if (_toStickers) { + p.drawTextRight(st::emojiSwitchSkip, st::emojiPanHeaderTop, width(), _text, _textWidth); + p.drawSpriteRight(QPoint(st::emojiSwitchImgSkip - st::emojiSwitchStickers.pxWidth(), (st::emojiPanHeader - st::emojiSwitchStickers.pxHeight()) / 2), width(), st::emojiSwitchStickers); + } else { + p.drawTextRight(st::emojiSwitchImgSkip - st::emojiSwitchEmoji.pxWidth(), st::emojiPanHeaderTop, width(), lang(lng_switch_emoji), _textWidth); + p.drawSpriteRight(QPoint(st::emojiSwitchSkip + _textWidth - st::emojiSwitchEmoji.pxWidth(), (st::emojiPanHeader - st::emojiSwitchEmoji.pxHeight()) / 2), width(), st::emojiSwitchEmoji); + } +} + +} // namespace internal + +EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) +, _maxHeight(st::emojiPanMaxHeight) +, _contentMaxHeight(st::emojiPanMaxHeight) +, _contentHeight(_contentMaxHeight) +, _contentHeightEmoji(_contentHeight - st::rbEmoji.height) +, _contentHeightStickers(_contentHeight - st::rbEmoji.height) +, _a_appearance(animation(this, &EmojiPan::step_appearance)) +, _shadow(st::dropdownDef.shadow) +, _recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent) +, _people(this , qsl("emoji_group"), dbietPeople , QString(), false, st::rbEmojiPeople) +, _nature(this , qsl("emoji_group"), dbietNature , QString(), false, st::rbEmojiNature) +, _food(this , qsl("emoji_group"), dbietFood , QString(), false, st::rbEmojiFood) +, _activity(this, qsl("emoji_group"), dbietActivity, QString(), false, st::rbEmojiActivity) +, _travel(this , qsl("emoji_group"), dbietTravel , QString(), false, st::rbEmojiTravel) +, _objects(this , qsl("emoji_group"), dbietObjects , QString(), false, st::rbEmojiObjects) +, _symbols(this , qsl("emoji_group"), dbietSymbols , QString(), false, st::rbEmojiSymbols) +, _a_icons(animation(this, &EmojiPan::step_icons)) +, _a_slide(animation(this, &EmojiPan::step_slide)) +, e_scroll(this, st::emojiScroll) +, e_inner() +, e_switch(&e_scroll, true) +, s_scroll(this, st::emojiScroll) +, s_inner() +, s_switch(&s_scroll, false) { + setFocusPolicy(Qt::NoFocus); + e_scroll.setFocusPolicy(Qt::NoFocus); + e_scroll.viewport()->setFocusPolicy(Qt::NoFocus); + s_scroll.setFocusPolicy(Qt::NoFocus); + s_scroll.viewport()->setFocusPolicy(Qt::NoFocus); + + _width = st::dropdownDef.padding.left() + st::emojiPanWidth + st::dropdownDef.padding.right(); + _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); + _bottom = 0; + resize(_width, _height); + + e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); + + e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); + e_scroll.setWidget(&e_inner); + s_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); + s_scroll.setWidget(&s_inner); + + e_inner.moveToLeft(0, 0, e_scroll.width()); + s_inner.moveToLeft(0, 0, s_scroll.width()); + + int32 left = _iconsLeft = st::dropdownDef.padding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2; + int32 top = _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; + prepareTab(left, top, _width, _recent); + prepareTab(left, top, _width, _people); + prepareTab(left, top, _width, _nature); + prepareTab(left, top, _width, _food); + prepareTab(left, top, _width, _activity); + prepareTab(left, top, _width, _travel); + prepareTab(left, top, _width, _objects); + prepareTab(left, top, _width, _symbols); + e_inner.fillPanels(e_panels); + updatePanelsPositions(e_panels, 0); + + _hideTimer.setSingleShot(true); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); + + connect(&e_inner, SIGNAL(scrollToY(int)), &e_scroll, SLOT(scrollToY(int))); + connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool))); + + connect(&s_inner, SIGNAL(scrollToY(int)), &s_scroll, SLOT(scrollToY(int))); + connect(&s_inner, SIGNAL(scrollUpdated()), this, SLOT(onScrollStickers())); + + connect(&e_scroll, SIGNAL(scrolled()), this, SLOT(onScrollEmoji())); + connect(&s_scroll, SIGNAL(scrolled()), this, SLOT(onScrollStickers())); + + connect(&e_inner, SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr))); + connect(&s_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*))); + connect(&s_inner, SIGNAL(selected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*))); + connect(&s_inner, SIGNAL(selected(InlineBots::Result*,UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*))); + + connect(&s_inner, SIGNAL(emptyInlineRows()), this, SLOT(onEmptyInlineRows())); + + connect(&s_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); + connect(&e_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); + s_switch.moveToRight(0, 0, st::emojiPanWidth); + e_switch.moveToRight(0, 0, st::emojiPanWidth); + + connect(&s_inner, SIGNAL(displaySet(quint64)), this, SLOT(onDisplaySet(quint64))); + connect(&s_inner, SIGNAL(installSet(quint64)), this, SLOT(onInstallSet(quint64))); + connect(&s_inner, SIGNAL(removeSet(quint64)), this, SLOT(onRemoveSet(quint64))); + connect(&s_inner, SIGNAL(refreshIcons()), this, SLOT(onRefreshIcons())); + connect(&e_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); + connect(&s_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); + + _saveConfigTimer.setSingleShot(true); + connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig())); + connect(&e_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); + connect(&s_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); + + // inline bots + _inlineRequestTimer.setSingleShot(true); + connect(&_inlineRequestTimer, SIGNAL(timeout()), this, SLOT(onInlineRequest())); + + if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { + connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); + } + + setMouseTracking(true); +// setAttribute(Qt::WA_AcceptTouchEvents); +} + +void EmojiPan::setMaxHeight(int32 h) { + _maxHeight = h; + updateContentHeight(); +} + +void EmojiPan::updateContentHeight() { + int32 h = qMin(_contentMaxHeight, _maxHeight); + int32 he = h - st::rbEmoji.height; + int32 hs = h - (s_inner.showSectionIcons() ? st::rbEmoji.height : 0); + if (h == _contentHeight && he == _contentHeightEmoji && hs == _contentHeightStickers) return; + + int32 was = _contentHeight, wase = _contentHeightEmoji, wass = _contentHeightStickers; + _contentHeight = h; + _contentHeightEmoji = he; + _contentHeightStickers = hs; + + _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); + + resize(_width, _height); + move(x(), _bottom - height()); + + if (was > _contentHeight || (was == _contentHeight && wass > _contentHeightStickers)) { + e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); + s_inner.setMaxHeight(_contentHeightStickers); + e_inner.setMaxHeight(_contentHeightEmoji); + } else { + s_inner.setMaxHeight(_contentHeightStickers); + e_inner.setMaxHeight(_contentHeightEmoji); + e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); + } + + _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; + _recent.move(_recent.x(), _iconsTop); + _people.move(_people.x(), _iconsTop); + _nature.move(_nature.x(), _iconsTop); + _food.move(_food.x(), _iconsTop); + _activity.move(_activity.x(), _iconsTop); + _travel.move(_travel.x(), _iconsTop); + _objects.move(_objects.x(), _iconsTop); + _symbols.move(_symbols.x(), _iconsTop); + + update(); +} + +void EmojiPan::prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab) { + tab.moveToLeft(left, top, _width); + left += tab.width(); + tab.setAttribute(Qt::WA_OpaquePaintEvent); + connect(&tab, SIGNAL(changed()), this, SLOT(onTabChange())); +} + +void EmojiPan::onWndActiveChanged() { + if (!App::wnd()->windowHandle()->isActive() && !isHidden()) { + leaveEvent(0); + } +} + +void EmojiPan::onSaveConfig() { + Local::writeUserSettings(); +} + +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); +} + +void EmojiPan::paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const { + if (auto unread = Global::FeaturedStickerSetsUnreadCount()) { + Dialogs::Layout::UnreadBadgeStyle unreadSt; + unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersPanel; + unreadSt.size = st::stickersSettingsUnreadSize; + int unreadRight = iconLeft + 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); + + float64 o = 1; + if (!_cache.isNull()) { + p.setOpacity(o = a_opacity.current()); + } + + QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); + + _shadow.paint(p, r, st::dropdownDef.shadowShift); + + if (_toCache.isNull()) { + if (_cache.isNull()) { + 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); + paintStickerSettingsIcon(p); + + if (!_icons.isEmpty()) { + int x = _iconsLeft, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current(); + + QRect clip(x, _iconsTop, _iconsLeft + 7 * st::rbEmoji.width - x, st::rbEmoji.height); + if (rtl()) clip.moveLeft(width() - x - clip.width()); + p.setClipRect(clip); + + auto getSpecialSetIcon = [](uint64 setId, bool active) { + if (setId == Stickers::NoneSetId) { + return active ? st::savedGifsActive : st::savedGifsOver; + } else if (setId == Stickers::FeaturedSetId) { + return active ? st::featuredStickersActive : st::featuredStickersOver; + } + return active ? st::rbEmojiRecent.chkImageRect : st::rbEmojiRecent.imageRect; + }; + + int i = 0; + i += _iconsX.current() / int(st::rbEmoji.width); + x -= _iconsX.current() % int(st::rbEmoji.width); + selxrel -= _iconsX.current(); + for (int l = qMin(_icons.size(), i + 8); i < l; ++i) { + auto &s = _icons.at(i); + if (s.sticker) { + s.sticker->thumb->load(); + QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh)); + + 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) { + 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 (s.setId == Stickers::FeaturedSetId) { + paintFeaturedStickerSetsBadge(p, x); + } + x += st::rbEmoji.width; + } + } + + if (rtl()) selx = width() - selx - st::rbEmoji.width; + p.setOpacity(1.); + p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor); + + float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.); + if (o_left > 0) { + p.setOpacity(o_left); + p.drawSpriteLeft(QRect(_iconsLeft, _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft); + } + float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.pxWidth(), 0., 1.); + if (o_right > 0) { + p.setOpacity(o_right); + p.drawSpriteRight(QRect(width() - _iconsLeft - 7 * st::rbEmoji.width, _iconsTop, st::stickerIconRight.pxWidth(), st::rbEmoji.height), width(), st::stickerIconRight); + } + } + } else if (_stickersShown) { + int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width()); + p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::white); + } else { + p.fillRect(r.left(), _recent.y(), (rtl() ? _objects.x() : _recent.x() - r.left()), st::rbEmoji.height, st::emojiPanCategories); + int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width()); + p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::emojiPanCategories); + } + } else { + p.fillRect(r, st::white); + p.drawPixmap(r.left(), r.top(), _cache); + } + } else { + p.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::rbEmoji.height), st::white->b); + p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height), st::emojiPanCategories->b); + p.setOpacity(o * a_fromAlpha.current()); + QRect fromDst = QRect(r.left() + a_fromCoord.current(), r.top(), _fromCache.width() / cIntRetinaFactor(), _fromCache.height() / cIntRetinaFactor()); + QRect fromSrc = QRect(0, 0, _fromCache.width(), _fromCache.height()); + if (fromDst.x() < r.left() + r.width() && fromDst.x() + fromDst.width() > r.left()) { + if (fromDst.x() < r.left()) { + fromSrc.setX((r.left() - fromDst.x()) * cIntRetinaFactor()); + fromDst.setX(r.left()); + } else if (fromDst.x() + fromDst.width() > r.left() + r.width()) { + fromSrc.setWidth((r.left() + r.width() - fromDst.x()) * cIntRetinaFactor()); + fromDst.setWidth(r.left() + r.width() - fromDst.x()); + } + p.drawPixmap(fromDst, _fromCache, fromSrc); + } + p.setOpacity(o * a_toAlpha.current()); + QRect toDst = QRect(r.left() + a_toCoord.current(), r.top(), _toCache.width() / cIntRetinaFactor(), _toCache.height() / cIntRetinaFactor()); + QRect toSrc = QRect(0, 0, _toCache.width(), _toCache.height()); + if (toDst.x() < r.left() + r.width() && toDst.x() + toDst.width() > r.left()) { + if (toDst.x() < r.left()) { + toSrc.setX((r.left() - toDst.x()) * cIntRetinaFactor()); + toDst.setX(r.left()); + } else if (toDst.x() + toDst.width() > r.left() + r.width()) { + toSrc.setWidth((r.left() + r.width() - toDst.x()) * cIntRetinaFactor()); + toDst.setWidth(r.left() + r.width() - toDst.x()); + } + p.drawPixmap(toDst, _toCache, toSrc); + } + } +} + +void EmojiPan::moveBottom(int32 bottom, bool force) { + _bottom = bottom; + if (isHidden() && !force) { + move(x(), _bottom - height()); + return; + } + if (_stickersShown && s_inner.inlineResultsShown()) { + moveToLeft(0, _bottom - height()); + } else { + moveToRight(0, _bottom - height()); + } +} + +void EmojiPan::enterEvent(QEvent *e) { + _hideTimer.stop(); + if (_hiding) showStart(); +} + +bool EmojiPan::preventAutoHide() const { + return _removingSetId || _displayingSetId; +} + +void EmojiPan::leaveEvent(QEvent *e) { + if (preventAutoHide() || s_inner.inlineResultsShown()) return; + if (_a_appearance.animating()) { + hideStart(); + } else { + _hideTimer.start(300); + } +} + +void EmojiPan::otherEnter() { + _hideTimer.stop(); + showStart(); +} + +void EmojiPan::otherLeave() { + if (preventAutoHide() || s_inner.inlineResultsShown()) return; + if (_a_appearance.animating()) { + hideStart(); + } else { + _hideTimer.start(0); + } +} + +void EmojiPan::mousePressEvent(QMouseEvent *e) { + if (!_stickersShown) return; + _iconsMousePos = e ? e->globalPos() : QCursor::pos(); + updateSelected(); + + if (_iconOver == _icons.size()) { + Ui::showLayer(new StickersBox()); + } else { + _iconDown = _iconOver; + _iconsMouseDown = _iconsMousePos; + _iconsStartX = _iconsX.current(); + } +} + +void EmojiPan::mouseMoveEvent(QMouseEvent *e) { + if (!_stickersShown) return; + _iconsMousePos = e ? e->globalPos() : QCursor::pos(); + updateSelected(); + + if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= 0) { + if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) { + _iconsDragging = true; + } + } + if (_iconsDragging) { + int32 newX = snap(_iconsStartX + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()), 0, _iconsMax); + if (newX != _iconsX.current()) { + _iconsX = anim::ivalue(newX, newX); + _iconsStartAnim = 0; + if (_iconAnimations.isEmpty()) _a_icons.stop(); + updateIcons(); + } + } +} + +void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { + if (!_stickersShown || _icons.isEmpty()) return; + + int32 wasDown = _iconDown; + _iconDown = -1; + + _iconsMousePos = e ? e->globalPos() : QCursor::pos(); + if (_iconsDragging) { + int32 newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax); + if (newX != _iconsX.current()) { + _iconsX = anim::ivalue(newX, newX); + _iconsStartAnim = 0; + if (_iconAnimations.isEmpty()) _a_icons.stop(); + updateIcons(); + } + _iconsDragging = false; + updateSelected(); + } else { + updateSelected(); + + if (wasDown == _iconOver && _iconOver >= 0 && _iconOver < _icons.size()) { + _iconSelX = anim::ivalue(_iconOver * st::rbEmoji.width, _iconOver * st::rbEmoji.width); + s_inner.showStickerSet(_icons.at(_iconOver).setId); + } + } +} + +bool EmojiPan::event(QEvent *e) { + if (e->type() == QEvent::TouchBegin) { + + } else if (e->type() == QEvent::Wheel) { + if (!_icons.isEmpty() && _iconOver >= 0 && _iconOver < _icons.size() && _iconDown < 0) { + QWheelEvent *ev = static_cast(e); + bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal); + bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical); + if (hor) _horizontal = true; + int32 newX = _iconsX.current(); + if (/*_horizontal && */hor) { + newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax); + } else if (/*!_horizontal && */ver) { + newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax); + } + if (newX != _iconsX.current()) { + _iconsX = anim::ivalue(newX, newX); + _iconsStartAnim = 0; + if (_iconAnimations.isEmpty()) _a_icons.stop(); + updateSelected(); + updateIcons(); + } + } + } + return TWidget::event(e); +} + +void EmojiPan::fastHide() { + if (_a_appearance.animating()) { + _a_appearance.stop(); + } + a_opacity = anim::fvalue(0, 0); + _hideTimer.stop(); + hide(); + _cache = QPixmap(); +} + +void EmojiPan::refreshStickers() { + s_inner.refreshStickers(); + if (!_stickersShown) { + s_inner.preloadImages(); + } + update(); +} + +void EmojiPan::refreshSavedGifs() { + e_switch.updateText(); + e_switch.moveToRight(0, 0, st::emojiPanWidth); + s_inner.refreshSavedGifs(); + if (!_stickersShown) { + s_inner.preloadImages(); + } +} + +void EmojiPan::onRefreshIcons() { + _iconOver = -1; + _iconHovers.clear(); + _iconAnimations.clear(); + s_inner.fillIcons(_icons); + s_inner.fillPanels(s_panels); + _iconsX.finish(); + _iconSelX.finish(); + _iconsStartAnim = 0; + _a_icons.stop(); + if (_icons.isEmpty()) { + _iconsMax = 0; + } else { + _iconHovers = QVector(_icons.size(), 0); + _iconsMax = qMax(int((_icons.size() - 7) * st::rbEmoji.width), 0); + } + if (_iconsX.current() > _iconsMax) { + _iconsX = anim::ivalue(_iconsMax, _iconsMax); + } + updatePanelsPositions(s_panels, s_scroll.scrollTop()); + updateSelected(); + if (_stickersShown) { + validateSelectedIcon(); + updateContentHeight(); + } + updateIcons(); +} + +void EmojiPan::onRefreshPanels() { + s_inner.refreshPanels(s_panels); + e_inner.refreshPanels(e_panels); + if (_stickersShown) { + updatePanelsPositions(s_panels, s_scroll.scrollTop()); + } else { + updatePanelsPositions(e_panels, e_scroll.scrollTop()); + } +} + +void EmojiPan::leaveToChildEvent(QEvent *e, QWidget *child) { + if (!_stickersShown) return; + _iconsMousePos = QCursor::pos(); + updateSelected(); +} + +void EmojiPan::updateSelected() { + if (_iconDown >= 0) { + return; + } + + QPoint p(mapFromGlobal(_iconsMousePos)); + int32 x = p.x(), y = p.y(), newOver = -1; + if (rtl()) x = width() - x; + x -= _iconsLeft; + if (x >= st::rbEmoji.width * 7 && x < st::rbEmoji.width * 8 && y >= _iconsTop && y < _iconsTop + st::rbEmoji.height) { + newOver = _icons.size(); + } else if (!_icons.isEmpty()) { + if (y >= _iconsTop && y < _iconsTop + st::rbEmoji.height && x >= 0 && x < 7 * st::rbEmoji.width && x < _icons.size() * st::rbEmoji.width) { + x += _iconsX.current(); + newOver = qFloor(x / st::rbEmoji.width); + } + } + if (newOver != _iconOver) { + if (newOver < 0) { + setCursor(style::cur_default); + } else if (_iconOver < 0) { + setCursor(style::cur_pointer); + } + bool startanim = false; + if (_iconOver >= 0 && _iconOver < _icons.size()) { + _iconAnimations.remove(_iconOver + 1); + if (_iconAnimations.find(-_iconOver - 1) == _iconAnimations.end()) { + if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true; + _iconAnimations.insert(-_iconOver - 1, getms()); + } + } + _iconOver = newOver; + if (_iconOver >= 0 && _iconOver < _icons.size()) { + _iconAnimations.remove(-_iconOver - 1); + if (_iconAnimations.find(_iconOver + 1) == _iconAnimations.end()) { + if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true; + _iconAnimations.insert(_iconOver + 1, getms()); + } + } + if (startanim && !_a_icons.animating()) _a_icons.start(); + } +} + +void EmojiPan::updateIcons() { + if (!_stickersShown || !s_inner.showSectionIcons()) return; + + QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); + update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); +} + +void EmojiPan::step_icons(uint64 ms, bool timer) { + if (!_stickersShown) { + _a_icons.stop(); + return; + } + + for (Animations::iterator i = _iconAnimations.begin(); i != _iconAnimations.end();) { + int index = qAbs(i.key()) - 1; + float64 dt = float64(ms - i.value()) / st::emojiPanDuration; + if (index >= _iconHovers.size()) { + i = _iconAnimations.erase(i); + } else if (dt >= 1) { + _iconHovers[index] = (i.key() > 0) ? 1 : 0; + i = _iconAnimations.erase(i); + } else { + _iconHovers[index] = (i.key() > 0) ? dt : (1 - dt); + ++i; + } + } + + if (_iconsStartAnim) { + float64 dt = (ms - _iconsStartAnim) / float64(st::stickerIconMove); + if (dt >= 1) { + _iconsStartAnim = 0; + _iconsX.finish(); + _iconSelX.finish(); + } else { + _iconsX.update(dt, anim::linear); + _iconSelX.update(dt, anim::linear); + } + if (timer) updateSelected(); + } + + if (timer) updateIcons(); + + if (_iconAnimations.isEmpty() && !_iconsStartAnim) { + _a_icons.stop(); + } +} + +void EmojiPan::step_slide(float64 ms, bool timer) { + float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration; + float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0; + if (dt2 >= 1) { + _a_slide.stop(); + a_fromCoord.finish(); + a_fromAlpha.finish(); + a_toCoord.finish(); + a_toAlpha.finish(); + _fromCache = _toCache = QPixmap(); + if (_cache.isNull()) showAll(); + } else { + a_fromCoord.update(dt1, st::introHideFunc); + a_fromAlpha.update(dt1, st::introAlphaHideFunc); + a_toCoord.update(dt2, st::introShowFunc); + a_toAlpha.update(dt2, st::introAlphaShowFunc); + } + if (timer) update(); +} + +void EmojiPan::step_appearance(float64 ms, bool timer) { + if (_cache.isNull()) { + _a_appearance.stop(); + return; + } + + float64 dt = ms / st::dropdownDef.duration; + if (dt >= 1) { + _a_appearance.stop(); + a_opacity.finish(); + if (_hiding) { + hideFinish(); + } else { + _cache = QPixmap(); + if (_toCache.isNull()) showAll(); + } + } else { + a_opacity.update(dt, anim::linear); + } + if (timer) update(); +} + +void EmojiPan::hideStart() { + if (preventAutoHide() || s_inner.inlineResultsShown()) return; + + hideAnimated(); +} + +void EmojiPan::prepareShowHideCache() { + if (_cache.isNull()) { + QPixmap from = _fromCache, to = _toCache; + _fromCache = _toCache = QPixmap(); + showAll(); + _cache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _fromCache = from; _toCache = to; + } +} + +void EmojiPan::hideAnimated() { + if (_hiding) return; + + prepareShowHideCache(); + hideAll(); + _hiding = true; + a_opacity.start(0); + _a_appearance.start(); +} + +void EmojiPan::hideFinish() { + hide(); + e_inner.hideFinish(); + s_inner.hideFinish(true); + _cache = _toCache = _fromCache = QPixmap(); + _a_slide.stop(); + _horizontal = false; + _hiding = false; + + e_scroll.scrollToY(0); + if (!_recent.checked()) { + _noTabUpdate = true; + _recent.setChecked(true); + _noTabUpdate = false; + } + s_scroll.scrollToY(0); + _iconOver = _iconDown = -1; + _iconSel = 0; + _iconsX = anim::ivalue(0, 0); + _iconSelX = anim::ivalue(0, 0); + _iconsStartAnim = 0; + _a_icons.stop(); + _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); + _iconAnimations.clear(); + + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); +} + +void EmojiPan::showStart() { + if (!isHidden() && !_hiding) { + return; + } + if (isHidden()) { + e_inner.refreshRecent(); + if (s_inner.inlineResultsShown() && refreshInlineRows()) { + _stickersShown = true; + _shownFromInlineQuery = true; + } else { + s_inner.refreshRecent(); + _stickersShown = false; + _shownFromInlineQuery = false; + _cache = QPixmap(); // clear after refreshInlineRows() + } + recountContentMaxHeight(); + s_inner.preloadImages(); + _fromCache = _toCache = QPixmap(); + _a_slide.stop(); + moveBottom(y() + height(), true); + } else if (_hiding) { + if (s_inner.inlineResultsShown() && refreshInlineRows()) { + onSwitch(); + } + } + prepareShowHideCache(); + hideAll(); + _hiding = false; + show(); + a_opacity.start(1); + _a_appearance.start(); + emit updateStickers(); +} + +bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { + if (e->type() == QEvent::Enter) { + //if (dynamic_cast(obj)) { + // enterEvent(e); + //} else { + otherEnter(); + //} + } else if (e->type() == QEvent::Leave) { + //if (dynamic_cast(obj)) { + // leaveEvent(e); + //} else { + otherLeave(); + //} + } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) { + if (isHidden() || _hiding) { + _hideTimer.stop(); + showStart(); + } else { + hideAnimated(); + } + } + return false; +} + +void EmojiPan::stickersInstalled(uint64 setId) { + _stickersShown = true; + if (isHidden()) { + moveBottom(y() + height(), true); + show(); + a_opacity = anim::fvalue(0, 1); + a_opacity.update(0, anim::linear); + _cache = _fromCache = _toCache = QPixmap(); + } + showAll(); + s_inner.showStickerSet(setId); + updateContentHeight(); + showStart(); +} + +void EmojiPan::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { + if (_stickersShown && !isHidden()) { + s_inner.notify_inlineItemLayoutChanged(layout); + } +} + +void EmojiPan::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) { + if (_stickersShown && !isHidden()) { + s_inner.ui_repaintInlineItem(layout); + } +} + +bool EmojiPan::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) { + if (_stickersShown && !isHidden()) { + return s_inner.ui_isInlineItemVisible(layout); + } + return false; +} + +bool EmojiPan::ui_isInlineItemBeingChosen() { + if (_stickersShown && !isHidden()) { + return s_inner.ui_isInlineItemBeingChosen(); + } + return false; +} + +void EmojiPan::showAll() { + if (_stickersShown) { + s_scroll.show(); + _recent.hide(); + _people.hide(); + _nature.hide(); + _food.hide(); + _activity.hide(); + _travel.hide(); + _objects.hide(); + _symbols.hide(); + e_scroll.hide(); + } else { + s_scroll.hide(); + _recent.show(); + _people.show(); + _nature.show(); + _food.show(); + _activity.show(); + _travel.show(); + _objects.show(); + _symbols.show(); + e_scroll.show(); + } +} + +void EmojiPan::hideAll() { + _recent.hide(); + _people.hide(); + _nature.hide(); + _food.hide(); + _activity.hide(); + _travel.hide(); + _objects.hide(); + _symbols.hide(); + e_scroll.hide(); + s_scroll.hide(); + e_inner.clearSelection(true); + s_inner.clearSelection(true); +} + +void EmojiPan::onTabChange() { + if (_noTabUpdate) return; + DBIEmojiTab newTab = dbietRecent; + if (_people.checked()) newTab = dbietPeople; + else if (_nature.checked()) newTab = dbietNature; + else if (_food.checked()) newTab = dbietFood; + else if (_activity.checked()) newTab = dbietActivity; + else if (_travel.checked()) newTab = dbietTravel; + else if (_objects.checked()) newTab = dbietObjects; + else if (_symbols.checked()) newTab = dbietSymbols; + e_inner.showEmojiPack(newTab); +} + +void EmojiPan::updatePanelsPositions(const QVector &panels, int32 st) { + for (int32 i = 0, l = panels.size(); i < l; ++i) { + int32 y = panels.at(i)->wantedY() - st; + if (y < 0) { + y = (i + 1 < l) ? qMin(panels.at(i + 1)->wantedY() - st - int(st::emojiPanHeader), 0) : 0; + } + panels.at(i)->move(0, y); + panels.at(i)->setDeleteVisible(y >= st::emojiPanHeader); + + // Somehow the panels gets hidden (not displayed) when scrolling + // by clicking on the scroll bar to the middle of the panel. + // This bug occurs only in the Section::Featured stickers. + if (s_inner.currentSet(0) == Stickers::FeaturedSetId) { + panels.at(i)->repaint(); + } + } +} + +void EmojiPan::onScrollEmoji() { + auto st = e_scroll.scrollTop(); + + updatePanelsPositions(e_panels, st); + + auto tab = e_inner.currentTab(st); + FlatRadiobutton *check = nullptr; + switch (tab) { + case dbietRecent: check = &_recent; break; + case dbietPeople: check = &_people; break; + case dbietNature: check = &_nature; break; + case dbietFood: check = &_food; break; + case dbietActivity: check = &_activity; break; + case dbietTravel: check = &_travel; break; + case dbietObjects: check = &_objects; break; + case dbietSymbols: check = &_symbols; break; + } + if (check && !check->checked()) { + _noTabUpdate = true; + check->setChecked(true); + _noTabUpdate = false; + } + + e_inner.setScrollTop(st); +} + +void EmojiPan::onScrollStickers() { + auto st = s_scroll.scrollTop(); + + updatePanelsPositions(s_panels, st); + + validateSelectedIcon(true); + if (st + s_scroll.height() > s_scroll.scrollTopMax()) { + onInlineRequest(); + } + + s_inner.setScrollTop(st); +} + +void EmojiPan::validateSelectedIcon(bool animated) { + uint64 setId = s_inner.currentSet(s_scroll.scrollTop()); + int32 newSel = 0; + for (int i = 0, l = _icons.size(); i < l; ++i) { + if (_icons.at(i).setId == setId) { + newSel = i; + break; + } + } + if (newSel != _iconSel) { + _iconSel = newSel; + if (animated) { + _iconSelX.start(newSel * st::rbEmoji.width); + } else { + _iconSelX = anim::ivalue(newSel * st::rbEmoji.width, newSel * st::rbEmoji.width); + } + _iconsX.start(snap((2 * newSel - 7) * int(st::rbEmoji.width) / 2, 0, _iconsMax)); + _iconsStartAnim = getms(); + _a_icons.start(); + updateSelected(); + updateIcons(); + } +} + +void EmojiPan::onSwitch() { + QPixmap cache = _cache; + _fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _stickersShown = !_stickersShown; + if (!_stickersShown) { + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); + } else { + if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { + s_inner.showStickerSet(Stickers::DefaultSetId); + } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && Global::StickerSetsOrder().isEmpty()) { + s_inner.showStickerSet(Stickers::NoneSetId); + } else { + s_inner.updateShowingSavedGifs(); + } + if (cShowingSavedGifs()) { + s_inner.showFinish(); + } + validateSelectedIcon(); + updateContentHeight(); + } + _iconOver = -1; + _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); + _iconAnimations.clear(); + _a_icons.stop(); + + _cache = QPixmap(); + showAll(); + _toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _cache = cache; + + hideAll(); + + if (_stickersShown) { + e_inner.hideFinish(); + } else { + s_inner.hideFinish(false); + } + + a_toCoord = (_stickersShown != rtl()) ? anim::ivalue(st::emojiPanWidth, 0) : anim::ivalue(-st::emojiPanWidth, 0); + a_toAlpha = anim::fvalue(0, 1); + a_fromCoord = (_stickersShown != rtl()) ? anim::ivalue(0, -st::emojiPanWidth) : anim::ivalue(0, st::emojiPanWidth); + a_fromAlpha = anim::fvalue(1, 0); + + _a_slide.start(); + update(); +} + +void EmojiPan::onDisplaySet(quint64 setId) { + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it != sets.cend()) { + _displayingSetId = setId; + auto box = new StickerSetBox(Stickers::inputSetId(*it)); + connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onDelayedHide())); + Ui::showLayer(box, KeepOtherLayers); + } +} + +void EmojiPan::onInstallSet(quint64 setId) { + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it != sets.cend()) { + MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_bool(false)), rpcDone(&EmojiPan::installSetDone), rpcFail(&EmojiPan::installSetFail, setId)); + + Stickers::installLocally(setId); + } +} + +void EmojiPan::installSetDone(const MTPmessages_StickerSetInstallResult &result) { + if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { + Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); + } +} + +bool EmojiPan::installSetFail(uint64 setId, const RPCError &error) { + if (MTP::isDefaultHandledError(error)) { + return false; + } + Stickers::undoInstallLocally(setId); + return true; +} + +void EmojiPan::onRemoveSet(quint64 setId) { + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { + _removingSetId = it->id; + ConfirmBox *box = new ConfirmBox(lng_stickers_remove_pack(lt_sticker_pack, it->title), lang(lng_box_remove)); + connect(box, SIGNAL(confirmed()), this, SLOT(onRemoveSetSure())); + connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onDelayedHide())); + Ui::showLayer(box); + } +} + +void EmojiPan::onRemoveSetSure() { + Ui::hideLayer(); + auto &sets = Global::RefStickerSets(); + auto it = sets.find(_removingSetId); + if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { + if (it->id && it->access) { + MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)))); + } else if (!it->shortName.isEmpty()) { + MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetShortName(MTP_string(it->shortName)))); + } + bool writeRecent = false; + RecentStickerPack &recent(cGetRecentStickers()); + for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { + if (it->stickers.indexOf(i->first) >= 0) { + i = recent.erase(i); + writeRecent = true; + } else { + ++i; + } + } + it->flags &= ~MTPDstickerSet::Flag::f_installed; + if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) { + sets.erase(it); + } + int removeIndex = Global::StickerSetsOrder().indexOf(_removingSetId); + if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); + refreshStickers(); + Local::writeInstalledStickers(); + if (writeRecent) Local::writeUserSettings(); + } + _removingSetId = 0; +} + +void EmojiPan::onDelayedHide() { + if (!rect().contains(mapFromGlobal(QCursor::pos()))) { + _hideTimer.start(3000); + } + _removingSetId = 0; + _displayingSetId = 0; +} + +void EmojiPan::clearInlineBot() { + inlineBotChanged(); + e_switch.updateText(); + e_switch.moveToRight(0, 0, st::emojiPanWidth); +} + +bool EmojiPan::hideOnNoInlineResults() { + return _inlineBot && _stickersShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername()); +} + +void EmojiPan::inlineBotChanged() { + if (!_inlineBot) return; + + if (!isHidden() && !_hiding) { + if (hideOnNoInlineResults() || !rect().contains(mapFromGlobal(QCursor::pos()))) { + hideAnimated(); + } + } + + if (_inlineRequestId) MTP::cancel(_inlineRequestId); + _inlineRequestId = 0; + _inlineQuery = _inlineNextQuery = _inlineNextOffset = QString(); + _inlineBot = nullptr; + for (InlineCache::const_iterator i = _inlineCache.cbegin(), e = _inlineCache.cend(); i != e; ++i) { + delete i.value(); + } + _inlineCache.clear(); + s_inner.inlineBotChanged(); + s_inner.hideInlineRowsPanel(); + + Notify::inlineBotRequesting(false); +} + +void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) { + _inlineRequestId = 0; + Notify::inlineBotRequesting(false); + + auto it = _inlineCache.find(_inlineQuery); + + bool adding = (it != _inlineCache.cend()); + if (result.type() == mtpc_messages_botResults) { + const auto &d(result.c_messages_botResults()); + const auto &v(d.vresults.c_vector().v); + uint64 queryId(d.vquery_id.v); + + if (!adding) { + it = _inlineCache.insert(_inlineQuery, new internal::InlineCacheEntry()); + } + it.value()->nextOffset = qs(d.vnext_offset); + if (d.has_switch_pm() && d.vswitch_pm.type() == mtpc_inlineBotSwitchPM) { + const auto &switchPm = d.vswitch_pm.c_inlineBotSwitchPM(); + it.value()->switchPmText = qs(switchPm.vtext); + it.value()->switchPmStartToken = qs(switchPm.vstart_param); + } + + if (int count = v.size()) { + it.value()->results.reserve(it.value()->results.size() + count); + } + int added = 0; + for_const (const auto &res, v) { + if (auto result = InlineBots::Result::create(queryId, res)) { + ++added; + it.value()->results.push_back(result.release()); + } + } + + if (!added) { + it.value()->nextOffset = QString(); + } + } else if (adding) { + it.value()->nextOffset = QString(); + } + + if (!showInlineRows(!adding)) { + it.value()->nextOffset = QString(); + } + onScrollStickers(); +} + +bool EmojiPan::inlineResultsFail(const RPCError &error) { + // show error? + Notify::inlineBotRequesting(false); + _inlineRequestId = 0; + return true; +} + +void EmojiPan::queryInlineBot(UserData *bot, PeerData *peer, QString query) { + bool force = false; + _inlineQueryPeer = peer; + if (bot != _inlineBot) { + inlineBotChanged(); + _inlineBot = bot; + force = true; + //if (_inlineBot->isBotInlineGeo()) { + // Ui::showLayer(new InformBox(lang(lng_bot_inline_geo_unavailable))); + //} + } + //if (_inlineBot && _inlineBot->isBotInlineGeo()) { + // return; + //} + + if (_inlineQuery != query || force) { + if (_inlineRequestId) { + MTP::cancel(_inlineRequestId); + _inlineRequestId = 0; + Notify::inlineBotRequesting(false); + } + if (_inlineCache.contains(query)) { + _inlineRequestTimer.stop(); + _inlineQuery = _inlineNextQuery = query; + showInlineRows(true); + } else { + _inlineNextQuery = query; + _inlineRequestTimer.start(InlineBotRequestDelay); + } + } +} + +void EmojiPan::onInlineRequest() { + if (_inlineRequestId || !_inlineBot || !_inlineQueryPeer) return; + _inlineQuery = _inlineNextQuery; + + QString nextOffset; + InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery); + if (i != _inlineCache.cend()) { + nextOffset = i.value()->nextOffset; + if (nextOffset.isEmpty()) return; + } + Notify::inlineBotRequesting(true); + MTPmessages_GetInlineBotResults::Flags flags = 0; + _inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(MTP_flags(flags), _inlineBot->inputUser, _inlineQueryPeer->input, MTPInputGeoPoint(), MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail)); +} + +void EmojiPan::onEmptyInlineRows() { + if (_shownFromInlineQuery || hideOnNoInlineResults()) { + hideAnimated(); + s_inner.clearInlineRowsPanel(); + } else if (!_inlineBot) { + s_inner.hideInlineRowsPanel(); + } else { + s_inner.clearInlineRowsPanel(); + } +} + +bool EmojiPan::refreshInlineRows(int32 *added) { + auto i = _inlineCache.constFind(_inlineQuery); + const internal::InlineCacheEntry *entry = nullptr; + if (i != _inlineCache.cend()) { + if (!i.value()->results.isEmpty() || !i.value()->switchPmText.isEmpty()) { + entry = i.value(); + } + _inlineNextOffset = i.value()->nextOffset; + } + if (!entry) prepareShowHideCache(); + int32 result = s_inner.refreshInlineRows(_inlineBot, entry, false); + if (added) *added = result; + return (entry != nullptr); +} + +int32 EmojiPan::showInlineRows(bool newResults) { + int32 added = 0; + bool clear = !refreshInlineRows(&added); + if (newResults) s_scroll.scrollToY(0); + + e_switch.updateText(s_inner.inlineResultsShown() ? _inlineBot->username : QString()); + e_switch.moveToRight(0, 0, st::emojiPanWidth); + + bool hidden = isHidden(); + if (!hidden && !clear) { + recountContentMaxHeight(); + } + if (clear) { + if (!hidden && hideOnNoInlineResults()) { + hideAnimated(); + } else if (!_hiding) { + _cache = QPixmap(); // clear after refreshInlineRows() + } + } else { + _hideTimer.stop(); + if (hidden || _hiding) { + showStart(); + } else if (!_stickersShown) { + onSwitch(); + } + } + + return added; +} + +void EmojiPan::recountContentMaxHeight() { + if (_shownFromInlineQuery) { + _contentMaxHeight = qMin(s_inner.countHeight(true), int(st::emojiPanMaxHeight)); + } else { + _contentMaxHeight = st::emojiPanMaxHeight; + } + updateContentHeight(); +} diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h new file mode 100644 index 000000000..ff6d609b5 --- /dev/null +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -0,0 +1,657 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "ui/twidget.h" +#include "ui/boxshadow.h" + +namespace InlineBots { +namespace Layout { +class ItemBase; +} // namespace Layout +class Result; +} // namespace InlineBots + +namespace internal { + +constexpr int InlineItemsMaxPerRow = 5; +constexpr int EmojiColorsCount = 5; + +using InlineResult = InlineBots::Result; +using InlineResults = QList; +using InlineItem = InlineBots::Layout::ItemBase; + +struct InlineCacheEntry { + ~InlineCacheEntry() { + clearResults(); + } + QString nextOffset; + QString switchPmText, switchPmStartToken; + InlineResults results; // owns this results list + void clearResults(); +}; + +class EmojiColorPicker : public TWidget { + Q_OBJECT + +public: + + EmojiColorPicker(); + + void showEmoji(uint32 code); + + void paintEvent(QPaintEvent *e); + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + + void step_appearance(float64 ms, bool timer); + void step_selected(uint64 ms, bool timer); + void showStart(); + + void clearSelection(bool fast = false); + +public slots: + + void hideStart(bool fast = false); + +signals: + + void emojiSelected(EmojiPtr emoji); + void hidden(); + +private: + + void drawVariant(Painter &p, int variant); + + void updateSelected(); + + bool _ignoreShow = false; + + EmojiPtr _variants[EmojiColorsCount + 1]; + + typedef QMap EmojiAnimations; // index - showing, -index - hiding + EmojiAnimations _emojiAnimations; + Animation _a_selected; + + float64 _hovers[EmojiColorsCount + 1]; + + int _selected = -1; + int _pressedSel = -1; + QPoint _lastMousePos; + + bool _hiding = false; + QPixmap _cache; + + anim::fvalue a_opacity; + Animation _a_appearance; + + QTimer _hideTimer; + + BoxShadow _shadow; + +}; + +class EmojiPanel; +class EmojiPanInner : public TWidget { + Q_OBJECT + +public: + EmojiPanInner(); + + 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(); + + void showEmojiPack(DBIEmojiTab packIndex); + + void clearSelection(bool fast = false); + + DBIEmojiTab currentTab(int yOffset) const; + + void refreshRecent(); + + void setScrollTop(int top); + + void fillPanels(QVector &panels); + void refreshPanels(QVector &panels); + +public slots: + + void updateSelected(); + + void onShowPicker(); + void onPickerHidden(); + void onColorSelected(EmojiPtr emoji); + + bool checkPickerHide(); + +signals: + + void selected(EmojiPtr emoji); + + void switchToStickers(); + + void scrollToY(int y); + void disableScroll(bool dis); + + void needRefreshPanels(); + void saveConfigDelayed(int32 delay); + +private: + + int32 _maxHeight; + + int countHeight(); + void selectEmoji(EmojiPtr emoji); + + QRect emojiRect(int tab, int sel); + + typedef QMap Animations; // index - showing, -index - hiding + Animations _animations; + Animation _a_selected; + + int _top = 0, _counts[emojiTabCount]; + + QVector _emojis[emojiTabCount]; + QVector _hovers[emojiTabCount]; + + int32 _esize; + + int _selected = -1; + int _pressedSel = -1; + int _pickerSel = -1; + QPoint _lastMousePos; + + EmojiColorPicker _picker; + QTimer _showPickerTimer; +}; + +struct StickerIcon { + StickerIcon(uint64 setId) : setId(setId) { + } + StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) { + } + uint64 setId; + DocumentData *sticker = nullptr; + int pixw = 0; + int pixh = 0; +}; + +class StickerPanInner : public TWidget { + 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); + void showFinish(); + void showStickerSet(uint64 setId); + void updateShowingSavedGifs(); + + bool showSectionIcons() const; + void clearSelection(bool fast = false); + + void refreshStickers(); + void refreshRecentStickers(bool resize = true); + void refreshSavedGifs(); + int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted); + void refreshRecent(); + void inlineBotChanged(); + void hideInlineRowsPanel(); + void clearInlineRowsPanel(); + + void fillIcons(QList &icons); + void fillPanels(QVector &panels); + void refreshPanels(QVector &panels); + + void setScrollTop(int top); + void preloadImages(); + + uint64 currentSet(int yOffset) const; + + void notify_inlineItemLayoutChanged(const InlineItem *layout); + void ui_repaintInlineItem(const InlineItem *layout); + bool ui_isInlineItemVisible(const InlineItem *layout); + bool ui_isInlineItemBeingChosen(); + + bool inlineResultsShown() const { + return (_section == Section::Inlines); + } + int countHeight(bool plain = false); + + ~StickerPanInner(); + +private slots: + void updateSelected(); + void onSettings(); + void onPreview(); + void onUpdateInlineItems(); + void onSwitchPm(); + +signals: + void selected(DocumentData *sticker); + void selected(PhotoData *photo); + void selected(InlineBots::Result *result, UserData *bot); + + void displaySet(quint64 setId); + void installSet(quint64 setId); + void removeSet(quint64 setId); + + void refreshIcons(); + void emptyInlineRows(); + + void switchToEmoji(); + + void scrollToY(int y); + void scrollUpdated(); + void disableScroll(bool dis); + void needRefreshPanels(); + + void saveConfigDelayed(int32 delay); + +private: + struct Set { + Set(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), hovers(hoversSize, 0), pack(pack) { + } + uint64 id; + MTPDstickerSet::Flags flags; + QString title; + QVector hovers; + StickerPack pack; + }; + using Sets = QList; + Sets &shownSets() { + return (_section == Section::Featured) ? _featuredSets : _mySets; + } + const Sets &shownSets() const { + return const_cast(this)->shownSets(); + } + int featuredRowHeight() const; + + bool showingInlineItems() const { // Gifs or Inline results + return (_section == Section::Inlines) || (_section == Section::Gifs); + } + + void paintInlineItems(Painter &p, const QRect &r); + void paintStickers(Painter &p, const QRect &r); + void paintSticker(Painter &p, Set &set, int y, int index); + bool featuredHasAddButton(int index) const; + int featuredContentWidth() const; + QRect featuredAddRect(int y) const; + + void refreshSwitchPmButton(const InlineCacheEntry *entry); + + void appendSet(Sets &to, uint64 setId); + + void selectEmoji(EmojiPtr emoji); + QRect stickerRect(int tab, int sel); + + int32 _maxHeight; + + typedef QMap Animations; // index - showing, -index - hiding + Animations _animations; + Animation _a_selected; + + int _top = 0; + + Sets _mySets; + Sets _featuredSets; + QList _custom; + + enum class Section { + Inlines, + Gifs, + Featured, + Stickers, + }; + Section _section = Section::Stickers; + bool _setGifCommand = false; + UserData *_inlineBot; + QString _inlineBotTitle; + uint64 _lastScrolled = 0; + QTimer _updateInlineItems; + bool _inlineWithThumb = false; + + std_::unique_ptr _switchPmButton; + QString _switchPmStartToken; + + typedef QVector InlineItems; + struct InlineRow { + InlineRow() : height(0) { + } + int32 height; + InlineItems items; + }; + typedef QVector InlineRows; + InlineRows _inlineRows; + void clearInlineRows(bool resultsDeleted); + + using GifLayouts = QMap; + GifLayouts _gifLayouts; + InlineItem *layoutPrepareSavedGif(DocumentData *doc, int32 position); + + using InlineLayouts = QMap; + InlineLayouts _inlineLayouts; + InlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position); + + bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth); + bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false); + + InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0); + void deleteUnusedGifLayouts(); + + void deleteUnusedInlineLayouts(); + + int validateExistingInlineRows(const InlineResults &results); + void selectInlineResult(int row, int column); + void removeRecentSticker(int tab, int index); + + int _selected = -1; + int _pressed = -1; + int _selectedFeaturedSet = -1; + int _pressedFeaturedSet = -1; + int _selectedFeaturedSetAdd = -1; + int _pressedFeaturedSetAdd = -1; + QPoint _lastMousePos; + + QString _addText; + int _addWidth; + + LinkButton _settings; + + QTimer _previewTimer; + bool _previewShown = false; +}; + +class EmojiPanel : public TWidget { + Q_OBJECT + +public: + + EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY); // Stickers::NoneSetId if in emoji + void setText(const QString &text); + void setDeleteVisible(bool isVisible); + + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e); + + int32 wantedY() const { + return _wantedY; + } + void setWantedY(int32 y) { + _wantedY = y; + } + +signals: + + void deleteClicked(quint64 setId); + void mousePressed(); + +public slots: + + void onDelete(); + +private: + + void updateText(); + + int32 _wantedY; + QString _text, _fullText; + uint64 _setId; + bool _special, _deleteVisible; + IconedButton *_delete; + +}; + +class EmojiSwitchButton : public Button { +public: + + EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji + void paintEvent(QPaintEvent *e); + void updateText(const QString &inlineBotUsername = QString()); + +protected: + + bool _toStickers; + QString _text; + int32 _textWidth; + +}; + +} // namespace internal + +class EmojiPan : public TWidget, public RPCSender { + Q_OBJECT + +public: + EmojiPan(QWidget *parent); + + void setMaxHeight(int32 h); + void paintEvent(QPaintEvent *e); + + void moveBottom(int32 bottom, bool force = false); + + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void otherEnter(); + void otherLeave(); + + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + + bool event(QEvent *e); + + void fastHide(); + bool hiding() const { + return _hiding || _hideTimer.isActive(); + } + + void step_appearance(float64 ms, bool timer); + void step_slide(float64 ms, bool timer); + void step_icons(uint64 ms, bool timer); + + bool eventFilter(QObject *obj, QEvent *e); + void stickersInstalled(uint64 setId); + + void queryInlineBot(UserData *bot, PeerData *peer, QString query); + void clearInlineBot(); + + bool overlaps(const QRect &globalRect) { + if (isHidden() || !_cache.isNull()) return false; + + return QRect(st::dropdownDef.padding.left(), + st::dropdownDef.padding.top(), + _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), + _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() + ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); + } + + void notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout); + void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout); + bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout); + bool ui_isInlineItemBeingChosen(); + + bool inlineResultsShown() const { + return s_inner.inlineResultsShown(); + } + +public slots: + void refreshStickers(); + void refreshSavedGifs(); + + void hideStart(); + void hideFinish(); + + void showStart(); + void onWndActiveChanged(); + + void onTabChange(); + void onScrollEmoji(); + void onScrollStickers(); + void onSwitch(); + + void onDisplaySet(quint64 setId); + void onInstallSet(quint64 setId); + void onRemoveSet(quint64 setId); + void onRemoveSetSure(); + void onDelayedHide(); + + void onRefreshIcons(); + void onRefreshPanels(); + + void onSaveConfig(); + void onSaveConfigDelayed(int32 delay); + + void onInlineRequest(); + void onEmptyInlineRows(); + +signals: + void emojiSelected(EmojiPtr emoji); + void stickerSelected(DocumentData *sticker); + void photoSelected(PhotoData *photo); + void inlineResultSelected(InlineBots::Result *result, UserData *bot); + + void updateStickers(); + +private: + bool preventAutoHide() const; + void installSetDone(const MTPmessages_StickerSetInstallResult &result); + bool installSetFail(uint64 setId, const RPCError &error); + + void paintStickerSettingsIcon(Painter &p) const; + void paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const; + + void validateSelectedIcon(bool animated = false); + void updateContentHeight(); + + void leaveToChildEvent(QEvent *e, QWidget *child); + void hideAnimated(); + void prepareShowHideCache(); + + void updateSelected(); + void updateIcons(); + + void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab); + void updatePanelsPositions(const QVector &panels, int32 st); + + void showAll(); + void hideAll(); + + int32 _maxHeight, _contentMaxHeight, _contentHeight, _contentHeightEmoji, _contentHeightStickers; + bool _horizontal = false; + + bool _noTabUpdate = false; + + int32 _width, _height, _bottom; + bool _hiding = false; + QPixmap _cache; + + anim::fvalue a_opacity = { 0. }; + Animation _a_appearance; + + QTimer _hideTimer; + + BoxShadow _shadow; + + FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols; + QList _icons; + QVector _iconHovers; + int _iconOver = -1; + int _iconSel = 0; + int _iconDown = -1; + bool _iconsDragging = false; + typedef QMap Animations; // index - showing, -index - hiding + Animations _iconAnimations; + Animation _a_icons; + QPoint _iconsMousePos, _iconsMouseDown; + int _iconsLeft = 0; + int _iconsTop = 0; + int _iconsStartX = 0; + int _iconsMax = 0; + anim::ivalue _iconsX = { 0, 0 }; + anim::ivalue _iconSelX = { 0, 0 }; + uint64 _iconsStartAnim = 0; + + bool _stickersShown = false; + bool _shownFromInlineQuery = false; + QPixmap _fromCache, _toCache; + anim::ivalue a_fromCoord, a_toCoord; + anim::fvalue a_fromAlpha, a_toAlpha; + Animation _a_slide; + + ScrollArea e_scroll; + internal::EmojiPanInner e_inner; + QVector e_panels; + internal::EmojiSwitchButton e_switch; + ScrollArea s_scroll; + internal::StickerPanInner s_inner; + QVector s_panels; + internal::EmojiSwitchButton s_switch; + + uint64 _displayingSetId = 0; + uint64 _removingSetId = 0; + + QTimer _saveConfigTimer; + + // inline bots + typedef QMap InlineCache; + InlineCache _inlineCache; + QTimer _inlineRequestTimer; + + void inlineBotChanged(); + int32 showInlineRows(bool newResults); + bool hideOnNoInlineResults(); + void recountContentMaxHeight(); + bool refreshInlineRows(int32 *added = 0); + UserData *_inlineBot = nullptr; + PeerData *_inlineQueryPeer = nullptr; + QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; + mtpRequestId _inlineRequestId = 0; + void inlineResultsDone(const MTPmessages_BotResults &result); + bool inlineResultsFail(const RPCError &error); + +}; diff --git a/Telegram/SourceFiles/stickers/stickers.cpp b/Telegram/SourceFiles/stickers/stickers.cpp new file mode 100644 index 000000000..4f5412d62 --- /dev/null +++ b/Telegram/SourceFiles/stickers/stickers.cpp @@ -0,0 +1,143 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "lang.h" + +#include "boxes/stickersetbox.h" +#include "boxes/confirmbox.h" +#include "apiwrap.h" +#include "localstorage.h" +#include "mainwidget.h" + +namespace Stickers { + +void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { + auto &v = d.vsets.c_vector().v; + auto &order = Global::RefStickerSetsOrder(); + Stickers::Order archived; + archived.reserve(v.size()); + QMap setsToRequest; + for_const (auto &stickerSet, v) { + const MTPDstickerSet *setData = nullptr; + switch (stickerSet.type()) { + case mtpc_stickerSetCovered: { + auto &d = stickerSet.c_stickerSetCovered(); + if (d.vset.type() == mtpc_stickerSet) { + setData = &d.vset.c_stickerSet(); + } + } break; + case mtpc_stickerSetMultiCovered: { + auto &d = stickerSet.c_stickerSetMultiCovered(); + if (d.vset.type() == mtpc_stickerSet) { + setData = &d.vset.c_stickerSet(); + } + } break; + } + if (setData) { + auto set = Stickers::feedSet(*setData); + if (set->stickers.isEmpty()) { + setsToRequest.insert(set->id, set->access); + } + auto index = order.indexOf(set->id); + if (index >= 0) { + order.removeAt(index); + } + archived.push_back(set->id); + } + } + if (!setsToRequest.isEmpty()) { + for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) { + App::api()->scheduleStickerSetRequest(i.key(), i.value()); + } + App::api()->requestStickerSets(); + } + Local::writeInstalledStickers(); + Local::writeArchivedStickers(); + Ui::showLayer(new StickersBox(archived), KeepOtherLayers); + + emit App::main()->stickersUpdated(); +} + +void installLocally(uint64 setId) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(setId); + if (it == sets.end()) { + return; + } + + auto flags = it->flags; + it->flags &= ~(MTPDstickerSet::Flag::f_archived | MTPDstickerSet_ClientFlag::f_unread); + it->flags |= MTPDstickerSet::Flag::f_installed; + auto changedFlags = flags ^ it->flags; + + 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::writeInstalledStickers(); + if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) Local::writeFeaturedStickers(); + if (changedFlags & MTPDstickerSet::Flag::f_archived) { + auto index = Global::RefArchivedStickerSetsOrder().indexOf(setId); + if (index >= 0) { + Global::RefArchivedStickerSetsOrder().removeAt(index); + Local::writeArchivedStickers(); + } + } + emit App::main()->stickersUpdated(); +} + +void undoInstallLocally(uint64 setId) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(setId); + if (it == sets.end()) { + return; + } + + it->flags &= ~MTPDstickerSet::Flag::f_installed; + + auto &order = Global::RefStickerSetsOrder(); + int currentIndex = order.indexOf(setId); + if (currentIndex >= 0) { + order.removeAt(currentIndex); + } + + Local::writeInstalledStickers(); + emit App::main()->stickersUpdated(); + + Ui::showLayer(new InformBox(lang(lng_stickers_not_found)), KeepOtherLayers); +} + +} // namespace Stickers diff --git a/Telegram/SourceFiles/stickers/stickers.h b/Telegram/SourceFiles/stickers/stickers.h new file mode 100644 index 000000000..06df73d47 --- /dev/null +++ b/Telegram/SourceFiles/stickers/stickers.h @@ -0,0 +1,29 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace Stickers { + +void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d); +void installLocally(uint64 setId); +void undoInstallLocally(uint64 setId); + +} // namespace Stickers diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style new file mode 100644 index 000000000..11168295c --- /dev/null +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -0,0 +1,39 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +using "basic.style"; + +featuredStickersHeader: 45px; +featuredStickersSkip: 15px; + +featuredStickersHeaderFont: semiboldFont; +featuredStickersHeaderFg: windowTextFg; +featuredStickersHeaderTop: 0px; +featuredStickersSubheaderFont: normalFont; +featuredStickersSubheaderFg: #777; +featuredStickersSubheaderTop: 20px; + +featuredStickersAddTop: 3px; +featuredStickersAdd: RoundButton(defaultActiveButton) { + width: -17px; + height: 26px; + textTop: 4px; + downTextTop: 5px; +} diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 9bffcf52f..e5938b127 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -38,6 +38,7 @@ '<(src_loc)/overview/overview.style', '<(src_loc)/profile/profile.style', '<(src_loc)/settings/settings.style', + '<(src_loc)/stickers/stickers.style', '<(src_loc)/ui/widgets/widgets.style', ], 'langpacks': [ @@ -377,6 +378,10 @@ '<(src_loc)/settings/settings_scale_widget.h', '<(src_loc)/settings/settings_widget.cpp', '<(src_loc)/settings/settings_widget.h', + '<(src_loc)/stickers/emoji_pan.cpp', + '<(src_loc)/stickers/emoji_pan.h', + '<(src_loc)/stickers/stickers.cpp', + '<(src_loc)/stickers/stickers.h', '<(src_loc)/ui/buttons/history_down_button.cpp', '<(src_loc)/ui/buttons/history_down_button.h', '<(src_loc)/ui/buttons/icon_button.cpp', diff --git a/Telegram/gyp/telegram_win.gypi b/Telegram/gyp/telegram_win.gypi index 1973d650c..ce23aa2e6 100644 --- a/Telegram/gyp/telegram_win.gypi +++ b/Telegram/gyp/telegram_win.gypi @@ -39,11 +39,6 @@ 'lib_exif', 'OpenAL32', 'common', - 'libavformat/libavformat.a', - 'libavcodec/libavcodec.a', - 'libavutil/libavutil.a', - 'libswresample/libswresample.a', - 'libswscale/libswscale.a', 'opus', 'celt', 'silk_common', @@ -52,6 +47,17 @@ 'lib/exception_handler', 'lib/crash_generation_client', ], + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + 'libavformat/libavformat.a', + 'libavcodec/libavcodec.a', + 'libavutil/libavutil.a', + 'libswresample/libswresample.a', + 'libswscale/libswscale.a', + ], + }, + }, 'configurations': { 'Debug': { 'include_dirs': [