Files
XCEngine/docs/api/XCEngine/Editor/UI/PropertyLayout/PropertyLayout.md
2026-03-29 01:36:53 +08:00

4.6 KiB
Raw Blame History

PropertyLayout

命名空间: XCEngine::Editor::UI

类型: header-helper + struct

源文件: editor/src/UI/PropertyLayout.h

描述: 为 Inspector 属性行提供统一的标签列 / 控件列几何计算,并把具体控件绘制委托给回调。

概述

PropertyLayout.h 是当前 Inspector 体验能否“像一个成熟编辑器”而不是“像一堆临时摆放的 ImGui 控件”的关键基础设施。

它不直接定义浮点框、复选框或向量输入框,而是先回答一个更底层的问题:

  • 标签列从哪里开始?
  • 控件列从哪里开始?
  • 当前行有多高?
  • 控件应该拿到多宽的可用空间?

把这层几何计算独立出来以后,ScalarControlsVectorControlsPropertyGrid 这些上层 helper 就可以共享同一套 Inspector 排版规则,而不需要每种控件都重复手算列宽和光标位置。

公开 API

成员 说明
PropertyLayoutSpec 描述标签缩进、控件列起点、标签和控件间距、控件右侧留白。
PropertyLayoutMetrics 保存一行属性被计算出的实际几何结果。
MakePropertyLayout() 生成使用默认 token 的 PropertyLayoutSpec
PushPropertyLayoutStyles() 推入当前属性行需要的样式变量。
GetPropertyControlWidth(...) 根据布局结果返回控件区域宽度。
SetNextPropertyControlWidth(...) 用布局结果直接设置下一个控件的宽度。
AlignPropertyControlToRight(...) 让较窄控件在属性列中右对齐。
DrawPropertyRow(...) 绘制一整行属性,并把具体控件绘制委托给回调。

当前实现行为

editor/src/UI/PropertyLayout.h 的实现:

  • PropertyLayoutSpec 的默认值直接来自 StyleTokens.h,例如 InspectorPropertyControlColumnStart()InspectorPropertyLabelInset()
  • PushPropertyLayoutStyles() 当前只推入 ImGuiStyleVar_FramePadding,并不负责整套 Inspector 样式。
  • DrawPropertyRow(...) 会:
    • labelPushID 的依据。
    • 读取当前内容区宽度并计算一整行的 label / control 几何。
    • 直接用 ImDrawList::AddText(...) 绘制标签文本,而不是创建一个独立的 ImGui label item。
    • 把光标移动到控件列起点,再调用外部回调绘制真正的控件。
    • 最后用 Dummy(...) 吃掉这一行占用的高度,确保后续布局连续。

这说明它是一层“布局执行器”,不是单纯的常量集合。

设计说明

商业引擎编辑器通常会把 Inspector 的“字段布局规则”和“字段编辑控件”拆开,这一点和 Unity Inspector 的使用体验很像:

  • 左边是稳定的属性标签列。
  • 右边是不同类型字段共享的一列编辑区域。
  • 上层组件编辑器只描述“这里是一条 Position / Rotation / Scale”而不是自己计算每个字段的横向坐标。

这样拆分的收益非常直接:

  • 所有组件编辑器天然保持一致的列对齐。
  • 以后如果想统一调整 Inspector 视觉密度,只需要修改 token 或布局层。
  • PropertyGrid 可以专注在“属性语义 + 撤销接线”,而不必再重复几何计算。

使用模式

更推荐的使用方式不是直接手算坐标,而是把控件包进 DrawPropertyRow(...) 的回调:

const UI::PropertyLayoutSpec layout = UI::MakePropertyLayout();

UI::DrawPropertyRow("Mass", layout, [&](const UI::PropertyLayoutMetrics& metrics) {
    UI::SetNextPropertyControlWidth(metrics);
    return ImGui::DragFloat("##value", &mass, 0.1f, 0.0f, 1000.0f, "%.2f");
});

如果只是常见属性类型,实际工程里更常见的入口是:

  • ScalarControls.h
  • VectorControls.h
  • PropertyGrid.h

它们已经把 PropertyLayout 作为底层依赖封装好了。

线程语义

  • 这一层完全依赖当前帧的 ImGui 上下文和当前窗口布局状态,只能在 UI 线程、ImGui 绘制阶段调用。
  • 它不维护后台状态,也不适合离线计算布局。

当前限制

  • PushID(label) 意味着同一作用域里若有重复标签,调用方应额外 PushID 以避免冲突。
  • 标签是直接画到 draw list 上的,因此不是可交互的独立 ImGui item。
  • 当前布局仍然是固定列起点模型,不是响应式表单系统。
  • PushPropertyLayoutStyles() 目前只做最小样式压栈,离完整的属性主题系统还有距离。

相关文档