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

+196 -25
View File
@@ -116,6 +116,21 @@ struct meow_widget {
int show_icon; /* 1=画左图标/缩略图(默认 1) */
int show_right;/* 1=画右尾件(默认 1) */
char empty_text[64]; /* 空态占位文案(首字符 0=不画) */
/* §5.5 左滑删除(列表行横向手势:滑动偏移/删除动画/回弹;calloc 归零,安全)。
* 手势由 App 在 on_touch_move 喂(swipe_begin/move/end);横向小位移判 swipe、纵向滚动优先;
* 行自绘 set_item_draw 配合(M7.3 通知行)。swipe_anim 由 meow_anim 静态池驱动。 */
int swipe_enabled; /* 1=左滑删除可用(on_delete 回调有效) */
int swipe_active; /* 1=当前行处于滑动态(swipe_idx 有效) */
int swipe_idx; /* 正被滑动的行号(-1=无) */
int swipe_dx; /* 当前行横向偏移(<=0=左移;滑动/动画中) */
int swipe_drag; /* 1=手指拖动中(限行内) */
int swipe_start_x, swipe_start_y; /* 手势起点(判定方向用) */
int swipe_started; /* 1=已裁定为横向 swipe(纵向滚动手势被抑制) */
int swipe_anim_delete;/* 1=删除动画(目标 -w);0=回弹(目标 0) */
meow_listview_swipe_fn swipe_cb; /* 删除回调(idx) */
void *swipe_ctx;
meow_anim *swipe_anim; /* 滑动/回弹动画(非 NULL=进行中) */
};
/* ---- §5.8 batch1 内部辅助:opts 取值(opts_used==0 时按控件内建默认)----
@@ -1161,7 +1176,7 @@ meow_widget *meow_listview_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 > 0 ? count : 0; t->src2 = src; t->ctx = ctx;
t->sel = -1; t->item_h = 56; t->scroll = 0;
t->sel = -1; t->item_h = 56; t->scroll = 0; t->swipe_idx = -1;
t->sel_mode = (mode >= MEOW_LIST_SEL_NONE && mode <= MEOW_LIST_SEL_MULTI) ? mode : MEOW_LIST_SEL_NONE;
t->lines = 2; t->show_icon = 1; t->show_right = 1; /* 默认双行富 item + 图标 + 右尾件(src2 布局不变) */
if (t->sel_mode == MEOW_LIST_SEL_MULTI && t->count > 0) {
@@ -1393,6 +1408,127 @@ int meow_listview_set_empty_text(meow_widget *w, const char *text)
return 0;
}
/* ============================================================ */
/* §5.5 左滑删除(M7.3 缺口 #9):手势判定 + 删除/回弹动画 */
/* ============================================================ */
#define MEOW_SWIPE_SLOP 10 /* 手势判定容差(px):超过才裁定方向 */
/* 删除阈值:|dx| ≥ 行宽*2/5。 */
static int swipe_thresh(const meow_widget *w) { return w->w * 2 / 5; }
/* 动画更新:设行偏移 + 重绘列表(只画可见行;widget clip 已裁剪)。 */
static void swipe_anim_update(meow_anim *a, float v, void *ctx)
{
(void)a;
meow_widget *lv = (meow_widget *)ctx;
if (!lv) return;
lv->swipe_dx = (int)v;
meow_widget_draw(lv);
}
static void swipe_anim_done(meow_anim *a, int canceled, void *ctx)
{
(void)a;
meow_widget *lv = (meow_widget *)ctx;
if (!lv) return;
int del = lv->swipe_anim_delete;
int idx = lv->swipe_idx;
lv->swipe_anim = NULL;
lv->swipe_anim_delete = 0;
lv->swipe_active = 0; lv->swipe_idx = -1; lv->swipe_dx = 0;
if (canceled) return; /* 取消:回正常位(无回调) */
if (del && lv->swipe_cb) /* 删除:滑出完成 → 回调(App 移除行 + 刷新 count) */
lv->swipe_cb(idx, lv->swipe_ctx);
}
void meow_listview_set_swipe_delete(meow_widget *lv, int enabled,
meow_listview_swipe_fn on_delete, void *ctx)
{
if (!lv || lv->type != W_LISTVIEW) return;
if (lv->swipe_anim) { meow_anim_cancel(lv->swipe_anim); lv->swipe_anim = NULL; }
lv->swipe_enabled = enabled ? 1 : 0;
lv->swipe_cb = on_delete; lv->swipe_ctx = ctx;
lv->swipe_active = 0; lv->swipe_idx = -1; lv->swipe_dx = 0; lv->swipe_drag = 0; lv->swipe_started = 0;
}
int meow_listview_swipe_begin(meow_widget *lv, int x, int y)
{
if (!lv || lv->type != W_LISTVIEW || !lv->swipe_enabled) return 0;
int idx = meow_listview_hit(lv, x, y);
if (idx < 0) return 0;
if (lv->swipe_anim) return 1; /* 已有动画:吞掉,不干扰当前行 */
lv->swipe_drag = 1; lv->swipe_started = 0;
lv->swipe_idx = idx; lv->swipe_dx = 0;
lv->swipe_start_x = x; lv->swipe_start_y = y;
return 1;
}
int meow_listview_swipe_move(meow_widget *lv, int x, int y)
{
if (!lv || lv->type != W_LISTVIEW || !lv->swipe_drag) return 0;
int dx = x - lv->swipe_start_x, dy = y - lv->swipe_start_y;
int adx = dx < 0 ? -dx : dx, ady = dy < 0 ? -dy : dy;
if (!lv->swipe_started) {
if (ady > adx && ady > MEOW_SWIPE_SLOP) { /* 纵向优先:取消 swipe,让位滚动 */
lv->swipe_drag = 0; lv->swipe_active = 0; lv->swipe_idx = -1; lv->swipe_dx = 0;
return 0;
}
if (adx > MEOW_SWIPE_SLOP && adx > ady) { /* 横向小位移:裁定为 swipe */
lv->swipe_started = 1; lv->swipe_active = 1;
} else {
return 0; /* 太小未裁定:暂不消费 */
}
} else {
if (ady > adx && ady > MEOW_SWIPE_SLOP) { /* 中途纵向接管:取消,让位滚动 */
lv->swipe_drag = 0; lv->swipe_active = 0; lv->swipe_idx = -1; lv->swipe_dx = 0;
return 0;
}
}
/* 只左滑(dx<0)露右删除区;右滑 clamp 到 0;触底 clamp 到 -w。 */
int off = dx < 0 ? dx : 0;
if (off < -lv->w) off = -lv->w;
if (off == lv->swipe_dx) return 1;
lv->swipe_dx = off;
meow_gfx_mark_dirty(lv->x, lv->y, lv->w, lv->h);
return 1;
}
int meow_listview_swipe_end(meow_widget *lv, int x, int y)
{
(void)x; (void)y;
if (!lv || lv->type != W_LISTVIEW) return 0;
if (!lv->swipe_drag) return 0; /* 非 swipe 行(tap/滚动)→ App 自理 */
lv->swipe_drag = 0;
if (!lv->swipe_started) return 0; /* 无位移 = 点击 → App 走 tap_at */
int dx = lv->swipe_dx;
int del = (-dx >= swipe_thresh(lv)); /* 超阈值 → 删除;否则回弹 */
int item = listv_item_h(lv);
int ry = lv->y + ((unsigned)lv->swipe_idx * (unsigned)item - lv->scroll);
if (lv->swipe_anim) { meow_anim_cancel(lv->swipe_anim); lv->swipe_anim = NULL; }
lv->swipe_anim_delete = del;
meow_anim_desc d; memset(&d, 0, sizeof d);
d.from = (float)dx;
d.to = (float)(del ? -lv->w : 0);
d.duration_ms = del ? 160u : 140u;
d.ease = del ? MEOW_EASE_OUT : MEOW_EASE_OVERSHOOT;
d.update = swipe_anim_update; d.done = swipe_anim_done; d.ctx = lv;
meow_anim *an = meow_anim_start(&d);
if (!an) { /* 池满:直接终态(防卡死) */
if (del) { int i = lv->swipe_idx; lv->swipe_idx = -1; lv->swipe_dx = 0;
if (lv->swipe_cb) lv->swipe_cb(i, lv->swipe_ctx); }
lv->swipe_active = 0; lv->swipe_dx = 0;
meow_gfx_mark_dirty(lv->x, lv->y, lv->w, lv->h);
return 1;
}
lv->swipe_anim = an;
meow_anim_set_region(an, lv->x, ry < lv->y ? lv->y : ry, lv->w, item); /* 动画帧只刷该行带 */
return 1;
}
int meow_listview_swipe_active(meow_widget *lv)
{ return (lv && lv->type == W_LISTVIEW && (lv->swipe_active || lv->swipe_anim)) ? 1 : 0; }
int meow_listview_swipe_offset(meow_widget *lv)
{ return (lv && lv->type == W_LISTVIEW) ? lv->swipe_dx : 0; }
static int hit(meow_widget *w, int x, int y)
{
if (!w) return 0;
@@ -1669,7 +1805,17 @@ void meow_widget_draw(meow_widget *w)
meow_input_touch_x() >= vx0 && meow_input_touch_x() < vx0 + w->w &&
meow_input_touch_y() >= ry && meow_input_touch_y() < ry + item)
st |= MEOW_LISTVIEW_ST_PRESSED;
w->vs_draw(i, vx0, ry, w->w, item, st, w->vs_ctx);
int rx0 = vx0; /* 左滑行:行 x 左移 + 右露删除区 */
if (w->swipe_active && w->swipe_idx == i && w->swipe_dx < 0) {
meow_ui_fill_rect(vx0 + w->w + w->swipe_dx, ry, -w->swipe_dx, item, th->error);
int thh2 = meow_ui_text_height(th->font_body);
int dly = ry + (item - thh2) / 2;
if (dly >= vy0 && dly + thh2 <= vy1)
meow_ui_text_center(vx0 + w->w + w->swipe_dx + (-w->swipe_dx) / 2, dly,
meow_str("common.delete"), th->font_body, th->onError);
rx0 = vx0 + w->swipe_dx;
}
w->vs_draw(i, rx0, ry, w->w, item, st, w->vs_ctx);
}
} else { /* ② 默认 renderer:固定 item 池(进入视口才 bind) */
for (int i = first; i < last; i++) {
@@ -1684,23 +1830,37 @@ void meow_widget_draw(meow_widget *w)
int press = meow_input_touch_down() &&
meow_input_touch_x() >= vx0 && meow_input_touch_x() < vx0 + w->w &&
meow_input_touch_y() >= ry && meow_input_touch_y() < ry + item;
/* 左滑删除(M7.3):本行被滑动 → 右露删除区 + 行 x 左移。非滑动行 shift=0 → 输出不变。 */
int shift = 0;
if (w->swipe_active && w->swipe_idx == i && w->swipe_dx < 0) {
meow_ui_fill_rect(vx0 + w->w + w->swipe_dx, ry, -w->swipe_dx, item, th->error);
int thh2 = meow_ui_text_height(th->font_body);
int dly = ry + (item - thh2) / 2;
if (dly >= vy0 && dly + thh2 <= vy1)
meow_ui_text_center(vx0 + w->w + w->swipe_dx + (-w->swipe_dx) / 2, dly,
meow_str("common.delete"), th->font_body, th->onError);
shift = w->swipe_dx;
}
int rlx = vx0 + shift; /* 行内容左移后的 x */
int rtx = text_x + shift; /* 文本 x(随行左移) */
int rop = op_start + shift; /* 操作区分界 x */
if (it->section) { /* 段头占位行(非粘性):surfaceContainerHigh 底 + primary 段名 */
meow_ui_fill_rect(vx0, cl, w->w, cr - cl, th->surfaceContainerHigh);
meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->surfaceContainerHigh);
int sty = ry + (item - thh) / 2;
if (it->title && sty >= vy0 && sty + thh <= vy1)
listv_text_trunc(text_x, sty, it->title, text_maxw, th->font_body, th->primary);
listv_text_trunc(rtx, sty, it->title, text_maxw, th->font_body, th->primary);
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(vx0, sy, w->w, 1, th->outlineVariant);
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
if (!it->enabled)
meow_ui_fill_rect_alpha(vx0, cl, w->w, cr - cl, th->onSurface, 90);
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
continue;
}
/* 选中/按压背景(primaryContainer / 半透明主题色按压层) */
if (sel) meow_ui_fill_rect(vx0, cl, w->w, cr - cl, th->primaryContainer);
else if (press) meow_ui_fill_rect_alpha(vx0, cl, w->w, cr - cl, th->primary, 26);
if (sel) meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->primaryContainer);
else if (press) meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->primary, 26);
uint32_t textcol = sel ? th->onPrimaryContainer : th->onSurface;
uint32_t subcol = sel ? th->onPrimaryContainer : th->onSurfaceVariant;
@@ -1711,38 +1871,38 @@ void meow_widget_draw(meow_widget *w)
: th->onSurfaceVariant);
int icy = ry + (item - isz) / 2;
if (icy + isz >= vy0 && icy < vy1)
listv_draw_icon(th, vx0 + pad, icy, it->icon_kind, it->icon, iccol);
listv_draw_icon(th, rlx + pad, icy, it->icon_kind, it->icon, iccol);
}
/* 标题 + 副标题(双行居中;单行(lines==1 或无副标题)单行居中);越界不画 */
if (w->lines == 2 && it->subtitle && it->subtitle[0]) {
int ty = ry + (item - (2 * thh + MEOW_LISTV_SUB_GAP)) / 2;
if (it->title && ty >= vy0 && ty + thh <= vy1)
listv_text_trunc(text_x, ty, it->title, text_maxw, th->font_body, textcol);
listv_text_trunc(rtx, ty, it->title, text_maxw, th->font_body, textcol);
int sy = ty + thh + MEOW_LISTV_SUB_GAP;
if (sy >= vy0 && sy + thh <= vy1)
listv_text_trunc(text_x, sy, it->subtitle, text_maxw, th->font_body, subcol);
listv_text_trunc(rtx, sy, it->subtitle, text_maxw, th->font_body, subcol);
} else {
int ty = ry + (item - thh) / 2;
if (it->title && ty >= vy0 && ty + thh <= vy1)
listv_text_trunc(text_x, ty, it->title, text_maxw, th->font_body, textcol);
listv_text_trunc(rtx, ty, it->title, text_maxw, th->font_body, textcol);
}
/* 右尾件(show_right=1:箭头/开关/复选/⋯) */
if (w->show_right && it->right != MEOW_LIST_RIGHT_NONE) {
int ax = w->x + w->w - pad - 24; /* 右缘动作槽(约 24px) */
if (ax >= op_start)
int ax = rlx + w->w - pad - 24; /* 右缘动作槽(约 24px) */
if (ax >= rop)
listv_draw_right(th, ax, ry, item, it->right, it->on, subcol);
}
/* 分隔线(仅落在可见带内才画) */
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(vx0, sy, w->w, 1, th->outlineVariant);
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
/* 禁用行整体变淡(§5.5/§5.8.3 enabled=0 → 变暗) */
if (!it->enabled)
meow_ui_fill_rect_alpha(vx0, cl, w->w, cr - cl, th->onSurface, 90);
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
}
}
} else {
@@ -1760,7 +1920,18 @@ void meow_widget_draw(meow_widget *w)
int sel = (i == w->sel);
uint32_t textcol = sel ? th->onPrimaryContainer : th->onSurface;
uint32_t subcol = sel ? th->onPrimaryContainer : th->onSurfaceVariant;
if (sel) meow_ui_fill_rect(vx0, cl, w->w, cr - cl, th->primaryContainer);
/* 左滑删除(M7.3):本行被滑动 → 右露删除区 + 行 x 左移。非滑动行 shift=0 → 输出不变。 */
int shift = 0, rlx = vx0, rtx = text_x, rop = op_start;
if (w->swipe_active && w->swipe_idx == i && w->swipe_dx < 0) {
meow_ui_fill_rect(vx0 + w->w + w->swipe_dx, ry, -w->swipe_dx, item, th->error);
int thh2 = meow_ui_text_height(th->font_body);
int dly = ry + (item - thh2) / 2;
if (dly >= vy0 && dly + thh2 <= vy1)
meow_ui_text_center(vx0 + w->w + w->swipe_dx + (-w->swipe_dx) / 2, dly,
meow_str("common.delete"), th->font_body, th->onError);
shift = w->swipe_dx; rlx = vx0 + shift; rtx = text_x + shift; rop = op_start + shift;
}
if (sel) meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->primaryContainer);
/* 图标(左) */
uint32_t iccol = sel ? th->onPrimaryContainer
@@ -1768,38 +1939,38 @@ void meow_widget_draw(meow_widget *w)
: th->onSurfaceVariant);
int icy = ry + (item - isz) / 2;
if (icy + isz >= vy0 && icy < vy1) /* 图标不越视口边界才画(防泄漏) */
listv_draw_icon(th, vx0 + pad, icy, it.icon_kind, it.icon, iccol);
listv_draw_icon(th, rlx + pad, icy, it.icon_kind, it.icon, iccol);
/* 标题 + 副标题(两行居中;无副标题=单行居中;越界不画防泄漏) */
int ty;
if (it.subtitle && it.subtitle[0]) {
ty = ry + (item - (2 * thh + MEOW_LISTV_SUB_GAP)) / 2;
if (it.title && ty >= vy0 && ty + thh <= vy1)
listv_text_trunc(text_x, ty, it.title, text_maxw, th->font_body, textcol);
listv_text_trunc(rtx, ty, it.title, text_maxw, th->font_body, textcol);
int sy = ty + thh + MEOW_LISTV_SUB_GAP;
if (sy >= vy0 && sy + thh <= vy1)
listv_text_trunc(text_x, sy, it.subtitle, text_maxw, th->font_body, subcol);
listv_text_trunc(rtx, sy, it.subtitle, text_maxw, th->font_body, subcol);
} else {
ty = ry + (item - thh) / 2;
if (it.title && ty >= vy0 && ty + thh <= vy1)
listv_text_trunc(text_x, ty, it.title, text_maxw, th->font_body, textcol);
listv_text_trunc(rtx, ty, it.title, text_maxw, th->font_body, textcol);
}
/* 右侧操作区 */
if (it.right != MEOW_LIST_RIGHT_NONE) {
int ax = w->x + w->w - pad - 24; /* 右缘动作槽(约 24px) */
if (ax >= op_start)
int ax = rlx + w->w - pad - 24; /* 右缘动作槽(约 24px) */
if (ax >= rop)
listv_draw_right(th, ax, ry, item, it.right, it.on, subcol);
}
/* 分隔线(仅落在可见带内才画) */
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(vx0, sy, w->w, 1, th->outlineVariant);
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
/* 禁用行整体变淡(§5.5/§5.8.3 enabled=0 → 变暗) */
if (!it.enabled)
meow_ui_fill_rect_alpha(vx0, cl, w->w, cr - cl, th->onSurface, 90);
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
}
}
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);