feat(ncm): 播放模式三态(随机/列表循环/单曲循环,播放页三键左)+当前实时播放列表(三键右,歌单滑窗/meow_listview虚拟化分页至5194首,当前曲高亮,点播切歌) — 模式持久化,宿主modetest/queuetest全过

This commit is contained in:
AskaEth committed 2026-09-03 18:51:28 +08:00
1 parent 29e13b2b5e
commit 7ca3e5bd84
8 files changed
+707 -28

No files matched your search

+63 -2
View File
@@ -89,7 +89,11 @@ static ncm_rect_t g_cover_r; /* 封面页静态带(方形大图
static ncm_rect_t g_dyn_r; /* 封面页动态带(元信息/进度/控制/指示点) */
static int g_title_y, g_artist_y, g_qual_y, g_times_y;
static ncm_rect_t g_bar;
static int g_ctl_y, g_prev_x, g_play_x, g_next_x, g_r_side, g_r_play, g_dots_y;
static int g_ctl_y, g_prev_x, g_play_x, g_next_x, g_mode_x, g_queue_x, g_r_side, g_r_play, g_dots_y;
/* §NEW 模式/队列钮:三键左(mode,prev 更左)/ 三键右(queue,next 更右)。间距 60 保不遮挡。 */
#define GAP_SIDE 60
#define MODE_BTN_R (g_r_side) /* 与侧钮同半径(命中取 CTL_RR) */
#define MODE_LBL_SZ 14 /* 模式/队列钮文字字号(两字,居中) */
/* ---- 歌词页几何 ---- */
static int g_lyr_cx; /* 歌词垂直中心(屏中线) */
@@ -284,6 +288,18 @@ static void draw_ctrl_btn(int icon, int cx, int cy, int r, uint32_t circle, uint
draw_md3_icon(icon, cx, cy, (r * 4) / 3, fg);
}
/* 文字钮:圆形底 + 居中两字文案(§NEW 模式/队列钮)。无逐帧三角行扫,纯圆形填充。 */
static void draw_text_btn(const char *label, int cx, int cy, int r, uint32_t circle, uint32_t fg)
{
for (int dy = -r; dy <= r; dy++) {
int half = (int)sqrt((double)(r * r - dy * dy));
if (half <= 0) continue;
meow_ui_fill_rect(cx - half, cy + dy, 2 * half, 1, circle);
}
if (label && label[0])
meow_ui_text_center(cx, cy - MODE_LBL_SZ / 2, label, MODE_LBL_SZ, fg);
}
/* 圆角封面四角裁掉:把方形图四角(圆角外-方形内)盖回背景色(廉价圆角,无 GPU 裁剪)。 */
static void round_corners_bg(int x, int y, int w, int h, int r, uint32_t bg)
{
@@ -335,6 +351,8 @@ static void player_compute_geom(void)
g_times_y = g_bar.y + g_bar.h + 12; /* 500 */
g_ctl_y = g_times_y + 64; /* 564 */
g_prev_x = W / 2 - 92; g_play_x = W / 2; g_next_x = W / 2 + 92;
g_mode_x = g_prev_x - GAP_SIDE; /* 播放模式三态钮(prev 更左) */
g_queue_x = g_next_x + GAP_SIDE; /* 当前播放队列钮(next 更右) */
g_r_side = 24; g_r_play = 34;
g_dots_y = g_ctl_y + 62; /* 626 */
@@ -449,6 +467,12 @@ static void plr_draw_cover_dyn(void *ctx)
g_play_x, g_ctl_y, g_r_play, pa->red, pa->on_red);
draw_ctrl_btn(NCM_I_NEXT, g_next_x, g_ctl_y, g_r_side, PLR_CTRL_BG, PLR_CTRL_FG);
/* §NEW 播放模式三态钮(三键左,prev 更左)+ 当前播放队列钮(三键右,next 更右):
* 文案反映当前模式(随机/列表/单曲);队列钮进入实时播放列表。 */
draw_text_btn(ncm_mode_name(player_manager_play_mode()), g_mode_x, g_ctl_y,
MODE_BTN_R, PLR_CTRL_BG, PLR_CTRL_FG);
draw_text_btn("\u961f\u5217", g_queue_x, g_ctl_y, MODE_BTN_R, PLR_CTRL_BG, PLR_CTRL_FG); /* 队列 */
/* 页面指示点 */
draw_page_dots(g_dots_y);
}
@@ -604,7 +628,9 @@ void player_touch_down(int x, int y)
interactive = 1;
if (in_circle(g_prev_x, g_ctl_y, CTL_RR, x, y) ||
in_circle(g_play_x, g_ctl_y, g_r_play + 6, x, y) ||
in_circle(g_next_x, g_ctl_y, CTL_RR, x, y))
in_circle(g_next_x, g_ctl_y, CTL_RR, x, y) ||
in_circle(g_mode_x, g_ctl_y, CTL_RR, x, y) ||
in_circle(g_queue_x, g_ctl_y, CTL_RR, x, y))
interactive = 1;
}
g_touch_on = (x > PLR_EDGE_W) && (y >= meow_ui_statusbar_hotzone()) && !interactive;
@@ -675,6 +701,19 @@ void player_on_tap(int x, int y)
}
if (in_circle(g_prev_x, g_ctl_y, CTL_RR, x, y)) { player_manager_next(-1); return; }
if (in_circle(g_next_x, g_ctl_y, CTL_RR, x, y)) { player_manager_next(1); return; }
/* §NEW 播放模式三态钮:点击循环切换(SHUFFLE→LISTLOOP→SINGLE→SHUFFLE)+ 持久化 */
if (in_circle(g_mode_x, g_ctl_y, CTL_RR, x, y)) {
int m = player_manager_play_mode();
int nxt = (m + 1) % NCM_MODE_COUNT;
player_manager_set_play_mode(nxt);
appstate_apply_play_mode(nxt); /* 记住模式(退出后保留) */
player_dyn_invalidate();
return;
}
/* §NEW 当前播放队列钮:进实时播放列表页 */
if (in_circle(g_queue_x, g_ctl_y, CTL_RR, x, y)) { queue_open(); return; }
if (y >= g_bar.y - 10 && y <= g_bar.y + g_bar.h + 10 && x >= g_bar.x && x <= g_bar.x + g_bar.w) {
int64_t dur = player_manager_dur_ms();
if (dur > 0) player_seek((int64_t)(x - g_bar.x) * dur / g_bar.w);
@@ -731,6 +770,28 @@ void player_play_search_song(int i)
meow_view_page_push(ACT_PLAYER);
}
/* 从「当前实时播放队列」第 idx 首播放(§NEW 队列页点播):保持当前 queue_src 不变
* (不把队列切回歌单/不覆写歌单窗口)——上/下一首仍在该队列内循环。封面用窗口/搜索数组
* 内的元数据(不触发网络),占位行由播放线程物化后快照接管。 */
void player_play_queue_song(int idx)
{
int cnt = appstate_queue_count();
if (idx < 0 || idx >= cnt) return;
ncm_song_t s;
int have = 0;
if (appstate_get_queue() == NCM_QUEUE_SEARCH) {
if (appstate_search_song(idx, &s) == 0) have = 1;
} else if (appstate_song_copy(idx, &s) == 0) {
have = 1;
}
const char *cover = have ? (s.al_pic[0] ? s.al_pic : g_st.cover_url) : g_st.cover_url;
player_manager_play(idx);
appstate_set_playing_song(idx, NULL);
if (have) cover_request(cover, s.id);
g_ppage = 0;
meow_view_page_push(ACT_PLAYER);
}
void player_pause_toggle(int on)
{
if (player_manager_state() == NCM_PC_PAUSED) player_manager_pause(0);