perf(reader): M30 删掉无人消费的下一页预取槽(refill 渲染减半、常驻 −0.69MB)+ 回书架置顶刚读完的书
This commit is contained in:
1 parent
dc559aa1ac
commit
a9638fa59c
5 files changed
+73
-34
No files matched your search
@@ -1 +1 @@
|
|||||||
{"name":"Reader","exec":"/usr/data/mnt/sd_0/MeowAPPs/os.meow.reader/reader","package":"os.meow.reader","version":"0.1.2","version_code":3,"framework":1,"fonts":"common","author":"MeowOS","icon":"icon.png","volume_page":true}
|
{"name":"Reader","exec":"/usr/data/mnt/sd_0/MeowAPPs/os.meow.reader/reader","package":"os.meow.reader","version":"0.1.3","version_code":4,"framework":1,"fonts":"common","author":"MeowOS","icon":"icon.png","volume_page":true}
|
||||||
@@ -29,9 +29,10 @@ static int g_reader_active;
|
|||||||
static meow_widget *g_canvas;
|
static meow_widget *g_canvas;
|
||||||
static int g_pw, g_ph, g_top, g_mx, g_my;
|
static int g_pw, g_ph, g_top, g_mx, g_my;
|
||||||
static int g_fs, g_ls;
|
static int g_fs, g_ls;
|
||||||
#define RD_PAGE_CACHE 2
|
/* M30:只保留当前页位图。原先的 next 预取槽全工程无消费者(翻页走 rd_book_turn + 重渲染
|
||||||
#define RD_PAGE_SLOT_CUR 0
|
* 当前页,不换槽),每页白渲染一页(真机 ~220ms)+ 常驻 480×720×2B = 0.69MB。 */
|
||||||
static uint16_t *g_page_buf[RD_PAGE_CACHE]; /* [0]=cur [1]=next(FreeType 页位图;B.1 降 3→2 槽,省 1 页 0.69MB) */
|
static uint16_t *g_page_buf; /* 当前页位图(FreeType/图片路径) */
|
||||||
|
static int g_page_valid; /* 1=位图内容即当前页(开书 refill 后 on_enter 复用,免二次渲染) */
|
||||||
static int g_has_font; /* 1=FreeType 路径;0=SDK 字库兜底 */
|
static int g_has_font; /* 1=FreeType 路径;0=SDK 字库兜底 */
|
||||||
static int g_menu_open;
|
static int g_menu_open;
|
||||||
static int g_ui_open; /* 「界面」排版面板(B) */
|
static int g_ui_open; /* 「界面」排版面板(B) */
|
||||||
@@ -791,9 +792,9 @@ static void reader_canvas_render(meow_widget *w, const meow_theme *th, void *ctx
|
|||||||
chapter_name(); /* 每帧同步顶栏/页底章节名(就绪后取章名,否则文件名) */
|
chapter_name(); /* 每帧同步顶栏/页底章节名(就绪后取章名,否则文件名) */
|
||||||
if (g_book) {
|
if (g_book) {
|
||||||
int page = rd_book_current(g_book);
|
int page = rd_book_current(g_book);
|
||||||
if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf[RD_PAGE_SLOT_CUR]) {
|
if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf) {
|
||||||
/* 翻页 blit:行拷贝当前页位图 → back_buf(局部脏区由 flush_region 上屏) */
|
/* 翻页 blit:行拷贝当前页位图 → back_buf(局部脏区由 flush_region 上屏) */
|
||||||
blit_rows(g_page_buf[RD_PAGE_SLOT_CUR], 0, 0, g_ph);
|
blit_rows(g_page_buf, 0, 0, g_ph);
|
||||||
} else {
|
} else {
|
||||||
rd_book_render_text(g_book, page, reader_draw_line_atlas, NULL);
|
rd_book_render_text(g_book, page, reader_draw_line_atlas, NULL);
|
||||||
}
|
}
|
||||||
@@ -884,37 +885,35 @@ static void reader_redraw(void)
|
|||||||
|
|
||||||
static void reader_free_page_bufs(void)
|
static void reader_free_page_bufs(void)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < RD_PAGE_CACHE; i++) { free(g_page_buf[i]); g_page_buf[i] = NULL; }
|
free(g_page_buf); g_page_buf = NULL;
|
||||||
|
g_page_valid = 0;
|
||||||
if (g_anim_old) { free(g_anim_old); g_anim_old = NULL; }
|
if (g_anim_old) { free(g_anim_old); g_anim_old = NULL; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分配 FreeType 页位图槽(cur+next 两槽)。已备当前槽则幂等返回(开书/重入共用)。
|
/* 分配当前页位图。已备则幂等返回(开书/重入共用)。
|
||||||
* 动画快照 g_anim_old 不在此常驻:翻页时懒分配、动画完成即释放(见 turn_animated/anim_done)。
|
* 动画快照 g_anim_old 不在此常驻:翻页时懒分配、动画完成即释放(见 turn_animated/anim_done)。
|
||||||
* 分配失败 → g_has_font=0,走字库兜底路径。 */
|
* 分配失败 → g_has_font=0,走字库兜底路径。 */
|
||||||
static void reader_alloc_page_bufs(void)
|
static void reader_alloc_page_bufs(void)
|
||||||
{
|
{
|
||||||
if (!(g_has_font || rd_book_is_image(g_book))) return;
|
if (!(g_has_font || rd_book_is_image(g_book))) return;
|
||||||
if (g_page_buf[RD_PAGE_SLOT_CUR]) return; /* 已备当前槽(重入)→ 幂等 */
|
if (g_page_buf) return; /* 已备(重入)→ 幂等 */
|
||||||
for (int i = 0; i < RD_PAGE_CACHE; i++)
|
g_page_buf = (uint16_t *)malloc((size_t)g_pw * (size_t)g_ph * 2);
|
||||||
g_page_buf[i] = (uint16_t *)malloc((size_t)g_pw * (size_t)g_ph * 2);
|
g_page_valid = 0;
|
||||||
if (!g_page_buf[RD_PAGE_SLOT_CUR]) { reader_free_page_bufs(); g_has_font = 0; }
|
if (!g_page_buf) g_has_font = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 渲染当前页 + 下一预取页到 2 槽缓存(B.1:降 3→2,去 prev 槽;动画档 无/滑动/淡入 R1 默认无)。 */
|
/* 渲染当前页到位图(M30:不再预渲染下一页——那一槽没有消费者)。 */
|
||||||
static int reader_refill_page(void)
|
static int reader_refill_page(void)
|
||||||
{
|
{
|
||||||
if (!g_book) return -1;
|
if (!g_book) return -1;
|
||||||
if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf[RD_PAGE_SLOT_CUR]) {
|
if ((g_has_font || rd_book_is_image(g_book)) && g_page_buf) {
|
||||||
uint16_t bg = rgb565(g_page_bg), fg = rgb565(g_page_fg);
|
uint16_t bg = rgb565(g_page_bg), fg = rgb565(g_page_fg);
|
||||||
int cur = rd_book_current(g_book);
|
int cur = rd_book_current(g_book);
|
||||||
int np = rd_book_pages(g_book);
|
int np = rd_book_pages(g_book);
|
||||||
int slots[RD_PAGE_CACHE] = { cur, cur + 1 };
|
if (cur >= 0 && cur < np) rd_book_render(g_book, cur, g_page_buf, g_pw * 2, bg, fg);
|
||||||
for (int i = 0; i < RD_PAGE_CACHE; i++) {
|
else for (int y = 0; y < g_ph; y++) /* 无页可画:填背景 */
|
||||||
int p = slots[i];
|
for (int x = 0; x < g_pw; x++) g_page_buf[(size_t)y * g_pw + x] = bg;
|
||||||
if (p >= 0 && p < np) rd_book_render(g_book, p, g_page_buf[i], g_pw * 2, bg, fg);
|
g_page_valid = 1;
|
||||||
else for (int y = 0; y < g_ph; y++) /* 空白槽填背景 */
|
|
||||||
for (int x = 0; x < g_pw; x++) g_page_buf[i][(size_t)y * g_pw + x] = bg;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 0; /* 兜底路径:render_text 直绘 */
|
return 0; /* 兜底路径:render_text 直绘 */
|
||||||
@@ -924,7 +923,7 @@ static int reader_refill_page(void)
|
|||||||
static void reader_anim_update(meow_anim *a, float v, void *ctx)
|
static void reader_anim_update(meow_anim *a, float v, void *ctx)
|
||||||
{
|
{
|
||||||
(void)a; (void)ctx;
|
(void)a; (void)ctx;
|
||||||
if (!g_book || !g_anim_old || !g_page_buf[RD_PAGE_SLOT_CUR]) return;
|
if (!g_book || !g_anim_old || !g_page_buf) return;
|
||||||
int off = (int)(v * g_pw);
|
int off = (int)(v * g_pw);
|
||||||
int y0 = rect_y0(), y1 = rect_y1();
|
int y0 = rect_y0(), y1 = rect_y1();
|
||||||
meow_ui_fill_rect(0, y0, g_pw, y1 - y0, g_page_bg); /* 内容区清底色 */
|
meow_ui_fill_rect(0, y0, g_pw, y1 - y0, g_page_bg); /* 内容区清底色 */
|
||||||
@@ -932,7 +931,7 @@ static void reader_anim_update(meow_anim *a, float v, void *ctx)
|
|||||||
if (g_anim_dir == 1) { old_x = -off; new_x = g_pw - off; } /* 下一页:旧左移/新右入 */
|
if (g_anim_dir == 1) { old_x = -off; new_x = g_pw - off; } /* 下一页:旧左移/新右入 */
|
||||||
else { old_x = off; new_x = off - g_pw; } /* 上一页:旧右移/新左入 */
|
else { old_x = off; new_x = off - g_pw; } /* 上一页:旧右移/新左入 */
|
||||||
blit_rows(g_anim_old, old_x, y0, y1);
|
blit_rows(g_anim_old, old_x, y0, y1);
|
||||||
blit_rows(g_page_buf[RD_PAGE_SLOT_CUR], new_x, y0, y1);
|
blit_rows(g_page_buf, new_x, y0, y1);
|
||||||
meow_ui_flush_region(0, y0, g_pw, y1 - y0);
|
meow_ui_flush_region(0, y0, g_pw, y1 - y0);
|
||||||
}
|
}
|
||||||
static void reader_anim_done(meow_anim *a, int canceled, void *ctx)
|
static void reader_anim_done(meow_anim *a, int canceled, void *ctx)
|
||||||
@@ -948,7 +947,7 @@ static void reader_turn_animated(int dir)
|
|||||||
if (!g_book) return;
|
if (!g_book) return;
|
||||||
if (g_anim) { meow_anim_cancel(g_anim); g_anim = NULL; } /* 取消进行中动画,重开 */
|
if (g_anim) { meow_anim_cancel(g_anim); g_anim = NULL; } /* 取消进行中动画,重开 */
|
||||||
/* 无页位图(atlas 兜底):瞬切 */
|
/* 无页位图(atlas 兜底):瞬切 */
|
||||||
if (!(g_has_font || rd_book_is_image(g_book)) || !g_page_buf[RD_PAGE_SLOT_CUR]) {
|
if (!(g_has_font || rd_book_is_image(g_book)) || !g_page_buf) {
|
||||||
rd_book_turn(g_book, dir);
|
rd_book_turn(g_book, dir);
|
||||||
g_prog_dirty = 1;
|
g_prog_dirty = 1;
|
||||||
reader_refill_page(); reader_redraw(); reader_reflow_persist();
|
reader_refill_page(); reader_redraw(); reader_reflow_persist();
|
||||||
@@ -956,7 +955,7 @@ static void reader_turn_animated(int dir)
|
|||||||
}
|
}
|
||||||
if (!g_anim_old) g_anim_old = (uint16_t *)malloc((size_t)g_pw * g_ph * 2);
|
if (!g_anim_old) g_anim_old = (uint16_t *)malloc((size_t)g_pw * g_ph * 2);
|
||||||
if (!g_anim_old) { rd_book_turn(g_book, dir); g_prog_dirty = 1; reader_refill_page(); reader_redraw(); return; }
|
if (!g_anim_old) { rd_book_turn(g_book, dir); g_prog_dirty = 1; reader_refill_page(); reader_redraw(); return; }
|
||||||
memcpy(g_anim_old, g_page_buf[RD_PAGE_SLOT_CUR], (size_t)g_pw * g_ph * 2);
|
memcpy(g_anim_old, g_page_buf, (size_t)g_pw * g_ph * 2);
|
||||||
rd_book_turn(g_book, dir);
|
rd_book_turn(g_book, dir);
|
||||||
g_prog_dirty = 1;
|
g_prog_dirty = 1;
|
||||||
reader_refill_page();
|
reader_refill_page();
|
||||||
@@ -1005,7 +1004,7 @@ static void reader_scrub_preview(int page)
|
|||||||
meow_ui_text_right(g_pw - 16, fy + 1, ft, 16, g_page_fg);
|
meow_ui_text_right(g_pw - 16, fy + 1, ft, 16, g_page_fg);
|
||||||
}
|
}
|
||||||
/* 右缘条区清残留(恢复页面内容)再画条 + 预览框;无页位图(字库兜底)则跳过恢复 */
|
/* 右缘条区清残留(恢复页面内容)再画条 + 预览框;无页位图(字库兜底)则跳过恢复 */
|
||||||
if (g_page_buf[RD_PAGE_SLOT_CUR]) blit_sub(g_page_buf[RD_PAGE_SLOT_CUR], g_pw - 120, y0, 120, y1 - y0);
|
if (g_page_buf) blit_sub(g_page_buf, g_pw - 120, y0, 120, y1 - y0);
|
||||||
reader_draw_scrub(th);
|
reader_draw_scrub(th);
|
||||||
if (g_show_footer) meow_ui_flush_region(0, g_ph - RD_FOOT_H, g_pw, RD_FOOT_H);
|
if (g_show_footer) meow_ui_flush_region(0, g_ph - RD_FOOT_H, g_pw, RD_FOOT_H);
|
||||||
meow_ui_flush_region(g_pw - 120, y0, 120, y1 - y0);
|
meow_ui_flush_region(g_pw - 120, y0, 120, y1 - y0);
|
||||||
@@ -1092,7 +1091,7 @@ int reader_open(const char *path)
|
|||||||
|
|
||||||
if (g_has_font || rd_book_is_image(g_book)) {
|
if (g_has_font || rd_book_is_image(g_book)) {
|
||||||
reader_free_page_bufs(); /* 丢弃上一本书的页位图(重开/换书) */
|
reader_free_page_bufs(); /* 丢弃上一本书的页位图(重开/换书) */
|
||||||
reader_alloc_page_bufs(); /* 重建 cur+next 两槽;g_anim_old 翻页时懒分配 */
|
reader_alloc_page_bufs(); /* 重建当前页位图;g_anim_old 翻页时懒分配 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 复位动画/刮擦/覆盖层状态(跨书) */
|
/* 复位动画/刮擦/覆盖层状态(跨书) */
|
||||||
@@ -1150,7 +1149,9 @@ static void reader_on_enter(meow_view *v, void *ctx)
|
|||||||
g_reader_active = 1;
|
g_reader_active = 1;
|
||||||
if (g_book) {
|
if (g_book) {
|
||||||
reader_alloc_page_bufs(); /* 离开已释放页位图 → 重建;未释放则幂等返回 */
|
reader_alloc_page_bufs(); /* 离开已释放页位图 → 重建;未释放则幂等返回 */
|
||||||
reader_refill_page();
|
/* 开书路径 reader_open 已渲染好当前页(g_page_valid=1)→ 这里不再重复渲染;
|
||||||
|
* 从其它页返回(leave 释放过位图)才 refill。 */
|
||||||
|
if (!g_page_valid) reader_refill_page();
|
||||||
chapter_name();
|
chapter_name();
|
||||||
}
|
}
|
||||||
reader_redraw();
|
reader_redraw();
|
||||||
@@ -1159,7 +1160,7 @@ static void reader_on_leave(meow_view *v, void *ctx)
|
|||||||
{
|
{
|
||||||
(void)v; (void)ctx;
|
(void)v; (void)ctx;
|
||||||
reader_reflow_persist(); /* 离开保存进度 */
|
reader_reflow_persist(); /* 离开保存进度 */
|
||||||
reader_free_page_bufs(); /* 离开阅读页释放页位图(cur+next 两槽+动画快照)→ on_enter 重建 */
|
reader_free_page_bufs(); /* 离开阅读页释放页位图(当前页 + 动画快照)→ on_enter 重建 */
|
||||||
g_reader_active = 0;
|
g_reader_active = 0;
|
||||||
}
|
}
|
||||||
static void reader_on_destroy(meow_view *v, void *ctx)
|
static void reader_on_destroy(meow_view *v, void *ctx)
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
* M29 性能(用户反馈「几百本书首页卡得飞起」):
|
* M29 性能(用户反馈「几百本书首页卡得飞起」):
|
||||||
* - **扫描结果进程内缓存**:只在本进程首次进入书架时扫 Books/;从阅读页返回不再重扫
|
* - **扫描结果进程内缓存**:只在本进程首次进入书架时扫 Books/;从阅读页返回不再重扫
|
||||||
* (真机 exFAT 全量扫描 3.1s,是「回书架冻结」的唯一大头)。顶栏「刷新」强制重扫。
|
* (真机 exFAT 全量扫描 3.1s,是「回书架冻结」的唯一大头)。顶栏「刷新」强制重扫。
|
||||||
* 行为变化:新放进 Books/ 的书、以及刚读完那本书的「最近阅读置顶」需点一次「刷新」才更新。
|
* 行为变化:新放进 Books/ 的书需点一次「刷新」才出现。
|
||||||
* - **行数多时关圆角容器**(见 SHELF_ROUND_MAX_ROWS):圆角卡逐像素填充,595 本时滑一步 46ms。
|
* - **行数多时关圆角容器**(见 SHELF_ROUND_MAX_ROWS):圆角卡逐像素填充,595 本时滑一步 46ms。
|
||||||
|
* M30:M29 的「刚读完不置顶」缺口补上(只刷一本书的 ts + 内存重排,不重扫全盘)。
|
||||||
*/
|
*/
|
||||||
#include "../reader_internal.h"
|
#include "../reader_internal.h"
|
||||||
#include "progress.h"
|
#include "progress.h"
|
||||||
@@ -39,6 +40,8 @@ static shelf_book g_books[SHELF_MAX_BOOKS];
|
|||||||
static int g_nbooks;
|
static int g_nbooks;
|
||||||
static int g_scanned; /* 1=本进程已扫过 Books/(进书架缓存;「刷新」动作强制重扫) */
|
static int g_scanned; /* 1=本进程已扫过 Books/(进书架缓存;「刷新」动作强制重扫) */
|
||||||
static int g_active;
|
static int g_active;
|
||||||
|
/* M30:刚打开过的书(返回书架时只刷这一本的 ts 并重排,避免为「最近阅读置顶」重扫全盘)。 */
|
||||||
|
static char g_just_read[400];
|
||||||
static meow_widget *g_list; /* listview(布局=list) */
|
static meow_widget *g_list; /* listview(布局=list) */
|
||||||
static meow_lazygrid *g_grid; /* lazygrid(布局=grid) */
|
static meow_lazygrid *g_grid; /* lazygrid(布局=grid) */
|
||||||
static meow_widget *g_appbar; /* meow_topappbar */
|
static meow_widget *g_appbar; /* meow_topappbar */
|
||||||
@@ -111,6 +114,23 @@ static void build_rows(void)
|
|||||||
g_nrows = shelf_build_rows(g_books, g_nbooks, g_group_folder, g_rows, SHELF_MAX_ROWS);
|
g_nrows = shelf_build_rows(g_books, g_nbooks, g_group_folder, g_rows, SHELF_MAX_ROWS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* M30:从阅读页返回时把「刚读完」置顶。只读这一本的进度文件 + 重排内存里的 g_books,
|
||||||
|
* 不 opendir/不遍历目录(对比全量重扫真机 3s)。重排后 on_enter 照常 build_rows + 重建控件。 */
|
||||||
|
static void promote_just_read(void)
|
||||||
|
{
|
||||||
|
if (!g_just_read[0]) return;
|
||||||
|
for (int i = 0; i < g_nbooks; i++) {
|
||||||
|
if (strcmp(g_books[i].path, g_just_read) != 0) continue;
|
||||||
|
uint64_t ts = shelf_ts_of(g_books[i].path);
|
||||||
|
if (ts > g_books[i].ts) {
|
||||||
|
g_books[i].ts = ts;
|
||||||
|
shelf_sort_recent(g_books, g_nbooks);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_just_read[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- list 数据源(vsource + 段头;container on) ---- */
|
/* ---- list 数据源(vsource + 段头;container on) ---- */
|
||||||
static int shelf_list_count(void *ctx) { (void)ctx; return g_nrows; }
|
static int shelf_list_count(void *ctx) { (void)ctx; return g_nrows; }
|
||||||
static void shelf_list_bind(int idx, meow_lv_item *item, void *ctx)
|
static void shelf_list_bind(int idx, meow_lv_item *item, void *ctx)
|
||||||
@@ -147,7 +167,10 @@ static void shelf_list_tapcb(int idx, int op, void *ctx)
|
|||||||
if (bi < 0 || bi >= g_nbooks) return;
|
if (bi < 0 || bi >= g_nbooks) return;
|
||||||
/* 点书开读:reader_open 打开书(含页位图)→ push ACT_READER 让阅读页成为 view activity,
|
/* 点书开读:reader_open 打开书(含页位图)→ push ACT_READER 让阅读页成为 view activity,
|
||||||
* 驱动 reader_on_enter/on_leave(重建/释放页位图);「返回书架」/左缘返回走 page_pop 回书架。 */
|
* 驱动 reader_on_enter/on_leave(重建/释放页位图);「返回书架」/左缘返回走 page_pop 回书架。 */
|
||||||
if (reader_open(g_books[bi].path) == 0) meow_view_page_push(ACT_READER);
|
if (reader_open(g_books[bi].path) == 0) {
|
||||||
|
snprintf(g_just_read, sizeof g_just_read, "%s", g_books[bi].path); /* 返回时置顶用 */
|
||||||
|
meow_view_page_push(ACT_READER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- grid 数据源(meow_lazygrid,4 列封面卡) ---- */
|
/* ---- grid 数据源(meow_lazygrid,4 列封面卡) ---- */
|
||||||
@@ -204,7 +227,10 @@ static void shelf_grid_tapcb(int idx, void *ctx)
|
|||||||
{
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
if (idx < 0 || idx >= g_nbooks) return;
|
if (idx < 0 || idx >= g_nbooks) return;
|
||||||
if (reader_open(g_books[idx].path) == 0) meow_view_page_push(ACT_READER);
|
if (reader_open(g_books[idx].path) == 0) {
|
||||||
|
snprintf(g_just_read, sizeof g_just_read, "%s", g_books[idx].path); /* 返回时置顶用 */
|
||||||
|
meow_view_page_push(ACT_READER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 控件装配(按当前布局重建 list / grid) ---- */
|
/* ---- 控件装配(按当前布局重建 list / grid) ---- */
|
||||||
@@ -274,9 +300,11 @@ static void shelf_on_enter(meow_view *v, void *ctx)
|
|||||||
g_group_folder = (c && strcmp(c->shelf_group, "folder") == 0) ? 1 : 0;
|
g_group_folder = (c && strcmp(c->shelf_group, "folder") == 0) ? 1 : 0;
|
||||||
/* 书架缓存:本进程只在首次进入时扫一次 Books/(从阅读页返回不再重扫)。
|
/* 书架缓存:本进程只在首次进入时扫一次 Books/(从阅读页返回不再重扫)。
|
||||||
* 扫描在真机是 3.1s(exFAT 434 目录 opendir/readdir),是「回书架冻结」的唯一大头。
|
* 扫描在真机是 3.1s(exFAT 434 目录 opendir/readdir),是「回书架冻结」的唯一大头。
|
||||||
* 代价:新放入的书 / 刚读完的书不会自动置顶,需点顶栏「刷新」(强制重扫)。
|
* 代价:新放入的书不会自动出现,需点顶栏「刷新」(强制重扫)。
|
||||||
|
* M30:刚读完那本例外——promote_just_read() 只读它一个进度文件并重排,低成本置顶。
|
||||||
* 提示条(超上限截断 / 超深目录)语义不变:首次扫描时算好并保留在 g_notice*。 */
|
* 提示条(超上限截断 / 超深目录)语义不变:首次扫描时算好并保留在 g_notice*。 */
|
||||||
if (!g_scanned) scan_books();
|
if (!g_scanned) scan_books();
|
||||||
|
else promote_just_read();
|
||||||
build_rows();
|
build_rows();
|
||||||
const meow_theme *th = meow_theme_get();
|
const meow_theme *th = meow_theme_get();
|
||||||
g_bg = th->bg; g_fg = th->fg; g_dim = th->dim;
|
g_bg = th->bg; g_fg = th->fg; g_dim = th->dim;
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ static int cmp_recent_then_name(const void *a, const void *b)
|
|||||||
return strcasecmp(x->title, y->title);
|
return strcasecmp(x->title, y->title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 最近阅读排序(扫描后 / 刚读完置顶共用同一口径)。 */
|
||||||
|
void shelf_sort_recent(shelf_book *books, int nbooks)
|
||||||
|
{
|
||||||
|
if (!books || nbooks < 2) return;
|
||||||
|
qsort(books, (size_t)nbooks, sizeof(shelf_book), cmp_recent_then_name);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct scan_ctx {
|
typedef struct scan_ctx {
|
||||||
shelf_book *books;
|
shelf_book *books;
|
||||||
int cap;
|
int cap;
|
||||||
@@ -177,7 +184,7 @@ int shelf_scan_books(const char *root, shelf_book *books, int cap,
|
|||||||
scan_ctx c;
|
scan_ctx c;
|
||||||
c.books = books; c.cap = cap; c.n = 0; c.truncated = 0; c.depth_skipped = 0; c.ts_fn = ts_fn;
|
c.books = books; c.cap = cap; c.n = 0; c.truncated = 0; c.depth_skipped = 0; c.ts_fn = ts_fn;
|
||||||
scan_dir(&c, root, 0, "", "");
|
scan_dir(&c, root, 0, "", "");
|
||||||
qsort(books, (size_t)c.n, sizeof(shelf_book), cmp_recent_then_name);
|
shelf_sort_recent(books, c.n);
|
||||||
if (rep) {
|
if (rep) {
|
||||||
rep->nbooks = c.n;
|
rep->nbooks = c.n;
|
||||||
rep->truncated = c.truncated;
|
rep->truncated = c.truncated;
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ typedef struct shelf_row {
|
|||||||
/* 进度时间戳查询(生产=rdprog_load;测试/基线=NULL,全部 ts=0)。 */
|
/* 进度时间戳查询(生产=rdprog_load;测试/基线=NULL,全部 ts=0)。 */
|
||||||
typedef uint64_t (*shelf_ts_fn)(const char *path);
|
typedef uint64_t (*shelf_ts_fn)(const char *path);
|
||||||
|
|
||||||
|
/* 最近阅读排序:ts 降序,同 ts 按书名(不区分大小写)。扫描后与「刚读完置顶」共用。 */
|
||||||
|
void shelf_sort_recent(shelf_book *books, int nbooks);
|
||||||
|
|
||||||
typedef struct shelf_scan_report {
|
typedef struct shelf_scan_report {
|
||||||
int nbooks;
|
int nbooks;
|
||||||
int truncated; /* 1=有书因 SHELF_MAX_BOOKS 上限被丢弃 */
|
int truncated; /* 1=有书因 SHELF_MAX_BOOKS 上限被丢弃 */
|
||||||
|
|||||||
Reference in new issue
Block a user