使用JS開發桌面端應用程序NW.js-1-Menu菜單的使用小記

前言

本文主要內容爲nw.js官方文檔中沒有提到,而在實際入手開發過程當中才碰到的問題以及經驗的彙總。
詳情請查看官方文檔:http://docs.nwjs.io/en/latest/References/Menu/git

1. MenuStrip與ContextMenu

在聊nwjs中的Menu以前先說下在傳統window桌面端應用開發中的兩種常見的菜單。github

windows中的MenuStrip

第一種:MenuStrip,菜單欄,一般在主窗體中的頂部,橫向展現。如圖:windows

app

windows中的ContextMenu

第二種:ContextMenu,上下文菜單,也就是右鍵菜單,關聯某個元素,再某個元素上點擊右鍵展示的菜單
函數

nw.js中的nw.Menu

而在nw.js中,將windows系統的這兩種菜單結合成一個對象:nw.Menu
而區分的方法在於構造的配置對象的 type屬性。ui

  1. type屬性設定爲"menubar"則展示爲MenuStrip行爲,即窗體頂部菜單欄。
  2. type屬性設定爲"contextmenu"則展示爲右鍵菜單行爲。默認爲右鍵菜單

官方說明: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;
}

2. nwjs中的多級菜單結構組成

在nwjs中,有關菜單的只有兩個對象,nw.Menunw.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;

效果以下:

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在不少功能細節上還需打磨和完善。

相關文章
相關標籤/搜索