nw
实例var nw = require('nw.gui')
获取nw
即可//获取当前窗口
var currentWindow = nw.Window.get();//基本上所有的原生界面对象都继承自NodeJS中的EventEmitter
currentWindow.on('minimize', e=>alert('窗口已被最小化'));
和web应用类似,如果引起某些错误,应用可能崩溃(并且可能没有异常会被抛出),所以一些好的习惯是:
APIs | 描述 |
---|---|
App | 设置应用基础功能,包括打开已绑定类型的本地文件、访问manifest文件、注册全局快捷键或退出应用等 |
Window | 操作一个或多个窗口,响应窗口事件等 |
Screen | 用一个单例对象,取得屏幕信息,并响应屏幕分辨率更改、增加屏幕等事件 |
Menu | 用来创建窗口菜单、托盘菜单或右键菜单 |
File对话框 | 用文件对话框来打开文件或保存文件等 |
Tray | 管理托盘状态图标 |
Clipboard | 访问系统剪贴板 |
Shell | 调用系统默认应用打开文件等 |
NW.js应用有多种办法打开文件,此处谈论的是打开关联的文件类型;也就是说如果我们开发一个文本编辑器,那么我们希望在系统中右键单击一个txt文件出现的“open with...”菜单中,能用我们的应用直接打开它
nw path/to/app path/to/file.txt
nw path/to/app path/to/a.txt path/to/b.txt
App.argv
属性,其返回一个参数数组//如果要实际运行例子,需要打包,否则无法获取参数
var args = nw.App.argv;
if (args.length) {
args.forEach(filepath=>{
//检查文件是否合法并进行某些操作
});
}
open
事件//此时的参数是文件路径的字符串
nw.App.on('open', filepath=>{
//操作文件
});
application data
目录路径所有操作系统都会提供一个默认的文件夹,用来关联每个用户及每个程序,以保存个人设置、应用支持文件,以及某些特定数据;为了避免在程序中硬编码每个平台的对应文件夹,可以用
App.dataPath
属性统一取得其路径
实际取得的值( 表示manifest文件中配置的应用名 ): - Win: $LOCALAPPDATA%/<name>
- Linux: ~/.config/<name>
- Mac: ~/Library/Application Support/<name>
//保存配置文件到`application data`目录的例子
var
fs = require('fs')
,path = require('path')
,settings = {
'show_sidebar': true,
'show_icons': false
}
,settingsFile = path.join(nw.App.dataPath, 'mySettings.json')
;
fs.writeFileSync(settingsFile, JSON.stringify(settings));
fs.readFile(settingsFile, 'utf8', (err, data)=>{
var saved = JSON.parse(data);
console.log(saved);
});
正如上一篇中介绍的,应用使用
package.json
作为主配置文件,尽管可以藉由Node.js取得其引用,但更方便的方法是使用App
中的属性
var manifestData = nw.App.manifest;
alert(manifestData.name);
如果以NW.js应用正常的生命周期来理解,应用打开的所有窗口都依次关闭后,整个应用才能退出;不过有两种方法可以干预这一进程:
该方法会触发各子窗口的close事件,从而提供了执行清理动作的机会:
//index.html(第一个窗口,主窗口)
window.open('settings.html');
var currWin = nw.Window.get();
currWin.on('close', function() {
nw.App.closeAllWindows();
this.close(true);
});//settings.html(第二个窗口,设置窗口)
var currWin = nw.Window.get();
currWin.on('close', function() {
//退出之前,在这里保存设置数据
this.close(true);
});
与上一种方法不同,该方法不发出任何关闭信号,程序直接退出
利用
App.registerGlobalHotKey()
方法,并结合Shortcut API(http://docs.nwjs.io/en/latest/References/Shortcut/)
,可以注册系统层级的组合快捷键,即便是在应用失去焦点、最小化或缩至托盘后,这些快捷键仍能生效
nw.App.registerGlobalHotKey(
new nw.Shortcut({
key: 'Ctrl+Alt+A',
active: function() { //组合键被正确按下时的回调
nw.Window.get().show(); //显示当前窗口
console.log('按下了 ', this.ke);
}
})
);
shortcut.on('active', function() {...})
App.unregisterGlobalHotKey(shortcut)
解除注册[文档地址](http://docs.nwjs.io/en/latest/References/App/)
在NW.js中,Window API 只不过是对DOM中window对象的一层包装,很多(并非所有)方法和属性继承了后者的用法,同时window对象也是 Node.js EventEmitter 的实例,可以对move或resize等实现事件监听
//取得当前窗口
var currWin = nw.Window.get();//向get()方法中传递一个DOM引用,取得其他窗口
var win = nw.Window.get( window.open('other.html') );//也可以用 nw.Window.open(url[,options][,callback]) 方法
var win = nw.Window.open('other.html', {
// options中的选项和manifest中 [相关参数](http://docs.nwjs.io/en/latest/References/Manifest%20Format/#window-subfields) 一致
position: 'center',
width: 600,
height: 500,
focus: true,
//也有几个额外定义的选项
'new-instance': true, //在新的Webkit进程中打开窗口
'inject-js-start': 'path/to/js', //在文档loaded前注入的脚本
'inject-js-end': 'path/to/js' //在文档unloaded前注入的脚本
});
NW.js窗口显示后,代码执行等后台工作还需要一段时间,为了更好等用户体验,可以有意先隐藏窗口
{
"window": {
"show": false
}
}
//html
window.onload = function() {
nw.Window.get().show();
}
开头提过:“在NW.js中,Window API 只不过是对DOM中window对象的一层包装”,但很多功能受限无法访问,为了获得原始的引用,可以使用Window.window
var currWin = nw.Window.get();
var nav = currWin.window.navigator;
var lang = nav.language;
console.log(lang); //zh-CN
在nw.Window实例中,最常见的属性包括 width, height, x, y 等,前两者不言自明,x,y则表示窗口之于屏幕的绝对位置
var currWin = nw.Window.get();
console.log({
x: currWin.x,
y: currWin.y,
width: currWin.width,
height: currWin.height
});
setTimeout(function() {
currWin.x = 100;
currWin.width = 600;
});
可以用以下方法限制窗口大小;但 max/min 不能和 setResizable(false) 方法同时使用,否则会无效
win.setResizable(bool);
win.setMaximumSize(maxW, maxH);
win.setMinimumSize(minW, minY);
可以用如下方法设定窗口位置,其中to设定绝对值,by设定相对偏移量
win.setPosition(posiStr); //有效参数为 null | 'center' | 'mouse'
win.moveTo(x, y);
win.moveBy(x, y);
win.resizeTo(w, h);
win.resizeBy(w, h);
窗口位置或尺寸变化时,触发以下事件
win.on('move', (x,y)=>console.log(x, y));
win.on('resize', (w,h)=>console.log(w, h));
每个桌面窗口都有几种不同的状态:minimized
,maximized
,hidden
,focused
,blur
和closed
;可以用以下方法设置:
var other = nw.Window.get().open('other.html');other.minimize();
other.restore(); //从最小化恢复other.show();
other.hide();other.maximize();
other.unmaximize();other.focus();
可以简单的设置isFullScreen属性:
win.isFullScreen = true;
也可以调用方法并监听事件:
win.enterFullScreen();
win.leaveFullScreen();
win.toggleFullScreen();
win.on('enter-fullscreen', function(){...});
win.on('leave-fullscreen', function(){...});
一种特殊的全屏模式,也有人称之为展台模式,就是类似网吧或取号机等场合那种不能轻易退出的定制模式
Alt+F4
,Ctrl+Alt+Del
等组合键退出win.enterKioskMode()
, win.leaveKioskMode()
, win.toggleKioskMode()
可以将窗口的边框禁用,从而更大程度的自定义窗口
//package.json
{
"name": "My App",
"main": "index.html",
"window": {
"frame": false
/* "fullscreen": true */ //应避免同时这样设置,否则将造成屏幕边缘无法捕获鼠标
}
}
同时,一旦设置为无边框,就无法拖动窗口了,除非自己设置一个可拖动区域
<div class="draggable">
可拖动区域
<a href="javascript:;">不被拖动干扰的链接</a>
</div>
<style>
.draggable {
-webkit-app-region: drag;
-webkit-user-select: none;
}
.draggable>a {
-webkit-app-region: no-drag;
}
</style>
当窗口失去焦点或最小化时,任务栏或Dock图标是吸引用户注意的重要途径,相关的API包括:
win.setShowInTaskbar(bool); //显示或隐藏图标
win.setBadgeLabel(label); //设置图标相关的文本标签,在Linux下一般无效
win.setProgressBar(num); //0到1//Mac上,参数为-1就跳一次,为1就一直跳直到用户点击
//Windows上,图标和窗口同时闪动参数指定的次数
//Linux上,在非激活状态下,非0的参数才会生效
win.requestAttention(number|bool);
前面用到过的 win.close([fouce]) 方法及相关的事件,可以用来在窗口关闭前方便的做收尾工作;需要注意的是这个过程也会减慢窗口的关闭,可以先隐藏窗口以提供比较好的用户体验:
win.on('close', function(type) {
this.hide(); //先隐藏
swtich (type) {
case 'quit': //从菜单、任务栏图标或快捷键关闭
break;
case undefined: //点击窗口关闭按钮
break;
}
//save data ...
this.close(true); //
});
//调用close()并不会立即关闭窗口,直到回调内的close(true),这样就很好的提供了很好的关闭流程
win.close();
当有多个窗口时,closed事件也可以用来清理窗口的实例引用等
var otherWin = nw.Window.get().open('other.html');
otherWin.on('closed', function() {
otherWin = null;
});
[文档地址](http://docs.nwjs.io/en/latest/References/Window/)
nw.Screen.Init(); //实例化Screen的单例对象,只需一次
var screens = nw.Screen.screens; //获得屏幕数组,保护一个或多个screen对象//每个屏幕对象包含这些信息:
/*
screen {
id: int, // 物理屏幕分辨率
bounds: {
x: int,
y: int,
width: int,
height: int
}, // 可用区域
work_area: {
x: int,
y: int,
width: int,
height: int
}, scaleFactor: float,
isBuiltIn: bool,
rotation: int,
touchSupport: int
}
*///可以方便的利用这些信息改善用户体验
var currWin = nw.Window.get();
var myScreen = screens[0];
var x = myScreen.work_area.width - currWin.width;
var y = myScreen.work_area.y;
currWin.moveTo(x, y); //窗口贴到了右上角//当连接投影仪时,分辨率有可能发生改变
nw.Screen.on('displayBoundsChanged', function(newScreen) {
//参数 newScreen 包含了新尺寸屏幕的所有信息
x = newScreen.work_area.width - currWin.width;
currWin.moveTo(x, y);
});
[完整的文档地址](http://docs.nwjs.io/en/latest/References/Screen/)
NW.js中,共有三种类型的菜单:
var menu = new nw.Menu(); //实例化一个菜单
menu.append(new nw.MenuItem({ //添加若干菜单项
label: 'Item A',
icon: 'xxx.png',
tooltip: 'hello world!',
enabled: true,
key: 'A', //快捷键的主键,仅在菜单打开时有效;如果同时为document监听了keyup,会同时生效
modifiers: 'ctrl-shift', //快捷键的修饰键
click: function() { //点击事件回调,也可以用 menuitem.on('click', callback) 的方式
alert('点击了 "Item A"');
}
}));
menu.append(new nw.MenuItem({
label: 'Item B',
checked: false,
type: 'checkbox' //类型2:点击后菜单项前面有对勾效果
}));
menu.append(new nw.MenuItem({
type: 'separator' //类型3:分割线
}));
menu.append(new nw.MenuItem({
label: 'Item C',
type: 'normal', //类型1: 普通
iconIsTemplate: true, //仅对Mac生效,系统自动根据 dark/light 等状态改变其样式
submenu: new nw.Menu(...) //子菜单
}));
document.querySelector('#area').addEventListener('contextmenu', function(ev) {
ev.preventDefault();
menu.popup(ev.x, ev.y); //右键时弹出菜单
return false;
}, false);
menu实例中一些其他的方法:
menu.items
: 一个由 MenuItem 组成的数组,包含了其持有的所有菜单项menu.insert(item, i)
menu.remove(item)
menu.removeAt(i)
窗口菜单的绝大多数用法和上下文菜单相同,几个不同点在于:
var winMenu = new nw.Menu({type: 'menubar'});
var os = require('os');
if (os.platform() === 'darwin') {
winMenu.createMacBuiltin("配置中的应用名称", {
hideEdit: true,
hideWindow: false
});
}
var mitem1 = new nw.MenuItem({
label: 'm1',
submenu: new nw.Menu
});
mitem1.submenu.append(new nw.MenuItem({label: 'aaa1'}));
mitem1.submenu.append(new nw.MenuItem({label: 'bbb1'}));
winMenu.append(mitem1);
var mitem2 = new nw.MenuItem({
label: 'm2',
submenu: new nw.Menu
});
mitem2.submenu.append(new nw.MenuItem({label: 'aaa2'}));
mitem2.submenu.append(new nw.MenuItem({label: 'bbb2'}));
mitem2.submenu.append(new nw.MenuItem({
label: '退出这个程序',
click: e=>nw.App.quit()
}));
winMenu.append(mitem2);
var currWin = nw.Window.get();
currWin.menu = winMenu;
[完整的文档地址](http://docs.nwjs.io/en/latest/References/Menu/)
托盘区一般处在系统状态栏的右侧,一些长时间运行的应用或服务的图标被安置在此处,以免都挤在任务栏中过于拥挤。(这些图标在不同平台叫法不同,Mac中叫做 Status Item, 一些Linux中叫做 Status Icon, Windows 中叫做 System Tray Icon)
// 创建一个托盘图标
var tray = new nw.Tray({ title: 'Tray', icon: 'img/icon.png' });// 添加菜单
var menu = new nw.Menu();
menu.append(new nw.MenuItem({ type: 'checkbox', label: 'box1' }));
tray.menu = menu;// 移除图标
tray.remove();
tray = null;
window.devicePixelRatio>1
后动态指定,或将2x图标和原始图标文件打包,[参考这里](http://www.cnblogs.com/lovelylife/p/6226314.html)[完整的文档地址](http://docs.nwjs.io/en/latest/References/Tray/)
在浏览器里,文件对话框可以上传下载文件。在NW.js里,同样的操作只是传递文件路径字符串而已,而非拷贝其内容;同时一些浏览器中的安全限制被解除,并赋予其一些增强的能力,从而使用户体验更接近原生应用
<input type="file" />
<a href="javascript:;" id="file-trigger">打开文件对话框</a><script>
var fipt = document.querySelector('[type=file]');
fipt.addEventListener('change', function(e) {
var path = this.value;
alert(path);
fipt.value = '';
});
document.getElementById('file-trigger').addEventListener('click', function() {
fipt.click(); //如果要自定义样式或自动触发,也可以直接调用
});
</script>
<input type="file" multiple />
<input type="file" accept=".doc,.xml,application/msword" />
<input type="file" nwdirectory />
<input type="file" nwsaveas[=默认路径] />
<input type="file" nwworkingdir="C:\Documents" />
<div id="drag_file_area" style="width: 300px;height: 300px;background-color: gray;">拖动文件到这里</div><script>
window.ondragover = window.ondrop = function(e) {
e.preventDefault()
};
var dragfile = document.getElementById('drag_file_area');
dragfile.ondragenter = function() {this.style.opacity = .5}
dragfile.ondragleave = function() {this.style.opacity = 1}
dragfile.ondrop = function(e) {
e.preventDefault();
this.style.opacity = 1;
let {files} = e.dataTransfer;
let paths = [].map.call(files, file=>file.path);
alert(paths);
};
</script>
// 获取单例
var clipboard = nw.Clipboard.get();// 从剪贴板读取
var text = clipboard.get('text');
console.log(text);// 写入剪贴板
clipboard.set('I love NW.js :)', 'text');// 清空
clipboard.clear();
[完整的文档地址](http://docs.nwjs.io/en/latest/References/Clipboard/)
Shell.openExternal(URI)
: 用系统默认的浏览器或邮件程序打开URIShell.openItem(file_path)
: 用系统默认的关联程序打开一个文件,如果没有指定,则打开"Open with"对话框Shell.showItemInFolder(path)
: 用资源管理器或finder打开指定的目录[完整的文档地址](http://docs.nwjs.io/en/latest/References/Shell/)
* 原创文章转载请注明出处