前言
本文主要内容為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
屬性。
- type屬性設定為"menubar"則展現為MenuStrip行為,即窗體頂部菜單欄。
- 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.Menu
和
nw.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菜單項有三種類型
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屬性隻能在建立時設定,不能在運作時動态更改。
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在很多功能細節上還需打磨和完善。
如果您認為本文對得起您所閱讀他所花的時間,歡迎點選右下角↘ 推薦。您的支援是我繼續寫作最大的動力,謝謝作者:小小滄海
出處:http://www.cnblogs.com/xxcanghai/
本文位址:http://www.cnblogs.com/xxcanghai/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。