eduwin 菜单 API

eduwin 支持三种菜单:主菜单(窗口菜单栏)、系统菜单(标题栏图标菜单)、上下文菜单(右键弹出)。


主菜单

HMENU create_main_menu(HWND win);
HMENU add_main_menu(HMENU main, const WCHAR *text);
int add_menu_item(HMENU popup, const WCHAR *text);
void add_menu_separator(HMENU popup);
HMENU add_sub_menu(HMENU parent, const WCHAR *text);
void on_menu_click(int id, void (*callback)(void *), void *data);

create_main_menu 创建菜单栏并附加到窗口,返回菜单栏句柄。add_main_menu 在菜单栏添加一个顶级弹出菜单(如"文件"),返回该弹出菜单句柄。

add_menu_item 向弹出菜单添加可点击的菜单项,返回自动分配的 ID。add_menu_separator 添加分隔线。add_sub_menu 添加子菜单,返回子菜单句柄。on_menu_click 注册点击回调。

菜单项文本中可以用 (&F) 指定助记符(Alt+F 快捷访问),用 \t 分隔快捷键提示文字(仅显示,快捷键注册见 set_menu_shortcut)。

HMENU main = create_main_menu(win);

HMENU file = add_main_menu(main, L"文件(&F)");
int id_new  = add_menu_item(file, L"新建\tCtrl+N");
int id_open = add_menu_item(file, L"打开\tCtrl+O");
add_menu_separator(file);
int id_exit = add_menu_item(file, L"退出");

on_menu_click(id_new,  on_new,  NULL);
on_menu_click(id_open, on_open, NULL);
on_menu_click(id_exit, on_exit, NULL);

// 带子菜单的"视图"菜单
HMENU view = add_main_menu(main, L"视图(&V)");
int id_status = add_menu_item(view, L"状态栏");
set_menu_checked(id_status, 1);

HMENU sort_sub = add_sub_menu(view, L"排序方式");
int id_sort_name = add_menu_item(sort_sub, L"按名称");
on_menu_click(id_sort_name, on_sort_name, NULL);

系统菜单

int create_sys_menu(HWND win, const WCHAR *text);
void add_sys_separator(HWND win);

向系统菜单(标题栏图标右键菜单)添加自定义项。create_sys_menu 添加一个菜单项并返回自动分配的 ID,add_sys_separator 添加分隔线。回调通过 on_menu_click 注册。

add_sys_separator(win);
int id_sys = create_sys_menu(win, L"关于 (&A)");
on_menu_click(id_sys, on_sys_about, NULL);

上下文菜单

HMENU create_context_menu(void);
void show_context_menu(HMENU menu, HWND relative, int x, int y);

create_context_menu 创建一个空的上下文菜单,add_menu_item / add_menu_separator 添加菜单项。

show_context_menu 在屏幕坐标 (x, y) 处弹出菜单。relative 用于确定菜单弹出后的归属。

HMENU ctx = create_context_menu();
int id_info   = add_menu_item(ctx, L"查看详情");
add_menu_separator(ctx);
int id_delete = add_menu_item(ctx, L"删除");
on_menu_click(id_info,   on_ctx_info,   NULL);
on_menu_click(id_delete, on_ctx_delete, NULL);

上下文菜单的坐标处理

show_context_menu 要求 屏幕坐标,不同场景下获取坐标的方式不同:

普通窗口(WM_CONTEXTMENU):

右键消息自带的坐标已经是屏幕坐标,可直接使用。

// 子类化窗口过程截获 WM_CONTEXTMENU
if (msg == WM_CONTEXTMENU) {
    POINT pt = {LOWORD(lp), HIWORD(lp)};
    show_context_menu(ctx_menu, (HWND)wp, pt.x, pt.y);
}

画布控件(canvas_on_mouse 回调):

画布鼠标回调中的 x, y画布客户区坐标,必须用 ClientToScreen 转换为屏幕坐标:

void on_mouse_down(int btn, int x, int y)
{
    if (btn != 1) return; // 只处理右键

    POINT pt = {x, y};
    ClientToScreen(canvas, &pt);
    show_context_menu(ctx_menu, canvas, pt.x, pt.y);
}

不能直接用 GetCursorPos 替代,因为右键消息不一定来自鼠标(也可能是键盘 Shift+F10 或触控)。


菜单状态控制

void set_menu_checked(int id, int checked);
void set_menu_enabled(int id, int enabled);

set_menu_checked 设置菜单项勾选状态(前面打勾 ✓)。set_menu_enabled 设置菜单项启用/禁用(灰色不可点击)。

set_menu_checked(id_status, 1);   // 勾选
set_menu_enabled(id_paste, 0);    // 禁用

快捷键

void set_menu_shortcut(int id, BYTE vkey, int ctrl, int shift);

为菜单项注册真正的键盘快捷键(加速键)。vkey 是虚拟键码(如 'N''Z'VK_F5),ctrlshift 表示是否需要按下 Ctrl / Shift 键。

set_menu_shortcut(id_new,  'N', 1, 0);    // Ctrl+N
set_menu_shortcut(id_undo, 'Z', 1, 0);   // Ctrl+Z

快捷键提示文字需要在 add_menu_item 的文本中用 \t 指定(如 L"新建\tCtrl+N"),set_menu_shortcut 只负责实际的键盘响应,不修改菜单文字。