#include "HighlightingPage.hpp" #include "Application.hpp" #include "controllers/highlights/BadgeHighlightModel.hpp" #include "controllers/highlights/HighlightBadge.hpp" #include "controllers/highlights/HighlightBlacklistModel.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" #include "controllers/highlights/HighlightModel.hpp" #include "controllers/highlights/HighlightPhrase.hpp" #include "controllers/highlights/UserHighlightModel.hpp" #include "providers/colors/ColorProvider.hpp" #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "util/Helpers.hpp" #include "util/LayoutCreator.hpp" #include "widgets/dialogs/BadgePickerDialog.hpp" #include "widgets/dialogs/ColorPickerDialog.hpp" #include "widgets/helper/EditableModelView.hpp" #include #include #include #include #include #include #define ALWAYS_PLAY "Play highlight sound even when Chatterino is focused" namespace chatterino { namespace { // Add additional badges for highlights here QList availableBadges = { {"Broadcaster", "broadcaster"}, {"Admin", "admin"}, {"Staff", "staff"}, {"Moderator", "moderator"}, {"Verified", "partner"}, {"VIP", "vip"}, {"Founder", "founder"}, {"Subscriber", "subscriber"}, {"Predicted Blue", "predictions/blue-1,predictions/blue-2"}, {"Predicted Pink", "predictions/pink-2,predictions/pink-1"}, }; } // namespace HighlightingPage::HighlightingPage() { LayoutCreator layoutCreator(this); auto layout = layoutCreator.emplace().withoutMargin(); { // GENERAL // layout.append(this->createCheckBox(ENABLE_HIGHLIGHTS, // getSettings()->enableHighlights)); // TABS auto tabs = layout.emplace(); { // HIGHLIGHTS auto highlights = tabs.appendTab(new QVBoxLayout, "Messages"); { highlights.emplace( "Play notification sounds and highlight messages based on " "certain patterns.\n" "Message highlights are prioritized over badge highlights " "and user highlights."); auto view = highlights .emplace( (new HighlightModel(nullptr)) ->initialized( &getSettings()->highlightedMessages)) .getElement(); view->addRegexHelpLink(); view->setTitles({"Pattern", "Show in\nMentions", "Flash\ntaskbar", "Enable\nregex", "Case-\nsensitive", "Play\nsound", "Custom\nsound", "Color"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch); // fourtf: make class extrend BaseWidget and add this to // dpiChanged QTimer::singleShot(1, [view] { view->getTableView()->resizeColumnsToContents(); view->getTableView()->setColumnWidth(0, 400); }); view->addButtonPressed.connect([] { getSettings()->highlightedMessages.append(HighlightPhrase{ "my phrase", true, true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); }); QObject::connect(view->getTableView(), &QTableView::clicked, [this, view](const QModelIndex &clicked) { this->tableCellClicked( clicked, view, HighlightTab::Messages); }); } auto pingUsers = tabs.appendTab(new QVBoxLayout, "Users"); { pingUsers.emplace( "Play notification sounds and highlight messages from " "certain users.\n" "User highlights are prioritized badge highlights, but " "under message highlights."); EditableModelView *view = pingUsers .emplace( (new UserHighlightModel(nullptr)) ->initialized(&getSettings()->highlightedUsers)) .getElement(); view->addRegexHelpLink(); view->getTableView()->horizontalHeader()->hideSection( HighlightModel::Column::UseRegex); view->getTableView()->horizontalHeader()->hideSection( HighlightModel::Column::CaseSensitive); // Case-sensitivity doesn't make sense for user names so it is // set to "false" by default & the column is hidden view->setTitles({"Username", "Show in\nMentions", "Flash\ntaskbar", "Enable\nregex", "Case-\nsensitive", "Play\nsound", "Custom\nsound", "Color"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch); // fourtf: make class extrend BaseWidget and add this to // dpiChanged QTimer::singleShot(1, [view] { view->getTableView()->resizeColumnsToContents(); view->getTableView()->setColumnWidth(0, 200); }); view->addButtonPressed.connect([] { getSettings()->highlightedUsers.append(HighlightPhrase{ "highlighted user", true, true, false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); }); QObject::connect(view->getTableView(), &QTableView::clicked, [this, view](const QModelIndex &clicked) { this->tableCellClicked( clicked, view, HighlightTab::Users); }); } auto badgeHighlights = tabs.appendTab(new QVBoxLayout, "Badges"); { badgeHighlights.emplace( "Play notification sounds and highlight messages based on " "user badges.\n" "Badge highlights are prioritzed under user and message " "highlights."); auto view = badgeHighlights .emplace( (new BadgeHighlightModel(nullptr)) ->initialized( &getSettings()->highlightedBadges)) .getElement(); view->setTitles({"Name", "Show In\nMentions", "Flash\ntaskbar", "Play\nsound", "Custom\nsound", "Color"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch); // fourtf: make class extrend BaseWidget and add this to // dpiChanged QTimer::singleShot(1, [view] { view->getTableView()->resizeColumnsToContents(); view->getTableView()->setColumnWidth(0, 200); }); view->addButtonPressed.connect([this] { auto d = std::make_shared( availableBadges, this); d->setWindowTitle("Choose badge"); if (d->exec() == QDialog::Accepted) { auto s = d->getSelection(); if (!s) { return; } getSettings()->highlightedBadges.append( HighlightBadge{s->badgeName(), s->displayName(), false, false, false, "", *ColorProvider::instance().color( ColorType::SelfHighlight)}); } }); QObject::connect(view->getTableView(), &QTableView::clicked, [this, view](const QModelIndex &clicked) { this->tableCellClicked( clicked, view, HighlightTab::Badges); }); } auto disabledUsers = tabs.appendTab(new QVBoxLayout, "Blacklisted Users"); { disabledUsers.emplace( "Disable notification sounds and highlights from certain " "users (e.g. bots)."); EditableModelView *view = disabledUsers .emplace( (new HighlightBlacklistModel(nullptr)) ->initialized(&getSettings()->blacklistedUsers)) .getElement(); view->addRegexHelpLink(); view->setTitles({"Username", "Enable\nregex"}); view->getTableView()->horizontalHeader()->setSectionResizeMode( QHeaderView::Fixed); view->getTableView()->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch); // fourtf: make class extrend BaseWidget and add this to // dpiChanged QTimer::singleShot(1, [view] { view->getTableView()->resizeColumnsToContents(); view->getTableView()->setColumnWidth(0, 200); }); view->addButtonPressed.connect([] { getSettings()->blacklistedUsers.append( HighlightBlacklistUser{"blacklisted user", false}); }); } } // MISC auto customSound = layout.emplace().withoutMargin(); { auto label = customSound.append(this->createLabel( [](const auto &value) { if (value.isEmpty()) { return QString("Default sound: Chatterino Ping"); } auto url = QUrl::fromLocalFile(value); return QString("Default sound: %2") .arg(url.toString(QUrl::FullyEncoded), shortenString(url.fileName(), 50)); }, getSettings()->pathHighlightSound)); label->setToolTip( "This sound will play for all highlight phrases that have " "sound enabled and don't have a custom sound set."); label->setTextFormat(Qt::RichText); label->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByKeyboard); label->setOpenExternalLinks(true); customSound->setStretchFactor(label.getElement(), 1); auto clearSound = customSound.emplace("Clear"); auto selectFile = customSound.emplace("Change..."); QObject::connect(selectFile.getElement(), &QPushButton::clicked, this, [this]() mutable { auto fileName = QFileDialog::getOpenFileName( this, tr("Open Sound"), "", tr("Audio Files (*.mp3 *.wav)")); getSettings()->pathHighlightSound = fileName; }); QObject::connect(clearSound.getElement(), &QPushButton::clicked, this, [=]() mutable { getSettings()->pathHighlightSound = QString(); }); getSettings()->pathHighlightSound.connect( [clearSound = clearSound.getElement()](const auto &value) { if (value.isEmpty()) { clearSound->hide(); } else { clearSound->show(); } }, this->managedConnections_); } layout.append(createCheckBox(ALWAYS_PLAY, getSettings()->highlightAlwaysPlaySound)); layout.append(createCheckBox( "Flash taskbar only stops highlighting when Chatterino is focused", getSettings()->longAlerts)); } // ---- misc this->disabledUsersChangedTimer_.setSingleShot(true); } void HighlightingPage::openSoundDialog(const QModelIndex &clicked, EditableModelView *view, int soundColumn) { auto fileUrl = QFileDialog::getOpenFileUrl(this, tr("Open Sound"), QUrl(), tr("Audio Files (*.mp3 *.wav)")); view->getModel()->setData(clicked, fileUrl, Qt::UserRole); view->getModel()->setData(clicked, fileUrl.fileName(), Qt::DisplayRole); } void HighlightingPage::openColorDialog(const QModelIndex &clicked, EditableModelView *view, HighlightTab tab) { auto initial = view->getModel()->data(clicked, Qt::DecorationRole).value(); auto dialog = new ColorPickerDialog(initial, this); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); dialog->closed.connect([=](auto selected) { if (selected.isValid()) { view->getModel()->setData(clicked, selected, Qt::DecorationRole); if (tab == HighlightTab::Messages) { /* * For preset highlights in the "Messages" tab, we need to * manually update the color map. */ auto instance = ColorProvider::instance(); switch (clicked.row()) { case 0: instance.updateColor(ColorType::SelfHighlight, selected); break; case 1: instance.updateColor(ColorType::Whisper, selected); break; case 2: instance.updateColor(ColorType::Subscription, selected); break; } } } }); } void HighlightingPage::tableCellClicked(const QModelIndex &clicked, EditableModelView *view, HighlightTab tab) { switch (tab) { case HighlightTab::Messages: case HighlightTab::Users: { using Column = HighlightModel::Column; bool restrictColorRow = (tab == HighlightTab::Messages && clicked.row() == HighlightModel::HighlightRowIndexes::WhisperRow); if (clicked.column() == Column::SoundPath && clicked.flags().testFlag(Qt::ItemIsEnabled)) { this->openSoundDialog(clicked, view, Column::SoundPath); } else if (clicked.column() == Column::Color && !restrictColorRow) { this->openColorDialog(clicked, view, tab); } } break; case HighlightTab::Badges: { using Column = BadgeHighlightModel::Column; if (clicked.column() == Column::SoundPath) { this->openSoundDialog(clicked, view, Column::SoundPath); } else if (clicked.column() == Column::Color) { this->openColorDialog(clicked, view, tab); } } break; } } } // namespace chatterino