diff --git a/chatterino.pro b/chatterino.pro index 0b752941..053d522c 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -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 = diff --git a/src/.clang-format b/src/.clang-format deleted file mode 120000 index 7cab60ce..00000000 --- a/src/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../.clang-format \ No newline at end of file diff --git a/src/logging/.clang-format b/src/logging/.clang-format deleted file mode 120000 index 7cab60ce..00000000 --- a/src/logging/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../.clang-format \ No newline at end of file diff --git a/src/messages/.clang-format b/src/messages/.clang-format deleted file mode 120000 index 7cab60ce..00000000 --- a/src/messages/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../.clang-format \ No newline at end of file diff --git a/src/messages/messageelement.cpp b/src/messages/messageelement.cpp index 58b0d1e4..2c7d247d 100644 --- a/src/messages/messageelement.cpp +++ b/src/messages/messageelement.cpp @@ -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); - } + 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 diff --git a/src/messages/messageelement.hpp b/src/messages/messageelement.hpp index 97aecdad..7aceba82 100644 --- a/src/messages/messageelement.hpp +++ b/src/messages/messageelement.hpp @@ -189,6 +189,7 @@ class TimestampElement : public MessageElement { QTime time; TextElement *element; + QString format; public: TimestampElement(); diff --git a/src/singletons/settingsmanager.hpp b/src/singletons/settingsmanager.hpp index 78d5bf88..3b23076b 100644 --- a/src/singletons/settingsmanager.hpp +++ b/src/singletons/settingsmanager.hpp @@ -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 diff --git a/src/singletons/thememanager.cpp b/src/singletons/thememanager.cpp index f11c5679..a844ede2 100644 --- a/src/singletons/thememanager.cpp +++ b/src/singletons/thememanager.cpp @@ -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,10 +157,10 @@ 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.saturationF() * 0.2); + color.setHslF( + color.hueF(), color.saturationF(), + color.lightnessF() - sin((color.hueF() - 0.1) / (0.3333 - 0.1) * 3.14159) * + color.saturationF() * 0.2); } } else { if (color.lightnessF() < 0.5f) { @@ -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.saturationF() * 0.4); + 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 -} diff --git a/src/singletons/thememanager.hpp b/src/singletons/thememanager.hpp index c9e5a207..c6463665 100644 --- a/src/singletons/thememanager.hpp +++ b/src/singletons/thememanager.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace chatterino { namespace singletons { @@ -102,10 +103,10 @@ public: boost::signals2::signal updated; -private: - pajlada::Settings::Setting themeName; + pajlada::Settings::Setting themeName; pajlada::Settings::Setting 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 -} diff --git a/src/twitch/.clang-format b/src/twitch/.clang-format deleted file mode 120000 index 7cab60ce..00000000 --- a/src/twitch/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../.clang-format \ No newline at end of file diff --git a/src/util/.clang-format b/src/util/.clang-format deleted file mode 120000 index 7cab60ce..00000000 --- a/src/util/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../.clang-format \ No newline at end of file diff --git a/src/util/layoutcreator.hpp b/src/util/layoutcreator.hpp new file mode 100644 index 00000000..16b4b57f --- /dev/null +++ b/src/util/layoutcreator.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include +#include + +#include +#include + +namespace chatterino { +namespace util { + +template +class LayoutCreator +{ +public: + LayoutCreator(T *_item) + : item(_item) + { + } + + T *operator->() + { + return this->item; + } + + T *getElement() + { + return this->item; + } + + template + LayoutCreator append(T2 *_item) + { + this->_addItem(this->getOrCreateLayout(), _item); + + return LayoutCreator(_item); + } + + template + LayoutCreator emplace(Args &&... args) + { + T2 *t = new T2(std::forward(args)...); + + this->_addItem(this->getOrCreateLayout(), t); + + return LayoutCreator(t); + } + + LayoutCreator assign(T **ptr) + { + *ptr = this->item; + + return *this; + } + + template ::value, int>::type = 0> + LayoutCreator withoutMargin() + { + this->item->setContentsMargins(0, 0, 0, 0); + + return *this; + } + +private: + T *item; + + template ::value, int>::type = 0> + void _addItem(QLayout *layout, T2 *item) + { + layout->addWidget(item); + } + + template ::value, int>::type = 0> + void _addItem(QLayout *layout, T2 *item) + { + QWidget *widget = new QWidget(); + widget->setLayout(item); + layout->addWidget(widget); + } + + template ::value, int>::type = 0> + QLayout *getOrCreateLayout() + { + return this->item; + } + + template ::value, int>::type = 0> + QLayout *getOrCreateLayout() + { + if (!this->item->layout()) { + this->item->setLayout(new QHBoxLayout()); + } + + return this->item->layout(); + } +}; +} // namespace util +} // namespace chatterino diff --git a/src/widgets/.clang-format b/src/widgets/.clang-format deleted file mode 120000 index 7cab60ce..00000000 --- a/src/widgets/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../.clang-format \ No newline at end of file diff --git a/src/widgets/helper/.clang-format b/src/widgets/helper/.clang-format deleted file mode 100644 index 7cab60ce..00000000 --- a/src/widgets/helper/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../.clang-format \ No newline at end of file diff --git a/src/widgets/helper/settingsdialogtab.cpp b/src/widgets/helper/settingsdialogtab.cpp index 8ebb2240..887c86de 100644 --- a/src/widgets/helper/settingsdialogtab.cpp +++ b/src/widgets/helper/settingsdialogtab.cpp @@ -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 *) @@ -51,8 +47,7 @@ void SettingsDialogTab::paintEvent(QPaintEvent *) this->style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); int a = (this->height() - 20) / 2; - QPixmap pixmap = this->ui.icon.pixmap(QSize(20,20)); - + QPixmap pixmap = this->ui.icon.pixmap(QSize(20, 20)); painter.drawPixmap(0, a, pixmap); diff --git a/src/widgets/helper/settingsdialogtab.hpp b/src/widgets/helper/settingsdialogtab.hpp index 8a16c7f4..003dd8ff 100644 --- a/src/widgets/helper/settingsdialogtab.hpp +++ b/src/widgets/helper/settingsdialogtab.hpp @@ -1,11 +1,14 @@ #pragma once +#include #include #include -#include 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; }; diff --git a/src/widgets/settingsdialog.cpp b/src/widgets/settingsdialog.cpp index 18bf5e07..28b9ece9 100644 --- a/src/widgets/settingsdialog.cpp +++ b/src/widgets/settingsdialog.cpp @@ -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 #include @@ -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 layoutCreator(this); + + // tab pages + layoutCreator.emplace() + .assign(&this->ui.tabContainerContainer) + .emplace() + .withoutMargin() + .assign(&this->ui.tabContainer); + + // right side layout + auto right = layoutCreator.emplace(); + { + right.emplace().assign(&this->ui.pageStack).emplace("NaM"); + + auto buttons = right.emplace(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 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 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::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::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 &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(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 &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::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 fourtf"); - created->setTextFormat(Qt::RichText); - created->setTextInteractionFlags(Qt::TextBrowserInteraction); - created->setOpenExternalLinks(true); - layout->addWidget(created); - - auto github = new QLabel(); - github->setText("Chatterino on Github"); - 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()) { +// // 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 &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 &setting, QStringList items, - std::function &)> 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 &setting, QStringList items, - std::function &)> 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 &setting) -{ - auto widget = new QLineEdit(QString::fromStdString(setting.getValue())); - - QObject::connect(widget, &QLineEdit::textChanged, this, - [&setting](const QString &newValue) { setting = newValue.toStdString(); }); - - return widget; -} +// // if (object->itemAt(i)->widget()->layout() && +// // !object->itemAt(i)->widget()->layout()->isEmpty()) { +// // setChildrensFont(object->itemAt(i)->widget()->layout(), font, indent + +// 2); +// // } +// // } +// // } +//} +///// 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(); diff --git a/src/widgets/settingsdialog.hpp b/src/widgets/settingsdialog.hpp index e2b9e9f0..b06ea4f1 100644 --- a/src/widgets/settingsdialog.hpp +++ b/src/widgets/settingsdialog.hpp @@ -3,6 +3,7 @@ #include "singletons/settingsmanager.hpp" #include "widgets/accountswitchwidget.hpp" #include "widgets/helper/settingsdialogtab.hpp" +#include "widgets/settingspages/appearancepage.hpp" #include #include @@ -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 tabs; - - pajlada::Settings::Setting 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 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 &setting); - QHBoxLayout *createCombobox(const QString &title, pajlada::Settings::Setting &setting, - QStringList items, - std::function &)> cb); - QHBoxLayout *createCombobox( - const QString &title, pajlada::Settings::Setting &setting, QStringList items, - std::function &)> cb); - QLineEdit *createLineEdit(pajlada::Settings::Setting &setting); - void okButtonClicked(); void cancelButtonClicked(); - std::vector managedConnections; - - static void setChildrensFont(QLayout *object, QFont &font, int indent = 0); + // static void setChildrensFont(QLayout *object, QFont &font, int indent = 0); }; } // namespace widgets diff --git a/src/widgets/settingspages/aboutpage.cpp b/src/widgets/settingspages/aboutpage.cpp new file mode 100644 index 00000000..84bd879a --- /dev/null +++ b/src/widgets/settingspages/aboutpage.cpp @@ -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 diff --git a/src/widgets/settingspages/aboutpage.hpp b/src/widgets/settingspages/aboutpage.hpp new file mode 100644 index 00000000..e838bb6c --- /dev/null +++ b/src/widgets/settingspages/aboutpage.hpp @@ -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 diff --git a/src/widgets/settingspages/accountspage.cpp b/src/widgets/settingspages/accountspage.cpp new file mode 100644 index 00000000..0ad7cdc6 --- /dev/null +++ b/src/widgets/settingspages/accountspage.cpp @@ -0,0 +1,46 @@ +#include "accountspage.hpp" + +#include +#include + +#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 layoutCreator(this); + + auto layout = layoutCreator.emplace().withoutMargin(); + auto buttons = layout.emplace(); + { + this->addButton = buttons->addButton("Add", QDialogButtonBox::YesRole); + this->removeButton = buttons->addButton("Remove", QDialogButtonBox::NoRole); + } + + auto accountSwitch = layout.emplace(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 diff --git a/src/widgets/settingspages/accountspage.hpp b/src/widgets/settingspages/accountspage.hpp new file mode 100644 index 00000000..fb553495 --- /dev/null +++ b/src/widgets/settingspages/accountspage.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#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 diff --git a/src/widgets/settingspages/appearancepage.cpp b/src/widgets/settingspages/appearancepage.cpp new file mode 100644 index 00000000..ee7c0a85 --- /dev/null +++ b/src/widgets/settingspages/appearancepage.cpp @@ -0,0 +1,148 @@ +#include "appearancepage.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#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 layoutCreator(this); + auto layout = layoutCreator.emplace().withoutMargin(); + + auto application = + layout.emplace("Application").emplace().withoutMargin(); + { + auto form = application.emplace(); + + // 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("Messages").emplace().withoutMargin(); + { + messages.append(this->createCheckBox("Show timestamp", settings.showTimestamps)); + auto tbox = messages.emplace(); + { + tbox.emplace("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 diff --git a/src/widgets/settingspages/appearancepage.hpp b/src/widgets/settingspages/appearancepage.hpp new file mode 100644 index 00000000..40a5f5cb --- /dev/null +++ b/src/widgets/settingspages/appearancepage.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "widgets/settingspages/settingspage.hpp" + +#include + +namespace chatterino { +namespace widgets { +namespace settingspages { + +class AppearancePage : public SettingsPage +{ +public: + AppearancePage(); + + QLayout *createThemeColorChanger(); + QLayout *createFontChanger(); +}; + +} // namespace settingspages +} // namespace widgets +} // namespace chatterino diff --git a/src/widgets/settingspages/behaviourpage.cpp b/src/widgets/settingspages/behaviourpage.cpp new file mode 100644 index 00000000..c26afc93 --- /dev/null +++ b/src/widgets/settingspages/behaviourpage.cpp @@ -0,0 +1,57 @@ +#include "behaviourpage.hpp" + +#include +#include +#include + +#include + +#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 layoutCreator(this); + + auto form = layoutCreator.emplace().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(newValue) / 99.f; + float newSliderValue = (mul * 2.1f) + 0.1f; + singletons::SettingManager::getInstance().mouseScrollMultiplier = newSliderValue; + }); + + return slider; +} +} // namespace settingspages +} // namespace widgets +} // namespace chatterino diff --git a/src/widgets/settingspages/behaviourpage.hpp b/src/widgets/settingspages/behaviourpage.hpp new file mode 100644 index 00000000..724827d3 --- /dev/null +++ b/src/widgets/settingspages/behaviourpage.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +#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 diff --git a/src/widgets/settingspages/commandpage.cpp b/src/widgets/settingspages/commandpage.cpp new file mode 100644 index 00000000..63a3c13f --- /dev/null +++ b/src/widgets/settingspages/commandpage.cpp @@ -0,0 +1,65 @@ +#include "commandpage.hpp" + +#include +#include + +#include +#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 layoutCreator(this); + auto layout = layoutCreator.emplace(); + + layout.emplace(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 diff --git a/src/widgets/settingspages/commandpage.hpp b/src/widgets/settingspages/commandpage.hpp new file mode 100644 index 00000000..34b67a65 --- /dev/null +++ b/src/widgets/settingspages/commandpage.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#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 diff --git a/src/widgets/settingspages/emotespage.cpp b/src/widgets/settingspages/emotespage.cpp new file mode 100644 index 00000000..5738ec26 --- /dev/null +++ b/src/widgets/settingspages/emotespage.cpp @@ -0,0 +1,27 @@ +#include "emotespage.hpp" + +#include + +namespace chatterino { +namespace widgets { +namespace settingspages { +EmotesPage::EmotesPage() + : SettingsPage("Emotes", ":/images/emote.svg") +{ + singletons::SettingManager &settings = singletons::SettingManager::getInstance(); + util::LayoutCreator layoutCreator(this); + auto layout = layoutCreator.emplace().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 diff --git a/src/widgets/settingspages/emotespage.hpp b/src/widgets/settingspages/emotespage.hpp new file mode 100644 index 00000000..c09ff45f --- /dev/null +++ b/src/widgets/settingspages/emotespage.hpp @@ -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 diff --git a/src/widgets/settingspages/highlightingpage.cpp b/src/widgets/settingspages/highlightingpage.cpp new file mode 100644 index 00000000..e6384bb9 --- /dev/null +++ b/src/widgets/settingspages/highlightingpage.cpp @@ -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 diff --git a/src/widgets/settingspages/highlightingpage.hpp b/src/widgets/settingspages/highlightingpage.hpp new file mode 100644 index 00000000..6aefea09 --- /dev/null +++ b/src/widgets/settingspages/highlightingpage.hpp @@ -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 diff --git a/src/widgets/settingspages/logspage.cpp b/src/widgets/settingspages/logspage.cpp new file mode 100644 index 00000000..606278c4 --- /dev/null +++ b/src/widgets/settingspages/logspage.cpp @@ -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 diff --git a/src/widgets/settingspages/logspage.hpp b/src/widgets/settingspages/logspage.hpp new file mode 100644 index 00000000..f43c13af --- /dev/null +++ b/src/widgets/settingspages/logspage.hpp @@ -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 diff --git a/src/widgets/settingspages/moderationpage.cpp b/src/widgets/settingspages/moderationpage.cpp new file mode 100644 index 00000000..20bbab24 --- /dev/null +++ b/src/widgets/settingspages/moderationpage.cpp @@ -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 diff --git a/src/widgets/settingspages/moderationpage.hpp b/src/widgets/settingspages/moderationpage.hpp new file mode 100644 index 00000000..bce2d7f7 --- /dev/null +++ b/src/widgets/settingspages/moderationpage.hpp @@ -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 diff --git a/src/widgets/settingspages/settingspage.cpp b/src/widgets/settingspages/settingspage.cpp new file mode 100644 index 00000000..7049b961 --- /dev/null +++ b/src/widgets/settingspages/settingspage.cpp @@ -0,0 +1,78 @@ +#include "settingspage.hpp" + +#include + +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 &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 &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 &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 diff --git a/src/widgets/settingspages/settingspage.hpp b/src/widgets/settingspages/settingspage.hpp new file mode 100644 index 00000000..4926f063 --- /dev/null +++ b/src/widgets/settingspages/settingspage.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +#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 &setting); + QComboBox *createComboBox(const QStringList &items, + pajlada::Settings::Setting &setting); + QLineEdit *createLineEdit(pajlada::Settings::Setting &setting); + +protected: + QString name; + QString resourceName; + + pajlada::Signals::NoArgSignal onCancel; + std::vector managedConnections; +}; +} // namespace settingspages +} // namespace widgets +} // namespace chatterino diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 73eb7196..d7900f1e 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -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);