sdk: sb_icons 扩充动作图标 + TopAppBar 位图图标支持

TopAppBar 动作项此前用字体码点(⚙↻⌕)绘制,4k 字库无这些码点→豆腐。改走位图:
- sb_icons.h 追加 15 个 Material Symbols Rounded 动作位图(settings/refresh/search/
  play_arrow/pause/skip_next/skip_previous/arrow_back/close/add/check/lyrics/
  queue_music/download/heart),FILL=1 实心与状态栏一致;sb_icons[] 只追加(0-14 不变),
  动作索引 15-29。
- 生成脚本 tools/gen_action_icons.py(cairosvg svg→32x32 单通道 alpha,SVG 缓存
  icons_src/ 可复现、断网可跑)。
- meow_ui_gfx.c 抽取图标 blit 像素核心 icon_blit_px,新增 meow_gfx_blit_icon_ink
  (控件路径不标顶栏脏区);meow_gfx_blit_icon 状态栏路径行为不变。
- meow_ui_widget.c W_TOPAPPBAR 项支持位图模式:新 API meow_topappbar_set_item_icon
  (w, idx, icon_idx),绘制取主题 onSurface/按压向栏底色淡 12%,文本项行为不变;
  hit 用统一项宽。
- 公共头 widgets.h 加 MEOW_ICON_*(15-29) 枚举与 set_item_icon 声明;internal.h 声明
  meow_gfx_blit_icon_ink。
- 宿主测试(test_meow_ui_widgets)加 topbar 位图图标绘制/命中/按压变淡 + 状态栏 blit
  回归断言;test_umbrella 加 MEOW_ICON_* 编译期锁;MEOW_APP_SDK.md 补说明+索引表。
- 验证:宿主测试 ALL PASS(14项);交叉 zig 构建 0 warning;meow_ui.a 新 md5
  1d69cdae55ab5dde3a98f5e2124f3656。
This commit is contained in:
AskaEth committed 2026-09-02 03:24:46 +08:00
1 parent 41427fdcb3
commit 3611cbbbde
24 files changed
+1006 -22

No files matched your search

+52 -3
View File
@@ -23,6 +23,12 @@ enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3,
/* 容器(TopAppBar 动作 / NavigationBar 项 / TabBar 页签)子项上限。 */
#define MEOW_CONTAINER_MAX 8
/* TopAppBar 位图动作图标(Material Symbols Rounded 单通道 alpha,见 sb_icons.h):
* 动作单元格宽(32px 图标槽水平居中 + 两侧留白 8px)与目标 ink 高(MD3 24dp 图标)。
* ink 垂直中线取栏中点(mid),与状态栏 28px 栏「文字中线」风格一致。 */
#define MEOW_TOPBAR_ACTION_W 48
#define MEOW_TOPBAR_ICON_INK 24
/* 按钮「按住」判定阈值:视觉上区分「短按按下」与「按住」(更深状态层)。
* 阈值取 meow_ui_long_press_ms()(§6.3 通用长按默认 500ms,App 可配)——与长按事件同源,
* 避免「长按已触发但视觉未加深」的不一致。触发语义不变(on_tap 仍按抬手判定)。 */
@@ -79,6 +85,9 @@ struct meow_widget {
char items[MEOW_CONTAINER_MAX][24]; /* W_TOPAPPBAR 动作 / W_NAVBAR 项 / W_TABBAR 页签 */
int nitems; /* 上述项数 */
int selected; /* W_NAVBAR / W_TABBAR 选中下标 */
/* W_TOPAPPBAR 动作位图图标:每项 sb_icons[] 索引(≥0=画该图标;-1=画 items[i] 文本)。
* 追加字段(calloc 归零为 0,故 create/add_action 显式置 -1 保证文本默认)。 */
int item_icon[MEOW_CONTAINER_MAX];
/* M2.71-bug8:W_LIST 行点击回调(可选)。 */
meow_list_tap_fn tap_cb; /* 命中行回调(可 NULL) */
void *tap_ctx; /* 回调 ctx */
@@ -124,6 +133,22 @@ static int widget_disabled(const meow_widget *w) { return !(w->state & MEOW_WS_E
static uint32_t state_tint(uint32_t base, const meow_theme *th, int t)
{ return blend_color(base, th->onSurface, t); }
/* ---- TopAppBar 位图动作图标辅助(绘制与命中共用的「项宽」/「按压淡色」)----
* 项宽:位图图标用固定单元格 MEOW_TOPBAR_ACTION_W(图标槽在该格内居中);文本项沿用
* 原「文本宽 + 12」不等式(行为不变)。命中与绘制同源,避免 icon/text 混合排布错位。 */
static int topbar_item_w(const meow_widget *w, const meow_theme *th, int i)
{
if (w->item_icon[i] >= 0) return MEOW_TOPBAR_ACTION_W;
return meow_ui_text_width(w->items[i], th->font_body) + 12;
}
/* 动作颜色:默认 onSurface;按压态向栏底色(surfaceContainerHigh)淡 12%(§5.8.4 状态层)。 */
static uint32_t topbar_action_color(const meow_widget *w, const meow_theme *th)
{
uint32_t col = th->onSurface;
if (w->state & MEOW_WS_PRESSED) col = blend_color(col, th->surfaceContainerHigh, 12);
return col;
}
/* ---- §5.8.2/§5.8.3 公共 API:Options + 状态机 ---- */
int meow_widget_set_opts(meow_widget *w, const meow_widget_opts *opts)
{
@@ -715,6 +740,7 @@ meow_widget *meow_topappbar_create(int x, int y, int w, int h, const char *title
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->back_on = 0; t->nitems = 0;
for (int i = 0; i < MEOW_CONTAINER_MAX; i++) t->item_icon[i] = -1; /* 默认文本项 */
if (title) { strncpy(t->label, title, sizeof t->label - 1); t->label[sizeof t->label - 1] = 0; }
meow_draw_widget_register(t);
return t;
@@ -737,10 +763,24 @@ int meow_topappbar_add_action(meow_widget *w, const char *label)
if (label) strncpy(w->items[w->nitems], label, 23);
w->items[w->nitems][23] = 0;
int idx = w->nitems;
w->item_icon[idx] = -1; /* 文本项(后续可用 set_item_icon 转位图) */
w->nitems++;
meow_gfx_mark_dirty(w->x, w->y, w->w, w->h);
return idx;
}
/* 把第 idx 个动作项设为位图图标(sb_icons[] 索引,MEOW_ICON_* 或 SB_I_*)。追加式 API:
* 文本项(add_action 默认)行为不变;仅「设为图标后」该动作改用 meow_gfx_blit_icon_ink
* 绘制(32px 槽居中 + ink 垂直对齐栏中点),颜色取主题 onSurface/按压态淡色。
* 返回 0=成功;NULL/非 topbar/越界 idx/非法 icon_idx=负值。 */
int meow_topappbar_set_item_icon(meow_widget *w, int idx, int icon_idx)
{
if (!w || w->type != W_TOPAPPBAR) return -1;
if (idx < 0 || idx >= w->nitems) return -1;
if (icon_idx < 0) return -1; /* ≥0 的顶层校验;越界索引由 blit 内部 clamp 兜底 */
w->item_icon[idx] = icon_idx;
meow_gfx_mark_dirty(w->x, w->y, w->w, w->h);
return 0;
}
int meow_topappbar_hit(meow_widget *w, int x, int y)
{
if (!w || w->type != W_TOPAPPBAR) return -1;
@@ -749,7 +789,7 @@ int meow_topappbar_hit(meow_widget *w, int x, int y)
const meow_theme *th = meow_theme_get();
int ax = w->x + w->w - 10;
for (int i = w->nitems - 1; i >= 0; i--) {
int aw = meow_ui_text_width(w->items[i], th->font_body) + 12;
int aw = topbar_item_w(w, th, i); /* 位图图标用固定单元格;文本项用原不等式 */
ax -= aw;
if (x >= ax && x < ax + aw && x >= w->x + 100) return i; /* 不压标题区/返回区 */
}
@@ -1849,12 +1889,21 @@ void meow_widget_draw(meow_widget *w)
tx = w->x + 48;
}
if (w->label[0]) meow_ui_text(tx, ty, w->label, th->font_title, th->onSurface);
/* 动作区:从右往左排。位图图标(item_icon>=0)用 sb_icons[] 位图(Material Symbols
* Rounded)经 meow_gfx_blit_icon_ink 画(32px 槽在动作格内水平居中 + ink 垂直对齐 mid,
* 颜色 = onSurface/按压淡色);文本项沿用 meow_ui_text_center(行为不变)。 */
int ay = mid - meow_ui_text_height(th->font_body) / 2;
uint32_t acol = topbar_action_color(w, th);
int ax = w->x + w->w - 12;
for (int i = w->nitems - 1; i >= 0; i--) {
int aw = meow_ui_text_width(w->items[i], th->font_body) + 12;
int aw = topbar_item_w(w, th, i);
ax -= aw;
meow_ui_text_center(ax + aw / 2, ay, w->items[i], th->font_body, th->onSurface);
if (w->item_icon[i] >= 0) {
int slot_x = ax + (aw - MEOW_SB_ICON_SIZE) / 2; /* 32px 槽在格内居中 */
meow_gfx_blit_icon_ink(w->item_icon[i], slot_x, MEOW_TOPBAR_ICON_INK, acol, mid);
} else {
meow_ui_text_center(ax + aw / 2, ay, w->items[i], th->font_body, acol);
}
}
meow_ui_fill_rect(w->x, w->y + w->h - 1, w->w, 1, th->outlineVariant);
break;