Merge branch 'espruino:master' into add_waternet

master
Willems Davy 2023-09-20 11:27:19 +02:00 committed by GitHub
commit 9c297817d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 179 additions and 66 deletions

View File

@ -45,3 +45,4 @@
0.40: Use substring of message when it's longer than fits the designated menu entry. 0.40: Use substring of message when it's longer than fits the designated menu entry.
0.41: Fix a menu bug affecting alarms with empty messages. 0.41: Fix a menu bug affecting alarms with empty messages.
0.42: Fix date not getting saved in event edit menu when tapping Confirm 0.42: Fix date not getting saved in event edit menu when tapping Confirm
0.43: New settings: Show confirm, Show Overflow, Show Type.

View File

@ -13,7 +13,7 @@ It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master
- `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely) - `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely)
- `New Timer` → Configure a new timer (triggered based on amount of time elapsed in hours/minutes/seconds) - `New Timer` → Configure a new timer (triggered based on amount of time elapsed in hours/minutes/seconds)
- `New Event` → Configure a new event (triggered based on time and date) - `New Event` → Configure a new event (triggered based on time and date)
- `Repeat` → Alarm can be be fired only once or repeated (every X number of _days_, _weeks_, _months_ or _years_) - `Repeat` → Alarm can be fired only once or repeated (every X number of _days_, _weeks_, _months_ or _years_)
- `Advanced` - `Advanced`
- `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details - `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details
- `Enable All` → Enable _all_ disabled alarms & timers - `Enable All` → Enable _all_ disabled alarms & timers

View File

@ -1,6 +1,11 @@
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
const settings = Object.assign({
showConfirm : true,
showAutoSnooze : true,
showHidden : true
}, require('Storage').readJSON('alarm.json',1)||{});
// 0 = Sunday (default), 1 = Monday // 0 = Sunday (default), 1 = Monday
const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0; const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0;
const WORKDAYS = 62; const WORKDAYS = 62;
@ -51,12 +56,14 @@ function getLabel(e) {
} }
function trimLabel(label, maxLength) { function trimLabel(label, maxLength) {
if(settings.showOverflow) return label;
return (label.length > maxLength return (label.length > maxLength
? label.substring(0,maxLength-3) + "..." ? label.substring(0,maxLength-3) + "..."
: label.substring(0,maxLength)); : label.substring(0,maxLength));
} }
function formatAlarmMessage(msg) { function formatAlarmProperty(msg) {
if(settings.showOverflow) return msg;
if (msg == null) { if (msg == null) {
return msg; return msg;
} else if (msg.length > 7) { } else if (msg.length > 7) {
@ -155,7 +162,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
}, },
/*LANG*/"Message": { /*LANG*/"Message": {
value: alarm.msg, value: alarm.msg,
format: formatAlarmMessage, format: formatAlarmProperty,
onchange: () => { onchange: () => {
setTimeout(() => { setTimeout(() => {
keyboard.input({text:alarm.msg}).then(result => { keyboard.input({text:alarm.msg}).then(result => {
@ -166,6 +173,19 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
}, 100); }, 100);
} }
}, },
/*LANG*/"Group": {
value: alarm.group,
format: formatAlarmProperty,
onchange: () => {
setTimeout(() => {
keyboard.input({text:alarm.group}).then(result => {
alarm.group = result;
prepareAlarmForSave(alarm, alarmIndex, time, date, true);
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate);
});
}, 100);
}
},
/*LANG*/"Enabled": { /*LANG*/"Enabled": {
value: alarm.on, value: alarm.on,
onchange: v => alarm.on = v onchange: v => alarm.on = v
@ -197,6 +217,10 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
}; };
if (!keyboard) delete menu[/*LANG*/"Message"]; if (!keyboard) delete menu[/*LANG*/"Message"];
if (!keyboard || !settings.showGroup) delete menu[/*LANG*/"Group"];
if (!settings.showConfirm) delete menu[/*LANG*/"Confirm"];
if (!settings.showAutoSnooze) delete menu[/*LANG*/"Auto Snooze"];
if (!settings.showHidden) delete menu[/*LANG*/"Hidden"];
if (!alarm.date) { if (!alarm.date) {
delete menu[/*LANG*/"Day"]; delete menu[/*LANG*/"Day"];
delete menu[/*LANG*/"Month"]; delete menu[/*LANG*/"Month"];
@ -387,7 +411,7 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
}, },
/*LANG*/"Message": { /*LANG*/"Message": {
value: timer.msg, value: timer.msg,
format: formatAlarmMessage, format: formatAlarmProperty,
onchange: () => { onchange: () => {
setTimeout(() => { setTimeout(() => {
keyboard.input({text:timer.msg}).then(result => { keyboard.input({text:timer.msg}).then(result => {
@ -420,6 +444,8 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
}; };
if (!keyboard) delete menu[/*LANG*/"Message"]; if (!keyboard) delete menu[/*LANG*/"Message"];
if (!settings.showConfirm) delete menu[/*LANG*/"Confirm"];
if (!settings.showHidden) delete menu[/*LANG*/"Hidden"];
if (!isNew) { if (!isNew) {
menu[/*LANG*/"Delete"] = () => { menu[/*LANG*/"Delete"] = () => {
E.showPrompt(getLabel(timer) + "\n" + /*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => { E.showPrompt(getLabel(timer) + "\n" + /*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => {

View File

@ -2,7 +2,7 @@
"id": "alarm", "id": "alarm",
"name": "Alarms & Timers", "name": "Alarms & Timers",
"shortName": "Alarms", "shortName": "Alarms",
"version": "0.42", "version": "0.43",
"description": "Set alarms and timers on your Bangle", "description": "Set alarms and timers on your Bangle",
"icon": "app.png", "icon": "app.png",
"tags": "tool,alarm", "tags": "tool,alarm",
@ -11,7 +11,8 @@
"dependencies": { "scheduler":"type", "alarm":"widget" }, "dependencies": { "scheduler":"type", "alarm":"widget" },
"storage": [ "storage": [
{ "name": "alarm.app.js", "url": "app.js" }, { "name": "alarm.app.js", "url": "app.js" },
{ "name": "alarm.img", "url": "app-icon.js", "evaluate": true } { "name": "alarm.img", "url": "app-icon.js", "evaluate": true },
{ "name": "alarm.settings.js", "url":"settings.js" }
], ],
"screenshots": [ "screenshots": [
{ "url": "screenshot-1.png" }, { "url": "screenshot-1.png" },
@ -25,5 +26,6 @@
{ "url": "screenshot-9.png" }, { "url": "screenshot-9.png" },
{ "url": "screenshot-10.png" }, { "url": "screenshot-10.png" },
{ "url": "screenshot-11.png" } { "url": "screenshot-11.png" }
] ],
"data":[ {"name":"alarm.settings.json"} ]
} }

51
apps/alarm/settings.js Normal file
View File

@ -0,0 +1,51 @@
(function(back) {
let settings = Object.assign({
showConfirm : true,
showAutoSnooze : true,
showHidden : true
}, require('Storage').readJSON('alarm.json',1)||{});
const save = () => require('Storage').write('alarm.json', settings);
const DATE_FORMATS = ['default', 'mmdd'];
const DATE_FORMATS_LABELS = [/*LANG*/'Default', /*LANG*/'MMDD'];
const appMenu = {
'': {title: 'alarm'}, '< Back': back,
/*LANG*/'Menu Date Format': {
value: DATE_FORMATS.indexOf(settings.menuDateFormat || 'default'),
format: v => DATE_FORMATS_LABELS[v],
min: 0,
max: DATE_FORMATS.length - 1,
onchange : v => {
if(v > 0) {
settings.menuDateFormat=DATE_FORMATS[v];
} else {
delete settings.menuDateFormat;
}
save();
}
},
/*LANG*/'Show Menu Auto Snooze': {
value : !!settings.showAutoSnooze,
onchange : v => { settings.showAutoSnooze=v; save();}
},
/*LANG*/'Show Menu Confirm': {
value : !!settings.showConfirm,
onchange : v => { settings.showConfirm=v; save();}
},
/*LANG*/'Show Menu Hidden': {
value : !!settings.showHidden,
onchange : v => { settings.showHidden=v; save();}
},
/*LANG*/'Show Menu Group': {
value : !!settings.showGroup,
onchange : v => { settings.showGroup=v; save();}
},
/*LANG*/'Show Text Overflow': {
value : !!settings.showOverflow,
onchange : v => { settings.showOverflow=v; save();}
},
};
E.showMenu(appMenu);
});

View File

@ -10,3 +10,4 @@
0.28: More config options for cleaner look, enabled fast loading 0.28: More config options for cleaner look, enabled fast loading
0.29: Fixed a bug that would leave old font files in storage. 0.29: Fixed a bug that would leave old font files in storage.
0.30: Added options to show widgets and date on twist and tap. New fonts. 0.30: Added options to show widgets and date on twist and tap. New fonts.
0.31: Bugfix, no more freeze.

View File

@ -34,9 +34,12 @@
extrasTimeout = undefined; extrasTimeout = undefined;
hideExtras(); hideExtras();
}, 5000); }, 5000);
extrasShown = false;
}; };
let drawExtras = function() { //draw date, day of the week and widgets let drawExtras = function() { //draw date, day of the week and widgets
let date = new Date(); let date = new Date();
g.reset();
g.clearRect(0, 138, g.getWidth() - 1, 176);
g.setFont("Teletext10x18Ascii").setFontAlign(0, 1); g.setFont("Teletext10x18Ascii").setFontAlign(0, 1);
if (settings.weekday) g.drawString(require("locale").dow(date).toUpperCase(), g.getWidth() / 2, g.getHeight() - 18); if (settings.weekday) g.drawString(require("locale").dow(date).toUpperCase(), g.getWidth() / 2, g.getHeight() - 18);
if (settings.date) g.drawString(require('locale').date(date, 1), g.getWidth() / 2, g.getHeight()); if (settings.date) g.drawString(require('locale').date(date, 1), g.getWidth() / 2, g.getHeight());
@ -45,21 +48,23 @@
}; };
let hideExtras = function() { let hideExtras = function() {
if (extrasTimeout) clearTimeout(extrasTimeout); if (extrasTimeout) clearTimeout(extrasTimeout);
extrasTimeout = undefined; //NEW
g.reset();
g.clearRect(0, 138, g.getWidth() - 1, 176); g.clearRect(0, 138, g.getWidth() - 1, 176);
require("widget_utils").hide(); require("widget_utils").hide();
extrasShown = false; extrasShown = false; ///NEW
}; };
let draw = function() { let draw = function() {
if (drawTimeout) clearTimeout(drawTimeout); //NEW
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
let date = new Date(); let date = new Date();
g.reset(); g.reset();
if (extrasShown) drawExtras(); if (extrasShown) drawExtras();
else hideExtras(); else hideExtras();
require('contourclock').drawClock(settings.fontIndex); require('contourclock').drawClock(settings.fontIndex);
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}; };
if (settings.hideWhenLocked) { if (settings.hideWhenLocked) {
onLock = locked => { onLock = locked => {
@ -83,6 +88,8 @@
Bangle.removeListener('twist', showExtras); Bangle.removeListener('twist', showExtras);
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
if (extrasTimeout) clearTimeout(extrasTimeout); if (extrasTimeout) clearTimeout(extrasTimeout);
drawTimeout = undefined;
extrasTimeout = undefined;
if (settings.hideWhenLocked) require("widget_utils").show(); if (settings.hideWhenLocked) require("widget_utils").show();
g.reset(); g.reset();
g.clear(); g.clear();
@ -91,7 +98,7 @@
g.clear(); g.clear();
if (settings.widgets) { if (settings.widgets) {
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); setTimeout(Bangle.drawWidgets,0); //NEW
} }
draw(); draw();
} }

View File

@ -1,7 +1,7 @@
{ "id": "contourclock", { "id": "contourclock",
"name": "Contour Clock", "name": "Contour Clock",
"shortName" : "Contour Clock", "shortName" : "Contour Clock",
"version":"0.30", "version":"0.31",
"icon": "app.png", "icon": "app.png",
"readme": "README.md", "readme": "README.md",
"description": "A Minimalist clockface with large Digits.", "description": "A Minimalist clockface with large Digits.",

View File

@ -7,3 +7,4 @@
0.07: Settings for display colors 0.07: Settings for display colors
0.08: Catch and discard swipe events on fw2v19 and up (as well as some cutting 0.08: Catch and discard swipe events on fw2v19 and up (as well as some cutting
edge 2v18 ones), allowing compatability with the Back Swipe app. edge 2v18 ones), allowing compatability with the Back Swipe app.
0.09: Fix colors settings, where color was stored as string instead of the expected int.

View File

@ -1,6 +1,6 @@
{ "id": "dragboard", { "id": "dragboard",
"name": "Dragboard", "name": "Dragboard",
"version":"0.08", "version":"0.09",
"description": "A library for text input via swiping keyboard", "description": "A library for text input via swiping keyboard",
"icon": "app.png", "icon": "app.png",
"type":"textinput", "type":"textinput",

View File

@ -21,7 +21,7 @@
value: settings[key] == color, value: settings[key] == color,
onchange: () => { onchange: () => {
if (color >= 0) { if (color >= 0) {
settings[key] = color; settings[key] = parseInt(color);
} else { } else {
delete settings[key]; delete settings[key];
} }

View File

@ -1,3 +1,4 @@
0.01: New App based on dragboard, but with a U shaped drag area 0.01: New App based on dragboard, but with a U shaped drag area
0.02: Catch and discard swipe events on fw2v19 and up (as well as some cutting 0.02: Catch and discard swipe events on fw2v19 and up (as well as some cutting
edge 2v18 ones), allowing compatability with the Back Swipe app. edge 2v18 ones), allowing compatability with the Back Swipe app.
0.03: Fix "Uncaught Error: Unhandled promise rejection: ReferenceError: "dragHandlerDB" is not defined"

View File

@ -148,7 +148,7 @@ exports.input = function(options) {
g.clearRect(Bangle.appRect); g.clearRect(Bangle.appRect);
resolve(text); resolve(text);
}, },
drag: dragHandlerDB drag: dragHandlerUB
}); });
Bangle.prependListener&&Bangle.prependListener('swipe', catchSwipe); // Intercept swipes on fw2v19 and later. Should not break on older firmwares. Bangle.prependListener&&Bangle.prependListener('swipe', catchSwipe); // Intercept swipes on fw2v19 and later. Should not break on older firmwares.

View File

@ -1,6 +1,6 @@
{ "id": "draguboard", { "id": "draguboard",
"name": "DragUboard", "name": "DragUboard",
"version":"0.02", "version":"0.03",
"description": "A library for text input via swiping U-shaped keyboard.", "description": "A library for text input via swiping U-shaped keyboard.",
"icon": "app.png", "icon": "app.png",
"type":"textinput", "type":"textinput",

View File

@ -21,7 +21,7 @@
value: settings[key] == color, value: settings[key] == color,
onchange: () => { onchange: () => {
if (color >= 0) { if (color >= 0) {
settings[key] = color; settings[key] = parseInt(color);
} else { } else {
delete settings[key]; delete settings[key];
} }

View File

@ -1,3 +1,4 @@
0.01: New app! 0.01: New app!
0.02-0.04: Bug fixes 0.02-0.04: Bug fixes
0.05: Submitted to the app loader 0.05: Submitted to the app loader
0.06: Added setting to show clock after start/resume

View File

@ -1,3 +1,5 @@
g.clear();
Bangle.POMOPLUS_ACTIVE = true; //Prevent the boot code from running. To avoid having to reload on every interaction, we'll control the vibrations from here when the user is in the app. Bangle.POMOPLUS_ACTIVE = true; //Prevent the boot code from running. To avoid having to reload on every interaction, we'll control the vibrations from here when the user is in the app.
const storage = require("Storage"); const storage = require("Storage");
@ -13,32 +15,36 @@ if (
} }
function drawButtons() { function drawButtons() {
let w = g.getWidth();
let h = g.getHeight();
//Draw the backdrop //Draw the backdrop
const BAR_TOP = g.getHeight() - 24; const BAR_TOP = h - 24;
g.setColor(0, 0, 1).setFontAlign(0, -1) g.setColor(0, 0, 1).setFontAlign(0, -1)
.clearRect(0, BAR_TOP, g.getWidth(), g.getHeight()) .clearRect(0, BAR_TOP, w, h)
.fillRect(0, BAR_TOP, g.getWidth(), g.getHeight()) .fillRect(0, BAR_TOP, w, h)
.setColor(1, 1, 1); .setColor(1, 1, 1);
if (!common.state.wasRunning) { //If the timer was never started, only show a play button if (!common.state.wasRunning) { //If the timer was never started, only show a play button
g.drawImage(common.BUTTON_ICONS.play, g.getWidth() / 2, BAR_TOP); g.drawImage(common.BUTTON_ICONS.play, w / 2, BAR_TOP);
} else { } else {
g.drawLine(g.getWidth() / 2, BAR_TOP, g.getWidth() / 2, g.getHeight()); g.drawLine(w / 2, BAR_TOP, w / 2, h);
if (common.state.running) { if (common.state.running) {
g.drawImage(common.BUTTON_ICONS.pause, g.getWidth() / 4, BAR_TOP) g.drawImage(common.BUTTON_ICONS.pause, w / 4, BAR_TOP)
.drawImage(common.BUTTON_ICONS.skip, g.getWidth() * 3 / 4, BAR_TOP); .drawImage(common.BUTTON_ICONS.skip, w * 3 / 4, BAR_TOP);
} else { } else {
g.drawImage(common.BUTTON_ICONS.reset, g.getWidth() / 4, BAR_TOP) g.drawImage(common.BUTTON_ICONS.reset, w / 4, BAR_TOP)
.drawImage(common.BUTTON_ICONS.play, g.getWidth() * 3 / 4, BAR_TOP); .drawImage(common.BUTTON_ICONS.play, w * 3 / 4, BAR_TOP);
} }
} }
} }
function drawTimerAndMessage() { function drawTimerAndMessage() {
let w = g.getWidth();
let h = g.getHeight();
g.reset() g.reset()
.setFontAlign(0, 0) .setFontAlign(0, 0)
.setFont("Vector", 36) .setFont("Vector", 36)
.clearRect(0, 24, 176, 152) .clearRect(w / 2 - 60, h / 2 - 34, w / 2 + 60, h / 2 + 34)
//Draw the timer //Draw the timer
.drawString((() => { .drawString((() => {
@ -53,7 +59,7 @@ function drawTimerAndMessage() {
if (hours >= 1) return `${parseInt(hours)}:${pad(minutes)}:${pad(seconds)}`; if (hours >= 1) return `${parseInt(hours)}:${pad(minutes)}:${pad(seconds)}`;
else return `${parseInt(minutes)}:${pad(seconds)}`; else return `${parseInt(minutes)}:${pad(seconds)}`;
})(), g.getWidth() / 2, g.getHeight() / 2) })(), w / 2, h / 2)
//Draw the phase label //Draw the phase label
.setFont("Vector", 12) .setFont("Vector", 12)
@ -63,7 +69,7 @@ function drawTimerAndMessage() {
else if (currentPhase == common.PHASE_SHORT_BREAK) return `Short break ${numShortBreaks + 1}/${common.settings.numShortBreaks}`; else if (currentPhase == common.PHASE_SHORT_BREAK) return `Short break ${numShortBreaks + 1}/${common.settings.numShortBreaks}`;
else return "Long break!"; else return "Long break!";
})(common.state.phase, common.state.numShortBreaks), })(common.state.phase, common.state.numShortBreaks),
g.getWidth() / 2, g.getHeight() / 2 + 18); w / 2, h / 2 + 18);
//Update phase with vibation if needed //Update phase with vibation if needed
if (common.getTimeLeft() <= 0) { if (common.getTimeLeft() <= 0) {
@ -91,6 +97,7 @@ Bangle.on("touch", (button, xy) => {
}; };
setupTimerInterval(); setupTimerInterval();
drawButtons(); drawButtons();
if (common.settings.showClock) Bangle.showClock();
} else if (common.state.running) { } else if (common.state.running) {
//If we are running, there are two buttons: pause and skip //If we are running, there are two buttons: pause and skip
@ -127,6 +134,7 @@ Bangle.on("touch", (button, xy) => {
drawTimerAndMessage(); drawTimerAndMessage();
setupTimerInterval(); setupTimerInterval();
drawButtons(); drawButtons();
if (common.settings.showClock) Bangle.showClock();
} }
} }
}); });
@ -154,4 +162,4 @@ E.on('kill', () => {
}); });
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();

View File

@ -105,14 +105,13 @@ exports.nextPhase = function (vibrate) {
if (vibrate) { if (vibrate) {
if (exports.state.phase == exports.PHASE_WORKING) { if (exports.state.phase == exports.PHASE_WORKING) {
Bangle.buzz(750, 1); Bangle.buzz(800, 1);
} else if (exports.state.phase == exports.PHASE_SHORT_BREAK) { } else if (exports.state.phase == exports.PHASE_SHORT_BREAK) {
Bangle.buzz(); Bangle.buzz();
setTimeout(Bangle.buzz, 400); setTimeout(Bangle.buzz, 400);
} else { } else {
Bangle.buzz(); Bangle.buzz();
setTimeout(Bangle.buzz, 400); setTimeout(Bangle.buzz, 400, 400);
setTimeout(Bangle.buzz, 600);
} }
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"id": "pomoplus", "id": "pomoplus",
"name": "Pomodoro Plus", "name": "Pomodoro Plus",
"version": "0.05", "version": "0.06",
"description": "A configurable pomodoro timer that runs in the background.", "description": "A configurable pomodoro timer that runs in the background.",
"icon": "icon.png", "icon": "icon.png",
"type": "app", "type": "app",

View File

@ -10,7 +10,8 @@ const storage = require("Storage");
longBreak: 900000, //15 minute long break longBreak: 900000, //15 minute long break
numShortBreaks: 3, //3 short breaks for every long break numShortBreaks: 3, //3 short breaks for every long break
pausedTimerExpireTime: 21600000, //If the timer was left paused for >6 hours, reset it on next launch pausedTimerExpireTime: 21600000, //If the timer was left paused for >6 hours, reset it on next launch
widget: false //If a widget is added in the future, whether the user wants it showClock: false, //Show clock after start/resume
widget: false, //If a widget is added in the future, whether the user wants it
}; };
} }
@ -89,6 +90,13 @@ const storage = require("Storage");
else return `${Math.floor(value / 3600000)}h ${(value % 3600000) / 60000}m` else return `${Math.floor(value / 3600000)}h ${(value % 3600000) / 60000}m`
} }
}, },
'Show clock': {
value: settings.showClock,
onchange: function(value) {
settings.showClock = value;
save();
},
},
}; };
E.showMenu(menu) E.showMenu(menu)
}) })

View File

@ -823,7 +823,7 @@ function showAppSettings(app) {
try { try {
appSettings = eval(appSettings); appSettings = eval(appSettings);
} catch (e) { } catch (e) {
console.log(`${app.name} settings error:`, e) console.log(`${app.name} settings error:`, e);
return showError(/*LANG*/'Error in settings'); return showError(/*LANG*/'Error in settings');
} }
if (typeof appSettings !== "function") { if (typeof appSettings !== "function") {
@ -833,7 +833,7 @@ function showAppSettings(app) {
// pass showAppSettingsMenu as "back" argument // pass showAppSettingsMenu as "back" argument
appSettings(()=>showAppSettingsMenu()); appSettings(()=>showAppSettingsMenu());
} catch (e) { } catch (e) {
console.log(`${app.name} settings error:`, e) console.log(`${app.name} settings error:`, e);
return showError(/*LANG*/'Error in settings'); return showError(/*LANG*/'Error in settings');
} }
} }

View File

@ -1,5 +1,7 @@
0.01: First version 0.01: First version
0.02: Don't break if running on 2v08 firmware (just don't display anything) 0.02: Don't break if running on 2v08 firmware (just don't display anything)
0.03: Works with light theme 0.03: Works with light theme
Doesn't drain battery by updating every 2 secs 0.04: Doesn't drain battery by updating every 2 secs
fix alignment 0.05: fix alignment
0.06: changed to solid red heart image when HRM is on

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,7 +1,7 @@
{ {
"id": "widhrt", "id": "widhrt",
"name": "HRM Widget", "name": "HRM Widget",
"version": "0.03", "version": "0.06",
"description": "Tiny widget to show the power on/off status of the Heart Rate Monitor", "description": "Tiny widget to show the power on/off status of the Heart Rate Monitor",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",

View File

@ -9,10 +9,13 @@
WIDGETS.widhrt={area:"tr",width:24,draw:function() { WIDGETS.widhrt={area:"tr",width:24,draw:function() {
g.reset(); g.reset();
if (Bangle.isHRMOn()) { if (Bangle.isHRMOn()) {
g.setColor("#f00"); // on = red g.setColor('#f00'); // on = red
} else { } else {
g.setColor(g.theme.dark ? "#333" : "#CCC"); // off = grey g.setColor(g.theme.dark ? '#fff' : '#000'); // off
} }
g.drawImage(atob("FhaBAAAAAAAAAAAAAcDgD8/AYeGDAwMMDAwwADDAAMOABwYAGAwAwBgGADAwAGGAAMwAAeAAAwAAAAAAAAAAAAA="), 1+this.x, 1+this.y);
// image converter https://www.espruino.com/Image+Converter ; settings to get a fillable image
// 1 bit bw, transparency? Y, transparent bg, white heart (must be white for color fill)
g.drawImage(atob("FBSBAAAAAAAAAAAB+fg//8f//n//5//+f//n//5//+P//D//wf/4D/8Af+AB+AAPAABgAAAA"), 1+this.x, 1+this.y);
}}; }};
})(); })();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 B

After

Width:  |  Height:  |  Size: 1.3 KiB

2
core

@ -1 +1 @@
Subproject commit 71813fe2eaf19987cec07db850ab9d1959694f96 Subproject commit 11f8e16d2ebb726bb92f29d812dc3ccba4f362a9

View File

@ -186,14 +186,15 @@ Layout.prototype.render = function (l) {
x+4,y+h-1, x+4,y+h-1,
x,y+h-5, x,y+h-5,
x,y+4 x,y+4
], bg = l.bgCol!==undefined?l.bgCol:gfx.theme.bg2, ], bg = l.bgCol!==undefined?l.bgCol:gfx.theme.bg,
btnborder = l.btnBorder!==undefined?l.btnBorder:gfx.theme.fg2; btnborder = l.btnBorderCol!==undefined?l.btnBorderCol:gfx.theme.fg2,
btnface = l.btnFaceCol!==undefined?l.btnFaceCol:gfx.theme.bg2;
if(l.selected){ if(l.selected){
bg = gfx.theme.bgH, btnborder = gfx.theme.fgH; btnface = gfx.theme.bgH, btnborder = gfx.theme.fgH;
} }
gfx.setColor(bg).fillPoly(poly).setColor(btnborder).drawPoly(poly); gfx.setColor(btnface).fillPoly(poly).setColor(btnborder).drawPoly(poly);
if (l.col!==undefined) gfx.setColor(l.col); if (l.col!==undefined) gfx.setColor(l.col);
if (l.src) gfx.setBgColor(bg).drawImage( if (l.src) gfx.setBgColor(btnface).drawImage(
"function"==typeof l.src?l.src():l.src, "function"==typeof l.src?l.src():l.src,
l.x + l.w/2, l.x + l.w/2,
l.y + l.h/2, l.y + l.h/2,

View File

@ -45,9 +45,10 @@ layout.render();
- A `scale` field, eg `2` to set scale of an image - A `scale` field, eg `2` to set scale of an image
- A `r` field to set rotation of text or images (0: 0°, 1: 90°, 2: 180°, 3: 270°). - A `r` field to set rotation of text or images (0: 0°, 1: 90°, 2: 180°, 3: 270°).
- A `wrap` field to enable line wrapping. Requires some combination of `width`/`height` and `fillx`/`filly` to be set. Not compatible with text rotation. - A `wrap` field to enable line wrapping. Requires some combination of `width`/`height` and `fillx`/`filly` to be set. Not compatible with text rotation.
- A `col` field, eg `#f00` for red - A `col` field, eg `#f00` for red. When `type:"btn"`, this sets the color of the button's text label (defaults to `Graphics.theme.fg2` if not set)
- A `bgCol` field for background color (will automatically fill on render). When `type:"btn"`, this sets the background color of the button, and will not change color on press - A `bgCol` field for background color (will automatically fill on render). When `type:"btn"`, this sets the color of the space outside the button border (defaults to parent layoutObject's `bgCol` if not set)
- A `btnBorder` field for button border color (will default to theme if not set) - A `btnBorderCol` field for button border color (defaults to `Graphics.theme.fg2` if not set)
- A `btnFaceCol` field for the background color of the area inside the button border (defaults to `Graphics.theme.bg2` if not set)
- A `halign` field to set horizontal alignment WITHIN a `v` container. `-1`=left, `1`=right, `0`=center - A `halign` field to set horizontal alignment WITHIN a `v` container. `-1`=left, `1`=right, `0`=center
- A `valign` field to set vertical alignment WITHIN a `h` container. `-1`=top, `1`=bottom, `0`=center - A `valign` field to set vertical alignment WITHIN a `h` container. `-1`=top, `1`=bottom, `0`=center
- A `pad` integer field to set pixels padding - A `pad` integer field to set pixels padding

16
modules/Layout.min.js vendored
View File

@ -4,11 +4,11 @@ h,b,f,a){var e=null==d.bgCol?a:g.toColor(d.bgCol);if(e!=a||"txt"==d.type||"btn"=
if(void 0===h&&this.buttons[b])return this.buttons[b].cb();this.buttons[b]&&(delete this.buttons[b].selected,this.render(this.buttons[b]));b=(b+f+h)%f;this.buttons[b]&&(this.buttons[b].selected=1,this.render(this.buttons[b]));this.selectedButton=b}),d=!0);!this.options.back&&!this.options.remove||d||Bangle.setUI({mode:"custom",back:this.options.back,remove:this.options.remove});if(this.b){function h(b,f){.75<f.time-f.lastTime&&this.b[b].cbl?this.b[b].cbl(f):this.b[b].cb&&this.b[b].cb(f)}Bangle.btnWatches&& if(void 0===h&&this.buttons[b])return this.buttons[b].cb();this.buttons[b]&&(delete this.buttons[b].selected,this.render(this.buttons[b]));b=(b+f+h)%f;this.buttons[b]&&(this.buttons[b].selected=1,this.render(this.buttons[b]));this.selectedButton=b}),d=!0);!this.options.back&&!this.options.remove||d||Bangle.setUI({mode:"custom",back:this.options.back,remove:this.options.remove});if(this.b){function h(b,f){.75<f.time-f.lastTime&&this.b[b].cbl?this.b[b].cbl(f):this.b[b].cb&&this.b[b].cb(f)}Bangle.btnWatches&&
Bangle.btnWatches.forEach(clearWatch);Bangle.btnWatches=[];this.b[0]&&Bangle.btnWatches.push(setWatch(h.bind(this,0),BTN1,{repeat:!0,edge:-1}));this.b[1]&&Bangle.btnWatches.push(setWatch(h.bind(this,1),BTN2,{repeat:!0,edge:-1}));this.b[2]&&Bangle.btnWatches.push(setWatch(h.bind(this,2),BTN3,{repeat:!0,edge:-1}))}if(2==process.env.HWVERSION){function h(b,f){b.cb&&f.x>=b.x&&f.y>=b.y&&f.x<=b.x+b.w&&f.y<=b.y+b.h&&(2==f.type&&b.cbl?b.cbl(f):b.cb&&b.cb(f));b.c&&b.c.forEach(a=>h(a,f))}Bangle.touchHandler= Bangle.btnWatches.forEach(clearWatch);Bangle.btnWatches=[];this.b[0]&&Bangle.btnWatches.push(setWatch(h.bind(this,0),BTN1,{repeat:!0,edge:-1}));this.b[1]&&Bangle.btnWatches.push(setWatch(h.bind(this,1),BTN2,{repeat:!0,edge:-1}));this.b[2]&&Bangle.btnWatches.push(setWatch(h.bind(this,2),BTN3,{repeat:!0,edge:-1}))}if(2==process.env.HWVERSION){function h(b,f){b.cb&&f.x>=b.x&&f.y>=b.y&&f.x<=b.x+b.w&&f.y<=b.y+b.h&&(2==f.type&&b.cbl?b.cbl(f):b.cb&&b.cb(f));b.c&&b.c.forEach(a=>h(a,f))}Bangle.touchHandler=
(b,f)=>h(this._l,f);Bangle.on("touch",Bangle.touchHandler)}};p.prototype.render=function(d){function h(c){"ram";b.reset();void 0!==c.col&&b.setColor(c.col);void 0!==c.bgCol&&b.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);f[c.type](c)}d||(d=this._l);this.updateNeeded&&this.update();var b=g,f={"":function(){},txt:function(c){"ram";if(c.wrap){var m=b.setFont(c.font).setFontAlign(0,-1).wrapString(c.label,c.w),n=c.y+(c.h-b.getFontHeight()*m.length>>1);b.drawString(m.join("\n"),c.x+(c.w>> (b,f)=>h(this._l,f);Bangle.on("touch",Bangle.touchHandler)}};p.prototype.render=function(d){function h(c){"ram";b.reset();void 0!==c.col&&b.setColor(c.col);void 0!==c.bgCol&&b.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);f[c.type](c)}d||(d=this._l);this.updateNeeded&&this.update();var b=g,f={"":function(){},txt:function(c){"ram";if(c.wrap){var m=b.setFont(c.font).setFontAlign(0,-1).wrapString(c.label,c.w),n=c.y+(c.h-b.getFontHeight()*m.length>>1);b.drawString(m.join("\n"),c.x+(c.w>>
1),n)}else b.setFont(c.font).setFontAlign(0,0,c.r).drawString(c.label,c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){"ram";var m=c.x+(0|c.pad),n=c.y+(0|c.pad),q=c.w-(c.pad<<1),r=c.h-(c.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=void 0!==c.bgCol?c.bgCol:b.theme.bg2;q=void 0!==c.btnBorder?c.btnBorder:b.theme.fg2;c.selected&&(n=b.theme.bgH,q=b.theme.fgH);b.setColor(n).fillPoly(m).setColor(q).drawPoly(m);void 0!==c.col&&b.setColor(c.col);c.src?b.setBgColor(n).drawImage("function"== 1),n)}else b.setFont(c.font).setFontAlign(0,0,c.r).drawString(c.label,c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){"ram";var m=c.x+(0|c.pad),n=c.y+(0|c.pad),q=c.w-(c.pad<<1),r=c.h-(c.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=void 0!==c.btnBorderCol?c.btnBorderCol:b.theme.fg2;q=void 0!==c.btnFaceCol?c.btnFaceCol:b.theme.bg2;c.selected&&(q=b.theme.bgH,n=b.theme.fgH);b.setColor(q).fillPoly(m).setColor(n).drawPoly(m);void 0!==c.col&&b.setColor(c.col);c.src?
typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)}):b.setFont(c.font||"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){"ram";b.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){"ram";c.render(c)},h:function(c){"ram";c.c.forEach(h)},v:function(c){"ram";c.c.forEach(h)}};if(this.lazy){this.rects||(this.rects={});var a=this.rects.clone(), b.setBgColor(q).drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)}):b.setFont(c.font||"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){"ram";b.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){"ram";c.render(c)},h:function(c){"ram";c.c.forEach(h)},v:function(c){"ram";c.c.forEach(h)}};if(this.lazy){this.rects||
e=[];t(d,a,e,this.rects,null);for(var l in a)delete this.rects[l];d=Object.keys(a).map(c=>a[c]).reverse();for(var k of d)b.setBgColor(k.bg).clearRect.apply(g,k);e.forEach(h)}else h(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var h={h:function(b){"ram";var f=b.x+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.fillx),0);e||(f+=b.w-b._w>>1,e=1);var l=f;b.c.forEach(k=>{k.x=0|l;f+=k._w;a+=0|k.fillx;l=f+Math.floor(a*(b.w-b._w)/e);k.w=0|l-k.x;k.h=0|(k.filly?b.h- (this.rects={});var a=this.rects.clone(),e=[];t(d,a,e,this.rects,null);for(var l in a)delete this.rects[l];d=Object.keys(a).map(c=>a[c]).reverse();for(var k of d)b.setBgColor(k.bg).clearRect.apply(g,k);e.forEach(h)}else h(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var h={h:function(b){"ram";var f=b.x+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.fillx),0);e||(f+=b.w-b._w>>1,e=1);var l=f;b.c.forEach(k=>{k.x=0|l;f+=k._w;a+=0|k.fillx;l=f+Math.floor(a*(b.w-
(b.pad<<1):k._h);k.y=0|b.y+(0|b.pad)+((1+(0|k.valign))*(b.h-(b.pad<<1)-k.h)>>1);if(k.c)h[k.type](k)})},v:function(b){"ram";var f=b.y+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.filly),0);e||(f+=b.h-b._h>>1,e=1);var l=f;b.c.forEach(k=>{k.y=0|l;f+=k._h;a+=0|k.filly;l=f+Math.floor(a*(b.h-b._h)/e);k.h=0|l-k.y;k.w=0|(k.fillx?b.w-(b.pad<<1):k._w);k.x=0|b.x+(0|b.pad)+((1+(0|k.halign))*(b.w-(b.pad<<1)-k.w)>>1);if(k.c)h[k.type](k)})}};if(h[d.type])h[d.type](d)};p.prototype.debug=function(d,h){d||(d=this._l); b._w)/e);k.w=0|l-k.x;k.h=0|(k.filly?b.h-(b.pad<<1):k._h);k.y=0|b.y+(0|b.pad)+((1+(0|k.valign))*(b.h-(b.pad<<1)-k.h)>>1);if(k.c)h[k.type](k)})},v:function(b){"ram";var f=b.y+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.filly),0);e||(f+=b.h-b._h>>1,e=1);var l=f;b.c.forEach(k=>{k.y=0|l;f+=k._h;a+=0|k.filly;l=f+Math.floor(a*(b.h-b._h)/e);k.h=0|l-k.y;k.w=0|(k.fillx?b.w-(b.pad<<1):k._w);k.x=0|b.x+(0|b.pad)+((1+(0|k.halign))*(b.w-(b.pad<<1)-k.w)>>1);if(k.c)h[k.type](k)})}};if(h[d.type])h[d.type](d)};p.prototype.debug=
h=h||1;g.setColor(h&1,h&2,h&4).drawRect(d.x+h-1,d.y+h-1,d.x+d.w-h,d.y+d.h-h);d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad,d.y+d.h-d.pad);h++;d.c&&d.c.forEach(b=>this.debug(b,h))};p.prototype.update=function(){function d(a){"ram";b[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=Math.max(a._w+(a.pad<<1),0|a.width);a._h=Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var h=g,b={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0, function(d,h){d||(d=this._l);h=h||1;g.setColor(h&1,h&2,h&4).drawRect(d.x+h-1,d.y+h-1,d.x+d.w-h,d.y+d.h-h);d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad,d.y+d.h-d.pad);h++;d.c&&d.c.forEach(b=>this.debug(b,h))};p.prototype.update=function(){function d(a){"ram";b[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=Math.max(a._w+(a.pad<<1),0|a.width);a._h=Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var h=g,b={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*
-1)/100));if(a.wrap)a._h=a._w=0;else{var e=g.setFont(a.font).stringMetrics(a.label);a._w=e.width;a._h=e.height}},btn:function(a){"ram";a.font&&a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));var e=a.src?h.imageMetrics("function"==typeof a.src?a.src():a.src):h.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=h.imageMetrics("function"==typeof a.src?a.src():a.src),l=a.scale||1;a._w=e.width*l;a._h=e.height* a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var e=g.setFont(a.font).stringMetrics(a.label);a._w=e.width;a._h=e.height}},btn:function(a){"ram";a.font&&a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));var e=a.src?h.imageMetrics("function"==typeof a.src?a.src():a.src):h.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=h.imageMetrics("function"==typeof a.src?a.src():a.src),l=a.scale||1;a._w=e.width*
l},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0},h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>Math.max(e,l._h),0);a._w=a.c.reduce((e,l)=>e+l._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>e+l._h,0);a._w=a.c.reduce((e,l)=>Math.max(e,l._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)}},f= l;a._h=e.height*l},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0},h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>Math.max(e,l._h),0);a._w=a.c.reduce((e,l)=>e+l._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>e+l._h,0);a._w=a.c.reduce((e,l)=>Math.max(e,l._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&
this._l;d(f);delete b;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p (a.filly=1)}},f=this._l;d(f);delete b;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p