mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 14:47:37 +03:00
Merge 5ae10a7eb6 into 78d6e5931c
This commit is contained in:
commit
addd1c8617
@ -49,4 +49,12 @@ static void readString(FsFile& file, std::string& s) {
|
||||
s.resize(len);
|
||||
file.read(&s[0], len);
|
||||
}
|
||||
|
||||
static void readString(FsFile& file, char* buffer, size_t maxLen) {
|
||||
uint32_t len;
|
||||
readPod(file, len);
|
||||
const uint32_t bytesToRead = (len < maxLen - 1) ? len : (maxLen - 1);
|
||||
file.read(reinterpret_cast<uint8_t*>(buffer), bytesToRead);
|
||||
buffer[bytesToRead] = '\0';
|
||||
}
|
||||
} // namespace serialization
|
||||
|
||||
@ -11,21 +11,132 @@
|
||||
// Initialize the static instance
|
||||
CrossPointSettings CrossPointSettings::instance;
|
||||
|
||||
void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) {
|
||||
uint8_t tempValue;
|
||||
serialization::readPod(file, tempValue);
|
||||
if (tempValue < maxValue) {
|
||||
member = tempValue;
|
||||
// SettingDescriptor implementations
|
||||
bool SettingDescriptor::validate(const CrossPointSettings& settings) const {
|
||||
if (type == SettingType::STRING) {
|
||||
return true; // Strings are always valid
|
||||
}
|
||||
if (!validator) {
|
||||
return true; // No validator means always valid
|
||||
}
|
||||
const uint8_t value = settings.*(memberPtr);
|
||||
return validator(value);
|
||||
}
|
||||
|
||||
uint8_t SettingDescriptor::getValue(const CrossPointSettings& settings) const { return settings.*(memberPtr); }
|
||||
|
||||
void SettingDescriptor::setValue(CrossPointSettings& settings, uint8_t value) const { settings.*(memberPtr) = value; }
|
||||
|
||||
void SettingDescriptor::resetToDefault(CrossPointSettings& settings) const {
|
||||
if (type == SettingType::STRING) {
|
||||
strncpy(stringPtr, stringData.defaultString, stringData.maxSize - 1);
|
||||
stringPtr[stringData.maxSize - 1] = '\0';
|
||||
return;
|
||||
}
|
||||
setValue(settings, defaultValue);
|
||||
}
|
||||
|
||||
void SettingDescriptor::save(FsFile& file, const CrossPointSettings& settings) const {
|
||||
if (type == SettingType::STRING) {
|
||||
serialization::writeString(file, std::string(stringPtr));
|
||||
return;
|
||||
}
|
||||
serialization::writePod(file, settings.*(memberPtr));
|
||||
}
|
||||
|
||||
void SettingDescriptor::load(FsFile& file, CrossPointSettings& settings) const {
|
||||
if (type == SettingType::STRING) {
|
||||
serialization::readString(file, stringPtr, stringData.maxSize);
|
||||
return;
|
||||
}
|
||||
uint8_t value;
|
||||
serialization::readPod(file, value);
|
||||
settings.*(memberPtr) = value;
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||
// Increment this when adding new persisted settings fields
|
||||
constexpr uint8_t SETTINGS_COUNT = 23;
|
||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||
} // namespace
|
||||
|
||||
// Define enum value arrays
|
||||
namespace {
|
||||
constexpr const char* sleepScreenValues[] = {"Dark", "Light", "Custom", "Cover", "None"};
|
||||
constexpr const char* shortPwrBtnValues[] = {"Ignore", "Sleep", "Page Turn"};
|
||||
constexpr const char* statusBarValues[] = {"None", "No Progress", "Full"};
|
||||
constexpr const char* orientationValues[] = {"Portrait", "Landscape CW", "Inverted", "Landscape CCW"};
|
||||
constexpr const char* frontButtonLayoutValues[] = {"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm",
|
||||
"Lft, Bck, Cnfrm, Rght", "Bck, Cnfrm, Rght, Lft"};
|
||||
constexpr const char* sideButtonLayoutValues[] = {"Prev/Next", "Next/Prev"};
|
||||
constexpr const char* fontFamilyValues[] = {"Bookerly", "Noto Sans", "Open Dyslexic"};
|
||||
constexpr const char* fontSizeValues[] = {"Small", "Medium", "Large", "X Large"};
|
||||
constexpr const char* lineSpacingValues[] = {"Tight", "Normal", "Wide"};
|
||||
constexpr const char* paragraphAlignmentValues[] = {"Justify", "Left", "Center", "Right"};
|
||||
constexpr const char* sleepTimeoutValues[] = {"1 min", "5 min", "10 min", "15 min", "30 min"};
|
||||
constexpr const char* refreshFrequencyValues[] = {"1 page", "5 pages", "10 pages", "15 pages", "30 pages"};
|
||||
constexpr const char* sleepScreenCoverModeValues[] = {"Fit", "Crop"};
|
||||
constexpr const char* hideBatteryPercentageValues[] = {"Never", "In Reader", "Always"};
|
||||
constexpr const char* sleepScreenCoverFilterValues[] = {"None", "Contrast", "Inverted"};
|
||||
|
||||
// Helper function template to deduce array size automatically
|
||||
template <size_t N>
|
||||
constexpr SettingDescriptor makeEnumDescriptor(const char* name, uint8_t CrossPointSettings::* ptr,
|
||||
uint8_t defaultValue, const char* const (&enumValues)[N]) {
|
||||
return SettingDescriptor(name, SettingType::ENUM, ptr, defaultValue, validateEnum<N>, enumValues, N);
|
||||
}
|
||||
|
||||
// Helper macro to create STRING descriptors without repetition
|
||||
#define makeStringDescriptor(name, member, defStr) \
|
||||
SettingDescriptor(name, SettingType::STRING, CrossPointSettings::instance.member, defStr, \
|
||||
sizeof(CrossPointSettings::member))
|
||||
} // namespace
|
||||
|
||||
// Define static constexpr members (required in C++14 and earlier)
|
||||
constexpr size_t CrossPointSettings::DESCRIPTOR_COUNT;
|
||||
|
||||
// Define the static constexpr array of all setting descriptors
|
||||
// Order must match current serialization order for file format compatibility!
|
||||
const std::array<SettingDescriptor, CrossPointSettings::DESCRIPTOR_COUNT> CrossPointSettings::descriptors = {{
|
||||
makeEnumDescriptor("Sleep Screen", &CrossPointSettings::sleepScreen, CrossPointSettings::DARK, sleepScreenValues),
|
||||
{"Extra Paragraph Spacing", SettingType::TOGGLE, &CrossPointSettings::extraParagraphSpacing, 1, validateToggle,
|
||||
nullptr, 0},
|
||||
makeEnumDescriptor("Short Power Button Click", &CrossPointSettings::shortPwrBtn, CrossPointSettings::IGNORE,
|
||||
shortPwrBtnValues),
|
||||
makeEnumDescriptor("Status Bar", &CrossPointSettings::statusBar, CrossPointSettings::FULL, statusBarValues),
|
||||
makeEnumDescriptor("Reading Orientation", &CrossPointSettings::orientation, CrossPointSettings::PORTRAIT,
|
||||
orientationValues),
|
||||
makeEnumDescriptor("Front Button Layout", &CrossPointSettings::frontButtonLayout,
|
||||
CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT, frontButtonLayoutValues),
|
||||
makeEnumDescriptor("Side Button Layout", &CrossPointSettings::sideButtonLayout, CrossPointSettings::PREV_NEXT,
|
||||
sideButtonLayoutValues),
|
||||
makeEnumDescriptor("Reader Font Family", &CrossPointSettings::fontFamily, CrossPointSettings::BOOKERLY,
|
||||
fontFamilyValues),
|
||||
makeEnumDescriptor("Reader Font Size", &CrossPointSettings::fontSize, CrossPointSettings::MEDIUM, fontSizeValues),
|
||||
makeEnumDescriptor("Reader Line Spacing", &CrossPointSettings::lineSpacing, CrossPointSettings::NORMAL,
|
||||
lineSpacingValues),
|
||||
makeEnumDescriptor("Reader Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
|
||||
CrossPointSettings::JUSTIFIED, paragraphAlignmentValues),
|
||||
makeEnumDescriptor("Time to Sleep", &CrossPointSettings::sleepTimeout, CrossPointSettings::SLEEP_10_MIN,
|
||||
sleepTimeoutValues),
|
||||
makeEnumDescriptor("Refresh Frequency", &CrossPointSettings::refreshFrequency, CrossPointSettings::REFRESH_15,
|
||||
refreshFrequencyValues),
|
||||
{"Reader Screen Margin", SettingType::VALUE, &CrossPointSettings::screenMargin, 5, validateRange<5, 40>,
|
||||
ValueRange{5, 40, 5}},
|
||||
makeEnumDescriptor("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, CrossPointSettings::FIT,
|
||||
sleepScreenCoverModeValues),
|
||||
makeStringDescriptor("OPDS Server URL", opdsServerUrl, ""),
|
||||
{"Text Anti-Aliasing", SettingType::TOGGLE, &CrossPointSettings::textAntiAliasing, 1, validateToggle, nullptr, 0},
|
||||
makeEnumDescriptor("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, CrossPointSettings::HIDE_NEVER,
|
||||
hideBatteryPercentageValues),
|
||||
{"Long-press Chapter Skip", SettingType::TOGGLE, &CrossPointSettings::longPressChapterSkip, 1, validateToggle,
|
||||
nullptr, 0},
|
||||
{"Hyphenation", SettingType::TOGGLE, &CrossPointSettings::hyphenationEnabled, 0, validateToggle, nullptr, 0},
|
||||
makeStringDescriptor("Username", opdsUsername, ""),
|
||||
makeStringDescriptor("Password", opdsPassword, ""),
|
||||
makeEnumDescriptor("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter,
|
||||
CrossPointSettings::NO_FILTER, sleepScreenCoverFilterValues),
|
||||
}};
|
||||
|
||||
bool CrossPointSettings::saveToFile() const {
|
||||
// Make sure the directory exists
|
||||
SdMan.mkdir("/.crosspoint");
|
||||
@ -36,31 +147,15 @@ bool CrossPointSettings::saveToFile() const {
|
||||
}
|
||||
|
||||
serialization::writePod(outputFile, SETTINGS_FILE_VERSION);
|
||||
serialization::writePod(outputFile, SETTINGS_COUNT);
|
||||
serialization::writePod(outputFile, sleepScreen);
|
||||
serialization::writePod(outputFile, extraParagraphSpacing);
|
||||
serialization::writePod(outputFile, shortPwrBtn);
|
||||
serialization::writePod(outputFile, statusBar);
|
||||
serialization::writePod(outputFile, orientation);
|
||||
serialization::writePod(outputFile, frontButtonLayout);
|
||||
serialization::writePod(outputFile, sideButtonLayout);
|
||||
serialization::writePod(outputFile, fontFamily);
|
||||
serialization::writePod(outputFile, fontSize);
|
||||
serialization::writePod(outputFile, lineSpacing);
|
||||
serialization::writePod(outputFile, paragraphAlignment);
|
||||
serialization::writePod(outputFile, sleepTimeout);
|
||||
serialization::writePod(outputFile, refreshFrequency);
|
||||
serialization::writePod(outputFile, screenMargin);
|
||||
serialization::writePod(outputFile, sleepScreenCoverMode);
|
||||
serialization::writeString(outputFile, std::string(opdsServerUrl));
|
||||
serialization::writePod(outputFile, textAntiAliasing);
|
||||
serialization::writePod(outputFile, hideBatteryPercentage);
|
||||
serialization::writePod(outputFile, longPressChapterSkip);
|
||||
serialization::writePod(outputFile, hyphenationEnabled);
|
||||
serialization::writeString(outputFile, std::string(opdsUsername));
|
||||
serialization::writeString(outputFile, std::string(opdsPassword));
|
||||
serialization::writePod(outputFile, sleepScreenCoverFilter);
|
||||
// New fields added at end for backward compatibility
|
||||
serialization::writePod(outputFile, static_cast<uint8_t>(CrossPointSettings::DESCRIPTOR_COUNT));
|
||||
|
||||
// Use descriptors to automatically serialize all uint8_t settings
|
||||
uint8_t descriptorIndex = 0;
|
||||
for (const auto& desc : descriptors) {
|
||||
desc.save(outputFile, *this);
|
||||
descriptorIndex++;
|
||||
}
|
||||
|
||||
outputFile.close();
|
||||
|
||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||
@ -68,8 +163,10 @@ bool CrossPointSettings::saveToFile() const {
|
||||
}
|
||||
|
||||
bool CrossPointSettings::loadFromFile() {
|
||||
Serial.printf("[%lu] [CPS] Loading settings from file\n", millis());
|
||||
FsFile inputFile;
|
||||
if (!SdMan.openFileForRead("CPS", SETTINGS_FILE, inputFile)) {
|
||||
Serial.printf("[%lu] [CPS] Deserialization failed: Could not open settings file\n", millis());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -84,74 +181,26 @@ bool CrossPointSettings::loadFromFile() {
|
||||
uint8_t fileSettingsCount = 0;
|
||||
serialization::readPod(inputFile, fileSettingsCount);
|
||||
|
||||
// load settings that exist (support older files with fewer fields)
|
||||
uint8_t settingsRead = 0;
|
||||
do {
|
||||
readAndValidate(inputFile, sleepScreen, SLEEP_SCREEN_MODE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, extraParagraphSpacing);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, shortPwrBtn, SHORT_PWRBTN_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, statusBar, STATUS_BAR_MODE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, orientation, ORIENTATION_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonLayout, FRONT_BUTTON_LAYOUT_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sideButtonLayout, SIDE_BUTTON_LAYOUT_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, fontFamily, FONT_FAMILY_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, fontSize, FONT_SIZE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, lineSpacing, LINE_COMPRESSION_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, paragraphAlignment, PARAGRAPH_ALIGNMENT_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sleepTimeout, SLEEP_TIMEOUT_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, refreshFrequency, REFRESH_FREQUENCY_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, screenMargin);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sleepScreenCoverMode, SLEEP_SCREEN_COVER_MODE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
std::string urlStr;
|
||||
serialization::readString(inputFile, urlStr);
|
||||
strncpy(opdsServerUrl, urlStr.c_str(), sizeof(opdsServerUrl) - 1);
|
||||
opdsServerUrl[sizeof(opdsServerUrl) - 1] = '\0';
|
||||
}
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, textAntiAliasing);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, hideBatteryPercentage, HIDE_BATTERY_PERCENTAGE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, longPressChapterSkip);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, hyphenationEnabled);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
std::string usernameStr;
|
||||
serialization::readString(inputFile, usernameStr);
|
||||
strncpy(opdsUsername, usernameStr.c_str(), sizeof(opdsUsername) - 1);
|
||||
opdsUsername[sizeof(opdsUsername) - 1] = '\0';
|
||||
}
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
std::string passwordStr;
|
||||
serialization::readString(inputFile, passwordStr);
|
||||
strncpy(opdsPassword, passwordStr.c_str(), sizeof(opdsPassword) - 1);
|
||||
opdsPassword[sizeof(opdsPassword) - 1] = '\0';
|
||||
}
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
// New fields added at end for backward compatibility
|
||||
} while (false);
|
||||
// Use descriptors to automatically deserialize all uint8_t settings
|
||||
uint8_t descriptorIndex = 0;
|
||||
uint8_t filePosition = 0;
|
||||
|
||||
for (const auto& desc : descriptors) {
|
||||
if (filePosition >= fileSettingsCount) {
|
||||
break; // File has fewer settings than current version
|
||||
}
|
||||
|
||||
desc.load(inputFile, *this);
|
||||
if (!desc.validate(*this)) {
|
||||
Serial.printf("[%lu] [CPS] Invalid value (0x%X) for %s, resetting to default\n", millis(), desc.getValue(*this),
|
||||
desc.name);
|
||||
desc.resetToDefault(*this);
|
||||
}
|
||||
descriptorIndex++;
|
||||
filePosition++;
|
||||
}
|
||||
inputFile.close();
|
||||
|
||||
Serial.printf("[%lu] [CPS] Settings loaded from file\n", millis());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,6 +1,112 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Setting descriptor infrastructure
|
||||
enum class SettingType { TOGGLE, ENUM, VALUE, STRING };
|
||||
|
||||
// Validator function pointer (not std::function to save memory)
|
||||
using SettingValidator = bool (*)(uint8_t);
|
||||
|
||||
// Forward declare for descriptors
|
||||
class CrossPointSettings;
|
||||
|
||||
// Forward declare file type
|
||||
class FsFile;
|
||||
|
||||
// Base descriptor for all settings (non-virtual for constexpr)
|
||||
struct SettingDescriptorBase {
|
||||
const char* name; // Display name
|
||||
SettingType type;
|
||||
};
|
||||
|
||||
// Value range for VALUE type settings
|
||||
struct ValueRange {
|
||||
uint8_t min, max, step;
|
||||
};
|
||||
|
||||
// Concrete descriptor for uint8_t settings (constexpr-compatible)
|
||||
struct SettingDescriptor : public SettingDescriptorBase {
|
||||
union {
|
||||
uint8_t CrossPointSettings::* memberPtr; // For TOGGLE/ENUM/VALUE types
|
||||
char* stringPtr; // For STRING type
|
||||
};
|
||||
uint8_t defaultValue;
|
||||
SettingValidator validator; // Optional validator function
|
||||
|
||||
union {
|
||||
// For ENUM types
|
||||
struct {
|
||||
const char* const* values;
|
||||
uint8_t count;
|
||||
} enumData;
|
||||
|
||||
// For VALUE types
|
||||
ValueRange valueRange;
|
||||
|
||||
// For STRING types
|
||||
struct {
|
||||
const char* defaultString; // Default string value
|
||||
size_t maxSize; // Max size of the string buffer
|
||||
} stringData;
|
||||
};
|
||||
|
||||
// Constexpr constructors for different setting types
|
||||
// TOGGLE/ENUM constructor
|
||||
constexpr SettingDescriptor(const char* name_, SettingType type_, uint8_t CrossPointSettings::* ptr, uint8_t defVal,
|
||||
SettingValidator val, const char* const* enumVals, uint8_t enumCnt)
|
||||
: SettingDescriptorBase{name_, type_},
|
||||
memberPtr(ptr),
|
||||
defaultValue(defVal),
|
||||
validator(val),
|
||||
enumData{enumVals, enumCnt} {}
|
||||
|
||||
// VALUE constructor
|
||||
constexpr SettingDescriptor(const char* name_, SettingType type_, uint8_t CrossPointSettings::* ptr, uint8_t defVal,
|
||||
SettingValidator val, ValueRange valRange)
|
||||
: SettingDescriptorBase{name_, type_},
|
||||
memberPtr(ptr),
|
||||
defaultValue(defVal),
|
||||
validator(val),
|
||||
valueRange(valRange) {}
|
||||
|
||||
// STRING constructor
|
||||
constexpr SettingDescriptor(const char* name_, SettingType type_, char* strPtr, const char* defStr, size_t maxSz)
|
||||
: SettingDescriptorBase{name_, type_},
|
||||
stringPtr(strPtr),
|
||||
defaultValue(0),
|
||||
validator(nullptr),
|
||||
stringData{defStr, maxSz} {}
|
||||
|
||||
bool validate(const CrossPointSettings& settings) const;
|
||||
uint8_t getValue(const CrossPointSettings& settings) const;
|
||||
void setValue(CrossPointSettings& settings, uint8_t value) const;
|
||||
void resetToDefault(CrossPointSettings& settings) const;
|
||||
void save(FsFile& file, const CrossPointSettings& settings) const;
|
||||
void load(FsFile& file, CrossPointSettings& settings) const;
|
||||
|
||||
// Helper to get enum value as string
|
||||
const char* getEnumValueString(uint8_t index) const {
|
||||
if (index < enumData.count && enumData.values) {
|
||||
return enumData.values[index];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// Validator functions (constexpr for compile-time optimization)
|
||||
constexpr bool validateToggle(uint8_t v) { return v <= 1; }
|
||||
template <uint8_t MAX>
|
||||
constexpr bool validateEnum(uint8_t v) {
|
||||
return v < MAX;
|
||||
}
|
||||
template <uint8_t MIN, uint8_t MAX>
|
||||
constexpr bool validateRange(uint8_t v) {
|
||||
return v >= MIN && v <= MAX;
|
||||
}
|
||||
|
||||
class CrossPointSettings {
|
||||
private:
|
||||
@ -15,13 +121,17 @@ 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_COVER_MODE { FIT = 0, CROP = 1, SLEEP_SCREEN_COVER_MODE_COUNT };
|
||||
// Static constexpr array of all setting descriptors
|
||||
static constexpr size_t DESCRIPTOR_COUNT = 23;
|
||||
static const std::array<SettingDescriptor, DESCRIPTOR_COUNT> descriptors;
|
||||
|
||||
// Should match with SettingsActivity text
|
||||
enum SLEEP_SCREEN_MODE { DARK = 0, LIGHT = 1, CUSTOM = 2, COVER = 3, BLANK = 4 };
|
||||
enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1 };
|
||||
enum SLEEP_SCREEN_COVER_FILTER {
|
||||
NO_FILTER = 0,
|
||||
BLACK_AND_WHITE = 1,
|
||||
INVERTED_BLACK_AND_WHITE = 2,
|
||||
SLEEP_SCREEN_COVER_FILTER_COUNT
|
||||
};
|
||||
|
||||
// Status bar display type enum
|
||||
@ -31,7 +141,6 @@ class CrossPointSettings {
|
||||
FULL = 2,
|
||||
FULL_WITH_PROGRESS_BAR = 3,
|
||||
ONLY_PROGRESS_BAR = 4,
|
||||
STATUS_BAR_MODE_COUNT
|
||||
};
|
||||
|
||||
enum ORIENTATION {
|
||||
@ -39,7 +148,6 @@ class CrossPointSettings {
|
||||
LANDSCAPE_CW = 1, // 800x480 logical coordinates, rotated 180° (swap top/bottom)
|
||||
INVERTED = 2, // 480x800 logical coordinates, inverted
|
||||
LANDSCAPE_CCW = 3, // 800x480 logical coordinates, native panel orientation
|
||||
ORIENTATION_COUNT
|
||||
};
|
||||
|
||||
// Front button layout options
|
||||
@ -50,25 +158,23 @@ class CrossPointSettings {
|
||||
LEFT_RIGHT_BACK_CONFIRM = 1,
|
||||
LEFT_BACK_CONFIRM_RIGHT = 2,
|
||||
BACK_CONFIRM_RIGHT_LEFT = 3,
|
||||
FRONT_BUTTON_LAYOUT_COUNT
|
||||
};
|
||||
|
||||
// Side button layout options
|
||||
// Default: Previous, Next
|
||||
// Swapped: Next, Previous
|
||||
enum SIDE_BUTTON_LAYOUT { PREV_NEXT = 0, NEXT_PREV = 1, SIDE_BUTTON_LAYOUT_COUNT };
|
||||
enum SIDE_BUTTON_LAYOUT { PREV_NEXT = 0, NEXT_PREV = 1 };
|
||||
|
||||
// Font family options
|
||||
enum FONT_FAMILY { BOOKERLY = 0, NOTOSANS = 1, OPENDYSLEXIC = 2, FONT_FAMILY_COUNT };
|
||||
enum FONT_FAMILY { BOOKERLY = 0, NOTOSANS = 1, OPENDYSLEXIC = 2 };
|
||||
// Font size options
|
||||
enum FONT_SIZE { SMALL = 0, MEDIUM = 1, LARGE = 2, EXTRA_LARGE = 3, FONT_SIZE_COUNT };
|
||||
enum LINE_COMPRESSION { TIGHT = 0, NORMAL = 1, WIDE = 2, LINE_COMPRESSION_COUNT };
|
||||
enum FONT_SIZE { SMALL = 0, MEDIUM = 1, LARGE = 2, EXTRA_LARGE = 3 };
|
||||
enum LINE_COMPRESSION { TIGHT = 0, NORMAL = 1, WIDE = 2 };
|
||||
enum PARAGRAPH_ALIGNMENT {
|
||||
JUSTIFIED = 0,
|
||||
LEFT_ALIGN = 1,
|
||||
CENTER_ALIGN = 2,
|
||||
RIGHT_ALIGN = 3,
|
||||
PARAGRAPH_ALIGNMENT_COUNT
|
||||
};
|
||||
|
||||
// Auto-sleep timeout options (in minutes)
|
||||
@ -78,7 +184,6 @@ class CrossPointSettings {
|
||||
SLEEP_10_MIN = 2,
|
||||
SLEEP_15_MIN = 3,
|
||||
SLEEP_30_MIN = 4,
|
||||
SLEEP_TIMEOUT_COUNT
|
||||
};
|
||||
|
||||
// E-ink refresh frequency (pages between full refreshes)
|
||||
@ -88,14 +193,13 @@ class CrossPointSettings {
|
||||
REFRESH_10 = 2,
|
||||
REFRESH_15 = 3,
|
||||
REFRESH_30 = 4,
|
||||
REFRESH_FREQUENCY_COUNT
|
||||
};
|
||||
|
||||
// Short power button press actions
|
||||
enum SHORT_PWRBTN { IGNORE = 0, SLEEP = 1, PAGE_TURN = 2, SHORT_PWRBTN_COUNT };
|
||||
enum SHORT_PWRBTN { IGNORE = 0, SLEEP = 1, PAGE_TURN = 2 };
|
||||
|
||||
// Hide battery percentage
|
||||
enum HIDE_BATTERY_PERCENTAGE { HIDE_NEVER = 0, HIDE_READER = 1, HIDE_ALWAYS = 2, HIDE_BATTERY_PERCENTAGE_COUNT };
|
||||
enum HIDE_BATTERY_PERCENTAGE { HIDE_NEVER = 0, HIDE_READER = 1, HIDE_ALWAYS = 2 };
|
||||
|
||||
// Sleep screen settings
|
||||
uint8_t sleepScreen = DARK;
|
||||
|
||||
@ -62,77 +62,84 @@ void CategorySettingsActivity::loop() {
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
const int totalItemsCount = descriptors.size() + actionItems.size();
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
||||
selectedSettingIndex = (selectedSettingIndex > 0) ? (selectedSettingIndex - 1) : (settingsCount - 1);
|
||||
selectedSettingIndex = (selectedSettingIndex > 0) ? (selectedSettingIndex - 1) : (totalItemsCount - 1);
|
||||
updateRequired = true;
|
||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
||||
selectedSettingIndex = (selectedSettingIndex < settingsCount - 1) ? (selectedSettingIndex + 1) : 0;
|
||||
selectedSettingIndex = (selectedSettingIndex < totalItemsCount - 1) ? (selectedSettingIndex + 1) : 0;
|
||||
updateRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CategorySettingsActivity::toggleCurrentSetting() {
|
||||
if (selectedSettingIndex < 0 || selectedSettingIndex >= settingsCount) {
|
||||
const int totalItemsCount = descriptors.size() + actionItems.size();
|
||||
|
||||
if (selectedSettingIndex < 0 || selectedSettingIndex >= totalItemsCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& setting = settingsList[selectedSettingIndex];
|
||||
// Check if it's a descriptor or an action item
|
||||
if (selectedSettingIndex < static_cast<int>(descriptors.size())) {
|
||||
// Handle descriptor
|
||||
const auto* desc = descriptors[selectedSettingIndex];
|
||||
|
||||
if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) {
|
||||
// Toggle the boolean value using the member pointer
|
||||
const bool currentValue = SETTINGS.*(setting.valuePtr);
|
||||
SETTINGS.*(setting.valuePtr) = !currentValue;
|
||||
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
||||
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
|
||||
SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast<uint8_t>(setting.enumValues.size());
|
||||
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
||||
const int8_t currentValue = SETTINGS.*(setting.valuePtr);
|
||||
if (currentValue + setting.valueRange.step > setting.valueRange.max) {
|
||||
SETTINGS.*(setting.valuePtr) = setting.valueRange.min;
|
||||
if (desc->type == SettingType::TOGGLE) {
|
||||
uint8_t currentValue = desc->getValue(SETTINGS);
|
||||
desc->setValue(SETTINGS, !currentValue);
|
||||
} else if (desc->type == SettingType::ENUM) {
|
||||
uint8_t currentValue = desc->getValue(SETTINGS);
|
||||
desc->setValue(SETTINGS, (currentValue + 1) % desc->enumData.count);
|
||||
} else if (desc->type == SettingType::VALUE) {
|
||||
uint8_t currentValue = desc->getValue(SETTINGS);
|
||||
if (currentValue + desc->valueRange.step > desc->valueRange.max) {
|
||||
desc->setValue(SETTINGS, desc->valueRange.min);
|
||||
} else {
|
||||
SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step;
|
||||
desc->setValue(SETTINGS, currentValue + desc->valueRange.step);
|
||||
}
|
||||
} else if (setting.type == SettingType::ACTION) {
|
||||
if (strcmp(setting.name, "KOReader Sync") == 0) {
|
||||
}
|
||||
|
||||
SETTINGS.saveToFile();
|
||||
} else {
|
||||
// Handle action item
|
||||
const int actionIndex = selectedSettingIndex - descriptors.size();
|
||||
const auto& action = actionItems[actionIndex];
|
||||
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
exitActivity();
|
||||
|
||||
switch (action.type) {
|
||||
case ActionItem::Type::KOREADER_SYNC:
|
||||
enterNewActivity(new KOReaderSettingsActivity(renderer, mappedInput, [this] {
|
||||
exitActivity();
|
||||
updateRequired = true;
|
||||
}));
|
||||
xSemaphoreGive(renderingMutex);
|
||||
} else if (strcmp(setting.name, "OPDS Browser") == 0) {
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
exitActivity();
|
||||
break;
|
||||
case ActionItem::Type::CALIBRE_SETTINGS:
|
||||
enterNewActivity(new CalibreSettingsActivity(renderer, mappedInput, [this] {
|
||||
exitActivity();
|
||||
updateRequired = true;
|
||||
}));
|
||||
xSemaphoreGive(renderingMutex);
|
||||
} else if (strcmp(setting.name, "Clear Cache") == 0) {
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
exitActivity();
|
||||
break;
|
||||
case ActionItem::Type::CLEAR_CACHE:
|
||||
enterNewActivity(new ClearCacheActivity(renderer, mappedInput, [this] {
|
||||
exitActivity();
|
||||
updateRequired = true;
|
||||
}));
|
||||
xSemaphoreGive(renderingMutex);
|
||||
} else if (strcmp(setting.name, "Check for updates") == 0) {
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
exitActivity();
|
||||
break;
|
||||
case ActionItem::Type::CHECK_UPDATES:
|
||||
enterNewActivity(new OtaUpdateActivity(renderer, mappedInput, [this] {
|
||||
exitActivity();
|
||||
updateRequired = true;
|
||||
}));
|
||||
xSemaphoreGive(renderingMutex);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
SETTINGS.saveToFile();
|
||||
xSemaphoreGive(renderingMutex);
|
||||
}
|
||||
}
|
||||
|
||||
void CategorySettingsActivity::displayTaskLoop() {
|
||||
@ -153,29 +160,31 @@ void CategorySettingsActivity::render() const {
|
||||
const auto pageWidth = renderer.getScreenWidth();
|
||||
const auto pageHeight = renderer.getScreenHeight();
|
||||
|
||||
// Draw header with category name
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, categoryName, true, EpdFontFamily::BOLD);
|
||||
|
||||
// Draw selection highlight
|
||||
renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30);
|
||||
|
||||
// Draw all settings
|
||||
for (int i = 0; i < settingsCount; i++) {
|
||||
const int settingY = 60 + i * 30; // 30 pixels between settings
|
||||
// Draw all descriptors
|
||||
for (size_t i = 0; i < descriptors.size(); i++) {
|
||||
const auto* desc = descriptors[i];
|
||||
const int settingY = 60 + i * 30;
|
||||
const bool isSelected = (i == selectedSettingIndex);
|
||||
|
||||
// Draw setting name
|
||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, !isSelected);
|
||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, desc->name, !isSelected);
|
||||
|
||||
// Draw value based on setting type
|
||||
std::string valueText;
|
||||
if (settingsList[i].type == SettingType::TOGGLE && settingsList[i].valuePtr != nullptr) {
|
||||
const bool value = SETTINGS.*(settingsList[i].valuePtr);
|
||||
if (desc->type == SettingType::TOGGLE) {
|
||||
const bool value = desc->getValue(SETTINGS);
|
||||
valueText = value ? "ON" : "OFF";
|
||||
} else if (settingsList[i].type == SettingType::ENUM && settingsList[i].valuePtr != nullptr) {
|
||||
const uint8_t value = SETTINGS.*(settingsList[i].valuePtr);
|
||||
valueText = settingsList[i].enumValues[value];
|
||||
} else if (settingsList[i].type == SettingType::VALUE && settingsList[i].valuePtr != nullptr) {
|
||||
valueText = std::to_string(SETTINGS.*(settingsList[i].valuePtr));
|
||||
} else if (desc->type == SettingType::ENUM) {
|
||||
const uint8_t value = desc->getValue(SETTINGS);
|
||||
valueText = desc->getEnumValueString(value);
|
||||
} else if (desc->type == SettingType::VALUE) {
|
||||
valueText = std::to_string(desc->getValue(SETTINGS));
|
||||
}
|
||||
if (!valueText.empty()) {
|
||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, valueText.c_str());
|
||||
@ -183,9 +192,22 @@ void CategorySettingsActivity::render() const {
|
||||
}
|
||||
}
|
||||
|
||||
// Draw all action items
|
||||
for (size_t i = 0; i < actionItems.size(); i++) {
|
||||
const auto& action = actionItems[i];
|
||||
const int itemIndex = descriptors.size() + i;
|
||||
const int settingY = 60 + itemIndex * 30;
|
||||
const bool isSelected = (itemIndex == selectedSettingIndex);
|
||||
|
||||
// Draw action name
|
||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, action.name, !isSelected);
|
||||
}
|
||||
|
||||
// Draw version text above button hints
|
||||
renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
|
||||
pageHeight - 60, CROSSPOINT_VERSION);
|
||||
|
||||
// Draw help text
|
||||
const auto labels = mappedInput.mapLabels("« Back", "Toggle", "", "");
|
||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
|
||||
|
||||
@ -7,38 +7,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "activities/ActivityWithSubactivity.h"
|
||||
|
||||
class CrossPointSettings;
|
||||
|
||||
enum class SettingType { TOGGLE, ENUM, ACTION, VALUE };
|
||||
|
||||
struct SettingInfo {
|
||||
// Action items for the System category
|
||||
struct ActionItem {
|
||||
const char* name;
|
||||
SettingType type;
|
||||
uint8_t CrossPointSettings::* valuePtr;
|
||||
std::vector<std::string> enumValues;
|
||||
|
||||
struct ValueRange {
|
||||
uint8_t min;
|
||||
uint8_t max;
|
||||
uint8_t step;
|
||||
};
|
||||
ValueRange valueRange;
|
||||
|
||||
static SettingInfo Toggle(const char* name, uint8_t CrossPointSettings::* ptr) {
|
||||
return {name, SettingType::TOGGLE, ptr};
|
||||
}
|
||||
|
||||
static SettingInfo Enum(const char* name, uint8_t CrossPointSettings::* ptr, std::vector<std::string> values) {
|
||||
return {name, SettingType::ENUM, ptr, std::move(values)};
|
||||
}
|
||||
|
||||
static SettingInfo Action(const char* name) { return {name, SettingType::ACTION, nullptr}; }
|
||||
|
||||
static SettingInfo Value(const char* name, uint8_t CrossPointSettings::* ptr, const ValueRange valueRange) {
|
||||
return {name, SettingType::VALUE, ptr, {}, valueRange};
|
||||
}
|
||||
enum class Type { KOREADER_SYNC, CALIBRE_SETTINGS, CLEAR_CACHE, CHECK_UPDATES };
|
||||
Type type;
|
||||
};
|
||||
|
||||
class CategorySettingsActivity final : public ActivityWithSubactivity {
|
||||
@ -47,8 +23,8 @@ class CategorySettingsActivity final : public ActivityWithSubactivity {
|
||||
bool updateRequired = false;
|
||||
int selectedSettingIndex = 0;
|
||||
const char* categoryName;
|
||||
const SettingInfo* settingsList;
|
||||
int settingsCount;
|
||||
const std::vector<const SettingDescriptor*> descriptors;
|
||||
const std::vector<ActionItem> actionItems;
|
||||
const std::function<void()> onGoBack;
|
||||
|
||||
static void taskTrampoline(void* param);
|
||||
@ -58,11 +34,12 @@ class CategorySettingsActivity final : public ActivityWithSubactivity {
|
||||
|
||||
public:
|
||||
CategorySettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const char* categoryName,
|
||||
const SettingInfo* settingsList, int settingsCount, const std::function<void()>& onGoBack)
|
||||
const std::vector<const SettingDescriptor*>& descriptors,
|
||||
const std::vector<ActionItem>& actionItems, const std::function<void()>& onGoBack)
|
||||
: ActivityWithSubactivity("CategorySettings", renderer, mappedInput),
|
||||
categoryName(categoryName),
|
||||
settingsList(settingsList),
|
||||
settingsCount(settingsCount),
|
||||
descriptors(descriptors),
|
||||
actionItems(actionItems),
|
||||
onGoBack(onGoBack) {}
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
#include <GfxRenderer.h>
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "CategorySettingsActivity.h"
|
||||
#include "CrossPointSettings.h"
|
||||
#include "MappedInputManager.h"
|
||||
@ -10,51 +12,12 @@
|
||||
|
||||
const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"};
|
||||
|
||||
namespace {
|
||||
constexpr int displaySettingsCount = 6;
|
||||
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 Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}),
|
||||
SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter,
|
||||
{"None", "Contrast", "Inverted"}),
|
||||
SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar,
|
||||
{"None", "No Progress", "Full w/ Percentage", "Full w/ Progress Bar", "Progress Bar"}),
|
||||
SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}),
|
||||
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
||||
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"})};
|
||||
|
||||
constexpr int readerSettingsCount = 9;
|
||||
const SettingInfo readerSettings[readerSettingsCount] = {
|
||||
SettingInfo::Enum("Font Family", &CrossPointSettings::fontFamily, {"Bookerly", "Noto Sans", "Open Dyslexic"}),
|
||||
SettingInfo::Enum("Font Size", &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}),
|
||||
SettingInfo::Enum("Line Spacing", &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}),
|
||||
SettingInfo::Value("Screen Margin", &CrossPointSettings::screenMargin, {5, 40, 5}),
|
||||
SettingInfo::Enum("Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
|
||||
{"Justify", "Left", "Center", "Right"}),
|
||||
SettingInfo::Toggle("Hyphenation", &CrossPointSettings::hyphenationEnabled),
|
||||
SettingInfo::Enum("Reading Orientation", &CrossPointSettings::orientation,
|
||||
{"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}),
|
||||
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
|
||||
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing)};
|
||||
|
||||
constexpr int controlsSettingsCount = 4;
|
||||
const SettingInfo controlsSettings[controlsSettingsCount] = {
|
||||
SettingInfo::Enum(
|
||||
"Front Button Layout", &CrossPointSettings::frontButtonLayout,
|
||||
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", "Lft, Bck, Cnfrm, Rght", "Bck, Cnfrm, Rght, Lft"}),
|
||||
SettingInfo::Enum("Side Button Layout (reader)", &CrossPointSettings::sideButtonLayout,
|
||||
{"Prev, Next", "Next, Prev"}),
|
||||
SettingInfo::Toggle("Long-press Chapter Skip", &CrossPointSettings::longPressChapterSkip),
|
||||
SettingInfo::Enum("Short Power Button Click", &CrossPointSettings::shortPwrBtn, {"Ignore", "Sleep", "Page Turn"})};
|
||||
|
||||
constexpr int systemSettingsCount = 5;
|
||||
const SettingInfo systemSettings[systemSettingsCount] = {
|
||||
SettingInfo::Enum("Time to Sleep", &CrossPointSettings::sleepTimeout,
|
||||
{"1 min", "5 min", "10 min", "15 min", "30 min"}),
|
||||
SettingInfo::Action("KOReader Sync"), SettingInfo::Action("OPDS Browser"), SettingInfo::Action("Clear Cache"),
|
||||
SettingInfo::Action("Check for updates")};
|
||||
} // namespace
|
||||
// Helper function to find descriptor by member pointer
|
||||
static const SettingDescriptor* findDescriptor(uint8_t CrossPointSettings::* memberPtr) {
|
||||
auto it = std::find_if(CrossPointSettings::descriptors.begin(), CrossPointSettings::descriptors.end(),
|
||||
[memberPtr](const SettingDescriptor& desc) { return desc.memberPtr == memberPtr; });
|
||||
return (it != CrossPointSettings::descriptors.end()) ? &(*it) : nullptr;
|
||||
}
|
||||
|
||||
void SettingsActivity::taskTrampoline(void* param) {
|
||||
auto* self = static_cast<SettingsActivity*>(param);
|
||||
@ -125,37 +88,51 @@ void SettingsActivity::loop() {
|
||||
}
|
||||
|
||||
void SettingsActivity::enterCategory(int categoryIndex) {
|
||||
if (categoryIndex < 0 || categoryIndex >= categoryCount) {
|
||||
return;
|
||||
std::vector<const SettingDescriptor*> descriptors;
|
||||
std::vector<ActionItem> actionItems;
|
||||
|
||||
switch (categoryIndex) {
|
||||
case 0: // Display
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::sleepScreen));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::sleepScreenCoverMode));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::sleepScreenCoverFilter));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::statusBar));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::hideBatteryPercentage));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::refreshFrequency));
|
||||
break;
|
||||
|
||||
case 1: // Reader
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::fontFamily));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::fontSize));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::lineSpacing));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::screenMargin));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::paragraphAlignment));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::hyphenationEnabled));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::orientation));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::extraParagraphSpacing));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::textAntiAliasing));
|
||||
break;
|
||||
|
||||
case 2: // Controls
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::frontButtonLayout));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::sideButtonLayout));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::longPressChapterSkip));
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::shortPwrBtn));
|
||||
break;
|
||||
|
||||
case 3: // System
|
||||
descriptors.push_back(findDescriptor(&CrossPointSettings::sleepTimeout));
|
||||
actionItems.push_back({"KOReader Sync", ActionItem::Type::KOREADER_SYNC});
|
||||
actionItems.push_back({"Calibre Settings", ActionItem::Type::CALIBRE_SETTINGS});
|
||||
actionItems.push_back({"Clear Cache", ActionItem::Type::CLEAR_CACHE});
|
||||
actionItems.push_back({"Check for updates", ActionItem::Type::CHECK_UPDATES});
|
||||
break;
|
||||
}
|
||||
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
exitActivity();
|
||||
|
||||
const SettingInfo* settingsList = nullptr;
|
||||
int settingsCount = 0;
|
||||
|
||||
switch (categoryIndex) {
|
||||
case 0: // Display
|
||||
settingsList = displaySettings;
|
||||
settingsCount = displaySettingsCount;
|
||||
break;
|
||||
case 1: // Reader
|
||||
settingsList = readerSettings;
|
||||
settingsCount = readerSettingsCount;
|
||||
break;
|
||||
case 2: // Controls
|
||||
settingsList = controlsSettings;
|
||||
settingsCount = controlsSettingsCount;
|
||||
break;
|
||||
case 3: // System
|
||||
settingsList = systemSettings;
|
||||
settingsCount = systemSettingsCount;
|
||||
break;
|
||||
}
|
||||
|
||||
enterNewActivity(new CategorySettingsActivity(renderer, mappedInput, categoryNames[categoryIndex], settingsList,
|
||||
settingsCount, [this] {
|
||||
enterNewActivity(new CategorySettingsActivity(renderer, mappedInput, categoryNames[categoryIndex], descriptors,
|
||||
actionItems, [this] {
|
||||
exitActivity();
|
||||
updateRequired = true;
|
||||
}));
|
||||
|
||||
@ -9,9 +9,6 @@
|
||||
|
||||
#include "activities/ActivityWithSubactivity.h"
|
||||
|
||||
class CrossPointSettings;
|
||||
struct SettingInfo;
|
||||
|
||||
class SettingsActivity final : public ActivityWithSubactivity {
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user