forked from FOSS/BangleApps
Merge branch 'espruino:master' into master
commit
c2a79d92a6
|
@ -0,0 +1 @@
|
|||
0.01: Basic agenda with events from GB
|
|
@ -0,0 +1,3 @@
|
|||
# Agenda
|
||||
|
||||
Basic agenda reading the events synchronised from GadgetBridge
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwg1yhGIxAPMBwIPFhH//GAC5n/C4oHBC5/IGwoXBHQQAKC4OIFAWOxHv9GO9wAKI4XoC4foEIIWLC4IABC4gIBFxnuE4IqBC4gARC4ZzNAAwXaxe7ACO4C625C4m4xIJBzAeCxGbCAOIFgQOBC4pOBxe4AYIPBAYQKCAYYXE3GL/ADBx/oxb3BC4X+xG4xwOBC4uP/YDB54MBf4Po3eM/4XBx/+C4pTBGIIkBLgOYAYIvB9GJBwI6BL45zCL4aCCL4h3GU64ALdYS1CI55bBAAgXFO4mMO4QDBDIO/////YxBU53IxIVB/GfDAWYa5wtC/GPAYWIL4wXBL4oSBC4jcBC4m4QIWYSwWIIQIAG/CnMMAIAC/JLCMIIvMIwZHFJAJfLC5yPHAYIRDAoy/KCIi7BMon4d4+Od4IXBxAZBEQLtB/+YxIXDL4SLCL4WPzAXCNgRFBLIKnKLIrcEI4gXNAAp3CxGZAAzCBC5KnCKAIAICxBlBC4IAJxG/C4/4wAXLhBgD/IcD3AXMGAIqDDgRGNGAoXDFxxhEI4W4FxwwCaoYWBFx4YDAAQWRAEQ"))
|
|
@ -0,0 +1,127 @@
|
|||
/* CALENDAR is a list of:
|
||||
{id:int,
|
||||
type,
|
||||
timestamp,
|
||||
durationInSeconds,
|
||||
title,
|
||||
description,
|
||||
location,
|
||||
allDay: bool,
|
||||
}
|
||||
*/
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
var FILE = "android.calendar.json";
|
||||
|
||||
var Locale = require("locale");
|
||||
|
||||
var fontSmall = "6x8";
|
||||
var fontMedium = g.getFonts().includes("6x15")?"6x15":"6x8:2";
|
||||
var fontBig = g.getFonts().includes("12x20")?"12x20":"6x8:2";
|
||||
var fontLarge = g.getFonts().includes("6x15")?"6x15:2":"6x8:4";
|
||||
|
||||
//FIXME maybe write the end from GB already? Not durationInSeconds here (or do while receiving?)
|
||||
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||
|
||||
CALENDAR=CALENDAR.sort((a,b)=>a.timestamp - b.timestamp)
|
||||
|
||||
function getDate(timestamp) {
|
||||
return new Date(timestamp*1000);
|
||||
}
|
||||
function formatDateLong(date, includeDay) {
|
||||
if(includeDay)
|
||||
return Locale.date(date)+" "+Locale.time(date,1);
|
||||
return Locale.time(date,1);
|
||||
}
|
||||
function formatDateShort(date) {
|
||||
return Locale.date(date).replace(/\d\d\d\d/,"")+Locale.time(date,1);
|
||||
}
|
||||
|
||||
var lines = [];
|
||||
function showEvent(ev) {
|
||||
var bodyFont = fontBig;
|
||||
if(!ev) return;
|
||||
g.setFont(bodyFont);
|
||||
//var lines = [];
|
||||
if (ev.title) lines = g.wrapString(ev.title, g.getWidth()-10)
|
||||
var titleCnt = lines.length;
|
||||
var start = getDate(ev.timestamp);
|
||||
var end = getDate((+ev.timestamp) + (+ev.durationInSeconds));
|
||||
var includeDay = true;
|
||||
if (titleCnt) lines.push(""); // add blank line after title
|
||||
if(start.getDay() == end.getDay() && start.getMonth() == end.getMonth())
|
||||
includeDay = false;
|
||||
if(includeDay) {
|
||||
lines = lines.concat(
|
||||
/*LANG*/"Start:",
|
||||
g.wrapString(formatDateLong(start, includeDay), g.getWidth()-10),
|
||||
/*LANG*/"End:",
|
||||
g.wrapString(formatDateLong(end, includeDay), g.getWidth()-10));
|
||||
} else {
|
||||
lines = lines.concat(
|
||||
g.wrapString(Locale.date(start), g.getWidth()-10),
|
||||
g.wrapString(/*LANG*/"Start"+": "+formatDateLong(start, includeDay), g.getWidth()-10),
|
||||
g.wrapString(/*LANG*/"End"+": "+formatDateLong(end, includeDay), g.getWidth()-10));
|
||||
}
|
||||
if(ev.location)
|
||||
lines = lines.concat(/*LANG*/"Location"+": ", g.wrapString(ev.location, g.getWidth()-10));
|
||||
if(ev.description)
|
||||
lines = lines.concat("",g.wrapString(ev.description, g.getWidth()-10));
|
||||
lines = lines.concat(["",/*LANG*/"< Back"]);
|
||||
E.showScroller({
|
||||
h : g.getFontHeight(), // height of each menu item in pixels
|
||||
c : lines.length, // number of menu items
|
||||
// a function to draw a menu item
|
||||
draw : function(idx, r) {
|
||||
// FIXME: in 2v13 onwards, clearRect(r) will work fine. There's a bug in 2v12
|
||||
g.setBgColor(idx<titleCnt ? g.theme.bg2 : g.theme.bg).
|
||||
setColor(idx<titleCnt ? g.theme.fg2 : g.theme.fg).
|
||||
clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
|
||||
g.setFont(bodyFont).drawString(lines[idx], r.x, r.y);
|
||||
}, select : function(idx) {
|
||||
if (idx>=lines.length-2)
|
||||
showList();
|
||||
},
|
||||
back : () => showList()
|
||||
});
|
||||
}
|
||||
|
||||
function showList() {
|
||||
if(CALENDAR.length == 0) {
|
||||
E.showMessage("No events");
|
||||
return;
|
||||
}
|
||||
E.showScroller({
|
||||
h : 52,
|
||||
c : Math.max(CALENDAR.length,3), // workaround for 2v10.219 firmware (min 3 not needed for 2v11)
|
||||
draw : function(idx, r) {"ram"
|
||||
var ev = CALENDAR[idx];
|
||||
g.setColor(g.theme.fg);
|
||||
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
|
||||
if (!ev) return;
|
||||
var isPast = ev.timestamp + ev.durationInSeconds < (new Date())/1000;
|
||||
var x = r.x+2, title = ev.title;
|
||||
var body = formatDateShort(getDate(ev.timestamp))+"\n"+ev.location;
|
||||
var m = ev.title+"\n"+ev.location, longBody=false;
|
||||
if (title) g.setFontAlign(-1,-1).setFont(fontBig)
|
||||
.setColor(isPast ? "#888" : g.theme.fg).drawString(title, x,r.y+2);
|
||||
if (body) {
|
||||
g.setFontAlign(-1,-1).setFont(fontMedium).setColor(isPast ? "#888" : g.theme.fg);
|
||||
var l = g.wrapString(body, r.w-(x+14));
|
||||
if (l.length>3) {
|
||||
l = l.slice(0,3);
|
||||
l[l.length-1]+="...";
|
||||
}
|
||||
longBody = l.length>2;
|
||||
g.drawString(l.join("\n"), x+10,r.y+20);
|
||||
}
|
||||
//if (!longBody && msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2);
|
||||
g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items
|
||||
},
|
||||
select : idx => showEvent(CALENDAR[idx]),
|
||||
back : () => load()
|
||||
});
|
||||
}
|
||||
showList();
|
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "agenda",
|
||||
"name": "Agenda",
|
||||
"version": "0.02",
|
||||
"description": "Simple agenda",
|
||||
"icon": "agenda.png",
|
||||
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],
|
||||
"tags": "agenda",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"agenda.app.js","url":"agenda.js"},
|
||||
{"name":"agenda.settings.js","url":"settings.js"},
|
||||
{"name":"agenda.img","url":"agenda-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1,37 @@
|
|||
(function(back) {
|
||||
function gbSend(message) {
|
||||
Bluetooth.println("");
|
||||
Bluetooth.println(JSON.stringify(message));
|
||||
}
|
||||
var CALENDAR = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||
var mainmenu = {
|
||||
"" : { "title" : "Agenda" },
|
||||
"< Back" : back,
|
||||
/*LANG*/"Connected" : { value : NRF.getSecurityStatus().connected?/*LANG*/"Yes":/*LANG*/"No" },
|
||||
/*LANG*/"Force calendar sync" : () => {
|
||||
if(NRF.getSecurityStatus().connected) {
|
||||
E.showPrompt(/*LANG*/"Do you want to also clear the internal database first?", {
|
||||
buttons: {/*LANG*/"Yes": 1, /*LANG*/"No": 2, /*LANG*/"Cancel": 3}
|
||||
}).then((v)=>{
|
||||
switch(v) {
|
||||
case 1:
|
||||
require("Storage").writeJSON("android.calendar.json",[]);
|
||||
CALENDAR = [];
|
||||
/* falls through */
|
||||
case 2:
|
||||
gbSend({t:"force_calendar_sync", ids: CALENDAR.map(e=>e.id)});
|
||||
E.showAlert(/*LANG*/"Request sent to the phone").then(()=>E.showMenu(mainmenu));
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
E.showMenu(mainmenu);
|
||||
return;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
E.showAlert(/*LANG*/"You are not connected").then(()=>E.showMenu(mainmenu));
|
||||
}
|
||||
},
|
||||
};
|
||||
E.showMenu(mainmenu);
|
||||
})
|
|
@ -90,6 +90,35 @@
|
|||
sched.setAlarms(alarms);
|
||||
sched.reload();
|
||||
},
|
||||
//TODO perhaps move those in a library (like messages), used also for viewing events?
|
||||
//simple package with events all together
|
||||
"calendarevents" : function() {
|
||||
require("Storage").writeJSON("android.calendar.json", event.events);
|
||||
},
|
||||
//add and remove events based on activity on phone (pebble-like)
|
||||
"calendar" : function() {
|
||||
var cal = require("Storage").readJSON("android.calendar.json",true);
|
||||
if (!cal || !Array.isArray(cal)) cal = [];
|
||||
var i = cal.findIndex(e=>e.id==event.id);
|
||||
if(i<0)
|
||||
cal.push(event);
|
||||
else
|
||||
cal[i] = event;
|
||||
require("Storage").writeJSON("android.calendar.json", cal);
|
||||
},
|
||||
"calendar-" : function() {
|
||||
var cal = require("Storage").readJSON("android.calendar.json",true);
|
||||
//if any of those happen we are out of sync!
|
||||
if (!cal || !Array.isArray(cal)) return;
|
||||
cal = cal.filter(e=>e.id!=event.id);
|
||||
require("Storage").writeJSON("android.calendar.json", cal);
|
||||
},
|
||||
//triggered by GB, send all ids
|
||||
"force_calendar_sync_start" : function() {
|
||||
var cal = require("Storage").readJSON("android.calendar.json",true);
|
||||
if (!cal || !Array.isArray(cal)) cal = [];
|
||||
gbSend({t:"force_calendar_sync", ids: cal.map(e=>e.id)});
|
||||
}
|
||||
};
|
||||
var h = HANDLERS[event.t];
|
||||
if (h) h(); else console.log("GB Unknown",event);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "android",
|
||||
"name": "Android Integration",
|
||||
"shortName": "Android",
|
||||
"version": "0.10",
|
||||
"version": "0.11",
|
||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||
|
@ -15,6 +15,6 @@
|
|||
{"name":"android.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"android.boot.js","url":"boot.js"}
|
||||
],
|
||||
"data": [{"name":"android.settings.json"}],
|
||||
"data": [{"name":"android.settings.json"}, {"name":"android.calendar.json"}],
|
||||
"sortorder": -8
|
||||
}
|
||||
|
|
|
@ -33,8 +33,6 @@ const clock = new ClockFace({
|
|||
this.scale = g.getWidth() / this.viewport.width;
|
||||
this.centerTimeScaleX = this.center.x + 32 * this.scale;
|
||||
this.centerDatesScaleX = this.center.x + 40 * this.scale;
|
||||
|
||||
this.showWeekNum = Object.assign({ showWeekNum: true }, require("Storage").readJSON("ffcniftya.json", true))["showWeekNum"];
|
||||
},
|
||||
draw: function (date) {
|
||||
const hour = date.getHours() - (this.is12Hour && date.getHours() > 12 ? 12 : 0);
|
||||
|
@ -55,6 +53,7 @@ const clock = new ClockFace({
|
|||
if (this.showWeekNum) g.drawString(format(ISO8601_week_no(date)), this.centerDatesScaleX, this.center.y + 15 * this.scale);
|
||||
g.drawString(monthName, this.centerDatesScaleX, this.center.y + 48 * this.scale);
|
||||
g.drawString(dayName, this.centerDatesScaleX, this.center.y + 66 * this.scale);
|
||||
}
|
||||
},
|
||||
settingsFile: "ffcniftya.json"
|
||||
});
|
||||
clock.start();
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Call setUI before loading widgets
|
||||
Fix bug with black being unselectable
|
||||
Improve settings page
|
||||
0.04: Use ClockFace library
|
||||
|
|
|
@ -1,20 +1,10 @@
|
|||
const is12Hour = Object.assign({ "12hour": false }, require("Storage").readJSON("setting.json", true))["12hour"];
|
||||
const color = Object.assign({ color: 63488 }, require("Storage").readJSON("ffcniftyb.json", true)).color; // Default to RED
|
||||
var scale;
|
||||
var screen;
|
||||
var center;
|
||||
var buf;
|
||||
var img;
|
||||
|
||||
/* Clock *********************************************/
|
||||
const scale = g.getWidth() / 176;
|
||||
|
||||
const screen = {
|
||||
width: g.getWidth(),
|
||||
height: g.getHeight() - 24,
|
||||
};
|
||||
|
||||
const center = {
|
||||
x: screen.width / 2,
|
||||
y: screen.height / 2,
|
||||
};
|
||||
|
||||
function d02(value) {
|
||||
function format(value) {
|
||||
return ("0" + value).substr(-2);
|
||||
}
|
||||
|
||||
|
@ -22,91 +12,69 @@ function renderEllipse(g) {
|
|||
g.fillEllipse(center.x - 5 * scale, center.y - 70 * scale, center.x + 160 * scale, center.y + 90 * scale);
|
||||
}
|
||||
|
||||
function renderText(g) {
|
||||
const now = new Date();
|
||||
function renderText(g, date) {
|
||||
const hour = date.getHours() - (this.is12Hour && date.getHours() > 12 ? 12 : 0);
|
||||
const month = date.getMonth() + 1;
|
||||
|
||||
const hour = d02(now.getHours() - (is12Hour && now.getHours() > 12 ? 12 : 0));
|
||||
const minutes = d02(now.getMinutes());
|
||||
const day = d02(now.getDate());
|
||||
const month = d02(now.getMonth() + 1);
|
||||
const year = now.getFullYear();
|
||||
|
||||
const month2 = require("locale").month(now, 3);
|
||||
const day2 = require("locale").dow(now, 3);
|
||||
const monthName = require("date_utils").month(month, 1);
|
||||
const dayName = require("date_utils").dow(date.getDay(), 1);
|
||||
|
||||
g.setFontAlign(1, 0).setFont("Vector", 90 * scale);
|
||||
g.drawString(hour, center.x + 32 * scale, center.y - 31 * scale);
|
||||
g.drawString(minutes, center.x + 32 * scale, center.y + 46 * scale);
|
||||
g.drawString(format(hour), center.x + 32 * scale, center.y - 31 * scale);
|
||||
g.drawString(format(date.getMinutes()), center.x + 32 * scale, center.y + 46 * scale);
|
||||
|
||||
g.setFontAlign(1, 0).setFont("Vector", 16 * scale);
|
||||
g.drawString(year, center.x + 80 * scale, center.y - 42 * scale);
|
||||
g.drawString(month, center.x + 80 * scale, center.y - 26 * scale);
|
||||
g.drawString(day, center.x + 80 * scale, center.y - 10 * scale);
|
||||
g.drawString(month2, center.x + 80 * scale, center.y + 44 * scale);
|
||||
g.drawString(day2, center.x + 80 * scale, center.y + 60 * scale);
|
||||
g.drawString(date.getFullYear(), center.x + 80 * scale, center.y - 42 * scale);
|
||||
g.drawString(format(month), center.x + 80 * scale, center.y - 26 * scale);
|
||||
g.drawString(format(date.getDate()), center.x + 80 * scale, center.y - 10 * scale);
|
||||
g.drawString(monthName, center.x + 80 * scale, center.y + 44 * scale);
|
||||
g.drawString(dayName, center.x + 80 * scale, center.y + 60 * scale);
|
||||
}
|
||||
|
||||
const buf = Graphics.createArrayBuffer(screen.width, screen.height, 1, {
|
||||
msb: true
|
||||
const ClockFace = require("ClockFace");
|
||||
const clock = new ClockFace({
|
||||
init: function () {
|
||||
const appRect = Bangle.appRect;
|
||||
|
||||
screen = {
|
||||
width: appRect.w,
|
||||
height: appRect.h
|
||||
};
|
||||
|
||||
center = {
|
||||
x: screen.width / 2,
|
||||
y: screen.height / 2
|
||||
};
|
||||
|
||||
buf = Graphics.createArrayBuffer(screen.width, screen.height, 1, { msb: true });
|
||||
|
||||
scale = g.getWidth() / screen.width;
|
||||
|
||||
img = {
|
||||
width: screen.width,
|
||||
height: screen.height,
|
||||
transparent: 0,
|
||||
bpp: 1,
|
||||
buffer: buf.buffer
|
||||
};
|
||||
|
||||
// default to RED (see settings.js)
|
||||
// don't use || to default because 0 is a valid color
|
||||
this.color = this.color === undefined ? 63488 : this.color;
|
||||
},
|
||||
draw: function (date) {
|
||||
// render outside text with ellipse
|
||||
buf.clear();
|
||||
renderText(buf.setColor(1), date);
|
||||
renderEllipse(buf.setColor(0));
|
||||
g.setColor(this.color).drawImage(img, 0, 24);
|
||||
|
||||
// render ellipse with inside text
|
||||
buf.clear();
|
||||
renderEllipse(buf.setColor(1));
|
||||
renderText(buf.setColor(0), date);
|
||||
g.setColor(this.color).drawImage(img, 0, 24);
|
||||
},
|
||||
settingsFile: "ffcniftyb.json"
|
||||
});
|
||||
|
||||
function draw() {
|
||||
|
||||
const img = {
|
||||
width: screen.width,
|
||||
height: screen.height,
|
||||
transparent: 0,
|
||||
bpp: 1,
|
||||
buffer: buf.buffer
|
||||
};
|
||||
|
||||
// cleat screen area
|
||||
g.clearRect(0, 24, g.getWidth(), g.getHeight());
|
||||
|
||||
// render outside text with ellipse
|
||||
buf.clear();
|
||||
renderText(buf.setColor(1));
|
||||
renderEllipse(buf.setColor(0));
|
||||
g.setColor(color).drawImage(img, 0, 24);
|
||||
|
||||
// render ellipse with inside text
|
||||
buf.clear();
|
||||
renderEllipse(buf.setColor(1));
|
||||
renderText(buf.setColor(0));
|
||||
g.setColor(color).drawImage(img, 0, 24);
|
||||
}
|
||||
|
||||
|
||||
/* Minute Ticker *************************************/
|
||||
|
||||
let ticker;
|
||||
|
||||
function stopTick() {
|
||||
if (ticker) {
|
||||
clearTimeout(ticker);
|
||||
ticker = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function startTick(run) {
|
||||
stopTick();
|
||||
run();
|
||||
ticker = setTimeout(() => startTick(run), 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
/* Init **********************************************/
|
||||
|
||||
g.clear();
|
||||
startTick(draw);
|
||||
|
||||
Bangle.on("lcdPower", (on) => {
|
||||
if (on) {
|
||||
startTick(draw);
|
||||
} else {
|
||||
stopTick();
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
clock.start();
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "ffcniftyb",
|
||||
"name": "Nifty-B Clock",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "A nifty clock (series B) with time, date and colour configuration",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
|
|
|
@ -82,6 +82,7 @@ function onInit(device) {
|
|||
if (crc==46757280) version = "2v11.58";
|
||||
if (crc==3508163280 || crc==1418074094) version = "2v12";
|
||||
if (crc==4056371285) version = "2v13";
|
||||
if (crc==1038322422) version = "2v14";
|
||||
if (!ok) {
|
||||
version += `(⚠ update required)`;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/nodejs
|
||||
#!/usr/bin/node
|
||||
/*
|
||||
Mashes together a bunch of different apps into a big binary blob.
|
||||
We then store this *inside* the Bangle.js firmware and can use it
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
|
||||
// See Layout.md for documentation
|
||||
|
||||
/* Minify to 'Layout.min.js' by:
|
||||
|
||||
* checking out: https://github.com/espruino/EspruinoDocs
|
||||
* run: ../EspruinoDocs/bin/minify.js modules/Layout.js modules/Layout.min.js
|
||||
|
||||
*/
|
||||
|
||||
|
||||
function Layout(layout, options) {
|
||||
this._l = this.l = layout;
|
||||
// Do we have >1 physical buttons?
|
||||
|
@ -71,7 +79,7 @@ function Layout(layout, options) {
|
|||
Layout.prototype.setUI = function() {
|
||||
Bangle.setUI(); // remove all existing input handlers
|
||||
|
||||
var uiSet;
|
||||
let uiSet;
|
||||
if (this.buttons) {
|
||||
// multiple buttons so we'll jus use back/next/select
|
||||
Bangle.setUI({mode:"updown", back:this.options.back}, dir=>{
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
function p(b,k){function d(h){h.id&&(f[h.id]=h);h.type||(h.type="");h.c&&h.c.forEach(d)}this._l=this.l=b;this.physBtns=2==process.env.HWVERSION?1:3;this.options=k||{};this.lazy=this.options.lazy||!1;if(2!=process.env.HWVERSION){var a=[];function h(m){"btn"==m.type&&a.push(m);m.c&&m.c.forEach(h)}h(b);a.length&&(this.physBtns=0,this.buttons=a,this.selectedButton=-1)}if(this.options.btns)if(this.b=b=this.options.btns,this.physBtns>=b.length){let h=Math.floor(Bangle.appRect.h/
|
||||
this.physBtns);for(2<this.physBtns&&1==b.length&&b.unshift({label:""});this.physBtns>b.length;)b.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:b.map(m=>(m.type="txt",m.font="6x8",m.height=h,m.r=1,m))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:b.map(h=>(h.type="btn",h.filly=1,h.width=32,h.r=1,h))}]},a&&a.push.apply(a,this._l.c[1].c);this.setUI();var f=this;d(this._l);this.updateNeeded=!0}function r(b,
|
||||
k,d,a,f){var h=null==b.bgCol?f:g.toColor(b.bgCol);if(h!=f||"txt"==b.type||"btn"==b.type||"img"==b.type||"custom"==b.type){var m=b.c;delete b.c;var c="H"+E.CRC32(E.toJS(b));m&&(b.c=m);delete k[c]||((a[c]=[b.x,b.y,b.x+b.w-1,b.y+b.h-1]).bg=null==f?g.theme.bg:f,d&&(d.push(b),d=null))}if(b.c)for(var l of b.c)r(l,k,d,a,h)}p.prototype.setUI=function(){Bangle.setUI();let b;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back},k=>{var d=this.selectedButton,a=this.buttons.length;if(void 0===k&&
|
||||
this.buttons[d])return this.buttons[d].cb();this.buttons[d]&&(delete this.buttons[d].selected,this.render(this.buttons[d]));d=(d+a+k)%a;this.buttons[d]&&(this.buttons[d].selected=1,this.render(this.buttons[d]));this.selectedButton=d}),b=!0);this.options.back&&!b&&Bangle.setUI({mode:"custom",back:this.options.back});if(this.b){function k(d,a){.75<a.time-a.lastTime&&this.b[d].cbl?this.b[d].cbl(a):this.b[d].cb&&this.b[d].cb(a)}Bangle.btnWatches&&Bangle.btnWatches.forEach(clearWatch);Bangle.btnWatches=
|
||||
[];this.b[0]&&Bangle.btnWatches.push(setWatch(k.bind(this,0),BTN1,{repeat:!0,edge:-1}));this.b[1]&&Bangle.btnWatches.push(setWatch(k.bind(this,1),BTN2,{repeat:!0,edge:-1}));this.b[2]&&Bangle.btnWatches.push(setWatch(k.bind(this,2),BTN3,{repeat:!0,edge:-1}))}if(2==process.env.HWVERSION){function k(d,a){d.cb&&a.x>=d.x&&a.y>=d.y&&a.x<=d.x+d.w&&a.y<=d.y+d.h&&(2==a.type&&d.cbl?d.cbl(a):d.cb&&d.cb(a));d.c&&d.c.forEach(f=>k(f,a))}Bangle.touchHandler=(d,a)=>k(this._l,a);Bangle.on("touch",Bangle.touchHandler)}};
|
||||
p.prototype.render=function(b){function k(c){"ram";g.reset();void 0!==c.col&&g.setColor(c.col);void 0!==c.bgCol&&g.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);d[c.type](c)}b||(b=this._l);this.updateNeeded&&this.update();var d={"":function(){},txt:function(c){if(c.wrap){g.setFont(c.font).setFontAlign(0,-1);var l=g.wrapString(c.label,c.w),e=c.y+(c.h-g.getFontHeight()*l.length>>1);l.forEach((n,q)=>g.drawString(n,c.x+(c.w>>1),e+g.getFontHeight()*q))}else g.setFont(c.font).setFontAlign(0,
|
||||
0,c.r).drawString(c.label,c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){var l=c.x+(0|c.pad),e=c.y+(0|c.pad),n=c.w-(c.pad<<1),q=c.h-(c.pad<<1);l=[l,e+4,l+4,e,l+n-5,e,l+n-1,e+4,l+n-1,e+q-5,l+n-5,e+q-1,l+4,e+q-1,l,e+q-5,l,e+4];e=c.selected?g.theme.bgH:g.theme.bg2;g.setColor(e).fillPoly(l).setColor(c.selected?g.theme.fgH:g.theme.fg2).drawPoly(l);void 0!==c.col&&g.setColor(c.col);c.src?g.setBgColor(e).drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*
|
||||
(c.r||0)}):g.setFont(c.font||"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){g.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){c.render(c)},h:function(c){c.c.forEach(k)},v:function(c){c.c.forEach(k)}};if(this.lazy){this.rects||(this.rects={});var a=this.rects.clone(),f=[];r(b,a,f,this.rects,null);for(var h in a)delete this.rects[h];b=Object.keys(a).map(c=>a[c]).reverse();
|
||||
for(var m of b)g.setBgColor(m.bg).clearRect.apply(g,m);f.forEach(k)}else k(b)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(b){switch(b.type){case "h":var k=b.x+(0|b.pad),d=0,a=b.c&&b.c.reduce((e,n)=>e+(0|n.fillx),0);a||(k+=b.w-b._w>>1,a=1);var f=k;b.c.forEach(e=>{e.x=0|f;k+=e._w;d+=0|e.fillx;f=k+Math.floor(d*(b.w-b._w)/a);e.w=0|f-e.x;e.h=0|(e.filly?b.h-(b.pad<<1):e._h);e.y=0|b.y+(0|b.pad)+((1+(0|e.valign))*(b.h-(b.pad<<1)-e.h)>>1);e.c&&this.layout(e)});break;
|
||||
case "v":var h=b.y+(0|b.pad),m=0,c=b.c&&b.c.reduce((e,n)=>e+(0|n.filly),0);c||(h+=b.h-b._h>>1,c=1);var l=h;b.c.forEach(e=>{e.y=0|l;h+=e._h;m+=0|e.filly;l=h+Math.floor(m*(b.h-b._h)/c);e.h=0|l-e.y;e.w=0|(e.fillx?b.w-(b.pad<<1):e._w);e.x=0|b.x+(0|b.pad)+((1+(0|e.halign))*(b.w-(b.pad<<1)-e.w)>>1);e.c&&this.layout(e)})}};p.prototype.debug=function(b,k){b||(b=this._l);k=k||1;g.setColor(k&1,k&2,k&4).drawRect(b.x+k-1,b.y+k-1,b.x+b.w-k,b.y+b.h-k);b.pad&&g.drawRect(b.x+b.pad-1,b.y+b.pad-1,b.x+b.w-b.pad,b.y+
|
||||
b.h-b.pad);k++;b.c&&b.c.forEach(d=>this.debug(d,k))};p.prototype.update=function(){function b(a){"ram";k[a.type](a);if(a.r&1){var f=a._w;a._w=a._h;a._h=f}a._w=0|Math.max(a._w+(a.pad<<1),0|a.width);a._h=0|Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var k={txt:function(a){a.font.endsWith("%")&&(a.font="Vector"+Math.round(g.getHeight()*a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var f=g.setFont(a.font).stringMetrics(a.label);a._w=f.width;a._h=f.height}},btn:function(a){a.font&&
|
||||
a.font.endsWith("%")&&(a.font="Vector"+Math.round(g.getHeight()*a.font.slice(0,-1)/100));var f=a.src?g.imageMetrics("function"==typeof a.src?a.src():a.src):g.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+f.height;a._w=20+f.width},img:function(a){var f=g.imageMetrics("function"==typeof a.src?a.src():a.src),h=a.scale||1;a._w=f.width*h;a._h=f.height*h},"":function(a){a._w=0;a._h=0},custom:function(a){a._w=0;a._h=0},h:function(a){a.c.forEach(b);a._h=a.c.reduce((f,h)=>Math.max(f,h._h),0);a._w=
|
||||
a.c.reduce((f,h)=>f+h._w,0);null==a.fillx&&a.c.some(f=>f.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(f=>f.filly)&&(a.filly=1)},v:function(a){a.c.forEach(b);a._h=a.c.reduce((f,h)=>f+h._h,0);a._w=a.c.reduce((f,h)=>Math.max(f,h._w),0);null==a.fillx&&a.c.some(f=>f.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(f=>f.filly)&&(a.filly=1)}},d=this._l;b(d);d.fillx||d.filly?(d.w=Bangle.appRect.w,d.h=Bangle.appRect.h,d.x=Bangle.appRect.x,d.y=Bangle.appRect.y):(d.w=d._w,d.h=d._h,d.x=Bangle.appRect.w-d.w>>1,d.y=
|
||||
Bangle.appRect.y+(Bangle.appRect.h-d.h>>1));this.layout(d)};p.prototype.clear=function(b){b||(b=this._l);g.reset();void 0!==b.bgCol&&g.setBgColor(b.bgCol);g.clearRect(b.x,b.y,b.x+b.w-1,b.y+b.h-1)};exports=p
|
|
@ -18,24 +18,35 @@ SRCJS=$1
|
|||
SRCBMP=$SRCDIR/`basename $SRCJS .js`.bmp
|
||||
echo "TEST $SRCJS ($SRCBMP)"
|
||||
|
||||
cat ../../modules/Layout.js > $TESTJS
|
||||
echo 'Bangle = { setUI : function(){}, appRect:{x:0,y:0,w:176,h:176,x2:175,y2:175} };BTN1=0;process.env = process.env;process.env.HWVERSION=2;' >> $TESTJS
|
||||
echo 'g = Graphics.createArrayBuffer(176,176,4);' >> $TESTJS
|
||||
cat $SRCJS >> $TESTJS || exit 1
|
||||
echo 'layout.render()' >> $TESTJS
|
||||
#echo 'layout.debug()' >> $TESTJS
|
||||
echo 'require("fs").writeFileSync("'$TESTBMP'",g.asBMP())' >> $TESTJS
|
||||
run_test () {
|
||||
LAYOUTFILE=$1
|
||||
echo 'exports = {};' > $TESTJS
|
||||
cat $LAYOUTFILE >> $TESTJS
|
||||
echo ';' >> $TESTJS
|
||||
echo 'Layout = exports;' >> $TESTJS
|
||||
echo 'Bangle = { setUI : function(){}, appRect:{x:0,y:0,w:176,h:176,x2:175,y2:175} };BTN1=0;process.env = process.env;process.env.HWVERSION=2;' >> $TESTJS
|
||||
echo 'g = Graphics.createArrayBuffer(176,176,4);' >> $TESTJS
|
||||
cat $SRCJS >> $TESTJS || exit 1
|
||||
echo 'layout.render()' >> $TESTJS
|
||||
#echo 'layout.debug()' >> $TESTJS
|
||||
echo 'require("fs").writeFileSync("'$TESTBMP'",g.asBMP())' >> $TESTJS
|
||||
echo =============================================
|
||||
echo TESTING $LAYOUTFILE $SRCJS
|
||||
bin/espruino $TESTJS || exit 1
|
||||
if ! cmp $TESTBMP $SRCBMP >/dev/null 2>&1
|
||||
then
|
||||
echo =============================================
|
||||
echo $LAYOUTFILE
|
||||
echo $TESTBMP $SRCBMP differ
|
||||
echo ==============================================
|
||||
convert "+append" $TESTBMP $SRCBMP testresult.bmp
|
||||
display testresult.bmp
|
||||
exit 1
|
||||
else
|
||||
echo Files are the same
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
bin/espruino $TESTJS || exit 1
|
||||
if ! cmp $TESTBMP $SRCBMP >/dev/null 2>&1
|
||||
then
|
||||
echo =============================================
|
||||
echo $TESTBMP $SRCBMP differ
|
||||
echo ==============================================
|
||||
convert "+append" $TESTBMP $SRCBMP testresult.bmp
|
||||
display testresult.bmp
|
||||
exit 1
|
||||
else
|
||||
echo Files are the same
|
||||
exit 0
|
||||
fi
|
||||
run_test ../../modules/Layout.js
|
||||
run_test ../../modules/Layout.min.js
|
||||
|
|
Loading…
Reference in New Issue