From a1687486bc612f4264a6e4bbda2fcb77a9e404b4 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Thu, 3 Sep 2026 06:51:44 +0800 Subject: [PATCH] =?UTF-8?q?sdk:=20full-sync=20M7.0a/b=20(theme=20view=20in?= =?UTF-8?q?put=20gfx=20=E7=AD=89,=20=E7=9C=9F=E5=AE=9E=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E9=9D=9EFT)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ sdk/include/meow_ui_theme.h | 22 ++++++++++++++++++++++ sdk/meow_ui_input.c | 14 +++++++++++++- sdk/meow_ui_theme.c | 27 +++++++++++++++++++++++++++ sdk/meow_ui_view.c | 12 ++++++++++-- sdk/meow_ui_view.h | 13 +++++++++---- sdk/test_umbrella.c | 4 +++- 7 files changed, 86 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b68aa22..46c04b9 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,11 @@ cd ../examples/hello - **动画基座 `meow_anim` / `meow_anim_tick`**(§12.2):声明式 tween(linear/ease-out/ease-in-out/overshoot),静态池 ≤16,无动画零开销;只 flush 动画影响区。 - **滚动物理 `meow_scroll`**(§12.3):速度采样 + 松手 fling + 边界 clamp/简单回弹 + snap 吸附;给 `meow_list`/`W_LIST`/`W_LISTVIEW` 加惯性滚动。 - **事件分发形式化**(§6.4):单一仲裁者——z 序自顶向下命中、命中即消费、down 捕获整个手势、模态层隔离、唯一焦点(点外释放/滚动容器转移)。 +- **视图事件路由**(§A.7,M7.0a):`meow_view_dispatch_event` 接入 `meow_ui_run`——TAP/KEY/SWIPE/LONG_PRESS 先走视图树 `on_event` 路由,命中即消费(不再落 App `on_*`);无视图树/未命中回落 App 原生 `on_*` 回调(完全向后兼容)。页内子导航按设计定案 b 标注**废弃**(不实现),页内「子页」用 `meow_view_add_child` 挂 CONTAINER 子视图(组合嵌套)。 - **ListView 虚拟化**(§5.5):固定 item 池(视口+2)、多行富 item、段头、空态、完全自绘钩子;App 只提供「总数 + bind」回调,几千/上万行不卡。 - **ListView 左滑删除**(§5.5):列表行横向手势 `meow_listview_swipe_begin/move/end` 三级喂入 + 删除/回弹动画;`meow_listview_set_swipe_delete` 绑删除回调,行自绘 `set_item_draw` 配合画右露删除区(主题 `error/onError`)。 - **圆形图标按钮 `meow_iconbtn_create`**(M7.0a):圆形命中 + 位图图标(`sb_icons[]`/`MEOW_ICON_*`)+ 实时按压/按住态;供电源菜单/播放控制/锁屏件用。 +- **主题色板导出/注入**(§8.1.5,M7.0a):`meow_theme_export`/`meow_theme_inject`——「单一权威桥」,导出当前 `style×mode` 解析生效的权威主题(9 角色字段),或注入一份自定义板覆写当前生效主题(`NULL` 恢复内建,覆写随 `meow_theme_reload()` 保留);供 stateless 绘制原语读单一权威、或 launcher/App 自定义背景/强调/风格。 - **字符串资源 / i18n**(§13):`meow_str`/`meow_strf`/`meow_str_set_locale`——内嵌 `zh_cn`/`en_us` 双语表 + TF 卡覆盖表;UI 文案零硬编码,按 locale 取整串。 - **图标库 `sb_icons`**(§5.8):30 个 MD3 Material Symbols 位图动作图标(`MEOW_ICON_*`),`meow_topappbar_set_item_icon` 把 TopAppBar 动作项换成位图图标(修复 ⚙↻⌕ 豆腐)。 - **图片降采样 `meow_img_downscale`**(`include/meow_ui_image.h`):把解码图降到 ≤maxdim 边长省内存(封面/大图用,如 8.28MB→0.78MB)。 diff --git a/sdk/include/meow_ui_theme.h b/sdk/include/meow_ui_theme.h index ebc80b0..849d9d0 100644 --- a/sdk/include/meow_ui_theme.h +++ b/sdk/include/meow_ui_theme.h @@ -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/ diff --git a/sdk/meow_ui_input.c b/sdk/meow_ui_input.c index 17217ea..8863236 100644 --- a/sdk/meow_ui_input.c +++ b/sdk/meow_ui_input.c @@ -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 #include @@ -140,7 +141,7 @@ static int meow_input_jack_debounce(void) } /* ---- §10.5 叠加让位:通知中心叠加会话期输入排空(播放中 App 不被 SIGSTOP 的配套)---- - * 媒体播放中的 App 前台唤起通知中心时 launcher 不冻结 App(保 PCM 供流,App 播放线程同进程), + * 媒体播放中的 App 前台唤起通知中心时 launcher 不冻结 App(保 PCM 供流,ncm 播放线程同进程), * App 输入线程仍在读同一批 evdev 设备(每 fd 各收一份事件副本,与 launcher 互不剥夺)——若不 * 抑制,面板上的一次点击会被 App 再消费一次(如面板媒体卡片「暂停」与 App 同坐标控件双重触发)。 * 故标记存在期:排空本进程 fd 队列并丢弃、EVIOCGKEY 位图静默对齐(不产生边沿语义,系统键动作 @@ -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); } } diff --git a/sdk/meow_ui_theme.c b/sdk/meow_ui_theme.c index 8ad3a35..b2a727a 100644 --- a/sdk/meow_ui_theme.c +++ b/sdk/meow_ui_theme.c @@ -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 即跟手)。 diff --git a/sdk/meow_ui_view.c b/sdk/meow_ui_view.c index 645e677..2bbd9ab 100644 --- a/sdk/meow_ui_view.c +++ b/sdk/meow_ui_view.c @@ -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) { diff --git a/sdk/meow_ui_view.h b/sdk/meow_ui_view.h index 73d8a96..41988db 100644 --- a/sdk/meow_ui_view.h +++ b/sdk/meow_ui_view.h @@ -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); diff --git a/sdk/test_umbrella.c b/sdk/test_umbrella.c index 9604ce4..efd8c10 100644 --- a/sdk/test_umbrella.c +++ b/sdk/test_umbrella.c @@ -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) */