- test_meow_ui_select: T1 选中高亮探针移到容器卡内(x=12), 适配 M6.37 起的 listview 默认容器模式;6/6 PASS - 各 test_*.c 头注释补 -Iinclude、meow_ui_nav.c 及精简源列表最小补集 (theme/widgets/notif_gesture/overlay/suppress/sdk_fixes/pinyin/ime_fix) - test_sb_statusbar 头注释给出可链接命令(--allow-multiple-definition) - notify_post/ui_audio_host/p1p2p3 标注需主仓组件,公开仓跳过 - docs/APP_DEV_GUIDE.md: 去掉叠字与过强表述,改为 34/38 实测口径
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 meow_ui_nav.c \
|
||
* -I. -Iinclude -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;
|
||
}
|