#pragma once #include #include #include #include #include #include "CrossPointSettings.h" #include "activities/ActivityWithSubactivity.h" class TxtReaderActivity final : public ActivityWithSubactivity { std::unique_ptr txt; TaskHandle_t displayTaskHandle = nullptr; SemaphoreHandle_t renderingMutex = nullptr; int currentPage = 0; int totalPages = 1; int pagesUntilFullRefresh = 0; bool updateRequired = false; const std::function onGoBack; const std::function onGoHome; // End-of-book prompt state bool showingEndOfBookPrompt = false; int endOfBookSelection = 2; // 0=Archive, 1=Delete, 2=Keep // Streaming text reader - stores file offsets for each page std::vector pageOffsets; // File offset for start of each page std::vector currentPageLines; int linesPerPage = 0; int viewportWidth = 0; bool initialized = false; // Cached settings for cache validation (different fonts/margins require re-indexing) int cachedFontId = 0; int cachedScreenMargin = 0; uint8_t cachedParagraphAlignment = CrossPointSettings::LEFT_ALIGN; static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); void renderScreen(); void renderPage(); void renderStatusBar(int orientedMarginRight, int orientedMarginBottom, int orientedMarginLeft) const; void renderEndOfBookPrompt(); void handleEndOfBookAction(); void initializeReader(); bool loadPageAtOffset(size_t offset, std::vector& outLines, size_t& nextOffset); void buildPageIndex(); bool loadPageIndexCache(); void savePageIndexCache() const; void saveProgress() const; void loadProgress(); int findPageForOffset(size_t targetOffset) const; public: explicit TxtReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr txt, const std::function& onGoBack, const std::function& onGoHome) : ActivityWithSubactivity("TxtReader", renderer, mappedInput), txt(std::move(txt)), onGoBack(onGoBack), onGoHome(onGoHome) {} void onEnter() override; void onExit() override; void loop() override; };