From 0d519b479c2c7c13518f91327bf99c7a1d3bf718 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 10 Feb 2020 16:41:49 +0100 Subject: [PATCH 01/67] changed wording in settings --- src/widgets/settingspages/GeneralPage.cpp | 12 ++++++------ .../settingspages/HighlightingPage.cpp | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 4032db9e..d4a64338 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -304,6 +304,12 @@ void GeneralPage::initLayout(SettingsLayout &layout) layout.addCheckbox("Start with Windows", s.autorun); #endif layout.addCheckbox("Restart on crash", s.restartOnCrash); + if (!BaseWindow::supportsCustomWindowFrame()) + { + layout.addCheckbox("Show preferences button (ctrl+p to show)", + s.hidePreferencesButton, true); + layout.addCheckbox("Show user button", s.hideUserButton, true); + } layout.addTitle("Chat"); @@ -344,12 +350,6 @@ void GeneralPage::initLayout(SettingsLayout &layout) s.pauseChatModifier); layout.addCheckbox("Show input when it's empty", s.showEmptyInput); layout.addCheckbox("Show message length while typing", s.showMessageLength); - if (!BaseWindow::supportsCustomWindowFrame()) - { - layout.addCheckbox("Show preferences button (ctrl+p to show)", - s.hidePreferencesButton, true); - layout.addCheckbox("Show user button", s.hideUserButton, true); - } layout.addTitle("Messages"); layout.addCheckbox("Seperate with lines", s.separateMessages); diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 46cdba3b..7113ee33 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -47,9 +47,8 @@ HighlightingPage::HighlightingPage() auto highlights = tabs.appendTab(new QVBoxLayout, "Messages"); { highlights.emplace( - "Messages can be highlighted if they match a certain " - "pattern.\n" - "NOTE: User highlights will override phrase highlights."); + "Play notification sounds and highlight messages based on " + "certain patterns."); EditableModelView *view = highlights @@ -89,8 +88,10 @@ HighlightingPage::HighlightingPage() auto pingUsers = tabs.appendTab(new QVBoxLayout, "Users"); { pingUsers.emplace( - "Messages from a certain user can be highlighted.\n" - "NOTE: User highlights will override phrase highlights."); + "Play notification sounds and highlight messages from " + "certain users.\n" + "User highlights are prioritized over message " + "highlights."); EditableModelView *view = pingUsers .emplace( @@ -132,11 +133,11 @@ HighlightingPage::HighlightingPage() } auto disabledUsers = - tabs.appendTab(new QVBoxLayout, "Excluded Users"); + tabs.appendTab(new QVBoxLayout, "Blacklisted Users"); { disabledUsers.emplace( - "This is a list of users (e.g. bots) whose messages should " - "not be highlighted."); + "Disable notification sounds and highlights from certain " + "users (e.g. bots)."); EditableModelView *view = disabledUsers .emplace( @@ -144,7 +145,7 @@ HighlightingPage::HighlightingPage() .getElement(); view->addRegexHelpLink(); - view->setTitles({"Pattern", "Enable\nregex"}); + view->setTitles({"Username", "Enable\nregex"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( From d16732f023c54a189984b0969ea70aeab5f8c489 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 10 Feb 2020 17:25:13 +0100 Subject: [PATCH 02/67] moved and renamed similarity settings to R9K --- src/widgets/settingspages/GeneralPage.cpp | 53 +++++++++++------------ src/widgets/settingspages/IgnoresPage.cpp | 3 +- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index d4a64338..68d5206b 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -412,6 +412,32 @@ void GeneralPage::initLayout(SettingsLayout &layout) "Apple", "Google", "Messenger"}, s.emojiSet); + layout.addTitle("R9K"); + layout.addDescription( + "Hide similar messages by the same user. Temporarily show hidden " + "messages by pressing Ctrl+H."); + layout.addCheckbox("Hide similar messages", s.similarityEnabled); + //layout.addCheckbox("Gray out matches", s.colorSimilarDisabled); + layout.addCheckbox("Hide my own messages", s.hideSimilarMyself); + layout.addCheckbox("Receive notification sounds from hidden messages", + s.shownSimilarTriggerHighlights); + s.hideSimilar.connect( + []() { getApp()->windows->forceLayoutChannelViews(); }, false); + layout.addDropdown( + "Similarity threshold", {"0.5", "0.75", "0.9"}, s.similarityPercentage, + [](auto val) { return QString::number(val); }, + [](auto args) { return fuzzyToFloat(args.value, 0.9f); }); + layout.addDropdown( + "Maximum delay between messages", + {"5s", "10s", "15s", "30s", "60s", "120s"}, s.hideSimilarMaxDelay, + [](auto val) { return QString::number(val) + "s"; }, + [](auto args) { return fuzzyToInt(args.value, 5); }); + layout.addDropdown( + "Amount of previous messages to check", {"1", "2", "3", "4", "5"}, + s.hideSimilarMaxMessagesToCheck, + [](auto val) { return QString::number(val); }, + [](auto args) { return fuzzyToInt(args.value, 3); }); + layout.addTitle("Visible badges"); layout.addCheckbox("Authority (staff, admin)", getSettings()->showBadgesGlobalAuthority); @@ -547,33 +573,6 @@ void GeneralPage::initLayout(SettingsLayout &layout) QDesktopServices::openUrl(getPaths()->rootAppDataDirectory); }); - layout.addTitle("Similarity"); - layout.addDescription( - "Hides or grays out similar messages from the same user"); - layout.addCheckbox("Similarity enabled", s.similarityEnabled); - layout.addCheckbox("Gray out similar messages", s.colorSimilarDisabled); - layout.addCheckbox("Hide similar messages (Ctrl + H)", s.hideSimilar); - layout.addCheckbox("Hide or gray out my own similar messages", - s.hideSimilarMyself); - layout.addCheckbox("Shown similar messages trigger highlights", - s.shownSimilarTriggerHighlights); - layout.addDropdown( - "Similarity percentage", {"0.5", "0.75", "0.9"}, s.similarityPercentage, - [](auto val) { return QString::number(val); }, - [](auto args) { return fuzzyToFloat(args.value, 0.9f); }); - s.hideSimilar.connect( - []() { getApp()->windows->forceLayoutChannelViews(); }, false); - layout.addDropdown( - "Similar messages max delay in seconds", - {"5", "10", "15", "30", "60", "120"}, s.hideSimilarMaxDelay, - [](auto val) { return QString::number(val); }, - [](auto args) { return fuzzyToInt(args.value, 5); }); - layout.addDropdown( - "Similar messages max previous messages to check", - {"1", "2", "3", "4", "5"}, s.hideSimilarMaxMessagesToCheck, - [](auto val) { return QString::number(val); }, - [](auto args) { return fuzzyToInt(args.value, 3); }); - // invisible element for width auto inv = new BaseWidget(this); inv->setScaleIndependantWidth(500); diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index 79128a49..f1bed284 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -42,8 +42,7 @@ IgnoresPage::IgnoresPage() void addPhrasesTab(LayoutCreator layout) { - layout.emplace("Messages can be ignored if " - "they match a certain pattern."); + layout.emplace("Ignore messages based certain patterns."); EditableModelView *view = layout .emplace(getApp()->ignores->createModel(nullptr)) From 59aa59106e210e16c12e25a2b4c3660b5ffa64b3 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 10 Feb 2020 17:26:51 +0100 Subject: [PATCH 03/67] updated R9K shortcut text --- src/widgets/settingspages/KeyboardSettingsPage.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/widgets/settingspages/KeyboardSettingsPage.cpp b/src/widgets/settingspages/KeyboardSettingsPage.cpp index 2d2a809f..ee2502f2 100644 --- a/src/widgets/settingspages/KeyboardSettingsPage.cpp +++ b/src/widgets/settingspages/KeyboardSettingsPage.cpp @@ -26,10 +26,8 @@ KeyboardSettingsPage::KeyboardSettingsPage() form->addRow(new QLabel("Ctrl + Shift + T"), new QLabel("Create new tab")); form->addRow(new QLabel("Ctrl + Shift + W"), new QLabel("Close current tab")); - form->addRow( - new QLabel("Ctrl + H"), - new QLabel( - "Hide/Show similar messages (Enable in General under Similarity)")); + form->addRow(new QLabel("Ctrl + H"), + new QLabel("Hide/Show similar messages (See General->R9K)")); form->addItem(new QSpacerItem(16, 16)); form->addRow(new QLabel("Ctrl + 1/2/3/..."), From da59232913af6bfb64979524ff54a2d6e8ea08d3 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 10 Feb 2020 17:49:45 +0100 Subject: [PATCH 04/67] "Open in player in browser" -> "Open player in browser" --- src/widgets/helper/CommonTexts.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/helper/CommonTexts.hpp b/src/widgets/helper/CommonTexts.hpp index 7f92051d..81b69a2c 100644 --- a/src/widgets/helper/CommonTexts.hpp +++ b/src/widgets/helper/CommonTexts.hpp @@ -1,6 +1,6 @@ #pragma once #define OPEN_IN_BROWSER "Open in browser" -#define OPEN_PLAYER_IN_BROWSER "Open in player in browser" +#define OPEN_PLAYER_IN_BROWSER "Open player in browser" #define OPEN_IN_STREAMLINK "Open in streamlink" #define DONT_OPEN "Don't open" From 3fd05849a20ee0ce60d4a480fa67e49a0ccc9a0b Mon Sep 17 00:00:00 2001 From: tuckerrrrrrrrrrrr Date: Mon, 10 Feb 2020 17:00:09 -0800 Subject: [PATCH 05/67] ctrl + end scrolls to the bottom of chat --- src/widgets/splits/SplitInput.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index e02a2e69..8e4aa0fd 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -9,6 +9,7 @@ #include "singletons/Theme.hpp" #include "util/LayoutCreator.hpp" #include "widgets/Notebook.hpp" +#include "widgets/Scrollbar.hpp" #include "widgets/dialogs/EmotePopup.hpp" #include "widgets/helper/ChannelView.hpp" #include "widgets/helper/EffectLabel.hpp" @@ -267,14 +268,21 @@ void SplitInput::installKeyPressedEvent() } else if (event->key() == Qt::Key_End) { - QTextCursor cursor = this->ui_.textEdit->textCursor(); - cursor.movePosition( - QTextCursor::End, - event->modifiers() & Qt::KeyboardModifier::ShiftModifier - ? QTextCursor::MoveMode::KeepAnchor - : QTextCursor::MoveMode::MoveAnchor); - this->ui_.textEdit->setTextCursor(cursor); - + if (event->modifiers() == Qt::ControlModifier) + { + this->split_->getChannelView().getScrollBar().scrollToBottom( + getSettings()->enableSmoothScrollingNewMessages.getValue()); + } + else + { + QTextCursor cursor = this->ui_.textEdit->textCursor(); + cursor.movePosition( + QTextCursor::End, + event->modifiers() & Qt::KeyboardModifier::ShiftModifier + ? QTextCursor::MoveMode::KeepAnchor + : QTextCursor::MoveMode::MoveAnchor); + this->ui_.textEdit->setTextCursor(cursor); + } event->accept(); } else if (event->key() == Qt::Key_H && From b5764c6a1fd0f5fe7f4a0e446fe5a979fc2fb8fd Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 15 Feb 2020 14:49:20 +0100 Subject: [PATCH 06/67] Remove stupid assert in PubSubClient Fixes #1550 --- src/providers/twitch/PubsubClient.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/providers/twitch/PubsubClient.cpp b/src/providers/twitch/PubsubClient.cpp index 9cf57c28..43b24f2e 100644 --- a/src/providers/twitch/PubsubClient.cpp +++ b/src/providers/twitch/PubsubClient.cpp @@ -1064,7 +1064,6 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData) else { qDebug() << "Invalid whisper type:" << whisperType.c_str(); - assert(false); return; } } From b93de7c0f62cbd4dcebc0a27cd3f86a3e1ba4cb0 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 15 Feb 2020 15:19:16 +0100 Subject: [PATCH 07/67] Add some debug output in our project file --- chatterino.pro | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/chatterino.pro b/chatterino.pro index d569991f..6cf3d812 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -562,3 +562,11 @@ git_hash = $$str_member($$git_commit, 0, 8) DEFINES += CHATTERINO_GIT_COMMIT=\\\"$$git_commit\\\" DEFINES += CHATTERINO_GIT_RELEASE=\\\"$$git_release\\\" DEFINES += CHATTERINO_GIT_HASH=\\\"$$git_hash\\\" + +CONFIG(debug, debug|release) { + message("Building Chatterino2 DEBUG") +} else { + message("Building Chatterino2 RELEASE") +} + +message("Injected git values: $$git_commit ($$git_release) $$git_hash") From 64c58e724ad1610ba3bfcfcfc46c21ed0f47af1d Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 15 Feb 2020 15:49:06 +0100 Subject: [PATCH 08/67] Use same sorting method in emote popup as in the completion model Fixes #1549 --- CHANGELOG.md | 4 ++++ src/common/CompletionModel.cpp | 19 ++++++++++++------- src/common/CompletionModel.hpp | 2 ++ src/widgets/dialogs/EmotePopup.cpp | 4 +++- 4 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6c427d8f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +## Unversioned +- Minor: Emotes in the emote popup are now sorted in the same order as the tab completion (#1549) diff --git a/src/common/CompletionModel.cpp b/src/common/CompletionModel.cpp index 4866e8a2..6cb3bc80 100644 --- a/src/common/CompletionModel.cpp +++ b/src/common/CompletionModel.cpp @@ -38,13 +38,7 @@ bool CompletionModel::TaggedString::operator<(const TaggedString &that) const return this->isEmote(); } - // try comparing insensitively, if they are the same then senstively - // (fixes order of LuL and LUL) - int k = QString::compare(this->string, that.string, Qt::CaseInsensitive); - if (k == 0) - return this->string > that.string; - - return k < 0; + return CompletionModel::compareStrings(this->string, that.string); } // @@ -191,4 +185,15 @@ void CompletionModel::refresh(const QString &prefix, bool isFirstWord) } } +bool CompletionModel::compareStrings(const QString &a, const QString &b) +{ + // try comparing insensitively, if they are the same then senstively + // (fixes order of LuL and LUL) + int k = QString::compare(a, b, Qt::CaseInsensitive); + if (k == 0) + return a > b; + + return k < 0; +} + } // namespace chatterino diff --git a/src/common/CompletionModel.hpp b/src/common/CompletionModel.hpp index 321929e7..2ce37439 100644 --- a/src/common/CompletionModel.hpp +++ b/src/common/CompletionModel.hpp @@ -49,6 +49,8 @@ public: void refresh(const QString &prefix, bool isFirstWord = false); + static bool compareStrings(const QString &a, const QString &b); + private: TaggedString createUser(const QString &str); diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index c000e3ec..9e0911cb 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -1,6 +1,7 @@ #include "EmotePopup.hpp" #include "Application.hpp" +#include "common/CompletionModel.hpp" #include "controllers/accounts/AccountController.hpp" #include "debug/Benchmark.hpp" #include "messages/Message.hpp" @@ -37,7 +38,8 @@ namespace { std::sort(vec.begin(), vec.end(), [](const std::pair &l, const std::pair &r) { - return l.first.string < r.first.string; + return CompletionModel::compareStrings( + l.first.string, r.first.string); }); for (const auto &emote : vec) { From 7a08d7343496f79dba831b8395d59c7a18517a60 Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 15 Feb 2020 17:16:10 +0100 Subject: [PATCH 09/67] Fix preview on hover not working when Animated emotes options was disabled. Fixes #1546 This change introduces a "window timer" that runs every 100ms that we use to update the pixmap if necessary, since there is no signal for "let me know when this image is done loading". --- CHANGELOG.md | 1 + src/singletons/TooltipPreviewImage.cpp | 46 ++++++++++++++++++++------ src/singletons/TooltipPreviewImage.hpp | 9 +++++ src/singletons/WindowManager.cpp | 6 ++++ src/singletons/WindowManager.hpp | 5 +++ 5 files changed, 57 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c427d8f..201b0be8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,3 +2,4 @@ ## Unversioned - Minor: Emotes in the emote popup are now sorted in the same order as the tab completion (#1549) +- Bugfix: Fix preview on hover not working when Animated emotes options was disabled (#1546) diff --git a/src/singletons/TooltipPreviewImage.cpp b/src/singletons/TooltipPreviewImage.cpp index 5355b7d8..ebb0fe6a 100644 --- a/src/singletons/TooltipPreviewImage.cpp +++ b/src/singletons/TooltipPreviewImage.cpp @@ -5,6 +5,7 @@ #include "widgets/TooltipWidget.hpp" namespace chatterino { + TooltipPreviewImage &TooltipPreviewImage::instance() { static TooltipPreviewImage *instance = new TooltipPreviewImage(); @@ -13,19 +14,19 @@ TooltipPreviewImage &TooltipPreviewImage::instance() TooltipPreviewImage::TooltipPreviewImage() { - connections_.push_back(getApp()->windows->gifRepaintRequested.connect([&] { - auto tooltipWidget = TooltipWidget::instance(); - if (this->image_ && !tooltipWidget->isHidden()) + auto windows = getApp()->windows; + + this->connections_.push_back(windows->gifRepaintRequested.connect([&] { + if (this->image_ && this->image_->animated()) { - auto pixmap = this->image_->pixmapOrLoad(); - if (pixmap) - { - tooltipWidget->setImage(*pixmap); - } + this->refreshTooltipWidgetPixmap(); } - else + })); + + this->connections_.push_back(windows->miscUpdate.connect([&] { + if (this->attemptRefresh) { - tooltipWidget->clearImage(); + this->refreshTooltipWidgetPixmap(); } })); } @@ -33,5 +34,30 @@ TooltipPreviewImage::TooltipPreviewImage() void TooltipPreviewImage::setImage(ImagePtr image) { this->image_ = image; + + this->refreshTooltipWidgetPixmap(); } + +void TooltipPreviewImage::refreshTooltipWidgetPixmap() +{ + auto tooltipWidget = TooltipWidget::instance(); + + if (this->image_ && !tooltipWidget->isHidden()) + { + if (auto pixmap = this->image_->pixmapOrLoad()) + { + tooltipWidget->setImage(*pixmap); + this->attemptRefresh = false; + } + else + { + this->attemptRefresh = true; + } + } + else + { + tooltipWidget->clearImage(); + } +} + } // namespace chatterino diff --git a/src/singletons/TooltipPreviewImage.hpp b/src/singletons/TooltipPreviewImage.hpp index 5b23945f..0d1f6f07 100644 --- a/src/singletons/TooltipPreviewImage.hpp +++ b/src/singletons/TooltipPreviewImage.hpp @@ -3,6 +3,7 @@ #include "messages/Image.hpp" namespace chatterino { + class TooltipPreviewImage { public: @@ -17,5 +18,13 @@ private: private: ImagePtr image_ = nullptr; std::vector connections_; + + // attemptRefresh is set to true in case we want to preview an image that has not loaded yet (if pixmapOrLoad fails) + bool attemptRefresh{false}; + + // Refresh the pixmap used in the Tooltip Widget + // Called from setImage and from the "gif repaint" signal if the image is animated + void refreshTooltipWidgetPixmap(); }; + } // namespace chatterino diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index bddb948f..fb1a28a3 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -111,6 +111,12 @@ WindowManager::WindowManager() QObject::connect(this->saveTimer, &QTimer::timeout, [] { getApp()->windows->save(); // }); + + this->miscUpdateTimer_.start(100); + + QObject::connect(&this->miscUpdateTimer_, &QTimer::timeout, [this] { + this->miscUpdate.invoke(); // + }); } MessageElementFlags WindowManager::getWordFlags() diff --git a/src/singletons/WindowManager.hpp b/src/singletons/WindowManager.hpp index 77084eaa..9dfd8d9b 100644 --- a/src/singletons/WindowManager.hpp +++ b/src/singletons/WindowManager.hpp @@ -80,6 +80,10 @@ public: pajlada::Signals::NoArgSignal wordFlagsChanged; + // This signal fires every 100ms and can be used to trigger random things that require a recheck. + // It is currently being used by the "Tooltip Preview Image" system to recheck if an image is ready to be rendered. + pajlada::Signals::NoArgSignal miscUpdate; + private: void encodeNodeRecusively(SplitContainer::Node *node, QJsonObject &obj); @@ -96,6 +100,7 @@ private: pajlada::SettingListener wordFlagsListener_; QTimer *saveTimer; + QTimer miscUpdateTimer_; }; } // namespace chatterino From 59296075e8ea2368411cb77642030e5b16faf0d3 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 15 Feb 2020 12:04:05 -0500 Subject: [PATCH 10/67] Fix the CI by downgrading install-qt-action version (#1554) --- .github/workflows/build.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7fd32834..75bdc29d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,9 +25,7 @@ jobs: submodules: true - name: Install Qt - uses: jurplel/install-qt-action@v2 - with: - modules: qtwebengine + uses: jurplel/install-qt-action@v1 # WINDOWS - name: Cache conan @@ -79,7 +77,7 @@ jobs: # LINUX - name: Install dependencies (Ubuntu) if: startsWith(matrix.os, 'ubuntu') - run: sudo apt-get update && sudo apt-get -y install libssl-dev libboost-dev libboost-system-dev libboost-filesystem-dev libpulse-dev libxkbcommon-x11-0 libgstreamer-plugins-base1.0-0 + run: sudo apt-get update && sudo apt-get -y install libssl-dev libboost-dev libboost-system-dev libboost-filesystem-dev libpulse-dev libxkbcommon-x11-0 libgstreamer-plugins-base1.0-0 build-essential libgl1-mesa-dev - name: Build (Ubuntu) if: startsWith(matrix.os, 'ubuntu') From 6229b2f43478daa478de3e12ee441d4f34b8ab86 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 16 Feb 2020 14:24:11 +0100 Subject: [PATCH 11/67] sync gif emotes of same length --- src/messages/Image.cpp | 19 +++++++++++++------ src/messages/Image.hpp | 1 + src/singletons/Emotes.hpp | 2 -- src/singletons/helper/GifTimer.cpp | 1 + src/singletons/helper/GifTimer.hpp | 7 +++++++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index b4222301..39a30545 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -41,6 +41,14 @@ namespace detail { getApp()->emotes->gifTimer.signal.connect( [this] { this->advance(); }); } + + auto totalLength = std::accumulate( + this->items_.begin(), this->items_.end(), 0UL, + [](auto init, auto &&frame) { return init + frame.duration; }); + + this->durationOffset_ = std::min( + int(getApp()->emotes->gifTimer.position() % totalLength), 60000); + this->processOffset(); } Frames::~Frames() @@ -58,17 +66,16 @@ namespace detail { void Frames::advance() { - this->durationOffset_ += GIF_FRAME_LENGTH; + this->durationOffset_ += gifFrameLength; + this->processOffset(); + } + void Frames::processOffset() + { while (true) { this->index_ %= this->items_.size(); - // TODO: Figure out what this was supposed to achieve - // if (this->index_ >= this->items_.size()) { - // this->index_ = this->index_; - // } - if (this->durationOffset_ > this->items_[this->index_].duration) { this->durationOffset_ -= this->items_[this->index_].duration; diff --git a/src/messages/Image.hpp b/src/messages/Image.hpp index f114d1f0..193ba578 100644 --- a/src/messages/Image.hpp +++ b/src/messages/Image.hpp @@ -35,6 +35,7 @@ namespace detail { boost::optional first() const; private: + void processOffset(); QVector> items_; int index_{0}; int durationOffset_{0}; diff --git a/src/singletons/Emotes.hpp b/src/singletons/Emotes.hpp index fc475d75..be7fdc48 100644 --- a/src/singletons/Emotes.hpp +++ b/src/singletons/Emotes.hpp @@ -2,8 +2,6 @@ #include "common/Singleton.hpp" -#define GIF_FRAME_LENGTH 33 - #include "providers/bttv/BttvEmotes.hpp" #include "providers/emoji/Emojis.hpp" #include "providers/ffz/FfzEmotes.hpp" diff --git a/src/singletons/helper/GifTimer.cpp b/src/singletons/helper/GifTimer.cpp index 28e1b77e..2edf7bac 100644 --- a/src/singletons/helper/GifTimer.cpp +++ b/src/singletons/helper/GifTimer.cpp @@ -22,6 +22,7 @@ void GIFTimer::initialize() qApp->activeWindow() == nullptr) return; + this->position_ += gifFrameLength; this->signal.invoke(); getApp()->windows->repaintGifEmotes(); }); diff --git a/src/singletons/helper/GifTimer.hpp b/src/singletons/helper/GifTimer.hpp index b44fcfb7..da47149b 100644 --- a/src/singletons/helper/GifTimer.hpp +++ b/src/singletons/helper/GifTimer.hpp @@ -5,15 +5,22 @@ namespace chatterino { +constexpr long unsigned gifFrameLength = 33; + class GIFTimer { public: void initialize(); pajlada::Signals::NoArgSignal signal; + long unsigned position() + { + return this->position_; + } private: QTimer timer; + long unsigned position_{}; }; } // namespace chatterino From d0839ac36ee3b6b2492c54f344d8ca31d2bea814 Mon Sep 17 00:00:00 2001 From: Leon Richardt Date: Thu, 20 Feb 2020 00:10:10 +0100 Subject: [PATCH 12/67] Make pre-defined highlight columns unselectable Fixes #1539. --- src/controllers/highlights/HighlightModel.cpp | 12 ++++++------ src/util/StandardItemHelper.hpp | 14 ++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/controllers/highlights/HighlightModel.cpp b/src/controllers/highlights/HighlightModel.cpp index 05f37bf7..ef134c12 100644 --- a/src/controllers/highlights/HighlightModel.cpp +++ b/src/controllers/highlights/HighlightModel.cpp @@ -63,10 +63,10 @@ void HighlightModel::afterInit() usernameRow[Column::CaseSensitive]->setFlags(0); QUrl selfSound = QUrl(getSettings()->selfHighlightSoundUrl.getValue()); - setFilePathItem(usernameRow[Column::SoundPath], selfSound); + setFilePathItem(usernameRow[Column::SoundPath], selfSound, false); auto selfColor = ColorProvider::instance().color(ColorType::SelfHighlight); - setColorItem(usernameRow[Column::Color], *selfColor); + setColorItem(usernameRow[Column::Color], *selfColor, false); this->insertCustomRow(usernameRow, 0); @@ -86,10 +86,10 @@ void HighlightModel::afterInit() QUrl whisperSound = QUrl(getSettings()->whisperHighlightSoundUrl.getValue()); - setFilePathItem(whisperRow[Column::SoundPath], whisperSound); + setFilePathItem(whisperRow[Column::SoundPath], whisperSound, false); auto whisperColor = ColorProvider::instance().color(ColorType::Whisper); - setColorItem(whisperRow[Column::Color], *whisperColor); + setColorItem(whisperRow[Column::Color], *whisperColor, false); this->insertCustomRow(whisperRow, 1); @@ -107,10 +107,10 @@ void HighlightModel::afterInit() subRow[Column::CaseSensitive]->setFlags(0); QUrl subSound = QUrl(getSettings()->subHighlightSoundUrl.getValue()); - setFilePathItem(subRow[Column::SoundPath], subSound); + setFilePathItem(subRow[Column::SoundPath], subSound, false); auto subColor = ColorProvider::instance().color(ColorType::Subscription); - setColorItem(subRow[Column::Color], *subColor); + setColorItem(subRow[Column::Color], *subColor, false); this->insertCustomRow(subRow, 2); } diff --git a/src/util/StandardItemHelper.hpp b/src/util/StandardItemHelper.hpp index 4b7ba52b..47e4db14 100644 --- a/src/util/StandardItemHelper.hpp +++ b/src/util/StandardItemHelper.hpp @@ -22,17 +22,23 @@ static void setStringItem(QStandardItem *item, const QString &value, (editable ? (Qt::ItemIsEditable) : 0))); } -static void setFilePathItem(QStandardItem *item, const QUrl &value) +static void setFilePathItem(QStandardItem *item, const QUrl &value, + bool selectable = true) { item->setData(value, Qt::UserRole); item->setData(value.fileName(), Qt::DisplayRole); - item->setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable)); + item->setFlags( + Qt::ItemFlags(Qt::ItemIsEnabled | + (selectable ? Qt::ItemIsSelectable : Qt::NoItemFlags))); } -static void setColorItem(QStandardItem *item, const QColor &value) +static void setColorItem(QStandardItem *item, const QColor &value, + bool selectable = true) { item->setData(value, Qt::DecorationRole); - item->setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable)); + item->setFlags( + Qt::ItemFlags(Qt::ItemIsEnabled | + (selectable ? Qt::ItemIsSelectable : Qt::NoItemFlags))); } static QStandardItem *emptyItem() From d6c3c996050d14aaca2b927982578ee2ae775a3c Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 20 Feb 2020 23:16:46 +0100 Subject: [PATCH 13/67] list settings can be reordered now --- src/common/SignalVectorModel.hpp | 64 +++++++++++++++++++++++- src/widgets/helper/EditableModelView.cpp | 24 ++++++--- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index adea7884..bacb6c4d 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -205,7 +205,9 @@ public: assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); - return this->rows_[row].items[column]->flags(); + return this->rows_[row].items[column]->flags() | + Qt::ItemFlag::ItemIsDragEnabled | + Qt::ItemFlag::ItemIsDropEnabled; } QStandardItem *getItem(int row, int column) @@ -239,6 +241,66 @@ public: return true; } + QStringList mimeTypes() const override + { + return {"chatterino_row_id"}; + } + + QMimeData *mimeData(const QModelIndexList &list) const + { + if (list.length() == 1) + { + return nullptr; + } + + // Check if all indices are in the same row -> single row selected + for (auto &&x : list) + { + if (x.row() != list.first().row()) + return nullptr; + } + + auto data = new QMimeData; + data->setData("chatterino_row_id", QByteArray::number(list[0].row())); + return data; + } + + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int /*row*/, + int /*column*/, const QModelIndex &parent) override + { + if (data->hasFormat("chatterino_row_id") && + action & (Qt::DropAction::MoveAction | Qt::DropAction::CopyAction)) + { + int from = data->data("chatterino_row_id").toInt(); + int to = parent.row(); + + if (from < 0 || from > this->vector_->getVector().size() || + to < 0 || to > this->vector_->getVector().size()) + { + return false; + } + + if (from != to) + { + auto item = this->vector_->getVector()[from]; + this->vector_->removeItem(from); + this->vector_->insertItem(item, to); + } + + // We return false since we remove items ourselves. + return false; + } + + return false; + } + + Qt::DropActions supportedDropActions() const override + { + return this->vector_->isSorted() + ? Qt::DropActions() + : Qt::DropAction::CopyAction | Qt::DropAction::MoveAction; + } + protected: virtual void afterInit() { diff --git a/src/widgets/helper/EditableModelView.cpp b/src/widgets/helper/EditableModelView.cpp index ff6f9214..62f1e4dc 100644 --- a/src/widgets/helper/EditableModelView.cpp +++ b/src/widgets/helper/EditableModelView.cpp @@ -15,9 +15,12 @@ EditableModelView::EditableModelView(QAbstractTableModel *model) { this->model_->setParent(this); this->tableView_->setModel(model); - this->tableView_->setSelectionMode(QAbstractItemView::ExtendedSelection); + this->tableView_->setSelectionMode(QAbstractItemView::SingleSelection); this->tableView_->setSelectionBehavior(QAbstractItemView::SelectRows); - this->tableView_->verticalHeader()->hide(); + this->tableView_->setDragDropMode(QTableView::DragDropMode::InternalMove); + this->tableView_->setDragDropOverwriteMode(false); + this->tableView_->setDefaultDropAction(Qt::DropAction::MoveAction); + this->tableView_->verticalHeader()->setVisible(false); // create layout QVBoxLayout *vbox = new QVBoxLayout(this); @@ -38,12 +41,17 @@ EditableModelView::EditableModelView(QAbstractTableModel *model) 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()); - } + auto selected = this->getTableView()->selectionModel()->selectedRows(0); + + // Remove rows backwards so indices don't shift. + std::vector rows; + for (auto &&index : selected) + rows.push_back(index.row()); + + std::sort(rows.begin(), rows.end(), std::greater{}); + + for (auto &&row : rows) + model_->removeRow(row); }); buttons->addStretch(); From 4a69095d5f8cd906bdcb927331d68204c721943c Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 20 Feb 2020 23:37:46 +0100 Subject: [PATCH 14/67] fix --- src/common/SignalVectorModel.hpp | 4 +--- src/controllers/commands/CommandModel.cpp | 10 ++++------ src/util/StandardItemHelper.hpp | 23 +++++++++++++++-------- src/widgets/settingspages/CommandPage.cpp | 1 + 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index bacb6c4d..bb7cd7c0 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -205,9 +205,7 @@ public: assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); - return this->rows_[row].items[column]->flags() | - Qt::ItemFlag::ItemIsDragEnabled | - Qt::ItemFlag::ItemIsDropEnabled; + return this->rows_[row].items[column]->flags(); } QStandardItem *getItem(int row, int column) diff --git a/src/controllers/commands/CommandModel.cpp b/src/controllers/commands/CommandModel.cpp index 4a55907d..d12a81c6 100644 --- a/src/controllers/commands/CommandModel.cpp +++ b/src/controllers/commands/CommandModel.cpp @@ -1,5 +1,7 @@ #include "CommandModel.hpp" +#include "util/StandardItemHelper.hpp" + namespace chatterino { // commandmodel @@ -20,12 +22,8 @@ Command CommandModel::getItemFromRow(std::vector &row, void CommandModel::getRowFromItem(const Command &item, std::vector &row) { - row[0]->setData(item.name, Qt::DisplayRole); - row[0]->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | - Qt::ItemIsEditable); - row[1]->setData(item.func, Qt::DisplayRole); - row[1]->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | - Qt::ItemIsEditable); + setStringItem(row[0], item.name); + setStringItem(row[1], item.func); } } // namespace chatterino diff --git a/src/util/StandardItemHelper.hpp b/src/util/StandardItemHelper.hpp index 47e4db14..f9caeb0a 100644 --- a/src/util/StandardItemHelper.hpp +++ b/src/util/StandardItemHelper.hpp @@ -4,12 +4,20 @@ namespace chatterino { +static auto defaultItemFlags(bool selectable) +{ + return Qt::ItemIsEnabled | + (selectable ? Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | + Qt::ItemIsDropEnabled + : Qt::ItemFlag()); +} + static void setBoolItem(QStandardItem *item, bool value, bool userCheckable = true, bool selectable = true) { - item->setFlags(Qt::ItemFlags( - Qt::ItemIsEnabled | (selectable ? Qt::ItemIsSelectable : 0) | - (userCheckable ? Qt::ItemIsUserCheckable : 0))); + item->setFlags( + Qt::ItemFlags(defaultItemFlags(selectable) | + (userCheckable ? Qt::ItemIsUserCheckable : 0))); item->setCheckState(value ? Qt::Checked : Qt::Unchecked); } @@ -17,8 +25,7 @@ static void setStringItem(QStandardItem *item, const QString &value, bool editable = true, bool selectable = true) { item->setData(value, Qt::EditRole); - item->setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | - (selectable ? Qt::ItemIsSelectable : 0) | + item->setFlags(Qt::ItemFlags(defaultItemFlags(selectable) | (editable ? (Qt::ItemIsEditable) : 0))); } @@ -28,7 +35,7 @@ static void setFilePathItem(QStandardItem *item, const QUrl &value, item->setData(value, Qt::UserRole); item->setData(value.fileName(), Qt::DisplayRole); item->setFlags( - Qt::ItemFlags(Qt::ItemIsEnabled | + Qt::ItemFlags(defaultItemFlags(selectable) | (selectable ? Qt::ItemIsSelectable : Qt::NoItemFlags))); } @@ -37,14 +44,14 @@ static void setColorItem(QStandardItem *item, const QColor &value, { item->setData(value, Qt::DecorationRole); item->setFlags( - Qt::ItemFlags(Qt::ItemIsEnabled | + Qt::ItemFlags(defaultItemFlags(selectable) | (selectable ? Qt::ItemIsSelectable : Qt::NoItemFlags))); } static QStandardItem *emptyItem() { auto *item = new QStandardItem(); - item->setFlags(Qt::ItemFlags(0)); + item->setFlags(Qt::ItemFlags()); return item; } diff --git a/src/widgets/settingspages/CommandPage.cpp b/src/widgets/settingspages/CommandPage.cpp index 6df83105..c3470c4b 100644 --- a/src/widgets/settingspages/CommandPage.cpp +++ b/src/widgets/settingspages/CommandPage.cpp @@ -51,6 +51,7 @@ CommandPage::CommandPage() Command{"/command", "I made a new command HeyGuys"}); }); + // TODO: asyncronously check path if (QFile(c1settingsPath()).exists()) { auto button = new QPushButton("Import commands from Chatterino 1"); From 0bb112a3c2132dc5829b3c0dba4c44e41a00d611 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 20 Feb 2020 23:47:25 +0100 Subject: [PATCH 15/67] improved performance of opening the settings dialog --- src/widgets/dialogs/SettingsDialog.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 73ad5f75..e9769e3f 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -28,13 +28,15 @@ SettingsDialog::SettingsDialog() { this->setWindowTitle("Chatterino Settings"); + this->themeChangedEvent(); + this->scaleChangedEvent(this->scale()); + this->initUi(); this->addTabs(); - this->scaleChangedEvent(this->scale()); - this->overrideBackgroundColor_ = QColor("#111111"); - this->themeChangedEvent(); + this->scaleChangedEvent( + this->scale()); // execute twice to fix performance + width of item this->resize(815, 600); } @@ -273,7 +275,8 @@ void SettingsDialog::scaleChangedEvent(float newDpi) this->setStyleSheet(styleSheet); - this->ui_.tabContainerContainer->setFixedWidth(int(150 * newDpi)); + if (this->ui_.tabContainerContainer) + this->ui_.tabContainerContainer->setFixedWidth(int(150 * newDpi)); } void SettingsDialog::themeChangedEvent() From fbbf34878f4ba7027936364831b922b9bb85249c Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 00:46:19 +0100 Subject: [PATCH 16/67] getSettingsPage -> page --- src/widgets/dialogs/SettingsDialog.cpp | 6 +++--- src/widgets/helper/SettingsDialogTab.cpp | 2 +- src/widgets/helper/SettingsDialogTab.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index e9769e3f..414aaa4c 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -199,7 +199,7 @@ void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment) void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser) { - this->ui_.pageStack->setCurrentWidget(tab->getSettingsPage()); + this->ui_.pageStack->setCurrentWidget(tab->page()); if (this->selectedTab_ != nullptr) { @@ -256,7 +256,7 @@ void SettingsDialog::refresh() for (auto *tab : this->tabs_) { - tab->getSettingsPage()->onShow(); + tab->page()->onShow(); } } @@ -304,7 +304,7 @@ void SettingsDialog::onCancelClicked() { for (auto &tab : this->tabs_) { - tab->getSettingsPage()->cancel(); + tab->page()->cancel(); } getSettings()->restoreSnapshot(); diff --git a/src/widgets/helper/SettingsDialogTab.cpp b/src/widgets/helper/SettingsDialogTab.cpp index 8089bf80..b74cf60d 100644 --- a/src/widgets/helper/SettingsDialogTab.cpp +++ b/src/widgets/helper/SettingsDialogTab.cpp @@ -34,7 +34,7 @@ void SettingsDialogTab::setSelected(bool _selected) emit selectedChanged(selected_); } -SettingsPage *SettingsDialogTab::getSettingsPage() +SettingsPage *SettingsDialogTab::page() { return this->page_; } diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index dfb765b1..3ee7609b 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -20,7 +20,7 @@ public: QString imageFileName); void setSelected(bool selected_); - SettingsPage *getSettingsPage(); + SettingsPage *page(); signals: void selectedChanged(bool); From 39d40d6db6522a5550693a158be50d8f0a06fc67 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 01:17:22 +0100 Subject: [PATCH 17/67] some settings refactorings --- src/widgets/dialogs/SettingsDialog.cpp | 41 +++++++++++++++++------- src/widgets/dialogs/SettingsDialog.hpp | 9 +++--- src/widgets/helper/SettingsDialogTab.cpp | 9 +++++- src/widgets/helper/SettingsDialogTab.hpp | 10 +++++- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 414aaa4c..3da4429a 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -4,7 +4,6 @@ #include "singletons/Resources.hpp" #include "util/LayoutCreator.hpp" #include "widgets/helper/Button.hpp" -#include "widgets/helper/SettingsDialogTab.hpp" #include "widgets/settingspages/AboutPage.hpp" #include "widgets/settingspages/AccountsPage.hpp" #include "widgets/settingspages/CommandPage.hpp" @@ -21,7 +20,7 @@ namespace chatterino { -SettingsDialog *SettingsDialog::handle = nullptr; +SettingsDialog *SettingsDialog::instance_ = nullptr; SettingsDialog::SettingsDialog() : BaseWindow(BaseWindow::DisableCustomScaling) @@ -105,10 +104,10 @@ void SettingsDialog::initUi() void SettingsDialog::filterElements(const QString &text) { // filter elements and hide pages - for (auto &&page : this->pages_) + for (auto &&tab : this->tabs_) { // filterElements returns true if anything on the page matches the search query - page->tab()->setVisible(page->filterElements(text)); + tab->setVisible(tab->page()->filterElements(text)); } // find next visible page @@ -146,9 +145,9 @@ void SettingsDialog::filterElements(const QString &text) } } -SettingsDialog *SettingsDialog::getHandle() +SettingsDialog *SettingsDialog::instance() { - return SettingsDialog::handle; + return SettingsDialog::instance_; } void SettingsDialog::addTabs() @@ -173,7 +172,7 @@ void SettingsDialog::addTabs() this->ui_.tabContainer->addSpacing(16); this->addTab(new KeyboardSettingsPage); - this->addTab(this->ui_.moderationPage = new ModerationPage); + this->addTab(new ModerationPage); this->addTab(new NotificationPage); this->addTab(new ExternalToolsPage); @@ -189,7 +188,6 @@ void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment) this->ui_.pageStack->addWidget(page); this->ui_.tabContainer->addWidget(tab, 0, alignment); this->tabs_.push_back(tab); - this->pages_.push_back(page); if (this->tabs_.size() == 1) { @@ -217,6 +215,21 @@ void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser) } } +void SettingsDialog::selectTab(SettingsTabId id) +{ + this->selectTab(this->tab(id)); +} + +SettingsDialogTab *SettingsDialog::tab(SettingsTabId id) +{ + for (auto &&tab : this->tabs_) + if (tab->id() == id) + return tab; + + assert(false); + return nullptr; +} + void SettingsDialog::selectPage(SettingsPage *page) { assert(page); @@ -233,12 +246,18 @@ void SettingsDialog::showDialog(SettingsDialogPreference preferredTab) switch (preferredTab) { case SettingsDialogPreference::Accounts: - instance->selectTab(instance->tabs_.at(1)); + instance->selectTab(SettingsTabId::Accounts); break; case SettingsDialogPreference::ModerationActions: - instance->selectPage(instance->ui_.moderationPage); - instance->ui_.moderationPage->selectModerationActions(); + if (auto tab = instance->tab(SettingsTabId::Moderation)) + { + instance->selectTab(tab); + if (auto page = dynamic_cast(tab->page())) + { + page->selectModerationActions(); + } + } break; default:; diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 5a064b66..7495352d 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -7,6 +7,7 @@ #include #include #include +#include "widgets/helper/SettingsDialogTab.hpp" class QLineEdit; @@ -32,7 +33,7 @@ class SettingsDialog : public BaseWindow public: SettingsDialog(); - static SettingsDialog *getHandle(); // may be NULL + static SettingsDialog *instance(); // may be NULL static void showDialog(SettingsDialogPreference preferredTab = SettingsDialogPreference::NoPreference); @@ -42,7 +43,7 @@ protected: virtual void showEvent(QShowEvent *) override; private: - static SettingsDialog *handle; + static SettingsDialog *instance_; void refresh(); @@ -51,6 +52,8 @@ private: void addTab(SettingsPage *page, Qt::Alignment alignment = Qt::AlignTop); void selectTab(SettingsDialogTab *tab, bool byUser = true); void selectPage(SettingsPage *page); + void selectTab(SettingsTabId id); + SettingsDialogTab *tab(SettingsTabId id); void filterElements(const QString &query); void onOkClicked(); @@ -62,11 +65,9 @@ private: QStackedLayout *pageStack{}; QPushButton *okButton{}; QPushButton *cancelButton{}; - ModerationPage *moderationPage{}; QLineEdit *search{}; } ui_; std::vector tabs_; - std::vector pages_; SettingsDialogTab *selectedTab_{}; SettingsDialogTab *lastSelectedByUser_{}; diff --git a/src/widgets/helper/SettingsDialogTab.cpp b/src/widgets/helper/SettingsDialogTab.cpp index b74cf60d..a5bc7145 100644 --- a/src/widgets/helper/SettingsDialogTab.cpp +++ b/src/widgets/helper/SettingsDialogTab.cpp @@ -8,10 +8,12 @@ namespace chatterino { SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog, - SettingsPage *_page, QString imageFileName) + SettingsPage *_page, QString imageFileName, + SettingsTabId id) : BaseWidget(_dialog) , dialog_(_dialog) , page_(_page) + , id_(id) { this->ui_.labelText = page_->getName(); this->ui_.icon.addFile(imageFileName); @@ -72,4 +74,9 @@ void SettingsDialogTab::mousePressEvent(QMouseEvent *event) this->setFocus(); } +SettingsTabId SettingsDialogTab::id() const +{ + return id_; +} + } // namespace chatterino diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index 3ee7609b..29b99478 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -11,16 +11,23 @@ namespace chatterino { class SettingsPage; class SettingsDialog; +enum SettingsTabId { + None, + Accounts, + Moderation, +}; + class SettingsDialogTab : public BaseWidget { Q_OBJECT public: SettingsDialogTab(SettingsDialog *dialog_, SettingsPage *page_, - QString imageFileName); + QString imageFileName, SettingsTabId id = {}); void setSelected(bool selected_); SettingsPage *page(); + SettingsTabId id() const; signals: void selectedChanged(bool); @@ -37,6 +44,7 @@ private: // Parent settings dialog SettingsDialog *dialog_; SettingsPage *page_; + SettingsTabId id_; bool selected_ = false; }; From 78ca0cb84fb36ee038bd5b0fa2c2186b11f138f5 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 01:22:50 +0100 Subject: [PATCH 18/67] moved settings tab icons to SettingsDialog --- src/widgets/dialogs/SettingsDialog.cpp | 25 ++++++++++--------- src/widgets/dialogs/SettingsDialog.hpp | 5 ++-- src/widgets/settingspages/AboutPage.cpp | 2 +- src/widgets/settingspages/AccountsPage.cpp | 2 +- src/widgets/settingspages/CommandPage.cpp | 2 +- .../settingspages/ExternalToolsPage.cpp | 2 +- src/widgets/settingspages/GeneralPage.cpp | 2 +- .../settingspages/HighlightingPage.cpp | 2 +- src/widgets/settingspages/IgnoresPage.cpp | 2 +- .../settingspages/KeyboardSettingsPage.cpp | 2 +- src/widgets/settingspages/ModerationPage.cpp | 2 +- .../settingspages/NotificationPage.cpp | 2 +- src/widgets/settingspages/SettingsPage.cpp | 8 +----- src/widgets/settingspages/SettingsPage.hpp | 6 +---- 14 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 3da4429a..8deaa830 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -157,32 +157,33 @@ void SettingsDialog::addTabs() this->ui_.tabContainer->setContentsMargins(0, 20, 0, 20); - this->addTab(new GeneralPage); + this->addTab(new GeneralPage, ":/settings/about.svg"); this->ui_.tabContainer->addSpacing(16); - this->addTab(new AccountsPage); + this->addTab(new AccountsPage, ":/settings/accounts.svg"); this->ui_.tabContainer->addSpacing(16); - this->addTab(new CommandPage); - this->addTab(new HighlightingPage); - this->addTab(new IgnoresPage); + this->addTab(new CommandPage, ":/settings/commands.svg"); + this->addTab(new HighlightingPage, ":/settings/notifications.svg"); + this->addTab(new IgnoresPage, ":/settings/ignore.svg"); this->ui_.tabContainer->addSpacing(16); - this->addTab(new KeyboardSettingsPage); - this->addTab(new ModerationPage); - this->addTab(new NotificationPage); - this->addTab(new ExternalToolsPage); + this->addTab(new KeyboardSettingsPage, ":/settings/keybinds.svg"); + this->addTab(new ModerationPage, ":/settings/moderation.svg"); + this->addTab(new NotificationPage, ":/settings/notification2.svg"); + this->addTab(new ExternalToolsPage, ":/settings/externaltools.svg"); this->ui_.tabContainer->addStretch(1); - this->addTab(new AboutPage, Qt::AlignBottom); + this->addTab(new AboutPage, ":/settings/about.svg", Qt::AlignBottom); } -void SettingsDialog::addTab(SettingsPage *page, Qt::Alignment alignment) +void SettingsDialog::addTab(SettingsPage *page, const QString &iconPath, + Qt::Alignment alignment) { - auto tab = new SettingsDialogTab(this, page, page->getIconResource()); + auto tab = new SettingsDialogTab(this, page, iconPath); page->setTab(tab); this->ui_.pageStack->addWidget(page); diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 7495352d..6e282d13 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -49,8 +49,9 @@ private: void initUi(); void addTabs(); - void addTab(SettingsPage *page, Qt::Alignment alignment = Qt::AlignTop); - void selectTab(SettingsDialogTab *tab, bool byUser = true); + void addTab(SettingsPage *page, const QString &iconPath, + Qt::Alignment alignment = Qt::AlignTop); + void selectTab(SettingsDialogTab *tab, const bool byUser = true); void selectPage(SettingsPage *page); void selectTab(SettingsTabId id); SettingsDialogTab *tab(SettingsTabId id); diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 5a7dcf53..1371bbee 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -18,7 +18,7 @@ namespace chatterino { AboutPage::AboutPage() - : SettingsPage("About", ":/settings/about.svg") + : SettingsPage("About") { LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/AccountsPage.cpp b/src/widgets/settingspages/AccountsPage.cpp index 94408553..1969e3fc 100644 --- a/src/widgets/settingspages/AccountsPage.cpp +++ b/src/widgets/settingspages/AccountsPage.cpp @@ -17,7 +17,7 @@ namespace chatterino { AccountsPage::AccountsPage() - : SettingsPage("Accounts", ":/settings/accounts.svg") + : SettingsPage("Accounts") { auto *app = getApp(); diff --git a/src/widgets/settingspages/CommandPage.cpp b/src/widgets/settingspages/CommandPage.cpp index c3470c4b..d565a10f 100644 --- a/src/widgets/settingspages/CommandPage.cpp +++ b/src/widgets/settingspages/CommandPage.cpp @@ -33,7 +33,7 @@ namespace { } // namespace CommandPage::CommandPage() - : SettingsPage("Commands", ":/settings/commands.svg") + : SettingsPage("Commands") { auto app = getApp(); diff --git a/src/widgets/settingspages/ExternalToolsPage.cpp b/src/widgets/settingspages/ExternalToolsPage.cpp index 57a8a496..f7aad15b 100644 --- a/src/widgets/settingspages/ExternalToolsPage.cpp +++ b/src/widgets/settingspages/ExternalToolsPage.cpp @@ -12,7 +12,7 @@ namespace chatterino { ExternalToolsPage::ExternalToolsPage() - : SettingsPage("External tools", ":/settings/externaltools.svg") + : SettingsPage("External tools") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.setLayoutType(); diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 68d5206b..38b3b4e3 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -237,7 +237,7 @@ bool SettingsLayout::filterElements(const QString &query) } GeneralPage::GeneralPage() - : SettingsPage("General", ":/settings/about.svg") + : SettingsPage("General") { auto y = new QVBoxLayout; auto scroll = new QScrollArea; diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 7113ee33..05a71bd7 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -29,7 +29,7 @@ namespace chatterino { HighlightingPage::HighlightingPage() - : SettingsPage("Highlights", ":/settings/notifications.svg") + : SettingsPage("Highlights") { auto app = getApp(); LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index f1bed284..af36ecad 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -29,7 +29,7 @@ static void addUsersTab(IgnoresPage &page, LayoutCreator box, QStringListModel &model); IgnoresPage::IgnoresPage() - : SettingsPage("Ignores", ":/settings/ignore.svg") + : SettingsPage("Ignores") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.setLayoutType(); diff --git a/src/widgets/settingspages/KeyboardSettingsPage.cpp b/src/widgets/settingspages/KeyboardSettingsPage.cpp index ee2502f2..0dd557a7 100644 --- a/src/widgets/settingspages/KeyboardSettingsPage.cpp +++ b/src/widgets/settingspages/KeyboardSettingsPage.cpp @@ -8,7 +8,7 @@ namespace chatterino { KeyboardSettingsPage::KeyboardSettingsPage() - : SettingsPage("Keybindings", ":/settings/keybinds.svg") + : SettingsPage("Keybindings") { auto layout = LayoutCreator(this).setLayoutType(); diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 3d2050ba..88d8ac9e 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -73,7 +73,7 @@ QString fetchLogDirectorySize() } ModerationPage::ModerationPage() - : SettingsPage("Moderation", ":/settings/moderation.svg") + : SettingsPage("Moderation") { auto app = getApp(); LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/NotificationPage.cpp b/src/widgets/settingspages/NotificationPage.cpp index 06acf44d..968c34f5 100644 --- a/src/widgets/settingspages/NotificationPage.cpp +++ b/src/widgets/settingspages/NotificationPage.cpp @@ -21,7 +21,7 @@ namespace chatterino { NotificationPage::NotificationPage() - : SettingsPage("Notifications", ":/settings/notification2.svg") + : SettingsPage("Notifications") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.emplace().withoutMargin(); diff --git a/src/widgets/settingspages/SettingsPage.cpp b/src/widgets/settingspages/SettingsPage.cpp index cbcc9019..40f6e049 100644 --- a/src/widgets/settingspages/SettingsPage.cpp +++ b/src/widgets/settingspages/SettingsPage.cpp @@ -64,9 +64,8 @@ bool filterItemsRec(QObject *object, const QString &query) return any; } -SettingsPage::SettingsPage(const QString &name, const QString &iconResource) +SettingsPage::SettingsPage(const QString &name) : name_(name) - , iconResource_(iconResource) { } @@ -81,11 +80,6 @@ const QString &SettingsPage::getName() return this->name_; } -const QString &SettingsPage::getIconResource() -{ - return this->iconResource_; -} - SettingsDialogTab *SettingsPage::tab() const { return this->tab_; diff --git a/src/widgets/settingspages/SettingsPage.hpp b/src/widgets/settingspages/SettingsPage.hpp index 0e92311f..eeb8b960 100644 --- a/src/widgets/settingspages/SettingsPage.hpp +++ b/src/widgets/settingspages/SettingsPage.hpp @@ -47,10 +47,9 @@ class SettingsPage : public QFrame Q_OBJECT public: - SettingsPage(const QString &name, const QString &iconResource); + SettingsPage(const QString &name); const QString &getName(); - const QString &getIconResource(); virtual bool filterElements(const QString &query); @@ -73,10 +72,7 @@ public: protected: QString name_; - QString iconResource_; - SettingsDialogTab *tab_; - pajlada::Signals::NoArgSignal onCancel_; std::vector managedConnections_; }; From 70e5bd1bfd6ba12c285969550e6c24a3e13537b9 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 01:59:58 +0100 Subject: [PATCH 19/67] lazily initialize settings pages --- src/widgets/dialogs/SettingsDialog.cpp | 61 +++++++++---------- src/widgets/dialogs/SettingsDialog.hpp | 5 +- src/widgets/helper/SettingsDialogTab.cpp | 18 +++++- src/widgets/helper/SettingsDialogTab.hpp | 15 +++-- src/widgets/settingspages/AboutPage.cpp | 1 - src/widgets/settingspages/AccountsPage.cpp | 1 - src/widgets/settingspages/CommandPage.cpp | 1 - .../settingspages/ExternalToolsPage.cpp | 1 - src/widgets/settingspages/GeneralPage.cpp | 5 +- .../settingspages/HighlightingPage.cpp | 1 - src/widgets/settingspages/IgnoresPage.cpp | 1 - .../settingspages/KeyboardSettingsPage.cpp | 1 - src/widgets/settingspages/ModerationPage.cpp | 1 - .../settingspages/NotificationPage.cpp | 1 - src/widgets/settingspages/SettingsPage.cpp | 11 +--- src/widgets/settingspages/SettingsPage.hpp | 5 +- 16 files changed, 63 insertions(+), 66 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 8deaa830..a3dbf345 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -107,7 +107,8 @@ void SettingsDialog::filterElements(const QString &text) for (auto &&tab : this->tabs_) { // filterElements returns true if anything on the page matches the search query - tab->setVisible(tab->page()->filterElements(text)); + tab->setVisible(tab->name().contains(text, Qt::CaseInsensitive) || + tab->page()->filterElements(text)); } // find next visible page @@ -157,36 +158,30 @@ void SettingsDialog::addTabs() this->ui_.tabContainer->setContentsMargins(0, 20, 0, 20); - this->addTab(new GeneralPage, ":/settings/about.svg"); - + // clang-format off + this->addTab([]{return new GeneralPage;}, "General", ":/settings/about.svg"); this->ui_.tabContainer->addSpacing(16); - - this->addTab(new AccountsPage, ":/settings/accounts.svg"); - + this->addTab([]{return new AccountsPage;}, "Accounts", ":/settings/accounts.svg"); this->ui_.tabContainer->addSpacing(16); - - this->addTab(new CommandPage, ":/settings/commands.svg"); - this->addTab(new HighlightingPage, ":/settings/notifications.svg"); - this->addTab(new IgnoresPage, ":/settings/ignore.svg"); - + this->addTab([]{return new CommandPage;}, "Commands", ":/settings/commands.svg"); + this->addTab([]{return new HighlightingPage;}, "Highlights", ":/settings/notifications.svg"); + this->addTab([]{return new IgnoresPage;}, "Ignores", ":/settings/ignore.svg"); this->ui_.tabContainer->addSpacing(16); - - this->addTab(new KeyboardSettingsPage, ":/settings/keybinds.svg"); - this->addTab(new ModerationPage, ":/settings/moderation.svg"); - this->addTab(new NotificationPage, ":/settings/notification2.svg"); - this->addTab(new ExternalToolsPage, ":/settings/externaltools.svg"); - + this->addTab([]{return new KeyboardSettingsPage;}, "Keybindings", ":/settings/keybinds.svg"); + this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg"); + this->addTab([]{return new NotificationPage;}, "Notifications", ":/settings/notification2.svg"); + this->addTab([]{return new ExternalToolsPage;}, "External tools", ":/settings/externaltools.svg"); this->ui_.tabContainer->addStretch(1); - this->addTab(new AboutPage, ":/settings/about.svg", Qt::AlignBottom); + this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", Qt::AlignBottom); + // clang-format on } -void SettingsDialog::addTab(SettingsPage *page, const QString &iconPath, +void SettingsDialog::addTab(std::function page, + const QString &name, const QString &iconPath, Qt::Alignment alignment) { - auto tab = new SettingsDialogTab(this, page, iconPath); - page->setTab(tab); + auto tab = new SettingsDialogTab(this, std::move(page), name, iconPath); - this->ui_.pageStack->addWidget(page); this->ui_.tabContainer->addWidget(tab, 0, alignment); this->tabs_.push_back(tab); @@ -198,6 +193,15 @@ void SettingsDialog::addTab(SettingsPage *page, const QString &iconPath, void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser) { + // add page if it's not been added yet + [&] { + for (int i = 0; i < this->ui_.pageStack->count(); i++) + if (this->ui_.pageStack->itemAt(i)->widget() == tab->page()) + return; + + this->ui_.pageStack->addWidget(tab->page()); + }(); + this->ui_.pageStack->setCurrentWidget(tab->page()); if (this->selectedTab_ != nullptr) @@ -231,18 +235,13 @@ SettingsDialogTab *SettingsDialog::tab(SettingsTabId id) return nullptr; } -void SettingsDialog::selectPage(SettingsPage *page) -{ - assert(page); - assert(page->tab()); - - this->selectTab(page->tab()); -} - void SettingsDialog::showDialog(SettingsDialogPreference preferredTab) { static SettingsDialog *instance = new SettingsDialog(); - instance->refresh(); + static bool hasShownBefore = false; + if (hasShownBefore) + instance->refresh(); + hasShownBefore = true; switch (preferredTab) { diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 6e282d13..84cdb98d 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "widgets/helper/SettingsDialogTab.hpp" @@ -49,10 +50,10 @@ private: void initUi(); void addTabs(); - void addTab(SettingsPage *page, const QString &iconPath, + void addTab(std::function page, const QString &name, + const QString &iconPath, Qt::Alignment alignment = Qt::AlignTop); void selectTab(SettingsDialogTab *tab, const bool byUser = true); - void selectPage(SettingsPage *page); void selectTab(SettingsTabId id); SettingsDialogTab *tab(SettingsTabId id); void filterElements(const QString &query); diff --git a/src/widgets/helper/SettingsDialogTab.cpp b/src/widgets/helper/SettingsDialogTab.cpp index a5bc7145..5ad366ab 100644 --- a/src/widgets/helper/SettingsDialogTab.cpp +++ b/src/widgets/helper/SettingsDialogTab.cpp @@ -8,14 +8,16 @@ namespace chatterino { SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog, - SettingsPage *_page, QString imageFileName, + std::function _lazyPage, + const QString &name, QString imageFileName, SettingsTabId id) : BaseWidget(_dialog) , dialog_(_dialog) - , page_(_page) + , lazyPage_(std::move(_lazyPage)) , id_(id) + , name_(name) { - this->ui_.labelText = page_->getName(); + this->ui_.labelText = name; this->ui_.icon.addFile(imageFileName); this->setCursor(QCursor(Qt::PointingHandCursor)); @@ -38,6 +40,11 @@ void SettingsDialogTab::setSelected(bool _selected) SettingsPage *SettingsDialogTab::page() { + if (this->page_) + return this->page_; + + this->page_ = this->lazyPage_(); + this->page_->setTab(this); return this->page_; } @@ -74,6 +81,11 @@ void SettingsDialogTab::mousePressEvent(QMouseEvent *event) this->setFocus(); } +const QString &SettingsDialogTab::name() const +{ + return name_; +} + SettingsTabId SettingsDialogTab::id() const { return id_; diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index 29b99478..49dc9ff2 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace chatterino { @@ -22,13 +23,17 @@ class SettingsDialogTab : public BaseWidget Q_OBJECT public: - SettingsDialogTab(SettingsDialog *dialog_, SettingsPage *page_, - QString imageFileName, SettingsTabId id = {}); + SettingsDialogTab(SettingsDialog *dialog_, + std::function page_, + const QString &name, QString imageFileName, + SettingsTabId id = {}); void setSelected(bool selected_); SettingsPage *page(); SettingsTabId id() const; + const QString &name() const; + signals: void selectedChanged(bool); @@ -42,9 +47,11 @@ private: } ui_; // Parent settings dialog - SettingsDialog *dialog_; - SettingsPage *page_; + SettingsDialog *dialog_{}; + SettingsPage *page_{}; + std::function lazyPage_; SettingsTabId id_; + QString name_; bool selected_ = false; }; diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 1371bbee..4a51feda 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -18,7 +18,6 @@ namespace chatterino { AboutPage::AboutPage() - : SettingsPage("About") { LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/AccountsPage.cpp b/src/widgets/settingspages/AccountsPage.cpp index 1969e3fc..55573877 100644 --- a/src/widgets/settingspages/AccountsPage.cpp +++ b/src/widgets/settingspages/AccountsPage.cpp @@ -17,7 +17,6 @@ namespace chatterino { AccountsPage::AccountsPage() - : SettingsPage("Accounts") { auto *app = getApp(); diff --git a/src/widgets/settingspages/CommandPage.cpp b/src/widgets/settingspages/CommandPage.cpp index d565a10f..99b83ec9 100644 --- a/src/widgets/settingspages/CommandPage.cpp +++ b/src/widgets/settingspages/CommandPage.cpp @@ -33,7 +33,6 @@ namespace { } // namespace CommandPage::CommandPage() - : SettingsPage("Commands") { auto app = getApp(); diff --git a/src/widgets/settingspages/ExternalToolsPage.cpp b/src/widgets/settingspages/ExternalToolsPage.cpp index f7aad15b..648af25b 100644 --- a/src/widgets/settingspages/ExternalToolsPage.cpp +++ b/src/widgets/settingspages/ExternalToolsPage.cpp @@ -12,7 +12,6 @@ namespace chatterino { ExternalToolsPage::ExternalToolsPage() - : SettingsPage("External tools") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.setLayoutType(); diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 38b3b4e3..bbc972a7 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -237,7 +237,6 @@ bool SettingsLayout::filterElements(const QString &query) } GeneralPage::GeneralPage() - : SettingsPage("General") { auto y = new QVBoxLayout; auto scroll = new QScrollArea; @@ -263,9 +262,7 @@ GeneralPage::GeneralPage() bool GeneralPage::filterElements(const QString &query) { if (this->settingsLayout_) - return this->settingsLayout_->filterElements(query) || - this->name_.contains(query, Qt::CaseInsensitive) || - query.isEmpty(); + return this->settingsLayout_->filterElements(query) || query.isEmpty(); else return false; } diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 05a71bd7..2612454b 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -29,7 +29,6 @@ namespace chatterino { HighlightingPage::HighlightingPage() - : SettingsPage("Highlights") { auto app = getApp(); LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index af36ecad..9cc65788 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -29,7 +29,6 @@ static void addUsersTab(IgnoresPage &page, LayoutCreator box, QStringListModel &model); IgnoresPage::IgnoresPage() - : SettingsPage("Ignores") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.setLayoutType(); diff --git a/src/widgets/settingspages/KeyboardSettingsPage.cpp b/src/widgets/settingspages/KeyboardSettingsPage.cpp index 0dd557a7..d09bb97d 100644 --- a/src/widgets/settingspages/KeyboardSettingsPage.cpp +++ b/src/widgets/settingspages/KeyboardSettingsPage.cpp @@ -8,7 +8,6 @@ namespace chatterino { KeyboardSettingsPage::KeyboardSettingsPage() - : SettingsPage("Keybindings") { auto layout = LayoutCreator(this).setLayoutType(); diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 88d8ac9e..479d0a7c 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -73,7 +73,6 @@ QString fetchLogDirectorySize() } ModerationPage::ModerationPage() - : SettingsPage("Moderation") { auto app = getApp(); LayoutCreator layoutCreator(this); diff --git a/src/widgets/settingspages/NotificationPage.cpp b/src/widgets/settingspages/NotificationPage.cpp index 968c34f5..27c1da4b 100644 --- a/src/widgets/settingspages/NotificationPage.cpp +++ b/src/widgets/settingspages/NotificationPage.cpp @@ -21,7 +21,6 @@ namespace chatterino { NotificationPage::NotificationPage() - : SettingsPage("Notifications") { LayoutCreator layoutCreator(this); auto layout = layoutCreator.emplace().withoutMargin(); diff --git a/src/widgets/settingspages/SettingsPage.cpp b/src/widgets/settingspages/SettingsPage.cpp index 40f6e049..78b9d938 100644 --- a/src/widgets/settingspages/SettingsPage.cpp +++ b/src/widgets/settingspages/SettingsPage.cpp @@ -64,20 +64,13 @@ bool filterItemsRec(QObject *object, const QString &query) return any; } -SettingsPage::SettingsPage(const QString &name) - : name_(name) +SettingsPage::SettingsPage() { } bool SettingsPage::filterElements(const QString &query) { - return filterItemsRec(this, query) || query.isEmpty() || - this->name_.contains(query, Qt::CaseInsensitive); -} - -const QString &SettingsPage::getName() -{ - return this->name_; + return filterItemsRec(this, query) || query.isEmpty(); } SettingsDialogTab *SettingsPage::tab() const diff --git a/src/widgets/settingspages/SettingsPage.hpp b/src/widgets/settingspages/SettingsPage.hpp index eeb8b960..d3a9ee11 100644 --- a/src/widgets/settingspages/SettingsPage.hpp +++ b/src/widgets/settingspages/SettingsPage.hpp @@ -47,9 +47,7 @@ class SettingsPage : public QFrame Q_OBJECT public: - SettingsPage(const QString &name); - - const QString &getName(); + SettingsPage(); virtual bool filterElements(const QString &query); @@ -71,7 +69,6 @@ public: } protected: - QString name_; SettingsDialogTab *tab_; pajlada::Signals::NoArgSignal onCancel_; std::vector managedConnections_; From 927ee09221203d78800914ca5dd4f672ed71361c Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 02:07:34 +0100 Subject: [PATCH 20/67] reordered resize call --- src/widgets/dialogs/SettingsDialog.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index a3dbf345..a15b214d 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -26,18 +26,15 @@ SettingsDialog::SettingsDialog() : BaseWindow(BaseWindow::DisableCustomScaling) { this->setWindowTitle("Chatterino Settings"); - + this->resize(815, 600); this->themeChangedEvent(); this->scaleChangedEvent(this->scale()); this->initUi(); this->addTabs(); - this->overrideBackgroundColor_ = QColor("#111111"); this->scaleChangedEvent( this->scale()); // execute twice to fix performance + width of item - - this->resize(815, 600); } void SettingsDialog::initUi() From a2d21510acf7cd15dd8be8a8cadfc8c08411f4a8 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 02:13:31 +0100 Subject: [PATCH 21/67] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 201b0be8..1e69784f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,3 +3,4 @@ ## Unversioned - Minor: Emotes in the emote popup are now sorted in the same order as the tab completion (#1549) - Bugfix: Fix preview on hover not working when Animated emotes options was disabled (#1546) +- Settings open faster From 5a2633ef5cf6c1df1e72bd01e6d0a1039ba2932d Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 02:16:17 +0100 Subject: [PATCH 22/67] fixed bug --- src/widgets/dialogs/SettingsDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index a15b214d..e33c260e 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -104,8 +104,8 @@ void SettingsDialog::filterElements(const QString &text) for (auto &&tab : this->tabs_) { // filterElements returns true if anything on the page matches the search query - tab->setVisible(tab->name().contains(text, Qt::CaseInsensitive) || - tab->page()->filterElements(text)); + tab->setVisible(tab->page()->filterElements(text) || + tab->name().contains(text, Qt::CaseInsensitive)); } // find next visible page From 03cf6e81ffb7fc871968c4a72c0f8067a607bd41 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 21 Feb 2020 03:01:48 +0100 Subject: [PATCH 23/67] fixed spaces and margins in general settings --- resources/qss/settings.qss | 1 - src/widgets/dialogs/SettingsDialog.cpp | 19 +++++++++---------- src/widgets/settingspages/GeneralPage.cpp | 12 ++++++++---- src/widgets/settingspages/GeneralPage.hpp | 6 ++++++ 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/resources/qss/settings.qss b/resources/qss/settings.qss index 590dff5f..a3172fd6 100644 --- a/resources/qss/settings.qss +++ b/resources/qss/settings.qss @@ -39,7 +39,6 @@ chatterino--TitleLabel { font-family: "Segoe UI light"; font-size: 24px; color: #4FC3F7; - margin-top: 16px; } chatterino--DescriptionLabel { diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index e33c260e..5fc79780 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -33,8 +33,7 @@ SettingsDialog::SettingsDialog() this->initUi(); this->addTabs(); this->overrideBackgroundColor_ = QColor("#111111"); - this->scaleChangedEvent( - this->scale()); // execute twice to fix performance + width of item + this->scaleChangedEvent(this->scale()); // execute twice to width of item } void SettingsDialog::initUi() @@ -67,13 +66,9 @@ void SettingsDialog::initUi() .assign(&this->ui_.tabContainer); // right side (pages) - auto right = - centerBox.emplace().withoutMargin().withoutSpacing(); - { - right.emplace() - .assign(&this->ui_.pageStack) - .withoutMargin(); - } + centerBox.emplace() + .assign(&this->ui_.pageStack) + .withoutMargin(); this->ui_.pageStack->setMargin(0); @@ -155,6 +150,8 @@ void SettingsDialog::addTabs() this->ui_.tabContainer->setContentsMargins(0, 20, 0, 20); + // Constructors are wrapped in std::function to remove some strain from first time loading. + // clang-format off this->addTab([]{return new GeneralPage;}, "General", ":/settings/about.svg"); this->ui_.tabContainer->addSpacing(16); @@ -268,8 +265,10 @@ void SettingsDialog::showDialog(SettingsDialogPreference preferredTab) void SettingsDialog::refresh() { + // Resets the cancel button. getSettings()->saveSnapshot(); + // Updates tabs. for (auto *tab : this->tabs_) { tab->page()->onShow(); @@ -300,7 +299,7 @@ void SettingsDialog::themeChangedEvent() BaseWindow::themeChangedEvent(); QPalette palette; - palette.setColor(QPalette::Background, QColor("#111")); + palette.setColor(QPalette::Window, QColor("#111")); this->setPalette(palette); } diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index bbc972a7..086b7a6a 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -74,14 +74,16 @@ namespace { TitleLabel *SettingsLayout::addTitle(const QString &title) { - auto label = new TitleLabel(title + ":"); + // space + if (!this->groups_.empty()) + this->addWidget(this->groups_.back().space = new Space); - if (this->count() == 0) - label->setStyleSheet("margin-top: 0"); + // title + auto label = new TitleLabel(title + ":"); this->addWidget(label); // groups - this->groups_.push_back(Group{title, label, {}}); + this->groups_.push_back(Group{title, label, nullptr, {}}); return label; } @@ -228,6 +230,8 @@ bool SettingsLayout::filterElements(const QString &query) } } + if (group.space) + group.space->setVisible(groupAny); group.title->setVisible(groupAny); any |= groupAny; } diff --git a/src/widgets/settingspages/GeneralPage.hpp b/src/widgets/settingspages/GeneralPage.hpp index 76e3dc46..d0dc0e4e 100644 --- a/src/widgets/settingspages/GeneralPage.hpp +++ b/src/widgets/settingspages/GeneralPage.hpp @@ -17,6 +17,11 @@ class QComboBox; namespace chatterino { +class Space : public QLabel +{ + Q_OBJECT +}; + class TitleLabel : public QLabel { Q_OBJECT @@ -162,6 +167,7 @@ private: struct Group { QString name; QWidget *title{}; + Space *space{}; std::vector widgets; }; From 2b5c6ffe338eb4229d612206cce4d185a0828bab Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 22 Feb 2020 06:05:36 -0500 Subject: [PATCH 24/67] Remove chatroom code (#1561) People who had chatrooms still open should notice no difference at all, since it will just try to join a channel with the chatroom name. Documentation for the closing/deprecation of twitch chat rooms: https://discuss.dev.twitch.tv/t/rooms-are-closing-how-it-affects-the-api-and-irc/22996 https://dev.twitch.tv/docs/change-log (see 2020-01-10 entry) --- CHANGELOG.md | 1 + chatterino.pro | 2 - src/providers/twitch/ChatroomChannel.cpp | 67 ------------------------ src/providers/twitch/ChatroomChannel.hpp | 28 ---------- src/providers/twitch/TwitchIrcServer.cpp | 30 +---------- 5 files changed, 3 insertions(+), 125 deletions(-) delete mode 100644 src/providers/twitch/ChatroomChannel.cpp delete mode 100644 src/providers/twitch/ChatroomChannel.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e69784f..a76cf906 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,3 +4,4 @@ - Minor: Emotes in the emote popup are now sorted in the same order as the tab completion (#1549) - Bugfix: Fix preview on hover not working when Animated emotes options was disabled (#1546) - Settings open faster +- Dev: Fully remove Twitch Chatroom support diff --git a/chatterino.pro b/chatterino.pro index 6cf3d812..02950839 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -178,7 +178,6 @@ SOURCES += \ src/providers/irc/IrcConnection2.cpp \ src/providers/irc/IrcServer.cpp \ src/providers/LinkResolver.cpp \ - src/providers/twitch/ChatroomChannel.cpp \ src/providers/twitch/IrcMessageHandler.cpp \ src/providers/twitch/PartialTwitchUser.cpp \ src/providers/twitch/PubsubActions.cpp \ @@ -382,7 +381,6 @@ HEADERS += \ src/providers/irc/IrcConnection2.hpp \ src/providers/irc/IrcServer.hpp \ src/providers/LinkResolver.hpp \ - src/providers/twitch/ChatroomChannel.hpp \ src/providers/twitch/EmoteValue.hpp \ src/providers/twitch/IrcMessageHandler.hpp \ src/providers/twitch/PartialTwitchUser.hpp \ diff --git a/src/providers/twitch/ChatroomChannel.cpp b/src/providers/twitch/ChatroomChannel.cpp deleted file mode 100644 index 5f393e42..00000000 --- a/src/providers/twitch/ChatroomChannel.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "ChatroomChannel.hpp" - -#include -#include "TwitchApi.hpp" -#include "common/Common.hpp" -#include "messages/Emote.hpp" -#include "providers/bttv/BttvEmotes.hpp" -#include "providers/bttv/LoadBttvChannelEmote.hpp" -#include "singletons/Emotes.hpp" - -namespace chatterino { - -ChatroomChannel::ChatroomChannel(const QString &channelName, - TwitchBadges &globalTwitchBadges, - BttvEmotes &globalBttv, FfzEmotes &globalFfz) - : TwitchChannel(channelName, globalTwitchBadges, globalBttv, globalFfz) -{ - auto listRef = channelName.splitRef(":"); - if (listRef.size() > 2) - { - this->chatroomOwnerId = listRef[1].toString(); - } -} - -void ChatroomChannel::refreshBTTVChannelEmotes() -{ - if (this->chatroomOwnerId.isEmpty()) - { - return; - } - TwitchApi::findUserName( - this->chatroomOwnerId, - [this, weak = weakOf(this)](QString username) { - BttvEmotes::loadChannel(username, [this, weak](auto &&emoteMap) { - if (auto shared = weak.lock()) - this->bttvEmotes_.set( - std::make_shared(std::move(emoteMap))); - }); - if (auto shared = weak.lock()) - { - this->chatroomOwnerName = username; - } - }); -} -void ChatroomChannel::refreshFFZChannelEmotes() -{ - if (this->chatroomOwnerId.isEmpty()) - { - return; - } - FfzEmotes::loadChannel( - this->chatroomOwnerId, - [this](auto &&emoteMap) { - this->ffzEmotes_.set( - std::make_shared(std::move(emoteMap))); - }, - [this](auto &&modBadge) { - this->ffzCustomModBadge_.set(std::move(modBadge)); - }); -} - -const QString &ChatroomChannel::getDisplayName() const -{ - return this->chatroomOwnerName; -} - -} // namespace chatterino diff --git a/src/providers/twitch/ChatroomChannel.hpp b/src/providers/twitch/ChatroomChannel.hpp deleted file mode 100644 index 2ed24914..00000000 --- a/src/providers/twitch/ChatroomChannel.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "TwitchChannel.hpp" - -#include -#include - -namespace chatterino { - -class ChatroomChannel : public TwitchChannel -{ -protected: - explicit ChatroomChannel(const QString &channelName, - TwitchBadges &globalTwitchBadges, - BttvEmotes &globalBttv, FfzEmotes &globalFfz); - virtual void refreshBTTVChannelEmotes() override; - virtual void refreshFFZChannelEmotes() override; - virtual const QString &getDisplayName() const override; - - QString chatroomOwnerId; - QString chatroomOwnerName; - - friend class TwitchIrcServer; - friend class TwitchMessageBuilder; - friend class IrcMessageHandler; -}; - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 45f50cec..d13850b4 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -10,7 +10,6 @@ #include "controllers/highlights/HighlightController.hpp" #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" -#include "providers/twitch/ChatroomChannel.hpp" #include "providers/twitch/IrcMessageHandler.hpp" #include "providers/twitch/PubsubClient.hpp" #include "providers/twitch/TwitchAccount.hpp" @@ -24,21 +23,6 @@ using namespace std::chrono_literals; namespace chatterino { -namespace { - bool isChatroom(const QString &channel) - { - if (channel.left(10) == "chatrooms:") - { - auto reflist = channel.splitRef(':'); - if (reflist.size() == 3) - { - return true; - } - } - return false; - } -} // namespace - TwitchIrcServer::TwitchIrcServer() : whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers)) , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions)) @@ -100,18 +84,8 @@ void TwitchIrcServer::initializeConnection(IrcConnection *connection, std::shared_ptr TwitchIrcServer::createChannel( const QString &channelName) { - std::shared_ptr channel; - if (isChatroom(channelName)) - { - channel = std::static_pointer_cast( - std::shared_ptr(new ChatroomChannel( - channelName, this->twitchBadges, this->bttv, this->ffz))); - } - else - { - channel = std::shared_ptr(new TwitchChannel( - channelName, this->twitchBadges, this->bttv, this->ffz)); - } + auto channel = std::shared_ptr(new TwitchChannel( + channelName, this->twitchBadges, this->bttv, this->ffz)); channel->initialize(); channel->sendMessageSignal.connect( From e1838154ff29a1dd8527c5cc93ab7d30e5e588fd Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 17:10:49 +0100 Subject: [PATCH 25/67] refactored SignalVector --- src/common/SignalVector.hpp | 337 ++++++++---------- .../accounts/AccountController.cpp | 4 +- .../highlights/HighlightController.cpp | 2 +- src/providers/twitch/TwitchMessageBuilder.cpp | 13 +- 4 files changed, 161 insertions(+), 195 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 2995cbba..95006011 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -4,243 +4,210 @@ #include #include #include -#include #include #include "debug/AssertInGuiThread.hpp" namespace chatterino { -template -struct SignalVectorItemArgs { - const TVectorItem &item; +template +struct SignalVectorItemEvent { + const T &item; int index; void *caller; }; -template -class ReadOnlySignalVector : boost::noncopyable +template +class SignalVector : boost::noncopyable { - using VecIt = typename std::vector::iterator; - public: - struct Iterator - : public std::iterator { - Iterator(VecIt &&it, std::shared_mutex &mutex) - : it_(std::move(it)) - , lock_(mutex) - , mutex_(mutex) - { - } + pajlada::Signals::Signal> itemInserted; + pajlada::Signals::Signal> itemRemoved; + pajlada::Signals::NoArgSignal delayedItemsChanged; - Iterator(const Iterator &other) - : it_(other.it_) - , lock_(other.mutex_) - , mutex_(other.mutex_) - { - } - - Iterator &operator=(const Iterator &other) - { - this->lock_ = std::shared_lock(other.mutex_.get()); - this->mutex_ = other.mutex_; - - return *this; - } - - TVectorItem &operator*() - { - return it_.operator*(); - } - - Iterator &operator++() - { - ++this->it_; - return *this; - } - - bool operator==(const Iterator &other) - { - return this->it_ == other.it_; - } - - bool operator!=(const Iterator &other) - { - return this->it_ != other.it_; - } - - auto operator-(const Iterator &other) - { - return this->it_ - other.it_; - } - - private: - VecIt it_; - std::shared_lock lock_; - std::reference_wrapper mutex_; - }; - - ReadOnlySignalVector() + SignalVector() + : readOnly_(new std::vector()) { QObject::connect(&this->itemsChangedTimer_, &QTimer::timeout, [this] { this->delayedItemsChanged.invoke(); }); this->itemsChangedTimer_.setInterval(100); this->itemsChangedTimer_.setSingleShot(true); } - virtual ~ReadOnlySignalVector() = default; - pajlada::Signals::Signal> itemInserted; - pajlada::Signals::Signal> itemRemoved; - pajlada::Signals::NoArgSignal delayedItemsChanged; - - Iterator begin() const + SignalVector(std::function &&compare) + : SignalVector() { - return Iterator( - const_cast &>(this->vector_).begin(), - this->mutex_); + itemCompare_ = std::move(compare); } - Iterator end() const + virtual bool isSorted() const { - return Iterator( - const_cast &>(this->vector_).end(), - this->mutex_); + return bool(this->itemCompare_); } - bool empty() const + /// A read-only version of the vector which can be used concurrently. + std::shared_ptr> readOnly() { - std::shared_lock lock(this->mutex_); - - return this->vector_.empty(); + return this->readOnly_; } - const std::vector &getVector() const + /// This may only be called from the GUI thread. + /// + /// @param item + /// Item to be inserted. + /// @param proposedIndex + /// Index to insert at. `-1` will append at the end. + /// Will be ignored if the vector is sorted. + /// @param caller + /// Caller id which will be passed in the itemInserted and itemRemoved + /// signals. + int insert(const T &item, int index = -1, void *caller = nullptr) { assertInGuiThread(); - return this->vector_; + if (this->isSorted()) + { + auto it = std::lower_bound(this->items_.begin(), this->items_.end(), + item, this->itemCompare_); + index = it - this->items_.begin(); + this->items_.insert(it, item); + } + else + { + if (index == -1) + index = this->items_.size(); + else + assert(index >= 0 && index <= this->items_.size()); + + this->items_.insert(this->items_.begin() + index, item); + } + + SignalVectorItemEvent args{item, index, caller}; + this->itemInserted.invoke(args); + this->itemsChanged_(); + + return index; } - std::vector cloneVector() const + /// This may only be called from the GUI thread. + /// + /// @param item + /// Item to be appended. + /// @param caller + /// Caller id which will be passed in the itemInserted and itemRemoved + /// signals. + int append(const T &item, void *caller = nullptr) { - std::shared_lock lock(this->mutex_); - - return this->vector_; + return this->insertItem(item, -1, caller); } - void invokeDelayedItemsChanged() + void removeAt(int index, void *caller = nullptr) + { + assertInGuiThread(); + assert(index >= 0 && index < int(this->items_.size())); + + T item = this->items_[index]; + this->items_.erase(this->items_.begin() + index); + + SignalVectorItemEvent args{item, index, caller}; + this->itemRemoved.invoke(args); + + this->itemsChanged_(); + } + + // compatability + [[deprecated]] int insertItem(const T &item, int proposedIndex = -1, + void *caller = nullptr) + { + return this->insert(item, proposedIndex, caller); + } + + [[deprecated]] int appendItem(const T &item, void *caller = nullptr) + { + return this->append(item, caller); + } + + [[deprecated]] void removeItem(int index, void *caller = nullptr) + { + this->removeAt(index, caller); + } + + [[deprecated]] const std::vector &getVector() const { assertInGuiThread(); + return this->items_; + } + + [[deprecated]] std::vector cloneVector() + { + return *this->readOnly(); + } + + // mirror vector functions + auto begin() const + { + assertInGuiThread(); + return this->items_.begin(); + } + + auto end() const + { + assertInGuiThread(); + return this->items_.end(); + } + + decltype(auto) operator[](size_t index) + { + assertInGuiThread(); + return this->items[index]; + } + + auto empty() + { + assertInGuiThread(); + return this->items_.empty(); + } + +private: + void itemsChanged_() + { + // emit delayed event if (!this->itemsChangedTimer_.isActive()) { this->itemsChangedTimer_.start(); } + + // update concurrent version + this->readOnly_ = std::make_shared>(this->items_); } - virtual bool isSorted() const = 0; - -protected: - std::vector vector_; + std::vector items_; + std::shared_ptr> readOnly_; QTimer itemsChangedTimer_; - mutable std::shared_mutex mutex_; + std::function itemCompare_; }; -template -class BaseSignalVector : public ReadOnlySignalVector +// compatability +template +using SignalVectorItemArgs = SignalVectorItemEvent; + +template +using ReadOnlySignalVector = SignalVector; + +template +using BaseSignalVector = SignalVector; + +template +using UnsortedSignalVector = SignalVector; + +template +class SortedSignalVector : public SignalVector { public: - // returns the actual index of the inserted item - virtual int insertItem(const TVectorItem &item, int proposedIndex = -1, - void *caller = nullptr) = 0; - - void removeItem(int index, void *caller = nullptr) + SortedSignalVector() + : SignalVector(Compare{}) { - assertInGuiThread(); - std::unique_lock lock(this->mutex_); - - assert(index >= 0 && index < int(this->vector_.size())); - - TVectorItem item = this->vector_[index]; - - this->vector_.erase(this->vector_.begin() + index); - lock.unlock(); // manual unlock - - SignalVectorItemArgs args{item, index, caller}; - this->itemRemoved.invoke(args); - - this->invokeDelayedItemsChanged(); - } - - int appendItem(const TVectorItem &item, void *caller = nullptr) - { - return this->insertItem(item, -1, caller); - } -}; - -template -class UnsortedSignalVector : public BaseSignalVector -{ -public: - virtual int insertItem(const TVectorItem &item, int index = -1, - void *caller = nullptr) override - { - assertInGuiThread(); - - { - std::unique_lock lock(this->mutex_); - if (index == -1) - { - index = this->vector_.size(); - } - else - { - assert(index >= 0 && index <= this->vector_.size()); - } - - this->vector_.insert(this->vector_.begin() + index, item); - } - - SignalVectorItemArgs args{item, index, caller}; - this->itemInserted.invoke(args); - this->invokeDelayedItemsChanged(); - return index; - } - - virtual bool isSorted() const override - { - return false; - } -}; - -template -class SortedSignalVector : public BaseSignalVector -{ -public: - virtual int insertItem(const TVectorItem &item, int = -1, - void *caller = nullptr) override - { - assertInGuiThread(); - int index = -1; - - { - std::unique_lock lock(this->mutex_); - - auto it = std::lower_bound(this->vector_.begin(), - this->vector_.end(), item, Compare{}); - index = it - this->vector_.begin(); - this->vector_.insert(it, item); - } - - SignalVectorItemArgs args{item, index, caller}; - this->itemInserted.invoke(args); - this->invokeDelayedItemsChanged(); - return index; - } - - virtual bool isSorted() const override - { - return true; } }; diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index cdaa5328..bd32401a 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -30,10 +30,10 @@ AccountController::AccountController() case ProviderId::Twitch: { if (args.caller != this) { - auto accs = this->twitch.accounts.cloneVector(); + auto &&accs = this->twitch.accounts; auto it = std::find(accs.begin(), accs.end(), args.item); assert(it != accs.end()); - this->twitch.accounts.removeItem(it - accs.begin(), this); + this->twitch.accounts.removeAt(it - accs.begin(), this); } } break; diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index d030f77b..a59218f3 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -87,7 +87,7 @@ HighlightBlacklistModel *HighlightController::createBlacklistModel( bool HighlightController::blacklistContains(const QString &username) { - for (const auto &blacklistedUser : this->blacklistedUsers) + for (const auto &blacklistedUser : *this->blacklistedUsers.readOnly()) { if (blacklistedUser.isMatch(username)) { diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 0d468047..2e1d18b4 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -170,7 +170,8 @@ bool TwitchMessageBuilder::isIgnored() const auto app = getApp(); // TODO(pajlada): Do we need to check if the phrase is valid first? - for (const auto &phrase : app->ignores->phrases) + auto phrases = app->ignores->phrases.readOnly(); + for (const auto &phrase : *phrases) { if (phrase.isBlock() && phrase.isMatch(this->originalMessage_)) { @@ -765,7 +766,7 @@ void TwitchMessageBuilder::runIgnoreReplaces( std::vector> &twitchEmotes) { auto app = getApp(); - const auto &phrases = app->ignores->phrases; + auto phrases = app->ignores->phrases.readOnly(); auto removeEmotesInRange = [](int pos, int len, std::vector> @@ -828,7 +829,7 @@ void TwitchMessageBuilder::runIgnoreReplaces( } }; - for (const auto &phrase : phrases) + for (const auto &phrase : *phrases) { if (phrase.isBlock()) { @@ -1056,11 +1057,9 @@ void TwitchMessageBuilder::parseHighlights() */ } - std::vector userHighlights = - app->highlights->highlightedUsers.cloneVector(); - // Highlight because of sender - for (const HighlightPhrase &userHighlight : userHighlights) + auto userHighlights = app->highlights->highlightedUsers.readOnly(); + for (const HighlightPhrase &userHighlight : *userHighlights) { if (!userHighlight.isMatch(this->ircMessage->nick())) { From 4a5dc80bc6cd27194f7f9e8a0c508cdc5ba106cf Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 17:45:59 +0100 Subject: [PATCH 26/67] some more refactoring --- src/common/SignalVector.hpp | 39 ++++++++-------- src/common/SignalVectorModel.hpp | 8 ++-- .../accounts/AccountController.cpp | 2 +- .../commands/CommandController.cpp | 2 +- .../highlights/HighlightController.cpp | 46 ++++++++----------- .../highlights/HighlightController.hpp | 7 --- src/controllers/ignores/IgnoreController.cpp | 2 +- .../moderationactions/ModerationActions.cpp | 2 +- .../notifications/NotificationController.cpp | 12 ++--- src/controllers/pings/PingController.cpp | 6 +-- src/widgets/dialogs/SelectChannelDialog.cpp | 7 ++- src/widgets/dialogs/UserInfoPopup.cpp | 4 +- 12 files changed, 62 insertions(+), 75 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 95006011..bf8cbfc5 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -115,30 +115,33 @@ public: this->itemsChanged_(); } - // compatability - [[deprecated]] int insertItem(const T &item, int proposedIndex = -1, - void *caller = nullptr) - { - return this->insert(item, proposedIndex, caller); - } - - [[deprecated]] int appendItem(const T &item, void *caller = nullptr) - { - return this->append(item, caller); - } - - [[deprecated]] void removeItem(int index, void *caller = nullptr) - { - this->removeAt(index, caller); - } - - [[deprecated]] const std::vector &getVector() const + const std::vector &raw() const { assertInGuiThread(); return this->items_; } + // compatability + [[deprecated("use insert")]] int insertItem(const T &item, + int proposedIndex = -1, + void *caller = nullptr) + { + return this->insert(item, proposedIndex, caller); + } + + [[deprecated("use append")]] int appendItem(const T &item, + void *caller = nullptr) + { + return this->append(item, caller); + } + + [[deprecated("use removeAt")]] void removeItem(int index, + void *caller = nullptr) + { + this->removeAt(index, caller); + } + [[deprecated]] std::vector cloneVector() { return *this->readOnly(); diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index bb7cd7c0..4fe39edd 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -52,7 +52,7 @@ public: }; int i = 0; - for (const TVectorItem &item : vec->getVector()) + for (const TVectorItem &item : vec->raw()) { SignalVectorItemArgs args{item, i++, 0}; @@ -272,15 +272,15 @@ public: int from = data->data("chatterino_row_id").toInt(); int to = parent.row(); - if (from < 0 || from > this->vector_->getVector().size() || - to < 0 || to > this->vector_->getVector().size()) + if (from < 0 || from > this->vector_->raw().size() || + to < 0 || to > this->vector_->raw().size()) { return false; } if (from != to) { - auto item = this->vector_->getVector()[from]; + auto item = this->vector_->raw()[from]; this->vector_->removeItem(from); this->vector_->insertItem(item, to); } diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index bd32401a..db65e944 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -16,7 +16,7 @@ AccountController::AccountController() this->twitch.accounts.itemRemoved.connect([this](const auto &args) { if (args.caller != this) { - auto &accs = this->twitch.accounts.getVector(); + auto &accs = this->twitch.accounts.raw(); auto it = std::find(accs.begin(), accs.end(), args.item); assert(it != accs.end()); diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index a6150cfa..75464bbb 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -212,7 +212,7 @@ void CommandController::initialize(Settings &, Paths &paths) // Update the setting when the vector of commands has been updated (most // likely from the settings dialog) this->items_.delayedItemsChanged.connect([this] { // - this->commandsSetting_->setValue(this->items_.getVector()); + this->commandsSetting_->setValue(this->items_.raw()); }); // Load commands from commands.json diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index a59218f3..60df26ff 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -12,38 +12,30 @@ HighlightController::HighlightController() { } +template +inline void persist(SignalVector &vec, const std::string &name) +{ + auto setting = std::make_unique>>(name); + + for (auto &&item : setting->getValue()) + vec.append(item); + + vec.delayedItemsChanged.connect([setting = setting.get(), vec = &vec] { + setting->setValue(vec->raw()); + }); + + // TODO + setting.release(); +} + void HighlightController::initialize(Settings &settings, Paths &paths) { assert(!this->initialized_); this->initialized_ = true; - for (const HighlightPhrase &phrase : this->highlightsSetting_.getValue()) - { - this->phrases.appendItem(phrase); - } - - this->phrases.delayedItemsChanged.connect([this] { // - this->highlightsSetting_.setValue(this->phrases.getVector()); - }); - - for (const HighlightBlacklistUser &blacklistedUser : - this->blacklistSetting_.getValue()) - { - this->blacklistedUsers.appendItem(blacklistedUser); - } - - this->blacklistedUsers.delayedItemsChanged.connect([this] { - this->blacklistSetting_.setValue(this->blacklistedUsers.getVector()); - }); - - for (const HighlightPhrase &user : this->userSetting_.getValue()) - { - this->highlightedUsers.appendItem(user); - } - - this->highlightedUsers.delayedItemsChanged.connect([this] { // - this->userSetting_.setValue(this->highlightedUsers.getVector()); - }); + persist(this->phrases, "/highlighting/highlights"); + persist(this->blacklistedUsers, "/highlighting/blacklist"); + persist(this->highlightedUsers, "/highlighting/users"); } HighlightModel *HighlightController::createModel(QObject *parent) diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index faa4683c..2f61b1a4 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -40,13 +40,6 @@ public: private: bool initialized_ = false; - - ChatterinoSetting> highlightsSetting_ = { - "/highlighting/highlights"}; - ChatterinoSetting> blacklistSetting_ = { - "/highlighting/blacklist"}; - ChatterinoSetting> userSetting_ = { - "/highlighting/users"}; }; } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 5548ee5d..b1028be0 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -18,7 +18,7 @@ void IgnoreController::initialize(Settings &, Paths &) } this->phrases.delayedItemsChanged.connect([this] { // - this->ignoresSetting_.setValue(this->phrases.getVector()); + this->ignoresSetting_.setValue(this->phrases.raw()); }); } diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index 0104c7de..cb68174b 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -27,7 +27,7 @@ void ModerationActions::initialize(Settings &settings, Paths &paths) } this->items.delayedItemsChanged.connect([this] { // - this->setting_->setValue(this->items.getVector()); + this->setting_->setValue(this->items.raw()); }); } diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index b9777c80..ec2f231c 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -31,7 +31,7 @@ void NotificationController::initialize(Settings &settings, Paths &paths) this->channelMap[Platform::Twitch].delayedItemsChanged.connect([this] { // this->twitchSetting_.setValue( - this->channelMap[Platform::Twitch].getVector()); + this->channelMap[Platform::Twitch].raw()); }); /* for (const QString &channelName : this->mixerSetting_.getValue()) { @@ -88,9 +88,9 @@ void NotificationController::removeChannelNotification( const QString &channelName, Platform p) { for (std::vector::size_type i = 0; - i != channelMap[p].getVector().size(); i++) + i != channelMap[p].raw().size(); i++) { - if (channelMap[p].getVector()[i].toLower() == channelName.toLower()) + if (channelMap[p].raw()[i].toLower() == channelName.toLower()) { channelMap[p].removeItem(i); i--; @@ -128,14 +128,14 @@ NotificationModel *NotificationController::createModel(QObject *parent, void NotificationController::fetchFakeChannels() { for (std::vector::size_type i = 0; - i != channelMap[Platform::Twitch].getVector().size(); i++) + i != channelMap[Platform::Twitch].raw().size(); i++) { auto chan = getApp()->twitch.server->getChannelOrEmpty( - channelMap[Platform::Twitch].getVector()[i]); + channelMap[Platform::Twitch].raw()[i]); if (chan->isEmpty()) { getFakeTwitchChannelLiveStatus( - channelMap[Platform::Twitch].getVector()[i]); + channelMap[Platform::Twitch].raw()[i]); } } } diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp index b61d6e79..67bd3ad6 100644 --- a/src/controllers/pings/PingController.cpp +++ b/src/controllers/pings/PingController.cpp @@ -12,7 +12,7 @@ void PingController::initialize(Settings &settings, Paths &paths) } this->channelVector.delayedItemsChanged.connect([this] { // - this->pingSetting_.setValue(this->channelVector.getVector()); + this->pingSetting_.setValue(this->channelVector.raw()); }); } @@ -43,9 +43,9 @@ void PingController::muteChannel(const QString &channelName) void PingController::unmuteChannel(const QString &channelName) { for (std::vector::size_type i = 0; - i != channelVector.getVector().size(); i++) + i != channelVector.raw().size(); i++) { - if (channelVector.getVector()[i].toLower() == channelName.toLower()) + if (channelVector.raw()[i].toLower() == channelName.toLower()) { channelVector.removeItem(i); i--; diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index 25d61ff0..c8e21a18 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -160,13 +160,12 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) view->getTableView(), &QTableView::doubleClicked, [](const QModelIndex &index) { auto editor = new IrcConnectionEditor( - Irc::instance() - .connections.getVector()[size_t(index.row())]); + Irc::instance().connections.raw()[size_t(index.row())]); if (editor->exec() == QDialog::Accepted) { auto data = editor->data(); - auto &&conns = Irc::instance().connections.getVector(); + auto &&conns = Irc::instance().connections.raw(); int i = 0; for (auto &&conn : conns) { @@ -348,7 +347,7 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const ->currentIndex() .row(); - auto &&vector = Irc::instance().connections.getVector(); + auto &&vector = Irc::instance().connections.raw(); if (row >= 0 && row < int(vector.size())) { diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index e530a5bf..bb75a61f 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -343,7 +343,7 @@ void UserInfoPopup::installEvents() else { const auto &vector = - getApp()->highlights->blacklistedUsers.getVector(); + getApp()->highlights->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { @@ -456,7 +456,7 @@ void UserInfoPopup::updateUserData() // get ignoreHighlights state bool isIgnoringHighlights = false; - const auto &vector = getApp()->highlights->blacklistedUsers.getVector(); + const auto &vector = getApp()->highlights->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) From 843e2ad99480a389169d03e7219ee130996d443e Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:31:43 +0100 Subject: [PATCH 27/67] refactored HighlightController --- src/common/SignalVectorModel.hpp | 13 +++-- .../accounts/AccountController.cpp | 2 +- .../commands/CommandController.cpp | 4 +- .../commands/CommandController.hpp | 2 - .../highlights/HighlightBlacklistModel.hpp | 4 +- .../highlights/HighlightController.cpp | 52 ++----------------- .../highlights/HighlightController.hpp | 19 ------- src/controllers/highlights/HighlightModel.hpp | 6 +-- .../highlights/UserHighlightModel.hpp | 3 +- src/controllers/ignores/IgnoreController.cpp | 2 +- .../moderationactions/ModerationActions.cpp | 2 +- .../notifications/NotificationController.cpp | 2 +- src/controllers/pings/PingController.cpp | 2 +- .../taggedusers/TaggedUsersController.cpp | 2 +- src/providers/irc/Irc2.cpp | 2 +- src/providers/twitch/IrcMessageHandler.cpp | 1 - .../settingspages/HighlightingPage.cpp | 16 +++--- 17 files changed, 34 insertions(+), 100 deletions(-) diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index 4fe39edd..31129769 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -25,7 +25,7 @@ public: } } - void init(BaseSignalVector *vec) + void initialize(BaseSignalVector *vec) { this->vector_ = vec; @@ -89,6 +89,13 @@ public: this->afterInit(); } + SignalVectorModel *initialized( + BaseSignalVector *vec) + { + this->initialize(vec); + return this; + } + virtual ~SignalVectorModel() { for (Row &row : this->rows_) @@ -272,8 +279,8 @@ public: int from = data->data("chatterino_row_id").toInt(); int to = parent.row(); - if (from < 0 || from > this->vector_->raw().size() || - to < 0 || to > this->vector_->raw().size()) + if (from < 0 || from > this->vector_->raw().size() || to < 0 || + to > this->vector_->raw().size()) { return false; } diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index db65e944..6d272cf0 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -50,7 +50,7 @@ AccountModel *AccountController::createModel(QObject *parent) { AccountModel *model = new AccountModel(parent); - model->init(&this->accounts_); + model->initialize(&this->accounts_); return model; } diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 75464bbb..418612be 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -234,7 +234,7 @@ void CommandController::save() CommandModel *CommandController::createModel(QObject *parent) { CommandModel *model = new CommandModel(parent); - model->init(&this->items_); + model->initialize(&this->items_); return model; } @@ -245,8 +245,6 @@ QString CommandController::execCommand(const QString &textNoEmoji, QString text = getApp()->emotes->emojis.replaceShortCodes(textNoEmoji); QStringList words = text.split(' ', QString::SkipEmptyParts); - std::lock_guard lock(this->mutex_); - if (words.length() == 0) { return text; diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index 938fe0f3..5435a60b 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -39,8 +39,6 @@ private: QMap commandsMap_; int maxSpaces_ = 0; - std::mutex mutex_; - std::shared_ptr sm_; // Because the setting manager is not initialized until the initialize // function is called (and not in the constructor), we have to diff --git a/src/controllers/highlights/HighlightBlacklistModel.hpp b/src/controllers/highlights/HighlightBlacklistModel.hpp index c3420b06..d4acc474 100644 --- a/src/controllers/highlights/HighlightBlacklistModel.hpp +++ b/src/controllers/highlights/HighlightBlacklistModel.hpp @@ -11,9 +11,9 @@ class HighlightController; class HighlightBlacklistModel : public SignalVectorModel { +public: explicit HighlightBlacklistModel(QObject *parent); -public: enum Column { Pattern = 0, UseRegex = 1, @@ -28,8 +28,6 @@ protected: // turns a row in the model into a vector item virtual void getRowFromItem(const HighlightBlacklistUser &item, std::vector &row) override; - - friend class HighlightController; }; } // namespace chatterino diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index 60df26ff..f341f349 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -1,17 +1,10 @@ #include "HighlightController.hpp" #include "Application.hpp" -#include "controllers/highlights/HighlightBlacklistModel.hpp" -#include "controllers/highlights/HighlightModel.hpp" -#include "controllers/highlights/UserHighlightModel.hpp" -#include "widgets/dialogs/NotificationPopup.hpp" +#include "common/ChatterinoSetting.hpp" namespace chatterino { -HighlightController::HighlightController() -{ -} - template inline void persist(SignalVector &vec, const std::string &name) { @@ -24,7 +17,7 @@ inline void persist(SignalVector &vec, const std::string &name) setting->setValue(vec->raw()); }); - // TODO + // TODO: Delete when appropriate. setting.release(); } @@ -38,65 +31,26 @@ void HighlightController::initialize(Settings &settings, Paths &paths) persist(this->highlightedUsers, "/highlighting/users"); } -HighlightModel *HighlightController::createModel(QObject *parent) -{ - HighlightModel *model = new HighlightModel(parent); - model->init(&this->phrases); - - return model; -} - -UserHighlightModel *HighlightController::createUserModel(QObject *parent) -{ - auto *model = new UserHighlightModel(parent); - model->init(&this->highlightedUsers); - - return model; -} - bool HighlightController::isHighlightedUser(const QString &username) { - const auto &userItems = this->highlightedUsers; - for (const auto &highlightedUser : userItems) + for (const auto &highlightedUser : this->highlightedUsers) { if (highlightedUser.isMatch(username)) - { return true; - } } return false; } -HighlightBlacklistModel *HighlightController::createBlacklistModel( - QObject *parent) -{ - auto *model = new HighlightBlacklistModel(parent); - model->init(&this->blacklistedUsers); - - return model; -} - bool HighlightController::blacklistContains(const QString &username) { for (const auto &blacklistedUser : *this->blacklistedUsers.readOnly()) { if (blacklistedUser.isMatch(username)) - { return true; - } } return false; } -void HighlightController::addHighlight(const MessagePtr &msg) -{ - // static NotificationPopup popup; - - // popup.updatePosition(); - // popup.addMessage(msg); - // popup.show(); -} - } // namespace chatterino diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index 2f61b1a4..5f746c8b 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -1,6 +1,5 @@ #pragma once -#include "common/ChatterinoSetting.hpp" #include "common/SignalVector.hpp" #include "common/Singleton.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" @@ -8,36 +7,18 @@ namespace chatterino { -struct Message; -using MessagePtr = std::shared_ptr; - -class Settings; -class Paths; - -class UserHighlightModel; -class HighlightModel; -class HighlightBlacklistModel; - class HighlightController final : public Singleton { public: - HighlightController(); - virtual void initialize(Settings &settings, Paths &paths) override; UnsortedSignalVector phrases; UnsortedSignalVector blacklistedUsers; UnsortedSignalVector highlightedUsers; - HighlightModel *createModel(QObject *parent); - HighlightBlacklistModel *createBlacklistModel(QObject *parent); - UserHighlightModel *createUserModel(QObject *parent); - bool isHighlightedUser(const QString &username); bool blacklistContains(const QString &username); - void addHighlight(const MessagePtr &msg); - private: bool initialized_ = false; }; diff --git a/src/controllers/highlights/HighlightModel.hpp b/src/controllers/highlights/HighlightModel.hpp index 13b85bca..aa37b4ff 100644 --- a/src/controllers/highlights/HighlightModel.hpp +++ b/src/controllers/highlights/HighlightModel.hpp @@ -7,13 +7,11 @@ namespace chatterino { -class HighlightController; - class HighlightModel : public SignalVectorModel { +public: explicit HighlightModel(QObject *parent); -public: // Used here, in HighlightingPage and in UserHighlightModel enum Column { Pattern = 0, @@ -40,8 +38,6 @@ protected: virtual void customRowSetData(const std::vector &row, int column, const QVariant &value, int role, int rowIndex) override; - - friend class HighlightController; }; } // namespace chatterino diff --git a/src/controllers/highlights/UserHighlightModel.hpp b/src/controllers/highlights/UserHighlightModel.hpp index dcc42a95..de433243 100644 --- a/src/controllers/highlights/UserHighlightModel.hpp +++ b/src/controllers/highlights/UserHighlightModel.hpp @@ -11,6 +11,7 @@ class HighlightController; class UserHighlightModel : public SignalVectorModel { +public: explicit UserHighlightModel(QObject *parent); protected: @@ -21,8 +22,6 @@ protected: virtual void getRowFromItem(const HighlightPhrase &item, std::vector &row) override; - - friend class HighlightController; }; } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index b1028be0..28b511a3 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -25,7 +25,7 @@ void IgnoreController::initialize(Settings &, Paths &) IgnoreModel *IgnoreController::createModel(QObject *parent) { IgnoreModel *model = new IgnoreModel(parent); - model->init(&this->phrases); + model->initialize(&this->phrases); return model; } diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index cb68174b..d13a71ea 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -34,7 +34,7 @@ void ModerationActions::initialize(Settings &settings, Paths &paths) ModerationActionModel *ModerationActions::createModel(QObject *parent) { ModerationActionModel *model = new ModerationActionModel(parent); - model->init(&this->items); + model->initialize(&this->items); return model; } diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index ec2f231c..59fed3b1 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -121,7 +121,7 @@ NotificationModel *NotificationController::createModel(QObject *parent, Platform p) { NotificationModel *model = new NotificationModel(parent); - model->init(&this->channelMap[p]); + model->initialize(&this->channelMap[p]); return model; } diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp index 67bd3ad6..36f4b08a 100644 --- a/src/controllers/pings/PingController.cpp +++ b/src/controllers/pings/PingController.cpp @@ -19,7 +19,7 @@ void PingController::initialize(Settings &settings, Paths &paths) PingModel *PingController::createModel(QObject *parent) { PingModel *model = new PingModel(parent); - model->init(&this->channelVector); + model->initialize(&this->channelVector); return model; } diff --git a/src/controllers/taggedusers/TaggedUsersController.cpp b/src/controllers/taggedusers/TaggedUsersController.cpp index 2aa82a6c..095bd779 100644 --- a/src/controllers/taggedusers/TaggedUsersController.cpp +++ b/src/controllers/taggedusers/TaggedUsersController.cpp @@ -11,7 +11,7 @@ TaggedUsersController::TaggedUsersController() TaggedUsersModel *TaggedUsersController::createModel(QObject *parent) { TaggedUsersModel *model = new TaggedUsersModel(parent); - model->init(&this->users); + model->initialize(&this->users); return model; } diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index 0a8f2ed6..ee59e94c 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -143,7 +143,7 @@ Irc::Irc() QAbstractTableModel *Irc::newConnectionModel(QObject *parent) { auto model = new Model(parent); - model->init(&this->connections); + model->initialize(&this->connections); return model; } diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 7a456eca..5b022bc1 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -241,7 +241,6 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message, if (highlighted) { server.mentionsChannel->addMessage(msg); - getApp()->highlights->addHighlight(msg); } } diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 2612454b..9b8d1af9 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -49,12 +49,12 @@ HighlightingPage::HighlightingPage() "Play notification sounds and highlight messages based on " "certain patterns."); - EditableModelView *view = + auto view = highlights .emplace( - app->highlights->createModel(nullptr)) + (new HighlightModel(nullptr)) + ->initialized(&app->highlights->phrases)) .getElement(); - view->addRegexHelpLink(); view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound", "Enable\nregex", "Case-\nsensitive", @@ -94,7 +94,9 @@ HighlightingPage::HighlightingPage() EditableModelView *view = pingUsers .emplace( - app->highlights->createUserModel(nullptr)) + (new UserHighlightModel(nullptr)) + ->initialized( + &app->highlights->highlightedUsers)) .getElement(); view->addRegexHelpLink(); @@ -140,7 +142,9 @@ HighlightingPage::HighlightingPage() EditableModelView *view = disabledUsers .emplace( - app->highlights->createBlacklistModel(nullptr)) + (new HighlightBlacklistModel(nullptr)) + ->initialized( + &app->highlights->blacklistedUsers)) .getElement(); view->addRegexHelpLink(); @@ -205,7 +209,7 @@ HighlightingPage::HighlightingPage() // ---- misc this->disabledUsersChangedTimer_.setSingleShot(true); -} +} // namespace chatterino void HighlightingPage::tableCellClicked(const QModelIndex &clicked, EditableModelView *view) From e2c493f3697854ffacfb6517417442cb817ffbf7 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:37:02 +0100 Subject: [PATCH 28/67] removed compat aliases for SignalVector --- src/common/SignalVector.hpp | 12 ------------ src/common/SignalVectorModel.hpp | 11 +++++------ src/controllers/commands/CommandController.hpp | 2 +- src/controllers/highlights/HighlightController.hpp | 6 +++--- src/controllers/ignores/IgnoreController.hpp | 2 +- .../moderationactions/ModerationActions.hpp | 2 +- .../notifications/NotificationController.hpp | 4 ++-- src/controllers/pings/PingController.hpp | 2 +- src/providers/irc/Irc2.hpp | 2 +- 9 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index bf8cbfc5..7181067f 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -192,18 +192,6 @@ private: }; // compatability -template -using SignalVectorItemArgs = SignalVectorItemEvent; - -template -using ReadOnlySignalVector = SignalVector; - -template -using BaseSignalVector = SignalVector; - -template -using UnsortedSignalVector = SignalVector; - template class SortedSignalVector : public SignalVector { diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index 31129769..a13bd97f 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -25,11 +25,11 @@ public: } } - void initialize(BaseSignalVector *vec) + void initialize(SignalVector *vec) { this->vector_ = vec; - auto insert = [this](const SignalVectorItemArgs &args) { + auto insert = [this](const SignalVectorItemEvent &args) { if (args.caller == this) { return; @@ -54,7 +54,7 @@ public: int i = 0; for (const TVectorItem &item : vec->raw()) { - SignalVectorItemArgs args{item, i++, 0}; + SignalVectorItemEvent args{item, i++, 0}; insert(args); } @@ -89,8 +89,7 @@ public: this->afterInit(); } - SignalVectorModel *initialized( - BaseSignalVector *vec) + SignalVectorModel *initialized(SignalVector *vec) { this->initialize(vec); return this; @@ -393,7 +392,7 @@ protected: private: std::vector> headerData_; - BaseSignalVector *vector_; + SignalVector *vector_; std::vector rows_; int columnCount_; diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index 5435a60b..ebc57d8c 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -22,7 +22,7 @@ class CommandModel; class CommandController final : public Singleton { public: - UnsortedSignalVector items_; + SignalVector items_; QString execCommand(const QString &text, std::shared_ptr channel, bool dryRun); diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp index 5f746c8b..6741e1db 100644 --- a/src/controllers/highlights/HighlightController.hpp +++ b/src/controllers/highlights/HighlightController.hpp @@ -12,9 +12,9 @@ class HighlightController final : public Singleton public: virtual void initialize(Settings &settings, Paths &paths) override; - UnsortedSignalVector phrases; - UnsortedSignalVector blacklistedUsers; - UnsortedSignalVector highlightedUsers; + SignalVector phrases; + SignalVector blacklistedUsers; + SignalVector highlightedUsers; bool isHighlightedUser(const QString &username); bool blacklistContains(const QString &username); diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index 1279f4a8..e3330c33 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -19,7 +19,7 @@ class IgnoreController final : public Singleton public: virtual void initialize(Settings &settings, Paths &paths) override; - UnsortedSignalVector phrases; + SignalVector phrases; IgnoreModel *createModel(QObject *parent); diff --git a/src/controllers/moderationactions/ModerationActions.hpp b/src/controllers/moderationactions/ModerationActions.hpp index e1bf1b80..7388019b 100644 --- a/src/controllers/moderationactions/ModerationActions.hpp +++ b/src/controllers/moderationactions/ModerationActions.hpp @@ -20,7 +20,7 @@ public: virtual void initialize(Settings &settings, Paths &paths) override; - UnsortedSignalVector items; + SignalVector items; ModerationActionModel *createModel(QObject *parent); diff --git a/src/controllers/notifications/NotificationController.hpp b/src/controllers/notifications/NotificationController.hpp index 228348ad..a9c048ef 100644 --- a/src/controllers/notifications/NotificationController.hpp +++ b/src/controllers/notifications/NotificationController.hpp @@ -30,9 +30,9 @@ public: void playSound(); - UnsortedSignalVector getVector(Platform p); + SignalVector getVector(Platform p); - std::map> channelMap; + std::map> channelMap; NotificationModel *createModel(QObject *parent, Platform p); diff --git a/src/controllers/pings/PingController.hpp b/src/controllers/pings/PingController.hpp index 80805e85..b0b5542b 100644 --- a/src/controllers/pings/PingController.hpp +++ b/src/controllers/pings/PingController.hpp @@ -28,7 +28,7 @@ public: private: bool initialized_ = false; - UnsortedSignalVector channelVector; + SignalVector channelVector; ChatterinoSetting> pingSetting_ = {"/pings/muted"}; }; diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp index de484924..0cdc60c7 100644 --- a/src/providers/irc/Irc2.hpp +++ b/src/providers/irc/Irc2.hpp @@ -41,7 +41,7 @@ public: static inline void *const noEraseCredentialCaller = reinterpret_cast(1); - UnsortedSignalVector connections; + SignalVector connections; QAbstractTableModel *newConnectionModel(QObject *parent); ChannelPtr getOrAddChannel(int serverId, QString name); From 2ebe07bace950c72425da147d9926141fc9048bc Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:44:13 +0100 Subject: [PATCH 29/67] removed compat functions in SignalVector --- src/common/SignalVector.hpp | 22 +------------------ src/common/SignalVectorModel.hpp | 12 +++++----- .../accounts/AccountController.cpp | 5 ++--- .../commands/CommandController.cpp | 2 +- src/controllers/ignores/IgnoreController.cpp | 2 +- .../moderationactions/ModerationActions.cpp | 2 +- .../notifications/NotificationController.cpp | 13 +++++------ src/controllers/pings/PingController.cpp | 6 ++--- src/providers/irc/Irc2.cpp | 2 +- src/providers/twitch/TwitchAccountManager.cpp | 2 +- src/widgets/dialogs/SelectChannelDialog.cpp | 6 ++--- src/widgets/dialogs/UserInfoPopup.cpp | 4 ++-- src/widgets/settingspages/CommandPage.cpp | 4 ++-- .../settingspages/HighlightingPage.cpp | 6 ++--- src/widgets/settingspages/IgnoresPage.cpp | 2 +- src/widgets/settingspages/ModerationPage.cpp | 2 +- .../settingspages/NotificationPage.cpp | 2 +- 17 files changed, 36 insertions(+), 58 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 7181067f..074614d8 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -98,7 +98,7 @@ public: /// signals. int append(const T &item, void *caller = nullptr) { - return this->insertItem(item, -1, caller); + return this->insert(item, -1, caller); } void removeAt(int index, void *caller = nullptr) @@ -122,26 +122,6 @@ public: return this->items_; } - // compatability - [[deprecated("use insert")]] int insertItem(const T &item, - int proposedIndex = -1, - void *caller = nullptr) - { - return this->insert(item, proposedIndex, caller); - } - - [[deprecated("use append")]] int appendItem(const T &item, - void *caller = nullptr) - { - return this->append(item, caller); - } - - [[deprecated("use removeAt")]] void removeItem(int index, - void *caller = nullptr) - { - this->removeAt(index, caller); - } - [[deprecated]] std::vector cloneVector() { return *this->readOnly(); diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index a13bd97f..76bc14b2 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -153,12 +153,12 @@ public: else { int vecRow = this->getVectorIndexFromModelIndex(row); - this->vector_->removeItem(vecRow, this); + this->vector_->removeAt(vecRow, this); assert(this->rows_[row].original); TVectorItem item = this->getItemFromRow( this->rows_[row].items, this->rows_[row].original.get()); - this->vector_->insertItem(item, vecRow, this); + this->vector_->insert(item, vecRow, this); } return true; @@ -225,7 +225,7 @@ public: void deleteRow(int row) { int signalVectorRow = this->getVectorIndexFromModelIndex(row); - this->vector_->removeItem(signalVectorRow); + this->vector_->removeAt(signalVectorRow); } bool removeRows(int row, int count, const QModelIndex &parent) override @@ -240,7 +240,7 @@ public: assert(row >= 0 && row < this->rows_.size()); int signalVectorRow = this->getVectorIndexFromModelIndex(row); - this->vector_->removeItem(signalVectorRow); + this->vector_->removeAt(signalVectorRow); return true; } @@ -287,8 +287,8 @@ public: if (from != to) { auto item = this->vector_->raw()[from]; - this->vector_->removeItem(from); - this->vector_->insertItem(item, to); + this->vector_->removeAt(from); + this->vector_->insert(item, to); } // We return false since we remove items ourselves. diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index 6d272cf0..d44e00d5 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -9,8 +9,7 @@ namespace chatterino { AccountController::AccountController() { this->twitch.accounts.itemInserted.connect([this](const auto &args) { - this->accounts_.insertItem( - std::dynamic_pointer_cast(args.item)); + this->accounts_.insert(std::dynamic_pointer_cast(args.item)); }); this->twitch.accounts.itemRemoved.connect([this](const auto &args) { @@ -20,7 +19,7 @@ AccountController::AccountController() auto it = std::find(accs.begin(), accs.end(), args.item); assert(it != accs.end()); - this->accounts_.removeItem(it - accs.begin(), this); + this->accounts_.removeAt(it - accs.begin(), this); } }); diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 418612be..92d89e5c 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -222,7 +222,7 @@ void CommandController::initialize(Settings &, Paths &paths) // of commands) for (const auto &command : this->commandsSetting_->getValue()) { - this->items_.appendItem(command); + this->items_.append(command); } } diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 28b511a3..0080af65 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -14,7 +14,7 @@ void IgnoreController::initialize(Settings &, Paths &) for (const IgnorePhrase &phrase : this->ignoresSetting_.getValue()) { - this->phrases.appendItem(phrase); + this->phrases.append(phrase); } this->phrases.delayedItemsChanged.connect([this] { // diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index d13a71ea..5d2ed9dc 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -23,7 +23,7 @@ void ModerationActions::initialize(Settings &settings, Paths &paths) for (auto &val : this->setting_->getValue()) { - this->items.insertItem(val); + this->items.insert(val); } this->items.delayedItemsChanged.connect([this] { // diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index 59fed3b1..72d7b012 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -26,12 +26,11 @@ void NotificationController::initialize(Settings &settings, Paths &paths) this->initialized_ = true; for (const QString &channelName : this->twitchSetting_.getValue()) { - this->channelMap[Platform::Twitch].appendItem(channelName); + this->channelMap[Platform::Twitch].append(channelName); } this->channelMap[Platform::Twitch].delayedItemsChanged.connect([this] { // - this->twitchSetting_.setValue( - this->channelMap[Platform::Twitch].raw()); + this->twitchSetting_.setValue(this->channelMap[Platform::Twitch].raw()); }); /* for (const QString &channelName : this->mixerSetting_.getValue()) { @@ -81,18 +80,18 @@ bool NotificationController::isChannelNotified(const QString &channelName, void NotificationController::addChannelNotification(const QString &channelName, Platform p) { - channelMap[p].appendItem(channelName); + channelMap[p].append(channelName); } void NotificationController::removeChannelNotification( const QString &channelName, Platform p) { - for (std::vector::size_type i = 0; - i != channelMap[p].raw().size(); i++) + for (std::vector::size_type i = 0; i != channelMap[p].raw().size(); + i++) { if (channelMap[p].raw()[i].toLower() == channelName.toLower()) { - channelMap[p].removeItem(i); + channelMap[p].removeAt(i); i--; } } diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp index 36f4b08a..db245acc 100644 --- a/src/controllers/pings/PingController.cpp +++ b/src/controllers/pings/PingController.cpp @@ -8,7 +8,7 @@ void PingController::initialize(Settings &settings, Paths &paths) this->initialized_ = true; for (const QString &channelName : this->pingSetting_.getValue()) { - this->channelVector.appendItem(channelName); + this->channelVector.append(channelName); } this->channelVector.delayedItemsChanged.connect([this] { // @@ -37,7 +37,7 @@ bool PingController::isMuted(const QString &channelName) void PingController::muteChannel(const QString &channelName) { - channelVector.appendItem(channelName); + channelVector.append(channelName); } void PingController::unmuteChannel(const QString &channelName) @@ -47,7 +47,7 @@ void PingController::unmuteChannel(const QString &channelName) { if (channelVector.raw()[i].toLower() == channelName.toLower()) { - channelVector.removeItem(i); + channelVector.removeAt(i); i--; } } diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index ee59e94c..2f685be6 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -252,7 +252,7 @@ void Irc::load() { ids.insert(data.id); - this->connections.appendItem(data); + this->connections.append(data); } } } diff --git a/src/providers/twitch/TwitchAccountManager.cpp b/src/providers/twitch/TwitchAccountManager.cpp index b772f291..5369e40e 100644 --- a/src/providers/twitch/TwitchAccountManager.cpp +++ b/src/providers/twitch/TwitchAccountManager.cpp @@ -221,7 +221,7 @@ TwitchAccountManager::AddUserResponse TwitchAccountManager::addUser( // std::lock_guard lock(this->mutex); - this->accounts.insertItem(newUser); + this->accounts.insert(newUser); return AddUserResponse::UserAdded; } diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index c8e21a18..a17c7022 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -152,7 +152,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) auto editor = new IrcConnectionEditor(unique); if (editor->exec() == QDialog::Accepted) { - Irc::instance().connections.appendItem(editor->data()); + Irc::instance().connections.append(editor->data()); } }); @@ -171,9 +171,9 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) { if (conn.id == data.id) { - Irc::instance().connections.removeItem( + Irc::instance().connections.removeAt( i, Irc::noEraseCredentialCaller); - Irc::instance().connections.insertItem(data, i); + Irc::instance().connections.insert(data, i); } i++; } diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index bb75a61f..e54a4373 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -336,7 +336,7 @@ void UserInfoPopup::installEvents() if (checked) { - getApp()->highlights->blacklistedUsers.insertItem( + getApp()->highlights->blacklistedUsers.insert( HighlightBlacklistUser{this->userName_, false}); this->ui_.ignoreHighlights->setEnabled(true); } @@ -349,7 +349,7 @@ void UserInfoPopup::installEvents() { if (this->userName_ == vector[i].getPattern()) { - getApp()->highlights->blacklistedUsers.removeItem(i); + getApp()->highlights->blacklistedUsers.removeAt(i); i--; } } diff --git a/src/widgets/settingspages/CommandPage.cpp b/src/widgets/settingspages/CommandPage.cpp index 99b83ec9..7dc72c55 100644 --- a/src/widgets/settingspages/CommandPage.cpp +++ b/src/widgets/settingspages/CommandPage.cpp @@ -46,7 +46,7 @@ CommandPage::CommandPage() view->setTitles({"Trigger", "Command"}); view->getTableView()->horizontalHeader()->setStretchLastSection(true); view->addButtonPressed.connect([] { - getApp()->commands->items_.appendItem( + getApp()->commands->items_.append( Command{"/command", "I made a new command HeyGuys"}); }); @@ -65,7 +65,7 @@ CommandPage::CommandPage() { if (int index = line.indexOf(' '); index != -1) { - getApp()->commands->items_.insertItem( + getApp()->commands->items_.insert( Command(line.mid(0, index), line.mid(index + 1))); } } diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 9b8d1af9..fb2d7da0 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -72,7 +72,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->phrases.appendItem(HighlightPhrase{ + getApp()->highlights->phrases.append(HighlightPhrase{ "my phrase", true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); @@ -120,7 +120,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->highlightedUsers.appendItem( + getApp()->highlights->highlightedUsers.append( HighlightPhrase{"highlighted user", true, false, false, false, "", *ColorProvider::instance().color( @@ -162,7 +162,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->blacklistedUsers.appendItem( + getApp()->highlights->blacklistedUsers.append( HighlightBlacklistUser{"blacklisted user", false}); }); } diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index 9cc65788..773fdea3 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -60,7 +60,7 @@ void addPhrasesTab(LayoutCreator layout) }); view->addButtonPressed.connect([] { - getApp()->ignores->phrases.appendItem( + getApp()->ignores->phrases.append( IgnorePhrase{"my pattern", false, false, getSettings()->ignoredPhraseReplace.getValue(), true}); }); diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 479d0a7c..87e3a5e5 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -184,7 +184,7 @@ ModerationPage::ModerationPage() 0, QHeaderView::Stretch); view->addButtonPressed.connect([] { - getApp()->moderationActions->items.appendItem( + getApp()->moderationActions->items.append( ModerationAction("/timeout {user} 300")); }); diff --git a/src/widgets/settingspages/NotificationPage.cpp b/src/widgets/settingspages/NotificationPage.cpp index 27c1da4b..b14af27f 100644 --- a/src/widgets/settingspages/NotificationPage.cpp +++ b/src/widgets/settingspages/NotificationPage.cpp @@ -100,7 +100,7 @@ NotificationPage::NotificationPage() view->addButtonPressed.connect([] { getApp() ->notifications->channelMap[Platform::Twitch] - .appendItem("channel"); + .append("channel"); }); } /* From 7e49371ba16f3fd0707a582684a0e17879a2e8a3 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:44:37 +0100 Subject: [PATCH 30/67] fixed formatting --- src/controllers/pings/PingController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp index db245acc..268dfe18 100644 --- a/src/controllers/pings/PingController.cpp +++ b/src/controllers/pings/PingController.cpp @@ -42,8 +42,8 @@ void PingController::muteChannel(const QString &channelName) void PingController::unmuteChannel(const QString &channelName) { - for (std::vector::size_type i = 0; - i != channelVector.raw().size(); i++) + for (std::vector::size_type i = 0; i != channelVector.raw().size(); + i++) { if (channelVector.raw()[i].toLower() == channelName.toLower()) { From 89389608e9ce40a3d585d03cc361788fb63192a9 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 19:49:45 +0100 Subject: [PATCH 31/67] removed compat class for SignalVector --- src/common/SignalVector.hpp | 11 ----------- src/controllers/accounts/AccountController.cpp | 1 + src/controllers/accounts/AccountController.hpp | 3 +-- src/controllers/taggedusers/TaggedUsersController.cpp | 1 + src/controllers/taggedusers/TaggedUsersController.hpp | 2 +- src/providers/twitch/TwitchAccountManager.cpp | 3 ++- src/providers/twitch/TwitchAccountManager.hpp | 4 +--- 7 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/common/SignalVector.hpp b/src/common/SignalVector.hpp index 074614d8..33bd8254 100644 --- a/src/common/SignalVector.hpp +++ b/src/common/SignalVector.hpp @@ -171,15 +171,4 @@ private: std::function itemCompare_; }; -// compatability -template -class SortedSignalVector : public SignalVector -{ -public: - SortedSignalVector() - : SignalVector(Compare{}) - { - } -}; - } // namespace chatterino diff --git a/src/controllers/accounts/AccountController.cpp b/src/controllers/accounts/AccountController.cpp index d44e00d5..2692b05a 100644 --- a/src/controllers/accounts/AccountController.cpp +++ b/src/controllers/accounts/AccountController.cpp @@ -7,6 +7,7 @@ namespace chatterino { AccountController::AccountController() + : accounts_(SharedPtrElementLess{}) { this->twitch.accounts.itemInserted.connect([this](const auto &args) { this->accounts_.insert(std::dynamic_pointer_cast(args.item)); diff --git a/src/controllers/accounts/AccountController.hpp b/src/controllers/accounts/AccountController.hpp index e297b9e4..e1ef3c70 100644 --- a/src/controllers/accounts/AccountController.hpp +++ b/src/controllers/accounts/AccountController.hpp @@ -27,8 +27,7 @@ public: TwitchAccountManager twitch; private: - SortedSignalVector, SharedPtrElementLess> - accounts_; + SignalVector> accounts_; }; } // namespace chatterino diff --git a/src/controllers/taggedusers/TaggedUsersController.cpp b/src/controllers/taggedusers/TaggedUsersController.cpp index 095bd779..a42265fd 100644 --- a/src/controllers/taggedusers/TaggedUsersController.cpp +++ b/src/controllers/taggedusers/TaggedUsersController.cpp @@ -5,6 +5,7 @@ namespace chatterino { TaggedUsersController::TaggedUsersController() + : users(std::less{}) { } diff --git a/src/controllers/taggedusers/TaggedUsersController.hpp b/src/controllers/taggedusers/TaggedUsersController.hpp index db9ff2d1..ea917195 100644 --- a/src/controllers/taggedusers/TaggedUsersController.hpp +++ b/src/controllers/taggedusers/TaggedUsersController.hpp @@ -14,7 +14,7 @@ class TaggedUsersController final : public Singleton public: TaggedUsersController(); - SortedSignalVector> users; + SignalVector users; TaggedUsersModel *createModel(QObject *parent = nullptr); }; diff --git a/src/providers/twitch/TwitchAccountManager.cpp b/src/providers/twitch/TwitchAccountManager.cpp index 5369e40e..c3b2ee69 100644 --- a/src/providers/twitch/TwitchAccountManager.cpp +++ b/src/providers/twitch/TwitchAccountManager.cpp @@ -7,7 +7,8 @@ namespace chatterino { TwitchAccountManager::TwitchAccountManager() - : anonymousUser_(new TwitchAccount(ANONYMOUS_USERNAME, "", "", "")) + : accounts(SharedPtrElementLess{}) + , anonymousUser_(new TwitchAccount(ANONYMOUS_USERNAME, "", "", "")) { this->currentUserChanged.connect([this] { auto currentUser = this->getCurrent(); diff --git a/src/providers/twitch/TwitchAccountManager.hpp b/src/providers/twitch/TwitchAccountManager.hpp index c9ce0215..5956fdf0 100644 --- a/src/providers/twitch/TwitchAccountManager.hpp +++ b/src/providers/twitch/TwitchAccountManager.hpp @@ -51,9 +51,7 @@ public: pajlada::Signals::NoArgSignal currentUserChanged; pajlada::Signals::NoArgSignal userListUpdated; - SortedSignalVector, - SharedPtrElementLess> - accounts; + SignalVector> accounts; private: enum class AddUserResponse { From 8ae9abb2506f46ff1a20fab52b681e21c3301471 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 20:01:35 +0100 Subject: [PATCH 32/67] refactored IgnoredController --- chatterino.pro | 1 + .../highlights/HighlightController.cpp | 18 +------------ src/controllers/ignores/IgnoreController.cpp | 18 ++----------- src/controllers/ignores/IgnoreController.hpp | 5 ---- src/controllers/ignores/IgnoreModel.hpp | 5 +--- src/util/PersistSignalVector.hpp | 25 +++++++++++++++++++ src/widgets/settingspages/IgnoresPage.cpp | 4 ++- 7 files changed, 33 insertions(+), 43 deletions(-) create mode 100644 src/util/PersistSignalVector.hpp diff --git a/chatterino.pro b/chatterino.pro index 02950839..78a22681 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -434,6 +434,7 @@ HEADERS += \ src/util/LayoutCreator.hpp \ src/util/LayoutHelper.hpp \ src/util/Overloaded.hpp \ + src/util/PersistSignalVector.hpp \ src/util/PostToThread.hpp \ src/util/QObjectRef.hpp \ src/util/QStringHash.hpp \ diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index f341f349..d81ea74e 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -1,26 +1,10 @@ #include "HighlightController.hpp" #include "Application.hpp" -#include "common/ChatterinoSetting.hpp" +#include "util/PersistSignalVector.hpp" namespace chatterino { -template -inline void persist(SignalVector &vec, const std::string &name) -{ - auto setting = std::make_unique>>(name); - - for (auto &&item : setting->getValue()) - vec.append(item); - - vec.delayedItemsChanged.connect([setting = setting.get(), vec = &vec] { - setting->setValue(vec->raw()); - }); - - // TODO: Delete when appropriate. - setting.release(); -} - void HighlightController::initialize(Settings &settings, Paths &paths) { assert(!this->initialized_); diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 0080af65..3ce1d562 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -2,6 +2,7 @@ #include "Application.hpp" #include "controllers/ignores/IgnoreModel.hpp" +#include "util/PersistSignalVector.hpp" #include @@ -12,22 +13,7 @@ void IgnoreController::initialize(Settings &, Paths &) assert(!this->initialized_); this->initialized_ = true; - for (const IgnorePhrase &phrase : this->ignoresSetting_.getValue()) - { - this->phrases.append(phrase); - } - - this->phrases.delayedItemsChanged.connect([this] { // - this->ignoresSetting_.setValue(this->phrases.raw()); - }); -} - -IgnoreModel *IgnoreController::createModel(QObject *parent) -{ - IgnoreModel *model = new IgnoreModel(parent); - model->initialize(&this->phrases); - - return model; + persist(this->phrases, "/ignore/phrases"); } } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index e3330c33..979647a0 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -21,13 +21,8 @@ public: SignalVector phrases; - IgnoreModel *createModel(QObject *parent); - private: bool initialized_ = false; - - ChatterinoSetting> ignoresSetting_ = { - "/ignore/phrases"}; }; } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreModel.hpp b/src/controllers/ignores/IgnoreModel.hpp index 1b9a5099..473b61b9 100644 --- a/src/controllers/ignores/IgnoreModel.hpp +++ b/src/controllers/ignores/IgnoreModel.hpp @@ -7,10 +7,9 @@ namespace chatterino { -class IgnoreController; - class IgnoreModel : public SignalVectorModel { +public: explicit IgnoreModel(QObject *parent); protected: @@ -21,8 +20,6 @@ protected: // turns a row in the model into a vector item virtual void getRowFromItem(const IgnorePhrase &item, std::vector &row) override; - - friend class IgnoreController; }; } // namespace chatterino diff --git a/src/util/PersistSignalVector.hpp b/src/util/PersistSignalVector.hpp new file mode 100644 index 00000000..b1dde778 --- /dev/null +++ b/src/util/PersistSignalVector.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "common/ChatterinoSetting.hpp" +#include "common/SignalVector.hpp" + +namespace chatterino { + +template +inline void persist(SignalVector &vec, const std::string &name) +{ + auto setting = std::make_unique>>(name); + + for (auto &&item : setting->getValue()) + vec.append(item); + + vec.delayedItemsChanged.connect([setting = setting.get(), vec = &vec] { + setting->setValue(vec->raw()); + }); + + // TODO: Delete when appropriate. + setting.release(); +} + +} // namespace chatterino diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index 773fdea3..c16b1858 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -44,7 +44,9 @@ void addPhrasesTab(LayoutCreator layout) layout.emplace("Ignore messages based certain patterns."); EditableModelView *view = layout - .emplace(getApp()->ignores->createModel(nullptr)) + .emplace( + (new IgnoreModel(nullptr)) + ->initialized(&getApp()->ignores->phrases)) .getElement(); view->setTitles( {"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"}); From df91ea44c4be7c6d229038b7a2777fe2eb15e03c Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 20:10:47 +0100 Subject: [PATCH 33/67] refactored ModeratinActions --- .../ModerationActionModel.hpp | 4 --- .../moderationactions/ModerationActions.cpp | 27 ++----------------- .../moderationactions/ModerationActions.hpp | 11 +------- src/widgets/settingspages/ModerationPage.cpp | 3 ++- 4 files changed, 5 insertions(+), 40 deletions(-) diff --git a/src/controllers/moderationactions/ModerationActionModel.hpp b/src/controllers/moderationactions/ModerationActionModel.hpp index e9f37236..e13b3b27 100644 --- a/src/controllers/moderationactions/ModerationActionModel.hpp +++ b/src/controllers/moderationactions/ModerationActionModel.hpp @@ -7,8 +7,6 @@ namespace chatterino { -class ModerationActions; - class ModerationActionModel : public SignalVectorModel { public: @@ -25,8 +23,6 @@ protected: std::vector &row) override; friend class HighlightController; - - friend class ModerationActions; }; } // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp index 5d2ed9dc..6215c81b 100644 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ b/src/controllers/moderationactions/ModerationActions.cpp @@ -2,41 +2,18 @@ #include "Application.hpp" #include "controllers/moderationactions/ModerationActionModel.hpp" -#include "singletons/Settings.hpp" +#include "util/PersistSignalVector.hpp" #include namespace chatterino { -ModerationActions::ModerationActions() -{ -} - void ModerationActions::initialize(Settings &settings, Paths &paths) { assert(!this->initialized_); this->initialized_ = true; - this->setting_ = - std::make_unique>>( - "/moderation/actions"); - - for (auto &val : this->setting_->getValue()) - { - this->items.insert(val); - } - - this->items.delayedItemsChanged.connect([this] { // - this->setting_->setValue(this->items.raw()); - }); -} - -ModerationActionModel *ModerationActions::createModel(QObject *parent) -{ - ModerationActionModel *model = new ModerationActionModel(parent); - model->initialize(&this->items); - - return model; + persist(this->items, "/moderation/actions"); } } // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActions.hpp b/src/controllers/moderationactions/ModerationActions.hpp index 7388019b..8e6a4ded 100644 --- a/src/controllers/moderationactions/ModerationActions.hpp +++ b/src/controllers/moderationactions/ModerationActions.hpp @@ -1,9 +1,7 @@ #pragma once -#include "common/Singleton.hpp" - -#include "common/ChatterinoSetting.hpp" #include "common/SignalVector.hpp" +#include "common/Singleton.hpp" #include "controllers/moderationactions/ModerationAction.hpp" namespace chatterino { @@ -11,21 +9,14 @@ namespace chatterino { class Settings; class Paths; -class ModerationActionModel; - class ModerationActions final : public Singleton { public: - ModerationActions(); - virtual void initialize(Settings &settings, Paths &paths) override; SignalVector items; - ModerationActionModel *createModel(QObject *parent); - private: - std::unique_ptr>> setting_; bool initialized_ = false; }; diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 87e3a5e5..8b1754d8 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -174,7 +174,8 @@ ModerationPage::ModerationPage() EditableModelView *view = modMode .emplace( - app->moderationActions->createModel(nullptr)) + (new ModerationActionModel(nullptr)) + ->initialized(&app->moderationActions->items)) .getElement(); view->setTitles({"Actions"}); From d0a81f3fe7f4c2709ebf2d7a6ef752bc90a42531 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 20:34:00 +0100 Subject: [PATCH 34/67] refactored TaggedUsers and Ping --- chatterino.pro | 8 +-- src/Application.cpp | 4 +- src/Application.hpp | 4 +- .../pings/MutedChannelController.cpp | 57 +++++++++++++++ .../pings/MutedChannelController.hpp | 32 +++++++++ src/controllers/pings/MutedChannelModel.cpp | 28 ++++++++ .../{PingModel.hpp => MutedChannelModel.hpp} | 8 +-- src/controllers/pings/PingController.cpp | 70 ------------------- src/controllers/pings/PingController.hpp | 36 ---------- src/controllers/pings/PingModel.cpp | 28 -------- .../taggedusers/TaggedUsersController.cpp | 8 --- .../taggedusers/TaggedUsersController.hpp | 2 - src/providers/twitch/TwitchMessageBuilder.cpp | 2 +- src/widgets/splits/SplitHeader.cpp | 5 +- 14 files changed, 131 insertions(+), 161 deletions(-) create mode 100644 src/controllers/pings/MutedChannelController.cpp create mode 100644 src/controllers/pings/MutedChannelController.hpp create mode 100644 src/controllers/pings/MutedChannelModel.cpp rename src/controllers/pings/{PingModel.hpp => MutedChannelModel.hpp} (79%) delete mode 100644 src/controllers/pings/PingController.cpp delete mode 100644 src/controllers/pings/PingController.hpp delete mode 100644 src/controllers/pings/PingModel.cpp diff --git a/chatterino.pro b/chatterino.pro index 78a22681..f663b52c 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -142,8 +142,8 @@ SOURCES += \ src/controllers/moderationactions/ModerationActions.cpp \ src/controllers/notifications/NotificationController.cpp \ src/controllers/notifications/NotificationModel.cpp \ - src/controllers/pings/PingController.cpp \ - src/controllers/pings/PingModel.cpp \ + src/controllers/pings/MutedChannelController.cpp \ + src/controllers/pings/MutedChannelModel.cpp \ src/controllers/taggedusers/TaggedUser.cpp \ src/controllers/taggedusers/TaggedUsersController.cpp \ src/controllers/taggedusers/TaggedUsersModel.cpp \ @@ -338,8 +338,8 @@ HEADERS += \ src/controllers/moderationactions/ModerationActions.hpp \ src/controllers/notifications/NotificationController.hpp \ src/controllers/notifications/NotificationModel.hpp \ - src/controllers/pings/PingController.hpp \ - src/controllers/pings/PingModel.hpp \ + src/controllers/pings/MutedChannelController.hpp \ + src/controllers/pings/MutedChannelModel.hpp \ src/controllers/taggedusers/TaggedUser.hpp \ src/controllers/taggedusers/TaggedUsersController.hpp \ src/controllers/taggedusers/TaggedUsersModel.hpp \ diff --git a/src/Application.cpp b/src/Application.cpp index fe0aab34..da891ab8 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -9,7 +9,7 @@ #include "controllers/ignores/IgnoreController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" -#include "controllers/pings/PingController.hpp" +#include "controllers/pings/MutedChannelController.hpp" #include "controllers/taggedusers/TaggedUsersController.hpp" #include "messages/MessageBuilder.hpp" #include "providers/bttv/BttvEmotes.hpp" @@ -56,7 +56,7 @@ Application::Application(Settings &_settings, Paths &_paths) , commands(&this->emplace()) , highlights(&this->emplace()) , notifications(&this->emplace()) - , pings(&this->emplace()) + , pings(&this->emplace()) , ignores(&this->emplace()) , taggedUsers(&this->emplace()) , moderationActions(&this->emplace()) diff --git a/src/Application.hpp b/src/Application.hpp index fac8ec0f..87489fdf 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -18,7 +18,7 @@ class TaggedUsersController; class AccountController; class ModerationActions; class NotificationController; -class PingController; +class MutedChannelController; class Theme; class WindowManager; @@ -60,7 +60,7 @@ public: CommandController *const commands{}; HighlightController *const highlights{}; NotificationController *const notifications{}; - PingController *const pings{}; + MutedChannelController *const pings{}; IgnoreController *const ignores{}; TaggedUsersController *const taggedUsers{}; ModerationActions *const moderationActions{}; diff --git a/src/controllers/pings/MutedChannelController.cpp b/src/controllers/pings/MutedChannelController.cpp new file mode 100644 index 00000000..809097ab --- /dev/null +++ b/src/controllers/pings/MutedChannelController.cpp @@ -0,0 +1,57 @@ +#include "controllers/pings/MutedChannelController.hpp" +#include "controllers/pings/MutedChannelModel.hpp" +#include "util/PersistSignalVector.hpp" + +namespace chatterino { + +void MutedChannelController::initialize(Settings &settings, Paths &paths) +{ + this->initialized_ = true; + + persist(this->channels, "/pings/muted"); +} + +bool MutedChannelController::isMuted(const QString &channelName) +{ + for (const auto &channel : this->channels) + { + if (channelName.toLower() == channel.toLower()) + { + return true; + } + } + return false; +} + +void MutedChannelController::mute(const QString &channelName) +{ + channels.append(channelName); +} + +void MutedChannelController::unmute(const QString &channelName) +{ + for (std::vector::size_type i = 0; i != channels.raw().size(); i++) + { + if (channels.raw()[i].toLower() == channelName.toLower()) + { + channels.removeAt(i); + i--; + } + } +} + +bool MutedChannelController::toggleMuted(const QString &channelName) +{ + if (this->isMuted(channelName)) + { + unmute(channelName); + return false; + } + else + { + mute(channelName); + return true; + } +} + +} // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.hpp b/src/controllers/pings/MutedChannelController.hpp new file mode 100644 index 00000000..ff4a6f05 --- /dev/null +++ b/src/controllers/pings/MutedChannelController.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "common/SignalVector.hpp" +#include "common/Singleton.hpp" +#include "singletons/Settings.hpp" + +namespace chatterino { + +class Settings; +class Paths; + +class MutedChannelModel; + +class MutedChannelController final : public Singleton, private QObject +{ +public: + virtual void initialize(Settings &settings, Paths &paths) override; + + bool isMuted(const QString &channelName); + bool toggleMuted(const QString &channelName); + +private: + void mute(const QString &channelName); + void unmute(const QString &channelName); + bool initialized_ = false; + + SignalVector channels; +}; + +} // namespace chatterino diff --git a/src/controllers/pings/MutedChannelModel.cpp b/src/controllers/pings/MutedChannelModel.cpp new file mode 100644 index 00000000..fc147350 --- /dev/null +++ b/src/controllers/pings/MutedChannelModel.cpp @@ -0,0 +1,28 @@ +#include "MutedChannelModel.hpp" + +#include "Application.hpp" +#include "singletons/Settings.hpp" +#include "util/StandardItemHelper.hpp" + +namespace chatterino { + +MutedChannelModel::MutedChannelModel(QObject *parent) + : SignalVectorModel(1, parent) +{ +} + +// turn a vector item into a model row +QString MutedChannelModel::getItemFromRow(std::vector &row, + const QString &original) +{ + return QString(row[0]->data(Qt::DisplayRole).toString()); +} + +// turn a model +void MutedChannelModel::getRowFromItem(const QString &item, + std::vector &row) +{ + setStringItem(row[0], item); +} + +} // namespace chatterino diff --git a/src/controllers/pings/PingModel.hpp b/src/controllers/pings/MutedChannelModel.hpp similarity index 79% rename from src/controllers/pings/PingModel.hpp rename to src/controllers/pings/MutedChannelModel.hpp index 137be5e0..c53b3177 100644 --- a/src/controllers/pings/PingModel.hpp +++ b/src/controllers/pings/MutedChannelModel.hpp @@ -7,11 +7,11 @@ namespace chatterino { -class PingController; +class MutedChannelController; -class PingModel : public SignalVectorModel +class MutedChannelModel : public SignalVectorModel { - explicit PingModel(QObject *parent); + explicit MutedChannelModel(QObject *parent); protected: // turn a vector item into a model row @@ -21,8 +21,6 @@ protected: // turns a row in the model into a vector item virtual void getRowFromItem(const QString &item, std::vector &row) override; - - friend class PingController; }; } // namespace chatterino diff --git a/src/controllers/pings/PingController.cpp b/src/controllers/pings/PingController.cpp deleted file mode 100644 index 268dfe18..00000000 --- a/src/controllers/pings/PingController.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "controllers/pings/PingController.hpp" -#include "controllers/pings/PingModel.hpp" - -namespace chatterino { - -void PingController::initialize(Settings &settings, Paths &paths) -{ - this->initialized_ = true; - for (const QString &channelName : this->pingSetting_.getValue()) - { - this->channelVector.append(channelName); - } - - this->channelVector.delayedItemsChanged.connect([this] { // - this->pingSetting_.setValue(this->channelVector.raw()); - }); -} - -PingModel *PingController::createModel(QObject *parent) -{ - PingModel *model = new PingModel(parent); - model->initialize(&this->channelVector); - return model; -} - -bool PingController::isMuted(const QString &channelName) -{ - for (const auto &channel : this->channelVector) - { - if (channelName.toLower() == channel.toLower()) - { - return true; - } - } - return false; -} - -void PingController::muteChannel(const QString &channelName) -{ - channelVector.append(channelName); -} - -void PingController::unmuteChannel(const QString &channelName) -{ - for (std::vector::size_type i = 0; i != channelVector.raw().size(); - i++) - { - if (channelVector.raw()[i].toLower() == channelName.toLower()) - { - channelVector.removeAt(i); - i--; - } - } -} - -bool PingController::toggleMuteChannel(const QString &channelName) -{ - if (this->isMuted(channelName)) - { - unmuteChannel(channelName); - return false; - } - else - { - muteChannel(channelName); - return true; - } -} - -} // namespace chatterino diff --git a/src/controllers/pings/PingController.hpp b/src/controllers/pings/PingController.hpp deleted file mode 100644 index b0b5542b..00000000 --- a/src/controllers/pings/PingController.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "singletons/Settings.hpp" - -namespace chatterino { - -class Settings; -class Paths; - -class PingModel; - -class PingController final : public Singleton, private QObject -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - - bool isMuted(const QString &channelName); - void muteChannel(const QString &channelName); - void unmuteChannel(const QString &channelName); - bool toggleMuteChannel(const QString &channelName); - - PingModel *createModel(QObject *parent); - -private: - bool initialized_ = false; - - SignalVector channelVector; - - ChatterinoSetting> pingSetting_ = {"/pings/muted"}; -}; - -} // namespace chatterino diff --git a/src/controllers/pings/PingModel.cpp b/src/controllers/pings/PingModel.cpp deleted file mode 100644 index 28098a20..00000000 --- a/src/controllers/pings/PingModel.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "PingModel.hpp" - -#include "Application.hpp" -#include "singletons/Settings.hpp" -#include "util/StandardItemHelper.hpp" - -namespace chatterino { - -PingModel::PingModel(QObject *parent) - : SignalVectorModel(1, parent) -{ -} - -// turn a vector item into a model row -QString PingModel::getItemFromRow(std::vector &row, - const QString &original) -{ - return QString(row[0]->data(Qt::DisplayRole).toString()); -} - -// turn a model -void PingModel::getRowFromItem(const QString &item, - std::vector &row) -{ - setStringItem(row[0], item); -} - -} // namespace chatterino diff --git a/src/controllers/taggedusers/TaggedUsersController.cpp b/src/controllers/taggedusers/TaggedUsersController.cpp index a42265fd..a81b100c 100644 --- a/src/controllers/taggedusers/TaggedUsersController.cpp +++ b/src/controllers/taggedusers/TaggedUsersController.cpp @@ -9,12 +9,4 @@ TaggedUsersController::TaggedUsersController() { } -TaggedUsersModel *TaggedUsersController::createModel(QObject *parent) -{ - TaggedUsersModel *model = new TaggedUsersModel(parent); - model->initialize(&this->users); - - return model; -} - } // namespace chatterino diff --git a/src/controllers/taggedusers/TaggedUsersController.hpp b/src/controllers/taggedusers/TaggedUsersController.hpp index ea917195..dd9c4d89 100644 --- a/src/controllers/taggedusers/TaggedUsersController.hpp +++ b/src/controllers/taggedusers/TaggedUsersController.hpp @@ -15,8 +15,6 @@ public: TaggedUsersController(); SignalVector users; - - TaggedUsersModel *createModel(QObject *parent = nullptr); }; } // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 2e1d18b4..09e5f675 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -4,7 +4,7 @@ #include "controllers/accounts/AccountController.hpp" #include "controllers/highlights/HighlightController.hpp" #include "controllers/ignores/IgnoreController.hpp" -#include "controllers/pings/PingController.hpp" +#include "controllers/pings/MutedChannelController.hpp" #include "messages/Message.hpp" #include "providers/chatterino/ChatterinoBadges.hpp" #include "providers/twitch/TwitchBadges.hpp" diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 79153b44..84a389aa 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -4,7 +4,7 @@ #include "controllers/accounts/AccountController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" -#include "controllers/pings/PingController.hpp" +#include "controllers/pings/MutedChannelController.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchIrcServer.hpp" #include "singletons/Resources.hpp" @@ -343,8 +343,7 @@ std::unique_ptr SplitHeader::createMainMenu() this->split_->getChannel()->getName())); }); action->connect(action, &QAction::triggered, this, [this]() { - getApp()->pings->toggleMuteChannel( - this->split_->getChannel()->getName()); + getApp()->pings->toggleMuted(this->split_->getChannel()->getName()); }); moreMenu->addAction(action); From f8a98501514e4d7dde415e6c3940037c64a309a7 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 21:18:40 +0100 Subject: [PATCH 35/67] absorbed HighlightController into Application --- chatterino.pro | 2 - src/Application.cpp | 40 +++++++++++++++++-- src/Application.hpp | 14 ++++++- .../highlights/HighlightController.cpp | 40 ------------------- .../highlights/HighlightController.hpp | 26 ------------ src/providers/colors/ColorProvider.cpp | 5 +-- src/providers/twitch/IrcMessageHandler.cpp | 1 - src/providers/twitch/TwitchIrcServer.cpp | 1 - src/providers/twitch/TwitchMessageBuilder.cpp | 7 ++-- src/widgets/dialogs/UserInfoPopup.cpp | 15 ++++--- .../settingspages/HighlightingPage.cpp | 22 +++++----- 11 files changed, 70 insertions(+), 103 deletions(-) delete mode 100644 src/controllers/highlights/HighlightController.cpp delete mode 100644 src/controllers/highlights/HighlightController.hpp diff --git a/chatterino.pro b/chatterino.pro index f663b52c..d19b4fb8 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -131,7 +131,6 @@ SOURCES += \ src/controllers/commands/CommandController.cpp \ src/controllers/commands/CommandModel.cpp \ src/controllers/highlights/HighlightBlacklistModel.cpp \ - src/controllers/highlights/HighlightController.cpp \ src/controllers/highlights/HighlightModel.cpp \ src/controllers/highlights/HighlightPhrase.cpp \ src/controllers/highlights/UserHighlightModel.cpp \ @@ -326,7 +325,6 @@ HEADERS += \ src/controllers/commands/CommandModel.hpp \ src/controllers/highlights/HighlightBlacklistModel.hpp \ src/controllers/highlights/HighlightBlacklistUser.hpp \ - src/controllers/highlights/HighlightController.hpp \ src/controllers/highlights/HighlightModel.hpp \ src/controllers/highlights/HighlightPhrase.hpp \ src/controllers/highlights/UserHighlightModel.hpp \ diff --git a/src/Application.cpp b/src/Application.cpp index da891ab8..4e0ae212 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -5,7 +5,8 @@ #include "common/Args.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/commands/CommandController.hpp" -#include "controllers/highlights/HighlightController.hpp" +#include "controllers/highlights/HighlightBlacklistUser.hpp" +#include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnoreController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" @@ -30,6 +31,7 @@ #include "singletons/Updates.hpp" #include "singletons/WindowManager.hpp" #include "util/IsBigEndian.hpp" +#include "util/PersistSignalVector.hpp" #include "util/PostToThread.hpp" #include "widgets/Notebook.hpp" #include "widgets/Window.hpp" @@ -46,7 +48,11 @@ Application *Application::instance = nullptr; // to each other Application::Application(Settings &_settings, Paths &_paths) - : themes(&this->emplace()) + : highlightedMessages(*new SignalVector()) + , highlightedUsers(*new SignalVector()) + , blacklistedUsers(*new SignalVector()) + + , themes(&this->emplace()) , fonts(&this->emplace()) , emotes(&this->emplace()) , windows(&this->emplace()) @@ -54,7 +60,6 @@ Application::Application(Settings &_settings, Paths &_paths) , accounts(&this->emplace()) , commands(&this->emplace()) - , highlights(&this->emplace()) , notifications(&this->emplace()) , pings(&this->emplace()) , ignores(&this->emplace()) @@ -63,10 +68,13 @@ Application::Application(Settings &_settings, Paths &_paths) , twitch2(&this->emplace()) , chatterinoBadges(&this->emplace()) , logging(&this->emplace()) - { this->instance = this; + persist(this->highlightedMessages, "/highlighting/highlights"); + persist(this->blacklistedUsers, "/highlighting/blacklist"); + persist(this->highlightedUsers, "/highlighting/users"); + this->fonts->fontChanged.connect( [this]() { this->windows->layoutChannelViews(); }); @@ -321,4 +329,28 @@ Application *getApp() return Application::instance; } +bool Application::isHighlightedUser(const QString &username) +{ + for (const auto &highlightedUser : this->highlightedUsers) + { + if (highlightedUser.isMatch(username)) + return true; + } + + return false; +} + +bool Application::isBlacklistedUser(const QString &username) +{ + auto items = this->blacklistedUsers.readOnly(); + + for (const auto &blacklistedUser : *items) + { + if (blacklistedUser.isMatch(username)) + return true; + } + + return false; +} + } // namespace chatterino diff --git a/src/Application.hpp b/src/Application.hpp index 87489fdf..966e21ef 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -3,6 +3,7 @@ #include #include +#include "common/SignalVector.hpp" #include "common/Singleton.hpp" #include "singletons/NativeMessaging.hpp" @@ -31,6 +32,9 @@ class Fonts; class Toasts; class ChatterinoBadges; +class HighlightPhrase; +class HighlightBlacklistUser; + class Application { std::vector> singletons_; @@ -50,6 +54,15 @@ public: friend void test(); + // clang-format off + SignalVector &highlightedMessages; + SignalVector &highlightedUsers; + SignalVector &blacklistedUsers; + // clang-format on + + bool isHighlightedUser(const QString &username); + bool isBlacklistedUser(const QString &username); + Theme *const themes{}; Fonts *const fonts{}; Emotes *const emotes{}; @@ -58,7 +71,6 @@ public: AccountController *const accounts{}; CommandController *const commands{}; - HighlightController *const highlights{}; NotificationController *const notifications{}; MutedChannelController *const pings{}; IgnoreController *const ignores{}; diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp deleted file mode 100644 index d81ea74e..00000000 --- a/src/controllers/highlights/HighlightController.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "HighlightController.hpp" - -#include "Application.hpp" -#include "util/PersistSignalVector.hpp" - -namespace chatterino { - -void HighlightController::initialize(Settings &settings, Paths &paths) -{ - assert(!this->initialized_); - this->initialized_ = true; - - persist(this->phrases, "/highlighting/highlights"); - persist(this->blacklistedUsers, "/highlighting/blacklist"); - persist(this->highlightedUsers, "/highlighting/users"); -} - -bool HighlightController::isHighlightedUser(const QString &username) -{ - for (const auto &highlightedUser : this->highlightedUsers) - { - if (highlightedUser.isMatch(username)) - return true; - } - - return false; -} - -bool HighlightController::blacklistContains(const QString &username) -{ - for (const auto &blacklistedUser : *this->blacklistedUsers.readOnly()) - { - if (blacklistedUser.isMatch(username)) - return true; - } - - return false; -} - -} // namespace chatterino diff --git a/src/controllers/highlights/HighlightController.hpp b/src/controllers/highlights/HighlightController.hpp deleted file mode 100644 index 6741e1db..00000000 --- a/src/controllers/highlights/HighlightController.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "controllers/highlights/HighlightBlacklistUser.hpp" -#include "controllers/highlights/HighlightPhrase.hpp" - -namespace chatterino { - -class HighlightController final : public Singleton -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - - SignalVector phrases; - SignalVector blacklistedUsers; - SignalVector highlightedUsers; - - bool isHighlightedUser(const QString &username); - bool blacklistContains(const QString &username); - -private: - bool initialized_ = false; -}; - -} // namespace chatterino diff --git a/src/providers/colors/ColorProvider.cpp b/src/providers/colors/ColorProvider.cpp index 492689aa..ce61a69f 100644 --- a/src/providers/colors/ColorProvider.cpp +++ b/src/providers/colors/ColorProvider.cpp @@ -1,6 +1,5 @@ #include "providers/colors/ColorProvider.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "singletons/Theme.hpp" namespace chatterino { @@ -38,12 +37,12 @@ QSet ColorProvider::recentColors() const * Currently, only colors used in highlight phrases are considered. This * may change at any point in the future. */ - for (auto phrase : getApp()->highlights->phrases) + for (auto phrase : getApp()->highlightedMessages) { retVal.insert(*phrase.getColor()); } - for (auto userHl : getApp()->highlights->highlightedUsers) + for (auto userHl : getApp()->highlightedUsers) { retVal.insert(*userHl.getColor()); } diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 5b022bc1..60d5a5c5 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -2,7 +2,6 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "messages/LimitedQueue.hpp" #include "messages/Message.hpp" #include "providers/twitch/TwitchAccountManager.hpp" diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index d13850b4..7ed568c8 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -7,7 +7,6 @@ #include "common/Common.hpp" #include "common/Env.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" #include "providers/twitch/IrcMessageHandler.hpp" diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 09e5f675..8902512d 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -2,7 +2,6 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "controllers/ignores/IgnoreController.hpp" #include "controllers/pings/MutedChannelController.hpp" #include "messages/Message.hpp" @@ -1018,7 +1017,7 @@ void TwitchMessageBuilder::parseHighlights() QString currentUsername = currentUser->getUserName(); - if (app->highlights->blacklistContains(this->ircMessage->nick())) + if (app->isBlacklistedUser(this->ircMessage->nick())) { // Do nothing. We ignore highlights from this user. return; @@ -1058,7 +1057,7 @@ void TwitchMessageBuilder::parseHighlights() } // Highlight because of sender - auto userHighlights = app->highlights->highlightedUsers.readOnly(); + auto userHighlights = app->highlightedUsers.readOnly(); for (const HighlightPhrase &userHighlight : *userHighlights) { if (!userHighlight.isMatch(this->ircMessage->nick())) @@ -1110,7 +1109,7 @@ void TwitchMessageBuilder::parseHighlights() // TODO: This vector should only be rebuilt upon highlights being changed // fourtf: should be implemented in the HighlightsController std::vector activeHighlights = - app->highlights->phrases.cloneVector(); + app->highlightedMessages.cloneVector(); if (getSettings()->enableSelfHighlight && currentUsername.size() > 0) { diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index e54a4373..d6041448 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -4,7 +4,7 @@ #include "common/Channel.hpp" #include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/highlights/HighlightController.hpp" +#include "controllers/highlights/HighlightBlacklistUser.hpp" #include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "singletons/Resources.hpp" @@ -336,24 +336,23 @@ void UserInfoPopup::installEvents() if (checked) { - getApp()->highlights->blacklistedUsers.insert( + getApp()->blacklistedUsers.insert( HighlightBlacklistUser{this->userName_, false}); this->ui_.ignoreHighlights->setEnabled(true); } else { - const auto &vector = - getApp()->highlights->blacklistedUsers.raw(); + const auto &vector = getApp()->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) { - getApp()->highlights->blacklistedUsers.removeAt(i); + getApp()->blacklistedUsers.removeAt(i); i--; } } - if (getApp()->highlights->blacklistContains(this->userName_)) + if (getApp()->isBlacklistedUser(this->userName_)) { this->ui_.ignoreHighlights->setToolTip( "Name matched by regex"); @@ -456,7 +455,7 @@ void UserInfoPopup::updateUserData() // get ignoreHighlights state bool isIgnoringHighlights = false; - const auto &vector = getApp()->highlights->blacklistedUsers.raw(); + const auto &vector = getApp()->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) @@ -465,7 +464,7 @@ void UserInfoPopup::updateUserData() break; } } - if (getApp()->highlights->blacklistContains(this->userName_) && + if (getApp()->isBlacklistedUser(this->userName_) && !isIgnoringHighlights) { this->ui_.ignoreHighlights->setToolTip("Name matched by regex"); diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index fb2d7da0..5013bc30 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -2,7 +2,6 @@ #include "Application.hpp" #include "controllers/highlights/HighlightBlacklistModel.hpp" -#include "controllers/highlights/HighlightController.hpp" #include "controllers/highlights/HighlightModel.hpp" #include "controllers/highlights/UserHighlightModel.hpp" #include "singletons/Settings.hpp" @@ -53,7 +52,7 @@ HighlightingPage::HighlightingPage() highlights .emplace( (new HighlightModel(nullptr)) - ->initialized(&app->highlights->phrases)) + ->initialized(&app->highlightedMessages)) .getElement(); view->addRegexHelpLink(); view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound", @@ -72,7 +71,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->phrases.append(HighlightPhrase{ + getApp()->highlightedMessages.append(HighlightPhrase{ "my phrase", true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); @@ -95,8 +94,7 @@ HighlightingPage::HighlightingPage() pingUsers .emplace( (new UserHighlightModel(nullptr)) - ->initialized( - &app->highlights->highlightedUsers)) + ->initialized(&app->highlightedUsers)) .getElement(); view->addRegexHelpLink(); @@ -120,11 +118,10 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->highlightedUsers.append( - HighlightPhrase{"highlighted user", true, false, false, - false, "", - *ColorProvider::instance().color( - ColorType::SelfHighlight)}); + getApp()->highlightedUsers.append(HighlightPhrase{ + "highlighted user", true, false, false, false, "", + *ColorProvider::instance().color( + ColorType::SelfHighlight)}); }); QObject::connect(view->getTableView(), &QTableView::clicked, @@ -143,8 +140,7 @@ HighlightingPage::HighlightingPage() disabledUsers .emplace( (new HighlightBlacklistModel(nullptr)) - ->initialized( - &app->highlights->blacklistedUsers)) + ->initialized(&app->blacklistedUsers)) .getElement(); view->addRegexHelpLink(); @@ -162,7 +158,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlights->blacklistedUsers.append( + getApp()->blacklistedUsers.append( HighlightBlacklistUser{"blacklisted user", false}); }); } From 5ad427bd61ab5921311e9783558b28a2a208e83f Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 22:15:13 +0100 Subject: [PATCH 36/67] moved some Settings from Application to Settings --- src/Application.cpp | 38 +------------- src/Application.hpp | 13 ----- src/controllers/ignores/IgnoreController.cpp | 2 - src/controllers/ignores/IgnoreController.hpp | 2 - src/providers/colors/ColorProvider.cpp | 4 +- src/providers/twitch/TwitchMessageBuilder.cpp | 11 ++-- src/singletons/Settings.cpp | 52 +++++++++++++++++++ src/singletons/Settings.hpp | 27 +++++++++- src/widgets/dialogs/UserInfoPopup.cpp | 13 ++--- .../settingspages/HighlightingPage.cpp | 13 ++--- src/widgets/settingspages/IgnoresPage.cpp | 4 +- 11 files changed, 102 insertions(+), 77 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index 4e0ae212..62f8f392 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -5,8 +5,6 @@ #include "common/Args.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/commands/CommandController.hpp" -#include "controllers/highlights/HighlightBlacklistUser.hpp" -#include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnoreController.hpp" #include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" @@ -31,7 +29,6 @@ #include "singletons/Updates.hpp" #include "singletons/WindowManager.hpp" #include "util/IsBigEndian.hpp" -#include "util/PersistSignalVector.hpp" #include "util/PostToThread.hpp" #include "widgets/Notebook.hpp" #include "widgets/Window.hpp" @@ -48,11 +45,7 @@ Application *Application::instance = nullptr; // to each other Application::Application(Settings &_settings, Paths &_paths) - : highlightedMessages(*new SignalVector()) - , highlightedUsers(*new SignalVector()) - , blacklistedUsers(*new SignalVector()) - - , themes(&this->emplace()) + : themes(&this->emplace()) , fonts(&this->emplace()) , emotes(&this->emplace()) , windows(&this->emplace()) @@ -62,7 +55,6 @@ Application::Application(Settings &_settings, Paths &_paths) , commands(&this->emplace()) , notifications(&this->emplace()) , pings(&this->emplace()) - , ignores(&this->emplace()) , taggedUsers(&this->emplace()) , moderationActions(&this->emplace()) , twitch2(&this->emplace()) @@ -71,10 +63,6 @@ Application::Application(Settings &_settings, Paths &_paths) { this->instance = this; - persist(this->highlightedMessages, "/highlighting/highlights"); - persist(this->blacklistedUsers, "/highlighting/blacklist"); - persist(this->highlightedUsers, "/highlighting/users"); - this->fonts->fontChanged.connect( [this]() { this->windows->layoutChannelViews(); }); @@ -329,28 +317,4 @@ Application *getApp() return Application::instance; } -bool Application::isHighlightedUser(const QString &username) -{ - for (const auto &highlightedUser : this->highlightedUsers) - { - if (highlightedUser.isMatch(username)) - return true; - } - - return false; -} - -bool Application::isBlacklistedUser(const QString &username) -{ - auto items = this->blacklistedUsers.readOnly(); - - for (const auto &blacklistedUser : *items) - { - if (blacklistedUser.isMatch(username)) - return true; - } - - return false; -} - } // namespace chatterino diff --git a/src/Application.hpp b/src/Application.hpp index 966e21ef..fa04a34d 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -32,9 +32,6 @@ class Fonts; class Toasts; class ChatterinoBadges; -class HighlightPhrase; -class HighlightBlacklistUser; - class Application { std::vector> singletons_; @@ -54,15 +51,6 @@ public: friend void test(); - // clang-format off - SignalVector &highlightedMessages; - SignalVector &highlightedUsers; - SignalVector &blacklistedUsers; - // clang-format on - - bool isHighlightedUser(const QString &username); - bool isBlacklistedUser(const QString &username); - Theme *const themes{}; Fonts *const fonts{}; Emotes *const emotes{}; @@ -73,7 +61,6 @@ public: CommandController *const commands{}; NotificationController *const notifications{}; MutedChannelController *const pings{}; - IgnoreController *const ignores{}; TaggedUsersController *const taggedUsers{}; ModerationActions *const moderationActions{}; TwitchIrcServer *const twitch2{}; diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp index 3ce1d562..0415032e 100644 --- a/src/controllers/ignores/IgnoreController.cpp +++ b/src/controllers/ignores/IgnoreController.cpp @@ -12,8 +12,6 @@ void IgnoreController::initialize(Settings &, Paths &) { assert(!this->initialized_); this->initialized_ = true; - - persist(this->phrases, "/ignore/phrases"); } } // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index 979647a0..c0b11e82 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -19,8 +19,6 @@ class IgnoreController final : public Singleton public: virtual void initialize(Settings &settings, Paths &paths) override; - SignalVector phrases; - private: bool initialized_ = false; }; diff --git a/src/providers/colors/ColorProvider.cpp b/src/providers/colors/ColorProvider.cpp index ce61a69f..e1be8d6f 100644 --- a/src/providers/colors/ColorProvider.cpp +++ b/src/providers/colors/ColorProvider.cpp @@ -37,12 +37,12 @@ QSet ColorProvider::recentColors() const * Currently, only colors used in highlight phrases are considered. This * may change at any point in the future. */ - for (auto phrase : getApp()->highlightedMessages) + for (auto phrase : getSettings()->highlightedMessages) { retVal.insert(*phrase.getColor()); } - for (auto userHl : getApp()->highlightedUsers) + for (auto userHl : getSettings()->highlightedUsers) { retVal.insert(*userHl.getColor()); } diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 8902512d..730bf3ca 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -169,7 +169,7 @@ bool TwitchMessageBuilder::isIgnored() const auto app = getApp(); // TODO(pajlada): Do we need to check if the phrase is valid first? - auto phrases = app->ignores->phrases.readOnly(); + auto phrases = getCSettings().ignoredMessages.readOnly(); for (const auto &phrase : *phrases) { if (phrase.isBlock() && phrase.isMatch(this->originalMessage_)) @@ -764,8 +764,7 @@ void TwitchMessageBuilder::appendUsername() void TwitchMessageBuilder::runIgnoreReplaces( std::vector> &twitchEmotes) { - auto app = getApp(); - auto phrases = app->ignores->phrases.readOnly(); + auto phrases = getCSettings().ignoredMessages.readOnly(); auto removeEmotesInRange = [](int pos, int len, std::vector> @@ -1017,7 +1016,7 @@ void TwitchMessageBuilder::parseHighlights() QString currentUsername = currentUser->getUserName(); - if (app->isBlacklistedUser(this->ircMessage->nick())) + if (getCSettings().isBlacklistedUser(this->ircMessage->nick())) { // Do nothing. We ignore highlights from this user. return; @@ -1057,7 +1056,7 @@ void TwitchMessageBuilder::parseHighlights() } // Highlight because of sender - auto userHighlights = app->highlightedUsers.readOnly(); + auto userHighlights = getCSettings().highlightedUsers.readOnly(); for (const HighlightPhrase &userHighlight : *userHighlights) { if (!userHighlight.isMatch(this->ircMessage->nick())) @@ -1109,7 +1108,7 @@ void TwitchMessageBuilder::parseHighlights() // TODO: This vector should only be rebuilt upon highlights being changed // fourtf: should be implemented in the HighlightsController std::vector activeHighlights = - app->highlightedMessages.cloneVector(); + getSettings()->highlightedMessages.cloneVector(); if (getSettings()->enableSelfHighlight && currentUsername.size() > 0) { diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 30ba15ec..0e42db01 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -1,19 +1,71 @@ #include "singletons/Settings.hpp" #include "Application.hpp" +#include "controllers/highlights/HighlightBlacklistUser.hpp" +#include "controllers/highlights/HighlightPhrase.hpp" +#include "controllers/ignores/IgnorePhrase.hpp" #include "singletons/Paths.hpp" #include "singletons/Resources.hpp" #include "singletons/WindowManager.hpp" +#include "util/PersistSignalVector.hpp" #include "util/WindowsHelper.hpp" namespace chatterino { +ConcurrentSettings *concurrentInstance_{}; + +ConcurrentSettings::ConcurrentSettings() + : highlightedMessages(*new SignalVector()) + , highlightedUsers(*new SignalVector()) + , blacklistedUsers(*new SignalVector()) + , ignoredMessages(*new SignalVector()) +{ +} + +bool ConcurrentSettings::isHighlightedUser(const QString &username) +{ + for (const auto &highlightedUser : this->highlightedUsers) + { + if (highlightedUser.isMatch(username)) + return true; + } + + return false; +} + +bool ConcurrentSettings::isBlacklistedUser(const QString &username) +{ + auto items = this->blacklistedUsers.readOnly(); + + for (const auto &blacklistedUser : *items) + { + if (blacklistedUser.isMatch(username)) + return true; + } + + return false; +} + +ConcurrentSettings &getCSettings() +{ + // `concurrentInstance_` gets assigned in Settings ctor. + assert(concurrentInstance_); + + return *concurrentInstance_; +} + Settings *Settings::instance_ = nullptr; Settings::Settings(const QString &settingsDirectory) : ABSettings(settingsDirectory) { instance_ = this; + concurrentInstance_ = this; + + persist(this->highlightedMessages, "/highlighting/highlights"); + persist(this->blacklistedUsers, "/highlighting/blacklist"); + persist(this->highlightedUsers, "/highlighting/users"); + persist(this->ignoredMessages, "/ignore/phrases"); #ifdef USEWINSDK this->autorun = isRegisteredForStartup(); diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index f0d1679d..cc2e4e3c 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -5,13 +5,38 @@ #include "BaseSettings.hpp" #include "common/Channel.hpp" +#include "common/SignalVector.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/moderationactions/ModerationAction.hpp" #include "singletons/Toasts.hpp" namespace chatterino { -class Settings : public ABSettings +class HighlightPhrase; +class HighlightBlacklistUser; +class IgnorePhrase; + +// Settings which are availlable for reading on all threads. +class ConcurrentSettings +{ +public: + ConcurrentSettings(); + + // clang-format off + SignalVector &highlightedMessages; + SignalVector &highlightedUsers; + SignalVector &blacklistedUsers; + SignalVector &ignoredMessages; + // clang-format on + + bool isHighlightedUser(const QString &username); + bool isBlacklistedUser(const QString &username); +}; + +ConcurrentSettings &getCSettings(); + +// These settings are still accessed concurrently in the code but it is bad practice. +class Settings : public ABSettings, public ConcurrentSettings { static Settings *instance_; diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index d6041448..3b830702 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -8,6 +8,7 @@ #include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "singletons/Resources.hpp" +#include "singletons/Settings.hpp" #include "util/LayoutCreator.hpp" #include "util/PostToThread.hpp" #include "widgets/Label.hpp" @@ -336,23 +337,23 @@ void UserInfoPopup::installEvents() if (checked) { - getApp()->blacklistedUsers.insert( + getSettings()->blacklistedUsers.insert( HighlightBlacklistUser{this->userName_, false}); this->ui_.ignoreHighlights->setEnabled(true); } else { - const auto &vector = getApp()->blacklistedUsers.raw(); + const auto &vector = getSettings()->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) { - getApp()->blacklistedUsers.removeAt(i); + getSettings()->blacklistedUsers.removeAt(i); i--; } } - if (getApp()->isBlacklistedUser(this->userName_)) + if (getSettings()->isBlacklistedUser(this->userName_)) { this->ui_.ignoreHighlights->setToolTip( "Name matched by regex"); @@ -455,7 +456,7 @@ void UserInfoPopup::updateUserData() // get ignoreHighlights state bool isIgnoringHighlights = false; - const auto &vector = getApp()->blacklistedUsers.raw(); + const auto &vector = getSettings()->blacklistedUsers.raw(); for (int i = 0; i < vector.size(); i++) { if (this->userName_ == vector[i].getPattern()) @@ -464,7 +465,7 @@ void UserInfoPopup::updateUserData() break; } } - if (getApp()->isBlacklistedUser(this->userName_) && + if (getSettings()->isBlacklistedUser(this->userName_) && !isIgnoringHighlights) { this->ui_.ignoreHighlights->setToolTip("Name matched by regex"); diff --git a/src/widgets/settingspages/HighlightingPage.cpp b/src/widgets/settingspages/HighlightingPage.cpp index 5013bc30..2ba16f62 100644 --- a/src/widgets/settingspages/HighlightingPage.cpp +++ b/src/widgets/settingspages/HighlightingPage.cpp @@ -52,7 +52,8 @@ HighlightingPage::HighlightingPage() highlights .emplace( (new HighlightModel(nullptr)) - ->initialized(&app->highlightedMessages)) + ->initialized( + &getSettings()->highlightedMessages)) .getElement(); view->addRegexHelpLink(); view->setTitles({"Pattern", "Flash\ntaskbar", "Play\nsound", @@ -71,7 +72,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlightedMessages.append(HighlightPhrase{ + getSettings()->highlightedMessages.append(HighlightPhrase{ "my phrase", true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); @@ -94,7 +95,7 @@ HighlightingPage::HighlightingPage() pingUsers .emplace( (new UserHighlightModel(nullptr)) - ->initialized(&app->highlightedUsers)) + ->initialized(&getSettings()->highlightedUsers)) .getElement(); view->addRegexHelpLink(); @@ -118,7 +119,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->highlightedUsers.append(HighlightPhrase{ + getSettings()->highlightedUsers.append(HighlightPhrase{ "highlighted user", true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); @@ -140,7 +141,7 @@ HighlightingPage::HighlightingPage() disabledUsers .emplace( (new HighlightBlacklistModel(nullptr)) - ->initialized(&app->blacklistedUsers)) + ->initialized(&getSettings()->blacklistedUsers)) .getElement(); view->addRegexHelpLink(); @@ -158,7 +159,7 @@ HighlightingPage::HighlightingPage() }); view->addButtonPressed.connect([] { - getApp()->blacklistedUsers.append( + getSettings()->blacklistedUsers.append( HighlightBlacklistUser{"blacklisted user", false}); }); } diff --git a/src/widgets/settingspages/IgnoresPage.cpp b/src/widgets/settingspages/IgnoresPage.cpp index c16b1858..0e3fe2ad 100644 --- a/src/widgets/settingspages/IgnoresPage.cpp +++ b/src/widgets/settingspages/IgnoresPage.cpp @@ -46,7 +46,7 @@ void addPhrasesTab(LayoutCreator layout) layout .emplace( (new IgnoreModel(nullptr)) - ->initialized(&getApp()->ignores->phrases)) + ->initialized(&getSettings()->ignoredMessages)) .getElement(); view->setTitles( {"Pattern", "Regex", "Case Sensitive", "Block", "Replacement"}); @@ -62,7 +62,7 @@ void addPhrasesTab(LayoutCreator layout) }); view->addButtonPressed.connect([] { - getApp()->ignores->phrases.append( + getSettings()->ignoredMessages.append( IgnorePhrase{"my pattern", false, false, getSettings()->ignoredPhraseReplace.getValue(), true}); }); From e537277fa80692ba28e542681dcfb476086cd06e Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 23 Feb 2020 23:07:28 +0100 Subject: [PATCH 37/67] did some more things and stuff --- chatterino.pro | 4 -- src/Application.cpp | 11 +--- src/Application.hpp | 8 --- .../moderationactions/ModerationAction.cpp | 14 ++++- .../moderationactions/ModerationAction.hpp | 3 +- .../moderationactions/ModerationActions.cpp | 19 ------ .../moderationactions/ModerationActions.hpp | 23 ------- .../pings/MutedChannelController.cpp | 45 -------------- .../pings/MutedChannelController.hpp | 5 -- .../taggedusers/TaggedUsersController.cpp | 12 ---- .../taggedusers/TaggedUsersController.hpp | 20 ------- src/messages/MessageElement.cpp | 5 +- src/providers/twitch/TwitchMessageBuilder.cpp | 2 +- src/singletons/Settings.cpp | 60 +++++++++++++++++-- src/singletons/Settings.hpp | 21 +++++-- src/widgets/settingspages/ModerationPage.cpp | 6 +- src/widgets/splits/SplitHeader.cpp | 14 ++--- 17 files changed, 98 insertions(+), 174 deletions(-) delete mode 100644 src/controllers/moderationactions/ModerationActions.cpp delete mode 100644 src/controllers/moderationactions/ModerationActions.hpp delete mode 100644 src/controllers/taggedusers/TaggedUsersController.cpp delete mode 100644 src/controllers/taggedusers/TaggedUsersController.hpp diff --git a/chatterino.pro b/chatterino.pro index d19b4fb8..755bc589 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -138,13 +138,11 @@ SOURCES += \ src/controllers/ignores/IgnoreModel.cpp \ src/controllers/moderationactions/ModerationAction.cpp \ src/controllers/moderationactions/ModerationActionModel.cpp \ - src/controllers/moderationactions/ModerationActions.cpp \ src/controllers/notifications/NotificationController.cpp \ src/controllers/notifications/NotificationModel.cpp \ src/controllers/pings/MutedChannelController.cpp \ src/controllers/pings/MutedChannelModel.cpp \ src/controllers/taggedusers/TaggedUser.cpp \ - src/controllers/taggedusers/TaggedUsersController.cpp \ src/controllers/taggedusers/TaggedUsersModel.cpp \ src/debug/Benchmark.cpp \ src/main.cpp \ @@ -333,13 +331,11 @@ HEADERS += \ src/controllers/ignores/IgnorePhrase.hpp \ src/controllers/moderationactions/ModerationAction.hpp \ src/controllers/moderationactions/ModerationActionModel.hpp \ - src/controllers/moderationactions/ModerationActions.hpp \ src/controllers/notifications/NotificationController.hpp \ src/controllers/notifications/NotificationModel.hpp \ src/controllers/pings/MutedChannelController.hpp \ src/controllers/pings/MutedChannelModel.hpp \ src/controllers/taggedusers/TaggedUser.hpp \ - src/controllers/taggedusers/TaggedUsersController.hpp \ src/controllers/taggedusers/TaggedUsersModel.hpp \ src/debug/AssertInGuiThread.hpp \ src/debug/Benchmark.hpp \ diff --git a/src/Application.cpp b/src/Application.cpp index 62f8f392..8f942501 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -6,10 +6,7 @@ #include "controllers/accounts/AccountController.hpp" #include "controllers/commands/CommandController.hpp" #include "controllers/ignores/IgnoreController.hpp" -#include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" -#include "controllers/pings/MutedChannelController.hpp" -#include "controllers/taggedusers/TaggedUsersController.hpp" #include "messages/MessageBuilder.hpp" #include "providers/bttv/BttvEmotes.hpp" #include "providers/chatterino/ChatterinoBadges.hpp" @@ -54,9 +51,6 @@ Application::Application(Settings &_settings, Paths &_paths) , accounts(&this->emplace()) , commands(&this->emplace()) , notifications(&this->emplace()) - , pings(&this->emplace()) - , taggedUsers(&this->emplace()) - , moderationActions(&this->emplace()) , twitch2(&this->emplace()) , chatterinoBadges(&this->emplace()) , logging(&this->emplace()) @@ -111,9 +105,6 @@ void Application::initialize(Settings &settings, Paths &paths) this->initNm(paths); this->initPubsub(); - - this->moderationActions->items.delayedItemsChanged.connect( - [this] { this->windows->forceLayoutChannelViews(); }); } int Application::run(QApplication &qtApp) @@ -126,6 +117,8 @@ int Application::run(QApplication &qtApp) getSettings()->betaUpdates.connect( [] { Updates::instance().checkForUpdates(); }, false); + getSettings()->moderationActions.delayedItemsChanged.connect( + [this] { this->windows->forceLayoutChannelViews(); }); return qtApp.exec(); } diff --git a/src/Application.hpp b/src/Application.hpp index fa04a34d..0c0e54bc 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -13,13 +13,8 @@ class TwitchIrcServer; class PubSub; class CommandController; -class HighlightController; -class IgnoreController; -class TaggedUsersController; class AccountController; -class ModerationActions; class NotificationController; -class MutedChannelController; class Theme; class WindowManager; @@ -60,9 +55,6 @@ public: AccountController *const accounts{}; CommandController *const commands{}; NotificationController *const notifications{}; - MutedChannelController *const pings{}; - TaggedUsersController *const taggedUsers{}; - ModerationActions *const moderationActions{}; TwitchIrcServer *const twitch2{}; ChatterinoBadges *const chatterinoBadges{}; diff --git a/src/controllers/moderationactions/ModerationAction.cpp b/src/controllers/moderationactions/ModerationAction.cpp index dce01904..d94cb234 100644 --- a/src/controllers/moderationactions/ModerationAction.cpp +++ b/src/controllers/moderationactions/ModerationAction.cpp @@ -71,11 +71,11 @@ ModerationAction::ModerationAction(const QString &action) } else if (action.startsWith("/ban ")) { - this->image_ = Image::fromPixmap(getResources().buttons.ban); + this->imageToLoad_ = 1; } else if (action.startsWith("/delete ")) { - this->image_ = Image::fromPixmap(getResources().buttons.trashCan); + this->imageToLoad_ = 2; } else { @@ -100,6 +100,16 @@ bool ModerationAction::isImage() const const boost::optional &ModerationAction::getImage() const { + assertInGuiThread(); + + if (this->imageToLoad_ != 0) + { + if (this->imageToLoad_ == 1) + this->image_ = Image::fromPixmap(getResources().buttons.ban); + else if (this->imageToLoad_ == 2) + this->image_ = Image::fromPixmap(getResources().buttons.trashCan); + } + return this->image_; } diff --git a/src/controllers/moderationactions/ModerationAction.hpp b/src/controllers/moderationactions/ModerationAction.hpp index eed7579a..3b4532d9 100644 --- a/src/controllers/moderationactions/ModerationAction.hpp +++ b/src/controllers/moderationactions/ModerationAction.hpp @@ -25,10 +25,11 @@ public: const QString &getAction() const; private: - boost::optional image_; + mutable boost::optional image_; QString line1_; QString line2_; QString action_; + int imageToLoad_{}; }; } // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActions.cpp b/src/controllers/moderationactions/ModerationActions.cpp deleted file mode 100644 index 6215c81b..00000000 --- a/src/controllers/moderationactions/ModerationActions.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "ModerationActions.hpp" - -#include "Application.hpp" -#include "controllers/moderationactions/ModerationActionModel.hpp" -#include "util/PersistSignalVector.hpp" - -#include - -namespace chatterino { - -void ModerationActions::initialize(Settings &settings, Paths &paths) -{ - assert(!this->initialized_); - this->initialized_ = true; - - persist(this->items, "/moderation/actions"); -} - -} // namespace chatterino diff --git a/src/controllers/moderationactions/ModerationActions.hpp b/src/controllers/moderationactions/ModerationActions.hpp deleted file mode 100644 index 8e6a4ded..00000000 --- a/src/controllers/moderationactions/ModerationActions.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "controllers/moderationactions/ModerationAction.hpp" - -namespace chatterino { - -class Settings; -class Paths; - -class ModerationActions final : public Singleton -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - - SignalVector items; - -private: - bool initialized_ = false; -}; - -} // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.cpp b/src/controllers/pings/MutedChannelController.cpp index 809097ab..62555fd5 100644 --- a/src/controllers/pings/MutedChannelController.cpp +++ b/src/controllers/pings/MutedChannelController.cpp @@ -7,51 +7,6 @@ namespace chatterino { void MutedChannelController::initialize(Settings &settings, Paths &paths) { this->initialized_ = true; - - persist(this->channels, "/pings/muted"); -} - -bool MutedChannelController::isMuted(const QString &channelName) -{ - for (const auto &channel : this->channels) - { - if (channelName.toLower() == channel.toLower()) - { - return true; - } - } - return false; -} - -void MutedChannelController::mute(const QString &channelName) -{ - channels.append(channelName); -} - -void MutedChannelController::unmute(const QString &channelName) -{ - for (std::vector::size_type i = 0; i != channels.raw().size(); i++) - { - if (channels.raw()[i].toLower() == channelName.toLower()) - { - channels.removeAt(i); - i--; - } - } -} - -bool MutedChannelController::toggleMuted(const QString &channelName) -{ - if (this->isMuted(channelName)) - { - unmute(channelName); - return false; - } - else - { - mute(channelName); - return true; - } } } // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.hpp b/src/controllers/pings/MutedChannelController.hpp index ff4a6f05..ea681fdc 100644 --- a/src/controllers/pings/MutedChannelController.hpp +++ b/src/controllers/pings/MutedChannelController.hpp @@ -18,12 +18,7 @@ class MutedChannelController final : public Singleton, private QObject public: virtual void initialize(Settings &settings, Paths &paths) override; - bool isMuted(const QString &channelName); - bool toggleMuted(const QString &channelName); - private: - void mute(const QString &channelName); - void unmute(const QString &channelName); bool initialized_ = false; SignalVector channels; diff --git a/src/controllers/taggedusers/TaggedUsersController.cpp b/src/controllers/taggedusers/TaggedUsersController.cpp deleted file mode 100644 index a81b100c..00000000 --- a/src/controllers/taggedusers/TaggedUsersController.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "TaggedUsersController.hpp" - -#include "controllers/taggedusers/TaggedUsersModel.hpp" - -namespace chatterino { - -TaggedUsersController::TaggedUsersController() - : users(std::less{}) -{ -} - -} // namespace chatterino diff --git a/src/controllers/taggedusers/TaggedUsersController.hpp b/src/controllers/taggedusers/TaggedUsersController.hpp deleted file mode 100644 index dd9c4d89..00000000 --- a/src/controllers/taggedusers/TaggedUsersController.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "common/Singleton.hpp" - -#include "common/SignalVector.hpp" -#include "controllers/taggedusers/TaggedUser.hpp" - -namespace chatterino { - -class TaggedUsersModel; - -class TaggedUsersController final : public Singleton -{ -public: - TaggedUsersController(); - - SignalVector users; -}; - -} // namespace chatterino diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index 0defed59..3849b8b4 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -1,7 +1,6 @@ #include "messages/MessageElement.hpp" #include "Application.hpp" -#include "controllers/moderationactions/ModerationActions.hpp" #include "debug/Benchmark.hpp" #include "messages/Emote.hpp" #include "messages/layouts/MessageLayoutContainer.hpp" @@ -374,8 +373,8 @@ void TwitchModerationElement::addToContainer(MessageLayoutContainer &container, { QSize size(int(container.getScale() * 16), int(container.getScale() * 16)); - - for (const auto &action : getApp()->moderationActions->items) + auto actions = getCSettings().moderationActions.readOnly(); + for (const auto &action : *actions) { if (auto image = action.getImage()) { diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 730bf3ca..76c42d66 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -238,7 +238,7 @@ void TwitchMessageBuilder::triggerHighlights() return; } - if (getApp()->pings->isMuted(this->channel->getName())) + if (getCSettings().isMutedChannel(this->channel->getName())) { // Do nothing. Pings are muted in this channel. return; diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 0e42db01..95a121d6 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -4,6 +4,7 @@ #include "controllers/highlights/HighlightBlacklistUser.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnorePhrase.hpp" +#include "controllers/pings/MutedChannelController.hpp" #include "singletons/Paths.hpp" #include "singletons/Resources.hpp" #include "singletons/WindowManager.hpp" @@ -15,11 +16,21 @@ namespace chatterino { ConcurrentSettings *concurrentInstance_{}; ConcurrentSettings::ConcurrentSettings() + // NOTE: these do not get deleted : highlightedMessages(*new SignalVector()) , highlightedUsers(*new SignalVector()) , blacklistedUsers(*new SignalVector()) , ignoredMessages(*new SignalVector()) + , mutedChannels(*new SignalVector()) + , moderationActions(*new SignalVector) { + persist(this->highlightedMessages, "/highlighting/highlights"); + persist(this->blacklistedUsers, "/highlighting/blacklist"); + persist(this->highlightedUsers, "/highlighting/users"); + persist(this->ignoredMessages, "/ignore/phrases"); + persist(this->mutedChannels, "/pings/muted"); + // tagged users? + persist(this->moderationActions, "/moderation/actions"); } bool ConcurrentSettings::isHighlightedUser(const QString &username) @@ -46,6 +57,50 @@ bool ConcurrentSettings::isBlacklistedUser(const QString &username) return false; } +bool ConcurrentSettings::isMutedChannel(const QString &channelName) +{ + for (const auto &channel : this->mutedChannels) + { + if (channelName.toLower() == channel.toLower()) + { + return true; + } + } + return false; +} + +void ConcurrentSettings::mute(const QString &channelName) +{ + mutedChannels.append(channelName); +} + +void ConcurrentSettings::unmute(const QString &channelName) +{ + for (std::vector::size_type i = 0; i != mutedChannels.raw().size(); + i++) + { + if (mutedChannels.raw()[i].toLower() == channelName.toLower()) + { + mutedChannels.removeAt(i); + i--; + } + } +} + +bool ConcurrentSettings::toggleMutedChannel(const QString &channelName) +{ + if (this->isMutedChannel(channelName)) + { + unmute(channelName); + return false; + } + else + { + mute(channelName); + return true; + } +} + ConcurrentSettings &getCSettings() { // `concurrentInstance_` gets assigned in Settings ctor. @@ -62,11 +117,6 @@ Settings::Settings(const QString &settingsDirectory) instance_ = this; concurrentInstance_ = this; - persist(this->highlightedMessages, "/highlighting/highlights"); - persist(this->blacklistedUsers, "/highlighting/blacklist"); - persist(this->highlightedUsers, "/highlighting/users"); - persist(this->ignoredMessages, "/ignore/phrases"); - #ifdef USEWINSDK this->autorun = isRegisteredForStartup(); this->autorun.connect( diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index cc2e4e3c..cb5613ef 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -15,26 +15,35 @@ namespace chatterino { class HighlightPhrase; class HighlightBlacklistUser; class IgnorePhrase; +class TaggedUser; -// Settings which are availlable for reading on all threads. +/// Settings which are availlable for reading on all threads. class ConcurrentSettings { public: ConcurrentSettings(); - // clang-format off - SignalVector &highlightedMessages; - SignalVector &highlightedUsers; + SignalVector &highlightedMessages; + SignalVector &highlightedUsers; SignalVector &blacklistedUsers; - SignalVector &ignoredMessages; - // clang-format on + SignalVector &ignoredMessages; + SignalVector &mutedChannels; + //SignalVector &taggedUsers; + SignalVector &moderationActions; bool isHighlightedUser(const QString &username); bool isBlacklistedUser(const QString &username); + bool isMutedChannel(const QString &channelName); + bool toggleMutedChannel(const QString &channelName); + +private: + void mute(const QString &channelName); + void unmute(const QString &channelName); }; ConcurrentSettings &getCSettings(); +/// Settings which are availlable for reading and writing on the gui thread. // These settings are still accessed concurrently in the code but it is bad practice. class Settings : public ABSettings, public ConcurrentSettings { diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index 8b1754d8..55f282c7 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -2,8 +2,6 @@ #include "Application.hpp" #include "controllers/moderationactions/ModerationActionModel.hpp" -#include "controllers/moderationactions/ModerationActions.hpp" -#include "controllers/taggedusers/TaggedUsersController.hpp" #include "controllers/taggedusers/TaggedUsersModel.hpp" #include "singletons/Logging.hpp" #include "singletons/Paths.hpp" @@ -175,7 +173,7 @@ ModerationPage::ModerationPage() modMode .emplace( (new ModerationActionModel(nullptr)) - ->initialized(&app->moderationActions->items)) + ->initialized(&getSettings()->moderationActions)) .getElement(); view->setTitles({"Actions"}); @@ -185,7 +183,7 @@ ModerationPage::ModerationPage() 0, QHeaderView::Stretch); view->addButtonPressed.connect([] { - getApp()->moderationActions->items.append( + getSettings()->moderationActions.append( ModerationAction("/timeout {user} 300")); }); diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 84a389aa..49a474e3 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -2,7 +2,6 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" -#include "controllers/moderationactions/ModerationActions.hpp" #include "controllers/notifications/NotificationController.hpp" #include "controllers/pings/MutedChannelController.hpp" #include "providers/twitch/TwitchChannel.hpp" @@ -189,7 +188,7 @@ void SplitHeader::initializeLayout() switch (button) { case Qt::LeftButton: - if (getApp()->moderationActions->items.empty()) + if (getSettings()->moderationActions.empty()) { getApp()->windows->showSettingsDialog( SettingsDialogPreference:: @@ -233,9 +232,9 @@ void SplitHeader::initializeLayout() }); // update moderation button when items changed - this->managedConnect(getApp()->moderationActions->items.delayedItemsChanged, + this->managedConnect(getSettings()->moderationActions.delayedItemsChanged, [this] { - if (getApp()->moderationActions->items.empty()) + if (getSettings()->moderationActions.empty()) { if (this->split_->getModerationMode()) this->split_->setModerationMode(true); @@ -339,11 +338,12 @@ std::unique_ptr SplitHeader::createMainMenu() action->setCheckable(true); QObject::connect(moreMenu, &QMenu::aboutToShow, this, [action, this]() { - action->setChecked(getApp()->pings->isMuted( + action->setChecked(getSettings()->isMutedChannel( this->split_->getChannel()->getName())); }); action->connect(action, &QAction::triggered, this, [this]() { - getApp()->pings->toggleMuted(this->split_->getChannel()->getName()); + getSettings()->toggleMutedChannel( + this->split_->getChannel()->getName()); }); moreMenu->addAction(action); @@ -567,7 +567,7 @@ void SplitHeader::updateChannelText() void SplitHeader::updateModerationModeIcon() { auto moderationMode = this->split_->getModerationMode() && - !getApp()->moderationActions->items.empty(); + !getSettings()->moderationActions.empty(); this->moderationButton_->setPixmap( moderationMode ? getResources().buttons.modModeEnabled From ba5d8427675bfb962f6cdf5b972c2f7120ff4cd3 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:04:25 +0100 Subject: [PATCH 38/67] removed MutedChannelController --- chatterino.pro | 3 --- src/controllers/ignores/IgnoreController.cpp | 17 ------------ src/controllers/ignores/IgnoreController.hpp | 19 ------------- .../pings/MutedChannelController.cpp | 12 --------- .../pings/MutedChannelController.hpp | 27 ------------------- src/providers/twitch/TwitchMessageBuilder.cpp | 2 +- src/singletons/Settings.cpp | 1 - src/widgets/splits/SplitHeader.cpp | 1 - 8 files changed, 1 insertion(+), 81 deletions(-) delete mode 100644 src/controllers/ignores/IgnoreController.cpp delete mode 100644 src/controllers/pings/MutedChannelController.cpp delete mode 100644 src/controllers/pings/MutedChannelController.hpp diff --git a/chatterino.pro b/chatterino.pro index 755bc589..41327803 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -134,13 +134,11 @@ SOURCES += \ src/controllers/highlights/HighlightModel.cpp \ src/controllers/highlights/HighlightPhrase.cpp \ src/controllers/highlights/UserHighlightModel.cpp \ - src/controllers/ignores/IgnoreController.cpp \ src/controllers/ignores/IgnoreModel.cpp \ src/controllers/moderationactions/ModerationAction.cpp \ src/controllers/moderationactions/ModerationActionModel.cpp \ src/controllers/notifications/NotificationController.cpp \ src/controllers/notifications/NotificationModel.cpp \ - src/controllers/pings/MutedChannelController.cpp \ src/controllers/pings/MutedChannelModel.cpp \ src/controllers/taggedusers/TaggedUser.cpp \ src/controllers/taggedusers/TaggedUsersModel.cpp \ @@ -333,7 +331,6 @@ HEADERS += \ src/controllers/moderationactions/ModerationActionModel.hpp \ src/controllers/notifications/NotificationController.hpp \ src/controllers/notifications/NotificationModel.hpp \ - src/controllers/pings/MutedChannelController.hpp \ src/controllers/pings/MutedChannelModel.hpp \ src/controllers/taggedusers/TaggedUser.hpp \ src/controllers/taggedusers/TaggedUsersModel.hpp \ diff --git a/src/controllers/ignores/IgnoreController.cpp b/src/controllers/ignores/IgnoreController.cpp deleted file mode 100644 index 0415032e..00000000 --- a/src/controllers/ignores/IgnoreController.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "controllers/ignores/IgnoreController.hpp" - -#include "Application.hpp" -#include "controllers/ignores/IgnoreModel.hpp" -#include "util/PersistSignalVector.hpp" - -#include - -namespace chatterino { - -void IgnoreController::initialize(Settings &, Paths &) -{ - assert(!this->initialized_); - this->initialized_ = true; -} - -} // namespace chatterino diff --git a/src/controllers/ignores/IgnoreController.hpp b/src/controllers/ignores/IgnoreController.hpp index c0b11e82..fed12f12 100644 --- a/src/controllers/ignores/IgnoreController.hpp +++ b/src/controllers/ignores/IgnoreController.hpp @@ -1,26 +1,7 @@ #pragma once -#include "common/ChatterinoSetting.hpp" -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "controllers/ignores/IgnorePhrase.hpp" - namespace chatterino { -class Settings; -class Paths; - -class IgnoreModel; - enum class ShowIgnoredUsersMessages { Never, IfModerator, IfBroadcaster }; -class IgnoreController final : public Singleton -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - -private: - bool initialized_ = false; -}; - } // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.cpp b/src/controllers/pings/MutedChannelController.cpp deleted file mode 100644 index 62555fd5..00000000 --- a/src/controllers/pings/MutedChannelController.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "controllers/pings/MutedChannelController.hpp" -#include "controllers/pings/MutedChannelModel.hpp" -#include "util/PersistSignalVector.hpp" - -namespace chatterino { - -void MutedChannelController::initialize(Settings &settings, Paths &paths) -{ - this->initialized_ = true; -} - -} // namespace chatterino diff --git a/src/controllers/pings/MutedChannelController.hpp b/src/controllers/pings/MutedChannelController.hpp deleted file mode 100644 index ea681fdc..00000000 --- a/src/controllers/pings/MutedChannelController.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -#include "common/SignalVector.hpp" -#include "common/Singleton.hpp" -#include "singletons/Settings.hpp" - -namespace chatterino { - -class Settings; -class Paths; - -class MutedChannelModel; - -class MutedChannelController final : public Singleton, private QObject -{ -public: - virtual void initialize(Settings &settings, Paths &paths) override; - -private: - bool initialized_ = false; - - SignalVector channels; -}; - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 76c42d66..a5ecaae4 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -3,7 +3,7 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/ignores/IgnoreController.hpp" -#include "controllers/pings/MutedChannelController.hpp" +#include "controllers/ignores/IgnorePhrase.hpp" #include "messages/Message.hpp" #include "providers/chatterino/ChatterinoBadges.hpp" #include "providers/twitch/TwitchBadges.hpp" diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index 95a121d6..60e38e55 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -4,7 +4,6 @@ #include "controllers/highlights/HighlightBlacklistUser.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/ignores/IgnorePhrase.hpp" -#include "controllers/pings/MutedChannelController.hpp" #include "singletons/Paths.hpp" #include "singletons/Resources.hpp" #include "singletons/WindowManager.hpp" diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 49a474e3..fb8c024b 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -3,7 +3,6 @@ #include "Application.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/notifications/NotificationController.hpp" -#include "controllers/pings/MutedChannelController.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchIrcServer.hpp" #include "singletons/Resources.hpp" From b887f1c90ee2401ded810a082e388dbf6513733c Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:05:50 +0100 Subject: [PATCH 39/67] fix crash --- src/widgets/dialogs/SettingsDialog.cpp | 17 +++++++++++------ src/widgets/dialogs/SettingsDialog.hpp | 2 +- src/widgets/helper/SettingsDialogTab.hpp | 2 +- src/widgets/settingspages/GeneralPage.cpp | 3 ++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 5fc79780..a9ca5432 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -155,26 +155,26 @@ void SettingsDialog::addTabs() // clang-format off this->addTab([]{return new GeneralPage;}, "General", ":/settings/about.svg"); this->ui_.tabContainer->addSpacing(16); - this->addTab([]{return new AccountsPage;}, "Accounts", ":/settings/accounts.svg"); + this->addTab([]{return new AccountsPage;}, "Accounts", ":/settings/accounts.svg", SettingsTabId::Accounts); this->ui_.tabContainer->addSpacing(16); this->addTab([]{return new CommandPage;}, "Commands", ":/settings/commands.svg"); this->addTab([]{return new HighlightingPage;}, "Highlights", ":/settings/notifications.svg"); this->addTab([]{return new IgnoresPage;}, "Ignores", ":/settings/ignore.svg"); this->ui_.tabContainer->addSpacing(16); this->addTab([]{return new KeyboardSettingsPage;}, "Keybindings", ":/settings/keybinds.svg"); - this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg"); + this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg", SettingsTabId::Moderation); this->addTab([]{return new NotificationPage;}, "Notifications", ":/settings/notification2.svg"); this->addTab([]{return new ExternalToolsPage;}, "External tools", ":/settings/externaltools.svg"); this->ui_.tabContainer->addStretch(1); - this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", Qt::AlignBottom); + this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", SettingsTabId(), Qt::AlignBottom); // clang-format on } void SettingsDialog::addTab(std::function page, const QString &name, const QString &iconPath, - Qt::Alignment alignment) + SettingsTabId id, Qt::Alignment alignment) { - auto tab = new SettingsDialogTab(this, std::move(page), name, iconPath); + auto tab = new SettingsDialogTab(this, std::move(page), name, iconPath, id); this->ui_.tabContainer->addWidget(tab, 0, alignment); this->tabs_.push_back(tab); @@ -216,7 +216,12 @@ void SettingsDialog::selectTab(SettingsDialogTab *tab, bool byUser) void SettingsDialog::selectTab(SettingsTabId id) { - this->selectTab(this->tab(id)); + auto t = this->tab(id); + assert(t); + if (!t) + return; + + this->selectTab(t); } SettingsDialogTab *SettingsDialog::tab(SettingsTabId id) diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 84cdb98d..5dbef8b6 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -51,7 +51,7 @@ private: void initUi(); void addTabs(); void addTab(std::function page, const QString &name, - const QString &iconPath, + const QString &iconPath, SettingsTabId id = {}, Qt::Alignment alignment = Qt::AlignTop); void selectTab(SettingsDialogTab *tab, const bool byUser = true); void selectTab(SettingsTabId id); diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index 49dc9ff2..97c17f93 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -26,7 +26,7 @@ public: SettingsDialogTab(SettingsDialog *dialog_, std::function page_, const QString &name, QString imageFileName, - SettingsTabId id = {}); + SettingsTabId id); void setSelected(bool selected_); SettingsPage *page(); diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 086b7a6a..d891d085 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -328,7 +328,8 @@ void GeneralPage::initLayout(SettingsLayout &layout) layout.addCheckbox("Smooth scrolling on new messages", s.enableSmoothScrollingNewMessages); layout.addDropdown( - "Pause on hover", {"Disabled", "0.5s", "1s", "2s", "5s", "Indefinite"}, + "Pause after hover", + {"Disabled", "0.5s", "1s", "2s", "5s", "Indefinite"}, s.pauseOnHoverDuration, [](auto val) { if (val < -0.5f) From d83b597c75efb68aa7864219550b101bb737e074 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:12:22 +0100 Subject: [PATCH 40/67] removed unused function in SettingsDialog --- src/widgets/dialogs/SettingsDialog.cpp | 7 ------- src/widgets/dialogs/SettingsDialog.hpp | 5 +---- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index a9ca5432..7effdb89 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -20,8 +20,6 @@ namespace chatterino { -SettingsDialog *SettingsDialog::instance_ = nullptr; - SettingsDialog::SettingsDialog() : BaseWindow(BaseWindow::DisableCustomScaling) { @@ -138,11 +136,6 @@ void SettingsDialog::filterElements(const QString &text) } } -SettingsDialog *SettingsDialog::instance() -{ - return SettingsDialog::instance_; -} - void SettingsDialog::addTabs() { this->ui_.tabContainer->setMargin(0); diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index 5dbef8b6..0d08f7c9 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -34,7 +34,6 @@ class SettingsDialog : public BaseWindow public: SettingsDialog(); - static SettingsDialog *instance(); // may be NULL static void showDialog(SettingsDialogPreference preferredTab = SettingsDialogPreference::NoPreference); @@ -44,18 +43,16 @@ protected: virtual void showEvent(QShowEvent *) override; private: - static SettingsDialog *instance_; - void refresh(); void initUi(); + SettingsDialogTab *tab(SettingsTabId id); void addTabs(); void addTab(std::function page, const QString &name, const QString &iconPath, SettingsTabId id = {}, Qt::Alignment alignment = Qt::AlignTop); void selectTab(SettingsDialogTab *tab, const bool byUser = true); void selectTab(SettingsTabId id); - SettingsDialogTab *tab(SettingsTabId id); void filterElements(const QString &query); void onOkClicked(); From 269d2ee67f40dca53696f90ba40cb9f6d7396d16 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:48:04 +0100 Subject: [PATCH 41/67] improved user and settings icon --- src/widgets/helper/Button.cpp | 1 - src/widgets/helper/NotebookButton.cpp | 20 +++++++++----------- src/widgets/helper/TitlebarButton.cpp | 2 -- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/widgets/helper/Button.cpp b/src/widgets/helper/Button.cpp index fe00b951..cba38570 100644 --- a/src/widgets/helper/Button.cpp +++ b/src/widgets/helper/Button.cpp @@ -154,7 +154,6 @@ void Button::fancyPaint(QPainter &painter) return; } - painter.setRenderHint(QPainter::HighQualityAntialiasing); painter.setRenderHint(QPainter::Antialiasing); QColor c; diff --git a/src/widgets/helper/NotebookButton.cpp b/src/widgets/helper/NotebookButton.cpp index 3b76adca..57f99c2e 100644 --- a/src/widgets/helper/NotebookButton.cpp +++ b/src/widgets/helper/NotebookButton.cpp @@ -91,7 +91,6 @@ void NotebookButton::paintEvent(QPaintEvent *event) case User: { painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); auto a = w / 8; QPainterPath path; @@ -99,19 +98,18 @@ void NotebookButton::paintEvent(QPaintEvent *event) path.arcMoveTo(a, 4 * a, 6 * a, 6 * a, 0); path.arcTo(a, 4 * a, 6 * a, 6 * a, 0, 180); + QPainterPath remove; + remove.addEllipse(2 * a, 1 * a, 4 * a, 4 * a); + path = path.subtracted(remove); + + path.addEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a); + painter.fillPath(path, foreground); - - painter.setBrush(background); - painter.drawEllipse(2 * a, 1 * a, 4 * a, 4 * a); - - painter.setBrush(foreground); - painter.drawEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a); } break; case Settings: { painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); auto a = w / 8; QPainterPath path; @@ -126,10 +124,10 @@ void NotebookButton::paintEvent(QPaintEvent *event) i * (360 / 8.0) + (360 / 32.0), (360 / 32.0)); } - painter.fillPath(path, foreground); + QPainterPath remove; + remove.addEllipse(3 * a, 3 * a, 2 * a, 2 * a); - painter.setBrush(background); - painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a); + painter.fillPath(path.subtracted(remove), foreground); } break; diff --git a/src/widgets/helper/TitlebarButton.cpp b/src/widgets/helper/TitlebarButton.cpp index d7fe2857..b6eeb878 100644 --- a/src/widgets/helper/TitlebarButton.cpp +++ b/src/widgets/helper/TitlebarButton.cpp @@ -67,7 +67,6 @@ void TitleBarButton::paintEvent(QPaintEvent *event) color = "#999"; painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); auto a = xD / 3; QPainterPath path; @@ -92,7 +91,6 @@ void TitleBarButton::paintEvent(QPaintEvent *event) case TitleBarButtonStyle::Settings: { color = "#999"; painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); painter.save(); painter.translate(3, 3); From 1e1a56bb87ce051e7eb2b7e0539a6d914d292023 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 19:52:15 +0100 Subject: [PATCH 42/67] fixed position of user icon head --- src/widgets/helper/NotebookButton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/helper/NotebookButton.cpp b/src/widgets/helper/NotebookButton.cpp index 57f99c2e..cfdf7d03 100644 --- a/src/widgets/helper/NotebookButton.cpp +++ b/src/widgets/helper/NotebookButton.cpp @@ -102,7 +102,7 @@ void NotebookButton::paintEvent(QPaintEvent *event) remove.addEllipse(2 * a, 1 * a, 4 * a, 4 * a); path = path.subtracted(remove); - path.addEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a); + path.addEllipse(2.5 * a, 1.5 * a, 3 * a, 3 * a); painter.fillPath(path, foreground); } From a889d8d186f8b28892e133aafb06438865a48893 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 28 Feb 2020 23:33:27 +0100 Subject: [PATCH 43/67] Disabled travis CI for now --- .travis.yml => _.travis.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .travis.yml => _.travis.yml (100%) diff --git a/.travis.yml b/_.travis.yml similarity index 100% rename from .travis.yml rename to _.travis.yml From 82354327e5f7dd075b2aec98c9dbdad9cd7390df Mon Sep 17 00:00:00 2001 From: mmb L Date: Sat, 29 Feb 2020 18:52:49 +0800 Subject: [PATCH 44/67] Add option for username tab completion only with an @ prefix (#1573) * add an additional check for username completion without an @ prefix * Changed setting name * default userCompletionOnlyWithAt to false --- src/common/CompletionModel.cpp | 2 +- src/singletons/Settings.hpp | 2 ++ src/widgets/settingspages/GeneralPage.cpp | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/CompletionModel.cpp b/src/common/CompletionModel.cpp index 6cb3bc80..f3d368da 100644 --- a/src/common/CompletionModel.cpp +++ b/src/common/CompletionModel.cpp @@ -127,7 +127,7 @@ void CompletionModel::refresh(const QString &prefix, bool isFirstWord) TaggedString::Type::Username); } } - else + else if (!getSettings()->userCompletionOnlyWithAt) { for (const auto &name : usernames->subrange(Prefix(usernamePrefix))) diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index cb5613ef..e6d97504 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -134,6 +134,8 @@ public: "/behaviour/autocompletion/smallStreamerLimit", 1000}; BoolSetting prefixOnlyEmoteCompletion = { "/behaviour/autocompletion/prefixOnlyCompletion", true}; + BoolSetting userCompletionOnlyWithAt = { + "/behaviour/autocompletion/userCompletionOnlyWithAt", false}; FloatSetting pauseOnHoverDuration = {"/behaviour/pauseOnHoverDuration", 0}; EnumSetting pauseChatModifier = { diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index d891d085..d0cf636e 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -525,6 +525,8 @@ void GeneralPage::initLayout(SettingsLayout &layout) layout.addCheckbox( "Only search for emote autocompletion at the start of emote names", s.prefixOnlyEmoteCompletion); + layout.addCheckbox("Only search for username autocompletion with an @", + s.userCompletionOnlyWithAt); layout.addCheckbox("Show twitch whispers inline", s.inlineWhispers); layout.addCheckbox("Highlight received inline whispers", From 26d3078defe74333a22eb9e7552a8b37d8595396 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sat, 29 Feb 2020 13:56:14 +0100 Subject: [PATCH 45/67] removed unused variables --- src/widgets/helper/ChannelView.hpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 30868162..6ea7659e 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -197,13 +197,6 @@ private: LimitedQueue messages_; - pajlada::Signals::Connection messageAppendedConnection_; - pajlada::Signals::Connection messageAddedAtStartConnection_; - pajlada::Signals::Connection messageRemovedConnection_; - pajlada::Signals::Connection messageReplacedConnection_; - pajlada::Signals::Connection repaintGifsConnection_; - pajlada::Signals::Connection layoutConnection_; - std::vector connections_; std::vector channelConnections_; From 20612a1381c690611a8b99a1d3f4fa095b5f0387 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sun, 1 Mar 2020 06:05:08 -0500 Subject: [PATCH 46/67] Fix repeated signal connects on reconnect in irc server (#1576) --- src/providers/irc/AbstractIrcServer.cpp | 22 ++++++ src/providers/irc/AbstractIrcServer.hpp | 13 ++++ src/providers/irc/IrcServer.cpp | 94 +++++++++++++----------- src/providers/irc/IrcServer.hpp | 2 + src/providers/twitch/TwitchIrcServer.cpp | 2 + 5 files changed, 92 insertions(+), 41 deletions(-) diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 0a3019e5..2618465a 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -71,8 +71,30 @@ AbstractIrcServer::AbstractIrcServer() }); } +void AbstractIrcServer::initializeIrc() +{ + assert(!this->initialized_); + + if (this->hasSeparateWriteConnection()) + { + this->initializeConnectionSignals(this->writeConnection_.get(), + ConnectionType::Write); + this->initializeConnectionSignals(this->readConnection_.get(), + ConnectionType::Read); + } + else + { + this->initializeConnectionSignals(this->readConnection_.get(), + ConnectionType::Both); + } + + this->initialized_ = true; +} + void AbstractIrcServer::connect() { + assert(this->initialized_); + this->disconnect(); if (this->hasSeparateWriteConnection()) diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index 20d78249..1c865ef5 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -21,6 +21,10 @@ public: virtual ~AbstractIrcServer() = default; + // initializeIrc must be called from the derived class + // this allows us to initialize the abstract irc server based on the derived class's parameters + void initializeIrc(); + // connection void connect(); void disconnect(); @@ -45,8 +49,15 @@ public: protected: AbstractIrcServer(); + // initializeConnectionSignals is called on a connection once in its lifetime. + // it can be used to connect signals to your class + virtual void initializeConnectionSignals(IrcConnection *connection, + ConnectionType type){}; + + // initializeConnection is called every time before we try to connect to the irc server virtual void initializeConnection(IrcConnection *connection, ConnectionType type) = 0; + virtual std::shared_ptr createChannel( const QString &channelName) = 0; @@ -83,6 +94,8 @@ private: // bool autoReconnect_ = false; pajlada::Signals::SignalHolder connections_; + + bool initialized_{false}; }; } // namespace chatterino diff --git a/src/providers/irc/IrcServer.cpp b/src/providers/irc/IrcServer.cpp index dd7505ff..1b3aea5f 100644 --- a/src/providers/irc/IrcServer.cpp +++ b/src/providers/irc/IrcServer.cpp @@ -15,6 +15,8 @@ namespace chatterino { IrcServer::IrcServer(const IrcServerData &data) : data_(new IrcServerData(data)) { + this->initializeIrc(); + this->connect(); } @@ -51,6 +53,57 @@ const QString &IrcServer::nick() return this->data_->nick.isEmpty() ? this->data_->user : this->data_->nick; } +void IrcServer::initializeConnectionSignals(IrcConnection *connection, + ConnectionType type) +{ + assert(type == Both); + + QObject::connect( + connection, &Communi::IrcConnection::socketError, this, + [this](QAbstractSocket::SocketError error) { + static int index = + QAbstractSocket::staticMetaObject.indexOfEnumerator( + "SocketError"); + + std::lock_guard lock(this->channelMutex); + + for (auto &&weak : this->channels) + { + if (auto shared = weak.lock()) + { + shared->addMessage(makeSystemMessage( + QStringLiteral("Socket error: ") + + QAbstractSocket::staticMetaObject.enumerator(index) + .valueToKey(error))); + } + } + }); + + QObject::connect(connection, &Communi::IrcConnection::nickNameRequired, + this, [](const QString &reserved, QString *result) { + *result = reserved + (std::rand() % 100); + }); + + QObject::connect(connection, &Communi::IrcConnection::noticeMessageReceived, + this, [this](Communi::IrcNoticeMessage *message) { + MessageBuilder builder; + + builder.emplace(); + builder.emplace( + message->nick(), MessageElementFlag::Username); + builder.emplace( + "-> you:", MessageElementFlag::Username); + builder.emplace(message->content(), + MessageElementFlag::Text); + + auto msg = builder.release(); + + for (auto &&weak : this->channels) + if (auto shared = weak.lock()) + shared->addMessage(msg); + }); +} + void IrcServer::initializeConnection(IrcConnection *connection, ConnectionType type) { @@ -90,47 +143,6 @@ void IrcServer::initializeConnection(IrcConnection *connection, this->open(Both); } } - - QObject::connect( - connection, &Communi::IrcConnection::socketError, this, - [this](QAbstractSocket::SocketError error) { - static int index = - QAbstractSocket::staticMetaObject.indexOfEnumerator( - "SocketError"); - - std::lock_guard lock(this->channelMutex); - - for (auto &&weak : this->channels) - if (auto shared = weak.lock()) - shared->addMessage(makeSystemMessage( - QStringLiteral("Socket error: ") + - QAbstractSocket::staticMetaObject.enumerator(index) - .valueToKey(error))); - }); - - QObject::connect(connection, &Communi::IrcConnection::nickNameRequired, - this, [](const QString &reserved, QString *result) { - *result = reserved + (std::rand() % 100); - }); - - QObject::connect(connection, &Communi::IrcConnection::noticeMessageReceived, - this, [this](Communi::IrcNoticeMessage *message) { - MessageBuilder builder; - - builder.emplace(); - builder.emplace( - message->nick(), MessageElementFlag::Username); - builder.emplace( - "-> you:", MessageElementFlag::Username); - builder.emplace(message->content(), - MessageElementFlag::Text); - - auto msg = builder.release(); - - for (auto &&weak : this->channels) - if (auto shared = weak.lock()) - shared->addMessage(msg); - }); } std::shared_ptr IrcServer::createChannel(const QString &channelName) diff --git a/src/providers/irc/IrcServer.hpp b/src/providers/irc/IrcServer.hpp index 0b5a0aad..6c1251fa 100644 --- a/src/providers/irc/IrcServer.hpp +++ b/src/providers/irc/IrcServer.hpp @@ -21,6 +21,8 @@ public: // AbstractIrcServer interface protected: + void initializeConnectionSignals(IrcConnection *connection, + ConnectionType type) override; void initializeConnection(IrcConnection *connection, ConnectionType type) override; std::shared_ptr createChannel(const QString &channelName) override; diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 7ed568c8..0f1d4884 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -27,6 +27,8 @@ TwitchIrcServer::TwitchIrcServer() , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions)) , watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching) { + this->initializeIrc(); + this->pubsub = new PubSub; // getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) { From be745862fd6d1a3bcd7abffc34fa9176e256d7af Mon Sep 17 00:00:00 2001 From: Daniel Pasch Date: Sun, 1 Mar 2020 12:20:59 +0100 Subject: [PATCH 47/67] Update contributors list (gempir) (#1583) --- resources/contributors.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/contributors.txt b/resources/contributors.txt index ea0644a7..cf94931e 100644 --- a/resources/contributors.txt +++ b/resources/contributors.txt @@ -29,6 +29,7 @@ TranRed | https://github.com/TranRed | | Contributor RAnders00 | https://github.com/RAnders00 | | Contributor YungLPR | https://github.com/leon-richardt | | Contributor Mm2PL | https://github.com/mm2pl | | Contributor +gempir | https://github.com/gempir | | Contributor # If you are a contributor add yourself above this line Defman21 | https://github.com/Defman21 | | Documentation From 2e39dd4d9bc19f46f60d3e1f8224efe26f6fe500 Mon Sep 17 00:00:00 2001 From: pajlada Date: Sun, 1 Mar 2020 08:15:43 -0500 Subject: [PATCH 48/67] macos ci: Replace create-dmg with builddmg (#1587) --- .CI/CreateDMG.sh | 20 ++-- .CI/dmg-settings.py | 247 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+), 10 deletions(-) create mode 100644 .CI/dmg-settings.py diff --git a/.CI/CreateDMG.sh b/.CI/CreateDMG.sh index 60a8f534..8ad191e2 100755 --- a/.CI/CreateDMG.sh +++ b/.CI/CreateDMG.sh @@ -1,13 +1,13 @@ #!/bin/sh echo "Running MACDEPLOYQT" -/usr/local/opt/qt/bin/macdeployqt chatterino.app -dmg -echo "Creating APP folder" -mkdir app -echo "Running hdiutil attach on the built DMG" -hdiutil attach chatterino.dmg -echo "Copying chatterino.app into the app folder" -cp -r /Volumes/chatterino/chatterino.app app/ -echo "Creating DMG with create-dmg" -create-dmg --volname Chatterino2 --volicon ../resources/chatterino.icns --icon-size 50 --app-drop-link 0 0 --format UDBZ chatterino-osx.dmg app/ -echo "DONE" +/usr/local/opt/qt/bin/macdeployqt chatterino.app +echo "Creating python3 virtual environment" +python3 -m venv venv +echo "Entering python3 virtual environment" +. venv/bin/activate +echo "Installing dmgbuild" +python3 -m pip install dmgbuild +echo "Running dmgbuild.." +dmgbuild --settings ./../.CI/dmg-settings.py -D app=./chatterino.app Chatterino2 chatterino-osx.dmg +echo "Done!" diff --git a/.CI/dmg-settings.py b/.CI/dmg-settings.py new file mode 100644 index 00000000..6b068fa1 --- /dev/null +++ b/.CI/dmg-settings.py @@ -0,0 +1,247 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import biplist +import os.path + +# +# Example settings file for dmgbuild +# + +# Use like this: dmgbuild -s settings.py "Test Volume" test.dmg + +# You can actually use this file for your own application (not just TextEdit) +# by doing e.g. +# +# dmgbuild -s settings.py -D app=/path/to/My.app "My Application" MyApp.dmg + +# .. Useful stuff .............................................................. + +application = defines.get('app', '/Applications/TextEdit.app') +appname = os.path.basename(application) + +def icon_from_app(app_path): + plist_path = os.path.join(app_path, 'Contents', 'Info.plist') + plist = biplist.readPlist(plist_path) + icon_name = plist['CFBundleIconFile'] + icon_root,icon_ext = os.path.splitext(icon_name) + if not icon_ext: + icon_ext = '.icns' + icon_name = icon_root + icon_ext + return os.path.join(app_path, 'Contents', 'Resources', icon_name) + +# .. Basics .................................................................... + +# Uncomment to override the output filename +# filename = 'test.dmg' + +# Uncomment to override the output volume name +# volume_name = 'Test' + +# Volume format (see hdiutil create -help) +format = defines.get('format', 'UDBZ') + +# Volume size +size = defines.get('size', None) + +# Files to include +files = [ application ] + +# Symlinks to create +symlinks = { 'Applications': '/Applications' } + +# Volume icon +# +# You can either define icon, in which case that icon file will be copied to the +# image, *or* you can define badge_icon, in which case the icon file you specify +# will be used to badge the system's Removable Disk icon +# +#icon = '/path/to/icon.icns' +badge_icon = icon_from_app(application) + +# Where to put the icons +icon_locations = { + appname: (140, 120), + 'Applications': (500, 120) + } + +# .. Window configuration ...................................................... + +# Background +# +# This is a STRING containing any of the following: +# +# #3344ff - web-style RGB color +# #34f - web-style RGB color, short form (#34f == #3344ff) +# rgb(1,0,0) - RGB color, each value is between 0 and 1 +# hsl(120,1,.5) - HSL (hue saturation lightness) color +# hwb(300,0,0) - HWB (hue whiteness blackness) color +# cmyk(0,1,0,0) - CMYK color +# goldenrod - X11/SVG named color +# builtin-arrow - A simple built-in background with a blue arrow +# /foo/bar/baz.png - The path to an image file +# +# The hue component in hsl() and hwb() may include a unit; it defaults to +# degrees ('deg'), but also supports radians ('rad') and gradians ('grad' +# or 'gon'). +# +# Other color components may be expressed either in the range 0 to 1, or +# as percentages (e.g. 60% is equivalent to 0.6). +background = 'builtin-arrow' + +show_status_bar = False +show_tab_view = False +show_toolbar = False +show_pathbar = False +show_sidebar = False +sidebar_width = 180 + +# Window position in ((x, y), (w, h)) format +window_rect = ((100, 100), (640, 280)) + +# Select the default view; must be one of +# +# 'icon-view' +# 'list-view' +# 'column-view' +# 'coverflow' +# +default_view = 'icon-view' + +# General view configuration +show_icon_preview = False + +# Set these to True to force inclusion of icon/list view settings (otherwise +# we only include settings for the default view) +include_icon_view_settings = 'auto' +include_list_view_settings = 'auto' + +# .. Icon view configuration ................................................... + +arrange_by = None +grid_offset = (0, 0) +grid_spacing = 100 +scroll_position = (0, 0) +label_pos = 'bottom' # or 'right' +text_size = 16 +icon_size = 128 + +# .. List view configuration ................................................... + +# Column names are as follows: +# +# name +# date-modified +# date-created +# date-added +# date-last-opened +# size +# kind +# label +# version +# comments +# +list_icon_size = 16 +list_text_size = 12 +list_scroll_position = (0, 0) +list_sort_by = 'name' +list_use_relative_dates = True +list_calculate_all_sizes = False, +list_columns = ('name', 'date-modified', 'size', 'kind', 'date-added') +list_column_widths = { + 'name': 300, + 'date-modified': 181, + 'date-created': 181, + 'date-added': 181, + 'date-last-opened': 181, + 'size': 97, + 'kind': 115, + 'label': 100, + 'version': 75, + 'comments': 300, + } +list_column_sort_directions = { + 'name': 'ascending', + 'date-modified': 'descending', + 'date-created': 'descending', + 'date-added': 'descending', + 'date-last-opened': 'descending', + 'size': 'descending', + 'kind': 'ascending', + 'label': 'ascending', + 'version': 'ascending', + 'comments': 'ascending', + } + +# .. License configuration ..................................................... + +# Text in the license configuration is stored in the resources, which means +# it gets stored in a legacy Mac encoding according to the language. dmgbuild +# will *try* to convert Unicode strings to the appropriate encoding, *but* +# you should be aware that Python doesn't support all of the necessary encodings; +# in many cases you will need to encode the text yourself and use byte strings +# instead here. + +# Recognized language names are: +# +# af_ZA, ar, be_BY, bg_BG, bn, bo, br, ca_ES, cs_CZ, cy, da_DK, de_AT, de_CH, +# de_DE, dz_BT, el_CY, el_GR, en_AU, en_CA, en_GB, en_IE, en_SG, en_US, eo, +# es_419, es_ES, et_EE, fa_IR, fi_FI, fo_FO, fr_001, fr_BE, fr_CA, fr_CH, +# fr_FR, ga-Latg_IE, ga_IE, gd, grc, gu_IN, gv, he_IL, hi_IN, hr_HR, hu_HU, +# hy_AM, is_IS, it_CH, it_IT, iu_CA, ja_JP, ka_GE, kl, ko_KR, lt_LT, lv_LV, +# mk_MK, mr_IN, mt_MT, nb_NO, ne_NP, nl_BE, nl_NL, nn_NO, pa, pl_PL, pt_BR, +# pt_PT, ro_RO, ru_RU, se, sk_SK, sl_SI, sr_RS, sv_SE, th_TH, to_TO, tr_TR, +# uk_UA, ur_IN, ur_PK, uz_UZ, vi_VN, zh_CN, zh_TW + +# license = { +# 'default-language': 'en_US', +# 'licenses': { +# # For each language, the text of the license. This can be plain text, +# # RTF (in which case it must start "{\rtf1"), or a path to a file +# # containing the license text. If you're using RTF, +# # watch out for Python escaping (or read it from a file). +# 'English': b'''{\\rtf1\\ansi\\ansicpg1252\\cocoartf1504\\cocoasubrtf820 +# {\\fonttbl\\f0\\fnil\\fcharset0 Helvetica-Bold;\\f1\\fnil\\fcharset0 Helvetica;} +# {\\colortbl;\\red255\\green255\\blue255;\\red0\\green0\\blue0;} +# {\\*\\expandedcolortbl;;\\cssrgb\\c0\\c0\\c0;} +# \\paperw11905\\paperh16837\\margl1133\\margr1133\\margb1133\\margt1133 +# \\deftab720 +# \\pard\\pardeftab720\\sa160\\partightenfactor0 + +# \\f0\\b\\fs60 \\cf2 \\expnd0\\expndtw0\\kerning0 +# \\up0 \\nosupersub \\ulnone \\outl0\\strokewidth0 \\strokec2 Test License\\ +# \\pard\\pardeftab720\\sa160\\partightenfactor0 + +# \\fs36 \\cf2 \\strokec2 What is this?\\ +# \\pard\\pardeftab720\\sa160\\partightenfactor0 + +# \\f1\\b0\\fs22 \\cf2 \\strokec2 This is the English license. It says what you are allowed to do with this software.\\ +# \\ +# }''', +# }, +# 'buttons': { +# # For each language, text for the buttons on the licensing window. +# # +# # Default buttons and text are built-in for the following languages: +# # +# # English (en_US), German (de_DE), Spanish (es_ES), French (fr_FR), +# # Italian (it_IT), Japanese (ja_JP), Dutch (nl_NL), Swedish (sv_SE), +# # Brazilian Portuguese (pt_BR), Simplified Chinese (zh_CN), +# # Traditional Chinese (zh_TW), Danish (da_DK), Finnish (fi_FI), +# # Korean (ko_KR), Norwegian (nb_NO) +# # +# # You don't need to specify them for those languages; if you fail to +# # specify them for some other language, English will be used instead. + +# 'en_US': ( +# b'English', +# b'Agree', +# b'Disagree', +# b'Print', +# b'Save', +# b'If you agree with the terms of this license, press "Agree" to ' +# b'install the software. If you do not agree, press "Disagree".' +# ), +# }, +# } + From 9a8b85e338ba4fe3b2bb1eee81f3f6f05387959f Mon Sep 17 00:00:00 2001 From: pajlada Date: Sat, 14 Mar 2020 07:13:57 -0400 Subject: [PATCH 49/67] Twitch API: v5 to Helix migration (#1560) There's a document in src/providers/twitch/api which describes how we interact with the Twitch API. Keeping this up to date might be a healthy way for us to ensure we keep using the right APIs for the right job. --- chatterino.pro | 8 +- .../commands/CommandController.cpp | 36 +- .../notifications/NotificationController.cpp | 106 +++-- .../notifications/NotificationController.hpp | 1 + src/main.cpp | 5 + src/providers/twitch/PartialTwitchUser.cpp | 85 ---- src/providers/twitch/PartialTwitchUser.hpp | 30 -- src/providers/twitch/TwitchAccount.cpp | 52 ++- src/providers/twitch/TwitchAccountManager.cpp | 4 + src/providers/twitch/TwitchApi.cpp | 85 ---- src/providers/twitch/TwitchApi.hpp | 19 - src/providers/twitch/TwitchChannel.cpp | 174 ++++----- src/providers/twitch/TwitchChannel.hpp | 8 +- .../twitch/TwitchParseCheerEmotes.cpp | 1 + src/providers/twitch/api/Helix.cpp | 368 ++++++++++++++++++ src/providers/twitch/api/Helix.hpp | 198 ++++++++++ src/providers/twitch/api/Kraken.cpp | 104 +++++ src/providers/twitch/api/Kraken.hpp | 61 +++ src/providers/twitch/api/README.md | 125 ++++++ src/singletons/Toasts.cpp | 59 +-- src/singletons/Toasts.hpp | 4 - src/widgets/dialogs/LoginDialog.cpp | 21 - src/widgets/dialogs/LoginDialog.hpp | 6 - src/widgets/dialogs/LogsPopup.cpp | 1 - src/widgets/dialogs/UserInfoPopup.cpp | 87 ++--- 25 files changed, 1076 insertions(+), 572 deletions(-) delete mode 100644 src/providers/twitch/PartialTwitchUser.cpp delete mode 100644 src/providers/twitch/PartialTwitchUser.hpp delete mode 100644 src/providers/twitch/TwitchApi.cpp delete mode 100644 src/providers/twitch/TwitchApi.hpp create mode 100644 src/providers/twitch/api/Helix.cpp create mode 100644 src/providers/twitch/api/Helix.hpp create mode 100644 src/providers/twitch/api/Kraken.cpp create mode 100644 src/providers/twitch/api/Kraken.hpp create mode 100644 src/providers/twitch/api/README.md diff --git a/chatterino.pro b/chatterino.pro index 41327803..6ec03fee 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -173,14 +173,14 @@ SOURCES += \ src/providers/irc/IrcConnection2.cpp \ src/providers/irc/IrcServer.cpp \ src/providers/LinkResolver.cpp \ + src/providers/twitch/api/Helix.cpp \ + src/providers/twitch/api/Kraken.cpp \ src/providers/twitch/IrcMessageHandler.cpp \ - src/providers/twitch/PartialTwitchUser.cpp \ src/providers/twitch/PubsubActions.cpp \ src/providers/twitch/PubsubClient.cpp \ src/providers/twitch/PubsubHelpers.cpp \ src/providers/twitch/TwitchAccount.cpp \ src/providers/twitch/TwitchAccountManager.cpp \ - src/providers/twitch/TwitchApi.cpp \ src/providers/twitch/TwitchBadge.cpp \ src/providers/twitch/TwitchBadges.cpp \ src/providers/twitch/TwitchChannel.cpp \ @@ -372,15 +372,15 @@ HEADERS += \ src/providers/irc/IrcConnection2.hpp \ src/providers/irc/IrcServer.hpp \ src/providers/LinkResolver.hpp \ + src/providers/twitch/api/Helix.hpp \ + src/providers/twitch/api/Kraken.hpp \ src/providers/twitch/EmoteValue.hpp \ src/providers/twitch/IrcMessageHandler.hpp \ - src/providers/twitch/PartialTwitchUser.hpp \ src/providers/twitch/PubsubActions.hpp \ src/providers/twitch/PubsubClient.hpp \ src/providers/twitch/PubsubHelpers.hpp \ src/providers/twitch/TwitchAccount.hpp \ src/providers/twitch/TwitchAccountManager.hpp \ - src/providers/twitch/TwitchApi.hpp \ src/providers/twitch/TwitchBadge.hpp \ src/providers/twitch/TwitchBadges.hpp \ src/providers/twitch/TwitchChannel.hpp \ diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 92d89e5c..fa8a08e9 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -8,9 +8,9 @@ #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" #include "messages/MessageElement.hpp" -#include "providers/twitch/TwitchApi.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/api/Helix.hpp" #include "singletons/Emotes.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" @@ -364,18 +364,17 @@ QString CommandController::execCommand(const QString &textNoEmoji, return ""; } - TwitchApi::findUserId( - target, [user, channel, target](QString userId) { - if (userId.isEmpty()) - { - channel->addMessage(makeSystemMessage( - "User " + target + " could not be followed!")); - return; - } - user->followUser(userId, [channel, target]() { + getHelix()->getUserByName( + target, + [user, channel, target](const auto &targetUser) { + user->followUser(targetUser.id, [channel, target]() { channel->addMessage(makeSystemMessage( "You successfully followed " + target)); }); + }, + [channel, target] { + channel->addMessage(makeSystemMessage( + "User " + target + " could not be followed!")); }); return ""; @@ -400,18 +399,17 @@ QString CommandController::execCommand(const QString &textNoEmoji, return ""; } - TwitchApi::findUserId( - target, [user, channel, target](QString userId) { - if (userId.isEmpty()) - { - channel->addMessage(makeSystemMessage( - "User " + target + " could not be followed!")); - return; - } - user->unfollowUser(userId, [channel, target]() { + getHelix()->getUserByName( + target, + [user, channel, target](const auto &targetUser) { + user->unfollowUser(targetUser.id, [channel, target]() { channel->addMessage(makeSystemMessage( "You successfully unfollowed " + target)); }); + }, + [channel, target] { + channel->addMessage(makeSystemMessage( + "User " + target + " could not be followed!")); }); return ""; diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index 72d7b012..2578558d 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -4,8 +4,8 @@ #include "common/NetworkRequest.hpp" #include "common/Outcome.hpp" #include "controllers/notifications/NotificationModel.hpp" -#include "providers/twitch/TwitchApi.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/api/Helix.hpp" #include "singletons/Toasts.hpp" #include "singletons/WindowManager.hpp" #include "widgets/Window.hpp" @@ -142,68 +142,52 @@ void NotificationController::fetchFakeChannels() void NotificationController::getFakeTwitchChannelLiveStatus( const QString &channelName) { - TwitchApi::findUserId(channelName, [channelName, this](QString roomID) { - if (roomID.isEmpty()) - { + getHelix()->getStreamByName( + channelName, + [channelName, this](bool live, const auto &stream) { + qDebug() << "[TwitchChannel" << channelName + << "] Refreshing live status"; + + if (!live) + { + // Stream is offline + this->removeFakeChannel(channelName); + return; + } + + // Stream is online + auto i = std::find(fakeTwitchChannels.begin(), + fakeTwitchChannels.end(), channelName); + + if (i != fakeTwitchChannels.end()) + { + // We have already pushed the live state of this stream + // Could not find stream in fake twitch channels! + return; + } + + if (Toasts::isEnabled()) + { + getApp()->toasts->sendChannelNotification(channelName, + Platform::Twitch); + } + if (getSettings()->notificationPlaySound) + { + getApp()->notifications->playSound(); + } + if (getSettings()->notificationFlashTaskbar) + { + getApp()->windows->sendAlert(); + } + + // Indicate that we have pushed notifications for this stream + fakeTwitchChannels.push_back(channelName); + }, + [channelName, this] { qDebug() << "[TwitchChannel" << channelName << "] Refreshing live status (Missing ID)"; - removeFakeChannel(channelName); - return; - } - qDebug() << "[TwitchChannel" << channelName - << "] Refreshing live status"; - - QString url("https://api.twitch.tv/kraken/streams/" + roomID); - NetworkRequest::twitchRequest(url) - .onSuccess([this, channelName](auto result) -> Outcome { - rapidjson::Document document = result.parseRapidJson(); - if (!document.IsObject()) - { - qDebug() << "[TwitchChannel:refreshLiveStatus] root is not " - "an object"; - return Failure; - } - - if (!document.HasMember("stream")) - { - qDebug() << "[TwitchChannel:refreshLiveStatus] Missing " - "stream in root"; - return Failure; - } - - const auto &stream = document["stream"]; - - if (!stream.IsObject()) - { - // Stream is offline (stream is most likely null) - // removeFakeChannel(channelName); - return Failure; - } - // Stream is live - auto i = std::find(fakeTwitchChannels.begin(), - fakeTwitchChannels.end(), channelName); - - if (!(i != fakeTwitchChannels.end())) - { - fakeTwitchChannels.push_back(channelName); - if (Toasts::isEnabled()) - { - getApp()->toasts->sendChannelNotification( - channelName, Platform::Twitch); - } - if (getSettings()->notificationPlaySound) - { - getApp()->notifications->playSound(); - } - if (getSettings()->notificationFlashTaskbar) - { - getApp()->windows->sendAlert(); - } - } - return Success; - }) - .execute(); - }); + this->removeFakeChannel(channelName); + }); } void NotificationController::removeFakeChannel(const QString channelName) diff --git a/src/controllers/notifications/NotificationController.hpp b/src/controllers/notifications/NotificationController.hpp index a9c048ef..e8d0c4ef 100644 --- a/src/controllers/notifications/NotificationController.hpp +++ b/src/controllers/notifications/NotificationController.hpp @@ -43,6 +43,7 @@ private: void removeFakeChannel(const QString channelName); void getFakeTwitchChannelLiveStatus(const QString &channelName); + // fakeTwitchChannels is a list of streams who are live that we have already sent out a notification for std::vector fakeTwitchChannels; QTimer *liveStatusTimer_; diff --git a/src/main.cpp b/src/main.cpp index 46346589..dfb42904 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,8 @@ #include "common/Args.hpp" #include "common/Modes.hpp" #include "common/Version.hpp" +#include "providers/twitch/api/Helix.hpp" +#include "providers/twitch/api/Kraken.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" #include "util/IncognitoBrowser.hpp" @@ -36,6 +38,9 @@ int main(int argc, char **argv) } else { + Helix::initialize(); + Kraken::initialize(); + Paths *paths{}; try diff --git a/src/providers/twitch/PartialTwitchUser.cpp b/src/providers/twitch/PartialTwitchUser.cpp deleted file mode 100644 index 5407fbad..00000000 --- a/src/providers/twitch/PartialTwitchUser.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "providers/twitch/PartialTwitchUser.hpp" - -#include "common/Common.hpp" -#include "common/NetworkRequest.hpp" -#include "providers/twitch/TwitchCommon.hpp" - -#include -#include - -namespace chatterino { - -PartialTwitchUser PartialTwitchUser::byName(const QString &username) -{ - PartialTwitchUser user; - user.username_ = username; - - return user; -} - -PartialTwitchUser PartialTwitchUser::byId(const QString &id) -{ - PartialTwitchUser user; - user.id_ = id; - - return user; -} - -void PartialTwitchUser::getId(std::function successCallback, - const QObject *caller) -{ - getId( - successCallback, [] {}, caller); -} -void PartialTwitchUser::getId(std::function successCallback, - std::function failureCallback, - const QObject *caller) -{ - assert(!this->username_.isEmpty()); - - NetworkRequest("https://api.twitch.tv/kraken/users?login=" + - this->username_) - .caller(caller) - .authorizeTwitchV5(getDefaultClientID()) - .onSuccess([successCallback, failureCallback](auto result) -> Outcome { - auto root = result.parseJson(); - if (!root.value("users").isArray()) - { - qDebug() - << "API Error while getting user id, users is not an array"; - failureCallback(); - return Failure; - } - - auto users = root.value("users").toArray(); - if (users.size() != 1) - { - qDebug() << "API Error while getting user id, users array size " - "is not 1"; - failureCallback(); - return Failure; - } - if (!users[0].isObject()) - { - qDebug() << "API Error while getting user id, first user is " - "not an object"; - failureCallback(); - return Failure; - } - auto firstUser = users[0].toObject(); - auto id = firstUser.value("_id"); - if (!id.isString()) - { - qDebug() << "API Error: while getting user id, first user " - "object `_id` key is not a string"; - failureCallback(); - return Failure; - } - successCallback(id.toString()); - - return Success; - }) - .execute(); -} - -} // namespace chatterino diff --git a/src/providers/twitch/PartialTwitchUser.hpp b/src/providers/twitch/PartialTwitchUser.hpp deleted file mode 100644 index 6537bb27..00000000 --- a/src/providers/twitch/PartialTwitchUser.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace chatterino { - -// Experimental class to test a method of calling APIs on twitch users -class PartialTwitchUser -{ - PartialTwitchUser() = default; - - QString username_; - QString id_; - -public: - static PartialTwitchUser byName(const QString &username); - static PartialTwitchUser byId(const QString &id); - - void getId(std::function successCallback, - const QObject *caller = nullptr); - - void getId(std::function successCallback, - std::function failureCallback, - const QObject *caller = nullptr); -}; - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchAccount.cpp b/src/providers/twitch/TwitchAccount.cpp index 13b4d409..1b0f2c0f 100644 --- a/src/providers/twitch/TwitchAccount.cpp +++ b/src/providers/twitch/TwitchAccount.cpp @@ -6,8 +6,8 @@ #include "common/Env.hpp" #include "common/NetworkRequest.hpp" #include "common/Outcome.hpp" -#include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchCommon.hpp" +#include "providers/twitch/api/Helix.hpp" #include "singletons/Emotes.hpp" #include "util/RapidjsonHelpers.hpp" @@ -151,12 +151,14 @@ void TwitchAccount::ignore( const QString &targetName, std::function onFinished) { - const auto onIdFetched = [this, targetName, - onFinished](QString targetUserId) { - this->ignoreByID(targetUserId, targetName, onFinished); // + const auto onUserFetched = [this, targetName, + onFinished](const auto &user) { + this->ignoreByID(user.id, targetName, onFinished); // }; - PartialTwitchUser::byName(targetName).getId(onIdFetched); + const auto onUserFetchFailed = [] {}; + + getHelix()->getUserByName(targetName, onUserFetched, onUserFetchFailed); } void TwitchAccount::ignoreByID( @@ -226,12 +228,14 @@ void TwitchAccount::unignore( const QString &targetName, std::function onFinished) { - const auto onIdFetched = [this, targetName, - onFinished](QString targetUserId) { - this->unignoreByID(targetUserId, targetName, onFinished); // + const auto onUserFetched = [this, targetName, + onFinished](const auto &user) { + this->unignoreByID(user.id, targetName, onFinished); // }; - PartialTwitchUser::byName(targetName).getId(onIdFetched); + const auto onUserFetchFailed = [] {}; + + getHelix()->getUserByName(targetName, onUserFetched, onUserFetchFailed); } void TwitchAccount::unignoreByID( @@ -270,28 +274,18 @@ void TwitchAccount::unignoreByID( void TwitchAccount::checkFollow(const QString targetUserID, std::function onFinished) { - QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + - "/follows/channels/" + targetUserID); + const auto onResponse = [onFinished](bool following, const auto &record) { + if (!following) + { + onFinished(FollowResult_NotFollowing); + return; + } - NetworkRequest(url) + onFinished(FollowResult_Following); + }; - .authorizeTwitchV5(this->getOAuthClient(), this->getOAuthToken()) - .onError([=](NetworkResult result) { - if (result.status() == 203) - { - onFinished(FollowResult_NotFollowing); - } - else - { - onFinished(FollowResult_Failed); - } - }) - .onSuccess([=](auto result) -> Outcome { - auto document = result.parseRapidJson(); - onFinished(FollowResult_Following); - return Success; - }) - .execute(); + getHelix()->getUserFollow(this->getUserId(), targetUserID, onResponse, + [] {}); } void TwitchAccount::followUser(const QString userID, diff --git a/src/providers/twitch/TwitchAccountManager.cpp b/src/providers/twitch/TwitchAccountManager.cpp index c3b2ee69..3813e982 100644 --- a/src/providers/twitch/TwitchAccountManager.cpp +++ b/src/providers/twitch/TwitchAccountManager.cpp @@ -3,6 +3,8 @@ #include "common/Common.hpp" #include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchCommon.hpp" +#include "providers/twitch/api/Helix.hpp" +#include "providers/twitch/api/Kraken.hpp" namespace chatterino { @@ -141,6 +143,8 @@ void TwitchAccountManager::load() if (user) { qDebug() << "Twitch user updated to" << newUsername; + getHelix()->update(user->getOAuthClient(), user->getOAuthToken()); + getKraken()->update(user->getOAuthClient(), user->getOAuthToken()); this->currentUser_ = user; } else diff --git a/src/providers/twitch/TwitchApi.cpp b/src/providers/twitch/TwitchApi.cpp deleted file mode 100644 index 6c3b6b6e..00000000 --- a/src/providers/twitch/TwitchApi.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "providers/twitch/TwitchApi.hpp" - -#include "common/Common.hpp" -#include "common/NetworkRequest.hpp" -#include "providers/twitch/TwitchCommon.hpp" - -#include -#include - -namespace chatterino { - -void TwitchApi::findUserId(const QString user, - std::function successCallback) -{ - QString requestUrl("https://api.twitch.tv/kraken/users?login=" + user); - - NetworkRequest(requestUrl) - - .authorizeTwitchV5(getDefaultClientID()) - .timeout(30000) - .onSuccess([successCallback](auto result) mutable -> Outcome { - auto root = result.parseJson(); - if (!root.value("users").isArray()) - { - qDebug() - << "API Error while getting user id, users is not an array"; - successCallback(""); - return Failure; - } - auto users = root.value("users").toArray(); - if (users.size() != 1) - { - qDebug() << "API Error while getting user id, users array size " - "is not 1"; - successCallback(""); - return Failure; - } - if (!users[0].isObject()) - { - qDebug() << "API Error while getting user id, first user is " - "not an object"; - successCallback(""); - return Failure; - } - auto firstUser = users[0].toObject(); - auto id = firstUser.value("_id"); - if (!id.isString()) - { - qDebug() << "API Error: while getting user id, first user " - "object `_id` key is not a string"; - successCallback(""); - return Failure; - } - successCallback(id.toString()); - return Success; - }) - .execute(); -} - -void TwitchApi::findUserName(const QString userid, - std::function successCallback) -{ - QString requestUrl("https://api.twitch.tv/kraken/users/" + userid); - - NetworkRequest(requestUrl) - - .authorizeTwitchV5(getDefaultClientID()) - .timeout(30000) - .onSuccess([successCallback](auto result) mutable -> Outcome { - auto root = result.parseJson(); - auto name = root.value("name"); - if (!name.isString()) - { - qDebug() << "API Error: while getting user name, `name` is not " - "a string"; - successCallback(""); - return Failure; - } - successCallback(name.toString()); - return Success; - }) - .execute(); -} - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchApi.hpp b/src/providers/twitch/TwitchApi.hpp deleted file mode 100644 index 8b1b85c1..00000000 --- a/src/providers/twitch/TwitchApi.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -namespace chatterino { - -class TwitchApi -{ -public: - static void findUserId(const QString user, - std::function callback); - static void findUserName(const QString userid, - std::function callback); - -private: -}; - -} // namespace chatterino diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index d305f0c4..05dac012 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -14,6 +14,8 @@ #include "providers/twitch/TwitchCommon.hpp" #include "providers/twitch/TwitchMessageBuilder.hpp" #include "providers/twitch/TwitchParseCheerEmotes.hpp" +#include "providers/twitch/api/Helix.hpp" +#include "providers/twitch/api/Kraken.hpp" #include "singletons/Emotes.hpp" #include "singletons/Settings.hpp" #include "singletons/Toasts.hpp" @@ -21,6 +23,7 @@ #include "util/PostToThread.hpp" #include "widgets/Window.hpp" +#include #include #include #include @@ -454,35 +457,25 @@ void TwitchChannel::refreshTitle() } this->titleRefreshedTime_ = QTime::currentTime(); - QString url("https://api.twitch.tv/kraken/channels/" + roomID); - NetworkRequest::twitchRequest(url) - .onSuccess( - [this, weak = weakOf(this)](auto result) -> Outcome { - ChannelPtr shared = weak.lock(); - if (!shared) - return Failure; + const auto onSuccess = [this, + weak = weakOf(this)](const auto &channel) { + ChannelPtr shared = weak.lock(); + if (!shared) + { + return; + } - const auto document = result.parseRapidJson(); + { + auto status = this->streamStatus_.access(); + status->title = channel.status; + } - auto statusIt = document.FindMember("status"); + this->liveStatusChanged.invoke(); + }; - if (statusIt == document.MemberEnd()) - { - return Failure; - } + const auto onFailure = [] {}; - { - auto status = this->streamStatus_.access(); - if (!rj::getSafe(statusIt->value, status->title)) - { - return Failure; - } - } - - this->liveStatusChanged.invoke(); - return Success; - }) - .execute(); + getKraken()->getChannel(roomID, onSuccess, onFailure); } void TwitchChannel::refreshLiveStatus() @@ -497,106 +490,73 @@ void TwitchChannel::refreshLiveStatus() return; } - QString url("https://api.twitch.tv/kraken/streams/" + roomID); + getHelix()->getStreamById( + roomID, + [this, weak = weakOf(this)](bool live, const auto &stream) { + ChannelPtr shared = weak.lock(); + if (!shared) + { + return; + } - // auto request = makeGetStreamRequest(roomID, QThread::currentThread()); - NetworkRequest::twitchRequest(url) - - .onSuccess( - [this, weak = weakOf(this)](auto result) -> Outcome { - ChannelPtr shared = weak.lock(); - if (!shared) - return Failure; - - return this->parseLiveStatus(result.parseRapidJson()); - }) - .execute(); + this->parseLiveStatus(live, stream); + }, + [] { + // failure + }); } -Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document) +void TwitchChannel::parseLiveStatus(bool live, const HelixStream &stream) { - if (!document.IsObject()) + if (!live) { - qDebug() << "[TwitchChannel:refreshLiveStatus] root is not an object"; - return Failure; - } - - if (!document.HasMember("stream")) - { - qDebug() << "[TwitchChannel:refreshLiveStatus] Missing stream in root"; - return Failure; - } - - const auto &stream = document["stream"]; - - if (!stream.IsObject()) - { - // Stream is offline (stream is most likely null) this->setLive(false); - return Failure; + return; } - if (!stream.HasMember("viewers") || !stream.HasMember("game") || - !stream.HasMember("channel") || !stream.HasMember("created_at")) - { - qDebug() - << "[TwitchChannel:refreshLiveStatus] Missing members in stream"; - this->setLive(false); - return Failure; - } - - const rapidjson::Value &streamChannel = stream["channel"]; - - if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) - { - qDebug() << "[TwitchChannel:refreshLiveStatus] Missing member " - "\"status\" in channel"; - return Failure; - } - - // Stream is live - { auto status = this->streamStatus_.access(); - status->viewerCount = stream["viewers"].GetUint(); - status->game = stream["game"].GetString(); - status->title = streamChannel["status"].GetString(); - QDateTime since = QDateTime::fromString( - stream["created_at"].GetString(), Qt::ISODate); + status->viewerCount = stream.viewerCount; + if (status->gameId != stream.gameId) + { + status->gameId = stream.gameId; + + // Resolve game ID to game name + getHelix()->getGameById( + stream.gameId, + [this, weak = weakOf(this)](const auto &game) { + ChannelPtr shared = weak.lock(); + if (!shared) + { + return; + } + + { + auto status = this->streamStatus_.access(); + status->game = game.name; + } + + this->liveStatusChanged.invoke(); + }, + [] { + // failure + }); + } + status->game = stream.gameId; + status->title = stream.title; + QDateTime since = QDateTime::fromString(stream.startedAt, Qt::ISODate); auto diff = since.secsTo(QDateTime::currentDateTime()); status->uptime = QString::number(diff / 3600) + "h " + QString::number(diff % 3600 / 60) + "m"; status->rerun = false; - if (stream.HasMember("stream_type")) - { - status->streamType = stream["stream_type"].GetString(); - } - else - { - status->streamType = QString(); - } - - if (stream.HasMember("broadcast_platform")) - { - const auto &broadcastPlatformValue = stream["broadcast_platform"]; - - if (broadcastPlatformValue.IsString()) - { - const char *broadcastPlatform = - stream["broadcast_platform"].GetString(); - if (strcmp(broadcastPlatform, "rerun") == 0) - { - status->rerun = true; - } - } - } + status->streamType = stream.type; } - setLive(true); + + this->setLive(true); + // Signal all listeners that the stream status has been updated this->liveStatusChanged.invoke(); - - return Success; } void TwitchChannel::loadRecentMessages() diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 026c9071..fc40ad32 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -8,14 +8,15 @@ #include "common/UniqueAccess.hpp" #include "common/UsernameSet.hpp" #include "providers/twitch/TwitchEmotes.hpp" +#include "providers/twitch/api/Helix.hpp" -#include #include #include #include #include -#include #include + +#include #include namespace chatterino { @@ -43,6 +44,7 @@ public: unsigned viewerCount = 0; QString title; QString game; + QString gameId; QString uptime; QString streamType; }; @@ -120,7 +122,7 @@ protected: private: // Methods void refreshLiveStatus(); - Outcome parseLiveStatus(const rapidjson::Document &document); + void parseLiveStatus(bool live, const HelixStream &stream); void refreshPubsub(); void refreshChatters(); void refreshBadges(); diff --git a/src/providers/twitch/TwitchParseCheerEmotes.cpp b/src/providers/twitch/TwitchParseCheerEmotes.cpp index 5258baf9..9667ffd2 100644 --- a/src/providers/twitch/TwitchParseCheerEmotes.cpp +++ b/src/providers/twitch/TwitchParseCheerEmotes.cpp @@ -278,6 +278,7 @@ namespace { return true; } + } // namespace // Look through the results of diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp new file mode 100644 index 00000000..974b6212 --- /dev/null +++ b/src/providers/twitch/api/Helix.cpp @@ -0,0 +1,368 @@ +#include "providers/twitch/api/Helix.hpp" + +#include "common/Outcome.hpp" + +namespace chatterino { + +static Helix *instance = nullptr; + +void Helix::fetchUsers(QStringList userIds, QStringList userLogins, + ResultCallback> successCallback, + HelixFailureCallback failureCallback) +{ + QUrlQuery urlQuery; + + for (const auto &id : userIds) + { + urlQuery.addQueryItem("id", id); + } + + for (const auto &login : userLogins) + { + urlQuery.addQueryItem("login", login); + } + + // TODO: set on success and on error + this->makeRequest("users", urlQuery) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + auto data = root.value("data"); + + if (!data.isArray()) + { + failureCallback(); + return Failure; + } + + std::vector users; + + for (const auto &jsonUser : data.toArray()) + { + users.emplace_back(jsonUser.toObject()); + } + + successCallback(users); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Helix::getUserByName(QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList userIds; + QStringList userLogins{userId}; + + this->fetchUsers( + userIds, userLogins, + [successCallback, + failureCallback](const std::vector &users) { + if (users.empty()) + { + failureCallback(); + return; + } + successCallback(users[0]); + }, + failureCallback); +} + +void Helix::getUserById(QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList userIds{userId}; + QStringList userLogins; + + this->fetchUsers( + userIds, userLogins, + [successCallback, failureCallback](const auto &users) { + if (users.empty()) + { + failureCallback(); + return; + } + successCallback(users[0]); + }, + failureCallback); +} + +void Helix::fetchUsersFollows( + QString fromId, QString toId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + assert(!fromId.isEmpty() || !toId.isEmpty()); + + QUrlQuery urlQuery; + + if (!fromId.isEmpty()) + { + urlQuery.addQueryItem("from_id", fromId); + } + + if (!toId.isEmpty()) + { + urlQuery.addQueryItem("to_id", toId); + } + + // TODO: set on success and on error + this->makeRequest("users/follows", urlQuery) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + if (root.empty()) + { + failureCallback(); + return Failure; + } + successCallback(HelixUsersFollowsResponse(root)); + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Helix::getUserFollowers( + QString userId, ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + this->fetchUsersFollows("", userId, successCallback, failureCallback); +} + +void Helix::getUserFollow( + QString userId, QString targetId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + this->fetchUsersFollows( + userId, targetId, + [successCallback](const auto &response) { + if (response.data.empty()) + { + successCallback(false, HelixUsersFollowsRecord()); + return; + } + + successCallback(true, response.data[0]); + }, + failureCallback); +} + +void Helix::fetchStreams( + QStringList userIds, QStringList userLogins, + ResultCallback> successCallback, + HelixFailureCallback failureCallback) +{ + QUrlQuery urlQuery; + + for (const auto &id : userIds) + { + urlQuery.addQueryItem("user_id", id); + } + + for (const auto &login : userLogins) + { + urlQuery.addQueryItem("user_login", login); + } + + // TODO: set on success and on error + this->makeRequest("streams", urlQuery) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + auto data = root.value("data"); + + if (!data.isArray()) + { + failureCallback(); + return Failure; + } + + std::vector streams; + + for (const auto &jsonStream : data.toArray()) + { + streams.emplace_back(jsonStream.toObject()); + } + + successCallback(streams); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Helix::getStreamById(QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList userIds{userId}; + QStringList userLogins; + + this->fetchStreams( + userIds, userLogins, + [successCallback, failureCallback](const auto &streams) { + if (streams.empty()) + { + successCallback(false, HelixStream()); + return; + } + successCallback(true, streams[0]); + }, + failureCallback); +} + +void Helix::getStreamByName(QString userName, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList userIds; + QStringList userLogins{userName}; + + this->fetchStreams( + userIds, userLogins, + [successCallback, failureCallback](const auto &streams) { + if (streams.empty()) + { + successCallback(false, HelixStream()); + return; + } + successCallback(true, streams[0]); + }, + failureCallback); +} + +/// + +void Helix::fetchGames(QStringList gameIds, QStringList gameNames, + ResultCallback> successCallback, + HelixFailureCallback failureCallback) +{ + assert((gameIds.length() + gameNames.length()) > 0); + + QUrlQuery urlQuery; + + for (const auto &id : gameIds) + { + urlQuery.addQueryItem("id", id); + } + + for (const auto &login : gameNames) + { + urlQuery.addQueryItem("name", login); + } + + // TODO: set on success and on error + this->makeRequest("games", urlQuery) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + auto data = root.value("data"); + + if (!data.isArray()) + { + failureCallback(); + return Failure; + } + + std::vector games; + + for (const auto &jsonStream : data.toArray()) + { + games.emplace_back(jsonStream.toObject()); + } + + successCallback(games); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Helix::getGameById(QString gameId, + ResultCallback successCallback, + HelixFailureCallback failureCallback) +{ + QStringList gameIds{gameId}; + QStringList gameNames; + + this->fetchGames( + gameIds, gameNames, + [successCallback, failureCallback](const auto &games) { + if (games.empty()) + { + failureCallback(); + return; + } + successCallback(games[0]); + }, + failureCallback); +} + +NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery) +{ + assert(!url.startsWith("/")); + + if (this->clientId.isEmpty()) + { + qDebug() + << "Helix::makeRequest called without a client ID set BabyRage"; + // return boost::none; + } + + if (this->oauthToken.isEmpty()) + { + qDebug() + << "Helix::makeRequest called without an oauth token set BabyRage"; + // return boost::none; + } + + const QString baseUrl("https://api.twitch.tv/helix/"); + + QUrl fullUrl(baseUrl + url); + + fullUrl.setQuery(urlQuery); + + return NetworkRequest(fullUrl) + .timeout(5 * 1000) + .header("Accept", "application/json") + .header("Client-ID", this->clientId) + .header("Authorization", "Bearer " + this->oauthToken); +} + +void Helix::update(QString clientId, QString oauthToken) +{ + this->clientId = clientId; + this->oauthToken = oauthToken; +} + +void Helix::initialize() +{ + assert(instance == nullptr); + + instance = new Helix(); +} + +Helix *getHelix() +{ + assert(instance != nullptr); + + return instance; +} + +} // namespace chatterino diff --git a/src/providers/twitch/api/Helix.hpp b/src/providers/twitch/api/Helix.hpp new file mode 100644 index 00000000..bfe54b69 --- /dev/null +++ b/src/providers/twitch/api/Helix.hpp @@ -0,0 +1,198 @@ +#pragma once + +#include "common/NetworkRequest.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace chatterino { + +using HelixFailureCallback = std::function; +template +using ResultCallback = std::function; + +struct HelixUser { + QString id; + QString login; + QString displayName; + QString description; + QString profileImageUrl; + int viewCount; + + explicit HelixUser(QJsonObject jsonObject) + : id(jsonObject.value("id").toString()) + , login(jsonObject.value("login").toString()) + , displayName(jsonObject.value("display_name").toString()) + , description(jsonObject.value("description").toString()) + , profileImageUrl(jsonObject.value("profile_image_url").toString()) + , viewCount(jsonObject.value("view_count").toInt()) + { + } +}; + +struct HelixUsersFollowsRecord { + QString fromId; + QString fromName; + QString toId; + QString toName; + QString followedAt; // date time object + + HelixUsersFollowsRecord() + : fromId("") + , fromName("") + , toId("") + , toName("") + , followedAt("") + { + } + + explicit HelixUsersFollowsRecord(QJsonObject jsonObject) + : fromId(jsonObject.value("from_id").toString()) + , fromName(jsonObject.value("from_name").toString()) + , toId(jsonObject.value("to_id").toString()) + , toName(jsonObject.value("to_name").toString()) + , followedAt(jsonObject.value("followed_at").toString()) + { + } +}; + +struct HelixUsersFollowsResponse { + int total; + std::vector data; + explicit HelixUsersFollowsResponse(QJsonObject jsonObject) + : total(jsonObject.value("total").toInt()) + { + const auto &jsonData = jsonObject.value("data").toArray(); + std::transform(jsonData.begin(), jsonData.end(), + std::back_inserter(this->data), + [](const QJsonValue &record) { + return HelixUsersFollowsRecord(record.toObject()); + }); + } +}; + +struct HelixStream { + QString id; // stream id + QString userId; + QString userName; + QString gameId; + QString type; + QString title; + int viewerCount; + QString startedAt; + QString language; + QString thumbnailUrl; + + HelixStream() + : id("") + , userId("") + , userName("") + , gameId("") + , type("") + , title("") + , viewerCount() + , startedAt("") + , language("") + , thumbnailUrl("") + { + } + + explicit HelixStream(QJsonObject jsonObject) + : id(jsonObject.value("id").toString()) + , userId(jsonObject.value("user_id").toString()) + , userName(jsonObject.value("user_name").toString()) + , gameId(jsonObject.value("game_id").toString()) + , type(jsonObject.value("type").toString()) + , title(jsonObject.value("title").toString()) + , viewerCount(jsonObject.value("viewer_count").toInt()) + , startedAt(jsonObject.value("started_at").toString()) + , language(jsonObject.value("language").toString()) + , thumbnailUrl(jsonObject.value("thumbnail_url").toString()) + { + } +}; + +struct HelixGame { + QString id; // stream id + QString name; + QString boxArtUrl; + + explicit HelixGame(QJsonObject jsonObject) + : id(jsonObject.value("id").toString()) + , name(jsonObject.value("name").toString()) + , boxArtUrl(jsonObject.value("box_art_url").toString()) + { + } +}; + +class Helix final : boost::noncopyable +{ +public: + // https://dev.twitch.tv/docs/api/reference#get-users + void fetchUsers(QStringList userIds, QStringList userLogins, + ResultCallback> successCallback, + HelixFailureCallback failureCallback); + void getUserByName(QString userName, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + void getUserById(QString userId, ResultCallback successCallback, + HelixFailureCallback failureCallback); + + // https://dev.twitch.tv/docs/api/reference#get-users-follows + void fetchUsersFollows( + QString fromId, QString toId, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + void getUserFollowers( + QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + void getUserFollow( + QString userId, QString targetId, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + // https://dev.twitch.tv/docs/api/reference#get-streams + void fetchStreams(QStringList userIds, QStringList userLogins, + ResultCallback> successCallback, + HelixFailureCallback failureCallback); + + void getStreamById(QString userId, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + void getStreamByName(QString userName, + ResultCallback successCallback, + HelixFailureCallback failureCallback); + + // https://dev.twitch.tv/docs/api/reference#get-games + void fetchGames(QStringList gameIds, QStringList gameNames, + ResultCallback> successCallback, + HelixFailureCallback failureCallback); + + void getGameById(QString gameId, ResultCallback successCallback, + HelixFailureCallback failureCallback); + + void update(QString clientId, QString oauthToken); + + static void initialize(); + +private: + NetworkRequest makeRequest(QString url, QUrlQuery urlQuery); + + QString clientId; + QString oauthToken; +}; + +Helix *getHelix(); + +} // namespace chatterino diff --git a/src/providers/twitch/api/Kraken.cpp b/src/providers/twitch/api/Kraken.cpp new file mode 100644 index 00000000..be7e98b3 --- /dev/null +++ b/src/providers/twitch/api/Kraken.cpp @@ -0,0 +1,104 @@ +#include "providers/twitch/api/Kraken.hpp" + +#include "common/Outcome.hpp" +#include "providers/twitch/TwitchCommon.hpp" + +namespace chatterino { + +static Kraken *instance = nullptr; + +void Kraken::getChannel(QString userId, + ResultCallback successCallback, + KrakenFailureCallback failureCallback) +{ + assert(!userId.isEmpty()); + + this->makeRequest("channels/" + userId, {}) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + + successCallback(root); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +void Kraken::getUser(QString userId, ResultCallback successCallback, + KrakenFailureCallback failureCallback) +{ + assert(!userId.isEmpty()); + + this->makeRequest("users/" + userId, {}) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + + successCallback(root); + + return Success; + }) + .onError([failureCallback](auto result) { + // TODO: make better xd + failureCallback(); + }) + .execute(); +} + +NetworkRequest Kraken::makeRequest(QString url, QUrlQuery urlQuery) +{ + assert(!url.startsWith("/")); + + if (this->clientId.isEmpty()) + { + qDebug() + << "Kraken::makeRequest called without a client ID set BabyRage"; + } + + const QString baseUrl("https://api.twitch.tv/kraken/"); + + QUrl fullUrl(baseUrl + url); + + fullUrl.setQuery(urlQuery); + + if (!this->oauthToken.isEmpty()) + { + return NetworkRequest(fullUrl) + .timeout(5 * 1000) + .header("Accept", "application/vnd.twitchtv.v5+json") + .header("Client-ID", this->clientId) + .header("Authorization", "OAuth " + this->oauthToken); + } + + return NetworkRequest(fullUrl) + .timeout(5 * 1000) + .header("Accept", "application/vnd.twitchtv.v5+json") + .header("Client-ID", this->clientId); +} + +void Kraken::update(QString clientId, QString oauthToken) +{ + this->clientId = clientId; + this->oauthToken = oauthToken; +} + +void Kraken::initialize() +{ + assert(instance == nullptr); + + instance = new Kraken(); + + getKraken()->update(getDefaultClientID(), ""); +} + +Kraken *getKraken() +{ + assert(instance != nullptr); + + return instance; +} + +} // namespace chatterino diff --git a/src/providers/twitch/api/Kraken.hpp b/src/providers/twitch/api/Kraken.hpp new file mode 100644 index 00000000..047173d6 --- /dev/null +++ b/src/providers/twitch/api/Kraken.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "common/NetworkRequest.hpp" + +#include +#include +#include +#include + +#include + +namespace chatterino { + +using KrakenFailureCallback = std::function; +template +using ResultCallback = std::function; + +struct KrakenChannel { + const QString status; + + KrakenChannel(QJsonObject jsonObject) + : status(jsonObject.value("status").toString()) + { + } +}; + +struct KrakenUser { + const QString createdAt; + + KrakenUser(QJsonObject jsonObject) + : createdAt(jsonObject.value("created_at").toString()) + { + } +}; + +class Kraken final : boost::noncopyable +{ +public: + // https://dev.twitch.tv/docs/v5/reference/users#follow-channel + void getChannel(QString userId, + ResultCallback resultCallback, + KrakenFailureCallback failureCallback); + + // https://dev.twitch.tv/docs/v5/reference/users#get-user-by-id + void getUser(QString userId, ResultCallback resultCallback, + KrakenFailureCallback failureCallback); + + void update(QString clientId, QString oauthToken); + + static void initialize(); + +private: + NetworkRequest makeRequest(QString url, QUrlQuery urlQuery); + + QString clientId; + QString oauthToken; +}; + +Kraken *getKraken(); + +} // namespace chatterino diff --git a/src/providers/twitch/api/README.md b/src/providers/twitch/api/README.md new file mode 100644 index 00000000..a95ac6bc --- /dev/null +++ b/src/providers/twitch/api/README.md @@ -0,0 +1,125 @@ +# Twitch API +this folder describes what sort of API requests we do, what permissions are required for the requests etc + +## Kraken (V5) +We use a bunch of Kraken (V5) in Chatterino2. + +### Get User +URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-by-id + +Migration path: **Unknown** + + * We implement this in `providers/twitch/api/Kraken.cpp getUser` + Used in: + * `UserInfoPopup` to get the "created at" date of a user + +### Get Channel +URL: https://dev.twitch.tv/docs/v5/reference/channels#get-channel + +Migration path: **Unknown** + + * We implement this in `providers/twitch/api/Kraken.cpp getChannel` + Used in: + * `TwitchChannel::refreshTitle` to check the current stream title/game of offline channels + +### Follow Channel +URL: https://dev.twitch.tv/docs/v5/reference/users#follow-channel +Requires `user_follows_edit` scope + +Migration path: **Unknown** + + * We implement this API in `providers/twitch/TwitchAccount.cpp followUser` + +### Unfollow Channel +URL: https://dev.twitch.tv/docs/v5/reference/users#unfollow-channel +Requires `user_follows_edit` scope + +Migration path: **Unknown** + + * We implement this API in `providers/twitch/TwitchAccount.cpp unfollowUser` + + +### Get Cheermotes +URL: https://dev.twitch.tv/docs/v5/reference/bits#get-cheermotes + +Migration path: **Not checked** + + * We implement this API in `providers/twitch/TwitchChannel.cpp` to resolve a chats available cheer emotes. This helps us parse incoming messages like `pajaCheer1000` + +### Get User Block List +URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-block-list + +Migration path: **Unknown** + + * We use this in `providers/twitch/TwitchAccount.cpp loadIgnores` + +### Block User +URL: https://dev.twitch.tv/docs/v5/reference/users#block-user +Requires `user_blocks_edit` scope + +Migration path: **Unknown** + + * We use this in `providers/twitch/TwitchAccount.cpp ignoreByID` + +### Unblock User +URL: https://dev.twitch.tv/docs/v5/reference/users#unblock-user +Requires `user_blocks_edit` scope + +Migration path: **Unknown** + + * We use this in `providers/twitch/TwitchAccount.cpp unignoreByID` + +### Get User Emotes +URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-emotes +Requires `user_subscriptions` scope + +Migration path: **Unknown** + + * We use this in `providers/twitch/TwitchAccount.cpp loadEmotes` to figure out which emotes a user is allowed to use! + +### AUTOMOD APPROVE +**Unofficial** documentation: https://discuss.dev.twitch.tv/t/allowing-others-aka-bots-to-use-twitchbot-reject/8508/2 + + * We use this in `providers/twitch/TwitchAccount.cpp autoModAllow` to approve an automod deny/allow question + +### AUTOMOD DENY +**Unofficial** documentation: https://discuss.dev.twitch.tv/t/allowing-others-aka-bots-to-use-twitchbot-reject/8508/2 + + * We use this in `providers/twitch/TwitchAccount.cpp autoModDeny` to deny an automod deny/allow question + +## Helix +Full Helix API reference: https://dev.twitch.tv/docs/api/reference + +### Get Users +URL: https://dev.twitch.tv/docs/api/reference#get-users + + * We implement this in `providers/twitch/api/Helix.cpp fetchUsers`. + Used in: + * `UserInfoPopup` to get ID and viewcount of username we clicked + * `CommandController` to power any commands that need to get a user ID + * `Toasts` to get the profile picture of a streamer who just went live + * `TwitchAccount` ignore and unignore features to translate user name to user ID + +### Get Users Follows +URL: https://dev.twitch.tv/docs/api/reference#get-users-follows + + * We implement this in `providers/twitch/api/Helix.cpp fetchUsersFollows` + Used in: + * `UserInfoPopup` to get number of followers a user has + +### Get Streams +URL: https://dev.twitch.tv/docs/api/reference#get-streams + + * We implement this in `providers/twitch/api/Helix.cpp fetchStreams` + Used in: + * `TwitchChannel` to get live status, game, title, and viewer count of a channel + * `NotificationController` to provide notifications for channels you might not have open in Chatterino, but are still interested in getting notifications for + +## TMI +The TMI api is undocumented. + +### Get Chatters +**Undocumented** + + * We use this in `widgets/splits/Split.cpp showViewerList` + * We use this in `providers/twitch/TwitchChannel.cpp refreshChatters` diff --git a/src/singletons/Toasts.cpp b/src/singletons/Toasts.cpp index def5df00..9316a879 100644 --- a/src/singletons/Toasts.cpp +++ b/src/singletons/Toasts.cpp @@ -7,6 +7,7 @@ #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchCommon.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "providers/twitch/api/Helix.hpp" #include "singletons/Paths.hpp" #include "util/StreamLink.hpp" #include "widgets/helper/CommonTexts.hpp" @@ -85,14 +86,17 @@ void Toasts::sendChannelNotification(const QString &channelName, Platform p) } else { - this->fetchChannelAvatar( + getHelix()->getUserByName( channelName, - [channelName, sendChannelNotification](QString avatarLink) { + [channelName, sendChannelNotification](const auto &user) { DownloadManager *manager = new DownloadManager(); - manager->setFile(avatarLink, channelName); + manager->setFile(user.profileImageUrl, channelName); manager->connect(manager, &DownloadManager::downloadComplete, sendChannelNotification); + }, + [] { + // on failure }); } } @@ -206,53 +210,4 @@ void Toasts::sendWindowsNotification(const QString &channelName, Platform p) #endif -void Toasts::fetchChannelAvatar(const QString channelName, - std::function successCallback) -{ - QString requestUrl("https://api.twitch.tv/kraken/users?login=" + - channelName); - - NetworkRequest(requestUrl) - - .authorizeTwitchV5(getDefaultClientID()) - .timeout(30000) - .onSuccess([successCallback](auto result) mutable -> Outcome { - auto root = result.parseJson(); - if (!root.value("users").isArray()) - { - // log("API Error while getting user id, users is not an array"); - successCallback(""); - return Failure; - } - auto users = root.value("users").toArray(); - if (users.size() != 1) - { - // log("API Error while getting user id, users array size is not - // 1"); - successCallback(""); - return Failure; - } - if (!users[0].isObject()) - { - // log("API Error while getting user id, first user is not an - // object"); - successCallback(""); - return Failure; - } - auto firstUser = users[0].toObject(); - auto avatar = firstUser.value("logo"); - if (!avatar.isString()) - { - // log("API Error: while getting user avatar, first user object " - // "`avatar` key " - // "is not a " - // "string"); - successCallback(""); - return Failure; - } - successCallback(avatar.toString()); - return Success; - }) - .execute(); -} } // namespace chatterino diff --git a/src/singletons/Toasts.hpp b/src/singletons/Toasts.hpp index 43c6d1da..5ef792c7 100644 --- a/src/singletons/Toasts.hpp +++ b/src/singletons/Toasts.hpp @@ -29,9 +29,5 @@ private: #ifdef Q_OS_WIN void sendWindowsNotification(const QString &channelName, Platform p); #endif - - static void fetchChannelAvatar( - const QString channelName, - std::function successCallback); }; } // namespace chatterino diff --git a/src/widgets/dialogs/LoginDialog.cpp b/src/widgets/dialogs/LoginDialog.cpp index f96c8634..3666ea65 100644 --- a/src/widgets/dialogs/LoginDialog.cpp +++ b/src/widgets/dialogs/LoginDialog.cpp @@ -4,7 +4,6 @@ #include "common/Common.hpp" #include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" -#include "providers/twitch/PartialTwitchUser.hpp" #include "util/Helpers.hpp" #ifdef USEWINSDK @@ -159,7 +158,6 @@ AdvancedLoginWidget::AdvancedLoginWidget() this->ui_.layout.addWidget(&this->ui_.instructionsLabel); this->ui_.layout.addLayout(&this->ui_.formLayout); this->ui_.layout.addLayout(&this->ui_.buttonUpperRow.layout); - this->ui_.layout.addLayout(&this->ui_.buttonLowerRow.layout); this->refreshButtons(); @@ -207,29 +205,10 @@ AdvancedLoginWidget::AdvancedLoginWidget() LogInWithCredentials(userID, username, clientID, oauthToken); }); - - /// Lower button row - this->ui_.buttonLowerRow.fillInUserIDButton.setText( - "Get user ID from username"); - - this->ui_.buttonLowerRow.layout.addWidget( - &this->ui_.buttonLowerRow.fillInUserIDButton); - - connect(&this->ui_.buttonLowerRow.fillInUserIDButton, &QPushButton::clicked, - [=]() { - const auto onIdFetched = [=](const QString &userID) { - this->ui_.userIDInput.setText(userID); // - }; - PartialTwitchUser::byName(this->ui_.usernameInput.text()) - .getId(onIdFetched, this); - }); } void AdvancedLoginWidget::refreshButtons() { - this->ui_.buttonLowerRow.fillInUserIDButton.setEnabled( - !this->ui_.usernameInput.text().isEmpty()); - if (this->ui_.userIDInput.text().isEmpty() || this->ui_.usernameInput.text().isEmpty() || this->ui_.clientIDInput.text().isEmpty() || diff --git a/src/widgets/dialogs/LoginDialog.hpp b/src/widgets/dialogs/LoginDialog.hpp index a6819e3a..796b98ca 100644 --- a/src/widgets/dialogs/LoginDialog.hpp +++ b/src/widgets/dialogs/LoginDialog.hpp @@ -57,12 +57,6 @@ public: QPushButton addUserButton; QPushButton clearFieldsButton; } buttonUpperRow; - - struct { - QHBoxLayout layout; - - QPushButton fillInUserIDButton; - } buttonLowerRow; } ui_; }; diff --git a/src/widgets/dialogs/LogsPopup.cpp b/src/widgets/dialogs/LogsPopup.cpp index 18e1f1a0..0afd6b9b 100644 --- a/src/widgets/dialogs/LogsPopup.cpp +++ b/src/widgets/dialogs/LogsPopup.cpp @@ -4,7 +4,6 @@ #include "common/Channel.hpp" #include "common/NetworkRequest.hpp" #include "messages/Message.hpp" -#include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchMessageBuilder.hpp" #include "util/PostToThread.hpp" diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index 3b830702..a4d0f222 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -5,8 +5,9 @@ #include "common/NetworkRequest.hpp" #include "controllers/accounts/AccountController.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" -#include "providers/twitch/PartialTwitchUser.hpp" #include "providers/twitch/TwitchChannel.hpp" +#include "providers/twitch/api/Helix.hpp" +#include "providers/twitch/api/Kraken.hpp" #include "singletons/Resources.hpp" #include "singletons/Settings.hpp" #include "util/LayoutCreator.hpp" @@ -22,9 +23,9 @@ #include #include -#define TEXT_FOLLOWERS "Followers: " -#define TEXT_VIEWS "Views: " -#define TEXT_CREATED "Created: " +const QString TEXT_VIEWS("Views: %1"); +const QString TEXT_FOLLOWERS("Followers: %1"); +const QString TEXT_CREATED("Created: %1"); #define TEXT_USER_ID "ID: " #define TEXT_UNAVAILABLE "(not available)" @@ -92,10 +93,11 @@ UserInfoPopup::UserInfoPopup() this->ui_.userIDLabel->setPalette(palette); } - vbox.emplace