Display emoji correctly in folder tags.

This commit is contained in:
John Preston 2024-12-31 12:17:34 +04:00
parent 51b81dba87
commit acfd92e2e6
8 changed files with 362 additions and 93 deletions

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/message_field.h" #include "chat_helpers/message_field.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "core/ui_integration.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_peer.h" #include "data/data_peer.h"
@ -443,6 +444,13 @@ void EditFilterBox(
nameEditing->settingDefault = false; nameEditing->settingDefault = false;
} }
}; };
const auto nameWithEntities = [=](bool upper = false) {
const auto entered = name->getTextWithTags();
return TextWithEntities{
(upper ? entered.text.toUpper() : entered.text),
TextUtilities::ConvertTextTagsToEntities(entered.tags),
};
};
const auto outer = box->getDelegate()->outerContainer(); const auto outer = box->getDelegate()->outerContainer();
CreateIconSelector( CreateIconSelector(
@ -546,18 +554,28 @@ void EditFilterBox(
colors->width(), colors->width(),
h); h);
}, preview->lifetime()); }, preview->lifetime());
const auto previewTag = preview->lifetime().make_state<QImage>();
const auto previewAlpha = preview->lifetime().make_state<float64>(1); struct TagState {
Ui::Animations::Simple animation;
Ui::ChatsFilterTagContext context;
QImage frame;
float64 alpha = 1.;
};
const auto tag = preview->lifetime().make_state<TagState>();
tag->context.textContext = Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [] {},
};
preview->paintRequest() | rpl::start_with_next([=] { preview->paintRequest() | rpl::start_with_next([=] {
auto p = QPainter(preview); auto p = QPainter(preview);
p.setOpacity(*previewAlpha); p.setOpacity(tag->alpha);
const auto size = previewTag->size() / style::DevicePixelRatio(); const auto size = tag->frame.size() / style::DevicePixelRatio();
const auto rect = QRect( const auto rect = QRect(
preview->width() - size.width() - st::boxRowPadding.right(), preview->width() - size.width() - st::boxRowPadding.right(),
(st::normalFont->height - size.height()) / 2, (st::normalFont->height - size.height()) / 2,
size.width(), size.width(),
size.height()); size.height());
p.drawImage(rect.topLeft(), *previewTag); p.drawImage(rect.topLeft(), tag->frame);
if (p.opacity() < 1) { if (p.opacity() < 1) {
p.setOpacity(1. - p.opacity()); p.setOpacity(1. - p.opacity());
p.setFont(st::normalFont); p.setFont(st::normalFont);
@ -574,16 +592,14 @@ void EditFilterBox(
Ui::CreateSkipWidget(colors, side), Ui::CreateSkipWidget(colors, side),
st::boxRowPadding); st::boxRowPadding);
auto buttons = std::vector<not_null<UserpicBuilder::CircleButton*>>(); auto buttons = std::vector<not_null<UserpicBuilder::CircleButton*>>();
const auto animation
= line->lifetime().make_state<Ui::Animations::Simple>();
const auto palette = [](int i) { const auto palette = [](int i) {
return Ui::EmptyUserpic::UserpicColor(i).color2; return Ui::EmptyUserpic::UserpicColor(i).color2;
}; };
name->changes() | rpl::start_with_next([=] { name->changes() | rpl::start_with_next([=] {
*previewTag = Ui::ChatsFilterTag( tag->context.color = palette(state->colorIndex.current())->c;
name->getLastText().toUpper(), tag->frame = Ui::ChatsFilterTag(
palette(state->colorIndex.current())->c, nameWithEntities(true),
false); tag->context);
preview->update(); preview->update();
}, preview->lifetime()); }, preview->lifetime());
for (auto i = 0; i < kColorsCount; ++i) { for (auto i = 0; i < kColorsCount; ++i) {
@ -597,12 +613,12 @@ void EditFilterBox(
const auto color = palette(i); const auto color = palette(i);
button->setBrush(color); button->setBrush(color);
if (progress == 1) { if (progress == 1) {
*previewTag = Ui::ChatsFilterTag( tag->context.color = color->c;
name->getLastText().toUpper(), tag->frame = Ui::ChatsFilterTag(
color->c, nameWithEntities(true),
false); tag->context);
if (i == kNoTag) { if (i == kNoTag) {
*previewAlpha = 0.; tag->alpha = 0.;
} }
} }
buttons.push_back(button); buttons.push_back(button);
@ -617,17 +633,17 @@ void EditFilterBox(
const auto c2 = palette(now); const auto c2 = palette(now);
const auto a1 = (was == kNoTag) ? 0. : 1.; const auto a1 = (was == kNoTag) ? 0. : 1.;
const auto a2 = (now == kNoTag) ? 0. : 1.; const auto a2 = (now == kNoTag) ? 0. : 1.;
animation->stop(); tag->animation.stop();
animation->start([=](float64 progress) { tag->animation.start([=](float64 progress) {
if (was >= 0) { if (was >= 0) {
buttons[was]->setSelectedProgress(1. - progress); buttons[was]->setSelectedProgress(1. - progress);
} }
buttons[now]->setSelectedProgress(progress); buttons[now]->setSelectedProgress(progress);
*previewTag = Ui::ChatsFilterTag( tag->context.color = anim::color(c1, c2, progress);
name->getLastText().toUpper(), tag->frame = Ui::ChatsFilterTag(
anim::color(c1, c2, progress), nameWithEntities(true),
false); tag->context);
*previewAlpha = anim::interpolateF(a1, a2, progress); tag->alpha = anim::interpolateF(a1, a2, progress);
preview->update(); preview->update();
}, 0., 1., st::universalDuration); }, 0., 1., st::universalDuration);
} }
@ -673,11 +689,7 @@ void EditFilterBox(
} }
const auto collect = [=]() -> std::optional<Data::ChatFilter> { const auto collect = [=]() -> std::optional<Data::ChatFilter> {
const auto entered = name->getTextWithTags(); const auto title = nameWithEntities();
const auto title = TextWithEntities{
entered.text,
TextUtilities::ConvertTextTagsToEntities(entered.tags),
};
const auto rules = data->current(); const auto rules = data->current();
if (title.empty()) { if (title.empty()) {
name->showError(); name->showError();

View file

@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ffmpeg/ffmpeg_frame_generator.h" #include "ffmpeg/ffmpeg_frame_generator.h"
#include "chat_helpers/stickers_lottie.h" #include "chat_helpers/stickers_lottie.h"
#include "storage/file_download.h" // kMaxFileInMemory #include "storage/file_download.h" // kMaxFileInMemory
#include "ui/chat/chats_filter_tag.h"
#include "ui/effects/credits_graphics.h" #include "ui/effects/credits_graphics.h"
#include "ui/widgets/fields/input_field.h" #include "ui/widgets/fields/input_field.h"
#include "ui/text/custom_emoji_instance.h" #include "ui/text/custom_emoji_instance.h"
@ -104,6 +105,14 @@ private:
return u"userpic:"_q; return u"userpic:"_q;
} }
[[nodiscard]] QString ScaledSimplePrefix() {
return u"scaled-simple:"_q;
}
[[nodiscard]] QString ScaledCustomPrefix() {
return u"scaled-custom:"_q;
}
[[nodiscard]] QString InternalPadding(QMargins value) { [[nodiscard]] QString InternalPadding(QMargins value) {
return value.isNull() ? QString() : QString(",%1,%2,%3,%4" return value.isNull() ? QString() : QString(",%1,%2,%3,%4"
).arg(value.left() ).arg(value.left()
@ -536,7 +545,16 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
Fn<void()> update, Fn<void()> update,
SizeTag tag, SizeTag tag,
int sizeOverride) { int sizeOverride) {
if (data.startsWith(InternalPrefix())) { if (data.startsWith(ScaledSimplePrefix())) {
const auto text = data.mid(ScaledSimplePrefix().size());
const auto emoji = Ui::Emoji::Find(text);
Assert(emoji != nullptr);
return Ui::MakeScaledSimpleEmoji(emoji);
} else if (data.startsWith(ScaledCustomPrefix())) {
const auto original = data.mid(ScaledCustomPrefix().size());
return Ui::MakeScaledCustomEmoji(
create(original, std::move(update), SizeTag::Large));
} else if (data.startsWith(InternalPrefix())) {
return internal(data); return internal(data);
} else if (data.startsWith(UserpicEmojiPrefix())) { } else if (data.startsWith(UserpicEmojiPrefix())) {
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();

View file

@ -112,8 +112,10 @@ taggedForumDialogRow: DialogRow(forumDialogRow) {
height: 96px; height: 96px;
tagTop: 77px; tagTop: 77px;
} }
dialogRowFilterTagSkip : 4px; dialogRowFilterTagSkip: 4px;
dialogRowFilterTagFont : font(10px); dialogRowFilterTagStyle: TextStyle(defaultTextStyle) {
font: font(10px);
}
dialogRowOpenBotTextStyle: semiboldTextStyle; dialogRowOpenBotTextStyle: semiboldTextStyle;
dialogRowOpenBotHeight: 20px; dialogRowOpenBotHeight: 20px;
dialogRowOpenBotRight: 10px; dialogRowOpenBotRight: 10px;

View file

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h" #include "core/application.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "core/shortcuts.h" #include "core/shortcuts.h"
#include "core/ui_integration.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
@ -220,6 +221,11 @@ struct InnerWidget::PeerSearchResult {
BasicRow row; BasicRow row;
}; };
struct InnerWidget::TagCache {
Ui::ChatsFilterTagContext context;
QImage frame;
};
Key InnerWidget::FilterResult::key() const { Key InnerWidget::FilterResult::key() const {
return row->key(); return row->key();
} }
@ -4161,32 +4167,41 @@ QImage *InnerWidget::cacheChatsFilterTag(
return nullptr; return nullptr;
} }
const auto key = SerializeFilterTagsKey(filter.id(), more, active); const auto key = SerializeFilterTagsKey(filter.id(), more, active);
{ auto &entry = _chatsFilterTags[key];
const auto it = _chatsFilterTags.find(key); if (!entry.frame.isNull()) {
if (it != end(_chatsFilterTags)) { if (!entry.context.loading) {
return &it->second; return &entry.frame;
}
for (const auto &[k, emoji] : entry.context.emoji) {
if (!emoji->ready()) {
return &entry.frame; // Still waiting for emoji.
}
} }
} }
auto roundedText = QString(); auto roundedText = TextWithEntities();
auto colorIndex = -1; auto colorIndex = -1;
if (filter.id()) { if (filter.id()) {
roundedText = filter.title().text.toUpper(); // todo filter emoji roundedText = filter.title();
roundedText.text = roundedText.text.toUpper();
if (filter.colorIndex()) { if (filter.colorIndex()) {
colorIndex = *(filter.colorIndex()); colorIndex = *(filter.colorIndex());
} }
} else if (more > 0) { } else if (more > 0) {
roundedText = QChar('+') + QString::number(more); roundedText.text = QChar('+') + QString::number(more);
colorIndex = st::colorIndexBlue; colorIndex = st::colorIndexBlue;
} }
if (roundedText.isEmpty() || colorIndex < 0) { if (roundedText.empty() || colorIndex < 0) {
return nullptr; return nullptr;
} }
return &_chatsFilterTags.emplace( const auto color = Ui::EmptyUserpic::UserpicColor(colorIndex).color2;
key, entry.context.color = color->c;
Ui::ChatsFilterTag( entry.context.active = active;
std::move(roundedText), entry.context.textContext = Core::MarkedTextContext{
Ui::EmptyUserpic::UserpicColor(colorIndex).color2->c, .session = &session(),
active)).first->second; .customEmojiRepaint = [] {},
};
entry.frame = Ui::ChatsFilterTag(roundedText, entry.context);
return &entry.frame;
} }
bool InnerWidget::chooseHashtag() { bool InnerWidget::chooseHashtag() {

View file

@ -221,6 +221,7 @@ private:
struct CollapsedRow; struct CollapsedRow;
struct HashtagResult; struct HashtagResult;
struct PeerSearchResult; struct PeerSearchResult;
struct TagCache;
enum class JumpSkip { enum class JumpSkip {
PreviousOrBegin, PreviousOrBegin,
@ -579,7 +580,7 @@ private:
base::flat_map<FilterId, int> _chatsFilterScrollStates; base::flat_map<FilterId, int> _chatsFilterScrollStates;
std::unordered_map<ChatsFilterTagsKey, QImage> _chatsFilterTags; std::unordered_map<ChatsFilterTagsKey, TagCache> _chatsFilterTags;
bool _waitingAllChatListEntryRefreshesForTags = false; bool _waitingAllChatListEntryRefreshesForTags = false;
rpl::lifetime _handleChatListEntryTagRefreshesLifetime; rpl::lifetime _handleChatListEntryTagRefreshesLifetime;

View file

@ -7,59 +7,246 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "ui/chat/chats_filter_tag.h" #include "ui/chat/chats_filter_tag.h"
#include "ui/text/text_custom_emoji.h"
#include "ui/emoji_config.h" #include "ui/emoji_config.h"
#include "ui/integration.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
namespace Ui { namespace Ui {
namespace {
QImage ChatsFilterTag(QString roundedText, QColor color, bool active) { class ScaledSimpleEmoji final : public Ui::Text::CustomEmoji {
const auto &roundedFont = st::dialogRowFilterTagFont; public:
const auto additionalWidth = roundedFont->spacew * 3; ScaledSimpleEmoji(EmojiPtr emoji);
struct EmojiReplacement final {
QPixmap pixmap; int width() override;
int from = -1; QString entityData() override;
int length = 0; void paint(QPainter &p, const Context &context) override;
float64 x = -1; void unload() override;
bool ready() override;
bool readyInDefaultState() override;
private:
const EmojiPtr _emoji;
QImage _frame;
QPoint _shift;
};
class ScaledCustomEmoji final : public Ui::Text::CustomEmoji {
public:
ScaledCustomEmoji(std::unique_ptr<Ui::Text::CustomEmoji> wrapped);
int width() override;
QString entityData() override;
void paint(QPainter &p, const Context &context) override;
void unload() override;
bool ready() override;
bool readyInDefaultState() override;
private:
const std::unique_ptr<Ui::Text::CustomEmoji> _wrapped;
QImage _frame;
QPoint _shift;
};
[[nodiscard]] int ScaledSize() {
return st::dialogRowFilterTagStyle.font->height - 2 * st::lineWidth;
}
ScaledSimpleEmoji::ScaledSimpleEmoji(EmojiPtr emoji)
: _emoji(emoji) {
}
int ScaledSimpleEmoji::width() {
return ScaledSize();
}
QString ScaledSimpleEmoji::entityData() {
return u"scaled-simple:"_q + _emoji->text();
}
void ScaledSimpleEmoji::paint(QPainter &p, const Context &context) {
if (_frame.isNull()) {
const auto adjusted = Text::AdjustCustomEmojiSize(st::emojiSize);
const auto xskip = (st::emojiSize - adjusted) / 2;
const auto yskip = xskip + (width() - st::emojiSize) / 2;
_shift = { xskip, yskip };
const auto ratio = style::DevicePixelRatio();
const auto large = Emoji::GetSizeLarge();
const auto size = QSize(large, large);
_frame = QImage(size, QImage::Format_ARGB32_Premultiplied);
_frame.setDevicePixelRatio(ratio);
_frame.fill(Qt::transparent);
auto p = QPainter(&_frame);
Emoji::Draw(p, _emoji, large, 0, 0);
p.end();
_frame = _frame.scaled(
QSize(width(), width()) * ratio,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
p.drawImage(context.position - _shift, _frame);
}
void ScaledSimpleEmoji::unload() {
}
bool ScaledSimpleEmoji::ready() {
return true;
}
bool ScaledSimpleEmoji::readyInDefaultState() {
return true;
}
ScaledCustomEmoji::ScaledCustomEmoji(
std::unique_ptr<Ui::Text::CustomEmoji> wrapped)
: _wrapped(std::move(wrapped)) {
}
int ScaledCustomEmoji::width() {
return ScaledSize();
}
QString ScaledCustomEmoji::entityData() {
return u"scaled-custom:"_q + _wrapped->entityData();
}
void ScaledCustomEmoji::paint(QPainter &p, const Context &context) {
if (_frame.isNull()) {
if (!_wrapped->ready()) {
return;
}
const auto ratio = style::DevicePixelRatio();
const auto large = Emoji::GetSizeLarge();
const auto largeadjust = Text::AdjustCustomEmojiSize(large / ratio);
const auto size = QSize(largeadjust, largeadjust) * ratio;
_frame = QImage(size, QImage::Format_ARGB32_Premultiplied);
_frame.setDevicePixelRatio(ratio);
_frame.fill(Qt::transparent);
auto p = QPainter(&_frame);
p.translate(-context.position);
const auto was = context.internal.forceFirstFrame;
context.internal.forceFirstFrame = true;
_wrapped->paint(p, context);
context.internal.forceFirstFrame = was;
p.end();
const auto smalladjust = Text::AdjustCustomEmojiSize(width());
_frame = _frame.scaled(
QSize(smalladjust, smalladjust) * ratio,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
_wrapped->unload();
const auto adjusted = Text::AdjustCustomEmojiSize(st::emojiSize);
const auto xskip = (st::emojiSize - adjusted) / 2;
const auto yskip = xskip + (width() - st::emojiSize) / 2;
const auto add = (width() - smalladjust) / 2;
_shift = QPoint(xskip, yskip) - QPoint(add, add);
}
p.drawImage(context.position - _shift, _frame);
}
void ScaledCustomEmoji::unload() {
_wrapped->unload();
}
bool ScaledCustomEmoji::ready() {
return !_frame.isNull() || _wrapped->ready();
}
bool ScaledCustomEmoji::readyInDefaultState() {
return !_frame.isNull() || _wrapped->ready();
}
[[nodiscard]] TextWithEntities PrepareSmallEmojiText(
TextWithEntities text,
ChatsFilterTagContext &context) {
auto i = text.entities.begin();
auto ch = text.text.constData();
auto &integration = Integration::Instance();
context.loading = false;
const auto end = text.text.constData() + text.text.size();
const auto adjust = [&](EntityInText &entity) {
if (entity.type() != EntityType::CustomEmoji) {
return;
}
const auto data = entity.data();
if (data.startsWith(u"scaled-simple:"_q)) {
return;
}
auto &emoji = context.emoji[data];
if (!emoji) {
emoji = integration.createCustomEmoji(
data,
context.textContext);
}
if (!emoji->ready()) {
context.loading = true;
}
entity = EntityInText(
entity.type(),
entity.offset(),
entity.length(),
u"scaled-custom:"_q + entity.data());
};
const auto till = [](EntityInText &entity) {
return entity.offset() + entity.length();
}; };
auto emojiReplacements = std::vector<EmojiReplacement>();
auto ch = roundedText.constData();
const auto end = ch + roundedText.size();
while (ch != end) { while (ch != end) {
auto emojiLength = 0; auto emojiLength = 0;
if (const auto emoji = Ui::Emoji::Find(ch, end, &emojiLength)) { if (const auto emoji = Ui::Emoji::Find(ch, end, &emojiLength)) {
const auto factor = style::DevicePixelRatio(); const auto f = int(ch - text.text.constData());
emojiReplacements.push_back({ const auto l = f + emojiLength;
.pixmap = Ui::Emoji::SinglePixmap( while (i != text.entities.end() && till(*i) <= f) {
emoji, adjust(*i);
st::normalFont->height * factor).scaledToHeight( ++i;
roundedFont->ascent * factor, }
Qt::SmoothTransformation),
.from = int(ch - roundedText.constData()),
.length = emojiLength,
});
ch += emojiLength; ch += emojiLength;
if (i != text.entities.end() && i->offset() < l) {
continue;
}
i = text.entities.insert(i, EntityInText{
EntityType::CustomEmoji,
f,
emojiLength,
u"scaled-simple:"_q + emoji->text(),
});
} else { } else {
ch++; ++ch;
} }
} }
if (!emojiReplacements.empty()) { for (; i != text.entities.end(); ++i) {
auto addedChars = 0; adjust(*i);
for (auto &e : emojiReplacements) {
const auto pixmapWidth = e.pixmap.width()
/ style::DevicePixelRatio();
const auto spaces = 1 + pixmapWidth / roundedFont->spacew;
const auto placeholder = QString(spaces, ' ');
const auto from = e.from + addedChars;
e.x = roundedFont->width(roundedText.mid(0, from))
+ additionalWidth / 2.
+ (roundedFont->width(placeholder) - pixmapWidth) / 2.;
roundedText.replace(from, e.length, placeholder);
addedChars += spaces - e.length;
}
} }
const auto roundedWidth = roundedFont->width(roundedText) return text;
+ additionalWidth; }
} // namespace
QImage ChatsFilterTag(
const TextWithEntities &text,
ChatsFilterTagContext &context) {
const auto &roundedFont = st::dialogRowFilterTagStyle.font;
const auto additionalWidth = roundedFont->spacew * 3;
auto rich = Text::String(
st::dialogRowFilterTagStyle,
PrepareSmallEmojiText(text, context),
kMarkupTextOptions,
kQFixedMax,
context.textContext);
const auto roundedWidth = rich.maxWidth() + additionalWidth;
const auto rect = QRect(0, 0, roundedWidth, roundedFont->height); const auto rect = QRect(0, 0, roundedWidth, roundedFont->height);
auto cache = QImage( auto cache = QImage(
rect.size() * style::DevicePixelRatio(), rect.size() * style::DevicePixelRatio(),
@ -68,9 +255,11 @@ QImage ChatsFilterTag(QString roundedText, QColor color, bool active) {
cache.fill(Qt::transparent); cache.fill(Qt::transparent);
{ {
auto p = QPainter(&cache); auto p = QPainter(&cache);
const auto pen = QPen(active ? st::dialogsBgActive->c : color); const auto pen = QPen(context.active
? st::dialogsBgActive->c
: context.color);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.setBrush(active p.setBrush(context.active
? st::dialogsTextFgActive->c ? st::dialogsTextFgActive->c
: anim::with_alpha(pen.color(), .15)); : anim::with_alpha(pen.color(), .15));
{ {
@ -80,13 +269,23 @@ QImage ChatsFilterTag(QString roundedText, QColor color, bool active) {
} }
p.setPen(pen); p.setPen(pen);
p.setFont(roundedFont); p.setFont(roundedFont);
p.drawText(rect, roundedText, style::al_center); const auto dx = (rect.width() - rich.maxWidth()) / 2;
for (const auto &e : emojiReplacements) { const auto dy = (rect.height() - roundedFont->height) / 2;
const auto h = e.pixmap.height() / style::DevicePixelRatio(); rich.draw(p, {
p.drawPixmap(QPointF(e.x, (rect.height() - h) / 2), e.pixmap); .position = rect.topLeft() + QPoint(dx, dy),
} .availableWidth = rich.maxWidth(),
});
} }
return cache; return cache;
} }
std::unique_ptr<Text::CustomEmoji> MakeScaledSimpleEmoji(EmojiPtr emoji) {
return std::make_unique<ScaledSimpleEmoji>(emoji);
}
std::unique_ptr<Text::CustomEmoji> MakeScaledCustomEmoji(
std::unique_ptr<Text::CustomEmoji> wrapped) {
return std::make_unique<ScaledCustomEmoji>(std::move(wrapped));
}
} // namespace Ui } // namespace Ui

View file

@ -7,8 +7,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "emoji.h"
namespace Ui::Text {
class CustomEmoji;
} // namespace Ui::Text
namespace Ui { namespace Ui {
[[nodiscard]] QImage ChatsFilterTag(QString text, QColor color, bool active); struct ChatsFilterTagContext {
base::flat_map<QString, std::unique_ptr<Text::CustomEmoji>> emoji;
std::any textContext;
QColor color;
bool active = false;
bool loading = false;
};
[[nodiscard]] QImage ChatsFilterTag(
const TextWithEntities &text,
ChatsFilterTagContext &context);
[[nodiscard]] std::unique_ptr<Text::CustomEmoji> MakeScaledSimpleEmoji(
EmojiPtr emoji);
[[nodiscard]] std::unique_ptr<Text::CustomEmoji> MakeScaledCustomEmoji(
std::unique_ptr<Text::CustomEmoji> wrapped);
} // namespace Ui } // namespace Ui

@ -1 +1 @@
Subproject commit c1ea8aef5073785ce6e35db9eda830604f81ed62 Subproject commit 8cb06a75d981d7a1a2f2a5df420ef20ff4c0b097