diff --git a/patch_v2/hello_app/hello b/patch_v2/hello_app/hello index c63acbc..d07d3df 100755 Binary files a/patch_v2/hello_app/hello and b/patch_v2/hello_app/hello differ diff --git a/patch_v2/hello_app/hello.c b/patch_v2/hello_app/hello.c index 4d4e87c..aa02370 100644 --- a/patch_v2/hello_app/hello.c +++ b/patch_v2/hello_app/hello.c @@ -140,6 +140,31 @@ static meow_widget *g_dlc; static rect_t r_list; /* LazyColumn */ static meow_dialog *g_dlg; static meow_sheet *g_sheet; +/* ---- demo 分区标题/slider 值文本:注册为 CUSTOM 绘制物(§6.2 区域重画会重画它们)---- + * 显式 z=D_TITLE_Z:> 一切自动 z(=127)、< chrome 置顶(999),故「内容→标题→slider值→chrome」。 + * 非控件直绘必须注册,否则 meow_ui_invalidate 清底后会漏画(非保留模型固有代价)。 */ +#define D_TITLE_Z 128 +static int g_dtitle_cid[D_TITLE_MAX]; /* 分区标题绘制物 id(rebuild 时注销/重注册) */ +static int g_slider_cid = 0; /* slider 值文本绘制物 id */ + +/* CUSTOM 绘制回调:分区标题(ctx=title 下标,指向 g_dtitle[i]) */ +static void demo_draw_title(void *ctx) +{ + int i = (int)(intptr_t)ctx; + if (i < 0 || i >= g_dtitle_n) return; + const meow_theme *th = meow_theme_get(); + meow_ui_text(g_dtitle[i].x, g_dtitle[i].y, g_dtitle[i].s, 20, th->primary); +} +/* CUSTOM 绘制回调:slider 当前值文本(bbox=r_slider_txt) */ +static void demo_draw_slider_val(void *ctx) +{ + (void)ctx; + if (!g_dslider) return; + const meow_theme *th = meow_theme_get(); + char buf[28]; snprintf(buf, sizeof buf, "slider = %d", meow_slider_get(g_dslider)); + meow_ui_text(r_slider_txt.x, r_slider_txt.y, buf, 20, th->dim); +} + /* 前向声明 */ static void redraw(void); static void demo_rebuild_to(int scroll); @@ -218,8 +243,16 @@ static void d_slot(meow_widget *w, rect_t r) } static void d_title(int x, int y, const char *s) { - if (g_dtitle_n < D_TITLE_MAX) { g_dtitle[g_dtitle_n].x = x; g_dtitle[g_dtitle_n].y = y; - g_dtitle[g_dtitle_n].s = s; g_dtitle_n++; } + if (g_dtitle_n < D_TITLE_MAX) { + int i = g_dtitle_n; + g_dtitle[i].x = x; g_dtitle[i].y = y; g_dtitle[i].s = s; + g_dtitle_n++; + /* 注册为 CUSTOM 绘制物(bbox=文本绘制范围;cb 读 g_dtitle[i]) */ + int tw = meow_ui_text_width(s, 20); + int th = meow_ui_text_height(20); + g_dtitle_cid[i] = meow_draw_custom_register(D_TITLE_Z, x, y, tw, th, + demo_draw_title, (void *)(intptr_t)i); + } } /* LazyColumn 数据源(100 项虚拟化) */ @@ -231,12 +264,15 @@ static int demo_list_src(int idx, char *label, int len, void *ctx) return 1; } -/* 释放当前全部内容控件(含 Card 的独立子按钮);chrome 不在其列。 */ +/* 释放当前全部内容控件(含 Card 的独立子按钮)与 CUSTOM 绘制物;chrome 不在其列。 */ static void demo_free_content(void) { for (int i = 0; i < g_dslot_n; i++) meow_widget_free(g_dslot[i].w); g_dslot_n = 0; if (g_dcard_btn) { meow_widget_free(g_dcard_btn); g_dcard_btn = NULL; } + for (int i = 0; i < g_dtitle_n; i++) + if (g_dtitle_cid[i] > 0) { meow_draw_unregister(g_dtitle_cid[i]); g_dtitle_cid[i] = 0; } + if (g_slider_cid > 0) { meow_draw_unregister(g_slider_cid); g_slider_cid = 0; } g_dtitle_n = 0; } @@ -272,8 +308,11 @@ static void demo_build_at(int scroll) d_title(DCL_X, SY(yb), "INPUT & STATE"); yb += 26; r_dsw = RX(DCL_X, SY(yb), 56, 32); g_dsw = meow_switch_create(r_dsw.x, r_dsw.y, g_ds.sw_on); SLOT(g_dsw, r_dsw.x, r_dsw.y, r_dsw.w, r_dsw.h); yb += 44; - r_slider_txt = RX(DCL_X, SY(yb), 100, 24); yb += 24; + r_slider_txt = RX(DCL_X, SY(yb), 160, 24); yb += 24; /* 加宽到覆盖值文本全宽(防局部刷漏尾) */ r_slider = RX(DCL_X, SY(yb), DFW, 40); g_dslider = meow_slider_create(r_slider.x, r_slider.y, r_slider.w, 0, 100, g_ds.slider); SLOT(g_dslider, r_slider.x, r_slider.y, r_slider.w, r_slider.h); yb += 48; + g_slider_cid = meow_draw_custom_register(D_TITLE_Z, r_slider_txt.x, r_slider_txt.y, + r_slider_txt.w, r_slider_txt.h, + demo_draw_slider_val, NULL); r_dtf = RX(DCL_X, SY(yb), DFW, 44); g_dtf = meow_textfield_create(r_dtf.x, r_dtf.y, r_dtf.w, r_dtf.h, "Type here...", g_ds.tf, 32, MEOW_IME_ASCII); SLOT(g_dtf, r_dtf.x, r_dtf.y, r_dtf.w, r_dtf.h); yb += 54; @@ -332,27 +371,14 @@ static void demo_build_at(int scroll) #undef SLOT } -/* demo 全页重绘:背景 + 内容控件(已定位) + 标题 + slider 值 + chrome(后画覆盖) + 内容区 flush */ +/* demo 全页重绘:整帧按 z 序重画全部(set_page_bg 清底 + 注册表按 z 升序)。 + * 此前手工遍历 g_dslot/g_dtitle/后画 chrome 已删;现在自动 z 排:内容(0..127)→标题/slider值(128) + * →chrome(999 置顶),与旧绘制序一致,但「清底+重画」由 SDK 统一按 z 保证(P1 修法下沉)。 */ static void demo_paint(void) { const meow_theme *th = meow_theme_get(); - int sb = meow_ui_statusbar_height(); - - meow_ui_fill_rect(0, sb, 480, 720 - sb, th->bg); - - for (int i = 0; i < g_dslot_n; i++) meow_widget_draw(g_dslot[i].w); - for (int i = 0; i < g_dtitle_n; i++) - meow_ui_text(g_dtitle[i].x, g_dtitle[i].y, g_dtitle[i].s, 20, th->primary); - if (g_dslider) { - char buf[28]; snprintf(buf, sizeof buf, "slider = %d", meow_slider_get(g_dslider)); - meow_ui_text(r_slider_txt.x, r_slider_txt.y, buf, 20, th->dim); - } - /* chrome(固定;后画覆盖内容溢出区) */ - meow_widget_draw(g_tb); - meow_widget_draw(g_nav); - meow_widget_draw(g_fab); - - meow_ui_flush_region(0, sb, 480, 720 - sb); + meow_ui_set_page_bg(th->bg); /* 页面底 = 主题 bg(区域/整帧重画清底用) */ + meow_ui_paint_full(); /* 整帧按 z 序重画 */ } /* 重建内容到指定 scroll(clamp 到 [0, max_scroll]);进入/滚动共用 */ @@ -369,51 +395,14 @@ static void demo_rebuild_to(int scroll) demo_paint(); } -/* 两矩形是否相交(P1:局部重绘时判定哪些控件落在该区) */ -static int d_rect_overlap(rect_t a, rect_t b) -{ - return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y; -} - -/* 局部刷新一个区域:清底 + 按 demo_paint 同序重画「落在该区的所有控件/文本/chrome」,再 flush。 - * 根治 P1:旧 d_redraw 只清底 + 重画单个控件,会把重叠区里上层控件(chrome 如 FAB/导航栏) - * 一并清掉、只刷单个控件矩形 → 下层动态控件(如圆形进度条实时重绘)盖住/抹掉上层浮层。 - * 修法:局部刷新=重画该区内完整 z 栈(内容→标题→slider 值→chrome,与整帧 paint 一致), - * 下层动态重绘不再抹掉上层。仍是局部(只画相交控件,不整帧、不冲状态栏)。 */ -static void d_paint_region(rect_t r) -{ - if (r.w <= 0 || r.h <= 0) return; - const meow_theme *th = meow_theme_get(); - meow_ui_fill_rect(r.x, r.y, r.w, r.h, th->bg); - for (int i = 0; i < g_dslot_n; i++) - if (d_rect_overlap(g_dslot[i].r, r)) meow_widget_draw(g_dslot[i].w); - for (int i = 0; i < g_dtitle_n; i++) - if (g_dtitle[i].y < r.y + r.h && g_dtitle[i].y + meow_ui_text_height(20) > r.y && - g_dtitle[i].x < r.x + r.w && g_dtitle[i].x + meow_ui_text_width(g_dtitle[i].s, 20) > r.x) - meow_ui_text(g_dtitle[i].x, g_dtitle[i].y, g_dtitle[i].s, 20, th->primary); - if (g_dslider && d_rect_overlap(r_slider_txt, r)) { - char buf[28]; snprintf(buf, sizeof buf, "slider = %d", meow_slider_get(g_dslider)); - meow_ui_text(r_slider_txt.x, r_slider_txt.y, buf, 20, th->dim); - } - /* chrome(固定;后画保证覆盖内容区溢出) */ - if (d_rect_overlap(r_tb, r)) meow_widget_draw(g_tb); - if (d_rect_overlap(r_nav, r)) meow_widget_draw(g_nav); - if (d_rect_overlap(r_fab, r)) meow_widget_draw(g_fab); - meow_ui_flush_region(r.x, r.y, r.w, r.h); -} - -/* 局部刷新一个控件:整区清底 + 重画重叠 z 栈(w 仅用于取矩形;重画逻辑见 d_paint_region) */ -static void d_redraw(meow_widget *w, rect_t r) -{ - (void)w; - d_paint_region(r); -} - +/* 局部刷新统一走 SDK meow_ui_invalidate(清底 + 按 z 重画相交绘制物 + 上屏); + * 旧 d_paint_region/d_redraw/d_rect_overlap(App 层 z 栈重画)已删除、下沉到 SDK(§6.2)。 + * 局部刷新一控件:整区清底 + 重画重叠 z 栈由注册表完成。 */ static void d_toggle_sel(meow_widget *w, rect_t r) { if (!w) return; meow_widget_set_selected(w, !meow_widget_selected(w)); - d_redraw(w, r); + meow_ui_invalidate(r.x, r.y, r.w, r.h); } /* Tap:落点在内容控件上,返回 1=已消费 */ @@ -423,29 +412,29 @@ static int demo_content_tap(int x, int y) for (int i = 0; i < 5; i++) if (meow_widget_hit(g_dbtn_b[i], x, y)) { d_toggle_sel(g_dbtn_b[i], r_btn[i]); g_ds.btn[i] = meow_widget_selected(g_dbtn_b[i]); return 1; } - if (meow_widget_hit(g_dr1, x, y)) { meow_widget_set_selected(g_dr1, 1); meow_widget_set_selected(g_dr2, 0); g_ds.r1 = 1; g_ds.r2 = 0; d_redraw(g_dr1, r_dr1); d_redraw(g_dr2, r_dr2); return 1; } - if (meow_widget_hit(g_dr2, x, y)) { meow_widget_set_selected(g_dr2, 1); meow_widget_set_selected(g_dr1, 0); g_ds.r2 = 1; g_ds.r1 = 0; d_redraw(g_dr1, r_dr1); d_redraw(g_dr2, r_dr2); return 1; } + if (meow_widget_hit(g_dr1, x, y)) { meow_widget_set_selected(g_dr1, 1); meow_widget_set_selected(g_dr2, 0); g_ds.r1 = 1; g_ds.r2 = 0; meow_ui_invalidate(r_dr1.x, r_dr1.y, r_dr1.w, r_dr1.h); meow_ui_invalidate(r_dr2.x, r_dr2.y, r_dr2.w, r_dr2.h); return 1; } + if (meow_widget_hit(g_dr2, x, y)) { meow_widget_set_selected(g_dr2, 1); meow_widget_set_selected(g_dr1, 0); g_ds.r2 = 1; g_ds.r1 = 0; meow_ui_invalidate(r_dr1.x, r_dr1.y, r_dr1.w, r_dr1.h); meow_ui_invalidate(r_dr2.x, r_dr2.y, r_dr2.w, r_dr2.h); return 1; } if (meow_widget_hit(g_dcb, x, y)) { d_toggle_sel(g_dcb, r_dcb); g_ds.ck = meow_widget_selected(g_dcb); return 1; } - if (meow_widget_hit(g_dsw, x, y)) { g_ds.sw_on = !meow_switch_get(g_dsw); meow_switch_set(g_dsw, g_ds.sw_on); d_redraw(g_dsw, r_dsw); return 1; } + if (meow_widget_hit(g_dsw, x, y)) { g_ds.sw_on = !meow_switch_get(g_dsw); meow_switch_set(g_dsw, g_ds.sw_on); meow_ui_invalidate(r_dsw.x, r_dsw.y, r_dsw.w, r_dsw.h); return 1; } if (meow_widget_hit(g_dslider, x, y)) { demo_slider_drag(x); return 1; } if (meow_widget_hit(g_dtf, x, y)) { meow_textfield_activate(g_dtf); /* 阻塞 IME,返回后刷新 + 状态回写 */ strncpy(g_ds.tf, meow_textfield_text(g_dtf), sizeof g_ds.tf - 1); g_ds.tf[sizeof g_ds.tf - 1] = 0; - d_redraw(g_dtf, r_dtf); + meow_ui_invalidate(r_dtf.x, r_dtf.y, r_dtf.w, r_dtf.h); return 1; } if (meow_widget_hit(g_dcard_btn, x, y)) { d_toggle_sel(g_dcard_btn, r_cardbtn); g_ds.card = meow_widget_selected(g_dcard_btn); return 1; } if (g_dtab) { int t = meow_tabbar_hit(g_dtab, x, y); - if (t >= 0) { g_ds.tab = t; meow_tabbar_set_selected(g_dtab, t); d_redraw(g_dtab, r_tab); return 1; } + if (t >= 0) { g_ds.tab = t; meow_tabbar_set_selected(g_dtab, t); meow_ui_invalidate(r_tab.x, r_tab.y, r_tab.w, r_tab.h); return 1; } } - if (meow_widget_hit(g_dbtn_dlg, x, y)) { meow_dialog_show(g_dlg); meow_log(MEOW_LOG_INFO, "dialog result=%d", meow_dialog_result(g_dlg)); d_redraw(g_dbtn_dlg, r_dlg); return 1; } - if (meow_widget_hit(g_dbtn_sheet, x, y)) { int r = meow_sheet_open(g_sheet); meow_log(MEOW_LOG_INFO, "sheet result=%d", r); d_redraw(g_dbtn_sheet, r_sheet); return 1; } + if (meow_widget_hit(g_dbtn_dlg, x, y)) { meow_dialog_show(g_dlg); meow_log(MEOW_LOG_INFO, "dialog result=%d", meow_dialog_result(g_dlg)); meow_ui_invalidate(r_dlg.x, r_dlg.y, r_dlg.w, r_dlg.h); return 1; } + if (meow_widget_hit(g_dbtn_sheet, x, y)) { int r = meow_sheet_open(g_sheet); meow_log(MEOW_LOG_INFO, "sheet result=%d", r); meow_ui_invalidate(r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h); return 1; } if (meow_widget_hit(g_dlc, x, y)) { g_demo_focus = 1; int li = meow_list_tap_at(g_dlc, x, y); /* M2.71-bug8:命中行→置选中并高亮 */ - if (li >= 0) { g_ds.list_sel = li; d_redraw(g_dlc, r_list); } + if (li >= 0) { g_ds.list_sel = li; meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h); } return 1; } return 0; @@ -463,12 +452,13 @@ static void demo_on_tap(int x, int y) char lb[16]; snprintf(lb, sizeof lb, "+%d", g_demo_fab); meow_widget_free(g_fab); g_fab = meow_fab_create(r_fab.x, r_fab.y, 56, lb); - d_paint_region(r_fab); /* 重画 FAB 及其下重叠内容(z 序一致,防抹掉下层) */ + meow_widget_set_z(g_fab, 999); /* 重建后重挂顶:新 entry 自动 z 低,须显式置顶 */ + meow_ui_invalidate(r_fab.x, r_fab.y, r_fab.w, r_fab.h); /* 重画 FAB 及其下重叠 z 栈 */ return; } if (g_nav) { int idx = meow_navigationbar_hit(g_nav, x, y); - if (idx >= 0) { meow_navigationbar_set_selected(g_nav, idx); d_redraw(g_nav, r_nav); return; } + if (idx >= 0) { meow_navigationbar_set_selected(g_nav, idx); meow_ui_invalidate(r_nav.x, r_nav.y, r_nav.w, r_nav.h); return; } } if (demo_content_tap(x, y)) return; } @@ -481,8 +471,8 @@ static void demo_tick(uint64_t now) g_demo_prog_last = now; g_ds.prog = (g_ds.prog + 3) % 101; g_demo_prog = g_ds.prog; - meow_progress_set(g_dprl, g_demo_prog); d_redraw(g_dprl, r_prl); - meow_progress_set(g_dprc, g_demo_prog); d_redraw(g_dprc, r_prc); + meow_progress_set(g_dprl, g_demo_prog); meow_ui_invalidate(r_prl.x, r_prl.y, r_prl.w, r_prl.h); + meow_progress_set(g_dprc, g_demo_prog); meow_ui_invalidate(r_prc.x, r_prc.y, r_prc.w, r_prc.h); } /* 按触碰 X 落 slider 值并局部刷(按下即定位 + 拖动实时,M2.71-bug2) */ @@ -491,8 +481,8 @@ static void demo_slider_drag(int x) int frac = x - r_slider.x; if (frac < 0) frac = 0; if (frac > r_slider.w) frac = r_slider.w; g_ds.slider = frac * 100 / r_slider.w; meow_slider_set(g_dslider, g_ds.slider); - d_redraw(g_dslider, r_slider); - d_paint_region(r_slider_txt); /* 重画 slider 值文本(z 序一致) */ + meow_ui_invalidate(r_slider.x, r_slider.y, r_slider.w, r_slider.h); + meow_ui_invalidate(r_slider_txt.x, r_slider_txt.y, r_slider_txt.w, r_slider_txt.h); /* 重画值文本 */ } /* demo 页 on_touch_down:锁一种拖拽(slider > list > 页内容)。非 demo 态跳过。 @@ -522,7 +512,7 @@ static void demo_touch_move(int x, int y) if (g_drag_slider) { demo_slider_drag(x); return; } if (g_drag_list) { int dy = y - g_drag_list_last_y; g_drag_list_last_y = y; - if (dy) { meow_list_scroll_by(g_dlc, -dy); g_ds.list = meow_list_scroll(g_dlc); d_redraw(g_dlc, r_list); } + if (dy) { meow_list_scroll_by(g_dlc, -dy); g_ds.list = meow_list_scroll(g_dlc); meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h); } return; } if (g_drag_page) { @@ -546,7 +536,7 @@ static void demo_swipe(int dir, int dist) if (g_demo_focus && g_dlc) { meow_list_scroll_by(g_dlc, del); g_ds.list = meow_list_scroll(g_dlc); - d_redraw(g_dlc, r_list); /* 列表区局部刷新 */ + meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h); /* 列表区局部刷新 */ } else { demo_rebuild_to(g_demo_scroll + del); } @@ -575,6 +565,10 @@ static void demo_init(void) meow_navigationbar_add_item(g_nav, "More"); meow_navigationbar_set_selected(g_nav, 0); r_fab = RX(406, 616, 56, 56); g_fab = meow_fab_create(406, 616, 56, "+"); + /* chrome 显式置顶(§6.2 置顶):> 一切自动 z(127),区域重画/整帧都最后画 chrome。 */ + meow_widget_set_z(g_tb, 999); + meow_widget_set_z(g_nav, 999); + meow_widget_set_z(g_fab, 999); demo_build_at(0); meow_ui_activity_pop(); g_demo_scroll = 0; diff --git a/patch_v2/sdk/build_sdk.sh b/patch_v2/sdk/build_sdk.sh index 9994d4d..b7440e4 100755 --- a/patch_v2/sdk/build_sdk.sh +++ b/patch_v2/sdk/build_sdk.sh @@ -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 ==" diff --git a/patch_v2/sdk/meow_ui.h b/patch_v2/sdk/meow_ui.h index 4c3da44..c1df222 100644 --- a/patch_v2/sdk/meow_ui.h +++ b/patch_v2/sdk/meow_ui.h @@ -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 系统级组件) */ /* ============================================================ */ diff --git a/patch_v2/sdk/meow_ui_draw.c b/patch_v2/sdk/meow_ui_draw.c new file mode 100644 index 0000000..fc25145 --- /dev/null +++ b/patch_v2/sdk/meow_ui_draw.c @@ -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 +#include + +#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); +} diff --git a/patch_v2/sdk/meow_ui_internal.h b/patch_v2/sdk/meow_ui_internal.h index 62dbf3c..bc3c89b 100644 --- a/patch_v2/sdk/meow_ui_internal.h +++ b/patch_v2/sdk/meow_ui_internal.h @@ -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); diff --git a/patch_v2/sdk/meow_ui_view.c b/patch_v2/sdk/meow_ui_view.c new file mode 100644 index 0000000..9ab349d --- /dev/null +++ b/patch_v2/sdk/meow_ui_view.c @@ -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 +#include + +#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; } diff --git a/patch_v2/sdk/meow_ui_view.h b/patch_v2/sdk/meow_ui_view.h new file mode 100644 index 0000000..0b221b9 --- /dev/null +++ b/patch_v2/sdk/meow_ui_view.h @@ -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 */ diff --git a/patch_v2/sdk/meow_ui_widget.c b/patch_v2/sdk/meow_ui_widget.c index c6a473f..0881ae8 100644 --- a/patch_v2/sdk/meow_ui_widget.c +++ b/patch_v2/sdk/meow_ui_widget.c @@ -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) diff --git a/patch_v2/sdk/test_ime_fix.c b/patch_v2/sdk/test_ime_fix.c index ba170aa..74f5891 100644 --- a/patch_v2/sdk/test_ime_fix.c +++ b/patch_v2/sdk/test_ime_fix.c @@ -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" diff --git a/patch_v2/sdk/test_meow_ui_widgets.c b/patch_v2/sdk/test_meow_ui_widgets.c index 4b1b447..18d4c59 100644 --- a/patch_v2/sdk/test_meow_ui_widgets.c +++ b/patch_v2/sdk/test_meow_ui_widgets.c @@ -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 屏幕) */ diff --git a/patch_v2/sdk/test_p1p2p3_repro.c b/patch_v2/sdk/test_p1p2p3_repro.c index eaefad4..f8e89f2 100644 --- a/patch_v2/sdk/test_p1p2p3_repro.c +++ b/patch_v2/sdk/test_p1p2p3_repro.c @@ -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_); diff --git a/patch_v2/sdk/test_sdk_fixes.c b/patch_v2/sdk/test_sdk_fixes.c index e2a3401..311396c 100644 --- a/patch_v2/sdk/test_sdk_fixes.c +++ b/patch_v2/sdk/test_sdk_fixes.c @@ -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"