From a85ec570184219d05edce5594679421788ec64d9 Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Tue, 3 Feb 2026 17:38:31 +0300 Subject: [PATCH] feat: implement screen rotation feature in Epub reader menu --- src/activities/reader/EpubReaderActivity.cpp | 99 +++++++++---------- src/activities/reader/EpubReaderActivity.h | 3 +- .../reader/EpubReaderMenuActivity.cpp | 16 ++- .../reader/EpubReaderMenuActivity.h | 11 ++- 4 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 8afa8205..26c4e97f 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -20,22 +20,8 @@ constexpr unsigned long goHomeMs = 1000; constexpr int statusBarMargin = 19; constexpr int progressBarMarginTop = 1; -} // namespace - -void EpubReaderActivity::taskTrampoline(void* param) { - auto* self = static_cast(param); - self->displayTaskLoop(); -} - -void EpubReaderActivity::onEnter() { - ActivityWithSubactivity::onEnter(); - - if (!epub) { - return; - } - - // Configure screen orientation based on settings - switch (SETTINGS.orientation) { +void applyReaderOrientation(GfxRenderer& renderer, const uint8_t orientation) { + switch (orientation) { case CrossPointSettings::ORIENTATION::PORTRAIT: renderer.setOrientation(GfxRenderer::Orientation::Portrait); break; @@ -51,6 +37,24 @@ void EpubReaderActivity::onEnter() { default: break; } +} + +} // namespace + +void EpubReaderActivity::taskTrampoline(void* param) { + auto* self = static_cast(param); + self->displayTaskLoop(); +} + +void EpubReaderActivity::onEnter() { + ActivityWithSubactivity::onEnter(); + + if (!epub) { + return; + } + + // Configure screen orientation based on settings + applyReaderOrientation(renderer, SETTINGS.orientation); renderingMutex = xSemaphoreCreateMutex(); @@ -127,11 +131,10 @@ void EpubReaderActivity::loop() { if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { // Don't start activity transition while rendering xSemaphoreTake(renderingMutex, portMAX_DELAY); - const int currentPage = section ? section->currentPage : 0; - const int totalPages = section ? section->pageCount : 0; exitActivity(); enterNewActivity(new EpubReaderMenuActivity( - this->renderer, this->mappedInput, epub->getTitle(), [this]() { onReaderMenuBack(); }, + this->renderer, this->mappedInput, epub->getTitle(), SETTINGS.orientation, + [this](const uint8_t orientation) { onReaderMenuBack(orientation); }, [this](EpubReaderMenuActivity::MenuAction action) { onReaderMenuConfirm(action); })); xSemaphoreGive(renderingMutex); } @@ -220,8 +223,9 @@ void EpubReaderActivity::loop() { } } -void EpubReaderActivity::onReaderMenuBack() { +void EpubReaderActivity::onReaderMenuBack(const uint8_t orientation) { exitActivity(); + applyOrientation(orientation); updateRequired = true; } @@ -268,40 +272,6 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction xSemaphoreGive(renderingMutex); break; } - case EpubReaderMenuActivity::MenuAction::ROTATE_SCREEN: { - xSemaphoreTake(renderingMutex, portMAX_DELAY); - if (section) { - cachedSpineIndex = currentSpineIndex; - cachedChapterTotalPageCount = section->pageCount; - nextPageNumber = section->currentPage; - } - - SETTINGS.orientation = (SETTINGS.orientation + 1) % CrossPointSettings::ORIENTATION_COUNT; - SETTINGS.saveToFile(); - - switch (SETTINGS.orientation) { - case CrossPointSettings::ORIENTATION::PORTRAIT: - renderer.setOrientation(GfxRenderer::Orientation::Portrait); - break; - case CrossPointSettings::ORIENTATION::LANDSCAPE_CW: - renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); - break; - case CrossPointSettings::ORIENTATION::INVERTED: - renderer.setOrientation(GfxRenderer::Orientation::PortraitInverted); - break; - case CrossPointSettings::ORIENTATION::LANDSCAPE_CCW: - renderer.setOrientation(GfxRenderer::Orientation::LandscapeCounterClockwise); - break; - default: - break; - } - - section.reset(); - exitActivity(); - updateRequired = true; - xSemaphoreGive(renderingMutex); - break; - } case EpubReaderMenuActivity::MenuAction::GO_HOME: { // 2. Trigger the reader's "Go Home" callback if (onGoHome) { @@ -337,6 +307,27 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction } } +void EpubReaderActivity::applyOrientation(const uint8_t orientation) { + if (SETTINGS.orientation == orientation) { + return; + } + + xSemaphoreTake(renderingMutex, portMAX_DELAY); + if (section) { + cachedSpineIndex = currentSpineIndex; + cachedChapterTotalPageCount = section->pageCount; + nextPageNumber = section->currentPage; + } + + SETTINGS.orientation = orientation; + SETTINGS.saveToFile(); + + applyReaderOrientation(renderer, SETTINGS.orientation); + + section.reset(); + xSemaphoreGive(renderingMutex); +} + void EpubReaderActivity::displayTaskLoop() { while (true) { if (updateRequired) { diff --git a/src/activities/reader/EpubReaderActivity.h b/src/activities/reader/EpubReaderActivity.h index ca7c0dc9..24d58e9c 100644 --- a/src/activities/reader/EpubReaderActivity.h +++ b/src/activities/reader/EpubReaderActivity.h @@ -29,8 +29,9 @@ class EpubReaderActivity final : public ActivityWithSubactivity { int orientedMarginBottom, int orientedMarginLeft); void renderStatusBar(int orientedMarginRight, int orientedMarginBottom, int orientedMarginLeft) const; void saveProgress(int spineIndex, int currentPage, int pageCount); - void onReaderMenuBack(); + void onReaderMenuBack(uint8_t orientation); void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action); + void applyOrientation(uint8_t orientation); public: explicit EpubReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr epub, diff --git a/src/activities/reader/EpubReaderMenuActivity.cpp b/src/activities/reader/EpubReaderMenuActivity.cpp index 5ce4881d..f5de4732 100644 --- a/src/activities/reader/EpubReaderMenuActivity.cpp +++ b/src/activities/reader/EpubReaderMenuActivity.cpp @@ -56,9 +56,15 @@ void EpubReaderMenuActivity::loop() { selectedIndex = (selectedIndex + 1) % menuItems.size(); updateRequired = true; } else if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { + const auto selectedAction = menuItems[selectedIndex].action; + if (selectedAction == MenuAction::ROTATE_SCREEN) { + pendingOrientation = (pendingOrientation + 1) % orientationLabels.size(); + updateRequired = true; + return; + } + // 1. Capture the callback and action locally auto actionCallback = onAction; - auto selectedAction = menuItems[selectedIndex].action; // 2. Execute the callback actionCallback(selectedAction); @@ -66,7 +72,7 @@ void EpubReaderMenuActivity::loop() { // 3. CRITICAL: Return immediately. 'this' is likely deleted now. return; } else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { - onBack(); + onBack(pendingOrientation); return; // Also return here just in case } } @@ -93,6 +99,12 @@ void EpubReaderMenuActivity::renderScreen() { } renderer.drawText(UI_10_FONT_ID, 20, displayY, menuItems[i].label.c_str(), !isSelected); + + if (menuItems[i].action == MenuAction::ROTATE_SCREEN) { + const auto value = orientationLabels[pendingOrientation]; + const auto width = renderer.getTextWidth(UI_10_FONT_ID, value); + renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, displayY, value, !isSelected); + } } // Footer / Hints diff --git a/src/activities/reader/EpubReaderMenuActivity.h b/src/activities/reader/EpubReaderMenuActivity.h index 7f7566f6..9f25d75d 100644 --- a/src/activities/reader/EpubReaderMenuActivity.h +++ b/src/activities/reader/EpubReaderMenuActivity.h @@ -16,9 +16,12 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity { enum class MenuAction { SELECT_CHAPTER, ROTATE_SCREEN, GO_HOME, DELETE_CACHE }; explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title, - const std::function& onBack, const std::function& onAction) + const uint8_t currentOrientation, + const std::function& onBack, + const std::function& onAction) : ActivityWithSubactivity("EpubReaderMenu", renderer, mappedInput), title(title), + pendingOrientation(currentOrientation), onBack(onBack), onAction(onAction) {} @@ -33,7 +36,7 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity { }; const std::vector menuItems = {{MenuAction::SELECT_CHAPTER, "Go to Chapter"}, - {MenuAction::ROTATE_SCREEN, "Rotate Screen"}, + {MenuAction::ROTATE_SCREEN, "Reading Orientation"}, {MenuAction::GO_HOME, "Go Home"}, {MenuAction::DELETE_CACHE, "Delete Book Cache"}}; @@ -42,8 +45,10 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity { TaskHandle_t displayTaskHandle = nullptr; SemaphoreHandle_t renderingMutex = nullptr; std::string title = "Reader Menu"; + uint8_t pendingOrientation = 0; + const std::vector orientationLabels = {"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}; - const std::function onBack; + const std::function onBack; const std::function onAction; static void taskTrampoline(void* param);