图库: 书库改文件管理器模型 + 关闭缩略图 + 根锁定/无卡容错(M18)
用户反馈四件事一起改: - 「目录里有一张图就被当成最终目录」→ 取消「目录含图=一本书」全部链路 (探测/页数/封面/续读角标/懒算 worker/sr2 补位),列表 = 目录 + 图片 + .cbz/.zip(目录优先、组内自然序)。点目录=进入(空目录也进),点图片= mr_reader_open_dir_at(当前目录, 图片自然序下标),点档案册=打开档案册。 - 「文件夹浏览特别卡」→ 删除缩略图线程/磁盘 bmp 缓存/LRU/解码路径与行内 缩略图;首帧只做一次 opendir/readdir+lstat,副标题(类型·大小)扫描期 生成一次,行合成/滚动/点击零 IO 零格式化;去掉逐行 snap(自由滚动手感)。 2000 项目录扫描 ~2.8ms(opendir=1, lstat=2000),行合成 2.78ms/200 万次 (宿主实测;旧实现 2000 项 entry 含 512B cover,内存 2.7MB→1.7MB)。 - 「不要访问 SD 卡上级目录」→ 浏览根锁定 MR_ROOT_DIR:删除越根的顶栏 「上级」/library_go_parent,根隐藏顶栏返回,左缘返回到根=退出 App。 - 「没有 SD 卡不要报错」→ 根不可访问(未插卡/挂载失败/根是文件)时友好空态 「未检测到 SD 卡」+ 顶栏刷新/重进页面重试;子目录不可读才弹提示横幅。 集成陷阱:书库图片下标与阅读器目录册共享同一套过滤器(隐藏项/图片扩展名/ 普通文件/符号链接)与 mr_natural_cmp,test_browse_lazy 直接对比 mr_dir_book_init 顺序,test_open_book_host 断言「点 img10 就开 img10」。 reader 侧 M17 遗留:mr_reader_open_dir_at 在阅读页已激活时先 pop 再 push, 走完整 leave/enter(SDK 对同 view 重复 push 是 no-op 且会压重复 activity)。 回归:run_tests.sh / run_webtoon_tests.sh 全 PASS;交叉构建零警告。
This commit is contained in:
1 parent
d07b99056f
commit
64336f95d9
16 files changed
+940
-1927
No files matched your search
@@ -6,8 +6,8 @@
|
||||
* A3 边界:末页前进→下一册、首页后退→上一册(调用方语义);路径末尾斜杠等价;
|
||||
* A4 排除:隐藏/系统目录、符号链接、普通文件不参与兄弟册;
|
||||
* A5 参数:delta 非 ±1、NULL/小缓冲、不存在的目录;
|
||||
* A6 书库扫描接线(M13 两段式):scan 只列单层、副标题占位「…」;
|
||||
* probe/on_tick 补位后出现页数与「· 续读 Pn」角标。
|
||||
* A6 书库行模型(M18):目录+图片+档案册同列、目录优先;副标题扫描期生成,
|
||||
* 不再有「· 续读 Pn」角标(角标随「目录自动当书」链路删除,续读仍由 reader 恢复)。
|
||||
*
|
||||
* 编译(在 tuku/ 下):
|
||||
* gcc -Wall -Wextra -O1 -I. test/test_cont_adjacent.c logic/library.c logic/reader.c \
|
||||
@@ -159,84 +159,43 @@ static void test_adjacent(const char *root)
|
||||
CHECK(mr_book_adjacent("/", 1, out, (int)sizeof out) == -1, "root path invalid");
|
||||
}
|
||||
|
||||
/* 模拟 UI 后台探测:逐行单层 probe 并补位(page>0 的书行由 format_subtitle 拼续读角标)。 */
|
||||
static void probe_all_rows(mr_library *lib)
|
||||
{
|
||||
for (int i = 0; i < lib->count; i++) {
|
||||
mr_library_probe p;
|
||||
if (mr_library_probe_cached(lib->items[i].path, &p) == 0)
|
||||
mr_library_apply_probe(lib, i, &p);
|
||||
else
|
||||
mr_library_mark_error(lib, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* A6 两段式(M13):scan 只做单层枚举、行副标题是占位「…」;后台 probe/on_tick 补位后
|
||||
* 才有页数与「· 续读 Pn」角标。 */
|
||||
static void test_library_badge(const char *root)
|
||||
/* A6 文件管理器行模型:扫描即出最终行(无占位/无后台补位/无续读角标);
|
||||
* 目录优先;当前层的图片行带图片下标(点图 → reader 起始页)。 */
|
||||
static void test_library_rows(const char *root)
|
||||
{
|
||||
mr_test_cfg_clear();
|
||||
char vol1[MR_PATH_MAX], vol2[MR_PATH_MAX];
|
||||
char vol1[MR_PATH_MAX];
|
||||
joinp(vol1, sizeof vol1, root, "vol1");
|
||||
joinp(vol2, sizeof vol2, root, "vol2");
|
||||
|
||||
/* vol1 有续读记录(第 5 页),vol2 没有 */
|
||||
/* vol1 有续读记录(第 5 页)——书库行不应出现角标。 */
|
||||
mr_cont_save(vol1, 4, MR_MODE_WEBTOON);
|
||||
|
||||
mr_library lib;
|
||||
memset(&lib, 0, sizeof lib);
|
||||
int n = mr_library_scan(&lib, root, MR_LIB_MAX_DEPTH);
|
||||
CHECK(n > 0, "library scan n=%d", n);
|
||||
if (n > 0)
|
||||
CHECK(lib.items[0].kind == MR_LIB_KIND_DIR, "dirs first: %s", lib.items[0].name);
|
||||
|
||||
/* 第一段:扫描期不判定,占位副标题。 */
|
||||
int seen1 = 0, seen2 = 0;
|
||||
int seen1 = 0, seen_img = 0;
|
||||
for (int i = 0; i < lib.count; i++) {
|
||||
mr_library_entry *e = &lib.items[i];
|
||||
const mr_library_entry *e = &lib.items[i];
|
||||
CHECK(e->subtitle[0] && strcmp(e->subtitle, "…") != 0,
|
||||
"%s subtitle pre-generated: %s", e->name, e->subtitle);
|
||||
CHECK(strstr(e->subtitle, "续读") == NULL, "%s has no badge: %s",
|
||||
e->name, e->subtitle);
|
||||
if (strcmp(e->name, "vol1") == 0) {
|
||||
seen1 = 1;
|
||||
CHECK(e->probe_state == MR_LIB_PENDING && e->pages == -1,
|
||||
"vol1 pending after scan");
|
||||
CHECK(strcmp(e->subtitle, "…") == 0, "vol1 placeholder subtitle=%s", e->subtitle);
|
||||
CHECK(e->kind == MR_LIB_KIND_DIR, "vol1 is a dir row");
|
||||
}
|
||||
if (strcmp(e->name, "vol2") == 0) {
|
||||
seen2 = 1;
|
||||
CHECK(e->probe_state == MR_LIB_PENDING && strcmp(e->subtitle, "…") == 0,
|
||||
"vol2 placeholder subtitle=%s", e->subtitle);
|
||||
if (strcmp(e->name, "loose.png") == 0) {
|
||||
seen_img = 1;
|
||||
CHECK(e->kind == MR_LIB_KIND_IMAGE, "loose.png is an image row");
|
||||
CHECK(mr_library_image_index(&lib, i) == 0, "loose.png is image #0");
|
||||
}
|
||||
}
|
||||
CHECK(seen1 == 1, "vol1 listed");
|
||||
CHECK(seen2 == 1, "vol2 listed");
|
||||
|
||||
/* 第二段:后台探测补位后,页数与续读角标出现。 */
|
||||
probe_all_rows(&lib);
|
||||
seen1 = seen2 = 0;
|
||||
for (int i = 0; i < lib.count; i++) {
|
||||
mr_library_entry *e = &lib.items[i];
|
||||
if (strcmp(e->name, "vol1") == 0) {
|
||||
seen1 = 1;
|
||||
CHECK(e->has_images == 1 && e->pages == 1, "vol1 probed pages=%d", e->pages);
|
||||
CHECK(strstr(e->subtitle, "page") != NULL, "vol1 keeps pages text: %s", e->subtitle);
|
||||
CHECK(strstr(e->subtitle, "续读 P5") != NULL, "vol1 subtitle=%s", e->subtitle);
|
||||
}
|
||||
if (strcmp(e->name, "vol2") == 0) {
|
||||
seen2 = 1;
|
||||
CHECK(e->has_images == 1 && e->pages == 1, "vol2 probed pages=%d", e->pages);
|
||||
CHECK(strstr(e->subtitle, "续读") == NULL, "vol2 no badge: %s", e->subtitle);
|
||||
CHECK(strcmp(e->subtitle, "1 page") == 0, "vol2 subtitle=%s", e->subtitle);
|
||||
}
|
||||
}
|
||||
CHECK(seen1 == 1, "vol1 listed after probe");
|
||||
CHECK(seen2 == 1, "vol2 listed after probe");
|
||||
mr_library_free(&lib);
|
||||
|
||||
/* 记录清除后角标消失(重扫 + 再探测;探测缓存只存页数,角标按当前配置重算)。 */
|
||||
mr_cont_save(vol1, 0, MR_MODE_WEBTOON);
|
||||
memset(&lib, 0, sizeof lib);
|
||||
CHECK(mr_library_scan(&lib, root, MR_LIB_MAX_DEPTH) > 0, "rescan");
|
||||
probe_all_rows(&lib);
|
||||
for (int i = 0; i < lib.count; i++)
|
||||
if (strcmp(lib.items[i].name, "vol1") == 0)
|
||||
CHECK(strstr(lib.items[i].subtitle, "续读") == NULL, "cleared badge gone");
|
||||
CHECK(seen_img == 1, "loose image listed");
|
||||
mr_library_free(&lib);
|
||||
}
|
||||
|
||||
@@ -280,7 +239,7 @@ int main(void)
|
||||
|
||||
test_has_images(root);
|
||||
test_adjacent(root);
|
||||
test_library_badge(root);
|
||||
test_library_rows(root);
|
||||
|
||||
rm_rf(root);
|
||||
printf("%s: %d passed, %d failed\n", g_fail ? "FAIL" : "PASS", g_pass, g_fail);
|
||||
|
||||
Reference in new issue
Block a user