Skip to content

Commit b84c0f4

Browse files
committed
Add findFont function for Bangle.js 2, use in showPrompt/showMessage
Note: this breaks Bangle.js 1 E.showMessage for now (until we have findFonts)
1 parent 5c1988e commit b84c0f4

File tree

13 files changed

+1524
-70
lines changed

13 files changed

+1524
-70
lines changed

boards/BANGLEJS2.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
'DEFINES += -DESPR_NO_LINE_NUMBERS=1', # we execute mainly from flash, so line numbers can be worked out
7070
'INCLUDE += -I$(ROOT)/libs/banglejs -I$(ROOT)/libs/misc',
7171
'WRAPPERSOURCES += libs/banglejs/jswrap_bangle.c',
72+
'WRAPPERSOURCES += libs/banglejs/jswrap_font_15.c',
73+
'WRAPPERSOURCES += libs/banglejs/jswrap_font_19.c',
74+
'WRAPPERSOURCES += libs/banglejs/jswrap_font_22.c',
7275
'WRAPPERSOURCES += libs/graphics/jswrap_font_6x15.c',
7376
'WRAPPERSOURCES += libs/graphics/jswrap_font_12x20.c',
7477
'SOURCES += libs/misc/nmea.c',

libs/banglejs/jswrap_font_15.c

Lines changed: 337 additions & 0 deletions
Large diffs are not rendered by default.

libs/banglejs/jswrap_font_15.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
3+
*
4+
* Copyright (C) 2023 Gordon Williams <gw@pur3.co.uk>
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*
10+
* ----------------------------------------------------------------------------
11+
* Generated by EspruinoWebTools/cli/fontconverter.js /home/gw/workspace/EspruinoCode/fonts/renaissance/files/renaissance_18_bold.pbff --range All --height 15 --shiftUp 7 --nudge --debug --spaceWidth 3 --opbfc 15
12+
*
13+
* Contains Custom Fonts
14+
* ----------------------------------------------------------------------------
15+
*/
16+
17+
#include "jsvar.h"
18+
19+
JsVar *jswrap_graphics_setFont15(JsVar *parent, int scale);

libs/banglejs/jswrap_font_19.c

Lines changed: 404 additions & 0 deletions
Large diffs are not rendered by default.

libs/banglejs/jswrap_font_19.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
3+
*
4+
* Copyright (C) 2023 Gordon Williams <gw@pur3.co.uk>
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*
10+
* ----------------------------------------------------------------------------
11+
* Generated by EspruinoWebTools/cli/fontconverter.js /home/gw/workspace/EspruinoCode/fonts/renaissance/files/renaissance_24_bold.pbff --range All --height 19 --shiftUp 8 --nudge --debug --spaceWidth 3 --opbfc 19
12+
*
13+
* Contains Custom Fonts
14+
* ----------------------------------------------------------------------------
15+
*/
16+
17+
#include "jsvar.h"
18+
19+
JsVar *jswrap_graphics_setFont19(JsVar *parent, int scale);

libs/banglejs/jswrap_font_22.c

Lines changed: 507 additions & 0 deletions
Large diffs are not rendered by default.

libs/banglejs/jswrap_font_22.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
3+
*
4+
* Copyright (C) 2023 Gordon Williams <gw@pur3.co.uk>
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*
10+
* ----------------------------------------------------------------------------
11+
* Generated by EspruinoWebTools/cli/fontconverter.js /home/gw/workspace/EspruinoCode/fonts/renaissance/files/renaissance_28.pbff --range All --height 22 --shiftUp 10 --nudge --debug --spaceWidth 4 --opbfc 22
12+
*
13+
* Contains Custom Fonts
14+
* ----------------------------------------------------------------------------
15+
*/
16+
17+
#include "jsvar.h"
18+
19+
JsVar *jswrap_graphics_setFont22(JsVar *parent, int scale);

libs/graphics/jswrap_graphics.c

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
#include "jswrap_graphics.h"
1717
#include "jswrap_math.h" // for jswrap_math_cos/sin
18+
#include "jswrap_string.h" // for jswrap_string_split
19+
#include "jswrap_array.h" // for jswrap_array_join
1820
#include "jsutils.h"
1921
#include "jsinteractive.h"
2022

@@ -50,7 +52,11 @@
5052
#ifdef ESPR_LINE_FONTS
5153
#include "line_font.h"
5254
#endif
53-
55+
#ifdef BANGLEJS2
56+
#include "jswrap_font_15.h"
57+
#include "jswrap_font_19.h"
58+
#include "jswrap_font_22.h"
59+
#endif
5460

5561
#ifdef GRAPHICS_PALETTED_IMAGES
5662
#if defined(ESPR_GRAPHICS_12BIT)
@@ -2297,7 +2303,7 @@ int jswrap_graphics_getFontHeight(JsVar *parent) {
22972303

22982304
typedef struct {
22992305
int stringWidth; // width in pixels
2300-
int stringHeight; // height in pixels
2306+
int stringHeight, lineHeight; // height in pixels
23012307
bool unrenderableChars; // are any chars in this not renderable in the current font?
23022308
#ifndef SAVE_ON_FLASH
23032309
int imageCount; // how many inline images are in this string?
@@ -2308,11 +2314,13 @@ typedef struct {
23082314
/** Work out the width and height of a bit of text. If 'lineStartIndex' is -1 the whole string is used
23092315
* otherwise *just* the line of text starting at that char index is used */
23102316
void _jswrap_graphics_stringMetrics(JsGraphics *gfx, JsVar *var, int lineStartIndex, StringMetricsResult *result) {
2317+
assert(result);
23112318
JsGraphicsFontInfo info;
23122319
_jswrap_graphics_getFontInfo(gfx, &info);
23132320
memset(result, 0, sizeof(StringMetricsResult));
23142321

23152322
int fontHeight = _jswrap_graphics_getFontHeightInternal(gfx, &info);
2323+
result->lineHeight = fontHeight;
23162324
JsVar *str = jsvAsString(var);
23172325
JsvStringIterator it;
23182326
jsvStringIteratorNewUTF8(&it, str, (size_t)((lineStartIndex<0)?0:lineStartIndex));
@@ -2585,6 +2593,148 @@ JsVar *jswrap_graphics_wrapString(JsVar *parent, JsVar *str, int maxWidth) {
25852593
return lines;
25862594
}
25872595

2596+
2597+
/*JSON{
2598+
"type" : "method",
2599+
"class" : "Graphics",
2600+
"name" : "findFont",
2601+
"ifdef" : "BANGLEJS2",
2602+
"generate" : "jswrap_graphics_findFont",
2603+
"params" : [
2604+
["text","JsVar","The text to render"],
2605+
["options","JsVar","Options for finding the required font"]
2606+
],
2607+
"return" : ["JsVar","An object containing info about the font"]
2608+
}
2609+
Works out which font to use, and sets the current font to it.
2610+
2611+
Usage:
2612+
2613+
```
2614+
g.findFont("Hello World", {
2615+
w : 100, // optional: width available (default = screen width)
2616+
h : 100, // optional: height available (default = screen height)
2617+
min : 10, // optional: min font height
2618+
max : 30, // optional: max font height
2619+
wrap : true // optional: allow word wrap?
2620+
trim : true // optional: trim to the specified height, add '...'
2621+
});
2622+
```
2623+
2624+
Returns:
2625+
2626+
```
2627+
{
2628+
text : "Hello\nWorld"
2629+
font : "..."
2630+
}
2631+
```
2632+
*/
2633+
2634+
2635+
#ifdef BANGLEJS2
2636+
typedef struct {
2637+
const char *name;
2638+
uint8_t height;
2639+
JsVar*(*setFont)(JsVar *parent, int scale);
2640+
} JswFindFontFont;
2641+
2642+
JsVar *jswrap_graphics_setFont6x8(JsVar *parent, int scale) {
2643+
return jswrap_graphics_setFontSizeX(parent, 1+JSGRAPHICS_FONTSIZE_6X8, false);
2644+
}
2645+
JsVar *jswrap_graphics_setFont4x6(JsVar *parent, int scale) {
2646+
return jswrap_graphics_setFontSizeX(parent, 1+JSGRAPHICS_FONTSIZE_4X6, false);
2647+
}
2648+
2649+
2650+
JsVar *jswrap_graphics_findFont(JsVar *parent, JsVar *text, JsVar *options) {
2651+
if (!jsvIsString(text)) return 0;
2652+
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0;
2653+
int width = gfx.data.width, height = gfx.data.height;
2654+
int minHeight = 4, maxHeight = 100;
2655+
bool wrap = false, trim = false;
2656+
JsVar *result = jsvNewObject();
2657+
if (!result) return 0;
2658+
2659+
jsvConfigObject configs[] = {
2660+
{"w", JSV_INTEGER, &width},
2661+
{"h", JSV_INTEGER, &height},
2662+
{"min", JSV_INTEGER, &minHeight},
2663+
{"max", JSV_INTEGER, &maxHeight},
2664+
{"wrap", JSV_BOOLEAN, &wrap},
2665+
{"trim", JSV_BOOLEAN, &trim},
2666+
};
2667+
if (!jsvReadConfigObject(options, configs, sizeof(configs) / sizeof(jsvConfigObject))) {
2668+
return 0;
2669+
}
2670+
2671+
const int FONTS = 5;
2672+
JswFindFontFont FONT[5] = {
2673+
{"22", 22, jswrap_graphics_setFont22},
2674+
{"19", 19, jswrap_graphics_setFont19},
2675+
{"15", 15, jswrap_graphics_setFont15},
2676+
{"6x8", 8, jswrap_graphics_setFont6x8},
2677+
{"4x6", 6, jswrap_graphics_setFont4x6}
2678+
};
2679+
int fontIdx = 0;
2680+
// check max font size
2681+
while (fontIdx<FONTS-1 && FONT[fontIdx].height>maxHeight)
2682+
fontIdx++;
2683+
// Run through fonts, big->small, to find one that fits
2684+
StringMetricsResult stringMetrics;
2685+
JsVar *finalText = jsvLockAgain(text);
2686+
JsVar *finalLines = NULL;
2687+
JsVar *newline = jsvNewFromString("\n");
2688+
while (fontIdx<FONTS-1) {
2689+
jsvUnLock(FONT[fontIdx].setFont(parent,1));
2690+
graphicsGetFromVar(&gfx, parent);
2691+
if (wrap) {
2692+
jsvUnLock2(finalText, finalLines);
2693+
finalLines= jswrap_graphics_wrapString(parent, text, width);
2694+
finalText = jsvArrayJoin(finalLines,newline,true);
2695+
_jswrap_graphics_stringMetrics(&gfx, finalText, -1, &stringMetrics);
2696+
} else
2697+
_jswrap_graphics_stringMetrics(&gfx, text, -1, &stringMetrics);
2698+
if (((stringMetrics.stringWidth <= width) && (stringMetrics.stringHeight <= height)) || // all good!
2699+
fontIdx==FONTS-1 || // no more fonts
2700+
FONT[fontIdx+1].height<minHeight // next font is too small
2701+
) break;
2702+
fontIdx++;
2703+
}
2704+
const char *fontName = FONT[fontIdx].name;
2705+
// if there were unrenderable characters, use the international font instead if we have one
2706+
if (stringMetrics.unrenderableChars) {
2707+
JsVar *intlFont = jspGetNamedField(parent, "setFontIntl", false);
2708+
if (intlFont) {
2709+
fontName = "Intl";
2710+
jsvUnLock(jspExecuteFunction(intlFont, parent, 0, NULL));
2711+
graphicsGetFromVar(&gfx, parent);
2712+
_jswrap_graphics_stringMetrics(&gfx, text, -1, &stringMetrics);
2713+
}
2714+
}
2715+
if (trim && stringMetrics.stringHeight > height) { // do we have to trim these lines to length?
2716+
JsVar *lines = jsvNewFromInteger(height / stringMetrics.lineHeight);
2717+
if (!finalLines)
2718+
finalLines = jswrap_string_split(finalText, newline);
2719+
JsVar *croppedArr = jswrap_array_slice(finalLines, 0, lines);
2720+
jsvUnLock2(finalText, lines);
2721+
finalText = jsvArrayJoin(croppedArr,newline,true);
2722+
jsvUnLock(croppedArr);
2723+
jsvAppendString(finalText, "..."); // Add ... to the end (TODO: check if room?)
2724+
_jswrap_graphics_stringMetrics(&gfx, finalText, -1, &stringMetrics); // work out string size again
2725+
}
2726+
// TODO: trim width if not wrapping?
2727+
jsvUnLock2(newline, finalLines);
2728+
jsvObjectSetChildAndUnLock(result, "text", finalText);
2729+
jsvObjectSetChildAndUnLock(result, "font", jsvNewFromString(fontName));
2730+
jsvObjectSetChildAndUnLock(result, "w", jsvNewFromInteger(stringMetrics.stringWidth));
2731+
jsvObjectSetChildAndUnLock(result, "h", jsvNewFromInteger(stringMetrics.stringHeight));
2732+
2733+
return result;
2734+
}
2735+
#endif
2736+
2737+
25882738
/*JSON{
25892739
"type" : "method",
25902740
"class" : "Graphics",

libs/graphics/jswrap_graphics.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ JsVar *jswrap_graphics_setClipRect(JsVar *parent, int x1, int y1, int x2, int y2
5858
JsVar *jswrap_graphics_setFontSizeX(JsVar *parent, int size, bool isVectorFont);
5959
JsVar *jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar, JsVar *width, int height);
6060
JsVar *jswrap_graphics_setFontPBF(JsVar *parent, JsVar *file, int scale);
61+
JsVar *jswrap_graphics_findFont(JsVar *parent, JsVar *text, JsVar *options);
6162
JsVar *jswrap_graphics_setFontAlign(JsVar *parent, int x, int y, int r);
6263
JsVar *jswrap_graphics_setFont(JsVar *parent, JsVar *name, int size);
6364
JsVar *jswrap_graphics_getFont(JsVar *parent);

libs/js/banglejs/E_showMessage.js

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
(function(msg,options) {
1+
(function(message,options) {
22
if ("string" == typeof options)
33
options = { title : options };
44
options = options||{};
5-
g.reset().clearRect(Bangle.appRect); // clear screen
6-
g.setFont("6x8",(g.getWidth()>128)?2:1).setFontAlign(0,-1);
7-
var Y = Bangle.appRect.y;
8-
var W = g.getWidth(), H = g.getHeight()-Y, FH=g.getFontHeight();
9-
var titleLines = g.wrapString(options.title, W-2);
10-
var msgLines = g.wrapString(msg||"", W-2);
11-
var y = Y + (H + (titleLines.length - msgLines.length)*FH )/2;
5+
var R = Bangle.appRect, Y = R.y, W = R.w;
6+
g.reset().clearRect(R).setFontAlign(0,0); // clear screen
7+
var title = g.findFont(options.title||"", {w:W-2,wrap:1,max:24});
8+
if (title.text)
9+
g.setColor(g.theme.fgH).setBgColor(g.theme.bgH).
10+
clearRect(0,Y,W-1,Y+4+title.h).
11+
drawString(title.text,W/2,Y+4+title.h/2);
12+
Y += title.h+4;
13+
var H = R.y2-Y;
1214
if (options.img) {
1315
var im = g.imageMetrics(options.img);
14-
g.drawImage(options.img,(W-im.width)/2,y - im.height/2);
15-
y += 4+im.height/2;
16+
g.drawImage(options.img,(W-im.width)/2, Y + 6);
17+
H -= im.height;
18+
Y += im.height;
1619
}
17-
g.drawString(msgLines.join("\n"),W/2,y);
18-
if (options.title)
19-
g.setColor(g.theme.fgH).setBgColor(g.theme.bgH).
20-
clearRect(0,Y,W-1,Y+4+titleLines.length*FH).
21-
drawString(titleLines.join("\n"),W/2,Y+2);
20+
var msg = g.findFont(message, {w:W-2,h:H,wrap:1,trim:1,min:16});
21+
g.setColor(g.theme.fg).setBgColor(g.theme.bg).
22+
drawString(msg.text,W/2,Y+H/2);
2223
g.flip(); // force immediate show of message
23-
Bangle.setLCDPower(1); // ensure screen is on
24+
Bangle.setLCDPower(1); // ensure screen is on
2425
})

0 commit comments

Comments
 (0)