diff options
Diffstat (limited to 'options')
-rw-r--r-- | options/base-value.cpp | 13 | ||||
-rw-r--r-- | options/base-value.hpp | 12 | ||||
-rw-r--r-- | options/bundle.cpp | 128 | ||||
-rw-r--r-- | options/bundle.hpp | 60 | ||||
-rw-r--r-- | options/connector.cpp | 88 | ||||
-rw-r--r-- | options/connector.hpp | 27 | ||||
-rw-r--r-- | options/globals.cpp | 45 | ||||
-rw-r--r-- | options/globals.hpp | 21 | ||||
-rw-r--r-- | options/group.cpp | 20 | ||||
-rw-r--r-- | options/group.hpp | 15 | ||||
-rw-r--r-- | options/metatype.cpp | 2 | ||||
-rw-r--r-- | options/tie.cpp | 9 | ||||
-rw-r--r-- | options/tie.hpp | 27 | ||||
-rw-r--r-- | options/value-traits.hpp | 125 | ||||
-rw-r--r-- | options/value.hpp | 75 |
15 files changed, 321 insertions, 346 deletions
diff --git a/options/base-value.cpp b/options/base-value.cpp index bcbbd1dc..ec96fe6c 100644 --- a/options/base-value.cpp +++ b/options/base-value.cpp @@ -2,18 +2,15 @@ using namespace options; -value_::value_(bundle const& b, const QString& name, value_::comparator cmp, std::type_index type_idx) : - b(b), - self_name(name), - cmp(cmp), - type_index(type_idx) +value_::value_(bundle const& b, const QString& name) : + b(b), self_name(name) { - b->on_value_created(name, this); + b->on_value_created(this); } value_::~value_() { - b->on_value_destructed(self_name, this); + b->on_value_destructed(this); } void value_::notify() const @@ -23,7 +20,7 @@ void value_::notify() const namespace options::detail { -void set_base_value_to_default(value_* val) +void set_value_to_default(value_* val) { val->set_to_default(); } diff --git a/options/base-value.hpp b/options/base-value.hpp index 750f93f1..6a4709ee 100644 --- a/options/base-value.hpp +++ b/options/base-value.hpp @@ -15,7 +15,6 @@ #include <QPointF> #include <QVariant> -#include <typeindex> #include <utility> #define OTR_OPTIONS_SLOT(t) void setValue(t datum) { store_(datum); } @@ -29,13 +28,12 @@ class OTR_OPTIONS_EXPORT value_ : public QObject friend class detail::connector; - using comparator = bool(*)(const QVariant& val1, const QVariant& val2); template<typename t> using signal_sig = void(value_::*)(cv_qualified<t>) const; public: QString name() const { return self_name; } - value_(bundle const& b, const QString& name, comparator cmp, std::type_index type_idx); + value_(bundle const& b, const QString& name); ~value_() override; // no C++17 "constexpr inline" for data declarations in MSVC @@ -68,8 +66,6 @@ signals: protected: bundle b; QString self_name; - comparator cmp; - std::type_index type_index; virtual void store_variant(const QVariant& x) = 0; @@ -77,8 +73,7 @@ protected: void store_(const t& datum) { using traits = detail::value_traits<t>; - using stored_type = typename traits::stored_type; - store_variant(QVariant::fromValue<stored_type>(datum)); + store_variant(traits::qvariant_from_value(datum)); } public slots: @@ -98,11 +93,10 @@ public slots: OTR_OPTIONS_SLOT(const QList<slider_value>&) OTR_OPTIONS_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(value_* val); + friend void ::options::detail::set_value_to_default(value_* val); }; } //ns options diff --git a/options/bundle.cpp b/options/bundle.cpp index c426dd2e..0ab50037 100644 --- a/options/bundle.cpp +++ b/options/bundle.cpp @@ -10,6 +10,8 @@ #include "value.hpp" #include "globals.hpp" +#include <cstdlib> + #include <QThread> #include <QApplication> @@ -18,8 +20,15 @@ using namespace options::globals; namespace options::detail { -bundle::bundle(const QString& group_name) - : mtx(QMutex::Recursive), +mutex::mutex(QMutex::RecursionMode mode) : QMutex(mode) {} + +mutex::operator QMutex*() const +{ + return const_cast<QMutex*>(static_cast<const QMutex*>(this)); +} + +bundle::bundle(const QString& group_name) : + mtx(QMutex::Recursive), group_name(group_name), saved(group_name), transient(saved) @@ -30,19 +39,25 @@ bundle::~bundle() = default; void bundle::reload() { - if (group_name.size()) + if (!is_ini_modified()) + return; + + if (!group_name.isEmpty()) { QMutexLocker l(&mtx); + + // XXX what do we do when values are and aren't equal? + // see QPointF -sh 20180830 + + // XXX we could probably skip assigning to `saved' -sh 20180830 saved = group(group_name); - const bool has_changes = is_modified(); transient = saved; - if (has_changes) - { - connector::notify_all_values(); - emit reloading(); - emit changed(); - } + mark_ini_modified(false); + + connector::notify_all_values(); + emit reloading(); + emit changed(); } } @@ -50,10 +65,9 @@ void bundle::set_all_to_default() { QMutexLocker l(&mtx); - forall([](const QString&, value_* val) { set_base_value_to_default(val); }); - - if (is_modified()) - mark_ini_modified(); + forall([](value_* val) { + set_value_to_default(val); + }); } void bundle::store_kv(const QString& name, const QVariant& new_value) @@ -62,16 +76,21 @@ void bundle::store_kv(const QString& name, const QVariant& new_value) if (!group_name.isEmpty()) { - const QVariant old_value = transient.get_variant(name); - if (!connector::is_equal(name, old_value, new_value)) - { - transient.put(name, new_value); - connector::notify_values(name); - emit changed(); - } + transient.put(name, new_value); + + mark_ini_modified(); + + connector::notify_values(name); + emit changed(); } } +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); @@ -83,56 +102,18 @@ void bundle::save() if (QThread::currentThread() != qApp->thread()) 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(); - } - } - - if (modified_) - { - qDebug() << "saving" << name(); - emit saving(); - } -} - -bool bundle::is_modified() const -{ - 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; - } - } - - for (const auto& kv : saved.kvs) - { - if (!transient.contains(kv.first)) - { - //if (logspam) - // qDebug() << "bundle" << group_name << "modified" << "key" << kv.first << "-" << other << "<>" << kv.second; - return true; - } + saved = transient; + saved.save(); } - return false; + qDebug() << "saving" << name(); + emit saving(); } void bundler::after_profile_changed_() @@ -159,11 +140,13 @@ void bundler::refresh_all_bundles() 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()) { @@ -177,11 +160,14 @@ 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); @@ -194,13 +180,11 @@ bundler& bundler::bundler_singleton() return ret; } -QMutex* bundle::get_mtx() const { return mtx; } - } // ns 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::bundler::bundler_singleton().make_bundle_(name); diff --git a/options/bundle.hpp b/options/bundle.hpp index 98291fc5..9ab7f74c 100644 --- a/options/bundle.hpp +++ b/options/bundle.hpp @@ -13,7 +13,7 @@ #include <memory> #include <tuple> -#include <map> +#include <unordered_map> #include <memory> #include <vector> @@ -21,13 +21,21 @@ #include <QString> #include <QVariant> #include <QMutex> -#include <QMutexLocker> #include <QDebug> +#include "compat/qhash.hpp" + #include "export.hpp" namespace options::detail { + class OTR_OPTIONS_EXPORT mutex final : public QMutex + { + public: + explicit mutex(QMutex::RecursionMode mode); + cc_noinline operator QMutex*() const; // NOLINT + }; + class bundle; } // ns options::detail @@ -39,20 +47,10 @@ namespace options { namespace options::detail { -struct bundler; - class OTR_OPTIONS_EXPORT bundle final : public QObject, public connector { Q_OBJECT - class OTR_OPTIONS_EXPORT mutex final : public QMutex - { - public: - explicit mutex(QMutex::RecursionMode mode) : QMutex(mode) {} - operator QMutex*() const { return const_cast<QMutex*>(static_cast<const QMutex*>(this)); } - }; - -private: mutex mtx; const QString group_name; group saved; @@ -65,24 +63,18 @@ signals: public: bundle(const bundle&) = delete; - bundle(bundle&&) = delete; - bundle& operator=(bundle&&) = delete; bundle& operator=(const bundle&) = delete; - QMutex* get_mtx() const override; - cc_noinline explicit bundle(const QString& group_name); - cc_noinline ~bundle() override; + QMutex* get_mtx() const override { return mtx; } QString name() const { return group_name; } - cc_noinline void store_kv(const QString& name, const QVariant& datum); - cc_noinline bool contains(const QString& name) const; - cc_noinline 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, const QVariant& datum); + bool contains(const QString& name) const; + + QVariant get_variant(const QString& name) const; public slots: void save(); @@ -97,27 +89,25 @@ struct OTR_OPTIONS_EXPORT bundler final using weak = std::weak_ptr<v>; using shared = std::shared_ptr<v>; + static void refresh_all_bundles(); + private: QMutex implsgl_mtx { QMutex::Recursive }; - std::map<k, weak> implsgl_data; - void after_profile_changed_(); + std::unordered_map<k, weak> implsgl_data {}; -public: - static void refresh_all_bundles(); + void after_profile_changed_(); -private: 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& bundler_singleton(); + [[nodiscard]] std::shared_ptr<v> make_bundle_(const k& key); + [[nodiscard]] static bundler& bundler_singleton(); bundler(); ~bundler(); }; -void set_base_value_to_default(value_* val); +void set_value_to_default(value_* val); } // ns options::detail diff --git a/options/connector.cpp b/options/connector.cpp index 73aefed8..838a2e0e 100644 --- a/options/connector.cpp +++ b/options/connector.cpp @@ -9,99 +9,57 @@ #include "connector.hpp" #include "value.hpp" -#include <utility> - namespace options::detail { -static bool generic_is_equal(const QVariant& val1, const QVariant& val2) -{ - return val1 == val2; -} - +connector::connector() = default; connector::~connector() = default; -bool connector::is_equal(const QString& name, const QVariant& val1, const QVariant& val2) const +void connector::on_value_destructed(value_type val) { - QMutexLocker l(get_mtx()); + const QString& name = val->name(); - auto it = connected_values.find(name); - - if (it != connected_values.cend() && !std::get<0>((*it).second).empty()) - 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) -{ QMutexLocker l(get_mtx()); - auto it = connected_values.find(name); + const auto it = connected_values.find(name); if (it != connected_values.end()) { - std::vector<value_type>& values = std::get<0>((*it).second); - for (auto it = values.begin(); it != values.end(); it++) + value_vec& values = it->second; + for (auto it = values.begin(); it != values.end(); ) { if (*it == val) - { - values.erase(it); - return true; - } + it = values.erase(it); + else + it++; } } - return false; -} - -void connector::on_value_destructed(const QString& name, value_type val) -{ - if (!name.size()) - return; - - 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); + if (it != connected_values.end() && it->second.empty()) + connected_values.erase(it); } -void connector::on_value_created(const QString& name, value_type val) +void connector::on_value_created(value_type val) { - if (!name.size()) + const QString& name = val->name(); + + 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); } } @@ -109,17 +67,15 @@ void connector::notify_values(const QString& name) const { auto it = connected_values.find(name); if (it != connected_values.cend()) - for (value_type val : std::get<0>((*it).second)) + for (value_type val : it->second) val->bundle_value_changed(); } void connector::notify_all_values() const { - for (auto& [k, v] : connected_values) - for (value_type val : std::get<0>(v)) + for (const auto& [k, v] : connected_values) + for (value_type val : v) val->bundle_value_changed(); } -connector::connector() = default; - } // ns options::detail diff --git a/options/connector.hpp b/options/connector.hpp index 26812e7f..11aa94da 100644 --- a/options/connector.hpp +++ b/options/connector.hpp @@ -8,15 +8,13 @@ #pragma once -#include <map> +#include <unordered_map> #include <vector> -#include <tuple> -#include <typeinfo> -#include <typeindex> -#include <QVariant> + #include <QString> #include <QMutex> -#include <QMutexLocker> + +#include "compat/qhash.hpp" #include "export.hpp" @@ -32,13 +30,10 @@ class OTR_OPTIONS_EXPORT connector 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; @@ -51,20 +46,16 @@ protected: QMutexLocker l(get_mtx()); for (auto& pair : connected_values) - for (auto& val : std::get<0>(pair.second)) - fun(pair.first, val); + for (auto& val : pair.second) + fun(val); } 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 options::detail diff --git a/options/globals.cpp b/options/globals.cpp index 85a6bdf2..33327090 100644 --- a/options/globals.cpp +++ b/options/globals.cpp @@ -9,6 +9,8 @@ namespace options::globals::detail { +ini_ctx::ini_ctx() = default; + bool is_portable_installation() { #if defined _WIN32 @@ -23,11 +25,11 @@ saver_::~saver_() { if (--ctx.refcount == 0 && ctx.modifiedp) { - ctx.modifiedp = false; 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(); } @@ -41,7 +43,6 @@ ini_ctx& cur_settings() { static ini_ctx ini; const QString pathname = ini_pathname(); - static QString ini_pathname; ini.mtx.lock(); @@ -51,7 +52,7 @@ ini_ctx& cur_settings() ini.pathname = pathname; } - if (pathname != ini_pathname) + if (ini.pathname != pathname) { ini.qsettings.emplace(pathname, QSettings::IniFormat); ini.pathname = pathname; @@ -66,15 +67,20 @@ ini_ctx& global_settings() ini.mtx.lock(); - if (!is_portable_installation()) - // Windows registry or xdg on Linux - ini.qsettings.emplace(OPENTRACK_ORG); - else + if (ini.pathname.isEmpty()) { - static const QString pathname = OPENTRACK_BASE_PATH + QStringLiteral("/globals.ini"); - // file in executable's directory - ini.qsettings.emplace(pathname, QSettings::IniFormat); - ini.pathname = pathname; + if (!is_portable_installation()) + // Windows registry or xdg on Linux + ini.qsettings.emplace(OPENTRACK_ORG); + else + { + static const QString pathname = OPENTRACK_BASE_PATH + QStringLiteral("/globals.ini"); + // file in executable's directory + ini.qsettings.emplace(pathname, QSettings::IniFormat); + ini.pathname = pathname; + } + + ini.pathname = "placeholder"; } return ini; @@ -85,6 +91,16 @@ ini_ctx& global_settings() 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) { @@ -119,11 +135,10 @@ QStringList ini_list() return list; } -void mark_ini_modified() +void mark_ini_modified(bool value) { - using namespace detail; auto& ini = cur_settings(); - ini.modifiedp = true; + ini.modifiedp = value; ini.mtx.unlock(); } @@ -138,7 +153,7 @@ QString ini_directory() static const QString subdir = "ini"; if (!QDir(dir).mkpath(subdir)) - return QString(); + return {}; return dir + '/' + subdir; } diff --git a/options/globals.hpp b/options/globals.hpp index 4d74cbf0..8032e5d8 100644 --- a/options/globals.hpp +++ b/options/globals.hpp @@ -11,20 +11,18 @@ namespace options::globals::detail { -struct ini_ctx; struct saver_; -OTR_OPTIONS_EXPORT ini_ctx& cur_settings(); -OTR_OPTIONS_EXPORT ini_ctx& global_settings(); -OTR_OPTIONS_EXPORT bool is_portable_installation(); - -struct ini_ctx +struct OTR_OPTIONS_EXPORT ini_ctx { std::optional<QSettings> qsettings { std::in_place }; + QString pathname; + QMutex mtx { QMutex::Recursive }; + int refcount = 0; bool modifiedp = false; - QMutex mtx { QMutex::Recursive }; - QString pathname; + + ini_ctx(); }; struct OTR_OPTIONS_EXPORT saver_ final @@ -44,11 +42,16 @@ auto with_settings_object_(ini_ctx& ini, F&& fun) return fun(*ini.qsettings); } +OTR_OPTIONS_EXPORT ini_ctx& cur_settings(); +OTR_OPTIONS_EXPORT ini_ctx& global_settings(); +OTR_OPTIONS_EXPORT bool is_portable_installation(); + } // ns options::globals::detail namespace options::globals { - OTR_OPTIONS_EXPORT void mark_ini_modified(); + OTR_OPTIONS_EXPORT void mark_ini_modified(bool value = true); + OTR_OPTIONS_EXPORT bool is_ini_modified(); OTR_OPTIONS_EXPORT QString ini_directory(); OTR_OPTIONS_EXPORT QString ini_filename(); OTR_OPTIONS_EXPORT QString ini_pathname(); diff --git a/options/group.cpp b/options/group.cpp index 5f6c3232..af66aaf2 100644 --- a/options/group.cpp +++ b/options/group.cpp @@ -14,9 +14,7 @@ #include <algorithm> #include <QFile> - #include <QDir> -#include <QDebug> namespace options::detail { @@ -29,14 +27,8 @@ group::group(const QString& name) : name(name) 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(); }); } @@ -48,11 +40,9 @@ void group::save() const 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(); }); } @@ -64,7 +54,7 @@ void group::put(const QString &s, const QVariant &d) bool group::contains(const QString &s) const { const auto it = kvs.find(s); - return it != kvs.cend() && it->second != QVariant::Invalid; + return it != kvs.cend(); } QVariant group::get_variant(const QString& name) const @@ -73,7 +63,7 @@ QVariant group::get_variant(const QString& name) const if (it != kvs.cend()) return it->second; - return {}; + return QVariant(); } } // ns options::detail diff --git a/options/group.hpp b/options/group.hpp index ef9f1121..76bb939b 100644 --- a/options/group.hpp +++ b/options/group.hpp @@ -4,21 +4,16 @@ #include "compat/base-path.hpp" #include "compat/library-path.hpp" -#include "compat/qhash.hpp" #include "compat/macros.hpp" +#include "compat/qhash.hpp" #include "export.hpp" #include <optional> #include <unordered_map> -#include <QHash> #include <QString> -#include <QMutex> -#include <QFile> -#include <QDir> -#include <QStandardPaths> #include <QVariant> -#include <QSettings> + #include <QDebug> // XXX TODO remove qsettings usage -sh 20180624 @@ -36,12 +31,6 @@ namespace options::detail { void put(const QString& s, const QVariant& d); bool contains(const QString& s) const; - template<typename t> - cc_noinline t get(const QString& k) const - { - return get_variant(k).value<t>(); - } - cc_noinline QVariant get_variant(const QString& name) const; }; } // ns options::detail diff --git a/options/metatype.cpp b/options/metatype.cpp index b6df94ca..44f30187 100644 --- a/options/metatype.cpp +++ b/options/metatype.cpp @@ -22,7 +22,7 @@ int declare_metatype_for_type(const char* str) #define OPENTRACK_DEFINE_METATYPE4(t, sym) \ class sym { \ static const int dribble; \ - } sym ## _singleton; \ + } sym; \ const int sym :: dribble = ::options::detail::declare_metatype_for_type<t>(#t); #define OPENTRACK_DEFINE_METATYPE(t) OPENTRACK_DEFINE_METATYPE2(t, __COUNTER__) diff --git a/options/tie.cpp b/options/tie.cpp index 1f2d7dae..671a72c2 100644 --- a/options/tie.cpp +++ b/options/tie.cpp @@ -8,6 +8,11 @@ #include "tie.hpp" #include "compat/run-in-thread.hpp" +#include "compat/macros.hpp" + +#include "value-traits.hpp" + +#include <cmath> namespace options { @@ -141,9 +146,9 @@ void tie_setting(value<slider_value>& v, QSlider* w) { 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 f2ec751d..c2607a6c 100644 --- a/options/tie.hpp +++ b/options/tie.hpp @@ -36,20 +36,17 @@ 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()); - 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); + 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); value_::connect(&v, value_::value_changed<int>(), - cb, [cb](int x) { + cb, [cb](int x) { run_in_thread_sync(cb, [=] { cb->setCurrentIndex(cb->findData(x)); }); - }, - v.DIRECT_CONNTYPE); + }, v.DIRECT_CONNTYPE); } template<typename t, typename From, typename To> @@ -79,8 +76,8 @@ void tie_setting(value<t>& v, QLabel* lb, F&& fun) closure(v()); value_::connect(&v, value_::value_changed<t>(), - lb, closure, - v.SAFE_CONNTYPE); + lb, closure, + v.SAFE_CONNTYPE); } template<typename t, typename F> @@ -92,8 +89,8 @@ void tie_setting(value<t>& v, QObject* obj, F&& fun) fun(v()); value_::connect(&v, value_::value_changed<t>(), - obj, fun, - v.DIRECT_CONNTYPE); + obj, fun, + v.DIRECT_CONNTYPE); } OTR_OPTIONS_EXPORT void tie_setting(value<int>& v, QComboBox* cb); diff --git a/options/value-traits.hpp b/options/value-traits.hpp index 1c98f3e9..3ab623da 100644 --- a/options/value-traits.hpp +++ b/options/value-traits.hpp @@ -1,62 +1,145 @@ #pragma once -#include "export.hpp" - #include "slider.hpp" +#include "export.hpp" #include <QString> +#include <cinttypes> #include <type_traits> namespace options::detail { +template<typename t, typename Enable = void> +struct value_traits; + template<typename t, typename u = t, typename Enable = void> struct default_value_traits { - virtual ~default_value_traits() = default; + using stored_type = std::decay_t<u>; + using value_type = std::decay_t<t>; - using stored_type = std::decay_t<t>; - using value_type = std::decay_t<u>; + using self = value_traits<t>; - static value_type from_value(const value_type& val, const value_type&) { return val; } - static value_type from_storage(const stored_type& x) { return static_cast<value_type>(x); } - static stored_type to_storage(const value_type& val) { return static_cast<stored_type>(val); } + static value_type value_with_default(const value_type& val, const value_type&) + { + return val; + } - static value_type value_from_variant(const QVariant& x) + static value_type value_from_storage(const stored_type& x) { - return from_storage(storage_from_variant(x)); + return static_cast<value_type>(x); } - static stored_type storage_from_variant(const QVariant& x) + static stored_type storage_from_value(const value_type& val) { + return static_cast<stored_type>(val); + } + + static value_type value_from_qvariant(const QVariant& x) + { + return self::value_from_storage(self::storage_from_qvariant(x)); + } + + static QVariant qvariant_from_value(const value_type& val) + { + return self::qvariant_from_storage(self::storage_from_value(val)); + } + + static constexpr inline + value_type pass_value(const value_type& x) + { + if constexpr(std::is_same_v<value_type, stored_type>) + return x; + else + return self::value_from_storage(self::storage_from_value(x)); + } + + static stored_type storage_from_qvariant(const QVariant& x) + { + // XXX TODO return x.value<stored_type>(); } + + static QVariant qvariant_from_storage(const stored_type& val) + { + // XXX TODO + return QVariant::fromValue<stored_type>(val); + } + + static bool is_equal(const value_type& x, const 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> +struct value_traits : default_value_traits<t> {}; + +template<> +struct value_traits<double> : default_value_traits<double> { + static bool is_equal(value_type x, value_type y) + { + if (x == y) + return true; + else + { + using I = std::int64_t; + constexpr int K = 1000; + + value_type x_, y_; + + return I(std::round(std::modf(x, &x_) * K)) == I(std::round(std::modf(y, &y_) * K)) && + I(std::round(x_)) == I(std::round(y_)); + } + } }; -template<> -struct value_traits<slider_value> : default_value_traits<slider_value> +template<> struct value_traits<bool> : default_value_traits<bool, int> { - static inline slider_value from_value(const slider_value& val, const slider_value& def) + static stored_type storage_from_qvariant(const QVariant& x) { - return { val.cur(), def.min(), def.max() }; + if (x.type() == QVariant::String) + return x.toBool(); + else + return !!x.toInt(); + } + + static QVariant qvariant_from_storage(const stored_type& val) + { + return QVariant::fromValue<int>(!!val); + } + + static value_type value_from_storage(const stored_type& x) + { + return !!x; } }; -// 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<> struct value_traits<float> : value_traits<float, double> { + static constexpr inline value_type pass_value(const value_type& x) { return x; } }; template<> -struct value_traits<float> : public default_value_traits<float, double, void> +struct value_traits<slider_value> : default_value_traits<slider_value> { + static slider_value value_with_default(const slider_value& val, const slider_value& def) + { + return { val.cur(), def.min(), def.max() }; + } + + static bool is_equal(const slider_value& x, const slider_value& y) + { + using tr = value_traits<double>; + return tr::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 diff --git a/options/value.hpp b/options/value.hpp index bfa8b352..887f0d39 100644 --- a/options/value.hpp +++ b/options/value.hpp @@ -23,51 +23,47 @@ namespace options::detail { template<typename t> - struct dereference_wrapper final + class dereference_wrapper final { - cc_forceinline constexpr t const* operator->() const { return &x; } - cc_forceinline constexpr t* operator->() { return &x; } t x; - constexpr explicit cc_forceinline dereference_wrapper(t&& x) : x(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> +template<typename u> class value final : public value_ { + using t = std::conditional_t<std::is_enum_v<remove_cvref_t<u>>, + std::decay_t<u>, + remove_cvref_t<u>>; const t def; using traits = detail::value_traits<t>; - using stored_type = typename traits::stored_type; - - static bool is_equal(const QVariant& val1, const QVariant& val2) - { - return val1.value<stored_type>() == val2.value<stored_type>(); - } cc_noinline t get() const { - if (self_name.isEmpty()) - return def; + if (self_name.isEmpty() || !b->contains(self_name)) + return traits::pass_value(def); - QVariant variant = b->get<QVariant>(self_name); + QVariant variant = b->get_variant(self_name); - if (!b->contains(self_name) || variant.type() == QVariant::Invalid) - return def; + if (variant.isNull() || !variant.isValid()) + return traits::pass_value(def); - const stored_type x { variant.value<stored_type>() }; - - return traits::from_value(traits::from_storage(x), def); + return traits::pass_value(traits::value_with_default(traits::value_from_qvariant(variant), def)); } friend class detail::connector; void bundle_value_changed() const override { if (!self_name.isEmpty()) - emit valueChanged(traits::to_storage(get())); + emit valueChanged(traits::storage_from_value(get())); } void store_variant(const QVariant& value) override @@ -75,21 +71,20 @@ class value final : public value_ if (self_name.isEmpty()) return; - if (value.type() == qMetaTypeId<stored_type>()) + if (traits::is_equal(get(), traits::value_from_qvariant(value))) + return; + + if (value.isValid() && !value.isNull()) b->store_kv(self_name, value); else - operator=(traits::value_from_variant(value)); + b->store_kv(self_name, traits::qvariant_from_value(def)); } public: cc_noinline - value<t>& operator=(const t& datum) + value<u>& operator=(const t& datum) { - if (self_name.isEmpty()) - return *this; - - if (datum != get()) - b->store_kv(self_name, QVariant::fromValue<stored_type>(traits::to_storage(datum))); + store_variant(traits::qvariant_from_value(traits::pass_value(datum))); return *this; } @@ -99,13 +94,8 @@ public: cc_noinline value(bundle b, const QString& name, t def) : - value_(b, name, &is_equal, std::type_index(typeid(stored_type))), - def(def) + value_(b, name), def(def) { - if (!self_name.isEmpty()) - QObject::connect(b.get(), &detail::bundle::reloading, - this, &value_::reload, - DIRECT_CONNTYPE); } cc_noinline @@ -120,7 +110,7 @@ public: *this = def; } - operator t() const { return get(); } // NOLINT + operator t() const { return get(); } template<typename u, typename = decltype(static_cast<u>(std::declval<t>()))> explicit cc_forceinline operator u() const { return to<u>(); } @@ -130,22 +120,13 @@ public: return detail::dereference_wrapper<t>{get()}; } - cc_noinline - void reload() override - { -#if 0 - if (!self_name.isEmpty()) - store(traits::to_storage(get())); -#endif - } - cc_forceinline t operator()() const { return get(); } cc_forceinline t operator*() const { return get(); } - template<typename u> - u to() const + template<typename w> + w to() const { - return static_cast<u>(get()); + return static_cast<w>(get()); } }; |