This commit is contained in:
hemirt 2018-07-03 17:21:41 +02:00
commit ef4c7dbe60
31 changed files with 434 additions and 291 deletions

View file

@ -223,7 +223,8 @@ SOURCES += \
src/singletons/Resources.cpp \ src/singletons/Resources.cpp \
src/singletons/Settings.cpp \ src/singletons/Settings.cpp \
src/singletons/Updates.cpp \ src/singletons/Updates.cpp \
src/singletons/Theme.cpp src/singletons/Theme.cpp \
src/controllers/moderationactions/ModerationActionModel.cpp
HEADERS += \ HEADERS += \
src/Application.hpp \ src/Application.hpp \
@ -393,7 +394,8 @@ HEADERS += \
src/singletons/Theme.hpp \ src/singletons/Theme.hpp \
src/common/SimpleSignalVector.hpp \ src/common/SimpleSignalVector.hpp \
src/common/SignalVector.hpp \ src/common/SignalVector.hpp \
src/common/Singleton.hpp src/common/Singleton.hpp \
src/controllers/moderationactions/ModerationActionModel.hpp
RESOURCES += \ RESOURCES += \
resources/resources.qrc \ resources/resources.qrc \

View file

@ -1,23 +1,7 @@
#tabWidget {
background-color: #333;
}
* { * {
font-size: <font-size>px; font-size: <font-size>px;
} }
SettingsDialogTab:hover {
border: 1px solid grey;
}
QLabel, QCheckBox, QGroupBox, SettingsDialogTab {
color: white;
}
QGroupBox {
background-color: #444;
}
QCheckBox::indicator { QCheckBox::indicator {
width: <checkbox-size>px; width: <checkbox-size>px;
height: <checkbox-size>px; height: <checkbox-size>px;

View file

@ -28,6 +28,7 @@ public:
TwitchWatching, TwitchWatching,
TwitchMentions, TwitchMentions,
TwitchEnd, TwitchEnd,
Misc
}; };
explicit Channel(const QString &_name, Type type); explicit Channel(const QString &_name, Type type);

View file

@ -50,6 +50,8 @@ public:
} }
} }
virtual bool isSorted() const = 0;
protected: protected:
std::vector<TVectorItem> vector; std::vector<TVectorItem> vector;
QTimer itemsChangedTimer; QTimer itemsChangedTimer;
@ -60,9 +62,10 @@ class BaseSignalVector : public ReadOnlySignalVector<TVectorItem>
{ {
public: public:
// returns the actual index of the inserted item // returns the actual index of the inserted item
virtual int insertItem(const TVectorItem &item, int proposedIndex = -1, void *caller = 0) = 0; virtual int insertItem(const TVectorItem &item, int proposedIndex = -1,
void *caller = nullptr) = 0;
void removeItem(int index, void *caller = 0) void removeItem(int index, void *caller = nullptr)
{ {
assertInGuiThread(); assertInGuiThread();
assert(index >= 0 && index < this->vector.size()); assert(index >= 0 && index < this->vector.size());
@ -75,7 +78,7 @@ public:
this->invokeDelayedItemsChanged(); this->invokeDelayedItemsChanged();
} }
int appendItem(const TVectorItem &item, void *caller = 0) int appendItem(const TVectorItem &item, void *caller = nullptr)
{ {
return this->insertItem(item, -1, caller); return this->insertItem(item, -1, caller);
} }
@ -85,7 +88,7 @@ template <typename TVectorItem>
class UnsortedSignalVector : public BaseSignalVector<TVectorItem> class UnsortedSignalVector : public BaseSignalVector<TVectorItem>
{ {
public: public:
virtual int insertItem(const TVectorItem &item, int index = -1, void *caller = 0) override virtual int insertItem(const TVectorItem &item, int index = -1, void *caller = nullptr) override
{ {
assertInGuiThread(); assertInGuiThread();
if (index == -1) { if (index == -1) {
@ -101,6 +104,11 @@ public:
this->invokeDelayedItemsChanged(); this->invokeDelayedItemsChanged();
return index; return index;
} }
virtual bool isSorted() const override
{
return false;
}
}; };
template <typename TVectorItem, typename Compare> template <typename TVectorItem, typename Compare>
@ -120,6 +128,11 @@ public:
this->invokeDelayedItemsChanged(); this->invokeDelayedItemsChanged();
return index; return index;
} }
virtual bool isSorted() const override
{
return true;
}
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -171,8 +171,8 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
auto target = words.at(1); auto target = words.at(1);
if (user->isAnon()) { if (user->isAnon()) {
channel->addMessage(Message::createSystemMessage( channel->addMessage(
"You must be logged in to ignore someone")); Message::createSystemMessage("You must be logged in to ignore someone"));
return ""; return "";
} }
@ -188,8 +188,8 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
auto target = words.at(1); auto target = words.at(1);
if (user->isAnon()) { if (user->isAnon()) {
channel->addMessage(Message::createSystemMessage( channel->addMessage(
"You must be logged in to ignore someone")); Message::createSystemMessage("You must be logged in to ignore someone"));
return ""; return "";
} }

View file

@ -24,7 +24,7 @@ public:
private: private:
bool isImage_; bool isImage_;
Image *image_; Image *image_ = nullptr;
QString line1_; QString line1_;
QString line2_; QString line2_;
QString action_; QString action_;

View file

@ -0,0 +1,27 @@
#include "ModerationActionModel.hpp"
#include "util/StandardItemHelper.hpp"
namespace chatterino {
// commandmodel
ModerationActionModel ::ModerationActionModel(QObject *parent)
: SignalVectorModel<ModerationAction>(1, parent)
{
}
// turn a vector item into a model row
ModerationAction ModerationActionModel::getItemFromRow(std::vector<QStandardItem *> &row,
const ModerationAction &original)
{
return ModerationAction(row[0]->data(Qt::DisplayRole).toString());
}
// turns a row in the model into a vector item
void ModerationActionModel::getRowFromItem(const ModerationAction &item,
std::vector<QStandardItem *> &row)
{
setStringItem(row[0], item.getAction());
}
} // namespace chatterino

View file

@ -0,0 +1,31 @@
#pragma once
#include <QObject>
#include "common/SignalVectorModel.hpp"
#include "controllers/moderationactions/ModerationAction.hpp"
namespace chatterino {
class ModerationActions;
class ModerationActionModel : public SignalVectorModel<ModerationAction>
{
public:
explicit ModerationActionModel(QObject *parent);
protected:
// turn a vector item into a model row
virtual ModerationAction getItemFromRow(std::vector<QStandardItem *> &row,
const ModerationAction &original) override;
// turns a row in the model into a vector item
virtual void getRowFromItem(const ModerationAction &item,
std::vector<QStandardItem *> &row) override;
friend class HighlightController;
friend class ModerationActions;
};
} // namespace chatterino

View file

@ -1,6 +1,7 @@
#include "ModerationActions.hpp" #include "ModerationActions.hpp"
#include "Application.hpp" #include "Application.hpp"
#include "controllers/moderationactions/ModerationActionModel.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include <QRegularExpression> #include <QRegularExpression>
@ -25,4 +26,12 @@ void ModerationActions::initialize()
}); });
} }
ModerationActionModel *ModerationActions::createModel(QObject *parent)
{
ModerationActionModel *model = new ModerationActionModel(parent);
model->init(&this->items);
return model;
}
} // namespace chatterino } // namespace chatterino

View file

@ -1,11 +1,13 @@
#pragma once #pragma once
#include "common/ChatterinoSetting.hpp"
#include "common/SignalVector.hpp" #include "common/SignalVector.hpp"
#include "controllers/moderationactions/ModerationAction.hpp" #include "controllers/moderationactions/ModerationAction.hpp"
#include "common/ChatterinoSetting.hpp"
namespace chatterino { namespace chatterino {
class ModerationActionModel;
class ModerationActions class ModerationActions
{ {
public: public:
@ -15,6 +17,8 @@ public:
UnsortedSignalVector<ModerationAction> items; UnsortedSignalVector<ModerationAction> items;
ModerationActionModel *createModel(QObject *parent);
private: private:
ChatterinoSetting<std::vector<ModerationAction>> setting = {"/moderation/actions"}; ChatterinoSetting<std::vector<ModerationAction>> setting = {"/moderation/actions"};
bool initialized = false; bool initialized = false;

View file

@ -47,8 +47,8 @@ QString MessageBuilder::matchLink(const QString &string)
{ {
LinkParser linkParser(string); LinkParser linkParser(string);
static QRegularExpression httpRegex("\\bhttps?://"); static QRegularExpression httpRegex("\\bhttps?://", QRegularExpression::CaseInsensitiveOption);
static QRegularExpression ftpRegex("\\bftps?://"); static QRegularExpression ftpRegex("\\bftps?://", QRegularExpression::CaseInsensitiveOption);
if (!linkParser.hasMatch()) { if (!linkParser.hasMatch()) {
return QString(); return QString();

View file

@ -247,7 +247,7 @@ void TwitchModerationElement::addToContainer(MessageLayoutContainer &container,
MessageElement::Flags _flags) MessageElement::Flags _flags)
{ {
if (_flags & MessageElement::ModeratorTools) { if (_flags & MessageElement::ModeratorTools) {
QSize size((int)(container.getScale() * 16), (int)(container.getScale() * 16)); QSize size(int(container.getScale() * 16), int(container.getScale() * 16));
for (const ModerationAction &m : getApp()->moderationActions->items.getVector()) { for (const ModerationAction &m : getApp()->moderationActions->items.getVector()) {
if (m.isImage()) { if (m.isImage()) {

View file

@ -38,10 +38,10 @@ void BTTVEmotes::loadGlobalEmotes()
EmoteData emoteData; EmoteData emoteData;
emoteData.image1x = new Image(getEmoteLink(urlTemplate, id, "1x"), 1, code, emoteData.image1x = new Image(getEmoteLink(urlTemplate, id, "1x"), 1, code,
code + "<br />Global BTTV Emote"); code + "<br />Global BTTV Emote");
emoteData.image2x = new Image(getEmoteLink(urlTemplate, id, "2x"), 0.5, emoteData.image2x = new Image(getEmoteLink(urlTemplate, id, "2x"), 0.5, code,
code, code + "<br />Global BTTV Emote"); code + "<br />Global BTTV Emote");
emoteData.image3x = new Image(getEmoteLink(urlTemplate, id, "3x"), 0.25, emoteData.image3x = new Image(getEmoteLink(urlTemplate, id, "3x"), 0.25, code,
code, code + "<br />Global BTTV Emote"); code + "<br />Global BTTV Emote");
emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id; emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id;
this->globalEmotes.insert(code, emoteData); this->globalEmotes.insert(code, emoteData);
@ -89,18 +89,15 @@ void BTTVEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr<Emo
EmoteData emoteData; EmoteData emoteData;
QString link = linkTemplate; QString link = linkTemplate;
link.detach(); link.detach();
emoteData.image1x = emoteData.image1x = new Image(link.replace("{{id}}", id).replace("{{image}}", "1x"),
new Image(link.replace("{{id}}", id).replace("{{image}}", "1x"), 1, 1, code, code + "<br />Channel BTTV Emote");
code, code + "<br />Channel BTTV Emote");
link = linkTemplate; link = linkTemplate;
link.detach(); link.detach();
emoteData.image2x = emoteData.image2x = new Image(link.replace("{{id}}", id).replace("{{image}}", "2x"),
new Image(link.replace("{{id}}", id).replace("{{image}}", "2x"),
0.5, code, code + "<br />Channel BTTV Emote"); 0.5, code, code + "<br />Channel BTTV Emote");
link = linkTemplate; link = linkTemplate;
link.detach(); link.detach();
emoteData.image3x = emoteData.image3x = new Image(link.replace("{{id}}", id).replace("{{image}}", "3x"),
new Image(link.replace("{{id}}", id).replace("{{image}}", "3x"),
0.25, code, code + "<br />Channel BTTV Emote"); 0.25, code, code + "<br />Channel BTTV Emote");
emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id; emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id;

View file

@ -260,8 +260,8 @@ void Emojis::loadEmojiSet()
urlPrefix = it->second; urlPrefix = it->second;
} }
QString url = urlPrefix + code + ".png"; QString url = urlPrefix + code + ".png";
emoji->emoteData.image1x = new Image( emoji->emoteData.image1x =
url, 0.35, emoji->value, ":" + emoji->shortCodes[0] + ":<br/>Emoji"); new Image(url, 0.35, emoji->value, ":" + emoji->shortCodes[0] + ":<br/>Emoji");
}); });
}); });
} }

View file

@ -253,8 +253,7 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message, Tw
auto it = tags.find("system-msg"); auto it = tags.find("system-msg");
if (it != tags.end()) { if (it != tags.end()) {
auto newMessage = auto newMessage = Message::createSystemMessage(parseTagString(it.value().toString()));
Message::createSystemMessage(parseTagString(it.value().toString()));
newMessage->flags |= Message::Subscription; newMessage->flags |= Message::Subscription;
@ -322,12 +321,22 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message) void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message)
{ {
static std::unordered_set<std::string> readConnectionOnlyIDs{ static std::unordered_set<std::string> readConnectionOnlyIDs{
"host_on", "host_off", "host_target_went_offline", "emote_only_on", "emote_only_off", "host_on",
"slow_on", "slow_off", "subs_on", "subs_off", "r9k_on", "r9k_off", "host_off",
"host_target_went_offline",
"emote_only_on",
"emote_only_off",
"slow_on",
"slow_off",
"subs_on",
"subs_off",
"r9k_on",
"r9k_off",
// Display for user who times someone out. This implies you're a moderator, at which point // Display for user who times someone out. This implies you're a moderator, at which point
// you will be connected to PubSub and receive a better message from there // you will be connected to PubSub and receive a better message from there
"timeout_success", "ban_success", "timeout_success",
"ban_success",
}; };
QVariant v = message->tag("msg-id"); QVariant v = message->tag("msg-id");

View file

@ -194,8 +194,7 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel, const QString
// check if you are sending messages too fast // check if you are sending messages too fast
if (!lastMessage.empty() && lastMessage.back() + minMessageOffset > now) { if (!lastMessage.empty() && lastMessage.back() + minMessageOffset > now) {
if (this->lastErrorTimeSpeed_ + 30s < now) { if (this->lastErrorTimeSpeed_ + 30s < now) {
auto errorMessage = auto errorMessage = Message::createSystemMessage("sending messages too fast");
Message::createSystemMessage("sending messages too fast");
channel->addMessage(errorMessage); channel->addMessage(errorMessage);
@ -212,8 +211,7 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel, const QString
// check if you are sending too many messages // check if you are sending too many messages
if (lastMessage.size() >= maxMessageCount) { if (lastMessage.size() >= maxMessageCount) {
if (this->lastErrorTimeAmount_ + 30s < now) { if (this->lastErrorTimeAmount_ + 30s < now) {
auto errorMessage = auto errorMessage = Message::createSystemMessage("sending too many messages");
Message::createSystemMessage("sending too many messages");
channel->addMessage(errorMessage); channel->addMessage(errorMessage);

View file

@ -98,7 +98,6 @@ void Paths::initSubDirectories()
// create settings subdirectories and validate that they are created properly // create settings subdirectories and validate that they are created properly
auto makePath = [&](const std::string &name) -> QString { auto makePath = [&](const std::string &name) -> QString {
auto path = combinePath(this->rootAppDataDirectory, QString::fromStdString(name)); auto path = combinePath(this->rootAppDataDirectory, QString::fromStdString(name));
if (!QDir().mkpath(path)) { if (!QDir().mkpath(path)) {

View file

@ -434,6 +434,8 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
default: default:
return QWidget::nativeEvent(eventType, message, result); return QWidget::nativeEvent(eventType, message, result);
} }
#else
return QWidget::nativeEvent(eventType, message, result);
#endif #endif
} }
@ -453,7 +455,12 @@ void BaseWindow::paintEvent(QPaintEvent *)
painter.drawRect(0, 0, this->width() - 1, this->height() - 1); painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
} }
this->drawCustomWindowFrame(painter); // this->drawCustomWindowFrame(painter);
// QPainter painter(this);
QColor bg = this->overrideBackgroundColor_.value_or(this->themeManager->window.background);
painter.fillRect(QRect(0, 1, this->width() - 0, this->height() - 0), bg);
} }
void BaseWindow::updateScale() void BaseWindow::updateScale()
@ -492,14 +499,16 @@ void BaseWindow::calcButtonsSizes()
void BaseWindow::drawCustomWindowFrame(QPainter &painter) void BaseWindow::drawCustomWindowFrame(QPainter &painter)
{ {
#ifdef USEWINSDK //#ifdef USEWINSDK
if (this->hasCustomWindowFrame()) { // if (this->hasCustomWindowFrame()) {
QPainter painter(this); // QPainter painter(this);
painter.fillRect(QRect(0, 1, this->width() - 0, this->height() - 0), // QColor bg =
this->themeManager->window.background); // this->overrideBackgroundColor_.value_or(this->themeManager->window.background);
}
#endif // painter.fillRect(QRect(0, 1, this->width() - 0, this->height() - 0), bg);
// }
//#endif
} }
bool BaseWindow::handleDPICHANGED(MSG *msg) bool BaseWindow::handleDPICHANGED(MSG *msg)

View file

@ -70,6 +70,8 @@ protected:
void updateScale(); void updateScale();
boost::optional<QColor> overrideBackgroundColor_;
private: private:
void init(); void init();
void moveIntoDesktopRect(QWidget *parent); void moveIntoDesktopRect(QWidget *parent);

View file

@ -76,8 +76,7 @@ void TooltipWidget::updateFont()
{ {
auto app = getApp(); auto app = getApp();
this->setFont( this->setFont(app->fonts->getFont(Fonts::Type::ChatMediumSmall, this->getScale()));
app->fonts->getFont(Fonts::Type::ChatMediumSmall, this->getScale()));
} }
void TooltipWidget::setText(QString text) void TooltipWidget::setText(QString text)

View file

@ -32,6 +32,8 @@ SettingsDialog::SettingsDialog()
this->addTabs(); this->addTabs();
this->scaleChangedEvent(this->getScale()); this->scaleChangedEvent(this->getScale());
this->overrideBackgroundColor_ = QColor("#282828");
} }
void SettingsDialog::initUi() void SettingsDialog::initUi()
@ -149,8 +151,6 @@ void SettingsDialog::showDialog(PreferredTab preferredTab)
void SettingsDialog::refresh() void SettingsDialog::refresh()
{ {
// this->ui.accountSwitchWidget->refresh();
getApp()->settings->saveSnapshot(); getApp()->settings->saveSnapshot();
for (auto *tab : this->tabs) { for (auto *tab : this->tabs) {
@ -163,16 +163,16 @@ void SettingsDialog::scaleChangedEvent(float newDpi)
QFile file(":/qss/settings.qss"); QFile file(":/qss/settings.qss");
file.open(QFile::ReadOnly); file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll()); QString styleSheet = QLatin1String(file.readAll());
styleSheet.replace("<font-size>", QString::number((int)(14 * newDpi))); styleSheet.replace("<font-size>", QString::number(int(14 * newDpi)));
styleSheet.replace("<checkbox-size>", QString::number((int)(14 * newDpi))); styleSheet.replace("<checkbox-size>", QString::number(int(14 * newDpi)));
for (SettingsDialogTab *tab : this->tabs) { for (SettingsDialogTab *tab : this->tabs) {
tab->setFixedHeight((int)(30 * newDpi)); tab->setFixedHeight(int(30 * newDpi));
} }
this->setStyleSheet(styleSheet); this->setStyleSheet(styleSheet);
this->ui_.tabContainerContainer->setFixedWidth((int)(200 * newDpi)); this->ui_.tabContainerContainer->setFixedWidth(int(200 * newDpi));
} }
void SettingsDialog::themeRefreshEvent() void SettingsDialog::themeRefreshEvent()
@ -180,32 +180,10 @@ void SettingsDialog::themeRefreshEvent()
BaseWindow::themeRefreshEvent(); BaseWindow::themeRefreshEvent();
QPalette palette; QPalette palette;
palette.setColor(QPalette::Background, QColor("#444")); palette.setColor(QPalette::Background, QColor("#f44"));
this->setPalette(palette); this->setPalette(palette);
} }
// void SettingsDialog::setChildrensFont(QLayout *object, QFont &font, int indent)
//{
// // for (QWidget *widget : this->widgets) {
// // widget->setFont(font);
// // }
// // for (int i = 0; i < object->count(); i++) {
// // if (object->itemAt(i)->layout()) {
// // setChildrensFont(object->layout()->itemAt(i)->layout(), font, indent + 2);
// // }
// // if (object->itemAt(i)->widget()) {
// // object->itemAt(i)->widget()->setFont(font);
// // if (object->itemAt(i)->widget()->layout() &&
// // !object->itemAt(i)->widget()->layout()->isEmpty()) {
// // setChildrensFont(object->itemAt(i)->widget()->layout(), font, indent +
// 2);
// // }
// // }
// // }
//}
///// Widget creation helpers ///// Widget creation helpers
void SettingsDialog::okButtonClicked() void SettingsDialog::okButtonClicked()
{ {

View file

@ -364,9 +364,11 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget()
} }
a->setBorderColor(color1); a->setBorderColor(color1);
QObject::connect(a.getElement(), &RippleEffectLabel2::clicked, [ QObject::connect(
this, timeout = std::get<1>(item) a.getElement(), &RippleEffectLabel2::clicked,
] { this->buttonClicked.invoke(std::make_pair(Action::Timeout, timeout)); }); [this, timeout = std::get<1>(item)] {
this->buttonClicked.invoke(std::make_pair(Action::Timeout, timeout));
});
} }
} }
}; };
@ -375,16 +377,21 @@ UserInfoPopup::TimeoutWidget::TimeoutWidget()
addTimeouts("sec", {{"1", 1}}); addTimeouts("sec", {{"1", 1}});
addTimeouts("min", { addTimeouts("min", {
{"1", 1 * 60}, {"5", 5 * 60}, {"10", 10 * 60}, {"1", 1 * 60},
{"5", 5 * 60},
{"10", 10 * 60},
}); });
addTimeouts("hour", { addTimeouts("hour", {
{"1", 1 * 60 * 60}, {"4", 4 * 60 * 60}, {"1", 1 * 60 * 60},
{"4", 4 * 60 * 60},
}); });
addTimeouts("days", { addTimeouts("days", {
{"1", 1 * 60 * 60 * 24}, {"3", 3 * 60 * 60 * 24}, {"1", 1 * 60 * 60 * 24},
{"3", 3 * 60 * 60 * 24},
}); });
addTimeouts("weeks", { addTimeouts("weeks", {
{"1", 1 * 60 * 60 * 24 * 7}, {"2", 2 * 60 * 60 * 24 * 7}, {"1", 1 * 60 * 60 * 24 * 7},
{"2", 2 * 60 * 60 * 24 * 7},
}); });
addButton(Ban, "ban", getApp()->resources->buttons.ban); addButton(Ban, "ban", getApp()->resources->buttons.ban);

View file

@ -238,8 +238,8 @@ void NotebookTab::paintEvent(QPaintEvent *)
// || SettingsDialog::getHandle() == QApplication::activeWindow(); // || SettingsDialog::getHandle() == QApplication::activeWindow();
QBrush tabBackground = /*this->mouseOver_ ? colors.backgrounds.hover QBrush tabBackground = /*this->mouseOver_ ? colors.backgrounds.hover
:*/ (windowFocused ? colors.backgrounds.regular :*/
: colors.backgrounds.unfocused); (windowFocused ? colors.backgrounds.regular : colors.backgrounds.unfocused);
// painter.fillRect(rect(), this->mouseOver_ ? regular.backgrounds.hover // painter.fillRect(rect(), this->mouseOver_ ? regular.backgrounds.hover
// : (windowFocused ? regular.backgrounds.regular // : (windowFocused ? regular.backgrounds.regular

View file

@ -93,8 +93,7 @@ void ResizingTextEdit::keyPressEvent(QKeyEvent *event)
return; return;
} }
auto *completionModel = auto *completionModel = static_cast<CompletionModel *>(this->completer->model());
static_cast<CompletionModel *>(this->completer->model());
if (!this->completionInProgress) { if (!this->completionInProgress) {
// First type pressing tab after modifying a message, we refresh our completion model // First type pressing tab after modifying a message, we refresh our completion model

View file

@ -4,6 +4,7 @@
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
#include "util/LayoutCreator.hpp" #include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp" #include "util/RemoveScrollAreaBackground.hpp"
#include "widgets/helper/Line.hpp"
#include <QFontDialog> #include <QFontDialog>
#include <QFormLayout> #include <QFormLayout>
@ -23,8 +24,6 @@
#define SCROLL_SMOOTH "Enable smooth scrolling" #define SCROLL_SMOOTH "Enable smooth scrolling"
#define SCROLL_NEWMSG "Enable smooth scrolling for new messages" #define SCROLL_NEWMSG "Enable smooth scrolling for new messages"
#define LAST_MSG "Mark the last message you read (dotted line)"
// clang-format off // clang-format off
#define TIMESTAMP_FORMATS "hh:mm a", "h:mm a", "hh:mm:ss a", "h:mm:ss a", "HH:mm", "H:mm", "HH:mm:ss", "H:mm:ss" #define TIMESTAMP_FORMATS "hh:mm a", "h:mm a", "hh:mm:ss a", "h:mm:ss a", "HH:mm", "H:mm", "HH:mm:ss", "H:mm:ss"
// clang-format on // clang-format on
@ -34,59 +33,95 @@ namespace chatterino {
AppearancePage::AppearancePage() AppearancePage::AppearancePage()
: SettingsPage("Look", ":/images/theme.svg") : SettingsPage("Look", ":/images/theme.svg")
{ {
auto app = getApp();
LayoutCreator<AppearancePage> layoutCreator(this); LayoutCreator<AppearancePage> layoutCreator(this);
auto scroll = layoutCreator.emplace<QScrollArea>(); auto xd = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
// settings
auto scroll = xd.emplace<QScrollArea>();
auto widget = scroll.emplaceScrollAreaWidget(); auto widget = scroll.emplaceScrollAreaWidget();
removeScrollAreaBackground(scroll.getElement(), widget.getElement()); removeScrollAreaBackground(scroll.getElement(), widget.getElement());
auto layout = widget.setLayoutType<QVBoxLayout>(); auto &layout = *widget.setLayoutType<QVBoxLayout>().withoutMargin();
auto application = this->addApplicationGroup(layout);
layout.emplace<QGroupBox>("Application").emplace<QVBoxLayout>().withoutMargin(); this->addMessagesGroup(layout);
{ this->addEmotesGroup(layout);
auto form = application.emplace<QFormLayout>();
auto *theme = this->createComboBox({THEME_ITEMS}, app->themes->themeName); // preview
xd.emplace<Line>(false);
auto channelView = xd.emplace<ChannelView>();
auto channel = this->createPreviewChannel();
channelView->setChannel(channel);
channelView->setScaleIndependantHeight(64);
layout.addStretch(1);
}
void AppearancePage::addApplicationGroup(QVBoxLayout &layout)
{
auto box = LayoutCreator<QVBoxLayout>(&layout)
.emplace<QGroupBox>("Application")
.emplace<QVBoxLayout>()
.withoutMargin();
auto form = box.emplace<QFormLayout>();
// theme
auto *theme = this->createComboBox({THEME_ITEMS}, getApp()->themes->themeName);
QObject::connect(theme, &QComboBox::currentTextChanged, QObject::connect(theme, &QComboBox::currentTextChanged,
[](const QString &) { getApp()->windows->forceLayoutChannelViews(); }); [](const QString &) { getApp()->windows->forceLayoutChannelViews(); });
form->addRow("Theme:", theme); form->addRow("Theme:", theme);
// form->addRow("Theme color:", this->createThemeColorChanger());
// ui scale
form->addRow("UI Scaling:", this->createUiScaleSlider()); form->addRow("UI Scaling:", this->createUiScaleSlider());
// font
form->addRow("Font:", this->createFontChanger()); form->addRow("Font:", this->createFontChanger());
form->addRow("Tabs:", this->createCheckBox(TAB_X, app->settings->showTabCloseButton)); // tab x
form->addRow("Tabs:", this->createCheckBox(TAB_X, getSettings()->showTabCloseButton));
// show buttons
#ifndef USEWINSDK #ifndef USEWINSDK
form->addRow("", this->createCheckBox(TAB_PREF, app->settings->hidePreferencesButton)); form->addRow("", this->createCheckBox(TAB_PREF, app->settings->hidePreferencesButton));
form->addRow("", this->createCheckBox(TAB_USER, app->settings->hideUserButton)); form->addRow("", this->createCheckBox(TAB_USER, app->settings->hideUserButton));
#endif #endif
// scrolling
form->addRow("Scrolling:", form->addRow("Scrolling:",
this->createCheckBox(SCROLL_SMOOTH, app->settings->enableSmoothScrolling)); this->createCheckBox(SCROLL_SMOOTH, getSettings()->enableSmoothScrolling));
form->addRow("", this->createCheckBox(SCROLL_NEWMSG, form->addRow(
app->settings->enableSmoothScrollingNewMessages)); "", this->createCheckBox(SCROLL_NEWMSG, getSettings()->enableSmoothScrollingNewMessages));
} }
auto messages = layout.emplace<QGroupBox>("Messages").emplace<QVBoxLayout>(); void AppearancePage::addMessagesGroup(QVBoxLayout &layout)
{
auto box =
LayoutCreator<QVBoxLayout>(&layout).emplace<QGroupBox>("Messages").emplace<QVBoxLayout>();
// timestamps
box.append(this->createCheckBox("Show timestamps", getSettings()->showTimestamps));
auto tbox = box.emplace<QHBoxLayout>().withoutMargin();
{ {
messages.append(this->createCheckBox("Show timestamp", app->settings->showTimestamps)); tbox.emplace<QLabel>("Timestamp format (a = am/pm):");
auto tbox = messages.emplace<QHBoxLayout>().withoutMargin(); tbox.append(this->createComboBox({TIMESTAMP_FORMATS}, getSettings()->timestampFormat));
{
tbox.emplace<QLabel>("timestamp format (a = am/pm):");
tbox.append(this->createComboBox({TIMESTAMP_FORMATS}, app->settings->timestampFormat));
tbox->addStretch(1); tbox->addStretch(1);
} }
messages.append(this->createCheckBox("Show badges", app->settings->showBadges)); // badges
box.append(this->createCheckBox("Show badges", getSettings()->showBadges));
// collapsing
{ {
auto *combo = new QComboBox(this); auto *combo = new QComboBox(this);
combo->addItems({"Never", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", combo->addItems(
"13", "14", "15"}); {"Never", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"});
const auto currentIndex = []() -> int { const auto currentIndex = []() -> int {
auto val = getApp()->settings->collpseMessagesMinLines.getValue(); auto val = getSettings()->collpseMessagesMinLines.getValue();
if (val > 0) { if (val > 0) {
--val; --val;
} }
@ -95,63 +130,29 @@ AppearancePage::AppearancePage()
combo->setCurrentIndex(currentIndex); combo->setCurrentIndex(currentIndex);
QObject::connect(combo, &QComboBox::currentTextChanged, [](const QString &str) { QObject::connect(combo, &QComboBox::currentTextChanged, [](const QString &str) {
getApp()->settings->collpseMessagesMinLines = str.toInt(); getSettings()->collpseMessagesMinLines = str.toInt();
}); });
auto hbox = messages.emplace<QHBoxLayout>().withoutMargin(); auto hbox = box.emplace<QHBoxLayout>().withoutMargin();
hbox.emplace<QLabel>("Collapse messages longer than"); hbox.emplace<QLabel>("Collapse messages longer than");
hbox.append(combo); hbox.append(combo);
hbox.emplace<QLabel>("lines"); hbox.emplace<QLabel>("lines");
} }
messages.append(this->createCheckBox("Separate messages", app->settings->separateMessages)); // seperate
messages.append(this->createCheckBox("Alternate message background color", box.append(this->createCheckBox("Separation lines", getSettings()->separateMessages));
app->settings->alternateMessageBackground));
messages.append(this->createCheckBox("Show message length while typing",
app->settings->showMessageLength));
messages.append(this->createCheckBox(LAST_MSG, app->settings->showLastMessageIndicator)); // alternate
{ box.append(this->createCheckBox("Alternate background colors",
auto *combo = new QComboBox(this); getSettings()->alternateMessageBackground));
combo->addItems({"Dotted", "Solid"}); }
const auto currentIndex = []() -> int { void AppearancePage::addEmotesGroup(QVBoxLayout &layout)
switch (getApp()->settings->lastMessagePattern.getValue()) { {
case Qt::SolidLine: { auto box = LayoutCreator<QVBoxLayout>(&layout)
return 1; .emplace<QGroupBox>("Emotes")
} .setLayoutType<QVBoxLayout>();
default:
case Qt::VerPattern: {
return 0;
}
}
}();
combo->setCurrentIndex(currentIndex);
QObject::connect(combo,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
[](int index) {
Qt::BrushStyle brush;
switch (index) {
case 1:
brush = Qt::SolidPattern;
break;
default:
case 0:
brush = Qt::VerPattern;
break;
}
getApp()->settings->lastMessagePattern = brush;
});
auto hbox = messages.emplace<QHBoxLayout>().withoutMargin();
hbox.emplace<QLabel>("Last message indicator pattern");
hbox.append(combo);
}
}
auto emotes = layout.emplace<QGroupBox>("Emotes").setLayoutType<QVBoxLayout>();
{
/* /*
emotes.append( emotes.append(
this->createCheckBox("Enable Twitch emotes", app->settings->enableTwitchEmotes)); this->createCheckBox("Enable Twitch emotes", app->settings->enableTwitchEmotes));
@ -161,12 +162,11 @@ AppearancePage::AppearancePage()
app->settings->enableFfzEmotes)); app->settings->enableFfzEmotes));
emotes.append(this->createCheckBox("Enable emojis", app->settings->enableEmojis)); emotes.append(this->createCheckBox("Enable emojis", app->settings->enableEmojis));
*/ */
emotes.append( box.append(this->createCheckBox("Animated emotes", getSettings()->enableGifAnimations));
this->createCheckBox("Enable animations", app->settings->enableGifAnimations));
auto scaleBox = emotes.emplace<QHBoxLayout>(); auto scaleBox = box.emplace<QHBoxLayout>();
{ {
scaleBox.emplace<QLabel>("Emote scale:"); scaleBox.emplace<QLabel>("Size:");
auto emoteScale = scaleBox.emplace<QSlider>(Qt::Horizontal); auto emoteScale = scaleBox.emplace<QSlider>(Qt::Horizontal);
emoteScale->setMinimum(5); emoteScale->setMinimum(5);
@ -176,36 +176,63 @@ AppearancePage::AppearancePage()
scaleLabel->setFixedWidth(100); scaleLabel->setFixedWidth(100);
QObject::connect(emoteScale.getElement(), &QSlider::valueChanged, QObject::connect(emoteScale.getElement(), &QSlider::valueChanged,
[scaleLabel](int value) mutable { [scaleLabel](int value) mutable {
float f = (float)value / 10.f; float f = float(value) / 10.f;
scaleLabel->setText(QString::number(f)); scaleLabel->setText(QString::number(f));
getApp()->settings->emoteScale.setValue(f); getSettings()->emoteScale.setValue(f);
}); });
emoteScale->setValue(std::max<int>( emoteScale->setValue(
5, std::min<int>(50, (int)(app->settings->emoteScale.getValue() * 10.f)))); std::max<int>(5, std::min<int>(50, int(getSettings()->emoteScale.getValue() * 10.f))));
scaleLabel->setText(QString::number(app->settings->emoteScale.getValue())); scaleLabel->setText(QString::number(getSettings()->emoteScale.getValue()));
} }
{ {
auto *combo = new QComboBox(this); auto *combo = new QComboBox(this);
combo->addItems({"EmojiOne 2", "EmojiOne 3", "Twitter", "Facebook", "Apple", "Google", combo->addItems(
"Messenger"}); {"EmojiOne 2", "EmojiOne 3", "Twitter", "Facebook", "Apple", "Google", "Messenger"});
combo->setCurrentText(getApp()->settings->emojiSet); combo->setCurrentText(getSettings()->emojiSet);
QObject::connect(combo, &QComboBox::currentTextChanged, [](const QString &str) { QObject::connect(combo, &QComboBox::currentTextChanged, [](const QString &str) {
getApp()->settings->emojiSet = str; // getSettings()->emojiSet = str; //
}); });
auto hbox = emotes.emplace<QHBoxLayout>().withoutMargin(); auto hbox = box.emplace<QHBoxLayout>().withoutMargin();
hbox.emplace<QLabel>("Emoji set"); hbox.emplace<QLabel>("Emoji set:");
hbox.append(combo); hbox.append(combo);
} }
}
ChannelPtr AppearancePage::createPreviewChannel()
{
auto channel = ChannelPtr(new Channel("preview", Channel::Misc));
{
auto message = MessagePtr(new Message());
message->addElement(new ImageElement(getApp()->resources->badgeModerator,
MessageElement::BadgeChannelAuthority));
message->addElement(new ImageElement(getApp()->resources->badgeSubscriber,
MessageElement::BadgeSubscription));
message->addElement(new TimestampElement());
message->addElement(new TextElement("username1:", MessageElement::Username,
QColor("#0094FF"), FontStyle::ChatMediumBold));
message->addElement(new TextElement("This is a preview message :)", MessageElement::Text));
channel->addMessage(message);
}
{
auto message = MessagePtr(new Message());
message->addElement(new ImageElement(getApp()->resources->badgePremium,
MessageElement::BadgeChannelAuthority));
message->addElement(new TimestampElement());
message->addElement(new TextElement("username2:", MessageElement::Username,
QColor("#FF6A00"), FontStyle::ChatMediumBold));
message->addElement(new TextElement("This is another one :)", MessageElement::Text));
channel->addMessage(message);
} }
layout->addStretch(1); return channel;
} }
QLayout *AppearancePage::createThemeColorChanger() QLayout *AppearancePage::createThemeColorChanger()
@ -297,18 +324,15 @@ QLayout *AppearancePage::createUiScaleSlider()
slider->setMinimum(WindowManager::uiScaleMin); slider->setMinimum(WindowManager::uiScaleMin);
slider->setMaximum(WindowManager::uiScaleMax); slider->setMaximum(WindowManager::uiScaleMax);
slider->setValue( slider->setValue(WindowManager::clampUiScale(getSettings()->uiScale.getValue()));
WindowManager::clampUiScale(getApp()->settings->uiScale.getValue()));
label->setMinimumWidth(100); label->setMinimumWidth(100);
QObject::connect(slider, &QSlider::valueChanged, QObject::connect(slider, &QSlider::valueChanged,
[](auto value) { getApp()->settings->uiScale.setValue(value); }); [](auto value) { getSettings()->uiScale.setValue(value); });
getApp()->settings->uiScale.connect( getSettings()->uiScale.connect(
[label](auto, auto) { [label](auto, auto) { label->setText(QString::number(WindowManager::getUiScaleValue())); },
label->setText(QString::number(WindowManager::getUiScaleValue()));
},
this->connections_); this->connections_);
return layout; return layout;

View file

@ -1,10 +1,13 @@
#pragma once #pragma once
#include "common/Channel.hpp"
#include "widgets/settingspages/SettingsPage.hpp" #include "widgets/settingspages/SettingsPage.hpp"
#include <QScrollArea> #include <QScrollArea>
#include <pajlada/signals/signalholder.hpp> #include <pajlada/signals/signalholder.hpp>
class QVBoxLayout;
namespace chatterino { namespace chatterino {
class AppearancePage : public SettingsPage class AppearancePage : public SettingsPage
@ -12,10 +15,16 @@ class AppearancePage : public SettingsPage
public: public:
AppearancePage(); AppearancePage();
void addApplicationGroup(QVBoxLayout &layout);
void addMessagesGroup(QVBoxLayout &layout);
void addEmotesGroup(QVBoxLayout &layout);
QLayout *createThemeColorChanger(); QLayout *createThemeColorChanger();
QLayout *createFontChanger(); QLayout *createFontChanger();
QLayout *createUiScaleSlider(); QLayout *createUiScaleSlider();
ChannelPtr createPreviewChannel();
std::vector<pajlada::Signals::ScopedConnection> connections_; std::vector<pajlada::Signals::ScopedConnection> connections_;
}; };

View file

@ -15,6 +15,7 @@
#endif #endif
#define INPUT_EMPTY "Hide input box when empty" #define INPUT_EMPTY "Hide input box when empty"
#define PAUSE_HOVERING "When hovering" #define PAUSE_HOVERING "When hovering"
#define LAST_MSG "Mark the last message you read (dotted line)"
#define LIMIT_CHATTERS_FOR_SMALLER_STREAMERS "Only fetch chatters list for viewers under X viewers" #define LIMIT_CHATTERS_FOR_SMALLER_STREAMERS "Only fetch chatters list for viewers under X viewers"
@ -38,6 +39,48 @@ BehaviourPage::BehaviourPage()
form->addRow( form->addRow(
"", this->createCheckBox("Show which users parted the channel (up to 1000 chatters)", "", this->createCheckBox("Show which users parted the channel (up to 1000 chatters)",
app->settings->showParts)); app->settings->showParts));
form->addRow("", this->createCheckBox("Show message length while typing",
getSettings()->showMessageLength));
form->addRow("", this->createCheckBox(LAST_MSG, getSettings()->showLastMessageIndicator));
{
auto *combo = new QComboBox(this);
combo->addItems({"Dotted", "Solid"});
const auto currentIndex = []() -> int {
switch (getApp()->settings->lastMessagePattern.getValue()) {
case Qt::SolidLine: {
return 1;
}
default:
case Qt::VerPattern: {
return 0;
}
}
}();
combo->setCurrentIndex(currentIndex);
QObject::connect(combo,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
[](int index) {
Qt::BrushStyle brush;
switch (index) {
case 1:
brush = Qt::SolidPattern;
break;
default:
case 0:
brush = Qt::VerPattern;
break;
}
getSettings()->lastMessagePattern = brush;
});
auto hbox = form.emplace<QHBoxLayout>().withoutMargin();
hbox.emplace<QLabel>("Last message indicator pattern");
hbox.append(combo);
}
form->addRow("Pause chat:", form->addRow("Pause chat:",
this->createCheckBox(PAUSE_HOVERING, app->settings->pauseChatHover)); this->createCheckBox(PAUSE_HOVERING, app->settings->pauseChatHover));
@ -76,7 +119,7 @@ QSlider *BehaviourPage::createMouseScrollSlider()
auto slider = new QSlider(Qt::Horizontal); auto slider = new QSlider(Qt::Horizontal);
float currentValue = app->settings->mouseScrollMultiplier; float currentValue = app->settings->mouseScrollMultiplier;
int sliderValue = ((currentValue - 0.1f) / 2.f) * 99.f; int sliderValue = int(((currentValue - 0.1f) / 2.f) * 99.f);
slider->setValue(sliderValue); slider->setValue(sliderValue);
QObject::connect(slider, &QSlider::valueChanged, [=](int newValue) { QObject::connect(slider, &QSlider::valueChanged, [=](int newValue) {

View file

@ -1,6 +1,8 @@
#include "ModerationPage.hpp" #include "ModerationPage.hpp"
#include "Application.hpp" #include "Application.hpp"
#include "controllers/moderationactions/ModerationActionModel.hpp"
#include "controllers/moderationactions/ModerationActions.hpp"
#include "controllers/taggedusers/TaggedUsersController.hpp" #include "controllers/taggedusers/TaggedUsersController.hpp"
#include "controllers/taggedusers/TaggedUsersModel.hpp" #include "controllers/taggedusers/TaggedUsersModel.hpp"
#include "singletons/Logging.hpp" #include "singletons/Logging.hpp"
@ -8,6 +10,7 @@
#include "util/LayoutCreator.hpp" #include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp" #include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QFormLayout> #include <QFormLayout>
#include <QGroupBox> #include <QGroupBox>
#include <QHBoxLayout> #include <QHBoxLayout>
@ -82,7 +85,7 @@ ModerationPage::ModerationPage()
// Logs end // Logs end
} }
auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation mode"); auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation buttons");
{ {
// clang-format off // clang-format off
auto label = modMode.emplace<QLabel>("Click the moderation mod button (<img width='18' height='18' src=':/images/moderatormode_disabled.png'>) in a channel that you moderate to enable moderator mode.<br>"); auto label = modMode.emplace<QLabel>("Click the moderation mod button (<img width='18' height='18' src=':/images/moderatormode_disabled.png'>) in a channel that you moderate to enable moderator mode.<br>");
@ -97,26 +100,17 @@ ModerationPage::ModerationPage()
// app->settings->timeoutAction)); // app->settings->timeoutAction));
// } // }
// auto modButtons = EditableModelView *view =
// modMode.emplace<QGroupBox>("Custom moderator buttons").setLayoutType<QVBoxLayout>(); modMode.emplace<EditableModelView>(app->moderationActions->createModel(nullptr))
// { .getElement();
// auto label2 =
// modButtons.emplace<QLabel>("One action per line. {user} will be replaced with the
// "
// "username.<br>Example `/timeout {user} 120`<br>");
// label2->setWordWrap(true);
// auto text = modButtons.emplace<QTextEdit>().getElement(); view->setTitles({"Actions"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
// text->setPlainText(app->moderationActions->items); view->addButtonPressed.connect([] {
getApp()->moderationActions->items.appendItem(ModerationAction("/timeout {user} 300"));
// QObject::connect(text, &QTextEdit::textChanged, this, });
// [this] { this->itemsChangedTimer.start(200); });
// QObject::connect(&this->itemsChangedTimer, &QTimer::timeout, this, [text, app]() {
// app->windows->moderationActions = text->toPlainText();
// });
// }
/*auto taggedUsers = tabs.appendTab(new QVBoxLayout, "Tagged users"); /*auto taggedUsers = tabs.appendTab(new QVBoxLayout, "Tagged users");
{ {

View file

@ -581,8 +581,7 @@ void SplitContainer::decodeNodeRecusively(QJsonObject &obj, Node *node)
auto _type = _obj.value("type"); auto _type = _obj.value("type");
if (_type == "split") { if (_type == "split") {
auto *split = new Split(this); auto *split = new Split(this);
split->setChannel( split->setChannel(WindowManager::decodeChannel(_obj.value("data").toObject()));
WindowManager::decodeChannel(_obj.value("data").toObject()));
Node *_node = new Node(); Node *_node = new Node();
_node->parent = node; _node->parent = node;
@ -605,8 +604,7 @@ void SplitContainer::decodeNodeRecusively(QJsonObject &obj, Node *node)
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
if (node->getChildren().size() < 2) { if (node->getChildren().size() < 2) {
auto *split = new Split(this); auto *split = new Split(this);
split->setChannel( split->setChannel(WindowManager::decodeChannel(obj.value("data").toObject()));
WindowManager::decodeChannel(obj.value("data").toObject()));
this->insertSplit(split, direction, node); this->insertSplit(split, direction, node);
} }

7
tools/clang-format-all.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/bash
read -p "Are you sure you want to run clang-format on all files in src/? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
find src/ -iname "*.hpp" -o -iname "*.cpp" -exec clang-format -i {} \;
fi