- 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.
463 lines
16 KiB
C++
463 lines
16 KiB
C++
#pragma once
|
||
#include "widget.h"
|
||
#include "render_command.h"
|
||
#include "dynamic_list.h"
|
||
#include "culling_config.h"
|
||
#include "event_target.h"
|
||
#include "modifiers/modifiers.h"
|
||
|
||
#include <tuple>
|
||
#include <vector>
|
||
#include <optional>
|
||
|
||
namespace mirage {
|
||
// ============================================================================
|
||
// 辅助函数:统一的属性提取
|
||
// ============================================================================
|
||
|
||
namespace stack_detail {
|
||
/// @brief 从子控件中提取 stretch 信息
|
||
template <typename Child>
|
||
std::optional<float> extract_stretch_factor(const Child& child) {
|
||
// 新系统:检测 stretch_modifier
|
||
if constexpr (modifier::is_stretch_modifier<Child>) {
|
||
return modifier::get_flex_factor(child);
|
||
}
|
||
return std::nullopt;
|
||
}
|
||
|
||
/// @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>;
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// v_stack 实现
|
||
// ============================================================================
|
||
|
||
template <widget... children>
|
||
class v_stack : public event_target, public z_order_mixin<v_stack<children...>> {
|
||
public:
|
||
constexpr explicit v_stack(children&&... in_children) : children_(std::forward<children>(in_children)...) {
|
||
}
|
||
|
||
auto measure(const vec2f_t& available_size) const -> vec2f_t {
|
||
float max_width = 0.0f;
|
||
float total_height = 0.0f;
|
||
|
||
std::apply([&](const auto&... child) {
|
||
(process_child_measure(child, available_size, max_width, total_height), ...);
|
||
}, children_);
|
||
|
||
return vec2f_t{max_width, total_height};
|
||
}
|
||
|
||
void arrange(const layout_state& state) {
|
||
// 保存布局状态用于事件处理
|
||
m_layout_state = state;
|
||
|
||
// 第一遍:计算固定尺寸和stretch总权重
|
||
float fixed_height = 0.0f;
|
||
float total_flex = 0.0f;
|
||
|
||
std::apply([&](const auto&... child) {
|
||
(calculate_flex_info(child, state.size(), fixed_height, total_flex), ...);
|
||
}, children_);
|
||
|
||
// 计算可用于stretch的剩余空间
|
||
const float available_stretch_height = std::max(0.0f, state.size().y() - fixed_height);
|
||
|
||
// 第二遍:实际布局
|
||
float current_y = 0.0f;
|
||
|
||
std::apply([&](auto&... child) {
|
||
(process_child_arrange_with_stretch(child, state, current_y, available_stretch_height, total_flex), ...
|
||
);
|
||
}, children_);
|
||
}
|
||
|
||
void build_render_commands(render_command_builder& builder) const {
|
||
// 应用 z_order 到构建器
|
||
this->apply_z_order_to_builder(builder);
|
||
|
||
std::apply([&builder](const auto&... child) {
|
||
(child.build_render_commands(builder), ...);
|
||
}, children_);
|
||
}
|
||
|
||
void build_render_commands(render_command_builder& builder, const culling_context& context) const {
|
||
// 应用 z_order 到构建器
|
||
this->apply_z_order_to_builder(builder);
|
||
|
||
std::apply([&builder, &context](const auto&... child) {
|
||
(invoke_build_render_commands(child, builder, context), ...);
|
||
}, children_);
|
||
}
|
||
|
||
const auto& get_children() const { return children_; }
|
||
|
||
// event_target 接口实现
|
||
bool contains_point(double global_x, double global_y) const override {
|
||
return m_layout_state.
|
||
contains_global_point(vec2f_t(static_cast<float>(global_x), static_cast<float>(global_y)));
|
||
}
|
||
|
||
std::pair<double, double> global_to_local(double global_x, double global_y) const override {
|
||
auto local = m_layout_state.to_local(vec2f_t(static_cast<float>(global_x), static_cast<float>(global_y)));
|
||
return {local.x(), local.y()};
|
||
}
|
||
|
||
std::vector<event_target*> get_event_children() const override {
|
||
std::vector<event_target*> event_children;
|
||
|
||
std::apply([&]<typename... T>(T&... child) {
|
||
(
|
||
[&]() {
|
||
using ChildType = std::remove_cvref_t<T>;
|
||
|
||
// 情况 1: dynamic_list
|
||
if constexpr (std::is_same_v<ChildType, dynamic_list>) {
|
||
event_children.push_back(const_cast<dynamic_list*>(&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_);
|
||
|
||
return event_children;
|
||
}
|
||
|
||
private:
|
||
/// @brief 计算flex布局信息(第一遍)
|
||
template <typename Child>
|
||
void calculate_flex_info(const Child& child, const vec2f_t& available_size,
|
||
float& fixed_height, float& total_flex) const {
|
||
if constexpr (std::is_same_v<std::decay_t<Child>, dynamic_list>) {
|
||
// dynamic_list 的子组件都按固定尺寸处理
|
||
for (const auto& sub_child : child.get_children()) {
|
||
const auto child_size = sub_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(总是固定尺寸)
|
||
const auto child_size = child.measure(available_size);
|
||
fixed_height += child_size.y();
|
||
}
|
||
}
|
||
|
||
// 辅助函数:处理单个子组件的测量
|
||
template <typename Child>
|
||
void process_child_measure(const Child& child, const vec2f_t& available_size,
|
||
float& max_width, float& total_height) const {
|
||
if constexpr (std::is_same_v<std::decay_t<Child>, dynamic_list>) {
|
||
// 展开 dynamic_list 的子组件
|
||
for (const auto& sub_child : child.get_children()) {
|
||
const auto child_size = sub_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 或其他修饰器
|
||
const auto child_size = child.measure(available_size);
|
||
max_width = std::max(max_width, child_size.x());
|
||
total_height += child_size.y();
|
||
}
|
||
}
|
||
|
||
// 辅助函数:处理单个子组件的布局(支持stretch)
|
||
template <typename Child>
|
||
void process_child_arrange_with_stretch(Child& child, const layout_state& state, float& current_y,
|
||
float available_stretch_height, float total_flex) const {
|
||
if constexpr (std::is_same_v<std::decay_t<Child>, dynamic_list>) {
|
||
// 展开 dynamic_list 的子组件
|
||
for (auto& sub_child : child.get_children()) {
|
||
const auto child_size = sub_child.measure(state.size());
|
||
const auto child_state = state.derive_child(vec2f_t{0.0f, current_y}, child_size);
|
||
sub_child.arrange(child_state);
|
||
current_y += child_size.y();
|
||
}
|
||
}
|
||
else if constexpr (modifier::is_stretch_modifier<Child>) {
|
||
// 新系统:stretch_modifier
|
||
vec2f_t child_size;
|
||
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;
|
||
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 或其他修饰器
|
||
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);
|
||
current_y += child_size.y();
|
||
}
|
||
}
|
||
|
||
std::tuple<children...> children_;
|
||
layout_state m_layout_state;
|
||
};
|
||
|
||
// ============================================================================
|
||
// h_stack 实现
|
||
// ============================================================================
|
||
|
||
template <widget... children>
|
||
class h_stack : public event_target, public z_order_mixin<h_stack<children...>> {
|
||
public:
|
||
explicit constexpr h_stack(children&&... in_children) : children_(std::forward<children>(in_children)...) {
|
||
}
|
||
|
||
[[nodiscard]] auto measure(const vec2f_t& available_size) const -> vec2f_t {
|
||
float total_width = 0.0f;
|
||
float max_height = 0.0f;
|
||
|
||
std::apply([&](const auto&... child) {
|
||
(process_child_measure(child, available_size, total_width, max_height), ...);
|
||
}, children_);
|
||
|
||
return vec2f_t{total_width, max_height};
|
||
}
|
||
|
||
void arrange(const layout_state& state) {
|
||
m_layout_state = state;
|
||
|
||
// 第一遍:计算固定尺寸和stretch总权重
|
||
float fixed_width = 0.0f;
|
||
float total_flex = 0.0f;
|
||
|
||
std::apply([&](const auto&... child) {
|
||
(calculate_flex_info(child, state.size(), fixed_width, total_flex), ...);
|
||
}, children_);
|
||
|
||
// 计算可用于stretch的剩余空间
|
||
const float available_stretch_width = std::max(0.0f, state.size().x() - fixed_width);
|
||
|
||
// 第二遍:实际布局
|
||
float current_x = 0.0f;
|
||
|
||
std::apply([&](auto&... child) {
|
||
(process_child_arrange_with_stretch(child, state, current_x, available_stretch_width, total_flex), ...);
|
||
}, children_);
|
||
}
|
||
|
||
void build_render_commands(render_command_builder& builder) const {
|
||
// 应用 z_order 到构建器
|
||
this->apply_z_order_to_builder(builder);
|
||
|
||
std::apply([&builder](const auto&... child) {
|
||
(child.build_render_commands(builder), ...);
|
||
}, children_);
|
||
}
|
||
|
||
void build_render_commands(render_command_builder& builder, const culling_context& context) const {
|
||
// 应用 z_order 到构建器
|
||
this->apply_z_order_to_builder(builder);
|
||
|
||
std::apply([&builder, &context](const auto&... child) {
|
||
(invoke_build_render_commands(child, builder, context), ...);
|
||
}, children_);
|
||
}
|
||
|
||
[[nodiscard]] const auto& get_children() const {
|
||
return children_;
|
||
}
|
||
|
||
// event_target 接口实现
|
||
bool contains_point(double global_x, double global_y) const override {
|
||
return m_layout_state.
|
||
contains_global_point(vec2f_t(static_cast<float>(global_x), static_cast<float>(global_y)));
|
||
}
|
||
|
||
std::pair<double, double> global_to_local(double global_x, double global_y) const override {
|
||
auto local = m_layout_state.to_local(vec2f_t(static_cast<float>(global_x), static_cast<float>(global_y)));
|
||
return {local.x(), local.y()};
|
||
}
|
||
|
||
std::vector<event_target*> get_event_children() const override {
|
||
std::vector<event_target*> event_children;
|
||
|
||
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: 布局修饰器 - 穿透获取内部事件目标
|
||
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));
|
||
}
|
||
}()
|
||
, ...);
|
||
};
|
||
std::apply(func, children_);
|
||
|
||
return event_children;
|
||
}
|
||
|
||
private:
|
||
/// @brief 计算flex布局信息(第一遍)
|
||
template <typename Child>
|
||
void calculate_flex_info(const Child& child, const vec2f_t& available_size,
|
||
float& fixed_width, float& total_flex) const {
|
||
if constexpr (std::is_same_v<std::decay_t<Child>, dynamic_list>) {
|
||
// dynamic_list 的子组件都按固定尺寸处理
|
||
for (const auto& sub_child : child.get_children()) {
|
||
const auto child_size = sub_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(总是固定尺寸)
|
||
const auto child_size = child.measure(available_size);
|
||
fixed_width += child_size.x();
|
||
}
|
||
}
|
||
|
||
// 辅助函数:处理单个子组件的测量
|
||
template <typename Child>
|
||
void process_child_measure(const Child& child, const vec2f_t& available_size,
|
||
float& total_width, float& max_height) const {
|
||
if constexpr (std::is_same_v<std::decay_t<Child>, dynamic_list>) {
|
||
// 展开 dynamic_list 的子组件
|
||
for (const auto& sub_child : child.get_children()) {
|
||
const auto child_size = sub_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 或其他修饰器
|
||
const auto child_size = child.measure(available_size);
|
||
total_width += child_size.x();
|
||
max_height = std::max(max_height, child_size.y());
|
||
}
|
||
}
|
||
|
||
// 辅助函数:处理单个子组件的布局(支持stretch)
|
||
template <typename Child>
|
||
void process_child_arrange_with_stretch(Child& child, const layout_state& state, float& current_x,
|
||
float available_stretch_width, float total_flex) const {
|
||
if constexpr (std::is_same_v<std::decay_t<Child>, dynamic_list>) {
|
||
// 展开 dynamic_list 的子组件
|
||
for (auto& sub_child : child.get_children()) {
|
||
const auto child_size = sub_child.measure(state.size());
|
||
const auto child_state = state.derive_child(vec2f_t{current_x, 0.0f}, child_size);
|
||
sub_child.arrange(child_state);
|
||
current_x += child_size.x();
|
||
}
|
||
}
|
||
else if constexpr (modifier::is_stretch_modifier<Child>) {
|
||
// 新系统:stretch_modifier
|
||
vec2f_t child_size;
|
||
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;
|
||
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 或其他修饰器
|
||
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);
|
||
current_x += child_size.x();
|
||
}
|
||
}
|
||
|
||
std::tuple<children...> children_;
|
||
layout_state m_layout_state;
|
||
};
|
||
}
|