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

+36 -19
View File
@@ -18,7 +18,7 @@ static volatile int g_err; /* 最后一次错误(网络不可达等) *
static int g_active;
static int g_custom_id; /* 绘制物 */
static ncm_rect_t r_cont;
static ncm_rect_t r_scr; /* 整屏(沉浸式,hero 从 y=0 起) */
/* ---- 绘制 ---- */
static void login_draw(void *ctx)
@@ -26,41 +26,57 @@ static void login_draw(void *ctx)
(void)ctx;
if (meow_ui_activity_active() != ACT_LOGIN) return;
const meow_theme *th = meow_theme_get();
const ncm_pal_t *pa = ncm_palette(g_dark);
int W = meow_ui_width(), H = meow_ui_height();
/* 标题 */
meow_ui_text_center(r_cont.x + r_cont.w/2, r_cont.y + 24, "NetEase Cloud Music", 28, th->fg);
meow_ui_text_center(r_cont.x + r_cont.w/2, r_cont.y + 64, "scan to sign in", 20, th->dim);
/* 品牌红 hero 带(沉浸,从 y=0 到 264;day=red / night=hero 深红压场)。 */
meow_ui_fill_rect(0, 0, W, 264, pa->hero);
/* 标题「网易云音乐」+ 红点 logo(用字体大小近似排版,host 字库用 ASCII 替文本)。 */
const char *title = "网易云音乐";
int tw = meow_ui_text_width(title, 34);
meow_ui_text_center(W/2, 112, title, 34, pa->on_red);
/* 24px 红点 logo(标题左前) */
ncm_fill_round(W/2 - tw/2 - 36, 112 + 4, 24, 24, 12, pa->red_lit);
meow_ui_text_center(W/2, 160, "扫码登录 · 发现好音乐", 22, pa->on_red_dim);
/* 弱分隔(hero 与内容衔接) */
meow_ui_fill_rect(0, 264, W, 1, th->outline);
/* QR 卡(128,228,224,224):day 纯白 / night surfaceContainer;圆角 16 + 内描边。 */
int qc = 224, qx = (W - qc) / 2, qy = 228;
ncm_fill_round(qx, qy, qc, qc, 16, th->surfaceContainer);
meow_ui_draw_rect_round(qx, qy, qc, qc, 16, th->outlineVariant);
/* QR 位图 */
int sz = g_st.qr_size;
if (sz > 0) {
int qs = sz * 6; /* 每模块 6px → 41x41=246,33x33=198 */
if (qs > 320) qs = sz * 4;
int ox = r_cont.x + (r_cont.w - qs) / 2;
int oy = r_cont.y + 108;
/* 白色底 + 边框 */
meow_ui_fill_rect(ox - 8, oy - 8, qs + 16, qs + 16, 0xFFFFFF);
meow_ui_draw_rect(ox - 8, oy - 8, qs + 16, qs + 16, th->outline);
int qs = sz * 6;
if (qs > qc - 32) qs = (qc - 32) / sz * sz;
int ox = qx + (qc - qs) / 2, oy = qy + (qc - qs) / 2;
meow_ui_fill_rect(ox, oy, qs, qs, 0xFFFFFF);
for (int y = 0; y < sz; y++)
for (int x = 0; x < sz; x++)
if (g_st.qr_mods[y*sz + x])
meow_ui_fill_rect(ox + x*6, oy + y*6, 6, 6, 0x000000);
} else {
meow_ui_text_center(r_cont.x + r_cont.w/2, r_cont.y + 180,
meow_ui_text_center(W/2, qy + qc/2 - 10,
g_err ? "no network - tap to retry" : "fetching QR key...", 20, th->dim);
}
/* 状态文字 */
/* 状态文字:成功/等待用红(day red / night red_lit),失败红字提示。 */
const char *st = login_status_str(g_st.login_state);
if (g_st.login_state == NCM_LOGIN_OK && g_done) st = "success";
meow_ui_text_center(r_cont.x + r_cont.w/2, r_cont.y + 108 + 320 + 44,
st, 20, (g_st.login_state == NCM_LOGIN_OK) ? th->primary : th->dim);
uint32_t scol = (g_st.login_state == NCM_LOGIN_OK) ? pa->red : th->dim;
meow_ui_text_center(W/2, 476, st, 20, scol);
/* 底部提示 */
meow_ui_text_center(W/2, H - 56, "由 MeowOS 驱动 · 仅本地解码", 18, th->dim);
}
/* 重绘 login 内容(QR+状态变化时)。 */
static void login_repaint(void)
{
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
meow_ui_invalidate(r_scr.x, r_scr.y, r_scr.w, r_scr.h);
}
const char *login_status_str(int state)
@@ -189,8 +205,9 @@ void login_tick(uint64_t now)
static void login_page_create(meow_view *v, void *ctx)
{
(void)v; (void)ctx;
r_cont = ncm_content_rect();
g_custom_id = meow_draw_custom_register(1, r_cont.x, r_cont.y, r_cont.w, r_cont.h,
r_scr = NR(0, 0, meow_ui_width(), meow_ui_height());
/* 沉浸式:登录页整屏自绘(hero 从 y=0 起,覆盖状态栏区)。只画一次并监听绘制物重绘。 */
g_custom_id = meow_draw_custom_register(1, r_scr.x, r_scr.y, r_scr.w, r_scr.h,
login_draw, NULL);
g_active = 0;
}