feat:data slove and update heatmap
This commit is contained in:
@@ -1,234 +1,234 @@
|
||||
#pragma once
|
||||
|
||||
#include "property.hh"
|
||||
|
||||
#include <qbitmap.h>
|
||||
#include <qvector.h>
|
||||
#include <qdebug.h>
|
||||
#include <qicon.h>
|
||||
#include <qwidget.h>
|
||||
|
||||
namespace creeper::common {
|
||||
template <typename Instance>
|
||||
struct Token {
|
||||
void apply(auto& self) const {
|
||||
const auto self_name = typeid(self).name();
|
||||
const auto prop_name = typeid(this).name();
|
||||
qDebug() << "Unimplemented" << prop_name << "is called by" << self_name;
|
||||
}
|
||||
};
|
||||
|
||||
namespace pro {
|
||||
|
||||
// 设置组建透明度
|
||||
template <class Token>
|
||||
using Opacity = SetterProp<Token, double, [](auto& self, double v) { self.set_opacity(v); }>;
|
||||
|
||||
// 设置圆角(NXNY)
|
||||
template <class Token>
|
||||
using RadiusNxNy =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_radius_nx_ny(v); }>;
|
||||
|
||||
// 设置圆角(PXPY)
|
||||
template <class Token>
|
||||
using RadiusPxPy =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_radius_px_py(v); }>;
|
||||
|
||||
// 设置圆角(NXPY)
|
||||
template <class Token>
|
||||
using RadiusNxPy =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_radius_nx_py(v); }>;
|
||||
|
||||
// 设置圆角(PXNY)
|
||||
template <class Token>
|
||||
using RadiusPxNy =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_radius_px_ny(v); }>;
|
||||
|
||||
// 设置圆角(X方向)
|
||||
template <class Token>
|
||||
using RadiusX = SetterProp<Token, double, [](auto& self, double v) { self.set_radius_x(v); }>;
|
||||
|
||||
// 设置圆角(Y方向)
|
||||
template <class Token>
|
||||
using RadiusY = SetterProp<Token, double, [](auto& self, double v) { self.set_radius_y(v); }>;
|
||||
|
||||
// 设置通用圆角
|
||||
template <class Token>
|
||||
using Radius = SetterProp<Token, double, [](auto& self, double v) { self.set_radius(v); }>;
|
||||
|
||||
// 通用边界宽度
|
||||
template <class Token>
|
||||
using BorderWidth =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_border_width(v); }>;
|
||||
|
||||
// 通用边界颜色
|
||||
template <class Token>
|
||||
using BorderColor =
|
||||
SetterProp<Token, QColor, [](auto& self, const QColor& v) { self.set_border_color(v); }>;
|
||||
|
||||
// 通用文字颜色
|
||||
template <class Token>
|
||||
using TextColor =
|
||||
SetterProp<Token, QColor, [](auto& self, const QColor& v) { self.set_text_color(v); }>;
|
||||
|
||||
// 通用背景颜色
|
||||
template <class Token>
|
||||
using Background =
|
||||
SetterProp<Token, QColor, [](auto& self, const QColor& v) { self.set_background(v); }>;
|
||||
|
||||
// 通用水波纹颜色
|
||||
template <class Token>
|
||||
using WaterColor =
|
||||
SetterProp<Token, QColor, [](auto& self, const QColor& v) { self.set_water_color(v); }>;
|
||||
|
||||
// 通用禁止属性
|
||||
template <class Token>
|
||||
using Disabled = SetterProp<Token, bool, [](auto& self, bool v) { self.set_disabled(v); }>;
|
||||
|
||||
// 通用 Checked 属性
|
||||
template <class Token>
|
||||
using Checked = SetterProp<Token, bool, [](auto& self, bool v) { self.set_checked(v); }>;
|
||||
|
||||
// 通用文本属性
|
||||
template <class Token, auto setter>
|
||||
struct String : public QString, Token {
|
||||
using QString::QString;
|
||||
|
||||
explicit String(const QString& text) noexcept
|
||||
: QString { text } { }
|
||||
explicit String(const std::string& text) noexcept
|
||||
: QString { QString::fromStdString(text) } { }
|
||||
|
||||
auto operator=(const QString& text) noexcept {
|
||||
QString::operator=(text);
|
||||
return *this;
|
||||
}
|
||||
auto operator=(QString&& text) noexcept {
|
||||
QString::operator=(std::move(text));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void apply(auto& self) const
|
||||
requires requires { setter(self, *this); }
|
||||
{
|
||||
setter(self, *this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Token, typename T, auto setter>
|
||||
struct Vector : public QVector<T>, Token {
|
||||
using QVector<T>::QVector;
|
||||
|
||||
explicit Vector(const QVector<T>& vec) noexcept
|
||||
: QVector<T> { vec } { }
|
||||
explicit Vector(const std::vector<T>& vec) noexcept
|
||||
: QVector<T> { vec } { }
|
||||
|
||||
void apply(auto& self) const
|
||||
requires requires {setter(self, *this); }
|
||||
{
|
||||
setter(self, *this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Token, typename T, std::size_t N, auto setter>
|
||||
struct Array : public std::array<T, N>, Token {
|
||||
using std::array<T, N>::array;
|
||||
explicit Array(const std::array<T, N>& arr) noexcept
|
||||
: std::array<T, N> { arr } { }
|
||||
|
||||
template <typename... Args>
|
||||
requires (sizeof...(Args) == N)
|
||||
explicit Array(Args&&... args) noexcept
|
||||
: std::array<T, N> {
|
||||
std::forward<Args>(args)...} {}
|
||||
|
||||
void apply(auto& self) const
|
||||
requires requires {setter(self, *this);}
|
||||
{
|
||||
setter(self, *this);
|
||||
}
|
||||
};
|
||||
|
||||
// template<class Token, typename T, auto setter>
|
||||
// Vector<Token, T, setter>::Vector(const QVector<T> &vec) noexcept:QVector { vec } { }
|
||||
|
||||
template <class Token>
|
||||
using Text = String<Token, [](auto& self, const auto& string) { self.setText(string); }>;
|
||||
|
||||
// 通用指针绑定
|
||||
template <class Token, class Final>
|
||||
struct Bind : Token {
|
||||
Final*& widget;
|
||||
explicit Bind(Final*& p)
|
||||
: widget(p) { };
|
||||
void apply(Final& self) const { widget = &self; }
|
||||
};
|
||||
|
||||
// 通用点击事件
|
||||
template <typename Callback, class Token>
|
||||
struct Clickable : Token {
|
||||
Callback callback;
|
||||
explicit Clickable(Callback callback) noexcept
|
||||
: callback { std::move(callback) } { }
|
||||
auto apply(auto& self) const noexcept -> void
|
||||
requires std::invocable<Callback, decltype(self)> || std::invocable<Callback>
|
||||
{
|
||||
using widget_t = std::remove_cvref_t<decltype(self)>;
|
||||
QObject::connect(&self, &widget_t::clicked, [function = callback, &self] {
|
||||
if constexpr (std::invocable<Callback, decltype(self)>) function(self);
|
||||
if constexpr (std::invocable<Callback>) function();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Callback, class Token>
|
||||
struct IndexChanged : Token {
|
||||
Callback callback;
|
||||
explicit IndexChanged(Callback callback) noexcept
|
||||
: callback {std::move(callback)} {}
|
||||
auto apply(auto& self) const noexcept -> void
|
||||
requires std::invocable<Callback, decltype(self)> || std::invocable<Callback> {
|
||||
using widget_t = std::remove_cvref_t<decltype(self)>;
|
||||
QObject::connect(&self, &widget_t::currentIndexChanged, [function = callback, &self] {
|
||||
if constexpr (std::invocable<Callback, decltype(self)>) function(self);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 自定义信号回调注册
|
||||
|
||||
namespace internal {
|
||||
template <typename T>
|
||||
struct FunctionArgs;
|
||||
|
||||
template <class C, class R, class... Args>
|
||||
struct FunctionArgs<auto (C::*)(Args...)->R> {
|
||||
using type = std::tuple<Args...>;
|
||||
};
|
||||
template <class C, class R, class... Args>
|
||||
struct FunctionArgs<auto (C::*)(Args...) const->R> {
|
||||
using type = std::tuple<Args...>;
|
||||
};
|
||||
|
||||
template <typename F, typename Tuple>
|
||||
concept tuple_invocable_trait = requires(F&& f, Tuple&& t) {
|
||||
std::apply(std::forward<F>(f), std::forward<Tuple>(t)); //
|
||||
};
|
||||
}
|
||||
|
||||
template <typename F, class Token, auto signal>
|
||||
struct SignalInjection : Token {
|
||||
F f;
|
||||
|
||||
using SignalArgs = typename internal::FunctionArgs<decltype(signal)>::type;
|
||||
|
||||
explicit SignalInjection(F f) noexcept
|
||||
requires internal::tuple_invocable_trait<F, SignalArgs>
|
||||
: f { std::forward<decltype(f)>(f) } { }
|
||||
|
||||
auto apply(auto& self) const noexcept { QObject::connect(&self, signal, f); }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "property.hh"
|
||||
|
||||
#include <qbitmap.h>
|
||||
#include <qvector.h>
|
||||
#include <qdebug.h>
|
||||
#include <qicon.h>
|
||||
#include <qwidget.h>
|
||||
|
||||
namespace creeper::common {
|
||||
template <typename Instance>
|
||||
struct Token {
|
||||
void apply(auto& self) const {
|
||||
const auto self_name = typeid(self).name();
|
||||
const auto prop_name = typeid(this).name();
|
||||
qDebug() << "Unimplemented" << prop_name << "is called by" << self_name;
|
||||
}
|
||||
};
|
||||
|
||||
namespace pro {
|
||||
|
||||
// 设置组建透明度
|
||||
template <class Token>
|
||||
using Opacity = SetterProp<Token, double, [](auto& self, double v) { self.set_opacity(v); }>;
|
||||
|
||||
// 设置圆角(NXNY)
|
||||
template <class Token>
|
||||
using RadiusNxNy =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_radius_nx_ny(v); }>;
|
||||
|
||||
// 设置圆角(PXPY)
|
||||
template <class Token>
|
||||
using RadiusPxPy =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_radius_px_py(v); }>;
|
||||
|
||||
// 设置圆角(NXPY)
|
||||
template <class Token>
|
||||
using RadiusNxPy =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_radius_nx_py(v); }>;
|
||||
|
||||
// 设置圆角(PXNY)
|
||||
template <class Token>
|
||||
using RadiusPxNy =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_radius_px_ny(v); }>;
|
||||
|
||||
// 设置圆角(X方向)
|
||||
template <class Token>
|
||||
using RadiusX = SetterProp<Token, double, [](auto& self, double v) { self.set_radius_x(v); }>;
|
||||
|
||||
// 设置圆角(Y方向)
|
||||
template <class Token>
|
||||
using RadiusY = SetterProp<Token, double, [](auto& self, double v) { self.set_radius_y(v); }>;
|
||||
|
||||
// 设置通用圆角
|
||||
template <class Token>
|
||||
using Radius = SetterProp<Token, double, [](auto& self, double v) { self.set_radius(v); }>;
|
||||
|
||||
// 通用边界宽度
|
||||
template <class Token>
|
||||
using BorderWidth =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.set_border_width(v); }>;
|
||||
|
||||
// 通用边界颜色
|
||||
template <class Token>
|
||||
using BorderColor =
|
||||
SetterProp<Token, QColor, [](auto& self, const QColor& v) { self.set_border_color(v); }>;
|
||||
|
||||
// 通用文字颜色
|
||||
template <class Token>
|
||||
using TextColor =
|
||||
SetterProp<Token, QColor, [](auto& self, const QColor& v) { self.set_text_color(v); }>;
|
||||
|
||||
// 通用背景颜色
|
||||
template <class Token>
|
||||
using Background =
|
||||
SetterProp<Token, QColor, [](auto& self, const QColor& v) { self.set_background(v); }>;
|
||||
|
||||
// 通用水波纹颜色
|
||||
template <class Token>
|
||||
using WaterColor =
|
||||
SetterProp<Token, QColor, [](auto& self, const QColor& v) { self.set_water_color(v); }>;
|
||||
|
||||
// 通用禁止属性
|
||||
template <class Token>
|
||||
using Disabled = SetterProp<Token, bool, [](auto& self, bool v) { self.set_disabled(v); }>;
|
||||
|
||||
// 通用 Checked 属性
|
||||
template <class Token>
|
||||
using Checked = SetterProp<Token, bool, [](auto& self, bool v) { self.set_checked(v); }>;
|
||||
|
||||
// 通用文本属性
|
||||
template <class Token, auto setter>
|
||||
struct String : public QString, Token {
|
||||
using QString::QString;
|
||||
|
||||
explicit String(const QString& text) noexcept
|
||||
: QString { text } { }
|
||||
explicit String(const std::string& text) noexcept
|
||||
: QString { QString::fromStdString(text) } { }
|
||||
|
||||
auto operator=(const QString& text) noexcept {
|
||||
QString::operator=(text);
|
||||
return *this;
|
||||
}
|
||||
auto operator=(QString&& text) noexcept {
|
||||
QString::operator=(std::move(text));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void apply(auto& self) const
|
||||
requires requires { setter(self, *this); }
|
||||
{
|
||||
setter(self, *this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Token, typename T, auto setter>
|
||||
struct Vector : public QVector<T>, Token {
|
||||
using QVector<T>::QVector;
|
||||
|
||||
explicit Vector(const QVector<T>& vec) noexcept
|
||||
: QVector<T> { vec } { }
|
||||
explicit Vector(const std::vector<T>& vec) noexcept
|
||||
: QVector<T> { vec } { }
|
||||
|
||||
void apply(auto& self) const
|
||||
requires requires {setter(self, *this); }
|
||||
{
|
||||
setter(self, *this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Token, typename T, std::size_t N, auto setter>
|
||||
struct Array : public std::array<T, N>, Token {
|
||||
using std::array<T, N>::array;
|
||||
explicit Array(const std::array<T, N>& arr) noexcept
|
||||
: std::array<T, N> { arr } { }
|
||||
|
||||
template <typename... Args>
|
||||
requires (sizeof...(Args) == N)
|
||||
explicit Array(Args&&... args) noexcept
|
||||
: std::array<T, N> {
|
||||
std::forward<Args>(args)...} {}
|
||||
|
||||
void apply(auto& self) const
|
||||
requires requires {setter(self, *this);}
|
||||
{
|
||||
setter(self, *this);
|
||||
}
|
||||
};
|
||||
|
||||
// template<class Token, typename T, auto setter>
|
||||
// Vector<Token, T, setter>::Vector(const QVector<T> &vec) noexcept:QVector { vec } { }
|
||||
|
||||
template <class Token>
|
||||
using Text = String<Token, [](auto& self, const auto& string) { self.setText(string); }>;
|
||||
|
||||
// 通用指针绑定
|
||||
template <class Token, class Final>
|
||||
struct Bind : Token {
|
||||
Final*& widget;
|
||||
explicit Bind(Final*& p)
|
||||
: widget(p) { };
|
||||
void apply(Final& self) const { widget = &self; }
|
||||
};
|
||||
|
||||
// 通用点击事件
|
||||
template <typename Callback, class Token>
|
||||
struct Clickable : Token {
|
||||
Callback callback;
|
||||
explicit Clickable(Callback callback) noexcept
|
||||
: callback { std::move(callback) } { }
|
||||
auto apply(auto& self) const noexcept -> void
|
||||
requires std::invocable<Callback, decltype(self)> || std::invocable<Callback>
|
||||
{
|
||||
using widget_t = std::remove_cvref_t<decltype(self)>;
|
||||
QObject::connect(&self, &widget_t::clicked, [function = callback, &self] {
|
||||
if constexpr (std::invocable<Callback, decltype(self)>) function(self);
|
||||
if constexpr (std::invocable<Callback>) function();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Callback, class Token>
|
||||
struct IndexChanged : Token {
|
||||
Callback callback;
|
||||
explicit IndexChanged(Callback callback) noexcept
|
||||
: callback {std::move(callback)} {}
|
||||
auto apply(auto& self) const noexcept -> void
|
||||
requires std::invocable<Callback, decltype(self)> || std::invocable<Callback> {
|
||||
using widget_t = std::remove_cvref_t<decltype(self)>;
|
||||
QObject::connect(&self, &widget_t::currentIndexChanged, [function = callback, &self] {
|
||||
if constexpr (std::invocable<Callback, decltype(self)>) function(self);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 自定义信号回调注册
|
||||
|
||||
namespace internal {
|
||||
template <typename T>
|
||||
struct FunctionArgs;
|
||||
|
||||
template <class C, class R, class... Args>
|
||||
struct FunctionArgs<auto (C::*)(Args...)->R> {
|
||||
using type = std::tuple<Args...>;
|
||||
};
|
||||
template <class C, class R, class... Args>
|
||||
struct FunctionArgs<auto (C::*)(Args...) const->R> {
|
||||
using type = std::tuple<Args...>;
|
||||
};
|
||||
|
||||
template <typename F, typename Tuple>
|
||||
concept tuple_invocable_trait = requires(F&& f, Tuple&& t) {
|
||||
std::apply(std::forward<F>(f), std::forward<Tuple>(t)); //
|
||||
};
|
||||
}
|
||||
|
||||
template <typename F, class Token, auto signal>
|
||||
struct SignalInjection : Token {
|
||||
F f;
|
||||
|
||||
using SignalArgs = typename internal::FunctionArgs<decltype(signal)>::type;
|
||||
|
||||
explicit SignalInjection(F f) noexcept
|
||||
requires internal::tuple_invocable_trait<F, SignalArgs>
|
||||
: f { std::forward<decltype(f)>(f) } { }
|
||||
|
||||
auto apply(auto& self) const noexcept { QObject::connect(&self, signal, f); }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
#pragma once
|
||||
#include "modern-qt/utility/qt_wrapper/margin_setter.hh"
|
||||
#include "modern-qt/utility/trait/widget.hh"
|
||||
#include "modern-qt/utility/wrapper/common.hh"
|
||||
|
||||
namespace creeper::layout::pro {
|
||||
|
||||
struct Layout { };
|
||||
using Token = common::Token<Layout>;
|
||||
|
||||
using ContentsMargin = SetterProp<Token, QMargins,
|
||||
[](auto& self, const auto& margins) { self.setContentsMargins(margins); }>;
|
||||
|
||||
using Alignment = SetterProp<Token, Qt::Alignment,
|
||||
[](auto& self, const auto& alignment) { self.setAlignment(alignment); }>;
|
||||
|
||||
using Spacing =
|
||||
SetterProp<Token, int, [](auto& self, const auto& spacing) { self.setSpacing(spacing); }>;
|
||||
|
||||
using Margin = SetterProp<Token, int, qt::margin_setter>;
|
||||
|
||||
template <widget_trait T>
|
||||
struct Widget : Token {
|
||||
|
||||
T* item_pointer = nullptr;
|
||||
|
||||
explicit Widget(T* pointer) noexcept
|
||||
: item_pointer { pointer } { }
|
||||
|
||||
explicit Widget(auto&&... args) noexcept
|
||||
requires std::constructible_from<T, decltype(args)...>
|
||||
: item_pointer { new T { std::forward<decltype(args)>(args)... } } { }
|
||||
|
||||
auto apply(auto& layout) const { layout.addWidget(item_pointer); }
|
||||
};
|
||||
|
||||
// 传入一个方法用来辅助构造,在没有想要的接口时用这个吧
|
||||
template <typename Lambda>
|
||||
struct Apply : Token {
|
||||
Lambda lambda;
|
||||
explicit Apply(Lambda lambda) noexcept
|
||||
: lambda { lambda } { }
|
||||
auto apply(auto& self) const noexcept -> void {
|
||||
if constexpr (std::invocable<Lambda>) lambda();
|
||||
if constexpr (std::invocable<Lambda, decltype(self)>) lambda(self);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept trait = std::derived_from<T, Token>;
|
||||
|
||||
CREEPER_DEFINE_CHECKER(trait);
|
||||
}
|
||||
#pragma once
|
||||
#include "modern-qt/utility/qt_wrapper/margin_setter.hh"
|
||||
#include "modern-qt/utility/trait/widget.hh"
|
||||
#include "modern-qt/utility/wrapper/common.hh"
|
||||
|
||||
namespace creeper::layout::pro {
|
||||
|
||||
struct Layout { };
|
||||
using Token = common::Token<Layout>;
|
||||
|
||||
using ContentsMargin = SetterProp<Token, QMargins,
|
||||
[](auto& self, const auto& margins) { self.setContentsMargins(margins); }>;
|
||||
|
||||
using Alignment = SetterProp<Token, Qt::Alignment,
|
||||
[](auto& self, const auto& alignment) { self.setAlignment(alignment); }>;
|
||||
|
||||
using Spacing =
|
||||
SetterProp<Token, int, [](auto& self, const auto& spacing) { self.setSpacing(spacing); }>;
|
||||
|
||||
using Margin = SetterProp<Token, int, qt::margin_setter>;
|
||||
|
||||
template <widget_trait T>
|
||||
struct Widget : Token {
|
||||
|
||||
T* item_pointer = nullptr;
|
||||
|
||||
explicit Widget(T* pointer) noexcept
|
||||
: item_pointer { pointer } { }
|
||||
|
||||
explicit Widget(auto&&... args) noexcept
|
||||
requires std::constructible_from<T, decltype(args)...>
|
||||
: item_pointer { new T { std::forward<decltype(args)>(args)... } } { }
|
||||
|
||||
auto apply(auto& layout) const { layout.addWidget(item_pointer); }
|
||||
};
|
||||
|
||||
// 传入一个方法用来辅助构造,在没有想要的接口时用这个吧
|
||||
template <typename Lambda>
|
||||
struct Apply : Token {
|
||||
Lambda lambda;
|
||||
explicit Apply(Lambda lambda) noexcept
|
||||
: lambda { lambda } { }
|
||||
auto apply(auto& self) const noexcept -> void {
|
||||
if constexpr (std::invocable<Lambda>) lambda();
|
||||
if constexpr (std::invocable<Lambda, decltype(self)>) lambda(self);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept trait = std::derived_from<T, Token>;
|
||||
|
||||
CREEPER_DEFINE_CHECKER(trait);
|
||||
}
|
||||
|
||||
@@ -1,153 +1,153 @@
|
||||
#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>;
|
||||
|
||||
}
|
||||
#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>;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <qwidget.h>
|
||||
|
||||
namespace creeper {
|
||||
|
||||
/// @brief 响应式属性包装器,允许属性变更后自动同步到所有已绑定组件。
|
||||
///
|
||||
/// 该模板用于包装具备 apply 方法的属性类型,使其具备响应式能力。
|
||||
/// 通过 Mutable 包装后的属性可以被多个组件共享,
|
||||
/// 当属性发生变更时,所有通过 Mutable 绑定的组件会自动更新自身属性,
|
||||
/// 达到类似“全局属性联动”的效果。
|
||||
///
|
||||
/// Usage:
|
||||
/// auto mutable_size = Mutable{widget::pro::FixedSize{100, 100}};
|
||||
/// auto card_1 = OutlinedCard{mutable_size};
|
||||
/// auto card_2 = OutlinedCard{mutable_size};
|
||||
/// // 修改 mutable_size,card1/card2 会同步变化
|
||||
/// mutable_size = {mutable_size.width() + 10, mutable_size.height() + 10};
|
||||
///
|
||||
/// @note
|
||||
/// 1. 不支持将 Mutable 实例放入 std::tuple 进行批量 apply,
|
||||
/// 必须显式调用 apply 绑定组件。
|
||||
/// 若允许混入 tuple,容易导致属性的响应式副作用被隐式传播,
|
||||
/// 使得属性绑定和变更难以追踪,带来较大维护隐患。
|
||||
/// 显式 apply 可以让响应式属性的边界清晰可控,
|
||||
/// 便于代码审查和后期维护。
|
||||
///
|
||||
/// 2. Mutable 不支持拷贝和移动构造,需确保唯一性和生命周期安全。
|
||||
///
|
||||
/// 3. 绑定对象被销毁时,内部回调会自动解绑,无内存泄漏风险。
|
||||
///
|
||||
/// @date 2025.7.28
|
||||
template <class T>
|
||||
struct Mutable : public T {
|
||||
public:
|
||||
explicit Mutable(const T& t) noexcept
|
||||
: T { t } { }
|
||||
|
||||
explicit Mutable(std::constructible_from<T> auto&&... args) noexcept
|
||||
: T { std::forward<decltype(args)>(args)... } { }
|
||||
|
||||
Mutable(const Mutable&) = delete;
|
||||
Mutable(Mutable&&) = delete;
|
||||
|
||||
auto apply(auto& self) const noexcept -> void {
|
||||
T::apply(self);
|
||||
Mutable::bind(self);
|
||||
}
|
||||
auto bind(auto& self) const noexcept -> void {
|
||||
callbacks_.insert({
|
||||
&self,
|
||||
[&](const T& pro) {
|
||||
pro.apply(self);
|
||||
self.update();
|
||||
},
|
||||
});
|
||||
QObject::connect(&self, &QObject::destroyed, //
|
||||
[pointer = &self, this] { callbacks_.erase(pointer); });
|
||||
}
|
||||
template <typename U>
|
||||
auto set(U&& u) noexcept -> Mutable<T>&
|
||||
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
|
||||
{
|
||||
static_cast<T&>(*this) = std::forward<U>(u);
|
||||
for (const auto& [_, f] : callbacks_)
|
||||
f(*this);
|
||||
return *this;
|
||||
}
|
||||
template <typename U>
|
||||
auto operator=(U&& u) noexcept -> Mutable<T>&
|
||||
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
|
||||
{
|
||||
return set(std::forward<U>(u));
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::unordered_map<QWidget*, std::function<void(const T&)>> callbacks_;
|
||||
};
|
||||
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <qwidget.h>
|
||||
|
||||
namespace creeper {
|
||||
|
||||
/// @brief 响应式属性包装器,允许属性变更后自动同步到所有已绑定组件。
|
||||
///
|
||||
/// 该模板用于包装具备 apply 方法的属性类型,使其具备响应式能力。
|
||||
/// 通过 Mutable 包装后的属性可以被多个组件共享,
|
||||
/// 当属性发生变更时,所有通过 Mutable 绑定的组件会自动更新自身属性,
|
||||
/// 达到类似“全局属性联动”的效果。
|
||||
///
|
||||
/// Usage:
|
||||
/// auto mutable_size = Mutable{widget::pro::FixedSize{100, 100}};
|
||||
/// auto card_1 = OutlinedCard{mutable_size};
|
||||
/// auto card_2 = OutlinedCard{mutable_size};
|
||||
/// // 修改 mutable_size,card1/card2 会同步变化
|
||||
/// mutable_size = {mutable_size.width() + 10, mutable_size.height() + 10};
|
||||
///
|
||||
/// @note
|
||||
/// 1. 不支持将 Mutable 实例放入 std::tuple 进行批量 apply,
|
||||
/// 必须显式调用 apply 绑定组件。
|
||||
/// 若允许混入 tuple,容易导致属性的响应式副作用被隐式传播,
|
||||
/// 使得属性绑定和变更难以追踪,带来较大维护隐患。
|
||||
/// 显式 apply 可以让响应式属性的边界清晰可控,
|
||||
/// 便于代码审查和后期维护。
|
||||
///
|
||||
/// 2. Mutable 不支持拷贝和移动构造,需确保唯一性和生命周期安全。
|
||||
///
|
||||
/// 3. 绑定对象被销毁时,内部回调会自动解绑,无内存泄漏风险。
|
||||
///
|
||||
/// @date 2025.7.28
|
||||
template <class T>
|
||||
struct Mutable : public T {
|
||||
public:
|
||||
explicit Mutable(const T& t) noexcept
|
||||
: T { t } { }
|
||||
|
||||
explicit Mutable(std::constructible_from<T> auto&&... args) noexcept
|
||||
: T { std::forward<decltype(args)>(args)... } { }
|
||||
|
||||
Mutable(const Mutable&) = delete;
|
||||
Mutable(Mutable&&) = delete;
|
||||
|
||||
auto apply(auto& self) const noexcept -> void {
|
||||
T::apply(self);
|
||||
Mutable::bind(self);
|
||||
}
|
||||
auto bind(auto& self) const noexcept -> void {
|
||||
callbacks_.insert({
|
||||
&self,
|
||||
[&](const T& pro) {
|
||||
pro.apply(self);
|
||||
self.update();
|
||||
},
|
||||
});
|
||||
QObject::connect(&self, &QObject::destroyed, //
|
||||
[pointer = &self, this] { callbacks_.erase(pointer); });
|
||||
}
|
||||
template <typename U>
|
||||
auto set(U&& u) noexcept -> Mutable<T>&
|
||||
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
|
||||
{
|
||||
static_cast<T&>(*this) = std::forward<U>(u);
|
||||
for (const auto& [_, f] : callbacks_)
|
||||
f(*this);
|
||||
return *this;
|
||||
}
|
||||
template <typename U>
|
||||
auto operator=(U&& u) noexcept -> Mutable<T>&
|
||||
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
|
||||
{
|
||||
return set(std::forward<U>(u));
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::unordered_map<QWidget*, std::function<void(const T&)>> callbacks_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define CREEPER_PIMPL_DEFINITION(Class) \
|
||||
public: \
|
||||
Class(); \
|
||||
~Class(); \
|
||||
Class(const Class&) = delete; \
|
||||
Class& operator=(const Class&) = delete; \
|
||||
\
|
||||
private: \
|
||||
struct Impl; \
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
|
||||
namespace creeper::internal {
|
||||
inline void use_std_unique_ptr() { auto unique_ptr = std::unique_ptr<int> {}; }
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define CREEPER_PIMPL_DEFINITION(Class) \
|
||||
public: \
|
||||
Class(); \
|
||||
~Class(); \
|
||||
Class(const Class&) = delete; \
|
||||
Class& operator=(const Class&) = delete; \
|
||||
\
|
||||
private: \
|
||||
struct Impl; \
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
|
||||
namespace creeper::internal {
|
||||
inline void use_std_unique_ptr() { auto unique_ptr = std::unique_ptr<int> {}; }
|
||||
}
|
||||
|
||||
@@ -1,162 +1,162 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
// 少量样板代码的生成宏是可以接受的,
|
||||
// 等 concept 可以作为参数的那一天,这个宏就可以废弃了
|
||||
#define CREEPER_DEFINE_CHECKER(TRAIT) \
|
||||
struct checker final { \
|
||||
template <class T> \
|
||||
static constexpr bool result = TRAIT<T>; \
|
||||
};
|
||||
|
||||
namespace creeper {
|
||||
|
||||
/// CHECKER
|
||||
template <class T>
|
||||
concept checker_trait = requires {
|
||||
{ T::template result<void> };
|
||||
};
|
||||
|
||||
template <checker_trait... Ts>
|
||||
struct CheckerOr {
|
||||
template <class T>
|
||||
static constexpr bool result = (Ts::template result<T> || ...);
|
||||
};
|
||||
template <checker_trait... Ts>
|
||||
struct CheckerAnd {
|
||||
template <class T>
|
||||
static constexpr bool result = (Ts::template result<T> && ...);
|
||||
};
|
||||
|
||||
/// PROP
|
||||
template <class Token, typename T, auto interface>
|
||||
struct SetterProp : Token {
|
||||
T value;
|
||||
|
||||
constexpr explicit SetterProp(T value) noexcept
|
||||
: value { std::move(value) } { }
|
||||
|
||||
constexpr explicit operator T(this auto&& self) noexcept { return self.value; }
|
||||
|
||||
template <typename O>
|
||||
auto operator=(O&& other) noexcept -> SetterProp&
|
||||
requires std::assignable_from<T&, O>
|
||||
{
|
||||
value = std::forward<O>(other);
|
||||
return *this; // x= y =6
|
||||
}
|
||||
|
||||
auto apply(auto& self) const noexcept -> void
|
||||
requires requires { interface(self, std::declval<T>()); }
|
||||
{
|
||||
interface(self, value);
|
||||
}
|
||||
};
|
||||
template <class Token, typename T, auto interface>
|
||||
struct DerivedProp : T, Token {
|
||||
using T::T;
|
||||
|
||||
template <typename... Args>
|
||||
requires std::constructible_from<T, Args&&...>
|
||||
constexpr explicit DerivedProp(Args&&... args)
|
||||
: T(std::forward<Args>(args)...) { }
|
||||
|
||||
template <typename U>
|
||||
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
|
||||
auto operator=(U&& value) -> DerivedProp& {
|
||||
T::operator=(std::forward<U>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto apply(this auto const& self, auto& widget) noexcept -> void {
|
||||
interface(widget, static_cast<const T&>(self));
|
||||
}
|
||||
};
|
||||
template <class Token, auto interface>
|
||||
struct ActionProp : Token {
|
||||
auto apply(auto& self) const noexcept -> void
|
||||
requires requires { interface(self); }
|
||||
{
|
||||
interface(self);
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief
|
||||
/// 声明式包装,非侵入式实现 Setter 的声明式化
|
||||
///
|
||||
/// 该包装器支持将 std::tuple 与单个 prop 混合传入,不受顺序限制。内部通过
|
||||
/// helper 检查 tuple 中的所有元素,递归调用 apply 将属性应用到 底层 Widget。
|
||||
/// 利用 CheckerT 延迟模板实例化,在 concept 层面约束属性类型, 从而避免因
|
||||
/// 递归参数展开而产生的海量且难以定位的模板错误。
|
||||
///
|
||||
/// @tparam W
|
||||
/// 需要被包装的组件类型。
|
||||
///
|
||||
/// @tparam checker
|
||||
/// 用于延迟模板实例化的“检查器”类型模板。典型形式如下:
|
||||
/// struct checker final {
|
||||
/// template <class T> struct result {
|
||||
/// static constexpr auto v = concept<T>;
|
||||
/// };
|
||||
/// };
|
||||
///
|
||||
/// @note
|
||||
/// 在不符合 checker 要求的类型被传入时,会在 concept 约束阶段直接报错,
|
||||
/// 提供简洁且精准的编译期错误提示,避免编译器自动展开大量构造函数
|
||||
/// 导致的冗长错误栈,但编译期报错信息依旧不友好。
|
||||
///
|
||||
template <class W, checker_trait checker>
|
||||
struct Declarative : public W {
|
||||
private:
|
||||
// For helpe check single props
|
||||
// 这里没法子塞一个 static_assert,无论如何这里都会被尝试错误地实例化
|
||||
template <typename T, class checker_>
|
||||
static constexpr auto impl_props_trait = checker_::template result<std::remove_cvref_t<T>>;
|
||||
|
||||
// For help check tuple of props
|
||||
// 使用 SFINAE 真是抱歉,没找到方便处理 tuple 的方法真是不好意思呢
|
||||
template <typename T, class checker_>
|
||||
static constexpr auto impl_tuple_trait = false;
|
||||
|
||||
template <typename... Ts, class checker_>
|
||||
static constexpr auto impl_tuple_trait<std::tuple<Ts...>, checker_> =
|
||||
(impl_props_trait<Ts, checker_> && ...);
|
||||
|
||||
public:
|
||||
/* Export widget type */
|
||||
using Widget = W;
|
||||
|
||||
/* Export checker type */
|
||||
using Checker = checker;
|
||||
|
||||
/* For check props */
|
||||
template <class T>
|
||||
static constexpr auto props_trait = impl_props_trait<std::remove_cvref_t<T>, checker>;
|
||||
|
||||
/* For check tuple */
|
||||
template <class T>
|
||||
static constexpr auto tuple_trait = impl_tuple_trait<std::remove_cvref_t<T>, checker>;
|
||||
|
||||
public:
|
||||
// 铁血的,热血的,冷血的声明式构造接口
|
||||
explicit Declarative(auto&&... props) noexcept
|
||||
requires((props_trait<decltype(props)> || tuple_trait<decltype(props)>) && ...)
|
||||
: W {} {
|
||||
(apply(std::forward<decltype(props)>(props)), ...);
|
||||
}
|
||||
auto apply(auto&& tuple) noexcept -> void
|
||||
requires tuple_trait<decltype(tuple)>
|
||||
{
|
||||
std::apply([this](auto&&... args) { (apply(std::forward<decltype(args)>(args)), ...); },
|
||||
std::forward<decltype(tuple)>(tuple));
|
||||
}
|
||||
auto apply(auto&& prop) noexcept -> void
|
||||
requires props_trait<decltype(prop)>
|
||||
{
|
||||
std::forward<decltype(prop)>(prop).apply(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
// 少量样板代码的生成宏是可以接受的,
|
||||
// 等 concept 可以作为参数的那一天,这个宏就可以废弃了
|
||||
#define CREEPER_DEFINE_CHECKER(TRAIT) \
|
||||
struct checker final { \
|
||||
template <class T> \
|
||||
static constexpr bool result = TRAIT<T>; \
|
||||
};
|
||||
|
||||
namespace creeper {
|
||||
|
||||
/// CHECKER
|
||||
template <class T>
|
||||
concept checker_trait = requires {
|
||||
{ T::template result<void> };
|
||||
};
|
||||
|
||||
template <checker_trait... Ts>
|
||||
struct CheckerOr {
|
||||
template <class T>
|
||||
static constexpr bool result = (Ts::template result<T> || ...);
|
||||
};
|
||||
template <checker_trait... Ts>
|
||||
struct CheckerAnd {
|
||||
template <class T>
|
||||
static constexpr bool result = (Ts::template result<T> && ...);
|
||||
};
|
||||
|
||||
/// PROP
|
||||
template <class Token, typename T, auto interface>
|
||||
struct SetterProp : Token {
|
||||
T value;
|
||||
|
||||
constexpr explicit SetterProp(T value) noexcept
|
||||
: value { std::move(value) } { }
|
||||
|
||||
constexpr explicit operator T(this auto&& self) noexcept { return self.value; }
|
||||
|
||||
template <typename O>
|
||||
auto operator=(O&& other) noexcept -> SetterProp&
|
||||
requires std::assignable_from<T&, O>
|
||||
{
|
||||
value = std::forward<O>(other);
|
||||
return *this; // x= y =6
|
||||
}
|
||||
|
||||
auto apply(auto& self) const noexcept -> void
|
||||
requires requires { interface(self, std::declval<T>()); }
|
||||
{
|
||||
interface(self, value);
|
||||
}
|
||||
};
|
||||
template <class Token, typename T, auto interface>
|
||||
struct DerivedProp : T, Token {
|
||||
using T::T;
|
||||
|
||||
template <typename... Args>
|
||||
requires std::constructible_from<T, Args&&...>
|
||||
constexpr explicit DerivedProp(Args&&... args)
|
||||
: T(std::forward<Args>(args)...) { }
|
||||
|
||||
template <typename U>
|
||||
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
|
||||
auto operator=(U&& value) -> DerivedProp& {
|
||||
T::operator=(std::forward<U>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto apply(this auto const& self, auto& widget) noexcept -> void {
|
||||
interface(widget, static_cast<const T&>(self));
|
||||
}
|
||||
};
|
||||
template <class Token, auto interface>
|
||||
struct ActionProp : Token {
|
||||
auto apply(auto& self) const noexcept -> void
|
||||
requires requires { interface(self); }
|
||||
{
|
||||
interface(self);
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief
|
||||
/// 声明式包装,非侵入式实现 Setter 的声明式化
|
||||
///
|
||||
/// 该包装器支持将 std::tuple 与单个 prop 混合传入,不受顺序限制。内部通过
|
||||
/// helper 检查 tuple 中的所有元素,递归调用 apply 将属性应用到 底层 Widget。
|
||||
/// 利用 CheckerT 延迟模板实例化,在 concept 层面约束属性类型, 从而避免因
|
||||
/// 递归参数展开而产生的海量且难以定位的模板错误。
|
||||
///
|
||||
/// @tparam W
|
||||
/// 需要被包装的组件类型。
|
||||
///
|
||||
/// @tparam checker
|
||||
/// 用于延迟模板实例化的“检查器”类型模板。典型形式如下:
|
||||
/// struct checker final {
|
||||
/// template <class T> struct result {
|
||||
/// static constexpr auto v = concept<T>;
|
||||
/// };
|
||||
/// };
|
||||
///
|
||||
/// @note
|
||||
/// 在不符合 checker 要求的类型被传入时,会在 concept 约束阶段直接报错,
|
||||
/// 提供简洁且精准的编译期错误提示,避免编译器自动展开大量构造函数
|
||||
/// 导致的冗长错误栈,但编译期报错信息依旧不友好。
|
||||
///
|
||||
template <class W, checker_trait checker>
|
||||
struct Declarative : public W {
|
||||
private:
|
||||
// For helpe check single props
|
||||
// 这里没法子塞一个 static_assert,无论如何这里都会被尝试错误地实例化
|
||||
template <typename T, class checker_>
|
||||
static constexpr auto impl_props_trait = checker_::template result<std::remove_cvref_t<T>>;
|
||||
|
||||
// For help check tuple of props
|
||||
// 使用 SFINAE 真是抱歉,没找到方便处理 tuple 的方法真是不好意思呢
|
||||
template <typename T, class checker_>
|
||||
static constexpr auto impl_tuple_trait = false;
|
||||
|
||||
template <typename... Ts, class checker_>
|
||||
static constexpr auto impl_tuple_trait<std::tuple<Ts...>, checker_> =
|
||||
(impl_props_trait<Ts, checker_> && ...);
|
||||
|
||||
public:
|
||||
/* Export widget type */
|
||||
using Widget = W;
|
||||
|
||||
/* Export checker type */
|
||||
using Checker = checker;
|
||||
|
||||
/* For check props */
|
||||
template <class T>
|
||||
static constexpr auto props_trait = impl_props_trait<std::remove_cvref_t<T>, checker>;
|
||||
|
||||
/* For check tuple */
|
||||
template <class T>
|
||||
static constexpr auto tuple_trait = impl_tuple_trait<std::remove_cvref_t<T>, checker>;
|
||||
|
||||
public:
|
||||
// 铁血的,热血的,冷血的声明式构造接口
|
||||
explicit Declarative(auto&&... props) noexcept
|
||||
requires((props_trait<decltype(props)> || tuple_trait<decltype(props)>) && ...)
|
||||
: W {} {
|
||||
(apply(std::forward<decltype(props)>(props)), ...);
|
||||
}
|
||||
auto apply(auto&& tuple) noexcept -> void
|
||||
requires tuple_trait<decltype(tuple)>
|
||||
{
|
||||
std::apply([this](auto&&... args) { (apply(std::forward<decltype(args)>(args)), ...); },
|
||||
std::forward<decltype(tuple)>(tuple));
|
||||
}
|
||||
auto apply(auto&& prop) noexcept -> void
|
||||
requires props_trait<decltype(prop)>
|
||||
{
|
||||
std::forward<decltype(prop)>(prop).apply(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
namespace creeper::util {
|
||||
|
||||
template <typename T> class Singleton {
|
||||
public:
|
||||
static T& instance();
|
||||
|
||||
Singleton(const Singleton&) = delete;
|
||||
Singleton& operator=(const Singleton&) = delete;
|
||||
|
||||
protected:
|
||||
struct token { };
|
||||
Singleton() = default;
|
||||
};
|
||||
|
||||
template <typename T> inline T& Singleton<T>::instance() {
|
||||
static const std::unique_ptr<T> instance { new T { token {} } };
|
||||
return *instance;
|
||||
}
|
||||
|
||||
}
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
namespace creeper::util {
|
||||
|
||||
template <typename T> class Singleton {
|
||||
public:
|
||||
static T& instance();
|
||||
|
||||
Singleton(const Singleton&) = delete;
|
||||
Singleton& operator=(const Singleton&) = delete;
|
||||
|
||||
protected:
|
||||
struct token { };
|
||||
Singleton() = default;
|
||||
};
|
||||
|
||||
template <typename T> inline T& Singleton<T>::instance() {
|
||||
static const std::unique_ptr<T> instance { new T { token {} } };
|
||||
return *instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,177 +1,177 @@
|
||||
#pragma once
|
||||
|
||||
#include "modern-qt/utility/wrapper/common.hh"
|
||||
#include "modern-qt/utility/wrapper/property.hh"
|
||||
|
||||
#include <qgraphicseffect.h>
|
||||
#include <qscreen.h>
|
||||
|
||||
namespace creeper::widget::pro {
|
||||
|
||||
using Token = common::Token<QWidget>;
|
||||
|
||||
using MinimumWidth = SetterProp<Token, int, [](auto& self, int v) { self.setMinimumWidth(v); }>;
|
||||
using MaximumWidth = SetterProp<Token, int, [](auto& self, int v) { self.setMaximumWidth(v); }>;
|
||||
using FixedWidth = SetterProp<Token, int, [](auto& self, int v) { self.setFixedWidth(v); }>;
|
||||
using MinimumHeight = SetterProp<Token, int, [](auto& self, int v) { self.setMinimumHeight(v); }>;
|
||||
using MaximumHeight = SetterProp<Token, int, [](auto& self, int v) { self.setMaximumHeight(v); }>;
|
||||
using FixedHeight = SetterProp<Token, int, [](auto& self, int v) { self.setFixedHeight(v); }>;
|
||||
|
||||
using LayoutDirection = SetterProp<Token, Qt::LayoutDirection,
|
||||
[](auto& self, Qt::LayoutDirection v) { self.setLayoutDirection(v); }>;
|
||||
using BackgroundRole = SetterProp<Token, QPalette::ColorRole,
|
||||
[](auto& self, QPalette::ColorRole v) { self.setBackgroundRole(v); }>;
|
||||
using ForegroundRole = SetterProp<Token, QPalette::ColorRole,
|
||||
[](auto& self, QPalette::ColorRole v) { self.setForegroundRole(v); }>;
|
||||
|
||||
using ClearMask = SetterProp<Token, std::nullptr_t, [](auto& self, auto) { self.clearMask(); }>;
|
||||
using GraphicsEffect = SetterProp<Token, QGraphicsEffect*,
|
||||
[](auto& self, QGraphicsEffect* v) { self.setGraphicsEffect(v); }>;
|
||||
|
||||
struct WindowFlag : Token {
|
||||
Qt::WindowType type;
|
||||
bool on;
|
||||
|
||||
explicit WindowFlag(Qt::WindowType type, bool on = true)
|
||||
: type { type }
|
||||
, on { on } { }
|
||||
|
||||
void apply(QWidget& widget) const { widget.setWindowFlag(type, on); }
|
||||
};
|
||||
using WindowFlags = SetterProp<Token, Qt::WindowFlags,
|
||||
[](auto& self, Qt::WindowFlags v) { self.setWindowFlags(v); }>;
|
||||
using WindowOpacity =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.setWindowOpacity(v); }>;
|
||||
|
||||
using Parent = SetterProp<Token, QWidget*, [](auto& self, QWidget* v) { self.setParent(v); }>;
|
||||
using Child = SetterProp<Token, QWidget*, [](auto& self, QWidget* v) { v->setParent(&self); }>;
|
||||
|
||||
using MinimumSize =
|
||||
DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setMinimumSize(v); }>;
|
||||
using MaximumSize =
|
||||
DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setMaximumSize(v); }>;
|
||||
using SizeIncrement =
|
||||
DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setSizeIncrement(v); }>;
|
||||
using BaseSize = DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setBaseSize(v); }>;
|
||||
using FixedSize =
|
||||
DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setFixedSize(v); }>;
|
||||
|
||||
using Font = DerivedProp<Token, QFont, [](auto& self, const QFont& v) { self.setFont(v); }>;
|
||||
using BitmapMask =
|
||||
DerivedProp<Token, QBitmap, [](auto& self, const QBitmap& v) { self.setMask(v); }>;
|
||||
using RegionMask =
|
||||
DerivedProp<Token, QRegion, [](auto& self, const QRegion& v) { self.setMask(v); }>;
|
||||
using WindowIcon =
|
||||
DerivedProp<Token, QIcon, [](auto& self, const QIcon& v) { self.setWindowIcon(v); }>;
|
||||
using WindowIconText =
|
||||
DerivedProp<Token, QString, [](auto& self, const QString& v) { self.setWindowIconText(v); }>;
|
||||
using WindowRole =
|
||||
DerivedProp<Token, QString, [](auto& self, const QString& v) { self.setWindowRole(v); }>;
|
||||
using WindowFilePath =
|
||||
DerivedProp<Token, QString, [](auto& self, const QString& v) { self.setWindowFilePath(v); }>;
|
||||
|
||||
using ToolTip =
|
||||
DerivedProp<Token, QString, [](auto& self, const QString& tip) { self.setToolTip(tip); }>;
|
||||
|
||||
struct MoveCenter : Token {
|
||||
auto apply(QWidget& self) const noexcept -> void {
|
||||
const auto screen = self.screen();
|
||||
|
||||
const auto screen_geometry = screen->availableGeometry();
|
||||
const auto screen_width = screen_geometry.width();
|
||||
const auto screen_height = screen_geometry.height();
|
||||
|
||||
const auto widget_geometry = self.geometry();
|
||||
const auto widget_width = widget_geometry.width();
|
||||
const auto widget_height = widget_geometry.height();
|
||||
|
||||
const auto x = (screen_width - widget_width) / 2;
|
||||
const auto y = (screen_height - widget_height) / 2;
|
||||
|
||||
self.move(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
struct SizePolicy : Token {
|
||||
QSizePolicy::Policy v, h;
|
||||
explicit SizePolicy(QSizePolicy::Policy policy)
|
||||
: v { policy }
|
||||
, h { policy } { }
|
||||
explicit SizePolicy(QSizePolicy::Policy v, QSizePolicy::Policy h)
|
||||
: v { v }
|
||||
, h { h } { }
|
||||
void apply(QWidget& self) const { self.setSizePolicy(h, v); }
|
||||
};
|
||||
|
||||
/// @note 该属性本质是转发构造,有 new 的行为
|
||||
template <class T>
|
||||
struct Layout : Token {
|
||||
T* layout_;
|
||||
|
||||
explicit Layout(T* pointer) noexcept
|
||||
requires std::convertible_to<decltype(pointer), QLayout*>
|
||||
: layout_ { pointer } { }
|
||||
|
||||
explicit Layout(auto&&... args)
|
||||
requires std::constructible_from<T, decltype(args)...>
|
||||
: layout_ { new T { std::forward<decltype(args)>(args)... } } { }
|
||||
|
||||
void apply(QWidget& widget) const { widget.setLayout(layout_); }
|
||||
};
|
||||
|
||||
/// @brief 绑定裸指针,为了避免赋值语句
|
||||
///
|
||||
/// 一般情况下需要绑定指针:
|
||||
///
|
||||
/// 假设下面是一个 layout 的内部
|
||||
/// clang-format 会在下面的赋值符号后换行
|
||||
/// @code
|
||||
/// auto widget = (Widget*)nullptr;
|
||||
/// ......
|
||||
/// { widget =
|
||||
/// new Widget {
|
||||
/// ......
|
||||
/// } },
|
||||
/// ......
|
||||
/// @endcode
|
||||
///
|
||||
/// 利用赋值语句的返回特性将该组件返回给 layout,同时完成赋值,
|
||||
/// 但这样会多一层缩进,为保证构造配置的简洁和同一,可以使用该包装:
|
||||
///
|
||||
/// @code
|
||||
/// ......
|
||||
/// { new Widget {
|
||||
/// pro::Bind { widget },
|
||||
/// } },
|
||||
/// ......
|
||||
/// @endcode
|
||||
///
|
||||
/// @tparam Final 需要绑定的组件类型(自动推导,无需显式指定)
|
||||
///
|
||||
/// @date 2025-06-19
|
||||
template <class Final>
|
||||
struct Bind : Token {
|
||||
Final*& widget;
|
||||
explicit Bind(Final*& widget) noexcept
|
||||
requires std::is_pointer<Final*>::value
|
||||
: widget(widget) { }
|
||||
void apply(Final& self) const noexcept { widget = &self; }
|
||||
};
|
||||
|
||||
// 传入一个方法用来辅助构造,在没有想要的接口时用这个吧
|
||||
template <typename Lambda>
|
||||
struct Apply : Token {
|
||||
Lambda lambda;
|
||||
explicit Apply(Lambda lambda) noexcept
|
||||
: lambda { lambda } { }
|
||||
auto apply(auto& self) const noexcept -> void {
|
||||
if constexpr (std::invocable<Lambda>) lambda();
|
||||
if constexpr (std::invocable<Lambda, decltype(self)>) lambda(self);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept trait = std::derived_from<T, Token>;
|
||||
|
||||
CREEPER_DEFINE_CHECKER(trait);
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "modern-qt/utility/wrapper/common.hh"
|
||||
#include "modern-qt/utility/wrapper/property.hh"
|
||||
|
||||
#include <qgraphicseffect.h>
|
||||
#include <qscreen.h>
|
||||
|
||||
namespace creeper::widget::pro {
|
||||
|
||||
using Token = common::Token<QWidget>;
|
||||
|
||||
using MinimumWidth = SetterProp<Token, int, [](auto& self, int v) { self.setMinimumWidth(v); }>;
|
||||
using MaximumWidth = SetterProp<Token, int, [](auto& self, int v) { self.setMaximumWidth(v); }>;
|
||||
using FixedWidth = SetterProp<Token, int, [](auto& self, int v) { self.setFixedWidth(v); }>;
|
||||
using MinimumHeight = SetterProp<Token, int, [](auto& self, int v) { self.setMinimumHeight(v); }>;
|
||||
using MaximumHeight = SetterProp<Token, int, [](auto& self, int v) { self.setMaximumHeight(v); }>;
|
||||
using FixedHeight = SetterProp<Token, int, [](auto& self, int v) { self.setFixedHeight(v); }>;
|
||||
|
||||
using LayoutDirection = SetterProp<Token, Qt::LayoutDirection,
|
||||
[](auto& self, Qt::LayoutDirection v) { self.setLayoutDirection(v); }>;
|
||||
using BackgroundRole = SetterProp<Token, QPalette::ColorRole,
|
||||
[](auto& self, QPalette::ColorRole v) { self.setBackgroundRole(v); }>;
|
||||
using ForegroundRole = SetterProp<Token, QPalette::ColorRole,
|
||||
[](auto& self, QPalette::ColorRole v) { self.setForegroundRole(v); }>;
|
||||
|
||||
using ClearMask = SetterProp<Token, std::nullptr_t, [](auto& self, auto) { self.clearMask(); }>;
|
||||
using GraphicsEffect = SetterProp<Token, QGraphicsEffect*,
|
||||
[](auto& self, QGraphicsEffect* v) { self.setGraphicsEffect(v); }>;
|
||||
|
||||
struct WindowFlag : Token {
|
||||
Qt::WindowType type;
|
||||
bool on;
|
||||
|
||||
explicit WindowFlag(Qt::WindowType type, bool on = true)
|
||||
: type { type }
|
||||
, on { on } { }
|
||||
|
||||
void apply(QWidget& widget) const { widget.setWindowFlag(type, on); }
|
||||
};
|
||||
using WindowFlags = SetterProp<Token, Qt::WindowFlags,
|
||||
[](auto& self, Qt::WindowFlags v) { self.setWindowFlags(v); }>;
|
||||
using WindowOpacity =
|
||||
SetterProp<Token, double, [](auto& self, double v) { self.setWindowOpacity(v); }>;
|
||||
|
||||
using Parent = SetterProp<Token, QWidget*, [](auto& self, QWidget* v) { self.setParent(v); }>;
|
||||
using Child = SetterProp<Token, QWidget*, [](auto& self, QWidget* v) { v->setParent(&self); }>;
|
||||
|
||||
using MinimumSize =
|
||||
DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setMinimumSize(v); }>;
|
||||
using MaximumSize =
|
||||
DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setMaximumSize(v); }>;
|
||||
using SizeIncrement =
|
||||
DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setSizeIncrement(v); }>;
|
||||
using BaseSize = DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setBaseSize(v); }>;
|
||||
using FixedSize =
|
||||
DerivedProp<Token, QSize, [](auto& self, const QSize& v) { self.setFixedSize(v); }>;
|
||||
|
||||
using Font = DerivedProp<Token, QFont, [](auto& self, const QFont& v) { self.setFont(v); }>;
|
||||
using BitmapMask =
|
||||
DerivedProp<Token, QBitmap, [](auto& self, const QBitmap& v) { self.setMask(v); }>;
|
||||
using RegionMask =
|
||||
DerivedProp<Token, QRegion, [](auto& self, const QRegion& v) { self.setMask(v); }>;
|
||||
using WindowIcon =
|
||||
DerivedProp<Token, QIcon, [](auto& self, const QIcon& v) { self.setWindowIcon(v); }>;
|
||||
using WindowIconText =
|
||||
DerivedProp<Token, QString, [](auto& self, const QString& v) { self.setWindowIconText(v); }>;
|
||||
using WindowRole =
|
||||
DerivedProp<Token, QString, [](auto& self, const QString& v) { self.setWindowRole(v); }>;
|
||||
using WindowFilePath =
|
||||
DerivedProp<Token, QString, [](auto& self, const QString& v) { self.setWindowFilePath(v); }>;
|
||||
|
||||
using ToolTip =
|
||||
DerivedProp<Token, QString, [](auto& self, const QString& tip) { self.setToolTip(tip); }>;
|
||||
|
||||
struct MoveCenter : Token {
|
||||
auto apply(QWidget& self) const noexcept -> void {
|
||||
const auto screen = self.screen();
|
||||
|
||||
const auto screen_geometry = screen->availableGeometry();
|
||||
const auto screen_width = screen_geometry.width();
|
||||
const auto screen_height = screen_geometry.height();
|
||||
|
||||
const auto widget_geometry = self.geometry();
|
||||
const auto widget_width = widget_geometry.width();
|
||||
const auto widget_height = widget_geometry.height();
|
||||
|
||||
const auto x = (screen_width - widget_width) / 2;
|
||||
const auto y = (screen_height - widget_height) / 2;
|
||||
|
||||
self.move(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
struct SizePolicy : Token {
|
||||
QSizePolicy::Policy v, h;
|
||||
explicit SizePolicy(QSizePolicy::Policy policy)
|
||||
: v { policy }
|
||||
, h { policy } { }
|
||||
explicit SizePolicy(QSizePolicy::Policy v, QSizePolicy::Policy h)
|
||||
: v { v }
|
||||
, h { h } { }
|
||||
void apply(QWidget& self) const { self.setSizePolicy(h, v); }
|
||||
};
|
||||
|
||||
/// @note 该属性本质是转发构造,有 new 的行为
|
||||
template <class T>
|
||||
struct Layout : Token {
|
||||
T* layout_;
|
||||
|
||||
explicit Layout(T* pointer) noexcept
|
||||
requires std::convertible_to<decltype(pointer), QLayout*>
|
||||
: layout_ { pointer } { }
|
||||
|
||||
explicit Layout(auto&&... args)
|
||||
requires std::constructible_from<T, decltype(args)...>
|
||||
: layout_ { new T { std::forward<decltype(args)>(args)... } } { }
|
||||
|
||||
void apply(QWidget& widget) const { widget.setLayout(layout_); }
|
||||
};
|
||||
|
||||
/// @brief 绑定裸指针,为了避免赋值语句
|
||||
///
|
||||
/// 一般情况下需要绑定指针:
|
||||
///
|
||||
/// 假设下面是一个 layout 的内部
|
||||
/// clang-format 会在下面的赋值符号后换行
|
||||
/// @code
|
||||
/// auto widget = (Widget*)nullptr;
|
||||
/// ......
|
||||
/// { widget =
|
||||
/// new Widget {
|
||||
/// ......
|
||||
/// } },
|
||||
/// ......
|
||||
/// @endcode
|
||||
///
|
||||
/// 利用赋值语句的返回特性将该组件返回给 layout,同时完成赋值,
|
||||
/// 但这样会多一层缩进,为保证构造配置的简洁和同一,可以使用该包装:
|
||||
///
|
||||
/// @code
|
||||
/// ......
|
||||
/// { new Widget {
|
||||
/// pro::Bind { widget },
|
||||
/// } },
|
||||
/// ......
|
||||
/// @endcode
|
||||
///
|
||||
/// @tparam Final 需要绑定的组件类型(自动推导,无需显式指定)
|
||||
///
|
||||
/// @date 2025-06-19
|
||||
template <class Final>
|
||||
struct Bind : Token {
|
||||
Final*& widget;
|
||||
explicit Bind(Final*& widget) noexcept
|
||||
requires std::is_pointer<Final*>::value
|
||||
: widget(widget) { }
|
||||
void apply(Final& self) const noexcept { widget = &self; }
|
||||
};
|
||||
|
||||
// 传入一个方法用来辅助构造,在没有想要的接口时用这个吧
|
||||
template <typename Lambda>
|
||||
struct Apply : Token {
|
||||
Lambda lambda;
|
||||
explicit Apply(Lambda lambda) noexcept
|
||||
: lambda { lambda } { }
|
||||
auto apply(auto& self) const noexcept -> void {
|
||||
if constexpr (std::invocable<Lambda>) lambda();
|
||||
if constexpr (std::invocable<Lambda, decltype(self)>) lambda(self);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept trait = std::derived_from<T, Token>;
|
||||
|
||||
CREEPER_DEFINE_CHECKER(trait);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user