M2.90: MIUIx 风格包落地 + ui.style 一键切换闭环
- meow_ui_theme.c: 为 MEOW_STYLE_MIUIX 实现整套 light/dark 配色(暖基面+小米橙 primary 0xF57200/0xFF8C4A+蓝 tertiary;圆角 20/卡片 28、间距 12、细描边 1),与 MD3 可感知差异 - display.background 随 style×mode 派生 MIUIx 色板(bg_surfaces 增 style 参;图片背景保持各 style 默认面) - meow_settings.c g_defs: ui.style 值域 'md3,miui'→'md3,miuix'(label Material You,MIUIx); apply_value SET_ENUM 对存量 'miui' 兼容映射为 miuix - settings_page s_commit: ui.style 分支 → meow_ui_set_style + paint_sb_right + meow_dirty_full 即时换肤 - font_theme s_ui_theme() 接 ui.style + MIUIx dark/light 表;main_menu 背景签名含 ui.style + 纯色底随 style 换族 - 宿主编译 -Wall -Wextra 0 warning;test_meow_ui_theme 四套 s×m 渲染全 PASS; test_meow_ui_widgets/test_sdk_fixes 全 PASS(MD3 行为一致) - 产物 md5: meow_ui.a a4231296 / launcher 9f97af1b(未部署走固件)/ hello e5527578 / files 993e41ca / ncm 6b9eeb0f(三 App 已 .new+mv 热更到卡, 设备 md5 已核一致) - 待后续: 状态栏图标集仍 MD3 一套(sb_icons.h 无 per-style 换集接口, MIUIx 仅换配色); App 进程 SDK 主题缓存 g_loaded 不随 settings 外部改写重读, 运行中 App 下次启动才换肤
This commit is contained in:
1 parent
f23720f934
commit
4e3f41a5a3
13 files changed
+243
-35
No files matched your search
+2
-1
@@ -877,7 +877,7 @@ void meow_theme_remove_listener(meow_theme_listener cb);
|
||||
|
||||
### 8.1 主题增强:顶级主题 `ui.style` × 明暗 `display.theme` × 背景 `display.background` [源:2026-08-30]
|
||||
|
||||
> ✅ **已实现(M2.70/0.7.65 起)**:`meow_ui_set_style`/`meow_ui_style`(`MEOW_STYLE_MD3/MIUIX`)、`meow_theme_get`(MD3 语义色板 + 风格参数 + 背景 surface/bg)、状态栏图标随 `ui.style` 换集(详见 §3.1「同一个状态栏」)。**MIUIx 当前仅 md3 生效**(MIUIx 深度细节留后续迭代);`meow_settings.c g_defs` 的 `miui` 值对齐为 `miuix` 待定案(见 §8.1.1 注)。
|
||||
> ✅ **已实现(M2.70/0.7.65 起 + MIUIx 完整包 M2.90/0.7.85)**:`meow_ui_set_style`/`meow_ui_style`(`MEOW_STYLE_MD3/MIUIX`)、`meow_theme_get`(MD3 语义色板 + 风格参数 + 背景 surface/bg)、状态栏图标随 `ui.style` 换集(详见 §3.1「同一个状态栏」)。**MIUIx 完整落定**:`meow_ui_theme.c` 为 `MEOW_STYLE_MIUIX` 实现整套 light/dark 配色(暖色基面 + 小米橙 primary + 蓝 tertiary 辅助、更大圆角 20/28、间距 12、细描边 1)+ `display.background` 按 style×mode 派生 MIUIx 色板(§8.1.1 矩阵);设置页 `ui.style` 一键切换即时生效。`meow_settings.c g_defs` 值域已由 `"md3,miui"` 对齐为 `"md3,miuix"`(label "Material You,MIUIx"),存量 `settings.conf`/备份里的 `"miui"` 兼容映射为 `miuix`(见 §8.1.1 注)。**状态栏图标集仍为 MD3 一套**(`sb_icons.h` 无 per-style 换集接口),MIUIx 仅换配色,见 §8.1.2 注。
|
||||
|
||||
> **一句话**:主题 = **顶级主题风格族(`ui.style`:MD3 / MIUIx)× 明暗(`display.theme`:dark / light)** 共同决定「整套配色 + 控件形态 + 背景底」。`display.background` **不再是独立全局维度**,而是「当前 style × 当前 mode」下**可选背景底选项集**——**可选集与默认随 style / mode 切换一起变**。**切明暗** → 同 style 的浅/深背景自动切换(修复"浅色不搭");**切 style** → 整套配色 + 背景换族(修复"背景仅对主页生效/只有深色")。本定案建立在 §5.8.4 的 MD3 语义色板之上(色板 = MD3 语义色;本节管 style×mode 选哪套 + 风格参数 + 背景源)。
|
||||
|
||||
@@ -919,6 +919,7 @@ const meow_theme *meow_theme_get(void);
|
||||
- **明暗联动**:`display.theme` / `display.theme_mode`(auto)变化仍走现有 `meow_theme_mode` + `meow_theme_listener`(§8 现状),仅「同一 style 下」的浅/深背景随切换自动换。
|
||||
- **命名对齐(与 `meow_ui.h` / launcher 现状)**:SDK **App 侧**结构体权威名 = **`meow_theme`**(既有 `meow_theme_get()`),**不是** `meow_ui_theme`;launcher 内部另有独立结构体 `meow_ui_theme_t`(`font_theme.c`,仅供状态栏/设置页,**与 App 侧无关、不暴露**)。故本节接口统一用 `meow_theme` / `meow_theme_get`;若实现期另起 `meow_ui_theme()`/`meow_ui_theme_t` 作同义别名亦可,视为与 `meow_theme` 等价(待定案)。
|
||||
- **状态栏图标随 `ui.style` 换集**:状态栏右侧图标集与风格族绑定(MD3=默认图标、MIUIx=另套基础);`meow_ui_set_style` 时框架**一并切换状态栏图标集**(触发全局刷新,见 §8.1.4),颜色随 `meow_theme`(style×mode)日/夜(详见 §3.1「同一个状态栏」正确闭环)。
|
||||
> **现状(M2.90 审计)**:图标集机制尚无 per-style 换集接口——`sb_icons.h` 只有一套 MD3 Material Symbols 位图(`meow_gfx_blit_icon(idx,…)` 按固定索引查 `sb_icons[]`),`meow_ui_set_style` 只改配色(颜色随 `meow_theme`),**不切换图标图形集**。MIUIx 深度图标(另套基础图形)需新增第二张图标表 + 按 `meow_ui_style()` 选择,**未实现**(留后续迭代;当前 MD3/MIUIx 共用同一图标图形、仅配色不同)。
|
||||
|
||||
#### 8.1.3 两套风格参数(草案值)
|
||||
|
||||
|
||||
+2
-2
@@ -44,7 +44,7 @@
|
||||
| | `font.class_other` | path | *(空=跟全局)* | 多字体:其他字符 所用字体 |
|
||||
| | `font.load_full` | int 0/1 | 0 | **加载大字库(M2.5x)**:1=**强制全系统用大字库(`gbk`≈20k 档)**,覆盖 app 未声明/覆盖默认 `font.file`;0=默认小字库(`common`≈4k),仅 app 声明 `fonts:"full"` 才大字库 [源:用户 2026-08-30] |
|
||||
| | `font.size.title` / `font.size.body` | int | 28/20 | 标题/正文字号 |
|
||||
| | `ui.style` | enum | `md3` | `md3`(默认,Material You)/`miuix`(MIUIx,**未实现**)——**顶级主题风格族**(定义整套配色风格/控件形态/圆角/间距/描边),对应 SDK `MEOW_STYLE_MD3`/`MEOW_STYLE_MIUIX`(MEOW_APP_SDK §8.1) |
|
||||
| | `ui.style` | enum | `md3` | `md3`(默认,Material You)/`miuix`(MIUIx,**M2.90 已实现**:完整 light/dark 配色+控件形态参数,见 MEOW_APP_SDK §8.1.3)——**顶级主题风格族**(定义整套配色风格/控件形态/圆角/间距/描边),对应 SDK `MEOW_STYLE_MD3`/`MEOW_STYLE_MIUIX`(MEOW_APP_SDK §8.1)。`g_defs` 值域 `"md3,miuix"`(label "Material You,MIUIx");存量 `"miui"` 兼容映射为 `miuix`。**状态栏图标集仍 MD3 一套**(无 per-style 换集接口,MIUIx 仅换配色) |
|
||||
| **通知** | `notify.center_enabled` | bool | true | 下拉通知中心开关 |
|
||||
| | `notify.app_quota` | int | 16 | 每应用通知配额 |
|
||||
| | `notify.categories` | int | 4 | 通知类型数上限(每 App,M2.53)[源:审计 2026-08-30]|
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
> 新增设置项时:在清单加一行 + 存储 schema + API 常量/枚举 + 实体 UI 控件 + web 面板控件(四端同步)。
|
||||
|
||||
> **主题三键语义(2026-08-30 定案,实现权威见 MEOW_APP_SDK §8.1)**:`ui.style`(MD3/MIUIx)=**顶级主题风格族**(定义整套配色/控件形态/圆角/描边);`display.theme`(dark/light)=该 style 下的**明暗**;`display.background` =**由「当前 style × 当前 mode」决定的背景底选项集**(非独立全局维度,可选集+默认随 style/mode 一起变)。三者关系:**切明暗 → 同 style 浅/深背景自动切换**(修复"浅色不搭");**切 style → 整套配色+背景换族**(修复"背景仅对主页生效/只有深色")——同时把 `display.background` 从"仅 launcher 主页/仅深色"改为"**全局背景源**"(状态栏/未声明背景 App/主页同步;App 显式声明背景则覆盖,App 声明优先)。`meow_ui_theme` 旧字段 `bg/fg/accent/dim` 保留映射到 MD3 语义色(§5.8.4)。**注**:`ui.style` 值域现状 `meow_settings.c g_defs` 为 `"md3,miui"`(label "Material You,MIUI");本定案将第二风格命名为 **`miuix`**(对应 `MEOW_STYLE_MIUIX`),实现时把 `g_defs` 的 `miui` 对齐为 `miuix`(待定案——MIUIx 未实现,当前仅 md3 生效)。
|
||||
> **主题三键语义(2026-08-30 定案,实现权威见 MEOW_APP_SDK §8.1)**:`ui.style`(MD3/MIUIx)=**顶级主题风格族**(定义整套配色/控件形态/圆角/描边);`display.theme`(dark/light)=该 style 下的**明暗**;`display.background` =**由「当前 style × 当前 mode」决定的背景底选项集**(非独立全局维度,可选集+默认随 style/mode 一起变)。三者关系:**切明暗 → 同 style 浅/深背景自动切换**(修复"浅色不搭");**切 style → 整套配色+背景换族**(修复"背景仅对主页生效/只有深色")——同时把 `display.background` 从"仅 launcher 主页/仅深色"改为"**全局背景源**"(状态栏/未声明背景 App/主页同步;App 显式声明背景则覆盖,App 声明优先)。`meow_ui_theme` 旧字段 `bg/fg/accent/dim` 保留映射到 MD3 语义色(§5.8.4)。**注**:`ui.style` 值域已由 `"md3,miui"` 对齐为 **`"md3,miuix"`**(label "Material You,MIUIx",对应 `MEOW_STYLE_MIUIX`);存量 `settings.conf`/备份里的 `"miui"` 在 `meow_settings.c apply_value`(SET_ENUM)里兼容映射为 `miuix`。**MIUIx 已实现**(M2.90):`meow_ui_theme.c` 为 `MEOW_STYLE_MIUIX` 提供整套 light/dark 配色+控件形态参数,`display.background` 按 style×mode 派生 MIUIx 色板;状态栏图标集仍为 MD3 一套(无 per-style 换集接口)。
|
||||
|
||||
## 2. 统一存储(单一数据源)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -746,6 +746,18 @@ static const meow_ui_theme_t meow_ui_theme_light = {
|
||||
0x4A4F5A, 0x9AA4C8, 0xB3261E, 0x2A5A8A
|
||||
};
|
||||
|
||||
/* MIUIx 族(§8.1.3 定案):暖色基面 + 小米橙 primary,供 launcher 主界面/设置页/通知中心/
|
||||
* 电源菜单(s_ui_theme())在 ui.style=miuix 时使用(与 SDK meow_theme MIUIx 同色系)。 */
|
||||
static const meow_ui_theme_t meow_ui_theme_miui_dark = {
|
||||
0x181310, 0x2A231C, 0x2E2620, 0xFF8C4A, 0xEDE5DC,
|
||||
0xAB9884, 0x4A3F35, 0xFFB4AB, 0xFF8C4A
|
||||
};
|
||||
|
||||
static const meow_ui_theme_t meow_ui_theme_miui_light = {
|
||||
0xF7F3EE, 0xE9DFD0, 0xF4EDE3, 0xF57200, 0x251A10,
|
||||
0x7D6A5A, 0xCBBFB2, 0xB3261E, 0xF57200
|
||||
};
|
||||
|
||||
/* 获取当前本地时刻的「当天第几分钟」(tm_hour*60+tm_min);供时段判定用 */
|
||||
static int s_now_min_of_day(void)
|
||||
{
|
||||
@@ -768,13 +780,18 @@ static int theme_should_dark(int now_min, int start, int end)
|
||||
return (now_min >= start || now_min < end) ? 1 : 0; /* 跨午夜 */
|
||||
}
|
||||
|
||||
/* 当前主题(dark/light 两套;接 display.theme_mode + display.theme)。
|
||||
/* 当前主题(style×明暗 两套;接 ui.style + display.theme_mode + display.theme)。
|
||||
* style(§8.1.1):ui.style 的 md3/miuix → 返回对应体系(MD3 tonal / MIUIx 暖色+橙)。
|
||||
* manual(mode==THEME_MODE_MANUAL):直读 display.theme(dark/light),保持原有行为。
|
||||
* auto(mode==THEME_MODE_AUTO):读深色时段两键 + 当前本地分钟;start==end 未配置 →
|
||||
* 回退 manual(读 display.theme,避免误判全天深色)。
|
||||
* 读取失败一律回退 dark。 */
|
||||
const meow_ui_theme_t *s_ui_theme(void)
|
||||
{
|
||||
int style = 0; /* ui.style:0=md3 / 1=miuix */
|
||||
if (meow_settings_get_enum("ui.style", &style) != 0) style = 0;
|
||||
const meow_ui_theme_t *light = (style == 1) ? &meow_ui_theme_miui_light : &meow_ui_theme_light;
|
||||
const meow_ui_theme_t *dark = (style == 1) ? &meow_ui_theme_miui_dark : &meow_ui_theme_dark;
|
||||
int mode = THEME_MODE_MANUAL;
|
||||
if (meow_settings_get_enum("display.theme_mode", &mode) != 0) mode = THEME_MODE_MANUAL;
|
||||
int v = 0; /* display.theme(manual)当前索引 */
|
||||
@@ -785,13 +802,13 @@ const meow_ui_theme_t *s_ui_theme(void)
|
||||
meow_settings_get_int("display.theme_dark_end", &end);
|
||||
int d = theme_should_dark(s_now_min_of_day(), start, end);
|
||||
if (d < 0) { /* 未配置:回退 manual(display.theme) */
|
||||
if (have_manual && v == 1) return &meow_ui_theme_light;
|
||||
return &meow_ui_theme_dark;
|
||||
if (have_manual && v == 1) return light;
|
||||
return dark;
|
||||
}
|
||||
return d ? &meow_ui_theme_dark : &meow_ui_theme_light;
|
||||
return d ? dark : light;
|
||||
}
|
||||
/* manual:照旧读 display.theme */
|
||||
if (have_manual && v == 1) return &meow_ui_theme_light;
|
||||
return &meow_ui_theme_dark;
|
||||
if (have_manual && v == 1) return light;
|
||||
return dark;
|
||||
}
|
||||
|
||||
@@ -214,15 +214,18 @@ static int menu_is_dark(const meow_ui_theme_t *th)
|
||||
return th ? (th->on_surface > 0x808080) : 1;
|
||||
}
|
||||
|
||||
/* 背景设置签名:display.background 串 + 叠加层强度 + 明暗。变化才重建缓存。 */
|
||||
/* 背景设置签名:display.background 串 + 叠加层强度 + 明暗 + ui.style(§8.1.1:背景底随
|
||||
* style×mode 换族)。变化才重建缓存——ui.style 切换必须入签名,否则切风格后主界面背景滞留旧族。 */
|
||||
static unsigned menu_bg_sig(const meow_ui_theme_t *th, int ov)
|
||||
{
|
||||
char bg[MEOW_SET_MAX_LEN];
|
||||
if (meow_settings_get_str("display.background", bg, sizeof bg) != 0) bg[0] = 0;
|
||||
int style = 0; if (meow_settings_get_enum("ui.style", &style) != 0) style = 0;
|
||||
unsigned h = 2166136261u;
|
||||
for (const char *p = bg; *p; p++) { h ^= (unsigned char)*p; h *= 16777619u; }
|
||||
h = h * 31u + (unsigned)ov;
|
||||
h = h * 31u + (unsigned)menu_is_dark(th);
|
||||
h = h * 31u + (unsigned)style;
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -291,11 +294,18 @@ static void menu_bg_prepare(const meow_ui_theme_t *th)
|
||||
menu_bg = th->surface;
|
||||
s_bg_img = menu_bg_load_img(bg);
|
||||
if (s_bg_img) s_bg_is_img = 1;
|
||||
} else { /* 纯色名(沿用既有 black/blue/green 语义) */
|
||||
} else { /* 纯色名(沿用既有 black/blue/green 语义;随 ui.style 换族) */
|
||||
int mstyle = 0; if (meow_settings_get_enum("ui.style", &mstyle) != 0) mstyle = 0;
|
||||
if (mstyle == MEOW_STYLE_MIUIX) { /* MIUIx 暖色族(与 SDK bg_surfaces 同色) */
|
||||
if (!strcmp(bg, "blue")) menu_bg = 0x101A2E;
|
||||
else if (!strcmp(bg, "green")) menu_bg = 0x14251A;
|
||||
else menu_bg = 0x181310; /* black(默认)=MIUIx 深色面 */
|
||||
} else { /* MD3(既有) */
|
||||
if (!strcmp(bg, "blue")) menu_bg = 0x0A1428; /* 深蓝 */
|
||||
else if (!strcmp(bg, "green")) menu_bg = 0x0A2814; /* 深绿 */
|
||||
else menu_bg = 0x000000; /* black(默认) */
|
||||
}
|
||||
}
|
||||
menu_bg_cache_build(th);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -118,7 +118,7 @@ static const meow_setting_def g_defs[] = {
|
||||
{ "font.load_full", SET_INT, "0", 0, 1, NULL, NULL },
|
||||
{ "font.size.title", SET_INT, "28", 0, 0, NULL, NULL },
|
||||
{ "font.size.body", SET_INT, "20", 0, 0, NULL, NULL },
|
||||
{ "ui.style", SET_ENUM, "md3", 0, 0, "md3,miui", "Material You,MIUI" },
|
||||
{ "ui.style", SET_ENUM, "md3", 0, 0, "md3,miuix", "Material You,MIUIx" },
|
||||
/* 通知 */
|
||||
{ "notify.center_enabled", SET_BOOL, "1", 0, 0, NULL, NULL },
|
||||
{ "notify.app_quota", SET_INT, "16", 0, 0, NULL, NULL },
|
||||
@@ -280,7 +280,13 @@ static void apply_value(meow_setting *s, const char *v)
|
||||
}
|
||||
case SET_ENUM: {
|
||||
int idx = enum_index_of_name_str(s->enum_names, v);
|
||||
if (idx < 0) {
|
||||
/* §8.1.1 注:旧版 ui.style 值域为 "md3,miui",本版对齐为 "md3,miuix"——存量
|
||||
* settings.conf / 备份里的 "miui" 应兼容映射为 "miuix"(而非 atoi 落到 md3=0)。 */
|
||||
if (!strcmp(s->key, "ui.style") && strcasecmp(v, "miui") == 0)
|
||||
idx = enum_index_of_name_str(s->enum_names, "miuix");
|
||||
if (idx < 0) idx = atoi(v); /* 向后兼容:数值索引 */
|
||||
}
|
||||
if (idx >= 0 && idx < enum_count_str(s->enum_names)) {
|
||||
const char *name = enum_at_str(s->enum_names, idx);
|
||||
if (name) strncpy(s->cur, name, sizeof s->cur - 1);
|
||||
|
||||
@@ -396,16 +396,35 @@ static void s_commit(const srow_t *r, int save_now)
|
||||
* 此前 SC_PRESET 只写 meow_settings,未应用 menu_bg → 黑/绿点了没用。 */
|
||||
else if (!strcmp(r->key, "display.background")) {
|
||||
char bg[16]; meow_settings_get_str(r->key, bg, sizeof bg);
|
||||
int mstyle = 0; if (meow_settings_get_enum("ui.style", &mstyle) != 0) mstyle = 0;
|
||||
if (mstyle == MEOW_STYLE_MIUIX) { /* MIUIx 暖色族(与 main_menu menu_bg_prepare 同色) */
|
||||
if (!strcmp(bg, "blue")) menu_bg = 0x101A2E;
|
||||
else if (!strcmp(bg, "green")) menu_bg = 0x14251A;
|
||||
else menu_bg = 0x181310;
|
||||
} else { /* MD3(既有) */
|
||||
if (!strcmp(bg, "blue")) menu_bg = 0x0A1428; /* 深蓝 */
|
||||
else if (!strcmp(bg, "green")) menu_bg = 0x0A2814; /* 深绿 */
|
||||
else menu_bg = 0x000000; /* black(默认) */
|
||||
/* 状态栏(menu_bg)与主界面背景都会用到 menu_bg;退出设置页回主界面时重绘即可。 */
|
||||
}
|
||||
/* menu_bg 仅主界面背景用(状态栏走 SDK meow_theme bg);退出设置页回主界面时经
|
||||
* menu_bg_sig(含 ui.style)重build 缓存。 */
|
||||
}
|
||||
/* M2.44 主题/mode 变更:状态栏文字/底色已接 s_ui_theme(),此处仅确保回主界面重绘。 */
|
||||
else if (!strcmp(r->key, "display.theme") || !strcmp(r->key, "display.theme_mode") ||
|
||||
!strcmp(r->key, "display.theme_dark_start") || !strcmp(r->key, "display.theme_dark_end")) {
|
||||
paint_sb_right(); /* 状态栏文字/底色随主题刷新(整条标脏) */
|
||||
}
|
||||
/* §8.1.2 一键切换闭环:ui.style(md3/miuix)顶级风格族切换 → 全系统即时换肤,无需重启。
|
||||
* meow_ui_set_style() 设 SDK g_style(launcher 同一进程内立即生效)+ 广播(App/状态栏下一帧换肤);
|
||||
* paint_sb_right() → sb_sdk_refresh() 逼 SDK 状态栏重读 settings(含 display.background 派生)跟手换肤;
|
||||
* meow_dirty_full() 让 launcher 主界面/设置页整帧重绘(s_ui_theme() 已接 ui.style → 换族配色)。
|
||||
* 主界面背景缓存经 menu_bg_sig(含 ui.style,见 main_menu.c)在回主界面时自动重建。 */
|
||||
else if (!strcmp(r->key, "ui.style")) {
|
||||
int st = 0; meow_settings_get_enum("ui.style", &st);
|
||||
meow_ui_set_style(st);
|
||||
paint_sb_right();
|
||||
meow_dirty_full();
|
||||
}
|
||||
/* M2.5x:字体文件(font.file)变更 → 立即 unload + reload(若已加载)。
|
||||
* 置 mfont_forced 跳过 meow_font_ensure 的 SDK 复用捷径,强制走
|
||||
* font.file → 卡默认 → 固件 链;全失败则回退 5x7 点阵(系统不死)。 */
|
||||
|
||||
@@ -86,8 +86,8 @@ static meow_theme_mode parse_manual(const char *v)
|
||||
return MEOW_THEME_DARK; /* "dark" / 未知 → 深色 */
|
||||
}
|
||||
|
||||
/* ---- 填充 4 张主题表的内建色板(草案值,MD3 默认;MIUIx 用 MD3 基础做占位)----
|
||||
* bg/surface 本函数填默认面;随后 apply_background() 按 display.background 覆写。 */
|
||||
/* ---- 填充 4 张主题表的内建色板(§8.1.3:MD3 / MIUIx 各一整套,light/dark 各一)----
|
||||
* bg/surface 本函数填默认面;随后 apply_background() 按 display.background 按 style 覆写。 */
|
||||
static void init_defaults(void)
|
||||
{
|
||||
meow_theme *t;
|
||||
@@ -126,26 +126,50 @@ static void init_defaults(void)
|
||||
t->shadow = 0x000000;
|
||||
t->radius = 12; t->radius_card = 16; t->spacing = 8; t->stroke_w = 0;
|
||||
|
||||
/* MIUIx = MD3 基础(占位),primary 更饱和、圆角/间距更大、细描边(§8.1.3 可感知差异)。
|
||||
* 先整表复制 MD3,再覆写差异字段。 */
|
||||
g_theme[MEOW_STYLE_MIUIX][0] = g_theme[MEOW_STYLE_MD3][0];
|
||||
g_theme[MEOW_STYLE_MIUIX][0].primary = 0x2A5AC8; g_theme[MEOW_STYLE_MIUIX][0].accent = 0x2A5AC8;
|
||||
g_theme[MEOW_STYLE_MIUIX][0].primaryContainer = 0xD6E1FA; g_theme[MEOW_STYLE_MIUIX][0].surfaceTint = 0x2A5AC8;
|
||||
g_theme[MEOW_STYLE_MIUIX][0].radius = 18; g_theme[MEOW_STYLE_MIUIX][0].radius_card = 24;
|
||||
g_theme[MEOW_STYLE_MIUIX][0].spacing = 10; g_theme[MEOW_STYLE_MIUIX][0].stroke_w = 1;
|
||||
/* MIUIx(§8.1.3 定案):完整独立色板(不复刻 MD3)——暖色基面 + 小米橙 primary +
|
||||
* 更大圆角/更松间距/细描边。观感:MIUI 系暖白(浅)/暖黑(深)、橙强调、蓝作 tertiary 辅助。
|
||||
* 与 MD3 保持可感知差异(圆角/描边/间距/主色),避免"切了没感觉"。 */
|
||||
t = &g_theme[MEOW_STYLE_MIUIX][0]; /* MIUIx light */
|
||||
t->bg = 0xF7F3EE; t->fg = 0x251A10; t->accent = 0xF57200; t->dim = 0x7D6A5A;
|
||||
t->font_title = 28; t->font_body = 20;
|
||||
t->primary = 0xF57200; t->onPrimary = 0xFFFFFF; t->primaryContainer = 0xFFE2CC; t->onPrimaryContainer = 0x5E2B00;
|
||||
t->secondary = 0x8A6F5C; t->onSecondary = 0xFFFFFF; t->secondaryContainer = 0xF0E2D6; t->onSecondaryContainer = 0x3A2A1C;
|
||||
t->tertiary = 0x3A5FA8; t->onTertiary = 0xFFFFFF; t->tertiaryContainer = 0xD6E2FA; t->onTertiaryContainer = 0x1E2C4A;
|
||||
t->surface = 0xF7F3EE; t->onSurface = 0x251A10; t->surfaceVariant = 0xEDE2D7; t->onSurfaceVariant = 0x7D6A5A;
|
||||
t->surfaceContainerLowest = 0xFFFFFF; t->surfaceContainerLow = 0xFAF6F1; t->surfaceContainer = 0xF4EDE3;
|
||||
t->surfaceContainerHigh = 0xEEE6D9; t->surfaceContainerHighest = 0xE9DFD0;
|
||||
t->surfaceDim = 0xDFD3C4; t->surfaceBright = 0xFFFBF6; t->surfaceTint = 0xF57200;
|
||||
t->outline = 0x85746A; t->outlineVariant = 0xCBBFB2; t->scrim = 0x000000;
|
||||
t->error = 0xB3261E; t->onError = 0xFFFFFF; t->errorContainer = 0xFFDAD6; t->onErrorContainer = 0x410002;
|
||||
t->inverseSurface = 0x322A22; t->inverseOnSurface = 0xF6EFE6; t->inversePrimary = 0xFFB58C;
|
||||
t->shadow = 0x000000;
|
||||
t->radius = 20; t->radius_card = 28; t->spacing = 12; t->stroke_w = 1;
|
||||
|
||||
g_theme[MEOW_STYLE_MIUIX][1] = g_theme[MEOW_STYLE_MD3][1];
|
||||
g_theme[MEOW_STYLE_MIUIX][1].primary = 0x4A7AE8; g_theme[MEOW_STYLE_MIUIX][1].accent = 0x4A7AE8;
|
||||
g_theme[MEOW_STYLE_MIUIX][1].primaryContainer = 0x2A3A6C; g_theme[MEOW_STYLE_MIUIX][1].surfaceTint = 0x4A7AE8;
|
||||
g_theme[MEOW_STYLE_MIUIX][1].radius = 18; g_theme[MEOW_STYLE_MIUIX][1].radius_card = 24;
|
||||
g_theme[MEOW_STYLE_MIUIX][1].spacing = 10; g_theme[MEOW_STYLE_MIUIX][1].stroke_w = 1;
|
||||
t = &g_theme[MEOW_STYLE_MIUIX][1]; /* MIUIx dark */
|
||||
t->bg = 0x181310; t->fg = 0xEDE5DC; t->accent = 0xFF8C4A; t->dim = 0xAB9884;
|
||||
t->font_title = 28; t->font_body = 20;
|
||||
t->primary = 0xFF8C4A; t->onPrimary = 0x241100; t->primaryContainer = 0x5E2B00; t->onPrimaryContainer = 0xFFDDC8;
|
||||
t->secondary = 0xC9B5A2; t->onSecondary = 0x34261A; t->secondaryContainer = 0x4A3A2C; t->onSecondaryContainer = 0xEADCCD;
|
||||
t->tertiary = 0x9EB8EC; t->onTertiary = 0x1E2C4A; t->tertiaryContainer = 0x2E4370; t->onTertiaryContainer = 0xD6E2FA;
|
||||
t->surface = 0x181310; t->onSurface = 0xEDE5DC; t->surfaceVariant = 0x332B25; t->onSurfaceVariant = 0xAB9884;
|
||||
t->surfaceContainerLowest = 0x120E0B; t->surfaceContainerLow = 0x211B16; t->surfaceContainer = 0x262019;
|
||||
t->surfaceContainerHigh = 0x302922; t->surfaceContainerHighest = 0x3A3229;
|
||||
t->surfaceDim = 0x120E0B; t->surfaceBright = 0x3F372E; t->surfaceTint = 0xFF8C4A;
|
||||
t->outline = 0x8A7B6B; t->outlineVariant = 0x4A3F35; t->scrim = 0x000000;
|
||||
t->error = 0xFFB4AB; t->onError = 0x690005; t->errorContainer = 0x93000A; t->onErrorContainer = 0xFFDAD6;
|
||||
t->inverseSurface = 0xEDE5DC; t->inverseOnSurface = 0x322A22; t->inversePrimary = 0xF57200;
|
||||
t->shadow = 0x000000;
|
||||
t->radius = 20; t->radius_card = 28; t->spacing = 12; t->stroke_w = 1;
|
||||
}
|
||||
|
||||
/* ---- 背景全局源(§8.1.4):display.background 是「style×mode 下背景底选项集」的当前值,
|
||||
* 决定主题 surface/bg(深浅成对)。按名映射 (dark_surface, light_surface):
|
||||
* dark 默认/black → 0x141824 / light 默认/white → 0xE8EAF0(既有默认,保证 App 无回归);
|
||||
* dark blue → 0x0A1428 / light blue → 0xE5EEFB;green → 0x0A2814 / 0xE6F6E8。
|
||||
* 图片路径(*.png/jpg/jpeg):背景图即观感 → 用默认 surface(本批草案)。 */
|
||||
* 决定主题 surface/bg(深浅成对),且随 style 换族(§8.1.1 矩阵 MD3/MIUIx 各一套)。
|
||||
* 按名映射 (style, dark_surface, light_surface):
|
||||
* MD3:dark 默认/black → 0x141824 / light 默认/white → 0xE8EAF0(既有默认,App 无回归);
|
||||
* blue → 0x0A1428 / 0xE5EEFB;green → 0x0A2814 / 0xE6F6E8。
|
||||
* MIUIx:dark 默认/black → 0x181310 / light 默认/white → 0xF7F3EE(暖色族);
|
||||
* blue → 0x101A2E / 0xEDEAF5;green → 0x14251A / 0xEFF3EC。
|
||||
* 图片路径(*.png/jpg/jpeg):背景图即观感 → 用该 style 的默认 surface(本批草案)。 */
|
||||
static int bg_is_image(const char *v)
|
||||
{
|
||||
while (*v == ' ' || *v == '\t') v++;
|
||||
@@ -153,11 +177,22 @@ static int bg_is_image(const char *v)
|
||||
if (!dot || dot == v) return 0;
|
||||
return !strncasecmp(dot, ".png", 4) || !strncasecmp(dot, ".jpg", 4) || !strncasecmp(dot, ".jpeg", 5);
|
||||
}
|
||||
static void bg_surfaces(const char *v, uint32_t *dark, uint32_t *light)
|
||||
static void bg_surfaces(int style, const char *v, uint32_t *dark, uint32_t *light)
|
||||
{
|
||||
*dark = 0x141824; *light = 0xE8EAF0; /* 默认(未知/图片/空)→ 既有默认面 */
|
||||
/* 默认面(未知/图片/空)→ 各 style 的默认 surface(MIUIx 暖色族) */
|
||||
if (style == MEOW_STYLE_MIUIX) { *dark = 0x181310; *light = 0xF7F3EE; }
|
||||
else { *dark = 0x141824; *light = 0xE8EAF0; }
|
||||
while (*v == ' ' || *v == '\t') v++;
|
||||
if (bg_is_image(v)) return; /* 背景图:用默认 */
|
||||
if (bg_is_image(v)) return; /* 背景图:用该 style 默认面 */
|
||||
if (style == MEOW_STYLE_MIUIX) {
|
||||
if (!strncmp(v, "blue", 4)) { *dark = 0x101A2E; *light = 0xEDEAF5; return; }
|
||||
if (!strncmp(v, "green", 5)) { *dark = 0x14251A; *light = 0xEFF3EC; return; }
|
||||
if (!strncmp(v, "white", 5)) { *dark = 0x181310; *light = 0xFFFFFF; return; }
|
||||
if (!strncmp(v, "lightblue", 9)) { *dark = 0x101A2E; *light = 0xEDEAF5; return; }
|
||||
if (!strncmp(v, "lightgreen", 10)) { *dark = 0x14251A; *light = 0xEFF3EC; return; }
|
||||
return; /* black / 其它未知 → MIUIx 默认暖面 */
|
||||
}
|
||||
/* MD3(既有语义,保持不变) */
|
||||
if (!strncmp(v, "blue", 4)) { *dark = 0x0A1428; *light = 0xE5EEFB; return; }
|
||||
if (!strncmp(v, "green", 5)) { *dark = 0x0A2814; *light = 0xE6F6E8; return; }
|
||||
if (!strncmp(v, "white", 5)) { *dark = 0x141824; *light = 0xFFFFFF; return; }
|
||||
@@ -168,8 +203,10 @@ static void bg_surfaces(const char *v, uint32_t *dark, uint32_t *light)
|
||||
static void apply_background(void)
|
||||
{
|
||||
uint32_t ds, ls;
|
||||
bg_surfaces(g_background, &ds, &ls);
|
||||
for (int s = 0; s < 2; s++) {
|
||||
/* 图片背景:底色即观感 → 保持 init_defaults 各 style 的默认 surface(不覆写) */
|
||||
if (bg_is_image(g_background)) continue;
|
||||
bg_surfaces(s, g_background, &ds, &ls);
|
||||
g_theme[s][0].bg = g_theme[s][0].surface = ls; /* light 行 */
|
||||
g_theme[s][1].bg = g_theme[s][1].surface = ds; /* dark 行 */
|
||||
}
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,118 @@
|
||||
/* test_meow_ui_theme.c — 宿主验证 §8.1 顶级主题:MD3 × MIUIx 各 light/dark 四套配色 & 控件形态。
|
||||
*
|
||||
* 用法:build 后跑 4 次(每次一个 style×mode 组合,dump 一张 PPM):
|
||||
* for s in 0 1; do for m in 0 1; do
|
||||
* MEOW_UI_DUMP=/tmp/meow_t_${s}_${m}.ppm ./test_meow_ui_theme $s $m
|
||||
* done; done
|
||||
* s: 0=MEOW_STYLE_MD3 1=MEOW_STYLE_MIUIX; m: 0=light 1=dark
|
||||
*
|
||||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
|
||||
* gcc -o test_meow_ui_theme test_meow_ui_theme.c test_host_stub.c \
|
||||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||||
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
||||
* meow_ui_draw.c meow_ui_view.c meow_ui_img.c meow_notify_ipc.c stb_image.c \
|
||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
||||
*
|
||||
* 验证要点:四套主题 primary/radius 各不相同(MD3 蓝 / MIUIx 橙 + 更大圆角),
|
||||
* 同 style 下 light/dark 的 surface 成对切换;图形用 MEOW_UI_DUMP 导出 PPM 目检。
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include "meow_ui_internal.h" /* meow_gfx_fill_round / meow_ui_back_buf(真实像素断言) */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
static meow_widget *g_w[16];
|
||||
static int g_wn = 0;
|
||||
|
||||
static void render_sample(const meow_theme *th)
|
||||
{
|
||||
int W = meow_ui_width(), H = meow_ui_height();
|
||||
meow_ui_clear(th->bg);
|
||||
|
||||
/* 卡片:surfaceContainerLow 底 + outlineVariant 圆角描边(radius_card) */
|
||||
meow_gfx_fill_round(20, 56, W - 40, 132, th->radius_card, th->surfaceContainerLow);
|
||||
meow_ui_draw_rect_round(20, 56, W - 40, 132, th->radius_card, th->outlineVariant);
|
||||
meow_ui_text(36, 74, "THEME SAMPLE", th->font_title, th->onSurface);
|
||||
meow_ui_text(36, 116, "surfaceContainerLow/outline", th->font_body, th->onSurfaceVariant);
|
||||
char info[64];
|
||||
snprintf(info, sizeof info, "primary=0x%06X radius=%d", th->primary, th->radius);
|
||||
meow_ui_text(36, 148, info, th->font_body, th->onSurfaceVariant);
|
||||
|
||||
/* 文字行:onSurface / onSurfaceVariant 对比 */
|
||||
meow_ui_text(20, 204, "onSurface text", th->font_body, th->onSurface);
|
||||
meow_ui_text(20, 232, "onSurfaceVariant dim", th->font_body, th->onSurfaceVariant);
|
||||
meow_ui_text(20, 260, "primary accent", th->font_body, th->primary);
|
||||
|
||||
/* 控件(真实控件走主题 radius/spacing/stroke 形态,meow_widget_draw 内取 meow_theme_get) */
|
||||
for (int i = 0; i < g_wn; i++) meow_widget_draw(g_w[i]);
|
||||
|
||||
/* 滑杆:surfaceContainer 轨道 + primary 已选段(spacing/radius 来自主题) */
|
||||
meow_gfx_fill_round(20, H - 90, W - 40, 8, 4, th->surfaceContainerHigh);
|
||||
meow_gfx_fill_round(20, H - 90, (W - 40) * 2 / 3, 8, 4, th->primary);
|
||||
|
||||
/* 开关(开):primary 圆钮(radius=控件圆角) */
|
||||
meow_gfx_fill_round(W - 90, H - 150, 52, 30, 15, th->primary);
|
||||
meow_gfx_fill_circle(W - 48, H - 135, 11, th->onPrimary);
|
||||
|
||||
/* 错误色 sample */
|
||||
meow_gfx_fill_round(20, H - 180, 120, 30, th->radius, th->errorContainer);
|
||||
meow_ui_text(36, H - 176, "error", th->font_body, th->onErrorContainer);
|
||||
|
||||
(void)H;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int style = argc > 1 ? atoi(argv[1]) : 0; /* 0=MD3 1=MIUIx */
|
||||
int dark = argc > 2 ? atoi(argv[2]) : 1; /* 0=light 1=dark */
|
||||
if (argc >= 2 && !strcmp(argv[1], "usage")) {
|
||||
fprintf(stderr, "usage: %s <style 0|1> <dark 0|1>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!getenv("MEOW_UI_DUMP")) setenv("MEOW_UI_DUMP", "/tmp/meow_theme.ppm", 1);
|
||||
meow_ui_opts o = { 480, 720 };
|
||||
assert(meow_ui_init(&o) == 0);
|
||||
assert(meow_ui_width() == 480 && meow_ui_height() == 720);
|
||||
|
||||
/* 控件样张(复刻 test_meow_ui_widgets 的五型按钮,验证控件形态随主题) */
|
||||
g_w[g_wn++] = meow_btn_create_variant(20, 290, 200, 52, "FILLED", MEOW_BTN_FILLED);
|
||||
g_w[g_wn++] = meow_btn_create_variant(20, 352, 200, 52, "TONAL", MEOW_BTN_TONAL);
|
||||
g_w[g_wn++] = meow_btn_create_variant(20, 414, 200, 52, "OUTLINED",MEOW_BTN_OUTLINED);
|
||||
g_w[g_wn++] = meow_btn_create_variant(20, 476, 200, 52, "TEXT", MEOW_BTN_TEXT);
|
||||
g_w[g_wn++] = meow_btn_create_variant(20, 538, 200, 52, "ELEVATED",MEOW_BTN_ELEVATED);
|
||||
|
||||
/* 切到目标 style×mode,再取主题渲染。
|
||||
* 先 set_mode(触发首次 load_settings,g_loaded 锁 1)再 set_style(设置后不再被 reload 覆盖)——
|
||||
* 宿主无 settings.conf,若先 set_style 再 set_mode,后者会 load_settings 把 g_style 重读回 MD3。 */
|
||||
meow_theme_set_mode(dark ? MEOW_THEME_DARK : MEOW_THEME_LIGHT);
|
||||
meow_ui_set_style(style);
|
||||
const meow_theme *th = meow_theme_get();
|
||||
|
||||
/* 断言:样式可感知差异(primary 不同、圆角更大、间距/描边不同)——直接约束主题包正确性 */
|
||||
if (style == MEOW_STYLE_MD3) {
|
||||
assert(th->primary == (dark ? 0x4A6A9A : 0x2A5A8A)); /* MD3 蓝 */
|
||||
assert(th->radius_card == 16 && th->radius == 12 && th->spacing == 8 && th->stroke_w == 0);
|
||||
} else {
|
||||
assert(th->primary == (dark ? 0xFF8C4A : 0xF57200)); /* MIUIx 橙 */
|
||||
assert(th->radius_card == 28 && th->radius == 20 && th->spacing == 12 && th->stroke_w == 1);
|
||||
/* 与 MD3 同明暗对比:primary 必须不同(避免"切了没感觉") */
|
||||
assert(th->primary != (dark ? 0x4A6A9A : 0x2A5A8A));
|
||||
}
|
||||
/* surface/bg 为「该 style 的派生背景」(宿主无 settings.conf → 默认面) */
|
||||
if (dark) assert(th->surface == (style == MEOW_STYLE_MIUIX ? 0x181310 : 0x141824));
|
||||
else assert(th->surface == (style == MEOW_STYLE_MIUIX ? 0xF7F3EE : 0xE8EAF0));
|
||||
|
||||
render_sample(th);
|
||||
meow_ui_flush();
|
||||
|
||||
printf("PASS theme[%d][%d] style=%s dark=%d bg=0x%06X primary=0x%06X radius=%d card=%d spacing=%d stroke=%d\n",
|
||||
style, dark, style ? "MIUIx" : "MD3", dark, th->bg, th->primary,
|
||||
th->radius, th->radius_card, th->spacing, th->stroke_w);
|
||||
printf("PASS dump: MEOW_UI_DUMP=%s\n", getenv("MEOW_UI_DUMP") ? getenv("MEOW_UI_DUMP") : "(none)");
|
||||
|
||||
meow_ui_shutdown();
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Reference in new issue
Block a user