Files
mirage/src/widget/layout/stack.h
nanako 74efe4e201 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.
2025-11-30 15:53:24 +08:00

463 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
};
}