diff --git a/examples/companion_radio/ui-lvgl/ConversationScreen.h b/examples/companion_radio/ui-lvgl/ConversationScreen.h index b3d130b0..ce828b4e 100644 --- a/examples/companion_radio/ui-lvgl/ConversationScreen.h +++ b/examples/companion_radio/ui-lvgl/ConversationScreen.h @@ -228,32 +228,47 @@ void UITask::conversationAction(uint8_t act) { // ── Message actions ─────────────────────────────────────────────────────────── // 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. -static void pathStop(lv_obj_t* parent, const char* name, bool first, bool last, uint32_t col) { +// 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, const char* role, bool first, bool last, + uint32_t col, bool end) { lv_obj_t* row = lv_obj_create(parent); lv_obj_remove_style_all(row); 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) { lv_obj_t* line = lv_obj_create(row); 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::SURFACE_2), 0); - lv_obj_set_size(line, 2, first || last ? 10 : 20); - lv_obj_set_pos(line, 6, first ? 10 : 0); + lv_obj_set_style_bg_color(line, lv_color_hex(theme::TEXT_MUTED), 0); + lv_obj_set_style_bg_opa(line, LV_OPA_50, 0); + lv_obj_set_size(line, 2, first || last ? 11 : 22); + lv_obj_set_pos(line, 6, first ? 11 : 0); } lv_obj_t* dot = lv_obj_create(row); lv_obj_remove_style_all(dot); 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); - bool end = first || last; 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_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_obj_set_width(t, 240); + lv_obj_set_width(t, 190); 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) { @@ -330,29 +345,29 @@ void UITask::messageMenu(int idx) { } label(panel, st, THEME_FONT_BODY, col); } else if (relay) { // our channel post: the repeaters heard passing it on - char hd[40]; - if (hops > 0) snprintf(hd, sizeof(hd), LV_SYMBOL_OK " Relayed by %u repeater%s", hops, hops == 1 ? "" : "s"); - else snprintf(hd, sizeof(hd), "No repeater heard relaying it yet"); + char hd[48]; + 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 passing it on yet"); label(panel, hd, THEME_FONT_BODY, hops > 0 ? theme::OK : theme::TEXT_MUTED); - for (uint8_t i = 0; i < hops; i++) { - char nm[33], line[40]; - contactctl::hopName(packed, path, i, nm, sizeof(nm)); - snprintf(line, sizeof(line), UI_SYMBOL_RADIO " %s", nm); - label(panel, line, THEME_FONT_SMALL, theme::TEXT_MUTED); + if (hops > 0) { // each one an echo of ours: a fan out from us, not a chain + lv_obj_t* diag = pathDiagram(panel); + pathStop(diag, the_mesh.getNodeName(), "you", true, false, theme::ACCENT, true); + for (uint8_t i = 0; i < hops; i++) { + 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 - lv_obj_t* diag = lv_obj_create(panel); - lv_obj_remove_style_all(diag); - 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); + lv_obj_t* diag = pathDiagram(panel); + pathStop(diag, m.from[0] ? m.from : "Sender", "sender", true, false, theme::ACCENT, true); for (uint8_t i = 0; i < hops; i++) { - char nm[33]; + char nm[33], role[12]; 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; diff --git a/examples/companion_radio/ui-lvgl/Theme.h b/examples/companion_radio/ui-lvgl/Theme.h index 15e44694..5f6cc671 100644 --- a/examples/companion_radio/ui-lvgl/Theme.h +++ b/examples/companion_radio/ui-lvgl/Theme.h @@ -39,6 +39,9 @@ namespace theme { #define THEME_FONT_TITLE (&ui_font_16) #define THEME_FONT_LARGE (&ui_font_20) #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_*. #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 // "selected" (ACCENT_DIM, not the default theme's full accent), buttons // 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_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) { (void)th; + lv_obj_add_style(obj, &s_sbar, LV_PART_SCROLLBAR); if (lv_obj_check_type(obj, &lv_button_class)) { lv_obj_add_style(obj, &s_btn, 0); 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_init(&s_checked); 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_theme_t* base = lv_theme_default_init(disp, lv_color_hex(ACCENT), lv_color_hex(ACCENT_DIM), true, THEME_FONT_BODY); diff --git a/examples/companion_radio/ui-lvgl/UITask.cpp b/examples/companion_radio/ui-lvgl/UITask.cpp index 35a1e16d..36cb6533 100644 --- a/examples/companion_radio/ui-lvgl/UITask.cpp +++ b/examples/companion_radio/ui-lvgl/UITask.cpp @@ -567,7 +567,7 @@ void UITask::buildStatusBar() { _status_time = label(bar, "--:--", THEME_FONT_SMALL, theme::TEXT); 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); _status_icons = lv_obj_create(bar); 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_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_style_pad_column(_status_icons, 3, 0); + lv_obj_set_style_pad_column(_status_icons, 2, 0); _toast = lv_obj_create(lv_layer_top()); 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; 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 // 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 strcpy(_status_sig, sig); lv_obj_clean(_status_icons); - for (int i = n - 1; i >= 0; i--) { // one cell each: glyphs vary in width, the rhythm doesn't - lv_obj_t* l = label(_status_icons, icons[i].sym, THEME_FONT_SMALL, icons[i].col); - lv_obj_set_width(l, 16); - lv_obj_set_style_text_align(l, LV_TEXT_ALIGN_CENTER, 0); - } + for (int i = n - 1; i >= 0; i--) // the icon font gives each the same size and cell + label(_status_icons, icons[i].sym, THEME_FONT_ICONS, icons[i].col); } - 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) { @@ -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. // 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, - 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. lv_obj_t* row = lv_obj_create(list); 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); } - 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_remove_style_all(line); lv_obj_remove_flag(line, LV_OBJ_FLAG_CLICKABLE); 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_align(line, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_style_pad_column(line, 6, 0); 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; } bool own = strcmp(from, "Me") == 0; - // Own posts: a check once a repeater echoed it, with how many distinct - // repeaters did (markChannelRelayed); nothing before -- no echo is normal. + // Own posts: once a repeater echoed it, how many distinct repeaters did + // (markChannelRelayed), or a check if that's unknown; nothing before -- + // no echo is normal. const char* st = NULL; uint32_t col = theme::TEXT_MUTED; - char relays[16]; + int nrel = 0; if (own && e.relay_status == ACK_OK) { - int nrel = e.path_len & 63; - if (nrel > 0) snprintf(relays, sizeof(relays), LV_SYMBOL_OK " %d", nrel); - else snprintf(relays, sizeof(relays), LV_SYMBOL_OK); - st = relays; col = theme::OK; + nrel = e.path_len & 63; + st = LV_SYMBOL_OK; col = theme::OK; } else if (own) st = ""; int loc = own ? -1 : noteMsgLocation(body, from); 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 { ContactInfo tc; diff --git a/examples/companion_radio/ui-lvgl/fonts/generate.sh b/examples/companion_radio/ui-lvgl/fonts/generate.sh index 3aba1fe6..2dbdd37c 100755 --- a/examples/companion_radio/ui-lvgl/fonts/generate.sh +++ b/examples/companion_radio/ui-lvgl/fonts/generate.sh @@ -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. $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" -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 " 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" diff --git a/examples/companion_radio/ui-lvgl/fonts/status_icons.py b/examples/companion_radio/ui-lvgl/fonts/status_icons.py new file mode 100644 index 00000000..16c646e3 --- /dev/null +++ b/examples/companion_radio/ui-lvgl/fonts/status_icons.py @@ -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 (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]) diff --git a/examples/companion_radio/ui-lvgl/fonts/ui_icons_14.c b/examples/companion_radio/ui-lvgl/fonts/ui_icons_14.c new file mode 100644 index 00000000..838ff8cb --- /dev/null +++ b/examples/companion_radio/ui-lvgl/fonts/ui_icons_14.c @@ -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*/ + diff --git a/examples/companion_radio/ui-lvgl/lv_conf.h b/examples/companion_radio/ui-lvgl/lv_conf.h index 5d9bc064..b1616177 100644 --- a/examples/companion_radio/ui-lvgl/lv_conf.h +++ b/examples/companion_radio/ui-lvgl/lv_conf.h @@ -31,7 +31,8 @@ #define LV_FONT_MONTSERRAT_14 0 #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_40) + LV_FONT_DECLARE(ui_font_40) \ + LV_FONT_DECLARE(ui_icons_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