refactor: Turn link-info into its own element and class (#5178)

This commit is contained in:
nerix
2024-02-18 13:34:00 +01:00
committed by GitHub
parent 42e4559910
commit e130c48f76
22 changed files with 752 additions and 314 deletions
+11 -46
View File
@@ -8,7 +8,7 @@
#include "messages/Message.hpp"
#include "messages/MessageColor.hpp"
#include "messages/MessageElement.hpp"
#include "providers/LinkResolver.hpp"
#include "providers/links/LinkResolver.hpp"
#include "providers/twitch/PubSubActions.hpp"
#include "providers/twitch/TwitchAccount.hpp"
#include "singletons/Emotes.hpp"
@@ -528,12 +528,12 @@ MessageBuilder::MessageBuilder(ImageUploaderResultTag /*unused*/,
this->emplace<TimestampElement>();
using MEF = MessageElementFlag;
auto addText = [this](QString text, MessageElementFlags mefs = MEF::Text,
auto addText = [this](QString text,
MessageColor color =
MessageColor::System) -> TextElement * {
this->message().searchText += text;
this->message().messageText += text;
return this->emplace<TextElement>(text, mefs, color);
return this->emplace<TextElement>(text, MEF::Text, color);
};
addText("Your image has been uploaded to");
@@ -541,16 +541,14 @@ MessageBuilder::MessageBuilder(ImageUploaderResultTag /*unused*/,
// ASSUMPTION: the user gave this uploader configuration to the program
// therefore they trust that the host is not wrong/malicious. This doesn't obey getSettings()->lowercaseDomains.
// This also ensures that the LinkResolver doesn't get these links.
addText(imageLink, {MEF::OriginalLink, MEF::LowercaseLink},
MessageColor::Link)
addText(imageLink, MessageColor::Link)
->setLink({Link::Url, imageLink})
->setTrailingSpace(false);
if (!deletionLink.isEmpty())
{
addText("(Deletion link:");
addText(deletionLink, {MEF::OriginalLink, MEF::LowercaseLink},
MessageColor::Link)
addText(deletionLink, MessageColor::Link)
->setLink({Link::Url, deletionLink})
->setTrailingSpace(false);
addText(")")->setTrailingSpace(false);
@@ -634,46 +632,13 @@ void MessageBuilder::addLink(const ParsedLink &parsedLink)
lowercaseLinkString += parsedLink.host.toString().toLower();
lowercaseLinkString += parsedLink.rest;
auto linkElement = Link(Link::Url, matchedLink);
auto textColor = MessageColor(MessageColor::Link);
auto *linkMELowercase =
this->emplace<TextElement>(lowercaseLinkString,
MessageElementFlag::LowercaseLink, textColor)
->setLink(linkElement);
auto *linkMEOriginal =
this->emplace<TextElement>(origLink, MessageElementFlag::OriginalLink,
textColor)
->setLink(linkElement);
LinkResolver::getLinkInfo(
matchedLink, nullptr,
[weakMessage = this->weakOf(), linkMELowercase, linkMEOriginal,
matchedLink](QString tooltipText, Link originalLink,
ImagePtr thumbnail) {
auto shared = weakMessage.lock();
if (!shared)
{
return;
}
if (!tooltipText.isEmpty())
{
linkMELowercase->setTooltip(tooltipText);
linkMEOriginal->setTooltip(tooltipText);
}
if (originalLink.value != matchedLink &&
!originalLink.value.isEmpty())
{
linkMELowercase->setLink(originalLink)->updateLink();
linkMEOriginal->setLink(originalLink)->updateLink();
}
linkMELowercase->setThumbnail(thumbnail);
linkMELowercase->setThumbnailType(
MessageElement::ThumbnailType::Link_Thumbnail);
linkMEOriginal->setThumbnail(thumbnail);
linkMEOriginal->setThumbnailType(
MessageElement::ThumbnailType::Link_Thumbnail);
});
auto *el = this->emplace<LinkElement>(
LinkElement::Parsed{.lowercase = lowercaseLinkString,
.original = matchedLink},
MessageElementFlag::Text, textColor);
el->setLink({Link::Url, matchedLink});
getIApp()->getLinkResolver()->resolve(el->linkInfo());
}
void MessageBuilder::addIrcMessageText(const QString &text)
+58 -93
View File
@@ -63,18 +63,6 @@ MessageElement *MessageElement::setTooltip(const QString &tooltip)
return this;
}
MessageElement *MessageElement::setThumbnail(const ImagePtr &thumbnail)
{
this->thumbnail_ = thumbnail;
return this;
}
MessageElement *MessageElement::setThumbnailType(const ThumbnailType type)
{
this->thumbnailType_ = type;
return this;
}
MessageElement *MessageElement::setTrailingSpace(bool value)
{
this->trailingSpace = value;
@@ -86,17 +74,7 @@ const QString &MessageElement::getTooltip() const
return this->tooltip_;
}
const ImagePtr &MessageElement::getThumbnail() const
{
return this->thumbnail_;
}
const MessageElement::ThumbnailType &MessageElement::getThumbnailType() const
{
return this->thumbnailType_;
}
const Link &MessageElement::getLink() const
Link MessageElement::getLink() const
{
return this->link_;
}
@@ -116,12 +94,6 @@ void MessageElement::addFlags(MessageElementFlags flags)
this->flags_.set(flags);
}
MessageElement *MessageElement::updateLink()
{
this->linkChanged.invoke();
return this;
}
// Empty
EmptyElement::EmptyElement()
: MessageElement(MessageElementFlag::None)
@@ -155,8 +127,8 @@ void ImageElement::addToContainer(MessageLayoutContainer &container,
auto size = QSize(this->image_->width() * container.getScale(),
this->image_->height() * container.getScale());
container.addElement((new ImageLayoutElement(*this, this->image_, size))
->setLink(this->getLink()));
container.addElement(
(new ImageLayoutElement(*this, this->image_, size)));
}
}
@@ -178,10 +150,8 @@ void CircularImageElement::addToContainer(MessageLayoutContainer &container,
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()));
container.addElement(new ImageWithCircleBackgroundLayoutElement(
*this, this->image_, imgSize, this->background_, this->padding_));
}
}
@@ -222,8 +192,7 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container,
QSize(int(container.getScale() * image->width() * emoteScale),
int(container.getScale() * image->height() * emoteScale));
container.addElement(this->makeImageLayoutElement(image, size)
->setLink(this->getLink()));
container.addElement(this->makeImageLayoutElement(image, size));
}
else
{
@@ -284,8 +253,7 @@ void LayeredEmoteElement::addToContainer(MessageLayoutContainer &container,
}
container.addElement(this->makeImageLayoutElement(
images, individualSizes, largestSize)
->setLink(this->getLink()));
images, individualSizes, largestSize));
}
else
{
@@ -441,8 +409,7 @@ EmotePtr BadgeElement::getEmote() const
MessageLayoutElement *BadgeElement::makeImageLayoutElement(
const ImagePtr &image, const QSize &size)
{
auto *element =
(new ImageLayoutElement(*this, image, size))->setLink(this->getLink());
auto *element = new ImageLayoutElement(*this, image, size);
return element;
}
@@ -459,9 +426,8 @@ MessageLayoutElement *ModBadgeElement::makeImageLayoutElement(
{
static const QColor modBadgeBackgroundColor("#34AE0A");
auto *element = (new ImageWithBackgroundLayoutElement(
*this, image, size, modBadgeBackgroundColor))
->setLink(this->getLink());
auto *element = new ImageWithBackgroundLayoutElement(
*this, image, size, modBadgeBackgroundColor);
return element;
}
@@ -476,8 +442,7 @@ VipBadgeElement::VipBadgeElement(const EmotePtr &data,
MessageLayoutElement *VipBadgeElement::makeImageLayoutElement(
const ImagePtr &image, const QSize &size)
{
auto *element =
(new ImageLayoutElement(*this, image, size))->setLink(this->getLink());
auto *element = new ImageLayoutElement(*this, image, size);
return element;
}
@@ -494,8 +459,7 @@ MessageLayoutElement *FfzBadgeElement::makeImageLayoutElement(
const ImagePtr &image, const QSize &size)
{
auto *element =
(new ImageWithBackgroundLayoutElement(*this, image, size, this->color))
->setLink(this->getLink());
new ImageWithBackgroundLayoutElement(*this, image, size, this->color);
return element;
}
@@ -507,11 +471,8 @@ TextElement::TextElement(const QString &text, MessageElementFlags flags,
, color_(color)
, style_(style)
{
for (const auto &word : text.split(' '))
{
this->words_.push_back({word, -1});
// fourtf: add logic to store multiple spaces after message
}
this->words_ = text.split(' ');
// fourtf: add logic to store multiple spaces after message
}
void TextElement::addToContainer(MessageLayoutContainer &container,
@@ -524,39 +485,29 @@ void TextElement::addToContainer(MessageLayoutContainer &container,
QFontMetrics metrics =
app->getFonts()->getFontMetrics(this->style_, container.getScale());
for (Word &word : this->words_)
for (const auto &word : this->words_)
{
auto getTextLayoutElement = [&](QString text, int width,
bool hasTrailingSpace) {
auto color = this->color_.getColor(*app->getThemes());
app->getThemes()->normalizeColor(color);
auto *e = (new TextLayoutElement(
*this, text, QSize(width, metrics.height()),
color, this->style_, container.getScale()))
->setLink(this->getLink());
auto *e = new TextLayoutElement(
*this, text, QSize(width, metrics.height()), color,
this->style_, container.getScale());
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;
};
// fourtf: add again
// if (word.width == -1) {
word.width = metrics.horizontalAdvance(word.text);
// }
auto width = metrics.horizontalAdvance(word);
// see if the text fits in the current line
if (container.fitsInLine(word.width))
if (container.fitsInLine(width))
{
container.addElementNoLineBreak(getTextLayoutElement(
word.text, word.width, this->hasTrailingSpace()));
word, width, this->hasTrailingSpace()));
continue;
}
@@ -565,35 +516,34 @@ void TextElement::addToContainer(MessageLayoutContainer &container,
{
container.breakLine();
if (container.fitsInLine(word.width))
if (container.fitsInLine(width))
{
container.addElementNoLineBreak(getTextLayoutElement(
word.text, word.width, this->hasTrailingSpace()));
word, width, this->hasTrailingSpace()));
continue;
}
}
// we done goofed, we need to wrap the text
QString text = word.text;
int textLength = text.length();
auto textLength = word.length();
int wordStart = 0;
int width = 0;
width = 0;
// QChar::isHighSurrogate(text[0].unicode()) ? 2 : 1
for (int i = 0; i < textLength; i++)
{
auto isSurrogate = text.size() > i + 1 &&
QChar::isHighSurrogate(text[i].unicode());
auto isSurrogate = word.size() > i + 1 &&
QChar::isHighSurrogate(word[i].unicode());
auto charWidth = isSurrogate
? metrics.horizontalAdvance(text.mid(i, 2))
: metrics.horizontalAdvance(text[i]);
? metrics.horizontalAdvance(word.mid(i, 2))
: metrics.horizontalAdvance(word[i]);
if (!container.fitsInLine(width + charWidth))
{
container.addElementNoLineBreak(getTextLayoutElement(
text.mid(wordStart, i - wordStart), width, false));
word.mid(wordStart, i - wordStart), width, false));
container.breakLine();
wordStart = i;
@@ -615,7 +565,7 @@ void TextElement::addToContainer(MessageLayoutContainer &container,
}
//add the final piece of wrapped text
container.addElementNoLineBreak(getTextLayoutElement(
text.mid(wordStart), width, this->hasTrailingSpace()));
word.mid(wordStart), width, this->hasTrailingSpace()));
}
}
}
@@ -649,19 +599,12 @@ void SingleLineTextElement::addToContainer(MessageLayoutContainer &container,
auto color = this->color_.getColor(*app->getThemes());
app->getThemes()->normalizeColor(color);
auto *e = (new TextLayoutElement(
*this, text, QSize(width, metrics.height()), color,
this->style_, container.getScale()))
->setLink(this->getLink());
auto *e = new TextLayoutElement(
*this, text, QSize(width, metrics.height()), color,
this->style_, container.getScale());
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;
};
@@ -749,6 +692,29 @@ void SingleLineTextElement::addToContainer(MessageLayoutContainer &container,
}
}
LinkElement::LinkElement(const Parsed &parsed, MessageElementFlags flags,
const MessageColor &color, FontStyle style)
: TextElement({}, flags, color, style)
, linkInfo_(parsed.original)
, lowercase_({parsed.lowercase})
, original_({parsed.original})
{
this->setTooltip(parsed.original);
}
void LinkElement::addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags)
{
this->words_ =
getSettings()->lowercaseDomains ? this->lowercase_ : this->original_;
TextElement::addToContainer(container, flags);
}
Link LinkElement::getLink() const
{
return {Link::Url, this->linkInfo_.url()};
}
// TIMESTAMP
TimestampElement::TimestampElement(QTime time)
: MessageElement(MessageElementFlag::Timestamp)
@@ -853,8 +819,7 @@ void ScalingImageElement::addToContainer(MessageLayoutContainer &container,
auto size = QSize(image->width() * container.getScale(),
image->height() * container.getScale());
container.addElement((new ImageLayoutElement(*this, image, size))
->setLink(this->getLink()));
container.addElement(new ImageLayoutElement(*this, image, size));
}
}
+42 -23
View File
@@ -4,6 +4,7 @@
#include "messages/ImageSet.hpp"
#include "messages/Link.hpp"
#include "messages/MessageColor.hpp"
#include "providers/links/LinkInfo.hpp"
#include "singletons/Fonts.hpp"
#include <pajlada/signals/signalholder.hpp>
@@ -136,10 +137,9 @@ enum class MessageElementFlag : int64_t {
BoldUsername = (1LL << 27),
NonBoldUsername = (1LL << 28),
// for links
LowercaseLink = (1LL << 29),
OriginalLink = (1LL << 30),
// used to check if links should be lowercased
LowercaseLinks = (1LL << 29),
// Unused = (1LL << 30)
// Unused: (1LL << 31)
// for elements of the message reply
@@ -166,9 +166,6 @@ public:
Update_Images = 4,
Update_All = Update_Text | Update_Emotes | Update_Images
};
enum ThumbnailType : char {
Link_Thumbnail = 1,
};
virtual ~MessageElement();
@@ -181,25 +178,18 @@ public:
MessageElement *setLink(const Link &link);
MessageElement *setText(const QString &text);
MessageElement *setTooltip(const QString &tooltip);
MessageElement *setThumbnailType(const ThumbnailType type);
MessageElement *setThumbnail(const ImagePtr &thumbnail);
MessageElement *setTrailingSpace(bool value);
const QString &getTooltip() const;
const ImagePtr &getThumbnail() const;
const ThumbnailType &getThumbnailType() const;
const Link &getLink() const;
virtual Link getLink() const;
bool hasTrailingSpace() const;
MessageElementFlags getFlags() const;
void addFlags(MessageElementFlags flags);
MessageElement *updateLink();
virtual void addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags) = 0;
pajlada::Signals::NoArgSignal linkChanged;
protected:
MessageElement(MessageElementFlags flags);
bool trailingSpace = true;
@@ -208,8 +198,6 @@ private:
QString text_;
Link link_;
QString tooltip_;
ImagePtr thumbnail_;
ThumbnailType thumbnailType_{};
MessageElementFlags flags_;
};
@@ -269,15 +257,12 @@ public:
void addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags) override;
protected:
QStringList words_;
private:
MessageColor color_;
FontStyle style_;
struct Word {
QString text;
int width = -1;
};
std::vector<Word> words_;
};
// contains a text that will be truncated to one line
@@ -303,6 +288,40 @@ private:
std::vector<Word> words_;
};
class LinkElement : public TextElement
{
public:
struct Parsed {
QString lowercase;
QString original;
};
LinkElement(const Parsed &parsed, MessageElementFlags flags,
const MessageColor &color = MessageColor::Text,
FontStyle style = FontStyle::ChatMedium);
~LinkElement() override = default;
LinkElement(const LinkElement &) = delete;
LinkElement(LinkElement &&) = delete;
LinkElement &operator=(const LinkElement &) = delete;
LinkElement &operator=(LinkElement &&) = delete;
void addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags) override;
Link getLink() const override;
[[nodiscard]] LinkInfo *linkInfo()
{
return &this->linkInfo_;
}
private:
LinkInfo linkInfo_;
// these are implicitly shared
QStringList lowercase_;
QStringList original_;
};
// contains emote data and will pick the emote based on :
// a) are images for the emote type enabled
// b) which size it wants
+8 -12
View File
@@ -77,9 +77,9 @@ MessageLayoutElement *MessageLayoutElement::setTrailingSpace(bool value)
return this;
}
MessageLayoutElement *MessageLayoutElement::setLink(const Link &_link)
MessageLayoutElement *MessageLayoutElement::setLink(const Link &link)
{
this->link_ = _link;
this->link_ = link;
return this;
}
@@ -89,9 +89,13 @@ MessageLayoutElement *MessageLayoutElement::setText(const QString &_text)
return this;
}
const Link &MessageLayoutElement::getLink() const
Link MessageLayoutElement::getLink() const
{
return this->link_;
if (this->link_)
{
return *this->link_;
}
return this->creator_.getLink();
}
const QString &MessageLayoutElement::getText() const
@@ -406,14 +410,6 @@ TextLayoutElement::TextLayoutElement(MessageElement &_creator, QString &_text,
this->setText(_text);
}
void TextLayoutElement::listenToLinkChanges()
{
this->managedConnections_.managedConnect(
static_cast<TextElement &>(this->getCreator()).linkChanged, [this]() {
this->setLink(this->getCreator().getLink());
});
}
void TextLayoutElement::addCopyTextToString(QString &str, uint32_t from,
uint32_t to) const
{
+13 -3
View File
@@ -44,7 +44,12 @@ public:
void setLine(size_t line);
MessageLayoutElement *setTrailingSpace(bool value);
MessageLayoutElement *setLink(const Link &link_);
/// @brief Overwrites the link for this layout element
///
/// @sa #getLink()
MessageLayoutElement *setLink(const Link &link);
MessageLayoutElement *setText(const QString &text_);
virtual void addCopyTextToString(QString &str, uint32_t from = 0,
@@ -57,7 +62,12 @@ public:
virtual int getMouseOverIndex(const QPoint &abs) const = 0;
virtual int getXFromIndex(size_t index) = 0;
const Link &getLink() const;
/// @brief Returns the link this layout element has
///
/// If there isn't any, an empty link is returned (type: None).
/// The link is sourced from the creator, but can be overwritten with
/// #setLink().
Link getLink() const;
const QString &getText() const;
FlagsEnum<MessageElementFlag> getFlags() const;
@@ -67,7 +77,7 @@ protected:
private:
QString text_;
QRect rect_;
Link link_;
std::optional<Link> link_;
MessageElement &creator_;
/**
* The line of the container this element is laid out at