Bangle.js Widgets
We'll assume you've already been through the Bangle.js Development page and have an idea how to get started, and have maybe looked at making an app
Widgets are bits of code that run in the background of other applications. For example the Battery level, Bluetooth Connection status, or other things. There are 24 px wide areas at the top and bottom of the screen which are usually reserved for Widgets to draw their information.
Not every application enables widgets (they need to call Bangle.loadWidgets()
and
Bangle.drawWidgets()
) but virtually all clock applications will allow them.
How does it work?
Bangle.loadWidgets()
loads any file ending in .wid.js
, and those
files add widgets to the global WIDGETS
variable.
Bangle.drawWidgets()
then goes through everything in WIDGETS
laying it out according to its position on the screen (tl/tr/bl/br
)
and width, and then drawing them.
When the screen is cleared by the host application, it is responsible
for calling Bangle.drawWidgets()
again.
However if your widget needs to change what it displays, it should
call its draw
method again. If it needs to change its width (eg.
to add a 'charging' indicator) it should call Bangle.drawWidgets()
to force all widgets to lay out and draw again.
How to develop
Developing a widget is much like developing an app - in a lot of ways it is simpler.
You can either develop the widget on its own, or inside another app. First, lets develop on its own since we can do that in the emulator if needed.
First, check out the example widget in apps/_example_widget/
Add WIDGETS = {};
to the top of the code, and Bangle.drawWidgets();
to the bottom.
Now come up with an ID for your widget. Ideally this should start with
wid
, be lowercase without spaces and be less than 20 chard long. Check
out the list of existing apps
for ideas.
Eventually you'll use this ID when adding to the app loader, but you should
also use it when assigning your widget, for example mywidget
in
WIDGETS["mywidget"] = ...
in the code below.
The Example widget will look like:
WIDGETS = {}; // <-- for development only
(() => {
function draw() {
g.reset(); // reset the graphics context to defaults (color/font/etc)
// add your code
g.drawString("X", this.x, this.y);
}
// add your widget
WIDGETS["mywidget"]={
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right), be aware that not all apps support widgets at the bottom of the screen
width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
draw:draw // called to draw the widget
};
})()
Bangle.drawWidgets(); // <-- for development only
Ensure the IDE is set to upload to RAM (the default) - use the down-arrow below the upload button.
If you upload this now, you will see an 'X' in the top left of the screen. Because
Bangle.js's corners are rounded, Widgets do not start at 0,0
but are instead inset
from the side slightly.
Example - displaying the date
As an example, let's display the date...
- Change the widget name from
mywidget
towiddate
(the ID we came up with) - Place it where we want it - in this case
tl
is top left - Change the width
- Write our own
draw
function - in this case draw just the month
In this case I've added a drawRect
so we can see the bounds that
we need to stay within.
WIDGETS = {}; // <-- for development only
(() => {
var width = 24; // width of the widget
function draw() {
var date = new Date();
g.reset(); // reset the graphics context to defaults (color/font/etc)
g.setFontAlign(0,0); // center fonts
g.drawRect(this.x, this.y, this.x+width-1, this.y+23); // check the bounds!
// Use 'locale' module to get a shortened month name
// in the correct language
var text = require("locale").month(date, 1);
g.setFont("6x8");
g.drawString(text, this.x+width/2, this.y+12);
}
setInterval(function() {
WIDGETS["widdate"].draw(WIDGETS["widdate"]);
}, 10*60000); // update every 10 minutes
// add your widget
WIDGETS["widdate"]={
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right), be aware that not all apps support widgets at the bottom of the screen
width: width, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
draw:draw // called to draw the widget
};
})()
Bangle.drawWidgets(); // <-- for development only
The widget should now look like:
Now, we'll need to make sure we update the day when it changes. We could be clever and figure out exactly when the next day was, but for this example we'll just update our widget every 10 minutes:
setInterval(function() {
WIDGETS["widdate"].draw(WIDGETS["widdate"]);
}, 10*60000); // update every 10 minutes
Finally, we might want to add an icon above the day to show what the text is meant to be. So...
- Search for a calandar icon
- Download the image:
- Scale/crop it and convert it to white on black:
- Open the Image Converter page and upload the image
- Choose No transparency, 2 bit black and white, image string
- And you'll get a line like
var img = E.toArrayBuffer(atob("DA0CDQBwv//+////////1VVX0AAH0AAH0AAH0AAH0AAH0AAH1VVXv//+"))
You can now draw the image using some code like:
g.drawImage(atob("DA0CDQBwv//+////////1VVX0AAH0AAH0AAH0AAH0AAH0AAH1VVXv//+"), this.x+6, this.y)
One the image is drawn, you could then draw the current day of the month inside it. And finally, the code looks like:
WIDGETS = {}; // <-- for development only
(() => {
var width = 24; // width of the widget
function draw() {
var date = new Date();
g.reset(); // reset the graphics context to defaults (color/font/etc)
g.setFontAlign(0,0); // center fonts
//g.drawRect(this.x, this.y, this.x+width-1, this.y+23); // check the bounds!
// Draw icon
g.drawImage(atob("DA0CDQBwv//+////////1VVX0AAH0AAH0AAH0AAH0AAH0AAH1VVXv//+"), this.x+6, this.y)
// Draw a small day of the month
g.drawString(date.getDate(), this.x+width/2, this.y+9);
// Use 'locale' module to get a shortened day of the week
// in the correct language
var text = require("locale").month(date,1);
g.setFont("6x8");
g.drawString(text, this.x+width/2, this.y+19);
}
setInterval(function() {
WIDGETS["widdate"].draw(WIDGETS["widdate"]);
}, 10*60000); // update every 10 minutes
// add your widget
WIDGETS["widdate"]={
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right), be aware that not all apps support widgets at the bottom of the screen
width: width, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
draw:draw // called to draw the widget
};
})()
Bangle.drawWidgets(); // <-- for development only
And the widget displayed should be:
Trying in an app
Note: if you're in the emulator you need to have a clock app installed
already. If you don't, run Bangle.factoryReset()
on the left-hand side of
the IDE to install one.
Trying inside an app requires some changes to the IDE's settings. We want to write the widget to a file in Storage, but to then run the default app (a clock) and not the widget itself.
- Click the down-arrow below the Upload button
- Click
Storage
- Click
New File
- Enter
widdate.wid.js
as the name - Open
Settings
,Communications
and changeLoad after saving
toLoad default application
- Delete the lines
WIDGETS = {};
(top) andBangle.drawWidgets();
(bottom) from your code
Now click the upload button and you should see your widget displayed alongside all the other widgets and your clock.
The widget is now installed and usable on your watch, even without the IDE!
Adding to the App Loader
Checking out the page on adding an app to the Bangle.js App Loader
Adding the widget is very similar...
- As mentioned above, come up with a short name (no spaces) for your widget that isn't listed in https://github.com/espruino/BangleApps/tree/master/apps - ideally starting with
wid
- Copy the example widget to
apps/yourshortname
- Save your widget's code to
apps/yourshortname/wid.js
- Find an icon for the app loader and put it in
apps/yourshortname/widget.png
- Open
apps/yourshortname/metadata.json
, change7chname
toyourshortname
, add your widget's details
You can then try it out your own custom App Loader and issue a Pull Request (PR) to add 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.