ConfigEditor 模块全面系统分析
目录
### 文章目录
- [ConfigEditor 模块全面系统分析](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [目录](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [@toc](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [1 模块概述](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [1.1 基本信息](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [1.2 模块定位:为什么要做 ConfigEditor?](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [2 模块整体架构解析](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [2.1 架构图](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [2.2 模块间依赖关系](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [2.3 模块划分](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [2.4 数据流走向](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [2.5 核心技术栈](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [2.6 架构设计优势](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [潜在改进点](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3 类级代码注释详解](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.1 IConfigEditorModule --- 模块对外接口](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.2 FConfigEditorModule --- 模块实现](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.3 SConfigEditor --- 主编辑器面板](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.4 STargetPlatformSelector --- 目标平台选择器](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.5 UConfigHierarchyPropertyView --- 配置层级数据模型](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.6 UPropertyConfigFileDisplayRow --- 配置文件行数据](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.7 FConfigPropertyCustomColumn --- 自定义属性列](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.8 FConfigPropertyCellPresenter / SConfigPropertyCell --- 属性值单元格](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [3.9 文件状态列 --- FConfigPropertyConfigFileStateCustomColumn](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [4 功能使用示例编写](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [示例1:在自定义 Details 面板中集成 ConfigEditor](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [示例2:注入自定义属性编辑控件](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [5 总结与最佳实践](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [5.1 核心要点](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [5.2 值得借鉴的设计模式](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [5.3 注意事项](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
- [5.4 模块文件结构参考](#文章目录 ConfigEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 模块定位:为什么要做 ConfigEditor? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IConfigEditorModule — 模块对外接口 3.2 FConfigEditorModule — 模块实现 3.3 SConfigEditor — 主编辑器面板 3.4 STargetPlatformSelector — 目标平台选择器 3.5 UConfigHierarchyPropertyView — 配置层级数据模型 3.6 UPropertyConfigFileDisplayRow — 配置文件行数据 3.7 FConfigPropertyCustomColumn — 自定义属性列 3.8 FConfigPropertyCellPresenter / SConfigPropertyCell — 属性值单元格 3.9 文件状态列 — FConfigPropertyConfigFileStateCustomColumn 4 功能使用示例编写 示例1:在自定义 Details 面板中集成 ConfigEditor 示例2:注入自定义属性编辑控件 示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性 5 总结与最佳实践 5.1 核心要点 5.2 值得借鉴的设计模式 5.3 注意事项 5.4 模块文件结构参考)
1 模块概述
1.1 基本信息
| 属性 | 值 |
|---|---|
| 模块名称 | ConfigEditor |
| 类型 | Editor |
| 位置 | Engine/Source/Editor/ConfigEditor |
| 描述 | 提供一个可视化编辑器,用于查看和编辑属性在不同配置文件层级中的值 |
整个模块代码量不大,文件结构紧凑。非常适合作为对 UE5 Editor 工具开发、Slate UI 框架、以及属性系统交互的学习案例。

在 ConfigEditor 模块中,主要包含以下核心类:IConfigEditorModule、FConfigEditorModule、SConfigEditor、STargetPlatformSelector、UConfigHierarchyPropertyView、UPropertyConfigFileDisplayRow,以及若干 PropertyTable 自定义列和单元格展示器。
1.2 模块定位:为什么要做 ConfigEditor?
在 UE5 中,一个 UObject 的属性可以存在于多个配置文件的层级中,这是 UE 配置系统(Config Hierarchy)的核心机制。以 BaseEngine.ini 为例,它的层级结构大致是这样的:
BaseEngine.ini → DefaultEngine.ini → [Platform]Engine.ini → UserEngine.ini
每一层都可以覆盖上一层中某个属性的值。这种设计非常灵活,但也带来了一个棘手的问题:当某个属性在多层配置中都有值时,编辑器里看到的到底是哪一层的值?如果想修改特定平台(比如 PS5、Android)的配置,该去哪里改?
在没有 ConfigEditor 之前,开发者只能手动打开各个 .ini 文件,逐行查找。这不仅效率低下,而且容易出错------可能改错了文件,或者遗漏了某个平台的特殊配置。
ConfigEditor 要解决的就是这个痛点。它提供了一个可视化的面板,让开发者能够:
- 一目了然地看到某个属性在所有配置层级中的值
- 按目标平台筛选,只查看该平台相关的配置
- 直接在编辑器中修改 各层级的配置值,而无需手动编辑
.ini文件 - 了解配置文件的版本控制状态(文件是否可写、是否被锁定)
这部分内容放在 Editor 模块中而非 Runtime 模块,是因为它纯粹是编辑器工具------打包后的游戏不需要这个功能。它依赖了 PropertyEditor、Slate、SourceControl、TargetPlatform 等编辑器模块,通过 IConfigEditorModule 接口对外暴露能力。
2 模块整体架构解析
2.1 架构图
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 外部调用者(PropertyEditor) │
│ 通过 IConfigEditorModule 接口与 ConfigEditor 交互 │
└───────────────────────────────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ FConfigEditorModule (模块入口) │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ 实现 IConfigEditorModule 接口: │ │
│ │ - CreateHierarchyEditor(FProperty*) │ │
│ │ - AddExternalPropertyValueWidgetAndConfigPairing() │ │
│ │ - GetValueWidgetForConfigProperty() │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ 内部管理: │ │
│ │ - TMap<FString, TSharedPtr<SWidget>> ExternalPropertyValueWidgetAndConfig... │ │
│ │ - TSharedPtr<SConfigEditor> PropertyConfigEditor │ │
│ │ - TWeakFieldPtr<FProperty> CachedPropertyToView │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────┬─────────────────────────────────────────┘
│ 创建并管理
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ SConfigEditor (主UI面板) │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ 子组件: │ │
│ │ - STargetPlatformSelector (目标平台下拉选择器) │ │
│ │ - IDetailsView (属性详情视图) │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ 核心逻辑: │ │
│ │ - CreateDisplayObjectForSelectedTargetPlatform() 遍历配置层级,构建显示数据 │ │
│ │ - HandleTargetPlatformChanged() 切换平台,刷新显示 │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└───────────────┬─────────────────────────────────────────────┬───────────────────┘
│ │
▼ ▼
┌───────────────────────────────────┐ ┌─────────────────────────────────────────┐
│ STargetPlatformSelector │ │ UConfigHierarchyPropertyView (数据模型) │
│ (平台选择器) │ ├─────────────────────────────────────────┤
├───────────────────────────────────┤ │ - EditProperty: TFieldPath<FProperty> │
│ - STextComboBox │ │ - ConfigFilePropertyObjects: │
│ - 从 ITargetPlatformManager 获取 │ │ TArray<UPropertyConfigFileDisplayRow> │
│ 可用平台列表 │ └──────────────────┬──────────────────────┘
└───────────────────────────────────┘ │
│ 包含多个
▼
┌─────────────────────────────────────────┐
│ UPropertyConfigFileDisplayRow (行数据) │
├─────────────────────────────────────────┤
│ - ConfigFileName: FString │
│ - ExternalProperty: TFieldPath<FProperty>│
│ - bIsFileWritable: bool │
└──────────────────┬──────────────────────┘
│
┌────────────────────────────────┼─────────────────────────┐
▼ ▼ ▼
┌───────────────────────────────┐ ┌───────────────────────────────┐ ┌───────────────────────────┐
│ FConfigPropertyCustomColumn │ │ FConfigPropertyCellPresenter │ │ FConfigPropertyConfigFile │
│ (自定义属性列) │ │ (单元格展示器) │ │ StateCustomColumn │
├───────────────────────────────┤ ├───────────────────────────────┤ │ (文件状态列) │
│ - Supports() │ │ - SConfigPropertyCell │ ├───────────────────────────┤
│ - CreateColumnLabel() │ │ (具体单元格Widget) │ │ - Supports() │
│ - CreateCellPresenter() │ │ ├─ 属性值编辑控件 │ │ - CreateCellPresenter() │
└───────────────────────────────┘ │ ├─ 添加按钮(+按钮) │ └───────────────────────────┘
│ └─ 移除按钮(X按钮) │
└───────────────────────────────┘
2.2 模块间依赖关系
FConfigEditorModule (模块入口)
│ 实现
▼
IConfigEditorModule (对外接口)
│ 被调用
▼
PropertyEditor 模块 (Details面板、PropertyTable)
│
├──→ SConfigEditor (Slate UI面板)
│ ├──→ STargetPlatformSelector (平台选择)
│ │ └──→ ITargetPlatformManagerModule (获取可用平台)
│ └──→ UConfigHierarchyPropertyView (数据模型)
│ └──→ UPropertyConfigFileDisplayRow[] (行数据)
│ └──→ ISourceControlModule (检查文件状态)
│
├──→ FConfigPropertyCustomColumn (PropertyTable自定义列)
│ └──→ FConfigPropertyCellPresenter (单元格展示)
│ └──→ SConfigPropertyCell (Slate单元格Widget)
│
└──→ FConfigPropertyConfigFileStateCustomColumn (文件状态列)
└──→ FConfigPropertyConfigFileStateCellPresenter
整个模块的依赖关系呈线性分层 结构,上层(FConfigEditorModule)负责模块初始化和对外接口,中间层(SConfigEditor)负责 UI 组装和用户交互,底层(UConfigHierarchyPropertyView 和各种 Cell/Column 类)负责数据模型和具体渲染。
2.3 模块划分
1. FConfigEditorModule --- 模块入口层
职责:
- 实现
IModuleInterface,管理模块生命周期 - 实现
IConfigEditorModule,对外提供三个核心 API - 注册 Nomad Tab(可在编辑器中任意位置停靠的标签页)
- 维护外部属性值 Widget 与配置文件之间的映射关系
特点:
- 充当"桥梁"角色,连接 PropertyEditor 系统和 ConfigEditor 的 Slate UI
- 通过
TMap<FString, TSharedPtr<SWidget>>实现 Widget 复用机制
2. SConfigEditor --- 主 UI 面板层
职责:
- 组装平台选择器 + 属性详情视图,形成完整的配置编辑器面板
- 根据选中的目标平台,遍历配置层级,构建数据模型
- 响应平台切换事件,刷新显示
特点:
- 使用
FConfigCacheIni临时缓存来加载配置文件 - 动态创建
UConfigHierarchyPropertyView作为 DetailsView 的数据源
3. STargetPlatformSelector --- 平台选择层
职责:
- 从
ITargetPlatformManagerModule获取所有可用目标平台 - 提供下拉框供用户选择平台
- 通过委托通知上级平台切换
特点:
- 使用
STextComboBox作为 UI 控件 - 支持运行时动态切换平台,无需重新打开面板
4. UConfigHierarchyPropertyView / UPropertyConfigFileDisplayRow --- 数据模型层
职责:
UConfigHierarchyPropertyView:承载要编辑的属性引用和所有配置层级的行数据UPropertyConfigFileDisplayRow:代表单个配置文件中的属性条目,包含文件路径、属性引用、可写性状态
特点:
- 继承自
UObject,可以被IDetailsView直接渲染 - 使用
TFieldPath<FProperty>安全地引用属性 - 集成 SourceControl 检查文件的可写状态
5. 自定义列与单元格展示器 --- 属性渲染层
职责:
FConfigPropertyCustomColumn:PropertyTable 的自定义列,用于显示属性的值编辑控件FConfigPropertyCellPresenter/SConfigPropertyCell:渲染单个单元格,包含编辑控件、添加/移除按钮FConfigPropertyConfigFileStateCustomColumn/FConfigPropertyConfigFileStateCellPresenter:显示配置文件的版本控制状态
特点:
- 实现了
IPropertyTableCustomColumn和IPropertyTableCellPresenter接口 - 与
FConfigEditorModule协作,通过GetValueWidgetForConfigProperty()获取外部提供的编辑控件
2.4 数据流走向
打开配置编辑器的完整流程:
1. 外部调用 FConfigEditorModule::CreateHierarchyEditor(EditProperty)
↓
2. FConfigEditorModule 缓存 CachedPropertyToView,创建 SConfigEditor
↓
3. SConfigEditor::Construct() 被调用
├─ 创建 STargetPlatformSelector(获取可用平台列表)
├─ 创建 IDetailsView(PropertyEditor 的详情视图)
└─ 调用 CreateDisplayObjectForSelectedTargetPlatform()
↓
4. CreateDisplayObjectForSelectedTargetPlatform()
├─ 获取当前选中的目标平台名称
├─ 创建/复用 UConfigHierarchyPropertyView 对象
├─ 通过 FConfigCacheIni 加载该平台的 .ini 文件
├─ 遍历配置文件的层级链(Hierarchy)
│ └─ 为每个层级创建一个 UPropertyConfigFileDisplayRow
│ └─ InitWithConfigAndProperty() 检查文件可写状态
└─ 将 ConfigFilePropertyObjects 设置到 DetailsView
↓
5. DetailsView 渲染 ConfigFilePropertyObjects 数组
├─ 常规列:由 PropertyEditor 默认渲染
├─ "ExternalProperty" 列:由 FConfigPropertyCustomColumn 渲染
│ └─ FConfigPropertyCellPresenter 创建 SConfigPropertyCell
│ └─ 从 FConfigEditorModule 获取外部编辑 Widget
└─ 文件状态列:由 FConfigPropertyConfigFileStateCustomColumn 渲染
↓
6. 用户切换平台 → HandleTargetPlatformChanged()
└─ 重新执行步骤 4,刷新显示
外部 Widget 注入机制:
1. 外部系统(如 PropertyEditor 的某个自定义详情面板)
调用 AddExternalPropertyValueWidgetAndConfigPairing(ConfigFile, ValueWidget)
↓
2. FConfigEditorModule 将 (ConfigFile, ValueWidget) 存入 Map
↓
3. SConfigPropertyCell 渲染时
调用 GetValueWidgetForConfigProperty(ConfigFile)
↓
4. FConfigEditorModule 从 Map 中查找并返回对应的 Widget
↓
5. SConfigPropertyCell 将该 Widget 嵌入到单元格中显示
这个"外部 Widget 注入"机制是整个模块最精妙的设计之一。它允许外部系统(如 PropertyEditor 的细节面板)提供属性类型对应的编辑控件(比如枚举用下拉框、布尔值用复选框),而 ConfigEditor 本身不需要知道具体的属性类型。这种"依赖倒置"的设计,让 ConfigEditor 保持了对各种属性类型的通用性。
2.5 核心技术栈
| 技术/类 | 用途 |
|---|---|
IModuleInterface |
模块生命周期管理 |
FGlobalTabmanager |
注册和管理 Nomad 标签页 |
SCompoundWidget |
Slate 复合 Widget 基类 |
IDetailsView |
属性详情视图,渲染 UObject 属性 |
FConfigCacheIni |
临时配置缓存,加载 .ini 文件 |
IPropertyTableCustomColumn |
PropertyTable 自定义列接口 |
IPropertyTableCellPresenter |
PropertyTable 单元格展示器接口 |
ITargetPlatformManagerModule |
获取可用目标平台列表 |
ISourceControlModule |
版本控制集成,检查文件状态 |
TFieldPath<FProperty> |
安全的属性引用(支持热重载) |
STextComboBox |
Slate 文本下拉框控件 |
FSimpleDelegate |
简单委托,用于平台切换通知 |
2.6 架构设计优势
- 关注点分离:UI 层(SConfigEditor)、数据层(UConfigHierarchyPropertyView)、渲染层(CustomColumn/CellPresenter)各司其职
- 低耦合 :通过
IConfigEditorModule接口对外暴露能力,通过 Widget 注入机制解耦属性类型与编辑器 - 可扩展性:自定义列和单元格展示器基于接口实现,可以灵活替换或扩展
- Slate 原生集成:完全使用 Slate 框架构建 UI,与 UE 编辑器风格一致,支持停靠和布局保存
- 平台感知 :与
ITargetPlatformManager集成,自动获取所有可用平台,无需手动维护平台列表
潜在改进点
- 移除功能未完成 :
SConfigPropertyCell::GetRemovePropertyFromConfigVisibility()始终返回Collapsed,注释表明受限于 UObject 和 Config 的技术限制,移除功能尚未实现 - 版本控制集成不完整 :
UPropertyConfigFileDisplayRow::InitWithConfigAndProperty()中 SourceControl 状态获取被注释掉了,当前只通过文件系统判断只读状态 - 错误处理:模块中对配置加载失败、属性不存在等异常情况的处理较为简单
- 批量操作:缺少对多个属性批量修改配置的能力
3 类级代码注释详解
3.1 IConfigEditorModule --- 模块对外接口
IConfigEditorModule.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Public/IConfigEditorModule.h)
概述:
IConfigEditorModule 是 ConfigEditor 模块对外暴露的抽象接口,继承自 IModuleInterface。任何需要集成 ConfigEditor 功能的模块,都通过这个接口来调用。它定义了三个纯虚函数,构成了 ConfigEditor 的全部对外能力。
核心设计理念:
- 最小化接口:只暴露三个方法,保持接口简洁
- Widget 注入模式:允许外部系统提供属性编辑控件,实现依赖倒置
- 按需创建:ConfigEditor 本身不维护属性编辑控件,而是通过注入机制按需获取
接口方法详解:
CreateHierarchyEditor(FProperty* EditProperty)
cpp
virtual void CreateHierarchyEditor(FProperty* EditProperty) = 0;
功能分析:
- 为指定的属性创建一个配置层级编辑器
- 接收一个
FProperty*参数,指向需要查看/编辑的属性 - 调用后,ConfigEditor 会缓存该属性引用,并在打开标签页时自动加载相关配置数据
使用场景:
- 在 PropertyEditor 的细节面板中,用户点击某个属性的"查看配置层级"按钮时调用
- 通常在属性旁边有一个小按钮(齿轮图标),点击后触发此方法
AddExternalPropertyValueWidgetAndConfigPairing(const FString& ConfigFile, const TSharedPtr<SWidget> ValueWidget)
cpp
virtual void AddExternalPropertyValueWidgetAndConfigPairing(const FString& ConfigFile, const TSharedPtr<SWidget> ValueWidget) = 0;
功能分析:
- 将一个属性值编辑控件(Widget)与特定的配置文件路径关联起来
- 这是 Widget 注入机制的核心入口
- 参数
ConfigFile是配置文件的完整路径 - 参数
ValueWidget是外部系统创建的属性编辑控件(如枚举下拉框、布尔复选框等)
为什么需要这个机制?
不同类型的属性需要不同的编辑控件------枚举需要下拉框,布尔需要复选框,数值需要输入框。ConfigEditor 本身不负责创建这些控件,而是由 PropertyEditor 系统根据属性类型创建好,然后通过这个接口注入进来。这样 ConfigEditor 就能保持对属性类型的无知,只需要在合适的时机把控件显示出来。
GetValueWidgetForConfigProperty(const FString& ConfigFile)
cpp
virtual TSharedRef<SWidget> GetValueWidgetForConfigProperty(const FString& ConfigFile) = 0;
功能分析:
- 根据配置文件路径,获取之前注入的属性值编辑控件
- 返回一个
TSharedRef<SWidget>,调用者可以放心使用(不会返回空指针) - 如果找不到对应的 Widget,返回
SNullWidget::NullWidget
调用时机:
- 在
SConfigPropertyCell::BuildDisplayAreaWidget()中被调用 - 每个配置行渲染时,需要显示对应属性的编辑控件
3.2 FConfigEditorModule --- 模块实现
ConfigEditorModule.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/ConfigEditorModule.h) | ConfigEditorModule.cpp(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/ConfigEditorModule.cpp)
概述:
FConfigEditorModule 是 IConfigEditorModule 的具体实现,也是整个 ConfigEditor 模块的入口。它负责模块的初始化、标签页注册、以及接口方法的实现。
StartupModule()
cpp
void FConfigEditorModule::StartupModule()
{
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
ConfigEditorModule::ConfigEditorId,
FOnSpawnTab::CreateRaw(this, &FConfigEditorModule::SpawnConfigEditorTab))
.SetDisplayName(NSLOCTEXT("ConfigEditorModule", "TabTitle", "Config Editor"))
.SetTooltipText(NSLOCTEXT("ConfigEditorModule", "TooltipText", "Open the Config Editor tab."))
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "ConfigEditor.TabIcon"));
}
功能分析:
- 在模块启动时注册一个 Nomad Tab(可任意停靠的标签页)
- Nomad Tab 意味着这个标签页可以独立于任何资产编辑器存在,用户可以将它拖到编辑器任意位置
- 设置了标签页的显示名称、工具提示和图标
- 标签页的实际内容由
SpawnConfigEditorTab()方法创建
设计意图:
使用 Nomad Tab 而非 Asset Editor Tab,是因为 ConfigEditor 不绑定任何特定资产------它查看的是某个属性在所有配置文件中的层级关系。用户可以随时打开它,查看任意属性的配置层级。
ShutdownModule()
cpp
void FConfigEditorModule::ShutdownModule()
{
}
功能分析:
- 目前为空实现
- 按照 UE 的惯例,通常不需要手动反注册 Nomad Tab,
FGlobalTabmanager在关闭时会自动清理
AddExternalPropertyValueWidgetAndConfigPairing()
cpp
void FConfigEditorModule::AddExternalPropertyValueWidgetAndConfigPairing(
const FString& ConfigFile, const TSharedPtr<SWidget> ValueWidget)
{
ExternalPropertyValueWidgetAndConfigPairings.Add(ConfigFile, ValueWidget);
}
功能分析:
- 将
(ConfigFile, ValueWidget)键值对存入内部的TMap - 这是一个简单的存储操作,但它是整个 Widget 注入机制的基础
- 同一个配置文件路径如果多次调用,后面的会覆盖前面的
GetValueWidgetForConfigProperty()
cpp
TSharedRef<SWidget> FConfigEditorModule::GetValueWidgetForConfigProperty(const FString& ConfigFile)
{
TSharedPtr<SWidget>* ValueWidget = ExternalPropertyValueWidgetAndConfigPairings.Find(ConfigFile);
return (ValueWidget != nullptr && (*ValueWidget).IsValid())
? (*ValueWidget).ToSharedRef()
: SNullWidget::NullWidget;
}
功能分析:
- 从内部 Map 中查找对应配置文件的 Widget
- 有防御性检查:指针非空且 Widget 有效
- 找不到时返回
SNullWidget::NullWidget(一个空占位 Widget),这是 Slate 中表示"无 Widget"的标准做法
SpawnConfigEditorTab()
cpp
TSharedRef<SDockTab> FConfigEditorModule::SpawnConfigEditorTab(const FSpawnTabArgs& Args)
{
return SNew(SDockTab)
.TabRole(ETabRole::NomadTab)
[
SAssignNew(PropertyConfigEditor, SConfigEditor, CachedPropertyToView)
];
}
功能分析:
- 创建标签页的实际内容
- 使用
SAssignNew创建SConfigEditor并同时将其赋值给PropertyConfigEditor成员变量 - 将
CachedPropertyToView(之前通过CreateHierarchyEditor缓存的属性)传递给SConfigEditor - 使用
SDockTab作为容器,ETabRole::NomadTab表示可任意停靠
CreateHierarchyEditor()
cpp
void FConfigEditorModule::CreateHierarchyEditor(FProperty* InEditProperty)
{
CachedPropertyToView = InEditProperty;
}
功能分析:
- 缓存要查看的属性引用
- 实际的 UI 创建在
SpawnConfigEditorTab()中进行 - 这种延迟创建的设计,使得属性可以先被缓存,等到标签页真正打开时再构建 UI
3.3 SConfigEditor --- 主编辑器面板
SConfigEditor.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/SConfigEditor.h) | SConfigEditor.cpp(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/SConfigEditor.cpp)
概述:
SConfigEditor 是整个 ConfigEditor 模块的核心 UI 面板。它继承自 SCompoundWidget,将平台选择器和属性详情视图组合在一起,形成一个完整的配置编辑器界面。
成员变量解析:
| 成员变量 | 类型 | 用途 |
|---|---|---|
TargetPlatformSelection |
TSharedPtr<STargetPlatformSelector> |
目标平台下拉选择器 |
PropHelper |
TWeakObjectPtr<UConfigHierarchyPropertyView> |
配置层级数据模型 |
PropertyValueEditor |
TSharedPtr<SWidget> |
属性值编辑区域 |
LocalConfigCache |
TSharedPtr<FConfigCacheIni> |
临时配置缓存 |
DetailsView |
TSharedPtr<IDetailsView> |
属性详情视图 |
EditProperty |
TWeakFieldPtr<FProperty> |
正在查看的属性 |
Construct() 方法详解:
cpp
void SConfigEditor::Construct(const FArguments& InArgs, TWeakFieldPtr<FProperty> InEditProperty)
{
// 1. 创建平台选择器
TargetPlatformSelection = SNew(STargetPlatformSelector)
.OnTargetPlatformChanged(this, &SConfigEditor::HandleTargetPlatformChanged);
EditProperty = InEditProperty;
// 2. 创建临时配置缓存
LocalConfigCache = MakeShareable(new FConfigCacheIni(EConfigCacheType::Temporary));
// 3. 创建属性详情视图
FDetailsViewArgs DetailsViewArgs;
{
DetailsViewArgs.bAllowSearch = false;
DetailsViewArgs.bHideSelectionTip = true;
DetailsViewArgs.bLockable = false;
DetailsViewArgs.bSearchInitialKeyFocus = true;
DetailsViewArgs.bUpdatesFromSelection = false;
DetailsViewArgs.bShowOptions = false;
DetailsViewArgs.bShowModifiedPropertiesOption = false;
}
DetailsView = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor")
.CreateDetailView(DetailsViewArgs);
// 4. 构建初始数据
CreateDisplayObjectForSelectedTargetPlatform();
// 5. 组装 UI 布局
PropertyValueEditor = DetailsView.ToSharedRef();
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
TargetPlatformSelection.ToSharedRef()
]
+ SVerticalBox::Slot()
[
PropertyValueEditor.ToSharedRef()
]
];
}
执行流程分析:
- 创建平台选择器 :
STargetPlatformSelector在构造时自动获取所有可用平台,不需要手动传入平台列表 - 创建配置缓存 :
EConfigCacheType::Temporary表示这是一个临时缓存,不会被持久化,也不会影响全局配置 - 创建详情视图 :通过
PropertyEditorModule创建,关闭了搜索、选项提示等功能,保持界面简洁 - 构建初始数据 :调用
CreateDisplayObjectForSelectedTargetPlatform()加载当前选中平台的配置数据 - 组装 UI:垂直布局,上面是平台选择器(自动高度),下面是属性详情视图(填充剩余空间)
CreateDisplayObjectForSelectedTargetPlatform() --- 核心数据构建方法:
cpp
void SConfigEditor::CreateDisplayObjectForSelectedTargetPlatform()
{
FString SelectedTargetPlatform = *TargetPlatformSelection->GetSelectedTargetPlatform().Get();
// 创建或复用 UConfigHierarchyPropertyView 对象
FString ConfigHelperName(TEXT("ConfigEditorPropertyHelper_"));
ConfigHelperName += SelectedTargetPlatform;
PropHelper = FindObject<UConfigHierarchyPropertyView>(GetTransientPackage(), *ConfigHelperName);
if (!PropHelper.IsValid())
{
PropHelper = NewObject<UConfigHierarchyPropertyView>(GetTransientPackage(), *ConfigHelperName);
PropHelper->AddToRoot(); // 防止被 GC 回收
}
PropHelper->EditProperty = EditProperty.Get();
FString ClassConfigName = PropHelper->EditProperty->GetOwnerClass()->ClassConfigName.ToString();
// 加载该平台的配置文件
FConfigFile PlatformIniFile;
LocalConfigCache->LoadLocalIniFile(PlatformIniFile, *ClassConfigName, true, *SelectedTargetPlatform);
// 遍历配置层级,为每个层级创建行数据
for (const auto& IniFile : PlatformIniFile.Branch->Hierarchy)
{
UPropertyConfigFileDisplayRow* ConfigFilePropertyObj =
NewObject<UPropertyConfigFileDisplayRow>(GetTransientPackage(), *IniFile.Value);
ConfigFilePropertyObj->InitWithConfigAndProperty(IniFile.Value, PropHelper->EditProperty.Get());
PropHelper->ConfigFilePropertyObjects.Add(ConfigFilePropertyObj);
}
DetailsView->SetObject(PropHelper.Get());
}
技术要点分析:
-
对象复用 :通过
ConfigEditorPropertyHelper_+ 平台名称的方式命名对象,并用FindObject查找已存在的对象。这意味着切换平台时,如果之前创建过该平台的数据对象,会直接复用而不是重新创建。 -
防止 GC :
AddToRoot()确保对象不会被垃圾回收。因为PropHelper是通过TWeakObjectPtr持有的,如果不加根引用,GC 可能会回收它。 -
配置层级遍历 :
PlatformIniFile.Branch->Hierarchy包含了从 Base → Default → Platform → User 的完整配置层级链。每个层级的配置文件路径作为IniFile.Value。 -
动态行对象创建 :每个配置层级创建一个
UPropertyConfigFileDisplayRow,其对象名就是配置文件路径。这同样实现了对象复用------同一个配置文件不会被重复创建。
3.4 STargetPlatformSelector --- 目标平台选择器
STargetPlatformSelector.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/STargetPlatformSelector.h) | STargetPlatformSelector.cpp(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/STargetPlatformSelector.cpp)
概述:
STargetPlatformSelector 是一个简单的 Slate 复合控件,提供目标平台的下拉选择功能。它从 ITargetPlatformManagerModule 获取所有可用平台,以 STextComboBox 的形式展示给用户。
CollateAvailableTargetPlatformEntries() --- 获取可用平台列表:
cpp
void STargetPlatformSelector::CollateAvailableTargetPlatformEntries()
{
ITargetPlatformManagerModule& TPM = GetTargetPlatformManagerRef();
TArray<ITargetPlatform*> ActiveTargetPlatforms = TPM.GetTargetPlatforms();
for (ITargetPlatform* TargetPlatform : ActiveTargetPlatforms)
{
const FString CurrentIniName = TargetPlatform->IniPlatformName();
if (!CurrentIniName.IsEmpty())
{
const TSharedPtr<FString>* ExistingEntry =
TargetPlatformOptionsSource.FindByPredicate(
[CurrentIniName](const TSharedPtr<FString>& CurElement)
{
return *CurElement == CurrentIniName;
});
if (ExistingEntry == nullptr || !(*ExistingEntry).IsValid())
{
TargetPlatformOptionsSource.Add(
MakeShareable(new FString(TargetPlatform->IniPlatformName())));
}
}
}
SelectedTargetPlatform = TargetPlatformOptionsSource[0];
}
功能分析:
- 从
ITargetPlatformManagerModule获取所有活动平台(如 Windows、Android、PS5 等) - 使用
IniPlatformName()获取平台的 ini 名称(如 "Windows"、"Android"、"PS5") - 通过
FindByPredicate去重,避免重复添加同名平台 - 默认选中第一个平台
注意: 这里使用的是 IniPlatformName() 而非 PlatformName()。IniPlatformName() 返回的是配置文件系统中使用的平台名称,例如用于 [Platform]Engine.ini 中的 [Platform] 部分。
3.5 UConfigHierarchyPropertyView --- 配置层级数据模型
ConfigPropertyHelper.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Public/ConfigPropertyHelper.h)
概述:
UConfigHierarchyPropertyView 是一个 UObject 派生类,用作 IDetailsView 的数据源。它持有要编辑的属性引用,以及一个 UPropertyConfigFileDisplayRow 数组,每个数组元素代表属性在一个配置层级中的状态。
cpp
UCLASS(MinimalAPI)
class UConfigHierarchyPropertyView : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(Transient, Category=Helper, EditAnywhere)
TFieldPath<FProperty> EditProperty;
UPROPERTY(Transient, Category = Helper, EditAnywhere)
TArray<TObjectPtr<UPropertyConfigFileDisplayRow>> ConfigFilePropertyObjects;
};
设计要点:
TFieldPath<FProperty>而非裸FProperty*:TFieldPath是 UE 对FProperty的安全封装,支持热重载------当 C++ 代码重新编译时,属性引用不会失效ConfigFilePropertyObjects使用EditAnywhere说明符,使得 DetailsView 可以自动展开渲染每个UPropertyConfigFileDisplayRow的属性- 所有属性标记为
Transient,不会被序列化保存
3.6 UPropertyConfigFileDisplayRow --- 配置文件行数据
ConfigPropertyHelper.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Public/ConfigPropertyHelper.h) | ConfigPropertyHelper.cpp(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/ConfigPropertyHelper.cpp)
概述:
UPropertyConfigFileDisplayRow 代表属性在单个配置文件中的状态。它包含配置文件路径、属性引用,以及文件是否可写的状态。
cpp
UCLASS(MinimalAPI)
class UPropertyConfigFileDisplayRow : public UObject
{
GENERATED_BODY()
public:
void InitWithConfigAndProperty(const FString& InConfigFileName, FProperty* InEditProperty);
UPROPERTY(Transient, Category = Helper, VisibleAnywhere)
FString ConfigFileName;
UPROPERTY(Transient, Category = Helper, EditAnywhere, meta=(EditCondition="bIsFileWritable"))
TFieldPath<FProperty> ExternalProperty;
UPROPERTY(Transient, Category = Helper, VisibleAnywhere)
bool bIsFileWritable;
};
成员变量解析:
| 成员 | 说明 |
|---|---|
ConfigFileName |
配置文件的完整路径,使用 VisibleAnywhere(只读展示) |
ExternalProperty |
指向要编辑的属性,使用 EditCondition="bIsFileWritable" ------只在文件可写时才能编辑 |
bIsFileWritable |
配置文件是否可写,影响编辑控件的启用状态 |
InitWithConfigAndProperty() --- 初始化与文件状态检查:
cpp
void UPropertyConfigFileDisplayRow::InitWithConfigAndProperty(
const FString& InConfigFileName, FProperty* InEditProperty)
{
ConfigFileName = FPaths::ConvertRelativePathToFull(InConfigFileName);
ExternalProperty = InEditProperty;
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
FSourceControlStatePtr SourceControlState = nullptr; // 版本控制集成暂未启用
if (FPaths::FileExists(ConfigFileName))
{
if (SourceControlState.IsValid())
{
bIsFileWritable = SourceControlState->IsCheckedOut() || SourceControlState->IsAdded();
}
else
{
bIsFileWritable = !IFileManager::Get().IsReadOnly(*ConfigFileName);
}
}
else
{
if (SourceControlState.IsValid())
{
bIsFileWritable = (SourceControlState->IsSourceControlled() && SourceControlState->CanAdd());
}
else
{
bIsFileWritable = false;
}
}
}
文件状态判断逻辑:
| 条件 | 结果 |
|---|---|
| 文件存在 + 版本控制 + 已签出/已添加 | 可写 |
| 文件存在 + 无版本控制 + 文件系统非只读 | 可写 |
| 文件存在 + 无版本控制 + 文件系统只读 | 不可写 |
| 文件不存在 + 版本控制 + 可添加 | 可写 |
| 文件不存在 + 其他情况 | 不可写 |
注意: 代码中 SourceControlState 被显式设置为 nullptr,版本控制集成实际上是注释掉的。这意味着当前版本只使用文件系统的只读属性来判断可写性。这是模块的一个已知待完善点。
3.7 FConfigPropertyCustomColumn --- 自定义属性列
ConfigPropertyColumn.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Public/PropertyVisualization/ConfigPropertyColumn.h) | ConfigPropertyColumn.cpp(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/PropertyVisualization/ConfigPropertyColumn.cpp)
概述:
FConfigPropertyCustomColumn 实现了 IPropertyTableCustomColumn 接口,用于在 PropertyTable(属性表格)中自定义渲染"ExternalProperty"这一列。它告诉 PropertyTable:"当遇到 ExternalProperty 这个属性时,不要用默认方式渲染,交给我来处理。"
Supports() --- 判断是否支持该列:
cpp
bool FConfigPropertyCustomColumn::Supports(
const TSharedRef<IPropertyTableColumn>& Column,
const TSharedRef<IPropertyTableUtilities>& Utilities) const
{
if (Column->GetDataSource()->IsValid())
{
TSharedPtr<FPropertyPath> PropertyPath = Column->GetDataSource()->AsPropertyPath();
if (PropertyPath.IsValid() && PropertyPath->GetNumProperties() > 0)
{
const FPropertyInfo& PropertyInfo = PropertyPath->GetRootProperty();
const FProperty* Property = PropertyInfo.Property.Get();
IsSupported = Property->GetFName() == TEXT("ExternalProperty");
}
}
return IsSupported;
}
实现原理:
- 通过列的数据源获取属性路径
- 获取根属性的名称,与
"ExternalProperty"比较 - 只有名称完全匹配时才返回
true,表示该列由本自定义列接管渲染
CreateColumnLabel() --- 创建列标题:
cpp
TSharedPtr<SWidget> FConfigPropertyCustomColumn::CreateColumnLabel(...) const
{
// ...
return SNew(STextBlock)
.Text(EditProperty->GetDisplayNameText());
}
功能分析:
- 列标题显示的是
EditProperty的显示名称(而非"ExternalProperty"这个内部名称) - 例如,如果查看的是
UObject::bIsEditorOnly,列标题会显示"Editor Only"
CreateCellPresenter() --- 创建单元格展示器:
cpp
TSharedPtr<IPropertyTableCellPresenter> FConfigPropertyCustomColumn::CreateCellPresenter(...) const
{
TSharedPtr<IPropertyHandle> PropertyHandle = Cell->GetPropertyHandle();
if (PropertyHandle.IsValid())
{
return MakeShareable(new FConfigPropertyCellPresenter(PropertyHandle));
}
return nullptr;
}
功能分析:
- 为每个单元格创建
FConfigPropertyCellPresenter - 通过
Cell->GetPropertyHandle()获取该单元格对应的属性句柄 - 属性句柄包含了该单元格对应的
UPropertyConfigFileDisplayRow对象信息
3.8 FConfigPropertyCellPresenter / SConfigPropertyCell --- 属性值单元格
ConfigPropertyCellPresenter.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/PropertyVisualization/ConfigPropertyCellPresenter.h) | ConfigPropertyCellPresenter.cpp(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/PropertyVisualization/ConfigPropertyCellPresenter.cpp)
概述:
FConfigPropertyCellPresenter 实现了 IPropertyTableCellPresenter 接口,负责在 PropertyTable 中渲染单个配置文件的属性值单元格。它内部创建 SConfigPropertyCell 作为实际的 Slate Widget。
SConfigPropertyCell 是单元格的具体 UI 实现,包含三个主要区域:
- 属性值编辑控件 :从
FConfigEditorModule获取的外部 Widget - 添加按钮(+):当配置文件还没有该属性条目时显示
- 移除按钮(X):当配置文件已有该属性条目时显示(目前功能未完成,始终隐藏)
SConfigPropertyCell 的 UI 结构:
SHorizontalBox
├── 属性值编辑区域 (Visible when: 已有属性条目)
│ └── SBorder
│ └── DisplayedValueWidget (从 ConfigEditorModule 获取的外部Widget)
├── 添加按钮区域 (Visible when: 无属性条目 且 文件可写)
│ └── SButton (+ 图标)
│ └── OnClicked → HandleAddPropertyToConfigClicked()
└── 移除按钮区域 (Visible when: Collapsed - 功能未完成)
└── SButton (X 图标)
└── OnClicked → HandleRemovePropertyFromConfigClicked()
DoesConfigFileHaveEntryForProperty() --- 检查属性是否已存在于配置文件中:
cpp
bool SConfigPropertyCell::DoesConfigFileHaveEntryForProperty()
{
UClass* OwnerClass = PropertyOwnerDisplayObject->ExternalProperty->GetOwnerClass();
FString SectionName = OwnerClass->GetPathName();
FString PropertyName = PropertyOwnerDisplayObject->ExternalProperty->GetName();
FString TempValue;
return GConfig->GetString(*SectionName, *PropertyName, TempValue,
PropertyOwnerDisplayObject->ConfigFileName);
}
功能分析:
- 通过
GConfig全局配置系统,在配置文件的指定 Section 下查找属性名 - Section 名称是属性所属类的路径名(如
/Script/Engine.Actor) - 如果找到该属性,返回
true;否则返回false - 这个返回值决定了显示编辑控件还是添加按钮
HandleAddPropertyToConfigClicked() --- 添加属性到配置文件:
cpp
FReply SConfigPropertyCell::HandleAddPropertyToConfigClicked()
{
PropertyOwnerDisplayObject->TryUpdateDefaultConfigFile(
PropertyOwnerDisplayObject->ConfigFileName);
bCachedConfigHasPropertyValue = true;
return FReply::Handled();
}
功能分析:
- 调用
TryUpdateDefaultConfigFile()将当前属性值写入配置文件 - 更新缓存状态,使 UI 从"显示添加按钮"切换为"显示编辑控件"
HandleRemovePropertyFromConfigClicked() --- 从配置文件中移除属性:
cpp
FReply SConfigPropertyCell::HandleRemovePropertyFromConfigClicked()
{
UClass* OwnerClass = PropertyOwnerDisplayObject->ExternalProperty->GetOwnerClass();
FString SectionName = OwnerClass->GetPathName();
FString PropertyName = PropertyOwnerDisplayObject->ExternalProperty->GetName();
GConfig->RemoveKey(*SectionName, *PropertyName, PropertyOwnerDisplayObject->ConfigFileName);
PropertyOwnerDisplayObject->TryUpdateDefaultConfigFile(PropertyOwnerDisplayObject->ConfigFileName);
return FReply::Handled();
}
功能分析:
- 通过
GConfig->RemoveKey()从配置文件中删除该属性条目 - 调用
TryUpdateDefaultConfigFile()刷新配置 - 但由于
GetRemovePropertyFromConfigVisibility()始终返回Collapsed,这个功能目前对用户不可见
3.9 文件状态列 --- FConfigPropertyConfigFileStateCustomColumn
ConfigPropertyConfigFileStateColumn.h(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Public/PropertyVisualization/ConfigPropertyConfigFileStateColumn.h) | ConfigPropertyConfigFileStateColumn.cpp(file:///f:/UE/UE_5.5/Engine/Source/Editor/ConfigEditor/Private/PropertyVisualization/ConfigPropertyConfigFileStateColumn.cpp)
概述:
FConfigPropertyConfigFileStateCustomColumn 是另一个自定义列,用于显示配置文件的版本控制状态。它和 FConfigPropertyCustomColumn 类似,但专门用于显示文件级别的状态信息。
Supports() --- 判断逻辑:
cpp
bool FConfigPropertyConfigFileStateCustomColumn::Supports(...) const
{
// ...
if (SupportedProperty == Property)
{
IsSupported = true;
}
// ...
}
功能分析:
- 与
FConfigPropertyCustomColumn通过名称匹配不同,这个类通过SupportedProperty指针直接比较 - 这意味着外部调用者需要显式设置
SupportedProperty来指定该列关联哪个属性 - 当前实现中,
CreateColumnLabel()返回NULL,文件状态列没有列标题
FConfigPropertyConfigFileStateCellPresenter:
cpp
TSharedRef<SWidget> FConfigPropertyConfigFileStateCellPresenter::ConstructDisplayWidget()
{
return SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(STextBlock)
.Text(LOCTEXT("SCC unconnected","SCC"))
];
}
功能分析:
- 目前显示一个固定的文本 "SCC"(Source Control 的缩写)
- 这表明版本控制集成的 UI 部分还处于早期阶段,尚未与实际的版本控制状态联动
4 功能使用示例编写
示例1:在自定义 Details 面板中集成 ConfigEditor
假设有一个自定义的 UObject 属性,希望在细节面板中提供一个按钮,点击后打开 ConfigEditor 查看该属性在所有配置层级中的值。
cpp
// MyCustomDetails.h
#pragma once
#include "CoreMinimal.h"
#include "IDetailCustomization.h"
class FMyCustomDetails : public IDetailCustomization
{
public:
static TSharedRef<IDetailCustomization> MakeInstance();
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
private:
// 点击"查看配置层级"按钮的回调
FReply OnViewConfigHierarchyClicked(FProperty* Property);
};
cpp
// MyCustomDetails.cpp
#include "MyCustomDetails.h"
#include "IConfigEditorModule.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Text/STextBlock.h"
TSharedRef<IDetailCustomization> FMyCustomDetails::MakeInstance()
{
return MakeShareable(new FMyCustomDetails);
}
void FMyCustomDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
// 获取要自定义的属性
TSharedPtr<IPropertyHandle> MyProperty = DetailBuilder.GetProperty(
GET_MEMBER_NAME_CHECKED(UMyObject, MyConfigProperty));
if (MyProperty.IsValid())
{
FProperty* RawProperty = MyProperty->GetProperty();
// 在属性行旁边添加一个按钮
DetailBuilder.EditCategory("MyCategory")
.AddCustomRow(MyProperty->GetPropertyDisplayName())
.NameContent()
[
MyProperty->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
MyProperty->CreatePropertyValueWidget()
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
[
SNew(SButton)
.Text(FText::FromString(TEXT("...")))
.ToolTipText(FText::FromString(TEXT("查看配置层级")))
.OnClicked_Lambda([this, RawProperty]()
{
return OnViewConfigHierarchyClicked(RawProperty);
})
]
];
}
}
FReply FMyCustomDetails::OnViewConfigHierarchyClicked(FProperty* Property)
{
if (Property)
{
// 获取 ConfigEditor 模块
IConfigEditorModule& ConfigEditor =
FModuleManager::LoadModuleChecked<IConfigEditorModule>("ConfigEditor");
// 通知 ConfigEditor 要查看的属性
ConfigEditor.CreateHierarchyEditor(Property);
// 打开 ConfigEditor 标签页
FGlobalTabmanager::Get()->TryInvokeTab(FName("ConfigEditor"));
}
return FReply::Handled();
}
示例2:注入自定义属性编辑控件
当需要为某种特殊类型的属性提供自定义编辑控件时,在 ConfigEditor 打开之前注入 Widget。
cpp
// 在某个自定义 Details 面板的 CustomizeDetails 中
void FMyConfigDetails::InjectValueWidgets(IDetailLayoutBuilder& DetailBuilder)
{
IConfigEditorModule& ConfigEditor =
FModuleManager::LoadModuleChecked<IConfigEditorModule>("ConfigEditor");
// 获取属性的配置文件路径(通常通过 GConfig 查询)
FString ConfigPath = FPaths::ConvertRelativePathToFull(
FPaths::ProjectConfigDir() + TEXT("DefaultGame.ini"));
// 创建一个自定义的编辑控件(比如一个带颜色预览的数值输入框)
TSharedPtr<SWidget> CustomWidget =
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SColorBlock)
.Color(FLinearColor::Red)
.Size(FVector2D(16.0f, 16.0f))
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SNumericEntryBox<float>)
.Value(1.0f)
];
// 注入到 ConfigEditor
ConfigEditor.AddExternalPropertyValueWidgetAndConfigPairing(
ConfigPath, CustomWidget);
}
示例3:从 C++ 代码中直接打开 ConfigEditor 查看任意属性
cpp
// 在某个编辑器的菜单命令中
void FMyEditorModule::OnOpenConfigHierarchy()
{
// 获取要查看的属性(例如 AActor::bReplicates)
FProperty* Property = AActor::StaticClass()->FindPropertyByName(
TEXT("bReplicates"));
if (Property)
{
IConfigEditorModule& ConfigEditor =
FModuleManager::LoadModuleChecked<IConfigEditorModule>("ConfigEditor");
ConfigEditor.CreateHierarchyEditor(Property);
FGlobalTabmanager::Get()->TryInvokeTab(FName("ConfigEditor"));
}
}
5 总结与最佳实践
5.1 核心要点
-
ConfigEditor 是一个"配置层级可视化工具",不是通用的配置文件编辑器。它的核心价值在于将属性在不同配置层级中的值以可视化的方式呈现出来,让开发者能够一目了然地看到配置的层级关系和覆盖情况。
-
Widget 注入机制是模块的精髓 。通过
AddExternalPropertyValueWidgetAndConfigPairing/GetValueWidgetForConfigProperty这一对方法,ConfigEditor 实现了对属性类型的完全解耦。无论属性是什么类型,只要有对应的编辑 Widget,就能在 ConfigEditor 中正常显示和编辑。 -
Nomad Tab 模式使得 ConfigEditor 可以独立于任何资产编辑器存在,用户可以随时打开它,查看任意属性的配置层级。这种设计非常适合"辅助工具"类功能的定位。
-
与 PropertyTable 系统的深度集成 。ConfigEditor 通过自定义列(
IPropertyTableCustomColumn)和单元格展示器(IPropertyTableCellPresenter)无缝融入 UE 的属性表格系统,保证了 UI 风格的一致性。
5.2 值得借鉴的设计模式
-
接口最小化 :
IConfigEditorModule只暴露三个方法,但足以支撑整个模块的功能。这种"窄接口"设计降低了模块间的耦合度,也让接口的语义更加清晰。 -
依赖倒置:ConfigEditor 不依赖具体的属性类型,而是通过 Widget 注入机制让外部提供编辑控件。这是一种典型的控制反转(IoC)模式。
-
对象复用 :
SConfigEditor::CreateDisplayObjectForSelectedTargetPlatform()中通过FindObject查找已存在的UConfigHierarchyPropertyView,避免重复创建数据对象。这种模式在 Editor 工具开发中非常实用。 -
延迟创建 :
CreateHierarchyEditor()只缓存属性引用,实际的 UI 创建在SpawnConfigEditorTab()中进行。这避免了在不需要时创建不必要的 UI 对象。
5.3 注意事项
-
TFieldPath优于裸FProperty*:在 Editor 工具中引用属性时,应使用TFieldPath<FProperty>而非裸指针。TFieldPath支持热重载,当 C++ 代码重新编译时不会产生悬空引用。 -
Transient 对象需要手动管理生命周期 :
UConfigHierarchyPropertyView通过AddToRoot()防止 GC,这意味着需要手动管理其生命周期。如果模块长时间运行,应注意在不需要时调用RemoveFromRoot()。 -
版本控制集成尚未完整:当前版本只通过文件系统判断可写性,如果项目使用了 Perforce 等版本控制系统,可能需要自行扩展这部分功能。
-
移除功能未完成 :
SConfigPropertyCell中的移除按钮始终隐藏,如果需要从配置文件中删除属性条目的功能,需要进一步完善。
5.4 模块文件结构参考
ConfigEditor/
├── ConfigEditor.Build.cs
├── Public/
│ ├── IConfigEditorModule.h
│ ├── ConfigPropertyHelper.h
│ └── PropertyVisualization/
│ ├── ConfigPropertyColumn.h
│ └── ConfigPropertyConfigFileStateColumn.h
└── Private/
├── ConfigEditorModule.h
├── ConfigEditorModule.cpp
├── ConfigPropertyHelper.cpp
├── SConfigEditor.h
├── SConfigEditor.cpp
├── STargetPlatformSelector.h
├── STargetPlatformSelector.cpp
└── PropertyVisualization/
├── ConfigPropertyCellPresenter.h
├── ConfigPropertyCellPresenter.cpp
├── ConfigPropertyColumn.cpp
└── ConfigPropertyConfigFileStateColumn.cpp
本文档基于 UE 5.5 引擎源码中的 ConfigEditor 模块编写,旨在帮助开发者理解该模块的架构设计、代码实现和使用方式。