From 0df16bc51f81ce47f0857f3328df7d0213921e68 Mon Sep 17 00:00:00 2001 From: Eunchurn Park Date: Fri, 26 Dec 2025 01:33:34 +0900 Subject: [PATCH] feat(home): add Continue Reading menu and long-press back for home - Add "Continue: " menu item in Home screen when a book was previously opened - Long press (1s+) BACK button while reading goes directly to home menu - Short press BACK button goes to file browser - Menu dynamically shows 3 or 4 items based on reading history --- src/activities/home/HomeActivity.cpp | 79 ++++++++++++++++---- src/activities/home/HomeActivity.h | 7 +- src/activities/reader/EpubReaderActivity.cpp | 10 ++- src/activities/reader/EpubReaderActivity.h | 8 +- src/activities/reader/ReaderActivity.cpp | 3 +- src/main.cpp | 4 +- 6 files changed, 89 insertions(+), 22 deletions(-) diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index bbda130..31b53e5 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -4,22 +4,24 @@ #include #include +#include "CrossPointState.h" #include "config.h" -namespace { -constexpr int menuItemCount = 3; -} - void HomeActivity::taskTrampoline(void* param) { auto* self = static_cast(param); self->displayTaskLoop(); } +int HomeActivity::getMenuItemCount() const { return hasContinueReading ? 4 : 3; } + void HomeActivity::onEnter() { Activity::onEnter(); renderingMutex = xSemaphoreCreateMutex(); + // Check if we have a book to continue reading + hasContinueReading = !APP_STATE.openEpubPath.empty() && SD.exists(APP_STATE.openEpubPath.c_str()); + selectorIndex = 0; // Trigger first update @@ -52,19 +54,35 @@ void HomeActivity::loop() { const bool nextPressed = inputManager.wasPressed(InputManager::BTN_DOWN) || inputManager.wasPressed(InputManager::BTN_RIGHT); + const int menuCount = getMenuItemCount(); + if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) { - if (selectorIndex == 0) { - onReaderOpen(); - } else if (selectorIndex == 1) { - onFileTransferOpen(); - } else if (selectorIndex == 2) { - onSettingsOpen(); + if (hasContinueReading) { + // Menu: Continue Reading, Browse, File transfer, Settings + if (selectorIndex == 0) { + onContinueReading(); + } else if (selectorIndex == 1) { + onReaderOpen(); + } else if (selectorIndex == 2) { + onFileTransferOpen(); + } else if (selectorIndex == 3) { + onSettingsOpen(); + } + } else { + // Menu: Browse, File transfer, Settings + if (selectorIndex == 0) { + onReaderOpen(); + } else if (selectorIndex == 1) { + onFileTransferOpen(); + } else if (selectorIndex == 2) { + onSettingsOpen(); + } } } else if (prevPressed) { - selectorIndex = (selectorIndex + menuItemCount - 1) % menuItemCount; + selectorIndex = (selectorIndex + menuCount - 1) % menuCount; updateRequired = true; } else if (nextPressed) { - selectorIndex = (selectorIndex + 1) % menuItemCount; + selectorIndex = (selectorIndex + 1) % menuCount; updateRequired = true; } } @@ -90,9 +108,40 @@ void HomeActivity::render() const { // Draw selection renderer.fillRect(0, 60 + selectorIndex * 30 + 2, pageWidth - 1, 30); - renderer.drawText(UI_FONT_ID, 20, 60, "Read", selectorIndex != 0); - renderer.drawText(UI_FONT_ID, 20, 90, "File transfer", selectorIndex != 1); - renderer.drawText(UI_FONT_ID, 20, 120, "Settings", selectorIndex != 2); + + int menuY = 60; + int menuIndex = 0; + + if (hasContinueReading) { + // Extract filename from path for display + std::string bookName = APP_STATE.openEpubPath; + const size_t lastSlash = bookName.find_last_of('/'); + if (lastSlash != std::string::npos) { + bookName = bookName.substr(lastSlash + 1); + } + // Remove .epub extension + if (bookName.length() > 5 && bookName.substr(bookName.length() - 5) == ".epub") { + bookName = bookName.substr(0, bookName.length() - 5); + } + // Truncate if too long + if (bookName.length() > 25) { + bookName = bookName.substr(0, 22) + "..."; + } + std::string continueLabel = "Continue: " + bookName; + renderer.drawText(UI_FONT_ID, 20, menuY, continueLabel.c_str(), selectorIndex != menuIndex); + menuY += 30; + menuIndex++; + } + + renderer.drawText(UI_FONT_ID, 20, menuY, "Browse", selectorIndex != menuIndex); + menuY += 30; + menuIndex++; + + renderer.drawText(UI_FONT_ID, 20, menuY, "File transfer", selectorIndex != menuIndex); + menuY += 30; + menuIndex++; + + renderer.drawText(UI_FONT_ID, 20, menuY, "Settings", selectorIndex != menuIndex); renderer.drawRect(25, pageHeight - 40, 106, 40); renderer.drawText(UI_FONT_ID, 25 + (105 - renderer.getTextWidth(UI_FONT_ID, "Back")) / 2, pageHeight - 35, "Back"); diff --git a/src/activities/home/HomeActivity.h b/src/activities/home/HomeActivity.h index 943a466..0704819 100644 --- a/src/activities/home/HomeActivity.h +++ b/src/activities/home/HomeActivity.h @@ -12,6 +12,8 @@ class HomeActivity final : public Activity { SemaphoreHandle_t renderingMutex = nullptr; int selectorIndex = 0; bool updateRequired = false; + bool hasContinueReading = false; + const std::function onContinueReading; const std::function onReaderOpen; const std::function onSettingsOpen; const std::function onFileTransferOpen; @@ -19,11 +21,14 @@ class HomeActivity final : public Activity { static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); void render() const; + int getMenuItemCount() const; public: - explicit HomeActivity(GfxRenderer& renderer, InputManager& inputManager, const std::function& onReaderOpen, + explicit HomeActivity(GfxRenderer& renderer, InputManager& inputManager, + const std::function& onContinueReading, const std::function& onReaderOpen, const std::function& onSettingsOpen, const std::function& onFileTransferOpen) : Activity("Home", renderer, inputManager), + onContinueReading(onContinueReading), onReaderOpen(onReaderOpen), onSettingsOpen(onSettingsOpen), onFileTransferOpen(onFileTransferOpen) {} diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 6195ec2..d556c6d 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -14,6 +14,7 @@ namespace { constexpr int pagesPerRefresh = 15; constexpr unsigned long skipChapterMs = 700; +constexpr unsigned long goHomeMs = 1000; constexpr float lineCompression = 0.95f; constexpr int marginTop = 8; constexpr int marginRight = 10; @@ -108,8 +109,13 @@ void EpubReaderActivity::loop() { xSemaphoreGive(renderingMutex); } - if (inputManager.wasPressed(InputManager::BTN_BACK)) { - onGoBack(); + // Long press (1s+) goes to home, short press goes to file selection + if (inputManager.wasReleased(InputManager::BTN_BACK)) { + if (inputManager.getHeldTime() >= goHomeMs) { + onGoHome(); + } else { + onGoBack(); + } return; } diff --git a/src/activities/reader/EpubReaderActivity.h b/src/activities/reader/EpubReaderActivity.h index 4edbabc..143f56b 100644 --- a/src/activities/reader/EpubReaderActivity.h +++ b/src/activities/reader/EpubReaderActivity.h @@ -17,6 +17,7 @@ class EpubReaderActivity final : public ActivityWithSubactivity { int pagesUntilFullRefresh = 0; bool updateRequired = false; const std::function onGoBack; + const std::function onGoHome; static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); @@ -26,8 +27,11 @@ class EpubReaderActivity final : public ActivityWithSubactivity { public: explicit EpubReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr epub, - const std::function& onGoBack) - : ActivityWithSubactivity("EpubReader", renderer, inputManager), epub(std::move(epub)), onGoBack(onGoBack) {} + const std::function& onGoBack, const std::function& onGoHome) + : ActivityWithSubactivity("EpubReader", renderer, inputManager), + epub(std::move(epub)), + onGoBack(onGoBack), + onGoHome(onGoHome) {} void onEnter() override; void onExit() override; void loop() override; diff --git a/src/activities/reader/ReaderActivity.cpp b/src/activities/reader/ReaderActivity.cpp index d888fb6..b9a01f0 100644 --- a/src/activities/reader/ReaderActivity.cpp +++ b/src/activities/reader/ReaderActivity.cpp @@ -46,7 +46,8 @@ void ReaderActivity::onGoToFileSelection() { void ReaderActivity::onGoToEpubReader(std::unique_ptr epub) { exitActivity(); - enterNewActivity(new EpubReaderActivity(renderer, inputManager, std::move(epub), [this] { onGoToFileSelection(); })); + enterNewActivity(new EpubReaderActivity(renderer, inputManager, std::move(epub), [this] { onGoToFileSelection(); }, + [this] { onGoBack(); })); } void ReaderActivity::onEnter() { diff --git a/src/main.cpp b/src/main.cpp index b71ea39..f3daeaa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -142,6 +142,7 @@ void onGoToReader(const std::string& initialEpubPath) { enterNewActivity(new ReaderActivity(renderer, inputManager, initialEpubPath, onGoHome)); } void onGoToReaderHome() { onGoToReader(std::string()); } +void onContinueReading() { onGoToReader(APP_STATE.openEpubPath); } void onGoToFileTransfer() { exitActivity(); @@ -155,7 +156,8 @@ void onGoToSettings() { void onGoHome() { exitActivity(); - enterNewActivity(new HomeActivity(renderer, inputManager, onGoToReaderHome, onGoToSettings, onGoToFileTransfer)); + enterNewActivity( + new HomeActivity(renderer, inputManager, onContinueReading, onGoToReaderHome, onGoToSettings, onGoToFileTransfer)); } void setupDisplayAndFonts() {