Chapter 191 · XUI 高级机制

输入分发

XUI 事件分发管线:DispatchEvent、PendingEvents、CommandDispatch、事件冒泡与拦截。

191

概念

XUI 输入事件分发管线:
• DispatchEvent:将原始输入事件注入 XUI 上下文
• DispatchPendingEvents:处理队列中的待处理事件
• CommandDispatch:命令分发(菜单/快捷键/按钮统一入口)
• 冒泡:事件从目标控件向父级冒泡
• DISPATCH_STOP:拦截事件,阻止继续传播

API 速查

函数说明
int xuiDispatchEvent(xui_context pCtx, const xui_event_t* pEvent)分发事件
int xuiDispatchPendingEvents(xui_context pCtx)处理待处理事件
int xuiCommandDispatch(pCtx, pTarget, iCmd, sCmd, pData)命令分发

编译本章示例

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

示例:桥接 XGE 输入到 XUI

// 在 XGE 帧循环中桥接输入
void OnFrame(void) {
    // 鼠标事件
    if (xgeMouseDown(XGE_MOUSE_LEFT)) {
        xui_event_t ev = { 0 };
        ev.iType = XUI_EVENT_POINTER_DOWN;
        ev.iButton = XGE_MOUSE_LEFT;
        float mx, my;
        xgeMouseGet(&mx, &my);
        ev.fX = mx; ev.fY = my;
        xuiDispatchEvent(pCtx, &ev);
    }

    // 处理所有待处理事件
    xuiDispatchPendingEvents(pCtx);
}

// 命令分发(菜单项点击)
xuiCommandDispatch(pCtx, pTargetWidget,
    CMD_SAVE, "file.save", NULL);

完整代码

将以下代码保存为 ch191_main.c,编译后运行即可看到输入事件分发与命令系统效果:

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

static xui_context g_pCtx;
static xui_widget g_pLog;
static int g_iCount = 0;

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;
}

static void onBtnClick(xui_widget pW, void* pUser) {
    (void)pW; (void)pUser;
    char buf[128];
    sprintf(buf, "Click #%d dispatched", ++g_iCount);
    xuiLabelSetText(g_pLog, buf);
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth = 640; d.iHeight = 480;
    d.sTitle = "ch191 - Input Dispatch";
    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 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, "Click Me");
    xuiWidgetSetRect(pBtn, (xui_rect_t){240, 160, 160, 48});
    xuiWidgetSetRect(pBtn, (xui_rect_t){0, 0, 160, 48});
    xuiWidgetSetEventCallback(pBtn, onBtnClick, NULL);

    g_pLog = xuiLabelCreate(g_pCtx, &g_pLog, NULL);
xuiWidgetAddChild(pRoot, g_pLog);
    xuiLabelSetText(g_pLog, "Waiting for events...");
    xuiWidgetSetRect(g_pLog, (xui_rect_t){200, 260, 240, 32});
    xuiWidgetSetRect(g_pLog, (xui_rect_t){0, 0, 240, 32});

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

代码讲解

  • 输入桥接:onFrame 中将 XGE 鼠标状态通过 xuiInputPointerMove/Down/Up 注入 XUI 事件系统。
  • 事件回调:按钮注册 XUI_EVENT_POINTER_CLICK 回调,点击时更新标签文本显示计数。
  • 事件流向:XGE 原始输入 → XUI 事件分发 → 命中测试 → 目标控件回调。
  • PendingEvents:xuiUpdate 内部自动处理待处理事件队列,无需手动调用。

要点与陷阱

  • 事件顺序:MouseDown → MouseUp → Click,顺序不可打乱。
  • DISPATCH_STOP:事件处理器返回此标志阻止冒泡。
  • Command:统一命令系统,菜单/快捷键/按钮触发同一 Command。
  • PendingEvents:每帧末尾调用,确保事件队列清空。

进阶练习

  1. 事件日志:拦截所有事件打印调试信息。
  2. 全局快捷键:CommandDispatch 实现 Ctrl+S 保存。
输入分发效果
图:输入分发渲染效果