-
-
I haven't looked too deeply at the new layout library yet, but maybe this will serve as some inspiration...
A while back I was experimenting with a layout library of my own. I never quite got it to the point where I was happy releasing it to the world, but it did have some interesting features. Most notably:
- A flexbox-inspired layout system, with column and row support
- Support for elements with dependencies between their width and their height (eg. wrapping text)
- Easy to extend with new types of elements
- Automatic lazy rerendering - it would detect which elements were new or different each frame (by way of hashing them) and only clear / redraw those elements
Usage looked like this:
let canvas = trui.canvas(); canvas.render( trui.column({x: 0, y: 24, width: 240, height: 216, children: [ {flex: 1}, trui.padding({left: 10, right: 10, child: trui.text({font: "Vector", fitWidth: true, text: `${hours}:${padNum(d.getMinutes(), 2)}`}) }), {flex: 1}, trui.row({children: [ {flex: 1}, trui.text({font: "Vector", fontSize: 28, text: meridian}), {flex: 1}, trui.text({font: "Vector", fontSize: 28, text: padNum(d.getSeconds(), 2)}), {flex: 1} ]}), {flex: 1}, trui.wrappingText({font: "Vector", fontSize: 18, lineHeight: 30, align: 0, text: locale.date(d,false)}), ]}) );
(Each
{flex: 1}
there is a flexible spacer)If anyone would like to see it in action, they can try it here: https://www.espruino.com/ide/?gist=bde5d085b2723a87e93f55411c03983a
- A flexbox-inspired layout system, with column and row support
-
In theory you could avoid some of the memory overhead of the array returned by
match()
by instead usingreplace
to generate a new string that contains only the newlines:s.replace(/[^\n]/g, "").length
Unfortunately, there seems to be a bug in replace() that prevents this from working perfectly.
-
-
I'm surprised that the weather widget makes that much of a difference. It doesn't do any active polling or anything, it just passively waits for your phone to send it weather events.
@user112504: There are instructions for fixing the API key issue at https://github.com/gelin/weather-notification/wiki/FAQ
-
One approach to making sure you clear the right area would be to take advantage of g.getModified:
g.getModified(true); // Reset modified area // Insert whatever draw calls you want here g.drawString(...); let m = g.getModified(); // Save modified area for later g.clearRect(m.x1, m.y1, m.x2, m.y2); // Will clear the precise area affected by the above draw calls
-
-
@gerardwr - That looks like a good solution to me, although I would have personally made an entirely new function named drawLines or something instead of overriding g.drawString.
-
-
Currently the font size accurately represents the height of letters like A, but it doesn't take into account accented characters (like À) or descenders (like j). Accents and descenders both currently protrude by about 1/4 of the font height, so the actual maximum height of a line of text is 1.5 times what you might expect.
For some stuff I'm currently working on it would be convenient if
g.clearRect(x, y, x+g.stringWidth(text), y+g.getFontHeight())
was always guaranteed to cover a single line drawn viag.drawString(text)
, which isn't currently the case.Overall, though, I don't have very strong feelings about whether we should tweak the font size.
-
Modules can come from a number of different places, including storage: https://www.espruino.com/Modules#from-storage
-
You could remove one layer of function nesting by making each face a module.
Each face would then follow the format
exports.getFace = function (){ function onSecond(){ //draw digits, hands etc } function drawAll(){ //draw background + initial state of digits, hands etc } return {init:drawAll, tick:onSecond}; };
And to load them you'd do something along the lines of
let FACES = require("Storage").list(/\.face\.js$/).map(require);
-
The issue with Bold Clock appears to be due to a bug in Graphics.asImage - I filed a bug report here: https://github.com/espruino/Espruino/issues/1863
The simplest workaround appears to be to change these lines
let tick0 = Graphics.createArrayBuffer(30,8,1); tick0.fillRect(0,0,tick0.getWidth()-1, tick0.getHeight()-1); let tick5 = Graphics.createArrayBuffer(20,6,1); tick5.fillRect(0,0,tick5.getWidth()-1, tick5.getHeight()-1); let tick1 = Graphics.createArrayBuffer(8,4,1); tick1.fillRect(0,0,tick1.getWidth()-1, tick1.getHeight()-1);
to
let tick0 = Graphics.createArrayBuffer(30,8,1,{msb:true}); tick0.fillRect(0,0,tick0.getWidth()-1, tick0.getHeight()-1); let tick5 = Graphics.createArrayBuffer(20,6,1,{msb:true}); tick5.fillRect(0,0,tick5.getWidth()-1, tick5.getHeight()-1); let tick1 = Graphics.createArrayBuffer(8,4,1,{msb:true}); tick1.fillRect(0,0,tick1.getWidth()-1, tick1.getHeight()-1);
-
-
-
-
I've been able to reproduce the issue a couple times and I got your exact symptoms - no logs, only the bluetooth widget frozen, completely unresponsive until I reset by holding BTN1+BTN2.
Looking at the content of the WIDGETS array on my watch, I see that the order of the widgets is bluetooth, batpc, ram. The fact that the ram widgets is consistently the only one visible when it freezes is makes me suspect the issue might somehow be triggered by the battery (with percentage) widget.
I modified your code a bit because I'm impatient, and now it generally hangs within 5 minutes:
var ctr = 0; g.clear(); Bangle.loadWidgets(); Bangle.drawWidgets(); Bangle.setGPSPower(1); Bangle.setHRMPower(1); setInterval(function(){ g.reset(); g.setFont("6x8", 3); g.drawString(ctr.toString(),60,60,true); ctr++; Bangle.drawWidgets(); g.flip(); },1);
-
-
I think that code will just get stuck at -1 and 24. This should work a bit better (untested):
min: -24, max: 23, step: 1, format: v => (v+24)%24, onchange: v => { /* same as above */ }
As for why you aren't seeing your changes - a bit of a long shot, but you could be running into this issue. If that's the case, upgrading to a newer firmware version should help.
Also, I assume you're using the espruino IDE to upload your changes to the watch? You might find it more convenient to clone the repository locally and test your changes using either apploader.js or a local server, as discussed in this thread.
-
-
-
I'm seeing the same issue. It looks to me like
marked.min.js
isn't always loaded by the time thatindex.js
tries to use it.https://espruino.github.io/BangleApps/ seems to be working a bit better.
-
You can calculate the amount of ram a buffer will take up as width*height*bpp/8.
A 240x192 2bpp buffer takes up 11952 bytes, or about 12kb. If you halve the size to 120x96 you can fit quadruple the bit depth (8bpp) in the exact same amount of space.
Using multiple buffers should work fine - I was actually just experimenting with that the other night.
-
After a bit more fiddling, I've decided that it's not worth changing the grid just for the minor tweaks I was considering.
In terms of the accented chars, it's a trade-off since the extra code needed for them takes up quite a lot of space too,
By my math directly including the accented chars would take up a bit over a kilobyte. I'm guessing the extra code likely takes up less space than that?
My example does use lazy rendering! Each time
canvas.render
is called, the only things that get redrawn are those that don't already have their hash recorded. The thing to pay attention to is therects
property of the canvas, which acts as a map between the hashes of the elements currently on screen and the areas they occupy.The hashing is pretty fast, since it just delegates to E.toJS and E.CRC32, and the rest of the logic honestly isn't too complicated. I'd say it's at least worth investigating.
The Vector Clock app I just submitted also uses very similar lazy rendering logic, without all the layout stuff.
Correct me if I'm wrong, but it sounds like my 'flex' system is a bit more powerful? I'm guessing that only one element can be set to
{fillx:true}
in a row or column, whereas in my system the remaining space can be divided between any number of flexible elements, in proportion to their flex numbers. This means, for example, you could allocate 1/3 of the space in a row to one element and 2/3 to another by giving the first one a flex number of "1" and the second a flex number of "2".