Files
MeowOS-For-R3P2/patch_v2/sdk/meow_ui_draw.c
T
AskaEth 54d2791f48 sdk: 事件分发形式化(§6.4)—— 统一命中/捕获/消费/层间隔离/焦点
根治「焦点/事件穿透」一类反复 bug。此前命中/捕获/消费/层间隔离各处 ad-hoc;
现上收为单一仲裁者(meow_ui_input.c),公共 API 只追加、不删改既有签名。

分发模型:
- 触摸沿 z 序自顶向下命中(meow_draw_hit_topdown,owner==active+enable 过滤);
- 命中即消费;模态层永远先审(meow_ui_modal_begin/end/active/depth);
- down 捕获(meow_ui_gesture_begin/owner/end):命中目标捕获手势直到 up,防滑出误触下层;
- 模态存在时下层完全收不到事件——hit_topdown 恒返回模态哨兵(背板消费,不透传);
- meow_ui_run 在 TOUCH_DOWN 自动 begin、TAP/SWIPE/LONG_PRESS/NOTIF_PULL 自动 end。

焦点模型:
- 唯一持有者(meow_ui_focus_acquire/release/owner/tap);
- 点非 focus 区域→释放并通知(meow_ui_focus_tap,list 焦点残留根治);
- 滚动容器(list/listview)tap_at 命中行自动 acquire,点外由 run 的 focus_tap 释放;
- lost 回调(meow_ui_focus_set_lost_cb)通知转移。

迁移(行为修复,正常交互语义不变):
- dialog/sheet → meow_ui_modal_begin/end(模态期下层收不到;叠黑由既有整帧快照+卡片仅重绘保证);
- list/listview tap → meow_ui_focus_acquire;
- 注入/叠加让位(overlay_input_suppressed)与模态层隔离互补,语义不变。

验证:新穿透矩阵 test_meow_ui_dispatch(M1 模态点下层不触发 / M2 弹层叠开两层层隔离 /
M3 捕获手势滑出不误触 / M4 list 焦点点外释放)ALL PASS;
全量回归 widgets/ime_fix/pinyin/overlay_suppress/notif_gesture/sdk_fixes/sb_collapse/
p1p2p3_repro/box/anim/scroll/sdk_input_keys 全 PASS;交叉 -Wall -Wextra 0 warning。

meow_ui.a 新 md5:a9fcfb6eeb07ddbdd48039df4f8f18ce(主会话统一重链,本仓未重链 App)
2026-09-02 00:57:58 +08:00

393 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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)
{
/* 子>父:view 或其任意祖先已进入即视为「可绘制」——配合 view_enter/leave 递归
* (meow_ui_view.c §A.4),父 on_enter 重绘时子视图(尚未单独进入)也要经 on_draw 画出;
* 且子随父进出("子视图随父进出"),祖先已进入 ⇒ 子树已进入,可绘制。 */
const struct meow_view *cur = v;
while (cur) {
if (meow_view_get_state(cur) & MEOW_VIEW_STATE_ENTERED) return 1;
cur = meow_view_parent(cur);
}
return 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;
}
}
/* ============================================================ */
/* §6.4 自上而下命中(事件分发形式化) */
/* ============================================================ */
void *meow_draw_hit_topdown(int x, int y)
{
void *best = NULL;
int bestz = -1, bestseq = -1;
for (int i = 0; i < g_n; i++) {
draw_entry *e = &g_e[i];
e_compute_auto(e);
/* 只对注册控件做顶层命中;view 是容器(其子 widget 是独立更高 z 的绘制物,各自命中),
* CUSTOM 是 App 直绘(不参与命中)。meow_widget_hit 内部含 owner==active + enable +
* 矩形/自定义命中回调。 */
if (e->kind != MEOW_ITEM_WIDGET || !e->w) continue;
if (!meow_widget_hit(e->w, x, y)) continue;
if (e->z > bestz || (e->z == bestz && e->seq > bestseq)) {
bestz = e->z; bestseq = e->seq; best = e->w;
}
}
return best;
}
/* ============================================================ */
/* 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];
/* 区域重画(meow_ui_invalidate):把绘制 clip 收敛到本次失效矩形,只画「真正相交且可见」的
* 部分——大绘制物(整页 card/布局)仅擦边相交时不再整块重画,而是裁到矩形内,避免 overdraw。
* 控件内部还会再 push 自身矩形(嵌套求交 = 矩形∩控件,语义不变:越界仍被裁)。 */
meow_gfx_clip_push(x, y, w, h);
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_gfx_clip_pop();
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);
}