mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 14:47:37 +03:00
## Summary * **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.) Implementation of a new feature in Display options as Image Filter * **What changes are included?** Black & White and Inverted Black & White options are added. ## Additional Context Here are some examples: | None | Contrast | Inverted | | --- | --- | --- | | <img alt="image" src="https://github.com/user-attachments/assets/fe02dd9b-f647-41bd-8495-c262f73177c4" /> | <img alt="image" src="https://github.com/user-attachments/assets/2d17747d-3ff6-48a9-b9b9-eb17cccf19cf" /> | <img alt="image" src="https://github.com/user-attachments/assets/792dea50-f003-4634-83fe-77849ca49095" /> | | <img alt="image" src="https://github.com/user-attachments/assets/28395b63-14f8-41e2-886b-8ddf3faeafc4" /> | <img alt="image" src="https://github.com/user-attachments/assets/71a569c8-fc54-4647-ad4c-ec96e220cddb" /> | <img alt="image" src="https://github.com/user-attachments/assets/9139e32c-9175-433e-8372-45fa042d3dc9" /> | * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). I have also tried adding Color inversion, but could not see much difference with that. It might be because my implementation was wrong. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _** PARTIALLY **_ --------- Co-authored-by: Dave Allie <dave@daveallie.com>
208 lines
8.0 KiB
C++
208 lines
8.0 KiB
C++
#include "SettingsActivity.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <HardwareSerial.h>
|
|
|
|
#include "CategorySettingsActivity.h"
|
|
#include "CrossPointSettings.h"
|
|
#include "MappedInputManager.h"
|
|
#include "fontIds.h"
|
|
|
|
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
|
|
|
|
void SettingsActivity::taskTrampoline(void* param) {
|
|
auto* self = static_cast<SettingsActivity*>(param);
|
|
self->displayTaskLoop();
|
|
}
|
|
|
|
void SettingsActivity::onEnter() {
|
|
Activity::onEnter();
|
|
renderingMutex = xSemaphoreCreateMutex();
|
|
|
|
// Reset selection to first category
|
|
selectedCategoryIndex = 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 category selection
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
|
enterCategory(selectedCategoryIndex);
|
|
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)
|
|
selectedCategoryIndex = (selectedCategoryIndex > 0) ? (selectedCategoryIndex - 1) : (categoryCount - 1);
|
|
updateRequired = true;
|
|
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
|
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
|
// Move selection down (with wrap around)
|
|
selectedCategoryIndex = (selectedCategoryIndex < categoryCount - 1) ? (selectedCategoryIndex + 1) : 0;
|
|
updateRequired = true;
|
|
}
|
|
}
|
|
|
|
void SettingsActivity::enterCategory(int categoryIndex) {
|
|
if (categoryIndex < 0 || categoryIndex >= categoryCount) {
|
|
return;
|
|
}
|
|
|
|
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] {
|
|
exitActivity();
|
|
updateRequired = true;
|
|
}));
|
|
xSemaphoreGive(renderingMutex);
|
|
}
|
|
|
|
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 + selectedCategoryIndex * 30 - 2, pageWidth - 1, 30);
|
|
|
|
// Draw all categories
|
|
for (int i = 0; i < categoryCount; i++) {
|
|
const int categoryY = 60 + i * 30; // 30 pixels between categories
|
|
|
|
// Draw category name
|
|
renderer.drawText(UI_10_FONT_ID, 20, categoryY, categoryNames[i], i != selectedCategoryIndex);
|
|
}
|
|
|
|
// 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", "Select", "", "");
|
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
|
|
// Always use standard refresh for settings screen
|
|
renderer.displayBuffer();
|
|
}
|