feat(ncm): 三期第二批 — 中文搜索接拼音IME+设置页做好(音质全档/缓存上限可设数字+清理缓存/关于)+缓存LRU淘汰接生产路径(写前写后+手动调低上限)
- 搜索页输入框 MEOW_IME_ASCII→MEOW_IME_PINYIN: 候选上屏UTF-8经 on_change 实时回写 g_st.search_query, 键盘「拼/英」「中」切换, 英文零回归; 搜索仍仅在点「搜索」时发 cloudsearch(风控) - 设置页 5 行: 音质(Hi-Res 默认,Lossless/Exhigh/Higher/Standard 全档,core 既有降级链)/缓存上限(1~100GB 预设+「自定义」NUM IME 数字输入,默认10上限100)/清理缓存(显示当前占用,ncm_cache_clear)/退出登录/关于行 - 缓存淘汰接线: ncm_cache_evict 进生产路径 — player_manager(下载写前/写后)+cover(写前/写后)+appstate_save_settings(手动调低上限立即淘汰); 封面与 mp3 同池; 上限读设置值 - core 新增 ncm_cache_clear; 设置持久化走 meow_config_* → MeowData/dev.player.ncm/data - bench/cache_evict_sim.c 离线回归(0网络) 6 组 PASS; pagination_sim/core(host+mipsel/qemu)/6页宿主渲染 全过; mipsel -Wall -Wextra 0 warning (未动 sdk/、meow_launcherd/、vendor/;0 真实 NCM 请求;真机部署留用户)
This commit is contained in:
1 parent
1a239c5eab
commit
f23720f934
13 files changed
+469
-33
No files matched your search
@@ -235,9 +235,11 @@ static void sr_page_create(meow_view *v, void *ctx)
|
||||
meow_ui_activity_push(ACT_SEARCH); /* owner=ACT_SEARCH */
|
||||
g_tb = ncm_topbar_create("搜索", 1, NULL);
|
||||
if (!g_st.search_query[0]) ncm_strlcpy(g_st.search_query, "", sizeof g_st.search_query);
|
||||
/* 中文搜索:IME 切 MEOW_IME_PINYIN(拼音候选上屏 UTF-8 到 g_st.search_query,经 on_change 实时回写;
|
||||
* 键盘「拼/英」键可切回 ASCII 输英文,「中」热区回拼音——英文零回归)。 */
|
||||
g_field = meow_textfield_create(field.x, field.y, field.w, field.h,
|
||||
"输入歌曲 / 歌手", g_st.search_query,
|
||||
sizeof g_st.search_query, MEOW_IME_ASCII);
|
||||
sizeof g_st.search_query, MEOW_IME_PINYIN);
|
||||
g_list = meow_listview_create(r_list.x, r_list.y, r_list.w, r_list.h, 0,
|
||||
sr_src2, NULL, MEOW_LIST_SEL_NONE);
|
||||
meow_listview_set_item_h(g_list, APP_ROW_H);
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
/* ui/settings.c — 设置页(ACT_SETTINGS)。
|
||||
*
|
||||
* 行项:音质(Hi-res/较高/标准,默认 Hi-res)+ 缓存上限(1~100GB,默认 10GB)+ 退出登录。
|
||||
* 设置持久化到 MeowData/dev.player.ncm/data(meow_config_*;缓存上限键 ncm.cache_limit_gb 与核心库
|
||||
* ncm_cache_load_setting 对齐)。音质/上限经 BottomSheet 选择;退出登录经 confirm 弹窗。
|
||||
* 空态/绘制用 custom 绘制物 + 手动行命中(仅 3 行,无需列表滚动)。
|
||||
* 行项:音质(Hi-res 默认,单曲不可用时 core ncm_api_song_url_auto 逐档降级)+ 缓存上限
|
||||
* (默认 10GB,上限 100GB,支持预设与「自定义」数字输入)+ 清理缓存(显示当前占用)+
|
||||
* 退出登录 + 关于/版本。设置持久化到 MeowData/dev.player.ncm/data(meow_config_*,与 core
|
||||
* ncm_store_read_cfg_int 同目录同键,ncm.cache_limit_gb/ncm.level)。超上限触发 ncm_cache LRU
|
||||
* 淘汰(写缓存前后 + 手动调低上限时,见 player_manager.c/cover.c/appstate.c)。
|
||||
* 空态/绘制用 custom 绘制物 + 手动行命中(仅 5 行,无需列表滚动)。
|
||||
*/
|
||||
#include "../ncm_app_internal.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct { meow_view *view; int active; } settings_ctrl;
|
||||
static settings_ctrl g_sc;
|
||||
@@ -17,6 +20,9 @@ static meow_widget *g_tb;
|
||||
static ncm_rect_t r_cont;
|
||||
static int g_custom_id;
|
||||
static int g_row_h; /* 行高 */
|
||||
static uint64_t g_cache_used; /* 缓存占用字节(进设置页时算一次,清理后清零) */
|
||||
|
||||
#define SETTINGS_ROWS 5
|
||||
|
||||
static const char *level_name(int lv)
|
||||
{
|
||||
@@ -29,6 +35,30 @@ static const char *level_name(int lv)
|
||||
}
|
||||
}
|
||||
|
||||
/* 缓存占用字节 → 人类可读(整数运算,不用浮点/软浮点)。buf ≥ 24。 */
|
||||
static void fmt_bytes(uint64_t b, char *buf, size_t sz)
|
||||
{
|
||||
const uint64_t GB = 1024ULL * 1024 * 1024;
|
||||
const uint64_t MB = 1024ULL * 1024;
|
||||
if (b >= GB) {
|
||||
uint64_t g = b / GB, fr = (b % GB) / (GB / 10);
|
||||
snprintf(buf, sz, "%llu.%llu GB", (unsigned long long)g, (unsigned long long)fr);
|
||||
} else if (b >= MB) {
|
||||
uint64_t m = b / MB, fr = (b % MB) / (MB / 10);
|
||||
snprintf(buf, sz, "%llu.%llu MB", (unsigned long long)m, (unsigned long long)fr);
|
||||
} else if (b >= 1024) {
|
||||
snprintf(buf, sz, "%llu KB", (unsigned long long)(b / 1024));
|
||||
} else {
|
||||
snprintf(buf, sz, "%llu B", (unsigned long long)b);
|
||||
}
|
||||
}
|
||||
|
||||
/* 刷新缓存占用缓存(进设置页/清理后调用)。 */
|
||||
static void refresh_cache_used(void)
|
||||
{
|
||||
g_cache_used = ncm_cache_total_bytes(&g_st.ctx.cache);
|
||||
}
|
||||
|
||||
static void settings_draw(void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
@@ -40,18 +70,23 @@ static void settings_draw(void *ctx)
|
||||
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");
|
||||
char usetxt[24];
|
||||
fmt_bytes(g_cache_used, usetxt, sizeof usetxt);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
/* 行:音质 / 缓存上限 / 清理缓存 / 退出登录 / 关于。 */
|
||||
struct { const char *title; char sub[160]; int red; } rows[SETTINGS_ROWS];
|
||||
rows[0].title = "音质"; rows[0].red = 0;
|
||||
rows[1].title = "缓存上限"; rows[1].red = 0;
|
||||
rows[2].title = "清理缓存"; rows[2].red = 0;
|
||||
rows[3].title = "退出登录"; rows[3].red = 1;
|
||||
rows[4].title = "关于网易云"; rows[4].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 · 上限 100", g_st.cache_limit_gb);
|
||||
snprintf(rows[2].sub, sizeof rows[2].sub, "占用: %s", usetxt);
|
||||
snprintf(rows[3].sub, sizeof rows[3].sub, "%s", g_st.nickname[0] ? g_st.nickname : "退出");
|
||||
snprintf(rows[4].sub, sizeof rows[4].sub, "%s", "v0.1 · MeowOS");
|
||||
|
||||
for (int i = 0; i < SETTINGS_ROWS; 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;
|
||||
@@ -61,9 +96,9 @@ static void settings_draw(void *ctx)
|
||||
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);
|
||||
/* 品牌红页脚小字。 */
|
||||
int ny = y + SETTINGS_ROWS * (g_row_h + 8) + 4;
|
||||
meow_ui_text(x, ny, "解锁完整音质请登录网易云黑胶会员", 20, th->onSurfaceVariant);
|
||||
meow_ui_text(x, ny + 24, "品牌红 © MeowOS · NCM", 20, pa->red);
|
||||
}
|
||||
|
||||
@@ -71,13 +106,36 @@ static int row_at(int x, int y)
|
||||
{
|
||||
(void)x;
|
||||
int r = r_cont.y + 16;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < SETTINGS_ROWS; i++) {
|
||||
int yy = r + i * (g_row_h + 8);
|
||||
if (y >= yy && y <= yy + g_row_h) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 「自定义」缓存上限:INPUT 弹窗 + NUM IME(数字键盘输入 1~100,越界钳制)。
|
||||
* 回车/ESC 关闭后 d->ime.buf 已同步(dialog INPUT 关闭时回写 ime_orig),读回即可。 */
|
||||
static int input_cache_limit_gb(void)
|
||||
{
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof buf, "%d", g_st.cache_limit_gb);
|
||||
meow_ime_ctx ime;
|
||||
memset(&ime, 0, sizeof ime);
|
||||
ime.mode = MEOW_IME_NUM;
|
||||
ime.maxlen = sizeof buf;
|
||||
ncm_strlcpy(ime.buf, buf, sizeof ime.buf);
|
||||
|
||||
meow_dialog *d = meow_dialog_create(MEOW_DLG_INPUT, "缓存上限 (GB, 1~100)");
|
||||
meow_dialog_set_ime(d, &ime);
|
||||
meow_dialog_show(d);
|
||||
meow_dialog_free(d);
|
||||
|
||||
int gb = atoi(ime.buf);
|
||||
if (gb < 1) gb = 1;
|
||||
if (gb > 100) gb = 100;
|
||||
return gb;
|
||||
}
|
||||
|
||||
int settings_is_active(void) { return g_sc.active; }
|
||||
|
||||
int settings_on_tap(int x, int y)
|
||||
@@ -91,38 +149,60 @@ int settings_on_tap(int x, int y)
|
||||
int row = row_at(x, y);
|
||||
if (row < 0) return 0;
|
||||
|
||||
if (row == 0) { /* 音质 */
|
||||
if (row == 0) { /* 音质:Top 档选择,单曲不可用时 core 逐档降级 */
|
||||
static const char *lbls[5] = { "Hi-Res", "Lossless", "Exhigh", "Higher", "Standard" };
|
||||
static const int lvls[5] = { NCM_LEVEL_HIRES, NCM_LEVEL_LOSSLESS,
|
||||
NCM_LEVEL_EXHIGH, NCM_LEVEL_HIGHER, NCM_LEVEL_STANDARD };
|
||||
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");
|
||||
for (int i = 0; i < 5; i++) meow_sheet_add_item(s, lbls[i]);
|
||||
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;
|
||||
if (op >= 0 && op < 5) {
|
||||
g_st.level = lvls[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 == 1) { /* 缓存上限 */
|
||||
if (row == 1) { /* 缓存上限:预设 + 自定义(数字输入 1~100) */
|
||||
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]);
|
||||
meow_sheet_add_item(s, "自定义...");
|
||||
int op = meow_sheet_open(s);
|
||||
meow_sheet_free(s);
|
||||
if (op >= 0 && op < 8) {
|
||||
g_st.cache_limit_gb = gbs[op];
|
||||
if (op == 8) g_st.cache_limit_gb = input_cache_limit_gb();
|
||||
else if (op >= 0 && op < 8) g_st.cache_limit_gb = gbs[op];
|
||||
if (op >= 0) {
|
||||
player_manager_set_quality(g_st.level, g_st.cache_limit_gb);
|
||||
appstate_save_settings();
|
||||
appstate_save_settings(); /* 内含手动调低上限后的 LRU 淘汰 */
|
||||
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (row == 2) { /* 退出登录 */
|
||||
if (row == 2) { /* 清理缓存 */
|
||||
meow_dialog *d = meow_dialog_create(MEOW_DLG_CONFIRM, "Clear Cache?");
|
||||
char msg[160];
|
||||
char usetxt[24];
|
||||
fmt_bytes(g_cache_used, usetxt, sizeof usetxt);
|
||||
snprintf(msg, sizeof msg, "将删除全部缓存 (%s)。\n已播放的歌曲需重新下载。", usetxt);
|
||||
const char *msgs[1];
|
||||
msgs[0] = msg;
|
||||
meow_dialog_set_items(d, msgs, 1, -1);
|
||||
int yes = meow_dialog_confirm(d);
|
||||
meow_dialog_free(d);
|
||||
if (yes) {
|
||||
ncm_cache_clear(&g_st.ctx.cache);
|
||||
refresh_cache_used();
|
||||
ncm_snack("缓存已清理");
|
||||
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (row == 3) { /* 退出登录 */
|
||||
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.";
|
||||
@@ -135,8 +215,8 @@ int settings_on_tap(int x, int y)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (row == 3) { /* 关于(v1 占位) */
|
||||
ncm_snack("关于页开发中");
|
||||
if (row == 4) { /* 关于 */
|
||||
ncm_snack("网易云音乐 v0.1 · MeowOS 移植版");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -154,6 +234,7 @@ void settings_open(void)
|
||||
|
||||
void settings_reload(void)
|
||||
{
|
||||
refresh_cache_used();
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_ui_set_page_bg(th->bg);
|
||||
meow_ui_paint_full();
|
||||
|
||||
Reference in new issue
Block a user