- M7 launcher 重写 5 期(虚拟化宫格/meow_theme 单一权威/横幅动画/自定义 PIN 键盘) - OOM 通用化(cachegov 上收 SDK)、自动切歌根治、meowpanel jv_free 泄漏修复、Hello 滚动卡顿修复 - ncm 歌词 UI / 媒体通知封面 / 封面分辨率 640 - 5 bug 修复:ncm 后台化 UI 叠层+点击穿透、再进 ncm 桌面叠层、播放页新旧 UI 叠层抢刷脏区(meow_ui_gfx 恢复边沿整帧重画)、PIN 亮屏亮度跳(keys.c meow_panel_on 按 settings_brightness_clamped 恢复)、通知中心封面显歌单封面(cover.c save_cover_raw 加 is_song + 命中块守卫) - 四 App 统一重链新 meow_ui.a 51caee4a;M6.19=0.8.19 固件 2d2331f2 推卡自刷验收全绿
361 lines
21 KiB
C
361 lines
21 KiB
C
/* test_menu_sdk.c — M7.4 主界面迁 SDK(meow_lazygrid 宫格 + meow_listview 列表 + meow_scroll)宿主验证。
|
||
*
|
||
* 复现 main_menu.c M7.4 SDK 路径所用几何/数据源(与 main_menu.c 同源常量镜像):
|
||
* app 区视口 = menu_app_region(apps_top, layout):宫格 x=12(PAD) w=xres-2*PAD / 列表 x=8 w=xres-16,
|
||
* 顶=apps_top(=ROW_TOP=96,宿主无字库→mfont_ready=0),底=EXIT_ROW-ROW_H-4=584;
|
||
* 宫格 cell_w=(xres-2*PAD-gap)/cols=223,cell_h=CARD_H=112,gap=10,row_h=122;
|
||
* 列表 row_h=LIST_ROW_H=60。应用数 MAX_APPS=8。
|
||
*
|
||
* 验证(M7 §4 验收点:双布局可切 / 图标·缺失·后台标注正确 / 空态 / 滚动惯性):
|
||
* G1 宫格虚拟化渲染:每可见格调自绘一次;格位置 = menu_app_region/menu_grid_cell_w 公式映射
|
||
* (480×720 校准:cell0=12,96 cell1=245,96 cell2=12,218 … 不被截断、不越视口)。
|
||
* G2 宫格命中:格中心→正确 idx;水平/垂直 gap→-1;越界→-1;滚动后命中换算正确。
|
||
* G3 宫格滚动:8 格(MAX_APPS)装得下 → max_scroll==0;12 格超屏 → max_scroll>0;scroll_attach 可建。
|
||
* L1 列表渲染+命中:视口 x=8,w=464;row 矩形与公式一致;8 行装得下 / 12 行超屏。
|
||
* E 空态:count==0 → 不画任何格/行卡(空态路径),无回退。
|
||
* M 图标·缺失·后台标注:自绘按 app->ok/meow_bg_is_backgrounded 决策「[缺失]」「·后台」/均无。
|
||
*
|
||
* 注:main_menu.c 的 menu_draw_grid_cell/menu_draw_list_cell 是 static(依赖 launcher 全局面),
|
||
* 宿主以同源公式/决策规则镜像复现其契约;SDK 控件行为(虚拟化/命中/滚动/空态)为真实验证。
|
||
*
|
||
* 编译(在 patch_v2/meow_launcherd 下,宿主 gcc):
|
||
* gcc -Wall -Wextra -o test_menu_sdk test_menu_sdk.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_lazygrid.c ../sdk/meow_ui_img.c ../sdk/meow_ui_media.c \
|
||
* ../sdk/meow_notify_ipc.c ../sdk/stb_image.c -I../sdk -I. -DMEOW_UI_HOST_TEST -pthread -lm
|
||
* 运行:./test_menu_sdk
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp + meow_gfx_clip */
|
||
#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)
|
||
|
||
/* ---- 与 main_menu.c / meow_launcher_internal.h 同源几何(镜像) ---- */
|
||
#define XRES 480
|
||
#define YRES 720
|
||
#define MENU_GRID_PAD 12
|
||
#define MENU_GRID_GAP 10
|
||
#define MENU_GRID_COLS 2
|
||
#define MENU_GRID_CARD_H 112
|
||
#define MENU_GRID_ICON 44
|
||
#define MENU_LIST_ROW_H 60
|
||
#define MENU_LIST_ICON 40
|
||
#define MENU_LIST_ICON_X 24
|
||
#define ROW_H 52
|
||
#define ROW_TOP 96
|
||
#define EXIT_ROW 640
|
||
#define APPS_TOP ROW_TOP /* 宿主无字库 → mfont_ready=0 → menu_apps_top()=ROW_TOP */
|
||
#define APPS_BOT (EXIT_ROW - ROW_H - 4) /* 584:设置行顶 */
|
||
#define SDK_ACT 0x55
|
||
|
||
/* menu_app_region 镜像:宫格 vx=12 w=xres-24;列表 vx=8 w=xres-16;vh=底-顶。 */
|
||
static int grid_cell_w(void) { return (XRES - 2 * MENU_GRID_PAD - (MENU_GRID_COLS - 1) * MENU_GRID_GAP) / MENU_GRID_COLS; }
|
||
static int grid_row_h(void) { return MENU_GRID_CARD_H + MENU_GRID_GAP; }
|
||
static void grid_region(int *vx, int *vy, int *vw, int *vh) { *vx = MENU_GRID_PAD; *vy = APPS_TOP; *vw = XRES - 2 * MENU_GRID_PAD; *vh = APPS_BOT - APPS_TOP; }
|
||
static void list_region(int *vx, int *vy, int *vw, int *vh) { *vx = 8; *vy = APPS_TOP; *vw = XRES - 16; *vh = APPS_BOT - APPS_TOP; }
|
||
|
||
/* ---- mock 应用(镜像 struct app 的关键字段) ---- */
|
||
typedef struct { const char *name; int ok; int bg; const char *pkg; } tapp_t;
|
||
static tapp_t g_apps[16];
|
||
static int g_napps = 0;
|
||
static int g_draw_cnt = 0; /* 自绘回调调用次数(窗口虚拟化) */
|
||
static int g_cell_x[16], g_cell_y[16], g_cell_w[16], g_cell_h[16]; /* 自绘收到的几何 */
|
||
static int g_cell_idx[16];
|
||
static char g_label[16][16]; /* 格/行自绘决策出的标注("" / "[缺失]" / "·后台") */
|
||
|
||
/* bind:读 mock app,title=名,enabled=1(命中即启动语义,同 main_menu menu_grid_bind)。 */
|
||
static void grid_bind(int idx, meow_lazygrid_item *it, void *ctx)
|
||
{
|
||
(void)ctx;
|
||
memset(it, 0, sizeof *it);
|
||
if (idx < 0 || idx >= g_napps) return;
|
||
it->title = g_apps[idx].name;
|
||
it->subtitle = NULL;
|
||
it->icon = NULL; /* 宿主无图标位图;自绘用文字/色块近似 */
|
||
it->enabled = 1;
|
||
it->user = &g_apps[idx];
|
||
}
|
||
static int grid_count(void *ctx) { (void)ctx; return g_napps; }
|
||
static void list_bind(int idx, meow_lv_item *it, void *ctx)
|
||
{
|
||
(void)ctx;
|
||
memset(it, 0, sizeof *it);
|
||
if (idx < 0 || idx >= g_napps) return;
|
||
it->title = g_apps[idx].name;
|
||
it->subtitle = NULL;
|
||
it->icon_kind = MEOW_LIST_ICON_NONE;
|
||
it->enabled = 1;
|
||
it->user = &g_apps[idx];
|
||
}
|
||
static int list_count(void *ctx) { (void)ctx; return g_napps; }
|
||
|
||
/* 镜像 menu_draw_grid_cell 的标注决策(ok→缺失[缺失];bg→·后台;均无→无)。 */
|
||
static void decide_label(int idx, char *out, int olen)
|
||
{
|
||
const tapp_t *a = &g_apps[idx];
|
||
if (!a->ok) snprintf(out, olen, "[缺失]");
|
||
else if (a->bg) snprintf(out, olen, "·后台");
|
||
else out[0] = 0;
|
||
}
|
||
|
||
/* 宫格自绘(镜像 menu_sdk_grid_draw → 记录几何 + 标注决策;宿主无字体用色块近似)。 */
|
||
static void grid_draw(int idx, int x, int y, int w, int h, int st, void *ctx)
|
||
{
|
||
(void)st; (void)ctx;
|
||
if (idx < 0 || idx >= g_napps) return;
|
||
if (g_draw_cnt < 16) {
|
||
g_cell_idx[g_draw_cnt] = idx; g_cell_x[g_draw_cnt] = x; g_cell_y[g_draw_cnt] = y;
|
||
g_cell_w[g_draw_cnt] = w; g_cell_h[g_draw_cnt] = h;
|
||
g_draw_cnt++;
|
||
}
|
||
decide_label(idx, g_label[idx], sizeof g_label[idx]);
|
||
meow_gfx_fill_round(x, y, w, h, 16, meow_theme_get()->surfaceContainer);
|
||
meow_gfx_fill_round(x + w / 2 - 22, y + 12, 44, 44, 13, meow_theme_get()->primary);
|
||
}
|
||
/* 列表自绘(镜像 menu_sdk_list_draw) */
|
||
static void list_draw(int idx, int x, int y, int w, int h, int st, void *ctx)
|
||
{
|
||
(void)st; (void)ctx;
|
||
if (idx < 0 || idx >= g_napps) return;
|
||
if (g_draw_cnt < 16) {
|
||
g_cell_idx[g_draw_cnt] = idx; g_cell_x[g_draw_cnt] = x; g_cell_y[g_draw_cnt] = y;
|
||
g_cell_w[g_draw_cnt] = w; g_cell_h[g_draw_cnt] = h;
|
||
g_draw_cnt++;
|
||
}
|
||
decide_label(idx, g_label[idx], sizeof g_label[idx]);
|
||
meow_gfx_fill_round(x, y, w, h, 12, meow_theme_get()->surfaceContainer);
|
||
meow_gfx_fill_round(x + MENU_LIST_ICON_X, y + (h - MENU_LIST_ICON) / 2, MENU_LIST_ICON, MENU_LIST_ICON, 12,
|
||
meow_theme_get()->primary);
|
||
}
|
||
|
||
static void seed_apps(int n)
|
||
{
|
||
static const char *nm[16] = {"应用一","应用二","应用三","应用四","应用五","应用六","应用七","应用八",
|
||
"应用九","应用十","应用十一","应用十二","应用十三","应用十四","应用十五","应用十六"};
|
||
for (int i = 0; i < n; i++) {
|
||
g_apps[i].name = nm[i]; g_apps[i].ok = 1; g_apps[i].bg = 0; g_apps[i].pkg = "os.meow.test";
|
||
}
|
||
/* 2 号缺失、3 号后台(用 0/1 锚定到前几格便于断言) */
|
||
if (n > 1) g_apps[1].ok = 0;
|
||
if (n > 2) g_apps[2].bg = 1;
|
||
g_napps = n;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
meow_ui_opts o = { XRES, YRES };
|
||
if (meow_ui_init(&o) != 0) { printf("init failed\n"); return 1; }
|
||
const meow_theme *th = meow_theme_get();
|
||
int gx, gy, gw, gh, lx, ly, lw, lh;
|
||
grid_region(&gx, &gy, &gw, &gh); list_region(&lx, &ly, &lw, &lh);
|
||
int cw = grid_cell_w(), rh = grid_row_h();
|
||
CHECK(gx == MENU_GRID_PAD && gw == XRES - 2 * MENU_GRID_PAD, "grid viewport x=%d w=%d (expect %d,%d)", gx, gw, MENU_GRID_PAD, XRES - 2 * MENU_GRID_PAD);
|
||
CHECK(lx == 8 && lw == XRES - 16, "list viewport x=%d w=%d", lx, lw);
|
||
CHECK(gh == APPS_BOT - APPS_TOP, "grid viewport h=%d (expect %d)", gh, APPS_BOT - APPS_TOP);
|
||
|
||
/* ============ G1 宫格虚拟化渲染 + 几何校准 ============ */
|
||
printf("G1 lazygrid render @480x720\n");
|
||
{
|
||
seed_apps(8);
|
||
meow_ui_activity_push(SDK_ACT);
|
||
meow_lazygrid *g = meow_lazygrid_create(gx, gy, gw, gh, MENU_GRID_COLS, cw, MENU_GRID_CARD_H, MENU_GRID_GAP);
|
||
CHECK(g != NULL, "lazygrid create");
|
||
CHECK(meow_lazygrid_set_vsource(g, grid_count, grid_bind, NULL) == 0, "vsource");
|
||
CHECK(meow_lazygrid_set_item_draw(g, grid_draw, NULL) == 0, "set_item_draw");
|
||
CHECK(meow_lazygrid_set_empty_text(g, "没有找到应用") == 0, "empty_text");
|
||
meow_ui_clear(th->bg);
|
||
g_draw_cnt = 0;
|
||
meow_lazygrid_draw(g, th);
|
||
/* 8 格(MAX_APPS)全可见:每格恰一次自绘 */
|
||
CHECK(g_draw_cnt == 8, "8 apps -> each visible cell drawn once got %d", g_draw_cnt);
|
||
/* 几何校准:cell0=12,96 cell1=245,96 cell2(行1col0)=12,218 cell3=245,218 */
|
||
CHECK(g_cell_idx[0] == 0 && g_cell_x[0] == 12 && g_cell_y[0] == 96, "cell0 geom idx=%d x=%d y=%d (expect 0,12,96)", g_cell_idx[0], g_cell_x[0], g_cell_y[0]);
|
||
CHECK(g_cell_idx[1] == 1 && g_cell_x[1] == 12 + cw + MENU_GRID_GAP && g_cell_y[1] == 96, "cell1 geom idx=%d x=%d y=%d", g_cell_idx[1], g_cell_x[1], g_cell_y[1]);
|
||
CHECK(g_cell_idx[2] == 2 && g_cell_x[2] == 12 && g_cell_y[2] == 96 + rh, "cell2 geom idx=%d x=%d y=%d", g_cell_idx[2], g_cell_x[2], g_cell_y[2]);
|
||
CHECK(g_cell_idx[7] == 7 && g_cell_y[7] == 96 + 3 * rh, "cell7 (row3col1) y=%d (expect %d)", g_cell_y[7], 96 + 3 * rh);
|
||
/* 全部格/行不越视口(右侧 ≤ gx+gw,底部 ≤ gy+gh) */
|
||
int bad = 0;
|
||
for (int i = 0; i < g_draw_cnt; i++)
|
||
if (g_cell_x[i] + g_cell_w[i] > gx + gw || g_cell_y[i] + g_cell_h[i] > gy + gh) bad++;
|
||
CHECK(bad == 0, "all cells within grid viewport (bad=%d, vp=[%d,%d]x[%d,%d])", bad, gx, gx + gw, gy, gy + gh);
|
||
/* 8 格(MAX_APPS)装得下 → max_scroll==0 */
|
||
CHECK(meow_lazygrid_max_scroll(g) == 0, "8 apps fit -> max_scroll=0 got %d", meow_lazygrid_max_scroll(g));
|
||
meow_lazygrid_free(g);
|
||
meow_ui_activity_pop();
|
||
}
|
||
|
||
/* ============ G2 宫格命中(含滚动换算:用 12 应用使超屏可滚) ============ */
|
||
printf("G2 lazygrid hit\n");
|
||
{
|
||
seed_apps(12);
|
||
meow_ui_activity_push(SDK_ACT);
|
||
meow_lazygrid *g = meow_lazygrid_create(gx, gy, gw, gh, MENU_GRID_COLS, cw, MENU_GRID_CARD_H, MENU_GRID_GAP);
|
||
meow_lazygrid_set_vsource(g, grid_count, grid_bind, NULL);
|
||
/* cell0 中心(123,152) → idx0;cell1 中心(356,152) → idx1;行1col0 中心(123,274=96+122+56) → idx2 */
|
||
CHECK(meow_lazygrid_hit(g, 12 + cw / 2, 96 + MENU_GRID_CARD_H / 2) == 0, "hit cell0 center got %d", meow_lazygrid_hit(g, 12 + cw / 2, 96 + MENU_GRID_CARD_H / 2));
|
||
CHECK(meow_lazygrid_hit(g, 12 + cw + MENU_GRID_GAP + cw / 2, 96 + MENU_GRID_CARD_H / 2) == 1, "hit cell1 center got %d", meow_lazygrid_hit(g, 12 + cw + MENU_GRID_GAP + cw / 2, 96 + MENU_GRID_CARD_H / 2));
|
||
CHECK(meow_lazygrid_hit(g, 12 + cw / 2, 96 + rh + MENU_GRID_CARD_H / 2) == 2, "hit row1col0 got %d", meow_lazygrid_hit(g, 12 + cw / 2, 96 + rh + MENU_GRID_CARD_H / 2));
|
||
/* 水平 gap x=240(col0[12,235) col1[245,468) 之间)→ -1 */
|
||
CHECK(meow_lazygrid_hit(g, 240, 96 + MENU_GRID_CARD_H / 2) == -1, "horizontal gap miss got %d", meow_lazygrid_hit(g, 240, 96 + MENU_GRID_CARD_H / 2));
|
||
/* 垂直 gap y=212(row0[96,208) row1[218,320) 之间)→ -1 */
|
||
CHECK(meow_lazygrid_hit(g, 12 + cw / 2, 212) == -1, "vertical gap miss got %d", meow_lazygrid_hit(g, 12 + cw / 2, 212));
|
||
/* 越界(右侧 x=475 / 列表区外 y=92)→ -1 */
|
||
CHECK(meow_lazygrid_hit(g, 475, 96 + MENU_GRID_CARD_H / 2) == -1, "right-out miss got %d", meow_lazygrid_hit(g, 475, 96 + MENU_GRID_CARD_H / 2));
|
||
CHECK(meow_lazygrid_hit(g, 12 + cw / 2, APPS_TOP - 4) == -1, "above viewport -> -1 got %d", meow_lazygrid_hit(g, 12 + cw / 2, APPS_TOP - 4));
|
||
/* 滚 1 行(rh=122)后,视口顶 y=96 中心(x)落在行1 → idx2 */
|
||
meow_lazygrid_set_scroll(g, rh);
|
||
CHECK(meow_lazygrid_hit(g, 12 + cw / 2, 96 + MENU_GRID_CARD_H / 2) == 2, "scroll+122: top center hits row1 -> idx2 got %d", meow_lazygrid_hit(g, 12 + cw / 2, 96 + MENU_GRID_CARD_H / 2));
|
||
/* tap_at:命中 → 选中 + 回调 */
|
||
meow_lazygrid_set_scroll(g, 0);
|
||
CHECK(meow_lazygrid_tap_at(g, 12 + cw / 2, 96 + MENU_GRID_CARD_H / 2) == 0, "tap_at idx0 got %d", meow_lazygrid_tap_at(g, 12 + cw / 2, 96 + MENU_GRID_CARD_H / 2));
|
||
CHECK(meow_lazygrid_sel(g) == 0, "sel=0");
|
||
meow_lazygrid_free(g);
|
||
meow_ui_activity_pop();
|
||
}
|
||
|
||
/* ============ G3 宫格滚动(超屏 + scroll_attach) ============ */
|
||
printf("G3 lazygrid overflow scroll\n");
|
||
{
|
||
seed_apps(12); /* 6 行 → 内容高 722 > 视口 488 */
|
||
meow_ui_activity_push(SDK_ACT);
|
||
meow_lazygrid *g = meow_lazygrid_create(gx, gy, gw, gh, MENU_GRID_COLS, cw, MENU_GRID_CARD_H, MENU_GRID_GAP);
|
||
meow_lazygrid_set_vsource(g, grid_count, grid_bind, NULL);
|
||
int ms = meow_lazygrid_max_scroll(g);
|
||
CHECK(ms > 0, "12 apps overflow max_scroll=%d (>0)", ms);
|
||
meow_lazygrid_set_item_draw(g, grid_draw, NULL);
|
||
meow_ui_clear(th->bg);
|
||
g_draw_cnt = 0;
|
||
meow_lazygrid_draw(g, th);
|
||
int win_rows = gh / rh + 1; /* 可见行窗口上界 */
|
||
CHECK(g_draw_cnt <= win_rows * MENU_GRID_COLS, "virtualized window: drew %d <= %d (not 12)", g_draw_cnt, win_rows * MENU_GRID_COLS);
|
||
/* scroll_by 往返 */
|
||
int before = meow_lazygrid_scroll(g);
|
||
meow_lazygrid_scroll_by(g, rh);
|
||
CHECK(meow_lazygrid_scroll(g) == before + rh, "scroll_by +row got %d (expect %d)", meow_lazygrid_scroll(g), before + rh);
|
||
meow_lazygrid_set_scroll(g, 0);
|
||
CHECK(meow_lazygrid_scroll(g) == 0, "set_scroll 0");
|
||
/* scroll_attach 可建(fling/snap 惯性滚动接线) */
|
||
meow_scroll *s = meow_lazygrid_scroll_attach(g);
|
||
CHECK(s != NULL, "scroll_attach non-NULL");
|
||
if (s) {
|
||
meow_scroll_set_snap(s, rh);
|
||
uint64_t t = meow_ui_time_ms();
|
||
for (int i = 0; i < 10; i++) meow_scroll_track(s, 40, t + (uint64_t)i * 16);
|
||
int st = meow_scroll_offset(s);
|
||
CHECK(st > 0, "drag advances offset got %d", st);
|
||
meow_scroll_fling(s);
|
||
int guard = 0;
|
||
while (meow_scroll_active(s) && guard++ < 300) { meow_anim_tick(); usleep(10000); }
|
||
meow_anim_tick();
|
||
int en = meow_scroll_offset(s);
|
||
CHECK(en >= st, "fling advances %d >= %d", en, st);
|
||
CHECK(en <= meow_lazygrid_max_scroll(g), "fling in range %d (max %d)", en, meow_lazygrid_max_scroll(g));
|
||
CHECK(meow_lazygrid_scroll(g) == en, "grid scroll mirrors offset");
|
||
meow_scroll_free(s);
|
||
}
|
||
meow_lazygrid_free(g);
|
||
meow_ui_activity_pop();
|
||
}
|
||
|
||
/* ============ L1 列表渲染 + 命中 + 滚动 ============ */
|
||
printf("L1 listview render + hit + scroll\n");
|
||
{
|
||
seed_apps(8);
|
||
meow_ui_activity_push(SDK_ACT);
|
||
meow_widget *lv = meow_listview_create(lx, ly, lw, lh, 0, NULL, NULL, MEOW_LIST_SEL_SINGLE);
|
||
CHECK(lv != NULL, "listview create");
|
||
meow_listview_set_vsource(lv, list_count, list_bind, NULL);
|
||
meow_listview_set_item_draw(lv, list_draw, NULL);
|
||
meow_listview_set_item_h(lv, MENU_LIST_ROW_H);
|
||
meow_ui_fill_rect(0, 0, XRES, YRES, th->bg);
|
||
meow_ui_fill_rect(lx, ly, lw, lh, th->surfaceVariant);
|
||
g_draw_cnt = 0;
|
||
meow_widget_draw(lv);
|
||
CHECK(g_draw_cnt == 8, "8 rows drawn got %d", g_draw_cnt);
|
||
CHECK(g_cell_x[0] == 8 && g_cell_w[0] == XRES - 16, "row0 x=%d w=%d (expect 8,%d)", g_cell_x[0], g_cell_w[0], XRES - 16);
|
||
CHECK(g_cell_y[0] == APPS_TOP && g_cell_y[1] == APPS_TOP + MENU_LIST_ROW_H, "row0/1 y=%d/%d", g_cell_y[0], g_cell_y[1]);
|
||
CHECK(meow_listview_max_scroll(lv) == 0, "8 rows fit -> max_scroll=0 got %d", meow_listview_max_scroll(lv));
|
||
CHECK(meow_listview_hit(lv, 100, APPS_TOP + MENU_LIST_ROW_H / 2) == 0, "hit row0 got %d", meow_listview_hit(lv, 100, APPS_TOP + MENU_LIST_ROW_H / 2));
|
||
CHECK(meow_listview_hit(lv, 100, APPS_TOP + MENU_LIST_ROW_H + MENU_LIST_ROW_H / 2) == 1, "hit row1 got %d", meow_listview_hit(lv, 100, APPS_TOP + MENU_LIST_ROW_H + MENU_LIST_ROW_H / 2));
|
||
CHECK(meow_listview_hit(lv, 4, APPS_TOP + MENU_LIST_ROW_H / 2) == -1, "left of list x=4 -> -1 got %d", meow_listview_hit(lv, 4, APPS_TOP + MENU_LIST_ROW_H / 2));
|
||
CHECK(meow_listview_hit(lv, 100, APPS_TOP - 4) == -1, "above list -> -1 got %d", meow_listview_hit(lv, 100, APPS_TOP - 4));
|
||
meow_widget_free(lv);
|
||
meow_ui_activity_pop();
|
||
|
||
/* 12 行超屏滚动 */
|
||
seed_apps(12);
|
||
meow_ui_activity_push(SDK_ACT);
|
||
meow_widget *lv2 = meow_listview_create(lx, ly, lw, lh, 0, NULL, NULL, MEOW_LIST_SEL_SINGLE);
|
||
meow_listview_set_vsource(lv2, list_count, list_bind, NULL);
|
||
meow_listview_set_item_draw(lv2, list_draw, NULL);
|
||
meow_listview_set_item_h(lv2, MENU_LIST_ROW_H);
|
||
CHECK(meow_listview_max_scroll(lv2) > 0, "12 rows overflow max_scroll=%d", meow_listview_max_scroll(lv2));
|
||
meow_listview_set_scroll(lv2, MENU_LIST_ROW_H);
|
||
/* 滚 1 行:视口顶 row 中心落在 idx1 */
|
||
CHECK(meow_listview_hit(lv2, 100, APPS_TOP + MENU_LIST_ROW_H / 2) == 1, "scrolled row0->idx1 got %d", meow_listview_hit(lv2, 100, APPS_TOP + MENU_LIST_ROW_H / 2));
|
||
meow_widget_free(lv2);
|
||
meow_ui_activity_pop();
|
||
seed_apps(8); /* 还原 */
|
||
}
|
||
|
||
/* ============ E 空态(count==0 → 空态路径,无格/行卡) ============ */
|
||
printf("E empty state\n");
|
||
{
|
||
seed_apps(0);
|
||
meow_ui_activity_push(SDK_ACT);
|
||
meow_lazygrid *g = meow_lazygrid_create(gx, gy, gw, gh, MENU_GRID_COLS, cw, MENU_GRID_CARD_H, MENU_GRID_GAP);
|
||
meow_lazygrid_set_vsource(g, grid_count, grid_bind, NULL);
|
||
meow_lazygrid_set_item_draw(g, grid_draw, NULL);
|
||
meow_lazygrid_set_empty_text(g, "没有找到应用");
|
||
meow_ui_clear(th->bg);
|
||
g_draw_cnt = 0;
|
||
meow_lazygrid_draw(g, th);
|
||
CHECK(g_draw_cnt == 0, "empty grid: 0 cells drawn got %d", g_draw_cnt);
|
||
meow_lazygrid_free(g);
|
||
/* 列表空态 */
|
||
meow_widget *lv = meow_listview_create(lx, ly, lw, lh, 0, NULL, NULL, MEOW_LIST_SEL_NONE);
|
||
meow_listview_set_vsource(lv, list_count, list_bind, NULL);
|
||
meow_listview_set_item_draw(lv, list_draw, NULL);
|
||
meow_listview_set_empty_text(lv, "没有找到应用");
|
||
meow_ui_fill_rect(lx, ly, lw, lh, th->surfaceVariant);
|
||
g_draw_cnt = 0;
|
||
meow_widget_draw(lv);
|
||
CHECK(g_draw_cnt == 0, "empty list: 0 rows drawn got %d", g_draw_cnt);
|
||
meow_widget_free(lv);
|
||
meow_ui_activity_pop();
|
||
seed_apps(8);
|
||
}
|
||
|
||
/* ============ M 图标·缺失·后台标注(镜像 menu_draw_grid_cell 决策) ============ */
|
||
printf("M missing / background label\n");
|
||
{
|
||
seed_apps(8); /* app1 ok=0 → [缺失];app2 bg=1 → ·后台;app0 → 无 */
|
||
CHECK(strcmp(g_label[0], "") == 0, "app0 (ok,no bg) no label got '%s'", g_label[0]);
|
||
CHECK(memcmp(g_label[1], "[缺失]", 6) == 0, "app1 (missing) -> '[缺失]' got '%s'", g_label[1]);
|
||
CHECK(memcmp(g_label[2], "·后台", 6) == 0, "app2 (backgrounded) -> '·后台' got '%s'", g_label[2]);
|
||
/* 命中语义:enabled=1 恒响应 —— 缺 app 亦可命中(启动→失败 toast,同旧 draw_menu 行为) */
|
||
meow_ui_activity_push(SDK_ACT);
|
||
meow_lazygrid *g = meow_lazygrid_create(gx, gy, gw, gh, MENU_GRID_COLS, cw, MENU_GRID_CARD_H, MENU_GRID_GAP);
|
||
meow_lazygrid_set_vsource(g, grid_count, grid_bind, NULL);
|
||
CHECK(meow_lazygrid_hit(g, 12 + cw / 2, 96 + rh + MENU_GRID_CARD_H / 2) == 2, "missing app1 tappable (enabled=1) -> hit row0col? got %d", meow_lazygrid_hit(g, 12 + cw / 2, 96 + rh + MENU_GRID_CARD_H / 2));
|
||
meow_lazygrid_free(g);
|
||
meow_ui_activity_pop();
|
||
}
|
||
|
||
meow_ui_shutdown();
|
||
printf("menu_sdk: %d pass / %d fail\n", g_pass, g_fail);
|
||
return g_fail ? 1 : 0;
|
||
}
|