Document PropertyEditor property grouping system in CLAUDE.md
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
130225e9b8
commit
744af1b95b
170
CLAUDE.md
170
CLAUDE.md
|
|
@ -680,8 +680,9 @@ QQuickDetailsViewModel::setObject(QObject*) ← QAbstractItemModel
|
|||
│ ├── 有 IPropertyTypeCustomization?
|
||||
│ │ YES → customizeChildren(handle, layoutBuilder)
|
||||
│ │ NO → PropertyTypeCustomization_ObjectDefault::customizeChildren()
|
||||
│ │ └─ 遍历 metaObject->propertyCount()
|
||||
│ │ └─ 每个 QMetaProperty 创建一个子 QPropertyHandle
|
||||
│ │ ├─ 检查 objectHandle 是否实现 IPropertyGroupProvider
|
||||
│ │ │ YES → 按 group 名称收集属性 → addGroup() + 子 LayoutBuilder
|
||||
│ │ │ NO → 遍历 metaObject->propertyCount() 平铺属性
|
||||
│ │ └─ layoutBuilder->addProperty(childHandle)
|
||||
│ │
|
||||
│ ▼
|
||||
|
|
@ -739,23 +740,27 @@ class QQuickDetailsViewModel : public QAbstractItemModel {
|
|||
};
|
||||
```
|
||||
|
||||
**模型的树结构**:
|
||||
**模型的树结构**(带分组示例):
|
||||
```
|
||||
mRoot (QDetailsViewRow_Property, 不可见)
|
||||
├── child[0]: QDetailsViewRow_Property("Int") ← QMetaProperty
|
||||
├── child[1]: QDetailsViewRow_Property("Float")
|
||||
├── child[2]: QDetailsViewRow_Property("LimitedDouble")
|
||||
├── child[3]: QDetailsViewRow_Property("String")
|
||||
├── ...
|
||||
├── child[N]: QDetailsViewRow_Property("CustomType") ← IPropertyTypeCustomization
|
||||
│ ├── child[0]: QDetailsViewRow_Property("ArraySize")
|
||||
│ └── child[1]: QDetailsViewRow_Property("Array")
|
||||
│ ├── child[0]: QDetailsViewRow_Property("[0]")
|
||||
│ ├── child[1]: QDetailsViewRow_Property("[1]")
|
||||
│ └── ...
|
||||
└── child[N+1]: QDetailsViewRow_Property("CustomGadget")
|
||||
└── child[0]: QDetailsViewRow_Property("LimitedDouble")
|
||||
└── child[1]: QDetailsViewRow_Property("Desc")
|
||||
├── QDetailsViewRow_Group("数值") ← 分组标题(可折叠)
|
||||
│ ├── QDetailsViewRow_Property("Int") ← QMetaProperty
|
||||
│ ├── QDetailsViewRow_Property("Float")
|
||||
│ └── QDetailsViewRow_Property("LimitedDouble")
|
||||
├── QDetailsViewRow_Group("文本") ← 分组标题(可折叠)
|
||||
│ ├── QDetailsViewRow_Property("String")
|
||||
│ └── QDetailsViewRow_Property("Directory")
|
||||
├── QDetailsViewRow_Group("自定义类型") ← 分组标题(可折叠)
|
||||
│ ├── QDetailsViewRow_Property("CustomEnum")
|
||||
│ ├── QDetailsViewRow_Property("CustomType") ← IPropertyTypeCustomization
|
||||
│ │ ├── QDetailsViewRow_Property("ArraySize")
|
||||
│ │ └── QDetailsViewRow_Property("Array")
|
||||
│ │ ├── QDetailsViewRow_Property("[0]")
|
||||
│ │ └── QDetailsViewRow_Property("[1]")
|
||||
│ └── QDetailsViewRow_Property("CustomGadget")
|
||||
│ ├── QDetailsViewRow_Property("LimitedDouble")
|
||||
│ └── QDetailsViewRow_Property("Desc")
|
||||
└── QDetailsViewRow_Property("m_lastBoudingRectF") ← 未分组属性(直接挂根)
|
||||
```
|
||||
|
||||
#### 3. QPropertyHandle(属性操作的统一入口)
|
||||
|
|
@ -813,10 +818,21 @@ IDetailsViewRow (抽象基类)
|
|||
│ 否则 → makePropertyRow(handle)(默认的名称+值编辑器布局)
|
||||
│ attachChildren(): 如果有 IPropertyTypeCustomization → customizeChildren()
|
||||
│ 否则 → PropertyTypeCustomization_ObjectDefault(遍历 metaObject->propertyCount())
|
||||
└── QDetailsViewRow_Custom ← 支持通过 lambda 创建自定义行
|
||||
setupItem(): 调用 mRowCreator(&builder) 直接构造 QML
|
||||
├── QDetailsViewRow_Custom ← 支持通过 lambda 创建自定义行
|
||||
│ setupItem(): 调用 mRowCreator(&builder) 直接构造 QML
|
||||
└── QDetailsViewRow_Group ← 可折叠的属性分组标题行
|
||||
isGroup() 返回 true,setupItem() 渲染粗体分组标题 + 折叠时显示子项计数 "(N)"
|
||||
```
|
||||
|
||||
**QDetailsViewRow_Group**:
|
||||
- `isGroup()` 返回 `true`,通过 model 的 `isGroup` role 暴露给 QML delegate
|
||||
- `setupItem()` 使用 `makeNameValueSlot()` 构建行布局:
|
||||
- **名称区**:粗体 (`font.bold: true`) 文本显示分组名(如"数值"、"颜色")
|
||||
- **值区**:折叠时显示 `"(N)"` 表示子属性数量(通过 `groupChildCount` 属性绑定)
|
||||
- 高度固定 25px,颜色使用 `ColorPalette.theme.labelPrimary`
|
||||
- 展开/折叠由 delegate 模板中的 `TapHandler` 触发 `detailsView.toggleExpanded(row)`
|
||||
- `setObject()` 时 C++ 侧自动展开所有 group 行(一次性,避免 delegate 重建时的重复展开循环)
|
||||
|
||||
#### 6. IPropertyTypeCustomization(类型自定义扩展接口)
|
||||
```cpp
|
||||
class IPropertyTypeCustomization {
|
||||
|
|
@ -858,6 +874,7 @@ QHash<QMetaType, TypeEditorCreator> mTypeEditorCreatorMap;
|
|||
- **LayoutBuilder**:管理子行结构
|
||||
- `addProperty(handle, overrideName)` — 添加属性子行
|
||||
- `addCustomRow(lambda, overrideName)` — 添加自定义行
|
||||
- `addGroup(groupName)` — 创建 `QDetailsViewRow_Group` 分组标题行,返回其指针供后续向组内添加子行
|
||||
- `addObject(QObject*)` — 添加对象作为属性行
|
||||
|
||||
### Q_CLASSINFO 元数据系统
|
||||
|
|
@ -911,8 +928,9 @@ void CMainWindow::onSignal_selectionChanged() {
|
|||
```
|
||||
|
||||
**当前图元的 Q_PROPERTY 状态**:
|
||||
- `GraphicsBusSectionItem`:唯一完整声明了 Q_PROPERTY 的图元(Int, Float, LimitedDouble, String, Directory, Vec2-4, Mat4, Color, ColorList, ColorMap, CustomEnum, CustomType, CustomGadget, CustomGadgetPtr, CustomGadgetSharedPtr 等) — 这些是 PropertyEditor 示例代码中的测试属性
|
||||
- `GraphicsBaseItem`、`GraphicsRectItem`、`GraphicsPolygonItem`、`GraphicsItemGroup`:**无 Q_PROPERTY 声明** — 因此属性编辑器不会显示任何属性行
|
||||
- `GraphicsBusSectionItem`:唯一完整声明了 Q_PROPERTY 的图元(Int, Float, LimitedDouble, String, Directory, Vec2-4, Mat4, Color, ColorList, ColorMap, CustomEnum, CustomType, CustomGadget, CustomGadgetPtr, CustomGadgetSharedPtr 等) — 同时重写 `getPropertyGroup()` 将属性分入 5 个可折叠分组(数值、文本、向量/矩阵、颜色、自定义类型)
|
||||
- `GraphicsBaseItem` 实现了 `IPropertyGroupProvider` 接口,默认返回空字符串(无分组),子类可重写
|
||||
- `GraphicsRectItem`、`GraphicsPolygonItem`、`GraphicsItemGroup`:**无 Q_PROPERTY 声明** — 因此属性编辑器不会显示任何属性行
|
||||
|
||||
**BayTemplate 自定义类型**:
|
||||
- `QCustomType` (结构体):`{ unsigned int ArraySize; QVector<int> Array; }` — 通过 `PropertyTypeCustomization_CustomType` 定制,header 显示 Sort 按钮,children 展开为 ArraySize + Array
|
||||
|
|
@ -934,6 +952,113 @@ QPropertyHandle::setVar(var)
|
|||
└── if (currVar != var) emit asRequestRollback(currVar) ← 值被拦截时通知编辑器回滚
|
||||
```
|
||||
|
||||
### 属性分组系统 (Property Grouping)
|
||||
|
||||
属性分组允许 QObject 子类将 Q_PROPERTY 属性归类到可折叠的分组标题下,使属性编辑器在有大量属性时更易浏览。
|
||||
|
||||
#### IPropertyGroupProvider 接口
|
||||
|
||||
```cpp
|
||||
// PropertyEditor/source/include/IPropertyGroupProvider.h
|
||||
class IPropertyGroupProvider {
|
||||
public:
|
||||
virtual ~IPropertyGroupProvider() = default;
|
||||
virtual QString getPropertyGroup(const QString& propertyName) const = 0;
|
||||
};
|
||||
```
|
||||
|
||||
- 返回空字符串表示属性不分组(默认行为)
|
||||
- 返回非空字符串表示属性属于该名称的分组
|
||||
- 同名分组的属性会被收集到同一个 group 下,按分组调用顺序排列
|
||||
- **不属于 PropertyEditor 库本身**,而是由使用方(如 BayTemplate 的 GraphicsBaseItem)实现
|
||||
|
||||
#### 分组构建流程
|
||||
|
||||
```
|
||||
PropertyTypeCustomization_ObjectDefault::customizeChildren(handle, builder)
|
||||
│
|
||||
├── 检查 objectHandle->getObject() 是否实现 IPropertyGroupProvider?
|
||||
│ NO → 原有逻辑:遍历 metaObject->propertyCount() 平铺所有属性
|
||||
│
|
||||
│ YES → 新分组逻辑:
|
||||
│ 1. 遍历所有 QMetaProperty
|
||||
│ 2. 调用 groupProvider->getPropertyGroup(prop.name()) 获取分组名
|
||||
│ 3. 空字符串 → 加入 ungroupedProps 列表
|
||||
│ 4. 非空 → 加入对应 groupedProps[groupName] 列表
|
||||
│ 5. 遍历 groupedProps:
|
||||
│ ├── builder->addGroup(groupName) → 创建 QDetailsViewRow_Group
|
||||
│ └── 在 group 内用子 LayoutBuilder 添加该组的属性
|
||||
│ 6. 遍历 ungroupedProps:
|
||||
│ └── builder->addProperty() 直接添加到根(不分组)
|
||||
```
|
||||
|
||||
#### QDetailsViewRow_Group 渲染
|
||||
|
||||
在 QML delegate 中通过 `isGroup` role 区分 group 行和普通属性行:
|
||||
|
||||
- **已展开**:显示粗体分组标题(如"颜色"),右侧无内容
|
||||
- **已折叠**:显示粗体分组标题 + 右侧 `"(N)"` 显示子属性数量
|
||||
- 展开/折叠箭头由 `makeNameValueSlot()` 中的图标控制(根据 `detailsDelegate.expanded` 选择 expand.png / unexpand.png)
|
||||
- 点击行触发 `TapHandler { onTapped: detailsView.toggleExpanded(row) }`
|
||||
|
||||
**折叠计数实现**(`QQuickDetailsViewRow.cpp`):
|
||||
```qml
|
||||
// 在 value slot 区域创建 countLabel
|
||||
Item {
|
||||
property int groupChildCount: 0
|
||||
visible: !detailsDelegate.expanded // 仅折叠时可见
|
||||
Text {
|
||||
anchors.right: parent.right
|
||||
text: "(" + groupChildCount + ")"
|
||||
color: ColorPalette.theme.textPrimary
|
||||
}
|
||||
}
|
||||
```
|
||||
`groupChildCount` 在 C++ 侧通过 `countLabel->setProperty("groupChildCount", childCount)` 设置。
|
||||
|
||||
#### Model 角色扩展
|
||||
|
||||
ViewModel 新增 `isGroup` role:
|
||||
|
||||
```cpp
|
||||
// QQuickDetailsViewModel
|
||||
enum Roles { name = 0, isGroup };
|
||||
// roleNames() 返回 { { Roles::isGroup, "isGroup" } }
|
||||
|
||||
// data() 分发
|
||||
case Roles::isGroup: return node->isGroup();
|
||||
```
|
||||
|
||||
`QQuickDetailsViewPrivate::updateRequiredProperties()` 在每个 delegate 初始化时通过 `setRequiredProperty("isGroup", ...)` 将值推送到 QML delegate。
|
||||
|
||||
#### setObject() 自动展开
|
||||
|
||||
`QQuickDetailsView::setObject()` 在切换检查对象后,遍历顶层 model 行,对所有 `isGroup()` 为 true 的行调用 `expand(row)`。这是一次性的 C++ 侧操作,在 delegate 创建之前完成,避免 delegate 重建时的重复展开循环。
|
||||
|
||||
#### BayTemplate 集成
|
||||
|
||||
**GraphicsBaseItem** 实现 `IPropertyGroupProvider`,默认返回空字符串(无分组):
|
||||
|
||||
```cpp
|
||||
class GraphicsBaseItem : public QObject, public AbstractShape, public IPropertyGroupProvider {
|
||||
QString getPropertyGroup(const QString&) const override { return {}; }
|
||||
};
|
||||
```
|
||||
|
||||
**GraphicsBusSectionItem** 重写 `getPropertyGroup()` 作为使用示例(`source/graphicsItem/graphicsBusSectionItem.cpp`):
|
||||
|
||||
| 分组名 | 包含属性 |
|
||||
|--------|---------|
|
||||
| 数值 | Int, Float, LimitedDouble |
|
||||
| 文本 | String, Directory |
|
||||
| 向量/矩阵 | Vec2, Vec3, Vec4, Mat4 |
|
||||
| 颜色 | Color, ColorList, ColorMap |
|
||||
| 自定义类型 | CustomEnum, CustomType, CustomGadget, CustomGadgetPtr, CustomGadgetSharedPtr |
|
||||
|
||||
不分组(返回空)的属性:`m_lastBoudingRectF`, `m_lastBoudingPointF`, `m_dRatioX`, `m_dRatioY`(内部状态属性)。
|
||||
|
||||
**扩展方式**:任意 QObject 子类只需实现 `IPropertyGroupProvider` 接口并重写 `getPropertyGroup()`,PropertyEditor 的 `PropertyTypeCustomization_ObjectDefault` 会自动检测并使用分组布局。
|
||||
|
||||
### 关键文件索引
|
||||
|
||||
| 文件 | 角色 |
|
||||
|
|
@ -952,7 +1077,8 @@ QPropertyHandle::setVar(var)
|
|||
| `PropertyEditor/source/src/QQuickDetailsViewRow.cpp` | 模型行节点(setupItem + attachChildren)|
|
||||
| `PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp` | QML 布局构建器 |
|
||||
| `PropertyEditor/source/include/IPropertyTypeCustomization.h` | 类型定制接口 |
|
||||
| `PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.cpp` | 默认 QObject 属性展开(遍历 QMetaProperty)|
|
||||
| `PropertyEditor/source/include/IPropertyGroupProvider.h` | 属性分组接口(QObject 声明属性所属分组)|
|
||||
| `PropertyEditor/source/src/Customization/PropertyTypeCustomization_ObjectDefault.cpp` | 默认 QObject 属性展开(含分组检测逻辑)|
|
||||
| `PropertyEditor/source/include/QQuickDetailsViewMananger.h` | 全局注册中心单例 |
|
||||
| `PropertyEditor/source/src/QQuickDetailsViewMananger.cpp` | 类型编辑器注册、自定义类型查找 |
|
||||
| `PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp` | 基本类型编辑器注册(int, float, QString, QColor 等)|
|
||||
|
|
|
|||
Loading…
Reference in New Issue