Date post: | 11-Apr-2017 |
Category: |
Technology |
Upload: | sencha |
View: | 110 times |
Download: | 1 times |
Advanced Techniques forBuilding Ext JS Apps
with ElectronJason Cline
@clinejm
Build ‘native’ desktop applications with Web technology
What is Electron?
What is Electron?
Chrome NodeJS
Sencha Uses ElectronArchitec
t Themer InspectorTest Studio
Who uses Electron?
Control the runtime
Why Electron?
Desktop integration
Why Electron?
Local access to NodeJS for real work
Why Electron?
ElectronSupported Platforms• Applications can run on
- Windows (32 or 64)
- Mac OS X
- Linux (x86, x86_64, and armv7l)
• Applications can be built on:- Windows (32 or 64)
- Mac OS X
- Linux (x86 or x86_64)
Don’t forget to sign your binaries!
ElectronBuild Process• Builds use the electron-packager Node.js package.
• Uses npm scripts instead of gulp or grunt.- Simplicity is good… can always switch over if needs require.
• The electron-packager wraps the compiled Ext JS application as produced by Sencha Cmd.
• Application code will be stored in an “asar” file.- See http://electron.atom.io/docs/tutorial/application-packaging/
- Just for convenience, not as a secure storage mechanism.
Render
Electron Processes
Main
./main.js ./app.jsnew BrowserWindow()
process.type: 'browser' 'render'
System UIHeavy Lifting
Presentation(DOM)
ElectronOrganization• Render process is almost a normal web app
- Entry point is “app.js” in root folder
- Other code is in “./app” folder
• Main process logic is just Node.js code- Entry point is “main.js” in root folder
- Other code is in “./main” folder
Inter-process Communication (IPC)
var remote = require('electron').remote;var service = remote.require('./main/service');
var v = service.heavyWork(42, result => { console.log(`Work is done: ${result}`);});
console.log(`Initial work value: ${v}`);
// log:// > Initial work value: 21// > Work is done: 427
Use remote To Off-load Work
• Push heavy workloads to the main process.
• Initial result is synchronously returned!
• Callbacks can be used to return data asynchronously as well.
• Very convenient compared to raw IPC methods.
module.exports = { heavyWork (value, done) { setTimeout(() => { done(value * 10 + 7); }, 2500);
return v / 2; }};
./main/service.js
./app/...
Native GUI Integration
Native GUI Integration
Electron provides access to many native elements such as:
• File Pickers
• Folder Pickers
• Tray Icons
• Notifications / “Balloons”
tbar: [{ xtype: 'electronfilefield', options: { filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },{ name: 'All Files', extensions: ['*'] }] }, bind: '{filename}', listeners: { change: 'onFileChange' } }]
electronfilefield
• Replacement for filefield
• Uses Electron’s dialog module
• Supports data-binding
Native Menus
Native Menus
Electron provides native menu access, but…
• Menu creation is easiest with a template
• Templates have no conditional pieces
• API’s to maintain menu state (checked, visible, enabled) are rather new
• Editing menus after creation is difficult
mixins: [ 'Ext.electron.menu.Manager' ], nativeMenus: { app: [{ label: 'File', submenu: [{ label: 'Reopen', submenu: 'getReopenMenu' }, { label: 'Exit', accelerator: 'CmdOrCtrl+Q', click: 'onExit' }] },
...
Ext.electron.menu.Manager
• Declarative, dynamic menus
• Menu and menu item properties can be specified via:- Value
- Inline function
- Named controller method
• Click handlers can be:- Inline function
- Named controller method
getReopenMenu () { var vm = this.getViewModel();
return this.recentFiles.map(file => { return { label: file, click () { vm.set('filename', file); } }; }); }
./app/…/MainController.js
Ext.electron.menu.Manager
• Submenus can be built in code
nativeMenus: { app: [{ label: 'File', submenu: [{ label: 'Reopen', submenu: 'getReopenMenu' }, {
...
onFileChange (picker, path) { var recentFiles = this.recentFiles;
let i = recentFiles.indexOf(path); if (i > -1) { recentFiles.splice(i, 1); }
recentFiles.push(path); if (recentFiles.length > 3) { recentFiles.shift(); }
this.getView().reloadNativeMenu('app'); }
Ext.electron.menu.Manager
• Call reload method when state changes to update the menu:
Fit and Finish
const Path = require('path');
const COMPANY = 'Acme';const COMPANY_LOWER = COMPANY.toLowerCase();
const profileDir = (function () { var ret = os.homedir();
switch (os.platform()) { case 'win32': return Path.join(process.env.APPDATA, `${COMPANY}`);
case 'darwin': return Path.join(ret, `Library/Application Support/${COMPANY}`);
case 'linux': return Path.join(ret, `.local/share/data/${COMPANY_LOWER}`); }
return Path.join(ret, `.${COMPANY_LOWER}`);})();
Respecting Native Conventions
• User file locations vary by platform:- Windows• C:\Users\Jason\AppData\Roaming\Acme\
- Mac OS X• /Users/Jason/Library/Application Support/Acme/
- Linux (may vary by distro)• /home/jason/.local/share/data/acme/
- When in doubt:• /home/jason/.acme/
win = new BrowserWindow({ width: initData.width || 1200, height: initData.height || 900, x: initData.x, y: initData.y, maximized: initData.maximized});
win.on('move', trackWindow);win.on('resize', trackWindow);
function trackWindow () { if (win.isMaximized()) { ... } else { windowBox = win.getBounds(); }
if (!syncTimer) { syncTimer = setTimeout(saveWindowState, 500); }}
Tracking The BrowserWindow
• Create the BrowserWindow using saved size information (no flicker)
• Track move and resize events at the level of the BrowserWindow.
• Buffer changes (they fire quickly)
Downside?
Enterprise Distribution
Challenges
Code Signing
Challenges
Create an Installer
Challenges
How to Update?
Challenges
User Adoption
Challenges
Code Security
Challenges
Build ‘native’ desktop applications with Web technology
Summary
Questions?
github.com/sencha/electron-demo