From d4036bf1f76d34f2a75f2c5f4d2793ff97181275 Mon Sep 17 00:00:00 2001 From: dpoulter Date: Sun, 1 Feb 2026 11:08:35 +0200 Subject: [PATCH] feat: add sleep screen selection --- src/CrossPointSettings.cpp | 31 ++++++++++----- src/CrossPointSettings.h | 23 +++++++++-- src/activities/ListSelectionActivity.cpp | 12 ++---- src/activities/ListSelectionActivity.h | 9 +---- src/activities/boot_sleep/SleepActivity.cpp | 38 ++++++++++--------- src/activities/boot_sleep/SleepActivity.h | 2 + .../RefreshFrequencySelectionActivity.cpp | 4 +- .../RefreshFrequencySelectionActivity.h | 4 +- .../ScreenMarginSelectionActivity.cpp | 27 ++++--------- .../settings/ScreenMarginSelectionActivity.h | 4 +- src/activities/settings/SettingsActivity.cpp | 1 - .../settings/SleepBmpSelectionActivity.h | 6 +-- .../settings/SleepScreenSelectionActivity.cpp | 6 +-- .../settings/SleepScreenSelectionActivity.h | 4 +- .../SleepTimeoutSelectionActivity.cpp | 6 +-- 15 files changed, 87 insertions(+), 90 deletions(-) diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index 8914384b..4348b516 100644 --- a/src/CrossPointSettings.cpp +++ b/src/CrossPointSettings.cpp @@ -290,6 +290,12 @@ const char* const SLEEP_TIMEOUT_OPTIONS[] = { TIMEOUT_DATA #undef X }; + +const char* const SCREEN_MARGIN_OPTIONS[] = { + #define X(val, str) str, + SCREEN_MARGIN_DATA + #undef X +}; } // namespace const char* CrossPointSettings::getRefreshFrequencyString(uint8_t value) { @@ -299,10 +305,6 @@ const char* CrossPointSettings::getRefreshFrequencyString(uint8_t value) { return REFRESH_FREQUENCY_OPTIONS[REFRESH_15]; } -size_t CrossPointSettings::getRefreshFrequencyCount() { - return REFRESH_FREQUENCY_COUNT; -} - const char* CrossPointSettings::getSleepScreenString(uint8_t value) { if (value < SLEEP_SCREEN_MODE_COUNT) { return SLEEP_SCREEN_OPTIONS[value]; @@ -310,10 +312,6 @@ const char* CrossPointSettings::getSleepScreenString(uint8_t value) { return SLEEP_SCREEN_OPTIONS[DARK]; } -size_t CrossPointSettings::getSleepScreenCount() { - return SLEEP_SCREEN_MODE_COUNT; -} - const char* CrossPointSettings::getSleepTimeoutString(uint8_t value) { if (value < SLEEP_TIMEOUT_COUNT) { return SLEEP_TIMEOUT_OPTIONS[value]; @@ -321,6 +319,19 @@ const char* CrossPointSettings::getSleepTimeoutString(uint8_t value) { return SLEEP_TIMEOUT_OPTIONS[SLEEP_10_MIN]; } -size_t CrossPointSettings::getSleepTimeoutCount() { - return SLEEP_TIMEOUT_COUNT; +const char* CrossPointSettings::getScreenMarginString(uint8_t index) { + if (index < SCREEN_MARGIN_COUNT) { + return SCREEN_MARGIN_OPTIONS[index]; + } + return SCREEN_MARGIN_OPTIONS[MARGIN_5]; } + +int CrossPointSettings::getScreenMarginIndex(uint8_t pixelValue) { + for (size_t i = 0; i < SCREEN_MARGIN_COUNT; i++) { + if (SCREEN_MARGIN_VALUES[i] == pixelValue) { + return static_cast(i); + } + } + return -1; +} + diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 48a08931..bdb8221f 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -113,6 +113,23 @@ class CrossPointSettings { SLEEP_TIMEOUT_COUNT }; + // Reader screen margin options (pixel values) + #define SCREEN_MARGIN_DATA \ + X(5, "5 px") X(10, "10 px") X(15, "15 px") X(20, "20 px") \ + X(25, "25 px") X(30, "30 px") X(35, "35 px") X(40, "40 px") + + enum SCREEN_MARGIN { + #define X(val, str) MARGIN_##val, + SCREEN_MARGIN_DATA + #undef X + SCREEN_MARGIN_COUNT + }; + static inline constexpr uint8_t SCREEN_MARGIN_VALUES[SCREEN_MARGIN_COUNT] = { + #define X(val, str) val, + SCREEN_MARGIN_DATA + #undef X + }; + // Short power button press actions enum SHORT_PWRBTN { IGNORE = 0, SLEEP = 1, PAGE_TURN = 2, SHORT_PWRBTN_COUNT }; @@ -177,11 +194,11 @@ class CrossPointSettings { // Helper functions to get option strings from enum values static const char* getRefreshFrequencyString(uint8_t value); - static size_t getRefreshFrequencyCount(); static const char* getSleepScreenString(uint8_t value); - static size_t getSleepScreenCount(); static const char* getSleepTimeoutString(uint8_t value); - static size_t getSleepTimeoutCount(); + static const char* getScreenMarginString(uint8_t index); + /** Returns index for pixel value, or -1 if not in allowed list. */ + static int getScreenMarginIndex(uint8_t pixelValue); float getReaderLineCompression() const; unsigned long getSleepTimeoutMs() const; diff --git a/src/activities/ListSelectionActivity.cpp b/src/activities/ListSelectionActivity.cpp index e562a441..bb0051e3 100644 --- a/src/activities/ListSelectionActivity.cpp +++ b/src/activities/ListSelectionActivity.cpp @@ -146,15 +146,9 @@ void ListSelectionActivity::render() const { const bool isSelected = (i == selectorIndex); const int itemY = START_Y + visibleIndex * LINE_HEIGHT; - if (customRenderItem) { - // Use custom renderer if provided - customRenderItem(i, 20, itemY, isSelected); - } else { - // Default rendering: truncate text and draw - const std::string itemText = getItemText(i); - auto truncated = renderer.truncatedText(UI_10_FONT_ID, itemText.c_str(), pageWidth - 40); - renderer.drawText(UI_10_FONT_ID, 20, itemY, truncated.c_str(), !isSelected); - } + const std::string itemText = getItemText(i); + auto truncated = renderer.truncatedText(UI_10_FONT_ID, itemText.c_str(), pageWidth - 40); + renderer.drawText(UI_10_FONT_ID, 20, itemY, truncated.c_str(), !isSelected); visibleIndex++; } diff --git a/src/activities/ListSelectionActivity.h b/src/activities/ListSelectionActivity.h index e2595b07..a583bec9 100644 --- a/src/activities/ListSelectionActivity.h +++ b/src/activities/ListSelectionActivity.h @@ -17,7 +17,6 @@ * - Automatic pagination based on screen size * - Page skipping when holding navigation buttons * - Configurable title, empty message, and button labels - * - Customizable item rendering */ class ListSelectionActivity : public Activity { protected: @@ -36,8 +35,7 @@ class ListSelectionActivity : public Activity { std::function getItemText; std::function onItemSelected; std::function onBack; - std::function customRenderItem; // index, x, y, isSelected - + // Constants static constexpr int SKIP_PAGE_MS = 700; static constexpr unsigned long IGNORE_INPUT_MS = 300; @@ -80,9 +78,4 @@ class ListSelectionActivity : public Activity { // Allow subclasses to set initial selection void setInitialSelection(size_t index) { selectorIndex = index; } size_t getCurrentSelection() const { return selectorIndex; } - - // Allow custom item rendering - void setCustomItemRenderer(std::function renderer) { - customRenderItem = renderer; - } }; diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index de6b939f..4be8925d 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -45,26 +45,30 @@ void SleepActivity::renderPopup(const char* message) const { renderer.displayBuffer(); } +bool SleepActivity::renderSelectedSleepBmp(FsFile& dir) const { + if (SETTINGS.selectedSleepBmp[0] == '\0') return false; + const std::string selectedFile = std::string(SETTINGS.selectedSleepBmp); + const std::string filename = "/sleep/" + selectedFile; + FsFile file; + if (!SdMan.openFileForRead("SLP", filename, file)) { + Serial.printf("[%lu] [SLP] Selected BMP not found or invalid, falling back to random\n", millis()); + return false; + } + Bitmap bitmap(file); + if (bitmap.parseHeaders() != BmpReaderError::Ok) { + file.close(); + Serial.printf("[%lu] [SLP] Selected BMP not found or invalid, falling back to random\n", millis()); + return false; + } + renderBitmapSleepScreen(bitmap); + dir.close(); + return true; +} + void SleepActivity::renderCustomSleepScreen() const { auto dir = SdMan.open("/sleep"); if (dir && dir.isDirectory()) { - if (SETTINGS.selectedSleepBmp[0] != '\0') { - const std::string selectedFile = std::string(SETTINGS.selectedSleepBmp); - const std::string filename = "/sleep/" + selectedFile; - FsFile file; - if (SdMan.openFileForRead("SLP", filename, file)) { - Bitmap bitmap(file); - if (bitmap.parseHeaders() == BmpReaderError::Ok) { - Serial.printf("[%lu] [SLP] Loading selected: /sleep/%s\n", millis(), selectedFile.c_str()); - delay(100); - renderBitmapSleepScreen(bitmap); - dir.close(); - return; - } - file.close(); - } - Serial.printf("[%lu] [SLP] Selected BMP not found or invalid, falling back to random\n", millis()); - } + if (renderSelectedSleepBmp(dir)) return; std::vector files; char name[500]; diff --git a/src/activities/boot_sleep/SleepActivity.h b/src/activities/boot_sleep/SleepActivity.h index 283220ce..175ef794 100644 --- a/src/activities/boot_sleep/SleepActivity.h +++ b/src/activities/boot_sleep/SleepActivity.h @@ -1,5 +1,6 @@ #pragma once #include "../Activity.h" +#include class Bitmap; @@ -16,4 +17,5 @@ class SleepActivity final : public Activity { void renderCoverSleepScreen() const; void renderBitmapSleepScreen(const Bitmap& bitmap) const; void renderBlankSleepScreen() const; + bool renderSelectedSleepBmp(FsFile& dir) const; }; diff --git a/src/activities/settings/RefreshFrequencySelectionActivity.cpp b/src/activities/settings/RefreshFrequencySelectionActivity.cpp index c8205363..461feeb4 100644 --- a/src/activities/settings/RefreshFrequencySelectionActivity.cpp +++ b/src/activities/settings/RefreshFrequencySelectionActivity.cpp @@ -21,14 +21,12 @@ RefreshFrequencySelectionActivity::RefreshFrequencySelectionActivity(GfxRenderer }, onBack, "No options available") { // Initialize options from enum - for (uint8_t i = 0; i < CrossPointSettings::getRefreshFrequencyCount(); i++) { + for (uint8_t i = 0; i < CrossPointSettings::REFRESH_FREQUENCY_COUNT; i++) { options.push_back(CrossPointSettings::getRefreshFrequencyString(i)); } } void RefreshFrequencySelectionActivity::loadItems() { - // Options are already set in constructor, just set initial selection - // Map current enum value to option index if (SETTINGS.refreshFrequency < options.size()) { selectorIndex = SETTINGS.refreshFrequency; } else { diff --git a/src/activities/settings/RefreshFrequencySelectionActivity.h b/src/activities/settings/RefreshFrequencySelectionActivity.h index b4c31cb9..427a8c02 100644 --- a/src/activities/settings/RefreshFrequencySelectionActivity.h +++ b/src/activities/settings/RefreshFrequencySelectionActivity.h @@ -6,10 +6,10 @@ #include "../ListSelectionActivity.h" class RefreshFrequencySelectionActivity final : public ListSelectionActivity { - std::vector options; // Refresh frequency options + std::vector options; protected: - void loadItems() override; // Called by base class onEnter + void loadItems() override; public: explicit RefreshFrequencySelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, diff --git a/src/activities/settings/ScreenMarginSelectionActivity.cpp b/src/activities/settings/ScreenMarginSelectionActivity.cpp index 1e891d5c..4ab70d4c 100644 --- a/src/activities/settings/ScreenMarginSelectionActivity.cpp +++ b/src/activities/settings/ScreenMarginSelectionActivity.cpp @@ -1,7 +1,6 @@ #include "ScreenMarginSelectionActivity.h" #include -#include #include "CrossPointSettings.h" @@ -15,33 +14,21 @@ ScreenMarginSelectionActivity::ScreenMarginSelectionActivity(GfxRenderer& render if (index >= options.size()) { return; } - // Map option index to margin value - // Options: "5 px", "10 px", "15 px", "20 px", "25 px", "30 px", "35 px", "40 px" - // Values: 5, 10, 15, 20, 25, 30, 35, 40 - SETTINGS.screenMargin = static_cast((index + 1) * 5); + SETTINGS.screenMargin = CrossPointSettings::SCREEN_MARGIN_VALUES[index]; SETTINGS.saveToFile(); onBack(); }, onBack, "No options available") { - // Initialize options: 5 to 40 in steps of 5 - for (int i = 5; i <= 40; i += 5) { - std::ostringstream oss; - oss << i << " px"; - options.push_back(oss.str()); + for (uint8_t i = 0; i < CrossPointSettings::SCREEN_MARGIN_COUNT; i++) { + options.push_back(CrossPointSettings::getScreenMarginString(i)); } } void ScreenMarginSelectionActivity::loadItems() { - // Options are already set in constructor, just set initial selection - // Map current margin value to option index - // margin value / 5 - 1 = index (e.g., 5 -> 0, 10 -> 1, etc.) - if (SETTINGS.screenMargin >= 5 && SETTINGS.screenMargin <= 40) { - selectorIndex = (SETTINGS.screenMargin / 5) - 1; - // Ensure index is within bounds - if (selectorIndex >= options.size()) { - selectorIndex = 0; // Default to "5 px" - } + const int idx = CrossPointSettings::getScreenMarginIndex(SETTINGS.screenMargin); + if (idx >= 0 && static_cast(idx) < options.size()) { + selectorIndex = static_cast(idx); } else { - selectorIndex = 0; // Default to "5 px" + selectorIndex = 0; } } diff --git a/src/activities/settings/ScreenMarginSelectionActivity.h b/src/activities/settings/ScreenMarginSelectionActivity.h index 8178ce92..f1169cd6 100644 --- a/src/activities/settings/ScreenMarginSelectionActivity.h +++ b/src/activities/settings/ScreenMarginSelectionActivity.h @@ -6,10 +6,10 @@ #include "../ListSelectionActivity.h" class ScreenMarginSelectionActivity final : public ListSelectionActivity { - std::vector options; // Screen margin options + std::vector options; protected: - void loadItems() override; // Called by base class onEnter + void loadItems() override; public: explicit ScreenMarginSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index ff4c4f26..87be2b87 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -11,7 +11,6 @@ #include "CategorySettingsActivity.h" #include "CrossPointSettings.h" #include "MappedInputManager.h" -#include "OtaUpdateActivity.h" #include "SleepBmpSelectionActivity.h" #include "fontIds.h" diff --git a/src/activities/settings/SleepBmpSelectionActivity.h b/src/activities/settings/SleepBmpSelectionActivity.h index f3ecddda..dbb86349 100644 --- a/src/activities/settings/SleepBmpSelectionActivity.h +++ b/src/activities/settings/SleepBmpSelectionActivity.h @@ -6,11 +6,11 @@ #include "../ListSelectionActivity.h" class SleepBmpSelectionActivity final : public ListSelectionActivity { - std::vector files; // Sorted list of valid BMP filenames ("Random" at index 0) - void loadFiles(); // Load and sort all valid BMP files + std::vector files; + void loadFiles(); protected: - void loadItems() override; // Called by base class onEnter + void loadItems() override; public: explicit SleepBmpSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, diff --git a/src/activities/settings/SleepScreenSelectionActivity.cpp b/src/activities/settings/SleepScreenSelectionActivity.cpp index b1417a9f..c3203630 100644 --- a/src/activities/settings/SleepScreenSelectionActivity.cpp +++ b/src/activities/settings/SleepScreenSelectionActivity.cpp @@ -14,21 +14,17 @@ SleepScreenSelectionActivity::SleepScreenSelectionActivity(GfxRenderer& renderer if (index >= options.size()) { return; } - // Map option index to enum value (index matches enum value) SETTINGS.sleepScreen = static_cast(index); SETTINGS.saveToFile(); onBack(); }, onBack, "No options available") { - // Initialize options from enum - for (uint8_t i = 0; i < CrossPointSettings::getSleepScreenCount(); i++) { + for (uint8_t i = 0; i < CrossPointSettings::SLEEP_SCREEN_MODE_COUNT; i++) { options.push_back(CrossPointSettings::getSleepScreenString(i)); } } void SleepScreenSelectionActivity::loadItems() { - // Options are already set in constructor, just set initial selection - // Map current enum value to option index if (SETTINGS.sleepScreen < options.size()) { selectorIndex = SETTINGS.sleepScreen; } else { diff --git a/src/activities/settings/SleepScreenSelectionActivity.h b/src/activities/settings/SleepScreenSelectionActivity.h index 1580d51a..fa18c37d 100644 --- a/src/activities/settings/SleepScreenSelectionActivity.h +++ b/src/activities/settings/SleepScreenSelectionActivity.h @@ -6,10 +6,10 @@ #include "../ListSelectionActivity.h" class SleepScreenSelectionActivity final : public ListSelectionActivity { - std::vector options; // Sleep screen mode options + std::vector options; protected: - void loadItems() override; // Called by base class onEnter + void loadItems() override; public: explicit SleepScreenSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, diff --git a/src/activities/settings/SleepTimeoutSelectionActivity.cpp b/src/activities/settings/SleepTimeoutSelectionActivity.cpp index 1a564b7c..866e99ba 100644 --- a/src/activities/settings/SleepTimeoutSelectionActivity.cpp +++ b/src/activities/settings/SleepTimeoutSelectionActivity.cpp @@ -14,21 +14,17 @@ SleepTimeoutSelectionActivity::SleepTimeoutSelectionActivity(GfxRenderer& render if (index >= options.size()) { return; } - // Map option index to enum value (index matches enum value) SETTINGS.sleepTimeout = static_cast(index); SETTINGS.saveToFile(); onBack(); }, onBack, "No options available") { - // Initialize options from enum - for (uint8_t i = 0; i < CrossPointSettings::getSleepTimeoutCount(); i++) { + for (uint8_t i = 0; i < CrossPointSettings::SLEEP_TIMEOUT_COUNT; i++) { options.push_back(CrossPointSettings::getSleepTimeoutString(i)); } } void SleepTimeoutSelectionActivity::loadItems() { - // Options are already set in constructor, just set initial selection - // Map current enum value to option index if (SETTINGS.sleepTimeout < options.size()) { selectorIndex = SETTINGS.sleepTimeout; } else {