Electron
ChtiJS #14 - 01/10/15
Qui suis-je ? (vite fait)
Electron…c'est quoi ?
Qui l'utilise ?
…et bien d'autres
Pourquoi ?
Tout comme NW.js, en fait
Presque ! Mais avec des plus
Now, let's code !
STOP !!!
Gestion des processus
Processus principal
Processus de rendu
Processus de rendu
Processus de rendu
Back to code !
Installation
Évitez l'installation globale !
npm init
{
"name": "electron-app",
"main": "main.js",
"devDependencies": {
"electron-prebuilt": "^0.33.3"
},
"scripts": {
"start": "electron ."
}
}
npm install electron-prebuilt --save-dev
touch main.js
'use strict';
const app = require('app');
const BrowserWindow = require('browser-window');
let mainWindow = null;
app.on('ready', () => {
mainWindow = new BrowserWindow({
width: 800,
height: 600
});
mainWindow.loadUrl('file://' + __dirname + '/index.html');
mainWindow.openDevTools();
mainWindow.on('closed', () => {
mainWindow = null;
});
});
app.on('window-all-closed', () => {
if (process.platform != 'darwin') {
app.quit();
}
});
Point d'entrée
touch index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Electron app</title>
</head>
<body>
Electron
<script>document.write(process.versions['electron'])</script><br/>
Node.js
<script>document.write(process.version)</script><br/>
Chromium
<script>document.write(process.versions['chrome'])</script><br/>
</body>
</html>
Une première fenêtre
npm start
Les modules
Processus principal
Processus de rendu
Les deux
Module ipc
(Communication inter-processus)
Processus principal
Processus de rendu
Processus de rendu
Processus de rendu
channel
souscris
reçois
publie
'use strict';
const app = require('app');
const BrowserWindow = require('browser-window');
const ipc = require('ipc');
let mainWindow = null;
app.on('ready', () => {
mainWindow = new BrowserWindow({
width: 800,
height: 600
});
mainWindow.loadUrl('file://' + __dirname + '/index.html');
mainWindow.openDevTools();
mainWindow.on('closed', () => {
mainWindow = null;
});
});
app.on('window-all-closed', () => {
if (process.platform != 'darwin') {
app.quit();
}
});
ipc.on('say-hello', (event, name) => {
console.log(`Hello ${name}`);
event.sender.send('hello-reply');
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Electron app</title>
</head>
<body>
<input type="text" class="hello-name">
<button class="hello-btn">Say Hello!</button>
<script>
'use strict';
const ipc = require('ipc');
let helloName = document.querySelector('.hello-name');
let helloBtn = document.querySelector('.hello-btn');
helloBtn.addEventListener('click', () => {
ipc.send('say-hello', helloName.value);
});
ipc.on('hello-reply', () => {
helloName.value = '';
});
</script>
</body>
</html>
Module web-contents
(EventEmitter)
ipc.on('say-hello', (event, name) => {
// event is a WebContents object
console.log(`Hello ${name}`);
event.sender.send('hello-reply');
});
Events
did-finish-load
did-fail-load
did-start-loading
did-stop-loading
dom-ready
etc.
Methods
webContents.loadUrl(url[, options])
webContents.getTitle()
webContents.reload()
webContents.printToPDF(options, callback)
webContents.send(channel[, args...])
etc.
'use strict';
const app = require('app');
const BrowserWindow = require('browser-window');
const ipc = require('ipc');
let mainWindow = null;
app.on('ready', () => {
mainWindow = new BrowserWindow({
width: 800,
height: 600
});
mainWindow.loadUrl('http://chtijs.francejs.org');
let webContents = mainWindow.webContents;
webContents.on('dom-ready', () => {
console.log('DOM is ready');
});
mainWindow.on('closed', () => {
mainWindow = null;
});
});
app.on('window-all-closed', () => {
if (process.platform != 'darwin') {
app.quit();
}
});
Module remote
(Controller le processes principal depuis un processus de rendu)
'use strict';
const app = require('app');
const BrowserWindow = require('browser-window');
let mainWindow = null;
app.on('ready', () => {
mainWindow = new BrowserWindow({
width: 800,
height: 600
});
mainWindow.loadUrl('file://' + __dirname + '/index.html');
mainWindow.on('closed', () => {
mainWindow = null;
});
});
app.on('window-all-closed', () => {
if (process.platform != 'darwin') {
app.quit();
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Electron app</title>
</head>
<body>
<input type="text" class="new-window-url">
<button class="new-window-btn">Open in a new window</button>
<script>
'use strict';
const remote = require('remote');
const BrowserWindow = remote.require('browser-window');
let newWindowUrl = document.querySelector('.new-window-url');
let newWindowBtn = document.querySelector('.new-window-btn');
newWindowBtn.addEventListener('click', () => {
let newWindow = new BrowserWindow({
width: 800,
height: 600
});
newWindow.loadUrl(newWindowUrl.value);
});
</script>
</body>
</html>
Quelques modules plus "fun"
(ceux qui interagissent avec l'OS)
Modules menu / menu-item
<!-- index.html -->
<script>
'use strict';
const remote = require('remote');
const Menu = remote.require('menu');
const MenuItem = remote.require('menu-item');
let menu = new Menu();
menu.append(
new MenuItem({
label: 'Log a message',
click: () => { console.log('Item was clicked'); }
})
);
menu.append(
new MenuItem({
label: 'Something status',
type: 'checkbox',
checked: true
})
);
window.addEventListener('contextmenu', (e) => {
e.preventDefault();
menu.popup(remote.getCurrentWindow());
}, false);
</script>
// main.js
let menuTemplate = [{
label: 'View',
submenu: [{
label: 'Toggle Developer Tools',
accelerator: (() => {
if (process.platform == 'darwin')
return 'Alt+Command+I';
else
return 'Ctrl+Shift+I';
})(),
click: (item, focusedWindow) => {
if (focusedWindow)
focusedWindow.toggleDevTools();
}
}]
}];
if (process.platform == 'darwin') {
let name = require('app').getName();
menuTemplate.unshift({
label: name,
submenu: [{
label: 'Quit',
accelerator: 'Command+Q',
click: () => { app.quit(); }
}]
});
}
let menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
Modules tray
'use strict';
const app = require('app');
const Menu = require('menu');
const Tray = require('tray');
let appIcon = null;
app.on('ready', () => {
appIcon = new Tray('./assets/icon.png');
let contextMenu = Menu.buildFromTemplate([
{ label: 'Value 1', type: 'radio', checked: true },
{ label: 'Value 2', type: 'radio' },
{ label: 'Value 3', type: 'radio' },
{ label: 'Value 4', type: 'radio' }
]);
appIcon.setToolTip('Tooltip example');
appIcon.setContextMenu(contextMenu);
});
Et bien d'autres !
Résumons
Richesse de Chromium
Richesse de Node.js
Richesse d'Electron
Couche d'abstraction forte
+
+
=
Démo time
Et ensuite ?
Merci de votre attention !
Des questions ?