Created
February 4, 2015 23:19
-
-
Save mrfabbri/3a32baf9c5eb5a446a48 to your computer and use it in GitHub Desktop.
App wide shortcuts in NW.js in "userland" [PoC]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
var gui = require("nw.gui"); | |
var AppShortcut = require("./appshortcut")(gui).AppShortcut; | |
var keycode = require('keycode'); | |
var win = gui.Window.get(); | |
if (process.platform === "darwin") { | |
var nativeMenuBar = new gui.Menu({type: "menubar"}); | |
nativeMenuBar.createMacBuiltin("My App"); | |
win.menu = nativeMenuBar; | |
} | |
function start() { | |
var shortcut = new AppShortcut({ | |
key: "c", | |
onKeyDown: function (event) { | |
console.log("keydown:", event); | |
document.getElementById("transcript").innerHTML += | |
"<div> keydown: " + keycode(event.keyCode) + "</div>"; | |
}, | |
onKeyUp: function (event) { | |
console.log("keyup:", event); | |
document.getElementById("transcript").innerHTML += | |
"<div> keyup: " + keycode(event.keyCode) + "</div>"; | |
} | |
}); | |
shortcut.start(); | |
new AppShortcut({ | |
key: "o", | |
onKeyDown: gui.Window.open.bind(null, "nw://blank") | |
}).start(); | |
}; | |
window.onload = start; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* NW.js application wide key events module. | |
* | |
* USAGE: | |
* ````JavaScript | |
* var AppShortcut = require("./appshortcut")(gui).AppShortcut; | |
* var shortcut = new AppShortcut({ key: "cmd+c", onKeyDown: someAction }); | |
* ```` | |
* | |
* NOTE: Must be required at the beginning of the main NW.js app file, as it | |
* instruments the Window API object to listen to key events from new windows. | |
* | |
* @module appshortcut | |
*/ | |
"use strict"; | |
var keycode = require("keycode"); | |
// NW.js native gui API | |
var gui; | |
/* Maps holding the keyboard shortcuts actions, per event type. */ | |
var shortcuts = { | |
keydown: new Map(), | |
keyup: new Map(), | |
keypress: new Map() | |
}; | |
/* Hash a keyboard shortcut, keycode+modifiers */ | |
function _keyHash(options) { | |
return options.keyCode + "" + | |
(options.altKey ? "alt" : "") + | |
(options.ctrlKey ? "ctrl" : "") + | |
(options.metaKey ? "meta" : "") + | |
(options.shiftKey ? "shift" : ""); | |
} | |
/* trigger matching shortcut on keydown */ | |
function _keyDown(event) { | |
if (!event.repeat) { // discard repeated keydown | |
var shortcut = shortcuts.keydown.get(_keyHash(event)); | |
if (shortcut && shortcut.isActive()) { shortcut.onKeyDown(event); } | |
} | |
} | |
/* trigger matching shortcut on keyup */ | |
function _keyUp(event) { | |
if (!event.repeat) { // discard repeated keypress | |
var shortcut = shortcuts.keyup.get(_keyHash(event)); | |
if (shortcut && shortcut.isActive()) { shortcut.onKeyUp(event); } | |
} | |
} | |
/* trigger matching shortcut on keypressed */ | |
function _keyPressed(event) { | |
var shortcut = shortcuts.keydown.get(_keyHash(event)); | |
if (shortcut && shortcut.isActive()) { shortcut.onKeyPressed(event); } | |
} | |
function _addListeners() { | |
// add listeners to the current window | |
(function () { | |
var _win = gui.Window.get(); | |
_win.window.addEventListener("keydown", _keyDown); | |
_win.window.addEventListener("keyup", _keyUp); | |
_win.window.addEventListener("keypress", _keyPressed); | |
})(); | |
// add listeners to all future windows | |
var _open = gui.Window.open; | |
gui.Window.open = function open(url, options) { | |
var _win = _open(url, options); | |
setTimeout(function () { | |
// otherwise nwjs is "Unable to get render view in GetWindowObject" | |
_win.window.addEventListener("keydown", _keyDown); | |
_win.window.addEventListener("keyup", _keyUp); | |
_win.window.addEventListener("keypress", _keyPressed); | |
}, 10); | |
return _win; | |
}; | |
var _window_open = window.open; | |
window.open = function open() { | |
var _win = _window_open.apply(window, arguments); | |
_win.addEventListener("keydown", _keyDown); | |
_win.addEventListener("keyup", _keyUp); | |
_win.addEventListener("keypress", _keyPressed); | |
return _win; | |
} | |
} | |
/* Is `key` a string representation of a modifier? */ | |
function _isModifier(key) { | |
return /alt|ctrl|meta|shift/.test(key); | |
} | |
/* Is `keyCode` a normal a key? */ | |
function _isKey(keyCode) { | |
return (65 <= keyCode && keyCode <= 90) || // a..z | |
(48 <= keyCode && keyCode <= 58); // 0..9 | |
} | |
function _parseKeyCombination(combination) { | |
var keys = combination.toLowerCase().split("+"); | |
var res = {}; | |
keys.forEach(function (key){ | |
var keyCode = keycode(key); | |
if (_isModifier(key)) { | |
res[key + "Key"] = true; | |
} | |
if (_isKey(keyCode)) { | |
res.keyCode = keyCode; | |
} | |
}); | |
if (!res.keyCode) { | |
throw new Error(combination + " is not a valid key"); | |
} | |
return res; | |
} | |
/** | |
* Create an application wide shortcut. The `options` argument can | |
* have the folling attributes: | |
* - `key`: single key or combination that activates the shortcut. | |
* - `onKeyDown`: event handler called whenever a `keyup` event for the given | |
* `key` combination is generated in any window of the application. | |
* - `onKeyUp`: event handler called whenever a `keypress` event for the given | |
* `key` combination is generated in any window of the application. | |
* - `onKeyPressed`: event handler called whenever a `keypress` event for the given | |
* `key` combination is generated in any window of the application. | |
* @constructor | |
* @param {Object} options - shortcut configuration options. | |
* @returns {AppShortcut} shortcut - the (active) application wide shortcut. | |
* @throws {MissingKeyException} - if no `key` attribute is present in the `options` | |
* @throws {TypeError} - if any of the handlers is not a `function` | |
* argument. | |
*/ | |
function AppShortcut(options) { | |
if (!options.key) { throw new Error("missing required argument attribute key"); } | |
this.key = options.key; | |
var parsed = _parseKeyCombination(options.key); | |
for(var k in Object.keys(parsed)) { this[k] = parsed[k]; } | |
var keyHash = _keyHash(parsed); | |
if (typeof options.onKeyDown === "function") { | |
this.onKeyDown = options.onKeyDown; | |
shortcuts.keydown.set(keyHash, this); | |
} | |
if (typeof options.onKeyUp === "function") { | |
this.onKeyUp = options.onKeyUp; | |
shortcuts.keyup.set(keyHash, this); | |
} | |
if (typeof options.onKeyPress === "function") { | |
this.onKeyPress = options.onKeyPress; | |
shortcuts.keypress.set(keyHash, this); | |
} | |
this.active = false; | |
} | |
/** | |
* Returns the status, `true` for active, `false` for inactive of the application | |
* wide shortcut. | |
*/ | |
AppShortcut.prototype.isActive = function isActive() { | |
return this.active; | |
}; | |
/** | |
* Activate the application wide shortcut. | |
*/ | |
AppShortcut.prototype.start = function start() { | |
this.active = true; | |
}; | |
/** | |
* Deactivate the application wide shortcut. | |
*/ | |
AppShortcut.prototype.stop = function stop() { | |
this.active = false; | |
}; | |
module.exports = function (nwgui) { | |
gui = nwgui; | |
_addListeners(); | |
return { | |
AppShortcut: AppShortcut | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta charset="utf-8"> | |
<script type="text/javascript" charset="utf-8" src="./app.js"></script> | |
</head> | |
<body> | |
<div>Press "c" to trigger shortcut.</div> | |
<div>Press "o" to open a new window (the shortcut will be active also in the new window).</div> | |
<div id="transcript"></div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "appshortcut", | |
"main": "index.html", | |
"description": "appshortcut test app", | |
"version": "0.0.1", | |
"window": { | |
"show": true, | |
"frame": true, | |
"toolbar": false, | |
"width": 600, | |
"height": 400, | |
"position": "center" | |
}, | |
"dependencies": { | |
"keycode": "~1.0.1" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment