2025-12-17 23:32:18 +11:00
|
|
|
#include "EpubReaderChapterSelectionActivity.h"
|
2025-12-13 21:17:34 +11:00
|
|
|
|
|
|
|
|
#include <GfxRenderer.h>
|
|
|
|
|
|
2026-01-19 06:55:35 -05:00
|
|
|
#include "KOReaderCredentialStore.h"
|
|
|
|
|
#include "KOReaderSyncActivity.h"
|
2025-12-30 15:09:30 +10:00
|
|
|
#include "MappedInputManager.h"
|
Aleo, Noto Sans, Open Dyslexic fonts (#163)
## Summary
* Swap out Bookerly font due to licensing issues, replace default font
with Aleo
* I did a bunch of searching around for a nice replacement font, and
this trumped several other like Literata, Merriwether, Vollkorn, etc
* Add Noto Sans, and Open Dyslexic as font options
* They can be selected in the settings screen
* Add font size options (Small, Medium, Large, Extra Large)
* Adjustable in settings
* Swap out uses of reader font in headings and replaced with slightly
larger Ubuntu font
* Replaced PixelArial14 font as it was difficult to track down, replace
with Space Grotesk
* Remove auto formatting on generated font files
* Massively speeds up formatting step now that there is a lot more CPP
font source
* Include fonts with their licenses in the repo
## Additional Context
Line compression setting will follow
| Font | Small | Medium | Large | X Large |
| --- | --- | --- | --- | --- |
| Aleo |

|

|

|

|
| Noto Sans |

|

|

|

|
| Open Dyslexic |

|

|

|

|
2025-12-30 18:21:47 +10:00
|
|
|
#include "fontIds.h"
|
2025-12-13 21:17:34 +11:00
|
|
|
|
2025-12-21 17:12:53 +11:00
|
|
|
namespace {
|
Rotation Support (#77)
• What is the goal of this PR?
Implement a horizontal EPUB reading mode so books can be read in
landscape orientation (both 90° and 270°), while keeping the rest of the
UI in portrait.
• What changes are included?
◦ Rendering / Display
▪ Added an orientation model to GfxRenderer (Portrait, LandscapeNormal,
LandscapeFlipped) and made:
▪ drawPixel, drawImage, displayWindow map logical coordinates
differently depending on orientation.
▪ getScreenWidth() / getScreenHeight() return orientation‑aware logical
dimensions (480×800 in portrait, 800×480 in landscape).
◦ Settings / Configuration
▪ Extended CrossPointSettings with:
▪ landscapeReading (toggle for portrait vs. landscape EPUB reading).
▪ landscapeFlipped (toggle to flip landscape 180° so both horizontal
holding directions are supported).
▪ Updated settings serialization/deserialization to persist these fields
while remaining backward‑compatible with existing settings files.
▪ Updated SettingsActivity to expose two new toggles:
▪ “Landscape Reading”
▪ “Flip Landscape (swap top/bottom)”
◦ EPUB Reader
▪ In EpubReaderActivity:
▪ On onEnter, set GfxRenderer orientation based on the new settings
(Portrait, LandscapeNormal, or LandscapeFlipped).
▪ On onExit, reset orientation back to Portrait so Home, WiFi, Settings,
etc. continue to render as before.
▪ Adjusted renderStatusBar to position the status bar and battery
indicator relative to GfxRenderer::getScreenHeight() instead of
hard‑coded Y coordinates, so it stays correctly at the bottom in both
portrait and landscape.
◦ EPUB Caching / Layout
▪ Extended Section cache metadata (section.bin) to include the logical
screenWidth and screenHeight used when pages were generated; bumped
SECTION_FILE_VERSION.
▪ Updated loadCacheMetadata to compare:
▪ font/margins/line compression/extraParagraphSpacing and screen
dimensions; mismatches now invalidate and clear the cache.
▪ Updated persistPageDataToSD and all call sites in EpubReaderActivity
to pass the current GfxRenderer::getScreenWidth() / getScreenHeight() so
portrait and landscape caches are kept separate and correctly sized.
Additional Context
• Cache behavior / migration
◦ Existing section.bin files (old SECTION_FILE_VERSION) will be detected
as incompatible and their caches cleared and rebuilt once per chapter
when first opened after this change.
◦ Within a given orientation, caches will be reused as before. Switching
orientation (portrait ↔ landscape) will cause a one‑time re‑index of
each chapter in the new orientation.
• Scope and risks
◦ Orientation changes are scoped to the EPUB reader; the Home screen,
Settings, WiFi selection, sleep screens, and web server UI continue to
assume portrait orientation.
◦ The renderer’s orientation is a static/global setting; if future code
uses GfxRenderer outside the reader while a reader instance is active,
it should be aware that orientation is no longer implicitly fixed.
◦ All drawing primitives now go through orientation‑aware coordinate
transforms; any code that previously relied on edge‑case behavior or
out‑of‑bounds writes might surface as logged “Outside range” warnings
instead.
• Testing suggestions / areas to focus on
◦ Verify in hardware:
▪ Portrait mode still renders correctly (boot, home, settings, WiFi,
reader).
▪ Landscape reading in both directions:
▪ Landscape Reading = ON, Flip Landscape = OFF.
▪ Landscape Reading = ON, Flip Landscape = ON.
▪ Status bar (page X/Y, % progress, battery icon) is fully visible and
aligned at the bottom in all three combinations.
◦ Open the same book:
▪ In portrait first, then switch to landscape and reopen it.
▪ Confirm that:
▪ Old portrait caches are rebuilt once for landscape (you should see the
“Indexing…” page).
▪ Progress save/restore still works (resume opens to the correct page in
the current orientation).
◦ Ensure grayscale rendering (the secondary pass in
EpubReaderActivity::renderContents) still looks correct in both
orientations.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-28 05:33:20 -05:00
|
|
|
// Time threshold for treating a long press as a page-up/page-down
|
2025-12-13 21:17:34 +11:00
|
|
|
constexpr int SKIP_PAGE_MS = 700;
|
2025-12-21 17:12:53 +11:00
|
|
|
} // namespace
|
2025-12-13 21:17:34 +11:00
|
|
|
|
2026-01-19 06:55:35 -05:00
|
|
|
bool EpubReaderChapterSelectionActivity::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); }
|
|
|
|
|
|
|
|
|
|
int EpubReaderChapterSelectionActivity::getTotalItems() const {
|
|
|
|
|
// Add 2 for sync options (top and bottom) if credentials are configured
|
|
|
|
|
const int syncCount = hasSyncOption() ? 2 : 0;
|
|
|
|
|
return epub->getTocItemsCount() + syncCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EpubReaderChapterSelectionActivity::isSyncItem(int index) const {
|
|
|
|
|
if (!hasSyncOption()) return false;
|
|
|
|
|
// First item and last item are sync options
|
|
|
|
|
return index == 0 || index == getTotalItems() - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int EpubReaderChapterSelectionActivity::tocIndexFromItemIndex(int itemIndex) const {
|
|
|
|
|
// Account for the sync option at the top
|
|
|
|
|
const int offset = hasSyncOption() ? 1 : 0;
|
|
|
|
|
return itemIndex - offset;
|
|
|
|
|
}
|
|
|
|
|
|
Rotation Support (#77)
• What is the goal of this PR?
Implement a horizontal EPUB reading mode so books can be read in
landscape orientation (both 90° and 270°), while keeping the rest of the
UI in portrait.
• What changes are included?
◦ Rendering / Display
▪ Added an orientation model to GfxRenderer (Portrait, LandscapeNormal,
LandscapeFlipped) and made:
▪ drawPixel, drawImage, displayWindow map logical coordinates
differently depending on orientation.
▪ getScreenWidth() / getScreenHeight() return orientation‑aware logical
dimensions (480×800 in portrait, 800×480 in landscape).
◦ Settings / Configuration
▪ Extended CrossPointSettings with:
▪ landscapeReading (toggle for portrait vs. landscape EPUB reading).
▪ landscapeFlipped (toggle to flip landscape 180° so both horizontal
holding directions are supported).
▪ Updated settings serialization/deserialization to persist these fields
while remaining backward‑compatible with existing settings files.
▪ Updated SettingsActivity to expose two new toggles:
▪ “Landscape Reading”
▪ “Flip Landscape (swap top/bottom)”
◦ EPUB Reader
▪ In EpubReaderActivity:
▪ On onEnter, set GfxRenderer orientation based on the new settings
(Portrait, LandscapeNormal, or LandscapeFlipped).
▪ On onExit, reset orientation back to Portrait so Home, WiFi, Settings,
etc. continue to render as before.
▪ Adjusted renderStatusBar to position the status bar and battery
indicator relative to GfxRenderer::getScreenHeight() instead of
hard‑coded Y coordinates, so it stays correctly at the bottom in both
portrait and landscape.
◦ EPUB Caching / Layout
▪ Extended Section cache metadata (section.bin) to include the logical
screenWidth and screenHeight used when pages were generated; bumped
SECTION_FILE_VERSION.
▪ Updated loadCacheMetadata to compare:
▪ font/margins/line compression/extraParagraphSpacing and screen
dimensions; mismatches now invalidate and clear the cache.
▪ Updated persistPageDataToSD and all call sites in EpubReaderActivity
to pass the current GfxRenderer::getScreenWidth() / getScreenHeight() so
portrait and landscape caches are kept separate and correctly sized.
Additional Context
• Cache behavior / migration
◦ Existing section.bin files (old SECTION_FILE_VERSION) will be detected
as incompatible and their caches cleared and rebuilt once per chapter
when first opened after this change.
◦ Within a given orientation, caches will be reused as before. Switching
orientation (portrait ↔ landscape) will cause a one‑time re‑index of
each chapter in the new orientation.
• Scope and risks
◦ Orientation changes are scoped to the EPUB reader; the Home screen,
Settings, WiFi selection, sleep screens, and web server UI continue to
assume portrait orientation.
◦ The renderer’s orientation is a static/global setting; if future code
uses GfxRenderer outside the reader while a reader instance is active,
it should be aware that orientation is no longer implicitly fixed.
◦ All drawing primitives now go through orientation‑aware coordinate
transforms; any code that previously relied on edge‑case behavior or
out‑of‑bounds writes might surface as logged “Outside range” warnings
instead.
• Testing suggestions / areas to focus on
◦ Verify in hardware:
▪ Portrait mode still renders correctly (boot, home, settings, WiFi,
reader).
▪ Landscape reading in both directions:
▪ Landscape Reading = ON, Flip Landscape = OFF.
▪ Landscape Reading = ON, Flip Landscape = ON.
▪ Status bar (page X/Y, % progress, battery icon) is fully visible and
aligned at the bottom in all three combinations.
◦ Open the same book:
▪ In portrait first, then switch to landscape and reopen it.
▪ Confirm that:
▪ Old portrait caches are rebuilt once for landscape (you should see the
“Indexing…” page).
▪ Progress save/restore still works (resume opens to the correct page in
the current orientation).
◦ Ensure grayscale rendering (the secondary pass in
EpubReaderActivity::renderContents) still looks correct in both
orientations.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-28 05:33:20 -05:00
|
|
|
int EpubReaderChapterSelectionActivity::getPageItems() const {
|
|
|
|
|
// Layout constants used in renderScreen
|
|
|
|
|
constexpr int startY = 60;
|
|
|
|
|
constexpr int lineHeight = 30;
|
|
|
|
|
|
|
|
|
|
const int screenHeight = renderer.getScreenHeight();
|
2026-01-12 00:59:02 -08:00
|
|
|
const int endY = screenHeight - lineHeight;
|
|
|
|
|
|
|
|
|
|
const int availableHeight = endY - startY;
|
Rotation Support (#77)
• What is the goal of this PR?
Implement a horizontal EPUB reading mode so books can be read in
landscape orientation (both 90° and 270°), while keeping the rest of the
UI in portrait.
• What changes are included?
◦ Rendering / Display
▪ Added an orientation model to GfxRenderer (Portrait, LandscapeNormal,
LandscapeFlipped) and made:
▪ drawPixel, drawImage, displayWindow map logical coordinates
differently depending on orientation.
▪ getScreenWidth() / getScreenHeight() return orientation‑aware logical
dimensions (480×800 in portrait, 800×480 in landscape).
◦ Settings / Configuration
▪ Extended CrossPointSettings with:
▪ landscapeReading (toggle for portrait vs. landscape EPUB reading).
▪ landscapeFlipped (toggle to flip landscape 180° so both horizontal
holding directions are supported).
▪ Updated settings serialization/deserialization to persist these fields
while remaining backward‑compatible with existing settings files.
▪ Updated SettingsActivity to expose two new toggles:
▪ “Landscape Reading”
▪ “Flip Landscape (swap top/bottom)”
◦ EPUB Reader
▪ In EpubReaderActivity:
▪ On onEnter, set GfxRenderer orientation based on the new settings
(Portrait, LandscapeNormal, or LandscapeFlipped).
▪ On onExit, reset orientation back to Portrait so Home, WiFi, Settings,
etc. continue to render as before.
▪ Adjusted renderStatusBar to position the status bar and battery
indicator relative to GfxRenderer::getScreenHeight() instead of
hard‑coded Y coordinates, so it stays correctly at the bottom in both
portrait and landscape.
◦ EPUB Caching / Layout
▪ Extended Section cache metadata (section.bin) to include the logical
screenWidth and screenHeight used when pages were generated; bumped
SECTION_FILE_VERSION.
▪ Updated loadCacheMetadata to compare:
▪ font/margins/line compression/extraParagraphSpacing and screen
dimensions; mismatches now invalidate and clear the cache.
▪ Updated persistPageDataToSD and all call sites in EpubReaderActivity
to pass the current GfxRenderer::getScreenWidth() / getScreenHeight() so
portrait and landscape caches are kept separate and correctly sized.
Additional Context
• Cache behavior / migration
◦ Existing section.bin files (old SECTION_FILE_VERSION) will be detected
as incompatible and their caches cleared and rebuilt once per chapter
when first opened after this change.
◦ Within a given orientation, caches will be reused as before. Switching
orientation (portrait ↔ landscape) will cause a one‑time re‑index of
each chapter in the new orientation.
• Scope and risks
◦ Orientation changes are scoped to the EPUB reader; the Home screen,
Settings, WiFi selection, sleep screens, and web server UI continue to
assume portrait orientation.
◦ The renderer’s orientation is a static/global setting; if future code
uses GfxRenderer outside the reader while a reader instance is active,
it should be aware that orientation is no longer implicitly fixed.
◦ All drawing primitives now go through orientation‑aware coordinate
transforms; any code that previously relied on edge‑case behavior or
out‑of‑bounds writes might surface as logged “Outside range” warnings
instead.
• Testing suggestions / areas to focus on
◦ Verify in hardware:
▪ Portrait mode still renders correctly (boot, home, settings, WiFi,
reader).
▪ Landscape reading in both directions:
▪ Landscape Reading = ON, Flip Landscape = OFF.
▪ Landscape Reading = ON, Flip Landscape = ON.
▪ Status bar (page X/Y, % progress, battery icon) is fully visible and
aligned at the bottom in all three combinations.
◦ Open the same book:
▪ In portrait first, then switch to landscape and reopen it.
▪ Confirm that:
▪ Old portrait caches are rebuilt once for landscape (you should see the
“Indexing…” page).
▪ Progress save/restore still works (resume opens to the correct page in
the current orientation).
◦ Ensure grayscale rendering (the secondary pass in
EpubReaderActivity::renderContents) still looks correct in both
orientations.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-28 05:33:20 -05:00
|
|
|
int items = availableHeight / lineHeight;
|
|
|
|
|
|
|
|
|
|
// Ensure we always have at least one item per page to avoid division by zero
|
|
|
|
|
if (items < 1) {
|
|
|
|
|
items = 1;
|
|
|
|
|
}
|
|
|
|
|
return items;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 23:32:18 +11:00
|
|
|
void EpubReaderChapterSelectionActivity::taskTrampoline(void* param) {
|
|
|
|
|
auto* self = static_cast<EpubReaderChapterSelectionActivity*>(param);
|
2025-12-13 21:17:34 +11:00
|
|
|
self->displayTaskLoop();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 23:32:18 +11:00
|
|
|
void EpubReaderChapterSelectionActivity::onEnter() {
|
2026-01-19 06:55:35 -05:00
|
|
|
ActivityWithSubactivity::onEnter();
|
2025-12-21 21:17:00 +11:00
|
|
|
|
2025-12-13 21:17:34 +11:00
|
|
|
if (!epub) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderingMutex = xSemaphoreCreateMutex();
|
2026-01-19 06:55:35 -05:00
|
|
|
|
|
|
|
|
// Account for sync option offset when finding current TOC index
|
|
|
|
|
const int syncOffset = hasSyncOption() ? 1 : 0;
|
2025-12-30 17:52:42 +10:00
|
|
|
selectorIndex = epub->getTocIndexForSpineIndex(currentSpineIndex);
|
|
|
|
|
if (selectorIndex == -1) {
|
|
|
|
|
selectorIndex = 0;
|
|
|
|
|
}
|
2026-01-19 06:55:35 -05:00
|
|
|
selectorIndex += syncOffset; // Offset for top sync option
|
2025-12-13 21:17:34 +11:00
|
|
|
|
|
|
|
|
// Trigger first update
|
|
|
|
|
updateRequired = true;
|
2025-12-17 23:32:18 +11:00
|
|
|
xTaskCreate(&EpubReaderChapterSelectionActivity::taskTrampoline, "EpubReaderChapterSelectionActivityTask",
|
2025-12-24 22:36:13 +11:00
|
|
|
4096, // Stack size
|
2025-12-13 21:17:34 +11:00
|
|
|
this, // Parameters
|
|
|
|
|
1, // Priority
|
|
|
|
|
&displayTaskHandle // Task handle
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 23:32:18 +11:00
|
|
|
void EpubReaderChapterSelectionActivity::onExit() {
|
2026-01-19 06:55:35 -05:00
|
|
|
ActivityWithSubactivity::onExit();
|
2025-12-21 21:17:00 +11:00
|
|
|
|
2025-12-13 21:17:34 +11:00
|
|
|
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
|
|
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
|
|
|
if (displayTaskHandle) {
|
|
|
|
|
vTaskDelete(displayTaskHandle);
|
|
|
|
|
displayTaskHandle = nullptr;
|
|
|
|
|
}
|
|
|
|
|
vSemaphoreDelete(renderingMutex);
|
|
|
|
|
renderingMutex = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-19 06:55:35 -05:00
|
|
|
void EpubReaderChapterSelectionActivity::launchSyncActivity() {
|
|
|
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
|
|
|
exitActivity();
|
|
|
|
|
enterNewActivity(new KOReaderSyncActivity(
|
|
|
|
|
renderer, mappedInput, epub, epubPath, currentSpineIndex, currentPage, totalPagesInSpine,
|
|
|
|
|
[this]() {
|
|
|
|
|
// On cancel
|
|
|
|
|
exitActivity();
|
|
|
|
|
updateRequired = true;
|
|
|
|
|
},
|
|
|
|
|
[this](int newSpineIndex, int newPage) {
|
|
|
|
|
// On sync complete
|
|
|
|
|
exitActivity();
|
|
|
|
|
onSyncPosition(newSpineIndex, newPage);
|
|
|
|
|
}));
|
|
|
|
|
xSemaphoreGive(renderingMutex);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 23:32:18 +11:00
|
|
|
void EpubReaderChapterSelectionActivity::loop() {
|
2026-01-19 06:55:35 -05:00
|
|
|
if (subActivity) {
|
|
|
|
|
subActivity->loop();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-28 21:59:14 -06:00
|
|
|
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::Up) ||
|
|
|
|
|
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
|
|
|
|
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
|
|
|
|
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
2025-12-13 21:17:34 +11:00
|
|
|
|
2025-12-28 21:59:14 -06:00
|
|
|
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
Rotation Support (#77)
• What is the goal of this PR?
Implement a horizontal EPUB reading mode so books can be read in
landscape orientation (both 90° and 270°), while keeping the rest of the
UI in portrait.
• What changes are included?
◦ Rendering / Display
▪ Added an orientation model to GfxRenderer (Portrait, LandscapeNormal,
LandscapeFlipped) and made:
▪ drawPixel, drawImage, displayWindow map logical coordinates
differently depending on orientation.
▪ getScreenWidth() / getScreenHeight() return orientation‑aware logical
dimensions (480×800 in portrait, 800×480 in landscape).
◦ Settings / Configuration
▪ Extended CrossPointSettings with:
▪ landscapeReading (toggle for portrait vs. landscape EPUB reading).
▪ landscapeFlipped (toggle to flip landscape 180° so both horizontal
holding directions are supported).
▪ Updated settings serialization/deserialization to persist these fields
while remaining backward‑compatible with existing settings files.
▪ Updated SettingsActivity to expose two new toggles:
▪ “Landscape Reading”
▪ “Flip Landscape (swap top/bottom)”
◦ EPUB Reader
▪ In EpubReaderActivity:
▪ On onEnter, set GfxRenderer orientation based on the new settings
(Portrait, LandscapeNormal, or LandscapeFlipped).
▪ On onExit, reset orientation back to Portrait so Home, WiFi, Settings,
etc. continue to render as before.
▪ Adjusted renderStatusBar to position the status bar and battery
indicator relative to GfxRenderer::getScreenHeight() instead of
hard‑coded Y coordinates, so it stays correctly at the bottom in both
portrait and landscape.
◦ EPUB Caching / Layout
▪ Extended Section cache metadata (section.bin) to include the logical
screenWidth and screenHeight used when pages were generated; bumped
SECTION_FILE_VERSION.
▪ Updated loadCacheMetadata to compare:
▪ font/margins/line compression/extraParagraphSpacing and screen
dimensions; mismatches now invalidate and clear the cache.
▪ Updated persistPageDataToSD and all call sites in EpubReaderActivity
to pass the current GfxRenderer::getScreenWidth() / getScreenHeight() so
portrait and landscape caches are kept separate and correctly sized.
Additional Context
• Cache behavior / migration
◦ Existing section.bin files (old SECTION_FILE_VERSION) will be detected
as incompatible and their caches cleared and rebuilt once per chapter
when first opened after this change.
◦ Within a given orientation, caches will be reused as before. Switching
orientation (portrait ↔ landscape) will cause a one‑time re‑index of
each chapter in the new orientation.
• Scope and risks
◦ Orientation changes are scoped to the EPUB reader; the Home screen,
Settings, WiFi selection, sleep screens, and web server UI continue to
assume portrait orientation.
◦ The renderer’s orientation is a static/global setting; if future code
uses GfxRenderer outside the reader while a reader instance is active,
it should be aware that orientation is no longer implicitly fixed.
◦ All drawing primitives now go through orientation‑aware coordinate
transforms; any code that previously relied on edge‑case behavior or
out‑of‑bounds writes might surface as logged “Outside range” warnings
instead.
• Testing suggestions / areas to focus on
◦ Verify in hardware:
▪ Portrait mode still renders correctly (boot, home, settings, WiFi,
reader).
▪ Landscape reading in both directions:
▪ Landscape Reading = ON, Flip Landscape = OFF.
▪ Landscape Reading = ON, Flip Landscape = ON.
▪ Status bar (page X/Y, % progress, battery icon) is fully visible and
aligned at the bottom in all three combinations.
◦ Open the same book:
▪ In portrait first, then switch to landscape and reopen it.
▪ Confirm that:
▪ Old portrait caches are rebuilt once for landscape (you should see the
“Indexing…” page).
▪ Progress save/restore still works (resume opens to the correct page in
the current orientation).
◦ Ensure grayscale rendering (the secondary pass in
EpubReaderActivity::renderContents) still looks correct in both
orientations.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-28 05:33:20 -05:00
|
|
|
const int pageItems = getPageItems();
|
2026-01-19 06:55:35 -05:00
|
|
|
const int totalItems = getTotalItems();
|
2025-12-13 21:17:34 +11:00
|
|
|
|
2025-12-28 21:59:14 -06:00
|
|
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
2026-01-19 06:55:35 -05:00
|
|
|
// Check if sync option is selected (first or last item)
|
|
|
|
|
if (isSyncItem(selectorIndex)) {
|
|
|
|
|
launchSyncActivity();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get TOC index (account for top sync offset)
|
|
|
|
|
const int tocIndex = tocIndexFromItemIndex(selectorIndex);
|
|
|
|
|
const auto newSpineIndex = epub->getSpineIndexForTocIndex(tocIndex);
|
2025-12-30 17:52:42 +10:00
|
|
|
if (newSpineIndex == -1) {
|
|
|
|
|
onGoBack();
|
|
|
|
|
} else {
|
|
|
|
|
onSelectSpineIndex(newSpineIndex);
|
|
|
|
|
}
|
2025-12-28 21:59:14 -06:00
|
|
|
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
2025-12-13 21:17:34 +11:00
|
|
|
onGoBack();
|
|
|
|
|
} else if (prevReleased) {
|
|
|
|
|
if (skipPage) {
|
2026-01-19 06:55:35 -05:00
|
|
|
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + totalItems) % totalItems;
|
2025-12-13 21:17:34 +11:00
|
|
|
} else {
|
2026-01-19 06:55:35 -05:00
|
|
|
selectorIndex = (selectorIndex + totalItems - 1) % totalItems;
|
2025-12-13 21:17:34 +11:00
|
|
|
}
|
|
|
|
|
updateRequired = true;
|
|
|
|
|
} else if (nextReleased) {
|
|
|
|
|
if (skipPage) {
|
2026-01-19 06:55:35 -05:00
|
|
|
selectorIndex = ((selectorIndex / pageItems + 1) * pageItems) % totalItems;
|
2025-12-13 21:17:34 +11:00
|
|
|
} else {
|
2026-01-19 06:55:35 -05:00
|
|
|
selectorIndex = (selectorIndex + 1) % totalItems;
|
2025-12-13 21:17:34 +11:00
|
|
|
}
|
|
|
|
|
updateRequired = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 23:32:18 +11:00
|
|
|
void EpubReaderChapterSelectionActivity::displayTaskLoop() {
|
2025-12-13 21:17:34 +11:00
|
|
|
while (true) {
|
2026-01-19 06:55:35 -05:00
|
|
|
if (updateRequired && !subActivity) {
|
2025-12-13 21:17:34 +11:00
|
|
|
updateRequired = false;
|
|
|
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
|
|
|
renderScreen();
|
|
|
|
|
xSemaphoreGive(renderingMutex);
|
|
|
|
|
}
|
|
|
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 23:32:18 +11:00
|
|
|
void EpubReaderChapterSelectionActivity::renderScreen() {
|
2025-12-13 21:17:34 +11:00
|
|
|
renderer.clearScreen();
|
|
|
|
|
|
|
|
|
|
const auto pageWidth = renderer.getScreenWidth();
|
Rotation Support (#77)
• What is the goal of this PR?
Implement a horizontal EPUB reading mode so books can be read in
landscape orientation (both 90° and 270°), while keeping the rest of the
UI in portrait.
• What changes are included?
◦ Rendering / Display
▪ Added an orientation model to GfxRenderer (Portrait, LandscapeNormal,
LandscapeFlipped) and made:
▪ drawPixel, drawImage, displayWindow map logical coordinates
differently depending on orientation.
▪ getScreenWidth() / getScreenHeight() return orientation‑aware logical
dimensions (480×800 in portrait, 800×480 in landscape).
◦ Settings / Configuration
▪ Extended CrossPointSettings with:
▪ landscapeReading (toggle for portrait vs. landscape EPUB reading).
▪ landscapeFlipped (toggle to flip landscape 180° so both horizontal
holding directions are supported).
▪ Updated settings serialization/deserialization to persist these fields
while remaining backward‑compatible with existing settings files.
▪ Updated SettingsActivity to expose two new toggles:
▪ “Landscape Reading”
▪ “Flip Landscape (swap top/bottom)”
◦ EPUB Reader
▪ In EpubReaderActivity:
▪ On onEnter, set GfxRenderer orientation based on the new settings
(Portrait, LandscapeNormal, or LandscapeFlipped).
▪ On onExit, reset orientation back to Portrait so Home, WiFi, Settings,
etc. continue to render as before.
▪ Adjusted renderStatusBar to position the status bar and battery
indicator relative to GfxRenderer::getScreenHeight() instead of
hard‑coded Y coordinates, so it stays correctly at the bottom in both
portrait and landscape.
◦ EPUB Caching / Layout
▪ Extended Section cache metadata (section.bin) to include the logical
screenWidth and screenHeight used when pages were generated; bumped
SECTION_FILE_VERSION.
▪ Updated loadCacheMetadata to compare:
▪ font/margins/line compression/extraParagraphSpacing and screen
dimensions; mismatches now invalidate and clear the cache.
▪ Updated persistPageDataToSD and all call sites in EpubReaderActivity
to pass the current GfxRenderer::getScreenWidth() / getScreenHeight() so
portrait and landscape caches are kept separate and correctly sized.
Additional Context
• Cache behavior / migration
◦ Existing section.bin files (old SECTION_FILE_VERSION) will be detected
as incompatible and their caches cleared and rebuilt once per chapter
when first opened after this change.
◦ Within a given orientation, caches will be reused as before. Switching
orientation (portrait ↔ landscape) will cause a one‑time re‑index of
each chapter in the new orientation.
• Scope and risks
◦ Orientation changes are scoped to the EPUB reader; the Home screen,
Settings, WiFi selection, sleep screens, and web server UI continue to
assume portrait orientation.
◦ The renderer’s orientation is a static/global setting; if future code
uses GfxRenderer outside the reader while a reader instance is active,
it should be aware that orientation is no longer implicitly fixed.
◦ All drawing primitives now go through orientation‑aware coordinate
transforms; any code that previously relied on edge‑case behavior or
out‑of‑bounds writes might surface as logged “Outside range” warnings
instead.
• Testing suggestions / areas to focus on
◦ Verify in hardware:
▪ Portrait mode still renders correctly (boot, home, settings, WiFi,
reader).
▪ Landscape reading in both directions:
▪ Landscape Reading = ON, Flip Landscape = OFF.
▪ Landscape Reading = ON, Flip Landscape = ON.
▪ Status bar (page X/Y, % progress, battery icon) is fully visible and
aligned at the bottom in all three combinations.
◦ Open the same book:
▪ In portrait first, then switch to landscape and reopen it.
▪ Confirm that:
▪ Old portrait caches are rebuilt once for landscape (you should see the
“Indexing…” page).
▪ Progress save/restore still works (resume opens to the correct page in
the current orientation).
◦ Ensure grayscale rendering (the secondary pass in
EpubReaderActivity::renderContents) still looks correct in both
orientations.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-28 05:33:20 -05:00
|
|
|
const int pageItems = getPageItems();
|
2026-01-19 06:55:35 -05:00
|
|
|
const int totalItems = getTotalItems();
|
2025-12-30 23:10:41 +01:00
|
|
|
|
2025-12-31 12:11:36 +10:00
|
|
|
const std::string title =
|
|
|
|
|
renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(), pageWidth - 40, EpdFontFamily::BOLD);
|
|
|
|
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, title.c_str(), true, EpdFontFamily::BOLD);
|
2025-12-13 21:17:34 +11:00
|
|
|
|
Rotation Support (#77)
• What is the goal of this PR?
Implement a horizontal EPUB reading mode so books can be read in
landscape orientation (both 90° and 270°), while keeping the rest of the
UI in portrait.
• What changes are included?
◦ Rendering / Display
▪ Added an orientation model to GfxRenderer (Portrait, LandscapeNormal,
LandscapeFlipped) and made:
▪ drawPixel, drawImage, displayWindow map logical coordinates
differently depending on orientation.
▪ getScreenWidth() / getScreenHeight() return orientation‑aware logical
dimensions (480×800 in portrait, 800×480 in landscape).
◦ Settings / Configuration
▪ Extended CrossPointSettings with:
▪ landscapeReading (toggle for portrait vs. landscape EPUB reading).
▪ landscapeFlipped (toggle to flip landscape 180° so both horizontal
holding directions are supported).
▪ Updated settings serialization/deserialization to persist these fields
while remaining backward‑compatible with existing settings files.
▪ Updated SettingsActivity to expose two new toggles:
▪ “Landscape Reading”
▪ “Flip Landscape (swap top/bottom)”
◦ EPUB Reader
▪ In EpubReaderActivity:
▪ On onEnter, set GfxRenderer orientation based on the new settings
(Portrait, LandscapeNormal, or LandscapeFlipped).
▪ On onExit, reset orientation back to Portrait so Home, WiFi, Settings,
etc. continue to render as before.
▪ Adjusted renderStatusBar to position the status bar and battery
indicator relative to GfxRenderer::getScreenHeight() instead of
hard‑coded Y coordinates, so it stays correctly at the bottom in both
portrait and landscape.
◦ EPUB Caching / Layout
▪ Extended Section cache metadata (section.bin) to include the logical
screenWidth and screenHeight used when pages were generated; bumped
SECTION_FILE_VERSION.
▪ Updated loadCacheMetadata to compare:
▪ font/margins/line compression/extraParagraphSpacing and screen
dimensions; mismatches now invalidate and clear the cache.
▪ Updated persistPageDataToSD and all call sites in EpubReaderActivity
to pass the current GfxRenderer::getScreenWidth() / getScreenHeight() so
portrait and landscape caches are kept separate and correctly sized.
Additional Context
• Cache behavior / migration
◦ Existing section.bin files (old SECTION_FILE_VERSION) will be detected
as incompatible and their caches cleared and rebuilt once per chapter
when first opened after this change.
◦ Within a given orientation, caches will be reused as before. Switching
orientation (portrait ↔ landscape) will cause a one‑time re‑index of
each chapter in the new orientation.
• Scope and risks
◦ Orientation changes are scoped to the EPUB reader; the Home screen,
Settings, WiFi selection, sleep screens, and web server UI continue to
assume portrait orientation.
◦ The renderer’s orientation is a static/global setting; if future code
uses GfxRenderer outside the reader while a reader instance is active,
it should be aware that orientation is no longer implicitly fixed.
◦ All drawing primitives now go through orientation‑aware coordinate
transforms; any code that previously relied on edge‑case behavior or
out‑of‑bounds writes might surface as logged “Outside range” warnings
instead.
• Testing suggestions / areas to focus on
◦ Verify in hardware:
▪ Portrait mode still renders correctly (boot, home, settings, WiFi,
reader).
▪ Landscape reading in both directions:
▪ Landscape Reading = ON, Flip Landscape = OFF.
▪ Landscape Reading = ON, Flip Landscape = ON.
▪ Status bar (page X/Y, % progress, battery icon) is fully visible and
aligned at the bottom in all three combinations.
◦ Open the same book:
▪ In portrait first, then switch to landscape and reopen it.
▪ Confirm that:
▪ Old portrait caches are rebuilt once for landscape (you should see the
“Indexing…” page).
▪ Progress save/restore still works (resume opens to the correct page in
the current orientation).
◦ Ensure grayscale rendering (the secondary pass in
EpubReaderActivity::renderContents) still looks correct in both
orientations.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-28 05:33:20 -05:00
|
|
|
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
2025-12-28 21:30:01 +10:00
|
|
|
renderer.fillRect(0, 60 + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1, 30);
|
2026-01-19 06:55:35 -05:00
|
|
|
|
|
|
|
|
for (int itemIndex = pageStartIndex; itemIndex < totalItems && itemIndex < pageStartIndex + pageItems; itemIndex++) {
|
|
|
|
|
const int displayY = 60 + (itemIndex % pageItems) * 30;
|
|
|
|
|
const bool isSelected = (itemIndex == selectorIndex);
|
|
|
|
|
|
|
|
|
|
if (isSyncItem(itemIndex)) {
|
|
|
|
|
// Draw sync option (at top or bottom)
|
|
|
|
|
renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected);
|
|
|
|
|
} else {
|
|
|
|
|
// Draw TOC item (account for top sync offset)
|
|
|
|
|
const int tocIndex = tocIndexFromItemIndex(itemIndex);
|
|
|
|
|
auto item = epub->getTocItem(tocIndex);
|
fix: truncate chapter names that are too long (#422)
## Summary
* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
- Implements a fix to truncate chapter names that exceeds display width
* **What changes are included?**
- Implements a fix to truncate chapter names that exceeds display width
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
- Prior to the fix, if the book contains multiple chapters with names
longer than the display width, there is a noticeable delay when
scrolling through the list of chapters.
Serial output of the issue:
```
[25673] [ACT] Entering activity: EpubReaderChapterSelection
[25693] [GFX] !! Outside range (485, 65) -> (65, -6)
[25693] [GFX] !! Outside range (486, 65) -> (65, -7)
[25693] [GFX] !! Outside range (487, 65) -> (65, -8)
[25693] [GFX] !! Outside range (488, 65) -> (65, -9)
[25693] [GFX] !! Outside range (485, 66) -> (66, -6)
[25693] [GFX] !! Outside range (486, 66) -> (66, -7)
[25694] [GFX] !! Outside range (487, 66) -> (66, -8)
[25694] [GFX] !! Outside range (484, 67) -> (67, -5)
[25694] [GFX] !! Outside range (485, 67) -> (67, -6)
[25694] [GFX] !! Outside range (486, 67) -> (67, -7)
[25694] [GFX] !! Outside range (483, 68) -> (68, -4)
[25694] [GFX] !! Outside range (484, 68) -> (68, -5)
[25694] [GFX] !! Outside range (485, 68) -> (68, -6)
```
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_
Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-19 21:01:51 +08:00
|
|
|
const int indentSize = 20 + (item.level - 1) * 15;
|
|
|
|
|
const std::string chapterName =
|
|
|
|
|
renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - indentSize);
|
|
|
|
|
renderer.drawText(UI_10_FONT_ID, indentSize, 60 + (tocIndex % pageItems) * 30, chapterName.c_str(),
|
|
|
|
|
tocIndex != selectorIndex);
|
2026-01-19 06:55:35 -05:00
|
|
|
}
|
2025-12-13 21:17:34 +11:00
|
|
|
}
|
|
|
|
|
|
2026-01-12 00:59:02 -08:00
|
|
|
const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down");
|
|
|
|
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
|
|
|
|
2025-12-13 21:17:34 +11:00
|
|
|
renderer.displayBuffer();
|
|
|
|
|
}
|