AlphaNum Keyboard Widget¶
The AlphaNum keyboard is a full QWERTY text-entry widget — a themed
4×10 key grid across three pages (lower / upper / symbols) for
name/label/search fields. Init installs the whole look (theme-tracked
SURFACE letter keys, an ACCENT Enter, a press highlight) and the built-in
3-page layout at a settable size; set_font() gives it text and, in its
default TEXT mode, the widget collects typed characters into its OWN
buffer — set_text_callbacks() wires a preview (fires after every key) and
a commit (fires on Enter/OK). enable_header_preview() /
enable_ok_cancel() add the same built-in editor chrome as the numpad, and
ugui_alphanum_popup_open() raises it as a modal "tap a text field" editor
exactly like the numpad's popup mode. An app can also replace the ENTIRE
layout — set_layouts() swaps in its own ugui_alphanum_key_t tables (a
different language, a PIN pad, a phone dialer) and set_colors() its own
fixed palette, all from application code, no library file touched.
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 Keyboard¶
A bare QWERTY keyboard exactly as ugui_widget_alphanum_init() delivers it: every colour comes from the theme roles and the built-in dark/light label set is picked automatically. Text mode is on by default, so the widget collects typed characters into its own buffer; a preview callback echoes that buffer into the readout row.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_DEMO = 10
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_readout;
#endif
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static char s_readout_str[40];
#endif
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static ugui_widget_t s_kbd;
static ugui_alphanum_keyboard_ext_t s_kbd_ext;
#define RD_Y (38)
#define RD_H 26
#define KB_X 6
#define KB_Y (RD_Y + RD_H + 6)
#define KB_W (UGUI_SCREEN_W - 12)
#define KB_H (UGUI_SCREEN_H - KB_Y - 6)
#endif
/* --- local helpers & event callbacks --- */
static void on_preview(const char* text, void* ctx)
{
(void)ctx;
#ifdef UGUI_ENABLE_FONT
(void)snprintf(s_readout_str, sizeof(s_readout_str), "%s",
(text[0] != '\0') ? text : "type here...");
ugui_widget_invalidate(&s_readout);
#endif
printf("[alphanum.default] text \"%s\"\n", text);
}
/* --- build it (in your screen's init) --- */
ugui_widget_alphanum_init(&s_root, &s_kbd, &s_kbd_ext);
ugui_widget_set_area(&s_kbd, KB_X, KB_Y, (int16_t)KB_W, (int16_t)KB_H);
ugui_widget_set_id(&s_kbd, WDGT_ID_DEMO);
#ifdef UGUI_ENABLE_FONT
ugui_widget_alphanum_set_font(&s_kbd, &lexend_14pt_2bpp);
#endif
ugui_widget_alphanum_set_text_callbacks(&s_kbd, on_preview, NULL, NULL);
ugui_screen_add_widget(&s_screen, &s_kbd);
Built-In Header, Preview & OK/Cancel¶
enable_header_preview() draws a title bar and a live preview of the edit buffer; enable_ok_cancel() adds the themed OK/Cancel row. In non-popup mode (bg_root = NULL) the widget still does ALL the editing via its built-in text mode — the app only wires OK/Cancel: OK copies the text to the readout, Cancel clears the entry.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_DEMO = 10
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_readout;
#endif
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static char s_readout_str[40];
#endif
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static ugui_widget_t s_kbd;
static ugui_alphanum_keyboard_ext_t s_kbd_ext;
#define RD_Y (38)
#define RD_H 24
#define KB_X 6
#define KB_Y (RD_Y + RD_H + 6)
#define KB_W (UGUI_SCREEN_W - 12)
#define KB_H (UGUI_SCREEN_H - KB_Y - 6)
#endif
/* --- local helpers & event callbacks --- */
static void show_committed(const char* text)
{
#ifdef UGUI_ENABLE_FONT
(void)snprintf(s_readout_str, sizeof(s_readout_str), "Committed: %s",
(text[0] != '\0') ? text : "-");
ugui_widget_invalidate(&s_readout);
#else
(void)text;
#endif
}
static void on_commit(const char* text, void* ctx)
{
(void)ctx;
show_committed(text);
printf("[alphanum.entry] commit \"%s\"\n", text);
}
static void on_ok(void* ctx)
{
(void)ctx;
show_committed(ugui_widget_alphanum_get_text(&s_kbd));
}
static void on_cancel(void* ctx)
{
(void)ctx;
ugui_widget_alphanum_set_text(&s_kbd, ""); /* clear the entry */
printf("[alphanum.entry] Cancel — entry cleared\n");
}
/* --- build it (in your screen's init) --- */
ugui_widget_alphanum_init(&s_root, &s_kbd, &s_kbd_ext);
ugui_widget_set_area(&s_kbd, KB_X, KB_Y, (int16_t)KB_W, (int16_t)KB_H);
ugui_widget_set_id(&s_kbd, WDGT_ID_DEMO);
#ifdef UGUI_ENABLE_FONT
ugui_widget_alphanum_set_font(&s_kbd, &lexend_14pt_2bpp);
#endif
ugui_widget_alphanum_set_text_callbacks(&s_kbd, NULL, on_commit, NULL);
ugui_widget_alphanum_enable_header_preview(&s_kbd, 18u, 24u, "Enter name");
/* bg_root = NULL keeps the widget visible (no popup); the app owns OK/Cancel. */
ugui_widget_alphanum_enable_ok_cancel(&s_kbd, 26u, NULL);
s_kbd_ext.on_ok = on_ok;
s_kbd_ext.on_cancel = on_cancel;
ugui_screen_add_widget(&s_screen, &s_kbd);
Tap-to-Edit Popup Form¶
Two text form fields (name/email); tapping one calls ugui_alphanum_popup_open() — the keyboard appears over the form, pre-filled with the field's current text. The built-in popup handlers do ALL the editing: OK copies the text back into the field and repaints it, Cancel discards.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_DEMO = 10
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD) && defined(UGUI_WIDGET_ENABLE_TEXTEDIT)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_kbd;
static ugui_alphanum_keyboard_ext_t s_kbd_ext;
#define POP_X 4
#define POP_Y 16
#define POP_W (UGUI_SCREEN_W - 8)
#define POP_H (UGUI_SCREEN_H - 28)
#endif
/* --- build it (in your screen's init) --- */
ugui_widget_alphanum_init(NULL, &s_kbd, &s_kbd_ext);
ugui_widget_set_area(&s_kbd, POP_X, POP_Y, (int16_t)POP_W, (int16_t)POP_H);
ugui_widget_set_id(&s_kbd, WDGT_ID_DEMO);
#ifdef UGUI_ENABLE_FONT
ugui_widget_alphanum_set_font(&s_kbd, &lexend_14pt_2bpp);
#endif
ugui_widget_alphanum_enable_header_preview(&s_kbd, 20u, 26u, "Edit field");
ugui_widget_alphanum_enable_ok_cancel(&s_kbd, 30u, &s_root);
ugui_screen_add_widget(&s_screen, &s_kbd);
Fully App-Defined Keyboard (QWERTZ)¶
The template for "I want a different keyboard": a German QWERTZ layout built entirely in application code — flash const key tables, a 3-page layout list, and a fixed palette via set_colors() — handed to the widget with set_layouts(). No uGui library file is touched; Z sits in the top row (tap it here) where Y would be on QWERTY.

/* --- widget IDs: only need to be unique within the screen (10+ leaves
* room for the IDs your app already uses, e.g. the root's) --- */
enum {
WDGT_ID_DEMO = 10
};
/* --- fonts: each is DEFINED in inc/fonts/<name>.h — #include that
* header in exactly one .c of your app; elsewhere just: --- */
extern const ugui_font_t lexend_14pt_2bpp;
/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static ugui_screen_t s_screen;
static ugui_widget_t s_root;
static ugui_widget_t s_readout;
#endif
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static char s_readout_str[40];
#endif
#if defined(UGUI_WIDGET_ENABLE_ALPHANUM_KEYBOARD)
static ugui_widget_t s_kbd;
static ugui_alphanum_keyboard_ext_t s_kbd_ext;
#if defined(UGUI_COLOR_FORMAT_RGB565)
#define LBL_WHITE { .value = (uint16_t)0xFFFFu }
#else
#define LBL_WHITE { .value = 0xFFFFFFFFu }
#endif
#define KW(lbl_, code_) { (lbl_), LBL_WHITE, (uint32_t)(code_) }
#define KSP KW(NULL, UGUI_ALNUM_KEY_SPACE)
#define KROW3_TAIL KW(",",','), KSP, KSP, KSP, KSP, KW(".",'.'), \
KW("Enter", UGUI_ALNUM_KEY_ENTER), KW(NULL, UGUI_ALNUM_KEY_ENTER)
static const ugui_alphanum_key_t de_lower[40] = {
/* Row 0: QWERTZ — note Z here, not Y */
KW("q",'q'), KW("w",'w'), KW("e",'e'), KW("r",'r'), KW("t",'t'),
KW("z",'z'), KW("u",'u'), KW("i",'i'), KW("o",'o'), KW("p",'p'),
/* Row 1 */
KW("a",'a'), KW("s",'s'), KW("d",'d'), KW("f",'f'), KW("g",'g'),
KW("h",'h'), KW("j",'j'), KW("k",'k'), KW("l",'l'), KW("'",'\''),
/* Row 2: shift | y x c v b n m | wide backspace (Y lives here) */
KW("aA", UGUI_ALNUM_KEY_SHIFT),
KW("y",'y'), KW("x",'x'), KW("c",'c'), KW("v",'v'),
KW("b",'b'), KW("n",'n'), KW("m",'m'),
KW("<", UGUI_ALNUM_KEY_BACKSPACE), KW(NULL, UGUI_ALNUM_KEY_BACKSPACE),
/* Row 3: [1#×2] , [space×4] . [Enter×2] */
KW("1#", UGUI_ALNUM_KEY_LAYOUT_SYMBOL), KW(NULL, UGUI_ALNUM_KEY_LAYOUT_SYMBOL),
KROW3_TAIL,
};
static const ugui_alphanum_key_t de_upper[40] = {
KW("Q",'Q'), KW("W",'W'), KW("E",'E'), KW("R",'R'), KW("T",'T'),
KW("Z",'Z'), KW("U",'U'), KW("I",'I'), KW("O",'O'), KW("P",'P'),
KW("A",'A'), KW("S",'S'), KW("D",'D'), KW("F",'F'), KW("G",'G'),
KW("H",'H'), KW("J",'J'), KW("K",'K'), KW("L",'L'), KW("'",'\''),
KW("Aa", UGUI_ALNUM_KEY_SHIFT),
KW("Y",'Y'), KW("X",'X'), KW("C",'C'), KW("V",'V'),
KW("B",'B'), KW("N",'N'), KW("M",'M'),
KW("<", UGUI_ALNUM_KEY_BACKSPACE), KW(NULL, UGUI_ALNUM_KEY_BACKSPACE),
KW("1#", UGUI_ALNUM_KEY_LAYOUT_SYMBOL), KW(NULL, UGUI_ALNUM_KEY_LAYOUT_SYMBOL),
KROW3_TAIL,
};
static const ugui_alphanum_key_t de_symbols[40] = {
KW("1",'1'), KW("2",'2'), KW("3",'3'), KW("4",'4'), KW("5",'5'),
KW("6",'6'), KW("7",'7'), KW("8",'8'), KW("9",'9'), KW("0",'0'),
KW("@",'@'), KW("#",'#'), KW("$",'$'), KW("%",'%'), KW("&",'&'),
KW("*",'*'), KW("(",'('), KW(")",')'), KW("-",'-'), KW("+",'+'),
KW("!",'!'), KW("?",'?'), KW("/",'/'), KW("=",'='), KW(":",':'),
KW(";",';'), KW("_",'_'), KW("\"",'"'),
KW("<", UGUI_ALNUM_KEY_BACKSPACE), KW(NULL, UGUI_ALNUM_KEY_BACKSPACE),
KW("abc", UGUI_ALNUM_KEY_LAYOUT_LOWER), KW(NULL, UGUI_ALNUM_KEY_LAYOUT_LOWER),
KROW3_TAIL,
};
static const ugui_alphanum_layout_t de_pages[3] = {
{ de_lower, 4u, 10u },
{ de_upper, 4u, 10u },
{ de_symbols, 4u, 10u },
};
#define KB_X 6
#define KB_Y (38 + 32)
#define KB_W (UGUI_SCREEN_W - 12)
#define KB_H (UGUI_SCREEN_H - KB_Y - 6)
#endif
/* --- local helpers & event callbacks --- */
static void on_preview(const char* text, void* ctx)
{
(void)ctx;
#ifdef UGUI_ENABLE_FONT
(void)snprintf(s_readout_str, sizeof(s_readout_str), "%s",
(text[0] != '\0') ? text : "QWERTZ - type here...");
ugui_widget_invalidate(&s_readout);
#endif
printf("[alphanum.custom] text \"%s\"\n", text);
}
/* --- build it (in your screen's init) --- */
ugui_widget_alphanum_init(&s_root, &s_kbd, &s_kbd_ext);
ugui_widget_set_area(&s_kbd, KB_X, KB_Y, (int16_t)KB_W, (int16_t)KB_H);
ugui_widget_set_id(&s_kbd, WDGT_ID_DEMO);
#ifdef UGUI_ENABLE_FONT
ugui_widget_alphanum_set_font(&s_kbd, &lexend_14pt_2bpp);
#endif
/* 1) the app's own key tables (German QWERTZ, 3 pages) */
ugui_widget_alphanum_set_layouts(&s_kbd, de_pages, 3u, 0u);
/* 2) the app's own fixed palette — theme-independent, always readable */
ugui_widget_alphanum_set_colors(&s_kbd,
ugui_color_hex(0x2B2F3Au), /* key face */
ugui_color_hex(0x3A4150u), /* border */
ugui_color_hex(0x4C6EF5u)); /* pressed / Enter */
s_kbd.color = ugui_color_hex(0x1E2230u); /* panel gap fill */
/* 3) the app owns key handling (built-in text mode still fills text_buf) */
ugui_widget_alphanum_set_text_callbacks(&s_kbd, on_preview, NULL, NULL);
ugui_screen_add_widget(&s_screen, &s_kbd);
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.