[置顶] nw.js node-webkit系列(13)Native UI API 其它

时间:2022-07-15 05:44:40

本节主要介绍Native UI API中一些不常用的功能,包括Shell、Handling files and arguments、Screen。

(一)Shell

Shell主要用来处理桌面相关应用的工作。如使用默认浏览器打开一个网页地址、使用文本的方式打开一个文件、在文件资源管理器中打开文件。

// Load native UI library.
var gui = require('nw.gui');

// Open URL with default browser.
gui.Shell.openExternal('https://github.com/rogerwang/node-webkit');

// Open a text file with default text editor.
gui.Shell.openItem('test.txt');

// Open a file in file explorer.
gui.Shell.showItemInFolder('test.txt');

(二)NW应用处理文件和参数的方式

针对一些应用,例如文本编辑器和IDE,它可以绑定文件类型并指定特定的APP打开,这个功能是很重要的。

Command line arguments

命令行参数传输。在node-webkit中,当使用命令行去指定你的APP去打开一个文件时,如

your-app file.txt file2.txt

其中

file.txt file2.txt
将会被记录而且你能够使用App.argv的方法去获取它们,如

var gui = require('nw.gui');
console.log(gui.App.argv);
// Print "file.txt, file2.txt"

Open file with existing app

使用现有的应用打开文件。在很多时候,当你使用你的APP打开多个文件时,你更希望使用一个APP会话去打开而不是多个会话。例如,一个IDE,如果你已经打开了一个IDE实例,然后再打开一个源码文件,你会希望使用已打开的IDE实例去编辑文件而不是重新打开一个IDE实例。

对于node-webkit的应用,这个问题已经被默认解决了。当你打开一个文件,node-webkit会检测你的APP是否已经打开,如果没有打开,你的应用实例将会被打开并且文件的路径会从App.argv中获取;如果你的应用已经在打开的状态,那么整个命令行的内容将会通过open事件监听到APP对象获取。两种情况获取的打开文件的参数对象方式不同。

// Listen to `open` event
gui.App.on('open', function(cmdline) {
console.log('command line: ' + cmdline);
});

注:open事件只有在你的APP是独立存在的情况下生效。例如使用这种方式进行打包:

https://github.com/nwjs/nw.js/wiki/How-to-package-and-distribute-your-apps

注:在苹果系统,如果以文件拖动到你的APP图标中的方式打开文件,open事件依然有效。

注:在苹果系统,你应该注册你的文件类型,使文件启动类型指向你的APP。操作方法参照:

https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/DocumentInteraction_TopicsForIOS/Articles/RegisteringtheFileTypesYourAppSupports.html

注:如果你不想使用这个功能,在Windows和Linux系统下,你可以在package配置文件中设置single-instance为false。然而这个设置对苹果系统并没有效果。


(三)Screen

Screen是EventEmitter对象的一个实例。你可以使用Screen.on(...)去回应本地屏幕事件。

Screen是一个独立的对象,它需要被初始化当使用的时候。如gui.Screen.Init()。


Screen.Init()

初始化Screen实例对象,你只需要调用一次即可。


Screen.screens

获取Screen实例对象的数据。Screen对象有以下的结构:

screen {
// unique id for a screen
id: int,

// physical screen resolution, can be negative, not necessarily start from 0,depending on screen arrangement
bounds: {
x: int,
y: int,
width: int,
height: int
},

// useable area within the screen bound
work_area: {
x: int,
y: int,
width: int,
height: int
},

scaleFactor: float,
isBuiltIn: bool,
rotation: int,
touchSupport: int
}

Screen.chooseDesktopMedia (array of DesktopCaptureSourceType sources, function callback)

该功能比较复杂,详细请参考:

https://github.com/nwjs/nw.js/issues/3077


更多对屏幕控制的函数,请参考

https://github.com/nwjs/nw.js/wiki/Screen


(四)小贴士

(1)Show window after page is ready

通常情况下,node-webkit打开一个APP,需要通过以下步骤:

1).显示浏览器

2).解压你的APP

3).创建渲染过程

4).初始化webkit

5).最后打开你的APP

这个过程需要些时间,而且这段时间用户将看到一个空的浏览器。这会导致看起来你的APP运行的非常缓慢,你应该等所有都准备就绪后才显示你的主窗口。针对这个问题,下面有些小贴士可以提供给你。

首先,你需要在package配置文件中设置窗口的显示为false,这样在node-webkit启动时便不会显示窗口。

{
"window": {
"show": false
}
}

当所有都准备就绪后,你再调用Windows API把窗口显示出来。

<script>
var gui = require('nw.gui');

onload = function() {
gui.Window.get().show();
}
</script>

(2)Minimize to tray

在windows系统中,一种通用的窗口设计模式是最小化窗口时会到通知区域。如当点击窗口的最小化按钮时,会最小化到托盘,点击托盘将返回窗口。

为了在node-webkit中实现这种功能,你可以使用以下代码:

<html>
<body>
<div>Minimize to tray</div>
<script>
// Load library
var gui = require('nw.gui');

// Reference to window and tray
var win = gui.Window.get();
var tray;

// Get the minimize event
win.on('minimize', function() {
// Hide window
this.hide();

// Show tray
tray = new gui.Tray({ icon: 'icon.png' });

// Show window and remove tray when clicked
tray.on('click', function() {
win.show();
this.remove();
tray = null;
});
});
</script>
</body>
</html>

(3)Preserve window state between sessions

在多次会话中保存窗口的状态。意思是当首次打开窗口时,将窗口的位置和宽高记录在node-webkit的Local Storage中,下次打开窗口时,恢复上次窗口的显示状态。

为了实现这个功能,首先你需要把窗口的显示状态设为false:

{
"window": {
"show": false
}
}

然后你可以使用下面这段代码:

/**
* https://github.com/nwjs/nw.js/wiki/Preserve-window-state-between-sessions
*
* Cross-platform window state preservation.
* Yes this code is quite complicated, but this is the best I came up with for
* current state of node-webkit Window API (v0.7.3 and later).
*
* Known issues:
* - Unmaximization not always sets the window (x, y) in the lastly used coordinates.
* - Unmaximization animation sometimes looks wierd.
* - Extra height added to window, at least in linux x64 gnome-shell env. It seems that
* when we read height then it returns it with window frame, but if we resize window
* then it applies dimensions only to internal document without external frame.
* Need to test in other environments with different visual themes.
*
* Change log:
* 2013-12-01
* - Workaround of extra height in gnome-shell added.
*
* 2014-03-22
* - Repared workaround (from 2013-12-01) behaviour when use frameless window.
* Now it works correctly.
* 2014-10-02
* - Fixed cannot set windowState of null error when attempting to set localStorage
*
* 2015-03-05
* - Don't call window.show() if dev tools are already open (see initWindowState).
*
* 2015-06-15
* - Don't resize the window when using LiveReload.
*/

var gui = require('nw.gui');
var win = gui.Window.get();
var winState;
var currWinMode;
var resizeTimeout;
var isMaximizationEvent = false;
// extra height added in linux x64 gnome-shell env, use it as workaround
var deltaHeight = gui.App.manifest.window.frame ? 0 : 'disabled';


function initWindowState() {
// Don't resize the window when using LiveReload.
// There seems to be no way to check whether a window was reopened, so let's
// check for dev tools - they can't be open on the app start, so if
// dev tools are open, LiveReload was used.
if (!win.isDevToolsOpen()) {
winState = JSON.parse(localStorage.windowState || 'null');

if (winState) {
currWinMode = winState.mode;
if (currWinMode === 'maximized') {
win.maximize();
} else {
restoreWindowState();
}
} else {
currWinMode = 'normal';
dumpWindowState();
}

win.show();
}
}

function dumpWindowState() {
if (!winState) {
winState = {};
}

// we don't want to save minimized state, only maximized or normal
if (currWinMode === 'maximized') {
winState.mode = 'maximized';
} else {
winState.mode = 'normal';
}

// when window is maximized you want to preserve normal
// window dimensions to restore them later (even between sessions)
if (currWinMode === 'normal') {
winState.x = win.x;
winState.y = win.y;
winState.width = win.width;
winState.height = win.height;

// save delta only of it is not zero
if (deltaHeight !== 'disabled' && deltaHeight !== 0 && currWinMode !== 'maximized') {
winState.deltaHeight = deltaHeight;
}
}
}

function restoreWindowState() {
// deltaHeight already saved, so just restore it and adjust window height
if (deltaHeight !== 'disabled' && typeof winState.deltaHeight !== 'undefined') {
deltaHeight = winState.deltaHeight
winState.height = winState.height - deltaHeight
}


//Make sure that the window is displayed somewhere on a screen that is connected to the PC.
//Imagine you run the program on a secondary screen connected to a laptop - and then the next time you start the
//program the screen is not connected...
gui.Screen.Init();
var screens = gui.Screen.screens;
var locationIsOnAScreen = false;
for (var i = 0; i < screens.length; i++) {
var screen = screens[i];
if (winState.x > screen.bounds.x && winState.x < screen.bounds.x + screen.bounds.width) {
if (winState.y > screen.bounds.y && winState.y < screen.bounds.y + screen.bounds.height) {
console.debug("Location of window (" + winState.x + "," + winState.y + ") is on screen " + JSON.stringify(screen));
locationIsOnAScreen = true;
}
}
}

if (!locationIsOnAScreen) {
console.debug("Last saved position of windows is not usable on current monitor setup. Moving window to center!");
win.setPosition("center");
} else {
win.resizeTo(winState.width, winState.height);
win.moveTo(winState.x, winState.y);
}
}

function saveWindowState() {
dumpWindowState();
localStorage['windowState'] = JSON.stringify(winState);
}

initWindowState();

win.on('maximize', function() {
isMaximizationEvent = true;
currWinMode = 'maximized';
});

win.on('unmaximize', function() {
currWinMode = 'normal';
restoreWindowState();
});

win.on('minimize', function() {
currWinMode = 'minimized';
});

win.on('restore', function() {
currWinMode = 'normal';
});

win.window.addEventListener('resize', function() {
// resize event is fired many times on one resize action,
// this hack with setTiemout forces it to fire only once
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {

// on MacOS you can resize maximized window, so it's no longer maximized
if (isMaximizationEvent) {
// first resize after maximization event should be ignored
isMaximizationEvent = false;
} else {
if (currWinMode === 'maximized') {
currWinMode = 'normal';
}
}

// there is no deltaHeight yet, calculate it and adjust window size
if (deltaHeight !== 'disabled' && deltaHeight === false) {
deltaHeight = win.height - winState.height;

// set correct size
if (deltaHeight !== 0) {
win.resizeTo(winState.width, win.height - deltaHeight);
}
}

dumpWindowState();

}, 500);
}, false);

win.on('close', function() {
try {
saveWindowState();
} catch (err) {
console.log("winstateError: " + err);
}
this.close(true);
});

最后,在html中导入这段代码即可

<!-- use your own path -->
<script src="lib/winstate.js"></script>


本节内容就介绍到这,如果后续博主发现更多Native UI API的相关功能,会继续在CSDN博客中发表,至此Native UI API相关功能已经介绍完毕