Movable status bar and dual-edge progress bars
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_system.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
@@ -111,6 +112,27 @@ const char* orientationToString(const GfxRenderer::Orientation orientation) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
int getImageOnlyPageYOffset(const Page& page, const int viewportHeight) {
|
||||
if (viewportHeight <= 0 || page.elements.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const bool imageOnlyPage = std::all_of(
|
||||
page.elements.begin(), page.elements.end(),
|
||||
[](const std::shared_ptr<PageElement>& element) { return element && element->getTag() == TAG_PageImage; });
|
||||
if (!imageOnlyPage) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t imgX, imgY, imgW, imgH;
|
||||
if (!page.getImageBoundingBox(imgX, imgY, imgW, imgH) || imgH >= viewportHeight) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int centeredTop = (viewportHeight - imgH) / 2;
|
||||
return std::max(0, centeredTop - static_cast<int>(imgY));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void EpubReaderActivity::onEnter() {
|
||||
@@ -241,7 +263,7 @@ void EpubReaderActivity::loop() {
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
buttonEvents.drain();
|
||||
automaticPageTurnActive = false;
|
||||
stopAutomaticPageTurn();
|
||||
// updates chapter title space to indicate page turn disabled
|
||||
requestUpdate();
|
||||
return;
|
||||
@@ -1009,9 +1031,30 @@ void EpubReaderActivity::applyTextDarkness(const uint8_t textDarkness) {
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
void EpubReaderActivity::stopAutomaticPageTurn() {
|
||||
if (!automaticPageTurnActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
automaticPageTurnActive = false;
|
||||
|
||||
if (UITheme::getStatusBarHeight(true) == UITheme::getStatusBarHeight()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Preserve current reading position so we can restore after reflow.
|
||||
RenderLock lock(*this);
|
||||
if (section) {
|
||||
cachedSpineIndex = currentSpineIndex;
|
||||
cachedChapterTotalPageCount = section->pageCount;
|
||||
nextPageNumber = section->currentPage;
|
||||
}
|
||||
section.reset();
|
||||
}
|
||||
|
||||
void EpubReaderActivity::toggleAutoPageTurn(const uint8_t selectedPageTurnOption) {
|
||||
if (selectedPageTurnOption == 0 || selectedPageTurnOption >= std::size(PAGE_TURN_LABELS)) {
|
||||
automaticPageTurnActive = false;
|
||||
stopAutomaticPageTurn();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1020,9 +1063,8 @@ void EpubReaderActivity::toggleAutoPageTurn(const uint8_t selectedPageTurnOption
|
||||
pageTurnDuration = (1UL * 60 * 1000) / PAGE_TURN_LABELS[selectedPageTurnOption];
|
||||
automaticPageTurnActive = true;
|
||||
|
||||
const uint8_t statusBarHeight = UITheme::getInstance().getStatusBarHeight();
|
||||
// resets cached section so that space is reserved for auto page turn indicator when None or progress bar only
|
||||
if (statusBarHeight == 0 || statusBarHeight == UITheme::getInstance().getProgressBarHeight()) {
|
||||
// Reset cached section when automatic page turn adds a forced status item band.
|
||||
if (UITheme::getStatusBarHeight(true) != UITheme::getStatusBarHeight()) {
|
||||
// Preserve current reading position so we can restore after reflow.
|
||||
RenderLock lock(*this);
|
||||
if (section) {
|
||||
@@ -1223,21 +1265,13 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
int orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft;
|
||||
renderer.getOrientedViewableTRBL(&orientedMarginTop, &orientedMarginRight, &orientedMarginBottom,
|
||||
&orientedMarginLeft);
|
||||
orientedMarginTop += SETTINGS.screenMargin;
|
||||
const int statusBarTopHeight = UITheme::getStatusBarTopHeight(automaticPageTurnActive);
|
||||
const int statusBarBottomHeight = UITheme::getStatusBarBottomHeight(automaticPageTurnActive);
|
||||
|
||||
orientedMarginTop += std::max(static_cast<int>(SETTINGS.screenMargin), statusBarTopHeight);
|
||||
orientedMarginLeft += SETTINGS.screenMargin;
|
||||
orientedMarginRight += SETTINGS.screenMargin;
|
||||
|
||||
const uint8_t statusBarHeight = UITheme::getInstance().getStatusBarHeight();
|
||||
|
||||
// reserves space for automatic page turn indicator when no status bar or progress bar only
|
||||
if (automaticPageTurnActive &&
|
||||
(statusBarHeight == 0 || statusBarHeight == UITheme::getInstance().getProgressBarHeight())) {
|
||||
orientedMarginBottom +=
|
||||
std::max(SETTINGS.screenMargin,
|
||||
static_cast<uint8_t>(statusBarHeight + UITheme::getInstance().getMetrics().statusBarVerticalMargin));
|
||||
} else {
|
||||
orientedMarginBottom += std::max(SETTINGS.screenMargin, statusBarHeight);
|
||||
}
|
||||
orientedMarginBottom += std::max(static_cast<int>(SETTINGS.screenMargin), statusBarBottomHeight);
|
||||
|
||||
const uint16_t viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
|
||||
const uint16_t viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
|
||||
@@ -1485,10 +1519,13 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
fcm->resetStats();
|
||||
logReaderMemSnapshot("prewarm_begin");
|
||||
|
||||
const int viewportHeight = std::max(0, renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom);
|
||||
const int contentTop = orientedMarginTop + getImageOnlyPageYOffset(*page, viewportHeight);
|
||||
|
||||
// Font prewarm: scan pass accumulates text, then prewarm, then real render
|
||||
const uint32_t heapBefore = esp_get_free_heap_size();
|
||||
auto scope = fcm->createPrewarmScope();
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, orientedMarginTop); // scan pass
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop); // scan pass
|
||||
scope.endScanAndPrewarm();
|
||||
const uint32_t heapAfter = esp_get_free_heap_size();
|
||||
fcm->logStats("prewarm");
|
||||
@@ -1506,7 +1543,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
lastRenderStats.forcedHalfRefresh = forceHalfRefreshThisPage;
|
||||
|
||||
logReaderMemSnapshot("before_bw_render");
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop);
|
||||
renderStatusBar();
|
||||
fcm->logStats("bw_render");
|
||||
const auto tBwRender = millis();
|
||||
@@ -1520,12 +1557,12 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
// Step 2: Re-render with images and display again (images appear clean)
|
||||
int16_t imgX, imgY, imgW, imgH;
|
||||
if (page->getImageBoundingBox(imgX, imgY, imgW, imgH)) {
|
||||
renderer.fillRect(imgX + orientedMarginLeft, imgY + orientedMarginTop, imgW, imgH, false);
|
||||
renderer.fillRect(imgX + orientedMarginLeft, imgY + contentTop, imgW, imgH, false);
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
|
||||
// Re-render page content to restore images into the blanked area
|
||||
// Status bar is not re-rendered here to avoid reading stale dynamic values (e.g. battery %)
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop);
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
} else {
|
||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||
@@ -1558,7 +1595,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
logReaderMemSnapshot("gray_lsb_begin");
|
||||
renderer.clearScreen(0x00);
|
||||
renderer.setRenderMode(GfxRenderer::GRAYSCALE_LSB);
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop);
|
||||
renderer.copyGrayscaleLsbBuffers();
|
||||
const auto tGrayLsb = millis();
|
||||
logReaderMemSnapshot("gray_lsb_end");
|
||||
@@ -1567,7 +1604,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
logReaderMemSnapshot("gray_msb_begin");
|
||||
renderer.clearScreen(0x00);
|
||||
renderer.setRenderMode(GfxRenderer::GRAYSCALE_MSB);
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop);
|
||||
renderer.copyGrayscaleMsbBuffers();
|
||||
const auto tGrayMsb = millis();
|
||||
logReaderMemSnapshot("gray_msb_end");
|
||||
@@ -1638,16 +1675,9 @@ void EpubReaderActivity::renderStatusBar() const {
|
||||
const float bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg) * 100;
|
||||
|
||||
std::string title;
|
||||
int textYOffset = 0;
|
||||
|
||||
if (automaticPageTurnActive) {
|
||||
title = tr(STR_AUTO_TURN_ENABLED) + std::to_string(60 * 1000 / pageTurnDuration);
|
||||
|
||||
const uint8_t statusBarHeight = UITheme::getInstance().getStatusBarHeight();
|
||||
if (statusBarHeight == 0 || statusBarHeight == UITheme::getInstance().getProgressBarHeight()) {
|
||||
textYOffset += UITheme::getInstance().getMetrics().statusBarVerticalMargin;
|
||||
}
|
||||
|
||||
} else if (SETTINGS.statusBarTitle == CrossPointSettings::STATUS_BAR_TITLE::CHAPTER_TITLE) {
|
||||
const int tocIndex =
|
||||
section ? section->getTocIndexForPage(section->currentPage) : epub->getTocIndexForSpineIndex(currentSpineIndex);
|
||||
@@ -1663,7 +1693,7 @@ void EpubReaderActivity::renderStatusBar() const {
|
||||
|
||||
const bool isStarred = section && bookmarkStore.has(static_cast<uint16_t>(currentSpineIndex),
|
||||
static_cast<uint16_t>(section->currentPage));
|
||||
GUI.drawStatusBar(renderer, bookProgress, currentPage, pageCount, title, 0, textYOffset, isStarred);
|
||||
GUI.drawStatusBar(renderer, bookProgress, currentPage, pageCount, title, 0, isStarred);
|
||||
}
|
||||
|
||||
void EpubReaderActivity::navigateToHref(const std::string& hrefStr, const bool savePosition) {
|
||||
@@ -1754,11 +1784,10 @@ bool EpubReaderActivity::drawCurrentPageToBuffer(const std::string& filePath, Gf
|
||||
// Compute margins exactly as render() does
|
||||
int marginTop, marginRight, marginBottom, marginLeft;
|
||||
renderer.getOrientedViewableTRBL(&marginTop, &marginRight, &marginBottom, &marginLeft);
|
||||
marginTop += SETTINGS.screenMargin;
|
||||
marginTop += std::max(static_cast<int>(SETTINGS.screenMargin), UITheme::getStatusBarTopHeight());
|
||||
marginLeft += SETTINGS.screenMargin;
|
||||
marginRight += SETTINGS.screenMargin;
|
||||
const uint8_t statusBarHeight = UITheme::getInstance().getStatusBarHeight();
|
||||
marginBottom += std::max(SETTINGS.screenMargin, statusBarHeight);
|
||||
marginBottom += std::max(static_cast<int>(SETTINGS.screenMargin), UITheme::getStatusBarBottomHeight());
|
||||
|
||||
const uint16_t viewportWidth = renderer.getScreenWidth() - marginLeft - marginRight;
|
||||
const uint16_t viewportHeight = renderer.getScreenHeight() - marginTop - marginBottom;
|
||||
|
||||
Reference in New Issue
Block a user