subtree pull sdk 来自主仓 pub/sdk(主仓 commit 68ce46a 等)。新增: - 布局引擎 meow_ui_box / 动画基座 meow_ui_anim / 滚动物理 meow_ui_scroll - §6.4 事件分发形式化 / W_LISTVIEW 虚拟化多行 / meow_str 字符串资源 i18n - sb_icons 动作图标扩充 + topbar 位图图标 / meow_img_downscale - tools/ 图标生成脚本与 SVG 源 - 对应宿主 test_*.c 与 demo_box_scroll.c build_sdk.sh 冲突解析:保留公开仓自包含版(vendored meow_font + -Iinclude -I.), OBJS 手工并入 4 个新模块(meow_ui_box/anim/scroll/str)。保留 vendored meow_font.c/ meow_ipc.h/meow_font.h/meow_notif.h,剔除宿主测试二进制与编译产物。
185 lines
6.6 KiB
C
185 lines
6.6 KiB
C
/* test_meow_ui_scroll.c — §12.3 meow_scroll 滚动物理宿主验证:
|
||
* 跟手拖动(track 跟随与 clamp)、速度跟踪→松手 fling(位移序列/停止位置/单调/不越界)、
|
||
* 边界过冲 clamp + 简单回弹、snap。时间推进用真实 usleep(meow_anim_tick 驱动 fling)。
|
||
*
|
||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/draw/activity/ime + 打桩 font/global):
|
||
* gcc -o test_meow_ui_scroll test_meow_ui_scroll.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_scroll
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include "meow_ui_internal.h"
|
||
#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 LIST_H 400
|
||
#define ITEM_H 44
|
||
#define COUNT 50
|
||
/* max_scroll = COUNT*ITEM_H - LIST_H = 2200 - 400 = 1800 */
|
||
#define MAXS (COUNT * ITEM_H - LIST_H)
|
||
|
||
static int src(int idx, char *label, int len, void *ctx)
|
||
{
|
||
(void)ctx;
|
||
if (idx < 0 || idx >= COUNT) return 0;
|
||
snprintf(label, (size_t)len, "row %d", idx);
|
||
return 1;
|
||
}
|
||
|
||
static meow_widget *make_list(void)
|
||
{
|
||
meow_widget *lv = meow_list_create(0, 0, 480, LIST_H, COUNT, src, NULL);
|
||
meow_list_set_item_h(lv, ITEM_H);
|
||
return lv;
|
||
}
|
||
|
||
/* 喂一串向下(正)速度采样,模拟快速下划 */
|
||
static void feed_drag(meow_scroll *s, int step, int n)
|
||
{
|
||
uint64_t now = meow_ui_time_ms();
|
||
for (int i = 0; i < n; i++) {
|
||
meow_scroll_track(s, step, now + (uint64_t)i * 16);
|
||
}
|
||
/* 最后用一个真实稍晚时刻的 track 让采样时间戳真实(保持一致钟) */
|
||
meow_scroll_track(s, 0, meow_ui_time_ms());
|
||
}
|
||
|
||
static void pump_until_idle(meow_scroll *s, int *max_off, int *mono_ok)
|
||
{
|
||
int prev = meow_scroll_offset(s);
|
||
int guard = 0;
|
||
while (meow_scroll_active(s) && guard++ < 300) {
|
||
meow_anim_tick();
|
||
int cur = meow_scroll_offset(s);
|
||
if (*max_off < cur) *max_off = cur;
|
||
if (cur < prev) *mono_ok = 0; /* 下划 fling 应单调不减(回弹段除外:此处只测无回弹) */
|
||
prev = cur;
|
||
usleep(10000);
|
||
}
|
||
meow_anim_tick();
|
||
}
|
||
|
||
static void test_track_follow(void)
|
||
{
|
||
meow_widget *lv = make_list();
|
||
meow_scroll *s = meow_scroll_wrap(lv, 0);
|
||
CHECK(s != NULL, "wrap");
|
||
CHECK(meow_scroll_offset(s) == 0, "init offset 0");
|
||
meow_scroll_track(s, 10, meow_ui_time_ms());
|
||
CHECK(meow_scroll_offset(s) == 10, "follow +10 got %d", meow_scroll_offset(s));
|
||
/* 超量拖到底:clamp 到 max */
|
||
meow_scroll_track(s, 100000, meow_ui_time_ms());
|
||
CHECK(meow_scroll_offset(s) == MAXS, "clamp to max got %d", meow_scroll_offset(s));
|
||
meow_scroll_free(s);
|
||
meow_widget_free(lv);
|
||
}
|
||
|
||
static void test_fling_no_boundary(void)
|
||
{
|
||
meow_widget *lv = make_list();
|
||
meow_scroll *s = meow_scroll_wrap(lv, 0);
|
||
/* 从 0 起拖 ~300px,建正初速 */
|
||
uint64_t t = meow_ui_time_ms();
|
||
for (int i = 0; i < 12; i++) meow_scroll_track(s, 25, t + (uint64_t)i * 16);
|
||
int start = meow_scroll_offset(s);
|
||
CHECK(start > 0, "dragged start>0 got %d", start);
|
||
meow_scroll_fling(s);
|
||
CHECK(meow_scroll_active(s) == 1, "fling active");
|
||
int max_off = start, mono = 1, prev = start;
|
||
int guard = 0;
|
||
while (meow_scroll_active(s) && guard++ < 300) {
|
||
meow_anim_tick();
|
||
int cur = meow_scroll_offset(s);
|
||
if (max_off < cur) max_off = cur;
|
||
if (cur < prev) mono = 0;
|
||
prev = cur;
|
||
usleep(10000);
|
||
}
|
||
meow_anim_tick();
|
||
int end = meow_scroll_offset(s);
|
||
CHECK(mono == 1, "monotonic non-decreasing during fling");
|
||
CHECK(end >= start, "fling advances end=%d start=%d", end, start);
|
||
CHECK(end <= MAXS, "no overflow end=%d max=%d", end, MAXS);
|
||
CHECK(meow_scroll_active(s) == 0, "settles idle");
|
||
meow_scroll_free(s);
|
||
meow_widget_free(lv);
|
||
}
|
||
|
||
static void test_fling_boundary_bounce(void)
|
||
{
|
||
meow_widget *lv = make_list();
|
||
meow_scroll *s = meow_scroll_wrap(lv, 0);
|
||
/* 先拖到近底(~1700) */
|
||
meow_list_set_scroll(lv, 1700);
|
||
/* 快速下划,冲过底界 */
|
||
uint64_t t = meow_ui_time_ms();
|
||
for (int i = 0; i < 8; i++) meow_scroll_track(s, 40, t + (uint64_t)i * 16);
|
||
meow_scroll_fling(s);
|
||
int max_off = 0, mono = 1;
|
||
pump_until_idle(s, &max_off, &mono);
|
||
int end = meow_scroll_offset(s);
|
||
CHECK(max_off <= MAXS, "never exceed max, max_off=%d max=%d", max_off, MAXS);
|
||
CHECK(end == MAXS, "bounce settles at bottom end=%d", end); /* 回弹后回到底 */
|
||
CHECK(meow_scroll_active(s) == 0, "idle after bounce");
|
||
meow_scroll_free(s);
|
||
meow_widget_free(lv);
|
||
}
|
||
|
||
static void test_snap(void)
|
||
{
|
||
meow_widget *lv = make_list();
|
||
meow_scroll *s = meow_scroll_wrap(lv, 0);
|
||
meow_scroll_set_snap(s, ITEM_H); /* 吸附到行边界 */
|
||
/* 慢拖到某非边界位置并给 small velocity → fling 结束 snap 到行倍数 */
|
||
uint64_t t = meow_ui_time_ms();
|
||
for (int i = 0; i < 6; i++) meow_scroll_track(s, 20, t + (uint64_t)i * 16);
|
||
meow_scroll_fling(s);
|
||
int max_off = 0, mono = 1;
|
||
pump_until_idle(s, &max_off, &mono);
|
||
int end = meow_scroll_offset(s);
|
||
CHECK((end % ITEM_H) == 0, "snap to item multiple got %d", end);
|
||
CHECK(end <= MAXS, "snap in range %d", end);
|
||
meow_scroll_free(s);
|
||
meow_widget_free(lv);
|
||
}
|
||
|
||
static void test_stop(void)
|
||
{
|
||
meow_widget *lv = make_list();
|
||
meow_scroll *s = meow_scroll_wrap(lv, 0);
|
||
uint64_t t = meow_ui_time_ms();
|
||
for (int i = 0; i < 6; i++) meow_scroll_track(s, 20, t + (uint64_t)i * 16);
|
||
meow_scroll_fling(s);
|
||
CHECK(meow_scroll_active(s) == 1, "fling active (stop test)");
|
||
meow_scroll_stop(s);
|
||
CHECK(meow_scroll_active(s) == 0, "stopped immediately");
|
||
meow_scroll_free(s);
|
||
meow_widget_free(lv);
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
meow_ui_opts o = { 480, 720 };
|
||
if (meow_ui_init(&o) != 0) { printf("init failed\n"); return 1; }
|
||
test_track_follow();
|
||
test_fling_no_boundary();
|
||
test_fling_boundary_bounce();
|
||
test_snap();
|
||
test_stop();
|
||
meow_ui_shutdown();
|
||
printf("scroll: %d pass / %d fail\n", g_pass, g_fail);
|
||
return g_fail ? 1 : 0;
|
||
}
|