#pragma once #include #include #include #include #include #include #include #include "../ActivityWithSubactivity.h" #include "util/BookSettings.h" #include "util/ButtonNavigator.h" class EpubReaderMenuActivity final : public ActivityWithSubactivity { public: // Menu actions available from the reader menu. enum class MenuAction { ADD_BOOKMARK, REMOVE_BOOKMARK, LOOKUP, LOOKED_UP_WORDS, ROTATE_SCREEN, LETTERBOX_FILL, SELECT_CHAPTER, GO_TO_BOOKMARK, GO_TO_PERCENT, GO_HOME, SYNC, DELETE_CACHE, DELETE_DICT_CACHE }; explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title, const int currentPage, const int totalPages, const int bookProgressPercent, const uint8_t currentOrientation, const bool hasDictionary, const bool isBookmarked, const std::string& bookCachePath, const std::function& onBack, const std::function& onAction) : ActivityWithSubactivity("EpubReaderMenu", renderer, mappedInput), menuItems(buildMenuItems(hasDictionary, isBookmarked)), title(title), pendingOrientation(currentOrientation), bookCachePath(bookCachePath), currentPage(currentPage), totalPages(totalPages), bookProgressPercent(bookProgressPercent), onBack(onBack), onAction(onAction) { // Load per-book settings to initialize the letterbox fill override auto bookSettings = BookSettings::load(bookCachePath); pendingLetterboxFill = bookSettings.letterboxFillOverride; } void onEnter() override; void onExit() override; void loop() override; private: struct MenuItem { MenuAction action; std::string label; }; std::vector menuItems; int selectedIndex = 0; bool updateRequired = false; TaskHandle_t displayTaskHandle = nullptr; SemaphoreHandle_t renderingMutex = nullptr; ButtonNavigator buttonNavigator; std::string title = "Reader Menu"; uint8_t pendingOrientation = 0; const std::vector orientationLabels = {"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}; std::string bookCachePath; // Letterbox fill override: 0xFF = Default (use global), 0 = Dithered, 1 = Solid, 2 = None uint8_t pendingLetterboxFill = BookSettings::USE_GLOBAL; static constexpr int LETTERBOX_FILL_OPTION_COUNT = 4; // Default + 3 modes const std::vector letterboxFillLabels = {"Default", "Dithered", "Solid", "None"}; int currentPage = 0; int totalPages = 0; int bookProgressPercent = 0; const std::function onBack; const std::function onAction; // Map the internal override value to an index into letterboxFillLabels. int letterboxFillToIndex() const { if (pendingLetterboxFill == BookSettings::USE_GLOBAL) return 0; // "Default" return pendingLetterboxFill + 1; // 0->1 (Dithered), 1->2 (Solid), 2->3 (None) } // Map an index from letterboxFillLabels back to an override value. static uint8_t indexToLetterboxFill(int index) { if (index == 0) return BookSettings::USE_GLOBAL; return static_cast(index - 1); } // Save the current letterbox fill override to the book's settings file. void saveLetterboxFill() const { auto bookSettings = BookSettings::load(bookCachePath); bookSettings.letterboxFillOverride = pendingLetterboxFill; BookSettings::save(bookCachePath, bookSettings); } static std::vector buildMenuItems(bool hasDictionary, bool isBookmarked) { std::vector items; if (isBookmarked) { items.push_back({MenuAction::REMOVE_BOOKMARK, "Remove Bookmark"}); } else { items.push_back({MenuAction::ADD_BOOKMARK, "Add Bookmark"}); } if (hasDictionary) { items.push_back({MenuAction::LOOKUP, "Lookup Word"}); items.push_back({MenuAction::LOOKED_UP_WORDS, "Lookup Word History"}); } items.push_back({MenuAction::ROTATE_SCREEN, "Reading Orientation"}); items.push_back({MenuAction::LETTERBOX_FILL, "Letterbox Fill"}); items.push_back({MenuAction::SELECT_CHAPTER, "Table of Contents"}); items.push_back({MenuAction::GO_TO_BOOKMARK, "Go to Bookmark"}); items.push_back({MenuAction::GO_TO_PERCENT, "Go to %"}); items.push_back({MenuAction::GO_HOME, "Close Book"}); items.push_back({MenuAction::SYNC, "Sync Progress"}); items.push_back({MenuAction::DELETE_CACHE, "Delete Book Cache"}); if (hasDictionary) { items.push_back({MenuAction::DELETE_DICT_CACHE, "Delete Dictionary Cache"}); } return items; } static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); void renderScreen(); };