UI Components
Ready-made widgets - buttons, meters, frames, images - drawn in the firmware's style
These sit one level above Graphics API: each is a small pile of primitives, so a tapp looks like it belongs on the device. They take the same gfx_t* and obey the same clip state; ui_draw_frame() and ui_draw_scrollbar() draw in colour 1 regardless of the current colour.
Sizes are mostly fixed by the design rather than by arguments - ui_draw_scrollbar()'s track is always 8px wide - so x/y place a known shape rather than defining one. Where a widget does take w/h, they are the outer bounds.
Images and animations
gfx_img_t comes out of the asset pipeline: drop a PNG or GIF in the tapp's assets/ folder and tapp-build compiles it into a gfx_img_t plus an extern declaration in the generated assets header. A GIF becomes a multi-frame image; ui_draw_anim() takes the frame index directly and wraps it, so drive it from os_tick_get() divided down to the desired frame rate - and return true from tick() while the animation is playing; otherwise no frames are scheduled for it. (Not ui_get_frame(): that counts paints, so it stalls as soon as frames stop being requested.)
ui_draw_img(gfx, 20, 20, &asset_logo);
ui_draw_anim(gfx, 120, 60, (uint16_t)(os_tick_get() / 80), &asset_dance_party);
ui_hints_set((const tapp_hint_cell_t[HintsActionsTotal]){[HintPress1] = {"play", gfx_icon(GfxIconPlay)}});| GfxIconPlay | 0 | play triangle |
| GfxIconPause | 1 | pause bars |
| GfxIconRec | 2 | record dot |
| GfxIconPlayRev | 3 | reverse play |
| GfxIconRewind | 4 | rewind, double chevron |
| GfxIconFwd | 5 | fast forward |
| GfxIconRev | 6 | fast reverse |
| GfxIconLoopStart | 7 | loop in point |
| GfxIconLoopEnd | 8 | loop out point |
| GfxIconVolume | 9 | speaker |
| GfxIconVolumeMute | 10 | speaker, muted |
| GfxIconSos | 11 | sound-on-sound |
| GfxIconMic | 12 | microphone |
| GfxIconJack | 13 | line jack |
| GfxIconFx | 14 | effects |
| GfxIconWet | 15 | wet |
| GfxIconSpeed | 16 | varispeed |
| GfxIconChain | 17 | link |
| GfxIconBpm | 18 | tempo |
| GfxIconApply | 19 | tick |
| GfxIconBack | 20 | back arrow |
| GfxIconAdd | 21 | plus |
| GfxIconRemove | 22 | cross |
| GfxIconErase | 23 | erase |
| GfxIconEdit | 24 | edit |
| GfxIconCut | 25 | cut |
| GfxIconCopy | 26 | copy |
| GfxIconPaste | 27 | paste |
| GfxIconSplit | 28 | split |
| GfxIconBookmark | 29 | mark / save |
| GfxIconRedo | 30 | redo |
| GfxIconMenu | 31 | hamburger |
| GfxIconSettings | 32 | cog |
| GfxIconPower | 33 | power |
| GfxIconRandom | 34 | random |
| GfxIconTotal | 35 |
One of the system glyphs
| gfx_icon_id_t | id | Which glyph |
Returns — A firmware image, or NULL for an id this firmware does not know
| GfxAnimLoopToPlay | 0 | |
| GfxAnimTotal | 1 | how many this header knows - NOT an animation |
One of the system animations
| gfx_anim_id_t | id | Which animation |
Returns — A firmware image with `frame_count > 1`, or NULL for an id this firmware does not know
ui_draw_anim(gfx, x, y, m->loop_frame, gfx_anim(GfxAnimLoopToPlay));
Draw the first frame of an image at (x, y)
| gfx_t* | gfx | Graphics context |
| gfx_coord_t | x | Left edge |
| gfx_coord_t | y | Top edge |
| const gfx_img_t* | img | Image from the asset pipeline |
Note — Transparent like gfx_draw_xbm(): 1 bits in the current colour, 0 bits left alone (gfx_set_bitmap_mode() makes it opaque).
Draw one frame of a multi-frame image
| gfx_t* | gfx | Graphics context |
| gfx_coord_t | x | Left edge |
| gfx_coord_t | y | Top edge |
| uint16_t | frame | Frame index; wrapped modulo the image's frame count, so a free-running counter is fine |
| const gfx_img_t* | img | Image from the asset pipeline |
Vertical scrollbar with a proportional thumb
| gfx_t* | gfx | Graphics context |
| gfx_coord_t | x | Left edge (the track is 8px wide) |
| gfx_coord_t | y | Top edge |
| uint16_t | height | Track height in pixels |
| uint16_t | pos | Zero-based index of the selected item |
| uint16_t | total | Number of items; 0 draws the empty track |
Rounded outline, with a double ring when selected
| gfx_t* | gfx | Graphics context |
| gfx_coord_t | x | Left edge |
| gfx_coord_t | y | Top edge |
| uint16_t | w | Width in pixels |
| uint16_t | h | Height in pixels |
| uint16_t | r | Corner radius |
| bool | active | true adds two outer rings - the firmware's selection cue. They grow *outward*, so leave 2px of margin around the frame. |
Horizontal bar meter over an integer range
| gfx_t* | gfx | Graphics context |
| gfx_coord_t | x | Left edge |
| gfx_coord_t | y | Top edge |
| uint16_t | w | Outer width in pixels |
| uint16_t | h | Outer height in pixels |
| uint32_t | val | Current value |
| uint32_t | min | Range minimum |
| uint32_t | max | Range maximum |
Slim float bar meter, drawn in the lower half of its box
| gfx_t* | gfx | Graphics context |
| gfx_coord_t | x | Left edge |
| gfx_coord_t | y | Top edge |
| uint16_t | w | Outer width in pixels |
| uint16_t | h | Outer height in pixels - the bar itself is about an eighth of it |
| float | val | Current value |
| float | min | Range minimum |
| float | max | Range maximum |
Mechanical rolling-TEXT state - split-flap style label animation.
| char[UI_ROLL_TEXT_MAX] | from | |
| char[UI_ROLL_TEXT_MAX] | tgt | |
| float[UI_ROLL_TEXT_MAX] | phase | |
| uint8_t | len | |
| uint8_t | inited |
Draw a rolling label, right edge at x_right, baseline at y
| gfx_t* | gfx | |
| gfx_coord_t | x_right | |
| gfx_coord_t | y | |
| ui_roll_text_t* | c | |
| const char* | value |
Uses the CURRENTLY-SET gfx font and color. Self-clocked: call once per frame from redraw() with the desired string - changed characters roll toward it, mid-flight changes re-aim without a jump. The first-ever draw seeds silently.
Returns — true while any character is still rolling
Note — This call advances the roll as it paints, so it only moves on painted frames - tick() must keep returning true until it reports false.
| ui_roll_text_t* | c | |
| const char* | value |
Set the label instantly, no roll (e.g. when the thing being named changes identity).