前言

本文主要内容为nw.js官方文档中没有提到,而在实际入手开发过程中才碰到的问题以及经验的汇总。

详情请查看官方文档:http://docs.nwjs.io/en/latest/References/Menu/

1. MenuStrip与ContextMenu

在聊nwjs中的Menu之前先说下在传统window桌面端应用开发中的两种常见的菜单。

windows中的MenuStrip

第一种:MenuStrip,菜单栏,通常在主窗体中的顶部,横向展示。如图:

windows中的ContextMenu

第二种:ContextMenu,上下文菜单,也就是右键菜单,关联某个元素,再某个元素上点击右键展现的菜单

nw.js中的nw.Menu

而在nw.js中,将windows系统的这两种菜单结合成一个对象:nw.Menu

而区分的方法在于构造的配置对象的 type属性。

  1. type属性设定为"menubar"则展现为MenuStrip行为,即窗体顶部菜单栏。
  2. type属性设定为"contextmenu"则展现为右键菜单行为。默认为右键菜单

官方说明:

/**
* 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;
}

2. nwjs中的多级菜单结构组成

在nwjs中,有关菜单的只有两个对象,nw.Menunw.MenuItem。其中nw.Menu更多的功能与行为应该称之为 菜单集合。而 nw.MenuItem才是真正的 菜单项

用一张图来描述Menu和MenuItem对应的实际结构如图:



如上图,红框为Menu,橙色为MenuItem,所有MenuItem的集合均为Menu,而MenuItem的子集的类型为Menu。

接下来创建一个上图中的顶部菜单栏的代码示例:

//创建一个顶部菜单栏,类型为: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;

效果如下:

3. MenItem必须最后再被Menu.append添加

问题见代码示例。

如将上述代码化简后:

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。

4. nwjs顶部菜单栏不支持纯一级菜单

在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

http://docs.nwjs.io/en/latest/References/Menu/#synopsis

个人猜测:

nwjs只所以这样做是因为在mac OS系统中,似乎不支持纯一级菜单项。所以nwjs为了要兼容三方平台,统一行为,所以屏蔽了菜单栏的纯一级菜单项的点击功能,让其不具有实际功能,只能打开二级菜单栏。

5. 关于MenuItem

关于type属性

MenuItem菜单项有三种类型normalcheckboxseparator

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属性只能在创建时设定,不能在运行时动态更改。

key属性不支持菜单栏形式下的一级菜单

在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在很多功能细节上还需打磨和完善。

使用JS开发桌面端应用程序NW.js-1-Menu菜单的使用小记的更多相关文章

  1. 使用JS开发桌面端应用程序NW.js-2-开发问题小记

    前言 本文为开发nw中遇到的各种问题,仅以记录供备忘以及遇到相同问题的人的一点点解决思路. 1. package.json中的window字段无效 原因:package.json中的window字段, ...

  2. 使用JS开发桌面端应用程序NW.js-3-开发问题小记

    前言 因为我们的项目是2C的,而XP系统是最大的用户量占比,所以只能使用nw开发而不能用Electron,本文谨记开发nw过程中遇到的各种问题以及解决方案. nw.Window.open打开新窗口不能 ...

  3. 鸿蒙js开发7 鸿蒙分组列表和弹出menu菜单

    鸿蒙入门指南,小白速来!从萌新到高手,怎样快速掌握鸿蒙开发?[课程入口]目录:1.鸿蒙视图效果2.js业务数据和事件3.页面视图代码4.跳转页面后的视图层5.js业务逻辑部分6.<鸿蒙js开发& ...

  4. arcpy+PyQt+py2exe快速开发桌面端ArcGIS应用程序

    前段时间有一个项目,大体是要做一个GIS数据处理工具. 一般的方法是基于ArcObjects来进行开发,因为我对ArcObjects不太熟悉,所以就思考有没有其他简单快速的方法来做. 在查看ArcGI ...

  5. heX——基于 HTML5 和 Node.JS 开发桌面应用

    heX 是网易有道团队的一个开源项目,允许你采用前端技术(HTML,CSS,JavaScript)开发桌面应用软件的跨平台解决方案.heX 是你开发桌面应用的一种新的选择,意在解决传统桌面应用开发中繁 ...

  6. node.js 开发桌面程序, 10个令人惊讶的NodeJS开源项目

    用 node-webkit 开源框架. 做企业站,杠杠地 包括电子书和支付宝系统都是node开发的,. 接收传感器发送的数据再运算...对水泵.风机.空调这些硬件进行远程控制. 细数10个令人惊讶的N ...

  7. heX:用HTML5和Node.JS开发桌面应用

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  8. (转)heX——基于 HTML5 和 Node.JS 开发桌面应用

    本文转载自:http://techblog.youdao.com/?p=685 简介:heX,一个允许你采用前端技术(HTML,CSS,JavaScript)开发桌面应用软件的跨平台解决方案.是你开发 ...

  9. 如何用原生js开发一个Chrome扩展程序

    原文地址:How to Build a Simple Chrome Extension in Vanilla JavaScript 开发一个Chrome扩展程序非常简单,只需要使用原生的js就可以完成 ...

随机推荐

  1. Spring定时任务解决博客缓存数据更新问题

    最近在做博客系统的时候,由于很多页面都有右边侧边栏,内容包括博客分类信息,归档日志,热门文章,标签列表等,为了不想每次访问页面都去查询数据库,因为本身这些东西相对来说是比较固定的,但是也有可能在网站后 ...

  2. pg_config executable not found

    Error: pg_config executable not found. Please add the directory containing pg_config to the PATH or ...

  3. PRINCE2学习

    今天对PRINCE2中提及的7大主题进行学习,主要的内容是通过概述和PMBOK之间的对比做一些总结,每个主题所包含的过程和方法并没有太多涉及,没有对整个体系有全面深入的学习,有些断章取义的地方也请博友 ...

  4. 20155206 2016-2017-2 《Java程序设计》第5周学习总结

    20155206 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 Java中所有错误都会被打包为对象,运用try.catch,可以在错误发生时显示友好的错误信 ...

  5. js的搜索遍历精讲

    搜索算法应该算是每种算法的重点与难点了,本文将讲解JavaScript中的各种常用的遍历算法:希望对大家有所帮助. 深度优先遍历顺序:1245367      广度优先遍历:1234567 1.深度优 ...

  6. Omi官方插件系列 - omi-transform介绍

    原文链接 - https://github.com/AlloyTeam/omi/tree/master/tutorial 写在前面 Omi框架正式发布了omi-transform作为Omi组件化开发特 ...

  7. 性能调优之MYSQL高并发优化

    性能调优之MYSQL高并发优化   一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实施之 ...

  8. (转)Java ConcurrentModificationException异常原因和解决方法

    转载自:http://www.cnblogs.com/dolphin0520/p/3933551.html 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会 ...

  9. Python爬虫 Urllib库的高级用法

    1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...

  10. 跨web浏览器的IC卡读卡器解决方案

    BS结构的程序,如果要与IC卡读卡器通信本身就是件不容易解决的事情.微软的activex ocx技术将这种应用限制在IE浏览器上了,不兼容其它的浏览器.而Chrome使用插件也不兼容IE和其他的浏览器 ...