Add submenus

This commit is contained in:
jpirnay
2026-04-12 14:21:48 +02:00
parent 1f1777385c
commit df3fec574d
5 changed files with 192 additions and 157 deletions
+5
View File
@@ -481,3 +481,8 @@ STR_MENU_SYS_TOOLS: "Tools"
STR_MENU_DISP_SLEEP: "Sleepscreen" STR_MENU_DISP_SLEEP: "Sleepscreen"
STR_MENU_DISP_BATTERY: "Battery symbol" STR_MENU_DISP_BATTERY: "Battery symbol"
STR_MENU_DISP_REFRESH: "Screen refresh" STR_MENU_DISP_REFRESH: "Screen refresh"
STR_MENU_READER_FONT: "Reader Font"
STR_MENU_READER_FONT_SETTINGS: "Font Settings"
STR_MENU_READER_LAYOUT: "Layout Settings"
STR_MENU_READER_TWEAKS: "Reader Tweaks"
STR_MENU_READER_SPACING: "Spacing"
+51 -29
View File
@@ -27,8 +27,14 @@
// //
// ACTION-type entries and entries without a key are device-only and are added directly // ACTION-type entries and entries without a key are device-only and are added directly
// in SettingsActivity::onEnter(), not here. // in SettingsActivity::onEnter(), not here.
inline const std::vector<SettingInfo>& getSettingsList() { //
static const std::vector<SettingInfo> list = { // Implementation note: the list is a namespace-level static (not a function-local static) so
// it is initialised during the global-static phase before setup() runs. A function-local
// static would trigger __cxa_guard_acquire on the first call, which creates a FreeRTOS mutex
// deep inside the heap allocator chain — enough stack to overflow the 8 KB loop task stack
// when called from inside SETTINGS.loadFromFile() at boot time.
namespace SettingsListDetail {
inline const std::vector<SettingInfo> list = {
// --- Display --- // --- Display ---
SettingInfo::Enum(StrId::STR_TIME_TO_SLEEP, &CrossPointSettings::sleepTimeout, SettingInfo::Enum(StrId::STR_TIME_TO_SLEEP, &CrossPointSettings::sleepTimeout,
{StrId::STR_MIN_1, StrId::STR_MIN_5, StrId::STR_MIN_10, StrId::STR_MIN_15, StrId::STR_MIN_30}, {StrId::STR_MIN_1, StrId::STR_MIN_5, StrId::STR_MIN_10, StrId::STR_MIN_15, StrId::STR_MIN_30},
@@ -41,8 +47,8 @@ inline const std::vector<SettingInfo>& getSettingsList() {
SettingInfo::Enum(StrId::STR_SLEEP_COVER_MODE, &CrossPointSettings::sleepScreenCoverMode, SettingInfo::Enum(StrId::STR_SLEEP_COVER_MODE, &CrossPointSettings::sleepScreenCoverMode,
{StrId::STR_FIT, StrId::STR_CROP}, "sleepScreenCoverMode", StrId::STR_CAT_DISPLAY), {StrId::STR_FIT, StrId::STR_CROP}, "sleepScreenCoverMode", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_SLEEP_COVER_FILTER, &CrossPointSettings::sleepScreenCoverFilter, SettingInfo::Enum(StrId::STR_SLEEP_COVER_FILTER, &CrossPointSettings::sleepScreenCoverFilter,
{StrId::STR_NONE_OPT, StrId::STR_FILTER_CONTRAST, StrId::STR_INVERTED}, {StrId::STR_NONE_OPT, StrId::STR_FILTER_CONTRAST, StrId::STR_INVERTED}, "sleepScreenCoverFilter",
"sleepScreenCoverFilter", StrId::STR_CAT_DISPLAY), StrId::STR_CAT_DISPLAY),
SettingInfo::Enum( SettingInfo::Enum(
StrId::STR_SLEEP_COVER_OVERLAY, &CrossPointSettings::sleepCoverOverlay, StrId::STR_SLEEP_COVER_OVERLAY, &CrossPointSettings::sleepCoverOverlay,
{StrId::STR_OVERLAY_OFF, StrId::STR_OVERLAY_WHITE, StrId::STR_OVERLAY_GRAY, StrId::STR_OVERLAY_BLACK}, {StrId::STR_OVERLAY_OFF, StrId::STR_OVERLAY_WHITE, StrId::STR_OVERLAY_GRAY, StrId::STR_OVERLAY_BLACK},
@@ -65,39 +71,54 @@ inline const std::vector<SettingInfo>& getSettingsList() {
StrId::STR_CAT_DISPLAY), StrId::STR_CAT_DISPLAY),
// --- Reader --- // --- Reader ---
// General reader settings
SettingInfo::Enum(StrId::STR_ORIENTATION, &CrossPointSettings::orientation,
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, StrId::STR_LANDSCAPE_CCW},
"orientation", StrId::STR_CAT_READER),
// Font
SettingInfo::Enum(StrId::STR_FONT_FAMILY, &CrossPointSettings::fontFamily, SettingInfo::Enum(StrId::STR_FONT_FAMILY, &CrossPointSettings::fontFamily,
{StrId::STR_BOOKERLY, StrId::STR_NOTO_SANS, StrId::STR_OPEN_DYSLEXIC}, "fontFamily", {StrId::STR_BOOKERLY, StrId::STR_NOTO_SANS, StrId::STR_OPEN_DYSLEXIC}, "fontFamily",
StrId::STR_CAT_READER), StrId::STR_CAT_READER)
.withSubcategory(StrId::STR_FONT_FAMILY),
SettingInfo::Enum(StrId::STR_FONT_SIZE, &CrossPointSettings::fontSize, SettingInfo::Enum(StrId::STR_FONT_SIZE, &CrossPointSettings::fontSize,
{StrId::STR_SMALL, StrId::STR_MEDIUM, StrId::STR_LARGE, StrId::STR_X_LARGE}, "fontSize", {StrId::STR_SMALL, StrId::STR_MEDIUM, StrId::STR_LARGE, StrId::STR_X_LARGE}, "fontSize",
StrId::STR_CAT_READER), StrId::STR_CAT_READER)
SettingInfo::Enum(StrId::STR_LINE_SPACING, &CrossPointSettings::lineSpacing, .withSubmenu(StrId::STR_MENU_READER_FONT),
{StrId::STR_TIGHT, StrId::STR_NORMAL, StrId::STR_WIDE}, "lineSpacing", StrId::STR_CAT_READER), SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing",
SettingInfo::Value(StrId::STR_SCREEN_MARGIN, &CrossPointSettings::screenMargin, {5, 40, 5}, "screenMargin", StrId::STR_CAT_READER)
StrId::STR_CAT_READER), .withSubmenu(StrId::STR_MENU_READER_FONT),
SettingInfo::Enum(StrId::STR_PARA_ALIGNMENT, &CrossPointSettings::paragraphAlignment, SettingInfo::Enum(StrId::STR_TEXT_DARKNESS, &CrossPointSettings::textDarkness,
{StrId::STR_JUSTIFY, StrId::STR_ALIGN_LEFT, StrId::STR_CENTER, StrId::STR_ALIGN_RIGHT, {StrId::STR_NORMAL, StrId::STR_DARK, StrId::STR_EXTRA_DARK, StrId::STR_MAX_DARK}, "textDarkness",
StrId::STR_BOOK_S_STYLE}, StrId::STR_CAT_READER)
"paragraphAlignment", StrId::STR_CAT_READER), .withSubmenu(StrId::STR_MENU_READER_FONT),
// Formatting settings
SettingInfo::Enum(
StrId::STR_PARA_ALIGNMENT, &CrossPointSettings::paragraphAlignment,
{StrId::STR_JUSTIFY, StrId::STR_ALIGN_LEFT, StrId::STR_CENTER, StrId::STR_ALIGN_RIGHT, StrId::STR_BOOK_S_STYLE},
"paragraphAlignment", StrId::STR_CAT_READER)
.withSubcategory(StrId::STR_MENU_READER_LAYOUT),
SettingInfo::Toggle(StrId::STR_EMBEDDED_STYLE, &CrossPointSettings::embeddedStyle, "embeddedStyle", SettingInfo::Toggle(StrId::STR_EMBEDDED_STYLE, &CrossPointSettings::embeddedStyle, "embeddedStyle",
StrId::STR_CAT_READER), StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_HYPHENATION, &CrossPointSettings::hyphenationEnabled, "hyphenationEnabled", SettingInfo::Toggle(StrId::STR_HYPHENATION, &CrossPointSettings::hyphenationEnabled, "hyphenationEnabled",
StrId::STR_CAT_READER), StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_ORIENTATION, &CrossPointSettings::orientation,
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, StrId::STR_LANDSCAPE_CCW},
"orientation", StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_EXTRA_SPACING, &CrossPointSettings::extraParagraphSpacing, "extraParagraphSpacing",
StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_TEXT_DARKNESS, &CrossPointSettings::textDarkness,
{StrId::STR_NORMAL, StrId::STR_DARK, StrId::STR_EXTRA_DARK, StrId::STR_MAX_DARK},
"textDarkness", StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering, SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering,
{StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS}, {StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
"imageRendering", StrId::STR_CAT_READER), "imageRendering", StrId::STR_CAT_READER),
SettingInfo::Value(StrId::STR_SCREEN_MARGIN, &CrossPointSettings::screenMargin, {5, 40, 5}, "screenMargin",
StrId::STR_CAT_READER)
.withSubmenu(StrId::STR_MENU_READER_SPACING),
SettingInfo::Enum(StrId::STR_LINE_SPACING, &CrossPointSettings::lineSpacing,
{StrId::STR_TIGHT, StrId::STR_NORMAL, StrId::STR_WIDE}, "lineSpacing", StrId::STR_CAT_READER)
.withSubmenu(StrId::STR_MENU_READER_SPACING),
SettingInfo::Toggle(StrId::STR_EXTRA_SPACING, &CrossPointSettings::extraParagraphSpacing, "extraParagraphSpacing",
StrId::STR_CAT_READER)
.withSubmenu(StrId::STR_MENU_READER_SPACING),
// Generic reader settings
SettingInfo::Toggle(StrId::STR_CREATE_FALLBACK_FOR_INVALID_TOC, &CrossPointSettings::syntheticTocFallback, SettingInfo::Toggle(StrId::STR_CREATE_FALLBACK_FOR_INVALID_TOC, &CrossPointSettings::syntheticTocFallback,
"syntheticTocFallback", StrId::STR_CAT_READER), "syntheticTocFallback", StrId::STR_CAT_READER)
.withSubcategory(StrId::STR_MENU_READER_TWEAKS),
// --- Controls --- // --- Controls ---
SettingInfo::Enum(StrId::STR_SIDE_BTN_LAYOUT, &CrossPointSettings::sideButtonLayout, SettingInfo::Enum(StrId::STR_SIDE_BTN_LAYOUT, &CrossPointSettings::sideButtonLayout,
{StrId::STR_PREV_NEXT, StrId::STR_NEXT_PREV}, "sideButtonLayout", StrId::STR_CAT_CONTROLS), {StrId::STR_PREV_NEXT, StrId::STR_NEXT_PREV}, "sideButtonLayout", StrId::STR_CAT_CONTROLS),
@@ -110,8 +131,8 @@ inline const std::vector<SettingInfo>& getSettingsList() {
// --- System --- // --- System ---
SettingInfo::Toggle(StrId::STR_SHOW_HIDDEN_FILES, &CrossPointSettings::showHiddenFiles, "showHiddenFiles", SettingInfo::Toggle(StrId::STR_SHOW_HIDDEN_FILES, &CrossPointSettings::showHiddenFiles, "showHiddenFiles",
StrId::STR_CAT_SYSTEM), StrId::STR_CAT_SYSTEM),
SettingInfo::Toggle(StrId::STR_SHOW_FILE_EXTENSIONS, &CrossPointSettings::showFileExtensions, SettingInfo::Toggle(StrId::STR_SHOW_FILE_EXTENSIONS, &CrossPointSettings::showFileExtensions, "showFileExtensions",
"showFileExtensions", StrId::STR_CAT_SYSTEM), StrId::STR_CAT_SYSTEM),
SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat12h, {StrId::STR_24H, StrId::STR_12H}, SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat12h, {StrId::STR_24H, StrId::STR_12H},
"clockFormat12h", StrId::STR_CAT_SYSTEM), "clockFormat12h", StrId::STR_CAT_SYSTEM),
SettingInfo::Enum(StrId::STR_TIMEZONE, &CrossPointSettings::timeZone, SettingInfo::Enum(StrId::STR_TIMEZONE, &CrossPointSettings::timeZone,
@@ -180,5 +201,6 @@ inline const std::vector<SettingInfo>& getSettingsList() {
SettingInfo::Toggle(StrId::STR_CLOCK, &CrossPointSettings::statusBarClock, "statusBarClock", SettingInfo::Toggle(StrId::STR_CLOCK, &CrossPointSettings::statusBarClock, "statusBarClock",
StrId::STR_CUSTOMISE_STATUS_BAR), StrId::STR_CUSTOMISE_STATUS_BAR),
}; };
return list; } // namespace SettingsListDetail
}
inline const std::vector<SettingInfo>& getSettingsList() { return SettingsListDetail::list; }
+12 -8
View File
@@ -333,17 +333,21 @@ void SettingsActivity::render(RenderLock&&) {
nullptr, nullptr,
[&settings](int i) { [&settings](int i) {
const auto& setting = settings[i]; const auto& setting = settings[i];
std::string valueText = "";
if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) { if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) {
const bool value = SETTINGS.*(setting.valuePtr); const bool value = SETTINGS.*(setting.valuePtr);
valueText = value ? tr(STR_STATE_ON) : tr(STR_STATE_OFF); return std::string(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; 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();
}, },
true); true);
@@ -22,8 +22,7 @@
void SettingsSubmenuActivity::onEnter() { void SettingsSubmenuActivity::onEnter() {
Activity::onEnter(); Activity::onEnter();
itemCount = static_cast<int>(items.size()); itemCount = static_cast<int>(items.size());
const auto pred = UITheme::makeSelectablePredicate(itemCount, const auto pred = UITheme::makeSelectablePredicate(itemCount, [this](int i) { return items[i].getTitle(); });
[this](int i) { return items[i].getTitle(); });
buttonNavigator.setSelectablePredicate(pred, itemCount); buttonNavigator.setSelectablePredicate(pred, itemCount);
if (!pred(selectedIndex)) { if (!pred(selectedIndex)) {
selectedIndex = buttonNavigator.nextIndex(selectedIndex); selectedIndex = buttonNavigator.nextIndex(selectedIndex);
@@ -151,6 +150,9 @@ void SettingsSubmenuActivity::render(RenderLock&&) {
if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) { if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
return std::to_string(SETTINGS.*(setting.valuePtr)); return std::to_string(SETTINGS.*(setting.valuePtr));
} }
if (setting.type == SettingType::ACTION && !setting.isSeparator) {
return std::string(">>");
}
return std::string(); return std::string();
}, },
true); true);
+3 -1
View File
@@ -204,10 +204,12 @@ void setup() {
HalSystem::checkPanic(); HalSystem::checkPanic();
HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info
LOG_DBG("MAIN", "System initialized, now setting up environment, millis=%lu", millis());
SETTINGS.loadFromFile(); SETTINGS.loadFromFile();
LOG_DBG("MAIN", "Settings loaded, now setting up clock and localization, millis=%lu", millis());
HalClock::applyTimezone(SETTINGS.timeZone); HalClock::applyTimezone(SETTINGS.timeZone);
I18N.loadSettings(); I18N.loadSettings();
LOG_DBG("MAIN", "Localization loaded, now setting up theme and button navigation, millis=%lu", millis());
KOREADER_STORE.loadFromFile(); KOREADER_STORE.loadFromFile();
WEATHER_SETTINGS.loadFromFile(); WEATHER_SETTINGS.loadFromFile();
UITheme::getInstance().reload(); UITheme::getInstance().reload();