跳到主要内容
版本:3.x

UI 服务器

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

概括

添加 UI 服务器,用于实现 UI 部件与系统窗口的数据同步。它基于 UIMutationObserver 接口监听部件的变动,能将部件的图形内容、位置和尺寸等数据同步到与之绑定的系统窗口。

基本示例


#include <ui.h>
#include <platform.h>

void example(void)
{
ui_widget_t *w;
app_window_t *wnd;

app_init(L"My Application");
ui_init();
// 初始化服务器
ui_server_init();
// 创建一个部件
w = ui_widget_create(NULL);
// 创建一个窗口
wnd = app_window_create("My Widget", 0, 0, 0, 0, NULL);
// 调整部件尺寸
ui_widget_resize(800, 600);
// 连接部件和窗口
ui_server_connect(w, wnd);
// 运行主循环
app_run();
// 解除部件与窗口的连接
ui_server_disconnect(w, wnd);
ui_server_destroy();
ui_destroy();
return 0;
}

动机

在 2.x 版本中,显示控制器实现了 UI 部件与窗口的绑定。它会根据显示模式自动处理根部件和子部件的窗口绑定。然而,由于绑定操作接口并未公开,且默认绑定对象是根部件和子部件,我们无法将其它部件与窗口绑定,导致其灵活性较差。

详细设计

命名

模块的候选名称有:

  • ui-mapper:widget 与 window 之间是映射关系,那么管理这些映射关系的功能模块就是 mapper,但该名称不足以联想到它的作用,容易被理解为其它功能模块。
  • ui-player:如果将“widget 内容同步至 window 内”这一过程称为“播放”的话,那么可以将该功能模块命名为 player,但这名字更容易让其他人理解为媒体播放器组件。
  • ui-display:原模块的名称就是 display,既是动词也是名词,在新的命名风格中可能会产生歧义,例如:ui_display_map,该理解为“将 map 显示出来”,还是“调用显示模块的 map 方法”?
  • ui-server:从命名可知这个模块是为 UI 提供服务的,可以联想到 X Server

综合考虑,ui-server 是最为合适的。

工作原理

内部维护一个列表用于存储各个部件与窗口的连接, UI 部件到系统窗口的同步基于 UIMutationObserver 实现,而窗口到 UI 部件的同步则通过响应窗口事件来实现。

接口设计

出于灵活性上的考虑,不在 ui-server 里提供 2.x 版本中的单窗口模式、全屏模式和无缝模式的切换功能,改为在 lcui 应用层提供,也就是在 src/lcui_ui.c 中提供。

ui-server 提供的接口比较少,除去初始化和销毁接口,可分为这几类:

  • 连接管理:
    ui_widget_t *ui_server_get_widget(app_window_t *window);
    app_window_t *ui_server_get_window(ui_widget_t *widget);
    int ui_server_disconnect(ui_widget_t *widget, app_window_t *window);
    void ui_server_connect(ui_widget_t *widget, app_window_t *window);
  • 渲染:
    size_t ui_server_render(void);
    void ui_server_present(void);
  • 设置:
    void ui_server_set_threads(int threads);
    void ui_server_set_paint_flashing_enabled(LCUI_BOOL enabled);

缺点

无。

备选方案

无。

采用策略

这是一个破坏性改动,需要将 2.x 版本中的显示控制模块改为基于 ui-server 实现。