feat: M6.10–M6.19 批量收编 + M6.19 推卡验收

- M7 launcher 重写 5 期(虚拟化宫格/meow_theme 单一权威/横幅动画/自定义 PIN 键盘)
- OOM 通用化(cachegov 上收 SDK)、自动切歌根治、meowpanel jv_free 泄漏修复、Hello 滚动卡顿修复
- ncm 歌词 UI / 媒体通知封面 / 封面分辨率 640
- 5 bug 修复:ncm 后台化 UI 叠层+点击穿透、再进 ncm 桌面叠层、播放页新旧 UI 叠层抢刷脏区(meow_ui_gfx 恢复边沿整帧重画)、PIN 亮屏亮度跳(keys.c meow_panel_on 按 settings_brightness_clamped 恢复)、通知中心封面显歌单封面(cover.c save_cover_raw 加 is_song + 命中块守卫)
- 四 App 统一重链新 meow_ui.a 51caee4a;M6.19=0.8.19 固件 2d2331f2 推卡自刷验收全绿
This commit is contained in:
AskaEth committed 2026-09-03 12:16:01 +08:00
1 parent c2ee7626a4
commit 3d70a3dd2c
176 files changed
+15507 -597

No files matched your search

+370 -202
View File
@@ -23,13 +23,14 @@ int g_notif_ctrl_died = 0;
static int g_ncp_dnd = 0;
/* ⑦ 行左滑删除的绘制态(供 ncp_draw_list / ncp_repaint_row 画该行位移)。
* g_ncp_swipe_row = 正在左滑的行下标(-1=无);g_ncp_swipe_off = 该行左移量(px,<=0)。
* run_notif_center 的手势状态机(row_swipe_row/row_off/row_settle/row_off_target/row_anim_at/row_paint)
* 与其同步,绘制只读这组全局,避免给 draw_notif_center/ncp_draw_list 加参数。 */
/* ⑦ 行左滑删除的绘制态(供旧手绘 ncp_draw_list / ncp_repaint_row 画该行位移)——M7.3 已迁 SDK
* meow_listview 左滑删除(SDK 内部持有 swipe_idx/swipe_dx,不再需要本组全局)。
* 保留在 #if 0 回退区内(随旧手绘内层一起重出)。 */
#if 0
static int g_ncp_swipe_row = -1;
static int g_ncp_swipe_off = 0;
#endif
/* ---- 阶段D附录B:通知中心顶部媒体卡片(固定于通知列表上方,活动媒体会话时显示)---- */
@@ -59,6 +60,58 @@ static int ncp_list_view(void) { int v = NCP_LIST_BOT - ncp_list_top(); return v
#define NCP_MEDIA_BTN_R 22 /* play 半径;prev/next 略小 */
#define NCP_MEDIA_PROG_Y 74 /* 相对卡片顶的进度条 top */
/* M7.0b:媒体卡片三控制按钮改用 SDK meow_iconbtn + sb_icons 矢量图标(与 Now Playing 同源),
* 取代原先手绘三角/竖条(ncp_media_tri/ncp_media_btn,用户反馈手绘有 bug)。up: 圆钮尺寸由
* meow_iconbtn 的 size 决定(ink=0.6*size),此处 prev/next 用 NCP_MEDIA_BTN_R-4、play 用
* NCP_MEDIA_BTN_R(与旧几何一致,命中仍按三圆心——ncp_media_card_tap 不变)。控件 lazy 建一次,
* owner=哨兵 0x52(不与电源菜单 0x50 / Now Playing 0x51 冲突);绘制经 meow_widget_draw。 */
#define NCP_MEDIA_ACT 0x52
#define NCP_MEDIA_PREV_R (NCP_MEDIA_BTN_R - 4) /* 18 */
#define NCP_MEDIA_NEXT_R (NCP_MEDIA_BTN_R - 4) /* 18 */
static meow_widget *g_ncp_btn_prev = NULL, *g_ncp_btn_play = NULL;
static meow_widget *g_ncp_btn_pause = NULL, *g_ncp_btn_next = NULL;
static int g_ncp_media_btn_init = 0;
/* 创建三控制按钮(owner 钉哨兵;play/pause 同位叠两钮,按状态只画其一——同 now_playing)。 */
static void ncp_media_btns_create(void)
{
meow_ui_activity_push(NCP_MEDIA_ACT);
int by = NCP_LIST_TOP + NCP_MEDIA_BTN_Y; /* 三钮圆心 y(与 ncp_draw_media_card/ncp_media_card_tap 同源) */
g_ncp_btn_prev = meow_iconbtn_create(NCP_MEDIA_PREV_CX - NCP_MEDIA_PREV_R, by - NCP_MEDIA_PREV_R,
NCP_MEDIA_PREV_R * 2, MEOW_ICON_SKIP_PREV);
g_ncp_btn_play = meow_iconbtn_create(NCP_MEDIA_PLAY_CX - NCP_MEDIA_BTN_R, by - NCP_MEDIA_BTN_R,
NCP_MEDIA_BTN_R * 2, MEOW_ICON_PLAY_ARROW);
g_ncp_btn_pause = meow_iconbtn_create(NCP_MEDIA_PLAY_CX - NCP_MEDIA_BTN_R, by - NCP_MEDIA_BTN_R,
NCP_MEDIA_BTN_R * 2, MEOW_ICON_PAUSE);
g_ncp_btn_next = meow_iconbtn_create(NCP_MEDIA_NEXT_CX - NCP_MEDIA_NEXT_R, by - NCP_MEDIA_NEXT_R,
NCP_MEDIA_NEXT_R * 2, MEOW_ICON_SKIP_NEXT);
/* 主播放钮保持「强调动作」观感(与 Now Playing 一致):底色=primary、图标=onPrimary;其余默认。 */
const meow_theme *th = meow_theme_get();
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = th->primary; op.fg = th->onPrimary;
if (g_ncp_btn_play) meow_widget_set_opts(g_ncp_btn_play, &op);
if (g_ncp_btn_pause) meow_widget_set_opts(g_ncp_btn_pause, &op);
meow_ui_activity_pop();
g_ncp_media_btn_init = 1;
}
/* 绘制三控制按钮(prev/next 固定;play/pause 按会话状态只画其一——两钮同位,改画时新圆底盖掉旧)。
* 每次刷新播放钮底色/图标(同 now_playing np_refresh_theme:跨主题切换跟手)。 */
static void ncp_draw_media_btns(const meow_media_session_t *s)
{
if (!g_ncp_media_btn_init) ncp_media_btns_create();
const meow_theme *th = meow_theme_get();
meow_widget_opts op; memset(&op, 0, sizeof op);
op.bg = th->primary; op.fg = th->onPrimary;
if (g_ncp_btn_play) meow_widget_set_opts(g_ncp_btn_play, &op);
if (g_ncp_btn_pause) meow_widget_set_opts(g_ncp_btn_pause, &op);
if (g_ncp_btn_prev) { meow_widget_set_selected(g_ncp_btn_prev, 0); meow_widget_draw(g_ncp_btn_prev); }
if (g_ncp_btn_next) { meow_widget_set_selected(g_ncp_btn_next, 0); meow_widget_draw(g_ncp_btn_next); }
int playing = (s && s->state == MEOW_MEDIA_PLAYING);
meow_widget *pb = playing ? g_ncp_btn_pause : g_ncp_btn_play;
if (pb) { meow_widget_set_selected(pb, 0); meow_widget_draw(pb); }
}
/* 短串拷贝(同 meow_notif.c 语义:保证 NUL + 截断) */
static void ncp_strncpy(char *dst, const char *src, size_t len)
@@ -340,7 +393,8 @@ static void ncp_chip_state(int chip, int *on, char *val, int vlen)
} else if (chip == NCP_CHIP_THEME) {
int v = 0; meow_settings_get_enum("display.theme", &v); /* dark=0, light=1 */
*on = (v == 1);
if (val && vlen > 0) snprintf(val, vlen, "%s", *on ? "浅色" : "深色");
if (val && vlen > 0) snprintf(val, vlen, "%s", *on ? meow_str_zh("ncp.theme_light", "浅色")
: meow_str_zh("ncp.theme_dark", "深色"));
} else if (chip == NCP_CHIP_BRIGHT) {
int v = 0; meow_settings_get_int("display.brightness", &v);
int vmin = 1; meow_settings_get_int("display.brightness_min", &vmin);
@@ -406,8 +460,199 @@ static int ncp_chip_apply(int chip)
}
/* ==================================================================== */
/* M7.3:通知中心内层迁 SDK 控件(芯片/双滑条/勿扰开关/通知列表/左滑删除) */
/* --------------------------------------------------------------------
* 定位:只迁「内层绘制/命中」。叠加会话骨架(SIGSTOP / 输入移交 / 恢复帧 / 顶条热区——见
* app_launcher.c run_app_notif_overlay)绝对不动,此处仅把 run_notif_center / draw_notif_center
* 中面板体的绘制与行命中交给 SDK meow_widget。SDK meow_ui.a 已定稿(c3d6371b),勿重编直接用。
*
* owner 钉哨兵 NCP_WIDGET_ACT(不冲突:0x50 电源菜单 / 0x51 锁屏·NowPlaying / 0x52 子页·媒体卡 /
* 0x53 设置页)。绘制经 meow_widget_draw / meow_listview_vs_draw;命中:通知列表走
* meow_listview_hit / meow_listview_swipe_begin·move·end(SDK 手势判定+删除/回弹动画),
* 芯片/滑条/勿扰开关沿用既有人工几何命中(与媒体卡 M7.0b 一致——叠加会话为手绘循环,不跑 SDK
* 输入线程,故不依赖 meow_widget_hit 的 owner==active 闸门)。 */
#define NCP_WIDGET_ACT 0x54
static meow_widget *g_ncp_lv = NULL; /* 通知列表:listview 虚拟化 + 行自绘 + 空态 + 左滑删除 */
static meow_widget *g_ncp_sl_bri = NULL; /* 亮度滑条 */
static meow_widget *g_ncp_sl_vol = NULL; /* 音量滑条 */
static meow_widget *g_ncp_sw_dnd = NULL; /* 勿扰开关 */
static meow_widget *g_ncp_chip[NCP_CHIP_COUNT]; /* 5 个快捷 chip(自定义画布:圆角 + 图标 + 标签) */
static int g_ncp_widget_init = 0;
static int g_ncp_need_paint = 0; /* M7.3:行左滑删除回调置位 → 主循环整面板重画 */
/* ---- 通知列表虚拟化源:count=当前行数(动态,逐帧实时查);bind 仅填 enabled 供
* meow_listview_hit 判定(行内容经 set_item_draw 自绘直读 g_rows[],无池缓存过期问题)。 ---- */
static int ncp_lv_count(void *ctx) { (void)ctx; return g_nrows; }
static void ncp_lv_bind(int idx, meow_lv_item *it, void *ctx)
{
(void)ctx;
memset(it, 0, sizeof *it);
it->enabled = (idx >= 0 && idx < g_nrows) ? 1 : 0;
}
/* 文本截断(SDK meow_ui_text_width/meow_ui_text;UTF-8 安全,与旧 ncp_text 同构)。 */
static void ncp_mt_text(int x, int y, const char *s, int size, uint32_t col, int maxw)
{
if (!s) s = "";
if (meow_ui_text_width(s, size) <= maxw) { meow_ui_text(x, y, s, size, col); return; }
int ell = meow_ui_text_width("...", size);
int limit = maxw - ell;
char buf[128]; int len = 0;
while (s[len] && len < (int)sizeof buf - 8) {
unsigned char c0 = (unsigned char)s[len];
int cp = 1;
if ((c0 & 0xE0) == 0xC0) cp = 2;
else if ((c0 & 0xF0) == 0xE0) cp = 3;
else if ((c0 & 0xF8) == 0xF0) cp = 4;
if (len + cp >= (int)sizeof buf - 1) break;
for (int k = 0; k < cp; k++) buf[len + k] = s[len + k];
buf[len + cp] = 0;
if (meow_ui_text_width(buf, size) > limit) { buf[len] = 0; break; }
len += cp;
}
if (len > 0) {
if (len + 3 < (int)sizeof buf) { buf[len] = '.'; buf[len+1] = '.'; buf[len+2] = '.'; buf[len+3] = 0; }
meow_ui_text(x, y, buf, size, col);
} else {
meow_ui_text(x, y, "...", size, col);
}
}
/* 通知行自绘(set_item_draw 覆盖默认 renderer)。x 已含左滑位移(SDK 先画右露删除区并把行 x 左移),
* 逐元素对齐旧 ncp_draw_row;颜色全取 meow_theme 语义色(M7 定案 #2 单一权威)。 */
static void ncp_lv_row_draw(int idx, int x, int y, int w, int h, int state, void *ctx)
{
(void)w; (void)state; (void)ctx; /* w=SDK 视口宽;行卡宽用 NCP_ROW_W(含 NCP_ROW_X 两侧留白) */
if (idx < 0 || idx >= g_nrows) return;
const ncp_row_t *r = &g_rows[idx];
const meow_theme *th = meow_theme_get();
meow_gfx_fill_round(x + NCP_ROW_X, y, NCP_ROW_W, h - 6, 12, th->surfaceContainer);
/* 图标 */
int icx = x + NCP_ICON_X, icy = y + (h - 32) / 2;
if (r->img) meow_ui_img_cover(icx, icy, 32, r->img);
else {
const char *nm = ncp_app_name(r->app_id);
ncp_dot(icx + 16, icy + 16, 16, th->primary);
char ch = nm[0] ? nm[0] : '?';
char cbuf[4] = { ch, 0, 0, 0 };
if ((unsigned char)ch >= 0x80 && nm[1]) { cbuf[1] = nm[1]; cbuf[2] = 0;
if ((unsigned char)ch >= 0xE0 && nm[2]) { cbuf[2] = nm[2]; cbuf[3] = 0; } }
int cw = meow_ui_text_width(cbuf, th->font_body);
meow_ui_text(icx + 16 - cw / 2, icy + 16 - meow_ui_text_height(th->font_body) / 2,
cbuf, th->font_body, th->onPrimary);
}
/* app 名 + 时间 */
meow_ui_text(x + NCP_TEXT_X, y + 10, ncp_app_name(r->app_id), th->font_body, th->onSurfaceVariant);
char tbuf[32]; mnp_time_ago(r->created_ms, (uint64_t)time(NULL) * 1000u, tbuf, sizeof tbuf);
meow_ui_text(x + NCP_ROW_X + NCP_ROW_W - 8 - meow_ui_text_width(tbuf, th->font_body),
y + 12, tbuf, th->font_body, th->onSurfaceVariant);
/* 标题 + 内容(SDK 截断) */
ncp_mt_text(x + NCP_TEXT_X, y + 30, r->summary, th->font_body, th->onSurface, NCP_TEXT_W);
ncp_mt_text(x + NCP_TEXT_X, y + 56, r->body, th->font_body, th->onSurfaceVariant, NCP_TEXT_W);
/* 进度(progress>=0):细条 */
if (r->progress >= 0) {
int px = x + NCP_TEXT_X, pw = NCP_TEXT_W, py = y + h - 14;
meow_ui_fill_rect(px, py, pw, 4, th->surfaceVariant);
int fw = r->progress * pw / 100;
if (fw > 0) meow_ui_fill_rect(px, py, fw, 4, th->primary);
}
}
/* 左滑删除完成回调(SDK 删除/回弹动画结束后触发):撤下该通知 + 重采集 + 重排。 */
static void ncp_lv_on_delete(int idx, void *ctx)
{
(void)ctx;
logmsg("notif-center: swipe delete row %d", idx);
if (idx >= 0 && idx < g_nrows) meow_notif_dismiss(g_rows[idx].id);
ncp_refresh_rows();
/* 行数变化 → 列表重排:置位让主循环整面板重画(含媒体卡出现/消失重排 + 列表滚动重钳)。 */
g_ncp_need_paint = 1;
}
/* 芯片自绘(自定义画布 render_cb:TONAL 观感圆角 + 位图图标 + 标签,与旧 draw_notif_center chip 循环一致)。
* launcher 侧 meow_widget 不透明 → 用 meow_widget_rect 取绝对几何(同 power_menu pm_render_cb)。 */
static void ncp_chip_render(meow_widget *w, const meow_theme *th, void *ctx)
{
int c = (int)(intptr_t)ctx;
if (c < 0 || c >= NCP_CHIP_COUNT) return;
int rc[4]; meow_widget_rect(w, rc);
int x = rc[0], y = rc[1], wd = rc[2], ht = rc[3];
int on = 0; char val[16] = ""; ncp_chip_state(c, &on, val, sizeof val);
uint32_t bg = on ? th->primary : th->surfaceContainer;
uint32_t fg = on ? th->onPrimary : th->onSurfaceVariant;
meow_gfx_fill_round(x, y, wd, ht, 12, bg);
int icon_cx = x + (wd - SB_ICON_SIZE) / 2;
int icon_cy = y + 14;
if (c == NCP_CHIP_WIFI) ncp_sb_icon(on ? SB_I_WIFI : SB_I_WIFI_OFF, icon_cx, icon_cy, fg, 22);
else if (c == NCP_CHIP_BT) ncp_sb_icon(on ? SB_I_BT : SB_I_BT_DIS, icon_cx, icon_cy, fg, 22);
else if (c == NCP_CHIP_THEME) {
if (on) ncp_dot(icon_cx + 16, icon_cy, 11, fg);
else { ncp_dot(icon_cx + 16, icon_cy, 11, fg); ncp_dot(icon_cx + 16, icon_cy, 6, bg); }
}
else if (c == NCP_CHIP_BRIGHT) ncp_sb_icon(SB_I_BRIGHT, icon_cx, icon_cy, fg, 22);
else if (c == NCP_CHIP_POWER) {
int pc = icon_cx + SB_ICON_SIZE / 2;
meow_gfx_fill_round(pc - 8, icon_cy - 8, 16, 16, 8, fg); /* 外环 */
meow_gfx_fill_round(pc - 4, icon_cy - 4, 8, 8, 4, bg); /* 内孔(显环厚) */
meow_gfx_fill_round(pc - 1, icon_cy - 16, 3, 9, 1, fg); /* 顶部短柄 */
}
static const char *chip_lbl[NCP_CHIP_COUNT] = { "WiFi", "蓝牙", "主题", "亮度", "电源菜单" };
static const char *chip_id[NCP_CHIP_COUNT] = { "ncp.chip_wifi", "ncp.chip_bt",
"ncp.chip_theme", "ncp.chip_bright",
"ncp.chip_power" };
char label[24]; snprintf(label, sizeof label, "%s", meow_str_zh(chip_id[c], chip_lbl[c]));
if (val[0] && c == NCP_CHIP_BRIGHT) snprintf(label, sizeof label, "%.9s", val);
ui_draw(x + (wd - ui_width(label, 14)) / 2, y + ht - 18, label, fg, 14);
}
/* 芯片自定义画布命中(矩形;未被主循环使用——命中仍走 ncp_chip_hit 人工几何,此处仅满足 W_CUSTOM 契约)。 */
static int ncp_chip_widget_hit(meow_widget *w, int x, int y, void *ctx)
{
(void)ctx;
int rc[4]; meow_widget_rect(w, rc);
return (x >= rc[0] && x < rc[0] + rc[2] && y >= rc[1] && y < rc[1] + rc[3]);
}
/* 创建 M7.3 内层控件集(owner 钉哨兵;仅首进创建一次,跨次复用——同媒体卡 M7.0b 惯用法)。 */
static void ncp_widgets_create(void)
{
meow_ui_activity_push(NCP_WIDGET_ACT);
/* 通知列表(视口 = 列表区;行高 NCP_ROW_H;空态 + 左滑删除) */
int lvy = ncp_list_top(), lvh = ncp_list_view();
g_ncp_lv = meow_listview_create(0, lvy, fb_xres, lvh, 0, NULL, NULL, MEOW_LIST_SEL_NONE);
if (g_ncp_lv) {
meow_listview_set_vsource(g_ncp_lv, ncp_lv_count, ncp_lv_bind, NULL);
meow_listview_set_item_draw(g_ncp_lv, ncp_lv_row_draw, NULL);
meow_listview_set_item_h(g_ncp_lv, NCP_ROW_H);
meow_listview_set_empty_text(g_ncp_lv, meow_str_zh("ncp.empty", "暂无通知"));
meow_listview_set_swipe_delete(g_ncp_lv, 1, ncp_lv_on_delete, NULL);
}
/* 亮度/音量双滑条(track 中心对齐旧 s_draw_slider:NCP_SL*_Y+3) */
int bmin = 1; meow_settings_get_int("display.brightness_min", &bmin);
int bval = 0; meow_settings_get_int("display.brightness", &bval);
bval = mnp_brightness_clamp(bval, bmin);
g_ncp_sl_bri = meow_slider_create(NCP_SLIDER_X, NCP_SL1_Y - 14, NCP_SLIDER_W, bmin, 100, bval);
int vmax = vol_clamp(settings_volume_max);
int vval = vol_clamp(settings_volume_get());
g_ncp_sl_vol = meow_slider_create(NCP_SLIDER_X, NCP_SL2_Y - 14, NCP_SLIDER_W, 0, vmax, vval);
/* 勿扰开关 */
g_ncp_sw_dnd = meow_switch_create(0, 0, g_ncp_dnd ? 1 : 0);
if (g_ncp_sw_dnd) meow_widget_set_rect(g_ncp_sw_dnd, NCP_DND_SPLIT_X - 6 - 48,
NCP_DND_Y + NCP_DND_H / 2 - 14, 48, 28);
/* 5 个快捷 chip(自定义画布;坐标与旧 ncp_chip_x / NCP_CHIP_Y 一致) */
for (int c = 0; c < NCP_CHIP_COUNT; c++)
g_ncp_chip[c] = meow_widget_custom_create(ncp_chip_x(c), NCP_CHIP_Y, NCP_CHIP_W, NCP_CHIP_H,
ncp_chip_render, ncp_chip_widget_hit,
(void *)(intptr_t)c);
meow_ui_activity_pop();
g_ncp_widget_init = 1;
}
/* ⑦ 画删除底:行右缘被空出的条(宽=-off)铺 error 红底 + 白「删除」字(条宽≥44 才显字,否则纯红渐进)。
* off<=0(负得多=卡滑出多、露出右条多);off=0 不画。 */
#if 0 /* M7.3 回退位:旧手绘行删除底(SDK 左滑删除已接管此绘制,见 meow_listview swipe 渲染)。 */
static void ncp_draw_delete_bg(int ry, int off, const meow_ui_theme_t *th)
{
if (off >= 0) return;
@@ -520,6 +765,35 @@ static void ncp_repaint_row(int scroll, int row, int off)
meow_dirty_add(0, ry, fb_xres, NCP_ROW_H);
meow_ui_flush();
}
#endif /* M7.3 回退位结束(上述旧手绘 ncp_draw_delete_bg/ncp_draw_row/ncp_draw_list/
* ncp_repaint_list/ncp_repaint_row 重开本区即回退;新路径由下方 SDK 版接管)。 */
/* ---- M7.3:通知列表绘制(SDK listview 版,替换上方 #if 0 的手绘版)---- */
/* 绘制通知列表可见区(scroll=像素偏移)。同步 listview 视口矩形(媒体卡出现/消失时列表顶缘
* 变化)+ 滚动偏移,再画整个列表控件(虚拟化 + set_item_draw 行自绘 + 空态 + 左滑删除渲染
* 全由 SDK 完成)。 */
static void ncp_draw_list(int scroll)
{
if (!g_ncp_widget_init || !g_ncp_lv) return;
int lt = ncp_list_top(), lv = ncp_list_view();
meow_widget_set_rect(g_ncp_lv, 0, lt, fb_xres, lv);
meow_listview_set_scroll(g_ncp_lv, mnp_scroll_clamp(scroll, g_nrows * NCP_ROW_H, lv));
meow_widget_draw(g_ncp_lv);
}
/* 仅重绘列表可见区(滚动专用;不整面板、不刷状态栏)。先清列表底,再画 SDK listview;
* 只标列表带脏([NCP_LIST_TOP, NCP_LIST_BOT))——「滚动只重绘列表可视区,绝不整屏」。 */
static void ncp_repaint_list(int scroll)
{
const meow_ui_theme_t *th = s_ui_theme();
int lt = ncp_list_top(), lv = ncp_list_view();
fill_rect(0, lt, fb_xres, lv, th->surface_variant);
ncp_draw_list(scroll);
meow_dirty_add(0, lt, fb_xres, lv);
meow_ui_flush();
}
/* ⑥/⑧ 勿扰开关带命中(面板内小 toggle,⑧ 收窄为左半段):
@@ -555,14 +829,20 @@ static void ncp_draw_dnd(const meow_ui_theme_t *th)
meow_gfx_fill_round(bx + 6, bcy - 11, 4, 4, 2, th->on_surface_variant);/* 顶蒂 */
ncp_dot(bx + 8, bcy + 6, 2, th->on_surface_variant); /* 底部小圆点 */
/* label + 状态 */
ui_draw(bx + 22, cy - meow_font_line_height(18) / 2 + 2, "勿扰", th->on_surface, 18);
const char *dnd_lbl = meow_str_zh("ncp.dnd", "勿扰");
ui_draw(bx + 22, cy - meow_font_line_height(18) / 2 + 2, dnd_lbl, th->on_surface, 18);
char st[16];
snprintf(st, sizeof st, "%s", dnd_on ? "开" : "关");
int lw = ui_width("勿扰", 18);
snprintf(st, sizeof st, "%s", dnd_on ? meow_str_zh("common.on", "开") : meow_str_zh("common.off", "关"));
int lw = ui_width(dnd_lbl, 18);
ui_draw(bx + 22 + lw + 12, cy - meow_font_line_height(18) / 2 + 2, st,
dnd_on ? th->primary : th->on_surface_variant, 18);
/* 左半段右侧开关(⑧:从原 fb_xres-24-48 左移到分割线左侧,48×28 垂直居中) */
s_draw_switch(NCP_DND_SPLIT_X - 6 - 48, cy - 14, dnd_on, th);
/* 左半段右侧开关(⑧:从原 fb_xres-24-48 左移到分割线左侧,48×28 垂直居中)。
* M7.3:迁 SDK meow_switch(绘制经 meow_widget_draw;命中仍走 ncp_dnd_hit 整带)。 */
if (g_ncp_widget_init && g_ncp_sw_dnd) {
meow_widget_set_rect(g_ncp_sw_dnd, NCP_DND_SPLIT_X - 6 - 48, cy - 14, 48, 28);
meow_switch_set(g_ncp_sw_dnd, dnd_on ? 1 : 0);
meow_widget_draw(g_ncp_sw_dnd);
}
}
@@ -576,7 +856,7 @@ static void ncp_draw_clear(const meow_ui_theme_t *th)
if (cw <= 0) return;
meow_gfx_fill_round(cx0, y + 4, cw, NCP_DND_H - 8, 12, th->surface_container);
/* 垃圾桶字形(简化:顶盖横条 + 桶身圆角 + 侧提手)+「清除」文字,整体在按钮内居中(美观对称)。 */
const char *lbl = "清除";
const char *lbl = meow_str_zh("ncp.clear", "清除");
int lw = ui_width(lbl, 18);
int unit = 22; /* 图标占位宽 + 与文字的间距 */
int total = unit + lw;
@@ -629,38 +909,6 @@ static void ncp_media_cover_load(const meow_media_session_t *s)
g_ncp_media_cover = meow_img_load_file(path);
}
/* 实心三角(dir=+1 右向 / -1 左向);size=最大宽度 px。按扫描线 fill_rect 近似 */
static void ncp_media_tri(int cx, int cy, int size, int dir, uint32_t col)
{
int half = size / 2;
for (int dy = -half; dy <= half; dy++) {
int w = half + 1 - (dy < 0 ? -dy : dy); /* 顶宽 1 → 底宽 half+1 */
if (w <= 0) continue;
int x0 = cx + (dir > 0 ? -w / 2 : w / 2 - w);
fill_rect(x0, cy + dy, w, 1, col);
}
}
/* 圆形按钮底 + 字形(kind:0=prev 1=play 2=next 3=pause 4=暂停态play)。 */
static void ncp_media_btn(int cx, int cy, int r, int kind, const meow_ui_theme_t *th)
{
meow_gfx_fill_round(cx - r, cy - r, r * 2, r * 2, r, th->primary);
uint32_t fg = 0xFFFFFF;
if (kind == 0) { /* prev:左三角 + 左竖条 */
ncp_media_tri(cx + 4, cy, r - 4, -1, fg);
fill_rect(cx - 3, cy - (r - 5), 3, (r - 5) * 2, fg);
} else if (kind == 1) { /* play:右三角 */
ncp_media_tri(cx + 2, cy, r - 2, 1, fg);
} else if (kind == 2) { /* next:右三角 + 右竖条 */
ncp_media_tri(cx - 4, cy, r - 4, 1, fg);
fill_rect(cx + 1, cy - (r - 5), 3, (r - 5) * 2, fg);
} else if (kind == 3) { /* pause:双竖条 */
int bw = (r - 6) / 2; if (bw < 2) bw = 2;
fill_rect(cx - 7, cy - (r - 5), bw, (r - 5) * 2, fg);
fill_rect(cx + 7 - bw, cy - (r - 5), bw, (r - 5) * 2, fg);
}
}
/* 画顶部媒体卡片(封面小图 + 标题/歌手 + 播放/暂停/上下曲 + 进度条)。 */
static void ncp_draw_media_card(const meow_ui_theme_t *th)
{
@@ -678,19 +926,15 @@ static void ncp_draw_media_card(const meow_ui_theme_t *th)
const char *t = s->title[0] ? s->title : s->app_id;
int cw = ui_width(t, 20);
ui_draw(cover_x + (cover_s - cw) / 2, cover_y + (cover_s - meow_font_line_height(20)) / 2,
t, 0xFFFFFF, 20);
t, meow_theme_get()->onPrimary, 20);
}
int tx = cover_x + cover_s + 14;
int tw = (NCP_MEDIA_PREV_CX - NCP_MEDIA_BTN_R - 8) - tx; /* 文本宽限到上一首按钮左缘前 8px */
if (tw < 0) tw = 0;
ncp_text(tx, cy + 14, s->title, 18, th->on_surface, tw);
ncp_text(tx, cy + 40, s->artist[0] ? s->artist : s->album, 16, th->on_surface_variant, tw);
/* 控制按钮 */
int by = cy + NCP_MEDIA_BTN_Y;
ncp_media_btn(NCP_MEDIA_PREV_CX, by, NCP_MEDIA_BTN_R - 4, 0, th);
int pk = (s->state == MEOW_MEDIA_PLAYING) ? 3 /* pause */ : 1 /* play */;
ncp_media_btn(NCP_MEDIA_PLAY_CX, by, NCP_MEDIA_BTN_R, pk, th);
ncp_media_btn(NCP_MEDIA_NEXT_CX, by, NCP_MEDIA_BTN_R - 4, 2, th);
/* 控制按钮(SDK meow_iconbtn + sb_icons,与 Now Playing 一致;命中仍走 ncp_media_card_tap 三圆心) */
ncp_draw_media_btns(s);
/* 进度条 + 时间 */
uint32_t pos = meow_media_display_pos(s);
int px = tx, pw = NCP_MEDIA_CARD_X + NCP_MEDIA_CARD_W - 12 - px, py = cy + NCP_MEDIA_PROG_Y;
@@ -762,61 +1006,34 @@ static void draw_notif_center(int scroll, int redraw_sb)
int grab_w = 44, grab_x = (W - grab_w) / 2;
meow_gfx_fill_round(grab_x, NCP_GRAB_Y, grab_w, 6, 3, th->outline);
/* 标题 + 「上滑/点空白关闭」 提示 */
ui_draw(24, NCP_TITLE_Y, "通知中心", th->on_surface, 22);
ui_draw(W - 24 - ui_width("点空白关闭", 16), NCP_TITLE_Y + 2, "点空白关闭", th->on_surface_variant, 16);
ui_draw(24, NCP_TITLE_Y, meow_str_zh("ncp.title", "通知中心"), th->on_surface, 22);
ui_draw(W - 24 - ui_width(meow_str_zh("ncp.tap_close", "点空白关闭"), 16), NCP_TITLE_Y + 2,
meow_str_zh("ncp.tap_close", "点空白关闭"), th->on_surface_variant, 16);
/* 快捷开关行(⑥:5 个 = WiFi/蓝牙/主题/亮度/电源菜单,居中自动随 NCP_CHIP_N) */
static const char *chip_lbl[NCP_CHIP_COUNT] = { "WiFi", "蓝牙", "主题", "亮度", "电源菜单" };
int total_chip = NCP_CHIP_N * NCP_CHIP_W + (NCP_CHIP_N - 1) * NCP_CHIP_GAP;
int chip_x0 = (W - total_chip) / 2;
for (int c = 0; c < NCP_CHIP_N; c++) {
int cx = chip_x0 + c * (NCP_CHIP_W + NCP_CHIP_GAP);
int on = 0; char val[16] = ""; ncp_chip_state(c, &on, val, sizeof val);
uint32_t bg = on ? th->primary : th->surface_container;
uint32_t fg = on ? 0xFFFFFF : th->on_surface_variant;
meow_gfx_fill_round(cx, NCP_CHIP_Y, NCP_CHIP_W, NCP_CHIP_H, 12, bg);
int icon_cx = cx + (NCP_CHIP_W - SB_ICON_SIZE) / 2;
int icon_cy = NCP_CHIP_Y + 14;
if (c == NCP_CHIP_WIFI) ncp_sb_icon(on ? SB_I_WIFI : SB_I_WIFI_OFF, icon_cx, icon_cy, fg, 22);
else if (c == NCP_CHIP_BT) ncp_sb_icon(on ? SB_I_BT : SB_I_BT_DIS, icon_cx, icon_cy, fg, 22);
else if (c == NCP_CHIP_THEME) {
if (on) ncp_dot(icon_cx + 16, icon_cy, 11, fg);
else { ncp_dot(icon_cx + 16, icon_cy, 11, fg); ncp_dot(icon_cx + 16, icon_cy, 6, bg); }
}
else if (c == NCP_CHIP_BRIGHT) ncp_sb_icon(SB_I_BRIGHT, icon_cx, icon_cy, fg, 22);
else if (c == NCP_CHIP_POWER) {
/* 电源符号:圆环 + 顶部短柄,近似 ⏻(用现有 meow_gfx_fill_round 近似圆)。 */
int pc = icon_cx + SB_ICON_SIZE / 2;
meow_gfx_fill_round(pc - 8, icon_cy - 8, 16, 16, 8, fg); /* 外环 */
meow_gfx_fill_round(pc - 4, icon_cy - 4, 8, 8, 4, bg); /* 内孔(显环厚) */
meow_gfx_fill_round(pc - 1, icon_cy - 16, 3, 9, 1, fg); /* 顶部短柄 */
}
char label[24];
snprintf(label, sizeof label, "%s", chip_lbl[c]);
if (val[0] && c == NCP_CHIP_BRIGHT)
snprintf(label, sizeof label, "%.9s", val); /* chip 下示值 */
ui_draw(cx + (NCP_CHIP_W - ui_width(label, 14)) / 2, NCP_CHIP_Y + NCP_CHIP_H - 18,
label, fg, 14);
}
/* 快捷开关行(⑥:5 个 = WiFi/蓝牙/主题/亮度/电源菜单)。M7.3:迁 SDK 自定义画布 chip
*(TONAL 观感圆角 + 位图图标 + 标签,见 ncp_chip_render;绘制经 meow_widget_draw)。 */
if (g_ncp_widget_init)
for (int c = 0; c < NCP_CHIP_N; c++)
if (g_ncp_chip[c]) meow_widget_draw(g_ncp_chip[c]);
/* 亮度滑条 */
/* 亮度滑条(M7.3:迁 SDK meow_slider;track 中心 = NCP_SL1_Y+3,与旧 s_draw_slider 对齐) */
int bmin = 1; meow_settings_get_int("display.brightness_min", &bmin);
int bval = 0; meow_settings_get_int("display.brightness", &bval);
bval = mnp_brightness_clamp(bval, bmin);
ui_draw(NCP_SLIDER_X, NCP_SL1_Y - 26, "亮度", th->on_surface, 18);
ui_draw(NCP_SLIDER_X, NCP_SL1_Y - 26, meow_str_zh("ncp.brightness", "亮度"), th->on_surface, 18);
char vbb[16]; snprintf(vbb, sizeof vbb, "%d%%", bval);
ui_draw(NCP_SLIDER_X + NCP_SLIDER_W - ui_width(vbb, 18), NCP_SL1_Y - 26, vbb,
th->on_surface_variant, 18);
s_draw_slider(NCP_SLIDER_X, NCP_SL1_Y, NCP_SLIDER_W, bval, bmin, 100, th);
if (g_ncp_widget_init && g_ncp_sl_bri) { meow_slider_set(g_ncp_sl_bri, bval); meow_widget_draw(g_ncp_sl_bri); }
/* 音量滑条 */
int vval = 0; meow_settings_get_int("audio.volume", &vval);
vval = vol_clamp(vval);
ui_draw(NCP_SLIDER_X, NCP_SL2_Y - 26, "音量", th->on_surface, 18);
ui_draw(NCP_SLIDER_X, NCP_SL2_Y - 26, meow_str_zh("ncp.volume", "音量"), th->on_surface, 18);
char vbl[16]; snprintf(vbl, sizeof vbl, "%d%%", vval);
ui_draw(NCP_SLIDER_X + NCP_SLIDER_W - ui_width(vbl, 18), NCP_SL2_Y - 26, vbl,
th->on_surface_variant, 18);
s_draw_slider(NCP_SLIDER_X, NCP_SL2_Y, NCP_SLIDER_W, vval, 0, vol_clamp(100), th);
if (g_ncp_widget_init && g_ncp_sl_vol) { meow_slider_set(g_ncp_sl_vol, vval); meow_widget_draw(g_ncp_sl_vol); }
/* 分隔线 */
fill_rect(12, NCP_DIV_Y, W - 24, 1, th->outline);
@@ -833,8 +1050,8 @@ static void draw_notif_center(int scroll, int redraw_sb)
ncp_draw_list(scroll);
/* 底部提示 */
ui_draw((W - ui_width("上滑或点空白关闭", 16)) / 2, NCP_FOOT_Y, "上滑或点空白关闭",
th->on_surface_variant, 16);
ui_draw((W - ui_width(meow_str_zh("ncp.swipe_close", "上滑或点空白关闭"), 16)) / 2, NCP_FOOT_Y,
meow_str_zh("ncp.swipe_close", "上滑或点空白关闭"), th->on_surface_variant, 16);
/* 状态栏画在面板体之上(0-27 由 SDK 全局状态栏维护,经 draw_statusbar→sb_sdk_refresh
* 喂数据+标脏顶条;不落面板体脏区)。仅 redraw_sb=1(打开/主题变化)才喂/标脏,交互时跳过
@@ -968,16 +1185,10 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
int scroll_invert = 0; /* input.swipe_invert(0=标准方向) */
int scroll_rebound = 0; /* 拖到顶/底仍继续拖(回弹)→ 抬手按关闭语义收起 */
int list_paint = 0; /* 仅列表区重绘(滚动专用,不整面板/不刷状态栏) */
/* ⑦ 单条通知行左滑删除(跟手+半程可回+回弹;绘制态见 g_ncp_swipe_row/off):
* row_swipe_row = 正在左滑的行下标(-1=无);row_off = 行左移量(px,<=0,跟手);
* row_settle = 0 跟手 / 1 松手后向 row_off_target 回弹或滑出;row_anim_at = 落位动画计时。
* row_paint = 仅重绘该行带(跟手局部 blit/位移,不整面板/不刷状态栏)。 */
int row_swipe_row = -1;
int row_off = 0;
int row_settle = 0;
int row_off_target = 0;
unsigned long long row_anim_at = 0;
int row_paint = 0; /* 仅重绘该行带(行左滑专用) */
/* ⑦ 单条通知行左滑删除(M7.3:迁 SDK meow_listview_set_swipe_delete + swipe_begin/move/end)。
* sdk_swipe = 本触摸已交给 SDK 行左滑手势(begin 命中可滑行);SDK 负责方向仲裁(横向=消费 /
* 纵向=让位滚动)、删除/回弹动画(meow_anim_tick 推进)与 on_delete 回调。 */
int sdk_swipe = 0;
/* ⑨ 面板跟手下拉、半程可回:
* panel_drag = 1 面板跟手拖动中(anim_p 直接由手指位移映射,状态时间轴暂停);
* drag_seed_reveal = 进入拖动时锁定的面板「露出高度」(= ph + off,防触发瞬间跳变);
@@ -1018,6 +1229,9 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
g_ncp_dnd = d;
}
/* M7.3:建内层 SDK 控件集(listview/双滑条/勿扰开关/芯片;owner 钉哨兵,仅首进一次)。 */
ncp_widgets_create();
/* 首次绘制:先拍背景(应用列表)→ 渲染最终位置整面板 → 拍面板快照 → 还原背景到 p=0
* 起点(面板隐藏、先露应用列表),随后进入循环由动画推进 p:0→FIX 滑入。 */
ncp_refresh_rows();
@@ -1092,9 +1306,7 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
td = 0; td_ok = 0; drag_slider = 0; tlx = tly = -1;
scroll_drag = 0; scroll_base = 0; scroll_y0 = 0;
scroll_invert = 0; scroll_rebound = 0; list_paint = 0;
row_swipe_row = -1; row_off = 0; row_settle = 0;
row_off_target = 0; row_anim_at = 0; row_paint = 0;
g_ncp_swipe_row = -1; g_ncp_swipe_off = 0;
sdk_swipe = 0; /* 唤醒:清行左滑手势(SDK swipe 内部状态亦重置) */
panel_drag = 0; drag_seed_reveal = 0; drag_y0 = 0; drag_paint = 0;
/* 唤醒:面板直接就位(不重播滑入动画,面板早已打开);重拍快照防内容已变 */
anim_p = anim_target = anim_last_target = NCP_ANIM_FIX;
@@ -1201,42 +1413,27 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
if (ncp_slider_band(tlx, tly, NCP_SL1_Y)) { drag_slider = 1; if (ncp_slider_apply(1, tlx, 0)) need_paint = 1; }
else if (ncp_slider_band(tlx, tly, NCP_SL2_Y)) { drag_slider = 2; if (ncp_slider_apply(2, tlx, 0)) need_paint = 1; }
}
/* M7.3:按下原点落在通知列表行 → 交 SDK 行左滑手势(begin 命中可滑行;
* 后续 move 由 SDK 做方向仲裁:横向=消费/纵向=让位滚动)。先同步 listview
* 滚动偏移(meow_listview_hit 用其内部 scroll)。 */
if (open_settled && !drag_slider && !panel_drag && g_ncp_widget_init && g_ncp_lv) {
meow_listview_set_scroll(g_ncp_lv, list_scroll);
sdk_swipe = meow_listview_swipe_begin(g_ncp_lv, tlx, tly);
}
}
if (drag_slider == 1 && td_ok) { if (ncp_slider_apply(1, tlx, 0)) need_paint = 1; }
else if (drag_slider == 2 && td_ok) { if (ncp_slider_apply(2, tlx, 0)) need_paint = 1; }
/* ⑦ 行左滑(进入):面板就位(open_settled)+ 未锁其它手势 + 位移达 slop,
* 判定横向主导(adx>ady)且左移(ddx<0)且起点落在可见行 → 进行左滑(只该行)。
* 与竖向滚动互斥:本分支锁 row_swipe_row 后下方 scroll 不再进入(gate 含
* row_swipe_row<0);竖向主导(ady>adx)或不左移则回退既有滚动(改变不大:
* 以前「横主导左移」也滚,现改为行左滑,符合「横向主导→行左滑」)。 */
if (open_settled && td_ok && !scroll_drag && drag_slider == 0 && row_swipe_row < 0) {
int ddx = tlx - tdx, ddy = tly - tdy;
int adx = ddx < 0 ? -ddx : ddx, ady = ddy < 0 ? -ddy : ddy;
if ((adx >= NCP_SWIPE_SLOP || ady >= S_SCROLL_SLOP) && adx > ady && ddx < 0) {
int eff = tdy - ncp_list_top() + list_scroll;
int row = mnp_row_at(eff, 0, g_nrows * NCP_ROW_H, NCP_ROW_H);
/* 起点必须在列表区(DND 带/底部提示等不在列表区,防 scroll 导致误匹行) */
if (tdy >= ncp_list_top() && tdy < NCP_LIST_BOT &&
row >= 0 && row < g_nrows) {
row_swipe_row = row;
row_off = mnp_swipe_clamp(ddx, NCP_ROW_W);
row_settle = 0; row_off_target = 0;
row_anim_at = now_ms();
g_ncp_swipe_row = row; g_ncp_swipe_off = row_off;
row_paint = 1;
}
}
}
/* ⑦ 行左滑跟手:已进则更新 off(反向可回 → 回落至 0);只该行生效。 */
if (row_swipe_row >= 0 && td_ok && !row_settle) {
row_off = mnp_swipe_clamp(tlx - tdx, NCP_ROW_W);
g_ncp_swipe_off = row_off;
row_paint = 1;
/* ⑦ 行左滑(SDK swipe-delete):已交给 SDK 手势 → 喂 move。SDK 方向仲裁:
* 横向小位移超容差 → 消费(返回 1,行左移/删除区由 SDK 画,本触摸不滚/不拖面板);
* 纵向主导 → 取消本行 swipe(返回 0,sdk_swipe=0),回退到下方滚动/面板拖动。 */
if (sdk_swipe && td_ok && drag_slider == 0) {
meow_listview_set_scroll(g_ncp_lv, list_scroll);
if (!meow_listview_swipe_move(g_ncp_lv, tlx, tly)) sdk_swipe = 0;
}
/* ⑨ 面板跟手拖动(进入):竖向位移达 slop、起点不在列表溢出滚动区(归 §11)、
* 未锁行左滑/滚动/滑条 → 进入拖动。锁定当前面板露出高度 + 当前手指 y,
* 此后 reveal = seed + (tly - drag_y0),方向无关、连续可反向(半程可回)。 */
if (td_ok && !drag_slider && !scroll_drag && row_swipe_row < 0 && !panel_drag) {
if (td_ok && !drag_slider && !scroll_drag && !sdk_swipe && !panel_drag) {
int ady2 = (tly - tdy) < 0 ? (tdy - tly) : (tly - tdy);
if (ady2 >= S_SCROLL_SLOP) {
int av = g_nrows * NCP_ROW_H;
@@ -1260,7 +1457,7 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
/* 列表滚动(§11):面板就位(open_settled)+ 起点在列表区 + 内容溢出 +
* 纵向位移达 slop → 进滚动。方向遵循 input.swipe_invert(与设置页列表同式);
* 拖到顶/底继续拖 → 回弹标记。动画进行中(面板在滑,未就位)不启动滚动。 */
if (open_settled && td_ok && !scroll_drag && drag_slider == 0 && row_swipe_row < 0) {
if (open_settled && td_ok && !scroll_drag && drag_slider == 0 && !sdk_swipe) {
int ddy = tly - scroll_y0;
if (ddy >= S_SCROLL_SLOP || ddy <= -S_SCROLL_SLOP) {
int total = g_nrows * NCP_ROW_H;
@@ -1290,10 +1487,8 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
td = 1; td_ok = 0; tlx = -1; tly = -1; drag_slider = 0;
scroll_drag = 0; scroll_base = 0; scroll_y0 = 0;
scroll_invert = 0; scroll_rebound = 0;
/* ⑦ 新触摸:清行左滑(防上一手势残留) */
row_swipe_row = -1; row_off = 0; row_settle = 0;
row_off_target = 0; row_anim_at = 0; row_paint = 0;
g_ncp_swipe_row = -1; g_ncp_swipe_off = 0;
/* ⑦ 新触摸:清行左滑手势(防上一手势残留) */
sdk_swipe = 0;
/* ⑨ 新触摸:清面板跟手(防上一拖动残留) */
panel_drag = 0; drag_seed_reveal = 0; drag_y0 = 0; drag_paint = 0;
}
@@ -1311,19 +1506,18 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
if (anim_ok) anim_target = 0; else closed = 1;
break;
}
if (row_swipe_row >= 0) {
/* ⑦ 行左滑松手:过阈值 → 滑出删除;未过/反向 → 回弹复原(半程取消)。
* 只该行生效;本手势锁定后不再走 tap/上滑关闭/点空白,不关面板。 */
/* 用抬手最终位移兜底刷新 off(可能最后有位移但无 ABS 事件跟进)。 */
row_off = mnp_swipe_clamp(dx, NCP_ROW_W);
g_ncp_swipe_off = row_off;
if (mnp_swipe_delete(row_off, NCP_ROW_W,
NCP_SWIPE_DELETE_FRAC, NCP_SWIPE_DELETE_MIN)) {
row_off_target = -NCP_ROW_W; /* 滑出(删除)目标:整行移出 */
} else {
row_off_target = 0; /* 回弹复原 */
if (sdk_swipe && g_ncp_widget_init && g_ncp_lv) {
/* ⑦ 行左滑松手(SDK swipe-delete):消费 → 删除/回弹动画交 SDK
* (meow_anim_tick 推进;删除完成后 ncp_lv_on_delete 置 g_ncp_need_paint)。
* 本手势不关面板、不走 tap/上滑/点空白。未滑动(=点击某行)→ 未消费,
* 清 sdk_swipe 后走下方 tap 分支(ncp_row_tap 命中该行 → dismiss+关面板)。 */
meow_listview_set_scroll(g_ncp_lv, list_scroll);
if (meow_listview_swipe_end(g_ncp_lv, tlx, tly)) {
sdk_swipe = 0;
td_ok = 0;
goto ncp_release_done;
}
row_settle = 1; row_anim_at = now_ms();
sdk_swipe = 0;
} else if (scroll_drag) {
/* 列表滚动落定:最终 clamp 到边界(拖动中已实时跟手,此处确认终值);
* 若回弹(拖到顶/底继续拖)→ 按「上滑/下滑关闭」收起(§11.3)。
@@ -1395,10 +1589,8 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
if (!hit && ncp_clear_hit(tx, ty)) {
int n = ncp_clear_all();
logmsg("notif-center: clear %d notif(s) (dumped first, kept ongo/media/progress)", n);
/* 清除后取消行左滑(下标会变),再重采集 + 重钳 + 整面板重画。 */
row_swipe_row = -1; row_off = 0; row_settle = 0;
row_off_target = 0; row_anim_at = 0; row_paint = 0;
g_ncp_swipe_row = -1; g_ncp_swipe_off = 0;
/* 清除后取消行左滑手势(下标会变),再重采集 + 重钳 + 整面板重画。 */
sdk_swipe = 0;
ncp_refresh_rows();
list_scroll = mnp_scroll_clamp(list_scroll, g_nrows * NCP_ROW_H,
ncp_list_view());
@@ -1430,6 +1622,8 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
/* 其余横滑/下滑忽略(不关闭不动作) */
}
td_ok = 0;
ncp_release_done:
;
}
}
}
@@ -1453,10 +1647,8 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
media_paint = 1; /* 已有源:进度/封面随 META 变化 → 仅重绘卡片带 */
}
if (ncp_rows_changed()) {
/* ⑦ 列表已变(新条/撤下/TTL):取消当前行左滑,避免行下标错位。⑨ 同时取消面板跟手。 */
row_swipe_row = -1; row_off = 0; row_settle = 0;
row_off_target = 0; row_anim_at = 0; row_paint = 0;
g_ncp_swipe_row = -1; g_ncp_swipe_off = 0;
/* ⑦ 列表已变(新条/撤下/TTL):取消当前行左滑手势,避免行下标错位。⑨ 同时取消面板跟手。 */
sdk_swipe = 0;
panel_drag = 0; drag_seed_reveal = 0; drag_y0 = 0; drag_paint = 0;
ncp_refresh_rows();
list_scroll = mnp_scroll_clamp(list_scroll, g_nrows * NCP_ROW_H,
@@ -1490,41 +1682,8 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
}
if (anim_ok && !panel_drag && !td && anim_target == 0 && anim_p == 0) break; /* 滑出到 0 且无按下触摸 → 真正退出子循环 */
/* ⑦ 行左滑落位动画:row_settle=1 时按壁钟差向 row_off_target 线性步进(回弹→0 / 滑出→-row_w)。
* 到达目标:0=回弹完成(行复原,无需重排);-row_w=删除 → dismiss + 重采集 + 重排(need_paint)。
* 只该行生效;与面板开关动画(上块)互不干扰(行左滑仅在面板稳态 open_settled 下进行)。 */
if (row_swipe_row >= 0 && row_settle) {
unsigned long long rnow = now_ms();
int rdt = (int)((rnow > row_anim_at) ? (rnow - row_anim_at) : 0);
row_anim_at = rnow;
int noff = mnp_swipe_step(row_off, row_off_target, (unsigned long long)rdt, NCP_SWIPE_RATE);
if (noff != row_off) {
row_off = noff;
g_ncp_swipe_off = noff;
row_paint = 1;
}
if (noff == row_off_target) {
if (row_off_target == 0) {
/* 回弹复原完成:先把行重画回 0 位(清残留偏移),再清滑动态(无需重排)。 */
ncp_repaint_row(list_scroll, row_swipe_row, 0);
row_swipe_row = -1; row_settle = 0; row_off = 0;
row_off_target = 0; row_paint = 0;
g_ncp_swipe_row = -1; g_ncp_swipe_off = 0;
if (anim_ok) ncp_snap_panel(); /* 行已复原:刷新面板快照(供滑出动画用) */
} else {
/* 滑出删除完成:撤下该通知 + 重采集 + 重排列表(整面板重画)。 */
if (row_swipe_row >= 0 && row_swipe_row < g_nrows)
meow_notif_dismiss(g_rows[row_swipe_row].id);
row_swipe_row = -1; row_settle = 0; row_off = 0;
row_off_target = 0; row_paint = 0;
g_ncp_swipe_row = -1; g_ncp_swipe_off = 0;
ncp_refresh_rows();
list_scroll = mnp_scroll_clamp(list_scroll, g_nrows * NCP_ROW_H,
ncp_list_view());
need_paint = 1; /* 行已移除/重排:整面板(列表区)重画 */
}
}
}
/* M7.3:行左滑落位动画已由 SDK 接管(meow_listview_swipe_end → meow_anim → swipe_anim_done
* → on_delete(idx) → ncp_lv_on_delete 置 g_ncp_need_paint)。此处不再需要手绘行位移/回弹/滑出。 */
/* 重绘(面板体区域;状态栏 0-27 由 draw_statusbar 单独维护,不落入此脏区)。
* need_sb=1(打开/主题变化)时 draw_notif_center 内另绘状态栏并标其脏——脏区仍
@@ -1561,14 +1720,8 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
ncp_anim_frame(anim_p, &anim_prev_bot, anim_ok);
drag_paint = 0;
}
/* 仅重绘该行带(行左滑跟手/回弹/滑出专用:只刷该行矩形,不整面板/不刷状态栏;同受动画门控)。
* row_swipe_row<0(行已回弹/删除/被取消)则跳过并清标记,避免对已移除行重绘。 */
if (row_paint) {
last_act = now;
if (row_swipe_row >= 0 && !(anim_ok && anim_p != anim_target))
ncp_repaint_row(list_scroll, row_swipe_row, row_off);
row_paint = 0;
}
/* M7.3:行左滑跟手/回弹/滑出重绘已由 SDK 接管(swipe_anim_update 经 meow_widget_draw(lv)
* 重绘列表 + meow_anim_set_region 标该行带脏)。此处不再有 row_paint 手绘路径。 */
/* ~1s 状态栏时钟 */
if (now - last_tick >= 1000) {
last_tick = now;
@@ -1576,12 +1729,27 @@ void run_notif_center(struct app *apps, int napps, notif_click_t *click,
fb_norm_buf0();
meow_ui_flush();
}
/* M7.3:推进 SDK 动画(行左滑删除/回弹、meow_scroll fling 等)。左滑删除完成后 on_delete
* 置 g_ncp_need_paint → 下一帧整面板重画。swipe 动画推进时 SDK 已重绘列表(swipe_anim_update
* 调 meow_widget_draw(lv)),此处只把列表带标脏上屏(覆盖 SDK 经 meow_gfx 标脏的列表矩形)。 */
meow_anim_tick();
if (g_ncp_need_paint) { g_ncp_need_paint = 0; need_paint = 1; }
if (g_ncp_widget_init && g_ncp_lv && meow_listview_swipe_active(g_ncp_lv)) {
meow_dirty_add(0, ncp_list_top(), fb_xres, ncp_list_view());
meow_ui_flush();
}
usleep(POLL_MS * 1000);
}
if (tfd >= 0) close(tfd);
kp_close(&kp_power); kp_close(&kp_keys);
ncp_snap_free(); /* 释放动画快照(退出即回收,不驻留) */
ncp_rows_free();
/* 封面缓存随面板关闭回收(对齐 Now Playing np_cleanup:每次下拉重新加载当前封面)。
* 旧码 g_ncp_media_cover_path 是文件级 static、从不重置——若首次加载失败/或 artwork 初
* 为空的瞬间进面板,其后即便封面落盘并因路径不变而被「按文本缓存」跳过重载,卡片永久
* 停留在「显示歌名」回退/旧封面。关面板即清缓存,保证下次下拉能拿到最新封面。 */
if (g_ncp_media_cover) { meow_img_free(g_ncp_media_cover); g_ncp_media_cover = NULL; }
g_ncp_media_cover_path[0] = 0;
logmsg("notification center closed");
}