feat(home): add Continue Reading menu and long-press back for home
- Add "Continue: <book name>" 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
This commit is contained in:
parent
504c7b307d
commit
0df16bc51f
@ -4,22 +4,24 @@
|
||||
#include <InputManager.h>
|
||||
#include <SD.h>
|
||||
|
||||
#include "CrossPointState.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace {
|
||||
constexpr int menuItemCount = 3;
|
||||
}
|
||||
|
||||
void HomeActivity::taskTrampoline(void* param) {
|
||||
auto* self = static_cast<HomeActivity*>(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");
|
||||
|
||||
@ -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<void()> onContinueReading;
|
||||
const std::function<void()> onReaderOpen;
|
||||
const std::function<void()> onSettingsOpen;
|
||||
const std::function<void()> 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<void()>& onReaderOpen,
|
||||
explicit HomeActivity(GfxRenderer& renderer, InputManager& inputManager,
|
||||
const std::function<void()>& onContinueReading, const std::function<void()>& onReaderOpen,
|
||||
const std::function<void()>& onSettingsOpen, const std::function<void()>& onFileTransferOpen)
|
||||
: Activity("Home", renderer, inputManager),
|
||||
onContinueReading(onContinueReading),
|
||||
onReaderOpen(onReaderOpen),
|
||||
onSettingsOpen(onSettingsOpen),
|
||||
onFileTransferOpen(onFileTransferOpen) {}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ class EpubReaderActivity final : public ActivityWithSubactivity {
|
||||
int pagesUntilFullRefresh = 0;
|
||||
bool updateRequired = false;
|
||||
const std::function<void()> onGoBack;
|
||||
const std::function<void()> 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> epub,
|
||||
const std::function<void()>& onGoBack)
|
||||
: ActivityWithSubactivity("EpubReader", renderer, inputManager), epub(std::move(epub)), onGoBack(onGoBack) {}
|
||||
const std::function<void()>& onGoBack, const std::function<void()>& onGoHome)
|
||||
: ActivityWithSubactivity("EpubReader", renderer, inputManager),
|
||||
epub(std::move(epub)),
|
||||
onGoBack(onGoBack),
|
||||
onGoHome(onGoHome) {}
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
void loop() override;
|
||||
|
||||
@ -46,7 +46,8 @@ void ReaderActivity::onGoToFileSelection() {
|
||||
|
||||
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> 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() {
|
||||
|
||||
@ -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() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user