feat(ui): add progress bar for chapter indexing

- Add progressFn callback to ChapterHtmlSlimParser for file-based progress
- Update progress every 10% to avoid excessive e-ink refreshes
- Display 200px progress bar below "Indexing..." text
- Use FAST_REFRESH for responsive progress updates
This commit is contained in:
Eunchurn Park 2025-12-25 19:53:46 +09:00
parent 8c726549bf
commit f11d70279e
No known key found for this signature in database
GPG Key ID: 29D94D9C697E3F92
5 changed files with 53 additions and 15 deletions

View File

@ -115,7 +115,7 @@ bool Section::clearCache() const {
bool Section::persistPageDataToSD(const int fontId, const float lineCompression, const int marginTop, bool Section::persistPageDataToSD(const int fontId, const float lineCompression, const int marginTop,
const int marginRight, const int marginBottom, const int marginLeft, const int marginRight, const int marginBottom, const int marginLeft,
const bool extraParagraphSpacing) { const bool extraParagraphSpacing, const std::function<void(int)>& progressFn) {
const auto localPath = epub->getSpineItem(spineIndex).href; const auto localPath = epub->getSpineItem(spineIndex).href;
const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html"; const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html";
@ -144,7 +144,8 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
ChapterHtmlSlimParser visitor(tmpHtmlPath, renderer, fontId, lineCompression, marginTop, marginRight, marginBottom, ChapterHtmlSlimParser visitor(tmpHtmlPath, renderer, fontId, lineCompression, marginTop, marginRight, marginBottom,
marginLeft, extraParagraphSpacing, marginLeft, extraParagraphSpacing,
[this](std::unique_ptr<Page> page) { this->onPageComplete(std::move(page)); }); [this](std::unique_ptr<Page> page) { this->onPageComplete(std::move(page)); },
progressFn);
success = visitor.parseAndBuildPages(); success = visitor.parseAndBuildPages();
SD.remove(tmpHtmlPath.c_str()); SD.remove(tmpHtmlPath.c_str());

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <functional>
#include <memory> #include <memory>
#include "Epub.h" #include "Epub.h"
@ -31,6 +32,7 @@ class Section {
void setupCacheDir() const; void setupCacheDir() const;
bool clearCache() const; bool clearCache() const;
bool persistPageDataToSD(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom, bool persistPageDataToSD(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom,
int marginLeft, bool extraParagraphSpacing); int marginLeft, bool extraParagraphSpacing,
const std::function<void(int)>& progressFn = nullptr);
std::unique_ptr<Page> loadPageFromSD() const; std::unique_ptr<Page> loadPageFromSD() const;
}; };

View File

@ -221,6 +221,11 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
return false; return false;
} }
// Get file size for progress calculation
const size_t totalSize = file.size();
size_t bytesRead = 0;
int lastProgress = -1;
XML_SetUserData(parser, this); XML_SetUserData(parser, this);
XML_SetElementHandler(parser, startElement, endElement); XML_SetElementHandler(parser, startElement, endElement);
XML_SetCharacterDataHandler(parser, characterData); XML_SetCharacterDataHandler(parser, characterData);
@ -249,6 +254,16 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
return false; return false;
} }
// Update progress (call every 10% change to avoid too frequent updates)
bytesRead += len;
if (progressFn && totalSize > 0) {
const int progress = static_cast<int>((bytesRead * 100) / totalSize);
if (progress != lastProgress && progress % 10 == 0) {
lastProgress = progress;
progressFn(progress);
}
}
done = file.available() == 0; done = file.available() == 0;
if (XML_ParseBuffer(parser, static_cast<int>(len), done) == XML_STATUS_ERROR) { if (XML_ParseBuffer(parser, static_cast<int>(len), done) == XML_STATUS_ERROR) {

View File

@ -18,6 +18,7 @@ class ChapterHtmlSlimParser {
const std::string& filepath; const std::string& filepath;
GfxRenderer& renderer; GfxRenderer& renderer;
std::function<void(std::unique_ptr<Page>)> completePageFn; std::function<void(std::unique_ptr<Page>)> completePageFn;
std::function<void(int)> progressFn; // Progress callback (0-100)
int depth = 0; int depth = 0;
int skipUntilDepth = INT_MAX; int skipUntilDepth = INT_MAX;
int boldUntilDepth = INT_MAX; int boldUntilDepth = INT_MAX;
@ -48,7 +49,8 @@ class ChapterHtmlSlimParser {
explicit ChapterHtmlSlimParser(const std::string& filepath, GfxRenderer& renderer, const int fontId, explicit ChapterHtmlSlimParser(const std::string& filepath, GfxRenderer& renderer, const int fontId,
const float lineCompression, const int marginTop, const int marginRight, const float lineCompression, const int marginTop, const int marginRight,
const int marginBottom, const int marginLeft, const bool extraParagraphSpacing, const int marginBottom, const int marginLeft, const bool extraParagraphSpacing,
const std::function<void(std::unique_ptr<Page>)>& completePageFn) const std::function<void(std::unique_ptr<Page>)>& completePageFn,
const std::function<void(int)>& progressFn = nullptr)
: filepath(filepath), : filepath(filepath),
renderer(renderer), renderer(renderer),
fontId(fontId), fontId(fontId),
@ -58,7 +60,8 @@ class ChapterHtmlSlimParser {
marginBottom(marginBottom), marginBottom(marginBottom),
marginLeft(marginLeft), marginLeft(marginLeft),
extraParagraphSpacing(extraParagraphSpacing), extraParagraphSpacing(extraParagraphSpacing),
completePageFn(completePageFn) {} completePageFn(completePageFn),
progressFn(progressFn) {}
~ChapterHtmlSlimParser() = default; ~ChapterHtmlSlimParser() = default;
bool parseAndBuildPages(); bool parseAndBuildPages();
void addLineToPage(std::shared_ptr<TextBlock> line); void addLineToPage(std::shared_ptr<TextBlock> line);

View File

@ -219,23 +219,40 @@ void EpubReaderActivity::renderScreen() {
SETTINGS.extraParagraphSpacing)) { SETTINGS.extraParagraphSpacing)) {
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis()); Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
// Progress bar dimensions
constexpr int barWidth = 200;
constexpr int barHeight = 10;
constexpr int boxMargin = 20;
const int textWidth = renderer.getTextWidth(READER_FONT_ID, "Indexing...");
const int boxWidth = (barWidth > textWidth ? barWidth : textWidth) + boxMargin * 2;
const int boxHeight = renderer.getLineHeight(READER_FONT_ID) + barHeight + boxMargin * 3;
const int boxX = (GfxRenderer::getScreenWidth() - boxWidth) / 2;
constexpr int boxY = 50;
const int barX = boxX + (boxWidth - barWidth) / 2;
const int barY = boxY + renderer.getLineHeight(READER_FONT_ID) + boxMargin * 2;
// Draw initial indexing box with 0% progress
{ {
const int textWidth = renderer.getTextWidth(READER_FONT_ID, "Indexing..."); renderer.fillRect(boxX, boxY, boxWidth, boxHeight, false);
constexpr int margin = 20; renderer.drawText(READER_FONT_ID, boxX + boxMargin, boxY + boxMargin, "Indexing...");
const int x = (GfxRenderer::getScreenWidth() - textWidth - margin * 2) / 2; renderer.drawRect(boxX + 5, boxY + 5, boxWidth - 10, boxHeight - 10);
constexpr int y = 50; // Draw empty progress bar outline
const int w = textWidth + margin * 2; renderer.drawRect(barX, barY, barWidth, barHeight);
const int h = renderer.getLineHeight(READER_FONT_ID) + margin * 2;
renderer.fillRect(x, y, w, h, false);
renderer.drawText(READER_FONT_ID, x + margin, y + margin, "Indexing...");
renderer.drawRect(x + 5, y + 5, w - 10, h - 10);
renderer.displayBuffer(); renderer.displayBuffer();
pagesUntilFullRefresh = 0; pagesUntilFullRefresh = 0;
} }
section->setupCacheDir(); section->setupCacheDir();
// Progress callback to update progress bar
auto progressCallback = [this, barX, barY, barWidth, barHeight](int progress) {
const int fillWidth = (barWidth - 2) * progress / 100;
renderer.fillRect(barX + 1, barY + 1, fillWidth, barHeight - 2, true);
renderer.displayBuffer(EInkDisplay::FAST_REFRESH);
};
if (!section->persistPageDataToSD(READER_FONT_ID, lineCompression, marginTop, marginRight, marginBottom, if (!section->persistPageDataToSD(READER_FONT_ID, lineCompression, marginTop, marginRight, marginBottom,
marginLeft, SETTINGS.extraParagraphSpacing)) { marginLeft, SETTINGS.extraParagraphSpacing, progressCallback)) {
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis()); Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
section.reset(); section.reset();
return; return;