From 62f9a863c9e57952ca7dc90fd88e4611f096fac6 Mon Sep 17 00:00:00 2001 From: Uri Tauber Date: Tue, 27 Jan 2026 19:25:56 +0200 Subject: [PATCH] refactor: use static design for reader TOC to avoid memory fragmentation --- src/activities/reader/ChaptersTab.cpp | 153 ------------- src/activities/reader/ChaptersTab.h | 44 ---- src/activities/reader/EpubReaderActivity.h | 2 +- .../reader/EpubReaderTocActivity.cpp | 209 ++++++++++++++++-- src/activities/reader/EpubReaderTocActivity.h | 47 ++-- .../{FootnotesTab.h => FootnotesData.h} | 27 +-- src/activities/reader/FootnotesTab.cpp | 71 ------ src/activities/reader/TocTab.h | 24 -- 8 files changed, 223 insertions(+), 354 deletions(-) delete mode 100644 src/activities/reader/ChaptersTab.cpp delete mode 100644 src/activities/reader/ChaptersTab.h rename src/activities/reader/{FootnotesTab.h => FootnotesData.h} (50%) delete mode 100644 src/activities/reader/FootnotesTab.cpp delete mode 100644 src/activities/reader/TocTab.h diff --git a/src/activities/reader/ChaptersTab.cpp b/src/activities/reader/ChaptersTab.cpp deleted file mode 100644 index df47d1fc..00000000 --- a/src/activities/reader/ChaptersTab.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "ChaptersTab.h" - -#include - -#include "KOReaderCredentialStore.h" -#include "MappedInputManager.h" -#include "fontIds.h" - -namespace { -constexpr int SKIP_PAGE_MS = 700; -constexpr int LINE_HEIGHT = 30; -} // namespace - -void ChaptersTab::onEnter() { - buildFilteredChapterList(); - - selectorIndex = 0; - for (size_t i = 0; i < filteredSpineIndices.size(); i++) { - if (filteredSpineIndices[i] == currentSpineIndex) { - selectorIndex = i; - break; - } - } - - if (hasSyncOption()) { - selectorIndex += 1; - } - updateRequired = true; -} - -bool ChaptersTab::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); } - -int ChaptersTab::getTotalItems() const { - const int syncCount = hasSyncOption() ? 2 : 0; - return filteredSpineIndices.size() + syncCount; -} - -bool ChaptersTab::isSyncItem(int index) const { - if (!hasSyncOption()) return false; - return index == 0 || index == getTotalItems() - 1; -} - -int ChaptersTab::tocIndexFromItemIndex(int itemIndex) const { - const int offset = hasSyncOption() ? 1 : 0; - return itemIndex - offset; -} - -int ChaptersTab::getPageItems(int contentTop, int contentHeight) const { - int items = contentHeight / LINE_HEIGHT; - return (items < 1) ? 1 : items; -} - -void ChaptersTab::buildFilteredChapterList() { - filteredSpineIndices.clear(); - for (int i = 0; i < epub->getSpineItemsCount(); i++) { - if (epub->shouldHideFromToc(i)) continue; - int tocIndex = epub->getTocIndexForSpineIndex(i); - if (tocIndex == -1) continue; - filteredSpineIndices.push_back(i); - } -} - -void ChaptersTab::loop() { - const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Up); - const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Down); - const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS; - const int totalItems = getTotalItems(); - - if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { - if (hasSyncOption() && (selectorIndex == 0 || selectorIndex == totalItems - 1)) { - onLaunchSync(); - return; - } - - int filteredIndex = selectorIndex; - if (hasSyncOption()) filteredIndex -= 1; - - if (filteredIndex >= 0 && filteredIndex < static_cast(filteredSpineIndices.size())) { - onSelectSpineIndex(filteredSpineIndices[filteredIndex]); - } - } else if (upReleased) { - if (totalItems > 0) { - if (skipPage) { - // This logic matches MyLibraryActivity - // But for simplicity let's just do a page jump - } - selectorIndex = (selectorIndex + totalItems - 1) % totalItems; - updateRequired = true; - } - } else if (downReleased) { - if (totalItems > 0) { - selectorIndex = (selectorIndex + 1) % totalItems; - updateRequired = true; - } - } -} - -void ChaptersTab::render(int contentTop, int contentHeight) { - const auto pageWidth = renderer.getScreenWidth(); - const int pageItems = getPageItems(contentTop, contentHeight); - const int totalItems = getTotalItems(); - - const auto pageStartIndex = selectorIndex / pageItems * pageItems; - renderer.fillRect(0, contentTop + (selectorIndex % pageItems) * LINE_HEIGHT - 2, pageWidth - 1, LINE_HEIGHT); - - for (int i = 0; i < pageItems; i++) { - int itemIndex = pageStartIndex + i; - if (itemIndex >= totalItems) break; - - const int displayY = contentTop + i * LINE_HEIGHT; - const bool isSelected = (itemIndex == selectorIndex); - - if (isSyncItem(itemIndex)) { - renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected); - } else { - int filteredIndex = itemIndex; - if (hasSyncOption()) filteredIndex -= 1; - - if (filteredIndex >= 0 && filteredIndex < static_cast(filteredSpineIndices.size())) { - int spineIndex = filteredSpineIndices[filteredIndex]; - int tocIndex = epub->getTocIndexForSpineIndex(spineIndex); - - if (tocIndex == -1) { - renderer.drawText(UI_10_FONT_ID, 20, displayY, "Unnamed", !isSelected); - } else { - auto item = epub->getTocItem(tocIndex); - const int indentSize = 20 + (item.level - 1) * 15; - const std::string chapterName = - renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - indentSize); - renderer.drawText(UI_10_FONT_ID, indentSize, displayY, chapterName.c_str(), !isSelected); - } - } - } - } -} - -int ChaptersTab::getCurrentPage() const { - // We don't have enough context here to know pageItems easily without contentHeight - // For now let's just return a placeholder or calculate it if we can. - // Actually onEnter can't know the height either if it's dynamic. - // Let's assume contentTop=60, contentHeight=screenHeight-120 - const int availableHeight = renderer.getScreenHeight() - 120; - const int itemsPerPage = availableHeight / LINE_HEIGHT; - return selectorIndex / (itemsPerPage > 0 ? itemsPerPage : 1) + 1; -} - -int ChaptersTab::getTotalPages() const { - const int availableHeight = renderer.getScreenHeight() - 120; - const int itemsPerPage = availableHeight / LINE_HEIGHT; - const int totalItems = getTotalItems(); - if (totalItems == 0) return 1; - return (totalItems + itemsPerPage - 1) / (itemsPerPage > 0 ? itemsPerPage : 1); -} diff --git a/src/activities/reader/ChaptersTab.h b/src/activities/reader/ChaptersTab.h deleted file mode 100644 index ae80107a..00000000 --- a/src/activities/reader/ChaptersTab.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include - -#include -#include -#include - -#include "TocTab.h" - -class ChaptersTab final : public TocTab { - std::shared_ptr epub; - int currentSpineIndex; - int selectorIndex = 0; - bool updateRequired = false; - std::vector filteredSpineIndices; - - const std::function onSelectSpineIndex; - const std::function onLaunchSync; - - int getPageItems(int contentTop, int contentHeight) const; - int getTotalItems() const; - bool hasSyncOption() const; - bool isSyncItem(int index) const; - int tocIndexFromItemIndex(int itemIndex) const; - void buildFilteredChapterList(); - - public: - ChaptersTab(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::shared_ptr& epub, - int currentSpineIndex, std::function onSelectSpineIndex, std::function onLaunchSync) - : TocTab(renderer, mappedInput), - epub(epub), - currentSpineIndex(currentSpineIndex), - onSelectSpineIndex(onSelectSpineIndex), - onLaunchSync(onLaunchSync) {} - - void onEnter() override; - void loop() override; - void render(int contentTop, int contentHeight) override; - - int getCurrentPage() const override; - int getTotalPages() const override; - bool isUpdateRequired() const override { return updateRequired; } - void clearUpdateRequired() override { updateRequired = false; } -}; diff --git a/src/activities/reader/EpubReaderActivity.h b/src/activities/reader/EpubReaderActivity.h index 5033b896..03ccab2d 100644 --- a/src/activities/reader/EpubReaderActivity.h +++ b/src/activities/reader/EpubReaderActivity.h @@ -5,7 +5,7 @@ #include #include -#include "FootnotesTab.h" +#include "FootnotesData.h" #include "activities/ActivityWithSubactivity.h" class EpubReaderActivity final : public ActivityWithSubactivity { diff --git a/src/activities/reader/EpubReaderTocActivity.cpp b/src/activities/reader/EpubReaderTocActivity.cpp index e3369369..0fbf43b3 100644 --- a/src/activities/reader/EpubReaderTocActivity.cpp +++ b/src/activities/reader/EpubReaderTocActivity.cpp @@ -3,6 +3,7 @@ #include #include +#include "KOReaderCredentialStore.h" #include "KOReaderSyncActivity.h" #include "MappedInputManager.h" #include "ScreenComponents.h" @@ -11,6 +12,9 @@ namespace { constexpr int TAB_BAR_Y = 15; constexpr int CONTENT_START_Y = 60; +constexpr int CHAPTER_LINE_HEIGHT = 30; +constexpr int FOOTNOTE_LINE_HEIGHT = 40; +constexpr int SKIP_PAGE_MS = 700; } // namespace void EpubReaderTocActivity::taskTrampoline(void* param) { @@ -22,8 +26,21 @@ void EpubReaderTocActivity::onEnter() { ActivityWithSubactivity::onEnter(); renderingMutex = xSemaphoreCreateMutex(); - chaptersTab->onEnter(); - footnotesTab->onEnter(); + // Init chapters state + buildFilteredChapterList(); + chaptersSelectorIndex = 0; + for (size_t i = 0; i < filteredSpineIndices.size(); i++) { + if (filteredSpineIndices[i] == currentSpineIndex) { + chaptersSelectorIndex = i; + break; + } + } + if (hasSyncOption()) { + chaptersSelectorIndex += 1; + } + + // Init footnotes state + footnotesSelectedIndex = 0; updateRequired = true; xTaskCreate(&EpubReaderTocActivity::taskTrampoline, "EpubReaderTocTask", 4096, this, 1, &displayTaskHandle); @@ -44,16 +61,14 @@ void EpubReaderTocActivity::launchSyncActivity() { xSemaphoreTake(renderingMutex, portMAX_DELAY); exitActivity(); enterNewActivity(new KOReaderSyncActivity( - renderer, mappedInput, epub, epubPath, currentSpineIndex, currentPage, totalPagesInSpine, + renderer, mappedInput, this->epub, epubPath, currentSpineIndex, currentPage, totalPagesInSpine, [this]() { - // On cancel exitActivity(); - updateRequired = true; + this->updateRequired = true; }, [this](int newSpineIndex, int newPage) { - // On sync complete exitActivity(); - onSyncPosition(newSpineIndex, newPage); + this->onSyncPosition(newSpineIndex, newPage); })); xSemaphoreGive(renderingMutex); } @@ -83,8 +98,65 @@ void EpubReaderTocActivity::loop() { return; } - getCurrentTab()->loop(); - if (getCurrentTab()->isUpdateRequired()) { + if (currentTab == Tab::CHAPTERS) { + loopChapters(); + } else { + loopFootnotes(); + } +} + +void EpubReaderTocActivity::loopChapters() { + const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Up); + const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Down); + const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS; + const int totalItems = getChaptersTotalItems(); + + if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { + if (hasSyncOption() && (chaptersSelectorIndex == 0 || chaptersSelectorIndex == totalItems - 1)) { + launchSyncActivity(); + return; + } + + int filteredIndex = chaptersSelectorIndex; + if (hasSyncOption()) filteredIndex -= 1; + + if (filteredIndex >= 0 && filteredIndex < static_cast(filteredSpineIndices.size())) { + onSelectSpineIndex(filteredSpineIndices[filteredIndex]); + } + } else if (upReleased) { + if (totalItems > 0) { + chaptersSelectorIndex = (chaptersSelectorIndex + totalItems - 1) % totalItems; + updateRequired = true; + } + } else if (downReleased) { + if (totalItems > 0) { + chaptersSelectorIndex = (chaptersSelectorIndex + 1) % totalItems; + updateRequired = true; + } + } +} + +void EpubReaderTocActivity::loopFootnotes() { + bool needsRedraw = false; + if (mappedInput.wasPressed(MappedInputManager::Button::Up)) { + if (footnotesSelectedIndex > 0) { + footnotesSelectedIndex--; + needsRedraw = true; + } + } + if (mappedInput.wasPressed(MappedInputManager::Button::Down)) { + if (footnotesSelectedIndex < footnotes.getCount() - 1) { + footnotesSelectedIndex++; + needsRedraw = true; + } + } + if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { + const FootnoteEntry* entry = footnotes.getEntry(footnotesSelectedIndex); + if (entry) { + onSelectFootnote(entry->href); + } + } + if (needsRedraw) { updateRequired = true; } } @@ -104,29 +176,128 @@ void EpubReaderTocActivity::displayTaskLoop() { void EpubReaderTocActivity::renderScreen() { renderer.clearScreen(); - // Draw tab bar std::vector tabs = {{"Chapters", currentTab == Tab::CHAPTERS}, {"Footnotes", currentTab == Tab::FOOTNOTES}}; ScreenComponents::drawTabBar(renderer, TAB_BAR_Y, tabs); const int screenHeight = renderer.getScreenHeight(); const int contentHeight = screenHeight - CONTENT_START_Y - 60; - getCurrentTab()->render(CONTENT_START_Y, contentHeight); + if (currentTab == Tab::CHAPTERS) { + renderChapters(CONTENT_START_Y, contentHeight); + } else { + renderFootnotes(CONTENT_START_Y, contentHeight); + } - // Draw scroll indicator - ScreenComponents::drawScrollIndicator(renderer, getCurrentTab()->getCurrentPage(), getCurrentTab()->getTotalPages(), - CONTENT_START_Y, contentHeight); + ScreenComponents::drawScrollIndicator(renderer, getCurrentPage(), getTotalPages(), CONTENT_START_Y, contentHeight); - // Draw button hints const auto labels = mappedInput.mapLabels("« Back", "Select", "< Tab", "Tab >"); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); - renderer.drawSideButtonHints(UI_10_FONT_ID, ">", "<"); renderer.displayBuffer(); } -TocTab* EpubReaderTocActivity::getCurrentTab() const { - return (currentTab == Tab::CHAPTERS) ? static_cast(chaptersTab.get()) - : static_cast(footnotesTab.get()); +void EpubReaderTocActivity::renderChapters(int contentTop, int contentHeight) { + const auto pageWidth = renderer.getScreenWidth(); + const int pageItems = getChaptersPageItems(contentHeight); + const int totalItems = getChaptersTotalItems(); + const auto pageStartIndex = chaptersSelectorIndex / pageItems * pageItems; + + renderer.fillRect(0, contentTop + (chaptersSelectorIndex % pageItems) * CHAPTER_LINE_HEIGHT - 2, pageWidth - 1, + CHAPTER_LINE_HEIGHT); + + for (int i = 0; i < pageItems; i++) { + int itemIndex = pageStartIndex + i; + if (itemIndex >= totalItems) break; + + const int displayY = contentTop + i * CHAPTER_LINE_HEIGHT; + const bool isSelected = (itemIndex == chaptersSelectorIndex); + + if (isSyncItem(itemIndex)) { + renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected); + } else { + int filteredIndex = itemIndex; + if (hasSyncOption()) filteredIndex -= 1; + + if (filteredIndex >= 0 && filteredIndex < static_cast(filteredSpineIndices.size())) { + int spineIndex = filteredSpineIndices[filteredIndex]; + int tocIndex = this->epub->getTocIndexForSpineIndex(spineIndex); + if (tocIndex == -1) { + renderer.drawText(UI_10_FONT_ID, 20, displayY, "Unnamed", !isSelected); + } else { + auto item = this->epub->getTocItem(tocIndex); + const int indentSize = 20 + (item.level - 1) * 15; + const std::string chapterName = + renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - indentSize); + renderer.drawText(UI_10_FONT_ID, indentSize, displayY, chapterName.c_str(), !isSelected); + } + } + } + } +} + +void EpubReaderTocActivity::renderFootnotes(int contentTop, int contentHeight) { + const int marginLeft = 20; + if (footnotes.getCount() == 0) { + renderer.drawText(SMALL_FONT_ID, marginLeft, contentTop + 20, "No footnotes on this page"); + return; + } + for (int i = 0; i < footnotes.getCount(); i++) { + const FootnoteEntry* entry = footnotes.getEntry(i); + if (!entry) continue; + const int y = contentTop + i * FOOTNOTE_LINE_HEIGHT; + if (i == footnotesSelectedIndex) { + renderer.drawText(UI_12_FONT_ID, marginLeft - 10, y, ">", EpdFontFamily::BOLD); + renderer.drawText(UI_12_FONT_ID, marginLeft + 10, y, entry->number, EpdFontFamily::BOLD); + } else { + renderer.drawText(UI_12_FONT_ID, marginLeft + 10, y, entry->number); + } + } +} + +void EpubReaderTocActivity::buildFilteredChapterList() { + filteredSpineIndices.clear(); + for (int i = 0; i < this->epub->getSpineItemsCount(); i++) { + if (this->epub->shouldHideFromToc(i)) continue; + int tocIndex = this->epub->getTocIndexForSpineIndex(i); + if (tocIndex == -1) continue; + filteredSpineIndices.push_back(i); + } +} + +bool EpubReaderTocActivity::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); } + +bool EpubReaderTocActivity::isSyncItem(int index) const { + if (!hasSyncOption()) return false; + return index == 0 || index == getChaptersTotalItems() - 1; +} + +int EpubReaderTocActivity::getChaptersTotalItems() const { + const int syncCount = hasSyncOption() ? 2 : 0; + return filteredSpineIndices.size() + syncCount; +} + +int EpubReaderTocActivity::getChaptersPageItems(int contentHeight) const { + int items = contentHeight / CHAPTER_LINE_HEIGHT; + return (items < 1) ? 1 : items; +} + +int EpubReaderTocActivity::getCurrentPage() const { + if (currentTab == Tab::CHAPTERS) { + const int availableHeight = renderer.getScreenHeight() - 120; + const int itemsPerPage = availableHeight / CHAPTER_LINE_HEIGHT; + return chaptersSelectorIndex / (itemsPerPage > 0 ? itemsPerPage : 1) + 1; + } + return 1; +} + +int EpubReaderTocActivity::getTotalPages() const { + if (currentTab == Tab::CHAPTERS) { + const int availableHeight = renderer.getScreenHeight() - 120; + const int itemsPerPage = availableHeight / CHAPTER_LINE_HEIGHT; + const int totalItems = getChaptersTotalItems(); + if (totalItems == 0) return 1; + return (totalItems + itemsPerPage - 1) / (itemsPerPage > 0 ? itemsPerPage : 1); + } + return 1; } diff --git a/src/activities/reader/EpubReaderTocActivity.h b/src/activities/reader/EpubReaderTocActivity.h index fce847a4..71a0f0fb 100644 --- a/src/activities/reader/EpubReaderTocActivity.h +++ b/src/activities/reader/EpubReaderTocActivity.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -7,8 +8,7 @@ #include #include "../ActivityWithSubactivity.h" -#include "ChaptersTab.h" -#include "FootnotesTab.h" +#include "FootnotesData.h" class EpubReaderTocActivity final : public ActivityWithSubactivity { public: @@ -27,11 +27,16 @@ class EpubReaderTocActivity final : public ActivityWithSubactivity { int totalPagesInSpine = 0; Tab currentTab = Tab::CHAPTERS; - std::unique_ptr chaptersTab; - std::unique_ptr footnotesTab; - bool updateRequired = false; + // Chapters tab state + int chaptersSelectorIndex = 0; + std::vector filteredSpineIndices; + + // Footnotes tab state + int footnotesSelectedIndex = 0; + + // Callbacks const std::function onGoBack; const std::function onSelectSpineIndex; const std::function onSelectFootnote; @@ -40,16 +45,33 @@ class EpubReaderTocActivity final : public ActivityWithSubactivity { static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); void renderScreen(); - TocTab* getCurrentTab() const; + + // Tab-specific methods + void loopChapters(); + void loopFootnotes(); + void renderChapters(int contentTop, int contentHeight); + void renderFootnotes(int contentTop, int contentHeight); + + // Chapters helpers + void buildFilteredChapterList(); + bool hasSyncOption() const; + bool isSyncItem(int index) const; + int getChaptersTotalItems() const; + int getChaptersPageItems(int contentHeight) const; + int tocIndexFromItemIndex(int itemIndex) const; + + // Indicator helpers + int getCurrentPage() const; + int getTotalPages() const; public: - EpubReaderTocActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::shared_ptr& epub, + EpubReaderTocActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::shared_ptr& epub_ptr, const std::string& epubPath, int currentSpineIndex, int currentPage, int totalPagesInSpine, const FootnotesData& footnotes, std::function onGoBack, std::function onSelectSpineIndex, std::function onSelectFootnote, std::function onSyncPosition) : ActivityWithSubactivity("EpubReaderToc", renderer, mappedInput), - epub(epub), + epub(epub_ptr), epubPath(epubPath), currentSpineIndex(currentSpineIndex), currentPage(currentPage), @@ -58,14 +80,7 @@ class EpubReaderTocActivity final : public ActivityWithSubactivity { onGoBack(onGoBack), onSelectSpineIndex(onSelectSpineIndex), onSelectFootnote(onSelectFootnote), - onSyncPosition(onSyncPosition) { - chaptersTab = std::unique_ptr(new ChaptersTab( - renderer, mappedInput, epub, currentSpineIndex, - [this](int spineIndex) { this->onSelectSpineIndex(spineIndex); }, - [this]() { this->launchSyncActivity(); })); - footnotesTab = std::unique_ptr(new FootnotesTab( - renderer, mappedInput, footnotes, [this](const char* href) { this->onSelectFootnote(href); })); - } + onSyncPosition(onSyncPosition) {} void onEnter() override; void onExit() override; diff --git a/src/activities/reader/FootnotesTab.h b/src/activities/reader/FootnotesData.h similarity index 50% rename from src/activities/reader/FootnotesTab.h rename to src/activities/reader/FootnotesData.h index fb5f5856..9a35c40d 100644 --- a/src/activities/reader/FootnotesTab.h +++ b/src/activities/reader/FootnotesData.h @@ -1,9 +1,6 @@ #pragma once #include -#include - -#include "../../lib/Epub/Epub/FootnoteEntry.h" -#include "TocTab.h" +#include class FootnotesData { private: @@ -45,25 +42,3 @@ class FootnotesData { return nullptr; } }; - -class FootnotesTab final : public TocTab { - const FootnotesData& footnotes; - int selectedIndex = 0; - bool updateRequired = false; - - const std::function onSelectFootnote; - - public: - FootnotesTab(GfxRenderer& renderer, MappedInputManager& mappedInput, const FootnotesData& footnotes, - std::function onSelectFootnote) - : TocTab(renderer, mappedInput), footnotes(footnotes), onSelectFootnote(onSelectFootnote) {} - - void onEnter() override; - void loop() override; - void render(int contentTop, int contentHeight) override; - - int getCurrentPage() const override; - int getTotalPages() const override; - bool isUpdateRequired() const override { return updateRequired; } - void clearUpdateRequired() override { updateRequired = false; } -}; diff --git a/src/activities/reader/FootnotesTab.cpp b/src/activities/reader/FootnotesTab.cpp deleted file mode 100644 index d374aca8..00000000 --- a/src/activities/reader/FootnotesTab.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "FootnotesTab.h" - -#include -#include - -#include "MappedInputManager.h" -#include "fontIds.h" - -namespace { -constexpr int LINE_HEIGHT = 40; -} - -void FootnotesTab::onEnter() { - selectedIndex = 0; - updateRequired = true; -} - -void FootnotesTab::loop() { - bool needsRedraw = false; - - if (mappedInput.wasPressed(MappedInputManager::Button::Up)) { - if (selectedIndex > 0) { - selectedIndex--; - needsRedraw = true; - } - } - - if (mappedInput.wasPressed(MappedInputManager::Button::Down)) { - if (selectedIndex < footnotes.getCount() - 1) { - selectedIndex++; - needsRedraw = true; - } - } - - if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { - const FootnoteEntry* entry = footnotes.getEntry(selectedIndex); - if (entry) { - onSelectFootnote(entry->href); - } - } - - if (needsRedraw) { - updateRequired = true; - } -} - -void FootnotesTab::render(int contentTop, int contentHeight) { - const int marginLeft = 20; - - if (footnotes.getCount() == 0) { - renderer.drawText(SMALL_FONT_ID, marginLeft, contentTop + 20, "No footnotes on this page"); - return; - } - - for (int i = 0; i < footnotes.getCount(); i++) { - const FootnoteEntry* entry = footnotes.getEntry(i); - if (!entry) continue; - - const int y = contentTop + i * LINE_HEIGHT; - - if (i == selectedIndex) { - renderer.drawText(UI_12_FONT_ID, marginLeft - 10, y, ">", EpdFontFamily::BOLD); - renderer.drawText(UI_12_FONT_ID, marginLeft + 10, y, entry->number, EpdFontFamily::BOLD); - } else { - renderer.drawText(UI_12_FONT_ID, marginLeft + 10, y, entry->number); - } - } -} - -int FootnotesTab::getCurrentPage() const { return 1; } -int FootnotesTab::getTotalPages() const { return 1; } diff --git a/src/activities/reader/TocTab.h b/src/activities/reader/TocTab.h deleted file mode 100644 index 37deb969..00000000 --- a/src/activities/reader/TocTab.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -class GfxRenderer; -class MappedInputManager; - -class TocTab { - protected: - GfxRenderer& renderer; - MappedInputManager& mappedInput; - - public: - TocTab(GfxRenderer& renderer, MappedInputManager& mappedInput) : renderer(renderer), mappedInput(mappedInput) {} - virtual ~TocTab() = default; - - virtual void onEnter() = 0; - virtual void onExit() {} - virtual void loop() = 0; - virtual void render(int contentTop, int contentHeight) = 0; - - virtual int getCurrentPage() const = 0; - virtual int getTotalPages() const = 0; - virtual bool isUpdateRequired() const = 0; - virtual void clearUpdateRequired() = 0; -};