promenu: typescript

master
Rob Pilling 2023-01-21 00:13:14 +00:00
parent 6c4bd363da
commit c22f16eb74
2 changed files with 352 additions and 159 deletions

View File

@ -1,162 +1,168 @@
E.showMenu = function(items) { "use strict";
function RectRnd(x1,y1,x2,y2,r) { E.showMenu = function (items) {
let pp = []; var RectRnd = function (x1, y1, x2, y2, r) {
pp.push.apply(pp,g.quadraticBezier([x2-r,y1, x2,y1,x2,y1+r])); var pp = [];
pp.push.apply(pp,g.quadraticBezier([x2,y2-r,x2,y2,x2-r,y2])); pp.push.apply(pp, g.quadraticBezier([x2 - r, y1, x2, y1, x2, y1 + r]));
pp.push.apply(pp,g.quadraticBezier([x1+r,y2,x1,y2,x1,y2-r])); pp.push.apply(pp, g.quadraticBezier([x2, y2 - r, x2, y2, x2 - r, y2]));
pp.push.apply(pp,g.quadraticBezier([x1,y1+r,x1,y1,x1+r,y1])); pp.push.apply(pp, g.quadraticBezier([x1 + r, y2, x1, y2, x1, y2 - r]));
return pp; pp.push.apply(pp, g.quadraticBezier([x1, y1 + r, x1, y1, x1 + r, y1]));
} return pp;
function fillRectRnd(x1,y1,x2,y2,r,c) {
g.setColor(c);
g.fillPoly(RectRnd(x1,y1,x2,y2,r),1);
g.setColor(255,255,255);
}
function drawRectRnd(x1,y1,x2,y2,r,c) {
g.setColor(c);
g.drawPoly(RectRnd(x1,y1,x2,y2,r),1);
g.setColor(255,255,255);
}
var menuItems = Object.keys(items);
var options = items[""];
if (!(options instanceof Object)) options = {};
Bangle.setLCDPower(1); // ensure screen is on
if (!items) {
Bangle.setUI();
return;
}
if (options) menuItems.splice(menuItems.indexOf(""),1);
options.fontHeight = options.fontHeight||25;
if (options.scroll)
options.selected = options.scroll;
else if (options.selected === undefined)
options.selected = 0;
var ar = Bangle.appRect;
if (options.topWidgets)
ar = {
x: ar.x,
y: ar.y + 24,
w: ar.w,
h: ar.h - 24,
x2: ar.x2,
y2: ar.y2 - 24,
}; };
var fillRectRnd = function (x1, y1, x2, y2, r, c) {
g.reset().clearRect(ar); g.setColor(c);
g.fillPoly(RectRnd(x1, y1, x2, y2, r));
var x = ar.x; g.setColor(255, 255, 255);
var x2 = ar.x2; };
var y = ar.y; var menuItems = Object.keys(items);
var y2 = ar.y2 - 12; // padding at end for arrow var options = items[""] || {};
if (options.title) if (!(options instanceof Object))
y += 22; options = {};
var loc = require("locale"); if (options)
var l = { menuItems.splice(menuItems.indexOf(""), 1);
lastIdx : 0, var fontHeight = options.fontHeight || 25;
draw : function(rowmin,rowmax) { var selected = options.scroll || options.selected || 0;
var rows = 0|Math.min((y2-y) / options.fontHeight,menuItems.length); var ar = Bangle.appRect;
var idx = E.clip(options.selected-( rows>>1),0,menuItems.length-rows); g.reset().clearRect(ar);
if (idx!=l.lastIdx) rowmin=undefined; // redraw all if we scrolled var x = ar.x;
l.lastIdx = idx; var x2 = ar.x2;
var iy = y; var y = ar.y;
g.reset().setFontAlign(0,-1,0).setFont('12x20'); var y2 = ar.y2 - 12;
if (options.predraw) options.predraw(g); if (options.title)
if (rowmin===undefined && options.title) y += 22;
g.drawString(options.title,(x+x2)/2,y-21).drawLine(x,y-2,x2,y-2). var lastIdx = 0;
setColor(g.theme.fg).setBgColor(g.theme.bg); var selectEdit = undefined;
iy += 4; var l = {
if (rowmin!==undefined) { draw: function (rowmin, rowmax) {
if (idx<rowmin) { var rows = 0 | Math.min((y2 - y) / fontHeight, menuItems.length);
iy += options.fontHeight*(rowmin-idx); var idx = E.clip(selected - (rows >> 1), 0, menuItems.length - rows);
idx=rowmin; if (idx != lastIdx)
} rowmin = undefined;
if (idx+rows>rowmax) { lastIdx = idx;
rows = 1+rowmax-rowmin; var iy = y;
} g.reset().setFontAlign(0, -1, 0).setFont12x20();
} if (options.predraw)
while (rows--) { options.predraw(g);
var name = menuItems[idx]; if (rowmin === undefined && options.title)
var item = items[name]; g.drawString(options.title, (x + x2) / 2, y - 21).drawLine(x, y - 2, x2, y - 2).
var hl = (idx==options.selected && !l.selectEdit); setColor(g.theme.fg).setBgColor(g.theme.bg);
if(g.theme.dark){ iy += 4;
fillRectRnd(x,iy,x2,iy+options.fontHeight-3,7,hl ? g.theme.bgH : g.theme.bg+40); if (rowmin !== undefined) {
}else{ if (idx < rowmin) {
fillRectRnd(x,iy,x2,iy+options.fontHeight-3,7,hl ? g.theme.bgH : g.theme.bg-20); iy += fontHeight * (rowmin - idx);
} idx = rowmin;
g.setColor(hl ? g.theme.fgH : g.theme.fg); }
g.setFontAlign(-1,-1); if (rowmax && idx + rows > rowmax) {
var v = item.value; rows = 1 + rowmax - rowmin;
v = loc.translate(""+v); }
if(loc.translate(name).length >= 17-v.length && "object" == typeof item){ }
if (item.format) v=item.format(v); while (rows--) {
g.drawString(loc.translate(name).substring(0, 12-v.length)+"...",x+3.7,iy+2.7); var name = menuItems[idx];
}else{ var item = items[name];
if(loc.translate(name).length >= 15){ var hl = (idx === selected && !selectEdit);
g.drawString(loc.translate(name).substring(0, 15)+"...",x+3.7,iy+2.7); if (g.theme.dark) {
}else{ fillRectRnd(x, iy, x2, iy + fontHeight - 3, 7, hl ? g.theme.bgH : g.theme.bg + 40);
g.drawString(loc.translate(name),x+3.7,iy+2.7); }
} else {
} fillRectRnd(x, iy, x2, iy + fontHeight - 3, 7, hl ? g.theme.bgH : g.theme.bg - 20);
if ("object" == typeof item) { }
var xo = x2; g.setColor(hl ? g.theme.fgH : g.theme.fg);
var v = item.value; g.setFontAlign(-1, -1);
if (item.format) v=item.format(v); var v = void 0;
v = loc.translate(""+v); if (typeof item === "object") {
if (l.selectEdit && idx==options.selected) { v = "format" in item
xo -= 24 + 1; ? item.format(item.value)
g.setColor(g.theme.fgH).drawImage("\x0c\x05\x81\x00 \x07\x00\xF9\xF0\x0E\x00@",xo,iy+(options.fontHeight-10)/2,{scale:2}); : item.value;
} }
g.setFontAlign(1,-1); else {
g.drawString(v,xo-2,iy+1); v = "";
} }
g.setColor(g.theme.fg); {
iy += options.fontHeight; if (name.length >= 17 - v.length && typeof item === "object") {
idx++; g.drawString(name.substring(0, 12 - v.length) + "...", x + 3.7, iy + 2.7);
} }
g.setFontAlign(-1,-1); else {
g.setColor((idx<menuItems.length)?g.theme.fg:g.theme.bg).fillPoly([72,166,104,166,88,174]); if (name.length >= 15) {
g.flip(); g.drawString(name.substring(0, 15) + "...", x + 3.7, iy + 2.7);
}, }
select : function() { else {
var item = items[menuItems[options.selected]]; g.drawString(name, x + 3.7, iy + 2.7);
if ("function" == typeof item) item(l); }
else if ("object" == typeof item) { }
// if a number, go into 'edit mode' var xo = x2;
if ("number" == typeof item.value) if (selectEdit && idx === selected) {
l.selectEdit = l.selectEdit?undefined:item; xo -= 24 + 1;
else { // else just toggle bools g.setColor(g.theme.fgH)
if ("boolean" == typeof item.value) item.value=!item.value; .drawImage("\x0c\x05\x81\x00 \x07\x00\xF9\xF0\x0E\x00@", xo, iy + (fontHeight - 10) / 2, { scale: 2 });
if (item.onchange) item.onchange(item.value); }
} g.setFontAlign(1, -1);
l.draw(); g.drawString(v, xo - 2, iy + 1);
} }
}, g.setColor(g.theme.fg);
move : function(dir) { iy += fontHeight;
var item = l.selectEdit; idx++;
if (item) { }
item.value -= (dir||1)*(item.step||1); g.setFontAlign(-1, -1);
if (item.min!==undefined && item.value<item.min) item.value = item.wrap ? item.max : item.min; g.setColor((idx < menuItems.length) ? g.theme.fg : g.theme.bg).fillPoly([72, 166, 104, 166, 88, 174]);
if (item.max!==undefined && item.value>item.max) item.value = item.wrap ? item.min : item.max; g.flip();
if (item.onchange) item.onchange(item.value); },
l.draw(options.selected,options.selected); select: function () {
} else { var item = items[menuItems[selected]];
var lastSelected=options.selected; if (typeof item === "function") {
options.selected = (dir+options.selected+menuItems.length)%menuItems.length; item();
l.draw(Math.min(lastSelected,options.selected), Math.max(lastSelected,options.selected)); }
} else if (typeof item === "object") {
if (typeof item.value === "number") {
selectEdit = selectEdit ? undefined : item;
}
else {
if (typeof item.value === "boolean")
item.value = !item.value;
if (item.onchange)
item.onchange(item.value);
}
l.draw();
}
},
move: function (dir) {
var item = selectEdit;
if (typeof item === "object" && typeof item.value === "number") {
item.value += (-dir || 1) * (item.step || 1);
if (item.min && item.value < item.min)
item.value = item.wrap ? item.max : item.min;
if ("max" in item && item.value > item.max)
item.value = item.wrap ? item.min : item.max;
if (item.onchange)
item.onchange(item.value);
l.draw(selected, selected);
}
else {
var lastSelected = selected;
selected = (selected + dir + menuItems.length) % menuItems.length;
l.draw(Math.min(lastSelected, selected), Math.max(lastSelected, selected));
}
},
};
l.draw();
Bangle.setUI("updown", function (dir) {
if (dir)
l.move(dir);
else
l.select();
});
var back = options.back;
if (!back) {
var backItem = items["< Back"];
if (typeof backItem === "function")
back = backItem;
else if (backItem && "back" in backItem)
back = backItem.back;
} }
}; if (typeof back === "function") {
l.draw(); var back_1 = back;
Bangle.setUI("updown",dir => { Bangle.on('swipe', function (lr, _ud) {
if (dir) l.move(dir); if (lr < 0)
else l.select(); back_1();
}); });
if (options.back) { }
Bangle.on('swipe', (lr, _ud) => { return l;
if (lr < 0) back();
})
}
return l;
}; };

187
apps/promenu/bootb2.ts Normal file
View File

@ -0,0 +1,187 @@
type ActualMenuItem = Exclude<Menu["..."], MenuOptions | undefined>;
(E.showMenu as any) = (items: Menu): MenuInstance => {
const RectRnd = (x1: number, y1: number, x2: number, y2: number, r: number) => {
const pp = [];
pp.push(...g.quadraticBezier([x2 - r, y1, x2, y1, x2, y1 + r]));
pp.push(...g.quadraticBezier([x2, y2 - r, x2, y2, x2 - r, y2]));
pp.push(...g.quadraticBezier([x1 + r, y2, x1, y2, x1, y2 - r]));
pp.push(...g.quadraticBezier([x1, y1 + r, x1, y1, x1 + r, y1]));
return pp;
};
const fillRectRnd = (x1: number, y1: number, x2: number, y2: number, r: number, c: ColorResolvable) => {
g.setColor(c);
g.fillPoly(RectRnd(x1, y1, x2, y2, r));
g.setColor(255, 255, 255);
};
const menuItems = Object.keys(items);
let options = items[""] || {};
if (!(options instanceof Object)) options = {};
if (options)
menuItems.splice(menuItems.indexOf(""), 1);
const fontHeight = options.fontHeight||25;
let selected = options.scroll || options.selected || 0;
const ar = Bangle.appRect;
g.reset().clearRect(ar);
const x = ar.x;
const x2 = ar.x2;
let y = ar.y;
const y2 = ar.y2 - 12; // padding at end for arrow
if (options.title)
y += 22;
let lastIdx = 0;
let selectEdit: undefined | ActualMenuItem = undefined;
const l = {
draw: (rowmin?: number, rowmax?: number) => {
let rows = 0|Math.min((y2 - y) / fontHeight, menuItems.length);
let idx = E.clip(selected - (rows>>1), 0, menuItems.length - rows);
if (idx != lastIdx) rowmin=undefined; // redraw all if we scrolled
lastIdx = idx;
let iy = y;
g.reset().setFontAlign(0, -1, 0).setFont12x20();
if (options.predraw) options.predraw(g);
if (rowmin === undefined && options.title)
g.drawString(options.title, (x + x2) / 2, y - 21).drawLine(x, y - 2, x2, y - 2).
setColor(g.theme.fg).setBgColor(g.theme.bg);
iy += 4;
if (rowmin !== undefined) {
if (idx < rowmin) {
iy += fontHeight * (rowmin - idx);
idx = rowmin;
}
if (rowmax && idx + rows > rowmax) {
rows = 1 + rowmax - rowmin;
}
}
while (rows--) {
const name = menuItems[idx];
const item = items[name]! as ActualMenuItem;
const hl = (idx === selected && !selectEdit);
if(g.theme.dark){
fillRectRnd(x, iy, x2, iy + fontHeight - 3, 7, hl ? g.theme.bgH : g.theme.bg + 40);
}else{
fillRectRnd(x, iy, x2, iy + fontHeight - 3, 7, hl ? g.theme.bgH : g.theme.bg - 20);
}
g.setColor(hl ? g.theme.fgH : g.theme.fg);
g.setFontAlign( - 1, -1);
let v;
if (typeof item === "object") {
v = "format" in item
? (item.format as any)(item.value) // <T>format(), value: T
: item.value;
} else {
v = "";
}
/*???*/{
if(name.length >= 17 - v.length && typeof item === "object"){
g.drawString(name.substring(0, 12 - v.length) + "...", x + 3.7, iy + 2.7);
}else{
if(name.length >= 15){
g.drawString(name.substring(0, 15) + "...", x + 3.7, iy + 2.7);
}else{
g.drawString(name, x + 3.7, iy + 2.7);
}
}
let xo = x2;
if (selectEdit && idx === selected) {
xo -= 24 + 1;
g.setColor(g.theme.fgH)
.drawImage(
"\x0c\x05\x81\x00 \x07\x00\xF9\xF0\x0E\x00@",
xo,
iy + (fontHeight - 10) / 2,
{scale:2},
);
}
g.setFontAlign(1, -1);
g.drawString(v, xo - 2, iy + 1);
}
g.setColor(g.theme.fg);
iy += fontHeight;
idx++;
}
g.setFontAlign( - 1, -1);
g.setColor((idx < menuItems.length)?g.theme.fg:g.theme.bg).fillPoly([72, 166, 104, 166, 88, 174]);
g.flip();
},
select: () => {
const item = items[menuItems[selected]] as ActualMenuItem;
if (typeof item === "function") {
item();
} else if (typeof item === "object") {
if (typeof item.value === "number") {
selectEdit = selectEdit ? undefined : item;
} else {
if (typeof item.value === "boolean")
item.value = !item.value;
if (item.onchange)
(item.onchange as any)(item.value);
}
l.draw();
}
},
move: (dir: number) => {
const item = selectEdit;
if (typeof item === "object" && typeof item.value === "number") {
item.value += (-dir||1) * (item.step||1);
if (item.min && item.value < item.min)
item.value = item.wrap ? item.max as number : item.min;
if ("max" in item && item.value > item.max)
item.value = item.wrap ? item.min as number : item.max;
if (item.onchange)
item.onchange(item.value);
l.draw(selected, selected);
} else {
const lastSelected = selected;
selected = (selected + dir + /*keep +ve*/menuItems.length) % menuItems.length;
l.draw(Math.min(lastSelected, selected), Math.max(lastSelected, selected));
}
},
};
l.draw();
Bangle.setUI("updown", dir => {
if (dir) l.move(dir);
else l.select();
});
let back = options.back;
if (!back) {
const backItem = items["< Back"];
if (typeof backItem === "function")
back = backItem;
else if (backItem && "back" in backItem)
back = backItem.back;
}
if (typeof back === "function") {
const back_ = back;
Bangle.on('swipe', (lr, _ud) => {
if (lr < 0) back_();
})
}
return l;
};