From c3b9bc38b909e585064bfca1df5031e64f5bf581 Mon Sep 17 00:00:00 2001 From: Luke Stein <44452336+lukestein@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:49:38 -0500 Subject: [PATCH] feat: Add Chapter Progress Bar status bar option (#636) ## Summary This pull request introduces a new "Chapter Progress Bar" mode to the status bar, allowing users to track their progress within the current chapter in addition to the existing book-level progress options. It also unifies and increases the progress bar height for better visibility, and updates the settings UI to support the new mode. Closes #636 **Status Bar/Progress Bar Enhancements:** * Added a new `CHAPTER_PROGRESS_BAR` mode to `CrossPointSettings::STATUS_BAR_MODE`, and updated the settings UI to allow users to select this mode. [[1]](diffhunk://#diff-3af36372bb6233a83387a68091b5e0651c23585c7c0a95669ed893268ca709a8R34) [[2]](diffhunk://#diff-c55df9ec3ade843be000ba463cb75aa3df27dc34620a56c248fc4cc4e917b34bL22-R23) * Implemented `drawChapterProgressBar` in `ScreenComponents` and integrated it into both EPUB and TXT reader activities, so the chapter progress bar is displayed when the new mode is selected. [[1]](diffhunk://#diff-be271778a942f7fab0d920acd73442512346ff811a4625c011275a7ca6be3a3eL51-R64) [[2]](diffhunk://#diff-dd410cab3a363d78172706d2ad6591f327e9b5b05f314db405db31a667af03faL16-R20) [[3]](diffhunk://#diff-82798dedbe135495e619d4aa27a4bef560c70c7663cf43148b67a26ddde45682R518-R525) [[4]](diffhunk://#diff-471ba9d9eb65b1a8451d41246db2aa695a42ea4ae4762163adfda4c20fec0950R563-R567) * Updated logic in EPUB and TXT reader activities to show the correct progress bar, progress text, and battery indicator based on the selected status bar mode, including the new chapter progress bar mode. [[1]](diffhunk://#diff-82798dedbe135495e619d4aa27a4bef560c70c7663cf43148b67a26ddde45682R470-R481) [[2]](diffhunk://#diff-82798dedbe135495e619d4aa27a4bef560c70c7663cf43148b67a26ddde45682L490-R503) [[3]](diffhunk://#diff-471ba9d9eb65b1a8451d41246db2aa695a42ea4ae4762163adfda4c20fec0950R522-R533) [[4]](diffhunk://#diff-471ba9d9eb65b1a8451d41246db2aa695a42ea4ae4762163adfda4c20fec0950L539-R548) **UI/Visual Tweaks:** * Increased the progress bar height from 4 to 6 pixels for improved visibility, and refactored code to use the new constant. [[1]](diffhunk://#diff-dd410cab3a363d78172706d2ad6591f327e9b5b05f314db405db31a667af03faL16-R20) [[2]](diffhunk://#diff-82798dedbe135495e619d4aa27a4bef560c70c7663cf43148b67a26ddde45682L295-R295) [[3]](diffhunk://#diff-471ba9d9eb65b1a8451d41246db2aa695a42ea4ae4762163adfda4c20fec0950L177-R177) These changes collectively provide users with more granular progress tracking options and a clearer visual indicator for reading progress. ## Additional Context --- ### AI Usage Did you use AI tools to help write this code? _**YES**_ --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- USER_GUIDE.md | 7 +++- src/CrossPointSettings.h | 5 +-- src/activities/reader/EpubReaderActivity.cpp | 34 ++++++++++++++------ src/activities/reader/TxtReaderActivity.cpp | 31 ++++++++++++------ src/activities/settings/SettingsActivity.cpp | 5 +-- src/components/themes/BaseTheme.cpp | 2 +- src/components/themes/BaseTheme.h | 2 +- 7 files changed, 60 insertions(+), 26 deletions(-) diff --git a/USER_GUIDE.md b/USER_GUIDE.md index c38b2ceb..6975e944 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -13,12 +13,14 @@ Welcome to the **CrossPoint** firmware. This guide outlines the hardware control - [3.2 Book Selection](#32-book-selection) - [3.3 Reading Mode](#33-reading-mode) - [3.4 File Upload Screen](#34-file-upload-screen) + - [3.4.1 Calibre Wireless Transfers](#341-calibre-wireless-transfers) - [3.5 Settings](#35-settings) - [3.6 Sleep Screen](#36-sleep-screen) - [4. Reading Mode](#4-reading-mode) - [Page Turning](#page-turning) - [Chapter Navigation](#chapter-navigation) - [System Navigation](#system-navigation) + - [Supported Languages](#supported-languages) - [5. Chapter Selection Screen](#5-chapter-selection-screen) - [6. Current Limitations \& Roadmap](#6-current-limitations--roadmap) - [7. Troubleshooting Issues \& Escaping Bootloop](#7-troubleshooting-issues--escaping-bootloop) @@ -114,7 +116,10 @@ The Settings screen allows you to configure the device's behavior. There are a f - **Status Bar**: Configure the status bar displayed while reading: - "None" - No status bar - "No Progress" - Show status bar without reading progress - - "Full" - Show status bar with reading progress + - "Full w/ Percentage" - Show status bar with book progress (as percentage) + - "Full w/ Book Bar" - Show status bar with book progress (as bar) + - "Book Bar Only" - Show book progress (as bar) + - "Full w/ Chapter Bar" - Show status bar with chapter progress (as bar) - **Hide Battery %**: Configure where to suppress the battery pecentage display in the status bar; the battery icon will still be shown: - "Never" - Always show battery percentage (default) - "In Reader" - Show battery percentage everywhere except in reading mode diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 8fa4c624..86700cad 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -37,8 +37,9 @@ class CrossPointSettings { NONE = 0, NO_PROGRESS = 1, FULL = 2, - FULL_WITH_PROGRESS_BAR = 3, - ONLY_PROGRESS_BAR = 4, + BOOK_PROGRESS_BAR = 3, + ONLY_BOOK_PROGRESS_BAR = 4, + CHAPTER_PROGRESS_BAR = 5, STATUS_BAR_MODE_COUNT }; diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index adee8a61..72600c57 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -558,8 +558,9 @@ void EpubReaderActivity::renderScreen() { // Add status bar margin if (SETTINGS.statusBar != CrossPointSettings::STATUS_BAR_MODE::NONE) { // Add additional margin for status bar if progress bar is shown - const bool showProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::ONLY_PROGRESS_BAR; + const bool showProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::ONLY_BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; orientedMarginBottom += statusBarMargin - SETTINGS.screenMargin + (showProgressBar ? (metrics.bookProgressBarHeight + progressBarMarginTop) : 0); } @@ -713,16 +714,20 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in // determine visible status bar elements const bool showProgressPercentage = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL; - const bool showProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::ONLY_PROGRESS_BAR; + const bool showBookProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::ONLY_BOOK_PROGRESS_BAR; + const bool showChapterProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; const bool showProgressText = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR; + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR; + const bool showBookPercentage = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; const bool showBattery = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::NO_PROGRESS || SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR; + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; const bool showChapterTitle = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::NO_PROGRESS || SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR; + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; const bool showBatteryPercentage = SETTINGS.hideBatteryPercentage == CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_NEVER; @@ -735,7 +740,7 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in const float sectionChapterProg = static_cast(section->currentPage) / section->pageCount; const float bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg) * 100; - if (showProgressText || showProgressPercentage) { + if (showProgressText || showProgressPercentage || showBookPercentage) { // Right aligned text for progress counter char progressStr[32]; @@ -743,6 +748,8 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in if (showProgressPercentage) { snprintf(progressStr, sizeof(progressStr), "%d/%d %.0f%%", section->currentPage + 1, section->pageCount, bookProgress); + } else if (showBookPercentage) { + snprintf(progressStr, sizeof(progressStr), "%.0f%%", bookProgress); } else { snprintf(progressStr, sizeof(progressStr), "%d/%d", section->currentPage + 1, section->pageCount); } @@ -752,9 +759,16 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in progressStr); } - if (showProgressBar) { + if (showBookProgressBar) { // Draw progress bar at the very bottom of the screen, from edge to edge of viewable area - GUI.drawBookProgressBar(renderer, static_cast(bookProgress)); + GUI.drawReadingProgressBar(renderer, static_cast(bookProgress)); + } + + if (showChapterProgressBar) { + // Draw chapter progress bar at the very bottom of the screen, from edge to edge of viewable area + const float chapterProgress = + (section->pageCount > 0) ? (static_cast(section->currentPage + 1) / section->pageCount) * 100 : 0; + GUI.drawReadingProgressBar(renderer, static_cast(chapterProgress)); } if (showBattery) { diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index 799b90a1..4d4985c5 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -177,8 +177,9 @@ void TxtReaderActivity::initializeReader() { // Add status bar margin if (SETTINGS.statusBar != CrossPointSettings::STATUS_BAR_MODE::NONE) { // Add additional margin for status bar if progress bar is shown - const bool showProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::ONLY_PROGRESS_BAR; + const bool showProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::ONLY_BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; orientedMarginBottom += statusBarMargin - cachedScreenMargin + (showProgressBar ? (metrics.bookProgressBarHeight + progressBarMarginTop) : 0); } @@ -491,30 +492,37 @@ void TxtReaderActivity::renderPage() { void TxtReaderActivity::renderStatusBar(const int orientedMarginRight, const int orientedMarginBottom, const int orientedMarginLeft) const { const bool showProgressPercentage = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL; - const bool showProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::ONLY_PROGRESS_BAR; + const bool showProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::ONLY_BOOK_PROGRESS_BAR; + const bool showChapterProgressBar = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; const bool showProgressText = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR; + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR; + const bool showBookPercentage = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; const bool showBattery = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::NO_PROGRESS || SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR; + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; const bool showTitle = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::NO_PROGRESS || SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL || - SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL_WITH_PROGRESS_BAR; + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::BOOK_PROGRESS_BAR || + SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::CHAPTER_PROGRESS_BAR; const bool showBatteryPercentage = SETTINGS.hideBatteryPercentage == CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_NEVER; auto metrics = UITheme::getInstance().getMetrics(); const auto screenHeight = renderer.getScreenHeight(); + // Adjust text position upward when progress bar is shown to avoid overlap const auto textY = screenHeight - orientedMarginBottom - 4; int progressTextWidth = 0; const float progress = totalPages > 0 ? (currentPage + 1) * 100.0f / totalPages : 0; - if (showProgressText || showProgressPercentage) { + if (showProgressText || showProgressPercentage || showBookPercentage) { char progressStr[32]; if (showProgressPercentage) { snprintf(progressStr, sizeof(progressStr), "%d/%d %.0f%%", currentPage + 1, totalPages, progress); + } else if (showBookPercentage) { + snprintf(progressStr, sizeof(progressStr), "%.0f%%", progress); } else { snprintf(progressStr, sizeof(progressStr), "%d/%d", currentPage + 1, totalPages); } @@ -526,7 +534,12 @@ void TxtReaderActivity::renderStatusBar(const int orientedMarginRight, const int if (showProgressBar) { // Draw progress bar at the very bottom of the screen, from edge to edge of viewable area - GUI.drawBookProgressBar(renderer, static_cast(progress)); + GUI.drawReadingProgressBar(renderer, static_cast(progress)); + } + + if (showChapterProgressBar) { + // For text mode, treat the entire book as one chapter, so chapter progress == book progress + GUI.drawReadingProgressBar(renderer, static_cast(progress)); } if (showBattery) { diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 5bfaa2c2..893eb108 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -25,8 +25,9 @@ const SettingInfo displaySettings[displaySettingsCount] = { SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}), SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter, {"None", "Contrast", "Inverted"}), - SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar, - {"None", "No Progress", "Full w/ Percentage", "Full w/ Progress Bar", "Progress Bar"}), + SettingInfo::Enum( + "Status Bar", &CrossPointSettings::statusBar, + {"None", "No Progress", "Full w/ Percentage", "Full w/ Book Bar", "Book Bar Only", "Full w/ Chapter Bar"}), SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}), SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency, {"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}), diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index d79c9144..3b23e3fe 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -630,7 +630,7 @@ void BaseTheme::fillPopupProgress(const GfxRenderer& renderer, const Rect& layou renderer.displayBuffer(HalDisplay::FAST_REFRESH); } -void BaseTheme::drawBookProgressBar(const GfxRenderer& renderer, const size_t bookProgress) const { +void BaseTheme::drawReadingProgressBar(const GfxRenderer& renderer, const size_t bookProgress) const { int vieweableMarginTop, vieweableMarginRight, vieweableMarginBottom, vieweableMarginLeft; renderer.getOrientedViewableTRBL(&vieweableMarginTop, &vieweableMarginRight, &vieweableMarginBottom, &vieweableMarginLeft); diff --git a/src/components/themes/BaseTheme.h b/src/components/themes/BaseTheme.h index 11e7a378..038a2913 100644 --- a/src/components/themes/BaseTheme.h +++ b/src/components/themes/BaseTheme.h @@ -114,5 +114,5 @@ class BaseTheme { const std::function& rowIcon) const; virtual Rect drawPopup(const GfxRenderer& renderer, const char* message) const; virtual void fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const; - virtual void drawBookProgressBar(const GfxRenderer& renderer, const size_t bookProgress) const; + virtual void drawReadingProgressBar(const GfxRenderer& renderer, const size_t bookProgress) const; }; \ No newline at end of file