feat(ui-lvgl): even status icons, relay count badge, clearer path popup, edge scrollbars

- Status bar icons come from ui_icons_14: FontAwesome refitted to one
  size by fonts/status_icons.py (falls back to ui_font_12 for the
  battery level text), the charging bolt in a tight cell.
- Own channel posts show the repeater count as a small badge, as L1,
  instead of a check and a number.
- Message path popup: a stronger connector, roles on the right
  (sender / hop N / you), our post's repeaters fanned out under us,
  long paths scroll.
- Scrollbars everywhere are thin and hug the edge, clear of the content.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-26 22:47:23 +02:00
co-authored by Claude Opus 5.5
parent 7a34f68369
commit 6f141a62f2
7 changed files with 518 additions and 52 deletions
@@ -228,32 +228,47 @@ void UITask::conversationAction(uint8_t act) {
// ── Message actions ─────────────────────────────────────────────────────────── // ── Message actions ───────────────────────────────────────────────────────────
// One stop on the path diagram: a dot on a vertical line (the line runs on to // One stop on the path diagram: a dot on a vertical line (the line runs on to
// the next stop unless `last`), the name beside it. // the next stop unless `last`), the name beside it, what it is on the right.
static void pathStop(lv_obj_t* parent, const char* name, bool first, bool last, uint32_t col) { static void pathStop(lv_obj_t* parent, const char* name, const char* role, bool first, bool last,
uint32_t col, bool end) {
lv_obj_t* row = lv_obj_create(parent); lv_obj_t* row = lv_obj_create(parent);
lv_obj_remove_style_all(row); lv_obj_remove_style_all(row);
lv_obj_remove_flag(row, LV_OBJ_FLAG_CLICKABLE); lv_obj_remove_flag(row, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(row, LV_PCT(100), 20); lv_obj_set_size(row, LV_PCT(100), 22);
if (!first || !last) { if (!first || !last) {
lv_obj_t* line = lv_obj_create(row); lv_obj_t* line = lv_obj_create(row);
lv_obj_remove_style_all(line); lv_obj_remove_style_all(line);
lv_obj_set_style_bg_opa(line, LV_OPA_COVER, 0); lv_obj_set_style_bg_color(line, lv_color_hex(theme::TEXT_MUTED), 0);
lv_obj_set_style_bg_color(line, lv_color_hex(theme::SURFACE_2), 0); lv_obj_set_style_bg_opa(line, LV_OPA_50, 0);
lv_obj_set_size(line, 2, first || last ? 10 : 20); lv_obj_set_size(line, 2, first || last ? 11 : 22);
lv_obj_set_pos(line, 6, first ? 10 : 0); lv_obj_set_pos(line, 6, first ? 11 : 0);
} }
lv_obj_t* dot = lv_obj_create(row); lv_obj_t* dot = lv_obj_create(row);
lv_obj_remove_style_all(dot); lv_obj_remove_style_all(dot);
lv_obj_set_style_bg_opa(dot, LV_OPA_COVER, 0); lv_obj_set_style_bg_opa(dot, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(dot, lv_color_hex(col), 0); lv_obj_set_style_bg_color(dot, lv_color_hex(end ? col : theme::SURFACE), 0);
lv_obj_set_style_border_color(dot, lv_color_hex(col), 0);
lv_obj_set_style_border_width(dot, end ? 0 : 2, 0);
lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, 0); lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, 0);
bool end = first || last;
lv_obj_set_size(dot, end ? 10 : 8, end ? 10 : 8); lv_obj_set_size(dot, end ? 10 : 8, end ? 10 : 8);
lv_obj_align(dot, LV_ALIGN_LEFT_MID, end ? 2 : 3, 0); lv_obj_align(dot, LV_ALIGN_LEFT_MID, end ? 2 : 3, 0);
lv_obj_t* t = label(row, name, end ? THEME_FONT_BODY : THEME_FONT_SMALL, end ? theme::TEXT : theme::TEXT_MUTED); lv_obj_t* t = label(row, name, end ? THEME_FONT_BODY : THEME_FONT_SMALL, theme::TEXT);
lv_label_set_long_mode(t, LV_LABEL_LONG_DOT); lv_label_set_long_mode(t, LV_LABEL_LONG_DOT);
lv_obj_set_width(t, 240); lv_obj_set_width(t, 190);
lv_obj_align(t, LV_ALIGN_LEFT_MID, 22, 0); lv_obj_align(t, LV_ALIGN_LEFT_MID, 22, 0);
if (role && role[0]) lv_obj_align(label(row, role, THEME_FONT_SMALL, theme::TEXT_MUTED), LV_ALIGN_RIGHT_MID, 0, 0);
}
// The stops of a path, in a box that scrolls once there are many (up to 64 hops).
static lv_obj_t* pathDiagram(lv_obj_t* panel) {
lv_obj_t* diag = lv_obj_create(panel);
lv_obj_remove_style_all(diag);
lv_obj_set_size(diag, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_max_height(diag, 5 * 22, 0);
lv_obj_set_style_pad_right(diag, 6, 0); // the roles clear this box's own scrollbar
lv_obj_set_flex_flow(diag, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scroll_dir(diag, LV_DIR_VER);
return diag;
} }
void UITask::messageMenu(int idx) { void UITask::messageMenu(int idx) {
@@ -330,29 +345,29 @@ void UITask::messageMenu(int idx) {
} }
label(panel, st, THEME_FONT_BODY, col); label(panel, st, THEME_FONT_BODY, col);
} else if (relay) { // our channel post: the repeaters heard passing it on } else if (relay) { // our channel post: the repeaters heard passing it on
char hd[40]; char hd[48];
if (hops > 0) snprintf(hd, sizeof(hd), LV_SYMBOL_OK " Relayed by %u repeater%s", hops, hops == 1 ? "" : "s"); if (hops > 0) snprintf(hd, sizeof(hd), "Heard %u repeater%s pass it on", hops, hops == 1 ? "" : "s");
else snprintf(hd, sizeof(hd), "No repeater heard relaying it yet"); else snprintf(hd, sizeof(hd), "No repeater heard passing it on yet");
label(panel, hd, THEME_FONT_BODY, hops > 0 ? theme::OK : theme::TEXT_MUTED); label(panel, hd, THEME_FONT_BODY, hops > 0 ? theme::OK : theme::TEXT_MUTED);
for (uint8_t i = 0; i < hops; i++) { if (hops > 0) { // each one an echo of ours: a fan out from us, not a chain
char nm[33], line[40]; lv_obj_t* diag = pathDiagram(panel);
contactctl::hopName(packed, path, i, nm, sizeof(nm)); pathStop(diag, the_mesh.getNodeName(), "you", true, false, theme::ACCENT, true);
snprintf(line, sizeof(line), UI_SYMBOL_RADIO " %s", nm); for (uint8_t i = 0; i < hops; i++) {
label(panel, line, THEME_FONT_SMALL, theme::TEXT_MUTED); char nm[33];
contactctl::hopName(packed, path, i, nm, sizeof(nm));
pathStop(diag, nm, UI_SYMBOL_RADIO, false, i == hops - 1, theme::OK, false);
}
} }
} else { // incoming: sender, each hop, then us } else { // incoming: sender, each hop, then us
lv_obj_t* diag = lv_obj_create(panel); lv_obj_t* diag = pathDiagram(panel);
lv_obj_remove_style_all(diag); pathStop(diag, m.from[0] ? m.from : "Sender", "sender", true, false, theme::ACCENT, true);
lv_obj_remove_flag(diag, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(diag, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(diag, LV_FLEX_FLOW_COLUMN);
pathStop(diag, m.from[0] ? m.from : "Sender", true, false, theme::ACCENT);
for (uint8_t i = 0; i < hops; i++) { for (uint8_t i = 0; i < hops; i++) {
char nm[33]; char nm[33], role[12];
contactctl::hopName(packed, path, i, nm, sizeof(nm)); contactctl::hopName(packed, path, i, nm, sizeof(nm));
pathStop(diag, nm, false, false, theme::TEXT_MUTED); snprintf(role, sizeof(role), "hop %u", (unsigned)(i + 1));
pathStop(diag, nm, role, false, false, theme::TEXT_MUTED, false);
} }
pathStop(diag, the_mesh.getNodeName(), false, true, theme::OK); pathStop(diag, the_mesh.getNodeName(), "you", false, true, theme::OK, true);
} }
bool can_reply = !m.own && m.from[0] && _compose_ta; bool can_reply = !m.own && m.from[0] && _compose_ta;
+14 -2
View File
@@ -39,6 +39,9 @@ namespace theme {
#define THEME_FONT_TITLE (&ui_font_16) #define THEME_FONT_TITLE (&ui_font_16)
#define THEME_FONT_LARGE (&ui_font_20) #define THEME_FONT_LARGE (&ui_font_20)
#define THEME_FONT_CLOCK (&ui_font_40) #define THEME_FONT_CLOCK (&ui_font_40)
// Status bar icons, refitted to one size (fonts/status_icons.py); any other
// character falls back to ui_font_12.
#define THEME_FONT_ICONS (&ui_icons_14)
// Extra FontAwesome glyphs baked into ui_font_12..20 beyond LVGL's LV_SYMBOL_*. // Extra FontAwesome glyphs baked into ui_font_12..20 beyond LVGL's LV_SYMBOL_*.
#define UI_SYMBOL_STAR "\xEF\x80\x85" // U+F005, favourite #define UI_SYMBOL_STAR "\xEF\x80\x85" // U+F005, favourite
@@ -78,11 +81,14 @@ namespace theme {
// Over LVGL's default theme, for every widget: a checked button is // Over LVGL's default theme, for every widget: a checked button is
// "selected" (ACCENT_DIM, not the default theme's full accent), buttons // "selected" (ACCENT_DIM, not the default theme's full accent), buttons
// have the card radius and give a little under the finger (the default // have the card radius and give a little under the finger (the default
// theme's transitions animate it both ways). // theme's transitions animate it both ways). Scrollbars are thin and hug
// the edge, inside the padding every panel and list has, instead of the
// default's 5 px bar 7 px in, which ran over right-aligned text.
static lv_theme_t s_theme; static lv_theme_t s_theme;
static lv_style_t s_btn, s_press, s_checked; static lv_style_t s_btn, s_press, s_checked, s_sbar;
static void applyTheme(lv_theme_t* th, lv_obj_t* obj) { static void applyTheme(lv_theme_t* th, lv_obj_t* obj) {
(void)th; (void)th;
lv_obj_add_style(obj, &s_sbar, LV_PART_SCROLLBAR);
if (lv_obj_check_type(obj, &lv_button_class)) { if (lv_obj_check_type(obj, &lv_button_class)) {
lv_obj_add_style(obj, &s_btn, 0); lv_obj_add_style(obj, &s_btn, 0);
lv_obj_add_style(obj, &s_press, LV_STATE_PRESSED); lv_obj_add_style(obj, &s_press, LV_STATE_PRESSED);
@@ -102,6 +108,12 @@ namespace theme {
lv_style_set_transform_height(&s_press, -2); lv_style_set_transform_height(&s_press, -2);
lv_style_init(&s_checked); lv_style_init(&s_checked);
lv_style_set_text_color(&s_checked, lv_color_hex(TEXT)); lv_style_set_text_color(&s_checked, lv_color_hex(TEXT));
lv_style_init(&s_sbar);
lv_style_set_width(&s_sbar, 3);
lv_style_set_pad_right(&s_sbar, 2); // vertical bar: 2 px off the edge
lv_style_set_pad_bottom(&s_sbar, 2); // horizontal bar likewise
lv_style_set_pad_top(&s_sbar, RADIUS / 2);
lv_style_set_pad_left(&s_sbar, RADIUS / 2);
} }
lv_style_set_bg_color(&s_checked, lv_color_hex(ACCENT_DIM)); lv_style_set_bg_color(&s_checked, lv_color_hex(ACCENT_DIM));
lv_theme_t* base = lv_theme_default_init(disp, lv_color_hex(ACCENT), lv_color_hex(ACCENT_DIM), true, THEME_FONT_BODY); lv_theme_t* base = lv_theme_default_init(disp, lv_color_hex(ACCENT), lv_color_hex(ACCENT_DIM), true, THEME_FONT_BODY);
+33 -20
View File
@@ -567,7 +567,7 @@ void UITask::buildStatusBar() {
_status_time = label(bar, "--:--", THEME_FONT_SMALL, theme::TEXT); _status_time = label(bar, "--:--", THEME_FONT_SMALL, theme::TEXT);
lv_obj_align(_status_time, LV_ALIGN_LEFT_MID, 0, 0); lv_obj_align(_status_time, LV_ALIGN_LEFT_MID, 0, 0);
_status_batt = label(bar, "", THEME_FONT_SMALL, theme::TEXT_MUTED); _status_batt = label(bar, "", THEME_FONT_ICONS, theme::TEXT_MUTED);
lv_obj_align(_status_batt, LV_ALIGN_RIGHT_MID, 0, 0); lv_obj_align(_status_batt, LV_ALIGN_RIGHT_MID, 0, 0);
_status_icons = lv_obj_create(bar); _status_icons = lv_obj_create(bar);
lv_obj_remove_style_all(_status_icons); lv_obj_remove_style_all(_status_icons);
@@ -575,7 +575,7 @@ void UITask::buildStatusBar() {
lv_obj_set_size(_status_icons, 200, LV_PCT(100)); // fixed: packed to its right edge lv_obj_set_size(_status_icons, 200, LV_PCT(100)); // fixed: packed to its right edge
lv_obj_set_flex_flow(_status_icons, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(_status_icons, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(_status_icons, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_flex_align(_status_icons, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(_status_icons, 3, 0); lv_obj_set_style_pad_column(_status_icons, 2, 0);
_toast = lv_obj_create(lv_layer_top()); _toast = lv_obj_create(lv_layer_top());
lv_obj_remove_flag(_toast, LV_OBJ_FLAG_SCROLLABLE); lv_obj_remove_flag(_toast, LV_OBJ_FLAG_SCROLLABLE);
@@ -619,7 +619,7 @@ void UITask::refreshStatusBar() {
case battery::VOLTAGE: snprintf(level, sizeof(level), " %u.%02u V", mv / 1000, (mv % 1000) / 10); break; case battery::VOLTAGE: snprintf(level, sizeof(level), " %u.%02u V", mv / 1000, (mv % 1000) / 10); break;
default: break; default: break;
} }
lv_label_set_text_fmt(_status_batt, "%s%s%s", _board->isExternalPowered() ? LV_SYMBOL_CHARGE " " : "", batt, level); lv_label_set_text_fmt(_status_batt, "%s%s%s", _board->isExternalPowered() ? LV_SYMBOL_CHARGE : "", batt, level);
// Status icons, as the original's status bar (ui-new): Bluetooth (bright // Status icons, as the original's status bar (ui-new): Bluetooth (bright
// when the app is connected), GPS (green with a fix), the alarm, mute, then // when the app is connected), GPS (green with a fix), the alarm, mute, then
@@ -649,13 +649,10 @@ void UITask::refreshStatusBar() {
if (strcmp(sig, _status_sig) != 0) { // rebuilt only when something changed if (strcmp(sig, _status_sig) != 0) { // rebuilt only when something changed
strcpy(_status_sig, sig); strcpy(_status_sig, sig);
lv_obj_clean(_status_icons); lv_obj_clean(_status_icons);
for (int i = n - 1; i >= 0; i--) { // one cell each: glyphs vary in width, the rhythm doesn't for (int i = n - 1; i >= 0; i--) // the icon font gives each the same size and cell
lv_obj_t* l = label(_status_icons, icons[i].sym, THEME_FONT_SMALL, icons[i].col); label(_status_icons, icons[i].sym, THEME_FONT_ICONS, icons[i].col);
lv_obj_set_width(l, 16);
lv_obj_set_style_text_align(l, LV_TEXT_ALIGN_CENTER, 0);
}
} }
lv_obj_align_to(_status_icons, _status_batt, LV_ALIGN_OUT_LEFT_MID, -8, 0); lv_obj_align_to(_status_icons, _status_batt, LV_ALIGN_OUT_LEFT_MID, -5, 0);
} }
void UITask::setGps(bool on) { void UITask::setGps(bool on) {
@@ -1964,7 +1961,8 @@ static void msgLocButton(lv_obj_t* parent, const char* text, int idx, bool save,
// One message bubble. Own messages right-aligned in amber, others left. // One message bubble. Own messages right-aligned in amber, others left.
// loc_idx >= 0: the text carries a position (s_msg_locs[loc_idx]). // loc_idx >= 0: the text carries a position (s_msg_locs[loc_idx]).
static void bubble(lv_obj_t* list, const char* from, const char* text, bool own, static void bubble(lv_obj_t* list, const char* from, const char* text, bool own,
uint32_t ts, const char* status, uint32_t status_col, int loc_idx = -1, int meta_idx = -1) { uint32_t ts, const char* status, uint32_t status_col, int loc_idx = -1, int meta_idx = -1,
int relays = 0) {
// Full-width row that pushes the bubble to its side. // Full-width row that pushes the bubble to its side.
lv_obj_t* row = lv_obj_create(list); lv_obj_t* row = lv_obj_create(list);
styleSurface(row, theme::BG); styleSurface(row, theme::BG);
@@ -2031,15 +2029,31 @@ static void bubble(lv_obj_t* list, const char* from, const char* text, bool own,
msgLocButton(acts, UI_SYMBOL_FLAG " Save", loc_idx, true, false); msgLocButton(acts, UI_SYMBOL_FLAG " Save", loc_idx, true, false);
} }
if (!meta_in_header) { // the age, then the delivery mark in its colour (as L1: ✓ 3) if (!meta_in_header) { // the age, then the delivery mark in its colour (as L1)
lv_obj_t* line = lv_obj_create(b); lv_obj_t* line = lv_obj_create(b);
lv_obj_remove_style_all(line); lv_obj_remove_style_all(line);
lv_obj_remove_flag(line, LV_OBJ_FLAG_CLICKABLE); lv_obj_remove_flag(line, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(line, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(line, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(line, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(line, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(line, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(line, 6, 0); lv_obj_set_style_pad_column(line, 6, 0);
label(line, meta, THEME_FONT_SMALL, theme::TEXT_MUTED); label(line, meta, THEME_FONT_SMALL, theme::TEXT_MUTED);
if (status && status[0]) label(line, status, THEME_FONT_SMALL, status_col); if (relays > 0) { // as L1: the count alone says it got out, no check beside it
lv_obj_t* c = lv_obj_create(line);
lv_obj_remove_style_all(c);
lv_obj_remove_flag(c, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(c, LV_SIZE_CONTENT, 15);
lv_obj_set_style_min_width(c, 15, 0);
lv_obj_set_style_pad_hor(c, 4, 0);
lv_obj_set_style_bg_opa(c, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(c, lv_color_hex(theme::OK), 0);
lv_obj_set_style_radius(c, LV_RADIUS_CIRCLE, 0);
lv_obj_t* n = label(c, "", THEME_FONT_SMALL, theme::BG);
lv_label_set_text_fmt(n, "%d", relays);
lv_obj_center(n);
} else if (status && status[0]) {
label(line, status, THEME_FONT_SMALL, status_col);
}
} }
} }
@@ -2161,19 +2175,18 @@ void UITask::refreshThread() {
body = sep + 2; body = sep + 2;
} }
bool own = strcmp(from, "Me") == 0; bool own = strcmp(from, "Me") == 0;
// Own posts: a check once a repeater echoed it, with how many distinct // Own posts: once a repeater echoed it, how many distinct repeaters did
// repeaters did (markChannelRelayed); nothing before -- no echo is normal. // (markChannelRelayed), or a check if that's unknown; nothing before --
// no echo is normal.
const char* st = NULL; uint32_t col = theme::TEXT_MUTED; const char* st = NULL; uint32_t col = theme::TEXT_MUTED;
char relays[16]; int nrel = 0;
if (own && e.relay_status == ACK_OK) { if (own && e.relay_status == ACK_OK) {
int nrel = e.path_len & 63; nrel = e.path_len & 63;
if (nrel > 0) snprintf(relays, sizeof(relays), LV_SYMBOL_OK " %d", nrel); st = LV_SYMBOL_OK; col = theme::OK;
else snprintf(relays, sizeof(relays), LV_SYMBOL_OK);
st = relays; col = theme::OK;
} else if (own) st = ""; } else if (own) st = "";
int loc = own ? -1 : noteMsgLocation(body, from); int loc = own ? -1 : noteMsgLocation(body, from);
bubble(_thread_list, own ? NULL : from, body, own, e.timestamp, st, col, bubble(_thread_list, own ? NULL : from, body, own, e.timestamp, st, col,
loc, noteMsgMeta(i, true, own, loc, own ? "" : from)); loc, noteMsgMeta(i, true, own, loc, own ? "" : from), nrel);
} }
} else { } else {
ContactInfo tc; ContactInfo tc;
@@ -45,4 +45,13 @@ for s in 12 14 16 20; do gen "$s" Medium "$TEXT" "ui_font_$s"; done
# Clock: digits, colon, dash, dot, space only. # Clock: digits, colon, dash, dot, space only.
$CONV --bpp 4 --size 40 --no-compress --font "$CACHE/NotoSans-SemiBold.ttf" -r "0x20,0x2D-0x2E,0x30-0x3A" \ $CONV --bpp 4 --size 40 --no-compress --font "$CACHE/NotoSans-SemiBold.ttf" -r "0x20,0x2D-0x2E,0x30-0x3A" \
--format lvgl --lv-include lvgl.h --lv-font-name ui_font_40 -o "$HERE/ui_font_40.c" --format lvgl --lv-include lvgl.h --lv-font-name ui_font_40 -o "$HERE/ui_font_40.c"
echo "done: $(ls "$HERE"/ui_font_*.c | wc -l) fonts" # Status bar icons: FontAwesome refitted to one size (status_icons.py, needs
# python3 with fonttools). Line metrics forced to ui_font_12's and text falls
# back to it, so "<battery> 85%" is one label.
ICONS="0xF024,0xF079,0xF0E7,0xF0F3,0xF124,0xF1EB,0xF240-0xF244,0xF293,0xF3C5,0xF4D7,0xF519,0xF6A9"
python3 "$HERE/status_icons.py" "$CACHE/fa.woff" "$CACHE/ui-icons.ttf"
$CONV --bpp 4 --size 14 --no-compress --font "$CACHE/ui-icons.ttf" -r "$ICONS" \
--format lvgl --lv-include lvgl.h --lv-font-name ui_icons_14 -o "$HERE/ui_icons_14.c"
sed -i.bak -e 's/\.line_height = [0-9]*,/.line_height = 18,/' -e 's/\.base_line = [0-9]*,/.base_line = 4,/' \
-e 's/\.fallback = NULL,/.fallback = \&ui_font_12,/' "$HERE/ui_icons_14.c" && rm "$HERE/ui_icons_14.c.bak"
echo "done:$(ls "$HERE"/ui_font_*.c | wc -l) fonts"
@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""Builds the status bar's icon font source: FontAwesome glyphs refitted so
every icon reads the same size.
FontAwesome draws each icon on its own canvas -- a battery is wide and flat,
a map pin narrow and tall, a mute sign short -- so at one font size they come
out anywhere from 9 to 16 px wide and 9 to 13 px tall. Here every glyph is
scaled to fit one box (BOX_H tall, at most BOX_W wide; WIDE icons get more
width), centred on the middle of the Noto digits, in a cell of ADVANCE (wide
icons a little more).
Line metrics match ui_font_12 (line height 18, baseline 4), so the digits of
"85%" can share a label with the icons (the font falls back to ui_font_12).
Usage: status_icons.py <FontAwesome .woff/.ttf> <out.ttf> (needs fonttools)
Called by generate.sh, which turns the result into ui_icons_14.c.
"""
import math
import sys
from fontTools.ttLib import TTFont
from fontTools.fontBuilder import FontBuilder
from fontTools.pens.recordingPen import DecomposingRecordingPen
from fontTools.pens.transformPen import TransformPen
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.pens.boundsPen import BoundsPen
SIZE = 14.0 # px per em, as generate.sh renders it
UPM = 1024
PX = UPM / SIZE # font units per pixel
BOX_H = 11.0 # every icon this tall...
BOX_W = 13.0 # ...unless that makes it wider than this
WIDE_W = 17.0 # battery-shaped icons may be this wide
ADVANCE = 16.0 # one cell per icon
MID_Y = 4.5 # icon centre, px above the baseline (middle of the digits)
ASCENT, DESCENT = 14.0, 4.0 # line height 18, baseline 4 = ui_font_12
ICONS = [ # codepoint, wide (True: wider box, None: tight cell)
(0xF293, False), # bluetooth
(0xF124, False), # location arrow (GPS)
(0xF0F3, False), # bell (alarm)
(0xF6A9, False), # volume mute
(0xF519, False), # broadcast tower (auto-advert)
(0xF4D7, False), # route (trail)
(0xF3C5, False), # map marker (live share)
(0xF079, True), # retweet (repeater)
(0xF024, False), # flag (arrival alert)
(0xF0E7, None), # bolt (charging): a tight cell, it leads the battery
(0xF1EB, True), # wifi
(0xF240, True), (0xF241, True), (0xF242, True), (0xF243, True), (0xF244, True), # battery
]
def main(src, out):
fa = TTFont(src)
gs = fa.getGlyphSet()
cmap = fa.getBestCmap()
order = [".notdef"]
glyphs, metrics, cmap_out = {}, {}, {}
pen = TTGlyphPen(None)
glyphs[".notdef"] = pen.glyph()
metrics[".notdef"] = (int(ADVANCE * PX), 0)
for cp, wide in ICONS:
name = cmap[cp]
rec = DecomposingRecordingPen(gs)
gs[name].draw(rec)
bp = BoundsPen(gs)
rec.replay(bp)
x0, y0, x1, y1 = bp.bounds
w, h = x1 - x0, y1 - y0
s = min(BOX_H * PX / h, (WIDE_W if wide else BOX_W) * PX / w)
adv = math.ceil(w * s / PX) + 2 # 1 px clear each side
if wide is not None: adv = max(ADVANCE, adv) # one cell for all; wide icons a wider one
dx = (adv * PX - w * s) / 2 - x0 * s
dy = MID_Y * PX - (y0 + h / 2) * s
pen = TTGlyphPen(None)
rec.replay(TransformPen(pen, (s, 0, 0, s, dx, dy)))
g = pen.glyph()
g.recalcBounds(None)
glyphs[name] = g
metrics[name] = (int(adv * PX), g.xMin) # left side bearing = xMin, or the centring is lost
order.append(name)
cmap_out[cp] = name
fb = FontBuilder(UPM, isTTF=True)
fb.setupGlyphOrder(order)
fb.setupCharacterMap(cmap_out)
fb.setupGlyf(glyphs)
fb.setupHorizontalMetrics(metrics)
asc, desc = round(ASCENT * PX), round(DESCENT * PX)
fb.setupHorizontalHeader(ascent=asc, descent=-desc)
fb.setupOS2(sTypoAscender=asc, sTypoDescender=-desc, sTypoLineGap=0,
usWinAscent=asc, usWinDescent=desc)
fb.setupNameTable({"familyName": "UiIcons", "styleName": "Regular"})
fb.setupPost()
fb.save(out)
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])
@@ -0,0 +1,315 @@
/*******************************************************************************
* Size: 14 px
* Bpp: 4
* Opts: --bpp 4 --size 14 --no-compress --font icons.ttf -r 0xF024,0xF079,0xF0E7,0xF0F3,0xF124,0xF1EB,0xF240-0xF244,0xF293,0xF3C5,0xF4D7,0xF519,0xF6A9 --format lvgl --lv-include lvgl.h --lv-font-name ui_icons_14 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_icons_14.c
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl.h"
#endif
#ifndef UI_ICONS_14
#define UI_ICONS_14 1
#endif
#if UI_ICONS_14
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+F024 "" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0xa0,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf8, 0x97, 0x20,
0x3, 0x82, 0xe, 0xff, 0xff, 0xfe, 0xff, 0xf5,
0xe, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xe, 0xff,
0xff, 0xff, 0xff, 0xf6, 0xe, 0xff, 0xff, 0xff,
0xff, 0xf6, 0xe, 0xff, 0xff, 0xff, 0xff, 0xf6,
0xe, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xe, 0xa2,
0x1, 0x59, 0xb8, 0x20, 0xe, 0x70, 0x0, 0x0,
0x0, 0x0, 0xd, 0x50, 0x0, 0x0, 0x0, 0x0,
/* U+F079 "" */
0x0, 0x5, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x5, 0xff, 0xb0, 0x1c, 0xcc, 0xcc,
0xcc, 0x50, 0x0, 0x5, 0xff, 0xff, 0xb1, 0xcf,
0xff, 0xff, 0xf9, 0x0, 0x0, 0xfe, 0xcf, 0xbf,
0x70, 0x0, 0x0, 0x1f, 0x90, 0x0, 0x5, 0x2a,
0xf1, 0x61, 0x0, 0x0, 0x1, 0xf9, 0x0, 0x0,
0x0, 0xaf, 0x10, 0x0, 0x0, 0x0, 0x1f, 0x90,
0x0, 0x0, 0xa, 0xf1, 0x0, 0x0, 0x1, 0x61,
0xf9, 0x35, 0x0, 0x0, 0xaf, 0x10, 0x0, 0x0,
0x7f, 0xbf, 0xce, 0xf0, 0x0, 0xa, 0xff, 0xff,
0xff, 0xb1, 0xbf, 0xff, 0xf4, 0x0, 0x0, 0x5c,
0xcc, 0xcc, 0xcb, 0x10, 0xbf, 0xf4, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x94, 0x0,
0x0,
/* U+F0E7 "" */
0x3f, 0xff, 0xc0, 0x0, 0x6f, 0xff, 0x90, 0x0,
0x8f, 0xff, 0x40, 0x0, 0xaf, 0xff, 0x99, 0x70,
0xcf, 0xff, 0xff, 0xb0, 0xef, 0xff, 0xff, 0x20,
0x23, 0x3f, 0xf8, 0x0, 0x0, 0x3f, 0xe0, 0x0,
0x0, 0x6f, 0x60, 0x0, 0x0, 0xac, 0x0, 0x0,
0x0, 0xc3, 0x0, 0x0,
/* U+F0F3 "" */
0x0, 0x0, 0x88, 0x0, 0x0, 0x0, 0x8, 0xff,
0x90, 0x0, 0x0, 0xcf, 0xff, 0xfc, 0x0, 0x4,
0xff, 0xff, 0xff, 0x40, 0x7, 0xff, 0xff, 0xff,
0x70, 0x9, 0xff, 0xff, 0xff, 0x90, 0xc, 0xff,
0xff, 0xff, 0xc0, 0x5f, 0xff, 0xff, 0xff, 0xf6,
0xaf, 0xff, 0xff, 0xff, 0xf9, 0x0, 0x1, 0x66,
0x20, 0x0, 0x0, 0x1, 0xdd, 0x10, 0x0,
/* U+F124 "" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3a, 0xe3, 0x0, 0x0, 0x0, 0x4b,
0xff, 0xf5, 0x0, 0x0, 0x5d, 0xff, 0xff, 0xe0,
0x0, 0x7e, 0xff, 0xff, 0xff, 0x70, 0x3e, 0xff,
0xff, 0xff, 0xff, 0x0, 0x6f, 0xff, 0xff, 0xff,
0xf8, 0x0, 0x2, 0x33, 0x3b, 0xff, 0xf1, 0x0,
0x0, 0x0, 0xb, 0xff, 0xa0, 0x0, 0x0, 0x0,
0xb, 0xff, 0x20, 0x0, 0x0, 0x0, 0xa, 0xfb,
0x0, 0x0, 0x0, 0x0, 0x6, 0xe3, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
/* U+F1EB "" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x6b, 0xdf, 0xfd, 0xb6, 0x10, 0x0,
0x0, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x0,
0x3e, 0xff, 0xa5, 0x10, 0x1, 0x5a, 0xff, 0xe3,
0xaf, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x2c, 0xfa,
0x6, 0x0, 0x3a, 0xef, 0xfe, 0xa3, 0x0, 0x60,
0x0, 0x8, 0xff, 0xfe, 0xef, 0xff, 0x80, 0x0,
0x0, 0xc, 0xf8, 0x10, 0x1, 0x8f, 0xc0, 0x0,
0x0, 0x0, 0x20, 0x0, 0x0, 0x2, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3f, 0xf3, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x8f, 0xf8, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2d, 0xd2, 0x0, 0x0, 0x0,
/* U+F240 "" */
0x6b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7,
0xf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf2, 0xfb, 0x13, 0x33, 0x33, 0x33, 0x33, 0x31,
0x8f, 0xef, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff,
0x95, 0xef, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xf9, 0xb, 0xff, 0xb7, 0xff, 0xff, 0xff, 0xff,
0xff, 0x95, 0xef, 0xfb, 0x13, 0x33, 0x33, 0x33,
0x33, 0x31, 0x8f, 0xef, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf2, 0x6b, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xb8, 0x0,
/* U+F241 "" */
0x6b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7,
0xf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf2, 0xfb, 0x13, 0x33, 0x33, 0x33, 0x30, 0x0,
0x8f, 0xef, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x0,
0x5, 0xef, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xf0,
0x0, 0xb, 0xff, 0xb7, 0xff, 0xff, 0xff, 0xff,
0x0, 0x5, 0xef, 0xfb, 0x13, 0x33, 0x33, 0x33,
0x30, 0x0, 0x8f, 0xef, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf2, 0x6b, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xb8, 0x0,
/* U+F242 "" */
0x6b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7,
0xf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf2, 0xfb, 0x13, 0x33, 0x33, 0x10, 0x0, 0x0,
0x8f, 0xef, 0xb7, 0xff, 0xff, 0xf8, 0x0, 0x0,
0x5, 0xef, 0xfb, 0x7f, 0xff, 0xff, 0x80, 0x0,
0x0, 0xb, 0xff, 0xb7, 0xff, 0xff, 0xf8, 0x0,
0x0, 0x5, 0xef, 0xfb, 0x13, 0x33, 0x33, 0x10,
0x0, 0x0, 0x8f, 0xef, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf2, 0x6b, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xb8, 0x0,
/* U+F243 "" */
0x6b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7,
0xf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf2, 0xfb, 0x13, 0x32, 0x0, 0x0, 0x0, 0x0,
0x8f, 0xef, 0xb7, 0xff, 0xf0, 0x0, 0x0, 0x0,
0x5, 0xef, 0xfb, 0x7f, 0xff, 0x0, 0x0, 0x0,
0x0, 0xb, 0xff, 0xb7, 0xff, 0xf0, 0x0, 0x0,
0x0, 0x5, 0xef, 0xfb, 0x13, 0x32, 0x0, 0x0,
0x0, 0x0, 0x8f, 0xef, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf2, 0x6b, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xb8, 0x0,
/* U+F244 "" */
0x6b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb7,
0xf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf2, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x8f, 0xef, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x5, 0xef, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xb, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x5, 0xef, 0xfb, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x8f, 0xef, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf2, 0x6b, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xb8, 0x0,
/* U+F293 "" */
0x0, 0x3b, 0xef, 0xc5, 0x0, 0x2, 0xff, 0xc8,
0xff, 0x40, 0xa, 0xff, 0xc0, 0x9f, 0xc0, 0xe,
0xd5, 0xc6, 0x4c, 0xf0, 0xf, 0xf8, 0x22, 0x4f,
0xf1, 0x1f, 0xff, 0x50, 0xef, 0xf2, 0xf, 0xfa,
0x11, 0x5f, 0xf1, 0xe, 0xd3, 0xb6, 0x4a, 0xf0,
0xa, 0xff, 0xc1, 0x7f, 0xc0, 0x3, 0xff, 0xd7,
0xff, 0x50, 0x0, 0x3b, 0xef, 0xd6, 0x0,
/* U+F3C5 "" */
0x0, 0x2b, 0xff, 0xb2, 0x0, 0x3, 0xff, 0xff,
0xff, 0x30, 0xc, 0xff, 0x88, 0xff, 0xc0, 0x1f,
0xf7, 0x0, 0x7f, 0xf0, 0x1f, 0xf5, 0x0, 0x5f,
0xf1, 0xd, 0xfd, 0x44, 0xef, 0xd0, 0x5, 0xff,
0xff, 0xff, 0x50, 0x0, 0xbf, 0xff, 0xfb, 0x0,
0x0, 0x1e, 0xff, 0xe1, 0x0, 0x0, 0x5, 0xff,
0x50, 0x0, 0x0, 0x0, 0x99, 0x0, 0x0,
/* U+F4D7 "" */
0x0, 0x0, 0x0, 0x1, 0xbf, 0xa0, 0x0, 0x0,
0x0, 0x7, 0xf7, 0xf6, 0x0, 0x0, 0x0, 0x7,
0xe6, 0xf6, 0x0, 0x0, 0x0, 0x1, 0xef, 0xd0,
0x0, 0x0, 0x1, 0xbf, 0xff, 0x30, 0x3, 0x84,
0x8, 0xe7, 0x64, 0x0, 0x3f, 0xff, 0x4a, 0xd2,
0x21, 0x0, 0x7f, 0xd, 0x83, 0xff, 0xff, 0xc0,
0x2f, 0xdf, 0x40, 0x13, 0x45, 0xf6, 0x9, 0xfb,
0x46, 0x66, 0x67, 0xf5, 0x0, 0xb4, 0xff, 0xff,
0xff, 0xa0,
/* U+F519 "" */
0xd, 0x30, 0x0, 0x0, 0x0, 0x3, 0xd0, 0x5f,
0x4, 0x30, 0x33, 0x3, 0x40, 0xf5, 0x7d, 0xd,
0x71, 0xff, 0x37, 0xd0, 0xd7, 0x6e, 0xc, 0x81,
0xff, 0x38, 0xc0, 0xe6, 0x2f, 0x20, 0x6, 0xfe,
0x70, 0x2, 0xf2, 0x4, 0x10, 0xc, 0x97, 0xe0,
0x1, 0x40, 0x0, 0x0, 0x3f, 0x64, 0xf5, 0x0,
0x0, 0x0, 0x0, 0xaf, 0xff, 0xfb, 0x0, 0x0,
0x0, 0x1, 0xf6, 0x11, 0x4f, 0x20, 0x0, 0x0,
0x7, 0xe0, 0x0, 0xd, 0x80, 0x0, 0x0, 0x5,
0x60, 0x0, 0x5, 0x60, 0x0,
/* U+F6A9 "" */
0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0,
0x0, 0xb, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xbf, 0xf0, 0x0, 0x0, 0x0, 0x5e, 0xef, 0xff,
0xf0, 0x1c, 0x32, 0xc2, 0x8f, 0xff, 0xff, 0xf0,
0x2e, 0xfe, 0xf3, 0x8f, 0xff, 0xff, 0xf0, 0x5,
0xff, 0x70, 0x8f, 0xff, 0xff, 0xf0, 0x2e, 0xfe,
0xf3, 0x5f, 0xff, 0xff, 0xf0, 0x1c, 0x43, 0xd2,
0x0, 0x0, 0xbf, 0xf0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xb, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x40, 0x0, 0x0, 0x0
};
/*---------------------
* GLYPH DESCRIPTION
*--------------------*/
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
{.bitmap_index = 0, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = -1},
{.bitmap_index = 72, .adv_w = 304, .box_w = 19, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 177, .adv_w = 144, .box_w = 8, .box_h = 11, .ofs_x = 1, .ofs_y = -1},
{.bitmap_index = 221, .adv_w = 256, .box_w = 10, .box_h = 11, .ofs_x = 3, .ofs_y = -1},
{.bitmap_index = 276, .adv_w = 256, .box_w = 12, .box_h = 13, .ofs_x = 2, .ofs_y = -2},
{.bitmap_index = 354, .adv_w = 288, .box_w = 16, .box_h = 12, .ofs_x = 1, .ofs_y = -1},
{.bitmap_index = 450, .adv_w = 304, .box_w = 17, .box_h = 9, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 527, .adv_w = 304, .box_w = 17, .box_h = 9, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 604, .adv_w = 304, .box_w = 17, .box_h = 9, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 681, .adv_w = 304, .box_w = 17, .box_h = 9, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 758, .adv_w = 304, .box_w = 17, .box_h = 9, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 835, .adv_w = 256, .box_w = 10, .box_h = 11, .ofs_x = 3, .ofs_y = -1},
{.bitmap_index = 890, .adv_w = 256, .box_w = 10, .box_h = 11, .ofs_x = 3, .ofs_y = -1},
{.bitmap_index = 945, .adv_w = 256, .box_w = 12, .box_h = 11, .ofs_x = 2, .ofs_y = -1},
{.bitmap_index = 1011, .adv_w = 256, .box_w = 14, .box_h = 11, .ofs_x = 1, .ofs_y = -1},
{.bitmap_index = 1088, .adv_w = 256, .box_w = 14, .box_h = 11, .ofs_x = 1, .ofs_y = -1}
};
/*---------------------
* CHARACTER MAPPING
*--------------------*/
static const uint16_t unicode_list_0[] = {
0x0, 0x55, 0xc3, 0xcf, 0x100, 0x1c7, 0x21c, 0x21d,
0x21e, 0x21f, 0x220, 0x26f, 0x3a1, 0x4b3, 0x4f5, 0x685
};
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 61476, .range_length = 1670, .glyph_id_start = 1,
.unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 16, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
/*--------------------
* ALL CUSTOM DATA
*--------------------*/
#if LVGL_VERSION_MAJOR == 8
/*Store all the custom data of the font*/
static lv_font_fmt_txt_glyph_cache_t cache;
#endif
#if LVGL_VERSION_MAJOR >= 8
static const lv_font_fmt_txt_dsc_t font_dsc = {
#else
static lv_font_fmt_txt_dsc_t font_dsc = {
#endif
.glyph_bitmap = glyph_bitmap,
.glyph_dsc = glyph_dsc,
.cmaps = cmaps,
.kern_dsc = NULL,
.kern_scale = 0,
.cmap_num = 1,
.bpp = 4,
.kern_classes = 0,
.bitmap_format = 0,
#if LVGL_VERSION_MAJOR == 8
.cache = &cache
#endif
};
/*-----------------
* PUBLIC FONT
*----------------*/
/*Initialize a public general font descriptor*/
#if LVGL_VERSION_MAJOR >= 8
const lv_font_t ui_icons_14 = {
#else
lv_font_t ui_icons_14 = {
#endif
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
.line_height = 18, /*The maximum line height required by the font*/
.base_line = 4, /*Baseline measured from the bottom of the line*/
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
.subpx = LV_FONT_SUBPX_NONE,
#endif
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
.underline_position = 0,
.underline_thickness = 0,
#endif
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
.fallback = &ui_font_12,
#endif
.user_data = NULL,
};
#endif /*#if UI_ICONS_14*/
+2 -1
View File
@@ -31,7 +31,8 @@
#define LV_FONT_MONTSERRAT_14 0 #define LV_FONT_MONTSERRAT_14 0
#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(ui_font_12) LV_FONT_DECLARE(ui_font_14) \ #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(ui_font_12) LV_FONT_DECLARE(ui_font_14) \
LV_FONT_DECLARE(ui_font_16) LV_FONT_DECLARE(ui_font_20) \ LV_FONT_DECLARE(ui_font_16) LV_FONT_DECLARE(ui_font_20) \
LV_FONT_DECLARE(ui_font_40) LV_FONT_DECLARE(ui_font_40) \
LV_FONT_DECLARE(ui_icons_14)
#define LV_FONT_DEFAULT &ui_font_14 #define LV_FONT_DEFAULT &ui_font_14
#define LV_USE_FONT_COMPRESSED 0 // fonts are generated uncompressed: no per-glyph decompression on every draw #define LV_USE_FONT_COMPRESSED 0 // fonts are generated uncompressed: no per-glyph decompression on every draw