feat(sdk): 控件库第二批 Material 子集(agent-152)

- 新增控件: Button五型(filled/tonal/outlined/text/elevated)/TextField(接IME: round外框idle-outline/focused-primary+占位+光标)/Card/Divider/Badge/Checkbox/Radio/ProgressBar(线圆, value=-1不定)/FAB/TopAppBar(标题+‹返回+动作)/NavigationBar(TabBar pageSwitcher)+TabBar(下划线)/LazyColumn/LazyRow(虚拟化+scroll clamp)
- Dialog(B③同构阻塞模态)/BottomSheet(B④), meow_ui_dialog.c
- meow_gfx 新增 fill_circle/line/ring_arc(无-lm, COS90象限查表)
- 结构体尾缀(对外opaque/calloc归零), 旧字段与create签名零破坏; meow_widget 类型扩到W_TEXTFIELD..W_FAB(17种)
- meow_ui.a=18c21da5 hello=5e31d919 launcher=4952261d(重编重链无回归); build_sdk 0warning; 宿主 test_meow_ui_widgets 全PASS+test_sb_collapse PASS+PPM目检正确
- 与 §5.8/§5.2-5.7 一致; 遗留真机观感/性能
This commit is contained in:
MeowOS committed 2026-08-30 22:33:51 +08:00
1 parent 2cb6a7c16f
commit 9c733fcf73
10 files changed
+1360 -24

No files matched your search

+180
View File
@@ -0,0 +1,180 @@
/* test_meow_ui_widgets.c — 宿主验证 §5.8.6 Material 子集纯逻辑(create/hit/state/滚动/选中)+
* 各组件绘制(5x7 字库兜底)dump PPM 目检。不依赖 /dev/fb0/字库(meow_font 打桩)。
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
* gcc -o test_meow_ui_widgets test_meow_ui_widgets.c test_host_stub.c \
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
* 运行:MEOW_UI_DUMP=/tmp/b2.ppm ./test_meow_ui_widgets (可选,dump 屏幕)
*/
#include "meow_ui.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* 测试数据源(列表容器) */
static int list_src(int idx, char *label, int len, void *ctx)
{
(void)ctx;
if (idx < 0 || idx >= 100) return 0;
snprintf(label, (size_t)len, "item %d", idx);
return 1;
}
static meow_widget *g_w[64];
static int g_wn = 0;
static meow_widget *W(meow_widget *w) { g_w[g_wn++] = w; return w; }
static void free_all(void)
{
for (int i = 0; i < g_wn; i++) meow_widget_free(g_w[i]);
g_wn = 0;
}
int main(void)
{
setenv("MEOW_UI_DUMP", "/tmp/b2.ppm", 1);
meow_ui_opts o = { 480, 720 };
assert(meow_ui_init(&o) == 0);
int xres = meow_ui_width(), yres = meow_ui_height();
assert(xres == 480 && yres == 720);
/* ---------- Button 五型:create_variant/set_variant + 命中 ---------- */
meow_widget *bf = W(meow_btn_create_variant(10, 10, 200, 56, "FILLED", MEOW_BTN_FILLED));
meow_widget *bt = W(meow_btn_create_variant(10, 80, 200, 56, "TONAL", MEOW_BTN_TONAL));
meow_widget *bo = W(meow_btn_create_variant(10, 150, 200, 56, "OUTLINED", MEOW_BTN_OUTLINED));
meow_widget *bx = W(meow_btn_create_variant(10, 220, 200, 56, "TEXT", MEOW_BTN_TEXT));
meow_widget *be = W(meow_btn_create_variant(10, 290, 200, 56, "ELEV", MEOW_BTN_ELEVATED));
assert(meow_btn_set_variant(bt, MEOW_BTN_TONAL) == 0); /* 同值 */
assert(meow_btn_set_variant(bo, MEOW_BTN_OUTLINED) == 0);
assert(meow_widget_hit(bf, 100, 30) == 1);
assert(meow_widget_hit(bf, 5, 5) == 0); /* 越界 */
meow_widget_set_enabled(bf, 0);
assert(meow_widget_hit(bf, 100, 30) == 0); /* 禁用不命中 */
meow_widget_set_enabled(bf, 1);
meow_widget_set_selected(be, 1);
assert(meow_widget_selected(be) == 1);
printf("PASS button-variant: create/set/hit/disabled/selected\n");
/* ---------- TextField ---------- */
char tfbuf[32] = "";
meow_widget *tf = W(meow_textfield_create(10, 360, 300, 44, "Type here...", tfbuf, 32, MEOW_IME_ASCII));
strcpy(tfbuf, "abc");
assert(strcmp(meow_textfield_text(tf), "abc") == 0);
assert(meow_widget_hit(tf, 20, 380) == 1);
meow_textfield_set_placeholder(tf, "placeholder");
printf("PASS textfield: create/text/placeholder/hit\n");
/* ---------- Card + content ---------- */
meow_widget *card = W(meow_card_create(240, 10, 220, 180));
meow_widget *inner = W(meow_btn_create_variant(260, 40, 180, 44, "IN CARD", MEOW_BTN_FILLED));
meow_card_set_content(card, inner);
assert(meow_widget_hit(card, 250, 20) == 1);
printf("PASS card: create/content/hit\n");
/* ---------- Divider / Badge ---------- */
meow_widget *dv = W(meow_divider_create(10, 470, 200, 0));
meow_widget *dv2 = W(meow_divider_create(230, 470, 8, 1));
meow_widget *bd = W(meow_badge_create(400, 100));
meow_badge_set(bd, 3);
meow_badge_set(bd, 0);
printf("PASS divider/badge: create/set\n");
/* ---------- Checkbox / Radio ---------- */
meow_widget *ck = W(meow_checkbox_create(300, 200));
meow_widget *rd = W(meow_radio_create(300, 250));
meow_widget_set_selected(ck, 1);
meow_widget_set_selected(rd, 1);
assert(meow_widget_selected(ck) == 1);
assert(meow_widget_hit(rd, 315, 265) == 1);
printf("PASS checkbox/radio: create/select/hit\n");
/* ---------- ProgressBar ---------- */
meow_widget *pg = W(meow_progress_create(10, 520, 300, 6, 40));
meow_progress_set(pg, 200); /* clamp 100(无 getter,仅不崩) */
meow_widget *pgc = W(meow_progress_circular_create(420, 520, 20, 66));
meow_progress_set(pgc, -1); /* 不定 */
printf("PASS progress: create/set linear+circular\n");
/* ---------- FAB ---------- */
meow_widget *fab = W(meow_fab_create(410, 620, 56, "+"));
assert(meow_widget_hit(fab, 430, 640) == 1);
printf("PASS fab: create/hit\n");
/* ---------- TopAppBar ---------- */
meow_widget *tab_ = W(meow_topappbar_create(0, 0, 480, 56, "Title"));
meow_topappbar_set_back(tab_, 1);
int a0 = meow_topappbar_add_action(tab_, "Save");
int a1 = meow_topappbar_add_action(tab_, "Edit");
assert(a0 == 0 && a1 == 1);
assert(meow_topappbar_hit(tab_, 10, 28) == MEOW_TOPBAR_BACK); /* 返回区 */
assert(meow_topappbar_hit(tab_, 440, 28) == 1); /* 右缘动作 "Edit"(最右) */
printf("PASS topappbar: create/back/action/hit\n");
/* ---------- NavigationBar ---------- */
meow_widget *nav = W(meow_navigationbar_create(0, 680, 480, 40));
int n0 = meow_navigationbar_add_item(nav, "Home");
int n1 = meow_navigationbar_add_item(nav, "Library");
int n2 = meow_navigationbar_add_item(nav, "Settings");
assert(n0 == 0 && n1 == 1 && n2 == 2);
meow_navigationbar_set_selected(nav, 2);
assert(meow_navigationbar_selected(nav) == 2);
assert(meow_navigationbar_hit(nav, 420, 700) == 2); /* 第 3 项 */
assert(meow_navigationbar_hit(nav, 10, 700) == 0);
printf("PASS navigationbar: add/select/hit\n");
/* ---------- TabBar ---------- */
meow_widget *tbb = W(meow_tabbar_create(0, 660, 480, 36));
meow_tabbar_add_tab(tbb, "Playlist");
meow_tabbar_add_tab(tbb, "Queue");
meow_tabbar_set_selected(tbb, 1);
assert(meow_tabbar_hit(tbb, 300, 678) == 1);
printf("PASS tabbar: add/select/hit\n");
/* ---------- 列表容器(LazyColumn 虚拟化 + 滚动) ---------- */
meow_widget *lc = W(meow_lazycolumn_create(20, 460, 260, 180, 40, 100, list_src, NULL));
assert(meow_list_scroll(lc) == 0);
assert(meow_list_max_scroll(lc) == 100 * 40 - 180); /* 4000-180=3820 */
meow_list_set_scroll(lc, 5000);
assert(meow_list_scroll(lc) == 3820); /* clamp 到 max */
meow_list_scroll_by(lc, -320);
assert(meow_list_scroll(lc) == 3500);
assert(meow_list_hit_index(lc, 30, 480) == ((480 - 460 + 3500) / 40)); /* 可见项换算 */
meow_list_set_item_h(lc, 50);
assert(meow_widget_hit(lc, 30, 470) == 1);
/* LazyRow 横向 */
meow_widget *lr = W(meow_lazyrow_create(20, 640, 380, 40, 60, 100, list_src, NULL));
meow_list_scroll_by(lr, 200);
assert(meow_list_hit_index(lr, 40, 660) == ((40 - 20 + 200) / 60));
printf("PASS lazy list: scroll clamp/virtualize/hit_index (col+row)\n");
/* ---------- Dialog / BottomSheet 纯逻辑(create/set/add;show 阻塞故不调) ---------- */
meow_dialog *dlg = meow_dialog_create(MEOW_DLG_SELECT, "Pick one");
const char *items[] = { "Alpha", "Beta", "Gamma" };
meow_dialog_set_items(dlg, items, 3, 1);
assert(meow_dialog_result(dlg) == 0); /* 未 show 默认 */
meow_dialog_free(dlg);
meow_dialog *dc = meow_dialog_create(MEOW_DLG_CONFIRM, "Sure?");
assert(meow_dialog_result(dc) == 0);
meow_dialog_free(dc);
meow_sheet *sh = meow_sheet_create("Options", 0);
int s0 = meow_sheet_add_item(sh, "Copy");
int s1 = meow_sheet_add_item(sh, "Delete");
assert(s0 == 0 && s1 == 1);
meow_sheet_free(sh);
printf("PASS dialog/sheet: create/set_items/add_item (blocking show skipped)\n");
/* ---------- 绘制所有控件到 back_buf(5x7 兜底),flush 出屏幕 ---------- */
const meow_theme *th = meow_theme_get();
meow_ui_clear(th->bg);
for (int i = 0; i < g_wn; i++) meow_widget_draw(g_w[i]);
meow_ui_flush();
printf("PASS draw: all %d widgets rendered, screen dumps to /tmp/b2.ppm\n", g_wn);
free_all();
meow_ui_shutdown();
printf("ALL PASS\n");
return 0;
}