371 lines
13 KiB
C
371 lines
13 KiB
C
/*
|
||
* 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;
|
||
}
|
||
}
|
||
|
||
/* ============================================================ */
|
||
/* 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);
|
||
}
|