Label Widget¶
The Label widget draws text — a caption, a value readout, a status
banner. Unlike most widgets it has no default geometry (init() only zeroes
the ext, so set_area() is mandatory), and set_style() sets the string,
font, colour, alignment and an optional filled background in one call.
bg_fill = false (the default) draws only glyphs, blending over whatever is
already on screen — ideal for a caption over an image or coloured panel;
bg_fill = true clears the widget rectangle first, so the label reads as a
self-contained chip on any background. ugui_label_set_text() swaps the
string pointer and invalidates the widget for live values, and — because hit
tests don't care about widget type — a label becomes fully clickable with
the same set_event_cb() call a button uses.
Every example below is complete — the file-scope storage, the callbacks and
the build calls — beside the exact frame it renders on a 480×320 panel. Each
compiles against nothing but the released library: drop it into the
Application Skeleton and it runs as-is. The icons come from
inc/icons/, the lexend fonts from inc/fonts/ — both ship with the library.
Examples¶
Default Label¶
The three calls that turn nothing into visible text: init() links the ext, set_area() gives it a rectangle (labels have no default geometry), and set_style() sets the string/font/colour with a transparent background so only the glyphs are drawn.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_DEMO = 10
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_lbl;
static ugui_label_ext_t s_lbl_ext;
#endif
/* --- build it (in your screen's init) --- */
ugui_widget_label_init(&s_root, &s_lbl, &s_lbl_ext); // Needed
ugui_widget_set_area(&s_lbl, 10, 100,
(int16_t)(UGUI_SCREEN_W - (2 * 10)), 28); // Needed
ugui_widget_set_id(&s_lbl, WDGT_ID_DEMO);
ugui_widget_label_set_style(&s_lbl,
/*bg_fill=*/false, UGUI_THEME_BG,
"Hello uGui", &lexend_14pt_2bpp,
UGUI_THEME_TEXT, UGUI_ALIGN_CENTER,
/*use_widget_area=*/1u); // Needed
ugui_screen_add_widget(&s_screen, &s_lbl); // Needed
Alignment¶
Three full-width labels that differ ONLY in their ugui_align_t: LEFT|V_CENTER, CENTER, RIGHT|V_CENTER. Each gets a faint filled background so its rectangle is visible — that is what makes the alignment obvious: the same-width box, the text pinned to a different edge.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_LEFT = 10, WDGT_ID_CENTER, WDGT_ID_RIGHT
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_left;
static ugui_label_ext_t s_left_ext;
static ugui_widget_t s_center;
static ugui_label_ext_t s_center_ext;
static ugui_widget_t s_right;
static ugui_label_ext_t s_right_ext;
#define AL_X 10
#define AL_W ((int16_t)(UGUI_SCREEN_W - (2 * 10)))
#define AL_H 34
#define AL_ROW0_Y 54
#define AL_ROW_STEP 52
#endif
/* --- build it (in your screen's init) --- */
ugui_widget_label_init(&s_root, &s_left, &s_left_ext);
ugui_widget_set_area(&s_left, AL_X, AL_ROW0_Y + 0 * AL_ROW_STEP, AL_W, AL_H);
ugui_widget_set_id(&s_left, WDGT_ID_LEFT);
ugui_widget_label_set_style(&s_left, /*bg_fill=*/true, UGUI_THEME_SURFACE,
"Left aligned", &lexend_14pt_2bpp, UGUI_THEME_TEXT,
(ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER),
1u);
ugui_screen_add_widget(&s_screen, &s_left);
ugui_widget_label_init(&s_root, &s_center, &s_center_ext);
ugui_widget_set_area(&s_center, AL_X, AL_ROW0_Y + 1 * AL_ROW_STEP, AL_W, AL_H);
ugui_widget_set_id(&s_center, WDGT_ID_CENTER);
ugui_widget_label_set_style(&s_center, /*bg_fill=*/true, UGUI_THEME_SURFACE,
"Center aligned", &lexend_14pt_2bpp, UGUI_THEME_TEXT,
UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_center);
ugui_widget_label_init(&s_root, &s_right, &s_right_ext);
ugui_widget_set_area(&s_right, AL_X, AL_ROW0_Y + 2 * AL_ROW_STEP, AL_W, AL_H);
ugui_widget_set_id(&s_right, WDGT_ID_RIGHT);
ugui_widget_label_set_style(&s_right, /*bg_fill=*/true, UGUI_THEME_SURFACE,
"Right aligned", &lexend_14pt_2bpp, UGUI_THEME_TEXT,
(ugui_align_t)(UGUI_ALIGN_RIGHT | UGUI_ALIGN_V_CENTER),
1u);
ugui_screen_add_widget(&s_screen, &s_right);
Transparent vs Opaque¶
The one property that separates a text overlay from a text box: bg_fill = false draws only glyphs over whatever is already there; bg_fill = true clears the rectangle to bg_color first. Both labels sit over the same busy accent panel so the difference is obvious.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_TRANSPARENT = 10, WDGT_ID_OPAQUE
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_transp;
static ugui_label_ext_t s_transp_ext;
static ugui_widget_t s_opaque;
static ugui_label_ext_t s_opaque_ext;
#endif
/* --- build it (in your screen's init) --- */
ugui_widget_label_init(&s_root, &s_transp, &s_transp_ext);
ugui_widget_set_area(&s_transp, 10 + 12, 70,
(int16_t)(UGUI_SCREEN_W - (2 * 10) - 24), 30);
ugui_widget_set_id(&s_transp, WDGT_ID_TRANSPARENT);
ugui_widget_label_set_style(&s_transp, /*bg_fill=*/false, UGUI_THEME_BG,
"Transparent (no fill)", &lexend_14pt_2bpp,
UGUI_THEME_KNOB, UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_transp);
/* Opaque label: bg_fill = true — a solid bg_color chip over the panel. */
ugui_widget_label_init(&s_root, &s_opaque, &s_opaque_ext);
ugui_widget_set_area(&s_opaque, 10 + 12, 128,
(int16_t)(UGUI_SCREEN_W - (2 * 10) - 24), 34);
ugui_widget_set_id(&s_opaque, WDGT_ID_OPAQUE);
ugui_widget_label_set_style(&s_opaque, /*bg_fill=*/true, UGUI_THEME_BG,
"Opaque bg_fill", &lexend_14pt_2bpp,
UGUI_THEME_TEXT, UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_opaque);
Semantic Colours¶
Six transparent labels, each a different colour drawn from the library's semantic roles: the theme accent, then OK / caution / alert / info / disabled. On an embedded status screen these are how a glance conveys state without any icon.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_SLOT0 = 10
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
#define COL_ROWS 6u
static ugui_widget_t s_lbl[COL_ROWS];
static ugui_label_ext_t s_lbl_ext[COL_ROWS];
#define COL_X 24
#define COL_W ((int16_t)(UGUI_SCREEN_W - 48))
#define COL_H 26
#define COL_ROW0_Y 46
#define COL_ROW_STEP 30
#endif
/* --- build it (in your screen's init) --- */
const struct { const char* text; ugui_color_t color; } rows[COL_ROWS] = {
{ "Accent (theme)", UGUI_THEME_ACCENT },
{ "OK / nominal", UGUI_COLOR_GREEN },
{ "Caution", UGUI_COLOR_AMBER },
{ "Alert / fault", UGUI_COLOR_RED },
{ "Info", UGUI_COLOR_CYAN },
{ "Disabled", UGUI_COLOR_GRAY },
};
for (uint8_t i = 0u; i < (uint8_t)COL_ROWS; i++) {
ugui_widget_label_init(&s_root, &s_lbl[i], &s_lbl_ext[i]);
ugui_widget_set_area(&s_lbl[i], COL_X,
(int16_t)(COL_ROW0_Y + (i * COL_ROW_STEP)),
COL_W, COL_H);
ugui_widget_set_id(&s_lbl[i], (uint8_t)(WDGT_ID_SLOT0 + i));
ugui_widget_label_set_style(&s_lbl[i], /*bg_fill=*/false, UGUI_THEME_BG,
rows[i].text, &lexend_14pt_2bpp, rows[i].color,
(ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER),
1u);
ugui_screen_add_widget(&s_screen, &s_lbl[i]);
}
Live Update (tap to change text)¶
A label is display-only, but its text is not frozen: ugui_label_set_text() swaps the string pointer and invalidates the widget, so the next frame redraws it. IMPORTANT: the call stores the POINTER, not a copy — the strings it is handed must outlive the widget, so a live value needs a static buffer, never a stack local.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_DEMO = 10, WDGT_ID_COUNT, WDGT_ID_NEXT_BTN
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_msg;
static ugui_label_ext_t s_msg_ext;
static ugui_widget_t s_count;
static ugui_label_ext_t s_count_ext;
static ugui_widget_t s_next_btn;
static ugui_button_ext_t s_next_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_text_cfg_t s_next_text;
#endif
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static char s_count_buf[24];
static uint32_t s_updates;
static const char* const s_msgs[] = {
"Tap to update ->",
"Text updated! [1]",
"New string [2]",
"Label is live [3]",
};
#define MSG_COUNT ((uint8_t)(sizeof(s_msgs) / sizeof(s_msgs[0])))
#endif
/* --- local helpers & event callbacks --- */
static void on_next(uint8_t widget_id, ugui_event_t event)
{
(void)widget_id;
if (event != UGUI_EVENT_CLICK) { return; }
s_updates++;
/* Swap the message label to the next string (pointer swap, no copy). */
ugui_label_set_text(&s_msg, s_msgs[s_updates % (uint32_t)MSG_COUNT]);
/* Rewrite the counter into its static buffer, then point the label at it. */
(void)snprintf(s_count_buf, sizeof(s_count_buf), "Updates: %lu",
(unsigned long)s_updates);
ugui_label_set_text(&s_count, s_count_buf);
printf("[label.dynamic] update #%lu\n", (unsigned long)s_updates);
}
static void add_button(ugui_widget_t* w, ugui_button_ext_t* ext, ugui_text_cfg_t* text,
uint8_t id, int16_t x, int16_t y, int16_t bw, int16_t bh,
const char* str, ugui_event_cb_t cb)
{
ugui_widget_button_init(&s_root, w, ext);
ugui_widget_set_area(w, x, y, bw, bh);
ugui_widget_set_id(w, id);
ugui_widget_button_set_style(w, true, 5u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(text, str, &lexend_14pt_2bpp, UGUI_THEME_TEXT,
UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_button_set_text(w, text);
#endif
ugui_widget_set_event_cb(w, cb,
UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP
| UGUI_EMASK_KEY_CONFIRM);
ugui_screen_add_widget(&s_screen, w);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, w);
#endif
}
/* --- build it (in your screen's init) --- */
ugui_widget_label_init(&s_root, &s_msg, &s_msg_ext);
ugui_widget_set_area(&s_msg, 20, 84, (int16_t)(UGUI_SCREEN_W - 40), 34);
ugui_widget_set_id(&s_msg, WDGT_ID_DEMO);
ugui_widget_label_set_style(&s_msg, /*bg_fill=*/true, UGUI_THEME_SURFACE,
s_msgs[0], &lexend_14pt_2bpp, UGUI_THEME_ACCENT,
UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_msg);
/* The running-count label. */
ugui_widget_label_init(&s_root, &s_count, &s_count_ext);
ugui_widget_set_area(&s_count, 20, 128, (int16_t)(UGUI_SCREEN_W - 40), 24);
ugui_widget_set_id(&s_count, WDGT_ID_COUNT);
ugui_widget_label_set_style(&s_count, false, UGUI_THEME_BG,
s_count_buf, &lexend_14pt_2bpp, UGUI_THEME_TEXT,
UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_count);
/* The driver button (a plain button widget, not the label under test). */
add_button(&s_next_btn, &s_next_ext, &s_next_text, WDGT_ID_NEXT_BTN,
40, 170, (int16_t)(UGUI_SCREEN_W - 80), 34,
"Next text", on_next);
Device Status Panel¶
Nothing new about a single label — this shows how the alignment/background/colour primitives combine into a genuine UI: a card holds a filled heading banner, five key/value rows (keys neutral + left-aligned, values right-aligned and coloured by their semantic state), and a filled footer banner.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_SLOT0 = 10, WDGT_ID_SLOT1, WDGT_ID_DEMO,
WDGT_ID_SLOT2
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_card;
static ugui_box_ext_t s_card_ext;
static ugui_widget_t s_head;
static ugui_label_ext_t s_head_ext;
static ugui_widget_t s_foot;
static ugui_label_ext_t s_foot_ext;
#define PANEL_ROWS 5u
static ugui_widget_t s_key[PANEL_ROWS];
static ugui_label_ext_t s_key_ext[PANEL_ROWS];
static ugui_widget_t s_val[PANEL_ROWS];
static ugui_label_ext_t s_val_ext[PANEL_ROWS];
#define CARD_X 10
#define CARD_Y 42
#define CARD_W ((int16_t)(UGUI_SCREEN_W - (2 * 10)))
#define CARD_H 188
#define ROW0_Y 86
#define ROW_STEP 23
#define ROW_H 20
#endif
/* --- build it (in your screen's init) --- */
/* Row data — value colours resolve at runtime, so the table is local. */
const struct { const char* key; const char* val; ugui_color_t color; }
rows[PANEL_ROWS] = {
{ "CPU load", "42 %", UGUI_COLOR_GREEN },
{ "Memory", "78 %", UGUI_COLOR_AMBER },
{ "Core temp", "58 C", UGUI_COLOR_GREEN },
{ "Uplink", "OK", UGUI_COLOR_GREEN },
{ "Battery", "17 %", UGUI_COLOR_RED },
};
/* Card background — the surface all the labels sit on. */
ugui_widget_box_init(&s_root, &s_card, &s_card_ext);
ugui_widget_set_area(&s_card, CARD_X, CARD_Y, CARD_W, CARD_H);
ugui_widget_set_id(&s_card, WDGT_ID_SLOT0);
ugui_widget_box_set_style(&s_card, true, 6u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
ugui_screen_add_widget(&s_screen, &s_card);
/* Heading banner — opaque accent bar with white text. */
ugui_widget_label_init(&s_root, &s_head, &s_head_ext);
ugui_widget_set_area(&s_head, CARD_X + 8, CARD_Y + 8, (int16_t)(CARD_W - 16), 26);
ugui_widget_set_id(&s_head, WDGT_ID_SLOT1);
ugui_widget_label_set_style(&s_head, /*bg_fill=*/true, UGUI_THEME_ACCENT,
"System Status", &lexend_14pt_2bpp, UGUI_THEME_KNOB,
UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_head);
/* Key/value rows — transparent labels over the card. */
for (uint8_t i = 0u; i < (uint8_t)PANEL_ROWS; i++) {
const int16_t y = (int16_t)(ROW0_Y + (i * ROW_STEP));
ugui_widget_label_init(&s_root, &s_key[i], &s_key_ext[i]);
ugui_widget_set_area(&s_key[i], CARD_X + 16, y, 150, ROW_H);
ugui_widget_set_id(&s_key[i], WDGT_ID_DEMO);
ugui_widget_label_set_style(&s_key[i], false, UGUI_THEME_SURFACE,
rows[i].key, &lexend_14pt_2bpp, UGUI_THEME_TEXT,
(ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER),
1u);
ugui_screen_add_widget(&s_screen, &s_key[i]);
ugui_widget_label_init(&s_root, &s_val[i], &s_val_ext[i]);
ugui_widget_set_area(&s_val[i], (int16_t)(CARD_X + CARD_W - 16 - 126), y,
126, ROW_H);
ugui_widget_set_id(&s_val[i], WDGT_ID_DEMO);
ugui_widget_label_set_style(&s_val[i], false, UGUI_THEME_SURFACE,
rows[i].val, &lexend_14pt_2bpp, rows[i].color,
(ugui_align_t)(UGUI_ALIGN_RIGHT | UGUI_ALIGN_V_CENTER),
1u);
ugui_screen_add_widget(&s_screen, &s_val[i]);
}
/* Footer banner — opaque caution bar summarising the rows. */
ugui_widget_label_init(&s_root, &s_foot, &s_foot_ext);
ugui_widget_set_area(&s_foot, CARD_X + 8, (int16_t)(CARD_Y + CARD_H - 30),
(int16_t)(CARD_W - 16), 22);
ugui_widget_set_id(&s_foot, WDGT_ID_SLOT2);
ugui_widget_label_set_style(&s_foot, /*bg_fill=*/true, UGUI_COLOR_AMBER,
"1 alert | 1 caution", &lexend_14pt_2bpp,
UGUI_COLOR_BLACK, UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_foot);
Clickable Label¶
A label is display-only by default, but hit-testing doesn't care about widget type — the SAME set_event_cb() call a button uses makes the whole label rectangle (not just the glyphs) a hit target. What it does NOT get for free is a button's press animation, so this screen supplies its own feedback: each tap swaps the fill/text/string via set_style().

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_DEMO = 10, WDGT_ID_COUNT
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_click;
static ugui_label_ext_t s_click_ext;
static ugui_widget_t s_count;
static ugui_label_ext_t s_count_ext;
static char s_count_buf[24];
static uint32_t s_taps;
static bool s_on;
#endif
/* --- local helpers & event callbacks --- */
static void apply_state(void)
{
if (s_on) {
ugui_widget_label_set_style(&s_click, /*bg_fill=*/true, UGUI_COLOR_GREEN,
"Tapped! (ON)", &lexend_14pt_2bpp,
UGUI_COLOR_BLACK, UGUI_ALIGN_CENTER, 1u);
} else {
ugui_widget_label_set_style(&s_click, /*bg_fill=*/true, UGUI_THEME_SURFACE,
"Tap me (OFF)", &lexend_14pt_2bpp,
UGUI_THEME_TEXT, UGUI_ALIGN_CENTER, 1u);
}
}
static void on_label_click(uint8_t widget_id, ugui_event_t event)
{
(void)widget_id;
if (event != UGUI_EVENT_CLICK) { return; }
s_on = !s_on;
s_taps++;
apply_state();
(void)snprintf(s_count_buf, sizeof(s_count_buf), "Taps: %lu",
(unsigned long)s_taps);
ugui_label_set_text(&s_count, s_count_buf);
printf("[label.clickable] tap #%lu -> %s\n",
(unsigned long)s_taps, s_on ? "ON" : "OFF");
}
/* --- build it (in your screen's init) --- */
ugui_widget_label_init(&s_root, &s_click, &s_click_ext);
ugui_widget_set_area(&s_click, 40, 90, (int16_t)(UGUI_SCREEN_W - 80), 40);
ugui_widget_set_id(&s_click, WDGT_ID_DEMO);
apply_state(); // seed the OFF style
ugui_widget_set_event_cb(&s_click, on_label_click,
UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP
| UGUI_EMASK_KEY_CONFIRM); // <-- makes it clickable
ugui_screen_add_widget(&s_screen, &s_click);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_click);
#endif
/* Tap counter — updated by on_label_click() above on every tap. */
ugui_widget_label_init(&s_root, &s_count, &s_count_ext);
ugui_widget_set_area(&s_count, 10, 148,
(int16_t)(UGUI_SCREEN_W - (2 * 10)), 24);
ugui_widget_set_id(&s_count, WDGT_ID_COUNT);
ugui_widget_label_set_style(&s_count, false, UGUI_THEME_BG,
s_count_buf, &lexend_14pt_2bpp, UGUI_THEME_TEXT,
UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_count);
From this widget to a working app
Everything above is the widget-specific code. The scaffolding it drops into — storage, HAL, main loop, display-mode bring-up — is the same for every app: copy it from the Application Skeleton.