sdk(M7.0a): 视图事件接线 + IconButton + 主题色板导出/注入(launcher 重写基座前置)
M7 launcher 整体 MD3 重写的 SDK 基座补全(只动 patch_v2/sdk/): - 缺口#7 meow_view 事件接线:meow_view_dispatch_event 接入 meow_ui_run 主循环 (§6.4 分流之上,TAP/KEY/SWIPE/LONG_PRESS 先经 view 路由、命中即消费、未命中回落 on_* —— 无 view 树/未命中完全向后兼容);meow_view_add_child 在父已进入时挂子视图 立即进入(递归 children 修正);meow_view_sub_push/pop 标注废弃(设计定案 b 不做页内 子导航栈)。 - 缺口#2 IconButton:meow_iconbtn_create —— sb_icons 位图 + 圆形命中 + 按压/按住态 (实时,同 meow_btn)+ owner/enabled 闸门;电源菜单/播放控制/锁屏通用件。 - 缺口#6 SDK 半主题收口:meow_theme_export/meow_theme_inject(§8.1.5 色板导出/注入), launcher s_ui_theme() 将据此变读 SDK 的薄壳(launcher 侧改动不在此批)。 - 文档:MEOW_APP_SDK.md 补 meow_iconbtn_create + meow_theme_export/inject 说明。 - 验证:新增 test_meow_ui_iconbtn / test_meow_ui_theme_export / test_meow_ui_view_event 全 PASS;全量 SDK 宿主回归(widgets/dispatch/box/anim/scroll/listvirt/str/ime/pinyin/ overlay/notif/input_keys/p1p2p3/umbrella/ft)全 PASS;交叉 -Wall -Wextra 0 warning。 meow_ui.a md5: be24214e24faadba63577f3a98c2c9d1 -> 7fc83f7731cadb2f91df1c3ed7d3814f
This commit is contained in:
1 parent
e67cc50afa
commit
a8c51be1fc
12 files changed
+595
-11
No files matched your search
@@ -765,8 +765,25 @@ meow_widget *meow_layout_spacer(int w, int h);
|
||||
| BottomSheet | 底部面板(复用 §5.7 B④ 面板) | ✅ 第二批 |
|
||||
| Dialog | 弹窗(复用 §5.6 B③ 弹窗) | ✅ 第二批 |
|
||||
| FloatingActionButton | 悬浮操作钮(圆角增强;无投影,仅描边/状态层近似) | ✅ 第二批 |
|
||||
| IconButton | 圆形/位图图标按钮(圆形命中 + 按压态;电源菜单/播放控制/锁屏通用) | 🆕 M7.0a |
|
||||
| 列表容器 | 可滚动列表(复用 §5.5 A② listview) | ✅ 第二批 |
|
||||
|
||||
> **IconButton(M7.0a,`meow_iconbtn_create`)**:把「位图图标 + 圆形命中 + 按压态」收敛为单一组件,
|
||||
> 供电源菜单圆钮 / Now Playing 播放控制 / 锁屏常用件等「图标圆钮」使用(launcher M7 控件化缺口 #2)。
|
||||
>
|
||||
> ```c
|
||||
> meow_widget *meow_iconbtn_create(int x, int y, int size, int icon_idx);
|
||||
> ```
|
||||
> - `x,y,size`:按钮外接正方形(`w=h=size`,圆心 = `(x+size/2, y+size/2)`,半径 = `size/2`)。
|
||||
> - `icon_idx`:`sb_icons[]` 位图下标(用 `MEOW_ICON_*` 或 `SB_I_*`;`<0` 画空位图、命中照常)。
|
||||
> - **命中**:圆形(`meow_widget_hit` 走半径判定,角部按下不点亮);`owner==active` + `enabled` 双闸门。
|
||||
> - **按压/按住态**:与 `meow_btn` 同源三路(状态机 `MEOW_WS_PRESSED` / 实时触摸命中 / `w->pressed`),
|
||||
> 经 `meow_widget_touch_refresh` 即时重绘(按压叠 `onSurface` 12%、按住 24%,禁用向 `onSurface` 38% 变淡)。
|
||||
> - **语义色**:底 `surfaceContainerLow`、图标 `onSurface`;选中(`meow_widget_set_selected`)→
|
||||
> `primaryContainer`/`onPrimaryContainer`;焦点用圆形描边(`ring_arc`)。
|
||||
> - **图标绘制**:与状态栏/TopAppBar 同源位图(`meow_gfx_blit_icon_ink`,Material Symbols Rounded 单通道 alpha),
|
||||
> ink 高 = `size*6/10`(MD3 24/40 图标比),垂直居中于圆心,只写像素不标顶栏脏区(控件 `clip` 管界)。
|
||||
|
||||
> **说明**:多数 Material 组件是**既有设计模板(§5.2-§5.7)的「MD3 观感化」**——如 Card=圆角容器、BottomSheet 复用 §5.7 面板、Dialog 复用 §5.6 弹窗、TabBar 联动 §5.2 分页、列表容器复用 §5.5 listview。故 Material 子集**不重复造结构**,只把这些模板的绘制观感对齐 MD3 语义色 + 统一基座。**第二批已实现(M2.71/0.7.66,见 §5.8.8)**。
|
||||
|
||||
> **TopAppBar 位图动作图标(M2.9x,追加式)**:TopAppBar 动作项默认是**文本**(`meow_topappbar_add_action`)。为支持「⚙↻⌕」类**动作图标**(4k 字库无这些码点,字体码点画图标会出豆腐),新增把某动作项改为**位图图标**的接口:
|
||||
@@ -1160,6 +1177,27 @@ int meow_ui_style(void); /* 当前 style(默认 MEOW_STYLE_MD3)
|
||||
const meow_theme *meow_theme_get(void);
|
||||
```
|
||||
|
||||
> **色板导出/注入(§8.1.5,M7.0a 主题收口前置)**:M7 用户定案「**SDK `meow_theme` 为唯一权威**」——
|
||||
> launcher 侧 `s_ui_theme()`(`font_theme.c` 4 套静态 `meow_ui_theme_t`)将改为读 SDK 的**薄壳**,不再自持色板。
|
||||
> SDK 侧为此前置提供「导出 / 注入」一对接口(launcher 薄壳改造不在本批):
|
||||
>
|
||||
> ```c
|
||||
> /* 导出:把当前「style×mode」解析生效的完整权威主题拷给调用方(单一权威来源)。 */
|
||||
> void meow_theme_export(meow_theme *out);
|
||||
> /* 注入:用一份自定义板覆写当前生效主题;NULL 恢复内建(随 settings 解析)。 */
|
||||
> void meow_theme_inject(const meow_theme *src);
|
||||
> ```
|
||||
> - **launcher 薄壳配方**:`s_ui_theme()` 调 `meow_theme_export(&th)`,再按角色字段映射到自己渲染用的
|
||||
> `meow_ui_theme_t`(9 色)——`surface→surface`、`surfaceVariant→surface_variant`、
|
||||
> `surfaceContainer→surface_container`、`primary→primary`、`onSurface→on_surface`、
|
||||
> `onSurfaceVariant→on_surface_variant`、`outline→outline`、`error→error`、`surfaceTint→surface_tint`。
|
||||
> 全部色值取自 SDK,**单一权威**;launcher 自持的 4 套静态色板退役。
|
||||
> - **注入**:供需要程序化覆写的调用方(如 launcher 自定义背景/强调、或 demo 预览另一调色板);
|
||||
> 覆写**不随** `meow_theme_reload()` 丢弃(程序化覆写优先于 settings,与 `meow_ui_set_style` 语义一致);
|
||||
> `inject(NULL)` 恢复内建。
|
||||
> - **一致性**:导出/注入/`meow_theme_get()` 三者同源(导出=拷贝 `meow_theme_get()` 结果;注入接管
|
||||
> `meow_theme_get()` 返回),保证「一处权威、处处同值」。
|
||||
|
||||
- **设置页联动**:设置页 `ui.style` → `meow_ui_set_style` → 框架**全局刷新**(状态栏 SDK 全局 / 未声明背景的 App / launcher 主页,§8.1.4)。App 在 `on_tick` 据通知重绘(`meow_ui_flush_region`,沿用 `meow_theme_listener` 机制)。
|
||||
- **明暗联动**:`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` 等价(待定案)。
|
||||
@@ -1505,6 +1543,7 @@ locale 权威值 = 板载 `settings.conf` 的 `ui.locale`(默认 `zh_cn`)。
|
||||
- [D3] 新增内部 `meow_gfx_fill_round/blit_rgba/mark_dirty`(控件/图片用,非公共 API)。
|
||||
- [D4] 新增 `meow_theme_defaults(mode)`(脱机取内置色板)。
|
||||
- [D5] 新增 `on_swipe_progress` 回调 + `meow_ui_swipe_hint(x)`(左缘返回手势视觉条)+ 内部 `MEOW_EV_SWIPE_PROGRESS` / `meow_input_inject`(宿主验证用)。
|
||||
- [D6](M7.0a)新增 `meow_iconbtn_create`(圆形/位图图标按钮,§5.8.6)+ `meow_theme_export`/`meow_theme_inject`(§8.1.5 色板导出/注入);`meow_view_dispatch_event` 接入 `meow_ui_run` 主循环(§6.4 之上,TAP/KEY/SWIPE/LONG_PRESS 先经 view 路由、命中即消费、未命中回落 `on_*`);`meow_view` 的 enter/leave 递归 children 已实现 + `meow_view_add_child` 在父进入后挂子立即进入;`meow_view_sub_push/pop` 标注为废弃(设计定案 b 不做页内子导航栈)。
|
||||
- `meow_ui.h` 为对外权威签名;本文件 §3-§8 作为设计蓝本。
|
||||
|
||||
---
|
||||
|
||||
@@ -62,6 +62,28 @@ void meow_theme_remove_listener(meow_theme_listener cb);
|
||||
/* [D4] 脱机访问两套内置色板(无 settings 文件也可靠):返回当前解析生效的色板副本。 */
|
||||
meow_theme meow_theme_defaults(meow_theme_mode m);
|
||||
|
||||
/* ---- §8.1.5 色板导出/注入(单一权威桥,M7.0a)----
|
||||
* 用户在 M7 定案「SDK meow_theme 为唯一权威」:launcher 的 s_ui_theme() 将改为读 SDK 的薄壳,
|
||||
* 不再自持 4 套静态色板。本接口是那个前置(launcher 侧改动不在此批)——
|
||||
*
|
||||
* meow_theme_export(out) 把当前「style×mode」解析生效的完整权威主题拷给调用方。
|
||||
* 调用方是 stateless 的绘制原语时(如 launcher s_ui_theme),由此读
|
||||
* 单一权威,再按它的角色字段(9 色)映射到自己的绘制便利结构,即:
|
||||
* surface → surface
|
||||
* surfaceVariant → surface_variant
|
||||
* surfaceContainer → surface_container
|
||||
* primary → primary
|
||||
* onSurface → on_surface
|
||||
* onSurfaceVariant → on_surface_variant
|
||||
* outline → outline
|
||||
* error → error
|
||||
* surfaceTint → surface_tint
|
||||
* meow_theme_inject(src) 用一份自定义板覆写当前生效主题(如 launcher 自定义背景/强调/风格),
|
||||
* src==NULL 恢复内建(随 settings 解析)。覆写不随 meow_theme_reload() 丢弃
|
||||
* (程序化覆写优先于 settings,与 meow_ui_set_style 语义一致)。 */
|
||||
void meow_theme_export(meow_theme *out);
|
||||
void meow_theme_inject(const meow_theme *src);
|
||||
|
||||
/* ---- §8.1 顶级主题风格族(minimal,2026-08-30)----
|
||||
* 主题 = 顶级主题风格族(ui.style:MD3/MIUIx)× 明暗(display.theme)。本批只落「状态栏图标集
|
||||
* 随 ui.style 换集」的最小闭环:meow_ui_set_style 记录 style(同值忽略)+ 触发 theme_reload/
|
||||
|
||||
@@ -210,6 +210,17 @@ enum {
|
||||
MEOW_ICON_HEART = 29,
|
||||
};
|
||||
|
||||
/* ---- IconButton(圆形/位图图标按钮,M7.0a 缺口#2)----
|
||||
* 圆形命中 + 位图图标(sb_icons[],Material Symbols Rounded 单通道 alpha)+ 按压态(实时)。
|
||||
* 用途:电源菜单圆钮 / Now Playing 播放控制 / 锁屏常用件等「位图图标 + 圆形」按钮。
|
||||
* icon_idx = sb_icons[] 下标(用上面 MEOW_ICON_* 枚举或 SB_I_*;越界画空位图,命中照常)。
|
||||
* size = 按钮直径(外接正方形 w=h=size;图标 ink 高 = size*6/10,MD3 图标比)。
|
||||
* 语义色:底 surfaceContainerLow、图标 onSurface;按压叠 onSurface 状态层(实时,同 meow_btn);
|
||||
* 禁用变淡;选中态 primaryContainer/onPrimaryContainer(meow_widget_set_selected)。
|
||||
* 命中:圆形(半径=size/2,圆心=按钮中心),owner==active + enabled 双闸门。
|
||||
* 返回按钮句柄;App 在 on_tap 里用 meow_widget_hit 判定是否命中本钮(命中即触发动作)。 */
|
||||
meow_widget *meow_iconbtn_create(int x, int y, int size, int icon_idx);
|
||||
|
||||
/* ---- NavigationBar(底部导航栏)---- */
|
||||
meow_widget *meow_navigationbar_create(int x, int y, int w, int h);
|
||||
int meow_navigationbar_add_item(meow_widget *w, const char *label);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "meow_ui_sys.h"
|
||||
#include "meow_ui_vol.h"
|
||||
#include "meow_ui_media.h"
|
||||
#include "meow_ui_view.h" /* M7.0a 缺口#7:把 meow_view_dispatch_event 接入 meow_ui_run */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -714,8 +715,14 @@ void meow_ui_run(const meow_ui_events *ev)
|
||||
/* §6.1 触摸分流:③ 系统全局(IME/状态栏…已在上面消费) → ② App 全局控件
|
||||
* (命中返回 1=消费,否则落到 ① active Activity 页面控件)。 */
|
||||
if (meow_ui_global_hit(e.a, e.b)) continue;
|
||||
/* M7.0a 缺口#7:meow_view 事件路由(§A.7)——有活动页 view 子树且其 on_event
|
||||
* 命中即消费(不再落 App on_tap);无 view 树/未命中返回 0 → 回落 on_tap 原路径。 */
|
||||
if (meow_view_dispatch_event(MEOW_EV_TAP, e.a, e.b)) continue;
|
||||
if (g_events_set && g_events.on_tap) g_events.on_tap(e.a, e.b);
|
||||
} else if (e.type == MEOW_EV_KEY) {
|
||||
/* M7.0a 缺口#7:先进 view 路由,命中即消费;否则回落 App on_key(语义不变——
|
||||
* 音量/媒体系统键已在输入线程消化,这里只决定 App/页面是否收到语义事件)。 */
|
||||
if (meow_view_dispatch_event(MEOW_EV_KEY, e.a, e.b)) continue;
|
||||
if (g_events_set && g_events.on_key) g_events.on_key(e.a, e.b);
|
||||
} else if (e.type == MEOW_EV_SWIPE) {
|
||||
if (in_startup) continue; /* 启动残留被当左缘滑动:丢弃,防进入即返回 */
|
||||
@@ -725,6 +732,9 @@ void meow_ui_run(const meow_ui_events *ev)
|
||||
/* 拼音 IME 候选栏横滑翻页(英文模式 handle_event 返回 0 → 沿用既有行为)。 */
|
||||
if (meow_ime_active() && meow_ime_handle_event(&e)) continue;
|
||||
meow_ui_gesture_end(); /* §6.4:抬手(滑动)边沿,释放手势捕获 */
|
||||
/* M7.0a 缺口#7:view 路由先于 App 窗口的 on_swipe 与默认返回手势——页 view
|
||||
* 可消费滑动(页内返回/翻页/横幅);命中即跳过下面对 on_swipe/默认退出的分发。 */
|
||||
if (meow_view_dispatch_event(MEOW_EV_SWIPE, e.a, e.b)) continue;
|
||||
/* A-0 全局左缘返回(方案A):
|
||||
* - APP 注册 on_swipe → APP 接管,自行决定(meow_ui_request_exit / 页内返回);
|
||||
* - APP 未注册 on_swipe 且非全屏(sb_active)→ SDK 默认退出 app(返回=退出);
|
||||
@@ -783,6 +793,8 @@ void meow_ui_run(const meow_ui_events *ev)
|
||||
/* 拼音 IME 打开:长按退格清空字段(英文模式 handle_event 返回 0 → 走既有长按回调)。 */
|
||||
if (meow_ime_active() && meow_ime_handle_event(&e)) continue;
|
||||
meow_ui_gesture_end(); /* §6.4:长按已触发,其抬手不再 TAP/SWIPE——顺手释放捕获 */
|
||||
/* M7.0a 缺口#7:view 路由先于 §6.3 通用长按回调(页 view 可消费长按)。 */
|
||||
if (meow_view_dispatch_event(MEOW_EV_LONG_PRESS, e.a, e.b)) continue;
|
||||
if (g_long_press_cb) g_long_press_cb(e.a, e.b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ static char g_background[64] = ""; /* display.background(black
|
||||
static meow_theme_listener g_listeners[8];
|
||||
static int g_nlisteners = 0;
|
||||
|
||||
/* §8.1.5 色板注入:一份自定义板覆写当前生效主题(src==NULL 恢复内建)。
|
||||
* 覆写优先于 settings 解析(随 meow_theme_reload() 重读 settings 后仍生效,与 meow_ui_set_style
|
||||
* 的「程序化覆写不清 settings」语义一致)。 */
|
||||
static meow_theme g_inject;
|
||||
static int g_inject_set = 0;
|
||||
|
||||
/* ---- 读 /usr/data/meow/settings.conf 的 key=value(兼容 "key = value" 带空格形式)----
|
||||
* 与 launcher meow_settings_import 同款解析:按 '=' 切分,key/value 各自去首尾空白,
|
||||
* 精确匹配 key(避免 display.theme 误匹配 display.theme_mode)。返回 0=命中,-1=未命中。 */
|
||||
@@ -295,6 +301,7 @@ static void broadcast(void)
|
||||
/* 当前「style×mode」主题:style=0/1(MD3/MIUIX),dark=0(light)/1(dark)。 */
|
||||
static const meow_theme *theme_current(int style, int dark)
|
||||
{
|
||||
if (g_inject_set) return &g_inject; /* §8.1.5 注入板优先 */
|
||||
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];
|
||||
@@ -339,6 +346,26 @@ meow_theme meow_theme_defaults(meow_theme_mode m)
|
||||
return g_theme[MEOW_STYLE_MD3][resolve_mode(m) == MEOW_THEME_DARK ? 1 : 0];
|
||||
}
|
||||
|
||||
/* ---- §8.1.5 色板导出/注入(单一权威桥)----
|
||||
* 导出:把当前「style×mode」解析生效的完整权威主题拷给调用方(不取指针以防调用方误改内部表)。
|
||||
* 注入:用一份自定义板覆写当前生效主题;NULL 恢复内建。二者都触发 load(g_loaded==0 时先读
|
||||
* settings.conf),使宿主无 settings 文件(单测/脱机)也可靠。 */
|
||||
void meow_theme_export(meow_theme *out)
|
||||
{
|
||||
if (!out) return;
|
||||
const meow_theme *t = meow_theme_get(); /* 解析 g_mode(AUTO→明/暗)+ g_style + 背景覆写 */
|
||||
*out = *t;
|
||||
}
|
||||
|
||||
void meow_theme_inject(const meow_theme *src)
|
||||
{
|
||||
if (!g_loaded) { load_settings(); g_loaded = 1; }
|
||||
if (!src) { g_inject_set = 0; broadcast(); return; }
|
||||
g_inject = *src;
|
||||
g_inject_set = 1;
|
||||
broadcast(); /* 通知 App/状态栏/控件下一帧用新注入板重绘 */
|
||||
}
|
||||
|
||||
/* ---- §8.1.2 顶级主题风格族(minimal)----
|
||||
* meow_ui_style() 返回当前 ui.style(读取走 load_settings,随 meow_theme_reload() 重读,
|
||||
* 故 launcher 改 settings.conf 后经 sb_sdk_refresh→theme_reload 即跟手)。
|
||||
|
||||
@@ -156,6 +156,10 @@ int meow_view_add_child(meow_view *parent, meow_view *child)
|
||||
if (parent->nchildren >= MEOW_VIEW_MAX_CHILD) return -1;
|
||||
parent->children[parent->nchildren++] = child;
|
||||
child->parent = parent;
|
||||
/* 递归修正(§A.4「子视图随父进出」):父已进入时挂上的子视图**立即**进入
|
||||
* (on_enter 不遗漏、参与事件分发)。页在 push/pop 时仍由 view_enter/view_leave 递归驱动,
|
||||
* 此处只补「运行期动态挂子」的间隙(hello 目录嵌套实为 create 后 page_push 前 add,不受影响)。 */
|
||||
if (parent->state & MEOW_VIEW_STATE_ENTERED) view_enter(child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -218,11 +222,15 @@ void meow_view_page_replace(int id)
|
||||
g_top_view = next ? next : cur;
|
||||
}
|
||||
|
||||
/* ---- 页内子导航(预留扩展位 a 桩:本批不实现,仅返回占位)---- */
|
||||
/* ---- 页内子导航(预留扩展位 a,**标注废弃**)----
|
||||
* 设计定案 b(SDK_VIEW_FRAMEWORK_DESIGN.md §F.2:只做子视图容器嵌套,顶层页切仍走 activity
|
||||
* 栈,不做页内子导航栈):本函数对为「将来 a」预留的桩,**不实现、不供依赖**——调用方不得
|
||||
* 认为会建立/弹出子导航。页内「子页」请用 meow_view_add_child 挂 CONTAINER 子视图(组合嵌套),
|
||||
* 或以 activity 栈驱动页切换(page_push/pop/replace)。保留符号仅为未来 a 的占位。 */
|
||||
int meow_view_sub_push(meow_view *v, meow_view *sub)
|
||||
{
|
||||
(void)v; (void)sub;
|
||||
return 0; /* 预留:将来在 ACTIVITY view 内维护子栈 */
|
||||
return 0; /* 预留:将来在 ACTIVITY view 内维护子栈(见设计定案 b,不实现) */
|
||||
}
|
||||
meow_view *meow_view_sub_pop(meow_view *v)
|
||||
{
|
||||
|
||||
@@ -82,12 +82,17 @@ void meow_view_page_replace(int id); /* 旧top on_leave + 新页 on_en
|
||||
|
||||
/* 入口:把语义事件注入当前活动页 view 子树路由——先发子视图(按 z 降序),命中即消费,
|
||||
* 再落本 view 的 on_event(其内做该 view 的 widget 命中)。返回 1=已消费(§A.7)。
|
||||
* 未接线于 meow_ui_run(App 可在 on_tap/on_swipe 等回调里改用它实现 SDK 原生路由)。 */
|
||||
* 已接线于 meow_ui_run(M7.0a):在 §6.4 分流之上,TAP/KEY/SWIPE/LONG_PRESS 先经本路由,
|
||||
* 命中即消费(不再落 App 的 on_* 回调);返回 0 时回落 on_* 原路径(无 view 树 / 未命中 →
|
||||
* 完全向后兼容)。App 也可在自持事件循环里直接调用本函数实现 SDK 原生路由。 */
|
||||
int meow_view_dispatch_event(int ev, int a, int b);
|
||||
|
||||
/* ---- 页内子导航(预留扩展位 a;本批为空实现/桩,仅在 ACTIVITY view 内维护子栈)---- */
|
||||
int meow_view_sub_push(meow_view *v, meow_view *sub); /* 预留;返回 0(未实现于本批) */
|
||||
meow_view *meow_view_sub_pop(meow_view *v); /* 预留;返回 NULL(未实现于本批) */
|
||||
/* ---- 页内子导航(预留扩展位 a,**标注废弃**)----
|
||||
* 设计定案 b(§F.2):不做页内子导航栈,只做子视图容器嵌套;顶层页切仍走 activity 栈。
|
||||
* 故本组为**不实现、不供依赖**的桩(返回 0/NULL);页内子页请用 meow_view_add_child 挂
|
||||
* CONTAINER 子视图,或以 page_push/pop/replace 走 activity 栈。保留符号仅作将来 a 的占位。 */
|
||||
int meow_view_sub_push(meow_view *v, meow_view *sub); /* 废弃(不实现);返回 0 */
|
||||
meow_view *meow_view_sub_pop(meow_view *v); /* 废弃(不实现);返回 NULL */
|
||||
|
||||
/* ---- 坐标:view 相对父内容原点的累计(父坐标链求和 → 屏幕绝对坐标)---- */
|
||||
int meow_view_screen_x(const meow_view *v);
|
||||
|
||||
@@ -15,7 +15,8 @@ enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3,
|
||||
W_TEXTFIELD = 7, W_CARD = 8, W_DIVIDER = 9, W_BADGE = 10,
|
||||
W_CHECKBOX = 11, W_RADIO = 12, W_PROGRESS = 13,
|
||||
W_TOPAPPBAR = 14, W_NAVBAR = 15, W_TABBAR = 16, W_FAB = 17,
|
||||
W_LISTVIEW = 18 }; /* §5.5 结构化列表(行=图标+标题+副标题+右侧操作+选中) */
|
||||
W_LISTVIEW = 18, /* §5.5 结构化列表(行=图标+标题+副标题+右侧操作+选中) */
|
||||
W_ICONBTN = 19 }; /* M7.0a 缺口#2:圆形/位图图标按钮(圆形命中 + 按压态) */
|
||||
|
||||
/* 布局子项数组上限(固定尺寸子项先做;超出静默拒收)。 */
|
||||
#define MEOW_LAYOUT_MAX_CHILD 32
|
||||
@@ -100,6 +101,8 @@ struct meow_widget {
|
||||
meow_listview_tap_fn lv_tap_cb; /* 行点击回调(idx, op) */
|
||||
void *lv_tap_ctx; /* 回调 ctx */
|
||||
|
||||
/* W_ICONBTN 位图图标:sb_icons[] 下标(= MEOW_ICON_* 或 SB_I_*;<0 画空位图,命中照常)。 */
|
||||
int icon_id;
|
||||
/* §5.5 虚拟化层(W_LISTVIEW 扩展:固定 item 池 + 多行渲染器 + 完全自绘 + 空态。
|
||||
* calloc 归零,安全;vs_count/vs_bind 均非 NULL 才进入 pool 模式,src2 模式不受影响。) */
|
||||
meow_listview_count_fn vs_count; /* 虚拟化总数回调(NULL=src2 模式) */
|
||||
@@ -225,7 +228,9 @@ static int g_live_prev_hold = 0; /* 该按钮上次渲染是否为按住
|
||||
|
||||
static void live_btn_register(meow_widget *w)
|
||||
{
|
||||
if (!w || w->type != W_BTN) return;
|
||||
if (!w) return;
|
||||
/* M7.0a 缺口#2:IconButton 也是实时按压钮(同 meow_btn 的按压/按住即时重绘)。 */
|
||||
if (w->type != W_BTN && w->type != W_ICONBTN) return;
|
||||
for (int i = 0; i < g_live_btn_n; i++)
|
||||
if (g_live_btns[i] == w) return;
|
||||
if (g_live_btn_n < MEOW_LIVE_BTN_MAX) g_live_btns[g_live_btn_n++] = w;
|
||||
@@ -246,6 +251,15 @@ static void live_btn_unregister(meow_widget *w)
|
||||
/* 命中:返回坐标所在的「当前 active Activity」的最上层按钮(创建序 = 绘制序,最后者在上)。
|
||||
* §6.1 ①:仅扫描 owner == 当前 active Activity 的按钮 —— 非 active Activity 的控件
|
||||
* 绝不参与命中(根治 agent-119 遗留的「全屏页触摸到主页按钮位置 → 闪现 accent 底」)。 */
|
||||
/* 圆形命中(IconButton;圆心=按钮中心、半径=size/2)。 */
|
||||
static int iconbtn_circular_hit(const meow_widget *w, int x, int y)
|
||||
{
|
||||
int cx = w->x + w->w / 2, cy = w->y + w->h / 2;
|
||||
int r = (w->w < w->h ? w->w : w->h) / 2;
|
||||
int dx = x - cx, dy = y - cy;
|
||||
return (dx * dx + dy * dy) <= (r * r);
|
||||
}
|
||||
|
||||
static meow_widget *live_btn_hit(int x, int y)
|
||||
{
|
||||
int active = meow_ui_activity_active();
|
||||
@@ -253,8 +267,12 @@ static meow_widget *live_btn_hit(int x, int y)
|
||||
for (int i = 0; i < g_live_btn_n; i++) {
|
||||
meow_widget *w = g_live_btns[i];
|
||||
if (w->owner != active) continue; /* 非 active activity:跳过 */
|
||||
if (x >= w->x && x < w->x + w->w && y >= w->y && y < w->y + w->h)
|
||||
/* M7.0a 缺口#2:IconButton 按圆形命中(角部按下不点亮圆钮);其余按矩形。 */
|
||||
if (w->type == W_ICONBTN) {
|
||||
if (iconbtn_circular_hit(w, x, y)) r = w;
|
||||
} else if (x >= w->x && x < w->x + w->w && y >= w->y && y < w->y + w->h) {
|
||||
r = w; /* 越后创建越上:保留最后命中 */
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -540,6 +558,20 @@ int meow_btn_set_variant(meow_widget *w, int variant)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- §5.8.6 M7.0a 缺口#2:IconButton(圆形/位图图标按钮)---- */
|
||||
meow_widget *meow_iconbtn_create(int x, int y, int size, int icon_idx)
|
||||
{
|
||||
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
|
||||
if (!t) return NULL;
|
||||
t->type = W_ICONBTN; t->x = x; t->y = y; t->w = size; t->h = size;
|
||||
t->owner = meow_ui_activity_active(); /* §6.1 ①:同既有控件绑当前 active Activity */
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->icon_id = icon_idx;
|
||||
live_btn_register(t); /* 实时按压/按住即时重绘(同 meow_btn,agent-119) */
|
||||
meow_draw_widget_register(t); /* §6.2 自动注册(创建即注册,App 零调用) */
|
||||
return t;
|
||||
}
|
||||
|
||||
/* ---- TextField ---- */
|
||||
meow_widget *meow_textfield_create(int x, int y, int w, int h, const char *placeholder,
|
||||
char *buf, int maxlen, meow_ime_mode mode)
|
||||
@@ -1374,9 +1406,11 @@ int meow_widget_hit(meow_widget *w, int x, int y)
|
||||
if (w->owner != meow_ui_activity_active()) return 0;
|
||||
/* §5.8.3:禁用不响应命中(也不进 pressed)。 */
|
||||
if (widget_disabled(w)) return 0;
|
||||
/* §5.8.1:自定义画布用 App 回调判定命中(可非矩形);其余矩形内含。 */
|
||||
/* §5.8.1:自定义画布用 App 回调判定命中(可非矩形);IconButton 圆形命中;其余矩形内含。 */
|
||||
if (w->type == W_CUSTOM)
|
||||
return w->hit_cb ? w->hit_cb(w, x, y, w->ctx) : hit(w, x, y);
|
||||
if (w->type == W_ICONBTN)
|
||||
return iconbtn_circular_hit(w, x, y);
|
||||
return hit(w, x, y);
|
||||
}
|
||||
int meow_btn_pressed(meow_widget *w)
|
||||
@@ -1480,6 +1514,35 @@ void meow_widget_draw(meow_widget *w)
|
||||
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, r, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_ICONBTN: {
|
||||
/* M7.0a 缺口#2:圆形底 + 位图图标 + 按压/按住状态层(实时)。
|
||||
* 按压三路来源(同 meow_btn,agent-119):状态机 MEOW_WS_PRESSED / 实时触摸命中 /
|
||||
* 位图取自 sb_icons[](meow_gfx_blit_icon_ink,只写像素不标顶栏脏区——控件矩形由
|
||||
* 本 draw 的 clip + invalidation 管理);图标墨色 onSurface,选中/按住淡向底。 */
|
||||
int live = meow_input_touch_down() &&
|
||||
iconbtn_circular_hit(w, meow_input_touch_x(), meow_input_touch_y());
|
||||
int press = (w->state & MEOW_WS_PRESSED) || live;
|
||||
int hold = live && meow_input_touch_hold_ms() >= (unsigned)meow_ui_long_press_ms();
|
||||
int disabled = widget_disabled(w);
|
||||
if (disabled) press = 0;
|
||||
int cx = w->x + w->w / 2, cy = w->y + w->h / 2;
|
||||
int r = (w->w < w->h ? w->w : w->h) / 2;
|
||||
int inkh = (w->h * 6) / 10; /* MD3 图标比:约 24/40 → 0.6*size */
|
||||
if (inkh < 8) inkh = (w->h > 0) ? w->h : 8;
|
||||
int selected = (w->state & MEOW_WS_SELECTED);
|
||||
uint32_t base = selected ? th->primaryContainer : widget_bg(w, th, th->surfaceContainerLow);
|
||||
uint32_t fgc = selected ? th->onPrimaryContainer : widget_fg(w, th, th->onSurface);
|
||||
uint32_t bg = (hold) ? state_tint(base, th, 24)
|
||||
: (press ? state_tint(base, th, 12) : base);
|
||||
if (disabled) bg = state_tint(base, th, 38); /* 禁用变淡(圆形底直接变灰) */
|
||||
if (w->state & MEOW_WS_FOCUSED) bg = state_tint(base, th, 8); /* 焦点浅提亮 */
|
||||
meow_gfx_fill_circle(cx, cy, r, bg);
|
||||
if (w->icon_id >= 0)
|
||||
meow_gfx_blit_icon_ink(w->icon_id, cx - MEOW_SB_ICON_SIZE / 2, inkh, fgc, cy);
|
||||
if (w->state & MEOW_WS_FOCUSED)
|
||||
meow_gfx_ring_arc(cx, cy, r - 1, 2, 0, 360, th->primary); /* 焦点圆形描边 */
|
||||
break;
|
||||
}
|
||||
case W_SLIDER: {
|
||||
/* 语义色:轨道=onSurfaceVariant(dim)、填充=primary(accent)、滑块=fg(onSurface)。 */
|
||||
int trace_y = w->y + w->h / 2 - 3;
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/* test_meow_ui_iconbtn.c — M7.0a 缺口#2 宿主验证:IconButton 圆形命中 + 按压态 + 位图图标。
|
||||
*
|
||||
* 覆盖:
|
||||
* I1 圆形命中:中心/边界命中,角部/外缘不命中(区别于矩形按钮);
|
||||
* I2 owner==active + enabled 双闸门:非 active 或禁用不命中;
|
||||
* I3 按压态视觉:设 MEOW_WS_PRESSED 后重绘,圆心像素向 onSurface 变亮(与未按不同);
|
||||
* 禁用时圆心向灰变暗;
|
||||
* I4 实时按压(meow_input_touch 注入):按下命中圆心 → 圆心变亮;释放恢复。
|
||||
*
|
||||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
|
||||
* gcc -Wall -Wextra -o test_meow_ui_iconbtn test_meow_ui_iconbtn.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_str.c \
|
||||
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
|
||||
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
|
||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
||||
* 运行:./test_meow_ui_iconbtn
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include "meow_ui_internal.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond, name) do { \
|
||||
if (cond) printf(" PASS %s\n", name); \
|
||||
else { printf(" FAIL %s\n", name); g_fail++; } } while (0)
|
||||
|
||||
/* ---- back_buf 像素读取(断言真实渲染,不依赖目检) ---- */
|
||||
static void rd_px(int x, int y, unsigned short *p)
|
||||
{
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp();
|
||||
if (!bb || x < 0 || y < 0) { *p = 0; return; }
|
||||
if (bpp == 16) *p = *(unsigned short *)(bb + (size_t)y * line + (size_t)x * 2);
|
||||
else *p = (unsigned short)(*(unsigned int *)(bb + (size_t)y * line + (size_t)x * 4) >> 16);
|
||||
}
|
||||
static unsigned short px565_at(int x, int y)
|
||||
{
|
||||
unsigned short p; rd_px(x, y, &p); return p;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setenv("MEOW_UI_DUMP", "/tmp/iconbtn.ppm", 1);
|
||||
meow_ui_opts o = { 480, 720 };
|
||||
assert(meow_ui_init(&o) == 0);
|
||||
meow_ui_activity_push(7); /* owner 对齐:IconButton 绑定 active=7 */
|
||||
meow_theme_set_mode(MEOW_THEME_DARK);
|
||||
|
||||
/* size=64 → 外接框 (100,100,64,64) → 圆心 (132,132) r=32 */
|
||||
meow_widget *ic = meow_iconbtn_create(100, 100, 64, MEOW_ICON_PLAY_ARROW);
|
||||
assert(ic != NULL);
|
||||
|
||||
/* ============ I1 圆形命中 ============ */
|
||||
printf("I1 circular hit\n");
|
||||
CHECK(meow_widget_hit(ic, 132, 132), "center hits");
|
||||
CHECK(meow_widget_hit(ic, 100, 132), "left edge (boundary) hits");
|
||||
CHECK(meow_widget_hit(ic, 132, 100), "top edge (boundary) hits");
|
||||
CHECK(!meow_widget_hit(ic, 100, 100), "corner (dist>r) MISSES (rect would hit)");
|
||||
CHECK(!meow_widget_hit(ic, 132, 165), "below center (dist 33>32) MISSES");
|
||||
|
||||
/* ============ I2 owner 闸门:切 active 后不命中 ============ */
|
||||
printf("I2 owner gate\n");
|
||||
meow_ui_activity_push(8); /* 换 active:original owner=7 ≠ active=8 */
|
||||
CHECK(!meow_widget_hit(ic, 132, 132), "non-active owner -> MISS (gate)");
|
||||
meow_ui_activity_pop(); /* 回 active=7 */
|
||||
CHECK(meow_widget_hit(ic, 132, 132), "back to active -> hits");
|
||||
/* enabled 闸门 */
|
||||
meow_widget_set_enabled(ic, 0);
|
||||
CHECK(!meow_widget_hit(ic, 132, 132), "disabled -> MISS");
|
||||
meow_widget_set_enabled(ic, 1);
|
||||
CHECK(meow_widget_hit(ic, 132, 132), "enabled -> hits");
|
||||
|
||||
/* ============ I3 按压态视觉(状态机位 + 禁用变暗) ============
|
||||
* 采样点选 (162,132):圆内 (r=30<32) 但超出图标 ink 右缘(约 x≤148)——取的是圆形底色,
|
||||
* 而非图标墨色(圆心为图标覆盖,不适合测底色 tint)。 */
|
||||
printf("I3 pressed visual (state machine / disabled)\n");
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_ui_clear(th->bg);
|
||||
unsigned short p_bg = px565_at(100, 100); /* 空屏角部 = 页面底色 */
|
||||
meow_widget_set_state(ic, MEOW_WS_PRESSED, 0); /* 未按 */
|
||||
meow_widget_draw(ic);
|
||||
unsigned short p_idle = px565_at(162, 132);
|
||||
meow_widget_set_state(ic, MEOW_WS_PRESSED, 1); /* 按下 */
|
||||
meow_widget_draw(ic);
|
||||
unsigned short p_press = px565_at(162, 132);
|
||||
CHECK(p_press != p_idle, "pressed circle-bg pixel differs from idle (tint layer)");
|
||||
/* 禁用:向灰变暗,圆 bg 应更接近 onSurface 混合(与 idle 不同且更亮/更灰) */
|
||||
meow_widget_set_state(ic, MEOW_WS_PRESSED, 0);
|
||||
meow_widget_set_enabled(ic, 0);
|
||||
meow_widget_draw(ic);
|
||||
unsigned short p_dis = px565_at(162, 132);
|
||||
CHECK(p_dis != p_idle, "disabled circle-bg pixel differs from idle (dim)");
|
||||
meow_widget_set_enabled(ic, 1);
|
||||
/* 角部(圆外)不应出现圆形底色:等于页面底色(圆形命中/填充不外溢) */
|
||||
meow_widget_draw(ic);
|
||||
CHECK(px565_at(100, 100) == p_bg, "corner (outside circle) = page bg (no stray fill)");
|
||||
|
||||
/* ============ I4 实时按压(meow_input_touch 注入) ============ */
|
||||
printf("I4 live press via input touch\n");
|
||||
meow_widget_set_state(ic, MEOW_WS_PRESSED, 0);
|
||||
meow_ui_clear(th->bg);
|
||||
meow_widget_draw(ic);
|
||||
unsigned short q_idle = px565_at(162, 132);
|
||||
meow_input_touch_set(1, 132, 132, 0); /* 按在圆心 */
|
||||
meow_widget_draw(ic);
|
||||
unsigned short q_live = px565_at(162, 132);
|
||||
CHECK(q_live != q_idle, "live touch press -> circle bg brightens");
|
||||
meow_input_touch_set(0, 0, 0, 0); /* 释放 */
|
||||
meow_widget_draw(ic);
|
||||
CHECK(px565_at(162, 132) == q_idle, "release -> circle bg restores");
|
||||
|
||||
meow_widget_free(ic);
|
||||
meow_ui_activity_pop();
|
||||
meow_ui_shutdown();
|
||||
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
|
||||
return g_fail;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/* test_meow_ui_theme_export.c — M7.0a 主题收口前置(缺口#6 SDK 半)宿主验证:
|
||||
* §8.1.5 色板导出/注入——meow_theme_export 拷当前权威主题;meow_theme_inject 覆写当前生效主题。
|
||||
*
|
||||
* 覆盖:
|
||||
* T1 导出一致性:export 后关键字段 == meow_theme_get()(当前 style×mode 权威板);
|
||||
* T2 注入:inject 一份自定义板 → meow_theme_get()/export 都返回注入板(单一权威反映);
|
||||
* 注入恢复内建:inject(NULL) 后回到目标 style×mode 的内建板;
|
||||
* T3 launcher 薄壳映射契约:导出板能映射出 meow_ui_theme_t 的 9 个角色字段(surface/
|
||||
* surface_variant/surface_container/primary/on_surface/on_surface_variant/outline/error/
|
||||
* surface_tint),值与 SDK meow_theme 对应字段一致。
|
||||
*
|
||||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
|
||||
* gcc -Wall -Wextra -o test_meow_ui_theme_export test_meow_ui_theme_export.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_str.c \
|
||||
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
|
||||
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
|
||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
||||
* 运行:./test_meow_ui_theme_export
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond, name) do { \
|
||||
if (cond) printf(" PASS %s\n", name); \
|
||||
else { printf(" FAIL %s\n", name); g_fail++; } } while (0)
|
||||
|
||||
/* launcher meow_ui_theme_t 的 9 字段形状(测试内复刻字段名,不依赖 launcher 内部头)。 */
|
||||
typedef struct {
|
||||
uint32_t surface, surface_variant, surface_container;
|
||||
uint32_t primary, on_surface, on_surface_variant;
|
||||
uint32_t outline, error, surface_tint;
|
||||
} shell_theme;
|
||||
|
||||
static shell_theme export_shell(const meow_theme *t)
|
||||
{
|
||||
shell_theme s;
|
||||
s.surface = t->surface;
|
||||
s.surface_variant = t->surfaceVariant;
|
||||
s.surface_container = t->surfaceContainer;
|
||||
s.primary = t->primary;
|
||||
s.on_surface = t->onSurface;
|
||||
s.on_surface_variant= t->onSurfaceVariant;
|
||||
s.outline = t->outline;
|
||||
s.error = t->error;
|
||||
s.surface_tint = t->surfaceTint;
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setenv("MEOW_UI_DUMP", "/tmp/theme_export.ppm", 1);
|
||||
meow_ui_opts o = { 480, 720 };
|
||||
assert(meow_ui_init(&o) == 0);
|
||||
|
||||
/* ---- T1 导出一致性(MD3 dark) ---- */
|
||||
printf("T1 export consistency\n");
|
||||
meow_theme_set_mode(MEOW_THEME_DARK);
|
||||
meow_ui_set_style(MEOW_STYLE_MD3);
|
||||
const meow_theme *t0 = meow_theme_get();
|
||||
meow_theme e0; meow_theme_export(&e0);
|
||||
CHECK(e0.surface == t0->surface, "export.surface == get.surface");
|
||||
CHECK(e0.primary == t0->primary, "export.primary == get.primary");
|
||||
CHECK(e0.surfaceVariant== t0->surfaceVariant,"export.surfaceVariant == get.surfaceVariant");
|
||||
CHECK(e0.onSurface == t0->onSurface, "export.onSurface == get.onSurface");
|
||||
CHECK(e0.radius == t0->radius, "export.radius == get.radius");
|
||||
CHECK(e0.bg == t0->bg, "export.bg == get.bg (background override applied)");
|
||||
|
||||
/* MIUIx light:确认导出随 style×mode 切换(不是固定板) */
|
||||
meow_ui_set_style(MEOW_STYLE_MIUIX);
|
||||
meow_theme_set_mode(MEOW_THEME_LIGHT);
|
||||
const meow_theme *t1 = meow_theme_get();
|
||||
meow_theme e1; meow_theme_export(&e1);
|
||||
CHECK(e1.primary == 0xF57200 && t1->primary == 0xF57200, "MIUIx light primary exported");
|
||||
CHECK(e1.bg == t1->bg && e1.surface == t1->surface, "MIUIx light bg/surface exported");
|
||||
CHECK(e1.surface == (uint32_t)0xF7F3EE, "MIUIx light surface == warm default");
|
||||
|
||||
/* ---- T2 注入覆写 + 恢复内建 ---- */
|
||||
printf("T2 inject override + restore\n");
|
||||
meow_theme custom; memset(&custom, 0, sizeof custom);
|
||||
custom.surface = 0x112233; custom.primary = 0xABCDEF; custom.onSurface = 0x0F0F0F;
|
||||
custom.radius = 99; custom.radius_card = 100;
|
||||
meow_theme_inject(&custom);
|
||||
const meow_theme *ti = meow_theme_get();
|
||||
meow_theme ei; meow_theme_export(&ei);
|
||||
CHECK(ti->primary == 0xABCDEF && ti->surface == 0x112233 && ti->radius == 99,
|
||||
"inject -> get() reads injected palette");
|
||||
CHECK(ei.primary == 0xABCDEF && ei.surface == 0x112233, "inject -> export reads injected palette");
|
||||
/* meow_theme_export == meow_theme_get even when injected(单一权威) */
|
||||
CHECK(ei.onSurface == ti->onSurface && ei.radius_card == ti->radius_card,
|
||||
"export == get while injected");
|
||||
/* 注入恢复内建 */
|
||||
meow_theme_inject(NULL);
|
||||
const meow_theme *tr = meow_theme_get();
|
||||
CHECK(tr->primary == 0xF57200 && tr->surface == 0xF7F3EE,
|
||||
"inject(NULL) restores builtin (MIUIx light)");
|
||||
|
||||
/* ---- T3 launcher 薄壳映射契约(9 角色字段) ---- */
|
||||
printf("T3 shell mapping contract\n");
|
||||
meow_theme_export(&e1);
|
||||
shell_theme sh = export_shell(&e1);
|
||||
CHECK(sh.surface == e1.surface, "shell.surface == surface");
|
||||
CHECK(sh.surface_variant == e1.surfaceVariant, "shell.surface_variant == surfaceVariant");
|
||||
CHECK(sh.surface_container == e1.surfaceContainer, "shell.surface_container == surfaceContainer");
|
||||
CHECK(sh.primary == e1.primary, "shell.primary == primary");
|
||||
CHECK(sh.on_surface == e1.onSurface, "shell.on_surface == onSurface");
|
||||
CHECK(sh.on_surface_variant == e1.onSurfaceVariant, "shell.on_surface_variant == onSurfaceVariant");
|
||||
CHECK(sh.outline == e1.outline, "shell.outline == outline");
|
||||
CHECK(sh.error == e1.error, "shell.error == error");
|
||||
CHECK(sh.surface_tint == e1.surfaceTint, "shell.surface_tint == surfaceTint");
|
||||
/* 关键角色字段值与 MIUIx light 内建一致(证明读的是 SDK 单一权威,而非 launcher 自持 4 套) */
|
||||
CHECK(sh.surface == 0xF7F3EE && sh.primary == 0xF57200 && sh.error == 0xB3261E,
|
||||
"shell roles match SDK builtin (single authority)");
|
||||
|
||||
meow_ui_shutdown();
|
||||
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
|
||||
return g_fail;
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/* test_meow_ui_view_event.c — M7.0a 缺口#7 宿主验证:meow_view 事件接线 + 递归 enter/leave。
|
||||
*
|
||||
* A 部(无 run 循环,直调 meow_view_*):递归 enter/leave 顺序 + 运行期动态挂子视图立即进入
|
||||
* (view.c meow_view_add_child 修正)+ 子>父 on_event 路由。
|
||||
* B 部(经 meow_ui_run 主循环):把语义事件注入队列 → run 循环先经 meow_view_dispatch_event
|
||||
* 路由,命中的事件不再落 App on_* 回调(on_tap/on_key/on_swipe/long_press 零触发),未命中
|
||||
* 的事件照常回落 App 回调——验证「接线于 meow_ui_run、于 §6.4 分流之上」。
|
||||
*
|
||||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
|
||||
* gcc -Wall -Wextra -o test_meow_ui_view_event test_meow_ui_view_event.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_str.c \
|
||||
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
|
||||
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c \
|
||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
||||
* 运行:./test_meow_ui_view_event
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include "meow_ui_internal.h" /* MEOW_EV_* */
|
||||
#include "meow_ui_view.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond, name) do { \
|
||||
if (cond) printf(" PASS %s\n", name); \
|
||||
else { printf(" FAIL %s\n", name); g_fail++; } } while (0)
|
||||
|
||||
/* ---------- A 部:递归 enter/leave 顺序 + 运行期动态挂子 ---------- */
|
||||
static int g_trace[16]; static int g_trace_n = 0;
|
||||
static void vtrace_enter(meow_view *v, void *ctx) { (void)ctx; g_trace[g_trace_n++] = v->id; }
|
||||
static void vtrace_leave(meow_view *v, void *ctx) { (void)ctx; g_trace[g_trace_n++] = -v->id; }
|
||||
static int g_child3_hits = 0;
|
||||
static int vev_child3(meow_view *v, int ev, int a, int b, void *ctx)
|
||||
{ (void)v;(void)ctx; if (ev == MEOW_EV_TAP && a == 33 && b == 33) { g_child3_hits++; return 1; } return 0; }
|
||||
|
||||
/* ---------- B 部:run 循环接线 ---------- */
|
||||
static int g_tap_calls = 0, g_key_calls = 0, g_swipe_calls = 0, g_lp_calls = 0;
|
||||
static void app_on_tap(int x, int y) { (void)x;(void)y; g_tap_calls++; }
|
||||
static void app_on_key(int k, int d) { (void)k;(void)d; g_key_calls++; }
|
||||
static void app_on_swipe(int dir, int dx, int dy) { (void)dir;(void)dx;(void)dy; g_swipe_calls++; }
|
||||
static void app_lp(int x, int y) { (void)x;(void)y; g_lp_calls++; }
|
||||
|
||||
/* 父 view:消费 TAP@(10,10) 与 LONG_PRESS(任何)。 */
|
||||
static int vev_parent(meow_view *v, int ev, int a, int b, void *ctx)
|
||||
{ (void)v;(void)ctx;
|
||||
if (ev == MEOW_EV_TAP && a == 10 && b == 10) return 1;
|
||||
if (ev == MEOW_EV_LONG_PRESS) return 1;
|
||||
return 0; }
|
||||
/* 子 view:消费 KEY(任何)与 SWIPE_LEFT。 */
|
||||
static int vev_child(meow_view *v, int ev, int a, int b, void *ctx)
|
||||
{ (void)v;(void)b;(void)ctx;
|
||||
if (ev == MEOW_EV_KEY) return 1;
|
||||
if (ev == MEOW_EV_SWIPE && a == MEOW_SWIPE_LEFT) return 1;
|
||||
return 0; }
|
||||
|
||||
static void *driver(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
usleep(700000); /* 等 startup cooldown(500ms) 过后再发 */
|
||||
meow_input_inject(MEOW_EV_KEY, MEOW_KEY_VOLUMEUP, 1); /* child 消费 → app_on_key 不触发 */
|
||||
usleep(80000);
|
||||
meow_input_inject(MEOW_EV_TAP, 10, 10); /* parent 消费 → app_on_tap 不触发 */
|
||||
usleep(80000);
|
||||
meow_input_inject(MEOW_EV_TAP, 50, 50); /* 无 view 命中 → 回落 app_on_tap */
|
||||
usleep(80000);
|
||||
meow_input_inject(MEOW_EV_SWIPE, MEOW_SWIPE_LEFT, 80); /* child 消费 → app_on_swipe 不触发 */
|
||||
usleep(80000);
|
||||
meow_input_inject(MEOW_EV_SWIPE, MEOW_SWIPE_RIGHT, 80); /* 无 view 命中 → 回落 app_on_swipe */
|
||||
usleep(80000);
|
||||
meow_input_inject(MEOW_EV_LONG_PRESS, 10, 10); /* parent 消费 → long_press 不触发 */
|
||||
usleep(80000);
|
||||
meow_ui_request_exit();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setenv("MEOW_UI_DUMP", "/tmp/view_event.ppm", 1);
|
||||
meow_ui_opts o = { 480, 720 };
|
||||
assert(meow_ui_init(&o) == 0);
|
||||
|
||||
/* ================= A 部:递归 enter/leave + 动态挂子 ================= */
|
||||
printf("A: recursive enter/leave + runtime child add\n");
|
||||
{
|
||||
static const meow_view_hooks ph = { .on_enter = vtrace_enter, .on_leave = vtrace_leave,
|
||||
.on_event = vev_parent };
|
||||
static const meow_view_hooks ch = { .on_enter = vtrace_enter, .on_leave = vtrace_leave,
|
||||
.on_event = vev_child };
|
||||
static const meow_view_hooks c3 = { .on_enter = vtrace_enter, .on_leave = vtrace_leave,
|
||||
.on_event = vev_child3 };
|
||||
meow_view *pv = meow_view_create(1, MEOW_VIEW_ACTIVITY, &ph, NULL);
|
||||
meow_view *cv = meow_view_create(2, MEOW_VIEW_CONTAINER, &ch, NULL);
|
||||
assert(meow_view_add_child(pv, cv) == 0);
|
||||
g_trace_n = 0;
|
||||
meow_view_page_push(1); /* pv.enter → cv.enter */
|
||||
CHECK(g_trace_n == 2 && g_trace[0] == 1 && g_trace[1] == 2, "push: parent.enter -> child.enter");
|
||||
/* 运行期动态挂第三个子视图(父已 ENTERED)→ 立即进入(view.c 修正) */
|
||||
meow_view *c3v = meow_view_create(3, MEOW_VIEW_CONTAINER, &c3, NULL);
|
||||
g_trace_n = 0;
|
||||
assert(meow_view_add_child(pv, c3v) == 0);
|
||||
CHECK(g_trace_n == 1 && g_trace[0] == 3, "runtime add-child-while-entered -> child.enter fires");
|
||||
/* 新挂子 view 参与事件路由:命中 (33,33) 被子 3 消费 */
|
||||
g_child3_hits = 0;
|
||||
CHECK(meow_view_dispatch_event(MEOW_EV_TAP, 33, 33) == 1 && g_child3_hits == 1,
|
||||
"runtime-added child participates in dispatch");
|
||||
/* 离开:子视图(挂载序 2 → 3)先后离开 → 父(递归离开,子->父) */
|
||||
g_trace_n = 0;
|
||||
meow_view_page_pop();
|
||||
CHECK(g_trace_n == 3 && g_trace[0] == -2 && g_trace[1] == -3 && g_trace[2] == -1,
|
||||
"pop: child2.leave -> child3.leave -> parent.leave");
|
||||
meow_view_free(pv); /* 递归释放 2/3 */
|
||||
printf(" PASS A: recursive order + dynamic child\n");
|
||||
}
|
||||
|
||||
/* ================= B 部:meow_ui_run 接线(§6.4 之上) ================= */
|
||||
printf("B: meow_view_dispatch_event wired into meow_ui_run\n");
|
||||
{
|
||||
static const meow_view_hooks ph = { .on_event = vev_parent };
|
||||
static const meow_view_hooks ch = { .on_event = vev_child };
|
||||
meow_view *pv = meow_view_create(20, MEOW_VIEW_ACTIVITY, &ph, NULL);
|
||||
meow_view *cv = meow_view_create(21, MEOW_VIEW_CONTAINER, &ch, NULL);
|
||||
assert(meow_view_add_child(pv, cv) == 0);
|
||||
meow_view_page_push(20); /* 进入父+子(g_top_view=20) */
|
||||
|
||||
g_tap_calls = g_key_calls = g_swipe_calls = g_lp_calls = 0;
|
||||
meow_ui_set_long_press_cb(app_lp);
|
||||
meow_ui_events ev; memset(&ev, 0, sizeof ev);
|
||||
ev.on_tap = app_on_tap; ev.on_key = app_on_key; ev.on_swipe = app_on_swipe;
|
||||
|
||||
pthread_t thd; pthread_create(&thd, NULL, driver, NULL);
|
||||
meow_ui_run(&ev); /* 阻塞直到 driver 里 request_exit */
|
||||
pthread_join(thd, NULL);
|
||||
|
||||
CHECK(g_key_calls == 0, "KEY consumed by child -> app_on_key NOT called");
|
||||
CHECK(g_tap_calls == 1, "TAP(10,10) consumed by parent; TAP(50,50) fell through -> exactly 1 on_tap");
|
||||
CHECK(g_swipe_calls == 1, "SWIPE_LEFT consumed by child; SWIPE_RIGHT fell through -> exactly 1 on_swipe");
|
||||
CHECK(g_lp_calls == 0, "LONG_PRESS consumed by parent -> long_press cb NOT called");
|
||||
printf(" PASS B: run loop routes through views, fallback preserved\n");
|
||||
|
||||
meow_view_page_pop();
|
||||
meow_view_free(pv);
|
||||
}
|
||||
|
||||
meow_ui_shutdown();
|
||||
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
|
||||
return g_fail;
|
||||
}
|
||||
@@ -58,14 +58,16 @@ static void umbrella_smoke(void)
|
||||
(void)&meow_ui_text_center; (void)&meow_ui_time_ms; (void)&meow_ui_effective_rate;
|
||||
/* 图片层(§4) */
|
||||
(void)&meow_img_load_file; (void)&meow_ui_img_cover; (void)&meow_img_w;
|
||||
/* 主题(§8) */
|
||||
/* 主题(§8 + §8.1.5 色板导出/注入) */
|
||||
(void)&meow_theme_get; (void)&meow_theme_defaults; (void)&meow_ui_set_style;
|
||||
(void)&meow_theme_export; (void)&meow_theme_inject;
|
||||
/* 输入法(§5.1) */
|
||||
(void)&meow_ui_text_input; (void)&meow_ime_text; (void)&meow_ime_composition;
|
||||
/* 控件层(§5/§5.8/§5.8.6/§5.5) */
|
||||
(void)&meow_btn_create; (void)&meow_widget_custom_create; (void)&meow_row_create;
|
||||
(void)&meow_topappbar_hit; (void)&meow_listview_create; (void)&meow_list_tap_at;
|
||||
(void)&meow_widget_set_opts; (void)&meow_widget_state; (void)&meow_progress_set;
|
||||
(void)&meow_iconbtn_create; /* M7.0a 缺口#2 */
|
||||
/* 输入层/生命周期(§6)+ 长按(§6.3) */
|
||||
(void)&meow_ui_run; (void)&meow_ui_swipe_hint; (void)&meow_ui_set_long_press_cb;
|
||||
/* Activity 栈(§6.1) */
|
||||
|
||||
Reference in new issue
Block a user