From ba4173822e1e2d2c7a43ca6603bd4080de478185 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 6 May 2018 00:32:45 +0200 Subject: [PATCH] created base for all the list based settings --- chatterino.pro | 12 +- src/application.cpp | 4 + src/application.hpp | 4 + src/controllers/commands/command.cpp | 8 +- src/controllers/commands/commandmodel.cpp | 12 -- src/controllers/commands/commandmodel.hpp | 6 - .../highlights/highlightcontroller.cpp | 39 +++++ .../highlights/highlightcontroller.hpp | 33 ++++ src/controllers/highlights/highlightmodel.cpp | 91 +++++++++++ src/controllers/highlights/highlightmodel.hpp | 36 +++++ .../highlights/highlightphrase.hpp | 89 +++++++++++ src/messages/highlightphrase.hpp | 142 ++++++++--------- src/providers/twitch/twitchmessagebuilder.cpp | 9 +- src/singletons/settingsmanager.hpp | 5 +- src/util/signalvector2.hpp | 6 + src/util/signalvectormodel.hpp | 144 ++++++++++++++---- src/util/standarditemhelper.hpp | 11 +- src/widgets/helper/editablemodelview.cpp | 75 +++++++++ src/widgets/helper/editablemodelview.hpp | 33 ++++ src/widgets/settingspages/commandpage.cpp | 110 ++----------- .../settingspages/highlightingpage.cpp | 94 ++---------- 21 files changed, 646 insertions(+), 317 deletions(-) create mode 100644 src/controllers/highlights/highlightcontroller.cpp create mode 100644 src/controllers/highlights/highlightcontroller.hpp create mode 100644 src/controllers/highlights/highlightmodel.cpp create mode 100644 src/controllers/highlights/highlightmodel.hpp create mode 100644 src/controllers/highlights/highlightphrase.hpp create mode 100644 src/widgets/helper/editablemodelview.cpp create mode 100644 src/widgets/helper/editablemodelview.hpp diff --git a/chatterino.pro b/chatterino.pro index 1e400b331..0af8bbdfc 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -188,7 +188,10 @@ SOURCES += \ src/util/signalvectormodel.cpp \ src/controllers/commands/command.cpp \ src/controllers/commands/commandmodel.cpp \ - src/controllers/commands/commandcontroller.cpp + src/controllers/commands/commandcontroller.cpp \ + src/controllers/highlights/highlightcontroller.cpp \ + src/controllers/highlights/highlightmodel.cpp \ + src/widgets/helper/editablemodelview.cpp HEADERS += \ src/precompiled_header.hpp \ @@ -196,7 +199,6 @@ HEADERS += \ src/const.hpp \ src/debug/log.hpp \ src/emojis.hpp \ - src/messages/highlightphrase.hpp \ src/messages/image.hpp \ src/messages/layouts/messagelayout.hpp \ src/messages/layouts/messagelayoutcontainer.hpp \ @@ -324,7 +326,11 @@ HEADERS += \ src/util/signalvectormodel.hpp \ src/controllers/commands/command.hpp \ src/controllers/commands/commandmodel.hpp \ - src/controllers/commands/commandcontroller.hpp + src/controllers/commands/commandcontroller.hpp \ + src/controllers/highlights/highlightcontroller.hpp \ + src/controllers/highlights/highlightphrase.hpp \ + src/controllers/highlights/highlightmodel.hpp \ + src/widgets/helper/editablemodelview.hpp RESOURCES += \ resources/resources.qrc diff --git a/src/application.cpp b/src/application.cpp index e2b8ce1f4..a8312ca42 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,6 +1,7 @@ #include "application.hpp" #include "controllers/commands/commandcontroller.hpp" +#include "controllers/highlights/highlightcontroller.hpp" #include "providers/twitch/pubsub.hpp" #include "providers/twitch/twitchserver.hpp" #include "singletons/accountmanager.hpp" @@ -64,6 +65,7 @@ void Application::construct() this->windows = new singletons::WindowManager; this->logging = new singletons::LoggingManager; this->commands = new controllers::commands::CommandController; + this->highlights = new controllers::highlights::HighlightController; this->accounts = new singletons::AccountManager; this->emotes = new singletons::EmoteManager; this->settings = new singletons::SettingManager; @@ -95,6 +97,8 @@ void Application::initialize() this->settings->load(); this->commands->load(); + this->highlights->initialize(); + this->emotes->loadGlobalEmotes(); this->accounts->load(); diff --git a/src/application.hpp b/src/application.hpp index 55fe778bf..2f23a50a0 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -20,6 +20,9 @@ namespace controllers { namespace commands { class CommandController; } +namespace highlights { +class HighlightController; +} } namespace singletons { @@ -59,6 +62,7 @@ public: singletons::WindowManager *windows = nullptr; singletons::LoggingManager *logging = nullptr; controllers::commands::CommandController *commands = nullptr; + controllers::highlights::HighlightController *highlights = nullptr; singletons::AccountManager *accounts = nullptr; singletons::EmoteManager *emotes = nullptr; singletons::NativeMessagingManager *nativeMessaging = nullptr; diff --git a/src/controllers/commands/command.cpp b/src/controllers/commands/command.cpp index f66d1d9d4..4ba5d3e80 100644 --- a/src/controllers/commands/command.cpp +++ b/src/controllers/commands/command.cpp @@ -14,13 +14,13 @@ Command::Command(const QString &_text) return; } - this->name = _text.mid(0, index); - this->func = _text.mid(index + 1); + this->name = _text.mid(0, index).trimmed(); + this->func = _text.mid(index + 1).trimmed(); } Command::Command(const QString &_name, const QString &_func) - : name(_name) - , func(_func) + : name(_name.trimmed()) + , func(_func.trimmed()) { } diff --git a/src/controllers/commands/commandmodel.cpp b/src/controllers/commands/commandmodel.cpp index 15603d4c3..d87ffacfc 100644 --- a/src/controllers/commands/commandmodel.cpp +++ b/src/controllers/commands/commandmodel.cpp @@ -25,18 +25,6 @@ void CommandModel::getRowFromItem(const Command &item, std::vectorsetFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); } -// returns the related index of the SignalVector -int CommandModel::getVectorIndexFromModelIndex(int index) -{ - return index; -} - -// returns the related index of the model -int CommandModel::getModelIndexFromVectorIndex(int index) -{ - return index; -} - } // namespace commands } // namespace controllers } // namespace chatterino diff --git a/src/controllers/commands/commandmodel.hpp b/src/controllers/commands/commandmodel.hpp index 07c1c5e9e..22931c5b5 100644 --- a/src/controllers/commands/commandmodel.hpp +++ b/src/controllers/commands/commandmodel.hpp @@ -22,12 +22,6 @@ protected: // turns a row in the model into a vector item virtual void getRowFromItem(const Command &item, std::vector &row) override; - // returns the related index of the SignalVector - virtual int getVectorIndexFromModelIndex(int index) override; - - // returns the related index of the model - virtual int getModelIndexFromVectorIndex(int index) override; - friend class CommandController; }; diff --git a/src/controllers/highlights/highlightcontroller.cpp b/src/controllers/highlights/highlightcontroller.cpp new file mode 100644 index 000000000..cab3cbbeb --- /dev/null +++ b/src/controllers/highlights/highlightcontroller.cpp @@ -0,0 +1,39 @@ +#include "highlightcontroller.hpp" + +#include "application.hpp" +#include "controllers/highlights/highlightmodel.hpp" + +namespace chatterino { +namespace controllers { +namespace highlights { + +HighlightController::HighlightController() +{ +} + +void HighlightController::initialize() +{ + assert(!this->initialized); + this->initialized = true; + + for (const HighlightPhrase &phrase : this->highlightsSetting.getValue()) { + this->phrases.appendItem(phrase); + } + + this->phrases.delayedItemsChanged.connect([this] { // + int xd = this->phrases.getVector().size(); + this->highlightsSetting.setValue(this->phrases.getVector()); + }); +} + +HighlightModel *HighlightController::createModel(QObject *parent) +{ + HighlightModel *model = new HighlightModel(parent); + model->init(&this->phrases); + + return model; +} + +} // namespace highlights +} // namespace controllers +} // namespace chatterino diff --git a/src/controllers/highlights/highlightcontroller.hpp b/src/controllers/highlights/highlightcontroller.hpp new file mode 100644 index 000000000..98ad843d2 --- /dev/null +++ b/src/controllers/highlights/highlightcontroller.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "controllers/highlights/highlightphrase.hpp" +#include "singletons/settingsmanager.hpp" +#include "util/signalvector2.hpp" + +namespace chatterino { +namespace controllers { +namespace highlights { + +class HighlightModel; + +class HighlightController +{ +public: + HighlightController(); + + void initialize(); + + util::UnsortedSignalVector phrases; + + HighlightModel *createModel(QObject *parent); + +private: + bool initialized = false; + + singletons::ChatterinoSetting> highlightsSetting = { + "/highlighting/highlights"}; +}; + +} // namespace highlights +} // namespace controllers +} // namespace chatterino diff --git a/src/controllers/highlights/highlightmodel.cpp b/src/controllers/highlights/highlightmodel.cpp new file mode 100644 index 000000000..39ab5f574 --- /dev/null +++ b/src/controllers/highlights/highlightmodel.cpp @@ -0,0 +1,91 @@ +#include "highlightmodel.hpp" + +#include "application.hpp" +#include "singletons/settingsmanager.hpp" +#include "util/standarditemhelper.hpp" + +namespace chatterino { +namespace controllers { +namespace highlights { + +// commandmodel +HighlightModel::HighlightModel(QObject *parent) + : util::SignalVectorModel(4, parent) +{ + // auto app = getApp(); + + // std::vector row = this->createRow(); + + // util::setBoolItem(row[0], app->settings->enableHighlightsSelf.getValue(), true, false); + // util::setBoolItem(row[1], app->settings->enableHighlightsSelf.getValue(), true, false); + // util::setBoolItem(row[2], app->settings->enableHighlightsSelf.getValue(), true, false); + // row[0]->setData("Your name (automatic)", Qt::DisplayRole); + + // this->insertCustomRow(row, 0); +} + +// app->settings->highlightProperties.setValue(phrases); +// app->settings->enableHighlightsSelf.setValue( +// model->item(0, 0)->data(Qt::CheckStateRole).toBool()); +// app->settings->enableHighlightTaskbar.setValue( +// model->item(0, 1)->data(Qt::CheckStateRole).toBool()); +// app->settings->enableHighlightSound.setValue( +// model->item(0, 2)->data(Qt::CheckStateRole).toBool()); + +// turn a vector item into a model row +HighlightPhrase HighlightModel::getItemFromRow(std::vector &row) +{ + // key, alert, sound, regex + + return HighlightPhrase{ + row[0]->data(Qt::DisplayRole).toString(), row[1]->data(Qt::CheckStateRole).toBool(), + row[2]->data(Qt::CheckStateRole).toBool(), row[3]->data(Qt::CheckStateRole).toBool()}; +} + +// turns a row in the model into a vector item +void HighlightModel::getRowFromItem(const HighlightPhrase &item, std::vector &row) +{ + util::setStringItem(row[0], item.key); + util::setBoolItem(row[1], item.alert); + util::setBoolItem(row[2], item.sound); + util::setBoolItem(row[3], item.regex); +} + +void HighlightModel::afterInit() +{ + std::vector row = this->createRow(); + util::setBoolItem(row[0], getApp()->settings->enableHighlightsSelf.getValue(), true, false); + row[0]->setData("Your username (automatic)", Qt::DisplayRole); + util::setBoolItem(row[1], getApp()->settings->enableHighlightTaskbar.getValue(), true, false); + util::setBoolItem(row[2], getApp()->settings->enableHighlightSound.getValue(), true, false); + this->insertCustomRow(row, 0); +} + +void HighlightModel::customRowSetData(const std::vector &row, int column, + const QVariant &value, int role) +{ + switch (column) { + case 0: { + if (role == Qt::CheckStateRole) { + getApp()->settings->enableHighlightsSelf.setValue(value.toBool()); + } + } break; + case 1: { + if (role == Qt::CheckStateRole) { + getApp()->settings->enableHighlightTaskbar.setValue(value.toBool()); + } + } break; + case 2: { + if (role == Qt::CheckStateRole) { + getApp()->settings->enableHighlightSound.setValue(value.toBool()); + } + } break; + case 3: { + // empty element + } break; + } +} + +} // namespace highlights +} // namespace controllers +} // namespace chatterino diff --git a/src/controllers/highlights/highlightmodel.hpp b/src/controllers/highlights/highlightmodel.hpp new file mode 100644 index 000000000..f4a24fb96 --- /dev/null +++ b/src/controllers/highlights/highlightmodel.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "controllers/highlights/highlightphrase.hpp" +#include "util/signalvectormodel.hpp" + +namespace chatterino { +namespace controllers { +namespace highlights { + +class HighlightController; + +class HighlightModel : public util::SignalVectorModel +{ + explicit HighlightModel(QObject *parent); + +protected: + // turn a vector item into a model row + virtual HighlightPhrase getItemFromRow(std::vector &row) override; + + // turns a row in the model into a vector item + virtual void getRowFromItem(const HighlightPhrase &item, + std::vector &row) override; + + virtual void afterInit() override; + + virtual void customRowSetData(const std::vector &row, int column, + const QVariant &value, int role) override; + + friend class HighlightController; +}; + +} // namespace highlights +} // namespace controllers +} // namespace chatterino diff --git a/src/controllers/highlights/highlightphrase.hpp b/src/controllers/highlights/highlightphrase.hpp new file mode 100644 index 000000000..3a39acb45 --- /dev/null +++ b/src/controllers/highlights/highlightphrase.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include "util/serialize-custom.hpp" + +#include +#include + +namespace chatterino { +namespace controllers { +namespace highlights { + +struct HighlightPhrase { + QString key; + bool alert; + bool sound; + bool regex; + + bool operator==(const HighlightPhrase &other) const + { + return std::tie(this->key, this->sound, this->alert, this->regex) == + std::tie(other.key, other.sound, other.alert, other.regex); + } +}; +} // namespace highlights +} // namespace controllers +} // namespace chatterino + +namespace pajlada { +namespace Settings { + +template <> +struct Serialize { + static rapidjson::Value get(const chatterino::controllers::highlights::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); + AddMember(ret, "regex", value.regex, a); + + return ret; + } +}; + +template <> +struct Deserialize { + static chatterino::controllers::highlights::HighlightPhrase get(const rapidjson::Value &value) + { + chatterino::controllers::highlights::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(); + } + } + + if (value.HasMember("regex")) { + const rapidjson::Value ®ex = value["regex"]; + if (regex.IsBool()) { + ret.regex = regex.GetBool(); + } + } + + return ret; + } +}; + +} // namespace Settings +} // namespace pajlada diff --git a/src/messages/highlightphrase.hpp b/src/messages/highlightphrase.hpp index 93721b1c8..6b39454bd 100644 --- a/src/messages/highlightphrase.hpp +++ b/src/messages/highlightphrase.hpp @@ -1,87 +1,87 @@ -#pragma once +//#pragma once -#include "util/serialize-custom.hpp" +//#include "util/serialize-custom.hpp" -#include -#include +//#include +//#include -namespace chatterino { -namespace messages { +//namespace chatterino { +//namespace messages { -struct HighlightPhrase { - QString key; - bool alert; - bool sound; - bool regex; +//struct HighlightPhrase { +// QString key; +// bool alert; +// bool sound; +// bool regex; - bool operator==(const HighlightPhrase &other) const - { - return std::tie(this->key, this->sound, this->alert, this->regex) == - std::tie(other.key, other.sound, other.alert, other.regex); - } -}; -} // namespace messages -} // namespace chatterino +// bool operator==(const HighlightPhrase &other) const +// { +// return std::tie(this->key, this->sound, this->alert, this->regex) == +// std::tie(other.key, other.sound, other.alert, other.regex); +// } +//}; +//} // namespace messages +//} // namespace chatterino -namespace pajlada { -namespace Settings { +//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); +//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); - AddMember(ret, "regex", value.regex, a); +// AddMember(ret, "key", value.key, a); +// AddMember(ret, "alert", value.alert, a); +// AddMember(ret, "sound", value.sound, a); +// AddMember(ret, "regex", value.regex, a); - return ret; - } -}; +// return ret; +// } +//}; -template <> -struct Deserialize { - static chatterino::messages::HighlightPhrase get(const rapidjson::Value &value) - { - chatterino::messages::HighlightPhrase ret; - if (!value.IsObject()) { - 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("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("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(); - } - } +// if (value.HasMember("sound")) { +// const rapidjson::Value &sound = value["sound"]; +// if (sound.IsBool()) { +// ret.sound = sound.GetBool(); +// } +// } - if (value.HasMember("regex")) { - const rapidjson::Value ®ex = value["regex"]; - if (regex.IsBool()) { - ret.regex = regex.GetBool(); - } - } +// if (value.HasMember("regex")) { +// const rapidjson::Value ®ex = value["regex"]; +// if (regex.IsBool()) { +// ret.regex = regex.GetBool(); +// } +// } - return ret; - } -}; +// return ret; +// } +//}; -} // namespace Settings -} // namespace pajlada +//} // namespace Settings +//} // namespace pajlada diff --git a/src/providers/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp index bf1f26938..61612f0eb 100644 --- a/src/providers/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -1,6 +1,7 @@ #include "providers/twitch/twitchmessagebuilder.hpp" #include "application.hpp" +#include "controllers/highlights/highlightcontroller.hpp" #include "debug/log.hpp" #include "providers/twitch/twitchchannel.hpp" #include "singletons/accountmanager.hpp" @@ -398,10 +399,12 @@ void TwitchMessageBuilder::parseHighlights() app->settings->highlightUserBlacklist.getValue().split("\n", QString::SkipEmptyParts); // TODO: This vector should only be rebuilt upon highlights being changed - auto activeHighlights = app->settings->highlightProperties.getValue(); + // fourtf: should be implemented in the HighlightsController + std::vector activeHighlights = + app->highlights->phrases.getVector(); if (app->settings->enableHighlightsSelf && currentUsername.size() > 0) { - messages::HighlightPhrase selfHighlight; + controllers::highlights::HighlightPhrase selfHighlight; selfHighlight.key = currentUsername; selfHighlight.sound = app->settings->enableHighlightSound; selfHighlight.alert = app->settings->enableHighlightTaskbar; @@ -415,7 +418,7 @@ void TwitchMessageBuilder::parseHighlights() bool hasFocus = (QApplication::focusWidget() != nullptr); if (!blackList.contains(this->ircMessage->nick(), Qt::CaseInsensitive)) { - for (const messages::HighlightPhrase &highlight : activeHighlights) { + for (const controllers::highlights::HighlightPhrase &highlight : activeHighlights) { int index = -1; while ((index = this->originalMessage.indexOf(highlight.key, index + 1, diff --git a/src/singletons/settingsmanager.hpp b/src/singletons/settingsmanager.hpp index 45fa4fd03..3df59463f 100644 --- a/src/singletons/settingsmanager.hpp +++ b/src/singletons/settingsmanager.hpp @@ -1,6 +1,6 @@ #pragma once -#include "messages/highlightphrase.hpp" +#include "controllers/highlights/highlightphrase.hpp" #include "messages/messageelement.hpp" #include "singletons/helper/chatterinosetting.hpp" #include "singletons/helper/moderationaction.hpp" @@ -104,9 +104,6 @@ public: /// Logging BoolSetting enableLogging = {"/logging/enabled", false}; - ChatterinoSetting> highlightProperties = { - "/highlighting/highlights"}; - QStringSetting pathHighlightSound = {"/highlighting/highlightSoundPath", "qrc:/sounds/ping2.wav"}; QStringSetting highlightUserBlacklist = {"/highlighting/blacklistedUsers", ""}; diff --git a/src/util/signalvector2.hpp b/src/util/signalvector2.hpp index 2d7617a96..22d1e8308 100644 --- a/src/util/signalvector2.hpp +++ b/src/util/signalvector2.hpp @@ -19,6 +19,8 @@ public: { QObject::connect(&this->itemsChangedTimer, &QTimer::timeout, [this] { this->delayedItemsChanged.invoke(); }); + this->itemsChangedTimer.setInterval(100); + this->itemsChangedTimer.setSingleShot(true); } virtual ~ReadOnlySignalVector() = default; @@ -69,6 +71,8 @@ public: this->vector.erase(this->vector.begin() + index); typename ReadOnlySignalVector::ItemArgs args{item, index, caller}; this->itemRemoved.invoke(args); + + this->invokeDelayedItemsChanged(); } int appendItem(const TVectorItem &item, void *caller = 0) @@ -94,6 +98,7 @@ public: typename ReadOnlySignalVector::ItemArgs args{item, index, caller}; this->itemInserted.invoke(args); + this->invokeDelayedItemsChanged(); return index; } }; @@ -111,6 +116,7 @@ public: this->vector.begin(); typename ReadOnlySignalVector::ItemArgs args{item, index, caller}; this->itemInserted.invoke(args); + this->invokeDelayedItemsChanged(); return index; } }; diff --git a/src/util/signalvectormodel.hpp b/src/util/signalvectormodel.hpp index 2590991f3..7da0637ed 100644 --- a/src/util/signalvectormodel.hpp +++ b/src/util/signalvectormodel.hpp @@ -32,20 +32,18 @@ public: } // get row index - int row = this->getModelIndexFromVectorIndex(args.index); - assert(row >= 0 && row <= this->rows.size()); + int index = this->getModelIndexFromVectorIndex(args.index); + assert(index >= 0 && index <= this->rows.size()); // get row items - std::vector items; - for (int i = 0; i < this->_columnCount; i++) { - items.push_back(new QStandardItem()); - } - - this->getRowFromItem(args.item, items); + std::vector row = this->createRow(); + this->getRowFromItem(args.item, row); // insert row - this->beginInsertRows(QModelIndex(), row, row); - this->rows.insert(this->rows.begin() + row, Row(items)); + index = this->beforeInsert(args.item, row, index); + + this->beginInsertRows(QModelIndex(), index, index); + this->rows.insert(this->rows.begin() + index, Row(row)); this->endInsertRows(); }; @@ -74,6 +72,8 @@ public: this->rows.erase(this->rows.begin() + row); this->endRemoveRows(); }); + + this->afterInit(); } virtual ~SignalVectorModel() @@ -108,12 +108,18 @@ public: int row = index.row(), column = index.column(); assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount); - this->rows[row].items[column]->setData(value, role); + Row &rowItem = this->rows[row]; - int vecRow = this->getVectorIndexFromModelIndex(row); - this->vector->removeItem(vecRow, this); - TVectorItem item = this->getItemFromRow(this->rows[row].items); - this->vector->insertItem(item, vecRow, this); + rowItem.items[column]->setData(value, role); + + if (rowItem.isCustomRow) { + this->customRowSetData(rowItem.items, column, value, role); + } else { + int vecRow = this->getVectorIndexFromModelIndex(row); + this->vector->removeItem(vecRow, this); + TVectorItem item = this->getItemFromRow(this->rows[row].items); + this->vector->insertItem(item, vecRow, this); + } return true; } @@ -140,6 +146,8 @@ public: } this->_headerData[section][role] = value; + + emit this->headerDataChanged(Qt::Horizontal, section, section); return true; } @@ -151,51 +159,133 @@ public: return this->rows[index.row()].items[index.column()]->flags(); } - virtual QStandardItem *getItem(int row, int column) + QStandardItem *getItem(int row, int column) { assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount); return rows[row].items[column]; } - void removeRow(int row) + void deleteRow(int row) { - assert(row >= 0 && row <= this->rows.size()); - int signalVectorRow = this->getVectorIndexFromModelIndex(row); this->vector->removeItem(signalVectorRow); } + virtual bool removeRows(int row, int count, const QModelIndex &parent) override + { + if (count != 1) { + return false; + } + + assert(row >= 0 && row < this->rows.size()); + + int signalVectorRow = this->getVectorIndexFromModelIndex(row); + this->vector->removeItem(signalVectorRow); + + return true; + } + protected: + virtual void afterInit() + { + } + // turn a vector item into a model row virtual TVectorItem getItemFromRow(std::vector &row) = 0; // turns a row in the model into a vector item virtual void getRowFromItem(const TVectorItem &item, std::vector &row) = 0; - // returns the related index of the SignalVector - virtual int getVectorIndexFromModelIndex(int index) = 0; + virtual int beforeInsert(const TVectorItem &item, std::vector &row, + int proposedIndex) + { + return proposedIndex; + } - // returns the related index of the model - virtual int getModelIndexFromVectorIndex(int index) = 0; + virtual void afterRemoved(const TVectorItem &item, std::vector &row, int index) + { + } + + virtual void customRowSetData(const std::vector &row, int column, + const QVariant &value, int role) + { + } + + void insertCustomRow(std::vector row, int index) + { + assert(index >= 0 && index <= this->rows.size()); + + this->beginInsertRows(QModelIndex(), index, index); + this->rows.insert(this->rows.begin() + index, Row(std::move(row), true)); + this->endInsertRows(); + } + + std::vector createRow() + { + std::vector row; + for (int i = 0; i < this->_columnCount; i++) { + row.push_back(new QStandardItem()); + } + return row; + } -private: struct Row { std::vector items; bool isCustomRow; - Row(const std::vector _items, bool _isCustomRow = false) - : items(_items) + Row(std::vector _items, bool _isCustomRow = false) + : items(std::move(_items)) , isCustomRow(_isCustomRow) { } }; - std::vector rows; + +private: std::vector> _headerData; BaseSignalVector *vector; int _columnCount; + + // returns the related index of the SignalVector + int getVectorIndexFromModelIndex(int index) + { + int i = 0; + + for (auto &row : this->rows) { + if (row.isCustomRow) { + index--; + continue; + } + + if (i == index) { + return i; + } + i++; + } + + return i; + } + + // returns the related index of the model + int getModelIndexFromVectorIndex(int index) + { + int i = 0; + + for (auto &row : this->rows) { + if (row.isCustomRow) { + index++; + } + + if (i == index) { + return i; + } + i++; + } + + return i; + } }; } // namespace util diff --git a/src/util/standarditemhelper.hpp b/src/util/standarditemhelper.hpp index dd315609a..42535d8f1 100644 --- a/src/util/standarditemhelper.hpp +++ b/src/util/standarditemhelper.hpp @@ -5,21 +5,20 @@ namespace chatterino { namespace util { -static QStandardItem *boolItem(bool value, bool userCheckable = true, bool selectable = true) +static void setBoolItem(QStandardItem *item, bool value, bool userCheckable = true, + bool selectable = true) { - auto *item = new QStandardItem(); item->setFlags((Qt::ItemFlags)(Qt::ItemIsEnabled | (selectable ? Qt::ItemIsSelectable : 0) | (userCheckable ? Qt::ItemIsUserCheckable : 0))); item->setCheckState(value ? Qt::Checked : Qt::Unchecked); - return item; } -static QStandardItem *stringItem(const QString &value, bool editable = true, bool selectable = true) +static void setStringItem(QStandardItem *item, const QString &value, bool editable = true, + bool selectable = true) { - auto *item = new QStandardItem(value); + item->setData(value, Qt::EditRole); item->setFlags((Qt::ItemFlags)(Qt::ItemIsEnabled | (selectable ? Qt::ItemIsSelectable : 0) | (editable ? (Qt::ItemIsEditable) : 0))); - return item; } static QStandardItem *emptyItem() diff --git a/src/widgets/helper/editablemodelview.cpp b/src/widgets/helper/editablemodelview.cpp new file mode 100644 index 000000000..774165aa6 --- /dev/null +++ b/src/widgets/helper/editablemodelview.cpp @@ -0,0 +1,75 @@ +#include "editablemodelview.hpp" + +#include +#include +#include +#include +#include +#include + +namespace chatterino { +namespace widgets { +namespace helper { + +EditableModelView::EditableModelView(QAbstractTableModel *_model) + : tableView(new QTableView(this)) + , model(_model) +{ + this->model->setParent(this); + this->tableView->setModel(_model); + this->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection); + this->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + this->tableView->verticalHeader()->hide(); + + // create layout + QVBoxLayout *vbox = new QVBoxLayout(this); + vbox->addWidget(this->tableView); + + // create button layout + QHBoxLayout *buttons = new QHBoxLayout(this); + vbox->addLayout(buttons); + + // add + QPushButton *add = new QPushButton("Add"); + buttons->addWidget(add); + QObject::connect(add, &QPushButton::clicked, [this] { this->addButtonPressed.invoke(); }); + + // remove + QPushButton *remove = new QPushButton("Remove"); + buttons->addWidget(remove); + QObject::connect(remove, &QPushButton::clicked, [this] { + QModelIndexList list; + while ((list = this->getTableView()->selectionModel()->selectedRows(0)).length() > 0) { + model->removeRow(list[0].row()); + } + }); + + // finish button layout + buttons->addStretch(1); +} + +void EditableModelView::setTitles(std::initializer_list titles) +{ + int i = 0; + for (const QString &title : titles) { + if (this->model->columnCount() == i) { + break; + } + + this->model->setHeaderData(i++, Qt::Horizontal, title, Qt::DisplayRole); + } +} + +QTableView *EditableModelView::getTableView() +{ + return this->tableView; +} + +QAbstractTableModel *EditableModelView::getModel() +{ + return this->model; +} + +} // namespace helper +} // namespace widgets +} // namespace chatterino diff --git a/src/widgets/helper/editablemodelview.hpp b/src/widgets/helper/editablemodelview.hpp new file mode 100644 index 000000000..9553c8538 --- /dev/null +++ b/src/widgets/helper/editablemodelview.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include + +class QAbstractTableModel; +class QTableView; + +namespace chatterino { +namespace widgets { +namespace helper { + +class EditableModelView : public QWidget +{ +public: + EditableModelView(QAbstractTableModel *model); + + void setTitles(std::initializer_list titles); + + QTableView *getTableView(); + QAbstractTableModel *getModel(); + + pajlada::Signals::NoArgSignal addButtonPressed; + +private: + QTableView *tableView; + QAbstractTableModel *model; +}; + +} // namespace helper +} // namespace widgets +} // namespace chatterino diff --git a/src/widgets/settingspages/commandpage.cpp b/src/widgets/settingspages/commandpage.cpp index 2864d8184..15b6f5548 100644 --- a/src/widgets/settingspages/commandpage.cpp +++ b/src/widgets/settingspages/commandpage.cpp @@ -1,5 +1,6 @@ #include "commandpage.hpp" +#include #include #include #include @@ -11,6 +12,7 @@ #include "controllers/commands/commandmodel.hpp" #include "util/layoutcreator.hpp" #include "util/standarditemhelper.hpp" +#include "widgets/helper/editablemodelview.hpp" //#include "widgets/helper/comboboxitemdelegate.hpp" #include @@ -34,107 +36,15 @@ CommandPage::CommandPage() util::LayoutCreator layoutCreator(this); auto layout = layoutCreator.emplace().withoutMargin(); - QTableView *view = *layout.emplace(); + helper::EditableModelView *view = + *layout.emplace(app->commands->createModel(nullptr)); - auto *model = app->commands->createModel(view); - view->setModel(model); - model->setHeaderData(0, Qt::Horizontal, "Trigger"); - model->setHeaderData(1, Qt::Horizontal, "Command"); - view->setSelectionMode(QAbstractItemView::ExtendedSelection); - view->setSelectionBehavior(QAbstractItemView::SelectRows); - view->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); - view->verticalHeader()->hide(); - - auto buttons = layout.emplace().withoutMargin(); - { - auto add = buttons.emplace("Add"); - QObject::connect(*add, &QPushButton::clicked, [model, view] { - getApp()->commands->items.appendItem( - controllers::commands::Command{"/command", "I made a new command HeyGuys"}); - view->scrollToBottom(); - }); - - auto remove = buttons.emplace("Remove"); - QObject::connect(*remove, &QPushButton::clicked, [view, model] { - std::vector indices; - - for (const QModelIndex &index : view->selectionModel()->selectedRows(0)) { - indices.push_back(index.row()); - } - - std::sort(indices.begin(), indices.end()); - - for (int i = indices.size() - 1; i >= 0; i--) { - model->removeRow(indices[i]); - } - }); - buttons->addStretch(1); - } - - // QTableView *view = *layout.emplace(); - // QStandardItemModel *model = new QStandardItemModel(0, 2, view); - - // view->setModel(model); - // model->setHeaderData(0, Qt::Horizontal, "Trigger"); - // model->setHeaderData(1, Qt::Horizontal, "Command"); - // view->setSelectionMode(QAbstractItemView::ExtendedSelection); - // view->setSelectionBehavior(QAbstractItemView::SelectRows); - // view->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); - - // for (const QString &string : app->commands->getCommands()) { - // int index = string.indexOf(' '); - // if (index == -1) { - // model->appendRow({util::stringItem(string), util::stringItem("")}); - // } else { - // model->appendRow( - // {util::stringItem(string.mid(0, index)), util::stringItem(string.mid(index + - // 1))}); - // } - // } - - // QObject::connect( - // model, &QStandardItemModel::dataChanged, - // [model](const QModelIndex &topLeft, const QModelIndex &bottomRight, - // const QVector &roles) { - // QStringList list; - - // for (int i = 0; i < model->rowCount(); i++) { - // QString command = model->item(i, 0)->data(Qt::EditRole).toString(); - // // int index = command.indexOf(' '); - // // if (index != -1) { - // // command = command.mid(index); - // // } - - // list.append(command + " " + model->item(i, 1)->data(Qt::EditRole).toString()); - // } - - // getApp()->commands->setCommands(list); - // }); - - // auto buttons = layout.emplace().withoutMargin(); - // { - // auto add = buttons.emplace("Add"); - // QObject::connect(*add, &QPushButton::clicked, [model, view] { - // model->appendRow({util::stringItem("/command"), util::stringItem("")}); - // view->scrollToBottom(); - // }); - - // auto remove = buttons.emplace("Remove"); - // QObject::connect(*remove, &QPushButton::clicked, [view, model] { - // std::vector indices; - - // for (const QModelIndex &index : view->selectionModel()->selectedRows(0)) { - // indices.push_back(index.row()); - // } - - // std::sort(indices.begin(), indices.end()); - - // for (int i = indices.size() - 1; i >= 0; i--) { - // model->removeRow(indices[i]); - // } - // }); - // buttons->addStretch(1); - // } + view->setTitles({"Trigger", "Command"}); + view->getTableView()->horizontalHeader()->setStretchLastSection(true); + view->addButtonPressed.connect([] { + getApp()->commands->items.appendItem( + controllers::commands::Command{"/command", "I made a new command HeyGuys"}); + }); layout.append(this->createCheckBox("Also match the trigger at the end of the message", app->settings->allowCommandsAtEnd)); diff --git a/src/widgets/settingspages/highlightingpage.cpp b/src/widgets/settingspages/highlightingpage.cpp index 82eaa1163..1b485117c 100644 --- a/src/widgets/settingspages/highlightingpage.cpp +++ b/src/widgets/settingspages/highlightingpage.cpp @@ -1,10 +1,13 @@ #include "highlightingpage.hpp" #include "application.hpp" +#include "controllers/highlights/highlightcontroller.hpp" +#include "controllers/highlights/highlightmodel.hpp" #include "debug/log.hpp" #include "singletons/settingsmanager.hpp" #include "util/layoutcreator.hpp" #include "util/standarditemhelper.hpp" +#include "widgets/helper/editablemodelview.hpp" #include #include @@ -41,95 +44,24 @@ HighlightingPage::HighlightingPage() // HIGHLIGHTS auto highlights = tabs.appendTab(new QVBoxLayout, "Highlights"); { - QTableView *view = *highlights.emplace(); - auto *model = new QStandardItemModel(0, 4, view); - model->setHeaderData(0, Qt::Horizontal, "Pattern"); - model->setHeaderData(1, Qt::Horizontal, "Flash taskbar"); - model->setHeaderData(2, Qt::Horizontal, "Play sound"); - model->setHeaderData(3, Qt::Horizontal, "Regex"); - view->setModel(model); - view->setSelectionMode(QAbstractItemView::ExtendedSelection); - view->setSelectionBehavior(QAbstractItemView::SelectRows); - view->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); + helper::EditableModelView *view = *highlights.emplace( + app->highlights->createModel(nullptr)); - // own name - auto *yourName = util::stringItem("Your name (automatic)", false, false); - yourName->setData(QBrush("#666"), Qt::ForegroundRole); - yourName->setFlags(yourName->flags() | Qt::ItemIsUserCheckable | - Qt::ItemIsUserCheckable); - yourName->setData(app->settings->enableHighlightsSelf ? 2 : 0, Qt::CheckStateRole); - model->appendRow( - {yourName, - util::boolItem(app->settings->enableHighlightTaskbar.getValue(), true, false), - util::boolItem(app->settings->enableHighlightSound.getValue(), true, false), - util::emptyItem()}); + view->getTableView()->hideColumn(3); - // highlight phrases - // fourtf: could crash - for (const messages::HighlightPhrase &phrase : - app->settings->highlightProperties.getValue()) { - model->appendRow({util::stringItem(phrase.key), util::boolItem(phrase.alert), - util::boolItem(phrase.sound), util::boolItem(phrase.regex)}); - } + view->setTitles({"Pattern", "Flash taskbar", "Play sound", "Regex"}); + view->getTableView()->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); // fourtf: make class extrend BaseWidget and add this to dpiChanged QTimer::singleShot(1, [view] { - view->resizeColumnsToContents(); - view->setColumnWidth(0, 250); + view->getTableView()->resizeColumnsToContents(); + view->getTableView()->setColumnWidth(0, 250); }); - auto buttons = highlights.emplace().withoutMargin(); - - QObject::connect( - model, &QStandardItemModel::dataChanged, - [model, app](const QModelIndex &topLeft, const QModelIndex &bottomRight, - const QVector &roles) { - std::vector phrases; - for (int i = 1; i < model->rowCount(); i++) { - phrases.push_back(messages::HighlightPhrase{ - model->item(i, 0)->data(Qt::DisplayRole).toString(), - model->item(i, 1)->data(Qt::CheckStateRole).toBool(), - model->item(i, 2)->data(Qt::CheckStateRole).toBool(), - model->item(i, 3)->data(Qt::CheckStateRole).toBool()}); - } - app->settings->highlightProperties.setValue(phrases); - app->settings->enableHighlightsSelf.setValue( - model->item(0, 0)->data(Qt::CheckStateRole).toBool()); - app->settings->enableHighlightTaskbar.setValue( - model->item(0, 1)->data(Qt::CheckStateRole).toBool()); - app->settings->enableHighlightSound.setValue( - model->item(0, 2)->data(Qt::CheckStateRole).toBool()); - }); - - auto add = buttons.emplace("Add"); - QObject::connect(*add, &QPushButton::clicked, [model, view] { - model->appendRow({util::stringItem(""), - util::boolItem(model->item(model->rowCount() - 1, 1) - ->data(Qt::CheckStateRole) - .toBool()), - util::boolItem(model->item(model->rowCount() - 1, 2) - ->data(Qt::CheckStateRole) - .toBool()), - util::boolItem(false)}); - view->scrollToBottom(); + view->addButtonPressed.connect([] { + getApp()->highlights->phrases.appendItem( + controllers::highlights::HighlightPhrase{"my phrase", true, false, false}); }); - auto remove = buttons.emplace("Remove"); - QObject::connect(*remove, &QPushButton::clicked, [view, model] { - std::vector indices; - - for (const QModelIndex &index : view->selectionModel()->selectedRows(0)) { - indices.push_back(index.row()); - } - - std::sort(indices.begin(), indices.end()); - - for (int i = indices.size() - 1; i >= 0; i--) { - model->removeRow(indices[i]); - } - }); - buttons->addStretch(1); - - view->hideColumn(3); } auto disabledUsers = tabs.appendTab(new QVBoxLayout, "Disabled Users"); {