垂直水平容器布局

This commit is contained in:
Nanako 2025-04-10 21:02:26 +08:00
parent 934dd653ee
commit b5fc6a9a26
4 changed files with 110 additions and 26 deletions

View File

@ -79,18 +79,22 @@ int main(int argc, char* argv[]) {
const auto button2 = std::make_shared<mbutton>();
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)
]
);

View File

@ -4,9 +4,9 @@
void mh_box::arrange_children(const geometry_t& in_allotted_geometry) {
arrange_box_children<orientation_t::horizontal>(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<orientation_t::vertical>(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 {

View File

@ -23,6 +23,7 @@ struct hbox_slot : mpanel_widget_slot<hbox_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<vbox_slot> {
SLOT_SIZE()
/** 定义边距属性,控制子组件周围的空间 */
SLOT_ATTRIBUTE(margin_t, margin)
SLOT_ATTRIBUTE(horizontal_alignment_t, horizontal_alignment)
};
/**

View File

@ -20,6 +20,15 @@ void arrange_single_child(
const margin_t& margin = margin_t() // 默认为空边距
);
template<typename T>
concept has_vertical_alignment = requires {
T().vertical_alignment();
};
template<typename T>
concept has_horizontal_alignment = requires {
T().horizontal_alignment();
};
template<orientation_t LayoutOrientation, typename SlotType>
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<mwidget> 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<slot_widget_info> 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<SlotType>) {
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<SlotType>) {
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;
}