Settings: its rows in five groups
CI / build (pull_request) Successful in 2m22s
Site / build (pull_request) Successful in 10s

Settings had grown to 21 rows in one list. It opens on five groups (This
device, Display, GNSS and radio, Network, System); Enter opens one, Back
returns to the groups, and Back from the groups leaves Settings.

SettingsMenu holds the groups and which one is open (host-tested: every
setting is in exactly one group). The guide, the how-tos, the README,
the scripts' messages and the firmware's own name the new paths, such as
"Settings > System > Firmware".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
This commit is contained in:
2026-10-10 00:11:52 +02:00
co-authored by Claude Opus 5.5
parent 37335b4829
commit 0c52613b72
43 changed files with 305 additions and 98 deletions
+79 -16
View File
@@ -16,19 +16,50 @@ struct RowDef {
const char* label;
};
const RowDef kRows[] = {
{Row::LongName, Kind::Text, "Long name"}, {Row::ShortName, Kind::Text, "Short name"},
{Row::Region, Kind::Choice, "Region"}, {Row::Timezone, Kind::Choice, "Timezone"},
{Row::Brightness, Kind::Slider, "Brightness"}, {Row::DimTimeout, Kind::Choice, "Dim after"},
{Row::OffTimeout, Kind::Choice, "Screen off after"}, {Row::Launcher, Kind::Toggle, "Launcher"},
{Row::Theme, Kind::Choice, "Theme"}, {Row::ThemeLight, Kind::Toggle, "Light or dark"}, {Row::Sound, Kind::Toggle, "Sound & LED"},
{Row::Gnss, Kind::Toggle, "GNSS"}, {Row::GnssQuiet, Kind::Toggle, "Pause GNSS for LoRa"},
{Row::Coordinates, Kind::Toggle, "Coordinates"},
{Row::ProbeMacs, Kind::Toggle, "Probe MACs"}, {Row::Wifi, Kind::Page, "Wi-Fi"},
{Row::CheckUpdates, Kind::Toggle, "Check for updates"}, {Row::Firmware, Kind::Page, "Firmware"},
{Row::Vpn, Kind::Page, "VPN"},
{Row::DebugConsole, Kind::Page, "Debug Console"}, {Row::About, Kind::Page, "About"},
const RowDef kGroups[] = {
{Row::GroupDevice, Kind::Group, "This device"}, {Row::GroupDisplay, Kind::Group, "Display"},
{Row::GroupPosition, Kind::Group, "GNSS and radio"}, {Row::GroupNetwork, Kind::Group, "Network"},
{Row::GroupSystem, Kind::Group, "System"},
};
constexpr int kGroupCount = sizeof(kGroups) / sizeof(kGroups[0]);
struct Member {
int group; // an index into kGroups
RowDef def;
};
// In the order each group lists them.
const Member kRows[] = {
{0, {Row::LongName, Kind::Text, "Long name"}},
{0, {Row::ShortName, Kind::Text, "Short name"}},
{0, {Row::Region, Kind::Choice, "Region"}},
{0, {Row::Timezone, Kind::Choice, "Timezone"}},
{0, {Row::Sound, Kind::Toggle, "Sound & LED"}},
{1, {Row::Brightness, Kind::Slider, "Brightness"}},
{1, {Row::DimTimeout, Kind::Choice, "Dim after"}},
{1, {Row::OffTimeout, Kind::Choice, "Screen off after"}},
{1, {Row::Theme, Kind::Choice, "Theme"}},
{1, {Row::ThemeLight, Kind::Toggle, "Light or dark"}},
{1, {Row::Launcher, Kind::Toggle, "Launcher"}},
{2, {Row::Gnss, Kind::Toggle, "GNSS"}},
{2, {Row::GnssQuiet, Kind::Toggle, "Pause GNSS for LoRa"}},
{2, {Row::Coordinates, Kind::Toggle, "Coordinates"}},
{2, {Row::ProbeMacs, Kind::Toggle, "Probe MACs"}},
{3, {Row::Wifi, Kind::Page, "Wi-Fi"}},
{3, {Row::Vpn, Kind::Page, "VPN"}},
{4, {Row::CheckUpdates, Kind::Toggle, "Check for updates"}},
{4, {Row::Firmware, Kind::Page, "Firmware"}},
{4, {Row::DebugConsole, Kind::Page, "Debug Console"}},
{4, {Row::About, Kind::Page, "About"}},
};
// Row i of what is listed: a group, or the i-th row of the open one.
const RowDef& at(int group, int i) {
if (group < 0) return kGroups[i < 0 || i >= kGroupCount ? 0 : i];
for (const Member& m : kRows)
if (m.group == group && i-- == 0) return m.def;
return kGroups[group]; // out of range: harmless
}
const int kDimSeconds[] = {10, 15, 30, 60, 120, 300};
const int kOffSeconds[] = {30, 60, 120, 300, 600, 1800};
@@ -65,10 +96,42 @@ int indexOf(const int (&table)[N], int value) {
} // namespace
int SettingsMenu::count() const { return sizeof(kRows) / sizeof(kRows[0]); }
SettingsMenu::Row SettingsMenu::row(int i) const { return kRows[i].row; }
SettingsMenu::Kind SettingsMenu::kind(int i) const { return kRows[i].kind; }
std::string SettingsMenu::label(int i) const { return kRows[i].label; }
bool SettingsMenu::open(int i) {
if (group_ >= 0 || i < 0 || i >= kGroupCount) return false;
group_ = lastGroup_ = i;
return true;
}
bool SettingsMenu::close() {
if (group_ < 0) return false;
group_ = -1;
return true;
}
std::string SettingsMenu::title() const { return group_ < 0 ? "" : kGroups[group_].label; }
bool SettingsMenu::reveal(Row r, int& index) {
int seen[kGroupCount] = {};
for (const Member& m : kRows) {
if (m.def.row == r) {
group_ = lastGroup_ = m.group;
index = seen[m.group];
return true;
}
seen[m.group]++;
}
return false;
}
int SettingsMenu::count() const {
if (group_ < 0) return kGroupCount;
int n = 0;
for (const Member& m : kRows) n += m.group == group_;
return n;
}
SettingsMenu::Row SettingsMenu::row(int i) const { return at(group_, i).row; }
SettingsMenu::Kind SettingsMenu::kind(int i) const { return at(group_, i).kind; }
std::string SettingsMenu::label(int i) const { return at(group_, i).label; }
std::string SettingsMenu::value(int i) const {
switch (row(i)) {