Zum Inhalt

tinyui_demo.tc

tinyui_demo.tc — demo of the TinyUI retained-mode widget layer

Source on GitHub

// tinyui_demo.tc — demo of the TinyUI retained-mode widget layer
//
// Uses the new ui* syscalls (uiScreen / uiTheme / uiLabel / uiProgress / uiGauge /
// uiCheckbox) to build a 3-screen dashboard on top of the standard TinyC display
// primitives. No external library, no graphical designer — just a thin wrapper
// over the existing Renderer with retained state so widgets redraw automatically
// when the screen changes.
//
// Screen 1: headline label + two live progress bars
// Screen 2: arc gauge centred on the display showing a value
// Screen 3: two interactive checkboxes + a momentary pushbutton (VButton touch pool)
//
// Navigation (all uppercase, no space between prefix and subcommand):
//   UINEXT   cycle to the next screen
//   UIHOME   jump to screen 1 (dashboard)
//   UIGAUGE  jump to screen 2 (arc gauge)
//   UICTRL   jump to screen 3 (checkboxes)
// Dispatched through the registered command prefix "UI".
//
// Labels 0..3 + progress 0..1 + gauge 0  live in the passive widget pool
// (tc_ui_widgets[]).
// Checkboxes 0..1 live in the VButton pool (buttons[]).
// Different index spaces, on purpose — they do not collide.

int current   = 1;    // which screen is visible
float power   = 450;  // live value shown on screen 1+2
int   vent_on = 0;    // checkbox 0 state
int   heat_on = 0;    // checkbox 1 state
int   pulses  = 0;    // pushbutton 2 press counter

// ── colours (RGB565) ────────────────────────────────────────────────
int COL_BG     = 0x0000;   // black
int COL_FG     = 0xFFFF;   // white
int COL_ACCENT = 0x07FF;   // cyan
int COL_BORDER = 0x39E7;   // dark grey
int COL_WARN   = 0xFD20;   // orange
int COL_OK     = 0x07E0;   // green

// ── helpers ─────────────────────────────────────────────────────────
void build_screen1() {
    // Headline label — widget #0, centred inside 320x30 area
    uiLabel(0,   0,  0, 320, 30, "Dashboard",     0);
    // Two labels for the live values — will be updated via uiLabelSet
    uiLabel(1,  10, 50, 150, 20, "Power:  0 W",   1);
    uiLabel(2, 160, 50, 150, 20, "Target: 500 W", 1);
    // Progress bars — widget #0 and #1 in the progress pool (overwrites labels' slot;
    // ok because they live in the same passive pool — pick unused indices).
    uiProgress(3,  10,  80, 300, 18,   0, 1000);
    uiProgress(4,  10, 110, 300, 18, 500, 1000);
}

void build_screen2() {
    uiLabel(0, 0, 10, 320, 30, "Arc Gauge", 0);
    // Centre of display (160, 140), radius 70
    uiGauge(5, 160, 140, 70, 0, 0, 1000);
    uiLabel(1, 60, 220, 200, 20, "Live power (W)", 0);
}

void build_screen3() {
    uiLabel(0, 0, 10, 320, 30, "Controls", 0);
    uiCheckbox(0,  40,  60, 160, 40, "Ventilation");
    uiCheckbox(1,  40, 110, 160, 40, "Heating");
    uiButton  (2,  40, 160, 160, 40, "PULSE");       // momentary
    uiLabel(6,    80, 210, 200, 20, "Tap a box", 1);
}

void enter_screen(int id) {
    current = id;
    uiScreen(id);             // clears + removes VButtons + redraws passive widgets
    if      (id == 1) build_screen1();
    else if (id == 2) build_screen2();
    else if (id == 3) build_screen3();
}

void screen_next() {
    int n = current + 1;
    if (n > 3) n = 1;
    enter_screen(n);
}

// ── callbacks ───────────────────────────────────────────────────────
void main() {
    // Register a console command prefix so Command() receives `UI <sub> <args>`
    addCommand("UI");
    // Theme set once — all future widgets inherit these colours
    uiTheme(COL_BG, COL_ACCENT, COL_FG, COL_BORDER);
    enter_screen(1);
}

void EverySecond() {
    // Pretend we're measuring power — bounce between 100 and 950 W
    power = power + 37;
    if (power > 950) power = 100;

    if (current == 1) {
        char buf[32];
        sprintfFloat(buf, "Power: %.0f W", power);
        uiLabelSet(1, buf);
        uiProgressSet(3, power);
    }
    else if (current == 2) {
        // uiGauge uses widget #5; reusing the same index updates the needle.
        uiGauge(5, 160, 140, 70, power, 0, 1000);
    }
}

// Called when any touch button/checkbox/pushbutton changes state.
// `num` is the VButton pool index.
//   - uiCheckbox: one event per press, state = new latched value (0/1)
//   - uiButton  : two events per tap, state = 1 on press, 0 on release
void TouchButton(int num, int state) {
    if (current != 3) return;
    if (num == 0) vent_on = state;
    if (num == 1) heat_on = state;
    if (num == 2 && state == 1) pulses = pulses + 1;   // count rising edges

    char line[40];
    if      (vent_on && heat_on) sprintfInt(line, "Vent+Heat ON  pulses=%d", pulses);
    else if (vent_on)            sprintfInt(line, "Vent ON       pulses=%d", pulses);
    else if (heat_on)            sprintfInt(line, "Heat ON       pulses=%d", pulses);
    else                         sprintfInt(line, "All OFF       pulses=%d", pulses);
    uiLabelSet(6, line);
}

// Console dispatch. Called with  UINEXT / UIHOME / UIGAUGE / UICTRL
void Command(char cmd[]) {
    char msg[48];
    if (strcmp(cmd, "NEXT") == 0) {
        screen_next();
    } else if (strcmp(cmd, "HOME") == 0) {
        enter_screen(1);
    } else if (strcmp(cmd, "GAUGE") == 0) {
        enter_screen(2);
    } else if (strcmp(cmd, "CTRL") == 0) {
        enter_screen(3);
    } else {
        responseCmnd("{\"UI\":\"? UINEXT UIHOME UIGAUGE UICTRL\"}");
        return;
    }
    sprintfInt(msg, "{\"UI\":\"screen %d\"}", current);
    responseCmnd(msg);
}