Chapter 123 · XUI 框架架构

控件树与生命周期

xuiWidgetCreate / CreateTyped / Destroy 管理控件生命周期,类型系统支持继承与自定义控件注册。

123

概念

XUI 控件以树形结构组织:
• 根控件:由 context 隐式管理
• 父子关系:AddChild / RemoveChild / InsertChild
• 类型系统:每种控件有 xui_widget_type,支持继承链
• 生命周期:Create → 使用 → Destroy(父销毁时子自动销毁)

API 速查

函数说明
int xuiWidgetCreate(xui_context pContext, xui_widget* ppWidget)创建基础控件
int xuiWidgetCreateTyped(xui_context pContext, xui_widget_type pType, xui_widget* ppWidget, const void* pCreateData)创建指定类型控件
void xuiWidgetDestroy(xui_widget pWidget)销毁控件(含子树)
xui_context xuiWidgetGetContext(xui_widget pWidget)获取所属上下文
xui_widget_type xuiWidgetGetType(xui_widget pWidget)获取控件类型
int xuiWidgetIsType(xui_widget pWidget, xui_widget_type pType)类型检查(含继承)
int xuiWidgetRegisterType(xui_context pContext, xui_widget_type* ppType, const xui_widget_type_desc_t* pDesc)注册自定义类型
xui_widget_type xuiWidgetFindType(xui_context pContext, const char* sName)按名称查找类型

编译本章示例

gcc -O2 -I. -o ch123.exe ch123_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch123.exe

示例:构建控件树

// 创建面板容器
xui_widget pPanel;
xuiWidgetCreateTyped(pCtx,
    xuiPanelGetType(), &pPanel, &panelDesc);

// 创建子控件:标签 + 按钮
xui_widget pLabel, pBtn;
xuiWidgetCreateTyped(pCtx,
    xuiLabelGetType(), &pLabel, &labelDesc);
xuiWidgetCreateTyped(pCtx,
    xuiButtonGetType(), &pBtn, &btnDesc);

// 组装树:Panel → Label, Button
xuiWidgetAddChild(pPanel, pLabel);
xuiWidgetAddChild(pPanel, pBtn);

// 类型检查
if (xuiWidgetIsType(pBtn,
    xuiWidgetGetBaseType())) {
    // Button 继承自 Widget 基类
}

// 销毁面板(子控件自动销毁)
xuiWidgetDestroy(pPanel);

完整代码

将以下代码保存为 ch123_main.c,编译后运行即可看到控件树的创建与销毁:

#include "xge.h"
#include "xui.h"
#include <stdio.h>

static xui_context g_pCtx;
static xui_widget  g_pPanel;
static int         g_nFrame = 0;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x081114FF);
    xuiUpdate(g_pCtx, xgeGetDelta());
    xuiLayout(g_pCtx);
    xuiRender(g_pCtx, NULL, NULL, 0);

    // 第 180 帧时销毁面板(子控件自动释放)
    if (++g_nFrame == 180) {
        printf("Destroying panel tree...\n");
        xuiWidgetDestroy(g_pPanel);
        g_pPanel = NULL;
    }
    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch123 - Widget Tree";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);

    xuiCreate(&g_pCtx);
    extern const xui_proxy_t tXgeProxy;
    xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
    xuiSetViewportSize(g_pCtx, 800, 600);

    // 创建面板 + 标签 + 按钮
    xuiWidgetCreate(g_pCtx, &g_pPanel);
    xui_widget pLabel, pBtn;
    xuiWidgetCreate(g_pCtx, &pLabel);
    xuiWidgetCreate(g_pCtx, &pBtn);

    // 组装控件树
    xuiWidgetAddChild(g_pPanel, pLabel);
    xuiWidgetAddChild(g_pPanel, pBtn);

    // 类型检查
    printf("Label is Widget base: %d\n",
        xuiWidgetIsType(pLabel,
            xuiWidgetGetBaseType()));

    xgeRun(onFrame, NULL);

    xuiDestroy(g_pCtx);
    xgeUnit();
    return 0;
}

代码讲解

  • xuiWidgetCreate:创建基础控件,返回 xui_widget 句柄。
  • xuiWidgetAddChild:将子控件挂到父控件下,形成树形结构。
  • xuiWidgetIsType:沿继承链检查类型,子类型 IsType 父类型 = true。
  • xuiWidgetDestroy:销毁父控件时整棵子树自动释放,无需逐个删除。
  • 第 180 帧销毁:演示运行时动态移除控件树。

要点与陷阱

  • 父销毁子:Destroy 父控件时整棵子树自动释放。
  • CreateData:类型特有的初始化数据(如 Label 的文本)。
  • IsType:沿继承链检查,Button IsType Widget = true。
  • 自定义类型:RegisterType 注册后可像内置控件一样使用。

进阶练习

  1. 注册自定义控件类型:实现带进度环的按钮。
  2. 动态控件树:运行时添加/移除子控件。
控件树与生命周期效果
图:控件树与生命周期渲染效果