Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/ui/search.c
T

291 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ui/search.c — 搜索页(ACT_SEARCH,顶栏搜索按钮进入)。
*
* 独立页面:搜索输入框(SDK TextField,接 IME §5.1,仅英文/数字)+ 红色「搜索」按钮 +
* 结果列表(点击可播,复用 g_st.songs[] 起点:播放/上下一首均对结果集生效)。
* 搜索结果网络走后台 worker(search_worker),UI 不卡。
*
* 风控纪律(用户指定):**搜索请求只在用户确认时发**——用户点「搜索」按钮才发起 cloudsearch,
* 进入页面不自动搜索;IME 输入仅本地编辑,不逐键发请求。
*/
#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;
static meow_widget *g_tb, *g_field, *g_list;
static ncm_rect_t r_cont, r_list, r_btn;
static int g_empty_id, g_footer_id, g_btn_id;
static int g_press_row, g_press_delta, g_dragging, g_drag_last_y;
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];
static void sr_drag_reset(void)
{
g_press_row = -1; g_press_delta = 0; g_dragging = 0; g_drag_last_y = 0;
}
static int sr_src2(int idx, meow_list_item *out, void *ctx)
{
(void)ctx;
if (idx < 0 || idx >= g_st.search_count) return 0;
ncm_song_t *s = &g_st.songs[idx];
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;
out->right = MEOW_LIST_RIGHT_ARROW;
out->on = 0; out->enabled = 1;
return 1;
}
static void sr_draw_empty(void *ctx)
{
(void)ctx;
if (meow_ui_activity_active() != ACT_SEARCH) return;
if (g_st.search_count != 0 && g_st.search_err == 0) return;
if (g_loading) { ncm_draw_empty(r_list, "SEARCHING", "正在搜索..."); return; }
if (g_st.search_err == NCM_ERR_AUTH) ncm_draw_empty(r_list, "SESSION EXPIRED", "请重新登录");
else if (g_st.search_err) ncm_draw_empty(r_list, "SEARCH FAILED", "点「搜索」重试");
else if (!g_query[0]) ncm_draw_empty(r_list, "NO KEYWORD", "输入关键词点「搜索」");
else ncm_draw_empty(r_list, "NO RESULTS", "未找到相关歌曲");
}
static void sr_draw_footer(void *ctx)
{
(void)ctx;
if (meow_ui_activity_active() != ACT_SEARCH) return;
if (g_st.search_err != 0 || g_st.search_count == 0) return;
char foot[64];
snprintf(foot, sizeof foot, "找到 %d 个结果", g_st.search_count);
ncm_draw_footer(r_cont, foot);
}
/* 「搜索」红色确认钮(SDK meow_btn FILLED 是 MD3 蓝;App 自绘品牌红)。 */
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, g_st.search_count);
meow_listview_set_scroll(g_list, 0);
meow_listview_set_sel(g_list, -1);
}
const meow_theme *th = meow_theme_get();
meow_ui_set_page_bg(th->bg);
meow_ui_paint_full();
}
/* 后台 worker:唯一网络调用(cloudsearch),UI 不卡。 */
static void *search_worker(void *arg)
{
(void)arg;
const char *kw = g_query;
int n = 0;
int rc = ncm_api_cloudsearch(g_st.ctx.device_id, g_st.ctx.cookie, kw,
g_st.songs, NCM_SEARCH_MAX, &n);
pthread_mutex_lock(&g_mu);
if (rc == NCM_OK) {
g_st.search_count = n;
g_st.song_count = n; /* 结果集作为当前播放上下文 */
g_st.search_err = 0;
} else {
g_st.search_count = 0;
g_st.song_count = 0;
g_st.search_err = rc;
}
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);
if (g_host_mock) { /* 宿主/测试:预置结果,同步 */
g_loading = 0; g_st.search_err = 0; sr_apply(); return;
}
if (g_thr_on) return; /* 已有 worker 在跑 */
g_st.search_err = 0;
g_st.search_count = 0;
g_loading = 1;
g_thr_on = 1;
if (pthread_create(&g_thr, NULL, search_worker, NULL) != 0) {
g_thr_on = 0; g_loading = 0; g_st.search_err = NCM_ERR_IO;
}
sr_apply();
}
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;
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;
}
if (g_field && meow_widget_hit(g_field, x, y)) { /* 唤 IME */
meow_textfield_activate(g_field);
return 1;
}
if (g_st.search_count > 0 && g_st.search_err == 0 && meow_widget_hit(g_list, x, y)) {
int row = meow_listview_hit(g_list, x, y);
if (row < 0) { sr_drag_reset(); return 1; }
sr_drag_reset();
player_play_song(row); /* 结果集即 g_st.songs[],点击可播 */
return 1;
}
return 0;
}
void search_touch_down(int x, int y)
{
sr_drag_reset();
if (g_st.search_count == 0 || g_st.search_err != 0) return;
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);
}
void search_swipe(int dir, int dist)
{
if (dir != MEOW_SWIPE_UP && dir != MEOW_SWIPE_DOWN) return;
if (g_st.search_count == 0 || g_st.search_err != 0) 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);
}
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);
r_list = NR(16, r_btn.y + r_btn.h + 12, r_cont.w - 32,
r_cont.y + r_cont.h - APP_FOOTER_H - (r_btn.y + r_btn.h + 12));
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);
if (!g_st.search_query[0]) ncm_strlcpy(g_st.search_query, "", sizeof g_st.search_query);
g_field = meow_textfield_create(field.x, field.y, field.w, field.h,
"输入歌曲 / 歌手", g_st.search_query,
sizeof g_st.search_query, MEOW_IME_ASCII);
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_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);
sr_drag_reset();
}
static void sr_page_enter(meow_view *v, void *ctx)
{
(void)v;
((search_ctrl *)ctx)->active = 1;
const meow_theme *th = meow_theme_get();
meow_ui_set_page_bg(th->bg);
meow_ui_paint_full();
}
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_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);
}