From 9001d4b3e97292da161dcd46913c930ffb462281 Mon Sep 17 00:00:00 2001 From: stweedo Date: Thu, 15 Jun 2023 21:11:39 -0500 Subject: [PATCH 01/48] [boxclk] - initial release candidate --- apps/boxclk/ChangeLog | 1 + apps/boxclk/app.js | 212 +++++++++++++++++++++++++++++++++++ apps/boxclk/beachhouse.js | 1 + apps/boxclk/boxclk.json | 57 ++++++++++ apps/boxclk/icon.js | 1 + apps/boxclk/icon.png | Bin 0 -> 1071 bytes apps/boxclk/metadata.json | 21 ++++ apps/boxclk/screenshot-1.png | Bin 0 -> 5936 bytes apps/boxclk/screenshot.png | Bin 0 -> 8061 bytes 9 files changed, 293 insertions(+) create mode 100644 apps/boxclk/ChangeLog create mode 100644 apps/boxclk/app.js create mode 100644 apps/boxclk/beachhouse.js create mode 100644 apps/boxclk/boxclk.json create mode 100644 apps/boxclk/icon.js create mode 100644 apps/boxclk/icon.png create mode 100644 apps/boxclk/metadata.json create mode 100644 apps/boxclk/screenshot-1.png create mode 100644 apps/boxclk/screenshot.png diff --git a/apps/boxclk/ChangeLog b/apps/boxclk/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/boxclk/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js new file mode 100644 index 000000000..f32ca4c84 --- /dev/null +++ b/apps/boxclk/app.js @@ -0,0 +1,212 @@ +Graphics.prototype.setFontBrunoAce = function() { + // Actual height 23 (24 - 2) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), + 46, + atob("CBEdChgYGhgaGBsaCQ=="), + 32|65536 + ); +}; + +let w = g.getWidth(); +let h = g.getHeight(); +let totalWidth, totalHeight; +let drawTimeout; +let enableSuffix = true; +let storage = require("Storage"); +let locale = require("locale"); +let date = new Date(); + +let bgImage; +let boxesConfig = storage.readJSON('boxclk.json', 1) || {}; +let boxes = {}; + +for (let key in boxesConfig) { + if (key === 'bg' && boxesConfig[key].img) { + bgImage = storage.read(boxesConfig[key].img); + } else { + boxes[key] = Object.assign({}, boxesConfig[key]); + } +} + +let boxPos = {}; +let isDragging = {}; +let wasDragging = {}; + +Object.keys(boxes).forEach((boxKey) => { + let boxConfig = boxes[boxKey]; + boxPos[boxKey] = { + x: w * boxConfig.boxPos.x, + y: h * boxConfig.boxPos.y + }; + isDragging[boxKey] = false; + wasDragging[boxKey] = false; +}); + +let g_drawString = g.drawString; +g.drawString = function(box, str, x, y) { + outlineText(box, str, x, y); + g.setColor(box.color); + g_drawString.call(g, str, x, y); +}; + +function outlineText(box, str, x, y) { + let px = box.outline; + let dx = [-px, 0, px, -px, px, -px, 0, px]; + let dy = [-px, -px, -px, 0, 0, px, px, px]; + g.setColor(box.outlineColor); + for (let i = 0; i < dx.length; i++) { + g_drawString.call(g, str, x + dx[i], y + dy[i]); + } +} + +function calcBoxSize(boxItem) { + g.reset(); + g.setFontAlign(0,0); + g.setFont(boxItem.font, boxItem.fontSize); + let strWidth = g.stringWidth(boxItem.string) + 2 * boxItem.outline; + let fontHeight = g.getFontHeight() + 2 * boxItem.outline; + totalWidth = strWidth + 2 * boxItem.xPadding; + totalHeight = fontHeight + 2 * boxItem.yPadding; +} + +function calcBoxPos(boxKey) { + return { + x1: boxPos[boxKey].x - totalWidth / 2, + y1: boxPos[boxKey].y - totalHeight / 2, + x2: boxPos[boxKey].x + totalWidth / 2, + y2: boxPos[boxKey].y + totalHeight / 2 + }; +} + +function getDate() { + const date = new Date(); + const dayOfMonth = date.getDate(); + const month = locale.month(date, 1); + const year = date.getFullYear(); + + let suffix; + if ([1, 21, 31].includes(dayOfMonth)) { + suffix = "st"; + } else if ([2, 22].includes(dayOfMonth)) { + suffix = "nd"; + } else if ([3, 23].includes(dayOfMonth)) { + suffix = "rd"; + } else { + suffix = "th"; + } + + let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; + + return month + " " + dayOfMonthStr + ", " + year; +} + +function getDayOfWeek(date) { + return locale.dow(date, 0); +} + +function draw(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() + "%"; + } + Object.keys(boxes).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); + drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); + } +} + +function setup() { + Bangle.setUI({ + mode : "clock", + remove : function() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + delete Graphics.prototype.setFontBrunoAce; + require("widget_utils").show(); + }}); + + Bangle.on('touch', function(zone, e) { + wasDragging = Object.assign({}, isDragging); + let boxTouched = false; + Object.keys(boxes).forEach((boxKey) => { + if (touchInText(e, boxes[boxKey], boxKey)) { + isDragging[boxKey] = true; + wasDragging[boxKey] = true; + boxTouched = true; + } + }); + if (!boxTouched) { + Object.keys(isDragging).forEach((boxKey) => { + isDragging[boxKey] = false; + }); + } + if (Object.values(wasDragging).some(Boolean) || !boxTouched) { + draw(boxes); + } + }); + + Bangle.on('drag', function(e) { + Object.keys(boxes).forEach((boxKey) => { + if (isDragging[boxKey]) { + let boxItem = boxes[boxKey]; + calcBoxSize(boxItem); + let newX = boxPos[boxKey].x + e.dx; + let newY = boxPos[boxKey].y + e.dy; + if (newX - totalWidth / 2 >= 0 && + newX + totalWidth / 2 <= w && + newY - totalHeight / 2 >= 0 && + newY + totalHeight / 2 <= h ) { + boxPos[boxKey].x = newX; + boxPos[boxKey].y = newY; + } + const pos = (boxKey); + g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); + } + }); + draw(boxes); + }); + g.reset(); + draw(boxes); +} + +function touchInText(e, boxItem, boxKey) { + calcBoxSize(boxItem); + const pos = calcBoxPos(boxKey); + return e.x >= pos.x1 && + e.x <= pos.x2 && + e.y >= pos.y1 && + e.y <= pos.y2; +} + +Bangle.loadWidgets(); +require("widget_utils").swipeOn(); +setup(); diff --git a/apps/boxclk/beachhouse.js b/apps/boxclk/beachhouse.js new file mode 100644 index 000000000..062a6b9d3 --- /dev/null +++ b/apps/boxclk/beachhouse.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("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")) diff --git a/apps/boxclk/boxclk.json b/apps/boxclk/boxclk.json new file mode 100644 index 000000000..f04d58aa4 --- /dev/null +++ b/apps/boxclk/boxclk.json @@ -0,0 +1,57 @@ +{ + "time": { + "font": "BrunoAce", + "fontSize": 1, + "outline": 2, + "color": "#000", + "outlineColor": "#fff", + "border": "#000", + "xPadding": 1, + "yPadding": -4, + "xOffset": 0, + "yOffset": 3, + "boxPos": { "x": 0.5, "y": 0.47 } + }, + "dow": { + "font": "6x8", + "fontSize": 2, + "outline": 1, + "color": "#000", + "outlineColor": "#fff", + "border": "#000", + "xPadding": -0.5, + "yPadding": 0.5, + "xOffset": 1, + "yOffset": 1, + "boxPos": { "x": 0.5, "y": 0.6 } + }, + "date": { + "font": "6x8", + "fontSize": 1, + "outline": 1, + "color": "#000", + "outlineColor": "#fff", + "border": "#000", + "xPadding": 0, + "yPadding": 0.5, + "xOffset": 1, + "yOffset": 0, + "boxPos": { "x": 0.5, "y": 0.69 } + }, + "batt": { + "font": "4x6", + "fontSize": 2, + "outline": 1, + "color": "#0ff", + "outlineColor": "#000", + "border": "#000", + "xPadding": -0.5, + "yPadding": -0.5, + "xOffset": 1, + "yOffset": 1, + "boxPos": { "x": 0.92, "y": 0.95 } + }, + "bg": { + "img": "beachhouse.img" + } +} diff --git a/apps/boxclk/icon.js b/apps/boxclk/icon.js new file mode 100644 index 000000000..f6f4412c6 --- /dev/null +++ b/apps/boxclk/icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkEogA/AEB5TCwVAC6aOCoED/4AQmAXDh4XR+AX5+URgERl4XR+KGEj4XP+cAgMz/8ziEAn4XOkEBCIfziECC5ouBl/zkMAiU/+QwGC4/wE4MgLwQFCQgoXHiEfO4Mj/8yO4PxgIXMHwMggb+DgRQBC5fygIPBn4xBiYFCiDDEC43xgfyLQJfCGoMvmDCEC5ABCVIJlBCoIXM+EPAIRgBAQIIDC542DC53xT4MfC4cCBAanLBQaWDBIgXo+YXXdgoXQbQcBd5YXHiAYHC5wAC+UQe4IXTDAMABAQXEgYPEiDQEAATdBAYMwC4ZUCK4aLHSoIACC5IuHSogXKCxAXHogXTCwQAFC5YUIC7b/EC6TFFC6IwJC5itBcwQXUbI7vBC5bFGAAgXMDBQXNJIQXUGBEEBog=")) diff --git a/apps/boxclk/icon.png b/apps/boxclk/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..89da3b69552b747c0e61721dcdb61dcd6d2ded22 GIT binary patch literal 1071 zcmV+~1kn45P)1-*b+->hps{`JK}H7J!WW{QNxoo95=` z6aaG6l?3^#=+9FK0{lNu;Q08MhlYl@q@;u`77Is3MM=SRX=y1B4-fOn$%+2~oSvSt z&1U1Uu&{Smj*yTLuBoZv!^1=W0B|@Q9334ku_lv=YinzHVq$`~x3~HF`dSLE;ln+| zAQp8dCntRiFgQ3UBZ|1Hsw&>!-+#B`y}doItgO_Y2Bw@(02~g7B!Dt7FrfK2H#fPh zt&P*u)3tX1*YM$HIu^hK2@h+Nk6U3k$^;5fQnmY@Y*w zr4l2FWYnC;$Rjo(A%WM|*L6ExTU(PD#J04wh;Mgym&3!uWd*~N^*R7vI{2}oK$zD6 z!pN)S4-O8vuC9(_V`DioGLnmni+O8nOYW_%t_nQrC|`ATwX``hGU9yzY)J59S%mO% z!p5{w$;ZdXzncF5TwY$5n5L#C@wK;QSEEcxg?fmfY;GX~xjudY}5Rq(@MnHT5peH12x7)Q{9wjp~Q+DW21vo!H zcMISpNK#Ugw2_#YC~bZS0AIn3CXcfX>SSeQ@kexXbHjOgdD2#AXXmT?v$M0t0bDJB z&>J;Zw6d~dv`<|*eU&_Pe}8ZEE48kAT?Oz|@=6B!?0E~o^bB!k6u{FW{k~40lYjI7 z0aqjqKR!Ns3&87=j*gCw0v6y29d&ni2Q0uZbc8PuGcz**3GhR8hEFDAV`BjakgukZ zDjgjihEJCM004b|x!jeNmAU-Q)79gGC-sGhzPWn@Avcc;-jJ1uZatX%8TmlDM0DXH pfF54}KwpXIClOt_nYhcxh2MP~HmTe9Ps#uQ002ovPDHLkV1jpL`q2OY literal 0 HcmV?d00001 diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json new file mode 100644 index 000000000..37a6d7758 --- /dev/null +++ b/apps/boxclk/metadata.json @@ -0,0 +1,21 @@ +{ + "id": "boxclk", + "name": "Box Clock", + "version": "0.01", + "description": "A customizable clock with configurable text boxes that can be positioned to show your favorite background", + "icon": "app.png", + "screenshots": [ + {"url":"screenshot.png"}, + {"url":"screenshot-1.png"} + ], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"boxclk.app.js","url":"app.js"}, + {"name":"boxclk.json","url":"boxclk.json"}, + {"name":"boxclock.img","url":"icon.js","evaluate":true}, + {"name":"beachhouse.img","url":"beachhouse.js","evaluate":true} + ] +} diff --git a/apps/boxclk/screenshot-1.png b/apps/boxclk/screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..91bf880c0bfb3a7d138f49c59280f6e11d6d0cb1 GIT binary patch literal 5936 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 Date: Thu, 15 Jun 2023 21:13:48 -0500 Subject: [PATCH 02/48] name change --- apps/boxclk/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json index 37a6d7758..a9c5064d4 100644 --- a/apps/boxclk/metadata.json +++ b/apps/boxclk/metadata.json @@ -15,7 +15,7 @@ "storage": [ {"name":"boxclk.app.js","url":"app.js"}, {"name":"boxclk.json","url":"boxclk.json"}, - {"name":"boxclock.img","url":"icon.js","evaluate":true}, + {"name":"boxclk.img","url":"icon.js","evaluate":true}, {"name":"beachhouse.img","url":"beachhouse.js","evaluate":true} ] } From 2a86dea55d83d752ed3366324907e365f30a7d13 Mon Sep 17 00:00:00 2001 From: stweedo Date: Thu, 15 Jun 2023 21:17:11 -0500 Subject: [PATCH 03/48] name change --- apps/boxclk/{icon.png => app.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename apps/boxclk/{icon.png => app.png} (100%) diff --git a/apps/boxclk/icon.png b/apps/boxclk/app.png similarity index 100% rename from apps/boxclk/icon.png rename to apps/boxclk/app.png From b72ad557919c46a3cde8b0fe665b0bce57f0837b Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Thu, 15 Jun 2023 21:57:04 -0500 Subject: [PATCH 04/48] Create README.md --- apps/boxclk/README.md | 65 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 apps/boxclk/README.md diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md new file mode 100644 index 000000000..e8a168805 --- /dev/null +++ b/apps/boxclk/README.md @@ -0,0 +1,65 @@ +# Box Clock + +Box Clock is a customizable clock app for Bangle.js 2 that features an interactive drag and drop interface and easy JSON configuration. + +## Unique Features + +__Drag & Drop:__ + +This intuitive feature allows you to reposition any element (box) on the clock face with ease. Tap on the box(s) you want to move and the border will show, drag into position and tap outside of the boxes to finish placing. + +__JSON Configuration:__ + +Each box can be customized extensively via a simple JSON configuration. Here's what an example configuration might look like: + +``` +{ + "time": { + "font": "BrunoAce", + "fontSize": 1, + "outline": 2, + "color": "#000", + "outlineColor": "#fff", + "border": "#000", + "xPadding": 1, + "yPadding": -4, + "xOffset": 0, + "yOffset": 3, + "boxPos": { "x": 0.5, "y": 0.5 } + }, + "bg": { + "img": "YourImageName.img" + } +} +``` +* **font:** The font name given to g.setFont() + +* **fontSize:** The size of the font. + +* **outline:** The thickness of the outline around the text. + +* **color:** The color of the text. + +* **outlineColor:** The color of the text outline. + +* **border:** The color of the box border when moving. + +* **xPadding, yPadding:** Additional padding around the text inside the box. + +* **xOffset, yOffset:** Offsets the text position within the box. + +* **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. + +* **bg:** This specifies a custom background image, with the img property defining the name of the image file on the Bangle.js storage. + +## Compatibility + +This app was built and tested with Bangle.js 2. + +## Feedback + +If you have any issues or suggestions, please open an issue on this GitHub repository. Contributions to improve the application are also welcomed. + +## Creator + +[stweedo](https://github.com/stweedo) From 7acdf5e7db461114ba8555e3bbf8950e39cd0ec4 Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Thu, 15 Jun 2023 21:57:36 -0500 Subject: [PATCH 05/48] Update metadata.json --- apps/boxclk/metadata.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json index a9c5064d4..d5b58a3c7 100644 --- a/apps/boxclk/metadata.json +++ b/apps/boxclk/metadata.json @@ -11,6 +11,7 @@ "type": "clock", "tags": "clock", "supports": ["BANGLEJS2"], + "readme": "README.md", "allow_emulator": true, "storage": [ {"name":"boxclk.app.js","url":"app.js"}, From fb5cd4f152725b2655eba3a634884796ba463c98 Mon Sep 17 00:00:00 2001 From: stweedo Date: Thu, 15 Jun 2023 22:05:24 -0500 Subject: [PATCH 06/48] change screenshot --- apps/boxclk/screenshot.png | Bin 8061 -> 8631 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/boxclk/screenshot.png b/apps/boxclk/screenshot.png index afe51563ea9415dd6dc89beb44ad671e6300dce6..12ccee5e25914b3585015e5be0c9831a5a8ae79f 100644 GIT binary patch literal 8631 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 JPDHLkV1j!T%ozXx literal 8061 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 Date: Thu, 15 Jun 2023 22:55:13 -0500 Subject: [PATCH 07/48] Update readme --- apps/boxclk/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index e8a168805..613c74fe0 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -10,15 +10,16 @@ This intuitive feature allows you to reposition any element (box) on the clock f __JSON Configuration:__ -Each box can be customized extensively via a simple JSON configuration. Here's what an example configuration might look like: +Each box can be customized extensively via a simple JSON configuration. You can also add a custom text string to your configuration with the "string": "Your custom text here", attribute. Here's what an example configuration might look like: ``` { - "time": { - "font": "BrunoAce", + "customBox": { + "string": "Your custom text here", + "font": "CustomFont", "fontSize": 1, "outline": 2, - "color": "#000", + "color": "#FF9900", "outlineColor": "#fff", "border": "#000", "xPadding": 1, @@ -32,6 +33,8 @@ Each box can be customized extensively via a simple JSON configuration. Here's w } } ``` +* **string:** The text string to be displayed inside the box. + * **font:** The font name given to g.setFont() * **fontSize:** The size of the font. From 654e8f9e2b764a7db20f808fa20cd4ff774c5c66 Mon Sep 17 00:00:00 2001 From: stweedo Date: Thu, 15 Jun 2023 23:24:37 -0500 Subject: [PATCH 08/48] Fixed launcher bug --- apps/boxclk/app.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index f32ca4c84..f1496b13d 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -1,13 +1,3 @@ -Graphics.prototype.setFontBrunoAce = function() { - // Actual height 23 (24 - 2) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), - 46, - atob("CBEdChgYGhgaGBsaCQ=="), - 32|65536 - ); -}; - let w = g.getWidth(); let h = g.getHeight(); let totalWidth, totalHeight; @@ -145,12 +135,23 @@ function draw(boxes) { } function setup() { + Graphics.prototype.setFontBrunoAce = function() { + // Actual height 23 (24 - 2) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), + 46, + atob("CBEdChgYGhgaGBsaCQ=="), + 32|65536 + ); + }; + Bangle.setUI({ mode : "clock", remove : function() { if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; delete Graphics.prototype.setFontBrunoAce; + g.drawString = g_drawString; require("widget_utils").show(); }}); From 96c6446b552e7d552c034778215504db6106dead Mon Sep 17 00:00:00 2001 From: stweedo Date: Thu, 15 Jun 2023 23:37:00 -0500 Subject: [PATCH 09/48] Update starting positions --- apps/boxclk/boxclk.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/boxclk/boxclk.json b/apps/boxclk/boxclk.json index f04d58aa4..0d2fa732f 100644 --- a/apps/boxclk/boxclk.json +++ b/apps/boxclk/boxclk.json @@ -10,7 +10,7 @@ "yPadding": -4, "xOffset": 0, "yOffset": 3, - "boxPos": { "x": 0.5, "y": 0.47 } + "boxPos": { "x": 0.65, "y": 0.16 } }, "dow": { "font": "6x8", @@ -23,7 +23,7 @@ "yPadding": 0.5, "xOffset": 1, "yOffset": 1, - "boxPos": { "x": 0.5, "y": 0.6 } + "boxPos": { "x": 0.65, "y": 0.3 } }, "date": { "font": "6x8", @@ -35,8 +35,8 @@ "xPadding": 0, "yPadding": 0.5, "xOffset": 1, - "yOffset": 0, - "boxPos": { "x": 0.5, "y": 0.69 } + "yOffset": 1, + "boxPos": { "x": 0.65, "y": 0.39 } }, "batt": { "font": "4x6", From 7496c97ae23d57fae48a03e14253f18d396cd941 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 00:50:42 -0500 Subject: [PATCH 10/48] Made custom font a function, touch/drag global --- apps/boxclk/app.js | 63 ++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index f1496b13d..9c3fe953a 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -1,6 +1,8 @@ let w = g.getWidth(); let h = g.getHeight(); let totalWidth, totalHeight; +let touchHandler; +let dragHandler; let drawTimeout; let enableSuffix = true; let storage = require("Storage"); @@ -11,6 +13,18 @@ let bgImage; let boxesConfig = storage.readJSON('boxclk.json', 1) || {}; let boxes = {}; +function setCustomFont() { + Graphics.prototype.setFontBrunoAce = function() { + // Actual height 23 (24 - 2) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), + 46, + atob("CBEdChgYGhgaGBsaCQ=="), + 32|65536 + ); + }; +} + for (let key in boxesConfig) { if (key === 'bg' && boxesConfig[key].img) { bgImage = storage.read(boxesConfig[key].img); @@ -135,27 +149,8 @@ function draw(boxes) { } function setup() { - Graphics.prototype.setFontBrunoAce = function() { - // Actual height 23 (24 - 2) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), - 46, - atob("CBEdChgYGhgaGBsaCQ=="), - 32|65536 - ); - }; - - Bangle.setUI({ - mode : "clock", - remove : function() { - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = undefined; - delete Graphics.prototype.setFontBrunoAce; - g.drawString = g_drawString; - require("widget_utils").show(); - }}); - - Bangle.on('touch', function(zone, e) { + // Define the touchHandler function + touchHandler = function(zone, e) { wasDragging = Object.assign({}, isDragging); let boxTouched = false; Object.keys(boxes).forEach((boxKey) => { @@ -173,9 +168,10 @@ function setup() { if (Object.values(wasDragging).some(Boolean) || !boxTouched) { draw(boxes); } - }); + }; - Bangle.on('drag', function(e) { + // Define the dragHandler function + dragHandler = function(e) { Object.keys(boxes).forEach((boxKey) => { if (isDragging[boxKey]) { let boxItem = boxes[boxKey]; @@ -194,8 +190,27 @@ function setup() { } }); draw(boxes); + }; + + Bangle.on('touch', touchHandler); + Bangle.on('drag', dragHandler); + + Bangle.setUI({ + mode : "clock", + remove : function() { + // remove the event handlers when the app should be removed + Bangle.removeListener('touch', touchHandler); + Bangle.removeListener('drag', dragHandler); + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + delete Graphics.prototype.setFontBrunoAce; + g.drawString = g_drawString; + require("widget_utils").show(); + setCustomFont(); + } }); - g.reset(); + + setCustomFont(); draw(boxes); } From d44c537c667ca75c6a71e16fcf6511143ac95f49 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 01:47:04 -0500 Subject: [PATCH 11/48] changed function name --- apps/boxclk/app.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 9c3fe953a..a2202cb68 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -13,7 +13,7 @@ let bgImage; let boxesConfig = storage.readJSON('boxclk.json', 1) || {}; let boxes = {}; -function setCustomFont() { +function loadCustomFont() { Graphics.prototype.setFontBrunoAce = function() { // Actual height 23 (24 - 2) return this.setFontCustom( @@ -88,7 +88,6 @@ function getDate() { const dayOfMonth = date.getDate(); const month = locale.month(date, 1); const year = date.getFullYear(); - let suffix; if ([1, 21, 31].includes(dayOfMonth)) { suffix = "st"; @@ -99,9 +98,7 @@ function getDate() { } else { suffix = "th"; } - let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; - return month + " " + dayOfMonthStr + ", " + year; } @@ -206,11 +203,9 @@ function setup() { delete Graphics.prototype.setFontBrunoAce; g.drawString = g_drawString; require("widget_utils").show(); - setCustomFont(); } }); - - setCustomFont(); + loadCustomFont(); draw(boxes); } From 6519ef43011b1b3d6324e4e81b752c91919a7d41 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 08:52:48 -0500 Subject: [PATCH 12/48] Rename img to show app name, move json to data --- apps/boxclk/metadata.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json index d5b58a3c7..fad943aac 100644 --- a/apps/boxclk/metadata.json +++ b/apps/boxclk/metadata.json @@ -15,8 +15,10 @@ "allow_emulator": true, "storage": [ {"name":"boxclk.app.js","url":"app.js"}, - {"name":"boxclk.json","url":"boxclk.json"}, {"name":"boxclk.img","url":"icon.js","evaluate":true}, - {"name":"beachhouse.img","url":"beachhouse.js","evaluate":true} + {"name":"boxclk.beachhouse.img","url":"beachhouse.js","evaluate":true}, + ], + "data": [ + {"name":"boxclk.json","url":"boxclk.json"} ] } From 0a4e01fd2c356b222167dfe57475953cb1e978a0 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 08:55:44 -0500 Subject: [PATCH 13/48] Rename img to show app, adjust border pos on batt --- apps/boxclk/boxclk.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/boxclk/boxclk.json b/apps/boxclk/boxclk.json index 0d2fa732f..047ab6224 100644 --- a/apps/boxclk/boxclk.json +++ b/apps/boxclk/boxclk.json @@ -47,11 +47,11 @@ "border": "#000", "xPadding": -0.5, "yPadding": -0.5, - "xOffset": 1, + "xOffset": 2, "yOffset": 1, "boxPos": { "x": 0.92, "y": 0.95 } }, "bg": { - "img": "beachhouse.img" + "img": "boxclk.beachhouse.img" } } From c734ef8347f9c3c20d08b957c7b3e94b00812f80 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 09:24:11 -0500 Subject: [PATCH 14/48] Wrap program in IIFE so variables don't leak --- apps/boxclk/app.js | 424 +++++++++++++++++++++++---------------------- 1 file changed, 213 insertions(+), 211 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index a2202cb68..bf4309737 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -1,223 +1,225 @@ -let w = g.getWidth(); -let h = g.getHeight(); -let totalWidth, totalHeight; -let touchHandler; -let dragHandler; -let drawTimeout; -let enableSuffix = true; -let storage = require("Storage"); -let locale = require("locale"); -let date = new Date(); +(function() { + let w = g.getWidth(); + let h = g.getHeight(); + let totalWidth, totalHeight; + let touchHandler; + let dragHandler; + let drawTimeout; + let enableSuffix = true; + let storage = require("Storage"); + let locale = require("locale"); + let date = new Date(); -let bgImage; -let boxesConfig = storage.readJSON('boxclk.json', 1) || {}; -let boxes = {}; + let bgImage; + let boxesConfig = storage.readJSON('boxclk.json', 1) || {}; + let boxes = {}; -function loadCustomFont() { - Graphics.prototype.setFontBrunoAce = function() { - // Actual height 23 (24 - 2) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), - 46, - atob("CBEdChgYGhgaGBsaCQ=="), - 32|65536 - ); - }; -} - -for (let key in boxesConfig) { - if (key === 'bg' && boxesConfig[key].img) { - bgImage = storage.read(boxesConfig[key].img); - } else { - boxes[key] = Object.assign({}, boxesConfig[key]); + function loadCustomFont() { + Graphics.prototype.setFontBrunoAce = function() { + // Actual height 23 (24 - 2) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), + 46, + atob("CBEdChgYGhgaGBsaCQ=="), + 32|65536 + ); + }; } -} -let boxPos = {}; -let isDragging = {}; -let wasDragging = {}; - -Object.keys(boxes).forEach((boxKey) => { - let boxConfig = boxes[boxKey]; - boxPos[boxKey] = { - x: w * boxConfig.boxPos.x, - y: h * boxConfig.boxPos.y - }; - isDragging[boxKey] = false; - wasDragging[boxKey] = false; -}); - -let g_drawString = g.drawString; -g.drawString = function(box, str, x, y) { - outlineText(box, str, x, y); - g.setColor(box.color); - g_drawString.call(g, str, x, y); -}; - -function outlineText(box, str, x, y) { - let px = box.outline; - let dx = [-px, 0, px, -px, px, -px, 0, px]; - let dy = [-px, -px, -px, 0, 0, px, px, px]; - g.setColor(box.outlineColor); - for (let i = 0; i < dx.length; i++) { - g_drawString.call(g, str, x + dx[i], y + dy[i]); + for (let key in boxesConfig) { + if (key === 'bg' && boxesConfig[key].img) { + bgImage = storage.read(boxesConfig[key].img); + } else { + boxes[key] = Object.assign({}, boxesConfig[key]); + } } -} -function calcBoxSize(boxItem) { - g.reset(); - g.setFontAlign(0,0); - g.setFont(boxItem.font, boxItem.fontSize); - let strWidth = g.stringWidth(boxItem.string) + 2 * boxItem.outline; - let fontHeight = g.getFontHeight() + 2 * boxItem.outline; - totalWidth = strWidth + 2 * boxItem.xPadding; - totalHeight = fontHeight + 2 * boxItem.yPadding; -} + let boxPos = {}; + let isDragging = {}; + let wasDragging = {}; -function calcBoxPos(boxKey) { - return { - x1: boxPos[boxKey].x - totalWidth / 2, - y1: boxPos[boxKey].y - totalHeight / 2, - x2: boxPos[boxKey].x + totalWidth / 2, - y2: boxPos[boxKey].y + totalHeight / 2 - }; -} - -function getDate() { - const date = new Date(); - const dayOfMonth = date.getDate(); - const month = locale.month(date, 1); - const year = date.getFullYear(); - let suffix; - if ([1, 21, 31].includes(dayOfMonth)) { - suffix = "st"; - } else if ([2, 22].includes(dayOfMonth)) { - suffix = "nd"; - } else if ([3, 23].includes(dayOfMonth)) { - suffix = "rd"; - } else { - suffix = "th"; - } - let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; - return month + " " + dayOfMonthStr + ", " + year; -} - -function getDayOfWeek(date) { - return locale.dow(date, 0); -} - -function draw(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() + "%"; - } Object.keys(boxes).forEach((boxKey) => { - let boxItem = boxes[boxKey]; + let boxConfig = boxes[boxKey]; + boxPos[boxKey] = { + x: w * boxConfig.boxPos.x, + y: h * boxConfig.boxPos.y + }; + isDragging[boxKey] = false; + wasDragging[boxKey] = false; + }); + + let g_drawString = g.drawString; + g.drawString = function(box, str, x, y) { + outlineText(box, str, x, y); + g.setColor(box.color); + g_drawString.call(g, str, x, y); + }; + + function outlineText(box, str, x, y) { + let px = box.outline; + let dx = [-px, 0, px, -px, px, -px, 0, px]; + let dy = [-px, -px, -px, 0, 0, px, px, px]; + g.setColor(box.outlineColor); + for (let i = 0; i < dx.length; i++) { + g_drawString.call(g, str, x + dx[i], y + dy[i]); + } + } + + function calcBoxSize(boxItem) { + g.reset(); + g.setFontAlign(0,0); + g.setFont(boxItem.font, boxItem.fontSize); + let strWidth = g.stringWidth(boxItem.string) + 2 * boxItem.outline; + let fontHeight = g.getFontHeight() + 2 * boxItem.outline; + totalWidth = strWidth + 2 * boxItem.xPadding; + totalHeight = fontHeight + 2 * boxItem.yPadding; + } + + function calcBoxPos(boxKey) { + return { + x1: boxPos[boxKey].x - totalWidth / 2, + y1: boxPos[boxKey].y - totalHeight / 2, + x2: boxPos[boxKey].x + totalWidth / 2, + y2: boxPos[boxKey].y + totalHeight / 2 + }; + } + + function getDate() { + const date = new Date(); + const dayOfMonth = date.getDate(); + const month = locale.month(date, 1); + const year = date.getFullYear(); + let suffix; + if ([1, 21, 31].includes(dayOfMonth)) { + suffix = "st"; + } else if ([2, 22].includes(dayOfMonth)) { + suffix = "nd"; + } else if ([3, 23].includes(dayOfMonth)) { + suffix = "rd"; + } else { + suffix = "th"; + } + let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; + return month + " " + dayOfMonthStr + ", " + year; + } + + function getDayOfWeek(date) { + return locale.dow(date, 0); + } + + function draw(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() + "%"; + } + Object.keys(boxes).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); + drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); + } + } + + function setup() { + // Define the touchHandler function + touchHandler = function(zone, e) { + wasDragging = Object.assign({}, isDragging); + let boxTouched = false; + Object.keys(boxes).forEach((boxKey) => { + if (touchInText(e, boxes[boxKey], boxKey)) { + isDragging[boxKey] = true; + wasDragging[boxKey] = true; + boxTouched = true; + } + }); + if (!boxTouched) { + Object.keys(isDragging).forEach((boxKey) => { + isDragging[boxKey] = false; + }); + } + if (Object.values(wasDragging).some(Boolean) || !boxTouched) { + draw(boxes); + } + }; + + // Define the dragHandler function + dragHandler = function(e) { + Object.keys(boxes).forEach((boxKey) => { + if (isDragging[boxKey]) { + let boxItem = boxes[boxKey]; + calcBoxSize(boxItem); + let newX = boxPos[boxKey].x + e.dx; + let newY = boxPos[boxKey].y + e.dy; + if (newX - totalWidth / 2 >= 0 && + newX + totalWidth / 2 <= w && + newY - totalHeight / 2 >= 0 && + newY + totalHeight / 2 <= h ) { + boxPos[boxKey].x = newX; + boxPos[boxKey].y = newY; + } + const pos = (boxKey); + g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); + } + }); + draw(boxes); + }; + + Bangle.on('touch', touchHandler); + Bangle.on('drag', dragHandler); + + Bangle.setUI({ + mode : "clock", + remove : function() { + // remove the event handlers when the app should be removed + Bangle.removeListener('touch', touchHandler); + Bangle.removeListener('drag', dragHandler); + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + delete Graphics.prototype.setFontBrunoAce; + g.drawString = g_drawString; + require("widget_utils").show(); + } + }); + loadCustomFont(); + draw(boxes); + } + + function touchInText(e, boxItem, 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); - drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); + return e.x >= pos.x1 && + e.x <= pos.x2 && + e.y >= pos.y1 && + e.y <= pos.y2; } -} -function setup() { - // Define the touchHandler function - touchHandler = function(zone, e) { - wasDragging = Object.assign({}, isDragging); - let boxTouched = false; - Object.keys(boxes).forEach((boxKey) => { - if (touchInText(e, boxes[boxKey], boxKey)) { - isDragging[boxKey] = true; - wasDragging[boxKey] = true; - boxTouched = true; - } - }); - if (!boxTouched) { - Object.keys(isDragging).forEach((boxKey) => { - isDragging[boxKey] = false; - }); - } - if (Object.values(wasDragging).some(Boolean) || !boxTouched) { - draw(boxes); - } - }; - - // Define the dragHandler function - dragHandler = function(e) { - Object.keys(boxes).forEach((boxKey) => { - if (isDragging[boxKey]) { - let boxItem = boxes[boxKey]; - calcBoxSize(boxItem); - let newX = boxPos[boxKey].x + e.dx; - let newY = boxPos[boxKey].y + e.dy; - if (newX - totalWidth / 2 >= 0 && - newX + totalWidth / 2 <= w && - newY - totalHeight / 2 >= 0 && - newY + totalHeight / 2 <= h ) { - boxPos[boxKey].x = newX; - boxPos[boxKey].y = newY; - } - const pos = (boxKey); - g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); - } - }); - draw(boxes); - }; - - Bangle.on('touch', touchHandler); - Bangle.on('drag', dragHandler); - - Bangle.setUI({ - mode : "clock", - remove : function() { - // remove the event handlers when the app should be removed - Bangle.removeListener('touch', touchHandler); - Bangle.removeListener('drag', dragHandler); - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = undefined; - delete Graphics.prototype.setFontBrunoAce; - g.drawString = g_drawString; - require("widget_utils").show(); - } - }); - loadCustomFont(); - draw(boxes); -} - -function touchInText(e, boxItem, boxKey) { - calcBoxSize(boxItem); - const pos = calcBoxPos(boxKey); - return e.x >= pos.x1 && - e.x <= pos.x2 && - e.y >= pos.y1 && - e.y <= pos.y2; -} - -Bangle.loadWidgets(); -require("widget_utils").swipeOn(); -setup(); + Bangle.loadWidgets(); + require("widget_utils").swipeOn(); + setup(); +})(); \ No newline at end of file From a15c69f06836dcdab8eba0439610a097e107cf31 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 09:26:14 -0500 Subject: [PATCH 15/48] Remove extra comma --- apps/boxclk/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json index fad943aac..8ec9f1bba 100644 --- a/apps/boxclk/metadata.json +++ b/apps/boxclk/metadata.json @@ -16,7 +16,7 @@ "storage": [ {"name":"boxclk.app.js","url":"app.js"}, {"name":"boxclk.img","url":"icon.js","evaluate":true}, - {"name":"boxclk.beachhouse.img","url":"beachhouse.js","evaluate":true}, + {"name":"boxclk.beachhouse.img","url":"beachhouse.js","evaluate":true} ], "data": [ {"name":"boxclk.json","url":"boxclk.json"} From 004e5ae37627b556effbc62d09fea6c519915075 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 09:56:25 -0500 Subject: [PATCH 16/48] Added comments and reorgranized --- apps/boxclk/app.js | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index bf4309737..f0b9fba9e 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -1,19 +1,27 @@ (function() { - let w = g.getWidth(); - let h = g.getHeight(); - let totalWidth, totalHeight; - let touchHandler; - let dragHandler; - let drawTimeout; - let enableSuffix = true; + // 1. Module dependencies and initial configurations let storage = require("Storage"); let locale = require("locale"); let date = new Date(); - let bgImage; let boxesConfig = storage.readJSON('boxclk.json', 1) || {}; let boxes = {}; + let boxPos = {}; + let isDragging = {}; + let wasDragging = {}; + // 2. Graphical and visual configurations + let w = g.getWidth(); + let h = g.getHeight(); + let totalWidth, totalHeight; + let enableSuffix = true; + let drawTimeout; + + // 3. Handlers + let touchHandler; + let dragHandler; + + // 4. Font loading function function loadCustomFont() { Graphics.prototype.setFontBrunoAce = function() { // Actual height 23 (24 - 2) @@ -26,6 +34,7 @@ }; } + // 5. Initial settings of boxes and their positions for (let key in boxesConfig) { if (key === 'bg' && boxesConfig[key].img) { bgImage = storage.read(boxesConfig[key].img); @@ -34,10 +43,6 @@ } } - let boxPos = {}; - let isDragging = {}; - let wasDragging = {}; - Object.keys(boxes).forEach((boxKey) => { let boxConfig = boxes[boxKey]; boxPos[boxKey] = { @@ -48,6 +53,7 @@ wasDragging[boxKey] = false; }); + // 6. Text and drawing functions let g_drawString = g.drawString; g.drawString = function(box, str, x, y) { outlineText(box, str, x, y); @@ -84,6 +90,7 @@ }; } + // 7. Date and time related functions function getDate() { const date = new Date(); const dayOfMonth = date.getDate(); @@ -107,6 +114,7 @@ return locale.dow(date, 0); } + // 8. Main draw function function draw(boxes) { date = new Date(); g.clear(); @@ -146,6 +154,7 @@ } } + // 9. Setup function to configure event handlers function setup() { // Define the touchHandler function touchHandler = function(zone, e) { @@ -210,6 +219,7 @@ draw(boxes); } + // 10. Helper function for touch event function touchInText(e, boxItem, boxKey) { calcBoxSize(boxItem); const pos = calcBoxPos(boxKey); @@ -219,7 +229,8 @@ e.y <= pos.y2; } + // 11. Main execution part Bangle.loadWidgets(); require("widget_utils").swipeOn(); setup(); -})(); \ No newline at end of file +})(); From 0d9805d4ed24a9f35ca97e648e80f429959637b5 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 11:00:11 -0500 Subject: [PATCH 17/48] Switch from function declarations to expressions --- apps/boxclk/app.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index f0b9fba9e..3bab2e3f1 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -1,4 +1,4 @@ -(function() { +{ // 1. Module dependencies and initial configurations let storage = require("Storage"); let locale = require("locale"); @@ -22,7 +22,7 @@ let dragHandler; // 4. Font loading function - function loadCustomFont() { + let loadCustomFont = function() { Graphics.prototype.setFontBrunoAce = function() { // Actual height 23 (24 - 2) return this.setFontCustom( @@ -32,7 +32,7 @@ 32|65536 ); }; - } + }; // 5. Initial settings of boxes and their positions for (let key in boxesConfig) { @@ -61,7 +61,7 @@ g_drawString.call(g, str, x, y); }; - function outlineText(box, str, x, y) { + let outlineText = function(box, str, x, y) { let px = box.outline; let dx = [-px, 0, px, -px, px, -px, 0, px]; let dy = [-px, -px, -px, 0, 0, px, px, px]; @@ -69,9 +69,9 @@ for (let i = 0; i < dx.length; i++) { g_drawString.call(g, str, x + dx[i], y + dy[i]); } - } + }; - function calcBoxSize(boxItem) { + let calcBoxSize = function(boxItem) { g.reset(); g.setFontAlign(0,0); g.setFont(boxItem.font, boxItem.fontSize); @@ -79,19 +79,19 @@ let fontHeight = g.getFontHeight() + 2 * boxItem.outline; totalWidth = strWidth + 2 * boxItem.xPadding; totalHeight = fontHeight + 2 * boxItem.yPadding; - } + }; - function calcBoxPos(boxKey) { + let calcBoxPos = function(boxKey) { return { x1: boxPos[boxKey].x - totalWidth / 2, y1: boxPos[boxKey].y - totalHeight / 2, x2: boxPos[boxKey].x + totalWidth / 2, y2: boxPos[boxKey].y + totalHeight / 2 }; - } + }; // 7. Date and time related functions - function getDate() { + let getDate = function() { const date = new Date(); const dayOfMonth = date.getDate(); const month = locale.month(date, 1); @@ -108,14 +108,14 @@ } let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; return month + " " + dayOfMonthStr + ", " + year; - } + }; - function getDayOfWeek(date) { + let getDayOfWeek = function(date) { return locale.dow(date, 0); - } + }; // 8. Main draw function - function draw(boxes) { + let draw = function(boxes) { date = new Date(); g.clear(); if (bgImage) { @@ -152,10 +152,10 @@ if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); } - } + }; // 9. Setup function to configure event handlers - function setup() { + let setup = function() { // Define the touchHandler function touchHandler = function(zone, e) { wasDragging = Object.assign({}, isDragging); @@ -192,7 +192,7 @@ boxPos[boxKey].x = newX; boxPos[boxKey].y = newY; } - const pos = (boxKey); + const pos = calcBoxPos(boxKey); g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); } }); @@ -217,20 +217,20 @@ }); loadCustomFont(); draw(boxes); - } + }; // 10. Helper function for touch event - function touchInText(e, boxItem, boxKey) { + let touchInText = function(e, boxItem, boxKey) { calcBoxSize(boxItem); const pos = calcBoxPos(boxKey); return e.x >= pos.x1 && e.x <= pos.x2 && e.y >= pos.y1 && e.y <= pos.y2; - } + }; // 11. Main execution part Bangle.loadWidgets(); require("widget_utils").swipeOn(); setup(); -})(); +} From 6fe81e3279e75d8b48c5aed34046164cb2f2a483 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 11:43:44 -0500 Subject: [PATCH 18/48] Hid widgets when dragging and reenable when done --- apps/boxclk/app.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 3bab2e3f1..d8b626f8e 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -171,6 +171,8 @@ Object.keys(isDragging).forEach((boxKey) => { isDragging[boxKey] = false; }); + require("widget_utils").show(); + require("widget_utils").swipeOn(); } if (Object.values(wasDragging).some(Boolean) || !boxTouched) { draw(boxes); @@ -181,6 +183,7 @@ dragHandler = function(e) { Object.keys(boxes).forEach((boxKey) => { if (isDragging[boxKey]) { + require("widget_utils").hide(); let boxItem = boxes[boxKey]; calcBoxSize(boxItem); let newX = boxPos[boxKey].x + e.dx; From 47451511b5daeb05d21490de72021ceb0316651e Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 12:57:58 -0500 Subject: [PATCH 19/48] Change order, add more comments for setUI --- apps/boxclk/app.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index d8b626f8e..9fbb9777a 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -154,7 +154,17 @@ } }; - // 9. Setup function to configure event handlers + // 9. Helper function for touch event + let touchInText = function(e, boxItem, boxKey) { + calcBoxSize(boxItem); + const pos = calcBoxPos(boxKey); + return e.x >= pos.x1 && + e.x <= pos.x2 && + e.y >= pos.y1 && + e.y <= pos.y2; + }; + + // 10. Setup function to configure event handlers let setup = function() { // Define the touchHandler function touchHandler = function(zone, e) { @@ -208,13 +218,13 @@ Bangle.setUI({ mode : "clock", remove : function() { - // remove the event handlers when the app should be removed + // Remove event handlers, stop draw timer, remove custom font if used Bangle.removeListener('touch', touchHandler); Bangle.removeListener('drag', dragHandler); if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; delete Graphics.prototype.setFontBrunoAce; - g.drawString = g_drawString; + g.drawString = g_drawString; // Return to original without outlines require("widget_utils").show(); } }); @@ -222,16 +232,6 @@ draw(boxes); }; - // 10. Helper function for touch event - let touchInText = function(e, boxItem, boxKey) { - calcBoxSize(boxItem); - const pos = calcBoxPos(boxKey); - return e.x >= pos.x1 && - e.x <= pos.x2 && - e.y >= pos.y1 && - e.y <= pos.y2; - }; - // 11. Main execution part Bangle.loadWidgets(); require("widget_utils").swipeOn(); From b0ffa73a01254793e5ce2736ff80847a1302027c Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 14:25:23 -0500 Subject: [PATCH 20/48] Increased contrast of bg --- apps/boxclk/beachhouse.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/beachhouse.js b/apps/boxclk/beachhouse.js index 062a6b9d3..ef0648658 100644 --- a/apps/boxclk/beachhouse.js +++ b/apps/boxclk/beachhouse.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("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")) +require("heatshrink").decompress(atob("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")) From 51e1f9e7da7cfd402e561347cca9d2144ca13e47 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 19:04:20 -0500 Subject: [PATCH 21/48] Add menu for multiple configs, update README --- apps/boxclk/README.md | 19 +++++++--- apps/boxclk/metadata.json | 1 + apps/boxclk/settings.js | 75 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 apps/boxclk/settings.js diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index 613c74fe0..7a0f3bb81 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -10,13 +10,15 @@ This intuitive feature allows you to reposition any element (box) on the clock f __JSON Configuration:__ -Each box can be customized extensively via a simple JSON configuration. You can also add a custom text string to your configuration with the "string": "Your custom text here", attribute. Here's what an example configuration might look like: +Each box can be customized extensively via a simple JSON configuration. You can also add a custom text string to your configuration with the "string" attribute. Here's what an example configuration might look like: + +## Config File Structure ``` { - "customBox": { - "string": "Your custom text here", - "font": "CustomFont", + "customBox": { // + "string": "Your text here", + "font": "CustomFont", // Custom fonts must be in main program and removed in setUI "fontSize": 1, "outline": 2, "color": "#FF9900", @@ -28,11 +30,12 @@ Each box can be customized extensively via a simple JSON configuration. You can "yOffset": 3, "boxPos": { "x": 0.5, "y": 0.5 } }, - "bg": { + "bg": { // Can also be removed for no backround "img": "YourImageName.img" } } ``` + * **string:** The text string to be displayed inside the box. * **font:** The font name given to g.setFont() @@ -55,6 +58,12 @@ Each box can be customized extensively via a simple JSON configuration. You can * **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 + +The app includes a settings menu that allows you to switch between different configurations. The selected configuration is stored in the default JSON file alongside the other configuration data using the selectedConfig property. + +If the selectedConfig property is not present or is set to 0, the app will use the default configuration. To create additional configurations, create separate JSON files with the naming convention boxclk-N.json, where N is the configuration number. The settings menu will list all available configurations. + ## Compatibility This app was built and tested with Bangle.js 2. diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json index 8ec9f1bba..351686fe7 100644 --- a/apps/boxclk/metadata.json +++ b/apps/boxclk/metadata.json @@ -15,6 +15,7 @@ "allow_emulator": true, "storage": [ {"name":"boxclk.app.js","url":"app.js"}, + {"name": "boxclk.settings.js","url":"settings.js"}, {"name":"boxclk.img","url":"icon.js","evaluate":true}, {"name":"boxclk.beachhouse.img","url":"beachhouse.js","evaluate":true} ], diff --git a/apps/boxclk/settings.js b/apps/boxclk/settings.js new file mode 100644 index 000000000..7d6262a65 --- /dev/null +++ b/apps/boxclk/settings.js @@ -0,0 +1,75 @@ +let storage = require("Storage"); +let fileRegex = /^boxclk-(\d+)\.json$/; +let selectedConfig; + +function getNextConfigNumber() { + let maxNumber = 0; + storage.list().forEach(file => { + let match = file.match(fileRegex); + if (match) { + let number = parseInt(match[1]); + if (number > maxNumber) { + maxNumber = number; + } + } + }); + return maxNumber + 1; +} + +(function () { + let configs = {}; + let hasDefaultConfig = false; + + function handleSelection(config) { + return function () { + selectedConfig = config === "Default" ? 0 : config; + menu["Cfg:"].value = selectedConfig === 0 ? "Default" : selectedConfig; + E.showMenu(menu); + + // Retrieve existing data and update selectedConfig + let defaultConfig = storage.readJSON("boxclk.json", 1) || {}; + defaultConfig.selectedConfig = selectedConfig; + storage.writeJSON("boxclk.json", defaultConfig); + }; + } + + storage.list().forEach(file => { + let match = file.match(fileRegex); + if (match) { + let configNumber = match[1]; + configs[configNumber] = handleSelection(configNumber); + } else if (file === "boxclk.json") { + hasDefaultConfig = true; + let defaultConfig = storage.readJSON(file, 1); + if (defaultConfig && defaultConfig.selectedConfig) { + selectedConfig = defaultConfig.selectedConfig === 0 ? 0 : defaultConfig.selectedConfig; + } + } + }); + + if (!selectedConfig) { + if (hasDefaultConfig) { + selectedConfig = "Default"; + } else { + let nextConfigNumber = getNextConfigNumber(); + selectedConfig = nextConfigNumber.toString(); + configs[selectedConfig] = handleSelection(selectedConfig); + } + } + + let menu = { + '': { 'title': '-- Box Clock --' }, + '< Back': () => Bangle.showLauncher(), + 'Cfg:': { value: selectedConfig === 0 ? "Default" : selectedConfig, format: () => selectedConfig === 0 ? "Default" : selectedConfig }, + }; + + if (hasDefaultConfig) { + menu['Default'] = handleSelection('Default'); + } + + Object.keys(configs).forEach(config => { + menu[config] = handleSelection(config); + }); + + E.showMenu(menu); +})(); From c3b07613e277b5e3e46013a26848b667ddeeb9dc Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:16:20 -0500 Subject: [PATCH 22/48] Update metadata.json - Removed space --- apps/boxclk/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/metadata.json b/apps/boxclk/metadata.json index 351686fe7..ad5ad72e9 100644 --- a/apps/boxclk/metadata.json +++ b/apps/boxclk/metadata.json @@ -15,7 +15,7 @@ "allow_emulator": true, "storage": [ {"name":"boxclk.app.js","url":"app.js"}, - {"name": "boxclk.settings.js","url":"settings.js"}, + {"name":"boxclk.settings.js","url":"settings.js"}, {"name":"boxclk.img","url":"icon.js","evaluate":true}, {"name":"boxclk.beachhouse.img","url":"beachhouse.js","evaluate":true} ], From 9c27ec1694295de71354b677b78a9a70f0fb3756 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 20:14:27 -0500 Subject: [PATCH 23/48] Update app for new menu, change screenshot --- apps/boxclk/app.js | 477 ++++++++++++++++++----------------- apps/boxclk/screenshot-1.png | Bin 5936 -> 6060 bytes apps/boxclk/settings.js | 37 ++- 3 files changed, 258 insertions(+), 256 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 9fbb9777a..1647bd731 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -1,239 +1,242 @@ { // 1. Module dependencies and initial configurations - let storage = require("Storage"); - let locale = require("locale"); - let date = new Date(); - let bgImage; - let boxesConfig = storage.readJSON('boxclk.json', 1) || {}; - let boxes = {}; - let boxPos = {}; - let isDragging = {}; - let wasDragging = {}; - - // 2. Graphical and visual configurations - let w = g.getWidth(); - let h = g.getHeight(); - let totalWidth, totalHeight; - let enableSuffix = true; - let drawTimeout; - - // 3. Handlers - let touchHandler; - let dragHandler; - - // 4. Font loading function - let loadCustomFont = function() { - Graphics.prototype.setFontBrunoAce = function() { - // Actual height 23 (24 - 2) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), - 46, - atob("CBEdChgYGhgaGBsaCQ=="), - 32|65536 - ); - }; - }; - - // 5. Initial settings of boxes and their positions - for (let key in boxesConfig) { - if (key === 'bg' && boxesConfig[key].img) { - bgImage = storage.read(boxesConfig[key].img); - } else { - boxes[key] = Object.assign({}, boxesConfig[key]); - } - } - - Object.keys(boxes).forEach((boxKey) => { - let boxConfig = boxes[boxKey]; - boxPos[boxKey] = { - x: w * boxConfig.boxPos.x, - y: h * boxConfig.boxPos.y - }; - isDragging[boxKey] = false; - wasDragging[boxKey] = false; - }); - - // 6. Text and drawing functions - let g_drawString = g.drawString; - g.drawString = function(box, str, x, y) { - outlineText(box, str, x, y); - g.setColor(box.color); - g_drawString.call(g, str, x, y); - }; - - let outlineText = function(box, str, x, y) { - let px = box.outline; - let dx = [-px, 0, px, -px, px, -px, 0, px]; - let dy = [-px, -px, -px, 0, 0, px, px, px]; - g.setColor(box.outlineColor); - for (let i = 0; i < dx.length; i++) { - g_drawString.call(g, str, x + dx[i], y + dy[i]); - } - }; - - let calcBoxSize = function(boxItem) { - g.reset(); - g.setFontAlign(0,0); - g.setFont(boxItem.font, boxItem.fontSize); - let strWidth = g.stringWidth(boxItem.string) + 2 * boxItem.outline; - let fontHeight = g.getFontHeight() + 2 * boxItem.outline; - totalWidth = strWidth + 2 * boxItem.xPadding; - totalHeight = fontHeight + 2 * boxItem.yPadding; - }; - - let calcBoxPos = function(boxKey) { - return { - x1: boxPos[boxKey].x - totalWidth / 2, - y1: boxPos[boxKey].y - totalHeight / 2, - x2: boxPos[boxKey].x + totalWidth / 2, - y2: boxPos[boxKey].y + totalHeight / 2 - }; - }; - - // 7. Date and time related functions - let getDate = function() { - const date = new Date(); - const dayOfMonth = date.getDate(); - const month = locale.month(date, 1); - const year = date.getFullYear(); - let suffix; - if ([1, 21, 31].includes(dayOfMonth)) { - suffix = "st"; - } else if ([2, 22].includes(dayOfMonth)) { - suffix = "nd"; - } else if ([3, 23].includes(dayOfMonth)) { - suffix = "rd"; - } else { - suffix = "th"; - } - let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; - return month + " " + dayOfMonthStr + ", " + year; - }; - - let getDayOfWeek = function(date) { - return locale.dow(date, 0); - }; - - // 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() + "%"; - } - Object.keys(boxes).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); - drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); - } - }; - - // 9. Helper function for touch event - let touchInText = function(e, boxItem, boxKey) { - calcBoxSize(boxItem); - const pos = calcBoxPos(boxKey); - return e.x >= pos.x1 && - e.x <= pos.x2 && - e.y >= pos.y1 && - e.y <= pos.y2; - }; - - // 10. Setup function to configure event handlers - let setup = function() { - // Define the touchHandler function - touchHandler = function(zone, e) { - wasDragging = Object.assign({}, isDragging); - let boxTouched = false; - Object.keys(boxes).forEach((boxKey) => { - if (touchInText(e, boxes[boxKey], boxKey)) { - isDragging[boxKey] = true; - wasDragging[boxKey] = true; - boxTouched = true; - } - }); - if (!boxTouched) { - Object.keys(isDragging).forEach((boxKey) => { - isDragging[boxKey] = false; - }); - require("widget_utils").show(); - require("widget_utils").swipeOn(); - } - if (Object.values(wasDragging).some(Boolean) || !boxTouched) { - draw(boxes); - } - }; - - // Define the dragHandler function - dragHandler = function(e) { - Object.keys(boxes).forEach((boxKey) => { - if (isDragging[boxKey]) { - require("widget_utils").hide(); - let boxItem = boxes[boxKey]; - calcBoxSize(boxItem); - let newX = boxPos[boxKey].x + e.dx; - let newY = boxPos[boxKey].y + e.dy; - if (newX - totalWidth / 2 >= 0 && - newX + totalWidth / 2 <= w && - newY - totalHeight / 2 >= 0 && - newY + totalHeight / 2 <= h ) { - boxPos[boxKey].x = newX; - boxPos[boxKey].y = newY; - } - const pos = calcBoxPos(boxKey); - g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); - } - }); - draw(boxes); - }; - - Bangle.on('touch', touchHandler); - Bangle.on('drag', dragHandler); - - Bangle.setUI({ - mode : "clock", - remove : function() { - // Remove event handlers, stop draw timer, remove custom font if used - Bangle.removeListener('touch', touchHandler); - Bangle.removeListener('drag', dragHandler); - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = undefined; - delete Graphics.prototype.setFontBrunoAce; - g.drawString = g_drawString; // Return to original without outlines - require("widget_utils").show(); - } - }); - loadCustomFont(); - draw(boxes); - }; - - // 11. Main execution part - Bangle.loadWidgets(); - require("widget_utils").swipeOn(); - setup(); -} + let storage = require("Storage"); + let locale = require("locale"); + let date = new Date(); + let bgImage; + let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0; + let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json'; + let boxesConfig = storage.readJSON(fileName, 1) || {}; + let boxes = {}; + let boxPos = {}; + let isDragging = {}; + let wasDragging = {}; + + // 2. Graphical and visual configurations + let w = g.getWidth(); + let h = g.getHeight(); + let totalWidth, totalHeight; + let enableSuffix = true; + let drawTimeout; + + // 3. Handlers + let touchHandler; + let dragHandler; + + // 4. Font loading function + let loadCustomFont = function() { + Graphics.prototype.setFontBrunoAce = function() { + // Actual height 23 (24 - 2) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), + 46, + atob("CBEdChgYGhgaGBsaCQ=="), + 32|65536 + ); + }; + }; + + // 5. Initial settings of boxes and their positions + for (let key in boxesConfig) { + if (key === 'bg' && boxesConfig[key].img) { + bgImage = storage.read(boxesConfig[key].img); + } else if (key !== 'selectedConfig') { + boxes[key] = Object.assign({}, boxesConfig[key]); + } + } + + Object.keys(boxes).forEach((key) => { + let boxConfig = boxes[key]; + boxPos[key] = { + x: w * boxConfig.boxPos.x, + y: h * boxConfig.boxPos.y + }; + isDragging[key] = false; + wasDragging[key] = false; + }); + + // 6. Text and drawing functions + let g_drawString = g.drawString; + g.drawString = function(box, str, x, y) { + outlineText(box, str, x, y); + g.setColor(box.color); + g_drawString.call(g, str, x, y); + }; + + let outlineText = function(box, str, x, y) { + let px = box.outline; + let dx = [-px, 0, px, -px, px, -px, 0, px]; + let dy = [-px, -px, -px, 0, 0, px, px, px]; + g.setColor(box.outlineColor); + for (let i = 0; i < dx.length; i++) { + g_drawString.call(g, str, x + dx[i], y + dy[i]); + } + }; + + let calcBoxSize = function(boxItem) { + g.reset(); + g.setFontAlign(0,0); + g.setFont(boxItem.font, boxItem.fontSize); + let strWidth = g.stringWidth(boxItem.string) + 2 * boxItem.outline; + let fontHeight = g.getFontHeight() + 2 * boxItem.outline; + totalWidth = strWidth + 2 * boxItem.xPadding; + totalHeight = fontHeight + 2 * boxItem.yPadding; + }; + + let calcBoxPos = function(boxKey) { + return { + x1: boxPos[boxKey].x - totalWidth / 2, + y1: boxPos[boxKey].y - totalHeight / 2, + x2: boxPos[boxKey].x + totalWidth / 2, + y2: boxPos[boxKey].y + totalHeight / 2 + }; + }; + + // 7. Date and time related functions + let getDate = function() { + const date = new Date(); + const dayOfMonth = date.getDate(); + const month = locale.month(date, 1); + const year = date.getFullYear(); + let suffix; + if ([1, 21, 31].includes(dayOfMonth)) { + suffix = "st"; + } else if ([2, 22].includes(dayOfMonth)) { + suffix = "nd"; + } else if ([3, 23].includes(dayOfMonth)) { + suffix = "rd"; + } else { + suffix = "th"; + } + let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; + return month + " " + dayOfMonthStr + ", " + year; + }; + + let getDayOfWeek = function(date) { + return locale.dow(date, 0); + }; + + // 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() + "%"; + } + Object.keys(boxes).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); + drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); + } + }; + + // 9. Helper function for touch event + let touchInText = function(e, boxItem, boxKey) { + calcBoxSize(boxItem); + const pos = calcBoxPos(boxKey); + return e.x >= pos.x1 && + e.x <= pos.x2 && + e.y >= pos.y1 && + e.y <= pos.y2; + }; + + // 10. Setup function to configure event handlers + let setup = function() { + // Define the touchHandler function + touchHandler = function(zone, e) { + wasDragging = Object.assign({}, isDragging); + let boxTouched = false; + Object.keys(boxes).forEach((boxKey) => { + if (touchInText(e, boxes[boxKey], boxKey)) { + isDragging[boxKey] = true; + wasDragging[boxKey] = true; + boxTouched = true; + } + }); + if (!boxTouched) { + Object.keys(isDragging).forEach((boxKey) => { + isDragging[boxKey] = false; + }); + require("widget_utils").show(); + require("widget_utils").swipeOn(); + } + if (Object.values(wasDragging).some(Boolean) || !boxTouched) { + draw(boxes); + } + }; + + // Define the dragHandler function + dragHandler = function(e) { + Object.keys(boxes).forEach((boxKey) => { + if (isDragging[boxKey]) { + require("widget_utils").hide(); + let boxItem = boxes[boxKey]; + calcBoxSize(boxItem); + let newX = boxPos[boxKey].x + e.dx; + let newY = boxPos[boxKey].y + e.dy; + if (newX - totalWidth / 2 >= 0 && + newX + totalWidth / 2 <= w && + newY - totalHeight / 2 >= 0 && + newY + totalHeight / 2 <= h ) { + boxPos[boxKey].x = newX; + boxPos[boxKey].y = newY; + } + const pos = calcBoxPos(boxKey); + g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); + } + }); + draw(boxes); + }; + + Bangle.on('touch', touchHandler); + Bangle.on('drag', dragHandler); + + Bangle.setUI({ + mode : "clock", + remove : function() { + // Remove event handlers, stop draw timer, remove custom font if used + Bangle.removeListener('touch', touchHandler); + Bangle.removeListener('drag', dragHandler); + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + delete Graphics.prototype.setFontBrunoAce; + g.drawString = g_drawString; // Return to original without outlines + require("widget_utils").show(); + } + }); + loadCustomFont(); + draw(boxes); + }; + + // 11. Main execution part + Bangle.loadWidgets(); + require("widget_utils").swipeOn(); + setup(); + } + \ No newline at end of file diff --git a/apps/boxclk/screenshot-1.png b/apps/boxclk/screenshot-1.png index 91bf880c0bfb3a7d138f49c59280f6e11d6d0cb1..9b270178ec7f7429be099d26a6da548a8994af58 100644 GIT binary patch literal 6060 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 Scg@=X0000 { - let match = file.match(fileRegex); - if (match) { - let number = parseInt(match[1]); - if (number > maxNumber) { - maxNumber = number; - } - } - }); - return maxNumber + 1; -} - (function () { + let storage = require("Storage"); + let fileRegex = /^boxclk-(\d+)\.json$/; + let selectedConfig; let configs = {}; let hasDefaultConfig = false; + function getNextConfigNumber() { + let maxNumber = 0; + storage.list().forEach(file => { + let match = file.match(fileRegex); + if (match) { + let number = parseInt(match[1]); + if (number > maxNumber) { + maxNumber = number; + } + } + }); + return maxNumber + 1; + } + function handleSelection(config) { return function () { selectedConfig = config === "Default" ? 0 : config; @@ -72,4 +71,4 @@ function getNextConfigNumber() { }); E.showMenu(menu); -})(); +}); From e3cab1d4e6d19f54256a272f7b51e545880ab5e9 Mon Sep 17 00:00:00 2001 From: stweedo Date: Fri, 16 Jun 2023 23:34:50 -0500 Subject: [PATCH 24/48] Formatting --- apps/boxclk/app.js | 479 ++++++++++++++++++++++----------------------- 1 file changed, 239 insertions(+), 240 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 1647bd731..5cadac83d 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -1,242 +1,241 @@ { // 1. Module dependencies and initial configurations - let storage = require("Storage"); - let locale = require("locale"); - let date = new Date(); - let bgImage; - let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0; - let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json'; - let boxesConfig = storage.readJSON(fileName, 1) || {}; - let boxes = {}; - let boxPos = {}; - let isDragging = {}; - let wasDragging = {}; - - // 2. Graphical and visual configurations - let w = g.getWidth(); - let h = g.getHeight(); - let totalWidth, totalHeight; - let enableSuffix = true; - let drawTimeout; - - // 3. Handlers - let touchHandler; - let dragHandler; - - // 4. Font loading function - let loadCustomFont = function() { - Graphics.prototype.setFontBrunoAce = function() { - // Actual height 23 (24 - 2) - return this.setFontCustom( - E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), - 46, - atob("CBEdChgYGhgaGBsaCQ=="), - 32|65536 - ); - }; - }; - - // 5. Initial settings of boxes and their positions - for (let key in boxesConfig) { - if (key === 'bg' && boxesConfig[key].img) { - bgImage = storage.read(boxesConfig[key].img); - } else if (key !== 'selectedConfig') { - boxes[key] = Object.assign({}, boxesConfig[key]); - } - } - - Object.keys(boxes).forEach((key) => { - let boxConfig = boxes[key]; - boxPos[key] = { - x: w * boxConfig.boxPos.x, - y: h * boxConfig.boxPos.y - }; - isDragging[key] = false; - wasDragging[key] = false; - }); - - // 6. Text and drawing functions - let g_drawString = g.drawString; - g.drawString = function(box, str, x, y) { - outlineText(box, str, x, y); - g.setColor(box.color); - g_drawString.call(g, str, x, y); - }; - - let outlineText = function(box, str, x, y) { - let px = box.outline; - let dx = [-px, 0, px, -px, px, -px, 0, px]; - let dy = [-px, -px, -px, 0, 0, px, px, px]; - g.setColor(box.outlineColor); - for (let i = 0; i < dx.length; i++) { - g_drawString.call(g, str, x + dx[i], y + dy[i]); - } - }; - - let calcBoxSize = function(boxItem) { - g.reset(); - g.setFontAlign(0,0); - g.setFont(boxItem.font, boxItem.fontSize); - let strWidth = g.stringWidth(boxItem.string) + 2 * boxItem.outline; - let fontHeight = g.getFontHeight() + 2 * boxItem.outline; - totalWidth = strWidth + 2 * boxItem.xPadding; - totalHeight = fontHeight + 2 * boxItem.yPadding; - }; - - let calcBoxPos = function(boxKey) { - return { - x1: boxPos[boxKey].x - totalWidth / 2, - y1: boxPos[boxKey].y - totalHeight / 2, - x2: boxPos[boxKey].x + totalWidth / 2, - y2: boxPos[boxKey].y + totalHeight / 2 - }; - }; - - // 7. Date and time related functions - let getDate = function() { - const date = new Date(); - const dayOfMonth = date.getDate(); - const month = locale.month(date, 1); - const year = date.getFullYear(); - let suffix; - if ([1, 21, 31].includes(dayOfMonth)) { - suffix = "st"; - } else if ([2, 22].includes(dayOfMonth)) { - suffix = "nd"; - } else if ([3, 23].includes(dayOfMonth)) { - suffix = "rd"; - } else { - suffix = "th"; - } - let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; - return month + " " + dayOfMonthStr + ", " + year; - }; - - let getDayOfWeek = function(date) { - return locale.dow(date, 0); - }; - - // 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() + "%"; - } - Object.keys(boxes).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); - drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); - } - }; - - // 9. Helper function for touch event - let touchInText = function(e, boxItem, boxKey) { - calcBoxSize(boxItem); - const pos = calcBoxPos(boxKey); - return e.x >= pos.x1 && - e.x <= pos.x2 && - e.y >= pos.y1 && - e.y <= pos.y2; - }; - - // 10. Setup function to configure event handlers - let setup = function() { - // Define the touchHandler function - touchHandler = function(zone, e) { - wasDragging = Object.assign({}, isDragging); - let boxTouched = false; - Object.keys(boxes).forEach((boxKey) => { - if (touchInText(e, boxes[boxKey], boxKey)) { - isDragging[boxKey] = true; - wasDragging[boxKey] = true; - boxTouched = true; - } - }); - if (!boxTouched) { - Object.keys(isDragging).forEach((boxKey) => { - isDragging[boxKey] = false; - }); - require("widget_utils").show(); - require("widget_utils").swipeOn(); - } - if (Object.values(wasDragging).some(Boolean) || !boxTouched) { - draw(boxes); - } - }; - - // Define the dragHandler function - dragHandler = function(e) { - Object.keys(boxes).forEach((boxKey) => { - if (isDragging[boxKey]) { - require("widget_utils").hide(); - let boxItem = boxes[boxKey]; - calcBoxSize(boxItem); - let newX = boxPos[boxKey].x + e.dx; - let newY = boxPos[boxKey].y + e.dy; - if (newX - totalWidth / 2 >= 0 && - newX + totalWidth / 2 <= w && - newY - totalHeight / 2 >= 0 && - newY + totalHeight / 2 <= h ) { - boxPos[boxKey].x = newX; - boxPos[boxKey].y = newY; - } - const pos = calcBoxPos(boxKey); - g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); - } - }); - draw(boxes); - }; - - Bangle.on('touch', touchHandler); - Bangle.on('drag', dragHandler); - - Bangle.setUI({ - mode : "clock", - remove : function() { - // Remove event handlers, stop draw timer, remove custom font if used - Bangle.removeListener('touch', touchHandler); - Bangle.removeListener('drag', dragHandler); - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = undefined; - delete Graphics.prototype.setFontBrunoAce; - g.drawString = g_drawString; // Return to original without outlines - require("widget_utils").show(); - } - }); - loadCustomFont(); - draw(boxes); - }; - - // 11. Main execution part - Bangle.loadWidgets(); - require("widget_utils").swipeOn(); - setup(); - } - \ No newline at end of file + let storage = require("Storage"); + let locale = require("locale"); + let date = new Date(); + let bgImage; + let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0; + let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json'; + let boxesConfig = storage.readJSON(fileName, 1) || {}; + let boxes = {}; + let boxPos = {}; + let isDragging = {}; + let wasDragging = {}; + + // 2. Graphical and visual configurations + let w = g.getWidth(); + let h = g.getHeight(); + let totalWidth, totalHeight; + let enableSuffix = true; + let drawTimeout; + + // 3. Handlers + let touchHandler; + let dragHandler; + + // 4. Font loading function + let loadCustomFont = function() { + Graphics.prototype.setFontBrunoAce = function() { + // Actual height 23 (24 - 2) + return this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('ABMHwADBh4DKg4bKgIPDAYUfAYV/AYX/AQMD/gmC+ADBn/AByE/GIU8AYUwLxcfAYX/8AnB//4JIP/FgMP4F+CQQBBjwJBFYRbBAd43DHoJpBh/g/xPEK4ZfDgEEORKDDAY8////wADLfZrTCgITBnhEBAYJMBAYMPw4DCM4QDjhwDCjwDBn0+AYMf/gDBh/4AYMH+ADBLpc4ToK/NGYZfnAYcfL4U/x5fBW4LvB/7vC+LvBgHAsBfIn76Cn4WBcYQDFEgJ+CQQYDyH4L/BAZbHLNYjjCAZc8ngDunycBZ4KkBa4KwBnEHY4UB+BfMgf/ZgMH/4XBc4cf4F/gE+ZgRjwAYcfj5jBM4U4M4RQBM4UA8BjIngDFEYJ8BAYUDAYQvCM4ZxBC4V+AYQvBnkBQ4M8gabBJQPAI4WAAYM/GYQaBAYJKCnqyCn5OCn4aBAYIaBAYJPCU4IABnBhIuDXCFAMD+Z/BY4IDBQwOPwEfv6TDAYUPAcwrDAYQ7BAYY/BI4cD8bLCK4RfEAA0BRYTeDcwIrFn0Pw43Bg4DugYDBjxBBU4SvDMYMH/5QBgP/LAQAP8EHN4UPwADHB4YAHA'))), + 46, + atob("CBEdChgYGhgaGBsaCQ=="), + 32|65536 + ); + }; + }; + + // 5. Initial settings of boxes and their positions + for (let key in boxesConfig) { + if (key === 'bg' && boxesConfig[key].img) { + bgImage = storage.read(boxesConfig[key].img); + } else if (key !== 'selectedConfig') { + boxes[key] = Object.assign({}, boxesConfig[key]); + } + } + + Object.keys(boxes).forEach((key) => { + let boxConfig = boxes[key]; + boxPos[key] = { + x: w * boxConfig.boxPos.x, + y: h * boxConfig.boxPos.y + }; + isDragging[key] = false; + wasDragging[key] = false; + }); + + // 6. Text and drawing functions + let g_drawString = g.drawString; + g.drawString = function(box, str, x, y) { + outlineText(box, str, x, y); + g.setColor(box.color); + g_drawString.call(g, str, x, y); + }; + + let outlineText = function(box, str, x, y) { + let px = box.outline; + let dx = [-px, 0, px, -px, px, -px, 0, px]; + let dy = [-px, -px, -px, 0, 0, px, px, px]; + g.setColor(box.outlineColor); + for (let i = 0; i < dx.length; i++) { + g_drawString.call(g, str, x + dx[i], y + dy[i]); + } + }; + + let calcBoxSize = function(boxItem) { + g.reset(); + g.setFontAlign(0,0); + g.setFont(boxItem.font, boxItem.fontSize); + let strWidth = g.stringWidth(boxItem.string) + 2 * boxItem.outline; + let fontHeight = g.getFontHeight() + 2 * boxItem.outline; + totalWidth = strWidth + 2 * boxItem.xPadding; + totalHeight = fontHeight + 2 * boxItem.yPadding; + }; + + let calcBoxPos = function(boxKey) { + return { + x1: boxPos[boxKey].x - totalWidth / 2, + y1: boxPos[boxKey].y - totalHeight / 2, + x2: boxPos[boxKey].x + totalWidth / 2, + y2: boxPos[boxKey].y + totalHeight / 2 + }; + }; + + // 7. Date and time related functions + let getDate = function() { + const date = new Date(); + const dayOfMonth = date.getDate(); + const month = locale.month(date, 1); + const year = date.getFullYear(); + let suffix; + if ([1, 21, 31].includes(dayOfMonth)) { + suffix = "st"; + } else if ([2, 22].includes(dayOfMonth)) { + suffix = "nd"; + } else if ([3, 23].includes(dayOfMonth)) { + suffix = "rd"; + } else { + suffix = "th"; + } + let dayOfMonthStr = enableSuffix ? dayOfMonth + suffix : dayOfMonth; + return month + " " + dayOfMonthStr + ", " + year; + }; + + let getDayOfWeek = function(date) { + return locale.dow(date, 0); + }; + + // 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() + "%"; + } + Object.keys(boxes).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); + drawTimeout = setTimeout(() => draw(boxes), 60000 - (Date.now() % 60000)); + } + }; + + // 9. Helper function for touch event + let touchInText = function(e, boxItem, boxKey) { + calcBoxSize(boxItem); + const pos = calcBoxPos(boxKey); + return e.x >= pos.x1 && + e.x <= pos.x2 && + e.y >= pos.y1 && + e.y <= pos.y2; + }; + + // 10. Setup function to configure event handlers + let setup = function() { + // Define the touchHandler function + touchHandler = function(zone, e) { + wasDragging = Object.assign({}, isDragging); + let boxTouched = false; + Object.keys(boxes).forEach((boxKey) => { + if (touchInText(e, boxes[boxKey], boxKey)) { + isDragging[boxKey] = true; + wasDragging[boxKey] = true; + boxTouched = true; + } + }); + if (!boxTouched) { + Object.keys(isDragging).forEach((boxKey) => { + isDragging[boxKey] = false; + }); + require("widget_utils").show(); + require("widget_utils").swipeOn(); + } + if (Object.values(wasDragging).some(Boolean) || !boxTouched) { + draw(boxes); + } + }; + + // Define the dragHandler function + dragHandler = function(e) { + Object.keys(boxes).forEach((boxKey) => { + if (isDragging[boxKey]) { + require("widget_utils").hide(); + let boxItem = boxes[boxKey]; + calcBoxSize(boxItem); + let newX = boxPos[boxKey].x + e.dx; + let newY = boxPos[boxKey].y + e.dy; + if (newX - totalWidth / 2 >= 0 && + newX + totalWidth / 2 <= w && + newY - totalHeight / 2 >= 0 && + newY + totalHeight / 2 <= h ) { + boxPos[boxKey].x = newX; + boxPos[boxKey].y = newY; + } + const pos = calcBoxPos(boxKey); + g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); + } + }); + draw(boxes); + }; + + Bangle.on('touch', touchHandler); + Bangle.on('drag', dragHandler); + + Bangle.setUI({ + mode : "clock", + remove : function() { + // Remove event handlers, stop draw timer, remove custom font if used + Bangle.removeListener('touch', touchHandler); + Bangle.removeListener('drag', dragHandler); + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + delete Graphics.prototype.setFontBrunoAce; + g.drawString = g_drawString; // Return to original without outlines + require("widget_utils").show(); + } + }); + loadCustomFont(); + draw(boxes); + }; + + // 11. Main execution part + Bangle.loadWidgets(); + require("widget_utils").swipeOn(); + setup(); +} From e94fc4368a82fbb7d9b75b7f756b5040036691c3 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 00:53:48 -0500 Subject: [PATCH 25/48] Add double tap to save positions --- apps/boxclk/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/settings.js b/apps/boxclk/settings.js index 529bea02c..bf254a860 100644 --- a/apps/boxclk/settings.js +++ b/apps/boxclk/settings.js @@ -58,7 +58,7 @@ let menu = { '': { 'title': '-- Box Clock --' }, - '< Back': () => Bangle.showLauncher(), + '< Back': () => Bangle.showClock(), 'Cfg:': { value: selectedConfig === 0 ? "Default" : selectedConfig, format: () => selectedConfig === 0 ? "Default" : selectedConfig }, }; From a87e867681d692f5eebc0ddd7174e2d781ce4e4e Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 00:54:40 -0500 Subject: [PATCH 26/48] Add double tap to save positions --- apps/boxclk/app.js | 60 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 5cadac83d..fe5e6b212 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -4,13 +4,18 @@ let locale = require("locale"); let date = new Date(); let bgImage; + let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0; let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json'; let boxesConfig = storage.readJSON(fileName, 1) || {}; + let boxes = {}; let boxPos = {}; let isDragging = {}; let wasDragging = {}; + let doubleTapTimer = null; + + let saveIcon = require("heatshrink").decompress(atob("mEwwkEogA/AHdP/4AK+gWVDBQWNAAIuVGBAIB+UQdhMfGBAHBCxUAgIXHIwPyCxQwEJAgXB+MAl/zBwQGBn8ggQjBGAQXG+EA/4XI/8gBIQXTGAMPC6n/C6HzkREBC6YACC6QAFC57aHCYIXOOgLsEn4XPABIX/C6vykQAEl6/WgCQBC5imFAAT2BC5gCBI4oUCC5x0IC/4X/C4K8Bl4XJ+TCCC4wKBABkvC4tEEoMQCxcBB4IWEC4XyDBUBFwIXGJAIAOIwowDABoWGGB4uHDBwWJAH4AzA")); // 2. Graphical and visual configurations let w = g.getWidth(); @@ -19,7 +24,7 @@ let enableSuffix = true; let drawTimeout; - // 3. Handlers + // 3. Touchscreen Handlers let touchHandler; let dragHandler; @@ -170,25 +175,44 @@ let setup = function() { // Define the touchHandler function touchHandler = function(zone, e) { - wasDragging = Object.assign({}, isDragging); - let boxTouched = false; - Object.keys(boxes).forEach((boxKey) => { - if (touchInText(e, boxes[boxKey], boxKey)) { - isDragging[boxKey] = true; - wasDragging[boxKey] = true; - boxTouched = true; - } - }); - if (!boxTouched) { - Object.keys(isDragging).forEach((boxKey) => { - isDragging[boxKey] = false; + if (doubleTapTimer) { + clearTimeout(doubleTapTimer); + doubleTapTimer = null; + Object.keys(boxPos).forEach((boxKey) => { + boxesConfig[boxKey].boxPos.x = boxPos[boxKey].x / w; + boxesConfig[boxKey].boxPos.y = boxPos[boxKey].y / h; }); - require("widget_utils").show(); - require("widget_utils").swipeOn(); - } - if (Object.values(wasDragging).some(Boolean) || !boxTouched) { - draw(boxes); + storage.write(fileName, JSON.stringify(boxesConfig)); + g.drawImage(saveIcon, w / 2 - 24, h / 2 - 24); + // Display save icon for 2 seconds + setTimeout(() => { + g.clearRect(w / 2 - 24, h / 2 - 24, w / 2 + 24, h / 2 + 24); + draw(boxes); + }, 2000); + return; } + doubleTapTimer = setTimeout(() => { + doubleTapTimer = null; + wasDragging = Object.assign({}, isDragging); + let boxTouched = false; + Object.keys(boxes).forEach((boxKey) => { + if (touchInText(e, boxes[boxKey], boxKey)) { + isDragging[boxKey] = true; + wasDragging[boxKey] = true; + boxTouched = true; + } + }); + if (!boxTouched) { + Object.keys(isDragging).forEach((boxKey) => { + isDragging[boxKey] = false; + }); + require("widget_utils").show(); + require("widget_utils").swipeOn(); + } + if (Object.values(wasDragging).some(Boolean) || !boxTouched) { + draw(boxes); + } + }, 300); // Increase or decrease this value based on the desired double tap timing }; // Define the dragHandler function From 4c897f4bea0da3a9ef383e585a6c7f59e55def50 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 00:57:08 -0500 Subject: [PATCH 27/48] Update screenshot --- apps/boxclk/screenshot-1.png | Bin 6060 -> 5798 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/boxclk/screenshot-1.png b/apps/boxclk/screenshot-1.png index 9b270178ec7f7429be099d26a6da548a8994af58..18798bb30832743407fecba85c8d47431fd88260 100644 GIT binary patch literal 5798 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 literal 6060 zcmV;d7gOkoP)Py1W=TXrRCr$Pol%ygAPhxo_P^+!qlgAf$V&*QRrXKMw1^Ncj}Q?5eLkPR&-?RQ z;Gq`yrvtq3y(D{oVlD96HNu_%z!8sm^LQ;V#sUDqy-Lx0|CowjpX+Ovh&BKKULNB$ zw)Z|;)&BneKA(U8KA(W!|CW4)|ID$=@M_%P0Xo1L3UgsoBmI5;KK}xJXdYIALj_MYECEgYlB*FfCc9##{Mex3qwf+q={9bRu` zcg4LxX;EdSihK=ZcVAY>r*8W? zy_Jp4XP(i?twlT1Z*!6`H%KeVm^qNtbbM)61w35NQ41Pa6oC|RwaqoYu=dPMJ3^d?b2=gZ28b&c zq8zW+%4W9C|8RGlGOED9i{{)^;##puMGD_q`g@`6N#g%+vO~Do0U!%yS56am+hbEH}_qD4Jzy7j&b6tmxBu;SRz=1ta!mo?>k0BXN}L{kT@=DPX4}$(E~4GRul-$Z z9@KkXV+gD$$)@dF>q@OhF=l(6^W(ra7(J&^_YI_7+6D zU+865D6t2Ya=yD+cH7AszsQAg2LOy$mHNC|TV8ctQ|jV6X2Z(Q(Uq6(rkm*#N9~0; zxg+835Ni15UWht8po6>)QF(89!*{(DUPg(tuiW&J(P#;x^o&nD%bn=oV6#Oxs$GJq z{E@Z5AP!RCG@P{C?%3RZ2c@pv3@utAb-J@++Z34Yt%z)XU2gm-=mfo74Dsh#-ig7R zyUgw;dLB=M<+`%g5NptnxnA@(W~8WlqZADRdTm(&&A=0Owe>16UC|ro{-lwdY(y6= z*QLO`l1fUe{au39d*2xNn@Sz9k~aQ9vU3I3JV>0NRonkt)hwD!^c7^)7bs08y6nu%I3t--T&|pJ-VQW5Kh_&Zn`5x{> z_JN6)?Rc|fW@t^rI4XLfJttfGc=u5Eh1~$ReP0aWOCVH%J<_>!h;-tW5B{lD{#-w4-$?nAV%`s?qwC&5EqU+U@)pXkH{x@FN6j$@1t%NLmF4I zK@q>!#@!>61lM`7WM6Rb+FXjKBXwl%O)%Sbul%;i=`bbw+HBac)tmyHdTj2oT_*#fDxP0K+?h*m^DzEK6 z6b8_zwJI=TD+1ebhFIf9$sgt>ahea@c+7PGZ}67uy@(};q;V;*bxrCNTC1x9siIX; zf%J$;5p|{cSOFGYK>dUtr1)df{ICL}ZUY|*JCr!Y2S!?#1y~5tWku!k{Tc<%mAePT zf&%j^Z&u}PMkiF&m7@FVymPZK9ye}sEKI({IJ1P`Up>|AF-Lw@HjI^n}Rvblavfh$uqqYW(?i&tRY zT|p<9M_DEI))cP-Tcra^KPd`F=wQgS4{U~4IV)rZ$d98R&>QZ#(x>%wWn*@PZ&0@&;OwzoRU3Y>>; z-vm+W6q1A?ok#~yatWFiZzwRVyh62p8Dds}&nK{GGmG4Gt#mEepw7mVCAbjh9BD$P zLrFzI1_dVTQ+DD+71(P3DzFGhw*q6Y`)EF}=JZ(=wUM5h!U^8VnjQJLq7YT9y$XEN zw)&g8&G~ghKIwf|D{u}4kk&VWcXSHZrhTHos_)xS+Jc-do_d z?d~hBTV(MnFlPkTh-2PklD$3D1T1%3pVa3Nm>Zko#1S=#PL$0-`G0-svz#tN); zLLXe$DDd%0KDLbf#B(CF_8N2jRp0>%+}>K+hFNHkRLWO@XDDzp76eGkn4Ws%iYa$& z6kdPU}Y|RCC*jp2{+l?howN`oWXmb--KEQ zJ;Ee)C*R!$Dds*L6E$F}0Den2$XL^|c@=n?srRseNLyGu_(v3Yu~$-6e2@4KDsZ3j zVj(hZFGXW9!%)0$^nEQK`fj=qB=87*uWPFC-R5CEzOZ#g?)#$<3%u()e})f>H{#}e zG$JkvaTSFIiEC^VyV?h~46|2R+Vb4&J}h3u`rRAQm1r*foME*T>6_EqIsv@7T{9W+ zg}=dlpmr%vsy`{Q9G!R-c>kmm24$b}0(&B`Vwfg6TEDpBdI@hM>ar^_5AmzOrq%=L zsyc;5fziDX-=M&!1;58qpT@+7$Q*AhbD90WRagv!J@`b1>Mk#g2ABlC{8P%g1v@;3N{b`sS2r z{Z9Nr8||*pSc4h z6GM~pTGRpXwO5ENTZ1a6>!|yk=O$Wer zV?MO=B+dL+f!UcQI1c$%%?Cdecs~*rshT6r4*`r=#|ODB)K=?t4_sw=Wd)uyp-S|gdJkfFcNzxZ zNtJaM*?OZ7$}vhTIUA_@HtJscz`OEV?d42gJin(?+MX{c@V7yX8f6CT_@NzWsjrVh zq!3>E3FwT5iW7jh#+&vIl%bTSq!@%_c9)?cvk{G4$AKq_P<9(GfH!v++I==uCRN@9 z=E%et^nGr{p}gKWuWo^>+U=_6vgZR|R~Ws4mJ}FlXu3Wq(UmxE;)d1MoND@Bh--Oy zqYx`0?M~4L`8c2V4EMD#nLAs+4G#i1R(WlqFH~uzJr5&%Ua9qe3*rX>9ME0&ENq>@ zs=#&Uh%EV=S80I<0BnP8tUG`!@pYiR3VfaIhdsHk6WA4aSVi89D+}CEf#r|0|DmYjaJw?ztIix=I^t>0|9PTv~NYtb2_5W?C)Js7WguNqs;Q=^Arob06(Rr zZc)zrEs{(C_&ik+ZzYR*Lv)gE}zkRzgl82 zfwvsq`@mvNPiCvnP=LeXU5$Ahp3#43hRs;x+VU>t7+1qlIS&MQ!8g6F6LY*!YUD+E z^UraHM$fGHYIuCU-)7y-zB6B28ik@S-+=%x#|xzoH^;4TC3y3Di~UE5UJb8*_MPmo zs>5oK7?JQnbmd*c04^26e`M%xoa*G6{!*6mM!S0RV5l>Gq-K)kUpyt-Oc?Gp0Bx-G~F@s5^28 z1;*MvxeY1pdKh-Nn}(I+E~x?lKB{us(pdRuM^plZx6enEjeJZ9Z*ihINQ1Ox6ukW_ zlZ66Iq;ie_X60Px8QwMYu(R6A*?tktIKmmsAt(1}_j!8}?onT~6axSzihP^lt@3Lq z%a}lUY%PMh(es@1Gx9)KOKS$ZVZ6e3* zfC`3o%Wn6P0iXDB0MFDwy5(|tu4QOTLbWZHY=ySoKeMjTvrccsR8X&()KaQLUMO;% zT!HvO0FR1O%T0H@?Lc2pg_?RBX^)pJX96g9Zoq(Vwh{pK3xR@riSv<{?QN9(Vr8w7Fj!tNp5e3XKZN<0?e7BsX$ zY8W_Jy?@&WSm0n0RE?!{oC2YF>VURV$ncg^)V%L|e;e|YX?`2MzOPQWLV29ovR~## zUl+jHaHrqV2l-Vs(^RYlVg|mYwiI?0YE7Lv$Xo7>q^j2d+*aN-q?vc6{>wYRw?k|y zO8qMA*%)`>?5x%FS2U^2hp+kmv6l%@w&^lk;5$X;%A7D&* zDR{-m#{kS-X912@U^*z&l70NYTt7r7TbAK13SbUO4kr+gOM@@Z>` zQ>t2dYX#-I5}A!^m-{AG05?*Uk| z&D|Ba2C{S|5>`}%ZP(?!6LmOE1$ggq%P~fqkIVl~56!0~jyN?@1E%iz*7VlPFd%pHO>-CoUOFnS@cd5+r0QdR8tZT4MmNaEvMUzA!D5N!0O@Pm5nemk>A>WVV z3q0I<^Swy83TZG6uoet0pd=q!Q%Ebo+3iElK}PJMb=dQDzYom$y!nLEwlWK=&buPR z3S90zuqbiu6Gthr6-aA(&iB<+-J*~!f17{K=9V?cYeh-yz|uR~3f%I6cQ;^RQP>sP zn$)egFq_|~0PJ?JcCb}-=6m3gsJu=g&k#q!n}!wJ<``>wE5Jy4E)AlV$0V)|@i>49 zMb%NbMO2DJ4~h;dtX6Pgvl{pnZnDLqbs~Smg}{~%+Y`FfJ^CQd<*;v0tYfoiIi#4b zq_$f6mOIi?$-Iw+Qy$te&{4pq$XffK!)ct0a2i>dg^Lrwf)DIii96uIDo@>x?1*DK z>$&{%!BmT>LQZ(cDsiJ!n>`y#)rgw+SBU4JUNoiv_dJtVVDn0x=05BiaD+r?sEuru zLzS=az}{~_BrmMm6wiU-u`dpoq{Mg*DW#_9^tuoSW)?G)Fq&V1bD4d{J(OoeJlSgc z%hP}D8Eyj9>^@<$0^6D`_W1ksF~j;(8b9eq?62|oT`^lrp1js`Lv&?`C~>{DQm(tY zkYOup-t6-)VkJ@5Y6}MbPCL;cks^T4FH&n7yFOHr{C5X z>+fwUzdeKk$VD&mWQhySMNFhPUpMdWtL@2DR~Rt=?cs*dRY5+R^D{q zd242^g4zV9rHXX347X=NQdtRZT34$Y)3WSSV={S@I+x||WtnB)v1>IP=krO5A_IAsP$!Wbt}xe1%m+AAEc)!SKOqA^ZcCySW-F($XeyH+G^#Y z&k+aGZKUB`f)>Cnzn!aJ(J_ZXY=yLp|E8e<9~o{0@L^s}QF~kq$u+4Poclkgej31t zB6Ibv!RLH-Wa5WBUl*6C6e!&I^KJ`pR=ivlE{sL5wLp?w2uYb+%2r>~hIj-ZDT>jW z+;Kj(H9d|=D Date: Sat, 17 Jun 2023 02:20:48 -0500 Subject: [PATCH 28/48] Removed some redundancy, better formatting --- apps/boxclk/settings.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/boxclk/settings.js b/apps/boxclk/settings.js index bf254a860..c7f0eed21 100644 --- a/apps/boxclk/settings.js +++ b/apps/boxclk/settings.js @@ -40,8 +40,8 @@ } else if (file === "boxclk.json") { hasDefaultConfig = true; let defaultConfig = storage.readJSON(file, 1); - if (defaultConfig && defaultConfig.selectedConfig) { - selectedConfig = defaultConfig.selectedConfig === 0 ? 0 : defaultConfig.selectedConfig; + if (defaultConfig && defaultConfig.selectedConfig != null) { + selectedConfig = defaultConfig.selectedConfig; } } }); @@ -59,7 +59,10 @@ let menu = { '': { 'title': '-- Box Clock --' }, '< Back': () => Bangle.showClock(), - 'Cfg:': { value: selectedConfig === 0 ? "Default" : selectedConfig, format: () => selectedConfig === 0 ? "Default" : selectedConfig }, + 'Cfg:': { + value: selectedConfig === 0 ? "Default" : selectedConfig, + format: () => selectedConfig === 0 ? "Default" : selectedConfig + } }; if (hasDefaultConfig) { From ecbc70cf8836ca0910b39c3788ce3b104f60d399 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 13:21:16 -0500 Subject: [PATCH 29/48] Add threshhold to distinguish touch and drag --- apps/boxclk/app.js | 128 ++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index fe5e6b212..6b5072b33 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -27,6 +27,7 @@ // 3. Touchscreen Handlers let touchHandler; let dragHandler; + let movementDistance = 0; // 4. Font loading function let loadCustomFont = function() { @@ -171,71 +172,92 @@ e.y <= pos.y2; }; + let deselectAllBoxes = function() { + Object.keys(isDragging).forEach((boxKey) => { + isDragging[boxKey] = false; + }); + require("widget_utils").show(); + require("widget_utils").swipeOn(); + }; + + let displaySaveIcon = function() { + g.drawImage(saveIcon, w / 2 - 24, h / 2 - 24); + // Display save icon for 2 seconds + setTimeout(() => { + g.clearRect(w / 2 - 24, h / 2 - 24, w / 2 + 24, h / 2 + 24); + draw(boxes); + }, 2000); + }; + // 10. Setup function to configure event handlers let setup = function() { // Define the touchHandler function touchHandler = function(zone, e) { - if (doubleTapTimer) { - clearTimeout(doubleTapTimer); - doubleTapTimer = null; - Object.keys(boxPos).forEach((boxKey) => { - boxesConfig[boxKey].boxPos.x = boxPos[boxKey].x / w; - boxesConfig[boxKey].boxPos.y = boxPos[boxKey].y / h; - }); - storage.write(fileName, JSON.stringify(boxesConfig)); - g.drawImage(saveIcon, w / 2 - 24, h / 2 - 24); - // Display save icon for 2 seconds - setTimeout(() => { - g.clearRect(w / 2 - 24, h / 2 - 24, w / 2 + 24, h / 2 + 24); - draw(boxes); - }, 2000); - return; + wasDragging = Object.assign({}, isDragging); + let boxTouched = false; + Object.keys(boxes).forEach((boxKey) => { + if (touchInText(e, boxes[boxKey], boxKey)) { + isDragging[boxKey] = true; + wasDragging[boxKey] = true; + boxTouched = true; + } + }); + if (!boxTouched) { + if (!Object.values(isDragging).some(Boolean)) { // check if no boxes are being dragged + deselectAllBoxes(); + if (doubleTapTimer) { + clearTimeout(doubleTapTimer); + doubleTapTimer = null; + // Save boxesConfig on double tap outside of any box and when no boxes are being dragged + Object.keys(boxPos).forEach((boxKey) => { + boxesConfig[boxKey].boxPos.x = boxPos[boxKey].x / w; + boxesConfig[boxKey].boxPos.y = boxPos[boxKey].y / h; + }); + storage.write(fileName, JSON.stringify(boxesConfig)); + displaySaveIcon(); + return; + } + } else { + // if any box is being dragged, just deselect all without saving + deselectAllBoxes(); + } + } + if (Object.values(wasDragging).some(Boolean) || !boxTouched) { + draw(boxes); } doubleTapTimer = setTimeout(() => { doubleTapTimer = null; - wasDragging = Object.assign({}, isDragging); - let boxTouched = false; - Object.keys(boxes).forEach((boxKey) => { - if (touchInText(e, boxes[boxKey], boxKey)) { - isDragging[boxKey] = true; - wasDragging[boxKey] = true; - boxTouched = true; - } - }); - if (!boxTouched) { - Object.keys(isDragging).forEach((boxKey) => { - isDragging[boxKey] = false; - }); - require("widget_utils").show(); - require("widget_utils").swipeOn(); - } - if (Object.values(wasDragging).some(Boolean) || !boxTouched) { - draw(boxes); - } - }, 300); // Increase or decrease this value based on the desired double tap timing + }, 1000); // Increase or decrease this value based on the desired double tap timing + movementDistance = 0; }; // Define the dragHandler function dragHandler = function(e) { - Object.keys(boxes).forEach((boxKey) => { - if (isDragging[boxKey]) { - require("widget_utils").hide(); - let boxItem = boxes[boxKey]; - calcBoxSize(boxItem); - let newX = boxPos[boxKey].x + e.dx; - let newY = boxPos[boxKey].y + e.dy; - if (newX - totalWidth / 2 >= 0 && - newX + totalWidth / 2 <= w && - newY - totalHeight / 2 >= 0 && - newY + totalHeight / 2 <= h ) { - boxPos[boxKey].x = newX; - boxPos[boxKey].y = newY; + // Calculate the movement distance + movementDistance += Math.abs(e.dx) + Math.abs(e.dy); + + // Check if the movement distance exceeds a threshold + if (movementDistance > 5) { + Object.keys(boxes).forEach((boxKey) => { + if (isDragging[boxKey]) { + require("widget_utils").hide(); + let boxItem = boxes[boxKey]; + calcBoxSize(boxItem); + let newX = boxPos[boxKey].x + e.dx; + let newY = boxPos[boxKey].y + e.dy; + if (newX - totalWidth / 2 >= 0 && + newX + totalWidth / 2 <= w && + newY - totalHeight / 2 >= 0 && + newY + totalHeight / 2 <= h ) { + boxPos[boxKey].x = newX; + boxPos[boxKey].y = newY; + } + const pos = calcBoxPos(boxKey); + g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); } - const pos = calcBoxPos(boxKey); - g.clearRect(pos.x1, pos.y1, pos.x2, pos.y2); - } - }); - draw(boxes); + }); + draw(boxes); + } }; Bangle.on('touch', touchHandler); From dd10287fe7c15e69582f0adfc43e1b18b0502592 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 13:39:56 -0500 Subject: [PATCH 30/48] Better looking comments --- apps/boxclk/app.js | 89 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 21 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 6b5072b33..ed7392751 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -1,5 +1,9 @@ { - // 1. Module dependencies and initial configurations + /** + * --------------------------------------------------------------- + * 1. Module dependencies and initial configurations + * --------------------------------------------------------------- + */ let storage = require("Storage"); let locale = require("locale"); let date = new Date(); @@ -17,19 +21,31 @@ let saveIcon = require("heatshrink").decompress(atob("mEwwkEogA/AHdP/4AK+gWVDBQWNAAIuVGBAIB+UQdhMfGBAHBCxUAgIXHIwPyCxQwEJAgXB+MAl/zBwQGBn8ggQjBGAQXG+EA/4XI/8gBIQXTGAMPC6n/C6HzkREBC6YACC6QAFC57aHCYIXOOgLsEn4XPABIX/C6vykQAEl6/WgCQBC5imFAAT2BC5gCBI4oUCC5x0IC/4X/C4K8Bl4XJ+TCCC4wKBABkvC4tEEoMQCxcBB4IWEC4XyDBUBFwIXGJAIAOIwowDABoWGGB4uHDBwWJAH4AzA")); - // 2. Graphical and visual configurations + /** + * --------------------------------------------------------------- + * 2. Graphical and visual configurations + * --------------------------------------------------------------- + */ let w = g.getWidth(); let h = g.getHeight(); let totalWidth, totalHeight; let enableSuffix = true; let drawTimeout; - // 3. Touchscreen Handlers + /** + * --------------------------------------------------------------- + * 3. Touchscreen Handlers + * --------------------------------------------------------------- + */ let touchHandler; let dragHandler; let movementDistance = 0; - // 4. Font loading function + /** + * --------------------------------------------------------------- + * 4. Font loading function + * --------------------------------------------------------------- + */ let loadCustomFont = function() { Graphics.prototype.setFontBrunoAce = function() { // Actual height 23 (24 - 2) @@ -42,7 +58,11 @@ }; }; - // 5. Initial settings of boxes and their positions + /** + * --------------------------------------------------------------- + * 5. Initial settings of boxes and their positions + * --------------------------------------------------------------- + */ for (let key in boxesConfig) { if (key === 'bg' && boxesConfig[key].img) { bgImage = storage.read(boxesConfig[key].img); @@ -61,7 +81,11 @@ wasDragging[key] = false; }); - // 6. Text and drawing functions + /** + * --------------------------------------------------------------- + * 6. Text and drawing functions + * --------------------------------------------------------------- + */ let g_drawString = g.drawString; g.drawString = function(box, str, x, y) { outlineText(box, str, x, y); @@ -98,7 +122,20 @@ }; }; - // 7. Date and time related functions + let displaySaveIcon = function() { + g.drawImage(saveIcon, w / 2 - 24, h / 2 - 24); + // Display save icon for 2 seconds + setTimeout(() => { + g.clearRect(w / 2 - 24, h / 2 - 24, w / 2 + 24, h / 2 + 24); + draw(boxes); + }, 2000); + }; + + /** + * --------------------------------------------------------------- + * 7. Date and time related functions + * --------------------------------------------------------------- + */ let getDate = function() { const date = new Date(); const dayOfMonth = date.getDate(); @@ -122,7 +159,11 @@ return locale.dow(date, 0); }; - // 8. Main draw function + /** + * --------------------------------------------------------------- + * 8. Main draw function + * --------------------------------------------------------------- + */ let draw = function(boxes) { date = new Date(); g.clear(); @@ -162,7 +203,11 @@ } }; - // 9. Helper function for touch event + /** + * --------------------------------------------------------------- + * 9. Helper function for touch event + * --------------------------------------------------------------- + */ let touchInText = function(e, boxItem, boxKey) { calcBoxSize(boxItem); const pos = calcBoxPos(boxKey); @@ -180,18 +225,15 @@ require("widget_utils").swipeOn(); }; - let displaySaveIcon = function() { - g.drawImage(saveIcon, w / 2 - 24, h / 2 - 24); - // Display save icon for 2 seconds - setTimeout(() => { - g.clearRect(w / 2 - 24, h / 2 - 24, w / 2 + 24, h / 2 + 24); - draw(boxes); - }, 2000); - }; - - // 10. Setup function to configure event handlers + /** + * --------------------------------------------------------------- + * 10. Setup function to configure event handlers + * --------------------------------------------------------------- + */ let setup = function() { + // ------------------------------------ // Define the touchHandler function + // ------------------------------------ touchHandler = function(zone, e) { wasDragging = Object.assign({}, isDragging); let boxTouched = false; @@ -231,11 +273,12 @@ movementDistance = 0; }; + // ------------------------------------ // Define the dragHandler function + // ------------------------------------ dragHandler = function(e) { // Calculate the movement distance movementDistance += Math.abs(e.dx) + Math.abs(e.dy); - // Check if the movement distance exceeds a threshold if (movementDistance > 5) { Object.keys(boxes).forEach((boxKey) => { @@ -280,7 +323,11 @@ draw(boxes); }; - // 11. Main execution part + /** + * --------------------------------------------------------------- + * 11. Main execution part + * --------------------------------------------------------------- + */ Bangle.loadWidgets(); require("widget_utils").swipeOn(); setup(); From 28431c1b51752803efba54f56e6ce50bdbd26bd3 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 13:46:06 -0500 Subject: [PATCH 31/48] Load box keys only once per app load, not per draw --- apps/boxclk/app.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index ed7392751..bdb291aa7 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -71,7 +71,9 @@ } } - Object.keys(boxes).forEach((key) => { + let boxKeys = Object.keys(boxes); + + boxKeys.forEach((key) => { let boxConfig = boxes[key]; boxPos[key] = { x: w * boxConfig.boxPos.x, @@ -182,7 +184,7 @@ if (boxes.batt) { boxes.batt.string = E.getBattery() + "%"; } - Object.keys(boxes).forEach((boxKey) => { + boxKeys.forEach((boxKey) => { let boxItem = boxes[boxKey]; calcBoxSize(boxItem); const pos = calcBoxPos(boxKey); @@ -237,7 +239,7 @@ touchHandler = function(zone, e) { wasDragging = Object.assign({}, isDragging); let boxTouched = false; - Object.keys(boxes).forEach((boxKey) => { + boxKeys.forEach((boxKey) => { if (touchInText(e, boxes[boxKey], boxKey)) { isDragging[boxKey] = true; wasDragging[boxKey] = true; @@ -281,7 +283,7 @@ movementDistance += Math.abs(e.dx) + Math.abs(e.dy); // Check if the movement distance exceeds a threshold if (movementDistance > 5) { - Object.keys(boxes).forEach((boxKey) => { + boxKeys.forEach((boxKey) => { if (isDragging[boxKey]) { require("widget_utils").hide(); let boxItem = boxes[boxKey]; From 7b4d9d0e083ed0c32d78852b87a986860bc9eb0f Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 14:04:04 -0500 Subject: [PATCH 32/48] Add double tap to save info to README --- apps/boxclk/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index 7a0f3bb81..9e26830a2 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -8,6 +8,10 @@ __Drag & Drop:__ This intuitive feature allows you to reposition any element (box) on the clock face with ease. Tap on the box(s) you want to move and the border will show, drag into position and tap outside of the boxes to finish placing. +__Double Tap to Save:__ + +After you've found the perfect position for your boxes, you can save their positions with a quick double tap on the background. This makes it easy to ensure your custom layout is stored for future use. A save icon will appear momentarily to confirm that your configuration has been saved. + __JSON Configuration:__ Each box can be customized extensively via a simple JSON configuration. You can also add a custom text string to your configuration with the "string" attribute. Here's what an example configuration might look like: From a3b9cf2eadda063be5347df216312529b7372f91 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 14:51:09 -0500 Subject: [PATCH 33/48] Check config exists, else default to 'boxclk.json' --- apps/boxclk/app.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index bdb291aa7..e715ad8c0 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -11,6 +11,10 @@ let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0; let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json'; + // Add a condition to check if the file exists, if it does not, default to 'boxclk.json' + if (!storage.read(fileName)) { + fileName = 'boxclk.json'; + } let boxesConfig = storage.readJSON(fileName, 1) || {}; let boxes = {}; From 85bfc38b43e780ee981b85197b1f8703c422cb95 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 14:59:32 -0500 Subject: [PATCH 34/48] Change border color for battery percent to white --- apps/boxclk/boxclk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/boxclk.json b/apps/boxclk/boxclk.json index 047ab6224..ecb548168 100644 --- a/apps/boxclk/boxclk.json +++ b/apps/boxclk/boxclk.json @@ -44,7 +44,7 @@ "outline": 1, "color": "#0ff", "outlineColor": "#000", - "border": "#000", + "border": "#fff", "xPadding": -0.5, "yPadding": -0.5, "xOffset": 2, From fd2102155fc51a11370c44b90fcb76d420e3181b Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 15:07:39 -0500 Subject: [PATCH 35/48] Handle case where config file doesn't exist --- apps/boxclk/settings.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/boxclk/settings.js b/apps/boxclk/settings.js index c7f0eed21..9c597c2d8 100644 --- a/apps/boxclk/settings.js +++ b/apps/boxclk/settings.js @@ -41,7 +41,17 @@ hasDefaultConfig = true; let defaultConfig = storage.readJSON(file, 1); if (defaultConfig && defaultConfig.selectedConfig != null) { - selectedConfig = defaultConfig.selectedConfig; + // Check if corresponding config file exists + let configFileName = 'boxclk-' + defaultConfig.selectedConfig + '.json'; + if (storage.read(configFileName)) { + // If it exists, assign selectedConfig + selectedConfig = defaultConfig.selectedConfig; + } else { + // If it does not exist, set selectedConfig to 0 and update boxclk.json + defaultConfig.selectedConfig = 0; + storage.writeJSON("boxclk.json", defaultConfig); + selectedConfig = 0; + } } } }); From 870d81eea7ddb117e745d0d627ed0066e0257ddc Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 15:43:19 -0500 Subject: [PATCH 36/48] Shift default positions left a little --- apps/boxclk/boxclk.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/boxclk/boxclk.json b/apps/boxclk/boxclk.json index ecb548168..e3db1b602 100644 --- a/apps/boxclk/boxclk.json +++ b/apps/boxclk/boxclk.json @@ -6,11 +6,11 @@ "color": "#000", "outlineColor": "#fff", "border": "#000", - "xPadding": 1, + "xPadding": 0, "yPadding": -4, "xOffset": 0, "yOffset": 3, - "boxPos": { "x": 0.65, "y": 0.16 } + "boxPos": { "x": 0.633, "y": 0.16 } }, "dow": { "font": "6x8", @@ -23,7 +23,7 @@ "yPadding": 0.5, "xOffset": 1, "yOffset": 1, - "boxPos": { "x": 0.65, "y": 0.3 } + "boxPos": { "x": 0.633, "y": 0.3 } }, "date": { "font": "6x8", @@ -36,7 +36,7 @@ "yPadding": 0.5, "xOffset": 1, "yOffset": 1, - "boxPos": { "x": 0.65, "y": 0.39 } + "boxPos": { "x": 0.633, "y": 0.39 } }, "batt": { "font": "4x6", From a862fdd5673f291cfa7e66d38045383e355409ab Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 15:58:00 -0500 Subject: [PATCH 37/48] Made require widget_utils a variable --- apps/boxclk/app.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index e715ad8c0..63822f5ef 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -6,6 +6,7 @@ */ let storage = require("Storage"); let locale = require("locale"); + let widgets = require("widget_utils"); let date = new Date(); let bgImage; @@ -227,8 +228,8 @@ Object.keys(isDragging).forEach((boxKey) => { isDragging[boxKey] = false; }); - require("widget_utils").show(); - require("widget_utils").swipeOn(); + widgets.show(); + widgets.swipeOn(); }; /** @@ -289,7 +290,7 @@ if (movementDistance > 5) { boxKeys.forEach((boxKey) => { if (isDragging[boxKey]) { - require("widget_utils").hide(); + widgets.hide(); let boxItem = boxes[boxKey]; calcBoxSize(boxItem); let newX = boxPos[boxKey].x + e.dx; @@ -322,7 +323,7 @@ drawTimeout = undefined; delete Graphics.prototype.setFontBrunoAce; g.drawString = g_drawString; // Return to original without outlines - require("widget_utils").show(); + widgets.show(); } }); loadCustomFont(); @@ -335,6 +336,6 @@ * --------------------------------------------------------------- */ Bangle.loadWidgets(); - require("widget_utils").swipeOn(); + widgets.swipeOn(); setup(); } From a23a24c67a5ee905d832e6086aa5508369a6bee2 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sat, 17 Jun 2023 17:00:52 -0500 Subject: [PATCH 38/48] Reduce timeout to prevent accidental saves --- apps/boxclk/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index 63822f5ef..a44f91320 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -276,7 +276,7 @@ } doubleTapTimer = setTimeout(() => { doubleTapTimer = null; - }, 1000); // Increase or decrease this value based on the desired double tap timing + }, 500); // Increase or decrease this value based on the desired double tap timing movementDistance = 0; }; From de84e0890d10b44adb3e5d8ca1c398a12d857b36 Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Sun, 18 Jun 2023 08:03:36 -0500 Subject: [PATCH 39/48] Prevents widgets from showing on save --- apps/boxclk/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index a44f91320..1084751c8 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -130,6 +130,7 @@ }; let displaySaveIcon = function() { + draw(boxes); g.drawImage(saveIcon, w / 2 - 24, h / 2 - 24); // Display save icon for 2 seconds setTimeout(() => { From 0612dfef60f9c1b6c8a6a25867c2678bff8be20d Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Sun, 18 Jun 2023 17:47:36 -0500 Subject: [PATCH 40/48] Update README.md - mention ability to match theme colors --- apps/boxclk/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index 9e26830a2..f9ed9c3d9 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -25,9 +25,9 @@ Each box can be customized extensively via a simple JSON configuration. You can "font": "CustomFont", // Custom fonts must be in main program and removed in setUI "fontSize": 1, "outline": 2, - "color": "#FF9900", - "outlineColor": "#fff", - "border": "#000", + "color": "#FF9900", // You can use 6-digit or 3-digit hex color code + "outlineColor": "bgH", // You can match system theme colors like this + "border": 65535, // Or use 16-bit decimal color codes like this "xPadding": 1, "yPadding": -4, "xOffset": 0, From cac07237d47cf787f20422c60f2f386a5302129c Mon Sep 17 00:00:00 2001 From: stweedo Date: Sun, 18 Jun 2023 17:49:41 -0500 Subject: [PATCH 41/48] Can now set and match theme colors from config --- apps/boxclk/app.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index a44f91320..f0003ce24 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -93,6 +93,20 @@ * 6. Text and drawing functions * --------------------------------------------------------------- */ + + // Overwrite the setColor function to allow the + // use of (x) in g.theme.x as a string + // in your JSON config ("fg", "bg", "fg2", "bg2", "fgH", "bgH") + let g_setColor = g.setColor; + g.setColor = function(color) { + if (typeof color === "string" && color in g.theme) { + g_setColor.call(g, g.theme[color]); + } else { + g_setColor.call(g, color); + } + }; + + // Overwrite the drawString function let g_drawString = g.drawString; g.drawString = function(box, str, x, y) { outlineText(box, str, x, y); @@ -130,6 +144,7 @@ }; let displaySaveIcon = function() { + draw(boxes); g.drawImage(saveIcon, w / 2 - 24, h / 2 - 24); // Display save icon for 2 seconds setTimeout(() => { @@ -259,8 +274,8 @@ doubleTapTimer = null; // Save boxesConfig on double tap outside of any box and when no boxes are being dragged Object.keys(boxPos).forEach((boxKey) => { - boxesConfig[boxKey].boxPos.x = boxPos[boxKey].x / w; - boxesConfig[boxKey].boxPos.y = boxPos[boxKey].y / h; + boxesConfig[boxKey].boxPos.x = (boxPos[boxKey].x / w).toFixed(3); + boxesConfig[boxKey].boxPos.y = (boxPos[boxKey].y / h).toFixed(3); }); storage.write(fileName, JSON.stringify(boxesConfig)); displaySaveIcon(); @@ -322,7 +337,10 @@ if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; delete Graphics.prototype.setFontBrunoAce; - g.drawString = g_drawString; // Return to original without outlines + // Restore original drawString function (no outlines) + g.drawString = g_drawString; + // Restore original setColor function (no string filtering) + g.setColor = g_setColor; widgets.show(); } }); From ce11203a124588531d10751601498ec3439328f3 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sun, 18 Jun 2023 17:53:55 -0500 Subject: [PATCH 42/48] Add missing "s" --- apps/boxclk/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index f9ed9c3d9..aa43cf5a0 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -25,7 +25,7 @@ Each box can be customized extensively via a simple JSON configuration. You can "font": "CustomFont", // Custom fonts must be in main program and removed in setUI "fontSize": 1, "outline": 2, - "color": "#FF9900", // You can use 6-digit or 3-digit hex color code + "color": "#FF9900", // You can use 6-digit or 3-digit hex color codes "outlineColor": "bgH", // You can match system theme colors like this "border": 65535, // Or use 16-bit decimal color codes like this "xPadding": 1, From c294a974e03b386a30a79b33fb73e2ab285f33b0 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sun, 18 Jun 2023 19:11:48 -0500 Subject: [PATCH 43/48] Fix bug if using theme colors w/ modded g.setColor --- apps/boxclk/app.js | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index f0003ce24..d0d1418d1 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -9,7 +9,6 @@ let widgets = require("widget_utils"); let date = new Date(); let bgImage; - let configNumber = (storage.readJSON("boxclk.json", 1) || {}).selectedConfig || 0; let fileName = 'boxclk' + (configNumber > 0 ? `-${configNumber}` : '') + '.json'; // Add a condition to check if the file exists, if it does not, default to 'boxclk.json' @@ -17,12 +16,12 @@ fileName = 'boxclk.json'; } let boxesConfig = storage.readJSON(fileName, 1) || {}; - let boxes = {}; let boxPos = {}; let isDragging = {}; let wasDragging = {}; let doubleTapTimer = null; + let g_setColor; let saveIcon = require("heatshrink").decompress(atob("mEwwkEogA/AHdP/4AK+gWVDBQWNAAIuVGBAIB+UQdhMfGBAHBCxUAgIXHIwPyCxQwEJAgXB+MAl/zBwQGBn8ggQjBGAQXG+EA/4XI/8gBIQXTGAMPC6n/C6HzkREBC6YACC6QAFC57aHCYIXOOgLsEn4XPABIX/C6vykQAEl6/WgCQBC5imFAAT2BC5gCBI4oUCC5x0IC/4X/C4K8Bl4XJ+TCCC4wKBABkvC4tEEoMQCxcBB4IWEC4XyDBUBFwIXGJAIAOIwowDABoWGGB4uHDBwWJAH4AzA")); @@ -97,12 +96,23 @@ // Overwrite the setColor function to allow the // use of (x) in g.theme.x as a string // in your JSON config ("fg", "bg", "fg2", "bg2", "fgH", "bgH") - let g_setColor = g.setColor; - g.setColor = function(color) { - if (typeof color === "string" && color in g.theme) { - g_setColor.call(g, g.theme[color]); - } else { - g_setColor.call(g, color); + let modSetColor = function() { + // Save the original setColor function + g_setColor = g.setColor; + // Overwrite setColor with the new function + g.setColor = function(color) { + if (typeof color === "string" && color in g.theme) { + g_setColor.call(g, g.theme[color]); + } else { + g_setColor.call(g, color); + } + }; + }; + + let restoreSetColor = function() { + // Restore the original setColor function + if (g_setColor) { + g.setColor = g_setColor; } }; @@ -243,8 +253,10 @@ Object.keys(isDragging).forEach((boxKey) => { isDragging[boxKey] = false; }); + restoreSetColor(); widgets.show(); widgets.swipeOn(); + modSetColor(); }; /** @@ -339,8 +351,7 @@ delete Graphics.prototype.setFontBrunoAce; // Restore original drawString function (no outlines) g.drawString = g_drawString; - // Restore original setColor function (no string filtering) - g.setColor = g_setColor; + restoreSetColor(); widgets.show(); } }); @@ -355,5 +366,6 @@ */ Bangle.loadWidgets(); widgets.swipeOn(); + modSetColor(); setup(); } From 4f31a7f9fdf048850f0854f0749ae360edb11196 Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Sun, 18 Jun 2023 19:28:28 -0500 Subject: [PATCH 44/48] Update README.md - shorten comments --- apps/boxclk/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index aa43cf5a0..341985135 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -22,12 +22,12 @@ Each box can be customized extensively via a simple JSON configuration. You can { "customBox": { // "string": "Your text here", - "font": "CustomFont", // Custom fonts must be in main program and removed in setUI + "font": "CustomFont", // Custom fonts must be removed in setUI "fontSize": 1, "outline": 2, - "color": "#FF9900", // You can use 6-digit or 3-digit hex color codes - "outlineColor": "bgH", // You can match system theme colors like this - "border": 65535, // Or use 16-bit decimal color codes like this + "color": "#FF9900", // Use 6 or 3 digit hex color codes + "outlineColor": "bgH", // Or match system theme colors like this + "border": 65535, // Or use 16-bit RGB565 like this "xPadding": 1, "yPadding": -4, "xOffset": 0, From 3ad2c2dd0cb91452844d777e3b91e7595b805869 Mon Sep 17 00:00:00 2001 From: stweedo Date: Sun, 18 Jun 2023 19:45:00 -0500 Subject: [PATCH 45/48] Change drag threshold to > 1 --- apps/boxclk/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/app.js b/apps/boxclk/app.js index d0d1418d1..8f671667a 100644 --- a/apps/boxclk/app.js +++ b/apps/boxclk/app.js @@ -314,7 +314,7 @@ // Calculate the movement distance movementDistance += Math.abs(e.dx) + Math.abs(e.dy); // Check if the movement distance exceeds a threshold - if (movementDistance > 5) { + if (movementDistance > 1) { boxKeys.forEach((boxKey) => { if (isDragging[boxKey]) { widgets.hide(); From abce2fadc5e8b9d4331642a164cdfe9af9d50008 Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Sun, 18 Jun 2023 19:52:37 -0500 Subject: [PATCH 46/48] Update README.md - Fix typo, specify available theme color options --- apps/boxclk/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index 341985135..7f0857461 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -34,7 +34,7 @@ Each box can be customized extensively via a simple JSON configuration. You can "yOffset": 3, "boxPos": { "x": 0.5, "y": 0.5 } }, - "bg": { // Can also be removed for no backround + "bg": { // Can also be removed for no background "img": "YourImageName.img" } } @@ -48,7 +48,7 @@ Each box can be customized extensively via a simple JSON configuration. You can * **outline:** The thickness of the outline around the text. -* **color:** The color of the text. +* **color:** The color of the text. Match current system theme colors with: ("fg", "bg", "fg2", "bg2", "fgH", "bgH"). * **outlineColor:** The color of the text outline. From 8d27267a95ddeeec7f80ad7f9a28062fdf2b1184 Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Sun, 18 Jun 2023 20:00:50 -0500 Subject: [PATCH 47/48] Update README.md - rephrasing/formatting --- apps/boxclk/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index 7f0857461..e86ca5651 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -14,10 +14,12 @@ After you've found the perfect position for your boxes, you can save their posit __JSON Configuration:__ -Each box can be customized extensively via a simple JSON configuration. You can also add a custom text string to your configuration with the "string" attribute. Here's what an example configuration might look like: +Each box can be customized extensively via a simple JSON configuration. You can add a custom text string to your configuration with the "string" parameter and you can match system theme colors by using "fg", "bg", "fg2", "bg2", "fgH", or "bgH" for any of the color parameters. ## Config File Structure +Here's what an example configuration might look like: + ``` { "customBox": { // @@ -39,6 +41,7 @@ Each box can be customized extensively via a simple JSON configuration. You can } } ``` +__Breakdown of Parameters:__ * **string:** The text string to be displayed inside the box. @@ -48,7 +51,7 @@ Each box can be customized extensively via a simple JSON configuration. You can * **outline:** The thickness of the outline around the text. -* **color:** The color of the text. Match current system theme colors with: ("fg", "bg", "fg2", "bg2", "fgH", "bgH"). +* **color:** The color of the text. * **outlineColor:** The color of the text outline. From f09f2c58c2c7e4e423d7f3e0a4f101bca9e74393 Mon Sep 17 00:00:00 2001 From: stweedo <108593831+stweedo@users.noreply.github.com> Date: Sun, 18 Jun 2023 20:14:34 -0500 Subject: [PATCH 48/48] Update README.md - add helpful hint for fine adjustments --- apps/boxclk/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/boxclk/README.md b/apps/boxclk/README.md index e86ca5651..001c7ed10 100644 --- a/apps/boxclk/README.md +++ b/apps/boxclk/README.md @@ -6,7 +6,7 @@ Box Clock is a customizable clock app for Bangle.js 2 that features an interacti __Drag & Drop:__ -This intuitive feature allows you to reposition any element (box) on the clock face with ease. Tap on the box(s) you want to move and the border will show, drag into position and tap outside of the boxes to finish placing. +This intuitive feature allows you to reposition any element (box) on the clock face with ease. Tap on the box(s) you want to move and the border will show, drag into position and tap outside of the boxes to finish placing. **Note:** Roll the tip of your finger slowly on the screen for fine adjustments. __Double Tap to Save:__