From affffb4a113ff48999a315ef5f0cf53c3c8f351e Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 22 May 2026 09:44:31 -0400 Subject: [PATCH 1/4] Add gradient, add custom settings menu on bangle.js 2 --- apps/clockbg/ChangeLog | 4 +- apps/clockbg/README.md | 19 +-- apps/clockbg/lib.js | 36 +++++- apps/clockbg/metadata.json | 4 +- apps/clockbg/screenshot7.png | Bin 0 -> 2466 bytes apps/clockbg/settings.js | 236 ++++++++++++++++++++++++++--------- 6 files changed, 226 insertions(+), 73 deletions(-) create mode 100644 apps/clockbg/screenshot7.png diff --git a/apps/clockbg/ChangeLog b/apps/clockbg/ChangeLog index 69f23b7f2ee..d4330280387 100644 --- a/apps/clockbg/ChangeLog +++ b/apps/clockbg/ChangeLog @@ -11,4 +11,6 @@ Default (solid colour) now uses a variety of shades Added .load/.unload to allow changes when fast loading clocks 0.09: Add more plasma and ring bg colours, random plasma color uses all the available colours -0.10: Added blobs background \ No newline at end of file +0.10: Added blobs background +0.11: Added gradient background + Bangle.js 2 can use colorpicker module for custom plasma and gradient colors \ No newline at end of file diff --git a/apps/clockbg/README.md b/apps/clockbg/README.md index e1b5e23ee8a..a3b6f40325b 100644 --- a/apps/clockbg/README.md +++ b/apps/clockbg/README.md @@ -11,14 +11,15 @@ You can either: * Go to [the Clock Backgrounds app](https://banglejs.com/apps/?id=clockbg) in the App Loader and use pre-made image backgrounds (or upload your own) * Go to the `Backgrounds` app on the Bangle itself, and choose between: - * `Solid Color` - one color that never changes - * `Random Color` - a new color every time the clock starts - * `Image` - choose from a previously uploaded image - * `Squares` - a randomly generated pattern of squares in the selected color palette - * `Plasma` - a randomly generated 'plasma' pattern of squares in the selected color palette (random noise with a gaussian filter applied) - * `Rings` - randomly generated rings in the selected color palette - * `Tris` - randomly generated overlapping triangles in the selected color palette - + * `Solid Color` - One color that never changes + * `Random Color` - A new color every time the clock starts + * `Image` - Choose from a previously uploaded image + * `Squares` - A randomly generated pattern of squares in the selected color palette + * `Plasma` - A randomly generated 'plasma' pattern of squares in the selected color palette (random noise with a gaussian filter applied) + * `Rings` - Randomly generated rings in the selected color palette + * `Tris` - Randomly generated overlapping triangles in the selected color palette + * `Blobs` - Randomly generated blobs of color in the selected color palette + * `Gradient` - A gradient from top to bottom with the selected colors ## Usage in code @@ -54,7 +55,7 @@ A few features could be added that would really improve functionality: * When 'fast loading', 'random' backgrounds don't update at the moment (calling `.reload` can fix this now, but it slows things down) * Support for >1 image to be uploaded (requires some image management in `interface.html`), and choose randomly between them -* Support for gradients (random colors) +* Support for random colored gradients * More types of auto-generated pattern (as long as they can be generated quickly or in the background) * Storing 'clear' areas of uploaded images so clocks can easily position themselves * Some backgrounds could update themselves in the background (eg a mandelbrot could calculate the one it should display next time while the watch is running) \ No newline at end of file diff --git a/apps/clockbg/lib.js b/apps/clockbg/lib.js index 5940292aede..64aa08d4794 100644 --- a/apps/clockbg/lib.js +++ b/apps/clockbg/lib.js @@ -66,7 +66,39 @@ exports.reload = function() { bg.palette.set(settings.colors.map(c=>g.toColor(c))); settings.img = bg; settings.imgOpt = {scale:g.getWidth()/16}; - } else if (settings.style=="blobs") { // ~25ms + + } else if (settings.style=="gradient") { // ~60ms + settings.style = "image"; + let c = settings.colors; + function pc(s) { + if (typeof s==="number") return s; + s=s.replace('#',''); + if (s.length==3) s=s[0]+s[0]+s[1]+s[1]+s[2]+s[2]; + return parseInt(s,16); + } + let a=pc(c[0]), b=pc(c[1]); + let bg=Graphics.createArrayBuffer(16,16,4,{msb:true}); + bg.palette=new Uint16Array(16); + let ar=(a>>16)&255,ag=(a>>8)&255,ab=a&255; + let br=(b>>16)&255,bgv=(b>>8)&255,bb=b&255; + for (let i=0;i<5;i++) { + let t=i/4; + bg.palette[i]=g.toColor((ar+(br-ar)*t)/255,(ag+(bgv-ag)*t)/255,(ab+(bb-ab)*t)/255); + } + // 4bpp, 8 bytes/row. boundaries always at rows 3,6,9,12 for 16 rows/5 colors + let buf=new Uint8Array(bg.buffer), r; + buf.fill(0x00, 0, 24); // rows 0-2: color 0 + r=Math.random()*65536|0; for(let x=24;x<32;x++,r>>=2) buf[x]=((r&1?1:0)<<4)|(r&2?1:0); + buf.fill(0x11, 32, 48); // rows 4-5: color 1 + r=Math.random()*65536|0; for(let x=48;x<56;x++,r>>=2) buf[x]=((r&1?2:1)<<4)|(r&2?2:1); + buf.fill(0x22, 56, 72); // rows 7-8: color 2 + r=Math.random()*65536|0; for(let x=72;x<80;x++,r>>=2) buf[x]=((r&1?3:2)<<4)|(r&2?3:2); + buf.fill(0x33, 80, 96); // rows 10-11: color 3 + r=Math.random()*65536|0; for(let x=96;x<104;x++,r>>=2) buf[x]=((r&1?4:3)<<4)|(r&2?4:3); + buf.fill(0x44,104,128); // rows 13-15: color 4 + settings.img=bg; + settings.imgOpt={scale:g.getWidth()/16}; + }else if (settings.style=="blobs") { // ~25ms settings.style = "image"; const S=11; // image size const Z=88,W=Z/S,H=Z/S; @@ -223,4 +255,4 @@ exports.fillRect = function(rect,y,x2,y2) { // load background exports.reload(); -//exports.fillRect(Bangle.appRect); // testing +//exports.fillRect(Bangle.appRect); // testing \ No newline at end of file diff --git a/apps/clockbg/metadata.json b/apps/clockbg/metadata.json index 81ab226aa6e..17994c65c50 100644 --- a/apps/clockbg/metadata.json +++ b/apps/clockbg/metadata.json @@ -1,10 +1,10 @@ { "id": "clockbg", "name": "Clock Backgrounds", "shortName":"Backgrounds", - "version": "0.10", + "version": "0.11", "description": "Library that allows clocks to include a custom background (generated on demand or uploaded).", "icon": "app.png", - "screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"},{"url":"screenshot4.png"},{"url":"screenshot5.png"},{"url":"screenshot6.png"}], + "screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"},{"url":"screenshot4.png"},{"url":"screenshot5.png"},{"url":"screenshot6.png"},{"url":"screenshot7.png"}], "type": "module", "readme": "README.md", "provides_modules" : ["clockbg"], diff --git a/apps/clockbg/screenshot7.png b/apps/clockbg/screenshot7.png new file mode 100644 index 0000000000000000000000000000000000000000..4b082df2369be0fd72126e24dfc3f36f2316371d GIT binary patch literal 2466 zcmai0c~n#963;>wBLtLCl~wynj3Q`#$Y$6#0Vzw7MSKDEKu`!<1wz0qE`@?ADxyGG zL=+bwp++!bkO;0|OxdKYVGWwdCWRz`&>OYq^}N&f-XHhQoo{A-^L^*L^PBm}_d9nD zlr9Pc0%>^cbMXgcjp{+dfj8&=7y$%Y6XfCI7(fQUFUn4o*&toJR#xrhry?ru7c9{A z56gbBN!WK>J&KWRX#0a*z%GkOFJNS= zJ>#7N85FJ~}w|xbExnb>Vb(8FJnnkgWJ&ye=3{zW1}Qk2V<2TeIOW=KM%H zEAZ@`h1$j;(!mCa)25+4OZCD#f2%>Ed`VMCmoDJDXOTC#9dHV5eAWbn{6*`4D@MmU z`brKrSENu!4*b3aJwoa7G1C}mhbv_J#s4<+=pX-T)s95xOyQ?(Xdd>qd~y+o?>6dN z`C+;PZWW!e=(YIn!U05r$;1xyo_boTg&<^aZ&@6UtEWM!%dyU+4u;Zvyy32Xt%boRL8PVynHPwpVsL%TDKT6 zW{(NZxLKQDvQoc`+1*#iHYv0>i8XIjy2^H~-eNu@&9}bDfvi0+fbUgL-ALIPv*M@6 z?32m~RN=ysT-?U1rWStN^uX8*wIe|1j#F+%S>ua>FWBQbKzKHr^U0V*)+xlhFkq)< z-;#_^q&&ja_;w9#1aID$GZ^TUGLa4BF(8mG-{7zra7?3T6m?Z?G$3&nNaVIg5=^P<~)WN8cs@+tq-u*?JEAujvwG9m!OyEM*P_FJK9S}$QI zY-gH;#V4))F)ywVTsRUKhQKv!cdw%dC+kI-z?F)*m|tE%>#6jhgvoEr zCf1x*S2{Zr<7I%WX}{=PmbGSE7W(0$`(y|36LCLM=GrNK;WsvCH0hj%n5bDFBxj}t z#R4eZ*MYNc7qtg&*ou&0kcoQ5&bg3Qiql?=#>MIA;qxLe?9H~^qbF{Z*&VysPXH26Q>5=k(M;EJvDQ>?H6e2B$^}UTMRJ5c zT2-WkEQcc0??A1`k24`YpJ*cG%8~OoA=6KRddB+!L=gbHJd!qv96|2?L=2`ZC~C*~ z`Db#0=a$u-#0KkfL2X?6w9PgIMiFx;E@CqQ#!_)Xkt$Y2GI#TibSZo|YXPdTTI$$6 zYw~tb#R+-SUwBJat^TAkoD2Bo{S$@%3{&T?)p%-3IL!U=`;~`6^eI3^#=+_r0g{JIaZkYU8Ot8JhoE&r!8i03%8n?y_l97CbBnBRVG2U zDtn-m*U`q)f1YyD z?ChFrTb67P9{o%*mR>7S#S+U`AorBW=+SE{fK7g)AJ`zb-Zpxrq56tksyqz;E%_2L z?Q|`fFt}QJKl-Q!ypH7o9HVwT+pA*!NXba=0ZTb?US`}|` z$8z2rqc*F@pCHy*m`RNQo6mEm#8O2b5^*0Tl~ZCANhi6d)>#H;PmG_mjq6TTp+5ll zC{=~7D}MNTL|z9R5J8{QQZRw0uBSt_)L^`XlXck`3ecYw*k7-|+#&!?#2ZV!fmX|M z$i-%WpheAjw=98f9tZI*)Q1d%UwiljAo=ceFg3?0$bLT{dEo1`901VJo=wdMWIS}= zsv&@9OFcBHy>C98{{8HFJTOKSdFLx`1Hb`%H4!kC5Vf(??O?c-&d$f&HM;~oXG+gC zI>y4ur?DKq3!FSY6 { - menu["-"+getColorsImage([col])] = () => { + + require("colorpicker").show({ + back:showMainMenu, + onSelect:function(col){ settings.style = "color"; settings.color = col; saveSettings(); - showMainMenu(); - }; - }); - E.showMenu(menu); + } + }) }, /*LANG*/"Random Color" : function() { - var cols = [ - ["#F00","#0F0","#FF0","#00F","#F0F","#0FF"], - ["#F00","#0F0","#00F"], - ["#FF0","#F0F","#0FF"], - ["#00f","#0bf","#0f7","#3f0","#ff0","#f30","#f07","#b0f"], - ["#66f","#6df","#6fb","#8f6","#ff6","#f86","#f6b","#d6f"], - ["#007","#057","#073","#170","#770","#710","#703","#507"] - // Please add some more! - ]; - var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; - cols.forEach(col => { - menu[getColorsImage(col)] = () => { - settings.style = "randomcolor"; - settings.colors = col; - saveSettings(); - showMainMenu(); + if(process.env.BOARD=="BANGLEJS2"){ + opts={ + onSelect:function(colors){ + settings.style = "randomcolor"; + settings.colors = colors; + saveSettings(); + }, + back:showMainMenu, + multiSelect:true, }; - }); - E.showMenu(menu); + if(settings.style=="randomcolor"){ + opts.startingSelection=settings.colors; + } + require("colorpicker").show(opts); + }else{ + var cols = [ + ["#F00","#0F0","#FF0","#00F","#F0F","#0FF"], + ["#F00","#0F0","#00F"], + ["#FF0","#F0F","#0FF"], + ["#00f","#0bf","#0f7","#3f0","#ff0","#f30","#f07","#b0f"], + ["#66f","#6df","#6fb","#8f6","#ff6","#f86","#f6b","#d6f"], + ["#007","#057","#073","#170","#770","#710","#703","#507"] + // Please add some more! + ]; + var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; + cols.forEach(col => { + menu[getColorsImage(col)] = () => { + settings.style = "randomcolor"; + settings.colors = col; + saveSettings(); + showMainMenu(); + } + }) + E.showMenu(menu);; + } + }, /*LANG*/"Image" : function() { let images = require("Storage").list(/clockbg\..*\.img/); @@ -120,36 +193,43 @@ E.showMenu(menu); }, /*LANG*/"Plasma" : function() { - var cols = [ // list of color palettes used as possible square colours - 16 entries - ["#00f","#05f","#0bf","#0fd","#0f7","#0f1","#3f0","#9f0","#ff0","#f90","#f30","#f01","#f07","#f0d","#b0f","#50f"], - ["#44f","#48f","#4df","#4fe","#4fa","#4f6","#7f4","#bf4","#ff4","#fb4","#f74","#f46","#f4a","#f4e","#d4f","#84f"], - ["#009","#039","#079","#098","#094","#091","#290","#590","#990","#950","#920","#901","#904","#908","#709","#309"], - ["#fff","#fef","#fdf","#fcf","#fbf","#fae","#f9e","#f8e","#f7e","#f6e","#f5d","#f4d","#f3d","#f2d","#f1d","#f0c"], - ["#fff","#eff","#dff","#cef","#bef","#adf","#9df","#8df","#7cf","#6cf","#5bf","#4bf","#3bf","#2af","#1af","#09f"], - ["#000","#110","#220","#330","#440","#550","#660","#770","#880","#990","#aa0","#bb0","#cc0","#dd0","#ee0","#ff0"], - ["#000","#010","#020","#130","#140","#250","#260","#270","#380","#390","#4a0","#4b0","#5c0","#5d0","#5e0","#6f0"], - ["#fff","#efe","#dfd","#cfc","#bfb","#afa","#9f9","#8f8","#7f7","#6f6","#5f5","#4f4","#3f3","#2f2","#1f1","#0f0"], - ["#fff","#fee","#fdd","#fcc","#fbb","#faa","#f99","#f88","#f77","#f66","#f55","#f44","#f33","#f22","#f11","#f00"], + let showMenu = () => { + var cols = [ // list of color palettes used as possible square colours - 16 entries + ["#00f","#05f","#0bf","#0fd","#0f7","#0f1","#3f0","#9f0","#ff0","#f90","#f30","#f01","#f07","#f0d","#b0f","#50f"], + ["#44f","#48f","#4df","#4fe","#4fa","#4f6","#7f4","#bf4","#ff4","#fb4","#f74","#f46","#f4a","#f4e","#d4f","#84f"], + ["#009","#039","#079","#098","#094","#091","#290","#590","#990","#950","#920","#901","#904","#908","#709","#309"], + ["#fff","#fef","#fdf","#fcf","#fbf","#fae","#f9e","#f8e","#f7e","#f6e","#f5d","#f4d","#f3d","#f2d","#f1d","#f0c"], + ["#fff","#eff","#dff","#cef","#bef","#adf","#9df","#8df","#7cf","#6cf","#5bf","#4bf","#3bf","#2af","#1af","#09f"], + ["#000","#110","#220","#330","#440","#550","#660","#770","#880","#990","#aa0","#bb0","#cc0","#dd0","#ee0","#ff0"], + ["#000","#010","#020","#130","#140","#250","#260","#270","#380","#390","#4a0","#4b0","#5c0","#5d0","#5e0","#6f0"], + ["#fff","#efe","#dfd","#cfc","#bfb","#afa","#9f9","#8f8","#7f7","#6f6","#5f5","#4f4","#3f3","#2f2","#1f1","#0f0"], + ["#fff","#fee","#fdd","#fcc","#fbb","#faa","#f99","#f88","#f77","#f66","#f55","#f44","#f33","#f22","#f11","#f00"], - // Please add some more! - ]; - var menu = {"":{title:/*LANG*/"Plasma", back:showModeMenu}, - /*LANG*/"Random" : () => { - settings.style = "plasma"; - settings.colors = cols; - saveSettings(); - showMainMenu(); - }}; - cols.forEach(col => { - menu[getColorsImage(col)] = () => { - settings.style = "plasma"; - settings.colors = col; - saveSettings(); - showMainMenu(); + // Please add some more! + ]; + + var plasmaMenu = {"":{title:/*LANG*/"Plasma", back:showModeMenu}, + /*LANG*/"Random" : () => { + settings.style = "plasma"; + settings.colors = cols; + saveSettings(); + showMainMenu(); + }, }; - }); - E.showMenu(menu); + if(process.env.BOARD === "BANGLEJS2")plasmaMenu[/*LANG*/"Custom"]= () => customMenu("plasma", /*LANG*/"Background",/*LANG*/"Foreground", showMenu ); + cols.forEach(col => { + plasmaMenu[getColorsImage(col)] = () => { + settings.style = "plasma"; + settings.colors = col; + saveSettings(); + showMainMenu(); + }; + }); + E.showMenu(plasmaMenu); + } + showMenu() }, + /*LANG*/"Rings" : function() { var cols = [ // list of color palettes used as possible square colours - 2 entries ["#ff0","#f00"], // yellow/red @@ -232,6 +312,42 @@ }); E.showMenu(menu); }, + /*LANG*/"Gradient" : function() { + showMenu=()=>{ + var cols = [ // list of color palettes used as gradient colors + ["#0ff","#00f"], + ["#ff0","#0f0"], + ["#0f0","#f00"], + ["#0ff","#f0f"], + ["#fff","#0ff"] + + + // Please add some more! + ]; + + var menu = {"":{title:/*LANG*/"Gradient", back:showModeMenu}, + /*LANG*/"Random" : () => { + settings.style = "gradient"; + settings.colors = cols; + saveSettings(); + showMainMenu(); + }, + }; + if(process.env.BOARD === "BANGLEJS2")menu[/*LANG*/"Custom"]= () => customMenu("gradient", /*LANG*/"Top",/*LANG*/"Bottom",showMenu); + cols.forEach(col => { + menu[getColorsImage(col)] = () => { + settings.style = "gradient"; + settings.colors = col; + print(col) + saveSettings(); + showMainMenu(); + }; + }); + E.showMenu(menu); + } + showMenu() + } + }); } @@ -250,6 +366,8 @@ }); } + showMainMenu(); + /* Scripts for generating colors. Change the values in HSBtoRGB to generate different effects @@ -270,6 +388,6 @@ }).join(",")) */ - - showMainMenu(); - }) + + +}) \ No newline at end of file From 289eeee431c3790ac7a619020a07971d33ff3475 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 22 May 2026 10:07:51 -0400 Subject: [PATCH 2/4] fix lint errors and variable definitions --- apps/clockbg/settings.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/clockbg/settings.js b/apps/clockbg/settings.js index 0da5d1796d6..608cfbff9af 100644 --- a/apps/clockbg/settings.js +++ b/apps/clockbg/settings.js @@ -104,7 +104,7 @@ let customMenu=function(style, col1name, col2name, bck) { }, /*LANG*/"Random Color" : function() { if(process.env.BOARD=="BANGLEJS2"){ - opts={ + let opts={ onSelect:function(colors){ settings.style = "randomcolor"; settings.colors = colors; @@ -136,7 +136,7 @@ let customMenu=function(style, col1name, col2name, bck) { showMainMenu(); } }) - E.showMenu(menu);; + E.showMenu(menu); } }, @@ -313,7 +313,7 @@ let customMenu=function(style, col1name, col2name, bck) { E.showMenu(menu); }, /*LANG*/"Gradient" : function() { - showMenu=()=>{ + let showMenu=()=>{ var cols = [ // list of color palettes used as gradient colors ["#0ff","#00f"], ["#ff0","#0f0"], From d456aa03eda3371ee0ba74c70fc80c2088c19158 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 22 May 2026 13:23:29 -0400 Subject: [PATCH 3/4] remove print statement --- apps/clockbg/settings.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/clockbg/settings.js b/apps/clockbg/settings.js index 608cfbff9af..8e410cd2e6b 100644 --- a/apps/clockbg/settings.js +++ b/apps/clockbg/settings.js @@ -338,7 +338,6 @@ let customMenu=function(style, col1name, col2name, bck) { menu[getColorsImage(col)] = () => { settings.style = "gradient"; settings.colors = col; - print(col) saveSettings(); showMainMenu(); }; @@ -390,4 +389,4 @@ let customMenu=function(style, col1name, col2name, bck) { */ -}) \ No newline at end of file +}) From 5ae8ce8f82dea6863883f2bbf7c12f7c5bd0cd61 Mon Sep 17 00:00:00 2001 From: RKBoss6 Date: Fri, 22 May 2026 13:32:57 -0400 Subject: [PATCH 4/4] Remove /*LANG*/ markers that don't do anything --- apps/clockbg/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/clockbg/settings.js b/apps/clockbg/settings.js index 8e410cd2e6b..6db04ff6d6d 100644 --- a/apps/clockbg/settings.js +++ b/apps/clockbg/settings.js @@ -48,7 +48,7 @@ let customMenu=function(style, col1name, col2name, bck) { var col1 = "", col2 = ""; function buildMenu() { var menu = {"":{title:/*LANG*/"Custom", back:bck}}; - menu[/*LANG*/col1name] = function() { + menu[col1name] = function() { require("colorpicker").show({ back: buildMenu, onSelect: function(c) { col1 = c }