senchacon 2016: advanced techniques for buidling ext js apps with electron - jason cline

Post on 11-Apr-2017

109 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

top related