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/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index 3d75e1209..b00055334 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -28,3 +28,4 @@ 0.26: Add support for Monday as first day of the week (#1780) 0.27: New UI! 0.28: Fix bug with alarms not firing when configured to fire only once +0.29: Fix wrong 'dow' handling in new timer if first day of week is Monday diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 8e2e2b0a6..fe0f67dbb 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -37,8 +37,8 @@ function handleFirstDayOfWeek(dow) { return dow; } -// Check the first day of week and update the dow field accordingly. -alarms.forEach(alarm => alarm.dow = handleFirstDayOfWeek(alarm.dow)); +// Check the first day of week and update the dow field accordingly (alarms only!) +alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow)); function showMainMenu() { const menu = { @@ -158,14 +158,14 @@ function saveAlarm(alarm, alarmIndex, time) { } function saveAndReload() { - // Before saving revert the dow to the standard format - alarms.forEach(a => a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek)); + // Before saving revert the dow to the standard format (alarms only!) + alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow)); require("sched").setAlarms(alarms); require("sched").reload(); // Fix after save - alarms.forEach(a => a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek)); + alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow)); } function decodeDOW(alarm) { diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index b38ab1ef1..cac837b5e 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -2,7 +2,7 @@ "id": "alarm", "name": "Alarms & Timers", "shortName": "Alarms", - "version": "0.28", + "version": "0.29", "description": "Set alarms and timers on your Bangle", "icon": "app.png", "tags": "tool,alarm,widget", diff --git a/apps/locale/locales.js b/apps/locale/locales.js index d1ac7687f..de56503fd 100644 --- a/apps/locale/locales.js +++ b/apps/locale/locales.js @@ -11,6 +11,8 @@ const speedUnits = { // how many kph per X? "kmh": 1, "kph": 1, "km/h": 1, + "kmt": 1, + "km/tim": 1, "mph": 1.60934, "kts": 1.852 }; diff --git a/apps/messages/app.js b/apps/messages/app.js index cb5a1f1ed..745f7d208 100644 --- a/apps/messages/app.js +++ b/apps/messages/app.js @@ -67,13 +67,6 @@ function saveMessages() { require("Storage").writeJSON("messages.json",MESSAGES) } -function getPosImage() { - return atob("GRSBAAAAAYAAAcAAAeAAAfAAAfAAAfAAAfAAAfAAAfBgAfA4AfAeAfAPgfAD4fAA+fAAP/AAD/AAA/AAAPAAADAAAA=="); -} -function getNegImage() { - return atob("FhaBADAAMeAB78AP/4B/fwP4/h/B/P4D//AH/4AP/AAf4AB/gAP/AB/+AP/8B/P4P4fx/A/v4B//AD94AHjAAMA="); -} - function showMapMessage(msg) { active = "map"; var m; @@ -300,7 +293,7 @@ function showMessage(msgid) { var buttons = [ ]; if (msg.positive) { - buttons.push({type:"btn", src:getPosImage(), cb:()=>{ + buttons.push({type:"btn", src:atob("GRSBAAAAAYAAAcAAAeAAAfAAAfAAAfAAAfAAAfAAAfBgAfA4AfAeAfAPgfAD4fAA+fAAP/AAD/AAA/AAAPAAADAAAA=="), cb:()=>{ msg.new = false; saveMessages(); cancelReloadTimeout(); // don't auto-reload to clock now Bangle.messageResponse(msg,true); @@ -309,7 +302,7 @@ function showMessage(msgid) { } if (msg.negative) { if (buttons.length) buttons.push({width:32}); // nasty hack... - buttons.push({type:"btn", src:getNegImage(), cb:()=>{ + buttons.push({type:"btn", src:atob("FhaBADAAMeAB78AP/4B/fwP4/h/B/P4D//AH/4AP/AAf4AB/gAP/AB/+AP/8B/P4P4fx/A/v4B//AD94AHjAAMA="), cb:()=>{ msg.new = false; saveMessages(); cancelReloadTimeout(); // don't auto-reload to clock now Bangle.messageResponse(msg,false); diff --git a/apps/mylocation/ChangeLog b/apps/mylocation/ChangeLog index 1239554f0..c14e64ba9 100644 --- a/apps/mylocation/ChangeLog +++ b/apps/mylocation/ChangeLog @@ -4,3 +4,4 @@ 0.04: Fixed issue selecting Frankfurt not saved 0.05: Fixed issue with back option 0.06: renamed source files to match standard +0.07: Move mylocation app into 'Settings -> Apps' diff --git a/apps/mylocation/README.md b/apps/mylocation/README.md index fd597397a..a6a16ce83 100644 --- a/apps/mylocation/README.md +++ b/apps/mylocation/README.md @@ -2,6 +2,8 @@ *Sets and stores GPS lat and lon of your preferred city* +To access, go to `Settings -> Apps -> My Location` + * Select one of the preset Cities or setup through the GPS * Other Apps can read this information to do calculations based on location * When the City shows ??? it means the location has been set through the GPS diff --git a/apps/mylocation/icon.js b/apps/mylocation/icon.js deleted file mode 100644 index b79f5875f..000000000 --- a/apps/mylocation/icon.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("mEw4UA///gH4AYPO/QPDgNVqtADY/1BYNfBQ0PBQIAB+ALFmoLDrgLF6oLDq4KEgYKDBYPABYcNBYlVuAuIGAwuEAANUBYYKFHgg6Bq4ZCr4DBHgQLBvWq2te1WlBYZGBBYOr1Wq1qSDBYNqBIILDKgQLLgoLHqBqDBfJHLBZBrOgKPCBYiPCU4NaBYe1WYrABBQLCCfgYGCrwVBa4kAirvKNgIAErgLDKgIAEKQQ8EAAY6DBZhIDIww8GHQg8GHQgwGFwowEFwx5EOog8GHQ0AlWpBYNq1AKFWIILBAYOgBYbICytWAgQKCgTgDcwYXGAAgvGAAY8EEgYWGBgoVEA==")) diff --git a/apps/mylocation/metadata.json b/apps/mylocation/metadata.json index 16549b2ba..4ab9aa37e 100644 --- a/apps/mylocation/metadata.json +++ b/apps/mylocation/metadata.json @@ -2,16 +2,15 @@ "name": "My Location", "shortName":"My Location", "icon": "app.png", - "type": "app", + "type": "settings", "screenshots": [{"url":"screenshot_1.png"}], - "version":"0.06", + "version":"0.07", "description": "Sets and stores the lat and long of your preferred City or it can be set from the GPS. mylocation.json can be used by other apps that need your main location lat and lon. See README", "readme": "README.md", "tags": "tool,utility", "supports": ["BANGLEJS", "BANGLEJS2"], "storage": [ - {"name":"mylocation.app.js","url":"app.js"}, - {"name":"mylocation.img","url":"icon.js","evaluate": true } + {"name":"mylocation.settings.js","url":"settings.js"} ], "data": [ {"name":"mylocation.json"} diff --git a/apps/mylocation/app.js b/apps/mylocation/settings.js similarity index 73% rename from apps/mylocation/app.js rename to apps/mylocation/settings.js index fd5c9cc6d..7033500fa 100644 --- a/apps/mylocation/app.js +++ b/apps/mylocation/settings.js @@ -1,5 +1,4 @@ -Bangle.loadWidgets(); -Bangle.drawWidgets(); +(function(back) { const SETTINGS_FILE = "mylocation.json"; let settings; @@ -18,7 +17,7 @@ function loadSettings() { } } -function save() { +function saveSettings() { settings = s; require('Storage').write(SETTINGS_FILE, settings); } @@ -34,29 +33,29 @@ function setFromGPS() { //console.log("fix from GPS"); s = {'lat': gps.lat, 'lon': gps.lon, 'location': '???' }; Bangle.buzz(1500); // buzz on first position - Bangle.setGPSPower(0); - save(); + Bangle.setGPSPower(0, "mylocation"); + saveSettings(); Bangle.setUI("updown", ()=>{ load(); }); - E.showPrompt("Location has been saved from the GPS fix",{ - title:"Location Saved", - buttons : {"OK":1} + E.showPrompt(/*LANG*/"Location has been saved from the GPS fix",{ + title:/*LANG*/"Location Saved", + buttons : {/*LANG*/"OK":1} }).then(function(v) { load(); // load default clock }); }); - Bangle.setGPSPower(1); - E.showMessage("Waiting for GPS fix. Place watch in the open. Could take 10 minutes. Long press to abort", "GPS Running"); + Bangle.setGPSPower(1, "mylocation"); + E.showMessage(/*LANG*/"Waiting for GPS fix. Place watch in the open. Could take 10 minutes. Long press to abort", "GPS Running"); Bangle.setUI("updown", undefined); } function showMainMenu() { //console.log("showMainMenu"); const mainmenu = { - '': { 'title': 'My Location' }, - '< Back': ()=>{ load(); }, - 'City': { + '': { 'title': /*LANG*/'My Location' }, + '< Back': ()=>{ back(); }, + /*LANG*/'City': { value: 0 | locations.indexOf(s.location), min: 0, max: locations.length - 1, format: v => locations[v], @@ -65,14 +64,15 @@ function showMainMenu() { s.location = locations[v]; s.lat = lats[v]; s.lon = lons[v]; - save(); + saveSettings(); } } }, - 'Set From GPS': ()=>{ setFromGPS(); } + /*LANG*/'Set From GPS': ()=>{ setFromGPS(); } }; return E.showMenu(mainmenu); } loadSettings(); showMainMenu(); +}) diff --git a/apps/r2d2clk/metadata.json b/apps/r2d2clk/metadata.json index f2cde76f4..249180ac8 100644 --- a/apps/r2d2clk/metadata.json +++ b/apps/r2d2clk/metadata.json @@ -4,6 +4,7 @@ "version":"0.01", "description": "A clock with R2D2's shiny metal face on it. :)", "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], "type": "clock", "tags": "clock", "supports" : ["BANGLEJS2"], diff --git a/apps/rndmclk/metadata.json b/apps/rndmclk/metadata.json index e837c4bce..bb8e92f95 100644 --- a/apps/rndmclk/metadata.json +++ b/apps/rndmclk/metadata.json @@ -6,7 +6,7 @@ "icon": "rndmclk.png", "type": "widget", "tags": "widget,clock", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "storage": [ {"name":"rndmclk.wid.js","url":"widget.js"} diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 6d3bbb468..eca2b7938 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -49,3 +49,4 @@ 0.44: Add "Start Week On X" option (#1780) UI improvements to Locale and Date & Time menu 0.45: Add calibrate battery option +0.46: Fix regression after making 'calibrate battery' only for Bangle.js 2 diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index c5c368fae..183290a85 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.45", + "version": "0.46", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 5c14e914d..150251e7d 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -545,8 +545,10 @@ function showUtilMenu() { setInterval(function() { var i=1000;while (i--); }, 1); - }, - /*LANG*/'Calibrate Battery': () => { + } + }; + if (BANGLEJS2) + menu[/*LANG*/'Calibrate Battery'] = () => { E.showPrompt(/*LANG*/"Is the battery fully charged?",{title:/*LANG*/"Calibrate"}).then(ok => { if (ok) { var s=require("Storage").readJSON("setting.json"); @@ -557,8 +559,8 @@ function showUtilMenu() { E.showAlert(/*LANG*/"Please charge Bangle.js for 3 hours and try again").then(() => load("settings.app.js")); } }); - }, - /*LANG*/'Reset Settings': () => { + }; + menu[/*LANG*/'Reset Settings'] = () => { E.showPrompt(/*LANG*/'Reset to Defaults?',{title:/*LANG*/"Settings"}).then((v) => { if (v) { E.showMessage('Resetting'); @@ -566,9 +568,9 @@ function showUtilMenu() { setTimeout(showMainMenu, 50); } else showUtilMenu(); }); - }, - /*LANG*/'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() } - }; + }; + menu[/*LANG*/'Turn Off'] = ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() }; + if (Bangle.factoryReset) { menu[/*LANG*/'Factory Reset'] = ()=>{ E.showPrompt(/*LANG*/'This will remove everything!',{title:/*LANG*/"Factory Reset"}).then((v) => { 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 404e98183..2054537a9 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 404e981834f2e8df9c505a8fab12ae12fe3bd562 +Subproject commit 2054537a9958f9812ae2cad908b6597ff01e449d diff --git a/modules/Layout.js b/modules/Layout.js index 43419a397..19cfabe11 100644 --- a/modules/Layout.js +++ b/modules/Layout.js @@ -1,74 +1,6 @@ /* 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 -* `layout.setUI()` - (called when module initialised) This sets up input (buttons, touch, etc) with Bangle._setUI - This can be useful if you called E.showMenu/showPrompt/etc and those grabbed input away from layour -*/ +// See Layout.md for documentation function Layout(layout, options) { this._l = this.l = layout; 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)