mirror of
https://github.com/Chatterino/chatterino2.git
synced 2026-08-24 10:04:53 -05:00
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:
Vendored
+2
-2
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()`
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user