feat(ncm): 复古UI重做 (agent-211, 按 NCM_UI_DESIGN.md) — 白红/黑红双套配色(ncm_palette+主题监听实时换肤); 主页红topbar+个人区+双页签(我创建的/我收藏的, is_collected/subscribed已补); 搜索页(cloudsearch,仅确认时发请求); 歌单详情封面头块+播放全部; 播放页黑胶唱盘(行扫圆+封面裁圆+唱针+分带刷新); 播放页tick分带刷新修整页重绘隐患

ncm→90a25cf0; 0 warning; 宿主14张双主题截图目检+core测试全过
This commit is contained in:
MeowOS committed 2026-08-31 10:26:44 +08:00
1 parent 4cec476dd9
commit 8964cd1750
33 files changed
+1333 -320

No files matched your search

+32 -1
View File
@@ -21,13 +21,16 @@ static void mock_playlists(void)
{
static const char *names[] = { "My Favorites", "Focus Mix", "Commute", "Workout", "Sleep" };
static const int counts[] = { 128, 56, 34, 22, 15 };
static const int coll[] = { 0, 1, 0, 0, 1 }; /* 0=我创建的 1=我收藏的 */
g_st.playlist_count = 5;
g_st.logged_in = 1;
ncm_strlcpy(g_st.nickname, "Tester", sizeof g_st.nickname);
ncm_strlcpy(g_st.uid, "408996274", sizeof g_st.uid);
for (int i = 0; i < 5; i++) {
snprintf(g_st.playlists[i].id, sizeof g_st.playlists[i].id, "pl%d", i);
snprintf(g_st.playlists[i].name, sizeof g_st.playlists[i].name, "%s", names[i]);
g_st.playlists[i].track_count = counts[i];
g_st.playlists[i].is_collected = coll[i];
snprintf(g_st.playlists[i].cover, sizeof g_st.playlists[i].cover,
"http://p1.music.126.net/cover%d.jpg", i);
}
@@ -84,6 +87,7 @@ static int page_id(const char *name)
if (strcmp(name, "player") == 0) return ACT_PLAYER;
if (strcmp(name, "lands") == 0) return ACT_PLAYER; /* 横屏布局 */
if (strcmp(name, "settings") == 0) return ACT_SETTINGS;
if (strcmp(name, "search") == 0) return ACT_SEARCH;
return -1;
}
@@ -100,7 +104,13 @@ int main(int argc, char **argv)
meow_ui_opts o = { APP_W, APP_H };
if (meow_ui_init(&o) != 0) { fprintf(stderr, "init failed\n"); return 1; }
meow_theme_add_listener(NULL);
/* 主题:NCM_THEME=light|dark 切换两套配色(白红/黑红),1=light 时用 meow_theme_set_mode。 */
const char *theme = getenv("NCM_THEME");
int dark = !(theme && strcmp(theme, "light") == 0);
meow_theme_set_mode(dark ? MEOW_THEME_DARK : MEOW_THEME_LIGHT);
g_dark = dark;
meow_theme_add_listener(ncm_on_theme);
ncm_on_theme(dark ? MEOW_THEME_DARK : MEOW_THEME_LIGHT); /* 同步初始 g_dark */
/* mock 状态 */
if (g_st.cache_dir[0] == 0) {
@@ -135,6 +145,23 @@ int main(int argc, char **argv)
ncm_strlcpy(g_st.nickname, "Tester", sizeof g_st.nickname);
g_st.level = NCM_LEVEL_HIRES;
g_st.cache_limit_gb = 10;
} else if (strcmp(name, "search") == 0) {
mock_playlists();
static const char *sn[] = { "Song One", "Song Two", "Song Three" };
static const char *ar[] = { "Artist A", "Artist B", "Artist C" };
static const char *al[] = { "Album 1", "Album 2", "Album 3" };
ncm_strlcpy(g_st.search_query, "test", sizeof g_st.search_query);
g_st.search_count = 3;
g_st.search_err = 0;
for (int i = 0; i < 3; i++) {
snprintf(g_st.songs[i].id, sizeof g_st.songs[i].id, "%ld", 1000L + i);
snprintf(g_st.songs[i].name, sizeof g_st.songs[i].name, "%s", sn[i]);
snprintf(g_st.songs[i].art_name, sizeof g_st.songs[i].art_name, "%s", ar[i]);
snprintf(g_st.songs[i].album_name, sizeof g_st.songs[i].album_name, "%s", al[i]);
g_st.songs[i].duration_ms = 180000 + i * 20000;
g_st.songs[i].fee = 0;
}
g_st.song_count = 3;
} else {
mock_playlists();
}
@@ -145,6 +172,7 @@ int main(int argc, char **argv)
else if (strcmp(name, "playlist") == 0) v = playlist_view_create();
else if (strcmp(name, "player") == 0 || strcmp(name, "lands") == 0) v = player_view_create();
else if (strcmp(name, "settings") == 0) v = settings_view_create();
else if (strcmp(name, "search") == 0) v = search_view_create();
if (!v) { fprintf(stderr, "bad page\n"); return 1; }
if (strcmp(name, "lands") == 0) player_set_screen(1);
@@ -156,6 +184,9 @@ int main(int argc, char **argv)
if (strcmp(name, "player") == 0 || strcmp(name, "lands") == 0) {
player_tick(meow_ui_time_ms());
}
if (strcmp(name, "search") == 0) {
search_submit(g_st.search_query); /* 宿主:应用预置搜索结果 */
}
meow_ui_shutdown();
printf("dumped %s -> %s\n", name, dump);