133 lines
3.8 KiB
C
133 lines
3.8 KiB
C
#include "bubble.h"
|
||
|
||
typedef struct {
|
||
float min_val;
|
||
float max_val;
|
||
float cur_val;
|
||
} bubble_level_dsc_t;
|
||
|
||
|
||
static void bubble_draw_cb(lv_event_t * e)
|
||
{
|
||
lvgl_port_lock(0);
|
||
|
||
lv_obj_t * obj = lv_event_get_target(e);
|
||
bubble_level_dsc_t * d = lv_obj_get_user_data(obj);
|
||
if(!d) return;
|
||
|
||
// 1) Get the LVGL draw layer (clipping is automatic)
|
||
lv_layer_t * layer = lv_event_get_layer(e);
|
||
|
||
// 2) Figure out the object's inner rectangle
|
||
lv_area_t coords;
|
||
lv_obj_get_coords(obj, &coords);
|
||
lv_area_t inner = {
|
||
.x1 = coords.x1 + 2, .y1 = coords.y1 + 2,
|
||
.x2 = coords.x2 - 2, .y2 = coords.y2 - 2
|
||
};
|
||
|
||
// 3) Draw the border
|
||
lv_draw_rect_dsc_t rect_dsc;
|
||
lv_draw_rect_dsc_init(&rect_dsc);
|
||
rect_dsc.border_color = lv_color_hex(0xAAAAAA);
|
||
rect_dsc.border_width = 2;
|
||
rect_dsc.bg_opa = LV_OPA_TRANSP;
|
||
lv_draw_rect(layer, &rect_dsc, &inner);
|
||
|
||
// 4) Draw guide lines at the center
|
||
lv_coord_t mid_x = (inner.x1 + inner.x2) / 2;
|
||
lv_coord_t mid_y = (inner.y1 + inner.y2) / 2;
|
||
|
||
lv_draw_line_dsc_t line_dsc;
|
||
lv_draw_line_dsc_init(&line_dsc);
|
||
line_dsc.color = lv_color_hex(0x444444);
|
||
line_dsc.width = 1;
|
||
|
||
// Set up points and convert to 'precise' type
|
||
lv_point_t tl = { inner.x1 + 10, mid_y };
|
||
lv_point_t tr = { inner.x2 - 10, mid_y };
|
||
line_dsc.p1 = lv_point_to_precise(&tl);
|
||
line_dsc.p2 = lv_point_to_precise(&tr);
|
||
lv_draw_line(layer, &line_dsc);
|
||
|
||
lv_point_t tt = { mid_x, inner.y1 + 10 };
|
||
lv_point_t tb = { mid_x, inner.y2 - 10 };
|
||
line_dsc.p1 = lv_point_to_precise(&tt);
|
||
line_dsc.p2 = lv_point_to_precise(&tb);
|
||
lv_draw_line(layer, &line_dsc);
|
||
|
||
// 5) Compute bubble position (normalized between min_val → max_val)
|
||
float norm = (d->cur_val - d->min_val) / (d->max_val - d->min_val);
|
||
norm = norm < 0 ? 0 : (norm > 1 ? 1 : norm);
|
||
|
||
const lv_coord_t bubble_d = 16;
|
||
lv_coord_t avail_w = (inner.x2 - inner.x1 + 1) - bubble_d;
|
||
lv_coord_t bx = inner.x1 + (lv_coord_t)(norm * avail_w);
|
||
lv_coord_t by = mid_y - (bubble_d / 2);
|
||
|
||
// 6) Draw the bubble as a filled circle (rounded rect)
|
||
lv_draw_rect_dsc_t circ_dsc;
|
||
lv_draw_rect_dsc_init(&circ_dsc);
|
||
circ_dsc.bg_color = lv_color_hex(0x00CC00);
|
||
circ_dsc.bg_opa = LV_OPA_COVER;
|
||
circ_dsc.radius = bubble_d / 2;
|
||
circ_dsc.border_opa = LV_OPA_TRANSP;
|
||
|
||
lv_area_t circ_area = {
|
||
.x1 = bx,
|
||
.y1 = by,
|
||
.x2 = bx + bubble_d - 1,
|
||
.y2 = by + bubble_d - 1
|
||
};
|
||
lv_draw_rect(layer, &circ_dsc, &circ_area);
|
||
|
||
lvgl_port_unlock();
|
||
}
|
||
|
||
|
||
lv_obj_t * bubble_create(lv_obj_t * parent,
|
||
lv_coord_t width, lv_coord_t height,
|
||
float min_val, float max_val, float initial)
|
||
{
|
||
// 1) Create a container object (no special style)
|
||
lv_obj_t * obj = lv_obj_create(parent);
|
||
lv_obj_set_size(obj, width, height);
|
||
|
||
// (Optional) center it or let the caller position it:
|
||
// lv_obj_center(obj);
|
||
|
||
// 2) Allocate or attach our descriptor
|
||
// In LVGL v9 you can use USER_DATA: need to enable LV_USE_USER_DATA = 1
|
||
bubble_level_dsc_t * dsc = lv_malloc(sizeof(bubble_level_dsc_t));
|
||
if(!dsc) return NULL; /* handle out-of-memory */
|
||
dsc->min_val = min_val;
|
||
dsc->max_val = max_val;
|
||
dsc->cur_val = initial;
|
||
lv_obj_set_user_data(obj, dsc);
|
||
|
||
// 3) Register our draw callback
|
||
lv_obj_add_event_cb(obj, bubble_draw_cb, LV_EVENT_DRAW_MAIN, NULL);
|
||
|
||
// 4) Make sure the object does NOT get a default background fill
|
||
lv_obj_set_style_bg_opa(obj, LV_OPA_TRANSP, 0);
|
||
|
||
return obj;
|
||
}
|
||
|
||
|
||
void bubble_setValue(lv_obj_t * bubble, float v)
|
||
{
|
||
lvgl_port_lock(0);
|
||
// 1) Retrieve the descriptor
|
||
bubble_level_dsc_t * d = lv_obj_get_user_data(bubble);
|
||
if(!d) return;
|
||
|
||
// 2) Update the stored value
|
||
d->cur_val = v;
|
||
|
||
// 3) Tell LVGL that object must be re‐drawn
|
||
lv_obj_invalidate(bubble);
|
||
|
||
lvgl_port_unlock();
|
||
}
|