Files
Crosspoint/src/activities/settings/SettingsActivity.h
T
Istiak TridipandDave Allie 64d161e88b feat: unify navigation handling with system-wide continuous navigation (#600)
This PR unifies navigation handling & adds system-wide support for
continuous navigation.

## Summary
Holding down a navigation button now continuously advances through items
until the button is released. This removes the need for repeated
press-and-release actions and makes navigation faster and smoother,
especially in long menus or documents.

When page-based navigation is available, it will navigate through pages.
If not, it will progress through menu items or similar list-based UI
elements.

Additionally, this PR fixes inconsistencies in wrap-around behavior and
navigation index calculations.

Places where the navigation system was updated:
- Home Page
- Settings Pages
- My Library Page
- WiFi Selection Page
- OPDS Browser Page
- Keyboard
- File Transfer Page
- XTC Chapter Selector Page
- EPUB Chapter Selector Page

I’ve tested this on the device as much as possible and tried to match
the existing behavior. Please let me know if I missed anything. Thanks 🙏


![crosspoint](https://github.com/user-attachments/assets/6a3c7482-f45e-4a77-b156-721bb3b679e6)

---

Following the request from @osteotek and @daveallie for system-wide
support, the old PR (#379) has been closed in favor of this
consolidated, system-wide implementation.

---

### AI Usage

Did you use AI tools to help write this code? _**PARTIALLY**_

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2026-02-09 20:19:34 +11:00

160 lines
5.0 KiB
C++

#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include <string>
#include <vector>
#include "activities/ActivityWithSubactivity.h"
#include "util/ButtonNavigator.h"
class CrossPointSettings;
enum class SettingType { TOGGLE, ENUM, ACTION, VALUE, STRING };
struct SettingInfo {
const char* name;
SettingType type;
uint8_t CrossPointSettings::* valuePtr = nullptr;
std::vector<std::string> enumValues;
struct ValueRange {
uint8_t min;
uint8_t max;
uint8_t step;
};
ValueRange valueRange = {};
const char* key = nullptr; // JSON API key (nullptr for ACTION types)
const char* category = nullptr; // Category for web UI grouping
// Direct char[] string fields (for settings stored in CrossPointSettings)
char* stringPtr = nullptr;
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;
static SettingInfo Toggle(const char* name, uint8_t CrossPointSettings::* ptr, const char* key = nullptr,
const char* category = nullptr) {
SettingInfo s;
s.name = name;
s.type = SettingType::TOGGLE;
s.valuePtr = ptr;
s.key = key;
s.category = category;
return s;
}
static SettingInfo Enum(const char* name, uint8_t CrossPointSettings::* ptr, std::vector<std::string> values,
const char* key = nullptr, const char* category = nullptr) {
SettingInfo s;
s.name = name;
s.type = SettingType::ENUM;
s.valuePtr = ptr;
s.enumValues = std::move(values);
s.key = key;
s.category = category;
return s;
}
static SettingInfo Action(const char* name) {
SettingInfo s;
s.name = name;
s.type = SettingType::ACTION;
return s;
}
static SettingInfo Value(const char* name, uint8_t CrossPointSettings::* ptr, const ValueRange valueRange,
const char* key = nullptr, const char* category = nullptr) {
SettingInfo s;
s.name = name;
s.type = SettingType::VALUE;
s.valuePtr = ptr;
s.valueRange = valueRange;
s.key = key;
s.category = category;
return s;
}
static SettingInfo String(const char* name, char* ptr, size_t maxLen, const char* key = nullptr,
const char* category = nullptr) {
SettingInfo s;
s.name = name;
s.type = SettingType::STRING;
s.stringPtr = ptr;
s.stringMaxLen = maxLen;
s.key = key;
s.category = category;
return s;
}
static SettingInfo DynamicEnum(const char* name, std::vector<std::string> values, std::function<uint8_t()> getter,
std::function<void(uint8_t)> setter, const char* key = nullptr,
const char* category = nullptr) {
SettingInfo s;
s.name = name;
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(const char* name, std::function<std::string()> getter,
std::function<void(const std::string&)> setter, const char* key = nullptr,
const char* category = nullptr) {
SettingInfo s;
s.name = name;
s.type = SettingType::STRING;
s.stringGetter = std::move(getter);
s.stringSetter = std::move(setter);
s.key = key;
s.category = category;
return s;
}
};
class SettingsActivity final : public ActivityWithSubactivity {
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
ButtonNavigator buttonNavigator;
bool updateRequired = false;
int selectedCategoryIndex = 0; // Currently selected category
int selectedSettingIndex = 0;
int settingsCount = 0;
// Per-category settings derived from shared list + device-only actions
std::vector<SettingInfo> displaySettings;
std::vector<SettingInfo> readerSettings;
std::vector<SettingInfo> controlsSettings;
std::vector<SettingInfo> systemSettings;
const std::vector<SettingInfo>* currentSettings = nullptr;
const std::function<void()> onGoHome;
static constexpr int categoryCount = 4;
static const char* categoryNames[categoryCount];
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void render() const;
void enterCategory(int categoryIndex);
void toggleCurrentSetting();
public:
explicit SettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onGoHome)
: ActivityWithSubactivity("Settings", renderer, mappedInput), onGoHome(onGoHome) {}
void onEnter() override;
void onExit() override;
void loop() override;
};