diff --git a/meow_ui_theme.c b/meow_ui_theme.c index 5a0e997..8ad3a35 100644 --- a/meow_ui_theme.c +++ b/meow_ui_theme.c @@ -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 行 */ } diff --git a/test_meow_ui_theme b/test_meow_ui_theme new file mode 100755 index 0000000..d24f89c Binary files /dev/null and b/test_meow_ui_theme differ diff --git a/test_meow_ui_theme.c b/test_meow_ui_theme.c new file mode 100644 index 0000000..6a11280 --- /dev/null +++ b/test_meow_ui_theme.c @@ -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 +#include +#include +#include + +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