plugins: added cloning of message elements (#6909)

Co-authored-by: Nerixyz <nerixdev@outlook.de>
Reviewed-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
Mm2PL
2026-04-25 11:40:02 +00:00
committed by GitHub
co-authored by Nerixyz
parent 4576d722cb
commit 0a10fa1c3e
8 changed files with 405 additions and 87 deletions
+2 -2
View File
@@ -206,7 +206,7 @@ declare namespace c2 {
highlight_color: string | null;
frozen: boolean;
elements(): MessageElement[];
append_element(init: MessageElementInit): void;
append_element(init: MessageElementInit | MessageElement): void;
}
interface MessageConstructor {
@@ -228,7 +228,7 @@ declare namespace c2 {
username_color?: string;
server_received_time?: number;
highlight_color?: string | null;
elements?: MessageElementInit[];
elements?: (MessageElementInit | MessageElement)[];
}
interface MessageElementBase {
+4 -3
View File
@@ -643,9 +643,10 @@ c2.Message = {}
function c2.Message:elements() end
--- Add an element to this message.
--- If given a MessageElement, it will be cloned before being added.
---
---@param init MessageElementInit The element to add
function c2.Message:append_element(init) end
---@param elem (MessageElement|MessageElementInit) The element to add
function c2.Message:append_element(elem) end
---A table to initialize a new message
---@class MessageInit
@@ -662,7 +663,7 @@ function c2.Message:append_element(init) end
---@field username_color? string The color of the username
---@field server_received_time? number The time the server received the message (in milliseconds since epoch)
---@field highlight_color? string|nil The color of the highlight (if any)
---@field elements? MessageElementInit[] The elements of the message
---@field elements? (MessageElementInit|MessageElement)[] The elements of the message
---@alias MessageColor "text"|"link"|"system"|string A color for a text element - "text", "link", and "system" are special values that take the current theme into account
+1
View File
@@ -752,6 +752,7 @@ end)
```
The full range of options can be found in the typing files ([LuaLS](./lua-meta/globals.lua), [TypeScript](./chatterino.d.ts)).
Existing `MessageElement`s in `elements` field of the table will be cloned regardless if they can be created in Lua.
##### `Message:elements()`
+98 -72
View File
@@ -239,70 +239,6 @@ std::unique_ptr<MessageElement> elementFromTable(const sol::table &tbl)
return el;
}
std::shared_ptr<Message> messageFromTable(const sol::table &tbl)
{
auto msg = std::make_shared<Message>();
msg->flags = tbl.get_or("flags", MessageFlag::None);
// This takes a UTC offset (not the milliseconds since the start of the day)
auto parseTime = tbl.get<std::optional<qint64>>("parse_time");
if (parseTime)
{
msg->parseTime = datetimeFromOffset(*parseTime).time();
}
msg->id = tbl.get_or("id", QString{});
msg->searchText = tbl.get_or("search_text", QString{});
msg->messageText = tbl.get_or("message_text", QString{});
msg->loginName = tbl.get_or("login_name", QString{});
msg->displayName = tbl.get_or("display_name", QString{});
msg->localizedName = tbl.get_or("localized_name", QString{});
msg->userID = tbl.get_or("user_id", QString{});
// missing: timeoutUser
msg->channelName = tbl.get_or("channel_name", QString{});
auto usernameColor = tbl.get_or("username_color", QString{});
if (!usernameColor.isEmpty())
{
msg->usernameColor = QColor(usernameColor);
}
auto serverReceivedTime =
tbl.get<std::optional<qint64>>("server_received_time");
if (serverReceivedTime)
{
msg->serverReceivedTime = datetimeFromOffset(*serverReceivedTime);
}
// missing: badges
// missing: badgeInfos
// we construct a color on the fly here
auto highlightColor = tbl.get_or("highlight_color", QString{});
if (!highlightColor.isEmpty())
{
msg->highlightColor = std::make_shared<QColor>(highlightColor);
}
// missing: replyThread
// missing: replyParent
// missing: count
auto elements = tbl.get<std::optional<sol::table>>("elements");
if (elements)
{
auto size = elements->size();
for (size_t i = 1; i <= size; i++)
{
msg->elements.emplace_back(
elementFromTable(elements->get<sol::table>(i)));
}
}
// missing: reward
return msg;
}
void checkWritable(Message *msg)
{
if (msg->frozen)
@@ -333,6 +269,8 @@ decltype(auto) memberAccessor()
});
}
std::shared_ptr<Message> messageFromTable(const sol::table &tbl);
} // namespace
namespace chatterino::lua::api::message {
@@ -852,16 +790,104 @@ void createUserType(sol::table &c2)
return MessageElements(msg);
},
"append_element",
[](Message *msg, const sol::table &tbl) {
checkWritable(msg);
auto el = elementFromTable(tbl);
if (el)
{
msg->elements.emplace_back(std::move(el));
}
});
sol::overload(
// Message:append_element(MessageElementInit)
// Create a new element
[](Message *msg, const sol::table &tbl) {
checkWritable(msg);
auto el = elementFromTable(tbl);
if (el)
{
msg->elements.emplace_back(std::move(el));
}
},
// Message:append_element(MessageElement)
// Create a clone of the given element and add it to the message
[](Message *msg, ElementRef &element) {
checkWritable(msg);
msg->elements.emplace_back(element.constElement()->clone());
}));
}
} // namespace chatterino::lua::api::message
namespace {
using namespace chatterino;
std::shared_ptr<Message> messageFromTable(const sol::table &tbl)
{
auto msg = std::make_shared<Message>();
msg->flags = tbl.get_or("flags", MessageFlag::None);
// This takes a UTC offset (not the milliseconds since the start of the day)
auto parseTime = tbl.get<std::optional<qint64>>("parse_time");
if (parseTime)
{
msg->parseTime = datetimeFromOffset(*parseTime).time();
}
msg->id = tbl.get_or("id", QString{});
msg->searchText = tbl.get_or("search_text", QString{});
msg->messageText = tbl.get_or("message_text", QString{});
msg->loginName = tbl.get_or("login_name", QString{});
msg->displayName = tbl.get_or("display_name", QString{});
msg->localizedName = tbl.get_or("localized_name", QString{});
msg->userID = tbl.get_or("user_id", QString{});
// missing: timeoutUser
msg->channelName = tbl.get_or("channel_name", QString{});
auto usernameColor = tbl.get_or("username_color", QString{});
if (!usernameColor.isEmpty())
{
msg->usernameColor = QColor(usernameColor);
}
auto serverReceivedTime =
tbl.get<std::optional<qint64>>("server_received_time");
if (serverReceivedTime)
{
msg->serverReceivedTime = datetimeFromOffset(*serverReceivedTime);
}
// missing: badges
// missing: badgeInfos
// we construct a color on the fly here
auto highlightColor = tbl.get_or("highlight_color", QString{});
if (!highlightColor.isEmpty())
{
msg->highlightColor = std::make_shared<QColor>(highlightColor);
}
// missing: replyThread
// missing: replyParent
// missing: count
auto elements = tbl.get<std::optional<sol::table>>("elements");
if (elements)
{
auto size = elements->size();
for (size_t i = 1; i <= size; i++)
{
auto ref =
elements->get<std::optional<lua::api::message::ElementRef>>(i);
if (ref.has_value())
{
msg->elements.emplace_back(ref->constElement()->clone());
}
else
{
msg->elements.emplace_back(
elementFromTable(elements->get<sol::table>(i)));
}
}
}
// missing: reward
return msg;
}
} // namespace
#endif
+4 -3
View File
@@ -192,9 +192,10 @@ c2.Message = {}
function c2.Message:elements() end
--- Add an element to this message.
--- If given a MessageElement, it will be cloned before being added.
---
---@param init MessageElementInit The element to add
function c2.Message:append_element(init) end
---@param elem (MessageElement|MessageElementInit) The element to add
function c2.Message:append_element(elem) end
---A table to initialize a new message
---@class MessageInit
@@ -211,7 +212,7 @@ function c2.Message:append_element(init) end
---@field username_color? string The color of the username
---@field server_received_time? number The time the server received the message (in milliseconds since epoch)
---@field highlight_color? string|nil The color of the highlight (if any)
---@field elements? MessageElementInit[] The elements of the message
---@field elements? (MessageElementInit|MessageElement)[] The elements of the message
---@alias MessageColor "text"|"link"|"system"|string A color for a text element - "text", "link", and "system" are special values that take the current theme into account
+198 -4
View File
@@ -25,6 +25,9 @@
#include <QJsonObject>
#include <QJsonValue>
#include <memory>
#include <utility>
#ifdef CHATTERINO_WITH_PRIVATE_QT_API
# include <QtGui/private/qtextengine_p.h>
#endif
@@ -106,6 +109,14 @@ void MessageElement::addFlags(MessageElementFlags flags)
this->flags_.set(flags);
}
void MessageElement::cloneFrom(const MessageElement &source)
{
this->link_ = source.link_;
this->tooltip_ = source.tooltip_;
this->flags_ = source.flags_;
this->trailingSpace = source.trailingSpace;
}
QJsonObject MessageElement::toJson() const
{
return {
@@ -158,6 +169,13 @@ std::string_view ImageElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> ImageElement::clone() const
{
auto im = std::make_unique<ImageElement>(this->image_, this->getFlags());
im->cloneFrom(*this);
return im;
}
CircularImageElement::CircularImageElement(ImagePtr image, int padding,
QColor background,
MessageElementFlags flags)
@@ -197,6 +215,14 @@ std::string_view CircularImageElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> CircularImageElement::clone() const
{
auto im = std::make_unique<CircularImageElement>(
this->image_, this->padding(), this->background(), this->getFlags());
im->cloneFrom(*this);
return im;
}
// EMOTE
EmoteElement::EmoteElement(const EmotePtr &emote, MessageElementFlags flags,
const MessageColor &textElementColor)
@@ -290,6 +316,14 @@ std::string_view EmoteElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> EmoteElement::clone() const
{
auto elem = std::make_unique<EmoteElement>(this->emote_, this->getFlags(),
this->textColor_);
elem->cloneFrom(*this);
return elem;
}
LayeredEmoteElement::LayeredEmoteElement(
std::vector<LayeredEmoteElement::Emote> &&emotes, MessageElementFlags flags,
const MessageColor &textElementColor)
@@ -490,6 +524,15 @@ std::string_view LayeredEmoteElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> LayeredEmoteElement::clone() const
{
std::vector<Emote> emotesCopy = this->emotes_;
auto elem = std::make_unique<LayeredEmoteElement>(
std::move(emotesCopy), this->getFlags(), this->textElementColor_);
elem->cloneFrom(*this);
return elem;
}
// BADGE
BadgeElement::BadgeElement(const EmotePtr &emote, MessageElementFlags flags)
: MessageElement(flags)
@@ -541,6 +584,12 @@ std::string_view BadgeElement::type() const
{
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> BadgeElement::clone() const
{
auto elem = std::make_unique<BadgeElement>(this->emote_, this->getFlags());
elem->cloneFrom(*this);
return elem;
}
// MOD BADGE
ModBadgeElement::ModBadgeElement(const EmotePtr &data,
@@ -573,6 +622,14 @@ std::string_view ModBadgeElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> ModBadgeElement::clone() const
{
auto elem =
std::make_unique<ModBadgeElement>(this->getEmote(), this->getFlags());
elem->cloneFrom(*this);
return elem;
}
// VIP BADGE
VipBadgeElement::VipBadgeElement(const EmotePtr &data,
MessageElementFlags flags_)
@@ -600,6 +657,13 @@ std::string_view VipBadgeElement::type() const
{
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> VipBadgeElement::clone() const
{
auto elem =
std::make_unique<VipBadgeElement>(this->getEmote(), this->getFlags());
elem->cloneFrom(*this);
return elem;
}
// FFZ Badge
FfzBadgeElement::FfzBadgeElement(const EmotePtr &data,
@@ -632,6 +696,14 @@ std::string_view FfzBadgeElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> FfzBadgeElement::clone() const
{
auto elem = std::make_unique<FfzBadgeElement>(
this->getEmote(), this->getFlags(), this->color);
elem->cloneFrom(*this);
return elem;
}
// TEXT
TextElement::TextElement(const QString &text, MessageElementFlags flags,
const MessageColor &color, FontStyle style)
@@ -642,6 +714,15 @@ TextElement::TextElement(const QString &text, MessageElementFlags flags,
this->words_ = text.split(' ');
// fourtf: add logic to store multiple spaces after message
}
TextElement::TextElement(TextElement::CloneConstructorTag /*hack*/,
QStringList words, MessageElementFlags flags,
const MessageColor &color, FontStyle style)
: MessageElement(flags)
, words_(std::move(words))
, color_(color)
, style_(style)
{
}
void TextElement::addToContainer(MessageLayoutContainer &container,
const MessageLayoutContext &ctx)
@@ -872,6 +953,15 @@ std::string_view TextElement::type() const
{
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> TextElement::clone() const
{
auto elem = std::make_unique<TextElement>(
TextElement::CloneConstructorTag{}, this->words_, this->getFlags(),
this->color_, this->style_);
elem->cloneFrom(*this);
return elem;
}
SingleLineTextElement::SingleLineTextElement(const QString &text,
MessageElementFlags flags,
@@ -883,6 +973,15 @@ SingleLineTextElement::SingleLineTextElement(const QString &text,
, words_(text.split(' '))
{
}
SingleLineTextElement::SingleLineTextElement(
SingleLineTextElement::CloneConstructorTag /*hack*/, QStringList words,
MessageElementFlags flags, const MessageColor &color, FontStyle style)
: MessageElement(flags)
, color_(color)
, style_(style)
, words_(std::move(words))
{
}
void SingleLineTextElement::addToContainer(MessageLayoutContainer &container,
const MessageLayoutContext &ctx)
@@ -1014,6 +1113,16 @@ std::string_view SingleLineTextElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> SingleLineTextElement::clone() const
{
auto elem = std::make_unique<SingleLineTextElement>(
SingleLineTextElement::CloneConstructorTag{}, this->words_,
this->getFlags(), this->color_, this->style_);
elem->cloneFrom(*this);
return elem;
}
LinkElement::LinkElement(const Parsed &parsed, const QString &fullUrl,
MessageElementFlags flags, const MessageColor &color,
FontStyle style)
@@ -1025,6 +1134,21 @@ LinkElement::LinkElement(const Parsed &parsed, const QString &fullUrl,
this->setTooltip(parsed.original);
}
LinkElement::LinkElement(TextElement::CloneConstructorTag /*hack*/,
QStringList lowercase, QStringList original,
const QString &fullUrl, MessageElementFlags flags,
const MessageColor &color, FontStyle style)
: TextElement({}, flags, color, style)
, linkInfo_(fullUrl)
, lowercase_(std::move(lowercase))
, original_(std::move(original))
{
if (!this->original_.isEmpty())
{
this->setTooltip(this->original_.at(0));
}
}
void LinkElement::addToContainer(MessageLayoutContainer &container,
const MessageLayoutContext &ctx)
{
@@ -1054,9 +1178,20 @@ std::string_view LinkElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> LinkElement::clone() const
{
auto elem = std::make_unique<LinkElement>(
LinkElement::CloneConstructorTag{}, this->lowercase_, this->original_,
this->linkInfo_.originalUrl(), this->getFlags(), this->color_,
this->style_);
elem->cloneFrom(*this);
return elem;
}
MentionElement::MentionElement(const QString &displayName, QString loginName_,
MessageColor fallbackColor_,
MessageColor userColor_)
const MessageColor &fallbackColor_,
const MessageColor &userColor_)
: TextElement(displayName,
{MessageElementFlag::Text, MessageElementFlag::Mention})
, fallbackColor_(fallbackColor_)
@@ -1065,9 +1200,22 @@ MentionElement::MentionElement(const QString &displayName, QString loginName_,
{
}
MentionElement::MentionElement(TextElement::CloneConstructorTag /* hack */,
QStringList words, QString loginName_,
const MessageColor &fallbackColor_,
const MessageColor &userColor_)
: TextElement(MentionElement::CloneConstructorTag{}, std::move(words),
{MessageElementFlag::Text, MessageElementFlag::Mention})
, fallbackColor_(fallbackColor_)
, userColor_(userColor_)
, userLoginName_(std::move(loginName_))
{
}
template <typename>
MentionElement::MentionElement(const QString &displayName, QString loginName_,
MessageColor fallbackColor_, QColor userColor_)
const MessageColor &fallbackColor_,
QColor userColor_)
: TextElement(displayName,
{MessageElementFlag::Text, MessageElementFlag::Mention})
, fallbackColor_(fallbackColor_)
@@ -1078,7 +1226,7 @@ MentionElement::MentionElement(const QString &displayName, QString loginName_,
template MentionElement::MentionElement(const QString &displayName,
QString loginName_,
MessageColor fallbackColor_,
const MessageColor &fallbackColor_,
QColor userColor_);
void MentionElement::addToContainer(MessageLayoutContainer &container,
@@ -1141,6 +1289,17 @@ std::string_view MentionElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> MentionElement::clone() const
{
auto elem = std::make_unique<MentionElement>(
TextElement::CloneConstructorTag{}, this->words_, this->userLoginName_,
this->fallbackColor_, this->userColor_);
elem->setTooltip(this->getTooltip());
elem->setTrailingSpace(this->hasTrailingSpace());
return elem;
}
// TIMESTAMP
TimestampElement::TimestampElement()
: TimestampElement(getApp()->isTest() ? QTime::fromMSecsSinceStartOfDay(0)
@@ -1209,6 +1368,13 @@ std::string_view TimestampElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> TimestampElement::clone() const
{
auto elem = std::make_unique<TimestampElement>(this->time_);
elem->cloneFrom(*this);
return elem;
}
// TWITCH MODERATION
TwitchModerationElement::TwitchModerationElement()
: MessageElement(MessageElementFlag::ModeratorTools)
@@ -1258,6 +1424,13 @@ std::string_view TwitchModerationElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> TwitchModerationElement::clone() const
{
auto elem = std::make_unique<TwitchModerationElement>();
elem->cloneFrom(*this);
return elem;
}
LinebreakElement::LinebreakElement(MessageElementFlags flags)
: MessageElement(flags)
{
@@ -1285,6 +1458,13 @@ std::string_view LinebreakElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> LinebreakElement::clone() const
{
auto elem = std::make_unique<LinebreakElement>(this->getFlags());
elem->setTrailingSpace(this->hasTrailingSpace());
return elem;
}
ScalingImageElement::ScalingImageElement(ImageSet images,
MessageElementFlags flags)
: MessageElement(flags)
@@ -1327,6 +1507,13 @@ std::string_view ScalingImageElement::type() const
{
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> ScalingImageElement::clone() const
{
auto elem =
std::make_unique<ScalingImageElement>(this->images_, this->getFlags());
elem->setTrailingSpace(this->hasTrailingSpace());
return elem;
}
ReplyCurveElement::ReplyCurveElement()
: MessageElement(MessageElementFlag::RepliedMessage)
@@ -1363,4 +1550,11 @@ std::string_view ReplyCurveElement::type() const
return std::remove_pointer_t<decltype(this)>::TYPE;
}
std::unique_ptr<MessageElement> ReplyCurveElement::clone() const
{
auto elem = std::make_unique<ReplyCurveElement>();
elem->setTrailingSpace(this->hasTrailingSpace());
return elem;
}
} // namespace chatterino
+63 -3
View File
@@ -196,10 +196,16 @@ public:
/// member.
virtual std::string_view type() const = 0;
/// Creates a new identical message element.
virtual std::unique_ptr<MessageElement> clone() const = 0;
protected:
MessageElement(MessageElementFlags flags);
bool trailingSpace = true;
/// Copy MessageElement private data from `source` to this MessageElement
void cloneFrom(const MessageElement &source);
private:
Link link_;
QString tooltip_;
@@ -222,6 +228,8 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
private:
ImagePtr image_;
};
@@ -240,6 +248,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
ImagePtr image() const
{
@@ -264,12 +273,23 @@ private:
// contains a text, it will split it into words
class TextElement : public MessageElement
{
protected:
struct CloneConstructorTag {
};
public:
static constexpr std::string_view TYPE = "text";
TextElement(const QString &text, MessageElementFlags flags,
const MessageColor &color = MessageColor::Text,
FontStyle style = FontStyle::ChatMedium);
/// This is intended only for cloning the element.
TextElement(TextElement::CloneConstructorTag, QStringList words,
MessageElementFlags flags,
const MessageColor &color = MessageColor::Text,
FontStyle style = FontStyle::ChatMedium);
~TextElement() override = default;
void addToContainer(MessageLayoutContainer &container,
@@ -277,6 +297,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
const MessageColor &color() const noexcept;
FontStyle fontStyle() const noexcept;
@@ -299,12 +320,22 @@ protected:
// contains a text that will be truncated to one line
class SingleLineTextElement : public MessageElement
{
struct CloneConstructorTag {
};
public:
static constexpr std::string_view TYPE = "single-line-text";
SingleLineTextElement(const QString &text, MessageElementFlags flags,
const MessageColor &color = MessageColor::Text,
FontStyle style = FontStyle::ChatMedium);
/// This is intended only for cloning the element.
SingleLineTextElement(SingleLineTextElement::CloneConstructorTag,
QStringList words, MessageElementFlags flags,
const MessageColor &color = MessageColor::Text,
FontStyle style = FontStyle::ChatMedium);
~SingleLineTextElement() override = default;
void addToContainer(MessageLayoutContainer &container,
@@ -312,6 +343,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
const MessageColor &color() const
{
@@ -349,6 +381,14 @@ public:
MessageElementFlags flags,
const MessageColor &color = MessageColor::Text,
FontStyle style = FontStyle::ChatMedium);
/// This is intended only for cloning the element.
LinkElement(TextElement::CloneConstructorTag, QStringList lowercase,
QStringList original, const QString &fullUrl,
MessageElementFlags flags,
const MessageColor &color = MessageColor::Text,
FontStyle style = FontStyle::ChatMedium);
~LinkElement() override = default;
LinkElement(const LinkElement &) = delete;
LinkElement(LinkElement &&) = delete;
@@ -376,6 +416,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
private:
LinkInfo linkInfo_;
@@ -400,14 +441,21 @@ public:
static constexpr std::string_view TYPE = "mention";
explicit MentionElement(const QString &displayName, QString loginName_,
MessageColor fallbackColor_,
MessageColor userColor_);
const MessageColor &fallbackColor_,
const MessageColor &userColor_);
/// This is intended only for cloning the element.
explicit MentionElement(TextElement::CloneConstructorTag, QStringList words,
QString loginName_,
const MessageColor &fallbackColor_,
const MessageColor &userColor_);
/// Deprioritized ctor allowing us to pass through a potentially invalid userColor_
///
/// If the userColor_ is invalid, we fall back to the fallbackColor_
template <typename = void>
explicit MentionElement(const QString &displayName, QString loginName_,
MessageColor fallbackColor_, QColor userColor_);
const MessageColor &fallbackColor_,
QColor userColor_);
~MentionElement() override = default;
MentionElement(const MentionElement &) = delete;
MentionElement(MentionElement &&) = delete;
@@ -435,6 +483,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
private:
/**
@@ -467,6 +516,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
protected:
virtual MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
@@ -512,6 +562,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
private:
MessageLayoutElement *makeImageLayoutElement(
@@ -543,6 +594,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
protected:
virtual MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
@@ -561,6 +613,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
protected:
MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
@@ -576,6 +629,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
protected:
MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
@@ -592,6 +646,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
protected:
MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image,
@@ -622,6 +677,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
private:
QTime time_;
@@ -643,6 +699,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
};
// Forces a linebreak
@@ -658,6 +715,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
};
// Image element which will pick the quality of the image based on ui scale
@@ -675,6 +733,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
private:
ImageSet images_;
@@ -692,6 +751,7 @@ public:
QJsonObject toJson() const override;
std::string_view type() const override;
std::unique_ptr<MessageElement> clone() const override;
};
} // namespace chatterino
+35
View File
@@ -651,3 +651,38 @@ TEST(TestIrcMessageHandlerP, Integrity)
{
ASSERT_FALSE(UPDATE_SNAPSHOTS); // make sure fixtures are actually tested
}
TEST_P(TestIrcMessageHandlerP, CloneElements)
{
auto channel = makeMockTwitchChannel(u"pajlada"_s, *this->snapshot);
VectorMessageSink sink;
for (auto prevInput : this->snapshot->param("prevMessages").toArray())
{
auto *ircMessage = Communi::IrcMessage::fromData(
prevInput.toString().toUtf8(), nullptr);
ASSERT_NE(ircMessage, nullptr);
IrcMessageHandler::parseMessageInto(ircMessage, sink, channel.get());
delete ircMessage;
}
auto *ircMessage =
Communi::IrcMessage::fromData(this->snapshot->inputUtf8(), nullptr);
ASSERT_NE(ircMessage, nullptr);
IrcMessageHandler::parseMessageInto(ircMessage, sink, channel.get());
delete ircMessage;
for (const auto &message : sink.messages())
{
for (const auto &original : message->elements)
{
auto originalObj = original->toJson();
auto clonedObj = original->clone()->toJson();
ASSERT_EQ(originalObj, clonedObj)
<< "\noriginal:\n"
<< QJsonDocument(originalObj).toJson() << "\ncloned:\n"
<< QJsonDocument(clonedObj).toJson();
}
}
}