真机 dmesg 实锤:OOM Kill ncm (score 460, anon-rss 26.9MB, 设备 56MB)。 画像:封面全分辨率 RGBA 8.28MB(1448x1448 原生解码驻留) 是最大可控块。 瘦身(按收益排序): 1. sdk meow_ui_img 新增纯增量 meow_img_downscale + cover.c cover_cap(≤480px) → 封面 8.28MB→0.78MB,-7.65MB(mem_sim 实测 PASS)。 2. ncm_api.c 瞬态响应 rbuf:song_url→64KB、lyric/song_detail/cloudsearch/user_playlist→256KB (detail/track_all 保 1MB)。 3. ncm_app_internal.h 新增 ncm_thread_create(显式 128KB 栈),替换全部 14 处 pthread_create。 4. 滑动窗口/meta 复核零回退(window_sim:608B/首,6000 首 280KB 恒定)。 验证:mem_sim 封面收益 PASS + host home/player 渲染 OK + stream_sim/player_flow_sim/ window_sim/search_sim/cache_evict_sim 全 PASS + test_ncm_core host+mipsel(qemu) PASS + build_ncm.sh -Wall -Wextra 0 warning。真机 scp 播放在跑确认(缓存曲,0 播放请求)。 新 ncm md5 44fa16fe(text 1015150/bss 1362500)。正式部署留主会话。
521 lines
21 KiB
C
521 lines
21 KiB
C
/* ui/search.c — 搜索页(ACT_SEARCH,顶栏搜索按钮进入)。
|
||
*
|
||
* §23(REPORT):
|
||
* 1. 搜索历史:进页自动弹键盘(meow_textfield_activate);历史本地持久化(meow_config,
|
||
* 最近 ≤12 条、去重、最新在前);未输入时显示历史列表(点=直接搜、左滑删单条、清空全部)。
|
||
* 2. 结果分类「单曲/歌单」:顶部小页签(单曲 type=1 / 歌单 type=1000);两页签各自独立
|
||
* 结果集与滚动位置(g_scroll_song/g_scroll_pl);歌单行点击=playlist_open_pl 进详情。
|
||
* 3. 加载更多:触底分页(offset 累加,NCM_SEARCH_PAGE=30/页,cloudsearch 同参数);
|
||
* 每类各记各的 offset(appstate_search_offset 回填)。
|
||
* 4. 修 search_on_tap 误触(NCM_UI_REVIEW §3-2):套用 g_press_row + g_press_delta>10 抑制
|
||
* (对齐 home/playlist §13.3)。
|
||
* 5. 结果不再覆写歌单上下文:单曲结果写 g_st.search_songs[](独立数组,appstate 持有),
|
||
* 播放搜索单曲走 player_play_search_song(建立「搜索队列」上下文,queue_src=SEARCH),
|
||
* 原歌单窗口(g_st.songs[]/track_ids_count)不被破坏。
|
||
*
|
||
* 风控纪律(用户指定):**真实请求只在用户点「搜索」按钮/切页签/翻页时发**——进页不自动搜索,
|
||
* IME 输入仅本地编辑,不逐键发请求。调试/测试全部离线 stub(g_host_mock)。
|
||
*/
|
||
#include "../ncm_app_internal.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <pthread.h>
|
||
|
||
typedef struct { meow_view *view; int active; } search_ctrl;
|
||
static search_ctrl g_sc;
|
||
|
||
/* 视图模式 */
|
||
enum { SR_HIST = 0, SR_RES, SR_EMPTY };
|
||
|
||
static meow_widget *g_tb, *g_field, *g_list;
|
||
static ncm_rect_t r_cont, r_list, r_btn, r_tabs[2];
|
||
static int g_empty_id, g_footer_id, g_btn_id, g_tabbar_id;
|
||
static int g_press_row, g_press_delta, g_dragging, g_drag_last_y;
|
||
static int g_scroll_song, g_scroll_pl; /* 两页签各自滚动位置(§23)*/
|
||
|
||
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
|
||
static pthread_t g_thr;
|
||
static int g_thr_on;
|
||
static volatile int g_gen;
|
||
static int g_acked;
|
||
static volatile int g_loading;
|
||
static char g_query[256]; /* worker 所搜关键词(不可含引用问题,见 core 转义) */
|
||
static int g_tab; /* 0=单曲 1=歌单 */
|
||
static int g_searched; /* 1=已提交过搜索(区分「输入后未搜」空态文案)*/
|
||
static int g_req_tab, g_req_offset, g_req_replace;
|
||
static volatile int g_more_err; /* 翻页失败(snack 一次)*/
|
||
|
||
static void *search_worker(void *arg); /* 前向声明(sr_maybe_load_more 先引用) */
|
||
|
||
static void sr_drag_reset(void)
|
||
{
|
||
g_press_row = -1; g_press_delta = 0; g_dragging = 0; g_drag_last_y = 0;
|
||
}
|
||
|
||
/* 当前视图(§23 历史/结果/空态)。由 g_st.search_query 是否为空 + 各页签结果数推导。 */
|
||
static int sr_view(void)
|
||
{
|
||
if (g_st.search_query[0] == 0)
|
||
return (appstate_search_history_count() > 0) ? SR_HIST : SR_EMPTY;
|
||
if (appstate_search_count(g_tab) > 0) return SR_RES;
|
||
return SR_EMPTY;
|
||
}
|
||
|
||
static int sr_count(void)
|
||
{
|
||
int v = sr_view();
|
||
if (v == SR_HIST) return appstate_search_history_count();
|
||
if (v == SR_RES) return appstate_search_count(g_tab);
|
||
return 0;
|
||
}
|
||
|
||
static void sr_save_scroll(void)
|
||
{
|
||
if (g_tab == 0) g_scroll_song = meow_listview_scroll(g_list);
|
||
else g_scroll_pl = meow_listview_scroll(g_list);
|
||
}
|
||
|
||
static void sr_restore_scroll(void)
|
||
{
|
||
meow_listview_set_scroll(g_list, (g_tab == 0) ? g_scroll_song : g_scroll_pl);
|
||
}
|
||
|
||
static int sr_src2(int idx, meow_list_item *out, void *ctx)
|
||
{
|
||
(void)ctx;
|
||
int v = sr_view();
|
||
if (v == SR_HIST) {
|
||
char q[NCM_SEARCH_HIST_LEN];
|
||
if (!appstate_search_history_get(idx, q, sizeof q)) return 0;
|
||
out->icon_kind = MEOW_LIST_ICON_NONE;
|
||
out->title = q;
|
||
out->subtitle = "";
|
||
out->right = MEOW_LIST_RIGHT_NONE;
|
||
out->on = 0; out->enabled = 1;
|
||
return 1;
|
||
}
|
||
if (v == SR_RES) {
|
||
if (g_tab == 0) {
|
||
static ncm_song_t s;
|
||
if (appstate_search_song(idx, &s) != 0) return 0;
|
||
static char sub[256];
|
||
if (s.art_name[0] && s.album_name[0]) snprintf(sub, sizeof sub, "%s / %s", s.art_name, s.album_name);
|
||
else if (s.art_name[0]) snprintf(sub, sizeof sub, "%s", s.art_name);
|
||
else snprintf(sub, sizeof sub, "%s", s.album_name);
|
||
out->icon_kind = MEOW_LIST_ICON_NONE;
|
||
out->title = s.name;
|
||
out->subtitle = sub;
|
||
} else {
|
||
static ncm_playlist_t p;
|
||
if (appstate_search_pl(idx, &p) != 0) return 0;
|
||
static char sub[64];
|
||
snprintf(sub, sizeof sub, "%d 首", p.track_count);
|
||
out->icon_kind = MEOW_LIST_ICON_NONE;
|
||
out->title = p.name;
|
||
out->subtitle = sub;
|
||
}
|
||
out->right = MEOW_LIST_RIGHT_ARROW;
|
||
out->on = 0; out->enabled = 1;
|
||
return 1;
|
||
}
|
||
return 0; /* SR_EMPTY:无行 */
|
||
}
|
||
|
||
static void sr_draw_empty(void *ctx)
|
||
{
|
||
(void)ctx;
|
||
if (meow_ui_activity_active() != ACT_SEARCH) return;
|
||
if (sr_view() != SR_EMPTY) return; /* 历史/结果态有行,空态不画 */
|
||
if (g_loading) { ncm_draw_empty(r_list, "搜索中", "正在搜索..."); return; }
|
||
if (g_st.search_err == NCM_ERR_AUTH) ncm_draw_empty(r_list, "登录已过期", "请重新登录");
|
||
else if (g_st.search_err) ncm_draw_empty(r_list, "搜索失败", "点「搜索」重试");
|
||
else if (!g_st.search_query[0]) ncm_draw_empty(r_list, "输入关键词", "输入后点「搜索」");
|
||
else if (!g_searched) ncm_draw_empty(r_list, "输入后点「搜索」", "或点历史记录");
|
||
else ncm_draw_empty(r_list, "没有结果",
|
||
g_tab == 0 ? "未找到相关歌曲" : "未找到相关歌单");
|
||
}
|
||
|
||
static void sr_draw_footer(void *ctx)
|
||
{
|
||
(void)ctx;
|
||
if (meow_ui_activity_active() != ACT_SEARCH) return;
|
||
int v = sr_view();
|
||
if (v == SR_HIST) { /* 历史:右缘「清空全部」按钮 */
|
||
const meow_theme *th = meow_theme_get();
|
||
const ncm_pal_t *pa = ncm_palette(g_dark);
|
||
int cw = meow_ui_text_width("清空全部", th->font_body);
|
||
int fx = r_cont.x + r_cont.w - 16 - cw;
|
||
int fy = r_cont.y + r_cont.h - APP_FOOTER_H + (APP_FOOTER_H - th->font_body) / 2;
|
||
meow_ui_text(fx, fy, "清空全部", th->font_body, pa->red);
|
||
return;
|
||
}
|
||
if (v == SR_RES) {
|
||
int n = appstate_search_count(g_tab);
|
||
if (n > 0) {
|
||
char foot[96];
|
||
snprintf(foot, sizeof foot, "%s %d %s%s",
|
||
g_tab == 0 ? "单曲" : "歌单", n,
|
||
g_tab == 0 ? "个结果" : "个歌单",
|
||
appstate_search_has_more(g_tab) ? " / 加载更多" : "");
|
||
ncm_draw_footer(r_cont, foot);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void sr_draw_tabs(void *ctx)
|
||
{
|
||
(void)ctx;
|
||
if (meow_ui_activity_active() != ACT_SEARCH) return;
|
||
if (sr_view() != SR_RES) return; /* 页签只在结果态显示 */
|
||
const meow_theme *th = meow_theme_get();
|
||
const ncm_pal_t *pa = ncm_palette(g_dark);
|
||
static const char *lab[2] = { "单曲", "歌单" };
|
||
for (int i = 0; i < 2; i++) {
|
||
int on = (g_tab == i);
|
||
uint32_t col = on ? pa->red : th->onSurfaceVariant;
|
||
meow_ui_text_center(r_tabs[i].x + r_tabs[i].w / 2, r_tabs[i].y + (r_tabs[i].h - th->font_body) / 2,
|
||
lab[i], th->font_body, col);
|
||
if (on) meow_ui_fill_rect(r_tabs[i].x, r_tabs[i].y + r_tabs[i].h - 3, r_tabs[i].w, 3, pa->red);
|
||
}
|
||
}
|
||
|
||
static void sr_draw_btn(void *ctx)
|
||
{
|
||
(void)ctx;
|
||
if (meow_ui_activity_active() != ACT_SEARCH) return;
|
||
const ncm_pal_t *pa = ncm_palette(g_dark);
|
||
ncm_draw_button(r_btn.x, r_btn.y, r_btn.w, r_btn.h, "搜索", pa->red, pa->on_red);
|
||
}
|
||
|
||
static void sr_apply(void)
|
||
{
|
||
if (g_list) meow_listview_set_count(g_list, sr_count());
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_ui_set_page_bg(th->bg);
|
||
meow_ui_paint_full();
|
||
}
|
||
|
||
/* 触底翻页(§23):RES 态 + 还有下一页 + 未在载 → 按当前页签的 offset 拉下一页。
|
||
* 仅由用户滚动动作触发(风控:翻页=用户动作)。 */
|
||
static void sr_maybe_load_more(void)
|
||
{
|
||
if (sr_view() != SR_RES) return;
|
||
if (g_thr_on || g_loading || g_st.search_err != 0) return;
|
||
int n = appstate_search_count(g_tab);
|
||
int cap = (g_tab == 0) ? NCM_SEARCH_SONG_MAX : NCM_SEARCH_PL_MAX;
|
||
if (n <= 0 || n >= cap || !appstate_search_has_more(g_tab)) return;
|
||
int item = APP_ROW_H;
|
||
int scroll = meow_listview_scroll(g_list);
|
||
if (scroll + r_list.h < n * item - item * 2) return; /* 未近底 */
|
||
g_req_tab = g_tab; g_req_offset = n; g_req_replace = 0;
|
||
/* 单飞:无 worker 才发(search_worker 尾部解锁) */
|
||
g_thr_on = 1; g_loading = 1; g_more_err = 0;
|
||
if (ncm_thread_create(&g_thr, search_worker, NULL) != 0) {
|
||
g_thr_on = 0; g_loading = 0; g_more_err = 1;
|
||
}
|
||
}
|
||
|
||
static void *search_worker(void *arg)
|
||
{
|
||
(void)arg;
|
||
int tab = g_req_tab, off = g_req_offset, repl = g_req_replace;
|
||
const char *kw = g_query;
|
||
int rc = NCM_ERR_IO, n = 0;
|
||
int64_t total = -1;
|
||
if (tab == 0) {
|
||
static ncm_song_t sbuf[NCM_SEARCH_PAGE];
|
||
rc = ncm_api_cloudsearch_song(g_st.ctx.device_id, g_st.ctx.cookie, kw,
|
||
off, NCM_SEARCH_PAGE, sbuf, NCM_SEARCH_PAGE, &n, &total);
|
||
pthread_mutex_lock(&g_mu);
|
||
if (rc == NCM_OK) {
|
||
if (repl) appstate_search_clear(0);
|
||
appstate_search_append_songs(n, sbuf, total);
|
||
g_st.search_err = 0; g_more_err = 0;
|
||
} else if (repl) g_st.search_err = rc;
|
||
else g_more_err = 1;
|
||
} else {
|
||
static ncm_playlist_t pbuf[NCM_SEARCH_PAGE];
|
||
rc = ncm_api_cloudsearch_playlist(g_st.ctx.device_id, g_st.ctx.cookie, kw,
|
||
off, NCM_SEARCH_PAGE, pbuf, NCM_SEARCH_PAGE, &n, &total);
|
||
pthread_mutex_lock(&g_mu);
|
||
if (rc == NCM_OK) {
|
||
if (repl) appstate_search_clear(1);
|
||
appstate_search_append_pls(n, pbuf, total);
|
||
g_st.search_err = 0; g_more_err = 0;
|
||
} else if (repl) g_st.search_err = rc;
|
||
else g_more_err = 1;
|
||
}
|
||
g_loading = 0;
|
||
g_gen++;
|
||
pthread_mutex_unlock(&g_mu);
|
||
return NULL;
|
||
}
|
||
|
||
void search_submit(const char *kw)
|
||
{
|
||
if (!kw || !kw[0]) { ncm_snack("请输入关键词"); return; }
|
||
ncm_strlcpy(g_query, kw, sizeof g_query);
|
||
ncm_strlcpy(g_st.search_query, kw, sizeof g_st.search_query);
|
||
appstate_search_history_add(kw); /* §23:历史(去重+最新在前+≤12)*/
|
||
g_searched = 1;
|
||
g_scroll_song = g_scroll_pl = 0;
|
||
if (g_list) { meow_listview_set_scroll(g_list, 0); }
|
||
appstate_search_clear(g_tab ^ 1); /* 新查询:清非当前页签,切过去时懒加载新词 */
|
||
if (g_host_mock) { /* 宿主/测试:预置结果,同步 */
|
||
g_loading = 0; g_st.search_err = 0; sr_apply(); return;
|
||
}
|
||
if (g_thr_on) return; /* 单飞:已有 worker 在跑 */
|
||
g_req_tab = g_tab; g_req_offset = 0; g_req_replace = 1;
|
||
g_st.search_err = 0;
|
||
g_loading = 1; g_more_err = 0;
|
||
g_thr_on = 1;
|
||
if (ncm_thread_create(&g_thr, search_worker, NULL) != 0) {
|
||
g_thr_on = 0; g_loading = 0; g_st.search_err = NCM_ERR_IO;
|
||
}
|
||
sr_apply();
|
||
}
|
||
|
||
/* 切页签:保存旧滚动 → 换页签 → 恢复该页签滚动;空结果页签懒加载(用户切页签动作)。 */
|
||
static void sr_set_tab(int t)
|
||
{
|
||
if (t == g_tab) return;
|
||
sr_save_scroll();
|
||
g_tab = t;
|
||
g_st.search_tab = t; /* 应用态同步(重启/重进保持) */
|
||
sr_restore_scroll();
|
||
sr_apply();
|
||
if (g_st.search_query[0] && !g_thr_on && !g_loading &&
|
||
g_st.search_err == 0 && appstate_search_count(g_tab) == 0) {
|
||
g_req_tab = g_tab; g_req_offset = 0; g_req_replace = 1;
|
||
g_loading = 1; g_more_err = 0;
|
||
g_thr_on = 1;
|
||
if (ncm_thread_create(&g_thr, search_worker, NULL) != 0) {
|
||
g_thr_on = 0; g_loading = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
void search_tick(uint64_t now)
|
||
{
|
||
(void)now;
|
||
chrome_tick();
|
||
if (!g_thr_on) return;
|
||
if (g_gen == g_acked) return;
|
||
g_acked = g_gen;
|
||
g_thr_on = 0;
|
||
g_loading = 0;
|
||
if (g_more_err) { ncm_snack("加载失败"); g_more_err = 0; }
|
||
sr_apply();
|
||
}
|
||
|
||
void search_open(void)
|
||
{
|
||
meow_view_page_push(ACT_SEARCH);
|
||
}
|
||
|
||
int search_is_active(void) { return g_sc.active; }
|
||
|
||
int search_on_tap(int x, int y)
|
||
{
|
||
if (g_tb) {
|
||
int ah = meow_topappbar_hit(g_tb, x, y);
|
||
if (ah == MEOW_TOPBAR_BACK) { sr_drag_reset(); meow_view_page_pop(); return 1; }
|
||
if (ah >= 0) { sr_drag_reset(); return 1; }
|
||
}
|
||
if (x >= r_btn.x && x < r_btn.x + r_btn.w && y >= r_btn.y && y < r_btn.y + r_btn.h) {
|
||
const char *t = meow_textfield_text(g_field); /* 「搜索」确认(风控:仅此处发) */
|
||
search_submit(t ? t : "");
|
||
return 1;
|
||
}
|
||
int v = sr_view();
|
||
if (v == SR_RES) { /* 页签切换 */
|
||
for (int i = 0; i < 2; i++)
|
||
if (x >= r_tabs[i].x && x < r_tabs[i].x + r_tabs[i].w &&
|
||
y >= r_tabs[i].y && y < r_tabs[i].y + r_tabs[i].h) { sr_set_tab(i); return 1; }
|
||
}
|
||
if (v == SR_HIST && /* 历史「清空全部」按钮 */
|
||
y >= r_cont.y + r_cont.h - APP_FOOTER_H && y < r_cont.y + r_cont.h &&
|
||
x >= r_cont.x + r_cont.w - 16 - 70) {
|
||
sr_drag_reset();
|
||
appstate_search_history_clear();
|
||
sr_apply();
|
||
return 1;
|
||
}
|
||
if (g_field && meow_widget_hit(g_field, x, y)) { /* 唤 IME */
|
||
meow_textfield_activate(g_field);
|
||
return 1;
|
||
}
|
||
if (meow_widget_hit(g_list, x, y)) {
|
||
if (v == SR_EMPTY) { /* 失败态:点内容区重试(风控:用户动作) */
|
||
if (g_st.search_err && g_st.search_query[0]) {
|
||
sr_drag_reset(); g_st.search_err = 0;
|
||
if (!g_thr_on) {
|
||
g_req_tab = g_tab; g_req_offset = 0; g_req_replace = 1;
|
||
g_loading = 1;
|
||
g_thr_on = 1;
|
||
if (ncm_thread_create(&g_thr, search_worker, NULL) != 0) {
|
||
g_thr_on = 0; g_loading = 0; g_st.search_err = NCM_ERR_IO;
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
sr_drag_reset();
|
||
return 1;
|
||
}
|
||
/* 误触修复(§13.3/§23):按下行 + 拖动>10 抑制(对齐 home/playlist) */
|
||
int was_drag = (g_press_delta > 10);
|
||
int row = (g_press_row >= 0) ? g_press_row : meow_listview_hit(g_list, x, y);
|
||
sr_drag_reset();
|
||
if (row < 0 || was_drag) return 1;
|
||
if (v == SR_HIST) {
|
||
char q[NCM_SEARCH_HIST_LEN];
|
||
if (appstate_search_history_get(row, q, sizeof q)) search_submit(q);
|
||
return 1;
|
||
}
|
||
if (g_tab == 0) player_play_search_song(row); /* 单曲:搜索队列起播(§23)*/
|
||
else {
|
||
ncm_playlist_t p;
|
||
if (appstate_search_pl(row, &p) == 0) playlist_open_pl(&p); /* 歌单:进详情 */
|
||
}
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int search_swipe_left(void)
|
||
{
|
||
if (sr_view() != SR_HIST) return 0; /* 仅历史模式左滑删单条 */
|
||
int row = g_press_row; /* touch_down 已记按下行 */
|
||
sr_drag_reset();
|
||
if (row >= 0) {
|
||
appstate_search_history_remove(row);
|
||
sr_apply();
|
||
return 1; /* 已消费:不触返回 */
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void search_touch_down(int x, int y)
|
||
{
|
||
sr_drag_reset();
|
||
if (meow_widget_hit(g_list, x, y)) {
|
||
int row = meow_listview_hit(g_list, x, y);
|
||
if (row >= 0) g_press_row = row;
|
||
g_dragging = 1; g_drag_last_y = y;
|
||
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
|
||
}
|
||
}
|
||
|
||
void search_touch_move(int x, int y)
|
||
{
|
||
(void)x;
|
||
if (!g_dragging) return;
|
||
if (g_press_row >= 0) {
|
||
int d = y - g_drag_last_y; if (d < 0) d = -d;
|
||
g_press_delta += d;
|
||
if (g_press_delta > 10) g_press_row = -1;
|
||
}
|
||
int dy = y - g_drag_last_y; g_drag_last_y = y;
|
||
if (dy == 0) return;
|
||
int before = meow_listview_scroll(g_list);
|
||
meow_listview_scroll_by(g_list, -dy);
|
||
if (meow_listview_scroll(g_list) != before) {
|
||
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
|
||
sr_maybe_load_more();
|
||
}
|
||
}
|
||
|
||
void search_swipe(int dir, int dist)
|
||
{
|
||
if (dir != MEOW_SWIPE_UP && dir != MEOW_SWIPE_DOWN) return;
|
||
if (sr_view() != SR_RES) return;
|
||
int del = (dir == MEOW_SWIPE_UP) ? dist : -dist;
|
||
int before = meow_listview_scroll(g_list);
|
||
meow_listview_scroll_by(g_list, del);
|
||
if (meow_listview_scroll(g_list) != before) {
|
||
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
|
||
sr_maybe_load_more();
|
||
}
|
||
}
|
||
|
||
static void sr_page_create(meow_view *v, void *ctx)
|
||
{
|
||
(void)v;
|
||
((search_ctrl *)ctx)->view = v;
|
||
r_cont = ncm_content_rect();
|
||
ncm_rect_t field = NR(16, r_cont.y + 12, r_cont.w - 32, 48);
|
||
r_btn = NR(field.x, field.y + field.h + 12, field.w, 44);
|
||
int by = r_btn.y + r_btn.h + 8;
|
||
r_tabs[0] = NR(r_btn.x, by, r_btn.w / 2 - 6, 36);
|
||
r_tabs[1] = NR(r_btn.x + r_btn.w / 2 + 6, by, r_btn.w / 2 - 6, 36);
|
||
r_list = NR(16, by + 36 + 8, r_cont.w - 32,
|
||
r_cont.y + r_cont.h - APP_FOOTER_H - (by + 36 + 8));
|
||
if (r_list.h < 0) r_list.h = 0;
|
||
meow_ui_activity_push(ACT_SEARCH); /* owner=ACT_SEARCH */
|
||
g_tb = ncm_topbar_create("搜索", 1, NULL);
|
||
/* 中文搜索:IME 切 MEOW_IME_PINYIN(拼音候选上屏 UTF-8 到 g_st.search_query,经 on_change 实时回写;
|
||
* 键盘「拼/英」键可切回 ASCII 输英文,「中」热区回拼音——英文零回归)。 */
|
||
g_field = meow_textfield_create(field.x, field.y, field.w, field.h,
|
||
"输入歌曲 / 歌手", g_st.search_query,
|
||
sizeof g_st.search_query, MEOW_IME_PINYIN);
|
||
g_list = meow_listview_create(r_list.x, r_list.y, r_list.w, r_list.h, 0,
|
||
sr_src2, NULL, MEOW_LIST_SEL_NONE);
|
||
meow_listview_set_item_h(g_list, APP_ROW_H);
|
||
meow_ui_activity_pop();
|
||
meow_widget_set_z(g_list, 10);
|
||
g_btn_id = meow_draw_custom_register(60, r_btn.x, r_btn.y, r_btn.w, r_btn.h,
|
||
sr_draw_btn, NULL);
|
||
g_tabbar_id = meow_draw_custom_register(40, r_tabs[0].x, r_tabs[0].y,
|
||
r_btn.w, r_tabs[0].h, sr_draw_tabs, NULL);
|
||
g_empty_id = meow_draw_custom_register(1, r_list.x, r_list.y, r_list.w, r_list.h,
|
||
sr_draw_empty, NULL);
|
||
g_footer_id = meow_draw_custom_register(1, r_cont.x, r_cont.y + r_cont.h - APP_FOOTER_H,
|
||
r_cont.w, APP_FOOTER_H, sr_draw_footer, NULL);
|
||
/* 页签初始 = 应用态记录值(宿主 dump 可指定 search_tab);越界回落 0。 */
|
||
g_tab = (g_st.search_tab == 0 || g_st.search_tab == 1) ? g_st.search_tab : 0;
|
||
sr_drag_reset();
|
||
}
|
||
|
||
static void sr_page_enter(meow_view *v, void *ctx)
|
||
{
|
||
(void)v;
|
||
((search_ctrl *)ctx)->active = 1;
|
||
sr_apply(); /* 进页即按当前模式设列表 count(历史/结果/空态),避免首屏空列表 */
|
||
#ifndef MEOW_UI_HOST_TEST
|
||
/* §23:进页自动弹键盘(字段为空时;返回浏览结果不打断)。宿主离屏为阻塞式模态 IME,跳过。 */
|
||
if (g_field && g_st.search_query[0] == 0) meow_textfield_activate(g_field);
|
||
#else
|
||
(void)0;
|
||
#endif
|
||
}
|
||
|
||
static void sr_page_leave(meow_view *v, void *ctx)
|
||
{
|
||
(void)v;
|
||
((search_ctrl *)ctx)->active = 0;
|
||
}
|
||
|
||
static void sr_page_destroy(meow_view *v, void *ctx)
|
||
{
|
||
(void)v; (void)ctx;
|
||
if (g_btn_id) { meow_draw_unregister(g_btn_id); g_btn_id = 0; }
|
||
if (g_tabbar_id){ meow_draw_unregister(g_tabbar_id);g_tabbar_id = 0; }
|
||
if (g_empty_id) { meow_draw_unregister(g_empty_id); g_empty_id = 0; }
|
||
if (g_footer_id){ meow_draw_unregister(g_footer_id);g_footer_id = 0; }
|
||
if (g_list) { meow_widget_free(g_list); g_list = NULL; }
|
||
if (g_field){ meow_widget_free(g_field); g_field = NULL; }
|
||
if (g_tb) { meow_widget_free(g_tb); g_tb = NULL; }
|
||
}
|
||
|
||
meow_view *search_view_create(void)
|
||
{
|
||
static const meow_view_hooks h = {
|
||
.on_create = sr_page_create,
|
||
.on_enter = sr_page_enter,
|
||
.on_leave = sr_page_leave,
|
||
.on_destroy = sr_page_destroy,
|
||
};
|
||
return meow_view_create(ACT_SEARCH, MEOW_VIEW_ACTIVITY, &h, &g_sc);
|
||
}
|