Provide custom emoji factory through MarkedTextContext.
This commit is contained in:
parent
bbfcac26c9
commit
21aa1323ec
|
@ -419,6 +419,8 @@ PRIVATE
|
||||||
data/notify/data_notify_settings.h
|
data/notify/data_notify_settings.h
|
||||||
data/notify/data_peer_notify_settings.cpp
|
data/notify/data_peer_notify_settings.cpp
|
||||||
data/notify/data_peer_notify_settings.h
|
data/notify/data_peer_notify_settings.h
|
||||||
|
data/stickers/data_custom_emoji.cpp
|
||||||
|
data/stickers/data_custom_emoji.h
|
||||||
data/stickers/data_stickers_set.cpp
|
data/stickers/data_stickers_set.cpp
|
||||||
data/stickers/data_stickers_set.h
|
data/stickers/data_stickers_set.h
|
||||||
data/stickers/data_stickers.cpp
|
data/stickers/data_stickers.cpp
|
||||||
|
|
|
@ -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_user.h"
|
#include "data/data_user.h"
|
||||||
|
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
|
@ -142,28 +142,32 @@ void EmojiPack::remove(not_null<const HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EmojiPack::stickerForEmoji(const IsolatedEmoji &emoji) -> Sticker {
|
auto EmojiPack::stickerForEmoji(EmojiPtr emoji) -> Sticker {
|
||||||
Expects(!emoji.empty());
|
Expects(emoji != nullptr);
|
||||||
|
|
||||||
if (emoji.items[1] != nullptr) {
|
const auto i = _map.find(emoji);
|
||||||
return Sticker();
|
|
||||||
}
|
|
||||||
const auto first = emoji.items[0];
|
|
||||||
const auto i = _map.find(first);
|
|
||||||
if (i != end(_map)) {
|
if (i != end(_map)) {
|
||||||
return { i->second.get(), nullptr };
|
return { i->second.get(), nullptr };
|
||||||
}
|
}
|
||||||
if (!first->colored()) {
|
if (!emoji->colored()) {
|
||||||
return Sticker();
|
return Sticker();
|
||||||
}
|
}
|
||||||
const auto j = _map.find(first->original());
|
const auto j = _map.find(emoji->original());
|
||||||
if (j != end(_map)) {
|
if (j != end(_map)) {
|
||||||
const auto index = first->variantIndex(first);
|
const auto index = emoji->variantIndex(emoji);
|
||||||
return { j->second.get(), ColorReplacements(index) };
|
return { j->second.get(), ColorReplacements(index) };
|
||||||
}
|
}
|
||||||
return Sticker();
|
return Sticker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto EmojiPack::stickerForEmoji(const IsolatedEmoji &emoji) -> Sticker {
|
||||||
|
Expects(!emoji.empty());
|
||||||
|
|
||||||
|
return (emoji.items[1] != nullptr)
|
||||||
|
? Sticker()
|
||||||
|
: stickerForEmoji(emoji.items[0]);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<LargeEmojiImage> EmojiPack::image(EmojiPtr emoji) {
|
std::shared_ptr<LargeEmojiImage> EmojiPack::image(EmojiPtr emoji) {
|
||||||
const auto i = _images.emplace(
|
const auto i = _images.emplace(
|
||||||
emoji,
|
emoji,
|
||||||
|
|
|
@ -66,6 +66,7 @@ public:
|
||||||
bool add(not_null<HistoryItem*> item);
|
bool add(not_null<HistoryItem*> item);
|
||||||
void remove(not_null<const HistoryItem*> item);
|
void remove(not_null<const HistoryItem*> item);
|
||||||
|
|
||||||
|
[[nodiscard]] Sticker stickerForEmoji(EmojiPtr emoji);
|
||||||
[[nodiscard]] Sticker stickerForEmoji(const IsolatedEmoji &emoji);
|
[[nodiscard]] Sticker stickerForEmoji(const IsolatedEmoji &emoji);
|
||||||
[[nodiscard]] std::shared_ptr<LargeEmojiImage> image(EmojiPtr emoji);
|
[[nodiscard]] std::shared_ptr<LargeEmojiImage> image(EmojiPtr emoji);
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "core/ui_integration.h"
|
#include "core/ui_integration.h"
|
||||||
|
|
||||||
|
#include "api/api_text_entities.h"
|
||||||
#include "core/local_url_handlers.h"
|
#include "core/local_url_handlers.h"
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/sandbox.h"
|
#include "core/sandbox.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
#include "ui/basic_click_handlers.h"
|
#include "ui/basic_click_handlers.h"
|
||||||
#include "ui/emoji_config.h"
|
#include "ui/emoji_config.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -243,7 +246,10 @@ std::unique_ptr<Ui::Text::CustomEmoji> UiIntegration::createCustomEmoji(
|
||||||
const QString &data,
|
const QString &data,
|
||||||
const std::any &context) {
|
const std::any &context) {
|
||||||
const auto my = std::any_cast<MarkedTextContext>(&context);
|
const auto my = std::any_cast<MarkedTextContext>(&context);
|
||||||
return nullptr;
|
if (!my || !my->session) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return my->session->data().customEmojiManager().create(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<> UiIntegration::forcePopupMenuHideRequests() {
|
rpl::producer<> UiIntegration::forcePopupMenuHideRequests() {
|
||||||
|
|
|
@ -65,6 +65,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
#include "data/data_peer_values.h"
|
#include "data/data_peer_values.h"
|
||||||
#include "data/data_premium_limits.h"
|
#include "data/data_premium_limits.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "base/call_delayed.h"
|
#include "base/call_delayed.h"
|
||||||
|
@ -246,7 +247,8 @@ Session::Session(not_null<Main::Session*> session)
|
||||||
, _stickers(std::make_unique<Stickers>(this))
|
, _stickers(std::make_unique<Stickers>(this))
|
||||||
, _sponsoredMessages(std::make_unique<SponsoredMessages>(this))
|
, _sponsoredMessages(std::make_unique<SponsoredMessages>(this))
|
||||||
, _reactions(std::make_unique<Reactions>(this))
|
, _reactions(std::make_unique<Reactions>(this))
|
||||||
, _notifySettings(std::make_unique<NotifySettings>(this)) {
|
, _notifySettings(std::make_unique<NotifySettings>(this))
|
||||||
|
, _customEmojiManager(std::make_unique<CustomEmojiManager>(this)) {
|
||||||
_cache->open(_session->local().cacheKey());
|
_cache->open(_session->local().cacheKey());
|
||||||
_bigFileCache->open(_session->local().cacheBigFileKey());
|
_bigFileCache->open(_session->local().cacheBigFileKey());
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ class PhotoMedia;
|
||||||
class Stickers;
|
class Stickers;
|
||||||
class GroupCall;
|
class GroupCall;
|
||||||
class NotifySettings;
|
class NotifySettings;
|
||||||
|
class CustomEmojiManager;
|
||||||
|
|
||||||
class Session final {
|
class Session final {
|
||||||
public:
|
public:
|
||||||
|
@ -120,6 +121,9 @@ public:
|
||||||
[[nodiscard]] NotifySettings ¬ifySettings() const {
|
[[nodiscard]] NotifySettings ¬ifySettings() const {
|
||||||
return *_notifySettings;
|
return *_notifySettings;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] CustomEmojiManager &customEmojiManager() const {
|
||||||
|
return *_customEmojiManager;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] MsgId nextNonHistoryEntryId() {
|
[[nodiscard]] MsgId nextNonHistoryEntryId() {
|
||||||
return ++_nonHistoryEntryId;
|
return ++_nonHistoryEntryId;
|
||||||
|
@ -978,6 +982,7 @@ private:
|
||||||
std::unique_ptr<SponsoredMessages> _sponsoredMessages;
|
std::unique_ptr<SponsoredMessages> _sponsoredMessages;
|
||||||
const std::unique_ptr<Reactions> _reactions;
|
const std::unique_ptr<Reactions> _reactions;
|
||||||
const std::unique_ptr<NotifySettings> _notifySettings;
|
const std::unique_ptr<NotifySettings> _notifySettings;
|
||||||
|
const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
|
||||||
|
|
||||||
MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
|
MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
|
||||||
|
#include "chat_helpers/stickers_emoji_pack.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "ui/text/text_block.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct CustomEmojiId {
|
||||||
|
StickerSetIdentifier set;
|
||||||
|
uint64 id = 0;
|
||||||
|
};
|
||||||
|
[[nodiscard]] QString SerializeCustomEmojiId(const CustomEmojiId &id) {
|
||||||
|
const auto &info = id.set;
|
||||||
|
const auto set = info.id
|
||||||
|
? (QString::number(info.id) + ':' + QString::number(info.accessHash))
|
||||||
|
: info.shortName;
|
||||||
|
return QString::number(id.id) + '@' + set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] CustomEmojiId ParseCustomEmojiData(const QString &data) {
|
||||||
|
const auto parts = data.split('@');
|
||||||
|
if (parts.size() != 2) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto id = parts[0].toULongLong();
|
||||||
|
if (!id) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto second = parts[1].split(':');
|
||||||
|
if (const auto set = second[0].toULongLong()) {
|
||||||
|
return {
|
||||||
|
.set = {.id = set, .accessHash = second[1].toULongLong() },
|
||||||
|
.id = id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.set = {.shortName = second[1] },
|
||||||
|
.id = id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomEmojiWithData : public Ui::Text::CustomEmoji {
|
||||||
|
public:
|
||||||
|
explicit CustomEmojiWithData(const QString &data);
|
||||||
|
|
||||||
|
QString entityData() final override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString _data;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
CustomEmojiWithData::CustomEmojiWithData(const QString &data) : _data(data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CustomEmojiWithData::entityData() {
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocumentCustomEmoji final : public CustomEmojiWithData {
|
||||||
|
public:
|
||||||
|
DocumentCustomEmoji(
|
||||||
|
const QString &data,
|
||||||
|
not_null<DocumentData*> document);
|
||||||
|
|
||||||
|
void paint(QPainter &p, int x, int y) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
not_null<DocumentData*> _document;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
DocumentCustomEmoji::DocumentCustomEmoji(
|
||||||
|
const QString &data,
|
||||||
|
not_null<DocumentData*> document)
|
||||||
|
: CustomEmojiWithData(data)
|
||||||
|
, _document(document) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentCustomEmoji::paint(QPainter &p, int x, int y) {
|
||||||
|
const auto size = Ui::Emoji::GetSizeNormal() / style::DevicePixelRatio();
|
||||||
|
p.fillRect(QRect{ x, y, size, size }, Qt::red);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResolvingCustomEmoji final : public CustomEmojiWithData {
|
||||||
|
public:
|
||||||
|
explicit ResolvingCustomEmoji(const QString &data);
|
||||||
|
|
||||||
|
void paint(QPainter &p, int x, int y) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<DocumentCustomEmoji> _resolved;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ResolvingCustomEmoji::ResolvingCustomEmoji(const QString &data)
|
||||||
|
: CustomEmojiWithData(data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingCustomEmoji::paint(QPainter &p, int x, int y) {
|
||||||
|
if (_resolved) {
|
||||||
|
_resolved->paint(p, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
CustomEmojiManager::CustomEmojiManager(not_null<Session*> owner)
|
||||||
|
: _owner(owner) {
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomEmojiManager::~CustomEmojiManager() = default;
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||||
|
const QString &data) {
|
||||||
|
const auto parsed = ParseCustomEmojiData(data);
|
||||||
|
if (!parsed.id) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto document = _owner->document(parsed.id);
|
||||||
|
if (!document->isNull()) {
|
||||||
|
return std::make_unique<DocumentCustomEmoji>(data, document);
|
||||||
|
}
|
||||||
|
return std::make_unique<ResolvingCustomEmoji>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Main::Session &CustomEmojiManager::session() const {
|
||||||
|
return _owner->session();
|
||||||
|
}
|
||||||
|
|
||||||
|
Session &CustomEmojiManager::owner() const {
|
||||||
|
return *_owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillTestCustomEmoji(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
TextWithEntities &text) {
|
||||||
|
const auto pack = &session->emojiStickersPack();
|
||||||
|
const auto begin = text.text.constData(), end = begin + text.text.size();
|
||||||
|
for (auto ch = begin; ch != end;) {
|
||||||
|
auto length = 0;
|
||||||
|
if (const auto emoji = Ui::Emoji::Find(ch, end, &length)) {
|
||||||
|
if (const auto found = pack->stickerForEmoji(emoji)) {
|
||||||
|
Expects(found.document->sticker() != nullptr);
|
||||||
|
|
||||||
|
text.entities.push_back({
|
||||||
|
EntityType::CustomEmoji,
|
||||||
|
(ch - begin),
|
||||||
|
length,
|
||||||
|
SerializeCustomEmojiId({
|
||||||
|
found.document->sticker()->set,
|
||||||
|
found.document->id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ch += length;
|
||||||
|
} else if (ch->isHighSurrogate()
|
||||||
|
&& (ch + 1 != end)
|
||||||
|
&& (ch + 1)->isLowSurrogate()) {
|
||||||
|
ch += 2;
|
||||||
|
} else {
|
||||||
|
++ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ranges::stable_sort(
|
||||||
|
text.entities,
|
||||||
|
ranges::less(),
|
||||||
|
&EntityInText::offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Data
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
class Session;
|
||||||
|
|
||||||
|
class CustomEmojiManager final {
|
||||||
|
public:
|
||||||
|
CustomEmojiManager(not_null<Session*> owner);
|
||||||
|
~CustomEmojiManager();
|
||||||
|
|
||||||
|
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create(
|
||||||
|
const QString &data);
|
||||||
|
|
||||||
|
[[nodiscard]] Main::Session &session() const;
|
||||||
|
[[nodiscard]] Session &owner() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<Session*> _owner;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void FillTestCustomEmoji(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
TextWithEntities &text);
|
||||||
|
|
||||||
|
} // namespace Data
|
|
@ -42,6 +42,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
|
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] MessageFlags NewForwardedFlags(
|
[[nodiscard]] MessageFlags NewForwardedFlags(
|
||||||
|
@ -329,12 +331,13 @@ HistoryMessage::HistoryMessage(
|
||||||
if (const auto media = data.vmedia()) {
|
if (const auto media = data.vmedia()) {
|
||||||
setMedia(*media);
|
setMedia(*media);
|
||||||
}
|
}
|
||||||
const auto textWithEntities = TextWithEntities{
|
auto textWithEntities = TextWithEntities{
|
||||||
qs(data.vmessage()),
|
qs(data.vmessage()),
|
||||||
Api::EntitiesFromMTP(
|
Api::EntitiesFromMTP(
|
||||||
&history->session(),
|
&history->session(),
|
||||||
data.ventities().value_or_empty())
|
data.ventities().value_or_empty())
|
||||||
};
|
};
|
||||||
|
Data::FillTestCustomEmoji(&history->session(), textWithEntities);
|
||||||
setText(_media ? textWithEntities : EnsureNonEmpty(textWithEntities));
|
setText(_media ? textWithEntities : EnsureNonEmpty(textWithEntities));
|
||||||
if (const auto groupedId = data.vgrouped_id()) {
|
if (const auto groupedId = data.vgrouped_id()) {
|
||||||
setGroupId(
|
setGroupId(
|
||||||
|
|
Loading…
Reference in New Issue