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

4.8 KiB
Raw Blame History

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

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

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.hget_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.cpp14 处):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 配对无泄漏。