#pragma once #include "mwidget.h" #include "geometry/geometry.h" #include "misc/mirage_type.h" /** * @brief 为单个子部件布局的通用函数 * * @param in_allotted_geometry 分配给容器的几何区域 * @param child_widget 子部件 * @param h_alignment 水平对齐方式 * @param v_alignment 垂直对齐方式 * @param margin 边距 */ void arrange_single_child( const geometry_t& in_allotted_geometry, const std::shared_ptr& child_widget, horizontal_alignment_t h_alignment, vertical_alignment_t v_alignment, const margin_t& margin = margin_t() // 默认为空边距 ); template void arrange_box_children( const geometry_t& in_allotted_geometry, float in_layout_scale_multiplier, visibility_t in_visibility_filter, const std::vector& child_slots, flow_direction_t flow_dir = flow_direction_t::left_to_right ){ const auto& container_size = in_allotted_geometry.get_local_size(); if (container_size.x() <= 0 || container_size.y() <= 0) { return; } // 确定主轴方向和是否反向 constexpr bool is_horizontal = LayoutOrientation == orientation_t::horizontal; const bool is_reversed = flow_dir == flow_direction_t::right_to_left; // 第一轮:收集可见部件信息并计算自动尺寸 struct slot_widget_info { std::shared_ptr widget; const SlotType* slot; float size; float margin_start; // 主轴起始边缘的margin float margin_end; // 主轴结束边缘的margin float margin_cross_start; // 交叉轴起始边缘的margin float margin_cross_end; // 交叉轴结束边缘的margin }; std::vector visible_widgets; float total_auto_size = 0.0f; float total_stretch_factor = 0.0f; float total_margins = 0.0f; // 主轴方向上所有边距的总和 for (const auto& child_slot : child_slots) { std::weak_ptr weak_widget = child_slot->get(); const auto widget = weak_widget.lock(); // 无效部件或不可见部件跳过 if (!widget) continue; if (!has_any_flag(widget->get_visibility(), in_visibility_filter)) continue; const auto& margin = child_slot->margin(); const float margin_left = margin.left; const float margin_right = margin.right; const float margin_top = margin.top; const float margin_bottom = margin.bottom; float margin_start, margin_end, margin_cross_start, margin_cross_end; if (is_horizontal) { margin_start = margin_left; margin_end = margin_right; margin_cross_start = margin_top; margin_cross_end = margin_bottom; } else { margin_start = margin_top; margin_end = margin_bottom; margin_cross_start = margin_left; margin_cross_end = margin_right; } total_margins += margin_start + margin_end; slot_widget_info info{ widget, child_slot, 0.0f, margin_start, margin_end, margin_cross_start, margin_cross_end }; const auto& size_attr = child_slot->size(); if (size_attr.is_auto_size()) { // 缓存部件的期望大小 widget->cache_desired_size(in_layout_scale_multiplier); const auto& desired_size = widget->get_desired_size().value(); info.size = is_horizontal ? desired_size.x() : desired_size.y(); total_auto_size += info.size; } else { total_stretch_factor += size_attr.stretch(); } visible_widgets.push_back(info); } if (visible_widgets.empty()) return; // 计算拉伸部件的可用空间(需要减去所有margin) const float container_primary_size = is_horizontal ? container_size.x() : container_size.y(); const float remaining_space = std::max(0.0f, container_primary_size - total_auto_size - total_margins); // 第二轮:计算拉伸部件的最终尺寸 for (auto& info : visible_widgets) { const auto& size_attr = info.slot->size(); if (!size_attr.is_auto_size()) { const float stretch_factor = size_attr.stretch(); if (total_stretch_factor > 0.0f) { info.size = remaining_space * (stretch_factor / total_stretch_factor); } else { info.size = 0.0f; } } } // 第三轮:排列部件 float position = 0.0f; if (is_reversed) std::reverse(visible_widgets.begin(), visible_widgets.end()); for (const auto& info : visible_widgets) { position += info.margin_start; Eigen::Vector2f pos = Eigen::Vector2f::Zero(); Eigen::Vector2f size = container_size; if (is_horizontal) { if (is_reversed) pos.x() = container_primary_size - position - info.size; else pos.x() = position; pos.y() += info.margin_cross_start; size.x() = info.size; size.y() -= info.margin_start + info.margin_end; } else { if (is_reversed) pos.y() = container_primary_size - position - info.size; else pos.y() = position; pos.x() += info.margin_cross_start; size.x() -= info.margin_start + info.margin_end; size.y() = info.size; } const auto& child_geometry = in_allotted_geometry.make_child(pos, size); info.widget->set_geometry(child_geometry); info.widget->arrange_children(child_geometry); position += info.size + info.margin_end; } } // 计算水平或垂直框的期望大小 template Eigen::Vector2f compute_box_desired_size(const std::vector& child_slots) { Eigen::Vector2f desired_size = Eigen::Vector2f::Zero(); for (const auto& slot : child_slots) { auto widget = slot->get().lock(); if (widget && has_any_flag(widget->get_visibility(), visibility_t::any_visible)) { widget->cache_desired_size(widget->get_dpi_scale()); Eigen::Vector2f widget_desired_size = widget->get_desired_size().value(); if (LayoutOrientation == orientation_t::horizontal) { desired_size.x() += widget_desired_size.x(); desired_size.y() = std::max(desired_size.y(), widget_desired_size.y()); } else { desired_size.x() = std::max(desired_size.x(), widget_desired_size.x()); desired_size.y() += widget_desired_size.y(); } } } return desired_size; }