first commit

This commit is contained in:
2025-10-20 00:32:01 +08:00
parent edac742f6a
commit 6ad03fc44f
106 changed files with 52165 additions and 0 deletions

View File

@@ -0,0 +1,215 @@
#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, 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); }
};
}
}

View File

@@ -0,0 +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);
}

View File

@@ -0,0 +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>;
}

View File

@@ -0,0 +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_sizecard1/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_;
};
}

View File

@@ -0,0 +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> {}; }
}

View File

@@ -0,0 +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);
}
};
}

View File

@@ -0,0 +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;
}
}

View File

@@ -0,0 +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);
}