本文主要内容为nw.js官方文档中没有提到,而在实际入手开发过程当中才碰到的问题以及经验的汇总。
详情请查看官方文档:http://docs.nwjs.io/en/latest/References/Menu/git
在聊nwjs中的Menu以前先说下在传统window桌面端应用开发中的两种常见的菜单。github
第一种:MenuStrip,菜单栏,一般在主窗体中的顶部,横向展现。如图:windows
app
第二种:ContextMenu,上下文菜单,也就是右键菜单,关联某个元素,再某个元素上点击右键展示的菜单
函数
而在nw.js中,将windows系统的这两种菜单结合成一个对象:nw.Menu
。
而区分的方法在于构造的配置对象的 type
属性。ui
官方说明:this
/** * Object that contains options to use while creation of nw.Menu. example: new nw.Menu(MenuOption) */ interface MenuOption { /** * {string} (Optional) two types are accepted by this method: "menubar" or "contextmenu". The value is set to "contextmenu" by default. */ type: string; }
在nwjs中,有关菜单的只有两个对象,nw.Menu
和 nw.MenuItem
。其中nw.Menu更多的功能与行为应该称之为 菜单集合。而 nw.MenuItem才是真正的 菜单项。3d
用一张图来描述Menu和MenuItem对应的实际结构如图:

如上图,红框为Menu,橙色为MenuItem,全部MenuItem的集合均为Menu,而MenuItem的子集的类型为Menu。code
接下来建立一个上图中的顶部菜单栏的代码示例:orm
//建立一个顶部菜单栏,类型为:menubar var menuBar = new nw.Menu({ type: 'menubar' }); //建立一个一级菜单项-文件 var fileMenu = new nw.MenuItem({ label: "文件", }); //文件菜单的子菜单集合,类型为:contextmenu var fileMenuColl = new nw.Menu({ type: "contextmenu" }); //设定一级菜单文件的子菜单 fileMenu.submenu = fileMenuColl; // 建立一级菜单文件的子菜单:打开 var openMenu = new nw.MenuItem({ label: "打开", click: function () { console.log("打开"); }, }); //将打开菜单项 添加入文件子菜单集合中 fileMenuColl.append(openMenu); //建立一级菜单文件的子菜单:资源管理器 var explorerMenu = new nw.MenuItem({ label: "资源管理器", click: function () { console.log("资源管理器"); }, }); fileMenuColl.append(explorerMenu); //最后将一级菜单项文件 添加入菜单栏 menuBar.append(fileMenu); //设定窗体菜单栏 nw.Window.get().menu = menuBar;
效果以下:

问题见代码示例。
如将上述代码化简后:
var menuBar = new nw.Menu({ type: 'menubar' }); var fileMenu = new nw.MenuItem({ label: "一级菜单" }); var fileMenuColl = new nw.Menu(); fileMenu.submenu = fileMenuColl; var openMenu = new nw.MenuItem({ label: "二级菜单" }); fileMenuColl.append(openMenu); menuBar.append(fileMenu);//关键,放在最后没问题,正常! win.menu = menuBar;
效果以下:

可是若是将menuBar.append(fileMenu)
放在刚刚建立完fileMenu
以后的话:
var menuBar = new nw.Menu({ type: 'menubar' }); var fileMenu = new nw.MenuItem({ label: "一级菜单" }); //关键,建立fileMenu后马上添加入菜单栏,会发生没法显示二级菜单的问题!!! menuBar.append(fileMenu); var fileMenuColl = new nw.Menu(); fileMenu.submenu = fileMenuColl; var openMenu = new nw.MenuItem({ label: "二级菜单" }); fileMenuColl.append(openMenu); win.menu = menuBar;
问题:会出现二级菜单没法打开,只能看到一级菜单的问题。
另外,若是再建立fileMenu时直接在构造函数的配置对象中制定了submenu的话,也能够避规此问题。
私觉得这个应该属于nwjs的一个bug或是一个缺陷。理论上来说都属性引用类型,先append再新增,或先新增再append应该都是能够的。至少在C#的WinForm开发菜单时是这样的。
缘由不明。
解决方案:只能在开发过程当中严格遵照: 全部级别的菜单项,必须所有建立完成后最后再被父级append。
在windows应用程序中:
顶部菜单栏能够只有一级菜单,而没有其下属二级菜单。没有二级菜单也可实现相应各个点击事件等等,不强制要求必须有二级菜单。
而在nwjs中:
顶部菜单栏的纯一级菜单,即没有二级菜单项的一级菜单没法响应点击事件,其设定的click事件也是无效的,必须在二级菜单及更高级别的菜单中才能够响应点击事件。
这就带来一个问题:
在把纯windows应用程序使用nwjs重写时,那些纯一级菜单就必须折叠到二级菜单中,才能使用。
另外:
nwjs中只有顶部菜单受此影响,右键菜单不受此影响。
这一点官方也有说明:
To create a menubar, usually you have to create a 2-level menu and assign it to win.menu
要建立一个窗体顶部菜单栏,必须使用二级菜单,并赋值给win.menu
我的猜想:
nwjs只因此这样作是由于在mac OS系统中,彷佛不支持纯一级菜单项。因此nwjs为了要兼容三方平台,统一行为,因此屏蔽了菜单栏的纯一级菜单项的点击功能,让其不具备实际功能,只能打开二级菜单栏。
MenuItem菜单项有三种类型normal
,checkbox
,separator
。
normal
:标准模式,也是默认模式,纯文本菜单项。
checkbox
:可选中的菜单项,前面会有一个对勾,重复点击状态来回切换。
separator
:分割菜单项,展示为一条直线分隔符。
示例代码:
var reloadMenu = new nw.MenuItem({ label: "刷新", type: "normal", }); var separatorMenu = new nw.MenuItem({ type: "separator" }); var checkMenu = new nw.MenuItem({ type: "checkbox", label: "是否展示缩略图" }); var exitMenu = new nw.MenuItem({ label: "退出", });
效果以下:

另外:MenuItem的type属性只能在建立时设定,不能在运行时动态更改。
在windows系统中:
菜单栏中的文字后面的括号下划线英文,是展开此菜单栏的快捷键,方式是 ALT+对应英文字母
。
以下图,展开文件菜单的快捷键是 ALT+F
,执行文件菜单中的关闭菜单项的快捷键是 ALT+C
。

在nwjs中:
也提供了相似的功能,是MenuItem的key
属性和modifiers
属性。
key
属性用来设定触发的快捷键。
modifiers
属性用来设定跟快捷键相关联的修饰键。
如咱们要设定菜单项刷新
的快捷键为F5
则能够:
var reloadMenu = new nw.MenuItem({ label: "刷新", key: "F5", click: function () { pageWindow.location.reload(); }, });
如要设定为快捷键为ALT+R
则能够:
var reloadMenu = new nw.MenuItem({ label: "刷新", key: "r", modifiers:"alt", click: function () { pageWindow.location.reload(); }, });
展示效果为:

nwjs的问题在于:
nwjs中不支持设定菜单栏的一级菜单的快捷键。
也就是不管如何设定,一级菜单都不会相应快捷键自动弹出显示。没法实现windows系统中的效果。只有二级菜单及如下才生效。
另外:
若是给一个含有三级菜单的二级菜单设定快捷键,也就是给一个包含子集展开项设定了快捷键,其行为也不会像windows系统中那样展开他的下属菜单集合,而是直接执行当前它自己的click
事件。
//刷新菜单的下属子集 var reloadMenuColl = new nw.Menu(); reloadMenuColl.append(new nw.MenuItem({ label: "刷新二级" })); /** 操做-刷新 */ var reloadMenu = new nw.MenuItem({ label: "刷新", key: "r", modifiers: "alt", click: function () { pageWindow.location.reload(); }, submenu: reloadMenuColl });
如图:

按下快捷键ALT+R
,直接刷新了页面,而没有展开二级菜单。但若是用鼠标点击那个刷新
菜单的话,是不管如何也不会触发刷新操做的。但经过快捷键反倒能够...
有关这一点nwjs的官方文档中并无特殊说明,我姑且认定为没有太大影响的缺陷吧。详见:http://docs.nwjs.io/en/latest/References/MenuItem/
本文上述源代码已托管至Github:
https://github.com/xxcanghai/nwjs-demo/tree/master/menu
欢迎Start & Follow~
以上为nw.js入坑两周来的有关菜单开发的小记。由于咱们要兼容XP系统,因此没得选,只能用nw.js。其github上的issue区也是红红火火,只能说感受nw在不少功能细节上还需打磨和完善。