docs: 同步主仓最新 SDK 设计/开发者指南 + README 新能力清单
MEOW_APP_SDK.md(相对主仓 MEOW_APP_SDK.md,保留公开仓化改写与免责声明): - 公共头模块索引补 meow_ui_str.h 行 - §5.5 新增「虚拟化层」(W_LISTVIEW 固定 item 池/多行富 item/自绘钩子;去除 ncm 引用) - §5.8 新增 TopAppBar 位图动作图标 + sb_icons[] 30 图标索引表 - §6.1 后插入 §6.4 事件分发形式化(命中/捕获/消费/层间隔离/唯一焦点) - 新增 §12 布局/动画/滚动(meow_box/meow_anim/meow_scroll)+ §13 字符串 i18n(meow_str_*) - 遗留/待实现 由 §12 重编号为 §14;去除 ncm/网易云/NetEase 字样 APP_DEV_GUIDE.md:新增 §7.11 字符串资源/i18n(保留既有公开化改写 §1.1/§5.2/§10 等) README.md:更新 include/ 子头计数(20),补「SDK 能力速览」(布局/动画/滚动/事件分发/ 虚拟列表/i18n/图标库/图片降采样) .gitignore:新增本次引入的宿主测试二进制名(test_meow_ui_box/anim/dispatch/listvirt/scroll/str)
This commit is contained in:
1 parent
4b094d62a5
commit
0af6af7c7e
4 files changed
+403
-3
No files matched your search
@@ -424,6 +424,15 @@ am resolve <pkg> <activity> # 路由决策(返回 CHAIN)
|
||||
- App 一般不需要联网;确需联网时**克制请求**(见 §8 风控式节制)。
|
||||
- 系统层面(`netmgrd` 期 0/1):无 iptables 用户态 / 无 cgroup / 无 conntrack → **内核级每应用管控不可行**;主杠杆 = DNS 代理(域名级)+ 连接普查(inet_diag)+ **SDK 自愿通道**。App 走 SDK 自愿通道配合管控是后续方向;当前 App 如联网,请自律、尊重断网降级。
|
||||
|
||||
### 7.11 字符串资源 / i18n(`meow_str_*`,2026-09-02 追加)
|
||||
|
||||
- **任何 UI 文案都不要硬编码**,用 `meow_str("域.名")` 按当前语言取串(示例:`meow_str("common.ok")`)。`meow_str` 永不返回 `NULL`:未知 id 原样返回 `id`,方便你打点/兜底展示。
|
||||
- **`meow_strf(out, outsz, id, ...)`**:取串后按 `snprintf` 语义格式化进调用方缓冲(自带 NUL;`out==NULL`/`outsz<=0` 返回 `-1`)。
|
||||
- **切换语言**:`meow_str_set_locale("zh_cn"|"en_us")` 即时生效;locale 存系统设置 `settings.conf` 的 `ui.locale`(默认 `zh_cn`,SDK 启动惰性读取)。App 里做语言切换页时调用它即可。
|
||||
- **覆盖/追加**:可不改二进制在 TF 卡放 `MeowData/<pkg>/strings/<locale>.txt`(每行 `id=text`,UTF-8)覆盖内嵌或新增自己的串(如 `player.pause=暂停`);无卡/无文件/无该 id → 回落内嵌双语默认表(`common.*` + `ime.*` 内置,见 `include/meow_ui_str.h`)。
|
||||
- **id 规范**:`域.名`;SDK 自带 `common.*`/`ime.*`,你自建 `域`(如 `player.*`)避免撞名。
|
||||
- **边界**:当前为 i18n 雏形,仅 `zh_cn`/`en_us` 双语**整串**取用;**不做复数规则 / BiDi / RTL / 动态字体回退**(详见 `MEOW_APP_SDK.md §13.7`)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 规范与红线
|
||||
|
||||
+374
-2
@@ -30,6 +30,7 @@
|
||||
| `meow_ui_config.h` | 应用配置读写 | §9.2 |
|
||||
| `meow_ui_font.h` | 字体 | §5.7 卡片字体列表 + §10 字体分层 |
|
||||
| `meow_ui_audio.h` | 独占音频客户端 | §11 |
|
||||
| `meow_ui_str.h` | 字符串资源/i18n | §13 `meow_str`/`meow_strf`/`meow_str_set_locale` |
|
||||
|
||||
其余按需显式 include、**不**随伞头发布的模块头(原样保留、本次未改):`meow_ui_view.h` / `meow_ui_nav.h` / `meow_ui_media.h` / `meow_ui_sys.h` / `meow_ui_vol.h`。`meow_ui_internal.h` 为 SDK 内部件,不随伞头发布。
|
||||
|
||||
@@ -373,7 +374,7 @@ int meow_ui_setform_hit_row(meow_widget *f, int x, int y);/* 命中行号(-1=
|
||||
|
||||
### 5.5 设计模板 A② 列表+选择 [源:2026-08-30]
|
||||
|
||||
> **性质**:SDK 设计模板,供 App 主页/子页按需选用;对既有 `meow_list_create`(B1 最小集,§5,仅 label 文本)做**结构化扩展**(行=图标+标题+副标题+右侧操作 + 选中态)。API「草案/待定案」,不实现。**(✅ 已实现:结构化列表控件 `W_LISTVIEW`,M2.76 · agent-186 · 243148e,见 §5.8.8 分批;长按事件 `MEOW_EV_LONG_PRESS` + `meow_ui_set_long_press_cb/ms`,M2.77 · agent-191 · e95552b。)**
|
||||
> **性质**:SDK 设计模板,供 App 主页/子页按需选用;对既有 `meow_list_create`(B1 最小集,§5,仅 label 文本)做**结构化扩展**(行=图标+标题+副标题+右侧操作 + 选中态)。API「草案/待定案」,不实现。**(✅ 已实现:结构化列表控件 `W_LISTVIEW`,M2.76 · agent-186 · 243148e,见 §5.8.8 分批;长按事件 `MEOW_EV_LONG_PRESS` + `meow_ui_set_long_press_cb/ms`,M2.77 · agent-191 · e95552b;虚拟化层(固定 item 池 + 多行富 item + 完全自绘 + 段头 + 空态),2026-09-02,见 §5.5「虚拟化层」。)**
|
||||
|
||||
**摘要**:可滚动选择容器。**行数据源回调**逐行给出(图标 / 标题 / 副标题 / 右侧操作 / 是否可选),容器负责绘制/滚动/选中/命中;支持**单选、多选、长按**(上下文操作)。适合「已保存网络 / 附近WiFi / 蓝牙设备 / 歌曲列表 / 设置子菜单」等。
|
||||
|
||||
@@ -420,6 +421,51 @@ int meow_ui_listview_hit(meow_widget *lv, int x, int y); /* 命中行;
|
||||
```
|
||||
> 说明:既有 `meow_list_create`(label-only)保留;`meow_ui_listview_*` 为**结构化扩展**(图标/副标题/右侧/选中),可作为真则替换裸 label 列表。
|
||||
|
||||
#### 虚拟化层(2026-09-02,W_LISTVIEW 扩展;API 追加式,旧 API 一字不改)
|
||||
|
||||
> **痛点**:W_LISTVIEW(src2 每帧按需取行)只画可见行,但**每帧重取全部可见行**;大列表(几千/
|
||||
> 上万首)App 只能自行造「滑动窗口」补载(如超长歌单的滑动窗口补载)。
|
||||
> 本层把「固定 item 池 + 多行富 item + 完全自绘」收编为 SDK 级能力,让 App 只提供「总数 + bind」回调。
|
||||
|
||||
**关键交互/数据模型**:
|
||||
- **数据源**:`meow_listview_count_fn`(返回总行数,任意长 ≥65535,不常驻 item 对象)+ `meow_listview_bind_fn`
|
||||
(`index, meow_lv_item*, ctx`,仅在行「进入视口」(绑定到固定池槽)时被调**一次**)。`count` 每帧实时查
|
||||
(搜索过滤/懒加载动态长度)。
|
||||
- **固定 item 池**:池容量 = 视口行数 + 2 静态槽(有界,`MEOW_LV_POOL_MAX`=96),行离开视口不销毁、
|
||||
**回滚不重绑**(同一 index 命中同一槽 → bind 不重调);滚动只改 viewport 偏移(复用既有 set_scroll/max_scroll),
|
||||
不重绑未进视口的项。**整表扫过后池发生 wrap(cap<count),回收合法重绑但每次 draw 有界(≤视口窗口)**。
|
||||
- **多行富 item**:`meow_lv_item` 含 主标题 / 副标题(第二行小字)/ 左图标·缩略图 / 右尾件(chevron·开关·复选·⋯)/
|
||||
`section`(段头占位行)/ `enabled`。行高可配:`set_item_h`(精确 px)或 `set_item_tier(lines)`(1→48 / 2→56)。
|
||||
- **默认 renderer**(`meow_listview_set_item_renderer(lines, show_icon, show_right)`)用主题语义色绘制富 item;
|
||||
**完全自绘**:`meow_listview_set_item_draw(draw_fn, ctx)` 覆盖默认 renderer(App 收 idx/rect/state 自绘整行)。
|
||||
- **段头**:bind 返回 `item->section=1`(title=段名)→ 段头占位行(非粘性;粘性为后续增强)。
|
||||
- **空态**:`meow_listview_set_empty_text(text)`——count==0 时视口内居中画文案。
|
||||
|
||||
**与既有机制集成**:焦点/事件走 §6.4(`meow_listview_tap_at` 命中行自动 `meow_ui_focus_acquire`,点外由
|
||||
`meow_ui_focus_tap` 释放——list 焦点残留根治下沉);滚动接 §12.3 `meow_scroll`(`meow_scroll_wrap(lv, 1)`,
|
||||
`track`+`fling` 跟手/惯性,`set_snap(item_h)` 吸附到行边界);选中/按压态样式走主题
|
||||
(`primaryContainer`/半透明 `primary` 按压层);`enabled=0` 行变淡且命中不响应。
|
||||
|
||||
**API 一览(`include/meow_ui_widgets.h`,§5.5)**:
|
||||
```c
|
||||
typedef struct meow_lv_item { const char *title, *subtitle; const meow_img *icon; int icon_kind;
|
||||
int right, on, enabled, section; void *user; } meow_lv_item;
|
||||
typedef int (*meow_listview_count_fn)(void *ctx);
|
||||
typedef void (*meow_listview_bind_fn)(int index, meow_lv_item *item, void *ctx);
|
||||
typedef void (*meow_listview_draw_fn)(int idx, int x, int y, int w, int h, int state, void *ctx);
|
||||
int meow_listview_set_vsource(meow_widget *lv, meow_listview_count_fn count,
|
||||
meow_listview_bind_fn bind, void *ctx); /* 切 pool 模式 */
|
||||
int meow_listview_set_item_renderer(meow_widget *lv, int lines, int show_icon, int show_right);
|
||||
int meow_listview_set_item_tier(meow_widget *lv, int lines); /* 1→48 / 2→56 */
|
||||
int meow_listview_set_item_draw(meow_widget *lv, meow_listview_draw_fn draw, void *ctx);
|
||||
int meow_listview_set_empty_text(meow_widget *lv, const char *text);
|
||||
```
|
||||
> 兼容:既有 `meow_listview_*`(src2 模式)全部保留不动(`src2==NULL` 走既有路径);本层为叠加。
|
||||
> `meow_widget` 尾部追加虚拟化字段(`calloc` 归零,安全);`meow_ui.a` 需重编,App 链新库(老 ABI 不重建名)。
|
||||
> 宿主验证 `test_meow_ui_listvirt`(T1 虚拟化/bind 有界/回滚、T2 多行/截断/图标/尾件、T3 段头/空态、
|
||||
> T4 自绘、T5 焦点/fling 联动)+ 全量回归 widgets/dispatch/box/anim/scroll/ime/pinyin/overlay/notif/
|
||||
> sdk_input_keys/p1p2p3 全 PASS;交叉 -Wall -Wextra 0 warning。
|
||||
|
||||
---
|
||||
|
||||
### 5.6 设计模板 B③ 弹窗/对话框 [源:2026-08-30]
|
||||
@@ -755,6 +801,58 @@ meow_widget *meow_layout_spacer(int w, int h);
|
||||
|
||||
---
|
||||
|
||||
> **TopAppBar 位图动作图标(M2.9x,追加式)**:TopAppBar 动作项默认是**文本**(`meow_topappbar_add_action`)。为支持「⚙↻⌕」类**动作图标**(4k 字库无这些码点,字体码点画图标会出豆腐),新增把某动作项改为**位图图标**的接口:
|
||||
>
|
||||
> ```c
|
||||
> int meow_topappbar_set_item_icon(meow_widget *w, int idx, int icon_idx);
|
||||
> ```
|
||||
> - `idx` = 动作项下标(`meow_topappbar_add_action` 返回值;越界返回负值)。
|
||||
> - `icon_idx` = `sb_icons[]` 位图索引(用 `MEOW_ICON_*` 常量,见下表;<0 非法)。
|
||||
> - 返回 `0`=成功;`NULL`/非 TopAppBar/越界 `idx`/非法 `icon_idx`=负值。
|
||||
> - 绘制:与状态栏**同源**位图(Material Symbols Rounded 32x32 单通道 alpha,`meow_gfx_blit_icon_ink`),
|
||||
> 图标槽在动作格内水平居中、ink 垂直对齐栏中点,颜色取主题 `onSurface`、**按压态向栏底色淡 12%**。
|
||||
> - **兼容**:未调用本 API 的动作项保持文本(行为不变);`items` 文本条目不感知位图化。
|
||||
>
|
||||
> **sb_icons[] 索引表**(Material Symbols Rounded,MD3,单通道 alpha 32x32;Apache-2.0,
|
||||
> 来源 google/material-design-icons,重新生成 `sdk/tools/gen_action_icons.py`):
|
||||
>
|
||||
> | 索引 | 状态栏 `SB_I_*`(SDK 内部) | TopAppBar `MEOW_ICON_*`(App 用) | 图形 | 用途 |
|
||||
> |---|---|---|---|---|
|
||||
> | 0 | `SB_I_WIFI` | — | Wi-Fi | 状态栏 WiFi(满格) |
|
||||
> | 1 | `SB_I_BT` | — | 蓝牙 | 状态栏蓝牙(连接中) |
|
||||
> | 2 | `SB_I_BATT` | — | 电池 | 状态栏电量 |
|
||||
> | 3 | `SB_I_VOL` | — | 音量 | 状态栏音量 |
|
||||
> | 4 | `SB_I_BRIGHT` | — | 亮度 | 状态栏背光 |
|
||||
> | 5 | `SB_I_WIFI_STRONG` | — | Wi-Fi | 状态栏(-60dBm 强) |
|
||||
> | 6 | `SB_I_WIFI_MEDIUM` | — | Wi-Fi | 状态栏(-75dBm 中) |
|
||||
> | 7 | `SB_I_WIFI_WEAK` | — | Wi-Fi | 状态栏(更弱) |
|
||||
> | 8 | `SB_I_WIFI_NO` | — | Wi-Fi | 状态栏(开未连接) |
|
||||
> | 9 | `SB_I_WIFI_OFF` | — | Wi-Fi | 状态栏(关闭) |
|
||||
> | 10 | `SB_I_BT_NO` | — | 蓝牙 | 状态栏(未连接) |
|
||||
> | 11 | `SB_I_BT_CONN` | — | 蓝牙 | 状态栏(已连接) |
|
||||
> | 12 | `SB_I_BT_DIS` | — | 蓝牙 | 状态栏(关闭) |
|
||||
> | 13 | `SB_I_BT_SEARCH` | — | 蓝牙 | 状态栏(搜索中) |
|
||||
> | 14 | `SB_I_BATT_CHG` | — | 电池 | 状态栏(充电) |
|
||||
> | 15 | `SB_I_SETTINGS` | `MEOW_ICON_SETTINGS` | ⚙ 齿轮 | 设置 |
|
||||
> | 16 | `SB_I_REFRESH` | `MEOW_ICON_REFRESH` | ↻ 回转 | 刷新 |
|
||||
> | 17 | `SB_I_SEARCH` | `MEOW_ICON_SEARCH` | 🔍 放大镜 | 搜索 |
|
||||
> | 18 | `SB_I_PLAY_ARROW` | `MEOW_ICON_PLAY_ARROW` | ▶ | 播放 |
|
||||
> | 19 | `SB_I_PAUSE` | `MEOW_ICON_PAUSE` | ⏸ | 暂停 |
|
||||
> | 20 | `SB_I_SKIP_NEXT` | `MEOW_ICON_SKIP_NEXT` | ⏭ | 下一首 |
|
||||
> | 21 | `SB_I_SKIP_PREV` | `MEOW_ICON_SKIP_PREV` | ⏮ | 上一首 |
|
||||
> | 22 | `SB_I_ARROW_BACK` | `MEOW_ICON_ARROW_BACK` | ← | 返回 |
|
||||
> | 23 | `SB_I_CLOSE` | `MEOW_ICON_CLOSE` | × | 关闭 |
|
||||
> | 24 | `SB_I_ADD` | `MEOW_ICON_ADD` | + | 添加 |
|
||||
> | 25 | `SB_I_CHECK` | `MEOW_ICON_CHECK` | ✓ | 勾选 |
|
||||
> | 26 | `SB_I_LYRICS` | `MEOW_ICON_LYRICS` | 歌词 | 歌词 |
|
||||
> | 27 | `SB_I_QUEUE_MUSIC` | `MEOW_ICON_QUEUE_MUSIC` | 队列 | 播放列表 |
|
||||
> | 28 | `SB_I_DOWNLOAD` | `MEOW_ICON_DOWNLOAD` | ↧ | 下载 |
|
||||
> | 29 | `SB_I_HEART` | `MEOW_ICON_HEART` | ♥ | 收藏 |
|
||||
>
|
||||
> **索引约定**:状态栏占用 `0-14`(既有序号,**勿改序**);TopAppBar 动作从 `15` 起连续追加(只追加,既有索引不变)。`MEOW_ICON_*`(公共头 `meow_ui_widgets.h`)与 `SB_I_*`(SDK `sb_icons.h`)**同值**,二者须保持表序一致(`test_umbrella.c` 有 `_Static_assert` 锁 15/21/27/29)。
|
||||
|
||||
---
|
||||
|
||||
### 5.9 音频客户端 API(`meow_audio_*`,M2.90 落地)
|
||||
|
||||
> **权威规范:`devdocs/plan/AUDIOD_DESIGN.md`**(用户定案五项决策)。**已实现**:A1(agent-244/246,audiod+CLI+SDK 客户端,M2.90 入固件)+ A2(流媒体客户端迁 SINK_AUDIOD,e013621)+ 后续批次(M2.91 FPGA 开机武装 / M2.93 长流护栏 / M2.94 几何收窄 / M2.95 音量反转 / M2.96 炸耳修复 / 261 电平监控待 M2.97 固件化)。IPC 号段 `0x00C0-0x00CA` 见 `meow_ipc.h` / docs/MEOWOS_SPEC_REF §4。
|
||||
@@ -881,6 +979,99 @@ void meow_ui_global_remove(int id);
|
||||
|
||||
---
|
||||
|
||||
### 6.4 事件分发形式化(2026-09-02):命中测试 / 捕获 / 消费 / 层间隔离 / 唯一焦点
|
||||
|
||||
> **定位**:把此前各处 ad-hoc 的「命中/捕获/消费/层间隔离/焦点」补丁(模态穿透、弹层叠黑、
|
||||
> list 焦点残留、同坐标二次消费)**上收为单一仲裁者**。三个历史根因(设置页滑条穿透、
|
||||
> Dialog/Sheet 弹层叠黑、list 焦点残留导致主页面滑不动)与「注入事件被下层同坐标控件二次消费」
|
||||
> 都在本节模型内覆盖。**兼容铁律:只追加 API、不删改既有签名**;未调用本节 API 的 App 行为与
|
||||
> 之前完全一致(命中/捕获仍可由 App 在 `on_tap`/`on_touch_*` 自理)。hello/files/reader 等
|
||||
> 既有 App 只把 SDK 提供的语义用于**收敛 bug**,不改正常交互语义。
|
||||
|
||||
#### 6.4.1 分发模型(自上而下命中 + 命中即消费 + down 捕获 + 模态层间隔离)
|
||||
|
||||
触摸事件沿 **z 序自顶向下**命中(模态层**永远先审**);某一层命中即消费(返回 consumed 才继续
|
||||
下探)。一次 **touch-down 命中后捕获整个手势直到 up**——期间 move/up 只发给捕获目标,**防滑出后
|
||||
误触发下层**(根治「手指滑出控件仍点下层」)。**模态层存在时下层完全收不到事件**(含兜底:
|
||||
模态背板未命中的事件由背板消费,不透传)。
|
||||
|
||||
层序(顶 → 底)与 §6.1 三层模型 +0 模态层合成一张表:
|
||||
|
||||
| 层 | 命中语义 | 是否透传 |
|
||||
|---|---|---|
|
||||
| **+0 模态层**(Dialog/Sheet/通知中心/电源菜单等) | 模态始终先审;未命中的事件由**背板消费**(点外=取消/关闭) | **不透传** |
|
||||
| **③ 系统全局**(IME/状态栏/通知中心入口……已在运行循环消费) | 命中即消费 | 返回 1 才透传 |
|
||||
| **② App 跨 Activity 全局**(`meow_ui_global_hit`) | 命中返回 1=消费 | 否则落下层 |
|
||||
| **① 当前 Activity 页面控件**(view/widget,z 降序) | 命中即消费 | 否则让 App 自理 |
|
||||
|
||||
SDK 提供统一仲裁者(`meow_ui_hit_topdown` 沿 z 降序返回首个命中控件,含 `owner==active`+enable
|
||||
过滤)。**模态存在时它恒返回模态哨兵**——下层控件(即使同坐标)绝不会成为捕获/命中目标。
|
||||
|
||||
手势捕获由 `meow_ui_gesture_begin/owner/end` 表达:`meow_ui_run` 在 `MEOW_EV_TOUCH_DOWN` 时
|
||||
自动 `begin`(命中并捕获),随后 move/up 只发给捕获目标(`meow_ui_gesture_owner` 恒定),
|
||||
抬手(TAP/SWIPE/LONG_PRESS/NOTIF_PULL/清队列)自动 `end` 释放。模态期间 begin 捕获的是模态
|
||||
哨兵——下层不被捕获,天然根治穿透。
|
||||
|
||||
#### 6.4.2 焦点模型(唯一持有者 + 点外释放 + 滚动容器转移)
|
||||
|
||||
**焦点持有者唯一**(`meow_ui_focus_owner`)。点非 focus 区域 → **释放焦点并触发通知回调**
|
||||
(`meow_ui_focus_tap`,list 焦点残留根治)。滚动容器(W_LIST/W_LISTVIEW)在
|
||||
`meow_list_tap_at`/`meow_listview_tap_at` 命中行时自动 `meow_ui_focus_acquire`(列表成为唯一
|
||||
焦点);任何落在该容器外的触摸(`meow_ui_focus_tap` 判定 hit != focus owner)释放焦点并通知,
|
||||
回退到页面级滚动。
|
||||
|
||||
**滚动容器间焦点转移规则**:
|
||||
|
||||
1. 某滚动容器(list/lazycolumn/row)获得焦点后,随后的**纵向** swipe / 拖动滚动该容器;
|
||||
2. 任何命中落在该容器**外**的触摸(含空白、页面内容、其它控件)→ 焦点释放 + `lost` 回调,
|
||||
后续拖动滚动**页面**(不再滚列表);
|
||||
3. 焦点释放/转移不销毁容器控件(`meow_widget` 仍由 App/视图持有),下次命中行重新 acquire。
|
||||
|
||||
#### 6.4.3 公共 API(至 `meow_ui_input.h`,仅追加)
|
||||
|
||||
```c
|
||||
/* 模态作用域:dialog/sheet/App 自绘模态「打开时 begin、关闭时 end」;支持嵌套(depth 递增)。 */
|
||||
typedef struct meow_ui_modal meow_ui_modal;
|
||||
meow_ui_modal *meow_ui_modal_begin(void); /* 打开模态层入栈;返回句柄(NULL=栈满) */
|
||||
void meow_ui_modal_end(meow_ui_modal *m); /* 关闭:弹到 m(含)一次性关上嵌套;幂等 */
|
||||
int meow_ui_modal_active(void); /* 当前是否有任一模态作用域 */
|
||||
int meow_ui_modal_depth(void); /* 模态栈深(弹层叠开计数) */
|
||||
|
||||
/* 自上而下命中:z 降序返回首个命中控件;模态存在时恒返回模态哨兵(背板消费,不透传)。 */
|
||||
void *meow_ui_hit_topdown(int x, int y);
|
||||
int meow_ui_widget_hit(meow_widget *w, int x, int y); /* meow_widget_hit 公共别名 */
|
||||
|
||||
/* 手势捕获:down 命中即捕获直到 up;期间 move/up 只给捕获目标(滑出不误触)。 */
|
||||
void *meow_ui_gesture_begin(int x, int y);
|
||||
void *meow_ui_gesture_owner(void);
|
||||
void meow_ui_gesture_end(void);
|
||||
|
||||
/* 唯一焦点:点非 focus 区域 → 释放并通知(list 焦点残留根治)。 */
|
||||
typedef void (*meow_ui_focus_lost_fn)(void *owner, void *ctx);
|
||||
void meow_ui_focus_set_lost_cb(meow_ui_focus_lost_fn cb, void *ctx);
|
||||
void *meow_ui_focus_owner(void);
|
||||
int meow_ui_focus_acquire(void *owner);
|
||||
int meow_ui_focus_release(void *owner);
|
||||
void meow_ui_focus_tap(int x, int y);
|
||||
```
|
||||
|
||||
#### 6.4.4 迁移与兼容
|
||||
|
||||
- **dialog/sheet**(`meow_ui_dialog.c`):`meow_dialog_show`/`meow_sheet_open` 已迁到
|
||||
`meow_ui_modal_begin/end`——模态打开期间 `meow_ui_modal_active()` 为真,下层收不到触摸;
|
||||
点卡片外由背板消费(关闭)。**弹层叠黑**由既有「打开前整帧快照 + 关闭还原 + 高亮仅重绘卡片
|
||||
不叠遮罩」保证(M2.71-bug3),与本节模态层隔离正交、互不影响。
|
||||
- **list/scroll**(`meow_ui_widget.c`):`meow_list_tap_at`/`meow_listview_tap_at` 命中行自动
|
||||
`meow_ui_focus_acquire`,点外由 `meow_ui_run` 的 `meow_ui_focus_tap` 统一释放——list 焦点残留
|
||||
根治下沉到 SDK;App 层不再需要各自「点非 list 区域手动清 focus」补丁。
|
||||
- **注入/叠加让位**:`overlay_input_suppressed`(§10.5)是**系统叠加层**的层间隔离(面板输入由
|
||||
launcher 接管、App 排空副本),与本节模态层隔离互补共同防「同坐标二次消费」,语义不变。
|
||||
- 以上均为**行为收敛**(修 bug),正常交互语义不变;`p1p2p3_repro`/`widgets`/`ime_fix`/`pinyin`/
|
||||
`overlay_suppress`/`notif_gesture`/`sdk_fixes`/`sb_collapse`/`box`/`anim`/`scroll`/`sdk_input_keys`
|
||||
全量回归通过。
|
||||
|
||||
---
|
||||
|
||||
## 7. 动画/时间(B1 最小可用)
|
||||
|
||||
当前无动画框架。B1 提供**帧驱动回调 + 内置时间**,应用据此做低成本的帧序列/淡入淡出:
|
||||
@@ -1073,7 +1264,188 @@ patch_v2/meow_launcherd/meow_font.a # 复用(静态链接)
|
||||
|
||||
---
|
||||
|
||||
## 12. 遗留 / 待实现
|
||||
## 12. 控件层扩展:可组合布局引擎 / 动画基座 / 滚动物理
|
||||
|
||||
> **性质**:三块**独立增量**(对照 §5.8.5 的「固定尺寸 Row/Column/Box」与 §7 的「B1 最小动画」做升级),
|
||||
> **全部追加式**:新头 `include/meow_ui_box.h` / `include/meow_ui_anim.h` / `include/meow_ui_scroll.h` +
|
||||
> 新 TU `meow_ui_box.c` / `meow_ui_anim.c` / `meow_ui_scroll.c`;**不改任何既有 API 签名/行为**。
|
||||
> 既有控件仍是**绝对定位**(x,y,w,h);布局只是「帮你算坐标」的容器,不改变绘制/命中/状态机。
|
||||
> 旧绝对坐标**永远有效**——不用这三块的 App 完全照旧。
|
||||
|
||||
### 12.1 线性布局引擎(`meow_box`)
|
||||
|
||||
在既有 `meow_row/column/box`(§5.8.5,只支持固定尺寸+居中)之上,提供更完整的线性布局:
|
||||
|
||||
- **容器**:竖直(`MEOW_BOX_V`)/ 水平(`MEOW_BOX_H`)。
|
||||
- **子项尺寸策略**:固定(`MEOW_BOX_FIXED`,取控件当前 w/h)| 权重(`MEOW_BOX_WEIGHT`,均分剩余主轴空间)| wrap(`MEOW_BOX_WRAP`,按内容测量)。
|
||||
- **间距/边距**:容器 `padding`(left/top/right/bottom)+ 项间 `gap`;子项 margin 读取既有 `meow_widget_opts.margin_x/y`。
|
||||
- **主轴对齐**:start / center / end / space-between / space-around / space-evenly。
|
||||
- **交叉轴对齐**:start / center / end / stretch(填满容器交叉向减 margin)。
|
||||
- **嵌套**:box 内可再挂子 box(`meow_box_add_sub`),求解深度 ≤ `MEOW_BOX_MAX_DEPTH`(16,静态栈,无递归爆炸)。
|
||||
|
||||
```c
|
||||
typedef struct meow_box meow_box;
|
||||
typedef enum { MEOW_BOX_H = 0, MEOW_BOX_V = 1 } meow_box_dir;
|
||||
typedef enum { MEOW_BOX_FIXED = 0, MEOW_BOX_WEIGHT = 1, MEOW_BOX_WRAP = 2 } meow_box_size;
|
||||
typedef enum {
|
||||
MEOW_BOX_ALIGN_START = 0, MEOW_BOX_ALIGN_CENTER, MEOW_BOX_ALIGN_END,
|
||||
MEOW_BOX_ALIGN_SPACE_BETWEEN, MEOW_BOX_ALIGN_SPACE_AROUND, MEOW_BOX_ALIGN_SPACE_EVENLY,
|
||||
} meow_box_align;
|
||||
typedef enum { MEOW_BOX_CROSS_START = 0, MEOW_BOX_CROSS_CENTER, MEOW_BOX_CROSS_END, MEOW_BOX_CROSS_STRETCH } meow_box_cross;
|
||||
|
||||
meow_box *meow_box_new(meow_box_dir dir); /* 命名避开既有 meow_box_create(W_LAYOUT) */
|
||||
void meow_box_free(meow_box *b);
|
||||
void meow_box_set_rect(meow_box *b, int x, int y, int w, int h); /* 容器矩形(绝对坐标) */
|
||||
void meow_box_set_padding(meow_box *b, int l, int t, int r, int btm);
|
||||
void meow_box_set_gap(meow_box *b, int gap);
|
||||
void meow_box_set_align(meow_box *b, int main_align, int cross_align);
|
||||
int meow_box_add(meow_box *b, meow_widget *w, int size, int weight); /* 固定/权重 */
|
||||
int meow_box_add_ex(meow_box *b, meow_widget *w, int size, int weight,
|
||||
int min, int preferred, meow_box_measure_fn m, void *mctx); /* WRAP 测量 */
|
||||
int meow_box_add_sub(meow_box *b, meow_box *sub, int size, int weight); /* 嵌套 */
|
||||
|
||||
/* 单遍求解:把每个子控件 x/y/w/h 写进 meow_widget(新增 meow_widget_set_rect);深度≤16 静态栈。
|
||||
* 子控件 margin(widget opts.margin_x/y)参与排布;嵌套 sub 递归。 */
|
||||
void meow_box_arrange(meow_box *b);
|
||||
```
|
||||
|
||||
- **与视图框架集成(opt-in)**:box 可作 `meow_view` 的布局提供者;view **不设**就保持现有绝对坐标行为。
|
||||
`meow_view_set_layout(v, box)` 登记 + 立即排一次;`meow_view_relayout(v)` 手动重排;
|
||||
顶层页在 `meow_view_page_push/pop/replace` 进入时自动重排。box 矩形默认跟 view 内容矩形(w/h 未设时自动填)。
|
||||
```c
|
||||
void meow_view_set_layout(meow_view *v, meow_box *box); /* NULL=取消 */
|
||||
void meow_view_relayout(meow_view *v);
|
||||
```
|
||||
- **控件尺寸测量**:`MEOW_BOX_FIXED`/`WRAP`(无 measure 回调)直接取控件当前 `w/h`(对既有固定尺寸控件天然兼容);
|
||||
`WRAP` 可用 `meow_box_measure_fn`(给出主轴向 min/preferred)供内容自适应的自定义控件用。
|
||||
- **内存纪律**:box 静态池、定长子项数组(`MEOW_BOX_MAX_CHILD=24`),arrange 热路径零 malloc;嵌套用深度≤16 静态栈。
|
||||
|
||||
### 12.2 动画基座 + UI 节拍器(`meow_anim` / `meow_anim_tick`)
|
||||
|
||||
把 §7「应用在 on_tick 里用 `meow_ui_time_ms` 做时间轴」收敛成**声明式 tween**:
|
||||
|
||||
- **缓动**:linear / ease-out / ease-in-out / overshoot(`meow_ease_apply`)。
|
||||
- **动画对象**:`meow_anim_start` 一次性声明 from/to/duration/easing + `update`(每帧插值回调)+ `done`(完成/取消回调);**并行多动画**(静态池 ≤ `MEOW_ANIM_MAX=16`)、**取消**(`meow_anim_cancel`)。
|
||||
- **节拍**:`meow_anim_tick()` 挂既有 `meow_ui_run` 主循环(on_tick 之后)自动调用;**无动画时零开销**(只查计数即返回),不引入常驻高频轮询。阻塞 IME 循环(`meow_ui_feed_tick`)不喂 tick(动画在模态期暂停)。
|
||||
- **与脏区集成**:`meow_anim_set_region(a,x,y,w,h)` 让每帧**只 flush 动画影响区**,不做整屏重绘;app 的 `update` 回调也可自行 `meow_ui_flush_region`。
|
||||
|
||||
```c
|
||||
typedef enum { MEOW_EASE_LINEAR = 0, MEOW_EASE_OUT, MEOW_EASE_IN_OUT, MEOW_EASE_OVERSHOOT } meow_ease;
|
||||
float meow_ease_apply(meow_ease e, float t); /* t∈[0,1]→[0,1];overshoot 中后段可略>1、末点=1 */
|
||||
|
||||
typedef struct meow_anim meow_anim;
|
||||
typedef void (*meow_anim_update_fn)(meow_anim *a, float v, void *ctx);
|
||||
typedef void (*meow_anim_done_fn)(meow_anim *a, int canceled, void *ctx);
|
||||
typedef struct meow_anim_desc {
|
||||
float from, to; uint32_t duration_ms; meow_ease ease;
|
||||
meow_anim_update_fn update; meow_anim_done_fn done; void *ctx;
|
||||
} meow_anim_desc;
|
||||
|
||||
meow_anim *meow_anim_start(const meow_anim_desc *d);
|
||||
void meow_anim_cancel(meow_anim *a);
|
||||
int meow_anim_active(meow_anim *a);
|
||||
float meow_anim_value(meow_anim *a);
|
||||
void meow_anim_set_region(meow_anim *a, int x, int y, int w, int h);
|
||||
void meow_anim_tick(void); /* 推进所有活动动画(挂主循环;无动画零开销) */
|
||||
unsigned meow_anim_count(void);
|
||||
```
|
||||
|
||||
- **与 §7 的关系**:§7(B1 最小可用)是手写时间轴;本模块是正式基座。应用可任选其一或混用。
|
||||
- **内存纪律**:静态池 ≤16,start/cancel/tick 全程零 malloc。
|
||||
|
||||
### 12.3 滚动物理(`meow_scroll`)
|
||||
|
||||
给既有滚动容器(`meow_list` / `W_LIST` lazycolumn/lazyrow / `meow_listview`)加**可惯性滚动**物理层:
|
||||
|
||||
- **速度跟踪**:`meow_scroll_track(dy, t)`(拖动跟手 + 采样),fling 时用**最近 N 个采样加权**得初速。
|
||||
- **松手 fling**:`meow_scroll_fling(s)` 起一条减速度曲线动画(用 §12.2 动画基座驱动)推进位移;每帧**只 redraw 列表视口区**(不整屏)。
|
||||
- **边界过冲 clamp + 简单回弹**:free 目标越界 → clamp 到 `[0,max]`;快速撞墙补两段短 IN_OUT 回弹(不做复杂 spring)。
|
||||
- **滚动停止自动归位**:`meow_scroll_set_snap(step)` 可选吸附到 step 倍数。
|
||||
- **跟手拖动行为保持现状不变**:`track` 内部调既有 `meow_list(viewport)_set_scroll`(clamp 一致),与 app 直接 `scroll_by` 等价;app 把拖动的 `scroll_by` 换成 `track` 即可获得速度采样。
|
||||
|
||||
```c
|
||||
typedef struct meow_scroll meow_scroll;
|
||||
meow_scroll *meow_scroll_create(meow_scroll_set_fn set, meow_scroll_max_fn max,
|
||||
meow_scroll_get_fn get, meow_scroll_redraw_fn redraw, void *ctx);
|
||||
meow_scroll *meow_scroll_wrap(meow_widget *list, int is_listview); /* 1=W_LISTVIEW / 0=W_LIST 或 lazycolumn/row */
|
||||
void meow_scroll_free(meow_scroll *s);
|
||||
void meow_scroll_track(meow_scroll *s, int dy, uint64_t t_ms); /* 跟手拖动 + 速度采样 */
|
||||
void meow_scroll_fling(meow_scroll *s); /* 松手惯性滑动(内部算初速) */
|
||||
void meow_scroll_stop(meow_scroll *s); /* 立即停止 */
|
||||
int meow_scroll_velocity(meow_scroll *s); /* 加权初速 px/s */
|
||||
int meow_scroll_active(meow_scroll *s);
|
||||
int meow_scroll_offset(meow_scroll *s);
|
||||
void meow_scroll_set_snap(meow_scroll *s, int step);
|
||||
void meow_scroll_set_friction(meow_scroll *s, int tau_ms); /* 默认 180ms */
|
||||
```
|
||||
|
||||
- **内存纪律**:静态池(应用侧按需建会话),track/fling/tick 全程零 malloc;无 fling 时 meow_scroll 不参与节拍(由 `meow_anim_tick` 活动计数保证零开销)。**不引 libm**(SDK 免 `-lm` 约定)。
|
||||
|
||||
### 12.4 对既有 App 的零影响(迁移说明)
|
||||
|
||||
- **追加式**:三块全为新 API;既有 `meow_row/column/box`(§5.8.5)与全部 `meow_*_create/hit/scroll` 签名一行未改。
|
||||
- **旧绝对坐标永远有效**:box 只是把坐标算进 `meow_widget`(`meow_widget_set_rect`);不用 box 的 App 照旧 `on_layout`/绝对定位。
|
||||
- **机制层新增**:`meow_ui_input.c` 主循环新增 `meow_anim_tick()` 调用(无动画零开销);`meow_ui_view.c` 新增布局登记表(未设布局的 view 行为不变);`meow_ui_widget.c` 新增 `meow_widget_set_rect`(只设字段不标脏,既有 `meow_layout_arrange` 不受影响)。
|
||||
- **同步重编注意**:`meow_ui.a` 内容变化(新增 box/anim/scroll 三个 `.o`,`meow_ui_widget.o`/`meow_ui_view.o`/`meow_ui_input.o` 重编)→ 链 `meow_ui.a` 的 App 需**重链**(`build_sdk.sh` 一键重编 + 各 App `build_*.sh` 重链)。既有 App **不改源码**即可零回归。
|
||||
|
||||
---
|
||||
|
||||
## 13. 字符串资源 / i18n(`meow_str_*`,2026-09-02 追加)
|
||||
|
||||
> **性质**:字符串资源外置(i18n 雏形)——公开 SDK 前,SDK/App 的 UI 文案不再硬编码在 C 源码:第三方开发者按系统 locale 取对应语言串,且可不改 SDK 二进制用 TF 卡覆盖/追加。**追加式**:新头 `include/meow_ui_str.h` + 新 `.o` `meow_ui_str.c`;既有 `meow_ui.h` 伞头自动聚合(仅新增 #include),既有 API/控件签名一行未改。
|
||||
|
||||
### 13.1 API(追加式,纯 libc,宿主 gcc 可直接单测)
|
||||
|
||||
```c
|
||||
const char *meow_str(const char *id); /* 按当前 locale 取 id 文案;静态串,永不 NULL;未知 id 原样返回 id */
|
||||
int meow_strf(char *out, int outsz, const char *id, ...); /* 取串后按 snprintf 语义格式化进 out(自带 NUL);out==NULL/outsz<=0→-1 */
|
||||
void meow_str_set_locale(const char *locale); /* 切 locale("zh_cn"|"en_us"),即时生效 + 重载 TF 覆盖表 + best-effort 持久化 ui.locale */
|
||||
const char *meow_str_locale(void); /* 当前生效 locale(缺省 "zh_cn") */
|
||||
```
|
||||
|
||||
> `meow_strf` 用**调用方自带缓冲**(而非静态/线程局部),避免 IME/模态等**重入路径**的缓冲竞态,且与 SDK 既有 `snprintf` 习惯一致(无格式参数时等价深拷贝 `meow_str`)。这是把任务书 `meow_strf(id, ...)` 落地为 `(out, outsz, id, ...)` 的定案(可重入、线程安全优先于少一个参数)。
|
||||
|
||||
### 13.2 取值优先级(四层,逐层回落,永不返回 NULL)
|
||||
|
||||
1. 当前 locale 的 **TF 覆盖表**:`MeowData/<pkg>/strings/<locale>.txt`,`id=text` 行格式 UTF-8,`meow_str_set_locale` 加载进静态数组(≤256 条);
|
||||
2. 内嵌表**当前语言**(`en_us→en`,其余→`zh`);
|
||||
3. 内嵌表**内置默认**(`zh_cn`,locale 缺省/未知语言回落);
|
||||
4. 仍不可得 → **原样返回 `id`**(调用方可展示 id/打点,绝不见 NULL)。
|
||||
|
||||
> 无卡/无覆盖文件/无该 id → 自动回落**内嵌双语默认表**(编译进二进制,必可用)。内嵌表含 `common.*`(确定/取消/返回/确认/完成/保存/删除/编辑/搜索/输入/请输入/请选择/加载中/暂无数据/重试/失败/重试失败/关闭/未知/设置/点击确定编辑,共 21 条)+ `ime.*`(空格/确定/取消/拼英/?123/清/拼音/(无候选)/中,共 9 条),`zh_cn`+`en_us` 双语。
|
||||
|
||||
### 13.3 ID 规范
|
||||
|
||||
`域.名`(`common.ok` / `common.cancel` / `player.pause`)。SDK 自带 `common.*` 通用组 + `ime.*` IME 键面组;第三方可自建 `域`(如 `player.*`),经 TF 覆盖表追加,不改 SDK 二进制。
|
||||
|
||||
### 13.4 locale 存系统设置
|
||||
|
||||
locale 权威值 = 板载 `settings.conf` 的 `ui.locale`(默认 `zh_cn`)。SDK 首次调用**惰性读取**;`meow_str_set_locale` 即时切换(运行时)并 **best-effort 持久化**回 `ui.locale`(流式改写、保留其它行——绝不 O_TRUNC 整文件;失败静默,切换仍生效)。SDK 与 launcher 系统组件读同一份,后续系统 UI 复用同键即当前语言。
|
||||
|
||||
### 13.5 性能
|
||||
|
||||
**启动时一次加载**到静态线性表:内嵌为编译期静态表;覆盖表为启动/切 locale 时的静态数组(≤256 条)。`meow_str` 只做 O(n) 线性扫描(表小可接受),**热路径零 malloc**(无堆申请、无引用计数)。
|
||||
|
||||
### 13.6 SDK 控件接入
|
||||
|
||||
- **dialog**(§5.8.6):默认按钮 `"CANCEL"/"OK"` → `meow_str("common.cancel"/"common.ok")`;INPUT 预览占位 `"(tap OK to edit...)"` → `meow_str("common.tap_ok_edit")`。
|
||||
- **IME**(§5.1):拼音键面/徽标(空格/确定/拼英/?123/清/拼音/(无候选))与 ASCII/NUM 顶条「中」→ `meow_str("ime.*")`。
|
||||
- **空态/加载态**:SDK 不硬编码空态文案;`common.loading`/`common.no_data` 供 App 空态/加载中直接用(如 `meow_listview_set_empty_text(w, meow_str("common.no_data"))`)。
|
||||
- **launcher 等系统组件**:后续经同框架接入(本任务只管 SDK 侧;launcher 复用同 `settings.conf ui.locale`)。
|
||||
|
||||
### 13.7 边界(明示不做,写清理由)
|
||||
|
||||
- **不做复数规则**:需占位符格式化协议 + 每语言复数表,超出「按 locale 取整串」的最小必要范围;
|
||||
- **不做 BiDi / RTL / 动态字体回退**:需文本整形 + 布局镜像 + 字体回退策略,面向 480×720 单行/短文案场景暂不需要;
|
||||
- 当前形态为 i18n **雏形**,复杂变体(复数/语言方向/字体重载)留后续批次;locale 仅 `zh_cn`/`en_us` 双语(任意串作覆盖表文件名,内嵌按非 `en` 视为内置默认)。
|
||||
|
||||
### 13.8 同步重编注意
|
||||
|
||||
`build_sdk.sh` OBJS 新增 `meow_ui_str`;`meow_ui_str.o`(含内嵌表)+ `meow_ui_dialog.o`/`meow_ui_ime.o` 重编 → **链 `meow_ui.a` 的 App 需重链**(一键 `build_sdk.sh`)。既有 App **不改源码**即零回归(仅按钮/键面文案随 locale 变化)。宿主回归测试需在编译命令补 `meow_ui_str.c`(各 test_*.c 头注释已同步)。
|
||||
|
||||
---
|
||||
|
||||
## 14. 遗留 / 待实现
|
||||
|
||||
1. `meow_ui` 渲染后端需从 launcher 里**抽公共绘制实现**(`back_buf`/`meow_dirty_*`/`meow_present`/`meow_font_bind` 目前是 launcher 静态函数——需提取为可链接的 `meow_ui_gfx` 或改为共享 obj,注意 launcher 与 app 是**不同进程**,不能共享同一 back_buf;`meow_ui.a` 是**独立库**,内部自建 fb 上下文,不复用 launcher 的全局 `g_dirty`)。
|
||||
2. 输入线程优先级:vs 主循环 10ms 热键轮询的竞态,需在 B1 实现时定义清楚(应用进程内独立事件循环,不与 launcher 抢)。
|
||||
|
||||
Reference in new issue
Block a user