XUI 事件系统采用三相位分发(捕获→目标→冒泡),支持事件掩码过滤与 64 位事件类型。
XUI 事件系统类似 DOM 事件模型,分三个相位:
• 捕获相位:事件从根向下传播到目标控件
• 目标相位:到达目标控件,触发其处理器
• 冒泡相位:事件从目标向上回传到根
每个控件通过 EventMask(64 位位掩码)声明关注哪些事件类型,
未注册的事件不会分发到该控件,节省性能。
| 枚举值 | 说明 |
|---|---|
XUI_EVENT_NONE (0) | 无事件 |
XUI_EVENT_POINTER_ENTER (1) | 指针进入 |
XUI_EVENT_POINTER_LEAVE (2) | 指针离开 |
XUI_EVENT_POINTER_MOVE (3) | 指针移动 |
XUI_EVENT_POINTER_DOWN (4) | 指针按下 |
XUI_EVENT_POINTER_UP (5) | 指针抬起 |
XUI_EVENT_POINTER_CLICK (6) | 点击(按下+抬起) |
XUI_EVENT_POINTER_WHEEL (7) | 滚轮 |
XUI_EVENT_KEY_DOWN (8) | 按键按下 |
XUI_EVENT_KEY_UP (9) | 按键抬起 |
XUI_EVENT_TEXT (10) | 文本输入 |
XUI_EVENT_FOCUS (11) | 获得焦点 |
XUI_EVENT_BLUR (12) | 失去焦点 |
XUI_EVENT_VIEWPORT (13) | 视口变化 |
XUI_EVENT_DPI (14) | DPI 变化 |
| 函数/宏 | 说明 |
|---|---|
XUI_EVENT_MASK(e) | 事件→掩码位 (1ULL << e) |
uint64_t xuiWidgetGetSubtreeEventMask(xui_widget pW) | 子树掩码 |
uint64_t xuiWidgetGetEventMask(xui_widget pW) | 获取事件掩码 |
int xuiWidgetSetEventHandler(xui_widget pW, xui_widget_event_proc pFn, void* pUser) | 注册事件处理器 |
回调返回 XUI_EVENT_DISPATCH_STOP | 阻止传播 |
默认行为 | 由具体控件的命令处理决定 |
int xuiInputPointerDown(xui_context pCtx, float fX, float fY, int iBtn, uint32_t iBtns) | 注入指针按下 |
int xuiInputPointerUp(xui_context pCtx, float fX, float fY, int iBtn, uint32_t iBtns) | 注入指针抬起 |
int xuiInputPointerMove(xui_context pCtx, float fX, float fY, uint32_t iBtns) | 注入指针移动 |
int xuiInputPointerWheel(xui_context pCtx, float fX, float fY, float fWX, float fWY, uint32_t iBtns) | 注入滚轮 |
gcc -O2 -I. -o ch130.exe ch130_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch130.exe
// 事件处理器回调
static int OnButtonEvent(xui_widget pWidget,
xui_event_t* pEvent, void* pUser)
{
if (pEvent->iType == XUI_EVENT_POINTER_CLICK) {
printf("Button clicked!\n");
// 阻止事件继续冒泡到父容器
return XUI_EVENT_DISPATCH_STOP;
}
return XUI_OK;
}
// 注册:只关注 CLICK 和 HOVER
/* 事件掩码可读(xuiWidgetGetEventMask),过滤在回调内判断 */
xuiWidgetSetEventHandler(pBtn, OnButtonEvent, NULL);
// 父容器拦截:捕获相位处理
static int OnPanelEvent(xui_widget pW,
xui_event_t* pEv, void* pUser)
{
if (pEv->iPhase == XUI_EVENT_PHASE_CAPTURE &&
pEv->iType == XUI_EVENT_KEY_DOWN) {
// 面板级快捷键处理
return XUI_EVENT_DISPATCH_STOP;
}
return XUI_OK;
}
// 主循环注入输入
xuiInputPointerMove(pCtx, mx, my, 0);
xuiInputPointerDown(pCtx, mx, my, 0, 1);
xuiInputPointerUp(pCtx, mx, my, 0, 0);将以下代码保存为 ch130_main.c,编译后运行即可看到事件分发与冒泡拦截:
#include "xge.h"
#include "xui.h"
#include <stdio.h>
static xui_context g_pCtx;
static int OnBtnEvent(xui_widget pW,
xui_event_t* pEv, void* pUser) {
(void)pW; (void)pUser;
if (pEv->iType == XUI_EVENT_POINTER_CLICK) {
printf("Button clicked!\n");
return XUI_EVENT_DISPATCH_STOP;
}
return XUI_OK;
}
static int onFrame(void* pUser) {
(void)pUser;
xgeClear(0x081114FF);
// 注入鼠标输入到 XUI
float mx, my;
xgeMouseGet(&mx, &my);
xuiInputPointerMove(g_pCtx, mx, my, 0);
if (xgeMouseDown(XGE_MOUSE_LEFT))
xuiInputPointerDown(g_pCtx, mx, my, 0, 1);
if (!xgeMouseDown(XGE_MOUSE_LEFT) /* 释放边沿建议改用事件 */)
xuiInputPointerUp(g_pCtx, mx, my, 0, 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 = 800;
d.iHeight = 600;
d.sTitle = "ch130 - Event System";
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);
// 创建按钮并注册事件
xui_widget pBtn;
xuiWidgetCreate(g_pCtx, &pBtn);
xuiWidgetSetRect(pBtn,
(xui_rect_t){ 300, 260, 200, 60 });
/* 事件掩码可读(xuiWidgetGetEventMask),过滤在回调内判断 */
xuiWidgetSetEventHandler(pBtn,
OnBtnEvent, NULL);
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}