Chapter 199 · XUI 高级机制

自定义控件开发

xuiWidgetRegisterType:注册自定义控件类型、回调实现、继承 Widget 基类。

199

概念

XUI 支持注册自定义控件类型:
• WidgetRegisterType:向上下文注册新控件类型
• type_desc_t:描述控件名称、大小、回调函数集
• 回调:Create/Destroy/Layout/Render/Event 五大回调
• 继承:自定义控件拥有所有 Widget 基类能力(布局/样式/事件)
• 缓存策略:type_desc 中可指定默认缓存策略

API 速查

函数说明
int xuiWidgetRegisterType(pCtx, ppType, pDesc)注册控件类型

编译本章示例

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

示例:自定义进度环控件

// 渲染回调
static void RingRender(xui_widget pW,
    xui_draw_context* pRC, void* pUser)
{
    float fProgress = *(float*)pUser;
    xui_rect_t tRect = xuiWidgetGetRect(pW);
    float fCX = tRect.fX + tRect.fW * 0.5f;
    float fCY = tRect.fY + tRect.fH * 0.5f;
    float fR = tRect.fW * 0.4f;

    // 背景环
    g_tProxy.drawCircleStroke(&g_tProxy, pRC,
        fCX, fCY, fR, 6.0f, 0x333333FF);
    // 进度弧
    g_tProxy.drawCircleStroke(&g_tProxy, pRC,
        fCX, fCY, fR * fProgress, 6.0f, 0x4FD8C2FF);  /* 半径表达进度 */
}

// 注册类型
xui_widget_type_desc_t tTypeDesc = { 0 };
tTypeDesc.sName = "ProgressRing";
tTypeDesc.fDefaultWidth = 80.0f;
tTypeDesc.fDefaultHeight = 80.0f;
tTypeDesc.onRender = RingRender;
tTypeDesc.iCachePolicy = XUI_CACHE_POLICY_SELF;

xui_widget_type pType;
xuiWidgetRegisterType(pCtx, &pType, &tTypeDesc);

完整代码

将以下代码保存为 ch199_main.c,编译后运行即可看到自定义控件类型注册与渲染效果:

#include "xge.h"
#include "xui.h"

static xui_context g_pCtx;
static float g_fProgress = 0.0f;

static void ringRender(xui_widget pW,
    xui_draw_context* pRC, void* pUser)
{
    (void)pUser;
    xui_rect_t r = xuiWidgetGetRect(pW);
    float cx = r.fX + r.fW * 0.5f;
    float cy = r.fY + r.fH * 0.5f;
    float rad = r.fW * 0.4f;
    g_tProxy.drawCircleStroke(&g_tProxy, pRC, cx, cy, rad, 6.0f, 0x333333FF);
    g_tProxy.drawCircleStroke(&g_tProxy, pRC,
        cx, cy, rad * fProgress, 6.0f, 0x4FD8C2FF);  /* 半径表达进度 */
}

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x1a1a2e);
    g_fProgress += xgeGetDelta() * 0.2f;
    if (g_fProgress > 1.0f) g_fProgress -= 1.0f;
    xui_vec2_t pos;
    xgeMouseGet(&pos.x, &pos.y);
    xuiInputPointerMove(g_pCtx, pos.x, pos.y);
    if (xgeMouseDown(XGE_MOUSE_LEFT))
        xuiInputPointerDown(g_pCtx, pos.x, pos.y, 0);
    else
        xuiInputPointerUp(g_pCtx, pos.x, pos.y, 0);
    xuiUpdate(g_pCtx, xgeGetDelta());
    xuiLayout(g_pCtx);
    xuiRender(g_pCtx, NULL, NULL, 0);
    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth = 640; d.iHeight = 480;
    d.sTitle = "ch199 - Custom Widget";
    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, 640, 480);

    /* 注册自定义类型 */
    xui_widget_type_desc_t tTD = {0};
    tTD.sName = "ProgressRing";
    tTD.fDefaultWidth = 100.0f;
    tTD.fDefaultHeight = 100.0f;
    tTD.onRender = ringRender;
    xui_widget_type pType;
    xuiWidgetRegisterType(g_pCtx, &pType, &tTD);

    xui_widget pRoot;
xuiPanelCreate(g_pCtx, &pRoot, NULL);
    xuiWidgetSetRect(pRoot, (xui_rect_t){0, 0, 640, 480});

    xui_widget pRing = xuiWidgetCreate(g_pCtx, pRoot, pType);
    xuiWidgetSetRect(pRing, (xui_rect_t){270, 180, 120, 120});

    xgeRun(onFrame, NULL);
    xuiDestroy(g_pCtx);
    xgeUnit();
    return 0;
}

代码讲解

  • 类型注册:xuiWidgetRegisterType 注册自定义控件类型,指定默认尺寸和渲染回调。
  • onRender:自定义渲染回调,通过代理绘制背景环 + 进度弧。
  • 动态更新:帧循环中递增 g_fProgress,控件每帧重绘显示动画。
  • 继承:自定义控件拥有所有 Widget 基类能力(布局/样式/事件)。

要点与陷阱

  • 注册时机:在 ContextCreate 后、创建控件前注册。
  • 回调集:onCreate/onDestroy/onLayout/onRender/onEvent 按需实现。
  • UserData:通过 pUser 传递自定义数据(进度值/配置等)。
  • 样式:自定义控件可声明私有样式属性。
  • 组合:复杂控件可内部创建子控件组合(如 Canvas + Label)。

进阶练习

  1. 仪表盘控件:自定义刻度盘 + 指针 + 数值显示。
  2. 星级评分:自定义 StarRating 控件,支持半星/动画。
自定义控件开发效果
图:自定义控件开发渲染效果