# 功能变更 ID: Feature-20260509-0011 > 适用场景:记录小到中等规模的功能、接口、行为、默认值或配置变化。 > 不适用场景:新增核心模块、重大模块重构、架构级设计,请使用“新增功能模块”模板。 ## 基本信息 - ID: Feature-20260509-0011 - 标题: 布局 API 使用说明与旧接口覆盖规则 - 状态:已验证 - 类型:新增 - 级别:L2 中等 - 模块: Layout API / Control - 版本 / 分支: 当前工作区 - 环境: Windows + EasyX - 负责人: Codex 协作修改 ## 变更背景 - 背景: - 当前布局系统内部已经以 `LayoutSpec` 为统一解算依据。 - 旧接口 `setLayoutMode(...)` / `setAnchor(...)` 仍然保留,但只能表达有限的双锚点语义。 - 新增公开布局 API 后,需要明确用户应如何设置轴向锚点、尺寸策略和固定尺寸位移策略。 - 目标: - 说明新布局 API 的职责和使用方式。 - 明确新旧接口混用时的覆盖规则。 - 给出常见布局组合和 `KEY2` 迁移场景中的推荐写法。 - 不做什么:[可选] - 不开放 `LayoutCapability` 的普通外部写入接口。 - 不修改 `Table` 当前版本 `Y Fixed` 的能力边界。 - 不处理 Tooltip 智能选位、`Table` 内部局部重绘、`Dialog` synthetic move 统一等后置项。 ## 变更内容 - 变更摘要: - 增加布局 API 使用说明文档。 - 明确新接口与旧接口的覆盖关系。 - 汇总常见布局模式的代码写法。 - 新增项:[可选] - 新公开接口说明 - 旧接口映射说明 - 混用覆盖规则 - 常见写法示例 - 调试态日志说明 - 修改项:[可选] - 无运行时代码修改,本文件只记录使用说明。 - 删除 / 废弃项:[可选] - 无 - 受影响的文件 / 类 / 函数: - [`Control.h`](D:/programming/imGUI-easyX/imGui-easyX/Control.h) - [`Control.cpp`](D:/programming/imGUI-easyX/imGui-easyX/Control.cpp) - [`z-testDome.cpp`](D:/programming/imGUI-easyX/imGui-easyX/z-testDome.cpp) - 对外 API / 属性变化:[可选] - `setHorizontalLayoutSpec(...)` - `setVerticalLayoutSpec(...)` - `setHorizontalAnchors(...)` - `setVerticalAnchors(...)` - `setHorizontalSizePolicy(...)` - `setVerticalSizePolicy(...)` - `setHorizontalAlignPolicy(...)` - `setVerticalAlignPolicy(...)` - `getHorizontalLayoutSpec()` - `getVerticalLayoutSpec()` ## 行为对照 - 变更前: - 用户主要通过 `setLayoutMode(...)` 与 `setAnchor(...)` 设置布局。 - 固定尺寸 + 居中、固定尺寸 + 比例位移等组合无法通过旧接口清晰表达。 - 旧双锚点在某些测试场景中会被迫承担 Stretch 语义,导致固定按钮和内容驱动标签在 resize 后错位。 - 变更后: - 用户可按水平轴 / 垂直轴分别设置锚点集合、尺寸策略和固定尺寸位移策略。 - 旧接口仍可用,但只作为兼容输入层。 - 新旧接口混用时,最后调用的接口覆盖对应轴的布局规格。 - 兼容性说明:兼容 - 迁移说明:[可选] - 新代码建议优先使用新布局 API。 - 旧代码可以继续使用 `setAnchor(...)`,但复杂布局应逐步迁移到新接口。 ## 验证与落地 - 验证方式: - 编译 `Control.cpp` - 编译 `z-testDome.cpp /DKEY=2` - 编译 `z-testDome.cpp /DKEY=5` - 手测 `KEY1 / KEY2 / KEY5` - 验证结果: - 编译级验证通过。 - 用户手测当前暂未发现问题。 - 关联 BUG / Fix:[可选] - [Feature-20260416-0009-KEY2-使用新布局API重构与联动刷新.md](D:/programming/imGUI-easyX/imGui-easyX/开发记录/功能变更/Feature-20260416-0009-KEY2-使用新布局API重构与联动刷新.md) - [Feature-20260418-0010-当前阶段测试用例矩阵与定位.md](D:/programming/imGUI-easyX/imGui-easyX/开发记录/功能变更/Feature-20260418-0010-当前阶段测试用例矩阵与定位.md) - Commit: - PR:[可选] - 发布版本:[可选] - 备注:[可选] - resize 收口阶段若开启高频 console 日志,可能出现一帧视觉延迟;当前按调试态 I/O 现象记录,不作为 bug 处理。 ## API 总览 ### 完整轴向规格接口 ```cpp void setHorizontalLayoutSpec(const StellarX::AxisLayoutSpec& spec); void setVerticalLayoutSpec(const StellarX::AxisLayoutSpec& spec); ``` - 直接覆盖对应轴的完整布局规格。 - 调用后自动切换到 `StellarX::LayoutMode::AnchorToEdges`。 - 适合调用方已经准备好完整 `AxisLayoutSpec` 的场景。 ### 锚点集合接口 ```cpp void setHorizontalAnchors(bool left, bool right); void setVerticalAnchors(bool top, bool bottom); ``` - 水平轴对应 `left / right`。 - 垂直轴对应 `top / bottom`。 - 只修改对应轴的锚点集合,不主动改变尺寸策略或位移策略。 ### 尺寸策略接口 ```cpp void setHorizontalSizePolicy(StellarX::AxisSizePolicy policy); void setVerticalSizePolicy(StellarX::AxisSizePolicy policy); ``` - `Stretch`:允许该轴尺寸随父容器变化。 - `FixedSize`:该轴尺寸保持设计态尺寸。 - 控件能力边界仍然生效。例如 `Table` 当前版本不允许 `Y Stretch`,即使用户设置垂直 `Stretch`,也会被能力边界降级。 ### 固定尺寸位移策略接口 ```cpp void setHorizontalAlignPolicy(StellarX::AxisAlignPolicy policy); void setVerticalAlignPolicy(StellarX::AxisAlignPolicy policy); ``` - 只在 `FixedSize` 语义下决定位置。 - 不负责改变尺寸。 - 常用值: - `Start`:保持起边关系,例如左固定、上固定。 - `End`:保持终边关系,例如右固定、下固定。 - `Center`:保持居中关系。 - `Proportional`:保持设计态相对位置比例,适合固定尺寸控件在可变宽容器中按比例移动。 ### 读取接口 ```cpp StellarX::AxisLayoutSpec getHorizontalLayoutSpec() const; StellarX::AxisLayoutSpec getVerticalLayoutSpec() const; ``` - 返回当前生效的新模型轴向规格。 - 不保证能完整逆映射回旧 `anchor_1 / anchor_2` 语义。 ## 新旧接口覆盖规则 ### 旧接口仍然保留 ```cpp void setLayoutMode(StellarX::LayoutMode layoutMode); void setAnchor(StellarX::Anchor anchor1, StellarX::Anchor anchor2); ``` - 旧接口是兼容入口。 - `setAnchor(...)` 内部会把旧双锚点映射到新的 `layoutSpec.horizontal` 与 `layoutSpec.vertical`。 - 旧接口无法表达所有新模型状态,例如固定尺寸 + 比例位移、固定尺寸 + 居中等。 ### 后调用者生效 规则: - 先调用 `setAnchor(...)`,再调用新 API,则新 API 覆盖对应轴规格。 - 先调用新 API,再调用 `setAnchor(...)`,则旧接口重新覆盖水平轴和垂直轴规格。 - 新 API 只覆盖自己负责的轴或字段。例如 `setHorizontalSizePolicy(...)` 只修改水平轴尺寸策略,不修改垂直轴。 示例: ```cpp button->setLayoutMode(StellarX::LayoutMode::AnchorToEdges); button->setAnchor(StellarX::Anchor::Left, StellarX::Anchor::Right); // 覆盖水平轴:改为固定尺寸 + 居中,不再沿用旧接口映射出的 Stretch。 button->setHorizontalSizePolicy(StellarX::AxisSizePolicy::FixedSize); button->setHorizontalAlignPolicy(StellarX::AxisAlignPolicy::Center); ``` ### 新 API 自动切换布局模式 所有新布局 setter 都会自动将 `layoutMode` 切换为 `AnchorToEdges`。 因此新代码通常不需要再额外写: ```cpp control->setLayoutMode(StellarX::LayoutMode::AnchorToEdges); ``` ## 常见布局写法 ### 左固定 ```cpp control->setHorizontalAnchors(true, false); control->setHorizontalSizePolicy(StellarX::AxisSizePolicy::FixedSize); control->setHorizontalAlignPolicy(StellarX::AxisAlignPolicy::Start); ``` ### 右固定 ```cpp control->setHorizontalAnchors(false, true); control->setHorizontalSizePolicy(StellarX::AxisSizePolicy::FixedSize); control->setHorizontalAlignPolicy(StellarX::AxisAlignPolicy::End); ``` ### 水平拉伸 ```cpp control->setHorizontalAnchors(true, true); control->setHorizontalSizePolicy(StellarX::AxisSizePolicy::Stretch); control->setHorizontalAlignPolicy(StellarX::AxisAlignPolicy::Start); ``` ### 固定尺寸居中 ```cpp control->setHorizontalAnchors(true, true); control->setHorizontalSizePolicy(StellarX::AxisSizePolicy::FixedSize); control->setHorizontalAlignPolicy(StellarX::AxisAlignPolicy::Center); ``` ### 固定尺寸比例位移 ```cpp control->setHorizontalAnchors(true, true); control->setHorizontalSizePolicy(StellarX::AxisSizePolicy::FixedSize); control->setHorizontalAlignPolicy(StellarX::AxisAlignPolicy::Proportional); ``` 适用场景: - 一组固定尺寸按钮需要随着父容器变宽按比例展开。 - `KEY2` 中每个 bit 单元就是该策略的典型使用方式。 ## KEY2 迁移要点 `KEY2` 原问题不是单纯参数错误,而是布局单元拆分不合理: - 位号 `Label` 是内容驱动尺寸。 - 位按钮是固定尺寸。 - 二者分别使用比例位移时,因为自身尺寸不同,resize 后会出现错位。 当前迁移方式: - 每一位创建一个小 `Canvas` 单元。 - 单元内部固定放置位号 `Label` 与位按钮。 - 单元自身使用 `FixedSize + Proportional`。 - 这样移动的是整个 bit 单元,而不是标签和按钮分别移动。 示意写法: ```cpp auto cell = std::make_unique(cellX, 0, 32, 50); cell->setHorizontalAnchors(true, true); cell->setHorizontalSizePolicy(StellarX::AxisSizePolicy::FixedSize); cell->setHorizontalAlignPolicy(StellarX::AxisAlignPolicy::Proportional); ``` ## 能力边界说明 布局 API 表达的是“调用方希望如何布局”,但控件能力边界仍然优先。 当前关键边界: - `Canvas`:`X / Y` 均允许 Stretch。 - `TabControl`:外层允许 Stretch,内部页签栏 / 页面区仍由自身管理。 - `Table`:当前版本 `X Stretch / Y Fixed`,这是当前实现边界,不是永久产品结论。 - `TextBox`:默认更适合 `X Stretch / Y FixedSize`。 - `Label`:默认固定或内容驱动刷新运行态尺寸,不建议做双轴 Stretch。 - `Dialog`:只居中,不参与父级拉伸。 如果用户设置的策略超出控件能力边界,统一解算层会按现有降级规则处理,并保留必要日志。 ## 调试态说明 resize / 最大化 / 还原过程中,如果开启大量 console 日志,可能出现一帧视觉延迟或短暂残影。 当前判断原因是日志系统同步写控制台且默认逐行 flush,导致主线程 resize 收口绘制被 I/O 拖慢。 处理建议: - 常规手测可关闭 console 日志。 - 需要日志时可优先输出到文件。 - 当前不作为布局或重绘 bug 处理,后续可在官网/API 文档中作为调试说明记录。