From 74efe4e2010bbcb1eb2bbb590c71184a04b75896 Mon Sep 17 00:00:00 2001 From: nanako <469449812@qq.com> Date: Sun, 30 Nov 2025 15:53:24 +0800 Subject: [PATCH] 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. --- example/new_pipeline/new_pipeline_example.cpp | 60 ++-- src/widget/any_widget.h | 15 +- src/widget/layout/canvas.h | 58 +--- src/widget/layout/container_capabilities.h | 32 +- src/widget/layout/container_properties.h | 2 +- src/widget/layout/modifiers/align_modifier.h | 69 ++++ src/widget/layout/modifiers/modifier_traits.h | 79 +++++ src/widget/layout/modifiers/modifiers.h | 8 + .../layout/modifiers/padding_modifier.h | 100 ++++++ .../layout/modifiers/stretch_modifier.h | 60 ++++ src/widget/layout/overlay.h | 48 ++- src/widget/layout/slot.h | 311 ------------------ src/widget/layout/slot_traits.h | 114 ------- src/widget/layout/stack.h | 195 +++++------ 14 files changed, 472 insertions(+), 679 deletions(-) create mode 100644 src/widget/layout/modifiers/align_modifier.h create mode 100644 src/widget/layout/modifiers/modifier_traits.h create mode 100644 src/widget/layout/modifiers/modifiers.h create mode 100644 src/widget/layout/modifiers/padding_modifier.h create mode 100644 src/widget/layout/modifiers/stretch_modifier.h delete mode 100644 src/widget/layout/slot.h delete mode 100644 src/widget/layout/slot_traits.h diff --git a/example/new_pipeline/new_pipeline_example.cpp b/example/new_pipeline/new_pipeline_example.cpp index ba947f1..964d83f 100644 --- a/example/new_pipeline/new_pipeline_example.cpp +++ b/example/new_pipeline/new_pipeline_example.cpp @@ -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; diff --git a/src/widget/any_widget.h b/src/widget/any_widget.h index 2ce668c..d72e7fe 100644 --- a/src/widget/any_widget.h +++ b/src/widget/any_widget.h @@ -2,6 +2,7 @@ #include "widget.h" #include "render_command.h" #include "event_target.h" +#include "layout/modifiers/modifier_traits.h" #include 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) { + // 情况 1: 布局修饰器 - 穿透获取内部事件目标 + if constexpr (mirage::modifier::is_layout_modifier) { + return mirage::modifier::get_inner_event_target(data); + } + // 情况 2: 继承自 event_target 的普通 widget + else if constexpr (std::is_base_of_v) { 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) { + // 情况 1: 布局修饰器 - 穿透获取内部事件目标 + if constexpr (mirage::modifier::is_layout_modifier) { + return mirage::modifier::get_inner_event_target(const_cast(data)); + } + // 情况 2: 继承自 event_target 的普通 widget + else if constexpr (std::is_base_of_v) { return &data; } else { diff --git a/src/widget/layout/canvas.h b/src/widget/layout/canvas.h index 7c269d7..43af49d 100644 --- a/src/widget/layout/canvas.h +++ b/src/widget/layout/canvas.h @@ -3,14 +3,12 @@ #include "render_command.h" #include "event_target.h" #include "any_widget.h" -#include "slot.h" +#include "layout_types.h" #include #include namespace mirage { - // anchors 已在 slot.h 中定义 - /// @brief Canvas槽位 - 存储子Widget及其布局信息 struct canvas_slot { any_widget child; // 子Widget(使用类型擦除) @@ -74,69 +72,21 @@ namespace mirage { /// @brief 添加子Widget(默认锚点:左上角) template auto&& add_child(this auto&& self, W&& child, vec2f_t position = vec2f_t::Zero()) { - using ChildType = std::remove_cvref_t; - - // 情况 1: property_slot 类型(新系统) - if constexpr (layout::is_property_slot_v) { - 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()) { - anch = anchor_p->value; - } - if (auto pos_p = child.template get_property()) { - pos = pos_p->value; - } - if (auto size_p = child.template get_property()) { - sz = size_p->value; - } - if (auto z_p = child.template get_property()) { - z = z_p->value; - } - - // property_slot 内部包含 slot,slot 会处理 padding 和 alignment - self.slots_.emplace_back(std::forward(child), anch, pos, sz, z); - } - // 情况 2: slot 类型(新系统,没有容器属性) - else if constexpr (layout::is_slot_v) { - // slot 会处理 padding 和 alignment,直接添加 - self.slots_.emplace_back(std::forward(child), anchors::top_left(), position); - } - // 情况 3: 普通 widget - else { - self.slots_.emplace_back(std::forward(child), anchors::top_left(), position); - } + self.slots_.emplace_back(std::forward(child), anchors::top_left(), position); return std::forward(self); } /// @brief 添加子Widget(指定锚点) template auto&& add_child(this auto&& self, W&& child, anchors anchor, vec2f_t position = vec2f_t::Zero()) { - using ChildType = std::remove_cvref_t; - - if constexpr (layout::is_property_slot_v || layout::is_slot_v) { - self.slots_.emplace_back(std::forward(child), anchor, position); - } - else { - self.slots_.emplace_back(std::forward(child), anchor, position); - } + self.slots_.emplace_back(std::forward(child), anchor, position); return std::forward(self); } /// @brief 添加拉伸子Widget template auto&& add_stretched_child(this auto&& self, W&& child, anchors anchor, vec2f_t position, vec2f_t size) { - using ChildType = std::remove_cvref_t; - - if constexpr (layout::is_property_slot_v || layout::is_slot_v) { - self.slots_.emplace_back(std::forward(child), anchor, position, size); - } - else { - self.slots_.emplace_back(std::forward(child), anchor, position, size); - } + self.slots_.emplace_back(std::forward(child), anchor, position, size); return std::forward(self); } diff --git a/src/widget/layout/container_capabilities.h b/src/widget/layout/container_capabilities.h index 6c37bc7..4fe8423 100644 --- a/src/widget/layout/container_capabilities.h +++ b/src/widget/layout/container_capabilities.h @@ -1,6 +1,6 @@ #pragma once #include "container_properties.h" -#include "slot_traits.h" +#include "widget.h" #include #include @@ -57,34 +57,4 @@ namespace mirage { inline constexpr bool container_supports_property_v = detail::tuple_contains_v::supported_properties>; - // ============================================================================ - // 编译期属性验证工具 - // ============================================================================ - - namespace detail { - /// @brief 检查 property_slot 的所有属性是否都被容器支持 - template - struct all_properties_supported_impl; - - template - struct all_properties_supported_impl> { - static constexpr bool value = (container_supports_property_v && ...); - }; - - template - struct all_properties_supported { - static constexpr bool value = true; // 非 property_slot 类型,默认支持 - }; - - template - struct all_properties_supported> - : all_properties_supported_impl> { - }; - } // namespace detail - - /// @brief 检查子控件的所有属性是否都被容器支持 - template - inline constexpr bool all_properties_supported_v = - detail::all_properties_supported>::value; - } // namespace mirage \ No newline at end of file diff --git a/src/widget/layout/container_properties.h b/src/widget/layout/container_properties.h index 20087bf..d18424c 100644 --- a/src/widget/layout/container_properties.h +++ b/src/widget/layout/container_properties.h @@ -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}; } diff --git a/src/widget/layout/modifiers/align_modifier.h b/src/widget/layout/modifiers/align_modifier.h new file mode 100644 index 0000000..f02ca74 --- /dev/null +++ b/src/widget/layout/modifiers/align_modifier.h @@ -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 + 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 + 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 + auto operator|(W&& w, align_config cfg) -> align_modifier> { + return align_modifier>(std::forward(w), cfg.value); + } + } // namespace mirage::modifier + + // 便捷工厂函数 + constexpr auto align(alignment a) { return modifier::align_config{a}; } +} // namespace mirage diff --git a/src/widget/layout/modifiers/modifier_traits.h b/src/widget/layout/modifiers/modifier_traits.h new file mode 100644 index 0000000..c076b5d --- /dev/null +++ b/src/widget/layout/modifiers/modifier_traits.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +namespace mirage { + // 前向声明 event_target(在 mirage 命名空间) + class event_target; +} + +namespace mirage::modifier { + // 前向声明 + template + class padding_modifier; + template + class align_modifier; + template + class stretch_modifier; + + // ===== 修饰器类型萃取 ===== + + // 检测是否为 stretch_modifier + template + concept is_stretch_modifier = std::is_base_of_v, T>; + + // 检测是否为 padding_modifier + template + concept is_padding_modifier = std::is_base_of_v>, std::remove_cvref_t>; + + // 检测是否为 align_modifier + template + concept is_align_modifier = std::is_base_of_v>, std::remove_cvref_t>; + + // 检测是否为任意布局修饰器 + template + concept is_layout_modifier = is_stretch_modifier || is_padding_modifier || is_align_modifier; + + template + concept has_child = requires(T t) { + { t.child() } -> std::same_as; + }; + + // 获取 flex_factor(用于 stretch) + template + constexpr float get_flex_factor([[maybe_unused]] const T& widget) { + if constexpr (is_stretch_modifier) { + return widget.flex_factor(); + } + else { + return 0.0f; + } + } + + // 检测是否应该 stretch + template + constexpr bool should_stretch([[maybe_unused]] const T& widget) { + return is_stretch_modifier; + } + + // ===== 事件穿透辅助函数 ===== + + /// @brief 递归获取修饰器内部的实际事件目标 + /// 如果子控件是布局修饰器,则继续向下查找 + /// 否则,如果子控件继承自 event_target,则返回该子控件的指针 + template + auto get_inner_event_target(Child& child) -> event_target* { + if constexpr (is_layout_modifier>) { + // 布局修饰器:递归获取内部子控件 + return get_inner_event_target(child.child()); + } + else if constexpr (std::is_base_of_v>) { + // 继承自 event_target 的子控件 + return &child; + } + else { + // 非 event_target 子控件 + return nullptr; + } + } +} // namespace mirage::modifier diff --git a/src/widget/layout/modifiers/modifiers.h b/src/widget/layout/modifiers/modifiers.h new file mode 100644 index 0000000..d56b2c2 --- /dev/null +++ b/src/widget/layout/modifiers/modifiers.h @@ -0,0 +1,8 @@ +#pragma once + +// 布局修饰器统一入口 + +#include "modifier_traits.h" +#include "padding_modifier.h" +#include "align_modifier.h" +#include "stretch_modifier.h" diff --git a/src/widget/layout/modifiers/padding_modifier.h b/src/widget/layout/modifiers/padding_modifier.h new file mode 100644 index 0000000..4175b0f --- /dev/null +++ b/src/widget/layout/modifiers/padding_modifier.h @@ -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 + 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 + 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 + auto operator|(W&& w, padding_config cfg) -> padding_modifier> { + return padding_modifier>(std::forward(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 diff --git a/src/widget/layout/modifiers/stretch_modifier.h b/src/widget/layout/modifiers/stretch_modifier.h new file mode 100644 index 0000000..fb28b20 --- /dev/null +++ b/src/widget/layout/modifiers/stretch_modifier.h @@ -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 + 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 + 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 + auto operator|(W&& w, stretch_config cfg) -> stretch_modifier> { + return stretch_modifier>(std::forward(w), cfg.flex_factor); + } + } + + // 便捷工厂函数 + constexpr auto stretch(float factor = 1.0f) { return modifier::stretch_config{factor}; } +} // namespace mirage::modifier diff --git a/src/widget/layout/overlay.h b/src/widget/layout/overlay.h index 818edd8..a9aef68 100644 --- a/src/widget/layout/overlay.h +++ b/src/widget/layout/overlay.h @@ -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 #include #include namespace mirage { - // alignment 和 margin 已在 slot.h 中定义 - /// @brief Overlay容器模板 template class overlay : public event_target, public z_order_mixin> { @@ -123,34 +122,28 @@ namespace mirage { std::vector get_event_children() const override { std::vector event_children; - - std::apply([&](auto&... child) { + auto func = [&](auto&... child) { ( - [&]() { + [&] { using ChildType = std::remove_cvref_t; + // 情况 1: dynamic_list if constexpr (std::is_same_v) { event_children.push_back(const_cast(&child)); } - // 情况 2: property_slot 类型(新系统) - else if constexpr (is_property_slot_v) { - using SlotChildType = slot_child_type_t; - if constexpr (std::is_base_of_v) { - event_children.push_back(const_cast(&child.child())); - } - } - // 情况 3: slot 类型(新系统) - else if constexpr (is_slot_v) { - using SlotChildType = slot_child_type_t; - if constexpr (std::is_base_of_v) { - event_children.push_back(const_cast(&child.child())); + // 情况 2: 布局修饰器 - 穿透获取内部事件目标 + else if constexpr (modifier::is_layout_modifier) { + if (auto* target = modifier::get_inner_event_target(const_cast(child))) { + event_children.push_back(target); } } + // 情况 3: 继承自 event_target 的普通 widget else if constexpr (std::is_base_of_v) { event_children.push_back(const_cast(&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 || is_slot_v) { - // 新系统:slot/property_slot 直接处理 padding 和 alignment - // 直接测量 slot,它会处理内部的 padding + else if constexpr (modifier::is_layout_modifier) { + // 新系统:布局修饰器(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 || is_slot_v) { - // 新系统:slot/property_slot 直接处理 padding 和 alignment - // 给 slot 分配整个 overlay 空间,让它自己处理对齐 + else if constexpr (modifier::is_layout_modifier) { + // 新系统:布局修饰器(padding, align, stretch) + // 修饰器内部已封装布局逻辑(align计算对齐位置,padding计算偏移) + // 给修饰器分配整个 overlay 空间,让它自己处理布局 const auto child_state = parent_state.derive_child(vec2f_t::Zero(), overlay_size); child.arrange(child_state); } diff --git a/src/widget/layout/slot.h b/src/widget/layout/slot.h deleted file mode 100644 index a84698d..0000000 --- a/src/widget/layout/slot.h +++ /dev/null @@ -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 -#include -#include -#include - -namespace mirage { - // ============================================================================ - // 前向声明 - // ============================================================================ - - // 容器前向声明 - template - class v_stack; - template - class h_stack; - template - class overlay; - class canvas; - - // ============================================================================ - // Slot 类 - // ============================================================================ - - /// @brief Slot - 处理内部布局属性(padding、alignment) - template - 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(self); - } - - auto&& padding(this auto&& self, float all) { - self.padding_ = margin(all, all, all, all); - return std::forward(self); - } - - auto&& padding(this auto&& self, float horizontal, float vertical) { - self.padding_ = margin(horizontal, vertical, horizontal, vertical); - return std::forward(self); - } - - auto&& padding(this auto&& self, float left, float top, float right, float bottom) { - self.padding_ = margin(left, top, right, bottom); - return std::forward(self); - } - - // ============================================================================ - // 链式调用设置 alignment - // ============================================================================ - - auto&& align(this auto&& self, alignment a) { - self.alignment_ = a; - return std::forward(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 - 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()) { - if (auto z = get_property()) { - 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()) { - if (auto z = get_property()) { - builder.set_z_order(z->value); - } - } - invoke_build_render_commands(slot_, builder, context); - } - - // ============================================================================ - // 属性检查和获取 - // ============================================================================ - - template - static constexpr bool has_property() { - return detail::contains_type_v; - } - - template - std::optional

get_property() const { - return detail::get_param

(properties_); - } - - template - P get_property_or(const P& default_value) const { - auto opt = get_property

(); - 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() const { return properties_; } - - private: - SlotType slot_; - std::tuple properties_; - }; - - // ============================================================================ - // 管道操作符 - // ============================================================================ - - /// @brief slot | property -> property_slot - template requires std::is_base_of_v - auto operator|(slot&& s, Property&& prop) { - return property_slot, std::remove_cvref_t>( - std::move(s), std::forward(prop) - ); - } - - /// @brief slot | property -> property_slot (左值版本) - template requires std::is_base_of_v - auto operator|(slot& s, Property&& prop) { - return property_slot, std::remove_cvref_t>( - std::move(s), std::forward(prop) - ); - } - - /// @brief property_slot | property -> property_slot (链式添加属性) - template requires std::is_base_of_v - auto operator|(property_slot&& ps, NewProperty&& prop) { - // 创建新的 property_slot,包含所有现有属性和新属性 - auto func = [&](auto&&... existing_props) { - return property_slot>( - std::move(ps.get_slot()), - std::forward(existing_props)..., - std::forward(prop) - ); - }; - return std::apply(func, std::move(ps).properties()); - } - - // ============================================================================ - // 辅助函数 - // ============================================================================ - - /// @brief 从 slot 或普通 widget 中提取子控件的引用 - template - decltype(auto) unwrap_slot(T&& item) { - if constexpr (is_property_slot_v) { - return std::forward(item).child(); - } - else if constexpr (is_slot_v) { - return std::forward(item).child(); - } - else { - return std::forward(item); - } - } - - /// @brief 从 property_slot 中提取指定类型的属性 - template - std::optional extract_property(const T& item) { - if constexpr (is_property_slot_v) { - return item.template get_property(); - } - else { - return std::nullopt; - } - } - - /// @brief 从 property_slot 中提取指定类型的属性,如果不存在则返回默认值 - template - PropertyType extract_property_or(const T& item, const PropertyType& default_value) { - if constexpr (is_property_slot_v) { - return item.template get_property_or(default_value); - } - else { - return default_value; - } - } - - /// @brief 检查 property_slot 是否包含指定属性 - template - constexpr bool has_property() { - if constexpr (is_property_slot_v) { - return T::template has_property(); - } - else { - return false; - } - } - - // ============================================================================ - // 便捷构造函数 - // ============================================================================ - - /// @brief 创建 slot 的便捷函数 - template - auto make_slot(W&& child) { - return slot>(std::forward(child)); - } -} // namespace mirage diff --git a/src/widget/layout/slot_traits.h b/src/widget/layout/slot_traits.h deleted file mode 100644 index 2972eb9..0000000 --- a/src/widget/layout/slot_traits.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace mirage { - // ============================================================================ - // 前向声明 - // ============================================================================ - - template - class slot; - - template - class property_slot; - - // ============================================================================ - // 类型萃取工具(detail 命名空间) - // ============================================================================ - - namespace detail { - /// @brief 检查类型T是否在参数包Params中 - template - struct contains_type : std::disjunction...> { - }; - - template - inline constexpr bool contains_type_v = contains_type::value; - - /// @brief 从tuple中获取指定类型的值 - template - constexpr std::optional get_param_impl(const Tuple& tuple, std::index_sequence) { - std::optional result; - ( - [&]() { - using ElemType = std::tuple_element_t; - if constexpr (std::is_same_v) { - result = std::get(tuple); - } - }() - , ...); - return result; - } - - template - constexpr std::optional get_param(const std::tuple& tuple) { - return get_param_impl(tuple, std::index_sequence_for{}); - } - - /// @brief 检查tuple中是否包含指定类型 - template - struct tuple_contains; - - template - struct tuple_contains> : contains_type { - }; - - template - inline constexpr bool tuple_contains_v = tuple_contains::value; - } // namespace detail - - // ============================================================================ - // Slot 类型萃取 - // ============================================================================ - - /// @brief 检查类型是否为 slot - template - struct is_slot : std::false_type { - }; - - template - struct is_slot> : std::true_type { - }; - - template - inline constexpr bool is_slot_v = is_slot>::value; - - /// @brief 检查类型是否为 property_slot - template - struct is_property_slot : std::false_type { - }; - - template - struct is_property_slot> : std::true_type { - }; - - template - inline constexpr bool is_property_slot_v = is_property_slot>::value; - - /// @brief 检查类型是否为 slot 或 property_slot - template - inline constexpr bool is_any_slot_v = is_slot_v || is_property_slot_v; - - /// @brief 获取 slot 的子控件类型 - template - struct slot_child_type { - using type = T; // 非 slot 类型,返回自身 - }; - - template - struct slot_child_type> { - using type = Child; - }; - - template - struct slot_child_type> { - using type = slot_child_type::type; - }; - - template - using slot_child_type_t = slot_child_type>::type; - -} // namespace mirage \ No newline at end of file diff --git a/src/widget/layout/stack.h b/src/widget/layout/stack.h index e311f64..ef453f5 100644 --- a/src/widget/layout/stack.h +++ b/src/widget/layout/stack.h @@ -4,7 +4,7 @@ #include "dynamic_list.h" #include "culling_config.h" #include "event_target.h" -#include "slot.h" +#include "modifiers/modifiers.h" #include #include @@ -16,32 +16,12 @@ namespace mirage { // ============================================================================ namespace stack_detail { - /// @brief 从子控件中提取 padding/margin - /// slot 直接处理 padding,这里返回 zero - template - margin extract_child_margin(const Child& child) { - if constexpr (is_slot_v) { - // slot 类型,padding 已经在 slot 内部处理 - return margin::zero(); - } - else if constexpr (is_property_slot_v) { - // property_slot 类型,padding 在内部 slot 处理 - return margin::zero(); - } - else { - // 普通 widget - return margin::zero(); - } - } - /// @brief 从子控件中提取 stretch 信息 template std::optional extract_stretch_factor(const Child& child) { - if constexpr (is_property_slot_v) { - // 新系统:从 property_slot 提取 stretch_tag - if (auto sp = child.template get_property()) { - return sp->flex_factor; - } + // 新系统:检测 stretch_modifier + if constexpr (modifier::is_stretch_modifier) { + return modifier::get_flex_factor(child); } return std::nullopt; } @@ -49,17 +29,33 @@ namespace mirage { /// @brief 判断子控件是否应该 stretch template bool should_stretch(const Child& child) { + // 新系统:检测 stretch_modifier + if constexpr (modifier::is_stretch_modifier) { + return true; + } + // 旧系统 return extract_stretch_factor(child).has_value(); } /// @brief 获取 stretch 的 flex 因子 template float get_flex_factor(const Child& child) { + // 新系统:检测 stretch_modifier + if constexpr (modifier::is_stretch_modifier) { + return modifier::get_flex_factor(child); + } + // 旧系统 if (auto factor = extract_stretch_factor(child)) { return *factor; } return 0.0f; } + + /// @brief 检测是否为布局修饰器 + template + constexpr bool is_layout_modifier() { + return modifier::is_layout_modifier; + } } // ============================================================================ @@ -150,21 +146,13 @@ namespace mirage { if constexpr (std::is_same_v) { event_children.push_back(const_cast(&child)); } - // 情况 2: property_slot 类型(新系统) - else if constexpr (is_property_slot_v) { - using SlotChildType = slot_child_type_t; - if constexpr (std::is_base_of_v) { - event_children.push_back(const_cast(&child.child())); + // 情况 2: 布局修饰器 - 穿透获取内部事件目标 + else if constexpr (modifier::is_layout_modifier) { + if (auto* target = modifier::get_inner_event_target(const_cast(child))) { + event_children.push_back(target); } } - // 情况 3: slot 类型(新系统) - else if constexpr (is_slot_v) { - using SlotChildType = slot_child_type_t; - if constexpr (std::is_base_of_v) { - event_children.push_back(const_cast(&child.child())); - } - } - // 情况 4: 继承自 event_target 的普通 widget + // 情况 3: 继承自 event_target 的普通 widget else if constexpr (std::is_base_of_v) { event_children.push_back(const_cast(&child)); } @@ -187,16 +175,14 @@ namespace mirage { fixed_height += child_size.y(); } } - else if constexpr (is_property_slot_v || is_slot_v) { - 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) { + // 新系统:stretch_modifier + total_flex += modifier::get_flex_factor(child); + } + else if constexpr (modifier::is_layout_modifier) { + // 其他布局修饰器(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 || is_slot_v) { - 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) { + // 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 || is_slot_v) { + else if constexpr (modifier::is_stretch_modifier) { + // 新系统: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 get_event_children() const override { std::vector event_children; - std::apply([&](auto&... child) { + auto func = [&](auto&... child) { ( [&]() { using ChildType = std::remove_cvref_t; + // 情况 1: dynamic_list if constexpr (std::is_same_v) { event_children.push_back(const_cast(&child)); } - // 情况 2: property_slot 类型(新系统) - else if constexpr (is_property_slot_v) { - using SlotChildType = slot_child_type_t; - if constexpr (std::is_base_of_v) { - event_children.push_back(const_cast(&child.child())); - } - } - // 情况 3: slot 类型(新系统) - else if constexpr (is_slot_v) { - using SlotChildType = slot_child_type_t; - if constexpr (std::is_base_of_v) { - event_children.push_back(const_cast(&child.child())); + // 情况 2: 布局修饰器 - 穿透获取内部事件目标 + else if constexpr (modifier::is_layout_modifier) { + if (auto* target = modifier::get_inner_event_target(const_cast(child))) { + event_children.push_back(target); } } + // 情况 3: 继承自 event_target 的普通 widget else if constexpr (std::is_base_of_v) { event_children.push_back(const_cast(&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 || is_slot_v) { - 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) { + // 新系统:stretch_modifier + total_flex += modifier::get_flex_factor(child); + } + else if constexpr (modifier::is_layout_modifier) { + // 其他布局修饰器(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 || is_slot_v) { - 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) { + // 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 || is_slot_v) { + else if constexpr (modifier::is_stretch_modifier) { + // 新系统: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);