Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Advanced Techniques for Building Ext JS Apps with Electron

Sencha
March 15, 2017

Advanced Techniques for Building Ext JS Apps with Electron

Sencha

March 15, 2017
Tweet

More Decks by Sencha

Other Decks in Technology

Transcript

  1. Electron Supported 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!
  2. Electron Build 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.
  3. Electron Organization • 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
  4. 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/...
  5. Native GUI Integration Electron provides access to many native elements

    such as: • File Pickers • Folder Pickers • Tray Icons • Notifications / “Balloons”
  6. 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
  7. 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
  8. 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
  9. 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' }, { ...
  10. 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:
  11. 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/
  12. 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)