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) - 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 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 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: Ignore unhandled BTTV user-events. (#4438)
- Dev: Only log debug messages when NDEBUG is not defined. (#4442) - Dev: Only log debug messages when NDEBUG is not defined. (#4442)
- Dev: Cleaned up theme related code. (#4450) - Dev: Cleaned up theme related code. (#4450)

View file

@ -42,6 +42,12 @@ public:
reinterpret_cast<Q &>(this->value_) |= static_cast<Q>(flag); 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) void unset(T flag)
{ {
reinterpret_cast<Q &>(this->value_) &= ~static_cast<Q>(flag); reinterpret_cast<Q &>(this->value_) &= ~static_cast<Q>(flag);
@ -69,6 +75,12 @@ public:
return xd; 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 bool hasAny(FlagsEnum flags) const
{ {
return static_cast<Q>(this->value_) & static_cast<Q>(flags.value_); return static_cast<Q>(this->value_) & static_cast<Q>(flags.value_);

View file

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

View file

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

View file

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

View file

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