Compare commits

...

5 commits

Author SHA1 Message Date
pajlada 7b5e33de69
Merge f54bdf939a into 18c4815ad7 2024-10-22 18:48:59 +02:00
iProdigy 18c4815ad7
feat: add shared chat badge (#5661) 2024-10-22 18:42:19 +02:00
Rasmus Karlsson f54bdf939a
add changelog entry 2024-09-08 14:54:02 +02:00
Rasmus Karlsson adf124c11b
feat: add SettingWidget
bit more like a builder pattern for adding settings to settings pages

i have migrated a few settings, but don't want to do all of them
2024-09-08 14:52:11 +02:00
Rasmus Karlsson 74101d40f5
fix: only require one of the keywords to match when searching for
settings
2024-09-08 14:30:16 +02:00
19 changed files with 460 additions and 81 deletions

View file

@ -7,7 +7,7 @@
- Major: Improve high-DPI support on Windows. (#4868, #5391, #5664, #5666) - Major: Improve high-DPI support on Windows. (#4868, #5391, #5664, #5666)
- Major: Added transparent overlay window (default keybind: <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>N</kbd>). (#4746, #5643, #5659) - Major: Added transparent overlay window (default keybind: <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>N</kbd>). (#4746, #5643, #5659)
- Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530) - Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530)
- Minor: Add support for Shared Chat messages. Shared chat messages can be filtered with the `flags.shared` filter variable, or with search using `is:shared`. Some messages like subscriptions are filtered on purpose to avoid confusion for the broadcaster. If you have both channels participating in Shared Chat open, only one of the message triggering your highlight will trigger. (#5606, #5625) - Minor: Add support for Shared Chat messages. Shared chat messages can be filtered with the `flags.shared` filter variable, or with search using `is:shared`. Some messages like subscriptions are filtered on purpose to avoid confusion for the broadcaster. If you have both channels participating in Shared Chat open, only one of the message triggering your highlight will trigger. (#5606, #5625, #5661)
- Minor: Moved tab visibility control to a submenu, without any toggle actions. (#5530) - Minor: Moved tab visibility control to a submenu, without any toggle actions. (#5530)
- Minor: Add option to customise Moderation buttons with images. (#5369) - Minor: Add option to customise Moderation buttons with images. (#5369)
- Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300) - Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300)
@ -70,6 +70,7 @@
- Dev: Refactor and document `Scrollbar`. (#5334, #5393) - Dev: Refactor and document `Scrollbar`. (#5334, #5393)
- Dev: Refactor `TwitchIrcServer`, making it abstracted. (#5421, #5435) - Dev: Refactor `TwitchIrcServer`, making it abstracted. (#5421, #5435)
- Dev: Reduced the amount of scale events. (#5404, #5406) - Dev: Reduced the amount of scale events. (#5404, #5406)
- Dev: Refactored settings widget creation. (#5585)
- Dev: Removed unused timegate settings. (#5361) - Dev: Removed unused timegate settings. (#5361)
- Dev: Add `Channel::addSystemMessage` helper function, allowing us to avoid the common `channel->addMessage(makeSystemMessage(...));` pattern. (#5500) - Dev: Add `Channel::addSystemMessage` helper function, allowing us to avoid the common `channel->addMessage(makeSystemMessage(...));` pattern. (#5500)
- Dev: Unsingletonize `Resources2`. (#5460) - Dev: Unsingletonize `Resources2`. (#5460)

View file

@ -3,6 +3,7 @@
#include "common/Args.hpp" #include "common/Args.hpp"
#include "mocks/DisabledStreamerMode.hpp" #include "mocks/DisabledStreamerMode.hpp"
#include "mocks/EmptyApplication.hpp" #include "mocks/EmptyApplication.hpp"
#include "mocks/TwitchUsers.hpp"
#include "providers/bttv/BttvLiveUpdates.hpp" #include "providers/bttv/BttvLiveUpdates.hpp"
#include "singletons/Fonts.hpp" #include "singletons/Fonts.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
@ -55,6 +56,11 @@ public:
return &this->fonts; return &this->fonts;
} }
ITwitchUsers *getTwitchUsers() override
{
return &this->twitchUsers;
}
BttvLiveUpdates *getBttvLiveUpdates() override BttvLiveUpdates *getBttvLiveUpdates() override
{ {
return nullptr; return nullptr;
@ -71,6 +77,7 @@ public:
DisabledStreamerMode streamerMode; DisabledStreamerMode streamerMode;
Theme theme; Theme theme;
Fonts fonts; Fonts fonts;
TwitchUsers twitchUsers;
}; };
} // namespace chatterino::mock } // namespace chatterino::mock

View file

@ -0,0 +1,24 @@
#pragma once
#include "providers/twitch/TwitchUser.hpp"
#include "providers/twitch/TwitchUsers.hpp"
namespace chatterino::mock {
class TwitchUsers : public ITwitchUsers
{
public:
TwitchUsers() = default;
std::shared_ptr<TwitchUser> resolveID(const UserId &id)
{
TwitchUser u = {
.id = id.string,
.name = {},
.displayName = {},
};
return std::make_shared<TwitchUser>(u);
}
};
} // namespace chatterino::mock

View file

@ -61,6 +61,11 @@ chatterino--DescriptionLabel {
color: #999; color: #999;
} }
QLabel#description {
color: #999;
padding-left: 10px;
}
chatterino--NavigationLabel { chatterino--NavigationLabel {
font-family: "Segoe UI light"; font-family: "Segoe UI light";
font-size: 15px; font-size: 15px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -713,6 +713,8 @@ set(SOURCE_FILES
widgets/settingspages/PluginsPage.hpp widgets/settingspages/PluginsPage.hpp
widgets/settingspages/SettingsPage.cpp widgets/settingspages/SettingsPage.cpp
widgets/settingspages/SettingsPage.hpp widgets/settingspages/SettingsPage.hpp
widgets/settingspages/SettingWidget.cpp
widgets/settingspages/SettingWidget.hpp
widgets/splits/ClosedSplits.cpp widgets/splits/ClosedSplits.cpp
widgets/splits/ClosedSplits.hpp widgets/splits/ClosedSplits.hpp

View file

@ -32,6 +32,7 @@
#include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchIrc.hpp" #include "providers/twitch/TwitchIrc.hpp"
#include "providers/twitch/TwitchIrcServer.hpp" #include "providers/twitch/TwitchIrcServer.hpp"
#include "providers/twitch/TwitchUsers.hpp"
#include "singletons/Emotes.hpp" #include "singletons/Emotes.hpp"
#include "singletons/Resources.hpp" #include "singletons/Resources.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
@ -380,6 +381,18 @@ EmotePtr makeAutoModBadge()
Url{"https://dashboard.twitch.tv/settings/moderation/automod"}}); Url{"https://dashboard.twitch.tv/settings/moderation/automod"}});
} }
EmotePtr makeSharedChatBadge(const QString &sourceName)
{
return std::make_shared<Emote>(Emote{
.name = EmoteName{},
.images = ImageSet{Image::fromResourcePixmap(
getResources().twitch.sharedChat, 0.25)},
.tooltip = Tooltip{"Shared Message" +
(sourceName.isEmpty() ? "" : " from " + sourceName)},
.homePage = Url{"https://link.twitch.tv/SharedChatViewer"},
});
}
std::tuple<std::optional<EmotePtr>, MessageElementFlags, bool> parseEmote( std::tuple<std::optional<EmotePtr>, MessageElementFlags, bool> parseEmote(
TwitchChannel *twitchChannel, const EmoteName &name) TwitchChannel *twitchChannel, const EmoteName &name)
{ {
@ -2751,6 +2764,28 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags,
return; return;
} }
if (this->message().flags.has(MessageFlag::SharedMessage))
{
const QString sourceId = tags["source-room-id"].toString();
QString sourceName;
if (sourceId.isEmpty())
{
sourceName = "";
}
else if (twitchChannel->roomId() == sourceId)
{
sourceName = twitchChannel->getName();
}
else
{
sourceName =
getApp()->getTwitchUsers()->resolveID({sourceId})->displayName;
}
this->emplace<BadgeElement>(makeSharedChatBadge(sourceName),
MessageElementFlag::BadgeSharedChannel);
}
auto badgeInfos = parseBadgeInfoTag(tags); auto badgeInfos = parseBadgeInfoTag(tags);
auto badges = parseBadgeTag(tags); auto badges = parseBadgeTag(tags);
appendBadges(this, badges, badgeInfos, twitchChannel); appendBadges(this, badges, badgeInfos, twitchChannel);

View file

@ -66,6 +66,10 @@ enum class MessageElementFlag : int64_t {
BitsStatic = (1LL << 11), BitsStatic = (1LL << 11),
BitsAnimated = (1LL << 12), BitsAnimated = (1LL << 12),
// Slot 0: Twitch
// - Shared Channel indicator badge
BadgeSharedChannel = (1LL << 37),
// Slot 1: Twitch // Slot 1: Twitch
// - Staff badge // - Staff badge
// - Admin badge // - Admin badge
@ -119,7 +123,7 @@ enum class MessageElementFlag : int64_t {
Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority | Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority |
BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV | BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV |
BadgeFfz, BadgeFfz | BadgeSharedChannel,
ChannelName = (1LL << 20), ChannelName = (1LL << 20),

View file

@ -195,6 +195,7 @@ void WindowManager::updateWordTypeMask()
flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic); flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic);
// badges // badges
flags.set(MEF::BadgeSharedChannel);
flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority
: MEF::None); : MEF::None);
flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions

View file

@ -2408,6 +2408,11 @@ void ChannelView::handleMouseClick(QMouseEvent *event,
return; return;
} }
if (link.value.startsWith("id:"))
{
return;
}
// Insert @username into split input // Insert @username into split input
const bool commaMention = const bool commaMention =
getSettings()->mentionUsersWithComma; getSettings()->mentionUsersWithComma;

View file

@ -20,6 +20,7 @@
#include "util/IncognitoBrowser.hpp" #include "util/IncognitoBrowser.hpp"
#include "widgets/BaseWindow.hpp" #include "widgets/BaseWindow.hpp"
#include "widgets/settingspages/GeneralPageView.hpp" #include "widgets/settingspages/GeneralPageView.hpp"
#include "widgets/settingspages/SettingWidget.hpp"
#include <magic_enum/magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include <QDesktopServices> #include <QDesktopServices>
@ -265,10 +266,13 @@ void GeneralPage::initLayout(GeneralPageView &layout)
}, },
false, "Choose which tabs are visible in the notebook"); false, "Choose which tabs are visible in the notebook");
layout.addCheckbox( SettingWidget::inverseCheckbox("Show message reply context",
"Show message reply context", s.hideReplyContext, true, s.hideReplyContext)
"This setting will only affect how messages are shown. You can reply " ->setTooltip(
"to a message regardless of this setting."); "This setting will only affect how messages are shown. You can "
"reply to a message regardless of this setting.")
->addTo(layout);
layout.addCheckbox("Show message reply button", s.showReplyButton, false, layout.addCheckbox("Show message reply button", s.showReplyButton, false,
"Show a reply button next to every chat message"); "Show a reply button next to every chat message");
@ -614,10 +618,19 @@ void GeneralPage::initLayout(GeneralPageView &layout)
"Google", "Google",
}, },
s.emojiSet); s.emojiSet);
layout.addCheckbox("Show BTTV global emotes", s.enableBTTVGlobalEmotes); SettingWidget::checkbox("Show BetterTTV global emotes",
layout.addCheckbox("Show BTTV channel emotes", s.enableBTTVChannelEmotes); s.enableBTTVGlobalEmotes)
layout.addCheckbox("Enable BTTV live emote updates (requires restart)", ->addKeywords({"bttv"})
s.enableBTTVLiveUpdates); ->addTo(layout);
SettingWidget::checkbox("Show BetterTTV channel emotes",
s.enableBTTVChannelEmotes)
->addKeywords({"bttv"})
->addTo(layout);
SettingWidget::checkbox(
"Enable BetterTTV live emote updates (requires restart)",
s.enableBTTVLiveUpdates)
->addKeywords({"bttv"})
->addTo(layout);
layout.addCheckbox("Show FFZ global emotes", s.enableFFZGlobalEmotes); layout.addCheckbox("Show FFZ global emotes", s.enableFFZGlobalEmotes);
layout.addCheckbox("Show FFZ channel emotes", s.enableFFZChannelEmotes); layout.addCheckbox("Show FFZ channel emotes", s.enableFFZChannelEmotes);
layout.addCheckbox("Show 7TV global emotes", s.enableSevenTVGlobalEmotes); layout.addCheckbox("Show 7TV global emotes", s.enableSevenTVGlobalEmotes);
@ -1063,10 +1076,11 @@ void GeneralPage::initLayout(GeneralPageView &layout)
false, false,
"Make all clickable links lowercase to deter " "Make all clickable links lowercase to deter "
"phishing attempts."); "phishing attempts.");
layout.addCheckbox( SettingWidget::checkbox("Show user's pronouns in user card", s.showPronouns)
"Show user's pronouns in user card", s.showPronouns, false, ->setDescription(
"Shows users' pronouns in their user card. " R"(Pronouns are retrieved from <a href="https://pr.alejo.io">pr.alejo.io</a> when a user card is opened.)")
"Pronouns are retrieved from alejo.io when the user card is opened."); ->addTo(layout);
layout.addCheckbox("Bold @usernames", s.boldUsernames, false, layout.addCheckbox("Bold @usernames", s.boldUsernames, false,
"Bold @mentions to make them more noticable."); "Bold @mentions to make them more noticable.");
layout.addCheckbox("Color @usernames", s.colorUsernames, false, layout.addCheckbox("Color @usernames", s.colorUsernames, false,
@ -1191,25 +1205,20 @@ void GeneralPage::initLayout(GeneralPageView &layout)
"@mention for the related thread. If the reply context is hidden, " "@mention for the related thread. If the reply context is hidden, "
"these mentions will never be stripped."); "these mentions will never be stripped.");
layout.addDropdownEnumClass<ChatSendProtocol>( SettingWidget::dropdown("Chat send protocol", s.chatSendProtocol)
"Chat send protocol", qmagicenum::enumNames<ChatSendProtocol>(), ->setTooltip("'Helix' will use Twitch's Helix API to send message. "
s.chatSendProtocol, "'IRC' will use IRC to send messages.")
"'Helix' will use Twitch's Helix API to send message. 'IRC' will use " ->addTo(layout);
"IRC to send messages.",
{});
layout.addCheckbox( SettingWidget::checkbox("Show send message button", s.showSendButton)
"Show send message button", s.showSendButton, false, ->setTooltip("Show a Send button next to each split input that can be "
"Show a Send button next to each split input that can be " "clicked to send the message")
"clicked to send the message"); ->addTo(layout);
auto *soundBackend = layout.addDropdownEnumClass<SoundBackend>( SettingWidget::dropdown("Sound backend (requires restart)", s.soundBackend)
"Sound backend (requires restart)", ->setTooltip("Change this only if you're noticing issues "
qmagicenum::enumNames<SoundBackend>(), s.soundBackend, "with sound playback on your system")
"Change this only if you're noticing issues with sound playback on " ->addTo(layout);
"your system",
{});
soundBackend->setMinimumWidth(soundBackend->minimumSizeHint().width());
layout.addStretch(); layout.addStretch();

View file

@ -6,6 +6,7 @@
#include "widgets/dialogs/ColorPickerDialog.hpp" #include "widgets/dialogs/ColorPickerDialog.hpp"
#include "widgets/helper/color/ColorButton.hpp" #include "widgets/helper/color/ColorButton.hpp"
#include "widgets/helper/Line.hpp" #include "widgets/helper/Line.hpp"
#include "widgets/settingspages/SettingWidget.hpp"
#include <QRegularExpression> #include <QRegularExpression>
#include <QScrollArea> #include <QScrollArea>
@ -44,9 +45,16 @@ GeneralPageView::GeneralPageView(QWidget *parent)
}); });
} }
void GeneralPageView::addWidget(QWidget *widget) void GeneralPageView::addWidget(QWidget *widget, QStringList keywords)
{ {
this->contentLayout_->addWidget(widget); this->contentLayout_->addWidget(widget);
if (!keywords.isEmpty())
{
this->groups_.back().widgets.push_back({
.element = widget,
.keywords = keywords,
});
}
} }
void GeneralPageView::addLayout(QLayout *layout) void GeneralPageView::addLayout(QLayout *layout)
@ -376,11 +384,10 @@ bool GeneralPageView::filterElements(const QString &query)
currentSubtitleVisible = true; currentSubtitleVisible = true;
widget.element->show(); widget.element->show();
groupAny = true; groupAny = true;
break;
} }
else
{ widget.element->hide();
widget.element->hide();
}
} }
} }

View file

@ -21,6 +21,7 @@ class QScrollArea;
namespace chatterino { namespace chatterino {
class ColorButton; class ColorButton;
class SettingWidget;
class Space : public QLabel class Space : public QLabel
{ {
@ -95,7 +96,7 @@ class GeneralPageView : public QWidget
public: public:
GeneralPageView(QWidget *parent = nullptr); GeneralPageView(QWidget *parent = nullptr);
void addWidget(QWidget *widget); void addWidget(QWidget *widget, QStringList keywords = {});
void addLayout(QLayout *layout); void addLayout(QLayout *layout);
void addStretch(); void addStretch();
@ -274,50 +275,6 @@ public:
return combo; return combo;
} }
template <typename T, std::size_t N>
ComboBox *addDropdownEnumClass(const QString &text,
const std::array<QStringView, N> &items,
EnumStringSetting<T> &setting,
QString toolTipText,
const QString &defaultValueText)
{
auto *combo = this->addDropdown(text, {}, std::move(toolTipText));
for (const auto &item : items)
{
combo->addItem(item.toString());
}
if (!defaultValueText.isEmpty())
{
combo->setCurrentText(defaultValueText);
}
setting.connect(
[&setting, combo](const QString &value) {
auto enumValue =
qmagicenum::enumCast<T>(value, qmagicenum::CASE_INSENSITIVE)
.value_or(setting.defaultValue);
auto i = magic_enum::enum_integer(enumValue);
combo->setCurrentIndex(i);
},
this->managedConnections_);
QObject::connect(
combo, &QComboBox::currentTextChanged,
[&setting](const auto &newText) {
// The setter for EnumStringSetting does not check that this value is valid
// Instead, it's up to the getters to make sure that the setting is legic - see the enum_cast above
// You could also use the settings `getEnum` function
setting = newText;
getApp()->getWindows()->forceLayoutChannelViews();
});
return combo;
}
void enableIf(QComboBox *widget, auto &setting, auto cb) void enableIf(QComboBox *widget, auto &setting, auto cb)
{ {
auto updateVisibility = [cb = std::move(cb), &setting, widget]() { auto updateVisibility = [cb = std::move(cb), &setting, widget]() {

View file

@ -0,0 +1,144 @@
#include "widgets/settingspages/SettingWidget.hpp"
#include "widgets/settingspages/GeneralPageView.hpp"
#include <QBoxLayout>
#include <QCheckBox>
#include <QLabel>
namespace {
constexpr int MAX_TOOLTIP_LINE_LENGTH = 50;
const auto MAX_TOOLTIP_LINE_LENGTH_PATTERN =
QStringLiteral(R"(.{%1}\S*\K(\s+))").arg(MAX_TOOLTIP_LINE_LENGTH);
const QRegularExpression MAX_TOOLTIP_LINE_LENGTH_REGEX(
MAX_TOOLTIP_LINE_LENGTH_PATTERN);
} // namespace
namespace chatterino {
SettingWidget::SettingWidget(const QString &mainKeyword)
: vLayout(new QVBoxLayout(this))
, hLayout(new QHBoxLayout)
{
this->vLayout->setContentsMargins(0, 0, 0, 0);
this->hLayout->setContentsMargins(0, 0, 0, 0);
this->vLayout->addLayout(hLayout);
this->keywords.append(mainKeyword);
}
SettingWidget *SettingWidget::checkbox(const QString &label,
BoolSetting &setting)
{
auto *widget = new SettingWidget(label);
auto *check = new QCheckBox(label);
widget->hLayout->addWidget(check);
// update when setting changes
setting.connect(
[check](const bool &value, auto) {
check->setChecked(value);
},
widget->managedConnections);
// update setting on toggle
QObject::connect(check, &QCheckBox::toggled, widget,
[&setting](bool state) {
setting = state;
});
widget->actionWidget = check;
widget->label = check;
return widget;
}
SettingWidget *SettingWidget::inverseCheckbox(const QString &label,
BoolSetting &setting)
{
auto *widget = new SettingWidget(label);
auto *check = new QCheckBox(label);
widget->hLayout->addWidget(check);
// update when setting changes
setting.connect(
[check](const bool &value, auto) {
check->setChecked(!value);
},
widget->managedConnections);
// update setting on toggle
QObject::connect(check, &QCheckBox::toggled, widget,
[&setting](bool state) {
setting = !state;
});
widget->actionWidget = check;
widget->label = check;
return widget;
}
SettingWidget *SettingWidget::setTooltip(QString tooltip)
{
assert(!tooltip.isEmpty());
if (tooltip.length() > MAX_TOOLTIP_LINE_LENGTH)
{
// match MAX_TOOLTIP_LINE_LENGTH characters, any remaining
// non-space, and then capture the following space for
// replacement with newline
tooltip.replace(MAX_TOOLTIP_LINE_LENGTH_REGEX, "\n");
}
if (this->label != nullptr)
{
this->label->setToolTip(tooltip);
}
if (this->actionWidget != nullptr)
{
this->actionWidget->setToolTip(tooltip);
}
this->keywords.append(tooltip);
return this;
}
SettingWidget *SettingWidget::setDescription(const QString &text)
{
auto *lbl = new QLabel(text);
lbl->setTextInteractionFlags(Qt::TextBrowserInteraction |
Qt::LinksAccessibleByKeyboard);
lbl->setOpenExternalLinks(true);
lbl->setWordWrap(true);
lbl->setObjectName("description");
this->vLayout->insertWidget(0, lbl);
this->keywords.append(text);
return this;
}
SettingWidget *SettingWidget::addKeywords(const QStringList &newKeywords)
{
this->keywords.append(newKeywords);
return this;
}
void SettingWidget::addTo(GeneralPageView &view)
{
view.addWidget(this, this->keywords);
}
} // namespace chatterino

View file

@ -0,0 +1,106 @@
#pragma once
#include "common/ChatterinoSetting.hpp"
#include "util/QMagicEnum.hpp"
#include "widgets/settingspages/GeneralPageView.hpp"
#include <pajlada/signals/signalholder.hpp>
#include <QBoxLayout>
#include <QComboBox>
#include <QLabel>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QtContainerFwd>
#include <QWidget>
namespace chatterino {
class GeneralPageView;
class SettingWidget : QWidget
{
Q_OBJECT
explicit SettingWidget(const QString &mainKeyword);
public:
~SettingWidget() override = default;
SettingWidget &operator=(const SettingWidget &) = delete;
SettingWidget &operator=(SettingWidget &&) = delete;
SettingWidget(const SettingWidget &other) = delete;
SettingWidget(SettingWidget &&other) = delete;
static SettingWidget *checkbox(const QString &label, BoolSetting &setting);
static SettingWidget *inverseCheckbox(const QString &label,
BoolSetting &setting);
template <typename T>
static SettingWidget *dropdown(const QString &label,
EnumStringSetting<T> &setting)
{
auto *widget = new SettingWidget(label);
auto *lbl = new QLabel(label % ":");
auto *combo = new ComboBox;
combo->setFocusPolicy(Qt::StrongFocus);
for (const auto &item : qmagicenum::enumNames<T>())
{
combo->addItem(item.toString());
}
// TODO: this can probably use some other size hint/size strategy
combo->setMinimumWidth(combo->minimumSizeHint().width());
widget->actionWidget = combo;
widget->label = lbl;
widget->hLayout->addWidget(lbl);
widget->hLayout->addStretch(1);
widget->hLayout->addWidget(combo);
setting.connect(
[&setting, combo](const QString &value) {
auto enumValue =
qmagicenum::enumCast<T>(value, qmagicenum::CASE_INSENSITIVE)
.value_or(setting.defaultValue);
auto i = magic_enum::enum_integer(enumValue);
combo->setCurrentIndex(i);
},
widget->managedConnections);
QObject::connect(
combo, &QComboBox::currentTextChanged,
[&setting](const auto &newText) {
// The setter for EnumStringSetting does not check that this value is valid
// Instead, it's up to the getters to make sure that the setting is legic - see the enum_cast above
// You could also use the settings `getEnum` function
setting = newText;
});
return widget;
}
SettingWidget *setTooltip(QString tooltip);
SettingWidget *setDescription(const QString &text);
/// Add extra keywords to the widget
///
/// All text from the tooltip, description, and label are already keywords
SettingWidget *addKeywords(const QStringList &newKeywords);
void addTo(GeneralPageView &view);
private:
QWidget *label = nullptr;
QWidget *actionWidget = nullptr;
QVBoxLayout *vLayout;
QHBoxLayout *hLayout;
pajlada::Signals::SignalHolder managedConnections;
QStringList keywords;
};
} // namespace chatterino

View file

@ -64,6 +64,24 @@
"trailingSpace": true, "trailingSpace": true,
"type": "TwitchModerationElement" "type": "TwitchModerationElement"
}, },
{
"emote": {
"homePage": "https://link.twitch.tv/SharedChatViewer",
"images": {
"1x": ""
},
"name": "",
"tooltip": "Shared Message"
},
"flags": "BadgeSharedChannel",
"link": {
"type": "None",
"value": ""
},
"tooltip": "Shared Message",
"trailingSpace": true,
"type": "BadgeElement"
},
{ {
"emote": { "emote": {
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge", "homePage": "https://www.twitch.tv/jobs?ref=chat_badge",

View file

@ -64,6 +64,24 @@
"trailingSpace": true, "trailingSpace": true,
"type": "TwitchModerationElement" "type": "TwitchModerationElement"
}, },
{
"emote": {
"homePage": "https://link.twitch.tv/SharedChatViewer",
"images": {
"1x": ""
},
"name": "",
"tooltip": "Shared Message from twitchdev"
},
"flags": "BadgeSharedChannel",
"link": {
"type": "None",
"value": ""
},
"tooltip": "Shared Message from twitchdev",
"trailingSpace": true,
"type": "BadgeElement"
},
{ {
"color": "#ffff0000", "color": "#ffff0000",
"flags": "Username", "flags": "Username",

View file

@ -64,6 +64,24 @@
"trailingSpace": true, "trailingSpace": true,
"type": "TwitchModerationElement" "type": "TwitchModerationElement"
}, },
{
"emote": {
"homePage": "https://link.twitch.tv/SharedChatViewer",
"images": {
"1x": ""
},
"name": "",
"tooltip": "Shared Message from twitchdev"
},
"flags": "BadgeSharedChannel",
"link": {
"type": "None",
"value": ""
},
"tooltip": "Shared Message from twitchdev",
"trailingSpace": true,
"type": "BadgeElement"
},
{ {
"emote": { "emote": {
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge", "homePage": "https://www.twitch.tv/jobs?ref=chat_badge",

View file

@ -64,6 +64,24 @@
"trailingSpace": true, "trailingSpace": true,
"type": "TwitchModerationElement" "type": "TwitchModerationElement"
}, },
{
"emote": {
"homePage": "https://link.twitch.tv/SharedChatViewer",
"images": {
"1x": ""
},
"name": "",
"tooltip": "Shared Message"
},
"flags": "BadgeSharedChannel",
"link": {
"type": "None",
"value": ""
},
"tooltip": "Shared Message",
"trailingSpace": true,
"type": "BadgeElement"
},
{ {
"emote": { "emote": {
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge", "homePage": "https://www.twitch.tv/jobs?ref=chat_badge",