diff options
Diffstat (limited to 'options')
-rw-r--r-- | options/base-value.cpp | 44 | ||||
-rw-r--r-- | options/base-value.hpp | 118 | ||||
-rw-r--r-- | options/bundle.cpp | 191 | ||||
-rw-r--r-- | options/bundle.hpp | 102 | ||||
-rw-r--r-- | options/connector.cpp | 125 | ||||
-rw-r--r-- | options/connector.hpp | 50 | ||||
-rw-r--r-- | options/defs.hpp | 7 | ||||
-rw-r--r-- | options/globals.cpp | 195 | ||||
-rw-r--r-- | options/globals.hpp | 74 | ||||
-rw-r--r-- | options/group.cpp | 190 | ||||
-rw-r--r-- | options/group.hpp | 102 | ||||
-rw-r--r-- | options/lang/de_DE.ts | 4 | ||||
-rw-r--r-- | options/lang/zh_CN.ts | 2 | ||||
-rw-r--r-- | options/metatype.cpp | 36 | ||||
-rw-r--r-- | options/metatype.hpp | 47 | ||||
-rw-r--r-- | options/options.hpp | 3 | ||||
-rw-r--r-- | options/scoped.cpp | 26 | ||||
-rw-r--r-- | options/scoped.hpp | 13 | ||||
-rw-r--r-- | options/slider.cpp | 56 | ||||
-rw-r--r-- | options/slider.hpp | 51 | ||||
-rw-r--r-- | options/tie.cpp | 109 | ||||
-rw-r--r-- | options/tie.hpp | 56 | ||||
-rw-r--r-- | options/value-traits.hpp | 127 | ||||
-rw-r--r-- | options/value.cpp | 2 | ||||
-rw-r--r-- | options/value.hpp | 237 |
25 files changed, 1044 insertions, 923 deletions
diff --git a/options/base-value.cpp b/options/base-value.cpp index 6c287321..950629d0 100644 --- a/options/base-value.cpp +++ b/options/base-value.cpp @@ -1,39 +1,33 @@ #include "base-value.hpp" +#include <QThread> using namespace options; -base_value::base_value(bundle b, const QString& name, base_value::comparator cmp, std::type_index type_idx) : - b(b), - self_name(name), - cmp(cmp), - type_index(type_idx) -{ - b->on_value_created(name, this); -} +//#define OTR_TRACE_NOTIFY -base_value::~base_value() -{ - b->on_value_destructed(self_name, this); -} +const bool value_::TRACE_NOTIFY = +#ifdef OTR_TRACE_NOTIFY + true; +#else + [] { + auto b = qgetenv("OTR_TRACE_NOTIFY"); + return !b.isEmpty() && b != "0"; + }(); +#endif -void base_value::notify() const +value_::value_(bundle const& b, const QString& name) noexcept : + b(b), self_name(name) { - bundle_value_changed(); + b->on_value_created(this); } -void base_value::store(const QVariant& datum) +value_::~value_() { - b->store_kv(self_name, datum); + b->on_value_destructed(this); } -namespace options { -namespace detail { - -void set_base_value_to_default(base_value* val) +void value_::maybe_trace(const char* str) const { - val->set_to_default(); + if (TRACE_NOTIFY) + qDebug().noquote() << str << QThread::currentThreadId() << b->name() << self_name << get_variant(); } - -} // ns options::detail -} // ns options - diff --git a/options/base-value.hpp b/options/base-value.hpp index b094d693..5317191b 100644 --- a/options/base-value.hpp +++ b/options/base-value.hpp @@ -3,9 +3,12 @@ #include "bundle.hpp" #include "slider.hpp" #include "connector.hpp" +#include "metatype.hpp" #include "export.hpp" -#include "compat/meta.hpp" +#include "value-traits.hpp" + +#include <utility> #include <QObject> #include <QString> @@ -13,92 +16,87 @@ #include <QPointF> #include <QVariant> -#include <typeindex> - -#define OPENTRACK_DEFINE_SLOT(t) void setValue(t datum) { store(datum); } -#define OPENTRACK_DEFINE_SIGNAL(t) void valueChanged(t) const +#define OTR_OPTIONS_SLOT(t) void setValue(t datum) noexcept { store_(datum); } +#define OTR_OPTIONS_SIGNAL(t) void valueChanged(t) const namespace options { -class OTR_OPTIONS_EXPORT base_value : public QObject +class OTR_OPTIONS_EXPORT value_ : public QObject { Q_OBJECT - friend class detail::connector; - - using comparator = bool(*)(const QVariant& val1, const QVariant& val2); + template<typename t> using cv_qualified = detail::cv_qualified<t>; template<typename t> - using signal_sig = void(base_value::*)(cv_qualified<t>) const; + using signal_sig = void(value_::*)(cv_qualified<t>) const; public: - bundle get_bundle() { return b; } QString name() const { return self_name; } - base_value(bundle b, const QString& name, comparator cmp, std::type_index type_idx); - ~base_value() override; + value_(bundle const& b, const QString& name) noexcept; + ~value_() override; - // MSVC has ODR problems in 15.4 - // no C++17 "constexpr inline" for data declarations in MSVC template<typename t> - constexpr static auto value_changed() + static constexpr auto value_changed() { - return static_cast<signal_sig<t>>(&base_value::valueChanged); + return static_cast<signal_sig<t>>(&value_::valueChanged); } - void notify() const; + static const bool TRACE_NOTIFY; signals: - OPENTRACK_DEFINE_SIGNAL(double); - OPENTRACK_DEFINE_SIGNAL(float); - OPENTRACK_DEFINE_SIGNAL(int); - OPENTRACK_DEFINE_SIGNAL(bool); - OPENTRACK_DEFINE_SIGNAL(const QString&); - OPENTRACK_DEFINE_SIGNAL(const slider_value&); - OPENTRACK_DEFINE_SIGNAL(const QPointF&); - OPENTRACK_DEFINE_SIGNAL(const QVariant&); - - OPENTRACK_DEFINE_SIGNAL(const QList<double>&); - OPENTRACK_DEFINE_SIGNAL(const QList<float>&); - OPENTRACK_DEFINE_SIGNAL(const QList<int>&); - OPENTRACK_DEFINE_SIGNAL(const QList<bool>&); - OPENTRACK_DEFINE_SIGNAL(const QList<QString>&); - OPENTRACK_DEFINE_SIGNAL(const QList<slider_value>&); - OPENTRACK_DEFINE_SIGNAL(const QList<QPointF>&); + OTR_OPTIONS_SIGNAL(double); + OTR_OPTIONS_SIGNAL(float); + OTR_OPTIONS_SIGNAL(int); + OTR_OPTIONS_SIGNAL(bool); + OTR_OPTIONS_SIGNAL(const QString&); + OTR_OPTIONS_SIGNAL(const slider_value&); + OTR_OPTIONS_SIGNAL(const QPointF&); + OTR_OPTIONS_SIGNAL(const QVariant&); + + OTR_OPTIONS_SIGNAL(const QList<double>&); + OTR_OPTIONS_SIGNAL(const QList<float>&); + OTR_OPTIONS_SIGNAL(const QList<int>&); + OTR_OPTIONS_SIGNAL(const QList<bool>&); + OTR_OPTIONS_SIGNAL(const QList<QString>&); + OTR_OPTIONS_SIGNAL(const QList<slider_value>&); + OTR_OPTIONS_SIGNAL(const QList<QPointF>&); + protected: bundle b; QString self_name; - comparator cmp; - std::type_index type_index; - void store(const QVariant& datum); + virtual void store_variant(QVariant&&) noexcept = 0; + virtual void store_variant(const QVariant&) noexcept = 0; + + void maybe_trace(const char* str) const; template<typename t> - void store(const t& datum) + void store_(const t& datum) { - b->store_kv(self_name, QVariant::fromValue(datum)); + using traits = detail::value_traits<t>; + store_variant(traits::qvariant_from_value(datum)); } public slots: - OPENTRACK_DEFINE_SLOT(double) - OPENTRACK_DEFINE_SLOT(int) - OPENTRACK_DEFINE_SLOT(bool) - OPENTRACK_DEFINE_SLOT(const QString&) - OPENTRACK_DEFINE_SLOT(const slider_value&) - OPENTRACK_DEFINE_SLOT(const QPointF&) - OPENTRACK_DEFINE_SLOT(const QVariant&) - - OPENTRACK_DEFINE_SLOT(const QList<double>&) - OPENTRACK_DEFINE_SLOT(const QList<float>&) - OPENTRACK_DEFINE_SLOT(const QList<int>&) - OPENTRACK_DEFINE_SLOT(const QList<bool>&) - OPENTRACK_DEFINE_SLOT(const QList<QString>&) - OPENTRACK_DEFINE_SLOT(const QList<slider_value>&) - OPENTRACK_DEFINE_SLOT(const QList<QPointF>&) - - virtual void reload() = 0; - virtual void bundle_value_changed() const = 0; - virtual void set_to_default() = 0; - - friend void ::options::detail::set_base_value_to_default(base_value* val); + OTR_OPTIONS_SLOT(double) + OTR_OPTIONS_SLOT(int) + OTR_OPTIONS_SLOT(bool) + OTR_OPTIONS_SLOT(const QString&) + OTR_OPTIONS_SLOT(const slider_value&) + OTR_OPTIONS_SLOT(const QPointF&) + OTR_OPTIONS_SLOT(const QVariant&) + + OTR_OPTIONS_SLOT(const QList<double>&) + OTR_OPTIONS_SLOT(const QList<float>&) + OTR_OPTIONS_SLOT(const QList<int>&) + OTR_OPTIONS_SLOT(const QList<bool>&) + OTR_OPTIONS_SLOT(const QList<QString>&) + OTR_OPTIONS_SLOT(const QList<slider_value>&) + OTR_OPTIONS_SLOT(const QList<QPointF>&) + + virtual void set_to_default() noexcept = 0; + virtual void notify() const = 0; + virtual void notify_() const = 0; + virtual QVariant get_variant() const noexcept = 0; }; } //ns options diff --git a/options/bundle.cpp b/options/bundle.cpp index 4a133402..517005f0 100644 --- a/options/bundle.cpp +++ b/options/bundle.cpp @@ -8,133 +8,134 @@ #include "bundle.hpp" #include "value.hpp" +#include "globals.hpp" -#include <QThread> -#include <QApplication> +#include <cstdlib> -using options::base_value; +#include <QThread> +#include <QCoreApplication> -namespace options -{ +using namespace options; +using namespace options::globals; -namespace detail { +namespace options::detail { -bundle::bundle(const QString& group_name) - : mtx(QMutex::Recursive), +bundle::bundle(const QString& group_name) : group_name(group_name), saved(group_name), transient(saved) { } -bundle::~bundle() +bundle::~bundle() = default; + +void bundle::reload_no_notify() { + if (group_name.isEmpty()) + return; + + QMutexLocker l{&mtx}; + + saved = group(group_name); + transient = saved; } -void bundle::reload() +void bundle::notify() { - if (group_name.size()) { QMutexLocker l(&mtx); - saved = group(group_name); - const bool has_changes = is_modified(); - transient = saved; + connector::notify_all_values(); + } - if (has_changes) - { - connector::notify_all_values(); - emit reloading(); - emit changed(); - } + emit reloading(); + emit changed(); +} + +void bundle::reload() +{ + { + QMutexLocker l{&mtx}; + reload_no_notify(); + connector::notify_all_values(); } + emit reloading(); + emit changed(); } void bundle::set_all_to_default() { - QMutexLocker l(&mtx); - - forall([](const QString&, base_value* val) { set_base_value_to_default(val); }); - - if (is_modified()) - group::mark_ini_modified(); + connector::set_all_to_default_(); } -void bundle::store_kv(const QString& name, const QVariant& datum) +void bundle::store_kv(const QString& name, QVariant&& value) { - QMutexLocker l(&mtx); - - transient.put(name, datum); + if (group_name.isEmpty()) + return; - if (group_name.size()) + { + options::globals::detail::mark_ini_modified(); + QMutexLocker l{&mtx}; + transient.put(name, value); connector::notify_values(name); + } emit changed(); } +void bundle::store_kv(const QString& name, const QVariant& value) +{ + if (group_name.isEmpty()) + return; + + store_kv(name, QVariant{value}); +} + +QVariant bundle::get_variant(const QString& name) const +{ + QMutexLocker l{&mtx}; + return transient.get_variant(name); +} + bool bundle::contains(const QString &name) const { - QMutexLocker l(mtx); + QMutexLocker l{&mtx}; return transient.contains(name); } void bundle::save() { - if (QThread::currentThread() != qApp->thread()) + if (QThread::currentThread() != qApp->thread()) // NOLINT qDebug() << "group::save - current thread not ui thread"; - if (group_name.size() == 0) + if (group_name.isEmpty()) return; - bool modified_ = false; - { - QMutexLocker l(&mtx); - - if (is_modified()) - { - //qDebug() << "bundle" << group_name << "changed, saving"; - modified_ = true; - saved = transient; - saved.save(); - } + QMutexLocker l{&mtx}; + saved = transient; + saved.save(); } - if (modified_) - { - qDebug() << "saving" << name(); - emit saving(); - } + emit saving(); } -bool bundle::is_modified() const +void bundler::reload_no_notify_() { - QMutexLocker l(mtx); - - for (const auto& kv : transient.kvs) - { - const QVariant other = saved.get<QVariant>(kv.first); - if (!saved.contains(kv.first) || !is_equal(kv.first, kv.second, other)) - { - //if (logspam) - // qDebug() << "bundle" << group_name << "modified" << "key" << kv.first << "-" << other << "<>" << kv.second; - return true; - } - } + QMutexLocker l(&implsgl_mtx); - for (const auto& kv : saved.kvs) + for (auto& kv : implsgl_data) { - if (!transient.contains(kv.first)) + weak bundle = kv.second; + shared bundle_ = bundle.lock(); + if (bundle_) { - //if (logspam) - // qDebug() << "bundle" << group_name << "modified" << "key" << kv.first << "-" << other << "<>" << kv.second; - return true; + //qDebug() << "bundle: reverting" << kv.first << "due to profile change"; + bundle_->reload_no_notify(); } } - - return false; } -void bundler::after_profile_changed_() +void bundler::notify_() { QMutexLocker l(&implsgl_mtx); @@ -145,30 +146,31 @@ void bundler::after_profile_changed_() if (bundle_) { //qDebug() << "bundle: reverting" << kv.first << "due to profile change"; - bundle_->reload(); + bundle_->notify(); } } } -void bundler::refresh_all_bundles() +void bundler::reload_() { - singleton().after_profile_changed_(); + QMutexLocker l(&implsgl_mtx); + notify_(); + reload_no_notify_(); } -bundler::bundler() : implsgl_mtx(QMutex::Recursive) -{ -} +void bundler::notify() { singleton().notify_(); } +void bundler::reload_no_notify() { singleton().reload_no_notify_(); } +void bundler::reload() { singleton().reload_(); } -bundler::~bundler() -{ - //qDebug() << "exit: bundle singleton"; -} +bundler::bundler() = default; +bundler::~bundler() = default; -std::shared_ptr<bundler::v> bundler::make_bundle(const bundler::k& key) +std::shared_ptr<bundler::v> bundler::make_bundle_(const k& key) { QMutexLocker l(&implsgl_mtx); - auto it = implsgl_data.find(key); + using iter = decltype(implsgl_data.cbegin()); + const iter it = implsgl_data.find(key); if (it != implsgl_data.end()) { @@ -182,33 +184,36 @@ std::shared_ptr<bundler::v> bundler::make_bundle(const bundler::k& key) auto shr = shared(new v(key), [this, key](v* ptr) { QMutexLocker l(&implsgl_mtx); - auto it = implsgl_data.find(key); + const iter it = implsgl_data.find(key); if (it != implsgl_data.end()) - implsgl_data.erase(it); + (void)implsgl_data.erase(it); else - qDebug() << "ERROR: can't find self-bundle!"; + { + qCritical() << "ERROR: can't find self-bundle!"; + std::abort(); + } delete ptr; }); implsgl_data[key] = weak(shr); return shr; } -OTR_OPTIONS_EXPORT bundler& singleton() +bundler& bundler::singleton() { static bundler ret; return ret; } -QMutex* bundle::get_mtx() const { return mtx; } +} // ns options::detail -} // end options::detail +namespace options { -OTR_OPTIONS_EXPORT std::shared_ptr<bundle_> make_bundle(const QString& name) +std::shared_ptr<bundle_> make_bundle(const QString& name) { - if (name.size()) - return detail::singleton().make_bundle(name); + if (!name.isEmpty()) + return detail::bundler::singleton().make_bundle_(name); else return std::make_shared<bundle_>(QString()); } -} // end options +} // ns options diff --git a/options/bundle.hpp b/options/bundle.hpp index 9b7b7f02..158fcef9 100644 --- a/options/bundle.hpp +++ b/options/bundle.hpp @@ -10,10 +10,11 @@ #include "group.hpp" #include "connector.hpp" +#include "compat/qhash.hpp" #include <memory> #include <tuple> -#include <map> +#include <unordered_map> #include <memory> #include <vector> @@ -21,93 +22,96 @@ #include <QString> #include <QVariant> #include <QMutex> -#include <QMutexLocker> #include <QDebug> #include "export.hpp" -namespace options { - -namespace detail { +namespace options::detail { + class bundle; + struct bundler; +} // ns options::detail -void set_base_value_to_default(base_value* val); +namespace options { + using bundle_ = detail::bundle; + using bundle = std::shared_ptr<bundle_>; + OTR_OPTIONS_EXPORT std::shared_ptr<detail::bundle> make_bundle(const QString& name); +} // ns options -struct bundler; +namespace options::detail { class OTR_OPTIONS_EXPORT bundle final : public QObject, public connector { Q_OBJECT - class OTR_OPTIONS_EXPORT mutex final : public QMutex - { - public: - mutex(QMutex::RecursionMode mode) : QMutex(mode) {} - operator QMutex*() const { return const_cast<QMutex*>(static_cast<const QMutex*>(this)); } - }; + friend struct bundler; -private: - mutex mtx; + mutable QMutex mtx { QMutex::Recursive }; const QString group_name; group saved; group transient; - bundle(const bundle&) = delete; - bundle(bundle&&) = delete; - bundle& operator=(bundle&&) = delete; - bundle& operator=(const bundle&) = delete; - QMutex* get_mtx() const override; + void reload_no_notify(); signals: void reloading(); void saving() const; void changed() const; + public: - never_inline bundle(const QString& group_name); - never_inline ~bundle() override; + bundle(const bundle&) = delete; + bundle& operator=(const bundle&) = delete; + + QMutex* get_mtx() const override { return &mtx; } QString name() const { return group_name; } - never_inline void store_kv(const QString& name, const QVariant& datum); - never_inline bool contains(const QString& name) const; - never_inline bool is_modified() const; - - template<typename t> - t get(const QString& name) const - { - QMutexLocker l(mtx); - return transient.get<t>(name); - } + + explicit bundle(const QString& group_name); + ~bundle() override; + + void store_kv(const QString& name, QVariant&& datum); + void store_kv(const QString& name, const QVariant& datum); + bool contains(const QString& name) const; + + QVariant get_variant(const QString& name) const; + void notify(); + public slots: void save(); void reload(); void set_all_to_default(); }; -OTR_OPTIONS_EXPORT bundler& singleton(); - -struct OTR_OPTIONS_EXPORT bundler +struct OTR_OPTIONS_EXPORT bundler final { -public: using k = QString; using v = bundle; using weak = std::weak_ptr<v>; using shared = std::shared_ptr<v>; + + static void notify(); + static void reload_no_notify(); + static void reload(); + private: - QMutex implsgl_mtx; - std::map<k, weak> implsgl_data; - void after_profile_changed_(); -public: + QMutex implsgl_mtx { QMutex::Recursive }; + std::unordered_map<k, weak> implsgl_data {}; + + void notify_(); + void reload_no_notify_(); + + void reload_(); + + friend OTR_OPTIONS_EXPORT + std::shared_ptr<v> options::make_bundle(const QString& name); + + std::shared_ptr<v> make_bundle_(const k& key); + static bundler& singleton(); + bundler(); ~bundler(); - std::shared_ptr<v> make_bundle(const k& key); - static void refresh_all_bundles(); }; -OTR_OPTIONS_EXPORT bundler& singleton(); -} // ns options::detail +void set_value_to_default(value_* val); -using bundle_ = detail::bundle; -using bundle = std::shared_ptr<bundle_>; - -OTR_OPTIONS_EXPORT std::shared_ptr<bundle_> make_bundle(const QString& name); +} // ns options::detail -} // ns options diff --git a/options/connector.cpp b/options/connector.cpp index 6031d9ed..e86958f7 100644 --- a/options/connector.cpp +++ b/options/connector.cpp @@ -9,132 +9,85 @@ #include "connector.hpp" #include "value.hpp" -#include <utility> +namespace options::detail { -namespace options { -namespace detail { +connector::connector() = default; +connector::~connector() = default; -static bool generic_is_equal(const QVariant& val1, const QVariant& val2) -{ - return val1 == val2; -} - -connector::~connector() {} - -bool connector::is_equal(const QString& name, const QVariant& val1, const QVariant& val2) const -{ - QMutexLocker l(get_mtx()); - - auto it = connected_values.find(name); - - if (it != connected_values.cend() && std::get<0>((*it).second).size() != 0) - return std::get<1>((*it).second)(val1, val2); - else - return generic_is_equal(val1, val2); -} - -bool connector::on_value_destructed_impl(const QString& name, value_type val) +void connector::on_value_destructed(value_type val) { + const QString& name = val->name(); QMutexLocker l(get_mtx()); - const bool ok = progn( - auto it = connected_values.find(name); + const auto it = connected_values.find(name); - if (it != connected_values.end()) + if (it != connected_values.end()) + { + value_vec& values = it->second; + for (auto it = values.begin(); it != values.end(); ) { - std::vector<value_type>& values = std::get<0>((*it).second); - for (auto it = values.begin(); it != values.end(); it++) - { - if (*it == val) - { - values.erase(it); - return true; - } - } + if (*it == val) + it = values.erase(it); + else + it++; } - return false; - ); + } - return ok; + if (it != connected_values.end() && it->second.empty()) + connected_values.erase(it); } - - -void connector::on_value_destructed(const QString& name, value_type val) +void connector::on_value_created(value_type val) { - if (!name.size()) - return; + const QString& name = val->name(); - const bool ok = on_value_destructed_impl(name, val); - - if (!ok) - qWarning() << "options/connector: value destructed without creating;" - << "bundle" - << val->b->name() - << "value-name" << name - << "value-ptr" << quintptr(val); -} - -void connector::on_value_created(const QString& name, value_type val) -{ - if (!name.size()) + if (name.isEmpty()) return; QMutexLocker l(get_mtx()); - int i = 1; - while (on_value_destructed_impl(name, val)) - { - qWarning() << "options/connector: value created twice;" - << "cnt" << i++ - << "bundle" << val->b->name() - << "value-name" << name - << "value-ptr" << quintptr(val); - } - auto it = connected_values.find(name); if (it != connected_values.end()) { - tt& tmp = (*it).second; - std::type_index& typeidx = std::get<2>(tmp); - std::vector<value_type>& values = std::get<0>(tmp); - - if (typeidx != val->type_index) - std::get<1>((*it).second) = generic_is_equal; + value_vec& values = it->second; values.push_back(val); } else { - std::vector<value_type> vec; + value_vec vec; + vec.reserve(4); vec.push_back(val); - connected_values.emplace(name, tt(vec, val->cmp, val->type_index)); + connected_values.emplace(name, vec); } } void connector::notify_values(const QString& name) const { + QMutexLocker l(get_mtx()); + auto it = connected_values.find(name); if (it != connected_values.cend()) - { - for (value_type val : std::get<0>((*it).second)) - { - val->bundle_value_changed(); - } - } + for (value_type val : it->second) + val->notify(); } void connector::notify_all_values() const { - for (auto& pair : connected_values) - for (value_type val : std::get<0>(pair.second)) - val->bundle_value_changed(); + QMutexLocker l(get_mtx()); + + for (const auto& [k, v] : connected_values) + for (value_type val : v) + val->notify(); } -connector::connector() +void connector::set_all_to_default_() { -} + QMutexLocker l(get_mtx()); + for (auto& pair : connected_values) + for (auto& val : pair.second) + val->set_to_default(); } -} +} // ns options::detail diff --git a/options/connector.hpp b/options/connector.hpp index 5091ae67..025efda2 100644 --- a/options/connector.hpp +++ b/options/connector.hpp @@ -8,64 +8,42 @@ #pragma once -#include <map> +#include "compat/qhash.hpp" + +#include <unordered_map> #include <vector> -#include <tuple> -#include <typeinfo> -#include <typeindex> -#include <QVariant> + #include <QString> #include <QMutex> -#include <QMutexLocker> #include "export.hpp" namespace options { + class value_; +} -class base_value; - -namespace detail { +namespace options::detail { class OTR_OPTIONS_EXPORT connector { - friend class ::options::base_value; + friend class ::options::value_; - using value_type = base_value*; + using value_type = value_*; using value_vec = std::vector<value_type>; - using comparator = bool(*)(const QVariant&, const QVariant&); - using tt = std::tuple<value_vec, comparator, std::type_index>; - std::map<QString, tt> connected_values; + std::unordered_map<QString, value_vec> connected_values; - void on_value_destructed(const QString& name, value_type val); - void on_value_created(const QString& name, value_type val); - bool on_value_destructed_impl(const QString& name, value_type val); + void on_value_destructed(value_type val); + void on_value_created(value_type val); protected: void notify_values(const QString& name) const; void notify_all_values() const; virtual QMutex* get_mtx() const = 0; - - template<typename F> - void forall(F&& fun) - { - QMutexLocker l(get_mtx()); - - for (auto& pair : connected_values) - for (auto& val : std::get<0>(pair.second)) - fun(pair.first, val); - } + void set_all_to_default_(); public: connector(); virtual ~connector(); - - bool is_equal(const QString& name, const QVariant& val1, const QVariant& val2) const; - - connector(const connector&) = default; - connector& operator=(const connector&) = default; - connector(connector&&) = default; - connector& operator=(connector&&) = default; }; -} // ns detail -} // ns options +} // ns options::detail diff --git a/options/defs.hpp b/options/defs.hpp index f093a22a..797a8fda 100644 --- a/options/defs.hpp +++ b/options/defs.hpp @@ -1,7 +1,4 @@ #pragma once -#include <QString> - -#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename" -#define OPENTRACK_DEFAULT_CONFIG "default.ini" -#define OPENTRACK_DEFAULT_CONFIG_Q QStringLiteral("default.ini") +#define OPENTRACK_PROFILE_FILENAME_KEY "settings-filename" +#define OPENTRACK_DEFAULT_PROFILE "default.ini" diff --git a/options/globals.cpp b/options/globals.cpp new file mode 100644 index 00000000..39eb6014 --- /dev/null +++ b/options/globals.cpp @@ -0,0 +1,195 @@ +#include "globals.hpp" +#include "compat/base-path.hpp" +#include "defs.hpp" +#include "opentrack-org.hxx" + +#include <QFile> +#include <QFileInfo> +#include <QDir> +#include <QStandardPaths> +#include <QDateTime> +#include <QDebug> + +namespace options::globals::detail { + +ini_ctx::ini_ctx() = default; + +static bool is_portable_installation() +{ +#if defined _WIN32 + // must keep consistent between invocations + static const bool ret = QFile::exists(OPENTRACK_BASE_PATH + "/portable.txt"); + return ret; +#else + return false; +#endif +} + +saver_::~saver_() +{ + if (--ctx.refcount == 0 && ctx.modifiedp) + { + auto& settings = *ctx.qsettings; + settings.sync(); + if (settings.status() != QSettings::NoError) + qDebug() << "error with .ini file" << settings.fileName() << settings.status(); + ctx.modifiedp = false; + } + ctx.mtx.unlock(); +} + +saver_::saver_(ini_ctx& ini) : ctx { ini } +{ + ctx.refcount++; +} + +ini_ctx& cur_settings() +{ + static ini_ctx ini; + const QString filename = ini_filename(); + + ini.mtx.lock(); + + if (ini.pathname != filename) + { + ini.qsettings.emplace(ini_combine(filename), QSettings::IniFormat); + ini.pathname = filename; + } + + return ini; +} + +ini_ctx& global_settings() +{ + static ini_ctx& ret = progn( + static ini_ctx ini; + + if (!is_portable_installation()) + // Windows registry or xdg on Linux + ini.qsettings.emplace(OPENTRACK_ORG); + else + // file in executable's directory + ini.qsettings.emplace(OPENTRACK_BASE_PATH + QStringLiteral("/globals.ini"), + QSettings::IniFormat); + + ini.pathname = QStringLiteral("."); + return (ini_ctx&)ini; + ); + ret.mtx.lock(); + return ret; +} + +void mark_ini_modified(bool value) +{ + auto& ini = cur_settings(); + ini.modifiedp = value; + ini.mtx.unlock(); +} + +} // ns options::globals::detail + +namespace options::globals +{ + +using namespace detail; + +bool is_ini_modified() +{ + ini_ctx& ini = cur_settings(); + bool ret = ini.modifiedp; + ini.mtx.unlock(); + return ret; +} + +QString ini_filename() +{ + return with_global_settings_object([&](QSettings& settings) { + static_assert(sizeof(OPENTRACK_DEFAULT_PROFILE) > 1); + static_assert(sizeof(OPENTRACK_PROFILE_FILENAME_KEY) > 1); + + const QString ret = settings.value(QStringLiteral(OPENTRACK_PROFILE_FILENAME_KEY), + QStringLiteral(OPENTRACK_DEFAULT_PROFILE)).toString(); + if (ret.isEmpty()) + return QStringLiteral(OPENTRACK_DEFAULT_PROFILE); + return ret; + }); +} + +QString ini_pathname() +{ + return ini_combine(ini_filename()); +} + +QString ini_combine(const QString& filename) +{ + return QStringLiteral("%1/%2").arg(ini_directory(), filename); +} + +QStringList ini_list() +{ + static QMutex mtx; + static QStringList list; + QMutexLocker l{&mtx}; + + const QString dirname = ini_directory(); + + { + static QDateTime last_time = {}; + auto time = QFileInfo{dirname}.lastModified(); + if (time == last_time) + return list; + last_time = time; + } + + QDir settings_dir(dirname); + + using f = QDir::Filter; + list = settings_dir.entryList({ QStringLiteral("*.ini") }, f::Files | f::Readable, QDir::Name); + std::sort(list.begin(), list.end()); + return list; +} + +void mark_global_ini_modified(bool value) +{ + auto& ini = global_settings(); + ini.modifiedp = value; + ini.mtx.unlock(); +} + +static QString ini_directory_() +{ + if (detail::is_portable_installation()) + { +fail: constexpr const char* subdir = "ini"; + QString dir = OPENTRACK_BASE_PATH; + if (dir.isEmpty()) + dir = '.'; + (void)QDir(dir).mkpath(subdir); + return QStringLiteral("%1/%2").arg(dir, subdir); + } + else + { + QString dir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0, QString()); + if (dir.isEmpty()) + goto fail; + const QString fmt = QStringLiteral("%1/%2"); +#if !defined _WIN32 && !defined __APPLE__ + if (!QFile::exists(fmt.arg(dir, OPENTRACK_ORG))) + { + dir = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).value(0, QString()); + if (dir.isEmpty()) + goto fail; + } +#endif + (void)QDir(dir).mkpath(OPENTRACK_ORG); + return fmt.arg(dir, OPENTRACK_ORG); + } +} + +QString ini_directory() +{ + static const QString dir = ini_directory_(); + return dir; +} + +} // ns options::globals diff --git a/options/globals.hpp b/options/globals.hpp new file mode 100644 index 00000000..af242dc9 --- /dev/null +++ b/options/globals.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include "export.hpp" +#include "compat/macros.h" + +#include <optional> + +#include <QString> +#include <QSettings> +#include <QMutex> + +namespace options::globals::detail { + +struct saver_; + +struct OTR_OPTIONS_EXPORT ini_ctx +{ + std::optional<QSettings> qsettings { std::in_place }; + QString pathname; + QMutex mtx { QMutex::Recursive }; + + unsigned refcount = 0; + bool modifiedp = false; + + ini_ctx(); +}; + +struct OTR_OPTIONS_EXPORT saver_ final +{ + ini_ctx& ctx; + + never_inline ~saver_(); + explicit never_inline saver_(ini_ctx& ini); +}; + +template<typename F> +never_inline +auto with_settings_object_(ini_ctx& ini, F&& fun) +{ + saver_ saver { ini }; + + return fun(*ini.qsettings); +} + +OTR_OPTIONS_EXPORT ini_ctx& cur_settings(); +OTR_OPTIONS_EXPORT ini_ctx& global_settings(); + +OTR_OPTIONS_EXPORT void mark_ini_modified(bool value = true); + +} // ns options::globals::detail + +namespace options::globals +{ + OTR_OPTIONS_EXPORT void mark_global_ini_modified(bool value = true); + OTR_OPTIONS_EXPORT QString ini_directory(); + OTR_OPTIONS_EXPORT QString ini_filename(); + OTR_OPTIONS_EXPORT QString ini_pathname(); + OTR_OPTIONS_EXPORT QString ini_combine(const QString& filename); + OTR_OPTIONS_EXPORT QStringList ini_list(); + + template<typename F> + auto with_settings_object(F&& fun) + { + using namespace detail; + return with_settings_object_(cur_settings(), fun); + } + + template<typename F> + auto with_global_settings_object(F&& fun) + { + using namespace detail; + return with_settings_object_(global_settings(), fun); + } +} // ns options::globals diff --git a/options/group.cpp b/options/group.cpp index 69378d51..d5829008 100644 --- a/options/group.cpp +++ b/options/group.cpp @@ -8,205 +8,75 @@ #include "group.hpp" #include "defs.hpp" +#include "globals.hpp" -#include "compat/timer.hpp" -#include "compat/library-path.hpp" - -#include <cmath> +#include <utility> +#include <algorithm> #include <QFile> -#include <QStandardPaths> #include <QDir> -#include <QDebug> -namespace options { +namespace options::detail { + +using namespace options::globals; group::group(const QString& name) : name(name) { - if (name == "") + constexpr unsigned reserved_buckets = 64; + kvs.reserve(reserved_buckets); + kvs.max_load_factor(0.4375); + + if (name.isEmpty()) return; with_settings_object([&](QSettings& conf) { conf.beginGroup(name); - for (auto& k_ : conf.childKeys()) - { - auto tmp = k_.toUtf8(); - QString k(tmp); - QVariant val = conf.value(k_); - if (val.type() != QVariant::Invalid) - kvs[k] = std::move(val); - } + for (auto const& k : conf.childKeys()) + kvs[k] = conf.value(k); conf.endGroup(); }); } void group::save() const { - if (name == "") + if (name.isEmpty()) return; with_settings_object([&](QSettings& s) { s.beginGroup(name); - for (auto& i : kvs) + for (auto const& i : kvs) s.setValue(i.first, i.second); s.endGroup(); - - mark_ini_modified(); }); } -void group::put(const QString &s, const QVariant &d) -{ - kvs[s] = d; -} - -bool group::contains(const QString &s) const -{ - const auto it = kvs.find(s); - return it != kvs.cend() && it->second != QVariant::Invalid; -} - -bool group::is_portable_installation() +void group::put(const QString& s, QVariant&& d) { -#if defined _WIN32 - if (QFile::exists(OPENTRACK_BASE_PATH + "/portable.txt")) - return true; -#endif - return false; -} - -QString group::ini_directory() -{ - - QString dir; - - if (is_portable_installation()) - { - dir = OPENTRACK_BASE_PATH; - - static const QString subdir = "ini"; - - if (!QDir(dir).mkpath(subdir)) - return QString(); - - return dir + '/' + subdir; - } + if (d.isNull()) + kvs.erase(s); else - { - dir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0, QString()); - if (dir.isEmpty()) - return QString(); - if (!QDir(dir).mkpath(OPENTRACK_ORG)) - return QString(); - - dir += '/'; - dir += OPENTRACK_ORG; - } - - return dir; -} - -QString group::ini_filename() -{ - return with_global_settings_object([&](QSettings& settings) { - const QString ret = settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString(); - if (ret.size() == 0) - return QStringLiteral(OPENTRACK_DEFAULT_CONFIG); - return ret; - }); + kvs[s] = d; } -QString group::ini_pathname() +void group::put(const QString& s, const QVariant& d) { - const auto dir = ini_directory(); - if (dir == "") - return ""; - return dir + "/" + ini_filename(); + put(s, QVariant{d}); } -QString group::ini_combine(const QString& filename) +bool group::contains(const QString& s) const { - return ini_directory() + QStringLiteral("/") + filename; -} - -QStringList group::ini_list() -{ - const auto dirname = ini_directory(); - if (dirname == "") - return QStringList(); - QDir settings_dir(dirname); - QStringList list = settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name ); - std::sort(list.begin(), list.end()); - return list; -} - -void group::mark_ini_modified() -{ - QMutexLocker l(&cur_ini_mtx); - ini_modifiedp = true; -} - -QString group::cur_ini_pathname; - -std::shared_ptr<QSettings> group::cur_ini; -QMutex group::cur_ini_mtx(QMutex::Recursive); -int group::ini_refcount = 0; -bool group::ini_modifiedp = false; - -std::shared_ptr<QSettings> group::cur_global_ini; -QMutex group::global_ini_mtx(QMutex::Recursive); -int group::global_ini_refcount = 0; -bool group::global_ini_modifiedp = false; - -std::shared_ptr<QSettings> group::cur_settings_object() -{ - const QString pathname = ini_pathname(); - - if (pathname.isEmpty()) - return std::make_shared<QSettings>(); - - if (pathname != cur_ini_pathname) - { - cur_ini = std::make_shared<QSettings>(pathname, QSettings::IniFormat); - cur_ini_pathname = pathname; - } - - return cur_ini; + const auto it = kvs.find(s); + return it != kvs.cend(); } -std::shared_ptr<QSettings> group::cur_global_settings_object() +QVariant group::get_variant(const QString& name) const { - if (cur_global_ini) - return cur_global_ini; - - if (!is_portable_installation()) - cur_global_ini = std::make_shared<QSettings>(OPENTRACK_ORG); - else - { - static const QString pathname = OPENTRACK_BASE_PATH + QStringLiteral("/globals.ini"); - cur_global_ini = std::make_shared<QSettings>(pathname, QSettings::IniFormat); - } + auto it = kvs.find(name); + if (it != kvs.cend()) + return it->second; - return cur_global_ini; + return {}; } -never_inline -group::saver_::~saver_() -{ - if (--refcount == 0 && modifiedp) - { - modifiedp = false; - s.sync(); - if (s.status() != QSettings::NoError) - qDebug() << "error with .ini file" << s.fileName() << s.status(); - } -} - -never_inline -group::saver_::saver_(QSettings& s, int& refcount, bool& modifiedp) : - s(s), refcount(refcount), modifiedp(modifiedp) -{ - refcount++; -} +} // ns options::detail -} // ns options diff --git a/options/group.hpp b/options/group.hpp index c933b134..11bab965 100644 --- a/options/group.hpp +++ b/options/group.hpp @@ -1,93 +1,39 @@ #pragma once +#include "options/defs.hpp" + +#include "compat/base-path.hpp" +#include "compat/library-path.hpp" +#include "compat/macros.h" +#include "compat/qhash.hpp" #include "export.hpp" -// included here to propagate into callers of options::group -#include "metatype.hpp" +#include <optional> +#include <unordered_map> -#include <map> -#include <memory> #include <QString> -#include <QList> #include <QVariant> -#include <QSettings> -#include <QMutex> - -namespace options { - -// snapshot of qsettings group at given time -class OTR_OPTIONS_EXPORT group final -{ - QString name; - - static QString cur_ini_pathname; - - static std::shared_ptr<QSettings> cur_ini; - static int ini_refcount; - static bool ini_modifiedp; - static QMutex cur_ini_mtx; - - static std::shared_ptr<QSettings> cur_global_ini; - static int global_ini_refcount; - static bool global_ini_modifiedp; - static QMutex global_ini_mtx; - - struct OTR_OPTIONS_EXPORT saver_ final - { - QSettings& s; - int& refcount; - bool& modifiedp; - - never_inline ~saver_(); - never_inline saver_(QSettings& s, int& refcount, bool& modifiedp); - }; - static std::shared_ptr<QSettings> cur_settings_object(); - static std::shared_ptr<QSettings> cur_global_settings_object(); -public: - std::map<QString, QVariant> kvs; - group(const QString& name); - void save() const; - void put(const QString& s, const QVariant& d); - bool contains(const QString& s) const; - static QString ini_directory(); - static QString ini_filename(); - static QString ini_pathname(); - static QString ini_combine(const QString& filename); - static QStringList ini_list(); - static bool is_portable_installation(); +#include <QDebug> - static void mark_ini_modified(); +// XXX TODO remove qsettings usage -sh 20180624 - template<typename t> - never_inline - t get(const QString& k) const +namespace options::detail { + // snapshot of qsettings group at given time + class OTR_OPTIONS_EXPORT group final { - auto value = kvs.find(k); - if (value != kvs.cend()) - return value->second.value<t>(); - return t(); - } + QString name; - template<typename F> - never_inline - static auto with_settings_object(F&& fun) - { - QMutexLocker l(&cur_ini_mtx); - saver_ saver { *cur_settings_object(), ini_refcount, ini_modifiedp }; + public: + std::unordered_map<QString, QVariant> kvs; + explicit group(const QString& name); + void save() const; + void put(const QString& s, const QVariant& d); + void put(const QString& s, QVariant&& d); + bool contains(const QString& s) const; - return fun(saver.s); - } - - template<typename F> - static auto with_global_settings_object(F&& fun) - { - QMutexLocker l(&global_ini_mtx); - saver_ saver { *cur_global_settings_object(), global_ini_refcount, global_ini_modifiedp }; - global_ini_modifiedp = true; + never_inline QVariant get_variant(const QString& name) const; + }; +} // ns options::detail - return fun(saver.s); - } -}; -} diff --git a/options/lang/de_DE.ts b/options/lang/de_DE.ts new file mode 100644 index 00000000..1552582e --- /dev/null +++ b/options/lang/de_DE.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_DE"> +</TS> diff --git a/options/lang/zh_CN.ts b/options/lang/zh_CN.ts index 6401616d..e5ca8aa9 100644 --- a/options/lang/zh_CN.ts +++ b/options/lang/zh_CN.ts @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> -<TS version="2.1"> +<TS version="2.1" language="zh_CN"> </TS> diff --git a/options/metatype.cpp b/options/metatype.cpp index ed12821b..7430c00a 100644 --- a/options/metatype.cpp +++ b/options/metatype.cpp @@ -1,22 +1,26 @@ -#include "metatype.hpp" - -#define OPENTRACK_REGISTER_METATYPE(t) ::options::detail::custom_type_initializer::declare_for_type<t>(#t) +#include <QMetaType> -namespace options { -namespace detail { +namespace options::detail { -custom_type_initializer::custom_type_initializer() +template<typename t> +void declare_metatype_for_type(const char* str) { - OPENTRACK_REGISTER_METATYPE(options::slider_value); - OPENTRACK_REGISTER_METATYPE(QList<double>); - OPENTRACK_REGISTER_METATYPE(QList<float>); - OPENTRACK_REGISTER_METATYPE(QList<int>); - OPENTRACK_REGISTER_METATYPE(QList<bool>); - OPENTRACK_REGISTER_METATYPE(QList<QString>); - OPENTRACK_REGISTER_METATYPE(QList<QPointF>); + qRegisterMetaType<t>(str); + qRegisterMetaTypeStreamOperators<t>(); } -const custom_type_initializer custom_type_initializer::singleton; +} // ns options::detail -} -} +#define OPENTRACK_DEFINE_METATYPE3(t, ctr) \ + static \ + const char init_##ctr = /* NOLINT(misc-definitions-in-headers) */ \ + (::options::detail::declare_metatype_for_type<t>(#t), 0); \ + +#define OPENTRACK_DEFINE_METATYPE2(t, ctr) \ + OPENTRACK_DEFINE_METATYPE3(t, ctr) + +#define OPENTRACK_DEFINE_METATYPE(t) \ + OPENTRACK_DEFINE_METATYPE2(t, __COUNTER__) + +#define OPENTRACK_METATYPE_(x) OPENTRACK_DEFINE_METATYPE(x) +#include "metatype.hpp" diff --git a/options/metatype.hpp b/options/metatype.hpp index c889766a..ca0947fb 100644 --- a/options/metatype.hpp +++ b/options/metatype.hpp @@ -1,38 +1,25 @@ #pragma once -#include <QMetaType> +#include "slider.hpp" +#include "defs.hpp" + #include <QList> #include <QString> #include <QPointF> -#include <QDataStream> #include <QDebug> -#include "export.hpp" -#include "slider.hpp" - -Q_DECLARE_METATYPE(QList<double>) -Q_DECLARE_METATYPE(QList<float>) -Q_DECLARE_METATYPE(QList<int>) -Q_DECLARE_METATYPE(QList<bool>) -Q_DECLARE_METATYPE(QList<QString>) -Q_DECLARE_METATYPE(QList<QPointF>) -Q_DECLARE_METATYPE(::options::slider_value) - -namespace options { -namespace detail { - -struct custom_type_initializer final -{ - static const custom_type_initializer singleton; - - custom_type_initializer(); - - template<typename t> static void declare_for_type(const char* str) - { - qRegisterMetaType<t>(str); - qRegisterMetaTypeStreamOperators<t>(); - } -}; +#include <QMetaType> -} -} +#if !defined OPENTRACK_METATYPE_ +# define OPENTRACK_METATYPE(x) Q_DECLARE_METATYPE(x) +#else +# define OPENTRACK_METATYPE(x) Q_DECLARE_METATYPE(x) OPENTRACK_METATYPE_(x) +#endif + +OPENTRACK_METATYPE(QList<double>) +OPENTRACK_METATYPE(QList<float>) +OPENTRACK_METATYPE(QList<int>) +OPENTRACK_METATYPE(QList<bool>) +OPENTRACK_METATYPE(QList<QString>) +OPENTRACK_METATYPE(QList<QPointF>) +OPENTRACK_METATYPE(::options::slider_value) diff --git a/options/options.hpp b/options/options.hpp index bc892120..a99d5c59 100644 --- a/options/options.hpp +++ b/options/options.hpp @@ -8,9 +8,8 @@ #include "defs.hpp" #include "bundle.hpp" -#include "group.hpp" #include "slider.hpp" #include "value.hpp" #include "tie.hpp" -#include "metatype.hpp" #include "scoped.hpp" +#include "globals.hpp" diff --git a/options/scoped.cpp b/options/scoped.cpp index 2974dfdb..33db7907 100644 --- a/options/scoped.cpp +++ b/options/scoped.cpp @@ -1,4 +1,5 @@ #include "scoped.hpp" +#include "compat/run-in-thread.hpp" #include <QApplication> #include <QThread> @@ -10,27 +11,27 @@ namespace options { // XXX hack: the flag shouldn't be here as action at distance -sh 20160926 -static std::atomic_bool teardown_flag(false); -static void set_teardown_flag(bool value); +static std::atomic<bool> teardown_flag = false; +[[nodiscard]] static bool set_teardown_flag(bool value); static void ensure_thread(); static bool is_tracker_teardown(); -static void set_teardown_flag(bool value) +static bool set_teardown_flag(bool value) { // flag being set means "opts" is about to go out of scope due to tracker stop // in this case we don't reload options. we only want to reload when cancel is pressed. ensure_thread(); - teardown_flag = value; + return teardown_flag.exchange(value); } static void ensure_thread() { // only as a bug check - if (qApp == nullptr) + if (qApp == nullptr) // NOLINT abort(); - const QThread* ui_thread = qApp->thread(); + const QThread* ui_thread = qApp->thread(); // NOLINT const QThread* curthread = QThread::currentThread(); if (ui_thread == nullptr) @@ -47,8 +48,14 @@ static bool is_tracker_teardown() opts::~opts() { - if (!is_tracker_teardown()) + // XXX TODO debug crash with ps3eye dialog in pt tracker -sh 20211019 + if (!is_tracker_teardown() && raii) +#if 1 + run_in_thread_sync(qApp->thread(), [this]{ b->reload(); }); +#else + assert(b); b->reload(); +#endif #if 0 else qDebug() << "in teardown, not reloading" << b->name(); @@ -59,14 +66,13 @@ opts::opts(const QString &name) : b(make_bundle(name)) { } -with_tracker_teardown::with_tracker_teardown() : old_value(teardown_flag) +with_tracker_teardown::with_tracker_teardown() : old_value{set_teardown_flag(true)} { - set_teardown_flag(true); } with_tracker_teardown::~with_tracker_teardown() { - set_teardown_flag(old_value); + (void)set_teardown_flag(old_value); } } // ns options diff --git a/options/scoped.hpp b/options/scoped.hpp index 49c3d18f..81e6bd19 100644 --- a/options/scoped.hpp +++ b/options/scoped.hpp @@ -6,8 +6,6 @@ #include "export.hpp" -#include <atomic> - namespace options { struct OTR_OPTIONS_EXPORT with_tracker_teardown final @@ -26,11 +24,16 @@ struct OTR_OPTIONS_EXPORT opts bundle b; - virtual ~opts(); - opts(const QString& name); - opts& operator=(const opts&) = delete; opts(const opts&) = delete; + + void set_raii_dtor_state(bool x) { raii = x; } + +protected: + explicit opts(const QString& name); + ~opts(); +private: + bool raii = true; }; } diff --git a/options/slider.cpp b/options/slider.cpp index 33000ee9..76d1dddc 100644 --- a/options/slider.cpp +++ b/options/slider.cpp @@ -24,39 +24,24 @@ slider_value::slider_value(double cur, double min, double max) : cur_ = min_; } -slider_value::slider_value(const slider_value& v) : slider_value(v.cur(), v.min(), v.max()) -{ -} - -slider_value::slider_value() : slider_value(0, 0, 0) -{ -} - -slider_value& slider_value::operator=(const slider_value& v) -{ - cur_ = v.cur_; - - min_ = v.min_; - max_ = v.max_; - - return *this; -} - bool slider_value::operator==(const slider_value& v) const { - using std::fabs; - constexpr double eps = 2e-3; #if 1 - return (fabs(v.cur_ - cur_) < eps && - fabs(v.min_ - min_) < eps && - fabs(v.max_ - max_) < eps); + return (std::fabs(v.cur_ - cur_) < eps && + std::fabs(v.min_ - min_) < eps && + std::fabs(v.max_ - max_) < eps); #else - return (fabs(v.cur_ - cur_) < eps); + return (std::fabs(v.cur_ - cur_) < eps); #endif } +bool slider_value::operator!=(const slider_value& v) const +{ + return !(*this == v); +} + slider_value slider_value::update_from_slider(int pos, int q_min, int q_max) const { slider_value v(*this); @@ -67,11 +52,11 @@ slider_value slider_value::update_from_slider(int pos, int q_min, int q_max) con : (((pos - q_min) * (v.max() - v.min())) / q_diff + v.min()); if (sv_pos < v.min()) - v = slider_value(v.min(), v.min(), v.max()); + v = { v.min(), v.min(), v.max() }; else if (sv_pos > v.max()) - v = slider_value(v.max(), v.min(), v.max()); + v = { v.max(), v.min(), v.max() }; else - v = slider_value(sv_pos, v.min(), v.max()); + v = { sv_pos, v.min(), v.max() }; return v; } @@ -86,11 +71,11 @@ int slider_value::to_slider_pos(int q_min, int q_max) const return int(std::round(((cur() - min()) * q_diff / div) + q_min)); } -} // end ns options +} // ns options -QT_BEGIN_NAMESPACE +using options::slider_value; -QDataStream& operator << (QDataStream& out, const options::slider_value& v) +QDataStream& operator << (QDataStream& out, const slider_value& v) { out << v.cur() << v.min() @@ -98,17 +83,16 @@ QDataStream& operator << (QDataStream& out, const options::slider_value& v) return out; } -QDebug operator << (QDebug dbg, const options::slider_value& val) +QDebug operator << (QDebug dbg, const slider_value& v) { - return dbg << val.cur(); + return dbg << QStringLiteral("slider_value(cur=%1, min=%2, max=%3)") + .arg(v.cur()).arg(v.min()).arg(v.max()).toUtf8().constData(); } -QDataStream& operator >> (QDataStream& in, options::slider_value& v) +QDataStream& operator >> (QDataStream& in, slider_value& v) { double cur = 0, min = 0, max = 0; in >> cur >> min >> max; - v = options::slider_value(cur, min, max); + v = slider_value(cur, min, max); return in; } - -QT_END_NAMESPACE diff --git a/options/slider.hpp b/options/slider.hpp index f3ba44f8..1e721ae0 100644 --- a/options/slider.hpp +++ b/options/slider.hpp @@ -8,10 +8,11 @@ #pragma once #include "export.hpp" -#include "compat/macros.hpp" -#include <QMetaType> +#include <type_traits> + #include <QDataStream> +#include <QMetaType> #include <QDebug> namespace options @@ -20,42 +21,28 @@ namespace options { double cur_, min_, max_; - template<typename t> - using arith_conversion_t = std::enable_if_t<std::is_arithmetic_v<std::decay_t<t>>, std::decay_t<t>>; public: slider_value(double cur, double min, double max); - template<typename t, typename u, typename v> slider_value(t cur, u min, v max) : - cur_(double(cur)), - min_(double(min)), - max_(double(max)) - {} - - template<typename t> - never_inline - explicit operator arith_conversion_t<t>() const - { - return t(cur_); - } - - slider_value(const slider_value& v); - slider_value(); - slider_value& operator=(const slider_value& v); + template<typename t, typename u, typename v> + slider_value(t cur, u min, v max) : + slider_value((double)cur, (double)min, (double)max) {} + + slider_value& operator=(const slider_value& v) = default; + slider_value(const slider_value& v) = default; + slider_value() : slider_value{0, 0, 0} {} + bool operator==(const slider_value& v) const; - operator double() const { return cur_; } - double cur() const { return cur_; } - double min() const { return min_; } - double max() const { return max_; } + bool operator!=(const slider_value& v) const; + constexpr operator double() const { return cur_; } + constexpr double cur() const { return cur_; } + constexpr double min() const { return min_; } + constexpr double max() const { return max_; } slider_value update_from_slider(int pos, int q_min, int q_max) const; int to_slider_pos(int q_min, int q_max) const; }; } -QT_BEGIN_NAMESPACE - -QDebug operator << (QDebug dbg, const options::slider_value& val); -QDataStream& operator << (QDataStream& out, const options::slider_value& v); -QDataStream& operator >> (QDataStream& in, options::slider_value& v); - -QT_END_NAMESPACE - +OTR_OPTIONS_EXPORT QDebug operator << (QDebug dbg, const options::slider_value& val); +OTR_OPTIONS_EXPORT QDataStream& operator << (QDataStream& out, const options::slider_value& v); +OTR_OPTIONS_EXPORT QDataStream& operator >> (QDataStream& in, options::slider_value& v); diff --git a/options/tie.cpp b/options/tie.cpp index baa8bb82..adf26b53 100644 --- a/options/tie.cpp +++ b/options/tie.cpp @@ -8,39 +8,45 @@ #include "tie.hpp" #include "compat/run-in-thread.hpp" +#include "compat/macros.h" + +#include "value-traits.hpp" + +#include <cmath> namespace options { -OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QComboBox* cb) +void tie_setting(value<int>& v, QComboBox* cb) { cb->setCurrentIndex(v); v = cb->currentIndex(); - base_value::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE); + value_::connect(cb, SIGNAL(currentIndexChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); + value_::connect(&v, SIGNAL(valueChanged(int)), cb, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE); } -OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QComboBox* cb) +void tie_setting(value<QString>& v, QComboBox* cb) { cb->setCurrentText(v); v = cb->currentText(); - base_value::connect(cb, SIGNAL(currentTextChanged(QString)), &v, SLOT(setValue(const QString&)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(const QString&)), cb, SLOT(setCurrentText(const QString&)), v.SAFE_CONNTYPE); + auto set_current_text = [cb, &v](const QString& str) { + cb->setCurrentText(str); + v = cb->currentText(); + }; + value_::connect(cb, &QComboBox::currentTextChanged, &v, v.value_changed<QString>(), v.DIRECT_CONNTYPE); + value_::connect(&v, v.value_changed<QString>(), cb, set_current_text, v.SAFE_CONNTYPE); } -OTR_OPTIONS_EXPORT void tie_setting(value<QVariant>& v, QComboBox* cb) +void tie_setting(value<QVariant>& v, QComboBox* cb) { auto set_idx = [cb](const QVariant& var) { const int sz = cb->count(); int idx = -1; for (int k = 0; k < sz; k++) - { - if (cb->itemData(k) == var) - { + if (cb->itemData(k) == var) { idx = k; break; } - } cb->setCurrentIndex(idx); return idx; }; @@ -50,65 +56,65 @@ OTR_OPTIONS_EXPORT void tie_setting(value<QVariant>& v, QComboBox* cb) if (idx != -1) v = cb->itemData(idx); else - v = QVariant(QVariant::Invalid); - - base_value::connect(cb, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - &v, [cb, &v](int idx) { - v = cb->itemData(idx); - }, v.DIRECT_CONNTYPE); - base_value::connect(&v, base_value::value_changed<QVariant>(), - cb, - [cb, set_idx](const QVariant& var) { - run_in_thread_sync(cb, [&]() { - set_idx(var); - }); - }, v.DIRECT_CONNTYPE); + v = {}; + + value_::connect(cb, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + &v, [cb, &v](int idx) { v = cb->itemData(idx); }, + v.DIRECT_CONNTYPE); + value_::connect(&v, value_::value_changed<QVariant>(), + cb, [set_idx](const QVariant& var) { set_idx(var); }, + v.SAFE_CONNTYPE); } -// XXX TODO need variant with setEnabled based on lambda retval -- sh 20170524 +void tie_setting(value<bool>& v, QRadioButton* cb) +{ + cb->setChecked(v); + value_::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE); + value_::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE); +} -OTR_OPTIONS_EXPORT void tie_setting(value<bool>& v, QCheckBox* cb) +void tie_setting(value<bool>& v, QCheckBox* cb) { cb->setChecked(v); - base_value::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE); + value_::connect(cb, SIGNAL(toggled(bool)), &v, SLOT(setValue(bool)), v.DIRECT_CONNTYPE); + value_::connect(&v, SIGNAL(valueChanged(bool)), cb, SLOT(setChecked(bool)), v.SAFE_CONNTYPE); } -OTR_OPTIONS_EXPORT void tie_setting(value<double>& v, QDoubleSpinBox* dsb) +void tie_setting(value<double>& v, QDoubleSpinBox* dsb) { dsb->setValue(v); - base_value::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE); + value_::connect(dsb, SIGNAL(valueChanged(double)), &v, SLOT(setValue(double)), v.DIRECT_CONNTYPE); + value_::connect(&v, SIGNAL(valueChanged(double)), dsb, SLOT(setValue(double)), v.SAFE_CONNTYPE); } -OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QSpinBox* sb) +void tie_setting(value<int>& v, QSpinBox* sb) { sb->setValue(v); - base_value::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE); + value_::connect(sb, SIGNAL(valueChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); + value_::connect(&v, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)), v.SAFE_CONNTYPE); } -OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QLineEdit* le) +void tie_setting(value<QString>& v, QLineEdit* le) { le->setText(v); - base_value::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE); - base_value::connect(&v, base_value::value_changed<QString>(), le, &QLineEdit::setText, v.SAFE_CONNTYPE); + value_::connect(le, SIGNAL(textChanged(QString)), &v, SLOT(setValue(QString)), v.DIRECT_CONNTYPE); + value_::connect(&v, value_::value_changed<QString>(), le, &QLineEdit::setText, v.SAFE_CONNTYPE); } -OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QLabel* lb) +void tie_setting(value<QString>& v, QLabel* lb) { lb->setText(v); - base_value::connect(&v, base_value::value_changed<QString>(), lb, &QLabel::setText, v.SAFE_CONNTYPE); + value_::connect(&v, value_::value_changed<QString>(), lb, &QLabel::setText, v.SAFE_CONNTYPE); } -OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QTabWidget* t) +void tie_setting(value<int>& v, QTabWidget* t) { t->setCurrentIndex(v); - base_value::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); - base_value::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE); + value_::connect(t, SIGNAL(currentChanged(int)), &v, SLOT(setValue(int)), v.DIRECT_CONNTYPE); + value_::connect(&v, SIGNAL(valueChanged(int)), t, SLOT(setCurrentIndex(int)), v.SAFE_CONNTYPE); } -OTR_OPTIONS_EXPORT void tie_setting(value<slider_value>& v, QSlider* w) +void tie_setting(value<slider_value>& v, QSlider* w) { { const int q_min = w->minimum(); @@ -118,10 +124,7 @@ OTR_OPTIONS_EXPORT void tie_setting(value<slider_value>& v, QSlider* w) v = v().update_from_slider(w->value(), q_min, q_max); } - base_value::connect(w, - &QSlider::valueChanged, - &v, - [=, &v](int pos) + value_::connect(w, &QSlider::valueChanged, &v, [=, &v](int pos) { run_in_thread_sync(w, [&]() { @@ -133,17 +136,17 @@ OTR_OPTIONS_EXPORT void tie_setting(value<slider_value>& v, QSlider* w) }, v.DIRECT_CONNTYPE); - base_value::connect(&v, - base_value::value_changed<slider_value>(), - w, - [=, &v](double) { + value_::connect(&v, + value_::value_changed<slider_value>(), + w, + [=, &v](double) { run_in_thread_sync(w, [=, &v]() { const int q_min = w->minimum(); const int q_max = w->maximum(); - const int pos = v().to_slider_pos(q_min, q_max); + const int pos = v->to_slider_pos(q_min, q_max); + v = v->update_from_slider(pos, q_min, q_max); w->setValue(pos); - v = v().update_from_slider(w->value(), q_min, q_max); }); }, v.DIRECT_CONNTYPE); diff --git a/options/tie.hpp b/options/tie.hpp index 4ebc1846..194a3a5d 100644 --- a/options/tie.hpp +++ b/options/tie.hpp @@ -20,6 +20,7 @@ #include <QLineEdit> #include <QLabel> #include <QTabWidget> +#include <QRadioButton> #include <cmath> @@ -31,39 +32,43 @@ namespace options { template<typename t> -std::enable_if_t<std::is_enum<t>::value> -tie_setting(value<t>& v, QComboBox* cb) +std::enable_if_t<std::is_enum_v<t>> tie_setting(value<t>& v, QComboBox* cb) { cb->setCurrentIndex(cb->findData(int(static_cast<t>(v)))); v = static_cast<t>(cb->currentData().toInt()); - base_value::connect(cb, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - &v, - [&v, cb](int idx) - { - run_in_thread_sync(cb, - [&]() { - v = static_cast<t>(cb->itemData(idx).toInt()); - }); - }, - v.DIRECT_CONNTYPE); - base_value::connect(&v, base_value::value_changed<int>(), - cb, [cb](int x) { - run_in_thread_sync(cb, [&]() { cb->setCurrentIndex(cb->findData(x)); }); - }, - v.DIRECT_CONNTYPE); + value_::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + &v, [&v, cb](int idx) { v = static_cast<t>(cb->itemData(idx).toInt()); }, + v.DIRECT_CONNTYPE); + + value_::connect(&v, value_::value_changed<int>(), + cb, [cb](int x) { cb->setCurrentIndex(cb->findData(x)); }, + v.SAFE_CONNTYPE); +} + +template<typename t, typename From, typename To> +void tie_setting(value<t>& v, QComboBox* cb, From&& fn_to_index, To&& fn_to_value) +{ + cb->setCurrentIndex(fn_to_index(v)); + v = fn_to_value(cb->currentIndex(), cb->currentData()); + + value_::connect(cb, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + &v, [&v, cb, fn_to_value](int idx) { v = fn_to_value(idx, cb->currentData()); }, + v.DIRECT_CONNTYPE); + value_::connect(&v, value_::value_changed<t>(), + cb, [cb, fn_to_index](detail::cv_qualified<t>& v) { cb->setCurrentIndex(fn_to_index(v)); }, + v.SAFE_CONNTYPE); } template<typename t, typename F> void tie_setting(value<t>& v, QLabel* lb, F&& fun) { - auto closure = [=](cv_qualified<t> x) { lb->setText(fun(x)); }; + auto closure = [lb, fun](detail::cv_qualified<t> v) { lb->setText(fun(v)); }; closure(v()); - base_value::connect(&v, base_value::value_changed<t>(), - lb, closure, - v.SAFE_CONNTYPE); + value_::connect(&v, value_::value_changed<t>(), + lb, closure, + v.SAFE_CONNTYPE); } template<typename t, typename F> @@ -74,15 +79,16 @@ void tie_setting(value<t>& v, QObject* obj, F&& fun) fun(v()); - base_value::connect(&v, base_value::value_changed<t>(), - obj, fun, - v.DIRECT_CONNTYPE); + value_::connect(&v, value_::value_changed<t>(), + obj, fun, + v.DIRECT_CONNTYPE); } OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QComboBox* cb); OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QComboBox* cb); OTR_OPTIONS_EXPORT void tie_setting(value<QVariant>& v, QComboBox* cb); OTR_OPTIONS_EXPORT void tie_setting(value<bool>& v, QCheckBox* cb); +OTR_OPTIONS_EXPORT void tie_setting(value<bool>& v, QRadioButton* cb); OTR_OPTIONS_EXPORT void tie_setting(value<double>& v, QDoubleSpinBox* dsb); OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QSpinBox* sb); OTR_OPTIONS_EXPORT void tie_setting(value<QString>& v, QLineEdit* le); diff --git a/options/value-traits.hpp b/options/value-traits.hpp index ac5f6e5c..145cd924 100644 --- a/options/value-traits.hpp +++ b/options/value-traits.hpp @@ -1,49 +1,122 @@ -#include "export.hpp" +#pragma once #include "slider.hpp" +#include "export.hpp" -#include <QString> - +#include <cmath> #include <type_traits> -namespace options { -namespace detail { +#include <QString> + +namespace options::detail { +template<typename t> +using cv_qualified = + std::conditional_t<std::is_fundamental_v<std::remove_cvref_t<t>>, + std::remove_cvref_t<t>, + std::add_lvalue_reference_t<std::add_const_t<std::remove_cvref_t<t>>>>; template<typename t, typename u = t, typename Enable = void> struct default_value_traits { - using element_type = std::decay_t<t>; - using value_type = std::decay_t<u>; + using value_type = t; + using stored_type = u; + + static inline + value_type value_with_default(cv_qualified<value_type> val, cv_qualified<value_type>) + { + return val; + } + + static inline + value_type value_from_storage(cv_qualified<stored_type> x) + { + return static_cast<value_type>(x); + } + + static inline + stored_type storage_from_value(cv_qualified<value_type> val) + { + return static_cast<stored_type>(val); + } + + static inline + value_type value_from_qvariant(const QVariant& x) + { + return value_from_storage(storage_from_qvariant(x)); + } + + static inline + QVariant qvariant_from_value(cv_qualified<value_type> val) + { + return qvariant_from_storage(storage_from_value(val)); + } + + static constexpr inline + value_type pass_value(cv_qualified<value_type> x) + { + if constexpr(std::is_same_v<value_type, stored_type>) + return x; + else + return value_from_storage(storage_from_value(x)); + } - static inline value_type from_value(const value_type& val, const value_type&) { return val; } - static inline value_type from_storage(const element_type& x) { return static_cast<value_type>(x); } - static inline element_type to_storage(const value_type& val) { return static_cast<element_type>(val); } + static inline + stored_type storage_from_qvariant(const QVariant& x) + { + // XXX TODO + return x.value<stored_type>(); + } + + static inline + QVariant qvariant_from_storage(cv_qualified<stored_type> val) + { + // XXX TODO + return QVariant::fromValue<stored_type>(val); + } + + static inline + bool is_equal(cv_qualified<value_type> x, cv_qualified<value_type> y) + { + return x == y; + } }; -template<typename t, typename u = t, typename Enable = void> -struct value_traits : default_value_traits<t, u, Enable> +template<typename t, typename Enable = void> +struct value_traits : default_value_traits<t> {}; + +template<> +inline +bool default_value_traits<double>::is_equal(double x, double y) { -}; + return std::fabs(x - y) < 1e-6; +} + +template<> struct value_traits<float, double> : default_value_traits<float> {}; template<> -struct value_traits<slider_value> : default_value_traits<slider_value> +inline +bool default_value_traits<float, double>::is_equal(float x, float y) { - static inline slider_value from_value(const slider_value& val, const slider_value& def) - { - return slider_value(val.cur(), def.min(), def.max()); - } -}; + return std::fabs(x - y) < 1e-6f; +} -// Qt uses int a lot in slots so use it for all enums -template<typename t> -struct value_traits<t, t, std::enable_if_t<std::is_enum_v<t>>> : public default_value_traits<int, t> +template<> +inline +slider_value default_value_traits<slider_value>::value_with_default(cv_qualified<slider_value> val, cv_qualified<slider_value> def) { -}; + return { val.cur(), def.min(), def.max() }; +} template<> -struct value_traits<float> : public default_value_traits<float, double, void> +inline +bool default_value_traits<slider_value>::is_equal(cv_qualified<slider_value> x, cv_qualified<slider_value> y) { -}; + return value_traits<double>::is_equal(x.cur(), y.cur()); +} + +// Qt uses int a lot in slots so use it for all enums +template<typename t> +struct value_traits<t, std::enable_if_t<std::is_enum_v<t>>> : default_value_traits<t, int> {}; + +} // ns options::detail -} // ns detail -} // ns options diff --git a/options/value.cpp b/options/value.cpp index 5796dcd6..81a094e6 100644 --- a/options/value.cpp +++ b/options/value.cpp @@ -1,5 +1,5 @@ // instantiate the "template class" value<t> symbols -#define OTR_OPT_VALUE OTR_TEMPLATE_EXPORT +#define OTR_OPTIONS_INST_VALUE OTR_TEMPLATE_EXPORT #include "value.hpp" diff --git a/options/value.hpp b/options/value.hpp index c8fb02ca..9a7487b8 100644 --- a/options/value.hpp +++ b/options/value.hpp @@ -14,157 +14,208 @@ #include "slider.hpp" #include "base-value.hpp" #include "value-traits.hpp" -#include "compat/macros.hpp" -#include <cstdio> #include <type_traits> -#include <typeinfo> -#include <typeindex> #include <utility> -#include <QVariant> -#include <QString> -#include <QPointF> -#include <QList> -#include <QMutex> +#include <QMetaType> + +namespace options::detail { + template<typename t> + class dereference_wrapper final + { + t x; + public: + constexpr t const* operator->() const { return &x; } + constexpr t* operator->() { return &x; } + constexpr explicit dereference_wrapper(t&& x) : x(x) {} + }; +} // ns options::detail namespace options { template<typename t> -class value final : public base_value +class value final : public value_ { - using traits = detail::value_traits<t, t, void>; - using element_type = typename traits::element_type; + static_assert(std::is_same_v<t, std::remove_cvref_t<t>>); + mutable QMutex mtx; + const t def; + mutable t cached_value; + using traits = detail::value_traits<t>; - static bool is_equal(const QVariant& val1, const QVariant& val2) + never_inline + auto get() const noexcept { - return val1.value<element_type>() == val2.value<element_type>(); + if (!b->contains(self_name)) + return traits::pass_value(def); + + QVariant variant = b->get_variant(self_name); + + if (variant.isNull() || !variant.isValid()) + return traits::pass_value(def); + + return traits::pass_value(traits::value_with_default(traits::value_from_qvariant(variant), def)); } never_inline - t get() const + void store_variant(QVariant&& value) noexcept override { - if (self_name.isEmpty()) - return def; + if (traits::is_equal(get(), traits::value_from_qvariant(value))) + return; - QVariant variant = b->get<QVariant>(self_name); + if (is_null()) + return; - if (!b->contains(self_name) || variant.type() == QVariant::Invalid) - return def; + if (value.isValid() && !value.isNull()) + b->store_kv(self_name, value); + else + b->store_kv(self_name, traits::qvariant_from_value(def)); + } - const element_type x(variant.value<element_type>()); + never_inline + void store_variant(const QVariant& value) noexcept override + { + QVariant copy{value}; + store_variant(std::move(copy)); + } - return traits::from_value(traits::from_storage(x), def); + bool is_null() const + { + return self_name.isEmpty() || b->name().isEmpty(); } public: - never_inline - t operator=(const t& datum) + QVariant get_variant() const noexcept override { - if (self_name.isEmpty()) - return def; - - if (datum != get()) - store(traits::to_storage(datum)); + if (QVariant ret{b->get_variant(self_name)}; ret.isValid() && !ret.isNull()) + return ret; - return datum; + return traits::qvariant_from_value(def); } - static constexpr inline Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection; - static constexpr inline Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection; + never_inline + void notify_() const override + { + auto x = get(); + { + QMutexLocker l(&mtx); + cached_value = x; + } + maybe_trace("notify +"); + emit valueChanged(traits::storage_from_value(x)); + maybe_trace("notify -"); + } never_inline - value(bundle b, const QString& name, t def) : - base_value(b, name, &is_equal, std::type_index(typeid(element_type))), - def(def) - { - if (!self_name.isEmpty()) - QObject::connect(b.get(), SIGNAL(reloading()), - this, SLOT(reload()), - DIRECT_CONNTYPE); + void notify() const override + { + if (is_null()) + return; + + auto x = get(); + { + QMutexLocker l(&mtx); + if (traits::is_equal(x, cached_value)) + { + //maybe_trace("notify ~"); + return; + } + else + { + cached_value = x; + l.unlock(); + maybe_trace("notify +"); + emit valueChanged(traits::storage_from_value(x)); + maybe_trace("notify -"); + } + } } - template<unsigned k> - inline value(bundle b, const char (&name)[k], t def) : value(b, QLatin1String(name, k-1), def) + auto& operator=(t&& datum) noexcept { - static_assert(k > 0, ""); + if (is_null()) + return *this; + + store_variant(traits::qvariant_from_value(traits::pass_value(datum))); + maybe_trace("set-value"); + return *this; } - never_inline - t default_value() const + auto& operator=(const t& datum) noexcept { - return def; + if (is_null()) + return *this; + + t copy{datum}; + *this = std::move(copy); + return *this; } - never_inline - void set_to_default() override + auto& operator=(const value<t>& datum) noexcept { - *this = def; + *this = *datum; + return *this; } - never_inline - operator t() const { return get(); } + static constexpr Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection; + static constexpr Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection; - never_inline - t operator->() const + value(bundle b, const QString& name, t def) noexcept : value_(b, name), def(std::move(def)), cached_value{get()} { - return get(); } - never_inline - void reload() override + //value(const value<t>& other) noexcept : value{other.b, other.self_name, other.def} {} + value(const value<t>&) = delete; + + t default_value() const { - if (!self_name.isEmpty()) - *this = static_cast<t>(*this); + return def; } - never_inline - void bundle_value_changed() const override + void set_to_default() noexcept override { - if (!self_name.isEmpty()) - emit valueChanged(traits::to_storage(get())); + *this = def; } - never_inline - t operator()() const + operator t() const { return get(); } + + template<typename u> + explicit force_inline operator u() const { return to<u>(); } + + auto operator->() const noexcept { - return get(); + return detail::dereference_wrapper<t>{get()}; } + force_inline auto operator()() const noexcept { return get(); } + force_inline auto operator*() const noexcept { return get(); } + template<typename u> - never_inline - u to() const + u to() const noexcept { return static_cast<u>(get()); } - -private: - const t def; }; -#if defined _MSC_VER - -# if !defined OTR_OPT_VALUE -# define OTR_OPT_VALUE OTR_TEMPLATE_IMPORT -# endif - - OTR_OPT_VALUE value<double>; - OTR_OPT_VALUE value<float>; - OTR_OPT_VALUE value<int>; - OTR_OPT_VALUE value<bool>; - OTR_OPT_VALUE value<QString>; - OTR_OPT_VALUE value<slider_value>; - OTR_OPT_VALUE value<QPointF>; - OTR_OPT_VALUE value<QVariant>; - - OTR_OPT_VALUE value<QList<double>>; - OTR_OPT_VALUE value<QList<float>>; - OTR_OPT_VALUE value<QList<int>>; - OTR_OPT_VALUE value<QList<bool>>; - OTR_OPT_VALUE value<QList<QString>>; - OTR_OPT_VALUE value<QList<slider_value>>; - OTR_OPT_VALUE value<QList<QPointF>>; - +#if !defined OTR_OPTIONS_INST_VALUE +# define OTR_OPTIONS_INST_VALUE OTR_TEMPLATE_IMPORT #endif +OTR_OPTIONS_INST_VALUE(value<double>) +OTR_OPTIONS_INST_VALUE(value<float>) +OTR_OPTIONS_INST_VALUE(value<int>) +OTR_OPTIONS_INST_VALUE(value<bool>) +OTR_OPTIONS_INST_VALUE(value<QString>) +OTR_OPTIONS_INST_VALUE(value<slider_value>) +OTR_OPTIONS_INST_VALUE(value<QPointF>) +OTR_OPTIONS_INST_VALUE(value<QVariant>) +OTR_OPTIONS_INST_VALUE(value<QList<double>>) +OTR_OPTIONS_INST_VALUE(value<QList<float>>) +OTR_OPTIONS_INST_VALUE(value<QList<int>>) +OTR_OPTIONS_INST_VALUE(value<QList<bool>>) +OTR_OPTIONS_INST_VALUE(value<QList<QString>>) +OTR_OPTIONS_INST_VALUE(value<QList<slider_value>>) +OTR_OPTIONS_INST_VALUE(value<QList<QPointF>>) +OTR_OPTIONS_INST_VALUE(value<QList<QVariant>>) + } // ns options |