图库: 书库点图改按文件名定位(修 P1 截断错位)+ 册间衔接根锁定(M18 评审 r1)

P1-1(会静默开错图):取消「书库从列表推导图片下标」。
- 新增 mr_reader_open_dir_at_name(dir, name):阅读器打开目录册后,在**自己刚枚举出的
  全量目录册**(mr_dir_book,与翻页同一份枚举)里按 basename 定位起始页(先精确,
  再大小写不敏感兜底;找不到退回续读/首页)。书库点击只传文件名。
- 删除 mr_library_image_index(不再有跨枚举的隐式契约)。
- 截断提示文案改为「仅显示前 2000 项(图片可在阅读器中翻到)」。
- 修复前(cb02722 探针,2100 张 4 位名夹具):rows=2000/truncated=1/
  library_images=2000/reader_pages=2100/**mismatched=590**(首行 p1412 → 打开 p1411);
  评审夹具为 1995/2000。修复后:2000 个可见图片行按名定位 **0 mismatch**,
  2100 页全量可翻;26 次真实 UI 点开(首尾各 3 + 20 个确定性伪随机)逐张命中。

连带处理(评审 r1):
1. 删掉 reader_page_enter 的 c->view 死写(reopen 判定用 activity active/depth);
   顺手删掉 library_page_create 里同款死写。
2. 阅读器册间衔接根锁定:新增 mr_path_within_root + mr_book_adjacent_rooted,
   book_switch 用浏览根(默认 MR_ROOT_DIR,测试可注入)钳制相邻册;
   根外的兄弟册按「没有相邻册」处理。test_cont_adjacent A7 验证「明文 _ex 会越根、
   rooted 拒绝、根内翻册不受影响」;test_mode_switch_host H6 验证条漫末页不越根。
3. test_reader_perf 不再依赖顶栏 tap 命中时序:模式切换改走测试钩子
   mr_reader_test_toggle_mode()(与顶栏动作同一个 mode_set),负载下稳定。
4. page_cache 注释去掉「缩略图缓存/缩略图线程」过时描述(只改文字)。

回归:run_tests.sh(library 81 / nav 74 / lazy 57 / perf 25 / … / open_book 含 2100 用例)
与 run_webtoon_tests.sh(含 A7/H6)全 PASS;交叉构建零警告。
This commit is contained in:
AskaEth committed 2026-11-05 20:26:40 +08:00
1 parent 64336f95d9
commit 4befbfe14c
17 files changed
+523 -112

No files matched your search

+16 -8
View File
@@ -4,7 +4,8 @@
*
* 浏览语义(M18,用户要求「完全像文件管理器」):
* - 列表 = 当前目录一层的「子目录 + 受支持图片 + .cbz/.zip 档案册」,目录优先、组内自然序;
* 点目录=进入(空目录也进),点图片=从该图开始读(mr_reader_open_dir_at(目录, 图片下标)),
* 点目录=进入(空目录也进),点图片=从该图开始读(mr_reader_open_dir_at_name(目录, 文件名),
* 由阅读器在全量目录册里定位起始页;列表被截断也不会点错图),
* 点档案册=打开档案册(第 1 页/续读);
* - 首帧只做**单层**枚举(一次 opendir/readdir+lstat):没有「目录含图=一本书」的探测、
* 页数、封面、角标,也没有懒算线程/缩略图解码——行副标题在扫描期生成一次;
@@ -268,8 +269,7 @@ int library_go_up(void)
char parent[MR_PATH_MAX];
if (mr_path_parent(g_cur_dir, parent, (int)sizeof parent) != 0) return 0;
/* 防御:父目录若越出浏览根(理论上不会发生),钳回根。 */
size_t rl = strlen(g_root_dir);
if (strlen(parent) < rl || strncmp(parent, g_root_dir, rl) != 0)
if (!mr_path_within_root(parent, g_root_dir))
snprintf(parent, sizeof parent, "%s", g_root_dir);
if (!load_dir(parent)) {
/* 父目录不可读 → 提示并停留(不越根、不丢当前列表)。 */
@@ -289,9 +289,9 @@ static void library_open_entry(int idx)
return;
}
if (e->kind == MR_LIB_KIND_IMAGE) {
/* 下标由 logic 用与阅读器目录册相同的过滤器/比较器数出,保证「点 A 开 A」。 */
int start = mr_library_image_index(&g_lib, idx);
mr_reader_open_dir_at(g_cur_dir, start < 0 ? 0 : start);
/* 按文件名定位起始页:由阅读器用自己的全量目录册算下标。
* 书库列表可能被 MR_LIB_SCAN_MAX 截断,绝不能从列表推导下标(会点 A 开 B)。 */
mr_reader_open_dir_at_name(g_cur_dir, e->name);
return;
}
if (e->kind == MR_LIB_KIND_ARCHIVE) {
@@ -401,8 +401,8 @@ void library_on_tick(void)
/* ============================================================ */
static void library_page_create(meow_view *v, void *ctx)
{
mr_page_ctrl *c = (mr_page_ctrl *)ctx;
c->view = v;
(void)v;
(void)ctx;
int w = meow_ui_width();
int sb = meow_ui_statusbar_height();
@@ -525,6 +525,14 @@ int library_test_src(int idx, meow_list_item *item)
return lib_src(idx, item, NULL);
}
/* 直接打开第 idx 行(等价点击行体;测试里避免滚动/坐标干扰)。 */
int library_test_open_row(int idx)
{
if (idx < 0 || idx >= g_lib.count) return 0;
library_open_entry(idx);
return 1;
}
int library_test_hint_active(void)
{
return g_hint[0] != '\0';