refactored the settings dialog

This commit is contained in:
fourtf 2018-01-12 23:09:05 +01:00
parent 4cbc3b8b61
commit e0bb061c81
39 changed files with 953 additions and 894 deletions

View file

@ -116,7 +116,17 @@ SOURCES += \
src/messages/image.cpp \
src/messages/layouts/messagelayout.cpp \
src/messages/layouts/messagelayoutelement.cpp \
src/messages/layouts/messagelayoutcontainer.cpp
src/messages/layouts/messagelayoutcontainer.cpp \
src/widgets/settingspages/appearancepage.cpp \
src/widgets/settingspages/settingspage.cpp \
src/widgets/settingspages/behaviourpage.cpp \
src/widgets/settingspages/commandpage.cpp \
src/widgets/settingspages/emotespage.cpp \
src/widgets/settingspages/highlightingpage.cpp \
src/widgets/settingspages/accountspage.cpp \
src/widgets/settingspages/aboutpage.cpp \
src/widgets/settingspages/moderationpage.cpp \
src/widgets/settingspages/logspage.cpp
HEADERS += \
src/precompiled_headers.hpp \
@ -203,7 +213,18 @@ HEADERS += \
src/messages/layouts/messagelayout.hpp \
src/messages/layouts/messagelayoutelement.hpp \
src/messages/layouts/messagelayoutcontainer.hpp \
src/util/property.hpp
src/util/property.hpp \
src/widgets/settingspages/appearancepage.hpp \
src/widgets/settingspages/settingspage.hpp \
src/util/layoutcreator.hpp \
src/widgets/settingspages/behaviourpage.hpp \
src/widgets/settingspages/commandpage.hpp \
src/widgets/settingspages/emotespage.hpp \
src/widgets/settingspages/highlightingpage.hpp \
src/widgets/settingspages/accountspage.hpp \
src/widgets/settingspages/aboutpage.hpp \
src/widgets/settingspages/moderationpage.hpp \
src/widgets/settingspages/logspage.hpp
PRECOMPILED_HEADER =

View file

@ -1 +0,0 @@
../.clang-format

View file

@ -1 +0,0 @@
../.clang-format

View file

@ -1 +0,0 @@
../.clang-format

View file

@ -213,23 +213,25 @@ TimestampElement::~TimestampElement()
void TimestampElement::addToContainer(MessageLayoutContainer &container,
MessageElement::Flags _flags)
{
if (singletons::SettingManager::getInstance().timestampFormat != this->format) {
this->format = singletons::SettingManager::getInstance().timestampFormat;
delete this->element;
this->element = TimestampElement::formatTime(this->time);
}
this->element->addToContainer(container, _flags);
}
void TimestampElement::update(UpdateFlags _flags)
{
if (_flags == UpdateFlags::Update_All) {
this->element = TimestampElement::formatTime(this->time);
} else {
this->element->update(_flags);
}
}
TextElement *TimestampElement::formatTime(const QTime &time)
{
QString text = time.toString(singletons::SettingManager::getInstance().timestampFormat);
QString format = time.toString(singletons::SettingManager::getInstance().timestampFormat);
return new TextElement(text, Flags::Timestamp, MessageColor::System, FontStyle::Medium);
return new TextElement(format, Flags::Timestamp, MessageColor::System, FontStyle::Medium);
}
// TWITCH MODERATION

View file

@ -189,6 +189,7 @@ class TimestampElement : public MessageElement
{
QTime time;
TextElement *element;
QString format;
public:
TimestampElement();

View file

@ -49,8 +49,8 @@ public:
BoolSetting allowDuplicateMessages = {"/behaviour/allowDuplicateMessages", true};
BoolSetting mentionUsersWithAt = {"/behaviour/mentionUsersWithAt", false};
FloatSetting mouseScrollMultiplier = {"/behaviour/mouseScrollMultiplier", 1.0};
StringSetting streamlinkPath = {"/behaviour/streamlink/path", ""};
StringSetting preferredQuality = {"/behaviour/streamlink/quality", "Choose"};
QStringSetting streamlinkPath = {"/behaviour/streamlink/path", ""};
QStringSetting preferredQuality = {"/behaviour/streamlink/quality", "Choose"};
BoolSetting pauseChatHover = {"/behaviour/pauseChatHover", false};
/// Commands

View file

@ -11,7 +11,7 @@ namespace singletons {
namespace detail {
double getMultiplierByTheme(const std::string &themeName)
double getMultiplierByTheme(const QString &themeName)
{
if (themeName == "Light") {
return 0.8;
@ -46,7 +46,7 @@ ThemeManager::ThemeManager()
void ThemeManager::update()
{
this->actuallyUpdate(this->themeHue, detail::getMultiplierByTheme(this->themeName));
this->actuallyUpdate(this->themeHue, detail::getMultiplierByTheme(this->themeName.getValue()));
}
// hue: theme color (0 - 1)
@ -157,9 +157,9 @@ void ThemeManager::normalizeColor(QColor &color)
}
if (color.lightnessF() > 0.4f && color.hueF() > 0.1 && color.hueF() < 0.33333) {
color.setHslF(color.hueF(), color.saturationF(),
color.lightnessF() -
sin((color.hueF() - 0.1) / (0.3333 - 0.1) * 3.14159) *
color.setHslF(
color.hueF(), color.saturationF(),
color.lightnessF() - sin((color.hueF() - 0.1) / (0.3333 - 0.1) * 3.14159) *
color.saturationF() * 0.2);
}
} else {
@ -168,13 +168,13 @@ void ThemeManager::normalizeColor(QColor &color)
}
if (color.lightnessF() < 0.6f && color.hueF() > 0.54444 && color.hueF() < 0.83333) {
color.setHslF(color.hueF(), color.saturationF(),
color.lightnessF() +
sin((color.hueF() - 0.54444) / (0.8333 - 0.54444) * 3.14159) *
color.setHslF(
color.hueF(), color.saturationF(),
color.lightnessF() + sin((color.hueF() - 0.54444) / (0.8333 - 0.54444) * 3.14159) *
color.saturationF() * 0.4);
}
}
}
} // namespace singletons
} // namespace chatterino
}

View file

@ -4,6 +4,7 @@
#include <QColor>
#include <boost/signals2.hpp>
#include <pajlada/settings/setting.hpp>
#include <util/serialize-custom.hpp>
namespace chatterino {
namespace singletons {
@ -102,10 +103,10 @@ public:
boost::signals2::signal<void()> updated;
private:
pajlada::Settings::Setting<std::string> themeName;
pajlada::Settings::Setting<QString> themeName;
pajlada::Settings::Setting<double> themeHue;
private:
void actuallyUpdate(double hue, double multiplier);
QColor blendColors(const QColor &color1, const QColor &color2, qreal ratio);
@ -122,5 +123,5 @@ private:
friend class WindowManager;
};
} // namespace singletons
} // namespace chatterino
}

View file

@ -1 +0,0 @@
../.clang-format

View file

@ -1 +0,0 @@
../.clang-format

103
src/util/layoutcreator.hpp Normal file
View file

@ -0,0 +1,103 @@
#pragma once
#include <QHBoxLayout>
#include <QWidget>
#include <cassert>
#include <type_traits>
namespace chatterino {
namespace util {
template <class T>
class LayoutCreator
{
public:
LayoutCreator(T *_item)
: item(_item)
{
}
T *operator->()
{
return this->item;
}
T *getElement()
{
return this->item;
}
template <typename T2>
LayoutCreator<T2> append(T2 *_item)
{
this->_addItem(this->getOrCreateLayout(), _item);
return LayoutCreator<T2>(_item);
}
template <typename T2, typename... Args>
LayoutCreator<T2> emplace(Args &&... args)
{
T2 *t = new T2(std::forward<Args>(args)...);
this->_addItem(this->getOrCreateLayout(), t);
return LayoutCreator<T2>(t);
}
LayoutCreator<T> assign(T **ptr)
{
*ptr = this->item;
return *this;
}
template <typename Q = T,
typename std::enable_if<std::is_base_of<QLayout, Q>::value, int>::type = 0>
LayoutCreator<T> withoutMargin()
{
this->item->setContentsMargins(0, 0, 0, 0);
return *this;
}
private:
T *item;
template <typename T2,
typename std::enable_if<std::is_base_of<QWidget, T2>::value, int>::type = 0>
void _addItem(QLayout *layout, T2 *item)
{
layout->addWidget(item);
}
template <typename T2,
typename std::enable_if<std::is_base_of<QLayout, T2>::value, int>::type = 0>
void _addItem(QLayout *layout, T2 *item)
{
QWidget *widget = new QWidget();
widget->setLayout(item);
layout->addWidget(widget);
}
template <typename Q = T,
typename std::enable_if<std::is_base_of<QLayout, Q>::value, int>::type = 0>
QLayout *getOrCreateLayout()
{
return this->item;
}
template <typename Q = T,
typename std::enable_if<std::is_base_of<QWidget, Q>::value, int>::type = 0>
QLayout *getOrCreateLayout()
{
if (!this->item->layout()) {
this->item->setLayout(new QHBoxLayout());
}
return this->item->layout();
}
};
} // namespace util
} // namespace chatterino

View file

@ -1 +0,0 @@
../.clang-format

View file

@ -1 +0,0 @@
../.clang-format

View file

@ -7,11 +7,12 @@
namespace chatterino {
namespace widgets {
SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog, QString _labelText,
SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog, settingspages::SettingsPage *_page,
QString imageFileName)
: dialog(_dialog)
, page(_page)
{
this->ui.labelText = _labelText;
this->ui.labelText = page->getName();
this->ui.icon.addFile(imageFileName);
this->setCursor(QCursor(Qt::PointingHandCursor));
@ -31,14 +32,9 @@ void SettingsDialogTab::setSelected(bool _selected)
emit selectedChanged(selected);
}
QWidget *SettingsDialogTab::getWidget()
settingspages::SettingsPage *SettingsDialogTab::getSettingsPage()
{
return this->ui.widget;
}
void SettingsDialogTab::setWidget(QWidget *widget)
{
this->ui.widget = widget;
return this->page;
}
void SettingsDialogTab::paintEvent(QPaintEvent *)
@ -53,7 +49,6 @@ void SettingsDialogTab::paintEvent(QPaintEvent *)
int a = (this->height() - 20) / 2;
QPixmap pixmap = this->ui.icon.pixmap(QSize(20, 20));
painter.drawPixmap(0, a, pixmap);
a = a + a + 20;

View file

@ -1,11 +1,14 @@
#pragma once
#include <QIcon>
#include <QPaintEvent>
#include <QWidget>
#include <QIcon>
namespace chatterino {
namespace widgets {
namespace settingspages {
class SettingsPage;
}
class SettingsDialog;
@ -14,11 +17,11 @@ class SettingsDialogTab : public QWidget
Q_OBJECT
public:
SettingsDialogTab(SettingsDialog *dialog, QString _label, QString imageFileName);
SettingsDialogTab(SettingsDialog *dialog, settingspages::SettingsPage *page,
QString imageFileName);
void setSelected(bool selected);
QWidget *getWidget();
void setWidget(QWidget *widget);
settingspages::SettingsPage *getSettingsPage();
signals:
void selectedChanged(bool);
@ -28,13 +31,13 @@ private:
void mousePressEvent(QMouseEvent *event);
struct {
QWidget *widget;
QString labelText;
QIcon icon;
} ui;
// Parent settings dialog
SettingsDialog *dialog;
settingspages::SettingsPage *page;
bool selected = false;
};

View file

@ -6,8 +6,18 @@
#include "singletons/windowmanager.hpp"
#include "twitch/twitchmessagebuilder.hpp"
#include "twitch/twitchuser.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/helper/settingsdialogtab.hpp"
#include "widgets/logindialog.hpp"
#include "widgets/settingspages/aboutpage.hpp"
#include "widgets/settingspages/accountspage.hpp"
#include "widgets/settingspages/appearancepage.hpp"
#include "widgets/settingspages/behaviourpage.hpp"
#include "widgets/settingspages/commandpage.hpp"
#include "widgets/settingspages/emotespage.hpp"
#include "widgets/settingspages/highlightingpage.hpp"
#include "widgets/settingspages/logspage.hpp"
#include "widgets/settingspages/moderationpage.hpp"
#include <QComboBox>
#include <QDebug>
@ -33,50 +43,53 @@ SettingsDialog *SettingsDialog::handle = nullptr;
SettingsDialog::SettingsDialog()
: BaseWidget()
, usernameDisplayMode(
"/appearance/messages/usernameDisplayMode",
twitch::TwitchMessageBuilder::UsernameDisplayMode::UsernameAndLocalizedName)
{
this->initAsWindow();
QPalette palette;
palette.setColor(QPalette::Background, QColor("#444"));
this->setPalette(palette);
this->ui.pageStack.setObjectName("pages");
this->setLayout(&this->ui.vbox);
this->ui.vbox.addLayout(&this->ui.hbox);
this->ui.vbox.addWidget(&this->ui.buttonBox);
auto tabWidget = &ui.tabWidget;
tabWidget->setObjectName("tabWidget");
tabWidget->setLayout(&this->ui.tabs);
this->ui.hbox.addWidget(tabWidget);
this->ui.hbox.addLayout(&this->ui.pageStack);
this->ui.buttonBox.addButton(&this->ui.okButton, QDialogButtonBox::ButtonRole::AcceptRole);
this->ui.buttonBox.addButton(&this->ui.cancelButton, QDialogButtonBox::ButtonRole::RejectRole);
QObject::connect(&this->ui.okButton, &QPushButton::clicked, this,
&SettingsDialog::okButtonClicked);
QObject::connect(&this->ui.cancelButton, &QPushButton::clicked, this,
&SettingsDialog::cancelButtonClicked);
this->ui.okButton.setText("OK");
this->ui.cancelButton.setText("Cancel");
this->resize(600, 500);
this->initUi();
this->addTabs();
this->dpiMultiplierChanged(this->getDpiMultiplier(), this->getDpiMultiplier());
}
void SettingsDialog::initUi()
{
util::LayoutCreator<SettingsDialog> layoutCreator(this);
// tab pages
layoutCreator.emplace<QWidget>()
.assign(&this->ui.tabContainerContainer)
.emplace<QVBoxLayout>()
.withoutMargin()
.assign(&this->ui.tabContainer);
// right side layout
auto right = layoutCreator.emplace<QVBoxLayout>();
{
right.emplace<QStackedLayout>().assign(&this->ui.pageStack).emplace<QPushButton>("NaM");
auto buttons = right.emplace<QDialogButtonBox>(Qt::Horizontal);
{
this->ui.okButton = buttons->addButton("Ok", QDialogButtonBox::YesRole);
this->ui.cancelButton = buttons->addButton("Cancel", QDialogButtonBox::NoRole);
}
}
// ---- misc
QPalette palette;
palette.setColor(QPalette::Background, QColor("#444"));
this->setPalette(palette);
this->ui.tabContainerContainer->setObjectName("tabWidget");
this->ui.pageStack->setObjectName("pages");
QObject::connect(this->ui.okButton, &QPushButton::clicked, this,
&SettingsDialog::okButtonClicked);
QObject::connect(this->ui.cancelButton, &QPushButton::clicked, this,
&SettingsDialog::cancelButtonClicked);
}
SettingsDialog *SettingsDialog::getHandle()
{
return SettingsDialog::handle;
@ -84,682 +97,34 @@ SettingsDialog *SettingsDialog::getHandle()
void SettingsDialog::addTabs()
{
this->addTab(this->createAccountsTab(), "Accounts", ":/images/accounts.svg");
this->addTab(this->createAppearanceTab(), "Appearance", ":/images/theme.svg");
this->addTab(this->createBehaviourTab(), "Behaviour", ":/images/behave.svg");
this->addTab(this->createCommandsTab(), "Commands", ":/images/commands.svg");
this->addTab(this->createEmotesTab(), "Emotes", ":/images/emote.svg");
// this->addTab(this->createIgnoredUsersTab(), "Ignored Users",
// ":/images/StatusAnnotations_Blocked_16xLG_color.png");
// this->addTab(this->createIgnoredMessagesTab(), "Ignored Messages",
// ":/images/Filter_16x.png");
// this->addTab(this->createLinksTab(), "Links", ":/images/VSO_Link_blue_16x.png");
// this->addTab(this->createLogsTab(), "Logs", ":/images/VSO_Link_blue_16x.png");
this->addTab(this->createHighlightingTab(), "Highlighting", ":/images/notifications.svg");
// this->addTab(this->createWhispersTab(), "Whispers", ":/images/Message_16xLG.png");
this->addTab(this->createAboutTab(), "About", ":/images/about.svg");
// Add stretch
this->ui.tabs.addStretch(1);
this->addTab(new settingspages::AccountsPage);
this->addTab(new settingspages::AppearancePage);
this->addTab(new settingspages::BehaviourPage);
this->addTab(new settingspages::CommandPage);
this->addTab(new settingspages::EmotesPage);
// this->addTab(new settingspages::HighlightingPage);
// this->addTab(new settingspages::LogsPage);
// this->addTab(new settingspages::ModerationPage);
this->ui.tabContainer->addStretch(1);
this->addTab(new settingspages::AboutPage, Qt::AlignBottom);
}
QVBoxLayout *SettingsDialog::createAccountsTab()
void SettingsDialog::addTab(settingspages::SettingsPage *page, Qt::Alignment alignment)
{
auto layout = new QVBoxLayout();
auto tab = new SettingsDialogTab(this, page, "xD");
// add remove buttons
auto buttonBox = new QDialogButtonBox(this);
this->ui.pageStack->addWidget(page);
this->ui.tabContainer->addWidget(tab, 0, alignment);
this->tabs.push_back(tab);
auto addButton = new QPushButton("Add", this);
addButton->setToolTip("Log in with a new account");
auto removeButton = new QPushButton("Remove", this);
removeButton->setToolTip("Remove selected account");
connect(addButton, &QPushButton::clicked, []() {
static auto loginWidget = new LoginWidget();
loginWidget->show();
});
buttonBox->addButton(addButton, QDialogButtonBox::YesRole);
buttonBox->addButton(removeButton, QDialogButtonBox::NoRole);
layout->addWidget(buttonBox);
this->ui.accountSwitchWidget = new AccountSwitchWidget(this);
connect(removeButton, &QPushButton::clicked, [this]() {
auto selectedUser = this->ui.accountSwitchWidget->currentItem()->text();
if (selectedUser == ANONYMOUS_USERNAME_LABEL) {
// Do nothing
return;
}
singletons::AccountManager::getInstance().Twitch.removeUser(selectedUser);
});
layout->addWidget(this->ui.accountSwitchWidget);
return layout;
}
QVBoxLayout *SettingsDialog::createAppearanceTab()
{
auto &settings = singletons::SettingManager::getInstance();
auto layout = this->createTabLayout();
{
auto group = new QGroupBox("Application");
auto form = new QFormLayout();
auto combo = new QComboBox();
auto fontLayout = new QHBoxLayout();
auto fontFamilyLabel = new QLabel("font family, size");
auto fontButton = new QPushButton("Select");
fontLayout->addWidget(fontButton);
fontLayout->addWidget(fontFamilyLabel);
{
auto &fontManager = singletons::FontManager::getInstance();
auto UpdateFontFamilyLabel = [fontFamilyLabel, &fontManager](auto) {
fontFamilyLabel->setText(
QString::fromStdString(fontManager.currentFontFamily.getValue()) + ", " +
QString::number(fontManager.currentFontSize) + "pt");
};
fontManager.currentFontFamily.connectSimple(UpdateFontFamilyLabel,
this->managedConnections);
fontManager.currentFontSize.connectSimple(UpdateFontFamilyLabel,
this->managedConnections);
}
fontButton->connect(fontButton, &QPushButton::clicked, []() {
auto &fontManager = singletons::FontManager::getInstance();
QFontDialog dialog(fontManager.getFont(singletons::FontManager::Medium, 1.));
dialog.connect(&dialog, &QFontDialog::fontSelected, [](const QFont &font) {
auto &fontManager = singletons::FontManager::getInstance();
fontManager.currentFontFamily = font.family().toStdString();
fontManager.currentFontSize = font.pointSize();
});
dialog.show();
dialog.exec();
});
auto compactTabs = createCheckbox("Hide tab X", settings.hideTabX);
auto hidePreferencesButton = createCheckbox("Hide preferences button (ctrl+p to show)",
settings.hidePreferencesButton);
auto hideUserButton = createCheckbox("Hide user button", settings.hideUserButton);
form->addRow("Theme:", combo);
{
auto hbox = new QHBoxLayout();
auto slider = new QSlider(Qt::Horizontal);
// Theme hue
slider->setMinimum(0);
slider->setMaximum(1000);
pajlada::Settings::Setting<double> themeHue("/appearance/theme/hue");
slider->setValue(std::min(std::max(themeHue.getValue(), 0.0), 1.0) * 1000);
hbox->addWidget(slider);
auto button = new QPushButton();
button->setFlat(true);
hbox->addWidget(button);
form->addRow("Theme color:", hbox);
QObject::connect(slider, &QSlider::valueChanged, this, [button](int value) mutable {
double newValue = value / 1000.0;
pajlada::Settings::Setting<double> themeHue("/appearance/theme/hue");
themeHue.setValue(newValue);
QPalette pal = button->palette();
QColor color;
color.setHsvF(newValue, 1.0, 1.0, 1.0);
pal.setColor(QPalette::Button, color);
button->setAutoFillBackground(true);
button->setPalette(pal);
button->update();
// TODO(pajlada): re-implement
// this->windowManager.updateAll();
});
}
form->addRow("Font:", fontLayout);
form->addRow("Tab bar:", compactTabs);
form->addRow("", hidePreferencesButton);
form->addRow("", hideUserButton);
{
// Theme name
combo->addItems({
"White", //
"Light", //
"Dark", //
"Black", //
});
// combo->addItem("White");
// combo->addItem("Light");
// combo->addItem("Dark");
// combo->addItem("Black");
QString currentComboText = QString::fromStdString(
pajlada::Settings::Setting<std::string>::get("/appearance/theme/name"));
combo->setCurrentText(currentComboText);
QObject::connect(combo, &QComboBox::currentTextChanged, this, [](const QString &value) {
// dirty hack
singletons::EmoteManager::getInstance().incGeneration();
pajlada::Settings::Setting<std::string>::set("/appearance/theme/name",
value.toStdString());
});
}
auto enableSmoothScrolling =
createCheckbox("Enable smooth scrolling", settings.enableSmoothScrolling);
form->addRow("Scrolling:", enableSmoothScrolling);
auto enableSmoothScrollingNewMessages = createCheckbox(
"Enable smooth scrolling for new messages", settings.enableSmoothScrollingNewMessages);
form->addRow("", enableSmoothScrollingNewMessages);
group->setLayout(form);
layout->addWidget(group);
}
{
auto group = new QGroupBox("Messages");
auto v = new QVBoxLayout();
v->addWidget(createCheckbox("Show timestamp", settings.showTimestamps));
// fourtf: add timestamp format
v->addWidget(createCheckbox("Show badges", settings.showBadges));
v->addWidget(createCheckbox("Allow sending duplicate messages (add a space at the end)",
settings.allowDuplicateMessages));
v->addWidget(createCheckbox("Seperate messages", settings.seperateMessages));
v->addWidget(createCheckbox("Show message length", settings.showMessageLength));
v->addLayout(this->createCombobox(
"Username display mode", this->usernameDisplayMode,
{"Username (Localized name)", "Username", "Localized name"},
[](const QString &newValue, pajlada::Settings::Setting<int> &setting) {
if (newValue == "Username (Localized name)") {
setting =
twitch::TwitchMessageBuilder::UsernameDisplayMode::UsernameAndLocalizedName;
} else if (newValue == "Username") {
setting = twitch::TwitchMessageBuilder::UsernameDisplayMode::Username;
} else if (newValue == "Localized name") {
setting = twitch::TwitchMessageBuilder::UsernameDisplayMode::LocalizedName;
}
}));
group->setLayout(v);
layout->addWidget(group);
}
return layout;
}
QVBoxLayout *SettingsDialog::createBehaviourTab()
{
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
auto layout = this->createTabLayout();
auto form = new QFormLayout();
// WINDOW
{
form->addRow("Window:", createCheckbox("Window always on top (requires restart)",
settings.windowTopMost));
// form->addRow("Messages:", createCheckbox("Mention users with a @ (except in
// commands)",
// settings.mentionUsersWithAt));
}
// MESSAGES
{
form->addRow("Messages:",
createCheckbox("Hide input box if empty", settings.hideEmptyInput));
form->addRow("", createCheckbox("Show last read message indicator",
settings.showLastMessageIndicator));
}
// PAUSE
{
form->addRow("Pause chat:", createCheckbox("When hovering", settings.pauseChatHover));
}
// MOUSE SCROLL SPEED
{
auto scroll = new QSlider(Qt::Horizontal);
form->addRow("Mouse scroll speed:", scroll);
float currentValue = singletons::SettingManager::getInstance().mouseScrollMultiplier;
int scrollValue = ((currentValue - 0.1f) / 2.f) * 99.f;
scroll->setValue(scrollValue);
connect(scroll, &QSlider::valueChanged, [](int newValue) {
float mul = static_cast<float>(newValue) / 99.f;
float newScrollValue = (mul * 2.1f) + 0.1f;
singletons::SettingManager::getInstance().mouseScrollMultiplier = newScrollValue;
});
}
// STREAMLINK
{
form->addRow("Streamlink path:", createLineEdit(settings.streamlinkPath));
form->addRow(this->createCombobox(
"Preferred quality:", settings.preferredQuality,
{"Choose", "Source", "High", "Medium", "Low", "Audio only"},
[](const QString &newValue, pajlada::Settings::Setting<std::string> &setting) {
setting = newValue.toStdString();
}));
}
layout->addLayout(form);
return layout;
}
QVBoxLayout *SettingsDialog::createCommandsTab()
{
singletons::CommandManager &commandManager = singletons::CommandManager::getInstance();
this->commandsTextChangedDelay.setSingleShot(true);
auto layout = this->createTabLayout();
layout->addWidget(new QLabel("One command per line."));
layout->addWidget(new QLabel("\"/cmd example command\" will print "
"\"example command\" when you type /cmd in chat."));
layout->addWidget(new QLabel("{1} will be replaced with the first word you type after the "
"command, {2} with the second and so on."));
layout->addWidget(new QLabel("{1+} will be replaced with first word and everything after, {2+} "
"with everything after the second word and so on"));
layout->addWidget(new QLabel("Duplicate commands will be ignored."));
QTextEdit *textEdit = new QTextEdit();
textEdit->setPlainText(QString(commandManager.getCommands().join('\n')));
layout->addWidget(textEdit);
QObject::connect(textEdit, &QTextEdit::textChanged,
[this] { this->commandsTextChangedDelay.start(200); });
QObject::connect(&this->commandsTextChangedDelay, &QTimer::timeout,
[textEdit, &commandManager] {
QString text = textEdit->toPlainText();
QStringList lines = text.split(QRegularExpression("(\r?\n|\r\n?)"));
commandManager.setCommands(lines);
});
return layout;
}
QVBoxLayout *SettingsDialog::createEmotesTab()
{
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
auto layout = this->createTabLayout();
layout->addWidget(createCheckbox("Enable Twitch Emotes", settings.enableTwitchEmotes));
layout->addWidget(createCheckbox("Enable BetterTTV Emotes", settings.enableBttvEmotes));
layout->addWidget(createCheckbox("Enable FrankerFaceZ Emotes", settings.enableFfzEmotes));
layout->addWidget(createCheckbox("Enable Gif Emotes", settings.enableGifAnimations));
layout->addWidget(createCheckbox("Enable Emojis", settings.enableEmojis));
layout->addWidget(createCheckbox("Enable Twitch Emotes", settings.enableTwitchEmotes));
// Preferred emote quality
{
auto box = new QHBoxLayout();
auto label = new QLabel("Preferred emote quality");
label->setToolTip("Select which emote quality you prefer to download");
auto widget = new QComboBox();
widget->addItems({"1x", "2x", "4x"});
box->addWidget(label);
box->addWidget(widget);
settings.preferredEmoteQuality.connect([widget](int newIndex, auto) {
widget->setCurrentIndex(newIndex); //
});
QObject::connect(
widget, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [](int index) {
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
settings.preferredEmoteQuality = index; //
});
layout->addLayout(box);
}
return layout;
}
QVBoxLayout *SettingsDialog::createAboutTab()
{
auto layout = this->createTabLayout();
QPixmap image;
image.load(":/images/aboutlogo.png");
auto aboutimage = new QLabel();
aboutimage->setPixmap(image.scaled(QSize(631, 132)));
layout->addWidget(aboutimage);
auto created = new QLabel();
created->setText("Twitch Chat Client created by <a href=\"github.com/fourtf\">fourtf</a>");
created->setTextFormat(Qt::RichText);
created->setTextInteractionFlags(Qt::TextBrowserInteraction);
created->setOpenExternalLinks(true);
layout->addWidget(created);
auto github = new QLabel();
github->setText("<a href=\"github.com/fourt/chatterino2\">Chatterino on Github</a>");
github->setTextFormat(Qt::RichText);
github->setTextInteractionFlags(Qt::TextBrowserInteraction);
github->setOpenExternalLinks(true);
layout->addWidget(github);
return layout;
}
QVBoxLayout *SettingsDialog::createIgnoredUsersTab()
{
auto layout = this->createTabLayout();
return layout;
}
QVBoxLayout *SettingsDialog::createIgnoredMessagesTab()
{
auto layout = this->createTabLayout();
return layout;
}
QVBoxLayout *SettingsDialog::createLinksTab()
{
auto layout = this->createTabLayout();
return layout;
}
QVBoxLayout *SettingsDialog::createLogsTab()
{
auto layout = this->createTabLayout();
return layout;
}
QVBoxLayout *SettingsDialog::createHighlightingTab()
{
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
auto layout = this->createTabLayout();
auto highlights = new QListWidget();
auto highlightUserBlacklist = new QTextEdit();
globalHighlights = highlights;
const auto highlightProperties = settings.highlightProperties.getValue();
for (const auto &highlightProperty : highlightProperties) {
highlights->addItem(highlightProperty.key);
}
highlightUserBlacklist->setText(settings.highlightUserBlacklist);
auto highlightTab = new QTabWidget();
auto customSound = new QHBoxLayout();
auto soundForm = new QFormLayout();
{
layout->addWidget(createCheckbox("Enable Highlighting", settings.enableHighlights));
layout->addWidget(createCheckbox("Highlight messages containing your name",
settings.enableHighlightsSelf));
layout->addWidget(createCheckbox("Play sound when your name is mentioned",
settings.enableHighlightSound));
layout->addWidget(createCheckbox("Flash taskbar when your name is mentioned",
settings.enableHighlightTaskbar));
customSound->addWidget(createCheckbox("Custom sound", settings.customHighlightSound));
auto selectBtn = new QPushButton("Select");
QObject::connect(selectBtn, &QPushButton::clicked, this, [&settings, this] {
auto fileName = QFileDialog::getOpenFileName(this, tr("Open Sound"), "",
tr("Audio Files (*.mp3 *.wav)"));
settings.pathHighlightSound = fileName;
});
customSound->addWidget(selectBtn);
}
soundForm->addRow(customSound);
{
auto hbox = new QHBoxLayout();
auto addBtn = new QPushButton("Add");
auto editBtn = new QPushButton("Edit");
auto delBtn = new QPushButton("Remove");
// Open "Add new highlight" dialog
QObject::connect(addBtn, &QPushButton::clicked, this, [highlights, this, &settings] {
auto show = new QWidget();
auto box = new QBoxLayout(QBoxLayout::TopToBottom);
auto edit = new QLineEdit();
auto add = new QPushButton("Add");
auto sound = new QCheckBox("Play sound");
auto task = new QCheckBox("Flash taskbar");
// Save highlight
QObject::connect(add, &QPushButton::clicked, this, [=, &settings] {
if (edit->text().length()) {
QString highlightKey = edit->text();
highlights->addItem(highlightKey);
auto properties = settings.highlightProperties.getValue();
messages::HighlightPhrase newHighlightProperty;
newHighlightProperty.key = highlightKey;
newHighlightProperty.sound = sound->isChecked();
newHighlightProperty.alert = task->isChecked();
properties.push_back(newHighlightProperty);
settings.highlightProperties = properties;
show->close();
}
});
box->addWidget(edit);
box->addWidget(add);
box->addWidget(sound);
box->addWidget(task);
show->setLayout(box);
show->show();
});
// Open "Edit selected highlight" dialog
QObject::connect(editBtn, &QPushButton::clicked, this, [highlights, this, &settings] {
if (highlights->selectedItems().isEmpty()) {
// No item selected
return;
}
QListWidgetItem *selectedHighlight = highlights->selectedItems().first();
QString highlightKey = selectedHighlight->text();
auto properties = settings.highlightProperties.getValue();
auto highlightIt = std::find_if(properties.begin(), properties.end(),
[highlightKey](const auto &highlight) {
return highlight.key == highlightKey; //
});
if (highlightIt == properties.end()) {
debug::Log("Unable to find highlight key {} in highlight properties. "
"This is weird",
highlightKey);
return;
}
messages::HighlightPhrase &selectedSetting = *highlightIt;
auto show = new QWidget();
auto box = new QBoxLayout(QBoxLayout::TopToBottom);
auto edit = new QLineEdit(highlightKey);
auto apply = new QPushButton("Apply");
auto sound = new QCheckBox("Play sound");
sound->setChecked(selectedSetting.sound);
auto task = new QCheckBox("Flash taskbar");
task->setChecked(selectedSetting.alert);
// Apply edited changes
QObject::connect(apply, &QPushButton::clicked, this, [=, &settings] {
QString newHighlightKey = edit->text();
if (newHighlightKey.length() == 0) {
return;
}
auto properties = settings.highlightProperties.getValue();
auto highlightIt =
std::find_if(properties.begin(), properties.end(), [=](const auto &highlight) {
return highlight.key == highlightKey; //
});
if (highlightIt == properties.end()) {
debug::Log("Unable to find highlight key {} in highlight properties. "
"This is weird",
highlightKey);
return;
}
auto &highlightProperty = *highlightIt;
highlightProperty.key = newHighlightKey;
highlightProperty.sound = sound->isCheckable();
highlightProperty.alert = task->isCheckable();
settings.highlightProperties = properties;
selectedHighlight->setText(newHighlightKey);
show->close();
});
box->addWidget(edit);
box->addWidget(apply);
box->addWidget(sound);
box->addWidget(task);
show->setLayout(box);
show->show();
});
// Delete selected highlight
QObject::connect(delBtn, &QPushButton::clicked, this, [highlights, &settings] {
if (highlights->selectedItems().isEmpty()) {
// No highlight selected
return;
}
QListWidgetItem *selectedHighlight = highlights->selectedItems().first();
QString highlightKey = selectedHighlight->text();
auto properties = settings.highlightProperties.getValue();
properties.erase(std::remove_if(properties.begin(), properties.end(),
[highlightKey](const auto &highlight) {
return highlight.key == highlightKey; //
}),
properties.end());
settings.highlightProperties = properties;
delete selectedHighlight;
});
layout->addLayout(soundForm);
layout->addWidget(
createCheckbox("Always play highlight sound (Even if Chatterino is focused)",
settings.highlightAlwaysPlaySound));
auto layoutVbox = new QVBoxLayout();
auto btnHbox = new QHBoxLayout();
auto highlightWidget = new QWidget();
auto btnWidget = new QWidget();
btnHbox->addWidget(addBtn);
btnHbox->addWidget(editBtn);
btnHbox->addWidget(delBtn);
btnWidget->setLayout(btnHbox);
layoutVbox->addWidget(highlights);
layoutVbox->addWidget(btnWidget);
highlightWidget->setLayout(layoutVbox);
highlightTab->addTab(highlightWidget, "Highlights");
highlightTab->addTab(highlightUserBlacklist, "Disabled Users");
layout->addWidget(highlightTab);
layout->addLayout(hbox);
}
QObject::connect(&this->ui.okButton, &QPushButton::clicked, this, [=, &settings]() {
QStringList list =
highlightUserBlacklist->toPlainText().split("\n", QString::SkipEmptyParts);
list.removeDuplicates();
settings.highlightUserBlacklist = list.join("\n") + "\n";
});
settings.highlightUserBlacklist.connect([=](const QString &str, auto) {
highlightUserBlacklist->setPlainText(str); //
});
return layout;
}
QVBoxLayout *SettingsDialog::createWhispersTab()
{
auto layout = this->createTabLayout();
return layout;
}
void SettingsDialog::addTab(QBoxLayout *layout, QString title, QString imageRes)
{
layout->addStretch(1);
auto widget = new QWidget();
widget->setLayout(layout);
auto tab = new SettingsDialogTab(this, title, imageRes);
tab->setWidget(widget);
this->ui.tabs.addWidget(tab, 0, Qt::AlignTop);
tabs.push_back(tab);
this->ui.pageStack.addWidget(widget);
if (this->ui.tabs.count() == 1) {
if (this->tabs.size() == 1) {
this->select(tab);
}
}
void SettingsDialog::select(SettingsDialogTab *tab)
{
this->ui.pageStack.setCurrentWidget(tab->getWidget());
this->ui.pageStack->setCurrentWidget(tab->getSettingsPage());
if (this->selectedTab != nullptr) {
this->selectedTab->setSelected(false);
@ -790,7 +155,7 @@ void SettingsDialog::showDialog(PreferredTab preferredTab)
void SettingsDialog::refresh()
{
this->ui.accountSwitchWidget->refresh();
// this->ui.accountSwitchWidget->refresh();
singletons::SettingManager::getInstance().saveSnapshot();
}
@ -809,107 +174,32 @@ void SettingsDialog::dpiMultiplierChanged(float oldDpi, float newDpi)
this->setStyleSheet(styleSheet);
this->ui.tabWidget.setFixedWidth((int)(200 * newDpi));
this->ui.tabContainerContainer->setFixedWidth((int)(200 * newDpi));
}
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);
// 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);
// // }
// // }
// // }
//}
// 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
QVBoxLayout *SettingsDialog::createTabLayout()
{
auto layout = new QVBoxLayout();
return layout;
}
QCheckBox *SettingsDialog::createCheckbox(const QString &title,
pajlada::Settings::Setting<bool> &setting)
{
auto checkbox = new QCheckBox(title);
// Set checkbox initial state
setting.connect([checkbox](const bool &value, auto) {
checkbox->setChecked(value); //
});
QObject::connect(checkbox, &QCheckBox::toggled, this, [&setting](bool state) {
qDebug() << "update checkbox value";
setting = state; //
});
return checkbox;
}
QHBoxLayout *SettingsDialog::createCombobox(
const QString &title, pajlada::Settings::Setting<int> &setting, QStringList items,
std::function<void(QString, pajlada::Settings::Setting<int> &)> cb)
{
auto box = new QHBoxLayout();
auto label = new QLabel(title);
auto widget = new QComboBox();
widget->addItems(items);
QObject::connect(widget, &QComboBox::currentTextChanged, this,
[&setting, cb](const QString &newValue) {
cb(newValue, setting); //
});
box->addWidget(label);
box->addWidget(widget);
return box;
}
QHBoxLayout *SettingsDialog::createCombobox(
const QString &title, pajlada::Settings::Setting<std::string> &setting, QStringList items,
std::function<void(QString, pajlada::Settings::Setting<std::string> &)> cb)
{
auto box = new QHBoxLayout();
auto label = new QLabel(title);
auto widget = new QComboBox();
widget->addItems(items);
widget->setCurrentText(QString::fromStdString(setting.getValue()));
QObject::connect(widget, &QComboBox::currentTextChanged, this,
[&setting, cb](const QString &newValue) {
cb(newValue, setting); //
});
box->addWidget(label);
box->addWidget(widget);
return box;
}
QLineEdit *SettingsDialog::createLineEdit(pajlada::Settings::Setting<std::string> &setting)
{
auto widget = new QLineEdit(QString::fromStdString(setting.getValue()));
QObject::connect(widget, &QLineEdit::textChanged, this,
[&setting](const QString &newValue) { setting = newValue.toStdString(); });
return widget;
}
///// Widget creation helpers
void SettingsDialog::okButtonClicked()
{
this->close();
@ -919,6 +209,10 @@ void SettingsDialog::cancelButtonClicked()
{
auto &settings = singletons::SettingManager::getInstance();
for (auto &tab : this->tabs) {
tab->getSettingsPage()->cancel();
}
settings.recallSnapshot();
this->close();

View file

@ -3,6 +3,7 @@
#include "singletons/settingsmanager.hpp"
#include "widgets/accountswitchwidget.hpp"
#include "widgets/helper/settingsdialogtab.hpp"
#include "widgets/settingspages/appearancepage.hpp"
#include <QButtonGroup>
#include <QCheckBox>
@ -27,6 +28,7 @@ namespace widgets {
class SettingsDialog : public BaseWidget
{
public:
SettingsDialog();
void select(SettingsDialogTab *tab);
@ -50,62 +52,26 @@ private:
void refresh();
static SettingsDialog *handle;
std::vector<SettingsDialogTab *> tabs;
pajlada::Settings::Setting<int> usernameDisplayMode;
QTimer commandsTextChangedDelay;
struct {
QVBoxLayout tabs;
QVBoxLayout vbox;
QHBoxLayout hbox;
QWidget tabWidget;
QStackedLayout pageStack;
QDialogButtonBox buttonBox;
QPushButton okButton;
QPushButton cancelButton;
AccountSwitchWidget *accountSwitchWidget = nullptr;
QWidget *tabContainerContainer;
QVBoxLayout *tabContainer;
QStackedLayout *pageStack;
QPushButton *okButton;
QPushButton *cancelButton;
} ui;
void addTab(QBoxLayout *layout, QString title, QString imageRes);
void addTabs();
std::vector<SettingsDialogTab *> tabs;
QVBoxLayout *createAccountsTab();
QVBoxLayout *createAppearanceTab();
QVBoxLayout *createMessagesTab();
QVBoxLayout *createBehaviourTab();
QVBoxLayout *createCommandsTab();
QVBoxLayout *createEmotesTab();
QVBoxLayout *createIgnoredUsersTab();
QVBoxLayout *createIgnoredMessagesTab();
QVBoxLayout *createLinksTab();
QVBoxLayout *createLogsTab();
QVBoxLayout *createHighlightingTab();
QVBoxLayout *createWhispersTab();
QVBoxLayout *createAboutTab();
void initUi();
void addTabs();
void addTab(settingspages::SettingsPage *page, Qt::Alignment alignment = Qt::AlignTop);
SettingsDialogTab *selectedTab = nullptr;
QListWidget *globalHighlights;
/// Widget creation helpers
QVBoxLayout *createTabLayout();
QCheckBox *createCheckbox(const QString &title, pajlada::Settings::Setting<bool> &setting);
QHBoxLayout *createCombobox(const QString &title, pajlada::Settings::Setting<int> &setting,
QStringList items,
std::function<void(QString, pajlada::Settings::Setting<int> &)> cb);
QHBoxLayout *createCombobox(
const QString &title, pajlada::Settings::Setting<std::string> &setting, QStringList items,
std::function<void(QString, pajlada::Settings::Setting<std::string> &)> cb);
QLineEdit *createLineEdit(pajlada::Settings::Setting<std::string> &setting);
void okButtonClicked();
void cancelButtonClicked();
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
static void setChildrensFont(QLayout *object, QFont &font, int indent = 0);
// static void setChildrensFont(QLayout *object, QFont &font, int indent = 0);
};
} // namespace widgets

View file

@ -0,0 +1,13 @@
#include "aboutpage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
AboutPage::AboutPage()
: SettingsPage("About", ":/images/about.svg")
{
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,16 @@
#pragma once
#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class AboutPage : public SettingsPage
{
public:
AboutPage();
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,46 @@
#include "accountspage.hpp"
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include "const.hpp"
#include "singletons/accountmanager.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/logindialog.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
AccountsPage::AccountsPage()
: SettingsPage("Accounts", ":/images/accounts.svg")
{
util::LayoutCreator<AccountsPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
auto buttons = layout.emplace<QDialogButtonBox>();
{
this->addButton = buttons->addButton("Add", QDialogButtonBox::YesRole);
this->removeButton = buttons->addButton("Remove", QDialogButtonBox::NoRole);
}
auto accountSwitch = 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;
}
singletons::AccountManager::getInstance().Twitch.removeUser(selectedUser);
});
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,24 @@
#pragma once
#include <QPushButton>
#include "widgets/accountswitchwidget.hpp"
#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class AccountsPage : public SettingsPage
{
public:
AccountsPage();
private:
QPushButton *addButton;
QPushButton *removeButton;
AccountSwitchWidget *accSwitchWidget;
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,148 @@
#include "appearancepage.hpp"
#include <QFontDialog>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <util/layoutcreator.hpp>
#define THEME_ITEMS "White", "Light", "Dark", "Black"
#define TAB_X "Hide tab x"
#define TAB_PREF "Hide preferences button (ctrl+p to show)"
#define TAB_USER "Hide user button"
#define SCROLL_SMOOTH "Enable smooth scrolling"
#define SCROLL_NEWMSG "Enable smooth scrolling for new messages"
#define TIMESTAMP_FORMATS "hh:mm a", "h:mm a", "HH:mm", "H:mm"
namespace chatterino {
namespace widgets {
namespace settingspages {
AppearancePage::AppearancePage()
: SettingsPage("Appearance", ":/images/theme.svg")
{
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
util::LayoutCreator<AppearancePage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
auto application =
layout.emplace<QGroupBox>("Application").emplace<QVBoxLayout>().withoutMargin();
{
auto form = application.emplace<QFormLayout>();
// clang-format off
form->addRow("Theme:", this->createComboBox({THEME_ITEMS}, singletons::ThemeManager::getInstance().themeName));
form->addRow("Theme color:", this->createThemeColorChanger());
form->addRow("Font:", this->createFontChanger());
form->addRow("Tab bar:", this->createCheckBox(TAB_X, settings.hideTabX));
form->addRow("", this->createCheckBox(TAB_PREF, settings.hidePreferencesButton));
form->addRow("", this->createCheckBox(TAB_USER, settings.hideUserButton));
form->addRow("Scrolling:", this->createCheckBox(SCROLL_SMOOTH, settings.enableSmoothScrolling));
form->addRow("", this->createCheckBox(SCROLL_NEWMSG, settings.enableSmoothScrollingNewMessages));
// clang-format on
}
auto messages = layout.emplace<QGroupBox>("Messages").emplace<QVBoxLayout>().withoutMargin();
{
messages.append(this->createCheckBox("Show timestamp", settings.showTimestamps));
auto tbox = messages.emplace<QHBoxLayout>();
{
tbox.emplace<QLabel>("timestamp format:");
tbox.append(this->createComboBox({TIMESTAMP_FORMATS}, settings.timestampFormat));
}
messages.append(this->createCheckBox("Show badges", settings.showBadges));
messages.append(this->createCheckBox("Seperate messages", settings.seperateMessages));
messages.append(this->createCheckBox("Show message length", settings.showMessageLength));
}
layout->addStretch(1);
}
QLayout *AppearancePage::createThemeColorChanger()
{
QHBoxLayout *layout = new QHBoxLayout;
auto &themeHue = singletons::ThemeManager::getInstance().themeHue;
// SLIDER
QSlider *slider = new QSlider(Qt::Horizontal);
layout->addWidget(slider);
slider->setValue(std::min(std::max(themeHue.getValue(), 0.0), 1.0) * 1000);
// BUTTON
QPushButton *button = new QPushButton;
layout->addWidget(button);
button->setFlat(true);
button->setFixedWidth(64);
// SIGNALS
QObject::connect(slider, &QSlider::valueChanged, this, [button, &themeHue](int value) mutable {
double newValue = value / 1000.0;
themeHue.setValue(newValue);
QPalette pal = button->palette();
QColor color;
color.setHsvF(newValue, 1.0, 1.0, 1.0);
pal.setColor(QPalette::Button, color);
button->setAutoFillBackground(true);
button->setPalette(pal);
button->update();
// TODO(pajlada): re-implement
// this->windowManager.updateAll();
});
return layout;
}
QLayout *AppearancePage::createFontChanger()
{
QHBoxLayout *layout = new QHBoxLayout;
auto &fontManager = singletons::FontManager::getInstance();
// LABEL
QLabel *label = new QLabel();
layout->addWidget(label);
auto updateFontFamilyLabel = [label, &fontManager](auto) {
label->setText(QString::fromStdString(fontManager.currentFontFamily.getValue()) + ", " +
QString::number(fontManager.currentFontSize) + "pt");
};
fontManager.currentFontFamily.connectSimple(updateFontFamilyLabel, this->managedConnections);
fontManager.currentFontSize.connectSimple(updateFontFamilyLabel, this->managedConnections);
// BUTTON
QPushButton *button = new QPushButton("Select");
layout->addWidget(button);
button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Policy::Fixed);
QObject::connect(button, &QPushButton::clicked, []() {
auto &fontManager = singletons::FontManager::getInstance();
QFontDialog dialog(fontManager.getFont(singletons::FontManager::Medium, 1.));
dialog.connect(&dialog, &QFontDialog::fontSelected, [](const QFont &font) {
auto &fontManager = singletons::FontManager::getInstance();
fontManager.currentFontFamily = font.family().toStdString();
fontManager.currentFontSize = font.pointSize();
});
dialog.show();
dialog.exec();
});
return layout;
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,22 @@
#pragma once
#include "widgets/settingspages/settingspage.hpp"
#include <QSlider>
namespace chatterino {
namespace widgets {
namespace settingspages {
class AppearancePage : public SettingsPage
{
public:
AppearancePage();
QLayout *createThemeColorChanger();
QLayout *createFontChanger();
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,57 @@
#include "behaviourpage.hpp"
#include <QFormLayout>
#include <QLabel>
#include <QVBoxLayout>
#include <util/layoutcreator.hpp>
#define WINDOW_TOPMOST "Window always on top (requires restart)"
#define INPUT_EMPTY "Hide input box when empty"
#define LAST_MSG "Show last read message indicator"
#define PAUSE_HOVERING "When hovering"
#define STREAMLINK_QUALITY "Source", "High", "Medium", "Low", "Audio only"
namespace chatterino {
namespace widgets {
namespace settingspages {
BehaviourPage::BehaviourPage()
: SettingsPage("Behaviour", nullptr)
{
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
util::LayoutCreator<BehaviourPage> layoutCreator(this);
auto form = layoutCreator.emplace<QFormLayout>().withoutMargin();
{
form->addRow("Window:", this->createCheckBox(WINDOW_TOPMOST, settings.windowTopMost));
form->addRow("Messages:", this->createCheckBox(INPUT_EMPTY, settings.windowTopMost));
form->addRow("", this->createCheckBox(LAST_MSG, settings.windowTopMost));
form->addRow("Pause chat:", this->createCheckBox(PAUSE_HOVERING, settings.windowTopMost));
form->addRow("Mouse scroll speed:", this->createMouseScrollSlider());
form->addRow("Streamlink path:", this->createLineEdit(settings.streamlinkPath));
form->addRow("Prefered quality:",
this->createComboBox({STREAMLINK_QUALITY}, settings.preferredQuality));
}
}
QSlider *BehaviourPage::createMouseScrollSlider()
{
QSlider *slider = new QSlider(Qt::Horizontal);
float currentValue = singletons::SettingManager::getInstance().mouseScrollMultiplier;
int sliderValue = ((currentValue - 0.1f) / 2.f) * 99.f;
slider->setValue(sliderValue);
QObject::connect(slider, &QSlider::valueChanged, [](int newValue) {
float mul = static_cast<float>(newValue) / 99.f;
float newSliderValue = (mul * 2.1f) + 0.1f;
singletons::SettingManager::getInstance().mouseScrollMultiplier = newSliderValue;
});
return slider;
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,22 @@
#pragma once
#include <QSlider>
#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class BehaviourPage : public SettingsPage
{
public:
BehaviourPage();
private:
QSlider *createMouseScrollSlider();
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,65 @@
#include "commandpage.hpp"
#include <QLabel>
#include <QTextEdit>
#include <util/layoutcreator.hpp>
#include "singletons/commandmanager.hpp"
// clang-format off
#define TEXT "One command per line.\n"\
"\"/cmd example command\" will print \"example command\" when you type /cmd in chat.\n"\
"{1} will be replaced with the first word you type after then command, {2} with the second and so on.\n"\
"{1+} will be replaced with first word and everything after, {2+} with everything after the second word and so on\n"\
"Duplicate commands will be ignored."
// clang-format on
namespace chatterino {
namespace widgets {
namespace settingspages {
CommandPage::CommandPage()
: SettingsPage("Commands", ":/images/commands.svg")
{
util::LayoutCreator<CommandPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>();
layout.emplace<QLabel>(TEXT)->setWordWrap(true);
layout.append(this->getCommandsTextEdit());
// ---- end of layout
this->commandsEditTimer.setSingleShot(true);
}
QTextEdit *CommandPage::getCommandsTextEdit()
{
singletons::CommandManager &commandManager = singletons::CommandManager::getInstance();
// cancel
QStringList currentCommands = commandManager.getCommands();
this->onCancel.connect(
[currentCommands, &commandManager] { commandManager.setCommands(currentCommands); });
// create text edit
QTextEdit *textEdit = new QTextEdit;
textEdit->setPlainText(QString(commandManager.getCommands().join('\n')));
QObject::connect(textEdit, &QTextEdit::textChanged,
[this] { this->commandsEditTimer.start(200); });
QObject::connect(&this->commandsEditTimer, &QTimer::timeout, [textEdit, &commandManager] {
QString text = textEdit->toPlainText();
QStringList lines = text.split(QRegularExpression("(\r?\n|\r\n?)"));
commandManager.setCommands(lines);
});
return textEdit;
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,25 @@
#pragma once
#include <QTextEdit>
#include <QTimer>
#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class CommandPage : public SettingsPage
{
public:
CommandPage();
private:
QTextEdit *getCommandsTextEdit();
QTimer commandsEditTimer;
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,27 @@
#include "emotespage.hpp"
#include <util/layoutcreator.hpp>
namespace chatterino {
namespace widgets {
namespace settingspages {
EmotesPage::EmotesPage()
: SettingsPage("Emotes", ":/images/emote.svg")
{
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
util::LayoutCreator<EmotesPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
// clang-format off
layout.append(this->createCheckBox("Enable Twitch emotes", settings.enableTwitchEmotes));
layout.append(this->createCheckBox("Enable BetterTTV emotes", settings.enableBttvEmotes));
layout.append(this->createCheckBox("Enable FrankerFaceZ emotes", settings.enableFfzEmotes));
layout.append(this->createCheckBox("Enable emojis", settings.enableEmojis));
layout.append(this->createCheckBox("Enable gif animations", settings.enableGifAnimations));
// clang-format on
layout->addStretch(1);
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,16 @@
#pragma once
#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class EmotesPage : public SettingsPage
{
public:
EmotesPage();
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,12 @@
#include "highlightingpage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
HighlightingPage::HighlightingPage()
: SettingsPage("Highlights", ":/images/VSO_Link_blue_16x.png")
{
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,17 @@
#pragma once
#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class HighlightingPage : public SettingsPage
{
public:
HighlightingPage();
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,12 @@
#include "logspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
LogsPage::LogsPage()
: SettingsPage("Logs", ":/images/VSO_Link_blue_16x.png")
{
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,16 @@
#pragma once
#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class LogsPage : public SettingsPage
{
public:
LogsPage();
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,12 @@
#include "moderationpage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
ModerationPage::ModerationPage()
: SettingsPage("Moderation", ":/images/VSO_Link_blue_16x.png")
{
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,16 @@
#pragma once
#include "widgets/settingspages/settingspage.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class ModerationPage : public SettingsPage
{
public:
ModerationPage();
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,78 @@
#include "settingspage.hpp"
#include <QDebug>
namespace chatterino {
namespace widgets {
namespace settingspages {
SettingsPage::SettingsPage(const QString &_name, const QString &_resourceName)
: name(_name)
, resourceName(_resourceName)
{
}
const QString &SettingsPage::getName()
{
return this->name;
}
void SettingsPage::cancel()
{
this->onCancel.invoke();
}
QCheckBox *SettingsPage::createCheckBox(const QString &text,
pajlada::Settings::Setting<bool> &setting)
{
QCheckBox *checkbox = new QCheckBox(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) {
qDebug() << "update checkbox value";
setting = state; //
});
return checkbox;
}
QComboBox *SettingsPage::createComboBox(const QStringList &items,
pajlada::Settings::Setting<QString> &setting)
{
QComboBox *combo = new QComboBox();
// 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;
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -0,0 +1,37 @@
#pragma once
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <pajlada/signals/signal.hpp>
#include "singletons/settingsmanager.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
class SettingsPage : public QWidget
{
public:
SettingsPage(const QString &name, const QString &resourceName);
const QString &getName();
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);
protected:
QString name;
QString resourceName;
pajlada::Signals::NoArgSignal onCancel;
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
};
} // namespace settingspages
} // namespace widgets
} // namespace chatterino

View file

@ -289,10 +289,9 @@ void Split::doOpenPopupPlayer()
void Split::doOpenStreamlink()
{
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
QString preferredQuality =
QString::fromStdString(settings.preferredQuality.getValue()).toLower();
QString preferredQuality = settings.preferredQuality;
// TODO(Confuseh): Default streamlink paths
QString path = QString::fromStdString(settings.streamlinkPath.getValue());
QString path = settings.streamlinkPath;
QString channel = QString::fromStdString(this->channelName.getValue());
QFileInfo fileinfo = QFileInfo(path);