diff --git a/CHANGELOG.md b/CHANGELOG.md index be24b4f9..7085d62d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Minor: Add an icon showing when streamer mode is enabled (#4410) - Minor: Added `/shoutout ` commands to shoutout specified user. (#4638) - Dev: Added command to set Qt's logging filter/rules at runtime (`/c2-set-logging-rules`). (#4637) - Dev: Added the ability to see & load custom themes from the Themes directory. No stable promises are made of this feature, changes might be made that breaks custom themes without notice. (#4570) diff --git a/resources/buttons/streamerModeEnabledDark.png b/resources/buttons/streamerModeEnabledDark.png new file mode 100644 index 00000000..7ab2b1d8 Binary files /dev/null and b/resources/buttons/streamerModeEnabledDark.png differ diff --git a/resources/buttons/streamerModeEnabledDark.svg b/resources/buttons/streamerModeEnabledDark.svg new file mode 100644 index 00000000..7f95aae5 --- /dev/null +++ b/resources/buttons/streamerModeEnabledDark.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/resources/buttons/streamerModeEnabledLight.png b/resources/buttons/streamerModeEnabledLight.png new file mode 100644 index 00000000..8da207f7 Binary files /dev/null and b/resources/buttons/streamerModeEnabledLight.png differ diff --git a/resources/buttons/streamerModeEnabledLight.svg b/resources/buttons/streamerModeEnabledLight.svg new file mode 100644 index 00000000..5f27d439 --- /dev/null +++ b/resources/buttons/streamerModeEnabledLight.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/Application.hpp b/src/Application.hpp index 27f1c2f6..6917190b 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -3,6 +3,8 @@ #include "common/Singleton.hpp" #include "singletons/NativeMessaging.hpp" +#include +#include #include #include @@ -153,6 +155,8 @@ public: } IUserDataController *getUserData() override; + pajlada::Signals::NoArgSignal streamerModeChanged; + private: void addSingleton(Singleton *singleton); void initPubSub(); diff --git a/src/singletons/Settings.cpp b/src/singletons/Settings.cpp index b32623c4..07e10142 100644 --- a/src/singletons/Settings.cpp +++ b/src/singletons/Settings.cpp @@ -1,5 +1,6 @@ #include "singletons/Settings.hpp" +#include "Application.hpp" #include "controllers/filters/FilterRecord.hpp" #include "controllers/highlights/HighlightBadge.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" @@ -136,6 +137,11 @@ Settings::Settings(const QString &settingsDirectory) }, false); #endif + this->enableStreamerMode.connect( + []() { + getApp()->streamerModeChanged.invoke(); + }, + false); } Settings &Settings::instance() diff --git a/src/util/StreamerMode.cpp b/src/util/StreamerMode.cpp index 03f3f59a..c905b88a 100644 --- a/src/util/StreamerMode.cpp +++ b/src/util/StreamerMode.cpp @@ -71,6 +71,7 @@ bool isInStreamerMode() p.exitStatus() == QProcess::NormalExit) { cache = (p.exitCode() == 0); + getApp()->streamerModeChanged.invoke(); return (p.exitCode() == 0); } @@ -89,6 +90,7 @@ bool isInStreamerMode() qCWarning(chatterinoStreamerMode) << "pgrep execution timed out!"; cache = false; + getApp()->streamerModeChanged.invoke(); return false; #endif @@ -122,6 +124,7 @@ bool isInStreamerMode() if (broadcastingBinaries().contains(processName)) { cache = true; + getApp()->streamerModeChanged.invoke(); return true; } } @@ -133,6 +136,7 @@ bool isInStreamerMode() } cache = false; + getApp()->streamerModeChanged.invoke(); #endif return false; } diff --git a/src/widgets/Notebook.cpp b/src/widgets/Notebook.cpp index 3f9a9c67..40a9237a 100644 --- a/src/widgets/Notebook.cpp +++ b/src/widgets/Notebook.cpp @@ -4,10 +4,12 @@ #include "common/QLogging.hpp" #include "controllers/hotkeys/HotkeyCategory.hpp" #include "controllers/hotkeys/HotkeyController.hpp" +#include "singletons/Resources.hpp" #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "singletons/WindowManager.hpp" #include "util/InitUpdateButton.hpp" +#include "util/StreamerMode.hpp" #include "widgets/dialogs/SettingsDialog.hpp" #include "widgets/helper/ChannelView.hpp" #include "widgets/helper/NotebookButton.hpp" @@ -1155,6 +1157,45 @@ void SplitNotebook::addCustomButtons() auto updateBtn = this->addCustomButton(); initUpdateButton(*updateBtn, this->signalHolder_); + + // streamer mode + this->streamerModeIcon_ = this->addCustomButton(); + QObject::connect(this->streamerModeIcon_, &NotebookButton::leftClicked, + [this] { + getApp()->windows->showSettingsDialog( + this, SettingsDialogPreference::StreamerMode); + }); + this->signalHolder_.managedConnect(getApp()->streamerModeChanged, [this]() { + this->updateStreamerModeIcon(); + }); + this->updateStreamerModeIcon(); +} + +void SplitNotebook::updateStreamerModeIcon() +{ + if (this->streamerModeIcon_ == nullptr) + { + return; + } + // A duplicate of this code is in Window class + // That copy handles the TitleBar icon in Window (main window on Windows) + // This one is the one near splits (on linux and mac or non-main windows on Windows) + if (getTheme()->isLightTheme()) + { + this->streamerModeIcon_->setPixmap( + getResources().buttons.streamerModeEnabledLight); + } + else + { + this->streamerModeIcon_->setPixmap( + getResources().buttons.streamerModeEnabledDark); + } + this->streamerModeIcon_->setVisible(isInStreamerMode()); +} + +void SplitNotebook::themeChangedEvent() +{ + this->updateStreamerModeIcon(); } SplitContainer *SplitNotebook::addPage(bool select) diff --git a/src/widgets/Notebook.hpp b/src/widgets/Notebook.hpp index 7cc49cfa..382b260e 100644 --- a/src/widgets/Notebook.hpp +++ b/src/widgets/Notebook.hpp @@ -123,6 +123,7 @@ public: SplitContainer *addPage(bool select = false); SplitContainer *getOrAddSelectedPage(); void select(QWidget *page, bool focusPage = true) override; + void themeChangedEvent() override; protected: void showEvent(QShowEvent *event) override; @@ -131,6 +132,11 @@ private: void addCustomButtons(); pajlada::Signals::SignalHolder signalHolder_; + + // Main window on Windows has basically a duplicate of this in Window + NotebookButton *streamerModeIcon_{}; + + void updateStreamerModeIcon(); }; } // namespace chatterino diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index dfac8ba6..12f60583 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -9,6 +9,7 @@ #include "controllers/hotkeys/HotkeyController.hpp" #include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchIrcServer.hpp" +#include "singletons/Resources.hpp" #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "singletons/Updates.hpp" @@ -186,6 +187,51 @@ void Window::addCustomTitlebarButtons() this->userLabel_->rect().bottomLeft())); }); this->userLabel_->setMinimumWidth(20 * scale()); + + // streamer mode + this->streamerModeTitlebarIcon_ = + this->addTitleBarButton(TitleBarButtonStyle::StreamerMode, [this] { + getApp()->windows->showSettingsDialog( + this, SettingsDialogPreference::StreamerMode); + }); + this->signalHolder_.managedConnect(getApp()->streamerModeChanged, [this]() { + this->updateStreamerModeIcon(); + }); +} + +void Window::updateStreamerModeIcon() +{ + // A duplicate of this code is in SplitNotebook class (in Notebook.{c,h}pp) + // That one is the one near splits (on linux and mac or non-main windows on Windows) + // This copy handles the TitleBar icon in Window (main window on Windows) + if (this->streamerModeTitlebarIcon_ == nullptr) + { + return; + } +#ifdef Q_OS_WIN + assert(this->getType() == WindowType::Main); + if (getTheme()->isLightTheme()) + { + this->streamerModeTitlebarIcon_->setPixmap( + getResources().buttons.streamerModeEnabledLight); + } + else + { + this->streamerModeTitlebarIcon_->setPixmap( + getResources().buttons.streamerModeEnabledDark); + } + this->streamerModeTitlebarIcon_->setVisible(isInStreamerMode()); +#else + // clang-format off + assert(false && "Streamer mode TitleBar icon should not exist on non-Windows OSes"); + // clang-format on +#endif +} + +void Window::themeChangedEvent() +{ + this->updateStreamerModeIcon(); + BaseWindow::themeChangedEvent(); } void Window::addDebugStuff(HotkeyController::HotkeyMap &actions) diff --git a/src/widgets/Window.hpp b/src/widgets/Window.hpp index d9974759..6d25d875 100644 --- a/src/widgets/Window.hpp +++ b/src/widgets/Window.hpp @@ -31,6 +31,7 @@ public: protected: void closeEvent(QCloseEvent *event) override; bool event(QEvent *event) override; + void themeChangedEvent() override; private: void addCustomTitlebarButtons(); @@ -51,6 +52,10 @@ private: pajlada::Signals::SignalHolder signalHolder_; std::vector bSignals_; + // this is only used on Windows and only on the main window, for the one used otherwise, see SplitNotebook in Notebook.hpp + TitleBarButton *streamerModeTitlebarIcon_ = nullptr; + void updateStreamerModeIcon(); + friend class Notebook; }; diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index 5bd21886..1ee94b4c 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -195,6 +195,11 @@ void SettingsDialog::filterElements(const QString &text) } } +void SettingsDialog::setElementFilter(const QString &query) +{ + this->ui_.search->setText(query); +} + void SettingsDialog::addTabs() { this->ui_.tabContainer->setSpacing(0); @@ -203,7 +208,7 @@ void SettingsDialog::addTabs() // 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->addTab([]{return new GeneralPage;}, "General", ":/settings/about.svg", SettingsTabId::General); this->ui_.tabContainer->addSpacing(16); this->addTab([]{return new AccountsPage;}, "Accounts", ":/settings/accounts.svg", SettingsTabId::Accounts); this->addTab([]{return new NicknamesPage;}, "Nicknames", ":/settings/accounts.svg"); @@ -316,10 +321,20 @@ void SettingsDialog::showDialog(QWidget *parent, } break; + case SettingsDialogPreference::StreamerMode: { + instance->selectTab(SettingsTabId::General); + } + break; + default:; } instance->show(); + if (preferredTab == SettingsDialogPreference::StreamerMode) + { + // this is needed because each time the settings are opened, the query is reset + instance->setElementFilter("Streamer Mode"); + } instance->activateWindow(); instance->raise(); instance->setFocus(); diff --git a/src/widgets/dialogs/SettingsDialog.hpp b/src/widgets/dialogs/SettingsDialog.hpp index ad8f873d..303842af 100644 --- a/src/widgets/dialogs/SettingsDialog.hpp +++ b/src/widgets/dialogs/SettingsDialog.hpp @@ -27,6 +27,7 @@ class PageHeader : public QFrame enum class SettingsDialogPreference { NoPreference, + StreamerMode, Accounts, ModerationActions, }; @@ -57,6 +58,7 @@ private: void selectTab(SettingsDialogTab *tab, const bool byUser = true); void selectTab(SettingsTabId id); void filterElements(const QString &query); + void setElementFilter(const QString &query); void onOkClicked(); void onCancelClicked(); diff --git a/src/widgets/helper/SettingsDialogTab.hpp b/src/widgets/helper/SettingsDialogTab.hpp index 1c907ad4..921d157f 100644 --- a/src/widgets/helper/SettingsDialogTab.hpp +++ b/src/widgets/helper/SettingsDialogTab.hpp @@ -15,6 +15,7 @@ class SettingsDialog; enum class SettingsTabId { None, + General, Accounts, Moderation, }; diff --git a/src/widgets/helper/TitlebarButton.hpp b/src/widgets/helper/TitlebarButton.hpp index 85e43500..46358237 100644 --- a/src/widgets/helper/TitlebarButton.hpp +++ b/src/widgets/helper/TitlebarButton.hpp @@ -11,7 +11,8 @@ enum class TitleBarButtonStyle { Unmaximize = 4, Close = 8, User = 16, - Settings = 32 + Settings = 32, + StreamerMode = 64, }; class TitleBarButton : public Button diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 05037621..9ea3153b 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -126,6 +126,9 @@ AboutPage::AboutPage() "https://github.com/getsentry/crashpad", ":/licenses/crashpad.txt"); #endif + addLicense(form.getElement(), "Fluent icons", + "https://github.com/microsoft/fluentui-system-icons", + ":/licenses/fluenticons.txt"); } // Attributions