/* app/settings.c — 全局设置页(C,书架顶栏「设置」入口 → 独立页 ACT_SETTINGS)。 * * 配置项(MVP,见 READER_UI_DESIGN §4.3):默认字号/默认行距/默认背景(黑白米黄)/主题(亮暗跟随 * 全局)/书架布局(list-grid)/页头条/页底条。全部存 App 私有 reader.conf(rdconf),不进系统 * settings.conf。UI:meow_topappbar(返回+标题) + meow_listview 行(每行 label + 当前值尾部箭头, * 点行开 meow_dialog(SELECT) 单选;值即改即存)。 */ #include "../reader_internal.h" #include "progress.h" #include "reader_conf.h" #include #include #include #define SET_X 0 #define SET_W 480 #define SET_APPBAR_H 56 static meow_widget *g_appbar; static meow_widget *g_list; static int g_active; /* 行项枚举(顺序 = 列表顺序) */ enum { ITEM_FS = 0, ITEM_LS, ITEM_BG, ITEM_THEME, ITEM_SHELF, ITEM_HDR, ITEM_FTR, ITEM_COUNT }; static const char *const g_labels[ITEM_COUNT] = { "默认字号", "默认行距", "默认背景", "主题", "书架布局", "页头条", "页底条" }; static void settings_redraw(void); static const char *bg_label(const rdconf *c) { if (!c) return "黑"; if (!strcmp(c->bg, "white")) return "白"; if (!strcmp(c->bg, "sepia")) return "米黄"; if (!strcmp(c->bg, "auto")) return "自动"; return "黑"; } static const char *theme_label(const rdconf *c) { if (!c) return "跟随系统"; if (!strcmp(c->theme, "light")) return "日"; if (!strcmp(c->theme, "dark")) return "夜"; return "跟随系统"; } static const char *shelf_label(const rdconf *c) { if (!c) return "列表"; return (!strcmp(c->shelf, "grid")) ? "宫格" : "列表"; } /* 当前行值(subtitle)。 */ static void item_value(int item, char *out, int outsz) { const rdconf *c = rdconf_get(); if (!c) { snprintf(out, (size_t)outsz, "-"); return; } switch (item) { case ITEM_FS: snprintf(out, (size_t)outsz, "%d", c->fs); break; case ITEM_LS: snprintf(out, (size_t)outsz, "%d", c->ls); break; case ITEM_BG: snprintf(out, (size_t)outsz, "%s", bg_label(c)); break; case ITEM_THEME: snprintf(out, (size_t)outsz, "%s", theme_label(c)); break; case ITEM_SHELF: snprintf(out, (size_t)outsz, "%s", shelf_label(c)); break; case ITEM_HDR: snprintf(out, (size_t)outsz, "%s", c->header ? "显示" : "隐藏"); break; case ITEM_FTR: snprintf(out, (size_t)outsz, "%s", c->footer ? "显示" : "隐藏"); break; default: snprintf(out, (size_t)outsz, "-"); break; } } /* ---- listview 行源(src2:每次绘制读最新 rdconf 值) ---- */ static int settings_src(int idx, meow_list_item *out, void *ctx) { (void)ctx; if (idx < 0 || idx >= ITEM_COUNT) return 0; static char valbuf[16]; item_value(idx, valbuf, sizeof valbuf); out->icon_kind = MEOW_LIST_ICON_NONE; out->title = g_labels[idx]; out->subtitle = valbuf; out->right = MEOW_LIST_RIGHT_ARROW; out->on = 0; out->enabled = 1; return 1; } /* 由当前值求 SELECT 对话框的选中下标。 */ static int idx_for(int item, const char *const *opts, int n, const rdconf *c) { if (item == ITEM_FS) { for (int i = 0; i < n; i++) if (atoi(opts[i]) == c->fs) return i; return 0; } if (item == ITEM_LS) { for (int i = 0; i < n; i++) if (atoi(opts[i]) == c->ls) return i; return 0; } if (item == ITEM_BG) { for (int i = 0; i < n; i++) if (!strcmp(opts[i], bg_label(c))) return i; return 0; } if (item == ITEM_THEME) { for (int i = 0; i < n; i++) if (!strcmp(opts[i], theme_label(c))) return i; return 0; } if (item == ITEM_SHELF) { for (int i = 0; i < n; i++) if (!strcmp(opts[i], shelf_label(c))) return i; return 0; } if (item == ITEM_HDR) return c->header ? 0 : 1; if (item == ITEM_FTR) return c->footer ? 0 : 1; return 0; } /* 选完整数数组项(字号/行距);写回 rdconf。 */ static void apply_fs_ls(int item, const char *sel) { rdconf *c = rdconf_get(); if (!c) return; int v = atoi(sel); if (item == ITEM_FS) { if (v >= 14 && v <= 40) c->fs = v; } else if (item == ITEM_LS) { if (v >= 100 && v <= 200) c->ls = v; } rdconf_persist(); } static void apply_choice(int item, int sel) { rdconf *c = rdconf_get(); if (!c) return; switch (item) { case ITEM_BG: if (sel < 0) sel = 0; if (sel > 3) sel = 3; if (sel == 0) strcpy(c->bg, "black"); else if (sel == 1) strcpy(c->bg, "white"); else if (sel == 2) strcpy(c->bg, "sepia"); else strcpy(c->bg, "auto"); break; case ITEM_THEME: if (sel < 0) sel = 0; if (sel > 2) sel = 2; if (sel == 0) strcpy(c->theme, "auto"); else if (sel == 1) strcpy(c->theme, "light"); else strcpy(c->theme, "dark"); break; case ITEM_SHELF: shelf_set_view(sel ? 1 : 0); /* 写 rdconf + 重建书架 */ return; case ITEM_HDR: c->header = sel ? 0 : 1; break; /* 0 显示 1 隐藏 */ case ITEM_FTR: c->footer = sel ? 0 : 1; break; default: return; } rdconf_persist(); } /* 打开一个 SELECT 对话框并应用。 */ static void settings_open_choice(int item) { const rdconf *c = rdconf_get(); if (!c) return; const char *title = g_labels[item]; meow_dialog *d = meow_dialog_create(MEOW_DLG_SELECT, title); if (!d) return; const char *opts[16]; int n = 0; int cur = 0; static char numopts[16][8]; if (item == ITEM_FS) { for (int v = 14; v <= 40; v += 2) { snprintf(numopts[n], sizeof numopts[n], "%d", v); opts[n] = numopts[n]; n++; } } else if (item == ITEM_LS) { for (int v = 100; v <= 200; v += 10) { snprintf(numopts[n], sizeof numopts[n], "%d", v); opts[n] = numopts[n]; n++; } } else if (item == ITEM_BG) { static const char *b[4] = { "黑", "白", "米黄", "自动" }; for (int i = 0; i < 4; i++) opts[n++] = b[i]; } else if (item == ITEM_THEME) { static const char *t[3] = { "跟随系统", "日", "夜" }; for (int i = 0; i < 3; i++) opts[n++] = t[i]; } else if (item == ITEM_SHELF) { static const char *s[2] = { "列表", "宫格" }; for (int i = 0; i < 2; i++) opts[n++] = s[i]; } else if (item == ITEM_HDR || item == ITEM_FTR) { static const char *h[2] = { "显示", "隐藏" }; for (int i = 0; i < 2; i++) opts[n++] = h[i]; } cur = idx_for(item, opts, n, c); meow_dialog_set_items(d, opts, n, cur); meow_dialog_show(d); int res = meow_dialog_result(d); meow_dialog_free(d); if (res < 0 || res >= n) return; /* 取消 */ if (item == ITEM_FS || item == ITEM_LS) apply_fs_ls(item, opts[res]); else apply_choice(item, res); settings_redraw(); } static void settings_redraw(void) { const meow_theme *th = meow_theme_get(); meow_ui_clear(th->bg); if (g_appbar) meow_widget_draw(g_appbar); if (g_list) meow_widget_draw(g_list); meow_ui_flush(); } /* view 生命周期 */ static void settings_on_create(meow_view *v, void *ctx) { (void)v; (void)ctx; } static void settings_on_enter(meow_view *v, void *ctx) { (void)v; (void)ctx; g_active = 1; int sbh = meow_ui_statusbar_height(); if (g_appbar) { meow_widget_free(g_appbar); g_appbar = NULL; } g_appbar = meow_topappbar_create(0, sbh, 480, SET_APPBAR_H, "阅读设置"); if (g_appbar) meow_topappbar_set_back(g_appbar, 1); if (g_list) { meow_widget_free(g_list); g_list = NULL; } int ly = sbh + SET_APPBAR_H + 8; g_list = meow_listview_create(SET_X, ly, SET_W, 720 - ly, ITEM_COUNT, settings_src, NULL, MEOW_LIST_SEL_SINGLE); if (g_list) { meow_listview_set_container(g_list, 1); meow_listview_set_item_tier(g_list, 2); } settings_redraw(); } static void settings_on_leave(meow_view *v, void *ctx) { (void)v; (void)ctx; g_active = 0; } static void settings_on_destroy(meow_view *v, void *ctx) { (void)v; (void)ctx; if (g_list) { meow_widget_free(g_list); g_list = NULL; } if (g_appbar) { meow_widget_free(g_appbar); g_appbar = NULL; } } meow_view *settings_view_create(void) { static meow_view_hooks hooks = { settings_on_create, settings_on_enter, settings_on_leave, settings_on_destroy, NULL, NULL, NULL }; return meow_view_create(ACT_SETTINGS, MEOW_VIEW_ACTIVITY, &hooks, NULL); } int settings_is_active(void) { return g_active; } void settings_reload(void) { if (g_active) settings_redraw(); } int settings_on_tap(int x, int y) { /* 顶栏返回 */ if (g_appbar) { int bi = meow_topappbar_hit(g_appbar, x, y); if (bi == MEOW_TOPBAR_BACK) { meow_view_page_pop(); return 1; } if (bi >= 0) return 1; } if (g_list) { int idx = meow_listview_tap_at(g_list, x, y); if (idx >= 0 && idx < ITEM_COUNT) { settings_open_choice(idx); return 1; } if (idx >= 0) { settings_redraw(); return 1; } } return 0; }