fix(reader): M41 r1 评审 P2 — 拖动重绘节流 + 终帧补齐 + 三条边界测试
P2-1(用户可感知):shelf_on_touch_move 每收到一条 TOUCH_MOVE 就同步全量重绘。 输入线程「坐标一变就一条 MOVE」且不合并(队列 128 条),folder ≤200 行走圆角卡 ≈40ms/帧 → 主循环追不上手指、抬手后还会继续滑、极端情况丢 release。修法: - 滚动位置仍每条 move 立即生效(纯算术,便宜);上屏节流:首帧立即画,之后每 max(16ms, 上一帧实测耗时) 一次(自适应窗口,圆角卡 40ms 帧自动降到 ~40ms 周期, 不再把队列拖垮)。 - 手势收尾(shelf_on_tap / shelf_on_swipe)无条件补终帧再清手势态;下一次按下也会 先把上一次遗留的未上屏帧补上(HOME/长按等没有 TAP/SWIPE 收尾的手势)。 - 位置没变(已到顶/底)连帧都不画。 P2-2(测试盲区): - 新增三条边界:顶栏起手不拖、副题区起手不拖、横向手势判向取消后不再滚动。 - 吞 TAP 边界带:20/40/60px(>8px 且 <65px,SDK 报 TAP)抬在目录行上不得进目录; 6px 抖动(≤8px)仍算点击能进目录。 - 断言从「像素变了」升级为「像素**恰好**位移 N px」(内容区快照逐行比对,避开视口 边缘裁剪带):swipe 120 → 恰好 120;拖动 60 → 恰好 60;并新增 6 步×10px 多步拖动 断言终帧恰好 60(专测节流不丢最后一段)。 - 修掉把 20px 写成「< SHELF_DRAG_TAP_PX」的错误注释。 顺手: - shelf_draw_notice 用 th->bg 而不是只在 shelf_redraw 赋值的 g_bg。 - build_reader.sh 用 trap 保证测试失败时也清 $TMPDIR/m41_scroll_sd。 验证:shelf_scroll_smoke 38/38、shelf_group_smoke 99/99、shelf_index_smoke 170/170。 红绿对照:dist=dy(旧 swipe 语义)→ 9 条 FAIL;拿掉「抬手补终帧」→ 恰好 1 条 FAIL (6 步拖动终帧那条),证明节流与终帧都有测试盯着。 reader 1719220B md5 890175ec…;mpk 818162B md5 ffbb7097…(0.1.8/code9,未签名)。
This commit is contained in:
1 parent
2de47154bf
commit
bd6fc8da3c
3 files changed
+181
-37
No files matched your search
@@ -65,6 +65,10 @@
|
||||
* (SDK 只在位移 ≥65px 时报 SWIPE,小于它的拖动会落成 TAP,不吞就会误进目录/误开书)。 */
|
||||
#define SHELF_DRAG_START_PX 4
|
||||
#define SHELF_DRAG_TAP_PX 8
|
||||
/* 拖动重绘节流:滚动位置每个 move 都立即生效(便宜),上屏最多每 FRAME_MS 一次;
|
||||
* 输入线程「坐标一变就一条 TOUCH_MOVE」(无合并、队列 128 条),圆角卡一帧 40ms+,
|
||||
* 不节流会让主循环永远追不上手指、抬手后还继续滑。窗口再取 max(16ms, 上一帧实测耗时)。 */
|
||||
#define SHELF_DRAG_FRAME_MS 16
|
||||
|
||||
static shelf_book g_books[SHELF_MAX_BOOKS];
|
||||
static int g_nbooks;
|
||||
@@ -86,6 +90,9 @@ static int g_drag_state;
|
||||
static int g_drag_x0, g_drag_y0; /* 按下原点(判向:纵向 vs 横向) */
|
||||
static int g_drag_last_y; /* 上一帧 Y(逐帧增量) */
|
||||
static int g_drag_scrolled; /* 本次手势已实际滚过(抬手不再叠加/不当作点击) */
|
||||
static int g_drag_dirty; /* 有已生效但未上屏的滚动(抬手补终帧) */
|
||||
static uint64_t g_drag_draw_ms; /* 上次拖动重绘时刻(0=本手势还没画过 → 首帧立即画) */
|
||||
static int g_drag_frame_ms; /* 上次拖动重绘耗时(节流窗口自适应) */
|
||||
static uint32_t g_bg, g_fg, g_dim;
|
||||
static char g_notice[160]; /* 提示第 1 行(opendir 失败/超上限截断等) */
|
||||
static char g_notice2[160]; /* 提示第 2 行(超量+超深并存时才用;空=单行) */
|
||||
@@ -617,7 +624,7 @@ static void shelf_draw_notice(void)
|
||||
int two = g_notice2[0] != 0;
|
||||
int top = two ? 610 : 620;
|
||||
int h = two ? 58 : 44;
|
||||
meow_ui_fill_rect(0, top, 480, h, g_bg);
|
||||
meow_ui_fill_rect(0, top, 480, h, th->bg);
|
||||
meow_ui_text(20, top + 4, g_notice, 20, th->primary);
|
||||
if (two) meow_ui_text(20, top + 30, g_notice2, 20, th->primary);
|
||||
}
|
||||
@@ -713,27 +720,56 @@ static void shelf_redraw_content(void)
|
||||
meow_ui_flush_region(0, top, SHELF_LIST_W, h);
|
||||
}
|
||||
|
||||
/* 滚动 del 像素(正=内容上移/显示更下面的行)。返回 1=视口确实变了(已重画)。 */
|
||||
static int shelf_scroll_by(int del)
|
||||
/* 只改滚动位置(不动屏幕):返回 1=位置变了。 */
|
||||
static int shelf_scroll_apply(int del)
|
||||
{
|
||||
if (!del) return 0;
|
||||
int before = shelf_scroll_get();
|
||||
if (g_layout == 1) { if (g_grid) meow_lazygrid_scroll_by(g_grid, del); }
|
||||
else { if (g_list) meow_listview_scroll_by(g_list, del); }
|
||||
if (shelf_scroll_get() == before) return 0;
|
||||
return shelf_scroll_get() != before;
|
||||
}
|
||||
|
||||
/* 改位置 + 立即上屏:swipe 的一次性滚动走这里。 */
|
||||
static int shelf_scroll_by(int del)
|
||||
{
|
||||
if (!shelf_scroll_apply(del)) return 0;
|
||||
shelf_redraw_content();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 拖动中的重绘:把已生效的滚动上屏,并记录帧耗时给节流窗口用。 */
|
||||
static void shelf_drag_flush(void)
|
||||
{
|
||||
if (!g_drag_dirty) return;
|
||||
uint64_t t0 = meow_ui_time_ms();
|
||||
shelf_redraw_content();
|
||||
uint64_t t1 = meow_ui_time_ms();
|
||||
g_drag_dirty = 0;
|
||||
g_drag_draw_ms = t0 ? t0 : 1; /* 记**起始**时刻:下一帧的周期 ≈ max(16ms, 本帧耗时)。0 是「没画过」哨兵 */
|
||||
g_drag_frame_ms = (t1 > t0) ? (int)(t1 - t0) : 0; /* 10ms 分辨率:小帧记 0 → 窗口仍 ≥16ms */
|
||||
}
|
||||
|
||||
static void shelf_drag_reset(void)
|
||||
{
|
||||
g_drag_state = 0;
|
||||
g_drag_scrolled = 0;
|
||||
g_drag_dirty = 0;
|
||||
g_drag_draw_ms = 0;
|
||||
g_drag_frame_ms = 0;
|
||||
}
|
||||
|
||||
/* 手势收尾:节流窗内没来得及上屏的最后一段**必须补一帧**,否则抬手后画面停在旧位置
|
||||
* (滚动位置已经生效,视觉上就是「拖了但没跟上」)。 */
|
||||
static void shelf_drag_finish(void)
|
||||
{
|
||||
if (g_drag_state == 2 && g_drag_dirty) shelf_drag_flush();
|
||||
shelf_drag_reset();
|
||||
}
|
||||
|
||||
void shelf_on_touch_down(int x, int y)
|
||||
{
|
||||
shelf_drag_reset();
|
||||
shelf_drag_finish(); /* 上一次手势若没走 TAP/SWIPE 收尾(HOME/长按等),先把遗留帧补上 */
|
||||
if (!g_active || g_nbooks <= 0) return;
|
||||
if (y < shelf_content_top() || y >= shelf_content_bottom()) return; /* 顶栏/副题不拖 */
|
||||
if (shelf_scroll_max() <= 0) return; /* 不足一屏:没有可滚的 */
|
||||
@@ -755,7 +791,15 @@ void shelf_on_touch_move(int x, int y)
|
||||
int step = y - g_drag_last_y;
|
||||
g_drag_last_y = y;
|
||||
if (!step) return;
|
||||
if (shelf_scroll_by(-step)) g_drag_scrolled = 1; /* 手指上移(y 减)→ 显示更下面的行 */
|
||||
/* 位置每次都立即生效(便宜);上屏节流:首帧立即画,之后每 max(16ms, 上一帧耗时) 一次。
|
||||
* 输入线程每条 MOVE 都入队且不合并,逐条全量重绘会让队列永远追不上手指。 */
|
||||
if (!shelf_scroll_apply(-step)) return; /* 已到顶/底:位置没变,连帧都不用画 */
|
||||
g_drag_scrolled = 1; /* 手指上移(y 减)→ 显示更下面的行 */
|
||||
g_drag_dirty = 1;
|
||||
uint64_t now = meow_ui_time_ms();
|
||||
int win = g_drag_frame_ms > SHELF_DRAG_FRAME_MS ? g_drag_frame_ms : SHELF_DRAG_FRAME_MS;
|
||||
if (g_drag_draw_ms == 0 || (now >= g_drag_draw_ms && now - g_drag_draw_ms >= (uint64_t)win))
|
||||
shelf_drag_flush();
|
||||
}
|
||||
|
||||
int shelf_on_tap(int x, int y)
|
||||
@@ -766,7 +810,7 @@ int shelf_on_tap(int x, int y)
|
||||
(g_drag_scrolled ||
|
||||
(g_drag_last_y - g_drag_y0 > SHELF_DRAG_TAP_PX) ||
|
||||
(g_drag_y0 - g_drag_last_y > SHELF_DRAG_TAP_PX)));
|
||||
shelf_drag_reset();
|
||||
shelf_drag_finish(); /* 先补终帧再清手势态 */
|
||||
if (dragged) return 0;
|
||||
/* 顶栏动作先命中 */
|
||||
if (g_appbar) {
|
||||
@@ -829,7 +873,7 @@ int shelf_on_tap(int x, int y)
|
||||
void shelf_on_swipe(int dir, int dx, int dy)
|
||||
{
|
||||
int already = (g_drag_state == 2 && g_drag_scrolled);
|
||||
shelf_drag_reset();
|
||||
shelf_drag_finish(); /* 先补终帧(节流窗内最后一段)再清手势态 */
|
||||
if (g_nbooks <= 0 || already) return;
|
||||
if (dir != MEOW_SWIPE_UP && dir != MEOW_SWIPE_DOWN) return;
|
||||
int dist = dx > 0 ? dx : dy; /* SDK: dist 在 dx;dy 分支只为兼容「位移放 dy」的旧/宿主编排 */
|
||||
|
||||
Reference in new issue
Block a user