Movable status bar and dual-edge progress bars

This commit is contained in:
jpirnay
2026-05-06 23:09:26 +02:00
parent 464846fd75
commit 4f140afd10
14 changed files with 475 additions and 168 deletions
+64 -35
View File
@@ -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;
@@ -177,6 +177,7 @@ class EpubReaderActivity final : public Activity {
void applyOrientation(uint8_t orientation);
void applyTextDarkness(uint8_t textDarkness);
void toggleAutoPageTurn(uint8_t selectedPageTurnOption);
void stopAutomaticPageTurn();
void applyBookReaderOverrides(int8_t embeddedStyleOverride, int8_t imageRenderingOverride, int8_t fontFamilyOverride,
const std::string& sdFontFamilyOverride, int8_t fontSizeOverride,
bool bionicReadingOverride);
+2 -3
View File
@@ -292,11 +292,10 @@ void MdReaderActivity::initializeReader() {
renderer.getOrientedViewableTRBL(&cachedOrientedMarginTop, &cachedOrientedMarginRight, &cachedOrientedMarginBottom,
&cachedOrientedMarginLeft);
cachedOrientedMarginTop += cachedScreenMargin;
cachedOrientedMarginTop += std::max(static_cast<int>(cachedScreenMargin), UITheme::getStatusBarTopHeight());
cachedOrientedMarginLeft += cachedScreenMargin;
cachedOrientedMarginRight += cachedScreenMargin;
cachedOrientedMarginBottom +=
std::max(cachedScreenMargin, static_cast<uint8_t>(UITheme::getInstance().getStatusBarHeight()));
cachedOrientedMarginBottom += std::max(static_cast<int>(cachedScreenMargin), UITheme::getStatusBarBottomHeight());
viewportWidth = renderer.getScreenWidth() - cachedOrientedMarginLeft - cachedOrientedMarginRight;
const int viewportHeight = renderer.getScreenHeight() - cachedOrientedMarginTop - cachedOrientedMarginBottom;
+7 -6
View File
@@ -7,6 +7,8 @@
#include <Serialization.h>
#include <Utf8.h>
#include <algorithm>
#include "CrossPointSettings.h"
#include "CrossPointState.h"
#include "GlobalBookmarkIndex.h"
@@ -207,11 +209,10 @@ void TxtReaderActivity::initializeReader() {
// Calculate viewport dimensions
renderer.getOrientedViewableTRBL(&cachedOrientedMarginTop, &cachedOrientedMarginRight, &cachedOrientedMarginBottom,
&cachedOrientedMarginLeft);
cachedOrientedMarginTop += cachedScreenMargin;
cachedOrientedMarginTop += std::max(static_cast<int>(cachedScreenMargin), UITheme::getStatusBarTopHeight());
cachedOrientedMarginLeft += cachedScreenMargin;
cachedOrientedMarginRight += cachedScreenMargin;
cachedOrientedMarginBottom +=
std::max(cachedScreenMargin, static_cast<uint8_t>(UITheme::getInstance().getStatusBarHeight()));
cachedOrientedMarginBottom += std::max(static_cast<int>(cachedScreenMargin), UITheme::getStatusBarBottomHeight());
viewportWidth = renderer.getScreenWidth() - cachedOrientedMarginLeft - cachedOrientedMarginRight;
const int viewportHeight = renderer.getScreenHeight() - cachedOrientedMarginTop - cachedOrientedMarginBottom;
@@ -407,7 +408,7 @@ void TxtReaderActivity::renderStatusBar() const {
title = txt->getTitle();
}
const bool isStarred = bookmarkStore.has(0, static_cast<uint16_t>(currentPage));
GUI.drawStatusBar(renderer, progress, currentPage + 1, totalPages, title, 0, 0, isStarred);
GUI.drawStatusBar(renderer, progress, currentPage + 1, totalPages, title, 0, isStarred);
}
void TxtReaderActivity::saveProgress() const {
@@ -644,10 +645,10 @@ bool TxtReaderActivity::drawCurrentPageToBuffer(const std::string& filePath, Gfx
int marginTop, marginRight, marginBottom, marginLeft;
renderer.getOrientedViewableTRBL(&marginTop, &marginRight, &marginBottom, &marginLeft);
marginTop += screenMargin;
marginTop += std::max(static_cast<int>(screenMargin), UITheme::getStatusBarTopHeight());
marginLeft += screenMargin;
marginRight += screenMargin;
marginBottom += std::max(screenMargin, static_cast<uint8_t>(UITheme::getInstance().getStatusBarHeight()));
marginBottom += std::max(static_cast<int>(screenMargin), UITheme::getStatusBarBottomHeight());
const int vw = renderer.getScreenWidth() - marginLeft - marginRight;
const int vh = renderer.getScreenHeight() - marginTop - marginBottom;
@@ -3,7 +3,10 @@
#include <GfxRenderer.h>
#include <I18n.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include "CrossPointSettings.h"
#include "MappedInputManager.h"
@@ -11,14 +14,19 @@
#include "fontIds.h"
namespace {
constexpr int MENU_ITEMS = 7;
const StrId menuNames[MENU_ITEMS] = {StrId::STR_CHAPTER_PAGE_COUNT,
StrId::STR_BOOK_PROGRESS_PERCENTAGE,
StrId::STR_PROGRESS_BAR,
StrId::STR_PROGRESS_BAR_THICKNESS,
StrId::STR_TITLE,
StrId::STR_BATTERY,
StrId::STR_CLOCK};
// Base menu items (without clock). Clock is appended conditionally at index 5.
constexpr int MENU_ITEMS_NO_CLOCK = 9;
constexpr int MENU_ITEMS_WITH_CLOCK = 10;
const StrId menuNames[MENU_ITEMS_WITH_CLOCK] = {StrId::STR_STATUS_ITEMS_POSITION,
StrId::STR_CHAPTER_PAGE_COUNT,
StrId::STR_BOOK_PROGRESS_PERCENTAGE,
StrId::STR_TITLE,
StrId::STR_BATTERY,
StrId::STR_CLOCK,
StrId::STR_UPPER_PROGRESS_BAR,
StrId::STR_UPPER_PROGRESS_BAR_THICKNESS,
StrId::STR_LOWER_PROGRESS_BAR,
StrId::STR_LOWER_PROGRESS_BAR_THICKNESS};
constexpr int PROGRESS_BAR_ITEMS = 3;
const StrId progressBarNames[PROGRESS_BAR_ITEMS] = {StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE};
@@ -29,26 +37,130 @@ const StrId progressBarThicknessNames[PROGRESS_BAR_THICKNESS_ITEMS] = {
constexpr int TITLE_ITEMS = 3;
const StrId titleNames[TITLE_ITEMS] = {StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE};
const int widthMargin = 10;
const int verticalPreviewPadding = 50;
const int verticalPreviewTextPadding = 40;
constexpr int STATUS_ITEMS_POSITION_ITEMS = 2;
const StrId statusItemsPositionNames[STATUS_ITEMS_POSITION_ITEMS] = {StrId::STR_TOP, StrId::STR_BOTTOM};
constexpr int previewHorizontalInset = 10;
constexpr int previewHeight = 78;
constexpr int previewInnerMargin = 4;
void drawPreviewProgressBar(const GfxRenderer& renderer, const Rect& rect, const uint8_t progressBar,
const uint8_t thickness, const bool topEdge) {
if (progressBar == CrossPointSettings::STATUS_BAR_PROGRESS_BAR::HIDE_PROGRESS) {
return;
}
const int percent = progressBar == CrossPointSettings::STATUS_BAR_PROGRESS_BAR::BOOK_PROGRESS ? 75 : 25;
const int barHeight = (thickness + 1) * 2;
const int y = topEdge ? rect.y + previewInnerMargin : rect.y + rect.height - previewInnerMargin - barHeight;
const int barWidth = (rect.width - previewInnerMargin * 2) * percent / 100;
renderer.fillRect(rect.x + previewInnerMargin, y, barWidth, barHeight);
}
void drawPreviewStatusItems(const GfxRenderer& renderer, const Rect& rect, const ThemeMetrics& metrics) {
const bool hasProgressText = SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage;
const bool hasTitle = SETTINGS.statusBarTitle != CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE;
const bool hasStatusItems =
hasProgressText || hasTitle || SETTINGS.statusBarBattery || (SETTINGS.useClock && SETTINGS.statusBarClock);
if (!hasStatusItems) {
return;
}
const bool statusItemsAtTop =
SETTINGS.statusBarItemsPosition == CrossPointSettings::STATUS_BAR_ITEMS_POSITION::STATUS_BAR_ITEMS_TOP;
const int adjacentProgressHeight = statusItemsAtTop
? UITheme::getProgressBarHeight(SETTINGS.statusBarUpperProgressBar,
SETTINGS.statusBarUpperProgressBarThickness)
: UITheme::getProgressBarHeight(SETTINGS.statusBarLowerProgressBar,
SETTINGS.statusBarLowerProgressBarThickness);
const int statusItemsHeight = UITheme::getStatusBarItemsHeight();
const int textY = statusItemsAtTop
? rect.y + previewInnerMargin + adjacentProgressHeight + 4
: rect.y + rect.height - previewInnerMargin - adjacentProgressHeight - statusItemsHeight + 4;
const bool showBatteryPercentage =
SETTINGS.statusBarBattery &&
SETTINGS.hideBatteryPercentage == CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_NEVER;
if (SETTINGS.statusBarBattery) {
GUI.drawBatteryLeft(renderer,
Rect{rect.x + previewInnerMargin + 2, textY, metrics.batteryWidth, metrics.batteryHeight},
showBatteryPercentage);
}
int progressTextWidth = 0;
if (hasProgressText) {
char progressStr[32] = "";
if (SETTINGS.statusBarChapterPageCount && SETTINGS.statusBarBookProgressPercentage) {
snprintf(progressStr, sizeof(progressStr), "%d/%d %d%%", 8, 32, 75);
} else if (SETTINGS.statusBarBookProgressPercentage) {
snprintf(progressStr, sizeof(progressStr), "%d%%", 75);
} else {
snprintf(progressStr, sizeof(progressStr), "%d/%d", 8, 32);
}
progressTextWidth = renderer.getTextWidth(SMALL_FONT_ID, progressStr);
renderer.drawText(SMALL_FONT_ID, rect.x + rect.width - previewInnerMargin - 2 - progressTextWidth, textY,
progressStr);
}
if (!hasTitle) {
return;
}
const char* title = SETTINGS.statusBarTitle == CrossPointSettings::STATUS_BAR_TITLE::BOOK_TITLE
? tr(STR_EXAMPLE_BOOK)
: tr(STR_EXAMPLE_CHAPTER);
int leftReserve = 6;
if (SETTINGS.statusBarBattery) {
leftReserve = metrics.batteryWidth + 28;
if (showBatteryPercentage) {
const int percentReserve = 2 + metrics.batteryWidth + BaseTheme::batteryPercentSpacing +
renderer.getTextWidth(SMALL_FONT_ID, "100%") + 8;
leftReserve = std::max(leftReserve, percentReserve);
}
}
const int rightReserve = progressTextWidth > 0 ? progressTextWidth + 18 : 6;
const int titleAreaWidth = rect.width - previewInnerMargin * 2 - leftReserve - rightReserve;
if (titleAreaWidth <= 0) {
return;
}
std::string previewTitle = renderer.truncatedText(SMALL_FONT_ID, title, titleAreaWidth);
const int titleWidth = renderer.getTextWidth(SMALL_FONT_ID, previewTitle.c_str());
renderer.drawText(SMALL_FONT_ID, rect.x + previewInnerMargin + leftReserve + (titleAreaWidth - titleWidth) / 2, textY,
previewTitle.c_str());
}
} // namespace
void StatusBarSettingsActivity::onEnter() {
Activity::onEnter();
const int menuCount = SETTINGS.useClock ? MENU_ITEMS : MENU_ITEMS - 1;
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
if (selectedIndex >= menuCount) {
selectedIndex = 0;
}
// Clamp statusBarProgressBar and statusBarTitle in case of corrupt/migrated data
if (SETTINGS.statusBarProgressBar >= PROGRESS_BAR_ITEMS) {
SETTINGS.statusBarProgressBar = CrossPointSettings::STATUS_BAR_PROGRESS_BAR::HIDE_PROGRESS;
// Clamp status bar settings in case of corrupt/migrated data.
if (SETTINGS.statusBarUpperProgressBar >= PROGRESS_BAR_ITEMS) {
SETTINGS.statusBarUpperProgressBar = CrossPointSettings::STATUS_BAR_PROGRESS_BAR::HIDE_PROGRESS;
}
if (SETTINGS.statusBarTitle >= PROGRESS_BAR_THICKNESS_ITEMS) {
SETTINGS.statusBarTitle = CrossPointSettings::STATUS_BAR_PROGRESS_BAR_THICKNESS::PROGRESS_BAR_NORMAL;
if (SETTINGS.statusBarLowerProgressBar >= PROGRESS_BAR_ITEMS) {
SETTINGS.statusBarLowerProgressBar = CrossPointSettings::STATUS_BAR_PROGRESS_BAR::HIDE_PROGRESS;
}
if (SETTINGS.statusBarUpperProgressBarThickness >= PROGRESS_BAR_THICKNESS_ITEMS) {
SETTINGS.statusBarUpperProgressBarThickness =
CrossPointSettings::STATUS_BAR_PROGRESS_BAR_THICKNESS::PROGRESS_BAR_NORMAL;
}
if (SETTINGS.statusBarLowerProgressBarThickness >= PROGRESS_BAR_THICKNESS_ITEMS) {
SETTINGS.statusBarLowerProgressBarThickness =
CrossPointSettings::STATUS_BAR_PROGRESS_BAR_THICKNESS::PROGRESS_BAR_NORMAL;
}
if (SETTINGS.statusBarItemsPosition >= STATUS_ITEMS_POSITION_ITEMS) {
SETTINGS.statusBarItemsPosition = CrossPointSettings::STATUS_BAR_ITEMS_POSITION::STATUS_BAR_ITEMS_BOTTOM;
}
if (SETTINGS.statusBarTitle >= TITLE_ITEMS) {
@@ -74,53 +186,67 @@ void StatusBarSettingsActivity::loop() {
// Handle navigation
buttonNavigator.onNextRelease([this] {
const int menuCount = SETTINGS.useClock ? MENU_ITEMS : MENU_ITEMS - 1;
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, menuCount);
requestUpdate();
});
buttonNavigator.onPreviousRelease([this] {
const int menuCount = SETTINGS.useClock ? MENU_ITEMS : MENU_ITEMS - 1;
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, menuCount);
requestUpdate();
});
buttonNavigator.onNextContinuous([this] {
const int menuCount = SETTINGS.useClock ? MENU_ITEMS : MENU_ITEMS - 1;
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, menuCount);
requestUpdate();
});
buttonNavigator.onPreviousContinuous([this] {
const int menuCount = SETTINGS.useClock ? MENU_ITEMS : MENU_ITEMS - 1;
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, menuCount);
requestUpdate();
});
}
void StatusBarSettingsActivity::handleSelection() {
if (selectedIndex == 0) {
// When clock is hidden, indices 6+ shift down by 1 (clock slot at index 5 is absent).
// We remap the logical index to match the menuNames array which always includes clock at slot 5.
const int logicalIndex = (!SETTINGS.useClock && selectedIndex >= 5) ? selectedIndex + 1 : selectedIndex;
if (logicalIndex == 0) {
// Status Items Position
SETTINGS.statusBarItemsPosition = (SETTINGS.statusBarItemsPosition + 1) % STATUS_ITEMS_POSITION_ITEMS;
} else if (logicalIndex == 1) {
// Chapter Page Count
SETTINGS.statusBarChapterPageCount = (SETTINGS.statusBarChapterPageCount + 1) % 2;
} else if (selectedIndex == 1) {
} else if (logicalIndex == 2) {
// Book Progress %
SETTINGS.statusBarBookProgressPercentage = (SETTINGS.statusBarBookProgressPercentage + 1) % 2;
} else if (selectedIndex == 2) {
// Progress Bar
SETTINGS.statusBarProgressBar = (SETTINGS.statusBarProgressBar + 1) % PROGRESS_BAR_ITEMS;
} else if (selectedIndex == 3) {
// Progress Bar Thickness
SETTINGS.statusBarProgressBarThickness =
(SETTINGS.statusBarProgressBarThickness + 1) % PROGRESS_BAR_THICKNESS_ITEMS;
} else if (selectedIndex == 4) {
// Chapter Title
} else if (logicalIndex == 3) {
// Title
SETTINGS.statusBarTitle = (SETTINGS.statusBarTitle + 1) % TITLE_ITEMS;
} else if (selectedIndex == 5) {
// Show Battery
} else if (logicalIndex == 4) {
// Battery
SETTINGS.statusBarBattery = (SETTINGS.statusBarBattery + 1) % 2;
} else if (selectedIndex == 6 && SETTINGS.useClock) {
// Show Clock
} else if (logicalIndex == 5 && SETTINGS.useClock) {
// Clock
SETTINGS.statusBarClock = (SETTINGS.statusBarClock + 1) % 2;
} else if (logicalIndex == 6) {
// Upper Progress Bar
SETTINGS.statusBarUpperProgressBar = (SETTINGS.statusBarUpperProgressBar + 1) % PROGRESS_BAR_ITEMS;
} else if (logicalIndex == 7) {
// Upper Progress Bar Thickness
SETTINGS.statusBarUpperProgressBarThickness =
(SETTINGS.statusBarUpperProgressBarThickness + 1) % PROGRESS_BAR_THICKNESS_ITEMS;
} else if (logicalIndex == 8) {
// Lower Progress Bar
SETTINGS.statusBarLowerProgressBar = (SETTINGS.statusBarLowerProgressBar + 1) % PROGRESS_BAR_ITEMS;
} else if (logicalIndex == 9) {
// Lower Progress Bar Thickness
SETTINGS.statusBarLowerProgressBarThickness =
(SETTINGS.statusBarLowerProgressBarThickness + 1) % PROGRESS_BAR_THICKNESS_ITEMS;
}
SETTINGS.saveToFile();
}
@@ -137,28 +263,44 @@ void StatusBarSettingsActivity::render(RenderLock&&) {
tr(STR_CUSTOMISE_STATUS_BAR));
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2;
const int menuCount = SETTINGS.useClock ? MENU_ITEMS : MENU_ITEMS - 1;
const int previewLabelHeight = renderer.getLineHeight(UI_10_FONT_ID);
const int previewAreaHeight = previewLabelHeight + previewHeight + metrics.verticalSpacing * 2;
const int contentHeight =
pageHeight - contentTop - metrics.buttonHintsHeight - previewAreaHeight - metrics.verticalSpacing * 2;
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
GUI.drawList(
renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast<int>(menuCount),
static_cast<int>(selectedIndex), [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr,
nullptr,
static_cast<int>(selectedIndex),
[](int index) {
const int logicalIndex = (!SETTINGS.useClock && index >= 5) ? index + 1 : index;
return std::string(I18N.get(menuNames[logicalIndex]));
},
nullptr, nullptr,
[this](int index) {
// Draw status for each setting
if (index == 0) {
// When clock is hidden, indices 6+ shift down by 1 in the display list but we still
// index into menuNames which has clock at slot 5. Remap for value display.
const int logicalIndex = (!SETTINGS.useClock && index >= 5) ? index + 1 : index;
if (logicalIndex == 0) {
return I18N.get(statusItemsPositionNames[SETTINGS.statusBarItemsPosition]);
} else if (logicalIndex == 1) {
return SETTINGS.statusBarChapterPageCount ? tr(STR_SHOW) : tr(STR_HIDE);
} else if (index == 1) {
} else if (logicalIndex == 2) {
return SETTINGS.statusBarBookProgressPercentage ? tr(STR_SHOW) : tr(STR_HIDE);
} else if (index == 2) {
return I18N.get(progressBarNames[SETTINGS.statusBarProgressBar]);
} else if (index == 3) {
return I18N.get(progressBarThicknessNames[SETTINGS.statusBarProgressBarThickness]);
} else if (index == 4) {
} else if (logicalIndex == 3) {
return I18N.get(titleNames[SETTINGS.statusBarTitle]);
} else if (index == 5) {
} else if (logicalIndex == 4) {
return SETTINGS.statusBarBattery ? tr(STR_SHOW) : tr(STR_HIDE);
} else if (index == 6) {
} else if (logicalIndex == 5) {
return SETTINGS.statusBarClock ? tr(STR_SHOW) : tr(STR_HIDE);
} else if (logicalIndex == 6) {
return I18N.get(progressBarNames[SETTINGS.statusBarUpperProgressBar]);
} else if (logicalIndex == 7) {
return I18N.get(progressBarThicknessNames[SETTINGS.statusBarUpperProgressBarThickness]);
} else if (logicalIndex == 8) {
return I18N.get(progressBarNames[SETTINGS.statusBarLowerProgressBar]);
} else if (logicalIndex == 9) {
return I18N.get(progressBarThicknessNames[SETTINGS.statusBarLowerProgressBarThickness]);
} else {
return tr(STR_HIDE);
}
@@ -169,19 +311,16 @@ void StatusBarSettingsActivity::render(RenderLock&&) {
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_TOGGLE), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
std::string title;
if (SETTINGS.statusBarTitle == CrossPointSettings::STATUS_BAR_TITLE::BOOK_TITLE) {
title = tr(STR_EXAMPLE_BOOK);
} else if (SETTINGS.statusBarTitle == CrossPointSettings::STATUS_BAR_TITLE::CHAPTER_TITLE) {
title = tr(STR_EXAMPLE_CHAPTER);
}
GUI.drawStatusBar(renderer, 75, 8, 32, title, verticalPreviewPadding);
renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding,
renderer.getScreenHeight() - UITheme::getInstance().getStatusBarHeight() - verticalPreviewPadding -
verticalPreviewTextPadding,
tr(STR_PREVIEW));
const int previewLabelY = contentTop + contentHeight + metrics.verticalSpacing;
renderer.drawText(UI_10_FONT_ID, metrics.contentSidePadding, previewLabelY, tr(STR_PREVIEW));
const Rect previewRect{previewHorizontalInset, previewLabelY + previewLabelHeight + metrics.verticalSpacing / 2,
pageWidth - previewHorizontalInset * 2, previewHeight};
renderer.drawRect(previewRect.x, previewRect.y, previewRect.width, previewRect.height);
drawPreviewProgressBar(renderer, previewRect, SETTINGS.statusBarUpperProgressBar,
SETTINGS.statusBarUpperProgressBarThickness, true);
drawPreviewProgressBar(renderer, previewRect, SETTINGS.statusBarLowerProgressBar,
SETTINGS.statusBarLowerProgressBarThickness, false);
drawPreviewStatusItems(renderer, previewRect, metrics);
renderer.displayBuffer();
}