菜单控件 (SMenu)¶
Warning
The current page still doesn't have a translation for this language.
You can read it through google translate.
基本信息¶
- 类名:
SMenu - 对应接口:
IMenu(TObjRefImpl<IMenu>) - XML 标签:无独立的布局标签。菜单通过
LoadMenu/LoadMenu2从 XML 资源加载,本质是对 Win32 原生HMENU的封装,不是布局控件,没有SOUI_ATTRS,也不参与 XML 布局树。 - 功能描述:创建弹出式菜单,支持多级子菜单、图标、勾选/单选、禁用、快捷键提示等,是构建右键菜单与系统菜单的基础组件。
重要纠正:旧文档把
SMenu当成普通布局控件并列出itemWidth/textOffset/iconBarWidth/separatorHeight/minWidth/colorBkgnd等“控件属性”,这些属性在源码中并不存在。SMenu自身没有任何SOUI_ATTRS。菜单的样式由SMenuAttr(menuattr)描述,菜单项由原生HMENU项构成,属性在SMenu::BuildMenu中解析。下文为对照源码(SOUI/include/helper/SMenu.h、SOUI/src/helper/SMenu.cpp)后的正确属性表。
菜单样式属性 (menuattr)¶
SMenu::LoadMenu2 在加载时会把 <menu> 根节点的属性交给 SMenuAttr 对象解析(pMenuAttr->InitFromXml(&xmlMenu)),因此下面这些属性写在 <menu> 根标签上。定义见 SOUI_ATTRS_BEGIN() of SMenuAttr。
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
itemSkin |
skin | 系统皮肤 SKIN_SYS_MENU_SKIN |
菜单项背景皮肤(含正常/选中两态)。必填,若未设置 LoadMenu2 会报错并返回 FALSE |
sepSkin |
skin | 系统皮肤 SKIN_SYS_MENU_SEP |
分隔线皮肤 |
checkSkin |
skin | 系统皮肤 SKIN_SYS_MENU_CHECK |
勾选 / 圈选标记皮肤 |
iconSkin |
skin | 无(NULL) |
图标皮肤;也可在代码中调用 SMenu::SetIconSkin 设置 |
itemHeight |
layoutsize | 25(dp) |
菜单项高度。若未显式设置且无 itemSkin,OnInitFinished 中回退为 30dp |
iconMargin |
layoutsize | 2(dp) |
图标到菜单项边缘的内边距 |
textMargin |
layoutsize | 5(dp) |
文本左右内边距 |
maxWidth |
layoutsize | 250(dp) |
菜单项最大宽度;未设置时取 -1(自动) |
iconSize |
layoutsize × 2 | 16 × 16(dp) |
图标尺寸(宽、高) |
font |
font | 系统菜单字体 | 菜单文本字体 |
colorText |
color | 系统 COLOR_MENUTEXT |
正常文本颜色 |
colorTextSel |
color | 系统 COLOR_HIGHLIGHTTEXT |
选中项文本颜色 |
colorTextGray |
color | 系统 COLOR_GRAYTEXT |
禁用项(灰)文本颜色 |
trCtx |
string | ""(空) |
翻译上下文,配合 SOUI 翻译模块使用 |
纠正:源码中 没有
colorBkgnd/minWidth/textOffset/iconBarWidth/separatorHeight这些属性;菜单背景完全由itemSkin决定。
菜单项属性 (<item>)¶
SMenu 的菜单项由原生 HMENU 项构成,属性在 SMenu::BuildMenu(SOUI/src/helper/SMenu.cpp)中解析,不是 SOUI_ATTRS 宏声明的标准控件属性。支持的 XML 属性如下:
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
text |
string | "" |
菜单项文本;也可直接写在 <item> 标签体内 |
id |
int | 0 |
菜单项命令 ID |
icon |
int | -1 |
图标在 iconSkin 中的索引 |
check |
bool | false |
是否带勾选标记(MF_CHECKED) |
radio |
bool | false |
是否单选圈选标记(MFT_RADIOCHECK + MF_CHECKED) |
disable |
bool | false |
是否禁用该项(MF_GRAYED) |
popup |
bool | false |
强制作为子菜单弹出(即使没有子节点) |
userData |
uint | 0 |
附加用户数据 |
contextHelpId |
uint | 0 |
上下文帮助 ID |
<sep> 标签表示分隔线,无属性。
纠正:旧文档中的
iconIndex/enable/hotkey属性名无效。图标索引属性名为icon(不是iconIndex),禁用属性名为disable(不是enable);hotkey通过 Win32 加速键设置,不会从 XML 解析。
使用示例¶
XML 资源示例¶
菜单通常定义在独立的 uires/xml 资源文件中,例如 menu_main.xml:
<menu itemSkin="skin_menu_item" iconSkin="skin_menu_icon"
itemHeight="26" iconMargin="4" textMargin="8">
<item text="新建" icon="0">
<item id="100" text="文件"/>
<item id="101" text="文件夹"/>
</item>
<sep/>
<item id="200" text="打开" icon="1"/>
<item id="201" text="保存" icon="2"/>
<sep/>
<item id="300" text="自动保存" check="1"/>
<item id="301" text="只读模式" radio="1"/>
<item id="302" text="禁用项" disable="1"/>
<item text="最近文件">
<item id="400" text="文档1.txt"/>
<item id="401" text="文档2.txt"/>
</item>
</menu>
代码加载与弹出¶
#include <helper/SMenu.h>
// 成员变量
SMenu m_contextMenu;
void OnInit()
{
// 从资源加载(resId 形如 "layout:menu_main")
if (!m_contextMenu.LoadMenu(L"layout:menu_main"))
{
SASSERT_FMT(FALSE, "加载菜单失败");
return;
}
}
// 右键菜单:弹出并返回命令 ID
LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
if (pt.x == -1 && pt.y == -1)
{
CRect rc;
GetWindowRect(&rc);
pt.SetPoint(rc.left + 5, rc.top + 5);
}
int nCmd = m_contextMenu.TrackPopupMenu(
TPM_RETURNCMD | TPM_RIGHTALIGN, pt.x, pt.y, m_hWnd);
if (nCmd)
OnCommand(nCmd); // 处理菜单命令
return 0;
}
代码动态操作¶
void MenuApiDemo()
{
SMenu menu;
menu.LoadMenu(L"layout:menu_demo");
// 追加 / 插入菜单项(真实 API:AppendMenu / InsertMenu)
menu.AppendMenu(MF_STRING, 1000, L"新菜单项", /*iIcon*/ -1);
menu.InsertMenu(0, MF_STRING, 1001, L"插入的菜单项", /*iIcon*/ 0);
// 启用 / 禁用、勾选
menu.EnableMenuItem(1000, MF_BYCOMMAND | MF_GRAYED);
menu.CheckMenuItem(300, MF_BYCOMMAND | MF_CHECKED);
// 删除与销毁
menu.DeleteMenu(1001, MF_BYCOMMAND);
}
事件处理¶
菜单项被选中后,TrackPopupMenu 返回对应的命令 ID(TPM_RETURNCMD 模式),由调用方统一处理;也可在宿主窗口中响应 EventCmd:
bool OnMenuCommand(EventCmd* pEvt)
{
int nID = pEvt->idFrom;
switch (nID)
{
case 100: CreateNewFile(); break;
case 200: OpenFile(); break;
case 201: SaveFile(); break;
}
return true;
}
常见问题¶
LoadMenu2返回FALSE:通常是<menu>根节点未设置itemSkin。- 图标不显示:确认设置了
iconSkin,且<item>的icon索引在皮肤范围内。 - 文本颜色不生效:普通文本用
colorText,选中项用colorTextSel,禁用项用colorTextGray,三者不要混用。 - 子菜单不弹出:除了包含子节点外,也可显式设置
popup="1"强制子菜单。