mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
added category navigation to general settings
This commit is contained in:
parent
4d676b4c51
commit
0ecea8ad83
9 changed files with 512 additions and 397 deletions
|
@ -224,6 +224,7 @@ SOURCES += \
|
|||
src/util/IncognitoBrowser.cpp \
|
||||
src/util/InitUpdateButton.cpp \
|
||||
src/util/JsonQuery.cpp \
|
||||
src/util/LayoutHelper.cpp \
|
||||
src/util/NuulsUploader.cpp \
|
||||
src/util/RapidjsonHelpers.cpp \
|
||||
src/util/StreamerMode.cpp \
|
||||
|
@ -283,6 +284,7 @@ SOURCES += \
|
|||
src/widgets/settingspages/ExternalToolsPage.cpp \
|
||||
src/widgets/settingspages/FiltersPage.cpp \
|
||||
src/widgets/settingspages/GeneralPage.cpp \
|
||||
src/widgets/settingspages/GeneralPageView.cpp \
|
||||
src/widgets/settingspages/HighlightingPage.cpp \
|
||||
src/widgets/settingspages/IgnoresPage.cpp \
|
||||
src/widgets/settingspages/KeyboardSettingsPage.cpp \
|
||||
|
@ -534,6 +536,7 @@ HEADERS += \
|
|||
src/widgets/settingspages/ExternalToolsPage.hpp \
|
||||
src/widgets/settingspages/FiltersPage.hpp \
|
||||
src/widgets/settingspages/GeneralPage.hpp \
|
||||
src/widgets/settingspages/GeneralPageView.hpp \
|
||||
src/widgets/settingspages/HighlightingPage.hpp \
|
||||
src/widgets/settingspages/IgnoresPage.hpp \
|
||||
src/widgets/settingspages/KeyboardSettingsPage.hpp \
|
||||
|
|
|
@ -16,7 +16,6 @@ QScrollArea {
|
|||
}
|
||||
|
||||
QScrollArea QFrame {
|
||||
background: #222;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
@ -27,8 +26,6 @@ QComboBox QFrame {
|
|||
|
||||
chatterino--SettingsPage {
|
||||
background: #222;
|
||||
/*border: 1px solid #555;
|
||||
border-left: none;*/
|
||||
}
|
||||
|
||||
chatterino--PageHeader {
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <QLayout>
|
||||
#include <QWidget>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
class QWidget;
|
||||
class QScrollArea;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
using LayoutItem = boost::variant<QWidget *, QLayout *>;
|
||||
|
||||
QWidget *wrapLayout(QLayout *layout);
|
||||
QScrollArea *makeScrollArea(LayoutItem item);
|
||||
|
||||
template <typename T>
|
||||
T *makeLayout(std::initializer_list<LayoutItem> items)
|
||||
{
|
||||
|
@ -26,9 +31,19 @@ T *makeLayout(std::initializer_list<LayoutItem> items)
|
|||
}
|
||||
}
|
||||
|
||||
t->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *makeStretchingLayout(std::initializer_list<LayoutItem> items)
|
||||
{
|
||||
auto layout = makeLayout<T>(items);
|
||||
layout->addStretch(1);
|
||||
return layout;
|
||||
}
|
||||
|
||||
template <typename T, typename With>
|
||||
T *makeWidget(With with)
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@ void SignalLabel::mousePressEvent(QMouseEvent *event)
|
|||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
emit mouseDown();
|
||||
emit leftMouseDown();
|
||||
}
|
||||
|
||||
event->ignore();
|
||||
|
@ -26,7 +26,7 @@ void SignalLabel::mouseReleaseEvent(QMouseEvent *event)
|
|||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
emit mouseUp();
|
||||
emit leftMouseUp();
|
||||
}
|
||||
|
||||
event->ignore();
|
||||
|
|
|
@ -18,8 +18,8 @@ public:
|
|||
signals:
|
||||
void mouseDoubleClick(QMouseEvent *ev);
|
||||
|
||||
void mouseDown();
|
||||
void mouseUp();
|
||||
void leftMouseDown();
|
||||
void leftMouseUp();
|
||||
void mouseMove(QMouseEvent *event);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
#include "util/IncognitoBrowser.hpp"
|
||||
#include "util/StreamerMode.hpp"
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
#include "widgets/dialogs/ColorPickerDialog.hpp"
|
||||
#include "widgets/helper/ColorButton.hpp"
|
||||
#include "widgets/helper/Line.hpp"
|
||||
#include "widgets/settingspages/GeneralPageView.hpp"
|
||||
|
||||
#define CHROME_EXTENSION_LINK \
|
||||
"https://chrome.google.com/webstore/detail/chatterino-native-host/" \
|
||||
|
@ -36,7 +35,7 @@
|
|||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
void addKeyboardModifierSetting(SettingsLayout &layout,
|
||||
void addKeyboardModifierSetting(GeneralPageView &layout,
|
||||
const QString &title,
|
||||
EnumSetting<Qt::KeyboardModifier> &setting)
|
||||
{
|
||||
|
@ -76,239 +75,32 @@ namespace {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
TitleLabel *SettingsLayout::addTitle(const QString &title)
|
||||
{
|
||||
// space
|
||||
if (!this->groups_.empty())
|
||||
this->addWidget(this->groups_.back().space = new Space);
|
||||
|
||||
// title
|
||||
auto label = new TitleLabel(title + ":");
|
||||
this->addWidget(label);
|
||||
|
||||
// groups
|
||||
this->groups_.push_back(Group{title, label, nullptr, {}});
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
QCheckBox *SettingsLayout::addCheckbox(const QString &text,
|
||||
BoolSetting &setting, bool inverse)
|
||||
{
|
||||
auto check = new QCheckBox(text);
|
||||
|
||||
// update when setting changes
|
||||
setting.connect(
|
||||
[inverse, check](const bool &value, auto) {
|
||||
check->setChecked(inverse ^ value);
|
||||
},
|
||||
this->managedConnections_);
|
||||
|
||||
// update setting on toggle
|
||||
QObject::connect(
|
||||
check, &QCheckBox::toggled, this,
|
||||
[&setting, inverse](bool state) { setting = inverse ^ state; });
|
||||
|
||||
this->addWidget(check);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({check, {text}});
|
||||
|
||||
return check;
|
||||
}
|
||||
|
||||
ComboBox *SettingsLayout::addDropdown(const QString &text,
|
||||
const QStringList &list)
|
||||
{
|
||||
auto layout = new QHBoxLayout;
|
||||
auto combo = new ComboBox;
|
||||
combo->setFocusPolicy(Qt::StrongFocus);
|
||||
combo->addItems(list);
|
||||
|
||||
auto label = new QLabel(text + ":");
|
||||
layout->addWidget(label);
|
||||
layout->addStretch(1);
|
||||
layout->addWidget(combo);
|
||||
|
||||
this->addLayout(layout);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({combo, {text}});
|
||||
this->groups_.back().widgets.push_back({label, {text}});
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
ComboBox *SettingsLayout::addDropdown(
|
||||
const QString &text, const QStringList &items,
|
||||
pajlada::Settings::Setting<QString> &setting, bool editable)
|
||||
{
|
||||
auto combo = this->addDropdown(text, items);
|
||||
|
||||
if (editable)
|
||||
combo->setEditable(true);
|
||||
|
||||
// update when setting changes
|
||||
setting.connect(
|
||||
[combo](const QString &value, auto) { combo->setCurrentText(value); },
|
||||
this->managedConnections_);
|
||||
|
||||
QObject::connect(combo, &QComboBox::currentTextChanged,
|
||||
[&setting](const QString &newValue) {
|
||||
setting = newValue;
|
||||
getApp()->windows->forceLayoutChannelViews();
|
||||
});
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
ColorButton *SettingsLayout::addColorButton(
|
||||
const QString &text, const QColor &color,
|
||||
pajlada::Settings::Setting<QString> &setting)
|
||||
{
|
||||
auto colorButton = new ColorButton(color);
|
||||
auto layout = new QHBoxLayout();
|
||||
auto label = new QLabel(text + ":");
|
||||
layout->addWidget(label);
|
||||
layout->addStretch(1);
|
||||
layout->addWidget(colorButton);
|
||||
this->addLayout(layout);
|
||||
QObject::connect(
|
||||
colorButton, &ColorButton::clicked, [&setting, colorButton]() {
|
||||
auto dialog = new ColorPickerDialog(QColor(setting));
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->show();
|
||||
dialog->closed.connect([&setting, colorButton, &dialog] {
|
||||
QColor selected = dialog->selectedColor();
|
||||
|
||||
if (selected.isValid())
|
||||
{
|
||||
setting = selected.name(QColor::HexArgb);
|
||||
colorButton->setColor(selected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this->groups_.back().widgets.push_back({label, {text}});
|
||||
this->groups_.back().widgets.push_back({colorButton, {text}});
|
||||
|
||||
return colorButton;
|
||||
}
|
||||
|
||||
DescriptionLabel *SettingsLayout::addDescription(const QString &text)
|
||||
{
|
||||
auto label = new DescriptionLabel(text);
|
||||
|
||||
label->setTextInteractionFlags(Qt::TextBrowserInteraction |
|
||||
Qt::LinksAccessibleByKeyboard);
|
||||
label->setOpenExternalLinks(true);
|
||||
label->setWordWrap(true);
|
||||
|
||||
this->addWidget(label);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({label, {text}});
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
void SettingsLayout::addSeperator()
|
||||
{
|
||||
this->addWidget(new Line(false));
|
||||
}
|
||||
|
||||
bool SettingsLayout::filterElements(const QString &query)
|
||||
{
|
||||
bool any{};
|
||||
|
||||
for (auto &&group : this->groups_)
|
||||
{
|
||||
// if a description in a group matches `query` then show the entire group
|
||||
bool descriptionMatches{};
|
||||
for (auto &&widget : group.widgets)
|
||||
{
|
||||
if (auto x = dynamic_cast<DescriptionLabel *>(widget.element); x)
|
||||
{
|
||||
if (x->text().contains(query, Qt::CaseInsensitive))
|
||||
{
|
||||
descriptionMatches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if group name matches then all should be visible
|
||||
if (group.name.contains(query, Qt::CaseInsensitive) ||
|
||||
descriptionMatches)
|
||||
{
|
||||
for (auto &&widget : group.widgets)
|
||||
widget.element->show();
|
||||
group.title->show();
|
||||
any = true;
|
||||
}
|
||||
// check if any match
|
||||
else
|
||||
{
|
||||
auto groupAny = false;
|
||||
|
||||
for (auto &&widget : group.widgets)
|
||||
{
|
||||
for (auto &&keyword : widget.keywords)
|
||||
{
|
||||
if (keyword.contains(query, Qt::CaseInsensitive))
|
||||
{
|
||||
widget.element->show();
|
||||
groupAny = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
widget.element->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (group.space)
|
||||
group.space->setVisible(groupAny);
|
||||
group.title->setVisible(groupAny);
|
||||
any |= groupAny;
|
||||
}
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
GeneralPage::GeneralPage()
|
||||
{
|
||||
auto y = new QVBoxLayout;
|
||||
auto scroll = new QScrollArea;
|
||||
scroll->setWidgetResizable(true);
|
||||
y->addWidget(scroll);
|
||||
auto x = new QHBoxLayout;
|
||||
auto layout = new SettingsLayout;
|
||||
this->settingsLayout_ = layout;
|
||||
x->addLayout(layout, 0);
|
||||
x->addStretch(1);
|
||||
auto view = new GeneralPageView;
|
||||
this->view_ = view;
|
||||
x->addWidget(view);
|
||||
auto z = new QFrame;
|
||||
z->setLayout(x);
|
||||
scroll->setWidget(z);
|
||||
y->addWidget(z);
|
||||
this->setLayout(y);
|
||||
|
||||
this->initLayout(*layout);
|
||||
|
||||
layout->addStretch(1);
|
||||
this->initLayout(*view);
|
||||
|
||||
this->initExtra();
|
||||
}
|
||||
|
||||
bool GeneralPage::filterElements(const QString &query)
|
||||
{
|
||||
if (this->settingsLayout_)
|
||||
return this->settingsLayout_->filterElements(query) || query.isEmpty();
|
||||
if (this->view_)
|
||||
return this->view_->filterElements(query) || query.isEmpty();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void GeneralPage::initLayout(SettingsLayout &layout)
|
||||
void GeneralPage::initLayout(GeneralPageView &layout)
|
||||
{
|
||||
auto &s = *getSettings();
|
||||
|
||||
|
|
|
@ -1,15 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "boost/variant.hpp"
|
||||
#include "pajlada/signals/signal.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "widgets/helper/ColorButton.hpp"
|
||||
#include "widgets/settingspages/SettingsPage.hpp"
|
||||
|
||||
class QLabel;
|
||||
|
@ -18,165 +8,9 @@ class QComboBox;
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
class Space : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
class TitleLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TitleLabel(const QString &text)
|
||||
: QLabel(text)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class DescriptionLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DescriptionLabel(const QString &text)
|
||||
: QLabel(text)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct DropdownArgs {
|
||||
QString value;
|
||||
int index;
|
||||
QComboBox *combobox;
|
||||
};
|
||||
|
||||
class ComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
void wheelEvent(QWheelEvent *event) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class SettingsLayout : public QVBoxLayout
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TitleLabel *addTitle(const QString &text);
|
||||
/// @param inverse Inverses true to false and vice versa
|
||||
QCheckBox *addCheckbox(const QString &text, BoolSetting &setting,
|
||||
bool inverse = false);
|
||||
ComboBox *addDropdown(const QString &text, const QStringList &items);
|
||||
ComboBox *addDropdown(const QString &text, const QStringList &items,
|
||||
pajlada::Settings::Setting<QString> &setting,
|
||||
bool editable = false);
|
||||
ColorButton *addColorButton(const QString &text, const QColor &color,
|
||||
pajlada::Settings::Setting<QString> &setting);
|
||||
|
||||
template <typename OnClick>
|
||||
QPushButton *makeButton(const QString &text, OnClick onClick)
|
||||
{
|
||||
auto button = new QPushButton(text);
|
||||
this->groups_.back().widgets.push_back({button, {text}});
|
||||
QObject::connect(button, &QPushButton::clicked, onClick);
|
||||
return button;
|
||||
}
|
||||
|
||||
template <typename OnClick>
|
||||
QPushButton *addButton(const QString &text, OnClick onClick)
|
||||
{
|
||||
auto button = makeButton(text, onClick);
|
||||
auto layout = new QHBoxLayout();
|
||||
layout->addWidget(button);
|
||||
layout->addStretch(1);
|
||||
this->addLayout(layout);
|
||||
return button;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ComboBox *addDropdown(
|
||||
const QString &text, const QStringList &items,
|
||||
pajlada::Settings::Setting<T> &setting,
|
||||
std::function<boost::variant<int, QString>(T)> getValue,
|
||||
std::function<T(DropdownArgs)> setValue, bool editable = true)
|
||||
{
|
||||
auto items2 = items;
|
||||
auto selected = getValue(setting.getValue());
|
||||
|
||||
if (selected.which() == 1)
|
||||
{
|
||||
// QString
|
||||
if (!editable && !items2.contains(boost::get<QString>(selected)))
|
||||
items2.insert(0, boost::get<QString>(selected));
|
||||
}
|
||||
|
||||
auto combo = this->addDropdown(text, items2);
|
||||
if (editable)
|
||||
combo->setEditable(true);
|
||||
|
||||
if (selected.which() == 0)
|
||||
{
|
||||
// int
|
||||
auto value = boost::get<int>(selected);
|
||||
if (value >= 0 && value < items2.size())
|
||||
combo->setCurrentIndex(value);
|
||||
}
|
||||
else if (selected.which() == 1)
|
||||
{
|
||||
// QString
|
||||
combo->setEditText(boost::get<QString>(selected));
|
||||
}
|
||||
|
||||
setting.connect(
|
||||
[getValue = std::move(getValue), combo](const T &value, auto) {
|
||||
auto var = getValue(value);
|
||||
if (var.which() == 0)
|
||||
combo->setCurrentIndex(boost::get<int>(var));
|
||||
else
|
||||
{
|
||||
combo->setCurrentText(boost::get<QString>(var));
|
||||
combo->setEditText(boost::get<QString>(var));
|
||||
}
|
||||
},
|
||||
this->managedConnections_);
|
||||
|
||||
QObject::connect(
|
||||
combo,
|
||||
QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
|
||||
// &QComboBox::editTextChanged,
|
||||
[combo, &setting,
|
||||
setValue = std::move(setValue)](const QString &newValue) {
|
||||
setting = setValue(
|
||||
DropdownArgs{newValue, combo->currentIndex(), combo});
|
||||
getApp()->windows->forceLayoutChannelViews();
|
||||
});
|
||||
|
||||
return combo;
|
||||
}
|
||||
DescriptionLabel *addDescription(const QString &text);
|
||||
|
||||
void addSeperator();
|
||||
bool filterElements(const QString &query);
|
||||
|
||||
private:
|
||||
struct Widget {
|
||||
QWidget *element;
|
||||
QStringList keywords;
|
||||
};
|
||||
|
||||
struct Group {
|
||||
QString name;
|
||||
QWidget *title{};
|
||||
Space *space{};
|
||||
std::vector<Widget> widgets;
|
||||
};
|
||||
|
||||
std::vector<Group> groups_;
|
||||
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
|
||||
};
|
||||
class GeneralPageView;
|
||||
class DescriptionLabel;
|
||||
struct DropdownArgs;
|
||||
|
||||
class GeneralPage : public SettingsPage
|
||||
{
|
||||
|
@ -188,13 +22,13 @@ public:
|
|||
bool filterElements(const QString &query);
|
||||
|
||||
private:
|
||||
void initLayout(SettingsLayout &layout);
|
||||
void initLayout(GeneralPageView &layout);
|
||||
void initExtra();
|
||||
|
||||
QString getFont(const DropdownArgs &args) const;
|
||||
|
||||
DescriptionLabel *cachePath_{};
|
||||
SettingsLayout *settingsLayout_{};
|
||||
GeneralPageView *view_{};
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
270
src/widgets/settingspages/GeneralPageView.cpp
Normal file
270
src/widgets/settingspages/GeneralPageView.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
#include "GeneralPageView.hpp"
|
||||
#include <QScrollBar>
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/LayoutHelper.hpp"
|
||||
#include "widgets/dialogs/ColorPickerDialog.hpp"
|
||||
#include "widgets/helper/ColorButton.hpp"
|
||||
#include "widgets/helper/Line.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
GeneralPageView::GeneralPageView(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
auto scrollArea = this->contentScrollArea_ =
|
||||
makeScrollArea(this->contentLayout_ = new QVBoxLayout);
|
||||
|
||||
auto navigation =
|
||||
wrapLayout(this->navigationLayout_ = makeLayout<QVBoxLayout>({}));
|
||||
|
||||
this->setLayout(makeLayout<QHBoxLayout>({scrollArea, navigation}));
|
||||
|
||||
QObject::connect(scrollArea->verticalScrollBar(), &QScrollBar::valueChanged,
|
||||
this, [=] { this->updateNavigationHighlighting(); });
|
||||
}
|
||||
|
||||
void GeneralPageView::addWidget(QWidget *widget)
|
||||
{
|
||||
this->contentLayout_->addWidget(widget);
|
||||
}
|
||||
|
||||
void GeneralPageView::addLayout(QLayout *layout)
|
||||
{
|
||||
this->contentLayout_->addLayout(layout);
|
||||
}
|
||||
|
||||
TitleLabel *GeneralPageView::addTitle(const QString &title)
|
||||
{
|
||||
// space
|
||||
if (!this->groups_.empty())
|
||||
this->addWidget(this->groups_.back().space = new Space);
|
||||
|
||||
// title
|
||||
auto label = new TitleLabel(title + ":");
|
||||
this->addWidget(label);
|
||||
|
||||
// navigation item
|
||||
auto navLabel = new NavigationLabel(title);
|
||||
navLabel->setCursor(Qt::PointingHandCursor);
|
||||
this->navigationLayout_->addWidget(navLabel);
|
||||
|
||||
QObject::connect(navLabel, &NavigationLabel::leftMouseUp, label, [=] {
|
||||
this->contentScrollArea_->verticalScrollBar()->setValue(label->y());
|
||||
});
|
||||
|
||||
// groups
|
||||
this->groups_.push_back(Group{title, label, navLabel, nullptr, {}});
|
||||
|
||||
if (this->groups_.size() == 1)
|
||||
this->updateNavigationHighlighting();
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
QCheckBox *GeneralPageView::addCheckbox(const QString &text,
|
||||
BoolSetting &setting, bool inverse)
|
||||
{
|
||||
auto check = new QCheckBox(text);
|
||||
|
||||
// update when setting changes
|
||||
setting.connect(
|
||||
[inverse, check](const bool &value, auto) {
|
||||
check->setChecked(inverse ^ value);
|
||||
},
|
||||
this->managedConnections_);
|
||||
|
||||
// update setting on toggle
|
||||
QObject::connect(
|
||||
check, &QCheckBox::toggled, this,
|
||||
[&setting, inverse](bool state) { setting = inverse ^ state; });
|
||||
|
||||
this->addWidget(check);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({check, {text}});
|
||||
|
||||
return check;
|
||||
}
|
||||
|
||||
ComboBox *GeneralPageView::addDropdown(const QString &text,
|
||||
const QStringList &list)
|
||||
{
|
||||
auto layout = new QHBoxLayout;
|
||||
auto combo = new ComboBox;
|
||||
combo->setFocusPolicy(Qt::StrongFocus);
|
||||
combo->addItems(list);
|
||||
|
||||
auto label = new QLabel(text + ":");
|
||||
layout->addWidget(label);
|
||||
layout->addStretch(1);
|
||||
layout->addWidget(combo);
|
||||
|
||||
this->addLayout(layout);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({combo, {text}});
|
||||
this->groups_.back().widgets.push_back({label, {text}});
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
ComboBox *GeneralPageView::addDropdown(
|
||||
const QString &text, const QStringList &items,
|
||||
pajlada::Settings::Setting<QString> &setting, bool editable)
|
||||
{
|
||||
auto combo = this->addDropdown(text, items);
|
||||
|
||||
if (editable)
|
||||
combo->setEditable(true);
|
||||
|
||||
// update when setting changes
|
||||
setting.connect(
|
||||
[combo](const QString &value, auto) { combo->setCurrentText(value); },
|
||||
this->managedConnections_);
|
||||
|
||||
QObject::connect(combo, &QComboBox::currentTextChanged,
|
||||
[&setting](const QString &newValue) {
|
||||
setting = newValue;
|
||||
getApp()->windows->forceLayoutChannelViews();
|
||||
});
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
ColorButton *GeneralPageView::addColorButton(
|
||||
const QString &text, const QColor &color,
|
||||
pajlada::Settings::Setting<QString> &setting)
|
||||
{
|
||||
auto colorButton = new ColorButton(color);
|
||||
auto layout = new QHBoxLayout();
|
||||
auto label = new QLabel(text + ":");
|
||||
layout->addWidget(label);
|
||||
layout->addStretch(1);
|
||||
layout->addWidget(colorButton);
|
||||
this->addLayout(layout);
|
||||
QObject::connect(
|
||||
colorButton, &ColorButton::clicked, [&setting, colorButton]() {
|
||||
auto dialog = new ColorPickerDialog(QColor(setting));
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->show();
|
||||
dialog->closed.connect([&setting, colorButton, &dialog] {
|
||||
QColor selected = dialog->selectedColor();
|
||||
|
||||
if (selected.isValid())
|
||||
{
|
||||
setting = selected.name(QColor::HexArgb);
|
||||
colorButton->setColor(selected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this->groups_.back().widgets.push_back({label, {text}});
|
||||
this->groups_.back().widgets.push_back({colorButton, {text}});
|
||||
|
||||
return colorButton;
|
||||
}
|
||||
|
||||
DescriptionLabel *GeneralPageView::addDescription(const QString &text)
|
||||
{
|
||||
auto label = new DescriptionLabel(text);
|
||||
|
||||
label->setTextInteractionFlags(Qt::TextBrowserInteraction |
|
||||
Qt::LinksAccessibleByKeyboard);
|
||||
label->setOpenExternalLinks(true);
|
||||
label->setWordWrap(true);
|
||||
|
||||
this->addWidget(label);
|
||||
|
||||
// groups
|
||||
this->groups_.back().widgets.push_back({label, {text}});
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
void GeneralPageView::addSeperator()
|
||||
{
|
||||
this->addWidget(new Line(false));
|
||||
}
|
||||
|
||||
bool GeneralPageView::filterElements(const QString &query)
|
||||
{
|
||||
bool any{};
|
||||
|
||||
for (auto &&group : this->groups_)
|
||||
{
|
||||
// if a description in a group matches `query` then show the entire group
|
||||
bool descriptionMatches{};
|
||||
for (auto &&widget : group.widgets)
|
||||
{
|
||||
if (auto x = dynamic_cast<DescriptionLabel *>(widget.element); x)
|
||||
{
|
||||
if (x->text().contains(query, Qt::CaseInsensitive))
|
||||
{
|
||||
descriptionMatches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if group name matches then all should be visible
|
||||
if (group.name.contains(query, Qt::CaseInsensitive) ||
|
||||
descriptionMatches)
|
||||
{
|
||||
for (auto &&widget : group.widgets)
|
||||
widget.element->show();
|
||||
group.title->show();
|
||||
any = true;
|
||||
}
|
||||
// check if any match
|
||||
else
|
||||
{
|
||||
auto groupAny = false;
|
||||
|
||||
for (auto &&widget : group.widgets)
|
||||
{
|
||||
for (auto &&keyword : widget.keywords)
|
||||
{
|
||||
if (keyword.contains(query, Qt::CaseInsensitive))
|
||||
{
|
||||
widget.element->show();
|
||||
groupAny = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
widget.element->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (group.space)
|
||||
group.space->setVisible(groupAny);
|
||||
group.title->setVisible(groupAny);
|
||||
any |= groupAny;
|
||||
}
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
void GeneralPageView::updateNavigationHighlighting()
|
||||
{
|
||||
auto scrollY = this->contentScrollArea_->verticalScrollBar()->value();
|
||||
auto first = true;
|
||||
|
||||
for (auto &&group : this->groups_)
|
||||
{
|
||||
if (first && (group.title->geometry().bottom() > scrollY ||
|
||||
&group == &this->groups_.back()))
|
||||
{
|
||||
first = false;
|
||||
group.navigationLink->setStyleSheet("color: #4FC3F7");
|
||||
}
|
||||
else
|
||||
{
|
||||
group.navigationLink->setStyleSheet("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
204
src/widgets/settingspages/GeneralPageView.hpp
Normal file
204
src/widgets/settingspages/GeneralPageView.hpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDebug>
|
||||
#include <boost/variant.hpp>
|
||||
#include "common/ChatterinoSetting.hpp"
|
||||
#include "widgets/helper/SignalLabel.hpp"
|
||||
|
||||
class QScrollArea;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class ColorButton;
|
||||
|
||||
class Space : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
class TitleLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TitleLabel(const QString &text)
|
||||
: QLabel(text)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class NavigationLabel : public SignalLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NavigationLabel(const QString &text)
|
||||
: SignalLabel()
|
||||
{
|
||||
this->setText(text);
|
||||
}
|
||||
};
|
||||
|
||||
class DescriptionLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DescriptionLabel(const QString &text)
|
||||
: QLabel(text)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
void wheelEvent(QWheelEvent *event) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct DropdownArgs {
|
||||
QString value;
|
||||
int index;
|
||||
QComboBox *combobox;
|
||||
};
|
||||
|
||||
class GeneralPageView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GeneralPageView(QWidget *parent = nullptr);
|
||||
|
||||
void addWidget(QWidget *widget);
|
||||
void addLayout(QLayout *layout);
|
||||
|
||||
TitleLabel *addTitle(const QString &text);
|
||||
/// @param inverse Inverses true to false and vice versa
|
||||
QCheckBox *addCheckbox(const QString &text, BoolSetting &setting,
|
||||
bool inverse = false);
|
||||
ComboBox *addDropdown(const QString &text, const QStringList &items);
|
||||
ComboBox *addDropdown(const QString &text, const QStringList &items,
|
||||
pajlada::Settings::Setting<QString> &setting,
|
||||
bool editable = false);
|
||||
ColorButton *addColorButton(const QString &text, const QColor &color,
|
||||
pajlada::Settings::Setting<QString> &setting);
|
||||
|
||||
template <typename OnClick>
|
||||
QPushButton *makeButton(const QString &text, OnClick onClick)
|
||||
{
|
||||
auto button = new QPushButton(text);
|
||||
this->groups_.back().widgets.push_back({button, {text}});
|
||||
QObject::connect(button, &QPushButton::clicked, onClick);
|
||||
return button;
|
||||
}
|
||||
|
||||
template <typename OnClick>
|
||||
QPushButton *addButton(const QString &text, OnClick onClick)
|
||||
{
|
||||
auto button = makeButton(text, onClick);
|
||||
auto layout = new QHBoxLayout();
|
||||
layout->addWidget(button);
|
||||
layout->addStretch(1);
|
||||
this->addLayout(layout);
|
||||
return button;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ComboBox *addDropdown(
|
||||
const QString &text, const QStringList &items,
|
||||
pajlada::Settings::Setting<T> &setting,
|
||||
std::function<boost::variant<int, QString>(T)> getValue,
|
||||
std::function<T(DropdownArgs)> setValue, bool editable = true)
|
||||
{
|
||||
auto items2 = items;
|
||||
auto selected = getValue(setting.getValue());
|
||||
|
||||
if (selected.which() == 1)
|
||||
{
|
||||
// QString
|
||||
if (!editable && !items2.contains(boost::get<QString>(selected)))
|
||||
items2.insert(0, boost::get<QString>(selected));
|
||||
}
|
||||
|
||||
auto combo = this->addDropdown(text, items2);
|
||||
if (editable)
|
||||
combo->setEditable(true);
|
||||
|
||||
if (selected.which() == 0)
|
||||
{
|
||||
// int
|
||||
auto value = boost::get<int>(selected);
|
||||
if (value >= 0 && value < items2.size())
|
||||
combo->setCurrentIndex(value);
|
||||
}
|
||||
else if (selected.which() == 1)
|
||||
{
|
||||
// QString
|
||||
combo->setEditText(boost::get<QString>(selected));
|
||||
}
|
||||
|
||||
setting.connect(
|
||||
[getValue = std::move(getValue), combo](const T &value, auto) {
|
||||
auto var = getValue(value);
|
||||
if (var.which() == 0)
|
||||
combo->setCurrentIndex(boost::get<int>(var));
|
||||
else
|
||||
{
|
||||
combo->setCurrentText(boost::get<QString>(var));
|
||||
combo->setEditText(boost::get<QString>(var));
|
||||
}
|
||||
},
|
||||
this->managedConnections_);
|
||||
|
||||
QObject::connect(
|
||||
combo,
|
||||
QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
|
||||
// &QComboBox::editTextChanged,
|
||||
[combo, &setting,
|
||||
setValue = std::move(setValue)](const QString &newValue) {
|
||||
setting = setValue(
|
||||
DropdownArgs{newValue, combo->currentIndex(), combo});
|
||||
getApp()->windows->forceLayoutChannelViews();
|
||||
});
|
||||
|
||||
return combo;
|
||||
}
|
||||
DescriptionLabel *addDescription(const QString &text);
|
||||
|
||||
void addSeperator();
|
||||
bool filterElements(const QString &query);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *ev) override
|
||||
{
|
||||
qDebug() << ev->size();
|
||||
}
|
||||
|
||||
private:
|
||||
void updateNavigationHighlighting();
|
||||
|
||||
struct Widget {
|
||||
QWidget *element;
|
||||
QStringList keywords;
|
||||
};
|
||||
|
||||
struct Group {
|
||||
QString name;
|
||||
QWidget *title{};
|
||||
QWidget *navigationLink{};
|
||||
Space *space{};
|
||||
std::vector<Widget> widgets;
|
||||
};
|
||||
|
||||
QScrollArea *contentScrollArea_;
|
||||
QVBoxLayout *contentLayout_;
|
||||
QVBoxLayout *navigationLayout_;
|
||||
|
||||
std::vector<Group> groups_;
|
||||
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
Loading…
Reference in a new issue