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.

446 lines
12 KiB

#ifndef QTMVVM_SETTINGSENTRY_H
#define QTMVVM_SETTINGSENTRY_H
#include <functional>
#include <QtCore/qlist.h>
#include <QtCore/qregularexpression.h>
#include <QtCore/qhash.h>
#include <QtCore/qsharedpointer.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 SettingsListNode
{
Q_DISABLE_COPY(SettingsListNode)
public:
class Deferred
{
Q_DISABLE_COPY(Deferred)
public:
inline Deferred(Deferred &&other) noexcept = default;
inline Deferred &operator=(Deferred &&other) noexcept = default;
inline ~Deferred() { commit(); }
inline TType &element() { return _element; }
inline TType &operator*() { return _element; }
inline TType *operator->() { return &_element; }
inline void commit() { if(!_commited) { _commited = true; _node->commit(_index);} }
private:
friend class SettingsListNode<TType>;
SettingsListNode<TType> *_node;
TType &_element;
bool _commited = false;
int _index;
inline Deferred(SettingsListNode<TType> *node, TType &element, int index) :
_node{node},
_element{element},
_index{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() = default;
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++() { ++_index; return *this; }
inline iterator_base<T> operator++(int) { return iterator_base<T>{_node, _index++}; }
inline iterator_base<T> &operator--() { --_index; return *this; }
inline iterator_base<T> operator--(int) { return iterator_base<T>{_node, _index--}; }
inline reference operator*() const { return _node->at(_index); }
inline pointer operator->() const { return &_node->at(_index); }
inline friend void swap(iterator_base<T> &lhs, iterator_base<T> &rhs) noexcept { std::swap(lhs._node, rhs._node); std::swap(lhs._index, rhs._index); }
inline friend bool operator==(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { assert(lhs._node == rhs._node); return lhs._index == rhs._index; }
inline friend bool operator!=(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { assert(lhs._node == rhs._node); return lhs._index != rhs._index; }
inline friend bool operator<(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { assert(lhs._node == rhs._node); return lhs._index < rhs._index; }
inline friend bool operator>(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { assert(lhs._node == rhs._node); return lhs._index > rhs._index; }
inline friend bool operator<=(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { assert(lhs._node == rhs._node); return lhs._index <= rhs._index; }
inline friend bool operator>=(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { assert(lhs._node == rhs._node); return lhs._index >= rhs._index; }
inline iterator_base<T> &operator+=(difference_type delta) { _index += delta; return *this; }
inline friend iterator_base<T> operator+(const iterator_base<T> &iter, difference_type delta) { return iterator_base<T>{iter._node, iter._index + delta}; }
inline friend iterator_base<T> operator+(difference_type delta, const iterator_base<T> &iter) { return iterator_base<T>{iter._node, iter._index + delta}; }
inline iterator_base<T> &operator-=(difference_type delta) { _index -= delta; return *this; }
inline friend iterator_base<T> operator-(const iterator_base<T> &iter, difference_type delta) { return iterator_base<T>{iter._node, iter._index - delta}; }
inline friend difference_type operator-(const iterator_base<T> &lhs, const iterator_base<T> &rhs) { assert(lhs._node == rhs._node); return lhs._index - rhs._index; }
inline reference operator[](difference_type delta) const { return _node->at(_index + delta); }
private:
friend class SettingsListNode<TType>;
SettingsListNode<TType> *_node;
int _index;
inline iterator_base(SettingsListNode<TType> *node, int index) :
_node{node},
_index{index}
{}
};
using iterator = iterator_base<TType>;
using const_iterator = iterator_base<const TType>;
SettingsListNode() = default;
bool isSet() const;
QString key() const;
int size() const;
const TType &at(int index) const;
TType &at(int index);
TType &push();
Deferred push_deferred();
void pop(int count = 1);
void reset();
TType &operator[](int index);
const TType &operator[](int index) const;
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(int)> &callback); // size
void addChangeCallback(QObject *scope, const std::function<void(int)> &callback);
// internal
void setup(QString key, ISettingsAccessor *accessor, std::function<void(int, TType&)> setupFn);
private:
struct ElementHolder {
bool __initialized = false;
QSharedPointer<TType> _element;
inline ElementHolder() : _element{new TType{}} {}
};
QString _key;
QString _sizeKey;
ISettingsAccessor *_accessor = nullptr;
std::function<void(int, TType&)> _setupFn;
mutable QHash<int, ElementHolder> _cache;
void commit(int index);
};
// ------------- 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 ListNode -------------
template<typename TType>
bool SettingsListNode<TType>::isSet() const
{
return _accessor->contains(_sizeKey);
}
template<typename TType>
QString SettingsListNode<TType>::key() const
{
return _key;
}
template<typename TType>
int SettingsListNode<TType>::size() const
{
return _accessor->load(_sizeKey, 0).toInt();
}
template<typename TType>
const TType &SettingsListNode<TType>::at(int index) const
{
auto &value = _cache[index];
if(!value.__initialized) {
_setupFn(index, *(value._element));
value.__initialized = true;
}
return *(value._element);
}
template<typename TType>
TType &SettingsListNode<TType>::at(int index)
{
auto &value = _cache[index];
if(!value.__initialized) {
_setupFn(index, *(value._element));
value.__initialized = true;
}
return *(value._element);
}
template<typename TType>
TType &SettingsListNode<TType>::push()
{
auto cIndex = size();
_accessor->save(_sizeKey, cIndex + 1);
return at(cIndex);
}
template<typename TType>
typename SettingsListNode<TType>::Deferred SettingsListNode<TType>::push_deferred()
{
auto cIndex = size();
return {this, at(cIndex), cIndex};
}
template<typename TType>
void SettingsListNode<TType>::pop(int count)
{
auto cSize = size();
auto nSize = qMax(size() - count, 0);
for(auto i = cSize - 1; i >= nSize; --i)
_accessor->remove(_key + QStringLiteral("/%1").arg(i));
_accessor->save(_sizeKey, nSize);
}
template<typename TType>
void SettingsListNode<TType>::reset()
{
_accessor->remove(_key);
}
template<typename TType>
const TType &SettingsListNode<TType>::operator[](int index) const
{
return at(index);
}
template<typename TType>
TType &SettingsListNode<TType>::operator[](int index)
{
return at(index);
}
template<typename TType>
typename SettingsListNode<TType>::iterator SettingsListNode<TType>::begin()
{
return iterator{this, 0};
}
template<typename TType>
typename SettingsListNode<TType>::iterator SettingsListNode<TType>::end()
{
return iterator{this, size()};
}
template<typename TType>
typename SettingsListNode<TType>::const_iterator SettingsListNode<TType>::begin() const
{
return constBegin();
}
template<typename TType>
typename SettingsListNode<TType>::const_iterator SettingsListNode<TType>::end() const
{
return constEnd();
}
template<typename TType>
typename SettingsListNode<TType>::const_iterator SettingsListNode<TType>::constBegin() const
{
return const_iterator{const_cast<SettingsListNode<TType>*>(this), 0};
}
template<typename TType>
typename SettingsListNode<TType>::const_iterator SettingsListNode<TType>::constEnd() const
{
return const_iterator{const_cast<SettingsListNode<TType>*>(this), size()};
}
template<typename TType>
void SettingsListNode<TType>::addChangeCallback(const std::function<void (int)> &callback)
{
addChangeCallback(_accessor, callback);
}
template<typename TType>
void SettingsListNode<TType>::addChangeCallback(QObject *scope, const std::function<void (int)> &callback)
{
QString mKey = _sizeKey;
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, callback](const QString &key) {
if(key == mKey)
callback(0);
});
}
template<typename TType>
void SettingsListNode<TType>::setup(QString key, ISettingsAccessor *accessor, std::function<void(int, TType&)> setupFn)
{
Q_ASSERT_X(accessor, Q_FUNC_INFO, "You must set a valid accessor before initializing the settings!");
_key = std::move(key);
_sizeKey = _key + QStringLiteral("/size");
_accessor = accessor;
_setupFn = std::move(setupFn);
}
template<typename TType>
void SettingsListNode<TType>::commit(int index)
{
_accessor->save(_sizeKey, qMax(size(), index + 1));
}
}
#endif // QTMVVM_SETTINGSENTRY_H