/* test_meow_ui_box.c — §12.1 meow_box 线性布局引擎宿主验证: * 布局求解矩阵(固定/weight/wrap/嵌套/对齐)、margin/padding/gap、交叉轴对齐、view 集成。 * 纯逻辑(只创控件 + arrange + 断言矩形),不依赖 /dev/fb0/字库;不绘制。 * * 编译(宿主 gcc,链真实 widget/draw/activity/theme/gfx + 打桩 font/global): * gcc -o test_meow_ui_box test_meow_ui_box.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_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_ui_box */ #include "meow_ui.h" #include "meow_ui_internal.h" /* meow_widget_rect:真实几何断言 */ #include "meow_ui_view.h" #include #include #include static int g_pass = 0, g_fail = 0; #define CHECK(cond, ...) do { \ if (cond) { g_pass++; } \ else { g_fail++; printf("FAIL %s:%d ", __FILE__, __LINE__); printf(__VA_ARGS__); printf("\n"); } \ } while (0) static void wr(meow_widget *w, int out[4]) { meow_widget_rect(w, out); } static void boxr(meow_box *b, int out[4]) { meow_box_rect(b, out); } /* 创建固定尺寸按钮(既有无绝对定位;注册到绘制物注册表即可) */ static meow_widget *mk(int w, int h) { meow_widget *b = meow_btn_create(0, 0, w, h, "X"); return b; } /* ---- 竖直列:固定 + padding + gap + CROSS_START ---- */ static void test_col_fixed(void) { meow_box *bx = meow_box_new(MEOW_BOX_V); CHECK(bx != NULL, "box_new V"); meow_box_set_rect(bx, 0, 0, 480, 720); meow_box_set_padding(bx, 20, 20, 20, 20); meow_box_set_gap(bx, 10); meow_box_set_align(bx, MEOW_BOX_ALIGN_START, MEOW_BOX_CROSS_START); meow_widget *a = mk(100, 50), *b = mk(120, 50), *c = mk(80, 50); CHECK(meow_box_add(bx, a, MEOW_BOX_FIXED, 0) == 0, "add a"); CHECK(meow_box_add(bx, b, MEOW_BOX_FIXED, 0) == 0, "add b"); CHECK(meow_box_add(bx, c, MEOW_BOX_FIXED, 0) == 0, "add c"); CHECK(meow_box_count(bx) == 3, "count"); meow_box_arrange(bx); int r[4]; wr(a, r); CHECK(r[0] == 20 && r[1] == 20 && r[2] == 100 && r[3] == 50, "A x=20 y=20 w100 h50 got %d,%d,%d,%d", r[0], r[1], r[2], r[3]); wr(b, r); CHECK(r[0] == 20 && r[1] == 80 && r[2] == 120, "B y=80 got %d,%d,%d,%d", r[0], r[1], r[2], r[3]); wr(c, r); CHECK(r[0] == 20 && r[1] == 140 && r[2] == 80, "C y=140 got %d,%d,%d,%d", r[0], r[1], r[2], r[3]); meow_box_free(bx); } /* ---- 横向行:END 对齐 + 交叉轴中心 ---- */ static void test_row_end_center(void) { meow_box *bx = meow_box_new(MEOW_BOX_H); meow_box_set_rect(bx, 10, 0, 480, 200); meow_box_set_padding(bx, 10, 10, 10, 10); meow_box_set_gap(bx, 5); meow_box_set_align(bx, MEOW_BOX_ALIGN_END, MEOW_BOX_CROSS_CENTER); meow_widget *a = mk(100, 50), *b = mk(100, 50), *c = mk(100, 50); CHECK(meow_box_add(bx, a, MEOW_BOX_FIXED, 0) == 0, "add"); CHECK(meow_box_add(bx, b, MEOW_BOX_FIXED, 0) == 0, "addb"); CHECK(meow_box_add(bx, c, MEOW_BOX_FIXED, 0) == 0, "addc"); meow_box_arrange(bx); /* 内容区 x∈[20,470] cw=460;foot=100*3+5*2=310;leftover=150;END → off0=150 */ int r[4]; wr(a, r); CHECK(r[0] == 20 + 150, "END first x=170 got %d", r[0]); /* 交叉轴中心:cy=10(pad_t);cross_size=ch=180,cross_base=50 → pad=130 → cross=65 → y=10+65=75 */ CHECK(r[1] == 75 && r[3] == 50, "center y=75 got %d,%d", r[1], r[3]); wr(b, r); CHECK(r[0] == 20 + 150 + 100 + 5, "END second x=%d", r[0]); wr(c, r); CHECK(r[0] == 380, "END third x=380 got %d", r[0]); /* 170+100+5+100+5+? = 380 */ meow_box_free(bx); } /* ---- weight 均分:竖直,两个 weight 1:3 ---- */ static void test_weight(void) { meow_box *bx = meow_box_new(MEOW_BOX_H); meow_box_set_rect(bx, 0, 0, 480, 200); /* 内容区 480 宽(无 pad/gap)。child 固定 80 + 两个权重 1:3 */ meow_widget *fixed = mk(80, 200), *w1 = mk(0, 200), *w2 = mk(0, 200); CHECK(meow_box_add(bx, fixed, MEOW_BOX_FIXED, 0) == 0, "fixed"); CHECK(meow_box_add(bx, w1, MEOW_BOX_WEIGHT, 1) == 0, "w1"); CHECK(meow_box_add(bx, w2, MEOW_BOX_WEIGHT, 3) == 0, "w2"); meow_box_arrange(bx); /* leftover = 480 - 80 = 400;w1=100, w2=300 */ int r[4]; wr(fixed, r); CHECK(r[0] == 0 && r[2] == 80, "fixed x=0 w80 got %d,%d", r[0], r[2]); wr(w1, r); CHECK(r[0] == 80 && r[2] == 100, "w1 x=80 w100 got %d,%d", r[0], r[2]); wr(w2, r); CHECK(r[0] == 180 && r[2] == 300, "w2 x=180 w300 got %d,%d", r[0], r[2]); meow_box_free(bx); } /* ---- WRAP 内容测量(measure 回调给 min/preferred) ---- */ static void wrap_measure(void *ctx, int *mn, int *pf) { (void)ctx; *mn = 40; *pf = 120; } static void test_wrap(void) { meow_box *bx = meow_box_new(MEOW_BOX_H); meow_box_set_rect(bx, 0, 0, 480, 100); meow_box_set_gap(bx, 10); meow_box_set_align(bx, MEOW_BOX_ALIGN_START, MEOW_BOX_CROSS_START); meow_widget *w = mk(5, 40); /* 尺寸无关,wrap 用 measure */ CHECK(meow_box_add_ex(bx, w, MEOW_BOX_WRAP, 0, 40, 120, wrap_measure, NULL) == 0, "wrap-add"); meow_box_arrange(bx); int r[4]; wr(w, r); /* wrap 主轴向 preferred=120;cross_start 交叉=40(widget h 落 V=40?这是 H box,交叉=h) */ CHECK(r[2] == 120, "wrap pref w=120 got %d", r[2]); meow_box_free(bx); } /* ---- 嵌套:Row 内嵌 Column ---- */ static void test_nested(void) { meow_box *row = meow_box_new(MEOW_BOX_H); meow_box *col = meow_box_new(MEOW_BOX_V); meow_box_set_rect(row, 0, 0, 480, 200); meow_box_set_rect(col, 0, 0, 200, 200); meow_box_set_gap(row, 10); meow_widget *a = mk(100, 50), *b = mk(100, 50), *c = mk(100, 50); meow_box_add(row, a, MEOW_BOX_FIXED, 0); meow_box_add_sub(row, col, MEOW_BOX_FIXED, 0); CHECK(meow_box_add_sub(row, col, MEOW_BOX_FIXED, 0) < 0, "sub dedupe"); meow_box_add(col, b, MEOW_BOX_FIXED, 0); meow_box_add(col, c, MEOW_BOX_FIXED, 0); meow_box_arrange(row); int r[4]; wr(a, r); CHECK(r[0] == 0 && r[2] == 100, "row a x0 w100 got %d,%d", r[0], r[2]); int cr[4]; boxr(col, cr); CHECK(cr[0] == 110 && cr[1] == 0 && cr[2] == 200, "sub box x=110 got %d", cr[0]); /* col 内 b,c 纵向:y 0,50(col y=0, gap0, cross_start) */ meow_widget_rect(b, r); CHECK(r[0] == 110 && r[1] == 0, "col b y0 got %d", r[1]); meow_widget_rect(c, r); CHECK(r[1] == 50, "col c y50 got %d", r[1]); meow_box_free(row); meow_box_free(col); } /* ---- margin(widget opts)+ 交叉轴 STRETCH ---- */ static void test_margin_stretch(void) { meow_box *bx = meow_box_new(MEOW_BOX_V); meow_box_set_rect(bx, 0, 0, 480, 300); meow_box_set_align(bx, MEOW_BOX_ALIGN_START, MEOW_BOX_CROSS_STRETCH); meow_widget *a = mk(100, 60); meow_widget_opts o; memset(&o, 0, sizeof o); o.margin_x = 30; o.margin_y = 10; CHECK(meow_widget_set_opts(a, &o) == 0, "set opts"); meow_box_add(bx, a, MEOW_BOX_FIXED, 0); meow_box_arrange(bx); int r[4]; wr(a, r); /* V:交叉轴=x,STRETCH → w = cw - 2*margin_x = 480 - 60 = 420;x = margin_x = 30。 * 主轴 y:lead=margin_y=10 → y=10;ext=h=60。 */ CHECK(r[0] == 30 && r[2] == 420, "stretch w=420 x=30 got %d,%d", r[0], r[2]); CHECK(r[1] == 10 && r[3] == 60, "y=10 h=60 got %d,%d", r[1], r[3]); meow_box_free(bx); } /* ---- SPACE_BETWEEN ---- */ static void test_space_between(void) { meow_box *bx = meow_box_new(MEOW_BOX_H); meow_box_set_rect(bx, 0, 0, 500, 100); meow_box_set_align(bx, MEOW_BOX_ALIGN_SPACE_BETWEEN, MEOW_BOX_CROSS_START); meow_widget *a = mk(100, 50), *b = mk(100, 50), *c = mk(100, 50); meow_box_add(bx, a, MEOW_BOX_FIXED, 0); meow_box_add(bx, b, MEOW_BOX_FIXED, 0); meow_box_add(bx, c, MEOW_BOX_FIXED, 0); meow_box_arrange(bx); /* cw=500;foot=300;leftover=200;n-1=2 → 每 gap += 100 → A.x=0 B.x=200 C.x=400 */ int r[4]; wr(a, r); CHECK(r[0] == 0, "sb A x0 got %d", r[0]); wr(b, r); CHECK(r[0] == 200, "sb B x200 got %d", r[0]); wr(c, r); CHECK(r[0] == 400, "sb C x400 got %d", r[0]); meow_box_free(bx); } /* ---- view 集成:meow_view_set_layout + relayout ---- */ static void test_view_layout(void) { meow_view *v = meow_view_create(900, MEOW_VIEW_ACTIVITY, NULL, NULL); CHECK(v != NULL, "view create"); v->x = 0; v->y = 0; v->w = 480; v->h = 720; meow_box *bx = meow_box_new(MEOW_BOX_V); meow_box_set_gap(bx, 8); meow_box_set_align(bx, MEOW_BOX_ALIGN_START, MEOW_BOX_CROSS_START); meow_widget *a = mk(300, 50), *b = mk(300, 50); meow_box_add(bx, a, MEOW_BOX_FIXED, 0); meow_box_add(bx, b, MEOW_BOX_FIXED, 0); meow_view_add_widget(v, a); meow_view_add_widget(v, b); meow_view_set_layout(v, bx); /* relayout 立即排一次 */ int r[4]; wr(a, r); CHECK(r[0] == 0 && r[1] == 0 && r[2] == 300, "view a x0 y0 w300 got %d,%d,%d", r[0], r[1], r[2]); wr(b, r); CHECK(r[1] == 58, "view b y=58 got %d", r[1]); /* 0 + 50 + gap8 */ meow_view_free(v); meow_box_free(bx); } /* ---- 越界去重 / 池满 ---- */ static void test_edge(void) { meow_box *bx = meow_box_new(MEOW_BOX_H); meow_widget *a = mk(10, 10), *dup = mk(10, 10); CHECK(meow_box_add(bx, dup, MEOW_BOX_FIXED, 0) == 0, "add dup"); CHECK(meow_box_add(bx, dup, MEOW_BOX_FIXED, 0) < 0, "dedupe"); CHECK(meow_box_add(bx, NULL, MEOW_BOX_FIXED, 0) < 0, "null add rejected"); meow_box_widget_at(bx, 0) ? 0 : 0; CHECK(meow_box_widget_at(bx, 0) == dup, "widget_at 0"); CHECK(meow_box_sub_at(bx, 0) == NULL, "sub_at null"); meow_box_free(bx); } int main(void) { test_col_fixed(); test_row_end_center(); test_weight(); test_wrap(); test_nested(); test_margin_stretch(); test_space_between(); test_view_layout(); test_edge(); printf("box: %d pass / %d fail\n", g_pass, g_fail); return g_fail ? 1 : 0; }