diff --git a/resources/images/moderatormode_disabled.png b/resources/images/moderatormode_disabled.png new file mode 100644 index 000000000..b0a063072 Binary files /dev/null and b/resources/images/moderatormode_disabled.png differ diff --git a/resources/images/moderatormode_enabled.png b/resources/images/moderatormode_enabled.png new file mode 100644 index 000000000..a563c4ba4 Binary files /dev/null and b/resources/images/moderatormode_enabled.png differ diff --git a/resources/resources.qrc b/resources/resources.qrc index 73976001b..cb535c604 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -44,6 +44,8 @@ images/commands.svg images/aboutlogo.png images/about.svg + images/moderatormode_disabled.png + images/moderatormode_enabled.png qt.conf diff --git a/src/messages/layouts/messagelayout.cpp b/src/messages/layouts/messagelayout.cpp index acddd5e56..af4909ca9 100644 --- a/src/messages/layouts/messagelayout.cpp +++ b/src/messages/layouts/messagelayout.cpp @@ -61,7 +61,7 @@ void MessageLayout::removeFlags(Flags _flags) // Layout // return true if redraw is required -bool MessageLayout::layout(int width, float scale) +bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags) { auto &emoteManager = singletons::EmoteManager::getInstance(); @@ -114,20 +114,20 @@ bool MessageLayout::layout(int width, float scale) return false; } - this->actuallyLayout(width); + this->actuallyLayout(width, flags); this->invalidateBuffer(); return true; } -void MessageLayout::actuallyLayout(int width) +void MessageLayout::actuallyLayout(int width, MessageElement::Flags flags) { this->container.clear(); this->container.width = width; this->container.scale = this->scale; for (const std::unique_ptr &element : this->message->getElements()) { - element->addToContainer(this->container, MessageElement::Default); + element->addToContainer(this->container, flags); } if (this->height != this->container.getHeight()) { diff --git a/src/messages/layouts/messagelayout.hpp b/src/messages/layouts/messagelayout.hpp index 08fac98e7..7792c1f6b 100644 --- a/src/messages/layouts/messagelayout.hpp +++ b/src/messages/layouts/messagelayout.hpp @@ -38,7 +38,7 @@ public: void removeFlags(Flags flags); // Layout - bool layout(int width, float scale); + bool layout(int width, float scale, MessageElement::Flags flags); // Painting void paint(QPainter &painter, int y, int messageIndex, Selection &selection); @@ -75,7 +75,7 @@ private: int collapsedHeight = 32; // methods - void actuallyLayout(int width); + void actuallyLayout(int width, MessageElement::Flags flags); void updateBuffer(QPixmap *pixmap, int messageIndex, Selection &selection); }; diff --git a/src/messages/messageelement.cpp b/src/messages/messageelement.cpp index 94ea99d10..193ae3c4b 100644 --- a/src/messages/messageelement.cpp +++ b/src/messages/messageelement.cpp @@ -68,10 +68,6 @@ void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElem (new ImageLayoutElement(*this, this->image, size))->setLink(this->getLink())); } -void ImageElement::update(UpdateFlags _flags) -{ -} - // EMOTE EmoteElement::EmoteElement(const util::EmoteData &_data, MessageElement::Flags flags) : MessageElement(flags) @@ -106,10 +102,6 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container, MessageElem container.addElement((new ImageLayoutElement(*this, _image, size))->setLink(this->getLink())); } -void EmoteElement::update(UpdateFlags _flags) -{ -} - // TEXT TextElement::TextElement(const QString &text, MessageElement::Flags flags, const MessageColor &_color, FontStyle _style) @@ -195,15 +187,6 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme } } -void TextElement::update(UpdateFlags _flags) -{ - if (_flags & UpdateFlags::Update_Text) { - for (Word &word : this->words) { - word.width = -1; - } - } -} - // TIMESTAMP TimestampElement::TimestampElement() : TimestampElement(QTime::currentTime()) @@ -235,11 +218,6 @@ void TimestampElement::addToContainer(MessageLayoutContainer &container, this->element->addToContainer(container, _flags); } -void TimestampElement::update(UpdateFlags _flags) -{ - this->element->update(_flags); -} - TextElement *TimestampElement::formatTime(const QTime &time) { QString format = time.toString(singletons::SettingManager::getInstance().timestampFormat); @@ -256,23 +234,24 @@ TwitchModerationElement::TwitchModerationElement() void TwitchModerationElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags) { - QSize size((int)(container.scale * 16), (int)(container.scale * 16)); + qDebug() << _flags; - 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()))); + if (_flags & MessageElement::ModeratorTools) { + 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) -{ -} } // namespace messages } // namespace chatterino diff --git a/src/messages/messageelement.hpp b/src/messages/messageelement.hpp index 33cd09edb..dd3e7c482 100644 --- a/src/messages/messageelement.hpp +++ b/src/messages/messageelement.hpp @@ -122,7 +122,6 @@ public: Flags getFlags() const; virtual void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) = 0; - virtual void update(UpdateFlags flags) = 0; protected: MessageElement(Flags flags); @@ -144,7 +143,6 @@ public: virtual void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; - virtual void update(UpdateFlags flags) override; }; // contains emote data and will pick the emote based on : @@ -159,7 +157,6 @@ public: virtual void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; - virtual void update(UpdateFlags flags) override; }; // contains a text, it will split it into words @@ -181,7 +178,6 @@ public: virtual void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; - virtual void update(UpdateFlags flags); }; // contains a text, formated depending on the preferences @@ -198,7 +194,6 @@ public: virtual void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; - virtual void update(UpdateFlags flags); TextElement *formatTime(const QTime &time); }; @@ -212,7 +207,6 @@ public: virtual void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override; - virtual void update(UpdateFlags flags); }; // adds bits as text, static image or animated image diff --git a/src/singletons/resourcemanager.cpp b/src/singletons/resourcemanager.cpp index 305e66da4..4383d8f14 100644 --- a/src/singletons/resourcemanager.cpp +++ b/src/singletons/resourcemanager.cpp @@ -265,7 +265,6 @@ inline void ParseCheermoteSets(std::vector &s } } // namespace - ResourceManager::ResourceManager() : badgeStaff(lli(":/images/staff_bg.png")) , badgeAdmin(lli(":/images/admin_bg.png")) @@ -285,6 +284,9 @@ ResourceManager::ResourceManager() , cheerBadge1(lli(":/images/cheer1")) , buttonBan(lli(":/images/button_ban.png", 0.25)) , buttonTimeout(lli(":/images/button_timeout.png", 0.25)) + , moderationmode_enabled(lli(":/images/moderatormode_enabled")) + , moderationmode_disabled(lli(":/images/moderatormode_disabled")) + , splitHeaderContext(lli(":/images/tool_moreCollapser_off16.png")) { this->loadDynamicTwitchBadges(); diff --git a/src/singletons/resourcemanager.hpp b/src/singletons/resourcemanager.hpp index 7564a8f5b..981aade8f 100644 --- a/src/singletons/resourcemanager.hpp +++ b/src/singletons/resourcemanager.hpp @@ -36,6 +36,11 @@ public: messages::Image *cheerBadge100; messages::Image *cheerBadge1; + messages::Image *moderationmode_enabled; + messages::Image *moderationmode_disabled; + + messages::Image *splitHeaderContext; + std::map cheerBadges; struct BadgeVersion { diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 2184d4612..a58c04bdb 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -153,6 +153,8 @@ void ChannelView::actuallyLayoutMessages() // (this->scrollBar.isVisible() ? width() - this->scrollBar.width() : width()) - 4; int layoutWidth = LAYOUT_WIDTH; + MessageElement::Flags flags = this->getFlags(); + // layout the visible messages in the view if (messagesSnapshot.getLength() > start) { int y = @@ -161,7 +163,7 @@ void ChannelView::actuallyLayoutMessages() for (size_t i = start; i < messagesSnapshot.getLength(); ++i) { auto message = messagesSnapshot[i]; - redrawRequired |= message->layout(layoutWidth, this->getDpiMultiplier()); + redrawRequired |= message->layout(layoutWidth, this->getDpiMultiplier(), flags); y += message->getHeight(); @@ -177,7 +179,7 @@ void ChannelView::actuallyLayoutMessages() for (int i = (int)messagesSnapshot.getLength() - 1; i >= 0; i--) { auto *message = messagesSnapshot[i].get(); - message->layout(layoutWidth, this->getDpiMultiplier()); + message->layout(layoutWidth, this->getDpiMultiplier(), flags); h -= message->getHeight(); @@ -432,6 +434,21 @@ void ChannelView::setSelection(const SelectionItem &start, const SelectionItem & // << max.charIndex; } +messages::MessageElement::Flags ChannelView::getFlags() const +{ + MessageElement::Flags flags = MessageElement::Default; + + Split *split = dynamic_cast(this->parentWidget()); + + if (split != nullptr) { + if (split->getModerationMode()) { + flags = (MessageElement::Flags)(flags | MessageElement::ModeratorTools); + } + } + + return flags; +} + void ChannelView::paintEvent(QPaintEvent * /*event*/) { // BENCH(timer); @@ -536,7 +553,8 @@ void ChannelView::wheelEvent(QWheelEvent *event) if (i == 0) { desired = 0; } else { - snapshot[i - 1]->layout(LAYOUT_WIDTH, this->getDpiMultiplier()); + snapshot[i - 1]->layout(LAYOUT_WIDTH, this->getDpiMultiplier(), + this->getFlags()); scrollFactor = 1; currentScrollLeft = snapshot[i - 1]->getHeight(); } @@ -558,7 +576,8 @@ void ChannelView::wheelEvent(QWheelEvent *event) if (i == snapshotLength - 1) { desired = snapshot.getLength(); } else { - snapshot[i + 1]->layout(LAYOUT_WIDTH, this->getDpiMultiplier()); + snapshot[i + 1]->layout(LAYOUT_WIDTH, this->getDpiMultiplier(), + this->getFlags()); scrollFactor = 1; currentScrollLeft = snapshot[i + 1]->getHeight(); diff --git a/src/widgets/helper/channelview.hpp b/src/widgets/helper/channelview.hpp index 1ed0aa8d1..90cb920c7 100644 --- a/src/widgets/helper/channelview.hpp +++ b/src/widgets/helper/channelview.hpp @@ -29,7 +29,7 @@ class ChannelView : public BaseWidget public: explicit ChannelView(BaseWidget *parent = 0); - ~ChannelView(); + virtual ~ChannelView(); void queueUpdate(); Scrollbar &getScrollBar(); @@ -80,6 +80,7 @@ private: void drawMessages(QPainter &painter); void setSelection(const messages::SelectionItem &start, const messages::SelectionItem &end); + messages::MessageElement::Flags getFlags() const; SharedChannel channel; diff --git a/src/widgets/helper/rippleeffectbutton.cpp b/src/widgets/helper/rippleeffectbutton.cpp index 2c25dcb83..a2c46c8fe 100644 --- a/src/widgets/helper/rippleeffectbutton.cpp +++ b/src/widgets/helper/rippleeffectbutton.cpp @@ -10,7 +10,7 @@ namespace widgets { RippleEffectButton::RippleEffectButton(BaseWidget *parent) : BaseWidget(parent) - + , pixmap(nullptr) { connect(&effectTimer, &QTimer::timeout, this, &RippleEffectButton::onMouseEffectTimeout); @@ -23,11 +23,36 @@ void RippleEffectButton::setMouseEffectColor(boost::optional color) this->mouseEffectColor = color; } +void RippleEffectButton::setPixmap(const QPixmap *_pixmap) +{ + this->pixmap = const_cast(_pixmap); + this->update(); +} + +const QPixmap *RippleEffectButton::getPixmap() const +{ + return this->pixmap; +} + void RippleEffectButton::paintEvent(QPaintEvent *) { QPainter painter(this); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + this->fancyPaint(painter); + + if (this->pixmap != nullptr) { + QRect rect = this->rect(); + int xD = 6 * this->getDpiMultiplier(); + + rect.moveLeft(xD); + rect.setRight(rect.right() - xD - xD); + rect.moveTop(xD); + rect.setBottom(rect.bottom() - xD - xD); + + painter.drawPixmap(rect, *this->pixmap); + } } void RippleEffectButton::fancyPaint(QPainter &painter) diff --git a/src/widgets/helper/rippleeffectbutton.hpp b/src/widgets/helper/rippleeffectbutton.hpp index cc4bf9239..5806ff4a4 100644 --- a/src/widgets/helper/rippleeffectbutton.hpp +++ b/src/widgets/helper/rippleeffectbutton.hpp @@ -31,6 +31,8 @@ public: RippleEffectButton(BaseWidget *parent); void setMouseEffectColor(boost::optional color); + void setPixmap(const QPixmap *pixmap); + const QPixmap *getPixmap() const; signals: void clicked(); @@ -50,6 +52,7 @@ protected: void fancyPaint(QPainter &painter); private: + QPixmap *pixmap; QPoint mousePos; double hoverMultiplier = 0.0; QTimer effectTimer; diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index 8683f85c6..d60015855 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -1,4 +1,5 @@ #include "widgets/helper/splitheader.hpp" +#include "singletons/resourcemanager.hpp" #include "singletons/thememanager.hpp" #include "twitch/twitchchannel.hpp" #include "util/layoutcreator.hpp" @@ -21,20 +22,20 @@ SplitHeader::SplitHeader(Split *_split) { this->setMouseTracking(true); + singletons::ResourceManager &resourceManager = singletons::ResourceManager::getInstance(); + util::LayoutCreator layoutCreator(this); auto layout = layoutCreator.emplace().withoutMargin(); { // dropdown label - auto dropdown = layout.emplace(this).assign(&this->dropdownLabel); - dropdown->getLabel().setTextFormat(Qt::RichText); - dropdown->getLabel().setText(""); - dropdown->getLabel().setScaledContents(true); + auto dropdown = layout.emplace(this).assign(&this->dropdownButton); dropdown->setMouseTracking(true); + dropdown->setPixmap(resourceManager.splitHeaderContext->getPixmap()); this->addDropdownItems(dropdown.getElement()); - QObject::connect(dropdown.getElement(), &RippleEffectLabel::clicked, this, [this] { + QObject::connect(dropdown.getElement(), &RippleEffectButton::clicked, this, [this] { QTimer::singleShot(80, [&] { this->dropdownMenu.move( - this->dropdownLabel->mapToGlobal(QPoint(0, this->dropdownLabel->height()))); + this->dropdownButton->mapToGlobal(QPoint(0, this->dropdownButton->height()))); this->dropdownMenu.show(); }); }); @@ -50,11 +51,13 @@ SplitHeader::SplitHeader(Split *_split) layout->addStretch(1); // moderation mode - auto moderation = layout.emplace(this).assign(&this->moderationLabel); - moderation->setMouseTracking(true); - moderation->getLabel().setScaledContents(true); - moderation->getLabel().setTextFormat(Qt::RichText); - moderation->getLabel().setText(""); + auto moderator = layout.emplace(this).assign(&this->moderationButton); + + QObject::connect(moderator.getElement(), &RippleEffectButton::clicked, this, [this] { + this->split->setModerationMode(!this->split->getModerationMode()); + }); + + this->updateModerationModeIcon(); } // ---- misc @@ -63,8 +66,6 @@ SplitHeader::SplitHeader(Split *_split) this->updateChannelText(); - // this->titleLabel.setAlignment(Qt::AlignCenter); - this->initializeChannelSignals(); this->split->channelChanged.connect([this]() { @@ -77,11 +78,8 @@ SplitHeader::~SplitHeader() this->onlineStatusChangedConnection.disconnect(); } -void SplitHeader::addDropdownItems(RippleEffectLabel *label) +void SplitHeader::addDropdownItems(RippleEffectButton *label) { - connect(this->dropdownLabel, &RippleEffectLabel::clicked, this, - &SplitHeader::leftButtonClicked); - // clang-format off this->dropdownMenu.addAction("Add new split", this->split, &Split::doAddSplit, QKeySequence(tr("Ctrl+T"))); this->dropdownMenu.addAction("Close split", this->split, &Split::doCloseSplit, QKeySequence(tr("Ctrl+W"))); @@ -122,8 +120,8 @@ void SplitHeader::resizeEvent(QResizeEvent *event) int w = 28 * getDpiMultiplier(); this->setFixedHeight(w); - this->dropdownLabel->setFixedWidth(w); - this->moderationLabel->setFixedWidth(w); + this->dropdownButton->setFixedWidth(w); + this->moderationButton->setFixedWidth(w); } void SplitHeader::updateChannelText() @@ -156,6 +154,14 @@ void SplitHeader::updateChannelText() } } +void SplitHeader::updateModerationModeIcon() +{ + singletons::ResourceManager &resourceManager = singletons::ResourceManager::getInstance(); + this->moderationButton->setPixmap(this->split->getModerationMode() + ? resourceManager.moderationmode_enabled->getPixmap() + : resourceManager.moderationmode_disabled->getPixmap()); +} + void SplitHeader::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -203,10 +209,6 @@ void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event) } } -void SplitHeader::leftButtonClicked() -{ -} - void SplitHeader::rightButtonClicked() { } @@ -216,9 +218,9 @@ void SplitHeader::refreshTheme() QPalette palette; palette.setColor(QPalette::Foreground, this->themeManager.splits.header.text); - this->dropdownLabel->setPalette(palette); + // this->dropdownButton->setPalette(palette); this->titleLabel->setPalette(palette); - this->moderationLabel->setPalette(palette); + // this->moderationLabel->setPalette(palette); } void SplitHeader::menuMoveSplit() diff --git a/src/widgets/helper/splitheader.hpp b/src/widgets/helper/splitheader.hpp index c0bd04a0f..f72429941 100644 --- a/src/widgets/helper/splitheader.hpp +++ b/src/widgets/helper/splitheader.hpp @@ -30,6 +30,7 @@ public: // Update channel text from chat widget void updateChannelText(); + void updateModerationModeIcon(); protected: virtual void paintEvent(QPaintEvent *) override; @@ -47,13 +48,12 @@ private: boost::signals2::connection onlineStatusChangedConnection; - RippleEffectLabel *dropdownLabel; + RippleEffectButton *dropdownButton; SignalLabel *titleLabel; - RippleEffectLabel *moderationLabel; + RippleEffectButton *moderationButton; QMenu dropdownMenu; - void leftButtonClicked(); void rightButtonClicked(); virtual void refreshTheme() override; @@ -64,7 +64,7 @@ private: bool isLive; public slots: - void addDropdownItems(RippleEffectLabel *label); + void addDropdownItems(RippleEffectButton *label); void menuMoveSplit(); void menuReloadChannelEmotes(); diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 052f2fad4..7307f75cb 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -52,6 +52,7 @@ Split::Split(SplitContainer *parent, const std::string &_uuid) , input(this) , flexSizeX(1) , flexSizeY(1) + , moderationMode(false) { this->setMouseTracking(true); @@ -115,6 +116,8 @@ Split::Split(SplitContainer *parent, const std::string &_uuid) this->input.show(); } }); + + this->header.updateModerationModeIcon(); } Split::~Split() @@ -168,6 +171,19 @@ double Split::getFlexSizeY() return this->flexSizeY; } +void Split::setModerationMode(bool value) +{ + if (value != this->moderationMode) { + this->moderationMode = value; + this->header.updateModerationModeIcon(); + } +} + +bool Split::getModerationMode() const +{ + return this->moderationMode; +} + void Split::channelNameUpdated(const std::string &newChannelName) { auto &cman = singletons::ChannelManager::getInstance(); diff --git a/src/widgets/split.hpp b/src/widgets/split.hpp index 83587d073..6cab1c932 100644 --- a/src/widgets/split.hpp +++ b/src/widgets/split.hpp @@ -62,6 +62,9 @@ public: void setFlexSizeY(double y); double getFlexSizeY(); + void setModerationMode(bool value); + bool getModerationMode() const; + bool showChangeChannelPopup(const char *dialogTitle, bool empty = false); void giveFocus(Qt::FocusReason reason); bool hasFocus() const; @@ -88,6 +91,8 @@ private: double flexSizeX; double flexSizeY; + bool moderationMode; + boost::signals2::connection channelIDChangedConnection; void setChannel(SharedChannel newChannel);