mirror of https://github.com/espruino/BangleApps
Merge pull request #2156 from halemmerich/unload
Fast switch for imageclock and iconlaunchpull/2158/head
commit
a456f4ff62
|
@ -1,3 +1,4 @@
|
||||||
0.01: Initial release
|
0.01: Initial release
|
||||||
0.02: implemented "direct launch" and "one click exit" settings
|
0.02: implemented "direct launch" and "one click exit" settings
|
||||||
0.03: Use default Bangle formatter for booleans
|
0.03: Use default Bangle formatter for booleans
|
||||||
|
0.04: Support new fast app switching
|
||||||
|
|
|
@ -1,163 +1,167 @@
|
||||||
const s = require("Storage");
|
{
|
||||||
const settings = s.readJSON("launch.json", true) || { showClocks: true, fullscreen: false,direct:false,oneClickExit:false };
|
const s = require("Storage");
|
||||||
|
const settings = s.readJSON("launch.json", true) || { showClocks: true, fullscreen: false,direct:false,oneClickExit:false };
|
||||||
|
|
||||||
if( settings.oneClickExit)
|
function returnToClock() {
|
||||||
setWatch(_=> load(), BTN1);
|
Bangle.setUI();
|
||||||
|
setTimeout(eval,0,s.read(".bootcde"));
|
||||||
|
}
|
||||||
|
|
||||||
if (!settings.fullscreen) {
|
if( settings.oneClickExit)
|
||||||
Bangle.loadWidgets();
|
setWatch(returnToClock, BTN1);
|
||||||
Bangle.drawWidgets();
|
|
||||||
}
|
|
||||||
|
|
||||||
var apps = s
|
if (!settings.fullscreen) {
|
||||||
.list(/\.info$/)
|
if (!global.WIDGETS) Bangle.loadWidgets();
|
||||||
.map((app) => {
|
Bangle.drawWidgets();
|
||||||
var a = s.readJSON(app, 1);
|
}
|
||||||
return (
|
|
||||||
a && {
|
|
||||||
name: a.name,
|
|
||||||
type: a.type,
|
|
||||||
icon: a.icon,
|
|
||||||
sortorder: a.sortorder,
|
|
||||||
src: a.src,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.filter(
|
|
||||||
(app) =>
|
|
||||||
app &&
|
|
||||||
(app.type == "app" ||
|
|
||||||
(app.type == "clock" && settings.showClocks) ||
|
|
||||||
!app.type)
|
|
||||||
);
|
|
||||||
apps.sort((a, b) => {
|
|
||||||
var n = (0 | a.sortorder) - (0 | b.sortorder);
|
|
||||||
if (n) return n; // do sortorder first
|
|
||||||
if (a.name < b.name) return -1;
|
|
||||||
if (a.name > b.name) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
apps.forEach((app) => {
|
|
||||||
if (app.icon) app.icon = s.read(app.icon); // should just be a link to a memory area
|
|
||||||
});
|
|
||||||
|
|
||||||
let scroll = 0;
|
var apps = s
|
||||||
let selectedItem = -1;
|
.list(/\.info$/)
|
||||||
const R = Bangle.appRect;
|
.map((app) => {
|
||||||
|
var a = s.readJSON(app, 1);
|
||||||
const iconSize = 48;
|
return (
|
||||||
|
a && {
|
||||||
const appsN = Math.floor(R.w / iconSize);
|
name: a.name,
|
||||||
const whitespace = (R.w - appsN * iconSize) / (appsN + 1);
|
type: a.type,
|
||||||
|
icon: a.icon,
|
||||||
const itemSize = iconSize + whitespace;
|
sortorder: a.sortorder,
|
||||||
|
src: a.src,
|
||||||
function drawItem(itemI, r) {
|
}
|
||||||
g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1);
|
|
||||||
let x = 0;
|
|
||||||
for (let i = itemI * appsN; i < appsN * (itemI + 1); i++) {
|
|
||||||
if (!apps[i]) break;
|
|
||||||
x += whitespace;
|
|
||||||
if (!apps[i].icon) {
|
|
||||||
g.setFontAlign(0,0,0).setFont("12x20:2").drawString("?", x + r.x+iconSize/2, r.y + iconSize/2);
|
|
||||||
} else {
|
|
||||||
g.drawImage(apps[i].icon, x + r.x, r.y);
|
|
||||||
}
|
|
||||||
if (selectedItem == i) {
|
|
||||||
g.drawRect(
|
|
||||||
x + r.x - 1,
|
|
||||||
r.y - 1,
|
|
||||||
x + r.x + iconSize + 1,
|
|
||||||
r.y + iconSize + 1
|
|
||||||
);
|
);
|
||||||
}
|
})
|
||||||
x += iconSize;
|
.filter(
|
||||||
}
|
(app) =>
|
||||||
drawText(itemI);
|
app &&
|
||||||
}
|
(app.type == "app" ||
|
||||||
|
(app.type == "clock" && settings.showClocks) ||
|
||||||
function drawItemAuto(i) {
|
!app.type)
|
||||||
var y = idxToY(i);
|
);
|
||||||
g.reset().setClipRect(R.x, y, R.x2, y + itemSize);
|
apps.sort((a, b) => {
|
||||||
drawItem(i, {
|
var n = (0 | a.sortorder) - (0 | b.sortorder);
|
||||||
x: R.x,
|
if (n) return n; // do sortorder first
|
||||||
y: y,
|
if (a.name < b.name) return -1;
|
||||||
w: R.w,
|
if (a.name > b.name) return 1;
|
||||||
h: itemSize
|
return 0;
|
||||||
|
});
|
||||||
|
apps.forEach((app) => {
|
||||||
|
if (app.icon) app.icon = s.read(app.icon); // should just be a link to a memory area
|
||||||
});
|
});
|
||||||
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastIsDown = false;
|
let scroll = 0;
|
||||||
|
let selectedItem = -1;
|
||||||
|
const R = Bangle.appRect;
|
||||||
|
|
||||||
function drawText(i) {
|
const iconSize = 48;
|
||||||
const selectedApp = apps[selectedItem];
|
|
||||||
const idy = (selectedItem - (selectedItem % 3)) / 3;
|
|
||||||
if (!selectedApp || i != idy) return;
|
|
||||||
const appY = idxToY(idy) + iconSize / 2;
|
|
||||||
g.setFontAlign(0, 0, 0);
|
|
||||||
g.setFont("12x20");
|
|
||||||
const rect = g.stringMetrics(selectedApp.name);
|
|
||||||
g.clearRect(
|
|
||||||
R.w / 2 - rect.width / 2,
|
|
||||||
appY - rect.height / 2,
|
|
||||||
R.w / 2 + rect.width / 2,
|
|
||||||
appY + rect.height / 2
|
|
||||||
);
|
|
||||||
g.drawString(selectedApp.name, R.w / 2, appY);
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectItem(id, e) {
|
const appsN = Math.floor(R.w / iconSize);
|
||||||
const iconN = E.clip(Math.floor((e.x - R.x) / itemSize), 0, appsN - 1);
|
const whitespace = (R.w - appsN * iconSize) / (appsN + 1);
|
||||||
const appId = id * appsN + iconN;
|
|
||||||
if( settings.direct && apps[appId])
|
const itemSize = iconSize + whitespace;
|
||||||
{
|
|
||||||
load(apps[appId].src);
|
function drawItem(itemI, r) {
|
||||||
return;
|
g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1);
|
||||||
}
|
let x = 0;
|
||||||
if (appId == selectedItem && apps[appId]) {
|
for (let i = itemI * appsN; i < appsN * (itemI + 1); i++) {
|
||||||
const app = apps[appId];
|
if (!apps[i]) break;
|
||||||
if (!app.src || s.read(app.src) === undefined) {
|
x += whitespace;
|
||||||
E.showMessage( /*LANG*/ "App Source\nNot found");
|
if (!apps[i].icon) {
|
||||||
} else {
|
g.setFontAlign(0,0,0).setFont("12x20:2").drawString("?", x + r.x+iconSize/2, r.y + iconSize/2);
|
||||||
load(app.src);
|
} else {
|
||||||
|
g.drawImage(apps[i].icon, x + r.x, r.y);
|
||||||
|
}
|
||||||
|
if (selectedItem == i) {
|
||||||
|
g.drawRect(
|
||||||
|
x + r.x - 1,
|
||||||
|
r.y - 1,
|
||||||
|
x + r.x + iconSize + 1,
|
||||||
|
r.y + iconSize + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
x += iconSize;
|
||||||
}
|
}
|
||||||
|
drawText(itemI);
|
||||||
}
|
}
|
||||||
selectedItem = appId;
|
|
||||||
drawItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
function idxToY(i) {
|
function drawItemAuto(i) {
|
||||||
return i * itemSize + R.y - (scroll & ~1);
|
var y = idxToY(i);
|
||||||
}
|
g.reset().setClipRect(R.x, y, R.x2, y + itemSize);
|
||||||
|
|
||||||
function YtoIdx(y) {
|
|
||||||
return Math.floor((y + (scroll & ~1) - R.y) / itemSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawItems() {
|
|
||||||
g.reset().clearRect(R.x, R.y, R.x2, R.y2);
|
|
||||||
g.setClipRect(R.x, R.y, R.x2, R.y2);
|
|
||||||
var a = YtoIdx(R.y);
|
|
||||||
var b = Math.min(YtoIdx(R.y2), 99);
|
|
||||||
for (var i = a; i <= b; i++)
|
|
||||||
drawItem(i, {
|
drawItem(i, {
|
||||||
x: R.x,
|
x: R.x,
|
||||||
y: idxToY(i),
|
y: y,
|
||||||
w: R.w,
|
w: R.w,
|
||||||
h: itemSize,
|
h: itemSize
|
||||||
});
|
});
|
||||||
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
|
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawItems();
|
let lastIsDown = false;
|
||||||
g.flip();
|
|
||||||
|
|
||||||
const itemsN = Math.ceil(apps.length / appsN);
|
function drawText(i) {
|
||||||
|
const selectedApp = apps[selectedItem];
|
||||||
|
const idy = (selectedItem - (selectedItem % 3)) / 3;
|
||||||
|
if (!selectedApp || i != idy) return;
|
||||||
|
const appY = idxToY(idy) + iconSize / 2;
|
||||||
|
g.setFontAlign(0, 0, 0);
|
||||||
|
g.setFont("12x20");
|
||||||
|
const rect = g.stringMetrics(selectedApp.name);
|
||||||
|
g.clearRect(
|
||||||
|
R.w / 2 - rect.width / 2,
|
||||||
|
appY - rect.height / 2,
|
||||||
|
R.w / 2 + rect.width / 2,
|
||||||
|
appY + rect.height / 2
|
||||||
|
);
|
||||||
|
g.drawString(selectedApp.name, R.w / 2, appY);
|
||||||
|
}
|
||||||
|
|
||||||
Bangle.setUI({
|
function selectItem(id, e) {
|
||||||
mode: "custom",
|
const iconN = E.clip(Math.floor((e.x - R.x) / itemSize), 0, appsN - 1);
|
||||||
drag: (e) => {
|
const appId = id * appsN + iconN;
|
||||||
|
if( settings.direct && apps[appId])
|
||||||
|
{
|
||||||
|
load(apps[appId].src);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (appId == selectedItem && apps[appId]) {
|
||||||
|
const app = apps[appId];
|
||||||
|
if (!app.src || s.read(app.src) === undefined) {
|
||||||
|
E.showMessage( /*LANG*/ "App Source\nNot found");
|
||||||
|
} else {
|
||||||
|
load(app.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedItem = appId;
|
||||||
|
drawItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
function idxToY(i) {
|
||||||
|
return i * itemSize + R.y - (scroll & ~1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function YtoIdx(y) {
|
||||||
|
return Math.floor((y + (scroll & ~1) - R.y) / itemSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawItems() {
|
||||||
|
g.reset().clearRect(R.x, R.y, R.x2, R.y2);
|
||||||
|
g.setClipRect(R.x, R.y, R.x2, R.y2);
|
||||||
|
var a = YtoIdx(R.y);
|
||||||
|
var b = Math.min(YtoIdx(R.y2), 99);
|
||||||
|
for (var i = a; i <= b; i++)
|
||||||
|
drawItem(i, {
|
||||||
|
x: R.x,
|
||||||
|
y: idxToY(i),
|
||||||
|
w: R.w,
|
||||||
|
h: itemSize,
|
||||||
|
});
|
||||||
|
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawItems();
|
||||||
|
g.flip();
|
||||||
|
|
||||||
|
const itemsN = Math.ceil(apps.length / appsN);
|
||||||
|
|
||||||
|
function onDrag(e){
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.setBgColor(g.theme.bg);
|
g.setBgColor(g.theme.bg);
|
||||||
let dy = e.dy;
|
let dy = e.dy;
|
||||||
|
@ -202,10 +206,15 @@ Bangle.setUI({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
|
g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1);
|
||||||
},
|
}
|
||||||
touch: (_, e) => {
|
|
||||||
if (e.y < R.y - 4) return;
|
Bangle.setUI({
|
||||||
var i = YtoIdx(e.y);
|
mode: "custom",
|
||||||
selectItem(i, e);
|
drag: onDrag,
|
||||||
},
|
touch: (_, e) => {
|
||||||
});
|
if (e.y < R.y - 4) return;
|
||||||
|
var i = YtoIdx(e.y);
|
||||||
|
selectItem(i, e);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "iconlaunch",
|
"id": "iconlaunch",
|
||||||
"name": "Icon Launcher",
|
"name": "Icon Launcher",
|
||||||
"shortName" : "Icon launcher",
|
"shortName" : "Icon launcher",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"description": "A launcher inspired by smartphones, with an icon-only scrollable menu.",
|
"description": "A launcher inspired by smartphones, with an icon-only scrollable menu.",
|
||||||
"tags": "tool,system,launcher",
|
"tags": "tool,system,launcher",
|
||||||
|
|
|
@ -8,3 +8,4 @@
|
||||||
0.07: Allow wrapping drawing in timeouts to get faster reactions
|
0.07: Allow wrapping drawing in timeouts to get faster reactions
|
||||||
Show/Hide widgets with swipe up or down
|
Show/Hide widgets with swipe up or down
|
||||||
0.08: Use default Bangle formatter for booleans
|
0.08: Use default Bangle formatter for booleans
|
||||||
|
0.09: Support new fast app switching
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,7 @@
|
||||||
"id": "imageclock",
|
"id": "imageclock",
|
||||||
"name": "Imageclock",
|
"name": "Imageclock",
|
||||||
"shortName": "Imageclock",
|
"shortName": "Imageclock",
|
||||||
"version": "0.08",
|
"version": "0.09",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"description": "BETA!!! File formats still subject to change --- This app is a highly customizable watchface. To use it, you need to select a watchface. You can build the watchfaces yourself without programming anything. All you need to do is write some json and create image files.",
|
"description": "BETA!!! File formats still subject to change --- This app is a highly customizable watchface. To use it, you need to select a watchface. You can build the watchfaces yourself without programming anything. All you need to do is write some json and create image files.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
Loading…
Reference in New Issue