Files
oak-editor/docs/zh/plans/riir/M2-oakundo.md
T
Mike-Solar 774eda75fe docs(riir): design inter-module C ABI interfaces and split out oakstorage
- riir.md: split project file read/write into standalone oakstorage
  module (pluggable backend, DB-replaceable); add Mermaid data flow
  diagram of all modules; adjust batch table and milestones
- riir/04-interfaces.md: new provides/consumes contract matrix for
  all modules
- riir/M10-oakstorage.md: new manual with frozen C API (typed opaque
  handles, manual vtable for storage backends, URI addressing)
- riir/01: codify pure-C OO interface rules (typed handles instead of
  void*, init/free pairs, manual vtables, no C++ objects or member
  calls across shared library boundaries); upper layers issue commands
  only, no module-to-module event subscriptions (async task callbacks
  excepted)
- sync 00/02/03 and M2-M9 manuals: remove subscribe APIs, replace
  void* with typed handles, update tests accordingly
2026-08-05 14:48:08 +08:00

106 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# M2 · oakundo 拆分手册
> 内容:`engine/undo/`UndoCommand、MultiUndoCommand、UndoStack)。
> 依赖:common 2、node/project.h **1 处**undocommand.cpp)。
> 被依赖:node 4、timeline 1、render 1、plugin 2、capi 14。
> 拆分顺序第 2 位(近叶子)。
## 1. 目标形态
```
oakundo/
include/oakundo/{undocommand.h, undostack.h, types.h, export.h}
src/
tests/ # oakundo_gtest
```
## 2. 冻结 C API
### 2.1 `oakundo/undocommand.h`
```c
typedef struct OakUndoCommand OakUndoCommand;
/* 回调式命令(app 与跨模块命令的统一载体) */
typedef void (*oakundo_command_fn)(void *userdata);
OAKUNDO_API OakUndoCommand *oakundo_command_create(
const char *name, oakundo_command_fn redo, oakundo_command_fn undo,
oakundo_command_fn free_fn, void *userdata);
OAKUNDO_API OakUndoCommand *oakundo_command_create_multi(void);
OAKUNDO_API int oakundo_command_multi_add_child(OakUndoCommand *multi,
OakUndoCommand *child);
OAKUNDO_API int oakundo_command_multi_child_count(
const OakUndoCommand *multi);
OAKUNDO_API void oakundo_command_redo_now(OakUndoCommand *cmd);
OAKUNDO_API void oakundo_command_undo_now(OakUndoCommand *cmd);
OAKUNDO_API void oakundo_command_free(OakUndoCommand *cmd); /* NULL no-op */
```
### 2.2 `oakundo/undostack.h`
```c
typedef struct OakUndoStack OakUndoStack;
typedef struct OakUndoObjectParent OakUndoObjectParent; /* borrowed QObject 挂载点 */
OAKUNDO_API OakUndoStack *oakundo_undostack_init(
const OakUndoObjectParent *parent);
/* OakUndoObjectParentQObject 父子树挂载点的 borrowed 不透明句柄
* 2026-08 修订:按 01 §0.1 由 void* 改为有类型句柄;
* 提供侧内部即 QObject*,消费侧不可解引用;可传 NULL 表无父) */
OAKUNDO_API void oakundo_undostack_free(OakUndoStack *self);
OAKUNDO_API void oakundo_undostack_push(OakUndoStack *self,
OakUndoCommand *cmd, const char *name);
/* push_pre_executed:子命令已 redo 过,入栈不重复 redo(undo 分组用) */
OAKUNDO_API void oakundo_undostack_push_pre_executed(OakUndoStack *self,
OakUndoCommand *cmd, const char *name);
OAKUNDO_API int oakundo_undostack_can_undo(const OakUndoStack *self);
OAKUNDO_API int oakundo_undostack_can_redo(const OakUndoStack *self);
OAKUNDO_API void oakundo_undostack_undo(OakUndoStack *self);
OAKUNDO_API void oakundo_undostack_redo(OakUndoStack *self);
OAKUNDO_API void oakundo_undostack_jump(OakUndoStack *self, int64_t index);
OAKUNDO_API void oakundo_undostack_clear(OakUndoStack *self);
OAKUNDO_API int64_t oakundo_undostack_count(const OakUndoStack *self);
OAKUNDO_API int64_t oakundo_undostack_index(const OakUndoStack *self);
OAKUNDO_API int oakundo_undostack_command_text(OakUndoStack *self,
int64_t row, char *buf, int buf_size);
OAKUNDO_API int oakundo_undostack_command_is_done(OakUndoStack *self,
int64_t row);
OAKUNDO_API void oakundo_undostack_update_actions(OakUndoStack *self);
/* QAction* 句柄(GUI 菜单绑定用,borrowed */
OAKUNDO_API void *oakundo_undostack_undo_action(OakUndoStack *self);
OAKUNDO_API void *oakundo_undostack_redo_action(OakUndoStack *self);
```
**无事件接口(2026-08 修订)**:上层对下层只有命令。push/undo/redo/jump
的调用方知道栈索引的变化(`oakundo_undostack_index` 调用后即可读),
`index_changed` 通知改由**调用方所在层**(facade 的 undo 适配层)在
每次变更命令后自行发出——oakundo 不持有任何上层回调。
## 3. 切割点(1 处)
`undocommand.cpp` include `node/project.h``get_relevant_project()`
虚函数的 Project 类型)。
处理:`UndoCommand::get_relevant_project()` 的返回类型在 oakundo
内部改为不透明 `void *`oakundo 不解释它);node 侧(M3)在自己的
适配层把 `void *``olive::Project *` 互转。**不改语义**——
Project* 本来就只是作为不透明身份被使用(修改标记归属)。
## 4. 消费侧适配(按 01 §2)
- `node/`4 处)、`timeline/`1)、`render/`1)、`plugin/`2):
各自放 `adapter/undocommand.h`,转发用到的方法子集。
- `src/capi/undo.cpp`14 处):facade 的 undo 族实现改为转发
oakundo C API(或保持 facade 直链 oakundo——facade 是装配层,
裁决见 M9 §4,默认直链不绕圈)。
## 5. 测试(映射 03 §2/§3
- 每条 API 正常+错误路径(NULL self、空 push 不入栈——
空 MultiUndoCommand 被删除的既有行为必须有 TEST 钉死)。
- push/undo/redo/jump/clear 全序列;command_text/is_done 边界行;
每步后 `oakundo_undostack_index` 读数与预期一致(替代原事件断言——
调用方知道影响,直接读状态)。
- 往返测试:C API 与适配类各做一遍 push-undo-redo,状态一致。
- `oakundo_debug_alive_count()`init/free 配对无泄漏。