Skip to content

MsgBox Widget

The MsgBox widget is a modal dialog — an alert, a confirmation, a small popup form. Init installs a theme-tracked panel with title and button bars sized from library defaults; set_text() attaches a title/message pair, add_button() docks a button in the button bar, and open()/close() show and hide it (with an optional dimming overlay). set_result_cb() fires exactly once per close — however it closed — with the index of the button that was pressed (or the library's 0x7F "no button" sentinel for a programmatic close). set_content() swaps the whole body for another widget (a slider, say), for popups that collect a value rather than just show a message.

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 Dialog

Three calls make a working modal: ugui_widget_msgbox_init() installs the whole look (theme colours, radius, title/button bar heights, and a 220x130 default size), set_text() attaches a title + message, and one add_button() docks an OK button — itself left at its own default style. No set_area()/set_style() for either widget anywhere in this file.

Default Dialog

/* --- 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_TRIGGER = 10, WDGT_ID_STATUS, WDGT_ID_DEMO,
    WDGT_ID_OK_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_MESSAGEBOX)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t     s_trigger;
static ugui_button_ext_t s_trigger_ext;
static ugui_widget_t     s_status;      /* live open/closed readout */
static ugui_label_ext_t  s_status_ext;
static ugui_widget_t     s_popup;
static ugui_msgbox_ext_t s_popup_ext;
static ugui_widget_t     s_ok_btn;
static ugui_button_ext_t s_ok_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_MESSAGEBOX)
static ugui_text_cfg_t   s_trigger_text;
static ugui_text_cfg_t   s_popup_title_text;
static ugui_text_cfg_t   s_popup_msg_text;
static ugui_text_cfg_t   s_ok_text;
#endif
#if defined(UGUI_WIDGET_ENABLE_MESSAGEBOX)
#define TRIGGER_W  180
#define TRIGGER_H  36
#define TRIGGER_X  ((int16_t)((UGUI_SCREEN_W - TRIGGER_W) / 2))
#define TRIGGER_Y  84
#endif

/* --- local helpers & event callbacks --- */
static void on_trigger(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    if (!ugui_widget_msgbox_is_open(&s_popup)) {
        ugui_widget_msgbox_open(&s_popup, NULL);   /* no overlay — stays minimal */
    }
}

static void on_ok(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    ugui_widget_msgbox_close(&s_popup, 0u);
#ifdef UGUI_ENABLE_FONT
    ugui_widget_label_update_text_str(&s_status, "Closed via OK");
#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_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_widget_button_init(&s_root, &s_trigger, &s_trigger_ext);
ugui_widget_set_area(&s_trigger, TRIGGER_X, TRIGGER_Y, TRIGGER_W, TRIGGER_H);
ugui_widget_set_id(&s_trigger, WDGT_ID_TRIGGER);
ugui_widget_button_set_style(&s_trigger, true, 5u, 1u, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_trigger_text, "Open dialog", &lexend_14pt_2bpp, UGUI_THEME_TEXT,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_button_set_text(&s_trigger, &s_trigger_text);
#endif
ugui_widget_set_event_cb(&s_trigger, on_trigger,
                         UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP
                         | UGUI_EMASK_KEY_CONFIRM);
ugui_screen_add_widget(&s_screen, &s_trigger);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_trigger);
#endif

add_label(&s_status, &s_status_ext, WDGT_ID_STATUS,
         0, (int16_t)(TRIGGER_Y + TRIGGER_H + 16), (int16_t)UGUI_SCREEN_W, 22,
         "Closed", UGUI_ALIGN_CENTER);

ugui_widget_msgbox_init(&s_root, &s_popup, &s_popup_ext, &s_screen);
ugui_widget_set_id(&s_popup, WDGT_ID_DEMO);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_popup_title_text, "Notice", &lexend_14pt_2bpp,
                   UGUI_THEME_KNOB, UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 0u);
ugui_text_cfg_init(&s_popup_msg_text, "Default style only.",
                   &lexend_14pt_2bpp, UGUI_THEME_TEXT,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 0u);
ugui_widget_msgbox_set_text(&s_popup, &s_popup_title_text, &s_popup_msg_text);
#endif

/* OK button: also left at its own default style (purple fill, no
 * ugui_widget_button_set_style() call) -- geometry is the only thing set,
 * since two identically-centred defaults would overlap. */
ugui_widget_button_init(&s_popup, &s_ok_btn, &s_ok_ext);
ugui_widget_set_area(&s_ok_btn, 60, 98, 100, 28);
ugui_widget_set_id(&s_ok_btn, WDGT_ID_OK_BTN);
ugui_widget_set_layer(&s_ok_btn, UGUI_LAYER_TOP);
ugui_widget_set_event_cb(&s_ok_btn, on_ok,
    UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP | UGUI_EMASK_KEY_CONFIRM);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_ok_text, "OK", &lexend_14pt_2bpp, UGUI_COLOR_WHITE,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_button_set_text(&s_ok_btn, &s_ok_text);
#endif
ugui_widget_msgbox_add_button(&s_popup, &s_ok_btn, 0u);
ugui_widget_set_visible(&s_ok_btn, false);   /* hidden until open() */
ugui_screen_add_widget(&s_screen, &s_ok_btn);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_ok_btn);
#endif

Open/Close Lifecycle

is_open() guards re-opening, a full-screen dimming overlay is passed to open(), and set_result_cb() fires exactly once per close — OK, Cancel, or a programmatic Force-close using the library's 0x7F "no button" sentinel — always with the correct button index.

Open/Close Lifecycle

/* --- 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_OPEN_BTN = 10, WDGT_ID_CLOSE_BTN, WDGT_ID_STATUS,
    WDGT_ID_DEMO, WDGT_ID_OK_BTN, WDGT_ID_CANCEL_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_MESSAGEBOX)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t     s_open_btn;
static ugui_button_ext_t s_open_ext;
static ugui_widget_t     s_close_btn;
static ugui_button_ext_t s_close_ext;
static ugui_widget_t     s_status;
static ugui_label_ext_t  s_status_ext;
static ugui_widget_t     s_overlay;
static ugui_box_ext_t    s_overlay_ext;
static ugui_widget_t     s_popup;
static ugui_msgbox_ext_t s_popup_ext;
static ugui_widget_t     s_ok_btn;
static ugui_button_ext_t s_ok_ext;
static ugui_widget_t     s_cancel_btn;
static ugui_button_ext_t s_cancel_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_MESSAGEBOX)
static ugui_text_cfg_t   s_open_text;
static ugui_text_cfg_t   s_close_text;
static ugui_text_cfg_t   s_popup_title_text;
static ugui_text_cfg_t   s_popup_msg_text;
static ugui_text_cfg_t   s_ok_text;
static ugui_text_cfg_t   s_cancel_text;
static char              s_status_buf[40];
#endif
#if defined(UGUI_WIDGET_ENABLE_MESSAGEBOX)
#define BTN_W      160
#define BTN_H      32
#define BTN_X0     ((int16_t)((UGUI_SCREEN_W / 2) - BTN_W - 6))
#define BTN_X1     ((int16_t)((UGUI_SCREEN_W / 2) + 6))
#define BTN_Y      84
#define POP_W      240
#define POP_H      140
#define POP_RADIUS 8u
#define POP_TITLE_H  28u
#define POP_BTN_H    40u
#endif

/* --- local helpers & event callbacks --- */
static void on_result(uint8_t btn_idx, void* ctx)
{
    (void)ctx;
#ifdef UGUI_ENABLE_FONT
    const char* label = (btn_idx == 0u) ? "OK"
                       : (btn_idx == 1u) ? "CANCEL"
                       : "programmatic (Force close)";
    (void)snprintf(s_status_buf, sizeof(s_status_buf), "Closed via: %s", label);
    ugui_widget_label_update_text_str(&s_status, s_status_buf);
#endif
    printf("[msgbox.states] on_result: btn_idx=%u\n", (unsigned)btn_idx);
}

static void on_open(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    if (!ugui_widget_msgbox_is_open(&s_popup)) {
        ugui_widget_msgbox_open(&s_popup, &s_overlay);
#ifdef UGUI_ENABLE_FONT
        ugui_widget_label_update_text_str(&s_status, "Open");
#endif
    }
}

static void on_force_close(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    if (ugui_widget_msgbox_is_open(&s_popup)) {
        ugui_widget_msgbox_close(&s_popup, 0x7Fu);
    }
}

static void on_ok(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event == UGUI_EVENT_CLICK) { ugui_widget_msgbox_close(&s_popup, 0u); }
}

static void on_cancel(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event == UGUI_EVENT_CLICK) { ugui_widget_msgbox_close(&s_popup, 1u); }
}

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);
#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_BG, str,
                                &lexend_14pt_2bpp, UGUI_THEME_TEXT, align, 1u);
    ugui_screen_add_widget(&s_screen, w);
}

/* --- build it (in your screen's init) --- */
add_button(&s_open_btn, &s_open_ext, &s_open_text, WDGT_ID_OPEN_BTN,
          BTN_X0, BTN_Y, BTN_W, BTN_H, UGUI_THEME_SURFACE,
          "Open (OK / Cancel)", on_open);
add_button(&s_close_btn, &s_close_ext, &s_close_text, WDGT_ID_CLOSE_BTN,
          BTN_X1, BTN_Y, BTN_W, BTN_H, ugui_color_blend(UGUI_THEME_SURFACE, UGUI_THEME_BG, 96u),
          "Force close", on_force_close);

add_label(&s_status, &s_status_ext, WDGT_ID_STATUS,
         0, (int16_t)(BTN_Y + BTN_H + 16), (int16_t)UGUI_SCREEN_W, 22,
         "Closed", UGUI_ALIGN_CENTER);

/* Full-screen dimming overlay (hidden until open()). */
ugui_widget_box_init(&s_root, &s_overlay, &s_overlay_ext);
ugui_widget_set_area(&s_overlay, 0, 0, (int16_t)UGUI_SCREEN_W, (int16_t)UGUI_SCREEN_H);
ugui_widget_box_set_style(&s_overlay, true, 0u, 0u, UGUI_THEME_BG, UGUI_THEME_BG);
ugui_widget_set_visible(&s_overlay, false);
ugui_screen_add_widget(&s_screen, &s_overlay);

ugui_widget_msgbox_init(&s_root, &s_popup, &s_popup_ext, &s_screen);
ugui_widget_set_id(&s_popup, WDGT_ID_DEMO);
ugui_widget_set_area(&s_popup, (int16_t)((UGUI_SCREEN_W - POP_W) / 2),
                     (int16_t)((UGUI_SCREEN_H - POP_H) / 2), POP_W, POP_H);
ugui_widget_msgbox_set_style(&s_popup,
    UGUI_THEME_SURFACE, UGUI_THEME_ACCENT, UGUI_THEME_ACCENT, ugui_color_blend(UGUI_THEME_SURFACE, UGUI_THEME_BG, 96u), UGUI_THEME_BG,
    POP_RADIUS, POP_TITLE_H, POP_BTN_H);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_popup_title_text, "Confirm", &lexend_14pt_2bpp,
                   UGUI_THEME_KNOB, UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 0u);
ugui_text_cfg_init(&s_popup_msg_text, "Discard unsaved changes?",
                   &lexend_14pt_2bpp, UGUI_THEME_TEXT,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 0u);
ugui_widget_msgbox_set_text(&s_popup, &s_popup_title_text, &s_popup_msg_text);
#endif
ugui_widget_msgbox_set_result_cb(&s_popup, on_result, NULL);

ugui_widget_button_init(&s_popup, &s_ok_btn, &s_ok_ext);
ugui_widget_set_area(&s_ok_btn, 16, (int16_t)(POP_H - POP_BTN_H + 6), 96, 30);
ugui_widget_set_id(&s_ok_btn, WDGT_ID_OK_BTN);
ugui_widget_set_layer(&s_ok_btn, UGUI_LAYER_TOP);
ugui_widget_button_set_style(&s_ok_btn, true, 6u, 1u, UGUI_THEME_ACCENT, UGUI_THEME_ACCENT);
ugui_widget_set_event_cb(&s_ok_btn, on_ok,
    UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP | UGUI_EMASK_KEY_CONFIRM);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_ok_text, "OK", &lexend_14pt_2bpp, UGUI_THEME_KNOB,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_button_set_text(&s_ok_btn, &s_ok_text);
#endif
ugui_widget_msgbox_add_button(&s_popup, &s_ok_btn, 0u);
ugui_widget_set_visible(&s_ok_btn, false);
ugui_screen_add_widget(&s_screen, &s_ok_btn);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_ok_btn);
#endif

ugui_widget_button_init(&s_popup, &s_cancel_btn, &s_cancel_ext);
ugui_widget_set_area(&s_cancel_btn, (int16_t)(POP_W - 16 - 96),
                     (int16_t)(POP_H - POP_BTN_H + 6), 96, 30);
ugui_widget_set_id(&s_cancel_btn, WDGT_ID_CANCEL_BTN);
ugui_widget_set_layer(&s_cancel_btn, UGUI_LAYER_TOP);
ugui_widget_button_set_style(&s_cancel_btn, true, 6u, 1u, ugui_color_blend(UGUI_THEME_SURFACE, UGUI_THEME_BG, 96u), ugui_color_blend(UGUI_THEME_SURFACE, UGUI_THEME_BG, 96u));
ugui_widget_set_event_cb(&s_cancel_btn, on_cancel,
    UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP | UGUI_EMASK_KEY_CONFIRM);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_cancel_text, "Cancel", &lexend_14pt_2bpp, UGUI_THEME_TEXT,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_button_set_text(&s_cancel_btn, &s_cancel_text);
#endif
ugui_widget_msgbox_add_button(&s_popup, &s_cancel_btn, 1u);
ugui_widget_set_visible(&s_cancel_btn, false);
ugui_screen_add_widget(&s_screen, &s_cancel_btn);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_cancel_btn);
#endif

Colour Presets (one popup, restyled)

One popup, restyled with set_style() — every colour, the radius and both bar heights in a single call — right before each open(), proving style is a live, re-appliable property rather than something fixed at init.

Colour Presets (one popup, restyled)

/* --- 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_INFO_BTN = 10, WDGT_ID_SUCCESS_BTN, WDGT_ID_ALERT_BTN,
    WDGT_ID_DEMO, WDGT_ID_OK_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_MESSAGEBOX)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t     s_info_btn;
static ugui_button_ext_t s_info_ext;
static ugui_widget_t     s_success_btn;
static ugui_button_ext_t s_success_ext;
static ugui_widget_t     s_alert_btn;
static ugui_button_ext_t s_alert_ext;
static ugui_widget_t     s_popup;
static ugui_msgbox_ext_t s_popup_ext;
static ugui_widget_t     s_ok_btn;
static ugui_button_ext_t s_ok_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_MESSAGEBOX)
static ugui_text_cfg_t   s_info_text;
static ugui_text_cfg_t   s_success_text;
static ugui_text_cfg_t   s_alert_text;
static ugui_text_cfg_t   s_popup_title_text;
static ugui_text_cfg_t   s_popup_msg_text;
static ugui_text_cfg_t   s_ok_text;
#endif
#if defined(UGUI_WIDGET_ENABLE_MESSAGEBOX)
#define BTN_W   130
#define BTN_H   32
#define BTN_GAP 10
#define ROW_Y   88
#define BTN_X0  ((int16_t)((UGUI_SCREEN_W - (3 * BTN_W + 2 * BTN_GAP)) / 2))
#define BTN_X1  ((int16_t)(BTN_X0 + BTN_W + BTN_GAP))
#define BTN_X2  ((int16_t)(BTN_X1 + BTN_W + BTN_GAP))
#define POP_W   240
#define POP_H   130
#define POP_TITLE_H  28u
#define POP_BTN_H    36u
typedef enum { PRESET_INFO = 0, PRESET_SUCCESS, PRESET_ALERT } preset_t;
#endif

/* --- local helpers & event callbacks --- */
static void apply_preset(preset_t p)
{
    ugui_color_t panel, titlebar, border;
    uint8_t      radius;
    const char*  title;
    const char*  msg;

    switch (p) {
        case PRESET_SUCCESS:
            panel = UGUI_THEME_SURFACE; titlebar = UGUI_THEME_OK; border = UGUI_THEME_OK;
            radius = 16u; title = "Success"; msg = "Changes saved."; break;
        case PRESET_ALERT:
            panel = UGUI_THEME_SURFACE; titlebar = UGUI_THEME_CANCEL; border = UGUI_THEME_CANCEL;
            radius = 0u; title = "Alert"; msg = "Connection lost."; break;
        case PRESET_INFO:
        default:
            panel = UGUI_THEME_SURFACE; titlebar = UGUI_THEME_ACCENT; border = UGUI_THEME_ACCENT;
            radius = 8u; title = "Info"; msg = "New firmware is available."; break;
    }

    ugui_widget_msgbox_set_style(&s_popup, panel, titlebar, border, ugui_color_blend(UGUI_THEME_SURFACE, UGUI_THEME_BG, 96u), UGUI_THEME_BG,
                                 radius, POP_TITLE_H, POP_BTN_H);
#ifdef UGUI_ENABLE_FONT
    ugui_text_cfg_init(&s_popup_title_text, title, &lexend_14pt_2bpp, UGUI_THEME_KNOB,
                       UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 0u);
    ugui_text_cfg_init(&s_popup_msg_text, msg, &lexend_14pt_2bpp, UGUI_THEME_TEXT,
                       UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 0u);
    ugui_widget_msgbox_set_text(&s_popup, &s_popup_title_text, &s_popup_msg_text);
#endif
}

static void on_info(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    if (!ugui_widget_msgbox_is_open(&s_popup)) { apply_preset(PRESET_INFO); ugui_widget_msgbox_open(&s_popup, NULL); }
}

static void on_success(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    if (!ugui_widget_msgbox_is_open(&s_popup)) { apply_preset(PRESET_SUCCESS); ugui_widget_msgbox_open(&s_popup, NULL); }
}

static void on_alert(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    if (!ugui_widget_msgbox_is_open(&s_popup)) { apply_preset(PRESET_ALERT); ugui_widget_msgbox_open(&s_popup, NULL); }
}

static void on_ok(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event == UGUI_EVENT_CLICK) { ugui_widget_msgbox_close(&s_popup, 0u); }
}

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);
#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) --- */
add_button(&s_info_btn, &s_info_ext, &s_info_text, WDGT_ID_INFO_BTN,
          BTN_X0, ROW_Y, BTN_W, BTN_H, UGUI_THEME_ACCENT, "Info", on_info);
add_button(&s_success_btn, &s_success_ext, &s_success_text, WDGT_ID_SUCCESS_BTN,
          BTN_X1, ROW_Y, BTN_W, BTN_H, UGUI_THEME_OK, "Success", on_success);
add_button(&s_alert_btn, &s_alert_ext, &s_alert_text, WDGT_ID_ALERT_BTN,
          BTN_X2, ROW_Y, BTN_W, BTN_H, UGUI_THEME_CANCEL, "Alert", on_alert);

ugui_widget_msgbox_init(&s_root, &s_popup, &s_popup_ext, &s_screen);
ugui_widget_set_id(&s_popup, WDGT_ID_DEMO);
ugui_widget_set_area(&s_popup, (int16_t)((UGUI_SCREEN_W - POP_W) / 2),
                     (int16_t)((UGUI_SCREEN_H - POP_H) / 2), POP_W, POP_H);
apply_preset(PRESET_INFO);   /* initial style before the first open() */

ugui_widget_button_init(&s_popup, &s_ok_btn, &s_ok_ext);
ugui_widget_set_area(&s_ok_btn, (int16_t)((POP_W - 100) / 2),
                     (int16_t)(POP_H - (int16_t)POP_BTN_H + 6), 100, 28);
ugui_widget_set_id(&s_ok_btn, WDGT_ID_OK_BTN);
ugui_widget_set_layer(&s_ok_btn, UGUI_LAYER_TOP);
ugui_widget_set_event_cb(&s_ok_btn, on_ok,
    UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP | UGUI_EMASK_KEY_CONFIRM);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_ok_text, "OK", &lexend_14pt_2bpp, UGUI_COLOR_WHITE,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_button_set_text(&s_ok_btn, &s_ok_text);
#endif
ugui_widget_msgbox_add_button(&s_popup, &s_ok_btn, 0u);
ugui_widget_set_visible(&s_ok_btn, false);
ugui_screen_add_widget(&s_screen, &s_ok_btn);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_ok_btn);
#endif

set_content() swaps the popup body for a live slider — OK reads the slider's current value back into the app, Cancel discards it, mirroring the edit/commit/discard pattern a text-field popup uses but with a slider as the content widget.

Popup Form (embedded slider)

/* --- 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_TRIGGER = 10, WDGT_ID_STATUS, WDGT_ID_DEMO,
    WDGT_ID_SLIDER, WDGT_ID_OK_BTN, WDGT_ID_CANCEL_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_MESSAGEBOX) && defined(UGUI_WIDGET_ENABLE_SLIDER)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t     s_trigger;
static ugui_button_ext_t s_trigger_ext;
static ugui_widget_t     s_status;
static ugui_label_ext_t  s_status_ext;
static ugui_widget_t     s_overlay;
static ugui_box_ext_t    s_overlay_ext;
static ugui_widget_t     s_popup;
static ugui_msgbox_ext_t s_popup_ext;
static ugui_widget_t     s_slider;
static ugui_slider_ext_t s_slider_ext;
static ugui_widget_t     s_ok_btn;
static ugui_button_ext_t s_ok_ext;
static ugui_widget_t     s_cancel_btn;
static ugui_button_ext_t s_cancel_ext;
#endif
#if defined(UGUI_WIDGET_ENABLE_MESSAGEBOX) && defined(UGUI_WIDGET_ENABLE_SLIDER)
static ugui_text_cfg_t   s_trigger_text;
static ugui_text_cfg_t   s_popup_title_text;
static ugui_text_cfg_t   s_ok_text;
static ugui_text_cfg_t   s_cancel_text;
static char              s_status_buf[24];
#endif
#if defined(UGUI_WIDGET_ENABLE_MESSAGEBOX) && defined(UGUI_WIDGET_ENABLE_SLIDER)
static int16_t s_applied_volume = 50;
#define TRIGGER_W  180
#define TRIGGER_H  36
#define TRIGGER_X  ((int16_t)((UGUI_SCREEN_W - TRIGGER_W) / 2))
#define TRIGGER_Y  84
#define POP_W        260
#define POP_H        130
#define POP_TITLE_H  26u
#define POP_BTN_H    40u
#define SLD_Y        ((int16_t)(POP_TITLE_H + 10u))
#define SLD_H        40
#endif

/* --- local helpers & event callbacks --- */
static void update_status(void)
{
#ifdef UGUI_ENABLE_FONT
    (void)snprintf(s_status_buf, sizeof(s_status_buf), "Volume: %d %%", (int)s_applied_volume);
    ugui_widget_label_update_text_str(&s_status, s_status_buf);
#endif
}

static void on_trigger(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    if (!ugui_widget_msgbox_is_open(&s_popup)) {
        ugui_widget_slider_set_value(&s_slider, s_applied_volume);  /* pre-select last value */
        ugui_widget_msgbox_open(&s_popup, &s_overlay);
    }
}

static void on_ok(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    s_applied_volume = ugui_widget_slider_get_value(&s_slider);
    printf("[msgbox.settings] OK: volume=%d\n", (int)s_applied_volume);
    update_status();
    ugui_widget_msgbox_close(&s_popup, 0u);
}

static void on_cancel(uint8_t widget_id, ugui_event_t event)
{
    (void)widget_id;
    if (event != UGUI_EVENT_CLICK) { return; }
    printf("[msgbox.settings] CANCEL: volume unchanged\n");
    ugui_widget_msgbox_close(&s_popup, 1u);
}

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_BG, str,
                                &lexend_14pt_2bpp, UGUI_THEME_TEXT, align, 1u);
    ugui_screen_add_widget(&s_screen, w);
}

/* --- build it (in your screen's init) --- */
add_button(&s_trigger, &s_trigger_ext, &s_trigger_text, WDGT_ID_TRIGGER,
          TRIGGER_X, TRIGGER_Y, TRIGGER_W, TRIGGER_H, "Set volume", on_trigger);

#ifdef UGUI_ENABLE_FONT
(void)snprintf(s_status_buf, sizeof(s_status_buf), "Volume: %d %%", (int)s_applied_volume);
#endif
add_label(&s_status, &s_status_ext, WDGT_ID_STATUS,
         0, (int16_t)(TRIGGER_Y + TRIGGER_H + 16), (int16_t)UGUI_SCREEN_W, 22,
         s_status_buf, UGUI_ALIGN_CENTER);

ugui_widget_box_init(&s_root, &s_overlay, &s_overlay_ext);
ugui_widget_set_area(&s_overlay, 0, 0, (int16_t)UGUI_SCREEN_W, (int16_t)UGUI_SCREEN_H);
ugui_widget_box_set_style(&s_overlay, true, 0u, 0u, UGUI_THEME_BG, UGUI_THEME_BG);
ugui_widget_set_visible(&s_overlay, false);
ugui_screen_add_widget(&s_screen, &s_overlay);

ugui_widget_msgbox_init(&s_root, &s_popup, &s_popup_ext, &s_screen);
ugui_widget_set_id(&s_popup, WDGT_ID_DEMO);
ugui_widget_set_area(&s_popup, (int16_t)((UGUI_SCREEN_W - POP_W) / 2),
                     (int16_t)((UGUI_SCREEN_H - POP_H) / 2), POP_W, POP_H);
ugui_widget_msgbox_set_style(&s_popup, UGUI_THEME_SURFACE, UGUI_THEME_ACCENT, UGUI_THEME_ACCENT,
                             ugui_color_blend(UGUI_THEME_SURFACE, UGUI_THEME_BG, 96u), UGUI_THEME_BG, 10u, POP_TITLE_H, POP_BTN_H);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_popup_title_text, "Volume", &lexend_14pt_2bpp,
                   UGUI_THEME_KNOB, UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 0u);
ugui_widget_msgbox_set_text(&s_popup, &s_popup_title_text, NULL);
#endif

ugui_widget_slider_init(&s_popup, &s_slider, &s_slider_ext);
ugui_widget_set_area(&s_slider, 16, SLD_Y, (int16_t)(POP_W - 32), SLD_H);
ugui_widget_set_id(&s_slider, WDGT_ID_SLIDER);
ugui_widget_set_layer(&s_slider, UGUI_LAYER_TOP);
s_slider_ext.show_labels = 1u;
#ifdef UGUI_ENABLE_FONT
s_slider_ext.label_font  = &lexend_14pt_2bpp;
#endif
ugui_widget_slider_set_range(&s_slider, 0, 100);
ugui_widget_slider_set_value(&s_slider, s_applied_volume);
ugui_widget_slider_set_step(&s_slider, 5);
ugui_widget_set_event_cb(&s_slider, NULL,
    UGUI_EMASK_SLIDE | UGUI_EMASK_DOWN | UGUI_EMASK_UP
    | UGUI_EMASK_KEY_LEFT | UGUI_EMASK_KEY_RIGHT);
ugui_widget_msgbox_set_content(&s_popup, &s_slider);
ugui_widget_set_visible(&s_slider, false);
ugui_screen_add_widget(&s_screen, &s_slider);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_slider);
#endif

ugui_widget_button_init(&s_popup, &s_ok_btn, &s_ok_ext);
ugui_widget_set_area(&s_ok_btn, 16, (int16_t)(POP_H - (int16_t)POP_BTN_H + 6),
                     (int16_t)((POP_W / 2) - 22), 30);
ugui_widget_set_id(&s_ok_btn, WDGT_ID_OK_BTN);
ugui_widget_set_layer(&s_ok_btn, UGUI_LAYER_TOP);
ugui_widget_button_set_style(&s_ok_btn, true, 6u, 1u, UGUI_THEME_OK, UGUI_THEME_OK);
ugui_widget_set_event_cb(&s_ok_btn, on_ok,
    UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP | UGUI_EMASK_KEY_CONFIRM);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_ok_text, "OK", &lexend_14pt_2bpp, UGUI_THEME_KNOB,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_button_set_text(&s_ok_btn, &s_ok_text);
#endif
ugui_widget_msgbox_add_button(&s_popup, &s_ok_btn, 0u);
ugui_widget_set_visible(&s_ok_btn, false);
ugui_screen_add_widget(&s_screen, &s_ok_btn);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_ok_btn);
#endif

ugui_widget_button_init(&s_popup, &s_cancel_btn, &s_cancel_ext);
ugui_widget_set_area(&s_cancel_btn, (int16_t)((POP_W / 2) + 6),
                     (int16_t)(POP_H - (int16_t)POP_BTN_H + 6),
                     (int16_t)((POP_W / 2) - 22), 30);
ugui_widget_set_id(&s_cancel_btn, WDGT_ID_CANCEL_BTN);
ugui_widget_set_layer(&s_cancel_btn, UGUI_LAYER_TOP);
ugui_widget_button_set_style(&s_cancel_btn, true, 6u, 1u, ugui_color_blend(UGUI_THEME_SURFACE, UGUI_THEME_BG, 96u), ugui_color_blend(UGUI_THEME_SURFACE, UGUI_THEME_BG, 96u));
ugui_widget_set_event_cb(&s_cancel_btn, on_cancel,
    UGUI_EMASK_CLICK | UGUI_EMASK_DOWN | UGUI_EMASK_UP | UGUI_EMASK_KEY_CONFIRM);
#ifdef UGUI_ENABLE_FONT
ugui_text_cfg_init(&s_cancel_text, "Cancel", &lexend_14pt_2bpp, UGUI_THEME_TEXT,
                   UGUI_ALIGN_CENTER, (ugui_point_t){ 0, 0 }, 1u);
ugui_widget_button_set_text(&s_cancel_btn, &s_cancel_text);
#endif
ugui_widget_msgbox_add_button(&s_popup, &s_cancel_btn, 1u);
ugui_widget_set_visible(&s_cancel_btn, false);
ugui_screen_add_widget(&s_screen, &s_cancel_btn);
#if UGUI_ENABLE_KEYPAD
ugui_screen_add_focus(&s_screen, &s_cancel_btn);
#endif


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.