feat(sdk/view): 视图框架批次① — z序绘制物注册表(meow_ui_draw.c: 自动注册+auto_z(深度*步长+层内seq)+set_z(999)置顶+MEOW_Z_AUTO_MAX=127) + meow_view骨架(meow_ui_view.c/.h: 嵌套容器+六生命周期+page_push/pop/replace包装activity栈+sub_push/pop预留桩) + 收敛live_btn走invalidate + hello迁移(d_paint_region/d_redraw删除→meow_ui_invalidate, chrome set_z(999), demo文本注册CUSTOM z=128) (agent-176, 定案见SDK_VIEW_FRAMEWORK_DESIGN.md §E①)

meow_ui.a d814d0ed→7e20f10e; hello 0a1c96a5→2ce09466
宿主验证: test_p1p2p3_repro P1c/P2/P3 ALL PASS; test_meow_ui_widgets ALL PASS 无回归; mipsel -Wall -Wextra 0 warning
This commit is contained in:
MeowOS committed 2026-08-31 02:38:32 +08:00
1 parent fbc412e96a
commit 08c70aa14c
13 files changed
+872 -96

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ AR="${AR:-/usr/bin/ar}" # 系统 GNU ar 即可归档 mipsel ELF 对象(架
rm -f meow_ui.a *.o
# -I../meow_launcherd:meow_notify_ipc.c 复用共享协议头 meow_ipc.h(同一份、杜绝两端漂移)
OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config stb_image"
OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view stb_image"
CFLAGS="-Os -static -I$ROOT/patch_v2/meow_launcherd"
for f in $OBJS; do
echo "== cc $f =="
+52
View File
@@ -210,6 +210,58 @@ void meow_ui_global_remove(int id);
/* ③ 系统级控件:状态栏/通知中心/左缘返回滑条/电源菜单等全局叠加层——仅 SDK 内部接口,
* 不向用户 App 开放任何公共悬浮/叠加层 API。既有状态栏/返回滑条逻辑(§3.1)不动。 */
/* ============================================================ */
/* §6.2 绘制物注册表 / z 序 / 区域重画(合并 SDK_Z_ORDER_DESIGN,源:2026-08-31)
* ------------------------------------------------------------
* 非保留模型(§5.8.7)下,把「清底 + 按 z 重画重叠区完整 z 栈」下沉到 SDK,让所有 App
* 自动获得正确的局部重画(根治 P1:下层动态控件局部重绘会把重叠区上层 chrome 抹掉)。
* - 控件(meow_widget)创建即自动注册进绘制物注册表;rect 实时读 w->x/y/w/h;
* - App 直绘的非控件内容(meow_ui_text/fill_rect 等)须显式 meow_draw_custom_register
* 注册(声明 bbox + 绘制回调),否则区域重画会漏画(非保留模型固有代价);
* - z 自动推导(子>父、同层注册先序);显式 meow_widget_set_z(999/MEOW_Z_TOP) 置顶(粘滞)。
* 用法:meow_ui_invalidate(x,y,w,h) 局部重画(清底+按 z 升序重画所有相交绘制物+上屏),
* 不整帧;meow_ui_paint_full() 整帧按 z 序重画。默认底=meow_theme bg(set_page_bg 覆盖)。
* 兼容:新 API 全部 opt-in;不调用本节的既有 App(widget/activity/flush 均不动)行为完全不变。 */
/* ============================================================ */
/* 绘制物种类:控件(自动注册)/ App 直绘内容(显式注册)。 */
typedef enum { MEOW_ITEM_WIDGET = 0, MEOW_ITEM_CUSTOM = 1 } meow_draw_kind;
typedef void (*meow_draw_fn)(void *ctx); /* CUSTOM 绘制回调(ctx 由 App 持有) */
/* ---- 层级常量(保证"子>父" + 值域远小于置顶值)---- */
#define MEOW_Z_STRIDE 16 /* 每层容器嵌套的 z 步进(子层至少 +16) */
#define MEOW_Z_DEPTH_MAX 7 /* 自动推导最大嵌套深度(0..6,>7 折叠) */
#define MEOW_Z_PER_LEVEL_MAX 15 /* 每层最多 16 槽(seq 0..15,超出折叠按注册序) */
#define MEOW_Z_AUTO_MAX (MEOW_Z_STRIDE * MEOW_Z_DEPTH_MAX + MEOW_Z_PER_LEVEL_MAX) /* 127 */
#define MEOW_Z_TOP 1000 /* 惯例"置顶"值;≥(MEOW_Z_AUTO_MAX+1)=128 即超一切自动值 */
/* 控件显式置层:z 为任意非负整数(0..INT_MAX,无上限),越大越上;z>所有其它控件即恒在最上。
* 惯例设 999(或 MEOW_Z_TOP)即"置顶"。显式 z 存于绘制注册表(按 w 反查 entry,不改
* struct meow_widget——零 ABI 破坏)且**粘滞**:不被后续自动推导覆盖;clear_z 才回退。 */
void meow_widget_set_z(meow_widget *w, int z);
int meow_widget_z(const meow_widget *w); /* 当前生效 z(显式=其值,否则=自动推导) */
void meow_widget_clear_z(meow_widget *w); /* 清除显式 z,回退自动推导 */
/* ---- 非控件直绘内容注册为绘制物(CUSTOM)----
* App 声明 bbox + 绘制回调 + 显式 z(任意非负;999=置顶);区域重画/整帧重画会画到它。
* 返回 id(供 meow_draw_unregister / meow_draw_set_z);注册表满返回 0。
* 生命周期契约:App 持有 ctx;widget 类内容不必注册(自动);离开页面前 unregister。 */
int meow_draw_custom_register(int z, int x, int y, int w, int h,
meow_draw_fn cb, void *ctx);
void meow_draw_unregister(int id); /* 注销(id 失效,勿再用) */
int meow_draw_set_z(int id, int z); /* 改 custom 显式 z;无效 id 返回 -1 */
/* 页面背景色(区域重画/整帧清底用);默认 meow_theme bg。 */
void meow_ui_set_page_bg(uint32_t color);
uint32_t meow_ui_page_bg(void);
/* 声明某区域内容已变化 → 清底(page_bg) + 按 z 升序重画所有相交绘制物 + 上屏。
* 局部(只画相交绘制物,完整 bbox——见 SDK_Z_ORDER_DESIGN §5),不整帧、不冲状态栏。 */
void meow_ui_invalidate(int x, int y, int w, int h);
/* 整帧按 z 序重画全部(set_page_bg 清底 + 全注册表按 z 升序 + 上屏)。 */
void meow_ui_paint_full(void);
/* ============================================================ */
/* §3.1 全局状态栏 / 全屏(A-0,SDK 系统级组件) */
/* ============================================================ */
+357
View File
@@ -0,0 +1,357 @@
/*
* meow_ui_draw.c — 绘制物注册表 / z 序 / 区域重画(§6.2 / SDK_Z_ORDER_DESIGN §2)。
*
* 非保留模型下,把「清底 + 按 z 重画重叠区完整 z 栈」下沉到 SDK:
* - 控件(meow_widget)创建即自动注册(create 末尾调 meow_draw_widget_register),
* meow_widget_free 自动注销;rect 实时读 w->x/y/w/h;
* - App 直绘的非控件内容显式 meow_draw_custom_register(声明 bbox + 绘制回调);
* - z 自动推导(子>父、同层注册先序);显式 set_z 粘滞、置顶。
* 显式 z 存于本注册表(按 w 反查 entry),**不改 struct meow_widget**(零 ABI 破坏)。
*/
#include "meow_ui.h"
#include "meow_ui_internal.h"
#include "meow_ui_view.h"
#include <stdlib.h>
#include <string.h>
#define MEOW_DRAW_MAX 256
#define MEOW_ITEM_VIEW 2 /* 内部 kinds(不进公共枚举):meow_view 绘制物 */
typedef struct draw_entry {
int kind; /* MEOW_ITEM_WIDGET / MEOW_ITEM_CUSTOM / MEOW_ITEM_VIEW */
meow_widget *w; /* WIDGET:绘制其 meow_widget_draw(w) */
meow_draw_fn draw_cb; /* CUSTOM:绘制回调 */
void *ctx; /* CUSTOM: 传给 draw_cb; VIEW: (meow_view*)v */
int rx, ry, rw, rh; /* CUSTOM bbox(WIDGET 读 w->x/y/w/h,VIEW 读 v->x/y/w/h) */
int z; /* 生效 z(显式=其值,否则=自动推导) */
int z_explicit; /* 1=显式(粘滞) */
int id; /* CUSTOM id(0=无效) */
int seq; /* 全局注册序(z 同值时稳定先序) */
struct meow_view *view; /* WIDGET:所属 view(z 深度源)+1 */
} draw_entry;
static draw_entry g_e[MEOW_DRAW_MAX];
static int g_n = 0;
static int g_seq = 0; /* 全局注册序(单调递增) */
static int g_next_id = 1; /* CUSTOM id 起点(0=无效) */
static uint32_t g_page_bg = 0;
static int g_page_bg_set = 0;
/* ---- 内部访问器(widget.c 提供;跨 TU 用 meow_ui_internal.h 声明)---- */
static int w_is_layout(const meow_widget *w) { return meow_widget_is_layout(w); }
/* ---- 查找 ---- */
static draw_entry *e_find_widget(const meow_widget *w)
{
for (int i = 0; i < g_n; i++)
if (g_e[i].kind == MEOW_ITEM_WIDGET && g_e[i].w == w) return &g_e[i];
return NULL;
}
static draw_entry *e_find_view(const meow_view *v)
{
for (int i = 0; i < g_n; i++)
if (g_e[i].kind == MEOW_ITEM_VIEW && g_e[i].ctx == (void *)v) return &g_e[i];
return NULL;
}
static draw_entry *e_find_id(int id)
{
for (int i = 0; i < g_n; i++)
if (g_e[i].kind == MEOW_ITEM_CUSTOM && g_e[i].id == id) return &g_e[i];
return NULL;
}
/* ---- 深度:view 树深度 ---- */
static int view_depth(const meow_view *v)
{
int d = 0;
const meow_view *p = v ? v->parent : NULL;
while (p) {
d++;
if (d > MEOW_Z_DEPTH_MAX) break;
p = p->parent;
}
return d;
}
/* ---- 深度:widget 所属嵌套深度(view 树为主;否则 W_LAYOUT children 反推;否则 0)---- */
static int widget_nest_depth(meow_widget *w, int depth)
{
if (depth > MEOW_Z_DEPTH_MAX) return MEOW_Z_DEPTH_MAX;
draw_entry *e = e_find_widget(w);
if (e && e->view) return view_depth(e->view) + 1; /* 视图树深度源(更确定) */
/* 注册表反推:w 是否经 meow_layout_add 挂在某布局下(此时布局自身已注册为 WIDGET)。 */
for (int i = 0; i < g_n; i++) {
if (g_e[i].kind != MEOW_ITEM_WIDGET) continue;
meow_widget *lw = g_e[i].w;
if (!lw || !w_is_layout(lw)) continue;
int nc = meow_widget_nchild(lw);
for (int j = 0; j < nc; j++)
if (meow_widget_child_at(lw, j) == w)
return widget_nest_depth(lw, depth + 1) + 1; /* 子层 >= 父层+1 */
}
return 0;
}
/* 计算 auto z(显式不重算——粘滞):depth*STRIDE + 层内 seq(注册先序,折叠 MEOW_Z_PER_LEVEL_MAX)。 */
static void e_compute_auto(draw_entry *e)
{
if (e->z_explicit) return;
int depth = 0;
if (e->kind == MEOW_ITEM_WIDGET && e->w) depth = widget_nest_depth(e->w, 0);
else if (e->kind == MEOW_ITEM_VIEW && e->ctx) depth = view_depth((const meow_view *)e->ctx);
/* CUSTOM:z 恒显式(register 传入),不进 auto 分支 */
int cnt = 0;
for (int i = 0; i < g_n; i++) {
draw_entry *o = &g_e[i];
if (o == e || o->z_explicit) continue;
if (o->seq >= e->seq) continue;
if (o->z / MEOW_Z_STRIDE == depth) cnt++;
}
if (cnt > MEOW_Z_PER_LEVEL_MAX) cnt = MEOW_Z_PER_LEVEL_MAX;
e->z = depth * MEOW_Z_STRIDE + cnt;
}
/* ============================================================ */
/* 控件自动注册 / 注销(create 末尾 / meow_widget_free 调用) */
/* ============================================================ */
void meow_draw_widget_register(meow_widget *w)
{
if (!w || e_find_widget(w)) return; /* 幂等 */
if (g_n >= MEOW_DRAW_MAX) return; /* 满:静默(骨架级) */
draw_entry *e = &g_e[g_n++];
memset(e, 0, sizeof *e);
e->kind = MEOW_ITEM_WIDGET;
e->w = w;
e->seq = g_seq++;
e_compute_auto(e);
}
void meow_draw_widget_unregister(meow_widget *w)
{
if (!w) return;
int idx = -1;
for (int i = 0; i < g_n; i++)
if (g_e[i].kind == MEOW_ITEM_WIDGET && g_e[i].w == w) { idx = i; break; }
if (idx < 0) return;
g_e[idx] = g_e[--g_n]; /* 交换移除(seq 保留,排序稳定) */
}
int meow_draw_widget_registered(const meow_widget *w)
{
return w ? e_find_widget(w) != NULL : 0;
}
void meow_draw_widget_set_view(meow_widget *w, struct meow_view *v)
{
draw_entry *e = e_find_widget(w);
if (!e) return;
e->view = v;
e_compute_auto(e);
}
/* ============================================================ */
/* 显式 z(widget) */
/* ============================================================ */
void meow_widget_set_z(meow_widget *w, int z)
{
if (!w || z < 0) return;
draw_entry *e = e_find_widget(w);
if (!e) return;
e->z_explicit = 1;
e->z = z;
}
int meow_widget_z(const meow_widget *w)
{
if (!w) return 0;
draw_entry *e = e_find_widget((meow_widget *)w);
if (!e) return 0;
if (e->z_explicit) return e->z;
e_compute_auto(e);
return e->z;
}
void meow_widget_clear_z(meow_widget *w)
{
if (!w) return;
draw_entry *e = e_find_widget(w);
if (!e) return;
e->z_explicit = 0;
e_compute_auto(e);
}
/* ============================================================ */
/* CUSTOM(App 直绘)注册 / 注销 / 改 z */
/* ============================================================ */
int meow_draw_custom_register(int z, int x, int y, int w, int h,
meow_draw_fn cb, void *ctx)
{
if (!cb || w <= 0 || h <= 0) return 0;
if (g_n >= MEOW_DRAW_MAX) return 0;
draw_entry *e = &g_e[g_n++];
memset(e, 0, sizeof *e);
e->kind = MEOW_ITEM_CUSTOM;
e->draw_cb = cb;
e->ctx = ctx;
e->rx = x; e->ry = y; e->rw = w; e->rh = h;
e->z = z < 0 ? 0 : z;
e->z_explicit = 1;
e->id = g_next_id++;
e->seq = g_seq++;
return e->id;
}
void meow_draw_unregister(int id)
{
if (id <= 0) return;
int idx = -1;
for (int i = 0; i < g_n; i++)
if (g_e[i].kind == MEOW_ITEM_CUSTOM && g_e[i].id == id) { idx = i; break; }
if (idx < 0) return;
g_e[idx] = g_e[--g_n];
}
int meow_draw_set_z(int id, int z)
{
if (id <= 0 || z < 0) return -1;
draw_entry *e = e_find_id(id);
if (!e) return -1;
e->z = z;
e->z_explicit = 1;
return 0;
}
/* ============================================================ */
/* view 绘制物注册 / 注销 */
/* ============================================================ */
void meow_draw_view_register(struct meow_view *v)
{
if (!v || e_find_view(v)) return;
if (g_n >= MEOW_DRAW_MAX) return;
draw_entry *e = &g_e[g_n++];
memset(e, 0, sizeof *e);
e->kind = MEOW_ITEM_VIEW;
e->ctx = (void *)v;
e->seq = g_seq++;
e_compute_auto(e);
}
void meow_draw_view_unregister(struct meow_view *v)
{
if (!v) return;
int idx = -1;
for (int i = 0; i < g_n; i++)
if (g_e[i].kind == MEOW_ITEM_VIEW && g_e[i].ctx == (void *)v) { idx = i; break; }
if (idx < 0) return;
g_e[idx] = g_e[--g_n];
}
int meow_draw_view_entered(const struct meow_view *v)
{
return v ? meow_view_get_state(v) & MEOW_VIEW_STATE_ENTERED : 0;
}
void meow_draw_view_set_z(struct meow_view *v, int z)
{
draw_entry *e = e_find_view(v);
if (!e) return;
if (z <= 0) {
e->z_explicit = 0;
e_compute_auto(e);
} else {
e->z_explicit = 1;
e->z = z;
}
}
/* ============================================================ */
/* page bg / 区域重画 / 整帧 */
/* ============================================================ */
void meow_ui_set_page_bg(uint32_t color) { g_page_bg = color; g_page_bg_set = 1; }
uint32_t meow_ui_page_bg(void) { return g_page_bg_set ? g_page_bg : meow_theme_get()->bg; }
static int rect_hit(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh)
{
return ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by;
}
static int entry_rect(draw_entry *e, int out[4])
{
if (e->kind == MEOW_ITEM_WIDGET && e->w) {
meow_widget_rect(e->w, out);
} else if (e->kind == MEOW_ITEM_VIEW && e->ctx) {
meow_view *v = (meow_view *)e->ctx;
out[0] = v->x; out[1] = v->y; out[2] = v->w; out[3] = v->h;
} else {
out[0] = e->rx; out[1] = e->ry; out[2] = e->rw; out[3] = e->rh;
}
return out[2] > 0 && out[3] > 0;
}
static void draw_one(draw_entry *e)
{
switch (e->kind) {
case MEOW_ITEM_WIDGET:
meow_widget_draw(e->w);
break;
case MEOW_ITEM_CUSTOM:
e->draw_cb(e->ctx);
break;
case MEOW_ITEM_VIEW: {
meow_view *v = (meow_view *)e->ctx;
if (v->hooks.on_draw) v->hooks.on_draw(v, meow_theme_get(), v->ctx);
break;
}
default:
break;
}
}
static void paint(int full, int x, int y, int w, int h)
{
uint32_t bg = meow_ui_page_bg();
if (full) {
meow_ui_clear(bg);
} else {
if (w <= 0 || h <= 0) return;
meow_ui_fill_rect(x, y, w, h, bg);
}
/* 刷新 auto z + 组装按 (z,seq) 升序的绘制序(owner==active 过滤 WIDGET;view 须已进入)。 */
draw_entry *order[MEOW_DRAW_MAX];
int nn = 0;
int active = meow_ui_activity_active();
for (int i = 0; i < g_n; i++) {
draw_entry *e = &g_e[i];
e_compute_auto(e);
if (e->kind == MEOW_ITEM_WIDGET && e->w && meow_widget_owner(e->w) != active) continue;
if (e->kind == MEOW_ITEM_VIEW && !meow_draw_view_entered((meow_view *)e->ctx)) continue;
order[nn++] = e;
}
/* 稳定插入排序:(z 升序,同 z 按 seq 升序=注册先序)。 */
for (int i = 1; i < nn; i++) {
draw_entry *key = order[i];
int j = i - 1;
while (j >= 0 && (order[j]->z > key->z ||
(order[j]->z == key->z && order[j]->seq > key->seq))) {
order[j + 1] = order[j];
j--;
}
order[j + 1] = key;
}
if (full) {
for (int i = 0; i < nn; i++) draw_one(order[i]);
meow_ui_flush();
} else {
int r[4];
for (int i = 0; i < nn; i++) {
if (!entry_rect(order[i], r)) continue;
if (rect_hit(r[0], r[1], r[2], r[3], x, y, w, h)) draw_one(order[i]);
}
meow_ui_flush_region(x, y, w, h);
}
}
void meow_ui_invalidate(int x, int y, int w, int h)
{
if (w <= 0 || h <= 0) return;
paint(0, x, y, w, h);
}
void meow_ui_paint_full(void)
{
paint(1, 0, 0, 0, 0);
}
+25
View File
@@ -184,6 +184,31 @@ void meow_widget_touch_refresh(void);
void meow_ui_global_render(void);
int meow_ui_global_hit(int x, int y);
/* ---- 绘制物注册表(meow_ui_draw.c,§6.2 绘制物注册表/z序/区域重画)----
* 内部协作接口:widget.c 在 create 末尾自动注册、meow_widget_free 自动注销;view.c 在
* meow_view_add_widget/meow_view_free 挂/摘 widget 与 view 的 z 深度源;live_btn 收敛查询
* 是否已注册(已注册→走 invalidate 区域重画,否则回退单控件 flush)。 */
struct meow_view; /* 仅前向声明;完整类型在 meow_ui_view.h */
void meow_draw_widget_register(struct meow_widget *w); /* create 末尾:自动注册(幂等) */
void meow_draw_widget_unregister(struct meow_widget *w); /* meow_widget_free:自动注销 */
int meow_draw_widget_registered(const struct meow_widget *w);/* 是否在注册表(live_btn 收敛用) */
void meow_draw_widget_set_view(struct meow_widget *w, struct meow_view *v); /* 挂 view:重算 auto z */
void meow_draw_view_register(struct meow_view *v); /* view create:注册绘制物(on_draw 用) */
void meow_draw_view_unregister(struct meow_view *v); /* view free:注销 */
int meow_draw_view_entered(const struct meow_view *v); /* view 是否已进入(绘制过滤参考) */
void meow_draw_view_set_z(struct meow_view *v, int z); /* view 显式 z(z<=0=清除回退自动) */
/* 控件布局深度反推(meow_ui_draw.c 用;widget.c 实现,读 struct meow_widget 内部字段)。
* 让绘制注册表在「无 meow_view 时」仍能从 W_LAYOUT children 反推子>父深度。 */
int meow_widget_is_layout(const struct meow_widget *w); /* 1 = W_LAYOUT */
int meow_widget_nchild(const struct meow_widget *w); /* 布局子项数 */
const struct meow_widget *meow_widget_child_at(const struct meow_widget *w, int idx);
/* 控件几何/归属访问器(meow_ui_draw.c 用:绘制物 rect 实时读 + owner 过滤)。 */
int meow_widget_owner(const struct meow_widget *w); /* 创建时绑定的 Activity */
void meow_widget_rect(const struct meow_widget *w, int out[4]); /* x,y,w,h */
/* ---- IME(meow_ui_ime.c)---- */
/* 当前是否显示软键盘。 */
int meow_ime_active(void);
+197
View File
@@ -0,0 +1,197 @@
/*
* meow_ui_view.c — meow_view 视图/页面框架骨架(§A 视图数据结构/嵌套/生命周期/页面导航)。
*
* meow_view 是**薄壳**:容器 + 生命周期 + 事件路由;叶子仍是 meow_widget(不改)。绘制走
* z 序绘制物注册表(on_draw 回调,非保留——不缓存像素)。opt-in:不调 meow_view_* 的 App
* 完全照旧(widget/activity/flush 不动)。页面模型 = 定案 b(顶层页切走 activity 栈,
* page_push/pop/replace 包装;不做页内子导航栈,sub_push/pop 为预留扩展位 a 桩)。
*/
#include "meow_ui.h"
#include "meow_ui_internal.h"
#include "meow_ui_view.h"
#include <stdlib.h>
#include <string.h>
#define MEOW_VIEW_BY_ID_MAX 32
static meow_view *g_view_by_id[MEOW_VIEW_BY_ID_MAX]; /* id → 顶层页 view(page_push/pop 查找) */
static meow_view *g_top_view = NULL; /* 当前进入的顶层页 view(生命周期跟踪) */
static void run_fn(meow_view_fn f, meow_view *v)
{
if (f) f(v, v->ctx);
}
/* 登记/清除 id→view 映射(幂等)。 */
static void view_map_set(meow_view *v)
{
if (!v) return;
int id = v->id;
if (id >= 0 && id < MEOW_VIEW_BY_ID_MAX) g_view_by_id[id] = v;
}
static void view_map_clear(meow_view *v)
{
if (!v) return;
int id = v->id;
if (id >= 0 && id < MEOW_VIEW_BY_ID_MAX && g_view_by_id[id] == v) g_view_by_id[id] = NULL;
}
static meow_view *view_map_get(int id)
{
return (id >= 0 && id < MEOW_VIEW_BY_ID_MAX) ? g_view_by_id[id] : NULL;
}
/* 进入一个 view(置 ENTERED + on_enter)。 */
static void view_enter(meow_view *v)
{
if (!v || (v->state & MEOW_VIEW_STATE_ENTERED)) return;
v->state |= MEOW_VIEW_STATE_ENTERED;
run_fn(v->hooks.on_enter, v);
}
/* 离开一个 view(清 ENTERED + on_leave;默认不销毁子 view/widget——弹回零重建)。 */
static void view_leave(meow_view *v)
{
if (!v || !(v->state & MEOW_VIEW_STATE_ENTERED)) return;
v->state &= ~MEOW_VIEW_STATE_ENTERED;
run_fn(v->hooks.on_leave, v);
}
/* ============================================================ */
/* 生命周期 */
/* ============================================================ */
meow_view *meow_view_create(int id, meow_view_type type,
const meow_view_hooks *hooks, void *ctx)
{
meow_view *v = (meow_view *)calloc(1, sizeof *v);
if (!v) return NULL;
v->id = id;
v->type = (type == MEOW_VIEW_CONTAINER) ? MEOW_VIEW_CONTAINER : MEOW_VIEW_ACTIVITY;
v->x = v->y = v->w = v->h = 0;
v->z = 0;
v->state = MEOW_VIEW_STATE_CREATED;
if (hooks) v->hooks = *hooks;
v->ctx = ctx;
view_map_set(v);
meow_draw_view_register(v); /* 登记为绘制物(on_draw 经 z 序重画触发) */
run_fn(v->hooks.on_create, v);
if (v->hooks.on_layout) run_fn(v->hooks.on_layout, v); /* 布局:create 后跑一次 */
return v;
}
void meow_view_free(meow_view *v)
{
if (!v) return;
if (v->state & MEOW_VIEW_STATE_ENTERED) view_leave(v);
run_fn(v->hooks.on_destroy, v); /* 释放视图态资源 */
view_map_clear(v);
meow_draw_view_unregister(v); /* 注销 z 序绘制物 */
for (int i = 0; i < v->nwidgets; i++) /* 叶子控件 */
meow_widget_free(v->widgets[i]);
v->nwidgets = 0;
for (int i = 0; i < v->nchildren; i++) /* 子 view 递归 */
meow_view_free(v->children[i]);
v->nchildren = 0;
free(v);
}
/* ============================================================ */
/* 嵌套(组合视图树) */
/* ============================================================ */
int meow_view_add_child(meow_view *parent, meow_view *child)
{
if (!parent || !child || child == parent) return -1;
for (int i = 0; i < parent->nchildren; i++)
if (parent->children[i] == child) return -1; /* 去重 */
if (parent->nchildren >= MEOW_VIEW_MAX_CHILD) return -1;
parent->children[parent->nchildren++] = child;
child->parent = parent;
return 0;
}
int meow_view_add_widget(meow_view *v, meow_widget *w)
{
if (!v || !w) return -1;
for (int i = 0; i < v->nwidgets; i++)
if (v->widgets[i] == w) return -1;
if (v->nwidgets >= MEOW_VIEW_MAX_WIDGET) return -1;
v->widgets[v->nwidgets++] = w;
meow_draw_widget_set_view(w, v); /* z 深度源 = view 树;重算 auto z */
return 0;
}
void meow_view_set_z(meow_view *v, int z)
{
if (!v) return;
v->z = (z < 0) ? 0 : z;
meow_draw_view_set_z(v, v->z);
}
int meow_view_z(const meow_view *v)
{
return v ? v->z : 0;
}
/* ============================================================ */
/* 页面导航(包装 activity 栈——唯一驱动 on_enter/on_leave) */
/* ============================================================ */
void meow_view_page_push(int id)
{
meow_view *cur = g_top_view;
meow_view *next = view_map_get(id);
if (cur && cur != next) view_leave(cur);
meow_ui_activity_push(id);
view_enter(next);
g_top_view = next ? next : cur;
}
void meow_view_page_pop(void)
{
meow_view *cur = g_top_view;
view_leave(cur);
meow_ui_activity_pop();
meow_view *next = view_map_get(meow_ui_activity_active());
view_enter(next);
g_top_view = next ? next : NULL;
}
/* 说明(诚实):replace 假定目标页已由 App 先 meow_view_create 指定 id 建好;本批不销毁旧页
* 对象(保留弹回),仅 leave+enter 切换——与设计「leave+destroy 当前」的差异见汇报。 */
void meow_view_page_replace(int id)
{
meow_view *cur = g_top_view;
meow_view *next = view_map_get(id);
if (cur && cur != next) view_leave(cur);
view_enter(next);
g_top_view = next ? next : cur;
}
/* ---- 页内子导航(预留扩展位 a 桩:本批不实现,仅返回占位)---- */
int meow_view_sub_push(meow_view *v, meow_view *sub)
{
(void)v; (void)sub;
return 0; /* 预留:将来在 ACTIVITY view 内维护子栈 */
}
meow_view *meow_view_sub_pop(meow_view *v)
{
(void)v;
return NULL; /* 预留 */
}
/* ============================================================ */
/* 坐标 / 查询 */
/* ============================================================ */
int meow_view_screen_x(const meow_view *v)
{
int x = v ? v->x : 0;
const meow_view *p = v ? v->parent : NULL;
while (p) { x += p->x; p = p->parent; }
return x;
}
int meow_view_screen_y(const meow_view *v)
{
int y = v ? v->y : 0;
const meow_view *p = v ? v->parent : NULL;
while (p) { y += p->y; p = p->parent; }
return y;
}
meow_view *meow_view_parent(const meow_view *v) { return v ? v->parent : NULL; }
int meow_view_get_state(const meow_view *v) { return v ? (int)v->state : 0; }
+98
View File
@@ -0,0 +1,98 @@
/*
* meow_ui_view.h — SDK 视图/页面框架(meow_view,嵌套容器 + 生命周期 + 事件路由)。
*
* 源:devdocs/plan/SDK_VIEW_FRAMEWORK_DESIGN.md §A。本文件为机制 ABI(结构 + 生命周期钩子 +
* 页面导航包装),与 z 序绘制物注册表(meow_ui_draw.c,§C/§6.2)协同:
* - meow_view 是**薄壳**:容器 + 生命周期 + 事件路由;叶子仍是 meow_widget(不改)。
* - on_draw 是回调,view 不缓存像素(非保留);重绘由 meow_ui_invalidate 触发。
* - opt-in:不调用 meow_view_* 的 App 完全照旧(widget/activity/flush 不动)。
* - z 深度源 = view 树(parent/children 深度);叶子 widget 深度 = 所属 view 深度 + 1。
*
* 页面模型(定案 b,预留 a):顶层页切换仍由 activity 栈(meow_view_page_push/pop/replace 包装),
* 不做页内子导航栈;meow_view_sub_push/pop 为将来扩展位(仅在一个 ACTIVITY view 内维护子栈)。
*/
#ifndef MEOW_UI_VIEW_H
#define MEOW_UI_VIEW_H
#include "meow_ui.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct meow_view meow_view;
typedef enum { MEOW_VIEW_ACTIVITY = 0, MEOW_VIEW_CONTAINER = 1 } meow_view_type;
typedef enum {
MEOW_VIEW_STATE_CREATED = 1u << 0,
MEOW_VIEW_STATE_ENTERED = 1u << 1,
MEOW_VIEW_STATE_DIRTY = 1u << 2,
} meow_view_state;
/* 生命周期/事件钩子(on_create→on_enter→(事件循环)→on_leave→on_destroy + on_layout)。
* 均以 view 自身 + ctx(=视图控制器,业务-UI 绑定点)为参。可 NULL。 */
typedef void (*meow_view_fn)(meow_view *v, void *ctx);
typedef int (*meow_view_ev_fn)(meow_view *v, int ev, int a, int b, void *ctx);
typedef void (*meow_view_draw_fn)(meow_view *v, const meow_theme *th, void *ctx);
typedef struct meow_view_hooks {
meow_view_fn on_create; /* 建 view 后跑一次(分配视图态/初始布局) */
meow_view_fn on_enter; /* 页面进入(push/replace 后;回页面自动恢复零重建) */
meow_view_fn on_leave; /* 页面离开(pop/replace 前;默认不销毁子 view/widget) */
meow_view_fn on_destroy; /* meow_view_free 释放(递归)时跑 */
meow_view_fn on_layout; /* 布局(on_create/page_push 后跑一次,算子控件绝对坐标) */
meow_view_draw_fn on_draw; /* 绘制回调(非保留;由 meow_ui_invalidate 触发) */
meow_view_ev_fn on_event; /* 事件路由(可 NULL;先发后代按 z 降序,命中即消费) */
} meow_view_hooks;
#define MEOW_VIEW_MAX_CHILD 16
#define MEOW_VIEW_MAX_WIDGET 32
struct meow_view {
int type; /* MEOW_VIEW_ACTIVITY / MEOW_VIEW_CONTAINER */
int id; /* 页面/视图 id(App 命名的稳定值;= page_push 用的 id) */
int x, y, w, h; /* 自身坐标(相对父内容原点;子 view 默认占满,叠放由 z 排) */
meow_view *parent;
meow_view *children[MEOW_VIEW_MAX_CHILD];
int nchildren;
meow_widget *widgets[MEOW_VIEW_MAX_WIDGET]; /* 叶子控件(直接挂本 view) */
int nwidgets;
int z; /* 显式 z(meow_view_set_z);0=未设(由子/注册表推导) */
unsigned state; /* meow_view_state 位掩码 */
meow_view_hooks hooks;
void *ctx; /* 视图控制器(biz-UI 绑定点) */
};
/* ---- 生命周期 ---- */
meow_view *meow_view_create(int id, meow_view_type type,
const meow_view_hooks *hooks, void *ctx);
void meow_view_free(meow_view *v); /* 递归 on_destroy + widget_free 叶子 + 注销 z 序 */
/* ---- 嵌套(组合视图树)---- */
int meow_view_add_child(meow_view *parent, meow_view *child); /* 任意深挂子 view;0/负=结果 */
int meow_view_add_widget(meow_view *v, meow_widget *w); /* 挂叶子控件(登记 z 深度源) */
void meow_view_set_z(meow_view *v, int z); /* 显式 z(999 置顶);影响其绘制物 z */
int meow_view_z(const meow_view *v);
/* ---- 页面导航(包装 activity 栈,唯一驱动 on_enter/on_leave——单一入口,App 不再分别调)----
* 顶层页切换走 activity 栈;页内子导航为预留(见设计定案 b)。 */
void meow_view_page_push(int id); /* activity_push + 旧top on_leave + 新页 on_enter */
void meow_view_page_pop(void); /* 旧top on_leave + activity_pop + 新top on_enter */
void meow_view_page_replace(int id); /* 旧top on_leave + 新页 on_enter(目标页须已 create) */
/* ---- 页内子导航(预留扩展位 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(未实现于本批) */
/* ---- 坐标:view 相对父内容原点的累计(父坐标链求和 → 屏幕绝对坐标)---- */
int meow_view_screen_x(const meow_view *v);
int meow_view_screen_y(const meow_view *v);
/* ---- 查询 ---- */
meow_view *meow_view_parent(const meow_view *v);
int meow_view_get_state(const meow_view *v); /* MEOW_VIEW_STATE_* 位掩码 */
#ifdef __cplusplus
}
#endif
#endif /* MEOW_UI_VIEW_H */
+47 -1
View File
@@ -211,10 +211,16 @@ static meow_widget *live_btn_hit(int x, int y)
return r;
}
/* 重绘并局部刷新一个按钮区(绘制内部调 meow_gfx_fill_round 已标脏;flush_region 合区+上屏)。 */
/* 重绘并局部刷新一个按钮区。
* §6.2 收敛:若按钮已注册绘制物 → 走 meow_ui_invalidate(清底 + 按 z 重画重叠区完整 z 栈),
* 避免按钮压上层 chrome 时被单控件 flush 抹掉(P1);未注册则回退单控件绘制+区域上屏。 */
static void live_btn_redraw(meow_widget *w)
{
if (!w) return;
if (meow_draw_widget_registered(w)) {
meow_ui_invalidate(w->x, w->y, w->w, w->h);
return;
}
meow_widget_draw(w);
meow_ui_flush_region(w->x, w->y, w->w, w->h);
}
@@ -275,6 +281,7 @@ meow_widget *meow_btn_create(int x, int y, int w, int h, const char *label)
t->state = MEOW_WS_ENABLED; /* §5.8.3:默认 ENABLED */
if (label) { strncpy(t->label, label, sizeof t->label - 1); t->label[sizeof t->label - 1] = 0; }
live_btn_register(t);
meow_draw_widget_register(t); /* §6.2 自动注册(创建即注册,App 零调用) */
return t;
}
@@ -286,6 +293,7 @@ meow_widget *meow_slider_create(int x, int y, int w, int min, int max, int val)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->min = min; t->max = (max > min) ? max : min + 1; t->val = (val < min) ? min : (val > t->max) ? t->max : val;
meow_draw_widget_register(t);
return t;
}
@@ -298,6 +306,7 @@ meow_widget *meow_switch_create(int x, int y, int on)
t->state = MEOW_WS_ENABLED;
t->on = on ? 1 : 0;
if (t->on) t->state |= MEOW_WS_SELECTED; /* §5.8.3:开关 on → selected */
meow_draw_widget_register(t);
return t;
}
@@ -310,12 +319,14 @@ meow_widget *meow_list_create(int x, int y, int w, int h, int count,
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->count = count; t->src = src; t->ctx = ctx; t->sel = 0;
meow_draw_widget_register(t);
return t;
}
void meow_widget_free(meow_widget *w)
{
if (!w) return;
meow_draw_widget_unregister(w); /* §6.2:绘制物注册表自动注销(stale id 失效勿用) */
live_btn_unregister(w);
free(w);
}
@@ -334,6 +345,7 @@ meow_widget *meow_widget_custom_create(int x, int y, int w, int h,
t->render_cb = render_cb;
t->hit_cb = hit_cb;
t->ctx = ctx;
meow_draw_widget_register(t);
return t;
}
@@ -346,6 +358,7 @@ meow_widget *meow_row_create(int x, int y, int w, int h, int spacing)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->spacing = spacing > 0 ? spacing : 0;
meow_draw_widget_register(t);
return t;
}
meow_widget *meow_column_create(int x, int y, int w, int h, int spacing)
@@ -356,6 +369,7 @@ meow_widget *meow_column_create(int x, int y, int w, int h, int spacing)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->spacing = spacing > 0 ? spacing : 0;
meow_draw_widget_register(t);
return t;
}
meow_widget *meow_box_create(int x, int y, int w, int h)
@@ -366,6 +380,7 @@ meow_widget *meow_box_create(int x, int y, int w, int h)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->spacing = 0;
meow_draw_widget_register(t);
return t;
}
meow_widget *meow_layout_spacer(int w, int h)
@@ -375,6 +390,7 @@ meow_widget *meow_layout_spacer(int w, int h)
t->type = W_SPACER; t->x = 0; t->y = 0; t->w = w > 0 ? w : 0; t->h = h > 0 ? h : 0;
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
meow_draw_widget_register(t);
return t;
}
@@ -421,6 +437,24 @@ void meow_layout_arrange(meow_widget *layout)
}
}
/* ---- 布局深度反推访问器(meow_ui_draw.c 用,读本 TU 的 struct meow_widget 内部字段)---- */
int meow_widget_is_layout(const meow_widget *w)
{ return w && w->type == W_LAYOUT; }
int meow_widget_nchild(const meow_widget *w)
{ return (w && w->type == W_LAYOUT) ? w->nchildren : 0; }
const meow_widget *meow_widget_child_at(const meow_widget *w, int idx)
{ return (w && w->type == W_LAYOUT && idx >= 0 && idx < w->nchildren) ? w->children[idx] : NULL; }
/* ---- 几何/归属访问器(meow_ui_draw.c 用:绘制物 rect 实时读 + owner 过滤)---- */
int meow_widget_owner(const meow_widget *w)
{ return w ? w->owner : 0; }
void meow_widget_rect(const meow_widget *w, int out[4])
{
if (!out) return;
if (!w) { out[0] = out[1] = out[2] = out[3] = 0; return; }
out[0] = w->x; out[1] = w->y; out[2] = w->w; out[3] = w->h;
}
/* ============================================================ */
/* §5.8.6 batch2 Material 组件子集 API(create/setter/getter/hit) */
/* ============================================================ */
@@ -460,6 +494,7 @@ meow_widget *meow_textfield_create(int x, int y, int w, int h, const char *place
t->buf = buf; t->buflen = buf ? (int)strlen(buf) : 0;
t->maxlen = (maxlen > 0 && maxlen <= 256) ? maxlen : 256;
t->ime_mode = mode;
meow_draw_widget_register(t);
return t;
}
void meow_textfield_set_placeholder(meow_widget *w, const char *placeholder)
@@ -515,6 +550,7 @@ meow_widget *meow_card_create(int x, int y, int w, int h)
t->type = W_CARD; t->x = x; t->y = y; t->w = w; t->h = h;
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
meow_draw_widget_register(t);
return t;
}
void meow_card_set_content(meow_widget *card, meow_widget *child)
@@ -535,6 +571,7 @@ meow_widget *meow_divider_create(int x, int y, int len, int vertical)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->variant = vertical ? 1 : 0;
meow_draw_widget_register(t);
return t;
}
@@ -547,6 +584,7 @@ meow_widget *meow_badge_create(int cx, int cy)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->value = 0; /* 红点 */
meow_draw_widget_register(t);
return t;
}
void meow_badge_set(meow_widget *w, int count)
@@ -564,6 +602,7 @@ meow_widget *meow_checkbox_create(int x, int y)
t->type = W_CHECKBOX; t->x = x; t->y = y; t->w = 34; t->h = 34;
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
meow_draw_widget_register(t);
return t;
}
meow_widget *meow_radio_create(int x, int y)
@@ -573,6 +612,7 @@ meow_widget *meow_radio_create(int x, int y)
t->type = W_RADIO; t->x = x; t->y = y; t->w = 34; t->h = 34;
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
meow_draw_widget_register(t);
return t;
}
@@ -586,6 +626,7 @@ meow_widget *meow_progress_create(int x, int y, int w, int h, int value)
t->state = MEOW_WS_ENABLED;
t->variant = 0; /* 线性 */
t->value = value < 0 ? -1 : (value > 100 ? 100 : value);
meow_draw_widget_register(t);
return t;
}
meow_widget *meow_progress_circular_create(int cx, int cy, int r, int value)
@@ -603,6 +644,7 @@ meow_widget *meow_progress_circular_create(int cx, int cy, int r, int value)
t->state = MEOW_WS_ENABLED;
t->variant = 1; /* 圆形 */
t->value = value < 0 ? -1 : (value > 100 ? 100 : value);
meow_draw_widget_register(t);
return t;
}
void meow_progress_set(meow_widget *w, int value)
@@ -621,6 +663,7 @@ meow_widget *meow_fab_create(int x, int y, int size, const char *label)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
if (label) { strncpy(t->label, label, sizeof t->label - 1); t->label[sizeof t->label - 1] = 0; }
meow_draw_widget_register(t);
return t;
}
@@ -634,6 +677,7 @@ meow_widget *meow_topappbar_create(int x, int y, int w, int h, const char *title
t->state = MEOW_WS_ENABLED;
t->back_on = 0; t->nitems = 0;
if (title) { strncpy(t->label, title, sizeof t->label - 1); t->label[sizeof t->label - 1] = 0; }
meow_draw_widget_register(t);
return t;
}
void meow_topappbar_set_title(meow_widget *w, const char *title)
@@ -682,6 +726,7 @@ meow_widget *meow_navigationbar_create(int x, int y, int w, int h)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->nitems = 0; t->selected = 0;
meow_draw_widget_register(t);
return t;
}
int meow_navigationbar_add_item(meow_widget *w, const char *label)
@@ -721,6 +766,7 @@ meow_widget *meow_tabbar_create(int x, int y, int w, int h)
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->nitems = 0; t->selected = 0;
meow_draw_widget_register(t);
return t;
}
int meow_tabbar_add_tab(meow_widget *w, const char *label)
+1
View File
@@ -8,6 +8,7 @@
* gcc -Wall -Wextra -o test_ime_fix test_ime_fix.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_draw.c meow_ui_view.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
*/
#include "meow_ui.h"
+1
View File
@@ -5,6 +5,7 @@
* gcc -o test_meow_ui_widgets test_meow_ui_widgets.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_draw.c meow_ui_view.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
* 运行:MEOW_UI_DUMP=/tmp/b2.ppm ./test_meow_ui_widgets (可选,dump 屏幕)
*/
+19 -15
View File
@@ -18,12 +18,13 @@
* -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 stb_image.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
* 运行:MEOW_UI_DUMP=/tmp/repro.ppm ./test_p1p2p3_repro (optional,dump 最终屏幕)
*
* 修复前(无 P1/P2/P3 修法)预期输出:RESULT: 4 FAIL(S);
* 修复后:RESULT: ALL PASS。
* 覆盖(批次① z 序下沉后):P1c 显式 z 置顶(set_z(g_fab,999)+invalidate(r_prc) 不抹 FAB——
* 显式 z 不依赖绘制先后)+ P2/P3 与迁移前一致。预期 RESULT: ALL PASS。
*/
#include "meow_ui.h"
#include "meow_ui_internal.h"
@@ -74,13 +75,15 @@ int main(void)
int fails = 0;
/* ================= P1:局部重绘不盖浮层(FAB) =================
* 场景:scroll=0 时圆形进度(413..453,605..645) 与 FAB(406..462,616..672) 重叠。
* demo_paint 先画内容后画 chrome → 场景正确 FAB 在上。随后「局部重绘进度条」:
/* ================= P1 / P1c:局部重绘不盖浮层(FAB,须显式置顶) =================
* 场景:scroll=0 时圆形进度 r_prc(413..453,605..645) 与 FAB(406..462,616..672) 重叠。
* demo_paint 整帧后 FAB 在上(demo_init set_z(g_fab,999) 显式置顶)。随后「局部重绘进度条」:
* 旧(清底+只画进度条+flush 其矩形)→ FAB 重叠区被清掉;
* 新(清底+重画重叠区内所有控件=hello d_redraw/d_paint_region)→ FAB 保持。 */
* 新(SDK meow_ui_invalidate:清底 + 按 z 重画重叠区完整 z 栈)→ FAB 保持。
* P1c:set_z(g_fab,999) 后 invalidate(r_prc),断言 FAB 重叠点(434,622) 仍=demo_paint 后值
* (显式 z 不依赖绘制先后——置顶在重绘前显式给出,下层重绘不抹上层)。 */
demo_paint();
int fx = r_fab.x + 28, fy = r_fab.y + 6; /* FAB 内、远离 "+" 文本处 */
int fx = r_fab.x + 28, fy = r_fab.y + 6; /* FAB 内、远离 "+" 文本处 → (434,622) */
int s0r, s0g, s0b; rd_px(fx, fy, &s0r, &s0g, &s0b);
printf("P1 scene (demo_paint) FAB-overlap "); pr_px("scene", fx, fy);
if (is_bg(s0r, s0g, s0b))
@@ -98,15 +101,16 @@ int main(void)
if (is_bg(obr, obg, obb)) printf(" [BUG-CONFIRMED] old path erased FAB overlap (bg)\n");
else printf(" [note] old path did NOT erase FAB here (color %02X%02X%02X)\n", obr, obg, obb);
/* 新式局部重绘(真实 hello d_redraw → d_paint_region,重画重叠栈)*/
/* P1c:显式置顶 + SDK 局部重画(invalidate 按 z 重画重叠栈) */
demo_paint(); /* 还原场景 */
meow_widget_set_z(g_fab, 999); /* 重新显式置顶(P1c 要求) */
meow_progress_set(g_dprc, 30);
d_redraw(g_dprc, r_prc); /* hello 的局部重绘入口 */
meow_ui_invalidate(r_prc.x, r_prc.y, r_prc.w, r_prc.h);
int nbr, nbg, nbb; rd_px(fx, fy, &nbr, &nbg, &nbb);
printf("P1 new-partial (d_redraw stack) "); pr_px("new", fx, fy);
printf("P1c set_z(999)+invalidate(r_prc) "); pr_px("p1c", fx, fy);
if (near(s0r, nbr) && near(s0g, nbg) && near(s0b, nbb))
printf(" P1-PASS FAB preserved over live progress redraw\n");
else { printf(" P1-FAIL FAB not preserved (scene %02X%02X%02X vs new %02X%02X%02X)\n",
printf(" P1-PASS explicit z:FAB preserved over live progress redraw\n");
else { printf(" P1-FAIL FAB not preserved (scene %02X%02X%02X vs p1c %02X%02X%02X)\n",
s0r, s0g, s0b, nbr, nbg, nbb); fails++; }
/* ================= P2:列表焦点按本次触摸绑定/释放 =================
@@ -128,7 +132,7 @@ int main(void)
printf("P3 list rect = (%d,%d,%d,%d)\n", r_list.x, r_list.y, r_list.w, r_list.h);
/* 顶部泄漏:选中行0 + scroll=20 → 行0 ry=top-20,填充越顶泄漏 */
meow_list_set_selected(g_dlc, 0); meow_list_set_scroll(g_dlc, 20);
d_redraw(g_dlc, r_list);
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
int ux = r_list.x + 10, uy = r_list.y - 5; /* 列表顶上方 5px(标题间隙) */
int ur, ug, ub; rd_px(ux, uy, &ur, &ug, &ub);
printf("P3 top-leak "); pr_px("above-top", ux, uy);
@@ -136,7 +140,7 @@ int main(void)
else { printf(" P3-top-FAIL leaked into title gap\n"); fails++; }
/* 底部泄漏:选中行4 + scroll=20 → 行4 ry=590..630 越底泄漏 */
meow_list_set_selected(g_dlc, 4); meow_list_set_scroll(g_dlc, 20);
d_redraw(g_dlc, r_list);
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
int dx = r_list.x + 10, dy_ = r_list.y + r_list.h + 5; /* 列表底下方 5px */
int dr, dg, db; rd_px(dx, dy_, &dr, &dg, &db);
printf("P3 bot-leak "); pr_px("below-bottom", dx, dy_);
+1
View File
@@ -10,6 +10,7 @@
* gcc -Wall -o test_sdk_fixes test_sdk_fixes.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_draw.c meow_ui_view.c \
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
*/
#include "meow_ui.h"