Files
ts-qt/modern-qt/utility/wrapper/mutable-value.hh
2025-10-20 00:32:01 +08:00

154 lines
4.9 KiB
C++

#pragma once
#include "modern-qt/utility/wrapper/widget.hh"
#include <qobject.h>
#include <unordered_map>
namespace creeper {
template <typename T>
struct MutableValue final {
T value;
struct Functor {
virtual ~Functor() noexcept = default;
virtual void update(const T&) = 0;
};
std::unordered_map<QObject*, std::unique_ptr<Functor>> callbacks;
struct Nothing { };
std::shared_ptr<Nothing> alive = std::make_shared<Nothing>();
MutableValue(const MutableValue&) = delete;
MutableValue(MutableValue&&) = delete;
template <typename... Args>
explicit MutableValue(Args&&... args) noexcept
requires std::constructible_from<T, Args&&...>
: value { std::forward<Args>(args)... } { }
constexpr auto get() const noexcept -> T { return value; }
constexpr operator T() const noexcept { return get(); }
template <typename U>
auto set(U&& u) noexcept -> void
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
{
value = std::forward<U>(u);
for (const auto& [_, f] : callbacks)
f->update(value);
}
template <typename U>
auto set_silent(U&& u) noexcept -> void
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
{
value = std::forward<U>(u);
}
template <typename U>
auto operator=(U&& u) noexcept -> void
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
{
set(std::forward<U>(u));
}
};
template <class P, typename T>
struct MutableForward final : public P {
MutableValue<T>& mutable_value;
explicit MutableForward(MutableValue<T>& m) noexcept
requires std::constructible_from<P, T>
: mutable_value { m }
, P { m.get() } { }
template <template <typename> class Smart>
explicit MutableForward(const Smart<MutableValue<T>>& m) noexcept
requires std::constructible_from<P, T>
: MutableForward { *m } { }
explicit MutableForward(const P&, MutableValue<T>& m) noexcept
requires std::constructible_from<P, T>
: MutableForward { m } { }
template <template <typename> class Smart>
explicit MutableForward(const P&, const Smart<MutableValue<T>>& m) noexcept
requires std::constructible_from<P, T>
: MutableForward { m } { }
auto attach_callback_to_mutable(auto& widget) noexcept {
struct Functor : MutableValue<T>::Functor {
decltype(widget) w;
Functor(decltype(w) w) noexcept
: w { w } { }
~Functor() noexcept = default;
auto update(const T& value) -> void override {
w.apply(P { value });
w.update();
}
};
auto functor = std::make_unique<Functor>(widget);
mutable_value.callbacks[&widget] = std::move(functor);
auto alive = std::weak_ptr { mutable_value.alive };
QObject::connect(&widget, &QObject::destroyed, [this, alive](auto* key) {
if (alive.lock()) mutable_value.callbacks.erase(key);
});
}
auto apply(auto& widget) noexcept -> void {
P::apply(widget);
attach_callback_to_mutable(widget);
}
};
template <typename T, typename F>
struct MutableTransform : widget::pro::Token {
F apply_function;
MutableValue<T>& mutable_value;
explicit MutableTransform(F f, MutableValue<T>& t) noexcept
: mutable_value { t }
, apply_function { std::move(f) } { }
template <template <typename> class Smart>
explicit MutableTransform(F f, const Smart<MutableValue<T>>& m) noexcept
: MutableTransform { std::move(f), *m } { }
auto attach_callback_to_mutable(auto& widget) noexcept {
struct Functor : MutableValue<T>::Functor {
decltype(widget) w;
F apply_function;
Functor(decltype(widget) w, F f) noexcept
: w { w }
, apply_function { std::move(f) } { }
~Functor() noexcept = default;
auto update(const T& value) -> void override {
apply_function(w, value);
w.update();
}
};
auto functor = std::make_unique<Functor>(widget, std::move(apply_function));
mutable_value.callbacks[&widget] = std::move(functor);
auto alive = std::weak_ptr { mutable_value.alive };
QObject::connect(&widget, &QObject::destroyed, [this, alive](auto* key) {
if (alive.lock()) mutable_value.callbacks.erase(key);
});
}
auto apply(auto& widget) noexcept -> void {
apply_function(widget, mutable_value.get());
attach_callback_to_mutable(widget);
}
};
using MutableQString = MutableValue<QString>;
using MutableDouble = MutableValue<double>;
using MutableFloat = MutableValue<float>;
using MutableUInt8 = MutableValue<uint8_t>;
using MutableUInt16 = MutableValue<uint16_t>;
using MutableInt8 = MutableValue<int8_t>;
using MutableInt16 = MutableValue<int16_t>;
}