mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
added moderation buttons
This commit is contained in:
parent
252f648ff8
commit
6d6b99f3ef
15 changed files with 367 additions and 76 deletions
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <boost/noncopyable.hpp>
|
||||
#include <climits>
|
||||
|
||||
#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
|
||||
|
|
|
@ -17,6 +17,7 @@ public:
|
|||
UserBan,
|
||||
InsertText,
|
||||
ShowMessage,
|
||||
UserAction,
|
||||
};
|
||||
|
||||
Link();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
#include <QtGlobal>
|
||||
|
||||
#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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <QFont>
|
||||
#include <QFontDatabase>
|
||||
#include <QFontMetrics>
|
||||
#include <pajlada/settings/setting.hpp>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
@ -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<std::pair<float, Font>> currentFontByDpi;
|
||||
std::list<std::pair<float, Font>> currentFontByScale;
|
||||
|
||||
int generation = 0;
|
||||
};
|
||||
}
|
||||
} // namespace singletons
|
||||
|
||||
typedef singletons::FontManager::Type FontStyle;
|
||||
} // namespace chatterino
|
||||
|
|
50
src/singletons/helper/moderationaction.cpp
Normal file
50
src/singletons/helper/moderationaction.cpp
Normal file
|
@ -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
|
31
src/singletons/helper/moderationaction.hpp
Normal file
31
src/singletons/helper/moderationaction.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
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
|
|
@ -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<ModerationAction> 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
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "messages/highlightphrase.hpp"
|
||||
#include "messages/messageelement.hpp"
|
||||
#include "singletons/helper/chatterinosetting.hpp"
|
||||
#include "singletons/helper/moderationaction.hpp"
|
||||
|
||||
#include <pajlada/settings/setting.hpp>
|
||||
#include <pajlada/settings/settinglistener.hpp>
|
||||
|
@ -105,14 +106,19 @@ public:
|
|||
void saveSnapshot();
|
||||
void recallSnapshot();
|
||||
|
||||
std::vector<ModerationAction> getModerationActions() const;
|
||||
|
||||
signals:
|
||||
void wordTypeMaskChanged();
|
||||
|
||||
private:
|
||||
std::vector<ModerationAction> _moderationActions;
|
||||
std::unique_ptr<rapidjson::Document> snapshot;
|
||||
|
||||
SettingManager();
|
||||
|
||||
void updateModerationActions();
|
||||
|
||||
messages::MessageElement::Flags wordTypeMask = messages::MessageElement::Default;
|
||||
|
||||
pajlada::Settings::SettingListener wordMaskListener;
|
||||
|
|
|
@ -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<ImageElement>(*(badge.badgeImage1x), MessageElement::BadgeVanity);
|
||||
this->append<ImageElement>(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<ImageElement>(*(badge.badgeImage1x), MessageElement::BadgeVanity);
|
||||
this->append<ImageElement>(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<ImageElement>(*resourceManager.badgeStaff,
|
||||
this->append<ImageElement>(resourceManager.badgeStaff,
|
||||
MessageElement::BadgeGlobalAuthority)
|
||||
->setTooltip("Twitch Staff");
|
||||
} else if (badge == "admin/1") {
|
||||
this->append<ImageElement>(*resourceManager.badgeAdmin,
|
||||
this->append<ImageElement>(resourceManager.badgeAdmin,
|
||||
MessageElement::BadgeGlobalAuthority)
|
||||
->setTooltip("Twitch Admin");
|
||||
} else if (badge == "global_mod/1") {
|
||||
this->append<ImageElement>(*resourceManager.badgeGlobalModerator,
|
||||
this->append<ImageElement>(resourceManager.badgeGlobalModerator,
|
||||
MessageElement::BadgeGlobalAuthority)
|
||||
->setTooltip("Twitch Global Moderator");
|
||||
} else if (badge == "moderator/1") {
|
||||
// TODO: Implement custom FFZ moderator badge
|
||||
this->append<ImageElement>(*resourceManager.badgeModerator,
|
||||
this->append<ImageElement>(resourceManager.badgeModerator,
|
||||
MessageElement::BadgeChannelAuthority)
|
||||
->setTooltip("Twitch Channel Moderator");
|
||||
} else if (badge == "turbo/1") {
|
||||
this->append<ImageElement>(*resourceManager.badgeTurbo,
|
||||
this->append<ImageElement>(resourceManager.badgeTurbo,
|
||||
MessageElement::BadgeGlobalAuthority)
|
||||
->setTooltip("Twitch Turbo Subscriber");
|
||||
} else if (badge == "broadcaster/1") {
|
||||
this->append<ImageElement>(*resourceManager.badgeBroadcaster,
|
||||
this->append<ImageElement>(resourceManager.badgeBroadcaster,
|
||||
MessageElement::BadgeChannelAuthority)
|
||||
->setTooltip("Twitch Broadcaster");
|
||||
} else if (badge == "premium/1") {
|
||||
this->append<ImageElement>(*resourceManager.badgePremium, MessageElement::BadgeVanity)
|
||||
this->append<ImageElement>(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<ImageElement>(*resourceManager.badgeVerified,
|
||||
this->append<ImageElement>(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<ImageElement>(*resourceManager.badgeSubscriber,
|
||||
this->append<ImageElement>(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<ImageElement>(*resourceManager.badgeSubscriber,
|
||||
this->append<ImageElement>(resourceManager.badgeSubscriber,
|
||||
MessageElement::BadgeSubscription)
|
||||
->setTooltip("Twitch Subscriber");
|
||||
continue;
|
||||
|
@ -596,8 +596,7 @@ void TwitchMessageBuilder::parseTwitchBadges()
|
|||
|
||||
auto &badgeVersion = badgeVersionIt->second;
|
||||
|
||||
this->append<ImageElement>(*badgeVersion.badgeImage1x,
|
||||
MessageElement::BadgeSubscription)
|
||||
this->append<ImageElement>(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<ImageElement>(*badgeVersion.badgeImage1x, badgeType)
|
||||
this->append<ImageElement>(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<ImageElement>(*badge->image, MessageElement::BadgeChatterino)
|
||||
this->append<ImageElement>(badge->image, MessageElement::BadgeChatterino)
|
||||
->setTooltip(QString::fromStdString(badge->tooltip));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,21 +21,21 @@ ModerationPage::ModerationPage()
|
|||
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
||||
{
|
||||
// clang-format off
|
||||
auto label = layout.emplace<QLabel>("In channels that you moderate there is a button <insert image of button here> to enable moderation mode.");
|
||||
auto label = layout.emplace<QLabel>("In channels that you moderate there is a button <insert image of button here> 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<QTextEdit>().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
|
||||
|
|
Loading…
Reference in a new issue