SDK 冲刺批①+②+⑤: meow_box 线性布局 + meow_anim 动画基座 + meow_scroll 滚动物理
- meow_box(include/meow_ui_box.h + meow_ui_box.c):竖直/水平容器;子项=固定|weight|wrap; margin/padding/gap;主轴对齐 start/center/end/space-between/around/evenly;交叉轴 start/center/end/stretch; 嵌套(深度≤16 静态栈,单遍求解,无递归爆炸)。可作 meow_view 布局提供者 (meow_view_set_layout/relayout,opt-in;未设布局的 view 保持现有绝对坐标行为)。 新增 meow_widget_set_rect(只写字段不标脏)作为 box 写坐标的通道。 - meow_anim(include/meow_ui_anim.h + meow_ui_anim.c):时间驱动 tween(from/to/duration/easing: linear/ease-out/ease-in-out/overshoot)+ update/done 回调;静态池≤16、取消、并行多动画; meow_anim_set_region 让每帧只 flush 动画影响区(不整屏)。meow_anim_tick 挂主循环 on_tick, 无动画零开销(只查计数即返回)。 - meow_scroll(include/meow_ui_scroll.h + meow_ui_scroll.c):速度跟踪(最近 N 采样加权)+ 松手 fling (减速度曲线,用 anim 基座驱动)+ 边界过冲 clamp + 简单回弹(不做 spring)+ 可选 snap; 跟手拖动行为不变(track 内部调既有 set_scroll);每帧只 redraw 列表视口区;不引 libm。 - 追加式:既有 meow_row/column/box 与全部 meow_*_create/hit/scroll 签名一行未改;旧绝对坐标永远有效; 既有 App 免改源码。 - 验证:宿主新测试 box(42)/anim(32)/scroll(17) 全 PASS;既存 9 回归(widgets/sdk_input_keys/ime_fix/ pinyin/overlay_suppress/notif_gesture/sdk_fixes/sb_collapse/p1p2p3_repro)全 PASS; 离屏 demo(demo_box_scroll.c)渲染 box 布局 + 列表 fling 正常;zig-mipsel -Wall -Wextra 全 TU 0 warning; meow_ui.a md5 83dd6a2d0d24cc748a8659c29f1de2b5(未重链/部署 hello/files/ncm/reader,由主会话统一处理)。
This commit is contained in:
1 parent
553a7d5d9e
commit
5a5e995ebd
19 files changed
+1839
-2
No files matched your search
@@ -0,0 +1,231 @@
|
||||
/* 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 <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;
|
||||
}
|
||||
Reference in new issue
Block a user