diff --git a/patch_v2/hello_app/app.c b/patch_v2/hello_app/app.c new file mode 100644 index 0000000..241e835 --- /dev/null +++ b/patch_v2/hello_app/app.c @@ -0,0 +1,115 @@ +/* app.c — hello 应用装配层:main + 组装 + 事件分发。 + * + * 职责(SDK_VIEW_FRAMEWORK_DESIGN.md §B.2):main 只做初始化/建页/跑事件循环/清理;把 + * meow_ui_run 的平铺输入回调(key/tap/swipe/tick/touch)路由到当前活动页(home/full/demo)。 + * 页面切换统一走 meow_view_page_push/pop(活动栈 + on_enter/on_leave 的唯一驱动,App 不再 + * 分别调 activity_push/pop)。业务变化经 logic 监听器送回 UI(见 ui/home.c)。 + * + * 契约(MEOW_APP_SDK.md §1/§3/§6):应用只调 meow_ui API。 + */ +#include "hello_internal.h" +#include + +/* Dialog / BottomSheet(阻塞模态;非控件,main() 创建一次,demo 页打开/关) */ +meow_dialog *g_dlg; +meow_sheet *g_sheet; + +static meow_view *g_home_view; +static meow_view *g_full_view; +static meow_view *g_demo_view; + +static void on_sigterm(int s) { (void)s; meow_ui_request_exit(); } + +/* ============================================================ */ +/* 事件分发:meow_ui_run 平铺回调 → 当前页 */ +/* ============================================================ */ + +static void app_on_key(int keycode, int is_down) +{ + (void)keycode; (void)is_down; + meow_log(MEOW_LOG_DEBUG, "key"); +} + +static void app_on_tap(int x, int y) +{ + if (demo_is_active()) { demo_on_tap(x, y); return; } + if (full_is_active()) { meow_view_page_pop(); return; } /* 全屏:点任意处回 home */ + home_on_tap(x, y); +} + +static void app_on_swipe(int dir, int dx, int dy) +{ + (void)dy; + if (demo_is_active()) { demo_swipe(dir, dx); return; } + if (dir == MEOW_SWIPE_LEFT) { + if (full_is_active()) { meow_view_page_pop(); } + else { meow_log(MEOW_LOG_INFO, "exit:swipe"); meow_ui_request_exit(); } + } +} + +/* M2.71-bug2/6:拖动过程回调(只对 demo 页有意义;home/全屏不拖拽)。 */ +static void app_on_touch_down(int x, int y) +{ + if (demo_is_active()) demo_touch_down(x, y); +} +static void app_on_touch_move(int x, int y) +{ + if (demo_is_active()) demo_touch_move(x, y); +} + +static void app_on_swipe_progress(int x_start, int dx) +{ + meow_ui_swipe_hint(x_start + dx); +} + +static void app_on_tick(void) +{ + uint64_t now = meow_ui_time_ms(); + if (demo_is_active()) { demo_tick(now); return; } + static uint64_t last = 0; /* home/全屏计时:1s 一跳(demo 不计数,同旧) */ + if (now - last >= 1000) { last = now; logic_secs_add(1); } +} + +/* ============================================================ */ +/* main */ +/* ============================================================ */ +int main(void) +{ + meow_log(MEOW_LOG_INFO, "start"); + meow_ui_opts o = { 480, 720 }; + if (meow_ui_init(&o) != 0) { meow_log(MEOW_LOG_ERROR, "init failed"); return 1; } + meow_theme_add_listener(NULL); + + signal(SIGTERM, on_sigterm); + + /* Dialog / BottomSheet 实例(阻塞模态;打开前排空残留触摸由 SDK 处理) */ + g_dlg = meow_dialog_create(MEOW_DLG_SELECT, "Pick a fruit"); + const char *items[] = { "Apple", "Banana", "Cherry" }; + meow_dialog_set_items(g_dlg, items, 3, 1); + g_sheet = meow_sheet_create("Options", 0); + meow_sheet_add_item(g_sheet, "Copy"); + meow_sheet_add_item(g_sheet, "Delete"); + meow_sheet_add_item(g_sheet, "Share"); + + /* 建三页 view(on_create 建各自控件/状态)+ 弹层实例 */ + g_home_view = home_view_create(); + g_full_view = full_view_create(); + g_demo_view = demo_view_create(); + + /* 进入根页(activity_push + home on_enter → home_redraw) */ + meow_view_page_push(ACT_HOME); + + meow_ui_events ev = { app_on_key, app_on_tap, app_on_swipe, app_on_tick, app_on_swipe_progress, + app_on_touch_down, app_on_touch_move }; + meow_ui_run(&ev); + + /* 清理:meow_view_free 触发各页 on_destroy(home 控件 / demo 内容+chrome) */ + meow_view_free(g_full_view); + meow_view_free(g_demo_view); + meow_view_free(g_home_view); + meow_dialog_free(g_dlg); + meow_sheet_free(g_sheet); + meow_ui_shutdown(); + meow_log(MEOW_LOG_INFO, "exit"); + return 0; +} diff --git a/patch_v2/hello_app/build_hello.sh b/patch_v2/hello_app/build_hello.sh index 860ce79..908e32a 100755 --- a/patch_v2/hello_app/build_hello.sh +++ b/patch_v2/hello_app/build_hello.sh @@ -1,11 +1,16 @@ #!/bin/bash -# Build the hello app (SDK B1 版): 链 meow_ui.a(已内含 meow_font.o), +# Build the hello app (SDK B1, 视图框架批次②文件拆分): 链 meow_ui.a(已内含 meow_font.o), # 额外链系统 libz.a(meow_font 的 inflate 依赖)+ -lpthread。静态 MIPS32r2 (musl sf)。 +# 批次②:hello.c 拆为多文件(app.c + ui/home.c + ui/full.c + ui/demo.c + ui/common/chrome.c + +# logic/notify.c + logic/state.c),仅改列文件,编译期不改 SDK(patch_v2/sdk/ 零触及)。 +# mipsel -Wall -Wextra 0 warning。 set -e cd "$(dirname "$0")" ROOT="$HOME/Project/R3ProII" CC="$ROOT/.tools/zig-mipsel-cc" SYSROOT="$ROOT/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf" -"$CC" -Os -static -I../sdk hello.c ../sdk/meow_ui.a "$SYSROOT/lib/libz.a" -lpthread -o hello +"$CC" -Os -static -Wall -Wextra -I../sdk -I. \ + app.c ui/home.c ui/full.c ui/demo.c ui/common/chrome.c logic/notify.c logic/state.c \ + ../sdk/meow_ui.a "$SYSROOT/lib/libz.a" -lpthread -o hello ls -la hello file hello diff --git a/patch_v2/hello_app/hello b/patch_v2/hello_app/hello index 9aa889b..b051950 100755 Binary files a/patch_v2/hello_app/hello and b/patch_v2/hello_app/hello differ diff --git a/patch_v2/hello_app/hello_internal.h b/patch_v2/hello_app/hello_internal.h new file mode 100644 index 0000000..15f8adc --- /dev/null +++ b/patch_v2/hello_app/hello_internal.h @@ -0,0 +1,102 @@ +/* hello_internal.h — hello 应用内部共享头(UI 各页 + app 装配层 + 宿主测试面)。 + * + * 层级(SDK_VIEW_FRAMEWORK_DESIGN.md §B.1/B.2): + * - ui/ 每页一个 view 文件(持视图态 scroll/focus/drag/几何);共享 chrome 在 ui/common/; + * - logic 下纯业务模块(state: taps/secs;notify: 通知发送/取消),UI 只经 logic_* / notify_* 调用; + * - app.c main + 装配 + 事件分发(把 meow_ui_run 的平铺回调路由到各页)。 + * 页面组织:home/full/demo 各建一个 meow_view(ACTIVITY),生命周期用 + * meow_view_page_push/pop 驱动(on_enter/on_leave 唯一入口);绘图仍走 batch① 已接线的 + * 路径(home/full 手绘 + demo 走 meow_ui_paint_full 注册表),on_draw 本批置 NULL(见报告)。 + * + * 本头是全应用/测试的跨 TU 声明:只声明(extern),不定义,避免多定义。宿主 test_p1p2p3_repro + * 直接 #include 本头 + 链接各 .c,驱动 demo 真实函数(demo_paint/demo_rebuild_to/demo_touch_down) + * 对 back_buf 做断言——这是测「真实 demo 代码 + 真实 SDK」,不是模拟桩。 + */ +#ifndef HELLO_INTERNAL_H +#define HELLO_INTERNAL_H + +#include +#include "meow_ui.h" +#include "meow_ui_view.h" +#include "logic/logic.h" + +/* ---- App 级弹层(app.c 创建;demo 页打开/关)---- */ +extern meow_dialog *g_dlg; /* Dialog(阻塞模态;main 创建一次) */ +extern meow_sheet *g_sheet; /* BottomSheet(阻塞模态;main 创建一次) */ + +/* ---- Activity 归属(§6.1 ①)---- */ +#define ACT_HOME 0 +#define ACT_FULL 1 +#define ACT_DEMO 2 + +/* ---- SDK 控件不透明:App 自记几何,用于局部 flush / 命中 / scroll 重建 ---- */ +typedef struct { int x, y, w, h; } rect_t; +#define RX(x,y,w,h) ((rect_t){ (x), (y), (w), (h) }) + +/* ---- 视图控制器(ctx = biz-UI 绑定点)---- + * 本批控制器为薄壳:持 meow_view 回指针;页面细粒度视图态(g_ds/g_drag_* /scroll 等)留在各页 + * 模块作用域全局(设计定案 B:demo 视图态不迁 logic)。控制器未来可吸收这些状态。 */ +typedef struct { + meow_view *view; + int active; /* 是否有页进入(与模块 active 全局同步) */ + int _pad; +} home_ctrl; +typedef struct { + meow_view *view; + int active; + int _pad; +} full_ctrl; +typedef struct { + meow_view *view; + int active; /* = g_demo */ + int focus; /* = g_demo_focus */ + int scroll; /* = g_demo_scroll */ + int _pad; +} demo_ctrl; + +/* ---- home 页(ui/home.c)---- */ +meow_view *home_view_create(void); /* 建 home view;on_create 建 home 控件 + 注册业务监听器 */ +int home_on_tap(int x, int y);/* home 态命中分发:返回 1=已消费(含导航到 demo/full) */ + +/* ---- full 页(ui/full.c)---- */ +meow_view *full_view_create(void); /* 建 full view(无控件;全屏文本由 full_draw 手绘) */ +int full_is_active(void); /* 1=处于全屏态 */ +void full_draw(void); /* 画全屏帧(clear accent + 文本 + flush) */ + +/* ---- demo 页(ui/demo.c)---- */ +meow_view *demo_view_create(void); /* 建 demo view;on_create 调 demo_init() 建 chrome+内容 */ +int demo_is_active(void); /* 1=处于 demo 页 */ +void demo_on_tap(int x, int y); +void demo_touch_down(int x, int y); +void demo_touch_move(int x, int y); +void demo_swipe(int dir, int dist); +void demo_tick(uint64_t now); + +/* demo 构建/绘制入口(宿主 test_p1p2p3_repro 直接驱动 + demo view 复用): + * demo_init 建 chrome+内容(scroll=0);demo_paint 整帧按 z 重画;demo_rebuild_to 重建到 scroll。 */ +void demo_init(void); +void demo_paint(void); +void demo_rebuild_to(int scroll); + +/* ---- demo 视图态(设计定案 B:g_ds/g_drag_* /scroll 留 ui/demo.c,不迁 logic)---- + * 暴露为 extern,供宿主测试对真实 demo 状态断言(P1/P2/P3)。 */ +extern int g_demo; /* 1=处于 demo 演示页 */ +extern int g_demo_focus; /* 1=LazyColumn 聚焦 */ +extern int g_demo_scroll; /* 内容区滚动偏移 */ +extern int g_drag_list; /* 手指在列表:拖拽滚列表 */ +extern int g_drag_page; /* 手指在页内容:拖拽滚页面 */ +extern meow_widget *g_dlc; /* LazyColumn */ +extern meow_widget *g_dprc; /* ProgressBar 圆形 */ +extern rect_t r_prc; /* 圆形进度外接框 */ +extern rect_t r_list; /* 列表区几何 */ +extern rect_t r_dr1, r_dr2; /* Radio 几何 */ + +/* ---- chrome(ui/common/chrome.c)---- */ +extern meow_widget *g_tb; /* TopAppBar(chrome 固定) */ +extern meow_widget *g_nav; /* NavigationBar(chrome 固定) */ +extern meow_widget *g_fab; /* FAB(chrome 置顶;demo 交互可重建换 label) */ +extern rect_t r_tb, r_nav, r_fab; +void chrome_create(void); /* 建 chrome(调用方须先 push ACT_DEMO 使 owner=ACT_DEMO) */ +void chrome_free(void); /* 释放 chrome(demo on_destroy 调) */ + +#endif /* HELLO_INTERNAL_H */ diff --git a/patch_v2/hello_app/logic/logic.h b/patch_v2/hello_app/logic/logic.h new file mode 100644 index 0000000..4ca4ae1 --- /dev/null +++ b/patch_v2/hello_app/logic/logic.h @@ -0,0 +1,30 @@ +/* logic/logic.h — 业务层接口(纯 C,不依赖 meow_ui.h / meow_view)。 + * + * 分层契约(SDK_VIEW_FRAMEWORK_DESIGN.md §B.1): + * ① UI→业务 = 纯函数命令 + getter(logic_* / notify_* 返回业务态,业务不回调 UI); + * ② 业务→UI = 可选 push 监听器(logic_listener_fn + logic_set_listener); + * ③ view 的 ctx = 视图控制器(biz-UI 绑定点)。 + * 本头只声明数据类型与函数原型,不含任何 meow_ui.h / meow_ui_view.h 依赖—— + * logic/state.c 因此保持纯 C;logic/notify.c 仅因要用 SDK 通知 API 而 include meow_ui.h。 + */ +#ifndef HELLO_LOGIC_H +#define HELLO_LOGIC_H + +/* 监听器事件类型(业务态变化 → 通知 UI) */ +#define LOGIC_EV_TAPS 1 /* taps 计数变化 */ +#define LOGIC_EV_SECS 2 /* secs 秒计数变化 */ + +typedef void (*logic_listener_fn)(int ev, void *ctx); /* ctx = 监听器注册时给的视图控制器 */ + +/* ---- 计数业务态(logic/state.c)---- */ +int logic_taps_get(void); /* 返回累计 taps */ +void logic_taps_bump(void); /* taps++,并触发监听器(若已注册) */ +int logic_secs_get(void); /* 返回过去秒数 */ +void logic_secs_add(int n); /* secs += n,并触发监听器(若已注册) */ +void logic_set_listener(logic_listener_fn cb, void *ctx); /* 注册业务→UI 推送监听器(可 NULL 注销) */ + +/* ---- 通知业务(logic/notify.c)---- */ +void notif_send(int which); /* 按索引发一条通知(数据/超时/优先级在业务层定) */ +void notif_cancel(void); /* 取消最近一条发送的通知 */ + +#endif /* HELLO_LOGIC_H */ diff --git a/patch_v2/hello_app/logic/notify.c b/patch_v2/hello_app/logic/notify.c new file mode 100644 index 0000000..8675bbd --- /dev/null +++ b/patch_v2/hello_app/logic/notify.c @@ -0,0 +1,56 @@ +/* logic/notify.c — 通知业务(notif_send/notif_cancel)。 + * + * 纯业务:只依赖 SDK 通知数据类型/API(meow_notify_*)+ meow_log,不碰任何 UI 控件/view。 + * UI 只经 notif_send()/notif_cancel() 调用(契约 ①);本层不回调 UI,最近一条 id 由本层持有。 + * 注:boundary 折中——通知是 SDK 服务(§9.1),故本文件 include meow_ui.h 仅用其通知类型/函数。 + */ +#include "logic.h" +#include "meow_ui.h" +#include +#include + +static uint32_t g_last_notif = 0; /* 最近一次 send 返回的 id,供 cancel */ + +void notif_send(int which) +{ + meow_notify n; + memset(&n, 0, sizeof n); + n.progress = -1; + n.timeout_ms = MEOW_NOTIFY_TIMEOUT_NEVER; + n.urgency = MEOW_NOTIFY_URG_NORMAL; + const char *label = "?"; + + switch (which) { + case 0: label = "INFO"; n.cat = MEOW_NOTIFY_CAT_GENERAL; + n.title = "测试-普通通知"; n.body = "普通 INFO 通知,timeout=5000ms"; n.timeout_ms = 5000; break; + case 1: label = "MEDIA"; n.cat = MEOW_NOTIFY_CAT_MEDIA; n.urgency = MEOW_NOTIFY_URG_LOW; + n.title = "测试-媒体通知"; n.body = "媒体单例,正在播放,timeout=-1 常驻"; + n.timeout_ms = MEOW_NOTIFY_TIMEOUT_ONGOING; break; + case 2: label = "HIGH"; n.cat = MEOW_NOTIFY_CAT_GENERAL; n.urgency = MEOW_NOTIFY_URG_HIGH; + n.title = "测试-紧急度HIGH"; n.body = "高紧急度:顶部横幅+提示音"; n.timeout_ms = 3000; break; + case 3: label = "PROG"; n.cat = MEOW_NOTIFY_CAT_PROGRESS; + n.title = "测试-进度通知"; n.body = "下载进度 40%"; n.timeout_ms = 8000; n.progress = 40; break; + case 4: label = "ONGO"; n.cat = MEOW_NOTIFY_CAT_GENERAL; + n.title = "测试-常驻通知"; n.body = "常驻不自动过期(timeout=-1),需手动取消"; + n.timeout_ms = MEOW_NOTIFY_TIMEOUT_ONGOING; break; + case 5: label = "TGT"; n.cat = MEOW_NOTIFY_CAT_MESSAGE; + n.title = "测试-跳转Activity"; n.body = "点击跳转 target_activity=main"; + n.timeout_ms = 6000; n.target_activity = "main"; n.icon = NULL; break; + default: return; + } + + uint32_t id = meow_notify_send(&n); + g_last_notif = id; + meow_log(MEOW_LOG_INFO, + "notify btn=%s id=%u cat=%s urg=%s tmo=%d prog=%d target=%s title=%s", + label, (unsigned)id, meow_notify_category_str(n.cat), + meow_notify_urgency_str(n.urgency), n.timeout_ms, n.progress, + n.target_activity ? n.target_activity : "-", n.title ? n.title : "-"); +} + +void notif_cancel(void) +{ + int r = meow_notify_cancel(g_last_notif); + meow_log(MEOW_LOG_INFO, "notify cancel id=%u result=%d", (unsigned)g_last_notif, r); + g_last_notif = 0; +} diff --git a/patch_v2/hello_app/logic/state.c b/patch_v2/hello_app/logic/state.c new file mode 100644 index 0000000..f676b0a --- /dev/null +++ b/patch_v2/hello_app/logic/state.c @@ -0,0 +1,38 @@ +/* logic/state.c — 计数业务态(taps/secs)+ 可选推送监听器(biz→UI)。 + * + * 纯 C(无 meow_ui.h 依赖):状态 + getter/setter + 监听器。UI 经 logic_taps_bump()/ + * logic_secs_add() 改状态;业务改后触发监听器(若已注册)通知 UI 刷新——契约 ①②(§B.1)。 + */ +#include "logic.h" +#include + +static int g_taps = 0; +static int g_secs = 0; +static logic_listener_fn g_listener = NULL; +static void *g_listener_ctx = NULL; + +int logic_taps_get(void) +{ + return g_taps; +} +void logic_taps_bump(void) +{ + g_taps++; + if (g_listener) g_listener(LOGIC_EV_TAPS, g_listener_ctx); +} + +int logic_secs_get(void) +{ + return g_secs; +} +void logic_secs_add(int n) +{ + g_secs += n; + if (g_listener) g_listener(LOGIC_EV_SECS, g_listener_ctx); +} + +void logic_set_listener(logic_listener_fn cb, void *ctx) +{ + g_listener = cb; + g_listener_ctx = ctx; +} diff --git a/patch_v2/hello_app/ui/common/chrome.c b/patch_v2/hello_app/ui/common/chrome.c new file mode 100644 index 0000000..25a2fea --- /dev/null +++ b/patch_v2/hello_app/ui/common/chrome.c @@ -0,0 +1,36 @@ +/* ui/common/chrome.c — demo 页共享 chrome(TopAppBar / NavigationBar / FAB)。 + * + * 与 demo 内容分离:chrome 固定不随内容重建滚动;显式 set_z(999) 置顶(§6.2), + * 区域/整帧重画都最后画 chrome。FAB 可被 demo 交互重建换计数 label(见 demo_on_tap)。 + * chrome 控件 owner=ACT_DEMO(由调用方 demo_init 先 push ACT_DEMO 再建),命中/绘制只在 demo 态生效。 + */ +#include "hello_internal.h" + +meow_widget *g_tb = NULL; /* TopAppBar(chrome 固定) */ +meow_widget *g_nav = NULL; /* NavigationBar(chrome 固定) */ +meow_widget *g_fab = NULL; /* FAB(chrome 固定,可重建换计数 label) */ +rect_t r_tb, r_nav, r_fab; + +void chrome_create(void) +{ + int sb = meow_ui_statusbar_height(); + r_tb = RX(0, sb, 480, 56); g_tb = meow_topappbar_create(0, sb, 480, 56, "Widgets Demo"); + meow_topappbar_set_back(g_tb, 1); + r_nav = RX(0, 680, 480, 40); g_nav = meow_navigationbar_create(0, 680, 480, 40); + meow_navigationbar_add_item(g_nav, "Buttons"); + meow_navigationbar_add_item(g_nav, "List"); + meow_navigationbar_add_item(g_nav, "More"); + meow_navigationbar_set_selected(g_nav, 0); + r_fab = RX(406, 616, 56, 56); g_fab = meow_fab_create(406, 616, 56, "+"); + /* chrome 显式置顶(§6.2):> 一切自动 z(127),区域重画/整帧都最后画 chrome。 */ + meow_widget_set_z(g_tb, 999); + meow_widget_set_z(g_nav, 999); + meow_widget_set_z(g_fab, 999); +} + +void chrome_free(void) +{ + if (g_tb) { meow_widget_free(g_tb); g_tb = NULL; } + if (g_nav) { meow_widget_free(g_nav); g_nav = NULL; } + if (g_fab) { meow_widget_free(g_fab); g_fab = NULL; } +} diff --git a/patch_v2/hello_app/hello.c b/patch_v2/hello_app/ui/demo.c similarity index 52% rename from patch_v2/hello_app/hello.c rename to patch_v2/hello_app/ui/demo.c index aa02370..cb25477 100644 --- a/patch_v2/hello_app/hello.c +++ b/patch_v2/hello_app/ui/demo.c @@ -1,95 +1,45 @@ -/* hello.c — MeowOS SDK 版示例应用(链 meow_ui.a,B1)。 +/* ui/demo.c — 控件库 Demo 演示页(ACT_DEMO)view + 控制器。 * - * 契约(MEOW_APP_SDK.md §1/§3/§6):应用只调 meow_ui API —— 屏幕(back_buf + - * flush 上屏)与输入(meow_ui_run 事件循环)由框架接管;不再 open/mmap/ioctl - * /dev/fb0、不再读 /dev/input/event*。 + * 内容控件以绝对坐标创建,页滚动 = 按「base_y - scroll」重建内容(owner=ACT_DEMO 不变); + * 各控件跨重建状态存 g_ds(App 持有),重建后恢复。绘制走 batch① 郑序注册表:demo_paint 调 + * meow_ui_set_page_bg + meow_ui_paint_full(整帧按 z 重画:内容→标题/slider值→chrome 置顶), + * 局部刷新统一 meow_ui_invalidate(清底+按 z 重画相交绘制物)。旧 d_paint_region/d_redraw/ + * d_rect_overlap(App 层郑栈重画)已删、下沉 SDK。 * - * 两大职责: - * A) 原有:验证「清屏 + 画文本 + 画按钮 + 响应触摸」最小闭环 + 通知触发按钮 - * (meow_notify_send,§9.1)端到端测试通知链路; - * B) 新增:**控件库 Demo 演示页**(§5.8 第一/二批组件)——作为 SDK 控件库的 - * 验证载体 + 活文档:用真实控件(meow_btn_* / meow_switch_* / meow_slider_* / - * meow_widget_custom_create / meow_*)搭「展示 + 可交互」界面,用户刷 hello - * 从 home 的「WIDGETS DEMO」按钮进入,逐项验证各组件外观 / 命中 / 状态。 - * - * 交互: - * - home:点 TAP ME → 回圈计数;6 个通知触发行;CANCEL LAST;FULLSCREEN; - * 新增「WIDGETS DEMO」按钮 → 进入 demo 页;左缘左滑 → 退出。 - * - demo 页:TopAppBar 返回箭头 / 左缘左滑 → 回 home;内容区展示并交互 - * Button 五型(含禁用) / Switch / Slider / TextField(点开阻塞 IME) / Checkbox / - * Radio(单选组) / ProgressBar(线性+圆形带动画) / Card(嵌子项) / Divider / - * Badge / TabBar / NavigationBar(底部) / TopAppBar(顶部) / Dialog+BottomSheet / - * FAB / LazyColumn(100 项列表,swipe 滚动)。配色取 meow_theme 语义色板(随系统 - * MD3/MIUIx + 明暗自动切,零硬编码品牌色)。交互局部刷新(meow_ui_flush_region, - * 不整屏、不冲状态栏)。 - * - * 页面/Activity(§6.1 ①):home(根,ACT_HOME 0)、全屏(ACT_FULL 1)、demo(ACT_DEMO 2) - * 三态互斥;控件 owner=创建时 active Activity,命中只在 owner==active 生效(根治跨页 - * 残留)。demo 控件在启动 push(ACT_DEMO) 下一次性创建(owner=ACT_DEMO),此后 home - * 态不命中不绘制、demo 态 home 控件不命中。 - * - * 滚动实现(关键:SDK 控件不透明——结构体对外不可见,App 不能读/写控件坐标;也无 - * 位置 setter): - * - 内容控件以**绝对坐标**创建(App 侧另存 rect_t 几何用于局部 flush/命中)。 - * - 页滚动 = 按「base_y - scroll」**重建**内容控件(仍处于 ACT_DEMO,owner 不变); - * 各控件跨重建状态存于 g_ds(App 持有),重建后恢复。 - * - LazyColumn 是第二滚动区:滚它时 meow_list_scroll_by + 列表区局部刷新,不重建。 - * - 因内容仅略超一屏(max_scroll 小),一次滑动仅触发 1~2 次重建,代价可接受。 - * - * 宿主/qemu 离屏:设 MEOW_UI_DUMP= 时,首帧 flush 即把 back_buf 写成 - * PPM 截图供检查;设备上无副作用。 - * - * Build(见 build_hello.sh): - * zig-mipsel-cc -Os -static hello.c meow_ui.a libz.a -lpthread -o hello + * 设计定案 B(SDK_VIEW_FRAMEWORK_DESIGN §B.3/F):demo 的 g_ds/g_drag_* /g_demo_scroll 等视图态 + * 留本文件(不迁 logic);UI→业务只经 logic_* / notify_*。页面进入/离开经 meow_view_page_push/pop + * 驱动 on_enter/on_leave(demo_page_enter/leave)。 */ -#include "meow_ui.h" +#include "hello_internal.h" #include #include -#include -#include -#include -#include +#include -/* §6.1 ①:Activity 归属。home=根(0);全屏=独立 Activity(无控件);demo=独立 Activity。 */ -#define ACT_HOME 0 -#define ACT_FULL 1 -#define ACT_DEMO 2 +/* 供 demo_content_tap / touch 回调用的前向声明 */ +static void demo_slider_drag(int x); -/* ---- home 既有控件 ---- */ -static meow_widget *g_btn; -static meow_widget *g_nbtn[6]; /* 通知触发按钮行 */ -static meow_widget *g_cancel; /* 取消最近一条通知 */ -static meow_widget *g_fs_btn; /* FULLSCREEN 进入按钮(主界面) */ -static meow_widget *g_demo_btn; /* 「控件库 Demo」入口按钮(主界面,新增) */ -static int g_fullscreen = 0; /* 1=处于全屏视图(点任意处退出回主界面) */ -static const char *g_notif_labels[6] = { "INFO", "MEDIA", "HIGH", "PROG", "ONGO", "TGT" }; -static uint32_t g_last_notif = 0; /* 最近一次 send 返回的 id,供 cancel */ -static int g_taps = 0; -static int g_secs = 0; -static char g_btn_label[32] = "TAP ME"; +static demo_ctrl g_demo_ctrl; /* ---- demo 演示页状态 ---- */ -static int g_demo = 0; /* 1=处于 demo 演示页 */ -static int g_demo_scroll = 0; /* 内容区滚动偏移(base 空间 px) */ -static int g_demo_focus = 0; /* 1=LazyColumn 聚焦(swipe 滚列表而非页面) */ -static int g_demo_fab = 0; /* FAB 点击计数反馈 */ -static int g_demo_prog = 0; /* ProgressBar 动画当前值 0..100 */ +int g_demo = 0; /* 1=处于 demo 演示页 */ +int g_demo_scroll = 0; /* 内容区滚动偏移(base 空间 px) */ +int g_demo_focus = 0; /* 1=LazyColumn 聚焦(swipe 滚列表而非页面) */ +static int g_demo_fab = 0; /* FAB 点击计数反馈 */ +static int g_demo_prog = 0; /* ProgressBar 动画当前值 0..100 */ static uint64_t g_demo_prog_last = 0; -static int g_demo_ct = 0; /* 内容视口顶(屏幕 y,= statusbar+56+8) */ -static int g_demo_cb = 0; /* 内容视口底(屏幕 y,=608,NavBar 上方留白) */ -static int g_demo_content_h = 0; /* 内容总高(base 空间;决定 max_scroll) */ +static int g_demo_ct = 0; /* 内容视口顶(屏y = statusbar+56+8) */ +static int g_demo_cb = 0; /* 内容视口底(屏y =608,NavBar 上方留白) */ +static int g_demo_content_h = 0;/* 内容总高(base 空间;决定 max_scroll) */ -/* M2.71-bug2/6:拖动过程(跟手)状态。on_touch_down 锁一种拖拽,on_touch_move 期间实时更新。 */ -static int g_drag_slider = 0; /* 手指按在 slider:拖拽改值(按即定位) */ -static int g_drag_list = 0; /* 手指按在 LazyColumn:拖拽滚列表 */ -static int g_drag_page = 0; /* 手指按在页内容(非 slider/list):拖拽滚页面 */ -static int g_drag_list_last_y = 0; /* 列表拖拽上一帧 y(求增量) */ +/* M2.71-bug2/6:拖动过程(跟手)状态。on_touch_down 锁一种拖拽,on_touch_move 实时更新。 */ +static int g_drag_slider = 0; /* 手指按在 slider */ +int g_drag_list = 0; /* 手指按在 LazyColumn:拖拽滚列表 */ +int g_drag_page = 0; /* 手指按在页内容(非 slider/list):拖拽滚页面 */ +static int g_drag_list_last_y = 0; /* 列表拖拽上一帧 y */ static int g_drag_page_last_y = 0; /* 页面拖拽上一帧 y */ static int g_drag_page_delta = 0; /* 页面拖拽累计位移(节流重建,≥8px 才重建一次) */ /* SDK 控件不透明:App 自记几何,用于局部 flush / 命中 / scroll 重建。 */ -typedef struct { int x, y, w, h; } rect_t; -#define RX(x,y,w,h) ((rect_t){ (x), (y), (w), (h) }) /* demo 各控件跨重建保留的状态(app 持有;重建后按此恢复) */ static struct { @@ -115,34 +65,27 @@ static int g_dslot_n = 0; static struct { int x, y; const char *s; } g_dtitle[D_TITLE_MAX]; static int g_dtitle_n = 0; -/* demo 交互组件引用(rebuild 时 g_* 与 r_* 同步重指/recompute) */ -static meow_widget *g_tb; static rect_t r_tb; /* TopAppBar(chrome 固定) */ -static meow_widget *g_nav; static rect_t r_nav; /* NavigationBar(chrome 固定) */ -static meow_widget *g_fab; static rect_t r_fab; /* FAB(chrome 固定,可重建换计数 label) */ +/* demo 交互组件引用(rebuild 时 g_* 与 r_* 同步重指/recompute;chrome 在 ui/common/chrome.c) */ static meow_widget *g_dbtn_b[5]; static rect_t r_btn[5]; /* Button 五型 */ static meow_widget *g_dbtn_d; /* DISABLED 按钮(禁用不命中,无需 rect) */ static meow_widget *g_dsw; static rect_t r_dsw; /* Switch */ static meow_widget *g_dslider; static rect_t r_slider, r_slider_txt; /* Slider + 值文本 */ static meow_widget *g_dtf; static rect_t r_dtf; /* TextField */ static meow_widget *g_dcb; static rect_t r_dcb; /* Checkbox */ -static meow_widget *g_dr1, *g_dr2; static rect_t r_dr1, r_dr2; /* Radio 单选组 */ +static meow_widget *g_dr1, *g_dr2; rect_t r_dr1, r_dr2; /* Radio 单选组 */ static meow_widget *g_dprl; static rect_t r_prl; /* ProgressBar 线性 */ -static meow_widget *g_dprc; static rect_t r_prc; /* ProgressBar 圆形 */ +meow_widget *g_dprc; rect_t r_prc; /* ProgressBar 圆形 */ static meow_widget *g_dcard; static rect_t r_card; /* Card */ static meow_widget *g_dcard_btn; static rect_t r_cardbtn; /* Card 内容子按钮 */ static meow_widget *g_dbadge; /* Badge */ static meow_widget *g_dtab; static rect_t r_tab; /* TabBar */ static meow_widget *g_dbtn_dlg; static rect_t r_dlg; /* 打开 Dialog 的按钮 */ static meow_widget *g_dbtn_sheet; static rect_t r_sheet;/* 打开 BottomSheet 的按钮 */ -static meow_widget *g_dlc; static rect_t r_list; /* LazyColumn */ - -/* Dialog / BottomSheet(阻塞模态;非控件,main() 创建一次) */ -static meow_dialog *g_dlg; -static meow_sheet *g_sheet; +meow_widget *g_dlc; rect_t r_list; /* LazyColumn */ /* ---- demo 分区标题/slider 值文本:注册为 CUSTOM 绘制物(§6.2 区域重画会重画它们)---- - * 显式 z=D_TITLE_Z:> 一切自动 z(=127)、< chrome 置顶(999),故「内容→标题→slider值→chrome」。 - * 非控件直绘必须注册,否则 meow_ui_invalidate 清底后会漏画(非保留模型固有代价)。 */ + * 显式 z=D_TITLE_Z:> 一切自动 z(=127)、< chrome 置顶(999)。非控件直绘必须注册,否则 + * meow_ui_invalidate 清底后会漏画。 */ #define D_TITLE_Z 128 static int g_dtitle_cid[D_TITLE_MAX]; /* 分区标题绘制物 id(rebuild 时注销/重注册) */ static int g_slider_cid = 0; /* slider 值文本绘制物 id */ @@ -165,77 +108,7 @@ static void demo_draw_slider_val(void *ctx) meow_ui_text(r_slider_txt.x, r_slider_txt.y, buf, 20, th->dim); } -/* 前向声明 */ -static void redraw(void); -static void demo_rebuild_to(int scroll); -static void demo_paint(void); -static void demo_slider_drag(int x); - -/* M2.51:统一日志改用 SDK meow_log()。 */ -static void on_sigterm(int s) { (void)s; meow_ui_request_exit(); } - -/* ============================================================ */ -/* 通知触发(home 既有) */ -/* ============================================================ */ -static void notif_send(int which) -{ - meow_notify n; - memset(&n, 0, sizeof n); - n.progress = -1; - n.timeout_ms = MEOW_NOTIFY_TIMEOUT_NEVER; - n.urgency = MEOW_NOTIFY_URG_NORMAL; - const char *label = "?"; - - switch (which) { - case 0: label = "INFO"; n.cat = MEOW_NOTIFY_CAT_GENERAL; - n.title = "测试-普通通知"; n.body = "普通 INFO 通知,timeout=5000ms"; n.timeout_ms = 5000; break; - case 1: label = "MEDIA"; n.cat = MEOW_NOTIFY_CAT_MEDIA; n.urgency = MEOW_NOTIFY_URG_LOW; - n.title = "测试-媒体通知"; n.body = "媒体单例,正在播放,timeout=-1 常驻"; - n.timeout_ms = MEOW_NOTIFY_TIMEOUT_ONGOING; break; - case 2: label = "HIGH"; n.cat = MEOW_NOTIFY_CAT_GENERAL; n.urgency = MEOW_NOTIFY_URG_HIGH; - n.title = "测试-紧急度HIGH"; n.body = "高紧急度:顶部横幅+提示音"; n.timeout_ms = 3000; break; - case 3: label = "PROG"; n.cat = MEOW_NOTIFY_CAT_PROGRESS; - n.title = "测试-进度通知"; n.body = "下载进度 40%"; n.timeout_ms = 8000; n.progress = 40; break; - case 4: label = "ONGO"; n.cat = MEOW_NOTIFY_CAT_GENERAL; - n.title = "测试-常驻通知"; n.body = "常驻不自动过期(timeout=-1),需手动取消"; - n.timeout_ms = MEOW_NOTIFY_TIMEOUT_ONGOING; break; - case 5: label = "TGT"; n.cat = MEOW_NOTIFY_CAT_MESSAGE; - n.title = "测试-跳转Activity"; n.body = "点击跳转 target_activity=main"; - n.timeout_ms = 6000; n.target_activity = "main"; n.icon = NULL; break; - default: return; - } - - uint32_t id = meow_notify_send(&n); - g_last_notif = id; - meow_log(MEOW_LOG_INFO, - "notify btn=%s id=%u cat=%s urg=%s tmo=%d prog=%d target=%s title=%s", - label, (unsigned)id, meow_notify_category_str(n.cat), - meow_notify_urgency_str(n.urgency), n.timeout_ms, n.progress, - n.target_activity ? n.target_activity : "-", n.title ? n.title : "-"); -} - -static void notif_cancel(void) -{ - int r = meow_notify_cancel(g_last_notif); - meow_log(MEOW_LOG_INFO, "notify cancel id=%u result=%d", (unsigned)g_last_notif, r); - g_last_notif = 0; -} - -static void set_fullscreen(int on) -{ - g_fullscreen = on ? 1 : 0; - meow_ui_set_fullscreen(g_fullscreen); - meow_ui_statusbar_set_collapsed(g_fullscreen); - if (on) meow_ui_activity_push(ACT_FULL); - else meow_ui_activity_pop(); - meow_log(MEOW_LOG_INFO, g_fullscreen ? "fullscreen ON (collapsed), activity->%d" - : "fullscreen OFF (restore), activity->%d", - meow_ui_activity_active()); -} - -/* ============================================================ */ -/* demo 演示页:布局 / 绘制 / 交互 */ -/* ============================================================ */ +/* ---- demo 构建/布局 ---- */ static void d_slot(meow_widget *w, rect_t r) { @@ -247,7 +120,6 @@ static void d_title(int x, int y, const char *s) int i = g_dtitle_n; g_dtitle[i].x = x; g_dtitle[i].y = y; g_dtitle[i].s = s; g_dtitle_n++; - /* 注册为 CUSTOM 绘制物(bbox=文本绘制范围;cb 读 g_dtitle[i]) */ int tw = meow_ui_text_width(s, 20); int th = meow_ui_text_height(20); g_dtitle_cid[i] = meow_draw_custom_register(D_TITLE_Z, x, y, tw, th, @@ -328,8 +200,6 @@ static void demo_build_at(int scroll) /* ---- ProgressBar(线性 + 圆形,on_tick 动画) ---- */ d_title(DCL_X, SY(yb), "PROGRESS (animated)"); yb += 26; r_prl = RX(DCL_X, SY(yb), DFW, 8); g_dprl = meow_progress_create(r_prl.x, r_prl.y, r_prl.w, r_prl.h, g_ds.prog); SLOT(g_dprl, r_prl.x, r_prl.y, r_prl.w, r_prl.h); yb += 16; - /* 圆形进度:r_prc = 整圆像素外接框(中心 ± (r+margin),r=17 → 40×40),使 d_redraw 的 - * flush 区与控件 bbox(=真实绘制范围)一致,不漏刷外圈(bgg M2.71-bug1)。中心仍取原视觉点。 */ r_prc = RX(DCL_X + 400 + 17 - 20, SY(yb) + 17 - 20, 40, 40); g_dprc = meow_progress_circular_create(r_prc.x + 20, r_prc.y + 20, 17, g_ds.prog); SLOT(g_dprc, r_prc.x, r_prc.y, r_prc.w, r_prc.h); yb += 48; @@ -371,10 +241,8 @@ static void demo_build_at(int scroll) #undef SLOT } -/* demo 全页重绘:整帧按 z 序重画全部(set_page_bg 清底 + 注册表按 z 升序)。 - * 此前手工遍历 g_dslot/g_dtitle/后画 chrome 已删;现在自动 z 排:内容(0..127)→标题/slider值(128) - * →chrome(999 置顶),与旧绘制序一致,但「清底+重画」由 SDK 统一按 z 保证(P1 修法下沉)。 */ -static void demo_paint(void) +/* demo 全页重绘:整帧按 z 序重画全部(set_page_bg 清底 + 注册表按 z 升序)。 */ +void demo_paint(void) { const meow_theme *th = meow_theme_get(); meow_ui_set_page_bg(th->bg); /* 页面底 = 主题 bg(区域/整帧重画清底用) */ @@ -382,7 +250,7 @@ static void demo_paint(void) } /* 重建内容到指定 scroll(clamp 到 [0, max_scroll]);进入/滚动共用 */ -static void demo_rebuild_to(int scroll) +void demo_rebuild_to(int scroll) { int vp_h = g_demo_cb - g_demo_ct; int maxs = g_demo_content_h - vp_h; @@ -395,9 +263,9 @@ static void demo_rebuild_to(int scroll) demo_paint(); } -/* 局部刷新统一走 SDK meow_ui_invalidate(清底 + 按 z 重画相交绘制物 + 上屏); - * 旧 d_paint_region/d_redraw/d_rect_overlap(App 层 z 栈重画)已删除、下沉到 SDK(§6.2)。 - * 局部刷新一控件:整区清底 + 重画重叠 z 栈由注册表完成。 */ +/* ---- demo 交互 ---- */ + +/* 局部刷新统一走 SDK meow_ui_invalidate(清底 + 按 z 重画相交绘制物 + 上屏)。 */ static void d_toggle_sel(meow_widget *w, rect_t r) { if (!w) return; @@ -441,11 +309,11 @@ static int demo_content_tap(int x, int y) } /* demo 页 on_tap 分发(chrome → 内容) */ -static void demo_on_tap(int x, int y) +void demo_on_tap(int x, int y) { if (g_tb) { int ah = meow_topappbar_hit(g_tb, x, y); - if (ah == MEOW_TOPBAR_BACK) { g_demo = 0; meow_ui_activity_pop(); redraw(); return; } + if (ah == MEOW_TOPBAR_BACK) { meow_view_page_pop(); return; } } if (g_fab && meow_widget_hit(g_fab, x, y)) { g_demo_fab++; @@ -460,11 +328,11 @@ static void demo_on_tap(int x, int y) int idx = meow_navigationbar_hit(g_nav, x, y); if (idx >= 0) { meow_navigationbar_set_selected(g_nav, idx); meow_ui_invalidate(r_nav.x, r_nav.y, r_nav.w, r_nav.h); return; } } - if (demo_content_tap(x, y)) return; + demo_content_tap(x, y); } /* demo 页 on_tick:ProgressBar 动画(局部刷,~120ms 步进) */ -static void demo_tick(uint64_t now) +void demo_tick(uint64_t now) { if (g_demo_prog_last == 0) g_demo_prog_last = now; if (now - g_demo_prog_last < 120) return; @@ -485,20 +353,15 @@ static void demo_slider_drag(int x) meow_ui_invalidate(r_slider_txt.x, r_slider_txt.y, r_slider_txt.w, r_slider_txt.h); /* 重画值文本 */ } -/* demo 页 on_touch_down:锁一种拖拽(slider > list > 页内容)。非 demo 态跳过。 - * P2:滚动焦点按「本次触摸起作用的控件」绑定、抬手/命中其它区域即释放——命中非 list - * 区域时不再沿用「已聚焦列表」的旧焦点去滚列表,而是切到页/上一层滚动(否则滑完列表再 - * 滑页面,焦点仍在列表里继续滚列表,需额外点一下才出来)。 */ -static void demo_touch_down(int x, int y) +/* demo 页 on_touch_down:锁一种拖拽(slider > list > 页内容)。 */ +void demo_touch_down(int x, int y) { g_drag_slider = 0; g_drag_list = 0; g_drag_page = 0; g_drag_page_delta = 0; if (!g_demo) return; if (meow_widget_hit(g_dslider, x, y)) { g_drag_slider = 1; demo_slider_drag(x); return; } - /* 命中列表 → 本次手势滚列表(焦点绑到列表) */ if (g_dlc && meow_widget_hit(g_dlc, x, y)) { g_demo_focus = 1; g_drag_list = 1; g_drag_list_last_y = y; return; } - /* 未命中列表 → 释放列表焦点,本次手势滚页面 */ g_demo_focus = 0; if (y >= g_demo_ct && y < g_demo_cb && x >= 0 && x < meow_ui_width()) { g_drag_page = 1; g_drag_page_last_y = y; @@ -506,7 +369,7 @@ static void demo_touch_down(int x, int y) } /* demo 页 on_touch_move:拖动过程实时更新(slider 值 / 列表滚动 / 页面滚动节流重建)。 */ -static void demo_touch_move(int x, int y) +void demo_touch_move(int x, int y) { if (!g_demo) return; if (g_drag_slider) { demo_slider_drag(x); return; } @@ -525,11 +388,10 @@ static void demo_touch_move(int x, int y) } } -/* demo 页 on_swipe:LEFT=回 home;UP/DOWN=列表聚焦滚列表,否则页滚动。 - * M2.71-bug6:拖动过程已在 on_touch_move 实时跟手滚过,抬手不再重复滚(否则叠加一次全距跳变)。 */ -static void demo_swipe(int dir, int dist) +/* demo 页 on_swipe:LEFT=回 home;UP/DOWN=列表聚焦滚列表,否则页滚动。 */ +void demo_swipe(int dir, int dist) { - if (dir == MEOW_SWIPE_LEFT) { g_demo = 0; meow_ui_activity_pop(); redraw(); return; } + if (dir == MEOW_SWIPE_LEFT) { meow_view_page_pop(); return; } if (dir != MEOW_SWIPE_UP && dir != MEOW_SWIPE_DOWN) return; if (g_drag_page || g_drag_list) return; /* 本次手势已实时跟手滚动过 */ int del = (dir == MEOW_SWIPE_UP) ? dist : -dist; /* 手指上移 → 偏移增大 */ @@ -542,178 +404,61 @@ static void demo_swipe(int dir, int dist) } } -/* 进入 demo 演示页(重建到顶部) */ -static void demo_enter(void) +/* ---- demo 页 view(on_enter/on_leave/on_destroy 驱动进入/离开/释放)---- */ + +static void demo_page_enter(meow_view *v, void *ctx) { + (void)v; + ((demo_ctrl *)ctx)->active = 1; g_demo = 1; g_demo_focus = 0; - meow_ui_activity_push(ACT_DEMO); demo_rebuild_to(0); meow_log(MEOW_LOG_INFO, "demo enter scroll=0"); } -/* 启动时创建 chrome(固定)+ 内容(scroll=0);push(ACT_DEMO) 后创建使 owner=ACT_DEMO。 */ -static void demo_init(void) +static void demo_page_leave(meow_view *v, void *ctx) +{ + (void)v; + ((demo_ctrl *)ctx)->active = 0; + g_demo = 0; +} + +static void demo_page_destroy(meow_view *v, void *ctx) +{ + (void)v; (void)ctx; + demo_free_content(); /* 内容控件 + CUSTOM 标题/slider值 */ + chrome_free(); /* chrome(TopAppBar / NavigationBar / FAB) */ +} + +void demo_init(void) { - int sb = meow_ui_statusbar_height(); meow_ui_activity_push(ACT_DEMO); - r_tb = RX(0, sb, 480, 56); g_tb = meow_topappbar_create(0, sb, 480, 56, "Widgets Demo"); - meow_topappbar_set_back(g_tb, 1); - r_nav = RX(0, 680, 480, 40); g_nav = meow_navigationbar_create(0, 680, 480, 40); - meow_navigationbar_add_item(g_nav, "Buttons"); - meow_navigationbar_add_item(g_nav, "List"); - meow_navigationbar_add_item(g_nav, "More"); - meow_navigationbar_set_selected(g_nav, 0); - r_fab = RX(406, 616, 56, 56); g_fab = meow_fab_create(406, 616, 56, "+"); - /* chrome 显式置顶(§6.2 置顶):> 一切自动 z(127),区域重画/整帧都最后画 chrome。 */ - meow_widget_set_z(g_tb, 999); - meow_widget_set_z(g_nav, 999); - meow_widget_set_z(g_fab, 999); + chrome_create(); /* chrome 位置/置顶在 chrome.c */ demo_build_at(0); meow_ui_activity_pop(); g_demo_scroll = 0; } -/* ============================================================ */ -/* 既有 home 绘制 / 事件 */ -/* ============================================================ */ - -static void redraw(void) +int demo_is_active(void) { - const meow_theme *th = meow_theme_get(); - int sb = meow_ui_statusbar_height(); - if (g_fullscreen) { - meow_ui_clear(th->accent); - meow_ui_text_center(240, 330, "FULLSCREEN ACTIVE", 28, 0xFFFFFF); - meow_ui_text_center(240, 366, "tap anywhere = back to home", 20, 0xFFFFFF); - meow_ui_text_center(240, 394, "(left-edge swipe also returns)", 20, 0xFFFFFF); - meow_ui_flush(); - return; - } - if (sb > 0) meow_ui_fill_rect(0, sb, 480, 720 - sb, th->bg); - else meow_ui_clear(th->bg); - meow_ui_text_center(240, sb + 8, "MEOWOS HELLO", 28, th->fg); - meow_ui_text_center(240, sb + 38, "SDK: meow_ui.a", 20, th->dim); - meow_widget_draw(g_btn); - meow_ui_text_center(240, 372, "tap = send notification (see log)", 20, th->dim); - for (int i = 0; i < 6; i++) meow_widget_draw(g_nbtn[i]); - meow_widget_draw(g_cancel); - meow_widget_draw(g_fs_btn); - meow_widget_draw(g_demo_btn); - char line[64]; - snprintf(line, sizeof line, "taps=%d uptime=%ds", g_taps, g_secs); - meow_ui_text_center(240, 516, line, 20, th->dim); - meow_ui_text(20, 660, "left-swipe = exit", 20, th->dim); - meow_ui_flush(); + return g_demo; } -static void on_tap(int x, int y) +/* demo view:on_create 建 chrome+内容(一次,owner=ACT_DEMO);进入时 rebuild 到顶。 */ +static void demo_page_create(meow_view *v, void *ctx) { - if (g_demo) { demo_on_tap(x, y); return; } - if (g_fullscreen) { set_fullscreen(0); redraw(); return; } - if (meow_widget_hit(g_demo_btn, x, y)) { demo_enter(); return; } - for (int i = 0; i < 6; i++) { - if (meow_widget_hit(g_nbtn[i], x, y)) { notif_send(i); redraw(); return; } - } - if (meow_widget_hit(g_cancel, x, y)) { notif_cancel(); redraw(); return; } - if (meow_widget_hit(g_fs_btn, x, y)) { set_fullscreen(1); redraw(); return; } - if (meow_widget_hit(g_btn, x, y)) { - g_taps++; - snprintf(g_btn_label, sizeof g_btn_label, "TAPPED x%d", g_taps); - meow_widget_free(g_btn); - g_btn = meow_btn_create(120, 300, 240, 64, g_btn_label); - redraw(); - } -} - -static void on_key(int keycode, int is_down) -{ - (void)keycode; (void)is_down; - meow_log(MEOW_LOG_DEBUG, "key"); -} - -static void on_swipe_progress(int x_start, int dx) -{ - meow_ui_swipe_hint(x_start + dx); -} - -/* M2.71-bug2/6:拖动过程回调(只对 demo 页有意义;home/全屏不拖拽)。 */ -static void on_touch_down(int x, int y) -{ - if (g_demo) demo_touch_down(x, y); -} -static void on_touch_move(int x, int y) -{ - if (g_demo) demo_touch_move(x, y); -} - -static void on_swipe(int dir, int dx, int dy) -{ - (void)dy; - if (g_demo) { demo_swipe(dir, dx); return; } - if (dir == MEOW_SWIPE_LEFT) { - if (g_fullscreen) { set_fullscreen(0); redraw(); } - else { meow_log(MEOW_LOG_INFO, "exit:swipe"); meow_ui_request_exit(); } - } -} - -static void on_tick(void) -{ - uint64_t now = meow_ui_time_ms(); - if (g_demo) { demo_tick(now); return; } - static uint64_t last = 0; - if (now - last >= 1000) { - last = now; - g_secs++; - redraw(); - } -} - -int main(void) -{ - meow_log(MEOW_LOG_INFO, "start"); - meow_ui_opts o = { 480, 720 }; - if (meow_ui_init(&o) != 0) { meow_log(MEOW_LOG_ERROR, "init failed"); return 1; } - meow_theme_add_listener(NULL); - - signal(SIGTERM, on_sigterm); - - g_btn = meow_btn_create(120, 300, 240, 64, g_btn_label); - for (int i = 0; i < 6; i++) - g_nbtn[i] = meow_btn_create(3 + i * 80, 400, 74, 56, g_notif_labels[i]); - g_cancel = meow_btn_create(36, 464, 200, 42, "CANCEL LAST"); - g_fs_btn = meow_btn_create(244, 464, 200, 42, "FULLSCREEN"); - g_demo_btn = meow_btn_create(36, 560, 408, 44, "WIDGETS DEMO"); - - /* Dialog / BottomSheet 实例(阻塞模态;打开前排空残留触摸由 SDK 处理) */ - g_dlg = meow_dialog_create(MEOW_DLG_SELECT, "Pick a fruit"); - const char *items[] = { "Apple", "Banana", "Cherry" }; - meow_dialog_set_items(g_dlg, items, 3, 1); - g_sheet = meow_sheet_create("Options", 0); - meow_sheet_add_item(g_sheet, "Copy"); - meow_sheet_add_item(g_sheet, "Delete"); - meow_sheet_add_item(g_sheet, "Share"); - + (void)v; + ((demo_ctrl *)ctx)->view = v; demo_init(); - redraw(); +} - meow_ui_events ev = { on_key, on_tap, on_swipe, on_tick, on_swipe_progress, - on_touch_down, on_touch_move }; - meow_ui_run(&ev); - - meow_widget_free(g_demo_btn); - for (int i = 0; i < 6; i++) meow_widget_free(g_nbtn[i]); - meow_widget_free(g_cancel); - meow_widget_free(g_fs_btn); - meow_widget_free(g_btn); - /* demo 内容 + chrome */ - demo_free_content(); - meow_widget_free(g_tb); - meow_widget_free(g_nav); - meow_widget_free(g_fab); - meow_dialog_free(g_dlg); - meow_sheet_free(g_sheet); - meow_ui_shutdown(); - meow_log(MEOW_LOG_INFO, "exit"); - return 0; +meow_view *demo_view_create(void) +{ + static const meow_view_hooks h = { + .on_create = demo_page_create, + .on_enter = demo_page_enter, + .on_leave = demo_page_leave, + .on_destroy = demo_page_destroy, + }; + return meow_view_create(ACT_DEMO, MEOW_VIEW_ACTIVITY, &h, &g_demo_ctrl); } diff --git a/patch_v2/hello_app/ui/full.c b/patch_v2/hello_app/ui/full.c new file mode 100644 index 0000000..c54547f --- /dev/null +++ b/patch_v2/hello_app/ui/full.c @@ -0,0 +1,56 @@ +/* ui/full.c — 全屏页(ACT_FULL)view + 控制器。 + * + * 无控件:进入时声明全屏+状态栏收合,绘制 accent 底 + 三行文本;点任意处/左缘左滑回 home。 + * 页面切换由 meow_view_page_push/pop 驱动 on_enter/on_leave(activity_push/pop 在 page_push/pop 内)。 + * 注:on_leave 里 activity_active() 仍为 ACT_FULL(activity_pop 在其后),故 OFF 日志的 activity + * id 与旧 set_fullscreen(0) 略异——纯诊断日志差异,功能无变化(见报告)。 + */ +#include "hello_internal.h" + +static full_ctrl g_full_ctrl; +static int g_full = 0; /* 1=处于全屏视图 */ + +void full_draw(void) +{ + const meow_theme *th = meow_theme_get(); + meow_ui_clear(th->accent); + meow_ui_text_center(240, 330, "FULLSCREEN ACTIVE", 28, 0xFFFFFF); + meow_ui_text_center(240, 366, "tap anywhere = back to home", 20, 0xFFFFFF); + meow_ui_text_center(240, 394, "(left-edge swipe also returns)", 20, 0xFFFFFF); + meow_ui_flush(); +} + +static void full_page_enter(meow_view *v, void *ctx) +{ + (void)v; + ((full_ctrl *)ctx)->active = 1; + g_full = 1; + meow_ui_set_fullscreen(1); + meow_ui_statusbar_set_collapsed(1); + full_draw(); + meow_log(MEOW_LOG_INFO, "fullscreen ON (collapsed), activity->%d", meow_ui_activity_active()); +} + +static void full_page_leave(meow_view *v, void *ctx) +{ + (void)v; + ((full_ctrl *)ctx)->active = 0; + g_full = 0; + meow_ui_set_fullscreen(0); + meow_ui_statusbar_set_collapsed(0); + meow_log(MEOW_LOG_INFO, "fullscreen OFF (restore), activity->%d", meow_ui_activity_active()); +} + +int full_is_active(void) +{ + return g_full; +} + +meow_view *full_view_create(void) +{ + static const meow_view_hooks h = { + .on_enter = full_page_enter, + .on_leave = full_page_leave, + }; + return meow_view_create(ACT_FULL, MEOW_VIEW_ACTIVITY, &h, &g_full_ctrl); +} diff --git a/patch_v2/hello_app/ui/home.c b/patch_v2/hello_app/ui/home.c new file mode 100644 index 0000000..bc252c8 --- /dev/null +++ b/patch_v2/hello_app/ui/home.c @@ -0,0 +1,119 @@ +/* ui/home.c — 主页(ACT_HOME)view + 控制器。 + * + * 负责 home 控件(TAP ME / 6 通知行 / CANCEL LAST / FULLSCREEN / WIDGETS DEMO)+ 手绘(非注册表 + * 路径,batch① 仅 demo 迁注册表,home 保持命令式 draw+flush)+ 命中分发 + 业务监听(logic 推送)。 + * UI→业务只经 logic_taps_bump()/notif_send()/notif_cancel();业务变化经 logic_set_listener 回调 + * home_state_listener 刷新(契约 ①②)。导航到 demo/full 经 meow_view_page_push(单一入口)。 + */ +#include "hello_internal.h" +#include +#include + +static home_ctrl g_home_ctrl; + +/* ---- home 既有控件 ---- */ +static meow_widget *g_btn; +static meow_widget *g_nbtn[6]; /* 通知触发按钮行 */ +static meow_widget *g_cancel; /* 取消最近一条通知 */ +static meow_widget *g_fs_btn; /* FULLSCREEN 进入按钮(主界面) */ +static meow_widget *g_demo_btn; /* 「控件库 Demo」入口按钮(主界面) */ +static const char *g_notif_labels[6] = { "INFO", "MEDIA", "HIGH", "PROG", "ONGO", "TGT" }; +static char g_btn_label[32] = "TAP ME"; + +void home_redraw(void) +{ + const meow_theme *th = meow_theme_get(); + int sb = meow_ui_statusbar_height(); + if (sb > 0) meow_ui_fill_rect(0, sb, 480, 720 - sb, th->bg); + else meow_ui_clear(th->bg); + meow_ui_text_center(240, sb + 8, "MEOWOS HELLO", 28, th->fg); + meow_ui_text_center(240, sb + 38, "SDK: meow_ui.a", 20, th->dim); + meow_widget_draw(g_btn); + meow_ui_text_center(240, 372, "tap = send notification (see log)", 20, th->dim); + for (int i = 0; i < 6; i++) meow_widget_draw(g_nbtn[i]); + meow_widget_draw(g_cancel); + meow_widget_draw(g_fs_btn); + meow_widget_draw(g_demo_btn); + char line[64]; + snprintf(line, sizeof line, "taps=%d uptime=%ds", logic_taps_get(), logic_secs_get()); + meow_ui_text_center(240, 516, line, 20, th->dim); + meow_ui_text(20, 660, "left-swipe = exit", 20, th->dim); + meow_ui_flush(); +} + +/* 业务→UI 监听器:taps 变化 → 重建按钮 label;secs 变化 → 重绘(仅本页在前台时,否则只改业务 + * 不覆写全屏帧——例如全屏态下 logic_secs_add 仍触发,但本页 active=0 跳过 redraw)。 */ +static void home_state_listener(int ev, void *ctx) +{ + home_ctrl *c = (home_ctrl *)ctx; + if (ev == LOGIC_EV_TAPS) { + char lb[32]; + snprintf(lb, sizeof lb, "TAPPED x%d", logic_taps_get()); + meow_widget_free(g_btn); + g_btn = meow_btn_create(120, 300, 240, 64, lb); + } + if (!c->active) return; /* 本页不在前台(全屏):不覆写当前帧 */ + home_redraw(); +} + +int home_on_tap(int x, int y) +{ + if (meow_widget_hit(g_demo_btn, x, y)) { meow_view_page_push(ACT_DEMO); return 1; } + for (int i = 0; i < 6; i++) { + if (meow_widget_hit(g_nbtn[i], x, y)) { notif_send(i); home_redraw(); return 1; } + } + if (meow_widget_hit(g_cancel, x, y)) { notif_cancel(); home_redraw(); return 1; } + if (meow_widget_hit(g_fs_btn, x, y)) { meow_view_page_push(ACT_FULL); return 1; } + if (meow_widget_hit(g_btn, x, y)) { logic_taps_bump(); return 1; } + return 0; +} + +static void home_page_create(meow_view *v, void *ctx) +{ + (void)v; + ((home_ctrl *)ctx)->view = v; + g_btn = meow_btn_create(120, 300, 240, 64, g_btn_label); + for (int i = 0; i < 6; i++) + g_nbtn[i] = meow_btn_create(3 + i * 80, 400, 74, 56, g_notif_labels[i]); + g_cancel = meow_btn_create(36, 464, 200, 42, "CANCEL LAST"); + g_fs_btn = meow_btn_create(244, 464, 200, 42, "FULLSCREEN"); + g_demo_btn = meow_btn_create(36, 560, 408, 44, "WIDGETS DEMO"); + /* 注册业务→UI 推送监听器(biz-UI 绑定点 = 本页控制器 ctx)。 */ + logic_set_listener(home_state_listener, ctx); +} + +static void home_page_enter(meow_view *v, void *ctx) +{ + (void)v; + ((home_ctrl *)ctx)->active = 1; + home_redraw(); +} + +static void home_page_leave(meow_view *v, void *ctx) +{ + (void)v; + ((home_ctrl *)ctx)->active = 0; /* 默认不销毁子控件;弹回时 on_enter 重绘恢复 */ +} + +static void home_page_destroy(meow_view *v, void *ctx) +{ + (void)v; (void)ctx; + meow_widget_free(g_demo_btn); + for (int i = 0; i < 6; i++) meow_widget_free(g_nbtn[i]); + meow_widget_free(g_cancel); + meow_widget_free(g_fs_btn); + meow_widget_free(g_btn); + g_btn = g_cancel = g_fs_btn = g_demo_btn = NULL; + for (int i = 0; i < 6; i++) g_nbtn[i] = NULL; +} + +meow_view *home_view_create(void) +{ + static const meow_view_hooks h = { + .on_create = home_page_create, + .on_enter = home_page_enter, + .on_leave = home_page_leave, + .on_destroy = home_page_destroy, + }; + return meow_view_create(ACT_HOME, MEOW_VIEW_ACTIVITY, &h, &g_home_ctrl); +} diff --git a/patch_v2/sdk/test_p1p2p3_repro.c b/patch_v2/sdk/test_p1p2p3_repro.c index f8e89f2..0811786 100644 --- a/patch_v2/sdk/test_p1p2p3_repro.c +++ b/patch_v2/sdk/test_p1p2p3_repro.c @@ -1,41 +1,43 @@ /* test_p1p2p3_repro.c — 宿主复现并校验 P1/P2/P3 三个真机问题的修复(离屏 480x720,16bpp)。 * - * 方法:#include 真实 `hello.c`(改 hello 的 main 名),直接驱动 demo 的真实函数 - * (demo_paint / d_redraw / demo_touch_down / demo_rebuild_to),对 back_buf 做像素断言。 + * 方法:直接驱动拆分后 hello 的 demo 真实函数(demo_paint / demo_rebuild_to / demo_touch_down) + * 与 demo 视图态(extern,见 ../hello_app/hello_internal.h),对 back_buf 做像素断言。 * 这样测的是「真实 demo 代码 + 真实 SDK」,不是模拟桩。不依赖 /dev/fb0 / 字库 / launcher。 * + * 批次② 变化:hello.c 已拆为 app.c + ui/{home,full,demo}.c + ui/common/chrome.c + + * logic/{notify,state}.c;本文件不再 #include hello.c,改为 extern 驱动 demo 层并链接多文件。 + * * 覆盖: * P1 z-order 叠层:圆形进度条(实时重绘)与 FAB 浮层重叠时,局部重绘不再清掉/盖住 FAB。 - * 旧 d_redraw=清底+只画单控件 → FAB 重叠区被抹成背景;新 d_redraw=d_paint_region - * 重画重叠区完整 z 栈(内容→标题→slider值→chrome)→ FAB 保持。 + * (批次① 后为 P1c:set_z(g_fab,999)+invalidate(r_prc),显式 z 不依赖绘制先后。) * P2 list 焦点:命中非 list 区域时释放列表焦点、切页滚动(不再沿用旧焦点继续滚列表)。 - * P3 list 脏区:W_LIST 绘制已裁剪到视口;滚动(非整行偏移)+选中行不再向标题间隙/底缘下泄漏 - * 填充(此前泄漏会标脏并上屏 → 标题栏/底部残留)。 + * P3 list 脏区:W_LIST 绘制已裁剪到视口;滚动(非整行偏移)+选中行不再向标题间隙/底缘下泄漏。 * * 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime/img/config/ - * notify/ipc + 打桩 font/global(test_host_stub) + stb_image): - * gcc -Wall -Wextra -DMEOW_UI_HOST_TEST -Dmain=hello_app_main \ - * -o test_p1p2p3_repro test_p1p2p3_repro.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_img.c meow_ui_config.c meow_ui_notify.c meow_notify_ipc.c meow_ui_draw.c \ - * meow_ui_view.c stb_image.c \ - * -I. -I../hello_app -I../meow_launcherd -pthread -lm + * notify/ipc + 打桩 font/global(test_host_stub) + stb_image + hello 拆分层): + * 1) 单独编译 app.c 并把 main 改名 hello_app_main(否则与本文件 main 冲突): + * gcc -c -Wall -Wextra -DMEOW_UI_HOST_TEST -Dmain=hello_app_main \ + * -I. -I../hello_app ../hello_app/app.c -o app.o + * 2) 编译本文件 + 其余 hello 模块 + SDK 源 + 打桩,链接 app.o: + * gcc -Wall -Wextra -DMEOW_UI_HOST_TEST -o test_p1p2p3_repro test_p1p2p3_repro.c app.o \ + * 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_img.c meow_ui_config.c meow_ui_notify.c meow_notify_ipc.c meow_ui_draw.c \ + * meow_ui_view.c stb_image.c \ + * ../hello_app/ui/home.c ../hello_app/ui/full.c ../hello_app/ui/demo.c \ + * ../hello_app/ui/common/chrome.c ../hello_app/logic/notify.c ../hello_app/logic/state.c \ + * -I. -I../hello_app -I../meow_launcherd -pthread -lm * 运行:MEOW_UI_DUMP=/tmp/repro.ppm ./test_p1p2p3_repro (optional,dump 最终屏幕) * - * 覆盖(批次① z 序下沉后):P1c 显式 z 置顶(set_z(g_fab,999)+invalidate(r_prc) 不抹 FAB—— - * 显式 z 不依赖绘制先后)+ P2/P3 与迁移前一致。预期 RESULT: ALL PASS。 + * 预期 RESULT: ALL PASS(P1c/P2/P3 断言真实有效,未弱化)。 */ #include "meow_ui.h" #include "meow_ui_internal.h" +#include "../hello_app/hello_internal.h" #include #include #include -#define main hello_app_main /* 让 hello.c 的 main 改名,由本文件提供 main */ -#include "hello.c" -#undef main - /* ---- 像素读取 ---- */ static void rd_px(int x, int y, int *r, int *g, int *b) {