Skip to content

LinGauge Widget

The LinGauge is a linear instrument — a bar, a segmented LED block readout, or a sliding pointer marker over a track — the horizontal/vertical counterpart to the round Gauge. Init installs a bar style with orientation auto-detected from the widget's aspect ratio (tall area → vertical), 6×1 ticks and damped motion; set_area() places it, set_range() maps the units. set_style() switches between BAR / SEGMENTS / POINTER — the same value, three presentations; set_pointer_image() centres any flash icon on the value position for the POINTER style. add_zone() paints coloured bands with an optional set_zone_tint() so the fill itself recolours by zone, plus the same edge-triggered on_zone alarm hook the round Gauge has.

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 LinGauge (live feed)

Three calls make a working instrument: ugui_widget_lingauge_init() installs the whole look, set_area() places it — a tall rect makes it vertical — and set_range() maps the units. Numerals are the one extra (optional) call so the scale reads. Only the span the fill edge travels through repaints; the scale never does.

Default LinGauge (live feed)

/* --- 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_LINGAUGE)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t       s_bar;
static ugui_lingauge_ext_t s_bar_ext;
#define BAR_W   120
#define BAR_H   (UGUI_SCREEN_H - 38 - 10)
#define BAR_X   ((UGUI_SCREEN_W - BAR_W) / 2)
#define BAR_Y   (38 + 2)
#endif

/* --- build it (in your screen's init) --- */
ugui_widget_lingauge_init(&s_root, &s_bar, &s_bar_ext);
ugui_widget_set_area(&s_bar, BAR_X, BAR_Y, BAR_W, BAR_H);
ugui_widget_lingauge_set_range(&s_bar, 0, 100);
ugui_widget_set_id(&s_bar, WDGT_ID_DEMO);
#ifdef UGUI_ENABLE_FONT
ugui_widget_lingauge_set_numerals(&s_bar, &lexend_14pt_2bpp);
#endif
ugui_screen_add_widget(&s_screen, &s_bar);

Bar / Segments / Pointer

The three display styles, one value: BAR is a continuous fill with numerals (fuel/temp presentation); SEGMENTS is 12 LED blocks with no scale (battery/VU presentation); POINTER is an image marker (a heart icon) sliding over an empty track with green/amber/red zones — set_pointer_image() centres any flash icon on the value position.

Bar / Segments / Pointer

/* --- 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_BAR = 10, WDGT_ID_SEGMENTS, WDGT_ID_POINTER
};

/* --- image assets (inc/icons/ + inc/images/, shipped with the library;
 *     each header DEFINES its image — include it in ONE .c only) --- */
#ifdef UGUI_WIDGET_ENABLE_IMAGE
#include "icons/heart_icon_1_fill_grayscale_4.h"   /* defines img_heart_icon_1_fill */
#endif

/* --- 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_LINGAUGE)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t       s_bar,     s_seg,     s_ptr;
static ugui_lingauge_ext_t s_bar_ext, s_seg_ext, s_ptr_ext;
#define COL_W    120
#define GAP      ((UGUI_SCREEN_W - 3 * COL_W) / 4)
#define COL_Y    (38 + 2)
#define CAP_H    20
#define COL_H    (UGUI_SCREEN_H - COL_Y - CAP_H - 8)
#define COL_X(i) (GAP + (i) * (COL_W + GAP))
#endif

/* --- build it (in your screen's init) --- */
/* BAR with numerals. */
ugui_widget_lingauge_init(&s_root, &s_bar, &s_bar_ext);
ugui_widget_set_area(&s_bar, COL_X(0), COL_Y, COL_W, COL_H);
ugui_widget_set_id(&s_bar, WDGT_ID_BAR);
ugui_widget_lingauge_set_range(&s_bar, 0, 100);
#ifdef UGUI_ENABLE_FONT
ugui_widget_lingauge_set_numerals(&s_bar, &lexend_14pt_2bpp);
#endif
ugui_screen_add_widget(&s_screen, &s_bar);

/* SEGMENTS: 12 LED blocks, no scale — clean full-width blocks. */
ugui_widget_lingauge_init(&s_root, &s_seg, &s_seg_ext);
ugui_widget_set_area(&s_seg, COL_X(1), COL_Y, COL_W, COL_H);
ugui_widget_set_id(&s_seg, WDGT_ID_SEGMENTS);
ugui_widget_lingauge_set_range(&s_seg, 0, 100);
ugui_widget_lingauge_set_style(&s_seg, UGUI_LINGAUGE_STYLE_SEGMENTS, 12u, 3u);
ugui_widget_lingauge_set_ticks(&s_seg, 0u, 0u, 1u, 1u);   /* scale off */
ugui_widget_set_color(&s_seg, UGUI_COLOR_GREEN);
ugui_screen_add_widget(&s_screen, &s_seg);

/* POINTER over zones: the marker indicates, the zones classify. */
ugui_widget_lingauge_init(&s_root, &s_ptr, &s_ptr_ext);
ugui_widget_set_area(&s_ptr, COL_X(2), COL_Y, COL_W, COL_H);
ugui_widget_set_id(&s_ptr, WDGT_ID_POINTER);
ugui_widget_lingauge_set_range(&s_ptr, 0, 100);
ugui_widget_lingauge_set_style(&s_ptr, UGUI_LINGAUGE_STYLE_POINTER, 0u, 0u);
ugui_widget_lingauge_set_track(&s_ptr, 0u, 5u);
(void)ugui_widget_lingauge_add_zone(&s_ptr,  0,  20, UGUI_COLOR_AMBER);
(void)ugui_widget_lingauge_add_zone(&s_ptr, 20,  80, UGUI_COLOR_GREEN);
(void)ugui_widget_lingauge_add_zone(&s_ptr, 80, 100, UGUI_COLOR_RED);
/* Marker colour must contrast the track in BOTH themes: the track is
 * UGUI_THEME_SURFACE (white in light mode), so a hardcoded white marker
 * disappears there — use the theme text role instead. */
ugui_widget_set_color(&s_ptr, UGUI_THEME_TEXT);
#ifdef UGUI_WIDGET_ENABLE_IMAGE
/* Image marker: the heart icon slides along the track, tinted with
 * w->color (theme text) — swap the icon or colour freely. */
ugui_widget_lingauge_set_pointer_image(&s_ptr, &img_heart_icon_1_fill);
#endif
ugui_screen_add_widget(&s_screen, &s_ptr);

Automotive Strip (fuel / coolant / battery)

Three horizontal gauges the way a cluster uses them: FUEL has a red low-fuel zone with set_zone_tint() (the bar itself turns red as the tank runs low) and an on_zone hook where a telltale would light; COOLANT has cold/normal/hot zones + tint; SoC is a 20-segment battery bar with no scale. All three play a looping piecewise-linear signal.

Automotive Strip (fuel / coolant / battery)

/* --- 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_FUEL = 10, WDGT_ID_COOLANT, WDGT_ID_BATTERY
};

/* --- 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_LINGAUGE)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t       s_fuel,     s_temp,     s_soc;
static ugui_lingauge_ext_t s_fuel_ext, s_temp_ext, s_soc_ext;
#define CAP_W    90
#define ROW_X    (CAP_W + 18)
#define ROW_W    (UGUI_SCREEN_W - ROW_X - 12)
#define ROW_H    ((UGUI_SCREEN_H - 38 - 16) / 3)
#define ROW_Y(i) (38 + 4 + (i) * (ROW_H + 4))
#endif

/* --- local helpers & event callbacks --- */
static void on_fuel_zone(ugui_widget_t* w, int8_t zone, void* ctx)
{
    (void)w; (void)ctx;
    printf("[lingauge.auto] fuel zone -> %s\n",
           (zone == 0) ? "LOW (telltale on)" : "ok");
}

/* --- build it (in your screen's init) --- */
/* FUEL 0-100 %: low-fuel zone + tint (bar turns red when low). */
ugui_widget_lingauge_init(&s_root, &s_fuel, &s_fuel_ext);
ugui_widget_set_area(&s_fuel, ROW_X, ROW_Y(0), ROW_W, (int16_t)ROW_H);
ugui_widget_set_id(&s_fuel, WDGT_ID_FUEL);
ugui_widget_lingauge_set_range(&s_fuel, 0, 100);
(void)ugui_widget_lingauge_add_zone(&s_fuel,  0,  12, UGUI_COLOR_RED);
(void)ugui_widget_lingauge_add_zone(&s_fuel, 12, 100, UGUI_COLOR_GREEN);
ugui_widget_lingauge_set_zone_tint(&s_fuel, true);
ugui_widget_lingauge_set_on_zone(&s_fuel, on_fuel_zone, NULL);
#ifdef UGUI_ENABLE_FONT
ugui_widget_lingauge_set_numerals(&s_fuel, &lexend_14pt_2bpp);
#endif
ugui_screen_add_widget(&s_screen, &s_fuel);

/* COOLANT 0-130 °C: cold / normal / hot zones + tint. */
ugui_widget_lingauge_init(&s_root, &s_temp, &s_temp_ext);
ugui_widget_set_area(&s_temp, ROW_X, ROW_Y(1), ROW_W, (int16_t)ROW_H);
ugui_widget_set_id(&s_temp, WDGT_ID_COOLANT);
ugui_widget_lingauge_set_range(&s_temp, 0, 130);
(void)ugui_widget_lingauge_add_zone(&s_temp,   0,  70, ugui_color_hex(0x2196F3u));
(void)ugui_widget_lingauge_add_zone(&s_temp,  70, 105, UGUI_COLOR_GREEN);
(void)ugui_widget_lingauge_add_zone(&s_temp, 105, 130, UGUI_COLOR_RED);
ugui_widget_lingauge_set_zone_tint(&s_temp, true);
#ifdef UGUI_ENABLE_FONT
ugui_widget_lingauge_set_numerals(&s_temp, &lexend_14pt_2bpp);
#endif
ugui_screen_add_widget(&s_screen, &s_temp);

/* BATTERY SoC: 20 green segments, no scale. */
ugui_widget_lingauge_init(&s_root, &s_soc, &s_soc_ext);
ugui_widget_set_area(&s_soc, ROW_X, ROW_Y(2), ROW_W, (int16_t)ROW_H);
ugui_widget_set_id(&s_soc, WDGT_ID_BATTERY);
ugui_widget_lingauge_set_range(&s_soc, 0, 100);
ugui_widget_lingauge_set_style(&s_soc, UGUI_LINGAUGE_STYLE_SEGMENTS, 20u, 3u);
ugui_widget_lingauge_set_ticks(&s_soc, 0u, 0u, 1u, 1u);   /* scale off */
ugui_widget_set_color(&s_soc, UGUI_COLOR_GREEN);
ugui_screen_add_widget(&s_screen, &s_soc);


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.