Normalize line endings in already existing files

This commit is contained in:
Leon Richardt
2019-09-08 22:27:57 +02:00
parent 8064f8a49e
commit b06eb9df83
157 changed files with 15175 additions and 15175 deletions
+222 -222
View File
@@ -1,222 +1,222 @@
#include "AboutPage.hpp"
#include "debug/Log.hpp"
#include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp"
#include "widgets/helper/SignalLabel.hpp"
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QTextEdit>
#include <QTextStream>
#include <QVBoxLayout>
#define PIXMAP_WIDTH 500
namespace chatterino {
AboutPage::AboutPage()
: SettingsPage("About", ":/settings/about.svg")
{
LayoutCreator<AboutPage> layoutCreator(this);
auto scroll = layoutCreator.emplace<QScrollArea>();
auto widget = scroll.emplaceScrollAreaWidget();
removeScrollAreaBackground(scroll.getElement(), widget.getElement());
auto layout = widget.setLayoutType<QVBoxLayout>();
{
QPixmap pixmap;
pixmap.load(":/settings/aboutlogo.png");
auto logo = layout.emplace<QLabel>().assign(&this->logo_);
logo->setPixmap(pixmap);
logo->setFixedSize(PIXMAP_WIDTH,
PIXMAP_WIDTH * pixmap.height() / pixmap.width());
logo->setScaledContents(true);
// this does nothing
// QPalette palette;
// palette.setColor(QPalette::Text, Qt::white);
// palette.setColor(QPalette::Link, "#a5cdff");
// palette.setColor(QPalette::LinkVisited, "#a5cdff");
/*auto xd = layout.emplace<QGroupBox>("Created by...");
{
auto created = xd.emplace<QLabel>();
{
created->setText("Created by <a
href=\"https://github.com/fourtf\">fourtf</a><br>" "with big help from
pajlada."); created->setTextFormat(Qt::RichText);
created->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
created->setOpenExternalLinks(true);
// created->setPalette(palette);
}
// auto github = xd.emplace<QLabel>();
// {
// github->setText(
// "<a
href=\"https://github.com/fourtf/chatterino2\">Chatterino on
// Github</a>");
// github->setTextFormat(Qt::RichText);
// github->setTextInteractionFlags(Qt::TextBrowserInteraction |
// Qt::LinksAccessibleByKeyboard |
// Qt::LinksAccessibleByKeyboard);
// github->setOpenExternalLinks(true);
// // github->setPalette(palette);
// }
}*/
auto licenses =
layout.emplace<QGroupBox>("Open source software used...");
{
auto form = licenses.emplace<QFormLayout>();
addLicense(form.getElement(), "Qt Framework", "https://www.qt.io",
":/licenses/qt_lgpl-3.0.txt");
addLicense(form.getElement(), "Boost", "https://www.boost.org/",
":/licenses/boost_boost.txt");
addLicense(form.getElement(), "Fmt", "http://fmtlib.net/",
":/licenses/fmt_bsd2.txt");
addLicense(form.getElement(), "LibCommuni",
"https://github.com/communi/libcommuni",
":/licenses/libcommuni_BSD3.txt");
addLicense(form.getElement(), "OpenSSL", "https://www.openssl.org/",
":/licenses/openssl.txt");
addLicense(form.getElement(), "RapidJson", "http://rapidjson.org/",
":/licenses/rapidjson.txt");
addLicense(form.getElement(), "Pajlada/Settings",
"https://github.com/pajlada/settings",
":/licenses/pajlada_settings.txt");
addLicense(form.getElement(), "Pajlada/Signals",
"https://github.com/pajlada/signals",
":/licenses/pajlada_signals.txt");
addLicense(form.getElement(), "Websocketpp",
"https://www.zaphoyd.com/websocketpp/",
":/licenses/websocketpp.txt");
}
auto attributions = layout.emplace<QGroupBox>("Attributions...");
{
auto l = attributions.emplace<QVBoxLayout>();
// clang-format off
l.emplace<QLabel>("EmojiOne 2 and 3 emojis provided by <a href=\"https://www.emojione.com/\">EmojiOne</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Twemoji emojis provided by <a href=\"https://github.com/twitter/twemoji\">Twitter's Twemoji</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Facebook emojis provided by <a href=\"https://facebook.com\">Facebook</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Apple emojis provided by <a href=\"https://apple.com\">Apple</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Google emojis provided by <a href=\"https://google.com\">Google</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Messenger emojis provided by <a href=\"https://facebook.com\">Facebook</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Emoji datasource provided by <a href=\"https://www.iamcal.com/\">Cal Henderson</a>"
"(<a href=\"https://github.com/iamcal/emoji-data/blob/master/LICENSE\">show license</a>)")->setOpenExternalLinks(true);
l.emplace<QLabel>("Twitch emote data provided by <a href=\"https://twitchemotes.com/\">twitchemotes.com</a> through the <a href=\"https://github.com/Chatterino/api\">Chatterino API</a>")->setOpenExternalLinks(true);
// clang-format on
}
// Contributors
auto contributors = layout.emplace<QGroupBox>("Contributors");
{
auto l = contributors.emplace<QVBoxLayout>();
QFile contributorsFile(":/contributors.txt");
contributorsFile.open(QFile::ReadOnly);
QTextStream stream(&contributorsFile);
stream.setCodec("UTF-8");
QString line;
while (stream.readLineInto(&line))
{
if (line.isEmpty() || line.startsWith('#'))
{
continue;
}
QStringList contributorParts = line.split("|");
if (contributorParts.size() != 4)
{
log("Missing parts in line '{}'", line);
continue;
}
QString username = contributorParts[0].trimmed();
QString url = contributorParts[1].trimmed();
QString avatarUrl = contributorParts[2].trimmed();
QString role = contributorParts[3].trimmed();
auto *usernameLabel =
new QLabel("<a href=\"" + url + "\">" + username + "</a>");
usernameLabel->setOpenExternalLinks(true);
auto *roleLabel = new QLabel(role);
auto contributorBox2 = l.emplace<QHBoxLayout>();
const auto addAvatar = [&avatarUrl, &contributorBox2] {
if (!avatarUrl.isEmpty())
{
QPixmap avatarPixmap;
avatarPixmap.load(avatarUrl);
auto avatar = contributorBox2.emplace<QLabel>();
avatar->setPixmap(avatarPixmap);
avatar->setFixedSize(64, 64);
avatar->setScaledContents(true);
}
};
const auto addLabels = [&contributorBox2, &usernameLabel,
&roleLabel] {
auto labelBox = new QVBoxLayout();
contributorBox2->addLayout(labelBox);
labelBox->addWidget(usernameLabel);
labelBox->addWidget(roleLabel);
};
addLabels();
addAvatar();
}
}
}
auto buildInfo = QStringList();
buildInfo += "Qt " QT_VERSION_STR;
#ifdef USEWINSDK
buildInfo += "Windows SDK";
#endif
#ifdef _MSC_FULL_VER
buildInfo += "MSVC " + QString::number(_MSC_FULL_VER, 10);
#endif
auto buildText = QString("Built with " + buildInfo.join(", "));
layout.emplace<QLabel>(buildText);
layout->addStretch(1);
}
void AboutPage::addLicense(QFormLayout *form, const QString &name,
const QString &website, const QString &licenseLink)
{
auto *a = new QLabel("<a href=\"" + website + "\">" + name + "</a>");
a->setOpenExternalLinks(true);
auto *b = new QLabel("<a href=\"" + licenseLink + "\">show license</a>");
QObject::connect(b, &QLabel::linkActivated, [licenseLink] {
auto *edit = new QTextEdit;
QFile file(licenseLink);
file.open(QIODevice::ReadOnly);
edit->setText(file.readAll());
edit->setReadOnly(true);
edit->show();
});
form->addRow(a, b);
}
} // namespace chatterino
#include "AboutPage.hpp"
#include "debug/Log.hpp"
#include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp"
#include "widgets/helper/SignalLabel.hpp"
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QTextEdit>
#include <QTextStream>
#include <QVBoxLayout>
#define PIXMAP_WIDTH 500
namespace chatterino {
AboutPage::AboutPage()
: SettingsPage("About", ":/settings/about.svg")
{
LayoutCreator<AboutPage> layoutCreator(this);
auto scroll = layoutCreator.emplace<QScrollArea>();
auto widget = scroll.emplaceScrollAreaWidget();
removeScrollAreaBackground(scroll.getElement(), widget.getElement());
auto layout = widget.setLayoutType<QVBoxLayout>();
{
QPixmap pixmap;
pixmap.load(":/settings/aboutlogo.png");
auto logo = layout.emplace<QLabel>().assign(&this->logo_);
logo->setPixmap(pixmap);
logo->setFixedSize(PIXMAP_WIDTH,
PIXMAP_WIDTH * pixmap.height() / pixmap.width());
logo->setScaledContents(true);
// this does nothing
// QPalette palette;
// palette.setColor(QPalette::Text, Qt::white);
// palette.setColor(QPalette::Link, "#a5cdff");
// palette.setColor(QPalette::LinkVisited, "#a5cdff");
/*auto xd = layout.emplace<QGroupBox>("Created by...");
{
auto created = xd.emplace<QLabel>();
{
created->setText("Created by <a
href=\"https://github.com/fourtf\">fourtf</a><br>" "with big help from
pajlada."); created->setTextFormat(Qt::RichText);
created->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
created->setOpenExternalLinks(true);
// created->setPalette(palette);
}
// auto github = xd.emplace<QLabel>();
// {
// github->setText(
// "<a
href=\"https://github.com/fourtf/chatterino2\">Chatterino on
// Github</a>");
// github->setTextFormat(Qt::RichText);
// github->setTextInteractionFlags(Qt::TextBrowserInteraction |
// Qt::LinksAccessibleByKeyboard |
// Qt::LinksAccessibleByKeyboard);
// github->setOpenExternalLinks(true);
// // github->setPalette(palette);
// }
}*/
auto licenses =
layout.emplace<QGroupBox>("Open source software used...");
{
auto form = licenses.emplace<QFormLayout>();
addLicense(form.getElement(), "Qt Framework", "https://www.qt.io",
":/licenses/qt_lgpl-3.0.txt");
addLicense(form.getElement(), "Boost", "https://www.boost.org/",
":/licenses/boost_boost.txt");
addLicense(form.getElement(), "Fmt", "http://fmtlib.net/",
":/licenses/fmt_bsd2.txt");
addLicense(form.getElement(), "LibCommuni",
"https://github.com/communi/libcommuni",
":/licenses/libcommuni_BSD3.txt");
addLicense(form.getElement(), "OpenSSL", "https://www.openssl.org/",
":/licenses/openssl.txt");
addLicense(form.getElement(), "RapidJson", "http://rapidjson.org/",
":/licenses/rapidjson.txt");
addLicense(form.getElement(), "Pajlada/Settings",
"https://github.com/pajlada/settings",
":/licenses/pajlada_settings.txt");
addLicense(form.getElement(), "Pajlada/Signals",
"https://github.com/pajlada/signals",
":/licenses/pajlada_signals.txt");
addLicense(form.getElement(), "Websocketpp",
"https://www.zaphoyd.com/websocketpp/",
":/licenses/websocketpp.txt");
}
auto attributions = layout.emplace<QGroupBox>("Attributions...");
{
auto l = attributions.emplace<QVBoxLayout>();
// clang-format off
l.emplace<QLabel>("EmojiOne 2 and 3 emojis provided by <a href=\"https://www.emojione.com/\">EmojiOne</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Twemoji emojis provided by <a href=\"https://github.com/twitter/twemoji\">Twitter's Twemoji</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Facebook emojis provided by <a href=\"https://facebook.com\">Facebook</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Apple emojis provided by <a href=\"https://apple.com\">Apple</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Google emojis provided by <a href=\"https://google.com\">Google</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Messenger emojis provided by <a href=\"https://facebook.com\">Facebook</a>")->setOpenExternalLinks(true);
l.emplace<QLabel>("Emoji datasource provided by <a href=\"https://www.iamcal.com/\">Cal Henderson</a>"
"(<a href=\"https://github.com/iamcal/emoji-data/blob/master/LICENSE\">show license</a>)")->setOpenExternalLinks(true);
l.emplace<QLabel>("Twitch emote data provided by <a href=\"https://twitchemotes.com/\">twitchemotes.com</a> through the <a href=\"https://github.com/Chatterino/api\">Chatterino API</a>")->setOpenExternalLinks(true);
// clang-format on
}
// Contributors
auto contributors = layout.emplace<QGroupBox>("Contributors");
{
auto l = contributors.emplace<QVBoxLayout>();
QFile contributorsFile(":/contributors.txt");
contributorsFile.open(QFile::ReadOnly);
QTextStream stream(&contributorsFile);
stream.setCodec("UTF-8");
QString line;
while (stream.readLineInto(&line))
{
if (line.isEmpty() || line.startsWith('#'))
{
continue;
}
QStringList contributorParts = line.split("|");
if (contributorParts.size() != 4)
{
log("Missing parts in line '{}'", line);
continue;
}
QString username = contributorParts[0].trimmed();
QString url = contributorParts[1].trimmed();
QString avatarUrl = contributorParts[2].trimmed();
QString role = contributorParts[3].trimmed();
auto *usernameLabel =
new QLabel("<a href=\"" + url + "\">" + username + "</a>");
usernameLabel->setOpenExternalLinks(true);
auto *roleLabel = new QLabel(role);
auto contributorBox2 = l.emplace<QHBoxLayout>();
const auto addAvatar = [&avatarUrl, &contributorBox2] {
if (!avatarUrl.isEmpty())
{
QPixmap avatarPixmap;
avatarPixmap.load(avatarUrl);
auto avatar = contributorBox2.emplace<QLabel>();
avatar->setPixmap(avatarPixmap);
avatar->setFixedSize(64, 64);
avatar->setScaledContents(true);
}
};
const auto addLabels = [&contributorBox2, &usernameLabel,
&roleLabel] {
auto labelBox = new QVBoxLayout();
contributorBox2->addLayout(labelBox);
labelBox->addWidget(usernameLabel);
labelBox->addWidget(roleLabel);
};
addLabels();
addAvatar();
}
}
}
auto buildInfo = QStringList();
buildInfo += "Qt " QT_VERSION_STR;
#ifdef USEWINSDK
buildInfo += "Windows SDK";
#endif
#ifdef _MSC_FULL_VER
buildInfo += "MSVC " + QString::number(_MSC_FULL_VER, 10);
#endif
auto buildText = QString("Built with " + buildInfo.join(", "));
layout.emplace<QLabel>(buildText);
layout->addStretch(1);
}
void AboutPage::addLicense(QFormLayout *form, const QString &name,
const QString &website, const QString &licenseLink)
{
auto *a = new QLabel("<a href=\"" + website + "\">" + name + "</a>");
a->setOpenExternalLinks(true);
auto *b = new QLabel("<a href=\"" + licenseLink + "\">show license</a>");
QObject::connect(b, &QLabel::linkActivated, [licenseLink] {
auto *edit = new QTextEdit;
QFile file(licenseLink);
file.open(QIODevice::ReadOnly);
edit->setText(file.readAll());
edit->setReadOnly(true);
edit->show();
});
form->addRow(a, b);
}
} // namespace chatterino
+22 -22
View File
@@ -1,22 +1,22 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
class QLabel;
class QFormLayout;
namespace chatterino {
class AboutPage : public SettingsPage
{
public:
AboutPage();
private:
void addLicense(QFormLayout *form, const QString &name_,
const QString &website, const QString &licenseLink);
QLabel *logo_;
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
class QLabel;
class QFormLayout;
namespace chatterino {
class AboutPage : public SettingsPage
{
public:
AboutPage();
private:
void addLicense(QFormLayout *form, const QString &name_,
const QString &website, const QString &licenseLink);
QLabel *logo_;
};
} // namespace chatterino
+69 -69
View File
@@ -1,69 +1,69 @@
#include "AccountsPage.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/accounts/AccountModel.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/dialogs/LoginDialog.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QTableView>
#include <QVBoxLayout>
#include <algorithm>
namespace chatterino {
AccountsPage::AccountsPage()
: SettingsPage("Accounts", ":/settings/accounts.svg")
{
auto *app = getApp();
LayoutCreator<AccountsPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
EditableModelView *view =
layout.emplace<EditableModelView>(app->accounts->createModel(nullptr))
.getElement();
view->getTableView()->horizontalHeader()->setVisible(false);
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
static auto loginWidget = new LoginWidget();
loginWidget->show();
loginWidget->raise();
});
view->getTableView()->setStyleSheet("background: #333");
// auto buttons = layout.emplace<QDialogButtonBox>();
// {
// this->addButton = buttons->addButton("Add",
// QDialogButtonBox::YesRole); this->removeButton =
// buttons->addButton("Remove", QDialogButtonBox::NoRole);
// }
// layout.emplace<AccountSwitchWidget>(this).assign(&this->accSwitchWidget);
// ----
// QObject::connect(this->addButton, &QPushButton::clicked, []() {
// static auto loginWidget = new LoginWidget();
// loginWidget->show();
// });
// QObject::connect(this->removeButton, &QPushButton::clicked, [this] {
// auto selectedUser = this->accSwitchWidget->currentItem()->text();
// if (selectedUser == ANONYMOUS_USERNAME_LABEL) {
// // Do nothing
// return;
// }
// getApp()->accounts->Twitch.removeUser(selectedUser);
// });
}
} // namespace chatterino
#include "AccountsPage.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/accounts/AccountModel.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/dialogs/LoginDialog.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QTableView>
#include <QVBoxLayout>
#include <algorithm>
namespace chatterino {
AccountsPage::AccountsPage()
: SettingsPage("Accounts", ":/settings/accounts.svg")
{
auto *app = getApp();
LayoutCreator<AccountsPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
EditableModelView *view =
layout.emplace<EditableModelView>(app->accounts->createModel(nullptr))
.getElement();
view->getTableView()->horizontalHeader()->setVisible(false);
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
static auto loginWidget = new LoginWidget();
loginWidget->show();
loginWidget->raise();
});
view->getTableView()->setStyleSheet("background: #333");
// auto buttons = layout.emplace<QDialogButtonBox>();
// {
// this->addButton = buttons->addButton("Add",
// QDialogButtonBox::YesRole); this->removeButton =
// buttons->addButton("Remove", QDialogButtonBox::NoRole);
// }
// layout.emplace<AccountSwitchWidget>(this).assign(&this->accSwitchWidget);
// ----
// QObject::connect(this->addButton, &QPushButton::clicked, []() {
// static auto loginWidget = new LoginWidget();
// loginWidget->show();
// });
// QObject::connect(this->removeButton, &QPushButton::clicked, [this] {
// auto selectedUser = this->accSwitchWidget->currentItem()->text();
// if (selectedUser == ANONYMOUS_USERNAME_LABEL) {
// // Do nothing
// return;
// }
// getApp()->accounts->Twitch.removeUser(selectedUser);
// });
}
} // namespace chatterino
+21 -21
View File
@@ -1,21 +1,21 @@
#pragma once
#include "widgets/AccountSwitchWidget.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
#include <QPushButton>
namespace chatterino {
class AccountsPage : public SettingsPage
{
public:
AccountsPage();
private:
QPushButton *addButton_;
QPushButton *removeButton_;
AccountSwitchWidget *accountSwitchWidget_;
};
} // namespace chatterino
#pragma once
#include "widgets/AccountSwitchWidget.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
#include <QPushButton>
namespace chatterino {
class AccountsPage : public SettingsPage
{
public:
AccountsPage();
private:
QPushButton *addButton_;
QPushButton *removeButton_;
AccountSwitchWidget *accountSwitchWidget_;
};
} // namespace chatterino
+88 -88
View File
@@ -1,88 +1,88 @@
#include "CommandPage.hpp"
#include <QHeaderView>
#include <QLabel>
#include <QPushButton>
#include <QStandardItemModel>
#include <QTableView>
#include <QTextEdit>
#include "Application.hpp"
#include "controllers/commands/CommandController.hpp"
#include "controllers/commands/CommandModel.hpp"
#include "util/LayoutCreator.hpp"
#include "util/StandardItemHelper.hpp"
#include "widgets/helper/EditableModelView.hpp"
//#include "widgets/helper/ComboBoxItemDelegate.hpp"
#include "util/CombinePath.hpp"
#include <QLabel>
#include <QTextEdit>
// clang-format off
#define TEXT "{1} => first word &nbsp;&nbsp;&nbsp; {1+} => first word and after &nbsp;&nbsp;&nbsp; {{ => { &nbsp;&nbsp;&nbsp; <a href='https://chatterino.com/help/commands'>more info</a>"
// clang-format on
namespace chatterino {
namespace {
QString c1settingsPath()
{
return combinePath(qgetenv("appdata"),
"Chatterino\\Custom\\Commands.txt");
}
} // namespace
CommandPage::CommandPage()
: SettingsPage("Commands", ":/settings/commands.svg")
{
auto app = getApp();
LayoutCreator<CommandPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
EditableModelView *view =
layout.emplace<EditableModelView>(app->commands->createModel(nullptr))
.getElement();
view->setTitles({"Trigger", "Command"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->commands->items_.appendItem(
Command{"/command", "I made a new command HeyGuys"});
});
if (QFile(c1settingsPath()).exists())
{
auto button = new QPushButton("Import commands from Chatterino 1");
view->addCustomButton(button);
QObject::connect(button, &QPushButton::clicked, this, [] {
QFile c1settings = c1settingsPath();
c1settings.open(QIODevice::ReadOnly);
for (auto line : QString(c1settings.readAll())
.split(QRegularExpression("[\r\n]"),
QString::SkipEmptyParts))
{
if (int index = line.indexOf(' '); index != -1)
{
getApp()->commands->items_.insertItem(
Command(line.mid(0, index), line.mid(index + 1)));
}
}
});
}
layout.append(
this->createCheckBox("Also match the trigger at the end of the message",
getSettings()->allowCommandsAtEnd));
QLabel *text = layout.emplace<QLabel>(TEXT).getElement();
text->setWordWrap(true);
text->setStyleSheet("color: #bbb");
text->setOpenExternalLinks(true);
// ---- end of layout
this->commandsEditTimer_.setSingleShot(true);
}
} // namespace chatterino
#include "CommandPage.hpp"
#include <QHeaderView>
#include <QLabel>
#include <QPushButton>
#include <QStandardItemModel>
#include <QTableView>
#include <QTextEdit>
#include "Application.hpp"
#include "controllers/commands/CommandController.hpp"
#include "controllers/commands/CommandModel.hpp"
#include "util/LayoutCreator.hpp"
#include "util/StandardItemHelper.hpp"
#include "widgets/helper/EditableModelView.hpp"
//#include "widgets/helper/ComboBoxItemDelegate.hpp"
#include "util/CombinePath.hpp"
#include <QLabel>
#include <QTextEdit>
// clang-format off
#define TEXT "{1} => first word &nbsp;&nbsp;&nbsp; {1+} => first word and after &nbsp;&nbsp;&nbsp; {{ => { &nbsp;&nbsp;&nbsp; <a href='https://chatterino.com/help/commands'>more info</a>"
// clang-format on
namespace chatterino {
namespace {
QString c1settingsPath()
{
return combinePath(qgetenv("appdata"),
"Chatterino\\Custom\\Commands.txt");
}
} // namespace
CommandPage::CommandPage()
: SettingsPage("Commands", ":/settings/commands.svg")
{
auto app = getApp();
LayoutCreator<CommandPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
EditableModelView *view =
layout.emplace<EditableModelView>(app->commands->createModel(nullptr))
.getElement();
view->setTitles({"Trigger", "Command"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->commands->items_.appendItem(
Command{"/command", "I made a new command HeyGuys"});
});
if (QFile(c1settingsPath()).exists())
{
auto button = new QPushButton("Import commands from Chatterino 1");
view->addCustomButton(button);
QObject::connect(button, &QPushButton::clicked, this, [] {
QFile c1settings = c1settingsPath();
c1settings.open(QIODevice::ReadOnly);
for (auto line : QString(c1settings.readAll())
.split(QRegularExpression("[\r\n]"),
QString::SkipEmptyParts))
{
if (int index = line.indexOf(' '); index != -1)
{
getApp()->commands->items_.insertItem(
Command(line.mid(0, index), line.mid(index + 1)));
}
}
});
}
layout.append(
this->createCheckBox("Also match the trigger at the end of the message",
getSettings()->allowCommandsAtEnd));
QLabel *text = layout.emplace<QLabel>(TEXT).getElement();
text->setWordWrap(true);
text->setStyleSheet("color: #bbb");
text->setOpenExternalLinks(true);
// ---- end of layout
this->commandsEditTimer_.setSingleShot(true);
}
} // namespace chatterino
+19 -19
View File
@@ -1,19 +1,19 @@
#pragma once
#include <QTextEdit>
#include <QTimer>
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class CommandPage : public SettingsPage
{
public:
CommandPage();
private:
QTimer commandsEditTimer_;
};
} // namespace chatterino
#pragma once
#include <QTextEdit>
#include <QTimer>
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class CommandPage : public SettingsPage
{
public:
CommandPage();
private:
QTimer commandsEditTimer_;
};
} // namespace chatterino
+74 -74
View File
@@ -1,74 +1,74 @@
#include "ExternalToolsPage.hpp"
#include "Application.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include <QGroupBox>
#define STREAMLINK_QUALITY \
"Choose", "Source", "High", "Medium", "Low", "Audio only"
namespace chatterino {
ExternalToolsPage::ExternalToolsPage()
: SettingsPage("External tools", ":/settings/externaltools.svg")
{
LayoutCreator<ExternalToolsPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
{
auto group = layout.emplace<QGroupBox>("Streamlink");
auto groupLayout = group.setLayoutType<QFormLayout>();
auto description = new QLabel(
"Streamlink is a command-line utility that pipes video streams "
"from various "
"services into a video player, such as VLC. Make sure to edit the "
"configuration file before you use it!");
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");
auto links = new QLabel(
createNamedLink("https://streamlink.github.io/", "Website") + " " +
createNamedLink(
"https://github.com/streamlink/streamlink/releases/latest",
"Download"));
links->setTextFormat(Qt::RichText);
links->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
links->setOpenExternalLinks(true);
groupLayout->setWidget(0, QFormLayout::SpanningRole, description);
groupLayout->setWidget(1, QFormLayout::SpanningRole, links);
auto customPathCb =
this->createCheckBox("Use custom path (Enable if using "
"non-standard streamlink installation path)",
getSettings()->streamlinkUseCustomPath);
groupLayout->setWidget(2, QFormLayout::SpanningRole, customPathCb);
auto customPath = this->createLineEdit(getSettings()->streamlinkPath);
customPath->setPlaceholderText(
"Path to folder where Streamlink executable can be found");
groupLayout->addRow("Custom streamlink path:", customPath);
groupLayout->addRow(
"Preferred quality:",
this->createComboBox({STREAMLINK_QUALITY},
getSettings()->preferredQuality));
groupLayout->addRow(
"Additional options:",
this->createLineEdit(getSettings()->streamlinkOpts));
getSettings()->streamlinkUseCustomPath.connect(
[=](const auto &value, auto) {
customPath->setEnabled(value); //
},
this->managedConnections_);
}
layout->addStretch(1);
}
} // namespace chatterino
#include "ExternalToolsPage.hpp"
#include "Application.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include <QGroupBox>
#define STREAMLINK_QUALITY \
"Choose", "Source", "High", "Medium", "Low", "Audio only"
namespace chatterino {
ExternalToolsPage::ExternalToolsPage()
: SettingsPage("External tools", ":/settings/externaltools.svg")
{
LayoutCreator<ExternalToolsPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
{
auto group = layout.emplace<QGroupBox>("Streamlink");
auto groupLayout = group.setLayoutType<QFormLayout>();
auto description = new QLabel(
"Streamlink is a command-line utility that pipes video streams "
"from various "
"services into a video player, such as VLC. Make sure to edit the "
"configuration file before you use it!");
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");
auto links = new QLabel(
createNamedLink("https://streamlink.github.io/", "Website") + " " +
createNamedLink(
"https://github.com/streamlink/streamlink/releases/latest",
"Download"));
links->setTextFormat(Qt::RichText);
links->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
links->setOpenExternalLinks(true);
groupLayout->setWidget(0, QFormLayout::SpanningRole, description);
groupLayout->setWidget(1, QFormLayout::SpanningRole, links);
auto customPathCb =
this->createCheckBox("Use custom path (Enable if using "
"non-standard streamlink installation path)",
getSettings()->streamlinkUseCustomPath);
groupLayout->setWidget(2, QFormLayout::SpanningRole, customPathCb);
auto customPath = this->createLineEdit(getSettings()->streamlinkPath);
customPath->setPlaceholderText(
"Path to folder where Streamlink executable can be found");
groupLayout->addRow("Custom streamlink path:", customPath);
groupLayout->addRow(
"Preferred quality:",
this->createComboBox({STREAMLINK_QUALITY},
getSettings()->preferredQuality));
groupLayout->addRow(
"Additional options:",
this->createLineEdit(getSettings()->streamlinkOpts));
getSettings()->streamlinkUseCustomPath.connect(
[=](const auto &value, auto) {
customPath->setEnabled(value); //
},
this->managedConnections_);
}
layout->addStretch(1);
}
} // namespace chatterino
+13 -13
View File
@@ -1,13 +1,13 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class ExternalToolsPage : public SettingsPage
{
public:
ExternalToolsPage();
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class ExternalToolsPage : public SettingsPage
{
public:
ExternalToolsPage();
};
} // namespace chatterino
File diff suppressed because it is too large Load Diff
+191 -191
View File
@@ -1,191 +1,191 @@
#pragma once
#include <QDebug>
#include <QLabel>
#include <QVBoxLayout>
#include "Application.hpp"
#include "boost/variant.hpp"
#include "pajlada/signals/signal.hpp"
#include "singletons/Settings.hpp"
#include "singletons/WindowManager.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
class QLabel;
class QCheckBox;
class QComboBox;
namespace chatterino {
class TitleLabel : public QLabel
{
Q_OBJECT
public:
TitleLabel(const QString &text)
: QLabel(text)
{
}
};
class DescriptionLabel : public QLabel
{
Q_OBJECT
public:
DescriptionLabel(const QString &text)
: QLabel(text)
{
}
};
struct DropdownArgs {
QString value;
int index;
QComboBox *combobox;
};
class ComboBox : public QComboBox
{
Q_OBJECT
void wheelEvent(QWheelEvent *event) override
{
}
};
class SettingsLayout : public QVBoxLayout
{
Q_OBJECT
public:
TitleLabel *addTitle(const QString &text);
/// @param inverse Inverses true to false and vice versa
QCheckBox *addCheckbox(const QString &text, BoolSetting &setting,
bool inverse = false);
ComboBox *addDropdown(const QString &text, const QStringList &items);
ComboBox *addDropdown(const QString &text, const QStringList &items,
pajlada::Settings::Setting<QString> &setting,
bool editable = false);
template <typename OnClick>
QPushButton *makeButton(const QString &text, OnClick onClick)
{
auto button = new QPushButton(text);
this->groups_.back().widgets.push_back({button, {text}});
QObject::connect(button, &QPushButton::clicked, onClick);
return button;
}
template <typename OnClick>
QPushButton *addButton(const QString &text, OnClick onClick)
{
auto button = makeButton(text, onClick);
auto layout = new QHBoxLayout();
layout->addWidget(button);
layout->addStretch(1);
this->addLayout(layout);
return button;
}
template <typename T>
ComboBox *addDropdown(
const QString &text, const QStringList &items,
pajlada::Settings::Setting<T> &setting,
std::function<boost::variant<int, QString>(T)> getValue,
std::function<T(DropdownArgs)> setValue, bool editable = true)
{
auto items2 = items;
auto selected = getValue(setting.getValue());
if (selected.which() == 1)
{
// QString
if (!editable && !items2.contains(boost::get<QString>(selected)))
items2.insert(0, boost::get<QString>(selected));
}
auto combo = this->addDropdown(text, items2);
if (editable)
combo->setEditable(true);
if (selected.which() == 0)
{
// int
auto value = boost::get<int>(selected);
if (value >= 0 && value < items2.size())
combo->setCurrentIndex(value);
}
else if (selected.which() == 1)
{
// QString
combo->setEditText(boost::get<QString>(selected));
}
setting.connect(
[getValue = std::move(getValue), combo](const T &value, auto) {
auto var = getValue(value);
if (var.which() == 0)
combo->setCurrentIndex(boost::get<int>(var));
else
{
combo->setCurrentText(boost::get<QString>(var));
combo->setEditText(boost::get<QString>(var));
}
},
this->managedConnections_);
QObject::connect(
combo,
QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
// &QComboBox::editTextChanged,
[combo, &setting,
setValue = std::move(setValue)](const QString &newValue) {
setting = setValue(
DropdownArgs{newValue, combo->currentIndex(), combo});
getApp()->windows->forceLayoutChannelViews();
});
return combo;
}
DescriptionLabel *addDescription(const QString &text);
void addSeperator();
bool filterElements(const QString &query);
private:
struct Widget {
QWidget *element;
QStringList keywords;
};
struct Group {
QString name;
QWidget *title{};
std::vector<Widget> widgets;
};
std::vector<Group> groups_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
class GeneralPage : public SettingsPage
{
Q_OBJECT
public:
GeneralPage();
bool filterElements(const QString &query);
private:
void initLayout(SettingsLayout &layout);
void initExtra();
QString getFont(const DropdownArgs &args) const;
DescriptionLabel *cachePath_{};
SettingsLayout *settingsLayout_{};
};
} // namespace chatterino
#pragma once
#include <QDebug>
#include <QLabel>
#include <QVBoxLayout>
#include "Application.hpp"
#include "boost/variant.hpp"
#include "pajlada/signals/signal.hpp"
#include "singletons/Settings.hpp"
#include "singletons/WindowManager.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
class QLabel;
class QCheckBox;
class QComboBox;
namespace chatterino {
class TitleLabel : public QLabel
{
Q_OBJECT
public:
TitleLabel(const QString &text)
: QLabel(text)
{
}
};
class DescriptionLabel : public QLabel
{
Q_OBJECT
public:
DescriptionLabel(const QString &text)
: QLabel(text)
{
}
};
struct DropdownArgs {
QString value;
int index;
QComboBox *combobox;
};
class ComboBox : public QComboBox
{
Q_OBJECT
void wheelEvent(QWheelEvent *event) override
{
}
};
class SettingsLayout : public QVBoxLayout
{
Q_OBJECT
public:
TitleLabel *addTitle(const QString &text);
/// @param inverse Inverses true to false and vice versa
QCheckBox *addCheckbox(const QString &text, BoolSetting &setting,
bool inverse = false);
ComboBox *addDropdown(const QString &text, const QStringList &items);
ComboBox *addDropdown(const QString &text, const QStringList &items,
pajlada::Settings::Setting<QString> &setting,
bool editable = false);
template <typename OnClick>
QPushButton *makeButton(const QString &text, OnClick onClick)
{
auto button = new QPushButton(text);
this->groups_.back().widgets.push_back({button, {text}});
QObject::connect(button, &QPushButton::clicked, onClick);
return button;
}
template <typename OnClick>
QPushButton *addButton(const QString &text, OnClick onClick)
{
auto button = makeButton(text, onClick);
auto layout = new QHBoxLayout();
layout->addWidget(button);
layout->addStretch(1);
this->addLayout(layout);
return button;
}
template <typename T>
ComboBox *addDropdown(
const QString &text, const QStringList &items,
pajlada::Settings::Setting<T> &setting,
std::function<boost::variant<int, QString>(T)> getValue,
std::function<T(DropdownArgs)> setValue, bool editable = true)
{
auto items2 = items;
auto selected = getValue(setting.getValue());
if (selected.which() == 1)
{
// QString
if (!editable && !items2.contains(boost::get<QString>(selected)))
items2.insert(0, boost::get<QString>(selected));
}
auto combo = this->addDropdown(text, items2);
if (editable)
combo->setEditable(true);
if (selected.which() == 0)
{
// int
auto value = boost::get<int>(selected);
if (value >= 0 && value < items2.size())
combo->setCurrentIndex(value);
}
else if (selected.which() == 1)
{
// QString
combo->setEditText(boost::get<QString>(selected));
}
setting.connect(
[getValue = std::move(getValue), combo](const T &value, auto) {
auto var = getValue(value);
if (var.which() == 0)
combo->setCurrentIndex(boost::get<int>(var));
else
{
combo->setCurrentText(boost::get<QString>(var));
combo->setEditText(boost::get<QString>(var));
}
},
this->managedConnections_);
QObject::connect(
combo,
QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
// &QComboBox::editTextChanged,
[combo, &setting,
setValue = std::move(setValue)](const QString &newValue) {
setting = setValue(
DropdownArgs{newValue, combo->currentIndex(), combo});
getApp()->windows->forceLayoutChannelViews();
});
return combo;
}
DescriptionLabel *addDescription(const QString &text);
void addSeperator();
bool filterElements(const QString &query);
private:
struct Widget {
QWidget *element;
QStringList keywords;
};
struct Group {
QString name;
QWidget *title{};
std::vector<Widget> widgets;
};
std::vector<Group> groups_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
class GeneralPage : public SettingsPage
{
Q_OBJECT
public:
GeneralPage();
bool filterElements(const QString &query);
private:
void initLayout(SettingsLayout &layout);
void initExtra();
QString getFont(const DropdownArgs &args) const;
DescriptionLabel *cachePath_{};
SettingsLayout *settingsLayout_{};
};
} // namespace chatterino
+175 -175
View File
@@ -1,175 +1,175 @@
#include "HighlightingPage.hpp"
#include "Application.hpp"
#include "controllers/highlights/HighlightBlacklistModel.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "controllers/highlights/HighlightModel.hpp"
#include "controllers/highlights/UserHighlightModel.hpp"
#include "debug/Log.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "util/StandardItemHelper.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QHeaderView>
#include <QListWidget>
#include <QPushButton>
#include <QStandardItemModel>
#include <QTabWidget>
#include <QTableView>
#include <QTextEdit>
#define ENABLE_HIGHLIGHTS "Enable Highlighting"
#define HIGHLIGHT_MSG "Highlight messages containing your name"
#define PLAY_SOUND "Play sound when your name is mentioned"
#define FLASH_TASKBAR "Flash taskbar when your name is mentioned"
#define ALWAYS_PLAY "Play highlight sound even when Chatterino is focused"
namespace chatterino {
HighlightingPage::HighlightingPage()
: SettingsPage("Highlights", ":/settings/notifications.svg")
{
auto app = getApp();
LayoutCreator<HighlightingPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
{
// GENERAL
// layout.append(this->createCheckBox(ENABLE_HIGHLIGHTS,
// getSettings()->enableHighlights));
// TABS
auto tabs = layout.emplace<QTabWidget>();
{
// HIGHLIGHTS
auto highlights = tabs.appendTab(new QVBoxLayout, "Messages");
{
highlights.emplace<QLabel>("Messages can be highlighted if "
"they match a certain pattern.");
EditableModelView *view =
highlights
.emplace<EditableModelView>(
app->highlights->createModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex", "Case-\nsensitive"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->phrases.appendItem(HighlightPhrase{
"my phrase", true, false, false, false});
});
}
auto pingUsers = tabs.appendTab(new QVBoxLayout, "Users");
{
pingUsers.emplace<QLabel>(
"Messages from a certain user can be highlighted.");
EditableModelView *view =
pingUsers
.emplace<EditableModelView>(
app->highlights->createUserModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->getTableView()->horizontalHeader()->hideSection(4);
// Case-sensitivity doesn't make sense for user names so it is
// set to "false" by default & no checkbox is shown
view->setTitles({"Username", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->highlightedUsers.appendItem(
HighlightPhrase{"highlighted user", true, false, false,
false});
});
}
auto disabledUsers =
tabs.appendTab(new QVBoxLayout, "Excluded Users");
{
disabledUsers.emplace<QLabel>(
"This is a list of users (e.g. bots) whose messages should "
"<u>not</u> be highlighted.");
EditableModelView *view =
disabledUsers
.emplace<EditableModelView>(
app->highlights->createBlacklistModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->blacklistedUsers.appendItem(
HighlightBlacklistUser{"blacklisted user", false});
});
}
}
// MISC
auto customSound = layout.emplace<QHBoxLayout>().withoutMargin();
{
customSound.append(this->createCheckBox(
"Custom sound", getSettings()->customHighlightSound));
auto selectFile =
customSound.emplace<QPushButton>("Select custom sound file");
QObject::connect(selectFile.getElement(), &QPushButton::clicked,
this, [this] {
auto fileName = QFileDialog::getOpenFileName(
this, tr("Open Sound"), "",
tr("Audio Files (*.mp3 *.wav)"));
getSettings()->pathHighlightSound = fileName;
});
}
layout.append(createCheckBox(ALWAYS_PLAY,
getSettings()->highlightAlwaysPlaySound));
layout.append(createCheckBox(
"Flash taskbar only stops highlighting when chatterino is focused",
getSettings()->longAlerts));
}
// ---- misc
this->disabledUsersChangedTimer_.setSingleShot(true);
}
} // namespace chatterino
#include "HighlightingPage.hpp"
#include "Application.hpp"
#include "controllers/highlights/HighlightBlacklistModel.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "controllers/highlights/HighlightModel.hpp"
#include "controllers/highlights/UserHighlightModel.hpp"
#include "debug/Log.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "util/StandardItemHelper.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QHeaderView>
#include <QListWidget>
#include <QPushButton>
#include <QStandardItemModel>
#include <QTabWidget>
#include <QTableView>
#include <QTextEdit>
#define ENABLE_HIGHLIGHTS "Enable Highlighting"
#define HIGHLIGHT_MSG "Highlight messages containing your name"
#define PLAY_SOUND "Play sound when your name is mentioned"
#define FLASH_TASKBAR "Flash taskbar when your name is mentioned"
#define ALWAYS_PLAY "Play highlight sound even when Chatterino is focused"
namespace chatterino {
HighlightingPage::HighlightingPage()
: SettingsPage("Highlights", ":/settings/notifications.svg")
{
auto app = getApp();
LayoutCreator<HighlightingPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
{
// GENERAL
// layout.append(this->createCheckBox(ENABLE_HIGHLIGHTS,
// getSettings()->enableHighlights));
// TABS
auto tabs = layout.emplace<QTabWidget>();
{
// HIGHLIGHTS
auto highlights = tabs.appendTab(new QVBoxLayout, "Messages");
{
highlights.emplace<QLabel>("Messages can be highlighted if "
"they match a certain pattern.");
EditableModelView *view =
highlights
.emplace<EditableModelView>(
app->highlights->createModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex", "Case-\nsensitive"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->phrases.appendItem(HighlightPhrase{
"my phrase", true, false, false, false});
});
}
auto pingUsers = tabs.appendTab(new QVBoxLayout, "Users");
{
pingUsers.emplace<QLabel>(
"Messages from a certain user can be highlighted.");
EditableModelView *view =
pingUsers
.emplace<EditableModelView>(
app->highlights->createUserModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->getTableView()->horizontalHeader()->hideSection(4);
// Case-sensitivity doesn't make sense for user names so it is
// set to "false" by default & no checkbox is shown
view->setTitles({"Username", "Flash\ntaskbar", "Play\nsound",
"Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->highlightedUsers.appendItem(
HighlightPhrase{"highlighted user", true, false, false,
false});
});
}
auto disabledUsers =
tabs.appendTab(new QVBoxLayout, "Excluded Users");
{
disabledUsers.emplace<QLabel>(
"This is a list of users (e.g. bots) whose messages should "
"<u>not</u> be highlighted.");
EditableModelView *view =
disabledUsers
.emplace<EditableModelView>(
app->highlights->createBlacklistModel(nullptr))
.getElement();
view->addRegexHelpLink();
view->setTitles({"Pattern", "Enable\nregex"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
// fourtf: make class extrend BaseWidget and add this to
// dpiChanged
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->highlights->blacklistedUsers.appendItem(
HighlightBlacklistUser{"blacklisted user", false});
});
}
}
// MISC
auto customSound = layout.emplace<QHBoxLayout>().withoutMargin();
{
customSound.append(this->createCheckBox(
"Custom sound", getSettings()->customHighlightSound));
auto selectFile =
customSound.emplace<QPushButton>("Select custom sound file");
QObject::connect(selectFile.getElement(), &QPushButton::clicked,
this, [this] {
auto fileName = QFileDialog::getOpenFileName(
this, tr("Open Sound"), "",
tr("Audio Files (*.mp3 *.wav)"));
getSettings()->pathHighlightSound = fileName;
});
}
layout.append(createCheckBox(ALWAYS_PLAY,
getSettings()->highlightAlwaysPlaySound));
layout.append(createCheckBox(
"Flash taskbar only stops highlighting when chatterino is focused",
getSettings()->longAlerts));
}
// ---- misc
this->disabledUsersChangedTimer_.setSingleShot(true);
}
} // namespace chatterino
+22 -22
View File
@@ -1,22 +1,22 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
#include <QAbstractTableModel>
#include <QTimer>
class QPushButton;
class QListWidget;
namespace chatterino {
class HighlightingPage : public SettingsPage
{
public:
HighlightingPage();
private:
QTimer disabledUsersChangedTimer_;
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
#include <QAbstractTableModel>
#include <QTimer>
class QPushButton;
class QListWidget;
namespace chatterino {
class HighlightingPage : public SettingsPage
{
public:
HighlightingPage();
private:
QTimer disabledUsersChangedTimer_;
};
} // namespace chatterino
+133 -133
View File
@@ -1,133 +1,133 @@
#include "IgnoresPage.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/ignores/IgnoreController.hpp"
#include "controllers/ignores/IgnoreModel.hpp"
#include "providers/twitch/TwitchAccount.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QCheckBox>
#include <QGroupBox>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QVBoxLayout>
// clang-format off
#define INFO "/ignore <user> in chat ignores a user.\n/unignore <user> in chat unignores a user.\nYou can also click on a user to open the usercard."
// clang-format on
namespace chatterino {
static void addPhrasesTab(LayoutCreator<QVBoxLayout> box);
static void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> box,
QStringListModel &model);
IgnoresPage::IgnoresPage()
: SettingsPage("Ignores", ":/settings/ignore.svg")
{
LayoutCreator<IgnoresPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
auto tabs = layout.emplace<QTabWidget>();
addPhrasesTab(tabs.appendTab(new QVBoxLayout, "Messages"));
addUsersTab(*this, tabs.appendTab(new QVBoxLayout, "Users"),
this->userListModel_);
}
void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
{
layout.emplace<QLabel>("Messages can be ignored if "
"they match a certain pattern.");
EditableModelView *view =
layout
.emplace<EditableModelView>(getApp()->ignores->createModel(nullptr))
.getElement();
view->setTitles(
{"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
view->addRegexHelpLink();
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->ignores->phrases.appendItem(
IgnorePhrase{"my pattern", false, false,
getSettings()->ignoredPhraseReplace.getValue(), true});
});
}
void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> users,
QStringListModel &userModel)
{
auto label = users.emplace<QLabel>(INFO);
label->setWordWrap(true);
users.append(page.createCheckBox("Enable twitch ignored users",
getSettings()->enableTwitchIgnoredUsers));
auto anyways = users.emplace<QHBoxLayout>().withoutMargin();
{
anyways.emplace<QLabel>("Show messages from ignored users anyways:");
auto combo = anyways.emplace<QComboBox>().getElement();
combo->addItems(
{"Never", "If you are Moderator", "If you are Broadcaster"});
auto &setting = getSettings()->showIgnoredUsersMessages;
setting.connect(
[combo](const int value) { combo->setCurrentIndex(value); });
QObject::connect(combo,
QOverload<int>::of(&QComboBox::currentIndexChanged),
[&setting](int index) {
if (index != -1)
setting = index;
});
anyways->addStretch(1);
}
/*auto addremove = users.emplace<QHBoxLayout>().withoutMargin();
{
auto add = addremove.emplace<QPushButton>("Ignore user");
auto remove = addremove.emplace<QPushButton>("Unignore User");
addremove->addStretch(1);
}*/
users.emplace<QLabel>("List of ignored users:");
users.emplace<QListView>()->setModel(&userModel);
}
void IgnoresPage::onShow()
{
auto app = getApp();
auto user = app->accounts->twitch.getCurrent();
if (user->isAnon())
{
return;
}
QStringList users;
for (const auto &ignoredUser : user->getIgnores())
{
users << ignoredUser.name;
}
users.sort(Qt::CaseInsensitive);
this->userListModel_.setStringList(users);
}
} // namespace chatterino
#include "IgnoresPage.hpp"
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/ignores/IgnoreController.hpp"
#include "controllers/ignores/IgnoreModel.hpp"
#include "providers/twitch/TwitchAccount.hpp"
#include "singletons/Settings.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QCheckBox>
#include <QGroupBox>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QVBoxLayout>
// clang-format off
#define INFO "/ignore <user> in chat ignores a user.\n/unignore <user> in chat unignores a user.\nYou can also click on a user to open the usercard."
// clang-format on
namespace chatterino {
static void addPhrasesTab(LayoutCreator<QVBoxLayout> box);
static void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> box,
QStringListModel &model);
IgnoresPage::IgnoresPage()
: SettingsPage("Ignores", ":/settings/ignore.svg")
{
LayoutCreator<IgnoresPage> layoutCreator(this);
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
auto tabs = layout.emplace<QTabWidget>();
addPhrasesTab(tabs.appendTab(new QVBoxLayout, "Messages"));
addUsersTab(*this, tabs.appendTab(new QVBoxLayout, "Users"),
this->userListModel_);
}
void addPhrasesTab(LayoutCreator<QVBoxLayout> layout)
{
layout.emplace<QLabel>("Messages can be ignored if "
"they match a certain pattern.");
EditableModelView *view =
layout
.emplace<EditableModelView>(getApp()->ignores->createModel(nullptr))
.getElement();
view->setTitles(
{"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
view->addRegexHelpLink();
QTimer::singleShot(1, [view] {
view->getTableView()->resizeColumnsToContents();
view->getTableView()->setColumnWidth(0, 200);
});
view->addButtonPressed.connect([] {
getApp()->ignores->phrases.appendItem(
IgnorePhrase{"my pattern", false, false,
getSettings()->ignoredPhraseReplace.getValue(), true});
});
}
void addUsersTab(IgnoresPage &page, LayoutCreator<QVBoxLayout> users,
QStringListModel &userModel)
{
auto label = users.emplace<QLabel>(INFO);
label->setWordWrap(true);
users.append(page.createCheckBox("Enable twitch ignored users",
getSettings()->enableTwitchIgnoredUsers));
auto anyways = users.emplace<QHBoxLayout>().withoutMargin();
{
anyways.emplace<QLabel>("Show messages from ignored users anyways:");
auto combo = anyways.emplace<QComboBox>().getElement();
combo->addItems(
{"Never", "If you are Moderator", "If you are Broadcaster"});
auto &setting = getSettings()->showIgnoredUsersMessages;
setting.connect(
[combo](const int value) { combo->setCurrentIndex(value); });
QObject::connect(combo,
QOverload<int>::of(&QComboBox::currentIndexChanged),
[&setting](int index) {
if (index != -1)
setting = index;
});
anyways->addStretch(1);
}
/*auto addremove = users.emplace<QHBoxLayout>().withoutMargin();
{
auto add = addremove.emplace<QPushButton>("Ignore user");
auto remove = addremove.emplace<QPushButton>("Unignore User");
addremove->addStretch(1);
}*/
users.emplace<QLabel>("List of ignored users:");
users.emplace<QListView>()->setModel(&userModel);
}
void IgnoresPage::onShow()
{
auto app = getApp();
auto user = app->accounts->twitch.getCurrent();
if (user->isAnon())
{
return;
}
QStringList users;
for (const auto &ignoredUser : user->getIgnores())
{
users << ignoredUser.name;
}
users.sort(Qt::CaseInsensitive);
this->userListModel_.setStringList(users);
}
} // namespace chatterino
+22 -22
View File
@@ -1,22 +1,22 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
#include <QStringListModel>
class QVBoxLayout;
namespace chatterino {
class IgnoresPage : public SettingsPage
{
public:
IgnoresPage();
void onShow() final;
private:
QStringListModel userListModel_;
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
#include <QStringListModel>
class QVBoxLayout;
namespace chatterino {
class IgnoresPage : public SettingsPage
{
public:
IgnoresPage();
void onShow() final;
private:
QStringListModel userListModel_;
};
} // namespace chatterino
@@ -1,50 +1,50 @@
#include "KeyboardSettingsPage.hpp"
#include "util/LayoutCreator.hpp"
#include <QFormLayout>
#include <QLabel>
namespace chatterino {
KeyboardSettingsPage::KeyboardSettingsPage()
: SettingsPage("Keybindings", ":/settings/keybinds.svg")
{
auto layout =
LayoutCreator<KeyboardSettingsPage>(this).setLayoutType<QVBoxLayout>();
auto form = layout.emplace<QFormLayout>().withoutMargin();
form->addRow(new QLabel("Hold Ctrl"), new QLabel("Show resize handles"));
form->addRow(new QLabel("Hold Ctrl + Alt"),
new QLabel("Show split overlay"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + T"), new QLabel("Create new split"));
form->addRow(new QLabel("Ctrl + W"), new QLabel("Close current split"));
form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab"));
form->addRow(new QLabel("Ctrl + Shift + W"),
new QLabel("Close current tab"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + 1/2/3/..."),
new QLabel("Select tab 1/2/3/..."));
form->addRow(new QLabel("Ctrl + Tab"), new QLabel("Select next tab"));
form->addRow(new QLabel("Ctrl + Shift + Tab"),
new QLabel("Select previous tab"));
form->addRow(new QLabel("Alt + Left/Up/Right/Down"),
new QLabel("Select split left/up/right/down"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + R"), new QLabel("Change channel"));
form->addRow(new QLabel("Ctrl + F"),
new QLabel("Search in current channel"));
form->addRow(new QLabel("Ctrl + E"), new QLabel("Open Emote menu"));
form->addRow(new QLabel("Ctrl + P"), new QLabel("Open Settings menu"));
form->addRow(new QLabel("F5"),
new QLabel("Reload subscriber and channel emotes"));
}
} // namespace chatterino
#include "KeyboardSettingsPage.hpp"
#include "util/LayoutCreator.hpp"
#include <QFormLayout>
#include <QLabel>
namespace chatterino {
KeyboardSettingsPage::KeyboardSettingsPage()
: SettingsPage("Keybindings", ":/settings/keybinds.svg")
{
auto layout =
LayoutCreator<KeyboardSettingsPage>(this).setLayoutType<QVBoxLayout>();
auto form = layout.emplace<QFormLayout>().withoutMargin();
form->addRow(new QLabel("Hold Ctrl"), new QLabel("Show resize handles"));
form->addRow(new QLabel("Hold Ctrl + Alt"),
new QLabel("Show split overlay"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + T"), new QLabel("Create new split"));
form->addRow(new QLabel("Ctrl + W"), new QLabel("Close current split"));
form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab"));
form->addRow(new QLabel("Ctrl + Shift + W"),
new QLabel("Close current tab"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + 1/2/3/..."),
new QLabel("Select tab 1/2/3/..."));
form->addRow(new QLabel("Ctrl + Tab"), new QLabel("Select next tab"));
form->addRow(new QLabel("Ctrl + Shift + Tab"),
new QLabel("Select previous tab"));
form->addRow(new QLabel("Alt + Left/Up/Right/Down"),
new QLabel("Select split left/up/right/down"));
form->addItem(new QSpacerItem(16, 16));
form->addRow(new QLabel("Ctrl + R"), new QLabel("Change channel"));
form->addRow(new QLabel("Ctrl + F"),
new QLabel("Search in current channel"));
form->addRow(new QLabel("Ctrl + E"), new QLabel("Open Emote menu"));
form->addRow(new QLabel("Ctrl + P"), new QLabel("Open Settings menu"));
form->addRow(new QLabel("F5"),
new QLabel("Reload subscriber and channel emotes"));
}
} // namespace chatterino
@@ -1,13 +1,13 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class KeyboardSettingsPage : public SettingsPage
{
public:
KeyboardSettingsPage();
};
} // namespace chatterino
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class KeyboardSettingsPage : public SettingsPage
{
public:
KeyboardSettingsPage();
};
} // namespace chatterino
+216 -216
View File
@@ -1,216 +1,216 @@
#include "ModerationPage.hpp"
#include "Application.hpp"
#include "controllers/moderationactions/ModerationActionModel.hpp"
#include "controllers/moderationactions/ModerationActions.hpp"
#include "controllers/taggedusers/TaggedUsersController.hpp"
#include "controllers/taggedusers/TaggedUsersModel.hpp"
#include "singletons/Logging.hpp"
#include "singletons/Paths.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QtConcurrent/QtConcurrent>
namespace chatterino {
qint64 dirSize(QString dirPath)
{
qint64 size = 0;
QDir dir(dirPath);
// calculate total size of current directories' files
QDir::Filters fileFilters = QDir::Files | QDir::System | QDir::Hidden;
for (QString filePath : dir.entryList(fileFilters))
{
QFileInfo fi(dir, filePath);
size += fi.size();
}
// add size of child directories recursively
QDir::Filters dirFilters =
QDir::Dirs | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden;
for (QString childDirPath : dir.entryList(dirFilters))
size += dirSize(dirPath + QDir::separator() + childDirPath);
return size;
}
QString formatSize(qint64 size)
{
QStringList units = {"Bytes", "KB", "MB", "GB", "TB", "PB"};
int i;
double outputSize = size;
for (i = 0; i < units.size() - 1; i++)
{
if (outputSize < 1024)
break;
outputSize = outputSize / 1024;
}
return QString("%0 %1").arg(outputSize, 0, 'f', 2).arg(units[i]);
}
QString fetchLogDirectorySize()
{
QString logPathDirectory = getSettings()->logPath.getValue().isEmpty()
? getPaths()->messageLogDirectory
: getSettings()->logPath;
qint64 logsSize = dirSize(logPathDirectory);
QString logsSizeLabel = "Your logs currently take up ";
logsSizeLabel += formatSize(logsSize);
logsSizeLabel += " of space";
return logsSizeLabel;
}
ModerationPage::ModerationPage()
: SettingsPage("Moderation", ":/settings/moderation.svg")
{
auto app = getApp();
LayoutCreator<ModerationPage> layoutCreator(this);
auto tabs = layoutCreator.emplace<QTabWidget>();
this->tabWidget_ = tabs.getElement();
auto logs = tabs.appendTab(new QVBoxLayout, "Logs");
{
logs.append(this->createCheckBox("Enable logging",
getSettings()->enableLogging));
auto logsPathLabel = logs.emplace<QLabel>();
// Logs (copied from LoggingMananger)
getSettings()->logPath.connect([logsPathLabel](const QString &logPath,
auto) mutable {
QString pathOriginal =
logPath.isEmpty() ? getPaths()->messageLogDirectory : logPath;
QString pathShortened =
"Logs are saved at <a href=\"file:///" + pathOriginal +
"\"><span style=\"color: white;\">" +
shortenString(pathOriginal, 50) + "</span></a>";
logsPathLabel->setText(pathShortened);
logsPathLabel->setToolTip(pathOriginal);
});
logsPathLabel->setTextFormat(Qt::RichText);
logsPathLabel->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard);
logsPathLabel->setOpenExternalLinks(true);
auto buttons = logs.emplace<QHBoxLayout>().withoutMargin();
// Select and Reset
auto selectDir = buttons.emplace<QPushButton>("Select log directory ");
auto resetDir = buttons.emplace<QPushButton>("Reset");
getSettings()->logPath.connect(
[element = resetDir.getElement()](const QString &path) {
element->setEnabled(!path.isEmpty());
});
buttons->addStretch();
logs->addStretch(1);
// Show how big (size-wise) the logs are
auto logsPathSizeLabel = logs.emplace<QLabel>();
logsPathSizeLabel->setText(
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
// Select event
QObject::connect(
selectDir.getElement(), &QPushButton::clicked, this,
[this, logsPathSizeLabel]() mutable {
auto dirName = QFileDialog::getExistingDirectory(this);
getSettings()->logPath = dirName;
// Refresh: Show how big (size-wise) the logs are
logsPathSizeLabel->setText(
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
});
buttons->addSpacing(16);
// Reset custom logpath
QObject::connect(resetDir.getElement(), &QPushButton::clicked, this,
[logsPathSizeLabel]() mutable {
getSettings()->logPath = "";
// Refresh: Show how big (size-wise) the logs are
logsPathSizeLabel->setText(QtConcurrent::run(
[] { return fetchLogDirectorySize(); }));
});
} // logs end
auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation buttons");
{
// clang-format off
auto label = modMode.emplace<QLabel>(
"Moderation mode is enabled by clicking <img width='18' height='18' src=':/buttons/modModeDisabled.png'> in a channel that you moderate.<br><br>"
"Moderation buttons can be bound to chat commands such as \"/ban {user}\", \"/timeout {user} 1000\", \"/w someusername !report {user} was bad in channel {channel}\" or any other custom text commands.<br>"
"For deleting messages use /delete {msg-id}.");
label->setWordWrap(true);
label->setStyleSheet("color: #bbb");
// clang-format on
// auto form = modMode.emplace<QFormLayout>();
// {
// form->addRow("Action on timed out messages
// (unimplemented):",
// this->createComboBox({"Disable", "Hide"},
// getSettings()->timeoutAction));
// }
EditableModelView *view =
modMode
.emplace<EditableModelView>(
app->moderationActions->createModel(nullptr))
.getElement();
view->setTitles({"Actions"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
view->addButtonPressed.connect([] {
getApp()->moderationActions->items.appendItem(
ModerationAction("/timeout {user} 300"));
});
/*auto taggedUsers = tabs.appendTab(new QVBoxLayout, "Tagged users");
{
EditableModelView *view = *taggedUsers.emplace<EditableModelView>(
app->taggedUsers->createModel(nullptr));
view->setTitles({"Name"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->taggedUsers->users.appendItem(
TaggedUser(ProviderId::Twitch, "example", "xD"));
});
}*/
}
// ---- misc
this->itemsChangedTimer_.setSingleShot(true);
}
void ModerationPage::selectModerationActions()
{
this->tabWidget_->setCurrentIndex(1);
}
} // namespace chatterino
#include "ModerationPage.hpp"
#include "Application.hpp"
#include "controllers/moderationactions/ModerationActionModel.hpp"
#include "controllers/moderationactions/ModerationActions.hpp"
#include "controllers/taggedusers/TaggedUsersController.hpp"
#include "controllers/taggedusers/TaggedUsersModel.hpp"
#include "singletons/Logging.hpp"
#include "singletons/Paths.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include "widgets/helper/EditableModelView.hpp"
#include <QFileDialog>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QTableView>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QtConcurrent/QtConcurrent>
namespace chatterino {
qint64 dirSize(QString dirPath)
{
qint64 size = 0;
QDir dir(dirPath);
// calculate total size of current directories' files
QDir::Filters fileFilters = QDir::Files | QDir::System | QDir::Hidden;
for (QString filePath : dir.entryList(fileFilters))
{
QFileInfo fi(dir, filePath);
size += fi.size();
}
// add size of child directories recursively
QDir::Filters dirFilters =
QDir::Dirs | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden;
for (QString childDirPath : dir.entryList(dirFilters))
size += dirSize(dirPath + QDir::separator() + childDirPath);
return size;
}
QString formatSize(qint64 size)
{
QStringList units = {"Bytes", "KB", "MB", "GB", "TB", "PB"};
int i;
double outputSize = size;
for (i = 0; i < units.size() - 1; i++)
{
if (outputSize < 1024)
break;
outputSize = outputSize / 1024;
}
return QString("%0 %1").arg(outputSize, 0, 'f', 2).arg(units[i]);
}
QString fetchLogDirectorySize()
{
QString logPathDirectory = getSettings()->logPath.getValue().isEmpty()
? getPaths()->messageLogDirectory
: getSettings()->logPath;
qint64 logsSize = dirSize(logPathDirectory);
QString logsSizeLabel = "Your logs currently take up ";
logsSizeLabel += formatSize(logsSize);
logsSizeLabel += " of space";
return logsSizeLabel;
}
ModerationPage::ModerationPage()
: SettingsPage("Moderation", ":/settings/moderation.svg")
{
auto app = getApp();
LayoutCreator<ModerationPage> layoutCreator(this);
auto tabs = layoutCreator.emplace<QTabWidget>();
this->tabWidget_ = tabs.getElement();
auto logs = tabs.appendTab(new QVBoxLayout, "Logs");
{
logs.append(this->createCheckBox("Enable logging",
getSettings()->enableLogging));
auto logsPathLabel = logs.emplace<QLabel>();
// Logs (copied from LoggingMananger)
getSettings()->logPath.connect([logsPathLabel](const QString &logPath,
auto) mutable {
QString pathOriginal =
logPath.isEmpty() ? getPaths()->messageLogDirectory : logPath;
QString pathShortened =
"Logs are saved at <a href=\"file:///" + pathOriginal +
"\"><span style=\"color: white;\">" +
shortenString(pathOriginal, 50) + "</span></a>";
logsPathLabel->setText(pathShortened);
logsPathLabel->setToolTip(pathOriginal);
});
logsPathLabel->setTextFormat(Qt::RichText);
logsPathLabel->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard);
logsPathLabel->setOpenExternalLinks(true);
auto buttons = logs.emplace<QHBoxLayout>().withoutMargin();
// Select and Reset
auto selectDir = buttons.emplace<QPushButton>("Select log directory ");
auto resetDir = buttons.emplace<QPushButton>("Reset");
getSettings()->logPath.connect(
[element = resetDir.getElement()](const QString &path) {
element->setEnabled(!path.isEmpty());
});
buttons->addStretch();
logs->addStretch(1);
// Show how big (size-wise) the logs are
auto logsPathSizeLabel = logs.emplace<QLabel>();
logsPathSizeLabel->setText(
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
// Select event
QObject::connect(
selectDir.getElement(), &QPushButton::clicked, this,
[this, logsPathSizeLabel]() mutable {
auto dirName = QFileDialog::getExistingDirectory(this);
getSettings()->logPath = dirName;
// Refresh: Show how big (size-wise) the logs are
logsPathSizeLabel->setText(
QtConcurrent::run([] { return fetchLogDirectorySize(); }));
});
buttons->addSpacing(16);
// Reset custom logpath
QObject::connect(resetDir.getElement(), &QPushButton::clicked, this,
[logsPathSizeLabel]() mutable {
getSettings()->logPath = "";
// Refresh: Show how big (size-wise) the logs are
logsPathSizeLabel->setText(QtConcurrent::run(
[] { return fetchLogDirectorySize(); }));
});
} // logs end
auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation buttons");
{
// clang-format off
auto label = modMode.emplace<QLabel>(
"Moderation mode is enabled by clicking <img width='18' height='18' src=':/buttons/modModeDisabled.png'> in a channel that you moderate.<br><br>"
"Moderation buttons can be bound to chat commands such as \"/ban {user}\", \"/timeout {user} 1000\", \"/w someusername !report {user} was bad in channel {channel}\" or any other custom text commands.<br>"
"For deleting messages use /delete {msg-id}.");
label->setWordWrap(true);
label->setStyleSheet("color: #bbb");
// clang-format on
// auto form = modMode.emplace<QFormLayout>();
// {
// form->addRow("Action on timed out messages
// (unimplemented):",
// this->createComboBox({"Disable", "Hide"},
// getSettings()->timeoutAction));
// }
EditableModelView *view =
modMode
.emplace<EditableModelView>(
app->moderationActions->createModel(nullptr))
.getElement();
view->setTitles({"Actions"});
view->getTableView()->horizontalHeader()->setSectionResizeMode(
QHeaderView::Fixed);
view->getTableView()->horizontalHeader()->setSectionResizeMode(
0, QHeaderView::Stretch);
view->addButtonPressed.connect([] {
getApp()->moderationActions->items.appendItem(
ModerationAction("/timeout {user} 300"));
});
/*auto taggedUsers = tabs.appendTab(new QVBoxLayout, "Tagged users");
{
EditableModelView *view = *taggedUsers.emplace<EditableModelView>(
app->taggedUsers->createModel(nullptr));
view->setTitles({"Name"});
view->getTableView()->horizontalHeader()->setStretchLastSection(true);
view->addButtonPressed.connect([] {
getApp()->taggedUsers->users.appendItem(
TaggedUser(ProviderId::Twitch, "example", "xD"));
});
}*/
}
// ---- misc
this->itemsChangedTimer_.setSingleShot(true);
}
void ModerationPage::selectModerationActions()
{
this->tabWidget_->setCurrentIndex(1);
}
} // namespace chatterino
+24 -24
View File
@@ -1,24 +1,24 @@
#pragma once
#include <QTimer>
#include "widgets/settingspages/SettingsPage.hpp"
class QTabWidget;
class QPushButton;
namespace chatterino {
class ModerationPage : public SettingsPage
{
public:
ModerationPage();
void selectModerationActions();
private:
QTimer itemsChangedTimer_;
QTabWidget *tabWidget_{};
};
} // namespace chatterino
#pragma once
#include <QTimer>
#include "widgets/settingspages/SettingsPage.hpp"
class QTabWidget;
class QPushButton;
namespace chatterino {
class ModerationPage : public SettingsPage
{
public:
ModerationPage();
void selectModerationActions();
private:
QTimer itemsChangedTimer_;
QTabWidget *tabWidget_{};
};
} // namespace chatterino
+176 -176
View File
@@ -1,176 +1,176 @@
#include "SettingsPage.hpp"
#include "Application.hpp"
#include "singletons/WindowManager.hpp"
#include <QDebug>
#include <QPainter>
#include <util/FunctionEventFilter.hpp>
namespace chatterino {
bool filterItemsRec(QObject *object, const QString &query)
{
bool any{};
for (auto &&child : object->children())
{
auto setOpacity = [&](auto *widget, bool condition) {
any |= condition;
widget->greyedOut = !condition;
widget->update();
};
if (auto x = dynamic_cast<SCheckBox *>(child); x)
{
setOpacity(x, x->text().contains(query, Qt::CaseInsensitive));
}
else if (auto x = dynamic_cast<SLabel *>(child); x)
{
setOpacity(x, x->text().contains(query, Qt::CaseInsensitive));
}
else if (auto x = dynamic_cast<SComboBox *>(child); x)
{
setOpacity(x, [=]() {
for (int i = 0; i < x->count(); i++)
{
if (x->itemText(i).contains(query, Qt::CaseInsensitive))
return true;
}
return false;
}());
}
else if (auto x = dynamic_cast<QTabWidget *>(child); x)
{
for (int i = 0; i < x->count(); i++)
{
bool tabAny{};
if (x->tabText(i).contains(query, Qt::CaseInsensitive))
{
tabAny = true;
}
auto widget = x->widget(i);
tabAny |= filterItemsRec(widget, query);
any |= tabAny;
}
}
else
{
any |= filterItemsRec(child, query);
}
}
return any;
}
SettingsPage::SettingsPage(const QString &name, const QString &iconResource)
: name_(name)
, iconResource_(iconResource)
{
}
bool SettingsPage::filterElements(const QString &query)
{
return filterItemsRec(this, query) || query.isEmpty() ||
this->name_.contains(query, Qt::CaseInsensitive);
}
const QString &SettingsPage::getName()
{
return this->name_;
}
const QString &SettingsPage::getIconResource()
{
return this->iconResource_;
}
SettingsDialogTab *SettingsPage::tab() const
{
return this->tab_;
}
void SettingsPage::setTab(SettingsDialogTab *tab)
{
this->tab_ = tab;
}
void SettingsPage::cancel()
{
this->onCancel_.invoke();
}
QCheckBox *SettingsPage::createCheckBox(
const QString &text, pajlada::Settings::Setting<bool> &setting)
{
QCheckBox *checkbox = new SCheckBox(text);
// update when setting changes
setting.connect(
[checkbox](const bool &value, auto) {
checkbox->setChecked(value); //
},
this->managedConnections_);
// update setting on toggle
QObject::connect(checkbox, &QCheckBox::toggled, this,
[&setting](bool state) {
setting = state;
getApp()->windows->forceLayoutChannelViews();
});
return checkbox;
}
QComboBox *SettingsPage::createComboBox(
const QStringList &items, pajlada::Settings::Setting<QString> &setting)
{
QComboBox *combo = new SComboBox();
// update setting on toogle
combo->addItems(items);
// update when setting changes
setting.connect(
[combo](const QString &value, auto) { combo->setCurrentText(value); },
this->managedConnections_);
QObject::connect(
combo, &QComboBox::currentTextChanged,
[&setting](const QString &newValue) { setting = newValue; });
return combo;
}
QLineEdit *SettingsPage::createLineEdit(
pajlada::Settings::Setting<QString> &setting)
{
QLineEdit *edit = new QLineEdit();
edit->setText(setting);
// update when setting changes
QObject::connect(
edit, &QLineEdit::textChanged,
[&setting](const QString &newValue) { setting = newValue; });
return edit;
}
QSpinBox *SettingsPage::createSpinBox(pajlada::Settings::Setting<int> &setting,
int min, int max)
{
QSpinBox *w = new QSpinBox;
w->setMinimum(min);
w->setMaximum(max);
setting.connect([w](const int &value, auto) { w->setValue(value); });
QObject::connect(w, QOverload<int>::of(&QSpinBox::valueChanged),
[&setting](int value) { setting.setValue(value); });
return w;
}
} // namespace chatterino
#include "SettingsPage.hpp"
#include "Application.hpp"
#include "singletons/WindowManager.hpp"
#include <QDebug>
#include <QPainter>
#include <util/FunctionEventFilter.hpp>
namespace chatterino {
bool filterItemsRec(QObject *object, const QString &query)
{
bool any{};
for (auto &&child : object->children())
{
auto setOpacity = [&](auto *widget, bool condition) {
any |= condition;
widget->greyedOut = !condition;
widget->update();
};
if (auto x = dynamic_cast<SCheckBox *>(child); x)
{
setOpacity(x, x->text().contains(query, Qt::CaseInsensitive));
}
else if (auto x = dynamic_cast<SLabel *>(child); x)
{
setOpacity(x, x->text().contains(query, Qt::CaseInsensitive));
}
else if (auto x = dynamic_cast<SComboBox *>(child); x)
{
setOpacity(x, [=]() {
for (int i = 0; i < x->count(); i++)
{
if (x->itemText(i).contains(query, Qt::CaseInsensitive))
return true;
}
return false;
}());
}
else if (auto x = dynamic_cast<QTabWidget *>(child); x)
{
for (int i = 0; i < x->count(); i++)
{
bool tabAny{};
if (x->tabText(i).contains(query, Qt::CaseInsensitive))
{
tabAny = true;
}
auto widget = x->widget(i);
tabAny |= filterItemsRec(widget, query);
any |= tabAny;
}
}
else
{
any |= filterItemsRec(child, query);
}
}
return any;
}
SettingsPage::SettingsPage(const QString &name, const QString &iconResource)
: name_(name)
, iconResource_(iconResource)
{
}
bool SettingsPage::filterElements(const QString &query)
{
return filterItemsRec(this, query) || query.isEmpty() ||
this->name_.contains(query, Qt::CaseInsensitive);
}
const QString &SettingsPage::getName()
{
return this->name_;
}
const QString &SettingsPage::getIconResource()
{
return this->iconResource_;
}
SettingsDialogTab *SettingsPage::tab() const
{
return this->tab_;
}
void SettingsPage::setTab(SettingsDialogTab *tab)
{
this->tab_ = tab;
}
void SettingsPage::cancel()
{
this->onCancel_.invoke();
}
QCheckBox *SettingsPage::createCheckBox(
const QString &text, pajlada::Settings::Setting<bool> &setting)
{
QCheckBox *checkbox = new SCheckBox(text);
// update when setting changes
setting.connect(
[checkbox](const bool &value, auto) {
checkbox->setChecked(value); //
},
this->managedConnections_);
// update setting on toggle
QObject::connect(checkbox, &QCheckBox::toggled, this,
[&setting](bool state) {
setting = state;
getApp()->windows->forceLayoutChannelViews();
});
return checkbox;
}
QComboBox *SettingsPage::createComboBox(
const QStringList &items, pajlada::Settings::Setting<QString> &setting)
{
QComboBox *combo = new SComboBox();
// update setting on toogle
combo->addItems(items);
// update when setting changes
setting.connect(
[combo](const QString &value, auto) { combo->setCurrentText(value); },
this->managedConnections_);
QObject::connect(
combo, &QComboBox::currentTextChanged,
[&setting](const QString &newValue) { setting = newValue; });
return combo;
}
QLineEdit *SettingsPage::createLineEdit(
pajlada::Settings::Setting<QString> &setting)
{
QLineEdit *edit = new QLineEdit();
edit->setText(setting);
// update when setting changes
QObject::connect(
edit, &QLineEdit::textChanged,
[&setting](const QString &newValue) { setting = newValue; });
return edit;
}
QSpinBox *SettingsPage::createSpinBox(pajlada::Settings::Setting<int> &setting,
int min, int max)
{
QSpinBox *w = new QSpinBox;
w->setMinimum(min);
w->setMaximum(max);
setting.connect([w](const int &value, auto) { w->setValue(value); });
QObject::connect(w, QOverload<int>::of(&QSpinBox::valueChanged),
[&setting](int value) { setting.setValue(value); });
return w;
}
} // namespace chatterino
+84 -84
View File
@@ -1,84 +1,84 @@
#pragma once
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include <pajlada/signals/signal.hpp>
#include "singletons/Settings.hpp"
#define SETTINGS_PAGE_WIDGET_BOILERPLATE(type, parent) \
class type : public parent \
{ \
using parent::parent; \
\
public: \
bool greyedOut{}; \
\
protected: \
void paintEvent(QPaintEvent *e) override \
{ \
parent::paintEvent(e); \
\
if (this->greyedOut) \
{ \
QPainter painter(this); \
QColor color = QColor("#222222"); \
color.setAlphaF(0.7); \
painter.fillRect(this->rect(), color); \
} \
} \
};
namespace chatterino {
// S* widgets are the same as their Q* counterparts,
// but they can be greyed out and will be if you search.
SETTINGS_PAGE_WIDGET_BOILERPLATE(SCheckBox, QCheckBox)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SLabel, QLabel)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SComboBox, QComboBox)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SPushButton, QPushButton)
class SettingsDialogTab;
class SettingsPage : public QFrame
{
Q_OBJECT
public:
SettingsPage(const QString &name, const QString &iconResource);
const QString &getName();
const QString &getIconResource();
virtual bool filterElements(const QString &query);
SettingsDialogTab *tab() const;
void setTab(SettingsDialogTab *tab);
void cancel();
QCheckBox *createCheckBox(const QString &text,
pajlada::Settings::Setting<bool> &setting);
QComboBox *createComboBox(const QStringList &items,
pajlada::Settings::Setting<QString> &setting);
QLineEdit *createLineEdit(pajlada::Settings::Setting<QString> &setting);
QSpinBox *createSpinBox(pajlada::Settings::Setting<int> &setting,
int min = 0, int max = 2500);
virtual void onShow()
{
}
protected:
QString name_;
QString iconResource_;
SettingsDialogTab *tab_;
pajlada::Signals::NoArgSignal onCancel_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
} // namespace chatterino
#pragma once
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include <pajlada/signals/signal.hpp>
#include "singletons/Settings.hpp"
#define SETTINGS_PAGE_WIDGET_BOILERPLATE(type, parent) \
class type : public parent \
{ \
using parent::parent; \
\
public: \
bool greyedOut{}; \
\
protected: \
void paintEvent(QPaintEvent *e) override \
{ \
parent::paintEvent(e); \
\
if (this->greyedOut) \
{ \
QPainter painter(this); \
QColor color = QColor("#222222"); \
color.setAlphaF(0.7); \
painter.fillRect(this->rect(), color); \
} \
} \
};
namespace chatterino {
// S* widgets are the same as their Q* counterparts,
// but they can be greyed out and will be if you search.
SETTINGS_PAGE_WIDGET_BOILERPLATE(SCheckBox, QCheckBox)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SLabel, QLabel)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SComboBox, QComboBox)
SETTINGS_PAGE_WIDGET_BOILERPLATE(SPushButton, QPushButton)
class SettingsDialogTab;
class SettingsPage : public QFrame
{
Q_OBJECT
public:
SettingsPage(const QString &name, const QString &iconResource);
const QString &getName();
const QString &getIconResource();
virtual bool filterElements(const QString &query);
SettingsDialogTab *tab() const;
void setTab(SettingsDialogTab *tab);
void cancel();
QCheckBox *createCheckBox(const QString &text,
pajlada::Settings::Setting<bool> &setting);
QComboBox *createComboBox(const QStringList &items,
pajlada::Settings::Setting<QString> &setting);
QLineEdit *createLineEdit(pajlada::Settings::Setting<QString> &setting);
QSpinBox *createSpinBox(pajlada::Settings::Setting<int> &setting,
int min = 0, int max = 2500);
virtual void onShow()
{
}
protected:
QString name_;
QString iconResource_;
SettingsDialogTab *tab_;
pajlada::Signals::NoArgSignal onCancel_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
};
} // namespace chatterino