docs: sync project browser docs
This commit is contained in:
44
docs/api/XCEngine/Editor/Core/EditorConsoleSink/Clear.md
Normal file
44
docs/api/XCEngine/Editor/Core/EditorConsoleSink/Clear.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# EditorConsoleSink::Clear
|
||||
|
||||
清空当前控制台缓冲区。
|
||||
|
||||
```cpp
|
||||
void Clear();
|
||||
```
|
||||
|
||||
## 当前行为
|
||||
|
||||
当前实现分两种情况:
|
||||
|
||||
### 缓冲区已经为空
|
||||
|
||||
- 直接返回
|
||||
- 不递增 `revision`
|
||||
- 不触发回调
|
||||
|
||||
### 缓冲区非空
|
||||
|
||||
1. 在锁内清空 `m_logs`
|
||||
2. 递增 `m_revision`
|
||||
3. 拷贝当前回调
|
||||
4. 解锁后执行回调
|
||||
|
||||
## 当前不会重置的状态
|
||||
|
||||
`Clear()` 不会重置:
|
||||
|
||||
- `m_nextSerial`
|
||||
- 已注册回调
|
||||
|
||||
所以清空之后再写入新日志时,新的 `serial` 会继续从上次编号往后增长。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 无。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [GetRevision](GetRevision.md)
|
||||
- [SetCallback](SetCallback.md)
|
||||
- [Log](Log.md)
|
||||
@@ -0,0 +1,35 @@
|
||||
# EditorConsoleSink::Constructor
|
||||
|
||||
构造一个编辑器控制台日志 sink,并把它登记为当前活动实例。
|
||||
|
||||
```cpp
|
||||
EditorConsoleSink();
|
||||
```
|
||||
|
||||
## 当前行为
|
||||
|
||||
当前构造函数本身没有复杂初始化逻辑,它只做一件关键的事:
|
||||
|
||||
- 把静态指针 `s_instance` 直接设为 `this`
|
||||
|
||||
对象其余状态使用头文件里的默认成员初始化:
|
||||
|
||||
- `m_logs` 为空
|
||||
- `m_callback` 为空
|
||||
- `m_nextSerial == 1`
|
||||
- `m_revision == 0`
|
||||
|
||||
## 设计含义
|
||||
|
||||
这意味着“最后构造出来的实例”会覆盖之前登记的活动实例。
|
||||
当前实现没有实例栈,也没有显式注册/反注册 API。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 无。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [GetInstance](GetInstance.md)
|
||||
- [Destructor](Destructor.md)
|
||||
@@ -0,0 +1,39 @@
|
||||
# EditorConsoleSink::Destructor
|
||||
|
||||
销毁控制台 sink,并在必要时撤销活动实例注册。
|
||||
|
||||
```cpp
|
||||
~EditorConsoleSink() override;
|
||||
```
|
||||
|
||||
## 当前行为
|
||||
|
||||
析构函数会检查:
|
||||
|
||||
```cpp
|
||||
s_instance == this
|
||||
```
|
||||
|
||||
只有当当前对象仍然是活动实例时,才会把 `s_instance` 置空。
|
||||
如果活动实例已经被其他后来构造的对象覆盖,析构当前对象不会再改全局指针。
|
||||
|
||||
## 析构后的可见行为
|
||||
|
||||
一旦活动实例被清空,后续 [GetInstance](GetInstance.md) 会直接返回 `nullptr`。
|
||||
如果当前对象在析构前已经被后来构造的实例覆盖,则析构不会影响那个更新的活动实例。
|
||||
|
||||
## 当前不做的事情
|
||||
|
||||
- 不会自动 `Flush()`
|
||||
- 不会转移现有日志
|
||||
- 不会通知回调“实例已销毁”
|
||||
|
||||
## 返回值
|
||||
|
||||
- 无。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [GetInstance](GetInstance.md)
|
||||
- [Constructor](Constructor.md)
|
||||
@@ -0,0 +1,45 @@
|
||||
# EditorConsoleRecord
|
||||
|
||||
**命名空间**: `XCEngine::Debug`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**源文件**: `editor/src/Core/EditorConsoleSink.h`
|
||||
|
||||
**描述**: `EditorConsoleSink` 内部使用的控制台记录快照,给每条日志附加一个单调递增的 `serial`。
|
||||
|
||||
## 概述
|
||||
|
||||
`EditorConsoleRecord` 是 `LogEntry` 的 Editor 侧包装层。
|
||||
它解决的问题不是“如何描述一条日志”,而是“如何在内存缓冲区里稳定地区分和追踪每一条日志”。
|
||||
|
||||
相比裸 `LogEntry`,它新增了一个 `serial` 字段,用来表达:
|
||||
|
||||
- 这条日志在当前 sink 实例中的追加顺序
|
||||
- Console 列表选中项的稳定身份
|
||||
- `Error Pause` 扫描“上次检查之后新增日志”的边界
|
||||
|
||||
## 字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `serial` | 当前 sink 实例内单调递增的记录编号。 |
|
||||
| `entry` | 原始日志条目副本。 |
|
||||
|
||||
## serial 语义
|
||||
|
||||
- `serial` 从 `1` 开始递增。
|
||||
- [Clear](Clear.md) 不会把它重置回 `1`。
|
||||
- 它只在当前进程、当前 sink 生命周期内有意义。
|
||||
- 它不是时间戳,也不是可持久化的稳定 ID。
|
||||
|
||||
## 当前使用位置
|
||||
|
||||
- [ConsolePanel](../../panels/ConsolePanel/ConsolePanel.md) 用它保持选择、做键盘导航和双击定位。
|
||||
- `Error Pause` 流程用它记录“最近已经扫描到哪一条”。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [EditorConsoleSink](EditorConsoleSink.md)
|
||||
- [Log](Log.md)
|
||||
- [GetRecords](GetRecords.md)
|
||||
@@ -6,37 +6,91 @@
|
||||
|
||||
**源文件**: `editor/src/Core/EditorConsoleSink.h`
|
||||
|
||||
**描述**: 编辑器专用日志 sink,把引擎日志桥接到 Console 面板可读取的内存缓冲区。
|
||||
**描述**: 编辑器专用日志 sink,把 `Logger` 的输出桥接成带 `serial` / `revision` 的内存快照,供 `ConsolePanel` 增量消费。
|
||||
|
||||
## 概述
|
||||
|
||||
`EditorConsoleSink` 虽然位于 `editor/src/Core`,但命名空间属于 `XCEngine::Debug`。
|
||||
它的定位很明确:作为 `Logger` 的一个输出目标,把日志保存在内存中,供 Console 面板显示。
|
||||
`EditorConsoleSink` 虽然位于 `editor/src/Core`,但命名空间仍属于 `XCEngine::Debug`。
|
||||
它不是单纯“把日志塞进数组”的轻量容器,而是当前 Editor Console 数据流的中心桥接层,负责三件事:
|
||||
|
||||
## 当前实现
|
||||
1. 作为 `ILogSink` 接收引擎日志。
|
||||
2. 把日志保存成带 [EditorConsoleRecord](EditorConsoleRecord.md) 的内存快照。
|
||||
3. 通过 `serial`、`revision` 和变更回调让 [ConsolePanel](../../panels/ConsolePanel/ConsolePanel.md) 可以做增量刷新、错误暂停扫描和选择保持。
|
||||
|
||||
- 继承自 `ILogSink`
|
||||
- 通过 `std::mutex` 保护 `m_logs`
|
||||
- `Log()` 会把新日志追加到数组尾部
|
||||
- 最大缓存条数固定为 `1000`
|
||||
- 超限时直接丢弃最早的一条日志
|
||||
- `GetLogs()` 返回日志数组的拷贝
|
||||
- `GetInstance()` 在没有显式注册实例时会返回一个静态 fallback 实例
|
||||
## 当前数据模型
|
||||
|
||||
## 设计说明
|
||||
### 日志记录
|
||||
|
||||
这是一个典型的“工具层 sink”。
|
||||
引擎日志系统不应该知道编辑器面板怎么画 UI,但编辑器又确实需要消费同一份日志流。把桥接能力做成一个 `ILogSink` 子类,是最干净的集成方式。
|
||||
内部缓冲区 `m_logs` 保存的是 `EditorConsoleRecord`,而不是裸 `LogEntry`:
|
||||
|
||||
## 当前限制
|
||||
- `serial` 是 sink 内单调递增的记录编号。
|
||||
- `entry` 是完整日志条目副本。
|
||||
|
||||
- 当前只保留最近 1000 条日志
|
||||
- `GetLogs()` 会复制整个缓冲区
|
||||
- `SetCallback()` 没有更复杂的线程调度机制,回调触发策略非常轻量
|
||||
`ConsolePanel` 当前依赖这个 `serial` 做:
|
||||
|
||||
- 新增错误扫描边界
|
||||
- 列表选中保持
|
||||
- 折叠/搜索后重新定位选中项
|
||||
|
||||
### revision
|
||||
|
||||
`m_revision` 是比 `serial` 更粗粒度的“内容变更版本号”:
|
||||
|
||||
- 每次 [Log](Log.md) 成功追加日志时加一
|
||||
- 每次 [Clear](Clear.md) 真正清空非空缓冲区时加一
|
||||
- [SetCallback](SetCallback.md) 和 [Flush](Flush.md) 不会改它
|
||||
|
||||
它的用途是让 UI 先判断“有没有变化”,再决定是否重建可见行。
|
||||
|
||||
## 当前生命周期
|
||||
|
||||
- [Constructor](Constructor.md) 会把当前对象登记为全局活动实例。
|
||||
- [Destructor](Destructor.md) 只在自己仍是活动实例时清掉全局指针。
|
||||
- [GetInstance](GetInstance.md) 只返回当前登记的活动实例;若当前没有活动 sink,则返回 `nullptr`。
|
||||
|
||||
这意味着当前接口层面上 `GetInstance()` 可能返回空指针。调用方需要像 [ConsolePanel](../../panels/ConsolePanel/ConsolePanel.md) 那样先判空,再进入日志读取与 UI 刷新流程。
|
||||
|
||||
## 线程语义
|
||||
|
||||
- `m_mutex` 保护 `m_logs`、`m_callback`、`m_nextSerial` 和 `m_revision`。
|
||||
- [GetLogs](GetLogs.md)、[GetRecords](GetRecords.md)、[GetRevision](GetRevision.md) 都返回锁内拍下的快照,不暴露内部引用。
|
||||
- [Log](Log.md) 和 [Clear](Clear.md) 会先在锁内取出当前回调,再在解锁后执行回调。
|
||||
|
||||
最后一点很关键:
|
||||
当前回调不是调度到 UI 线程,而是谁触发 `Log()` / `Clear()`,谁就在自己的调用线程上同步执行回调。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 最大缓存条数固定为 `1000`,超限时直接丢弃最早一条。
|
||||
- `GetLogs()` / `GetRecords()` 都会复制整份缓冲区,不是零拷贝视图。
|
||||
- [Flush](Flush.md) 当前是空实现,只是满足 `ILogSink` 契约。
|
||||
- [SetCallback](SetCallback.md) 只保存一个回调,后设置的会覆盖先前的。
|
||||
- [Clear](Clear.md) 不会重置 `m_nextSerial`,所以清空后新日志仍继续增长编号。
|
||||
|
||||
## 公开方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| [GetInstance](GetInstance.md) | 获取当前活动 sink;若没有已注册实例则返回 `nullptr`。 |
|
||||
| [Constructor](Constructor.md) | 构造一个可注册为活动实例的编辑器日志 sink。 |
|
||||
| [Destructor](Destructor.md) | 在销毁时撤销活动实例注册。 |
|
||||
| [Log](Log.md) | 追加一条日志并在需要时通知观察者。 |
|
||||
| [Flush](Flush.md) | 满足 `ILogSink` 接口的空刷新函数。 |
|
||||
| [GetLogs](GetLogs.md) | 返回不含 `serial` 的 `LogEntry` 快照。 |
|
||||
| [GetRecords](GetRecords.md) | 返回带 `serial` 的完整控制台记录快照。 |
|
||||
| [GetRevision](GetRevision.md) | 返回当前缓冲区的变更版本号。 |
|
||||
| [Clear](Clear.md) | 清空当前缓冲区并发布一次内容变化。 |
|
||||
| [SetCallback](SetCallback.md) | 注册或替换一条内容变更回调。 |
|
||||
|
||||
## 相关类型
|
||||
|
||||
- [EditorConsoleRecord](EditorConsoleRecord.md)
|
||||
- [LogEntry](../../../Debug/LogEntry/LogEntry.md)
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Core](../Core.md)
|
||||
- [ConsoleActionRouter](../../Actions/ConsoleActionRouter/ConsoleActionRouter.md)
|
||||
- [ConsolePanel](../../panels/ConsolePanel/ConsolePanel.md)
|
||||
- [Debug::Logger](../../../Debug/Logger/Logger.md)
|
||||
- [ConsoleFilterState](../../UI/ConsoleFilterState/ConsoleFilterState.md)
|
||||
- [ILogSink](../../../Debug/ILogSink/ILogSink.md)
|
||||
- [Logger](../../../Debug/Logger/Logger.md)
|
||||
|
||||
30
docs/api/XCEngine/Editor/Core/EditorConsoleSink/Flush.md
Normal file
30
docs/api/XCEngine/Editor/Core/EditorConsoleSink/Flush.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# EditorConsoleSink::Flush
|
||||
|
||||
满足 `ILogSink` 接口的刷新函数。
|
||||
|
||||
```cpp
|
||||
void Flush() override;
|
||||
```
|
||||
|
||||
## 当前行为
|
||||
|
||||
当前实现为空函数,不执行任何操作。
|
||||
|
||||
## 原因
|
||||
|
||||
`EditorConsoleSink` 只维护内存缓冲区,没有:
|
||||
|
||||
- 文件句柄
|
||||
- socket
|
||||
- 延迟写盘缓存
|
||||
|
||||
所以它不需要像文件 sink 那样做显式落地。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 无。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [ILogSink](../../../Debug/ILogSink/ILogSink.md)
|
||||
@@ -0,0 +1,47 @@
|
||||
# EditorConsoleSink::GetInstance
|
||||
|
||||
获取当前活动的编辑器控制台 sink。
|
||||
|
||||
```cpp
|
||||
static EditorConsoleSink* GetInstance();
|
||||
```
|
||||
|
||||
## 当前行为
|
||||
|
||||
当前实现没有兜底实例,也不会在这里隐式构造对象。它只做一件事:
|
||||
|
||||
1. 直接返回静态指针 `s_instance` 的当前值。
|
||||
|
||||
因此:
|
||||
|
||||
- 若已有活动 sink 被构造并登记,返回那个实例。
|
||||
- 若当前没有活动 sink,直接返回 `nullptr`。
|
||||
|
||||
## 实例来源
|
||||
|
||||
活动实例的登记与撤销完全由生命周期驱动:
|
||||
|
||||
- [Constructor](Constructor.md) 会把 `s_instance` 设为 `this`
|
||||
- [Destructor](Destructor.md) 会在 `s_instance == this` 时把它清空
|
||||
|
||||
该方法本身不负责:
|
||||
|
||||
- 创建 fallback sink
|
||||
- 维持实例栈
|
||||
- 区分“正式实例”和“临时实例”
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 返回的是原始指针,不表达所有权。
|
||||
- 调用方必须处理 `nullptr`,不能假设 Editor 全程都已注册控制台 sink。
|
||||
- 如果较晚构造的新实例覆盖了旧实例,后续调用会直接观察到新的 `s_instance`。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 当前活动实例;如果没有已注册实例,则返回 `nullptr`。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [Constructor](Constructor.md)
|
||||
- [Destructor](Destructor.md)
|
||||
44
docs/api/XCEngine/Editor/Core/EditorConsoleSink/GetLogs.md
Normal file
44
docs/api/XCEngine/Editor/Core/EditorConsoleSink/GetLogs.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# EditorConsoleSink::GetLogs
|
||||
|
||||
返回不含 `serial` 的日志条目快照。
|
||||
|
||||
```cpp
|
||||
std::vector<LogEntry> GetLogs() const;
|
||||
```
|
||||
|
||||
## 当前行为
|
||||
|
||||
当前实现会:
|
||||
|
||||
1. 在锁内读取 `m_logs`
|
||||
2. 新建一个 `std::vector<LogEntry>`
|
||||
3. 把每条 `EditorConsoleRecord::entry` 复制进去
|
||||
4. 返回这份副本
|
||||
|
||||
## 与 GetRecords() 的区别
|
||||
|
||||
- [GetLogs](GetLogs.md) 只返回 `LogEntry`
|
||||
- [GetRecords](GetRecords.md) 保留 `serial`
|
||||
|
||||
如果调用方需要做:
|
||||
|
||||
- 增量刷新
|
||||
- 选中保持
|
||||
- 错误扫描边界
|
||||
|
||||
应优先使用 `GetRecords()`。
|
||||
|
||||
## 成本与限制
|
||||
|
||||
- 返回值是整份副本,不是视图。
|
||||
- 当前调用一次就会复制当前全部缓冲区。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 当前日志缓冲区的 `LogEntry` 副本数组。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [GetRecords](GetRecords.md)
|
||||
- [Log](Log.md)
|
||||
@@ -0,0 +1,41 @@
|
||||
# EditorConsoleSink::GetRecords
|
||||
|
||||
返回带 `serial` 的控制台记录快照。
|
||||
|
||||
```cpp
|
||||
std::vector<EditorConsoleRecord> GetRecords() const;
|
||||
```
|
||||
|
||||
## 当前行为
|
||||
|
||||
当前实现非常直接:
|
||||
|
||||
1. 在锁内复制 `m_logs`
|
||||
2. 返回整份 `std::vector<EditorConsoleRecord>` 副本
|
||||
|
||||
这也是 [ConsolePanel](../../panels/ConsolePanel/ConsolePanel.md) 当前的主要读取接口。
|
||||
|
||||
## 为什么需要它
|
||||
|
||||
相比 [GetLogs](GetLogs.md),这里保留了 `serial`。
|
||||
这让调用方可以知道:
|
||||
|
||||
- 哪些日志是“新追加”的
|
||||
- 当前选中项在过滤/折叠后对应哪条记录
|
||||
- `Error Pause` 应该从哪条记录之后开始扫描
|
||||
|
||||
## 快照语义
|
||||
|
||||
- 返回的是调用时刻的拷贝。
|
||||
- 后续新的 `Log()` 或 `Clear()` 不会回写到已经拿到的返回值。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 当前控制台缓冲区的完整 `EditorConsoleRecord` 副本数组。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [EditorConsoleRecord](EditorConsoleRecord.md)
|
||||
- [GetLogs](GetLogs.md)
|
||||
- [GetRevision](GetRevision.md)
|
||||
@@ -0,0 +1,40 @@
|
||||
# EditorConsoleSink::GetRevision
|
||||
|
||||
返回当前控制台缓冲区的变更版本号。
|
||||
|
||||
```cpp
|
||||
uint64_t GetRevision() const;
|
||||
```
|
||||
|
||||
## 当前行为
|
||||
|
||||
该方法会在锁内读取并返回 `m_revision`。
|
||||
|
||||
## revision 递增规则
|
||||
|
||||
当前实现里,`revision` 只在两类内容变化时递增:
|
||||
|
||||
- [Log](Log.md) 追加一条日志
|
||||
- [Clear](Clear.md) 真正清空一个非空缓冲区
|
||||
|
||||
以下操作不会改它:
|
||||
|
||||
- [SetCallback](SetCallback.md)
|
||||
- [Flush](Flush.md)
|
||||
- 对空缓冲区调用 [Clear](Clear.md)
|
||||
|
||||
## 典型用途
|
||||
|
||||
UI 层通常先比较 `revision`,只有检测到变化时才重建过滤结果或决定是否滚动到底部。
|
||||
这比每帧都深比较整份日志数组更便宜。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 当前缓冲区版本号。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [Log](Log.md)
|
||||
- [Clear](Clear.md)
|
||||
- [GetRecords](GetRecords.md)
|
||||
45
docs/api/XCEngine/Editor/Core/EditorConsoleSink/Log.md
Normal file
45
docs/api/XCEngine/Editor/Core/EditorConsoleSink/Log.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# EditorConsoleSink::Log
|
||||
|
||||
追加一条日志到控制台缓冲区,并在需要时通知观察者。
|
||||
|
||||
```cpp
|
||||
void Log(const LogEntry& entry) override;
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
- `entry` - 待写入控制台缓冲区的日志条目。
|
||||
|
||||
## 当前行为
|
||||
|
||||
当前实现流程是:
|
||||
|
||||
1. 进入互斥区。
|
||||
2. 如果缓冲区已达到 `MAX_LOGS == 1000`,先删除最早的一条。
|
||||
3. 以当前 `m_nextSerial` 构造一条 [EditorConsoleRecord](EditorConsoleRecord.md)。
|
||||
4. 递增 `m_nextSerial`。
|
||||
5. 递增 `m_revision`。
|
||||
6. 拷贝当前回调到局部变量。
|
||||
7. 离开互斥区后执行回调。
|
||||
|
||||
## serial 与裁剪语义
|
||||
|
||||
- 新日志总是追加到尾部。
|
||||
- 超限裁剪只丢弃最老记录,不影响新记录的 `serial` 递增。
|
||||
- 即使缓冲区被多次清空,后续日志的 `serial` 仍然继续增长。
|
||||
|
||||
## 回调线程语义
|
||||
|
||||
回调不是异步投递,而是由 `Log()` 的调用线程同步执行。
|
||||
当前实现之所以先复制回调、再解锁后调用,是为了避免回调内部再次访问 sink 时发生重入锁问题。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 无。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [EditorConsoleRecord](EditorConsoleRecord.md)
|
||||
- [GetRevision](GetRevision.md)
|
||||
- [SetCallback](SetCallback.md)
|
||||
@@ -0,0 +1,46 @@
|
||||
# EditorConsoleSink::SetCallback
|
||||
|
||||
注册或替换一条“控制台内容变化”回调。
|
||||
|
||||
```cpp
|
||||
void SetCallback(std::function<void()> callback);
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
- `callback` - 新的通知回调;可以为空。
|
||||
|
||||
## 当前行为
|
||||
|
||||
当前实现只是在锁内执行:
|
||||
|
||||
- `m_callback = std::move(callback)`
|
||||
|
||||
它不会:
|
||||
|
||||
- 立即触发新回调
|
||||
- 保留旧回调形成链式通知
|
||||
- 做线程切换或延迟调度
|
||||
|
||||
## 触发时机
|
||||
|
||||
新回调会在以下操作后被调用:
|
||||
|
||||
- [Log](Log.md) 成功追加日志
|
||||
- [Clear](Clear.md) 真正清空非空缓冲区
|
||||
|
||||
并且都是在对应调用线程上、解锁之后同步执行。
|
||||
|
||||
## 使用建议
|
||||
|
||||
如果回调里要刷新 UI 或触发更上层逻辑,调用方应自己保证线程上下文安全,不要假设这里已经切到主线程。
|
||||
|
||||
## 返回值
|
||||
|
||||
- 无。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [返回类型总览](EditorConsoleSink.md)
|
||||
- [Log](Log.md)
|
||||
- [Clear](Clear.md)
|
||||
@@ -0,0 +1,58 @@
|
||||
# IProjectManager / Current Items And Selection
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `interface-group`
|
||||
|
||||
**源文件**: `editor/src/Core/IProjectManager.h`
|
||||
|
||||
## 相关签名
|
||||
|
||||
```cpp
|
||||
virtual const std::vector<AssetItemPtr>& GetCurrentItems() const = 0;
|
||||
virtual AssetItemPtr GetRootFolder() const = 0;
|
||||
virtual AssetItemPtr GetCurrentFolder() const = 0;
|
||||
|
||||
virtual AssetItemPtr GetSelectedItem() const = 0;
|
||||
virtual const std::string& GetSelectedItemPath() const = 0;
|
||||
virtual int GetSelectedIndex() const = 0;
|
||||
virtual void SetSelectedIndex(int index) = 0;
|
||||
virtual void SetSelectedItem(const AssetItemPtr& item) = 0;
|
||||
virtual void ClearSelection() = 0;
|
||||
virtual int FindCurrentItemIndex(const std::string& fullPath) const = 0;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
这一组接口定义的是“当前正在浏览的目录里有什么,以及当前选中了什么”。
|
||||
|
||||
调用方通常用它们驱动:
|
||||
|
||||
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md) 的目录内容绘制
|
||||
- 资源卡片选中高亮
|
||||
- 上下文菜单目标同步
|
||||
- 基于当前目录的资源创建、删除、重命名和拖放移动
|
||||
|
||||
## 语义说明
|
||||
|
||||
- `GetCurrentItems()` 返回当前目录直接子项,不是全项目递归结果。
|
||||
- `GetRootFolder()` 与 `GetCurrentFolder()` 都是 `AssetItem` 视图节点,而不是磁盘句柄。
|
||||
- `GetSelectedItemPath()` 提供稳定的路径标识,便于刷新后重新解析当前选择。
|
||||
- `GetSelectedIndex()` / `SetSelectedIndex()` 以“当前目录列表索引”为坐标,不跨目录。
|
||||
- `FindCurrentItemIndex(fullPath)` 只在当前目录中找,不做全树搜索。
|
||||
|
||||
## 当前默认实现
|
||||
|
||||
在 [ProjectManager](../../Managers/ProjectManager/ProjectManager.md) 中:
|
||||
|
||||
- 选择状态按 `m_selectedItemPath` 保存,而不是长期持有旧 `AssetItemPtr`
|
||||
- `GetSelectedItem()` 会在当前目录里按路径重新解析
|
||||
- `SetSelectedIndex()` 越界时会清空选择
|
||||
- `RefreshCurrentFolder()` 后若旧路径仍在当前目录内,选择可被恢复
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [IProjectManager](IProjectManager.md)
|
||||
- [Navigation And Path](Navigation-And-Path.md)
|
||||
- [ProjectManager](../../Managers/ProjectManager/ProjectManager.md)
|
||||
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)
|
||||
@@ -0,0 +1,54 @@
|
||||
# IProjectManager / File Operations
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `interface-group`
|
||||
|
||||
**源文件**: `editor/src/Core/IProjectManager.h`
|
||||
|
||||
## 相关签名
|
||||
|
||||
```cpp
|
||||
virtual AssetItemPtr CreateFolder(const std::string& name) = 0;
|
||||
virtual bool DeleteItem(const std::string& fullPath) = 0;
|
||||
virtual bool MoveItem(const std::string& sourceFullPath, const std::string& destFolderFullPath) = 0;
|
||||
virtual bool RenameItem(const std::string& sourceFullPath, const std::string& newName) = 0;
|
||||
|
||||
virtual const std::string& GetProjectPath() const = 0;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
这一组接口定义的是项目浏览器层面的基础资源改动能力。
|
||||
|
||||
它们负责:
|
||||
|
||||
- 在当前目录下创建文件夹
|
||||
- 删除某个资源或目录
|
||||
- 把资源移动到目标目录
|
||||
- 重命名资源或目录
|
||||
- 暴露项目根路径,供命令层生成相对路径、项目描述文件和 fallback 场景路径
|
||||
|
||||
## 参数与返回值
|
||||
|
||||
- `CreateFolder(name)` 返回新建目录对应的 `AssetItemPtr`,失败时返回 `nullptr`
|
||||
- `DeleteItem(...)` / `MoveItem(...)` / `RenameItem(...)` 以布尔值表示成功或失败
|
||||
- `MoveItem(...)` 的目标参数是“目标目录完整路径”,不是目标文件完整路径
|
||||
- `GetProjectPath()` 返回项目根目录;它和 `GetCurrentPath()` 的含义不同
|
||||
|
||||
## 当前默认实现
|
||||
|
||||
在 [ProjectManager](../../Managers/ProjectManager/ProjectManager.md) 中,这组操作还承担了若干实际约束:
|
||||
|
||||
- 名称会先 trim,并拒绝 Windows 非法文件名
|
||||
- 所有改动必须位于当前项目 `Assets` 根目录之内
|
||||
- 根目录本身不能被删除、移动或重命名
|
||||
- `.meta` sidecar 会在删除、移动、重命名时跟随同步
|
||||
- 完成后会刷新目录树,并尽量保持当前路径与选择一致
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [IProjectManager](IProjectManager.md)
|
||||
- [Initialization And Refresh](Initialization-And-Refresh.md)
|
||||
- [ProjectCommands](../../Commands/ProjectCommands/ProjectCommands.md)
|
||||
- [ProjectManager](../../Managers/ProjectManager/ProjectManager.md)
|
||||
@@ -6,36 +6,82 @@
|
||||
|
||||
**源文件**: `editor/src/Core/IProjectManager.h`
|
||||
|
||||
**描述**: 定义项目浏览器接口,负责当前资产列表、路径导航、文件夹刷新以及基础资源文件操作。
|
||||
**描述**: 定义 editor 项目工作流的抽象接口,负责当前资产列表、路径导航、选择状态,以及项目 `Assets` 树上的基础文件操作契约。
|
||||
|
||||
## 概述
|
||||
|
||||
`IProjectManager` 面向的是编辑器 Project 面板这一类工作流。
|
||||
`IProjectManager` 面向的不只是 `ProjectPanel` 的目录浏览,它还是 editor 侧项目资源模型的抽象接口。
|
||||
|
||||
它的接口组合非常清晰:
|
||||
按当前头文件,它的接口可以分成五组:
|
||||
|
||||
- 当前目录的资产项列表
|
||||
- 当前选中项索引
|
||||
- 当前选中项与选中路径
|
||||
- 路径导航
|
||||
- 项目根路径
|
||||
- 文件夹刷新/创建/删除/移动
|
||||
- 项目根路径与目录刷新
|
||||
- 创建 / 删除 / 移动 / 重命名
|
||||
|
||||
因此它是当前这条链路里的中间抽象层:
|
||||
|
||||
```text
|
||||
MainMenuActionRouter / ProjectPanel
|
||||
-> ProjectCommands
|
||||
-> IProjectManager
|
||||
-> ProjectManager
|
||||
```
|
||||
|
||||
菜单层和面板层都不直接操作文件系统细节,而是通过这个接口进入项目模型与文件操作。
|
||||
|
||||
## 核心接口
|
||||
|
||||
| 方法 | 作用 |
|
||||
|------|------|
|
||||
| `GetCurrentItems()` | 获取当前目录条目。 |
|
||||
| `GetSelectedItem()` / `GetSelectedItemPath()` | 获取当前选中资源及其路径。 |
|
||||
| `GetSelectedIndex()` / `SetSelectedIndex()` | 管理当前选中项索引。 |
|
||||
| `SetSelectedItem()` / `ClearSelection()` | 直接管理当前选择。 |
|
||||
| `NavigateToFolder()` / `NavigateBack()` / `NavigateToIndex()` | 处理路径导航。 |
|
||||
| `CanNavigateBack()` | 判断能否返回上一级。 |
|
||||
| `GetCurrentPath()` / `GetPathDepth()` / `GetPathName()` | 查询当前路径面包屑。 |
|
||||
| `Initialize()` | 初始化项目目录。 |
|
||||
| `RefreshCurrentFolder()` | 刷新当前目录内容。 |
|
||||
| `CreateFolder()` / `DeleteItem()` / `MoveItem()` | 执行基础文件操作。 |
|
||||
| `CreateFolder()` / `DeleteItem()` / `MoveItem()` / `RenameItem()` | 执行基础文件操作。 |
|
||||
| `GetProjectPath()` | 获取项目根目录。 |
|
||||
|
||||
## 边界与职责
|
||||
|
||||
`IProjectManager` 当前明确不负责:
|
||||
|
||||
- 文件对话框
|
||||
- 菜单文本或快捷键
|
||||
- 资产数据库导入 / artifact 构建
|
||||
- 项目级脚本构建
|
||||
- 项目保存与项目切换策略
|
||||
|
||||
它负责的是同步项目模型与维护动作契约,也就是:
|
||||
|
||||
- UI 层能看到什么
|
||||
- 基础文件操作怎么做
|
||||
- 当前项目根目录和 `Assets` 浏览树如何对外暴露
|
||||
|
||||
这也是当前接口设计上的一个取舍:
|
||||
|
||||
- 资源浏览和基础文件操作都通过同步返回值表达结果
|
||||
- 更高层的项目保存、脚本构建和项目切换仍留给 [ProjectCommands](../../Commands/ProjectCommands/ProjectCommands.md) 与 `Application`
|
||||
- 界面层如何呈现这些结果,仍由上层自己决定
|
||||
|
||||
## 设计说明
|
||||
|
||||
把 `ProjectPanel` 依赖的目录模型、导航状态和文件操作抽象成接口,有两个直接收益:
|
||||
|
||||
- UI 层可以围绕 `AssetItem` 模型稳定开发,而不必直接接触文件系统细节
|
||||
- 更高层的项目命令层可以只依赖抽象,而不耦合到具体实现
|
||||
|
||||
这也是为什么 `IProjectManager` 的当前职责比较克制:它更像“项目资源浏览模型接口”,而不是完整的项目系统门面。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Core](../Core.md)
|
||||
- [AssetItem](../AssetItem/AssetItem.md)
|
||||
- [ProjectCommands](../../Commands/ProjectCommands/ProjectCommands.md)
|
||||
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)
|
||||
- [ProjectManager](../../Managers/ProjectManager/ProjectManager.md)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# IProjectManager / Initialization And Refresh
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `interface-group`
|
||||
|
||||
**源文件**: `editor/src/Core/IProjectManager.h`
|
||||
|
||||
## 相关签名
|
||||
|
||||
```cpp
|
||||
virtual void Initialize(const std::string& projectPath) = 0;
|
||||
virtual void RefreshCurrentFolder() = 0;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
这一组接口负责把 manager 绑定到某个项目根目录,并在外部文件系统状态变化后刷新当前浏览视图。
|
||||
|
||||
它们通常由以下工作流触发:
|
||||
|
||||
- [ProjectPanel::Initialize](../../panels/ProjectPanel/Initialize.md)
|
||||
- [ProjectCommands::SwitchProject](../../Commands/ProjectCommands/ProjectCommands.md)
|
||||
- `SaveProject(...)`、`RebuildScriptAssemblies(...)` 这类可能影响项目目录状态的命令
|
||||
|
||||
## 语义说明
|
||||
|
||||
- `Initialize(projectPath)` 把 manager 切到新的项目上下文。
|
||||
- `RefreshCurrentFolder()` 重建当前项目资产树的可见部分。
|
||||
- 这两者都只负责项目浏览模型,不负责文件对话框、场景切换确认或撤销历史重置。
|
||||
|
||||
## 当前默认实现
|
||||
|
||||
在 [ProjectManager](../../Managers/ProjectManager/ProjectManager.md) 中:
|
||||
|
||||
- `Initialize(...)` 会把 `<project>/Assets` 作为根目录,并确保最小的 `Assets/Scenes` 结构存在
|
||||
- `RefreshCurrentFolder()` 会重建目录树,同时尽量保留当前路径和当前选择
|
||||
- 若扫描或文件系统操作抛异常,默认实现倾向于保留最小可工作的空根目录,而不是把异常继续上抛
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [IProjectManager](IProjectManager.md)
|
||||
- [File Operations](File-Operations.md)
|
||||
- [ProjectCommands](../../Commands/ProjectCommands/ProjectCommands.md)
|
||||
- [ProjectManager](../../Managers/ProjectManager/ProjectManager.md)
|
||||
@@ -0,0 +1,54 @@
|
||||
# IProjectManager / Navigation And Path
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `interface-group`
|
||||
|
||||
**源文件**: `editor/src/Core/IProjectManager.h`
|
||||
|
||||
## 相关签名
|
||||
|
||||
```cpp
|
||||
virtual void NavigateToFolder(const AssetItemPtr& folder) = 0;
|
||||
virtual void NavigateBack() = 0;
|
||||
virtual void NavigateToIndex(size_t index) = 0;
|
||||
virtual bool CanNavigateBack() const = 0;
|
||||
|
||||
virtual std::string GetCurrentPath() const = 0;
|
||||
virtual size_t GetPathDepth() const = 0;
|
||||
virtual std::string GetPathName(size_t index) const = 0;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
这一组接口把项目浏览路径暴露成了一个显式的面包屑模型,而不只是“当前目录指针”。
|
||||
|
||||
上层通常用它们驱动:
|
||||
|
||||
- 左侧目录树点击导航
|
||||
- 右侧 breadcrumb 点击回跳
|
||||
- “是否还能返回上级目录”的按钮或状态判断
|
||||
|
||||
## 路径模型
|
||||
|
||||
- `NavigateToFolder(...)` 直接跳到某个目录节点。
|
||||
- `NavigateBack()` 回退一级。
|
||||
- `NavigateToIndex(index)` 以 breadcrumb 层级为单位回跳。
|
||||
- `GetPathDepth()` / `GetPathName(index)` 提供 breadcrumb 数据源。
|
||||
- `GetCurrentPath()` 返回逻辑浏览路径,而不是磁盘绝对路径。
|
||||
|
||||
## 当前默认实现
|
||||
|
||||
在 [ProjectManager](../../Managers/ProjectManager/ProjectManager.md) 中:
|
||||
|
||||
- 路径栈由 `m_path` 维护
|
||||
- `GetCurrentPath()` 始终从 `"Assets"` 开始拼接
|
||||
- `CanNavigateBack()` 只在 `m_path.size() > 1` 时返回 `true`
|
||||
- 导航发生后会清空当前资源选择,避免旧目录索引泄漏到新目录
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [IProjectManager](IProjectManager.md)
|
||||
- [Current Items And Selection](Current-Items-And-Selection.md)
|
||||
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)
|
||||
- [ProjectManager](../../Managers/ProjectManager/ProjectManager.md)
|
||||
@@ -0,0 +1,68 @@
|
||||
# ProjectRootResolver
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `header-helper`
|
||||
|
||||
**源文件**: `editor/src/Core/ProjectRootResolver.h`
|
||||
|
||||
**描述**: 提供编辑器启动时的项目根目录解析和工作目录切换 helper。
|
||||
|
||||
## 概述
|
||||
|
||||
`ProjectRootResolver.h` 解决的是“编辑器现在应该把哪个目录视为当前项目根”的问题。
|
||||
|
||||
当前 [Application](../../Application/Application.md) 会在启动时调用这里的 helper,尽量把项目定位到:
|
||||
|
||||
- 命令行显式指定的路径
|
||||
- 当前工作区下的默认 `project/`
|
||||
- 或者最后回退到工作目录 / 可执行文件目录
|
||||
|
||||
## 当前解析顺序
|
||||
|
||||
`ResolveEditorProjectRootUtf8()` 的顺序是:
|
||||
|
||||
1. 读取当前工作目录与可执行目录。
|
||||
2. 先解析命令行 `--project` / `-p` 覆盖项。
|
||||
3. 向上查找工作区根目录。
|
||||
4. 若找到工作区,则优先使用 `<workspace>/project`。
|
||||
5. 若仍找不到,再尝试从可执行目录向上查找工作区。
|
||||
6. 最后回退到工作目录,若工作目录为空则回退到可执行目录。
|
||||
|
||||
## 工作区与项目判定
|
||||
|
||||
内部 helper 当前使用的规则很直接:
|
||||
|
||||
- `IsWorkspaceRoot(candidate)`
|
||||
- 需要同时存在 `CMakeLists.txt`
|
||||
- 需要有 `editor/` 与 `engine/` 目录
|
||||
- `IsEditorProjectRoot(candidate)`
|
||||
- 只要存在 `Project.xcproject`,或存在 `Assets/` 目录
|
||||
|
||||
这说明它不是通用项目发现器,而是明显面向当前仓库结构的启动辅助逻辑。
|
||||
|
||||
## 默认项目策略
|
||||
|
||||
`ResolveWorkspaceDefaultProjectRoot()` 当前优先返回:
|
||||
|
||||
- `<workspace>/project`
|
||||
|
||||
如果该目录本身已经符合项目根判定,就直接使用它;否则才尝试把工作区根本身当作项目根。
|
||||
|
||||
## 工作目录切换
|
||||
|
||||
`SetEditorWorkingDirectory(projectRootUtf8)` 会把进程当前工作目录切换到解析出来的项目根,并返回是否成功。
|
||||
|
||||
这保证了后续相对路径、资源加载和项目输出目录尽量围绕项目根工作,而不是围绕可执行文件目录。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 当前实现明显偏 Windows 桌面路径和当前仓库布局,不是跨平台项目定位库。
|
||||
- 命令行解析只识别 `--project` / `-p` 两种形式。
|
||||
- `FindWorkspaceRoot()` 依赖向上查找 `CMakeLists.txt + editor + engine` 组合,不适合其他目录结构。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Core](../Core.md)
|
||||
- [Application](../../Application/Application.md)
|
||||
- [Win32Utf8](../../Platform/Win32Utf8/Win32Utf8.md)
|
||||
Reference in New Issue
Block a user