Image Widget¶
The Image widget draws a compressed flash asset — an icon, glyph or
sprite — the library's answer to "a picture," at a fraction of a raw
bitmap's size (grayscale-4 icons run ≈290 bytes). Init installs a default
TINT (theme ACCENT) and BG (page background) at a settable size;
set_src() points it at a ugui_compressed_image_t asset from
inc/icons/. Because the asset is a shape — a grayscale mask, not fixed
colours — the codec blends every gray sample between bg (gray 0) and
tint (max gray), so set_colors() recolours the SAME asset into an
accent, ok, warning or alert indicator with no extra image data. set_src()
alone re-points a widget at a different asset with no relayout, which is
how an icon becomes STATE (a Wi-Fi signal level, a mic mute glyph) rather
than decoration. Like a label, an image is display-only until given an
event callback — the input layer hit-tests any visible, enabled widget
regardless of type, so a tappable icon needs nothing beyond
set_event_cb().
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 Image: Bare vs set_colors()¶
The three calls that put an icon on screen: ugui_widget_image_init(), set_area(), set_src(). With defaults on, init seeds tint = theme ACCENT and bg = page background, so a bare image (left) already shows an on-theme colour with no colour call at all; the right icon calls set_colors() to force white, overriding the default.

/* --- 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_SLOT1
};
/* --- image assets (inc/icons/ + inc/images/, shipped with the library;
* each header DEFINES its image — include it in ONE .c only) --- */
#ifdef UGUI_WIDGET_ENABLE_IMAGE
#include "icons/gallery_icon_1_grayscale_4.h" /* defines img_gallery_icon_1 */
#endif
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_IMAGE) && 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_img_default;
static ugui_image_ext_t s_img_default_ext;
static ugui_widget_t s_img_white;
static ugui_image_ext_t s_img_white_ext;
#define ICON_SZ 24
#define ICON_Y 96
#define ICON_L_X ((int16_t)((UGUI_SCREEN_W / 2) - 60 - (ICON_SZ / 2)))
#define ICON_R_X ((int16_t)((UGUI_SCREEN_W / 2) + 60 - (ICON_SZ / 2)))
#endif
/* --- build it (in your screen's init) --- */
ugui_widget_image_init(&s_root, &s_img_default, &s_img_default_ext);
ugui_widget_set_area(&s_img_default, ICON_L_X, ICON_Y, ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_img_default, WDGT_ID_DEMO);
ugui_widget_image_set_src(&s_img_default, &img_gallery_icon_1);
ugui_screen_add_widget(&s_screen, &s_img_default);
/* Same icon, but set_colors() overrides the default to white. */
ugui_widget_image_init(&s_root, &s_img_white, &s_img_white_ext);
ugui_widget_set_area(&s_img_white, ICON_R_X, ICON_Y, ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_img_white, WDGT_ID_SLOT1);
ugui_widget_image_set_src(&s_img_white, &img_gallery_icon_1);
ugui_widget_image_set_colors(&s_img_white, UGUI_COLOR_WHITE, UGUI_THEME_BG);
ugui_screen_add_widget(&s_screen, &s_img_white);
Recolouring One Shape via Tint¶
A single grayscale heart icon becomes an accent, ok, warning or alert indicator just by changing set_colors()'s tint argument — the same ≈290-byte asset, no extra image data. Each icon's bg is set to its card's fill so the anti-aliased edge blends into the card instead of fringing.

/* --- 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_DEMO, WDGT_ID_TITLE
};
/* --- image assets (inc/icons/ + inc/images/, shipped with the library;
* each header DEFINES its image — include it in ONE .c only) --- */
#ifdef UGUI_WIDGET_ENABLE_IMAGE
#include "icons/heart_icon_1_fill_grayscale_4.h" /* defines img_heart_icon_1_fill */
#endif
/* --- 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_IMAGE) && defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
#define TINT_N 4u
static ugui_widget_t s_card[TINT_N];
static ugui_box_ext_t s_card_ext[TINT_N];
static ugui_widget_t s_icon[TINT_N];
static ugui_image_ext_t s_icon_ext[TINT_N];
static ugui_widget_t s_lbl[TINT_N];
static ugui_label_ext_t s_lbl_ext[TINT_N];
#define CARD_W 68
#define CARD_H 96
#define CARD_Y 64
#define CARD_GAP 9
#define CARD_X0 10
#define ICON_SZ 24
#endif
/* --- build it (in your screen's init) --- */
const struct { const char* label; ugui_color_t tint; } cols[TINT_N] = {
{ "accent", UGUI_THEME_ACCENT },
{ "ok", UGUI_COLOR_GREEN },
{ "warn", UGUI_COLOR_AMBER },
{ "alert", UGUI_COLOR_RED },
};
for (uint8_t i = 0u; i < (uint8_t)TINT_N; i++) {
const int16_t cx = (int16_t)(CARD_X0 + (i * (CARD_W + CARD_GAP)));
/* Card (also the bg the icon's AA edge blends into). */
ugui_widget_box_init(&s_root, &s_card[i], &s_card_ext[i]);
ugui_widget_set_area(&s_card[i], cx, CARD_Y, CARD_W, CARD_H);
ugui_widget_set_id(&s_card[i], (uint8_t)(WDGT_ID_SLOT0 + i));
ugui_widget_box_set_style(&s_card[i], true, 8u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
ugui_screen_add_widget(&s_screen, &s_card[i]);
/* Icon centred near the top of the card. */
ugui_widget_image_init(&s_root, &s_icon[i], &s_icon_ext[i]);
ugui_widget_set_area(&s_icon[i], (int16_t)(cx + ((CARD_W - ICON_SZ) / 2)),
CARD_Y + 22, ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_icon[i], WDGT_ID_DEMO);
ugui_widget_image_set_src(&s_icon[i], &img_heart_icon_1_fill);
ugui_widget_image_set_colors(&s_icon[i], cols[i].tint, UGUI_THEME_SURFACE);
ugui_screen_add_widget(&s_screen, &s_icon[i]);
/* Caption in the lower part of the card. */
ugui_widget_label_init(&s_root, &s_lbl[i], &s_lbl_ext[i]);
ugui_widget_set_area(&s_lbl[i], cx, CARD_Y + 62, CARD_W, 20);
ugui_widget_set_id(&s_lbl[i], WDGT_ID_TITLE);
ugui_widget_label_set_style(&s_lbl[i], false, UGUI_THEME_SURFACE, cols[i].label,
&lexend_14pt_2bpp, UGUI_THEME_TEXT,
UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_lbl[i]);
}
Icon Gallery / App Launcher Grid¶
Six independent icons laid out as a 3×2 grid of labelled tiles — a phone home screen or a device main menu. The same three-call recipe from the default screen drops straight into a loop over a small table; all six share the accent tint so the grid reads as one on-theme set.

/* --- 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_DEMO, WDGT_ID_TITLE
};
/* --- image assets (inc/icons/ + inc/images/, shipped with the library;
* each header DEFINES its image — include it in ONE .c only) --- */
#ifdef UGUI_WIDGET_ENABLE_IMAGE
#include "icons/camera_icon_1_grayscale_4.h" /* defines img_camera_icon_1 */
#include "icons/clock_icon_1_grayscale_4.h" /* defines img_clock_icon_1 */
#include "icons/gift_icon_1_grayscale_4.h" /* defines img_gift_icon_1 */
#include "icons/music_icon_1_grayscale_4.h" /* defines img_music_icon_1 */
#include "icons/search_icon_1_grayscale_4.h" /* defines img_search_icon_1 */
#include "icons/setting_icon_1_grayscale_4.h" /* defines img_setting_icon_1 */
#endif
/* --- 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_IMAGE) && defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
#define TILE_N 6u
static ugui_widget_t s_card[TILE_N];
static ugui_box_ext_t s_card_ext[TILE_N];
static ugui_widget_t s_icon[TILE_N];
static ugui_image_ext_t s_icon_ext[TILE_N];
static ugui_widget_t s_lbl[TILE_N];
static ugui_label_ext_t s_lbl_ext[TILE_N];
#define GRID_COLS 3
#define GRID_MARGIN 10
#define GRID_GAP 11
#define TILE_W 92
#define TILE_H 88
#define GRID_Y0 46
#define ROW_STEP 98
#define ICON_SZ 24
#endif
/* --- build it (in your screen's init) --- */
const struct { const ugui_compressed_image_t* img; const char* label; }
tiles[TILE_N] = {
{ &img_search_icon_1, "Search" },
{ &img_music_icon_1, "Music" },
{ &img_camera_icon_1, "Camera" },
{ &img_clock_icon_1, "Clock" },
{ &img_setting_icon_1, "Settings" },
{ &img_gift_icon_1, "Gifts" },
};
for (uint8_t i = 0u; i < (uint8_t)TILE_N; i++) {
const uint8_t col = (uint8_t)(i % (uint8_t)GRID_COLS);
const uint8_t row = (uint8_t)(i / (uint8_t)GRID_COLS);
const int16_t x = (int16_t)(GRID_MARGIN + (col * (TILE_W + GRID_GAP)));
const int16_t y = (int16_t)(GRID_Y0 + (row * ROW_STEP));
ugui_widget_box_init(&s_root, &s_card[i], &s_card_ext[i]);
ugui_widget_set_area(&s_card[i], x, y, TILE_W, TILE_H);
ugui_widget_set_id(&s_card[i], (uint8_t)(WDGT_ID_SLOT0 + i));
ugui_widget_box_set_style(&s_card[i], true, 8u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
ugui_screen_add_widget(&s_screen, &s_card[i]);
ugui_widget_image_init(&s_root, &s_icon[i], &s_icon_ext[i]);
ugui_widget_set_area(&s_icon[i], (int16_t)(x + ((TILE_W - ICON_SZ) / 2)),
(int16_t)(y + 16), ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_icon[i], WDGT_ID_DEMO);
ugui_widget_image_set_src(&s_icon[i], tiles[i].img);
ugui_widget_image_set_colors(&s_icon[i], UGUI_THEME_ACCENT, UGUI_THEME_SURFACE);
ugui_screen_add_widget(&s_screen, &s_icon[i]);
ugui_widget_label_init(&s_root, &s_lbl[i], &s_lbl_ext[i]);
ugui_widget_set_area(&s_lbl[i], x, (int16_t)(y + TILE_H - 26), TILE_W, 20);
ugui_widget_set_id(&s_lbl[i], WDGT_ID_TITLE);
ugui_widget_label_set_style(&s_lbl[i], false, UGUI_THEME_SURFACE, tiles[i].label,
&lexend_14pt_2bpp, UGUI_THEME_TEXT,
UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_lbl[i]);
}
Image as a Button (Tap to Mute)¶
An image becomes interactive the moment it is given an event callback — the input layer hit-tests it like any other widget. Each tap flips a muted flag and, in one handler, swaps the image source between the "on"/"off" icons via set_src(), recolours it via set_colors() (green live, grey muted), and updates a status label.

/* --- 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
};
/* --- image assets (inc/icons/ + inc/images/, shipped with the library;
* each header DEFINES its image — include it in ONE .c only) --- */
#ifdef UGUI_WIDGET_ENABLE_IMAGE
#include "icons/mic_icon_1_off_grayscale_4.h" /* defines img_mic_icon_1_off */
#include "icons/mic_icon_1_on_grayscale_4.h" /* defines img_mic_icon_1_on */
#endif
/* --- 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_IMAGE) && 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_mic;
static ugui_image_ext_t s_mic_ext;
static ugui_widget_t s_status;
static bool s_muted;
#define CARD_W 120
#define CARD_H 120
#define CARD_X ((int16_t)((UGUI_SCREEN_W - CARD_W) / 2))
#define CARD_Y 58
#define ICON_SZ 24
#endif
/* --- local helpers & event callbacks --- */
static void apply_state(void)
{
ugui_widget_image_set_src(&s_mic,
s_muted ? &img_mic_icon_1_off : &img_mic_icon_1_on);
ugui_widget_image_set_colors(&s_mic,
s_muted ? UGUI_COLOR_GRAY : UGUI_COLOR_GREEN, UGUI_THEME_SURFACE);
ugui_widget_label_set_style(&s_status, false, UGUI_THEME_BG,
s_muted ? "Microphone: OFF" : "Microphone: ON",
&lexend_14pt_2bpp, s_muted ? UGUI_COLOR_GRAY : UGUI_COLOR_GREEN,
UGUI_ALIGN_CENTER, 1u);
}
static void on_mic_click(uint8_t widget_id, ugui_event_t event)
{
(void)widget_id;
if (event != UGUI_EVENT_CLICK) { return; }
s_muted = !s_muted;
apply_state();
printf("[image.button] microphone %s\n", s_muted ? "muted" : "live");
}
/* --- build it (in your screen's init) --- */
ugui_widget_image_init(&s_root, &s_mic, &s_mic_ext);
ugui_widget_set_area(&s_mic, (int16_t)(CARD_X + ((CARD_W - ICON_SZ) / 2)),
(int16_t)(CARD_Y + ((CARD_H - ICON_SZ) / 2)), ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_mic, WDGT_ID_DEMO);
ugui_widget_set_event_cb(&s_mic, on_mic_click,
UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP
| UGUI_EMASK_KEY_CONFIRM);
ugui_screen_add_widget(&s_screen, &s_mic);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_mic);
#endif
Live Status Bar: Icon as State¶
A connectivity strip: set_src() re-points a widget at a different flash asset with no relayout, so a Wi-Fi glyph steps through its signal levels the same way a real driver would drive it from an RSSI reading. The Weaker/Stronger buttons step a level 0..5, re-pointing BOTH the strip icon and the big preview and recolouring them by strength.

/* --- 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_SLOT4 = 10, WDGT_ID_SLOT0, WDGT_ID_SLOT1,
WDGT_ID_SLOT2, WDGT_ID_SLOT3, WDGT_ID_DEMO,
WDGT_ID_TITLE, WDGT_ID_BACK, WDGT_ID_SLOT5
};
/* --- image assets (inc/icons/ + inc/images/, shipped with the library;
* each header DEFINES its image — include it in ONE .c only) --- */
#ifdef UGUI_WIDGET_ENABLE_IMAGE
#include "icons/cellular_icon_1_signal_full_grayscale_4.h" /* defines img_cellular_icon_1_signal_full */
#include "icons/wifi_icon_1_offline_grayscale_4.h" /* defines img_wifi_icon_1_offline */
#include "icons/wifi_icon_1_signal_1_grayscale_4.h" /* defines img_wifi_icon_1_signal_1 */
#include "icons/wifi_icon_1_signal_2_grayscale_4.h" /* defines img_wifi_icon_1_signal_2 */
#include "icons/wifi_icon_1_signal_3_grayscale_4.h" /* defines img_wifi_icon_1_signal_3 */
#include "icons/wifi_icon_1_signal_4_grayscale_4.h" /* defines img_wifi_icon_1_signal_4 */
#include "icons/wifi_icon_1_signal_5_full_grayscale_4.h" /* defines img_wifi_icon_1_signal_5_full */
#endif
/* --- 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_IMAGE) && 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_strip;
static ugui_box_ext_t s_strip_ext;
static ugui_widget_t s_clock;
static ugui_label_ext_t s_clock_ext;
static ugui_widget_t s_cell;
static ugui_image_ext_t s_cell_ext;
static ugui_widget_t s_wifi_bar;
static ugui_image_ext_t s_wifi_bar_ext;
static ugui_widget_t s_card;
static ugui_box_ext_t s_card_ext;
static ugui_widget_t s_wifi_big;
static ugui_image_ext_t s_wifi_big_ext;
static ugui_widget_t s_level_lbl;
static ugui_label_ext_t s_level_lbl_ext;
static ugui_widget_t s_weaker; static ugui_button_ext_t s_weaker_ext;
static ugui_widget_t s_stronger; static ugui_button_ext_t s_stronger_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_IMAGE) && defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static ugui_text_cfg_t s_weaker_txt;
static ugui_text_cfg_t s_stronger_txt;
#endif
#if defined(UGUI_WIDGET_ENABLE_IMAGE) && defined(UGUI_WIDGET_ENABLE_LABEL) && defined(UGUI_ENABLE_FONT)
static char s_level_buf[24];
static int8_t s_level; /* 0 (offline) .. 5 (full) */
#define WIFI_LEVELS 6
static const ugui_compressed_image_t* const s_wifi_img[WIFI_LEVELS] = {
&img_wifi_icon_1_offline,
&img_wifi_icon_1_signal_1,
&img_wifi_icon_1_signal_2,
&img_wifi_icon_1_signal_3,
&img_wifi_icon_1_signal_4,
&img_wifi_icon_1_signal_5_full,
};
static const char* const s_wifi_name[WIFI_LEVELS] = {
"Offline", "Very weak", "Weak", "OK", "Good", "Full",
};
#define ICON_SZ 24
#endif
/* --- local helpers & event callbacks --- */
static ugui_color_t level_color(int8_t lvl)
{
if (lvl <= 0) { return UGUI_COLOR_GRAY; }
if (lvl <= 2) { return UGUI_COLOR_AMBER; }
return UGUI_COLOR_GREEN;
}
static void apply_level(void)
{
const ugui_compressed_image_t* img = s_wifi_img[s_level];
const ugui_color_t col = level_color(s_level);
ugui_widget_image_set_src(&s_wifi_bar, img);
ugui_widget_image_set_colors(&s_wifi_bar, col, UGUI_THEME_SURFACE);
ugui_widget_image_set_src(&s_wifi_big, img);
ugui_widget_image_set_colors(&s_wifi_big, col, UGUI_THEME_SURFACE);
(void)snprintf(s_level_buf, sizeof(s_level_buf), "Wi-Fi: %s",
s_wifi_name[s_level]);
ugui_label_set_text(&s_level_lbl, s_level_buf);
}
static void on_step(uint8_t widget_id, ugui_event_t event)
{
if (event != UGUI_EVENT_CLICK) { return; }
if (widget_id == WDGT_ID_SLOT4) { /* Weaker */
if (s_level > 0) { s_level--; }
} else { /* Stronger */
if (s_level < (WIFI_LEVELS - 1)) { s_level++; }
}
apply_level();
printf("[image.statusbar] wifi level -> %d (%s)\n",
(int)s_level, s_wifi_name[s_level]);
}
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,
ugui_color_t fill, 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, fill, 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);
#else
(void)str;
#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) --- */
/* Status strip. */
ugui_widget_box_init(&s_root, &s_strip, &s_strip_ext);
ugui_widget_set_area(&s_strip, 10, 44,
(int16_t)(UGUI_SCREEN_W - (2 * 10)), 30);
ugui_widget_set_id(&s_strip, WDGT_ID_SLOT0);
ugui_widget_box_set_style(&s_strip, true, 6u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
ugui_screen_add_widget(&s_screen, &s_strip);
ugui_widget_label_init(&s_root, &s_clock, &s_clock_ext);
ugui_widget_set_area(&s_clock, 10 + 12, 44, 100, 30);
ugui_widget_set_id(&s_clock, WDGT_ID_SLOT1);
ugui_widget_label_set_style(&s_clock, false, UGUI_THEME_SURFACE, "12:45",
&lexend_14pt_2bpp, UGUI_THEME_TEXT,
(ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER), 1u);
ugui_screen_add_widget(&s_screen, &s_clock);
ugui_widget_image_init(&s_root, &s_cell, &s_cell_ext);
ugui_widget_set_area(&s_cell, (int16_t)(UGUI_SCREEN_W - 10 - 62), 47, ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_cell, WDGT_ID_SLOT2);
ugui_widget_image_set_src(&s_cell, &img_cellular_icon_1_signal_full);
ugui_widget_image_set_colors(&s_cell, UGUI_COLOR_GREEN, UGUI_THEME_SURFACE);
ugui_screen_add_widget(&s_screen, &s_cell);
ugui_widget_image_init(&s_root, &s_wifi_bar, &s_wifi_bar_ext);
ugui_widget_set_area(&s_wifi_bar, (int16_t)(UGUI_SCREEN_W - 10 - 32), 47, ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_wifi_bar, WDGT_ID_SLOT3);
ugui_screen_add_widget(&s_screen, &s_wifi_bar);
/* Big preview card. */
ugui_widget_box_init(&s_root, &s_card, &s_card_ext);
ugui_widget_set_area(&s_card, (int16_t)((UGUI_SCREEN_W - 100) / 2), 86, 100, 84);
ugui_widget_set_id(&s_card, WDGT_ID_DEMO);
ugui_widget_box_set_style(&s_card, true, 10u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
ugui_screen_add_widget(&s_screen, &s_card);
ugui_widget_image_init(&s_root, &s_wifi_big, &s_wifi_big_ext);
ugui_widget_set_area(&s_wifi_big, (int16_t)((UGUI_SCREEN_W - ICON_SZ) / 2), 106,
ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_wifi_big, WDGT_ID_TITLE);
ugui_screen_add_widget(&s_screen, &s_wifi_big);
ugui_widget_label_init(&s_root, &s_level_lbl, &s_level_lbl_ext);
ugui_widget_set_area(&s_level_lbl, 10, 174,
(int16_t)(UGUI_SCREEN_W - (2 * 10)), 22);
ugui_widget_set_id(&s_level_lbl, WDGT_ID_BACK);
ugui_widget_label_set_style(&s_level_lbl, false, UGUI_THEME_BG, s_level_buf,
&lexend_14pt_2bpp, UGUI_THEME_TEXT, UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_level_lbl);
/* Stepper buttons. */
add_button(&s_weaker, &s_weaker_ext, &s_weaker_txt, WDGT_ID_SLOT4,
30, 202, 120, 32, UGUI_THEME_SURFACE, "Weaker", on_step);
add_button(&s_stronger, &s_stronger_ext, &s_stronger_txt, WDGT_ID_SLOT5,
170, 202, 120, 32, UGUI_THEME_SURFACE, "Stronger", on_step);
apply_level(); /* seed both wifi icons + the label for the initial level */
Weather Dashboard: Composition¶
The complex endpoint of the family: a "today" hero card (big condition icon + temperature + place) above a three-day forecast row (per-day card: weekday, tinted condition icon, high/low). Nothing new about a single image — the point is composition: image widgets for the glyphs, labels for the numbers, boxes for the cards, all laid out from a small table.

/* --- 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_DEMO, WDGT_ID_SLOT1,
WDGT_ID_SLOT2, WDGT_ID_SLOT3, WDGT_ID_TITLE,
WDGT_ID_BACK
};
/* --- image assets (inc/icons/ + inc/images/, shipped with the library;
* each header DEFINES its image — include it in ONE .c only) --- */
#ifdef UGUI_WIDGET_ENABLE_IMAGE
#include "icons/weather_icon_1_cloudy_grayscale_4.h" /* defines img_weather_icon_1_cloudy */
#include "icons/weather_icon_1_rainy_grayscale_4.h" /* defines img_weather_icon_1_rainy */
#include "icons/weather_icon_1_snowflake_grayscale_4.h" /* defines img_weather_icon_1_snowflake */
#include "icons/weather_icon_1_thunderstorms_grayscale_4.h" /* defines img_weather_icon_1_thunderstorms */
#endif
/* --- 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_IMAGE) && 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_today;
static ugui_box_ext_t s_today_ext;
static ugui_widget_t s_today_icon;
static ugui_image_ext_t s_today_icon_ext;
static ugui_widget_t s_temp;
static ugui_label_ext_t s_temp_ext;
static ugui_widget_t s_place;
static ugui_label_ext_t s_place_ext;
#define FC_N 3u
static ugui_widget_t s_fc_card[FC_N];
static ugui_box_ext_t s_fc_card_ext[FC_N];
static ugui_widget_t s_fc_icon[FC_N];
static ugui_image_ext_t s_fc_icon_ext[FC_N];
static ugui_widget_t s_fc_day[FC_N];
static ugui_label_ext_t s_fc_day_ext[FC_N];
static ugui_widget_t s_fc_temp[FC_N];
static ugui_label_ext_t s_fc_temp_ext[FC_N];
#define ICON_SZ 24
#endif
/* --- build it (in your screen's init) --- */
/* ---- Today hero card ---- */
ugui_widget_box_init(&s_root, &s_today, &s_today_ext);
ugui_widget_set_area(&s_today, 10, 42,
(int16_t)(UGUI_SCREEN_W - (2 * 10)), 76);
ugui_widget_set_id(&s_today, WDGT_ID_SLOT0);
ugui_widget_box_set_style(&s_today, true, 10u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
ugui_screen_add_widget(&s_screen, &s_today);
ugui_widget_image_init(&s_root, &s_today_icon, &s_today_icon_ext);
ugui_widget_set_area(&s_today_icon, 10 + 22, 68, ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_today_icon, WDGT_ID_DEMO);
ugui_widget_image_set_src(&s_today_icon, &img_weather_icon_1_cloudy);
ugui_widget_image_set_colors(&s_today_icon, UGUI_THEME_ACCENT, UGUI_THEME_SURFACE);
ugui_screen_add_widget(&s_screen, &s_today_icon);
ugui_widget_label_init(&s_root, &s_temp, &s_temp_ext);
ugui_widget_set_area(&s_temp, 10 + 70, 52, 200, 28);
ugui_widget_set_id(&s_temp, WDGT_ID_SLOT1);
ugui_widget_label_set_style(&s_temp, false, UGUI_THEME_SURFACE, "18 C Cloudy",
&lexend_14pt_2bpp, UGUI_THEME_ACCENT,
(ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER), 1u);
ugui_screen_add_widget(&s_screen, &s_temp);
ugui_widget_label_init(&s_root, &s_place, &s_place_ext);
ugui_widget_set_area(&s_place, 10 + 70, 82, 220, 22);
ugui_widget_set_id(&s_place, WDGT_ID_SLOT2);
ugui_widget_label_set_style(&s_place, false, UGUI_THEME_SURFACE, "Berlin - updated 12:45",
&lexend_14pt_2bpp, UGUI_THEME_TEXT,
(ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER), 1u);
ugui_screen_add_widget(&s_screen, &s_place);
/* ---- 3-day forecast ---- */
const struct { const char* day; const ugui_compressed_image_t* img;
const char* temp; ugui_color_t tint; } fc[FC_N] = {
{ "Wed", &img_weather_icon_1_rainy, "12/7", UGUI_COLOR_CYAN },
{ "Thu", &img_weather_icon_1_snowflake, "3/-2", UGUI_COLOR_CYAN },
{ "Fri", &img_weather_icon_1_thunderstorms, "9/5", UGUI_COLOR_AMBER },
};
const int16_t fc_w = 93;
const int16_t fc_y = 128;
const int16_t fc_h = 100;
for (uint8_t i = 0u; i < (uint8_t)FC_N; i++) {
const int16_t x = (int16_t)(10 + (i * (fc_w + 10)));
ugui_widget_box_init(&s_root, &s_fc_card[i], &s_fc_card_ext[i]);
ugui_widget_set_area(&s_fc_card[i], x, fc_y, fc_w, fc_h);
ugui_widget_set_id(&s_fc_card[i], (uint8_t)(WDGT_ID_SLOT3 + i));
ugui_widget_box_set_style(&s_fc_card[i], true, 8u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
ugui_screen_add_widget(&s_screen, &s_fc_card[i]);
ugui_widget_label_init(&s_root, &s_fc_day[i], &s_fc_day_ext[i]);
ugui_widget_set_area(&s_fc_day[i], x, (int16_t)(fc_y + 8), fc_w, 18);
ugui_widget_set_id(&s_fc_day[i], WDGT_ID_TITLE);
ugui_widget_label_set_style(&s_fc_day[i], false, UGUI_THEME_SURFACE, fc[i].day,
&lexend_14pt_2bpp, UGUI_THEME_TEXT, UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_fc_day[i]);
ugui_widget_image_init(&s_root, &s_fc_icon[i], &s_fc_icon_ext[i]);
ugui_widget_set_area(&s_fc_icon[i], (int16_t)(x + ((fc_w - ICON_SZ) / 2)),
(int16_t)(fc_y + 34), ICON_SZ, ICON_SZ);
ugui_widget_set_id(&s_fc_icon[i], WDGT_ID_DEMO);
ugui_widget_image_set_src(&s_fc_icon[i], fc[i].img);
ugui_widget_image_set_colors(&s_fc_icon[i], fc[i].tint, UGUI_THEME_SURFACE);
ugui_screen_add_widget(&s_screen, &s_fc_icon[i]);
ugui_widget_label_init(&s_root, &s_fc_temp[i], &s_fc_temp_ext[i]);
ugui_widget_set_area(&s_fc_temp[i], x, (int16_t)(fc_y + fc_h - 26), fc_w, 18);
ugui_widget_set_id(&s_fc_temp[i], WDGT_ID_BACK);
ugui_widget_label_set_style(&s_fc_temp[i], false, UGUI_THEME_SURFACE, fc[i].temp,
&lexend_14pt_2bpp, UGUI_THEME_TEXT, UGUI_ALIGN_CENTER, 1u);
ugui_screen_add_widget(&s_screen, &s_fc_temp[i]);
}
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.