diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c2fb46db03..5e88ba9ff37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unversioned +- Minor: Add muting of all application notifications via keybind/window menu. (#5629) + ## 2.5.2-beta.1 - Major: Add option to show pronouns in user card. (#5442, #5583) diff --git a/src/controllers/hotkeys/ActionNames.hpp b/src/controllers/hotkeys/ActionNames.hpp index f15d2e2026b..bb83048b631 100644 --- a/src/controllers/hotkeys/ActionNames.hpp +++ b/src/controllers/hotkeys/ActionNames.hpp @@ -355,6 +355,19 @@ inline const std::map actionNames{ .argumentsPromptHover = "Should the tabs be enabled, disabled, " "toggled, or live-only.", }}, + {"toggleGlobalNotificationSuppression", + ActionDefinition{ + .displayName = "Toggle muting of all notifications", + .argumentDescription = "[on, off, or toggle. default: toggle]", + .minCountArguments = 0, + .maxCountArguments = 1, + .possibleArguments{{"Toggle", {}}, + {"Mute notifications", {"on"}}, + {"Unmute notifications", {"off"}}}, + .argumentsPrompt = "New value:", + .argumentsPromptHover = "Should all highlight notifications be " + "enabled, disabled, or toggled.", + }}, }}, }; diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 38d2170bc3e..eae56beff8a 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -169,6 +169,12 @@ void actuallyTriggerHighlights(const QString &channelName, bool playSound, return; } + if (getSettings()->globallySuppressNotifications) + { + // Notifications are globally suppressed, ignore it. + return; + } + const bool hasFocus = (QApplication::focusWidget() != nullptr); const bool resolveFocus = !hasFocus || getSettings()->highlightAlwaysPlaySound; diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index e996a83250d..a2c6bf4be88 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -525,6 +525,9 @@ class Settings BoolSetting suppressInitialLiveNotification = { "/notifications/suppressInitialLive", false}; + BoolSetting globallySuppressNotifications = { + "/notifications/globalSuppression", false}; + BoolSetting notificationToast = {"/notifications/enableToast", false}; IntSetting openFromToast = {"/notifications/openFromToast", static_cast(ToastReaction::OpenInBrowser)}; diff --git a/src/widgets/Notebook.cpp b/src/widgets/Notebook.cpp index 53c6dfcfa45..abd4bc28ab8 100644 --- a/src/widgets/Notebook.cpp +++ b/src/widgets/Notebook.cpp @@ -78,6 +78,26 @@ Notebook::Notebook(QWidget *parent) << "Notebook must be created within a BaseWindow"; } + this->toggleNotificationSuppression_ = + new QAction("Mute all notifications", this); + this->toggleNotificationSuppression_->setCheckable(true); + this->toggleNotificationSuppression_->setChecked( + getSettings()->globallySuppressNotifications); + this->toggleNotificationSuppression_->setShortcut( + getApp()->getHotkeys()->getDisplaySequence( + HotkeyCategory::Window, "toggleGlobalNotificationSuppression")); + + QObject::connect(this->toggleNotificationSuppression_, &QAction::triggered, + [] { + getSettings()->globallySuppressNotifications = + !getSettings()->globallySuppressNotifications; + }); + getSettings()->globallySuppressNotifications.connect( + [this](const bool &value) { + this->toggleNotificationSuppression_->setChecked(value); + }, + this->signalHolder_); + // Manually resize the add button so the initial paint uses the correct // width when computing the maximum width occupied per column in vertical // tab rendering. @@ -1241,8 +1261,8 @@ void Notebook::setLockNotebookLayout(bool value) void Notebook::addNotebookActionsToMenu(QMenu *menu) { menu->addAction(this->lockNotebookLayoutAction_); - menu->addAction(this->toggleTopMostAction_); + menu->addAction(this->toggleNotificationSuppression_); } NotebookButton *Notebook::getAddButton() @@ -1558,6 +1578,19 @@ void SplitNotebook::addCustomButtons() }); QObject::connect(getApp()->getStreamerMode(), &IStreamerMode::changed, this, &SplitNotebook::updateStreamerModeIcon); + + // do not disturb + this->doNotDisturbIcon_ = this->addCustomButton(); + QObject::connect(this->doNotDisturbIcon_, &NotebookButton::leftClicked, + [this] { + getSettings()->globallySuppressNotifications = false; + }); + getSettings()->globallySuppressNotifications.connect( + [this] { + this->updateDoNotDisturbIcon(); + }, + this->signalHolder_); + this->updateStreamerModeIcon(); } @@ -1584,6 +1617,31 @@ void SplitNotebook::updateStreamerModeIcon() getApp()->getStreamerMode()->isEnabled()); } +void SplitNotebook::updateDoNotDisturbIcon() +{ + // TODO(jupjohn): add custom icon for this + if (this->doNotDisturbIcon_ == 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->doNotDisturbIcon_->setPixmap( + getResources().buttons.streamerModeEnabledLight); + } + else + { + this->doNotDisturbIcon_->setPixmap( + getResources().buttons.streamerModeEnabledDark); + } + this->doNotDisturbIcon_->setVisible( + getSettings()->globallySuppressNotifications); +} + void SplitNotebook::themeChangedEvent() { this->updateStreamerModeIcon(); diff --git a/src/widgets/Notebook.hpp b/src/widgets/Notebook.hpp index ac6c4dad79f..ab239a5cde4 100644 --- a/src/widgets/Notebook.hpp +++ b/src/widgets/Notebook.hpp @@ -210,6 +210,7 @@ class Notebook : public BaseWidget QAction *lockNotebookLayoutAction_; QAction *toggleTopMostAction_; + QAction *toggleNotificationSuppression_; // This filter, if set, is used to figure out the visibility of // the tabs in this notebook. @@ -252,6 +253,9 @@ class SplitNotebook : public Notebook // Main window on Windows has basically a duplicate of this in Window NotebookButton *streamerModeIcon_{}; void updateStreamerModeIcon(); + + NotebookButton *doNotDisturbIcon_{}; + void updateDoNotDisturbIcon(); }; } // namespace chatterino diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index 92b85c72571..da2aa7eed5d 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -222,6 +222,17 @@ void Window::addCustomTitlebarButtons() QObject::connect(getApp()->getStreamerMode(), &IStreamerMode::changed, this, &Window::updateStreamerModeIcon); + // do not disturb + this->doNotDisturbTitlebarIcon_ = + this->addTitleBarButton(TitleBarButtonStyle::DoNotDisturb, [this] { + getSettings()->globallySuppressNotifications = false; + }); + getSettings()->globallySuppressNotifications.connect( + [this] { + this->updateDoNotDisturbIcon(); + }, + this->signalHolder_); + // Update initial state this->updateStreamerModeIcon(); } @@ -256,6 +267,38 @@ void Window::updateStreamerModeIcon() #endif } +void Window::updateDoNotDisturbIcon() +{ + // TODO(jupjohn): add custom icon for this + + // 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->doNotDisturbTitlebarIcon_ == nullptr) + { + return; + } +#ifdef Q_OS_WIN + assert(this->getType() == WindowType::Main); + if (getTheme()->isLightTheme()) + { + this->doNotDisturbIcon_->setPixmap( + getResources().buttons.streamerModeEnabledLight); + } + else + { + this->doNotDisturbIcon_->setPixmap( + getResources().buttons.streamerModeEnabledDark); + } + this->doNotDisturbIcon_->setVisible( + getSettings()->globallySuppressNotifications); +#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(); @@ -682,7 +725,38 @@ void Window::addShortcuts() return ""; }}, - }; + {"toggleGlobalNotificationSuppression", + [](const std::vector &arguments) -> QString { + QString arg = arguments.empty() ? "toggle" : arguments.front(); + + bool desiredValue = false; + if (arg == "toggle") + { + desiredValue = !getSettings()->globallySuppressNotifications; + } + else if (arg == "on") + { + desiredValue = true; + } + else if (arg == "off") + { + desiredValue = false; + } + else + { + qCWarning(chatterinoHotkeys) + << "Invalid argument for " + "toggleGlobalNotificationSuppression hotkey: " + << arg; + return QString("Invalid argument for " + "toggleGlobalNotificationSuppression hotkey: " + "%1. Use \"on\", \"off\", or \"toggle\".") + .arg(arg); + } + + getSettings()->globallySuppressNotifications = desiredValue; + return ""; + }}}; this->addDebugStuff(actions); diff --git a/src/widgets/Window.hpp b/src/widgets/Window.hpp index 6d25d875c5a..ee2420a7b9d 100644 --- a/src/widgets/Window.hpp +++ b/src/widgets/Window.hpp @@ -56,6 +56,9 @@ class Window : public BaseWindow TitleBarButton *streamerModeTitlebarIcon_ = nullptr; void updateStreamerModeIcon(); + TitleBarButton *doNotDisturbTitlebarIcon_ = nullptr; + void updateDoNotDisturbIcon(); + friend class Notebook; }; diff --git a/src/widgets/helper/TitlebarButton.hpp b/src/widgets/helper/TitlebarButton.hpp index c1868f9d9d8..2ef490a9377 100644 --- a/src/widgets/helper/TitlebarButton.hpp +++ b/src/widgets/helper/TitlebarButton.hpp @@ -13,6 +13,7 @@ enum class TitleBarButtonStyle { User = 16, Settings = 32, StreamerMode = 64, + DoNotDisturb = 128, }; class TitleBarButton : public Button