Files
AskaEth a2aec783a7 feat(ui): M6.38批 — P1 MD3细化(SDK对话框28)+批②底部上滑回桌面+批6 ncm顶栏回播放页+批④面板后台列表+launcher UI微调(歌词页黑半透明底/播放页布局下移)+封面空artwork保留修复
- P1: meow_ui_dialog.c 对话框底框 radius_card→MEOW_SHAPE_EXTRA_LARGE(28) 三处(:103填充/:104描边/:361 BottomSheet); meow_ui_widgets.h 新增 MEOW_SHAPE_EXTRA_LARGE 28 宏; TextField/FAB/Slider/Progress 按设计文档保留
- 批②: np_gesture.h 新增 NP_GEST_HOME+np_home_gesture(底部热区上滑,与左缘返回对称互斥); now_playing.c 新增 np_draw_home_bar(屏底左缘pill指示条)+手势衔接; test_now_playing_widgets G5b
- 批6: ncm home.c 顶栏加回播放页按钮(MEOW_ICON_QUEUE_MUSIC)→push(ACT_PLAYER), 无音频也生效
- 批④: meowpanel /api/ams/bg 只读端点+前端后台列表页+杀/清复用kill通道
- UI微调: now_playing.c 播放页布局宏下移(NP_COVER_Y 84/NP_BTN_Y 612消底部留白)+歌词页背景统一黑色半透明叠层(弃封面取色底,删 np_draw_lyric_coverbg)
- 封面保留: now_playing.c/notif_center.c 空artwork保留上一张已解码封面(防暂停/开歌单致META报空artwork后封面闪没); test_np_cover C2断言更新+mirror同步

0warning;宿主测试 test_np_cover ALL PASS;launcher/ncm/panel 宿主测试无回归。二进制留批量批次未入库
2026-09-04 13:09:03 +08:00

397 lines
21 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_now_playing_widgets.c — M7.0b Now Playing 控件化宿主验证。
*
* 复现 run_now_playing() 所建控件集(np_widgets_create:meow_progress 进度条 + meow_iconbtn
* 三钮,play/pause 同位双钮按状态只画其一),验证:
* N1 进度条:value clamp 0-100 + track/fill 渲染(填充分界像素不同);
* N2 三钮圆形命中:圆心命中、角部不命中;
* N3 play/pause 同位切换:两钮图标位图不同 → 中心像素不同(画 PAUSE vs PLAY_ARROW);
* N4 主题四态(MD3/MIUIx × 明暗)下进度条+三钮渲染非背景像素。
*
* 几何常量与 now_playing.c 同源(镜像);控件创建方式与 np_widgets_create 同。
* 编译(在 patch_v2/meow_launcherd 下):
* gcc -Wall -Wextra -o test_now_playing_widgets test_now_playing_widgets.c \
* ../sdk/test_host_stub.c ../sdk/meow_ui_gfx.c ../sdk/meow_ui_sys.c \
* ../sdk/meow_ui_theme.c ../sdk/meow_ui_vol.c ../sdk/meow_ui_log.c \
* ../sdk/meow_ui_widget.c ../sdk/meow_ui_input.c ../sdk/meow_ui_activity.c \
* ../sdk/meow_ui_dialog.c ../sdk/meow_ui_ime.c ../sdk/meow_ui_str.c ../sdk/meow_pinyin.c \
* ../sdk/meow_ui_draw.c ../sdk/meow_ui_view.c ../sdk/meow_ui_box.c ../sdk/meow_ui_anim.c \
* ../sdk/meow_ui_scroll.c ../sdk/meow_ui_img.c ../sdk/meow_ui_media.c \
* ../sdk/meow_notify_ipc.c ../sdk/stb_image.c \
* ../sdk/meow_lyric.c ../sdk/meow_lyric_view.c \
* -I../sdk -I. -DMEOW_UI_HOST_TEST -pthread -lm
* 运行:./test_now_playing_widgets
*/
#include "meow_ui.h"
#include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp */
#include "np_gesture.h" /* 批4:双页方向感知手势决策(纯逻辑,宿主可单测) */
#include "meow_lyric_view.h" /* 批2:歌词页控件(批4接入,数据批3 meow_media_lyric_*) */
#include "meow_img_color.h" /* 批5b:封面取色主题派生(局部重绘进度条/按钮带取色用例) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
static int g_fail = 0;
#define CHECK(cond, name) do { \
if (cond) printf(" PASS %s\n", name); \
else { printf(" FAIL %s\n", name); g_fail++; } } while (0)
/* ---- 与 now_playing.c 同源几何(镜像) ---- */
#define NP_PROG_X 40
#define NP_PROG_W (480 - 80) /* 400 */
#define NP_PROG_Y 518
#define NP_TIME_Y 492
#define NP_BTN_Y 568
#define NP_PLAY_CX 240
#define NP_PREV_CX (NP_PLAY_CX - 78) /* 162 */
#define NP_NEXT_CX (NP_PLAY_CX + 78) /* 318 */
#define NP_PLAY_R 30
#define NP_PREV_R 22
#define NP_SENTINEL 0x51
static meow_widget *prog = NULL, *prev = NULL, *play = NULL, *pause = NULL, *next = NULL;
static void rd_px(int x, int y, unsigned short *p)
{
unsigned char *bb = meow_ui_back_buf();
int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp();
if (!bb || x < 0 || y < 0) { *p = 0; return; }
if (bpp == 16) *p = *(unsigned short *)(bb + (size_t)y * line + (size_t)x * 2);
else *p = (unsigned short)(*(unsigned int *)(bb + (size_t)y * line + (size_t)x * 4) >> 16);
}
static unsigned short px565(int x, int y) { unsigned short p; rd_px(x, y, &p); return p; }
static unsigned short rgb565(uint32_t c)
{ return (unsigned short)(((c >> 8) & 0xF800) | ((c >> 5) & 0x07E0) | ((c >> 3) & 0x001F)); }
static int region_nonbg(int x, int y, int w, int h, uint32_t bg)
{
unsigned short bg565 = ((bg >> 8) & 0xF800) | ((bg >> 5) & 0x07E0) | ((bg >> 3) & 0x001F);
int cnt = 0;
for (int dy = 0; dy < h; dy++)
for (int dx = 0; dx < w; dx++)
if (px565(x + dx, y + dy) != bg565) cnt++;
return cnt;
}
static void mk_widgets(void)
{
meow_ui_activity_push(NP_SENTINEL);
prog = meow_progress_create(NP_PROG_X, NP_PROG_Y, NP_PROG_W, 6, 0);
prev = meow_iconbtn_create(NP_PREV_CX - NP_PREV_R, NP_BTN_Y - NP_PREV_R,
NP_PREV_R * 2, MEOW_ICON_SKIP_PREV);
play = meow_iconbtn_create(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R,
NP_PLAY_R * 2, MEOW_ICON_PLAY_ARROW);
pause = meow_iconbtn_create(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R,
NP_PLAY_R * 2, MEOW_ICON_PAUSE);
next = meow_iconbtn_create(NP_NEXT_CX - NP_PREV_R, NP_BTN_Y - NP_PREV_R,
NP_PREV_R * 2, MEOW_ICON_SKIP_NEXT);
/* 主播放钮强调:primary/onPrimary(镜像 np_widgets_create) */
const meow_theme *th = meow_theme_get();
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = th->primary; op.fg = th->onPrimary;
meow_widget_set_opts(play, &op);
meow_widget_set_opts(pause, &op);
meow_ui_activity_pop();
}
/* 歌词视图宽度测量(宿主:字节数近似,够用不裂屏;launcher 侧为 ui_width)。 */
static int lv_test_width(const char *u, int size, void *ctx)
{
(void)size; (void)ctx;
return (int)strlen(u);
}
int main(void)
{
setbuf(stdout, NULL);
setenv("MEOW_UI_DUMP", "/tmp/nowplaying.ppm", 1);
meow_ui_opts o = { 480, 720 };
assert(meow_ui_init(&o) == 0);
meow_ui_activity_push(NP_SENTINEL);
mk_widgets();
const meow_theme *th = meow_theme_get();
meow_ui_set_style(MEOW_STYLE_MD3);
meow_theme_set_mode(MEOW_THEME_DARK);
th = meow_theme_get();
/* ============ N1 进度条 clamp + track/fill 渲染 ============ */
printf("N1 progress value + track/fill\n");
meow_widget_set_opts(prog, &(meow_widget_opts){ .bg = 0 });
meow_ui_clear(0);
meow_progress_set(prog, 150); /* clamp → 100 */
meow_widget_draw(prog);
unsigned short fill_end = px565(NP_PROG_X + NP_PROG_W - 2, NP_PROG_Y + 3);
unsigned short track_mid = px565(NP_PROG_X + NP_PROG_W / 2, NP_PROG_Y + 3);
CHECK(fill_end != 0 && track_mid != 0, "value 150 clamp 100 -> full fill renders");
meow_progress_set(prog, 50);
meow_widget_draw(prog);
/* 50% 填充区 [40,240) 内像素应为主色;240 之后的轨道区应为轨道色(surfaceVariant) */
unsigned short infill = px565(NP_PROG_X + NP_PROG_W / 4, NP_PROG_Y + 3); /* x=140,填充区 */
unsigned short empty = px565(NP_PROG_X + NP_PROG_W - 4, NP_PROG_Y + 3); /* x=436,轨道区 */
CHECK(infill != empty, "value 50 -> fill region differs from empty tail");
/* ============ N2 三钮圆形命中 ============ */
printf("N2 circular hit\n");
CHECK(meow_widget_hit(prev, NP_PREV_CX, NP_BTN_Y), "prev center hits");
CHECK(!meow_widget_hit(prev, NP_PREV_CX - NP_PREV_R, NP_BTN_Y - NP_PREV_R), "prev corner MISSES");
CHECK(meow_widget_hit(play, NP_PLAY_CX, NP_BTN_Y), "play center hits");
CHECK(meow_widget_hit(pause, NP_PLAY_CX, NP_BTN_Y), "pause(overlay) center hits");
CHECK(!meow_widget_hit(play, NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R), "play corner MISSES");
CHECK(meow_widget_hit(next, NP_NEXT_CX, NP_BTN_Y), "next center hits");
/* ============ N3 play/pause 同位切换(两钮位图不同 → 像素不同) ============ */
printf("N3 play/pause overlay icon switch\n");
meow_ui_clear(0);
meow_widget_draw(play); meow_ui_clear(0); /* 只画 play */
meow_widget_draw(play);
unsigned short p_play = px565(NP_PLAY_CX, NP_BTN_Y); /* 中心=图标墨色 */
meow_ui_clear(0);
meow_widget_draw(pause);
unsigned short p_pause = px565(NP_PLAY_CX, NP_BTN_Y);
CHECK(p_play != p_pause, "play vs pause icon differs at centre (overlay switch works)");
/* 圆心区域非页面底色(画出来了) */
meow_ui_clear(0); meow_widget_draw(pause);
CHECK(region_nonbg(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R,
NP_PLAY_R * 2, NP_PLAY_R * 2, th->surface) > 0,
"pause button renders non-bg in circle");
/* ============ N4 主题四态:进度条+三钮渲染非背景 ============ */
printf("N4 theme 4-state render\n");
const int styles[2] = { MEOW_STYLE_MD3, MEOW_STYLE_MIUIX };
const int modes[2] = { MEOW_THEME_LIGHT, MEOW_THEME_DARK };
for (int si = 0; si < 2; si++) {
for (int mi = 0; mi < 2; mi++) {
meow_ui_set_style(styles[si]);
meow_theme_set_mode((meow_theme_mode)modes[mi]);
th = meow_theme_get();
meow_theme ex; meow_theme_export(&ex);
CHECK(ex.primary == th->primary && ex.surface == th->surface &&
ex.surfaceVariant == th->surfaceVariant, "export == get (spot roles)");
meow_ui_clear(0);
meow_progress_set(prog, 40); meow_widget_draw(prog);
meow_widget_draw(prev);
meow_widget_draw(pause);
meow_widget_draw(next);
char nm[80];
int progpx = region_nonbg(NP_PROG_X, NP_PROG_Y, NP_PROG_W, 6, th->surface);
int btnpx = region_nonbg(NP_PLAY_CX - NP_PLAY_R, NP_BTN_Y - NP_PLAY_R,
NP_PLAY_R * 2, NP_PLAY_R * 2, th->surface);
snprintf(nm, sizeof nm, "style=%s mode=%s prog+btn render",
styles[si] ? "MIUIx" : "MD3", modes[mi] ? "dark" : "light");
CHECK(progpx > 0 && btnpx > 0, nm);
}
}
/* ============ R1 批5b:局部重绘取色——进度条 fill=accent/track=fg_dim + 默认回退 ============
* 镜像 np_theme_widgets(取色有效→进度条 opts.bg=fg_dim/opts.fg=accent)+ np_draw_progress。
* 用「直接 fill_rect 参考像素」对比,绕开 565 精度差异(同一渲染器同色 → 同 565)。 */
printf("R1 progress cover-theme fill/track + default fallback\n");
meow_ui_set_style(MEOW_STYLE_MD3);
meow_theme_set_mode(MEOW_THEME_DARK);
th = meow_theme_get();
meow_img_color_theme cc; memset(&cc, 0, sizeof cc);
meow_img_color_derive(0xE53935u, 0x1E88E5u, th, &cc); /* 红/蓝两色 → 派生取色主题 */
/* 覆盖取色:进度条 fill=accent / track=fg_dim */
{
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = cc.fg_dim; op.fg = cc.accent;
meow_widget_set_opts(prog, &op);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PROG_X, NP_PROG_Y, 5, 6, cc.accent); /* fill 参考 */
unsigned short ref_fill = px565(NP_PROG_X + 2, NP_PROG_Y + 3);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PROG_X + NP_PROG_W - 4, NP_PROG_Y, 3, 6, cc.fg_dim); /* track 参考 */
unsigned short ref_track = px565(NP_PROG_X + NP_PROG_W - 3, NP_PROG_Y + 3);
meow_ui_clear(0);
meow_progress_set(prog, 50);
meow_widget_draw(prog);
int fill_x = NP_PROG_X + NP_PROG_W / 4; /* 50% 填充区内 */
int track_x = NP_PROG_X + NP_PROG_W - 4; /* 尾部轨道区 */
CHECK(px565(fill_x, NP_PROG_Y + 3) == ref_fill, "cover: progress fill == accent");
CHECK(px565(track_x, NP_PROG_Y + 3) == ref_track, "cover: progress track == fg_dim");
/* 默认回退:清 opts → 回到主题 primary/surfaceVariant(无取色回退默认) */
memset(&op, 0, sizeof op);
meow_widget_set_opts(prog, &op);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PROG_X, NP_PROG_Y, 5, 6, th->primary);
unsigned short ref_pri = px565(NP_PROG_X + 2, NP_PROG_Y + 3);
meow_ui_clear(0);
meow_progress_set(prog, 50);
meow_widget_draw(prog);
CHECK(px565(fill_x, NP_PROG_Y + 3) == ref_pri, "default: progress fill == theme primary");
CHECK(px565(fill_x, NP_PROG_Y + 3) != ref_fill, "default vs cover fill differ");
}
/* ============ R2 批5b:进度/按钮带底色随取色渐变(非默认 surface) ============
* 镜像 np_repaint_progress 的带底色分支(np_page_bg_row 按整页 span 插值)。
* 旧 bug:本地重绘用 th->surface 清带 → 带内被刷成默认色;现应取色渐变且 != surface。 */
printf("R2 band background uses cover gradient (not default surface)\n");
{
#define NP_SB_H 28
int H = 720;
meow_ui_clear(0);
for (int yy = NP_TIME_Y; yy < NP_BTN_Y + NP_PLAY_R; yy++) {
int t = (H - NP_SB_H <= 1) ? 0 : (yy - NP_SB_H) * 255 / (H - NP_SB_H - 1);
if (t < 0) t = 0; else if (t > 255) t = 255;
int tr = (cc.bg_top >> 16) & 255, tg = (cc.bg_top >> 8) & 255, tb = cc.bg_top & 255;
int br = (cc.bg_bottom >> 16) & 255, bg = (cc.bg_bottom >> 8) & 255, bb = cc.bg_bottom & 255;
int r = tr + ((br - tr) * t + 128) / 255;
int g = tg + ((bg - tg) * t + 128) / 255;
int b = tb + ((bb - tb) * t + 128) / 255;
meow_ui_fill_rect(0, yy, 480, 1, ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b);
}
unsigned short bandpx = px565(10, NP_TIME_Y + 55);
CHECK(bandpx != rgb565(th->surface), "cover: band bg != default surface");
CHECK(bandpx != 0, "cover: band bg rendered");
}
/* ============ R3 批5b:控制三钮(含 prev/next)随取色 accent + 默认回退 ============
* 镜像 np_theme_widgets:取色有效→四钮 opts.bg=accent/opts.fg=accent_fg(prev/next 不再默认底色)。 */
printf("R3 control buttons follow cover accent\n");
{
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = cc.accent; op.fg = cc.accent_fg;
meow_widget_set_opts(prev, &op);
meow_widget_set_opts(next, &op);
meow_widget_set_opts(play, &op);
meow_widget_set_opts(pause, &op);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PREV_CX - 2, NP_BTN_Y - 18, 4, 2, cc.accent); /* accent 参考(圆底上方、图标外) */
unsigned short ref_acct = px565(NP_PREV_CX, NP_BTN_Y - 17);
meow_ui_clear(0);
meow_widget_draw(prev); /* r=22,cy-17 在圆内且高于图标(cy-13) */
CHECK(px565(NP_PREV_CX, NP_BTN_Y - 17) == ref_acct, "cover: prev bg == accent");
meow_ui_clear(0);
meow_widget_draw(play); /* r=30,cy-26 在圆内且高于图标(cy-18) */
CHECK(px565(NP_PLAY_CX, NP_BTN_Y - 26) == ref_acct, "cover: play bg == accent");
/* 默认回退:清 prev/next opts → 回 surfaceContainerLow(无取色不回归旧形态) */
memset(&op, 0, sizeof op);
meow_widget_set_opts(prev, &op);
meow_widget_set_opts(next, &op);
meow_ui_clear(0);
meow_ui_fill_rect(NP_PREV_CX - 2, NP_BTN_Y - 18, 4, 2, th->surfaceContainerLow);
unsigned short ref_def = px565(NP_PREV_CX, NP_BTN_Y - 17);
meow_ui_clear(0);
meow_widget_draw(prev);
CHECK(px565(NP_PREV_CX, NP_BTN_Y - 17) == ref_def, "default: prev bg == surfaceContainerLow");
}
/* ============ G5 双页手势方向感知(np_gesture.h,纯逻辑) ============ */
printf("G5 dual-page direction-aware gestures\n");
/* 播放页:右→左滑 → 进歌词页 */
CHECK(np_gesture_decide(NP_PAGE_PLAY, 200, 100, -50, 5) == NP_GEST_NEXT,
"play: left-swipe -> lyric");
/* 播放页:左缘右滑 → 回桌面(保留 M6.26) */
CHECK(np_gesture_decide(NP_PAGE_PLAY, 8, 100, 80, 5) == NP_GEST_EXIT,
"play: edge right-swipe -> exit");
/* 播放页:任意右滑 → 返回桌面(用户定案「播放页任意右滑回桌面」,含非左缘;np_gesture.h 恒 EXIT) */
CHECK(np_gesture_decide(NP_PAGE_PLAY, 200, 100, 80, 5) == NP_GEST_EXIT,
"play: non-edge right-swipe -> EXIT");
/* 歌词页:左→右滑 → 回播放页(不是回桌面) */
CHECK(np_gesture_decide(NP_PAGE_LYRIC, 200, 100, 60, 5) == NP_GEST_PREV,
"lyric: right-swipe -> play");
/* 歌词页:左缘右滑 → 回播放页(不接桌面,避免误退) */
CHECK(np_gesture_decide(NP_PAGE_LYRIC, 8, 100, 80, 5) == NP_GEST_PREV,
"lyric: edge right-swipe -> play (not exit)");
/* 歌词页:右→左滑 → 歌词页是末页,忽略 */
CHECK(np_gesture_decide(NP_PAGE_LYRIC, 200, 100, -60, 5) == NP_GEST_NONE,
"lyric: left-swipe -> none");
/* 顶部热区下滑 → 通知中心(两个页面同语义) */
CHECK(np_gesture_decide(NP_PAGE_PLAY, 100, 10, 0, 70) == NP_GEST_TOP,
"top swipe-down -> notif");
/* 近点按 → TAP;纵向主导 → NONE(不误切) */
CHECK(np_gesture_decide(NP_PAGE_PLAY, 200, 300, 5, 5) == NP_GEST_TAP,
"near tap -> TAP");
CHECK(np_gesture_decide(NP_PAGE_PLAY, 200, 300, 5, 80) == NP_GEST_NONE,
"vertical dominant -> NONE");
/* ============ G5b 批② 底部上滑(home)手势(np_gesture.h np_home_gesture,纯逻辑) ============ */
printf("G5b bottom-swipe-up home gesture\n");
const int FBRES = 720; /* 屏高(宿主 480x720;真机 fb_yres=720) */
const int BZ = FBRES - NP_HOME_EDGE_H; /* 底部起滑区上界 692 */
/* 底部起滑 + 向上主导 ≥阈值 → HOME */
CHECK(np_home_gesture(200, BZ + 10, 0, -120, FBRES) == 1,
"home: bottom zone + up-swipe -> HOME");
CHECK(np_home_gesture(100, FBRES - 1, 20, -80, FBRES) == 1,
"home: very bottom + slight right-drift up -> HOME");
/* 与左缘返回互斥:左缘起点(down_x<NP_EDGE_W)不认 home(让给左缘返回) */
CHECK(np_home_gesture(10, BZ + 10, 0, -120, FBRES) == 0,
"home: left-edge origin -> NOT home (reserved for edge-back)");
/* 起点不在底部起滑区(顶部/中部)不认 home */
CHECK(np_home_gesture(200, 10, 0, -120, FBRES) == 0,
"home: top origin -> NOT home");
CHECK(np_home_gesture(200, 300, 0, -120, FBRES) == 0,
"home: mid origin -> NOT home");
/* 横向主导(向右滑)不认 home(该判给左缘返回/切页) */
CHECK(np_home_gesture(200, BZ + 10, 120, 0, FBRES) == 0,
"home: right-dominant -> NOT home");
/* 向下滑 / 未达阈值 / 竖向不主导 不认 home */
CHECK(np_home_gesture(200, BZ + 10, 0, 120, FBRES) == 0,
"home: downward -> NOT home");
CHECK(np_home_gesture(200, BZ + 10, 0, -NP_HOME_SWIPE + 1, FBRES) == 0,
"home: below up-threshold -> NOT home");
CHECK(np_home_gesture(200, BZ + 10, -120, -80, FBRES) == 0,
"home: horizontal-dominant up -> NOT home");
/* 与 now_playing.c 集成顺序一致:np_gesture_decide 对底部纵滑返回 NONE,再由 home 覆盖为 HOME */
{
int dx = 0, dy = -120, tdx = 200, tdy = BZ + 10;
np_gest_t gnp = np_gesture_decide(NP_PAGE_PLAY, tdx, tdy, dx, dy);
if (np_home_gesture(tdx, tdy, dx, dy, FBRES)) gnp = NP_GEST_HOME;
CHECK(gnp == NP_GEST_HOME,
"integration: bottom up-swipe -> HOME (overrides decide-NONE)");
}
/* 集成:左缘起点底部上滑不被误判为 home(保留给左缘返回) */
{
int tdx = 8, tdy = BZ + 10, dx = 0, dy = -120; /* 左缘 + 上滑(竖向主导) */
np_gest_t gnp = np_gesture_decide(NP_PAGE_PLAY, tdx, tdy, dx, dy);
if (np_home_gesture(tdx, tdy, dx, dy, FBRES)) gnp = NP_GEST_HOME;
CHECK(gnp != NP_GEST_HOME,
"integration: left-edge origin up-swipe -> NOT home");
}
/* ============ G6 歌词页:空态 + 渲染(批2 控件 + 批3 数据语义) ============ */
printf("G6 lyric view: empty + render\n");
meow_ui_activity_push(NP_SENTINEL);
meow_lyric_view *lv = meow_lyric_view_create(0, 28, 480, 720 - 28, lv_test_width, NULL);
meow_lyric_view_set_empty(lv, "暂无歌词");
meow_ui_activity_pop();
CHECK(lv != NULL, "lyric_view created");
if (lv) {
/* 无歌词空态:无解析 → 0 行、visible=0(视图内建空态文案) */
meow_lyric_clear();
CHECK(meow_lyric_count() == 0, "clear -> 0 lines");
CHECK(meow_lyric_view_visible(lv, 0, NULL, 0) == 0, "empty -> 0 visible entries");
/* 有词:解析两行 → count/line_at/visible/当前行色 */
int np = meow_lyric_parse("[00:01.00]第一行歌词\n[00:05.00]第二行歌词", "");
CHECK(np == 2, "parse -> 2 lines");
CHECK(meow_lyric_count() == 2, "count == 2");
CHECK(meow_lyric_line_at(0) == 0, "line_at(0) -> first");
CHECK(meow_lyric_line_at(3000) == 0, "line_at(3000) -> first");
CHECK(meow_lyric_line_at(6000) == 1, "line_at(6000) -> second");
CHECK(strcmp(meow_lyric_line_text(0), "第一行歌词") == 0, "text(0) == 第一行歌词");
meow_lyric_view_vis vb[9];
int vn = meow_lyric_view_visible(lv, 3000, vb, 9);
CHECK(vn > 0, "visible(3000) > 0");
CHECK(meow_lyric_view_cur_color(lv) == meow_theme_get()->onSurface,
"cur_color == theme onSurface");
/* 批5 取色预留:set_colors 可接入(覆盖活动行为主题 accent 后再还原默认) */
meow_lyric_view_colors c; memset(&c, 0, sizeof c);
c.cur = meow_theme_get()->accent;
meow_lyric_view_set_colors(lv, &c);
CHECK(meow_lyric_view_cur_color(lv) == meow_theme_get()->accent,
"set_colors cur override works");
meow_lyric_view_set_colors(lv, NULL); /* 回主题默认 */
CHECK(meow_lyric_view_cur_color(lv) == meow_theme_get()->onSurface,
"set_colors NULL -> theme default");
meow_lyric_view_free(lv);
}
meow_ui_activity_pop();
meow_ui_shutdown();
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
return g_fail;
}