From 6d6b99f3efe2325046290c5eb3a9e025c5473e4c Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 17 Jan 2018 14:14:31 +0100 Subject: [PATCH] added moderation buttons --- chatterino.pro | 6 +- src/messages/layouts/messagelayoutelement.cpp | 90 +++++++++++++++++-- src/messages/layouts/messagelayoutelement.hpp | 32 ++++++- src/messages/link.hpp | 1 + src/messages/messageelement.cpp | 33 +++++-- src/messages/messageelement.hpp | 4 +- src/singletons/fontmanager.cpp | 48 +++++----- src/singletons/fontmanager.hpp | 17 ++-- src/singletons/helper/moderationaction.cpp | 50 +++++++++++ src/singletons/helper/moderationaction.hpp | 31 +++++++ src/singletons/settingsmanager.cpp | 75 ++++++++++++++++ src/singletons/settingsmanager.hpp | 6 ++ src/twitch/twitchmessagebuilder.cpp | 31 ++++--- src/widgets/helper/channelview.cpp | 9 +- src/widgets/settingspages/moderationpage.cpp | 10 +-- 15 files changed, 367 insertions(+), 76 deletions(-) create mode 100644 src/singletons/helper/moderationaction.cpp create mode 100644 src/singletons/helper/moderationaction.hpp diff --git a/chatterino.pro b/chatterino.pro index c844c745a..0b82abade 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -127,7 +127,8 @@ SOURCES += \ src/widgets/settingspages/aboutpage.cpp \ src/widgets/settingspages/moderationpage.cpp \ src/widgets/settingspages/logspage.cpp \ - src/widgets/basewindow.cpp + src/widgets/basewindow.cpp \ + src/singletons/helper/moderationaction.cpp HEADERS += \ src/precompiled_headers.hpp \ @@ -226,7 +227,8 @@ HEADERS += \ src/widgets/settingspages/aboutpage.hpp \ src/widgets/settingspages/moderationpage.hpp \ src/widgets/settingspages/logspage.hpp \ - src/widgets/basewindow.hpp + src/widgets/basewindow.hpp \ + src/singletons/helper/moderationaction.hpp PRECOMPILED_HEADER = diff --git a/src/messages/layouts/messagelayoutelement.cpp b/src/messages/layouts/messagelayoutelement.cpp index 4cef16981..b168a4637 100644 --- a/src/messages/layouts/messagelayoutelement.cpp +++ b/src/messages/layouts/messagelayoutelement.cpp @@ -41,11 +41,22 @@ MessageLayoutElement *MessageLayoutElement::setTrailingSpace(bool value) return this; } +MessageLayoutElement *MessageLayoutElement::setLink(const Link &_link) +{ + this->link = _link; + return this; +} + +const Link &MessageLayoutElement::getLink() const +{ + return this->link; +} + // // IMAGE // -ImageLayoutElement::ImageLayoutElement(MessageElement &_creator, Image &_image, QSize _size) +ImageLayoutElement::ImageLayoutElement(MessageElement &_creator, Image *_image, const QSize &_size) : MessageLayoutElement(_creator, _size) , image(_image) { @@ -54,7 +65,7 @@ ImageLayoutElement::ImageLayoutElement(MessageElement &_creator, Image &_image, void ImageLayoutElement::addCopyTextToString(QString &str, int from, int to) const { - str += this->image.getName(); + str += this->image->getName(); if (this->hasTrailingSpace()) { str += " "; @@ -68,9 +79,9 @@ int ImageLayoutElement::getSelectionIndexCount() void ImageLayoutElement::paint(QPainter &painter) { - const QPixmap *pixmap = this->image.getPixmap(); + const QPixmap *pixmap = this->image->getPixmap(); - if (pixmap != nullptr && !this->image.isAnimated()) { + if (pixmap != nullptr && !this->image->isAnimated()) { // fourtf: make it use qreal values painter.drawPixmap(QRectF(this->getRect()), *pixmap, QRectF()); } @@ -78,12 +89,12 @@ void ImageLayoutElement::paint(QPainter &painter) void ImageLayoutElement::paintAnimated(QPainter &painter, int yOffset) { - if (this->image.isAnimated()) { - if (this->image.getPixmap() != nullptr) { + if (this->image->isAnimated()) { + if (this->image->getPixmap() != nullptr) { // fourtf: make it use qreal values QRect rect = this->getRect(); rect.moveTop(rect.y() + yOffset); - painter.drawPixmap(QRectF(rect), *this->image.getPixmap(), QRectF()); + painter.drawPixmap(QRectF(rect), *this->image->getPixmap(), QRectF()); } } } @@ -109,7 +120,7 @@ int ImageLayoutElement::getXFromIndex(int index) // TEXT // -TextLayoutElement::TextLayoutElement(MessageElement &_creator, QString &_text, QSize _size, +TextLayoutElement::TextLayoutElement(MessageElement &_creator, QString &_text, const QSize &_size, QColor _color, FontStyle _style, float _scale) : MessageLayoutElement(_creator, _size) , text(_text) @@ -188,6 +199,69 @@ int TextLayoutElement::getXFromIndex(int index) return this->getRect().right(); } } + +// TEXT ICON +TextIconLayoutElement::TextIconLayoutElement(MessageElement &creator, const QString &_line1, + const QString &_line2, float _scale, const QSize &size) + : MessageLayoutElement(creator, size) + , scale(_scale) + , line1(_line1) + , line2(_line2) +{ +} + +void TextIconLayoutElement::addCopyTextToString(QString &str, int from, int to) const +{ +} + +int TextIconLayoutElement::getSelectionIndexCount() +{ + return this->trailingSpace ? 2 : 1; +} + +void TextIconLayoutElement::paint(QPainter &painter) +{ + QFont font = singletons::FontManager::getInstance().getFont(FontStyle::Tiny, this->scale); + + painter.setBrush(singletons::ThemeManager::getInstance().messages.textColors.regular); + painter.setFont(font); + + QTextOption option; + option.setAlignment(Qt::AlignHCenter); + + if (this->line2.isEmpty()) { + QRect rect(this->getRect()); + painter.drawText(rect, this->line1, option); + } else { + painter.drawText( + QPoint(this->getRect().x(), this->getRect().y() + this->getRect().height() / 2), + this->line1); + painter.drawText( + QPoint(this->getRect().x(), this->getRect().y() + this->getRect().height()), + this->line2); + } +} + +void TextIconLayoutElement::paintAnimated(QPainter &painter, int yOffset) +{ +} + +int TextIconLayoutElement::getMouseOverIndex(const QPoint &abs) +{ + return 0; +} + +int TextIconLayoutElement::getXFromIndex(int index) +{ + if (index <= 0) { + return this->getRect().left(); + } else if (index == 1) { + // fourtf: remove space width + return this->getRect().right(); + } else { + return this->getRect().right(); + } +} } // namespace layouts } // namespace messages } // namespace chatterino diff --git a/src/messages/layouts/messagelayoutelement.hpp b/src/messages/layouts/messagelayoutelement.hpp index 3bf05362b..38f8ae3a6 100644 --- a/src/messages/layouts/messagelayoutelement.hpp +++ b/src/messages/layouts/messagelayoutelement.hpp @@ -7,6 +7,7 @@ #include #include +#include "messages/link.hpp" #include "messages/messagecolor.hpp" #include "singletons/fontmanager.hpp" @@ -30,6 +31,7 @@ public: bool hasTrailingSpace() const; MessageLayoutElement *setTrailingSpace(bool value); + MessageLayoutElement *setLink(const Link &link); virtual void addCopyTextToString(QString &str, int from = 0, int to = INT_MAX) const = 0; virtual int getSelectionIndexCount() = 0; @@ -37,12 +39,14 @@ public: virtual void paintAnimated(QPainter &painter, int yOffset) = 0; virtual int getMouseOverIndex(const QPoint &abs) = 0; virtual int getXFromIndex(int index) = 0; + const Link &getLink() const; protected: bool trailingSpace = true; private: QRect rect; + Link link; // bool isInNewLine; MessageElement &creator; }; @@ -51,7 +55,7 @@ private: class ImageLayoutElement : public MessageLayoutElement { public: - ImageLayoutElement(MessageElement &creator, Image &image, QSize size); + ImageLayoutElement(MessageElement &creator, Image *image, const QSize &size); protected: virtual void addCopyTextToString(QString &str, int from = 0, int to = INT_MAX) const override; @@ -62,14 +66,14 @@ protected: virtual int getXFromIndex(int index) override; private: - Image ℑ + Image *image; }; // TEXT class TextLayoutElement : public MessageLayoutElement { public: - TextLayoutElement(MessageElement &creator, QString &text, QSize size, QColor color, + TextLayoutElement(MessageElement &creator, QString &text, const QSize &size, QColor color, FontStyle style, float scale); protected: @@ -86,6 +90,28 @@ private: FontStyle style; float scale; }; + +// TEXT ICON +// two lines of text (characters) in the size of a normal chat badge +class TextIconLayoutElement : public MessageLayoutElement +{ +public: + TextIconLayoutElement(MessageElement &creator, const QString &line1, const QString &line2, + float scale, const QSize &size); + +protected: + virtual void addCopyTextToString(QString &str, int from = 0, int to = INT_MAX) const override; + virtual int getSelectionIndexCount() override; + virtual void paint(QPainter &painter) override; + virtual void paintAnimated(QPainter &painter, int yOffset) override; + virtual int getMouseOverIndex(const QPoint &abs) override; + virtual int getXFromIndex(int index) override; + +private: + QString line1; + QString line2; + float scale; +}; } // namespace layouts } // namespace messages } // namespace chatterino diff --git a/src/messages/link.hpp b/src/messages/link.hpp index abb3d498c..8a10dc21a 100644 --- a/src/messages/link.hpp +++ b/src/messages/link.hpp @@ -17,6 +17,7 @@ public: UserBan, InsertText, ShowMessage, + UserAction, }; Link(); diff --git a/src/messages/messageelement.cpp b/src/messages/messageelement.cpp index bae2e222a..94ea99d10 100644 --- a/src/messages/messageelement.cpp +++ b/src/messages/messageelement.cpp @@ -52,19 +52,20 @@ MessageElement::Flags MessageElement::getFlags() const } // IMAGE -ImageElement::ImageElement(Image &_image, MessageElement::Flags flags) +ImageElement::ImageElement(Image *_image, MessageElement::Flags flags) : MessageElement(flags) , image(_image) { - this->setTooltip(_image.getTooltip()); + this->setTooltip(_image->getTooltip()); } void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags) { - QSize size(this->image.getWidth() * this->image.getScale() * container.scale, - this->image.getHeight() * this->image.getScale() * container.scale); + QSize size(this->image->getWidth() * this->image->getScale() * container.scale, + this->image->getHeight() * this->image->getScale() * container.scale); - container.addElement(new ImageLayoutElement(*this, this->image, size)); + container.addElement( + (new ImageLayoutElement(*this, this->image, size))->setLink(this->getLink())); } void ImageElement::update(UpdateFlags _flags) @@ -102,7 +103,7 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container, MessageElem QSize size((int)(container.scale * _image->getScaledWidth()), (int)(container.scale * _image->getScaledHeight())); - container.addElement(new ImageLayoutElement(*this, *_image, size)); + container.addElement((new ImageLayoutElement(*this, _image, size))->setLink(this->getLink())); } void EmoteElement::update(UpdateFlags _flags) @@ -130,9 +131,10 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme for (Word &word : this->words) { auto getTextLayoutElement = [&](QString text, int width, bool trailingSpace) { - auto e = new TextLayoutElement(*this, text, QSize(width, metrics.height()), - this->color.getColor(themeManager), this->style, - container.scale); + auto e = (new TextLayoutElement(*this, text, QSize(width, metrics.height()), + this->color.getColor(themeManager), this->style, + container.scale)) + ->setLink(this->getLink()); e->setTrailingSpace(trailingSpace); return e; }; @@ -254,6 +256,19 @@ TwitchModerationElement::TwitchModerationElement() void TwitchModerationElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags) { + QSize size((int)(container.scale * 16), (int)(container.scale * 16)); + + for (const singletons::ModerationAction &m : + singletons::SettingManager::getInstance().getModerationActions()) { + if (m.isImage()) { + container.addElement((new ImageLayoutElement(*this, m.getImage(), size)) + ->setLink(Link(Link::UserAction, m.getAction()))); + } else { + container.addElement((new TextIconLayoutElement(*this, m.getLine1(), m.getLine2(), + container.scale, size)) + ->setLink(Link(Link::UserAction, m.getAction()))); + } + } } void TwitchModerationElement::update(UpdateFlags _flags) diff --git a/src/messages/messageelement.hpp b/src/messages/messageelement.hpp index 829a9b82f..33cd09edb 100644 --- a/src/messages/messageelement.hpp +++ b/src/messages/messageelement.hpp @@ -137,10 +137,10 @@ private: // contains a simple image class ImageElement : public MessageElement { - Image ℑ + Image *image; public: - ImageElement(Image &image, MessageElement::Flags flags); + ImageElement(Image *image, MessageElement::Flags flags); virtual void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; diff --git a/src/singletons/fontmanager.cpp b/src/singletons/fontmanager.cpp index 3952976c6..d464cdb47 100644 --- a/src/singletons/fontmanager.cpp +++ b/src/singletons/fontmanager.cpp @@ -4,16 +4,16 @@ #include #ifdef Q_OS_WIN32 - #define DEFAULT_FONT_FAMILY "Segoe UI" - #define DEFAULT_FONT_SIZE 10 +#define DEFAULT_FONT_FAMILY "Segoe UI" +#define DEFAULT_FONT_SIZE 10 #else - #ifdef Q_OS_MACOS - #define DEFAULT_FONT_FAMILY "Helvetica Neue" - #define DEFAULT_FONT_SIZE 12 - #else - #define DEFAULT_FONT_FAMILY "Arial" - #define DEFAULT_FONT_SIZE 11 - #endif +#ifdef Q_OS_MACOS +#define DEFAULT_FONT_FAMILY "Helvetica Neue" +#define DEFAULT_FONT_SIZE 12 +#else +#define DEFAULT_FONT_FAMILY "Arial" +#define DEFAULT_FONT_SIZE 11 +#endif #endif namespace chatterino { @@ -27,13 +27,13 @@ FontManager::FontManager() this->currentFontFamily.connect([this](const std::string &newValue, auto) { this->incGeneration(); // this->currentFont.setFamily(newValue.c_str()); - this->currentFontByDpi.clear(); + this->currentFontByScale.clear(); this->fontChanged.invoke(); }); this->currentFontSize.connect([this](const int &newValue, auto) { this->incGeneration(); // this->currentFont.setSize(newValue); - this->currentFontByDpi.clear(); + this->currentFontByScale.clear(); this->fontChanged.invoke(); }); } @@ -45,21 +45,23 @@ FontManager &FontManager::getInstance() return instance; } -QFont &FontManager::getFont(Type type, float dpi) +QFont &FontManager::getFont(Type type, float scale) { // return this->currentFont.getFont(type); - return this->getCurrentFont(dpi).getFont(type); + return this->getCurrentFont(scale).getFont(type); } -QFontMetrics &FontManager::getFontMetrics(Type type, float dpi) +QFontMetrics &FontManager::getFontMetrics(Type type, float scale) { // return this->currentFont.getFontMetrics(type); - return this->getCurrentFont(dpi).getFontMetrics(type); + return this->getCurrentFont(scale).getFontMetrics(type); } FontManager::FontData &FontManager::Font::getFontData(Type type) { switch (type) { + case Tiny: + return this->tiny; case Small: return this->small; case MediumSmall: @@ -90,18 +92,18 @@ QFontMetrics &FontManager::Font::getFontMetrics(Type type) return this->getFontData(type).metrics; } -FontManager::Font &FontManager::getCurrentFont(float dpi) +FontManager::Font &FontManager::getCurrentFont(float scale) { - for (auto it = this->currentFontByDpi.begin(); it != this->currentFontByDpi.end(); it++) { - if (it->first == dpi) { + for (auto it = this->currentFontByScale.begin(); it != this->currentFontByScale.end(); it++) { + if (it->first == scale) { return it->second; } } - this->currentFontByDpi.push_back(std::make_pair( - dpi, - Font(this->currentFontFamily.getValue().c_str(), this->currentFontSize.getValue() * dpi))); + this->currentFontByScale.push_back( + std::make_pair(scale, Font(this->currentFontFamily.getValue().c_str(), + this->currentFontSize.getValue() * scale))); - return this->currentFontByDpi.back().second; + return this->currentFontByScale.back().second; } +} // namespace singletons } // namespace chatterino -} diff --git a/src/singletons/fontmanager.hpp b/src/singletons/fontmanager.hpp index c7ca84685..3f59c8d1c 100644 --- a/src/singletons/fontmanager.hpp +++ b/src/singletons/fontmanager.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -16,6 +17,7 @@ class FontManager public: enum Type : uint8_t { + Tiny, Small, MediumSmall, Medium, @@ -28,8 +30,8 @@ public: // FontManager is initialized only once, on first use static FontManager &getInstance(); - QFont &getFont(Type type, float dpi); - QFontMetrics &getFontMetrics(Type type, float dpi); + QFont &getFont(Type type, float scale); + QFontMetrics &getFontMetrics(Type type, float scale); int getGeneration() const { @@ -62,7 +64,8 @@ private: Font() = delete; explicit Font(const char *fontFamilyName, int mediumSize) - : small(QFont(fontFamilyName, mediumSize - 4)) + : tiny(QFont("Monospace", 8)) + , small(QFont(fontFamilyName, mediumSize - 4)) , mediumSmall(QFont(fontFamilyName, mediumSize - 2)) , medium(QFont(fontFamilyName, mediumSize)) , mediumBold(QFont(fontFamilyName, mediumSize, QFont::DemiBold)) @@ -70,6 +73,7 @@ private: , large(QFont(fontFamilyName, mediumSize)) , veryLarge(QFont(fontFamilyName, mediumSize)) { + tiny.font.setStyleHint(QFont::TypeWriter); } void setFamily(const char *newFamily) @@ -114,6 +118,7 @@ private: QFont &getFont(Type type); QFontMetrics &getFontMetrics(Type type); + FontData tiny; FontData small; FontData mediumSmall; FontData medium; @@ -123,16 +128,16 @@ private: FontData veryLarge; }; - Font &getCurrentFont(float dpi); + Font &getCurrentFont(float scale); // Future plans: // Could have multiple fonts in here, such as "Menu font", "Application font", "Chat font" - std::list> currentFontByDpi; + std::list> currentFontByScale; int generation = 0; }; -} +} // namespace singletons typedef singletons::FontManager::Type FontStyle; } // namespace chatterino diff --git a/src/singletons/helper/moderationaction.cpp b/src/singletons/helper/moderationaction.cpp new file mode 100644 index 000000000..47433fe03 --- /dev/null +++ b/src/singletons/helper/moderationaction.cpp @@ -0,0 +1,50 @@ +#include "moderationaction.hpp" + +#include "singletons/resourcemanager.hpp" + +namespace chatterino { +namespace singletons { + +ModerationAction::ModerationAction(messages::Image *_image, const QString &_action) + : _isImage(true) + , image(_image) + , action(_action) +{ +} + +ModerationAction::ModerationAction(const QString &_line1, const QString &_line2, + const QString &_action) + : _isImage(false) + , image(nullptr) + , line1(_line1) + , line2(_line2) + , action(_action) +{ +} + +bool ModerationAction::isImage() const +{ + return this->_isImage; +} + +messages::Image *ModerationAction::getImage() const +{ + return this->image; +} + +const QString &ModerationAction::getLine1() const +{ + return this->line1; +} + +const QString &ModerationAction::getLine2() const +{ + return this->line2; +} + +const QString &ModerationAction::getAction() const +{ + return this->action; +} +} // namespace singletons +} // namespace chatterino diff --git a/src/singletons/helper/moderationaction.hpp b/src/singletons/helper/moderationaction.hpp new file mode 100644 index 000000000..1170df80b --- /dev/null +++ b/src/singletons/helper/moderationaction.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace chatterino { +namespace messages { +class Image; +} +namespace singletons { + +class ModerationAction +{ +public: + ModerationAction(messages::Image *image, const QString &action); + ModerationAction(const QString &line1, const QString &line2, const QString &action); + + bool isImage() const; + messages::Image *getImage() const; + const QString &getLine1() const; + const QString &getLine2() const; + const QString &getAction() const; + +private: + bool _isImage; + messages::Image *image; + QString line1; + QString line2; + QString action; +}; +} // namespace singletons +} // namespace chatterino diff --git a/src/singletons/settingsmanager.cpp b/src/singletons/settingsmanager.cpp index 2c227d4a2..53a06b5ea 100644 --- a/src/singletons/settingsmanager.cpp +++ b/src/singletons/settingsmanager.cpp @@ -1,6 +1,7 @@ #include "singletons/settingsmanager.hpp" #include "debug/log.hpp" #include "singletons/pathmanager.hpp" +#include "singletons/resourcemanager.hpp" using namespace chatterino::messages; @@ -26,6 +27,8 @@ SettingManager::SettingManager() this->wordMaskListener.cb = [this](auto) { this->updateWordTypeMask(); // }; + + this->moderationActions.connect([this](auto, auto) { this->updateModerationActions(); }); } MessageElement::Flags SettingManager::getWordTypeMask() @@ -127,5 +130,77 @@ void SettingManager::recallSnapshot() } } +std::vector SettingManager::getModerationActions() const +{ + return this->_moderationActions; +} + +void SettingManager::updateModerationActions() +{ + auto &resources = singletons::ResourceManager::getInstance(); + + this->_moderationActions.clear(); + + static QRegularExpression newLineRegex("(\r\n?|\n)+"); + static QRegularExpression replaceRegex("[!/.]"); + static QRegularExpression timeoutRegex("^[./]timeout.* (\\d+)"); + QStringList list = this->moderationActions.getValue().split(newLineRegex); + + int multipleTimeouts = 0; + + for (QString &str : list) { + if (timeoutRegex.match(str).hasMatch()) { + multipleTimeouts++; + if (multipleTimeouts > 1) { + break; + } + } + } + + for (int i = 0; i < list.size(); i++) { + QString &str = list[i]; + + if (str.isEmpty()) { + continue; + } + + auto timeoutMatch = timeoutRegex.match(str); + + if (timeoutMatch.hasMatch()) { + if (multipleTimeouts > 1) { + QString line1; + QString line2; + + int amount = timeoutMatch.captured(1).toInt(); + + if (amount < 60) { + line1 = QString::number(amount); + line2 = "s"; + } else if (amount < 60 * 60) { + line1 = QString::number(amount / 60); + line2 = "m"; + } else if (amount < 60 * 60 * 24) { + line1 = QString::number(amount / 60 / 60); + line2 = "h"; + } else { + line1 = QString::number(amount / 60 / 60 / 24); + line2 = "d"; + } + + this->_moderationActions.emplace_back(line1, line2, str); + } else { + this->_moderationActions.emplace_back(resources.buttonTimeout, str); + } + } else if (str.startsWith("/ban ")) { + this->_moderationActions.emplace_back(resources.buttonBan, str); + } else { + QString xD = str; + + xD.replace(replaceRegex, ""); + + this->_moderationActions.emplace_back(xD.mid(0, 2), xD.mid(2, 2), str); + } + } +} } // namespace singletons } // namespace chatterino diff --git a/src/singletons/settingsmanager.hpp b/src/singletons/settingsmanager.hpp index 8f7ecbcbc..d9bcab8a7 100644 --- a/src/singletons/settingsmanager.hpp +++ b/src/singletons/settingsmanager.hpp @@ -3,6 +3,7 @@ #include "messages/highlightphrase.hpp" #include "messages/messageelement.hpp" #include "singletons/helper/chatterinosetting.hpp" +#include "singletons/helper/moderationaction.hpp" #include #include @@ -105,14 +106,19 @@ public: void saveSnapshot(); void recallSnapshot(); + std::vector getModerationActions() const; + signals: void wordTypeMaskChanged(); private: + std::vector _moderationActions; std::unique_ptr snapshot; SettingManager(); + void updateModerationActions(); + messages::MessageElement::Flags wordTypeMask = messages::MessageElement::Default; pajlada::Settings::SettingListener wordMaskListener; diff --git a/src/twitch/twitchmessagebuilder.cpp b/src/twitch/twitchmessagebuilder.cpp index f7f56361b..9a9d213b6 100644 --- a/src/twitch/twitchmessagebuilder.cpp +++ b/src/twitch/twitchmessagebuilder.cpp @@ -511,7 +511,7 @@ void TwitchMessageBuilder::parseTwitchBadges() // Try to fetch channel-specific bit badge try { const auto &badge = channelResources.badgeSets.at("bits").versions.at(versionKey); - this->append(*(badge.badgeImage1x), MessageElement::BadgeVanity); + this->append(badge.badgeImage1x, MessageElement::BadgeVanity); continue; } catch (const std::out_of_range &) { // Channel does not contain a special bit badge for this version @@ -520,44 +520,44 @@ void TwitchMessageBuilder::parseTwitchBadges() // Use default bit badge try { const auto &badge = resourceManager.badgeSets.at("bits").versions.at(versionKey); - this->append(*(badge.badgeImage1x), MessageElement::BadgeVanity); + this->append(badge.badgeImage1x, MessageElement::BadgeVanity); } catch (const std::out_of_range &) { debug::Log("No default bit badge for version {} found", versionKey); continue; } } else if (badge == "staff/1") { - this->append(*resourceManager.badgeStaff, + this->append(resourceManager.badgeStaff, MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Staff"); } else if (badge == "admin/1") { - this->append(*resourceManager.badgeAdmin, + this->append(resourceManager.badgeAdmin, MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Admin"); } else if (badge == "global_mod/1") { - this->append(*resourceManager.badgeGlobalModerator, + this->append(resourceManager.badgeGlobalModerator, MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Global Moderator"); } else if (badge == "moderator/1") { // TODO: Implement custom FFZ moderator badge - this->append(*resourceManager.badgeModerator, + this->append(resourceManager.badgeModerator, MessageElement::BadgeChannelAuthority) ->setTooltip("Twitch Channel Moderator"); } else if (badge == "turbo/1") { - this->append(*resourceManager.badgeTurbo, + this->append(resourceManager.badgeTurbo, MessageElement::BadgeGlobalAuthority) ->setTooltip("Twitch Turbo Subscriber"); } else if (badge == "broadcaster/1") { - this->append(*resourceManager.badgeBroadcaster, + this->append(resourceManager.badgeBroadcaster, MessageElement::BadgeChannelAuthority) ->setTooltip("Twitch Broadcaster"); } else if (badge == "premium/1") { - this->append(*resourceManager.badgePremium, MessageElement::BadgeVanity) + this->append(resourceManager.badgePremium, MessageElement::BadgeVanity) ->setTooltip("Twitch Prime Subscriber"); } else if (badge.startsWith("partner/")) { int index = badge.midRef(8).toInt(); switch (index) { case 1: { - this->append(*resourceManager.badgeVerified, + this->append(resourceManager.badgeVerified, MessageElement::BadgeVanity) ->setTooltip("Twitch Verified"); } break; @@ -574,7 +574,7 @@ void TwitchMessageBuilder::parseTwitchBadges() auto badgeSetIt = channelResources.badgeSets.find("subscriber"); if (badgeSetIt == channelResources.badgeSets.end()) { // Fall back to default badge - this->append(*resourceManager.badgeSubscriber, + this->append(resourceManager.badgeSubscriber, MessageElement::BadgeSubscription) ->setTooltip("Twitch Subscriber"); continue; @@ -588,7 +588,7 @@ void TwitchMessageBuilder::parseTwitchBadges() if (badgeVersionIt == badgeSet.versions.end()) { // Fall back to default badge - this->append(*resourceManager.badgeSubscriber, + this->append(resourceManager.badgeSubscriber, MessageElement::BadgeSubscription) ->setTooltip("Twitch Subscriber"); continue; @@ -596,8 +596,7 @@ void TwitchMessageBuilder::parseTwitchBadges() auto &badgeVersion = badgeVersionIt->second; - this->append(*badgeVersion.badgeImage1x, - MessageElement::BadgeSubscription) + this->append(badgeVersion.badgeImage1x, MessageElement::BadgeSubscription) ->setTooltip("Twitch " + QString::fromStdString(badgeVersion.title)); } else { if (!resourceManager.dynamicBadgesLoaded) { @@ -623,7 +622,7 @@ void TwitchMessageBuilder::parseTwitchBadges() try { auto &badgeVersion = badgeSet.versions.at(versionKey); - this->append(*badgeVersion.badgeImage1x, badgeType) + this->append(badgeVersion.badgeImage1x, badgeType) ->setTooltip("Twitch " + QString::fromStdString(badgeVersion.title)); } catch (const std::exception &e) { qDebug() << "Exception caught:" << e.what() @@ -648,7 +647,7 @@ void TwitchMessageBuilder::addChatterinoBadges() const auto badge = it->second; - this->append(*badge->image, MessageElement::BadgeChatterino) + this->append(badge->image, MessageElement::BadgeChatterino) ->setTooltip(QString::fromStdString(badge->tooltip)); } diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 6010c3fe8..2184d4612 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -641,7 +641,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) } // check if word has a link - if (hoverLayoutElement->getCreator().getLink().isValid()) { + if (hoverLayoutElement->getLink().isValid()) { this->setCursor(Qt::PointingHandCursor); } else { this->setCursor(Qt::ArrowCursor); @@ -764,7 +764,7 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) return; } - auto &link = hoverLayoutElement->getCreator().getLink(); + auto &link = hoverLayoutElement->getLink(); switch (link.getType()) { case messages::Link::UserInfo: { @@ -782,6 +782,11 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event) QDesktopServices::openUrl(QUrl(link.getValue())); break; } + case messages::Link::UserAction: { + QString value = link.getValue(); + value.replace("{user}", layout->getMessage()->loginName); + this->channel->sendMessage(value); + } } } diff --git a/src/widgets/settingspages/moderationpage.cpp b/src/widgets/settingspages/moderationpage.cpp index c1ab7157a..e2e7d2597 100644 --- a/src/widgets/settingspages/moderationpage.cpp +++ b/src/widgets/settingspages/moderationpage.cpp @@ -21,21 +21,21 @@ ModerationPage::ModerationPage() auto layout = layoutCreator.emplace().withoutMargin(); { // clang-format off - auto label = layout.emplace("In channels that you moderate there is a button to enable moderation mode."); + auto label = layout.emplace("In channels that you moderate there is a button to enable moderation mode.\n\nOne action per line. {user} will be replaced with the username.\nExample `/timeout {user} 120`"); label->setWordWrap(true); // clang-format on auto text = layout.emplace().getElement(); + settings.moderationActions.connect([=](const QString &str, auto) { + text->setPlainText(str); // + }); + QObject::connect(text, &QTextEdit::textChanged, this, [this] { this->itemsChangedTimer.start(200); }); QObject::connect(&this->itemsChangedTimer, &QTimer::timeout, this, [text, &settings]() { settings.moderationActions = text->toPlainText(); }); - - settings.highlightUserBlacklist.connect([=](const QString &str, auto) { - text->setPlainText(str); // - }); } // ---- misc