Refactor Settings usage
This commit is contained in:
+8
-8
@@ -151,22 +151,22 @@ inline const std::vector<SettingInfo> list = {
|
|||||||
|
|
||||||
// --- KOReader Sync (web-only, uses KOReaderCredentialStore) ---
|
// --- KOReader Sync (web-only, uses KOReaderCredentialStore) ---
|
||||||
SettingInfo::DynamicString(
|
SettingInfo::DynamicString(
|
||||||
StrId::STR_SYNC_SERVER_URL, [] { return KOREADER_STORE.getServerUrl(); },
|
StrId::STR_SYNC_SERVER_URL, [](void*) { return KOREADER_STORE.getServerUrl(); },
|
||||||
[](const std::string& v) {
|
[](void*, const std::string& v) {
|
||||||
KOREADER_STORE.setServerUrl(v);
|
KOREADER_STORE.setServerUrl(v);
|
||||||
KOREADER_STORE.saveToFile();
|
KOREADER_STORE.saveToFile();
|
||||||
},
|
},
|
||||||
"koServerUrl", StrId::STR_KOREADER_SYNC),
|
"koServerUrl", StrId::STR_KOREADER_SYNC),
|
||||||
SettingInfo::DynamicString(
|
SettingInfo::DynamicString(
|
||||||
StrId::STR_KOREADER_USERNAME, [] { return KOREADER_STORE.getUsername(); },
|
StrId::STR_KOREADER_USERNAME, [](void*) { return KOREADER_STORE.getUsername(); },
|
||||||
[](const std::string& v) {
|
[](void*, const std::string& v) {
|
||||||
KOREADER_STORE.setCredentials(v, KOREADER_STORE.getPassword());
|
KOREADER_STORE.setCredentials(v, KOREADER_STORE.getPassword());
|
||||||
KOREADER_STORE.saveToFile();
|
KOREADER_STORE.saveToFile();
|
||||||
},
|
},
|
||||||
"koUsername", StrId::STR_KOREADER_SYNC),
|
"koUsername", StrId::STR_KOREADER_SYNC),
|
||||||
SettingInfo::DynamicString(
|
SettingInfo::DynamicString(
|
||||||
StrId::STR_KOREADER_PASSWORD, [] { return KOREADER_STORE.getPassword(); },
|
StrId::STR_KOREADER_PASSWORD, [](void*) { return KOREADER_STORE.getPassword(); },
|
||||||
[](const std::string& v) {
|
[](void*, const std::string& v) {
|
||||||
KOREADER_STORE.setCredentials(KOREADER_STORE.getUsername(), v);
|
KOREADER_STORE.setCredentials(KOREADER_STORE.getUsername(), v);
|
||||||
KOREADER_STORE.saveToFile();
|
KOREADER_STORE.saveToFile();
|
||||||
},
|
},
|
||||||
@@ -174,8 +174,8 @@ inline const std::vector<SettingInfo> list = {
|
|||||||
.withObfuscated(),
|
.withObfuscated(),
|
||||||
SettingInfo::DynamicEnum(
|
SettingInfo::DynamicEnum(
|
||||||
StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
|
StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
|
||||||
[] { return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod()); },
|
[](void*) { return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod()); },
|
||||||
[](uint8_t v) {
|
[](void*, uint8_t v) {
|
||||||
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
|
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
|
||||||
KOREADER_STORE.saveToFile();
|
KOREADER_STORE.saveToFile();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace {
|
|||||||
// pagesPerRefresh now comes from SETTINGS.getRefreshFrequency()
|
// pagesPerRefresh now comes from SETTINGS.getRefreshFrequency()
|
||||||
constexpr unsigned long skipChapterMs = 700;
|
constexpr unsigned long skipChapterMs = 700;
|
||||||
// pages per minute, first item is 1 to prevent division by zero if accessed
|
// pages per minute, first item is 1 to prevent division by zero if accessed
|
||||||
const std::vector<int> PAGE_TURN_LABELS = {1, 1, 3, 6, 12};
|
constexpr int PAGE_TURN_LABELS[] = {1, 1, 3, 6, 12};
|
||||||
|
|
||||||
void logReaderMemSnapshot(const char* stage) {
|
void logReaderMemSnapshot(const char* stage) {
|
||||||
const uint32_t freeHeap = esp_get_free_heap_size();
|
const uint32_t freeHeap = esp_get_free_heap_size();
|
||||||
@@ -756,7 +756,7 @@ void EpubReaderActivity::applyTextDarkness(const uint8_t textDarkness) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EpubReaderActivity::toggleAutoPageTurn(const uint8_t selectedPageTurnOption) {
|
void EpubReaderActivity::toggleAutoPageTurn(const uint8_t selectedPageTurnOption) {
|
||||||
if (selectedPageTurnOption == 0 || selectedPageTurnOption >= PAGE_TURN_LABELS.size()) {
|
if (selectedPageTurnOption == 0 || selectedPageTurnOption >= std::size(PAGE_TURN_LABELS)) {
|
||||||
automaticPageTurnActive = false;
|
automaticPageTurnActive = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,44 +50,57 @@ void EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes, bool hasStarredPa
|
|||||||
// --- Appearance ---
|
// --- Appearance ---
|
||||||
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_APPEARANCE));
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_APPEARANCE));
|
||||||
|
|
||||||
|
auto* self = this;
|
||||||
|
|
||||||
// Embedded style: cycles default(-1) -> ON(1) -> OFF(0) via DynamicEnum indices 0/1/2
|
// Embedded style: cycles default(-1) -> ON(1) -> OFF(0) via DynamicEnum indices 0/1/2
|
||||||
menuItems.push_back(SettingInfo::DynamicEnum(
|
menuItems.push_back(SettingInfo::DynamicEnumCtx(
|
||||||
StrId::STR_EMBEDDED_STYLE, {StrId::STR_DEFAULT_VALUE, StrId::STR_STATE_ON, StrId::STR_STATE_OFF},
|
StrId::STR_EMBEDDED_STYLE, {StrId::STR_DEFAULT_VALUE, StrId::STR_STATE_ON, StrId::STR_STATE_OFF}, self,
|
||||||
[this]() -> uint8_t {
|
[](void* ctx) -> uint8_t {
|
||||||
if (pendingEmbeddedStyleOverride < 0) return 0;
|
auto* s = static_cast<EpubReaderMenuActivity*>(ctx);
|
||||||
if (pendingEmbeddedStyleOverride > 0) return 1;
|
if (s->pendingEmbeddedStyleOverride < 0) return 0;
|
||||||
|
if (s->pendingEmbeddedStyleOverride > 0) return 1;
|
||||||
return 2;
|
return 2;
|
||||||
},
|
},
|
||||||
[this](uint8_t v) {
|
[](void* ctx, uint8_t v) {
|
||||||
|
auto* s = static_cast<EpubReaderMenuActivity*>(ctx);
|
||||||
if (v == 0)
|
if (v == 0)
|
||||||
pendingEmbeddedStyleOverride = -1;
|
s->pendingEmbeddedStyleOverride = -1;
|
||||||
else if (v == 1)
|
else if (v == 1)
|
||||||
pendingEmbeddedStyleOverride = 1;
|
s->pendingEmbeddedStyleOverride = 1;
|
||||||
else
|
else
|
||||||
pendingEmbeddedStyleOverride = 0;
|
s->pendingEmbeddedStyleOverride = 0;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Image rendering: cycles default(-1) -> display(0) -> placeholder(1) -> suppress(2)
|
// Image rendering: cycles default(-1) -> display(0) -> placeholder(1) -> suppress(2)
|
||||||
menuItems.push_back(SettingInfo::DynamicEnum(
|
menuItems.push_back(SettingInfo::DynamicEnumCtx(
|
||||||
StrId::STR_IMAGES,
|
StrId::STR_IMAGES,
|
||||||
{StrId::STR_DEFAULT_VALUE, StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
|
{StrId::STR_DEFAULT_VALUE, StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
|
||||||
[this]() -> uint8_t { return (pendingImageRenderingOverride < 0) ? 0 : (pendingImageRenderingOverride + 1); },
|
self,
|
||||||
[this](uint8_t v) { pendingImageRenderingOverride = (v == 0) ? -1 : static_cast<int8_t>(v - 1); }));
|
[](void* ctx) -> uint8_t {
|
||||||
|
auto* s = static_cast<EpubReaderMenuActivity*>(ctx);
|
||||||
|
return (s->pendingImageRenderingOverride < 0) ? 0 : (s->pendingImageRenderingOverride + 1);
|
||||||
|
},
|
||||||
|
[](void* ctx, uint8_t v) {
|
||||||
|
auto* s = static_cast<EpubReaderMenuActivity*>(ctx);
|
||||||
|
s->pendingImageRenderingOverride = (v == 0) ? -1 : static_cast<int8_t>(v - 1);
|
||||||
|
}));
|
||||||
|
|
||||||
// Text darkness: straightforward 0-3 cycle
|
// Text darkness: straightforward 0-3 cycle
|
||||||
menuItems.push_back(SettingInfo::DynamicEnum(
|
menuItems.push_back(SettingInfo::DynamicEnumCtx(
|
||||||
StrId::STR_TEXT_DARKNESS, {StrId::STR_NORMAL, StrId::STR_DARK, StrId::STR_EXTRA_DARK, StrId::STR_MAX_DARK},
|
StrId::STR_TEXT_DARKNESS, {StrId::STR_NORMAL, StrId::STR_DARK, StrId::STR_EXTRA_DARK, StrId::STR_MAX_DARK}, self,
|
||||||
[this]() -> uint8_t { return pendingTextDarkness; }, [this](uint8_t v) { pendingTextDarkness = v; }));
|
[](void* ctx) -> uint8_t { return static_cast<EpubReaderMenuActivity*>(ctx)->pendingTextDarkness; },
|
||||||
|
[](void* ctx, uint8_t v) { static_cast<EpubReaderMenuActivity*>(ctx)->pendingTextDarkness = v; }));
|
||||||
|
|
||||||
// Helper functions, reading ruler, auto page turn, orientation
|
// Helper functions, reading ruler, auto page turn, orientation
|
||||||
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_UTILS));
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_UTILS));
|
||||||
// Auto page turn: ACTION type with custom cycling in onActionSelected
|
// Auto page turn: ACTION type with custom cycling in onActionSelected
|
||||||
menuItems.push_back(SettingInfo::Action(StrId::STR_AUTO_TURN_PAGES_PER_MIN, SettingAction::None));
|
menuItems.push_back(SettingInfo::Action(StrId::STR_AUTO_TURN_PAGES_PER_MIN, SettingAction::None));
|
||||||
// Orientation: straightforward 0-3 cycle
|
// Orientation: straightforward 0-3 cycle
|
||||||
menuItems.push_back(SettingInfo::DynamicEnum(
|
menuItems.push_back(SettingInfo::DynamicEnumCtx(
|
||||||
StrId::STR_ORIENTATION,
|
StrId::STR_ORIENTATION,
|
||||||
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, StrId::STR_LANDSCAPE_CCW},
|
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, StrId::STR_LANDSCAPE_CCW}, self,
|
||||||
[this]() -> uint8_t { return pendingOrientation; }, [this](uint8_t v) { pendingOrientation = v; }));
|
[](void* ctx) -> uint8_t { return static_cast<EpubReaderMenuActivity*>(ctx)->pendingOrientation; },
|
||||||
|
[](void* ctx, uint8_t v) { static_cast<EpubReaderMenuActivity*>(ctx)->pendingOrientation = v; }));
|
||||||
|
|
||||||
// --- Synchronisation (only if credentials are set) ---
|
// --- Synchronisation (only if credentials are set) ---
|
||||||
if (KOREADER_STORE.hasCredentials()) {
|
if (KOREADER_STORE.hasCredentials()) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const StrId timeZoneNames[CrossPointSettings::TIMEZONE_COUNT] = {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ClockSettingsActivity::buildMenuItems() {
|
void ClockSettingsActivity::buildMenuItems() {
|
||||||
|
menuItems.reserve(6);
|
||||||
menuItems.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_TITLE));
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_TITLE));
|
||||||
menuItems.push_back(
|
menuItems.push_back(
|
||||||
SettingInfo::Toggle(StrId::STR_USE_CLOCK, &CrossPointSettings::useClock, "useClock", StrId::STR_CAT_SYSTEM));
|
SettingInfo::Toggle(StrId::STR_USE_CLOCK, &CrossPointSettings::useClock, "useClock", StrId::STR_CAT_SYSTEM));
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ KOReaderSettingsActivity::KOReaderSettingsActivity(GfxRenderer& renderer, Mapped
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KOReaderSettingsActivity::buildMenuItems() {
|
void KOReaderSettingsActivity::buildMenuItems() {
|
||||||
|
menuItems.reserve(6);
|
||||||
// Username, Password, Server URL: ACTION items with custom value display
|
// Username, Password, Server URL: ACTION items with custom value display
|
||||||
menuItems.push_back(SettingInfo::Action(StrId::STR_SYNC_SERVER_URL, SettingAction::None)
|
menuItems.push_back(SettingInfo::Action(StrId::STR_SYNC_SERVER_URL, SettingAction::None)
|
||||||
.withSubcategory(StrId::STR_MENU_KOSYNC_SERVER));
|
.withSubcategory(StrId::STR_MENU_KOSYNC_SERVER));
|
||||||
@@ -25,8 +26,8 @@ void KOReaderSettingsActivity::buildMenuItems() {
|
|||||||
// Document matching: DynamicEnum toggling between Filename and Binary
|
// Document matching: DynamicEnum toggling between Filename and Binary
|
||||||
menuItems.push_back(SettingInfo::DynamicEnum(
|
menuItems.push_back(SettingInfo::DynamicEnum(
|
||||||
StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
|
StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
|
||||||
[] { return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod()); },
|
[](void*) { return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod()); },
|
||||||
[](uint8_t v) {
|
[](void*, uint8_t v) {
|
||||||
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
|
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
|
||||||
KOREADER_STORE.saveToFile();
|
KOREADER_STORE.saveToFile();
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ std::string SettingInfo::getDisplayValue() const {
|
|||||||
if (valuePtr)
|
if (valuePtr)
|
||||||
value = SETTINGS.*(valuePtr);
|
value = SETTINGS.*(valuePtr);
|
||||||
else if (valueGetter)
|
else if (valueGetter)
|
||||||
value = valueGetter();
|
value = callValueGetter();
|
||||||
else
|
else
|
||||||
return {};
|
return {};
|
||||||
return std::string(value ? tr(STR_STATE_ON) : tr(STR_STATE_OFF));
|
return std::string(value ? tr(STR_STATE_ON) : tr(STR_STATE_OFF));
|
||||||
@@ -29,7 +29,7 @@ std::string SettingInfo::getDisplayValue() const {
|
|||||||
if (valuePtr)
|
if (valuePtr)
|
||||||
value = SETTINGS.*(valuePtr);
|
value = SETTINGS.*(valuePtr);
|
||||||
else if (valueGetter)
|
else if (valueGetter)
|
||||||
value = valueGetter();
|
value = callValueGetter();
|
||||||
else
|
else
|
||||||
return {};
|
return {};
|
||||||
if (value < enumValues.size()) return std::string(I18N.get(enumValues[value]));
|
if (value < enumValues.size()) return std::string(I18N.get(enumValues[value]));
|
||||||
@@ -37,7 +37,7 @@ std::string SettingInfo::getDisplayValue() const {
|
|||||||
}
|
}
|
||||||
case SettingType::VALUE: {
|
case SettingType::VALUE: {
|
||||||
if (valuePtr) return std::to_string(SETTINGS.*(valuePtr));
|
if (valuePtr) return std::to_string(SETTINGS.*(valuePtr));
|
||||||
if (valueGetter) return std::to_string(valueGetter());
|
if (valueGetter) return std::to_string(callValueGetter());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case SettingType::ACTION:
|
case SettingType::ACTION:
|
||||||
@@ -56,7 +56,7 @@ void SettingInfo::toggleValue() const {
|
|||||||
if (valuePtr) {
|
if (valuePtr) {
|
||||||
SETTINGS.*(valuePtr) = !(SETTINGS.*(valuePtr));
|
SETTINGS.*(valuePtr) = !(SETTINGS.*(valuePtr));
|
||||||
} else if (valueGetter && valueSetter) {
|
} else if (valueGetter && valueSetter) {
|
||||||
valueSetter(!valueGetter());
|
callValueSetter(!callValueGetter());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ void SettingInfo::toggleValue() const {
|
|||||||
if (valuePtr) {
|
if (valuePtr) {
|
||||||
SETTINGS.*(valuePtr) = (SETTINGS.*(valuePtr) + 1) % count;
|
SETTINGS.*(valuePtr) = (SETTINGS.*(valuePtr) + 1) % count;
|
||||||
} else if (valueGetter && valueSetter) {
|
} else if (valueGetter && valueSetter) {
|
||||||
valueSetter((valueGetter() + 1) % count);
|
callValueSetter((callValueGetter() + 1) % count);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <I18n.h>
|
#include <I18n.h>
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -49,11 +48,25 @@ struct SettingInfo {
|
|||||||
size_t stringOffset = 0;
|
size_t stringOffset = 0;
|
||||||
size_t stringMaxLen = 0;
|
size_t stringMaxLen = 0;
|
||||||
|
|
||||||
// Dynamic accessors (for settings stored outside CrossPointSettings, e.g. KOReaderCredentialStore)
|
// Dynamic accessors (for settings stored outside CrossPointSettings, e.g. KOReaderCredentialStore).
|
||||||
std::function<uint8_t()> valueGetter;
|
// Function pointers + opaque context avoid the heap allocation of std::function. Stateless
|
||||||
std::function<void(uint8_t)> valueSetter;
|
// lambdas pass ctx=nullptr; captures must be hand-written as trampoline functions. See
|
||||||
std::function<std::string()> stringGetter;
|
// DynamicEnumCtx / DynamicStringCtx factories below.
|
||||||
std::function<void(const std::string&)> stringSetter;
|
using ValueGetterFn = uint8_t (*)(void*);
|
||||||
|
using ValueSetterFn = void (*)(void*, uint8_t);
|
||||||
|
using StringGetterFn = std::string (*)(void*);
|
||||||
|
using StringSetterFn = void (*)(void*, const std::string&);
|
||||||
|
|
||||||
|
void* accessorCtx = nullptr;
|
||||||
|
ValueGetterFn valueGetter = nullptr;
|
||||||
|
ValueSetterFn valueSetter = nullptr;
|
||||||
|
StringGetterFn stringGetter = nullptr;
|
||||||
|
StringSetterFn stringSetter = nullptr;
|
||||||
|
|
||||||
|
uint8_t callValueGetter() const { return valueGetter(accessorCtx); }
|
||||||
|
void callValueSetter(uint8_t v) const { valueSetter(accessorCtx, v); }
|
||||||
|
std::string callStringGetter() const { return stringGetter(accessorCtx); }
|
||||||
|
void callStringSetter(const std::string& v) const { stringSetter(accessorCtx, v); }
|
||||||
|
|
||||||
SettingInfo& withObfuscated() {
|
SettingInfo& withObfuscated() {
|
||||||
obfuscated = true;
|
obfuscated = true;
|
||||||
@@ -115,33 +128,49 @@ struct SettingInfo {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SettingInfo DynamicEnum(StrId nameId, std::vector<StrId> values, std::function<uint8_t()> getter,
|
// Stateless variant — getter/setter are free/static functions with no captured state.
|
||||||
std::function<void(uint8_t)> setter, const char* key = nullptr,
|
static SettingInfo DynamicEnum(StrId nameId, std::vector<StrId> values, ValueGetterFn getter, ValueSetterFn setter,
|
||||||
StrId category = StrId::STR_NONE_OPT) {
|
const char* key = nullptr, StrId category = StrId::STR_NONE_OPT) {
|
||||||
SettingInfo s;
|
SettingInfo s;
|
||||||
s.nameId = nameId;
|
s.nameId = nameId;
|
||||||
s.type = SettingType::ENUM;
|
s.type = SettingType::ENUM;
|
||||||
s.enumValues = std::move(values);
|
s.enumValues = std::move(values);
|
||||||
s.valueGetter = std::move(getter);
|
s.valueGetter = getter;
|
||||||
s.valueSetter = std::move(setter);
|
s.valueSetter = setter;
|
||||||
s.key = key;
|
s.key = key;
|
||||||
s.category = category;
|
s.category = category;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SettingInfo DynamicString(StrId nameId, std::function<std::string()> getter,
|
// Context-carrying variant — trampolines receive `ctx` as first argument and cast it back to
|
||||||
std::function<void(const std::string&)> setter, const char* key = nullptr,
|
// their concrete owner type.
|
||||||
StrId category = StrId::STR_NONE_OPT) {
|
static SettingInfo DynamicEnumCtx(StrId nameId, std::vector<StrId> values, void* ctx, ValueGetterFn getter,
|
||||||
|
ValueSetterFn setter, const char* key = nullptr,
|
||||||
|
StrId category = StrId::STR_NONE_OPT) {
|
||||||
|
SettingInfo s = DynamicEnum(nameId, std::move(values), getter, setter, key, category);
|
||||||
|
s.accessorCtx = ctx;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SettingInfo DynamicString(StrId nameId, StringGetterFn getter, StringSetterFn setter,
|
||||||
|
const char* key = nullptr, StrId category = StrId::STR_NONE_OPT) {
|
||||||
SettingInfo s;
|
SettingInfo s;
|
||||||
s.nameId = nameId;
|
s.nameId = nameId;
|
||||||
s.type = SettingType::STRING;
|
s.type = SettingType::STRING;
|
||||||
s.stringGetter = std::move(getter);
|
s.stringGetter = getter;
|
||||||
s.stringSetter = std::move(setter);
|
s.stringSetter = setter;
|
||||||
s.key = key;
|
s.key = key;
|
||||||
s.category = category;
|
s.category = category;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SettingInfo DynamicStringCtx(StrId nameId, void* ctx, StringGetterFn getter, StringSetterFn setter,
|
||||||
|
const char* key = nullptr, StrId category = StrId::STR_NONE_OPT) {
|
||||||
|
SettingInfo s = DynamicString(nameId, getter, setter, key, category);
|
||||||
|
s.accessorCtx = ctx;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
static SettingInfo Separator(StrId nameId) {
|
static SettingInfo Separator(StrId nameId) {
|
||||||
SettingInfo s;
|
SettingInfo s;
|
||||||
s.nameId = nameId;
|
s.nameId = nameId;
|
||||||
|
|||||||
@@ -30,16 +30,22 @@ void SettingsActivity::onEnter() {
|
|||||||
controlsSettings.clear();
|
controlsSettings.clear();
|
||||||
systemSettings.clear();
|
systemSettings.clear();
|
||||||
submenuData.clear();
|
submenuData.clear();
|
||||||
|
displaySettings.reserve(20);
|
||||||
|
readerSettings.reserve(30);
|
||||||
|
controlsSettings.reserve(8);
|
||||||
|
systemSettings.reserve(20);
|
||||||
|
submenuData.reserve(4);
|
||||||
|
|
||||||
StrId lastDisplaySub = StrId::STR_NONE_OPT;
|
StrId lastDisplaySub = StrId::STR_NONE_OPT;
|
||||||
StrId lastReaderSub = StrId::STR_NONE_OPT;
|
StrId lastReaderSub = StrId::STR_NONE_OPT;
|
||||||
StrId lastControlsSub = StrId::STR_NONE_OPT;
|
StrId lastControlsSub = StrId::STR_NONE_OPT;
|
||||||
StrId lastSystemSub = StrId::STR_NONE_OPT;
|
StrId lastSystemSub = StrId::STR_NONE_OPT;
|
||||||
|
|
||||||
auto addTo = [this](std::vector<SettingInfo>& vec, StrId& lastSub, SettingInfo s) {
|
// Shared placement logic — locates submenu target or inserts separator.
|
||||||
|
// Returns the vector the caller should push into (either `vec` or a submenu's items).
|
||||||
|
auto locateTarget = [this](std::vector<SettingInfo>& vec, StrId& lastSub,
|
||||||
|
const SettingInfo& s) -> std::vector<SettingInfo>* {
|
||||||
if (s.submenu != StrId::STR_NONE_OPT) {
|
if (s.submenu != StrId::STR_NONE_OPT) {
|
||||||
// Item belongs to a submenu — collect it and insert a placeholder in the main
|
|
||||||
// list the first time this submenu ID is encountered.
|
|
||||||
auto it = std::find_if(submenuData.begin(), submenuData.end(),
|
auto it = std::find_if(submenuData.begin(), submenuData.end(),
|
||||||
[&s](const SubmenuData& d) { return d.id == s.submenu; });
|
[&s](const SubmenuData& d) { return d.id == s.submenu; });
|
||||||
if (it == submenuData.end()) {
|
if (it == submenuData.end()) {
|
||||||
@@ -47,14 +53,21 @@ void SettingsActivity::onEnter() {
|
|||||||
submenuData.push_back({s.submenu, {}});
|
submenuData.push_back({s.submenu, {}});
|
||||||
it = submenuData.end() - 1;
|
it = submenuData.end() - 1;
|
||||||
}
|
}
|
||||||
it->items.push_back(std::move(s));
|
return &it->items;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (s.subcategory != StrId::STR_NONE_OPT && s.subcategory != lastSub) {
|
if (s.subcategory != StrId::STR_NONE_OPT && s.subcategory != lastSub) {
|
||||||
vec.push_back(SettingInfo::Separator(s.subcategory));
|
vec.push_back(SettingInfo::Separator(s.subcategory));
|
||||||
lastSub = s.subcategory;
|
lastSub = s.subcategory;
|
||||||
}
|
}
|
||||||
vec.push_back(std::move(s));
|
return &vec;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto addTo = [&locateTarget](std::vector<SettingInfo>& vec, StrId& lastSub, const SettingInfo& s) {
|
||||||
|
locateTarget(vec, lastSub, s)->push_back(s);
|
||||||
|
};
|
||||||
|
auto addToMoved = [&locateTarget](std::vector<SettingInfo>& vec, StrId& lastSub, SettingInfo&& s) {
|
||||||
|
auto* target = locateTarget(vec, lastSub, s);
|
||||||
|
target->push_back(std::move(s));
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& setting : getSettingsList()) {
|
for (const auto& setting : getSettingsList()) {
|
||||||
@@ -80,34 +93,34 @@ void SettingsActivity::onEnter() {
|
|||||||
controlsSettings.insert(controlsSettings.begin(),
|
controlsSettings.insert(controlsSettings.begin(),
|
||||||
SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons));
|
SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons));
|
||||||
|
|
||||||
addTo(readerSettings, lastReaderSub,
|
addToMoved(readerSettings, lastReaderSub,
|
||||||
SettingInfo::Action(StrId::STR_CUSTOMISE_STATUS_BAR, SettingAction::CustomiseStatusBar));
|
SettingInfo::Action(StrId::STR_CUSTOMISE_STATUS_BAR, SettingAction::CustomiseStatusBar));
|
||||||
|
|
||||||
addTo(systemSettings, lastSystemSub, SettingInfo::Action(StrId::STR_LANGUAGE, SettingAction::Language));
|
addToMoved(systemSettings, lastSystemSub, SettingInfo::Action(StrId::STR_LANGUAGE, SettingAction::Language));
|
||||||
addTo(systemSettings, lastSystemSub,
|
addToMoved(systemSettings, lastSystemSub,
|
||||||
SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network)
|
std::move(SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network)
|
||||||
.withSubcategory(StrId::STR_MENU_SYS_NETWORK));
|
.withSubcategory(StrId::STR_MENU_SYS_NETWORK)));
|
||||||
addTo(systemSettings, lastSystemSub,
|
addToMoved(systemSettings, lastSystemSub,
|
||||||
SettingInfo::Action(StrId::STR_KOREADER_SYNC, SettingAction::KOReaderSync)
|
std::move(SettingInfo::Action(StrId::STR_KOREADER_SYNC, SettingAction::KOReaderSync)
|
||||||
.withSubcategory(StrId::STR_MENU_SYS_NETWORK));
|
.withSubcategory(StrId::STR_MENU_SYS_NETWORK)));
|
||||||
addTo(systemSettings, lastSystemSub,
|
addToMoved(systemSettings, lastSystemSub,
|
||||||
SettingInfo::Action(StrId::STR_OPDS_BROWSER, SettingAction::OPDSBrowser)
|
std::move(SettingInfo::Action(StrId::STR_OPDS_BROWSER, SettingAction::OPDSBrowser)
|
||||||
.withSubcategory(StrId::STR_MENU_SYS_NETWORK));
|
.withSubcategory(StrId::STR_MENU_SYS_NETWORK)));
|
||||||
addTo(systemSettings, lastSystemSub,
|
addToMoved(systemSettings, lastSystemSub,
|
||||||
SettingInfo::Action(StrId::STR_CLOCK_SETTINGS, SettingAction::ClockSettings)
|
std::move(SettingInfo::Action(StrId::STR_CLOCK_SETTINGS, SettingAction::ClockSettings)
|
||||||
.withSubcategory(StrId::STR_MENU_SYS_TOOLS));
|
.withSubcategory(StrId::STR_MENU_SYS_TOOLS)));
|
||||||
addTo(systemSettings, lastSystemSub,
|
addToMoved(systemSettings, lastSystemSub,
|
||||||
SettingInfo::Action(StrId::STR_WEATHER_SETTINGS, SettingAction::Weather)
|
std::move(SettingInfo::Action(StrId::STR_WEATHER_SETTINGS, SettingAction::Weather)
|
||||||
.withSubcategory(StrId::STR_MENU_SYS_TOOLS));
|
.withSubcategory(StrId::STR_MENU_SYS_TOOLS)));
|
||||||
addTo(systemSettings, lastSystemSub,
|
addToMoved(systemSettings, lastSystemSub,
|
||||||
SettingInfo::Action(StrId::STR_CLEAR_READING_CACHE, SettingAction::ClearCache)
|
std::move(SettingInfo::Action(StrId::STR_CLEAR_READING_CACHE, SettingAction::ClearCache)
|
||||||
.withSubcategory(StrId::STR_MENU_SYS_SYSTEM));
|
.withSubcategory(StrId::STR_MENU_SYS_SYSTEM)));
|
||||||
addTo(systemSettings, lastSystemSub,
|
addToMoved(systemSettings, lastSystemSub,
|
||||||
SettingInfo::Action(StrId::STR_CHECK_UPDATES, SettingAction::CheckForUpdates)
|
std::move(SettingInfo::Action(StrId::STR_CHECK_UPDATES, SettingAction::CheckForUpdates)
|
||||||
.withSubcategory(StrId::STR_MENU_SYS_SYSTEM));
|
.withSubcategory(StrId::STR_MENU_SYS_SYSTEM)));
|
||||||
addTo(systemSettings, lastSystemSub,
|
addToMoved(systemSettings, lastSystemSub,
|
||||||
SettingInfo::Action(StrId::STR_SYSTEM_INFO, SettingAction::SystemInfo)
|
std::move(SettingInfo::Action(StrId::STR_SYSTEM_INFO, SettingAction::SystemInfo)
|
||||||
.withSubcategory(StrId::STR_MENU_SYS_SYSTEM));
|
.withSubcategory(StrId::STR_MENU_SYS_SYSTEM)));
|
||||||
|
|
||||||
// Reset selection to first category
|
// Reset selection to first category
|
||||||
selectedCategoryIndex = 0;
|
selectedCategoryIndex = 0;
|
||||||
@@ -212,11 +225,12 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
auto resultHandler = [this](const ActivityResult&) { SETTINGS.saveToFile(); };
|
auto resultHandler = [this](const ActivityResult&) { SETTINGS.saveToFile(); };
|
||||||
|
|
||||||
if (setting.action == SettingAction::Submenu) {
|
if (setting.action == SettingAction::Submenu) {
|
||||||
const auto it = std::find_if(submenuData.cbegin(), submenuData.cend(),
|
auto it = std::find_if(submenuData.begin(), submenuData.end(),
|
||||||
[&setting](const SubmenuData& d) { return d.id == setting.nameId; });
|
[&setting](const SubmenuData& d) { return d.id == setting.nameId; });
|
||||||
if (it != submenuData.cend()) {
|
if (it != submenuData.end()) {
|
||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
std::make_unique<SettingsSubmenuActivity>(renderer, mappedInput, setting.nameId, it->items), resultHandler);
|
std::make_unique<SettingsSubmenuActivity>(renderer, mappedInput, setting.nameId, std::move(it->items)),
|
||||||
|
resultHandler);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto activity = createActivityForAction(setting.action, renderer, mappedInput);
|
auto activity = createActivityForAction(setting.action, renderer, mappedInput);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
void WeatherSettingsActivity::buildMenuItems() {
|
void WeatherSettingsActivity::buildMenuItems() {
|
||||||
|
menuItems.reserve(10);
|
||||||
menuItems.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_TITLE));
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_TITLE));
|
||||||
menuItems.push_back(SettingInfo::Toggle(StrId::STR_USE_WEATHER, &CrossPointSettings::useWeather, "useWeather",
|
menuItems.push_back(SettingInfo::Toggle(StrId::STR_USE_WEATHER, &CrossPointSettings::useWeather, "useWeather",
|
||||||
StrId::STR_CAT_SYSTEM));
|
StrId::STR_CAT_SYSTEM));
|
||||||
|
|||||||
@@ -1240,7 +1240,7 @@ void CrossPointWebServer::handleGetSettings() const {
|
|||||||
if (s.valuePtr) {
|
if (s.valuePtr) {
|
||||||
doc["value"] = static_cast<int>(SETTINGS.*(s.valuePtr));
|
doc["value"] = static_cast<int>(SETTINGS.*(s.valuePtr));
|
||||||
} else if (s.valueGetter) {
|
} else if (s.valueGetter) {
|
||||||
doc["value"] = static_cast<int>(s.valueGetter());
|
doc["value"] = static_cast<int>(s.callValueGetter());
|
||||||
}
|
}
|
||||||
JsonArray options = doc["options"].to<JsonArray>();
|
JsonArray options = doc["options"].to<JsonArray>();
|
||||||
for (const auto& opt : s.enumValues) {
|
for (const auto& opt : s.enumValues) {
|
||||||
@@ -1261,7 +1261,7 @@ void CrossPointWebServer::handleGetSettings() const {
|
|||||||
case SettingType::STRING: {
|
case SettingType::STRING: {
|
||||||
doc["type"] = "string";
|
doc["type"] = "string";
|
||||||
if (s.stringGetter) {
|
if (s.stringGetter) {
|
||||||
doc["value"] = s.stringGetter();
|
doc["value"] = s.callStringGetter();
|
||||||
} else if (s.stringMaxLen > 0) {
|
} else if (s.stringMaxLen > 0) {
|
||||||
doc["value"] = reinterpret_cast<const char*>(&SETTINGS) + s.stringOffset;
|
doc["value"] = reinterpret_cast<const char*>(&SETTINGS) + s.stringOffset;
|
||||||
}
|
}
|
||||||
@@ -1326,7 +1326,7 @@ void CrossPointWebServer::handlePostSettings() {
|
|||||||
if (s.valuePtr) {
|
if (s.valuePtr) {
|
||||||
SETTINGS.*(s.valuePtr) = static_cast<uint8_t>(val);
|
SETTINGS.*(s.valuePtr) = static_cast<uint8_t>(val);
|
||||||
} else if (s.valueSetter) {
|
} else if (s.valueSetter) {
|
||||||
s.valueSetter(static_cast<uint8_t>(val));
|
s.callValueSetter(static_cast<uint8_t>(val));
|
||||||
}
|
}
|
||||||
applied++;
|
applied++;
|
||||||
}
|
}
|
||||||
@@ -1345,7 +1345,7 @@ void CrossPointWebServer::handlePostSettings() {
|
|||||||
case SettingType::STRING: {
|
case SettingType::STRING: {
|
||||||
const std::string val = doc[s.key].as<std::string>();
|
const std::string val = doc[s.key].as<std::string>();
|
||||||
if (s.stringSetter) {
|
if (s.stringSetter) {
|
||||||
s.stringSetter(val);
|
s.callStringSetter(val);
|
||||||
} else if (s.stringMaxLen > 0) {
|
} else if (s.stringMaxLen > 0) {
|
||||||
char* ptr = reinterpret_cast<char*>(&SETTINGS) + s.stringOffset;
|
char* ptr = reinterpret_cast<char*>(&SETTINGS) + s.stringOffset;
|
||||||
strncpy(ptr, val.c_str(), s.stringMaxLen - 1);
|
strncpy(ptr, val.c_str(), s.stringMaxLen - 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user