/* * meow_ui_widget.c — 控件层(B1.3):按钮/滑条/开关/列表最小集。 * * 控件只负责绘制与状态;命中分发由应用在 on_tap 里调 meow_widget_hit 完成 * (与 MEOW_APP_SDK.md §5 一致)。绘制读当前主题(meow_theme_get)走 back_buf。 */ #include "meow_ui.h" #include "meow_ui_internal.h" #include #include enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3 }; struct meow_widget { int type; int x, y, w, h; char label[128]; int pressed; /* 按钮按下视觉态 */ int min, max, val; /* 滑条 */ int on; /* 开关 */ int count, sel; /* 列表 */ meow_list_source src; void *ctx; }; meow_widget *meow_btn_create(int x, int y, int w, int h, const char *label) { meow_widget *t = (meow_widget *)calloc(1, sizeof *t); if (!t) return NULL; t->type = W_BTN; t->x = x; t->y = y; t->w = w; t->h = h; if (label) { strncpy(t->label, label, sizeof t->label - 1); t->label[sizeof t->label - 1] = 0; } return t; } meow_widget *meow_slider_create(int x, int y, int w, int min, int max, int val) { meow_widget *t = (meow_widget *)calloc(1, sizeof *t); if (!t) return NULL; t->type = W_SLIDER; t->x = x; t->y = y; t->w = w; t->h = 40; t->min = min; t->max = (max > min) ? max : min + 1; t->val = (val < min) ? min : (val > t->max) ? t->max : val; return t; } meow_widget *meow_switch_create(int x, int y, int on) { meow_widget *t = (meow_widget *)calloc(1, sizeof *t); if (!t) return NULL; t->type = W_SWITCH; t->x = x; t->y = y; t->w = 56; t->h = 32; t->on = on ? 1 : 0; return t; } meow_widget *meow_list_create(int x, int y, int w, int h, int count, meow_list_source src, void *ctx) { meow_widget *t = (meow_widget *)calloc(1, sizeof *t); if (!t) return NULL; t->type = W_LIST; t->x = x; t->y = y; t->w = w; t->h = h; t->count = count; t->src = src; t->ctx = ctx; t->sel = 0; return t; } void meow_widget_free(meow_widget *w) { if (w) free(w); } static int hit(meow_widget *w, int x, int y) { if (!w) return 0; return x >= w->x && x < w->x + w->w && y >= w->y && y < w->y + w->h; } int meow_widget_hit(meow_widget *w, int x, int y) { return hit(w, x, y); } int meow_btn_pressed(meow_widget *w) { return w && w->type == W_BTN ? w->pressed : 0; } void meow_slider_set(meow_widget *w, int val) { if (!w || w->type != W_SLIDER) return; if (val < w->min) val = w->min; if (val > w->max) val = w->max; w->val = val; } int meow_slider_get(meow_widget *w) { return w && w->type == W_SLIDER ? w->val : 0; } void meow_switch_set(meow_widget *w, int on) { if (w && w->type == W_SWITCH) w->on = on ? 1 : 0; } int meow_switch_get(meow_widget *w) { return w && w->type == W_SWITCH ? w->on : 0; } /* 供应用在 on_tap 里对外可控回馈:这里不存命中态(由 app 分发),仅暴露绘制 */ void meow_widget_draw(meow_widget *w) { if (!w) return; const meow_theme *th = meow_theme_get(); switch (w->type) { case W_BTN: { uint32_t bg = w->pressed ? th->dim : th->accent; meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, bg); meow_ui_text_center(w->x + w->w / 2, w->y + (w->h - meow_ui_text_height(th->font_body)) / 2, w->label, th->font_body, 0xFFFFFF); break; } case W_SLIDER: { int trace_y = w->y + w->h / 2 - 3; meow_ui_fill_rect(w->x, trace_y, w->w, 6, th->dim); int range = w->max - w->min; int frac = range ? (w->val - w->min) * w->w / range : 0; if (frac > 0) meow_ui_fill_rect(w->x, trace_y, frac, 6, th->accent); int tx = w->x + frac; int ty = w->y + w->h / 2; meow_gfx_fill_round(tx - 10, ty - 10, 20, 20, 10, th->fg); break; } case W_SWITCH: { uint32_t bg = w->on ? th->accent : th->dim; meow_gfx_fill_round(w->x, w->y, w->w, w->h, w->h / 2, bg); int knob_r = w->h / 2 - 3; int kx = w->on ? w->x + w->w - knob_r * 2 - 3 : w->x + 3; meow_gfx_fill_round(kx, w->y + 3, knob_r * 2, knob_r * 2, knob_r, 0xFFFFFF); break; } case W_LIST: { int rowh = 44, pad = 16; for (int i = 0; i < w->count; i++) { int ry = w->y + i * rowh; if (ry >= w->y + w->h) break; if (i == w->sel) meow_ui_fill_rect(w->x, ry, w->w, rowh, th->accent); char label[64] = ""; if (w->src && w->src(i, label, (int)sizeof label, w->ctx)) { uint32_t col = (i == w->sel) ? 0xFFFFFF : th->fg; meow_ui_text(w->x + pad, ry + (rowh - meow_ui_text_height(th->font_body)) / 2, label, th->font_body, col); } meow_ui_fill_rect(w->x, ry + rowh - 1, w->w, 1, th->dim); } break; } default: break; } }