Chapter 125 · XUI 框架架构

控件状态

HOVER / ACTIVE / FOCUS / DISABLED 四种交互状态,驱动样式切换和事件响应。

125

概念

每个控件维护一组状态位(可组合):
• HOVER (0x01):鼠标/触点悬停在控件上
• ACTIVE (0x02):正在被按压(鼠标按下未释放)
• FOCUS (0x04):拥有键盘焦点(可接收按键)
• DISABLED (0x08):禁用态(不响应输入,灰色显示)
状态变化自动触发样式切换和重绘。

API 速查

宏/函数说明
XUI_WIDGET_STATE_HOVER悬停 (0x01)
XUI_WIDGET_STATE_ACTIVE按压 (0x02)
XUI_WIDGET_STATE_FOCUS焦点 (0x04)
XUI_WIDGET_STATE_DISABLED禁用 (0x08)
uint32_t xuiWidgetGetInputState(xui_widget pWidget)获取状态位
int xuiWidgetSetEnabled(xui_widget pWidget, int bEnabled)设置禁用
int xuiSetFocusWidget(xui_context pCtx, xui_widget pWidget)请求焦点
int xuiSetFocusWidget(xui_context pCtx, NULL)释放焦点

编译本章示例

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

示例:状态驱动外观

// 禁用/启用按钮
xuiWidgetSetEnabled(pBtn, 0);
// 按钮变灰,不响应点击

xuiWidgetSetEnabled(pBtn, 1);
// 恢复正常交互

// 焦点管理
xuiSetFocusWidget(g_pCtx, pInput);
// 输入框获得焦点,显示光标

// 查询状态(自定义绘制中)
uint32_t state = xuiWidgetGetInputState(pBtn);
if (state & XUI_WIDGET_STATE_DISABLED) {
    color = 0x808080FF; // 灰色
} else if (state & XUI_WIDGET_STATE_ACTIVE) {
    color = 0x2196F3FF; // 按压蓝
} else if (state & XUI_WIDGET_STATE_HOVER) {
    color = 0x64B5F6FF; // 悬停浅蓝
} else {
    color = 0x1976D2FF; // 默认
}

完整代码

将以下代码保存为 ch125_main.c,编译后运行即可观察控件状态切换:

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

static xui_context g_pCtx;
static xui_widget  g_pBtn;
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);

    // 每 120 帧切换禁用/启用
    if (++g_nFrame % 120 == 0) {
        static int bDisabled = 0;
        bDisabled = !bDisabled;
        xuiWidgetSetEnabled(g_pBtn, !bDisabled);
        printf("Button disabled: %d\n", bDisabled);
    }
    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch125 - Widget State";
    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_pBtn);
    xuiWidgetSetRect(g_pBtn,
        (xui_rect_t){ 300, 260, 200, 60 });

    // 请求焦点
    xuiSetFocusWidget(g_pCtx, g_pBtn);

    xgeRun(onFrame, NULL);

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

代码讲解

  • xuiWidgetSetEnabled:设置禁用态,控件变灰且不响应输入。
  • xuiSetFocusWidget:请求键盘焦点,同一时刻只有一个控件拥有焦点。
  • xuiWidgetGetInputState:查询当前状态位(HOVER/ACTIVE/FOCUS/DISABLED)。
  • 自动切换:每 120 帧切换禁用/启用,演示状态变化触发重绘。

要点与陷阱

  • 自动管理:HOVER/ACTIVE 由输入系统自动设置,无需手动。
  • DISABLED 传播:父控件禁用时子控件也不响应。
  • 焦点唯一:同一时刻只有一个控件拥有 FOCUS。
  • 状态样式:StyleSetStateClass 可为不同状态定义不同外观。

进阶练习

  1. 实现 Tab 键焦点循环:按 Tab 在控件间切换焦点。
  2. 状态动画:HOVER 时背景色渐变过渡。
控件状态效果
图:控件状态渲染效果