diff --git a/apps/boxclk/ChangeLog b/apps/boxclk/ChangeLog index 5560f00bc..ba46af04e 100644 --- a/apps/boxclk/ChangeLog +++ b/apps/boxclk/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: New config options such as step, meridian, short/long formats, custom prefix/suffix diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index 001c7ed10..1dc8ef98f 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -22,7 +22,7 @@ Here's what an example configuration might look like: ``` { - "customBox": { // + "customBox": { "string": "Your text here", "font": "CustomFont", // Custom fonts must be removed in setUI "fontSize": 1, @@ -34,15 +34,23 @@ Here's what an example configuration might look like: "yPadding": -4, "xOffset": 0, "yOffset": 3, - "boxPos": { "x": 0.5, "y": 0.5 } + "boxPos": { "x": 0.5, "y": 0.5 }, + "prefix": "", // Adds a string to the beginning of the main string + "suffix": "", // Adds a string to the end of the main string + "disableSuffix": true, // Only used to remove the DayOfMonth suffix + "short": false // Gets long format value of time, meridian, date, or DoW + }, "bg": { // Can also be removed for no background "img": "YourImageName.img" } } ``` + __Breakdown of Parameters:__ +* **Box Name:** The name of your text box. Box Clock includes functional support for "time", "date", "meridian" (AM/PM), "dow" (Day of Week), "batt" (Battery), and "step" (Step count). You can add additional custom boxes with unique titles. + * **string:** The text string to be displayed inside the box. * **font:** The font name given to g.setFont() @@ -63,6 +71,14 @@ __Breakdown of Parameters:__ * **boxPos:** Initial position of the box on the screen. Values are fractions of the screen width (x) and height (y), so { "x": 0.5, "y": 0.5 } would be in the middle of the screen. +* **prefix:** Adds a string to the beginning of the main string. For example, you can set "prefix": "Steps: " to display "Steps: 100" for the step count. + +* **suffix:** Adds a string to the end of the main string. For example, you can set "suffix": "%" to display "80%" for the battery percentage. + +* **disableSuffix:** Applies only to the "date" box. Set to true to disable the DayOfMonth suffix. This is used to remove the "st","nd","rd", or "th" from the DayOfMonth number + +* **short:** Set to false to get the long format value of time, meridian, date, or DayOfWeek. Short formats are used by default, + * **bg:** This specifies a custom background image, with the img property defining the name of the image file on the Bangle.js storage. ## Multiple Configurations diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 8f671667a..41636e1ef 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -33,7 +33,6 @@ let w = g.getWidth(); let h = g.getHeight(); let totalWidth, totalHeight; - let enableSuffix = true; let drawTimeout; /** @@ -165,13 +164,17 @@ /** * --------------------------------------------------------------- - * 7. Date and time related functions + * 7. String forming helper functions * --------------------------------------------------------------- */ - let getDate = function() { + let isBool = function(val, defaultVal) { + return typeof val !== 'undefined' ? Boolean(val) : defaultVal; + }; + + let getDate = function(short, disableSuffix) { const date = new Date(); const dayOfMonth = date.getDate(); - const month = locale.month(date, 1); + const month = short ? locale.month(date, 0) : locale.month(date, 1); const year = date.getFullYear(); let suffix; if ([1, 21, 31].includes(dayOfMonth)) { @@ -183,12 +186,24 @@ } else { suffix = "th"; } - let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; - return month + " " + dayOfMonthStr + ", " + year; + let dayOfMonthStr = disableSuffix ? dayOfMonth : dayOfMonth + suffix; + return month + " " + dayOfMonthStr + (short ? '' : (", " + year)); // not including year for short version }; - let getDayOfWeek = function(date) { - return locale.dow(date, 0); + let getDayOfWeek = function(date, short) { + return locale.dow(date, short ? 1 : 0); + }; + + locale.meridian = function(date, short) { + let hours = date.getHours(); + let meridian = hours >= 12 ? 'PM' : 'AM'; + return short ? meridian[0] : meridian; + }; + + let modString = function(boxItem, data) { + let prefix = boxItem.prefix || ''; + let suffix = boxItem.suffix || ''; + return prefix + data + suffix; }; /** @@ -196,44 +211,56 @@ * 8. Main draw function * --------------------------------------------------------------- */ - let draw = function(boxes) { - date = new Date(); - g.clear(); - if (bgImage) { - g.drawImage(bgImage, 0, 0); - } - if (boxes.time) { - boxes.time.string = locale.time(date, 1); - } - if (boxes.date) { - boxes.date.string = getDate(); - } - if (boxes.dow) { - boxes.dow.string = getDayOfWeek(date); - } - if (boxes.batt) { - boxes.batt.string = E.getBattery() + "%"; - } - boxKeys.forEach((boxKey) => { - let boxItem = boxes[boxKey]; - calcBoxSize(boxItem); - const pos = calcBoxPos(boxKey); - if (isDragging[boxKey]) { - g.setColor(boxItem.border); - g.drawRect(pos.x1, pos.y1, pos.x2, pos.y2); + let draw = (function() { + let updatePerMinute = true; // variable to track the state of time display + + return function(boxes) { + date = new Date(); + g.clear(); + if (bgImage) { + g.drawImage(bgImage, 0, 0); } - g.drawString( - boxItem, - boxItem.string, - boxPos[boxKey].x + boxItem.xOffset, - boxPos[boxKey].y + boxItem.yOffset - ); - }); - if (!Object.values(isDragging).some(Boolean)) { - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); - } - }; + if (boxes.time) { + boxes.time.string = modString(boxes.time, locale.time(date, isBool(boxes.time.short, true) ? 1 : 0)); + updatePerMinute = isBool(boxes.time.short, true); + } + if (boxes.meridian) { + boxes.meridian.string = modString(boxes.meridian, locale.meridian(date, isBool(boxes.meridian.short, true))); + } + if (boxes.date) { + boxes.date.string = modString(boxes.date, getDate(isBool(boxes.date.short, true), isBool(boxes.date.disableSuffix, false))); + } + if (boxes.dow) { + boxes.dow.string = modString(boxes.dow, getDayOfWeek(date, isBool(boxes.dow.short, true))); + } + if (boxes.batt) { + boxes.batt.string = modString(boxes.batt, E.getBattery()); + } + if (boxes.step) { + boxes.step.string = modString(boxes.step, Bangle.getStepCount()); + } + boxKeys.forEach((boxKey) => { + let boxItem = boxes[boxKey]; + calcBoxSize(boxItem); + const pos = calcBoxPos(boxKey); + if (isDragging[boxKey]) { + g.setColor(boxItem.border); + g.drawRect(pos.x1, pos.y1, pos.x2, pos.y2); + } + g.drawString( + boxItem, + boxItem.string, + boxPos[boxKey].x + boxItem.xOffset, + boxPos[boxKey].y + boxItem.yOffset + ); + }); + if (!Object.values(isDragging).some(Boolean)) { + if (drawTimeout) clearTimeout(drawTimeout); + let interval = updatePerMinute ? 60000 - (Date.now() % 60000) : 1000; + drawTimeout = setTimeout(() => draw(boxes), interval); + } + }; + })(); /** * --------------------------------------------------------------- diff --git a/apps/boxclk/boxclk.json b/apps/boxclk/boxclk.json index e3db1b602..51a67ebd6 100644 --- a/apps/boxclk/boxclk.json +++ b/apps/boxclk/boxclk.json @@ -23,7 +23,8 @@ "yPadding": 0.5, "xOffset": 1, "yOffset": 1, - "boxPos": { "x": 0.633, "y": 0.3 } + "boxPos": { "x": 0.633, "y": 0.3 }, + "short": false }, "date": { "font": "6x8", @@ -36,7 +37,8 @@ "yPadding": 0.5, "xOffset": 1, "yOffset": 1, - "boxPos": { "x": 0.633, "y": 0.39 } + "boxPos": { "x": 0.633, "y": 0.39 }, + "short": false }, "batt": { "font": "4x6", @@ -47,9 +49,10 @@ "border": "#fff", "xPadding": -0.5, "yPadding": -0.5, - "xOffset": 2, + "xOffset": 1, "yOffset": 1, - "boxPos": { "x": 0.92, "y": 0.95 } + "boxPos": { "x": 0.9, "y": 0.95 }, + "suffix": "%" }, "bg": { "img": "boxclk.beachhouse.img" diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json index ad5ad72e9..9b759def7 100644 --- a/apps/boxclk/metadata.json +++ b/apps/boxclk/metadata.json @@ -1,7 +1,7 @@ { "id": "boxclk", "name": "Box Clock", - "version": "0.01", + "version": "0.02", "description": "A customizable clock with configurable text boxes that can be positioned to show your favorite background", "icon": "app.png", "screenshots": [ diff --git a/apps/boxclk/settings.js b/apps/boxclk/settings.js index 9c597c2d8..e35db455d 100644 --- a/apps/boxclk/settings.js +++ b/apps/boxclk/settings.js @@ -32,11 +32,11 @@ }; } + let configFiles = []; storage.list().forEach(file => { let match = file.match(fileRegex); if (match) { - let configNumber = match[1]; - configs[configNumber] = handleSelection(configNumber); + configFiles.push({ file: file, number: parseInt(match[1]) }); } else if (file === "boxclk.json") { hasDefaultConfig = true; let defaultConfig = storage.readJSON(file, 1); @@ -56,6 +56,13 @@ } }); + // Sort the config files by number + configFiles.sort((a, b) => a.number - b.number); + + configFiles.forEach(configFile => { + configs[configFile.number] = handleSelection(configFile.number); + }); + if (!selectedConfig) { if (hasDefaultConfig) { selectedConfig = "Default";