diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index 76c39d8a..da35443e 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -115,7 +115,7 @@ bool Section::clearCache() const { bool Section::persistPageDataToSD(const int fontId, const float lineCompression, const int marginTop, const int marginRight, const int marginBottom, const int marginLeft, - const bool extraParagraphSpacing) { + const bool extraParagraphSpacing, const std::function& progressFn) { const auto localPath = epub->getSpineItem(spineIndex).href; 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, marginLeft, extraParagraphSpacing, - [this](std::unique_ptr page) { this->onPageComplete(std::move(page)); }); + [this](std::unique_ptr page) { this->onPageComplete(std::move(page)); }, + progressFn); success = visitor.parseAndBuildPages(); SD.remove(tmpHtmlPath.c_str()); diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index d7a2c721..d4e76c62 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include "Epub.h" @@ -31,6 +32,7 @@ class Section { void setupCacheDir() const; bool clearCache() const; bool persistPageDataToSD(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom, - int marginLeft, bool extraParagraphSpacing); + int marginLeft, bool extraParagraphSpacing, + const std::function& progressFn = nullptr); std::unique_ptr loadPageFromSD() const; }; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 766e5ca6..0b1bb8db 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -221,6 +221,11 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { 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_SetElementHandler(parser, startElement, endElement); XML_SetCharacterDataHandler(parser, characterData); @@ -249,6 +254,16 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { return false; } + // Update progress (call every 10% change to avoid too frequent updates) + bytesRead += len; + if (progressFn && totalSize > 0) { + const int progress = static_cast((bytesRead * 100) / totalSize); + if (progress != lastProgress && progress % 10 == 0) { + lastProgress = progress; + progressFn(progress); + } + } + done = file.available() == 0; if (XML_ParseBuffer(parser, static_cast(len), done) == XML_STATUS_ERROR) { diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index 7f74602a..7d753173 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -18,6 +18,7 @@ class ChapterHtmlSlimParser { const std::string& filepath; GfxRenderer& renderer; std::function)> completePageFn; + std::function progressFn; // Progress callback (0-100) int depth = 0; int skipUntilDepth = INT_MAX; int boldUntilDepth = INT_MAX; @@ -48,7 +49,8 @@ class ChapterHtmlSlimParser { explicit ChapterHtmlSlimParser(const std::string& filepath, GfxRenderer& renderer, const int fontId, const float lineCompression, const int marginTop, const int marginRight, const int marginBottom, const int marginLeft, const bool extraParagraphSpacing, - const std::function)>& completePageFn) + const std::function)>& completePageFn, + const std::function& progressFn = nullptr) : filepath(filepath), renderer(renderer), fontId(fontId), @@ -58,7 +60,8 @@ class ChapterHtmlSlimParser { marginBottom(marginBottom), marginLeft(marginLeft), extraParagraphSpacing(extraParagraphSpacing), - completePageFn(completePageFn) {} + completePageFn(completePageFn), + progressFn(progressFn) {} ~ChapterHtmlSlimParser() = default; bool parseAndBuildPages(); void addLineToPage(std::shared_ptr line); diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 6195ec22..cc9b9fc1 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -219,23 +219,40 @@ void EpubReaderActivity::renderScreen() { SETTINGS.extraParagraphSpacing)) { 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..."); - constexpr int margin = 20; - const int x = (GfxRenderer::getScreenWidth() - textWidth - margin * 2) / 2; - constexpr int y = 50; - const int w = textWidth + margin * 2; - 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.fillRect(boxX, boxY, boxWidth, boxHeight, false); + renderer.drawText(READER_FONT_ID, boxX + boxMargin, boxY + boxMargin, "Indexing..."); + renderer.drawRect(boxX + 5, boxY + 5, boxWidth - 10, boxHeight - 10); + // Draw empty progress bar outline + renderer.drawRect(barX, barY, barWidth, barHeight); renderer.displayBuffer(); pagesUntilFullRefresh = 0; } 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, - marginLeft, SETTINGS.extraParagraphSpacing)) { + marginLeft, SETTINGS.extraParagraphSpacing, progressCallback)) { Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis()); section.reset(); return;