- meow_box(include/meow_ui_box.h + meow_ui_box.c):竖直/水平容器;子项=固定|weight|wrap; margin/padding/gap;主轴对齐 start/center/end/space-between/around/evenly;交叉轴 start/center/end/stretch; 嵌套(深度≤16 静态栈,单遍求解,无递归爆炸)。可作 meow_view 布局提供者 (meow_view_set_layout/relayout,opt-in;未设布局的 view 保持现有绝对坐标行为)。 新增 meow_widget_set_rect(只写字段不标脏)作为 box 写坐标的通道。 - meow_anim(include/meow_ui_anim.h + meow_ui_anim.c):时间驱动 tween(from/to/duration/easing: linear/ease-out/ease-in-out/overshoot)+ update/done 回调;静态池≤16、取消、并行多动画; meow_anim_set_region 让每帧只 flush 动画影响区(不整屏)。meow_anim_tick 挂主循环 on_tick, 无动画零开销(只查计数即返回)。 - meow_scroll(include/meow_ui_scroll.h + meow_ui_scroll.c):速度跟踪(最近 N 采样加权)+ 松手 fling (减速度曲线,用 anim 基座驱动)+ 边界过冲 clamp + 简单回弹(不做 spring)+ 可选 snap; 跟手拖动行为不变(track 内部调既有 set_scroll);每帧只 redraw 列表视口区;不引 libm。 - 追加式:既有 meow_row/column/box 与全部 meow_*_create/hit/scroll 签名一行未改;旧绝对坐标永远有效; 既有 App 免改源码。 - 验证:宿主新测试 box(42)/anim(32)/scroll(17) 全 PASS;既存 9 回归(widgets/sdk_input_keys/ime_fix/ pinyin/overlay_suppress/notif_gesture/sdk_fixes/sb_collapse/p1p2p3_repro)全 PASS; 离屏 demo(demo_box_scroll.c)渲染 box 布局 + 列表 fling 正常;zig-mipsel -Wall -Wextra 全 TU 0 warning; meow_ui.a md5 83dd6a2d0d24cc748a8659c29f1de2b5(未重链/部署 hello/files/ncm/reader,由主会话统一处理)。
283 lines
10 KiB
C
283 lines
10 KiB
C
/*
|
||
* 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 "include/meow_ui_box.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(生命周期跟踪) */
|
||
|
||
/* ---- 布局提供者注册表(view → meow_box,opt-in) ---- */
|
||
static struct { meow_view *v; meow_box *box; } g_view_layout[MEOW_VIEW_BY_ID_MAX];
|
||
static int g_view_layout_n = 0;
|
||
|
||
static void layout_reg_set(meow_view *v, meow_box *box)
|
||
{
|
||
for (int i = 0; i < g_view_layout_n; i++) {
|
||
if (g_view_layout[i].v == v) {
|
||
g_view_layout[i].box = box;
|
||
return;
|
||
}
|
||
}
|
||
if (g_view_layout_n < MEOW_VIEW_BY_ID_MAX) {
|
||
g_view_layout[g_view_layout_n].v = v;
|
||
g_view_layout[g_view_layout_n].box = box;
|
||
g_view_layout_n++;
|
||
}
|
||
}
|
||
static meow_box *layout_reg_get(const meow_view *v)
|
||
{
|
||
for (int i = 0; i < g_view_layout_n; i++)
|
||
if (g_view_layout[i].v == v) return g_view_layout[i].box;
|
||
return NULL;
|
||
}
|
||
|
||
/* 重排:把 view 的布局 box 解算一次(box 矩形未设时自动跟 view 内容矩形)。 */
|
||
void meow_view_relayout(meow_view *v)
|
||
{
|
||
if (!v) return;
|
||
meow_box *b = layout_reg_get(v);
|
||
if (!b) return;
|
||
int r[4]; meow_box_rect(b, r);
|
||
if (r[2] <= 0 || r[3] <= 0) /* w/h 未设 → 跟 view 内容矩形 */
|
||
meow_box_set_rect(b, v->x, v->y, v->w, v->h);
|
||
meow_box_arrange(b);
|
||
}
|
||
void meow_view_set_layout(meow_view *v, struct meow_box *box)
|
||
{
|
||
if (!v) return;
|
||
if (!box) { layout_reg_set(v, NULL); return; }
|
||
layout_reg_set(v, box);
|
||
meow_view_relayout(v); /* 立即按当前尺寸排一次 */
|
||
}
|
||
|
||
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),**递归 children**(子视图随父进出,§A.4)。
|
||
* 进入顺序:父 on_enter 先(记录顺序 home.enter→demo.enter),随后子视图依递归逐个进入;
|
||
* 子视图的 on_draw 经 meow_draw_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);
|
||
for (int i = 0; i < v->nchildren; i++)
|
||
view_enter(v->children[i]);
|
||
}
|
||
/* 离开一个 view(清 ENTERED + on_leave;默认不销毁子 view/widget——弹回零重建),
|
||
* **递归 children**:先子后父(demo.leave→home.leave)。 */
|
||
static void view_leave(meow_view *v)
|
||
{
|
||
if (!v || !(v->state & MEOW_VIEW_STATE_ENTERED)) return;
|
||
for (int i = 0; i < v->nchildren; i++)
|
||
view_leave(v->children[i]);
|
||
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);
|
||
meow_view_relayout(next); /* 布局提供者(meow_box)随页进入自动排一次 */
|
||
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);
|
||
meow_view_relayout(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);
|
||
meow_view_relayout(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; /* 预留 */
|
||
}
|
||
|
||
/* ============================================================ */
|
||
/* 事件路由(§A.7:每个 view 的 on_event 先把事件发给后代(按 z 降序),命中即消费,
|
||
* 再落本 view 的控件)。SDK 提供递归树走;叶子 on_event 负责本 view 的 widget 命中。 */
|
||
/* ============================================================ */
|
||
static int view_dispatch_event(meow_view *v, int ev, int a, int b)
|
||
{
|
||
if (!v || !(v->state & MEOW_VIEW_STATE_ENTERED)) return 0; /* 仅已进入的视图参与 */
|
||
if (v->nchildren > 0) {
|
||
meow_view *kids[MEOW_VIEW_MAX_CHILD];
|
||
int nn = v->nchildren;
|
||
for (int i = 0; i < nn; i++) kids[i] = v->children[i];
|
||
/* 按 z 降序(同 z 保持挂载序稳定);z 同值时树序,靠后挂的在先。 */
|
||
for (int i = 1; i < nn; i++) {
|
||
meow_view *key = kids[i];
|
||
int j = i - 1;
|
||
while (j >= 0 && meow_view_z(kids[j]) < meow_view_z(key)) { kids[j + 1] = kids[j]; j--; }
|
||
kids[j + 1] = key;
|
||
}
|
||
for (int i = 0; i < nn; i++)
|
||
if (view_dispatch_event(kids[i], ev, a, b)) return 1; /* 子命中即消费 */
|
||
}
|
||
if (v->hooks.on_event && v->hooks.on_event(v, ev, a, b, v->ctx)) return 1; /* 落到本 view 事件 */
|
||
return 0;
|
||
}
|
||
|
||
int meow_view_dispatch_event(int ev, int a, int b)
|
||
{
|
||
return view_dispatch_event(g_top_view, ev, a, b);
|
||
}
|
||
|
||
/* ============================================================ */
|
||
/* 坐标 / 查询 */
|
||
/* ============================================================ */
|
||
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; }
|