Formalize zero-width emote implementation (#4314)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
Daniel Sage
2023-03-18 12:30:08 -04:00
committed by GitHub
parent db97a14cdc
commit 0acbc0d2c3
18 changed files with 947 additions and 146 deletions
+180
View File
@@ -15,6 +15,24 @@
namespace chatterino {
namespace {
// Computes the bounding box for the given vector of images
QSize getBoundingBoxSize(const std::vector<ImagePtr> &images)
{
int width = 0;
int height = 0;
for (const auto &img : images)
{
width = std::max(width, img->width());
height = std::max(height, img->height());
}
return QSize(width, height);
}
} // namespace
MessageElement::MessageElement(MessageElementFlags flags)
: flags_(flags)
{
@@ -216,6 +234,168 @@ MessageLayoutElement *EmoteElement::makeImageLayoutElement(
return new ImageLayoutElement(*this, image, size);
}
LayeredEmoteElement::LayeredEmoteElement(std::vector<EmotePtr> &&emotes,
MessageElementFlags flags,
const MessageColor &textElementColor)
: MessageElement(flags)
, emotes_(std::move(emotes))
, textElementColor_(textElementColor)
{
this->updateTooltips();
}
void LayeredEmoteElement::addEmoteLayer(const EmotePtr &emote)
{
this->emotes_.push_back(emote);
this->updateTooltips();
}
void LayeredEmoteElement::addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags)
{
if (flags.hasAny(this->getFlags()))
{
if (flags.has(MessageElementFlag::EmoteImages))
{
auto images = this->getLoadedImages(container.getScale());
if (images.empty())
{
return;
}
auto emoteScale = getSettings()->emoteScale.getValue();
float overallScale = emoteScale * container.getScale();
auto largestSize = getBoundingBoxSize(images) * overallScale;
std::vector<QSize> individualSizes;
individualSizes.reserve(this->emotes_.size());
for (auto img : images)
{
individualSizes.push_back(QSize(img->width(), img->height()) *
overallScale);
}
container.addElement(this->makeImageLayoutElement(
images, individualSizes, largestSize)
->setLink(this->getLink()));
}
else
{
if (this->textElement_)
{
this->textElement_->addToContainer(container,
MessageElementFlag::Misc);
}
}
}
}
std::vector<ImagePtr> LayeredEmoteElement::getLoadedImages(float scale)
{
std::vector<ImagePtr> res;
res.reserve(this->emotes_.size());
for (auto emote : this->emotes_)
{
auto image = emote->images.getImageOrLoaded(scale);
if (image->isEmpty())
{
continue;
}
res.push_back(image);
}
return res;
}
MessageLayoutElement *LayeredEmoteElement::makeImageLayoutElement(
const std::vector<ImagePtr> &images, const std::vector<QSize> &sizes,
QSize largestSize)
{
return new LayeredImageLayoutElement(*this, images, sizes, largestSize);
}
void LayeredEmoteElement::updateTooltips()
{
if (!this->emotes_.empty())
{
QString copyStr = this->getCopyString();
this->textElement_.reset(new TextElement(
copyStr, MessageElementFlag::Misc, this->textElementColor_));
this->setTooltip(copyStr);
}
std::vector<QString> result;
result.reserve(this->emotes_.size());
for (auto &emote : this->emotes_)
{
result.push_back(emote->tooltip.string);
}
this->emoteTooltips_ = std::move(result);
}
const std::vector<QString> &LayeredEmoteElement::getEmoteTooltips() const
{
return this->emoteTooltips_;
}
QString LayeredEmoteElement::getCleanCopyString() const
{
QString result;
for (size_t i = 0; i < this->emotes_.size(); ++i)
{
if (i != 0)
{
result += " ";
}
result +=
TwitchEmotes::cleanUpEmoteCode(this->emotes_[i]->getCopyString());
}
return result;
}
QString LayeredEmoteElement::getCopyString() const
{
QString result;
for (size_t i = 0; i < this->emotes_.size(); ++i)
{
if (i != 0)
{
result += " ";
}
result += this->emotes_[i]->getCopyString();
}
return result;
}
const std::vector<EmotePtr> &LayeredEmoteElement::getEmotes() const
{
return this->emotes_;
}
std::vector<EmotePtr> LayeredEmoteElement::getUniqueEmotes() const
{
// Functor for std::copy_if that keeps track of seen elements
struct NotDuplicate {
bool operator()(const EmotePtr &element)
{
return seen.insert(element).second;
}
private:
std::set<EmotePtr> seen;
};
// Get unique emotes while maintaining relative layering order
NotDuplicate dup;
std::vector<EmotePtr> unique;
std::copy_if(this->emotes_.begin(), this->emotes_.end(),
std::back_insert_iterator(unique), dup);
return unique;
}
// BADGE
BadgeElement::BadgeElement(const EmotePtr &emote, MessageElementFlags flags)
: MessageElement(flags)