mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
0f44a2e0f4
The feature itself still doesn't work for me, but this code does the same thing. only thing that's really different is it also disconnects the signal properly once the TextLayoutElement is deleted
331 lines
9.6 KiB
C++
331 lines
9.6 KiB
C++
#include "messages/MessageElement.hpp"
|
|
|
|
#include "Application.hpp"
|
|
#include "controllers/moderationactions/ModerationActions.hpp"
|
|
#include "debug/Benchmark.hpp"
|
|
#include "messages/Emote.hpp"
|
|
#include "messages/layouts/MessageLayoutContainer.hpp"
|
|
#include "messages/layouts/MessageLayoutElement.hpp"
|
|
#include "singletons/Settings.hpp"
|
|
#include "singletons/Theme.hpp"
|
|
#include "util/DebugCount.hpp"
|
|
|
|
namespace chatterino {
|
|
|
|
MessageElement::MessageElement(MessageElementFlags flags)
|
|
: flags_(flags)
|
|
{
|
|
DebugCount::increase("message elements");
|
|
}
|
|
|
|
MessageElement::~MessageElement()
|
|
{
|
|
DebugCount::decrease("message elements");
|
|
}
|
|
|
|
MessageElement *MessageElement::setLink(const Link &link)
|
|
{
|
|
this->link_ = link;
|
|
return this;
|
|
}
|
|
|
|
MessageElement *MessageElement::setText(const QString &text)
|
|
{
|
|
this->text_ = text;
|
|
return this;
|
|
}
|
|
|
|
MessageElement *MessageElement::setTooltip(const QString &tooltip)
|
|
{
|
|
this->tooltip_ = tooltip;
|
|
return this;
|
|
}
|
|
|
|
MessageElement *MessageElement::setTrailingSpace(bool value)
|
|
{
|
|
this->trailingSpace = value;
|
|
return this;
|
|
}
|
|
|
|
const QString &MessageElement::getTooltip() const
|
|
{
|
|
return this->tooltip_;
|
|
}
|
|
|
|
const Link &MessageElement::getLink() const
|
|
{
|
|
return this->link_;
|
|
}
|
|
|
|
bool MessageElement::hasTrailingSpace() const
|
|
{
|
|
return this->trailingSpace;
|
|
}
|
|
|
|
MessageElementFlags MessageElement::getFlags() const
|
|
{
|
|
return this->flags_;
|
|
}
|
|
|
|
MessageElement *MessageElement::updateLink()
|
|
{
|
|
this->linkChanged.invoke();
|
|
return this;
|
|
}
|
|
|
|
// IMAGE
|
|
ImageElement::ImageElement(ImagePtr image, MessageElementFlags flags)
|
|
: MessageElement(flags)
|
|
, image_(image)
|
|
{
|
|
// this->setTooltip(image->getTooltip());
|
|
}
|
|
|
|
void ImageElement::addToContainer(MessageLayoutContainer &container,
|
|
MessageElementFlags flags)
|
|
{
|
|
if (flags.hasAny(this->getFlags()))
|
|
{
|
|
auto size = QSize(this->image_->width() * container.getScale(),
|
|
this->image_->height() * container.getScale());
|
|
|
|
container.addElement((new ImageLayoutElement(*this, this->image_, size))
|
|
->setLink(this->getLink()));
|
|
}
|
|
}
|
|
|
|
// EMOTE
|
|
EmoteElement::EmoteElement(const EmotePtr &emote, MessageElementFlags flags)
|
|
: MessageElement(flags)
|
|
, emote_(emote)
|
|
{
|
|
this->textElement_.reset(
|
|
new TextElement(emote->getCopyString(), MessageElementFlag::Misc));
|
|
|
|
this->setTooltip(emote->tooltip.string);
|
|
}
|
|
|
|
EmotePtr EmoteElement::getEmote() const
|
|
{
|
|
return this->emote_;
|
|
}
|
|
|
|
void EmoteElement::addToContainer(MessageLayoutContainer &container,
|
|
MessageElementFlags flags)
|
|
{
|
|
if (flags.hasAny(this->getFlags()))
|
|
{
|
|
if (flags.has(MessageElementFlag::EmoteImages))
|
|
{
|
|
auto image = this->emote_->images.getImage(container.getScale());
|
|
if (image->isEmpty())
|
|
return;
|
|
|
|
auto emoteScale = getSettings()->emoteScale.getValue();
|
|
|
|
auto size =
|
|
QSize(int(container.getScale() * image->width() * emoteScale),
|
|
int(container.getScale() * image->height() * emoteScale));
|
|
|
|
container.addElement((new ImageLayoutElement(*this, image, size))
|
|
->setLink(this->getLink()));
|
|
}
|
|
else
|
|
{
|
|
if (this->textElement_)
|
|
{
|
|
this->textElement_->addToContainer(container,
|
|
MessageElementFlag::Misc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TEXT
|
|
TextElement::TextElement(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});
|
|
// fourtf: add logic to store multiple spaces after message
|
|
}
|
|
}
|
|
|
|
void TextElement::addToContainer(MessageLayoutContainer &container,
|
|
MessageElementFlags flags)
|
|
{
|
|
auto app = getApp();
|
|
|
|
if (flags.hasAny(this->getFlags()))
|
|
{
|
|
QFontMetrics metrics =
|
|
app->fonts->getFontMetrics(this->style_, container.getScale());
|
|
|
|
for (Word &word : this->words_)
|
|
{
|
|
auto getTextLayoutElement = [&](QString text, int width,
|
|
bool trailingSpace) {
|
|
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(trailingSpace);
|
|
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.width(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()));
|
|
continue;
|
|
}
|
|
|
|
// see if the text fits in the next line
|
|
if (!container.atStartOfLine())
|
|
{
|
|
container.breakLine();
|
|
|
|
if (container.fitsInLine(word.width))
|
|
{
|
|
container.addElementNoLineBreak(getTextLayoutElement(
|
|
word.text, word.width, this->hasTrailingSpace()));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// we done goofed, we need to wrap the text
|
|
QString text = word.text;
|
|
int textLength = text.length();
|
|
int wordStart = 0;
|
|
int 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 charWidth = isSurrogate ? metrics.width(text.mid(i, 2))
|
|
: metrics.width(text[i]);
|
|
|
|
if (!container.fitsInLine(width + charWidth)) //
|
|
{
|
|
container.addElementNoLineBreak(getTextLayoutElement(
|
|
text.mid(wordStart, i - wordStart), width, false));
|
|
container.breakLine();
|
|
|
|
wordStart = i;
|
|
width = charWidth;
|
|
|
|
if (isSurrogate)
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
width += charWidth;
|
|
|
|
if (isSurrogate)
|
|
i++;
|
|
}
|
|
|
|
container.addElement(getTextLayoutElement(
|
|
text.mid(wordStart), width, this->hasTrailingSpace()));
|
|
container.breakLine();
|
|
}
|
|
}
|
|
}
|
|
|
|
// TIMESTAMP
|
|
TimestampElement::TimestampElement(QTime time)
|
|
: MessageElement(MessageElementFlag::Timestamp)
|
|
, time_(time)
|
|
, element_(this->formatTime(time))
|
|
{
|
|
assert(this->element_ != nullptr);
|
|
}
|
|
|
|
void TimestampElement::addToContainer(MessageLayoutContainer &container,
|
|
MessageElementFlags flags)
|
|
{
|
|
if (flags.hasAny(this->getFlags()))
|
|
{
|
|
auto app = getApp();
|
|
if (getSettings()->timestampFormat != this->format_)
|
|
{
|
|
this->format_ = getSettings()->timestampFormat.getValue();
|
|
this->element_.reset(this->formatTime(this->time_));
|
|
}
|
|
|
|
this->element_->addToContainer(container, flags);
|
|
}
|
|
}
|
|
|
|
TextElement *TimestampElement::formatTime(const QTime &time)
|
|
{
|
|
static QLocale locale("en_US");
|
|
|
|
QString format = locale.toString(time, getSettings()->timestampFormat);
|
|
|
|
return new TextElement(format, MessageElementFlag::Timestamp,
|
|
MessageColor::System, FontStyle::ChatMedium);
|
|
}
|
|
|
|
// TWITCH MODERATION
|
|
TwitchModerationElement::TwitchModerationElement()
|
|
: MessageElement(MessageElementFlag::ModeratorTools)
|
|
{
|
|
}
|
|
|
|
void TwitchModerationElement::addToContainer(MessageLayoutContainer &container,
|
|
MessageElementFlags flags)
|
|
{
|
|
if (flags.has(MessageElementFlag::ModeratorTools))
|
|
{
|
|
QSize size(int(container.getScale() * 16),
|
|
int(container.getScale() * 16));
|
|
|
|
for (const auto &action :
|
|
getApp()->moderationActions->items.getVector())
|
|
{
|
|
if (auto image = action.getImage())
|
|
{
|
|
container.addElement(
|
|
(new ImageLayoutElement(*this, image.get(), size))
|
|
->setLink(Link(Link::UserAction, action.getAction())));
|
|
}
|
|
else
|
|
{
|
|
container.addElement(
|
|
(new TextIconLayoutElement(*this, action.getLine1(),
|
|
action.getLine2(),
|
|
container.getScale(), size))
|
|
->setLink(Link(Link::UserAction, action.getAction())));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace chatterino
|