Skip to content

rozek/banglejs-2-activities

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

banglejs-2-activities

a series of studies and experiments with Bangle.js 2

The Bangle.js 2 is the second iteration of a JavaScript-programmable smartwatch made available by Espruino creator Gordon Williams.

This repository documents a series of studies and experiments conducted by the author in the course of developing an application for the Bangle.js 2 - looking at the discussions in the Espruino forum, their outcome may be of interest to other people as well.

Usage

Most "activities" come with some source code. Unless otherwise noted, this code may directly be loaded into the Espruino Web IDE - either for the Bangle.js 2 emulator or for a real device.

Just a small note: if you like this write-up and plan to use it, consider "starring" this repository (you will find the "Star" button on the top right of this page), so that I know which of my repositories to take most care of.

Overview

A click on one of the links shown below leads directly to the corresponding topic:

Color Handling


Basic Colors

Bitmap Preparation

"Half Colors"

"Quarter Colors"

Color Wheel

Color Wedges

Color Stripes

Color Discs

Theming


Theme Display

Theme Setter

Layouting


additional Concepts

Label

Image

Drawable

Button

Checkbox

Radiobutton

Analog Clock Faces


Quarter Hour Numbers

Hour Numbers

colored Hour Numbers

colored Hour Numbers
with Dots

Hour and Minute Hands

Hour, Minute and
Second Hands

rounded Hands

hollow Hands

Miscellany


drawRoundedRect

fillRoundedRect

drawMoonPhase

Color Handling

Basic Colors

Because of a restriction to 3-bit colors, a Bangle.js 2 may only display 8 different colors without dithering.

The underlying source code for this example may be run both in the emulator and on a real device.


Bitmap Preparation

When it comes to preparing a bitmap for being shown on a Bangle.js 2 display, the converter built into the Web IDE's "Device Storage" manager does a remarkably good job (given the constraints it is suffering from)

However, this step also has its limitations, as shown in the following test image:

Flat gradients seem to cause problems, but detailed areas come out quite well.

"Half Colors"

Forum user "Numerist" suggested to use RGB channel value 0.5 in addition to the natively supported values 0.0 and 1.0 in programs as the dithering algorithm built into the Bangle.js 2 seemed to produce acceptable results:

The source code for this example may be run both in the emulator and on a real device.

Please note that increasing the color resolution (from 8 to 27) comes at a price, namely the decrease of spatial resolution. The actual resolution loss depends very much on what you draw on the screen, but it may well halve your effectively usable screen size to 88x88 pixels.


Forum user "HughB" suggested to make the color patches touchable and display the hexadecimal color code of any touched patch:

The underlying source code may be run both in the emulator and on a real device.

Just tap on any colored patch in order to see the associated color code. Tapping outside any such patch is simply ignored.


"Quarter Colors"

While "Half Colors" still look acceptable, the next refinement step ("Quarter Colors") no longer produces good looking results:

The source code for this example may be run both in the emulator and on a real device.


Color Wheel

Because of a need for a series of different colors, the author made this little "color wheel":

As usual, you may run the source code both in the emulator and on a real device.


 

Again, there is also a touchable version (both for the emulator and a real device) which displays the internal hexdecimal code of any touched color:

The whole screen is touchable:
 
  • touch outside the wheel for "black"
  • touch inside the wheel for "white"
  • touch on any segment of the wheel for the shown color.

This version also exists as a small Bangle.js 2 application - just look for "ColorWheel"


Color Wedges

In view of a planned application, an attempt was made to draw "intensity wedges" in various colors:

You may try yourself:
 
  • fading to black, in the emulator and on a real device, or
  • fading to white, in the emulator and on a real device
 
However, the results don't look promising at all.

In order to be able to assess the problem properly, here are the same images generated with 24-bit graphics during a conversion to 3-bit color:

As one can see, the results of the built-in dithering come pretty close to what you may expect using off-line dithering. Or, in other words, don't try to use color wedges...

Color Stripes

Now that we know that we should avoid intensity gradients on a Bangle.js 2, the question arises whether we may use color gradients, at least.

Technical base for such a gradient is the function E.HSBtoRGB which converts a given "hue" (together with a "saturation" and a "brightness") into an RGB value, that may then be used to set the current foreground color with g.setColor.

A description of the theory behind this conversion may be found in Wikipedia. "Pure" colors are shown at hue angles which are multiples of 60° (i.e., 60°, 120°, 180°, ...) if angles are measured in degrees (which is the common case). In the HSB model, "black" and "white" are not counted as "colors", but may be generated by setting "brightness" or "saturation" to 0, resp.

The first example shows these "pure" colors at their corresponding angles.

You may run the source code both in the emulator and on a real device.


 

The next example shows stripes of colors with hues ranging from 0 to 1 at different resolutions.

The underlying source code may again be run both in the emulator and on a real device.

The result looks surprisingly good - although you should presumably limit yourself to 24 different hue values at most (the second bottom line does so whereas the bottommost one shows 176 different hue values)


This example shows a color wheel with said 24 different hue values.

As usual, you may run the source code both in the emulator and on a real device.


 

As before, here is again a similar image generated using 24-bit graphics during a conversion to 3-bit colors:

Color Discs

The promising outcome of the previous experiment immediately arouses interest in further studies:

This example shows a color disc with 24 different hue and 5 different brightness values.

You may run the source code both in the emulator and on a real device.


 

And here is the same with 24 different hue and 5 different saturation values.

This source code may also be run both in the emulator and on a real device.


 

More than 4 gradations of brightness or saturation don't seem useful as the result of reducing brightness or saturation in smaller steps becomes increasingly unpredictable after dithering:

  • source code for brightness example
    • to be run in the emulator
    • to be run on a real device
 
  • source code for saturation example
    • to be run in the emulator
    • to be run on a real device


 

As usual, here is a similar image generated using 24-bit graphics during a conversion to 3-bit colors:

Theming

In order to unify the visual appearance of multiple Bangle.js apps, users may configure a "theme" consisting of 6 different color settings.

Theme Display

This example displays the currently active theme settings.

The underlying source code for this example may be run both in the emulator and on a real device.


ThemeSetter

"Theme Setter" is a small application allowing to configure the global theming of bangle.js apps (provided that these apps consider the configured theme and do not provide their own color settings)


Right now, you may find the app in the author's personal app store only - but soon, hopefully, in the official one as well.

The underlying sources may be found in another repository on GitHub.

Layouting

The built-in layout library significantly simplifies the implementation of non-trivial user interfaces. It has its limitations, but these may easily be overcome with "custom renderers" and functions which generate the appropriate layout descriptions.

Additional Concepts

The built-in "layout" library is really helpful when it comes to creating non-trivial user interfaces on the Bangle - but it has its limitations. Two of them are really painful:

  • the set of built-in component types is rather limited - fortunately, however, you can provide your own renderers, at least
  • the way of describing a user interface tends to repetitive writing down of the same settings over and over again

The controls mentioned in this section deal with these pain points in the following manner:

  • "factory functions" - to be used instead of object literals - provide a more abstract and, thus, more comfortable way of describing individual components as they can hide details of their settings from the programmer
  • "common settings" may be defined (once) in form of an object literal and then provided as part of a control description in a (newly introduced) common attribute - the "factory functions" already take care of this attribute and use them as defaults. "Common settings" may be easily cascaded (by means of Object.assign) and overwritten in factory functions, if need be

Factory Functions

Using factory functions is easy - as shown in the following (synthetic) example:

let Display = new Layout(
  Label('Test',{ bold:true })
);
Display.render();

Their output is nothing else but a plain JavaScript object as expected by the layout library. Most often, the type of such a description will be custom - in that case, the factory function also provides the appropriate render function.

The following code (taken from an early version of the "Label" component) shows a typical implementation of such a "factory function":

  function Label (Text, Options) {
    function renderLabel (Details) {
      let halfWidth  = Details.w/2, xAlignment = Details.halign || 0;
      let halfHeight = Details.h/2, yAlignment = Details.valign || 0;
      let Padding = Details.pad || 0;

      g.setColor(Details.col || g.theme.fg || '#000000');

      if (Details.font != null) { g.setFont(Details.font); }
      g.setFontAlign(xAlignment,yAlignment);

      let x = Details.x + halfWidth  + xAlignment*(halfWidth+Padding);
      let y = Details.y + halfHeight + yAlignment*(halfHeight+Padding);

      g.drawString(Details.label, x,y);
      if (Details.bold) {
        g.drawString(Details.label, x+1,y);
        g.drawString(Details.label, x,y+1);
        g.drawString(Details.label, x+1,y+1);
      }
    }

    let Result = Object.assign((
      Options == null ? {} : Object.assign({}, Options.common || {}, Options)
    ), {
      type:'custom', render:renderLabel, label:Text || ''
    });
      let TextMetrics;
      if (! Result.width || ! Result.height) {
        if (Result.font != null) { g.setFont(Result.font); }
        TextMetrics = g.stringMetrics(Result.label);
      }

      Result.width  = Result.width  || TextMetrics.width  + 2*(Result.pad || 0);
      Result.height = Result.height || TextMetrics.height + 2*(Result.pad || 0);
    return Result;
  }

It supports an Options argument for specific settings, takes care of "common settings" (see below), computes its minimal size and provides its own renderer.

Common Settings

"Common settings" are like style classes for HTML elements - but stripped down and simplified for Bangle.js.

Using common settings is easy - as shown in the following (synthetic) example:

let StdFont = { font:'12x20' };
let legible = Object.assign({ col:'#000000', bgCol:'#FFFFFF' }, StdFont);
let Display = new Layout(
  Label('Test',{ common:legible, bold:true })
);
Display.render();

"Common settings" are plain JavaScript objects containing attributes supported by the layout library (or "factory functions") with values that should be shared. With the help of Object.assign, individual settings may be cascaded and merged into new ones. Attributes specified alongside common settings have priority and override them.

Generic Event Dispatching

Some user interfaces may not expect user interaction - but most of them do. Until now, the layout library provides very little support for event handling only. The "generic event dispatching" described below solves this problem in a very lightweight way by scanning the currently active layout for the outermost control at the location of a touch (or similar) and invoking that control's handler for the event (if one is provided). In this way, every control (even labels or vertical/horizontal layouts) may react to any kind of event.

"Generic event dispatching" requires a few common functions and event-specific handler functions in any event consuming controls.

/**** EventConsumerAtPoint ****/

  let activeLayout;

  function EventConsumerAtPoint (HandlerName, x,y) {
    let Layout = (activeLayout || {}).l;
    if (Layout == null) { return; }

    function ConsumerIn (Control) {
      if (
        (x < Control.x) || (x >= Control.x + Control.w) ||
        (y < Control.y) || (y >= Control.y + Control.h)
      ) { return undefined; }

      if (typeof Control[HandlerName] === 'function') { return Control; }

      if (Control.c != null) {
        let ControlList = Control.c;
        for (let i = 0, l = ControlList.length; i < l; i++) {
          let Consumer = ConsumerIn(ControlList[i]);
          if (Consumer != null) { return Consumer; }
        }
      }

      return undefined;
    }

    return ConsumerIn(Layout);
  }

/**** dispatchTouchEvent ****/

  function dispatchTouchEvent (DefaultHandler) {
    function handleTouchEvent (Button, xy) {
      if (activeLayout == null) {
        if (typeof DefaultHandler === 'function') {
          DefaultHandler();
        }
      } else {
        let Control = EventConsumerAtPoint('onTouch', xy.x,xy.y);
        if (Control != null) {
          Control.onTouch(Control, Button, xy);
        }
      }
    }
    Bangle.on('touch',handleTouchEvent);
  }
  dispatchTouchEvent();

/**** dispatchStrokeEvent ****/

  function dispatchStrokeEvent (DefaultHandler) {
    function handleStrokeEvent (Coordinates) {
      if (activeLayout == null) {
        if (typeof DefaultHandler === 'function') {
          DefaultHandler();
        }
      } else {
        let Control = EventConsumerAtPoint('onStroke', Coordinates.xy[0],Coordinates.xy[1]);
        if (Control != null) {
          Control.onStroke(Control, Coordinates);
        }
      }
    }
    Bangle.on('stroke',handleStrokeEvent);
  }
  dispatchStrokeEvent();

After executing the code from above, event handling becomes really easy:

  let Layout = require('Layout');
  let activeLayout = new Layout({
    type:'txt', label:'Touch here', id:'Test',
      onTouch:(Control) => { print('touched "' + Control.id + '"'); }
  });
  activeLayout.render();

Several "screens" may be implemented by changing (and rerendering) activeLayout - events will always be dispatched to the currently active screen.

If you don't have a layout (because you implemented a clock with additional configuration screens - and the clock itself does not use a layout), you may provide a "default handler" for dispatchTouchEvent or dispatchStrokeEvent which is invoked instead of a control-specific event handler.

Label

The "Label" component is an enhanced counterpart of the built-in "txt" layout type - with the following additional features:

  • labels may be horizontally and/or vertically aligned within their layout cell
  • "Label" components may be bordered and that border may be drawn in a given color or remain "transparent". Since the layout of a component with a "transparent" border is the same as with an opaque one, borders may be used as selection indicators
  • "Label" components may be "highlighted" (e.g., to indicate a selection)
  • drawing is restricted to the computed layout cell (less any padding and bordering)

The following screenshot illustrates many of these features:

source code for the "Label" component itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device

(the demonstrator also already uses the new "common settings" feature)


 

Label is actually a factory function with the following signature:

Label(Text, Options)

and the following arguments:

  • Text - specifies the text to be shown
  • Options - is an optional object containing named options (see below)

Options is a JavaScript object basically containing the same options you normally specify when describing a component for the layout library (including font, col, bgCol etc.) with the following particularities:

  • font - optionally specifies the font to be used for rendering the given text. If not explicitly given, Label uses the font which was configured when the factory function was invoked
  • width - optionally specifies the requested minimum width of a label. If not explicitly defined, the width of the given text is used (when rendered using the configured font)
  • height - optionally specifies the requested minimum height of a label. If not explicitly defined, the height of the given text is used (when rendered using the configured font)
  • border - is an optional integer ≧ 0 which indicates whether a border should be drawn at the outer edges of (but still within) the layout cell and, if so, how thick that border should be. The color of that border may be specified by means of BorderColor - otherwise, the current foreground color will be used. If border is 0, no border will be drawn
  • BorderColor - optionally specifies the color, in which a border should be drawn (provided, that the given border value is greater than zero). If BorderColor is set to null, space for a border will be provided, but no actual border drawn. If not explicitly defined, the fg color of the current theme at the time of drawing is also used for the border
  • halign - either -1 to left-align the given text, 0 to center it horizontally, or 1 to right-align it. By default, a text is centered within its layout cell
  • valign - either -1 to top-align the given text, 0 to center it vertically, or 1 to bottom-align it. By default, a text is centered within its layout cell
  • hilite - optionally specifies whether the component should be "highlighted" or not (by default, it is not). If highlighted, the current theme's fgH and bgH highlighting colors are used to draw background and text
  • col - optionally specifies the color in which the given text is drawn. If not explicitly defined, the fg color of the current theme at the time of drawing is used
  • bgCol - optionally specifies the color with which the background of a layout cell is filled before the actual text is drawn. If not explicitly defined, the layout cell will not be filled with any color at all
  • bold - when set to true, the given Text is shown in bold, otherwise it is drawn normally

Any other option is simply passed through to the layout library without further processing.

Implementation note: if bold is set to true, the given text is drawn four times - once at the original x,y coordinates, and then again with an offset of 1 pixel in any direction. This implementation is not really efficient, but produces a reasonably good looking effect independent of the currently used font.

Important note: if you plan to use fonts 6x15 or 12x20 for Label controls, you will have to explicitly configure them as part of a label's options since the implementation cannot retrieve the current setting if such a font has been set (this is due to restrictions within Espruino itself, see this discussion in the forum)

Image

The "Image" component is an enhanced counterpart of the built-in "img" layout type - with the following additional features:

  • images may be rotated (the built-in "img" layout type supports scaling only)
  • images may be horizontally and/or vertically aligned within their layout cell
  • "Image" components may be bordered and that border may be drawn in a given color or remain "transparent". Since the layout of a component with a "transparent" border is the same as with an opaque one, borders may be used as selection indicators
  • "Image" components may be "highlighted" (e.g., to indicate a selection)
  • drawing is restricted to the computed layout cell (less any padding and bordering)

The following screenshot illustrates many of these features:

source code for the "Image" component itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device

(the demonstrator also already uses the new "common settings" feature)


 

Image is actually a factory function with the following signature:

Image(Image, Options)

and the following arguments:

  • Image - specifies the image to be shown (in any format supported by g.imageMetrics and g.drawImage)
  • Options - is an optional object containing named options (see below)

Options is a JavaScript object basically containing the same options you normally specify when describing a component for the layout library (including halign, valign, col, bgCol etc.) with the following particularities:

  • width - optionally specifies the requested minimum width of an Image. If not explicitly defined, the width of the given image is used - after applying the configured scale, but without considering any configured rotation
  • height - optionally specifies the requested minimum height of an Image. If not explicitly defined, the height of the given image is used - after applying the configured scale, but without considering any configured rotation
  • border - is an optional integer ≧ 0 which indicates whether a border should be drawn at the outer edges of (but still within) the layout cell and, if so, how thick that border should be. The color of that border may be specified by means of BorderColor - otherwise, the current foreground color will be used. If border is 0, no border will be drawn
  • BorderColor - optionally specifies the color, in which a border should be drawn (provided, that the given border value is greater than zero). If BorderColor is set to null, space for a border will be provided, but no actual border drawn. If not explicitly defined, the fg color of the current theme at the time of drawing is also used for the border
  • halign - either -1 to left-align the given image, 0 to center it horizontally, or 1 to right-align it. By default, an image is centered within its layout cell
  • valign - either -1 to top-align the given image, 0 to center it vertically, or 1 to bottom-align it. By default, an image is centered within its layout cell
  • scale - optionally specifies the factor, by which the given Image should be scaled before being drawn. If not explicitly defined, a scale factor of 1 is assumed
  • rotate - optionally specifies an agle (in radians) by which the given Image should be rotated before being drawn. If not explicitly defined, a rotation angle of 0 is assumed
  • hilite - optionally specifies whether the component should be "highlighted" or not (by default, it is not). If highlighted, the current theme's fgH and bgH highlighting colors are used to draw background and image
  • col - optionally specifies the color in which the given image is drawn. If not explicitly defined, the fg color of the current theme at the time of drawing is used
  • bgCol - optionally specifies the color with which the background of a layout cell is filled before the actual image is drawn. If not explicitly defined, the layout cell will not be filled with any color at all

Any other option is simply passed through to the layout library.

Drawable

The "Drawable" component is the enhanced counterpart of the built-in "custom" layout type - with the following additional features:

  • drawings may be horizontally and/or vertically aligned within their layout cell
  • "Drawable" components may be bordered and that border may be drawn in a given color or remain "transparent". Since the layout of a component with a "transparent" border is the same as with an opaque one, borders may be used as selection indicators
  • "Drawable" components may be "highlighted" (e.g., to indicate a selection)
  • drawing is restricted to the computed layout cell (less any padding and bordering)

The following screenshot illustrates many of these features:

source code for the "Drawable" component itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device

(the demonstrator also already uses the new "common settings" feature)


 

Drawable is actually a factory function with the following signature:

Drawable(Callback, Options)

and the following arguments:

  • Callback - specifies the function which is automatically invoked whenever the "Drawable" should be rendered (see below for its signature) and is responsible for the actual drawing
  • Options - is an optional object containing named options (see below)

Options is a JavaScript object basically containing the same options you normally specify when describing a component for the layout library (including halign, valign, col, bgCol etc.) with the following particularities:

  • DrawableWidth - optionally specifies the requested minimum width of the "Drawable" itself (not its layout cell!) If not explicitly defined, a minimum width of 10 pixels is assumed
  • DrawableHeight - optionally specifies the requested minimum height of the "Drawable" itself (not its layout cell!) If not explicitly defined, a minimum height of 10 pixels is assumed
  • width - optionally specifies the requested minimum width of a layout cell for the "Drawable". If not explicitly defined, the configured width of the "Drawable" itself is used
  • height - optionally specifies the requested minimum height of a layout cell for the "Drawable". If not explicitly defined, the configured height of the "Drawable" itself is used
  • border - is an optional integer ≧ 0 which indicates whether a border should be drawn at the outer edges of (but still within) the layout cell and, if so, how thick that border should be. The color of that border may be specified by means of BorderColor - otherwise, the current foreground color will be used. If border is 0, no border will be drawn
  • BorderColor - optionally specifies the color, in which a border should be drawn (provided, that the given border value is greater than zero). If BorderColor is set to null, space for a border will be provided, but no actual border drawn. If not explicitly defined, the fg color of the current theme at the time of drawing is also used for the border
  • halign - either -1 to left-align the given "Drawable", 0 to center it horizontally, or 1 to right-align it. By default, a "Drawable" is centered within its layout cell
  • valign - either -1 to top-align the given "Drawable", 0 to center it vertically, or 1 to bottom-align it. By default, a "Drawable" is centered within its layout cell
  • hilite - optionally specifies whether the component should be "highlighted" or not (by default, it is not). If highlighted, the current theme's fgH and bgH highlighting colors are used for background and drawing
  • col - optionally specifies the foreground color for the drawing. If not explicitly defined, the fg color of the current theme at the time of drawing is used
  • bgCol - optionally specifies the color with which the background of a layout cell is filled before the actual "Drawable" is drawn. If not explicitly defined, the layout cell will not be filled with any color

Any other option is simply passed through to the layout library.

"Drawable" callbacks should have the following signature:

function (DrawableX,DrawableY, DrawableWidth,DrawableHeight, Details) { ... }

with the following arguments:

  • DrawableX - contains the x coordinate of the drawing region's left upper corner after applying any border, padding and horizontal alignment
  • DrawableY - contains the y coordinate of the drawing region's left upper corner after applying any border, padding and vertical alignment
  • DrawableWidth - contains the configured width of this "Drawable". This width may be greater than that of the assigned layout cell - in that case, any attempts to draw outside that cell will be inhibited
  • DrawableHeight - contains the configured height of this "Drawable". This height may be greater than that of the assigned layout cell - in that case, any attempts to draw outside that cell will be inhibited
  • Details - contains the complete layout descriptor for the control (as provided by the layout library itself)

Before the callback actually gets invoked, the "Drawable" clears the whole layout cell (if a bgCol has been configured or hilite has been set to true), sets the configured foreground and background colors and prepares a clipping rectangle as an intersection of the assigned layout cell and the requested drawing region.

Button

The "Button" component is the enhanced counterpart of the built-in "btn" layout type - with the following additional features:

  • basically, "Button" components are just text labels (drawn in bold) with a rounded rectangle border
  • "Button" components may be "highlighted" (e.g., to emphasize a preferred button)
  • label drawing is restricted to the computed layout cell (less any padding and the border)

The following screenshot illustrates many of these features:

source code for the "Button" component itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device

(the demonstrator also already uses the new "common settings" feature)


 

Button is actually a factory function with the following signature:

Button(Text, Options)

and the following arguments:

  • Text - specifies the text to be shown
  • Options - is an optional object containing named options (see below)

Options is a JavaScript object basically containing the same options you normally specify when describing a component for the layout library (including halign, valign, col, bgCol etc.) with the following particularities:

  • width - optionally specifies the requested minimum width of a layout cell for the "Button". If not explicitly defined, the width of the given text (when rendered using the configured font) plus some room for the border is used
  • height - optionally specifies the requested minimum height of a layout cell for the "Button". If not explicitly defined, the height of the given text (when rendered using the configured font) plus some room for the border is used
  • hilite - optionally specifies whether the inside of the button should be "highlighted" or not (by default, it is not). If highlighted, the current theme's fgH and bgH highlighting colors are used for background, border and label
  • col - optionally specifies the foreground color for label and border of the button. If not explicitly defined, the fg color of the current theme at the time of drawing is used
  • bgCol - optionally specifies the color with which the background of a layout cell is filled before the actual "Button" is drawn. If not explicitly defined, the layout cell will not be filled with any color

Any other option is simply passed through to the layout library.

Note: in order to react on button presses, just pass an event handler as onTouch option.

Important note: if you plan to use fonts 6x15 or 12x20 for Button controls, you will have to explicitly configure them as part of a button's options since the implementation cannot retrieve the current setting if such a font has been set (this is due to restrictions within Espruino itself, see this discussion in the forum)

Checkbox

The "Checkbox" component represents a small checkbox which may either be in a checked or an unchecked state (as illustrated by the following screenshot):

source code for the "Checkbox" component itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device

(the demonstrator also already uses the new "common settings" feature)


 

Checkbox is actually a factory function with the following signature:

Checkbox(Options)

and the following argument:

  • Options - is an optional object containing named options (see below)

Options is a JavaScript object basically containing the same options you normally specify when describing a component for the layout library (including halign, valign, col, bgCol etc.) with the following particularities:

  • width - optionally specifies the requested minimum width of a layout cell for the "Checkbox". If not explicitly defined, a width of 20 pixels is assumed
  • height - optionally specifies the requested minimum height of a layout cell for the "Checkbox". If not explicitly defined, a height of 20 pixels is assumed
  • col - optionally specifies the foreground color for the checkbox. If not explicitly defined, the fg color of the current theme at the time of drawing is used
  • bgCol - optionally specifies the color with which the background of a layout cell is filled before the actual "Checkbox" is drawn. If not explicitly defined, the layout cell will not be filled with any color
  • checked - if set to true, the checkbox is considered as "checked", otherwise as "unchecked". During operation, this attribute may change its initial value in order to always reflect the current checkbox state
  • onChange - optionally specifies a function which is to be called whenever the checkbox's state changes. This function has the signature function onChange (Control)

Any other option is simply passed through to the layout library.

Tip: if you want to increase the touch area for a given checkbox and combine it with a textual or graphical label, simply pack both into a horizontal or vertical layout and add the following option to that layout: onTouch:toggleInnerCheckbox. From then on, touching anywhere within the layout area will toggle the contained checkbox. Note: do not pack multiple checkboxes into the same layout component with such a handler!

Radiobutton

The "Radiobutton" component represents a small "radio button" that (normally) belongs to a "radio button group" in which at most one button may be "checked" while all others are "unchecked" (as illustrated by the following screenshot):

source code for the "Radiobutton" component itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device

(the demonstrator also already uses the new "common settings" feature)


 

Radiobutton is actually a factory function with the following signature:

Radiobutton(Options)

and the following argument:

  • Options - is an optional object containing named options (see below)

Options is a JavaScript object basically containing the same options you normally specify when describing a component for the layout library (including halign, valign, col, bgCol etc.) with the following particularities:

  • GroupName - optionally specifies the name of a group of radio buttons within which at most one radio button may be "checked" while all others are "unchecked". Radio buttons belonging to the same group may be distributed throughout the whole currently active layout
  • width - optionally specifies the requested minimum width of a layout cell for the "Radiobutton". If not explicitly defined, a width of 20 pixels is assumed
  • height - optionally specifies the requested minimum height of a layout cell for the "Radiobutton". If not explicitly defined, a height of 20 pixels is assumed
  • col - optionally specifies the foreground color for the radio button. If not explicitly defined, the fg color of the current theme at the time of drawing is used
  • bgCol - optionally specifies the color with which the background of a layout cell is filled before the actual "Radiobutton" is drawn. If not explicitly defined, the layout cell will not be filled with any color
  • checked - if set to true, the radio button is considered as "checked", otherwise as "unchecked". During operation, this attribute may change its initial value in order to always reflect the current radio button state. Note: the factory function itself does not check whether multiple radio buttons of the same group have initially been set to "checked" - this is only guaranteed during operation whenever an unchecked radio button becomes checked
  • onChange - optionally specifies a function which is to be called whenever the radio button's state changes to "checked". This function has the signature function onChange (Control)

Any other option is simply passed through to the layout library.

Tip: if you want to increase the touch area for a given radio button and combine it with a textual or graphical label, simply pack both into a horizontal or vertical layout and add the following option to that layout: onTouch:checkInnerRadiobutton. From then on, touching anywhere within the layout area will check the contained radio button. Note: do not pack multiple radio buttons into the same layout component with such a handler!

Analog Clock Faces

At the time of this writing, there are many clock faces for the Bangle.js 2 - even analog ones - but very few (one?) that also display numbers. The following examples have been written to fill this gap.

QuarterHour Numbers

source code for clock face only
  • to be run in the emulator or
  • to be run on a real device
 
source code for clock face and hands
  • to be run in the emulator or
  • to be run on a real device


The referenced source code considers the currently configured "theme" (and may therefore look different than shown in the screenshot on your watch depending on which theme you prefer).

A clock made with this face is perfectly readable even without backlight. If you don't need to know the seconds, you may even remove the second hand (and reduce power consumption a bit)

Hour Numbers

source code for clock face only
  • to be run in the emulator or
  • to be run on a real device
 
source code for clock face and hands
  • to be run in the emulator or
  • to be run on a real device


The referenced source code considers the currently configured "theme" (and may therefore look different than shown in the screenshot on your watch depending on which theme you prefer).

Colored Hour Numbers

source code for clock face only
  • to be run in the emulator or
  • to be run on a real device
 
source code for clock face and hands
  • to be run in the emulator or
  • to be run on a real device


The referenced source code considers the currently configured "theme" (and may therefore look different than shown in the screenshot on your watch depending on which theme you prefer).

Colored Hour Numbers with Dots

source code for clock face only
  • to be run in the emulator or
  • to be run on a real device
 
source code for clock face and hands
  • to be run in the emulator or
  • to be run on a real device


The referenced source code considers the currently configured "theme" (and may therefore look different than shown in the screenshot on your watch depending on which theme you prefer).

Every analog clock needs some hands - here are code snippets for clocks with hour and minute hands and with hour, minute and second hands

Hour and Minute Hands

source code for hands
  • to be run in the emulator or
  • to be run on a real device


 

The referenced source code considers the currently configured "theme" (and may therefore look different than shown in the screenshot on your watch depending on which theme you prefer).

Hour, Minute and Second Hands

source code for hands
  • to be run in the emulator or
  • to be run on a real device

If you like it minimalistic, you may even make a clock just from these hands only.


 

The referenced source code considers the currently configured "theme" (and may therefore look different than shown in the screenshot on your watch depending on which theme you prefer).

Rounded Hands

At the request of user "HughB" from the Espruino forum the author has also implemented some "rounded hands".

source code for hands
  • to be run in the emulator or
  • to be run on a real device

These hands also include a stylish "bolt" in the center.

A minimal clock with just these hands only (and no real clock face) can be found in the author's personal app store.


The referenced source code considers the currently configured "theme" (and may therefore look different than shown in the screenshot on your watch depending on which theme you prefer).

Hollow Hands

At the request of user "HughB" from the Espruino forum the author has implemented the "hollow hands" which can be found in some mechanical watches.

source code for hands
  • to be run in the emulator or
  • to be run on a real device

These hands also include a small "bolt" in the center.


 

The referenced source code considers the currently configured "theme" (and may therefore look different than shown in the screenshot on your watch depending on which theme you prefer).

Widget Handling

Since clocks are shown all the time, they should probably take care of any installed "widgets" and display them properly along with the clock itself. If widgets occupy the corners of a Bangle screen only, not many code changes will be necessary - otherwise, the actual clock will have to be scaled down in order to cover that part of the screen only which is guaranteed to be free of widgets.

source code for widget handling only
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device

Please note: widget display on a dark background is not yet optimal - does anybody have an idea how to enhance it?


Miscellany

drawRoundedRect

The need to draw rectangles with rounded corners arose while developing an app with (customizable) buttons in its user interface. The current code was developed based on a hint at a Bresenham algorithm for circles mentioned by user "RaoulDuke" in the Espruino forum and the discussions that followed.

source code for drawRoundedRect itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device


 

drawRoundedRect has the signature

g.drawRoundedRect(x1,y1, x2,y2, r);

with the following arguments:

  • x1 - x coordinate of first corner
  • y1 - y coordinate of first corner
  • x2 - x coordinate of second corner (opposite the first one)
  • y2 - y coordinate of second corner (opposite the first one)
  • r - corner radius

r is limited to 50% of the shorter side of the rectangle and will be reduced automatically when needed.

drawRoundedRect has been designed as a "polyfill" for the global graphics context g (and will no longer install itself as soon as Espruino provides its own method with that name) and draws the (rounded) rectangle in the current foreground color.

fillRoundedRect

fillRoundedRect is a by-product of the drawRoundedRect development

source code for fillRoundedRect itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device


 

fillRoundedRect has the signature

g.fillRoundedRect(x1,y1, x2,y2, r);

with the following arguments:

  • x1 - x coordinate of first corner
  • y1 - y coordinate of first corner
  • x2 - x coordinate of second corner (opposite the first one)
  • y2 - y coordinate of second corner (opposite the first one)
  • r - corner radius

r is limited to 50% of the shorter side of the rectangle and will be reduced automatically when needed.

fillRoundedRect has been designed as a "polyfill" for the global graphics context g (and will no longer install itself as soon as Espruino provides its own method with that name) and fills the (rounded) rectangle with the current foreground color.

drawMoonPhase

As a by-product of the draw/fillRoundedRect development, drawMoonPhase was written at a request from user "HilmarSt" in the Espruino forum.

source code for drawMoonPhase itself
 
source code for the demonstrator
  • to be run in the emulator or
  • to be run on a real device


 

drawMoonPhase has the signature

drawMoonPhase(CenterX,CenterY, MoonRadius, leftFactor,rightFactor);

with the following arguments:

  • CenterX - x coordinate of moon center
  • CenterY - y coordinate of moon center
  • MoonRadius - moon radius
  • leftFactor - is used to control the moon phase (see below)
  • rightFactor - is used to control the moon phase (see below)

leftFactor and rightFactor are values in the range -1.0...1.0 which control the actually shown moon phase:

  • rightFactor = 1.0 and leftFactor starting from -1.0 and increasing up to 1.0 show a waxing moon
  • rightFactor = 1.0 and leftFactor = 1.0 show a full moon
  • leftFactor = 1.0 and rightFactor starting from 1.0 and decreasing down to -1.0 show a waning moon

License

MIT License

About

a series of studies and experiments with Bangle.js 2

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published