本文主要內容爲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在不少功能細節上還需打磨和完善。