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/IncognitoBrowser.cpp \
|
||||||
src/util/InitUpdateButton.cpp \
|
src/util/InitUpdateButton.cpp \
|
||||||
src/util/JsonQuery.cpp \
|
src/util/JsonQuery.cpp \
|
||||||
|
src/util/LayoutHelper.cpp \
|
||||||
src/util/NuulsUploader.cpp \
|
src/util/NuulsUploader.cpp \
|
||||||
src/util/RapidjsonHelpers.cpp \
|
src/util/RapidjsonHelpers.cpp \
|
||||||
src/util/StreamerMode.cpp \
|
src/util/StreamerMode.cpp \
|
||||||
|
@ -283,6 +284,7 @@ SOURCES += \
|
||||||
src/widgets/settingspages/ExternalToolsPage.cpp \
|
src/widgets/settingspages/ExternalToolsPage.cpp \
|
||||||
src/widgets/settingspages/FiltersPage.cpp \
|
src/widgets/settingspages/FiltersPage.cpp \
|
||||||
src/widgets/settingspages/GeneralPage.cpp \
|
src/widgets/settingspages/GeneralPage.cpp \
|
||||||
|
src/widgets/settingspages/GeneralPageView.cpp \
|
||||||
src/widgets/settingspages/HighlightingPage.cpp \
|
src/widgets/settingspages/HighlightingPage.cpp \
|
||||||
src/widgets/settingspages/IgnoresPage.cpp \
|
src/widgets/settingspages/IgnoresPage.cpp \
|
||||||
src/widgets/settingspages/KeyboardSettingsPage.cpp \
|
src/widgets/settingspages/KeyboardSettingsPage.cpp \
|
||||||
|
@ -534,6 +536,7 @@ HEADERS += \
|
||||||
src/widgets/settingspages/ExternalToolsPage.hpp \
|
src/widgets/settingspages/ExternalToolsPage.hpp \
|
||||||
src/widgets/settingspages/FiltersPage.hpp \
|
src/widgets/settingspages/FiltersPage.hpp \
|
||||||
src/widgets/settingspages/GeneralPage.hpp \
|
src/widgets/settingspages/GeneralPage.hpp \
|
||||||
|
src/widgets/settingspages/GeneralPageView.hpp \
|
||||||
src/widgets/settingspages/HighlightingPage.hpp \
|
src/widgets/settingspages/HighlightingPage.hpp \
|
||||||
src/widgets/settingspages/IgnoresPage.hpp \
|
src/widgets/settingspages/IgnoresPage.hpp \
|
||||||
src/widgets/settingspages/KeyboardSettingsPage.hpp \
|
src/widgets/settingspages/KeyboardSettingsPage.hpp \
|
||||||
|
|
|
@ -16,7 +16,6 @@ QScrollArea {
|
||||||
}
|
}
|
||||||
|
|
||||||
QScrollArea QFrame {
|
QScrollArea QFrame {
|
||||||
background: #222;
|
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +26,6 @@ QComboBox QFrame {
|
||||||
|
|
||||||
chatterino--SettingsPage {
|
chatterino--SettingsPage {
|
||||||
background: #222;
|
background: #222;
|
||||||
/*border: 1px solid #555;
|
|
||||||
border-left: none;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chatterino--PageHeader {
|
chatterino--PageHeader {
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
#include <QWidget>
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
|
class QWidget;
|
||||||
|
class QScrollArea;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
using LayoutItem = boost::variant<QWidget *, QLayout *>;
|
using LayoutItem = boost::variant<QWidget *, QLayout *>;
|
||||||
|
|
||||||
|
QWidget *wrapLayout(QLayout *layout);
|
||||||
|
QScrollArea *makeScrollArea(LayoutItem item);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T *makeLayout(std::initializer_list<LayoutItem> items)
|
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;
|
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>
|
template <typename T, typename With>
|
||||||
T *makeWidget(With with)
|
T *makeWidget(With with)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,7 @@ void SignalLabel::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton)
|
if (event->button() == Qt::LeftButton)
|
||||||
{
|
{
|
||||||
emit mouseDown();
|
emit leftMouseDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
event->ignore();
|
event->ignore();
|
||||||
|
@ -26,7 +26,7 @@ void SignalLabel::mouseReleaseEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton)
|
if (event->button() == Qt::LeftButton)
|
||||||
{
|
{
|
||||||
emit mouseUp();
|
emit leftMouseUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
event->ignore();
|
event->ignore();
|
||||||
|
|
|
@ -18,8 +18,8 @@ public:
|
||||||
signals:
|
signals:
|
||||||
void mouseDoubleClick(QMouseEvent *ev);
|
void mouseDoubleClick(QMouseEvent *ev);
|
||||||
|
|
||||||
void mouseDown();
|
void leftMouseDown();
|
||||||
void mouseUp();
|
void leftMouseUp();
|
||||||
void mouseMove(QMouseEvent *event);
|
void mouseMove(QMouseEvent *event);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -15,9 +15,8 @@
|
||||||
#include "util/IncognitoBrowser.hpp"
|
#include "util/IncognitoBrowser.hpp"
|
||||||
#include "util/StreamerMode.hpp"
|
#include "util/StreamerMode.hpp"
|
||||||
#include "widgets/BaseWindow.hpp"
|
#include "widgets/BaseWindow.hpp"
|
||||||
#include "widgets/dialogs/ColorPickerDialog.hpp"
|
|
||||||
#include "widgets/helper/ColorButton.hpp"
|
|
||||||
#include "widgets/helper/Line.hpp"
|
#include "widgets/helper/Line.hpp"
|
||||||
|
#include "widgets/settingspages/GeneralPageView.hpp"
|
||||||
|
|
||||||
#define CHROME_EXTENSION_LINK \
|
#define CHROME_EXTENSION_LINK \
|
||||||
"https://chrome.google.com/webstore/detail/chatterino-native-host/" \
|
"https://chrome.google.com/webstore/detail/chatterino-native-host/" \
|
||||||
|
@ -36,7 +35,7 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace {
|
namespace {
|
||||||
void addKeyboardModifierSetting(SettingsLayout &layout,
|
void addKeyboardModifierSetting(GeneralPageView &layout,
|
||||||
const QString &title,
|
const QString &title,
|
||||||
EnumSetting<Qt::KeyboardModifier> &setting)
|
EnumSetting<Qt::KeyboardModifier> &setting)
|
||||||
{
|
{
|
||||||
|
@ -76,239 +75,32 @@ namespace {
|
||||||
}
|
}
|
||||||
} // 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()
|
GeneralPage::GeneralPage()
|
||||||
{
|
{
|
||||||
auto y = new QVBoxLayout;
|
auto y = new QVBoxLayout;
|
||||||
auto scroll = new QScrollArea;
|
|
||||||
scroll->setWidgetResizable(true);
|
|
||||||
y->addWidget(scroll);
|
|
||||||
auto x = new QHBoxLayout;
|
auto x = new QHBoxLayout;
|
||||||
auto layout = new SettingsLayout;
|
auto view = new GeneralPageView;
|
||||||
this->settingsLayout_ = layout;
|
this->view_ = view;
|
||||||
x->addLayout(layout, 0);
|
x->addWidget(view);
|
||||||
x->addStretch(1);
|
|
||||||
auto z = new QFrame;
|
auto z = new QFrame;
|
||||||
z->setLayout(x);
|
z->setLayout(x);
|
||||||
scroll->setWidget(z);
|
y->addWidget(z);
|
||||||
this->setLayout(y);
|
this->setLayout(y);
|
||||||
|
|
||||||
this->initLayout(*layout);
|
this->initLayout(*view);
|
||||||
|
|
||||||
layout->addStretch(1);
|
|
||||||
|
|
||||||
this->initExtra();
|
this->initExtra();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneralPage::filterElements(const QString &query)
|
bool GeneralPage::filterElements(const QString &query)
|
||||||
{
|
{
|
||||||
if (this->settingsLayout_)
|
if (this->view_)
|
||||||
return this->settingsLayout_->filterElements(query) || query.isEmpty();
|
return this->view_->filterElements(query) || query.isEmpty();
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralPage::initLayout(SettingsLayout &layout)
|
void GeneralPage::initLayout(GeneralPageView &layout)
|
||||||
{
|
{
|
||||||
auto &s = *getSettings();
|
auto &s = *getSettings();
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,5 @@
|
||||||
#pragma once
|
#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"
|
#include "widgets/settingspages/SettingsPage.hpp"
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
|
@ -18,165 +8,9 @@ class QComboBox;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class Space : public QLabel
|
class GeneralPageView;
|
||||||
{
|
class DescriptionLabel;
|
||||||
Q_OBJECT
|
struct DropdownArgs;
|
||||||
};
|
|
||||||
|
|
||||||
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 GeneralPage : public SettingsPage
|
class GeneralPage : public SettingsPage
|
||||||
{
|
{
|
||||||
|
@ -188,13 +22,13 @@ public:
|
||||||
bool filterElements(const QString &query);
|
bool filterElements(const QString &query);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initLayout(SettingsLayout &layout);
|
void initLayout(GeneralPageView &layout);
|
||||||
void initExtra();
|
void initExtra();
|
||||||
|
|
||||||
QString getFont(const DropdownArgs &args) const;
|
QString getFont(const DropdownArgs &args) const;
|
||||||
|
|
||||||
DescriptionLabel *cachePath_{};
|
DescriptionLabel *cachePath_{};
|
||||||
SettingsLayout *settingsLayout_{};
|
GeneralPageView *view_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // 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