B4J Tutorial [BANanoElectron] Beginning Electron with BANano

Mashiane

Expert
Licensed User
Ola

Download

What is Electron?

Electron is a desktop application frameworks powered by Node.js. It allow developers to create cross-platform desktop apps using HTML, CSS, and JavaScript. It offers web designers and developers a way to take their existing skills for crafting web apps and interfaces, and apply that to building desktop apps. The framework also support shipping apps for Mac OS, Windows, and Linux from the same codebase, meaning that developers can save time and energy when creating desktop apps that all OSs can use. Electron was born when the node-webkit, currently known as NW.js could not work well with the Atom editor from Github. It was created by Cheng Zhao.

How is it similar to NW.js?

NW.js and Electron come from a shared history, and have some similar approaches to app features. When the node web kit was not actively developed, Cheng worked on it to create the atom-shell but later created a completely new framework for desktop development based on HTML, CSS and JavaScript.

We have BANanoNWjs

There is a thread here about using NW.js (node-webkit) with BANano, the BANanoNWjs library. As these share the same approach to app development, we will use the basics we learned from BANanoNWjs to begin BANanoElectron.


Setting up the IDE

1. Obviously you will need BANano
2. Install Visual Studio Code (my preference and it makes things easy)
2. Install Node.js
3. Install Electron

Some few examples..

1. Hello World

 
Last edited:

Mashiane

Expert
Licensed User
Hello World

BANanoElectron.gif


Differences with NWjs

The entry point for NWjs is an index.html file, however for the Electron framework it needs to be a javascript file. For this to work, we create an app.js file and a package.json file and write some content to them. Our index.html file will be created the same way we have been doing with BANano.

Creating the entry point app.js file

B4X:
Dim AppJs As String = $"'use strict';
const electron = require('electron');
const app = electron.app;                                               
const BrowserWindow = electron.BrowserWindow;

let mainWindow = null;

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

app.on('ready', () => {
  mainWindow = new BrowserWindow();
  mainWindow.loadURL(`file://~{__dirname}/index.html`);
  mainWindow.on('closed', () => { mainWindow = null; });
});"$
    '
    AppJs = AppJs.Replace("~","$")
    File.WriteString(WorkingFolder, "app.js", AppJs)
Creating the package.json file

B4X:
    Dim PackageJson As String = $"{
      "name": "${AppName}",
      "version": "${Version}",
      "description": "",
      "main": "app.js",
      "author": "BANanoNinja",
      "license": "MIT"
    }"$

    File.WriteString(WorkingFolder, "package.json", PackageJson)
Running the attached demo

1. Run the demo with BANano, ensure you change your publish folder to suit your needs.
2. Right click the BanELDemo folder in your publish folder, and click Open with Code

1602530139488.png


3. On Visual Studio Code, on the menu, select Terminal.
4. On the terminal type electron . and press enter, your demo app should run.
5. Click on the Say Hello button, this will show an alert.

Congratulations!!
 

Attachments

Mashiane

Expert
Licensed User
Hello World with BANanoNWJS

If you were wondering if the same Hello BANAnoElectron example can be donw using NWJS, the answer is a definate YES!

You should note the following:

As indicated on the BANanoNWjs thread, download NWJs and save it on c:\nwjs in this example. c:\nwjs will be your publish folder.

You will have a structure like this after running this demo.

1602533804185.png


For this example I am not using the BANanoNWjs library as the functionality in that library is not needed yet as we are just showcasing. For this example, the entry point is index.html file and not app.js, so we follow the BANanoNWjs Demo example as is and just update our BANano_Ready Code.

This is the demo when ran, the example project is herein attached.

Hello BANanoNWJS.gif


What have you noticed?

1. On the Electron example, there was a menu and menu items created, with NWJS there arent any.
2. The BANano.Alert(??) whilst being called shows differently whilst we used the same code base.
3. Electron had an "icon" for electron whilst this has a world icon.

Now whether you want to use Electron / NWjs is purely up to you. With these two examples, we have used the exact code base except for the "publication" of the app in App_Start.

Download and run this demo in the B4X IDE, wait for the app to open and run.
 

Attachments

Mashiane

Expert
Licensed User
Upcoming Updates.

With some shell commands, we are able to:

1. Determine if Electron is installed, if not installed, the app installs it by calling the package manager npm.
2. After detecting installation, run the application.

This is possible with code..

B4X:
'lets work with electron
    Dim electron As BANanoElectron
    electron.Initialize(Publish, AppName, "com.mashy.helloelectron", "BANano Electron")
    Log("=======================================")
    Log(">> Checking if Electron is already installed...")
    Dim rs As ResumableSub = electron.isInstalled
    Wait For (rs) Complete(Result As Boolean)
    If Not(Result) Then
        Log(">> Electron is not yet installed. Installing...")
        Dim rs As ResumableSub = electron.Install
        Wait For (rs) Complete(Result As Boolean)
        If Not(Result) Then ExitApplication
    Else
        Log(">> Electron is installed!")
    End If
    Log(">> Attempting to run the application...")
    Dim rs As ResumableSub = electron.Run
    Wait For (rs) Complete(Result As Boolean)
    If Not(Result) Then
        Log(">> Could not run the Electron App...")
        If Not(Result) Then ExitApplication
    Else
        Log(">> Electron App running, check the output!")
    End If
    ExitApplication
Thanks to @Toky Olivier shell snippets..
 

Mashiane

Expert
Licensed User
Creating a Windows Executable for Distribution - Electron Packager

BANanoElectronPackaging.gif


Assuming that you are done with your Electron App, you can package it for distribution and create a Windows Executable. There are various ways to do this and we will look at the electron packager.

There are some few things we need to do first and update our package.json file.

1. Install electron as a dependency to our app.
2. Install electron-package as a dependency to our app

Both these update the package.json file and make it ready for further use.

At the end we have something like this

B4X:
{
  "name": "BanELDemo",
  "version": "1.0.0",
  "productName": "BanELDemo",
  "description": "BanELDemo",
  "main": "app.js",
  "scripts": {
    "start": "electron app.js"
  },
  "author": "BANanoNinja",
  "license": "MIT",
  "devDependencies": {
    "electron": "^10.1.3",
    "electron-packager": "^15.1.0"
  }
}
To run the application we can fire..

B4X:
npm install
This checks the package.json for any needed dependency and installs it.

B4X:
npm start
This finds the start script on the package.json file and executes that code, in this case, "electron app.js"

So to enable an automated build process for our executable we have added more functionality for the 2 processes above to be done via B4X. These can be sumarized as follows

Install the electron-packager to the node_modules of the app
B4X:
Dim rs As ResumableSub = electron.InstallDevDependency("electron-packager")
Install electron to the node_modules of the app
B4X:
Dim rs As ResumableSub = electron.InstallDevDependency("electron")
Create the windows executable of the app (as demonstrated in the attached gif)
B4X:
Dim rs As ResumableSub = electron.Run("electron-packager --overwrite")
Electron distributions are just 180MB+ large hey.

To run our app (you can comment out the executable building steps)

Call npm install on the app folder
B4X:
Dim rs As ResumableSub = electron.AppInstall
Call npm run start on the app folder
B4X:
Dim rs As ResumableSub = electron.AppStart
Enjoy.

Update project on 1st post!
 

Mashiane

Expert
Licensed User
Using a Web Framework with BANanoElectron - A case study about BANanoVuetifyAD(2)

BANanoVuetifyAD (BETA) is discussed on this thread. Here we use the same library (just renamed for this case study) to create a designer layout and then attempt to run the same inside Electron and perhaps as a windows app. As this is a case study, we will take this step by step. We want to run away from a native feel but more a desktop web feel.

BVMDemo.gif


This is a case study about using BANanoVueMaterial inside BANanoElectron. For this we just created a blank page with a navbar and a footer and a drawer. This is just stage 1 of the tests of this approach. Of course there is a lot to learn and explore.

1. Copy the library in the External Folder to your B4J External folders library.
2. Open and run the BVMDemo project.
3. You can try and put more components on the Designer Layout as you wish. You will have to refer to Vuetify documentation on creating some useful complex components.

We are using custom views to create the layout and we load this on the pgIndex module.

B4X:
Sub Init
    'initialize the library
    VA.Initialize(Me)
    'load a layout
    BANano.LoadLayout("#placeholder", "mainapp")
    
    'bind navdrawer and hamburger to app state
    navdrawer.AddToApp(VA)
    hamburger.AddToApp(VA)
    '
    'hide the drawer
    VA.setdata("drawer", False)
    'feed placeholder content to the template
    VA.Placeholder2Template    
    'serve the application
    VA.Serve
End Sub

Sub hamburger_clickstop (e As BANanoEvent)
    VA.ToggleState("drawer")
End Sub
The layout file

bvmdemo layout.jpg


As seen above, we want to toggle the drawer when the hamburger is clicked.

Ta!

Source code on first post.
 

Mashiane

Expert
Licensed User
Building Executables for MacOS, Linux and Windows

The output of this run creates folders for each of the distributables. The distributables are written to the build folder.

1602701946571.png


To achieve this, we update the build process in the package.json file and include the following code:

B4X:
"build": "npm run build-mac && npm run build-win && npm run build-linux",
"build-mac": "electron-packager . --platform=darwin --out=build --icons=icons/Icon.icns --overwrite --asar",
"build-win": "electron-packager . --platform=win32 --out=build --icons=icons/Icon.ico --overwrite --asar",
"build-linux": "electron-packager . --platform=linux --out=build --icons=icons/Icon.png --overwrite --asar"
We will call the build process with npm run build. We will run this via the B4x code.

This builds each of the distributables for each of the platforms, darwnin for MacOS and then the other 2.

the --asar directive "conceals" the source code of the app from the eyes. This is usually in the "resources" folder of the builds.

Ta!

PS: MacOS builds should be done on a Mac computer as the build here is incomplete.
 

Mashiane

Expert
Licensed User
Upcoming Updates: A new way to create the package.json file.

B4X:
Dim electron As BANanoElectron
    electron.Initialize(Publish, AppName, "com.mashy.helloelectron", ProductName)
    electron.version = Version
    electron.description = "Beginning Electron with BANano"
    electron.AddScript("start", "electron app.js")
    electron.AddScript("build", "npm run build-mac && npm run build-win && npm run build-linux")
    electron.AddScript("build-mac", "electron-packager . --platform=darwin --out=build --icons=icons/Icon.icns --overwrite --asar")
    electron.AddScript("build-win", "electron-packager . --platform=win32 --out=build --icons=icons/Icon.ico --overwrite --asar")
    electron.AddScript("build-linux", "electron-packager . --platform=linux --out=build --icons=icons/Icon.png --overwrite --asar")
    electron.AddKeyword("BANano")
    electron.AddKeyword("Electron")
    electron.author = "BANanoNinja"
    electron.license = "MIT"
    electron.Save
 

Mashiane

Expert
Licensed User
BrowserWindow

We have seen how to start creating apps, creating executables, lets perhaps start learning how this works.

There are 2 processes, the main process and the renderer process. These talk to each other. You dont need to have a rendererprocess.

You app is started by the main process and this has access to native APIs of node.js. The rendererprocess ensures that your UI is rendered. Everything in the UI sits in a browserWindow. This needs to be created in the main process as it is a native object.

For more information, please read the Electron Documentation.

1603050003658.png


In this example above we create a frameless browser window.

B4X:
'set window options
    'https://www.electronjs.org/docs/api/browser-window#class-browserwindow
    Dim wo As Map = CreateMap()
    wo.Put("width", 800)
    wo.Put("height", 600)
    wo.Put("show", False)
    wo.Put("backgroundColor", "#FFF")
    wo.Put("resizable", True)
    'wo.Put("movable", True)
    'wo.Put("alwaysOnTop", True)
    wo.Put("frame", False)
    'wo.Put("titleBarStyle", "hidden-inset")
    'wo.Put("transparent", True)
    Dim woJSON As String = electron.Map2Json(wo)
    MP.script($"mainWindow = new BrowserWindow(${woJSON})"$)
    MP.newline
    MP.comment("load index.html to app")
    MP.script("mainWindow.loadURL(url.format({")
    MP.script("pathname: path.join(__dirname, 'index.html'),")
    MP.script("protocol: 'file:',")
    MP.script("slashes: true")
    MP.script("}))")
    MP.newline
We have defined another approach to create the main.js file we earlier referenced. This should make it easy for us to add code as and when we want. This code is created in AppStart and no where else.

Because its a frameless window, we need to add a drag style to the body so we can drag the window. We do this on banano_ready.

B4X:
Sub BANano_Ready()
    Dim body As BANanoElement = BANano.GetElement("#body")
    body.SetStyle(BANano.ToJson(CreateMap("-webkit-app-region": "drag", "border": "1px solid red")))
 
    Dim h1 As BANanoElement = BANanoShared.CreateElement("#body", "h1", "myh1")
    h1.SetText("Hello BANanoElectron - BrowserWindow")
 
    'create a script
    Dim script As BANanoElement = BANanoShared.CreateElement("html", "script", "s1")
    script.SetText("require('./renderer.js')")
End Sub
 

Mashiane

Expert
Licensed User
Menus

By default, Electron creates menu items for your app (see #8 above). You can manipulate them and create your own or remove the menu completely on the menu bar.

The result will be like..

1603053526941.png


Clearing the menu

1. Dimension the menu from the Electron menu module.

B4X:
MP.dimension("Menu", "electron.Menu")
2. Create a blank menu

B4X:
MP.comment("create a menu")
    MP.script("let template = []")
    MP.newline
3. We update the app.onReady call

B4X:
MP.comment("Fired when electron is ready to create the window")
    MP.script("app.on('ready', function () {")
    MP.dimension("appmenu", "Menu.buildFromTemplate(template)")
    MP.script("Menu.setApplicationMenu(appmenu)")
    MP.script("createWindow()")
    MP.script("})")
    MP.newline
Output in main.js

1603053737531.png
 

Mashiane

Expert
Licensed User
Creating Own Menu Items

To do this we use normal B4x lists and maps. We then feed this as a JSON string to main.js.

B4X:
MP.comment("create a menu")
    Dim menu1 As ELMenu = electron.CreateMenu("menu1", "Menu 1")
    menu1.AddItem(electron.CreateMenu("menu1item1", "Menu 1 Item 1"))
    Dim menu2 As ELMenu = electron.CreateMenu("menu2", "Menu 2")
    menu2.AddItem(electron.CreateMenu("menu2item1", "Menu 2 Item 1"))
    menu2.AddItem(electron.CreateMenu("menu2item2", "Menu 2 Item 2"))
    Dim template As List = electron.NewList
    template.Add(menu1.menu)
    template.Add(menu2.menu)
    'convert to json
    Dim templateJSON As String = electron.List2Json(template)
    MP.let("template", templateJSON)
    MP.newline
The output of this is:

1603055725882.png


BANanoElectronMenus.gif
 

Mashiane

Expert
Licensed User
Creating Extended Menus

1603057580791.png


B4X:
Dim editapp As ELMenu = electron.CreateMenu("editapp", "Edit App")
    Dim undo As ELMenu = electron.CreateMenu("undo", "Undo").SetAccelerator("CmdOrCtrl+Z").SetRole("undo")
    Dim redo As ELMenu = electron.CreateMenu("redo", "Redo").SetAccelerator("Shift+CmdOrCtrl+Z").SetRole("redo")
    Dim sep1 As ELMenu = electron.CreateMenu("","").Separator
    Dim cut As ELMenu = electron.CreateMenu("cut", "Cut").SetAccelerator("CmdOrCtrl+X").SetRole("cut")
    Dim copy As ELMenu = electron.CreateMenu("copy", "Copy").SetAccelerator("CmdOrCtrl+C").SetRole("copy")
    Dim paste As ELMenu = electron.CreateMenu("paste", "Paste").SetAccelerator("CmdOrCtrl+V").SetRole("paste")
    Dim selectall As ELMenu = electron.CreateMenu("selectall", "Select All").SetAccelerator("CmdOrCtrl+A").SetRole("selectall")
    Dim sep2 As ELMenu = electron.CreateMenu("","").Separator
    Dim quit As ELMenu = electron.CreateMenu("quit", "Quit")
    
    '
    editapp.AddItem(undo)
    editapp.AddItem(redo)
    editapp.AddItem(sep1)
    editapp.AddItem(cut)
    editapp.AddItem(copy)
    editapp.AddItem(paste)
    editapp.AddItem(selectall)
    editapp.AddItem(sep2)
    editapp.AddItem(quit)
Ta!
 

Mashiane

Expert
Licensed User
Menu Radio & CheckBoxes

BANanoElectronMenus.gif


B4X:
Dim menu1 As ELMenu = electron.CreateMenu("menu1", "Menu 1")
    menu1.AddItem(electron.CreateMenu("menu1item1", "Menu 1 Item 1"))
    menu1.AddItem(electron.CreateMenu("menu1item2", "Menu 1 Item 2").SetChecked(True).SetCheckBox)
    menu1.AddItem(electron.CreateMenu("menu1item3", "Menu 1 Item 3").SetChecked(False).SetRadio)
 
Top