23 changed files with 805 additions and 84 deletions
Before Width: | Height: | Size: 224 B |
@ -0,0 +1,53 @@ |
|||
#include "multifilterproxymodel.h" |
|||
using namespace QtMvvm; |
|||
|
|||
MultiFilterProxyModel::MultiFilterProxyModel(QObject *parent) : |
|||
QSortFilterProxyModel(parent), |
|||
_filterRoles() |
|||
{} |
|||
|
|||
void MultiFilterProxyModel::addFilterRole(int role) |
|||
{ |
|||
_filterRoles.insert(role); |
|||
invalidateFilter(); |
|||
} |
|||
|
|||
void MultiFilterProxyModel::addFilterRoles(const QList<int> &roles) |
|||
{ |
|||
_filterRoles.unite(QSet<int>::fromList(roles)); |
|||
invalidateFilter(); |
|||
} |
|||
|
|||
void MultiFilterProxyModel::clearFilterRoles() |
|||
{ |
|||
_filterRoles.clear(); |
|||
invalidateFilter(); |
|||
} |
|||
|
|||
void MultiFilterProxyModel::setFilter(const QRegularExpression ®ex) |
|||
{ |
|||
_filterRegex = regex; |
|||
invalidateFilter(); |
|||
} |
|||
|
|||
bool MultiFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const |
|||
{ |
|||
if(!_filterRegex.isValid() || _filterRoles.isEmpty()) |
|||
return true; |
|||
|
|||
for(auto role : _filterRoles) { |
|||
auto rData = sourceModel()->data(sourceModel()->index(source_row, 0, source_parent), role); |
|||
//try as stringlist
|
|||
auto strList = rData.toStringList(); |
|||
for(auto str : strList) { |
|||
if(_filterRegex.match(str).hasMatch()) |
|||
return true; |
|||
} |
|||
//try as string
|
|||
auto str = rData.toString(); |
|||
if(!str.isNull() && _filterRegex.match(str).hasMatch()) |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
@ -0,0 +1,33 @@ |
|||
#ifndef QTMVVM_MULTIFILTERPROXYMODEL_H |
|||
#define QTMVVM_MULTIFILTERPROXYMODEL_H |
|||
|
|||
#include <QtCore/QSortFilterProxyModel> |
|||
#include <QtCore/QRegularExpression> |
|||
#include <QtCore/QSet> |
|||
|
|||
namespace QtMvvm { |
|||
|
|||
class MultiFilterProxyModel : public QSortFilterProxyModel |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
explicit MultiFilterProxyModel(QObject *parent = nullptr); |
|||
|
|||
void addFilterRole(int role); |
|||
void addFilterRoles(const QList<int> &roles); |
|||
void clearFilterRoles(); |
|||
|
|||
void setFilter(const QRegularExpression ®ex); |
|||
|
|||
protected: |
|||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; |
|||
|
|||
private: |
|||
QSet<int> _filterRoles; |
|||
QRegularExpression _filterRegex; |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif // QTMVVM_MULTIFILTERPROXYMODEL_H
|
@ -0,0 +1,116 @@ |
|||
#include "settingsentrymodel.h" |
|||
|
|||
#include <QtCore/QRegularExpression> |
|||
|
|||
using namespace QtMvvm; |
|||
|
|||
const QList<int> SettingsEntryModel::FilterRoles { |
|||
SettingsEntryModel::GroupRole, |
|||
SettingsEntryModel::TitleRole, |
|||
SettingsEntryModel::ToolTipRole, |
|||
SettingsEntryModel::SearchKeysRole |
|||
}; |
|||
|
|||
SettingsEntryModel::SettingsEntryModel(QObject *parent) : |
|||
QAbstractListModel(parent), |
|||
_entries() |
|||
{} |
|||
|
|||
void SettingsEntryModel::setup(const SettingsElements::Section §ion, SettingsViewModel *viewModel, InputViewFactory *factory) |
|||
{ |
|||
beginResetModel(); |
|||
_entries.clear(); |
|||
_viewModel = viewModel; |
|||
auto rIndex = 0; |
|||
for(auto group : section.groups) { |
|||
for(auto entry : group.entries) { |
|||
auto url = factory->getDelegate(entry.type, entry.properties); |
|||
if(group.title.isEmpty()) // unnamed groups are presented first
|
|||
_entries.insert(rIndex++, EntryInfo{entry, url}); |
|||
else |
|||
_entries.append(EntryInfo{entry, url, group}); |
|||
} |
|||
} |
|||
endResetModel(); |
|||
} |
|||
|
|||
int SettingsEntryModel::rowCount(const QModelIndex &parent) const |
|||
{ |
|||
if (parent.isValid()) |
|||
return 0; |
|||
else |
|||
return _entries.size(); |
|||
} |
|||
|
|||
QVariant SettingsEntryModel::data(const QModelIndex &index, int role) const |
|||
{ |
|||
if (!index.isValid()) |
|||
return QVariant(); |
|||
|
|||
switch (role) { |
|||
case Qt::DisplayRole: |
|||
case TitleRole: |
|||
return _entries.value(index.row()).title; |
|||
case KeyRole: |
|||
return _entries.value(index.row()).key; |
|||
case TypeRole: |
|||
return QString::fromUtf8(_entries.value(index.row()).type); |
|||
case ToolTipRole: |
|||
return _entries.value(index.row()).tooltip; |
|||
case DelegateUrlRole: |
|||
return _entries.value(index.row()).delegateUrl; |
|||
case SettingsValueRole: |
|||
return _viewModel->loadValue(_entries.value(index.row()).key); |
|||
case PropertiesRole: |
|||
return _entries.value(index.row()).properties; |
|||
case GroupRole: |
|||
return _entries.value(index.row()).group.title; |
|||
case SearchKeysRole: |
|||
return _entries.value(index.row()).searchKeys; |
|||
default: |
|||
return QVariant(); |
|||
} |
|||
} |
|||
|
|||
bool SettingsEntryModel::setData(const QModelIndex &index, const QVariant &value, int role) |
|||
{ |
|||
if (!index.isValid() || role != SettingsValueRole) |
|||
return false; |
|||
|
|||
_viewModel->saveValue(_entries.value(index.row()).key, value); |
|||
emit dataChanged(index, index, {SettingsValueRole}); |
|||
return true; |
|||
} |
|||
|
|||
QHash<int, QByteArray> SettingsEntryModel::roleNames() const |
|||
{ |
|||
return { |
|||
{GroupRole, "group"}, |
|||
{KeyRole, "key"}, |
|||
{TypeRole, "type"}, |
|||
{TitleRole, "title"}, |
|||
{ToolTipRole, "tooltip"}, |
|||
{DelegateUrlRole, "delegateUrl"}, |
|||
{SettingsValueRole, "settingsValue"}, |
|||
{PropertiesRole, "properties"} |
|||
}; |
|||
} |
|||
|
|||
Qt::ItemFlags SettingsEntryModel::flags(const QModelIndex &index) const |
|||
{ |
|||
return QAbstractListModel::flags(index) | Qt::ItemIsEditable; |
|||
} |
|||
|
|||
|
|||
|
|||
SettingsEntryModel::EntryInfo::EntryInfo(SettingsElements::Entry entry, const QUrl &delegateUrl, SettingsElements::Group group) : |
|||
Entry(entry), |
|||
delegateUrl(delegateUrl), |
|||
group(group) |
|||
{ |
|||
static const QRegularExpression nameRegex(QStringLiteral("&(?!&)"), |
|||
QRegularExpression::DontCaptureOption | |
|||
QRegularExpression::OptimizeOnFirstUsageOption); |
|||
title.remove(nameRegex); |
|||
group.entries.clear(); |
|||
} |
@ -0,0 +1,58 @@ |
|||
#ifndef QTMVVM_SETTINGSENTRYMODEL_H |
|||
#define QTMVVM_SETTINGSENTRYMODEL_H |
|||
|
|||
#include <QtCore/QAbstractListModel> |
|||
|
|||
#include <QtMvvmCore/SettingsViewModel> |
|||
#include <QtMvvmCore/SettingsElements> |
|||
|
|||
#include <QtMvvmQuick/InputViewFactory> |
|||
|
|||
namespace QtMvvm { |
|||
|
|||
class SettingsEntryModel : public QAbstractListModel |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
enum Roles { |
|||
GroupRole = Qt::UserRole + 1, |
|||
KeyRole, |
|||
TypeRole, |
|||
TitleRole, |
|||
ToolTipRole, |
|||
DelegateUrlRole, |
|||
SettingsValueRole, |
|||
PropertiesRole, |
|||
SearchKeysRole |
|||
}; |
|||
Q_ENUM(Roles) |
|||
|
|||
static const QList<int> FilterRoles; |
|||
|
|||
explicit SettingsEntryModel(QObject *parent = nullptr); |
|||
|
|||
void setup(const SettingsElements::Section &setup, SettingsViewModel *viewModel, InputViewFactory *factory); |
|||
|
|||
int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
|||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
|||
bool setData(const QModelIndex &index, const QVariant &value, int role) override; |
|||
QHash<int, QByteArray> roleNames() const override; |
|||
Qt::ItemFlags flags(const QModelIndex &index) const override; |
|||
|
|||
private: |
|||
struct EntryInfo : public SettingsElements::Entry { |
|||
public: |
|||
EntryInfo(SettingsElements::Entry entry = {}, const QUrl &delegateUrl = {}, SettingsElements::Group group = {}); |
|||
|
|||
QUrl delegateUrl; |
|||
SettingsElements::Group group; |
|||
}; |
|||
|
|||
SettingsViewModel *_viewModel; |
|||
QList<EntryInfo> _entries; |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif // QTMVVM_SETTINGSENTRYMODEL_H
|
@ -0,0 +1,112 @@ |
|||
#include "settingssectionmodel.h" |
|||
#include "settingsuibuilder.h" |
|||
using namespace QtMvvm; |
|||
|
|||
const QList<int> SettingsSectionModel::FilterRoles { |
|||
SettingsSectionModel::CategoryRole, |
|||
SettingsSectionModel::TitleRole, |
|||
SettingsSectionModel::ToolTipRole, |
|||
SettingsSectionModel::ExtraSearchKeysRole |
|||
}; |
|||
|
|||
SettingsSectionModel::SettingsSectionModel(QObject *parent) : |
|||
QAbstractListModel{parent}, |
|||
_sections{}, |
|||
_hasSections{false} |
|||
{} |
|||
|
|||
void SettingsSectionModel::setup(const SettingsElements::Setup &setup) |
|||
{ |
|||
beginResetModel(); |
|||
_sections.clear(); |
|||
_hasSections = false; |
|||
auto rIndex = 0; |
|||
for(auto category : setup.categories) { |
|||
if(category.sections.size() == 1) { // single sects are always at the beginning
|
|||
_hasSections = true; |
|||
_sections.insert(rIndex++, category); |
|||
} else { |
|||
for(auto section : category.sections) |
|||
_sections.append(SectionInfo{section, category}); |
|||
} |
|||
} |
|||
endResetModel(); |
|||
} |
|||
|
|||
int SettingsSectionModel::rowCount(const QModelIndex &parent) const |
|||
{ |
|||
if (parent.isValid()) |
|||
return 0; |
|||
else |
|||
return _sections.size(); |
|||
} |
|||
|
|||
QVariant SettingsSectionModel::data(const QModelIndex &index, int role) const |
|||
{ |
|||
if (!index.isValid()) |
|||
return QVariant(); |
|||
|
|||
switch (role) { |
|||
case Qt::DisplayRole: |
|||
case TitleRole: |
|||
return _sections.value(index.row()).title; |
|||
case IconRole: |
|||
return _sections.value(index.row()).icon; |
|||
case ToolTipRole: |
|||
return _sections.value(index.row()).tooltip; |
|||
case CategoryRole: |
|||
return _sections.value(index.row()).category.title; |
|||
case SectionRole: |
|||
return QVariant::fromValue<SettingsElements::Section>(_sections.value(index.row())); |
|||
case ExtraSearchKeysRole: |
|||
{ |
|||
const auto §ion = _sections[index.row()]; |
|||
if(section.searchKeys.isEmpty()) { |
|||
for(auto group : section.groups) { |
|||
section.searchKeys.append(group.title); |
|||
for(auto entry : group.entries) { |
|||
section.searchKeys.append(entry.title); |
|||
section.searchKeys.append(entry.tooltip); |
|||
section.searchKeys.append(entry.searchKeys); |
|||
} |
|||
} |
|||
} |
|||
return section.searchKeys; |
|||
} |
|||
default: |
|||
return QVariant(); |
|||
} |
|||
} |
|||
|
|||
QHash<int, QByteArray> SettingsSectionModel::roleNames() const |
|||
{ |
|||
return { |
|||
{CategoryRole, "category"}, |
|||
{TitleRole, "title"}, |
|||
{IconRole, "iconUrl"}, |
|||
{ToolTipRole, "tooltip"}, |
|||
{SectionRole, "section"} |
|||
}; |
|||
} |
|||
|
|||
bool SettingsSectionModel::hasSections() const |
|||
{ |
|||
return _hasSections; |
|||
} |
|||
|
|||
|
|||
|
|||
SettingsSectionModel::SectionInfo::SectionInfo(SettingsElements::Section section, SettingsElements::Category category) : |
|||
Section{section}, |
|||
category{category} |
|||
{ |
|||
icon = SettingsUiBuilder::svgEscape(icon); |
|||
category.sections.clear(); |
|||
} |
|||
|
|||
SettingsSectionModel::SectionInfo::SectionInfo(SettingsElements::Category category) : |
|||
Section{category.title, category.icon, category.tooltip, category.sections.first().groups, {}, {}}, |
|||
category{} |
|||
{ |
|||
icon = SettingsUiBuilder::svgEscape(icon); |
|||
} |
@ -0,0 +1,55 @@ |
|||
#ifndef QTMVVM_SETTINGSSECTIONMODEL_H |
|||
#define QTMVVM_SETTINGSSECTIONMODEL_H |
|||
|
|||
#include <QtCore/QAbstractListModel> |
|||
|
|||
#include <QtMvvmCore/SettingsElements> |
|||
|
|||
namespace QtMvvm { |
|||
|
|||
class SettingsSectionModel : public QAbstractListModel |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
Q_PROPERTY(bool hasSections READ hasSections CONSTANT) |
|||
|
|||
public: |
|||
enum Roles { |
|||
CategoryRole = Qt::UserRole + 1, |
|||
TitleRole, |
|||
IconRole, |
|||
ToolTipRole, |
|||
SectionRole, |
|||
ExtraSearchKeysRole |
|||
}; |
|||
Q_ENUM(Roles) |
|||
|
|||
static const QList<int> FilterRoles; |
|||
|
|||
explicit SettingsSectionModel(QObject *parent = nullptr); |
|||
|
|||
void setup(const SettingsElements::Setup &setup); |
|||
|
|||
int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
|||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
|||
QHash<int, QByteArray> roleNames() const override; |
|||
|
|||
bool hasSections() const; |
|||
|
|||
private: |
|||
struct SectionInfo : public SettingsElements::Section { |
|||
public: |
|||
SectionInfo(SettingsElements::Section section = {}, SettingsElements::Category category = {}); |
|||
SectionInfo(SettingsElements::Category category); |
|||
|
|||
SettingsElements::Category category; |
|||
mutable QStringList searchKeys; |
|||
}; |
|||
|
|||
QList<SectionInfo> _sections; |
|||
bool _hasSections; |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif // QTMVVM_SETTINGSSECTIONMODEL_H
|
@ -0,0 +1,125 @@ |
|||
#include "settingsuibuilder.h" |
|||
|
|||
#include <QtCore/QRegularExpression> |
|||
#include <QtCore/QCoreApplication> |
|||
|
|||
#include <QtMvvmCore/CoreApp> |
|||
#include <QtMvvmCore/Messages> |
|||
|
|||
#include <QtQml/QQmlInfo> |
|||
|
|||
#include <QtMvvmQuick/private/quickpresenter_p.h> |
|||
|
|||
using namespace QtMvvm; |
|||
|
|||
SettingsUiBuilder::SettingsUiBuilder(QObject *parent) : |
|||
QObject(parent), |
|||
_buildView(nullptr), |
|||
_viewModel(nullptr), |
|||
_filterText(), |
|||
_allowSearch(true), |
|||
_allowRestore(true), |
|||
_sectionFilterModel(new MultiFilterProxyModel(this)), |
|||
_sectionModel(new SettingsSectionModel(this)), |
|||
_entryFilterModel(new MultiFilterProxyModel(this)), |
|||
_entryModel(new SettingsEntryModel(this)) |
|||
{ |
|||
_sectionFilterModel->setSourceModel(_sectionModel); |
|||
_sectionFilterModel->addFilterRoles(SettingsSectionModel::FilterRoles); |
|||
_entryFilterModel->setSourceModel(_entryModel); |
|||
_entryFilterModel->addFilterRoles(SettingsEntryModel::FilterRoles); |
|||
|
|||
connect(this, &SettingsUiBuilder::buildViewChanged, |
|||
this, &SettingsUiBuilder::startBuildUi); |
|||
connect(this, &SettingsUiBuilder::viewModelChanged, |
|||
this, &SettingsUiBuilder::startBuildUi); |
|||
} |
|||
|
|||
QString SettingsUiBuilder::filterText() const |
|||
{ |
|||
return _filterText; |
|||
} |
|||
|
|||
QUrl SettingsUiBuilder::svgEscape(QUrl url) |
|||
{ |
|||
if(url.scheme() == QStringLiteral("qrc")) { |
|||
auto path = url.path(); |
|||
if(path.endsWith(QStringLiteral(".svg"))) { |
|||
path.chop(4); |
|||
path.prepend(QStringLiteral("image://svg")); |
|||
return path; |
|||
} |
|||
} |
|||
|
|||
return url; |
|||
} |
|||
|
|||
void SettingsUiBuilder::loadSection(const SettingsElements::Section §ion) |
|||
{ |
|||
auto inputFactory = QuickPresenterPrivate::currentPresenter()->inputViewFactory(); |
|||
_entryModel->setup(section, _viewModel, inputFactory); |
|||
emit presentSection(_entryFilterModel); |
|||
} |
|||
|
|||
void SettingsUiBuilder::showDialog(const QString &key, const QString &title, const QString &type, const QVariantMap &properties) |
|||
{ |
|||
if(type == QStringLiteral("action")) |
|||
_viewModel->callAction(key, properties.value(QStringLiteral("args")).toMap()); |
|||
else { |
|||
getInput(title + tr(":"), QString(), qUtf8Printable(type), this, [this, key](QVariant value) { |
|||
if(value.isValid()) |
|||
_viewModel->saveValue(key, value); |
|||
}, _viewModel->loadValue(key), properties); |
|||
} |
|||
} |
|||
|
|||
void SettingsUiBuilder::restoreDefaults() |
|||
{ |
|||
if(!_viewModel->canRestoreDefaults()) |
|||
return; |
|||
|
|||
auto result = CoreApp::showDialog(_viewModel->restoreConfig()); |
|||
connect(result, &MessageResult::dialogDone, this, [this](MessageConfig::StandardButton btn) { |
|||
if(btn != MessageConfig::Yes) |
|||
return; |
|||
emit closeSettings(); |
|||
}, Qt::QueuedConnection); |
|||
} |
|||
|
|||
void SettingsUiBuilder::setFilterText(QString filterText) |
|||
{ |
|||
if (_filterText == filterText) |
|||
return; |
|||
|
|||
_filterText = filterText; |
|||
emit filterTextChanged(filterText); |
|||
|
|||
QRegularExpression regex(filterText, |
|||
QRegularExpression::CaseInsensitiveOption | |
|||
QRegularExpression::UseUnicodePropertiesOption | |
|||
QRegularExpression::DontCaptureOption); |
|||
_sectionFilterModel->setFilter(regex); |
|||
_entryFilterModel->setFilter(regex); |
|||
} |
|||
|
|||
void SettingsUiBuilder::startBuildUi() |
|||
{ |
|||
if(!_buildView || !_viewModel) |
|||
return; |
|||
|
|||
auto setup = _viewModel->loadSetup(QStringLiteral("quick")); |
|||
|
|||
//search/restore properties
|
|||
_allowSearch = setup.allowSearch; |
|||
emit allowSearchChanged(_allowSearch); |
|||
_allowRestore = setup.allowRestore; |
|||
emit allowRestoreChanged(_allowRestore); |
|||
|
|||
if(setup.categories.size() == 1 && |
|||
setup.categories.first().sections.size() == 1) |
|||
loadSection(setup.categories.first().sections.first()); |
|||
else { |
|||
_sectionModel->setup(setup); |
|||
emit presentOverview(_sectionFilterModel, _sectionModel->hasSections()); |
|||
} |
|||
} |
@ -0,0 +1,71 @@ |
|||
#ifndef QTMVVM_SETTINGSUIBUILDER_H |
|||
#define QTMVVM_SETTINGSUIBUILDER_H |
|||
|
|||
#include <QtCore/QObject> |
|||
#include <QtCore/QUrl> |
|||
|
|||
#include <QtMvvmCore/SettingsViewModel> |
|||
|
|||
#include <QtQuick/QQuickItem> |
|||
|
|||
#include "multifilterproxymodel.h" |
|||
#include "settingssectionmodel.h" |
|||
#include "settingsentrymodel.h" |
|||
|
|||
namespace QtMvvm { |
|||
|
|||
class SettingsUiBuilder : public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
Q_PROPERTY(QQuickItem* buildView MEMBER _buildView NOTIFY buildViewChanged) |
|||
Q_PROPERTY(SettingsViewModel* viewModel MEMBER _viewModel NOTIFY viewModelChanged) |
|||
Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged) |
|||
Q_PROPERTY(bool allowSearch MEMBER _allowSearch NOTIFY allowSearchChanged) |
|||
Q_PROPERTY(bool allowRestore MEMBER _allowRestore NOTIFY allowRestoreChanged) |
|||
|
|||
public: |
|||
explicit SettingsUiBuilder(QObject *parent = nullptr); |
|||
|
|||
QString filterText() const; |
|||
|
|||
static QUrl svgEscape(QUrl url); |
|||
|
|||
public Q_SLOTS: |
|||
void loadSection(const QtMvvm::SettingsElements::Section §ion); |
|||
void showDialog(const QString &key, const QString &title, const QString &type, const QVariantMap &properties); |
|||
|
|||
void restoreDefaults(); |
|||
void setFilterText(QString filterText); |
|||
|
|||
Q_SIGNALS: |
|||
void presentOverview(QAbstractItemModel *model, bool hasSections); |
|||
void presentSection(QAbstractItemModel *model); |
|||
void closeSettings(); |
|||
|
|||
void buildViewChanged(QQuickItem* buildView); |
|||
void viewModelChanged(SettingsViewModel* viewModel); |
|||
void filterTextChanged(QString filterText); |
|||
void allowSearchChanged(bool allowSearch); |
|||
void allowRestoreChanged(bool allowRestore); |
|||
|
|||
private Q_SLOTS: |
|||
void startBuildUi(); |
|||
|
|||
private: |
|||
QQuickItem* _buildView; |
|||
SettingsViewModel *_viewModel; |
|||
QString _filterText; |
|||
|
|||
bool _allowSearch; |
|||
bool _allowRestore; |
|||
|
|||
MultiFilterProxyModel *_sectionFilterModel; |
|||
SettingsSectionModel *_sectionModel; |
|||
MultiFilterProxyModel *_entryFilterModel; |
|||
SettingsEntryModel *_entryModel; |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif // QTMVVM_SETTINGSUIBUILDER_H
|
Loading…
Reference in new issue