Migration of QtMvvm from github
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

570 lines
18 KiB

#ifndef QTMVVM_SETTINGSENTRY_H
#define QTMVVM_SETTINGSENTRY_H
#include <QtCore/qlist.h>
#include <QtCore/qregularexpression.h>
#include "QtMvvmCore/qtmvvmcore_global.h"
#include "QtMvvmCore/isettingsaccessor.h"
namespace QtMvvm {
template <typename T>
class SettingsEntry
{
Q_DISABLE_COPY(SettingsEntry)
public:
SettingsEntry() = default;
bool isSet() const;
QString key() const;
T get() const;
void set(const T &value);
void reset();
SettingsEntry<T> &operator=(const T &value);
operator T() const;
void addChangeCallback(const std::function<void(T)> &callback);
void addChangeCallback(QObject *scope, const std::function<void(T)> &callback);
// internal
void setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue = {});
private:
QString _key;
ISettingsAccessor *_accessor = nullptr;
QVariant _default;
};
template <>
class SettingsEntry<void>
{
Q_DISABLE_COPY(SettingsEntry)
public:
inline SettingsEntry() = default;
// internal
inline void setup(const QString &, ISettingsAccessor *, const QVariant & = {}) {}
};
template <typename TType>
class SettingsListEntry
{
Q_DISABLE_COPY(SettingsListEntry)
public:
class ListElement
{
Q_DISABLE_COPY(ListElement)
public:
inline ListElement(ListElement &&other) noexcept = default;
inline ListElement &operator=(ListElement &&other) noexcept = default;
TType get() const && { return _self->getAt(_index); }
void set(const TType &value) && { _self->setAt(_index, value); }
inline ListElement &operator=(const TType &value) && { _self->setAt(_index, value); return *this; }
inline operator TType() const && { return _self->getAt(_index); }
private:
friend class SettingsListEntry<TType>;
inline ListElement(SettingsListEntry<TType> *self, int index) : _self{self}, _index{index} {}
SettingsListEntry<TType> *_self;
int _index;
};
template <typename T>
class iterator_base;
class iterator_value
{
public:
TType get() const { return _self->getAt(_index); }
void set(const TType &value) { _self->setAt(_index, value); }
inline iterator_value &operator=(const TType &value) { _self->setAt(_index, value); return *this; }
inline operator TType() const { return _self->getAt(_index); }
inline friend void swap(iterator_value& lhs, iterator_value& rhs) { qSwap(lhs._self, rhs._self); qSwap(lhs._index, rhs._index); }
inline friend bool operator==(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index == rhs._index; }
inline friend bool operator!=(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index != rhs._index; }
inline friend bool operator<(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index < rhs._index; }
inline friend bool operator>(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index > rhs._index; }
inline friend bool operator<=(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index <= rhs._index; }
inline friend bool operator>=(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index >= rhs._index; }
inline friend iterator_value operator+(const iterator_value &value, int delta) { return iterator_value{value._self, value._index + delta}; }
inline friend iterator_value operator+(int delta, const iterator_value &value) { return iterator_value{value._self, value._index + delta}; }
inline friend iterator_value operator-(const iterator_value &value, int delta) { return iterator_value{value._self, value._index - delta}; }
inline friend int operator-(const iterator_value &lhs, const iterator_value &rhs) { assert(lhs._self == rhs._self); return lhs._index - rhs._index; }
private:
template <typename T>
friend class SettingsListEntry<TType>::iterator_base;
inline iterator_value(SettingsListEntry<TType> *self, int index) : _self{self}, _index{index} {}
inline iterator_value(const iterator_value &other) = default;
inline iterator_value &operator=(const iterator_value &other) = default;
inline iterator_value(iterator_value &&other) noexcept = default;
inline iterator_value &operator=(iterator_value &&other) noexcept = default;
SettingsListEntry<TType> *_self;
int _index;
};
template <typename T>
class iterator_base
{
public:
using iterator_category = std::random_access_iterator_tag;
using difference_type = int;
using value_type = T;
using pointer = value_type*;
using reference = value_type&;
inline iterator_base(const iterator_base<T> &other) = default;
inline iterator_base &operator=(const iterator_base<T> &other) = default;
inline iterator_base(iterator_base<T> &&other) noexcept = default;
inline iterator_base &operator=(iterator_base<T> &&other) noexcept = default;
inline iterator_base<T> &operator++() { ++_value._index; return *this; }
inline iterator_base<T> operator++(int) { return iterator_base<T>{_value._self, _value._index++}; }
inline iterator_base<T> &operator--() { --_value._index; return *this; }
inline iterator_base<T> operator--(int) { return iterator_base<T>{_value._self, _value._index--}; }
inline const value_type &operator*() const { return _value; }
inline value_type &operator*() { return _value; }
inline const value_type *operator->() const { return &_value; }
inline value_type *operator->() { return &_value; }
inline friend void swap(iterator_base<T> &lhs, iterator_base<T> &rhs) { swap(lhs._value, rhs._value); }
inline friend bool operator==(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { return lhs._value == rhs._value; }
inline friend bool operator!=(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { return lhs._value != rhs._value; }
inline friend bool operator<(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { return lhs._value < rhs._value; }
inline friend bool operator>(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { return lhs._value > rhs._value; }
inline friend bool operator<=(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { return lhs._value <= rhs._value; }
inline friend bool operator>=(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { return lhs._value >= rhs._value; }
inline iterator_base<T> &operator+=(difference_type delta) { _value._index += delta; return *this; }
inline friend iterator_base<T> operator+(const iterator_base<T> &iter, difference_type delta) { return iterator_base<T>{iter._value + delta}; }
inline friend iterator_base<T> operator+(difference_type delta, const iterator_base<T> &iter) { return iterator_base<T>{iter._value + delta}; }
inline iterator_base<T> &operator-=(difference_type delta) { _value._index -= delta; return *this; }
inline friend iterator_base<T> operator-(const iterator_base<T> &iter, difference_type delta) { return iterator_base<T>{iter._value - delta}; }
inline friend difference_type operator-(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { return lhs._value - rhs._value; }
inline value_type operator[](difference_type delta) const { return iterator_value{_value._self, _value._index + delta}; }
private:
friend class SettingsListEntry<TType>;
inline iterator_base(SettingsListEntry<TType> *self, int index) : _value{self, index} {}
inline iterator_base(iterator_value value) : _value{std::move(value)} {}
iterator_value _value;
};
using iterator = iterator_base<iterator_value>;
using const_iterator = iterator_base<const iterator_value>;
SettingsListEntry() = default;
bool isSet() const;
QString key() const;
QList<TType> get() const;
void set(const QList<TType> &value);
void reset(bool reInit = true);
SettingsListEntry<TType> &operator=(const QList<TType> &value);
operator QList<TType>() const;
int size() const;
TType getAt(int index) const;
void setAt(int index, const TType &value);
void push(const TType &value);
void push(const QList<TType> &values);
TType pop();
void chop(int count);
ListElement operator[](int index);
const ListElement operator[](int index) const;
SettingsListEntry<TType> &operator+=(const TType &value);
SettingsListEntry<TType> &operator+=(const QList<TType> &values);
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator constBegin() const;
const_iterator constEnd() const;
void addChangeCallback(const std::function<void(QList<TType>)> &callback);
void addChangeCallback(QObject *scope, const std::function<void(QList<TType>)> &callback);
void addChangeCallback(const std::function<void(int, TType)> &callback); // index, value
void addChangeCallback(QObject *scope, const std::function<void(int, TType)> &callback);
void addSizeChangeCallback(const std::function<void(int)> &callback); // size
void addSizeChangeCallback(QObject *scope, const std::function<void(int)> &callback);
// internal
void setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue = {});
void setupInit(QList<TType> init);
private:
QString _key;
ISettingsAccessor *_accessor = nullptr;
QVariant _default;
QList<TType> _init;
};
// ------------- Generic Implementation -------------
template<typename T>
bool SettingsEntry<T>::isSet() const
{
return _accessor->contains(_key);
}
template<typename T>
QString SettingsEntry<T>::key() const
{
return _key;
}
template<typename T>
T SettingsEntry<T>::get() const
{
return _accessor->load(_key, _default).template value<T>();
}
template<>
Q_MVVMCORE_EXPORT QVariant SettingsEntry<QVariant>::get() const;
template<typename T>
void SettingsEntry<T>::set(const T &value)
{
_accessor->save(_key, QVariant::fromValue(value));
}
template<>
Q_MVVMCORE_EXPORT void SettingsEntry<QVariant>::set(const QVariant &value);
template<typename T>
void SettingsEntry<T>::reset()
{
_accessor->remove(_key);
}
template<typename T>
SettingsEntry<T> &SettingsEntry<T>::operator=(const T &value)
{
set(value);
return (*this);
}
template<typename T>
SettingsEntry<T>::operator T() const
{
return get();
}
template<typename T>
void SettingsEntry<T>::addChangeCallback(const std::function<void (T)> &callback)
{
addChangeCallback(_accessor, callback);
}
template<typename T>
void SettingsEntry<T>::addChangeCallback(QObject *scope, const std::function<void (T)> &callback)
{
auto mKey = _key;
auto mDefault = _default;
QObject::connect(_accessor, &ISettingsAccessor::entryChanged,
scope, [mKey, callback](const QString &key, const QVariant &value) {
if(key == mKey)
callback(value.template value<T>());
});
QObject::connect(_accessor, &ISettingsAccessor::entryRemoved,
scope, [mKey, mDefault, callback](const QString &key) {
if(key == mKey)
callback(mDefault.template value<T>());
});
}
template<>
Q_MVVMCORE_EXPORT void SettingsEntry<QVariant>::addChangeCallback(QObject *scope, const std::function<void (QVariant)> &callback);
template<typename T>
void SettingsEntry<T>::setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue)
{
Q_ASSERT_X(accessor, Q_FUNC_INFO, "You must set a valid accessor before initializing the settings!");
_key = std::move(key);
_accessor = accessor;
_default = std::move(defaultValue);
}
// ------------- Generic Implementation ListEntry -------------
template<typename TType>
bool SettingsListEntry<TType>::isSet() const
{
return _accessor->contains(_key + QStringLiteral("/size"));
}
template<typename TType>
QString SettingsListEntry<TType>::key() const
{
return _key;
}
template<typename TType>
QList<TType> SettingsListEntry<TType>::get() const
{
auto mSize = size();
QList<TType> resList;
resList.reserve(mSize);
for(auto i = 0; i < mSize; ++i)
resList.append(getAt(i));
return resList;
}
template<typename TType>
void SettingsListEntry<TType>::set(const QList<TType> &value)
{
reset(false);
push(value);
}
template<typename TType>
void SettingsListEntry<TType>::reset(bool reInit)
{
_accessor->remove(_key);
if(reInit && !_init.isEmpty())
push(_init);
}
template<typename TType>
SettingsListEntry<TType> &SettingsListEntry<TType>::operator=(const QList<TType> &value)
{
set(value);
return *this;
}
template<typename TType>
SettingsListEntry<TType>::operator QList<TType>() const
{
return get();
}
template<typename TType>
int SettingsListEntry<TType>::size() const
{
return _accessor->load(_key + QStringLiteral("/size"), 0).toInt();
}
template<typename TType>
TType SettingsListEntry<TType>::getAt(int index) const
{
return _accessor->load(_key + QStringLiteral("/%1/value").arg(index), _default).template value<TType>();
}
template<typename TType>
void SettingsListEntry<TType>::setAt(int index, const TType &value)
{
_accessor->save(_key + QStringLiteral("/%1/value").arg(index), QVariant::fromValue(value));
}
template<typename TType>
void SettingsListEntry<TType>::push(const TType &value)
{
push(QList<TType>{value});
}
template<typename TType>
void SettingsListEntry<TType>::push(const QList<TType> &values)
{
auto cIndex = size();
for(const auto &value : values)
setAt(cIndex++, value);
_accessor->save(_key + QStringLiteral("/size"), cIndex);
}
template<typename TType>
TType QtMvvm::SettingsListEntry<TType>::pop()
{
auto res = getAt(size() - 1);
chop(1);
return res;
}
template<typename TType>
void QtMvvm::SettingsListEntry<TType>::chop(int count)
{
auto cSize = size();
auto nSize = qMax(size() - count, 0);
for(auto i = cSize - 1; i >= nSize; --i)
_accessor->remove(_key + QStringLiteral("/%1/value").arg(i));
_accessor->save(_key + QStringLiteral("/size"), nSize);
}
template<typename TType>
const typename SettingsListEntry<TType>::ListElement SettingsListEntry<TType>::operator[](int index) const
{
return ListElement{this, index};
}
template<typename TType>
typename SettingsListEntry<TType>::ListElement SettingsListEntry<TType>::operator[](int index)
{
return ListElement{this, index};
}
template<typename TType>
SettingsListEntry<TType> &SettingsListEntry<TType>::operator+=(const TType &value)
{
push(value);
return *this;
}
template<typename TType>
SettingsListEntry<TType> &SettingsListEntry<TType>::operator+=(const QList<TType> &values)
{
push(values);
return *this;
}
template<typename TType>
typename SettingsListEntry<TType>::iterator SettingsListEntry<TType>::begin()
{
return iterator{this, 0};
}
template<typename TType>
typename SettingsListEntry<TType>::iterator QtMvvm::SettingsListEntry<TType>::end()
{
return iterator{this, size()};
}
template<typename TType>
typename SettingsListEntry<TType>::const_iterator SettingsListEntry<TType>::begin() const
{
return constBegin();
}
template<typename TType>
typename SettingsListEntry<TType>::const_iterator SettingsListEntry<TType>::end() const
{
return constEnd();
}
template<typename TType>
typename SettingsListEntry<TType>::const_iterator SettingsListEntry<TType>::constBegin() const
{
return const_iterator{const_cast<SettingsListEntry<TType>*>(this), 0};
}
template<typename TType>
typename SettingsListEntry<TType>::const_iterator SettingsListEntry<TType>::constEnd() const
{
return const_iterator{const_cast<SettingsListEntry<TType>*>(this), size()};
}
template<typename TType>
void SettingsListEntry<TType>::addChangeCallback(const std::function<void (QList<TType>)> &callback)
{
addChangeCallback(_accessor, callback);
}
template<typename TType>
void SettingsListEntry<TType>::addChangeCallback(QObject *scope, const std::function<void (QList<TType>)> &callback)
{
QObject::connect(_accessor, &ISettingsAccessor::entryChanged,
scope, [this, callback](const QString &key, const QVariant &) {
if(key.startsWith(_key))
callback(get());
});
QObject::connect(_accessor, &ISettingsAccessor::entryRemoved,
scope, [this, callback](const QString &key) {
if(key.startsWith(_key))
callback(get());
});
}
template<typename TType>
void SettingsListEntry<TType>::addChangeCallback(const std::function<void (int, TType)> &callback)
{
addChangeCallback(_accessor, callback);
}
template<typename TType>
void SettingsListEntry<TType>::addChangeCallback(QObject *scope, const std::function<void (int, TType)> &callback)
{
QRegularExpression mKey {QStringLiteral("^%1\\/\\d+\\/value$").arg(QRegularExpression::escape(_key))};
mKey.optimize();
auto mDefault = _default;
QObject::connect(_accessor, &ISettingsAccessor::entryChanged,
scope, [mKey, callback](const QString &key, const QVariant &value) {
auto match = mKey.match(key);
if(match.hasMatch())
callback(match.captured(1).toInt(), value.template value<TType>());
});
QObject::connect(_accessor, &ISettingsAccessor::entryRemoved,
scope, [mKey, mDefault, callback](const QString &key) {
auto match = mKey.match(key);
if(match.hasMatch())
callback(match.captured(1).toInt(), mDefault.template value<TType>());
});
}
template<typename TType>
void SettingsListEntry<TType>::addSizeChangeCallback(const std::function<void (int)> &callback)
{
addSizeChangeCallback(_accessor, callback);
}
template<typename TType>
void SettingsListEntry<TType>::addSizeChangeCallback(QObject *scope, const std::function<void (int)> &callback)
{
QString mKey = _key + QStringLiteral("/size");
auto mInit = _init;
QObject::connect(_accessor, &ISettingsAccessor::entryChanged,
scope, [mKey, callback](const QString &key, const QVariant &value) {
if(key == mKey)
callback(value.toInt());
});
QObject::connect(_accessor, &ISettingsAccessor::entryRemoved,
scope, [mKey, mInit, callback](const QString &key) {
if(key == mKey)
callback(0); //is ok, as in case of a "reinit", the entry changed will be emitted next
});
}
template<typename TType>
void SettingsListEntry<TType>::setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue)
{
Q_ASSERT_X(accessor, Q_FUNC_INFO, "You must set a valid accessor before initializing the settings!");
_key = std::move(key);
_accessor = accessor;
_default = std::move(defaultValue);
}
template<typename TType>
void QtMvvm::SettingsListEntry<TType>::setupInit(QList<TType> init)
{
_init = std::move(init);
if(!isSet())
push(_init);
}
}
#endif // QTMVVM_SETTINGSENTRY_H