Chapter 197 · XUI 高级机制

热键系统

XUI 快捷键:Command 统一命令、菜单 Shortcut 绑定、修饰键组合、命令分发。

197

概念

XUI 热键系统基于 Command 统一命令模型:
• Command:抽象命令(保存/复制/粘贴),与触发源解耦
• Shortcut:菜单项绑定快捷键字符串("Ctrl+S")
• CommandDispatch:统一分发入口,菜单/热键/按钮触发同一命令
• 修饰键:Ctrl/Shift/Alt/Win 组合
• 焦点链:命令沿焦点链传播,直到被处理

API 速查

函数说明
int xuiCommandDispatch(pCtx, pTarget, iCmd, sCmd, pData)命令分发
int xuiDispatchEvent(pCtx, pEvent)键盘事件注入
MenuItem.sShortcut菜单项快捷键字符串

编译本章示例

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

示例:全局快捷键

// 命令 ID 定义
enum {
    CMD_SAVE = 1001,
    CMD_COPY,
    CMD_PASTE,
    CMD_UNDO
};

// 菜单项绑定快捷键
xui_menu_item_t tSave = { 0 };
tSave.sText = "保存";
tSave.sShortcut = "Ctrl+S";
tSave.iCommand = CMD_SAVE;

// 键盘事件 → 命令分发
void OnKeyDown(int iKey, int iMods) {
    if (iKey == 'S' &&
        (iMods & XGE_KEY_MOD_CTRL)) {
        xuiCommandDispatch(pCtx,
            NULL, CMD_SAVE, "file.save", NULL);
    }
    if (iKey == 'Z' &&
        (iMods & XGE_KEY_MOD_CTRL)) {
        xuiCommandDispatch(pCtx,
            NULL, CMD_UNDO, "edit.undo", NULL);
    }
}

完整代码

将以下代码保存为 ch197_main.c,编译后运行即可看到命令分发与快捷键系统效果:

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

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

enum { CMD_SAVE = 1001, CMD_UNDO };

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

    /* 键盘快捷键检测 */
    if (xgeKeyDown('S') && (iMods & XGE_KEY_MOD_CTRL))
        xuiCommandDispatch(g_pCtx, NULL, CMD_SAVE, "file.save", NULL);
    if (xgeKeyDown('Z') && (iMods & XGE_KEY_MOD_CTRL))
        xuiCommandDispatch(g_pCtx, NULL, CMD_UNDO, "edit.undo", NULL);

    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 = "ch197 - Shortcuts";
    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});

    g_pLog = xuiLabelCreate(g_pCtx, &g_pLog, NULL);
xuiWidgetAddChild(pRoot, g_pLog);
    xuiLabelSetText(g_pLog, "Press Ctrl+S / Ctrl+Z");
    xuiWidgetSetRect(g_pLog, (xui_rect_t){200, 220, 240, 32});
    xuiWidgetSetRect(g_pLog, (xui_rect_t){0, 0, 240, 32});

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

代码讲解

  • 命令 ID:用枚举定义统一命令 ID,菜单/快捷键/按钮触发同一命令。
  • 键盘检测:帧循环中检测 Ctrl+S/Ctrl+Z 组合键,触发 CommandDispatch。
  • 统一入口:xuiCommandDispatch 是命令唯一入口,逻辑只写一次。
  • 焦点路由:pTarget 为 NULL 时命令发给当前焦点控件。

要点与陷阱

  • 统一入口:菜单点击和键盘热键都通过 CommandDispatch,逻辑只写一次。
  • 焦点:pTarget 为 NULL 时命令发给当前焦点控件。
  • 冲突:同一组合键不要绑定多个命令,按焦点链优先级处理。
  • 显示:菜单自动在右侧显示 sShortcut 文本。

进阶练习

  1. 快捷键设置面板:允许用户自定义按键绑定。
  2. 命令面板:Ctrl+Shift+P 弹出命令搜索列表(类 VS Code)。
热键系统效果
图:热键系统渲染效果