/* ui/settings.c — 设置页(ACT_SETTINGS)。 * * 行项:音质(Hi-res/较高/标准,默认 Hi-res)+ 缓存上限(1~100GB,默认 10GB)+ 退出登录。 * 设置持久化到 MeowData/os.meow.ncm/data(meow_config_*;缓存上限键 ncm.cache_limit_gb 与核心库 * ncm_cache_load_setting 对齐)。音质/上限经 BottomSheet 选择;退出登录经 confirm 弹窗。 * 空态/绘制用 custom 绘制物 + 手动行命中(仅 3 行,无需列表滚动)。 */ #include "../ncm_app_internal.h" #include #include #include typedef struct { meow_view *view; int active; } settings_ctrl; static settings_ctrl g_sc; static meow_widget *g_tb; static ncm_rect_t r_cont; static int g_custom_id; static int g_row_h; /* 行高 */ static const char *level_name(int lv) { switch (lv) { case NCM_LEVEL_HIRES: return "Hi-Res"; case NCM_LEVEL_LOSSLESS:return "Lossless"; case NCM_LEVEL_EXHIGH: return "Exhigh"; case NCM_LEVEL_HIGHER: return "Higher"; default: return "Standard"; } } static void settings_draw(void *ctx) { (void)ctx; if (meow_ui_activity_active() != ACT_SETTINGS) return; const meow_theme *th = meow_theme_get(); const ncm_pal_t *pa = ncm_palette(g_dark); g_row_h = 60; int y = r_cont.y + 16; int w = r_cont.w - 2 * APP_PAD; int x = APP_PAD; /* 行:音质 / 缓存上限 / 退出登录 / 关于(色 = 语义色;退出 = error)。 */ struct { const char *title; char sub[160]; int red; } rows[4]; rows[0].title = "音质"; rows[0].red = 0; rows[1].title = "缓存上限"; rows[1].red = 0; rows[2].title = "退出登录"; rows[2].red = 1; rows[3].title = "关于网易云"; rows[3].red = 0; snprintf(rows[0].sub, sizeof rows[0].sub, "当前: %s", level_name(g_st.level)); snprintf(rows[1].sub, sizeof rows[1].sub, "当前: %d GB", g_st.cache_limit_gb); snprintf(rows[2].sub, sizeof rows[2].sub, "%s", g_st.nickname[0] ? g_st.nickname : "退出"); snprintf(rows[3].sub, sizeof rows[3].sub, "%s", "v0.1 · MeowOS"); for (int i = 0; i < 4; i++) { int yy = y + i * (g_row_h + 8); ncm_fill_round(x, yy, w, g_row_h, 12, th->surfaceContainer); uint32_t tcol = rows[i].red ? th->error : th->onSurface; uint32_t scol = rows[i].red ? th->error : th->onSurfaceVariant; meow_ui_text(x + APP_PAD, yy + 12, rows[i].title, 20, tcol); meow_ui_text(x + APP_PAD, yy + 34, rows[i].sub, 20, scol); meow_ui_text_right(x + w - APP_PAD, yy + 18, ">", 28, rows[i].red ? th->error : th->onSurfaceVariant); } /* 分组说明(占位)+ 品牌红小字。 */ int ny = y + 4 * (g_row_h + 8) + 4; meow_ui_text(x, ny, "以下为占位,v1 不启用", 20, th->onSurfaceVariant); meow_ui_text(x, ny + 24, "品牌红 © MeowOS · NCM", 20, pa->red); } static int row_at(int x, int y) { (void)x; int r = r_cont.y + 16; for (int i = 0; i < 4; i++) { int yy = r + i * (g_row_h + 8); if (y >= yy && y <= yy + g_row_h) return i; } return -1; } int settings_is_active(void) { return g_sc.active; } int settings_on_tap(int x, int y) { if (g_tb) { int ah = meow_topappbar_hit(g_tb, x, y); if (ah == MEOW_TOPBAR_BACK) { meow_view_page_pop(); return 1; } if (ah >= 0) { return 1; } } if (x < APP_PAD || x > r_cont.w - APP_PAD) return 0; int row = row_at(x, y); if (row < 0) return 0; if (row == 0) { /* 音质 */ meow_sheet *s = meow_sheet_create("Music Quality", 0); meow_sheet_add_item(s, "Hi-Res"); meow_sheet_add_item(s, "Higher"); meow_sheet_add_item(s, "Standard"); int op = meow_sheet_open(s); meow_sheet_free(s); if (op >= 0) { g_st.level = (op == 0) ? NCM_LEVEL_HIRES : (op == 1) ? NCM_LEVEL_HIGHER : NCM_LEVEL_STANDARD; player_manager_set_quality(g_st.level, g_st.cache_limit_gb); appstate_save_settings(); meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h); } return 1; } if (row == 1) { /* 缓存上限 */ static const int gbs[] = { 1, 2, 5, 10, 20, 30, 50, 100 }; static const char *labels[] = { "1 GB", "2 GB", "5 GB", "10 GB", "20 GB", "30 GB", "50 GB", "100 GB" }; meow_sheet *s = meow_sheet_create("Cache Limit", 0); for (int i = 0; i < 8; i++) meow_sheet_add_item(s, labels[i]); int op = meow_sheet_open(s); meow_sheet_free(s); if (op >= 0 && op < 8) { g_st.cache_limit_gb = gbs[op]; player_manager_set_quality(g_st.level, g_st.cache_limit_gb); appstate_save_settings(); meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h); } return 1; } if (row == 2) { /* 退出登录 */ meow_dialog *d = meow_dialog_create(MEOW_DLG_CONFIRM, "Sign out?"); const char *msgs[1]; msgs[0] = "Playback will stop.\nYou'll need to scan a QR code again."; meow_dialog_set_items(d, msgs, 1, -1); int yes = meow_dialog_confirm(d); meow_dialog_free(d); if (yes) { appstate_logout(); meow_view_page_replace(ACT_LOGIN); } return 1; } if (row == 3) { /* 关于(v1 占位) */ ncm_snack("关于页开发中"); return 1; } return 0; } void settings_swipe(int dir, int dist) { (void)dir; (void)dist; } void settings_touch_down(int x, int y) { (void)x; (void)y; } void settings_touch_move(int x, int y) { (void)x; (void)y; } void settings_open(void) { g_st.playing_song = -1; meow_view_page_push(ACT_SETTINGS); } void settings_reload(void) { const meow_theme *th = meow_theme_get(); meow_ui_set_page_bg(th->bg); meow_ui_paint_full(); } static void st_page_create(meow_view *v, void *ctx) { (void)v; ((settings_ctrl *)ctx)->view = v; r_cont = ncm_content_rect(); meow_ui_activity_push(ACT_SETTINGS); /* owner=ACT_SETTINGS */ g_tb = ncm_topbar_create("Settings", 1, NULL); meow_ui_activity_pop(); g_row_h = 60; g_custom_id = meow_draw_custom_register(1, r_cont.x, r_cont.y, r_cont.w, r_cont.h, settings_draw, NULL); } static void st_page_enter(meow_view *v, void *ctx) { (void)v; ((settings_ctrl *)ctx)->active = 1; settings_reload(); } static void st_page_leave(meow_view *v, void *ctx) { (void)v; ((settings_ctrl *)ctx)->active = 0; } static void st_page_destroy(meow_view *v, void *ctx) { (void)v; (void)ctx; if (g_custom_id) { meow_draw_unregister(g_custom_id); g_custom_id = 0; } if (g_tb) { meow_widget_free(g_tb); g_tb = NULL; } } meow_view *settings_view_create(void) { static const meow_view_hooks h = { .on_create = st_page_create, .on_enter = st_page_enter, .on_leave = st_page_leave, .on_destroy = st_page_destroy, }; return meow_view_create(ACT_SETTINGS, MEOW_VIEW_ACTIVITY, &h, &g_sc); }