feat: Add per-book letterbox fill override
Introduce BookSettings utility for per-book settings stored in the book's cache directory (book_settings.bin). Add "Letterbox Fill" option to the EPUB reader menu that cycles Default/Dithered/Solid/None. At sleep time, the per-book override is loaded and takes precedence over the global setting for all book types (EPUB, XTC, TXT). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
#include "CrossPointState.h"
|
#include "CrossPointState.h"
|
||||||
|
#include "util/BookSettings.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "images/Logo120.h"
|
#include "images/Logo120.h"
|
||||||
@@ -450,7 +451,8 @@ void SleepActivity::renderDefaultSleepScreen() const {
|
|||||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap, const std::string& edgeCachePath) const {
|
void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap, const std::string& edgeCachePath,
|
||||||
|
uint8_t fillModeOverride) const {
|
||||||
int x, y;
|
int x, y;
|
||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
@@ -494,8 +496,11 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap, const std::str
|
|||||||
const float scale =
|
const float scale =
|
||||||
std::min(static_cast<float>(pageWidth) / effectiveWidth, static_cast<float>(pageHeight) / effectiveHeight);
|
std::min(static_cast<float>(pageWidth) / effectiveWidth, static_cast<float>(pageHeight) / effectiveHeight);
|
||||||
|
|
||||||
// Determine letterbox fill settings
|
// Determine letterbox fill settings (per-book override takes precedence)
|
||||||
const uint8_t fillMode = SETTINGS.sleepScreenLetterboxFill;
|
const uint8_t fillMode = (fillModeOverride != BookSettings::USE_GLOBAL &&
|
||||||
|
fillModeOverride < CrossPointSettings::SLEEP_SCREEN_LETTERBOX_FILL_COUNT)
|
||||||
|
? fillModeOverride
|
||||||
|
: SETTINGS.sleepScreenLetterboxFill;
|
||||||
const bool wantFill = (fillMode != CrossPointSettings::SLEEP_SCREEN_LETTERBOX_FILL::LETTERBOX_NONE);
|
const bool wantFill = (fillMode != CrossPointSettings::SLEEP_SCREEN_LETTERBOX_FILL::LETTERBOX_NONE);
|
||||||
|
|
||||||
static const char* fillModeNames[] = {"dithered", "solid", "none"};
|
static const char* fillModeNames[] = {"dithered", "solid", "none"};
|
||||||
@@ -582,6 +587,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string coverBmpPath;
|
std::string coverBmpPath;
|
||||||
|
std::string bookCachePath;
|
||||||
bool cropped = SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP;
|
bool cropped = SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP;
|
||||||
|
|
||||||
// Check if the current book is XTC, TXT, or EPUB
|
// Check if the current book is XTC, TXT, or EPUB
|
||||||
@@ -600,6 +606,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
coverBmpPath = lastXtc.getCoverBmpPath();
|
coverBmpPath = lastXtc.getCoverBmpPath();
|
||||||
|
bookCachePath = lastXtc.getCachePath();
|
||||||
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".txt")) {
|
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".txt")) {
|
||||||
// Handle TXT file - looks for cover image in the same folder
|
// Handle TXT file - looks for cover image in the same folder
|
||||||
Txt lastTxt(APP_STATE.openEpubPath, "/.crosspoint");
|
Txt lastTxt(APP_STATE.openEpubPath, "/.crosspoint");
|
||||||
@@ -614,6 +621,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
coverBmpPath = lastTxt.getCoverBmpPath();
|
coverBmpPath = lastTxt.getCoverBmpPath();
|
||||||
|
bookCachePath = lastTxt.getCachePath();
|
||||||
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".epub")) {
|
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".epub")) {
|
||||||
// Handle EPUB file
|
// Handle EPUB file
|
||||||
Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint");
|
Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint");
|
||||||
@@ -629,10 +637,18 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
coverBmpPath = lastEpub.getCoverBmpPath(cropped);
|
coverBmpPath = lastEpub.getCoverBmpPath(cropped);
|
||||||
|
bookCachePath = lastEpub.getCachePath();
|
||||||
} else {
|
} else {
|
||||||
return (this->*renderNoCoverSleepScreen)();
|
return (this->*renderNoCoverSleepScreen)();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load per-book letterbox fill override (falls back to global if not set)
|
||||||
|
uint8_t fillModeOverride = BookSettings::USE_GLOBAL;
|
||||||
|
if (!bookCachePath.empty()) {
|
||||||
|
auto bookSettings = BookSettings::load(bookCachePath);
|
||||||
|
fillModeOverride = bookSettings.letterboxFillOverride;
|
||||||
|
}
|
||||||
|
|
||||||
FsFile file;
|
FsFile file;
|
||||||
if (Storage.openFileForRead("SLP", coverBmpPath, file)) {
|
if (Storage.openFileForRead("SLP", coverBmpPath, file)) {
|
||||||
Bitmap bitmap(file);
|
Bitmap bitmap(file);
|
||||||
@@ -644,7 +660,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
if (dotPos != std::string::npos) {
|
if (dotPos != std::string::npos) {
|
||||||
edgeCachePath = coverBmpPath.substr(0, dotPos) + "_edges.bin";
|
edgeCachePath = coverBmpPath.substr(0, dotPos) + "_edges.bin";
|
||||||
}
|
}
|
||||||
renderBitmapSleepScreen(bitmap, edgeCachePath);
|
renderBitmapSleepScreen(bitmap, edgeCachePath, fillModeOverride);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class SleepActivity final : public Activity {
|
|||||||
void renderDefaultSleepScreen() const;
|
void renderDefaultSleepScreen() const;
|
||||||
void renderCustomSleepScreen() const;
|
void renderCustomSleepScreen() const;
|
||||||
void renderCoverSleepScreen() const;
|
void renderCoverSleepScreen() const;
|
||||||
void renderBitmapSleepScreen(const Bitmap& bitmap, const std::string& edgeCachePath = "") const;
|
// fillModeOverride: 0xFF = use global setting, otherwise a SLEEP_SCREEN_LETTERBOX_FILL value.
|
||||||
|
void renderBitmapSleepScreen(const Bitmap& bitmap, const std::string& edgeCachePath = "",
|
||||||
|
uint8_t fillModeOverride = 0xFF) const;
|
||||||
void renderBlankSleepScreen() const;
|
void renderBlankSleepScreen() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ void EpubReaderActivity::loop() {
|
|||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new EpubReaderMenuActivity(
|
enterNewActivity(new EpubReaderMenuActivity(
|
||||||
this->renderer, this->mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent,
|
this->renderer, this->mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent,
|
||||||
SETTINGS.orientation, hasDictionary, isBookmarked,
|
SETTINGS.orientation, hasDictionary, isBookmarked, epub->getCachePath(),
|
||||||
[this](const uint8_t orientation) { onReaderMenuBack(orientation); },
|
[this](const uint8_t orientation) { onReaderMenuBack(orientation); },
|
||||||
[this](EpubReaderMenuActivity::MenuAction action) { onReaderMenuConfirm(action); }));
|
[this](EpubReaderMenuActivity::MenuAction action) { onReaderMenuConfirm(action); }));
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
@@ -776,6 +776,10 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Handled locally in the menu activity (cycle on Confirm, never dispatched here)
|
||||||
|
case EpubReaderMenuActivity::MenuAction::ROTATE_SCREEN:
|
||||||
|
case EpubReaderMenuActivity::MenuAction::LETTERBOX_FILL:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ void EpubReaderMenuActivity::loop() {
|
|||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (selectedAction == MenuAction::LETTERBOX_FILL) {
|
||||||
|
// Cycle through: Default -> Dithered -> Solid -> None -> Default ...
|
||||||
|
int idx = (letterboxFillToIndex() + 1) % LETTERBOX_FILL_OPTION_COUNT;
|
||||||
|
pendingLetterboxFill = indexToLetterboxFill(idx);
|
||||||
|
saveLetterboxFill();
|
||||||
|
updateRequired = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Capture the callback and action locally
|
// 1. Capture the callback and action locally
|
||||||
auto actionCallback = onAction;
|
auto actionCallback = onAction;
|
||||||
@@ -139,6 +147,12 @@ void EpubReaderMenuActivity::renderScreen() {
|
|||||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
|
||||||
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
|
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
|
||||||
}
|
}
|
||||||
|
if (menuItems[i].action == MenuAction::LETTERBOX_FILL) {
|
||||||
|
// Render current letterbox fill value on the right edge of the content area.
|
||||||
|
const auto value = letterboxFillLabels[letterboxFillToIndex()];
|
||||||
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
|
||||||
|
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Footer / Hints
|
// Footer / Hints
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "../ActivityWithSubactivity.h"
|
#include "../ActivityWithSubactivity.h"
|
||||||
|
#include "util/BookSettings.h"
|
||||||
#include "util/ButtonNavigator.h"
|
#include "util/ButtonNavigator.h"
|
||||||
|
|
||||||
class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
||||||
@@ -20,6 +21,7 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
|||||||
LOOKUP,
|
LOOKUP,
|
||||||
LOOKED_UP_WORDS,
|
LOOKED_UP_WORDS,
|
||||||
ROTATE_SCREEN,
|
ROTATE_SCREEN,
|
||||||
|
LETTERBOX_FILL,
|
||||||
SELECT_CHAPTER,
|
SELECT_CHAPTER,
|
||||||
GO_TO_BOOKMARK,
|
GO_TO_BOOKMARK,
|
||||||
GO_TO_PERCENT,
|
GO_TO_PERCENT,
|
||||||
@@ -32,18 +34,23 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
|||||||
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
||||||
const int currentPage, const int totalPages, const int bookProgressPercent,
|
const int currentPage, const int totalPages, const int bookProgressPercent,
|
||||||
const uint8_t currentOrientation, const bool hasDictionary,
|
const uint8_t currentOrientation, const bool hasDictionary,
|
||||||
const bool isBookmarked,
|
const bool isBookmarked, const std::string& bookCachePath,
|
||||||
const std::function<void(uint8_t)>& onBack,
|
const std::function<void(uint8_t)>& onBack,
|
||||||
const std::function<void(MenuAction)>& onAction)
|
const std::function<void(MenuAction)>& onAction)
|
||||||
: ActivityWithSubactivity("EpubReaderMenu", renderer, mappedInput),
|
: ActivityWithSubactivity("EpubReaderMenu", renderer, mappedInput),
|
||||||
menuItems(buildMenuItems(hasDictionary, isBookmarked)),
|
menuItems(buildMenuItems(hasDictionary, isBookmarked)),
|
||||||
title(title),
|
title(title),
|
||||||
pendingOrientation(currentOrientation),
|
pendingOrientation(currentOrientation),
|
||||||
|
bookCachePath(bookCachePath),
|
||||||
currentPage(currentPage),
|
currentPage(currentPage),
|
||||||
totalPages(totalPages),
|
totalPages(totalPages),
|
||||||
bookProgressPercent(bookProgressPercent),
|
bookProgressPercent(bookProgressPercent),
|
||||||
onBack(onBack),
|
onBack(onBack),
|
||||||
onAction(onAction) {}
|
onAction(onAction) {
|
||||||
|
// Load per-book settings to initialize the letterbox fill override
|
||||||
|
auto bookSettings = BookSettings::load(bookCachePath);
|
||||||
|
pendingLetterboxFill = bookSettings.letterboxFillOverride;
|
||||||
|
}
|
||||||
|
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
void onExit() override;
|
void onExit() override;
|
||||||
@@ -65,6 +72,11 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
|||||||
std::string title = "Reader Menu";
|
std::string title = "Reader Menu";
|
||||||
uint8_t pendingOrientation = 0;
|
uint8_t pendingOrientation = 0;
|
||||||
const std::vector<const char*> orientationLabels = {"Portrait", "Landscape CW", "Inverted", "Landscape CCW"};
|
const std::vector<const char*> orientationLabels = {"Portrait", "Landscape CW", "Inverted", "Landscape CCW"};
|
||||||
|
std::string bookCachePath;
|
||||||
|
// Letterbox fill override: 0xFF = Default (use global), 0 = Dithered, 1 = Solid, 2 = None
|
||||||
|
uint8_t pendingLetterboxFill = BookSettings::USE_GLOBAL;
|
||||||
|
static constexpr int LETTERBOX_FILL_OPTION_COUNT = 4; // Default + 3 modes
|
||||||
|
const std::vector<const char*> letterboxFillLabels = {"Default", "Dithered", "Solid", "None"};
|
||||||
int currentPage = 0;
|
int currentPage = 0;
|
||||||
int totalPages = 0;
|
int totalPages = 0;
|
||||||
int bookProgressPercent = 0;
|
int bookProgressPercent = 0;
|
||||||
@@ -72,6 +84,25 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
|||||||
const std::function<void(uint8_t)> onBack;
|
const std::function<void(uint8_t)> onBack;
|
||||||
const std::function<void(MenuAction)> onAction;
|
const std::function<void(MenuAction)> onAction;
|
||||||
|
|
||||||
|
// Map the internal override value to an index into letterboxFillLabels.
|
||||||
|
int letterboxFillToIndex() const {
|
||||||
|
if (pendingLetterboxFill == BookSettings::USE_GLOBAL) return 0; // "Default"
|
||||||
|
return pendingLetterboxFill + 1; // 0->1 (Dithered), 1->2 (Solid), 2->3 (None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map an index from letterboxFillLabels back to an override value.
|
||||||
|
static uint8_t indexToLetterboxFill(int index) {
|
||||||
|
if (index == 0) return BookSettings::USE_GLOBAL;
|
||||||
|
return static_cast<uint8_t>(index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the current letterbox fill override to the book's settings file.
|
||||||
|
void saveLetterboxFill() const {
|
||||||
|
auto bookSettings = BookSettings::load(bookCachePath);
|
||||||
|
bookSettings.letterboxFillOverride = pendingLetterboxFill;
|
||||||
|
BookSettings::save(bookCachePath, bookSettings);
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<MenuItem> buildMenuItems(bool hasDictionary, bool isBookmarked) {
|
static std::vector<MenuItem> buildMenuItems(bool hasDictionary, bool isBookmarked) {
|
||||||
std::vector<MenuItem> items;
|
std::vector<MenuItem> items;
|
||||||
if (isBookmarked) {
|
if (isBookmarked) {
|
||||||
@@ -84,6 +115,7 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
|
|||||||
items.push_back({MenuAction::LOOKED_UP_WORDS, "Lookup Word History"});
|
items.push_back({MenuAction::LOOKED_UP_WORDS, "Lookup Word History"});
|
||||||
}
|
}
|
||||||
items.push_back({MenuAction::ROTATE_SCREEN, "Reading Orientation"});
|
items.push_back({MenuAction::ROTATE_SCREEN, "Reading Orientation"});
|
||||||
|
items.push_back({MenuAction::LETTERBOX_FILL, "Letterbox Fill"});
|
||||||
items.push_back({MenuAction::SELECT_CHAPTER, "Table of Contents"});
|
items.push_back({MenuAction::SELECT_CHAPTER, "Table of Contents"});
|
||||||
items.push_back({MenuAction::GO_TO_BOOKMARK, "Go to Bookmark"});
|
items.push_back({MenuAction::GO_TO_BOOKMARK, "Go to Bookmark"});
|
||||||
items.push_back({MenuAction::GO_TO_PERCENT, "Go to %"});
|
items.push_back({MenuAction::GO_TO_PERCENT, "Go to %"});
|
||||||
|
|||||||
60
src/util/BookSettings.cpp
Normal file
60
src/util/BookSettings.cpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include "BookSettings.h"
|
||||||
|
|
||||||
|
#include <HalStorage.h>
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <Serialization.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr uint8_t BOOK_SETTINGS_VERSION = 1;
|
||||||
|
constexpr uint8_t BOOK_SETTINGS_COUNT = 1; // Number of persisted fields
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::string BookSettings::filePath(const std::string& cachePath) { return cachePath + "/book_settings.bin"; }
|
||||||
|
|
||||||
|
BookSettings BookSettings::load(const std::string& cachePath) {
|
||||||
|
BookSettings settings;
|
||||||
|
FsFile f;
|
||||||
|
if (!Storage.openFileForRead("BST", filePath(cachePath), f)) {
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t version;
|
||||||
|
serialization::readPod(f, version);
|
||||||
|
if (version != BOOK_SETTINGS_VERSION) {
|
||||||
|
f.close();
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t fieldCount;
|
||||||
|
serialization::readPod(f, fieldCount);
|
||||||
|
|
||||||
|
// Read fields that exist (supports older files with fewer fields)
|
||||||
|
uint8_t fieldsRead = 0;
|
||||||
|
do {
|
||||||
|
serialization::readPod(f, settings.letterboxFillOverride);
|
||||||
|
if (++fieldsRead >= fieldCount) break;
|
||||||
|
// New fields added here for forward compatibility
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
f.close();
|
||||||
|
Serial.printf("[%lu] [BST] Loaded book settings from %s (letterboxFill=%d)\n", millis(), filePath(cachePath).c_str(),
|
||||||
|
settings.letterboxFillOverride);
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BookSettings::save(const std::string& cachePath, const BookSettings& settings) {
|
||||||
|
FsFile f;
|
||||||
|
if (!Storage.openFileForWrite("BST", filePath(cachePath), f)) {
|
||||||
|
Serial.printf("[%lu] [BST] Could not save book settings!\n", millis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialization::writePod(f, BOOK_SETTINGS_VERSION);
|
||||||
|
serialization::writePod(f, BOOK_SETTINGS_COUNT);
|
||||||
|
serialization::writePod(f, settings.letterboxFillOverride);
|
||||||
|
// New fields added here
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [BST] Saved book settings to %s\n", millis(), filePath(cachePath).c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
31
src/util/BookSettings.h
Normal file
31
src/util/BookSettings.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
|
|
||||||
|
// Per-book settings stored in the book's cache directory.
|
||||||
|
// Fields default to sentinel values (0xFF) meaning "use global setting".
|
||||||
|
class BookSettings {
|
||||||
|
public:
|
||||||
|
// 0xFF = use global default; otherwise one of SLEEP_SCREEN_LETTERBOX_FILL values (0-2).
|
||||||
|
uint8_t letterboxFillOverride = USE_GLOBAL;
|
||||||
|
|
||||||
|
static constexpr uint8_t USE_GLOBAL = 0xFF;
|
||||||
|
|
||||||
|
// Returns the effective letterbox fill mode: the per-book override if set,
|
||||||
|
// otherwise the global setting from CrossPointSettings.
|
||||||
|
uint8_t getEffectiveLetterboxFill() const {
|
||||||
|
if (letterboxFillOverride != USE_GLOBAL &&
|
||||||
|
letterboxFillOverride < CrossPointSettings::SLEEP_SCREEN_LETTERBOX_FILL_COUNT) {
|
||||||
|
return letterboxFillOverride;
|
||||||
|
}
|
||||||
|
return SETTINGS.sleepScreenLetterboxFill;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BookSettings load(const std::string& cachePath);
|
||||||
|
static bool save(const std::string& cachePath, const BookSettings& settings);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string filePath(const std::string& cachePath);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user