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.

/* --- 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.

/* --- 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.

/* --- 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
Popup Form (embedded slider)¶
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.

/* --- 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.