@@ -13,7 +13,12 @@ namespace chatterino {
|
|||||||
|
|
||||||
struct EmojiData {
|
struct EmojiData {
|
||||||
QString value;
|
QString value;
|
||||||
|
|
||||||
|
// what's used in the emoji-one url
|
||||||
QString code;
|
QString code;
|
||||||
|
|
||||||
|
// i.e. thinking
|
||||||
|
QString shortCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Emojis
|
class Emojis
|
||||||
|
|||||||
+75
-47
@@ -188,74 +188,102 @@ void EmoteManager::loadEmojis()
|
|||||||
EmojiData emojiData{
|
EmojiData emojiData{
|
||||||
QString::fromUcs4(unicodeBytes, numUnicodeBytes), //
|
QString::fromUcs4(unicodeBytes, numUnicodeBytes), //
|
||||||
code, //
|
code, //
|
||||||
|
shortCode, //
|
||||||
};
|
};
|
||||||
|
|
||||||
shortCodeToEmoji.insert(shortCode, emojiData);
|
this->emojiShortCodeToEmoji.insert(shortCode, emojiData);
|
||||||
emojiToShortCode.insert(emojiData.value, shortCode);
|
|
||||||
|
this->emojiFirstByte[emojiData.value.at(0)].append(emojiData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
for (auto const &emoji : shortCodeToEmoji.toStdMap()) {
|
|
||||||
auto iter = firstEmojiChars.find(emoji.first.at(0));
|
|
||||||
|
|
||||||
if (iter != firstEmojiChars.end()) {
|
|
||||||
iter.value().insert(emoji.second.value, emoji.second.value);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstEmojiChars.insert(emoji.first.at(0),
|
|
||||||
QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmoteManager::parseEmojis(
|
void EmoteManager::parseEmojis(
|
||||||
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector, const QString &text)
|
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &parsedWords, const QString &text)
|
||||||
{
|
{
|
||||||
// TODO(pajlada): Add this method to EmoteManager instead
|
int lastParsedEmojiEndIndex = 0;
|
||||||
long lastSlice = 0;
|
|
||||||
|
|
||||||
for (auto i = 0; i < text.length() - 1; i++) {
|
for (auto i = 0; i < text.length() - 1; i++) {
|
||||||
if (!text.at(i).isLowSurrogate()) {
|
const QChar character = text.at(i);
|
||||||
auto iter = firstEmojiChars.find(text.at(i));
|
|
||||||
|
|
||||||
if (iter != firstEmojiChars.end()) {
|
if (character.isLowSurrogate()) {
|
||||||
for (auto j = std::min(8, text.length() - i); j > 0; j--) {
|
continue;
|
||||||
QString emojiString = text.mid(i, 2);
|
}
|
||||||
auto emojiIter = iter.value().find(emojiString);
|
|
||||||
|
|
||||||
if (emojiIter != iter.value().end()) {
|
auto it = this->emojiFirstByte.find(character);
|
||||||
QString url = "https://cdnjs.cloudflare.com/ajax/libs/"
|
if (it == this->emojiFirstByte.end()) {
|
||||||
"emojione/2.2.6/assets/png/" +
|
// No emoji starts with this character
|
||||||
emojiIter.value() + ".png";
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (i - lastSlice != 0) {
|
const QVector<EmojiData> possibleEmojis = it.value();
|
||||||
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
|
||||||
nullptr, text.mid(lastSlice, i - lastSlice)));
|
|
||||||
}
|
|
||||||
|
|
||||||
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
int remainingCharacters = text.length() - i;
|
||||||
emojis.getOrAdd(url,
|
|
||||||
[this, &url] {
|
|
||||||
return new LazyLoadedImage(
|
|
||||||
*this, this->windowManager, url, 0.35); //
|
|
||||||
}),
|
|
||||||
QString()));
|
|
||||||
|
|
||||||
i += j - 1;
|
EmojiData matchedEmoji;
|
||||||
|
|
||||||
lastSlice = i + 1;
|
int matchedEmojiLength = 0;
|
||||||
|
|
||||||
break;
|
for (const EmojiData &emoji : possibleEmojis) {
|
||||||
}
|
if (remainingCharacters < emoji.value.length()) {
|
||||||
|
// It cannot be this emoji, there's not enough space for it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match = true;
|
||||||
|
|
||||||
|
for (int j = 1; j < emoji.value.length(); ++j) {
|
||||||
|
if (text.at(i + j) != emoji.value.at(j)) {
|
||||||
|
match = false;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
matchedEmoji = emoji;
|
||||||
|
matchedEmojiLength = emoji.value.length();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matchedEmojiLength == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentParsedEmojiFirstIndex = i;
|
||||||
|
int currentParsedEmojiEndIndex = i + (matchedEmojiLength);
|
||||||
|
|
||||||
|
int charactersFromLastParsedEmoji = currentParsedEmojiFirstIndex - lastParsedEmojiEndIndex;
|
||||||
|
|
||||||
|
if (charactersFromLastParsedEmoji > 0) {
|
||||||
|
// Add characters inbetween emojis
|
||||||
|
parsedWords.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||||
|
nullptr, text.mid(lastParsedEmojiEndIndex, charactersFromLastParsedEmoji)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString url = "https://cdnjs.cloudflare.com/ajax/libs/"
|
||||||
|
"emojione/2.2.6/assets/png/" +
|
||||||
|
matchedEmoji.code + ".png";
|
||||||
|
|
||||||
|
// Create or fetch cached emoji image
|
||||||
|
auto emojiImage = this->emojiCache.getOrAdd(url, [this, &url] {
|
||||||
|
return new LazyLoadedImage(*this, this->windowManager, url, 0.35); //
|
||||||
|
});
|
||||||
|
|
||||||
|
// Push the emoji as a word to parsedWords
|
||||||
|
parsedWords.push_back(
|
||||||
|
std::tuple<messages::LazyLoadedImage *, QString>(emojiImage, QString()));
|
||||||
|
|
||||||
|
lastParsedEmojiEndIndex = currentParsedEmojiEndIndex;
|
||||||
|
|
||||||
|
i += matchedEmojiLength - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastSlice < text.length()) {
|
if (lastParsedEmojiEndIndex < text.length()) {
|
||||||
vector.push_back(
|
// Add remaining characters
|
||||||
std::tuple<messages::LazyLoadedImage *, QString>(nullptr, text.mid(lastSlice)));
|
parsedWords.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||||
|
nullptr, text.mid(lastParsedEmojiEndIndex)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-14
@@ -62,44 +62,42 @@ private:
|
|||||||
WindowManager &windowManager;
|
WindowManager &windowManager;
|
||||||
Resources &resources;
|
Resources &resources;
|
||||||
|
|
||||||
// Emojis
|
/// Emojis
|
||||||
// shortCodeToEmoji maps strings like ":sunglasses:" to the unicode character
|
// shortCodeToEmoji maps strings like "sunglasses" to its emoji
|
||||||
QMap<QString, EmojiData> shortCodeToEmoji;
|
QMap<QString, EmojiData> emojiShortCodeToEmoji;
|
||||||
|
|
||||||
// emojiToShortCode maps the unicode character to the shortcode like ":sunglasses:"
|
// Maps the first character of the emoji unicode string to a vector of possible emojis
|
||||||
QMap<QString, QString> emojiToShortCode;
|
QMap<QChar, QVector<EmojiData>> emojiFirstByte;
|
||||||
|
|
||||||
// TODO(pajlada): Figure out what this is for
|
// url Emoji-one image
|
||||||
QMap<QChar, QMap<QString, QString>> firstEmojiChars;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> emojiCache;
|
||||||
|
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> emojis;
|
|
||||||
|
|
||||||
void loadEmojis();
|
void loadEmojis();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
void parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &parsedWords,
|
||||||
const QString &text);
|
const QString &text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Twitch emotes
|
/// Twitch emotes
|
||||||
ConcurrentMap<QString, twitch::EmoteValue *> _twitchEmotes;
|
ConcurrentMap<QString, twitch::EmoteValue *> _twitchEmotes;
|
||||||
ConcurrentMap<long, messages::LazyLoadedImage *> _twitchEmoteFromCache;
|
ConcurrentMap<long, messages::LazyLoadedImage *> _twitchEmoteFromCache;
|
||||||
|
|
||||||
// BTTV emotes
|
/// BTTV emotes
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> bttvChannelEmotes;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> bttvChannelEmotes;
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvEmotes;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvEmotes;
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvChannelEmoteFromCaches;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvChannelEmoteFromCaches;
|
||||||
|
|
||||||
void loadBTTVEmotes();
|
void loadBTTVEmotes();
|
||||||
|
|
||||||
// FFZ emotes
|
/// FFZ emotes
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> ffzChannelEmotes;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> ffzChannelEmotes;
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> _ffzEmotes;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _ffzEmotes;
|
||||||
ConcurrentMap<int, messages::LazyLoadedImage *> _ffzChannelEmoteFromCaches;
|
ConcurrentMap<int, messages::LazyLoadedImage *> _ffzChannelEmoteFromCaches;
|
||||||
|
|
||||||
void loadFFZEmotes();
|
void loadFFZEmotes();
|
||||||
|
|
||||||
// Chatterino emotes
|
/// Chatterino emotes
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> _chatterinoEmotes;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _chatterinoEmotes;
|
||||||
|
|
||||||
// ???
|
// ???
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "twitch/twitchmessagebuilder.hpp"
|
#include "twitch/twitchmessagebuilder.hpp"
|
||||||
#include "colorscheme.hpp"
|
#include "colorscheme.hpp"
|
||||||
#include "emojis.hpp"
|
|
||||||
#include "emotemanager.hpp"
|
#include "emotemanager.hpp"
|
||||||
#include "ircmanager.hpp"
|
#include "ircmanager.hpp"
|
||||||
#include "resources.hpp"
|
#include "resources.hpp"
|
||||||
@@ -190,6 +189,7 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
|
|||||||
// split words
|
// split words
|
||||||
std::vector<std::tuple<LazyLoadedImage *, QString>> parsed;
|
std::vector<std::tuple<LazyLoadedImage *, QString>> parsed;
|
||||||
|
|
||||||
|
// Parse emojis and take all non-emojis and put them in parsed as full text-words
|
||||||
emoteManager.parseEmojis(parsed, split);
|
emoteManager.parseEmojis(parsed, split);
|
||||||
|
|
||||||
for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) {
|
for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) {
|
||||||
|
|||||||
Reference in New Issue
Block a user