feat(sdk): 绘制期通用clip + view递归/on_event接线 (agent-195) — ①栈式clip rect(g_clip_stack[16]), 所有写像素原语过clip, meow_widget_draw push/pop控件矩形(含W_CUSTOM, 根治越界脏区), IME/模态顶层全屏不受影响 ②view enter/leave递归children + meow_view_dispatch_event(z降序子>父命中即消费); hello App层显式驱动退役
meow_ui.a→da4d3a7d; hello→c655ba43; 宿主4套件全PASS(含clip越界断言+view递归断言) 出入: W_SLIDER拇指在max处被裁(既有几何瑕疵可见化,待修); on_draw祖先已进入即画
This commit is contained in:
1 parent
b2e7cc8c70
commit
32a6a8ed1e
14 files changed
+257
-44
No files matched your search
Binary file not shown.
@@ -95,11 +95,10 @@ extern rect_t r_dr1, r_dr2; /* Radio 几何 */
|
|||||||
/* ---- 列表区 CONTAINER 子视图(ui/demo/list/list.c;批③ 视图深度化)----
|
/* ---- 列表区 CONTAINER 子视图(ui/demo/list/list.c;批③ 视图深度化)----
|
||||||
* demo 页把列表逻辑区块抽成 meow_view CONTAINER 子视图挂到 DEMO ACTIVITY view children,
|
* demo 页把列表逻辑区块抽成 meow_view CONTAINER 子视图挂到 DEMO ACTIVITY view children,
|
||||||
* 承接列表交互(tap/down/move/swipe)、列表视图态与列表区块标题 on_draw 直绘。
|
* 承接列表交互(tap/down/move/swipe)、列表视图态与列表区块标题 on_draw 直绘。
|
||||||
* 嵌套生命周期(list_view_enter/leave)由 demo 页 on_enter/on_leave 驱动(SDK 骨架不递归)。 */
|
* 嵌套生命周期由 SDK view_enter/view_leave 递归驱动(随 demo 页进出;list_view_enter/leave
|
||||||
|
* 已退役,无需 App 层显式接管)。 */
|
||||||
meow_view *list_view_create(void); /* 建 CONTAINER 子视图(须即 meow_view_add_child) */
|
meow_view *list_view_create(void); /* 建 CONTAINER 子视图(须即 meow_view_add_child) */
|
||||||
meow_view *list_view_get(void); /* 返回子视图(宿主测嵌套 on_enter/on_leave 断言) */
|
meow_view *list_view_get(void); /* 返回子视图(宿主测嵌套 on_enter/on_leave 断言) */
|
||||||
void list_view_enter(void); /* 进入子视图(置 ENTERED + on_enter,幂等) */
|
|
||||||
void list_view_leave(void); /* 离开子视图(清 ENTERED + on_leave,幂等) */
|
|
||||||
void list_view_bind(meow_widget *w, rect_t list, rect_t title); /* 内容重建后绑控件+几何+恢复态 */
|
void list_view_bind(meow_widget *w, rect_t list, rect_t title); /* 内容重建后绑控件+几何+恢复态 */
|
||||||
void list_view_release(void); /* 内容释放后摘引用/几何 */
|
void list_view_release(void); /* 内容释放后摘引用/几何 */
|
||||||
int list_view_hit(int x, int y); /* tap 分发给列表区,命中即消费(含选中) */
|
int list_view_hit(int x, int y); /* tap 分发给列表区,命中即消费(含选中) */
|
||||||
|
|||||||
@@ -408,7 +408,8 @@ static void demo_page_enter(meow_view *v, void *ctx)
|
|||||||
((demo_ctrl *)ctx)->active = 1;
|
((demo_ctrl *)ctx)->active = 1;
|
||||||
g_demo = 1;
|
g_demo = 1;
|
||||||
g_demo_focus = 0;
|
g_demo_focus = 0;
|
||||||
list_view_enter(); /* 先进入列表区子视图(其 on_draw 在下面 rebuild→paint 中经注册表触发) */
|
/* 列表区 CONTAINER 子视图随本页进入由 SDK view_enter 递归驱动(其 on_draw 在 demo_rebuild_to
|
||||||
|
* →demo_paint 中经注册表绘制;meow_draw_view_entered 含祖先已进入判定,首批可见)。 */
|
||||||
demo_rebuild_to(0);
|
demo_rebuild_to(0);
|
||||||
meow_log(MEOW_LOG_INFO, "demo enter scroll=0");
|
meow_log(MEOW_LOG_INFO, "demo enter scroll=0");
|
||||||
}
|
}
|
||||||
@@ -418,7 +419,7 @@ static void demo_page_leave(meow_view *v, void *ctx)
|
|||||||
(void)v;
|
(void)v;
|
||||||
((demo_ctrl *)ctx)->active = 0;
|
((demo_ctrl *)ctx)->active = 0;
|
||||||
g_demo = 0;
|
g_demo = 0;
|
||||||
list_view_leave(); /* 子离开先于父离开(嵌套顺序) */
|
/* 列表区子视图由 SDK view_leave 在父之前递归离开(子<先>父,§A.4/D.1)。 */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void demo_page_destroy(meow_view *v, void *ctx)
|
static void demo_page_destroy(meow_view *v, void *ctx)
|
||||||
|
|||||||
@@ -13,8 +13,9 @@
|
|||||||
* 实现「子>父」命中。list **控件创建/释放**仍留在 demo 页内容重建流(demo_build_at/
|
* 实现「子>父」命中。list **控件创建/释放**仍留在 demo 页内容重建流(demo_build_at/
|
||||||
* demo_free_content),因为 widget 由 z 序注册表 auto 注册/注销、并随 scroll 重建——完整
|
* demo_free_content),因为 widget 由 z 序注册表 auto 注册/注销、并随 scroll 重建——完整
|
||||||
* 的「控件归容器拥有」需 SDK 侧支持(本批冻结 patch_v2/sdk/),故以绑定引用的方式折中。
|
* 的「控件归容器拥有」需 SDK 侧支持(本批冻结 patch_v2/sdk/),故以绑定引用的方式折中。
|
||||||
* - SDK 骨架(meow_ui_view.c)的 view_enter/view_leave 不递归 children(批次① 仅顶层页);
|
* - SDK(meow_ui_view.c)的 view_enter/view_leave **递归 children**(批次①→③ 后接线):
|
||||||
* 本子视图的 on_enter/on_leave 由 demo 页 on_enter/on_leave 显式驱动(list_view_enter/leave)。
|
* 本子视图随 demo 页 on_enter/on_leave 自动进入/离开,无需 App 层显式驱动(list_view_enter/leave
|
||||||
|
* 已退役);父 on_enter 重绘时子视图经 meow_draw_view_entered(含祖先已进入)可见。
|
||||||
* - on_draw 是回调、view 不缓存像素(非保留,§5.8.7);绘制由 meow_ui_paint_full/invalidate
|
* - on_draw 是回调、view 不缓存像素(非保留,§5.8.7);绘制由 meow_ui_paint_full/invalidate
|
||||||
* 触发,仅当本子视图 state 含 MEOW_VIEW_STATE_ENTERED 时注册表才绘制(meow_draw_view_entered)。
|
* 触发,仅当本子视图 state 含 MEOW_VIEW_STATE_ENTERED 时注册表才绘制(meow_draw_view_entered)。
|
||||||
*/
|
*/
|
||||||
@@ -66,24 +67,8 @@ static void list_page_destroy(meow_view *v, void *ctx)
|
|||||||
g_list_view = NULL; /* meow_view_free 递归释放前清引用,防悬垂 */
|
g_list_view = NULL; /* meow_view_free 递归释放前清引用,防悬垂 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 进入子视图:置 ENTERED + on_enter(幂等)。SDK view_enter 不递归,故由父页显式驱动。 */
|
/* 建 CONTAINER 子视图(demo_page_create 调,随即 meow_view_add_child 到 demo ACTIVITY view)。
|
||||||
void list_view_enter(void)
|
* 进入/离开经 SDK view_enter/view_leave 递归驱动(随 demo 页),本文件不再显式接管。 */
|
||||||
{
|
|
||||||
if (!g_list_view) return;
|
|
||||||
if (g_list_view->state & MEOW_VIEW_STATE_ENTERED) return;
|
|
||||||
g_list_view->state |= MEOW_VIEW_STATE_ENTERED;
|
|
||||||
if (g_list_view->hooks.on_enter) g_list_view->hooks.on_enter(g_list_view, g_list_view->ctx);
|
|
||||||
}
|
|
||||||
/* 离开子视图:清 ENTERED + on_leave(幂等)。 */
|
|
||||||
void list_view_leave(void)
|
|
||||||
{
|
|
||||||
if (!g_list_view) return;
|
|
||||||
if (!(g_list_view->state & MEOW_VIEW_STATE_ENTERED)) return;
|
|
||||||
g_list_view->state &= ~MEOW_VIEW_STATE_ENTERED;
|
|
||||||
if (g_list_view->hooks.on_leave) g_list_view->hooks.on_leave(g_list_view, g_list_view->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 建 CONTAINER 子视图(demo_page_create 调,随即 meow_view_add_child 到 demo ACTIVITY view)。 */
|
|
||||||
meow_view *list_view_create(void)
|
meow_view *list_view_create(void)
|
||||||
{
|
{
|
||||||
static const meow_view_hooks h = {
|
static const meow_view_hooks h = {
|
||||||
|
|||||||
@@ -243,7 +243,15 @@ void meow_draw_view_unregister(struct meow_view *v)
|
|||||||
}
|
}
|
||||||
int meow_draw_view_entered(const struct meow_view *v)
|
int meow_draw_view_entered(const struct meow_view *v)
|
||||||
{
|
{
|
||||||
return v ? meow_view_get_state(v) & MEOW_VIEW_STATE_ENTERED : 0;
|
/* 子>父: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)
|
void meow_draw_view_set_z(struct meow_view *v, int z)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,6 +62,48 @@ static int g_dirty[/*MUI_DIRTY_MAX*/12][4]; /* [x,y,w,h] */
|
|||||||
static int g_ndirty = 0;
|
static int g_ndirty = 0;
|
||||||
static int g_dirty_full = 1;
|
static int g_dirty_full = 1;
|
||||||
|
|
||||||
|
/* ---- 绘制期裁剪(clip rect,§3;默认全屏)----
|
||||||
|
* 栈式嵌套:push 与当前 clip 求交后压栈,pop 恢复。写像素原语(put_px / put_px_blend /
|
||||||
|
* fill_rect / fill_rect_alpha / round / circle / ring / blit_icon / blit_rgba / text)
|
||||||
|
* 统一过 g_clip;顶层(g_clip_n==0)= 全屏(g_xres×g_yres),故 IME/模态的全屏 scrim
|
||||||
|
* 直绘不受裁剪。 */
|
||||||
|
#define MUI_CLIP_MAX 16
|
||||||
|
static int g_clip_stack[MUI_CLIP_MAX][4]; /* [x0,y0,x1,y1](闭开区间) */
|
||||||
|
static int g_clip_n = 0;
|
||||||
|
|
||||||
|
static void clip_cur(int out[4]) { /* 当前 clip 边界(无 clip = 全屏) */
|
||||||
|
if (g_clip_n > 0) {
|
||||||
|
out[0] = g_clip_stack[g_clip_n - 1][0]; out[1] = g_clip_stack[g_clip_n - 1][1];
|
||||||
|
out[2] = g_clip_stack[g_clip_n - 1][2]; out[3] = g_clip_stack[g_clip_n - 1][3];
|
||||||
|
} else { out[0] = 0; out[1] = 0; out[2] = g_xres; out[3] = g_yres; }
|
||||||
|
}
|
||||||
|
static int clip_contains(int x, int y)
|
||||||
|
{
|
||||||
|
if (g_clip_n == 0) return x >= 0 && x < g_xres && y >= 0 && y < g_yres;
|
||||||
|
return x >= g_clip_stack[g_clip_n - 1][0] && x < g_clip_stack[g_clip_n - 1][2] &&
|
||||||
|
y >= g_clip_stack[g_clip_n - 1][1] && y < g_clip_stack[g_clip_n - 1][3];
|
||||||
|
}
|
||||||
|
|
||||||
|
void meow_gfx_clip_push(int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
if (!g_init_ok || w <= 0 || h <= 0) return;
|
||||||
|
int c[4]; clip_cur(c);
|
||||||
|
int ix0 = x > c[0] ? x : c[0];
|
||||||
|
int iy0 = y > c[1] ? y : c[1];
|
||||||
|
int ix1 = (x + w) < c[2] ? (x + w) : c[2];
|
||||||
|
int iy1 = (y + h) < c[3] ? (y + h) : c[3];
|
||||||
|
if (ix1 < ix0) ix1 = ix0;
|
||||||
|
if (iy1 < iy0) iy1 = iy0;
|
||||||
|
if (g_clip_n >= MUI_CLIP_MAX) return; /* 栈满:忽略本次 push(保持当前 clip) */
|
||||||
|
g_clip_stack[g_clip_n][0] = ix0; g_clip_stack[g_clip_n][1] = iy0;
|
||||||
|
g_clip_stack[g_clip_n][2] = ix1; g_clip_stack[g_clip_n][3] = iy1;
|
||||||
|
g_clip_n++;
|
||||||
|
}
|
||||||
|
void meow_gfx_clip_pop(void)
|
||||||
|
{
|
||||||
|
if (g_clip_n > 0) g_clip_n--;
|
||||||
|
}
|
||||||
|
|
||||||
static char *g_dump_path = NULL;
|
static char *g_dump_path = NULL;
|
||||||
|
|
||||||
/* ---- 状态栏「上收出屏只留提示线」(collapsed,基座)----
|
/* ---- 状态栏「上收出屏只留提示线」(collapsed,基座)----
|
||||||
@@ -158,6 +200,17 @@ static void dirty_add(int x, int y, int w, int h)
|
|||||||
if (x + w > g_xres) w = g_xres - x;
|
if (x + w > g_xres) w = g_xres - x;
|
||||||
if (y + h > g_yres) h = g_yres - y;
|
if (y + h > g_yres) h = g_yres - y;
|
||||||
if (w <= 0 || h <= 0) return;
|
if (w <= 0 || h <= 0) return;
|
||||||
|
/* 绘制期裁剪:脏区也不越 clip(顶层 clip=全屏 → 无操作)。 */
|
||||||
|
if (g_clip_n > 0) {
|
||||||
|
int *c = g_clip_stack[g_clip_n - 1];
|
||||||
|
int x1 = x + w, y1 = y + h;
|
||||||
|
if (x < c[0]) x = c[0];
|
||||||
|
if (y < c[1]) y = c[1];
|
||||||
|
if (x1 > c[2]) x1 = c[2];
|
||||||
|
if (y1 > c[3]) y1 = c[3];
|
||||||
|
if (x1 <= x || y1 <= y) return;
|
||||||
|
w = x1 - x; h = y1 - y;
|
||||||
|
}
|
||||||
for (int i = 0; i < g_ndirty; i++) {
|
for (int i = 0; i < g_ndirty; i++) {
|
||||||
if (rect_hit(g_dirty[i][0], g_dirty[i][1], g_dirty[i][2], g_dirty[i][3], x, y, w, h)) {
|
if (rect_hit(g_dirty[i][0], g_dirty[i][1], g_dirty[i][2], g_dirty[i][3], x, y, w, h)) {
|
||||||
int nx1 = g_dirty[i][0] < x ? g_dirty[i][0] : x;
|
int nx1 = g_dirty[i][0] < x ? g_dirty[i][0] : x;
|
||||||
@@ -237,6 +290,7 @@ void meow_gfx_blit_icon(int idx, int x, int y, int ink_h, uint32_t fg, int cy)
|
|||||||
for (int dx = 0; dx < t_w; dx++) {
|
for (int dx = 0; dx < t_w; dx++) {
|
||||||
int tx = x0 + dx;
|
int tx = x0 + dx;
|
||||||
if (tx < 0 || tx >= g_xres) continue;
|
if (tx < 0 || tx >= g_xres) continue;
|
||||||
|
if (!clip_contains(tx, ty)) continue;
|
||||||
int sx = minx + (dx * ink_w) / t_w;
|
int sx = minx + (dx * ink_w) / t_w;
|
||||||
int a = src[sy * SB_ICON_SIZE + sx];
|
int a = src[sy * SB_ICON_SIZE + sx];
|
||||||
if (a < 8) continue; /* 透明像素跳过 */
|
if (a < 8) continue; /* 透明像素跳过 */
|
||||||
@@ -265,7 +319,7 @@ static uint16_t px_read_rgb565(int x, int y)
|
|||||||
|
|
||||||
static void put_px(int x, int y, uint32_t color)
|
static void put_px(int x, int y, uint32_t color)
|
||||||
{
|
{
|
||||||
if (x < 0 || x >= g_xres || y < 0 || y >= g_yres) return;
|
if (!clip_contains(x, y)) return;
|
||||||
if (g_bpp == 16) {
|
if (g_bpp == 16) {
|
||||||
px_write_rgb565(x, y, rgb565(color));
|
px_write_rgb565(x, y, rgb565(color));
|
||||||
} else {
|
} else {
|
||||||
@@ -276,7 +330,7 @@ static void put_px(int x, int y, uint32_t color)
|
|||||||
|
|
||||||
static void put_px_blend(int x, int y, uint32_t color, int alpha)
|
static void put_px_blend(int x, int y, uint32_t color, int alpha)
|
||||||
{
|
{
|
||||||
if (x < 0 || x >= g_xres || y < 0 || y >= g_yres) return;
|
if (!clip_contains(x, y)) return;
|
||||||
uint16_t fg = rgb565(color);
|
uint16_t fg = rgb565(color);
|
||||||
if (g_bpp == 16) {
|
if (g_bpp == 16) {
|
||||||
uint16_t bg = px_read_rgb565(x, y);
|
uint16_t bg = px_read_rgb565(x, y);
|
||||||
@@ -300,6 +354,10 @@ static void put_px_blend(int x, int y, uint32_t color, int alpha)
|
|||||||
void meow_ui_clear(uint32_t color)
|
void meow_ui_clear(uint32_t color)
|
||||||
{
|
{
|
||||||
if (!g_init_ok) return;
|
if (!g_init_ok) return;
|
||||||
|
if (g_clip_n > 0) { /* 绘制期裁剪:清当前 clip 区(顶层 clip=全屏 → 走快速整刷) */
|
||||||
|
meow_ui_fill_rect(0, 0, g_xres, g_yres, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
/* 快速整刷(16bit 用半字填充,可 memset 当色值由 32bit 拼接掩码) */
|
/* 快速整刷(16bit 用半字填充,可 memset 当色值由 32bit 拼接掩码) */
|
||||||
if (g_bpp == 16) {
|
if (g_bpp == 16) {
|
||||||
uint16_t c16 = rgb565(color);
|
uint16_t c16 = rgb565(color);
|
||||||
@@ -327,6 +385,16 @@ void meow_ui_fill_rect(int x, int y, int w, int h, uint32_t color)
|
|||||||
if (x + w > g_xres) w = g_xres - x;
|
if (x + w > g_xres) w = g_xres - x;
|
||||||
if (y + h > g_yres) h = g_yres - y;
|
if (y + h > g_yres) h = g_yres - y;
|
||||||
if (w <= 0 || h <= 0) return;
|
if (w <= 0 || h <= 0) return;
|
||||||
|
if (g_clip_n > 0) { /* 绘制期裁剪:与当前 clip 求交(顶层 clip=全屏 → 无操作) */
|
||||||
|
int *c = g_clip_stack[g_clip_n - 1];
|
||||||
|
int x1 = x + w, y1 = y + h;
|
||||||
|
if (x < c[0]) x = c[0];
|
||||||
|
if (y < c[1]) y = c[1];
|
||||||
|
if (x1 > c[2]) x1 = c[2];
|
||||||
|
if (y1 > c[3]) y1 = c[3];
|
||||||
|
if (x1 <= x || y1 <= y) return;
|
||||||
|
w = x1 - x; h = y1 - y;
|
||||||
|
}
|
||||||
if (g_bpp == 16) {
|
if (g_bpp == 16) {
|
||||||
uint16_t c16 = rgb565(color);
|
uint16_t c16 = rgb565(color);
|
||||||
for (int ry = y; ry < y + h; ry++) {
|
for (int ry = y; ry < y + h; ry++) {
|
||||||
@@ -534,6 +602,16 @@ void meow_ui_fill_rect_alpha(int x, int y, int w, int h, uint32_t color, int alp
|
|||||||
if (x + w > g_xres) w = g_xres - x;
|
if (x + w > g_xres) w = g_xres - x;
|
||||||
if (y + h > g_yres) h = g_yres - y;
|
if (y + h > g_yres) h = g_yres - y;
|
||||||
if (w <= 0 || h <= 0) return;
|
if (w <= 0 || h <= 0) return;
|
||||||
|
if (g_clip_n > 0) { /* 绘制期裁剪:与当前 clip 求交(顶层 clip=全屏 → 无操作) */
|
||||||
|
int *c = g_clip_stack[g_clip_n - 1];
|
||||||
|
int x1 = x + w, y1 = y + h;
|
||||||
|
if (x < c[0]) x = c[0];
|
||||||
|
if (y < c[1]) y = c[1];
|
||||||
|
if (x1 > c[2]) x1 = c[2];
|
||||||
|
if (y1 > c[3]) y1 = c[3];
|
||||||
|
if (x1 <= x || y1 <= y) return;
|
||||||
|
w = x1 - x; h = y1 - y;
|
||||||
|
}
|
||||||
for (int ry = y; ry < y + h; ry++)
|
for (int ry = y; ry < y + h; ry++)
|
||||||
for (int rx = x; rx < x + w; rx++) put_px_blend(rx, ry, color, alpha);
|
for (int rx = x; rx < x + w; rx++) put_px_blend(rx, ry, color, alpha);
|
||||||
dirty_add(x, y, w, h);
|
dirty_add(x, y, w, h);
|
||||||
@@ -677,13 +755,28 @@ static void text_draw(int x, int y, const char *s, int size, uint32_t color)
|
|||||||
{
|
{
|
||||||
if (!g_init_ok || !s) return;
|
if (!g_init_ok || !s) return;
|
||||||
if (g_mfont_ready && g_bpp == 16) {
|
if (g_mfont_ready && g_bpp == 16) {
|
||||||
meow_font_drawtext((uint16_t *)g_fb, x, y, s, rgb565(color), size);
|
|
||||||
int w = meow_font_width_text(s, size);
|
int w = meow_font_width_text(s, size);
|
||||||
int hh = meow_font_line_height(size);
|
int hh = meow_font_line_height(size);
|
||||||
|
if (g_clip_n > 0) {
|
||||||
|
int *c = g_clip_stack[g_clip_n - 1];
|
||||||
|
if (x + w <= c[0] || x >= c[2] || y + hh <= c[1] || y >= c[3]) return; /* 与 clip 无交 */
|
||||||
|
if (!(x >= c[0] && x + w <= c[2] && y >= c[1] && y + hh <= c[3])) {
|
||||||
|
/* 部分相交:用「偏移 fb 子矩形 + 临时 fb 几何」做裁剪(meow_font 只按全屏剪)。
|
||||||
|
* 把 fb 基址指向 clip 左上角、fb_w/h 设为 clip 尺寸、原点平移到 clip 坐标,
|
||||||
|
* 这样 meow_font 的逐字形裁剪天然落在 clip 内;画完恢复全屏几何。 */
|
||||||
|
uint16_t *fb = (uint16_t *)(g_fb + (size_t)c[1] * g_line + (size_t)c[0] * 2);
|
||||||
|
meow_font_set_fb((unsigned)(c[2] - c[0]), (unsigned)(c[3] - c[1]), (unsigned)g_line);
|
||||||
|
meow_font_drawtext(fb, x - c[0], y - c[1], s, rgb565(color), size);
|
||||||
|
meow_font_set_fb((unsigned)g_xres, (unsigned)g_yres, (unsigned)g_line);
|
||||||
|
dirty_add(x, y, w, hh);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
meow_font_drawtext((uint16_t *)g_fb, x, y, s, rgb565(color), size);
|
||||||
dirty_add(x, y, w, hh);
|
dirty_add(x, y, w, hh);
|
||||||
} else {
|
} else {
|
||||||
int sc = fallback_scale(size);
|
int sc = fallback_scale(size);
|
||||||
draw_text5x7(x, y, s, sc, color);
|
draw_text5x7(x, y, s, sc, color); /* 走 put_px,自动过 clip */
|
||||||
dirty_add(x, y, (int)strlen(s) * 6 * sc, 7 * sc);
|
dirty_add(x, y, (int)strlen(s) * 6 * sc, 7 * sc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,14 @@ void meow_gfx_mark_dirty(int x, int y, int w, int h);
|
|||||||
* 解除后由调用方一次 flush 原子上屏,避免覆盖层与 App on_tick 重绘同帧两刷频闪。 */
|
* 解除后由调用方一次 flush 原子上屏,避免覆盖层与 App on_tick 重绘同帧两刷频闪。 */
|
||||||
void meow_gfx_flush_hold(int on);
|
void meow_gfx_flush_hold(int on);
|
||||||
|
|
||||||
|
/* 绘制期裁剪(MEOW_APP_SDK.md §3,2026-08-31):当前 clip 矩形内才写像素,默认全屏。
|
||||||
|
* meow_widget_draw(w) 画任何控件前 push 该控件矩形、画完 pop——任何控件(含 W_CUSTOM)
|
||||||
|
* 画不出自身边界,根治自定义画布越界脏区。可嵌套(入栈/出栈,push 与当前 clip 求交)。
|
||||||
|
* 顶层 clip=全屏,故 IME/模态(snapshot+整帧路径)经 gfx 原语直绘全屏 scrim 时不受裁剪;
|
||||||
|
* meow_gfx_flush_hold 语义不依赖 clip(flush 只在解除后原子上屏)。 */
|
||||||
|
void meow_gfx_clip_push(int x, int y, int w, int h);
|
||||||
|
void meow_gfx_clip_pop(void);
|
||||||
|
|
||||||
/* 标记整帧脏(launcher 手动脏模型桥接:自绘 raw 像素后标全屏,present 走 SDK)。 */
|
/* 标记整帧脏(launcher 手动脏模型桥接:自绘 raw 像素后标全屏,present 走 SDK)。 */
|
||||||
void meow_gfx_mark_full(void);
|
void meow_gfx_mark_full(void);
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -5,9 +5,9 @@
|
|||||||
* fopen/fprintf),重复、易错、无分离/级别。本文件实现 SDK 统一 meow_log():
|
* fopen/fprintf),重复、易错、无分离/级别。本文件实现 SDK 统一 meow_log():
|
||||||
*
|
*
|
||||||
* - app 一行调用 meow_log(MEOW_LOG_INFO, "start")(见 meow_ui.h);
|
* - app 一行调用 meow_log(MEOW_LOG_INFO, "start")(见 meow_ui.h);
|
||||||
* - SDK 启动时读 env:MEOW_LOG_DIR(日志目录)、MEOW_LOG_APP(app_id,无则 "app")、
|
* - SDK 启动时读 env:MEOW_LOG_DIR(日志目录,launcher 已注入到 logs/<app_id>,含 app 分层)、
|
||||||
* MEOW_LOG_LEVEL(本 app 级别,无则默认 INFO)→ 写
|
* MEOW_LOG_APP(app_id,无则 "app")、MEOW_LOG_LEVEL(本 app 级别,无则默认 INFO)→ 写
|
||||||
* `<log_dir>/apps/<app_id>/logs/<启动时间>_<pid>[._<n>].log`(目录自动 mkdir -p),
|
* `<log_dir>/<启动时间>_<pid>[._<n>].log`(目录自动 mkdir -p),
|
||||||
* 按 app 分目录 + 每次启动新文件(不同启动=不同文件,同一次启动内 O_APPEND 追加),
|
* 按 app 分目录 + 每次启动新文件(不同启动=不同文件,同一次启动内 O_APPEND 追加),
|
||||||
* 级别过滤(lvl < level 丢弃);
|
* 级别过滤(lvl < level 丢弃);
|
||||||
* - 格式:`<monotonic_ms> [<app_id>] <LEVEL> message\n`(meow_ui_time_ms() 取毫秒);
|
* - 格式:`<monotonic_ms> [<app_id>] <LEVEL> message\n`(meow_ui_time_ms() 取毫秒);
|
||||||
@@ -57,8 +57,8 @@ static char g_app_id[64] = "app"; /* env MEOW_LOG_APP,无则 "app" */
|
|||||||
static char g_log_dir[256] = ""; /* env MEOW_LOG_DIR,无/空则降级 stdio */
|
static char g_log_dir[256] = ""; /* env MEOW_LOG_DIR,无/空则降级 stdio */
|
||||||
static meow_log_level g_level = MEOW_LOG_INFO; /* 默认 INFO */
|
static meow_log_level g_level = MEOW_LOG_INFO; /* 默认 INFO */
|
||||||
|
|
||||||
/* M2.52:日志文件为 `<log_dir>/apps/<app_id>/logs/<ts>_<pid>[._<n>].log`。
|
/* M2.52:日志文件为 `<log_dir>/<ts>_<pid>[._<n>].log`;M2.x 简化后 MEOW_LOG_DIR 已含 app 分层
|
||||||
* g_log_base 存不含序号后缀的路径前缀 `<dir>/apps/<app>/logs/<ts>_<pid>`;
|
* (launcher 注入 logs/<app_id>)。g_log_base 存不含序号后缀的路径前缀 `<dir>/<ts>_<pid>`;
|
||||||
* g_log_seq 为轮转序号(0=首个文件无后缀;>0 追加 `_<n>`)。两者均在 log_init()
|
* g_log_seq 为轮转序号(0=首个文件无后缀;>0 追加 `_<n>`)。两者均在 log_init()
|
||||||
* (pthread_once 那次)里生成一次。 */
|
* (pthread_once 那次)里生成一次。 */
|
||||||
static char g_log_base[384] = "";
|
static char g_log_base[384] = "";
|
||||||
@@ -93,7 +93,7 @@ static void log_init(void)
|
|||||||
s = getenv("MEOW_LOG_DIR");
|
s = getenv("MEOW_LOG_DIR");
|
||||||
if (s && s[0]) snprintf(g_log_dir, sizeof g_log_dir, "%s", s);
|
if (s && s[0]) snprintf(g_log_dir, sizeof g_log_dir, "%s", s);
|
||||||
|
|
||||||
/* M2.52:构建完整前缀 `<log_dir>/apps/<app_id>/logs/<ts>_<pid>`。
|
/* M2.52:构建完整前缀 `<log_dir>/<ts>_<pid>`(M2.x 简化:MEOW_LOG_DIR 已含 app 分层)。
|
||||||
* ts 用 localtime 的 %Y%m%d_%H%M%S(启动时刻);尾拼 _<pid> 防同秒重复启动撞名,
|
* ts 用 localtime 的 %Y%m%d_%H%M%S(启动时刻);尾拼 _<pid> 防同秒重复启动撞名,
|
||||||
* 从而每次启动=新文件,同一次启动内 O_APPEND 追加。若 log_dir 为空(无卡)
|
* 从而每次启动=新文件,同一次启动内 O_APPEND 追加。若 log_dir 为空(无卡)
|
||||||
* 则跳过(log_open 降级 stderr)。 */
|
* 则跳过(log_open 降级 stderr)。 */
|
||||||
@@ -103,8 +103,8 @@ static void log_init(void)
|
|||||||
struct tm tmnow;
|
struct tm tmnow;
|
||||||
localtime_r(&now, &tmnow);
|
localtime_r(&now, &tmnow);
|
||||||
strftime(ts, sizeof ts, "%Y%m%d_%H%M%S", &tmnow);
|
strftime(ts, sizeof ts, "%Y%m%d_%H%M%S", &tmnow);
|
||||||
snprintf(g_log_base, sizeof g_log_base, "%s/apps/%s/logs/%s_%d",
|
snprintf(g_log_base, sizeof g_log_base, "%s/%s_%d",
|
||||||
g_log_dir, g_app_id, ts, (int)getpid());
|
g_log_dir, ts, (int)getpid());
|
||||||
log_rebuild_file();
|
log_rebuild_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ static int log_mkdir_p(const char *path)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* M2.52:对 `<log_dir>/apps/<app_id>/logs` 增量 mkdir -p,并打开 g_log_file
|
/* M2.52:对 `<log_dir>`(M2.x 简化后已含 app 分层)增量 mkdir -p,并打开 g_log_file
|
||||||
* (O_APPEND|O_CREAT;同一次启动内追加,不同启动=不同文件)。失败置 g_log_fallback。
|
* (O_APPEND|O_CREAT;同一次启动内追加,不同启动=不同文件)。失败置 g_log_fallback。
|
||||||
* 首次由 meow_log 经 g_log_open_tried 守卫调用;轮转后由 meow_log 再次调用(目录已存在,
|
* 首次由 meow_log 经 g_log_open_tried 守卫调用;轮转后由 meow_log 再次调用(目录已存在,
|
||||||
* mkdir_p 幂等)。仅在持有 g_log_mu 时调用(g_log_base 已构建)。 */
|
* mkdir_p 幂等)。仅在持有 g_log_mu 时调用(g_log_base 已构建)。 */
|
||||||
@@ -146,7 +146,7 @@ static void log_open(void)
|
|||||||
{
|
{
|
||||||
char logs[512];
|
char logs[512];
|
||||||
if (!g_log_dir[0] || !g_log_file[0]) { g_log_fallback = 1; return; }
|
if (!g_log_dir[0] || !g_log_file[0]) { g_log_fallback = 1; return; }
|
||||||
if (snprintf(logs, sizeof logs, "%s/apps/%s/logs", g_log_dir, g_app_id) >= (int)sizeof logs) {
|
if (snprintf(logs, sizeof logs, "%s", g_log_dir) >= (int)sizeof logs) {
|
||||||
g_log_fallback = 1; return;
|
g_log_fallback = 1; return;
|
||||||
}
|
}
|
||||||
if (log_mkdir_p(logs) != 0) { g_log_fallback = 1; return; }
|
if (log_mkdir_p(logs) != 0) { g_log_fallback = 1; return; }
|
||||||
@@ -165,7 +165,7 @@ static int log_spill_cmp(const void *a, const void *b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 每 App 总量清理:扫描 `<log_dir>/apps/<app_id>/logs/` 下所有 .log(跳过当前正写的
|
/* 每 App 总量清理:扫描 `<log_dir>/`(M2.x 简化后已含 app 分层)下所有 .log(跳过当前正写的
|
||||||
* g_log_file),累计大小,超过 MEOW_LOG_TOTAL_MAX 则按 mtime 从最旧删到 ≤ 阈值。
|
* g_log_file),累计大小,超过 MEOW_LOG_TOTAL_MAX 则按 mtime 从最旧删到 ≤ 阈值。
|
||||||
* 只规整本 App 目录(进程隔离),节流调用(见 meow_log)。仅在持有 g_log_mu 时调用。 */
|
* 只规整本 App 目录(进程隔离),节流调用(见 meow_log)。仅在持有 g_log_mu 时调用。 */
|
||||||
static void log_cleanup_old(void)
|
static void log_cleanup_old(void)
|
||||||
@@ -178,7 +178,7 @@ static void log_cleanup_old(void)
|
|||||||
long total = 0;
|
long total = 0;
|
||||||
|
|
||||||
if (!g_log_dir[0] || !g_log_file[0]) return;
|
if (!g_log_dir[0] || !g_log_file[0]) return;
|
||||||
if (snprintf(logs, sizeof logs, "%s/apps/%s/logs", g_log_dir, g_app_id)
|
if (snprintf(logs, sizeof logs, "%s", g_log_dir)
|
||||||
>= (int)sizeof logs) return;
|
>= (int)sizeof logs) return;
|
||||||
d = opendir(logs);
|
d = opendir(logs);
|
||||||
if (!d) return;
|
if (!d) return;
|
||||||
|
|||||||
@@ -40,17 +40,24 @@ static meow_view *view_map_get(int id)
|
|||||||
return (id >= 0 && id < MEOW_VIEW_BY_ID_MAX) ? g_view_by_id[id] : NULL;
|
return (id >= 0 && id < MEOW_VIEW_BY_ID_MAX) ? g_view_by_id[id] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 进入一个 view(置 ENTERED + on_enter)。 */
|
/* 进入一个 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)
|
static void view_enter(meow_view *v)
|
||||||
{
|
{
|
||||||
if (!v || (v->state & MEOW_VIEW_STATE_ENTERED)) return;
|
if (!v || (v->state & MEOW_VIEW_STATE_ENTERED)) return;
|
||||||
v->state |= MEOW_VIEW_STATE_ENTERED;
|
v->state |= MEOW_VIEW_STATE_ENTERED;
|
||||||
run_fn(v->hooks.on_enter, v);
|
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——弹回零重建)。 */
|
/* 离开一个 view(清 ENTERED + on_leave;默认不销毁子 view/widget——弹回零重建),
|
||||||
|
* **递归 children**:先子后父(demo.leave→home.leave)。 */
|
||||||
static void view_leave(meow_view *v)
|
static void view_leave(meow_view *v)
|
||||||
{
|
{
|
||||||
if (!v || !(v->state & MEOW_VIEW_STATE_ENTERED)) return;
|
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;
|
v->state &= ~MEOW_VIEW_STATE_ENTERED;
|
||||||
run_fn(v->hooks.on_leave, v);
|
run_fn(v->hooks.on_leave, v);
|
||||||
}
|
}
|
||||||
@@ -175,6 +182,36 @@ meow_view *meow_view_sub_pop(meow_view *v)
|
|||||||
return NULL; /* 预留 */
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================ */
|
/* ============================================================ */
|
||||||
/* 坐标 / 查询 */
|
/* 坐标 / 查询 */
|
||||||
/* ============================================================ */
|
/* ============================================================ */
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ void meow_view_page_push(int id); /* activity_push + 旧top on_leav
|
|||||||
void meow_view_page_pop(void); /* 旧top on_leave + activity_pop + 新top 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) */
|
void meow_view_page_replace(int id); /* 旧top on_leave + 新页 on_enter(目标页须已 create) */
|
||||||
|
|
||||||
|
/* 入口:把语义事件注入当前活动页 view 子树路由——先发子视图(按 z 降序),命中即消费,
|
||||||
|
* 再落本 view 的 on_event(其内做该 view 的 widget 命中)。返回 1=已消费(§A.7)。
|
||||||
|
* 未接线于 meow_ui_run(App 可在 on_tap/on_swipe 等回调里改用它实现 SDK 原生路由)。 */
|
||||||
|
int meow_view_dispatch_event(int ev, int a, int b);
|
||||||
|
|
||||||
/* ---- 页内子导航(预留扩展位 a;本批为空实现/桩,仅在 ACTIVITY view 内维护子栈)---- */
|
/* ---- 页内子导航(预留扩展位 a;本批为空实现/桩,仅在 ACTIVITY view 内维护子栈)---- */
|
||||||
int meow_view_sub_push(meow_view *v, meow_view *sub); /* 预留;返回 0(未实现于本批) */
|
int meow_view_sub_push(meow_view *v, meow_view *sub); /* 预留;返回 0(未实现于本批) */
|
||||||
meow_view *meow_view_sub_pop(meow_view *v); /* 预留;返回 NULL(未实现于本批) */
|
meow_view *meow_view_sub_pop(meow_view *v); /* 预留;返回 NULL(未实现于本批) */
|
||||||
|
|||||||
@@ -1204,6 +1204,10 @@ int meow_switch_get(meow_widget *w) { return w && w->type == W_SWITCH ? w->on :
|
|||||||
void meow_widget_draw(meow_widget *w)
|
void meow_widget_draw(meow_widget *w)
|
||||||
{
|
{
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
/* 绘制期裁剪(§3):把绘制 clip 设为该控件矩形,画完恢复——任何控件(含 W_CUSTOM
|
||||||
|
* 自定义画布)不可能画出自身边界,从根上杜绝越界脏区。可嵌套(W_LAYOUT/W_CARD 递归
|
||||||
|
* 子控件会再 push 子控件矩形);顶层 clip=全屏,故 IME/模态直绘不受影响。 */
|
||||||
|
meow_gfx_clip_push(w->x, w->y, w->w, w->h);
|
||||||
const meow_theme *th = meow_theme_get();
|
const meow_theme *th = meow_theme_get();
|
||||||
int disabled = widget_disabled(w);
|
int disabled = widget_disabled(w);
|
||||||
|
|
||||||
@@ -1616,4 +1620,5 @@ void meow_widget_draw(meow_widget *w)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
meow_gfx_clip_pop(); /* 恢复进入前的 clip(嵌套:回到父控件矩形/全屏) */
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -14,6 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "meow_ui.h"
|
#include "meow_ui.h"
|
||||||
#include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp:真实像素断言 */
|
#include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp:真实像素断言 */
|
||||||
|
#include "meow_ui_view.h" /* 任务② view 递归 + on_event 路由断言 */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -95,6 +96,31 @@ static void free_all(void)
|
|||||||
/* §6.3 通用长按测试回调(开闸长按判定用;事件本身经 meow_input_pop 断言)。 */
|
/* §6.3 通用长按测试回调(开闸长按判定用;事件本身经 meow_input_pop 断言)。 */
|
||||||
static void lp_recv(int x, int y) { (void)x; (void)y; }
|
static void lp_recv(int x, int y) { (void)x; (void)y; }
|
||||||
|
|
||||||
|
/* ---- 任务① 绘制期 clip:W_CUSTOM 越界绘制被裁掉(render 回调经 flag 切换越界)----
|
||||||
|
* 控件几何硬编码为 (40,40,80,40)(与下方创建一致):meow_widget 是对外不透明类型,
|
||||||
|
* 测试经 meow_widget_custom_create(40,40,80,40,...) 建在同址,回调按常量绘制。 */
|
||||||
|
static int g_custom_oob = 1; /* 1=画越界实心块+越界文本;0=只画控件内 */
|
||||||
|
static void clip_oob_render(meow_widget *w, const meow_theme *th, void *ctx)
|
||||||
|
{
|
||||||
|
(void)w; (void)th; (void)ctx;
|
||||||
|
meow_ui_fill_rect(45, 45, 20, 20, 0xFF0000); /* 控件 (40,40,80,40) 内:必画红块 */
|
||||||
|
if (g_custom_oob) {
|
||||||
|
/* 越界(右/下,出控件矩形 [40..120)×[40..80)):应被 widget clip 裁掉 */
|
||||||
|
meow_ui_fill_rect(140, 40, 60, 40, 0xFF0000);
|
||||||
|
meow_ui_text(160, 100, "OOB", 20, 0xFF0000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 任务② view 递归 enter/leave + on_event 子>父:trace 顺序 + 命中计数 ---- */
|
||||||
|
static int g_vtrace[16]; static int g_vtrace_n = 0;
|
||||||
|
static void vtrace_enter(meow_view *v, void *ctx) { (void)ctx; g_vtrace[g_vtrace_n++] = v->id; }
|
||||||
|
static void vtrace_leave(meow_view *v, void *ctx) { (void)ctx; g_vtrace[g_vtrace_n++] = -v->id; }
|
||||||
|
static int g_ev_child_hits = 0, g_ev_parent_hits = 0;
|
||||||
|
static int vev_child(meow_view *v, int ev, int a, int b, void *ctx)
|
||||||
|
{ (void)v; (void)ctx; if (ev == 7 && a == 10 && b == 10) { g_ev_child_hits++; return 1; } return 0; }
|
||||||
|
static int vev_parent(meow_view *v, int ev, int a, int b, void *ctx)
|
||||||
|
{ (void)v; (void)ctx; if (ev == 7) { g_ev_parent_hits++; return 1; } return 0; }
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
setenv("MEOW_UI_DUMP", "/tmp/b2.ppm", 1);
|
setenv("MEOW_UI_DUMP", "/tmp/b2.ppm", 1);
|
||||||
@@ -361,6 +387,52 @@ int main(void)
|
|||||||
printf("PASS long-press: trigger/suppress-tap/short-tap/cancel-move/configurable/opt-in\n");
|
printf("PASS long-press: trigger/suppress-tap/short-tap/cancel-move/configurable/opt-in\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- 任务① 绘制期 clip:W_CUSTOM 越界绘制被裁掉 ---------- */
|
||||||
|
{
|
||||||
|
const meow_theme *thc = meow_theme_get();
|
||||||
|
int bg_r, bg_g, bg_b; UNPK(thc->bg, bg_r, bg_g, bg_b);
|
||||||
|
meow_widget *cw = W(meow_widget_custom_create(40, 40, 80, 40, clip_oob_render, NULL, NULL));
|
||||||
|
/* a) clip 生效:经 meow_widget_draw(push 控件矩形)→ 越界块/文本被裁掉 */
|
||||||
|
g_custom_oob = 1;
|
||||||
|
meow_ui_clear(thc->bg);
|
||||||
|
meow_widget_draw(cw);
|
||||||
|
assert(region_has(45, 45, 20, 20, 0xFF, 0x00, 0x00) > 30); /* 控件内红块画出 */
|
||||||
|
assert(px_near(40 + 80 + 25, 40 + 10, bg_r, bg_g, bg_b)); /* 越界实心块被裁 → bg */
|
||||||
|
assert(px_near(40 + 80 + 45, 40 + 65, bg_r, bg_g, bg_b)); /* 越界文本被裁 → bg */
|
||||||
|
/* b) 对照:无 clip 直绘同尺寸块会画出(证明是 clip 在裁) */
|
||||||
|
meow_ui_clear(thc->bg);
|
||||||
|
meow_ui_fill_rect(40 + 80 + 20, 40, 60, 40, 0x00FF00);
|
||||||
|
assert(region_has(40 + 80 + 20, 40, 60, 40, 0x00, 0xFF, 0x00) > 100);
|
||||||
|
printf("PASS clip: W_CUSTOM out-of-bounds fill+text clipped by widget clip\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 任务② view 递归 enter/leave + on_event 子>父命中 ---------- */
|
||||||
|
{
|
||||||
|
static const meow_view_hooks ph = { .on_enter = vtrace_enter, .on_leave = vtrace_leave,
|
||||||
|
.on_event = vev_parent };
|
||||||
|
static const meow_view_hooks ch = { .on_enter = vtrace_enter, .on_leave = vtrace_leave,
|
||||||
|
.on_event = vev_child };
|
||||||
|
/* 页 id 须 < MEOW_VIEW_BY_ID_MAX(32) 才能进 g_view_by_id(page_push 查找) */
|
||||||
|
meow_view *pv = meow_view_create(3, MEOW_VIEW_ACTIVITY, &ph, NULL);
|
||||||
|
meow_view *cv = meow_view_create(4, MEOW_VIEW_CONTAINER, &ch, NULL);
|
||||||
|
assert(meow_view_add_child(pv, cv) == 0);
|
||||||
|
g_vtrace_n = 0; g_ev_child_hits = 0; g_ev_parent_hits = 0;
|
||||||
|
meow_view_page_push(3); /* 进入 pv → 递归进 cv:pv.on_enter→cv.on_enter */
|
||||||
|
assert(g_vtrace_n == 2 && g_vtrace[0] == 3 && g_vtrace[1] == 4);
|
||||||
|
/* 命中 (10,10):先发子(按 z 降序),子命中即消费,父不再收 */
|
||||||
|
assert(meow_view_dispatch_event(7, 10, 10) == 1);
|
||||||
|
assert(g_ev_child_hits == 1 && g_ev_parent_hits == 0);
|
||||||
|
/* 命中 (20,20):子不命中 → 落父 on_event */
|
||||||
|
assert(meow_view_dispatch_event(7, 20, 20) == 1);
|
||||||
|
assert(g_ev_child_hits == 1 && g_ev_parent_hits == 1);
|
||||||
|
/* 离开 pv → 递归离开:cv.on_leave(-4) → pv.on_leave(-3) */
|
||||||
|
g_vtrace_n = 0;
|
||||||
|
meow_view_page_pop();
|
||||||
|
assert(g_vtrace_n == 2 && g_vtrace[0] == -4 && g_vtrace[1] == -3);
|
||||||
|
meow_view_free(pv); /* 递归释放 cv */
|
||||||
|
printf("PASS view: recursive enter/leave order + on_event child>parent hit\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- 绘制所有控件到 back_buf(5x7 兜底),flush 出屏幕 ---------- */
|
/* ---------- 绘制所有控件到 back_buf(5x7 兜底),flush 出屏幕 ---------- */
|
||||||
const meow_theme *th = meow_theme_get();
|
const meow_theme *th = meow_theme_get();
|
||||||
meow_ui_clear(th->bg);
|
meow_ui_clear(th->bg);
|
||||||
|
|||||||
Reference in new issue
Block a user