From 23ecc52261e44f0265de4b182881d4cb6c1c9b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B3=D0=BE=D1=80=20=D0=9C=D0=B0=D1=80=D1=82=D1=8B?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= Date: Thu, 5 Feb 2026 18:46:14 +0700 Subject: [PATCH] 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 --- USER_GUIDE.md | 1 + src/CrossPointSettings.h | 10 +++- src/activities/boot_sleep/SleepActivity.cpp | 50 ++++++++++++-------- src/activities/settings/SettingsActivity.cpp | 3 +- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/USER_GUIDE.md b/USER_GUIDE.md index f46ddd07..22c6a941 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -103,6 +103,7 @@ The Settings screen allows you to configure the device's behavior. There are a f - "Custom" - Custom images from the SD card; see [Sleep Screen](#36-sleep-screen) below for more information - "Cover" - The book cover image (Note: this is experimental and may not work as expected) - "None" - A blank screen + - "Cover + Custom" - The book cover image, fallbacks to "Custom" behavior - **Sleep Screen Cover Mode**: How to display the book cover when "Cover" sleep screen is selected: - "Fit" (default) - Scale the image down to fit centered on the screen, padding with white borders as necessary - "Crop" - Scale the image down and crop as necessary to try to to fill the screen (Note: this is experimental and may not work as expected) diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 656481a1..1d85e8d8 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -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, diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 6af6ff8e..f1ee92cd 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -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 { diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index dd769f79..5bf04bfd 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -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"}),