#ifndef QTMVVM_SETTINGSENTRY_H #define QTMVVM_SETTINGSENTRY_H #include #include #include #include #include #include "QtMvvmCore/qtmvvmcore_global.h" #include "QtMvvmCore/isettingsaccessor.h" namespace QtMvvm { template 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 &operator=(const T &value); operator T() const; void addChangeCallback(const std::function &callback); void addChangeCallback(QObject *scope, const std::function &callback); // internal void setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue = {}); private: QString _key; ISettingsAccessor *_accessor = nullptr; QVariant _default; }; template <> class SettingsEntry { Q_DISABLE_COPY(SettingsEntry) public: inline SettingsEntry() = default; // internal inline void setup(const QString &, ISettingsAccessor *, const QVariant & = {}) {} }; template 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; SettingsListNode *_node; TType &_element; bool _commited = false; int _index; inline Deferred(SettingsListNode *node, TType &element, int index) : _node{node}, _element{element}, _index{index} {} }; template 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 &other) = default; inline iterator_base &operator=(const iterator_base &other) = default; inline iterator_base(iterator_base &&other) noexcept = default; inline iterator_base &operator=(iterator_base &&other) noexcept = default; inline iterator_base &operator++() { ++_index; return *this; } inline iterator_base operator++(int) { return iterator_base{_node, _index++}; } inline iterator_base &operator--() { --_index; return *this; } inline iterator_base operator--(int) { return iterator_base{_node, _index--}; } inline reference operator*() const { return _node->at(_index); } inline pointer operator->() const { return &_node->at(_index); } inline friend void swap(iterator_base &lhs, iterator_base &rhs) noexcept { std::swap(lhs._node, rhs._node); std::swap(lhs._index, rhs._index); } inline friend bool operator==(const iterator_base &lhs, const iterator_base &rhs) { assert(lhs._node == rhs._node); return lhs._index == rhs._index; } inline friend bool operator!=(const iterator_base &lhs, const iterator_base &rhs) { assert(lhs._node == rhs._node); return lhs._index != rhs._index; } inline friend bool operator<(const iterator_base &lhs, const iterator_base &rhs) { assert(lhs._node == rhs._node); return lhs._index < rhs._index; } inline friend bool operator>(const iterator_base &lhs, const iterator_base &rhs) { assert(lhs._node == rhs._node); return lhs._index > rhs._index; } inline friend bool operator<=(const iterator_base &lhs, const iterator_base &rhs) { assert(lhs._node == rhs._node); return lhs._index <= rhs._index; } inline friend bool operator>=(const iterator_base &lhs, const iterator_base &rhs) { assert(lhs._node == rhs._node); return lhs._index >= rhs._index; } inline iterator_base &operator+=(difference_type delta) { _index += delta; return *this; } inline friend iterator_base operator+(const iterator_base &iter, difference_type delta) { return iterator_base{iter._node, iter._index + delta}; } inline friend iterator_base operator+(difference_type delta, const iterator_base &iter) { return iterator_base{iter._node, iter._index + delta}; } inline iterator_base &operator-=(difference_type delta) { _index -= delta; return *this; } inline friend iterator_base operator-(const iterator_base &iter, difference_type delta) { return iterator_base{iter._node, iter._index - delta}; } inline friend difference_type operator-(const iterator_base &lhs, const iterator_base &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; SettingsListNode *_node; int _index; inline iterator_base(SettingsListNode *node, int index) : _node{node}, _index{index} {} }; using iterator = iterator_base; using const_iterator = iterator_base; 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 &callback); // size void addChangeCallback(QObject *scope, const std::function &callback); // internal void setup(QString key, ISettingsAccessor *accessor, std::function setupFn); void commit(int index); private: struct ElementHolder { bool __initialized = false; QSharedPointer _element; inline ElementHolder() : _element{new TType{}} {} }; QString _key; QString _sizeKey; ISettingsAccessor *_accessor = nullptr; std::function _setupFn; mutable QHash _cache; }; // ------------- Generic Implementation ------------- template bool SettingsEntry::isSet() const { return _accessor->contains(_key); } template QString SettingsEntry::key() const { return _key; } template T SettingsEntry::get() const { return _accessor->load(_key, _default).template value(); } template<> Q_MVVMCORE_EXPORT QVariant SettingsEntry::get() const; template void SettingsEntry::set(const T &value) { _accessor->save(_key, QVariant::fromValue(value)); } template<> Q_MVVMCORE_EXPORT void SettingsEntry::set(const QVariant &value); template void SettingsEntry::reset() { _accessor->remove(_key); } template SettingsEntry &SettingsEntry::operator=(const T &value) { set(value); return (*this); } template SettingsEntry::operator T() const { return get(); } template void SettingsEntry::addChangeCallback(const std::function &callback) { addChangeCallback(_accessor, callback); } template void SettingsEntry::addChangeCallback(QObject *scope, const std::function &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()); }); QObject::connect(_accessor, &ISettingsAccessor::entryRemoved, scope, [mKey, mDefault, callback](const QString &key) { if(key == mKey) callback(mDefault.template value()); }); } template<> Q_MVVMCORE_EXPORT void SettingsEntry::addChangeCallback(QObject *scope, const std::function &callback); template void SettingsEntry::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 bool SettingsListNode::isSet() const { return _accessor->contains(_sizeKey); } template QString SettingsListNode::key() const { return _key; } template int SettingsListNode::size() const { return _accessor->load(_sizeKey, 0).toInt(); } template const TType &SettingsListNode::at(int index) const { auto &value = _cache[index]; if(!value.__initialized) { _setupFn(index, *(value._element)); value.__initialized = true; } return *(value._element); } template TType &SettingsListNode::at(int index) { auto &value = _cache[index]; if(!value.__initialized) { _setupFn(index, *(value._element)); value.__initialized = true; } return *(value._element); } template TType &SettingsListNode::push() { auto cIndex = size(); _accessor->save(_sizeKey, cIndex + 1); return at(cIndex); } template typename SettingsListNode::Deferred SettingsListNode::push_deferred() { auto cIndex = size(); return {this, at(cIndex), cIndex}; } template void SettingsListNode::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 void SettingsListNode::reset() { _accessor->remove(_key); } template const TType &SettingsListNode::operator[](int index) const { return at(index); } template TType &SettingsListNode::operator[](int index) { return at(index); } template typename SettingsListNode::iterator SettingsListNode::begin() { return iterator{this, 0}; } template typename SettingsListNode::iterator SettingsListNode::end() { return iterator{this, size()}; } template typename SettingsListNode::const_iterator SettingsListNode::begin() const { return constBegin(); } template typename SettingsListNode::const_iterator SettingsListNode::end() const { return constEnd(); } template typename SettingsListNode::const_iterator SettingsListNode::constBegin() const { return const_iterator{const_cast*>(this), 0}; } template typename SettingsListNode::const_iterator SettingsListNode::constEnd() const { return const_iterator{const_cast*>(this), size()}; } template void SettingsListNode::addChangeCallback(const std::function &callback) { addChangeCallback(_accessor, callback); } template void SettingsListNode::addChangeCallback(QObject *scope, const std::function &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 void SettingsListNode::setup(QString key, ISettingsAccessor *accessor, std::function 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 void SettingsListNode::commit(int index) { _accessor->save(_sizeKey, qMax(size(), index + 1)); } } #endif // QTMVVM_SETTINGSENTRY_H