跳转至

lib/graphic/widget/progressbar/progressbar.c

Functions

Name
void draw_rect_border(CFBD_GraphicDevice * dev, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
void fill_rect(CFBD_GraphicDevice * dev, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
void clear_rect(CFBD_GraphicDevice * dev, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
void compute_inner_box(CFBD_ProgressBar * pb, uint16_t * out_x, uint16_t * out_y, uint16_t * out_w, uint16_t * out_h)
float smoothstep(float t)
uint16_t compute_fill_width(CFBD_ProgressBar * pb, int32_t value)
void CFBD_ProgressBar_Draw(CFBD_ProgressBar * pb)
void CFBD_ProgressBar_SetValue(CFBD_ProgressBar * pb, int32_t new_value)
CFBD_Bool CFBD_ProgressBar_SetProperty(CFBD_ProgressBar * pb, const char * property, const void * value)
void CFBD_ProgressBar_Init(CFBD_ProgressBar * pb, CFBD_GraphicDevice * device, CFBDGraphic_Point * tl, CFBDGraphicSize * sz, int32_t min, int32_t max)

Attributes

Name
CFBD_ProgressBarOps ops

Functions Documentation

function draw_rect_border

static void draw_rect_border(
    CFBD_GraphicDevice * dev,
    uint16_t x,
    uint16_t y,
    uint16_t w,
    uint16_t h
)

function fill_rect

static void fill_rect(
    CFBD_GraphicDevice * dev,
    uint16_t x,
    uint16_t y,
    uint16_t w,
    uint16_t h
)

function clear_rect

static void clear_rect(
    CFBD_GraphicDevice * dev,
    uint16_t x,
    uint16_t y,
    uint16_t w,
    uint16_t h
)

function compute_inner_box

static void compute_inner_box(
    CFBD_ProgressBar * pb,
    uint16_t * out_x,
    uint16_t * out_y,
    uint16_t * out_w,
    uint16_t * out_h
)

function smoothstep

static float smoothstep(
    float t
)

function compute_fill_width

static uint16_t compute_fill_width(
    CFBD_ProgressBar * pb,
    int32_t value
)

function CFBD_ProgressBar_Draw

void CFBD_ProgressBar_Draw(
    CFBD_ProgressBar * pb
)

function CFBD_ProgressBar_SetValue

void CFBD_ProgressBar_SetValue(
    CFBD_ProgressBar * pb,
    int32_t new_value
)

function CFBD_ProgressBar_SetProperty

CFBD_Bool CFBD_ProgressBar_SetProperty(
    CFBD_ProgressBar * pb,
    const char * property,
    const void * value
)

function CFBD_ProgressBar_Init

void CFBD_ProgressBar_Init(
    CFBD_ProgressBar * pb,
    CFBD_GraphicDevice * device,
    CFBDGraphic_Point * tl,
    CFBDGraphicSize * sz,
    int32_t min,
    int32_t max
)

Attributes Documentation

variable ops

CFBD_ProgressBarOps ops = {.immediate_draw = CFBD_ProgressBar_Draw,
                           .set_property = CFBD_ProgressBar_SetProperty,
                           .set_value = CFBD_ProgressBar_SetValue};

Source code

#include "progressbar.h"

#include <stdint.h>
#include <string.h>

#include "cfbd_define.h"
#include "device/graphic_device.h"
#include "sys_clock/system_clock.h" /* for system_delay_ms */
#include "widget/animation/animation.h"
#include "widget/base_support/common/helpers.h"

/* Draw rectangle border using setPixel (1px thick) */
static void
draw_rect_border(CFBD_GraphicDevice* dev, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
    CFBD_Bool (*SetPixelAction)(CFBD_GraphicDevice* dev, uint16_t x, uint16_t y) =
            dev->ops->setPixel;
    /* top/bottom */
    for (uint16_t ix = x; ix < x + w; ++ix) {
        SetPixelAction(dev, ix, y);
        SetPixelAction(dev, ix, y + h - 1);
    }
    /* left/right */
    for (uint16_t iy = y; iy < y + h; ++iy) {
        SetPixelAction(dev, x, iy);
        SetPixelAction(dev, x + w - 1, iy);
    }
}

/* Fill rectangular area with pixels (setPixel) */
static void fill_rect(CFBD_GraphicDevice* dev, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
    CFBD_Bool (*SetPixelAction)(CFBD_GraphicDevice* dev, uint16_t x, uint16_t y) =
            dev->ops->setPixel;
    for (uint16_t iy = y; iy < y + h; ++iy) {
        for (uint16_t ix = x; ix < x + w; ++ix) {
            SetPixelAction(dev, ix, iy);
        }
    }
}

/* Clear area using device-provided clear_area (faster than per-pixel clear) */
static void clear_rect(CFBD_GraphicDevice* dev, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
    dev->ops->clear_area(dev, x, y, w, h);
}

/* internal: compute inner box (where fill is drawn) */
static void compute_inner_box(CFBD_ProgressBar* pb,
                              uint16_t* out_x,
                              uint16_t* out_y,
                              uint16_t* out_w,
                              uint16_t* out_h)
{
    uint16_t x = pb->tl.x;
    uint16_t y = pb->tl.y;
    uint16_t w = pb->size.width;
    uint16_t h = pb->size.height;

    uint8_t pad = pb->padding;
    if (pb->border) {
        /* 1 px border assumed */
        if (w <= 2)
            w = 0;
        else {
            x += 1;
            w -= 2;
        }
        if (h <= 2)
            h = 0;
        else {
            y += 1;
            h -= 2;
        }
    }
    /* apply padding inside */
    if (w > 2 * pad) {
        x += pad;
        w -= (uint16_t) (2 * pad);
    }
    else
        w = 0;
    if (h > 2 * pad) {
        y += pad;
        h -= (uint16_t) (2 * pad);
    }
    else
        h = 0;

    *out_x = x;
    *out_y = y;
    *out_w = w;
    *out_h = h;
}

static float smoothstep(float t)
{
    /* classic smoothstep: 3t^2 - 2t^3 */
    return t * t * (3.0f - 2.0f * t);
}

static uint16_t compute_fill_width(CFBD_ProgressBar* pb, int32_t value)
{
    uint16_t ix, iy, iw, ih;
    compute_inner_box(pb, &ix, &iy, &iw, &ih);

    if (iw == 0 || pb->max <= pb->min)
        return 0;

    value = clamp_i32(value, pb->min, pb->max);
    float frac = (float) (value - pb->min) / (float) (pb->max - pb->min);

    if (frac < 0.0f)
        frac = 0.0f;
    if (frac > 1.0f)
        frac = 1.0f;

    return (uint16_t) (frac * (float) iw + 0.5f);
}

void CFBD_ProgressBar_Draw(CFBD_ProgressBar* pb)
{
    if (!pb || !pb->device)
        return;

    CFBD_GraphicDevice* dev = pb->device;

    uint16_t ix, iy, iw, ih;
    compute_inner_box(pb, &ix, &iy, &iw, &ih);

    /* clear inner area */
    clear_rect(dev, ix, iy, iw, ih);

    /* draw filled area */
    uint16_t fill_w = compute_fill_width(pb, pb->value);
    if (fill_w > 0)
        fill_rect(dev, ix, iy, fill_w, ih);

    /* border */
    if (pb->border)
        draw_rect_border(dev, pb->tl.x, pb->tl.y, pb->size.width, pb->size.height);

    dev->ops->update_area(dev, pb->tl.x, pb->tl.y, pb->size.width, pb->size.height);
}

void CFBD_ProgressBar_SetValue(CFBD_ProgressBar* pb, int32_t new_value)
{
    if (!pb || !pb->device)
        return;

    new_value = clamp_i32(new_value, pb->min, pb->max);

    int32_t old_value = pb->value;
    if (pb->animation.anim_frames <= 1) {
        pb->value = new_value;
        CFBD_ProgressBar_Draw(pb);
        return;
    }

    uint16_t ix, iy, iw, ih;
    compute_inner_box(pb, &ix, &iy, &iw, &ih);
    if (iw == 0 || ih == 0) {
        pb->value = new_value;
        return;
    }

    int frames = pb->animation.anim_frames;
    int delay = (pb->animation.anim_frame_delay_ms > 0) ? pb->animation.anim_frame_delay_ms : 16;

    uint16_t last_fill = compute_fill_width(pb, old_value);

    for (int f = 1; f <= frames; ++f) {
        float t = (float) f / (float) frames;
        float st = smoothstep(t);

        int32_t cur_value = old_value + (int32_t) ((float) (new_value - old_value) * st);

        uint16_t cur_fill = compute_fill_width(pb, cur_value);

        /* incremental update only */
        if (cur_fill > last_fill) {
            /* grow */
            fill_rect(pb->device, ix + last_fill, iy, cur_fill - last_fill, ih);
        }
        else if (cur_fill < last_fill) {
            /* shrink */
            clear_rect(pb->device, ix + cur_fill, iy, last_fill - cur_fill, ih);
        }

        if (pb->border)
            draw_rect_border(pb->device, pb->tl.x, pb->tl.y, pb->size.width, pb->size.height);

        pb->device->ops->update_area(pb->device,
                                     pb->tl.x,
                                     pb->tl.y,
                                     pb->size.width,
                                     pb->size.height);

        last_fill = cur_fill;
        pb->value = cur_value;

        system_delay_ms(delay);
    }

    pb->value = new_value;
}

/*
 *          -> border, uint8_t as boarders
 *          -> padding, uint8_t as padding
 *          -> animation, CFBD_BaseAnimation* passed
 */
CFBD_Bool
CFBD_ProgressBar_SetProperty(CFBD_ProgressBar* pb, const char* property, const void* value)
{
    if (strcmp(property, "border") == 0) {
        uint8_t border = *(uint8_t*) (value);
        pb->border = border;
        return CFBD_TRUE;
    }
    else if (strcmp(property, "padding") == 0) {
        uint8_t padding = *(uint8_t*) (value);
        pb->padding = padding;
        return CFBD_TRUE;
    }
    else if (strcmp(property, "animation") == 0) {
        CFBD_BaseAnimation* animation = (CFBD_BaseAnimation*) (value);
        pb->animation = *animation;
        return CFBD_TRUE;
    }
    return CFBD_FALSE;
}

CFBD_ProgressBarOps ops = {.immediate_draw = CFBD_ProgressBar_Draw,
                           .set_property = CFBD_ProgressBar_SetProperty,
                           .set_value = CFBD_ProgressBar_SetValue};

/* simple setters */
void CFBD_ProgressBar_Init(CFBD_ProgressBar* pb,
                           CFBD_GraphicDevice* device,
                           CFBDGraphic_Point* tl,
                           CFBDGraphicSize* sz,
                           int32_t min,
                           int32_t max)
{
    if (!pb)
        return;
    memset(pb, 0, sizeof(*pb));
    pb->tl = *tl;
    pb->size = *sz;
    pb->min = min;
    pb->max = max;
    pb->value = min;
    pb->device = device;
    pb->border = 1;
    pb->padding = 1;
    pb->ops = &ops;
}

Updated on 2026-02-03 at 13:21:55 +0000