mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-06 15:47:39 +03:00
## Summary Fixes https://github.com/daveallie/crosspoint-reader/issues/233 ## Additional Context By disabling the Text Anti-Aliasing in the settings, you can get faster black-and-white page turns that work exactly like the stock firmware, instead of having an additional flash after rendering (see issue linked above for example). Tested on the X4 and confirmed working. --------- Co-authored-by: Dave Allie <dave@daveallie.com>
223 lines
8.8 KiB
C++
223 lines
8.8 KiB
C++
#include "SettingsActivity.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <HardwareSerial.h>
|
|
|
|
#include <cstring>
|
|
|
|
#include "CalibreSettingsActivity.h"
|
|
#include "CrossPointSettings.h"
|
|
#include "MappedInputManager.h"
|
|
#include "OtaUpdateActivity.h"
|
|
#include "fontIds.h"
|
|
|
|
// Define the static settings list
|
|
namespace {
|
|
constexpr int settingsCount = 18;
|
|
const SettingInfo settingsList[settingsCount] = {
|
|
// 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("Status Bar", &CrossPointSettings::statusBar, {"None", "No Progress", "Full"}),
|
|
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
|
|
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing),
|
|
SettingInfo::Toggle("Short Power Button Click", &CrossPointSettings::shortPwrBtn),
|
|
SettingInfo::Enum("Reading Orientation", &CrossPointSettings::orientation,
|
|
{"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}),
|
|
SettingInfo::Enum("Front Button Layout", &CrossPointSettings::frontButtonLayout,
|
|
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", "Lft, Bck, Cnfrm, Rght"}),
|
|
SettingInfo::Enum("Side Button Layout (reader)", &CrossPointSettings::sideButtonLayout,
|
|
{"Prev, Next", "Next, Prev"}),
|
|
SettingInfo::Enum("Reader Font Family", &CrossPointSettings::fontFamily,
|
|
{"Bookerly", "Noto Sans", "Open Dyslexic"}),
|
|
SettingInfo::Enum("Reader Font Size", &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}),
|
|
SettingInfo::Enum("Reader Line Spacing", &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}),
|
|
SettingInfo::Value("Reader Screen Margin", &CrossPointSettings::screenMargin, {5, 40, 5}),
|
|
SettingInfo::Enum("Reader Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
|
|
{"Justify", "Left", "Center", "Right"}),
|
|
SettingInfo::Enum("Time to Sleep", &CrossPointSettings::sleepTimeout,
|
|
{"1 min", "5 min", "10 min", "15 min", "30 min"}),
|
|
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
|
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}),
|
|
SettingInfo::Action("Calibre Settings"),
|
|
SettingInfo::Action("Check for updates")};
|
|
} // namespace
|
|
|
|
void SettingsActivity::taskTrampoline(void* param) {
|
|
auto* self = static_cast<SettingsActivity*>(param);
|
|
self->displayTaskLoop();
|
|
}
|
|
|
|
void SettingsActivity::onEnter() {
|
|
Activity::onEnter();
|
|
renderingMutex = xSemaphoreCreateMutex();
|
|
|
|
// Reset selection to first item
|
|
selectedSettingIndex = 0;
|
|
|
|
// Trigger first update
|
|
updateRequired = true;
|
|
|
|
xTaskCreate(&SettingsActivity::taskTrampoline, "SettingsActivityTask",
|
|
4096, // Stack size
|
|
this, // Parameters
|
|
1, // Priority
|
|
&displayTaskHandle // Task handle
|
|
);
|
|
}
|
|
|
|
void SettingsActivity::onExit() {
|
|
ActivityWithSubactivity::onExit();
|
|
|
|
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
if (displayTaskHandle) {
|
|
vTaskDelete(displayTaskHandle);
|
|
displayTaskHandle = nullptr;
|
|
}
|
|
vSemaphoreDelete(renderingMutex);
|
|
renderingMutex = nullptr;
|
|
}
|
|
|
|
void SettingsActivity::loop() {
|
|
if (subActivity) {
|
|
subActivity->loop();
|
|
return;
|
|
}
|
|
|
|
// Handle actions with early return
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
|
toggleCurrentSetting();
|
|
updateRequired = true;
|
|
return;
|
|
}
|
|
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
|
SETTINGS.saveToFile();
|
|
onGoHome();
|
|
return;
|
|
}
|
|
|
|
// Handle navigation
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
|
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
|
// Move selection up (with wrap-around)
|
|
selectedSettingIndex = (selectedSettingIndex > 0) ? (selectedSettingIndex - 1) : (settingsCount - 1);
|
|
updateRequired = true;
|
|
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
|
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
|
// Move selection down (with wrap around)
|
|
selectedSettingIndex = (selectedSettingIndex < settingsCount - 1) ? (selectedSettingIndex + 1) : 0;
|
|
updateRequired = true;
|
|
}
|
|
}
|
|
|
|
void SettingsActivity::toggleCurrentSetting() {
|
|
// Validate index
|
|
if (selectedSettingIndex < 0 || selectedSettingIndex >= settingsCount) {
|
|
return;
|
|
}
|
|
|
|
const auto& setting = settingsList[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) {
|
|
// Decreasing would also be nice for large ranges I think but oh well can't have everything
|
|
const int8_t currentValue = SETTINGS.*(setting.valuePtr);
|
|
// Wrap to minValue if exceeding setting value boundary
|
|
if (currentValue + setting.valueRange.step > setting.valueRange.max) {
|
|
SETTINGS.*(setting.valuePtr) = setting.valueRange.min;
|
|
} else {
|
|
SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step;
|
|
}
|
|
} else if (setting.type == SettingType::ACTION) {
|
|
if (strcmp(setting.name, "Calibre Settings") == 0) {
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
exitActivity();
|
|
enterNewActivity(new CalibreSettingsActivity(renderer, mappedInput, [this] {
|
|
exitActivity();
|
|
updateRequired = true;
|
|
}));
|
|
xSemaphoreGive(renderingMutex);
|
|
} else if (strcmp(setting.name, "Check for updates") == 0) {
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
exitActivity();
|
|
enterNewActivity(new OtaUpdateActivity(renderer, mappedInput, [this] {
|
|
exitActivity();
|
|
updateRequired = true;
|
|
}));
|
|
xSemaphoreGive(renderingMutex);
|
|
}
|
|
} else {
|
|
// Only toggle if it's a toggle type and has a value pointer
|
|
return;
|
|
}
|
|
|
|
// Save settings when they change
|
|
SETTINGS.saveToFile();
|
|
}
|
|
|
|
void SettingsActivity::displayTaskLoop() {
|
|
while (true) {
|
|
if (updateRequired && !subActivity) {
|
|
updateRequired = false;
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
render();
|
|
xSemaphoreGive(renderingMutex);
|
|
}
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
|
|
void SettingsActivity::render() const {
|
|
renderer.clearScreen();
|
|
|
|
const auto pageWidth = renderer.getScreenWidth();
|
|
const auto pageHeight = renderer.getScreenHeight();
|
|
|
|
// Draw header
|
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, EpdFontFamily::BOLD);
|
|
|
|
// Draw selection
|
|
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 setting name
|
|
renderer.drawText(UI_10_FONT_ID, 20, settingY, settingsList[i].name, i != selectedSettingIndex);
|
|
|
|
// 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);
|
|
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));
|
|
}
|
|
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);
|
|
}
|
|
|
|
// 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("« Save", "Toggle", "", "");
|
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
|
|
// Always use standard refresh for settings screen
|
|
renderer.displayBuffer();
|
|
}
|