扩展菜单控件 (SMenuEx)¶
基本信息¶
- 类名:
SMenuEx - 基类:
SHostWnd(宿主窗口,实现IMenuEx) - 菜单项标签:
menuItem(对应SMenuExItem,继承自SWindow) - 功能描述:基于 SOUI 布局系统的扩展菜单,菜单项本身就是
SWindow派生控件,因此可以完全自定义外观(皮肤、布局、子控件),并支持图标、勾选、单选、悬停高亮、子菜单等特性。
重要纠正:旧文档把
SMenuEx当成普通布局控件并列出iconSkin/itemHeight/iconMargin/textMargin/separatorHeight/maxWidth等“控件属性”,这些在SMenuEx源码(SOUI/include/helper/SMenuEx.h)中并不存在——SMenuEx本身没有SOUI_ATTRS。它的外观由布局 XML(根窗口样式与皮肤)决定;可配置的 XML 属性只存在于菜单项menuItem(SMenuExItem)上。下为对照源码后的正确属性表。
菜单项属性 (<menuItem>)¶
每个菜单项在布局 XML 中是一个 <menuItem> 标签,对应 SMenuExItem,其属性在 SMenuExItem::SOUI_ATTRS_BEGIN 中声明:
| 属性名 | 类型 | 默认值 | 是否重绘 | 说明 |
|---|---|---|---|---|
icon |
int | -1 |
是 | 图标索引(指向菜单项皮肤中的图标帧) |
check |
int | 0 |
是 | 是否勾选(1 勾选,0 不勾选) |
radio |
int | 0 |
是 | 是否为单选按钮样式(1 单选,0 否) |
hotKey |
char | 0(无) |
否 | 热键字符(显示在菜单项右侧) |
说明:
check/radio虽以int声明(ATTR_INT),但语义上是布尔开关。菜单项其余外观(背景、文本颜色、字体、间距等)由布局 XML 中menuItem的皮肤 / 样式决定,非独立 XML 属性。
使用示例¶
布局 XML 示例¶
SMenuEx 通过 LoadMenu / LoadMenu2 从一个布局 XML 加载,根节点下直接放置 <menuItem>:
<menuExRoot>
<root>
<menuItem text="新建" icon="0">
<menuItem id="100" text="文件"/>
<menuItem id="101" text="文件夹"/>
</menuItem>
<menuItem text="编辑" icon="1">
<menuItem id="200" text="复制" hotKey="C"/>
<menuItem id="201" text="粘贴" hotKey="V"/>
</menuItem>
<menuItem id="300" text="自动保存" check="1"/>
<menuItem id="301" text="只读模式" radio="1"/>
<menuItem id="302" text="禁用项" disable="1"/>
</root>
</menuExRoot>
代码加载与弹出¶
#include <helper/SMenuEx.h>
SMenuEx m_mainMenu;
void OnInit()
{
if (!m_mainMenu.LoadMenu(L"layout:menu_main_ex"))
{
SASSERT_FMT(FALSE, "加载扩展菜单失败");
return;
}
}
void ShowContextMenu(CPoint pt)
{
int nCmd = m_mainMenu.TrackPopupMenu(
TPM_RETURNCMD | TPM_RIGHTALIGN, pt.x, pt.y, m_hWnd);
if (nCmd)
OnCommand(nCmd);
}
动态增删菜单项¶
void MenuExApiDemo(SMenuEx& menu)
{
// 真实 API:InsertMenu(UINT uPosition, UINT uFlags, int nIDNewItem, LPCTSTR strText, int iIcon)
menu.InsertMenu(0, MF_STRING, 1000, L"新菜单项", /*iIcon*/ -1);
// 启用 / 禁用、勾选
menu.EnableMenuItem(1000, MF_BYCOMMAND | MF_GRAYED);
menu.CheckMenuItem(300, MF_BYCOMMAND | MF_CHECKED);
// 删除与销毁
menu.DeleteMenu(1000, MF_BYCOMMAND);
}
事件处理¶
扩展菜单的菜单项均为 SWindow 派生控件,选中时向宿主发送 EventCmd;悬停时触发 EventMenuHover:
bool OnMenuCommand(EventCmd* pEvt)
{
int nID = pEvt->idFrom;
switch (nID)
{
case 100: CreateNewFile(); break;
case 200: OpenFile(); break;
case 201: SaveFile(); break;
}
return true;
}
bool OnMenuHover(EventMenuHover* pEvt)
{
SMenuExItem* pItem = sobj_cast<SMenuExItem>(pEvt->sender);
if (pItem)
UpdateStatusText(pItem->GetText());
return true;
}
常见问题¶
SMenuExvsSMenu:SMenuEx的菜单项是真正的SWindow(menuItem),外观可完全自定义;SMenu基于原生HMENU,由SMenuAttr控制样式。需要高度自定义时优先选SMenuEx。- 项属性不生效:确认属性写在
<menuItem>上,而不是根节点或<sep>上。 - 取消“控件属性”误解:
SMenuEx没有itemHeight/iconMargin/maxWidth这类属性,项高度/间距应通过项的皮肤尺寸或布局参数控制。