diff --git a/README.md b/README.md index ee555cad2..b3da9f685 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,7 @@ and which gives information about the app for the Launcher. // 'clock' - a clock - required for clocks to automatically start // 'widget' - a widget // 'bootloader' - an app that at startup (app.boot.js) but doesn't have a launcher entry for 'app.js' + // 'settings' - apps that appear in Settings->Apps (with appname.settings.js) but that have no 'app.js' // 'RAM' - code that runs and doesn't upload anything to storage // 'launch' - replacement 'Launcher' // 'textinput' - provides a 'textinput' library that allows text to be input on the Bangle diff --git a/android.html b/android.html new file mode 100644 index 000000000..93999008f --- /dev/null +++ b/android.html @@ -0,0 +1,352 @@ + + +
+ + + + + + + + + + + + + + + + ++ STOP! This page must be served over HTTPS. Please reload this page via HTTPS. +
+
diff --git a/apps/speedalt2/app.js b/apps/speedalt2/app.js
index ed16131a4..4cdf71913 100644
--- a/apps/speedalt2/app.js
+++ b/apps/speedalt2/app.js
@@ -5,8 +5,9 @@ Mike Bennett mike[at]kereru.com
1.14 : Add VMG screen
1.34 : Add bluetooth data stream for Droidscript
1.43 : Keep GPS in SuperE mode while using Droiscript screen mirroring
+1.50 : Add cfg.wptSfx one char suffix to append to waypoints.json filename. Protects speedalt2 waypoints from other apps that use the same file name for waypoints.
*/
-var v = '1.49';
+var v = '1.50';
var vDroid = '1.50'; // Required DroidScript program version
/*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */
@@ -209,7 +210,7 @@ function nxtWp(){
}
function loadWp() {
- var w = require("Storage").readJSON('waypoints.json')||[{name:"NONE"}];
+ var w = require("Storage").readJSON('waypoints'+cfg.wptSfx+'.json')||[{name:"NONE"}];
if (cfg.wp>=w.length) cfg.wp=0;
if (cfg.wp<0) cfg.wp = w.length-1;
savSettings();
@@ -718,6 +719,7 @@ cfg.primSpd = cfg.primSpd||0; // 1 = Spd in primary, 0 = Spd in secondary
cfg.spdFilt = cfg.spdFilt==undefined?true:cfg.spdFilt;
cfg.altFilt = cfg.altFilt==undefined?true:cfg.altFilt;
cfg.touch = cfg.touch==undefined?true:cfg.touch;
+cfg.wptSfx = cfg.wptSfx==undefined?'':cfg.wptSfx;
if ( cfg.spdFilt ) var spdFilter = new KalmanFilter({R: 0.1 , Q: 1 });
if ( cfg.altFilt ) var altFilter = new KalmanFilter({R: 0.01, Q: 2 });
diff --git a/apps/speedalt2/metadata.json b/apps/speedalt2/metadata.json
index 4ace46854..2a111af28 100644
--- a/apps/speedalt2/metadata.json
+++ b/apps/speedalt2/metadata.json
@@ -2,7 +2,7 @@
"id": "speedalt2",
"name": "GPS Adventure Sports II",
"shortName":"GPS Adv Sport II",
- "version":"1.49",
+ "version":"1.50",
"description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
"icon": "app.png",
"type": "app",
@@ -15,5 +15,11 @@
{"name":"speedalt2.img","url":"app-icon.js","evaluate":true},
{"name":"speedalt2.settings.js","url":"settings.js"}
],
- "data": [{"name":"speedalt2.json"}]
+ "data": [
+ {"name":"speedalt2.json"},
+ {"name":"waypoints.json"},
+ {"name":"waypoints1.json"},
+ {"name":"waypoints2.json"},
+ {"name":"waypoints3.json"}
+ ]
}
diff --git a/apps/speedalt2/settings.js b/apps/speedalt2/settings.js
index babb03061..1bdb58f9d 100644
--- a/apps/speedalt2/settings.js
+++ b/apps/speedalt2/settings.js
@@ -30,6 +30,11 @@
writeSettings();
}
+ function setSfx(s) {
+ settings.wptSfx = s;
+ writeSettings();
+ }
+
const appMenu = {
'': {'title': 'GPS Adv Sprt II'},
@@ -38,6 +43,7 @@
'Units' : function() { E.showMenu(unitsMenu); },
'Colours' : function() { E.showMenu(colMenu); },
'Kalman Filter' : function() { E.showMenu(kalMenu); },
+ 'Wpt File Suffix' : function() { E.showMenu(sfxMenu); },
'Touch' : {
value : settings.touch,
format : v => v?"On":"Off",
@@ -69,6 +75,15 @@
'Inverted' : function() { setColour(3); }
};
+ const sfxMenu = {
+ '': {'title': 'Wpt File Suffix'},
+ '< Back': function() { E.showMenu(appMenu); },
+ 'Default' : function() { setSfx(''); },
+ '1' : function() { setSfx('1'); },
+ '2' : function() { setSfx('2'); },
+ '3' : function() { setSfx('3'); }
+ };
+
const kalMenu = {
'': {'title': 'Kalman Filter'},
'< Back': function() { E.showMenu(appMenu); },
diff --git a/apps/terminalclock/ChangeLog b/apps/terminalclock/ChangeLog
index b752c829d..ce31583e9 100644
--- a/apps/terminalclock/ChangeLog
+++ b/apps/terminalclock/ChangeLog
@@ -3,3 +3,4 @@
0.03: Add Banglejs 1 compatibility
0.04: Fix settings bug
0.05: Add altitude display (only Bangle.js 2)
+0.06: Add power related settings to control the HR and pressure(altitude) sensor from the watchface
diff --git a/apps/terminalclock/README.md b/apps/terminalclock/README.md
index c7452397d..93967e8a7 100644
--- a/apps/terminalclock/README.md
+++ b/apps/terminalclock/README.md
@@ -8,3 +8,8 @@ It can display :
- hrm
- motion
- steps
+
+
+"Power saving" setting control the HR and pressure (altitude) sensors.
+If "Off" they will always be on.
+If "On" the sensors will be turned on every "Power on interval" minutes for 45 secondes
diff --git a/apps/terminalclock/app.js b/apps/terminalclock/app.js
index 61861f745..7dc3bf1d1 100644
--- a/apps/terminalclock/app.js
+++ b/apps/terminalclock/app.js
@@ -3,15 +3,14 @@ var fontColor = g.theme.dark ? "#0f0" : "#000";
var heartRate = 0;
var altitude = -9001;
-// handling the differents versions of the Banglejs smartwatch
+// handling the differents versions of the Banglejs smartwatch screen sizes
if (process.env.HWVERSION == 1){
var paddingY = 3;
var font6x8At4Size = 48;
var font6x8At2Size = 27;
var font6x8FirstTextSize = 6;
var font6x8DefaultTextSize = 3;
-}
-else{
+} else{
var paddingY = 2;
var font6x8At4Size = 32;
var font6x8At2Size = 18;
@@ -66,7 +65,7 @@ function drawDate(now, pos){
drawLine(locale_date, pos);
}
-function drawInput(now, pos){
+function drawInput(pos){
clearField(pos);
drawLine(">", pos);
}
@@ -129,16 +128,52 @@ function draw(){
drawStepCount(curPos);
curPos++;
}
- drawInput(now, curPos);
+ drawInput(curPos);
}
+function turnOnServices(){
+ if(settings.showHRM){
+ Bangle.setHRMPower(true, "terminalclock");
+ }
+ if(settings.showAltitude && process.env.HWVERSION != 1){
+ Bangle.setBarometerPower(true, "terminalclock");
+ }
+ if(settings.powerSaving){
+ setTimeout(function () {
+ turnOffServices();
+ }, 45000);
+ }
+}
+
+function turnOffServices(){
+ if(settings.showHRM){
+ Bangle.setHRMPower(false, "terminalclock");
+ }
+ if(settings.showAltitude && process.env.HWVERSION != 1){
+ Bangle.setBarometerPower(false, "terminalclock");
+ }
+}
+
+var unlockDrawIntervalID = -1;
+Bangle.on('lock', function(on){
+ if(!on){ // unclock
+ if(settings.powerSaving){
+ turnOnServices();
+ }
+ unlockDrawIntervalID = setInterval(draw, 1000); // every second
+ }
+ if(on && unlockDrawIntervalID != -1){ // lock
+ clearInterval(unlockDrawIntervalID);
+ }
+});
+
Bangle.on('HRM',function(hrmInfo) {
if(hrmInfo.confidence >= settings.HRMinConfidence)
heartRate = hrmInfo.bpm;
});
-var MEDIANLENGTH = 20;
-var avr = [], median;
+var MEDIANLENGTH = 20; // technical
+var avr = [], median; // technical
Bangle.on('pressure', function(e) {
while (avr.length>MEDIANLENGTH) avr.pop();
avr.unshift(e.altitude);
@@ -161,18 +196,20 @@ var settings = Object.assign({
showActivity: true,
showStepCount: true,
showAltitude: process.env.HWVERSION != 1 ? true : false,
+ powerSaving: true,
+ PowerOnInterval: 15,
}, require('Storage').readJSON("terminalclock.json", true) || {});
-if(settings.showAltitude && process.env.HWVERSION != 1){
- Bangle.setBarometerPower(true, "app");
+// turn the services before drawing anything
+turnOnServices();
+if(settings.powerSaving){
+ setInterval(turnOnServices, settings.PowerOnInterval*60000); // every PowerOnInterval min
}
-
// Show launcher when middle button pressed
Bangle.setUI("clock");
-// Load widgets
+// Load and draw widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
// draw immediately at first
draw();
-
-var secondInterval = setInterval(draw, 10000);
+setInterval(draw, 10000); // every 10 seconds
diff --git a/apps/terminalclock/metadata.json b/apps/terminalclock/metadata.json
index 7bc00bca4..9f76ed8f2 100644
--- a/apps/terminalclock/metadata.json
+++ b/apps/terminalclock/metadata.json
@@ -3,7 +3,7 @@
"name": "Terminal Clock",
"shortName":"Terminal Clock",
"description": "A terminal cli like clock displaying multiple sensor data",
- "version":"0.05",
+ "version":"0.06",
"icon": "app.png",
"type": "clock",
"tags": "clock",
diff --git a/apps/terminalclock/settings.js b/apps/terminalclock/settings.js
index 4b09aad6a..bd860b491 100644
--- a/apps/terminalclock/settings.js
+++ b/apps/terminalclock/settings.js
@@ -8,6 +8,8 @@
showHRM: true,
showActivity: true,
showStepCount: true,
+ powerSaving: true,
+ PowerOnInterval: 15,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
@@ -65,10 +67,29 @@
settings.showStepCount = v;
writeSettings();
}
+ },
+ 'Power saving': {
+ value: settings.powerSaving,
+ format: v => v?"On":"Off",
+ onchange: v => {
+ settings.powerSaving = v;
+ writeSettings();
+ }
+ },
+ 'Power on interval': {
+ value: settings.PowerOnInterval,
+ min: 3, max: 60,
+ onchange: v => {
+ settings.PowerOnInterval = v;
+ writeSettings();
+ },
+ format: x => {
+ return x + " min";
+ }
}
}
if (process.env.HWVERSION == 1) {
delete menu['Show Altitude']
}
E.showMenu(menu);
-})
+})
\ No newline at end of file
diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js
index 850b793f4..81c0f75ac 100755
--- a/bin/sanitycheck.js
+++ b/bin/sanitycheck.js
@@ -65,7 +65,7 @@ const APP_KEYS = [
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports'];
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports'
-const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale"]; // values allowed for "type" field
+const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale","settings"]; // values allowed for "type" field
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"];
@@ -140,7 +140,7 @@ apps.forEach((app,appIdx) => {
ERROR(`App ${app.id} 'dependencies' must all be tagged 'type' or 'app' right now`);
if (app.dependencies[dependency]=="type" && !METADATA_TYPES.includes(dependency))
ERROR(`App ${app.id} 'type' dependency must be one of `+METADATA_TYPES);
-
+
});
} else
ERROR(`App ${app.id} 'dependencies' must be an object`);
diff --git a/core b/core
index 147892754..2054537a9 160000
--- a/core
+++ b/core
@@ -1 +1 @@
-Subproject commit 147892754eaf50c8581ebfb4d8651b9ec24aa44e
+Subproject commit 2054537a9958f9812ae2cad908b6597ff01e449d
diff --git a/lang/it_IT.json b/lang/it_IT.json
index 4bc36ee48..310af8580 100644
--- a/lang/it_IT.json
+++ b/lang/it_IT.json
@@ -192,7 +192,15 @@
"Notifications": "Notifiche",
"Scheduler": "Schedulatore",
"Stop": "Stop",
- "Min Font": "Dimensione minima del font"
+ "Min Font": "Dimensione minima del font",
+ "White": "Bianco",
+ "Red": "Rosso",
+ "Yellow": "Giallo",
+ "Cyan": "Ciano",
+ "Green": "Verde",
+ "Blue": "Blu",
+ "Black": "Nero",
+ "Show Week Number": "Mostra numero settimana"
},
"//2": "App-specific overrides",
"alarm": {
diff --git a/modules/ClockFace.js b/modules/ClockFace.js
index 25e2430bf..d6c3a2e66 100644
--- a/modules/ClockFace.js
+++ b/modules/ClockFace.js
@@ -66,6 +66,10 @@ ClockFace.prototype.tick = function() {
};
ClockFace.prototype.start = function() {
+ /* Some widgets want to know if we're in a clock or not (like chrono, widget clock, etc). Normally
+ .CLOCK is set by Bangle.setUI('clock') but we want to load widgets so we can check appRect and *then*
+ call setUI. see #1864 */
+ Bangle.CLOCK = 1;
Bangle.loadWidgets();
if (this.init) this.init.apply(this);
if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d]));
@@ -103,4 +107,4 @@ ClockFace.prototype.redraw = function() {
this.tick();
};
-exports = ClockFace;
\ No newline at end of file
+exports = ClockFace;
diff --git a/modules/Layout.js b/modules/Layout.js
index 019d63815..19cfabe11 100644
--- a/modules/Layout.js
+++ b/modules/Layout.js
@@ -1,83 +1,16 @@
/* Copyright (c) 2022 Bangle.js contributors. See the file LICENSE for copying permission. */
-/*
-Take a look at README.md for hints on developing with this library.
-Usage:
-```
-var Layout = require("Layout");
-var layout = new Layout( layoutObject, options )
-layout.render(optionalObject);
-```
-For example:
-```
-var Layout = require("Layout");
-var layout = new Layout( {
- type:"v", c: [
- {type:"txt", font:"20%", label:"12:00" },
- {type:"txt", font:"6x8", label:"The Date" }
- ]
-});
-g.clear();
-layout.render();
-```
-layoutObject has:
-* A `type` field of:
- * `undefined` - blank, can be used for padding
- * `"txt"` - a text label, with value `label`. 'font' is required
- * `"btn"` - a button, with value `label` and callback `cb`
- optional `src` specifies an image (like img) in which case label is ignored
- Default font is `6x8`, scale 2. This can be overridden with the `font` or `scale` fields.
- * `"img"` - an image where `src` is an image, or a function which is called to return an image to draw.
- * `"custom"` - a custom block where `render(layoutObj)` is called to render
- * `"h"` - Horizontal layout, `c` is an array of more `layoutObject`
- * `"v"` - Vertical layout, `c` is an array of more `layoutObject`
-* A `id` field. If specified the object is added with this name to the
- returned `layout` object, so can be referenced as `layout.foo`
-* A `font` field, eg `6x8` or `30%` to use a percentage of screen height. Set scale with :, e.g. `6x8:2`.
-* A `scale` field, eg `2` to set scale of an image
-* A `r` field to set rotation of text or images (0: 0°, 1: 90°, 2: 180°, 3: 270°).
-* A `wrap` field to enable line wrapping. Requires some combination of `width`/`height`
- and `fillx`/`filly` to be set. Not compatible with text rotation.
-* A `col` field, eg `#f00` for red
-* A `bgCol` field for background color (will automatically fill on render)
-* A `halign` field to set horizontal alignment WITHIN a `v` container. `-1`=left, `1`=right, `0`=center
-* A `valign` field to set vertical alignment WITHIN a `h` container. `-1`=top, `1`=bottom, `0`=center
-* A `pad` integer field to set pixels padding
-* A `fillx` int to choose if the object should fill available space in x. 0=no, 1=yes, 2=2x more space
-* A `filly` int to choose if the object should fill available space in y. 0=no, 1=yes, 2=2x more space
-* `width` and `height` fields to optionally specify minimum size
-options is an object containing:
-* `lazy` - a boolean specifying whether to enable automatic lazy rendering
-* `btns` - array of objects containing:
- * `label` - the text on the button
- * `cb` - a callback function
- * `cbl` - a callback function for long presses
-* `back` - a callback function, passed as `back` into Bangle.setUI (which usually adds an icon in the top left)
-
-If automatic lazy rendering is enabled, calls to `layout.render()` will attempt to automatically
-determine what objects have changed or moved, clear their previous locations, and re-render just those objects.
-Once `layout.update()` is called, the following fields are added
-to each object:
-* `x` and `y` for the top left position
-* `w` and `h` for the width and height
-* `_w` and `_h` for the **minimum** width and height
-Other functions:
-* `layout.update()` - update positions of everything if contents have changed
-* `layout.debug(obj)` - draw outlines for objects on screen
-* `layout.clear(obj)` - clear the given object (you can also just specify `bgCol` to clear before each render)
-* `layout.forgetLazyState()` - if lazy rendering is enabled, makes the next call to `render()` perform a full re-render
-*/
+// See Layout.md for documentation
function Layout(layout, options) {
this._l = this.l = layout;
// Do we have >1 physical buttons?
this.physBtns = (process.env.HWVERSION==2) ? 1 : 3;
- options = options || {};
- this.lazy = options.lazy || false;
+ this.options = options || {};
+ this.lazy = this.options.lazy || false;
- var btnList, uiSet;
- Bangle.setUI(); // remove all existing input handlers
+ var btnList;
if (process.env.HWVERSION!=2) {
// no touchscreen, find any buttons in 'layout'
btnList = [];
@@ -91,48 +24,19 @@ function Layout(layout, options) {
this.physBtns = 0;
this.buttons = btnList;
this.selectedButton = -1;
- Bangle.setUI({mode:"updown", back:options.back}, dir=>{
- var s = this.selectedButton, l=this.buttons.length;
- if (dir===undefined && this.buttons[s])
- return this.buttons[s].cb();
- if (this.buttons[s]) {
- delete this.buttons[s].selected;
- this.render(this.buttons[s]);
- }
- s = (s+l+dir) % l;
- if (this.buttons[s]) {
- this.buttons[s].selected = 1;
- this.render(this.buttons[s]);
- }
- this.selectedButton = s;
- });
- uiSet = true;
}
}
- if (options.back && !uiSet) Bangle.setUI({mode: "custom", back: options.back});
- if (options.btns) {
- var buttons = options.btns;
+ if (this.options.btns) {
+ var buttons = this.options.btns;
this.b = buttons;
if (this.physBtns >= buttons.length) {
- // Handler for button watch events
- function pressHandler(btn,e) {
- if (e.time-e.lastTime > 0.75 && this.b[btn].cbl)
- this.b[btn].cbl(e);
- else
- if (this.b[btn].cb) this.b[btn].cb(e);
- }
// enough physical buttons
let btnHeight = Math.floor(Bangle.appRect.h / this.physBtns);
- if (Bangle.btnWatches) Bangle.btnWatches.forEach(clearWatch);
- Bangle.btnWatches = [];
if (this.physBtns > 2 && buttons.length==1)
buttons.unshift({label:""}); // pad so if we have a button in the middle
while (this.physBtns > buttons.length)
buttons.push({label:""});
- if (buttons[0]) Bangle.btnWatches.push(setWatch(pressHandler.bind(this,0), BTN1, {repeat:true,edge:-1}));
- if (buttons[1]) Bangle.btnWatches.push(setWatch(pressHandler.bind(this,1), BTN2, {repeat:true,edge:-1}));
- if (buttons[2]) Bangle.btnWatches.push(setWatch(pressHandler.bind(this,2), BTN3, {repeat:true,edge:-1}));
this._l.width = g.getWidth()-8; // text width
this._l = {type:"h", filly:1, c: [
this._l,
@@ -149,19 +53,8 @@ function Layout(layout, options) {
if (btnList) btnList.push.apply(btnList, this._l.c[1].c);
}
}
- if (process.env.HWVERSION==2) {
-
- // Handler for touch events
- function touchHandler(l,e) {
- if (l.cb && e.x>=l.x && e.y>=l.y && e.x<=l.x+l.w && e.y<=l.y+l.h) {
- if (e.type==2 && l.cbl) l.cbl(e); else if (l.cb) l.cb(e);
- }
- if (l.c) l.c.forEach(n => touchHandler(n,e));
- }
- Bangle.touchHandler = (_,e)=>touchHandler(this._l,e);
- Bangle.on('touch',Bangle.touchHandler);
- }
-
+ // Link in all buttons/touchscreen/etc
+ this.setUI();
// recurse over layout doing some fixing up if needed
var ll = this;
function recurser(l) {
@@ -175,16 +68,57 @@ function Layout(layout, options) {
this.updateNeeded = true;
}
-Layout.prototype.remove = function (l) {
- if (Bangle.btnWatches) {
- Bangle.btnWatches.forEach(clearWatch);
- delete Bangle.btnWatches;
+Layout.prototype.setUI = function() {
+ Bangle.setUI(); // remove all existing input handlers
+
+ var uiSet;
+ if (this.buttons) {
+ // multiple buttons so we'll jus use back/next/select
+ Bangle.setUI({mode:"updown", back:this.options.back}, dir=>{
+ var s = this.selectedButton, l=this.buttons.length;
+ if (dir===undefined && this.buttons[s])
+ return this.buttons[s].cb();
+ if (this.buttons[s]) {
+ delete this.buttons[s].selected;
+ this.render(this.buttons[s]);
+ }
+ s = (s+l+dir) % l;
+ if (this.buttons[s]) {
+ this.buttons[s].selected = 1;
+ this.render(this.buttons[s]);
+ }
+ this.selectedButton = s;
+ });
+ uiSet = true;
}
- if (Bangle.touchHandler) {
- Bangle.removeListener("touch",Bangle.touchHandler);
- delete Bangle.touchHandler;
+ if (this.options.back && !uiSet) Bangle.setUI({mode: "custom", back: this.options.back});
+ // physical buttons -> actual applications
+ if (this.b) {
+ // Handler for button watch events
+ function pressHandler(btn,e) {
+ if (e.time-e.lastTime > 0.75 && this.b[btn].cbl)
+ this.b[btn].cbl(e);
+ else
+ if (this.b[btn].cb) this.b[btn].cb(e);
+ }
+ if (Bangle.btnWatches) Bangle.btnWatches.forEach(clearWatch);
+ Bangle.btnWatches = [];
+ if (this.b[0]) Bangle.btnWatches.push(setWatch(pressHandler.bind(this,0), BTN1, {repeat:true,edge:-1}));
+ if (this.b[1]) Bangle.btnWatches.push(setWatch(pressHandler.bind(this,1), BTN2, {repeat:true,edge:-1}));
+ if (this.b[2]) Bangle.btnWatches.push(setWatch(pressHandler.bind(this,2), BTN3, {repeat:true,edge:-1}));
}
-};
+ // Handle touch events on new Bangle.js
+ if (process.env.HWVERSION==2) {
+ function touchHandler(l,e) {
+ if (l.cb && e.x>=l.x && e.y>=l.y && e.x<=l.x+l.w && e.y<=l.y+l.h) {
+ if (e.type==2 && l.cbl) l.cbl(e); else if (l.cb) l.cb(e);
+ }
+ if (l.c) l.c.forEach(n => touchHandler(n,e));
+ }
+ Bangle.touchHandler = (_,e)=>touchHandler(this._l,e);
+ Bangle.on('touch',Bangle.touchHandler);
+ }
+}
function prepareLazyRender(l, rectsToClear, drawList, rects, parentBg) {
var bgCol = l.bgCol == null ? parentBg : g.toColor(l.bgCol);
diff --git a/modules/Layout.md b/modules/Layout.md
new file mode 100644
index 000000000..7a4177957
--- /dev/null
+++ b/modules/Layout.md
@@ -0,0 +1,81 @@
+Bangle.js Layout Library
+========================
+
+> Take a look at README.md for hints on developing with this library.
+
+Usage
+-----
+
+```JS
+var Layout = require("Layout");
+var layout = new Layout(layoutObject, options)
+
+layout.render(optionalObject);
+```
+
+For example:
+
+```JS
+var Layout = require("Layout");
+var layout = new Layout({
+ type:"v",
+ c: [
+ { type: "txt", font: "20%", label: "12:00" },
+ { type: "txt", font: "6x8", label: "The Date" }
+ ]
+});
+
+g.clear();
+
+layout.render();
+```
+
+`layoutObject` has:
+
+- A `type` field of:
+ - `undefined` - blank, can be used for padding
+ - `"txt"` - a text label, with value `label`. `font` is required
+ - `"btn"` - a button, with value `label` and callback `cb`. Optional `src` specifies an image (like img) in which case label is ignored. Default font is `6x8`, scale 2. This can be overridden with the `font` or `scale` fields.
+ - `"img"` - an image where `src` is an image, or a function which is called to return an image to draw
+ - `"custom"` - a custom block where `render(layoutObj)` is called to render
+ - `"h"` - Horizontal layout, `c` is an array of more `layoutObject`
+ - `"v"` - Vertical layout, `c` is an array of more `layoutObject`
+- A `id` field. If specified the object is added with this name to the returned `layout` object, so can be referenced as `layout.foo`
+- A `font` field, eg `6x8` or `30%` to use a percentage of screen height. Set scale with :, e.g. `6x8:2`.
+- A `scale` field, eg `2` to set scale of an image
+- A `r` field to set rotation of text or images (0: 0°, 1: 90°, 2: 180°, 3: 270°).
+- A `wrap` field to enable line wrapping. Requires some combination of `width`/`height` and `fillx`/`filly` to be set. Not compatible with text rotation.
+- A `col` field, eg `#f00` for red
+- A `bgCol` field for background color (will automatically fill on render)
+- A `halign` field to set horizontal alignment WITHIN a `v` container. `-1`=left, `1`=right, `0`=center
+- A `valign` field to set vertical alignment WITHIN a `h` container. `-1`=top, `1`=bottom, `0`=center
+- A `pad` integer field to set pixels padding
+- A `fillx` int to choose if the object should fill available space in x. 0=no, 1=yes, 2=2x more space
+- A `filly` int to choose if the object should fill available space in y. 0=no, 1=yes, 2=2x more space
+- `width` and `height` fields to optionally specify minimum size options is an object containing:
+- `lazy` - a boolean specifying whether to enable automatic lazy rendering
+- `btns` - array of objects containing:
+ - `label` - the text on the button
+ - `cb` - a callback function
+ - `cbl` - a callback function for long presses
+- `back` - a callback function, passed as `back` into Bangle.setUI (which usually adds an icon in the top left)
+
+If automatic lazy rendering is enabled, calls to `layout.render()` will attempt to automatically determine what objects have changed or moved, clear their previous locations, and re-render just those objects.
+
+Once `layout.update()` is called, the following fields are added to each object:
+
+- `x` and `y` for the top left position
+- `w` and `h` for the width and height
+- `_w` and `_h` for the **minimum** width and height
+
+Other functions:
+
+- `layout.update()` - update positions of everything if contents have changed
+- `layout.debug(obj)` - draw outlines for objects on screen
+- `layout.clear(obj)` - clear the given object (you can also just specify `bgCol` to clear before each render)
+- `layout.forgetLazyState()` - if lazy rendering is enabled, makes the next call to `render()` perform a full re-render
+
+Links
+-----
+
+- [Official tutorial](https://www.espruino.com/Bangle.js+Layout)
diff --git a/typescript/types/globals.d.ts b/typescript/types/globals.d.ts
index 2ef52dcdf..e82c3da3d 100644
--- a/typescript/types/globals.d.ts
+++ b/typescript/types/globals.d.ts
@@ -140,7 +140,7 @@ declare const require: ((module: 'heatshrink') => {
declare const Bangle: {
// functions
- buzz: () => void;
+ buzz: (duration?: number, intensity?: number) => Promise;
drawWidgets: () => void;
isCharging: () => boolean;
// events
@@ -158,9 +158,9 @@ declare type Image = {
};
declare type GraphicsApi = {
- reset: () => void;
+ reset: () => GraphicsApi;
flip: () => void;
- setColor: (color: string) => void; // TODO we can most likely type color more usefully than this
+ setColor: (color: string) => GraphicsApi; // TODO we can most likely type color more usefully than this
drawImage: (
image: string | Image | ArrayBuffer,
xOffset: number,
@@ -169,7 +169,7 @@ declare type GraphicsApi = {
rotate?: number;
scale?: number;
}
- ) => void;
+ ) => GraphicsApi;
// TODO add more
};