公开 SDK 前把 SDK/App 硬编码文案外置:新增 meow_str/meow_strf/meow_str_set_locale/ meow_str_locale,按当前 locale 取串(永不 NULL),第三方开发者可做多语言。 - 新增 include/meow_ui_str.h + meow_ui_str.c:取串优先级 = TF 覆盖表 > 内嵌当前语言 (en_us→en 其余→zh) > 内嵌内置默认(zh_cn) > 原样返回 id。内嵌双语表(common.* 21 条 + ime.* 9 条);热路径零 malloc(编译期静态表 + ≤256 静态覆盖数组);纯 libc 宿主可单测。 - locale 存系统设置 settings.conf 的 ui.locale(默认 zh_cn,首次调用惰性读取); meow_str_set_locale 即时切换 + 重载覆盖表 + best-effort 持久化(流式改写保留其它行)。 - TF 覆盖表 MeowData/<pkg>/strings/<locale>.txt(id=text 行,UTF-8,'=' 两侧去空白); 无卡/无文件/无该 id → 回落内嵌。meow_strf 用调用方缓冲(snprintf 语义,重入安全)。 - SDK 控件接入:dialog 默认按钮 OK/CANCEL + INPUT 占位、IME 键面/徽标(空格/确定/拼英/ ?123/清/拼音/(无候选)/中)改走 meow_str;空态/加载态给 App 提供 common.no_data/loading。 - 测试:新增 test_meow_ui_str.c(内嵌回退/TF 覆盖优先级/未知 id 回退/切 locale 即时生效/ 查找正确性/meow_strf,tmp+env 两模式);全量宿主回归(widgets/dispatch/box/anim/scroll/ listvirt/theme/ime_fix/pinyin/overlay/notif/sdk_input_keys/p1p2p3/config/vol/sb_collapse/ notify_*) 0 回归;交叉 zig-mipsel -Wall -Wextra 0 warning。 - 文档:MEOW_APP_SDK.md 新增 §13(字符串资源/i18n,遗留顺延为 §14);docs/APP_DEV_GUIDE.md 新增 §7.11。各宿主 test_*.c 头注释补 meow_ui_str.c 编译依赖。 边界(§13.7):仅 zh_cn/en_us 整串取用;不做复数规则 / BiDi / RTL / 动态字体回退(雏形)。
232 lines
10 KiB
C
232 lines
10 KiB
C
/* 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_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_ui_box
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include "meow_ui_internal.h" /* meow_widget_rect:真实几何断言 */
|
||
#include "meow_ui_view.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
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;
|
||
}
|