-
In the context of learning how to write (non-trivial) apps for the Bangle.js 2, I've implemented a "ThemeSetter" which allows you to configure global theming in a more comfortable way than through the settings menu.
While it already uses the "layout" library, I still had to implement a lot myself, such as
- text views with configurable alignment and "bold" text,
- a few custom controls, and
- generic event dispatching
The current state can already be found in my own app loader - but soon, hopefully, also in the official "app store". Source code is available as well.
After lunch, I'll start some "code golfing" in order to make the source more compact - e.g., by introducing a mechanism similar to the "class" attributes of HTML elements.
- text views with configurable alignment and "bold" text,
-
-
I've just added a
bold
option to the "Label" component - see GitHub for details -
See attached screenshot as an example - I'd definitely prefer
- a white lock icon,
- a battery icon with black and white exchanged, and
- a version display with white text on black background
Keep in mind that, e.g., the lock icon can only be recognized because I still draw the expected white background - if I would use a bitmap as background, I would not clear the widgets area first...
- a white lock icon,
-
well,
since I could not find an existing method, I quickly hacked one myself (good enough for my own purposes):
/**** drawRoundedRect ****/ const roundedRectSines = [ 0, Math.sin(15*Math.PI/180), Math.sin(30*Math.PI/180), Math.sin(45*Math.PI/180), Math.sin(60*Math.PI/180), Math.sin(75*Math.PI/180), 1 ]; const roundedRectPoly = Array(56); function prepareRoundedRect (x1,y1, x2,y2, r) { r = Math.min(r || 0, Math.abs(x1-x2), Math.abs(y1-y2)); for (let i = 0, j = 0; i <= 6; i++, j += 2) { roundedRectPoly[j] = x1 + r - r*roundedRectSines[6-i]; roundedRectPoly[j+1] = y1 + r - r*roundedRectSines[i]; } for (let i = 0, j = 14; i <= 6; i++, j += 2) { roundedRectPoly[j] = x2 - r + r*roundedRectSines[i]; roundedRectPoly[j+1] = y1 + r - r*roundedRectSines[6-i]; } for (let i = 0, j = 28; i <= 6; i++, j += 2) { roundedRectPoly[j] = x2 - r + r*roundedRectSines[6-i]; roundedRectPoly[j+1] = y2 - r + r*roundedRectSines[i]; } for (let i = 0, j = 42; i <= 6; i++, j += 2) { roundedRectPoly[j] = x1 + r - r*roundedRectSines[i]; roundedRectPoly[j+1] = y2 - r + r*roundedRectSines[6-i]; } } g.drawRoundedRect = function drawRoundedRect (x1,y1, x2,y2, r) { prepareRoundedRect(x1,y1, x2,y2, r); this.drawPoly(roundedRectPoly,true); } g.fillRoundedRect = function fillRoundedRect (x1,y1, x2,y2, r) { prepareRoundedRect(x1,y1, x2,y2, r); this.fillPoly(roundedRectPoly,true); } g.clear(); g.setColor('#000000'); g.drawRoundedRect(10,20, 160,100, 20); g.fillRoundedRect(40,10, 100,160, 10);
The two functions have been designed to be added to the
g
variable in order to be invoked like any other of its methods - but there is certainly a better alternative. -
-
-
I guess you refer to the line
g.setTheme({bg:"#f0f",fg:"#fff",dark:true}).clear();
This is actually an variant I tried before - and it is not working!
If background is globally set to green, f.e., it remains green even after that line!
In order to give you some steps to reproduce the problem (in the REPL):
Try
let customTheme = { fg:g.toColor(0,0,0), fg2:g.toColor(0,0,1), fgH:g.toColor(0,0,0), bg:g.toColor(0,1,0), bg2:g.toColor(0,1,0), bgH:g.toColor(1,1,0) }; let globalSettings = Object.assign( require('Storage').readJSON('setting.json', true) || {}, { theme:customTheme } ); require('Storage').writeJSON('setting.json', globalSettings);
first, then try
g.setTheme({ bg:'#000' }); g.clear(false);
(tested on a real device with firmware 2v11)
-
-
The attached screenshot illustrates what I expected - but I reached that state with a custom renderer only:
/**** Label ****/ 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); g.drawString(Details.label, Details.x + halfWidth + xAlignment*(halfWidth+Padding), Details.y + halfHeight + yAlignment*(halfHeight+Padding) ); } let Result = Object.assign({}, Options || {}, { type:'custom', render:renderLabel, label:Text || '' }); let TextMetrics if (! Options.width || ! Options.height) { if (Options.font != null) { g.setFont(Options.font); } TextMetrics = g.stringMetrics(Result.label); } Result.width = Options.width || TextMetrics.width + 2*(Options.pad || 0); Result.height = Options.height || TextMetrics.height + 2*(Options.pad || 0); return Result; }
Here is a simple "smoke test" for the above "component":
let ScreenWidth = g.getWidth(), ColumnWidth = ScreenWidth/3; // 3 columns let ScreenHeight = g.getHeight(), RowHeight = ScreenHeight/3; // and 3 rows g.clear(true); /**** Label ****/ 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); g.drawString(Details.label, Details.x + halfWidth + xAlignment*(halfWidth+Padding), Details.y + halfHeight + yAlignment*(halfHeight+Padding) ); } let Result = Object.assign({}, Options || {}, { type:'custom', render:renderLabel, label:Text || '' }); let TextMetrics if (! Options.width || ! Options.height) { if (Options.font != null) { g.setFont(Options.font); } TextMetrics = g.stringMetrics(Result.label); } Result.width = Options.width || TextMetrics.width + 2*(Options.pad || 0); Result.height = Options.height || TextMetrics.height + 2*(Options.pad || 0); return Result; } g.setFont12x20(); // does not seem to be respected in layout! let Layout = require('Layout'); let Display = new Layout({ type:'v', c:[ { type:'h', c:[ Label('Test',{ valign:-1, halign:-1, font:'12x20', width:ColumnWidth, height:RowHeight }), Label('Test',{ valign:0, halign:-1, font:'12x20', width:ColumnWidth, height:RowHeight }), Label('Test',{ valign:1, halign:-1, font:'12x20', width:ColumnWidth, height:RowHeight }), ] }, { type:'h', c:[ Label('Test',{ valign:-1, halign:0, font:'12x20', width:ColumnWidth, height:RowHeight }), Label('Test',{ valign:0, halign:0, font:'12x20', width:ColumnWidth, height:RowHeight }), Label('Test',{ valign:1, halign:0, font:'12x20', width:ColumnWidth, height:RowHeight }), ] }, { type:'h', c:[ Label('Test',{ valign:-1, halign:1, font:'12x20', width:ColumnWidth, height:RowHeight }), Label('Test',{ valign:0, halign:1, font:'12x20', width:ColumnWidth, height:RowHeight }), Label('Test',{ valign:1, halign:1, font:'12x20', width:ColumnWidth, height:RowHeight }), ] }, ] }); Display.render();
(the "Label" component is part of my Bangle.js 2 activities as documented on GitHub)
-
While global settings may not include an explicit configuration for a global theme, you may still set one which is from then on respected in any apps (unless they override any theme settings):
let customTheme = { fg:g.toColor(0,0,0), fg2:g.toColor(0,0,1), fgH:g.toColor(0,0,0), bg:g.toColor(0,1,0), bg2:g.toColor(0,1,0), bgH:g.toColor(1,1,0) }; let globalSettings = Object.assign( require('Storage').readJSON('setting.json', true) || {}, { theme:customTheme } ); require('Storage').writeJSON('setting.json', globalSettings);
It is important, however, that you use
g.toColor
in order to get the proper color values for the Bangle.js 2! -
-
Strange,
according to the docs, I should be able to define a (local) theme using
g.setTheme({ bg:'#00FF00' }); g.clear(false); // to test the new theme
but that does not seem to be respected. Indeed, none of the alternatives I tried had any effect:
g.setTheme({ bg:'#0F0' }); // g.setTheme({ bg:'#00FF00' }); // g.setTheme({ bg:0x00FF00 }); // g.setTheme({ bg:g.toColor(0,1,0) }); g.clear(false);
What am I doing wrong?
-
I know that I can find the current general settings using
let Settings = require('Storage').readJSON('setting.json', true) || {}; print('Settings',Settings);
but where do I find details of the currently configured theme?
The documentation tells us to look into a file called
settings.js
which sounds strange on one hand and does not exist on the other... -
Is there any possibility to align some text within its cell when using the layout library?
halign
andvalign
don't seem to be respected as you can see yourself by running the following example:let ScreenWidth = g.getWidth(), ColumnWidth = ScreenWidth/3; // 3 columns let ScreenHeight = g.getHeight(), RowHeight = ScreenHeight/3; // and 3 rows g.clear(true); let Layout = require('Layout'); let Display = new Layout({ type:'v', c:[ { type:'h', c:[ { type:'txt', font:'12x20', label:'Test', valign:-1, halign:-1, width:ColumnWidth, height:RowHeight }, { type:'txt', font:'12x20', label:'Test', valign:0, halign:-1, width:ColumnWidth, height:RowHeight }, { type:'txt', font:'12x20', label:'Test', valign:1, halign:-1, width:ColumnWidth, height:RowHeight }, ] }, { type:'h', c:[ { type:'txt', font:'12x20', label:'Test', valign:-1, halign:0, width:ColumnWidth, height:RowHeight }, { type:'txt', font:'12x20', label:'Test', valign:0, halign:0, width:ColumnWidth, height:RowHeight }, { type:'txt', font:'12x20', label:'Test', valign:1, halign:0, width:ColumnWidth, height:RowHeight }, ] }, { type:'h', c:[ { type:'txt', font:'12x20', label:'Test', valign:-1, halign:1, width:ColumnWidth, height:RowHeight }, { type:'txt', font:'12x20', label:'Test', valign:0, halign:1, width:ColumnWidth, height:RowHeight }, { type:'txt', font:'12x20', label:'Test', valign:1, halign:1, width:ColumnWidth, height:RowHeight }, ] }, ] }); Display.render();
-
For those interested: while playing around with theming, I made the following theme display
let ScreenWidth = g.getWidth(), CenterX = ScreenWidth/2; let ScreenHeight = g.getHeight(); g.reset(); // automatically loads current theme g.clearRect(0,0, ScreenWidth,ScreenHeight); g.setFont12x20(); g.setFontAlign(0,-1); g.drawString('current Theme', CenterX,0); g.setFontAlign(-1,-1); let Theme = g.theme; g.setColor(Theme.fg); g.drawString('fg', 0,35); g.drawString('bg', CenterX,35); g.drawString('fg2', 0,70); g.drawString('bg2', CenterX,70); g.drawString('fgH', 0,105); g.drawString('bgH', CenterX,105); g.drawString('dark', 0,140); g.setColor(Theme.fg); g.fillRect(40,35, 70,55); g.setColor(Theme.fg); g.drawRect(CenterX+39,34, CenterX+71,56); g.setColor(Theme.bg); g.fillRect(CenterX+40,35, CenterX+70,55); g.setColor(Theme.fg2); g.fillRect(40,70, 70,90); g.setColor(Theme.fg); g.drawRect(CenterX+39,69, CenterX+71,91); g.setColor(Theme.bg2); g.fillRect(CenterX+40,70, CenterX+70,90); g.setColor(Theme.fgH); g.fillRect(40,105, 70,125); g.setColor(Theme.fg); g.drawRect(CenterX+39,104, CenterX+71,126); g.setColor(Theme.bgH); g.fillRect(CenterX+40,105, CenterX+70,125); g.setColor(Theme.fg); g.drawString(Theme.dark ? 'yes' : 'no', 50,140);
-
I might have found a solution:
- at first, connect using the Web IDE
- then, in another window, open the App Loader and connect (the Web IDE will show you any responses from the watch)
- upgrade any outdated apps (e.g., Settings, Bootloader etc.) and/or delete an old/install a new application
From then on, everything seems to work fine again - but don't ask me why!
- at first, connect using the Web IDE
-
-
I just updated my Bangle.js 2 to firmware 2v11 - but now, the app loader no longer allows me to do anything. Since the error messages ("toasts") disappear after a few seconds (could that be changed, please!), I had to look into the browser debug console for more information. Here is what I found there:
Chosen device BANGLEJS2 puck.js:406 <BLE> Device Name: Bangle.js 8636 puck.js:406 <BLE> Device ID: C6uV/VXV/s05Mi2CwS5cWw== puck.js:406 <BLE> Connected comms.js:151 <COMMS> Ctrl-C gave "\r\u001b[J let date=new Date();g.reset();g.clearRect(0,24,239,239);g.setColor...\r\n ^\r\ndebug>" comms.js:186 <COMMS> ERROR Parsing JSON SyntaxError: Unexpected token I in JSON at position 0 comms.js:187 <COMMS> Actual response: "In debug mode: Expected a simple ID, type 'help' for more info.\r" ui.js:86 <TOAST>[error] Device connection failed, Invalid JSON puck.js:406 <BLE> Disconnected (gattserverdisconnected) comms.js:260 <COMMS> removeAllApps start puck.js:406 <BLE> Device Name: Bangle.js 8636 puck.js:406 <BLE> Device ID: C6uV/VXV/s05Mi2CwS5cWw== puck.js:406 <BLE> Connected comms.js:265 <COMMS> removeAllApps: received "In debug mode: Expected a simple ID, type 'help' for more info.\r" ui.js:86 <TOAST>[error] App removal failed, Got "In debug mode: Expected a simple ID, type 'help' for more info." 2puck.js:406 <BLE> Disconnected (gattserverdisconnected) puck.js:406 <BLE> Device Name: Bangle.js 8636 puck.js:406 <BLE> Device ID: C6uV/VXV/s05Mi2CwS5cWw== puck.js:406 <BLE> Connected comms.js:151 <COMMS> Ctrl-C gave "" puck.js:406 <BLE> SEND ERROR: NotSupportedError: GATT operation failed for unknown reason. 2puck.js:406 <BLE> Disconnected (gattserverdisconnected) comms.js:265 <COMMS> removeAllApps: received null puck.js:406 <BLE> Disconnected (gattserverdisconnected) ui.js:86 <TOAST>[error] App Install failed,
Basically, the watch claims to be "in debug mode" which is why the App Loader does not get the expected JSON.
If I connect to the watch using the Web IDE and then type "help" into its left side, I get the response
"help" is not defined
:>help Uncaught ReferenceError: "help" is not defined at line 1 col 1 help ^
I already reset my smartwatch several times, without any luck.
-
-
-
I'm currently developing clock faces with a black background.
Since clocks should respect any installed widgets, my clock also draws them - however, most widgets seem to expect a white background and look ugly (if not unrecognizable) on a black one.
Does anybody have an idea how to enhance the look of such widgets?
-
...although it may just be a mistake in the documentation - but it took me surprisingly long to find that out.
From the documentation of E.HSBtoRGB, you might be tempted to write
let Color = E.HSBtoRGB(i/Steps,1,1, true); g.setColor(Color[0],Color[1],Color[2]);
but this is wrong! You have to write
let Color = E.HSBtoRGB(i/Steps,1,1, true); g.setColor(Color[0]/255,Color[1]/255,Color[2]/255);
instead in order to get the expected results!
(Please note: you cannot simply write
let Color = E.HSBtoRGB(i/Steps,1,1); g.setColor(Color);
as the Bangle.js 2 does not support 24-bit color)
(see GitHub for some examples)
-
I've started documenting my Bangle.js 2 "activities" on GitHub - this may be a better place for small code snippets than this forum...
Thanks for the remarks - will work on them one after the other. The need for changes will be a nice "stress test" of layout library and my additional stuff.
The icon exists already, but seems to be in a wrong format (although I used to documented way of creating it).
But: is there a (well-known) list of named themes? Or do you expect me to invent them?