Skip to content

Line Widget

The Line widget draws a straight stroke — a section divider, a chart axis, a threshold marker, a "muted" cross-out. The widget's box IS the line: horizontal and vertical strokes fill their box directly (no anti-aliasing needed), while diagonals and the set_points() endpoint API reuse the library's AA line renderer. A NULL ext is a valid, zero-RAM form — an always-visible divider in the widget's own colour with an AUTO-resolved direction.

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 Separators

A line created with nothing but ugui_widget_line_init() + set_align() — the 200×2 theme-divider default, centred. Below it, the zero-RAM form: settings-list row separators initialised with a NULL ext — AUTO direction resolves to horizontal from the wide box, colour comes from the theme, and each separator costs only the widget node.

Default Separators

/* --- 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, WDGT_ID_SLOT0
};

/* --- 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_LINE)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
static ugui_widget_t   s_demo;
static ugui_line_ext_t s_demo_ext;
#define ROW_N 3
static ugui_widget_t    s_row[ROW_N];
static ugui_label_ext_t s_row_ext[ROW_N];
static ugui_widget_t s_sep[ROW_N - 1];         /* deliberately NO ext structs */
static const char* const s_row_txt[ROW_N] = {
    "Brightness", "Volume", "Language"
};
#define LIST_X      ((int16_t)(UGUI_SCREEN_W / 8))
#define LIST_W      ((int16_t)(UGUI_SCREEN_W - (2 * LIST_X)))
#define LIST_Y      ((int16_t)(UGUI_SCREEN_H / 2))
#define ROW_H       26
#define ROW_STEP    (ROW_H + 2)
#endif

/* --- local helpers & event callbacks --- */
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_line_init(&s_root, &s_demo, &s_demo_ext);
ugui_widget_set_align(&s_demo, UGUI_ALIGN_H_CENTER | UGUI_ALIGN_TOP,
                      0, (int16_t)(38 + 38));
ugui_widget_set_id(&s_demo, WDGT_ID_DEMO);
ugui_screen_add_widget(&s_screen, &s_demo);

/* Zero-RAM separators: a settings-style list. Each separator is a line
 * widget with a NULL ext — AUTO direction (wide box -> horizontal),
 * default thickness, theme divider colour. */
for (uint8_t i = 0u; i < (uint8_t)ROW_N; i++) {
    add_label(&s_row[i], &s_row_ext[i], (uint8_t)(WDGT_ID_SLOT0 + i),
             LIST_X, (int16_t)(LIST_Y + ((int16_t)i * ROW_STEP)),
             LIST_W, ROW_H, s_row_txt[i], UGUI_ALIGN_LEFT);
    if (i < (uint8_t)(ROW_N - 1)) {
        ugui_widget_line_init(&s_root, &s_sep[i], NULL);
        ugui_widget_set_area(&s_sep[i],
                             LIST_X,
                             (int16_t)(LIST_Y + ((int16_t)(i + 1u) * ROW_STEP) - 2),
                             LIST_W, 1);
        ugui_screen_add_widget(&s_screen, &s_sep[i]);
    }
}

Directions & Thickness

Every geometry API side by side: a horizontal thickness ramp (AUTO direction resolves from the wide box), an explicit vertical ramp via set_dir(), two diagonals parented to a panel (so the AA edge blends against the panel colour and the panel clips it), and the endpoint-style set_points(x0,y0,x1,y1) API with dots marking the requested endpoints.

Directions & Thickness

/* --- 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_SLOT0 = 10, WDGT_ID_DEMO
};

/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LINE)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
#define H_N 4
#define V_N 3
static ugui_widget_t   s_h[H_N];
static ugui_line_ext_t s_h_ext[H_N];
static const uint8_t   s_h_thick[H_N] = { 1u, 2u, 4u, 8u };
static ugui_widget_t   s_v[V_N];
static ugui_line_ext_t s_v_ext[V_N];
static const uint8_t   s_v_thick[V_N] = { 1u, 3u, 6u };
static ugui_widget_t   s_panel[2];             /* clip + AA-blend hosts   */
static ugui_box_ext_t  s_panel_ext[2];
static ugui_widget_t   s_diag[2];
static ugui_line_ext_t s_diag_ext[2];
static ugui_widget_t   s_pts;                  /* set_points() footer demo */
static ugui_line_ext_t s_pts_ext;
static ugui_widget_t   s_dot[2];               /* endpoint markers        */
static ugui_circle_ext_t s_dot_ext[2];
#define COL_W      ((int16_t)(UGUI_SCREEN_W / 3))
#define BLK_Y      ((int16_t)(38 + 24))    /* below column captions */
#define BLK_H      ((int16_t)((UGUI_SCREEN_H * 38) / 100))
#define FOOT_Y     ((int16_t)(BLK_Y + BLK_H + 26))   /* set_points strip      */
#define DOT_R      4                                 /* endpoint marker size  */
#endif

/* --- build it (in your screen's init) --- */
/* Column 1 — thickness ramp; AUTO direction resolves to H (wide boxes).
 * Accent colour so the ramp pops against the page background. */
for (uint8_t i = 0u; i < (uint8_t)H_N; i++) {
    ugui_widget_line_init(&s_root, &s_h[i], &s_h_ext[i]);
    ugui_widget_set_area(&s_h[i],
                         (int16_t)(10 + 4),
                         (int16_t)(BLK_Y + 8 + ((int16_t)i * (BLK_H / H_N))),
                         (int16_t)(COL_W - (2 * (10 + 4))), 12);
    ugui_widget_line_set_thickness(&s_h[i], s_h_thick[i]);
    s_h[i].color = UGUI_THEME_ACCENT;
    ugui_widget_set_id(&s_h[i], (uint8_t)(WDGT_ID_SLOT0 + i));
    ugui_screen_add_widget(&s_screen, &s_h[i]);
}

/* Column 2 — explicit vertical direction, thickness ramp. */
for (uint8_t i = 0u; i < (uint8_t)V_N; i++) {
    ugui_widget_line_init(&s_root, &s_v[i], &s_v_ext[i]);
    ugui_widget_set_area(&s_v[i],
                         (int16_t)(COL_W + 20 + ((int16_t)i * ((COL_W - 40) / V_N))),
                         (int16_t)(BLK_Y + 4), 12, (int16_t)(BLK_H - 8));
    ugui_widget_line_set_dir(&s_v[i], UGUI_LINE_DIR_V);
    ugui_widget_line_set_thickness(&s_v[i], s_v_thick[i]);
    s_v[i].color = UGUI_THEME_ACCENT;
    ugui_screen_add_widget(&s_screen, &s_v[i]);
}

/* Column 3 — the two diagonals, each inside a surface panel. The line is
 * a CHILD of the panel, so its AA edge blends against the panel colour
 * and the panel's clip bounds it. */
for (uint8_t i = 0u; i < 2u; i++) {
    ugui_widget_box_init(&s_root, &s_panel[i], &s_panel_ext[i]);
    ugui_widget_set_area(&s_panel[i],
                         (int16_t)((2 * COL_W) + 14),
                         (int16_t)(BLK_Y + 4 + ((int16_t)i * ((BLK_H / 2) + 2))),
                         (int16_t)(COL_W - 28), (int16_t)((BLK_H / 2) - 6));
    ugui_widget_box_set_style(&s_panel[i], true, 4u, 0u,
                              UGUI_THEME_SURFACE, UGUI_THEME_SURFACE);
    ugui_screen_add_widget(&s_screen, &s_panel[i]);

    ugui_widget_line_init(&s_panel[i], &s_diag[i], &s_diag_ext[i]);
    ugui_widget_set_area(&s_diag[i], 0, 0,
                         (int16_t)(COL_W - 28), (int16_t)((BLK_H / 2) - 6));
    ugui_widget_line_set_dir(&s_diag[i], (i == 0u) ? UGUI_LINE_DIR_DIAG_DOWN
                                                   : UGUI_LINE_DIR_DIAG_UP);
    ugui_widget_line_set_thickness(&s_diag[i], 3u);
    s_diag[i].color = UGUI_THEME_ACCENT;
    ugui_screen_add_widget(&s_screen, &s_diag[i]);
}

/* Footer — the endpoint-style API: one call places box + direction.
 * The two dots sit ON the requested endpoints. */
{
    int16_t x0 = (int16_t)(UGUI_SCREEN_W / 4);
    int16_t y0 = (int16_t)(FOOT_Y + 26);
    int16_t x1 = (int16_t)((UGUI_SCREEN_W * 3) / 4);
    int16_t y1 = (int16_t)(FOOT_Y + 4);

    ugui_widget_line_init(&s_root, &s_pts, &s_pts_ext);
    ugui_widget_line_set_thickness(&s_pts, 2u);
    ugui_widget_line_set_points(&s_pts, x0, y0, x1, y1);
    s_pts.color = UGUI_THEME_TEXT;
    ugui_widget_set_id(&s_pts, WDGT_ID_DEMO);
    ugui_screen_add_widget(&s_screen, &s_pts);

    for (uint8_t i = 0u; i < 2u; i++) {
        int16_t cx = (i == 0u) ? x0 : x1;
        int16_t cy = (i == 0u) ? y0 : y1;
        ugui_widget_circle_init(&s_root, &s_dot[i], &s_dot_ext[i]);
        ugui_widget_set_area(&s_dot[i],
                             (int16_t)(cx - DOT_R), (int16_t)(cy - DOT_R),
                             (int16_t)(2 * DOT_R + 1), (int16_t)(2 * DOT_R + 1));
        ugui_widget_circle_set_fill_color(&s_dot[i], UGUI_THEME_ACCENT);
        ugui_screen_add_widget(&s_screen, &s_dot[i]);
    }
}

Dash Patterns & Markers

set_dash() patterns from dotted to a wide dash-gap, then two classic marker idioms built from plain line widgets: an "alarm limit" (a solid measured-level line plus a dashed limit line, both parented to a panel) and a "muted" cross-out (a disc with a thick diagonal child line through it).

Dash Patterns & Markers

/* --- 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_SLOT0 = 10, WDGT_ID_DEMO
};

/* --- file-scope storage --- */
#if defined(UGUI_WIDGET_ENABLE_LINE)
static ugui_screen_t  s_screen;
static ugui_widget_t   s_root;
#define D_N 3
static ugui_widget_t   s_d[D_N];
static ugui_line_ext_t s_d_ext[D_N];
static const uint8_t   s_d_dash[D_N] = { 2u,  6u, 12u };
static const uint8_t   s_d_gap [D_N] = { 2u,  3u,  6u };
static ugui_widget_t   s_panel;                /* alarm-limit host        */
static ugui_box_ext_t  s_panel_ext;
static ugui_widget_t   s_level;                /* solid "measured" line   */
static ugui_line_ext_t s_level_ext;
static ugui_widget_t   s_limit;                /* dashed alert line       */
static ugui_line_ext_t s_limit_ext;
static ugui_widget_t     s_disc;               /* cross-out pictogram     */
static ugui_circle_ext_t s_disc_ext;
static ugui_widget_t     s_cross;
static ugui_line_ext_t   s_cross_ext;
#define DASH_X      ((int16_t)(UGUI_SCREEN_W / 6))
#define DASH_W      ((int16_t)((UGUI_SCREEN_W * 4) / 6))
#define DASH_Y      ((int16_t)(38 + 24))
#define DASH_STEP   18
#define DEMO_Y      ((int16_t)(DASH_Y + (D_N * DASH_STEP) + 26))
#define PANEL_W     ((int16_t)((UGUI_SCREEN_W / 2) - (2 * 10)))
#define PANEL_H     ((int16_t)(UGUI_SCREEN_H - DEMO_Y - 36))
#define DISC_D      ((int16_t)((PANEL_H < 72) ? PANEL_H : 72))
#endif

/* --- build it (in your screen's init) --- */
/* Dash ramp — same stroke, three patterns. */
for (uint8_t i = 0u; i < (uint8_t)D_N; i++) {
    ugui_widget_line_init(&s_root, &s_d[i], &s_d_ext[i]);
    ugui_widget_set_area(&s_d[i],
                         DASH_X, (int16_t)(DASH_Y + ((int16_t)i * DASH_STEP)),
                         DASH_W, 8);
    ugui_widget_line_set_dash(&s_d[i], s_d_dash[i], s_d_gap[i]);
    s_d[i].color = UGUI_THEME_TEXT;
    ugui_widget_set_id(&s_d[i], (uint8_t)(WDGT_ID_SLOT0 + i));
    ugui_screen_add_widget(&s_screen, &s_d[i]);
}

/* Alarm limit — solid level line + dashed alert limit, both children of
 * the panel so AA blend and clip come from the panel automatically. */
ugui_widget_box_init(&s_root, &s_panel, &s_panel_ext);
ugui_widget_set_area(&s_panel, 10, DEMO_Y, PANEL_W, PANEL_H);
ugui_widget_box_set_style(&s_panel, true, 4u, 0u, UGUI_THEME_SURFACE, UGUI_THEME_SURFACE);
ugui_screen_add_widget(&s_screen, &s_panel);

ugui_widget_line_init(&s_panel, &s_level, &s_level_ext);
ugui_widget_set_area(&s_level, 8, (int16_t)((PANEL_H * 2) / 3),
                     (int16_t)(PANEL_W - 16), 6);
ugui_widget_line_set_thickness(&s_level, 6u);
s_level.color = UGUI_THEME_OK;
ugui_screen_add_widget(&s_screen, &s_level);

ugui_widget_line_init(&s_panel, &s_limit, &s_limit_ext);
ugui_widget_set_area(&s_limit, 8, (int16_t)(PANEL_H / 3),
                     (int16_t)(PANEL_W - 16), 2);
ugui_widget_line_set_dash(&s_limit, 6u, 4u);
s_limit.color = UGUI_THEME_CANCEL;
ugui_widget_set_id(&s_limit, WDGT_ID_DEMO);
ugui_screen_add_widget(&s_screen, &s_limit);

/* Cross-out — disc + thick diagonal child = "muted" pictogram. */
ugui_widget_circle_init(&s_root, &s_disc, &s_disc_ext);
ugui_widget_set_area(&s_disc,
                     (int16_t)((UGUI_SCREEN_W * 3) / 4 - (DISC_D / 2)),
                     (int16_t)(DEMO_Y + ((PANEL_H - DISC_D) / 2)),
                     DISC_D, DISC_D);
ugui_screen_add_widget(&s_screen, &s_disc);

ugui_widget_line_init(&s_disc, &s_cross, &s_cross_ext);
ugui_widget_set_area(&s_cross, (int16_t)(DISC_D / 5), (int16_t)(DISC_D / 5),
                     (int16_t)((DISC_D * 3) / 5), (int16_t)((DISC_D * 3) / 5));
ugui_widget_line_set_dir(&s_cross, UGUI_LINE_DIR_DIAG_UP);
ugui_widget_line_set_thickness(&s_cross, 4u);
s_cross.color = UGUI_THEME_KNOB;
ugui_screen_add_widget(&s_screen, &s_cross);


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.