From 7feff4e241a7b9a646006d5b9fced84e7e19efdc Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sun, 12 Apr 2026 06:49:28 +0200 Subject: [PATCH] Step 3 --- lib/I18n/translations/english.yaml | 5 +- src/SettingsList.h | 40 ++++++----- src/activities/settings/SettingsActivity.cpp | 70 ++++++++++++++------ src/activities/settings/SettingsActivity.h | 6 ++ 4 files changed, 82 insertions(+), 39 deletions(-) diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index 53514c4f..a9ce9a21 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -477,4 +477,7 @@ STR_READER_NAVIGATION: "Navigation" STR_READER_APPEARANCE: "Appearance" STR_MENU_SYS_SYSTEM: "System" STR_MENU_SYS_NETWORK: "Network" -STR_MENU_SYS_TOOLS: "Tools" \ No newline at end of file +STR_MENU_SYS_TOOLS: "Tools" +STR_MENU_DISP_SLEEP: "Sleepscreen" +STR_MENU_DISP_BATTERY: "Battery symbol" +STR_MENU_DISP_REFRESH: "Screen refresh" \ No newline at end of file diff --git a/src/SettingsList.h b/src/SettingsList.h index 45b85dba..ef6e99cb 100644 --- a/src/SettingsList.h +++ b/src/SettingsList.h @@ -9,15 +9,30 @@ #include "activities/settings/SettingsActivity.h" // Shared settings list used by both the device settings UI and the web settings API. -// Each entry has a key (for JSON API) and category (for grouping). -// ACTION-type entries and entries without a key are device-only. +// +// Fields that drive UI behaviour: +// category — which tab the setting appears under (STR_CAT_DISPLAY, STR_CAT_READER, …). +// Entries with STR_NONE_OPT or web-only categories are skipped by the device UI. +// subcategory — optional section heading within a tab. Items remain in their defined order; +// no reordering or grouping occurs. When an item's subcategory differs from the +// previous item's, SettingsActivity::onEnter() automatically inserts a separator +// row before it. Add with .withSubcategory(StrId::STR_MY_SECTION). +// Items without a subcategory (STR_NONE_OPT) never trigger a separator. +// key — JSON property name used by the web settings API (nullptr = device-only). +// +// ACTION-type entries and entries without a key are device-only and are added directly +// in SettingsActivity::onEnter(), not here. inline const std::vector& getSettingsList() { static const std::vector list = { // --- Display --- + 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}, + "sleepTimeout", StrId::STR_CAT_DISPLAY), SettingInfo::Enum(StrId::STR_SLEEP_SCREEN, &CrossPointSettings::sleepScreen, {StrId::STR_DARK, StrId::STR_LIGHT, StrId::STR_CUSTOM, StrId::STR_COVER, StrId::STR_NONE_OPT, StrId::STR_COVER_CUSTOM, StrId::STR_PAGE_OVERLAY}, - "sleepScreen", StrId::STR_CAT_DISPLAY), + "sleepScreen", StrId::STR_CAT_DISPLAY) + .withSubcategory(StrId::STR_MENU_DISP_SLEEP), SettingInfo::Enum(StrId::STR_SLEEP_COVER_MODE, &CrossPointSettings::sleepScreenCoverMode, {StrId::STR_FIT, StrId::STR_CROP}, "sleepScreenCoverMode", StrId::STR_CAT_DISPLAY), SettingInfo::Enum(StrId::STR_SLEEP_COVER_FILTER, &CrossPointSettings::sleepScreenCoverFilter, @@ -31,16 +46,18 @@ inline const std::vector& getSettingsList() { {StrId::STR_RANDOM, StrId::STR_SEQUENTIAL}, "sleepImagePickMode", StrId::STR_CAT_DISPLAY), SettingInfo::Enum(StrId::STR_HIDE_BATTERY, &CrossPointSettings::hideBatteryPercentage, {StrId::STR_NEVER, StrId::STR_IN_READER, StrId::STR_ALWAYS}, "hideBatteryPercentage", - StrId::STR_CAT_DISPLAY), + StrId::STR_CAT_DISPLAY) + .withSubcategory(StrId::STR_MENU_DISP_BATTERY), SettingInfo::Enum( StrId::STR_REFRESH_FREQ, &CrossPointSettings::refreshFrequency, {StrId::STR_PAGES_1, StrId::STR_PAGES_5, StrId::STR_PAGES_10, StrId::STR_PAGES_15, StrId::STR_PAGES_30}, - "refreshFrequency", StrId::STR_CAT_DISPLAY), + "refreshFrequency", StrId::STR_CAT_DISPLAY) + .withSubcategory(StrId::STR_MENU_DISP_REFRESH), + SettingInfo::Toggle(StrId::STR_SUNLIGHT_FADING_FIX, &CrossPointSettings::fadingFix, "fadingFix", + StrId::STR_CAT_DISPLAY), SettingInfo::Enum(StrId::STR_UI_THEME, &CrossPointSettings::uiTheme, {StrId::STR_THEME_CLASSIC, StrId::STR_THEME_LYRA, StrId::STR_THEME_LYRA_EXTENDED}, "uiTheme", StrId::STR_CAT_DISPLAY), - SettingInfo::Toggle(StrId::STR_SUNLIGHT_FADING_FIX, &CrossPointSettings::fadingFix, "fadingFix", - StrId::STR_CAT_DISPLAY), // --- Reader --- SettingInfo::Enum(StrId::STR_FONT_FAMILY, &CrossPointSettings::fontFamily, @@ -74,12 +91,6 @@ inline const std::vector& getSettingsList() { SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering, {StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS}, "imageRendering", StrId::STR_CAT_READER), -#ifdef ENABLE_IMAGE_DITHERING_EXTENSION - SettingInfo::Enum( - StrId::STR_IMAGE_DITHERING, &CrossPointSettings::imageDithering, - {StrId::STR_IMAGE_DITHER_BAYER, StrId::STR_IMAGE_DITHER_ATKINSON, StrId::STR_IMAGE_DITHER_DIFFUSED_BAYER}, - "imageDithering", StrId::STR_CAT_READER), -#endif SettingInfo::Toggle(StrId::STR_CREATE_FALLBACK_FOR_INVALID_TOC, &CrossPointSettings::syntheticTocFallback, "syntheticTocFallback", StrId::STR_CAT_READER), // --- Controls --- @@ -92,9 +103,6 @@ inline const std::vector& getSettingsList() { StrId::STR_CAT_CONTROLS), // --- System --- - 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}, - "sleepTimeout", StrId::STR_CAT_SYSTEM), SettingInfo::Toggle(StrId::STR_SHOW_HIDDEN_FILES, &CrossPointSettings::showHiddenFiles, "showHiddenFiles", StrId::STR_CAT_SYSTEM), SettingInfo::Toggle(StrId::STR_SHOW_FILE_EXTENSIONS, &CrossPointSettings::showFileExtensions, diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index d03f6c6e..34f2cdca 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -38,12 +38,27 @@ bool SettingsActivity::isListItemSelectable(int settingIdx) const { void SettingsActivity::onEnter() { Activity::onEnter(); - // Build per-category vectors from the shared settings list + // Build per-category vectors from the shared settings list. + // addTo tracks the last subcategory per vector and automatically inserts a separator + // row whenever a setting carries a new subcategory label. displaySettings.clear(); readerSettings.clear(); controlsSettings.clear(); systemSettings.clear(); + StrId lastDisplaySub = StrId::STR_NONE_OPT; + StrId lastReaderSub = StrId::STR_NONE_OPT; + StrId lastControlsSub = StrId::STR_NONE_OPT; + StrId lastSystemSub = StrId::STR_NONE_OPT; + + auto addTo = [](std::vector& vec, StrId& lastSub, SettingInfo s) { + if (s.subcategory != StrId::STR_NONE_OPT && s.subcategory != lastSub) { + vec.push_back(SettingInfo::Separator(s.subcategory)); + lastSub = s.subcategory; + } + vec.push_back(std::move(s)); + }; + for (const auto& setting : getSettingsList()) { if (setting.category == StrId::STR_NONE_OPT) continue; if (setting.category == StrId::STR_CAT_SYSTEM && @@ -52,38 +67,49 @@ void SettingsActivity::onEnter() { continue; } if (setting.category == StrId::STR_CAT_DISPLAY) { - displaySettings.push_back(setting); + addTo(displaySettings, lastDisplaySub, setting); } else if (setting.category == StrId::STR_CAT_READER) { - readerSettings.push_back(setting); + addTo(readerSettings, lastReaderSub, setting); } else if (setting.category == StrId::STR_CAT_CONTROLS) { - controlsSettings.push_back(setting); + addTo(controlsSettings, lastControlsSub, setting); } else if (setting.category == StrId::STR_CAT_SYSTEM) { - systemSettings.push_back(setting); + addTo(systemSettings, lastSystemSub, setting); } // Web-only categories (KOReader Sync, OPDS Browser) are skipped for device UI } - // Append device-only ACTION items + // Device-only ACTION items — subcategory drives separator insertion automatically. controlsSettings.insert(controlsSettings.begin(), SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons)); - systemSettings.push_back(SettingInfo::Action(StrId::STR_LANGUAGE, SettingAction::Language)); - // Network section - systemSettings.push_back(SettingInfo::Separator(StrId::STR_MENU_SYS_NETWORK)); - 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)); - // Tools section - systemSettings.push_back(SettingInfo::Separator(StrId::STR_MENU_SYS_TOOLS)); - systemSettings.push_back(SettingInfo::Action(StrId::STR_CLOCK_SETTINGS, SettingAction::ClockSettings)); - systemSettings.push_back(SettingInfo::Action(StrId::STR_WEATHER_SETTINGS, SettingAction::Weather)); - // System section - systemSettings.push_back(SettingInfo::Separator(StrId::STR_MENU_SYS_SYSTEM)); - 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_SYSTEM_INFO, SettingAction::SystemInfo)); + addTo(readerSettings, lastReaderSub, + SettingInfo::Action(StrId::STR_CUSTOMISE_STATUS_BAR, SettingAction::CustomiseStatusBar)); - readerSettings.push_back(SettingInfo::Action(StrId::STR_CUSTOMISE_STATUS_BAR, SettingAction::CustomiseStatusBar)); + addTo(systemSettings, lastSystemSub, SettingInfo::Action(StrId::STR_LANGUAGE, SettingAction::Language)); + addTo(systemSettings, lastSystemSub, + SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network) + .withSubcategory(StrId::STR_MENU_SYS_NETWORK)); + addTo(systemSettings, lastSystemSub, + SettingInfo::Action(StrId::STR_KOREADER_SYNC, SettingAction::KOReaderSync) + .withSubcategory(StrId::STR_MENU_SYS_NETWORK)); + addTo(systemSettings, lastSystemSub, + SettingInfo::Action(StrId::STR_OPDS_BROWSER, SettingAction::OPDSBrowser) + .withSubcategory(StrId::STR_MENU_SYS_NETWORK)); + addTo(systemSettings, lastSystemSub, + SettingInfo::Action(StrId::STR_CLOCK_SETTINGS, SettingAction::ClockSettings) + .withSubcategory(StrId::STR_MENU_SYS_TOOLS)); + addTo(systemSettings, lastSystemSub, + SettingInfo::Action(StrId::STR_WEATHER_SETTINGS, SettingAction::Weather) + .withSubcategory(StrId::STR_MENU_SYS_TOOLS)); + addTo(systemSettings, lastSystemSub, + SettingInfo::Action(StrId::STR_CLEAR_READING_CACHE, SettingAction::ClearCache) + .withSubcategory(StrId::STR_MENU_SYS_SYSTEM)); + addTo(systemSettings, lastSystemSub, + SettingInfo::Action(StrId::STR_CHECK_UPDATES, SettingAction::CheckForUpdates) + .withSubcategory(StrId::STR_MENU_SYS_SYSTEM)); + addTo(systemSettings, lastSystemSub, + SettingInfo::Action(StrId::STR_SYSTEM_INFO, SettingAction::SystemInfo) + .withSubcategory(StrId::STR_MENU_SYS_SYSTEM)); // Reset selection to first category selectedCategoryIndex = 0; diff --git a/src/activities/settings/SettingsActivity.h b/src/activities/settings/SettingsActivity.h index 589b2c39..161f7ddf 100644 --- a/src/activities/settings/SettingsActivity.h +++ b/src/activities/settings/SettingsActivity.h @@ -152,7 +152,13 @@ struct SettingInfo { } bool isSeparator = false; + StrId subcategory = StrId::STR_NONE_OPT; [[nodiscard]] std::string getTitle() const; + + SettingInfo& withSubcategory(StrId sub) { + subcategory = sub; + return *this; + } }; class SettingsActivity final : public Activity {