Allow each layered image to retain its own flags (#4460)

This fixes an issue where context-menu items for zero-width emotes displayed the wrong provider.
This commit is contained in:
nerix 2023-03-19 11:26:30 +01:00 committed by GitHub
parent 0acbc0d2c3
commit a777a227d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 29 deletions

View file

@ -5,6 +5,7 @@
- Minor: Added support for FrankerFaceZ animated emotes. (#4434)
- Bugfix: Fixed an issue where animated emotes would render on top of zero-width emotes. (#4314)
- Bugfix: Fixed an issue where it was difficult to hover a zero-width emote. (#4314)
- Bugfix: Fixed an issue where context-menu items for zero-width emotes displayed the wrong provider. (#4460)
- Dev: Ignore unhandled BTTV user-events. (#4438)
- Dev: Only log debug messages when NDEBUG is not defined. (#4442)
- Dev: Cleaned up theme related code. (#4450)

View file

@ -42,6 +42,12 @@ public:
reinterpret_cast<Q &>(this->value_) |= static_cast<Q>(flag);
}
/** Adds the flags from `flags` in this enum. */
void set(FlagsEnum flags)
{
reinterpret_cast<Q &>(this->value_) |= static_cast<Q>(flags.value_);
}
void unset(T flag)
{
reinterpret_cast<Q &>(this->value_) &= ~static_cast<Q>(flag);
@ -69,6 +75,12 @@ public:
return xd;
}
FlagsEnum operator|(FlagsEnum rhs)
{
return static_cast<T>(static_cast<Q>(this->value_) |
static_cast<Q>(rhs.value_));
}
bool hasAny(FlagsEnum flags) const
{
return static_cast<Q>(this->value_) & static_cast<Q>(flags.value_);

View file

@ -110,6 +110,11 @@ MessageElementFlags MessageElement::getFlags() const
return this->flags_;
}
void MessageElement::addFlags(MessageElementFlags flags)
{
this->flags_.set(flags);
}
MessageElement *MessageElement::updateLink()
{
this->linkChanged.invoke();
@ -234,9 +239,9 @@ MessageLayoutElement *EmoteElement::makeImageLayoutElement(
return new ImageLayoutElement(*this, image, size);
}
LayeredEmoteElement::LayeredEmoteElement(std::vector<EmotePtr> &&emotes,
MessageElementFlags flags,
const MessageColor &textElementColor)
LayeredEmoteElement::LayeredEmoteElement(
std::vector<LayeredEmoteElement::Emote> &&emotes, MessageElementFlags flags,
const MessageColor &textElementColor)
: MessageElement(flags)
, emotes_(std::move(emotes))
, textElementColor_(textElementColor)
@ -244,7 +249,7 @@ LayeredEmoteElement::LayeredEmoteElement(std::vector<EmotePtr> &&emotes,
this->updateTooltips();
}
void LayeredEmoteElement::addEmoteLayer(const EmotePtr &emote)
void LayeredEmoteElement::addEmoteLayer(const LayeredEmoteElement::Emote &emote)
{
this->emotes_.push_back(emote);
this->updateTooltips();
@ -295,9 +300,9 @@ std::vector<ImagePtr> LayeredEmoteElement::getLoadedImages(float scale)
std::vector<ImagePtr> res;
res.reserve(this->emotes_.size());
for (auto emote : this->emotes_)
for (const auto &emote : this->emotes_)
{
auto image = emote->images.getImageOrLoaded(scale);
auto image = emote.ptr->images.getImageOrLoaded(scale);
if (image->isEmpty())
{
continue;
@ -327,9 +332,9 @@ void LayeredEmoteElement::updateTooltips()
std::vector<QString> result;
result.reserve(this->emotes_.size());
for (auto &emote : this->emotes_)
for (const auto &emote : this->emotes_)
{
result.push_back(emote->tooltip.string);
result.push_back(emote.ptr->tooltip.string);
}
this->emoteTooltips_ = std::move(result);
@ -349,8 +354,8 @@ QString LayeredEmoteElement::getCleanCopyString() const
{
result += " ";
}
result +=
TwitchEmotes::cleanUpEmoteCode(this->emotes_[i]->getCopyString());
result += TwitchEmotes::cleanUpEmoteCode(
this->emotes_[i].ptr->getCopyString());
}
return result;
}
@ -364,23 +369,25 @@ QString LayeredEmoteElement::getCopyString() const
{
result += " ";
}
result += this->emotes_[i]->getCopyString();
result += this->emotes_[i].ptr->getCopyString();
}
return result;
}
const std::vector<EmotePtr> &LayeredEmoteElement::getEmotes() const
const std::vector<LayeredEmoteElement::Emote> &LayeredEmoteElement::getEmotes()
const
{
return this->emotes_;
}
std::vector<EmotePtr> LayeredEmoteElement::getUniqueEmotes() const
std::vector<LayeredEmoteElement::Emote> LayeredEmoteElement::getUniqueEmotes()
const
{
// Functor for std::copy_if that keeps track of seen elements
struct NotDuplicate {
bool operator()(const EmotePtr &element)
bool operator()(const Emote &element)
{
return seen.insert(element).second;
return seen.insert(element.ptr).second;
}
private:
@ -389,7 +396,7 @@ std::vector<EmotePtr> LayeredEmoteElement::getUniqueEmotes() const
// Get unique emotes while maintaining relative layering order
NotDuplicate dup;
std::vector<EmotePtr> unique;
std::vector<Emote> unique;
std::copy_if(this->emotes_.begin(), this->emotes_.end(),
std::back_insert_iterator(unique), dup);

View file

@ -187,6 +187,7 @@ public:
const Link &getLink() const;
bool hasTrailingSpace() const;
MessageElementFlags getFlags() const;
void addFlags(MessageElementFlags flags);
MessageElement *updateLink();
virtual void addToContainer(MessageLayoutContainer &container,
@ -325,19 +326,24 @@ private:
class LayeredEmoteElement : public MessageElement
{
public:
struct Emote {
EmotePtr ptr;
MessageElementFlags flags;
};
LayeredEmoteElement(
std::vector<EmotePtr> &&emotes, MessageElementFlags flags,
std::vector<Emote> &&emotes, MessageElementFlags flags,
const MessageColor &textElementColor = MessageColor::Text);
void addEmoteLayer(const EmotePtr &emote);
void addEmoteLayer(const Emote &emote);
void addToContainer(MessageLayoutContainer &container,
MessageElementFlags flags) override;
// Returns a concatenation of each emote layer's cleaned copy string
QString getCleanCopyString() const;
const std::vector<EmotePtr> &getEmotes() const;
std::vector<EmotePtr> getUniqueEmotes() const;
const std::vector<Emote> &getEmotes() const;
std::vector<Emote> getUniqueEmotes() const;
const std::vector<QString> &getEmoteTooltips() const;
private:
@ -349,7 +355,7 @@ private:
void updateTooltips();
std::vector<ImagePtr> getLoadedImages(float scale);
std::vector<EmotePtr> emotes_;
std::vector<Emote> emotes_;
std::vector<QString> emoteTooltips_;
std::unique_ptr<TextElement> textElement_;

View file

@ -1076,17 +1076,20 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
// Need to remove EmoteElement and replace with LayeredEmoteElement
auto baseEmoteElement = this->releaseBack();
std::vector<EmotePtr> layers = {baseEmote, emote.get()};
this->emplace<LayeredEmoteElement>(std::move(layers),
baseEmoteElement->getFlags(),
this->textColor_);
std::vector<LayeredEmoteElement::Emote> layers = {
{baseEmote, baseEmoteElement->getFlags()},
{emote.get(), flags}};
this->emplace<LayeredEmoteElement>(
std::move(layers), baseEmoteElement->getFlags() | flags,
this->textColor_);
return Success;
}
auto asLayered = dynamic_cast<LayeredEmoteElement *>(&this->back());
if (asLayered)
{
asLayered->addEmoteLayer(emote.get());
asLayered->addEmoteLayer({emote.get(), flags});
asLayered->addFlags(flags);
return Success;
}

View file

@ -1710,7 +1710,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
for (size_t i = 0; i < upperLimit; ++i)
{
auto &emote = layeredEmotes[i];
const auto &emote = layeredEmotes[i].ptr;
if (i == 0)
{
// First entry gets a large image and full description
@ -2202,10 +2202,10 @@ void ChannelView::addImageContextMenuItems(
// Give each emote its own submenu
for (auto &emote : layeredElement->getUniqueEmotes())
{
auto emoteAction = menu.addAction(emote->name.string);
auto emoteAction = menu.addAction(emote.ptr->name.string);
auto emoteMenu = new QMenu(&menu);
emoteAction->setMenu(emoteMenu);
addEmoteContextMenuItems(*emote, creatorFlags, *emoteMenu);
addEmoteContextMenuItems(*emote.ptr, emote.flags, *emoteMenu);
}
}
}