From e6220793a4196b4220e8ee48121dfac79a43e46e Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sun, 3 May 2026 00:28:22 +0200 Subject: [PATCH] Add a button overview activity --- lib/I18n/translations/english.yaml | 5 + .../ButtonActionsOverviewActivity.cpp | 142 ++++++++++++++++++ .../settings/ButtonActionsOverviewActivity.h | 14 ++ .../settings/SettingActionDispatch.cpp | 3 + src/activities/settings/SettingInfo.h | 1 + src/activities/settings/SettingsActivity.cpp | 6 + 6 files changed, 171 insertions(+) create mode 100644 src/activities/settings/ButtonActionsOverviewActivity.cpp create mode 100644 src/activities/settings/ButtonActionsOverviewActivity.h diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index 56cf2b47..fbcc52ee 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -555,6 +555,11 @@ STR_CAPTIVE_PORTAL_HINT_2: "visit the URL below to authorize, then press OK." STR_CAPTIVE_PORTAL_DONE: "I'm authorized" STR_MENU_BTN_ACTIONS: "Button Actions" STR_MENU_BTN_PHYSICAL: "Physical Buttons" +STR_BTN_ACTIONS_OVERVIEW: "Button Actions Overview" +STR_BTN_OVERVIEW_HEADER_BUTTON: "Button" +STR_BTN_OVERVIEW_HEADER_SHORT: "Short" +STR_BTN_OVERVIEW_HEADER_DOUBLE: "Double" +STR_BTN_OVERVIEW_HEADER_LONG: "Long" STR_BTN_SHORT_PRESS: "Short Press" STR_BTN_DOUBLE_PRESS: "Double Press" STR_BTN_LONG_PRESS: "Long Press" diff --git a/src/activities/settings/ButtonActionsOverviewActivity.cpp b/src/activities/settings/ButtonActionsOverviewActivity.cpp new file mode 100644 index 00000000..85b1ae13 --- /dev/null +++ b/src/activities/settings/ButtonActionsOverviewActivity.cpp @@ -0,0 +1,142 @@ +#include "ButtonActionsOverviewActivity.h" + +#include +#include + +#include + +#include "MappedInputManager.h" +#include "SettingInfo.h" +#include "SettingsList.h" +#include "components/UITheme.h" +#include "fontIds.h" + +namespace { + +struct ButtonRow { + StrId submenu; // e.g. STR_BTN_BACK — identifies which logical button + StrId labelStrId; // e.g. STR_BTN_BACK — display label for the row +}; + +// Order matches the on-screen rendering order — power → confirm. +constexpr std::array kButtonRows = {{ + {StrId::STR_BTN_POWER, StrId::STR_BTN_POWER}, + {StrId::STR_BTN_BACK, StrId::STR_BTN_BACK}, + {StrId::STR_BTN_LEFT, StrId::STR_BTN_LEFT}, + {StrId::STR_BTN_RIGHT, StrId::STR_BTN_RIGHT}, + {StrId::STR_BTN_PAGE_BACK, StrId::STR_BTN_PAGE_BACK}, + {StrId::STR_BTN_PAGE_FORWARD, StrId::STR_BTN_PAGE_FORWARD}, + {StrId::STR_BTN_CONFIRM, StrId::STR_BTN_CONFIRM}, +}}; + +// Find the SettingInfo for a given (button submenu, press kind) pair in the shared settings list. +const SettingInfo* findEntry(StrId submenu, StrId pressKind) { + for (const auto& s : getSettingsList()) { + if (s.submenu == submenu && s.nameId == pressKind && s.category == StrId::STR_CAT_CONTROLS) { + return &s; + } + } + return nullptr; +} + +std::string cellValue(StrId submenu, StrId pressKind) { + const SettingInfo* s = findEntry(submenu, pressKind); + if (!s) return {}; + return s->getDisplayValue(); +} + +} // namespace + +void ButtonActionsOverviewActivity::onEnter() { + Activity::onEnter(); + requestUpdate(); +} + +void ButtonActionsOverviewActivity::onExit() { Activity::onExit(); } + +void ButtonActionsOverviewActivity::loop() { + if (mappedInput.wasPressed(MappedInputManager::Button::Back) || + mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + finish(); + return; + } +} + +void ButtonActionsOverviewActivity::render(RenderLock&&) { + renderer.clearScreen(); + + const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, /*hasBottomHints=*/true, /*hasSideHints=*/false); + + GUI.drawHeader(renderer, + Rect{contentRect.x, contentRect.y + metrics.topPadding, contentRect.width, metrics.headerHeight}, + tr(STR_BTN_ACTIONS_OVERVIEW), CROSSPOINT_VERSION); + + const int fontId = UI_10_FONT_ID; + const int lineH = renderer.getLineHeight(fontId); + const int rowStep = lineH + 4; + const int padX = metrics.verticalSpacing * 2; + + // 4-column layout: button label, short, double, long. + // Button column gets a fixed share, the three action columns share the remainder evenly. + const int innerLeft = contentRect.x + padX; + const int innerWidth = contentRect.width - padX * 2; + const int colButtonWidth = innerWidth * 28 / 100; + const int colActionWidth = (innerWidth - colButtonWidth) / 3; + + const int colX[4] = { + innerLeft, + innerLeft + colButtonWidth, + innerLeft + colButtonWidth + colActionWidth, + innerLeft + colButtonWidth + colActionWidth * 2, + }; + const int colW[4] = { + colButtonWidth - 2, + colActionWidth - 2, + colActionWidth - 2, + innerLeft + innerWidth - colX[3] - 2, + }; + + int y = contentRect.y + metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; + + // Header row (bold) + const char* headers[4] = { + tr(STR_BTN_OVERVIEW_HEADER_BUTTON), + tr(STR_BTN_OVERVIEW_HEADER_SHORT), + tr(STR_BTN_OVERVIEW_HEADER_DOUBLE), + tr(STR_BTN_OVERVIEW_HEADER_LONG), + }; + for (int c = 0; c < 4; c++) { + const std::string clipped = renderer.truncatedText(fontId, headers[c], colW[c], EpdFontFamily::BOLD); + renderer.drawText(fontId, colX[c], y, clipped.c_str(), true, EpdFontFamily::BOLD); + } + y += lineH + 2; + + // Underline below header + const int underlineX[4] = {innerLeft, innerLeft + innerWidth, innerLeft + innerWidth, innerLeft}; + const int underlineY[4] = {y, y, y + 1, y + 1}; + renderer.fillPolygon(underlineX, underlineY, 4, true); + y += 4; + + // Data rows + for (const auto& row : kButtonRows) { + const std::string label = I18N.get(row.labelStrId); + const std::string clippedLabel = renderer.truncatedText(fontId, label.c_str(), colW[0], EpdFontFamily::BOLD); + renderer.drawText(fontId, colX[0], y, clippedLabel.c_str(), true, EpdFontFamily::BOLD); + + const std::string vShort = cellValue(row.submenu, StrId::STR_BTN_SHORT_PRESS); + const std::string vDouble = cellValue(row.submenu, StrId::STR_BTN_DOUBLE_PRESS); + const std::string vLong = cellValue(row.submenu, StrId::STR_BTN_LONG_PRESS); + + renderer.drawText(fontId, colX[1], y, renderer.truncatedText(fontId, vShort.c_str(), colW[1]).c_str()); + renderer.drawText(fontId, colX[2], y, renderer.truncatedText(fontId, vDouble.c_str(), colW[2]).c_str()); + renderer.drawText(fontId, colX[3], y, renderer.truncatedText(fontId, vLong.c_str(), colW[3]).c_str()); + + y += rowStep; + } + + const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + + renderer.displayBuffer(); +} diff --git a/src/activities/settings/ButtonActionsOverviewActivity.h b/src/activities/settings/ButtonActionsOverviewActivity.h new file mode 100644 index 00000000..00a91282 --- /dev/null +++ b/src/activities/settings/ButtonActionsOverviewActivity.h @@ -0,0 +1,14 @@ +#pragma once + +#include "activities/Activity.h" + +class ButtonActionsOverviewActivity final : public Activity { + public: + explicit ButtonActionsOverviewActivity(GfxRenderer& renderer, MappedInputManager& mappedInput) + : Activity("ButtonActionsOverview", renderer, mappedInput) {} + + void onEnter() override; + void onExit() override; + void loop() override; + void render(RenderLock&&) override; +}; diff --git a/src/activities/settings/SettingActionDispatch.cpp b/src/activities/settings/SettingActionDispatch.cpp index f0efb8bb..ba48c3ba 100644 --- a/src/activities/settings/SettingActionDispatch.cpp +++ b/src/activities/settings/SettingActionDispatch.cpp @@ -1,5 +1,6 @@ #include "SettingActionDispatch.h" +#include "ButtonActionsOverviewActivity.h" #include "ButtonRemapActivity.h" #include "ClearCacheActivity.h" #include "ClockSettingsActivity.h" @@ -20,6 +21,8 @@ std::unique_ptr createActivityForAction(SettingAction action, GfxRende switch (action) { case SettingAction::RemapFrontButtons: return std::make_unique(renderer, mappedInput); + case SettingAction::ButtonActionsOverview: + return std::make_unique(renderer, mappedInput); case SettingAction::CustomiseStatusBar: return std::make_unique(renderer, mappedInput); case SettingAction::DownloadFonts: diff --git a/src/activities/settings/SettingInfo.h b/src/activities/settings/SettingInfo.h index 7384c9b8..745e48d8 100644 --- a/src/activities/settings/SettingInfo.h +++ b/src/activities/settings/SettingInfo.h @@ -14,6 +14,7 @@ enum class SettingType { TOGGLE, ENUM, ACTION, VALUE, STRING }; enum class SettingAction { None, RemapFrontButtons, + ButtonActionsOverview, CustomiseStatusBar, DownloadFonts, ClockSettings, diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 96a8464e..e6f362a2 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -119,6 +119,12 @@ void SettingsActivity::onEnter() { SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons)); controlsSettings.insert(controlsSettings.begin(), SettingInfo::Separator(StrId::STR_MENU_BTN_PHYSICAL)); + // Button Actions overview lives at the end of the Button Actions section (same subcategory as + // the per-button submenus, so no new separator is inserted). + addToMoved(controlsSettings, lastControlsSub, + std::move(SettingInfo::Action(StrId::STR_BTN_ACTIONS_OVERVIEW, SettingAction::ButtonActionsOverview) + .withSubcategory(StrId::STR_MENU_BTN_ACTIONS))); + addToMoved(readerSettings, lastReaderSub, SettingInfo::Action(StrId::STR_CUSTOMISE_STATUS_BAR, SettingAction::CustomiseStatusBar));