From 0be14efaec20bb6c261da53f845f5e6030cc2147 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Sat, 15 Jan 2022 18:16:48 +0100 Subject: [PATCH 1/8] wid_edit: Widget Editor --- apps.json | 18 ++++ apps/wid_edit/ChangeLog | 1 + apps/wid_edit/README.md | 19 ++++ apps/wid_edit/boot.js | 45 ++++++++ apps/wid_edit/icon.png | Bin 0 -> 1897 bytes apps/wid_edit/settings.js | 218 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 301 insertions(+) create mode 100644 apps/wid_edit/ChangeLog create mode 100644 apps/wid_edit/README.md create mode 100644 apps/wid_edit/boot.js create mode 100644 apps/wid_edit/icon.png create mode 100644 apps/wid_edit/settings.js diff --git a/apps.json b/apps.json index 2e11e37f6..fe37683f3 100644 --- a/apps.json +++ b/apps.json @@ -5682,5 +5682,23 @@ {"name":"crowclk.app.js","url":"crow_clock.js"}, {"name":"crowclk.img","url":"crow_clock-icon.js","evaluate":true} ] + }, + { + "id": "wid_edit", + "version": "0.01", + "name": "Widget Editor", + "icon": "icon.png", + "description": "Customize widget locations", + "supports": ["BANGLEJS", "BANGLEJS2"], + "readme": "README.md", + "type": "bootloader", + "tags": "widget,tool", + "storage": [ + {"name":"wid_edit.boot.js","url":"boot.js"}, + {"name":"wid_edit.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"wid_edit.json"} + ] } ] diff --git a/apps/wid_edit/ChangeLog b/apps/wid_edit/ChangeLog new file mode 100644 index 000000000..2fa857bd8 --- /dev/null +++ b/apps/wid_edit/ChangeLog @@ -0,0 +1 @@ +0.01: new Widget Editor! \ No newline at end of file diff --git a/apps/wid_edit/README.md b/apps/wid_edit/README.md new file mode 100644 index 000000000..123ac8946 --- /dev/null +++ b/apps/wid_edit/README.md @@ -0,0 +1,19 @@ +# Widget Editor + +This adds a setting menu which allows you to change the location of all widgets. + +## Settings + +There is no app icon in the launcher; you can find the settings under +`Settings`->`App/Widget Settings`->`Widget Editor`. + +For every widget, you have these options: +* **Area**: In which corner to draw the widget. + * **Note**: Not all apps handle widgets in the bottom corners well. +* **Sort Order**: Changes the order if several widgets use the same corner. +* **Enabled**: We do our best to hide disabled widgets, but they are still + loaded and use RAM. + +## Creator + +Richard de Boer diff --git a/apps/wid_edit/boot.js b/apps/wid_edit/boot.js new file mode 100644 index 000000000..e4f909eab --- /dev/null +++ b/apps/wid_edit/boot.js @@ -0,0 +1,45 @@ +Bangle.loadWidgets = function() { + global.WIDGETS={}; global._WIDGETS={}; + require("Storage").list(/\.wid\.js$/) + .forEach(widget=>{ + try { eval(require("Storage").read(widget)); } + catch (e) {print(widget,e);} + }); + const o = require("Storage").readJSON("wid_edit.json", 1) || {}, + c = o.custom || {}; + let _W; + for (const w in c){ + if (!(w in WIDGETS)) continue; + _W= {}; + if (c[w].hide) { + // disabled: move widget to _WIDGETS, and place it way offscreen (in case it tries to draw itself anyway) + _W = WIDGETS[w]; _W.x = 1000; _W.y = 1000; + WIDGETS[w] = {draw:()=>{}}; // in case it tries to call itself + } + else { + // enabled: store default area/sortorder in _WIDGETS (only if changed) + if (c[w].area) _W.area = WIDGETS[w].area; + if ('sortorder' in c[w]) _W.sortorder = WIDGETS[w].sortorder; + Object.assign(WIDGETS[w], c[w]); + } + if (Object.keys(_W).length) _WIDGETS[w] = _W; + } + if (!Object.keys(_WIDGETS).length) delete _WIDGETS; // no need for this + const W = WIDGETS; + WIDGETS = {}; + let a, t=0, b=0; + Object.keys(W) + .sort((a, b) => (0|W[b].sortorder)-(0|W[a].sortorder)) + .forEach(k => { + WIDGETS[k] = W[k]; + if (a=W[k].area) { + t = t || (a[0]=="t"); + b = b || (a[0]=="b"); + } + }); + Bangle.appRect = { + x: 0, y: t*24, + w: g.getWidth(), h: g.getHeight()-1-(t+b)*24, + x2: g.getWidth()-1, y2: g.getHeight()-1-b*24 + } +} \ No newline at end of file diff --git a/apps/wid_edit/icon.png b/apps/wid_edit/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e02a636af52160ff0d16a0f3bf323b87f10ea9f4 GIT binary patch literal 1897 zcmV-v2bTDWP)G(@y{(3T1Z5k&~~fmHR8pej-uY(pro#3Xok?b-do)jkLOV&d&dT?>YZ_=8P<|8hd2M!MDcNi0W^F{ES0Lg$5hr8^F+*dqnpwZI69-roe~y#_t!=!$80_6>;Z; z;RN4X9oliWrl3m<>t5h{YC=u!#5SDZlY`wqC^#z0LqH;h zL|bg#w1IDrKByRnfjlqpUg{*_)@VaIsSLM9*Q#&`$Z-r?AnSW-f-iabRBhjrFSfPJ zTh}FjL+|xDyFS^`+=tVx2JRmY`Dh@ysGly#q$}1Px|y- zJU_Ba9?B@f_oqCB0ye)x_^Eq5F*JlZJmgh2{2tL&psRpCr1%XFk?`_^V~7!%wj|yV zX}(vHUjbzz!qv+c8Hj%D`YWkF{_+bgPQO)^-`j%dF2^>IUsTNelADq{5mS|IolTp6 zo(&!&TLK-8^=~S=DXDw^i$~q-(4)4rdF;q9fL^C&4#i?4i9RyWo-O!E5g66|InD%j z*+*|4`eocbc>K@{ap3CM9!cRr%21N+yR80W4U<&l7+d#nyN_iEz?S1L!|jZ zRo(?EM)J@mSTxryhXLT&s>)4|B?neNwCQn$W6n1%RUP%cF27$A^ZEUhEH1}L4M7CCyQ6XQpQdyNRJ6~FwBhqnTvCc{ zSs8=3EtBGfWuD-H1Y*ahK7=g)TfjfOrt%9y%q_b)<4vDeb_@9h!E`sg)v>ewpHog? z|IeNnwp1TUR~iHhiz!)LPHrF{+qTE%g(|jf;m^&ZWYMh@6c(o`BNWr0`|YekF`Jv; zIeGlb7gw$>0q$^3T>^qWpPyh+F?j_+e1?JH_uZrc>#(RmNNgQ#mv8XF+~eX$qnQuSKS6G zMdkCTtd$$5^CipVK-Pz9Mh&Ou2bZXA-q}+f`pw9DLG!mj;`?{j9d+LK%}A?1$iRTB zkL0v7`y1<)r}UB{E#E=-Pv_3Mzb$L;zsttBv!^<^XG#bxe=fSv@^KR3vx;o&tqFEc z@W%Eou)@2qrLS~0)>owSUDn$FWn1Kp(a`$jZI$KE^%Yuvx)4?*JJlJq+s~zcn4HTH zJV4{pw&>Fn5(u}&JU^x_dRFtToZKV8ZDR@R>hZT!E~Gt6(DPY___!p(tt#d;_-Vg*OkSYM)L@zln!+gJb*aA6AQ8%G*972uDCdD zskQQ4P4P#}fQ?5&6n_$Y3V}5Co~qo_TN}K@4B9A(E}(GIDkSh<6?4^0gLmY_*Ma^? z5m05&G+5nRJ-3%xvKu61C~E>Dk% j_>8O3nI@4bc!2){CAl3-AqjN_00000NkvXXu0mjfZ`HIs literal 0 HcmV?d00001 diff --git a/apps/wid_edit/settings.js b/apps/wid_edit/settings.js new file mode 100644 index 000000000..cdbe90801 --- /dev/null +++ b/apps/wid_edit/settings.js @@ -0,0 +1,218 @@ +/** + * @param {function} back Use back() to return to settings menu + */ +(function(back) { + const names = {}; + const settings = require("Storage").readJSON("wid_edit.json", 1) || {}; + if (!('custom' in settings)) settings.custom = {}; + global._WIDGETS = global._WIDGETS || {}; + // Adjust appRect to always have room for widgets while in this app + Object.assign(Bangle.appRect, { + y: 24, h: g.getHeight()-25, y2: g.getHeight()-48, + }); + /** + * Sort & redraw all widgets + */ + function redrawWidgets() { + let W = WIDGETS; + global.WIDGETS = {}; + Object.keys(W) + .sort((a, b) => (0|W[b].sortorder)-(0|W[a].sortorder)) + .forEach(k => {WIDGETS[k] = W[k]}); + Bangle.drawWidgets(); + } + + /** + * Try to find app name for widget + * @param {string} widget WIDGETS key + * @return {string} widget name + */ + function name(widget) { + if (!(widget in names)) { + let infoFile = widget+".info"; + // widget names don't always correspond to appid :-( + // so we try both with and without 'wid'-prefix + if (!require("Storage").list(new RegExp(`^${infoFile}$`)).length) { + infoFile = (widget.substr(0, 3)==="wid") ? infoFile.substr(3) : ("wid"+infoFile); + } + names[widget] = (require("Storage").readJSON(infoFile, 1) || {}).name || widget; + } + return names[widget]; + } + + function edit(id) { + // disabled widgets were moved to _WIDGETS and replaced with a dummy {draw}, so don't have area set + let WIDGET = WIDGETS[id].area ? WIDGETS[id] : _WIDGETS[id], + def = {area: WIDGET.area, sortorder: WIDGET.sortorder|0}; // default values (disabled is never the default) + if (WIDGET.area && id in _WIDGETS) Object.assign(def, _WIDGETS[id]); // enabled widgets have defaults saved in _WIDGETS + + const areas = ['tl','tr','bl','br']; + settings.custom = settings.custom||{}; + let saved = settings.custom[id] || {}, + area = saved.area || def.area, + sortorder = ("sortorder" in saved) ? saved.sortorder : def.sortorder, + enabled = !saved.hide; + + /** + * Draw highlighted widget (if enabled) + */ + function highlight() { + if (WIDGET.width && enabled) { + WIDGET.draw(); + g.setColor(g.theme.fgH) + .drawRect(WIDGET.x, WIDGET.y, WIDGET.x+WIDGET.width-1, WIDGET.y+23); + } + } + highlight(); + + /** + * Save widget and redraw with new settings + */ + function save() { + // we only save non-default values + saved = {}; + if ((area!==def.area) || (sortorder!==def.sortorder) || !enabled) { + if (area!==def.area) saved.area = area; + if (sortorder!==def.sortorder) saved.sortorder = sortorder; + if (!enabled) saved.hide = true; + settings.custom = settings.custom || {}; + settings.custom[id] = saved; + } else if (settings.custom) { + delete settings.custom[id] + } + if (!Object.keys(settings.custom).length) delete settings.custom; + require("Storage").writeJSON("wid_edit.json", settings); + delete saved.hide; // no need to save this inside the widget + Object.assign(WIDGET, def, saved); + if (WIDGET.sortorder === undefined) delete WIDGET.sortorder; // default can be undefined, but don't put that in the widget + if (enabled) { + WIDGETS[id] = WIDGET; + delete _WIDGETS[id]; + // if we assigned custom values, store defaults in _WIDGETS + let _W = {}; + if (saved.area) _W.area = def.area; + if (def.sortorder !== undefined) { + if ('sortorder' in saved) _W.sortorder = def.sortorder; + } + if (Object.keys(_W).length) _WIDGETS[id] = _W; + } else { + // disabled: move original widget into _WIDGETS, and place it offscreen + _WIDGETS[id] = WIDGET; + WIDGETS[id] = {draw: () => {}}; // in case it tries to call itself + } + // drawWidgets won't clear e.g. bottom bar if we just disabled the last bottom widget + if (WIDGET.width) g.reset().clearRect(WIDGET.x, WIDGET.y, WIDGET.x+WIDGET.width-1, WIDGET.y+23); + redrawWidgets(); + + highlight(); + m.draw(); + } + + const menu = { + "": {"title": name(id)}, + /*LANG*/"< Back": () => { + if (WIDGET.width) { + g.reset().clearRect(WIDGET.x, WIDGET.y, WIDGET.x+WIDGET.width-1, WIDGET.y+23); + WIDGET.draw(); + } + mainMenu(); + }, + /*LANG*/"Area": { + value: areas.indexOf(area), + min: 0, max: 3, wrap: true, + format: a => [ + /*LANG*/"Top Left", /*LANG*/"Top Right", + /*LANG*/"Bottom Left", /*LANG*/"Bottom Right" + ][a], + onchange: a => { + area = areas[a]; + save(); + } + }, + /*LANG*/"Sort Order": { + value: sortorder, + onchange: o => { + sortorder = o; + save(); + } + }, + /*LANG*/"Enabled": { + value: enabled, + format: e => e ?/*LANG*/"Yes" :/*LANG*/"No", + onchange: e => { + enabled = e; + save(); + } + }, + /*LANG*/"Reset": () => { + area = def.area; + sortorder = def.sortorder; + enabled = true; + save(); + mainMenu(); // changing multiple values made the rest of the menu wrong, so take the easy out + } + } + + let m = E.showMenu(menu); + } + + + function mainMenu() { + let menu = { + "": {"title": /*LANG*/"Widgets"}, + }; + menu[/*LANG*/"< Back"] = ()=>{ + if (!Object.keys(_WIDGETS).length) delete _WIDGETS; + // adjust appRect for new widget locations + let a, t=0, b=0; + for (let WIDGET of WIDGETS) { + if (a=WIDGET.area) { + t = t || (a[0]=="t"); + b = b || (a[0]=="b"); + } + } + Object.assign(Bangle.appRect, { + y: t*24, + h: g.getHeight()-(t+b)*24, + y2: g.getHeight()-1-b*24 + }); + back(); + }; + const enabled = Object.keys(WIDGETS).filter(id => WIDGETS[id].area); + enabled.forEach(function(id) { + menu[name(id)+((id in _WIDGETS) ? " *" : "")] = () => edit(id); + }); + const disabled = Object.keys(WIDGETS).filter(id => !WIDGETS[id].area); + if (disabled.length) { + menu[" == "+/*LANG*/"Disabled"+" == "] = undefined; + disabled.forEach(function(id) { + menu[name(id)+" *"] = () => edit(id); + }); + } + if (Object.keys(_WIDGETS).length) { // only show reset if there is anything to reset + menu[/*LANG*/"Reset All"] = () => { + E.showPrompt(/*LANG*/"Reset all widgets?").then(confirm => { + if (confirm) { + delete settings.custom; + require("Storage").writeJSON("wid_edit.json", settings); + for(let id in _WIDGETS) { + if (WIDGETS[id].area) Object.assign(WIDGETS[id], _WIDGETS[id]) // restore defaults + else WIDGETS[id] = _WIDGETS[id]; // restore widget + } + global._WIDGETS = {}; + for(let WIDGET of WIDGETS) { + // drawWidgets won't clear e.g. bottom bar if there are no bottom widgets because we just moved them away + // (area has been reset by now, but x/y not yet) + if (WIDGET.width) g.reset().clearRect(WIDGET.x, WIDGET.y, WIDGET.x+WIDGET.width-1, WIDGET.y+23); + } + redrawWidgets(); + } + mainMenu(); // reload with reset widgets + }) + } + } + + E.showMenu(menu); + } + mainMenu(); +}); From 66120e31e4555752f2f741ead54f055a9de78795 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 17 Jan 2022 19:36:41 +0100 Subject: [PATCH 2/8] wid_edit: only allow top row, don't "disable" widgets --- apps/wid_edit/README.md | 11 ++-- apps/wid_edit/boot.js | 34 +++---------- apps/wid_edit/icon.png | Bin 1897 -> 8260 bytes apps/wid_edit/settings.js | 104 +++++++++----------------------------- 4 files changed, 35 insertions(+), 114 deletions(-) diff --git a/apps/wid_edit/README.md b/apps/wid_edit/README.md index 123ac8946..e5003280c 100644 --- a/apps/wid_edit/README.md +++ b/apps/wid_edit/README.md @@ -1,18 +1,15 @@ # Widget Editor -This adds a setting menu which allows you to change the location of all widgets. +This adds a setting menu which allows you to change the location of widgets. ## Settings There is no app icon in the launcher; you can find the settings under -`Settings`->`App/Widget Settings`->`Widget Editor`. +`Apps`->`Widget Editor`. For every widget, you have these options: -* **Area**: In which corner to draw the widget. - * **Note**: Not all apps handle widgets in the bottom corners well. -* **Sort Order**: Changes the order if several widgets use the same corner. -* **Enabled**: We do our best to hide disabled widgets, but they are still - loaded and use RAM. +* **Side**: On which side to draw the widget. +* **Sort Order**: Changes the order if several widgets use the same side. ## Creator diff --git a/apps/wid_edit/boot.js b/apps/wid_edit/boot.js index e4f909eab..e615c2eb1 100644 --- a/apps/wid_edit/boot.js +++ b/apps/wid_edit/boot.js @@ -7,39 +7,19 @@ Bangle.loadWidgets = function() { }); const o = require("Storage").readJSON("wid_edit.json", 1) || {}, c = o.custom || {}; - let _W; for (const w in c){ if (!(w in WIDGETS)) continue; - _W= {}; - if (c[w].hide) { - // disabled: move widget to _WIDGETS, and place it way offscreen (in case it tries to draw itself anyway) - _W = WIDGETS[w]; _W.x = 1000; _W.y = 1000; - WIDGETS[w] = {draw:()=>{}}; // in case it tries to call itself - } - else { - // enabled: store default area/sortorder in _WIDGETS (only if changed) - if (c[w].area) _W.area = WIDGETS[w].area; - if ('sortorder' in c[w]) _W.sortorder = WIDGETS[w].sortorder; - Object.assign(WIDGETS[w], c[w]); - } - if (Object.keys(_W).length) _WIDGETS[w] = _W; + let _W = {}; + // store default area/sortorder in _WIDGETS + if (c[w].area) _W.area = WIDGETS[w].area; + if ('sortorder' in c[w]) _W.sortorder = WIDGETS[w].sortorder; + Object.assign(WIDGETS[w], c[w]); + _WIDGETS[w] = _W; } if (!Object.keys(_WIDGETS).length) delete _WIDGETS; // no need for this const W = WIDGETS; WIDGETS = {}; - let a, t=0, b=0; Object.keys(W) .sort((a, b) => (0|W[b].sortorder)-(0|W[a].sortorder)) - .forEach(k => { - WIDGETS[k] = W[k]; - if (a=W[k].area) { - t = t || (a[0]=="t"); - b = b || (a[0]=="b"); - } - }); - Bangle.appRect = { - x: 0, y: t*24, - w: g.getWidth(), h: g.getHeight()-1-(t+b)*24, - x2: g.getWidth()-1, y2: g.getHeight()-1-b*24 - } + .forEach(k => WIDGETS[k] = W[k]); } \ No newline at end of file diff --git a/apps/wid_edit/icon.png b/apps/wid_edit/icon.png index e02a636af52160ff0d16a0f3bf323b87f10ea9f4..1d072c381208ad2e028c41b9542df7ac736ba62a 100644 GIT binary patch literal 8260 zcmV-KAiLj*P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3*tk|Vher2q32a|G_oak!ez4d(ds0SqbEs_r&> zm0cyZG892#ad!YT`~UslWBw0+a*o-=RBCQHTmHlroA11-_Wo)5ud`AA)j#3+761R` zbMyX%=cDj(W$DlLozK(97hcbBnEbrInMe@17pS$?nsO|eE zw^}|gbv!qk&xN{_=Wl#3>%PeUn=c;}th5pj3f}MFg4KWPT|tn)K6G9k|GH4TyleVC z8eV)T&&@E%m+ie?t^I7EFGha7GvBTMe)rAzzCWLbZ??>jjd=6xFI@Wed3Zm@e+!3a zMgHqArginlcAmfIta{GAyX!Gy(fmY|8r!{Y!-WHr2mM;+r|>uNzUWWmr`m2OE?+!x z@H1U=9v|H(6rz+v5}O{q#XUtN5t-sUC~+~dV{9XE68{y+Sfzy0#R{Frm5n8?}swiWC064xw4JSYEh6%KLd@mo>@ zzkVL~+fVUBsN%t-X0B|o+i`C(lKY6QpneWqmw3G?mn_>qu=aIO4@7^5OcXo*pz{pt}H!j|_da(S;b>%TrE2r

csEXWX!R7|!jM@G1`DJ9xSs*48lLBt`lj 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 {}}; // in case it tries to call itself - } + // if we assigned custom values, store defaults in _WIDGETS + let _W = {}; + if (saved.area) _W.area = def.area; + if ('sortorder' in saved) _W.sortorder = def.sortorder; + if (Object.keys(_W).length) _WIDGETS[id] = _W; + else delete _WIDGETS[id]; + // drawWidgets won't clear e.g. bottom bar if we just disabled the last bottom widget - if (WIDGET.width) g.reset().clearRect(WIDGET.x, WIDGET.y, WIDGET.x+WIDGET.width-1, WIDGET.y+23); redrawWidgets(); highlight(); @@ -117,15 +100,11 @@ } mainMenu(); }, - /*LANG*/"Area": { - value: areas.indexOf(area), - min: 0, max: 3, wrap: true, - format: a => [ - /*LANG*/"Top Left", /*LANG*/"Top Right", - /*LANG*/"Bottom Left", /*LANG*/"Bottom Right" - ][a], - onchange: a => { - area = areas[a]; + /*LANG*/"Side": { + value: (area === 'tl'), + format: tl => tl ? /*LANG*/"Left" : /*LANG*/"Right", + onchange: tl => { + area = tl ? "tl" : "tr"; save(); } }, @@ -136,18 +115,9 @@ save(); } }, - /*LANG*/"Enabled": { - value: enabled, - format: e => e ?/*LANG*/"Yes" :/*LANG*/"No", - onchange: e => { - enabled = e; - save(); - } - }, /*LANG*/"Reset": () => { area = def.area; sortorder = def.sortorder; - enabled = true; save(); mainMenu(); // changing multiple values made the rest of the menu wrong, so take the easy out } @@ -162,33 +132,13 @@ "": {"title": /*LANG*/"Widgets"}, }; menu[/*LANG*/"< Back"] = ()=>{ - if (!Object.keys(_WIDGETS).length) delete _WIDGETS; - // adjust appRect for new widget locations - let a, t=0, b=0; - for (let WIDGET of WIDGETS) { - if (a=WIDGET.area) { - t = t || (a[0]=="t"); - b = b || (a[0]=="b"); - } - } - Object.assign(Bangle.appRect, { - y: t*24, - h: g.getHeight()-(t+b)*24, - y2: g.getHeight()-1-b*24 - }); + if (!Object.keys(_WIDGETS).length) delete _WIDGETS; // no defaults to remember back(); }; - const enabled = Object.keys(WIDGETS).filter(id => WIDGETS[id].area); - enabled.forEach(function(id) { + Object.keys(WIDGETS).forEach(id=>{ + // mark customized widgets with asterisk menu[name(id)+((id in _WIDGETS) ? " *" : "")] = () => edit(id); }); - const disabled = Object.keys(WIDGETS).filter(id => !WIDGETS[id].area); - if (disabled.length) { - menu[" == "+/*LANG*/"Disabled"+" == "] = undefined; - disabled.forEach(function(id) { - menu[name(id)+" *"] = () => edit(id); - }); - } if (Object.keys(_WIDGETS).length) { // only show reset if there is anything to reset menu[/*LANG*/"Reset All"] = () => { E.showPrompt(/*LANG*/"Reset all widgets?").then(confirm => { @@ -196,15 +146,9 @@ delete settings.custom; require("Storage").writeJSON("wid_edit.json", settings); for(let id in _WIDGETS) { - if (WIDGETS[id].area) Object.assign(WIDGETS[id], _WIDGETS[id]) // restore defaults - else WIDGETS[id] = _WIDGETS[id]; // restore widget + Object.assign(WIDGETS[id], _WIDGETS[id]) // restore defaults } global._WIDGETS = {}; - for(let WIDGET of WIDGETS) { - // drawWidgets won't clear e.g. bottom bar if there are no bottom widgets because we just moved them away - // (area has been reset by now, but x/y not yet) - if (WIDGET.width) g.reset().clearRect(WIDGET.x, WIDGET.y, WIDGET.x+WIDGET.width-1, WIDGET.y+23); - } redrawWidgets(); } mainMenu(); // reload with reset widgets From 80a1513030920d40ffb554e0575eb247b33ea0f0 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 17 Jan 2022 19:40:33 +0100 Subject: [PATCH 3/8] wid_edit: clean up removed widgets from settings file on boot --- apps/wid_edit/boot.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/wid_edit/boot.js b/apps/wid_edit/boot.js index e615c2eb1..5f522b440 100644 --- a/apps/wid_edit/boot.js +++ b/apps/wid_edit/boot.js @@ -5,10 +5,16 @@ Bangle.loadWidgets = function() { try { eval(require("Storage").read(widget)); } catch (e) {print(widget,e);} }); - const o = require("Storage").readJSON("wid_edit.json", 1) || {}, - c = o.custom || {}; + const s = require("Storage").readJSON("wid_edit.json", 1) || {}, + c = s.custom || {}; + let u = false; // do we need to write updated settings? for (const w in c){ - if (!(w in WIDGETS)) continue; + if (!(w in WIDGETS)) { + // widget no longer exists: remove it from settings file + delete c[w]; + u = true; + continue; + } let _W = {}; // store default area/sortorder in _WIDGETS if (c[w].area) _W.area = WIDGETS[w].area; @@ -16,7 +22,11 @@ Bangle.loadWidgets = function() { Object.assign(WIDGETS[w], c[w]); _WIDGETS[w] = _W; } - if (!Object.keys(_WIDGETS).length) delete _WIDGETS; // no need for this + if (!Object.keys(_WIDGETS).length) delete _WIDGETS; // no need for this after all + if (u) { + s.custom = c; + require("Storage").writeJSON("wid_edit.json", s); + } const W = WIDGETS; WIDGETS = {}; Object.keys(W) From 832f308fde5955fa8f5cdc2165c4054f93ac8d21 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 17 Jan 2022 19:49:37 +0100 Subject: [PATCH 4/8] readme: add widget `sortorder`, no more bottom bar --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8e186cf79..6d0a981ac 100644 --- a/README.md +++ b/README.md @@ -172,12 +172,13 @@ The widget example is available in [`apps/_example_widget`](apps/_example_widget Widgets are just small bits of code that run whenever an app that supports them calls `Bangle.loadWidgets()`. If they want to display something in the 24px high -widget bars at the top and bottom of the screen they can add themselves to -the global `WIDGETS` array with: +widget bar at the top of the screen they can add themselves to the global +`WIDGETS` array with: ``` WIDGETS["mywidget"]={ - area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right) + area:"tl", // tl (top left), tr (top right) + sortorder:0, // (Optional) determines order of widgets in the same corner width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout draw:draw // called to draw the widget }; @@ -461,16 +462,13 @@ The screen is parted in a widget and app area for lcd mode `direct`(default). | areas | as rectangle or point | | :-:| :-: | | Widget | (0,0,239,23) | -| Widget bottom bar (optional) | (0,216,239,239) | -| Apps | (0,24,239,239) (see below) | +| Apps | (0,24,239,239) | | BTN1 | (230, 55) | | BTN2 | (230, 140) | | BTN3 | (230, 210) | | BTN4 | (0,0,119, 239)| | BTN5 | (120,0,239,239) | -- If there are widgets at the bottom of the screen, apps should actually keep the bottom 24px free, so should keep to the area (0,24,239,215) - - Use `g.setFontAlign(0, 0, 3)` to draw rotated string to BTN1-BTN3 with `g.drawString()`. - For BTN4-5 the touch area is named From 9a8ccf0f4f5a206c936ab0bcfa93364ed5c7b363 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 17 Jan 2022 20:35:18 +0100 Subject: [PATCH 5/8] wid_edit: sort widgets with same sortorder by name (assuming Array.sort() is stable) --- apps/wid_edit/boot.js | 1 + apps/wid_edit/settings.js | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/wid_edit/boot.js b/apps/wid_edit/boot.js index 5f522b440..86a222af0 100644 --- a/apps/wid_edit/boot.js +++ b/apps/wid_edit/boot.js @@ -30,6 +30,7 @@ Bangle.loadWidgets = function() { const W = WIDGETS; WIDGETS = {}; Object.keys(W) + .sort() .sort((a, b) => (0|W[b].sortorder)-(0|W[a].sortorder)) .forEach(k => WIDGETS[k] = W[k]); } \ No newline at end of file diff --git a/apps/wid_edit/settings.js b/apps/wid_edit/settings.js index 9863bd9d2..d3bb67929 100644 --- a/apps/wid_edit/settings.js +++ b/apps/wid_edit/settings.js @@ -14,6 +14,7 @@ let W = WIDGETS; global.WIDGETS = {}; Object.keys(W) + .sort() .sort((a, b) => (0|W[b].sortorder)-(0|W[a].sortorder)) .forEach(k => {WIDGETS[k] = W[k]}); Bangle.drawWidgets(); From d5a14cacbdb64aa069a7b722d73cc87ae9975699 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 17 Jan 2022 21:02:44 +0100 Subject: [PATCH 6/8] wid_edit: clean up removed widgets in settings app instead to keep boot code as small as possible --- apps/wid_edit/boot.js | 12 +----------- apps/wid_edit/settings.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/wid_edit/boot.js b/apps/wid_edit/boot.js index 86a222af0..4fbafb0e6 100644 --- a/apps/wid_edit/boot.js +++ b/apps/wid_edit/boot.js @@ -7,14 +7,8 @@ Bangle.loadWidgets = function() { }); const s = require("Storage").readJSON("wid_edit.json", 1) || {}, c = s.custom || {}; - let u = false; // do we need to write updated settings? for (const w in c){ - if (!(w in WIDGETS)) { - // widget no longer exists: remove it from settings file - delete c[w]; - u = true; - continue; - } + if (!(w in WIDGETS)) continue; let _W = {}; // store default area/sortorder in _WIDGETS if (c[w].area) _W.area = WIDGETS[w].area; @@ -23,10 +17,6 @@ Bangle.loadWidgets = function() { _WIDGETS[w] = _W; } if (!Object.keys(_WIDGETS).length) delete _WIDGETS; // no need for this after all - if (u) { - s.custom = c; - require("Storage").writeJSON("wid_edit.json", s); - } const W = WIDGETS; WIDGETS = {}; Object.keys(W) diff --git a/apps/wid_edit/settings.js b/apps/wid_edit/settings.js index d3bb67929..395eea100 100644 --- a/apps/wid_edit/settings.js +++ b/apps/wid_edit/settings.js @@ -7,6 +7,19 @@ if (!('custom' in settings)) settings.custom = {}; global._WIDGETS = global._WIDGETS || {}; + let cleanup = false; + for (const id in settings.custom) { + if (!(id in WIDGETS)) { + // widget which no longer exists + cleanup = true; + delete settings.custom[id]; + } + } + if (cleanup) { + if (!Object.keys(settings.custom).length) delete settings.custom; + require("Storage").writeJSON("wid_edit.json", settings); + } + /** * Sort & redraw all widgets */ From d28f2c62364171d096526707f9e36c26aabf1ed8 Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 17 Jan 2022 21:18:02 +0100 Subject: [PATCH 7/8] wid_edit: shrink boot code a bit --- apps/wid_edit/boot.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/wid_edit/boot.js b/apps/wid_edit/boot.js index 4fbafb0e6..872965c97 100644 --- a/apps/wid_edit/boot.js +++ b/apps/wid_edit/boot.js @@ -1,22 +1,20 @@ Bangle.loadWidgets = function() { - global.WIDGETS={}; global._WIDGETS={}; + global.WIDGETS={}; require("Storage").list(/\.wid\.js$/) - .forEach(widget=>{ - try { eval(require("Storage").read(widget)); } - catch (e) {print(widget,e);} + .forEach(w=>{ + try { eval(require("Storage").read(w)); } + catch (e) { print(w, e); } }); const s = require("Storage").readJSON("wid_edit.json", 1) || {}, c = s.custom || {}; for (const w in c){ - if (!(w in WIDGETS)) continue; - let _W = {}; - // store default area/sortorder in _WIDGETS - if (c[w].area) _W.area = WIDGETS[w].area; - if ('sortorder' in c[w]) _W.sortorder = WIDGETS[w].sortorder; + if (!(w in WIDGETS)) continue; // widget no longer exists + // store defaults of customized values in _WIDGETS + global._WIDGETS=global._WIDGETS||{}; + _WIDGETS[w] = {}; + Object.keys(c[w]).forEach(k => _WIDGETS[w][k] = WIDGETS[w][k]); Object.assign(WIDGETS[w], c[w]); - _WIDGETS[w] = _W; } - if (!Object.keys(_WIDGETS).length) delete _WIDGETS; // no need for this after all const W = WIDGETS; WIDGETS = {}; Object.keys(W) From e8de7aa662fe94b5f94ffca61d81c4e784adf30c Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Mon, 17 Jan 2022 21:39:04 +0100 Subject: [PATCH 8/8] wid_edit: "highlight" hidden widgets when editing --- apps/wid_edit/settings.js | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/wid_edit/settings.js b/apps/wid_edit/settings.js index 395eea100..0969ed533 100644 --- a/apps/wid_edit/settings.js +++ b/apps/wid_edit/settings.js @@ -65,10 +65,27 @@ * Draw highlighted widget */ function highlight() { - if (WIDGET.width) { + if (WIDGET.width > 0) { + // draw widget, then draw a highlighted border on top WIDGET.draw(); g.setColor(g.theme.fgH) .drawRect(WIDGET.x, WIDGET.y, WIDGET.x+WIDGET.width-1, WIDGET.y+23); + } else { + // hidden widget: fake a width and provide our own draw() + const draw = WIDGET.draw, width = WIDGET.width; + WIDGET.width = 24; + WIDGET.draw = function() { + g.setColor(g.theme.bgH).setColor(g.theme.fgH) + .clearRect(this.x, this.y, this.x+23, this.y+23) + .drawRect(this.x, this.y, this.x+23, this.y+23) + .drawLine(this.x, this.y, this.x+23, this.y+23) + .drawLine(this.x, this.y+23, this.x+23, this.y); + }; + // re-layout+draw all widgets with our placeholder in between + redrawWidgets(); + // and restore original values + WIDGET.draw = draw; + WIDGET.width = width; } } highlight(); @@ -108,12 +125,9 @@ const menu = { "": {"title": name(id)}, /*LANG*/"< Back": () => { - if (WIDGET.width) { - g.reset().clearRect(WIDGET.x, WIDGET.y, WIDGET.x+WIDGET.width-1, WIDGET.y+23); - WIDGET.draw(); - } + redrawWidgets(); mainMenu(); - }, + }, /*LANG*/"Side": { value: (area === 'tl'), format: tl => tl ? /*LANG*/"Left" : /*LANG*/"Right",