Files
MeowOS-For-R3P2/patch_v2/sdk/test_meow_ui_anim.c
T
MeowOS 5a5e995ebd 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,由主会话统一处理)。
2026-09-01 23:49:24 +08:00

134 lines
6.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* test_meow_ui_anim.c — §12.2 meow_anim 动画基座宿主验证:
* 缓动数值(tween 中点/终点)、生命周期(start/update/done/cancel)、并行多动画、脏区 flush。
* 时间推进用真实 usleep(meow_ui_time_ms 是单调钟),对中点用宽容差、终点/取消做精确断言。
*
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/draw/activity/ime + 打桩 font/global):
* gcc -o test_meow_ui_anim test_meow_ui_anim.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_anim
*/
#include "meow_ui.h"
#include "meow_ui_internal.h" /* meow_ui_back_buf 等(脏区 flush 值域校验) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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)
#define NEAR(a, b) (((a)-(b)) < 0.0001f && ((b)-(a)) < 0.0001f)
/* ---- 缓动数值 ---- */
static void test_ease(void)
{
CHECK(NEAR(meow_ease_apply(MEOW_EASE_LINEAR, 0.0f), 0.0f), "lin 0");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_LINEAR, 0.5f), 0.5f), "lin .5");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_LINEAR, 1.0f), 1.0f), "lin 1");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_OUT, 0.0f), 0.0f), "out 0");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_OUT, 1.0f), 1.0f), "out 1");
CHECK(meow_ease_apply(MEOW_EASE_OUT, 0.5f) > 0.5f && meow_ease_apply(MEOW_EASE_OUT, 0.5f) < 1.0f, "out .5 between");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_IN_OUT, 0.0f), 0.0f), "inout 0");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_IN_OUT, 0.5f), 0.5f), "inout .5");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_IN_OUT, 1.0f), 1.0f), "inout 1");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_OVERSHOOT, 0.0f), 0.0f), "overshoot 0");
CHECK(NEAR(meow_ease_apply(MEOW_EASE_OVERSHOOT, 1.0f), 1.0f), "overshoot 1");
CHECK(meow_ease_apply(MEOW_EASE_OVERSHOOT, 0.85f) > 1.0f, "overshoot mid >1");
}
/* ---- update/done 记录 ---- */
typedef struct { meow_anim *a; float last; int updates; int done; int canceled; } anim_track;
static void on_update(meow_anim *a, float v, void *ctx)
{ anim_track *t = ctx; t->last = v; t->updates++; }
static void on_done(meow_anim *a, int canceled, void *ctx)
{ anim_track *t = ctx; t->a = a; t->done++; t->canceled = canceled; }
static void test_tween_complete(void)
{
anim_track t; memset(&t, 0, sizeof t);
meow_anim_desc d; memset(&d, 0, sizeof d);
d.from = 0; d.to = 100; d.duration_ms = 200; d.ease = MEOW_EASE_LINEAR;
d.update = on_update; d.done = on_done; d.ctx = &t;
meow_anim *a = meow_anim_start(&d);
CHECK(a != NULL, "start");
CHECK(meow_anim_active(a) == 1, "active at start");
CHECK(NEAR(meow_anim_value(a), 0.0f), "value from");
CHECK(t.updates == 1 && NEAR(t.last, 0.0f), "update t0");
meow_anim_tick(); /* 与 start 同时刻:仍 ~0 */
CHECK(t.done == 0, "not done yet");
usleep(100000); meow_anim_tick(); /* 中点 */
CHECK(t.last > 10.0f && t.last < 90.0f, "mid value %f", t.last);
usleep(150000); meow_anim_tick(); /* 超过 duration */
CHECK(t.done == 1 && t.canceled == 0, "done once non-cancel");
CHECK(NEAR(t.last, 100.0f), "final value 100 got %f", t.last);
CHECK(meow_anim_active(a) == 0, "inactive after done");
usleep(10000); meow_anim_tick();
CHECK(t.done == 1, "no re-done");
}
static void test_cancel(void)
{
anim_track t; memset(&t, 0, sizeof t);
meow_anim_desc d; memset(&d, 0, sizeof d);
d.from = 0; d.to = 100; d.duration_ms = 500; d.ease = MEOW_EASE_LINEAR;
d.update = on_update; d.done = on_done; d.ctx = &t;
meow_anim *a = meow_anim_start(&d);
usleep(50000); meow_anim_tick();
int updates_before = t.updates;
meow_anim_cancel(a);
CHECK(t.done == 1 && t.canceled == 1, "cancel done(canceled=1)");
CHECK(meow_anim_active(a) == 0, "inactive after cancel");
meow_anim_tick();
CHECK(t.updates == updates_before, "no update after cancel");
}
static void test_parallel(void)
{
anim_track t1, t2; memset(&t1, 0, sizeof t1); memset(&t2, 0, sizeof t2);
meow_anim_desc a, b; memset(&a, 0, sizeof a); memset(&b, 0, sizeof b);
a.from = 0; a.to = 10; a.duration_ms = 120; a.ease = MEOW_EASE_LINEAR; a.update = on_update; a.done = on_done; a.ctx = &t1;
b.from = 100; b.to = 0; b.duration_ms = 120; b.ease = MEOW_EASE_LINEAR; b.update = on_update; b.done = on_done; b.ctx = &t2;
meow_anim *x = meow_anim_start(&a);
meow_anim *y = meow_anim_start(&b);
CHECK(x && y, "parallel start");
CHECK(meow_anim_count() == 2, "count 2");
usleep(150000); meow_anim_tick();
CHECK(t1.done == 1 && t2.done == 1, "both done");
CHECK(NEAR(t1.last, 10.0f) && NEAR(t2.last, 0.0f), "endpoints");
CHECK(meow_anim_count() == 0, "count 0");
}
static void test_region_flush(void)
{
anim_track t; memset(&t, 0, sizeof t);
meow_anim_desc d; memset(&d, 0, sizeof d);
d.from = 0; d.to = 1; d.duration_ms = 80; d.ease = MEOW_EASE_LINEAR;
d.update = on_update; d.done = on_done; d.ctx = &t;
meow_anim *a = meow_anim_start(&d);
meow_anim_set_region(a, 100, 200, 60, 40); /* 每 tick 只 flush 此区 */
usleep(100000); meow_anim_tick();
CHECK(t.done == 1, "region anim done");
CHECK(t.updates >= 1, "region updates");
}
int main(void)
{
meow_ui_opts o = { 480, 720 };
if (meow_ui_init(&o) != 0) { printf("init failed\n"); return 1; }
test_ease();
test_tween_complete();
test_cancel();
test_parallel();
test_region_flush();
meow_ui_shutdown();
printf("anim: %d pass / %d fail\n", g_pass, g_fail);
return g_fail ? 1 : 0;
}