全局快捷键 Shortcut
快捷键是效率的牙齿。HiEasyX 的全局快捷键系统不跟控件抢饭碗——它只在所有控件都处理完消息之后、本帧结束前执行,因此
Ctrl+C在文本框里是复制,在蓝图里也是复制,互不冲突。
核心设计
快捷键系统遵循后处理原则:
- 控件先消费消息(如
TextInput吃掉Ctrl+A做全选)。 HX::End()内部检查剩余未处理的KeyPressed消息。- 如果按键组合匹配已注册的快捷键,触发回调。
这意味着:只要某个控件把消息标记为 Processed,快捷键就不会误触发。
数据结构
HXShortcut
struct HXShortcut {
bool Ctrl = false; // 是否需要按住 Ctrl
bool Shift = false; // 是否需要按住 Shift
bool Alt = false; // 是否需要按住 Alt
int Key = 0; // 虚拟键码,如 'S', VK_F5, VK_DELETE
};
虚拟键码
- 字母数字直接用字符字面量:
'S','1'。 - 功能键用 Windows 虚拟键码:
VK_F5, VK_DELETE,VK_RETURN,VK_ESCAPE`。 - 方向键:
VK_LEFT,VK_RIGHT,VK_UP,VK_DOWN。
API 详解
RegisterShortcut
void RegisterShortcut(const HXShortcut &shortcut, std::function<void()> callback);
参数
| 参数 | 类型 | 说明 |
|---|---|---|
shortcut | HXShortcut | 按键组合。 |
callback | std::function<void()> | 触发时执行的回调。 |
注册时机
建议在程序初始化时一次性注册,不要在每帧的 Window 里重复注册(虽然内部会去重,但没必要浪费 CPU)。
UnregisterShortcut
void UnregisterShortcut(int key, bool ctrl, bool shift, bool alt);
参数
| 参数 | 类型 | 说明 |
|---|---|---|
key | int | 虚拟键码。 |
ctrl | bool | 是否匹配 Ctrl。 |
shift | bool | 是否匹配 Shift。 |
alt | bool | 是否匹配 Alt。 |
精确匹配
注销时必须提供完全一致的修饰键组合。只传 key 而忽略修饰符是注销不掉的。
完整示例:编辑器快捷键
#include <include/hex.h>
#include <include/impl/EasyX/hex_impl_easyx.h>
void SetupShortcuts() {
// Ctrl+S:保存
HX::RegisterShortcut({true, false, false, 'S'}, []() {
SaveProject();
});
// Ctrl+Shift+S:另存为
HX::RegisterShortcut({true, true, false, 'S'}, []() {
SaveProjectAs();
});
// Ctrl+Z:撤销
HX::RegisterShortcut({true, false, false, 'Z'}, []() {
Undo();
});
// Ctrl+Y / Ctrl+Shift+Z:重做
HX::RegisterShortcut({true, false, false, 'Y'}, []() {
Redo();
});
HX::RegisterShortcut({true, true, false, 'Z'}, []() {
Redo();
});
// Delete:删除选中对象(蓝图/树/表通用)
HX::RegisterShortcut({false, false, false, VK_DELETE}, []() {
DeleteSelection();
});
// F5:运行
HX::RegisterShortcut({false, false, false, VK_F5}, []() {
PlayGame();
});
}
int main() {
initgraph(1280, 720);
HX::HXInitForEasyX();
HX::SetBuffer(GetWorkingImage());
BeginBatchDraw();
SetupShortcuts();
while (true) {
HX::HXBegin();
ExMessage msg;
while (peekmessage(&msg)) {
HX::PushMessage(HX::GetHXMessage(&msg));
}
HX::WindowProfile wp;
wp.Title = HXStr("My Editor");
HX::Window(HXStr("main"), wp);
HX::Text(HXStr("Press Ctrl+S to save, F5 to play."));
// ... 其他控件 ...
HX::End();
HX::Render();
FlushBatchDraw();
Sleep(16);
}
return 0;
}
与控件焦点的关系
为什么不冲突?
TextInput 在收到 Ctrl+A 时会把它标记为 Processed。HX::End() 里的快捷键匹配只扫未处理的 KeyPressed 消息,所以文本框里的 Ctrl+A 不会触发全局“全选”动作。
但如果你注册了一个文本框不会消费的组合,比如 Ctrl+Shift+P(打开命令面板),即使在 TextInput 里也能触发。
不要滥用全局快捷键
如果某个操作必须在特定控件激活时才有效(如 Ctrl+B 加粗只有选中文字时才该出现),请把它做成控件的内部逻辑,而不是全局快捷键。全局快捷键适合与当前焦点无关的操作:保存、运行、切换面板、撤销/重做、删除选中项。
总结
| 需求 | 做法 |
|---|---|
| 注册全局快捷键 | RegisterShortcut({ctrl, shift, alt, key}, callback) |
| 注销 | UnregisterShortcut(key, ctrl, shift, alt) |
| 避免与 TextInput 冲突 | 框架自动处理,只匹配未 Processed 的消息 |
| 支持组合键 | 同时注册 Ctrl+Z 和 Ctrl+Shift+Z 即可 |
| 功能键 | 使用 VK_F1 ~ VK_F12、VK_DELETE 等 |
快捷键注册一次,受益整个生命周期。把最常用的命令绑上去,你的用户会感谢你的 ⚡