From 45a292bc5d6b08096d819eb8261ae43de7cb00f0 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Sat, 4 Dec 2021 22:13:44 +0100 Subject: [PATCH] new custom menu: menuwheel --- apps.json | 18 +++ apps/menuwheel/ChangeLog | 1 + apps/menuwheel/README.md | 25 +++ apps/menuwheel/boot.js | 213 +++++++++++++++++++++++++ apps/menuwheel/icon.png | Bin 0 -> 880 bytes apps/menuwheel/screenshot_b1_dark.png | Bin 0 -> 4610 bytes apps/menuwheel/screenshot_b1_edit.png | Bin 0 -> 4203 bytes apps/menuwheel/screenshot_b1_light.png | Bin 0 -> 4183 bytes apps/menuwheel/screenshot_b2_dark.png | Bin 0 -> 2905 bytes apps/menuwheel/screenshot_b2_edit.png | Bin 0 -> 2636 bytes apps/menuwheel/screenshot_b2_light.png | Bin 0 -> 2567 bytes 11 files changed, 257 insertions(+) create mode 100644 apps/menuwheel/ChangeLog create mode 100644 apps/menuwheel/README.md create mode 100644 apps/menuwheel/boot.js create mode 100644 apps/menuwheel/icon.png create mode 100644 apps/menuwheel/screenshot_b1_dark.png create mode 100644 apps/menuwheel/screenshot_b1_edit.png create mode 100644 apps/menuwheel/screenshot_b1_light.png create mode 100644 apps/menuwheel/screenshot_b2_dark.png create mode 100644 apps/menuwheel/screenshot_b2_edit.png create mode 100644 apps/menuwheel/screenshot_b2_light.png diff --git a/apps.json b/apps.json index a312b90a3..d5e84f9dd 100644 --- a/apps.json +++ b/apps.json @@ -4675,5 +4675,23 @@ "data": [ {"name":"pooqroman.json"} ] + }, + { + "id": "menuwheel", + "name": "Wheel Menus", + "version": "0.01", + "description": "Replace Bangle.js 2's menus with a version that contains variable-size text and a back button", + "readme": "README.md", + "icon": "icon.png", + "screenshots": [ + {"url":"screenshot_b1_dark.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_light.png"}, + {"url":"screenshot_b2_dark.png"},{"url":"screenshot_b2_edit.png"},{"url":"screenshot_b2_light.png"} + ], + "type": "boot", + "tags": "system", + "supports": ["BANGLEJS","BANGLEJS2"], + "storage": [ + {"name":"menuwheel.boot.js","url":"boot.js"} + ] } ] diff --git a/apps/menuwheel/ChangeLog b/apps/menuwheel/ChangeLog new file mode 100644 index 000000000..defdb5049 --- /dev/null +++ b/apps/menuwheel/ChangeLog @@ -0,0 +1 @@ +0.01: New menu! diff --git a/apps/menuwheel/README.md b/apps/menuwheel/README.md new file mode 100644 index 000000000..22cb49466 --- /dev/null +++ b/apps/menuwheel/README.md @@ -0,0 +1,25 @@ +# Wheel Menu + +Replace Bangle.js 2's menus with a version that contains variable-size text and a back button. + +Bangle.js 1: +![Dark Mode Screenshot](screenshot_b1_dark.png) +![Light Mode Screenshot](screenshot_b1_light.png) + +Bangle.js 2: +![Dark Mode Screenshot](screenshot_b2_dark.png) +![Editing Screenshot](screenshot_b2_edit.png) +![Light Mode Screenshot](screenshot_b2_light.png) + + +## Features + +If the menu contains "Back" or "Exit", it is shown as a button instead. +The menu wraps around, with a divider between the last and first items. + +## Controls + +Bangle.js 1: Use BTN1/BTN3 to scroll through items, BTN2 to open/edit the selected item. +Bangle.js 2: Swipe up/down to scroll through items, tap/BTN to open/edit the selected item. + +Press the back button (if present) to go back. \ No newline at end of file diff --git a/apps/menuwheel/boot.js b/apps/menuwheel/boot.js new file mode 100644 index 000000000..3e708e9a8 --- /dev/null +++ b/apps/menuwheel/boot.js @@ -0,0 +1,213 @@ +E.showMenu = function(items) { + g.clearRect(Bangle.appRect); // clear screen if no menu supplied + // clean up back button listener + if (Bangle.backHandler) Bangle.removeListener('touch', Bangle.backHandler) + delete Bangle.backHandler; + if (!items) { + Bangle.setUI(); + return; + } + + var B2 = process.env.HWVERSION===2, + loc = require("locale"), + menuItems = Object.keys(items), + options = items[""]; + if (options) menuItems.splice(menuItems.indexOf(""),1); + if (!(options instanceof Object)) options = {}; + + // show "< Back" item (or similar) as button instead (i.e. remove from the menu) + var back,backLbl; + for (var b of ['Back', 'Exit', 'Cancel']) { + if (!items[b] && items['< '+b]) b = '< '+b; + back = items[b]; + if (typeof back === "function") { + backLbl = loc.translate(b); + menuItems.splice(menuItems.indexOf(b),1); + break; + } + else back = undefined; + } + // font sizes + var small = B2?15:22, + large = B2?30:45; + if (options.selected === undefined) options.selected = 0; + var ar = Bangle.appRect, + x = ar.x, + x2 = ar.x2, + w = ar.w, + y = ar.y, + y2 = ar.y2; + if (options.title) y += 22; + var wrap = menuItems.length>3; // don't wrap if all items are always in view anyway + + var vc=Math.round((y+y2)/2), // vertical center + hc = Math.round((x+x2)/2), // horizontal center + ih = large+small*2; // active item height + + var getItem = idx => { + // we wrap out-of-range indexes + while (idx<0) idx+=menuItems.length; + idx = idx%menuItems.length; + var name = menuItems[idx]; + var item = items[name]; + var v; + if ("object"== typeof item) { + v = item.value; + if (item.format) v = item.format(v); + v = loc.translate(""+v); + } + return {lbl: loc.translate(name), v: v}; + }; + var l = { + lastIdx : null, // we want a complete redraw on first run + draw : function() { + var idx = options.selected, + edit = l.selectEdit; + g.reset(); + + // don't highlight whole item when editing + g.setColor(edit?g.theme.fg:g.theme.fgH) + .setBgColor(edit?g.theme.bg:g.theme.bgH) + .setFont('Vector', large); + var item = getItem(idx), + lw = g.stringWidth(item.lbl)+2; + if (lw+2 >= w) { // label width doesn't fit at large size: scale it down + g.setFont('Vector', Math.floor(large*ar.w/lw)); + } + g.clearRect(x,vc-ih/2,x2,vc+ih/2) + .setFontAlign(0,0,0).drawString(item.lbl,hc,vc); + + if (item.v !== undefined) { + g.setColor(g.theme.fgH).setBgColor(g.theme.bgH) // always highlighted: either as part of item, or while editing + .setFontAlign(0,1,0) + .setFont('Vector', small) + .clearRect(x,vc+ih/2-small-2,x2,vc+ih/2) + .drawString(item.v,hc,vc+ih/2-1); + if (edit) { + g.drawImage("\x0c\x05\x81\x00 \x07\x00\xF9\xF0\x0E\x00@",x2-23,vc+ih/2-small+(B2?1:5),{scale:2}); + } + } + if (l.lastIdx !== idx) { + // we scrolled: redraw all + l.lastIdx=idx; + g.reset(); + + if (options.title) { + if (B2) g.setFont('12x20'); + else g.setFont('6x8',2); + g.drawLine(x, y-2, x2, y-2) + .setFontAlign(0,1,0) + .drawString(options.title, (x+x2)/2, y-2); + } + + // clear prev/next items area + g.clearRect(x,y,x2,vc-ih/2-1) + .clearRect(x,vc+ih/2+1,x2,y2); + + // get display label by index + var lbl = idx => { + var item = getItem(idx); + if (item.v !== undefined) item.lbl+=': '+item.v; + return item.lbl; + } + // previous two items + g.setFontAlign(0, 1) + if (wrap||idx>0) g.setFont('Vector', small).drawString(lbl(idx-1), hc, vc-ih/2-5); + if (wrap||idx>1) g.setFont('Vector', small/2).drawString(lbl(idx-2), hc, vc-ih/2-small-10); + // next two items + g.setFontAlign(0, -1); + if (wrap||idx g.drawLine(x, y, x2, y); + if (idx===0) div(vc-ih/2-1); + if (idx===1) div(vc-ih/2-small-8); + // if (s === 2) div(vc-ih/2-small*1.5-13); + if (idx===menuItems.length-1) div(vc+ih/2+1); + if (idx===menuItems.length-2) div(vc+ih/2+small+6); + // if (s === 2) div(vc+ih/2+small*1.5+13); + } + + if (back) { + g.setBgColor(g.theme.bg2) + .setFont('Vector', small); + var bw=g.stringWidth(backLbl); + g.clearRect(x,y, x+bw+2, y+small+2); + var bx1=x, by1=y, bx2=x+bw+2, by2=y+small+2; + // g.drawRect(x,y, x+bw+2, y+small+2); + var poly = [ // button outline + bx1+2,by1, + bx2-2,by1, + bx2, by1+2, + bx2, by2-2, + bx2-2,by2, + bx1+2,by2, + bx1, by2-2, + bx1, by1+2, + ] + g.setColor(g.theme.bg2).fillPoly(poly, true) + .setColor(g.theme.fg2).drawPoly(poly, true) + .setFontAlign(-1,-1,0).drawString(backLbl, x+2,y+2); + } + } + g.flip(); + }, + select : function() { // same as default menu + var item = items[menuItems[options.selected]]; + if ("function" == typeof item) {l.lastIdx=null; item(l);} // force a redraw after callback + else if ("object" == typeof item) { + // if a number, go into 'edit mode' + if ("number" == typeof item.value) + l.selectEdit = l.selectEdit?undefined:item; + else { // else just toggle bools + if ("boolean" == typeof item.value) item.value=!item.value; + if (item.onchange) {l.lastIdx=null; item.onchange(item.value);} // force a redraw after callback + } + l.draw(); + } + }, + move : function(dir) { + if (l.selectEdit) { // same as default menu + var item = l.selectEdit; + item.value -= (dir||1)*(item.step||1); + if (item.min!==undefined && item.valueitem.max) item.value = item.wrap ? item.min : item.max; + if (item.onchange) {l.lastIdx=null; item.onchange(item.value);} // force a redraw after callback + } else { + if (B2) dir=-dir; // swipe vs button scrolling + if (!wrap && (options.selected+dir<0 || options.selected+dir>=menuItems.length)) { + return; + } + options.selected = (options.selected+dir+menuItems.length)%menuItems.length; + } + l.draw(); + } + }; + l.draw(); + Bangle.setUI("updown",dir => { + if (dir) l.move(dir); + else l.select(); + }); + if (back) { + // we have a back button: check touches before passing them to setUI's touchHandler + if (B2) { + Bangle.removeListener('touch', Bangle.touchHandler); + Bangle.backHandler = (b, xy) => { + // anywhere top-left (but above the active item) = back button + if (xy.x { + // left side = back button + if (b===1) back(); + } + } + // note: backHandler is cleaned up at the top of this file + Bangle.on('touch', Bangle.backHandler); + } + return l; +}; diff --git a/apps/menuwheel/icon.png b/apps/menuwheel/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..61f94a035078db81a386a8a42af3c00f16e91614 GIT binary patch literal 880 zcmW-fJ!lnC5QWdvC?+9-l@csO!Ad1C+K3Pn67vEk#Ag~)Jqe6$F#eIoAo4&`*aUGv zBq;iQ)9r~4_eg#A z?dIM6*1yHocWrEDOnUWj@ZZ%v#I5Dd)OhRb#}ju6jx4r2Gt$a&Y4yJJmrtudq?@4p z>`G@JO9z*pY+pJj?OL0=I6wJb%6dv>WO}A$sz#+`i7cMQvZyAbWJHE%ScYn?m-I;Y zbW2xlwUQQTo@QwRDOFOD@|2|rzy=yDnyFTuQ%{uQrC2GdV~HY4cnK?^%7UavaW8Jg zRY_56QOt{3F~O16qA2nrD`G0F0fRIwp+%0tU;_;n&8mWG%<%||P}Rp+BKL3$SCtD3 zlICF+Ce%ZU+K`7VggpF!Cf(S9en_AY)__48mXyMS!C(Uoma20#P0TTORU^^ISRykw z;VW`sLDFR|e8g!CfEVQ$jasb44`|Yj9dJT03SkWxr0IkfJQxf%(5fq{)uf3zVq+MI zKE@Ime^2u_RJqN9*=XHov!qI@@x89Mv9fY(+^O4c1Xs$9hPwQQR#nhU0var~gn4LU z)QjamYh0}xo!QGe|NRS})xXrfvMilCR-e|LXQQ|HcxHNHY`*(hNZ?VW|A@1FM#JUZRG{b^+3_5RNb>ldV7hezM8|K1t|K+}`66EDZR G_x=N44umcM literal 0 HcmV?d00001 diff --git a/apps/menuwheel/screenshot_b1_dark.png b/apps/menuwheel/screenshot_b1_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..c6dfb802b1bc3cc46ff9710f07c20ba6758a7c0d GIT binary patch literal 4610 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 Jr5W!2zX4`DYqS6W literal 0 HcmV?d00001 diff --git a/apps/menuwheel/screenshot_b1_light.png b/apps/menuwheel/screenshot_b1_light.png new file mode 100644 index 0000000000000000000000000000000000000000..35ac01fe972f46b7c9cd4f51e891210ef3220810 GIT binary patch literal 4183 zcmds5`8(8Y)E{3XgD^v@WlYv&7f%>tDK$x`QH7;{8?@m21BB(tt>A@c`W3PP?$%gU?ZqF?oRxI z>Dk-%l{gcs-nsqSHx+r8oe;0fj|dTB7Dx(D70f-igaPCb1v&;FEf0Zr^zQwKKbp?y)c?#b(i&_)- zZkb`l78ZT8mpB|yg!K!!A(Ohoa_}LTNn*?{S;OP<`Ux$rB{k%X+f|=;V7=b|?mojh zVr6Zl{+J<8#5Nm{=YAtFq2|;dSx90^5(o(JgHE0jDg6D=m7!Fzha-q^UQUJq<~^bm zBx9~OYWDfVi{FLQ%6!0ojWU$b(Yaw(D*avHOgwD1%tnzbNaKk{-*Kt;)Hnc`pIfoy zn=qwk#lVC7o7?5fNPsW^%cEql+cNB^7969S3Bz)|ptF)-I=qQ)W**0ZSa@ye;Kgp% zI+y1Qe=~P?c5C077RSjUB(r5JtvTut_r^-$+A)w+GT3fE8D9CG$74B9)uy^)deR{` zFsOZ{z_+$NKT!z8Rv&V>E>ft(eGHyLld7vWkx}rA({g9Ma3D*>{E!|X8Df~PU+ZAZqPB19Z) zxr{)=c+NWcfG6E$_S@N8nSAh-l;kLu)RY2N=DVCc__HmRJ7{P|V`9Rsj z=_wk2y!fS%cD?oj`R6>8@8Qjb0YR~M>{5qmxsDZBC2m5*pB)2;55UZ*Cmmbn_&P`q zC}HTdRvZwZLy_rt(O}&1j5x)>EVbBF?XCG%RCHO0nXX&|q?P7k_7{y)Pcy1CSF%

P8u>Ns5%r~m0zRXO?^ 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 NHkS4lRc7Sd{|4C5NoW87 literal 0 HcmV?d00001 diff --git a/apps/menuwheel/screenshot_b2_edit.png b/apps/menuwheel/screenshot_b2_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..bca98a9a5401dfc21ad5bac060a1ff5aaa82c989 GIT binary patch literal 2636 zcmbuBYc$mR8pnS#V`emi3ArQ|8RGTZ-eXv(C%2)>-Ggc%JWz?^@5BXFZ?q^M|W5K^mii z0RTv!JYnayt#$t_Nz`_)ZTR4`EwGDj1RGHEO7$ZE7}QBS>(ex!`Qq0xEOn{lW?NgY zpL7@YkGrX(TVJ=27Ss?W#{>85n|V1oJ3G7ar`oQ_2M~f$Y%kNm!~lJDE)g)iQ*k1g zGU}`@fNI)OKLaU5jRNo+9B+#SS7Cz~P&X?cHwSHuP$YQw+gc=^;SX`(V9u0of`jyh zBp8UbZRMGt2~@J5WMC*D(DW!JRvo;lI~I6kQ&p0bU`F);l2~4;%N-T2c__BWb8=4gskc zdQcQpgnYSBWC>57#QvgVlvhNo{DUA0;j}C*lHt5)w6*l712B9s(==80F~Uzn2Sd-_ z{jB4GloUiX)VnkSZySzryC0Cj?m_`wht0A5b|`$~+DxlSK*(vz)DJh2?}pt@)0K+{ zhvsboE4)mL?XDg=(U3nzV&;^Sjz&?)48F4i?rY9^vGjysu?_G%z}NNdWhQhktOyX| zd0FvK0BmHA%|E$JU#Ld%*2e^^ z?)x(fne0&gX|GsB9cpD(wWdH}U2i|gLYGTN`p6yJ{RqjYl<0xT!l;XpH`2*i`6FTz zUT?ctw6BI0q(Jixo$0>9OTf((wNys*ruGNVM+}Ipdq_hMSK2+h?(6gcbLDjAbOd8H z`IhhZ26~0{DEwWM^2?+P#fE4Y&m|ykZzaUu&CM^Y{-rukQv1zdSMGpTch1bYcv8}2 zs8Axx3;)PLT0v;IZ15f~Hr#bbO0^lw)jQ z;Fr19?pn@w=>?(#{J(VMzAG;O1c(LuyEN!FjT7r{M4k%PwSj z$BxK2(;s1lmPESk z0cFSvj4w{EyTcp$o-un-Y2WiKWWjGdiQYXB^~!2tI#^q{5$}ciQZq-0(O}J0evl32 zJB8ja-9v&LB)-yz1( zJOUT`uTFW)uVF0B%T!f_M8(HeACafmxtFom?qu*H))mn>fh3a-P!&eb|%(+$Djn`S#}L0 zWZ7u=`R^4bPShUYDOKoZ?`frN6isEBMe%79Z_gSdgh^s;KyuYKy6y%GL7?_^y(IE3Zo z6%*>^5z4p7_EMtMALumYSVsIIH}SU=R~W}YjJJx{vNh$`VJaV zL-$y$TUbd*t?lDyL=W5?u_hSGgqky8Q2dRUJ{P13T7yaaNweV1t3wkc$M2GXPnt#_ z3@yYQCP?4DQ07H_8mB~2Ea5MbPP%sEz_%8iaac^<3E@Nv9GGk1y?Z?Xqc;nN|7Dvl z@)NI@=}55lO<3Mzl33g}>n-MK0fDv5su42?SBH9?cc*-B*dRuCvSn?O7Xqw;bq96U_%B vpayI#YX>5dMH7S@0eW+GXOaHDtj{>R!WL)!MS{-udk9V*cebms@k{;(%;URD literal 0 HcmV?d00001 diff --git a/apps/menuwheel/screenshot_b2_light.png b/apps/menuwheel/screenshot_b2_light.png new file mode 100644 index 0000000000000000000000000000000000000000..4ffe08fe35f376e8eecd9c5fedbde53df07669de GIT binary patch literal 2567 zcmcIm`#Y5D8h&Rq#*AZFvMmj=P?)faB*&a$oEnFQk@aCw873m9k|7mIv$PmHEgi|3 zVa$&ge$?@@WmB(w~ z0RZxDC!EMgWdCQ$NF#fF^Hc~DQ1N8f-_TOFT1 z8^k>A4L@n$x5-`an9%;%f5+jSmlkhE{N<&cD#b4Y3rvcScI^ZJ1}gL%1|-j}?zRTq zFdq*@{8`;(a6O~!J0L6@auoqNDUuO@N##r-N?n?T+$i$W>Xb5-Gp+@|q{KE83#n6r zP$0xAZI>c!%I2U!TY^M}zzBu@2jW;wnAzK1z#iQSIY^!S8g?f-doT{n7>;UVggPG2 z^)bREXI7OlXr*X`$-%&SS6hV}Y7E4>eGYo_y0^3`NNaNL2>xc_HejP5+NG0GM_1}c zcTB#pC8u$16%^F7-NI97GMjUyC2}5P<=Jj=UUmQP5)=gP7kc^z136gVC(;}(gcw{W zl0dtGqomeRJaGGgRIDk?S{h#?%q8w>xaf(TtNrX@roccY}=eSYchj+E&RPE@}-(7+BaP=;Mqyb>jRGf;N@c< zpY;>2ZJBS^PPr_rTZEttMVp#R4mZ%udO>T6g30f4X?CRK(m(I~!s}g@5*^IzxwIE- zr^H6y{jj{YcA5SL^Q|U3{l zwPV42Xz1~;wxgHH8C1X1i5`Cwa`f1Ke<@Q&na(2YI9wjwUPMR#fcjxrs=OHAj$zn_MaPh#gk!FV zI$O4$McC9#t!`F2X(PhKO%e)%8VgPnK~9D zA3JN)#%i8PZhR-ceu8$3HCngc^C57_qlx;>GGf7*pRcsR2+wO+t=<&$UfrnV8r;77 zaJ8p#O_4KN6;Pbagzm&BTC)C-TylQU*1Zq&?JVO#epy9PmT)=g#an9);Zn7@H=X+u z9qTRE*(jK!&kJ(IAb8%nj91!`GDyZfF*F5&=jRvi*06>-UFXs&6900aSIYsNYQ-sDF=gMs>4H`ug#V)vM{g&kzj8m$7 zObxACgUS8_M;>x?M(s`X|IkIP@925ZWC!DAL*M?)n&+q+k1RKu!}FyXC#Z;^1E$rF zTtfqEXAPOh{wP5Aza8hhAHcq)8QMYr4Hj+O@G6(d)%Jm(0`M-OD~gQx1;0m$<%t4K zvsrISkpeHx(J{V<2GK*fYBz+(O~sL7fdOMrRRVrB1n{r=*V)l{!1gq#(f0=o15dfo zx2gcT=SA_X0>Haiy{qv6fGtYc#eOJo|FBtBgfC#kScRHbB2PURCe#1a-WpzN&i>{l z5WBwHm*$r0Bw?=l_n)@)9vG>5p_iCDV8j}JZPk(IKZs}e2Bz+6)__c>ZrmN&;l#ca1-&`$*RA=smGQELA-lZmr zaM?QiJu`9%L!nWv#{;5g=sKBdfeB9#IK8x9hEaBdE9P~cGdn`hFY^LA;z=Pgc(yCCdD>z%&9d;sbz=I zxxVoSJa|t|t(c*q>O?gmO%g&A)3;rI*coOD>^R>~iFh^<-Ydp#?5=lG?8EE){vXqs z(!}pNE~*r+j)11A12^l-QXx-uS?k$Ms*z~g5k08fZ3v1a5TL)m62hRBhtV)}n?PEF zg}dm?-y^y?oa6Tm+(T3sgId{jE%k#@T@rx0W zY1%g5$5Um&J~Gekd7h;*)E}=c_%Nc9)L-aL>>LC|Bh!OUt##tf#xA3^ffQ5)YB8sB zXkARqGO%Ab7NG`5iJdn`wKo&OUfc_y<-4bzn-^K*tmQ0YMzNG*M@N;ovAtr)n@Wt*>cVfnrFH+2N;_)$_>ay8j%LhH6))aJ6eWECT<+r#n^65r+yC3ht{t zQv2x#`zL8Jm{|F+N<*rG7uU6ZZTG