feat(settings): add "Cover + Custom" sleep screen mode (#582)

## Summary

Allows to fallback to custom sleep screens if the book does not have a
cover.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< partially >**_

---------

Co-authored-by: mrtnvgr <root@unixis.fun>
This commit is contained in:
Егор Мартынов
2026-02-05 18:46:14 +07:00
committed by GitHub
parent edaf8fff9d
commit 23ecc52261
4 changed files with 41 additions and 23 deletions

View File

@@ -15,7 +15,15 @@ class CrossPointSettings {
CrossPointSettings(const CrossPointSettings&) = delete;
CrossPointSettings& operator=(const CrossPointSettings&) = delete;
enum SLEEP_SCREEN_MODE { DARK = 0, LIGHT = 1, CUSTOM = 2, COVER = 3, BLANK = 4, SLEEP_SCREEN_MODE_COUNT };
enum SLEEP_SCREEN_MODE {
DARK = 0,
LIGHT = 1,
CUSTOM = 2,
COVER = 3,
BLANK = 4,
COVER_CUSTOM = 5,
SLEEP_SCREEN_MODE_COUNT
};
enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1, SLEEP_SCREEN_COVER_MODE_COUNT };
enum SLEEP_SCREEN_COVER_FILTER {
NO_FILTER = 0,

View File

@@ -17,19 +17,17 @@ void SleepActivity::onEnter() {
Activity::onEnter();
GUI.drawPopup(renderer, "Entering Sleep...");
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) {
return renderBlankSleepScreen();
switch (SETTINGS.sleepScreen) {
case (CrossPointSettings::SLEEP_SCREEN_MODE::BLANK):
return renderBlankSleepScreen();
case (CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM):
return renderCustomSleepScreen();
case (CrossPointSettings::SLEEP_SCREEN_MODE::COVER):
case (CrossPointSettings::SLEEP_SCREEN_MODE::COVER_CUSTOM):
return renderCoverSleepScreen();
default:
return renderDefaultSleepScreen();
}
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM) {
return renderCustomSleepScreen();
}
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::COVER) {
return renderCoverSleepScreen();
}
renderDefaultSleepScreen();
}
void SleepActivity::renderCustomSleepScreen() const {
@@ -197,8 +195,18 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const {
}
void SleepActivity::renderCoverSleepScreen() const {
void (SleepActivity::*renderNoCoverSleepScreen)() const;
switch (SETTINGS.sleepScreen) {
case (CrossPointSettings::SLEEP_SCREEN_MODE::COVER_CUSTOM):
renderNoCoverSleepScreen = &SleepActivity::renderCustomSleepScreen;
break;
default:
renderNoCoverSleepScreen = &SleepActivity::renderDefaultSleepScreen;
break;
}
if (APP_STATE.openEpubPath.empty()) {
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
std::string coverBmpPath;
@@ -211,12 +219,12 @@ void SleepActivity::renderCoverSleepScreen() const {
Xtc lastXtc(APP_STATE.openEpubPath, "/.crosspoint");
if (!lastXtc.load()) {
Serial.println("[SLP] Failed to load last XTC");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
if (!lastXtc.generateCoverBmp()) {
Serial.println("[SLP] Failed to generate XTC cover bmp");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
coverBmpPath = lastXtc.getCoverBmpPath();
@@ -225,12 +233,12 @@ void SleepActivity::renderCoverSleepScreen() const {
Txt lastTxt(APP_STATE.openEpubPath, "/.crosspoint");
if (!lastTxt.load()) {
Serial.println("[SLP] Failed to load last TXT");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
if (!lastTxt.generateCoverBmp()) {
Serial.println("[SLP] No cover image found for TXT file");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
coverBmpPath = lastTxt.getCoverBmpPath();
@@ -240,17 +248,17 @@ void SleepActivity::renderCoverSleepScreen() const {
// Skip loading css since we only need metadata here
if (!lastEpub.load(true, true)) {
Serial.println("[SLP] Failed to load last epub");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
if (!lastEpub.generateCoverBmp(cropped)) {
Serial.println("[SLP] Failed to generate cover bmp");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
coverBmpPath = lastEpub.getCoverBmpPath(cropped);
} else {
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
FsFile file;
@@ -263,7 +271,7 @@ void SleepActivity::renderCoverSleepScreen() const {
}
}
renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
void SleepActivity::renderBlankSleepScreen() const {

View File

@@ -20,7 +20,8 @@ constexpr int changeTabsMs = 700;
constexpr int displaySettingsCount = 7;
const SettingInfo displaySettings[displaySettingsCount] = {
// Should match with SLEEP_SCREEN_MODE
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen,
{"Dark", "Light", "Custom", "Cover", "None", "Cover + Custom"}),
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}),
SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter,
{"None", "Contrast", "Inverted"}),