forked from FOSS/BangleApps
commit
115230ea8f
12
README.md
12
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
|
||||
|
|
18
apps.json
18
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"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: new Widget Editor!
|
|
@ -0,0 +1,16 @@
|
|||
# Widget Editor
|
||||
|
||||
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
|
||||
`Apps`->`Widget Editor`.
|
||||
|
||||
For every widget, you have these options:
|
||||
* **Side**: On which side to draw the widget.
|
||||
* **Sort Order**: Changes the order if several widgets use the same side.
|
||||
|
||||
## Creator
|
||||
|
||||
Richard de Boer <rigrig+banglejs@tubul.net>
|
|
@ -0,0 +1,24 @@
|
|||
Bangle.loadWidgets = function() {
|
||||
global.WIDGETS={};
|
||||
require("Storage").list(/\.wid\.js$/)
|
||||
.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; // 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]);
|
||||
}
|
||||
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]);
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
|
@ -0,0 +1,190 @@
|
|||
/**
|
||||
* @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 || {};
|
||||
|
||||
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
|
||||
*/
|
||||
function redrawWidgets() {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
let WIDGET = WIDGETS[id],
|
||||
def = {area: WIDGET.area, sortorder: WIDGET.sortorder|0}; // default values
|
||||
Object.assign(def, _WIDGETS[id]||{}); // defaults were saved in _WIDGETS
|
||||
|
||||
settings.custom = settings.custom||{};
|
||||
let saved = settings.custom[id] || {},
|
||||
area = saved.area || def.area,
|
||||
sortorder = ("sortorder" in saved) ? saved.sortorder : def.sortorder;
|
||||
|
||||
/**
|
||||
* Draw highlighted widget
|
||||
*/
|
||||
function highlight() {
|
||||
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();
|
||||
|
||||
/**
|
||||
* Save widget and redraw with new settings
|
||||
*/
|
||||
function save() {
|
||||
// we only save non-default values
|
||||
saved = {};
|
||||
if ((area!==def.area) || (sortorder!==def.sortorder)) {
|
||||
if (area!==def.area) saved.area = area;
|
||||
if (sortorder!==def.sortorder) saved.sortorder = sortorder;
|
||||
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);
|
||||
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 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
|
||||
redrawWidgets();
|
||||
|
||||
highlight();
|
||||
m.draw();
|
||||
}
|
||||
|
||||
const menu = {
|
||||
"": {"title": name(id)},
|
||||
/*LANG*/"< Back": () => {
|
||||
redrawWidgets();
|
||||
mainMenu();
|
||||
},
|
||||
/*LANG*/"Side": {
|
||||
value: (area === 'tl'),
|
||||
format: tl => tl ? /*LANG*/"Left" : /*LANG*/"Right",
|
||||
onchange: tl => {
|
||||
area = tl ? "tl" : "tr";
|
||||
save();
|
||||
}
|
||||
},
|
||||
/*LANG*/"Sort Order": {
|
||||
value: sortorder,
|
||||
onchange: o => {
|
||||
sortorder = o;
|
||||
save();
|
||||
}
|
||||
},
|
||||
/*LANG*/"Reset": () => {
|
||||
area = def.area;
|
||||
sortorder = def.sortorder;
|
||||
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; // no defaults to remember
|
||||
back();
|
||||
};
|
||||
Object.keys(WIDGETS).forEach(id=>{
|
||||
// mark customized widgets with asterisk
|
||||
menu[name(id)+((id in _WIDGETS) ? " *" : "")] = () => 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) {
|
||||
Object.assign(WIDGETS[id], _WIDGETS[id]) // restore defaults
|
||||
}
|
||||
global._WIDGETS = {};
|
||||
redrawWidgets();
|
||||
}
|
||||
mainMenu(); // reload with reset widgets
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
mainMenu();
|
||||
});
|
Loading…
Reference in New Issue