mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-05 07:07:38 +03:00
feat: add Refresh Frequency user preference setting
This commit is contained in:
parent
00369161f9
commit
73959ea9ac
@ -4,6 +4,7 @@
|
|||||||
#include <EpdFontFamily.h>
|
#include <EpdFontFamily.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "Bitmap.h"
|
#include "Bitmap.h"
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ default_envs = default
|
|||||||
|
|
||||||
[base]
|
[base]
|
||||||
platform = espressif32 @ 6.12.0
|
platform = espressif32 @ 6.12.0
|
||||||
|
platform_packages = espressif/toolchain-riscv32-esp@11.2.0+2022r1
|
||||||
board = esp32-c3-devkitm-1
|
board = esp32-c3-devkitm-1
|
||||||
framework = arduino
|
framework = arduino
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
@ -43,6 +44,7 @@ lib_deps =
|
|||||||
InputManager=symlink://open-x4-sdk/libs/hardware/InputManager
|
InputManager=symlink://open-x4-sdk/libs/hardware/InputManager
|
||||||
EInkDisplay=symlink://open-x4-sdk/libs/display/EInkDisplay
|
EInkDisplay=symlink://open-x4-sdk/libs/display/EInkDisplay
|
||||||
SDCardManager=symlink://open-x4-sdk/libs/hardware/SDCardManager
|
SDCardManager=symlink://open-x4-sdk/libs/hardware/SDCardManager
|
||||||
|
SdFat @ 2.2.3
|
||||||
ArduinoJson @ 7.4.2
|
ArduinoJson @ 7.4.2
|
||||||
QRCode @ 0.0.1
|
QRCode @ 0.0.1
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ CrossPointSettings CrossPointSettings::instance;
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||||
// Increment this when adding new persisted settings fields
|
// Increment this when adding new persisted settings fields
|
||||||
constexpr uint8_t SETTINGS_COUNT = 11;
|
constexpr uint8_t SETTINGS_COUNT = 12;
|
||||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ bool CrossPointSettings::saveToFile() const {
|
|||||||
serialization::writePod(outputFile, fontSize);
|
serialization::writePod(outputFile, fontSize);
|
||||||
serialization::writePod(outputFile, lineSpacing);
|
serialization::writePod(outputFile, lineSpacing);
|
||||||
serialization::writePod(outputFile, sleepTimeout);
|
serialization::writePod(outputFile, sleepTimeout);
|
||||||
|
serialization::writePod(outputFile, refreshFrequency);
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
||||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||||
@ -86,6 +87,8 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
serialization::readPod(inputFile, sleepTimeout);
|
serialization::readPod(inputFile, sleepTimeout);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, refreshFrequency);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
@ -145,6 +148,22 @@ unsigned long CrossPointSettings::getSleepTimeoutMs() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CrossPointSettings::getRefreshFrequency() const {
|
||||||
|
switch (refreshFrequency) {
|
||||||
|
case REFRESH_1:
|
||||||
|
return 1;
|
||||||
|
case REFRESH_5:
|
||||||
|
return 5;
|
||||||
|
case REFRESH_10:
|
||||||
|
return 10;
|
||||||
|
case REFRESH_15:
|
||||||
|
default:
|
||||||
|
return 15;
|
||||||
|
case REFRESH_30:
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int CrossPointSettings::getReaderFontId() const {
|
int CrossPointSettings::getReaderFontId() const {
|
||||||
switch (fontFamily) {
|
switch (fontFamily) {
|
||||||
case BOOKERLY:
|
case BOOKERLY:
|
||||||
|
|||||||
@ -47,6 +47,9 @@ class CrossPointSettings {
|
|||||||
// Auto-sleep timeout options (in minutes)
|
// Auto-sleep timeout options (in minutes)
|
||||||
enum SLEEP_TIMEOUT { SLEEP_1_MIN = 0, SLEEP_5_MIN = 1, SLEEP_10_MIN = 2, SLEEP_15_MIN = 3, SLEEP_30_MIN = 4 };
|
enum SLEEP_TIMEOUT { SLEEP_1_MIN = 0, SLEEP_5_MIN = 1, SLEEP_10_MIN = 2, SLEEP_15_MIN = 3, SLEEP_30_MIN = 4 };
|
||||||
|
|
||||||
|
// E-ink refresh frequency (pages between full refreshes)
|
||||||
|
enum REFRESH_FREQUENCY { REFRESH_1 = 0, REFRESH_5 = 1, REFRESH_10 = 2, REFRESH_15 = 3, REFRESH_30 = 4 };
|
||||||
|
|
||||||
// Sleep screen settings
|
// Sleep screen settings
|
||||||
uint8_t sleepScreen = DARK;
|
uint8_t sleepScreen = DARK;
|
||||||
// Status bar settings
|
// Status bar settings
|
||||||
@ -67,6 +70,8 @@ class CrossPointSettings {
|
|||||||
uint8_t lineSpacing = NORMAL;
|
uint8_t lineSpacing = NORMAL;
|
||||||
// Auto-sleep timeout setting (default 10 minutes)
|
// Auto-sleep timeout setting (default 10 minutes)
|
||||||
uint8_t sleepTimeout = SLEEP_10_MIN;
|
uint8_t sleepTimeout = SLEEP_10_MIN;
|
||||||
|
// E-ink refresh frequency (default 15 pages)
|
||||||
|
uint8_t refreshFrequency = REFRESH_15;
|
||||||
|
|
||||||
~CrossPointSettings() = default;
|
~CrossPointSettings() = default;
|
||||||
|
|
||||||
@ -81,6 +86,7 @@ class CrossPointSettings {
|
|||||||
|
|
||||||
float getReaderLineCompression() const;
|
float getReaderLineCompression() const;
|
||||||
unsigned long getSleepTimeoutMs() const;
|
unsigned long getSleepTimeoutMs() const;
|
||||||
|
int getRefreshFrequency() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper macro to access settings
|
// Helper macro to access settings
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int pagesPerRefresh = 15;
|
// pagesPerRefresh now comes from SETTINGS.getRefreshFrequency()
|
||||||
constexpr unsigned long skipChapterMs = 700;
|
constexpr unsigned long skipChapterMs = 700;
|
||||||
constexpr unsigned long goHomeMs = 1000;
|
constexpr unsigned long goHomeMs = 1000;
|
||||||
constexpr int topPadding = 5;
|
constexpr int topPadding = 5;
|
||||||
@ -22,9 +22,9 @@ constexpr int horizontalPadding = 5;
|
|||||||
// contentGap = space between last line of book text and delimiter line
|
// contentGap = space between last line of book text and delimiter line
|
||||||
// lineToText = space between delimiter line and footer text
|
// lineToText = space between delimiter line and footer text
|
||||||
// footerTextHeight ~= 12px for small font
|
// footerTextHeight ~= 12px for small font
|
||||||
constexpr int footerHeight = 34; // total footer area height (includes bottom margin)
|
constexpr int footerHeight = 34; // total footer area height (includes bottom margin)
|
||||||
constexpr int contentGap = 6; // gap above delimiter line
|
constexpr int contentGap = 6; // gap above delimiter line
|
||||||
constexpr int lineToText = 6; // gap below delimiter line to text
|
constexpr int lineToText = 6; // gap below delimiter line to text
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void EpubReaderActivity::taskTrampoline(void* param) {
|
void EpubReaderActivity::taskTrampoline(void* param) {
|
||||||
@ -384,7 +384,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
|||||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||||
if (pagesUntilFullRefresh <= 1) {
|
if (pagesUntilFullRefresh <= 1) {
|
||||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||||
pagesUntilFullRefresh = pagesPerRefresh;
|
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
|
||||||
} else {
|
} else {
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
pagesUntilFullRefresh--;
|
pagesUntilFullRefresh--;
|
||||||
@ -431,12 +431,12 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
|
|||||||
// So book content ends at: screenHeight - orientedMarginBottom
|
// So book content ends at: screenHeight - orientedMarginBottom
|
||||||
// Line should be at: screenHeight - orientedMarginBottom + contentGap (just below content)
|
// Line should be at: screenHeight - orientedMarginBottom + contentGap (just below content)
|
||||||
// Footer text at: lineY + lineToText + some offset for text baseline
|
// Footer text at: lineY + lineToText + some offset for text baseline
|
||||||
|
|
||||||
const auto screenHeight = renderer.getScreenHeight();
|
const auto screenHeight = renderer.getScreenHeight();
|
||||||
const int contentBottom = screenHeight - orientedMarginBottom;
|
const int contentBottom = screenHeight - orientedMarginBottom;
|
||||||
const int lineY = contentBottom + contentGap;
|
const int lineY = contentBottom + contentGap;
|
||||||
const int textY = lineY + lineToText;
|
const int textY = lineY + lineToText;
|
||||||
|
|
||||||
renderer.drawLine(orientedMarginLeft, lineY, renderer.getScreenWidth() - orientedMarginRight, lineY);
|
renderer.drawLine(orientedMarginLeft, lineY, renderer.getScreenWidth() - orientedMarginRight, lineY);
|
||||||
|
|
||||||
int progressTextWidth = 0;
|
int progressTextWidth = 0;
|
||||||
|
|||||||
@ -123,8 +123,8 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
|
|||||||
const int pageItems = getPageItems();
|
const int pageItems = getPageItems();
|
||||||
|
|
||||||
// Draw header with book title
|
// Draw header with book title
|
||||||
const std::string title =
|
const std::string title = renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(),
|
||||||
renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(), pageWidth - horizontalMargin * 2, EpdFontFamily::BOLD);
|
pageWidth - horizontalMargin * 2, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, headerY, title.c_str(), true, EpdFontFamily::BOLD);
|
renderer.drawCenteredText(UI_12_FONT_ID, headerY, title.c_str(), true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
// Subtle separator line under header
|
// Subtle separator line under header
|
||||||
|
|||||||
@ -199,7 +199,8 @@ void FileSelectionActivity::render() const {
|
|||||||
renderer.fillRect(0, listStartY + (selectorIndex % PAGE_ITEMS) * rowHeight - 2, pageWidth - 1, rowHeight);
|
renderer.fillRect(0, listStartY + (selectorIndex % PAGE_ITEMS) * rowHeight - 2, pageWidth - 1, rowHeight);
|
||||||
for (int i = pageStartIndex; i < files.size() && i < pageStartIndex + PAGE_ITEMS; i++) {
|
for (int i = pageStartIndex; i < files.size() && i < pageStartIndex + PAGE_ITEMS; i++) {
|
||||||
auto item = renderer.truncatedText(UI_10_FONT_ID, files[i].c_str(), pageWidth - horizontalMargin * 2 - 8);
|
auto item = renderer.truncatedText(UI_10_FONT_ID, files[i].c_str(), pageWidth - horizontalMargin * 2 - 8);
|
||||||
renderer.drawText(UI_10_FONT_ID, horizontalMargin + 4, listStartY + (i % PAGE_ITEMS) * rowHeight, item.c_str(), i != selectorIndex);
|
renderer.drawText(UI_10_FONT_ID, horizontalMargin + 4, listStartY + (i % PAGE_ITEMS) * rowHeight, item.c_str(),
|
||||||
|
i != selectorIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
// Define the static settings list
|
// Define the static settings list
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int settingsCount = 12;
|
constexpr int settingsCount = 13;
|
||||||
const SettingInfo settingsList[settingsCount] = {
|
const SettingInfo settingsList[settingsCount] = {
|
||||||
// Should match with SLEEP_SCREEN_MODE
|
// Should match with SLEEP_SCREEN_MODE
|
||||||
{"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}},
|
{"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}},
|
||||||
@ -34,7 +34,14 @@ const SettingInfo settingsList[settingsCount] = {
|
|||||||
{"Bookerly", "Noto Sans", "Open Dyslexic"}},
|
{"Bookerly", "Noto Sans", "Open Dyslexic"}},
|
||||||
{"Reader Font Size", SettingType::ENUM, &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}},
|
{"Reader Font Size", SettingType::ENUM, &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}},
|
||||||
{"Reader Line Spacing", SettingType::ENUM, &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}},
|
{"Reader Line Spacing", SettingType::ENUM, &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}},
|
||||||
{"Time to Sleep", SettingType::ENUM, &CrossPointSettings::sleepTimeout, {"1 min", "5 min", "10 min", "15 min", "30 min"}},
|
{"Time to Sleep",
|
||||||
|
SettingType::ENUM,
|
||||||
|
&CrossPointSettings::sleepTimeout,
|
||||||
|
{"1 min", "5 min", "10 min", "15 min", "30 min"}},
|
||||||
|
{"Refresh Frequency",
|
||||||
|
SettingType::ENUM,
|
||||||
|
&CrossPointSettings::refreshFrequency,
|
||||||
|
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}},
|
||||||
{"Check for updates", SettingType::ACTION, nullptr, {}},
|
{"Check for updates", SettingType::ACTION, nullptr, {}},
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -163,18 +170,28 @@ void SettingsActivity::render() const {
|
|||||||
const auto pageWidth = renderer.getScreenWidth();
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
const auto pageHeight = renderer.getScreenHeight();
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
// Draw header
|
// Layout constants
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, EpdFontFamily::BOLD);
|
constexpr int headerY = 16;
|
||||||
|
constexpr int separatorY = 42;
|
||||||
|
constexpr int listStartY = 54;
|
||||||
|
constexpr int rowHeight = 28;
|
||||||
|
constexpr int horizontalMargin = 16;
|
||||||
|
|
||||||
// Draw selection
|
// Draw header
|
||||||
renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30);
|
renderer.drawCenteredText(UI_12_FONT_ID, headerY, "Settings", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
|
// Subtle separator line under header
|
||||||
|
renderer.drawLine(horizontalMargin, separatorY, pageWidth - horizontalMargin, separatorY);
|
||||||
|
|
||||||
|
// Draw selection highlight
|
||||||
|
renderer.fillRect(0, listStartY + selectedSettingIndex * rowHeight - 2, pageWidth - 1, rowHeight);
|
||||||
|
|
||||||
// Draw all settings
|
// Draw all settings
|
||||||
for (int i = 0; i < settingsCount; i++) {
|
for (int i = 0; i < settingsCount; i++) {
|
||||||
const int settingY = 60 + i * 30; // 30 pixels between settings
|
const int settingY = listStartY + i * rowHeight;
|
||||||
|
|
||||||
// Draw setting name
|
// Draw setting name
|
||||||
renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, i != selectedSettingIndex);
|
renderer.drawText(UI_10_FONT_ID, horizontalMargin + 4, settingY, settingsList[i].name, i != selectedSettingIndex);
|
||||||
|
|
||||||
// Draw value based on setting type
|
// Draw value based on setting type
|
||||||
std::string valueText = "";
|
std::string valueText = "";
|
||||||
@ -186,12 +203,13 @@ void SettingsActivity::render() const {
|
|||||||
valueText = settingsList[i].enumValues[value];
|
valueText = settingsList[i].enumValues[value];
|
||||||
}
|
}
|
||||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, valueText.c_str());
|
const auto width = renderer.getTextWidth(UI_10_FONT_ID, valueText.c_str());
|
||||||
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, valueText.c_str(), i != selectedSettingIndex);
|
renderer.drawText(UI_10_FONT_ID, pageWidth - horizontalMargin - 4 - width, settingY, valueText.c_str(),
|
||||||
|
i != selectedSettingIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw version text above button hints
|
// Draw version text above button hints
|
||||||
renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
|
renderer.drawText(SMALL_FONT_ID, pageWidth - 16 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
|
||||||
pageHeight - 60, CROSSPOINT_VERSION);
|
pageHeight - 58, CROSSPOINT_VERSION);
|
||||||
|
|
||||||
// Draw help text
|
// Draw help text
|
||||||
const auto labels = mappedInput.mapLabels("« Save", "Toggle", "", "");
|
const auto labels = mappedInput.mapLabels("« Save", "Toggle", "", "");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user