/* test_meow_md3.c — 宿主验证 MD3(Material You)对齐修复(agent-434 P0/P1/P3): * P0 listview 容器模式(默认 renderer:行=surfaceContainer 圆角卡 + 行间空隙 + 去分隔线); * P1 按钮胶囊化(圆角=h/2); * P3 Switch off-track/off-thumb、NavigationBar active pill 64×32、Checkbox/Radio 视觉尺寸。 * 断言全部「画完读 back_buf 像素」(region_has/px_near),不依赖 /dev/fb0/字库(meow_font 打桩)。 * * 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global): * gcc -Wall -Wextra -o test_meow_md3 test_meow_md3.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 meow_ui_str.c \ * meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \ * meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \ * -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm * 运行:./test_meow_md3 */ #include "meow_ui.h" #include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp */ #include #include #include #include static void rd_px(int x, int y, int *r, int *g, int *b) { unsigned char *bb = meow_ui_back_buf(); int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp(); if (!bb || x < 0 || y < 0) { *r = *g = *b = 0; return; } if (bpp == 16) { uint16_t px = *(uint16_t *)(bb + (size_t)y * line + (size_t)x * 2); *r = (((px >> 11) & 0x1F) << 3) | ((px >> 11) & 0x1F) >> 2; *g = (((px >> 5) & 0x3F) << 2) | ((px >> 5) & 0x3F) >> 4; *b = ((px & 0x1F) << 3) | ((px & 0x1F) >> 2); } else { uint32_t px = *(uint32_t *)(bb + (size_t)y * line + (size_t)x * 4); *r = (px >> 16) & 0xFF; *g = (px >> 8) & 0xFF; *b = px & 0xFF; } } static int near2(int a, int b) { int d = a - b; if (d < 0) d = -d; return d <= 12; } static int px_near(int x, int y, int r, int g, int b) { int rr, gg, bb; rd_px(x, y, &rr, &gg, &bb); return near2(rr, r) && near2(gg, g) && near2(bb, b); } #define UNPK(c, rr, gg, bb) do { (rr) = ((c) >> 16) & 0xFF; (gg) = ((c) >> 8) & 0xFF; (bb) = (c) & 0xFF; } while (0) /* Listview src2 数据源:2 行普通富 item(无图标、无右尾件,label-only)。 */ typedef struct { const char *title; const char *sub; int right; int enabled; } trow_t; static trow_t g_rows[2] = { { "Row 0", NULL, MEOW_LIST_RIGHT_NONE, 1 }, { "Row 1", NULL, MEOW_LIST_RIGHT_NONE, 1 }, }; static int trow_src(int idx, meow_list_item *out, void *ctx) { (void)ctx; if (idx < 0 || idx >= 2) return 0; out->icon_kind = MEOW_LIST_ICON_NONE; out->title = g_rows[idx].title; out->subtitle = g_rows[idx].sub; out->right = g_rows[idx].right; out->on = 0; out->enabled = g_rows[idx].enabled; return 1; } int main(void) { setenv("MEOW_UI_DUMP", "/tmp/md3.ppm", 1); meow_ui_opts o = { 480, 720 }; assert(meow_ui_init(&o) == 0); assert(meow_ui_width() == 480 && meow_ui_height() == 720); const meow_theme *th = meow_theme_get(); int sf_r, sf_g, sf_b, sc_r, sc_g, sc_b, p_r, p_g, p_b; int pc_r, pc_g, pc_b, ov_r, ov_g, ov_b, sch_r, sch_g, sch_b, scs_r, scs_g, scs_b; UNPK(th->surface, sf_r, sf_g, sf_b); UNPK(th->surfaceContainer, sc_r, sc_g, sc_b); UNPK(th->primary, p_r, p_g, p_b); UNPK(th->primaryContainer, pc_r, pc_g, pc_b); UNPK(th->outline, ov_r, ov_g, ov_b); UNPK(th->surfaceContainerHighest, sch_r, sch_g, sch_b); UNPK(th->secondaryContainer, scs_r, scs_g, scs_b); /* ---- P0 listview 容器模式:行=surfaceContainer 圆角卡 + 空隙,去分隔线 ---- */ meow_widget *lv = meow_listview_create(20, 240, 320, 112, 2, trow_src, NULL, MEOW_LIST_SEL_SINGLE); assert(lv); assert(meow_listview_set_item_h(lv, 56) == 56); assert(meow_listview_set_container(lv, 1) == 0); meow_ui_clear(th->surface); meow_widget_draw(lv); /* 行 0 卡(x=28,y=244,w=304,h=48):中心=surfaceContainer;行 1 同。 */ assert(px_near(180, 268, sc_r, sc_g, sc_b)); assert(px_near(180, 324, sc_r, sc_g, sc_b)); /* 卡间空隙(y=292..300):surface(行间分隔用空隙)。 */ assert(px_near(180, 296, sf_r, sf_g, sf_b)); /* 去分隔线:行边界不再画 outlineVariant(改空隙代替)。 */ assert(!px_near(180, 292, ov_r, ov_g, ov_b)); /* 选中行 0 → primaryContainer(tonal,非满幅实心)。 */ meow_listview_set_sel(lv, 0); meow_ui_clear(th->surface); meow_widget_draw(lv); assert(px_near(180, 268, pc_r, pc_g, pc_b)); assert(px_near(180, 324, sc_r, sc_g, sc_b)); /* 行 1 未选仍卡 */ printf("PASS listview container: rounded card + gap + no-divider + tonal select\n"); /* ---- P1 按钮胶囊化:圆角=h/2(56/2=28)---- */ meow_widget *btn = meow_btn_create_variant(10, 10, 200, 56, "FILLED", MEOW_BTN_FILLED); assert(btn); meow_ui_clear(th->surface); meow_widget_draw(btn); assert(px_near(110, 22, p_r, p_g, p_b)); /* 上部(避开文字)=primary */ /* 顶缘中心在圆角胶囊内(仍 primary);角落(圆角外)回 surface。 */ assert(px_near(110, 13, p_r, p_g, p_b)); assert(px_near(12, 12, sf_r, sf_g, sf_b)); /* 角落被圆角切掉 */ /* 同高矮按钮(h=40 → 圆角 20):上部仍 primary,角落 surface。 */ meow_widget *btn2 = meow_btn_create_variant(10, 120, 120, 40, "B", MEOW_BTN_FILLED); assert(btn2); meow_ui_clear(th->surface); meow_widget_draw(btn2); assert(px_near(70, 125, p_r, p_g, p_b)); assert(px_near(12, 122, sf_r, sf_g, sf_b)); printf("PASS button: capsule radius = h/2\n"); /* ---- P3 Switch off-track/off-thumb ---- */ meow_widget *sw = meow_switch_create(20, 300, 0); /* off */ assert(sw); meow_ui_clear(th->surface); meow_widget_draw(sw); assert(px_near(60, 316, sch_r, sch_g, sch_b)); /* off-track=surfaceContainerHighest */ assert(px_near(36, 316, sf_r, sf_g, sf_b)); /* off-thumb=surface(表面色,非恒白) */ meow_switch_set(sw, 1); meow_ui_clear(th->surface); meow_widget_draw(sw); assert(px_near(28, 316, p_r, p_g, p_b)); /* on-track=primary(thumb 左侧) */ printf("PASS switch: off-track surfaceContainerHighest / off-thumb surface\n"); /* ---- P3 NavigationBar active pill 64×32 ---- */ meow_widget *nb = meow_navigationbar_create(0, 600, 480, 80); assert(nb); meow_navigationbar_add_item(nb, "A"); meow_navigationbar_add_item(nb, "B"); meow_navigationbar_add_item(nb, "C"); meow_navigationbar_add_item(nb, "D"); meow_navigationbar_set_selected(nb, 0); meow_ui_clear(th->surface); meow_widget_draw(nb); /* item0 pill 中心 (ix+60, y+40)=(60,640) — 64×32 居中于栏;pill 内=secondaryContainer。 */ assert(px_near(60, 640, scs_r, scs_g, scs_b)); /* pill 外(栏上部)= surfaceContainer(navbar 容器底)。 */ assert(px_near(60, 610, sc_r, sc_g, sc_b)); printf("PASS navbar: active pill 64x32 (was 48x22)\n"); /* ---- P3 Checkbox 视觉 24dp(原 34 偏大):勾选框视觉居中,角落回 surface ---- */ meow_widget *ck = meow_checkbox_create(150, 60); assert(ck); meow_ui_clear(th->surface); meow_widget_draw(ck); /* 未选:24dp 描边框(左缘=150+5=155);框边=outline;内部=surface(空心)。 */ assert(px_near(155, 77, ov_r, ov_g, ov_b)); /* 框左缘 stroke */ assert(px_near(167, 77, sf_r, sf_g, sf_b)); /* 内部空心 */ meow_widget_set_selected(ck, 1); meow_ui_clear(th->surface); meow_widget_draw(ck); /* 选中:24dp 实心 primary 居中(左缘 155..179 顶 65..89);控件矩形角落(视觉 24 外)不填。 */ assert(px_near(150, 60, sf_r, sf_g, sf_b)); /* 视觉 24 外 → surface */ assert(px_near(167, 77, p_r, p_g, p_b)); /* 视觉中心=primary */ printf("PASS checkbox: 24dp visual (was 34)\n"); meow_widget_free(lv); meow_widget_free(btn); meow_widget_free(btn2); meow_widget_free(sw); meow_widget_free(nb); meow_widget_free(ck); printf("ALL MD3 PASS\n"); return 0; }