From 1427e0bb5d5b979ce3247fc077793c620528897c Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 15:46:13 -0500 Subject: [PATCH 01/22] Refactor Overlay and Controls in main.js Refactor Overlay and Controls for improved functionality and visuals. --- apps/ctrlpad/main.js | 228 ++++++++++++++++++++++++------------------- 1 file changed, 126 insertions(+), 102 deletions(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 93f2864f7c..d2d780b99d 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -15,28 +15,45 @@ } var Overlay = (function () { function Overlay() { - this.width = g.getWidth() - 10 * 2; + this.width = g.getWidth() - 2 * 2; this.height = g.getHeight() - 24 - 10; this.g2 = Graphics.createArrayBuffer(this.width, this.height, 4, { msb: true }); + this.g2.transparent = 13; this.renderG2(); } Overlay.prototype.setBottom = function (bottom) { var g2 = this.g2; var y = bottom - this.height; - Bangle.setLCDOverlay(g2, 10, y - 10); + Bangle.setLCDOverlay(g2, 2, y - 10); }; Overlay.prototype.hide = function () { Bangle.setLCDOverlay(); }; Overlay.prototype.renderG2 = function () { - this.g2 - .reset() - .setColor(g.theme.bg) - .fillRect(0, 0, this.width, this.height) - .setColor(colour.on.bg) - .drawRect(0, 0, this.width - 1, this.height - 1) - .drawRect(1, 1, this.width - 2, this.height - 2); - }; + var border = 3; + this.g2 + .reset() + .setColor(13) + .fillRect(0, 0, this.width, this.height) // Background (Transparent) + + .setColor(colour.on.bg) + .fillRect({ + x: 0, + y: 0, + w: this.width, + h: this.height, + r: 20 + }) // The Outer Shape + + .setColor(g.theme.bg == "0" ? "#000" : g.theme.bg) + .fillRect({ + x: border, + y: border, + w: this.width - (border * 2), + h: this.height - (border * 2), + r: 16 + }); + }; return Overlay; }()); var colour = { @@ -46,7 +63,7 @@ }, off: { fg: "#000", - bg: "#bbb", + bg: g.theme.dark?"#fff":"#bbb", }, }; var Controls = (function () { @@ -59,15 +76,24 @@ { x: width / 4 - 10, y: centreY - circleGapY }, { x: width / 2, y: centreY - circleGapY }, { x: width * 3 / 4 + 10, y: centreY - circleGapY }, - { x: width / 3, y: centreY + circleGapY }, - { x: width * 2 / 3, y: centreY + circleGapY }, + { x: width / 4 - 10, y: centreY + circleGapY }, + { x: width / 2, y: centreY + circleGapY }, + { x: width * 3 / 4 + 10, y: centreY + circleGapY }, ].map(function (xy, i) { - var ctrl = xy; - var from = controls[i]; - ctrl.text = from.text; - ctrl.cb = from.cb; - Object.assign(ctrl, from.cb(false) ? colour.on : colour.off); - return ctrl; + var ctrl = xy; + var from = controls[i]; + + // Wrap into a cb function + ctrl.cb = function(tap) { + if (tap) return from.toggle(); + return from.get(); + }; + + ctrl.text = from.text; + ctrl.img = from.img; + Object.assign(ctrl, {}, ctrl.cb(false) ? colour.on : colour.off); + + return ctrl; }); } Controls.prototype.draw = function (g, single) { @@ -80,7 +106,7 @@ .setColor(ctrl.bg) .fillCircle(ctrl.x, ctrl.y, 23) .setColor(ctrl.fg) - .drawString(ctrl.text, ctrl.x, ctrl.y); + .drawImage(ctrl.img,ctrl.x-12,ctrl.y-12) } }; Controls.prototype.hitTest = function (x, y) { @@ -109,79 +135,59 @@ var initUI = function () { if (ui) return; - var controls = [ - { - text: "BLE", - cb: function (tap) { - var on = NRF.getSecurityStatus().advertising; - if (tap) { - if (on) - NRF.sleep(); - else - NRF.wake(); - } - return on !== tap; - } - }, - { - text: "DnD", - cb: function (tap) { - var on; - if ((on = !!origBuzz)) { - if (tap) { - Bangle.buzz = origBuzz; - origBuzz = undefined; - } - } - else { - if (tap) { - origBuzz = Bangle.buzz; - Bangle.buzz = function () { return Promise.resolve(); }; - setTimeout(function () { - if (!origBuzz) - return; - Bangle.buzz = origBuzz; - origBuzz = undefined; - }, 1000 * 60 * 10); - } - } - return on !== tap; - } - }, - { - text: "HRM", - cb: function (tap) { - var _a; - var id = "widhid"; - var hrm = (_a = Bangle._PWR) === null || _a === void 0 ? void 0 : _a.HRM; - var off = !hrm || hrm.indexOf(id) === -1; - if (off) { - if (tap) - Bangle.setHRMPower(1, id); - } - else if (tap) { - Bangle.setHRMPower(0, id); - } - return !off !== tap; - } - }, - { - text: "clk", - cb: function (tap) { - if (tap) - Bangle.showClock(), terminateUI(); - return true; - }, - }, - { - text: "lch", - cb: function (tap) { - if (tap) - Bangle.showLauncher(), terminateUI(); - return true; - }, - }, - ]; + // ... inside initUI ... +var controls = [ + { + text: "BLE", + img:atob("GBiBAAAAAAAYAAAcAAAfAAAbgAAZ4AYYYAcY4AOZwAH/AAB+AAA8AAA8AAB+AAD/AAOZwAcY4A4YYAQZ4AAbgAAfAAAcAAAYAAAAAA=="), + get: () => NRF.getSecurityStatus().advertising||NRF.getSecurityStatus().connected, + toggle: () => { + if (NRF.getSecurityStatus().advertising||NRF.getSecurityStatus().connected) NRF.sleep(); else NRF.wake(); + } + }, + { + text: "DnD", + img:atob("GBiBAAAAAAAAAAA8AAAYAAAYAAD/AAH/gAH/gAP/wAP/wAP/wAP/wAP/wAP/wAP/wAf/4Af/4A//8B//+A//8AAAAAA8AAAAAAAAAA=="), + get: () => { + return require("Storage").readJSON("setting.json", 1).quiet === 1 ; + }, + toggle: () => { + let s = require("Storage").readJSON("setting.json", 1); + s.quiet = s.quiet ? 0 : 1; + require("Storage").writeJSON("setting.json", s); + //if qm widget is present, update that + if (typeof WIDGETS === "object" && "qmsched" in WIDGETS) WIDGETS["qmsched"].draw(); + if (global.setAppQuietMode) setAppQuietMode(s.quiet); + } + }, + { + text: "HRM", + img:atob("GBiBAAAAAA+B8B/D+D/n/H///v/////////P///P///H/3+WQAC2Xj82HB86uA/48Af54AP9wAH9gAD/AAB+AAA8AAAYAAAAAAAAAA=="), + get: () => Bangle.isHRMOn(), + toggle: () => { + Bangle.setHRMPower(!Bangle.isHRMOn(), "widhid"); + } + }, + { + text: "clock", + img:atob("GBiBAAB+AAP/wAeB4A4AcBgAGDAADHAYDmAYBmAYBsAYA8AYA8AYA8AcA8AOA8AHA2ADBmAABnAADjAADBgAGA4AcAeB4AP/wAB+AA=="), + get: () => false, // Always looks "off" until pressed + toggle: () => {Bangle.showClock(); return "close";} + }, + { + text: "launch", + img:atob("GBiBAAAAAAAAAAAAAA/AwB/gwBhgwBhn+Bhn+BhgwB/gwA/AwAAAAAAAAA/D8B/n+BhmGBhmGBhmGBhmGB/n+A/D8AAAAAAAAAAAAA=="), + get: () => false, + toggle: () => {Bangle.showLauncher(); return "close";} + }, + { + text: "settings", + img:atob("GBiBAAA8AAB+AAR+IA7/cB//+D///B//+A/D8B8A+H8A/v4Af/4Af/4Af/4Af38A/h8A+A/D8B//+D///B//+A7/cAR+IAB+AAA8AA=="), + get: () => false, + toggle: () => {Bangle.load("setting.app.js"); return "close";} + } +]; + var overlay = new Overlay(); ui = { overlay: overlay, @@ -218,7 +224,7 @@ break; case 0: if (e.b && !touchDown) { - if (e.y <= 40) { + if (e.y <= 10) { state = 1; startY = e.y; (_a = E.stopEventPropagation) === null || _a === void 0 ? void 0 : _a.call(E); @@ -289,6 +295,31 @@ if (e.b) touchDown = true; }); + var origBuzz; + var onCtrlTap = function(ctrl) { + Bangle.buzz(20); + + var result=ctrl.cb(true); + if(result=="close"){ + terminateUI() + return; + } + ui.ctrls.controls.forEach(function(c) { + var isActive = c.cb(false); + Object.assign(c, isActive ? colour.on : colour.off); + }); + + // Clear and Redraw the buffer + ui.overlay.renderG2(); + ui.ctrls.draw(ui.overlay.g2); + + // Force an update through the overlay + var y = g.getHeight() - ui.overlay.height; + Bangle.setLCDOverlay(ui.overlay.g2, 2, y - 10); + Bangle.buzz(10); + + }; + var onTouch = (function (_btn, xy) { var _a; if (!ui || !xy) @@ -301,14 +332,7 @@ (_a = E.stopEventPropagation) === null || _a === void 0 ? void 0 : _a.call(E); } }); - var origBuzz; - var onCtrlTap = function (ctrl, ui) { - Bangle.buzz(20); - var col = ctrl.cb(true) ? colour.on : colour.off; - ctrl.fg = col.fg; - ctrl.bg = col.bg; - ui.ctrls.draw(ui.overlay.g2, ctrl); - }; + Bangle.prependListener("drag", onDrag); Bangle.on("lock", terminateUI); })(); From 8e88249555f46654cd3b552b7390406464fc5139 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 15:48:31 -0500 Subject: [PATCH 02/22] Update ChangeLog --- apps/ctrlpad/ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/ctrlpad/ChangeLog b/apps/ctrlpad/ChangeLog index d8c4777010..a75c544869 100644 --- a/apps/ctrlpad/ChangeLog +++ b/apps/ctrlpad/ChangeLog @@ -1,2 +1,9 @@ 0.01: New app - forked from widhid 0.02: Minor code improvements +0.03: Major code refactor + Change to a rounded style + Add a button for settings + Make buttons/toggles much more consistent and reliable + Instead of text, use icons instead + Make menu slightly wider to feel more spacious without losing the overlay feel + Change toggle styles/colors slightly From 1b9a08a60c135545e92910dfc9eb523e79cd8739 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 15:49:31 -0500 Subject: [PATCH 03/22] Bump version to 0.03 and update description Updated version number and enhanced description for clarity. --- apps/ctrlpad/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ctrlpad/metadata.json b/apps/ctrlpad/metadata.json index a4e1573239..b4845ab4d8 100644 --- a/apps/ctrlpad/metadata.json +++ b/apps/ctrlpad/metadata.json @@ -2,9 +2,9 @@ "id": "ctrlpad", "name": "Control Panel", "shortName": "ctrlpad", - "version": "0.02", + "version": "0.03", "author": "bobrippling", - "description": "Fast access (via a downward swipe) to common functions, such as bluetooth/HRM power and Do Not Disturb", + "description": "Fast access (via a downward swipe) to common functions, such as BLE/HRM power, quiet mode, and quick access to launcher, clock, and settings.", "icon": "icon.png", "readme": "README.md", "type": "bootloader", From 15cc162a6a9f96155f5de24019f3763975e38765 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 16:31:36 -0500 Subject: [PATCH 04/22] Enhance README with new features and gesture details Updated the README to include additional features and clarify gesture handling. --- apps/ctrlpad/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/ctrlpad/README.md b/apps/ctrlpad/README.md index 492957fe7e..87d1950640 100644 --- a/apps/ctrlpad/README.md +++ b/apps/ctrlpad/README.md @@ -1,9 +1,10 @@ # Description -A control pad app to provide fast access to common functions, such as bluetooth power, HRM and Do Not Disturb. +A control pad app to provide fast access to common functions, such as bluetooth power, HRM and Do Not Disturb, as well as shortcuts to load the clock, launcher, and settings app. + By dragging from the top of the watch, you have this control without leaving your current app (e.g. on a run, bike ride or just watching the clock). -The app is designed to not conflict with other gestures - when the control pad is visible, it'll prevent propagation of events past it (touch, drag and swipe specifically). When the control pad is hidden, it'll ignore touch, drag and swipe events with the exception of an event dragging from the top 40 pixels of the screen. +The app is designed to not conflict with other gestures - when the control pad is visible, it'll prevent propagation of events past it (touch, drag and swipe specifically). When the control pad is hidden, it'll ignore touch, drag and swipe events with the exception of an event dragging from the top 10 pixels of the screen. # Usage From d416be20ec6d184b63ae4728ed2af48ef9e04abd Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:40:18 -0500 Subject: [PATCH 05/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index d2d780b99d..0b7696bf60 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -301,7 +301,7 @@ var controls = [ var result=ctrl.cb(true); if(result=="close"){ - terminateUI() + terminateUI(); return; } ui.ctrls.controls.forEach(function(c) { From a0ca73ac28a50daf73790f0767c2af13c65f18d6 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:40:36 -0500 Subject: [PATCH 06/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 0b7696bf60..af906e0558 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -300,7 +300,7 @@ var controls = [ Bangle.buzz(20); var result=ctrl.cb(true); - if(result=="close"){ + if (result === "close") { terminateUI(); return; } From 3315d0ec11532ba7af8b5cc9e2b2dc7ed2af9235 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:40:46 -0500 Subject: [PATCH 07/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index af906e0558..74fcf0931e 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -30,7 +30,7 @@ Bangle.setLCDOverlay(); }; Overlay.prototype.renderG2 = function () { - var border = 3; + var border = 3; this.g2 .reset() .setColor(13) From 14fd51151b717c1a3c1c696359726bcee7979c71 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:41:02 -0500 Subject: [PATCH 08/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 74fcf0931e..af5abbca2a 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -149,7 +149,7 @@ var controls = [ text: "DnD", img:atob("GBiBAAAAAAAAAAA8AAAYAAAYAAD/AAH/gAH/gAP/wAP/wAP/wAP/wAP/wAP/wAP/wAf/4Af/4A//8B//+A//8AAAAAA8AAAAAAAAAA=="), get: () => { - return require("Storage").readJSON("setting.json", 1).quiet === 1 ; + return require("Storage").readJSON("setting.json", 1).quiet === 1; }, toggle: () => { let s = require("Storage").readJSON("setting.json", 1); From 14a01e502608c48ddfd46fbac7b2c7967f73861b Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:41:17 -0500 Subject: [PATCH 09/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index af5abbca2a..958e2215cb 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -299,7 +299,7 @@ var controls = [ var onCtrlTap = function(ctrl) { Bangle.buzz(20); - var result=ctrl.cb(true); + var result = ctrl.cb(true); if (result === "close") { terminateUI(); return; From 800628a44c9f1770ada4422496a1e4ca7c75e5b9 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:41:24 -0500 Subject: [PATCH 10/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 958e2215cb..725a3425f5 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -310,7 +310,7 @@ var controls = [ }); // Clear and Redraw the buffer - ui.overlay.renderG2(); + ui.overlay.renderG2(); ui.ctrls.draw(ui.overlay.g2); // Force an update through the overlay From ddf5a3c4871155574a5c1b89da4d6d2719c22a94 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:41:51 -0500 Subject: [PATCH 11/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 725a3425f5..9575bfdc21 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -91,7 +91,7 @@ ctrl.text = from.text; ctrl.img = from.img; - Object.assign(ctrl, {}, ctrl.cb(false) ? colour.on : colour.off); + Object.assign(ctrl, ctrl.cb(false) ? colour.on : colour.off); return ctrl; }); From 544cd3af8f966f867cdf1e65e763913fab43c4aa Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:42:48 -0500 Subject: [PATCH 12/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 9575bfdc21..8d718ed9da 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -140,7 +140,10 @@ var controls = [ { text: "BLE", img:atob("GBiBAAAAAAAYAAAcAAAfAAAbgAAZ4AYYYAcY4AOZwAH/AAB+AAA8AAA8AAB+AAD/AAOZwAcY4A4YYAQZ4AAbgAAfAAAcAAAYAAAAAA=="), - get: () => NRF.getSecurityStatus().advertising||NRF.getSecurityStatus().connected, + get: () => { + const status = NRF.getSecurityStatus(); + return status.advertising || status.connected; + }, toggle: () => { if (NRF.getSecurityStatus().advertising||NRF.getSecurityStatus().connected) NRF.sleep(); else NRF.wake(); } From c6b9a116eec7f9074d2d77e24de0509ff2c0389e Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:43:54 -0500 Subject: [PATCH 13/22] Update apps/ctrlpad/main.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 8d718ed9da..54d30ab28d 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -298,7 +298,6 @@ var controls = [ if (e.b) touchDown = true; }); - var origBuzz; var onCtrlTap = function(ctrl) { Bangle.buzz(20); From 32152b171ea84bfdf48d68389abc94152a8c7eea Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 17:44:48 -0500 Subject: [PATCH 14/22] Update ChangeLog --- apps/ctrlpad/ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/ctrlpad/ChangeLog b/apps/ctrlpad/ChangeLog index a75c544869..c83c1c7008 100644 --- a/apps/ctrlpad/ChangeLog +++ b/apps/ctrlpad/ChangeLog @@ -7,3 +7,5 @@ Instead of text, use icons instead Make menu slightly wider to feel more spacious without losing the overlay feel Change toggle styles/colors slightly + Migrate DnD functionality to global setting + Update toggle calls for speed and precision From 45936302cbb875139ed4215c0a5a37eae401fa27 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Tue, 20 Jan 2026 21:43:14 -0500 Subject: [PATCH 15/22] Fix color setting logic for background --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 54d30ab28d..9361a98027 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -45,7 +45,7 @@ r: 20 }) // The Outer Shape - .setColor(g.theme.bg == "0" ? "#000" : g.theme.bg) + .setColor(g.theme.bg) .fillRect({ x: border, y: border, From 99074a179823c88c92f5dffbad3795e37967d9bc Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Wed, 21 Jan 2026 14:35:15 -0500 Subject: [PATCH 16/22] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index 9361a98027..a637310607 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -175,19 +175,28 @@ var controls = [ text: "clock", img:atob("GBiBAAB+AAP/wAeB4A4AcBgAGDAADHAYDmAYBmAYBsAYA8AYA8AYA8AcA8AOA8AHA2ADBmAABnAADjAADBgAGA4AcAeB4AP/wAB+AA=="), get: () => false, // Always looks "off" until pressed - toggle: () => {Bangle.showClock(); return "close";} + toggle: () => { + Bangle.showClock(); + return "close"; + } }, { text: "launch", img:atob("GBiBAAAAAAAAAAAAAA/AwB/gwBhgwBhn+Bhn+BhgwB/gwA/AwAAAAAAAAA/D8B/n+BhmGBhmGBhmGBhmGB/n+A/D8AAAAAAAAAAAAA=="), get: () => false, - toggle: () => {Bangle.showLauncher(); return "close";} + toggle: () => { + Bangle.showLauncher(); + return "close"; + } }, { text: "settings", img:atob("GBiBAAA8AAB+AAR+IA7/cB//+D///B//+A/D8B8A+H8A/v4Af/4Af/4Af/4Af38A/h8A+A/D8B//+D///B//+A7/cAR+IAB+AAA8AA=="), get: () => false, - toggle: () => {Bangle.load("setting.app.js"); return "close";} + toggle: () => { + Bangle.load("setting.app.js"); + return "close"; + } } ]; From 908a49ca997e311b6a41b74687a4a2520f923fa6 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Wed, 21 Jan 2026 14:46:15 -0500 Subject: [PATCH 17/22] Add files via upload --- apps/ctrlpad/scr.png | Bin 0 -> 6517 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/ctrlpad/scr.png diff --git a/apps/ctrlpad/scr.png b/apps/ctrlpad/scr.png new file mode 100644 index 0000000000000000000000000000000000000000..7e2779fd9e787160a0833e01712df0b4918c0ebe GIT binary patch literal 6517 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 literal 0 HcmV?d00001 From 04e4031c5fcde836ccb3f64b37a63150c5fd947f Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Wed, 21 Jan 2026 14:47:15 -0500 Subject: [PATCH 18/22] Add screenshots field to metadata.json --- apps/ctrlpad/metadata.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/ctrlpad/metadata.json b/apps/ctrlpad/metadata.json index b4845ab4d8..2a71726a79 100644 --- a/apps/ctrlpad/metadata.json +++ b/apps/ctrlpad/metadata.json @@ -9,6 +9,7 @@ "readme": "README.md", "type": "bootloader", "tags": "bluetooth", + "screenshots": [{ "url":"scr.png" }], "supports": ["BANGLEJS2"], "storage": [ {"name":"ctrlpad.boot.js","url":"main.js"}, From f8878e467c6a902a094f919e6d45513257ea3a90 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Wed, 21 Jan 2026 14:50:24 -0500 Subject: [PATCH 19/22] Enhance README with design details and contributors Expanded the README with additional details about the app's design and contributors. --- apps/ctrlpad/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/ctrlpad/README.md b/apps/ctrlpad/README.md index 87d1950640..86601b96d7 100644 --- a/apps/ctrlpad/README.md +++ b/apps/ctrlpad/README.md @@ -4,8 +4,7 @@ A control pad app to provide fast access to common functions, such as bluetooth By dragging from the top of the watch, you have this control without leaving your current app (e.g. on a run, bike ride or just watching the clock). -The app is designed to not conflict with other gestures - when the control pad is visible, it'll prevent propagation of events past it (touch, drag and swipe specifically). When the control pad is hidden, it'll ignore touch, drag and swipe events with the exception of an event dragging from the top 10 pixels of the screen. - +The app is designed to not conflict with other gestures - when the control pad is visible, it'll prevent propagation of events past it (touch, drag and swipe specifically). When the control pad is hidden, it'll ignore touch, drag and swipe events with the exception of an event dragging from the top 10 pixels of the screen. It's also designed to blend into the watch UI, with rounded borders, and neat design, and if you have `qmsched` installed, it will update that as well for a seamless experience. # Usage @@ -23,3 +22,8 @@ The control pad disables drag and touch event handlers while active, preventing - Handle rotated screen (`g.setRotation(...)`) - Handle notifications (sharing of `setLCDOverlay`) + +## Creator +Bobrippling +## Contributors +RKBoss6 From 47a0eeab7f4cbf1dcb47d4ed7f6c77187f82c21c Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Wed, 21 Jan 2026 21:49:01 +0000 Subject: [PATCH 20/22] ctrlpad: copy changes from js -> ts --- apps/ctrlpad/main.ts | 100 ++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 24 deletions(-) diff --git a/apps/ctrlpad/main.ts b/apps/ctrlpad/main.ts index 5faac60fa2..7c1a7f2b63 100644 --- a/apps/ctrlpad/main.ts +++ b/apps/ctrlpad/main.ts @@ -58,13 +58,30 @@ } renderG2(): void { + const border = 3; + this.g2 .reset() .setColor(g.theme.bg) - .fillRect(0, 0, this.width, this.height) + .fillRect(0, 0, this.width, this.height) // background (transparent) + .setColor(colour.on.bg) - .drawRect(0, 0, this.width - 1, this.height - 1) - .drawRect(1, 1, this.width - 2, this.height - 2); + .fillRect({ + x: 0, + y: 0, + w: this.width, + h: this.height, + r: 20 + }) // outer shape + + .setColor(g.theme.bg) + .fillRect({ + x: border, + y: border, + w: this.width - (border * 2), + h: this.height - (border * 2), + r: 16 + }); } } @@ -85,7 +102,7 @@ }, off: { fg: "#000", - bg: "#bbb", + bg: g.theme.dark ? "#fff" : "#bbb", }, } as const; @@ -117,14 +134,17 @@ { x: width / 4 - 10, y: centreY - circleGapY }, { x: width / 2, y: centreY - circleGapY }, { x: width * 3/4 + 10, y: centreY - circleGapY }, - { x: width / 3, y: centreY + circleGapY }, - { x: width * 2/3, y: centreY + circleGapY }, + { x: width / 4 - 10, y: centreY + circleGapY }, + { x: width / 2, y: centreY + circleGapY }, + { x: width * 3 / 4 + 10, y: centreY + circleGapY }, ].map((xy, i) => { const ctrl = xy as Control; const from = controls[i]!; + ctrl.text = from.text; - ctrl.cb = from.cb; - Object.assign(ctrl, from.cb(false) ? colour.on : colour.off); + ctrl.img = from.img; + + Object.assign(ctrl, ctrl.cb(false) ? colour.on : colour.off); return ctrl; }) as FiveOf; } @@ -139,7 +159,7 @@ .setColor(ctrl.bg) .fillCircle(ctrl.x, ctrl.y, 23) .setColor(ctrl.fg) - .drawString(ctrl.text, ctrl.x, ctrl.y); + .drawImage(ctrl.img, ctrl.x-12, ctrl.y-12); } } @@ -181,17 +201,21 @@ const controls: FiveOf = [ { text: "BLE", + img: atob("GBiBAAAAAAAYAAAcAAAfAAAbgAAZ4AYYYAcY4AOZwAH/AAB+AAA8AAA8AAB+AAD/AAOZwAcY4A4YYAQZ4AAbgAAfAAAcAAAYAAAAAA=="), cb: tap => { - const on = NRF.getSecurityStatus().advertising; + let on = NRF.getSecurityStatus().advertising; if(tap){ if(on) NRF.sleep(); else NRF.wake(); + + on = !on; } - return on !== tap; // on ^ tap + return on; } }, { text: "DnD", + img:atob("GBiBAAAAAAAAAAA8AAAYAAAYAAD/AAH/gAH/gAP/wAP/wAP/wAP/wAP/wAP/wAP/wAf/4Af/4A//8B//+A//8AAAAAA8AAAAAAAAAA=="), cb: tap => { let on; if((on = !!origBuzz)){ @@ -210,26 +234,31 @@ }, 1000 * 60 * 10); } } - return on !== tap; // on ^ tap + + if(tap) on = !on; + + return on; } }, { text: "HRM", + img:atob("GBiBAAAAAA+B8B/D+D/n/H///v/////////P///P///H/3+WQAC2Xj82HB86uA/48Af54AP9wAH9gAD/AAB+AAA8AAAYAAAAAAAAAA=="), cb: tap => { const id = "widhid"; const hrm = (Bangle as any)._PWR?.HRM as undefined | Array ; const off = !hrm || hrm.indexOf(id) === -1; - if(off){ - if(tap) - Bangle.setHRMPower(1, id); - }else if(tap){ - Bangle.setHRMPower(0, id); + + if(tap){ + Bangle.setHRMPower(off, id); + off = !off; } - return !off !== tap; // on ^ tap + + return !off; } }, { text: "clk", + img:atob("GBiBAAB+AAP/wAeB4A4AcBgAGDAADHAYDmAYBmAYBsAYA8AYA8AYA8AcA8AOA8AHA2ADBmAABnAADjAADBgAGA4AcAeB4AP/wAB+AA=="), cb: tap => { if (tap) Bangle.showClock(), terminateUI(); return true; @@ -237,11 +266,21 @@ }, { text: "lch", + img:atob("GBiBAAAAAAAAAAAAAA/AwB/gwBhgwBhn+Bhn+BhgwB/gwA/AwAAAAAAAAA/D8B/n+BhmGBhmGBhmGBhmGB/n+A/D8AAAAAAAAAAAAA=="), cb: tap => { if (tap) Bangle.showLauncher(), terminateUI(); return true; }, }, + { + text: "settings", + img: atob("GBiBAAA8AAB+AAR+IA7/cB//+D///B//+A/D8B8A+H8A/v4Af/4Af/4Af/4Af38A/h8A+A/D8B//+D///B//+A7/cAR+IAB+AAA8AA=="), + cb: tap => { + if(tap) + Bangle.load("setting.app.js"), terminateUI() + return false; + } + }, ]; const overlay = new Overlay(); @@ -284,7 +323,7 @@ case State.Idle: if(e.b && !touchDown){ // no need to check Bangle.CLKINFO_FOCUS - if(e.y <= 40){ + if(e.y <= 10){ state = State.TopDrag startY = e.y; E.stopEventPropagation?.(); @@ -378,12 +417,25 @@ const onCtrlTap = (ctrl: Control, ui: UI) => { Bangle.buzz(20); - const col = ctrl.cb(true) ? colour.on : colour.off; - ctrl.fg = col.fg; - ctrl.bg = col.bg; - //console.log("hit on " + ctrl.text + ", col: " + ctrl.fg); + const result = ctrl.cb(true); + + if (result === "close"){ + terminateUI(); + return; + } + ui.ctrlx.controls.forEach(c => { + const isActive = c.cb(false); + Object.assign(c, isActive ? colour.on : colour.off); + }); + + // Clear and Redraw the buffer + ui.overlay.renderG2(); + ui.ctrls.draw(ui.overlay.g2); - ui.ctrls.draw(ui.overlay.g2, ctrl); + // Force an update through the overlay + const y = g.getHeight() - ui.overlay.height; + Bangle.setLCDOverlay(ui.overlay.g2, 2, y - 10); + Bangle.buzz(10); }; Bangle.prependListener("drag", onDrag); From 32d8545ed24eb4f9da512dfc3e2998cfddc4b4c4 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Wed, 21 Jan 2026 16:53:12 -0500 Subject: [PATCH 21/22] Update apps/ctrlpad/README.md Co-authored-by: Rob Pilling --- apps/ctrlpad/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/ctrlpad/README.md b/apps/ctrlpad/README.md index 86601b96d7..8dd7dc5311 100644 --- a/apps/ctrlpad/README.md +++ b/apps/ctrlpad/README.md @@ -24,6 +24,7 @@ The control pad disables drag and touch event handlers while active, preventing - Handle notifications (sharing of `setLCDOverlay`) ## Creator -Bobrippling +[bobrippling](https://github.com/bobrippling) + ## Contributors -RKBoss6 +[RKBoss6](https://github.com/rkboss6) From 5ef9b079f7b6eb706857ada3f607edde8e8b8382 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Wed, 21 Jan 2026 16:58:19 -0500 Subject: [PATCH 22/22] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/ctrlpad/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ctrlpad/main.js b/apps/ctrlpad/main.js index a637310607..99bbcc1c25 100644 --- a/apps/ctrlpad/main.js +++ b/apps/ctrlpad/main.js @@ -325,7 +325,7 @@ var controls = [ ui.ctrls.draw(ui.overlay.g2); // Force an update through the overlay - var y = g.getHeight() - ui.overlay.height; + var y = g.getHeight() - ui.overlay.height; Bangle.setLCDOverlay(ui.overlay.g2, 2, y - 10); Bangle.buzz(10);