451 lines
20 KiB
C++
451 lines
20 KiB
C++
#include "EpubReaderMenuActivity.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <I18n.h>
|
|
|
|
#include "KOReaderCredentialStore.h"
|
|
#include "MappedInputManager.h"
|
|
#include "SdCardFontGlobals.h"
|
|
#include "activities/settings/SettingsSubmenuActivity.h"
|
|
#include "components/UITheme.h"
|
|
#include "fontIds.h"
|
|
|
|
namespace {
|
|
// Returns the localized name of the family currently used as the global default
|
|
// for the reader. When the user has selected an SD card font globally, the
|
|
// override menu's "Default" label should reflect that family by name even
|
|
// though the per-book override list itself is built-in only.
|
|
std::string defaultFontFamilyLabel(const SettingInfo& item) {
|
|
if (SETTINGS.sdFontFamilyName[0] != '\0') {
|
|
return std::string(SETTINGS.sdFontFamilyName);
|
|
}
|
|
// Built-in: enumValues[0] is STR_DEFAULT_VALUE, [1..3] are the three families
|
|
// in the same order as CrossPointSettings::FONT_FAMILY (BOOKERLY, NOTOSANS,
|
|
// OPENDYSLEXIC).
|
|
const auto idx = static_cast<size_t>(SETTINGS.fontFamily + 1);
|
|
if (idx < item.enumValues.size()) {
|
|
return I18N.get(item.enumValues[idx]);
|
|
}
|
|
return {};
|
|
}
|
|
} // namespace
|
|
|
|
EpubReaderMenuActivity::EpubReaderMenuActivity(
|
|
GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title, const int currentPage,
|
|
const int totalPages, const int bookProgressPercent, const uint8_t currentOrientation, const bool hasFootnotes,
|
|
const int8_t initialEmbeddedStyleOverride, const int8_t initialImageRenderingOverride,
|
|
const int8_t initialFontFamilyOverride, const int8_t initialFontSizeOverride, const uint8_t initialTextDarkness,
|
|
const bool initialBionicReadingOverride, const bool hasStarredPages, const bool isCurrentPageStarred)
|
|
: MenuListActivity("EpubReaderMenu", renderer, mappedInput),
|
|
currentPageStarred(isCurrentPageStarred),
|
|
pendingOrientation(currentOrientation),
|
|
pendingEmbeddedStyleOverride(initialEmbeddedStyleOverride),
|
|
pendingImageRenderingOverride(initialImageRenderingOverride),
|
|
pendingFontFamilyOverride(initialFontFamilyOverride),
|
|
pendingFontSizeOverride(initialFontSizeOverride),
|
|
pendingTextDarkness(initialTextDarkness),
|
|
pendingBionicReading(initialBionicReadingOverride),
|
|
title(title),
|
|
currentPage(currentPage),
|
|
totalPages(totalPages),
|
|
bookProgressPercent(bookProgressPercent) {
|
|
buildMenuItems(hasFootnotes, hasStarredPages);
|
|
}
|
|
|
|
void EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes, bool hasStarredPages) {
|
|
menuItems.reserve(20);
|
|
|
|
// --- Navigation ---
|
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_NAVIGATION));
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_SELECT_CHAPTER, SettingAction::None));
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_GO_TO_PERCENT, SettingAction::None));
|
|
|
|
// Bookmarks, footnotes
|
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_BOOKMARKS));
|
|
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_STAR_PAGE, SettingAction::None));
|
|
if (hasStarredPages) {
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_STARRED_PAGES, SettingAction::None));
|
|
}
|
|
if (hasFootnotes) {
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_FOOTNOTES, SettingAction::None));
|
|
}
|
|
|
|
// --- Appearance ---
|
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_APPEARANCE));
|
|
|
|
auto* self = this;
|
|
// Orientation: straightforward 0-3 cycle
|
|
menuItems.push_back(SettingInfo::DynamicEnumCtx(
|
|
StrId::STR_ORIENTATION,
|
|
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, StrId::STR_LANDSCAPE_CCW}, self,
|
|
[](const void* ctx) -> uint8_t { return static_cast<const EpubReaderMenuActivity*>(ctx)->pendingOrientation; },
|
|
[](void* ctx, uint8_t v) { static_cast<EpubReaderMenuActivity*>(ctx)->pendingOrientation = v; }));
|
|
|
|
// Embedded style: cycles default(-1) -> ON(1) -> OFF(0) via DynamicEnum indices 0/1/2
|
|
menuItems.push_back(SettingInfo::DynamicEnumCtx(
|
|
StrId::STR_EMBEDDED_STYLE,
|
|
{StrId::STR_DEFAULT_VALUE, StrId::STR_STATE_ON, StrId::STR_STATE_OFF}, self,
|
|
[](const void* ctx) -> uint8_t {
|
|
const auto* s = static_cast<const EpubReaderMenuActivity*>(ctx);
|
|
if (s->pendingEmbeddedStyleOverride < 0) return 0;
|
|
if (s->pendingEmbeddedStyleOverride > 0) return 1;
|
|
return 2;
|
|
},
|
|
[](void* ctx, uint8_t v) {
|
|
auto* s = static_cast<EpubReaderMenuActivity*>(ctx);
|
|
if (v == 0)
|
|
s->pendingEmbeddedStyleOverride = -1;
|
|
else if (v == 1)
|
|
s->pendingEmbeddedStyleOverride = 1;
|
|
else
|
|
s->pendingEmbeddedStyleOverride = 0;
|
|
})
|
|
.withSubmenu(StrId::STR_READER_OVERRIDES));
|
|
|
|
// Image rendering: cycles default(-1) -> display(0) -> placeholder(1) -> suppress(2)
|
|
menuItems.push_back(SettingInfo::DynamicEnumCtx(
|
|
StrId::STR_IMAGES,
|
|
{StrId::STR_DEFAULT_VALUE, StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER,
|
|
StrId::STR_IMAGES_SUPPRESS},
|
|
self,
|
|
[](const void* ctx) -> uint8_t {
|
|
const auto* s = static_cast<const EpubReaderMenuActivity*>(ctx);
|
|
return (s->pendingImageRenderingOverride < 0) ? 0 : (s->pendingImageRenderingOverride + 1);
|
|
},
|
|
[](void* ctx, uint8_t v) {
|
|
auto* s = static_cast<EpubReaderMenuActivity*>(ctx);
|
|
s->pendingImageRenderingOverride = (v == 0) ? -1 : static_cast<int8_t>(v - 1);
|
|
})
|
|
.withSubmenu(StrId::STR_READER_OVERRIDES));
|
|
|
|
// Reader font family: cycles default(-1) -> Bookerly(0) -> Noto Sans(1) -> Open Dyslexic(2)
|
|
menuItems.push_back(
|
|
SettingInfo::DynamicEnumCtx(
|
|
StrId::STR_FONT_FAMILY,
|
|
{StrId::STR_DEFAULT_VALUE, StrId::STR_BOOKERLY, StrId::STR_NOTO_SANS, StrId::STR_OPEN_DYSLEXIC}, self,
|
|
[](const void* ctx) -> uint8_t {
|
|
const auto* s = static_cast<const EpubReaderMenuActivity*>(ctx);
|
|
return (s->pendingFontFamilyOverride < 0) ? 0 : static_cast<uint8_t>(s->pendingFontFamilyOverride + 1);
|
|
},
|
|
[](void* ctx, uint8_t v) {
|
|
auto* s = static_cast<EpubReaderMenuActivity*>(ctx);
|
|
s->pendingFontFamilyOverride = (v == 0) ? -1 : static_cast<int8_t>(v - 1);
|
|
})
|
|
.withSubmenu(StrId::STR_READER_OVERRIDES));
|
|
|
|
// Reader font size: cycles default(-1) -> Small(0) -> Medium(1) -> Large(2) -> X Large(3)
|
|
menuItems.push_back(
|
|
SettingInfo::DynamicEnumCtx(
|
|
StrId::STR_FONT_SIZE,
|
|
{StrId::STR_DEFAULT_VALUE, StrId::STR_SMALL, StrId::STR_MEDIUM, StrId::STR_LARGE, StrId::STR_X_LARGE}, self,
|
|
[](const void* ctx) -> uint8_t {
|
|
const auto* s = static_cast<const EpubReaderMenuActivity*>(ctx);
|
|
return (s->pendingFontSizeOverride < 0) ? 0 : static_cast<uint8_t>(s->pendingFontSizeOverride + 1);
|
|
},
|
|
[](void* ctx, uint8_t v) {
|
|
auto* s = static_cast<EpubReaderMenuActivity*>(ctx);
|
|
s->pendingFontSizeOverride = (v == 0) ? -1 : static_cast<int8_t>(v - 1);
|
|
})
|
|
.withSubmenu(StrId::STR_READER_OVERRIDES));
|
|
|
|
// Text darkness: straightforward 0-3 cycle
|
|
menuItems.push_back(
|
|
SettingInfo::DynamicEnumCtx(
|
|
StrId::STR_TEXT_DARKNESS, {StrId::STR_NORMAL, StrId::STR_DARK, StrId::STR_EXTRA_DARK, StrId::STR_MAX_DARK},
|
|
self,
|
|
[](const void* ctx) -> uint8_t {
|
|
return static_cast<const EpubReaderMenuActivity*>(ctx)->pendingTextDarkness;
|
|
},
|
|
[](void* ctx, uint8_t v) { static_cast<EpubReaderMenuActivity*>(ctx)->pendingTextDarkness = v; })
|
|
.withSubmenu(StrId::STR_READER_OVERRIDES));
|
|
|
|
menuItems.push_back(
|
|
SettingInfo::DynamicEnumCtx(
|
|
StrId::STR_BIONIC_READING, {StrId::STR_STATE_OFF, StrId::STR_STATE_ON}, self,
|
|
[](const void* ctx) -> uint8_t {
|
|
return static_cast<const EpubReaderMenuActivity*>(ctx)->pendingBionicReading ? 1 : 0;
|
|
},
|
|
[](void* ctx, uint8_t v) { static_cast<EpubReaderMenuActivity*>(ctx)->pendingBionicReading = (v != 0); })
|
|
.withSubmenu(StrId::STR_READER_OVERRIDES));
|
|
|
|
// Helper functions, reading ruler, auto page turn, orientation
|
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_UTILS));
|
|
// Auto page turn: ACTION type with custom cycling in onActionSelected
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_AUTO_TURN_PAGES_PER_MIN, SettingAction::None));
|
|
|
|
// --- Synchronisation (only if credentials are set) ---
|
|
if (KOREADER_STORE.hasCredentials()) {
|
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_KOREADER_SYNC));
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_PULL_PROGRESS_FROM_OTHER_DEVICES, SettingAction::None));
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_PUSH_PROGRESS_FROM_THIS_DEVICE, SettingAction::None));
|
|
}
|
|
|
|
// --- Tools ---
|
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_TOOLS));
|
|
menuItems.push_back(
|
|
SettingInfo::Action(StrId::STR_SCREENSHOT_BUTTON, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS));
|
|
menuItems.push_back(
|
|
SettingInfo::Action(StrId::STR_DISPLAY_QR, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS));
|
|
menuItems.push_back(
|
|
SettingInfo::Action(StrId::STR_DELETE_CACHE, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS));
|
|
menuItems.push_back(SettingInfo::Action(StrId::STR_GO_HOME_BUTTON, SettingAction::None));
|
|
menuItems.push_back(
|
|
SettingInfo::Action(StrId::STR_RENDER_BENCHMARK, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS));
|
|
}
|
|
|
|
EpubReaderMenuActivity::MenuAction EpubReaderMenuActivity::actionForNameId(StrId nameId) {
|
|
switch (nameId) {
|
|
case StrId::STR_SELECT_CHAPTER:
|
|
return MenuAction::SELECT_CHAPTER;
|
|
case StrId::STR_GO_TO_PERCENT:
|
|
return MenuAction::GO_TO_PERCENT;
|
|
case StrId::STR_STARRED_PAGES:
|
|
return MenuAction::STARRED_PAGES;
|
|
case StrId::STR_STAR_PAGE:
|
|
return MenuAction::STAR_PAGE;
|
|
case StrId::STR_FOOTNOTES:
|
|
return MenuAction::FOOTNOTES;
|
|
case StrId::STR_AUTO_TURN_PAGES_PER_MIN:
|
|
return MenuAction::AUTO_PAGE_TURN;
|
|
case StrId::STR_EMBEDDED_STYLE:
|
|
return MenuAction::EMBEDDED_STYLE;
|
|
case StrId::STR_IMAGES:
|
|
return MenuAction::IMAGE_RENDERING;
|
|
case StrId::STR_TEXT_DARKNESS:
|
|
return MenuAction::TEXT_DARKNESS;
|
|
case StrId::STR_ORIENTATION:
|
|
return MenuAction::ROTATE_SCREEN;
|
|
case StrId::STR_PULL_PROGRESS_FROM_OTHER_DEVICES:
|
|
return MenuAction::PULL_REMOTE;
|
|
case StrId::STR_PUSH_PROGRESS_FROM_THIS_DEVICE:
|
|
return MenuAction::PUSH_LOCAL;
|
|
case StrId::STR_SCREENSHOT_BUTTON:
|
|
return MenuAction::SCREENSHOT;
|
|
case StrId::STR_DISPLAY_QR:
|
|
return MenuAction::DISPLAY_QR;
|
|
case StrId::STR_DELETE_CACHE:
|
|
return MenuAction::DELETE_CACHE;
|
|
case StrId::STR_RENDER_BENCHMARK:
|
|
return MenuAction::RENDER_BENCHMARK;
|
|
case StrId::STR_GO_HOME_BUTTON:
|
|
return MenuAction::GO_HOME;
|
|
default:
|
|
return MenuAction::NONE;
|
|
}
|
|
}
|
|
|
|
EpubReaderMenuActivity::MenuAction EpubReaderMenuActivity::actionForSettingAction(SettingAction action) {
|
|
switch (action) {
|
|
case SettingAction::None:
|
|
case SettingAction::Submenu:
|
|
return MenuAction::NONE;
|
|
default:
|
|
return MenuAction::NONE;
|
|
}
|
|
}
|
|
|
|
void EpubReaderMenuActivity::finishWithAction(MenuAction action) {
|
|
setResult(MenuResult{static_cast<int>(action), -1, pendingOrientation, selectedPageTurnOption,
|
|
pendingEmbeddedStyleOverride, pendingImageRenderingOverride, pendingFontFamilyOverride,
|
|
pendingFontSizeOverride, pendingTextDarkness, static_cast<uint8_t>(pendingBionicReading)});
|
|
finish();
|
|
}
|
|
|
|
void EpubReaderMenuActivity::onActionSelected(int index) {
|
|
const auto& item = menuItems[index];
|
|
|
|
// Auto page turn cycles locally (not a DynamicEnum because labels are raw strings)
|
|
if (item.nameId == StrId::STR_AUTO_TURN_PAGES_PER_MIN) {
|
|
selectedPageTurnOption = (selectedPageTurnOption + 1) % 5;
|
|
requestUpdate();
|
|
return;
|
|
}
|
|
|
|
// All other ACTION items finish with a result
|
|
finishWithAction(actionForNameId(item.nameId));
|
|
}
|
|
|
|
void EpubReaderMenuActivity::onSettingToggled(int /*index*/) {
|
|
// DynamicEnum items update pending state via their setters — no persistence needed.
|
|
}
|
|
|
|
void EpubReaderMenuActivity::onBackPressed() {
|
|
ActivityResult result;
|
|
result.isCancelled = true;
|
|
result.data = MenuResult{-1,
|
|
-1,
|
|
pendingOrientation,
|
|
selectedPageTurnOption,
|
|
pendingEmbeddedStyleOverride,
|
|
pendingImageRenderingOverride,
|
|
pendingFontFamilyOverride,
|
|
pendingFontSizeOverride,
|
|
pendingTextDarkness,
|
|
static_cast<uint8_t>(pendingBionicReading)};
|
|
setResult(std::move(result));
|
|
finish();
|
|
}
|
|
|
|
std::string EpubReaderMenuActivity::getItemValueString(int index) const {
|
|
const auto& item = menuItems[index];
|
|
|
|
// Auto page turn: custom labels
|
|
if (item.nameId == StrId::STR_AUTO_TURN_PAGES_PER_MIN) {
|
|
if (selectedPageTurnOption == 0) return std::string(tr(STR_STATE_OFF));
|
|
return std::string(pageTurnLabels[selectedPageTurnOption]);
|
|
}
|
|
|
|
// Star page: reflect current page's star state
|
|
if (item.nameId == StrId::STR_STAR_PAGE) {
|
|
return currentPageStarred ? std::string(tr(STR_STATE_ON)) : std::string(tr(STR_STATE_OFF));
|
|
}
|
|
|
|
if (item.type == SettingType::ACTION) {
|
|
if (item.action == SettingAction::Submenu) {
|
|
return MenuListActivity::getItemValueString(index);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
if (item.type == SettingType::ENUM) {
|
|
if (item.nameId == StrId::STR_EMBEDDED_STYLE && pendingEmbeddedStyleOverride < 0) {
|
|
const auto defaultEffective = (SETTINGS.embeddedStyle != 0) ? tr(STR_STATE_ON) : tr(STR_STATE_OFF);
|
|
return std::string(tr(STR_DEFAULT_VALUE)) + " (" + defaultEffective + ")";
|
|
}
|
|
if (item.nameId == StrId::STR_IMAGES && pendingImageRenderingOverride < 0) {
|
|
const auto defaultIndex = static_cast<size_t>(SETTINGS.imageRendering + 1);
|
|
if (defaultIndex < item.enumValues.size()) {
|
|
return std::string(tr(STR_DEFAULT_VALUE)) + " (" + I18N.get(item.enumValues[defaultIndex]) + ")";
|
|
}
|
|
}
|
|
if (item.nameId == StrId::STR_FONT_FAMILY && pendingFontFamilyOverride < 0) {
|
|
const auto label = defaultFontFamilyLabel(item);
|
|
if (!label.empty()) {
|
|
return std::string(tr(STR_DEFAULT_VALUE)) + " (" + label + ")";
|
|
}
|
|
}
|
|
if (item.nameId == StrId::STR_FONT_SIZE && pendingFontSizeOverride < 0) {
|
|
const auto defaultIndex = static_cast<size_t>(SETTINGS.fontSize + 1);
|
|
if (defaultIndex < item.enumValues.size()) {
|
|
return std::string(tr(STR_DEFAULT_VALUE)) + " (" + I18N.get(item.enumValues[defaultIndex]) + ")";
|
|
}
|
|
}
|
|
}
|
|
|
|
// DynamicEnum items use the standard display
|
|
return MenuListActivity::getItemValueString(index);
|
|
}
|
|
|
|
void EpubReaderMenuActivity::openSubmenu(const SettingInfo& submenuEntry) {
|
|
auto it = std::find_if(submenuData.begin(), submenuData.end(),
|
|
[&submenuEntry](const SettingInfo::SubmenuData& d) { return d.id == submenuEntry.nameId; });
|
|
if (it == submenuData.end()) return;
|
|
|
|
auto itemValueStringOverride = [this](const SettingInfo& item) -> std::string {
|
|
if (item.nameId == StrId::STR_EMBEDDED_STYLE && pendingEmbeddedStyleOverride < 0) {
|
|
const auto defaultEffective = (SETTINGS.embeddedStyle != 0) ? tr(STR_STATE_ON) : tr(STR_STATE_OFF);
|
|
return std::string(tr(STR_DEFAULT_VALUE)) + " (" + defaultEffective + ")";
|
|
}
|
|
if (item.nameId == StrId::STR_IMAGES && pendingImageRenderingOverride < 0) {
|
|
const auto valueIndex = static_cast<size_t>(SETTINGS.imageRendering + 1);
|
|
if (valueIndex < item.enumValues.size()) {
|
|
return std::string(tr(STR_DEFAULT_VALUE)) + " (" + I18N.get(item.enumValues[valueIndex]) + ")";
|
|
}
|
|
}
|
|
if (item.nameId == StrId::STR_FONT_FAMILY && pendingFontFamilyOverride < 0) {
|
|
const auto label = defaultFontFamilyLabel(item);
|
|
if (!label.empty()) {
|
|
return std::string(tr(STR_DEFAULT_VALUE)) + " (" + label + ")";
|
|
}
|
|
}
|
|
if (item.nameId == StrId::STR_FONT_SIZE && pendingFontSizeOverride < 0) {
|
|
const auto valueIndex = static_cast<size_t>(SETTINGS.fontSize + 1);
|
|
if (valueIndex < item.enumValues.size()) {
|
|
return std::string(tr(STR_DEFAULT_VALUE)) + " (" + I18N.get(item.enumValues[valueIndex]) + ")";
|
|
}
|
|
}
|
|
return item.getDisplayValue();
|
|
};
|
|
|
|
startActivityForResult(std::make_unique<SettingsSubmenuActivity>(renderer, mappedInput, submenuEntry.nameId,
|
|
it->items, std::move(itemValueStringOverride)),
|
|
[this](const ActivityResult& result) {
|
|
if (!result.isCancelled) {
|
|
const auto* menuResult = std::get_if<MenuResult>(&result.data);
|
|
if (menuResult) {
|
|
if (menuResult->action != -1) {
|
|
const auto action =
|
|
actionForSettingAction(static_cast<SettingAction>(menuResult->action));
|
|
if (action != MenuAction::NONE) {
|
|
finishWithAction(action);
|
|
return;
|
|
}
|
|
}
|
|
if (menuResult->nameId != -1) {
|
|
const auto action = actionForNameId(static_cast<StrId>(menuResult->nameId));
|
|
if (action != MenuAction::NONE) {
|
|
finishWithAction(action);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
requestUpdate();
|
|
});
|
|
}
|
|
|
|
void EpubReaderMenuActivity::toggleCurrentItem() {
|
|
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(menuItems.size())) return;
|
|
const auto& item = menuItems[selectedIndex];
|
|
if (item.isSeparator) return;
|
|
|
|
if (item.type == SettingType::ACTION) {
|
|
if (item.action == SettingAction::Submenu) {
|
|
openSubmenu(item);
|
|
return;
|
|
}
|
|
onActionSelected(selectedIndex);
|
|
return;
|
|
}
|
|
|
|
menuItems[selectedIndex].toggleValue();
|
|
onSettingToggled(selectedIndex);
|
|
requestUpdate();
|
|
}
|
|
|
|
void EpubReaderMenuActivity::onEnter() { MenuListActivity::onEnter(); }
|
|
|
|
void EpubReaderMenuActivity::render(RenderLock&&) {
|
|
renderer.clearScreen();
|
|
const Rect contentRect = UITheme::getContentRect(renderer, true, false);
|
|
|
|
// Title
|
|
const std::string truncTitle =
|
|
renderer.truncatedText(UI_12_FONT_ID, title.c_str(), contentRect.width - 40, EpdFontFamily::BOLD);
|
|
const int titleX =
|
|
contentRect.x +
|
|
(contentRect.width - renderer.getTextWidth(UI_12_FONT_ID, truncTitle.c_str(), EpdFontFamily::BOLD)) / 2;
|
|
renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentRect.y, truncTitle.c_str(), true, EpdFontFamily::BOLD);
|
|
|
|
// Progress summary
|
|
std::string progressLine;
|
|
if (totalPages > 0) {
|
|
progressLine = std::string(tr(STR_CHAPTER_PREFIX)) + std::to_string(currentPage) + "/" +
|
|
std::to_string(totalPages) + std::string(tr(STR_PAGES_SEPARATOR));
|
|
}
|
|
progressLine += std::string(tr(STR_BOOK_PREFIX)) + std::to_string(bookProgressPercent) + "%";
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 45, progressLine.c_str());
|
|
|
|
// Menu Items
|
|
const int startY = 75 + contentRect.y;
|
|
const int listHeight = contentRect.height - (startY - contentRect.y);
|
|
drawMenuList(Rect{contentRect.x, startY, contentRect.width, listHeight});
|
|
|
|
// Footer / Hints
|
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
|
|
renderer.displayBuffer();
|
|
}
|