Files
AskaEth 54857bc0fd sdk(ui): 字符串资源外置(i18n 雏形 §13)—— meow_str_* + 内嵌双语表 + TF 覆盖表 + 控件接入
公开 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 / 动态字体回退(雏形)。
2026-09-02 02:37:38 +08:00

134 lines
6.0 KiB
C
Raw Permalink 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_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_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;
}