summaryrefslogtreecommitdiffhomepage
path: root/options
diff options
context:
space:
mode:
Diffstat (limited to 'options')
-rw-r--r--options/base-value.cpp13
-rw-r--r--options/base-value.hpp12
-rw-r--r--options/bundle.cpp128
-rw-r--r--options/bundle.hpp60
-rw-r--r--options/connector.cpp88
-rw-r--r--options/connector.hpp27
-rw-r--r--options/globals.cpp45
-rw-r--r--options/globals.hpp21
-rw-r--r--options/group.cpp20
-rw-r--r--options/group.hpp15
-rw-r--r--options/metatype.cpp2
-rw-r--r--options/tie.cpp9
-rw-r--r--options/tie.hpp27
-rw-r--r--options/value-traits.hpp125
-rw-r--r--options/value.hpp75
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());
}
};