通知中心 ⑥⑦: 电源菜单chip + 勿扰面板内开关 + 单条左滑删除(agent-132)
- ⑥ 快捷行 5chip: WiFi/蓝牙/亮度/主题/电源菜单; 电源菜单chip点击=唤起 show_power_menu(真弹) + after_modal 还原 - 勿扰=面板内开关(notify.dnd bool, 零旁路), ncp_draw_list 按 urgency>=2 过滤; NCP_LIST_TOP 308 - ⑦ 单条通知行左滑删除: 跟手(mnp_swipe_clamp)+半程可回(未过阈值回弹), 过阈值滑出删除(mnp_swipe_delete/step) - 手势区分: 行内左滑=删该条, 竖向=滚动, meow_edge_back(左缘<24) 优先; 局部刷该行带 - meow_ui.a 零改动(d249c6e6); launcher=115e2b1d; 宿主147断言PASS; 新增0warn - SYSTEM_SETTINGS §1 加 notify.dnd; NOTIFICATION_SYSTEM §9 增 ⑥/⑦ 定案
This commit is contained in:
1 parent
a8ec8377ed
commit
82b77d64b4
7 files changed
+452
-46
No files matched your search
@@ -71,6 +71,8 @@ target_activity(可选, 来自 hints) / progress(可选 int) / urgency(0-3, 缺
|
||||
- **渲染主体 = 系统进程(meow_launcherd),非 APP**:通知中心 UI 由系统进程负责渲染(复用 SDK 控件/状态栏基底);**APP 进程不渲染通知中心**(系统级控件仅内部,见 MEOW_APP_SDK §6.1 ③、FRAMEWORK_DESIGN §10.2)。
|
||||
- **数据来源**:通知列表经 IPC 从通知存储/通知服务读(如 `MSG_NOTIFY_LIST` 重同步、`target_activity`/`progress`/`icon` 等字段),launcher 只做 UI 呈现与手势,不各画各的。
|
||||
- **交互**:**状态栏下滑展开**(用户已定);**上滑 / 点面板外关**;**左缘返回关**(与全局左缘返回/顶部滑条按起始位置分流,见 spec-ref §5.1)。列表显示通知(app 图标 + summary + body + target Activity),点击按路由表解析跳转(M3 导航栈),下拉关闭/清除。
|
||||
- **⑥ 快捷 chip + 面板内开关(launcher 侧,用户拍板 2026-08-30)**:快捷行为 **5 个**(WiFi/蓝牙/亮度/主题/**电源菜单**——第 5 个 chip 点击**唤起既有系统电源菜单** `show_power_menu`,真弹对话框);**勿扰不做 chip**,改**通知中心面板内开关**(列表上方小 toggle),开=只显示 `urgency>=2`(HIGH/CRITICAL),状态存 `notify.dnd`(SYSTEM_SETTINGS §1),面板内读写 `meow_settings_*` 零旁路。
|
||||
- **⑦ 单条通知左滑删除(launcher 侧,用户拍板 2026-08-30)**:通知行**从右往左横向滑动删除**,跟手 + 半程可回(未过阈值/反向滑回 → 回弹复原);行内左滑=删该条、竖向=列表滚动、`meow_edge_back`(左缘起点)优先不变,滑动只在该行生效;松手过阈值 → `meow_notif_dismiss` + 行移除/重排。
|
||||
- **协议/约束**:遵守 freedesktop 字段(summary/body/app_icon/actions/hints/expire_timeout/replaces_id)、配额(每 App 4 类型×每类型 4、媒体单例)、TTL、转储(10s + `MeowOS/notifications/` 10MB ring)、`urgency`/`icon`/`progress`(见 §5-§8)。
|
||||
- **依赖**:M2 存储 + M3 导航栈(点击跳转)+ SDK 控件基座。**阶段 B 进行中。**(叠加/与播放器帧备份的互斥边界见 DEV_NOTES #16。)
|
||||
- **App 前台变体(跨进程,[源:2026-08-30])**:全屏 App 前台时,App 进程内顶部下滑也能唤起通知中心——**App
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
| | `notify.categories` | int | 4 | 通知类型数上限(每 App,M2.53)[源:审计 2026-08-30]|
|
||||
| | `notify.category_quota` | int | 4 | 每类型活动通知上限(4×4=16/App)|
|
||||
| | `notify.media_quota` | int | 1 | 媒体通知单例(每 App 仅 1 条)|
|
||||
| | `notify.dnd` | bool | false | 通知中心「勿扰」面板内开关(通知列表上方小 toggle;开=只显示 `urgency>=2`(HIGH/CRITICAL),关=全部;面板内读写 `meow_settings_*`,零旁路)[源:用户 2026-08-30] |
|
||||
| **日志** | `log.level` | enum | `info` | `error/warn/info/debug` |
|
||||
| | `log.to_card` | bool | true | 写 TF 卡(无卡回退内存环) |
|
||||
| **音频** | `audio.volume` | int 0-100 | 60 | 系统音量(MeowOS 直写 CS43198 DAC;读回为追踪值) |
|
||||
|
||||
Binary file not shown.
@@ -6779,13 +6779,15 @@ done:
|
||||
#define NCP_CHIP_H 44
|
||||
#define NCP_CHIP_W 70
|
||||
#define NCP_CHIP_GAP 8
|
||||
#define NCP_CHIP_N 4
|
||||
#define NCP_CHIP_N 5 /* ⑥:快捷 chip 扩充为 5 个(+电源菜单) */
|
||||
#define NCP_SLIDER_X 20
|
||||
#define NCP_SLIDER_W (fb_xres - 40)
|
||||
#define NCP_SL1_Y 162 /* 亮度滑条 track 顶(label 在 -26 =136) */
|
||||
#define NCP_SL2_Y 214 /* 音量滑条 track 顶(label 在 -26 =188) */
|
||||
#define NCP_DIV_Y 258
|
||||
#define NCP_LIST_TOP 266
|
||||
#define NCP_DND_Y 264 /* ⑥:勿扰面板内开关带顶缘(分隔线下方) */
|
||||
#define NCP_DND_H 40 /* 勿扰开关带高(label+状态+s_draw_switch 48×28) */
|
||||
#define NCP_LIST_TOP 308 /* 通知列表顶缘(原 266 → 下移 42 给勿扰带) */
|
||||
#define NCP_LIST_BOT 694
|
||||
#define NCP_ROW_H 92
|
||||
#define NCP_ROW_X 12
|
||||
@@ -6798,13 +6800,22 @@ done:
|
||||
#define NCP_SCROLL_REBOUND_TOL 48 /* px:列表拖到顶/底仍继续拖过此容差 → 判回弹(关闭语义),
|
||||
避免在边界小幅抖动即误关(> S_SCROLL_SLOP=40) */
|
||||
|
||||
/* ---- ⑦ 单条通知行左滑删除(跟手 + 半程可回 + 回弹)---- */
|
||||
#define NCP_SWIPE_SLOP 24 /* px:行内水平位移达此值且横向主导+左移 → 进行左滑(>阈值) */
|
||||
#define NCP_SWIPE_RATE 3 /* px/ms:行左滑落位动画线性速率(回弹/滑出) */
|
||||
#define NCP_SWIPE_DELETE_FRAC 40 /* %:行宽百分比阈值(│off│>=row_w*40/100 → 删) */
|
||||
#define NCP_SWIPE_DELETE_MIN 110 /* px:绝对像素阈值(│off│>=110 → 删;OR 语义,窄行由 frac 起作用) */
|
||||
|
||||
/* 勿扰过滤阈值:dnd=1 只显示 urgency>=2(HIGH=2/CRITICAL=3) */
|
||||
#define NCP_DND_MIN_URGENCY 2
|
||||
|
||||
/* ---- 开关平移动画(§9:状态时间轴 + 快照 blit 平移,不整屏重绘)---- */
|
||||
#define NCP_ANIM_FIX 1024 /* 进度 p 的定点分母(1/1024 精度) */
|
||||
#define NCP_ANIM_MS 180 /* 单程打开/关闭时长(ms,真机可调) */
|
||||
|
||||
/* chip 索引 */
|
||||
/* chip 索引(⑥:第 5 个 = 电源菜单,点击唤起既有系统电源菜单 show_power_menu) */
|
||||
enum { NCP_CHIP_WIFI=0, NCP_CHIP_BT=1, NCP_CHIP_THEME=2, NCP_CHIP_BRIGHT=3,
|
||||
NCP_CHIP_COUNT=4 };
|
||||
NCP_CHIP_POWER=4, NCP_CHIP_COUNT=5 };
|
||||
|
||||
/* 通知行快照(面板读取字段 + 缓存的 icon 句柄)。 */
|
||||
typedef struct {
|
||||
@@ -6825,6 +6836,17 @@ static int g_nrows = 0;
|
||||
static struct app *g_ncp_apps = NULL; /* run_notif_center 注入(通知点击找 app 用) */
|
||||
static int g_ncp_napps = 0;
|
||||
|
||||
/* ⑥ 勿扰面板内开关:当前 dnd 态(开面板/切换时从 meow_settings notify.dnd 读/写,零旁路)。
|
||||
* ncp_collect_cb / ncp_active_count 按它过滤行(only urgency>=NCP_DND_MIN_URGENCY when on)。 */
|
||||
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 加参数。 */
|
||||
static int g_ncp_swipe_row = -1;
|
||||
static int g_ncp_swipe_off = 0;
|
||||
|
||||
/* 短串拷贝(同 meow_notif.c 语义:保证 NUL + 截断) */
|
||||
static void ncp_strncpy(char *dst, const char *src, size_t len)
|
||||
{
|
||||
@@ -6859,6 +6881,8 @@ static int ncp_collect_cb(const meow_notif_t *n, void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
if (g_nrows >= NCP_MAX_ROWS) return 1; /* 满:中止遍历 */
|
||||
/* 勿扰(⑥):dnd 开时只收集 urgency>=NCP_DND_MIN_URGENCY(HIGH/CRITICAL)。 */
|
||||
if (!mnp_dnd_should_show(n->urgency, g_ncp_dnd)) return 0;
|
||||
ncp_row_t *r = &g_rows[g_nrows++];
|
||||
memset(r, 0, sizeof *r);
|
||||
r->id = n->id;
|
||||
@@ -6890,12 +6914,25 @@ static int ncp_refresh_rows(void)
|
||||
return g_nrows;
|
||||
}
|
||||
|
||||
/* 通知列表是否变化(简单按活跃数比;新条/撤下/TTL 释放都反映在 count)。 */
|
||||
/* 当前应显示的活跃通知数(按 g_ncp_dnd 过滤,与 g_rows 同口径)——供变化检测/重采集用。 */
|
||||
static int ncp_count_cb(const meow_notif_t *n, void *ctx)
|
||||
{
|
||||
if (!mnp_dnd_should_show(n->urgency, g_ncp_dnd)) return 0;
|
||||
(*(int *)ctx)++;
|
||||
return 0;
|
||||
}
|
||||
static int ncp_active_count(void)
|
||||
{
|
||||
int c = 0;
|
||||
meow_notif_list(NULL, ncp_count_cb, &c);
|
||||
if (c > NCP_MAX_ROWS) c = NCP_MAX_ROWS;
|
||||
return c;
|
||||
}
|
||||
|
||||
/* 通知列表是否变化(按过滤后活跃数比;新条/撤下/TTL 释放都反映在 count,含 dnd 过滤)。 */
|
||||
static int ncp_rows_changed(void)
|
||||
{
|
||||
int c = meow_notif_count(NULL);
|
||||
if (c > NCP_MAX_ROWS) c = NCP_MAX_ROWS;
|
||||
return c != g_nrows;
|
||||
return ncp_active_count() != g_nrows;
|
||||
}
|
||||
|
||||
/* ---- 开关平移动画辅助(§9:快照 blit + 平移;只在 run_notif_center 内分配/释放)----
|
||||
@@ -7057,11 +7094,16 @@ static void ncp_chip_state(int chip, int *on, char *val, int vlen)
|
||||
int vc = mnp_brightness_clamp(v, vmin);
|
||||
if (val && vlen > 0) snprintf(val, vlen, "%d%%", vc);
|
||||
*on = (vc >= 50); /* 过半视为"亮"态 */
|
||||
} else if (chip == NCP_CHIP_POWER) {
|
||||
/* 电源菜单 chip:非开关(无 on/off 态),恒示中性;点击由 ncp_chip_apply 唤起电源菜单。 */
|
||||
*on = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* chip 快捷动作(写 meow_settings_* + 侧效应;零手写文件)。
|
||||
* 返回 1=主题变化(需重刷状态栏);0=其它。 */
|
||||
* 返回 1=主题变化(需重刷状态栏);2=电源菜单已弹出(调用方置 after_modal=1 整帧重绘还原);
|
||||
* 0=其它。电源菜单 chip 复用既有系统级 show_power_menu(含 dim/圆钮/音量导航/电源确认/8s强关/
|
||||
* 空闲自动息屏),真弹对话框,动作在函数内 system();取消则返回续 panel —— 与电源键长按同款。 */
|
||||
static int ncp_chip_apply(int chip)
|
||||
{
|
||||
if (chip == NCP_CHIP_WIFI) {
|
||||
@@ -7098,12 +7140,72 @@ static int ncp_chip_apply(int chip)
|
||||
g_sys.brightness = vmin;
|
||||
}
|
||||
paint_sb_right();
|
||||
} else if (chip == NCP_CHIP_POWER) {
|
||||
/* 电源菜单 chip:唤起既有系统级电源菜单(真弹对话框,动作在函数内 system();取消则返回)。
|
||||
* 不涉及任何设置项(电源菜单不走 meow_settings,零旁路)。返回 2 → 调用方整帧重绘还原面板/状态栏。 */
|
||||
logmsg("notif-center: power chip -> show power menu");
|
||||
show_power_menu();
|
||||
return 2;
|
||||
}
|
||||
meow_settings_save();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 绘制通知列表可见区(scroll=像素偏移)。每行:图标 + app名 + 标题 + 内容 + 时间 + 进度。 */
|
||||
/* ⑦ 画删除底:行右缘被空出的条(宽=-off)铺 error 红底 + 白「删除」字(条宽≥44 才显字,否则纯红渐进)。
|
||||
* off<=0(负得多=卡滑出多、露出右条多);off=0 不画。 */
|
||||
static void ncp_draw_delete_bg(int ry, int off, const meow_ui_theme_t *th)
|
||||
{
|
||||
if (off >= 0) return;
|
||||
int bw = -off;
|
||||
if (bw <= 0) return;
|
||||
int bx = NCP_ROW_X + NCP_ROW_W + off; /* 被空出的右条左缘 */
|
||||
fill_rect(bx, ry, bw, NCP_ROW_H - 6, th->error);
|
||||
if (bw >= 44) {
|
||||
int cx = bx + bw / 2, cy = ry + (NCP_ROW_H - 6) / 2;
|
||||
ui_draw(cx - ui_width("删除", 16) / 2, cy - meow_font_line_height(16) / 2,
|
||||
"删除", 0xFFFFFF, 16);
|
||||
}
|
||||
}
|
||||
|
||||
/* 画一行内容(行卡 + 图标/APP名/时间/标题/正文/进度),整体随 off 左移(⑦ 行左滑跟手)。
|
||||
* i=g_rows 下标;ry=行顶(已含 scroll);off=行左移量(px,<=0)。 */
|
||||
static void ncp_draw_row(int i, int ry, int off, const meow_ui_theme_t *th)
|
||||
{
|
||||
const ncp_row_t *r = &g_rows[i];
|
||||
meow_gfx_fill_round(NCP_ROW_X + off, ry, NCP_ROW_W, NCP_ROW_H - 6, 12, th->surface_container);
|
||||
/* 图标 */
|
||||
int icx = NCP_ICON_X + off, icy = ry + (NCP_ROW_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 = ui_width(cbuf, 20);
|
||||
ui_draw(icx + 16 - cw / 2, icy + 16 - meow_font_line_height(20) / 2, cbuf, 0xFFFFFF, 20);
|
||||
}
|
||||
/* app 名 + 时间 */
|
||||
ui_draw(NCP_TEXT_X + off, ry + 10, ncp_app_name(r->app_id), th->on_surface_variant, 16);
|
||||
char tbuf[32]; mnp_time_ago(r->created_ms, (uint64_t)time(NULL) * 1000u, tbuf, sizeof tbuf);
|
||||
ui_draw(NCP_ROW_X + NCP_ROW_W - 8 - ui_width(tbuf, 16) + off, ry + 12, tbuf, th->on_surface_variant, 16);
|
||||
/* 标题 + 内容 */
|
||||
ncp_text(NCP_TEXT_X + off, ry + 30, r->summary, 20, th->on_surface, NCP_TEXT_W);
|
||||
ncp_text(NCP_TEXT_X + off, ry + 56, r->body, 18, th->on_surface_variant, NCP_TEXT_W);
|
||||
/* 进度(progress>=0):细条 */
|
||||
if (r->progress >= 0) {
|
||||
int px = NCP_TEXT_X + off, pw = NCP_TEXT_W, py = ry + NCP_ROW_H - 14;
|
||||
fill_rect(px, py, pw, 4, th->surface_variant);
|
||||
int fw = r->progress * pw / 100;
|
||||
if (fw > 0) fill_rect(px, py, fw, 4, th->primary);
|
||||
}
|
||||
}
|
||||
|
||||
/* 绘制通知列表可见区(scroll=像素偏移)。每行:图标 + app名 + 标题 + 内容 + 时间 + 进度。
|
||||
* ⑦:左滑中的行(g_ncp_swipe_row)按 g_ncp_swipe_off 左移,其下先画删除底(ncp_draw_delete_bg)。 */
|
||||
static void ncp_draw_list(int scroll)
|
||||
{
|
||||
const meow_ui_theme_t *th = s_ui_theme();
|
||||
@@ -7116,37 +7218,9 @@ static void ncp_draw_list(int scroll)
|
||||
for (int i = 0; i < g_nrows; i++) {
|
||||
int ry = NCP_LIST_TOP + i * NCP_ROW_H - scroll;
|
||||
if (ry + NCP_ROW_H < NCP_LIST_TOP || ry >= NCP_LIST_BOT) continue; /* 视口外整行跳过 */
|
||||
const ncp_row_t *r = &g_rows[i];
|
||||
meow_gfx_fill_round(NCP_ROW_X, ry, NCP_ROW_W, NCP_ROW_H - 6, 12, th->surface_container);
|
||||
/* 图标 */
|
||||
int icx = NCP_ICON_X, icy = ry + (NCP_ROW_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 = ui_width(cbuf, 20);
|
||||
ui_draw(icx + 16 - cw / 2, icy + 16 - meow_font_line_height(20) / 2, cbuf, 0xFFFFFF, 20);
|
||||
}
|
||||
/* app 名 + 时间 */
|
||||
ui_draw(NCP_TEXT_X, ry + 10, ncp_app_name(r->app_id), th->on_surface_variant, 16);
|
||||
char tbuf[32]; mnp_time_ago(r->created_ms, (uint64_t)time(NULL) * 1000u, tbuf, sizeof tbuf);
|
||||
ui_draw(NCP_ROW_X + NCP_ROW_W - 8 - ui_width(tbuf, 16), ry + 12, tbuf, th->on_surface_variant, 16);
|
||||
/* 标题 + 内容 */
|
||||
ncp_text(NCP_TEXT_X, ry + 30, r->summary, 20, th->on_surface, NCP_TEXT_W);
|
||||
ncp_text(NCP_TEXT_X, ry + 56, r->body, 18, th->on_surface_variant, NCP_TEXT_W);
|
||||
/* 进度(progress>=0):细条 */
|
||||
if (r->progress >= 0) {
|
||||
int px = NCP_TEXT_X, pw = NCP_TEXT_W, py = ry + NCP_ROW_H - 14;
|
||||
fill_rect(px, py, pw, 4, th->surface_variant);
|
||||
int fw = r->progress * pw / 100;
|
||||
if (fw > 0) fill_rect(px, py, fw, 4, th->primary);
|
||||
}
|
||||
int off = (i == g_ncp_swipe_row) ? g_ncp_swipe_off : 0; /* 左滑中的行位移 */
|
||||
if (off < 0) ncp_draw_delete_bg(ry, off, th);
|
||||
ncp_draw_row(i, ry, off, th);
|
||||
}
|
||||
/* 滚动条 */
|
||||
if (total > NCP_LIST_BOT - NCP_LIST_TOP) {
|
||||
@@ -7170,6 +7244,51 @@ static void ncp_repaint_list(int scroll)
|
||||
meow_ui_flush();
|
||||
}
|
||||
|
||||
/* ⑦ 仅重绘一行(行左滑跟手/回弹/滑出专用;只刷该行矩形,不整面板、不刷状态栏)。
|
||||
* 清该行带(含被滑出的右条)→ 画删除底(若 off<0)+ 该行内容位移 → 只标该行带脏。 */
|
||||
static void ncp_repaint_row(int scroll, int row, int off)
|
||||
{
|
||||
const meow_ui_theme_t *th = s_ui_theme();
|
||||
if (row < 0 || row >= g_nrows) return;
|
||||
int ry = NCP_LIST_TOP + row * NCP_ROW_H - scroll;
|
||||
if (ry + NCP_ROW_H < NCP_LIST_TOP || ry >= NCP_LIST_BOT) return; /* 视口外:无需重绘 */
|
||||
/* 清该行带全宽(含左缘 [0,NCP_ROW_X),因深滑时卡可滑到左缘外),再画删除底 + 位移行卡。 */
|
||||
fill_rect(0, ry, NCP_ROW_X + NCP_ROW_W, NCP_ROW_H, th->surface_variant);
|
||||
if (off < 0) ncp_draw_delete_bg(ry, off, th);
|
||||
ncp_draw_row(row, ry, off, th);
|
||||
meow_dirty_add(0, ry, fb_xres, NCP_ROW_H);
|
||||
meow_ui_flush();
|
||||
}
|
||||
|
||||
/* ⑥ 勿扰开关带命中(面板内小 toggle):落在 [NCP_DND_Y, NCP_DND_Y+NCP_DND_H) 整宽 → 命中。
|
||||
* 不与 5 chip(y≤130)、滑条带(y≤238)、通知列表(y≥NCP_LIST_TOP=308)、状态栏(y<28)重叠。 */
|
||||
static int ncp_dnd_hit(int tx, int ty)
|
||||
{
|
||||
return (ty >= NCP_DND_Y && ty < NCP_DND_Y + NCP_DND_H && tx >= 0 && tx < fb_xres);
|
||||
}
|
||||
|
||||
/* ⑥ 绘勿扰带:label「勿扰」+ 铃铛图标 + 状态「开/关」 + 右侧 s_draw_switch(48×28)。 */
|
||||
static void ncp_draw_dnd(const meow_ui_theme_t *th)
|
||||
{
|
||||
int y = NCP_DND_Y;
|
||||
int dnd_on = g_ncp_dnd;
|
||||
int cy = y + NCP_DND_H / 2;
|
||||
/* 铃铛(简化:圆顶 + 顶蒂 + 底部小圆点)。 */
|
||||
int bx = NCP_ROW_X + 4, bcy = cy - 2;
|
||||
meow_gfx_fill_round(bx, bcy - 7, 16, 13, 7, th->on_surface_variant); /* 铃身圆顶 */
|
||||
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);
|
||||
char st[16];
|
||||
snprintf(st, sizeof st, "%s", dnd_on ? "开" : "关");
|
||||
int lw = ui_width("勿扰", 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);
|
||||
/* 右侧开关(既有的开关绘制;48×28 垂直居中) */
|
||||
s_draw_switch(fb_xres - 24 - 48, cy - 14, dnd_on, th);
|
||||
}
|
||||
|
||||
/* 绘制整个通知中心面板(画进 SDK back_buf;redraw_sb=1 时另绘状态栏在最顶)。
|
||||
* 性能:面板体单独;(0..27) 状态栏只在打开/主题变化时 redraw_sb=1 重绘,交互时跳过,
|
||||
* 使脏区仅限面板体,绝不整屏重绘。 */
|
||||
@@ -7191,8 +7310,8 @@ static void draw_notif_center(int scroll, int redraw_sb)
|
||||
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);
|
||||
|
||||
/* 快捷开关行 */
|
||||
static const char *chip_lbl[NCP_CHIP_COUNT] = { "WiFi", "蓝牙", "主题", "亮度" };
|
||||
/* 快捷开关行(⑥: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++) {
|
||||
@@ -7210,6 +7329,13 @@ static void draw_notif_center(int scroll, int redraw_sb)
|
||||
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)
|
||||
@@ -7240,6 +7366,9 @@ static void draw_notif_center(int scroll, int redraw_sb)
|
||||
/* 分隔线 */
|
||||
fill_rect(12, NCP_DIV_Y, W - 24, 1, th->outline);
|
||||
|
||||
/* ⑥ 勿扰面板内开关带(list 上方) */
|
||||
ncp_draw_dnd(th);
|
||||
|
||||
/* 通知列表 */
|
||||
ncp_draw_list(scroll);
|
||||
|
||||
@@ -7349,6 +7478,16 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
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; /* 仅重绘该行带(行左滑专用) */
|
||||
int closed = 0; /* 非动画路径的兜底关面板(快照不可用时瞬时退出) */
|
||||
int after_modal = 0; /* 电源菜单返回:整帧重绘还原被 dim 的状态栏 */
|
||||
int wake_suppress = 0;
|
||||
@@ -7365,6 +7504,12 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
int anim_prev_bot = NCP_TOP; /* 上一帧面板覆盖底缘(exclusive;初始=NCP_TOP=未覆盖) */
|
||||
int anim_ok = 0;
|
||||
|
||||
/* ⑥ 读勿扰面板内开关态(开面板一次;切换在下面 tap 分支写回)。 */
|
||||
{
|
||||
int d = 0; meow_settings_get_bool("notify.dnd", &d);
|
||||
g_ncp_dnd = d;
|
||||
}
|
||||
|
||||
/* 首次绘制:先拍背景(应用列表)→ 渲染最终位置整面板 → 拍面板快照 → 还原背景到 p=0
|
||||
* 起点(面板隐藏、先露应用列表),随后进入循环由动画推进 p:0→FIX 滑入。 */
|
||||
ncp_refresh_rows();
|
||||
@@ -7428,6 +7573,9 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
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;
|
||||
/* 唤醒:面板直接就位(不重播滑入动画,面板早已打开);重拍快照防内容已变 */
|
||||
anim_p = anim_target = anim_last_target = NCP_ANIM_FIX;
|
||||
if (anim_ok) anim_prev_bot = NCP_TOP + g_ncp_ph;
|
||||
@@ -7536,10 +7684,39 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
}
|
||||
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;
|
||||
}
|
||||
/* 列表滚动(§11):面板就位(open_settled)+ 起点在列表区 + 内容溢出 +
|
||||
* 纵向位移达 slop → 进滚动。方向遵循 input.swipe_invert(与设置页列表同式);
|
||||
* 拖到顶/底继续拖 → 回弹标记。动画进行中(面板在滑,未就位)不启动滚动。 */
|
||||
if (open_settled && td_ok && !scroll_drag && drag_slider == 0) {
|
||||
if (open_settled && td_ok && !scroll_drag && drag_slider == 0 && row_swipe_row < 0) {
|
||||
int ddy = tly - scroll_y0;
|
||||
if (ddy >= S_SCROLL_SLOP || ddy <= -S_SCROLL_SLOP) {
|
||||
int total = g_nrows * NCP_ROW_H;
|
||||
@@ -7569,6 +7746,10 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
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;
|
||||
}
|
||||
} else if (!val && td) {
|
||||
td = 0;
|
||||
@@ -7584,7 +7765,20 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
if (anim_ok) anim_target = 0; else closed = 1;
|
||||
break;
|
||||
}
|
||||
if (scroll_drag) {
|
||||
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; /* 回弹复原 */
|
||||
}
|
||||
row_settle = 1; row_anim_at = now_ms();
|
||||
} else if (scroll_drag) {
|
||||
/* 列表滚动落定:最终 clamp 到边界(拖动中已实时跟手,此处确认终值);
|
||||
* 若回弹(拖到顶/底继续拖)→ 按「上滑/下滑关闭」收起(§11.3)。
|
||||
* 绝不把「滑列表」误判成「上滑关闭」——scroll_drag 一旦置位即跳过
|
||||
@@ -7605,11 +7799,44 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
int hit = 0;
|
||||
for (int c = 0; c < NCP_CHIP_COUNT; c++)
|
||||
if (ncp_chip_hit(c, tx, ty)) {
|
||||
if (ncp_chip_apply(c)) need_sb = 1; /* 主题变化 → 重刷状态栏 */
|
||||
int ar = ncp_chip_apply(c);
|
||||
if (ar == 1) need_sb = 1; /* 主题变化 → 重刷状态栏 */
|
||||
else if (ar == 2) {
|
||||
after_modal = 1; /* 电源菜单已弹 → 整帧重绘还原 */
|
||||
/* 电源菜单返回:菜单期间用户可能按电源确认/取消,键边沿可能错配,
|
||||
* 同电源键长按路径(line 7480-7488)重 poll + 重采 m_prev_*,防误判「单击」→ 误切 panel。 */
|
||||
memset(&kd_pwr, 0, sizeof kd_pwr);
|
||||
memset(&kd_vold, 0, sizeof kd_vold);
|
||||
memset(&kd_volu, 0, sizeof kd_volu);
|
||||
memset(&kd_play, 0, sizeof kd_play);
|
||||
kp_poll(&kp_power, NULL, NULL);
|
||||
kp_poll(&kp_keys, NULL, NULL);
|
||||
m_prev_pwr = kp_is_down(&kp_power, KEY_POWER);
|
||||
m_prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
|
||||
m_prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP);
|
||||
m_prev_play = kp_is_down(&kp_keys, KEY_PLAYPAUSE);
|
||||
/* 菜单期间用户的触摸经 evdev 广播也会进本 panel 的 tfd → 立即排空,
|
||||
* 防返回后把「菜单里的点击」误判为 panel 内 tap/滑动。 */
|
||||
if (tfd >= 0) {
|
||||
unsigned char ev2[16];
|
||||
while (read(tfd, ev2, sizeof ev2) == 16) {}
|
||||
}
|
||||
}
|
||||
hit = 1; need_paint = 1; break;
|
||||
}
|
||||
if (!hit && ncp_slider_band(tx, ty, NCP_SL1_Y)) { hit = 1; if (ncp_slider_apply(1, tx, 1)) need_paint = 1; }
|
||||
else if (!hit && ncp_slider_band(tx, ty, NCP_SL2_Y)) { hit = 1; if (ncp_slider_apply(2, tx, 1)) need_paint = 1; }
|
||||
/* ⑥ 勿扰面板内开关(列表上方小 toggle):tap 即切换(整带命中) */
|
||||
if (!hit && ncp_dnd_hit(tx, ty)) {
|
||||
int dv = 0; meow_settings_get_bool("notify.dnd", &dv);
|
||||
meow_settings_set_bool("notify.dnd", dv ? 0 : 1);
|
||||
meow_settings_save();
|
||||
g_ncp_dnd = dv ? 0 : 1;
|
||||
ncp_refresh_rows(); /* 按 dnd 过滤重采集 */
|
||||
list_scroll = mnp_scroll_clamp(list_scroll, g_nrows * NCP_ROW_H,
|
||||
NCP_LIST_BOT - NCP_LIST_TOP);
|
||||
hit = 1; need_paint = 1;
|
||||
}
|
||||
if (!hit && ty >= NCP_LIST_TOP && ty < NCP_LIST_BOT) {
|
||||
if (ncp_row_tap(tx, ty, list_scroll, want_launch)) {
|
||||
hit = 1;
|
||||
@@ -7644,6 +7871,10 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
last_listchk = now;
|
||||
meow_notif_tick();
|
||||
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;
|
||||
ncp_refresh_rows();
|
||||
list_scroll = mnp_scroll_clamp(list_scroll, g_nrows * NCP_ROW_H,
|
||||
NCP_LIST_BOT - NCP_LIST_TOP);
|
||||
@@ -7681,6 +7912,42 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
}
|
||||
if (anim_ok && 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_BOT - NCP_LIST_TOP);
|
||||
need_paint = 1; /* 行已移除/重排:整面板(列表区)重画 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 重绘(面板体区域;状态栏 0-27 由 draw_statusbar 单独维护,不落入此脏区)。
|
||||
* need_sb=1(打开/主题变化)时 draw_notif_center 内另绘状态栏并标其脏——脏区仍
|
||||
* 只含「面板体 + 28px 状态栏」两带,绝不整屏重绘。
|
||||
@@ -7709,6 +7976,14 @@ static void run_notif_center(struct app *apps, int napps, int *want_launch)
|
||||
list_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;
|
||||
}
|
||||
/* ~1s 状态栏时钟 */
|
||||
if (now - last_tick >= 1000) {
|
||||
last_tick = now;
|
||||
|
||||
@@ -193,4 +193,55 @@ static inline int mnp_anim_covered_bot(int top, int ph, int off)
|
||||
return top + ph + off;
|
||||
}
|
||||
|
||||
/* ---- 勿扰(DND)过滤(通知中心面板内开关 → 基于 urgency 过滤,纯逻辑,可单测)----
|
||||
* urgency 见 meow_notif.h:0=LOW 低 / 1=NORMAL 普通 / 2=HIGH 高 / 3=CRITICAL 紧急(缺省 NORMAL)。
|
||||
* dnd=0(关):全部显示;dnd=1(开):只显示 urgency>=2(HIGH/CRITICAL)。返回 1=应显示。 */
|
||||
static inline int mnp_dnd_should_show(int urgency, int dnd)
|
||||
{
|
||||
if (!dnd) return 1;
|
||||
return urgency >= 2;
|
||||
}
|
||||
|
||||
/* ---- 单条通知行左滑删除(纯逻辑,可单测)----
|
||||
* 交互(launcher run_notif_center):行内左横滑(横向主导+左移)→ 该行跟随左移(off<0,露出删除底);
|
||||
* 松手过阈值 → meow_notif_dismiss + 行移除/重排;未过/反向滑回 → 回弹复原(跟手、半程可回)。
|
||||
* 本组函数只做标量:clamp / 删除阈值 / 落位步进,不碰 fb/几何——供 launcher 与宿主单测共用。 */
|
||||
|
||||
/* 行左移 clamp:把手指位移钳到 [-row_w, 0](正向=右移钳 0;越左钳 -row_w=卡全滑出)。
|
||||
* row_w=行宽(如 NCP_ROW_W),off<=0 表示左移程度(负得多=滑出多)。 */
|
||||
static inline int mnp_swipe_clamp(int off, int row_w)
|
||||
{
|
||||
if (off > 0) off = 0;
|
||||
if (off < -row_w) off = -row_w;
|
||||
return off;
|
||||
}
|
||||
|
||||
/* 松手删除判定:│off│ >= row_w*frac_pct/100 或 │off│ >= min_px → 返回 1(应删除);
|
||||
* 否则 0(未过阈值/半程取消 → 回弹)。frac_pct 为行宽百分比(如 40)、min_px 为绝对像素保底
|
||||
* (防极窄行/极小滑也判删,取二者较大者实际生效——满足「≥row_w 某比例 或 ≥ 阈值 px」)。 */
|
||||
static inline int mnp_swipe_delete(int off, int row_w, int frac_pct, int min_px)
|
||||
{
|
||||
if (off > 0) off = 0;
|
||||
int amt = -off; /* 左移幅度(px,>=0) */
|
||||
if (amt >= row_w * frac_pct / 100) return 1;
|
||||
if (amt >= min_px) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 落位动画线性步进:off 沿方向向 target 走 dt_ms*px_per_ms,到 target 即停(可正/反向回弹或滑出)。
|
||||
* dt_ms=距上次 tick 单调毫秒差;px_per_ms=速率(px/ms,如 3)。dt_ms<=0 或已到位返回原 off。
|
||||
* 单调、可打断(任意时刻改 off 即反向),与面板开关平移动画(mnp_anim_step)同「状态时间轴」思想,
|
||||
* 但按固定 px/ms 而非剩余比例(行位移远小于面板高,线性更稳)。 */
|
||||
static inline int mnp_swipe_step(int off, int target, unsigned long long dt_ms, int px_per_ms)
|
||||
{
|
||||
if (dt_ms == 0 || off == target) return off;
|
||||
int delta = target - off;
|
||||
int dir = (delta < 0) ? -1 : 1;
|
||||
long long absd = (long long)(dir < 0 ? -delta : delta);
|
||||
long long step = (long long)dt_ms * px_per_ms;
|
||||
if (step > absd) step = absd;
|
||||
if (step < 1) step = 1; /* 单调前进,防快循环 dt 极小卡住 */
|
||||
return off + dir * (int)step;
|
||||
}
|
||||
|
||||
#endif /* MEOW_NOTIF_PANEL_H */
|
||||
@@ -121,6 +121,9 @@ static const meow_setting_def g_defs[] = {
|
||||
{ "notify.categories", SET_INT, "4", 1, 4, NULL, NULL },
|
||||
{ "notify.category_quota", SET_INT, "4", 1, 16, NULL, NULL },
|
||||
{ "notify.media_quota", SET_INT, "1", 1, 1, NULL, NULL },
|
||||
/* 通知中心「勿扰」面板内开关(SET_BOOL,默认 0=关):开=只显示 urgency>=2(HIGH/CRITICAL)。
|
||||
* 面板内 toggle 读写均走 meow_settings_set_bool/get_bool,零旁路(通知中心 UI 方案 §12.2)。 */
|
||||
{ "notify.dnd", SET_BOOL, "0", 0, 0, NULL, NULL },
|
||||
/* 日志 */
|
||||
{ "log.level", SET_ENUM, "info", 0, 0, "error,warn,info,debug", "错误,警告,信息,调试" },
|
||||
{ "log.to_card", SET_BOOL, "1", 0, 0, NULL, NULL },
|
||||
|
||||
@@ -238,6 +238,79 @@ static void test_anim_pure(void)
|
||||
CHECK(mnp_anim_step(0, 0, FIX, 10, 180) == 0, "at p==0 with target 0 stays put (would break)");
|
||||
}
|
||||
|
||||
/* P5 ⑥ 勿扰过滤 + ⑦ 行左滑删除(纯逻辑,header-only):
|
||||
* - mnp_dnd_should_show:dnd=0 全显示;dnd=1 只 urgency>=2(HIGH/CRITICAL)。
|
||||
* - mnp_swipe_clamp:跟手 clamp 到 [-row_w,0]。
|
||||
* - mnp_swipe_delete:松手阈值(行宽百分比 / 绝对像素,取大者)。
|
||||
* - mnp_swipe_step:落位动画线性步进(回弹/滑出,正反向,到底停,半程取消)。
|
||||
* 另加一段 mini 状态机:手指拖 → 松手过阈值=滑出删除 / 未过=回弹复原(跟手、半程可回)。 */
|
||||
static void test_dnd_swipe_pure(void)
|
||||
{
|
||||
fprintf(stderr, "--- P5 DND filter + row left-swipe ---\n");
|
||||
/* mnp_dnd_should_show */
|
||||
CHECK(mnp_dnd_should_show(0, 0) == 1, "dnd off -> LOW(0) shows");
|
||||
CHECK(mnp_dnd_should_show(1, 0) == 1, "dnd off -> NORMAL(1) shows");
|
||||
CHECK(mnp_dnd_should_show(2, 0) == 1, "dnd off -> HIGH(2) shows");
|
||||
CHECK(mnp_dnd_should_show(3, 0) == 1, "dnd off -> CRITICAL(3) shows");
|
||||
CHECK(mnp_dnd_should_show(0, 1) == 0, "dnd on -> LOW(0) hidden");
|
||||
CHECK(mnp_dnd_should_show(1, 1) == 0, "dnd on -> NORMAL(1) hidden");
|
||||
CHECK(mnp_dnd_should_show(2, 1) == 1, "dnd on -> HIGH(2) shows");
|
||||
CHECK(mnp_dnd_should_show(3, 1) == 1, "dnd on -> CRITICAL(3) shows");
|
||||
|
||||
/* mnp_swipe_clamp(跟手 offset clamp:off∈[-row_w,0]) */
|
||||
CHECK(mnp_swipe_clamp(-200, 456) == -200, "swipe_clamp in-range kept");
|
||||
CHECK(mnp_swipe_clamp(30, 456) == 0, "swipe_clamp rightward -> 0");
|
||||
CHECK(mnp_swipe_clamp(-500, 456) == -456, "swipe_clamp past left -> -row_w");
|
||||
CHECK(mnp_swipe_clamp(-456, 456) == -456, "swipe_clamp exactly -row_w kept");
|
||||
CHECK(mnp_swipe_clamp(0, 456) == 0, "swipe_clamp 0 kept");
|
||||
|
||||
/* mnp_swipe_delete(row_w=456, frac_pct=40 → 40%=182.4;min_px=110;OR 语义 → 有效阈值=110px) */
|
||||
CHECK(mnp_swipe_delete(-80, 456, 40, 110) == 0, "delete -80 (<110 & <182.4) -> cancel");
|
||||
CHECK(mnp_swipe_delete(-110, 456, 40, 110) == 1, "delete -110 (>=min_px) -> delete");
|
||||
CHECK(mnp_swipe_delete(-150, 456, 40, 110) == 1, "delete -150 (>=min_px, <40%) -> delete");
|
||||
CHECK(mnp_swipe_delete(-183, 456, 40, 110) == 1, "delete -183 (>=40%) -> delete");
|
||||
CHECK(mnp_swipe_delete(-456, 456, 40, 110) == 1, "delete full slide -> delete");
|
||||
CHECK(mnp_swipe_delete(0, 456, 40, 110) == 0, "delete 0 -> cancel");
|
||||
/* 窄行:40%=24px(row_w=60),min_px=110 不起作用 → 阈值=24px */
|
||||
CHECK(mnp_swipe_delete(-25, 60, 40, 110) == 1, "delete narrow row -25 (>=40%) -> delete");
|
||||
CHECK(mnp_swipe_delete(-20, 60, 40, 110) == 0, "delete narrow row -20 (<40%) -> cancel");
|
||||
|
||||
/* mnp_swipe_step(线性步进:off 向 target 走 dt*rate,可正/反向,到底停) */
|
||||
CHECK(mnp_swipe_step(-100, 0, 10, 3) == -70, "step springback +30px/tick");
|
||||
CHECK(mnp_swipe_step(-100, 0, 0, 3) == -100, "step dt=0 -> unchanged");
|
||||
CHECK(mnp_swipe_step(-100, 0, 1000, 3) == 0, "step huge dt -> clamp at 0 (springback done)");
|
||||
CHECK(mnp_swipe_step(-50, -456, 10, 3) == -80, "step slide-out leftward");
|
||||
CHECK(mnp_swipe_step(-50, -456, 1000, 3) == -456, "step huge dt -> clamp at -row_w (deleted)");
|
||||
CHECK(mnp_swipe_step(-30, -30, 10, 3) == -30, "step at target -> stays");
|
||||
CHECK(mnp_swipe_step(0, 0, 10, 3) == 0, "step 0 target 0 -> stays");
|
||||
CHECK(mnp_swipe_step(-1000, 0, 5, 3) == -985, "step linear rate not exponential (small dt)");
|
||||
|
||||
/* mini 状态机:跟手拖 → 松手判定 → 动画到目标(半程取消 vs 删除)。 */
|
||||
int row_w = 456, off = 0;
|
||||
/* 拖 80px 左(低于阈值 110)→ 松手回弹复原 */
|
||||
off = mnp_swipe_clamp(-80, row_w);
|
||||
CHECK(mnp_swipe_delete(off, row_w, 40, 110) == 0, "mini: drag -80 -> cancel (springback)");
|
||||
int target = mnp_swipe_delete(off, row_w, 40, 110) ? -row_w : 0;
|
||||
off = mnp_swipe_step(off, target, 1000, 3);
|
||||
CHECK(off == 0, "mini: springback reaches 0 (row restored)");
|
||||
/* 拖 150px 左(>=110)→ 松手滑出删除 */
|
||||
off = mnp_swipe_clamp(-150, row_w);
|
||||
CHECK(mnp_swipe_delete(off, row_w, 40, 110) == 1, "mini: drag -150 -> delete");
|
||||
target = mnp_swipe_delete(off, row_w, 40, 110) ? -row_w : 0;
|
||||
off = mnp_swipe_step(off, target, 1000, 3);
|
||||
CHECK(off == -row_w, "mini: slide-out reaches -row_w (dismiss)");
|
||||
/* 半程可反向:拖 200 左再反向回摄(dx 变 +30)→ off 回 0(跟手可回、松手在途取消) */
|
||||
off = mnp_swipe_clamp(-200, row_w);
|
||||
CHECK(off == -200, "mini: drag -200 follows finger");
|
||||
off = mnp_swipe_clamp(30, row_w); /* 反向拖回原点左侧以右 → clamp 0 */
|
||||
CHECK(off == 0, "mini: reverse drag -> row slides back to 0 (halfway cancel)");
|
||||
/* 半程取消:拖 150 左(未达 182.4 的 40% 但达 110px 阈值)→ 松手删;若再拖回 <110 → 回弹 */
|
||||
off = mnp_swipe_clamp(-150, row_w);
|
||||
CHECK(mnp_swipe_delete(off, row_w, 40, 110) == 1, "mini: -150 passed threshold -> delete");
|
||||
off = mnp_swipe_clamp(-100, row_w); /* 又拖回 100 <110 */
|
||||
CHECK(mnp_swipe_delete(off, row_w, 40, 110) == 0, "mini: -100 back under threshold -> cancel");
|
||||
}
|
||||
|
||||
static void test_storage_contract(void)
|
||||
{
|
||||
fprintf(stderr, "--- P0 storage contract (panel reads) ---\n");
|
||||
@@ -341,6 +414,7 @@ int main(void)
|
||||
test_pure_logic();
|
||||
test_scroll_pure();
|
||||
test_anim_pure();
|
||||
test_dnd_swipe_pure();
|
||||
test_storage_contract();
|
||||
|
||||
unlink(g_tflog); rmdir(dir);
|
||||
|
||||
Reference in new issue
Block a user