Something went wrong. Try again.
A fork of https://github.com/crosspoint-reader/crosspoint-reader
Something went wrong. Try again.
6.1 kB · 165 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166#include "EpubReaderChapterSelectionActivity.h"
#include <FontCacheManager.h>#include <GfxRenderer.h>#include <I18n.h>
#include <string>
#include "MappedInputManager.h"#include "components/UIScale.h"#include "components/UITheme.h"
namespace fui = freeink::ui;
EpubReaderChapterSelectionActivity::EpubReaderChapterSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::shared_ptr<Epub>& epub, const int currentSpineIndex) : UiListActivity("EpubReaderChapterSelection", renderer, mappedInput), epub(epub), currentSpineIndex(currentSpineIndex) {}
void EpubReaderChapterSelectionActivity::onEnter() { UiListActivity::onEnter();
// The reader underneath pins its page-render glyph arenas while this // overlay is up. clearCache() is heap-adaptive: below the retention floor // it frees them (the next page render's PrewarmScope rebuilds them at // normal page-turn cost), giving this list room to keep every row's // fallback glyphs resident — otherwise each repaint re-reads the visible // rows' glyphs from SD. if (auto* fcm = renderer.getFontCacheManager()) { fcm->clearCache(); }
if (!epub) { return; }
// Start with the current chapter at the top of the viewport; the first // screen build pulls the viewport to it (ListNav follow-on-build) and // materializes the row window there (refreshTocWindow in buildScreen). int tocIndex = epub->getTocIndexForSpineIndex(currentSpineIndex); if (tocIndex == -1) { tocIndex = 0; } nav.selected = tocIndex;}
// Materialize the ListItem/label window starting at `start` (clamped). TOC// entries are SD LUT reads (getTocItem), so this runs only when the viewport// leaves the current window. Finishes with a batch prewarm of the window's// CJK fallback glyphs -- one bounded SD pass per list page; repaints inside// the window stay RAM-only.void EpubReaderChapterSelectionActivity::refreshTocWindow(const int start) { const int total = listCount(); int clamped = start; if (clamped > total - TOC_WINDOW) clamped = total - TOC_WINDOW; if (clamped < 0) clamped = 0; if (clamped == windowStart) return;
windowCount = total - clamped < TOC_WINDOW ? total - clamped : TOC_WINDOW; for (int i = 0; i < windowCount; i++) { const auto tocItem = epub->getTocItem(clamped + i); std::string indent(tocItem.level > 0 ? (tocItem.level - 1) * 2 : 0, ' '); windowLabels[i] = indent + tocItem.title; fui::ListItem item; item.label = windowLabels[i].c_str(); item.actionValue = static_cast<int16_t>(clamped + i); windowItems[i] = item; } windowStart = clamped;
struct PrewarmCtx { const std::string* labels; int count; } prewarmCtx{windowLabels, windowCount}; renderer.prewarmFallbackText( uiScaleSpec().bodyFontId, [](const void* ctx, uint32_t i) -> const char* { const auto* c = static_cast<const PrewarmCtx*>(ctx); return i < static_cast<uint32_t>(c->count) ? c->labels[i].c_str() : nullptr; }, &prewarmCtx, static_cast<uint32_t>(windowCount));}
void EpubReaderChapterSelectionActivity::activateIndex(const int index) { if (index < 0 || index >= listCount()) { return; } // The activated row leaves this screen (finish); a lingering flash would gray // an unrelated element on the next render. app.clearTapFlash(); nav.selected = index; const auto tocItem = epub->getTocItem(index); if (tocItem.spineIndex == -1) { ActivityResult result; result.isCancelled = true; setResult(std::move(result)); finish(); } else { setResult(ChapterResult{tocItem.spineIndex, tocItem.anchor}); finish(); }}
bool EpubReaderChapterSelectionActivity::handleButtons() { if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { ActivityResult result; result.isCancelled = true; setResult(std::move(result)); finish(); return true; }
if (!epub) { return true; }
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { activateIndex(nav.selected); return true; }
return false;}
void EpubReaderChapterSelectionActivity::buildScreen(UiScreen& screen) { const auto& metrics = UITheme::getInstance().getMetrics(); const Rect safe = UITheme::getInstance().getScreenSafeArea(renderer, true, false); // Content: the safe area minus the header band drawChrome paints the title in. screen.setContentMarginFromScreen(fui::Insets{ static_cast<int16_t>(safe.y + metrics.topPadding + metrics.headerHeight), static_cast<int16_t>(renderer.getScreenWidth() - (safe.x + safe.width)), static_cast<int16_t>(renderer.getScreenHeight() - (safe.y + safe.height)), static_cast<int16_t>(safe.x)}); screen.spacer(static_cast<int16_t>(metrics.verticalSpacing));
if (!epub) { return; } if (listCount() == 0) { screen.centeredText(tr(STR_NO_CHAPTERS), screen.theme().bodyText); return; }
fui::ListProps props; props.count = static_cast<uint16_t>(listCount()); props.action = ACTION_ROW; props.inputMask = fui::InputTouch; // physical buttons stay in loop() syncListViewport(screen, props); // Materialize the row window for the final viewport (syncListViewport just // applied follow/clamping to nav.top) and hand list() the window with its // absolute base index. refreshTocWindow(nav.top); props.items = windowItems; props.itemsWindowFirst = static_cast<uint16_t>(windowStart); screen.list(props);}
void EpubReaderChapterSelectionActivity::drawChrome() { const auto& metrics = UITheme::getInstance().getMetrics(); const Rect safe = UITheme::getInstance().getScreenSafeArea(renderer, true, false); GUI.drawHeader(renderer, Rect{safe.x, safe.y + metrics.topPadding, safe.width, metrics.headerHeight}, tr(STR_SELECT_CHAPTER));}