mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 22:57:50 +03:00
feat: add sleep screen selection
This commit is contained in:
parent
da142c362e
commit
d4036bf1f7
@ -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<int>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@ -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<std::string(size_t)> getItemText;
|
||||
std::function<void(size_t)> onItemSelected;
|
||||
std::function<void()> onBack;
|
||||
std::function<void(size_t, int, int, bool)> 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<void(size_t, int, int, bool)> renderer) {
|
||||
customRenderItem = renderer;
|
||||
}
|
||||
};
|
||||
|
||||
@ -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<std::string> files;
|
||||
char name[500];
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "../Activity.h"
|
||||
#include <SDCardManager.h>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
#include "../ListSelectionActivity.h"
|
||||
|
||||
class RefreshFrequencySelectionActivity final : public ListSelectionActivity {
|
||||
std::vector<std::string> options; // Refresh frequency options
|
||||
std::vector<std::string> options;
|
||||
|
||||
protected:
|
||||
void loadItems() override; // Called by base class onEnter
|
||||
void loadItems() override;
|
||||
|
||||
public:
|
||||
explicit RefreshFrequencySelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "ScreenMarginSelectionActivity.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
#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<uint8_t>((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<size_t>(idx) < options.size()) {
|
||||
selectorIndex = static_cast<size_t>(idx);
|
||||
} else {
|
||||
selectorIndex = 0; // Default to "5 px"
|
||||
selectorIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
#include "../ListSelectionActivity.h"
|
||||
|
||||
class ScreenMarginSelectionActivity final : public ListSelectionActivity {
|
||||
std::vector<std::string> options; // Screen margin options
|
||||
std::vector<std::string> options;
|
||||
|
||||
protected:
|
||||
void loadItems() override; // Called by base class onEnter
|
||||
void loadItems() override;
|
||||
|
||||
public:
|
||||
explicit ScreenMarginSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
#include "CategorySettingsActivity.h"
|
||||
#include "CrossPointSettings.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "OtaUpdateActivity.h"
|
||||
#include "SleepBmpSelectionActivity.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
|
||||
@ -6,11 +6,11 @@
|
||||
#include "../ListSelectionActivity.h"
|
||||
|
||||
class SleepBmpSelectionActivity final : public ListSelectionActivity {
|
||||
std::vector<std::string> files; // Sorted list of valid BMP filenames ("Random" at index 0)
|
||||
void loadFiles(); // Load and sort all valid BMP files
|
||||
std::vector<std::string> files;
|
||||
void loadFiles();
|
||||
|
||||
protected:
|
||||
void loadItems() override; // Called by base class onEnter
|
||||
void loadItems() override;
|
||||
|
||||
public:
|
||||
explicit SleepBmpSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||
|
||||
@ -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<uint8_t>(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 {
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
#include "../ListSelectionActivity.h"
|
||||
|
||||
class SleepScreenSelectionActivity final : public ListSelectionActivity {
|
||||
std::vector<std::string> options; // Sleep screen mode options
|
||||
std::vector<std::string> options;
|
||||
|
||||
protected:
|
||||
void loadItems() override; // Called by base class onEnter
|
||||
void loadItems() override;
|
||||
|
||||
public:
|
||||
explicit SleepScreenSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||
|
||||
@ -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<uint8_t>(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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user