Improve emoji set box design.

This commit is contained in:
John Preston 2022-07-22 14:39:28 +03:00
parent 152bcb3570
commit b31a3ba5a3
20 changed files with 407 additions and 219 deletions

View file

@ -45,19 +45,24 @@ void AttachedStickers::request(
return; return;
} }
// Single attached sticker pack. // Single attached sticker pack.
const auto setData = result.v.front().match([&](const auto &data) { const auto data = result.v.front().match([&](const auto &data) {
return data.vset().match([&](const MTPDstickerSet &data) { return &data.vset().data();
return &data;
});
}); });
const auto setId = (setData->vid().v && setData->vaccess_hash().v) const auto setId = (data->vid().v && data->vaccess_hash().v)
? StickerSetIdentifier{ ? StickerSetIdentifier{
.id = setData->vid().v, .id = data->vid().v,
.accessHash = setData->vaccess_hash().v } .accessHash = data->vaccess_hash().v }
: StickerSetIdentifier{ .shortName = qs(setData->vshort_name()) }; : StickerSetIdentifier{ .shortName = qs(data->vshort_name()) };
strongController->show( strongController->show(
Box<StickerSetBox>(strongController, setId), Box<StickerSetBox>(
strongController,
setId,
(data->is_emojis()
? Data::StickersType::Emoji
: data->is_masks()
? Data::StickersType::Masks
: Data::StickersType::Stickers)),
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
}).fail([=] { }).fail([=] {
_requestId = 0; _requestId = 0;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_document_media.h" #include "data/data_document_media.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h"
#include "menu/menu_send.h" #include "menu/menu_send.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/image/image_location_factory.h" #include "ui/image/image_location_factory.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/text/custom_emoji_instance.h"
#include "ui/effects/path_shift_gradient.h" #include "ui/effects/path_shift_gradient.h"
#include "ui/emoji_config.h" #include "ui/emoji_config.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
@ -115,7 +117,8 @@ public:
Inner( Inner(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
const StickerSetIdentifier &set); const StickerSetIdentifier &set,
Data::StickersType type);
[[nodiscard]] bool loaded() const; [[nodiscard]] bool loaded() const;
[[nodiscard]] bool notInstalled() const; [[nodiscard]] bool notInstalled() const;
@ -153,12 +156,15 @@ protected:
void leaveEventHook(QEvent *e) override; void leaveEventHook(QEvent *e) override;
private: private:
using CustomInstance = Ui::CustomEmoji::SeparateInstance;
struct Element { struct Element {
not_null<DocumentData*> document; not_null<DocumentData*> document;
std::shared_ptr<Data::DocumentMedia> documentMedia; std::shared_ptr<Data::DocumentMedia> documentMedia;
Lottie::Animation *lottie = nullptr; Lottie::Animation *lottie = nullptr;
Media::Clip::ReaderPointer webm; Media::Clip::ReaderPointer webm;
CustomInstance *emoji = nullptr;
Ui::Animations::Simple overAnimation; Ui::Animations::Simple overAnimation;
mutable QImage premiumLock; mutable QImage premiumLock;
}; };
@ -178,6 +184,10 @@ private:
Media::Clip::Notification notification, Media::Clip::Notification notification,
not_null<DocumentData*> document, not_null<DocumentData*> document,
int index); int index);
void setupEmoji(int index);
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance(
not_null<DocumentData*> document);
void customEmojiRepaint();
void updateSelected(); void updateSelected();
void setSelected(int selected); void setSelected(int selected);
@ -196,10 +206,17 @@ private:
void updateItems(); void updateItems();
void repaintItems(crl::time now = 0); void repaintItems(crl::time now = 0);
not_null<Window::SessionController*> _controller; const not_null<Window::SessionController*> _controller;
MTP::Sender _api; MTP::Sender _api;
std::vector<Element> _elements; std::vector<Element> _elements;
std::unique_ptr<Lottie::MultiPlayer> _lottiePlayer; std::unique_ptr<Lottie::MultiPlayer> _lottiePlayer;
base::flat_map<
not_null<DocumentData*>,
std::unique_ptr<CustomInstance>> _instances;
std::unique_ptr<Ui::CustomEmoji::SimpleManager> _manager;
bool _repaintScheduled = false;
StickersPack _pack; StickersPack _pack;
StickersByEmojiMap _emoji; StickersByEmojiMap _emoji;
bool _loaded = false; bool _loaded = false;
@ -226,6 +243,7 @@ private:
base::Timer _updateItemsTimer; base::Timer _updateItemsTimer;
StickerSetIdentifier _input; StickerSetIdentifier _input;
QMargins _padding;
mtpRequestId _installRequest = 0; mtpRequestId _installRequest = 0;
@ -246,9 +264,18 @@ private:
StickerSetBox::StickerSetBox( StickerSetBox::StickerSetBox(
QWidget*, QWidget*,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
const StickerSetIdentifier &set) const StickerSetIdentifier &set,
Data::StickersType type)
: _controller(controller) : _controller(controller)
, _set(set) { , _set(set)
, _type(type) {
}
StickerSetBox::StickerSetBox(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<Data::StickersSet*> set)
: StickerSetBox(parent, controller, set->identifier(), set->type()) {
} }
QPointer<Ui::BoxContent> StickerSetBox::Show( QPointer<Ui::BoxContent> StickerSetBox::Show(
@ -257,7 +284,10 @@ QPointer<Ui::BoxContent> StickerSetBox::Show(
if (const auto sticker = document->sticker()) { if (const auto sticker = document->sticker()) {
if (sticker->set) { if (sticker->set) {
return controller->show( return controller->show(
Box<StickerSetBox>(controller, sticker->set), Box<StickerSetBox>(
controller,
sticker->set,
sticker->setType),
Ui::LayerOption::KeepOther).data(); Ui::LayerOption::KeepOther).data();
} }
} }
@ -268,14 +298,18 @@ void StickerSetBox::prepare() {
setTitle(tr::lng_contacts_loading()); setTitle(tr::lng_contacts_loading());
_inner = setInnerWidget( _inner = setInnerWidget(
object_ptr<Inner>(this, _controller, _set), object_ptr<Inner>(this, _controller, _set, _type),
st::stickersScroll); st::stickersScroll);
_controller->session().data().stickers().updated( _controller->session().data().stickers().updated(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
updateButtons(); updateButtons();
}, lifetime()); }, lifetime());
setDimensions(st::boxWideWidth, st::stickersMaxHeight); setDimensions(
st::boxWideWidth,
(_type == Data::StickersType::Emoji
? st::emojiSetMaxHeight
: st::stickersMaxHeight));
updateTitleAndButtons(); updateTitleAndButtons();
@ -476,10 +510,12 @@ void StickerSetBox::resizeEvent(QResizeEvent *e) {
StickerSetBox::Inner::Inner( StickerSetBox::Inner::Inner(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
const StickerSetIdentifier &set) const StickerSetIdentifier &set,
Data::StickersType type)
: RpWidget(parent) : RpWidget(parent)
, _controller(controller) , _controller(controller)
, _api(&_controller->session().mtp()) , _api(&_controller->session().mtp())
, _manager(std::make_unique<Ui::CustomEmoji::SimpleManager>())
, _setId(set.id) , _setId(set.id)
, _setAccessHash(set.accessHash) , _setAccessHash(set.accessHash)
, _setShortName(set.shortName) , _setShortName(set.shortName)
@ -489,6 +525,9 @@ StickerSetBox::Inner::Inner(
[=] { repaintItems(); })) [=] { repaintItems(); }))
, _updateItemsTimer([=] { updateItems(); }) , _updateItemsTimer([=] { updateItems(); })
, _input(set) , _input(set)
, _padding((type == Data::StickersType::Emoji)
? st::emojiSetPadding
: st::stickersPadding)
, _previewTimer([=] { showPreview(); }) { , _previewTimer([=] { showPreview(); }) {
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
@ -613,16 +652,11 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
} }
_perRow = isEmojiSet() ? kEmojiPerRow : kStickersPerRow; _perRow = isEmojiSet() ? kEmojiPerRow : kStickersPerRow;
_rowsCount = (_pack.size() + _perRow - 1) / _perRow; _rowsCount = (_pack.size() + _perRow - 1) / _perRow;
const auto fullWidth = st::boxWideWidth; _singleSize = isEmojiSet() ? st::emojiSetSize : st::stickersSize;
const auto rowsLeft = st::stickersPadding.left();
const auto rowsRight = rowsLeft;
const auto singleWidth = (fullWidth - rowsLeft - rowsRight)
/ _perRow;
_singleSize = isEmojiSet()
? QSize(singleWidth, singleWidth)
: st::stickersSize;
resize(st::stickersPadding.left() + _perRow * _singleSize.width(), st::stickersPadding.top() + _rowsCount * _singleSize.height() + st::stickersPadding.bottom()); resize(
_padding.left() + _perRow * _singleSize.width(),
_padding.top() + _rowsCount * _singleSize.height() + _padding.bottom());
_loaded = true; _loaded = true;
updateSelected(); updateSelected();
@ -862,8 +896,8 @@ void StickerSetBox::Inner::startOverAnimation(int index, float64 from, float64 t
_elements[index].overAnimation.start([=] { _elements[index].overAnimation.start([=] {
const auto row = index / _perRow; const auto row = index / _perRow;
const auto column = index % _perRow; const auto column = index % _perRow;
const auto left = st::stickersPadding.left() + column * _singleSize.width(); const auto left = _padding.left() + column * _singleSize.width();
const auto top = st::stickersPadding.top() + row * _singleSize.height(); const auto top = _padding.top() + row * _singleSize.height();
rtlupdate(left, top, _singleSize.width(), _singleSize.height()); rtlupdate(left, top, _singleSize.width(), _singleSize.height());
}, from, to, st::emojiPanDuration); }, from, to, st::emojiPanDuration);
} }
@ -903,8 +937,8 @@ not_null<Lottie::MultiPlayer*> StickerSetBox::Inner::getLottiePlayer() {
int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const { int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
QPoint l(mapFromGlobal(p)); QPoint l(mapFromGlobal(p));
if (rtl()) l.setX(width() - l.x()); if (rtl()) l.setX(width() - l.x());
int32 row = (l.y() >= st::stickersPadding.top()) ? qFloor((l.y() - st::stickersPadding.top()) / _singleSize.height()) : -1; int32 row = (l.y() >= _padding.top()) ? qFloor((l.y() - _padding.top()) / _singleSize.height()) : -1;
int32 col = (l.x() >= st::stickersPadding.left()) ? qFloor((l.x() - st::stickersPadding.left()) / _singleSize.width()) : -1; int32 col = (l.x() >= _padding.left()) ? qFloor((l.x() - _padding.left()) / _singleSize.width()) : -1;
if (row >= 0 && col >= 0 && col < _perRow) { if (row >= 0 && col >= 0 && col < _perRow) {
int32 result = row * _perRow + col; int32 result = row * _perRow + col;
return (result < _pack.size()) ? result : -1; return (result < _pack.size()) ? result : -1;
@ -915,6 +949,8 @@ int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
void StickerSetBox::Inner::paintEvent(QPaintEvent *e) { void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
_repaintScheduled = false;
p.fillRect(e->rect(), st::boxBg); p.fillRect(e->rect(), st::boxBg);
if (_elements.empty()) { if (_elements.empty()) {
return; return;
@ -933,7 +969,9 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
if (index >= _elements.size()) { if (index >= _elements.size()) {
break; break;
} }
const auto pos = QPoint(st::stickersPadding.left() + j * _singleSize.width(), st::stickersPadding.top() + i * _singleSize.height()); const auto pos = QPoint(
_padding.left() + j * _singleSize.width(),
_padding.top() + i * _singleSize.height());
paintSticker(p, index, pos, paused, now); paintSticker(p, index, pos, paused, now);
} }
} }
@ -984,7 +1022,7 @@ void StickerSetBox::Inner::visibleTopBottomUpdated(
} }
} }
}; };
const auto rowsTop = st::stickersPadding.top(); const auto rowsTop = _padding.top();
const auto singleHeight = _singleSize.height(); const auto singleHeight = _singleSize.height();
const auto rowsBottom = rowsTop + _rowsCount * singleHeight; const auto rowsBottom = rowsTop + _rowsCount * singleHeight;
if (visibleTop >= rowsTop + singleHeight && visibleTop < rowsBottom) { if (visibleTop >= rowsTop + singleHeight && visibleTop < rowsBottom) {
@ -1058,6 +1096,37 @@ void StickerSetBox::Inner::clipCallback(
updateItems(); updateItems();
} }
void StickerSetBox::Inner::setupEmoji(int index) {
auto &element = _elements[index];
element.emoji = resolveCustomInstance(element.document);
}
auto StickerSetBox::Inner::resolveCustomInstance(
not_null<DocumentData*> document)
-> not_null<CustomInstance*> {
const auto i = _instances.find(document);
if (i != end(_instances)) {
return i->second.get();
}
auto instance = _manager->make(
_controller->session().data().customEmojiManager().createLoader(
document,
Data::CustomEmojiManager::SizeTag::Large),
[=] { customEmojiRepaint(); });
return _instances.emplace(
document,
std::move(instance)
).first->second.get();
}
void StickerSetBox::Inner::customEmojiRepaint() {
if (_repaintScheduled) {
return;
}
_repaintScheduled = true;
update();
}
void StickerSetBox::Inner::paintSticker( void StickerSetBox::Inner::paintSticker(
Painter &p, Painter &p,
int index, int index,
@ -1080,7 +1149,9 @@ void StickerSetBox::Inner::paintSticker(
&& !_controller->session().premium(); && !_controller->session().premium();
media->checkStickerSmall(); media->checkStickerSmall();
if (media->loaded()) { if (sticker->setType == Data::StickersType::Emoji) {
const_cast<Inner*>(this)->setupEmoji(index);
} else if (media->loaded()) {
if (sticker->isLottie() && !element.lottie) { if (sticker->isLottie() && !element.lottie) {
const_cast<Inner*>(this)->setupLottie(index); const_cast<Inner*>(this)->setupLottie(index);
} else if (sticker->isWebm() && !element.webm) { } else if (sticker->isWebm() && !element.webm) {
@ -1095,7 +1166,15 @@ void StickerSetBox::Inner::paintSticker(
(_singleSize.width() - size.width()) / 2, (_singleSize.width() - size.width()) / 2,
(_singleSize.height() - size.height()) / 2); (_singleSize.height() - size.height()) / 2);
auto lottieFrame = QImage(); auto lottieFrame = QImage();
if (element.lottie && element.lottie->ready()) { if (element.emoji) {
element.emoji->object.paint(
p,
ppos.x(),
ppos.y(),
now,
st::windowBgOver->c,
paused);
} else if (element.lottie && element.lottie->ready()) {
lottieFrame = element.lottie->frame(); lottieFrame = element.lottie->frame();
p.drawImage( p.drawImage(
QRect(ppos, lottieFrame.size() / cIntRetinaFactor()), QRect(ppos, lottieFrame.size() / cIntRetinaFactor()),

View file

@ -19,12 +19,21 @@ namespace Ui {
class PlainShadow; class PlainShadow;
} // namespace Ui } // namespace Ui
namespace Data {
class StickersSet;
} // namespace Data
class StickerSetBox final : public Ui::BoxContent { class StickerSetBox final : public Ui::BoxContent {
public: public:
StickerSetBox( StickerSetBox(
QWidget*, QWidget*,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
const StickerSetIdentifier &set); const StickerSetIdentifier &set,
Data::StickersType type);
StickerSetBox(
QWidget*,
not_null<Window::SessionController*> controller,
not_null<Data::StickersSet*> set);
static QPointer<Ui::BoxContent> Show( static QPointer<Ui::BoxContent> Show(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
@ -48,6 +57,7 @@ private:
const not_null<Window::SessionController*> _controller; const not_null<Window::SessionController*> _controller;
const StickerSetIdentifier _set; const StickerSetIdentifier _set;
const Data::StickersType _type;
class Inner; class Inner;
QPointer<Inner> _inner; QPointer<Inner> _inner;

View file

@ -1806,7 +1806,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
const auto showSetByRow = [&](const Row &row) { const auto showSetByRow = [&](const Row &row) {
setSelected(SelectedRow()); setSelected(SelectedRow());
_controller->show( _controller->show(
Box<StickerSetBox>(_controller, row.set->identifier()), Box<StickerSetBox>(_controller, row.set),
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
}; };
if (selectedIndex >= 0 && !_inDragArea) { if (selectedIndex >= 0 && !_inDragArea) {

View file

@ -79,6 +79,9 @@ stickersFeaturedInstalled: icon {{ "chat/input_save", lightButtonFg }};
stickersMaxHeight: 320px; stickersMaxHeight: 320px;
stickersPadding: margins(19px, 13px, 19px, 13px); stickersPadding: margins(19px, 13px, 19px, 13px);
stickersSize: size(64px, 64px); stickersSize: size(64px, 64px);
emojiSetPadding: margins(12px, 0px, 12px, 0px);
emojiSetMaxHeight: 197px;
emojiSetSize: size(42px, 39px);
stickersScroll: ScrollArea(boxScroll) { stickersScroll: ScrollArea(boxScroll) {
deltat: 19px; deltat: 19px;
deltab: 9px; deltab: 9px;
@ -225,6 +228,9 @@ stickerIconMove: 400;
stickerPreviewDuration: 150; stickerPreviewDuration: 150;
stickerPreviewMin: 0.1; stickerPreviewMin: 0.1;
emojiIconWidth: 35px;
emojiIconArea: 32px;
stickerGroupCategorySize: 28px; stickerGroupCategorySize: 28px;
stickerGroupCategoryAbout: defaultTextStyle; stickerGroupCategoryAbout: defaultTextStyle;
stickerGroupCategoryAddMargin: margins(0px, 10px, 0px, 5px); stickerGroupCategoryAddMargin: margins(0px, 10px, 0px, 5px);

View file

@ -96,17 +96,9 @@ private:
}; };
struct EmojiListWidget::CustomInstance { struct EmojiListWidget::CustomInstance : Ui::CustomEmoji::SeparateInstance {
CustomInstance( using SeparateInstance::SeparateInstance;
std::unique_ptr<Ui::CustomEmoji::Loader> loader,
Fn<void(
not_null<Ui::CustomEmoji::Instance*>,
Ui::CustomEmoji::RepaintRequest)> repaintLater,
Fn<void()> repaint,
bool recentOnly = false);
Ui::CustomEmoji::Instance emoji;
Ui::CustomEmoji::Object object;
bool recentOnly = false; bool recentOnly = false;
}; };
@ -115,20 +107,6 @@ struct EmojiListWidget::RecentOne {
RecentEmojiId id; RecentEmojiId id;
}; };
EmojiListWidget::CustomInstance::CustomInstance(
std::unique_ptr<Ui::CustomEmoji::Loader> loader,
Fn<void(
not_null<Ui::CustomEmoji::Instance*>,
Ui::CustomEmoji::RepaintRequest)> repaintLater,
Fn<void()> repaint,
bool recentOnly)
: emoji(
Ui::CustomEmoji::Loading(std::move(loader), Ui::CustomEmoji::Preview()),
std::move(repaintLater))
, object(&emoji, std::move(repaint))
, recentOnly(recentOnly) {
}
EmojiColorPicker::EmojiColorPicker(QWidget *parent) EmojiColorPicker::EmojiColorPicker(QWidget *parent)
: RpWidget(parent) { : RpWidget(parent) {
setMouseTracking(true); setMouseTracking(true);
@ -540,23 +518,42 @@ void EmojiListWidget::unloadNotSeenCustom(
int visibleTop, int visibleTop,
int visibleBottom) { int visibleBottom) {
enumerateSections([&](const SectionInfo &info) { enumerateSections([&](const SectionInfo &info) {
if (info.section < kEmojiSectionCount if (info.rowsBottom <= visibleTop || info.rowsTop >= visibleBottom) {
|| (info.rowsBottom > visibleTop unloadCustomIn(info);
&& info.rowsTop < visibleBottom)) {
return true;
}
auto &custom = _custom[info.section - kEmojiSectionCount];
if (!custom.painted) {
return true;
}
custom.painted = false;
for (const auto &single : custom.list) {
single.instance->object.unload();
} }
return true; return true;
}); });
} }
void EmojiListWidget::unloadAllCustom() {
enumerateSections([&](const SectionInfo &info) {
unloadCustomIn(info);
return true;
});
}
void EmojiListWidget::unloadCustomIn(const SectionInfo &info) {
if (!info.section && _recentPainted) {
_recentPainted = false;
for (const auto &single : _recent) {
if (const auto instance = single.instance) {
instance->object.unload();
}
}
return;
} else if (info.section < kEmojiSectionCount) {
return;
}
auto &custom = _custom[info.section - kEmojiSectionCount];
if (!custom.painted) {
return;
}
custom.painted = false;
for (const auto &single : custom.list) {
single.instance->object.unload();
}
}
object_ptr<TabbedSelector::InnerFooter> EmojiListWidget::createFooter() { object_ptr<TabbedSelector::InnerFooter> EmojiListWidget::createFooter() {
Expects(_footer == nullptr); Expects(_footer == nullptr);
@ -1020,7 +1017,7 @@ void EmojiListWidget::displaySet(uint64 setId) {
auto it = sets.find(setId); auto it = sets.find(setId);
if (it != sets.cend()) { if (it != sets.cend()) {
checkHideWithBox(controller()->show( checkHideWithBox(controller()->show(
Box<StickerSetBox>(controller(), it->second->identifier()), Box<StickerSetBox>(controller(), it->second.get()),
Ui::LayerOption::KeepOther).data()); Ui::LayerOption::KeepOther).data());
} }
} }
@ -1206,9 +1203,14 @@ void EmojiListWidget::processHideFinished() {
_picker->hideFast(); _picker->hideFast();
_pickerSelected = v::null; _pickerSelected = v::null;
} }
unloadAllCustom();
clearSelection(); clearSelection();
} }
void EmojiListWidget::processPanelHideFinished() {
unloadAllCustom();
}
void EmojiListWidget::refreshRecent() { void EmojiListWidget::refreshRecent() {
clearSelection(); clearSelection();
fillRecent(); fillRecent();
@ -1302,11 +1304,12 @@ auto EmojiListWidget::customInstanceWithLoader(
}); });
} }
}; };
return std::make_unique<CustomInstance>( auto result = std::make_unique<CustomInstance>(
std::move(loader), std::move(loader),
std::move(repaintDelayed), std::move(repaintDelayed),
std::move(repaintNow), std::move(repaintNow));
recentOnly); result->recentOnly = recentOnly;
return result;
} }
auto EmojiListWidget::resolveCustomInstance( auto EmojiListWidget::resolveCustomInstance(

View file

@ -96,6 +96,7 @@ protected:
TabbedSelector::InnerFooter *getFooter() const override; TabbedSelector::InnerFooter *getFooter() const override;
void processHideFinished() override; void processHideFinished() override;
void processPanelHideFinished() override;
int countDesiredHeight(int newWidth) override; int countDesiredHeight(int newWidth) override;
private: private:
@ -179,6 +180,8 @@ private:
bool checkPickerHide(); bool checkPickerHide();
void refreshCustom(); void refreshCustom();
void unloadNotSeenCustom(int visibleTop, int visibleBottom); void unloadNotSeenCustom(int visibleTop, int visibleBottom);
void unloadAllCustom();
void unloadCustomIn(const SectionInfo &info);
void ensureLoaded(int section); void ensureLoaded(int section);
void updateSelected(); void updateSelected();

View file

@ -41,36 +41,12 @@ constexpr auto kAnimationDuration = crl::time(120);
} // namespace } // namespace
struct SuggestionsWidget::CustomInstance {
CustomInstance(
std::unique_ptr<Ui::CustomEmoji::Loader> loader,
Fn<void(
not_null<Ui::CustomEmoji::Instance*>,
Ui::CustomEmoji::RepaintRequest)> repaintLater,
Fn<void()> repaint);
Ui::CustomEmoji::Instance emoji;
Ui::CustomEmoji::Object object;
};
SuggestionsWidget::CustomInstance::CustomInstance(
std::unique_ptr<Ui::CustomEmoji::Loader> loader,
Fn<void(
not_null<Ui::CustomEmoji::Instance*>,
Ui::CustomEmoji::RepaintRequest)> repaintLater,
Fn<void()> repaint)
: emoji(
Ui::CustomEmoji::Loading(std::move(loader), Ui::CustomEmoji::Preview()),
std::move(repaintLater))
, object(&emoji, std::move(repaint)) {
}
SuggestionsWidget::SuggestionsWidget( SuggestionsWidget::SuggestionsWidget(
QWidget *parent, QWidget *parent,
not_null<Main::Session*> session) not_null<Main::Session*> session)
: RpWidget(parent) : RpWidget(parent)
, _session(session) , _session(session)
, _repaintTimer([=] { invokeRepaints(); }) , _manager(std::make_unique<Ui::CustomEmoji::SimpleManager>())
, _oneWidth(st::emojiSuggestionSize) , _oneWidth(st::emojiSuggestionSize)
, _padding(st::emojiSuggestionsPadding) { , _padding(st::emojiSuggestionsPadding) {
resize( resize(
@ -180,77 +156,23 @@ auto SuggestionsWidget::resolveCustomInstance(
if (i != end(_instances)) { if (i != end(_instances)) {
return i->second.get(); return i->second.get();
} }
const auto repaintDelayed = [=]( auto instance = _manager->make(
not_null<Ui::CustomEmoji::Instance*> instance,
Ui::CustomEmoji::RepaintRequest request) {
if (_instances.empty() || !request.when) {
return;
}
auto &when = _repaints[request.duration];
if (when < request.when) {
when = request.when;
}
if (_repaintTimerScheduled) {
return;
}
scheduleRepaintTimer();
};
const auto repaintNow = [=] {
update();
};
auto instance = std::make_unique<CustomInstance>(
_session->data().customEmojiManager().createLoader( _session->data().customEmojiManager().createLoader(
document, document,
Data::CustomEmojiManager::SizeTag::Large), Data::CustomEmojiManager::SizeTag::Large),
std::move(repaintDelayed), [=] { customEmojiRepaint(); });
std::move(repaintNow));
return _instances.emplace( return _instances.emplace(
document, document,
std::move(instance) std::move(instance)
).first->second.get(); ).first->second.get();
} }
void SuggestionsWidget::scheduleRepaintTimer() { void SuggestionsWidget::customEmojiRepaint() {
_repaintTimerScheduled = true; if (_repaintScheduled) {
Ui::PostponeCall(this, [=] { return;
_repaintTimerScheduled = false;
auto next = crl::time();
for (const auto &[duration, when] : _repaints) {
if (!next || next > when) {
next = when;
}
}
if (next && (!_repaintNext || _repaintNext > next)) {
const auto now = crl::now();
if (now >= next) {
_repaintNext = 0;
_repaintTimer.cancel();
invokeRepaints();
} else {
_repaintNext = next;
_repaintTimer.callOnce(next - now);
}
}
});
}
void SuggestionsWidget::invokeRepaints() {
_repaintNext = 0;
auto invoke = false;
const auto now = crl::now();
for (auto i = begin(_repaints); i != end(_repaints);) {
if (i->second > now) {
++i;
continue;
}
invoke = true;
i = _repaints.erase(i);
} }
if (invoke) { _repaintScheduled = true;
update(); update();
}
scheduleRepaintTimer();
} }
SuggestionsWidget::Row::Row( SuggestionsWidget::Row::Row(
@ -378,6 +300,8 @@ void SuggestionsWidget::scrollByWheelEvent(not_null<QWheelEvent*> e) {
void SuggestionsWidget::paintEvent(QPaintEvent *e) { void SuggestionsWidget::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
_repaintScheduled = false;
const auto clip = e->rect(); const auto clip = e->rect();
p.fillRect(clip, st::boxBg); p.fillRect(clip, st::boxBg);

View file

@ -19,11 +19,16 @@ class Session;
} // namespace Main } // namespace Main
namespace Ui { namespace Ui {
class InnerDropdown; class InnerDropdown;
class InputField; class InputField;
} // namespace Ui
namespace Emoji { namespace Ui::CustomEmoji {
struct SeparateInstance;
class SimpleManager;
} // namespace Ui::CustomEmoji
namespace Ui::Emoji {
class SuggestionsWidget final : public Ui::RpWidget { class SuggestionsWidget final : public Ui::RpWidget {
public: public:
@ -43,7 +48,7 @@ public:
[[nodiscard]] rpl::producer<Chosen> triggered() const; [[nodiscard]] rpl::producer<Chosen> triggered() const;
private: private:
struct CustomInstance; using CustomInstance = Ui::CustomEmoji::SeparateInstance;
struct Row { struct Row {
Row(not_null<EmojiPtr> emoji, const QString &replacement); Row(not_null<EmojiPtr> emoji, const QString &replacement);
@ -89,19 +94,17 @@ private:
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance( [[nodiscard]] not_null<CustomInstance*> resolveCustomInstance(
not_null<DocumentData*> document); not_null<DocumentData*> document);
void scheduleRepaintTimer(); void customEmojiRepaint();
void invokeRepaints();
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
QString _query; QString _query;
std::vector<Row> _rows; std::vector<Row> _rows;
base::flat_map< base::flat_map<
not_null<DocumentData*>, not_null<DocumentData*>,
std::unique_ptr<CustomInstance>> _instances; std::unique_ptr<CustomInstance>> _instances;
base::flat_map<crl::time, crl::time> _repaints; std::unique_ptr<Ui::CustomEmoji::SimpleManager> _manager;
bool _repaintTimerScheduled = false; bool _repaintScheduled = false;
base::Timer _repaintTimer;
crl::time _repaintNext = 0;
std::optional<QPoint> _lastMousePosition; std::optional<QPoint> _lastMousePosition;
bool _mouseSelection = false; bool _mouseSelection = false;
@ -190,5 +193,4 @@ private:
}; };
} // namespace Emoji } // namespace Ui::Emoji
} // namespace Ui

View file

@ -139,8 +139,8 @@ bool StickersListFooter::ScrollState::animationCallback(crl::time now) {
return false; return false;
} }
x.update(dt, anim::linear); x.update(dt, anim::linear);
selectionX.update(dt, anim::linear); selectionX.update(dt, anim::easeOutCubic);
selectionWidth.update(dt, anim::linear); selectionWidth.update(dt, anim::easeOutCubic);
return true; return true;
} }
@ -151,7 +151,8 @@ StickersListFooter::StickersListFooter(Descriptor &&descriptor)
, _settingsButtonVisible(descriptor.settingsButtonVisible) , _settingsButtonVisible(descriptor.settingsButtonVisible)
, _iconState([=] { update(); }) , _iconState([=] { update(); })
, _subiconState([=] { update(); }) , _subiconState([=] { update(); })
, _selectionBg(st::roundRadiusSmall, st::windowBgRipple) , _selectionBg(st::roundRadiusLarge, st::windowBgRipple)
, _subselectionBg(st::emojiIconArea / 2, st::windowBgRipple)
, _barSelection(descriptor.barSelection) { , _barSelection(descriptor.barSelection) {
setMouseTracking(true); setMouseTracking(true);
@ -299,14 +300,13 @@ void StickersListFooter::enumerateIcons(
const auto shift = _iconsLeft - iconsX; const auto shift = _iconsLeft - iconsX;
const auto emojiId = AllEmojiSectionSetId(); const auto emojiId = AllEmojiSectionSetId();
const auto right = width(); const auto right = width();
const auto single = st::stickerIconWidth;
for (auto i = 0, count = int(_icons.size()); i != count; ++i) { for (auto i = 0, count = int(_icons.size()); i != count; ++i) {
auto &icon = _icons[i]; auto &icon = _icons[i];
const auto width = (icon.setId == emojiId) const auto width = (icon.setId == emojiId)
? _subiconsWidthAnimation.value(_subiconsExpanded ? _subiconsWidthAnimation.value(_subiconsExpanded
? _subiconsWidth ? _subiconsWidth
: single) : _singleWidth)
: single; : _singleWidth;
const auto shifted = shift + left; const auto shifted = shift + left;
const auto visible = (shifted + width > 0 && shifted < right); const auto visible = (shifted + width > 0 && shifted < right);
const auto result = callback({ const auto result = callback({
@ -331,20 +331,19 @@ void StickersListFooter::enumerateSubicons(
const auto right = _subiconsWidth; const auto right = _subiconsWidth;
using Section = Ui::Emoji::Section; using Section = Ui::Emoji::Section;
for (auto i = int(Section::People); i <= int(Section::Symbols); ++i) { for (auto i = int(Section::People); i <= int(Section::Symbols); ++i) {
const auto width = st::stickerIconWidth;
const auto shifted = shift + left; const auto shifted = shift + left;
const auto visible = (shifted + width > 0 && shifted < right); const auto visible = (shifted + _singleWidth > 0 && shifted < right);
const auto result = callback({ const auto result = callback({
.index = i - int(Section::People), .index = i - int(Section::People),
.left = left, .left = left,
.adjustedLeft = shifted, .adjustedLeft = shifted,
.width = int(base::SafeRound(width)), .width = _singleWidth,
.visible = visible, .visible = visible,
}); });
if (!result) { if (!result) {
break; break;
} }
left += width; left += _singleWidth;
} }
} }
@ -432,8 +431,8 @@ void StickersListFooter::updateEmojiSectionWidth() {
_subiconsExpanded = expanded; _subiconsExpanded = expanded;
_subiconsWidthAnimation.start( _subiconsWidthAnimation.start(
[=] { updateEmojiWidthCallback(); }, [=] { updateEmojiWidthCallback(); },
expanded ? st::stickerIconWidth : _subiconsWidth, expanded ? _singleWidth : _subiconsWidth,
expanded ? _subiconsWidth : st::stickerIconWidth, expanded ? _subiconsWidth : _singleWidth,
st::slideDuration); st::slideDuration);
} }
@ -580,12 +579,27 @@ void StickersListFooter::paintSelectionBg(Painter &p) const {
selx = width() - selx - selw; selx = width() - selx - selw;
} }
const auto skip = st::emojiIconSelectSkip; const auto skip = st::emojiIconSelectSkip;
const auto sely = _iconsTop const auto sely = _iconsTop;
+ (st::emojiFooterHeight - st::stickerIconWidth) / 2; const auto area = st::emojiIconArea;
const auto selh = st::stickerIconWidth; const auto rect = QRect(
const auto rect = QRect(selx, sely, selw, selh); QPoint(selx, sely) + _areaPosition,
const auto fill = rect.marginsRemoved({ skip, skip, skip, skip }); QSize(selw - 2 * _areaPosition.x(), area));
_selectionBg.paint(p, fill); if (rect.width() == rect.height() || _subiconsWidth <= _singleWidth) {
_selectionBg.paint(p, rect);
} else if (selw == _subiconsWidth) {
_subselectionBg.paint(p, rect);
} else {
PainterHighQualityEnabler hq(p);
const auto progress = (selw - _singleWidth)
/ float64(_subiconsWidth - _singleWidth);
const auto radius = anim::interpolate(
st::roundRadiusLarge,
area / 2,
progress);
p.setPen(Qt::NoPen);
p.setBrush(st::windowBgRipple);
p.drawRoundedRect(rect, radius, radius);
}
} }
void StickersListFooter::paintSelectionBar(Painter &p) const { void StickersListFooter::paintSelectionBar(Painter &p) const {
@ -864,17 +878,17 @@ void StickersListFooter::updateSelected() {
auto x = p.x(), y = p.y(); auto x = p.x(), y = p.y();
if (rtl()) x = width() - x; if (rtl()) x = width() - x;
const auto settingsLeft = width() - _iconsRight; const auto settingsLeft = width() - _iconsRight;
const auto searchLeft = _iconsLeft - st::stickerIconWidth; const auto searchLeft = _iconsLeft - _singleWidth;
auto newOver = OverState(SpecialOver::None); auto newOver = OverState(SpecialOver::None);
if (_searchButtonVisible if (_searchButtonVisible
&& x >= searchLeft && x >= searchLeft
&& x < searchLeft + st::stickerIconWidth && x < searchLeft + _singleWidth
&& y >= _iconsTop && y >= _iconsTop
&& y < _iconsTop + st::emojiFooterHeight) { && y < _iconsTop + st::emojiFooterHeight) {
newOver = SpecialOver::Search; newOver = SpecialOver::Search;
} else if (_settingsButtonVisible } else if (_settingsButtonVisible
&& x >= settingsLeft && x >= settingsLeft
&& x < settingsLeft + st::stickerIconWidth && x < settingsLeft + _singleWidth
&& y >= _iconsTop && y >= _iconsTop
&& y < _iconsTop + st::emojiFooterHeight) { && y < _iconsTop + st::emojiFooterHeight) {
if (!_icons.empty() && !hasOnlyFeaturedSets()) { if (!_icons.empty() && !hasOnlyFeaturedSets()) {
@ -975,6 +989,17 @@ void StickersListFooter::refreshIconsGeometry(
_iconState.selectionWidth.finish(); _iconState.selectionWidth.finish();
_iconState.animationStart = 0; _iconState.animationStart = 0;
_iconState.animation.stop(); _iconState.animation.stop();
if (_barSelection) {
_singleWidth = st::stickerIconWidth;
} else if (_icons.size() > 1
&& _icons[1].setId == EmojiSectionSetId(EmojiSection::People)) {
_singleWidth = (width() - _iconsLeft - _iconsRight) / _icons.size();
} else {
_singleWidth = st::emojiIconWidth;
}
_areaPosition = QPoint(
(_singleWidth - st::emojiIconArea) / 2,
(st::emojiFooterHeight - st::emojiIconArea) / 2);
refreshScrollableDimensions(); refreshScrollableDimensions();
updateSelected(); updateSelected();
validateSelectedIcon(_activeByScrollId, animations); validateSelectedIcon(_activeByScrollId, animations);
@ -988,18 +1013,18 @@ void StickersListFooter::refreshSubiconsGeometry() {
_subiconState.selectionWidth.finish(); _subiconState.selectionWidth.finish();
_subiconState.animationStart = 0; _subiconState.animationStart = 0;
_subiconState.animation.stop(); _subiconState.animation.stop();
const auto single = st::stickerIconWidth; const auto half = _singleWidth / 2;
const auto half = single / 2;
const auto count = int(Section::Symbols) - int(Section::Recent); const auto count = int(Section::Symbols) - int(Section::Recent);
const auto widthMax = count * single; const auto widthMax = count * _singleWidth;
const auto widthMin = 4 * single + half; const auto widthMin = 4 * _singleWidth + half;
const auto collapsedWidth = int(_icons.size()) * single; const auto collapsedWidth = int(_icons.size()) * _singleWidth;
_subiconsWidth = std::clamp( _subiconsWidth = std::clamp(
width() + single - collapsedWidth, width() + _singleWidth - collapsedWidth,
widthMin, widthMin,
widthMax); widthMax);
if (_subiconsWidth < widthMax) { if (_subiconsWidth < widthMax) {
_subiconsWidth = ((_subiconsWidth - half) / single) * single + half; _subiconsWidth = half
+ (((_subiconsWidth - half) / _singleWidth) * _singleWidth);
} }
_subiconState.max = std::max( _subiconState.max = std::max(
widthMax - _subiconsWidth, widthMax - _subiconsWidth,
@ -1020,16 +1045,16 @@ void StickersListFooter::paintStickerSettingsIcon(Painter &p) const {
st::stickersSettings.paint( st::stickersSettings.paint(
p, p,
settingsLeft settingsLeft
+ (st::stickerIconWidth - st::stickersSettings.width()) / 2, + (_singleWidth - st::stickersSettings.width()) / 2,
_iconsTop + st::emojiCategory.iconPosition.y(), _iconsTop + st::emojiCategory.iconPosition.y(),
width()); width());
} }
void StickersListFooter::paintSearchIcon(Painter &p) const { void StickersListFooter::paintSearchIcon(Painter &p) const {
const auto searchLeft = _iconsLeft - st::stickerIconWidth; const auto searchLeft = _iconsLeft - _singleWidth;
st::stickersSearch.paint( st::stickersSearch.paint(
p, p,
searchLeft + (st::stickerIconWidth - st::stickersSearch.width()) / 2, searchLeft + (_singleWidth - st::stickersSearch.width()) / 2,
_iconsTop + st::emojiCategory.iconPosition.y(), _iconsTop + st::emojiCategory.iconPosition.y(),
width()); width());
} }
@ -1100,7 +1125,7 @@ void StickersListFooter::updateSetIcon(uint64 setId) {
} }
void StickersListFooter::updateSetIconAt(int left) { void StickersListFooter::updateSetIconAt(int left) {
update(left, _iconsTop, st::stickerIconWidth, st::emojiFooterHeight); update(left, _iconsTop, _singleWidth, st::emojiFooterHeight);
} }
void StickersListFooter::paintSetIcon( void StickersListFooter::paintSetIcon(
@ -1118,8 +1143,7 @@ void StickersListFooter::paintSetIcon(
: icon.stickerMedia : icon.stickerMedia
? icon.stickerMedia->thumbnail() ? icon.stickerMedia->thumbnail()
: nullptr; : nullptr;
const auto x = info.adjustedLeft const auto x = info.adjustedLeft + (_singleWidth - icon.pixw) / 2;
+ (st::stickerIconWidth - icon.pixw) / 2;
const auto y = _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2; const auto y = _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2;
if (icon.lottie && icon.lottie->ready()) { if (icon.lottie && icon.lottie->ready()) {
const auto frame = icon.lottie->frame(); const auto frame = icon.lottie->frame();
@ -1130,8 +1154,7 @@ void StickersListFooter::paintSetIcon(
} }
p.drawImage( p.drawImage(
QRect( QRect(
(info.adjustedLeft (info.adjustedLeft + (_singleWidth - size.width()) / 2),
+ (st::stickerIconWidth - size.width()) / 2),
_iconsTop + (st::emojiFooterHeight - size.height()) / 2, _iconsTop + (st::emojiFooterHeight - size.height()) / 2,
size.width(), size.width(),
size.height()), size.height()),
@ -1166,7 +1189,7 @@ void StickersListFooter::paintSetIcon(
icon.megagroup->paintUserpicLeft( icon.megagroup->paintUserpicLeft(
p, p,
icon.megagroupUserpic, icon.megagroupUserpic,
info.adjustedLeft + (st::stickerIconWidth - size) / 2, info.adjustedLeft + (_singleWidth - size) / 2,
_iconsTop + (st::emojiFooterHeight - size) / 2, _iconsTop + (st::emojiFooterHeight - size) / 2,
width(), width(),
st::stickerGroupCategorySize); st::stickerGroupCategorySize);
@ -1174,7 +1197,7 @@ void StickersListFooter::paintSetIcon(
validatePremiumIcon(); validatePremiumIcon();
const auto size = st::stickersPremium.size(); const auto size = st::stickersPremium.size();
p.drawImage( p.drawImage(
info.adjustedLeft + (st::stickerIconWidth - size.width()) / 2, info.adjustedLeft + (_singleWidth - size.width()) / 2,
_iconsTop + (st::emojiFooterHeight - size.height()) / 2, _iconsTop + (st::emojiFooterHeight - size.height()) / 2,
_premiumIcon); _premiumIcon);
} else { } else {
@ -1207,12 +1230,12 @@ void StickersListFooter::paintSetIcon(
const auto paintOne = [&](int left, const style::icon *icon) { const auto paintOne = [&](int left, const style::icon *icon) {
icon->paint( icon->paint(
p, p,
left + (st::stickerIconWidth - icon->width()) / 2, left + (_singleWidth - icon->width()) / 2,
_iconsTop + (st::emojiFooterHeight - icon->height()) / 2, _iconsTop + (st::emojiFooterHeight - icon->height()) / 2,
width()); width());
}; };
if (_icons[info.index].setId == AllEmojiSectionSetId() if (_icons[info.index].setId == AllEmojiSectionSetId()
&& info.width > st::stickerIconWidth) { && info.width > _singleWidth) {
const auto skip = st::emojiIconSelectSkip; const auto skip = st::emojiIconSelectSkip;
p.save(); p.save();
p.setClipRect( p.setClipRect(

View file

@ -226,11 +226,13 @@ private:
int _iconsLeft = 0; int _iconsLeft = 0;
int _iconsRight = 0; int _iconsRight = 0;
int _iconsTop = 0; int _iconsTop = 0;
int _singleWidth = 0;
QPoint _areaPosition;
ScrollState _iconState; ScrollState _iconState;
ScrollState _subiconState; ScrollState _subiconState;
Ui::RoundRect _selectionBg; Ui::RoundRect _selectionBg, _subselectionBg;
Ui::Animations::Simple _subiconsWidthAnimation; Ui::Animations::Simple _subiconsWidthAnimation;
int _subiconsWidth = 0; int _subiconsWidth = 0;
bool _subiconsExpanded = false; bool _subiconsExpanded = false;

View file

@ -2611,7 +2611,7 @@ void StickersListWidget::displaySet(uint64 setId) {
auto it = sets.find(setId); auto it = sets.find(setId);
if (it != sets.cend()) { if (it != sets.cend()) {
checkHideWithBox(controller()->show( checkHideWithBox(controller()->show(
Box<StickerSetBox>(controller(), it->second->identifier()), Box<StickerSetBox>(controller(), it->second.get()),
Ui::LayerOption::KeepOther).data()); Ui::LayerOption::KeepOther).data());
} }
} }

View file

@ -82,7 +82,10 @@ bool ShowStickerSet(
Core::App().hideMediaView(); Core::App().hideMediaView();
controller->show(Box<StickerSetBox>( controller->show(Box<StickerSetBox>(
controller, controller,
StickerSetIdentifier{ .shortName = match->captured(2) })); StickerSetIdentifier{ .shortName = match->captured(2) },
(match->captured(1) == "addemoji"
? Data::StickersType::Emoji
: Data::StickersType::Stickers)));
controller->window().activate(); controller->window().activate();
return true; return true;
} }

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/stickers/data_stickers.h"
#include "storage/file_download.h" #include "storage/file_download.h"
#include "ui/image/image.h" #include "ui/image/image.h"
@ -97,6 +98,14 @@ StickerSetIdentifier StickersSet::identifier() const {
}; };
} }
StickersType StickersSet::type() const {
return (flags & StickersSetFlag::Emoji)
? StickersType::Emoji
: (flags & StickersSetFlag::Masks)
? StickersType::Masks
: StickersType::Stickers;
}
void StickersSet::setThumbnail(const ImageWithLocation &data) { void StickersSet::setThumbnail(const ImageWithLocation &data) {
Data::UpdateCloudFile( Data::UpdateCloudFile(
_thumbnail, _thumbnail,

View file

@ -24,6 +24,8 @@ using SavedGifs = QVector<DocumentData*>;
using StickersPack = QVector<DocumentData*>; using StickersPack = QVector<DocumentData*>;
using StickersByEmojiMap = QMap<EmojiPtr, StickersPack>; using StickersByEmojiMap = QMap<EmojiPtr, StickersPack>;
enum class StickersType : uchar;
class StickersSet; class StickersSet;
using StickersSets = base::flat_map<uint64, std::unique_ptr<StickersSet>>; using StickersSets = base::flat_map<uint64, std::unique_ptr<StickersSet>>;
@ -81,6 +83,7 @@ public:
[[nodiscard]] MTPInputStickerSet mtpInput() const; [[nodiscard]] MTPInputStickerSet mtpInput() const;
[[nodiscard]] StickerSetIdentifier identifier() const; [[nodiscard]] StickerSetIdentifier identifier() const;
[[nodiscard]] StickersType type() const;
void setThumbnail(const ImageWithLocation &data); void setThumbnail(const ImageWithLocation &data);

View file

@ -1043,7 +1043,8 @@ void GenerateItems(
controller->show( controller->show(
Box<StickerSetBox>( Box<StickerSetBox>(
controller, controller,
Data::FromInputSet(set)), Data::FromInputSet(set),
Data::StickersType::Stickers),
Ui::LayerOption::CloseOther); Ui::LayerOption::CloseOther);
} }
}); });

View file

@ -1324,7 +1324,10 @@ void AddEmojiPacksAction(
} }
// Single used emoji pack. // Single used emoji pack.
strong->show( strong->show(
Box<StickerSetBox>(strong, packIds.front()), Box<StickerSetBox>(
strong,
packIds.front(),
Data::StickersType::Emoji),
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
}); });

View file

@ -195,7 +195,7 @@ void StickerToast::showWithTitle(const QString &title) {
} }
button->setClickedCallback([=] { button->setClickedCallback([=] {
_controller->show( _controller->show(
Box<StickerSetBox>(_controller, _for->sticker()->set), Box<StickerSetBox>(_controller, _for->sticker()->set, setType),
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
hideToast(); hideToast();
}); });

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/custom_emoji_instance.h" #include "ui/text/custom_emoji_instance.h"
#include "ui/effects/frame_generator.h" #include "ui/effects/frame_generator.h"
#include "ui/ui_utility.h"
#include <crl/crl_async.h> #include <crl/crl_async.h>
#include <lz4.h> #include <lz4.h>
@ -647,4 +648,84 @@ void Object::repaint() {
_repaint(); _repaint();
} }
SeparateInstance::SeparateInstance(
std::unique_ptr<Loader> loader,
Fn<void(not_null<Instance*>, RepaintRequest)> repaintLater,
Fn<void()> repaint)
: emoji(Loading(std::move(loader), Preview()), std::move(repaintLater))
, object(&emoji, std::move(repaint)) {
}
SimpleManager::SimpleManager()
: _repaintTimer([=] { invokeRepaints(); }) {
}
std::unique_ptr<SeparateInstance> SimpleManager::make(
std::unique_ptr<Loader> loader,
Fn<void()> repaint) {
auto repaintLater = [=](
not_null<Instance*> instance,
RepaintRequest request) {
if (!request.when) {
return;
}
auto &when = _repaints[request.duration];
if (when < request.when) {
when = request.when;
}
if (_repaintTimerScheduled) {
return;
}
scheduleRepaintTimer();
};
_simpleRepaint = repaint;
return std::make_unique<SeparateInstance>(
std::move(loader),
std::move(repaintLater),
std::move(repaint));
}
void SimpleManager::scheduleRepaintTimer() {
_repaintTimerScheduled = true;
Ui::PostponeCall(this, [=] {
_repaintTimerScheduled = false;
auto next = crl::time();
for (const auto &[duration, when] : _repaints) {
if (!next || next > when) {
next = when;
}
}
if (next && (!_repaintNext || _repaintNext > next)) {
const auto now = crl::now();
if (now >= next) {
_repaintNext = 0;
_repaintTimer.cancel();
invokeRepaints();
} else {
_repaintNext = next;
_repaintTimer.callOnce(next - now);
}
}
});
}
void SimpleManager::invokeRepaints() {
_repaintNext = 0;
auto invoke = false;
const auto now = crl::now();
for (auto i = begin(_repaints); i != end(_repaints);) {
if (i->second > now) {
++i;
continue;
}
invoke = true;
i = _repaints.erase(i);
}
if (invoke && _simpleRepaint) {
_simpleRepaint();
}
scheduleRepaintTimer();
}
} // namespace Ui::CustomEmoji } // namespace Ui::CustomEmoji

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_block.h" #include "ui/text/text_block.h"
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
#include "base/bytes.h" #include "base/bytes.h"
#include "base/timer.h"
class QColor; class QColor;
class QPainter; class QPainter;
@ -266,4 +267,34 @@ private:
}; };
struct SeparateInstance {
SeparateInstance(
std::unique_ptr<Loader> loader,
Fn<void(not_null<Instance*>, RepaintRequest)> repaintLater,
Fn<void()> repaint);
Instance emoji;
Object object;
};
class SimpleManager final : public base::has_weak_ptr {
public:
SimpleManager();
[[nodiscard]] std::unique_ptr<SeparateInstance> make(
std::unique_ptr<Loader> loader,
Fn<void()> repaint);
private:
void scheduleRepaintTimer();
void invokeRepaints();
base::flat_map<crl::time, crl::time> _repaints;
bool _repaintTimerScheduled = false;
crl::time _repaintNext = 0;
base::Timer _repaintTimer;
Fn<void()> _simpleRepaint;
};
} // namespace Ui::CustomEmoji } // namespace Ui::CustomEmoji