diff --git a/example/src/main.cpp b/example/src/main.cpp index 434ca8d..8b114eb 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -79,18 +79,22 @@ int main(int argc, char* argv[]) { const auto button2 = std::make_shared(); button2->push_slot( mslot(mbutton) + .margin({10}) .visibility(visibility_t::visible) (text_block2) ); const auto& window = mwindow::create({ 800, 600 }, L"Hello, World!"); window->set_content( - mnew(mborder) + mnew(mv_box) [ - mslot(mborder) - .h_alignment(horizontal_alignment_t::center) - .v_alignment(vertical_alignment_t::center) - (button) + mslot(mv_box) + .horizontal_alignment(horizontal_alignment_t::left) + (button), + + mslot(mv_box) + .horizontal_alignment(horizontal_alignment_t::right) + (button2) ] ); diff --git a/src/mirage_widget/src/widget/panel_widget/mbox.cpp b/src/mirage_widget/src/widget/panel_widget/mbox.cpp index abdc583..82ee8f1 100644 --- a/src/mirage_widget/src/widget/panel_widget/mbox.cpp +++ b/src/mirage_widget/src/widget/panel_widget/mbox.cpp @@ -4,9 +4,9 @@ void mh_box::arrange_children(const geometry_t& in_allotted_geometry) { arrange_box_children(in_allotted_geometry, - get_dpi_scale(), - visibility_t::any_visible, - get_slots()); + get_dpi_scale(), + visibility_t::any_visible, + get_slots()); } Eigen::Vector2f mh_box::compute_desired_size(float in_layout_scale_multiplier) const { @@ -16,9 +16,9 @@ Eigen::Vector2f mh_box::compute_desired_size(float in_layout_scale_multiplier) c void mv_box::arrange_children(const geometry_t& in_allotted_geometry) { arrange_box_children(in_allotted_geometry, - get_dpi_scale(), - visibility_t::any_visible, - get_slots()); + get_dpi_scale(), + visibility_t::any_visible, + get_slots()); } Eigen::Vector2f mv_box::compute_desired_size(float in_layout_scale_multiplier) const { diff --git a/src/mirage_widget/src/widget/panel_widget/mbox.h b/src/mirage_widget/src/widget/panel_widget/mbox.h index a463c67..4648816 100644 --- a/src/mirage_widget/src/widget/panel_widget/mbox.h +++ b/src/mirage_widget/src/widget/panel_widget/mbox.h @@ -23,6 +23,7 @@ struct hbox_slot : mpanel_widget_slot { SLOT_SIZE() /** 定义边距属性,控制子组件周围的空间 */ SLOT_ATTRIBUTE(margin_t, margin) + SLOT_ATTRIBUTE(vertical_alignment_t, vertical_alignment) }; /** @@ -68,6 +69,7 @@ struct vbox_slot : mpanel_widget_slot { SLOT_SIZE() /** 定义边距属性,控制子组件周围的空间 */ SLOT_ATTRIBUTE(margin_t, margin) + SLOT_ATTRIBUTE(horizontal_alignment_t, horizontal_alignment) }; /** diff --git a/src/mirage_widget/src/widget_utils/layout_utils.h b/src/mirage_widget/src/widget_utils/layout_utils.h index 2988816..86453d6 100644 --- a/src/mirage_widget/src/widget_utils/layout_utils.h +++ b/src/mirage_widget/src/widget_utils/layout_utils.h @@ -20,6 +20,15 @@ void arrange_single_child( const margin_t& margin = margin_t() // 默认为空边距 ); +template +concept has_vertical_alignment = requires { + T().vertical_alignment(); +}; +template +concept has_horizontal_alignment = requires { + T().horizontal_alignment(); +}; + template void arrange_box_children( const geometry_t& in_allotted_geometry, @@ -41,11 +50,11 @@ void arrange_box_children( 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 + float size{}; + float margin_start{}; // 主轴起始边缘的margin + float margin_end{}; // 主轴结束边缘的margin + float margin_cross_start{}; // 交叉轴起始边缘的margin + float margin_cross_end{}; // 交叉轴结束边缘的margin }; std::vector visible_widgets; @@ -126,7 +135,7 @@ void arrange_box_children( } } - // 第三轮:排列部件 + // 第三轮:排列部件 float position = 0.0f; if (is_reversed) @@ -135,32 +144,101 @@ void arrange_box_children( for (const auto& info : visible_widgets) { position += info.margin_start; + // 获取部件期望的大小(需要同时考虑主轴和交叉轴) + // 确保获取最新的期望尺寸,因为布局可能会嵌套调用 + info.widget->cache_desired_size(in_layout_scale_multiplier); + const auto& desired_size = info.widget->get_desired_size().value(); + Eigen::Vector2f pos = Eigen::Vector2f::Zero(); - Eigen::Vector2f size = container_size; + Eigen::Vector2f size = Eigen::Vector2f::Zero(); // 初始化为0 if (is_horizontal) { + // --- 主轴 (X) --- + size.x() = info.size; // 主轴尺寸由之前的计算确定 (Auto 或 Stretch) 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 { + // --- 交叉轴 (Y) --- + pos.y() = info.margin_cross_start; + const float available_height = container_size.y() - (info.margin_cross_start + info.margin_cross_end); + const float widget_height = desired_size.y(); // 控件期望的高度 + float final_height = available_height; // 默认拉伸交叉轴 + + if constexpr (has_vertical_alignment) { + const auto& v_alignment = info.slot->vertical_alignment(); + // **如果指定了具体的对齐方式(非拉伸),则使用控件期望的高度** + // (这里假设 Top/Center/Bottom 意味着不拉伸,需要根据你的枚举定义调整) + bool use_desired_height = (v_alignment == vertical_alignment_t::top || + v_alignment == vertical_alignment_t::center || + v_alignment == vertical_alignment_t::bottom); + + if (use_desired_height) { + final_height = std::min(widget_height, available_height); // 使用期望高度,但不超过可用空间 + if (available_height > widget_height) { // 只有在有剩余空间时才需要调整对齐位置 + if (v_alignment == vertical_alignment_t::center) { + pos.y() += (available_height - widget_height) / 2.0f; + } else if (v_alignment == vertical_alignment_t::bottom) { + pos.y() += (available_height - widget_height); + } + // Top 对齐: pos.y() 初始值就是正确的 + } + } + // else: 拉伸对齐,保持 final_height = available_height + } + // else (SlotType 没有垂直对齐属性): 默认行为是拉伸,final_height = available_height + + size.y() = final_height; // **传递给子控件的最终高度** + + } else { // 垂直布局 + // --- 主轴 (Y) --- + size.y() = info.size; // 主轴尺寸由之前的计算确定 (Auto 或 Stretch) 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; + // --- 交叉轴 (X) --- + pos.x() = info.margin_cross_start; + const float available_width = container_size.x() - (info.margin_cross_start + info.margin_cross_end); + const float widget_width = desired_size.x(); // 控件期望的宽度 + float final_width = available_width; // 默认拉伸交叉轴 + + if constexpr (has_horizontal_alignment) { + const auto& h_alignment = info.slot->horizontal_alignment(); + // **如果指定了具体的对齐方式(非拉伸),则使用控件期望的宽度** + // (这里假设 Left/Center/Right 意味着不拉伸) + bool use_desired_width = (h_alignment == horizontal_alignment_t::left || + h_alignment == horizontal_alignment_t::center || + h_alignment == horizontal_alignment_t::right); + + if (use_desired_width) { + final_width = std::min(widget_width, available_width); // 使用期望宽度,但不超过可用空间 + if (available_width > widget_width) { // 只有在有剩余空间时才需要调整对齐位置 + if (h_alignment == horizontal_alignment_t::center) { + pos.x() += (available_width - widget_width) / 2.0f; + } else if (h_alignment == horizontal_alignment_t::right) { + pos.x() += (available_width - widget_width); + } + // **Left 对齐**: pos.x() 初始值就是正确的 + } + } + // else: 拉伸对齐,保持 final_width = available_width + } + // else (SlotType 没有水平对齐属性): 默认行为是拉伸,final_width = available_width + + size.x() = final_width; // **传递给子控件的最终宽度** } + // 确保尺寸不为负 + size.x() = std::max(0.0f, size.x()); + size.y() = std::max(0.0f, size.y()); + + // 使用计算好的位置和尺寸创建子控件的几何信息 const auto& child_geometry = in_allotted_geometry.make_child(pos, size); info.widget->set_geometry(child_geometry); - info.widget->arrange_children(child_geometry); + info.widget->arrange_children(child_geometry); // 子控件在其分配到的几何区域内进行布局 position += info.size + info.margin_end; }