notify: Update notify API for #527
Also some cleanup: - fall back to "src" if title is missing - move message-splitting into separate functionmaster
parent
8df352cca9
commit
7bb98d38e0
|
|
@ -80,7 +80,7 @@
|
|||
"name": "Notifications (default)",
|
||||
"shortName":"Notifications",
|
||||
"icon": "notify.png",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "A handler for displaying notifications that displays them in a bar at the top of the screen",
|
||||
"tags": "widget",
|
||||
"type": "notify",
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New Library!
|
||||
0.02: Add notification ID option
|
||||
0.03: Pass `area{x,y,w,h}` to render callback instead of just `y`
|
||||
|
|
@ -16,7 +16,7 @@ options = {
|
|||
src : string, // optional source name
|
||||
body : string, // optional body text
|
||||
icon : string, // optional icon (image string)
|
||||
render function(y) {} // function callback to render
|
||||
render function(area) {} // function callback to render in area{x,y,w,h}
|
||||
};
|
||||
// eg... show notification
|
||||
require("notify").show({title:"Test", body:"Hello"});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,36 @@
|
|||
var pos = 0;
|
||||
var id = null;
|
||||
let pos = 0;
|
||||
let id = null;
|
||||
|
||||
/**
|
||||
* Fit text into area, trying to insert newlines between words
|
||||
* Appends "..." if more text was present but didn't fit
|
||||
*
|
||||
* @param {string} text
|
||||
* @param {number} rows Maximum number of rows
|
||||
* @param {number} width Maximum line length, in characters
|
||||
*/
|
||||
function fitWords(text,rows,width) {
|
||||
// We never need more than rows*width characters anyway, split by any whitespace
|
||||
const words = text.trim().substr(0,rows*width).split(/\s+/);
|
||||
let row=1,len=0,limit=width;
|
||||
let result = "";
|
||||
for (let word of words) {
|
||||
// len==0 means first word of row, after that we also add a space
|
||||
if ((len?len+1:0)+word.length > limit) {
|
||||
if (row>=rows) {
|
||||
result += "...";
|
||||
break;
|
||||
}
|
||||
result += "\n";
|
||||
len=0;
|
||||
row++;
|
||||
if (row===rows) limit -= 3; // last row needs space for "..."
|
||||
}
|
||||
result += (len?" ":"") + word;
|
||||
len += (len?1:0) + word.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
options = {
|
||||
|
|
@ -13,77 +44,90 @@ var id = null;
|
|||
render function(y) // function callback to render
|
||||
}
|
||||
*/
|
||||
/*
|
||||
The screen is 240x240px, but has a 240x320 buffer, used like this:
|
||||
0,0: top-left ... 239,0: top-right
|
||||
[Normal screen contents: lines 0-239]
|
||||
239,0: bottom-left ... 239,239: bottom-right
|
||||
[Usually off-screen: lines 240-319]
|
||||
319,0: last line in buffer ... 319,239: last pixel in buffer
|
||||
|
||||
When moving the display area, the buffer wraps around
|
||||
|
||||
So we draw notifications at the end of the buffer,
|
||||
then shift the display down to show them without touching regular content.
|
||||
Apps don't know about this, so can just keep updating the usual display area.
|
||||
|
||||
For example, a size 40 notification:
|
||||
- Draws in bottom 40 buffer lines (279-319)
|
||||
- Shifts display down by 40px
|
||||
Display now shows buffer lines 279-319,0-199
|
||||
Apps/widgets keep drawing to buffer line 0-239 like nothing happened
|
||||
*/
|
||||
exports.show = function(options) {
|
||||
options = options || {};
|
||||
if (options.on===undefined) options.on = true;
|
||||
id = ("id" in options)?options.id:null;
|
||||
var h = options.size||80;
|
||||
var oldMode = Bangle.getLCDMode();
|
||||
let size = options.size || 80;
|
||||
if (size>80) {size = 80}
|
||||
const oldMode = Bangle.getLCDMode();
|
||||
// TODO: throw exception if double-buffered?
|
||||
// TODO: throw exception if size>80?
|
||||
|
||||
Bangle.setLCDMode("direct");
|
||||
var y = 320-h;
|
||||
var x = 4;
|
||||
g.setClipRect(0, y, 239, 319);
|
||||
// drawing area
|
||||
let x = 0,
|
||||
y = 320-size,
|
||||
w = 240,
|
||||
h = size,
|
||||
b = y+h-1, r = x+w-1; // bottom,right
|
||||
g.setClipRect(x,y, r,b);
|
||||
// clear area
|
||||
g.setColor(0).fillRect(0, y+1, 239, 317);
|
||||
// border
|
||||
g.setColor(0x39C7).fillRect(0, 318, 239, 319);
|
||||
// top bar
|
||||
var top = 0;
|
||||
if (options.title) {
|
||||
g.setColor(0x39C7).fillRect(0, y, 239, y+20);
|
||||
g.setColor(0).fillRect(x,y, r,b);
|
||||
// bottom border
|
||||
g.setColor(0x39C7).fillRect(0,b-1, r,b);
|
||||
b -= 2;h -= 2;
|
||||
// title bar
|
||||
if (options.title || options.src) {
|
||||
g.setColor(0x39C7).fillRect(x,y, r,y+20);
|
||||
const title = options.title||options.src;
|
||||
g.setColor(-1).setFontAlign(-1, -1, 0).setFont("6x8", 2);
|
||||
g.drawString(options.title.trim().substring(0, 13), 25, y+3);
|
||||
y+=20;
|
||||
g.drawString(title.trim().substring(0, 13), x+25,y+3);
|
||||
if (options.title && options.src) {
|
||||
g.setFont("6x8", 1);
|
||||
g.drawString(options.src.substring(0, 10), x+215,y+5);
|
||||
}
|
||||
if (options.src) {
|
||||
g.setColor(-1).setFontAlign(1, -1, 0).setFont("6x8", 1);
|
||||
g.drawString(options.src.substring(0, 10), 215, 322-h);
|
||||
y += 20;h -= 20;
|
||||
}
|
||||
if (options.icon) {
|
||||
let i = options.icon;
|
||||
let i = options.icon, iw;
|
||||
g.drawImage(i, x,y+4);
|
||||
if ("string"==typeof i) x += i.charCodeAt(0);
|
||||
else x += i[0];
|
||||
if ("string"==typeof i) {iw = i.charCodeAt(0)}
|
||||
else {iw = i[0]}
|
||||
x += iw;w -= iw;
|
||||
}
|
||||
// body text
|
||||
if (options.body) {
|
||||
var body = options.body;
|
||||
const maxChars = Math.floor((300-x)/8);
|
||||
var limit = maxChars;
|
||||
let row = 1;
|
||||
let words = body.trim().replace("\n", " ").split(" ");
|
||||
body = "";
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
if (body.length + words[i].length + 1 > limit) {
|
||||
if (row>=5) {
|
||||
body += "...";
|
||||
break;
|
||||
}
|
||||
body += "\n " + words[i];
|
||||
row++;
|
||||
limit += maxChars;
|
||||
if (row==5) limit -= 4;
|
||||
} else {
|
||||
body += " " + words[i];
|
||||
}
|
||||
}
|
||||
g.setColor(-1).setFont("6x8", 1).setFontAlign(-1, -1, 0).drawString(body, x-4, y+4);
|
||||
const maxRows=Math.floor((h-4)/8), // font=6x8
|
||||
maxChars=Math.floor(w/6)-2,
|
||||
text=fitWords(options.body, maxRows, maxChars);
|
||||
g.setColor(-1).setFont("6x8", 1).setFontAlign(-1, -1, 0).drawString(text, x+6,y+4);
|
||||
}
|
||||
|
||||
if (options.render) options.render(320 - h);
|
||||
if (options.render) {
|
||||
options.render({x:x, y:y, w:w, h:h});
|
||||
}
|
||||
|
||||
if (options.on) Bangle.setLCDPower(1); // light up
|
||||
Bangle.setLCDMode(oldMode); // clears cliprect
|
||||
|
||||
function anim() {
|
||||
pos -= 2;
|
||||
if (pos < -h) {
|
||||
pos = -h;
|
||||
if (pos < -size) {
|
||||
pos = -size;
|
||||
}
|
||||
Bangle.setLCDOffset(pos);
|
||||
if (pos > -h) setTimeout(anim, 15);
|
||||
if (pos > -size) setTimeout(anim, 15);
|
||||
}
|
||||
anim();
|
||||
Bangle.on("touch", exports.hide);
|
||||
|
|
|
|||
Loading…
Reference in New Issue