/* ui/demo/demo.c — 控件库 Demo 演示页(ACT_DEMO)view + 控制器(批次③ 目录嵌套后)。 * * 内容控件以绝对坐标创建,页滚动 = 按「base_y - scroll」重建内容(owner=ACT_DEMO 不变); * 各控件跨重建状态存 g_ds(App 持有),重建后恢复。绘制走批次① z 序注册表:demo_paint 调 * meow_ui_set_page_bg + meow_ui_paint_full(整帧按 z 重画:内容→标题/slider值→chrome 置顶), * 局部刷新统一 meow_ui_invalidate(清底+按 z 重画相交绘制物)。旧 d_paint_region/d_redraw/ * d_rect_overlap(App 层 z 栈重画)已删、下沉 SDK。 * * 批次②③ 拆分/深度化(SDK_VIEW_FRAMEWORK_DESIGN §B.2/§B.4/§E③): * - 列表区逻辑区块抽成 CONTAINER 子视图 ui/demo/list/list.c(挂到本页 ACTIVITY view children), * 列表视图态(g_dlc/r_list/g_demo_focus/g_drag_list)随迁;本页只做页级分发(子>父)。 * - 进度语义值 g_ds.prog 已上收 logic/state.c(logic_prog_get/set,经监听器)。 * - 设计定案 B:其余视图态(demo 页 g_ds 非列表字段/g_demo_scroll/drag_page)留本文件(不迁 logic); * UI→业务只经 logic_* / notify_*。页面进入/离开经 meow_view_page_push/pop 驱动。 */ #include "hello_internal.h" #include "meow_ui_media.h" /* ② demo 页按钮上报媒体 META + 收 PLAYCTL */ #include #include #include /* 供 demo_content_tap / touch 回调用的前向声明 */ static void demo_slider_drag(int x); static void demo_media_send(void); static demo_ctrl g_demo_ctrl; /* ---- demo 演示页状态 ---- */ int g_demo = 0; /* 1=处于 demo 演示页 */ int g_demo_scroll = 0; /* 内容区滚动偏移(base 空间 px) */ static int g_demo_fab = 0; /* FAB 点击计数反馈 */ static uint64_t g_demo_prog_last = 0; static int g_demo_ct = 0; /* 内容视口顶(屏y = statusbar+56+8) */ static int g_demo_cb = 0; /* 内容视口底(屏y =608,NavBar 上方留白) */ static int g_demo_content_h = 0;/* 内容总高(base 空间;决定 max_scroll) */ /* 注:g_demo_focus/g_drag_list/g_dlc/r_list 已迁 ui/demo/list/list.c(列表视图态归列表区 * CONTAINER 子视图),本文件仅经 hello_internal.h 的 extern 引用;g_demo_prog 已上收 logic/state.c。 */ /* M2.71-bug2/6:拖动过程(跟手)状态。on_touch_down 锁一种拖拽,on_touch_move 实时更新。 * g_drag_list(列表拖拽)/g_drag_list_last_y 归列表区 CONTAINER 子视图 list.c(extern 引用)。 * 注:g_drag_page 仍为本页「页内容拖拽」标志。 */ static int g_drag_slider = 0; /* 手指按在 slider */ int g_drag_page = 0; /* 手指按在页内容(非 slider/list):拖拽滚页面 */ static int g_drag_page_last_y = 0; /* 页面拖拽上一帧 y */ static int g_drag_page_delta = 0; /* 页面拖拽累计位移(节流重建,≥8px 才重建一次) */ static uint64_t g_drag_page_tlast = 0; /* 页面拖拽上次重建时刻(359:每 poll 合并一次 rebuild) */ /* SDK 控件不透明:App 自记几何,用于局部 flush / 命中 / scroll 重建。 */ /* demo 各控件跨重建保留的状态(app 持有;重建后按此恢复)—— * 列表态 list/list_sel 已迁 ui/demo/list/list.c(列表区 CONTAINER 子视图)持有; * 进度语义值 prog 已上收 logic/state.c(logic_prog_*)。 */ static struct { int sw_on; int slider; char tf[32]; char tf_py[32]; int ck; int r1, r2; int btn[5]; int card; int tab; } g_ds = { .sw_on = 1, .slider = 40, .r1 = 1, .tab = 1 }; /* demo 内容布局常量(base 空间) */ #define DCL_X 16 #define DCR_X 252 #define DCW 212 #define DFW 448 #define DBH 44 #define DCH 52 /* 一行两列按钮步进 */ /* demo 内容槽:控件 + 其当前屏幕几何(rebuild 时重算) */ #define D_SLOT_MAX 64 static struct { meow_widget *w; rect_t r; } g_dslot[D_SLOT_MAX]; static int g_dslot_n = 0; /* demo 分区小标题(纯文本,非控件;屏幕坐标在 rebuild 时算好) */ #define D_TITLE_MAX 16 static struct { int x, y; const char *s; } g_dtitle[D_TITLE_MAX]; static int g_dtitle_n = 0; /* demo 交互组件引用(rebuild 时 g_* 与 r_* 同步重指/recompute;chrome 在 ui/common/chrome.c) */ static meow_widget *g_dbtn_b[5]; static rect_t r_btn[5]; /* Button 五型 */ static meow_widget *g_dbtn_d; /* DISABLED 按钮(禁用不命中,无需 rect) */ static meow_widget *g_dsw; static rect_t r_dsw; /* Switch */ static meow_widget *g_dslider; static rect_t r_slider, r_slider_txt; /* Slider + 值文本 */ static meow_widget *g_dtf; static rect_t r_dtf; /* TextField (英文) */ static meow_widget *g_dtf_py; static rect_t r_dtf_py; /* TextField (拼音 IME 演示) */ static meow_widget *g_dcb; static rect_t r_dcb; /* Checkbox */ static meow_widget *g_dr1, *g_dr2; rect_t r_dr1, r_dr2; /* Radio 单选组 */ static meow_widget *g_dprl; static rect_t r_prl; /* ProgressBar 线性 */ meow_widget *g_dprc; rect_t r_prc; /* ProgressBar 圆形 */ static meow_widget *g_dcard; static rect_t r_card; /* Card */ static meow_widget *g_dcard_btn; static rect_t r_cardbtn; /* Card 内容子按钮 */ static meow_widget *g_dbadge; /* Badge */ static meow_widget *g_dtab; static rect_t r_tab; /* TabBar */ static meow_widget *g_dbtn_dlg; static rect_t r_dlg; /* 打开 Dialog 的按钮 */ static meow_widget *g_dbtn_sheet; static rect_t r_sheet;/* 打开 BottomSheet 的按钮 */ /* ② 媒体会话上报按钮(demo 页演示):点按 → meow_ui_media_meta 上报一条播放中元数据。 */ static meow_widget *g_dbtn_media; static rect_t r_media; /* LazyColumn:g_dlc/r_list 已迁 list.c(列表区 CONTAINER 子视图),此处仅 extern 引用。 */ /* ---- demo 分区标题/slider 值文本:注册为 CUSTOM 绘制物(§6.2 区域重画会重画它们)---- * 显式 z=D_TITLE_Z:> 一切自动 z(=127)、< chrome 置顶(999)。非控件直绘必须注册,否则 * 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); } /* ---- demo 构建/布局 ---- */ static void d_slot(meow_widget *w, rect_t r) { if (g_dslot_n < D_SLOT_MAX) { g_dslot[g_dslot_n].w = w; g_dslot[g_dslot_n].r = r; g_dslot_n++; } } static void d_title(int x, int y, const char *s) { 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++; 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 项虚拟化) */ static int demo_list_src(int idx, char *label, int len, void *ctx) { (void)ctx; if (idx < 0 || idx >= 100) return 0; snprintf(label, (size_t)len, "list item %d", idx); return 1; } /* 释放当前全部内容控件(含 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; list_view_release(); /* 列表控件已在上方 g_dslot 释放;摘容器引用/几何 */ sdk2_release(); /* SDK 批次2 子视图:释放 grid/btn/list 控件引用 + 清几何 */ } /* 按 scroll 重建全部内容控件(owner=ACT_DEMO:调用方须已 push ACT_DEMO)。 * 控件以绝对坐标创建于 (x, base_y - scroll),几何同步写入 r_* / 槽;状态从 g_ds 恢复。 */ static void demo_build_at(int scroll) { int ct = meow_ui_statusbar_height() + 56 + 8; g_demo_ct = ct; g_demo_cb = 608; g_dslot_n = 0; g_dtitle_n = 0; int yb = 0; #define SY(base) (ct + (base) - scroll) /* base 空间 → 屏幕 y */ #define SLOT(w,x,y,ww,hh) do { rect_t _r=RX(x,y,ww,hh); d_slot((w),_r); } while(0) /* ---- Buttons(五型 + 禁用) ---- */ d_title(DCL_X, SY(yb), "BUTTONS 5-variants"); yb += 26; r_btn[0] = RX(DCL_X, SY(yb), DCW, DBH); g_dbtn_b[0] = meow_btn_create_variant(r_btn[0].x, r_btn[0].y, DCW, DBH, "FILLED", MEOW_BTN_FILLED); SLOT(g_dbtn_b[0], r_btn[0].x, r_btn[0].y, r_btn[0].w, r_btn[0].h); r_btn[1] = RX(DCR_X, SY(yb), DCW, DBH); g_dbtn_b[1] = meow_btn_create_variant(r_btn[1].x, r_btn[1].y, DCW, DBH, "TONAL", MEOW_BTN_TONAL); SLOT(g_dbtn_b[1], r_btn[1].x, r_btn[1].y, r_btn[1].w, r_btn[1].h); yb += DCH; r_btn[2] = RX(DCL_X, SY(yb), DCW, DBH); g_dbtn_b[2] = meow_btn_create_variant(r_btn[2].x, r_btn[2].y, DCW, DBH, "OUTLINED", MEOW_BTN_OUTLINED); SLOT(g_dbtn_b[2], r_btn[2].x, r_btn[2].y, r_btn[2].w, r_btn[2].h); r_btn[3] = RX(DCR_X, SY(yb), DCW, DBH); g_dbtn_b[3] = meow_btn_create_variant(r_btn[3].x, r_btn[3].y, DCW, DBH, "TEXT", MEOW_BTN_TEXT); SLOT(g_dbtn_b[3], r_btn[3].x, r_btn[3].y, r_btn[3].w, r_btn[3].h); yb += DCH; r_btn[4] = RX(DCL_X, SY(yb), DCW, DBH); g_dbtn_b[4] = meow_btn_create_variant(r_btn[4].x, r_btn[4].y, DCW, DBH, "ELEVATED", MEOW_BTN_ELEVATED); SLOT(g_dbtn_b[4], r_btn[4].x, r_btn[4].y, r_btn[4].w, r_btn[4].h); g_dbtn_d = meow_btn_create_variant(DCR_X, SY(yb), DCW, DBH, "DISABLED", MEOW_BTN_FILLED); SLOT(g_dbtn_d, DCR_X, SY(yb), DCW, DBH); meow_widget_set_enabled(g_dbtn_d, 0); /* 禁用态:不命中 + 变暗 */ yb += DCH; for (int i = 0; i < 5; i++) meow_widget_set_selected(g_dbtn_b[i], g_ds.btn[i]); SLOT(meow_divider_create(DCL_X, SY(yb), DFW, 0), DCL_X, SY(yb), DFW, 1); yb += 16; /* ---- Input / State(Switch / Slider / TextField / Checkbox / Radio) ---- */ 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), 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; /* 拼音演示:MEOW_IME_PINYIN,输入「nihao」出候选「你好」上屏 */ r_dtf_py = RX(DCL_X, SY(yb), DFW, 44); g_dtf_py = meow_textfield_create(r_dtf_py.x, r_dtf_py.y, r_dtf_py.w, r_dtf_py.h, "拼音: nihao", g_ds.tf_py, 32, MEOW_IME_PINYIN); SLOT(g_dtf_py, r_dtf_py.x, r_dtf_py.y, r_dtf_py.w, r_dtf_py.h); yb += 54; r_dcb = RX(DCL_X, SY(yb), 34, 34); g_dcb = meow_checkbox_create(r_dcb.x, r_dcb.y); SLOT(g_dcb, r_dcb.x, r_dcb.y, r_dcb.w, r_dcb.h); r_dr1 = RX(DCR_X, SY(yb), 34, 34); g_dr1 = meow_radio_create(r_dr1.x, r_dr1.y); SLOT(g_dr1, r_dr1.x, r_dr1.y, r_dr1.w, r_dr1.h); yb += 40; r_dr2 = RX(DCR_X, SY(yb), 34, 34); g_dr2 = meow_radio_create(r_dr2.x, r_dr2.y); SLOT(g_dr2, r_dr2.x, r_dr2.y, r_dr2.w, r_dr2.h); yb += 40; meow_widget_set_selected(g_dcb, g_ds.ck); meow_widget_set_selected(g_dr1, g_ds.r1); meow_widget_set_selected(g_dr2, g_ds.r2); /* ---- ProgressBar(线性 + 圆形,on_tick 动画)---- * 进度语义值已上收 logic/state.c;重建从 logic_prog_get() 恢复。 */ d_title(DCL_X, SY(yb), "PROGRESS (animated)"); yb += 26; r_prl = RX(DCL_X, SY(yb), DFW, 8); g_dprl = meow_progress_create(r_prl.x, r_prl.y, r_prl.w, r_prl.h, logic_prog_get()); SLOT(g_dprl, r_prl.x, r_prl.y, r_prl.w, r_prl.h); yb += 16; r_prc = RX(DCL_X + 400 + 17 - 20, SY(yb) + 17 - 20, 40, 40); g_dprc = meow_progress_circular_create(r_prc.x + 20, r_prc.y + 20, 17, logic_prog_get()); SLOT(g_dprc, r_prc.x, r_prc.y, r_prc.w, r_prc.h); yb += 48; /* ---- Container(Card / Badge / TabBar) ---- */ d_title(DCL_X, SY(yb), "CONTAINER"); yb += 26; r_card = RX(DCL_X, SY(yb), DFW, 88); g_dcard = meow_card_create(r_card.x, r_card.y, r_card.w, r_card.h); SLOT(g_dcard, r_card.x, r_card.y, r_card.w, r_card.h); r_cardbtn = RX(DCL_X + 16, SY(yb) + 20, DFW - 32, 40); g_dcard_btn = meow_btn_create_variant(r_cardbtn.x, r_cardbtn.y, r_cardbtn.w, r_cardbtn.h, "IN-CARD BUTTON", MEOW_BTN_TONAL); meow_widget_set_selected(g_dcard_btn, g_ds.card); meow_card_set_content(g_dcard, g_dcard_btn); yb += 96; g_dbadge = meow_badge_create(DCL_X + 60, SY(yb) + 10); SLOT(g_dbadge, DCL_X + 52, SY(yb) + 2, 18, 18); meow_badge_set(g_dbadge, 3); yb += 30; r_tab = RX(DCL_X, SY(yb), DFW, 36); g_dtab = meow_tabbar_create(r_tab.x, r_tab.y, r_tab.w, r_tab.h); SLOT(g_dtab, r_tab.x, r_tab.y, r_tab.w, r_tab.h); meow_tabbar_add_tab(g_dtab, "Playlist"); meow_tabbar_add_tab(g_dtab, "Queue"); meow_tabbar_add_tab(g_dtab, "Now"); meow_tabbar_set_selected(g_dtab, g_ds.tab); yb += 44; /* ---- Modals(Dialog + BottomSheet) ---- */ d_title(DCL_X, SY(yb), "MODALS (blocking)"); yb += 26; r_dlg = RX(DCL_X, SY(yb), DCW, DBH); g_dbtn_dlg = meow_btn_create_variant(r_dlg.x, r_dlg.y, r_dlg.w, r_dlg.h, "DIALOG", MEOW_BTN_OUTLINED); SLOT(g_dbtn_dlg, r_dlg.x, r_dlg.y, r_dlg.w, r_dlg.h); r_sheet = RX(DCR_X, SY(yb), DCW, DBH); g_dbtn_sheet = meow_btn_create_variant(r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h, "SHEET", MEOW_BTN_OUTLINED); SLOT(g_dbtn_sheet, r_sheet.x, r_sheet.y, r_sheet.w, r_sheet.h); yb += DCH; /* ---- Media Session(② demo 按钮上报媒体 META,验证 SDK 封装) ---- */ d_title(DCL_X, SY(yb), "MEDIA SESSION (meta)"); yb += 26; r_media = RX(DCL_X, SY(yb), DCW, DBH); g_dbtn_media = meow_btn_create_variant(r_media.x, r_media.y, r_media.w, r_media.h, "SEND META", MEOW_BTN_FILLED); SLOT(g_dbtn_media, r_media.x, r_media.y, r_media.w, r_media.h); yb += DCH; /* ---- SDK 批次2 新控件(meow_select / meow_lazygrid / swipe-delete)---- * 三节由 sdk2 CONTAINER 子视图(ui/demo/sdk2/sdk2.c)承接:章节标题/宫格/select 值文本走 * 子视图 on_draw 直绘(同列表区标题);select 按钮 + swipe 列表 widget 由 sdk2 自持、此处创建。 * 控件创建后几何已内存;跨重建视图态(grid scroll/sel、swipe 行集/scroll、select 值)由 * sdk2_build_at 从子视图持有态恢复。 * 注:sdk2 置于 LazyColumn 之前(列表保持 demo 页内容**末尾**区块),确保「滚动到底列表上屏」 * 的既有宿主 P3 断言坐标假设仍然成立(sdk2 增高在列表上方,列表仍是内容底)。 */ yb = sdk2_build_at(yb, ct, scroll); /* ---- LazyColumn(100 项列表滚动) ---- * 本区块标题改由列表区 CONTAINER 子视图(ui/demo/list/list.c)的 on_draw 绘制(注册表→ * invalidate 链路);列表滚动/选中由 list_view_bind 从子视图持有态恢复(不再经 g_ds)。 */ int list_title_y = SY(yb); yb += 26; /* 标题条(子视图 on_draw 直绘,几何计入容器) */ r_list = RX(DCL_X, SY(yb), DFW, 150); g_dlc = meow_lazycolumn_create(r_list.x, r_list.y, r_list.w, r_list.h, 40, 100, demo_list_src, NULL); SLOT(g_dlc, r_list.x, r_list.y, r_list.w, r_list.h); list_view_bind(g_dlc, r_list, RX(DCL_X, list_title_y, DFW, 26)); yb += 158; g_demo_content_h = yb; #undef SY #undef SLOT } /* demo 全页重绘:整帧按 z 序重画全部(set_page_bg 清底 + 注册表按 z 升序)。 */ void demo_paint(void) { const meow_theme *th = meow_theme_get(); meow_ui_set_page_bg(th->bg); /* 页面底 = 主题 bg(区域/整帧重画清底用) */ meow_ui_paint_full(); /* 整帧按 z 序重画 */ } /* 内容视口坐标(每次 build 同步):屏 y ∈ [g_demo_ct, g_demo_cb),x 跨 0..width。 */ static void demo_repaint_viewport(void) { int h = g_demo_cb - g_demo_ct; if (h <= 0) return; /* 滚动重绘:只清底+按 z 重画内容视口矩形 [0, ct, w, h],不整帧。 * 359 教训(滚动只刷视口 + 局部脏区不整屏重绘铁律):meow_ui_invalidate 内部已把绘制 clip * 收敛到矩形(meow_gfx_clip_push)+ meow_ui_flush_region 只上屏该区;chrome(TopAppBar/ * NavBar/FAB,z=999,位于视口外)不触及,上一帧像素保留,滚动不再重画 chrome 字形。 */ meow_ui_invalidate(0, g_demo_ct, meow_ui_width(), h); } /* 重建内容到指定 scroll(clamp 到 [0, max_scroll]);进入/滚动共用。 * 滚动路径用视口局部刷(demo_repaint_viewport)替代整帧 demo_paint——每刻度重建仍发生, * 但不再整帧重绘(359 教训:SDK 控件内建局部刷,卡顿源于调用方整页重绘)。 */ void demo_rebuild_to(int scroll) { int vp_h = g_demo_cb - g_demo_ct; int maxs = g_demo_content_h - vp_h; if (maxs < 0) maxs = 0; if (scroll < 0) scroll = 0; if (scroll > maxs) scroll = maxs; demo_free_content(); demo_build_at(scroll); g_demo_scroll = scroll; demo_repaint_viewport(); } /* ---- demo 交互 ---- */ /* 局部刷新统一走 SDK meow_ui_invalidate(清底 + 按 z 重画相交绘制物 + 上屏)。 */ static void d_toggle_sel(meow_widget *w, rect_t r) { if (!w) return; meow_widget_set_selected(w, !meow_widget_selected(w)); meow_ui_invalidate(r.x, r.y, r.w, r.h); } /* Tap:落点在内容控件上,返回 1=已消费 */ static int demo_content_tap(int x, int y) { g_demo_focus = 0; /* 默认切回页滚动;仅命中列表时改置 1 */ /* 嵌套命中(子>父,§A.7 设计):列表区 CONTAINER 子视图先发,命中即消费。 */ if (list_view_hit(x, y)) return 1; /* SDK 批次2 子视图(select 按钮/宫格/列表行左滑松手):命中即消费。 */ if (sdk2_hit(x, y)) return 1; 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; 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); 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; meow_ui_invalidate(r_dtf.x, r_dtf.y, r_dtf.w, r_dtf.h); return 1; } if (meow_widget_hit(g_dtf_py, x, y)) { meow_textfield_activate(g_dtf_py); /* 拼音 IME */ strncpy(g_ds.tf_py, meow_textfield_text(g_dtf_py), sizeof g_ds.tf_py - 1); g_ds.tf_py[sizeof g_ds.tf_py - 1] = 0; meow_ui_invalidate(r_dtf_py.x, r_dtf_py.y, r_dtf_py.w, r_dtf_py.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); 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)); 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_dbtn_media, x, y)) { demo_media_send(); meow_ui_invalidate(r_media.x, r_media.y, r_media.w, r_media.h); return 1; } return 0; /* 列表区分发已由顶部 list_view_hit 处理(子>父) */ } /* ② 经 SDK 媒体会话封装上报一条「播放中」元数据(best-effort:无 launcher 静默降级)。 * 上报后 launcher(通知中心媒体卡片/主界面 Now Playing 页)可显示;demo 在 on_playctl 回调 * 打日志验证 receive 侧。 */ static void demo_media_send(void) { meow_ui_media_meta(1 /* playing */, 0, 180000, "MeowOS Demo Track", "SDK", "meow_ui_media", NULL, NULL); meow_log(MEOW_LOG_INFO, "demo: media meta sent (playing, 0/180000)"); } /* demo 页 on_tap 分发(chrome → 内容) */ /* 页面拖拽抬手把节流窗内遗留的 delta 一次性重建(视口局部刷)——slow drag 结束为 TAP 时也精确落点。 */ static void demo_flush_page_delta(void) { if (g_drag_page && (g_drag_page_delta >= 1 || g_drag_page_delta <= -1)) { demo_rebuild_to(g_demo_scroll + g_drag_page_delta); g_drag_page_delta = 0; } } void demo_on_tap(int x, int y) { demo_flush_page_delta(); /* 手势结束:TAP 抬手边沿(= 0) { meow_navigationbar_set_selected(g_nav, idx); meow_ui_invalidate(r_nav.x, r_nav.y, r_nav.w, r_nav.h); return; } } demo_content_tap(x, y); } /* demo 页 on_tick:ProgressBar 动画(局部刷,~120ms 步进)。 * 进度语义值已上收 logic/state.c(logic_prog_get/set),本处只驱动显示。 */ void demo_tick(uint64_t now) { if (g_demo_prog_last == 0) g_demo_prog_last = now; if (now - g_demo_prog_last < 120) return; g_demo_prog_last = now; int p = logic_prog_get() + 3; if (p > 100) p -= 101; /* (v+3)%101,wrap 0..100 */ logic_prog_set(p); meow_progress_set(g_dprl, p); meow_ui_invalidate(r_prl.x, r_prl.y, r_prl.w, r_prl.h); meow_progress_set(g_dprc, p); meow_ui_invalidate(r_prc.x, r_prc.y, r_prc.w, r_prc.h); } /* 按触碰 X 落 slider 值并局部刷(按下即定位 + 拖动实时,M2.71-bug2) */ 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); 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 > sdk2 > 页内容)。 * 列表区走 list_view_down(子>父:命中列表区由列表 CONTAINER 子视图收口); * SDK 批次2 子视图走 sdk2_down(宫格拖拽/列表行左滑 begin,子>父收口)。 */ void demo_touch_down(int x, int y) { g_drag_slider = 0; g_drag_list = 0; g_drag_page = 0; g_drag_page_delta = 0; if (!g_demo) return; if (meow_widget_hit(g_dslider, x, y)) { g_drag_slider = 1; demo_slider_drag(x); return; } if (list_view_down(x, y)) return; /* 列表区:置 focus+drag_list(list.c) */ if (sdk2_down(x, y)) return; /* SDK 批次2:宫格/列表行左滑 begin(sdk2.c) */ g_demo_focus = 0; if (y >= g_demo_ct && y < g_demo_cb && x >= 0 && x < meow_ui_width()) { g_drag_page = 1; g_drag_page_last_y = y; } } /* demo 页 on_touch_move:拖动过程实时更新(slider 值 / 列表滚动 / sdk2 滚动 / 页面滚动节流重建)。 */ void demo_touch_move(int x, int y) { if (!g_demo) return; if (g_drag_slider) { demo_slider_drag(x); return; } if (list_view_move(y)) return; /* 列表拖动(list.c) */ if (sdk2_move(x, y)) return; /* SDK 批次2:宫格滚动/列表左滑 move/列表竖向滚动(sdk2.c) */ if (g_drag_page) { int dy = y - g_drag_page_last_y; g_drag_page_last_y = y; g_drag_page_delta += -dy; /* 手指上移(y 减) → scroll 增(显示更低内容),与 on_swipe 一致 */ /* 359 教训:滚动期间每个 poll 合并一次 rebuild(勿每触摸事件各自重建/上屏)。 * 累计位移超阈值(≥8) 且(距上次 ≥16ms 或 已是大步 ≥32)才重建一次;未到窗则继续 * 累积 delta(不丢),下个窗口一次性重建到精确位置。 */ uint64_t now = meow_ui_time_ms(); int big = (g_drag_page_delta >= 8 || g_drag_page_delta <= -8); int merge = (now - g_drag_page_tlast >= 16) || (g_drag_page_delta >= 32) || (g_drag_page_delta <= -32); if (big && merge) { demo_rebuild_to(g_demo_scroll + g_drag_page_delta); g_drag_page_tlast = now; g_drag_page_delta = 0; } } } /* demo 页 on_swipe:LEFT=回 home;UP/DOWN=列表/宫格/sdk2 聚焦滚自身,否则页滚动。 */ void demo_swipe(int dir, int dist) { if (dir == MEOW_SWIPE_LEFT) { meow_view_page_pop(); return; } if (dir != MEOW_SWIPE_UP && dir != MEOW_SWIPE_DOWN) return; if (g_drag_page || g_drag_list || sdk2_dragging()) { /* 本次手势已实时跟手滚动过 */ demo_flush_page_delta(); /* 抬手:flush 节流窗内遗留页面 delta(视口局部刷)精确落点 */ return; } int del = (dir == MEOW_SWIPE_UP) ? dist : -dist; /* 手指上移 → 偏移增大 */ if (list_view_swipe(del)) return; /* 列表聚焦:滚列表(list.c,子>父消费) */ if (sdk2_swipe(del)) return; /* SDK 批次2:宫格/列表聚焦滚自身(sdk2.c,子>父消费) */ demo_rebuild_to(g_demo_scroll + del); } /* ---- demo 页 view(on_enter/on_leave/on_destroy 驱动进入/离开/释放)---- */ static void demo_page_enter(meow_view *v, void *ctx) { (void)v; ((demo_ctrl *)ctx)->active = 1; g_demo = 1; g_demo_focus = 0; hello_nav_report(); /* ① 上报 demo 栈深/顶层 */ /* 列表区 CONTAINER 子视图随本页进入由 SDK view_enter 递归驱动(其 on_draw 在 demo_rebuild_to * →demo_paint 中经注册表绘制;meow_draw_view_entered 含祖先已进入判定,首批可见)。 */ demo_rebuild_to(0); /* 内容建到 scroll=0(视口局部刷) */ demo_paint(); /* 进入页整帧首绘(含 chrome 置顶 z=999);仅进入一次,非滚动路径 */ meow_log(MEOW_LOG_INFO, "demo enter scroll=0"); } static void demo_page_leave(meow_view *v, void *ctx) { (void)v; ((demo_ctrl *)ctx)->active = 0; g_demo = 0; /* 列表区子视图由 SDK view_leave 在父之前递归离开(子<先>父,§A.4/D.1)。 */ } static void demo_page_destroy(meow_view *v, void *ctx) { (void)v; (void)ctx; demo_free_content(); /* 内容控件 + CUSTOM 标题/slider值 */ chrome_free(); /* chrome(TopAppBar / NavigationBar / FAB) */ } void demo_init(void) { meow_ui_activity_push(ACT_DEMO); chrome_create(); /* chrome 位置/置顶在 chrome.c */ demo_build_at(0); meow_ui_activity_pop(); g_demo_scroll = 0; } int demo_is_active(void) { return g_demo; } /* demo view:on_create 建 chrome+内容(一次,owner=ACT_DEMO);进入时 rebuild 到顶。 * 视图深度化:先挂列表区 + SDK 批次2 两个 CONTAINER 子视图到本 ACTIVITY view 的 children,再建内容。 * 子视图 id 用非页面值,不占 g_view_by_id(见 list.c/sdk2.c)。 */ static void demo_page_create(meow_view *v, void *ctx) { (void)v; ((demo_ctrl *)ctx)->view = v; meow_view *lv = list_view_create(); if (lv) meow_view_add_child(v, lv); meow_view *s2 = sdk2_view_create(); if (s2) meow_view_add_child(v, s2); demo_init(); } meow_view *demo_view_create(void) { static const meow_view_hooks h = { .on_create = demo_page_create, .on_enter = demo_page_enter, .on_leave = demo_page_leave, .on_destroy = demo_page_destroy, }; return meow_view_create(ACT_DEMO, MEOW_VIEW_ACTIVITY, &h, &g_demo_ctrl); }