feat(sdk): 控件库第一批通用基座(agent-150) + launcher/hello 重链
- meow_widget 基座收敛(尾部追加 Options+状态机位+自定义回调+布局子项), W_CUSTOM/W_LAYOUT/W_SPACER - meow_theme 尾部追加 MD3 语义色+风格参数(旧字段 bg/fg/accent/dim 映射兼容), g_theme[2][2](style×mode×明暗), display.background 背景源深浅成对, 图片路径回退 - meow_widget_opts(Modifier式, 不改create签名)/MEOW_WS_*状态机(enabled阻断hit,pressed)/meow_widget_custom_create(render/hit_cb)/meow_row/column/box+arrange(算绝对坐标兼容) - meow_ui_set_style/style/meow_theme_get 完整主题(MD3默认/MIUIx占位) - meow_ui.a=29c5078d hello=24116e6b(向后兼容外观零变化) launcher重链=626546f9;build_sdk 0warning; 宿主 test_sdk_controls T1-T10 PASS + test_sb_statusbar ALL PASS - 未做(第二批/后续): Material子集/实时按压刷新扩展/物理键焦点移动运行时(留草案)
This commit is contained in:
1 parent
3d6de43410
commit
26074e47ac
3 files changed
+527
-49
No files matched your search
@@ -264,13 +264,37 @@ unsigned long long meow_ui_frames_presented(void);
|
||||
/* ============================================================ */
|
||||
typedef enum { MEOW_THEME_LIGHT, MEOW_THEME_DARK, MEOW_THEME_AUTO } meow_theme_mode;
|
||||
|
||||
/* 主题(§5.8.4 + §8.1.3)。既有字段 bg/fg/accent/dim 保留(兼容映射,见下表),
|
||||
* 其后追加 MD3 语义色板 + 风格参数段(扩展兼容——既有代码读旧字段不受影响)。
|
||||
* 映射:bg↔surface、fg↔onSurface、accent↔primary、dim↔onSurfaceVariant。
|
||||
* 「当前 style×mode」完整主题 = meow_theme_get()(MD3 默认;MIUIx 用 MD3 基础/占位)。 */
|
||||
typedef struct meow_theme {
|
||||
uint32_t bg; /* 背景色 */
|
||||
uint32_t fg; /* 前景/正文 */
|
||||
uint32_t accent; /* 强调色(按钮/选中) */
|
||||
uint32_t dim; /* 次要文字 */
|
||||
/* —— 既有字段(兼容保留;映射见上)—— */
|
||||
uint32_t bg; /* 背景色 = surface */
|
||||
uint32_t fg; /* 前景/正文 = onSurface */
|
||||
uint32_t accent; /* 强调色(按钮/选中)= primary */
|
||||
uint32_t dim; /* 次要文字 = onSurfaceVariant */
|
||||
int font_title; /* 28 */
|
||||
int font_body; /* 20 */
|
||||
|
||||
/* —— MD3 语义色板(§5.8.4;日/夜各一组,AUTO 按 meow_theme_mode 切换)—— */
|
||||
uint32_t primary, onPrimary, primaryContainer, onPrimaryContainer;
|
||||
uint32_t secondary, onSecondary, secondaryContainer, onSecondaryContainer;
|
||||
uint32_t tertiary, onTertiary, tertiaryContainer, onTertiaryContainer;
|
||||
uint32_t surface, onSurface, surfaceVariant, onSurfaceVariant;
|
||||
uint32_t surfaceContainerLowest, surfaceContainerLow, surfaceContainer,
|
||||
surfaceContainerHigh, surfaceContainerHighest;
|
||||
uint32_t surfaceDim, surfaceBright, surfaceTint;
|
||||
uint32_t outline, outlineVariant, scrim;
|
||||
uint32_t error, onError, errorContainer, onErrorContainer;
|
||||
uint32_t inverseSurface, inverseOnSurface, inversePrimary;
|
||||
uint32_t shadow; /* 仅状态层/描边近似用;无 GPU 不做投影(§5.8.7) */
|
||||
|
||||
/* —— 风格参数段(§8.1.3;MD3 默认 / MIUIx 加大圆角+描边+间距)—— */
|
||||
int radius; /* 控件默认圆角(按钮等,px) */
|
||||
int radius_card; /* 卡片/容器默认圆角(px) */
|
||||
int spacing; /* 布局/间距步进(px) */
|
||||
int stroke_w; /* 默认细描边宽(0=无边框,MD3;MIUIx=1) */
|
||||
} meow_theme;
|
||||
|
||||
const meow_theme *meow_theme_get(void);
|
||||
@@ -295,6 +319,90 @@ meow_theme meow_theme_defaults(meow_theme_mode m);
|
||||
void meow_ui_set_style(int style); /* 设顶级主题风格族;越界 clamp 到合法值;同值忽略 */
|
||||
int meow_ui_style(void); /* 当前 style(默认 MEOW_STYLE_MD3) */
|
||||
|
||||
/* ============================================================ */
|
||||
/* §5.8 控件层升级(第一批=通用基座):Options / 状态机 / 自定义画布 / 布局 */
|
||||
/* ============================================================ */
|
||||
|
||||
/* ---- §5.8.2 Modifier 式通用 Options(轻量结构体,非链式 DSL)----
|
||||
* 经 meow_widget_set_opts 一次性挂载,作用于任意控件(标准/自定义)。全部控件共用。
|
||||
* 哨兵语义:bg/fg==0=用控件/主题默认;stroke==0=无描边;stroke_w=描边宽;
|
||||
* radius==-1=控件默认(如按钮 10),>=0=指定圆角(0=直角);pad/margin=0=无;
|
||||
* disabled/selected=1=置位(立即反映到状态机位)。 */
|
||||
typedef struct meow_widget_opts {
|
||||
uint32_t bg; /* 背景色(0=控件/主题默认) */
|
||||
uint32_t fg; /* 前景/文字(0=主题 onSurface) */
|
||||
uint32_t stroke; /* 描边色(0=无描边) */
|
||||
int stroke_w; /* 描边宽(px) */
|
||||
int radius; /* 圆角(px;-1=控件默认,如按钮 10) */
|
||||
int pad_x; /* 内边距(水平,px) */
|
||||
int pad_y; /* 内边距(垂直,px) */
|
||||
int margin_x; /* 外边距(水平,px;布局用) */
|
||||
int margin_y; /* 外边距(垂直,px;布局用) */
|
||||
int disabled; /* 1=禁用(视觉变暗 + 不响应命中,见状态机 §5.8.3) */
|
||||
int selected; /* 1=选中(列表高亮/勾选,见状态机 §5.8.3) */
|
||||
} meow_widget_opts;
|
||||
|
||||
/* 挂载:作用于任意控件(标准/自定义)。w 内部浅拷这份 opts。
|
||||
* 返回 0=成功;NULL/无效=负值(不销毁 w)。 */
|
||||
int meow_widget_set_opts(meow_widget *w, const meow_widget_opts *opts);
|
||||
/* 取回当前 opts(浅拷到 out;0=成功)。自定义画布用它读通用修饰。 */
|
||||
int meow_widget_opts_get(const meow_widget *w, meow_widget_opts *out);
|
||||
|
||||
/* ---- §5.8.3 统一状态机(enabled / selected / focused / pressed)----
|
||||
* 标准/自定义控件共用;visual 层由 SDK/App 按状态位叠。enabled=0 阻断 hit/pressed。 */
|
||||
#define MEOW_WS_ENABLED (1u<<0)
|
||||
#define MEOW_WS_SELECTED (1u<<1)
|
||||
#define MEOW_WS_FOCUSED (1u<<2)
|
||||
#define MEOW_WS_PRESSED (1u<<3)
|
||||
|
||||
/* 设/查状态(置位/清位,其它位不动)。on=1 置位 / 0 清位。 */
|
||||
void meow_widget_set_state(meow_widget *w, unsigned bits, int on);
|
||||
unsigned meow_widget_state(const meow_widget *w);
|
||||
|
||||
/* 便捷:禁用/启用、选中/取消(单态等效于 set_state)。 */
|
||||
int meow_widget_enabled(const meow_widget *w);
|
||||
void meow_widget_set_enabled(meow_widget *w, int on);
|
||||
int meow_widget_selected(const meow_widget *w);
|
||||
void meow_widget_set_selected(meow_widget *w, int on);
|
||||
|
||||
/* 一键叠状态层(自定义画布可选):把 pressed/focused/disabled/selected 的视觉层
|
||||
* 叠到 w 的矩形上(半透明状态层 + 焦点描边)。标准控件由 SDK 自动叠,无需调用;
|
||||
* 自定义画布在 render_cb 里画完内容后可调它(App 也可据 meow_widget_state 自行叠)。 */
|
||||
void meow_widget_draw_state_layer(meow_widget *w, const meow_theme *th);
|
||||
|
||||
/* ---- §5.8.1 自定义画布(App 提供 render/hit 回调;同一个 meow_widget)----
|
||||
* render:SDK 在 meow_widget_draw 时调用,th=当前生效主题(MD3 语义色板);
|
||||
* w 的 x/y/w/h 已就位;App 用 §3/§4 绘制 API 自绘(往 back_buf 画)。
|
||||
* hit :命中判定,App 自定义(如圆形/区域外镂空);返回 1=命中。 */
|
||||
typedef void (*meow_widget_render_fn)(meow_widget *w, const meow_theme *th, void *ctx);
|
||||
typedef int (*meow_widget_hit_fn)(meow_widget *w, int x, int y, void *ctx);
|
||||
|
||||
/* 创建:自定义画布(x,y,w,h = 绝对坐标,同既有控件)。
|
||||
* 生命周期/Activity 归属:创建时绑定当前 active Activity(同 meow_btn_create,§6.1 ①);
|
||||
* 命中走 hit_cb(先过 owner==active + enabled 双闸门)。 */
|
||||
meow_widget *meow_widget_custom_create(int x, int y, int w, int h,
|
||||
meow_widget_render_fn render_cb,
|
||||
meow_widget_hit_fn hit_cb,
|
||||
void *ctx);
|
||||
|
||||
/* ---- §5.8.5 可组合布局(Row / Column / Box + Spacer)----
|
||||
* 布局容器 = meow_widget(W_LAYOUT);arrange 把子控件算成绝对坐标(写进各 child 的
|
||||
* x/y/w/h)→ 与现有绝对定位完全兼容(draw/hit/flush_region/owner 闸门照常)。
|
||||
* 本批先支持固定尺寸子项(w/h 均已知);弹性(w/h==0)为后续增强。 */
|
||||
enum { MEOW_LAYOUT_ROW = 0, MEOW_LAYOUT_COLUMN = 1, MEOW_LAYOUT_BOX = 2 };
|
||||
|
||||
meow_widget *meow_row_create(int x, int y, int w, int h, int spacing);
|
||||
meow_widget *meow_column_create(int x, int y, int w, int h, int spacing);
|
||||
meow_widget *meow_box_create(int x, int y, int w, int h);
|
||||
|
||||
/* 添加子控件(子可为普通控件/自定义/另一布局/Spacer)。返回 0=成功;无效=负值。 */
|
||||
int meow_layout_add(meow_widget *layout, meow_widget *child);
|
||||
/* 计算子控件绝对坐标(写入各 child 的 x,y,w/h;嵌套布局递归);此后照常 draw/hit。 */
|
||||
void meow_layout_arrange(meow_widget *layout);
|
||||
|
||||
/* 固定尺寸空白(也作撑余量;不可见,hit 恒 0)。 */
|
||||
meow_widget *meow_layout_spacer(int w, int h);
|
||||
|
||||
/* ============================================================ */
|
||||
/* §9 应用日志(统一日志 API,M2.51 最小可用版) */
|
||||
/* ============================================================ */
|
||||
|
||||
+121
-14
@@ -14,17 +14,17 @@
|
||||
#include "meow_ui_internal.h" /* meow_theme_reload() 定义(供 launcher 逼重读 settings) */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MEOW_SETTINGS_FILE "/usr/data/meow/settings.conf"
|
||||
|
||||
static meow_theme g_light = {
|
||||
0xE8EAF0, 0x1A1A2A, 0x2A5A8A, 0x4A4F5A, 28, 20
|
||||
};
|
||||
static meow_theme g_dark = {
|
||||
0x141824, 0xFFFFFF, 0x4A6A9A, 0x9AA4C8, 28, 20
|
||||
};
|
||||
/* ---- 主题表(§5.8.4 + §8.1.3):[style][dark] ———
|
||||
* 0=MEOW_STYLE_MD3, 1=MEOW_STYLE_MIUIX;dark 0=light, 1=dark。
|
||||
* 旧字段 bg/fg/accent/dim = surface/onSurface/primary/onSurfaceVariant(映射见 meow_ui.h)。
|
||||
* bg/surface 随后由「背景全局源」(display.background,§8.1.4)按 style×mode 覆写。 */
|
||||
static meow_theme g_theme[2][2];
|
||||
|
||||
static meow_theme_mode g_mode = MEOW_THEME_DARK; /* 配置生效的判定模式(默认深色,与 launcher 默认一致) */
|
||||
static meow_theme_mode g_manual = MEOW_THEME_DARK; /* display.theme 解析出的手动深浅;auto 未配置时段时回退用之 */
|
||||
@@ -32,6 +32,7 @@ static int g_dark_start = 0; /* 深色时段开始(分
|
||||
static int g_dark_end = 0; /* 深色时段结束(分钟 0-1439) */
|
||||
static int g_loaded = 0;
|
||||
static int g_style = MEOW_STYLE_MD3; /* 顶级主题风格族(§8.1.2):默认 MD3 */
|
||||
static char g_background[64] = ""; /* display.background(black/blue/green/white/… 或图片路径) */
|
||||
|
||||
static meow_theme_listener g_listeners[8];
|
||||
static int g_nlisteners = 0;
|
||||
@@ -85,6 +86,95 @@ static meow_theme_mode parse_manual(const char *v)
|
||||
return MEOW_THEME_DARK; /* "dark" / 未知 → 深色 */
|
||||
}
|
||||
|
||||
/* ---- 填充 4 张主题表的内建色板(草案值,MD3 默认;MIUIx 用 MD3 基础做占位)----
|
||||
* bg/surface 本函数填默认面;随后 apply_background() 按 display.background 覆写。 */
|
||||
static void init_defaults(void)
|
||||
{
|
||||
meow_theme *t;
|
||||
|
||||
/* MD3 light */
|
||||
t = &g_theme[MEOW_STYLE_MD3][0];
|
||||
t->bg = 0xE8EAF0; t->fg = 0x1A1A2A; t->accent = 0x2A5A8A; t->dim = 0x4A4F5A;
|
||||
t->font_title = 28; t->font_body = 20;
|
||||
t->primary = 0x2A5A8A; t->onPrimary = 0xFFFFFF; t->primaryContainer = 0xC9E2F5; t->onPrimaryContainer = 0x0A2B46;
|
||||
t->secondary = 0x53626E; t->onSecondary = 0xFFFFFF; t->secondaryContainer = 0xD6E5F2; t->onSecondaryContainer = 0x101E28;
|
||||
t->tertiary = 0x635B78; t->onTertiary = 0xFFFFFF; t->tertiaryContainer = 0xE9DEFB; t->onTertiaryContainer = 0x1E1734;
|
||||
t->surface = 0xE8EAF0; t->onSurface = 0x1A1A2A; t->surfaceVariant = 0xDDE1EA; t->onSurfaceVariant = 0x4A4F5A;
|
||||
t->surfaceContainerLowest = 0xFFFFFF; t->surfaceContainerLow = 0xF2F3F7; t->surfaceContainer = 0xECEEF3;
|
||||
t->surfaceContainerHigh = 0xE6E8EE; t->surfaceContainerHighest = 0xE1E3E9;
|
||||
t->surfaceDim = 0xD8DAE0; t->surfaceBright = 0xFFFFFF; t->surfaceTint = 0x2A5A8A;
|
||||
t->outline = 0x74777F; t->outlineVariant = 0xC4C6D0; t->scrim = 0x000000;
|
||||
t->error = 0xBA1A1A; t->onError = 0xFFFFFF; t->errorContainer = 0xFFDAD6; t->onErrorContainer = 0x410002;
|
||||
t->inverseSurface = 0x2F3036; t->inverseOnSurface = 0xF0F0F5; t->inversePrimary = 0xA6C8EE;
|
||||
t->shadow = 0x000000;
|
||||
t->radius = 12; t->radius_card = 16; t->spacing = 8; t->stroke_w = 0;
|
||||
|
||||
/* MD3 dark */
|
||||
t = &g_theme[MEOW_STYLE_MD3][1];
|
||||
t->bg = 0x141824; t->fg = 0xFFFFFF; t->accent = 0x4A6A9A; t->dim = 0x9AA4C8;
|
||||
t->font_title = 28; t->font_body = 20;
|
||||
t->primary = 0x4A6A9A; t->onPrimary = 0xFFFFFF; t->primaryContainer = 0x2A3A5C; t->onPrimaryContainer = 0xD5E4F5;
|
||||
t->secondary = 0xB8C6D2; t->onSecondary = 0x27323B; t->secondaryContainer = 0x3A4650; t->onSecondaryContainer = 0xD6E5F2;
|
||||
t->tertiary = 0xCDC2E6; t->onTertiary = 0x372C52; t->tertiaryContainer = 0x4E4269; t->onTertiaryContainer = 0xE9DEFB;
|
||||
t->surface = 0x141824; t->onSurface = 0xFFFFFF; t->surfaceVariant = 0x2A2F3C; t->onSurfaceVariant = 0x9AA4C8;
|
||||
t->surfaceContainerLowest = 0x0F121C; t->surfaceContainerLow = 0x1C2030; t->surfaceContainer = 0x20253A;
|
||||
t->surfaceContainerHigh = 0x262C44; t->surfaceContainerHighest = 0x2C334E;
|
||||
t->surfaceDim = 0x12151E; t->surfaceBright = 0x3A3E50; t->surfaceTint = 0x4A6A9A;
|
||||
t->outline = 0x8B93A3; t->outlineVariant = 0x3E4450; t->scrim = 0x000000;
|
||||
t->error = 0xFFB4AB; t->onError = 0x690005; t->errorContainer = 0x93000A; t->onErrorContainer = 0xFFDAD6;
|
||||
t->inverseSurface = 0xF0F0F5; t->inverseOnSurface = 0x2F3036; t->inversePrimary = 0xA6C8EE;
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* ---- 背景全局源(§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(本批草案)。 */
|
||||
static int bg_is_image(const char *v)
|
||||
{
|
||||
while (*v == ' ' || *v == '\t') v++;
|
||||
const char *dot = strrchr(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)
|
||||
{
|
||||
*dark = 0x141824; *light = 0xE8EAF0; /* 默认(未知/图片/空)→ 既有默认面 */
|
||||
while (*v == ' ' || *v == '\t') v++;
|
||||
if (bg_is_image(v)) return; /* 背景图:用默认 */
|
||||
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; }
|
||||
if (!strncmp(v, "lightblue", 9)) { *dark = 0x0A1428; *light = 0xE5EEFB; return; }
|
||||
if (!strncmp(v, "lightgreen", 10)) { *dark = 0x0A2814; *light = 0xE6F6E8; return; }
|
||||
/* black / 其它未知 → 默认面 */
|
||||
}
|
||||
static void apply_background(void)
|
||||
{
|
||||
uint32_t ds, ls;
|
||||
bg_surfaces(g_background, &ds, &ls);
|
||||
for (int s = 0; s < 2; s++) {
|
||||
g_theme[s][0].bg = g_theme[s][0].surface = ls; /* light 行 */
|
||||
g_theme[s][1].bg = g_theme[s][1].surface = ds; /* dark 行 */
|
||||
}
|
||||
}
|
||||
|
||||
static void load_settings(void)
|
||||
{
|
||||
char buf[64];
|
||||
@@ -117,6 +207,16 @@ static void load_settings(void)
|
||||
while (*v == ' ' || *v == '\t') v++;
|
||||
if (!strncmp(v, "miuix", 5) || !strncmp(v, "miui", 4)) g_style = MEOW_STYLE_MIUIX;
|
||||
}
|
||||
|
||||
/* 背景全局源(§8.1.4):display.background 为 style×mode 下背景底选项集的当前值。
|
||||
* 读取缺失/未知 → ""(默认面)。 */
|
||||
g_background[0] = 0;
|
||||
if (get_setting_str("display.background", g_background, sizeof g_background) != 0)
|
||||
g_background[0] = 0;
|
||||
|
||||
/* 载入/覆写 4 张主题表(色板 + style×mode 背景 surface/bg) */
|
||||
init_defaults();
|
||||
apply_background();
|
||||
}
|
||||
|
||||
/* 当前本地时刻的「当天第几分钟」(tm_hour*60+tm_min);供时段判定用(与 launcher s_now_min_of_day 一致)。 */
|
||||
@@ -155,13 +255,21 @@ static void broadcast(void)
|
||||
if (g_listeners[i]) g_listeners[i](g_mode);
|
||||
}
|
||||
|
||||
/* 当前「style×mode」主题:style=0/1(MD3/MIUIX),dark=0(light)/1(dark)。 */
|
||||
static const meow_theme *theme_current(int style, int dark)
|
||||
{
|
||||
if (style < MEOW_STYLE_MD3) style = MEOW_STYLE_MD3;
|
||||
if (style > MEOW_STYLE_MIUIX) style = MEOW_STYLE_MIUIX;
|
||||
return &g_theme[style][dark ? 1 : 0];
|
||||
}
|
||||
|
||||
const meow_theme *meow_theme_get(void)
|
||||
{
|
||||
if (!g_loaded) {
|
||||
load_settings();
|
||||
g_loaded = 1;
|
||||
}
|
||||
return resolve_mode(g_mode) == MEOW_THEME_DARK ? &g_dark : &g_light;
|
||||
return theme_current(g_style, resolve_mode(g_mode) == MEOW_THEME_DARK);
|
||||
}
|
||||
|
||||
meow_theme_mode meow_theme_get_mode(void)
|
||||
@@ -189,11 +297,9 @@ void meow_theme_reload(void)
|
||||
|
||||
meow_theme meow_theme_defaults(meow_theme_mode m)
|
||||
{
|
||||
switch (resolve_mode(m)) {
|
||||
case MEOW_THEME_DARK: return g_dark;
|
||||
case MEOW_THEME_LIGHT:
|
||||
default: return g_light;
|
||||
}
|
||||
if (!g_loaded) { load_settings(); g_loaded = 1; }
|
||||
/* 脱机取内置板:返回 MD3(默认)style 下解析出的模式表。 */
|
||||
return g_theme[MEOW_STYLE_MD3][resolve_mode(m) == MEOW_THEME_DARK ? 1 : 0];
|
||||
}
|
||||
|
||||
/* ---- §8.1.2 顶级主题风格族(minimal)----
|
||||
@@ -213,8 +319,9 @@ void meow_ui_set_style(int style)
|
||||
if (style > MEOW_STYLE_MIUIX) style = MEOW_STYLE_MIUIX;
|
||||
if (style == g_style) return;
|
||||
g_style = style;
|
||||
/* 触发下一帧按新 style 重绘:清 settings 缓存(下次 get 重读)不必要(style 已记录),
|
||||
* 仅广播主题 listener(沿用 theme 广播机制,让控件/状态栏下一帧用新 style 重绘)。 */
|
||||
/* 触发下一帧按新 style 重绘(§8.1.2):g_theme[s][*] 是独立表(不随 g_style 重算),
|
||||
* g_style 记录后 meow_theme_get() 立即可取新 style×mode 表;只需广播主题 listener,
|
||||
* 让 App/状态栏/控件下一帧用新 style 重绘/换图标集。不清 settings 缓存(避免覆盖程序化 style)。 */
|
||||
broadcast();
|
||||
}
|
||||
|
||||
|
||||
+294
-31
@@ -9,7 +9,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3 };
|
||||
enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3,
|
||||
W_CUSTOM = 4, W_LAYOUT = 5, W_SPACER = 6 };
|
||||
|
||||
/* 布局子项数组上限(固定尺寸子项先做;超出静默拒收)。 */
|
||||
#define MEOW_LAYOUT_MAX_CHILD 32
|
||||
|
||||
/* 按钮「按住」(长按)判定阈值(ms):超过即进入更强态(更深状态层)。
|
||||
* 视觉区分「短按按下」与「按住」;触发语义不变(on_tap 仍按抬手判定)。 */
|
||||
@@ -30,14 +34,105 @@ struct meow_widget {
|
||||
int x, y, w, h;
|
||||
int owner; /* §6.1 ①:创建时绑定的 Activity;命中只在 owner==active 才生效 */
|
||||
char label[128];
|
||||
int pressed; /* 按钮按下视觉态 */
|
||||
int pressed; /* 按钮按下视觉态(遗留字面;状态机用 MEOW_WS_PRESSED 位) */
|
||||
int min, max, val; /* 滑条 */
|
||||
int on; /* 开关 */
|
||||
int count, sel; /* 列表 */
|
||||
meow_list_source src;
|
||||
void *ctx;
|
||||
|
||||
/* §5.8 batch1:统一状态机(§5.8.3)+ 通用 Options(§5.8.2)+ 自定义回调(§5.8.1)+ 布局(§5.8.5) */
|
||||
unsigned state; /* MEOW_WS_* 位掩码(默认 MEOW_WS_ENABLED) */
|
||||
meow_widget_opts opts; /* 通用修饰(浅拷;opts_used==0 时按控件内建默认) */
|
||||
int opts_used; /* 1=set_opts 显式挂载过 */
|
||||
meow_widget_render_fn render_cb; /* W_CUSTOM:自绘回调 */
|
||||
meow_widget_hit_fn hit_cb; /* W_CUSTOM:自定义命中回调 */
|
||||
int kind; /* W_LAYOUT:MEOW_LAYOUT_ROW/COLUMN/BOX */
|
||||
int spacing; /* W_LAYOUT:项间间距 */
|
||||
meow_widget *children[MEOW_LAYOUT_MAX_CHILD]; /* W_LAYOUT:子控件 */
|
||||
int nchildren;
|
||||
};
|
||||
|
||||
/* ---- §5.8 batch1 内部辅助:opts 取值(opts_used==0 时按控件内建默认)----
|
||||
* 注意:必须先定义 struct meow_widget,才能访问其成员(C 要求类型完整)。 */
|
||||
static uint32_t widget_bg(const meow_widget *w, const meow_theme *th, uint32_t def)
|
||||
{ (void)th; return (w->opts_used && w->opts.bg) ? w->opts.bg : def; }
|
||||
static uint32_t widget_fg(const meow_widget *w, const meow_theme *th, uint32_t def)
|
||||
{ (void)th; return (w->opts_used && w->opts.fg) ? w->opts.fg : def; }
|
||||
static int widget_radius(const meow_widget *w, int def)
|
||||
{ return (w->opts_used && w->opts.radius >= 0) ? w->opts.radius : def; }
|
||||
static int widget_pady(const meow_widget *w)
|
||||
{ return (w->opts_used && w->opts.pad_y > 0) ? w->opts.pad_y : 0; }
|
||||
|
||||
/* 是否禁用(状态机:MEOW_WS_ENABLED 未置位)。 */
|
||||
static int widget_disabled(const meow_widget *w) { return !(w->state & MEOW_WS_ENABLED); }
|
||||
|
||||
/* MD3 状态层叠色:把基色向 onSurface 叠 t% 调出按下/按住/聚焦状态层(§5.8.4)。 */
|
||||
static uint32_t state_tint(uint32_t base, const meow_theme *th, int t)
|
||||
{ return blend_color(base, th->onSurface, t); }
|
||||
|
||||
/* ---- §5.8.2/§5.8.3 公共 API:Options + 状态机 ---- */
|
||||
int meow_widget_set_opts(meow_widget *w, const meow_widget_opts *opts)
|
||||
{
|
||||
if (!w || !opts) return -1;
|
||||
w->opts = *opts;
|
||||
w->opts_used = 1;
|
||||
/* disabled/selected 立即反映到状态机位(§5.8.3)。 */
|
||||
if (opts->disabled) w->state &= ~MEOW_WS_ENABLED;
|
||||
else w->state |= MEOW_WS_ENABLED;
|
||||
if (opts->selected) w->state |= MEOW_WS_SELECTED;
|
||||
else w->state &= ~MEOW_WS_SELECTED;
|
||||
meow_gfx_mark_dirty(w->x, w->y, w->w, w->h); /* 挂载会改变绘制:标区域脏 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int meow_widget_opts_get(const meow_widget *w, meow_widget_opts *out)
|
||||
{
|
||||
if (!w || !out) return -1;
|
||||
if (!w->opts_used) {
|
||||
/* 默认视图:radius=-1(控件默认)、disabled/selected 取状态位。 */
|
||||
memset(out, 0, sizeof *out);
|
||||
out->radius = -1;
|
||||
out->disabled = widget_disabled(w) ? 1 : 0;
|
||||
out->selected = (w->state & MEOW_WS_SELECTED) ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
*out = w->opts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void meow_widget_set_state(meow_widget *w, unsigned bits, int on)
|
||||
{
|
||||
if (!w) return;
|
||||
if (on) w->state |= bits;
|
||||
else w->state &= ~bits;
|
||||
meow_gfx_mark_dirty(w->x, w->y, w->w, w->h); /* 状态变化会改变绘制 */
|
||||
}
|
||||
unsigned meow_widget_state(const meow_widget *w) { return w ? w->state : MEOW_WS_ENABLED; }
|
||||
int meow_widget_enabled(const meow_widget *w) { return w ? (w->state & MEOW_WS_ENABLED) != 0 : 0; }
|
||||
void meow_widget_set_enabled(meow_widget *w, int on) { meow_widget_set_state(w, MEOW_WS_ENABLED, on ? 1 : 0); }
|
||||
int meow_widget_selected(const meow_widget *w) { return w ? (w->state & MEOW_WS_SELECTED) != 0 : 0; }
|
||||
void meow_widget_set_selected(meow_widget *w, int on) { meow_widget_set_state(w, MEOW_WS_SELECTED, on ? 1 : 0); }
|
||||
|
||||
/* 一键叠状态层(自定义画布可选;标准控件由 SDK 自动叠,不调用):半透明状态层 + 焦点描边。 */
|
||||
void meow_widget_draw_state_layer(meow_widget *w, const meow_theme *th)
|
||||
{
|
||||
if (!w || !th) return;
|
||||
if (widget_disabled(w)) {
|
||||
meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
return; /* 禁用不再叠按下/聚焦 */
|
||||
}
|
||||
if (w->state & MEOW_WS_PRESSED) {
|
||||
/* 向 onSurface 叠半透明状态层铺满控件矩形(按下/按住) */
|
||||
int t = (w->opts_used && w->opts.bg) ? 24 : 12;
|
||||
meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, t * 255 / 100);
|
||||
}
|
||||
if (w->state & MEOW_WS_FOCUSED) {
|
||||
int r = widget_radius(w, 10);
|
||||
meow_ui_draw_rect_round(w->x, w->y, w->w, w->h, r, th->primary);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 实时按压即时刷新(agent-119)----
|
||||
* 按压/按住视觉由 meow_widget_draw 读实时触摸(meow_input_touch_*)呈现,但 app 只在
|
||||
* on_tick(1s)/on_tap 重绘 → 快速点/按住时按压态刷新滞后/漏。此处在 SDK 触摸按下/释放/
|
||||
@@ -103,6 +198,7 @@ void meow_widget_touch_refresh(void)
|
||||
* 例如点 FULLSCREEN 后进入全屏),静默清引用——不绘制、不闪现(否则会在新页面底上
|
||||
* 画出旧按钮的 accent 残留)。 */
|
||||
if (g_live_btn && g_live_btn->owner != active) {
|
||||
g_live_btn->state &= ~MEOW_WS_PRESSED; /* §5.8.3:清除按压态(跨页面不残留) */
|
||||
g_live_btn = NULL;
|
||||
g_live_prev_hold = 0;
|
||||
}
|
||||
@@ -112,18 +208,29 @@ void meow_widget_touch_refresh(void)
|
||||
|
||||
if (!down) {
|
||||
/* 释放:把最近按压按钮恢复为常态 */
|
||||
if (g_live_btn) { live_btn_redraw(g_live_btn); g_live_btn = NULL; }
|
||||
if (g_live_btn) {
|
||||
g_live_btn->state &= ~MEOW_WS_PRESSED;
|
||||
live_btn_redraw(g_live_btn);
|
||||
g_live_btn = NULL;
|
||||
}
|
||||
g_live_prev_hold = 0;
|
||||
return;
|
||||
}
|
||||
if (hit != g_live_btn) {
|
||||
/* 按下/滑至新按钮:先恢复旧(若手指滑离),再按压新 */
|
||||
if (g_live_btn) live_btn_redraw(g_live_btn);
|
||||
if (g_live_btn) {
|
||||
g_live_btn->state &= ~MEOW_WS_PRESSED;
|
||||
live_btn_redraw(g_live_btn);
|
||||
}
|
||||
g_live_btn = hit;
|
||||
g_live_prev_hold = hold;
|
||||
if (hit) live_btn_redraw(hit);
|
||||
if (hit) {
|
||||
if (widget_disabled(hit)) { g_live_btn = NULL; } /* 禁用不响应按压 */
|
||||
else { hit->state |= MEOW_WS_PRESSED; live_btn_redraw(hit); }
|
||||
}
|
||||
} else if (hit && hold != g_live_prev_hold) {
|
||||
/* 同一按钮按住达阈值:升级到更强态 */
|
||||
hit->state |= MEOW_WS_PRESSED;
|
||||
live_btn_redraw(hit);
|
||||
g_live_prev_hold = hold;
|
||||
}
|
||||
@@ -135,6 +242,7 @@ meow_widget *meow_btn_create(int x, int y, int w, int h, const char *label)
|
||||
if (!t) return NULL;
|
||||
t->type = W_BTN; t->x = x; t->y = y; t->w = w; t->h = h;
|
||||
t->owner = meow_ui_activity_active(); /* §6.1 ①:绑定当前 active Activity */
|
||||
t->state = MEOW_WS_ENABLED; /* §5.8.3:默认 ENABLED */
|
||||
if (label) { strncpy(t->label, label, sizeof t->label - 1); t->label[sizeof t->label - 1] = 0; }
|
||||
live_btn_register(t);
|
||||
return t;
|
||||
@@ -146,6 +254,7 @@ meow_widget *meow_slider_create(int x, int y, int w, int min, int max, int val)
|
||||
if (!t) return NULL;
|
||||
t->type = W_SLIDER; t->x = x; t->y = y; t->w = w; t->h = 40;
|
||||
t->owner = meow_ui_activity_active();
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->min = min; t->max = (max > min) ? max : min + 1; t->val = (val < min) ? min : (val > t->max) ? t->max : val;
|
||||
return t;
|
||||
}
|
||||
@@ -156,7 +265,9 @@ meow_widget *meow_switch_create(int x, int y, int on)
|
||||
if (!t) return NULL;
|
||||
t->type = W_SWITCH; t->x = x; t->y = y; t->w = 56; t->h = 32;
|
||||
t->owner = meow_ui_activity_active();
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->on = on ? 1 : 0;
|
||||
if (t->on) t->state |= MEOW_WS_SELECTED; /* §5.8.3:开关 on → selected */
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -167,6 +278,7 @@ meow_widget *meow_list_create(int x, int y, int w, int h, int count,
|
||||
if (!t) return NULL;
|
||||
t->type = W_LIST; t->x = x; t->y = y; t->w = w; t->h = h;
|
||||
t->owner = meow_ui_activity_active();
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->count = count; t->src = src; t->ctx = ctx; t->sel = 0;
|
||||
return t;
|
||||
}
|
||||
@@ -178,6 +290,107 @@ void meow_widget_free(meow_widget *w)
|
||||
free(w);
|
||||
}
|
||||
|
||||
/* ---- §5.8.1 自定义画布:App 提供 render/hit 回调,同一个 meow_widget 基座 ---- */
|
||||
meow_widget *meow_widget_custom_create(int x, int y, int w, int h,
|
||||
meow_widget_render_fn render_cb,
|
||||
meow_widget_hit_fn hit_cb,
|
||||
void *ctx)
|
||||
{
|
||||
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
|
||||
if (!t) return NULL;
|
||||
t->type = W_CUSTOM; t->x = x; t->y = y; t->w = w; t->h = h;
|
||||
t->owner = meow_ui_activity_active(); /* §6.1 ①:同既有控件绑当前 active Activity */
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->render_cb = render_cb;
|
||||
t->hit_cb = hit_cb;
|
||||
t->ctx = ctx;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* ---- §5.8.5 可组合布局:Row / Column / Box + Spacer ---- */
|
||||
meow_widget *meow_row_create(int x, int y, int w, int h, int spacing)
|
||||
{
|
||||
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
|
||||
if (!t) return NULL;
|
||||
t->type = W_LAYOUT; t->kind = MEOW_LAYOUT_ROW; t->x = x; t->y = y; t->w = w; t->h = h;
|
||||
t->owner = meow_ui_activity_active();
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->spacing = spacing > 0 ? spacing : 0;
|
||||
return t;
|
||||
}
|
||||
meow_widget *meow_column_create(int x, int y, int w, int h, int spacing)
|
||||
{
|
||||
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
|
||||
if (!t) return NULL;
|
||||
t->type = W_LAYOUT; t->kind = MEOW_LAYOUT_COLUMN; t->x = x; t->y = y; t->w = w; t->h = h;
|
||||
t->owner = meow_ui_activity_active();
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->spacing = spacing > 0 ? spacing : 0;
|
||||
return t;
|
||||
}
|
||||
meow_widget *meow_box_create(int x, int y, int w, int h)
|
||||
{
|
||||
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
|
||||
if (!t) return NULL;
|
||||
t->type = W_LAYOUT; t->kind = MEOW_LAYOUT_BOX; t->x = x; t->y = y; t->w = w; t->h = h;
|
||||
t->owner = meow_ui_activity_active();
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->spacing = 0;
|
||||
return t;
|
||||
}
|
||||
meow_widget *meow_layout_spacer(int w, int h)
|
||||
{
|
||||
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
|
||||
if (!t) return NULL;
|
||||
t->type = W_SPACER; t->x = 0; t->y = 0; t->w = w > 0 ? w : 0; t->h = h > 0 ? h : 0;
|
||||
t->owner = meow_ui_activity_active();
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
return t;
|
||||
}
|
||||
|
||||
int meow_layout_add(meow_widget *layout, meow_widget *child)
|
||||
{
|
||||
if (!layout || layout->type != W_LAYOUT || !child) return -1;
|
||||
if (layout->nchildren >= MEOW_LAYOUT_MAX_CHILD) return -1;
|
||||
for (int i = 0; i < layout->nchildren; i++)
|
||||
if (layout->children[i] == child) return -1; /* 同子项去重 */
|
||||
layout->children[layout->nchildren++] = child;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 把子控件算成绝对坐标(写进各 child 的 x,y;w/h 固定尺寸,先不做弹性)。
|
||||
* Row:子项水平排(垂直居中于容器 h);Column:垂直排(水平居中);Box:叠放(各放容器左上角)。
|
||||
* 子项的 margin_x/margin_y(Options)参与排布;子项为布局时递归 arrange。 */
|
||||
void meow_layout_arrange(meow_widget *layout)
|
||||
{
|
||||
if (!layout || layout->type != W_LAYOUT) return;
|
||||
int kind = layout->kind;
|
||||
int cur = (kind == MEOW_LAYOUT_COLUMN) ? layout->y : layout->x;
|
||||
for (int i = 0; i < layout->nchildren; i++) {
|
||||
meow_widget *c = layout->children[i];
|
||||
if (!c) continue;
|
||||
int mx = (c->opts_used && c->opts.margin_x > 0) ? c->opts.margin_x : 0;
|
||||
int my = (c->opts_used && c->opts.margin_y > 0) ? c->opts.margin_y : 0;
|
||||
if (kind == MEOW_LAYOUT_ROW) {
|
||||
cur += mx;
|
||||
c->x = cur;
|
||||
c->y = layout->y + (layout->h - c->h) / 2; /* 垂直居中 */
|
||||
if (c->y < layout->y) c->y = layout->y;
|
||||
cur += c->w + mx + layout->spacing;
|
||||
} else if (kind == MEOW_LAYOUT_COLUMN) {
|
||||
cur += my;
|
||||
c->y = cur;
|
||||
c->x = layout->x + (layout->w - c->w) / 2; /* 水平居中 */
|
||||
if (c->x < layout->x) c->x = layout->x;
|
||||
cur += c->h + my + layout->spacing;
|
||||
} else { /* BOX:叠放,各放容器左上角 */
|
||||
c->x = layout->x;
|
||||
c->y = layout->y;
|
||||
}
|
||||
if (c->type == W_LAYOUT) meow_layout_arrange(c); /* 嵌套递归 */
|
||||
}
|
||||
}
|
||||
|
||||
static int hit(meow_widget *w, int x, int y)
|
||||
{
|
||||
if (!w) return 0;
|
||||
@@ -189,9 +402,15 @@ int meow_widget_hit(meow_widget *w, int x, int y)
|
||||
if (!w) return 0;
|
||||
/* §6.1 ①:页面控件只对自身 Activity 负责,不跨页面复用命中。 */
|
||||
if (w->owner != meow_ui_activity_active()) return 0;
|
||||
/* §5.8.3:禁用不响应命中(也不进 pressed)。 */
|
||||
if (widget_disabled(w)) return 0;
|
||||
/* §5.8.1:自定义画布用 App 回调判定命中(可非矩形);其余矩形内含。 */
|
||||
if (w->type == W_CUSTOM)
|
||||
return w->hit_cb ? w->hit_cb(w, x, y, w->ctx) : hit(w, x, y);
|
||||
return hit(w, x, y);
|
||||
}
|
||||
int meow_btn_pressed(meow_widget *w) { return w && w->type == W_BTN ? w->pressed : 0; }
|
||||
int meow_btn_pressed(meow_widget *w)
|
||||
{ return w && w->type == W_BTN ? (w->pressed || (w->state & MEOW_WS_PRESSED)) : 0; }
|
||||
|
||||
void meow_slider_set(meow_widget *w, int val)
|
||||
{
|
||||
@@ -201,56 +420,86 @@ void meow_slider_set(meow_widget *w, int val)
|
||||
}
|
||||
int meow_slider_get(meow_widget *w) { return w && w->type == W_SLIDER ? w->val : 0; }
|
||||
void meow_switch_set(meow_widget *w, int on)
|
||||
{ if (w && w->type == W_SWITCH) w->on = on ? 1 : 0; }
|
||||
{
|
||||
if (w && w->type == W_SWITCH) {
|
||||
w->on = on ? 1 : 0;
|
||||
if (w->on) w->state |= MEOW_WS_SELECTED; /* §5.8.3:on → selected */
|
||||
else w->state &= ~MEOW_WS_SELECTED;
|
||||
meow_gfx_mark_dirty(w->x, w->y, w->w, w->h);
|
||||
}
|
||||
}
|
||||
int meow_switch_get(meow_widget *w) { return w && w->type == W_SWITCH ? w->on : 0; }
|
||||
|
||||
/* 供应用在 on_tap 里对外可控回馈:这里不存命中态(由 app 分发),仅暴露绘制 */
|
||||
/* 供应用在 on_tap 里对外可控回馈:这里不存命中态(由 app 分发),仅暴露绘制。
|
||||
* §5.8 升级:统一取语义色(meow_theme),读 opts(背景/圆角/描边/pad/fg)与状态机位
|
||||
* (disabled/selected/pressed/focused),标准控件由 SDK 自动叠状态层;自定义画布(W_CUSTOM)
|
||||
* 由 App render_cb 全权自绘(SDK 只注入主题、不画背景/状态层);布局(W_LAYOUT)递归绘制子控件。 */
|
||||
void meow_widget_draw(meow_widget *w)
|
||||
{
|
||||
if (!w) return;
|
||||
const meow_theme *th = meow_theme_get();
|
||||
int disabled = widget_disabled(w);
|
||||
|
||||
switch (w->type) {
|
||||
case W_BTN: {
|
||||
/* 按下/按住 = 视觉态(不改 on_tap/btn_pressed/hit 语义)。按下来源两路:
|
||||
* - w->pressed:应用在 on_tap 命中后标记(既有语义,保留);
|
||||
/* 按下/按住 = 视觉态(不改 on_tap/btn_pressed/hit 语义)。按下来源三路:
|
||||
* - 状态机 MEOW_WS_PRESSED(App 经 set_state 或 SDK 触摸层置位);
|
||||
* - w->pressed(既有字面,保留);
|
||||
* - 实时触摸命中:SDK 输入层当前正按在该控件内(无需 app 追踪)。
|
||||
* 按住=实时触摸命中且持续 ≥MEOW_BTN_HOLD_MS;按下=任一来源。 */
|
||||
* 按住=实时触摸命中且持续 ≥MEOW_BTN_HOLD_MS;按下=任一来源。禁用时不叠按下底。 */
|
||||
int live = meow_input_touch_down() &&
|
||||
meow_input_touch_x() >= w->x && meow_input_touch_x() < w->x + w->w &&
|
||||
meow_input_touch_y() >= w->y && meow_input_touch_y() < w->y + w->h;
|
||||
int press = w->pressed || live;
|
||||
int press = (w->state & MEOW_WS_PRESSED) || w->pressed || live;
|
||||
int hold = live && meow_input_touch_hold_ms() >= MEOW_BTN_HOLD_MS;
|
||||
/* 纯色状态层(无描边环、无分层):一层即满,深浅二态均由 accent 向黑调出。
|
||||
* 常态=accent、按下=accent+黑12%、按住=accent+黑24%。整体一层不「先底色再套圈」。 */
|
||||
if (disabled) press = 0;
|
||||
/* 底色:选中态用 primaryContainer(onPrimaryContainer 文字);否则 primary(onPrimary 文字)。
|
||||
* 可被 opts.bg 覆盖(此时文字用 opts.fg 或 onPrimary)。 */
|
||||
int selected = (w->state & MEOW_WS_SELECTED);
|
||||
uint32_t base, default_fg;
|
||||
if (selected && !(w->opts_used && w->opts.bg)) { base = th->primaryContainer; default_fg = th->onPrimaryContainer; }
|
||||
else { base = widget_bg(w, th, th->primary); default_fg = th->onPrimary; }
|
||||
uint32_t bg;
|
||||
if (hold)
|
||||
bg = blend_color(th->accent, 0x000000, 24);
|
||||
else if (press)
|
||||
bg = blend_color(th->accent, 0x000000, 12);
|
||||
else
|
||||
bg = th->accent;
|
||||
meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, bg);
|
||||
meow_ui_text_center(w->x + w->w / 2, w->y + (w->h - meow_ui_text_height(th->font_body)) / 2,
|
||||
w->label, th->font_body, 0xFFFFFF);
|
||||
if (hold) bg = state_tint(base, th, 24); /* 按住 = 深状态层 */
|
||||
else if (press) bg = state_tint(base, th, 12); /* 按下 = 状态层 */
|
||||
else bg = base;
|
||||
int r = widget_radius(w, 10);
|
||||
meow_gfx_fill_round(w->x, w->y, w->w, w->h, r, bg);
|
||||
/* 内容区受 pad 垂直内缩(水平居中文本,pad_x 不影响中心) */
|
||||
int pady = widget_pady(w);
|
||||
int cx = w->x + w->w / 2;
|
||||
int cy = w->y + pady + (w->h - 2 * pady - meow_ui_text_height(th->font_body)) / 2;
|
||||
uint32_t tc = widget_fg(w, th, default_fg);
|
||||
meow_ui_text_center(cx, cy, w->label, th->font_body, tc);
|
||||
/* opts 描边 / 焦点外圈 */
|
||||
if (w->opts_used && w->opts.stroke && w->opts.stroke_w > 0)
|
||||
meow_ui_draw_rect_round(w->x, w->y, w->w, w->h, r, w->opts.stroke);
|
||||
if (w->state & MEOW_WS_FOCUSED)
|
||||
meow_ui_draw_rect_round(w->x, w->y, w->w, w->h, r, th->primary);
|
||||
/* 禁用:整体变暗(状态层向 onSurface 叠 α) */
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_SLIDER: {
|
||||
/* 语义色:轨道=onSurfaceVariant(dim)、填充=primary(accent)、滑块=fg(onSurface)。 */
|
||||
int trace_y = w->y + w->h / 2 - 3;
|
||||
meow_ui_fill_rect(w->x, trace_y, w->w, 6, th->dim);
|
||||
meow_ui_fill_rect(w->x, trace_y, w->w, 6, th->onSurfaceVariant);
|
||||
int range = w->max - w->min;
|
||||
int frac = range ? (w->val - w->min) * w->w / range : 0;
|
||||
if (frac > 0) meow_ui_fill_rect(w->x, trace_y, frac, 6, th->accent);
|
||||
if (frac > 0) meow_ui_fill_rect(w->x, trace_y, frac, 6, th->primary);
|
||||
int tx = w->x + frac;
|
||||
int ty = w->y + w->h / 2;
|
||||
meow_gfx_fill_round(tx - 10, ty - 10, 20, 20, 10, th->fg);
|
||||
meow_gfx_fill_round(tx - 10, ty - 10, 20, 20, 10, th->onSurface);
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_SWITCH: {
|
||||
uint32_t bg = w->on ? th->accent : th->dim;
|
||||
uint32_t bg = w->on ? th->primary : th->onSurfaceVariant;
|
||||
meow_gfx_fill_round(w->x, w->y, w->w, w->h, w->h / 2, bg);
|
||||
int knob_r = w->h / 2 - 3;
|
||||
int kx = w->on ? w->x + w->w - knob_r * 2 - 3 : w->x + 3;
|
||||
meow_gfx_fill_round(kx, w->y + 3, knob_r * 2, knob_r * 2, knob_r, 0xFFFFFF);
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_LIST: {
|
||||
@@ -258,17 +507,31 @@ void meow_widget_draw(meow_widget *w)
|
||||
for (int i = 0; i < w->count; i++) {
|
||||
int ry = w->y + i * rowh;
|
||||
if (ry >= w->y + w->h) break;
|
||||
if (i == w->sel) meow_ui_fill_rect(w->x, ry, w->w, rowh, th->accent);
|
||||
if (i == w->sel) meow_ui_fill_rect(w->x, ry, w->w, rowh, th->primary);
|
||||
char label[64] = "";
|
||||
if (w->src && w->src(i, label, (int)sizeof label, w->ctx)) {
|
||||
uint32_t col = (i == w->sel) ? 0xFFFFFF : th->fg;
|
||||
uint32_t col = (i == w->sel) ? 0xFFFFFF : th->onSurface;
|
||||
meow_ui_text(w->x + pad, ry + (rowh - meow_ui_text_height(th->font_body)) / 2,
|
||||
label, th->font_body, col);
|
||||
}
|
||||
meow_ui_fill_rect(w->x, ry + rowh - 1, w->w, 1, th->dim);
|
||||
meow_ui_fill_rect(w->x, ry + rowh - 1, w->w, 1, th->onSurfaceVariant);
|
||||
}
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
case W_CUSTOM:
|
||||
/* SDK 不替自定义画布画背景/状态层;App 在 render_cb 里自绘(th 供取色),
|
||||
* 可读 meow_widget_state(状态位)/ meow_widget_opts_get(通用修饰)自行叠。 */
|
||||
if (w->render_cb) w->render_cb(w, th, w->ctx);
|
||||
break;
|
||||
case W_LAYOUT:
|
||||
/* 递归绘制子控件(子控件坐标已由 meow_layout_arrange 算成绝对坐标)。 */
|
||||
for (int i = 0; i < w->nchildren; i++)
|
||||
if (w->children[i]) meow_widget_draw(w->children[i]);
|
||||
break;
|
||||
case W_SPACER:
|
||||
break; /* 不可见空白 */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user