Files
crosspoint-reader-mod/src/activities/settings/SettingsActivity.cpp
Zach Nelson 410c70ab89 perf: UITheme::getMetrics const and const-ref usage (#1094)
## 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**_
2026-02-23 13:29:18 +01:00

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();
}