refactor(hello): 视图框架批次② — hello.c 拆分为 app.c+ui/{home,full,demo}.c+ui/common/chrome.c+logic/{notify,state}.c(定案B: demo视图态留ui/;定案b: page导航包装activity栈);test_p1p2p3_repro 改多文件链接形态 (agent-177)

hello f45ba051→c06b3b4f; mipsel -Wall -Wextra 0 warning; 宿主 P1c/P2/P3+widgets 全 PASS
出入(诚实): view on_draw/on_event 路径暂未启用(注册表直绘已覆盖, on_draw 双画风险), 留批③实测; home/full 仍手绘未迁注册表
This commit is contained in:
MeowOS committed 2026-08-31 03:31:09 +08:00
1 parent 301bdcfcec
commit d1a3cce7f1
11 files changed
+646 -344

No files matched your search

+115
View File
@@ -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 <signal.h>
/* 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;
}
+7 -2
View File
@@ -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
BIN
View File
Binary file not shown.
+102
View File
@@ -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 <stdint.h>
#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 */
+30
View File
@@ -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 */
+56
View File
@@ -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 <string.h>
#include <stdint.h>
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;
}
+38
View File
@@ -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 <stddef.h>
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;
}
+36
View File
@@ -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; }
}
+87 -342
View File
@@ -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=<ppm 路径> 时,首帧 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>
/* §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);
}
+56
View File
@@ -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);
}
+119
View File
@@ -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 <stdio.h>
#include <string.h>
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);
}