diff --git a/CHANGELOG.md b/CHANGELOG.md index 52df34528ac..29e627f7316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ - Bugfix: Fixed an issue where the setting `Only search for emote autocompletion at the start of emote names` wouldn't disable if it was enabled when the client started. (#4855) - Bugfix: Fixed empty page being added when showing out of bounds dialog. (#4849) - Bugfix: Fixed issue on Windows preventing the title bar from being dragged in the top left corner. (#4873) -- Bugfix: Fixed an issue where reply context didn't render correctly if an emoji was touching text. (#4875) +- Bugfix: Fixed an issue where reply context didn't render correctly if an emoji was touching text. (#4875, #4977) - Bugfix: Fixed the input completion popup from disappearing when clicking on it on Windows and macOS. (#4876) - Bugfix: Fixed double-click text selection moving its position with each new message. (#4898) - Bugfix: Fixed an issue where notifications on Windows would contain no or an old avatar. (#4899) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a9a5756569..a7bb6dc5be9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -929,7 +929,7 @@ if (CHATTERINO_DEBUG_NATIVE_MESSAGES) endif () if (MSVC) - target_compile_options(${LIBRARY_PROJECT} PUBLIC /EHsc /bigobj) + target_compile_options(${LIBRARY_PROJECT} PUBLIC /EHsc /bigobj /utf-8) endif () if (APPLE AND BUILD_APP) diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index 638b893f4af..6c60088a165 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -664,16 +664,23 @@ void SingleLineTextElement::addToContainer(MessageLayoutContainer &container, QString currentText; container.first = FirstWord::Neutral; + + bool firstIteration = true; for (Word &word : this->words_) { + if (firstIteration) + { + firstIteration = false; + } + else + { + currentText += ' '; + } + for (const auto &parsedWord : app->emotes->emojis.parse(word.text)) { if (parsedWord.type() == typeid(QString)) { - if (!currentText.isEmpty()) - { - currentText += ' '; - } currentText += boost::get(parsedWord); QString prev = currentText; // only increments the ref-count @@ -714,7 +721,8 @@ void SingleLineTextElement::addToContainer(MessageLayoutContainer &container, container.addElementNoLineBreak( (new ImageLayoutElement(*this, image, emoteSize)) - ->setLink(this->getLink())); + ->setLink(this->getLink()) + ->setTrailingSpace(false)); } } } @@ -723,9 +731,6 @@ void SingleLineTextElement::addToContainer(MessageLayoutContainer &container, // Add the last of the pending message text to the container. if (!currentText.isEmpty()) { - // Remove trailing space. - currentText = currentText.trimmed(); - int width = metrics.horizontalAdvance(currentText); container.addElementNoLineBreak( getTextLayoutElement(currentText, width, false)); diff --git a/src/providers/emoji/Emojis.cpp b/src/providers/emoji/Emojis.cpp index f0a0f14d9f1..57107ddc1e6 100644 --- a/src/providers/emoji/Emojis.cpp +++ b/src/providers/emoji/Emojis.cpp @@ -152,6 +152,12 @@ namespace chatterino { void Emojis::load() { + if (this->loaded_) + { + return; + } + this->loaded_ = true; + this->loadEmojis(); this->sortEmojis(); diff --git a/src/providers/emoji/Emojis.hpp b/src/providers/emoji/Emojis.hpp index d0f21c862c9..deb585e3d1a 100644 --- a/src/providers/emoji/Emojis.hpp +++ b/src/providers/emoji/Emojis.hpp @@ -80,6 +80,8 @@ class Emojis : public IEmojis // Maps the first character of the emoji unicode string to a vector of // possible emojis QMap>> emojiFirstByte_; + + bool loaded_ = false; }; } // namespace chatterino diff --git a/tests/src/Emojis.cpp b/tests/src/Emojis.cpp index ed303ab290d..f984538f824 100644 --- a/tests/src/Emojis.cpp +++ b/tests/src/Emojis.cpp @@ -1,10 +1,13 @@ #include "providers/emoji/Emojis.hpp" +#include "common/Literals.hpp" + #include #include #include using namespace chatterino; +using namespace literals; TEST(Emojis, ShortcodeParsing) { @@ -53,3 +56,110 @@ TEST(Emojis, ShortcodeParsing) << "Input " << test.input.toStdString() << " failed"; } } + +TEST(Emojis, Parse) +{ + Emojis emojis; + + emojis.load(); + + struct TestCase { + QString input; + std::vector> expectedOutput; + }; + + auto getEmoji = [&](auto code) { + std::shared_ptr emoji; + emojis.getEmojis().tryGet(code, emoji); + return emoji->emote; + }; + + auto penguin = getEmoji("1F427"); + auto cool = getEmoji("1F192"); + auto skinTone6 = getEmoji("1F3FF"); + auto england = getEmoji("1F3F4-E0067-E0062-E0065-E006E-E0067-E007F"); + auto womanRunningtone2 = getEmoji("1F3C3-1F3FC-200D-2640-FE0F"); + auto kissWomanManTone1 = + getEmoji("1F468-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB"); + auto heavyEqualsSign = getEmoji("1F7F0"); + auto coupleKissTone1Tone2 = + getEmoji("1F9D1-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FC"); + auto hearHands = getEmoji("1FAF6"); + + const std::vector tests{ + { + "abc", + {"abc"}, + }, + { + "abc def", + {"abc def"}, + }, + { + "abc🐧def", + {"abc", penguin, "def"}, + }, + { + "abc 🐧def", + {"abc ", penguin, "def"}, + }, + { + " abc🐧 def ", + {" abc", penguin, " def "}, + }, + { + "🐧", + {penguin}, + }, + { + "🐧🐧🐧🐧", + {penguin, penguin, penguin, penguin}, + }, + { + // england + u"\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F"_s + // cool + "\U0001F192" + // skin tone 6 + "\U0001F3FF" + // woman running tone2 + "\U0001F3C3\U0001F3FC\u200D\u2640\uFE0F" + // [running] non-qualified + "\U0001F3C3\U0001F3FC\u200D\u2640", + {england, cool, skinTone6, womanRunningtone2, womanRunningtone2}, + }, + { + // kiss woman tone1 man tone 1 + u"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB"_s + // [kiss] non-qualified + "\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D" + "\U0001F468" + "\U0001F3FB" + // heavy equals sign + "\U0001F7F0", + {kissWomanManTone1, kissWomanManTone1, heavyEqualsSign}, + }, + { + // couple kiss tone 1, tone 2 + u"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC"_s + // [kiss] non-qualified + "\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1" + "\U0001F3FC" + // heart hands + "\U0001FAF6", + {coupleKissTone1Tone2, coupleKissTone1Tone2, hearHands}, + }, + }; + + for (const auto &test : tests) + { + auto output = emojis.parse(test.input); + + // can't use EXPECT_EQ because EmotePtr can't be printed + if (output != test.expectedOutput) + { + EXPECT_TRUE(false) + << "Input " << test.input.toStdString() << " failed"; + } + } +}