Files
AskaEth 2826c1e905 feat: M6.40批 — 阅读器UI改版(书架list·grid+空态骨架+界面设置+App设置页+header·footer+背景黑默认) + 系统级音量翻页映射 + P1 MD3(select面板28/对话框按钮胶囊) + MeowPanel写端点护栏 + SDK公开仓·文档同步
- reader: app.c/shelf.c/reader.c 全套UI; 新增 reader_conf(reader.conf)/settings(App设置页)/reader_ipc(音量翻页IPC响应); app.json加volume_page
- launcher: keys.c meow_volpage_*门 + util.c app_id→fd映射(HELLO记fd/断开清) + app_launcher.c 前台三处接线(进入sink/按键路径volpage_key/离开clear) → 前台声明volume_page的App音量键→MSG_KEY_PAGEUP/PAGEDOWN翻页(非阅读器前台音量照旧)
- sdk: meow_select.c 底部面板28 + meow_ui_dialog.c 对话框按钮胶囊(h/2) + test_meow_ui_select.c修采样
- meowpanel: /api/net/policy·/api/power/policy 加防自锁+policy默认关两条护栏(panel.policy_enabled默认0)
- 文档: MEOW_APP_SDK.md §12.6 + meowpanel README端点表

0warning;宿主测试全过;二进制留批量批次未入库
2026-09-04 18:13:18 +08:00

251 lines
9.1 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}