25 changed files with 1024 additions and 100 deletions
@ -0,0 +1,39 @@ |
|||||
|
TARGET = QtMvvmSettingsWidgets |
||||
|
|
||||
|
QT = core gui mvvmsettingscore mvvmwidgets mvvmcore-private |
||||
|
|
||||
|
HEADERS += qtmvvmsettingswidgets_global.h \ |
||||
|
settingsdialog.h \ |
||||
|
settingsdialog_p.h |
||||
|
|
||||
|
SOURCES += \ |
||||
|
settingsdialog.cpp |
||||
|
|
||||
|
TRANSLATIONS += \ |
||||
|
translations/qtmvvmsettingswidgets_de.ts \ |
||||
|
translations/qtmvvmsettingswidgets_template.ts |
||||
|
|
||||
|
DISTFILES += $$TRANSLATIONS |
||||
|
|
||||
|
qpmx_ts_target.path = $$[QT_INSTALL_TRANSLATIONS] |
||||
|
qpmx_ts_target.depends += lrelease |
||||
|
|
||||
|
load(qt_module) |
||||
|
|
||||
|
win32 { |
||||
|
QMAKE_TARGET_PRODUCT = "$$TARGET" |
||||
|
QMAKE_TARGET_COMPANY = "Skycoder42" |
||||
|
QMAKE_TARGET_COPYRIGHT = "Felix Barz" |
||||
|
} else:mac { |
||||
|
QMAKE_TARGET_BUNDLE_PREFIX = "com.skycoder42." |
||||
|
} |
||||
|
|
||||
|
!ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.) |
||||
|
else: include($$OUT_PWD/qpmx_generated.pri) |
||||
|
|
||||
|
qpmx_ts_target.files -= $$OUT_PWD/$$QPMX_WORKINGDIR/qtmvvmsettingswidgets_template.qm |
||||
|
qpmx_ts_target.files += translations/qtmvvmsettingswidgets_template.ts |
||||
|
|
||||
|
FORMS += \ |
||||
|
settingsdialog.ui |
||||
|
|
@ -0,0 +1,14 @@ |
|||||
|
{ |
||||
|
"dependencies": [], |
||||
|
"license": { |
||||
|
"file": "", |
||||
|
"name": "" |
||||
|
}, |
||||
|
"prcFile": "", |
||||
|
"priFile": "", |
||||
|
"priIncludes": [ |
||||
|
], |
||||
|
"publishers": { |
||||
|
}, |
||||
|
"source": false |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
#ifndef QTMVVMSETTINGSWIDGETS_GLOBAL_H |
||||
|
#define QTMVVMSETTINGSWIDGETS_GLOBAL_H |
||||
|
|
||||
|
#include <QtCore/qglobal.h> |
||||
|
|
||||
|
#if defined(QT_BUILD_MVVMSETTINGSWIDGETS_LIB) |
||||
|
# define Q_MVVMSETTINGSWIDGETS_EXPORT Q_DECL_EXPORT |
||||
|
#else |
||||
|
# define Q_MVVMSETTINGSWIDGETS_EXPORT Q_DECL_IMPORT |
||||
|
#endif |
||||
|
|
||||
|
#endif // QTMVVMSETTINGSWIDGETS_GLOBAL_H
|
@ -0,0 +1,477 @@ |
|||||
|
#include "settingsdialog.h" |
||||
|
#include "settingsdialog_p.h" |
||||
|
#include "ui_settingsdialog.h" |
||||
|
|
||||
|
#include <QtCore/QMetaProperty> |
||||
|
#include <QtCore/QRegularExpression> |
||||
|
|
||||
|
#include <QtMvvmCore/CoreApp> |
||||
|
|
||||
|
#include <QtWidgets/QPushButton> |
||||
|
#include <QtWidgets/QScrollArea> |
||||
|
|
||||
|
#include <QtMvvmWidgets/WidgetsPresenter> |
||||
|
#include <QtMvvmWidgets/InputWidgetFactory> |
||||
|
|
||||
|
#include <QtMvvmCore/private/qtmvvm_logging_p.h> |
||||
|
|
||||
|
using namespace QtMvvm; |
||||
|
|
||||
|
SettingsDialog::SettingsDialog(ViewModel *viewModel, QWidget *parent) : |
||||
|
QDialog(parent), |
||||
|
d(new SettingsDialogPrivate(this, viewModel)) |
||||
|
{ |
||||
|
d->ui->setupUi(this); |
||||
|
//TODO ???
|
||||
|
// d->ui->buttonBox->button(QDialogButtonBox::Ok)->setAutoDefault(false);
|
||||
|
// d->ui->buttonBox->button(QDialogButtonBox::Cancel)->setAutoDefault(false);
|
||||
|
// d->ui->buttonBox->button(QDialogButtonBox::Apply)->setAutoDefault(false);
|
||||
|
// d->ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setAutoDefault(false);
|
||||
|
// d->ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
|
||||
|
connect(d->ui->buttonBox, &QDialogButtonBox::clicked, |
||||
|
d, &SettingsDialogPrivate::buttonBoxClicked); |
||||
|
connect(d->ui->filterLineEdit, &QLineEdit::textChanged, |
||||
|
d, &SettingsDialogPrivate::filterTextChanged); |
||||
|
|
||||
|
if(parentWidget()) { |
||||
|
setWindowModality(Qt::WindowModal); |
||||
|
setWindowFlags(Qt::Sheet | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint); |
||||
|
} else { |
||||
|
setWindowModality(Qt::ApplicationModal); |
||||
|
setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint); |
||||
|
} |
||||
|
|
||||
|
#ifdef Q_OS_OSX |
||||
|
auto font = d->ui->titleLabel->font(); |
||||
|
font.setPointSize(16); |
||||
|
d->ui->titleLabel->setFont(font); |
||||
|
#endif |
||||
|
|
||||
|
int listSpacing = d->calcSpacing(Qt::Vertical); |
||||
|
d->delegate = new CategoryItemDelegate(std::bind(&SettingsDialogPrivate::updateWidth, d, std::placeholders::_1), |
||||
|
d->ui->categoryListWidget->iconSize(), |
||||
|
qMax(qRound(listSpacing * (2./3.)), 1), |
||||
|
this); |
||||
|
d->ui->categoryListWidget->setItemDelegate(d->delegate); |
||||
|
d->ui->categoryListWidget->setSpacing(qMax(qRound(listSpacing / 3.), 1) - 1); |
||||
|
|
||||
|
int spacing = d->calcSpacing(Qt::Horizontal); |
||||
|
d->ui->contentLayout->setSpacing(spacing); |
||||
|
d->ui->categoryLineSpacer->changeSize(spacing, |
||||
|
0, |
||||
|
QSizePolicy::Fixed, |
||||
|
QSizePolicy::Fixed); |
||||
|
|
||||
|
connect(d->viewModel, &SettingsViewModel::beginLoadSetup, |
||||
|
d, &SettingsDialogPrivate::createUi); |
||||
|
} |
||||
|
|
||||
|
SettingsDialog::~SettingsDialog() {} |
||||
|
|
||||
|
QString SettingsDialog::labelFilterStyleSheet() const |
||||
|
{ |
||||
|
return QStringLiteral("QLabel {" |
||||
|
" background-color: rgba(19,232,51,0.4);" |
||||
|
" border: 1px solid rgba(19,196,45,0.8);" |
||||
|
" border-radius: 4px;" |
||||
|
"}"); |
||||
|
} |
||||
|
|
||||
|
QUrl SettingsDialog::iconOverwrite() const |
||||
|
{ |
||||
|
return QStringLiteral("qrc:/qtmvvm/icons/settings.ico"); |
||||
|
} |
||||
|
|
||||
|
// ------------- Private Implementation -------------
|
||||
|
|
||||
|
const QString SettingsDialogPrivate::TabContentId(QStringLiteral("__qtmvvm_settings_widgets_tab_content")); |
||||
|
|
||||
|
SettingsDialogPrivate::SettingsDialogPrivate(SettingsDialog *q_ptr, ViewModel *viewModel) : |
||||
|
QObject(q_ptr), |
||||
|
q(q_ptr), |
||||
|
viewModel(static_cast<SettingsViewModel*>(viewModel)), |
||||
|
ui(new Ui::SettingsDialog()), |
||||
|
delegate(nullptr), |
||||
|
maxWidthBase(0), |
||||
|
entryMap(), |
||||
|
changedEntries() |
||||
|
{} |
||||
|
|
||||
|
SettingsDialogPrivate::~SettingsDialogPrivate() {} |
||||
|
|
||||
|
void SettingsDialogPrivate::createUi() |
||||
|
{ |
||||
|
auto icoUrl = q->iconOverwrite(); |
||||
|
if(icoUrl.isValid()) |
||||
|
viewModel->settingsSetupLoader()->changeDefaultIcon(icoUrl); |
||||
|
auto setup = viewModel->loadSetup(QStringLiteral("widgets")); |
||||
|
|
||||
|
ui->filterLineEdit->setVisible(setup.allowSearch); |
||||
|
ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setVisible(setup.allowRestore && viewModel->canRestoreDefaults()); |
||||
|
|
||||
|
for(auto category : setup.categories) |
||||
|
createCategory(category); |
||||
|
|
||||
|
resetListSize(); |
||||
|
ui->categoryListWidget->setCurrentRow(0); |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::createCategory(const SettingsElements::Category &category) |
||||
|
{ |
||||
|
auto item = new QListWidgetItem(); |
||||
|
item->setText(category.title); |
||||
|
item->setIcon(loadIcon(category.icon)); |
||||
|
item->setToolTip(category.tooltip.isNull() ? category.title : category.tooltip); |
||||
|
item->setWhatsThis(item->toolTip()); |
||||
|
auto tab = new QTabWidget(ui->contentStackWidget); |
||||
|
tab->setTabBarAutoHide(true); |
||||
|
|
||||
|
ui->contentStackWidget->addWidget(tab); |
||||
|
ui->categoryListWidget->addItem(item); |
||||
|
|
||||
|
for(auto section : category.sections) |
||||
|
createSection(section, tab); |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::createSection(const SettingsElements::Section §ion, QTabWidget *tabWidget) |
||||
|
{ |
||||
|
auto scrollArea = new QScrollArea(tabWidget); |
||||
|
scrollArea->setWidgetResizable(true); |
||||
|
scrollArea->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); |
||||
|
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
||||
|
scrollArea->setAutoFillBackground(true); |
||||
|
auto pal = scrollArea->palette(); |
||||
|
pal.setColor(QPalette::Window, tabWidget->palette().color(QPalette::Base)); |
||||
|
scrollArea->setPalette(pal); |
||||
|
scrollArea->setFrameShape(QFrame::NoFrame); |
||||
|
|
||||
|
auto scrollContent = new QWidget(scrollArea); |
||||
|
scrollContent->setObjectName(TabContentId); |
||||
|
auto layout = new QFormLayout(scrollContent); |
||||
|
scrollContent->setLayout(layout); |
||||
|
scrollArea->setWidget(scrollContent); |
||||
|
|
||||
|
auto index = tabWidget->addTab(scrollArea, loadIcon(section.icon), section.title); |
||||
|
auto tooltip = section.tooltip.isNull() ? section.title : section.tooltip; |
||||
|
tabWidget->tabBar()->setTabToolTip(index, tooltip); |
||||
|
tabWidget->tabBar()->setTabWhatsThis(index, tooltip); |
||||
|
|
||||
|
for(auto group : section.groups) |
||||
|
createGroup(group, scrollContent, layout); |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::createGroup(const SettingsElements::Group &group, QWidget *contentWidget, QFormLayout *layout) |
||||
|
{ |
||||
|
QWidget *sectionWidget = nullptr; |
||||
|
QFormLayout *sectionLayout = nullptr; |
||||
|
if(group.title.isNull()) { |
||||
|
sectionWidget = contentWidget; |
||||
|
sectionLayout = layout; |
||||
|
} else { |
||||
|
auto groupBox = new QGroupBox(group.title, contentWidget); |
||||
|
groupBox->setToolTip(group.tooltip.isNull() ? group.title : group.tooltip); |
||||
|
layout->addRow(groupBox); |
||||
|
sectionWidget = groupBox; |
||||
|
sectionLayout = new QFormLayout(groupBox); |
||||
|
groupBox->setLayout(sectionLayout); |
||||
|
} |
||||
|
|
||||
|
for(auto entry : group.entries) |
||||
|
createEntry(entry, sectionWidget, sectionLayout); |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::createEntry(const SettingsElements::Entry &entry, QWidget *sectionWidget, QFormLayout *layout) |
||||
|
{ |
||||
|
QWidget *content = nullptr; |
||||
|
if(entry.type == "action") { |
||||
|
auto btn = new QPushButton(sectionWidget); |
||||
|
for(auto it = entry.properties.constBegin(); it != entry.properties.constEnd(); it++) |
||||
|
btn->setProperty(qUtf8Printable(it.key()), it.value()); |
||||
|
auto key = entry.key; |
||||
|
auto args = entry.properties.value(QStringLiteral("args")).toMap(); |
||||
|
connect(btn, &QPushButton::clicked, this, [this, key, args](){ |
||||
|
viewModel->callAction(key, args); |
||||
|
}); |
||||
|
content = btn; |
||||
|
} else { |
||||
|
auto presenter = dynamic_cast<WidgetsPresenter*>(CoreApp::instance()->presenter()); |
||||
|
if(!presenter) { |
||||
|
logWarning() << "Unable to convert QtMvvm::CoreApp::presenter to a QtMvvm::WidgetsPresenter - cannot create settings entry"; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
auto widgetFactory = presenter->inputWidgetFactory(); |
||||
|
content = widgetFactory->createInput(entry.type, sectionWidget, entry.properties); |
||||
|
if(!content) { |
||||
|
logWarning() << "Failed to create settings widget for type" << entry.type; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
auto property = content->metaObject()->userProperty(); |
||||
|
property.write(content, viewModel->loadValue(entry.key, entry.defaultValue)); |
||||
|
if(property.hasNotifySignal()) { |
||||
|
auto changedSlot = metaObject()->method(metaObject()->indexOfSlot("propertyChanged()")); |
||||
|
connect(content, property.notifySignal(), |
||||
|
this, changedSlot); |
||||
|
} else |
||||
|
changedEntries.insert(content); |
||||
|
|
||||
|
entryMap.insert(content, {entry, property}); |
||||
|
} |
||||
|
|
||||
|
auto label = new QLabel(entry.title + tr(":"), sectionWidget); |
||||
|
label->setBuddy(content); |
||||
|
label->setToolTip(entry.tooltip.isNull() ? entry.title : entry.tooltip); |
||||
|
label->setWhatsThis(label->toolTip()); |
||||
|
if(content->toolTip().isNull()) |
||||
|
content->setToolTip(label->toolTip()); |
||||
|
if(content->whatsThis().isNull()) |
||||
|
content->setWhatsThis(label->toolTip()); |
||||
|
|
||||
|
layout->addRow(label, content); |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::saveValues() |
||||
|
{ |
||||
|
for(auto it = changedEntries.begin(); it != changedEntries.end();) { |
||||
|
auto widget = *it; |
||||
|
auto info = entryMap.value(widget); |
||||
|
viewModel->saveValue(info.first.key, info.second.read(widget)); |
||||
|
if(info.second.hasNotifySignal()) |
||||
|
it = changedEntries.erase(it); |
||||
|
else |
||||
|
it++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::restoreValues() |
||||
|
{ |
||||
|
for(auto info : entryMap) |
||||
|
viewModel->resetValue(info.first.key); |
||||
|
} |
||||
|
|
||||
|
int SettingsDialogPrivate::calcSpacing(Qt::Orientation orientation) |
||||
|
{ |
||||
|
auto baseSize = q->style()->pixelMetric(orientation == Qt::Horizontal ? |
||||
|
QStyle::PM_LayoutHorizontalSpacing : |
||||
|
QStyle::PM_LayoutVerticalSpacing); |
||||
|
if(baseSize < 0) |
||||
|
baseSize = q->style()->layoutSpacing(QSizePolicy::DefaultType, QSizePolicy::DefaultType, orientation); |
||||
|
if(baseSize < 0) { |
||||
|
#ifdef Q_OS_OSX |
||||
|
baseSize = 10; |
||||
|
#else |
||||
|
baseSize = 6; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
return baseSize; |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::updateWidth(int width) |
||||
|
{ |
||||
|
if(width > maxWidthBase) { |
||||
|
maxWidthBase = width; |
||||
|
QStyle *style = ui->categoryListWidget->style(); |
||||
|
width += style->pixelMetric(QStyle::PM_ScrollBarExtent); |
||||
|
width += style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); |
||||
|
ui->categoryListWidget->setFixedWidth(width); |
||||
|
} |
||||
|
} |
||||
|
void SettingsDialogPrivate::resetListSize() |
||||
|
{ |
||||
|
int max = ui->categoryListWidget->count(); |
||||
|
if(max <= 1) { |
||||
|
ui->categoryContentWidget->hide(); |
||||
|
q->resize(q->width() - ui->categoryContentWidget->sizeHint().width(), q->height()); |
||||
|
} else { |
||||
|
auto width = ui->categoryListWidget->sizeHint().width(); |
||||
|
ui->categoryListWidget->setFixedWidth(width); |
||||
|
maxWidthBase = width; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
QIcon SettingsDialogPrivate::loadIcon(const QUrl &icon) |
||||
|
{ |
||||
|
if(icon.scheme() == QStringLiteral("qrc")) |
||||
|
return QIcon(QLatin1Char(':') + icon.path()); |
||||
|
else |
||||
|
return QIcon(icon.toLocalFile()); |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::searchInDialog(const QRegularExpression ®ex) |
||||
|
{ |
||||
|
for(int i = 0, max = ui->categoryListWidget->count(); i < max; ++i) { |
||||
|
auto item = ui->categoryListWidget->item(i); |
||||
|
auto tab = qobject_cast<QTabWidget*>(ui->contentStackWidget->widget(i)); |
||||
|
if(!tab) |
||||
|
continue; |
||||
|
|
||||
|
if(searchInCategory(regex, tab) || |
||||
|
regex.match(item->text()).hasMatch()) { |
||||
|
item->setHidden(false); |
||||
|
if(ui->categoryListWidget->currentRow() == -1) |
||||
|
ui->categoryListWidget->setCurrentRow(i); |
||||
|
} else { |
||||
|
item->setHidden(true); |
||||
|
if(ui->categoryListWidget->currentRow() == i) { |
||||
|
auto found = false; |
||||
|
for(int j = 0; j < max; j++) { |
||||
|
if(!ui->categoryListWidget->item(j)->isHidden()){ |
||||
|
ui->categoryListWidget->setCurrentRow(j); |
||||
|
found = true; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if(!found) { |
||||
|
ui->categoryListWidget->setCurrentRow(-1); |
||||
|
ui->contentStackWidget->setCurrentIndex(max); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool SettingsDialogPrivate::searchInCategory(const QRegularExpression ®ex, QTabWidget *tab) |
||||
|
{ |
||||
|
auto someFound = false; |
||||
|
for(int i = 0, max = tab->count(); i < max; ++i) { |
||||
|
if(searchInSection(regex, tab->widget(i)->findChild<QWidget*>(TabContentId)) || |
||||
|
regex.match(tab->tabText(i)).hasMatch()){ |
||||
|
tab->setTabEnabled(i, true); |
||||
|
someFound = true; |
||||
|
} else |
||||
|
tab->setTabEnabled(i, false); |
||||
|
} |
||||
|
|
||||
|
return someFound; |
||||
|
} |
||||
|
|
||||
|
bool SettingsDialogPrivate::searchInSection(const QRegularExpression ®ex, QWidget *contentWidget) |
||||
|
{ |
||||
|
auto layout = qobject_cast<QFormLayout*>(contentWidget->layout()); |
||||
|
if(!layout) |
||||
|
return false; |
||||
|
|
||||
|
auto someFound = false; |
||||
|
for(int i = 0, max = layout->rowCount(); i < max; ++i) { |
||||
|
auto spanItem = layout->itemAt(i, QFormLayout::SpanningRole); |
||||
|
if(spanItem) { |
||||
|
auto group = qobject_cast<QGroupBox*>(spanItem->widget()); |
||||
|
if(!group) |
||||
|
continue; |
||||
|
someFound |= searchInGroup(regex, group) || |
||||
|
regex.match(group->title()).hasMatch(); |
||||
|
} else { |
||||
|
auto label = qobject_cast<QLabel*>(layout->itemAt(i, QFormLayout::LabelRole)->widget()); |
||||
|
if(!label) |
||||
|
continue; |
||||
|
auto content = layout->itemAt(i, QFormLayout::FieldRole)->widget(); |
||||
|
someFound |= searchInEntry(regex, label, content); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return someFound; |
||||
|
} |
||||
|
|
||||
|
bool SettingsDialogPrivate::searchInGroup(const QRegularExpression ®ex, QGroupBox *groupWidget) |
||||
|
{ |
||||
|
auto layout = qobject_cast<QFormLayout*>(groupWidget->layout()); |
||||
|
if(!layout) |
||||
|
return false; |
||||
|
|
||||
|
auto someFound = false; |
||||
|
for(int i = 0, max = layout->rowCount(); i < max; ++i) { |
||||
|
auto label = qobject_cast<QLabel*>(layout->itemAt(i, QFormLayout::LabelRole)->widget()); |
||||
|
if(!label) |
||||
|
continue; |
||||
|
|
||||
|
auto content = layout->itemAt(i, QFormLayout::FieldRole)->widget(); |
||||
|
someFound |= searchInEntry(regex, label, content); |
||||
|
} |
||||
|
|
||||
|
return someFound; |
||||
|
} |
||||
|
|
||||
|
bool SettingsDialogPrivate::searchInEntry(const QRegularExpression ®ex, QLabel *label, QWidget *content) |
||||
|
{ |
||||
|
if(regex.pattern().isEmpty()) { |
||||
|
label->setStyleSheet(QString()); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
auto keys = entryMap.value(content).first.searchKeys; |
||||
|
keys.append(label->text()); |
||||
|
for(auto key : keys) { |
||||
|
if(regex.match(key).hasMatch()) { |
||||
|
label->setStyleSheet(q->labelFilterStyleSheet()); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
label->setStyleSheet(QString()); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::propertyChanged() |
||||
|
{ |
||||
|
auto widget = qobject_cast<QWidget*>(sender()); |
||||
|
if(widget) |
||||
|
changedEntries.insert(widget); |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::buttonBoxClicked(QAbstractButton *button) |
||||
|
{ |
||||
|
switch(ui->buttonBox->standardButton(button)) { |
||||
|
case QDialogButtonBox::Ok: |
||||
|
saveValues(); |
||||
|
q->accept(); |
||||
|
break; |
||||
|
case QDialogButtonBox::Cancel: |
||||
|
q->reject(); |
||||
|
break; |
||||
|
case QDialogButtonBox::Apply: |
||||
|
saveValues(); |
||||
|
break; |
||||
|
case QDialogButtonBox::RestoreDefaults: |
||||
|
if(viewModel->canRestoreDefaults()) { |
||||
|
auto result = CoreApp::showDialog(viewModel->restoreConfig()); |
||||
|
connect(result, &MessageResult::dialogDone, this, [this](MessageConfig::StandardButton result) { |
||||
|
if(result == MessageConfig::Yes) { |
||||
|
restoreValues(); |
||||
|
q->accept(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
Q_UNREACHABLE(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void SettingsDialogPrivate::filterTextChanged(const QString &searchText) |
||||
|
{ |
||||
|
searchInDialog(QRegularExpression { |
||||
|
searchText, |
||||
|
QRegularExpression::CaseInsensitiveOption | |
||||
|
QRegularExpression::DontCaptureOption | |
||||
|
QRegularExpression::UseUnicodePropertiesOption |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
CategoryItemDelegate::CategoryItemDelegate(std::function<void (int)> updateFunc, const QSize &iconSize, int layoutSpacing, QObject *parent) : |
||||
|
QStyledItemDelegate(parent), |
||||
|
_iconSize(), |
||||
|
_updateFunc(updateFunc) |
||||
|
{ |
||||
|
this->_iconSize = iconSize + QSize(0, layoutSpacing); |
||||
|
} |
||||
|
|
||||
|
QSize CategoryItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const |
||||
|
{ |
||||
|
QSize size = QStyledItemDelegate::sizeHint(option, index); |
||||
|
_updateFunc(size.width()); |
||||
|
return size.expandedTo(_iconSize); |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
#ifndef QTMVVM_SETTINGSDIALOG_H |
||||
|
#define QTMVVM_SETTINGSDIALOG_H |
||||
|
|
||||
|
#include <QtCore/qscopedpointer.h> |
||||
|
|
||||
|
#include <QtMvvmSettingsCore/settingsviewmodel.h> |
||||
|
|
||||
|
#include <QtWidgets/qdialog.h> |
||||
|
|
||||
|
#include "QtMvvmSettingsWidgets/qtmvvmsettingswidgets_global.h" |
||||
|
|
||||
|
namespace QtMvvm { |
||||
|
|
||||
|
class SettingsDialogPrivate; |
||||
|
class Q_MVVMSETTINGSWIDGETS_EXPORT SettingsDialog : public QDialog |
||||
|
{ |
||||
|
Q_OBJECT |
||||
|
|
||||
|
public: |
||||
|
Q_INVOKABLE explicit SettingsDialog(QtMvvm::ViewModel *viewModel, QWidget *parent = nullptr); |
||||
|
~SettingsDialog(); |
||||
|
|
||||
|
protected: |
||||
|
virtual QString labelFilterStyleSheet() const; |
||||
|
virtual QUrl iconOverwrite() const; |
||||
|
|
||||
|
private: |
||||
|
friend class QtMvvm::SettingsDialogPrivate; |
||||
|
SettingsDialogPrivate *d; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#endif // QTMVVM_SETTINGSDIALOG_H
|
@ -0,0 +1,232 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<ui version="4.0"> |
||||
|
<class>SettingsDialog</class> |
||||
|
<widget class="QDialog" name="SettingsDialog"> |
||||
|
<property name="geometry"> |
||||
|
<rect> |
||||
|
<x>0</x> |
||||
|
<y>0</y> |
||||
|
<width>850</width> |
||||
|
<height>500</height> |
||||
|
</rect> |
||||
|
</property> |
||||
|
<property name="windowTitle"> |
||||
|
<string>Settings</string> |
||||
|
</property> |
||||
|
<property name="sizeGripEnabled"> |
||||
|
<bool>true</bool> |
||||
|
</property> |
||||
|
<property name="modal"> |
||||
|
<bool>true</bool> |
||||
|
</property> |
||||
|
<layout class="QHBoxLayout" name="settingsRootLayout" stretch="0,1"> |
||||
|
<property name="spacing"> |
||||
|
<number>0</number> |
||||
|
</property> |
||||
|
<item> |
||||
|
<widget class="QWidget" name="categoryContentWidget" native="true"> |
||||
|
<layout class="QHBoxLayout" name="categoryLayout"> |
||||
|
<property name="spacing"> |
||||
|
<number>0</number> |
||||
|
</property> |
||||
|
<property name="leftMargin"> |
||||
|
<number>0</number> |
||||
|
</property> |
||||
|
<property name="topMargin"> |
||||
|
<number>0</number> |
||||
|
</property> |
||||
|
<property name="rightMargin"> |
||||
|
<number>0</number> |
||||
|
</property> |
||||
|
<property name="bottomMargin"> |
||||
|
<number>0</number> |
||||
|
</property> |
||||
|
<item> |
||||
|
<widget class="QListWidget" name="categoryListWidget"> |
||||
|
<property name="palette"> |
||||
|
<palette> |
||||
|
<active> |
||||
|
<colorrole role="Base"> |
||||
|
<brush brushstyle="SolidPattern"> |
||||
|
<color alpha="0"> |
||||
|
<red>255</red> |
||||
|
<green>255</green> |
||||
|
<blue>255</blue> |
||||
|
</color> |
||||
|
</brush> |
||||
|
</colorrole> |
||||
|
</active> |
||||
|
<inactive> |
||||
|
<colorrole role="Base"> |
||||
|
<brush brushstyle="SolidPattern"> |
||||
|
<color alpha="0"> |
||||
|
<red>255</red> |
||||
|
<green>255</green> |
||||
|
<blue>255</blue> |
||||
|
</color> |
||||
|
</brush> |
||||
|
</colorrole> |
||||
|
</inactive> |
||||
|
<disabled> |
||||
|
<colorrole role="Base"> |
||||
|
<brush brushstyle="SolidPattern"> |
||||
|
<color alpha="255"> |
||||
|
<red>240</red> |
||||
|
<green>240</green> |
||||
|
<blue>240</blue> |
||||
|
</color> |
||||
|
</brush> |
||||
|
</colorrole> |
||||
|
</disabled> |
||||
|
</palette> |
||||
|
</property> |
||||
|
<property name="frameShape"> |
||||
|
<enum>QFrame::NoFrame</enum> |
||||
|
</property> |
||||
|
<property name="verticalScrollBarPolicy"> |
||||
|
<enum>Qt::ScrollBarAsNeeded</enum> |
||||
|
</property> |
||||
|
<property name="horizontalScrollBarPolicy"> |
||||
|
<enum>Qt::ScrollBarAlwaysOff</enum> |
||||
|
</property> |
||||
|
<property name="editTriggers"> |
||||
|
<set>QAbstractItemView::NoEditTriggers</set> |
||||
|
</property> |
||||
|
<property name="iconSize"> |
||||
|
<size> |
||||
|
<width>32</width> |
||||
|
<height>32</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
<property name="resizeMode"> |
||||
|
<enum>QListView::Adjust</enum> |
||||
|
</property> |
||||
|
<property name="spacing"> |
||||
|
<number>1</number> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="Line" name="listSeperatorLine"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Vertical</enum> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<spacer name="categoryLineSpacer"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Horizontal</enum> |
||||
|
</property> |
||||
|
<property name="sizeHint" stdset="0"> |
||||
|
<size> |
||||
|
<width>6</width> |
||||
|
<height>0</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
</spacer> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<layout class="QVBoxLayout" name="contentLayout"> |
||||
|
<item> |
||||
|
<layout class="QHBoxLayout" name="horizontalLayout"> |
||||
|
<property name="rightMargin"> |
||||
|
<number>2</number> |
||||
|
</property> |
||||
|
<item> |
||||
|
<widget class="QLabel" name="titleLabel"> |
||||
|
<property name="font"> |
||||
|
<font> |
||||
|
<pointsize>10</pointsize> |
||||
|
<weight>75</weight> |
||||
|
<bold>true</bold> |
||||
|
</font> |
||||
|
</property> |
||||
|
<property name="textFormat"> |
||||
|
<enum>Qt::PlainText</enum> |
||||
|
</property> |
||||
|
<property name="indent"> |
||||
|
<number>4</number> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QLineEdit" name="filterLineEdit"> |
||||
|
<property name="sizePolicy"> |
||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
||||
|
<horstretch>0</horstretch> |
||||
|
<verstretch>0</verstretch> |
||||
|
</sizepolicy> |
||||
|
</property> |
||||
|
<property name="minimumSize"> |
||||
|
<size> |
||||
|
<width>200</width> |
||||
|
<height>0</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
<property name="placeholderText"> |
||||
|
<string>Search in settings…</string> |
||||
|
</property> |
||||
|
<property name="clearButtonEnabled"> |
||||
|
<bool>true</bool> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QStackedWidget" name="contentStackWidget"/> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QDialogButtonBox" name="buttonBox"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Horizontal</enum> |
||||
|
</property> |
||||
|
<property name="standardButtons"> |
||||
|
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</widget> |
||||
|
<resources/> |
||||
|
<connections> |
||||
|
<connection> |
||||
|
<sender>categoryListWidget</sender> |
||||
|
<signal>currentRowChanged(int)</signal> |
||||
|
<receiver>contentStackWidget</receiver> |
||||
|
<slot>setCurrentIndex(int)</slot> |
||||
|
<hints> |
||||
|
<hint type="sourcelabel"> |
||||
|
<x>123</x> |
||||
|
<y>172</y> |
||||
|
</hint> |
||||
|
<hint type="destinationlabel"> |
||||
|
<x>488</x> |
||||
|
<y>189</y> |
||||
|
</hint> |
||||
|
</hints> |
||||
|
</connection> |
||||
|
<connection> |
||||
|
<sender>categoryListWidget</sender> |
||||
|
<signal>currentTextChanged(QString)</signal> |
||||
|
<receiver>titleLabel</receiver> |
||||
|
<slot>setText(QString)</slot> |
||||
|
<hints> |
||||
|
<hint type="sourcelabel"> |
||||
|
<x>156</x> |
||||
|
<y>42</y> |
||||
|
</hint> |
||||
|
<hint type="destinationlabel"> |
||||
|
<x>466</x> |
||||
|
<y>16</y> |
||||
|
</hint> |
||||
|
</hints> |
||||
|
</connection> |
||||
|
</connections> |
||||
|
</ui> |
@ -0,0 +1,89 @@ |
|||||
|
#ifndef QTMVVM_SETTINGSDIALOG_P_H |
||||
|
#define QTMVVM_SETTINGSDIALOG_P_H |
||||
|
|
||||
|
#include <functional> |
||||
|
|
||||
|
#include <QtCore/QSet> |
||||
|
|
||||
|
#include <QtWidgets/QStyledItemDelegate> |
||||
|
#include <QtWidgets/QFormLayout> |
||||
|
#include <QtWidgets/QGroupBox> |
||||
|
#include <QtWidgets/QLabel> |
||||
|
#include <QtWidgets/QTabWidget> |
||||
|
#include <QtWidgets/QAbstractButton> |
||||
|
|
||||
|
#include "qtmvvmsettingswidgets_global.h" |
||||
|
#include "settingsdialog.h" |
||||
|
|
||||
|
namespace Ui { |
||||
|
class SettingsDialog; |
||||
|
} |
||||
|
|
||||
|
namespace QtMvvm { |
||||
|
|
||||
|
class CategoryItemDelegate : public QStyledItemDelegate |
||||
|
{ |
||||
|
public: |
||||
|
CategoryItemDelegate(std::function<void(int)> _updateFunc, |
||||
|
const QSize &_iconSize, |
||||
|
int layoutSpacing, |
||||
|
QObject *parent = nullptr); |
||||
|
|
||||
|
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; |
||||
|
|
||||
|
private: |
||||
|
QSize _iconSize; |
||||
|
std::function<void(int)> _updateFunc; |
||||
|
}; |
||||
|
|
||||
|
class SettingsDialogPrivate : public QObject |
||||
|
{ |
||||
|
Q_OBJECT |
||||
|
|
||||
|
public: |
||||
|
SettingsDialogPrivate(SettingsDialog *q_ptr, ViewModel *viewModel); |
||||
|
~SettingsDialogPrivate(); |
||||
|
|
||||
|
static const QString TabContentId; |
||||
|
|
||||
|
SettingsDialog *q; |
||||
|
SettingsViewModel *viewModel; |
||||
|
QScopedPointer<Ui::SettingsDialog> ui; |
||||
|
CategoryItemDelegate *delegate; |
||||
|
int maxWidthBase; |
||||
|
|
||||
|
typedef QPair<SettingsElements::Entry, QMetaProperty> EntryInfo; |
||||
|
QHash<QWidget*, EntryInfo> entryMap; |
||||
|
QSet<QWidget*> changedEntries; |
||||
|
|
||||
|
void createCategory(const SettingsElements::Category &category); |
||||
|
void createSection(const SettingsElements::Section §ion, QTabWidget *tabWidget); |
||||
|
void createGroup(const SettingsElements::Group &group, QWidget *contentWidget, QFormLayout *layout); |
||||
|
void createEntry(const SettingsElements::Entry &entry, QWidget *sectionWidget, QFormLayout *layout); |
||||
|
|
||||
|
void saveValues(); |
||||
|
void restoreValues(); |
||||
|
|
||||
|
int calcSpacing(Qt::Orientation orientation); |
||||
|
void updateWidth(int width); |
||||
|
void resetListSize(); |
||||
|
|
||||
|
QIcon loadIcon(const QUrl &icon); |
||||
|
|
||||
|
void searchInDialog(const QRegularExpression ®ex); |
||||
|
bool searchInCategory(const QRegularExpression ®ex, QTabWidget *tab); |
||||
|
bool searchInSection(const QRegularExpression ®ex, QWidget *contentWidget); |
||||
|
bool searchInGroup(const QRegularExpression ®ex, QGroupBox *groupWidget); |
||||
|
bool searchInEntry(const QRegularExpression ®ex, QLabel *label, QWidget *content); |
||||
|
|
||||
|
public Q_SLOTS: |
||||
|
void createUi(); |
||||
|
|
||||
|
void propertyChanged(); |
||||
|
void buttonBoxClicked(QAbstractButton *button); |
||||
|
void filterTextChanged(const QString &searchText); |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#endif // QTMVVM_SETTINGSDIALOG_P_H
|
Loading…
Reference in new issue