EUI-NEO

布局系统

EUI-NEO 技术文档。

布局系统

布局系统位于 core/layout.h,是 header-only 的纯计算模块,不依赖 OpenGL。DSL 中的 Row / Column / Stack 最终都会转换成 core::Node 进行 measure 和 layout。

容器类型

enum class LayoutType {
    Row,
    Column,
    Stack
};

DSL 写法:

ui.row("toolbar")
ui.column("content")
ui.stack("root")

对齐

enum class Align {
    START,
    CENTER,
    END
};

每个布局节点有:

规则:

DSL 写法:

.justifyContent(core::Align::CENTER)
.alignItems(core::Align::CENTER)
.align(core::Align::CENTER, core::Align::CENTER)

尺寸模式

enum class SizeMode {
    Fixed,
    WrapContent,
    Fill
};

SizeValue

SizeValue::fixed(240.0f)
SizeValue::wrapContent()
SizeValue::fill()

DSL 写法:

.width(240.0f)
.height(70.0f)
.size(240.0f, 70.0f)
.width(core::SizeValue::fill())
.height(core::SizeValue::wrapContent())
.fill()
.wrapContent()

当前 Fill 比较基础:会使用父级传入的 available size,没有实现 flex-grow / flex-shrink 的分配算法。

位置

布局位置是逻辑坐标,原点在左上角,y 向下。

.x(40.0f)
.y(24.0f)
.position(40.0f, 24.0f)

Stack 中,指定 x/y 会覆盖对应方向的自动对齐;未指定时按 align 规则放置。

Row / Column 中,子项位置主要由布局流决定,x/y 当前仍会进入 layout node,但推荐把显式定位主要用于 Stack

Margin 与 Gap

Margin:

.margin(8.0f)
.margin(12.0f, 8.0f)
.margin(12.0f, 8.0f, 12.0f, 8.0f)

Gap:

.gap(12.0f)
.spacing(12.0f)

gap/spacing 只对 RowColumn 的子元素间距有意义。

Measure / Layout 流程

Runtime 每次 compose 后会调用:

ui.layout(screen);

内部流程:

1. 把 DSL Element 树转换为 core::Node 树。 2. 调用 measure(availableWidth, availableHeight) 计算尺寸。 3. 调用 layout(x, y) 计算最终 frame。 4. 把每个 Node::frame() 写回对应 Element::frame

最终 frame 存在逻辑坐标中,渲染阶段再乘 dpiScale 转成 framebuffer 像素。

Row 示例

ui.row("toolbar")
    .size(420.0f, 64.0f)
    .gap(12.0f)
    .alignItems(core::Align::CENTER)
    .content([&] {
        components::button(ui, "save")
            .size(120.0f, 44.0f)
            .text("Save")
            .build();

        components::button(ui, "cancel")
            .size(120.0f, 44.0f)
            .text("Cancel")
            .build();
    });

Row 会:

1. 计算所有子元素 outer width 总和。 2. 加上 gap。 3. 根据 justifyContent 决定 x 起点。 4. 根据 alignItems 决定每个子元素 y。

Column 示例

ui.column("panel.content")
    .size(360.0f, 260.0f)
    .gap(10.0f)
    .justifyContent(core::Align::CENTER)
    .alignItems(core::Align::CENTER)
    .content([&] {
        ui.text("title")
            .size(300.0f, 42.0f)
            .text("Gallery")
            .fontSize(32.0f)
            .build();

        components::button(ui, "primary")
            .size(220.0f, 58.0f)
            .text("Open")
            .build();
    });

Column 会:

1. 计算所有子元素 outer height 总和。 2. 加上 gap。 3. 根据 justifyContent 决定 y 起点。 4. 根据 alignItems 决定每个子元素 x。

Stack 示例

ui.stack("stage")
    .size(600.0f, 320.0f)
    .content([&] {
        ui.rect("stage.bg")
            .size(600.0f, 320.0f)
            .color({0.10f, 0.12f, 0.16f, 1.0f})
            .build();

        ui.rect("actor")
            .x(active ? 420.0f : 40.0f)
            .y(80.0f)
            .size(120.0f, 80.0f)
            .transition(0.3f)
            .build();
    });

Stack 会按声明顺序绘制,后声明的元素盖在前面的元素上。

与脏区渲染的关系

布局系统只计算逻辑 frame。Runtime 会用 frame 和视觉属性计算 dirty rect:

因此写 DSL 页面时不需要手动标记脏区。

当前限制