diff --git a/chatterino.pro b/chatterino.pro index b551c9d0..694a3a39 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -195,7 +195,9 @@ HEADERS += \ src/singletons/helper/chatterinosetting.hpp \ src/singletons/resourcemanager.hpp \ src/util/emotemap.hpp \ - src/singletons/helper/ircmessagehandler.hpp + src/singletons/helper/ircmessagehandler.hpp \ + src/util/serialize-custom.hpp \ + src/messages/highlightphrase.hpp PRECOMPILED_HEADER = diff --git a/src/application.cpp b/src/application.cpp index 4a3dde62..5c1e91ac 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -34,8 +34,6 @@ Application::Application() Application::~Application() { this->save(); - - chatterino::singletons::SettingManager::getInstance().save(); } int Application::run(QApplication &qtApp) diff --git a/src/messages/highlightphrase.hpp b/src/messages/highlightphrase.hpp new file mode 100644 index 00000000..f5ea0f29 --- /dev/null +++ b/src/messages/highlightphrase.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include "util/serialize-custom.hpp" + +#include +#include + +namespace chatterino { +namespace messages { + +struct HighlightPhrase { + QString key; + bool sound; + bool alert; + + bool operator==(const HighlightPhrase &rhs) const + { + return std::tie(this->key, this->sound, this->alert) == + std::tie(rhs.key, rhs.sound, rhs.alert); + } +}; +} // namespace messages +} // namespace chatterino + +namespace pajlada { +namespace Settings { + +template <> +struct Serialize { + static rapidjson::Value get(const chatterino::messages::HighlightPhrase &value, + rapidjson::Document::AllocatorType &a) + { + rapidjson::Value ret(rapidjson::kObjectType); + + AddMember(ret, "key", value.key, a); + AddMember(ret, "alert", value.alert, a); + AddMember(ret, "sound", value.sound, a); + + return ret; + } +}; + +template <> +struct Deserialize { + static chatterino::messages::HighlightPhrase get(const rapidjson::Value &value) + { + chatterino::messages::HighlightPhrase ret; + if (!value.IsObject()) { + return ret; + } + + if (value.HasMember("key")) { + const rapidjson::Value &key = value["key"]; + if (key.IsString()) { + ret.key = key.GetString(); + } + } + + if (value.HasMember("alert")) { + const rapidjson::Value &alert = value["alert"]; + if (alert.IsBool()) { + ret.alert = alert.GetBool(); + } + } + + if (value.HasMember("sound")) { + const rapidjson::Value &sound = value["sound"]; + if (sound.IsBool()) { + ret.sound = sound.GetBool(); + } + } + + return ret; + } +}; + +} // namespace Settings +} // namespace pajlada diff --git a/src/singletons/helper/chatterinosetting.hpp b/src/singletons/helper/chatterinosetting.hpp index 05d28d53..54f86433 100644 --- a/src/singletons/helper/chatterinosetting.hpp +++ b/src/singletons/helper/chatterinosetting.hpp @@ -8,6 +8,12 @@ template class ChatterinoSetting : public pajlada::Settings::Setting { public: + ChatterinoSetting(const std::string &_path) + : pajlada::Settings::Setting(_path) + { + _registerSetting(this->data); + } + ChatterinoSetting(const std::string &_path, const Type &_defaultValue) : pajlada::Settings::Setting(_path, _defaultValue) { diff --git a/src/singletons/settingsmanager.cpp b/src/singletons/settingsmanager.cpp index 86d591f6..75cab001 100644 --- a/src/singletons/settingsmanager.cpp +++ b/src/singletons/settingsmanager.cpp @@ -22,8 +22,6 @@ SettingManager::SettingManager() , preferredQuality("/behaviour/streamlink/quality", "Choose") , emoteScale(this->settingsItems, "emoteScale", 1.0) , pathHighlightSound(this->settingsItems, "pathHighlightSound", "qrc:/sounds/ping2.wav") - , highlightProperties(this->settingsItems, "highlightProperties", - QMap>()) , highlightUserBlacklist(this->settingsItems, "highlightUserBlacklist", "") , snapshot(nullptr) , settings(Path::getAppdataPath() + "settings.ini", QSettings::IniFormat) @@ -40,49 +38,6 @@ SettingManager::SettingManager() }; } -void SettingManager::save() -{ - for (auto &item : this->settingsItems) { - if (item.get().getName() != "highlightProperties") { - this->settings.setValue(item.get().getName(), item.get().getVariant()); - } else { - this->settings.beginGroup("Highlights"); - QStringList list = highlightProperties.get().keys(); - list.removeAll(""); - this->settings.remove(""); - for (auto string : list) { - this->settings.beginGroup(string); - this->settings.setValue("highlightSound", - highlightProperties.get().value(string).first); - this->settings.setValue("highlightTask", - highlightProperties.get().value(string).second); - this->settings.endGroup(); - } - this->settings.endGroup(); - } - } -} - -void SettingManager::load() -{ - for (auto &item : this->settingsItems) { - if (item.get().getName() != "highlightProperties") { - item.get().setVariant(this->settings.value(item.get().getName())); - } else { - this->settings.beginGroup("Highlights"); - QStringList list = this->settings.childGroups(); - for (auto string : list) { - this->settings.beginGroup(string); - highlightProperties.insertMap(string, - this->settings.value("highlightSound").toBool(), - this->settings.value("highlightTask").toBool()); - this->settings.endGroup(); - } - this->settings.endGroup(); - } - } -} - Word::Flags SettingManager::getWordTypeMask() { return this->wordTypeMask; @@ -98,6 +53,12 @@ QSettings &SettingManager::getQSettings() return this->settings; } +void SettingManager::load() +{ + // Just to make sure the singleton is initialized + debug::Log("."); +} + void SettingManager::updateWordTypeMask() { uint32_t newMaskUint = Word::Text; @@ -184,5 +145,5 @@ void SettingManager::recallSnapshot() } } +} // namespace singletons } // namespace chatterino -} diff --git a/src/singletons/settingsmanager.hpp b/src/singletons/settingsmanager.hpp index 57e19a34..f381f4bf 100644 --- a/src/singletons/settingsmanager.hpp +++ b/src/singletons/settingsmanager.hpp @@ -1,5 +1,6 @@ #pragma once +#include "messages/highlightphrase.hpp" #include "messages/word.hpp" #include "setting.hpp" #include "singletons/helper/chatterinosetting.hpp" @@ -21,13 +22,12 @@ class SettingManager : public QObject using FloatSetting = ChatterinoSetting; public: - void load(); - void save(); - messages::Word::Flags getWordTypeMask(); bool isIgnoredEmote(const QString &emote); QSettings &getQSettings(); + void load(); + /// Appearance BoolSetting showTimestamps = {"/appearance/messages/showTimestamps", true}; BoolSetting showTimestampSeconds = {"/appearance/messages/showTimestampSeconds", true}; @@ -73,9 +73,10 @@ public: pajlada::Settings::Setting preferredQuality; Setting emoteScale; + ChatterinoSetting> highlightProperties = { + "/highlighting/highlights"}; Setting pathHighlightSound; - Setting>> highlightProperties; Setting highlightUserBlacklist; BoolSetting highlightAlwaysPlaySound = {"/highlighting/alwaysPlaySound", false}; @@ -107,5 +108,5 @@ private: pajlada::Settings::SettingListener wordMaskListener; }; +} // namespace singletons } // namespace chatterino -} diff --git a/src/twitch/twitchmessagebuilder.cpp b/src/twitch/twitchmessagebuilder.cpp index 21da5a8f..852aeba1 100644 --- a/src/twitch/twitchmessagebuilder.cpp +++ b/src/twitch/twitchmessagebuilder.cpp @@ -407,35 +407,18 @@ void TwitchMessageBuilder::parseHighlights() currentPlayerUrl = highlightSoundUrl; } - struct Highlight { - Highlight(const QString &_target, bool _sound, bool _alert) - : target(_target) - , sound(_sound) - , alert(_alert) - { - } - - QString target; - bool sound; - bool alert; - }; - QStringList blackList = settings.highlightUserBlacklist.get().split("\n", QString::SkipEmptyParts); // TODO: This vector should only be rebuilt upon highlights being changed - std::vector activeHighlights; + auto activeHighlights = settings.highlightProperties.getValue(); if (settings.enableHighlightsSelf && currentUsername.size() > 0) { - activeHighlights.emplace_back(currentUsername, settings.enableHighlightSound, - settings.enableHighlightTaskbar); - } - - const auto &highlightProperties = settings.highlightProperties.get(); - - for (auto it = highlightProperties.begin(); it != highlightProperties.end(); ++it) { - auto properties = it.value(); - activeHighlights.emplace_back(it.key(), properties.first, properties.second); + messages::HighlightPhrase selfHighlight; + selfHighlight.key = currentUsername; + selfHighlight.sound = settings.enableHighlightSound; + selfHighlight.alert = settings.enableHighlightTaskbar; + activeHighlights.emplace_back(std::move(selfHighlight)); } bool doHighlight = false; @@ -445,10 +428,10 @@ void TwitchMessageBuilder::parseHighlights() bool hasFocus = (QApplication::focusWidget() != nullptr); if (!blackList.contains(this->ircMessage->nick(), Qt::CaseInsensitive)) { - for (const Highlight &highlight : activeHighlights) { - if (this->originalMessage.contains(highlight.target, Qt::CaseInsensitive)) { + for (const messages::HighlightPhrase &highlight : activeHighlights) { + if (this->originalMessage.contains(highlight.key, Qt::CaseInsensitive)) { debug::Log("Highlight because {} contains {}", this->originalMessage, - highlight.target); + highlight.key); doHighlight = true; if (highlight.sound) { diff --git a/src/util/serialize-custom.hpp b/src/util/serialize-custom.hpp new file mode 100644 index 00000000..bd42bc18 --- /dev/null +++ b/src/util/serialize-custom.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace pajlada { +namespace Settings { + +template <> +struct Serialize { + static rapidjson::Value get(const QString &value, rapidjson::Document::AllocatorType &a) + { + rapidjson::Value ret(qPrintable(value), a); + + return ret; + } +}; + +template <> +struct Deserialize { + static QString get(const rapidjson::Value &value) + { + if (!value.IsString()) { + throw std::runtime_error("Deserialized rapidjson::Value is not a string"); + } + + return value.GetString(); + } +}; + +} // namespace Settings +} // namespace pajlada diff --git a/src/widgets/settingsdialog.cpp b/src/widgets/settingsdialog.cpp index 3132dc18..fc4aa2b7 100644 --- a/src/widgets/settingsdialog.cpp +++ b/src/widgets/settingsdialog.cpp @@ -418,8 +418,10 @@ QVBoxLayout *SettingsDialog::createHighlightingTab() auto highlights = new QListWidget(); auto highlightUserBlacklist = new QTextEdit(); globalHighlights = highlights; - QStringList items = settings.highlightProperties.get().keys(); - highlights->addItems(items); + const auto highlightProperties = settings.highlightProperties.getValue(); + for (const auto &highlightProperty : highlightProperties) { + highlights->addItem(highlightProperty.key); + } highlightUserBlacklist->setText(settings.highlightUserBlacklist.getnonConst()); auto highlightTab = new QTabWidget(); auto customSound = new QHBoxLayout(); @@ -450,6 +452,7 @@ QVBoxLayout *SettingsDialog::createHighlightingTab() 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); @@ -460,11 +463,23 @@ QVBoxLayout *SettingsDialog::createHighlightingTab() 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()) { - highlights->addItem(edit->text()); - settings.highlightProperties.insertMap(edit->text(), sound->isChecked(), - task->isChecked()); + 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(); } }); @@ -475,50 +490,103 @@ QVBoxLayout *SettingsDialog::createHighlightingTab() show->setLayout(box); show->show(); }); + + // Open "Edit selected highlight" dialog QObject::connect(editBtn, &QPushButton::clicked, this, [highlights, this, &settings] { - if (!highlights->selectedItems().isEmpty()) { - auto show = new QWidget(); - auto box = new QBoxLayout(QBoxLayout::TopToBottom); - - auto edit = new QLineEdit(); - edit->setText(highlights->selectedItems().first()->text()); - auto add = new QPushButton("Apply"); - - auto sound = new QCheckBox("Play sound"); - auto task = new QCheckBox("Flash taskbar"); - - QObject::connect(add, &QPushButton::clicked, this, [=, &settings] { - if (edit->text().length()) { - settings.highlightProperties.getnonConst().remove( - highlights->selectedItems().first()->text()); - delete highlights->selectedItems().first(); - highlights->addItem(edit->text()); - settings.highlightProperties.insertMap(edit->text(), sound->isChecked(), - task->isChecked()); - show->close(); - } - }); - box->addWidget(edit); - box->addWidget(add); - box->addWidget(sound); - sound->setChecked(settings.highlightProperties.get() - .value(highlights->selectedItems().first()->text()) - .first); - box->addWidget(task); - task->setChecked(settings.highlightProperties.get() - .value(highlights->selectedItems().first()->text()) - .second); - show->setLayout(box); - show->show(); + 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()) { - settings.highlightProperties.getnonConst().remove( - highlights->selectedItems().first()->text()); - delete highlights->selectedItems().first(); + 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)", @@ -765,13 +833,6 @@ void SettingsDialog::cancelButtonClicked() settings.recallSnapshot(); - QStringList list = settings.highlightProperties.get().keys(); - list.removeDuplicates(); - while (globalHighlights->count() > 0) { - delete globalHighlights->takeItem(0); - } - globalHighlights->addItems(list); - this->close(); }