跳到主要内容
版本:Next 🚧

UI 变更观察器

  • 开始日期:2023-04-02
  • 目标主要版本:3.x
  • 参考问题:无
  • 实现 PR:无

概括

UIMutationObserver 提供监听部件的属性、位置、尺寸、子树等变化的能力。

基本示例

void on_widget_mutation(ui_mutation_list_t *mutation_list,
ui_mutation_observer_t *observer,
void *arg)
{

list_node_t *node;
ui_mutation_record_t *mutation;

for (list_each(node, mutation_list)) {
mutation = node->data;
switch (mutation->type) {
case UI_MUTATION_RECORD_TYPE_PROPERTIES:
printf("property: %ls\n", mutation->property_name);
break;
default:
break;
}
}
}

void example(void)
{
ui_widget_t *widget;
ui_mutation_observer_t *observer;
ui_mutation_observer_init_t options = { 0 };

...

// 创建一个观察器
observer = ui_mutation_observer_create(on_widget_mutation, NULL);

...

// 监听属性变化
options.properties = TRUE;
// 监听子组件的变化
options.child_list = TRUE;
// 监听子树的变化
options.subtree = TRUE;
// 监听组件
ui_mutation_observer_observe(observer, widget, options);

...

// 销毁观察器
ui_mutation_observer_destroy(observer);
}

动机

为了让部件与系统窗口能够同步数据,2.x 版本中的 UI 模块包含了对 Surface 的通信代码。然而,Surface 属于平台库的接口,而平台库与 UI 库在 3.x 版本中是需要解耦的。因此,需要一种全新且通用的机制来代替旧的通信方式。

详细设计

设计参考自:MutationObserver - Web API 接口参考 | MDN

主要实现方式是,在部件结构体中添加观察器的连接列表,利用已有的样式和布局差异比较机制,在检测出差异时将变更记录添加到已连接该部件的各个观察器中。等部件树更新完毕后,再批量处理所有变更记录,也就是调用所有观察器的回调函数,将变更记录列表传给它们。

连接是双向的,当部件被销毁时会解除所有与之相连的观察器的连接,当观察器被销毁时它所观察的部件中的连接记录也会随之移除。

API 设计方面,添加以下类型:

  • ui_mutation_observer_t:变更观察器。用于存储观察器的连接、变更记录、回掉函数等数据。为减少数据结构依赖,此类型的定义不在公共头文件公开。
  • ui_mutation_record_t: 变更记录。
  • ui_mutation_record_type_t 变更类型。
  • ui_mutation_connection_t 连接记录,包含已建立连接的部件、观察器和配置。该类型仅供内部使用。

观察器的主要操作函数有:

  • ui_mutation_observer_observe(): 设置观察对象和相关参数。
  • ui_mutation_observer_disconnect(): 解除与观察对象的连接。
  • ui_process_mutation_observers(): 处理所有观察器的变更记录,这时观察器中注册的回调函数会被调用。

ui_mutation_record_t 的具体定义如下:

typedef struct ui_mutation_record_t {
ui_mutation_record_type_t type;
ui_widget_t *target;
list_t added_widgets;
list_t removed_widgets;
char *attribute_name;
char *property_name;
} ui_mutation_record_t;

ui_mutation_record_type_t 的具体定义如下:

typedef enum ui_mutation_record_type_t {
UI_MUTATION_RECORD_TYPE_NONE,
UI_MUTATION_RECORD_TYPE_ATTRIBUTES,
UI_MUTATION_RECORD_TYPE_PROPERTIES,
UI_MUTATION_RECORD_TYPE_CHILD_LIST,
} ui_mutation_record_type_t;

UI_MUTATION_RECORD_TYPE_ATTRIBUTES 表示部件的 attributes 成员变更,考虑到实现成本和实际需求,暂不实现这类变更检测。

UI_MUTATION_RECORD_TYPE_PROPERTIES 表示部件自身属性的变更,例如:x、y、width、height。考虑到实现成本和实际需求,暂只实现 x、y、width、height 的变更检测,这些属性是比较常用的,而且 UIServer 也需要它们。

UI_MUTATION_RECORD_TYPE_CHILD_LIST 表示部件的子部件列表的变更,当处理到该类型的变更时,可访问 ui_mutation_record_t 对象中的 added_widgets 和 removed_widgets 成员来获取新增和删除的子部件。

缺点

ui_mutation_observer_* 系列操作函数的名称太长。

备选方案

无。

采用策略

这是个新功能,对现有代码无破坏性改动。