Bangle.js Software Modification

As it comes, Bangle.js provides several utility functions that other apps use to provide more functionality.

For example:

For usage info on these, see the reference

These functions are built in and work a certain way, but they can be changed if you don't like the look of them, for instance with the Small Menus App.

You might want to change the positioning, font size, or even the way you interact with the menus completely!

So how does this work?

For this example, we'll look at E.showMessage as it's nice and simple.

Get the original implementation

In most cases, the implementation of the function will be in JavaScript in the Espruino Repository, but it's worth checking.

For E.showMessage you're taken to the Pixl.js implementation (there can be more than one implementation) which looks like this:

/*JSON{
    "type" : "staticmethod",
    "class" : "E",
    "name" : "showMessage",
    "generate_js" : "libs/js/pixljs/E_showMessage.min.js",
    "params" : [
      ["message","JsVar","A message to display. Can include newlines"],
      ["title","JsVar","(optional) a title for the message"]
    ],
    "ifdef" : "PIXLJS"
}
...
*/

So here you can see it's loading up libs/js/pixljs/E_showMessage.min.js. We can guess where the Bangle.js one is... libs/js/banglejs/E_showMessage.js

Note: sometimes there will be multiple versions of a file, like _Q3 (Bangle.js 2) and _F18 (Bangle.js 1)

Bear in mind these functions are generally written for execution speed and not readability!

Test it out locally

Now you have your file, copy it into the IDE with E.showMessage = before it (or whatever function you're trying to replace) and copy out one of the usage examples from the documentation to right below it - like this:

E.showMessage = function(msg,options) {
  if ("string" == typeof options)
    options = { title : options };
  options = options||{};
  g.clear(1); // clear screen
  Bangle.drawWidgets(); // redraw widgets
  g.reset().setFont("6x8",(g.getWidth()>128)?2:1).setFontAlign(0,-1);
  var Y = global.WIDGETS ? 24 : 0;
  var W = g.getWidth(), H = g.getHeight()-Y, FH=g.getFontHeight();
  var titleLines = g.wrapString(options.title, W-2);
  var msgLines = g.wrapString(msg||"", W-2);
  var y = Y + (H + (titleLines.length - msgLines.length)*FH )/2;
  if (options.img) {
    var im = g.imageMetrics(options.img);
    g.drawImage(options.img,(W-im.width)/2,y - im.height/2);
    y += 4+im.height/2;
  }
  g.drawString(msgLines.join("\n"),W/2,y);  
  if (options.title)
    g.setColor(g.theme.fgH).setBgColor(g.theme.bgH).
      clearRect(0,Y,W-1,Y+4+titleLines.length*FH).
      drawString(titleLines.join("\n"),W/2,Y+2);
  Bangle.setLCDPower(1); // ensure screen is on
};

E.showMessage("Lots of text will wrap automatically",{
  title:"Warning",
  img:atob("FBQBAfgAf+Af/4P//D+fx/n+f5/v+f//n//5//+f//n////3//5/n+P//D//wf/4B/4AH4A=")
})

You can now run it - make sure you upload to RAM:

You may also want to test it with widgets, in which case you can add these lines right before your test call:

Bangle.loadWidgets();
Bangle.drawWidgets();

Make modifications

Ok, now you can tweak the function - let's say we want to put a rounded border around the edge:

E.showMessage = function(msg,options) {
  if ("string" == typeof options)
    options = { title : options };
  options = options||{};
  g.clear(1); // clear screen
  Bangle.drawWidgets(); // redraw widgets
  g.reset().setFont("6x8",(g.getWidth()>128)?2:1).setFontAlign(0,-1);
  var Y = global.WIDGETS ? 24 : 0;
  var W = g.getWidth(), H = g.getHeight()-Y, FH=g.getFontHeight();
  var titleLines = g.wrapString(options.title, W-2);
  var msgLines = g.wrapString(msg||"", W-2);
  var y = Y + (H + (titleLines.length - msgLines.length)*FH )/2;
  var yt = Y + titleLines.length*FH;
  // colour everything
  g.setColor(g.theme.bgH).fillRect(0,Y,W-1,g.getHeight());
  // add a white inner with rounded borders
  g.setColor(g.theme.bg).
    fillRect(8,yt+16,W-8,g.getHeight()-16).
    fillRect(16,yt+8,W-17,g.getHeight()-8).
    fillCircle(16,yt+16,8).
    fillCircle(W-16,yt+16,8).
    fillCircle(16,g.getHeight()-16,8).
    fillCircle(W-16,g.getHeight()-16,8).
    setColor(g.theme.fg);
  // draw image
  if (options.img) {
    var im = g.imageMetrics(options.img);
    g.drawImage(options.img,(W-im.width)/2,y - im.height/2);
    y += 4+im.height/2;
  }
  // message body
  g.drawString(msgLines.join("\n"),W/2,y);  
  // title
  if (options.title)
    g.setColor(g.theme.fgH).setBgColor(g.theme.bgH).
      drawString(titleLines.join("\n"),W/2,Y+2);
  Bangle.setLCDPower(1); // ensure screen is on
};


E.showMessage("Lots of text will wrap automatically",{
  title:"Warning",
  img:atob("FBQBAfgAf+Af/4P//D+fx/n+f5/v+f//n//5//+f//n////3//5/n+P//D//wf/4B/4AH4A=")
})

Write them to your Bangle.js

This is really easy - simply delete all your test code, so you're left with just something like:

E.showMessage = function(msg,options) {
  // ...
};

Now:

Nothing will happen immediately, but now try typing E.showMessage("Boo!") in the left hand side of the IDE - you'll get the new message box.

Your watch will now be usable as it was before, but all apps will have your updated menu.

Note: The next time you upload, the saved version of your code will not update because Bangle.js caches the boot files. You will need to type load("bootupdate.js") into the left-hand side of the IDE.

Turn your modifications into an app

Now everything is working, you can turn this into an app.

{
  "id": "mytweaks",
  "name": "Rounded showMessage",
  "version": "0.01",
  "description": "Replace built in E.showMessage",
  "icon": "app.png",
  "type": "boot",
  "tags": "system",
  "supports": ["BANGLEJS2"],
  "storage": [
    {"name":"mytweaks.boot.js","url":"boot.js"}
  ]
}

And now you're done! You can either use it on your own personal app loader, or can submit it to the main Bangle.js app loader!

This page is auto-generated from GitHub. If you see any mistakes or have suggestions, please let us know.