Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: experiment with a new setting widget builder #5585

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
- Dev: Refactor and document `Scrollbar`. (#5334, #5393)
- Dev: Refactor `TwitchIrcServer`, making it abstracted. (#5421, #5435)
- Dev: Reduced the amount of scale events. (#5404, #5406)
- Dev: Refactored settings widget creation. (#5585)
- Dev: Removed unused timegate settings. (#5361)
- Dev: Add `Channel::addSystemMessage` helper function, allowing us to avoid the common `channel->addMessage(makeSystemMessage(...));` pattern. (#5500)
- Dev: Unsingletonize `Resources2`. (#5460)
Expand Down
5 changes: 5 additions & 0 deletions resources/qss/settings.qss
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ chatterino--DescriptionLabel {
color: #999;
}

QLabel#description {
color: #999;
padding-left: 10px;
}

chatterino--NavigationLabel {
font-family: "Segoe UI light";
font-size: 15px;
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,8 @@ set(SOURCE_FILES
widgets/settingspages/PluginsPage.hpp
widgets/settingspages/SettingsPage.cpp
widgets/settingspages/SettingsPage.hpp
widgets/settingspages/SettingWidget.cpp
widgets/settingspages/SettingWidget.hpp

widgets/splits/ClosedSplits.cpp
widgets/splits/ClosedSplits.hpp
Expand Down
69 changes: 39 additions & 30 deletions src/widgets/settingspages/GeneralPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "util/IncognitoBrowser.hpp"
#include "widgets/BaseWindow.hpp"
#include "widgets/settingspages/GeneralPageView.hpp"
#include "widgets/settingspages/SettingWidget.hpp"

#include <magic_enum/magic_enum.hpp>
#include <QDesktopServices>
Expand Down Expand Up @@ -265,10 +266,13 @@ void GeneralPage::initLayout(GeneralPageView &layout)
},
false, "Choose which tabs are visible in the notebook");

layout.addCheckbox(
"Show message reply context", s.hideReplyContext, true,
"This setting will only affect how messages are shown. You can reply "
"to a message regardless of this setting.");
SettingWidget::inverseCheckbox("Show message reply context",
s.hideReplyContext)
->setTooltip(
"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,
"Show a reply button next to every chat message");

Expand Down Expand Up @@ -614,10 +618,19 @@ void GeneralPage::initLayout(GeneralPageView &layout)
"Google",
},
s.emojiSet);
layout.addCheckbox("Show BTTV global emotes", s.enableBTTVGlobalEmotes);
layout.addCheckbox("Show BTTV channel emotes", s.enableBTTVChannelEmotes);
layout.addCheckbox("Enable BTTV live emote updates (requires restart)",
s.enableBTTVLiveUpdates);
SettingWidget::checkbox("Show BetterTTV global emotes",
s.enableBTTVGlobalEmotes)
->addKeywords({"bttv"})
->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 channel emotes", s.enableFFZChannelEmotes);
layout.addCheckbox("Show 7TV global emotes", s.enableSevenTVGlobalEmotes);
Expand Down Expand Up @@ -1029,10 +1042,11 @@ void GeneralPage::initLayout(GeneralPageView &layout)
false,
"Make all clickable links lowercase to deter "
"phishing attempts.");
layout.addCheckbox(
"Show user's pronouns in user card", s.showPronouns, false,
"Shows users' pronouns in their user card. "
"Pronouns are retrieved from alejo.io when the user card is opened.");
SettingWidget::checkbox("Show user's pronouns in user card", s.showPronouns)
->setDescription(
R"(Pronouns are retrieved from <a href="https://pr.alejo.io">pr.alejo.io</a> when a user card is opened.)")
->addTo(layout);

layout.addCheckbox("Bold @usernames", s.boldUsernames, false,
"Bold @mentions to make them more noticable.");
layout.addCheckbox("Color @usernames", s.colorUsernames, false,
Expand Down Expand Up @@ -1157,25 +1171,20 @@ void GeneralPage::initLayout(GeneralPageView &layout)
"@mention for the related thread. If the reply context is hidden, "
"these mentions will never be stripped.");

layout.addDropdownEnumClass<ChatSendProtocol>(
"Chat send protocol", qmagicenum::enumNames<ChatSendProtocol>(),
s.chatSendProtocol,
"'Helix' will use Twitch's Helix API to send message. 'IRC' will use "
"IRC to send messages.",
{});
SettingWidget::dropdown("Chat send protocol", s.chatSendProtocol)
->setTooltip("'Helix' will use Twitch's Helix API to send message. "
"'IRC' will use IRC to send messages.")
->addTo(layout);

layout.addCheckbox(
"Show send message button", s.showSendButton, false,
"Show a Send button next to each split input that can be "
"clicked to send the message");

auto *soundBackend = layout.addDropdownEnumClass<SoundBackend>(
"Sound backend (requires restart)",
qmagicenum::enumNames<SoundBackend>(), s.soundBackend,
"Change this only if you're noticing issues with sound playback on "
"your system",
{});
soundBackend->setMinimumWidth(soundBackend->minimumSizeHint().width());
SettingWidget::checkbox("Show send message button", s.showSendButton)
->setTooltip("Show a Send button next to each split input that can be "
"clicked to send the message")
->addTo(layout);

SettingWidget::dropdown("Sound backend (requires restart)", s.soundBackend)
->setTooltip("Change this only if you're noticing issues "
"with sound playback on your system")
->addTo(layout);

layout.addStretch();

Expand Down
17 changes: 12 additions & 5 deletions src/widgets/settingspages/GeneralPageView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "widgets/dialogs/ColorPickerDialog.hpp"
#include "widgets/helper/color/ColorButton.hpp"
#include "widgets/helper/Line.hpp"
#include "widgets/settingspages/SettingWidget.hpp"

#include <QRegularExpression>
#include <QScrollArea>
Expand Down Expand Up @@ -44,9 +45,16 @@ GeneralPageView::GeneralPageView(QWidget *parent)
});
}

void GeneralPageView::addWidget(QWidget *widget)
void GeneralPageView::addWidget(QWidget *widget, QStringList keywords)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the parameter 'keywords' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]

src/widgets/settingspages/GeneralPageView.hpp:98:

-     void addWidget(QWidget *widget, QStringList keywords = {});
+     void addWidget(QWidget *widget, const QStringList& keywords = {});
Suggested change
void GeneralPageView::addWidget(QWidget *widget, QStringList keywords)
void GeneralPageView::addWidget(QWidget *widget, const QStringList& keywords)

{
this->contentLayout_->addWidget(widget);
if (!keywords.isEmpty())
{
this->groups_.back().widgets.push_back({
.element = widget,
.keywords = keywords,
});
}
}

void GeneralPageView::addLayout(QLayout *layout)
Expand Down Expand Up @@ -376,11 +384,10 @@ bool GeneralPageView::filterElements(const QString &query)
currentSubtitleVisible = true;
widget.element->show();
groupAny = true;
break;
}
else
{
widget.element->hide();
}

widget.element->hide();
}
}

Expand Down
47 changes: 2 additions & 45 deletions src/widgets/settingspages/GeneralPageView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class QScrollArea;
namespace chatterino {

class ColorButton;
class SettingWidget;

class Space : public QLabel
{
Expand Down Expand Up @@ -95,7 +96,7 @@ class GeneralPageView : public QWidget
public:
GeneralPageView(QWidget *parent = nullptr);

void addWidget(QWidget *widget);
void addWidget(QWidget *widget, QStringList keywords = {});
void addLayout(QLayout *layout);
void addStretch();

Expand Down Expand Up @@ -274,50 +275,6 @@ class GeneralPageView : public QWidget
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)
{
auto updateVisibility = [cb = std::move(cb), &setting, widget]() {
Expand Down
144 changes: 144 additions & 0 deletions src/widgets/settingspages/SettingWidget.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading