## Summary **What is the goal of this PR?** Small cleanup to make getTheme and getMetrics methods on UITheme const. They return const refs, so updated call sites to use `const auto&`. Realistically this won't make much performance difference, but it better conveys the nature of theme metrics being shared const state. --- ### 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? _**NO**_
262 lines
9.5 KiB
C++
262 lines
9.5 KiB
C++
#include "SettingsActivity.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <Logging.h>
|
|
|
|
#include "ButtonRemapActivity.h"
|
|
#include "CalibreSettingsActivity.h"
|
|
#include "ClearCacheActivity.h"
|
|
#include "CrossPointSettings.h"
|
|
#include "KOReaderSettingsActivity.h"
|
|
#include "LanguageSelectActivity.h"
|
|
#include "MappedInputManager.h"
|
|
#include "OtaUpdateActivity.h"
|
|
#include "SettingsList.h"
|
|
#include "activities/network/WifiSelectionActivity.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};
|
|
|
|
void SettingsActivity::onEnter() {
|
|
Activity::onEnter();
|
|
|
|
// Build per-category vectors from the shared settings list
|
|
displaySettings.clear();
|
|
readerSettings.clear();
|
|
controlsSettings.clear();
|
|
systemSettings.clear();
|
|
|
|
for (auto& setting : getSettingsList()) {
|
|
if (setting.category == StrId::STR_NONE_OPT) continue;
|
|
if (setting.category == StrId::STR_CAT_DISPLAY) {
|
|
displaySettings.push_back(std::move(setting));
|
|
} else if (setting.category == StrId::STR_CAT_READER) {
|
|
readerSettings.push_back(std::move(setting));
|
|
} else if (setting.category == StrId::STR_CAT_CONTROLS) {
|
|
controlsSettings.push_back(std::move(setting));
|
|
} else if (setting.category == StrId::STR_CAT_SYSTEM) {
|
|
systemSettings.push_back(std::move(setting));
|
|
}
|
|
// Web-only categories (KOReader Sync, OPDS Browser) are skipped for device UI
|
|
}
|
|
|
|
// Append device-only ACTION items
|
|
controlsSettings.insert(controlsSettings.begin(),
|
|
SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons));
|
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network));
|
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_KOREADER_SYNC, SettingAction::KOReaderSync));
|
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_OPDS_BROWSER, SettingAction::OPDSBrowser));
|
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_CLEAR_READING_CACHE, SettingAction::ClearCache));
|
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_CHECK_UPDATES, SettingAction::CheckForUpdates));
|
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_LANGUAGE, SettingAction::Language));
|
|
|
|
// Reset selection to first category
|
|
selectedCategoryIndex = 0;
|
|
selectedSettingIndex = 0;
|
|
|
|
// Initialize with first category (Display)
|
|
currentSettings = &displaySettings;
|
|
settingsCount = static_cast<int>(displaySettings.size());
|
|
|
|
// Trigger first update
|
|
requestUpdate();
|
|
}
|
|
|
|
void SettingsActivity::onExit() {
|
|
ActivityWithSubactivity::onExit();
|
|
|
|
UITheme::getInstance().reload(); // Re-apply theme in case it was changed
|
|
}
|
|
|
|
void SettingsActivity::loop() {
|
|
if (subActivity) {
|
|
subActivity->loop();
|
|
return;
|
|
}
|
|
bool hasChangedCategory = false;
|
|
|
|
// Handle actions with early return
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
|
if (selectedSettingIndex == 0) {
|
|
selectedCategoryIndex = (selectedCategoryIndex < categoryCount - 1) ? (selectedCategoryIndex + 1) : 0;
|
|
hasChangedCategory = true;
|
|
requestUpdate();
|
|
} else {
|
|
toggleCurrentSetting();
|
|
requestUpdate();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
|
SETTINGS.saveToFile();
|
|
onGoHome();
|
|
return;
|
|
}
|
|
|
|
// Handle navigation
|
|
buttonNavigator.onNextRelease([this] {
|
|
selectedSettingIndex = ButtonNavigator::nextIndex(selectedSettingIndex, settingsCount + 1);
|
|
requestUpdate();
|
|
});
|
|
|
|
buttonNavigator.onPreviousRelease([this] {
|
|
selectedSettingIndex = ButtonNavigator::previousIndex(selectedSettingIndex, settingsCount + 1);
|
|
requestUpdate();
|
|
});
|
|
|
|
buttonNavigator.onNextContinuous([this, &hasChangedCategory] {
|
|
hasChangedCategory = true;
|
|
selectedCategoryIndex = ButtonNavigator::nextIndex(selectedCategoryIndex, categoryCount);
|
|
requestUpdate();
|
|
});
|
|
|
|
buttonNavigator.onPreviousContinuous([this, &hasChangedCategory] {
|
|
hasChangedCategory = true;
|
|
selectedCategoryIndex = ButtonNavigator::previousIndex(selectedCategoryIndex, categoryCount);
|
|
requestUpdate();
|
|
});
|
|
|
|
if (hasChangedCategory) {
|
|
selectedSettingIndex = (selectedSettingIndex == 0) ? 0 : 1;
|
|
switch (selectedCategoryIndex) {
|
|
case 0:
|
|
currentSettings = &displaySettings;
|
|
break;
|
|
case 1:
|
|
currentSettings = &readerSettings;
|
|
break;
|
|
case 2:
|
|
currentSettings = &controlsSettings;
|
|
break;
|
|
case 3:
|
|
currentSettings = &systemSettings;
|
|
break;
|
|
}
|
|
settingsCount = static_cast<int>(currentSettings->size());
|
|
}
|
|
}
|
|
|
|
void SettingsActivity::toggleCurrentSetting() {
|
|
int selectedSetting = selectedSettingIndex - 1;
|
|
if (selectedSetting < 0 || selectedSetting >= settingsCount) {
|
|
return;
|
|
}
|
|
|
|
const auto& setting = (*currentSettings)[selectedSetting];
|
|
|
|
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) {
|
|
auto enterSubActivity = [this](Activity* activity) {
|
|
exitActivity();
|
|
enterNewActivity(activity);
|
|
};
|
|
|
|
auto onComplete = [this] {
|
|
exitActivity();
|
|
requestUpdate();
|
|
};
|
|
|
|
auto onCompleteBool = [this](bool) {
|
|
exitActivity();
|
|
requestUpdate();
|
|
};
|
|
|
|
switch (setting.action) {
|
|
case SettingAction::RemapFrontButtons:
|
|
enterSubActivity(new ButtonRemapActivity(renderer, mappedInput, onComplete));
|
|
break;
|
|
case SettingAction::KOReaderSync:
|
|
enterSubActivity(new KOReaderSettingsActivity(renderer, mappedInput, onComplete));
|
|
break;
|
|
case SettingAction::OPDSBrowser:
|
|
enterSubActivity(new CalibreSettingsActivity(renderer, mappedInput, onComplete));
|
|
break;
|
|
case SettingAction::Network:
|
|
enterSubActivity(new WifiSelectionActivity(renderer, mappedInput, onCompleteBool, false));
|
|
break;
|
|
case SettingAction::ClearCache:
|
|
enterSubActivity(new ClearCacheActivity(renderer, mappedInput, onComplete));
|
|
break;
|
|
case SettingAction::CheckForUpdates:
|
|
enterSubActivity(new OtaUpdateActivity(renderer, mappedInput, onComplete));
|
|
break;
|
|
case SettingAction::Language:
|
|
enterSubActivity(new LanguageSelectActivity(renderer, mappedInput, onComplete));
|
|
break;
|
|
case SettingAction::None:
|
|
// Do nothing
|
|
break;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
SETTINGS.saveToFile();
|
|
}
|
|
|
|
void SettingsActivity::render(Activity::RenderLock&&) {
|
|
renderer.clearScreen();
|
|
|
|
const auto pageWidth = renderer.getScreenWidth();
|
|
const auto pageHeight = renderer.getScreenHeight();
|
|
|
|
const auto& metrics = UITheme::getInstance().getMetrics();
|
|
|
|
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_SETTINGS_TITLE),
|
|
CROSSPOINT_VERSION);
|
|
|
|
std::vector<TabInfo> tabs;
|
|
tabs.reserve(categoryCount);
|
|
for (int i = 0; i < categoryCount; i++) {
|
|
tabs.push_back({I18N.get(categoryNames[i]), selectedCategoryIndex == i});
|
|
}
|
|
GUI.drawTabBar(renderer, Rect{0, metrics.topPadding + metrics.headerHeight, pageWidth, metrics.tabBarHeight}, tabs,
|
|
selectedSettingIndex == 0);
|
|
|
|
const auto& settings = *currentSettings;
|
|
GUI.drawList(
|
|
renderer,
|
|
Rect{0, metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing, pageWidth,
|
|
pageHeight - (metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.buttonHintsHeight +
|
|
metrics.verticalSpacing * 2)},
|
|
settingsCount, selectedSettingIndex - 1,
|
|
[&settings](int index) { return std::string(I18N.get(settings[index].nameId)); }, nullptr, nullptr,
|
|
[&settings](int i) {
|
|
const auto& setting = settings[i];
|
|
std::string valueText = "";
|
|
if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) {
|
|
const bool value = SETTINGS.*(setting.valuePtr);
|
|
valueText = value ? tr(STR_STATE_ON) : tr(STR_STATE_OFF);
|
|
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
|
const uint8_t value = SETTINGS.*(setting.valuePtr);
|
|
valueText = I18N.get(setting.enumValues[value]);
|
|
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
|
valueText = std::to_string(SETTINGS.*(setting.valuePtr));
|
|
}
|
|
return valueText;
|
|
},
|
|
true);
|
|
|
|
// Draw help text
|
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_TOGGLE), tr(STR_DIR_UP), tr(STR_DIR_DOWN));
|
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
|
|
// Always use standard refresh for settings screen
|
|
renderer.displayBuffer();
|
|
} |