Merge branch 'master' into layout-img-object
|
|
@ -6,4 +6,5 @@ package-lock.json
|
|||
appdates.csv
|
||||
.vscode
|
||||
.idea/
|
||||
_config.yml
|
||||
_config.yml
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ easily distinguish between file types, we use the following:
|
|||
|
||||
## Adding your app to the menu
|
||||
|
||||
* Come up with a unique (all lowercase, nu spaces) name, we'll assume `7chname`. Bangle.js
|
||||
* Come up with a unique (all lowercase, no spaces) name, we'll assume `7chname`. Bangle.js
|
||||
is limited to 28 char filenames and appends a file extension (eg `.js`) so please
|
||||
try and keep filenames short to avoid overflowing the buffer.
|
||||
* Create a folder called `apps/<id>`, lets assume `apps/7chname`
|
||||
|
|
|
|||
142
apps.json
|
|
@ -118,7 +118,7 @@
|
|||
{ "id": "welcome",
|
||||
"name": "Welcome",
|
||||
"icon": "app.png",
|
||||
"version":"0.11",
|
||||
"version":"0.12",
|
||||
"description": "Appears at first boot and explains how to use Bangle.js",
|
||||
"tags": "start,welcome",
|
||||
"allow_emulator":true,
|
||||
|
|
@ -136,7 +136,7 @@
|
|||
"name": "Customised Welcome",
|
||||
"shortName": "My Welcome",
|
||||
"icon": "app.png",
|
||||
"version":"0.11",
|
||||
"version":"0.12",
|
||||
"description": "Appears at first boot and explains how to use Bangle.js. Like 'Welcome', but can be customised with a greeting",
|
||||
"tags": "start,welcome",
|
||||
"custom":"custom.html",
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
{ "id": "gbridge",
|
||||
"name": "Gadgetbridge",
|
||||
"icon": "app.png",
|
||||
"version":"0.23",
|
||||
"version":"0.24",
|
||||
"description": "The default notification handler for Gadgetbridge notifications from Android",
|
||||
"tags": "tool,system,android,widget",
|
||||
"readme": "README.md",
|
||||
|
|
@ -532,7 +532,7 @@
|
|||
{ "id": "heart",
|
||||
"name": "Heart Rate Recorder",
|
||||
"icon": "app.png",
|
||||
"version":"0.05",
|
||||
"version":"0.06",
|
||||
"interface": "interface.html",
|
||||
"description": "Application that allows you to record your heart rate. Can run in background",
|
||||
"tags": "tool,health,widget",
|
||||
|
|
@ -571,7 +571,7 @@
|
|||
{ "id": "weather",
|
||||
"name": "Weather",
|
||||
"icon": "icon.png",
|
||||
"version":"0.05",
|
||||
"version":"0.07",
|
||||
"description": "Show Gadgetbridge weather report",
|
||||
"readme": "readme.md",
|
||||
"tags": "widget,outdoors",
|
||||
|
|
@ -639,10 +639,11 @@
|
|||
"name": "Battery Level Widget (with percentage)",
|
||||
"shortName": "Battery Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.11",
|
||||
"version":"0.12",
|
||||
"description": "Show the current battery level and charging status in the top right of the clock, with charge percentage",
|
||||
"tags": "widget,battery",
|
||||
"tags": "widget,battery,b2",
|
||||
"type":"widget",
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"widbatpc.wid.js","url":"widget.js"},
|
||||
{"name":"widbatpc.settings.js","url":"settings.js"}
|
||||
|
|
@ -721,7 +722,7 @@
|
|||
{ "id": "widhrm",
|
||||
"name": "Simple Heart Rate widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "When the screen is on, the widget turns on the heart rate monitor and displays the current heart rate (or last known in grey). For this to work well you'll need at least a 15 second LCD Timeout.",
|
||||
"tags": "health,widget",
|
||||
"type": "widget",
|
||||
|
|
@ -1287,7 +1288,7 @@
|
|||
{ "id": "wohrm",
|
||||
"name": "Workout HRM",
|
||||
"icon": "app.png",
|
||||
"version":"0.07",
|
||||
"version":"0.08",
|
||||
"readme": "README.md",
|
||||
"description": "Workout heart rate monitor notifies you with a buzz if your heart rate goes above or below the set limits.",
|
||||
"tags": "hrm,workout",
|
||||
|
|
@ -1466,7 +1467,7 @@
|
|||
"id": "balltastic",
|
||||
"name": "Balltastic",
|
||||
"icon": "app.png",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "Simple but fun ball eats dots game.",
|
||||
"tags": "game,fun",
|
||||
"type": "app",
|
||||
|
|
@ -1517,7 +1518,7 @@
|
|||
"name": "OpenStreetMap",
|
||||
"shortName":"OpenStMap",
|
||||
"icon": "app.png",
|
||||
"version":"0.08",
|
||||
"version":"0.09",
|
||||
"description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are",
|
||||
"tags": "outdoors,gps,b2",
|
||||
"custom": "custom.html", "customConnect":true,
|
||||
|
|
@ -1895,7 +1896,7 @@
|
|||
{ "id": "ballmaze",
|
||||
"name": "Ball Maze",
|
||||
"icon": "icon.png",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "Navigate a ball through a maze by tilting your watch.",
|
||||
"readme": "README.md",
|
||||
"tags": "game",
|
||||
|
|
@ -1944,26 +1945,16 @@
|
|||
"id": "largeclock",
|
||||
"name": "Large Clock",
|
||||
"icon": "largeclock.png",
|
||||
"version": "0.09",
|
||||
"version": "0.10",
|
||||
"description": "A readable and informational digital watch, with date, seconds and moon phase",
|
||||
"readme": "README.md",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{
|
||||
"name": "largeclock.app.js",
|
||||
"url": "largeclock.js"
|
||||
},
|
||||
{
|
||||
"name": "largeclock.img",
|
||||
"url": "largeclock-icon.js",
|
||||
"evaluate": true
|
||||
},
|
||||
{
|
||||
"name": "largeclock.settings.js",
|
||||
"url": "settings.js"
|
||||
}
|
||||
{"name": "largeclock.app.js", "url": "largeclock.js"},
|
||||
{"name": "largeclock.img", "url": "largeclock-icon.js", "evaluate": true},
|
||||
{"name": "largeclock.settings.js", "url": "settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"largeclock.json"}
|
||||
|
|
@ -3265,14 +3256,15 @@
|
|||
"name": "Hour Strike",
|
||||
"shortName": "Hour Strike",
|
||||
"icon": "app-icon.png",
|
||||
"version": "0.07",
|
||||
"version": "0.08",
|
||||
"description": "Strike the clock on the hour. A great tool to remind you an hour has passed!",
|
||||
"tags": "tool,alarm",
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"hourstrike.app.js","url":"app.js"},
|
||||
{"name":"hourstrike.boot.js","url":"boot.js"},
|
||||
{"name":"hourstrike.img","url":"app-icon.js","evaluate":true}
|
||||
{"name":"hourstrike.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"hourstrike.json","url":"hourstrike.json"}
|
||||
]
|
||||
},
|
||||
{ "id": "whereworld",
|
||||
|
|
@ -3430,13 +3422,103 @@
|
|||
"name": "Car Crazy",
|
||||
"shortName":"Car Crazy",
|
||||
"icon": "carcrash.png",
|
||||
"version":"0.01",
|
||||
"version":"0.03",
|
||||
"description": "A simple car game where you try to avoid the other cars by tilting your wrist left and right. Hold down button 2 to start.",
|
||||
"tags": "game",
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"carcrazy.app.js","url":"app.js"},
|
||||
{"name":"carcrazy.img","url":"app-icon.js","evaluate":true}
|
||||
{"name":"carcrazy.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"carcrazy.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"app.json"}
|
||||
]
|
||||
},
|
||||
{ "id": "shortcuts",
|
||||
"name": "Shortcuts",
|
||||
"shortName":"Shortcuts",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "Quickly load your favourite apps from (almost) any watch face.",
|
||||
"tags": "tool",
|
||||
"type": "bootloader",
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"shortcuts.boot.js","url":"boot.js"},
|
||||
{"name":"shortcuts.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"shortcuts.json"}
|
||||
]
|
||||
},
|
||||
{ "id": "vectorclock",
|
||||
"name": "Vector Clock",
|
||||
"icon": "app.png",
|
||||
"version": "0.02",
|
||||
"description": "A digital clock that uses the built-in vector font.",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"vectorclock.app.js","url":"app.js"},
|
||||
{"name":"vectorclock.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "fd6fdetect",
|
||||
"name": "fd6fdetect",
|
||||
"shortName":"fd6fdetect",
|
||||
"icon": "app.png",
|
||||
"version":"0.1",
|
||||
"description": "Allows you to see 0xFD6F beacons near you.",
|
||||
"tags": "tool",
|
||||
"storage": [
|
||||
{"name":"fd6fdetect.app.js","url":"app.js"},
|
||||
{"name":"fd6fdetect.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "choozi",
|
||||
"name": "Choozi",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "Choose people or things at random using Bangle.js.",
|
||||
"tags": "tool",
|
||||
"readme": "README.md",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"choozi.app.js","url":"app.js"},
|
||||
{"name":"choozi.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "widclkbttm",
|
||||
"name": "Digital clock (Bottom) widget",
|
||||
"shortName":"Digital clock Bottom Widget",
|
||||
"icon": "widclkbttm.png",
|
||||
"version":"0.03",
|
||||
"description": "Displays time in the bottom area.",
|
||||
"readme": "README.md",
|
||||
"tags": "widget",
|
||||
"type": "widget",
|
||||
"storage": [
|
||||
{"name":"widclkbttm.wid.js","url":"widclkbttm.wid.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "pastel",
|
||||
"name": "Pastel Clock",
|
||||
"shortName": "Pastel",
|
||||
"icon": "pastel.png",
|
||||
"version":"0.01",
|
||||
"description": "A Configurable clock with custom fonts and background",
|
||||
"tags": "clock,b2",
|
||||
"type":"clock",
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"pastel.app.js","url":"pastel.app.js"},
|
||||
{"name":"pastel.img","url":"pastel.icon.js","evaluate":true},
|
||||
{"name":"pastel.settings.js","url":"pastel.settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"pastel.json"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Set LCD timeout for Espruino 2v10 compatibility
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
(() => {
|
||||
(() => {
|
||||
Bangle.setLCDTimeout(0);
|
||||
let intervalID;
|
||||
let settings = require("Storage").readJSON("ballmaze.json",true) || {};
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
0.01: Initial version of Balltastic released! Happy!
|
||||
0.02: Set LCD timeout for Espruino 2v10 compatibility
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
Bangle.setLCDBrightness(1);
|
||||
Bangle.setLCDMode("doublebuffered");
|
||||
Bangle.setLCDTimeout(0);
|
||||
|
||||
let points = 0;
|
||||
let level = 1;
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
0.01: Car Crazy is now avialable for testing in beta!
|
||||
0.02: 10 Levels are now added making the game harder as it goes along. Some of the levels include multiple cars and faster cars. More levels coming soon.
|
||||
0.03: Settings are now added so that you can reset your high score.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Car Crazy
|
||||
Car crazy is a fun game where you tilt your wrist left and right to avoid incoming cars. If you get hit by a car you lose a heart. In the game you have three hearts, if you get hit 3 times you are sent to the game over screen. Your goal is to try to last as long as you can. Because this game is still in beta please report any bugs here: https://forms.office.com/r/HnwYzG9Sk7.
|
||||
Car crazy is a fun game where you tilt your wrist left and right to avoid incoming cars. If you get hit by a car you lose a heart. In the game you have three hearts, if you get hit 3 times you are sent to the game over screen. Recently levels have been added making the game get harder as you play. Your goal is to try to last as long as you can. Because this game is still in production please report any bugs here: https://forms.office.com/r/HnwYzG9Sk7.
|
||||
|
||||
### Images:
|
||||
(Coming Soon)
|
||||
|
|
@ -11,8 +11,50 @@ BNT2: Hold down this button to start the game if you are on the starting page an
|
|||
Tilting Left-Right: Tilt your wrist left and right to steer your car and try not to get hit by the enemy car.
|
||||
|
||||
### Feautures Coming Soon:
|
||||
0.02: Levels are creating making the game get harder as it goes along.
|
||||
0.02: Levels are creating making the game get harder as it goes along. (Completed)
|
||||
|
||||
0.03: Optional soundtrack in settings. More levels.
|
||||
0.03: Setting for reseting high score. (Completed)
|
||||
|
||||
0.04: With higher scores you can now unlock different colors of cars.
|
||||
0.04: Optional soundtrack in settings. More levels.
|
||||
|
||||
0.05: With higher scores you can now unlock different colors of cars. More settings.
|
||||
|
||||
0.06: Car selector at game over screen and start screen.
|
||||
|
||||
0.07 More levels.
|
||||
|
||||
0.08 More types of Cars.
|
||||
|
||||
### Levels
|
||||
Here is a list of all the levels:
|
||||
|
||||
##### Level 1:
|
||||
Basic single car moving at 10 speed.
|
||||
|
||||
##### Level 2:
|
||||
A single car can come from any position at the top of the screen.
|
||||
|
||||
##### Level 3:
|
||||
A single car are coming at 12 speed and can come from any position at the top of the screen.
|
||||
|
||||
##### Level 4:
|
||||
2 cars come at 8 speed.
|
||||
|
||||
##### Level 5:
|
||||
2 cars come at 9 speed.
|
||||
|
||||
##### Level 6:
|
||||
2 cars come at 9.5 and 8 speed.
|
||||
|
||||
##### Level 7:
|
||||
2 cars come at 10 and 8 speed.
|
||||
|
||||
##### Level 8:
|
||||
2 cars come at 11.5 speed.
|
||||
|
||||
##### Level 9 and Above:
|
||||
2 cars come at 13 and 14 speed.
|
||||
|
||||
### Other
|
||||
|
||||
-Settings are now avialable for resetting your high score.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ var PurpleCar = require("heatshrink").decompress(atob("ol74UBitg///BIP/7lVqtUDJU
|
|||
|
||||
var LightGreenCar = require("heatshrink").decompress(atob("ol74UBocF///BIP1z9VqtUDJUVBwIABq2qABOVCCkolQJC0AwDgWolAQD1EqwBCH0EqCCdKxQuEAAkKwGhCH4Q/CGD7ECDINEAAoQIwBACgQQYwQ+EAAcC1EACAeAlQfDAAheBCG2oCBUCCB8qCCiQBdp3+fxW/CH4Q/CH4QxgQQKwAQD1QQK1QQUmQQKxiH/CBGqCB3/5WACA+j/4Qf14QxKjGqRI0CCAv+x4QBEYcC1Wo/SpFCBtUlQQBBgISBAAQGBCAOo6te1QQB///lQPC1AGBCAOqyoQQlAuBCAXPAQIAD/Q2BlGoCAgAGCAUqCBnyCAVS1WrB5AAB9RUDCB1WCB0VVIIQMXQYQ/CAbFBBw/8BQIQFn4QewDNBCQgOCgEACDADBAAMC1T5BCAwABCQIACA4QQZABAQ/CH4Q/CH4QC1APKhQQPlQQUxWgGJOCCAcCCBWACAmqGhAQFwEqCA8qFYIQTuWohWqAAIQE1WghUFCANVD4JFBAAqzBlQPCCCNyHIIQGMoONCAdWQ5WVCCjNJZIYQM1WtCAt6KYwAB0oQFIoIPGIIoQTqhDG6oMDA"));
|
||||
|
||||
function consoleDebug(message) {
|
||||
//console.log(message);
|
||||
}
|
||||
|
||||
function getRandomInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
|
|
@ -30,21 +34,55 @@ function getRandomInt(min, max) {
|
|||
|
||||
function moveEnemyPosition(){
|
||||
score += 1;
|
||||
randomRoadPositionIndicator = getRandomInt(1, 4);
|
||||
if ((randomRoadPositionIndicator == 1)) {
|
||||
randomRoadPosition = 85;
|
||||
}else if((randomRoadPositionIndicator == 2)){
|
||||
randomRoadPosition = 120;
|
||||
}else {
|
||||
randomRoadPosition = 155;
|
||||
checkForNextLevel();
|
||||
if(level == 1){
|
||||
randomRoadPositionIndicator = getRandomInt(1, 4);
|
||||
if ((randomRoadPositionIndicator == 1)) {
|
||||
enemyPositonCenterX = 85;
|
||||
}else if((randomRoadPositionIndicator == 2)){
|
||||
enemyPositonCenterX = 120;
|
||||
}else {
|
||||
enemyPositonCenterX = 155;
|
||||
}
|
||||
}else if(level == 2||level==3){
|
||||
enemyPositonCenterX = getRandomInt(85, 155);
|
||||
}else if(level == 4 || level == 5 || level == 6 || level == 8 || level == 9 || level == 10 || level > 10){
|
||||
do{
|
||||
randomRoadPositionIndicator = getRandomInt(1, 4);
|
||||
randomRoadPositionIndicator2 = getRandomInt(1, 4);
|
||||
}while(randomRoadPositionIndicator==randomRoadPositionIndicator2);
|
||||
|
||||
if ((randomRoadPositionIndicator == 1)) {
|
||||
enemyPositonCenterX = 85;
|
||||
}else if((randomRoadPositionIndicator == 2)){
|
||||
enemyPositonCenterX = 120;
|
||||
}else if((randomRoadPositionIndicator == 3)){
|
||||
enemyPositonCenterX = 155;
|
||||
}
|
||||
|
||||
if ((randomRoadPositionIndicator2 == 1)) {
|
||||
enemyPositonCenterX2 = 85;
|
||||
}else if((randomRoadPositionIndicator2 == 2)){
|
||||
enemyPositonCenterX2 = 120;
|
||||
}else if((randomRoadPositionIndicator2 == 3)){
|
||||
enemyPositonCenterX2 = 155;
|
||||
}else if(level == 7||level == 8){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function collision(){
|
||||
if(gameStatus == GAMEPLAYING){
|
||||
consoleDebug("Px:"+playerCarLeftX+", "+playerCarRightX);
|
||||
consoleDebug("1x:"+enemyCarLeftX+", "+enemyCarRightX);
|
||||
consoleDebug("2x:"+enemyCarLeftX2+", "+enemyCarRightX2);
|
||||
consoleDebug("Py:"+playerCarFrontY);
|
||||
consoleDebug("1y:"+enemyCarFrontY);
|
||||
consoleDebug("2y:"+enemyCarFrontY2);
|
||||
if
|
||||
(
|
||||
(enemyCarFrontY > playerCarFrontY)
|
||||
(enemyCarFrontY < 300 && enemyCarFrontY > playerCarFrontY)
|
||||
&&
|
||||
(
|
||||
(enemyCarLeftX > playerCarLeftX && enemyCarLeftX < playerCarRightX)
|
||||
|
|
@ -52,51 +90,105 @@ function collision(){
|
|||
(enemyCarRightX > playerCarLeftX && enemyCarRightX < playerCarRightX)
|
||||
)
|
||||
){
|
||||
// hit
|
||||
setTimeout(collision, 2500); // wait 2.5 second for the function to actiavte agian.
|
||||
numberofHearts -= 1;
|
||||
score -= 1;
|
||||
Bangle.buzz();
|
||||
}else{
|
||||
// miss
|
||||
setTimeout(collision, 1); // try again in 1 milliseconds.
|
||||
// hit car 1
|
||||
consoleDebug("1 HIT");
|
||||
enemyPositonCenterY = 300;
|
||||
numberofHearts -= 1;
|
||||
Bangle.buzz(50,50);
|
||||
}else if
|
||||
(
|
||||
(enemyCarFrontY2 < 300 && enemyCarFrontY2 > playerCarFrontY)
|
||||
&&
|
||||
(
|
||||
(enemyCarLeftX2 > playerCarLeftX && enemyCarLeftX2 < playerCarRightX)
|
||||
||
|
||||
(enemyCarRightX2 > playerCarLeftX && enemyCarRightX2 < playerCarRightX)
|
||||
)
|
||||
){
|
||||
// hit car 2
|
||||
consoleDebug("2 HIT");
|
||||
enemyPositonCenterY2 = 300;
|
||||
numberofHearts -= 1;
|
||||
Bangle.buzz(50,50);
|
||||
}
|
||||
setTimeout(collision, 50); // try again in 50 milliseconds.
|
||||
}
|
||||
}
|
||||
|
||||
function storeMyData(data) {
|
||||
// ensure there are less than 500 elements in the array
|
||||
while (log.length >= 500) log.shift();
|
||||
// append a new item to the array
|
||||
log.push(data);
|
||||
function checkForNextLevel(){
|
||||
if(score < 10){
|
||||
level = 1;
|
||||
}else if(score >= 10 && score < 20){
|
||||
level = 2;
|
||||
}else if(score >= 20 && score < 30){
|
||||
level = 3;
|
||||
}else if(score >= 30 && score < 40){
|
||||
level = 4;
|
||||
}else if(score >= 40 && score < 50){
|
||||
level = 5;
|
||||
}else if(score >= 50 && score < 60){
|
||||
level = 6;
|
||||
}else if(score >= 60 && score < 70){
|
||||
level = 7;
|
||||
}else if(score >= 70 && score < 80){
|
||||
level = 8;
|
||||
}else if(score >= 80 && score < 90){
|
||||
level = 9;
|
||||
}else if(score >= 90){
|
||||
level = 10;
|
||||
}
|
||||
}
|
||||
|
||||
var accel = Bangle.getAccel();
|
||||
|
||||
var file = require("Storage").open("CarCrazy.csv","r");
|
||||
var currentHighScore = file.readLine();
|
||||
if (currentHighScore == undefined) currentHighScore = 0;
|
||||
|
||||
var BackgroundStartingPosition = 75;
|
||||
var carScale = 0.5;
|
||||
var accel = Bangle.getAccel();
|
||||
var playerCarPosition = 120-accel.x*40;
|
||||
var BackgroundYPosition = BackgroundStartingPosition;
|
||||
var randomRoadPositionIndicator = getRandomInt(1, 3);
|
||||
var randomRoadPosition = 120;
|
||||
var enemyPositonY = 30;
|
||||
|
||||
var randomRoadPositionIndicator;
|
||||
var randomRoadPositionIndicator2;
|
||||
var enemyPositonCenterX;
|
||||
var enemyPositonCenterX2;
|
||||
|
||||
var carScale = 0.5;
|
||||
var carWidth = 30;
|
||||
var carHeight = 60;
|
||||
var playerCarY = 130;
|
||||
var enemyCarLeftX;
|
||||
var enemyCarRightX;
|
||||
|
||||
var playerCarCenterY = 130;
|
||||
var playerCarCenterX;
|
||||
|
||||
var enemyPositonCenterY = 0 - carHeight/2;
|
||||
var enemyPositonCenterY2 = 0 - carHeight/2;
|
||||
|
||||
var playerCarLeftX;
|
||||
var playerCarRightX;
|
||||
var enemyCarFrontY;
|
||||
var playerCarFrontY;
|
||||
|
||||
var playerCarFrontY;
|
||||
var playerCarBackY;
|
||||
var playerCarLeftX;
|
||||
var playerCarRightX;
|
||||
|
||||
var enemyCarFrontY;
|
||||
var enemyCarBackY;
|
||||
var enemyCarLeftX;
|
||||
var enemyCarRightX;
|
||||
|
||||
var enemyCarFrontY2;
|
||||
var enemyCarBackY2;
|
||||
var enemyCarLeftX2;
|
||||
var enemyCarRightX2;
|
||||
|
||||
var GAMEPLAYING = 1;
|
||||
var GAMEOVER = 2;
|
||||
var GAMESTART = 3;
|
||||
var gameStatus = GAMESTART;
|
||||
var score = 0;
|
||||
var level = 1;
|
||||
|
||||
|
||||
moveEnemyPosition();
|
||||
collision();
|
||||
|
|
@ -114,11 +206,16 @@ function draw(){
|
|||
if(gameStatus == GAMEPLAYING){
|
||||
BackgroundYPosition += 10;
|
||||
accel = Bangle.getAccel();
|
||||
playerCarPosition = 120-accel.x*40;
|
||||
playerCarCenterX = Math.round(120-accel.x*120);
|
||||
if (playerCarCenterX > 170) { playerCarCenterX = 170; }
|
||||
if (playerCarCenterX < 70) { playerCarCenterX = 70; }
|
||||
g.flip();
|
||||
g.drawImage(backgroundImage,125,BackgroundYPosition, {scale:13,rotate:0});
|
||||
g.drawImage(RedCar,playerCarPosition,playerCarY, {scale:carScale,rotate:3.142});
|
||||
g.drawImage(OrangeCar,randomRoadPosition,enemyPositonY, {scale:carScale,rotate:0});
|
||||
g.drawImage(RedCar,playerCarCenterX,playerCarCenterY, {scale:carScale,rotate:3.142});
|
||||
g.drawImage(OrangeCar,enemyPositonCenterX,enemyPositonCenterY, {scale:carScale,rotate:0});
|
||||
if(level>=4){
|
||||
g.drawImage(OrangeCar,enemyPositonCenterX2,enemyPositonCenterY2, {scale:carScale,rotate:0});
|
||||
}
|
||||
|
||||
if(numberofHearts==3){
|
||||
g.drawImage(heartImage,10,10, {scale:2,rotate:0});
|
||||
|
|
@ -141,23 +238,38 @@ function draw(){
|
|||
}
|
||||
}
|
||||
|
||||
playerCarFrontY = playerCarY-carHeight/2;
|
||||
playerCarBackY = playerCarY+carHeight/2;
|
||||
playerCarLeftX = playerCarPosition-carWidth/2;
|
||||
playerCarRightX = playerCarPosition+carWidth/2;
|
||||
playerCarFrontY = playerCarCenterY-carHeight/2;
|
||||
playerCarBackY = playerCarCenterY+carHeight/2;
|
||||
playerCarLeftX = playerCarCenterX-carWidth/2;
|
||||
playerCarRightX = playerCarCenterX+carWidth/2;
|
||||
|
||||
enemyCarFrontY = enemyPositonY+carHeight/2;
|
||||
enemyCarBackY = enemyPositonY-carHeight/2;
|
||||
enemyCarLeftX = randomRoadPosition-carWidth/2;
|
||||
enemyCarRightX = randomRoadPosition+carWidth/2;
|
||||
enemyCarFrontY = enemyPositonCenterY+carHeight/2;
|
||||
enemyCarBackY = enemyPositonCenterY-carHeight/2;
|
||||
enemyCarLeftX = enemyPositonCenterX-carWidth/2;
|
||||
enemyCarRightX = enemyPositonCenterX+carWidth/2;
|
||||
|
||||
enemyCarFrontY2 = enemyPositonCenterY2+carHeight/2;
|
||||
enemyCarBackY2 = enemyPositonCenterY2-carHeight/2;
|
||||
enemyCarLeftX2 = enemyPositonCenterX2-carWidth/2;
|
||||
enemyCarRightX2 = enemyPositonCenterX2+carWidth/2;
|
||||
|
||||
g.setColor(255,0,0);
|
||||
//g.drawRect(playerCarLeftX, playerCarFrontY, playerCarRightX, playerCarBackY);
|
||||
//g.drawRect(enemyCarLeftX, enemyCarFrontY, enemyCarRightX, enemyCarBackY);
|
||||
//g.drawRect(enemyCarLeftX2, enemyCarFrontY2, enemyCarRightX2, enemyCarBackY2);
|
||||
|
||||
g.setColor(0,0,0);
|
||||
g.drawString("Score: "+score,180,5);
|
||||
g.drawString("HighScore:",178,15);
|
||||
g.drawString(currentHighScore,205,25);
|
||||
g.drawString("Level: "+level,180,150);
|
||||
|
||||
//g.drawString("P:"+playerCarLeftX+", "+playerCarRightX,180,90);
|
||||
//g.drawString("1:"+enemyCarLeftX+", "+enemyCarRightX,180,100);
|
||||
//g.drawString("2:"+enemyCarLeftX2+", "+enemyCarRightX2,180,110);
|
||||
//g.drawString("P:"+playerCarFrontY,180,120);
|
||||
//g.drawString("1:"+enemyCarFrontY,180,130);
|
||||
//g.drawString("2:"+enemyCarFrontY2,180,140);
|
||||
|
||||
if(BackgroundYPosition > 170){
|
||||
BackgroundYPosition = BackgroundStartingPosition;
|
||||
|
|
@ -200,41 +312,75 @@ function draw(){
|
|||
g.drawString("2 To Start",10,130);
|
||||
g.drawImage(LightGreenCar,180,115, {scale:0.5,rotate:3});
|
||||
g.drawImage(PurpleCar,215,115, {scale:0.5,rotate:3});
|
||||
//setTimeout(displayPopup, 3000);
|
||||
}
|
||||
}
|
||||
setInterval(draw ,10);
|
||||
setInterval(draw, 50);
|
||||
|
||||
|
||||
function moveEnemyCar(){
|
||||
if(gameStatus == GAMEPLAYING){
|
||||
enemyPositonY = enemyPositonY + 10;
|
||||
if((enemyPositonY > 200)){
|
||||
enemyPositonY = 30;
|
||||
if(level==1||level==2){
|
||||
enemyPositonCenterY = enemyPositonCenterY + 10;
|
||||
enemyPositonCenterY2 = 0;
|
||||
}else if(level==3){
|
||||
enemyPositonCenterY = enemyPositonCenterY + 12;
|
||||
enemyPositonCenterY2 = 0;
|
||||
}else if(level==4){
|
||||
enemyPositonCenterY = enemyPositonCenterY + 8;
|
||||
enemyPositonCenterY2 = enemyPositonCenterY2 + 8;
|
||||
}else if(level==5){
|
||||
enemyPositonCenterY = enemyPositonCenterY + 9;
|
||||
enemyPositonCenterY2 = enemyPositonCenterY2 + 9;
|
||||
}else if(level==6){
|
||||
enemyPositonCenterY = enemyPositonCenterY + 9.5;
|
||||
enemyPositonCenterY2 = enemyPositonCenterY2 + 8;
|
||||
}else if(level==7){
|
||||
enemyPositonCenterY = enemyPositonCenterY + 10;
|
||||
enemyPositonCenterY2 = enemyPositonCenterY2 + 8;
|
||||
}else if(level==8){
|
||||
enemyPositonCenterY = enemyPositonCenterY + 11.5;
|
||||
enemyPositonCenterY2 = enemyPositonCenterY2 + 11.5;
|
||||
}else if(level>=9){
|
||||
enemyPositonCenterY = enemyPositonCenterY + 13;
|
||||
enemyPositonCenterY2 = enemyPositonCenterY2 + 14;
|
||||
}
|
||||
if(enemyPositonCenterY > 200){
|
||||
enemyPositonCenterY = 300;
|
||||
}
|
||||
if(enemyPositonCenterY2 > 200){
|
||||
enemyPositonCenterY2 = 300;
|
||||
}
|
||||
if(enemyPositonCenterY > 200 && (enemyPositonCenterY2 > 200 || level < 4)){
|
||||
enemyPositonCenterY = 0 - carHeight/2;
|
||||
if (level >= 4) { enemyPositonCenterY2 = 0 - carHeight/2; }
|
||||
moveEnemyPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
setInterval(moveEnemyCar,10);
|
||||
setInterval(moveEnemyCar,50);
|
||||
|
||||
setWatch(() => {
|
||||
if(gameStatus == GAMESTART){
|
||||
gameStatus = GAMEPLAYING;
|
||||
collision();
|
||||
enemyPositonY = 0;
|
||||
numberofHearts = 3;
|
||||
enemyPositonCenterX = 120;
|
||||
enemyPositonCenterY = 0 - carHeight/2;
|
||||
enemyPositonCenterX2 = 120;
|
||||
enemyPositonCenterY2 = 0 - carHeight/2;
|
||||
score = 0;
|
||||
level = 1;
|
||||
checkForNextLevel();
|
||||
}else if(gameStatus == GAMEOVER){
|
||||
gameStatus = GAMEPLAYING;
|
||||
collision();
|
||||
enemyPositonY = 0;
|
||||
enemyPositonCenterX = 120;
|
||||
enemyPositonCenterY = 0 - carHeight/2;
|
||||
enemyPositonCenterX2 = 120;
|
||||
enemyPositonCenterY2 = 0 - carHeight/2;
|
||||
numberofHearts = 3;
|
||||
score = 0;
|
||||
level = 1;
|
||||
checkForNextLevel();
|
||||
}
|
||||
}, BTN2, {repeat:true});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
(function (back) {
|
||||
const menu = {
|
||||
'': { 'title': 'Car Crazy' },
|
||||
'< Back': back,
|
||||
'Reset Highscore': () => {
|
||||
E.showPrompt('Reset Highscore?').then((v) => {
|
||||
let delay = 50;
|
||||
if (v) {
|
||||
delay = 500;
|
||||
E.showMessage('Resetting');
|
||||
var f = require('Storage').open('CarCrazy.csv', 'w');
|
||||
f.write('0\n');
|
||||
}
|
||||
setTimeout(() => E.showMenu(menu), delay);
|
||||
});
|
||||
}
|
||||
};
|
||||
E.showMenu(menu);
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Choozi
|
||||
|
||||
Choose people or things at random using Bangle.js.
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/4cqOLNM5ei8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
## Usage
|
||||
|
||||
You can use Choozi to pick a person to play first in a board game. With all
|
||||
the players seated in a circle, set the number of segments equal to the number
|
||||
of players, ensure that each person knows which colour represents them, and then
|
||||
choose a segment. After a short animation, the chosen segment will fill the screen.
|
||||
|
||||
You can use Choozi to randomly select an element from any set with 2 to 13 members,
|
||||
as long as you can define a bijection between members of the set and coloured
|
||||
segments on the Bangle.js display.
|
||||
|
||||
## Controls
|
||||
|
||||
BTN1: increase the number of segments
|
||||
BTN2: choose a segment at random
|
||||
BTN3: decrease the number of segments
|
||||
|
||||
## Creator
|
||||
|
||||
James Stanley
|
||||
September 2021
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwggLIrnM4uqAAIhPgvMAAPFzIABzWgCxkMCweqC4QABDBYtC5QVFDBoWCCo5KLOQIWKDARFICxhJIFwOpC5owFFyAwGUYIuOGAwuRC4guSJAgXBCyIwDIyQXF5IXSzJeVMAReUAAOQhheTMAVcC6yOUC4aOUC7GZUyoXXzWqhQXVxGqC9mYC7OqC9eoxEKC6uBC6uIwAXBPCSmBwEAC6Z2BiAXBJCR2BgEAjQXSlGBC4JgSLwYABJCJGBLwJIDGB+IIwRIDGByNBIwZIDGBhdBRoQwSLoIuFGAYYKCwIuGGAgYI1QWBRgYYJMYmaFoSMEAAyrBAAgVCCxgYGjAWQAAMBC4UILZQA=="))
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
|
||||
/* Choozi - Choose people or things at random using Bangle.js.
|
||||
* Inspired by the "Chwazi" Android app
|
||||
*
|
||||
* James Stanley 2021
|
||||
*/
|
||||
|
||||
var colours = ['#ff0000', '#ff8080', '#00ff00', '#80ff80', '#0000ff', '#8080ff', '#ffff00', '#00ffff', '#ff00ff', '#ff8000', '#ff0080', '#8000ff', '#0080ff'];
|
||||
|
||||
var stepAngle = 0.18; // radians - resolution of polygon
|
||||
var gapAngle = 0.035; // radians - gap between segments
|
||||
var perimMin = 110; // px - min. radius of perimeter
|
||||
var perimMax = 120; // px - max. radius of perimeter
|
||||
|
||||
var segmentMax = 106; // px - max radius of filled-in segment
|
||||
var segmentStep = 5; // px - step size of segment fill animation
|
||||
var circleStep = 4; // px - step size of circle fill animation
|
||||
|
||||
// rolling ball animation:
|
||||
var maxSpeed = 0.08; // rad/sec
|
||||
var minSpeed = 0.001; // rad/sec
|
||||
var animStartSteps = 300; // how many steps before it can start slowing?
|
||||
var accel = 0.0002; // rad/sec/sec - acc-/deceleration rate
|
||||
var ballSize = 3; // px - ball radius
|
||||
var ballTrack = 100; // px - radius of ball path
|
||||
|
||||
var centreX = 120; // px - centre of screen
|
||||
var centreY = 120; // px - centre of screen
|
||||
|
||||
var fontSize = 50; // px
|
||||
|
||||
var radians = 2*Math.PI; // radians per circle
|
||||
|
||||
var defaultN = 3; // default value for N
|
||||
var minN = 2;
|
||||
var maxN = colours.length;
|
||||
var N;
|
||||
var arclen;
|
||||
|
||||
// https://www.frankmitchell.org/2015/01/fisher-yates/
|
||||
function shuffle (array) {
|
||||
var i = 0
|
||||
, j = 0
|
||||
, temp = null;
|
||||
|
||||
for (i = array.length - 1; i > 0; i -= 1) {
|
||||
j = Math.floor(Math.random() * (i + 1));
|
||||
temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
// draw an arc between radii minR and maxR, and between
|
||||
// angles minAngle and maxAngle
|
||||
function arc(minR, maxR, minAngle, maxAngle) {
|
||||
var step = stepAngle;
|
||||
var angle = minAngle;
|
||||
var inside = [];
|
||||
var outside = [];
|
||||
var c, s;
|
||||
while (angle < maxAngle) {
|
||||
c = Math.cos(angle);
|
||||
s = Math.sin(angle);
|
||||
inside.push(centreX+c*minR); // x
|
||||
inside.push(centreY+s*minR); // y
|
||||
// outside coordinates are built up in reverse order
|
||||
outside.unshift(centreY+s*maxR); // y
|
||||
outside.unshift(centreX+c*maxR); // x
|
||||
angle += step;
|
||||
}
|
||||
c = Math.cos(maxAngle);
|
||||
s = Math.sin(maxAngle);
|
||||
inside.push(centreX+c*minR);
|
||||
inside.push(centreY+s*minR);
|
||||
outside.unshift(centreY+s*maxR);
|
||||
outside.unshift(centreX+c*maxR);
|
||||
|
||||
var vertices = inside.concat(outside);
|
||||
g.fillPoly(vertices, true);
|
||||
}
|
||||
|
||||
// draw the arc segments around the perimeter
|
||||
function drawPerimeter() {
|
||||
g.clear();
|
||||
for (var i = 0; i < N; i++) {
|
||||
g.setColor(colours[i%colours.length]);
|
||||
var minAngle = (i/N)*radians;
|
||||
arc(perimMin,perimMax,minAngle,minAngle+arclen);
|
||||
}
|
||||
}
|
||||
|
||||
// animate a ball rolling around and settling at "target" radians
|
||||
function animateChoice(target) {
|
||||
var angle = 0;
|
||||
var speed = 0;
|
||||
var oldx = -10;
|
||||
var oldy = -10;
|
||||
var decelFromAngle = -1;
|
||||
var allowDecel = false;
|
||||
for (var i = 0; true; i++) {
|
||||
angle = angle + speed;
|
||||
if (angle > radians) angle -= radians;
|
||||
if (i < animStartSteps || (speed < maxSpeed && !allowDecel)) {
|
||||
speed = speed + accel;
|
||||
if (speed > maxSpeed) {
|
||||
speed = maxSpeed;
|
||||
/* when we reach max speed, we know how long it takes
|
||||
* to accelerate, and therefore how long to decelerate, so
|
||||
* we can work out what angle to start decelerating from */
|
||||
if (decelFromAngle < 0) {
|
||||
decelFromAngle = target-angle;
|
||||
while (decelFromAngle < 0) decelFromAngle += radians;
|
||||
while (decelFromAngle > radians) decelFromAngle -= radians;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!allowDecel && (angle < decelFromAngle) && (angle+speed >= decelFromAngle)) allowDecel = true;
|
||||
if (allowDecel) speed = speed - accel;
|
||||
if (speed < minSpeed) speed = minSpeed;
|
||||
if (speed == minSpeed && angle < target && angle+speed >= target) return;
|
||||
}
|
||||
|
||||
var r = i/2;
|
||||
if (r > ballTrack) r = ballTrack;
|
||||
var x = centreX+Math.cos(angle)*r;
|
||||
var y = centreY+Math.sin(angle)*r;
|
||||
g.setColor('#000000');
|
||||
g.fillCircle(oldx,oldy,ballSize+1);
|
||||
g.setColor('#ffffff');
|
||||
g.fillCircle(x, y, ballSize);
|
||||
oldx=x;
|
||||
oldy=y;
|
||||
}
|
||||
}
|
||||
|
||||
// choose a winning segment and animate its selection
|
||||
function choose() {
|
||||
var chosen = Math.floor(Math.random()*N);
|
||||
var minAngle = (chosen/N)*radians;
|
||||
var maxAngle = minAngle + arclen;
|
||||
animateChoice((minAngle+maxAngle)/2);
|
||||
g.setColor(colours[chosen%colours.length]);
|
||||
for (var i = segmentMax-segmentStep; i >= 0; i -= segmentStep)
|
||||
arc(i, perimMax, minAngle, maxAngle);
|
||||
arc(0, perimMax, minAngle, maxAngle);
|
||||
for (var r = 1; r < segmentMax; r += circleStep)
|
||||
g.fillCircle(centreX,centreY,r);
|
||||
g.fillCircle(centreX,centreY,segmentMax);
|
||||
}
|
||||
|
||||
// draw the current value of N in the middle of the screen, with
|
||||
// up/down arrows
|
||||
function drawN() {
|
||||
g.setColor('#ffffff');
|
||||
g.setFont("Vector",fontSize);
|
||||
g.drawString(N,centreX-g.stringWidth(N)/2+4,centreY-fontSize/2);
|
||||
if (N < maxN)
|
||||
g.fillPoly([centreX-6,centreY-fontSize/2-7, centreX+6,centreY-fontSize/2-7, centreX, centreY-fontSize/2-14]);
|
||||
if (N > minN)
|
||||
g.fillPoly([centreX-6,centreY+fontSize/2+5, centreX+6,centreY+fontSize/2+5, centreX, centreY+fontSize/2+12]);
|
||||
}
|
||||
|
||||
// update number of segments, with min/max limit, "arclen" update,
|
||||
// and screen reset
|
||||
function setN(n) {
|
||||
N = n;
|
||||
if (N < minN) N = minN;
|
||||
if (N > maxN) N = maxN;
|
||||
arclen = radians/N - gapAngle;
|
||||
drawPerimeter();
|
||||
}
|
||||
|
||||
// save N to choozi.txt
|
||||
function writeN() {
|
||||
var file = require("Storage").open("choozi.txt","w");
|
||||
file.write(N);
|
||||
}
|
||||
|
||||
// load N from choozi.txt
|
||||
function readN() {
|
||||
var file = require("Storage").open("choozi.txt","r");
|
||||
var n = file.readLine();
|
||||
if (n !== undefined) setN(parseInt(n));
|
||||
else setN(defaultN);
|
||||
}
|
||||
|
||||
shuffle(colours); // is this really best?
|
||||
Bangle.setLCDMode("direct");
|
||||
Bangle.setLCDTimeout(0); // keep screen on
|
||||
readN();
|
||||
drawN();
|
||||
|
||||
setWatch(() => {
|
||||
setN(N+1);
|
||||
drawN();
|
||||
}, BTN1, {repeat:true});
|
||||
|
||||
setWatch(() => {
|
||||
writeN();
|
||||
drawPerimeter();
|
||||
choose();
|
||||
}, BTN2, {repeat:true});
|
||||
|
||||
setWatch(() => {
|
||||
setN(N-1);
|
||||
drawN();
|
||||
}, BTN3, {repeat:true});
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
|
|
@ -0,0 +1 @@
|
|||
0.1: Added source code
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwIjgg/gAp0IgfAiAFBjkP+E4AoM8n/8ngFBvn//8+AoP//Ef/4FBv/Agf+AoMPwEB+AFCjEYAoUenk8vAvCAoIvCnAFBjgFCC4IFCCgUeEQNwAoMO+EPuPD4eOAoPz8fH54FH+IRBx4FBDogpFGoxBFJopZFMopxFPoqJFSoqhFVoq5FgAFBa6gAW"))
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
g.clear();
|
||||
let amount = 'global value';
|
||||
function FindFD6FBeacons() {
|
||||
NRF.findDevices(function(devices) {
|
||||
g.setFont('Vector', 75);
|
||||
g.setFontAlign(0,0);
|
||||
var amount = devices.length;
|
||||
g.clear();
|
||||
g.drawString(amount, 125, 100);
|
||||
if (amount == 1) {
|
||||
g.setFont('Vector', 25);
|
||||
g.drawString('FD6F', 125, 150);
|
||||
g.drawString('beacon', 125, 175);
|
||||
g.drawString('nearby', 125, 200);
|
||||
} else{
|
||||
g.setFont('Vector', 25);
|
||||
g.drawString('FD6F', 125, 150);
|
||||
g.drawString('beacons', 125, 175);
|
||||
g.drawString('nearby', 125, 200);
|
||||
}
|
||||
}, {timeout : 1000, filters : [{services: ['fd6f'] }] });
|
||||
}
|
||||
setInterval(FindFD6FBeacons, 2000);
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -23,3 +23,4 @@
|
|||
0.21: Fix HRM setting
|
||||
0.22: Respect Quiet Mode
|
||||
0.23: Allow notification dismiss to remove from phone too
|
||||
0.24: tag HRM power requests to allow this to work alongside other widgets/apps (fix #799)
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@
|
|||
if (activityInterval)
|
||||
clearInterval(activityInterval);
|
||||
activityInterval = undefined;
|
||||
if (s.hrm) Bangle.setHRMPower(1);
|
||||
if (s.hrm) Bangle.setHRMPower(1,"gbr");
|
||||
if (s.hrm) {
|
||||
if (realtime) {
|
||||
// if realtime reporting, leave HRM on and use that to trigger events
|
||||
|
|
@ -138,7 +138,7 @@
|
|||
hrmTimeout = 5;
|
||||
activityInterval = setInterval(function() {
|
||||
hrmTimeout = 5;
|
||||
Bangle.setHRMPower(1);
|
||||
Bangle.setHRMPower(1,"gbr");
|
||||
}, interval*1000);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -281,7 +281,7 @@
|
|||
if (hrmTimeout!==undefined) hrmTimeout--;
|
||||
if (ok || hrmTimeout<=0) {
|
||||
if (hrmTimeout!==undefined)
|
||||
Bangle.setHRMPower(0);
|
||||
Bangle.setHRMPower(0,"gbr");
|
||||
sendActivity(hrm.confidence>20 ? hrm.bpm : -1);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,3 +11,4 @@
|
|||
Reduce memory usage by ~30%
|
||||
Generate scale based on defined minimum and maximum measurement
|
||||
Added background line on 50% to ease estimation of drawn values
|
||||
0.06: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@
|
|||
if (settings.isRecording) {
|
||||
WIDGETS["heart"].width = 24;
|
||||
Bangle.on('HRM',onHRM);
|
||||
Bangle.setHRMPower(1);
|
||||
Bangle.setHRMPower(1,"heart");
|
||||
var n = settings.fileNbr.toString(36);
|
||||
recFile = require("Storage").open(".heart"+n,"a");
|
||||
} else {
|
||||
WIDGETS["heart"].width = 0;
|
||||
Bangle.setHRMPower(0);
|
||||
Bangle.setHRMPower(0,"heart");
|
||||
recFile = undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@
|
|||
0.05: Add display for the next strike time
|
||||
0.06: Move the next strike time to the first row of display
|
||||
0.07: Change the boot function to avoid reloading the entire watch
|
||||
0.08: Default to no strikes. Fix file-not-found issue during the first boot. Add data file.
|
||||
|
|
|
|||
|
|
@ -1,25 +1,10 @@
|
|||
const storage = require('Storage');
|
||||
let settings;
|
||||
var settings = storage.readJSON('hourstrike.json', 1);
|
||||
|
||||
function updateSettings() {
|
||||
storage.write('hourstrike.json', settings);
|
||||
}
|
||||
|
||||
function resetSettings() {
|
||||
settings = {
|
||||
interval: 3600,
|
||||
start: 9,
|
||||
end: 21,
|
||||
vlevel: 0.5,
|
||||
next_hour: -1,
|
||||
next_minute: -1,
|
||||
};
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
settings = storage.readJSON('hourstrike.json', 1);
|
||||
if (!settings) resetSettings();
|
||||
|
||||
function showMainMenu() {
|
||||
var mode_txt = ['Off','1 min','5 min','10 min','1/4 h','1/2 h','1 h'];
|
||||
var mode_interval = [-1,60,300,600,900,1800,3600];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
(function() {
|
||||
function setup () {
|
||||
var settings = require('Storage').readJSON('hourstrike.json',1)||[];
|
||||
var settings = require('Storage').readJSON('hourstrike.json',1);
|
||||
var t = new Date();
|
||||
var t_min_sec = t.getMinutes()*60+t.getSeconds();
|
||||
var wait_msec = settings.interval>0?(settings.interval-t_min_sec%settings.interval)*1000:-1;
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
{"interval":-1,"start":9,"end":21,"vlevel":0.5,"next_hour":-1,"next_minute":-1}
|
||||
|
|
@ -7,3 +7,4 @@
|
|||
0.07: Don't clear all intervals during initialisation
|
||||
0.08: Use Bangle.setUI for button/launcher handling
|
||||
0.09: fix font size for latest firmwares
|
||||
0.10: Configure the side text direction based on the wrist on which you wear your watch
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ A readable and informational digital watch, with date, seconds and moon phase an
|
|||
- Readable
|
||||
- Informative: hours, minutes, secondsa, date, year and moon phase
|
||||
- Pairs nicely with any other apps: in setting > large clock any installed app can be assigned to BTN1 and BTN3 in order to open it easily directly from the watch, without the hassle of passing trough the launcher. For example BTN1 can be assigned to alarm and BTN3 to chronometer.
|
||||
- Configure the text direction on the side depending on the wrist on which you wear your watch.
|
||||
|
||||
## How to use it
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ const settings = require("Storage").readJSON("largeclock.json", 1)||{};
|
|||
const BTN1app = settings.BTN1 || "";
|
||||
const BTN3app = settings.BTN3 || "";
|
||||
|
||||
const right_hand = !!settings.right_hand;
|
||||
const rotation = right_hand ? 3 : 1;
|
||||
|
||||
function drawMoon(d) {
|
||||
const BLACK = 0,
|
||||
MOON = 0x41f,
|
||||
|
|
@ -145,9 +148,9 @@ function drawTime(d) {
|
|||
g.setColor(1, 50, 1);
|
||||
g.drawString(minutes, 40, 130, true);
|
||||
g.setFont("Vector", 20);
|
||||
g.setRotation(3);
|
||||
g.drawString(`${dow} ${day} ${month}`, 60, 10, true);
|
||||
g.drawString(year, is12Hour ? 46 : 75, 205, true);
|
||||
g.setRotation(rotation);
|
||||
g.drawString(`${dow} ${day} ${month}`, 60, right_hand?10:205, true);
|
||||
g.drawString(year, is12Hour?(right_hand?56:120):(right_hand?85:115), right_hand?205:10, true);
|
||||
lastMinutes = minutes;
|
||||
}
|
||||
g.setRotation(0);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"BTN1": "",
|
||||
"BTN3": ""
|
||||
"BTN3": "",
|
||||
"right_hand": false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@
|
|||
|
||||
const settings = s.readJSON("largeclock.json", 1) || {
|
||||
BTN1: "",
|
||||
BTN3: ""
|
||||
BTN3: "",
|
||||
right_hand: false
|
||||
};
|
||||
|
||||
function showApps(btn) {
|
||||
|
|
@ -67,10 +68,19 @@
|
|||
}
|
||||
|
||||
const mainMenu = {
|
||||
"": { title: "Large Clock Settings" },
|
||||
"": { title: "Large Clock" },
|
||||
"< Back": back,
|
||||
"BTN1 app": () => showApps("BTN1"),
|
||||
"BTN3 app": () => showApps("BTN3")
|
||||
"BTN3 app": () => showApps("BTN3"),
|
||||
"On right hand": {
|
||||
value: !!settings.right_hand,
|
||||
format: v=>v?"Yes":"No",
|
||||
onchange: v=>{
|
||||
settings.right_hand = v;
|
||||
s.writeJSON("largeclock.json", settings);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
E.showMenu(mainMenu);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@
|
|||
BTN2 now goes to menu on release
|
||||
0.10: Add birthday style
|
||||
0.11: Skip double buffering, use 240x240 size
|
||||
0.12: Fix swipe direction (#800)
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ function move(dir) {
|
|||
|
||||
|
||||
|
||||
Bangle.on('swipe',move);
|
||||
Bangle.on('swipe', dir => move(-dir));
|
||||
setWatch(()=>move(1), BTN3, {repeat:true});
|
||||
setWatch(()=>{
|
||||
// If we're on the last page
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@
|
|||
0.06: Add support for scrolling, option for 3 bit maps
|
||||
0.07: Move to 96px tiles - less files (64 -> 25) and speed up rendering
|
||||
0.08: Update for drag event refactor
|
||||
0.09: Use current theme cols when drawing GPS info
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@ function drawMarker() {
|
|||
var fix;
|
||||
Bangle.on('GPS',function(f) {
|
||||
fix=f;
|
||||
g.clearRect(0,y1,240,y1+8);
|
||||
g.setColor(1,1,1);
|
||||
g.setFont("6x8");
|
||||
g.setFontAlign(0,0);
|
||||
g.reset().clearRect(0,y1,240,y1+8).setFont("6x8").setFontAlign(0,0);
|
||||
var txt = fix.satellites+" satellites";
|
||||
if (!fix.fix)
|
||||
txt += " - NO FIX";
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
0.01: First release
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Pastel Clock - a configurable clock with custom fonts and background
|
||||
|
||||
* Designed specifically for Bangle 1 and Bangle 2
|
||||
* A choice of 5 different custom fonts
|
||||
* Supports the Light and Dark themes
|
||||
* Has a settings menu, change font, enable/disable the grid and the date display
|
||||
|
||||
|
||||
I came up with the name Pastel due to the shade of the grid background.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A1PYIqqAAUqp0kGMopCuVOvHHAAV4GEcAFQgAFuQwhLoIuJAAMqGEEAzovL44vgkguM49OGD0ApwvNzovfdpSQjF+FyEwskewyPFgAADX7kkA414EwIqCp14zmcuUkGJ5FEkgnFpwHHFgXBTQ0kFhpSBvAABD4KHHEY0ATAw7EFpTnHRA8Azg2FFxIvMCxGcHI0qG4kAlQuJF5gXJcIIwEvARDlSVGfZAwJuQWKSQwuCRpQAB4IvK/0kC5K0BGAouHzucvAIFL5bvHvAbCGAMkEAQuEzi0BAAZ9FX5QuH48kDgILB4IFCAAPB4IsCklySZIvKUxWdLYYABpyhBuUkIxAADHoJfSJI140SOBzgTNFxQwPQ4QuBAgK1FOoyiCF5QwBpwaIzgZBzlydgT2BCZGcHwMrmMHGJjnDAAd4JAImCvDRDGAIKBvF4uTJBA4MrrtjrljrowOAAIZCEQPBFQTuBHgpXBCYkGnBcCmVjsaTOJYOcFgZ+BeAXHkiRBAAprBrkxFAM4MAMyMgIvMkjaGKQICBMQUqfxInBlZ+DMIIGBF5QgGgAHBWQLCGAAnBgC5BFIUrR4KQNF4wGCki+DgCQHBwMySQNcFQMHSQKPTFQ4vJlS/BrhaBMgIwBd5ofHXwYALXALuCGQcxHAIvLznBD4q6JX41jmIvCdoKOBMB0ASQtyTJIADp0GdIVjgwvBMYQvMGISzEL5ucRIQAIg4vOzmdzpdBM4NOZA1OlUqBoUrFIZeCAAUrF5qSCAAIGDlSIEBgoNBGAVdBQMGmJoBgwvOG5JZBFgoNFLoIMCJgwxXO5wpYAH4A/AH4A/ABwA="))
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
require("Storage").write("pastel.info",{
|
||||
"id":"pastel",
|
||||
"name":"Configurable clock with custom fonts and background",
|
||||
"src":"pastel.app.js",
|
||||
"icon":"pastel.img",
|
||||
"type":"clock"
|
||||
});
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
|
@ -0,0 +1,56 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "pastel.json";
|
||||
|
||||
// initialize with default settings...
|
||||
let s = {
|
||||
'grid': false,
|
||||
'date': false,
|
||||
'font': "Lato"
|
||||
}
|
||||
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
const storage = require('Storage')
|
||||
let settings = storage.readJSON(SETTINGS_FILE, 1) || {}
|
||||
const saved = settings || {}
|
||||
for (const key in saved) {
|
||||
s[key] = saved[key]
|
||||
}
|
||||
|
||||
function save() {
|
||||
settings = s
|
||||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
var font_options = ["Lato","Architect","GochiHand","CabinSketch","Orbitron"];
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Pastel Clock' },
|
||||
'Font': {
|
||||
value: 0 | font_options.indexOf(s.font),
|
||||
min: 0, max: 4,
|
||||
format: v => font_options[v],
|
||||
onchange: v => {
|
||||
s.font = font_options[v];
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Show Grid': {
|
||||
value: s.grid,
|
||||
format: () => (s.grid ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.grid = !s.grid
|
||||
save()
|
||||
},
|
||||
},
|
||||
'Show Date': {
|
||||
value: s.date,
|
||||
format: () => (s.date ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.date = !s.date
|
||||
save()
|
||||
},
|
||||
},
|
||||
'< Back': back,
|
||||
})
|
||||
})
|
||||
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
|
@ -0,0 +1,6 @@
|
|||
const SETTINGS_FILE = "pastel.json";
|
||||
let settings = {"grid":false, "date":false, "font":"Lato"}
|
||||
|
||||
require("Storage").write(SETTINGS_FILE, settings);
|
||||
settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
|
||||
console.log(settings);
|
||||
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Shortcuts
|
||||
|
||||
Any installed app can be assigned to BTN1 and BTN3 and launched directly from compatible watch faces. This works with any watch face that uses `Bangle.setUI("clock")`.
|
||||
|
||||
## Credits
|
||||
|
||||
<a target="_blank" href="https://icons8.com/icon/i1z7pQ2orcJk/shortcut">Shortcut</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
|
||||
|
After Width: | Height: | Size: 767 B |
|
|
@ -0,0 +1,15 @@
|
|||
(function() {
|
||||
var sui = Bangle.setUI;
|
||||
Bangle.setUI = function(mode, cb) {
|
||||
if (mode!="clock") return sui(mode,cb);
|
||||
return sui("clockupdown", (dir) => {
|
||||
let settings = require("Storage").readJSON("shortcuts.json", 1)||{};
|
||||
if (dir == -1) {
|
||||
if (settings.BTN1) load(settings.BTN1);
|
||||
} else if (dir == 1) {
|
||||
if (settings.BTN3) load(settings.BTN3);
|
||||
}
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
(function(back) {
|
||||
const s = require("Storage");
|
||||
const apps = s
|
||||
.list(/\.info$/)
|
||||
.map(app => {
|
||||
var a = s.readJSON(app, 1);
|
||||
return a && (a.type=="app" || a.type=="clock" || !a.type) && {n: a.name, src: a.src};
|
||||
})
|
||||
.filter(Boolean);
|
||||
apps.sort((a, b) => {
|
||||
if (a.n < b.n) return -1;
|
||||
if (a.n > b.n) return 1;
|
||||
return 0;
|
||||
});
|
||||
apps.push({n: "NONE", src: null});
|
||||
|
||||
const settings = s.readJSON("shortcuts.json", 1) || {
|
||||
BTN1: null,
|
||||
BTN3: null
|
||||
};
|
||||
|
||||
function showApps(btn) {
|
||||
function format(v) {
|
||||
return v === settings[btn] ? "*" : "";
|
||||
}
|
||||
|
||||
function onchange(v) {
|
||||
settings[btn] = v;
|
||||
s.writeJSON("shortcuts.json", settings);
|
||||
}
|
||||
|
||||
const btnMenu = {
|
||||
"": {
|
||||
title: `Apps for ${btn}`
|
||||
},
|
||||
"< Back": () => E.showMenu(mainMenu)
|
||||
};
|
||||
|
||||
if (apps.length > 0) {
|
||||
for (let a of apps) {
|
||||
btnMenu[a.n] = {
|
||||
value: a.src,
|
||||
format: format,
|
||||
onchange: onchange
|
||||
};
|
||||
}
|
||||
} else {
|
||||
btnMenu["...No Apps..."] = {
|
||||
value: undefined,
|
||||
format: () => "",
|
||||
onchange: () => {}
|
||||
};
|
||||
}
|
||||
|
||||
E.showMenu(btnMenu);
|
||||
}
|
||||
|
||||
const mainMenu = {
|
||||
"": { title: "Shortcuts Settings" },
|
||||
"< Back": back,
|
||||
"BTN1 app": () => showApps("BTN1"),
|
||||
"BTN3 app": () => showApps("BTN3")
|
||||
};
|
||||
E.showMenu(mainMenu);
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
0.1: New watch face
|
||||
0.2: Use Bangle.setUI for button/launcher handling
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkEIf4AxgMhgUAiIHCmYKCmcgBAUCmQDEgUzkcg+QJBl//AYfzDgX///wAYcCmMzmQXC+c/AYM/kU/iEAgfzkfyAYcCAYM/HIUwC4QxCkEhgcwEYIDDgUwgcSC4QsBLQf/iATEAYcBiB3FC4cvKAIvINAMxgSGDC4UT/4ICC5EDkcjI40/mIHCgaNBC4IDCBAMDmUiXwU/mcQ/8zAYMyQ4M/RYQDBC4yxBIgIDDE4M//5FBAYasBifxf5QA/AC0iAALFDA4ICBgMhC5SBB//wA4gCBUoIXLXYKaBAAUjC54KJC6Ejmcxe4MAiczC4IJBmEjNwILBL4b5Bl7vCD4IEB+cCNgUP+A3EL4MyC4IkBiE/BoMD+cP+MvCoPygfxI4wXBA4UD+QZBh8wgacB/4ODC5YvC+EfC4JVBiAXLgP/n5JBZgcPSwgXIRYPz+cBQoIXBLwgARgU/c4gAQJYJeDF6TXBAH5OMmUBmcQkcgicxBQMTkBbDBIMCSAcTl8jmcxmXymczBQIECCIQEBkbDBAAUfFgMwgPymUDiUg+EjiUwgUhBIMQC4cCfgIXBeAINBicwC4JBBgY7BgcAC4cfkEDkUx+UAmUjBQPxmZZDBIQXDl//kQBC+UvDQIKBmM//4FCBYP/bX4A/ACIA="))
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
const is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"];
|
||||
const locale = require("locale");
|
||||
|
||||
function padNum(n, l) {
|
||||
return ("0".repeat(l)+n).substr(-l);
|
||||
}
|
||||
|
||||
let rects = {};
|
||||
let rectsToClear = {};
|
||||
let commands = [];
|
||||
|
||||
function pushCommand(command) {
|
||||
let hash = E.CRC32(E.toJS(arguments));
|
||||
if (!delete rectsToClear[hash]) {
|
||||
commands.push({hash: hash, command: Function.apply.bind(command, null, arguments.slice(1))});
|
||||
}
|
||||
}
|
||||
|
||||
function executeCommands() {
|
||||
"ram";
|
||||
for (let hash in rectsToClear) delete rects[hash];
|
||||
for (let r of rectsToClear) if (r) g.clearRect(r.x1, r.y1, r.x2, r.y2);
|
||||
g.getModified(true);
|
||||
for (let c of commands) {
|
||||
c.command();
|
||||
rects[c.hash] = g.getModified(true);
|
||||
}
|
||||
rectsToClear = Object.assign({}, rects);
|
||||
commands = [];
|
||||
}
|
||||
|
||||
function drawVectorText(text, size, x, y, alignX, alignY) {
|
||||
g.setFont("Vector", size).setFontAlign(alignX, alignY).drawString(text, x, y);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
g.reset();
|
||||
|
||||
let d = new Date();
|
||||
let hours = is12Hour ? ((d.getHours() + 11) % 12) + 1 : d.getHours();
|
||||
let timeText = `${hours}:${padNum(d.getMinutes(), 2)}`;
|
||||
let meridian = is12Hour ? ((d.getHours() < 12) ? "AM" : "PM") : "";
|
||||
let secondsText = padNum(d.getSeconds(), 2);
|
||||
let dowText = locale.dow(d);
|
||||
let dateText = locale.date(d, true);
|
||||
|
||||
g.setFont("Vector", 256);
|
||||
let timeFontSize = g.getWidth() / ((g.stringWidth(timeText) / 256) + (Math.max(g.stringWidth(meridian), g.stringWidth(secondsText)) / 512 * 9 / 10));
|
||||
let dowFontSize = g.getWidth() / (g.stringWidth(dowText) / 256);
|
||||
let dateFontSize = g.getWidth() / (g.stringWidth(dateText) / 256);
|
||||
|
||||
let timeHeight = g.setFont("Vector", timeFontSize).getFontHeight() * 9 / 10;
|
||||
let dowHeight = g.setFont("Vector", dowFontSize).getFontHeight();
|
||||
let dateHeight = g.setFont("Vector", dateFontSize).getFontHeight();
|
||||
|
||||
let remainingHeight = g.getHeight() - 24 - timeHeight - dowHeight - dateHeight;
|
||||
let spacer = remainingHeight / 4;
|
||||
|
||||
let y = 24 + spacer;
|
||||
|
||||
pushCommand(drawVectorText, timeText, timeFontSize, 0, y, -1, -1);
|
||||
pushCommand(drawVectorText, meridian, timeFontSize*9/20, g.getWidth(), y, 1, -1);
|
||||
pushCommand(drawVectorText, secondsText, timeFontSize*9/20, g.getWidth(), y + timeHeight, 1, 1);
|
||||
y += timeHeight + spacer;
|
||||
|
||||
pushCommand(drawVectorText, dowText, dowFontSize, g.getWidth()/2, y, 0, -1);
|
||||
y += dowHeight + spacer;
|
||||
|
||||
pushCommand(drawVectorText, dateText, dateFontSize, g.getWidth()/2, y, 0, -1);
|
||||
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
let timeout;
|
||||
|
||||
function tick() {
|
||||
draw();
|
||||
timeout = setTimeout(tick, 1000 - getTime() % 1 * 1000);
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = null;
|
||||
if (on) tick();
|
||||
});
|
||||
|
||||
g.clear();
|
||||
tick();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -1,4 +1,6 @@
|
|||
0.02: Make minor adjustments to widget, and discard stale weather data after a configurable period.
|
||||
0.03: Fix flickering last updated time.
|
||||
0.04: Adjust "weather unknown" message according to Bluetooth connection.
|
||||
0.05: Add wind direction.
|
||||
0.05: Add wind direction.
|
||||
0.06: Use setUI for launcher.
|
||||
0.07: Add theme support and unknown icon.
|
||||
|
|
@ -12,12 +12,12 @@
|
|||
function draw() {
|
||||
let w = weather.current;
|
||||
g.reset();
|
||||
g.setColor(0).fillRect(0, 24, 239, 239);
|
||||
g.clearRect(0, 24, 239, 239);
|
||||
|
||||
weather.drawIcon(w.txt, 65, 90, 55);
|
||||
const locale = require("locale");
|
||||
|
||||
g.setColor(-1);
|
||||
g.reset();
|
||||
|
||||
const temp = locale.temp(w.temp-273.15).match(/^(\D*\d*)(.*)$/);
|
||||
let width = g.setFont("Vector", 40).stringWidth(temp[1]);
|
||||
|
|
@ -54,8 +54,8 @@
|
|||
if (!weather.current || !weather.current.time) return;
|
||||
let text = `Last update received ${formatDuration(Date.now() - weather.current.time)} ago`;
|
||||
g.reset();
|
||||
g.setColor(0).fillRect(0, 202, 239, 210);
|
||||
g.setColor(-1).setFont("6x8", 1).setFontAlign(0, 0, 0);
|
||||
g.clearRect(0, 202, 239, 210);
|
||||
g.setFont("6x8", 1).setFontAlign(0, 0, 0);
|
||||
g.drawString(text, 120, 206);
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
update();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
setWatch(Bangle.showLauncher, BTN2, {repeat: false, edge: 'falling'});
|
||||
Bangle.setUI("clock");
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
|
|
|||
|
|
@ -68,13 +68,13 @@ setCurrentWeather(storage.readJSON('weather.json')||{});
|
|||
|
||||
exports.drawIcon = function(cond, x, y, r) {
|
||||
function drawSun(x, y, r) {
|
||||
g.setColor("#FF7700");
|
||||
g.setColor(g.theme.dark ? "#FE0" : "#FC0");
|
||||
g.fillCircle(x, y, r);
|
||||
}
|
||||
|
||||
function drawCloud(x, y, r, c) {
|
||||
const u = r/12;
|
||||
if (c==null) c = "#EEEEEE";
|
||||
if (c==null) c = g.theme.dark ? "#BBB" : "#AAA";
|
||||
g.setColor(c);
|
||||
g.fillCircle(x-8*u, y+3*u, 4*u);
|
||||
g.fillCircle(x-4*u, y-2*u, 5*u);
|
||||
|
|
@ -91,7 +91,7 @@ exports.drawIcon = function(cond, x, y, r) {
|
|||
}
|
||||
|
||||
function drawBrokenClouds(x, y, r) {
|
||||
drawCloud(x+1/8*r, y-1/8*r, 7/8*r, "#777777");
|
||||
drawCloud(x+1/8*r, y-1/8*r, 7/8*r, "#777");
|
||||
drawCloud(x-1/8*r, y+1/8*r, 7/8*r);
|
||||
}
|
||||
|
||||
|
|
@ -101,24 +101,25 @@ exports.drawIcon = function(cond, x, y, r) {
|
|||
}
|
||||
|
||||
function drawRainLines(x, y, r) {
|
||||
g.setColor("#FFFFFF");
|
||||
g.setColor(g.theme.dark ? "#0CF" : "#07F");
|
||||
const y1 = y+1/2*r;
|
||||
const y2 = y+1*r;
|
||||
g.fillPoly([
|
||||
x-6/12*r+1, y1,
|
||||
x-8/12*r+1, y2,
|
||||
|
||||
g.fillPolyAA([
|
||||
x-6/12*r, y1,
|
||||
x-8/12*r, y2,
|
||||
x-7/12*r, y2,
|
||||
x-5/12*r, y1,
|
||||
]);
|
||||
g.fillPoly([
|
||||
x-2/12*r+1, y1,
|
||||
x-4/12*r+1, y2,
|
||||
g.fillPolyAA([
|
||||
x-2/12*r, y1,
|
||||
x-4/12*r, y2,
|
||||
x-3/12*r, y2,
|
||||
x-1/12*r, y1,
|
||||
]);
|
||||
g.fillPoly([
|
||||
x+2/12*r+1, y1,
|
||||
x+0/12*r+1, y2,
|
||||
g.fillPolyAA([
|
||||
x+2/12*r, y1,
|
||||
x+0/12*r, y2,
|
||||
x+1/12*r, y2,
|
||||
x+3/12*r, y1,
|
||||
]);
|
||||
|
|
@ -136,7 +137,7 @@ exports.drawIcon = function(cond, x, y, r) {
|
|||
|
||||
function drawThunderstorm(x, y, r) {
|
||||
function drawLightning(x, y, r) {
|
||||
g.setColor("#FF7700");
|
||||
g.setColor(g.theme.dark ? "#FE0" : "#FC0");
|
||||
g.fillPoly([
|
||||
x-2/6*r, y-r,
|
||||
x-4/6*r, y+1/6*r,
|
||||
|
|
@ -164,7 +165,7 @@ exports.drawIcon = function(cond, x, y, r) {
|
|||
}
|
||||
}
|
||||
|
||||
g.setColor("#FFFFFF");
|
||||
g.setColor(g.theme.dark ? "#FFF" : "#CCC");
|
||||
const w = 1/12*r;
|
||||
for(let i = 0; i<=6; ++i) {
|
||||
const points = [
|
||||
|
|
@ -199,7 +200,7 @@ exports.drawIcon = function(cond, x, y, r) {
|
|||
[-0.2, 0.3],
|
||||
];
|
||||
|
||||
g.setColor("#FFFFFF");
|
||||
g.setColor(g.theme.dark ? "#FFF" : "#CCC");
|
||||
for(let i = 0; i<5; ++i) {
|
||||
g.fillRect(x+layers[i][0]*r, y+(0.4*i-0.9)*r, x+layers[i][1]*r,
|
||||
y+(0.4*i-0.7)*r-1);
|
||||
|
|
@ -208,6 +209,11 @@ exports.drawIcon = function(cond, x, y, r) {
|
|||
}
|
||||
}
|
||||
|
||||
function drawUnknown(x, y, r) {
|
||||
drawCloud(x, y, r, "#777");
|
||||
g.setColor(g.theme.fg).setFontAlign(0, 0).setFont('Vector', r*2).drawString("?", x+r/10, y+r/6);
|
||||
}
|
||||
|
||||
function chooseIcon(condition) {
|
||||
if (!condition) return () => {};
|
||||
condition = condition.toLowerCase();
|
||||
|
|
@ -225,7 +231,18 @@ exports.drawIcon = function(cond, x, y, r) {
|
|||
if (condition.includes("few clouds")) return drawFewClouds;
|
||||
if (condition.includes("scattered clouds")) return drawCloud;
|
||||
if (condition.includes("clouds")) return drawBrokenClouds;
|
||||
return drawMist;
|
||||
if (condition.includes("mist") ||
|
||||
condition.includes("smoke") ||
|
||||
condition.includes("haze") ||
|
||||
condition.includes("sand") ||
|
||||
condition.includes("dust") ||
|
||||
condition.includes("fog") ||
|
||||
condition.includes("ash") ||
|
||||
condition.includes("squalls") ||
|
||||
condition.includes("tornado")) {
|
||||
return drawMist;
|
||||
}
|
||||
return drawUnknown;
|
||||
}
|
||||
|
||||
chooseIcon(cond)(x, y, r);
|
||||
|
|
|
|||
|
|
@ -5,16 +5,16 @@
|
|||
const w = weather.current;
|
||||
if (!w) return;
|
||||
g.reset();
|
||||
g.setColor(0).fillRect(this.x, this.y, this.x+this.width-1, this.y+23);
|
||||
g.clearRect(this.x, this.y, this.x+this.width-1, this.y+23);
|
||||
if (w.txt) {
|
||||
weather.drawIcon(w.txt, this.x+10, this.y+8, 7.5);
|
||||
}
|
||||
if (w.temp) {
|
||||
let t = require('locale').temp(w.temp-273.15); // applies conversion
|
||||
t = t.match(/[\d\-]*/)[0]; // but we have no room for units
|
||||
g.reset();
|
||||
g.setFontAlign(0, 1); // center horizontally at bottom of widget
|
||||
g.setFont('6x8', 1);
|
||||
g.setColor(-1);
|
||||
g.drawString(t, this.x+10, this.y+24);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@
|
|||
BTN2 now goes to menu on release
|
||||
0.10: Tweaks to reduce memory usage
|
||||
0.11: Fix initial screen fill colour
|
||||
0.12: Fix swipe direction (#800)
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ function move(dir) {
|
|||
|
||||
|
||||
|
||||
Bangle.on('swipe',move);
|
||||
Bangle.on('swipe', dir => move(-dir));
|
||||
setWatch(()=>move(1), BTN3, {repeat:true});
|
||||
setWatch(()=>{
|
||||
// If we're on the last page
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@
|
|||
0.09: Fix regression stopping correct widget updates
|
||||
0.10: Add 'hide if charge greater than'
|
||||
0.11: Don't overwrite existing settings on app update
|
||||
0.12: Fixed for Bangle 2
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# Battery Widget
|
||||
|
||||
Show the current battery level and charging status in the top right of the clock, with charge percentage
|
||||
|
||||
Works with Bangle 1 and Bangle 2
|
||||
|
||||

|
||||
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
|
@ -1,10 +1,23 @@
|
|||
(function(){
|
||||
const COLORS = {
|
||||
'white': -1,
|
||||
'charging': 0x07E0, // "Green"
|
||||
'high': 0x05E0, // slightly darker green
|
||||
'ok': 0xFD20, // "Orange"
|
||||
'low':0xF800, // "Red"
|
||||
let COLORS = {};
|
||||
|
||||
if (process.env.HWVERSION == 1) {
|
||||
COLORS = {
|
||||
'white': -1, // White
|
||||
'charging': 0x07E0, // Green
|
||||
'high': 0x05E0, // lightly darker green
|
||||
'ok': 0xFD20, // Orange
|
||||
'low': 0xF800, // Red
|
||||
};
|
||||
} else {
|
||||
// bangle 2 is only 7 bit colors
|
||||
COLORS = {
|
||||
'white': "#fff", // White
|
||||
'charging': "#0f0", // Green
|
||||
'high': "#0f0", // Green
|
||||
'ok': "#ff0", // Orange
|
||||
'low': "#f00", // Red
|
||||
};
|
||||
}
|
||||
const SETTINGS_FILE = 'widbatpc.json'
|
||||
|
||||
|
|
@ -66,29 +79,28 @@
|
|||
// else...
|
||||
var s = 39;
|
||||
var x = this.x, y = this.y;
|
||||
const l = E.getBattery(),
|
||||
c = levelColor(l);
|
||||
const l = E.getBattery();
|
||||
const xl = x+4+l*(s-12)/100
|
||||
|
||||
c = levelColor(l);
|
||||
|
||||
if (Bangle.isCharging() && setting('charger')) {
|
||||
g.setColor(chargerColor()).drawImage(atob(
|
||||
"DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"),x,y);
|
||||
x+=16;
|
||||
}
|
||||
g.setColor(-1);
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(x,y+2,x+s-4,y+21);
|
||||
g.clearRect(x+2,y+4,x+s-6,y+19);
|
||||
g.fillRect(x+s-3,y+10,x+s,y+14);
|
||||
|
||||
g.setColor(c).fillRect(x+4,y+6,xl,y+17);
|
||||
g.setColor(-1);
|
||||
g.setColor(g.theme.fg);
|
||||
if (!setting('percentage')) {
|
||||
return;
|
||||
}
|
||||
let gfx = g
|
||||
if (setting('color') === 'Monochrome') {
|
||||
// draw text inverted on battery level
|
||||
gfx = Graphics.createCallback(240, 240, 1,
|
||||
gfx = Graphics.createCallback(g.getWidth(),g.getHeight(), 1,
|
||||
(x,y) => {g.setPixel(x,y,x<=xl?0:-1)})
|
||||
}
|
||||
gfx.setFontAlign(-1,-1);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
0.01: Fork of widclk v0.04 github.com/espruino/BangleApps/tree/master/apps/widclk
|
||||
0.02: Modification for bottom widget area and text color
|
||||
0.03: based in widclk v0.05 compatible at same time, bottom area and color
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# Digital clock widget (bottom widget area)
|
||||
This very basic widget clock allows to test the unfrequently used widget bottom area.
|
||||
|
||||
forked from
|
||||
https://github.com/espruino/BangleApps/tree/master/apps/widclk
|
||||
|
||||
## Photo
|
||||
|
||||
Example of usage
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Upload the widget file
|
||||
Open an app that supports displaying widgets
|
||||
|
||||
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
This app is so basic that probably the easiest is to just edit the code ;)
|
||||
|
||||
Otherwise you can contact me [here](https://github.com/dapgo)
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 660 B |
|
|
@ -0,0 +1,31 @@
|
|||
(function() {
|
||||
// don't show widget if we know we have a clock app running
|
||||
if (Bangle.CLOCK) return;
|
||||
|
||||
let intervalRef = null;
|
||||
var width = 5 * 6*2;
|
||||
var text_color=0x07FF;//cyan
|
||||
|
||||
function draw() {
|
||||
g.reset().setFont("6x8", 2).setFontAlign(-1, 0).setColor(text_color);
|
||||
var time = require("locale").time(new Date(),1);
|
||||
g.drawString(time, this.x, this.y+11, true); // 5 * 6*2 = 60
|
||||
}
|
||||
function clearTimers(){
|
||||
if(intervalRef) {
|
||||
clearInterval(intervalRef);
|
||||
intervalRef = null;
|
||||
}
|
||||
}
|
||||
function startTimers(){
|
||||
intervalRef = setInterval(()=>WIDGETS["wdclkbttm"].draw(), 60*1000);
|
||||
WIDGETS["wdclkbttm"].draw();
|
||||
}
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
clearTimers();
|
||||
if (on) startTimers();
|
||||
});
|
||||
|
||||
WIDGETS["wdclkbttm"]={area:"br",width:width,draw:draw};
|
||||
if (Bangle.isLCDOn) intervalRef = setInterval(()=>WIDGETS["wdclkbttm"].draw(), 60*1000);
|
||||
})()
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New Widget!
|
||||
0.02: Tweaks for variable size widget system
|
||||
0.03: Ensure redrawing works with variable size widget system
|
||||
0.04: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
|
||||
|
|
|
|||
|
|
@ -26,12 +26,12 @@
|
|||
// redraw when the LCD turns on
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (on) {
|
||||
Bangle.setHRMPower(1);
|
||||
Bangle.setHRMPower(1,"widhrm");
|
||||
firstBPM = true;
|
||||
currentBPM = undefined;
|
||||
WIDGETS["hrm"].draw();
|
||||
} else {
|
||||
Bangle.setHRMPower(0);
|
||||
Bangle.setHRMPower(0,"widhrm");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
}
|
||||
WIDGETS["hrm"].draw();
|
||||
});
|
||||
Bangle.setHRMPower(Bangle.isLCDOn());
|
||||
Bangle.setHRMPower(Bangle.isLCDOn(),"widhrm");
|
||||
|
||||
// add your widget
|
||||
WIDGETS["hrm"]={area:"tl",width:24,draw:draw};
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@
|
|||
0.04: Only buzz on high confidence (>85%)
|
||||
0.05: Improved buzz timing and rendering
|
||||
0.06: Removed debug outputs, fixed rendering for upper limit, improved rendering for +/- icons, changelog version order fixed
|
||||
0.07: Home button fixed and README added
|
||||
0.07: Home button fixed and README added
|
||||
0.08: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const Setter = {
|
|||
UPPER: 'upper',
|
||||
LOWER: 'lower'
|
||||
};
|
||||
|
||||
|
||||
const shortBuzzTimeInMs = 80;
|
||||
const longBuzzTimeInMs = 400;
|
||||
|
||||
|
|
@ -164,12 +164,12 @@ function renderPlusMinusIcons() {
|
|||
} else {
|
||||
g.setColor(1, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
g.setFontVector(14);
|
||||
|
||||
//+ for Btn1
|
||||
g.drawString("+", 222, 50);
|
||||
|
||||
|
||||
//- for Btn3
|
||||
g.drawString("-", 222,165);
|
||||
|
||||
|
|
@ -288,7 +288,7 @@ function resetHighlightTimeout() {
|
|||
}
|
||||
|
||||
function switchOffApp(){
|
||||
Bangle.setHRMPower(0);
|
||||
Bangle.setHRMPower(0,"wohrm");
|
||||
Bangle.showLauncher();
|
||||
}
|
||||
|
||||
|
|
@ -306,7 +306,7 @@ Bangle.on('lcdPower', (on) => {
|
|||
}
|
||||
});
|
||||
|
||||
Bangle.setHRMPower(1);
|
||||
Bangle.setHRMPower(1,"wohrm");
|
||||
Bangle.on('HRM', onHrm);
|
||||
|
||||
setWatch(incrementLimit, BTN1, {edge:"rising", debounce:50, repeat:true});
|
||||
|
|
|
|||
2
core
|
|
@ -1 +1 @@
|
|||
Subproject commit 39f89acf1468dc7d58ac54d15a092b2bd6d73cce
|
||||
Subproject commit 2aac601e38d659876eb7db5aebc7a12dd3c39da7
|
||||
|
|
@ -11,7 +11,7 @@ if (window.location.host=="banglejs.com") {
|
|||
'This is not the official Bangle.js App Loader - you can try the <a href="https://banglejs.com/apps/">Official Version</a> here.';
|
||||
}
|
||||
|
||||
var RECOMMENDED_VERSION = "2v09";
|
||||
var RECOMMENDED_VERSION = "2v10";
|
||||
// could check http://www.espruino.com/json/BANGLEJS.json for this
|
||||
|
||||
(function() {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
|
||||
/*
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
var Layout = require("Layout");
|
||||
var layout = new Layout( layoutObject, btns )
|
||||
var layout = new Layout( layoutObject, btns, options )
|
||||
layout.render(optionalObject);
|
||||
```
|
||||
|
||||
|
|
@ -42,8 +41,8 @@ layoutObject has:
|
|||
* A `halign` field to set horizontal alignment. `-1`=left, `1`=right, `0`=center
|
||||
* A `valign` field to set vertical alignment. `-1`=top, `1`=bottom, `0`=center
|
||||
* A `pad` integer field to set pixels padding
|
||||
* A `fillx` boolean to choose if the object should fill available space in x
|
||||
* A `filly` boolean to choose if the object should fill available space in y
|
||||
* 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
|
||||
|
||||
btns is an array of objects containing:
|
||||
|
|
@ -52,6 +51,13 @@ btns is an array of objects containing:
|
|||
* `cb` - a callback function
|
||||
* `cbl` - a callback function for long presses
|
||||
|
||||
options is an object containing:
|
||||
|
||||
* `lazy` - a boolean specifying whether to enable automatic lazy rendering
|
||||
|
||||
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:
|
||||
|
||||
|
|
@ -69,17 +75,20 @@ Other functions:
|
|||
*/
|
||||
|
||||
|
||||
function Layout(layout, buttons) {
|
||||
function Layout(layout, buttons, options) {
|
||||
this._l = this.l = layout;
|
||||
this.b = buttons;
|
||||
// Do we have >1 physical buttons?
|
||||
this.physBtns = (process.env.HWVERSION==2) ? 1 : 3;
|
||||
this.yOffset = Object.keys(global.WIDGETS).length ? 24 : 0;
|
||||
|
||||
if (buttons) {
|
||||
options = options || {};
|
||||
this.lazy = options.lazy || false;
|
||||
|
||||
if (buttons) {
|
||||
if (this.physBtns >= buttons.length) {
|
||||
// enough physical buttons
|
||||
var btnHeight = Math.floor((g.getHeight()-this.yOffset) / this.physBtns);
|
||||
let btnHeight = Math.floor((g.getHeight()-this.yOffset) / this.physBtns);
|
||||
if (Bangle.btnWatch) Bangle.btnWatch.forEach(clearWatch);
|
||||
Bangle.btnWatch = [];
|
||||
if (this.physBtns > 2 && buttons.length==1)
|
||||
|
|
@ -95,7 +104,7 @@ function Layout(layout, buttons) {
|
|||
{type:"v", pad:1, filly:1, c: buttons.map(b=>(b.type="txt",b.font="6x8",b.height=btnHeight,b.r=1,b))}
|
||||
]};
|
||||
} else {
|
||||
var btnHeight = Math.floor((g.getHeight()-this.yOffset) / buttons.length);
|
||||
let btnHeight = Math.floor((g.getHeight()-this.yOffset) / buttons.length);
|
||||
this._l.width = g.getWidth()-20; // button width
|
||||
this._l = {type:"h", c: [
|
||||
this._l,
|
||||
|
|
@ -104,7 +113,7 @@ function Layout(layout, buttons) {
|
|||
}
|
||||
}
|
||||
if (process.env.HWVERSION==2) {
|
||||
Bangle.touchHandler = (_,e) => touchHandler(layout,e);
|
||||
Bangle.touchHandler = function(_,e){touchHandler(layout,e)};
|
||||
Bangle.on('touch',Bangle.touchHandler);
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +121,7 @@ function Layout(layout, buttons) {
|
|||
var ll = this;
|
||||
function idRecurser(l) {
|
||||
if (l.id) ll[l.id] = l;
|
||||
if (!l.type) l.type="";
|
||||
if (l.c) l.c.forEach(idRecurser);
|
||||
}
|
||||
idRecurser(layout);
|
||||
|
|
@ -144,79 +154,41 @@ function touchHandler(l,e) {
|
|||
if (l.c) l.c.forEach(n => touchHandler(n,e));
|
||||
}
|
||||
|
||||
function prepareLazyRender(l, rectsToClear, drawList, rects, bgCol) {
|
||||
if ((l.bgCol != null && l.bgCol != bgCol) || l.type == "txt" || l.type == "btn" || l.type == "img" || l.type == "custom") {
|
||||
// Hash the layoutObject without including its children
|
||||
let c = l.c;
|
||||
delete l.c;
|
||||
let hash = "H"+E.CRC32(E.toJS(l)); // String keys maintain insertion order
|
||||
if (c) l.c = c;
|
||||
|
||||
function updateMin(l) {
|
||||
switch (l.type) {
|
||||
case "txt": {
|
||||
if (l.font.endsWith("%"))
|
||||
l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100);
|
||||
// FIXME ':'/fsz not needed in new firmwares - it's handled internally
|
||||
if (l.font.includes(":")) {
|
||||
var f = l.font.split(":");
|
||||
l.font = f[0];
|
||||
l.fsz = f[1];
|
||||
if (!delete rectsToClear[hash]) {
|
||||
rects[hash] = {bg: bgCol, r: [l.x,l.y,l.x+l.w-1,l.y+l.h-1]};
|
||||
if (drawList) {
|
||||
drawList.push(l);
|
||||
drawList = null; // Prevent children from being redundantly added to the drawList
|
||||
}
|
||||
g.setFont(l.font,l.fsz);
|
||||
l._h = g.getFontHeight();
|
||||
l._w = g.stringWidth(l.label);
|
||||
break;
|
||||
}
|
||||
case "btn": {
|
||||
l._h = 24;
|
||||
l._w = 14 + l.label.length*8;
|
||||
break;
|
||||
}
|
||||
case "img": {
|
||||
var src = l.src();
|
||||
if (typeof(src) === 'object') {
|
||||
l._h = ("width" in src) ? src.width : src.getWidth();
|
||||
l._w = ("height" in src) ? src.height : src.getHeight();
|
||||
} else {
|
||||
var im = E.toString(src);
|
||||
l._h = im.charCodeAt(0);
|
||||
l._w = im.charCodeAt(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case undefined:
|
||||
case "custom": {
|
||||
// size should already be set up in width/height
|
||||
l._w = 0;
|
||||
l._h = 0;
|
||||
break;
|
||||
}
|
||||
case "h": {
|
||||
l.c.forEach(updateMin);
|
||||
l._h = l.c.reduce((a,b)=>Math.max(a,b._h+(b.pad<<1)),0);
|
||||
l._w = l.c.reduce((a,b)=>a+b._w+(b.pad<<1),0);
|
||||
l.fillx |= l.c.some(c=>c.fillx);
|
||||
break;
|
||||
}
|
||||
case "v": {
|
||||
l.c.forEach(updateMin);
|
||||
l._h = l.c.reduce((a,b)=>a+b._h+(b.pad<<1),0);
|
||||
l._w = l.c.reduce((a,b)=>Math.max(a,b._w+(b.pad<<1)),0);
|
||||
l.filly |= l.c.some(c=>c.filly);
|
||||
break;
|
||||
}
|
||||
default: throw "Unknown item type "+l.type;
|
||||
}
|
||||
if (l.r&1) { // rotation
|
||||
var t = l._w;l._w=l._h;l._h=t;
|
||||
}
|
||||
l._w = Math.max(l._w, 0|l.width);
|
||||
l._h = Math.max(l._h, 0|l.height);
|
||||
|
||||
if (l.c) for (let ch of l.c) prepareLazyRender(ch, rectsToClear, drawList, rects, l.bgCol == null ? bgCol : l.bgCol);
|
||||
}
|
||||
function render(l) {
|
||||
if (!l) l = this.l;
|
||||
g.reset();
|
||||
if (l.col) g.setColor(l.col);
|
||||
if (l.bgCol!==undefined) g.setBgColor(l.bgCol).clearRect(l.x,l.y,l.x+l.w,l.y+l.h);
|
||||
switch (l.type) {
|
||||
case "txt":
|
||||
g.setFont(l.font,l.fsz).setFontAlign(0,0,l.r).drawString(l.label, l.x+(l.w>>1), l.y+(l.h>>1), true/*solid bg*/);
|
||||
break;
|
||||
case "btn":
|
||||
|
||||
Layout.prototype.render = function (l) {
|
||||
if (!l) l = this._l;
|
||||
|
||||
function render(l) {"ram"
|
||||
g.reset();
|
||||
if (l.col) g.setColor(l.col);
|
||||
if (l.bgCol!==undefined) g.setBgColor(l.bgCol).clearRect(l.x,l.y,l.x+l.w-1,l.y+l.h-1);
|
||||
cb[l.type](l);
|
||||
}
|
||||
|
||||
var cb = {
|
||||
"":function(){},
|
||||
"txt":function(l){
|
||||
g.setFont(l.font,l.fsz).setFontAlign(0,0,l.r).drawString(l.label, l.x+(l.w>>1), l.y+(l.h>>1));
|
||||
}, "btn":function(l){
|
||||
var poly = [
|
||||
l.x,l.y+4,
|
||||
l.x+4,l.y,
|
||||
|
|
@ -229,33 +201,41 @@ function render(l) {
|
|||
l.x,l.y+4
|
||||
];
|
||||
g.setColor(g.theme.bgH).fillPoly(poly).setColor(l.selected ? g.theme.fgH : g.theme.fg).drawPoly(poly).setFont("4x6",2).setFontAlign(0,0,l.r).drawString(l.label,l.x+l.w/2,l.y+l.h/2);
|
||||
break;
|
||||
case "img":
|
||||
}, "img":function(l){
|
||||
g.drawImage(l.src(), l.x, l.y);
|
||||
break;
|
||||
case "custom":
|
||||
}, "custom":function(l){
|
||||
l.render(l);
|
||||
break;
|
||||
}
|
||||
if (l.c) l.c.forEach(render);
|
||||
}
|
||||
},"h":function(l) { l.c.forEach(render); },
|
||||
"v":function(l) { l.c.forEach(render); }
|
||||
};
|
||||
|
||||
Layout.prototype.render = function (l) {
|
||||
if (!l) l = this._l;
|
||||
render(l);
|
||||
if (this.lazy) {
|
||||
// we have to use 'var' here not 'let', otherwise the minifier
|
||||
// renames vars to the same name, which causes problems as Espruino
|
||||
// doesn't yet honour the scoping of 'let'
|
||||
if (!this.rects) this.rects = {};
|
||||
var rectsToClear = this.rects.clone();
|
||||
var drawList = [];
|
||||
prepareLazyRender(l, rectsToClear, drawList, this.rects, g.getBgColor());
|
||||
for (var h in rectsToClear) delete this.rects[h];
|
||||
var clearList = Object.keys(rectsToClear).map(k=>rectsToClear[k]).reverse(); // Rects are cleared in reverse order so that the original bg color is restored
|
||||
for (var r of clearList) g.setBgColor(r.bg).clearRect.apply(g, r.r);
|
||||
drawList.forEach(render);
|
||||
} else { // non-lazy
|
||||
render(l);
|
||||
}
|
||||
};
|
||||
|
||||
Layout.prototype.layout = function (l) {
|
||||
// l = current layout element
|
||||
// exw,exh = extra width/height available
|
||||
var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
|
||||
var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
|
||||
switch (l.type) {
|
||||
case "h": {
|
||||
let x = l.x + (l.w-l._w)/2;
|
||||
var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
|
||||
if (fillx) { x = l.x; }
|
||||
l.c.forEach(c => {
|
||||
c.w = c._w + (c.fillx?(l.w-l._w)/fillx:0);
|
||||
c.w = c._w + ((0|c.fillx)*(l.w-l._w)/(fillx||1));
|
||||
c.h = c.filly ? l.h : c._h;
|
||||
c.x = x;
|
||||
c.y = l.y + (1+(0|c.valign))*(l.h-c.h)/2;
|
||||
|
|
@ -265,18 +245,17 @@ Layout.prototype.layout = function (l) {
|
|||
c.w += c.pad*2;
|
||||
c.h += c.pad*2;
|
||||
}
|
||||
if (c.c) {
|
||||
this.layout(c);
|
||||
}
|
||||
if (c.c) this.layout(c);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "v": {
|
||||
let y = l.y + (l.h-l._h)/2;
|
||||
var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
|
||||
if (filly) { y = l.y; }
|
||||
l.c.forEach(c => {
|
||||
c.w = c.fillx ? l.w : c._w;
|
||||
c.h = c._h + (c.filly?(l.h-l._h)/filly:0);
|
||||
c.h = c._h + ((0|c.filly)*(l.h-l._h)/(filly||1));
|
||||
c.x = l.x + (1+(0|c.halign))*(l.w-c.w)/2;
|
||||
c.y = y;
|
||||
y += c.h;
|
||||
|
|
@ -304,6 +283,62 @@ Layout.prototype.update = function() {
|
|||
var y = this.yOffset;
|
||||
var h = g.getHeight()-y;
|
||||
// update sizes
|
||||
function updateMin(l) {"ram"
|
||||
cb[l.type](l);
|
||||
if (l.r&1) { // rotation
|
||||
var t = l._w;l._w=l._h;l._h=t;
|
||||
}
|
||||
l._w = Math.max(l._w, 0|l.width);
|
||||
l._h = Math.max(l._h, 0|l.height);
|
||||
}
|
||||
var cb = {
|
||||
"txt" : function(l) {
|
||||
if (l.font.endsWith("%"))
|
||||
l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100);
|
||||
// FIXME ':'/fsz not needed in new firmwares - it's handled internally
|
||||
if (l.font.includes(":")) {
|
||||
var f = l.font.split(":");
|
||||
l.font = f[0];
|
||||
l.fsz = f[1];
|
||||
}
|
||||
g.setFont(l.font,l.fsz);
|
||||
l._h = g.getFontHeight();
|
||||
l._w = g.stringWidth(l.label);
|
||||
}, "btn": function(l) {
|
||||
l._h = 24;
|
||||
l._w = 14 + l.label.length*8;
|
||||
}, "img": function(l) {
|
||||
var src = l.src();
|
||||
if (typeof(src) === 'object') {
|
||||
l._h = ("width" in src) ? src.width : src.getWidth();
|
||||
l._w = ("height" in src) ? src.height : src.getHeight();
|
||||
} else {
|
||||
var im = E.toString(src);
|
||||
l._h = im.charCodeAt(0);
|
||||
l._w = im.charCodeAt(1);
|
||||
}
|
||||
}, "": function(l) {
|
||||
// size should already be set up in width/height
|
||||
l._w = 0;
|
||||
l._h = 0;
|
||||
}, "custom": function(l) {
|
||||
// size should already be set up in width/height
|
||||
l._w = 0;
|
||||
l._h = 0;
|
||||
}, "h": function(l) {
|
||||
l.c.forEach(updateMin);
|
||||
l._h = l.c.reduce((a,b)=>Math.max(a,b._h+(b.pad<<1)),0);
|
||||
l._w = l.c.reduce((a,b)=>a+b._w+(b.pad<<1),0);
|
||||
if (l.c.some(c=>c.fillx)) l.fillx = 1;
|
||||
if (l.c.some(c=>c.filly)) l.filly = 1;
|
||||
}, "v": function(l) {
|
||||
l.c.forEach(updateMin);
|
||||
l._h = l.c.reduce((a,b)=>a+b._h+(b.pad<<1),0);
|
||||
l._w = l.c.reduce((a,b)=>Math.max(a,b._w+(b.pad<<1)),0);
|
||||
if (l.c.some(c=>c.fillx)) l.fillx = 1;
|
||||
if (l.c.some(c=>c.filly)) l.filly = 1;
|
||||
}
|
||||
};
|
||||
updateMin(l);
|
||||
// center
|
||||
if (l.fillx || l.filly) {
|
||||
|
|
|
|||
|
|
@ -1,325 +0,0 @@
|
|||
|
||||
/*
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
var Layout = require("Layout");
|
||||
var layout = new Layout( layoutObject, btns )
|
||||
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` and `r` for text rotation. 'font' is required
|
||||
* `"btn"` - a button, with value `label` and callback `cb`
|
||||
* `"img"` - an image where the function `src` 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"` - Veritical 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
|
||||
* 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. `-1`=left, `1`=right, `0`=center
|
||||
* A `valign` field to set vertical alignment. `-1`=top, `1`=bottom, `0`=center
|
||||
* A `pad` integer field to set pixels padding
|
||||
* A `fillx` boolean to choose if the object should fill available space in x
|
||||
* A `filly` boolean to choose if the object should fill available space in y
|
||||
* `width` and `height` fields to optionally specify minimum size
|
||||
|
||||
btns is an array of objects containing:
|
||||
|
||||
* `label` - the text on the button
|
||||
* `cb` - a callback function
|
||||
* `cbl` - a callback function for long presses
|
||||
|
||||
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)
|
||||
|
||||
*/
|
||||
|
||||
|
||||
function Layout(layout, buttons) {
|
||||
this._l = this.l = layout;
|
||||
this.b = buttons;
|
||||
// Do we have >1 physical buttons?
|
||||
this.physBtns = (process.env.HWVERSION==2) ? 1 : 3;
|
||||
this.yOffset = Object.keys(global.WIDGETS).length ? 24 : 0;
|
||||
|
||||
if (buttons) {
|
||||
if (this.physBtns >= buttons.length) {
|
||||
// enough physical buttons
|
||||
var btnHeight = Math.floor((g.getHeight()-this.yOffset) / this.physBtns);
|
||||
if (Bangle.btnWatch) Bangle.btnWatch.forEach(clearWatch);
|
||||
Bangle.btnWatch = [];
|
||||
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.btnWatch.push(setWatch(pressHandler.bind(this,0), BTN1, {repeat:true,edge:-1}));
|
||||
if (buttons[1]) Bangle.btnWatch.push(setWatch(pressHandler.bind(this,1), BTN2, {repeat:true,edge:-1}));
|
||||
if (buttons[2]) Bangle.btnWatch.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,
|
||||
{type:"v", pad:1, filly:1, c: buttons.map(b=>(b.type="txt",b.font="6x8",b.height=btnHeight,b.r=1,b))}
|
||||
]};
|
||||
} else {
|
||||
var btnHeight = Math.floor((g.getHeight()-this.yOffset) / buttons.length);
|
||||
this._l.width = g.getWidth()-20; // button width
|
||||
this._l = {type:"h", c: [
|
||||
this._l,
|
||||
{type:"v", c: buttons.map(b=>(b.type="btn",b.h=btnHeight,b.w=32,b.r=1,b))}
|
||||
]};
|
||||
}
|
||||
}
|
||||
if (process.env.HWVERSION==2) {
|
||||
Bangle.touchHandler = (_,e) => touchHandler(layout,e);
|
||||
Bangle.on('touch',Bangle.touchHandler);
|
||||
}
|
||||
|
||||
// add IDs
|
||||
var ll = this;
|
||||
function idRecurser(l) {
|
||||
if (l.id) ll[l.id] = l;
|
||||
if (l.c) l.c.forEach(idRecurser);
|
||||
}
|
||||
idRecurser(layout);
|
||||
this.update();
|
||||
}
|
||||
|
||||
Layout.prototype.remove = function (l) {
|
||||
if (Bangle.btnWatch) {
|
||||
Bangle.btnWatch.forEach(clearWatch);
|
||||
delete Bangle.btnWatch;
|
||||
}
|
||||
if (Bangle.touchHandler) {
|
||||
Bangle.removeListener("touch",Bangle.touchHandler);
|
||||
delete Bangle.touchHandler;
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Handler for touch events
|
||||
function touchHandler(l,e) {
|
||||
if (l.type=="btn" && l.cb && e.x>=l.x && e.y>=l.y && e.x<=l.x+l.w && e.y<=l.y+l.h)
|
||||
l.cb(e);
|
||||
if (l.c) l.c.forEach(n => touchHandler(n,e));
|
||||
}
|
||||
|
||||
|
||||
function updateMin(l) {
|
||||
switch (l.type) {
|
||||
case "txt": {
|
||||
if (l.font.endsWith("%"))
|
||||
l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100);
|
||||
// FIXME ':'/fsz not needed in new firmwares - it's handled internally
|
||||
if (l.font.includes(":")) {
|
||||
var f = l.font.split(":");
|
||||
l.font = f[0];
|
||||
l.fsz = f[1];
|
||||
}
|
||||
g.setFont(l.font,l.fsz);
|
||||
l._h = g.getFontHeight();
|
||||
l._w = g.stringWidth(l.label);
|
||||
break;
|
||||
}
|
||||
case "btn": {
|
||||
l._h = 24;
|
||||
l._w = 14 + l.label.length*8;
|
||||
break;
|
||||
}
|
||||
case "img": {
|
||||
var im = E.toString(l.src());
|
||||
l._h = im.charCodeAt(0);
|
||||
l._w = im.charCodeAt(1);
|
||||
break;
|
||||
}
|
||||
case undefined:
|
||||
case "custom": {
|
||||
// size should already be set up in width/height
|
||||
l._w = 0;
|
||||
l._h = 0;
|
||||
break;
|
||||
}
|
||||
case "h": {
|
||||
l.c.forEach(updateMin);
|
||||
l._h = l.c.reduce((a,b)=>Math.max(a,b._h+(b.pad<<1)),0);
|
||||
l._w = l.c.reduce((a,b)=>a+b._w+(b.pad<<1),0);
|
||||
l.fillx |= l.c.some(c=>c.fillx);
|
||||
break;
|
||||
}
|
||||
case "v": {
|
||||
l.c.forEach(updateMin);
|
||||
l._h = l.c.reduce((a,b)=>a+b._h+(b.pad<<1),0);
|
||||
l._w = l.c.reduce((a,b)=>Math.max(a,b._w+(b.pad<<1)),0);
|
||||
l.filly |= l.c.some(c=>c.filly);
|
||||
break;
|
||||
}
|
||||
default: throw "Unknown item type "+l.type;
|
||||
}
|
||||
if (l.r&1) { // rotation
|
||||
var t = l._w;l._w=l._h;l._h=t;
|
||||
}
|
||||
l._w = Math.max(l._w, 0|l.width);
|
||||
l._h = Math.max(l._h, 0|l.height);
|
||||
}
|
||||
function render(l) {
|
||||
if (!l) l = this.l;
|
||||
g.reset();
|
||||
if (l.col) g.setColor(l.col);
|
||||
if (l.bgCol!==undefined) g.setBgColor(l.bgCol).clearRect(l.x,l.y,l.x+l.w,l.y+l.h);
|
||||
switch (l.type) {
|
||||
case "txt":
|
||||
g.setFont(l.font,l.fsz).setFontAlign(0,0,l.r).drawString(l.label, l.x+(l.w>>1), l.y+(l.h>>1), true/*solid bg*/);
|
||||
break;
|
||||
case "btn":
|
||||
var poly = [
|
||||
l.x,l.y+4,
|
||||
l.x+4,l.y,
|
||||
l.x+l.w-5,l.y,
|
||||
l.x+l.w-1,l.y+4,
|
||||
l.x+l.w-1,l.y+l.h-5,
|
||||
l.x+l.w-5,l.y+l.h-1,
|
||||
l.x+4,l.y+l.h-1,
|
||||
l.x,l.y+l.h-5,
|
||||
l.x,l.y+4
|
||||
];
|
||||
g.setColor(g.theme.bgH).fillPoly(poly).setColor(l.selected ? g.theme.fgH : g.theme.fg).drawPoly(poly).setFont("4x6",2).setFontAlign(0,0,l.r).drawString(l.label,l.x+l.w/2,l.y+l.h/2);
|
||||
break;
|
||||
case "img":
|
||||
g.drawImage(l.src(), l.x, l.y);
|
||||
break;
|
||||
case "custom":
|
||||
l.render(l);
|
||||
break;
|
||||
}
|
||||
if (l.c) l.c.forEach(render);
|
||||
}
|
||||
|
||||
Layout.prototype.render = function (l) {
|
||||
if (!l) l = this._l;
|
||||
render(l);
|
||||
};
|
||||
|
||||
Layout.prototype.layout = function (l) {
|
||||
// l = current layout element
|
||||
// exw,exh = extra width/height available
|
||||
var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
|
||||
var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
|
||||
switch (l.type) {
|
||||
case "h": {
|
||||
let x = l.x + (l.w-l._w)/2;
|
||||
if (fillx) { x = l.x; }
|
||||
l.c.forEach(c => {
|
||||
c.w = c._w + (c.fillx?(l.w-l._w)/fillx:0);
|
||||
c.h = c.filly ? l.h : c._h;
|
||||
c.x = x;
|
||||
c.y = l.y + (1+(0|c.valign))*(l.h-c.h)/2;
|
||||
x += c.w;
|
||||
if (c.pad) {
|
||||
x += c.pad*2;
|
||||
c.w += c.pad*2;
|
||||
c.h += c.pad*2;
|
||||
}
|
||||
if (c.c) {
|
||||
this.layout(c);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "v": {
|
||||
let y = l.y + (l.h-l._h)/2;
|
||||
if (filly) { y = l.y; }
|
||||
l.c.forEach(c => {
|
||||
c.w = c.fillx ? l.w : c._w;
|
||||
c.h = c._h + (c.filly?(l.h-l._h)/filly:0);
|
||||
c.x = l.x + (1+(0|c.halign))*(l.w-c.w)/2;
|
||||
c.y = y;
|
||||
y += c.h;
|
||||
if (c.pad) {
|
||||
y += c.pad*2;
|
||||
c.w += c.pad*2;
|
||||
c.h += c.pad*2;
|
||||
}
|
||||
if (c.c) this.layout(c);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
Layout.prototype.debug = function(l,c) {
|
||||
if (!l) l = this._l;
|
||||
c=c||1;
|
||||
g.setColor(c&1,c&2,c&4).drawRect(l.x+c-1, l.y+c-1, l.x+l.w-c, l.y+l.h-c);
|
||||
c++;
|
||||
if (l.c) l.c.forEach(n => this.debug(n,c));
|
||||
};
|
||||
Layout.prototype.update = function() {
|
||||
var l = this._l;
|
||||
var w = g.getWidth();
|
||||
var y = this.yOffset;
|
||||
var h = g.getHeight()-y;
|
||||
// update sizes
|
||||
updateMin(l);
|
||||
// center
|
||||
if (l.fillx || l.filly) {
|
||||
l.w = w;
|
||||
l.h = h;
|
||||
l.x = 0;
|
||||
l.y = y;
|
||||
} else {
|
||||
l.w = l._w;
|
||||
l.h = l._h;
|
||||
l.x = (w-l.w)/2;
|
||||
l.y = y+(h-l.h)/2;
|
||||
}
|
||||
// layout children
|
||||
this.layout(l);
|
||||
};
|
||||
|
||||
Layout.prototype.clear = function(l) {
|
||||
if (!l) l = this._l;
|
||||
g.reset();
|
||||
if (l.bgCol!==undefined) g.setBgColor(l.bgCol);
|
||||
g.clearRect(l.x,l.y,l.x+l.w-1,l.y+l.h-1);
|
||||
};
|
||||
|
||||
exports = Layout;
|
||||