Skip to content

Progress Bar Widget

The Progress Bar is a filled-track indicator — a percentage readout, a fuel gauge, a system-status meter. Init installs theme-tracked fill/track/ border colours, a default border width and corner radius, and (with UGUI_ENABLE_DEFAULTS) a visible default size. set_value() moves the fill; set_range() remaps the scale away from a raw 0..100 percentage; set_orient() switches between horizontal and vertical fill growth; and an optional set_text() overlay renders a centred label on top of the fill, two-tone so it stays legible over both the filled and empty regions.

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 Bar

A bar created with nothing but ugui_widget_progress_init() — theme colours, default border/radius/size, all from the library. set_text() attaches the built-in percentage overlay; a "Bump +10%" button drives set_value() to show the fill (and its own overlay text) updating live.

Default Bar

/* --- 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_TRIGGER
};

/* --- 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_PROGRESS_BAR)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t            s_bar;
static ugui_progress_bar_ext_t  s_bar_ext;
static ugui_widget_t            s_trigger;
static ugui_button_ext_t        s_trigger_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_PROGRESS_BAR)
static ugui_text_cfg_t          s_bar_text;
static char                     s_bar_buf[12];   /* e.g. "65 %" */
static ugui_text_cfg_t          s_trigger_text;
#endif
#if defined(UGUI_WIDGET_ENABLE_PROGRESS_BAR)
#define TRIGGER_W  180
#define TRIGGER_H  36
#define TRIGGER_X  ((int16_t)((UGUI_SCREEN_W - TRIGGER_W) / 2))
#define TRIGGER_Y  ((int16_t)((UGUI_SCREEN_H / 2) + 30))
#endif

/* --- local helpers & event callbacks --- */
static void update_label(void)
{
#ifdef UGUI_ENABLE_FONT
    int16_t v = ugui_widget_progress_get_value(&s_bar);
    (void)snprintf(s_bar_buf, sizeof(s_bar_buf), "%d %%", (int)v);
    s_bar_text.str = s_bar_buf;
    ugui_widget_invalidate(&s_bar);
#endif
}

static void on_bump(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    int16_t v = ugui_widget_progress_get_value(&s_bar);
    v = (int16_t)(v + 10);
    if (v > 100) { v = 0; }   /* wrap back to empty after reaching full */
    ugui_widget_progress_set_value(&s_bar, v);
    update_label();
}

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_progress_init(&s_root, &s_bar, &s_bar_ext);
ugui_widget_set_align(&s_bar, UGUI_ALIGN_CENTER, 0, -20);
ugui_widget_set_id(&s_bar, WDGT_ID_DEMO);
ugui_widget_progress_set_value(&s_bar, 40);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_bar_text, s_bar_buf, &lexend_14pt_2bpp, UGUI_THEME_KNOB,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_progress_set_text(&s_bar, &s_bar_text);
#endif
ugui_screen_add_widget(&s_screen, &s_bar);
update_label();

add_button(&s_trigger, &s_trigger_ext, &s_trigger_text, WDGT_ID_TRIGGER,
          TRIGGER_X, TRIGGER_Y, TRIGGER_W, TRIGGER_H, "Bump +10%", on_bump);

Value Coverage & Orientation

Three horizontal bars pinned at the extremes and the midpoint — the three shapes a fill region can take. Beside them, the same kind of bar with set_orient(UGUI_PBAR_VERT) (fill grows bottom-up) and a remapped set_range(0, 200) — a "fuel: 0..200 L" reading instead of a raw percentage, showing the widget isn't tied to 0..100.

Value Coverage & Orientation

/* --- 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_SLOT4, WDGT_ID_SLOT7,
    WDGT_ID_SLOT8
};

/* --- 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_PROGRESS_BAR)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
#define ROW_N  3u
static ugui_widget_t            s_row_w  [ROW_N];
static ugui_progress_bar_ext_t  s_row_ext[ROW_N];
static ugui_widget_t            s_row_lbl[ROW_N];
static ugui_label_ext_t         s_row_lbl_ext[ROW_N];
static const char* const k_row_label[ROW_N] = { "Empty (0%)", "Half (50%)", "Full (100%)" };
static const int16_t     k_row_value[ROW_N] = { 0, 50, 100 };
static ugui_widget_t            s_vert_w;
static ugui_progress_bar_ext_t  s_vert_ext;
static ugui_widget_t            s_vert_val;
static ugui_label_ext_t         s_vert_val_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_PROGRESS_BAR)
static char s_vert_buf[16];
#endif
#if defined(UGUI_WIDGET_ENABLE_PROGRESS_BAR)
#define ROW_X     10
#define ROW_W     260
#define LBL_H     18
#define BAR_H     28
#define ROW_STEP  56
#define ROW0_Y    48
#define VCOL_X     ((int16_t)(ROW_X + ROW_W + 40))
#define VCOL_W     44
#define VCOL_LBL_Y ROW0_Y
#define VCOL_BAR_Y ((int16_t)(VCOL_LBL_Y + LBL_H + 4))
#define VCOL_BAR_H 170
#define VCOL_VAL_Y ((int16_t)(VCOL_BAR_Y + VCOL_BAR_H + 6))
#endif

/* --- local helpers & event callbacks --- */
static void add_label(ugui_widget_t* w, ugui_label_ext_t* ext, uint8_t id,
                      int16_t x, int16_t y, int16_t cw, int16_t ch,
                      const char* str, ugui_align_t align)
{
    ugui_widget_label_init(&s_root, w, ext);
    ugui_widget_set_area(w, x, y, cw, ch);
    ugui_widget_set_id  (w, id);
    ugui_widget_label_set_style(w, /*bg_fill=*/false, UGUI_THEME_BG, str,
                                &lexend_14pt_2bpp, UGUI_THEME_TEXT, align, 1u);
    ugui_screen_add_widget(&s_screen, w);
}

/* --- build it (in your screen's init) --- */
uint8_t i;
for (i = 0u; i < ROW_N; i++) {
    int16_t lbl_y = (int16_t)(ROW0_Y + (int16_t)i * ROW_STEP);
    int16_t bar_y = (int16_t)(lbl_y + LBL_H);

    add_label(&s_row_lbl[i], &s_row_lbl_ext[i], (uint8_t)(WDGT_ID_SLOT0 + i),
             ROW_X, lbl_y, ROW_W, LBL_H, k_row_label[i], UGUI_ALIGN_LEFT);

    ugui_widget_progress_init(&s_root, &s_row_w[i], &s_row_ext[i]);
    ugui_widget_set_area(&s_row_w[i], ROW_X, bar_y, ROW_W, BAR_H);
    ugui_widget_set_id(&s_row_w[i], (uint8_t)(WDGT_ID_SLOT4 + i));
    ugui_widget_progress_set_value(&s_row_w[i], k_row_value[i]);
    ugui_screen_add_widget(&s_screen, &s_row_w[i]);
}

ugui_widget_progress_init(&s_root, &s_vert_w, &s_vert_ext);
ugui_widget_set_area(&s_vert_w, VCOL_X, VCOL_BAR_Y, VCOL_W, VCOL_BAR_H);
ugui_widget_set_id(&s_vert_w, WDGT_ID_SLOT7);
ugui_widget_progress_set_orient(&s_vert_w, UGUI_PBAR_VERT);
ugui_widget_progress_set_range(&s_vert_w, 0, 200);
ugui_widget_progress_set_value(&s_vert_w, 130);
ugui_screen_add_widget(&s_screen, &s_vert_w);

/* Readout: get_value() reflected back as text under the bar. */
#ifdef UGUI_ENABLE_FONT
(void)snprintf(s_vert_buf, sizeof(s_vert_buf), "%d L", (int)ugui_widget_progress_get_value(&s_vert_w));
#endif
add_label(&s_vert_val, &s_vert_val_ext, WDGT_ID_SLOT8,
         (int16_t)(VCOL_X - 20), VCOL_VAL_Y, 90, 20,
#ifdef UGUI_ENABLE_FONT
         s_vert_buf,
#else
         "130 L",
#endif
         UGUI_ALIGN_LEFT);

Colours, Sizes & Text Overlay

A colour column (four set_colors() presets), a size column (the same bar at three border/radius/height combos — plain ext fields, no dedicated setter), and a label demo turning on the built-in centred percentage overlay via set_text().

Colours, Sizes & Text Overlay

/* --- 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_SLOT8, WDGT_ID_SLOT17
};

/* --- 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_PROGRESS_BAR)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
#define COL_N  4u
static ugui_widget_t            s_col_w  [COL_N];
static ugui_progress_bar_ext_t  s_col_ext[COL_N];
static ugui_widget_t            s_col_lbl[COL_N];
static ugui_label_ext_t         s_col_lbl_ext[COL_N];
static const char* const k_col_name[COL_N] = { "Accent", "Success", "Caution", "Alert" };
#define SIZE_N  3u
static ugui_widget_t            s_size_w  [SIZE_N];
static ugui_progress_bar_ext_t  s_size_ext[SIZE_N];
static ugui_widget_t            s_size_lbl[SIZE_N];
static ugui_label_ext_t         s_size_lbl_ext[SIZE_N];
static const char* const k_size_name  [SIZE_N] = { "Small", "Medium", "Large" };
static const uint8_t     k_size_border[SIZE_N] = { 1u, 2u, 3u };
static const uint8_t     k_size_radius[SIZE_N] = { 2u, 4u, 8u };
static const int16_t     k_size_h     [SIZE_N] = { 14, 22, 32 };
static ugui_widget_t            s_lbl_w;
static ugui_progress_bar_ext_t  s_lbl_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_PROGRESS_BAR)
static ugui_text_cfg_t          s_lbl_text;
#endif
#if defined(UGUI_WIDGET_ENABLE_PROGRESS_BAR)
#define HDR_Y      38
#define COL_LBL_X  10
#define COL_LBL_W  74
#define COL_BAR_X  ((int16_t)(COL_LBL_X + COL_LBL_W + 4))
#define COL_BAR_W  120
#define COL_BAR_H  22
#define COL_ROW0   ((int16_t)(HDR_Y + 24))
#define COL_STEP   32
#define SIZE_LBL_X ((int16_t)(COL_BAR_X + COL_BAR_W + 30))
#define SIZE_LBL_W 62
#define SIZE_BAR_X ((int16_t)(SIZE_LBL_X + SIZE_LBL_W + 4))
#define SIZE_BAR_W 140
#define LBL_DEMO_Y     220
#define LBL_DEMO_BAR_Y ((int16_t)(LBL_DEMO_Y + 22))
#define LBL_DEMO_W     ((int16_t)(UGUI_SCREEN_W - 2 * 10))
#define LBL_DEMO_H     30
#endif

/* --- local helpers & event callbacks --- */
static void add_label(ugui_widget_t* w, ugui_label_ext_t* ext, uint8_t id,
                      int16_t x, int16_t y, int16_t cw, int16_t ch,
                      const char* str, ugui_align_t align)
{
    ugui_widget_label_init(&s_root, w, ext);
    ugui_widget_set_area(w, x, y, cw, ch);
    ugui_widget_set_id  (w, id);
    ugui_widget_label_set_style(w, /*bg_fill=*/false, UGUI_THEME_BG, str,
                                &lexend_14pt_2bpp, UGUI_THEME_TEXT, align, 1u);
    ugui_screen_add_widget(&s_screen, w);
}

/* --- build it (in your screen's init) --- */
ugui_color_t fill_col[COL_N];
fill_col[0] = UGUI_THEME_ACCENT;
fill_col[1] = UGUI_THEME_OK;
fill_col[2] = ugui_color_preset_caution();
fill_col[3] = UGUI_THEME_CANCEL;

uint8_t i;
for (i = 0u; i < COL_N; i++) {
    int16_t row_y = (int16_t)(COL_ROW0 + (int16_t)i * COL_STEP);

    add_label(&s_col_lbl[i], &s_col_lbl_ext[i], (uint8_t)(WDGT_ID_SLOT0 + i),
             COL_LBL_X, row_y, COL_LBL_W, COL_BAR_H, k_col_name[i],
             (ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER));

    ugui_widget_progress_init(&s_root, &s_col_w[i], &s_col_ext[i]);
    ugui_widget_set_area(&s_col_w[i], COL_BAR_X, row_y, COL_BAR_W, COL_BAR_H);
    ugui_widget_progress_set_colors(&s_col_w[i], fill_col[i], UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
    ugui_widget_progress_set_value(&s_col_w[i], 65);
    ugui_screen_add_widget(&s_screen, &s_col_w[i]);
}

int16_t size_y = COL_ROW0;
for (i = 0u; i < SIZE_N; i++) {
    int16_t row_h = k_size_h[i];

    add_label(&s_size_lbl[i], &s_size_lbl_ext[i], (uint8_t)(WDGT_ID_SLOT8 + i),
             SIZE_LBL_X, size_y, SIZE_LBL_W, row_h, k_size_name[i],
             (ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER));

    ugui_widget_progress_init(&s_root, &s_size_w[i], &s_size_ext[i]);
    ugui_widget_set_area(&s_size_w[i], SIZE_BAR_X, size_y, SIZE_BAR_W, row_h);
    s_size_ext[i].border_width  = k_size_border[i];
    s_size_ext[i].corner_radius = k_size_radius[i];
    ugui_widget_progress_set_value(&s_size_w[i], 55);
    ugui_screen_add_widget(&s_screen, &s_size_w[i]);

    size_y = (int16_t)(size_y + row_h + 10);
}

ugui_widget_progress_init(&s_root, &s_lbl_w, &s_lbl_ext);
ugui_widget_set_area(&s_lbl_w, 10, LBL_DEMO_BAR_Y, LBL_DEMO_W, LBL_DEMO_H);
ugui_widget_set_id(&s_lbl_w, WDGT_ID_SLOT17);
ugui_widget_progress_set_value(&s_lbl_w, 42);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_lbl_text, "42 %", &lexend_14pt_2bpp, UGUI_THEME_KNOB,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_progress_set_text(&s_lbl_w, &s_lbl_text);
#endif
ugui_screen_add_widget(&s_screen, &s_lbl_w);

System Status Panel

A realistic dashboard: four bars re-coloured by set_colors() from a shared green/amber/red threshold function, each carrying its own live percentage overlay. "Randomize" redraws every row with a new value (and usually a new colour band), proving set_value() + set_colors() + the text overlay all update together on every press.

System Status Panel

/* --- 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_RANDOMIZE
};

/* --- 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_PROGRESS_BAR)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
#define ROW_N  4u
static ugui_widget_t            s_bar_w  [ROW_N];
static ugui_progress_bar_ext_t  s_bar_ext[ROW_N];
static ugui_widget_t            s_label  [ROW_N];
static ugui_label_ext_t         s_label_ext[ROW_N];
static ugui_widget_t            s_randomize_btn;
static ugui_button_ext_t        s_randomize_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_PROGRESS_BAR)
static ugui_text_cfg_t          s_bar_text[ROW_N];
static char                     s_bar_buf [ROW_N][12];
static ugui_text_cfg_t          s_randomize_text;
#endif
#if defined(UGUI_WIDGET_ENABLE_PROGRESS_BAR)
static const char* const  k_row_label [ROW_N] = { "CPU", "Memory", "Disk", "Battery" };
static const int16_t      k_row_init  [ROW_N] = { 42,     78,       91,     15 };
static const uint8_t      k_row_invert[ROW_N] = { 0u,     0u,       0u,     1u };
#define BTN_H     32
#define BTN_Y     ((int16_t)(UGUI_SCREEN_H - BTN_H - 10))
#define PANEL_X   10
#define PANEL_Y   ((int16_t)(38 + 4))
#define PANEL_W   ((int16_t)(UGUI_SCREEN_W - 2 * 10))
#define PANEL_H   ((int16_t)(BTN_Y - PANEL_Y - 8))
#define ROW_STEP  ((int16_t)(PANEL_H / (int16_t)ROW_N))
#define BAR_H     26
#define LBL_X     ((int16_t)(PANEL_X + 12))
#define LBL_W     90
#define BAR_X     ((int16_t)(LBL_X + LBL_W + 8))
#define BAR_W     ((int16_t)(PANEL_X + PANEL_W - BAR_X - 12))
static uint32_t s_rng_state = 0x2026u;
#endif

/* --- local helpers & event callbacks --- */
static uint32_t next_rand(void)
{
    uint32_t x = s_rng_state;
    x ^= (uint32_t)(x << 13);
    x ^= (x >> 17);
    x ^= (uint32_t)(x << 5);
    s_rng_state = x;
    return x;
}

static ugui_color_t threshold_color(int16_t pct, uint8_t invert)
{
    int16_t bad_hi  = invert ? 15  : 85;   /* Battery: <=15% is critical   */
    int16_t warn_hi = invert ? 40  : 60;   /* Battery: <=40% is low        */

    if (invert != 0u) {
        if (pct <= bad_hi)  { return UGUI_THEME_CANCEL; }          /* red   */
        if (pct <= warn_hi) { return ugui_color_preset_caution(); } /* amber */
        return UGUI_THEME_OK;                                       /* green */
    }
    if (pct >= bad_hi)  { return UGUI_THEME_CANCEL; }              /* red   */
    if (pct >= warn_hi) { return ugui_color_preset_caution(); }     /* amber */
    return UGUI_THEME_OK;                                           /* green */
}

static void apply_row(uint8_t i, int16_t pct)
{
    ugui_widget_progress_set_value(&s_bar_w[i], pct);
    ugui_widget_progress_set_colors(&s_bar_w[i], threshold_color(pct, k_row_invert[i]),
                                    UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
#ifdef UGUI_ENABLE_FONT
    (void)snprintf(s_bar_buf[i], sizeof(s_bar_buf[i]), "%d %%", (int)pct);
    s_bar_text[i].str = s_bar_buf[i];
    ugui_widget_invalidate(&s_bar_w[i]);
#endif
}

static void on_randomize(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    uint8_t i;
    for (i = 0u; i < ROW_N; i++) {
        int16_t pct = (int16_t)(next_rand() % 101u);
        apply_row(i, pct);
    }
    printf("[progress.settings] Randomize\n");
}

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
}

static void add_label(ugui_widget_t* w, ugui_label_ext_t* ext, uint8_t id,
                      int16_t x, int16_t y, int16_t cw, int16_t ch,
                      const char* str, ugui_align_t align)
{
    ugui_widget_label_init(&s_root, w, ext);
    ugui_widget_set_area(w, x, y, cw, ch);
    ugui_widget_set_id  (w, id);
    ugui_widget_label_set_style(w, /*bg_fill=*/false, UGUI_THEME_SURFACE, str,
                                &lexend_14pt_2bpp, UGUI_THEME_TEXT, align, 1u);
    ugui_screen_add_widget(&s_screen, w);
}

/* --- build it (in your screen's init) --- */
uint8_t i;
for (i = 0u; i < ROW_N; i++) {
    int16_t row_y = (int16_t)(PANEL_Y + (int16_t)i * ROW_STEP
                              + (ROW_STEP - BAR_H) / 2);

    add_label(&s_label[i], &s_label_ext[i], (uint8_t)(WDGT_ID_SLOT4 + i),
             LBL_X, row_y, LBL_W, BAR_H, k_row_label[i],
             (ugui_align_t)(UGUI_ALIGN_LEFT | UGUI_ALIGN_V_CENTER));

    ugui_widget_progress_init(&s_root, &s_bar_w[i], &s_bar_ext[i]);
    ugui_widget_set_area(&s_bar_w[i], BAR_X, row_y, BAR_W, BAR_H);
    ugui_widget_set_id(&s_bar_w[i], (uint8_t)(WDGT_ID_SLOT0 + i));
#ifdef UGUI_ENABLE_FONT
    ugui_text_cfg_init(&s_bar_text[i], "", &lexend_14pt_2bpp, UGUI_THEME_KNOB,
                       UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
    ugui_widget_progress_set_text(&s_bar_w[i], &s_bar_text[i]);
#endif
    apply_row(i, k_row_init[i]);
    ugui_screen_add_widget(&s_screen, &s_bar_w[i]);
}

add_button(&s_randomize_btn, &s_randomize_ext, &s_randomize_text, WDGT_ID_RANDOMIZE,
          PANEL_X, BTN_Y, PANEL_W, BTN_H, "Randomize", on_randomize);


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.