Added support for Twitch's Chat Replies (#3722)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
Daniel Sage
2022-07-31 06:45:25 -04:00
committed by GitHub
parent a280089693
commit 20c974fdab
53 changed files with 2022 additions and 310 deletions
+178
View File
@@ -5,6 +5,8 @@
#include "messages/Emote.hpp"
#include "messages/layouts/MessageLayoutContainer.hpp"
#include "messages/layouts/MessageLayoutElement.hpp"
#include "providers/emoji/Emojis.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Settings.hpp"
#include "singletons/Theme.hpp"
#include "util/DebugCount.hpp"
@@ -132,6 +134,31 @@ void ImageElement::addToContainer(MessageLayoutContainer &container,
}
}
CircularImageElement::CircularImageElement(ImagePtr image, int padding,
QColor background,
MessageElementFlags flags)
: MessageElement(flags)
, image_(image)
, padding_(padding)
, background_(background)
{
}
void CircularImageElement::addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags)
{
if (flags.hasAny(this->getFlags()))
{
auto imgSize = QSize(this->image_->width(), this->image_->height()) *
container.getScale();
container.addElement((new ImageWithCircleBackgroundLayoutElement(
*this, this->image_, imgSize,
this->background_, this->padding_))
->setLink(this->getLink()));
}
}
// EMOTE
EmoteElement::EmoteElement(const EmotePtr &emote, MessageElementFlags flags,
const MessageColor &textElementColor)
@@ -395,6 +422,137 @@ void TextElement::addToContainer(MessageLayoutContainer &container,
}
}
SingleLineTextElement::SingleLineTextElement(const QString &text,
MessageElementFlags flags,
const MessageColor &color,
FontStyle style)
: MessageElement(flags)
, color_(color)
, style_(style)
{
for (const auto &word : text.split(' '))
{
this->words_.push_back({word, -1});
}
}
void SingleLineTextElement::addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags)
{
auto app = getApp();
if (flags.hasAny(this->getFlags()))
{
QFontMetrics metrics =
app->fonts->getFontMetrics(this->style_, container.getScale());
auto getTextLayoutElement = [&](QString text, int width,
bool hasTrailingSpace) {
auto color = this->color_.getColor(*app->themes);
app->themes->normalizeColor(color);
auto e = (new TextLayoutElement(
*this, text, QSize(width, metrics.height()), color,
this->style_, container.getScale()))
->setLink(this->getLink());
e->setTrailingSpace(hasTrailingSpace);
e->setText(text);
// If URL link was changed,
// Should update it in MessageLayoutElement too!
if (this->getLink().type == Link::Url)
{
static_cast<TextLayoutElement *>(e)->listenToLinkChanges();
}
return e;
};
static const auto ellipsis = QStringLiteral("...");
auto addEllipsis = [&]() {
int ellipsisSize = metrics.horizontalAdvance(ellipsis);
container.addElementNoLineBreak(
getTextLayoutElement(ellipsis, ellipsisSize, false));
};
for (Word &word : this->words_)
{
auto parsedWords = app->emotes->emojis.parse(word.text);
if (parsedWords.size() == 0)
{
continue; // sanity check
}
auto &parsedWord = parsedWords[0];
if (parsedWord.type() == typeid(EmotePtr))
{
auto emote = boost::get<EmotePtr>(parsedWord);
auto image =
emote->images.getImageOrLoaded(container.getScale());
if (!image->isEmpty())
{
auto emoteScale = getSettings()->emoteScale.getValue();
auto size = QSize(image->width(), image->height()) *
(emoteScale * container.getScale());
if (!container.fitsInLine(size.width()))
{
addEllipsis();
break;
}
container.addElementNoLineBreak(
(new ImageLayoutElement(*this, image, size))
->setLink(this->getLink()));
}
}
else if (parsedWord.type() == typeid(QString))
{
word.width = metrics.horizontalAdvance(word.text);
// see if the text fits in the current line
if (container.fitsInLine(word.width))
{
container.addElementNoLineBreak(getTextLayoutElement(
word.text, word.width, this->hasTrailingSpace()));
}
else
{
// word overflows, try minimum truncation
bool cutSuccess = false;
for (size_t cut = 1; cut < word.text.length(); ++cut)
{
// Cut off n characters and append the ellipsis.
// Try removing characters one by one until the word fits.
QString truncatedWord =
word.text.chopped(cut) + ellipsis;
int newSize = metrics.horizontalAdvance(truncatedWord);
if (container.fitsInLine(newSize))
{
container.addElementNoLineBreak(
getTextLayoutElement(truncatedWord, newSize,
false));
cutSuccess = true;
break;
}
}
if (!cutSuccess)
{
// We weren't able to show any part of the current word, so
// just append the ellipsis.
addEllipsis();
}
break;
}
}
}
container.breakLine();
}
}
// TIMESTAMP
TimestampElement::TimestampElement(QTime time)
: MessageElement(MessageElementFlag::Timestamp)
@@ -502,4 +660,24 @@ void ScalingImageElement::addToContainer(MessageLayoutContainer &container,
}
}
ReplyCurveElement::ReplyCurveElement()
: MessageElement(MessageElementFlag::RepliedMessage)
// these values nicely align with a single badge
, neededMargin_(3)
, size_(18, 14)
{
}
void ReplyCurveElement::addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags)
{
if (flags.hasAny(this->getFlags()))
{
QSize boxSize = this->size_ * container.getScale();
container.addElement(new ReplyCurveLayoutElement(
*this, boxSize, 1.5 * container.getScale(),
this->neededMargin_ * container.getScale()));
}
}
} // namespace chatterino