zoukankan      html  css  js  c++  java
  • 使用JS开发桌面端应用程序NW.js-1-Menu菜单的使用小记

    前言

    本文主要内容为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在很多功能细节上还需打磨和完善。

  • 相关阅读:
    Spring基础问答--来自http://my.oschina.net/u/2260184/blog/540576博客
    一些java异常集
    java汉字乱码解决办法
    oracle数据库-表空间基础语法及举例
    大师传承-java代码编写的30条建议
    oracle的表分区的定义与操作
    数据库备份
    PL/SQL语言编程中异常分类及其处理
    PL/SQL编程基本语法学习
    国药准字和药品本位码
  • 原文地址:https://www.cnblogs.com/xxcanghai/p/6845160.html
Copyright © 2011-2022 走看看