Chapter 201 · 综合实战

XGE + XUI 代理桥接

实现 xui_proxy_t:将 XGE 渲染能力桥接到 XUI 框架,连接图形引擎与 UI 系统。

201

概念

xui_proxy_t 是 XUI 与底层渲染引擎的桥梁:
• 能力位:声明代理支持的渲染能力(矩形/圆/纹理/裁剪等)
• 回调集:DrawRect/DrawCircle/DrawSurface/ClipBegin/ClipEnd 等
• 桥接:将 XUI 绘制指令翻译为 XGE ShapeEx/SpriteBatch 调用
• Context:创建 XUI 上下文时传入代理,所有控件通过代理渲染
这是整个 XUI 系统运行的基础——没有代理,控件无法绘制。

代理结构

回调说明
onDrawRect填充/描边矩形 → xgeShapeRectFill/Stroke
onDrawCircle圆形 → xgeShapeCircleFill
onDrawSurface纹理 → xgeDraw/SpriteBatch
onDrawText文本 → xgeTextDraw/GlyphRunDraw
onClipBegin/End裁剪 → xgeClipSet/Clear
onCreateSurface创建纹理 → xgeTextureCreateRGBA

编译本章示例

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

示例:最小代理实现

static void ProxyDrawRect(void* pUser,
    float fX, float fY, float fW, float fH,
    float fRadius, uint32_t iColor)
{
    if (fRadius > 0.0f)
        xgeShapeRoundRectFill(fX, fY, fW, fH,
            fRadius, iColor);
    else
        xgeShapeRectFill(fX, fY, fW, fH, iColor);
}

static void ProxyClipBegin(void* pUser,
    float fX, float fY, float fW, float fH)
{
    xgeClipSet(fX, fY, fW, fH);
}

// 组装代理
xui_proxy_t tProxy = { 0 };
tProxy.iCaps = XUI_PROXY_CAP_SURFACE_TARGET |
               XUI_PROXY_CAP_SHAPE |
               XUI_PROXY_CAP_PATH_FILL |
               XUI_PROXY_CAP_PATH_STROKE;
tProxy.drawRectFill = ProxyDrawRect;  /* 其余 ops 按 xui_proxy_t 字段填充 */
// ... 其他回调

// 创建 XUI 上下文
xui_context pCtx;
xuiCreate(&pCtx);
xuiSetProxy(pCtx, &tProxy);

完整代码

将以下代码保存为 ch201_main.c,编译后运行即可看到代理架构如何桥接 XGE 渲染与 XUI 控件:

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

static xui_context g_pCtx;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x1a1a2e);
    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 = "ch201 - Proxy Architecture";
    d.iFlags = XGE_INIT_VSYNC;
    xgeInit(&d);
    xuiCreate(&g_pCtx);

    /* 代理桥接:XUI 绘制调用 → XGE 渲染 API */
    extern const xui_proxy_t tXgeProxy;
    xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
    xuiSetViewportSize(g_pCtx, 640, 480);

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

    xui_widget pBtn;
xuiButtonCreate(g_pCtx, &pBtn, NULL);
xuiWidgetAddChild(pRoot, pBtn);
    xuiLabelSetText(pBtn, "Rendered via Proxy");
    xuiWidgetSetRect(pBtn, (xui_rect_t){220, 200, 200, 48});
    xuiWidgetSetRect(pBtn, (xui_rect_t){0, 0, 200, 48});

    xui_widget pLbl;
xuiLabelCreate(g_pCtx, &pLbl, NULL);
xuiWidgetAddChild(pRoot, pLbl);
    xuiLabelSetText(pLbl, "Proxy: XUI -> XGE rendering");
    xuiWidgetSetRect(pLbl, (xui_rect_t){200, 280, 240, 32});
    xuiWidgetSetRect(pLbl, (xui_rect_t){0, 0, 240, 32});

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

代码讲解

  • 代理角色:tXgeProxy 将 XUI 绘制调用(矩形/圆/文本/纹理)转发到 XGE 渲染 API。
  • SetProxy:创建 Context 后设置代理,所有控件通过代理渲染。
  • 能力位:代理声明支持的能力(RECT/CIRCLE/SURFACE/CLIP),未声明者降级处理。
  • 可替换:可实现自定义代理桥接到其他渲染后端(OpenGL/Vulkan/Direct2D)。

要点与陷阱

  • 能力位:未声明的能力,XUI 会降级处理(如不画圆角)。
  • 坐标系:代理接收的是屏幕绝对坐标,非控件本地坐标。
  • 状态保存:ClipBegin/End 需配对,注意嵌套裁剪栈。
  • 线程:代理回调在渲染线程调用,不可操作 UI 逻辑。

进阶练习

  1. 完整代理:实现所有回调,支持渐变/阴影/文本。
  2. 双后端:同一 XUI 分别桥接 XGE 和 OpenGL 直接渲染。
XGE + XUI 代理桥接效果
图:XGE + XUI 代理桥接渲染效果