From fe4d6121a235aabe0d2064e4d32b3b709488c3bf Mon Sep 17 00:00:00 2001 From: nerix Date: Sun, 8 Oct 2023 12:09:42 +0200 Subject: [PATCH] Display all parsed elements when parsing emojis in replies (#4875) --- CHANGELOG.md | 1 + src/CMakeLists.txt | 1 + src/messages/MessageElement.cpp | 86 +++++++++++++++++---------------- src/util/Variant.hpp | 28 +++++++++++ 4 files changed, 74 insertions(+), 42 deletions(-) create mode 100644 src/util/Variant.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 824490f0b..f862b26ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Bugfix: Fixed too much text being copied when copying chat messages. (#4812, #4830, #4839) - 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 the input completion popup from disappearing when clicking on it on Windows and macOS. (#4876) - Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791) - Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ea5cb930..3165cd1fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -458,6 +458,7 @@ set(SOURCE_FILES util/Twitch.cpp util/Twitch.hpp util/TypeName.hpp + util/Variant.hpp util/WidgetHelpers.cpp util/WidgetHelpers.hpp util/WindowsHelper.cpp diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index f69d0b785..55503d27b 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -12,6 +12,7 @@ #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "util/DebugCount.hpp" +#include "util/Variant.hpp" namespace chatterino { @@ -665,55 +666,56 @@ void SingleLineTextElement::addToContainer(MessageLayoutContainer &container, container.first = FirstWord::Neutral; for (Word &word : this->words_) { - auto parsedWords = app->emotes->emojis.parse(word.text); - if (parsedWords.size() == 0) + for (const auto &parsedWord : app->emotes->emojis.parse(word.text)) { - continue; // sanity check - } - - auto &parsedWord = parsedWords[0]; - if (parsedWord.type() == typeid(QString)) - { - if (!currentText.isEmpty()) + if (parsedWord.type() == typeid(QString)) { - currentText += ' '; - } - currentText += word.text; - QString prev = currentText; // only increments the ref-count - currentText = metrics.elidedText(currentText, Qt::ElideRight, - container.remainingWidth()); - if (currentText != prev) - { - break; - } - } - else if (parsedWord.type() == typeid(EmotePtr)) - { - auto emote = boost::get(parsedWord); - auto image = - emote->images.getImageOrLoaded(container.getScale()); - if (!image->isEmpty()) - { - auto emoteScale = getSettings()->emoteScale.getValue(); - - int currentWidth = metrics.horizontalAdvance(currentText); - auto emoteSize = QSize(image->width(), image->height()) * - (emoteScale * container.getScale()); - - if (!container.fitsInLine(currentWidth + emoteSize.width())) + if (!currentText.isEmpty()) + { + currentText += ' '; + } + currentText += boost::get(parsedWord); + QString prev = + currentText; // only increments the ref-count + currentText = + metrics.elidedText(currentText, Qt::ElideRight, + container.remainingWidth()); + if (currentText != prev) { - currentText += ellipsis; break; } + } + else if (parsedWord.type() == typeid(EmotePtr)) + { + auto emote = boost::get(parsedWord); + auto image = + emote->images.getImageOrLoaded(container.getScale()); + if (!image->isEmpty()) + { + auto emoteScale = getSettings()->emoteScale.getValue(); - // Add currently pending text to container, then add the emote after. - container.addElementNoLineBreak( - getTextLayoutElement(currentText, currentWidth, false)); - currentText.clear(); + int currentWidth = + metrics.horizontalAdvance(currentText); + auto emoteSize = + QSize(image->width(), image->height()) * + (emoteScale * container.getScale()); - container.addElementNoLineBreak( - (new ImageLayoutElement(*this, image, emoteSize)) - ->setLink(this->getLink())); + if (!container.fitsInLine(currentWidth + + emoteSize.width())) + { + currentText += ellipsis; + break; + } + + // Add currently pending text to container, then add the emote after. + container.addElementNoLineBreak(getTextLayoutElement( + currentText, currentWidth, false)); + currentText.clear(); + + container.addElementNoLineBreak( + (new ImageLayoutElement(*this, image, emoteSize)) + ->setLink(this->getLink())); + } } } } diff --git a/src/util/Variant.hpp b/src/util/Variant.hpp new file mode 100644 index 000000000..8e7a2b093 --- /dev/null +++ b/src/util/Variant.hpp @@ -0,0 +1,28 @@ +#pragma once + +namespace chatterino::variant { + +/// Compile-time safe visitor for std and boost variants. +/// +/// From https://en.cppreference.com/w/cpp/utility/variant/visit +/// +/// Usage: +/// +/// ``` +/// std::variant v; +/// std::visit(variant::Overloaded{ +/// [](double) { qDebug() << "double"; }, +/// [](int) { qDebug() << "int"; } +/// }, v); +/// ``` +template +struct Overloaded : Ts... { + using Ts::operator()...; +}; + +// Technically, we shouldn't need this, as we're on C++ 20, +// but not all of our compilers support CTAD for aggregates yet. +template +Overloaded(Ts...) -> Overloaded; + +} // namespace chatterino::variant