Refactor layout system: Replace slot and property_slot with layout modifiers

- Removed the slot and property_slot classes, consolidating their functionality into a new modifier system.
- Introduced align_modifier, padding_modifier, and stretch_modifier to handle layout properties.
- Updated overlay and stack classes to utilize the new modifier system for layout management.
- Simplified event handling by integrating layout modifiers directly into the event target retrieval process.
- Removed unused includes and cleaned up code for better readability and maintainability.
This commit is contained in:
2025-11-30 15:53:24 +08:00
parent 3a7d810a15
commit 74efe4e201
14 changed files with 472 additions and 679 deletions

View File

@@ -20,7 +20,7 @@
#include "post_effect.h"
#include "layout/stack.h"
#include "fill_box.h"
#include "layout/slot.h"
#include "layout/modifiers/modifiers.h" // 新的布局修饰器
#include "image/texture_manager.h"
using namespace mirage;
@@ -408,31 +408,51 @@ private:
std::cout << "clicked" << std::endl;
};
// 使用 slot 系统演示
// slot 允许在不修改子控件的情况下附加布局参数
// ===================================================================
// 布局修饰器管道语法示例
// ===================================================================
//
// ===== 旧写法(已弃用)=====
// slot{
// imager{}.texture_id(id_)
// }.align(alignment::CENTER).padding(10.f)
//
// slot{
// overlay{...}
// } | stretch()
//
// ===== 新写法(推荐)=====
// imager{}.texture_id(id_)
// | align(alignment::CENTER)
// | padding(10.f)
//
// overlay{...} | stretch()
//
// ===================================================================
// 使用新的布局修饰器管道语法
v_stack v{
// 普通方式:直接放置控件
button{
"Hello",
on_click
},
// 使用 slot 方式:通过 slot 包装控件并设置对齐和边距
slot{
overlay{
// 使用 slot 设置图片居中对齐
slot{
imager{}
.texture_id(texture_id_)
.fit(image_fit::contain)
.set_texture_size(texture_size_)
}.align(alignment::CENTER).padding(10.f),
// 普通后效控件(不使用 slot
post_effect_widget{
fill_box{},
blur_effect{20.f}
},
}
} | stretch()
// 新的管道语法:通过修饰器添加布局属性
// 不再需要 slot{} 包装,直接使用 | 管道操作符
overlay{
// 使用管道语法设置图片居中对齐和边距
imager{}
.texture_id(texture_id_)
.fit(image_fit::contain)
.set_texture_size(texture_size_)
| align(alignment::CENTER)
| padding(10.f),
// 普通后效控件
post_effect_widget{
fill_box{},
blur_effect{20.f}
},
} | stretch() // 直接对 overlay 应用 stretch无需 slot 包装
};
layout_state state;

View File

@@ -2,6 +2,7 @@
#include "widget.h"
#include "render_command.h"
#include "event_target.h"
#include "layout/modifiers/modifier_traits.h"
#include <memory>
using mirage::event_target;
@@ -41,7 +42,12 @@ class any_widget {
}
[[nodiscard]] auto get_event_target() -> event_target* override {
if constexpr (std::is_base_of_v<event_target, T>) {
// 情况 1: 布局修饰器 - 穿透获取内部事件目标
if constexpr (mirage::modifier::is_layout_modifier<T>) {
return mirage::modifier::get_inner_event_target(data);
}
// 情况 2: 继承自 event_target 的普通 widget
else if constexpr (std::is_base_of_v<event_target, T>) {
return &data;
}
else {
@@ -50,7 +56,12 @@ class any_widget {
}
[[nodiscard]] auto get_event_target() const -> const event_target* override {
if constexpr (std::is_base_of_v<event_target, T>) {
// 情况 1: 布局修饰器 - 穿透获取内部事件目标
if constexpr (mirage::modifier::is_layout_modifier<T>) {
return mirage::modifier::get_inner_event_target(const_cast<T&>(data));
}
// 情况 2: 继承自 event_target 的普通 widget
else if constexpr (std::is_base_of_v<event_target, T>) {
return &data;
}
else {

View File

@@ -3,14 +3,12 @@
#include "render_command.h"
#include "event_target.h"
#include "any_widget.h"
#include "slot.h"
#include "layout_types.h"
#include <vector>
#include <utility>
namespace mirage {
// anchors 已在 slot.h 中定义
/// @brief Canvas槽位 - 存储子Widget及其布局信息
struct canvas_slot {
any_widget child; // 子Widget使用类型擦除
@@ -74,69 +72,21 @@ namespace mirage {
/// @brief 添加子Widget默认锚点左上角
template <typename W>
auto&& add_child(this auto&& self, W&& child, vec2f_t position = vec2f_t::Zero()) {
using ChildType = std::remove_cvref_t<W>;
// 情况 1: property_slot 类型(新系统)
if constexpr (layout::is_property_slot_v<ChildType>) {
anchors anch = anchors::top_left();
vec2f_t pos = position;
vec2f_t sz = vec2f_t::Zero();
int32_t z = 0;
// 从 property_slot 中提取属性
if (auto anchor_p = child.template get_property<layout::anchor_tag>()) {
anch = anchor_p->value;
}
if (auto pos_p = child.template get_property<layout::position_tag>()) {
pos = pos_p->value;
}
if (auto size_p = child.template get_property<layout::size_tag>()) {
sz = size_p->value;
}
if (auto z_p = child.template get_property<layout::z_order_tag>()) {
z = z_p->value;
}
// property_slot 内部包含 slotslot 会处理 padding 和 alignment
self.slots_.emplace_back(std::forward<W>(child), anch, pos, sz, z);
}
// 情况 2: slot 类型(新系统,没有容器属性)
else if constexpr (layout::is_slot_v<ChildType>) {
// slot 会处理 padding 和 alignment直接添加
self.slots_.emplace_back(std::forward<W>(child), anchors::top_left(), position);
}
// 情况 3: 普通 widget
else {
self.slots_.emplace_back(std::forward<W>(child), anchors::top_left(), position);
}
self.slots_.emplace_back(std::forward<W>(child), anchors::top_left(), position);
return std::forward<decltype(self)>(self);
}
/// @brief 添加子Widget指定锚点
template <typename W>
auto&& add_child(this auto&& self, W&& child, anchors anchor, vec2f_t position = vec2f_t::Zero()) {
using ChildType = std::remove_cvref_t<W>;
if constexpr (layout::is_property_slot_v<ChildType> || layout::is_slot_v<ChildType>) {
self.slots_.emplace_back(std::forward<W>(child), anchor, position);
}
else {
self.slots_.emplace_back(std::forward<W>(child), anchor, position);
}
self.slots_.emplace_back(std::forward<W>(child), anchor, position);
return std::forward<decltype(self)>(self);
}
/// @brief 添加拉伸子Widget
template <typename W>
auto&& add_stretched_child(this auto&& self, W&& child, anchors anchor, vec2f_t position, vec2f_t size) {
using ChildType = std::remove_cvref_t<W>;
if constexpr (layout::is_property_slot_v<ChildType> || layout::is_slot_v<ChildType>) {
self.slots_.emplace_back(std::forward<W>(child), anchor, position, size);
}
else {
self.slots_.emplace_back(std::forward<W>(child), anchor, position, size);
}
self.slots_.emplace_back(std::forward<W>(child), anchor, position, size);
return std::forward<decltype(self)>(self);
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include "container_properties.h"
#include "slot_traits.h"
#include "widget.h"
#include <tuple>
#include <type_traits>
@@ -57,34 +57,4 @@ namespace mirage {
inline constexpr bool container_supports_property_v =
detail::tuple_contains_v<Property, typename container_capabilities<Container>::supported_properties>;
// ============================================================================
// 编译期属性验证工具
// ============================================================================
namespace detail {
/// @brief 检查 property_slot 的所有属性是否都被容器支持
template <typename Container, typename PropertySlot>
struct all_properties_supported_impl;
template <typename Container, typename SlotType, typename... Properties>
struct all_properties_supported_impl<Container, mirage::property_slot<SlotType, Properties...>> {
static constexpr bool value = (container_supports_property_v<Container, Properties> && ...);
};
template <typename Container, typename T>
struct all_properties_supported {
static constexpr bool value = true; // 非 property_slot 类型,默认支持
};
template <typename Container, typename SlotType, typename... Properties>
struct all_properties_supported<Container, mirage::property_slot<SlotType, Properties...>>
: all_properties_supported_impl<Container, mirage::property_slot<SlotType, Properties...>> {
};
} // namespace detail
/// @brief 检查子控件的所有属性是否都被容器支持
template <typename Container, typename Child>
inline constexpr bool all_properties_supported_v =
detail::all_properties_supported<Container, std::remove_cvref_t<Child>>::value;
} // namespace mirage

View File

@@ -61,7 +61,7 @@ namespace mirage {
// 属性工厂函数
// ============================================================================
inline constexpr auto stretch(float factor = 1.0f) { return stretch_tag{factor}; }
// inline constexpr auto stretch(float factor = 1.0f) { return stretch_tag{factor}; }
inline auto anchor(const anchors& a) { return anchor_tag{a}; }
inline auto at(const vec2f_t& pos) { return position_tag{pos}; }
inline auto at(float x, float y) { return position_tag{x, y}; }

View File

@@ -0,0 +1,69 @@
#pragma once
#include "layout/layout_types.h"
#include "layout/alignment_utils.h"
#include "widget.h"
#include "layout_state.h"
namespace mirage {
namespace modifier {
// align 配置类
struct align_config {
alignment value;
constexpr explicit align_config(alignment a) : value(a) {
}
};
// align 修饰器类
template <widget Child>
class align_modifier {
Child child_;
alignment alignment_;
public:
explicit align_modifier(Child child, alignment a) : child_(std::move(child)), alignment_(a) {
}
// ===== widget concept 实现 =====
[[nodiscard]] auto measure(const vec2f_t& available_size) const -> vec2f_t {
// 对于 FILL 对齐,返回可用空间;否则返回子控件测量尺寸
if (alignment_ == alignment::FILL) {
return available_size;
}
return child_.measure(available_size);
}
void arrange(const layout_state& state) {
const vec2f_t child_size = (alignment_ == alignment::FILL)
? state.size()
: child_.measure(state.size());
const vec2f_t pos = calculate_aligned_position(child_size, state.size(), alignment_);
child_.arrange(state.derive_child(pos, child_size));
}
template <typename Builder>
void build_render_commands(Builder& builder) const {
child_.build_render_commands(builder);
}
// 访问内部子控件
[[nodiscard]] const Child& child() const { return child_; }
[[nodiscard]] Child& child() { return child_; }
// 访问对齐值
[[nodiscard]] alignment get_alignment() const { return alignment_; }
};
// 管道操作符
template <widget W>
auto operator|(W&& w, align_config cfg) -> align_modifier<std::remove_cvref_t<W>> {
return align_modifier<std::remove_cvref_t<W>>(std::forward<W>(w), cfg.value);
}
} // namespace mirage::modifier
// 便捷工厂函数
constexpr auto align(alignment a) { return modifier::align_config{a}; }
} // namespace mirage

View File

@@ -0,0 +1,79 @@
#pragma once
#include <type_traits>
namespace mirage {
// 前向声明 event_target在 mirage 命名空间)
class event_target;
}
namespace mirage::modifier {
// 前向声明
template <widget Child>
class padding_modifier;
template <widget Child>
class align_modifier;
template <widget Child>
class stretch_modifier;
// ===== 修饰器类型萃取 =====
// 检测是否为 stretch_modifier
template<typename T>
concept is_stretch_modifier = std::is_base_of_v<stretch_modifier<T>, T>;
// 检测是否为 padding_modifier
template<typename T>
concept is_padding_modifier = std::is_base_of_v<padding_modifier<std::remove_cvref_t<T>>, std::remove_cvref_t<T>>;
// 检测是否为 align_modifier
template<typename T>
concept is_align_modifier = std::is_base_of_v<align_modifier<std::remove_cvref_t<T>>, std::remove_cvref_t<T>>;
// 检测是否为任意布局修饰器
template <typename T>
concept is_layout_modifier = is_stretch_modifier<T> || is_padding_modifier<T> || is_align_modifier<T>;
template<typename T>
concept has_child = requires(T t) {
{ t.child() } -> std::same_as<typename T::child_type>;
};
// 获取 flex_factor用于 stretch
template <typename T>
constexpr float get_flex_factor([[maybe_unused]] const T& widget) {
if constexpr (is_stretch_modifier<T>) {
return widget.flex_factor();
}
else {
return 0.0f;
}
}
// 检测是否应该 stretch
template <typename T>
constexpr bool should_stretch([[maybe_unused]] const T& widget) {
return is_stretch_modifier<T>;
}
// ===== 事件穿透辅助函数 =====
/// @brief 递归获取修饰器内部的实际事件目标
/// 如果子控件是布局修饰器,则继续向下查找
/// 否则,如果子控件继承自 event_target则返回该子控件的指针
template <typename Child>
auto get_inner_event_target(Child& child) -> event_target* {
if constexpr (is_layout_modifier<std::remove_cvref_t<Child>>) {
// 布局修饰器:递归获取内部子控件
return get_inner_event_target(child.child());
}
else if constexpr (std::is_base_of_v<event_target, std::remove_cvref_t<Child>>) {
// 继承自 event_target 的子控件
return &child;
}
else {
// 非 event_target 子控件
return nullptr;
}
}
} // namespace mirage::modifier

View File

@@ -0,0 +1,8 @@
#pragma once
// 布局修饰器统一入口
#include "modifier_traits.h"
#include "padding_modifier.h"
#include "align_modifier.h"
#include "stretch_modifier.h"

View File

@@ -0,0 +1,100 @@
#pragma once
#include "layout/layout_types.h"
#include "widget.h"
#include "layout_state.h"
namespace mirage {
namespace modifier {
// padding 配置类
struct padding_config {
margin value;
constexpr explicit padding_config(float all) : value{all, all, all, all} {
}
constexpr padding_config(float horizontal, float vertical) : value{
horizontal, vertical, horizontal, vertical
} {
}
constexpr padding_config(float left, float top, float right, float bottom) : value{
left, top, right, bottom
} {
}
constexpr explicit padding_config(margin m) : value(m) {
}
};
// padding 修饰器类
template <widget Child>
class padding_modifier {
Child child_;
margin padding_;
public:
explicit padding_modifier(Child child, margin p) : child_(std::move(child)), padding_(p) {
}
// ===== widget concept 实现 =====
[[nodiscard]] auto measure(const vec2f_t& available_size) const -> vec2f_t {
const float h_padding = padding_.left + padding_.right;
const float v_padding = padding_.top + padding_.bottom;
// 减去 padding 后传给子控件
const vec2f_t inner_size{
available_size.x() > h_padding ? available_size.x() - h_padding : 0.0f,
available_size.y() > v_padding ? available_size.y() - v_padding : 0.0f
};
const vec2f_t child_size = child_.measure(inner_size);
// 加上 padding 返回
return vec2f_t{child_size.x() + h_padding, child_size.y() + v_padding};
}
void arrange(const layout_state& state) {
const float h_padding = padding_.left + padding_.right;
const float v_padding = padding_.top + padding_.bottom;
const vec2f_t inner_size{
state.size().x() > h_padding ? state.size().x() - h_padding : 0.0f,
state.size().y() > v_padding ? state.size().y() - v_padding : 0.0f
};
const vec2f_t offset{padding_.left, padding_.top};
child_.arrange(state.derive_child(offset, inner_size));
}
template <typename Builder>
void build_render_commands(Builder& builder) const {
child_.build_render_commands(builder);
}
// 访问内部子控件
[[nodiscard]] const Child& child() const { return child_; }
[[nodiscard]] Child& child() { return child_; }
// 访问 padding 值
[[nodiscard]] const margin& get_padding() const { return padding_; }
};
// 管道操作符
template <widget W>
auto operator|(W&& w, padding_config cfg) -> padding_modifier<std::remove_cvref_t<W>> {
return padding_modifier<std::remove_cvref_t<W>>(std::forward<W>(w), cfg.value);
}
}
// 便捷工厂函数
constexpr auto padding(float all) { return modifier::padding_config{all}; }
constexpr auto padding(float horizontal, float vertical) { return modifier::padding_config{horizontal, vertical}; }
constexpr auto padding(float left, float top, float right, float bottom) {
return modifier::padding_config{left, top, right, bottom};
}
constexpr auto padding(margin m) { return modifier::padding_config{m}; }
} // namespace mirage::modifier

View File

@@ -0,0 +1,60 @@
#pragma once
#include "widget.h"
#include "layout_state.h"
namespace mirage {
namespace modifier {
// stretch 配置类
struct stretch_config {
float flex_factor = 1.0f;
constexpr explicit stretch_config(float f = 1.0f) : flex_factor(f) {
}
};
// stretch 修饰器类 - 作为容器属性的标记
template <widget Child>
class stretch_modifier {
Child child_;
float flex_factor_;
public:
// 标记:表明这是一个 stretch 类型
static constexpr bool is_stretch = true;
explicit stretch_modifier(Child child, float flex = 1.0f) : child_(std::move(child)), flex_factor_(flex) {
}
[[nodiscard]] float flex_factor() const { return flex_factor_; }
// ===== widget concept 转发 =====
[[nodiscard]] auto measure(const vec2f_t& available_size) const -> vec2f_t {
return child_.measure(available_size);
}
void arrange(const layout_state& state) {
child_.arrange(state);
}
template <typename Builder>
void build_render_commands(Builder& builder) const {
child_.build_render_commands(builder);
}
// 访问内部子控件
[[nodiscard]] const Child& child() const { return child_; }
[[nodiscard]] Child& child() { return child_; }
};
// 管道操作符
template <widget W>
auto operator|(W&& w, stretch_config cfg) -> stretch_modifier<std::remove_cvref_t<W>> {
return stretch_modifier<std::remove_cvref_t<W>>(std::forward<W>(w), cfg.flex_factor);
}
}
// 便捷工厂函数
constexpr auto stretch(float factor = 1.0f) { return modifier::stretch_config{factor}; }
} // namespace mirage::modifier

View File

@@ -4,14 +4,13 @@
#include "event_target.h"
#include "dynamic_list.h"
#include "culling_config.h"
#include "slot.h"
#include "modifiers/modifiers.h"
#include "layout_types.h"
#include <tuple>
#include <vector>
#include <utility>
namespace mirage {
// alignment 和 margin 已在 slot.h 中定义
/// @brief Overlay容器模板
template <widget... Children>
class overlay : public event_target, public z_order_mixin<overlay<Children...>> {
@@ -123,34 +122,28 @@ namespace mirage {
std::vector<event_target*> get_event_children() const override {
std::vector<event_target*> event_children;
std::apply([&](auto&... child) {
auto func = [&](auto&... child) {
(
[&]() {
[&] {
using ChildType = std::remove_cvref_t<decltype(child)>;
// 情况 1: dynamic_list
if constexpr (std::is_same_v<ChildType, dynamic_list>) {
event_children.push_back(const_cast<dynamic_list*>(&child));
}
// 情况 2: property_slot 类型(新系统)
else if constexpr (is_property_slot_v<ChildType>) {
using SlotChildType = slot_child_type_t<ChildType>;
if constexpr (std::is_base_of_v<event_target, SlotChildType>) {
event_children.push_back(const_cast<SlotChildType*>(&child.child()));
}
}
// 情况 3: slot 类型(新系统)
else if constexpr (is_slot_v<ChildType>) {
using SlotChildType = slot_child_type_t<ChildType>;
if constexpr (std::is_base_of_v<event_target, SlotChildType>) {
event_children.push_back(const_cast<SlotChildType*>(&child.child()));
// 情况 2: 布局修饰器 - 穿透获取内部事件目标
else if constexpr (modifier::is_layout_modifier<ChildType>) {
if (auto* target = modifier::get_inner_event_target(const_cast<ChildType&>(child))) {
event_children.push_back(target);
}
}
// 情况 3: 继承自 event_target 的普通 widget
else if constexpr (std::is_base_of_v<event_target, ChildType>) {
event_children.push_back(const_cast<ChildType*>(&child));
}
}()
, ...);
}, children_);
}(),
...);
};
std::apply(func, children_);
return event_children;
}
@@ -174,9 +167,9 @@ namespace mirage {
max_size = max_size.cwiseMax(child_size);
}
}
else if constexpr (is_property_slot_v<Child> || is_slot_v<Child>) {
// 新系统:slot/property_slot 直接处理 padding alignment
// 直接测量 slot它会处理内部的 padding
else if constexpr (modifier::is_layout_modifier<Child>) {
// 新系统:布局修饰器(padding, align, stretch
// 修饰器内部已封装布局逻辑,直接调用 measure
auto child_size = child.measure(available_size);
max_size = max_size.cwiseMax(child_size);
}
@@ -200,9 +193,10 @@ namespace mirage {
arrange_single_widget(sub_child, parent_state, overlay_size);
}
}
else if constexpr (is_property_slot_v<Child> || is_slot_v<Child>) {
// 新系统:slot/property_slot 直接处理 padding alignment
// 给 slot 分配整个 overlay 空间,让它自己处理对齐
else if constexpr (modifier::is_layout_modifier<Child>) {
// 新系统:布局修饰器(padding, align, stretch
// 修饰器内部已封装布局逻辑align计算对齐位置padding计算偏移
// 给修饰器分配整个 overlay 空间,让它自己处理布局
const auto child_state = parent_state.derive_child(vec2f_t::Zero(), overlay_size);
child.arrange(child_state);
}

View File

@@ -1,311 +0,0 @@
#pragma once
#include "widget.h"
#include "types.h"
#include "layout_types.h"
#include "container_properties.h"
#include "slot_traits.h"
#include "container_capabilities.h"
#include "alignment_utils.h"
#include <tuple>
#include <optional>
#include <type_traits>
#include <utility>
namespace mirage {
// ============================================================================
// 前向声明
// ============================================================================
// 容器前向声明
template <widget... Children>
class v_stack;
template <widget... Children>
class h_stack;
template <widget... Children>
class overlay;
class canvas;
// ============================================================================
// Slot 类
// ============================================================================
/// @brief Slot - 处理内部布局属性padding、alignment
template <typename Child>
class slot {
public:
explicit slot(Child child) : child_(std::move(child)) {
}
// ============================================================================
// 链式调用设置 padding
// ============================================================================
auto&& padding(this auto&& self, const margin& m) {
self.padding_ = m;
return std::forward<decltype(self)>(self);
}
auto&& padding(this auto&& self, float all) {
self.padding_ = margin(all, all, all, all);
return std::forward<decltype(self)>(self);
}
auto&& padding(this auto&& self, float horizontal, float vertical) {
self.padding_ = margin(horizontal, vertical, horizontal, vertical);
return std::forward<decltype(self)>(self);
}
auto&& padding(this auto&& self, float left, float top, float right, float bottom) {
self.padding_ = margin(left, top, right, bottom);
return std::forward<decltype(self)>(self);
}
// ============================================================================
// 链式调用设置 alignment
// ============================================================================
auto&& align(this auto&& self, alignment a) {
self.alignment_ = a;
return std::forward<decltype(self)>(self);
}
// ============================================================================
// Widget concept 实现
// ============================================================================
auto measure(const vec2f_t& available_size) const -> vec2f_t {
const vec2f_t inner_available(
std::max(0.0f, available_size.x() - padding_.left - padding_.right),
std::max(0.0f, available_size.y() - padding_.top - padding_.bottom)
);
vec2f_t child_size = child_.measure(inner_available);
return vec2f_t(
child_size.x() + padding_.left + padding_.right,
child_size.y() + padding_.top + padding_.bottom
);
}
void arrange(const layout_state& state) {
const vec2f_t inner_size(
std::max(0.0f, state.size().x() - padding_.left - padding_.right),
std::max(0.0f, state.size().y() - padding_.top - padding_.bottom)
);
const vec2f_t child_size = (alignment_ == alignment::FILL)
? inner_size
: child_.measure(inner_size);
const vec2f_t child_pos = calculate_aligned_position(child_size, inner_size, alignment_);
const vec2f_t final_offset(
padding_.left + child_pos.x(),
padding_.top + child_pos.y()
);
const auto child_state = state.derive_child(final_offset, child_size);
child_.arrange(child_state);
}
void build_render_commands(mirage::render_command_builder& builder) const {
child_.build_render_commands(builder);
}
void build_render_commands(mirage::render_command_builder& builder,
const mirage::culling_context& context) const {
invoke_build_render_commands(child_, builder, context);
}
// ============================================================================
// 访问器
// ============================================================================
const Child& child() const { return child_; }
Child& child() { return child_; }
const margin& get_padding() const { return padding_; }
alignment get_alignment() const { return alignment_; }
private:
Child child_;
margin padding_ = margin::zero();
alignment alignment_ = alignment::TOP_LEFT;
};
// ============================================================================
// property_slot 包装器
// ============================================================================
/// @brief 带容器属性的 Slot 包装器
template <typename SlotType, typename... Properties>
class property_slot {
public:
property_slot(SlotType slot, Properties... props) : slot_(std::move(slot)), properties_(std::move(props)...) {
}
// ============================================================================
// Widget concept 转发
// ============================================================================
auto measure(const vec2f_t& available_size) const -> vec2f_t {
return slot_.measure(available_size);
}
void arrange(const layout_state& state) {
slot_.arrange(state);
}
void build_render_commands(mirage::render_command_builder& builder) const {
// 应用 z_order如果有
if constexpr (has_property<z_order_tag>()) {
if (auto z = get_property<z_order_tag>()) {
builder.set_z_order(z->value);
}
}
slot_.build_render_commands(builder);
}
void build_render_commands(mirage::render_command_builder& builder,
const mirage::culling_context& context) const {
// 应用 z_order如果有
if constexpr (has_property<z_order_tag>()) {
if (auto z = get_property<z_order_tag>()) {
builder.set_z_order(z->value);
}
}
invoke_build_render_commands(slot_, builder, context);
}
// ============================================================================
// 属性检查和获取
// ============================================================================
template <typename P>
static constexpr bool has_property() {
return detail::contains_type_v<P, Properties...>;
}
template <typename P>
std::optional<P> get_property() const {
return detail::get_param<P>(properties_);
}
template <typename P>
P get_property_or(const P& default_value) const {
auto opt = get_property<P>();
return opt.has_value() ? opt.value() : default_value;
}
// ============================================================================
// 访问器
// ============================================================================
const SlotType& get_slot() const { return slot_; }
SlotType& get_slot() { return slot_; }
// 便捷访问子控件
const auto& child() const { return slot_.child(); }
auto& child() { return slot_.child(); }
const std::tuple<Properties...>& properties() const { return properties_; }
private:
SlotType slot_;
std::tuple<Properties...> properties_;
};
// ============================================================================
// 管道操作符
// ============================================================================
/// @brief slot | property -> property_slot
template <typename Child, typename Property> requires std::is_base_of_v<container_property_tag, Property>
auto operator|(slot<Child>&& s, Property&& prop) {
return property_slot<slot<Child>, std::remove_cvref_t<Property>>(
std::move(s), std::forward<Property>(prop)
);
}
/// @brief slot | property -> property_slot (左值版本)
template <typename Child, typename Property> requires std::is_base_of_v<container_property_tag, Property>
auto operator|(slot<Child>& s, Property&& prop) {
return property_slot<slot<Child>, std::remove_cvref_t<Property>>(
std::move(s), std::forward<Property>(prop)
);
}
/// @brief property_slot | property -> property_slot (链式添加属性)
template <typename SlotType, typename... ExistingProps, typename NewProperty> requires std::is_base_of_v<container_property_tag, NewProperty>
auto operator|(property_slot<SlotType, ExistingProps...>&& ps, NewProperty&& prop) {
// 创建新的 property_slot包含所有现有属性和新属性
auto func = [&](auto&&... existing_props) {
return property_slot<SlotType, ExistingProps..., std::remove_cvref_t<NewProperty>>(
std::move(ps.get_slot()),
std::forward<decltype(existing_props)>(existing_props)...,
std::forward<NewProperty>(prop)
);
};
return std::apply(func, std::move(ps).properties());
}
// ============================================================================
// 辅助函数
// ============================================================================
/// @brief 从 slot 或普通 widget 中提取子控件的引用
template <typename T>
decltype(auto) unwrap_slot(T&& item) {
if constexpr (is_property_slot_v<T>) {
return std::forward<T>(item).child();
}
else if constexpr (is_slot_v<T>) {
return std::forward<T>(item).child();
}
else {
return std::forward<T>(item);
}
}
/// @brief 从 property_slot 中提取指定类型的属性
template <typename PropertyType, typename T>
std::optional<PropertyType> extract_property(const T& item) {
if constexpr (is_property_slot_v<T>) {
return item.template get_property<PropertyType>();
}
else {
return std::nullopt;
}
}
/// @brief 从 property_slot 中提取指定类型的属性,如果不存在则返回默认值
template <typename PropertyType, typename T>
PropertyType extract_property_or(const T& item, const PropertyType& default_value) {
if constexpr (is_property_slot_v<T>) {
return item.template get_property_or<PropertyType>(default_value);
}
else {
return default_value;
}
}
/// @brief 检查 property_slot 是否包含指定属性
template <typename PropertyType, typename T>
constexpr bool has_property() {
if constexpr (is_property_slot_v<T>) {
return T::template has_property<PropertyType>();
}
else {
return false;
}
}
// ============================================================================
// 便捷构造函数
// ============================================================================
/// @brief 创建 slot 的便捷函数
template <widget W>
auto make_slot(W&& child) {
return slot<std::remove_cvref_t<W>>(std::forward<W>(child));
}
} // namespace mirage

View File

@@ -1,114 +0,0 @@
#pragma once
#include <tuple>
#include <optional>
#include <type_traits>
#include <utility>
namespace mirage {
// ============================================================================
// 前向声明
// ============================================================================
template <typename Child>
class slot;
template <typename SlotType, typename... Properties>
class property_slot;
// ============================================================================
// 类型萃取工具detail 命名空间)
// ============================================================================
namespace detail {
/// @brief 检查类型T是否在参数包Params中
template <typename T, typename... Params>
struct contains_type : std::disjunction<std::is_same<T, Params>...> {
};
template <typename T, typename... Params>
inline constexpr bool contains_type_v = contains_type<T, Params...>::value;
/// @brief 从tuple中获取指定类型的值
template <typename T, typename Tuple, std::size_t... Is>
constexpr std::optional<T> get_param_impl(const Tuple& tuple, std::index_sequence<Is...>) {
std::optional<T> result;
(
[&]() {
using ElemType = std::tuple_element_t<Is, Tuple>;
if constexpr (std::is_same_v<ElemType, T>) {
result = std::get<Is>(tuple);
}
}()
, ...);
return result;
}
template <typename T, typename... Params>
constexpr std::optional<T> get_param(const std::tuple<Params...>& tuple) {
return get_param_impl<T>(tuple, std::index_sequence_for<Params...>{});
}
/// @brief 检查tuple中是否包含指定类型
template <typename T, typename Tuple>
struct tuple_contains;
template <typename T, typename... Types>
struct tuple_contains<T, std::tuple<Types...>> : contains_type<T, Types...> {
};
template <typename T, typename Tuple>
inline constexpr bool tuple_contains_v = tuple_contains<T, Tuple>::value;
} // namespace detail
// ============================================================================
// Slot 类型萃取
// ============================================================================
/// @brief 检查类型是否为 slot
template <typename T>
struct is_slot : std::false_type {
};
template <typename Child>
struct is_slot<slot<Child>> : std::true_type {
};
template <typename T>
inline constexpr bool is_slot_v = is_slot<std::remove_cvref_t<T>>::value;
/// @brief 检查类型是否为 property_slot
template <typename T>
struct is_property_slot : std::false_type {
};
template <typename SlotType, typename... Properties>
struct is_property_slot<property_slot<SlotType, Properties...>> : std::true_type {
};
template <typename T>
inline constexpr bool is_property_slot_v = is_property_slot<std::remove_cvref_t<T>>::value;
/// @brief 检查类型是否为 slot 或 property_slot
template <typename T>
inline constexpr bool is_any_slot_v = is_slot_v<T> || is_property_slot_v<T>;
/// @brief 获取 slot 的子控件类型
template <typename T>
struct slot_child_type {
using type = T; // 非 slot 类型,返回自身
};
template <typename Child>
struct slot_child_type<slot<Child>> {
using type = Child;
};
template <typename SlotType, typename... Properties>
struct slot_child_type<property_slot<SlotType, Properties...>> {
using type = slot_child_type<SlotType>::type;
};
template <typename T>
using slot_child_type_t = slot_child_type<std::remove_cvref_t<T>>::type;
} // namespace mirage

View File

@@ -4,7 +4,7 @@
#include "dynamic_list.h"
#include "culling_config.h"
#include "event_target.h"
#include "slot.h"
#include "modifiers/modifiers.h"
#include <tuple>
#include <vector>
@@ -16,32 +16,12 @@ namespace mirage {
// ============================================================================
namespace stack_detail {
/// @brief 从子控件中提取 padding/margin
/// slot 直接处理 padding这里返回 zero
template <typename Child>
margin extract_child_margin(const Child& child) {
if constexpr (is_slot_v<Child>) {
// slot 类型padding 已经在 slot 内部处理
return margin::zero();
}
else if constexpr (is_property_slot_v<Child>) {
// property_slot 类型padding 在内部 slot 处理
return margin::zero();
}
else {
// 普通 widget
return margin::zero();
}
}
/// @brief 从子控件中提取 stretch 信息
template <typename Child>
std::optional<float> extract_stretch_factor(const Child& child) {
if constexpr (is_property_slot_v<Child>) {
// 新系统:从 property_slot 提取 stretch_tag
if (auto sp = child.template get_property<stretch_tag>()) {
return sp->flex_factor;
}
// 新系统:检测 stretch_modifier
if constexpr (modifier::is_stretch_modifier<Child>) {
return modifier::get_flex_factor(child);
}
return std::nullopt;
}
@@ -49,17 +29,33 @@ namespace mirage {
/// @brief 判断子控件是否应该 stretch
template <typename Child>
bool should_stretch(const Child& child) {
// 新系统:检测 stretch_modifier
if constexpr (modifier::is_stretch_modifier<Child>) {
return true;
}
// 旧系统
return extract_stretch_factor(child).has_value();
}
/// @brief 获取 stretch 的 flex 因子
template <typename Child>
float get_flex_factor(const Child& child) {
// 新系统:检测 stretch_modifier
if constexpr (modifier::is_stretch_modifier<Child>) {
return modifier::get_flex_factor(child);
}
// 旧系统
if (auto factor = extract_stretch_factor(child)) {
return *factor;
}
return 0.0f;
}
/// @brief 检测是否为布局修饰器
template <typename Child>
constexpr bool is_layout_modifier() {
return modifier::is_layout_modifier<Child>;
}
}
// ============================================================================
@@ -150,21 +146,13 @@ namespace mirage {
if constexpr (std::is_same_v<ChildType, dynamic_list>) {
event_children.push_back(const_cast<dynamic_list*>(&child));
}
// 情况 2: property_slot 类型(新系统)
else if constexpr (is_property_slot_v<ChildType>) {
using SlotChildType = slot_child_type_t<ChildType>;
if constexpr (std::is_base_of_v<event_target, SlotChildType>) {
event_children.push_back(const_cast<SlotChildType*>(&child.child()));
// 情况 2: 布局修饰器 - 穿透获取内部事件目标
else if constexpr (modifier::is_layout_modifier<ChildType>) {
if (auto* target = modifier::get_inner_event_target(const_cast<ChildType&>(child))) {
event_children.push_back(target);
}
}
// 情况 3: slot 类型(新系统)
else if constexpr (is_slot_v<ChildType>) {
using SlotChildType = slot_child_type_t<ChildType>;
if constexpr (std::is_base_of_v<event_target, SlotChildType>) {
event_children.push_back(const_cast<SlotChildType*>(&child.child()));
}
}
// 情况 4: 继承自 event_target 的普通 widget
// 情况 3: 继承自 event_target 的普通 widget
else if constexpr (std::is_base_of_v<event_target, ChildType>) {
event_children.push_back(const_cast<ChildType*>(&child));
}
@@ -187,16 +175,14 @@ namespace mirage {
fixed_height += child_size.y();
}
}
else if constexpr (is_property_slot_v<Child> || is_slot_v<Child>) {
if (stack_detail::should_stretch(child)) {
// stretch 子组件:尺寸由 flex 分配
total_flex += stack_detail::get_flex_factor(child);
}
else {
// 固定尺寸子组件
const auto child_size = child.measure(available_size);
fixed_height += child_size.y();
}
else if constexpr (modifier::is_stretch_modifier<Child>) {
// 新系统stretch_modifier
total_flex += modifier::get_flex_factor(child);
}
else if constexpr (modifier::is_layout_modifier<Child>) {
// 其他布局修饰器padding, align按固定尺寸处理
const auto child_size = child.measure(available_size);
fixed_height += child_size.y();
}
else {
// 直接处理普通 widget总是固定尺寸
@@ -217,22 +203,14 @@ namespace mirage {
total_height += child_size.y();
}
}
else if constexpr (is_property_slot_v<Child> || is_slot_v<Child>) {
if (stack_detail::should_stretch(child)) {
// stretch 子组件在 measure 时不贡献高
// 但仍需测量宽度
const auto child_size = child.measure(available_size);
max_width = std::max(max_width, child_size.x());
}
else {
// 固定尺寸子组件
const auto child_size = child.measure(available_size);
max_width = std::max(max_width, child_size.x());
total_height += child_size.y();
}
else if constexpr (modifier::is_stretch_modifier<Child>) {
// stretch 子组件在 measure 时不贡献高度
// 但仍需测量宽
const auto child_size = child.measure(available_size);
max_width = std::max(max_width, child_size.x());
}
else {
// 直接处理普通 widget
// 直接处理普通 widget 或其他修饰器
const auto child_size = child.measure(available_size);
max_width = std::max(max_width, child_size.x());
total_height += child_size.y();
@@ -252,27 +230,24 @@ namespace mirage {
current_y += child_size.y();
}
}
else if constexpr (is_property_slot_v<Child> || is_slot_v<Child>) {
else if constexpr (modifier::is_stretch_modifier<Child>) {
// 新系统stretch_modifier
vec2f_t child_size;
if (stack_detail::should_stretch(child) && total_flex > 0.0f) {
// stretch 子组件:根据 flex 比例分配高度
const float flex_factor = stack_detail::get_flex_factor(child);
if (total_flex > 0.0f) {
const float flex_factor = modifier::get_flex_factor(child);
const float stretch_height = (flex_factor / total_flex) * available_stretch_height;
// 测量得到宽度,使用 stretch 高度
const auto measured_size = child.measure(state.size());
child_size = vec2f_t{measured_size.x(), stretch_height};
const auto measured_size = child.measure(state.size());
child_size = vec2f_t{measured_size.x(), stretch_height};
}
else {
// 固定尺寸子组件
child_size = child.measure(state.size());
}
const auto child_state = state.derive_child(vec2f_t{0.0f, current_y}, child_size);
child.arrange(child_state);
current_y += child_size.y();
}
else {
// 直接处理普通 widget
// 直接处理普通 widget 或其他修饰器
const auto child_size = child.measure(state.size());
const auto child_state = state.derive_child(vec2f_t{0.0f, current_y}, child_size);
child.arrange(child_state);
@@ -363,33 +338,28 @@ namespace mirage {
std::vector<event_target*> get_event_children() const override {
std::vector<event_target*> event_children;
std::apply([&](auto&... child) {
auto func = [&](auto&... child) {
(
[&]() {
using ChildType = std::remove_cvref_t<decltype(child)>;
// 情况 1: dynamic_list
if constexpr (std::is_same_v<ChildType, dynamic_list>) {
event_children.push_back(const_cast<dynamic_list*>(&child));
}
// 情况 2: property_slot 类型(新系统)
else if constexpr (is_property_slot_v<ChildType>) {
using SlotChildType = slot_child_type_t<ChildType>;
if constexpr (std::is_base_of_v<event_target, SlotChildType>) {
event_children.push_back(const_cast<SlotChildType*>(&child.child()));
}
}
// 情况 3: slot 类型(新系统)
else if constexpr (is_slot_v<ChildType>) {
using SlotChildType = slot_child_type_t<ChildType>;
if constexpr (std::is_base_of_v<event_target, SlotChildType>) {
event_children.push_back(const_cast<SlotChildType*>(&child.child()));
// 情况 2: 布局修饰器 - 穿透获取内部事件目标
else if constexpr (modifier::is_layout_modifier<ChildType>) {
if (auto* target = modifier::get_inner_event_target(const_cast<ChildType&>(child))) {
event_children.push_back(target);
}
}
// 情况 3: 继承自 event_target 的普通 widget
else if constexpr (std::is_base_of_v<event_target, ChildType>) {
event_children.push_back(const_cast<ChildType*>(&child));
}
}()
, ...);
}, children_);
};
std::apply(func, children_);
return event_children;
}
@@ -406,16 +376,14 @@ namespace mirage {
fixed_width += child_size.x();
}
}
else if constexpr (is_property_slot_v<Child> || is_slot_v<Child>) {
if (stack_detail::should_stretch(child)) {
// stretch 子组件:尺寸由 flex 分配
total_flex += stack_detail::get_flex_factor(child);
}
else {
// 固定尺寸子组件
const auto child_size = child.measure(available_size);
fixed_width += child_size.x();
}
else if constexpr (modifier::is_stretch_modifier<Child>) {
// 新系统stretch_modifier
total_flex += modifier::get_flex_factor(child);
}
else if constexpr (modifier::is_layout_modifier<Child>) {
// 其他布局修饰器padding, align按固定尺寸处理
const auto child_size = child.measure(available_size);
fixed_width += child_size.x();
}
else {
// 直接处理普通 widget总是固定尺寸
@@ -436,22 +404,14 @@ namespace mirage {
max_height = std::max(max_height, child_size.y());
}
}
else if constexpr (is_property_slot_v<Child> || is_slot_v<Child>) {
if (stack_detail::should_stretch(child)) {
// stretch 子组件在 measure 时不贡献宽
// 但仍需测量高度
const auto child_size = child.measure(available_size);
max_height = std::max(max_height, child_size.y());
}
else {
// 固定尺寸子组件
const auto child_size = child.measure(available_size);
total_width += child_size.x();
max_height = std::max(max_height, child_size.y());
}
else if constexpr (modifier::is_stretch_modifier<Child>) {
// stretch 子组件在 measure 时不贡献宽度
// 但仍需测量高
const auto child_size = child.measure(available_size);
max_height = std::max(max_height, child_size.y());
}
else {
// 直接处理普通 widget
// 直接处理普通 widget 或其他修饰器
const auto child_size = child.measure(available_size);
total_width += child_size.x();
max_height = std::max(max_height, child_size.y());
@@ -471,27 +431,24 @@ namespace mirage {
current_x += child_size.x();
}
}
else if constexpr (is_property_slot_v<Child> || is_slot_v<Child>) {
else if constexpr (modifier::is_stretch_modifier<Child>) {
// 新系统stretch_modifier
vec2f_t child_size;
if (stack_detail::should_stretch(child) && total_flex > 0.0f) {
// stretch 子组件:根据 flex 比例分配宽度
const float flex_factor = stack_detail::get_flex_factor(child);
if (total_flex > 0.0f) {
const float flex_factor = modifier::get_flex_factor(child);
const float stretch_width = (flex_factor / total_flex) * available_stretch_width;
// 测量得到高度,使用 stretch 宽度
const auto measured_size = child.measure(state.size());
child_size = vec2f_t{stretch_width, measured_size.y()};
const auto measured_size = child.measure(state.size());
child_size = vec2f_t{stretch_width, measured_size.y()};
}
else {
// 固定尺寸子组件
child_size = child.measure(state.size());
}
const auto child_state = state.derive_child(vec2f_t{current_x, 0.0f}, child_size);
child.arrange(child_state);
current_x += child_size.x();
}
else {
// 直接处理普通 widget
// 直接处理普通 widget 或其他修饰器
const auto child_size = child.measure(state.size());
const auto child_state = state.derive_child(vec2f_t{current_x, 0.0f}, child_size);
child.arrange(child_state);