Introduce common helper functions
This commit is contained in:
@@ -3,8 +3,6 @@
|
||||
#include <GfxRenderer.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "KOReaderAuthActivity.h"
|
||||
#include "KOReaderCredentialStore.h"
|
||||
#include "MappedInputManager.h"
|
||||
@@ -12,51 +10,58 @@
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
namespace {
|
||||
constexpr int MENU_ITEMS = 6;
|
||||
const StrId menuNames[MENU_ITEMS] = {StrId::STR_USERNAME, StrId::STR_PASSWORD, StrId::STR_SYNC_SERVER_URL,
|
||||
StrId::STR_DOCUMENT_MATCHING, StrId::STR_AUTHENTICATE, StrId::STR_REGISTER};
|
||||
} // namespace
|
||||
|
||||
void KOReaderSettingsActivity::onEnter() {
|
||||
Activity::onEnter();
|
||||
|
||||
selectedIndex = 0;
|
||||
requestUpdate();
|
||||
KOReaderSettingsActivity::KOReaderSettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||
: MenuListActivity("KOReaderSettings", renderer, mappedInput) {
|
||||
buildMenuItems();
|
||||
}
|
||||
|
||||
void KOReaderSettingsActivity::onExit() { Activity::onExit(); }
|
||||
void KOReaderSettingsActivity::buildMenuItems() {
|
||||
// Username, Password, Server URL: ACTION items with custom value display
|
||||
menuItems.push_back(SettingInfo::Action(StrId::STR_USERNAME, SettingAction::None));
|
||||
menuItems.push_back(SettingInfo::Action(StrId::STR_PASSWORD, SettingAction::None));
|
||||
menuItems.push_back(SettingInfo::Action(StrId::STR_SYNC_SERVER_URL, SettingAction::None));
|
||||
|
||||
void KOReaderSettingsActivity::loop() {
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// Document matching: DynamicEnum toggling between Filename and Binary
|
||||
menuItems.push_back(SettingInfo::DynamicEnum(
|
||||
StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
|
||||
[] { return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod()); },
|
||||
[](uint8_t v) {
|
||||
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
|
||||
KOREADER_STORE.saveToFile();
|
||||
}));
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
||||
handleSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedIndex = (selectedIndex + 1) % MENU_ITEMS;
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS;
|
||||
requestUpdate();
|
||||
});
|
||||
// Authenticate and Register: ACTION items
|
||||
menuItems.push_back(SettingInfo::Action(StrId::STR_AUTHENTICATE, SettingAction::None));
|
||||
menuItems.push_back(SettingInfo::Action(StrId::STR_REGISTER, SettingAction::None));
|
||||
}
|
||||
|
||||
void KOReaderSettingsActivity::handleSelection() {
|
||||
if (selectedIndex == 0) {
|
||||
// Username
|
||||
std::string KOReaderSettingsActivity::getItemValueString(int index) const {
|
||||
const auto& item = menuItems[index];
|
||||
|
||||
if (item.nameId == StrId::STR_USERNAME) {
|
||||
auto username = KOREADER_STORE.getUsername();
|
||||
return username.empty() ? std::string(tr(STR_NOT_SET)) : username;
|
||||
}
|
||||
if (item.nameId == StrId::STR_PASSWORD) {
|
||||
return KOREADER_STORE.getPassword().empty() ? std::string(tr(STR_NOT_SET)) : std::string("******");
|
||||
}
|
||||
if (item.nameId == StrId::STR_SYNC_SERVER_URL) {
|
||||
auto serverUrl = KOREADER_STORE.getServerUrl();
|
||||
return serverUrl.empty() ? std::string(tr(STR_DEFAULT_VALUE)) : serverUrl;
|
||||
}
|
||||
if (item.nameId == StrId::STR_AUTHENTICATE || item.nameId == StrId::STR_REGISTER) {
|
||||
return KOREADER_STORE.hasCredentials() ? "" : std::string("[") + tr(STR_SET_CREDENTIALS_FIRST) + "]";
|
||||
}
|
||||
|
||||
return MenuListActivity::getItemValueString(index);
|
||||
}
|
||||
|
||||
void KOReaderSettingsActivity::onActionSelected(int index) {
|
||||
const auto& item = menuItems[index];
|
||||
|
||||
if (item.nameId == StrId::STR_USERNAME) {
|
||||
startActivityForResult(std::make_unique<KeyboardEntryActivity>(renderer, mappedInput, tr(STR_KOREADER_USERNAME),
|
||||
KOREADER_STORE.getUsername(),
|
||||
64, // maxLength
|
||||
false), // not password
|
||||
KOREADER_STORE.getUsername(), 64, false),
|
||||
[this](const ActivityResult& result) {
|
||||
if (!result.isCancelled) {
|
||||
const auto& kb = std::get<KeyboardResult>(result.data);
|
||||
@@ -64,12 +69,9 @@ void KOReaderSettingsActivity::handleSelection() {
|
||||
KOREADER_STORE.saveToFile();
|
||||
}
|
||||
});
|
||||
} else if (selectedIndex == 1) {
|
||||
// Password
|
||||
} else if (item.nameId == StrId::STR_PASSWORD) {
|
||||
startActivityForResult(std::make_unique<KeyboardEntryActivity>(renderer, mappedInput, tr(STR_KOREADER_PASSWORD),
|
||||
KOREADER_STORE.getPassword(),
|
||||
64, // maxLength
|
||||
false), // show characters
|
||||
KOREADER_STORE.getPassword(), 64, false),
|
||||
[this](const ActivityResult& result) {
|
||||
if (!result.isCancelled) {
|
||||
const auto& kb = std::get<KeyboardResult>(result.data);
|
||||
@@ -77,14 +79,11 @@ void KOReaderSettingsActivity::handleSelection() {
|
||||
KOREADER_STORE.saveToFile();
|
||||
}
|
||||
});
|
||||
} else if (selectedIndex == 2) {
|
||||
// Sync Server URL - prefill with https:// if empty to save typing
|
||||
} else if (item.nameId == StrId::STR_SYNC_SERVER_URL) {
|
||||
const std::string currentUrl = KOREADER_STORE.getServerUrl();
|
||||
const std::string prefillUrl = currentUrl.empty() ? "https://" : currentUrl;
|
||||
startActivityForResult(
|
||||
std::make_unique<KeyboardEntryActivity>(renderer, mappedInput, tr(STR_SYNC_SERVER_URL), prefillUrl,
|
||||
128, // maxLength - URLs can be long
|
||||
false), // not password
|
||||
std::make_unique<KeyboardEntryActivity>(renderer, mappedInput, tr(STR_SYNC_SERVER_URL), prefillUrl, 128, false),
|
||||
[this](const ActivityResult& result) {
|
||||
if (!result.isCancelled) {
|
||||
const auto& kb = std::get<KeyboardResult>(result.data);
|
||||
@@ -93,28 +92,13 @@ void KOReaderSettingsActivity::handleSelection() {
|
||||
KOREADER_STORE.saveToFile();
|
||||
}
|
||||
});
|
||||
} else if (selectedIndex == 3) {
|
||||
// Document Matching - toggle between Filename and Binary
|
||||
const auto current = KOREADER_STORE.getMatchMethod();
|
||||
const auto newMethod =
|
||||
(current == DocumentMatchMethod::FILENAME) ? DocumentMatchMethod::BINARY : DocumentMatchMethod::FILENAME;
|
||||
KOREADER_STORE.setMatchMethod(newMethod);
|
||||
KOREADER_STORE.saveToFile();
|
||||
requestUpdate();
|
||||
} else if (selectedIndex == 4) {
|
||||
// Authenticate
|
||||
if (!KOREADER_STORE.hasCredentials()) {
|
||||
// Can't authenticate without credentials - just show message briefly
|
||||
return;
|
||||
}
|
||||
} else if (item.nameId == StrId::STR_AUTHENTICATE) {
|
||||
if (!KOREADER_STORE.hasCredentials()) return;
|
||||
startActivityForResult(
|
||||
std::make_unique<KOReaderAuthActivity>(renderer, mappedInput, KOReaderAuthActivity::Mode::LOGIN),
|
||||
[](const ActivityResult&) {});
|
||||
} else if (selectedIndex == 5) {
|
||||
// Register
|
||||
if (!KOREADER_STORE.hasCredentials()) {
|
||||
return;
|
||||
}
|
||||
} else if (item.nameId == StrId::STR_REGISTER) {
|
||||
if (!KOREADER_STORE.hasCredentials()) return;
|
||||
startActivityForResult(
|
||||
std::make_unique<KOReaderAuthActivity>(renderer, mappedInput, KOReaderAuthActivity::Mode::REGISTER),
|
||||
[](const ActivityResult&) {});
|
||||
@@ -132,33 +116,8 @@ void KOReaderSettingsActivity::render(RenderLock&&) {
|
||||
|
||||
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
|
||||
const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing * 2;
|
||||
GUI.drawList(
|
||||
renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, static_cast<int>(MENU_ITEMS),
|
||||
static_cast<int>(selectedIndex), [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr,
|
||||
nullptr,
|
||||
[this](int index) {
|
||||
// Draw status for each setting
|
||||
if (index == 0) {
|
||||
auto username = KOREADER_STORE.getUsername();
|
||||
return username.empty() ? std::string(tr(STR_NOT_SET)) : username;
|
||||
} else if (index == 1) {
|
||||
return KOREADER_STORE.getPassword().empty() ? std::string(tr(STR_NOT_SET)) : std::string("******");
|
||||
} else if (index == 2) {
|
||||
auto serverUrl = KOREADER_STORE.getServerUrl();
|
||||
return serverUrl.empty() ? std::string(tr(STR_DEFAULT_VALUE)) : serverUrl;
|
||||
} else if (index == 3) {
|
||||
return KOREADER_STORE.getMatchMethod() == DocumentMatchMethod::FILENAME ? std::string(tr(STR_FILENAME))
|
||||
: std::string(tr(STR_BINARY));
|
||||
} else if (index == 4) {
|
||||
return KOREADER_STORE.hasCredentials() ? "" : std::string("[") + tr(STR_SET_CREDENTIALS_FIRST) + "]";
|
||||
} else if (index == 5) {
|
||||
return KOREADER_STORE.hasCredentials() ? "" : std::string("[") + tr(STR_SET_CREDENTIALS_FIRST) + "]";
|
||||
}
|
||||
return std::string(tr(STR_NOT_SET));
|
||||
},
|
||||
true);
|
||||
drawMenuList(Rect{contentRect.x, contentTop, contentRect.width, contentHeight});
|
||||
|
||||
// Draw help text at bottom
|
||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "activities/Activity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
#include "activities/MenuListActivity.h"
|
||||
|
||||
/**
|
||||
* Submenu for KOReader Sync settings.
|
||||
* Shows username, password, and authenticate options.
|
||||
* Shows username, password, server URL, document matching, authenticate, and register.
|
||||
*/
|
||||
class KOReaderSettingsActivity final : public Activity {
|
||||
class KOReaderSettingsActivity final : public MenuListActivity {
|
||||
public:
|
||||
explicit KOReaderSettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||
: Activity("KOReaderSettings", renderer, mappedInput) {}
|
||||
explicit KOReaderSettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput);
|
||||
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
void loop() override;
|
||||
void render(RenderLock&&) override;
|
||||
|
||||
private:
|
||||
ButtonNavigator buttonNavigator;
|
||||
void buildMenuItems();
|
||||
|
||||
size_t selectedIndex = 0;
|
||||
|
||||
void handleSelection();
|
||||
// MenuListActivity overrides
|
||||
std::string getItemValueString(int index) const override;
|
||||
void onActionSelected(int index) override;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "SettingActionDispatch.h"
|
||||
|
||||
#include "ButtonRemapActivity.h"
|
||||
#include "CalibreSettingsActivity.h"
|
||||
#include "ClearCacheActivity.h"
|
||||
#include "ClockSettingsActivity.h"
|
||||
#include "DetectTimezoneActivity.h"
|
||||
#include "KOReaderSettingsActivity.h"
|
||||
#include "LanguageSelectActivity.h"
|
||||
#include "OtaUpdateActivity.h"
|
||||
#include "StatusBarSettingsActivity.h"
|
||||
#include "SyncTimeActivity.h"
|
||||
#include "SystemInformationActivity.h"
|
||||
#include "activities/network/WifiSelectionActivity.h"
|
||||
#include "activities/weather/WeatherSettingsActivity.h"
|
||||
|
||||
std::unique_ptr<Activity> createActivityForAction(SettingAction action, GfxRenderer& renderer,
|
||||
MappedInputManager& mappedInput) {
|
||||
switch (action) {
|
||||
case SettingAction::RemapFrontButtons:
|
||||
return std::make_unique<ButtonRemapActivity>(renderer, mappedInput);
|
||||
case SettingAction::CustomiseStatusBar:
|
||||
return std::make_unique<StatusBarSettingsActivity>(renderer, mappedInput);
|
||||
case SettingAction::ClockSettings:
|
||||
return std::make_unique<ClockSettingsActivity>(renderer, mappedInput);
|
||||
case SettingAction::KOReaderSync:
|
||||
return std::make_unique<KOReaderSettingsActivity>(renderer, mappedInput);
|
||||
case SettingAction::OPDSBrowser:
|
||||
return std::make_unique<CalibreSettingsActivity>(renderer, mappedInput);
|
||||
case SettingAction::Network:
|
||||
return std::make_unique<WifiSelectionActivity>(renderer, mappedInput, false);
|
||||
case SettingAction::ClearCache:
|
||||
return std::make_unique<ClearCacheActivity>(renderer, mappedInput);
|
||||
case SettingAction::CheckForUpdates:
|
||||
return std::make_unique<OtaUpdateActivity>(renderer, mappedInput);
|
||||
case SettingAction::Language:
|
||||
return std::make_unique<LanguageSelectActivity>(renderer, mappedInput);
|
||||
case SettingAction::Weather:
|
||||
return std::make_unique<WeatherSettingsActivity>(renderer, mappedInput);
|
||||
case SettingAction::SystemInfo:
|
||||
return std::make_unique<SystemInformationActivity>(renderer, mappedInput);
|
||||
case SettingAction::SyncTime:
|
||||
return std::make_unique<SyncTimeActivity>(renderer, mappedInput);
|
||||
case SettingAction::DetectTimezone:
|
||||
return std::make_unique<DetectTimezoneActivity>(renderer, mappedInput);
|
||||
case SettingAction::Submenu:
|
||||
case SettingAction::None:
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
#include "SettingInfo.h"
|
||||
|
||||
class Activity;
|
||||
class GfxRenderer;
|
||||
class MappedInputManager;
|
||||
|
||||
// Creates the sub-activity corresponding to the given SettingAction.
|
||||
// Returns nullptr for None, Submenu, or unknown actions (caller handles those).
|
||||
std::unique_ptr<Activity> createActivityForAction(SettingAction action, GfxRenderer& renderer,
|
||||
MappedInputManager& mappedInput);
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "SettingInfo.h"
|
||||
|
||||
#include <I18n.h>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "components/UITheme.h"
|
||||
|
||||
std::string SettingInfo::getTitle() const {
|
||||
const auto t = I18N.get(nameId);
|
||||
return isSeparator ? UITheme::makeSeparatorTitle(t) : t;
|
||||
}
|
||||
|
||||
std::string SettingInfo::getDisplayValue() const {
|
||||
if (isSeparator) return {};
|
||||
|
||||
switch (type) {
|
||||
case SettingType::TOGGLE: {
|
||||
bool value;
|
||||
if (valuePtr)
|
||||
value = SETTINGS.*(valuePtr);
|
||||
else if (valueGetter)
|
||||
value = valueGetter();
|
||||
else
|
||||
return {};
|
||||
return std::string(value ? tr(STR_STATE_ON) : tr(STR_STATE_OFF));
|
||||
}
|
||||
case SettingType::ENUM: {
|
||||
uint8_t value;
|
||||
if (valuePtr)
|
||||
value = SETTINGS.*(valuePtr);
|
||||
else if (valueGetter)
|
||||
value = valueGetter();
|
||||
else
|
||||
return {};
|
||||
if (value < enumValues.size()) return std::string(I18N.get(enumValues[value]));
|
||||
return {};
|
||||
}
|
||||
case SettingType::VALUE: {
|
||||
if (valuePtr) return std::to_string(SETTINGS.*(valuePtr));
|
||||
if (valueGetter) return std::to_string(valueGetter());
|
||||
return {};
|
||||
}
|
||||
case SettingType::ACTION:
|
||||
return std::string(">>");
|
||||
case SettingType::STRING:
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void SettingInfo::toggleValue() const {
|
||||
if (isSeparator) return;
|
||||
|
||||
switch (type) {
|
||||
case SettingType::TOGGLE:
|
||||
if (valuePtr) {
|
||||
SETTINGS.*(valuePtr) = !(SETTINGS.*(valuePtr));
|
||||
} else if (valueGetter && valueSetter) {
|
||||
valueSetter(!valueGetter());
|
||||
}
|
||||
break;
|
||||
|
||||
case SettingType::ENUM: {
|
||||
const auto count = static_cast<uint8_t>(enumValues.size());
|
||||
if (count == 0) break;
|
||||
if (valuePtr) {
|
||||
SETTINGS.*(valuePtr) = (SETTINGS.*(valuePtr) + 1) % count;
|
||||
} else if (valueGetter && valueSetter) {
|
||||
valueSetter((valueGetter() + 1) % count);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SettingType::VALUE:
|
||||
if (valuePtr) {
|
||||
const auto current = static_cast<int8_t>(SETTINGS.*(valuePtr));
|
||||
SETTINGS.*(valuePtr) =
|
||||
(current + valueRange.step > valueRange.max) ? valueRange.min : current + valueRange.step;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
#pragma once
|
||||
#include <I18n.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
|
||||
enum class SettingType { TOGGLE, ENUM, ACTION, VALUE, STRING };
|
||||
|
||||
enum class SettingAction {
|
||||
None,
|
||||
RemapFrontButtons,
|
||||
CustomiseStatusBar,
|
||||
ClockSettings,
|
||||
KOReaderSync,
|
||||
OPDSBrowser,
|
||||
Network,
|
||||
ClearCache,
|
||||
CheckForUpdates,
|
||||
Language,
|
||||
SystemInfo,
|
||||
DetectTimezone,
|
||||
SyncTime,
|
||||
Weather,
|
||||
Submenu,
|
||||
};
|
||||
|
||||
struct SettingInfo {
|
||||
StrId nameId;
|
||||
SettingType type;
|
||||
uint8_t CrossPointSettings::* valuePtr = nullptr;
|
||||
std::vector<StrId> enumValues;
|
||||
SettingAction action = SettingAction::None;
|
||||
|
||||
struct ValueRange {
|
||||
uint8_t min;
|
||||
uint8_t max;
|
||||
uint8_t step;
|
||||
};
|
||||
ValueRange valueRange = {};
|
||||
|
||||
const char* key = nullptr; // JSON API key (nullptr for ACTION types)
|
||||
StrId category = StrId::STR_NONE_OPT; // Category for web UI grouping
|
||||
bool obfuscated = false; // Save/load via base64 obfuscation (passwords)
|
||||
|
||||
// Direct char[] string fields (for settings stored in CrossPointSettings)
|
||||
size_t stringOffset = 0;
|
||||
size_t stringMaxLen = 0;
|
||||
|
||||
// Dynamic accessors (for settings stored outside CrossPointSettings, e.g. KOReaderCredentialStore)
|
||||
std::function<uint8_t()> valueGetter;
|
||||
std::function<void(uint8_t)> valueSetter;
|
||||
std::function<std::string()> stringGetter;
|
||||
std::function<void(const std::string&)> stringSetter;
|
||||
|
||||
SettingInfo& withObfuscated() {
|
||||
obfuscated = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static SettingInfo Toggle(StrId nameId, uint8_t CrossPointSettings::* ptr, const char* key = nullptr,
|
||||
StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::TOGGLE;
|
||||
s.valuePtr = ptr;
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Enum(StrId nameId, uint8_t CrossPointSettings::* ptr, std::vector<StrId> values,
|
||||
const char* key = nullptr, StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ENUM;
|
||||
s.valuePtr = ptr;
|
||||
s.enumValues = std::move(values);
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Action(StrId nameId, SettingAction action) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ACTION;
|
||||
s.action = action;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Value(StrId nameId, uint8_t CrossPointSettings::* ptr, const ValueRange valueRange,
|
||||
const char* key = nullptr, StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::VALUE;
|
||||
s.valuePtr = ptr;
|
||||
s.valueRange = valueRange;
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo String(StrId nameId, char* ptr, size_t maxLen, const char* key = nullptr,
|
||||
StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::STRING;
|
||||
s.stringOffset = (size_t)ptr - (size_t)&SETTINGS;
|
||||
s.stringMaxLen = maxLen;
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo DynamicEnum(StrId nameId, std::vector<StrId> values, std::function<uint8_t()> getter,
|
||||
std::function<void(uint8_t)> setter, const char* key = nullptr,
|
||||
StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ENUM;
|
||||
s.enumValues = std::move(values);
|
||||
s.valueGetter = std::move(getter);
|
||||
s.valueSetter = std::move(setter);
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo DynamicString(StrId nameId, std::function<std::string()> getter,
|
||||
std::function<void(const std::string&)> setter, const char* key = nullptr,
|
||||
StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::STRING;
|
||||
s.stringGetter = std::move(getter);
|
||||
s.stringSetter = std::move(setter);
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Separator(StrId nameId) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ACTION;
|
||||
s.isSeparator = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool isSeparator = false;
|
||||
StrId subcategory = StrId::STR_NONE_OPT; // Triggers a separator row on first use and on change
|
||||
StrId submenu = StrId::STR_NONE_OPT; // Routes item into a submenu; hidden from main list
|
||||
|
||||
// Inserts a separator row in the parent tab when this item's subcategory first appears or changes.
|
||||
SettingInfo& withSubcategory(StrId sub) {
|
||||
subcategory = sub;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Hides this item from the parent tab and places it inside a SettingsSubmenuActivity instead.
|
||||
// All items sharing the same submenu StrId are grouped under one placeholder entry.
|
||||
SettingInfo& withSubmenu(StrId sub) {
|
||||
submenu = sub;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For internal use by SettingsActivity: placeholder entry that launches the submenu.
|
||||
static SettingInfo SubmenuEntry(StrId titleId) {
|
||||
SettingInfo s;
|
||||
s.nameId = titleId;
|
||||
s.type = SettingType::ACTION;
|
||||
s.action = SettingAction::Submenu;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Returns the localised title; separators are decorated automatically.
|
||||
[[nodiscard]] std::string getTitle() const;
|
||||
|
||||
// Returns the current display value string (ON/OFF for toggles, enum label, numeric value, >>).
|
||||
[[nodiscard]] std::string getDisplayValue() const;
|
||||
|
||||
// Toggles/cycles the underlying value for TOGGLE, ENUM, and VALUE types.
|
||||
// Does nothing for ACTION and STRING types (callers handle those separately).
|
||||
// Marked const because it mutates the external SETTINGS global (via valuePtr),
|
||||
// not the SettingInfo itself.
|
||||
void toggleValue() const;
|
||||
};
|
||||
@@ -4,34 +4,17 @@
|
||||
#include <HalClock.h>
|
||||
#include <Logging.h>
|
||||
|
||||
#include "ButtonRemapActivity.h"
|
||||
#include "CalibreSettingsActivity.h"
|
||||
#include "ClearCacheActivity.h"
|
||||
#include "ClockSettingsActivity.h"
|
||||
#include "CrossPointSettings.h"
|
||||
#include "DetectTimezoneActivity.h"
|
||||
#include "KOReaderSettingsActivity.h"
|
||||
#include "LanguageSelectActivity.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "OtaUpdateActivity.h"
|
||||
#include "SettingActionDispatch.h"
|
||||
#include "SettingsList.h"
|
||||
#include "SettingsSubmenuActivity.h"
|
||||
#include "StatusBarSettingsActivity.h"
|
||||
#include "SyncTimeActivity.h"
|
||||
#include "SystemInformationActivity.h"
|
||||
#include "activities/network/WifiSelectionActivity.h"
|
||||
#include "activities/weather/WeatherSettingsActivity.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
const StrId SettingsActivity::categoryNames[categoryCount] = {StrId::STR_CAT_DISPLAY, StrId::STR_CAT_READER,
|
||||
StrId::STR_CAT_CONTROLS, StrId::STR_CAT_SYSTEM};
|
||||
|
||||
std::string SettingInfo::getTitle() const {
|
||||
const auto t = I18N.get(nameId);
|
||||
return isSeparator ? UITheme::makeSeparatorTitle(t) : t;
|
||||
}
|
||||
|
||||
bool SettingsActivity::isListItemSelectable(int settingIdx) const {
|
||||
return settingIdx >= 0 && settingIdx < settingsCount && !(*currentSettings)[settingIdx].isSeparator;
|
||||
}
|
||||
@@ -225,82 +208,24 @@ void SettingsActivity::toggleCurrentSetting() {
|
||||
const auto& setting = (*currentSettings)[selectedSetting];
|
||||
if (setting.isSeparator) return;
|
||||
|
||||
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;
|
||||
} else {
|
||||
SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step;
|
||||
}
|
||||
} else if (setting.type == SettingType::ACTION) {
|
||||
if (setting.type == SettingType::ACTION) {
|
||||
auto resultHandler = [this](const ActivityResult&) { SETTINGS.saveToFile(); };
|
||||
|
||||
switch (setting.action) {
|
||||
case SettingAction::RemapFrontButtons:
|
||||
startActivityForResult(std::make_unique<ButtonRemapActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::CustomiseStatusBar:
|
||||
startActivityForResult(std::make_unique<StatusBarSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::ClockSettings:
|
||||
startActivityForResult(std::make_unique<ClockSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::KOReaderSync:
|
||||
startActivityForResult(std::make_unique<KOReaderSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::OPDSBrowser:
|
||||
startActivityForResult(std::make_unique<CalibreSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::Network:
|
||||
startActivityForResult(std::make_unique<WifiSelectionActivity>(renderer, mappedInput, false), resultHandler);
|
||||
break;
|
||||
case SettingAction::ClearCache:
|
||||
startActivityForResult(std::make_unique<ClearCacheActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::CheckForUpdates:
|
||||
startActivityForResult(std::make_unique<OtaUpdateActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::Language:
|
||||
startActivityForResult(std::make_unique<LanguageSelectActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::Weather:
|
||||
startActivityForResult(std::make_unique<WeatherSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::SystemInfo:
|
||||
startActivityForResult(std::make_unique<SystemInformationActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::SyncTime:
|
||||
startActivityForResult(std::make_unique<SyncTimeActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::DetectTimezone:
|
||||
startActivityForResult(std::make_unique<DetectTimezoneActivity>(renderer, mappedInput), resultHandler);
|
||||
break;
|
||||
case SettingAction::Submenu: {
|
||||
const auto it = std::find_if(submenuData.cbegin(), submenuData.cend(),
|
||||
[&setting](const SubmenuData& d) { return d.id == setting.nameId; });
|
||||
if (it != submenuData.cend()) {
|
||||
startActivityForResult(
|
||||
std::make_unique<SettingsSubmenuActivity>(renderer, mappedInput, setting.nameId, it->items),
|
||||
resultHandler);
|
||||
}
|
||||
break;
|
||||
if (setting.action == SettingAction::Submenu) {
|
||||
const auto it = std::find_if(submenuData.cbegin(), submenuData.cend(),
|
||||
[&setting](const SubmenuData& d) { return d.id == setting.nameId; });
|
||||
if (it != submenuData.cend()) {
|
||||
startActivityForResult(
|
||||
std::make_unique<SettingsSubmenuActivity>(renderer, mappedInput, setting.nameId, it->items), resultHandler);
|
||||
}
|
||||
case SettingAction::None:
|
||||
// Do nothing
|
||||
break;
|
||||
} else {
|
||||
auto activity = createActivityForAction(setting.action, renderer, mappedInput);
|
||||
if (activity) startActivityForResult(std::move(activity), resultHandler);
|
||||
}
|
||||
return; // Results will be handled in the result handler, so we can return early here
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
setting.toggleValue();
|
||||
SETTINGS.saveToFile();
|
||||
}
|
||||
|
||||
@@ -331,24 +256,7 @@ void SettingsActivity::render(RenderLock&&) {
|
||||
(metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing * 2)},
|
||||
settingsCount, selectedSettingIndex - 1, [&settings](int index) { return settings[index].getTitle(); }, nullptr,
|
||||
nullptr,
|
||||
[&settings](int i) {
|
||||
const auto& setting = settings[i];
|
||||
if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) {
|
||||
const bool value = SETTINGS.*(setting.valuePtr);
|
||||
return std::string(value ? tr(STR_STATE_ON) : tr(STR_STATE_OFF));
|
||||
}
|
||||
if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
||||
const uint8_t value = SETTINGS.*(setting.valuePtr);
|
||||
return std::string(I18N.get(setting.enumValues[value]));
|
||||
}
|
||||
if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
||||
return std::to_string(SETTINGS.*(setting.valuePtr));
|
||||
}
|
||||
if (setting.type == SettingType::ACTION && !setting.isSeparator) {
|
||||
return std::string(">>");
|
||||
}
|
||||
return std::string();
|
||||
},
|
||||
[&settings](int i) { return settings[i].getDisplayValue(); },
|
||||
true);
|
||||
|
||||
// Draw help text
|
||||
|
||||
@@ -1,185 +1,12 @@
|
||||
#pragma once
|
||||
#include <I18n.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "SettingInfo.h"
|
||||
#include "activities/Activity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
enum class SettingType { TOGGLE, ENUM, ACTION, VALUE, STRING };
|
||||
|
||||
enum class SettingAction {
|
||||
None,
|
||||
RemapFrontButtons,
|
||||
CustomiseStatusBar,
|
||||
ClockSettings,
|
||||
KOReaderSync,
|
||||
OPDSBrowser,
|
||||
Network,
|
||||
ClearCache,
|
||||
CheckForUpdates,
|
||||
Language,
|
||||
SystemInfo,
|
||||
DetectTimezone,
|
||||
SyncTime,
|
||||
Weather,
|
||||
Submenu,
|
||||
};
|
||||
|
||||
struct SettingInfo {
|
||||
StrId nameId;
|
||||
SettingType type;
|
||||
uint8_t CrossPointSettings::* valuePtr = nullptr;
|
||||
std::vector<StrId> enumValues;
|
||||
SettingAction action = SettingAction::None;
|
||||
|
||||
struct ValueRange {
|
||||
uint8_t min;
|
||||
uint8_t max;
|
||||
uint8_t step;
|
||||
};
|
||||
ValueRange valueRange = {};
|
||||
|
||||
const char* key = nullptr; // JSON API key (nullptr for ACTION types)
|
||||
StrId category = StrId::STR_NONE_OPT; // Category for web UI grouping
|
||||
bool obfuscated = false; // Save/load via base64 obfuscation (passwords)
|
||||
|
||||
// Direct char[] string fields (for settings stored in CrossPointSettings)
|
||||
size_t stringOffset = 0;
|
||||
size_t stringMaxLen = 0;
|
||||
|
||||
// Dynamic accessors (for settings stored outside CrossPointSettings, e.g. KOReaderCredentialStore)
|
||||
std::function<uint8_t()> valueGetter;
|
||||
std::function<void(uint8_t)> valueSetter;
|
||||
std::function<std::string()> stringGetter;
|
||||
std::function<void(const std::string&)> stringSetter;
|
||||
|
||||
SettingInfo& withObfuscated() {
|
||||
obfuscated = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static SettingInfo Toggle(StrId nameId, uint8_t CrossPointSettings::* ptr, const char* key = nullptr,
|
||||
StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::TOGGLE;
|
||||
s.valuePtr = ptr;
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Enum(StrId nameId, uint8_t CrossPointSettings::* ptr, std::vector<StrId> values,
|
||||
const char* key = nullptr, StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ENUM;
|
||||
s.valuePtr = ptr;
|
||||
s.enumValues = std::move(values);
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Action(StrId nameId, SettingAction action) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ACTION;
|
||||
s.action = action;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Value(StrId nameId, uint8_t CrossPointSettings::* ptr, const ValueRange valueRange,
|
||||
const char* key = nullptr, StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::VALUE;
|
||||
s.valuePtr = ptr;
|
||||
s.valueRange = valueRange;
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo String(StrId nameId, char* ptr, size_t maxLen, const char* key = nullptr,
|
||||
StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::STRING;
|
||||
s.stringOffset = (size_t)ptr - (size_t)&SETTINGS;
|
||||
s.stringMaxLen = maxLen;
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo DynamicEnum(StrId nameId, std::vector<StrId> values, std::function<uint8_t()> getter,
|
||||
std::function<void(uint8_t)> setter, const char* key = nullptr,
|
||||
StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ENUM;
|
||||
s.enumValues = std::move(values);
|
||||
s.valueGetter = std::move(getter);
|
||||
s.valueSetter = std::move(setter);
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo DynamicString(StrId nameId, std::function<std::string()> getter,
|
||||
std::function<void(const std::string&)> setter, const char* key = nullptr,
|
||||
StrId category = StrId::STR_NONE_OPT) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::STRING;
|
||||
s.stringGetter = std::move(getter);
|
||||
s.stringSetter = std::move(setter);
|
||||
s.key = key;
|
||||
s.category = category;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SettingInfo Separator(StrId nameId) {
|
||||
SettingInfo s;
|
||||
s.nameId = nameId;
|
||||
s.type = SettingType::ACTION;
|
||||
s.isSeparator = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool isSeparator = false;
|
||||
StrId subcategory = StrId::STR_NONE_OPT; // Triggers a separator row on first use and on change
|
||||
StrId submenu = StrId::STR_NONE_OPT; // Routes item into a submenu; hidden from main list
|
||||
[[nodiscard]] std::string getTitle() const;
|
||||
|
||||
// Inserts a separator row in the parent tab when this item's subcategory first appears or changes.
|
||||
SettingInfo& withSubcategory(StrId sub) {
|
||||
subcategory = sub;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Hides this item from the parent tab and places it inside a SettingsSubmenuActivity instead.
|
||||
// All items sharing the same submenu StrId are grouped under one placeholder entry.
|
||||
SettingInfo& withSubmenu(StrId sub) {
|
||||
submenu = sub;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For internal use by SettingsActivity: placeholder entry that launches the submenu.
|
||||
static SettingInfo SubmenuEntry(StrId titleId) {
|
||||
SettingInfo s;
|
||||
s.nameId = titleId;
|
||||
s.type = SettingType::ACTION;
|
||||
s.action = SettingAction::Submenu;
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
class SettingsActivity final : public Activity {
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
|
||||
@@ -3,123 +3,21 @@
|
||||
#include <GfxRenderer.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include "ButtonRemapActivity.h"
|
||||
#include "CalibreSettingsActivity.h"
|
||||
#include "ClearCacheActivity.h"
|
||||
#include "ClockSettingsActivity.h"
|
||||
#include "CrossPointSettings.h"
|
||||
#include "KOReaderSettingsActivity.h"
|
||||
#include "LanguageSelectActivity.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "OtaUpdateActivity.h"
|
||||
#include "StatusBarSettingsActivity.h"
|
||||
#include "SystemInformationActivity.h"
|
||||
#include "activities/network/WifiSelectionActivity.h"
|
||||
#include "activities/weather/WeatherSettingsActivity.h"
|
||||
#include "SettingActionDispatch.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
void SettingsSubmenuActivity::onEnter() {
|
||||
Activity::onEnter();
|
||||
itemCount = static_cast<int>(items.size());
|
||||
const auto pred = UITheme::makeSelectablePredicate(itemCount, [this](int i) { return items[i].getTitle(); });
|
||||
buttonNavigator.setSelectablePredicate(pred, itemCount);
|
||||
if (!pred(selectedIndex)) {
|
||||
selectedIndex = buttonNavigator.nextIndex(selectedIndex);
|
||||
}
|
||||
requestUpdate();
|
||||
void SettingsSubmenuActivity::onActionSelected(int index) {
|
||||
const auto& setting = menuItems[index];
|
||||
auto resultHandler = [this](const ActivityResult&) { SETTINGS.saveToFile(); };
|
||||
|
||||
auto activity = createActivityForAction(setting.action, renderer, mappedInput);
|
||||
if (activity) startActivityForResult(std::move(activity), resultHandler);
|
||||
}
|
||||
|
||||
void SettingsSubmenuActivity::onExit() { Activity::onExit(); }
|
||||
|
||||
void SettingsSubmenuActivity::loop() {
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
||||
toggleItem();
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
buttonNavigator.onNextRelease([this] {
|
||||
selectedIndex = buttonNavigator.nextIndex(selectedIndex);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onPreviousRelease([this] {
|
||||
selectedIndex = buttonNavigator.previousIndex(selectedIndex);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextContinuous([this] {
|
||||
selectedIndex = buttonNavigator.nextIndex(selectedIndex);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onPreviousContinuous([this] {
|
||||
selectedIndex = buttonNavigator.previousIndex(selectedIndex);
|
||||
requestUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
void SettingsSubmenuActivity::toggleItem() {
|
||||
if (selectedIndex < 0 || selectedIndex >= itemCount) return;
|
||||
const auto& setting = items[selectedIndex];
|
||||
if (setting.isSeparator) return;
|
||||
|
||||
if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) {
|
||||
SETTINGS.*(setting.valuePtr) = !(SETTINGS.*(setting.valuePtr));
|
||||
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
||||
const uint8_t cur = SETTINGS.*(setting.valuePtr);
|
||||
SETTINGS.*(setting.valuePtr) = (cur + 1) % static_cast<uint8_t>(setting.enumValues.size());
|
||||
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
||||
const int8_t cur = SETTINGS.*(setting.valuePtr);
|
||||
SETTINGS.*(setting.valuePtr) = (cur + setting.valueRange.step > setting.valueRange.max)
|
||||
? setting.valueRange.min
|
||||
: cur + setting.valueRange.step;
|
||||
} else if (setting.type == SettingType::ACTION) {
|
||||
auto resultHandler = [this](const ActivityResult&) { SETTINGS.saveToFile(); };
|
||||
switch (setting.action) {
|
||||
case SettingAction::RemapFrontButtons:
|
||||
startActivityForResult(std::make_unique<ButtonRemapActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::CustomiseStatusBar:
|
||||
startActivityForResult(std::make_unique<StatusBarSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::ClockSettings:
|
||||
startActivityForResult(std::make_unique<ClockSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::KOReaderSync:
|
||||
startActivityForResult(std::make_unique<KOReaderSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::OPDSBrowser:
|
||||
startActivityForResult(std::make_unique<CalibreSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::Network:
|
||||
startActivityForResult(std::make_unique<WifiSelectionActivity>(renderer, mappedInput, false), resultHandler);
|
||||
return;
|
||||
case SettingAction::ClearCache:
|
||||
startActivityForResult(std::make_unique<ClearCacheActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::CheckForUpdates:
|
||||
startActivityForResult(std::make_unique<OtaUpdateActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::Language:
|
||||
startActivityForResult(std::make_unique<LanguageSelectActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::Weather:
|
||||
startActivityForResult(std::make_unique<WeatherSettingsActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
case SettingAction::SystemInfo:
|
||||
startActivityForResult(std::make_unique<SystemInformationActivity>(renderer, mappedInput), resultHandler);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SETTINGS.saveToFile();
|
||||
}
|
||||
void SettingsSubmenuActivity::onSettingToggled(int /*index*/) { SETTINGS.saveToFile(); }
|
||||
|
||||
void SettingsSubmenuActivity::render(RenderLock&&) {
|
||||
renderer.clearScreen();
|
||||
@@ -132,30 +30,7 @@ void SettingsSubmenuActivity::render(RenderLock&&) {
|
||||
|
||||
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
|
||||
const int contentHeight = contentRect.height - contentTop - metrics.verticalSpacing;
|
||||
|
||||
GUI.drawList(
|
||||
renderer, Rect{contentRect.x, contentTop, contentRect.width, contentHeight}, itemCount, selectedIndex,
|
||||
[this](int index) { return items[index].getTitle(); }, nullptr, nullptr,
|
||||
[this](int i) {
|
||||
const auto& setting = items[i];
|
||||
if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) {
|
||||
return std::string(SETTINGS.*(setting.valuePtr) ? tr(STR_STATE_ON) : tr(STR_STATE_OFF));
|
||||
}
|
||||
if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
||||
const uint8_t value = SETTINGS.*(setting.valuePtr);
|
||||
if (value < setting.enumValues.size()) {
|
||||
return std::string(I18N.get(setting.enumValues[value]));
|
||||
}
|
||||
}
|
||||
if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
||||
return std::to_string(SETTINGS.*(setting.valuePtr));
|
||||
}
|
||||
if (setting.type == SettingType::ACTION && !setting.isSeparator) {
|
||||
return std::string(">>");
|
||||
}
|
||||
return std::string();
|
||||
},
|
||||
true);
|
||||
drawMenuList(Rect{contentRect.x, contentTop, contentRect.width, contentHeight});
|
||||
|
||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
|
||||
@@ -3,28 +3,24 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "activities/Activity.h"
|
||||
#include "activities/settings/SettingsActivity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
#include "SettingInfo.h"
|
||||
#include "activities/MenuListActivity.h"
|
||||
|
||||
// Displays a flat list of SettingInfo items launched from a SettingsActivity submenu entry.
|
||||
// Supports subcategory separators (withSubcategory) exactly as the parent settings tabs do.
|
||||
class SettingsSubmenuActivity final : public Activity {
|
||||
class SettingsSubmenuActivity final : public MenuListActivity {
|
||||
StrId titleId;
|
||||
std::vector<SettingInfo> items;
|
||||
int selectedIndex = 0;
|
||||
int itemCount = 0;
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
void toggleItem();
|
||||
// MenuListActivity overrides
|
||||
void onActionSelected(int index) override;
|
||||
void onSettingToggled(int index) override;
|
||||
|
||||
public:
|
||||
explicit SettingsSubmenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, StrId titleId,
|
||||
std::vector<SettingInfo> items)
|
||||
: Activity("SettingsSubmenu", renderer, mappedInput), titleId(titleId), items(std::move(items)) {}
|
||||
: MenuListActivity("SettingsSubmenu", renderer, mappedInput), titleId(titleId) {
|
||||
menuItems = std::move(items);
|
||||
}
|
||||
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
void loop() override;
|
||||
void render(RenderLock&&) override;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user