From 400b92e78880aedf66c0df15894c2226f8a0d9c7 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Tue, 20 Jan 2026 08:42:35 +0500 Subject: [PATCH 01/18] refactor: streamline popup rendering and remove unused BootActivity --- lib/Epub/Epub/Section.cpp | 7 ---- lib/Epub/Epub/Section.h | 1 - lib/GfxRenderer/GfxRenderer.h | 2 - src/ScreenComponents.cpp | 39 +++++++++++++++++ src/ScreenComponents.h | 22 ++++++++++ src/activities/boot_sleep/BootActivity.cpp | 20 --------- src/activities/boot_sleep/BootActivity.h | 9 ---- src/activities/boot_sleep/SleepActivity.cpp | 15 ------- src/activities/boot_sleep/SleepActivity.h | 1 - src/activities/reader/EpubReaderActivity.cpp | 44 +++----------------- src/activities/reader/TxtReaderActivity.cpp | 26 +----------- src/main.cpp | 5 +-- 12 files changed, 69 insertions(+), 122 deletions(-) delete mode 100644 src/activities/boot_sleep/BootActivity.cpp delete mode 100644 src/activities/boot_sleep/BootActivity.h diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index 581a364f..31a99af1 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -123,9 +123,7 @@ bool Section::clearCache() const { bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint16_t viewportHeight, const bool hyphenationEnabled, - const std::function& progressSetupFn, const std::function& progressFn) { - constexpr uint32_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB const auto localPath = epub->getSpineItem(spineIndex).href; const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html"; @@ -171,11 +169,6 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c Serial.printf("[%lu] [SCT] Streamed temp HTML to %s (%d bytes)\n", millis(), tmpHtmlPath.c_str(), fileSize); - // Only show progress bar for larger chapters where rendering overhead is worth it - if (progressSetupFn && fileSize >= MIN_SIZE_FOR_PROGRESS) { - progressSetupFn(); - } - if (!SdMan.openFileForWrite("SCT", filePath, file)) { return false; } diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index 5b726141..cfa01fe6 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -33,7 +33,6 @@ class Section { bool clearCache() const; bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment, uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, - const std::function& progressSetupFn = nullptr, const std::function& progressFn = nullptr); std::unique_ptr loadPageFromSectionFile(); }; diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index b1fea69b..009309eb 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -55,8 +55,6 @@ class GfxRenderer { int getScreenWidth() const; int getScreenHeight() const; void displayBuffer(EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH) const; - // EXPERIMENTAL: Windowed update - display only a rectangular region - void displayWindow(int x, int y, int width, int height) const; void invertScreen() const; void clearScreen(uint8_t color = 0xFF) const; diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index 42b6ef7b..cf579fc5 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -42,6 +42,45 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4); } +ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message, + const int y, const int minWidth, + const int minHeight) { + const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD); + constexpr int margin = 16; + const int contentWidth = textWidth > minWidth ? textWidth : minWidth; + const int x = (renderer.getScreenWidth() - contentWidth - margin * 2) / 2; + const int w = contentWidth + margin * 2; + const int contentHeight = renderer.getLineHeight(UI_12_FONT_ID) + margin * 2; + const int h = contentHeight >= minHeight ? contentHeight : minHeight; + renderer.fillRect(x - 2, y - 2, w + 4, h + 4, true); + renderer.fillRect(x + 2, y + 2, w - 4, h - 4, false); + + const int barWidth = POPUP_DEFAULT_MIN_WIDTH; + const int barHeight = POPUP_DEFAULT_BAR_HEIGHT; + const int barX = x + (w - barWidth) / 2; + const int barY = y + renderer.getLineHeight(UI_12_FONT_ID) + margin * 2 - 6; + + const int textX = x + margin + (contentWidth - textWidth) / 2; + renderer.drawText(UI_12_FONT_ID, textX, y + margin, message, true, EpdFontFamily::BOLD); + renderer.displayBuffer(); + return {x, y, w, h, barX, barY, barWidth, barHeight}; +} + +void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, + const int progress) { + int fillWidth = layout.barWidth * progress / 100; + if (fillWidth < 0) { + fillWidth = 0; + } else if (fillWidth > layout.barWidth) { + fillWidth = layout.barWidth; + } + + if (fillWidth > 2) { + renderer.fillRect(layout.barX + 1, layout.barY + 1, fillWidth - 2, layout.barHeight - 2, true); + } + renderer.displayBuffer(EInkDisplay::FAST_REFRESH); +} + void ScreenComponents::drawProgressBar(const GfxRenderer& renderer, const int x, const int y, const int width, const int height, const size_t current, const size_t total) { if (total == 0) { diff --git a/src/ScreenComponents.h b/src/ScreenComponents.h index 150fb0c8..e272b348 100644 --- a/src/ScreenComponents.h +++ b/src/ScreenComponents.h @@ -7,8 +7,30 @@ class GfxRenderer; class ScreenComponents { public: + static constexpr int POPUP_DEFAULT_MIN_HEIGHT = 72; + static constexpr int POPUP_DEFAULT_BAR_HEIGHT = 6; + static constexpr int POPUP_DEFAULT_MIN_WIDTH = 200; + + struct PopupLayout { + int x; + int y; + int width; + int height; + + int barX; + int barY; + int barWidth; + int barHeight; + }; + static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true); + static PopupLayout drawPopup(const GfxRenderer& renderer, const char* message, int y = 117, + int minWidth = POPUP_DEFAULT_MIN_WIDTH, + int minHeight = POPUP_DEFAULT_MIN_HEIGHT); + + static void fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, int progress); + /** * Draw a progress bar with percentage text. * @param renderer The graphics renderer diff --git a/src/activities/boot_sleep/BootActivity.cpp b/src/activities/boot_sleep/BootActivity.cpp deleted file mode 100644 index 65eb6a07..00000000 --- a/src/activities/boot_sleep/BootActivity.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "BootActivity.h" - -#include - -#include "fontIds.h" -#include "images/CrossLarge.h" - -void BootActivity::onEnter() { - Activity::onEnter(); - - const auto pageWidth = renderer.getScreenWidth(); - const auto pageHeight = renderer.getScreenHeight(); - - renderer.clearScreen(); - renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128); - renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD); - renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "BOOTING"); - renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, CROSSPOINT_VERSION); - renderer.displayBuffer(); -} diff --git a/src/activities/boot_sleep/BootActivity.h b/src/activities/boot_sleep/BootActivity.h deleted file mode 100644 index 312f2ab0..00000000 --- a/src/activities/boot_sleep/BootActivity.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../Activity.h" - -class BootActivity final : public Activity { - public: - explicit BootActivity(GfxRenderer& renderer, MappedInputManager& mappedInput) - : Activity("Boot", renderer, mappedInput) {} - void onEnter() override; -}; diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index bf2b5857..994bf8bd 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -14,7 +14,6 @@ void SleepActivity::onEnter() { Activity::onEnter(); - renderPopup("Entering Sleep..."); if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) { return renderBlankSleepScreen(); @@ -31,20 +30,6 @@ void SleepActivity::onEnter() { renderDefaultSleepScreen(); } -void SleepActivity::renderPopup(const char* message) const { - const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD); - constexpr int margin = 20; - const int x = (renderer.getScreenWidth() - textWidth - margin * 2) / 2; - constexpr int y = 117; - const int w = textWidth + margin * 2; - const int h = renderer.getLineHeight(UI_12_FONT_ID) + margin * 2; - // renderer.clearScreen(); - renderer.fillRect(x - 5, y - 5, w + 10, h + 10, true); - renderer.fillRect(x + 5, y + 5, w - 10, h - 10, false); - renderer.drawText(UI_12_FONT_ID, x + margin, y + margin, message, true, EpdFontFamily::BOLD); - renderer.displayBuffer(); -} - void SleepActivity::renderCustomSleepScreen() const { // Check if we have a /sleep directory auto dir = SdMan.open("/sleep"); diff --git a/src/activities/boot_sleep/SleepActivity.h b/src/activities/boot_sleep/SleepActivity.h index 283220ce..87df8ba1 100644 --- a/src/activities/boot_sleep/SleepActivity.h +++ b/src/activities/boot_sleep/SleepActivity.h @@ -10,7 +10,6 @@ class SleepActivity final : public Activity { void onEnter() override; private: - void renderPopup(const char* message) const; void renderDefaultSleepScreen() const; void renderCustomSleepScreen() const; void renderCoverSleepScreen() const; diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index d70a15c4..15c7d252 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -283,49 +283,15 @@ void EpubReaderActivity::renderScreen() { viewportHeight, SETTINGS.hyphenationEnabled)) { 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(UI_12_FONT_ID, "Indexing..."); - const int boxWidthWithBar = (barWidth > textWidth ? barWidth : textWidth) + boxMargin * 2; - const int boxWidthNoBar = textWidth + boxMargin * 2; - const int boxHeightWithBar = renderer.getLineHeight(UI_12_FONT_ID) + barHeight + boxMargin * 3; - const int boxHeightNoBar = renderer.getLineHeight(UI_12_FONT_ID) + boxMargin * 2; - const int boxXWithBar = (renderer.getScreenWidth() - boxWidthWithBar) / 2; - const int boxXNoBar = (renderer.getScreenWidth() - boxWidthNoBar) / 2; - constexpr int boxY = 50; - const int barX = boxXWithBar + (boxWidthWithBar - barWidth) / 2; - const int barY = boxY + renderer.getLineHeight(UI_12_FONT_ID) + boxMargin * 2; - - // Always show "Indexing..." text first - { - renderer.fillRect(boxXNoBar, boxY, boxWidthNoBar, boxHeightNoBar, false); - renderer.drawText(UI_12_FONT_ID, boxXNoBar + boxMargin, boxY + boxMargin, "Indexing..."); - renderer.drawRect(boxXNoBar + 5, boxY + 5, boxWidthNoBar - 10, boxHeightNoBar - 10); - renderer.displayBuffer(); - pagesUntilFullRefresh = 0; - } - - // Setup callback - only called for chapters >= 50KB, redraws with progress bar - auto progressSetup = [this, boxXWithBar, boxWidthWithBar, boxHeightWithBar, barX, barY] { - renderer.fillRect(boxXWithBar, boxY, boxWidthWithBar, boxHeightWithBar, false); - renderer.drawText(UI_12_FONT_ID, boxXWithBar + boxMargin, boxY + boxMargin, "Indexing..."); - renderer.drawRect(boxXWithBar + 5, boxY + 5, boxWidthWithBar - 10, boxHeightWithBar - 10); - renderer.drawRect(barX, barY, barWidth, barHeight); - renderer.displayBuffer(); - }; - - // 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); + pagesUntilFullRefresh = 0; + const auto popupLayout = ScreenComponents::drawPopup(renderer, "Indexing..."); + const auto progressCallback = [this, popupLayout](int progress) { + ScreenComponents::fillPopupProgress(renderer, popupLayout, progress); }; if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, - viewportHeight, SETTINGS.hyphenationEnabled, progressSetup, progressCallback)) { + viewportHeight, SETTINGS.hyphenationEnabled, progressCallback)) { Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis()); section.reset(); return; diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index db725320..60cafa7d 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -192,24 +192,7 @@ void TxtReaderActivity::buildPageIndex() { Serial.printf("[%lu] [TRS] Building page index for %zu bytes...\n", millis(), fileSize); - // Progress bar dimensions (matching EpubReaderActivity style) - constexpr int barWidth = 200; - constexpr int barHeight = 10; - constexpr int boxMargin = 20; - const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, "Indexing..."); - const int boxWidth = (barWidth > textWidth ? barWidth : textWidth) + boxMargin * 2; - const int boxHeight = renderer.getLineHeight(UI_12_FONT_ID) + barHeight + boxMargin * 3; - const int boxX = (renderer.getScreenWidth() - boxWidth) / 2; - constexpr int boxY = 50; - const int barX = boxX + (boxWidth - barWidth) / 2; - const int barY = boxY + renderer.getLineHeight(UI_12_FONT_ID) + boxMargin * 2; - - // Draw initial progress box - renderer.fillRect(boxX, boxY, boxWidth, boxHeight, false); - renderer.drawText(UI_12_FONT_ID, boxX + boxMargin, boxY + boxMargin, "Indexing..."); - renderer.drawRect(boxX + 5, boxY + 5, boxWidth - 10, boxHeight - 10); - renderer.drawRect(barX, barY, barWidth, barHeight); - renderer.displayBuffer(); + const auto popupLayout = ScreenComponents::drawPopup(renderer, "Indexing..."); while (offset < fileSize) { std::vector tempLines; @@ -235,9 +218,7 @@ void TxtReaderActivity::buildPageIndex() { lastProgressPercent = progressPercent; // Fill progress bar - const int fillWidth = (barWidth - 2) * progressPercent / 100; - renderer.fillRect(barX + 1, barY + 1, fillWidth, barHeight - 2, true); - renderer.displayBuffer(EInkDisplay::FAST_REFRESH); + ScreenComponents::fillPopupProgress(renderer, popupLayout, progressPercent); } // Yield to other tasks periodically @@ -383,9 +364,6 @@ void TxtReaderActivity::renderScreen() { // Initialize reader if not done if (!initialized) { - renderer.clearScreen(); - renderer.drawCenteredText(UI_12_FONT_ID, 300, "Indexing...", true, EpdFontFamily::BOLD); - renderer.displayBuffer(); initializeReader(); } diff --git a/src/main.cpp b/src/main.cpp index e0ad316a..ee9c77c7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,7 @@ #include "CrossPointState.h" #include "KOReaderCredentialStore.h" #include "MappedInputManager.h" -#include "activities/boot_sleep/BootActivity.h" +#include "ScreenComponents.h" #include "activities/boot_sleep/SleepActivity.h" #include "activities/browser/OpdsBookBrowserActivity.h" #include "activities/home/HomeActivity.h" @@ -300,9 +300,6 @@ void setup() { setupDisplayAndFonts(); - exitActivity(); - enterNewActivity(new BootActivity(renderer, mappedInputManager)); - APP_STATE.loadFromFile(); if (APP_STATE.openEpubPath.empty()) { onGoHome(); From 27176da81164cad2c140d9e814808edf2b1d8080 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Tue, 20 Jan 2026 08:53:34 +0500 Subject: [PATCH 02/18] refactor: remove unused ScreenComponents include from main.cpp --- src/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index ee9c77c7..2be87aac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,6 @@ #include "CrossPointState.h" #include "KOReaderCredentialStore.h" #include "MappedInputManager.h" -#include "ScreenComponents.h" #include "activities/boot_sleep/SleepActivity.h" #include "activities/browser/OpdsBookBrowserActivity.h" #include "activities/home/HomeActivity.h" From 4bd7232948489633ee4d935fb9a6069055c1357d Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Tue, 20 Jan 2026 08:58:38 +0500 Subject: [PATCH 03/18] clang format fix --- src/ScreenComponents.cpp | 8 +++----- src/ScreenComponents.h | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index cf579fc5..6cd1f659 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -42,9 +42,8 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4); } -ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message, - const int y, const int minWidth, - const int minHeight) { +ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message, const int y, + const int minWidth, const int minHeight) { const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD); constexpr int margin = 16; const int contentWidth = textWidth > minWidth ? textWidth : minWidth; @@ -66,8 +65,7 @@ ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& ren return {x, y, w, h, barX, barY, barWidth, barHeight}; } -void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, - const int progress) { +void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, const int progress) { int fillWidth = layout.barWidth * progress / 100; if (fillWidth < 0) { fillWidth = 0; diff --git a/src/ScreenComponents.h b/src/ScreenComponents.h index e272b348..45448b18 100644 --- a/src/ScreenComponents.h +++ b/src/ScreenComponents.h @@ -26,8 +26,7 @@ class ScreenComponents { static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true); static PopupLayout drawPopup(const GfxRenderer& renderer, const char* message, int y = 117, - int minWidth = POPUP_DEFAULT_MIN_WIDTH, - int minHeight = POPUP_DEFAULT_MIN_HEIGHT); + int minWidth = POPUP_DEFAULT_MIN_WIDTH, int minHeight = POPUP_DEFAULT_MIN_HEIGHT); static void fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, int progress); From 21304ec72df04fd2f46b54cb740b014858683eb8 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Tue, 20 Jan 2026 09:18:52 +0500 Subject: [PATCH 04/18] refactor: simplify PopupLayout by removing unused progress bar attributes --- src/ScreenComponents.cpp | 20 ++++++++++---------- src/ScreenComponents.h | 5 ----- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index 6cd1f659..d2ba8070 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -54,27 +54,27 @@ ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& ren renderer.fillRect(x - 2, y - 2, w + 4, h + 4, true); renderer.fillRect(x + 2, y + 2, w - 4, h - 4, false); - const int barWidth = POPUP_DEFAULT_MIN_WIDTH; - const int barHeight = POPUP_DEFAULT_BAR_HEIGHT; - const int barX = x + (w - barWidth) / 2; - const int barY = y + renderer.getLineHeight(UI_12_FONT_ID) + margin * 2 - 6; - const int textX = x + margin + (contentWidth - textWidth) / 2; renderer.drawText(UI_12_FONT_ID, textX, y + margin, message, true, EpdFontFamily::BOLD); renderer.displayBuffer(); - return {x, y, w, h, barX, barY, barWidth, barHeight}; + return {x, y, w, h}; } void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, const int progress) { - int fillWidth = layout.barWidth * progress / 100; + const int barWidth = POPUP_DEFAULT_MIN_WIDTH; + const int barHeight = POPUP_DEFAULT_BAR_HEIGHT; + const int barX = layout.x + (layout.width - barWidth) / 2; + const int barY = layout.y + layout.height - 16; // 16 pixels above bottom of popup + + int fillWidth = barWidth * progress / 100; if (fillWidth < 0) { fillWidth = 0; - } else if (fillWidth > layout.barWidth) { - fillWidth = layout.barWidth; + } else if (fillWidth > barWidth) { + fillWidth = barWidth; } if (fillWidth > 2) { - renderer.fillRect(layout.barX + 1, layout.barY + 1, fillWidth - 2, layout.barHeight - 2, true); + renderer.fillRect(barX + 1, barY + 1, fillWidth - 2, barHeight - 2, true); } renderer.displayBuffer(EInkDisplay::FAST_REFRESH); } diff --git a/src/ScreenComponents.h b/src/ScreenComponents.h index 45448b18..eb7c2729 100644 --- a/src/ScreenComponents.h +++ b/src/ScreenComponents.h @@ -16,11 +16,6 @@ class ScreenComponents { int y; int width; int height; - - int barX; - int barY; - int barWidth; - int barHeight; }; static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true); From e23091a109ab2b3e9e516f901a1be28b246ed504 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Tue, 20 Jan 2026 09:28:37 +0500 Subject: [PATCH 05/18] clang format fix --- src/ScreenComponents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index d2ba8070..80a0e1e8 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -64,7 +64,7 @@ void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const Popu const int barWidth = POPUP_DEFAULT_MIN_WIDTH; const int barHeight = POPUP_DEFAULT_BAR_HEIGHT; const int barX = layout.x + (layout.width - barWidth) / 2; - const int barY = layout.y + layout.height - 16; // 16 pixels above bottom of popup + const int barY = layout.y + layout.height - 16; // 16 pixels above bottom of popup int fillWidth = barWidth * progress / 100; if (fillWidth < 0) { From 93ce00aed90f904d62c3a32699c609a4e3adc006 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Wed, 21 Jan 2026 22:12:48 +0500 Subject: [PATCH 06/18] clang format fix --- src/ScreenComponents.cpp | 152 +++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index 2a8396c0..083b77ee 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -79,94 +79,94 @@ void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const Popu renderer.displayBuffer(EInkDisplay::FAST_REFRESH); } - int ScreenComponents::drawTabBar(const GfxRenderer& renderer, const int y, const std::vector& tabs) { - constexpr int tabPadding = 20; // Horizontal padding between tabs - constexpr int leftMargin = 20; // Left margin for first tab - constexpr int underlineHeight = 2; // Height of selection underline - constexpr int underlineGap = 4; // Gap between text and underline +int ScreenComponents::drawTabBar(const GfxRenderer& renderer, const int y, const std::vector& tabs) { + constexpr int tabPadding = 20; // Horizontal padding between tabs + constexpr int leftMargin = 20; // Left margin for first tab + constexpr int underlineHeight = 2; // Height of selection underline + constexpr int underlineGap = 4; // Gap between text and underline - const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID); - const int tabBarHeight = lineHeight + underlineGap + underlineHeight; + const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID); + const int tabBarHeight = lineHeight + underlineGap + underlineHeight; - int currentX = leftMargin; + int currentX = leftMargin; - for (const auto& tab : tabs) { - const int textWidth = - renderer.getTextWidth(UI_12_FONT_ID, tab.label, tab.selected ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR); + for (const auto& tab : tabs) { + const int textWidth = + renderer.getTextWidth(UI_12_FONT_ID, tab.label, tab.selected ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR); - // Draw tab label - renderer.drawText(UI_12_FONT_ID, currentX, y, tab.label, true, - tab.selected ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR); + // Draw tab label + renderer.drawText(UI_12_FONT_ID, currentX, y, tab.label, true, + tab.selected ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR); - // Draw underline for selected tab - if (tab.selected) { - renderer.fillRect(currentX, y + lineHeight + underlineGap, textWidth, underlineHeight); - } - - currentX += textWidth + tabPadding; + // Draw underline for selected tab + if (tab.selected) { + renderer.fillRect(currentX, y + lineHeight + underlineGap, textWidth, underlineHeight); } - return tabBarHeight; + currentX += textWidth + tabPadding; } - void ScreenComponents::drawScrollIndicator(const GfxRenderer& renderer, const int currentPage, const int totalPages, - const int contentTop, const int contentHeight) { - if (totalPages <= 1) { - return; // No need for indicator if only one page - } + return tabBarHeight; +} - const int screenWidth = renderer.getScreenWidth(); - constexpr int indicatorWidth = 20; - constexpr int arrowSize = 6; - constexpr int margin = 15; // Offset from right edge - - const int centerX = screenWidth - indicatorWidth / 2 - margin; - const int indicatorTop = contentTop + 60; // Offset to avoid overlapping side button hints - const int indicatorBottom = contentTop + contentHeight - 30; - - // Draw up arrow at top (^) - narrow point at top, wide base at bottom - for (int i = 0; i < arrowSize; ++i) { - const int lineWidth = 1 + i * 2; - const int startX = centerX - i; - renderer.drawLine(startX, indicatorTop + i, startX + lineWidth - 1, indicatorTop + i); - } - - // Draw down arrow at bottom (v) - wide base at top, narrow point at bottom - for (int i = 0; i < arrowSize; ++i) { - const int lineWidth = 1 + (arrowSize - 1 - i) * 2; - const int startX = centerX - (arrowSize - 1 - i); - renderer.drawLine(startX, indicatorBottom - arrowSize + 1 + i, startX + lineWidth - 1, - indicatorBottom - arrowSize + 1 + i); - } - - // Draw page fraction in the middle (e.g., "1/3") - const std::string pageText = std::to_string(currentPage) + "/" + std::to_string(totalPages); - const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, pageText.c_str()); - const int textX = centerX - textWidth / 2; - const int textY = (indicatorTop + indicatorBottom) / 2 - renderer.getLineHeight(SMALL_FONT_ID) / 2; - - renderer.drawText(SMALL_FONT_ID, textX, textY, pageText.c_str()); +void ScreenComponents::drawScrollIndicator(const GfxRenderer& renderer, const int currentPage, const int totalPages, + const int contentTop, const int contentHeight) { + if (totalPages <= 1) { + return; // No need for indicator if only one page } - void ScreenComponents::drawProgressBar(const GfxRenderer& renderer, const int x, const int y, const int width, - const int height, const size_t current, const size_t total) { - if (total == 0) { - return; - } + const int screenWidth = renderer.getScreenWidth(); + constexpr int indicatorWidth = 20; + constexpr int arrowSize = 6; + constexpr int margin = 15; // Offset from right edge - // Use 64-bit arithmetic to avoid overflow for large files - const int percent = static_cast((static_cast(current) * 100) / total); + const int centerX = screenWidth - indicatorWidth / 2 - margin; + const int indicatorTop = contentTop + 60; // Offset to avoid overlapping side button hints + const int indicatorBottom = contentTop + contentHeight - 30; - // Draw outline - renderer.drawRect(x, y, width, height); - - // Draw filled portion - const int fillWidth = (width - 4) * percent / 100; - if (fillWidth > 0) { - renderer.fillRect(x + 2, y + 2, fillWidth, height - 4); - } - - // Draw percentage text centered below bar - const std::string percentText = std::to_string(percent) + "%"; - renderer.drawCenteredText(UI_10_FONT_ID, y + height + 15, percentText.c_str()); + // Draw up arrow at top (^) - narrow point at top, wide base at bottom + for (int i = 0; i < arrowSize; ++i) { + const int lineWidth = 1 + i * 2; + const int startX = centerX - i; + renderer.drawLine(startX, indicatorTop + i, startX + lineWidth - 1, indicatorTop + i); } + + // Draw down arrow at bottom (v) - wide base at top, narrow point at bottom + for (int i = 0; i < arrowSize; ++i) { + const int lineWidth = 1 + (arrowSize - 1 - i) * 2; + const int startX = centerX - (arrowSize - 1 - i); + renderer.drawLine(startX, indicatorBottom - arrowSize + 1 + i, startX + lineWidth - 1, + indicatorBottom - arrowSize + 1 + i); + } + + // Draw page fraction in the middle (e.g., "1/3") + const std::string pageText = std::to_string(currentPage) + "/" + std::to_string(totalPages); + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, pageText.c_str()); + const int textX = centerX - textWidth / 2; + const int textY = (indicatorTop + indicatorBottom) / 2 - renderer.getLineHeight(SMALL_FONT_ID) / 2; + + renderer.drawText(SMALL_FONT_ID, textX, textY, pageText.c_str()); +} + +void ScreenComponents::drawProgressBar(const GfxRenderer& renderer, const int x, const int y, const int width, + const int height, const size_t current, const size_t total) { + if (total == 0) { + return; + } + + // Use 64-bit arithmetic to avoid overflow for large files + const int percent = static_cast((static_cast(current) * 100) / total); + + // Draw outline + renderer.drawRect(x, y, width, height); + + // Draw filled portion + const int fillWidth = (width - 4) * percent / 100; + if (fillWidth > 0) { + renderer.fillRect(x + 2, y + 2, fillWidth, height - 4); + } + + // Draw percentage text centered below bar + const std::string percentText = std::to_string(percent) + "%"; + renderer.drawCenteredText(UI_10_FONT_ID, y + height + 15, percentText.c_str()); +} From d418948a764a20c7c860bae337b555f2f56f7a05 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Thu, 22 Jan 2026 18:35:28 +0500 Subject: [PATCH 07/18] refactor: Update popup dimensions and styles; add sleep entry message --- lib/GfxRenderer/GfxRenderer.h | 2 ++ platformio.ini | 6 ++++- src/ScreenComponents.cpp | 25 +++++++++++---------- src/ScreenComponents.h | 6 ++--- src/activities/boot_sleep/SleepActivity.cpp | 3 +++ src/main.cpp | 3 +++ 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index 009309eb..dc0dfa03 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -55,6 +55,8 @@ class GfxRenderer { int getScreenWidth() const; int getScreenHeight() const; void displayBuffer(EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH) const; + // EXPERIMENTAL: Windowed update - display only a rectangular region -- not implemented + // void displayWindow(int x, int y, int width, int height) const; void invertScreen() const; void clearScreen(uint8_t color = 0xFF) const; diff --git a/platformio.ini b/platformio.ini index 7f42637d..faf4fd9a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,7 +27,11 @@ build_flags = # https://libexpat.github.io/doc/api/latest/#XML_GE -DXML_GE=0 -DXML_CONTEXT_BYTES=1024 - -std=c++2a + ; -std=c++2a + -std=gnu++2a + +build_unflags = -std=gnu++11 + # Enable UTF-8 long file names in SdFat -DUSE_UTF8_LONG_NAMES=1 diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index 083b77ee..042ff735 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -44,27 +44,29 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message, const int y, const int minWidth, const int minHeight) { + constexpr int margin = 15; const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD); - constexpr int margin = 16; - const int contentWidth = textWidth > minWidth ? textWidth : minWidth; - const int x = (renderer.getScreenWidth() - contentWidth - margin * 2) / 2; - const int w = contentWidth + margin * 2; + const int contentWidth = std::max(textWidth, minWidth); const int contentHeight = renderer.getLineHeight(UI_12_FONT_ID) + margin * 2; - const int h = contentHeight >= minHeight ? contentHeight : minHeight; + const int w = contentWidth + margin * 2 + 50; + // const int x = (renderer.getScreenWidth() - w) / 2; + const int x = renderer.getScreenWidth() - w - margin; + const int h = std::max(contentHeight, minHeight); renderer.fillRect(x - 2, y - 2, w + 4, h + 4, true); renderer.fillRect(x + 2, y + 2, w - 4, h - 4, false); const int textX = x + margin + (contentWidth - textWidth) / 2; - renderer.drawText(UI_12_FONT_ID, textX, y + margin, message, true, EpdFontFamily::BOLD); + // renderer.drawText(UI_12_FONT_ID, textX, y + margin + 4, message, true, EpdFontFamily::BOLD); + renderer.drawText(NOTOSANS_18_FONT_ID, textX, y + margin + 4, message, true, EpdFontFamily::BOLD); renderer.displayBuffer(); return {x, y, w, h}; } void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, const int progress) { - const int barWidth = POPUP_DEFAULT_MIN_WIDTH; - const int barHeight = POPUP_DEFAULT_BAR_HEIGHT; + constexpr int barWidth = POPUP_DEFAULT_MIN_WIDTH; + constexpr int barHeight = POPUP_DEFAULT_BAR_HEIGHT; const int barX = layout.x + (layout.width - barWidth) / 2; - const int barY = layout.y + layout.height - 16; // 16 pixels above bottom of popup + const int barY = layout.y + layout.height - 15; int fillWidth = barWidth * progress / 100; if (fillWidth < 0) { @@ -73,9 +75,8 @@ void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const Popu fillWidth = barWidth; } - if (fillWidth > 2) { - renderer.fillRect(barX + 1, barY + 1, fillWidth - 2, barHeight - 2, true); - } + renderer.fillRect(barX, barY, fillWidth, barHeight, true); + renderer.displayBuffer(EInkDisplay::FAST_REFRESH); } diff --git a/src/ScreenComponents.h b/src/ScreenComponents.h index 89299f56..fba62251 100644 --- a/src/ScreenComponents.h +++ b/src/ScreenComponents.h @@ -13,9 +13,9 @@ struct TabInfo { class ScreenComponents { public: - static constexpr int POPUP_DEFAULT_MIN_HEIGHT = 72; - static constexpr int POPUP_DEFAULT_BAR_HEIGHT = 6; static constexpr int POPUP_DEFAULT_MIN_WIDTH = 200; + static constexpr int POPUP_DEFAULT_MIN_HEIGHT = 72; + static constexpr int POPUP_DEFAULT_BAR_HEIGHT = 4; struct PopupLayout { int x; @@ -26,7 +26,7 @@ class ScreenComponents { static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true); - static PopupLayout drawPopup(const GfxRenderer& renderer, const char* message, int y = 117, + static PopupLayout drawPopup(const GfxRenderer& renderer, const char* message, int y = 125, int minWidth = POPUP_DEFAULT_MIN_WIDTH, int minHeight = POPUP_DEFAULT_MIN_HEIGHT); static void fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, int progress); diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 69de73fd..7da55d8d 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -11,10 +11,13 @@ #include "fontIds.h" #include "images/CrossLarge.h" #include "util/StringUtils.h" +#include "ScreenComponents.h" void SleepActivity::onEnter() { Activity::onEnter(); + ScreenComponents::drawPopup(renderer, "Entering Sleep..."); + if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) { return renderBlankSleepScreen(); } diff --git a/src/main.cpp b/src/main.cpp index 3ccd9432..c0222e0d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -332,6 +332,9 @@ void setup() { setupDisplayAndFonts(); + exitActivity(); + enterNewActivity(new BootActivity(renderer, mappedInputManager)); + APP_STATE.loadFromFile(); RECENT_BOOKS.loadFromFile(); From 1facf55fbda2d765d84c79bb79a0459075183de1 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Fri, 23 Jan 2026 08:23:19 +0500 Subject: [PATCH 08/18] refactor: Update popup dimensions and styles; add rounded rectangle drawing functions --- .../Epub/parsers/ChapterHtmlSlimParser.cpp | 5 +- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 6 + lib/GfxRenderer/GfxRenderer.cpp | 165 ++++++++++++++++++ lib/GfxRenderer/GfxRenderer.h | 4 + src/ScreenComponents.cpp | 31 ++-- src/ScreenComponents.h | 7 +- src/activities/boot_sleep/SleepActivity.cpp | 5 +- src/activities/reader/EpubReaderActivity.cpp | 2 +- 8 files changed, 200 insertions(+), 25 deletions(-) diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 1d7e2ab3..e93b3a72 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -10,9 +10,6 @@ const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"}; constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]); -// Minimum file size (in bytes) to show progress bar - smaller chapters don't benefit from it -constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB - const char* BLOCK_TAGS[] = {"p", "li", "div", "br", "blockquote"}; constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]); @@ -305,7 +302,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { // Update progress (call every 10% change to avoid too frequent updates) // Only show progress for larger chapters where rendering overhead is worth it bytesRead += len; - if (progressFn && totalSize >= MIN_SIZE_FOR_PROGRESS) { + if (progressFn && totalSize >= ChapterHtmlSlimParser::MIN_SIZE_FOR_PROGRESS) { const int progress = static_cast((bytesRead * 100) / totalSize); if (lastProgress / 10 != progress / 10) { lastProgress = progress; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index 5355211a..89355e50 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -15,6 +16,11 @@ class GfxRenderer; #define MAX_WORD_SIZE 200 class ChapterHtmlSlimParser { + public: + // Minimum file size (in bytes) to show progress bar - smaller chapters don't benefit from it. + static constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB + + private: const std::string& filepath; GfxRenderer& renderer; std::function)> completePageFn; diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 08420bf9..7a343ad2 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -2,6 +2,19 @@ #include +namespace { +int clampRoundedRadius(const int width, const int height, int radius) { + if (radius <= 0) { + return 0; + } + const int maxRadius = (std::min(width, height) - 1) / 2; + if (maxRadius <= 0) { + return 0; + } + return radius > maxRadius ? maxRadius : radius; +} +} // namespace + void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); } void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int* rotatedY) const { @@ -138,6 +151,158 @@ void GfxRenderer::drawRect(const int x, const int y, const int width, const int drawLine(x, y, x, y + height - 1, state); } +void GfxRenderer::drawRoundedRect(const int x, const int y, const int width, const int height, int radius, + const bool state) const { + if (width <= 0 || height <= 0) { + return; + } + + radius = clampRoundedRadius(width, height, radius); + if (radius <= 0) { + drawRect(x, y, width, height, state); + return; + } + + const int right = x + width - 1; + const int bottom = y + height - 1; + const int x0 = x + radius; + const int x1 = right - radius; + const int y0 = y + radius; + const int y1 = bottom - radius; + + if (x0 <= x1) { + drawLine(x0, y, x1, y, state); + drawLine(x0, bottom, x1, bottom, state); + } + if (y0 <= y1) { + drawLine(x, y0, x, y1, state); + drawLine(right, y0, right, y1, state); + } + + const int cxLeft = x + radius; + const int cxRight = right - radius; + const int cyTop = y + radius; + const int cyBottom = bottom - radius; + + auto plotCornerPoints = [&](const int offsetX, const int offsetY) { + drawPixel(cxLeft - offsetX, cyTop - offsetY, state); + drawPixel(cxRight + offsetX, cyTop - offsetY, state); + drawPixel(cxRight + offsetX, cyBottom + offsetY, state); + drawPixel(cxLeft - offsetX, cyBottom + offsetY, state); + + if (offsetX == offsetY) { + return; + } + + drawPixel(cxLeft - offsetY, cyTop - offsetX, state); + drawPixel(cxRight + offsetY, cyTop - offsetX, state); + drawPixel(cxRight + offsetY, cyBottom + offsetX, state); + drawPixel(cxLeft - offsetY, cyBottom + offsetX, state); + }; + + int f = 1 - radius; + int ddF_x = 1; + int ddF_y = -2 * radius; + int offsetX = 0; + int offsetY = radius; + + while (offsetX <= offsetY) { + plotCornerPoints(offsetX, offsetY); + if (f >= 0) { + offsetY--; + ddF_y += 2; + f += ddF_y; + } + offsetX++; + ddF_x += 2; + f += ddF_x; + plotCornerPoints(offsetX, offsetY); + } +} + +void GfxRenderer::fillRoundedRect(const int x, const int y, const int width, const int height, int radius, + const bool state) const { + if (width <= 0 || height <= 0) { + return; + } + + radius = clampRoundedRadius(width, height, radius); + if (radius <= 0) { + fillRect(x, y, width, height, state); + return; + } + + const int right = x + width - 1; + const int bottom = y + height - 1; + const int innerTop = y + radius; + const int innerBottom = bottom - radius; + + if (innerBottom >= innerTop) { + fillRect(x, innerTop, width, innerBottom - innerTop + 1, state); + } + + int f = 1 - radius; + int ddF_x = 1; + int ddF_y = -2 * radius; + int offsetX = 0; + int offsetY = radius; + + while (offsetX <= offsetY) { + int left = x + radius - offsetX; + int rightSpan = right - radius + offsetX; + int topY = innerTop - offsetY; + int bottomY = innerBottom + offsetY; + drawLine(left, topY, rightSpan, topY, state); + drawLine(left, bottomY, rightSpan, bottomY, state); + + if (offsetX != offsetY) { + left = x + radius - offsetY; + rightSpan = right - radius + offsetY; + topY = innerTop - offsetX; + bottomY = innerBottom + offsetX; + drawLine(left, topY, rightSpan, topY, state); + drawLine(left, bottomY, rightSpan, bottomY, state); + } + + if (f >= 0) { + offsetY--; + ddF_y += 2; + f += ddF_y; + } + offsetX++; + ddF_x += 2; + f += ddF_x; + } +} + +void GfxRenderer::drawRoundedRectFrame(const int x, const int y, const int width, const int height, int radius, + int thickness, const bool frameState, const bool fillState) const { + if (width <= 0 || height <= 0) { + return; + } + + const int maxThickness = (std::min(width, height) - 1) / 2; + thickness = std::min(thickness, maxThickness); + if (thickness <= 0) { + fillRoundedRect(x, y, width, height, radius, fillState); + return; + } + + fillRoundedRect(x, y, width, height, radius, frameState); + + const int inset = thickness * 2; + const int innerX = x + thickness; + const int innerY = y + thickness; + const int innerW = width - inset; + const int innerH = height - inset; + if (innerW <= 0 || innerH <= 0) { + return; + } + + const int innerRadius = radius - thickness; + fillRoundedRect(innerX, innerY, innerW, innerH, innerRadius, fillState); +} + void GfxRenderer::fillRect(const int x, const int y, const int width, const int height, const bool state) const { for (int fillY = y; fillY < y + height; fillY++) { drawLine(x, fillY, x + width - 1, fillY, state); diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index dc0dfa03..1c6fc4ea 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -64,6 +64,10 @@ class GfxRenderer { void drawPixel(int x, int y, bool state = true) const; void drawLine(int x1, int y1, int x2, int y2, bool state = true) const; void drawRect(int x, int y, int width, int height, bool state = true) const; + void drawRoundedRect(int x, int y, int width, int height, int radius, bool state = true) const; + void fillRoundedRect(int x, int y, int width, int height, int radius, bool state = true) const; + void drawRoundedRectFrame(int x, int y, int width, int height, int radius, int thickness = 1, + bool frameState = true, bool fillState = false) const; void fillRect(int x, int y, int width, int height, bool state = true) const; void drawImage(const uint8_t bitmap[], int x, int y, int width, int height) const; void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight, float cropX = 0, diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index 042ff735..e3eed0d6 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -42,22 +42,23 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4); } -ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message, const int y, - const int minWidth, const int minHeight) { - constexpr int margin = 15; +ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message) { + constexpr int margin = 12; + constexpr int frameThickness = 4; + constexpr int frameInset = frameThickness / 2; + constexpr int frameRadius = 8; + constexpr int bottomOffset = 20; const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD); - const int contentWidth = std::max(textWidth, minWidth); - const int contentHeight = renderer.getLineHeight(UI_12_FONT_ID) + margin * 2; - const int w = contentWidth + margin * 2 + 50; - // const int x = (renderer.getScreenWidth() - w) / 2; - const int x = renderer.getScreenWidth() - w - margin; - const int h = std::max(contentHeight, minHeight); - renderer.fillRect(x - 2, y - 2, w + 4, h + 4, true); - renderer.fillRect(x + 2, y + 2, w - 4, h - 4, false); + const int w = std::max(textWidth, POPUP_DEFAULT_MIN_WIDTH) + margin * 2; + const int h = std::max(renderer.getLineHeight(UI_12_FONT_ID) + margin * 2, POPUP_DEFAULT_MIN_HEIGHT); + const int x = std::max(0, (renderer.getScreenWidth() - w) / 2); + const int y = std::max(0, renderer.getScreenHeight() - h - margin - bottomOffset); - const int textX = x + margin + (contentWidth - textWidth) / 2; - // renderer.drawText(UI_12_FONT_ID, textX, y + margin + 4, message, true, EpdFontFamily::BOLD); - renderer.drawText(NOTOSANS_18_FONT_ID, textX, y + margin + 4, message, true, EpdFontFamily::BOLD); + renderer.drawRoundedRectFrame(x - frameInset, y - frameInset, w + frameInset * 2, h + frameInset * 2, + frameRadius, frameThickness, true, false); + + const int textX = x + (w - textWidth) / 2; + renderer.drawText(UI_12_FONT_ID, textX, y + margin - 2, message, true, EpdFontFamily::BOLD); renderer.displayBuffer(); return {x, y, w, h}; } @@ -66,7 +67,7 @@ void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const Popu constexpr int barWidth = POPUP_DEFAULT_MIN_WIDTH; constexpr int barHeight = POPUP_DEFAULT_BAR_HEIGHT; const int barX = layout.x + (layout.width - barWidth) / 2; - const int barY = layout.y + layout.height - 15; + const int barY = layout.y + layout.height - 13; int fillWidth = barWidth * progress / 100; if (fillWidth < 0) { diff --git a/src/ScreenComponents.h b/src/ScreenComponents.h index fba62251..00f3ee81 100644 --- a/src/ScreenComponents.h +++ b/src/ScreenComponents.h @@ -13,8 +13,8 @@ struct TabInfo { class ScreenComponents { public: - static constexpr int POPUP_DEFAULT_MIN_WIDTH = 200; - static constexpr int POPUP_DEFAULT_MIN_HEIGHT = 72; + static constexpr int POPUP_DEFAULT_MIN_WIDTH = 130; + static constexpr int POPUP_DEFAULT_MIN_HEIGHT = 50; static constexpr int POPUP_DEFAULT_BAR_HEIGHT = 4; struct PopupLayout { @@ -26,8 +26,7 @@ class ScreenComponents { static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true); - static PopupLayout drawPopup(const GfxRenderer& renderer, const char* message, int y = 125, - int minWidth = POPUP_DEFAULT_MIN_WIDTH, int minHeight = POPUP_DEFAULT_MIN_HEIGHT); + static PopupLayout drawPopup(const GfxRenderer& renderer, const char* message); static void fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, int progress); diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 7da55d8d..738c1c0c 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -16,7 +16,10 @@ void SleepActivity::onEnter() { Activity::onEnter(); - ScreenComponents::drawPopup(renderer, "Entering Sleep..."); + ScreenComponents::drawPopup(renderer, "Sleeping"); + + // debug delay to see sleep screen + delay(5000); if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) { return renderBlankSleepScreen(); diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index e77e559b..196a7455 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -286,7 +286,7 @@ void EpubReaderActivity::renderScreen() { Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis()); pagesUntilFullRefresh = 0; - const auto popupLayout = ScreenComponents::drawPopup(renderer, "Indexing..."); + const auto popupLayout = ScreenComponents::drawPopup(renderer, "Rendering"); const auto progressCallback = [this, popupLayout](int progress) { ScreenComponents::fillPopupProgress(renderer, popupLayout, progress); }; From 492e79f9c0784b8aedd9485b5234ec8ac01db72b Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 14:18:12 +0500 Subject: [PATCH 09/18] rectangular frame for popups --- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 2 +- lib/GfxRenderer/GfxRenderer.cpp | 167 ------------------ lib/GfxRenderer/GfxRenderer.h | 4 - src/ScreenComponents.cpp | 33 ++-- src/ScreenComponents.h | 4 - src/activities/boot_sleep/SleepActivity.cpp | 5 +- src/activities/reader/EpubReaderActivity.cpp | 2 +- 7 files changed, 16 insertions(+), 201 deletions(-) diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index 89355e50..456b19f8 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -2,8 +2,8 @@ #include -#include #include +#include #include #include diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 7a343ad2..cb268fc8 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -2,19 +2,6 @@ #include -namespace { -int clampRoundedRadius(const int width, const int height, int radius) { - if (radius <= 0) { - return 0; - } - const int maxRadius = (std::min(width, height) - 1) / 2; - if (maxRadius <= 0) { - return 0; - } - return radius > maxRadius ? maxRadius : radius; -} -} // namespace - void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); } void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int* rotatedY) const { @@ -151,158 +138,6 @@ void GfxRenderer::drawRect(const int x, const int y, const int width, const int drawLine(x, y, x, y + height - 1, state); } -void GfxRenderer::drawRoundedRect(const int x, const int y, const int width, const int height, int radius, - const bool state) const { - if (width <= 0 || height <= 0) { - return; - } - - radius = clampRoundedRadius(width, height, radius); - if (radius <= 0) { - drawRect(x, y, width, height, state); - return; - } - - const int right = x + width - 1; - const int bottom = y + height - 1; - const int x0 = x + radius; - const int x1 = right - radius; - const int y0 = y + radius; - const int y1 = bottom - radius; - - if (x0 <= x1) { - drawLine(x0, y, x1, y, state); - drawLine(x0, bottom, x1, bottom, state); - } - if (y0 <= y1) { - drawLine(x, y0, x, y1, state); - drawLine(right, y0, right, y1, state); - } - - const int cxLeft = x + radius; - const int cxRight = right - radius; - const int cyTop = y + radius; - const int cyBottom = bottom - radius; - - auto plotCornerPoints = [&](const int offsetX, const int offsetY) { - drawPixel(cxLeft - offsetX, cyTop - offsetY, state); - drawPixel(cxRight + offsetX, cyTop - offsetY, state); - drawPixel(cxRight + offsetX, cyBottom + offsetY, state); - drawPixel(cxLeft - offsetX, cyBottom + offsetY, state); - - if (offsetX == offsetY) { - return; - } - - drawPixel(cxLeft - offsetY, cyTop - offsetX, state); - drawPixel(cxRight + offsetY, cyTop - offsetX, state); - drawPixel(cxRight + offsetY, cyBottom + offsetX, state); - drawPixel(cxLeft - offsetY, cyBottom + offsetX, state); - }; - - int f = 1 - radius; - int ddF_x = 1; - int ddF_y = -2 * radius; - int offsetX = 0; - int offsetY = radius; - - while (offsetX <= offsetY) { - plotCornerPoints(offsetX, offsetY); - if (f >= 0) { - offsetY--; - ddF_y += 2; - f += ddF_y; - } - offsetX++; - ddF_x += 2; - f += ddF_x; - plotCornerPoints(offsetX, offsetY); - } -} - -void GfxRenderer::fillRoundedRect(const int x, const int y, const int width, const int height, int radius, - const bool state) const { - if (width <= 0 || height <= 0) { - return; - } - - radius = clampRoundedRadius(width, height, radius); - if (radius <= 0) { - fillRect(x, y, width, height, state); - return; - } - - const int right = x + width - 1; - const int bottom = y + height - 1; - const int innerTop = y + radius; - const int innerBottom = bottom - radius; - - if (innerBottom >= innerTop) { - fillRect(x, innerTop, width, innerBottom - innerTop + 1, state); - } - - int f = 1 - radius; - int ddF_x = 1; - int ddF_y = -2 * radius; - int offsetX = 0; - int offsetY = radius; - - while (offsetX <= offsetY) { - int left = x + radius - offsetX; - int rightSpan = right - radius + offsetX; - int topY = innerTop - offsetY; - int bottomY = innerBottom + offsetY; - drawLine(left, topY, rightSpan, topY, state); - drawLine(left, bottomY, rightSpan, bottomY, state); - - if (offsetX != offsetY) { - left = x + radius - offsetY; - rightSpan = right - radius + offsetY; - topY = innerTop - offsetX; - bottomY = innerBottom + offsetX; - drawLine(left, topY, rightSpan, topY, state); - drawLine(left, bottomY, rightSpan, bottomY, state); - } - - if (f >= 0) { - offsetY--; - ddF_y += 2; - f += ddF_y; - } - offsetX++; - ddF_x += 2; - f += ddF_x; - } -} - -void GfxRenderer::drawRoundedRectFrame(const int x, const int y, const int width, const int height, int radius, - int thickness, const bool frameState, const bool fillState) const { - if (width <= 0 || height <= 0) { - return; - } - - const int maxThickness = (std::min(width, height) - 1) / 2; - thickness = std::min(thickness, maxThickness); - if (thickness <= 0) { - fillRoundedRect(x, y, width, height, radius, fillState); - return; - } - - fillRoundedRect(x, y, width, height, radius, frameState); - - const int inset = thickness * 2; - const int innerX = x + thickness; - const int innerY = y + thickness; - const int innerW = width - inset; - const int innerH = height - inset; - if (innerW <= 0 || innerH <= 0) { - return; - } - - const int innerRadius = radius - thickness; - fillRoundedRect(innerX, innerY, innerW, innerH, innerRadius, fillState); -} - void GfxRenderer::fillRect(const int x, const int y, const int width, const int height, const bool state) const { for (int fillY = y; fillY < y + height; fillY++) { drawLine(x, fillY, x + width - 1, fillY, state); @@ -807,8 +642,6 @@ uint8_t* GfxRenderer::getFrameBuffer() const { return einkDisplay.getFrameBuffer size_t GfxRenderer::getBufferSize() { return EInkDisplay::BUFFER_SIZE; } -void GfxRenderer::grayscaleRevert() const { einkDisplay.grayscaleRevert(); } - void GfxRenderer::copyGrayscaleLsbBuffers() const { einkDisplay.copyGrayscaleLsbBuffers(einkDisplay.getFrameBuffer()); } void GfxRenderer::copyGrayscaleMsbBuffers() const { einkDisplay.copyGrayscaleMsbBuffers(einkDisplay.getFrameBuffer()); } diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index 1c6fc4ea..dc0dfa03 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -64,10 +64,6 @@ class GfxRenderer { void drawPixel(int x, int y, bool state = true) const; void drawLine(int x1, int y1, int x2, int y2, bool state = true) const; void drawRect(int x, int y, int width, int height, bool state = true) const; - void drawRoundedRect(int x, int y, int width, int height, int radius, bool state = true) const; - void fillRoundedRect(int x, int y, int width, int height, int radius, bool state = true) const; - void drawRoundedRectFrame(int x, int y, int width, int height, int radius, int thickness = 1, - bool frameState = true, bool fillState = false) const; void fillRect(int x, int y, int width, int height, bool state = true) const; void drawImage(const uint8_t bitmap[], int x, int y, int width, int height) const; void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight, float cropX = 0, diff --git a/src/ScreenComponents.cpp b/src/ScreenComponents.cpp index e3eed0d6..4f163af1 100644 --- a/src/ScreenComponents.cpp +++ b/src/ScreenComponents.cpp @@ -43,38 +43,31 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, } ScreenComponents::PopupLayout ScreenComponents::drawPopup(const GfxRenderer& renderer, const char* message) { - constexpr int margin = 12; - constexpr int frameThickness = 4; - constexpr int frameInset = frameThickness / 2; - constexpr int frameRadius = 8; - constexpr int bottomOffset = 20; + constexpr int margin = 15; + constexpr int y = 60; const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD); - const int w = std::max(textWidth, POPUP_DEFAULT_MIN_WIDTH) + margin * 2; - const int h = std::max(renderer.getLineHeight(UI_12_FONT_ID) + margin * 2, POPUP_DEFAULT_MIN_HEIGHT); - const int x = std::max(0, (renderer.getScreenWidth() - w) / 2); - const int y = std::max(0, renderer.getScreenHeight() - h - margin - bottomOffset); + const int textHeight = renderer.getLineHeight(UI_12_FONT_ID); + const int w = textWidth + margin * 2; + const int h = textHeight + margin * 2; + const int x = (renderer.getScreenWidth() - w) / 2; - renderer.drawRoundedRectFrame(x - frameInset, y - frameInset, w + frameInset * 2, h + frameInset * 2, - frameRadius, frameThickness, true, false); + renderer.fillRect(x - 2, y - 2, w + 4, h + 4, true); // frame thickness 2 + renderer.fillRect(x, y, w, h, false); const int textX = x + (w - textWidth) / 2; - renderer.drawText(UI_12_FONT_ID, textX, y + margin - 2, message, true, EpdFontFamily::BOLD); + const int textY = y + margin - 2; + renderer.drawText(UI_12_FONT_ID, textX, textY, message, true, EpdFontFamily::BOLD); renderer.displayBuffer(); return {x, y, w, h}; } void ScreenComponents::fillPopupProgress(const GfxRenderer& renderer, const PopupLayout& layout, const int progress) { - constexpr int barWidth = POPUP_DEFAULT_MIN_WIDTH; - constexpr int barHeight = POPUP_DEFAULT_BAR_HEIGHT; + constexpr int barHeight = 4; + const int barWidth = layout.width - 30; // twice the margin in drawPopup to match text width const int barX = layout.x + (layout.width - barWidth) / 2; - const int barY = layout.y + layout.height - 13; + const int barY = layout.y + layout.height - 10; int fillWidth = barWidth * progress / 100; - if (fillWidth < 0) { - fillWidth = 0; - } else if (fillWidth > barWidth) { - fillWidth = barWidth; - } renderer.fillRect(barX, barY, fillWidth, barHeight, true); diff --git a/src/ScreenComponents.h b/src/ScreenComponents.h index 00f3ee81..ba69f1a6 100644 --- a/src/ScreenComponents.h +++ b/src/ScreenComponents.h @@ -13,10 +13,6 @@ struct TabInfo { class ScreenComponents { public: - static constexpr int POPUP_DEFAULT_MIN_WIDTH = 130; - static constexpr int POPUP_DEFAULT_MIN_HEIGHT = 50; - static constexpr int POPUP_DEFAULT_BAR_HEIGHT = 4; - struct PopupLayout { int x; int y; diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 738c1c0c..50b06a90 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -8,19 +8,16 @@ #include "CrossPointSettings.h" #include "CrossPointState.h" +#include "ScreenComponents.h" #include "fontIds.h" #include "images/CrossLarge.h" #include "util/StringUtils.h" -#include "ScreenComponents.h" void SleepActivity::onEnter() { Activity::onEnter(); ScreenComponents::drawPopup(renderer, "Sleeping"); - // debug delay to see sleep screen - delay(5000); - if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) { return renderBlankSleepScreen(); } diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 196a7455..e77e559b 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -286,7 +286,7 @@ void EpubReaderActivity::renderScreen() { Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis()); pagesUntilFullRefresh = 0; - const auto popupLayout = ScreenComponents::drawPopup(renderer, "Rendering"); + const auto popupLayout = ScreenComponents::drawPopup(renderer, "Indexing..."); const auto progressCallback = [this, popupLayout](int progress) { ScreenComponents::fillPopupProgress(renderer, popupLayout, progress); }; From f4625b56e1bb28a7c3d89187a6c888ca9b198fc7 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 14:23:55 +0500 Subject: [PATCH 10/18] refactor: Add grayscale revert function and remove unused variable in renderScreen --- lib/GfxRenderer/GfxRenderer.cpp | 2 ++ src/activities/reader/EpubReaderActivity.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index cb268fc8..08420bf9 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -642,6 +642,8 @@ uint8_t* GfxRenderer::getFrameBuffer() const { return einkDisplay.getFrameBuffer size_t GfxRenderer::getBufferSize() { return EInkDisplay::BUFFER_SIZE; } +void GfxRenderer::grayscaleRevert() const { einkDisplay.grayscaleRevert(); } + void GfxRenderer::copyGrayscaleLsbBuffers() const { einkDisplay.copyGrayscaleLsbBuffers(einkDisplay.getFrameBuffer()); } void GfxRenderer::copyGrayscaleMsbBuffers() const { einkDisplay.copyGrayscaleMsbBuffers(einkDisplay.getFrameBuffer()); } diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index e77e559b..991ac696 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -285,7 +285,6 @@ void EpubReaderActivity::renderScreen() { viewportHeight, SETTINGS.hyphenationEnabled)) { Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis()); - pagesUntilFullRefresh = 0; const auto popupLayout = ScreenComponents::drawPopup(renderer, "Indexing..."); const auto progressCallback = [this, popupLayout](int progress) { ScreenComponents::fillPopupProgress(renderer, popupLayout, progress); From aa2e6ad0b352240eff6a8a3de1928c56da76b81a Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 14:26:38 +0500 Subject: [PATCH 11/18] refactor: Move MIN_SIZE_FOR_PROGRESS constant to the correct location and remove unused include --- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp | 5 ++++- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index e93b3a72..1d7e2ab3 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -10,6 +10,9 @@ const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"}; constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]); +// Minimum file size (in bytes) to show progress bar - smaller chapters don't benefit from it +constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB + const char* BLOCK_TAGS[] = {"p", "li", "div", "br", "blockquote"}; constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]); @@ -302,7 +305,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { // Update progress (call every 10% change to avoid too frequent updates) // Only show progress for larger chapters where rendering overhead is worth it bytesRead += len; - if (progressFn && totalSize >= ChapterHtmlSlimParser::MIN_SIZE_FOR_PROGRESS) { + if (progressFn && totalSize >= MIN_SIZE_FOR_PROGRESS) { const int progress = static_cast((bytesRead * 100) / totalSize); if (lastProgress / 10 != progress / 10) { lastProgress = progress; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index 456b19f8..afdf86d3 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -3,7 +3,6 @@ #include #include -#include #include #include From 43cd44c061d1582c60b9b0a570e31d76a3bf492a Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 14:27:12 +0500 Subject: [PATCH 12/18] refactor: Remove MIN_SIZE_FOR_PROGRESS constant from ChapterHtmlSlimParser --- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index afdf86d3..5355211a 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -15,11 +15,6 @@ class GfxRenderer; #define MAX_WORD_SIZE 200 class ChapterHtmlSlimParser { - public: - // Minimum file size (in bytes) to show progress bar - smaller chapters don't benefit from it. - static constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB - - private: const std::string& filepath; GfxRenderer& renderer; std::function)> completePageFn; From c78a0db5584a9b200abfe21700c0f29ddb010707 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 14:28:53 +0500 Subject: [PATCH 13/18] refactor: revert build flags changes --- platformio.ini | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index faf4fd9a..da5ca523 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,10 +27,7 @@ build_flags = # https://libexpat.github.io/doc/api/latest/#XML_GE -DXML_GE=0 -DXML_CONTEXT_BYTES=1024 - ; -std=c++2a - -std=gnu++2a - -build_unflags = -std=gnu++11 + -std=c++2a # Enable UTF-8 long file names in SdFat -DUSE_UTF8_LONG_NAMES=1 From 11366862cb6f610df72f28e2b2b482f81d065eff Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 14:29:17 +0500 Subject: [PATCH 14/18] refactor: remove unnecessary blank line in platformio.ini --- platformio.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index da5ca523..7f42637d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,7 +28,6 @@ build_flags = -DXML_GE=0 -DXML_CONTEXT_BYTES=1024 -std=c++2a - # Enable UTF-8 long file names in SdFat -DUSE_UTF8_LONG_NAMES=1 From d9006b2991e1632e737f20d16394c3f8973429ff Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 15:01:09 +0500 Subject: [PATCH 15/18] refactor: remove progress callback and related logic from section file creation and indexing --- lib/Epub/Epub/Section.cpp | 6 ++--- lib/Epub/Epub/Section.h | 4 +--- .../Epub/parsers/ChapterHtmlSlimParser.cpp | 24 ++++++------------- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 7 ++---- src/activities/reader/EpubReaderActivity.cpp | 7 +----- src/activities/reader/TxtReaderActivity.cpp | 12 +--------- 6 files changed, 14 insertions(+), 46 deletions(-) diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index 31a99af1..eef35b31 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -122,8 +122,7 @@ bool Section::clearCache() const { bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, - const uint16_t viewportHeight, const bool hyphenationEnabled, - const std::function& progressFn) { + const uint16_t viewportHeight, const bool hyphenationEnabled) { const auto localPath = epub->getSpineItem(spineIndex).href; const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html"; @@ -179,8 +178,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c ChapterHtmlSlimParser visitor( tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth, viewportHeight, hyphenationEnabled, - [this, &lut](std::unique_ptr page) { lut.emplace_back(this->onPageComplete(std::move(page))); }, - progressFn); + [this, &lut](std::unique_ptr page) { lut.emplace_back(this->onPageComplete(std::move(page))); }); Hyphenator::setPreferredLanguage(epub->getLanguage()); success = visitor.parseAndBuildPages(); diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index cfa01fe6..4bb26fdf 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include "Epub.h" @@ -32,7 +31,6 @@ class Section { uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled); bool clearCache() const; bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment, - uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, - const std::function& progressFn = nullptr); + uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled); std::unique_ptr loadPageFromSectionFile(); }; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 1d7e2ab3..e73bc9cc 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "../Page.h" @@ -10,8 +11,8 @@ const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"}; constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]); -// Minimum file size (in bytes) to show progress bar - smaller chapters don't benefit from it -constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB +// Minimum file size (in bytes) to show indexing popup - smaller chapters don't benefit from it +constexpr size_t MIN_SIZE_FOR_POPUP = 50 * 1024; // 50KB const char* BLOCK_TAGS[] = {"p", "li", "div", "br", "blockquote"}; constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]); @@ -269,10 +270,10 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { return false; } - // Get file size for progress calculation - const size_t totalSize = file.size(); - size_t bytesRead = 0; - int lastProgress = -1; + // Get file size to decide whether to show indexing popup. + if (file.size() >= MIN_SIZE_FOR_POPUP) { + ScreenComponents::drawPopup(renderer, "Indexing..."); + } XML_SetUserData(parser, this); XML_SetElementHandler(parser, startElement, endElement); @@ -302,17 +303,6 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { return false; } - // Update progress (call every 10% change to avoid too frequent updates) - // Only show progress for larger chapters where rendering overhead is worth it - bytesRead += len; - if (progressFn && totalSize >= MIN_SIZE_FOR_PROGRESS) { - const int progress = static_cast((bytesRead * 100) / totalSize); - if (lastProgress / 10 != progress / 10) { - 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 5355211a..7d66bd20 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -18,7 +18,6 @@ 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; @@ -50,8 +49,7 @@ class ChapterHtmlSlimParser { const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint16_t viewportHeight, const bool hyphenationEnabled, - const std::function)>& completePageFn, - const std::function& progressFn = nullptr) + const std::function)>& completePageFn) : filepath(filepath), renderer(renderer), fontId(fontId), @@ -61,8 +59,7 @@ class ChapterHtmlSlimParser { viewportWidth(viewportWidth), viewportHeight(viewportHeight), hyphenationEnabled(hyphenationEnabled), - completePageFn(completePageFn), - progressFn(progressFn) {} + completePageFn(completePageFn) {} ~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 991ac696..10996aee 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -285,14 +285,9 @@ void EpubReaderActivity::renderScreen() { viewportHeight, SETTINGS.hyphenationEnabled)) { Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis()); - const auto popupLayout = ScreenComponents::drawPopup(renderer, "Indexing..."); - const auto progressCallback = [this, popupLayout](int progress) { - ScreenComponents::fillPopupProgress(renderer, popupLayout, progress); - }; - if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, - viewportHeight, SETTINGS.hyphenationEnabled, progressCallback)) { + viewportHeight, SETTINGS.hyphenationEnabled)) { Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis()); section.reset(); return; diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index 60cafa7d..7a7d87d2 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -188,11 +188,10 @@ void TxtReaderActivity::buildPageIndex() { size_t offset = 0; const size_t fileSize = txt->getFileSize(); - int lastProgressPercent = -1; Serial.printf("[%lu] [TRS] Building page index for %zu bytes...\n", millis(), fileSize); - const auto popupLayout = ScreenComponents::drawPopup(renderer, "Indexing..."); + ScreenComponents::drawPopup(renderer, "Indexing..."); while (offset < fileSize) { std::vector tempLines; @@ -212,15 +211,6 @@ void TxtReaderActivity::buildPageIndex() { pageOffsets.push_back(offset); } - // Update progress bar every 10% (matching EpubReaderActivity logic) - int progressPercent = (offset * 100) / fileSize; - if (lastProgressPercent / 10 != progressPercent / 10) { - lastProgressPercent = progressPercent; - - // Fill progress bar - ScreenComponents::fillPopupProgress(renderer, popupLayout, progressPercent); - } - // Yield to other tasks periodically if (pageOffsets.size() % 20 == 0) { vTaskDelay(1); From 192acba809c3a39cb4391d3d406147e57c2189a7 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 15:09:35 +0500 Subject: [PATCH 16/18] refactor: add popup callback to section file creation for indexing feedback --- lib/Epub/Epub/Section.cpp | 5 +++-- lib/Epub/Epub/Section.h | 4 +++- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp | 5 ++--- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 7 +++++-- src/activities/reader/EpubReaderActivity.cpp | 10 +++++++++- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index eef35b31..cf67108b 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -122,7 +122,8 @@ bool Section::clearCache() const { bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, - const uint16_t viewportHeight, const bool hyphenationEnabled) { + const uint16_t viewportHeight, const bool hyphenationEnabled, + const std::function& popupFn) { const auto localPath = epub->getSpineItem(spineIndex).href; const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html"; @@ -178,7 +179,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c ChapterHtmlSlimParser visitor( tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth, viewportHeight, hyphenationEnabled, - [this, &lut](std::unique_ptr page) { lut.emplace_back(this->onPageComplete(std::move(page))); }); + [this, &lut](std::unique_ptr page) { lut.emplace_back(this->onPageComplete(std::move(page))); }, popupFn); Hyphenator::setPreferredLanguage(epub->getLanguage()); success = visitor.parseAndBuildPages(); diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index 4bb26fdf..5fdf210a 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 { uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled); bool clearCache() const; bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment, - uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled); + uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, + const std::function& popupFn = nullptr); std::unique_ptr loadPageFromSectionFile(); }; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index e73bc9cc..3241d335 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "../Page.h" @@ -271,8 +270,8 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { } // Get file size to decide whether to show indexing popup. - if (file.size() >= MIN_SIZE_FOR_POPUP) { - ScreenComponents::drawPopup(renderer, "Indexing..."); + if (popupFn && file.size() >= MIN_SIZE_FOR_POPUP) { + popupFn(); } XML_SetUserData(parser, this); diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index 7d66bd20..caf8ec4d 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 popupFn; // Popup callback int depth = 0; int skipUntilDepth = INT_MAX; int boldUntilDepth = INT_MAX; @@ -49,7 +50,8 @@ class ChapterHtmlSlimParser { const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint16_t viewportHeight, const bool hyphenationEnabled, - const std::function)>& completePageFn) + const std::function)>& completePageFn, + const std::function& popupFn = nullptr) : filepath(filepath), renderer(renderer), fontId(fontId), @@ -59,7 +61,8 @@ class ChapterHtmlSlimParser { viewportWidth(viewportWidth), viewportHeight(viewportHeight), hyphenationEnabled(hyphenationEnabled), - completePageFn(completePageFn) {} + completePageFn(completePageFn), + popupFn(popupFn) {} ~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 10996aee..c95d271b 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -285,9 +285,17 @@ void EpubReaderActivity::renderScreen() { viewportHeight, SETTINGS.hyphenationEnabled)) { Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis()); + bool popupShown = false; + const auto popupFn = [this, &popupShown]() { + if (!popupShown) { + ScreenComponents::drawPopup(renderer, "Indexing..."); + popupShown = true; + } + }; + if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, - viewportHeight, SETTINGS.hyphenationEnabled)) { + viewportHeight, SETTINGS.hyphenationEnabled, popupFn)) { Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis()); section.reset(); return; From adda1954b99b003f8d97abca52bbb2473859aa18 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 15:12:21 +0500 Subject: [PATCH 17/18] refactor: simplify popup logic in renderScreen function --- src/activities/reader/EpubReaderActivity.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index c95d271b..c7b533ee 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -285,13 +285,7 @@ void EpubReaderActivity::renderScreen() { viewportHeight, SETTINGS.hyphenationEnabled)) { Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis()); - bool popupShown = false; - const auto popupFn = [this, &popupShown]() { - if (!popupShown) { - ScreenComponents::drawPopup(renderer, "Indexing..."); - popupShown = true; - } - }; + const auto popupFn = [this]() { ScreenComponents::drawPopup(renderer, "Indexing..."); }; if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, From 6873404f50514e6c1f49315de7321a822b1fe37a Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Mon, 26 Jan 2026 15:39:07 +0500 Subject: [PATCH 18/18] refactor: restore wording --- src/activities/boot_sleep/SleepActivity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 50b06a90..80b1ec97 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -16,7 +16,7 @@ void SleepActivity::onEnter() { Activity::onEnter(); - ScreenComponents::drawPopup(renderer, "Sleeping"); + ScreenComponents::drawPopup(renderer, "Entering Sleep..."); if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) { return renderBlankSleepScreen();