From 4e3c3cffa5b6919823903e15c1ced15b2f1eef32 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Fri, 6 Feb 2026 18:10:29 -0600 Subject: [PATCH 1/7] Setup core design of new discrete menu tiles --- include/engine/engine.h | 2 ++ src/core/context-manager.cpp | 12 ++++---- src/engine/engine.cpp | 58 ++++++++++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/include/engine/engine.h b/include/engine/engine.h index 4c9b742..b3208e1 100644 --- a/include/engine/engine.h +++ b/include/engine/engine.h @@ -16,9 +16,11 @@ namespace Engine SystemCore::ContextManager contextManager; uint32_t lastRender = 0; float disconnectedLedPhaseShift = 0; + static constexpr uint16_t menuTileWidth = 12; void initializeEngine(); void renderLedStrip(); + void displayMenuNavigation(); void displayMainMenuSelection(); void displayGameSelection(); void displaySceneSelection(); diff --git a/src/core/context-manager.cpp b/src/core/context-manager.cpp index 927bc6b..af4decc 100644 --- a/src/core/context-manager.cpp +++ b/src/core/context-manager.cpp @@ -42,13 +42,13 @@ namespace SystemCore void ContextManager::navigateMainMenu() { - if (controller.wasPressed(Player::ControllerButton::Down)) + if (controller.wasPressed(Player::ControllerButton::Down) || controller.wasPressed(Player::ControllerButton::Right)) { stateManager.selectNextMenu(); logf("Highlighting Main Menu option %d", stateManager.getUserMenuChoice()); } - if (controller.wasPressed(Player::ControllerButton::Up)) + if (controller.wasPressed(Player::ControllerButton::Up) || controller.wasPressed(Player::ControllerButton::Left)) { stateManager.selectNextMenu(Engine::MenuNavigationDirection::Reverse); logf("Highlighting Main Menu option %d", stateManager.getUserMenuChoice()); @@ -72,13 +72,13 @@ namespace SystemCore void ContextManager::navigateGameMenu() { - if (controller.wasPressed(Player::ControllerButton::Down)) + if (controller.wasPressed(Player::ControllerButton::Down) || controller.wasPressed(Player::ControllerButton::Right)) { stateManager.selectNextGame(); logf("Highlighting Games Submenu option %s", stateManager.printGameName(static_cast(stateManager.getUserGameChoice()))); } - if (controller.wasPressed(Player::ControllerButton::Up)) + if (controller.wasPressed(Player::ControllerButton::Up) || controller.wasPressed(Player::ControllerButton::Left)) { stateManager.selectNextGame(Engine::MenuNavigationDirection::Reverse); logf("Highlighting Games Submenu option %s", stateManager.printGameName(static_cast(stateManager.getUserGameChoice()))); @@ -110,13 +110,13 @@ namespace SystemCore void ContextManager::navigateSceneMenu() { - if (controller.wasPressed(Player::ControllerButton::Down)) + if (controller.wasPressed(Player::ControllerButton::Down) || controller.wasPressed(Player::ControllerButton::Right)) { stateManager.selectNextScene(); logf("Highlighting Scene Submenu option %s", stateManager.printSceneName(static_cast(stateManager.getUserSceneChoice()))); } - if (controller.wasPressed(Player::ControllerButton::Up)) + if (controller.wasPressed(Player::ControllerButton::Up) || controller.wasPressed(Player::ControllerButton::Left)) { stateManager.selectNextScene(Engine::MenuNavigationDirection::Reverse); logf("Highlighting Scene Submenu option %s", stateManager.printSceneName(static_cast(stateManager.getUserSceneChoice()))); diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index d0700e5..a452246 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -33,15 +33,15 @@ namespace Engine { case SystemState::MenuHome: contextManager.navigateMainMenu(); - displayMainMenuSelection(); + displayMenuNavigation(); break; case SystemState::MenuGames: contextManager.navigateGameMenu(); - displayGameSelection(); + displayMenuNavigation(); break; case SystemState::MenuScenes: contextManager.navigateSceneMenu(); - displaySceneSelection(); + displayMenuNavigation(); break; case SystemState::GameRecall: case SystemState::GamePhaseEvasion: @@ -138,6 +138,58 @@ namespace Engine disconnectedLedPhaseShift = 0; } + void GameEngine::displayMenuNavigation() + { + constexpr uint8_t numModes = static_cast(MainMenuSelection::COUNT); + uint8_t modeSelected = static_cast(contextManager.stateManager.getUserMenuChoice()); + + constexpr uint8_t numGames = static_cast(GameSelection::COUNT); + uint8_t gameSelected = static_cast(contextManager.stateManager.getUserGameChoice()); + + constexpr uint8_t numScenes = static_cast(SceneSelection::COUNT); + uint8_t sceneSelected = static_cast(contextManager.stateManager.getUserSceneChoice()); + + uint16_t displayIndex = SystemCore::Configuration::numLeds - 1; + + for (uint8_t modeIdx = 0; modeIdx < numModes; ++modeIdx) + { + for (uint16_t i = 0; i < menuTileWidth; ++i) + { + if (modeIdx == modeSelected) + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuLightGreen}; + else + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::Color::DarkSlateGray}; + + displayIndex--; + } + + if (modeIdx < numModes - 1) + displayIndex -= menuTileWidth; + } + + displayIndex = 0; + + uint16_t gamesOrScenesAvailable = contextManager.stateManager.current() == SystemState::MenuHome || contextManager.stateManager.current() == SystemState::MenuGames ? numGames : numScenes; + + for (uint8_t modeIdx = 0; modeIdx < gamesOrScenesAvailable; ++modeIdx) + { + for (uint16_t i = 0; i < menuTileWidth; ++i) + { + if (contextManager.stateManager.current() == SystemState::MenuGames && modeIdx == gameSelected) + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuLightBlue}; + else if (contextManager.stateManager.current() == SystemState::MenuScenes && modeIdx == sceneSelected) + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::GameRed}; + else + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::Color::DarkSlateGray}; + + displayIndex++; + } + + if (modeIdx < gamesOrScenesAvailable - 1) + displayIndex += menuTileWidth; + } + } + void GameEngine::displayMainMenuSelection() { constexpr uint8_t numOfSupportedModes = static_cast(MainMenuSelection::COUNT); From 05b60b9c27bafbded38826d38cf3ba31958d7f66 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sat, 7 Feb 2026 10:27:53 -0600 Subject: [PATCH 2/7] Consistent color themes --- include/lights/color-code.h | 10 ++++------ include/lights/color.h | 8 ++++---- src/core/context-manager.cpp | 2 +- src/engine/engine.cpp | 12 ++++++------ 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/include/lights/color-code.h b/include/lights/color-code.h index b64eb17..3cc2e07 100644 --- a/include/lights/color-code.h +++ b/include/lights/color-code.h @@ -6,11 +6,9 @@ namespace Lights { enum class ColorCode : uint32_t { - MenuLightBlue = 0x4da6ff, - MenuLightGreen = 0x4dff4d, - GameBlue = 0x4da6ff, - GameRed = 0xff4d4d, - GameGreen = 0x4dff4d, - GameYellow = 0xffd24d + ThemeBlue = 0x4da6ff, + ThemeRed = 0xff4d4d, + ThemeGreen = 0x4dff4d, + ThemeYellow = 0xffd24d }; } \ No newline at end of file diff --git a/include/lights/color.h b/include/lights/color.h index 8a6a52a..0cf3ba4 100644 --- a/include/lights/color.h +++ b/include/lights/color.h @@ -61,8 +61,8 @@ namespace Lights }; inline const Color colorPalette[4] = { - Color(ColorCode::GameBlue), - Color(ColorCode::GameRed), - Color(ColorCode::GameGreen), - Color(ColorCode::GameYellow)}; + Color(ColorCode::ThemeBlue), + Color(ColorCode::ThemeRed), + Color(ColorCode::ThemeGreen), + Color(ColorCode::ThemeYellow)}; } \ No newline at end of file diff --git a/src/core/context-manager.cpp b/src/core/context-manager.cpp index af4decc..c26c802 100644 --- a/src/core/context-manager.cpp +++ b/src/core/context-manager.cpp @@ -20,7 +20,7 @@ namespace SystemCore void ContextManager::checkExitRequest() { - if (controller.wasPressed(Player::ControllerButton::Ps)) + if (controller.wasPressedAndReleased(Player::ControllerButton::Ps)) { stateManager.setNext(Engine::SystemState::MenuHome); stateManager.setNextUserMenuChoice(Engine::MainMenuSelection::Games); diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a452246..ad7e082 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -156,7 +156,7 @@ namespace Engine for (uint16_t i = 0; i < menuTileWidth; ++i) { if (modeIdx == modeSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuLightGreen}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen}; else contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::Color::DarkSlateGray}; @@ -176,9 +176,9 @@ namespace Engine for (uint16_t i = 0; i < menuTileWidth; ++i) { if (contextManager.stateManager.current() == SystemState::MenuGames && modeIdx == gameSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuLightBlue}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue}; else if (contextManager.stateManager.current() == SystemState::MenuScenes && modeIdx == sceneSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::GameRed}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeRed}; else contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::Color::DarkSlateGray}; @@ -204,7 +204,7 @@ namespace Engine { double x = static_cast(i); double scope = std::exp(-0.5 * std::pow((x - mu) / sigma, 2.0)); - contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::MenuLightGreen} * scope; + contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::ThemeGreen} * scope; } } @@ -222,7 +222,7 @@ namespace Engine { double x = static_cast(i); double scope = 100 * std::exp(-0.5 * std::pow((x - mu) / sigma, 2.0)); - contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::MenuLightBlue} * (scope / 100.0); + contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::ThemeBlue} * (scope / 100.0); } } @@ -240,7 +240,7 @@ namespace Engine { double x = static_cast(i); double scope = 100 * std::exp(-0.5 * std::pow((x - mu) / sigma, 2.0)); - contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::MenuLightBlue} * (scope / 100.0); + contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::ThemeBlue} * (scope / 100.0); } } From 3fd779a1c03bc84d1f7589fce510b13a5139fab0 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sat, 7 Feb 2026 11:11:59 -0600 Subject: [PATCH 3/7] Set dimming effect when inactive --- include/lights/color-code.h | 3 ++- src/engine/engine.cpp | 22 ++++++++++++---------- tools/led_gui/virtualization.py | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/include/lights/color-code.h b/include/lights/color-code.h index 3cc2e07..a86c788 100644 --- a/include/lights/color-code.h +++ b/include/lights/color-code.h @@ -9,6 +9,7 @@ namespace Lights ThemeBlue = 0x4da6ff, ThemeRed = 0xff4d4d, ThemeGreen = 0x4dff4d, - ThemeYellow = 0xffd24d + ThemeYellow = 0xffd24d, + MenuUnselected = 0x404040, }; } \ No newline at end of file diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ad7e082..b1d1bac 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -143,22 +143,17 @@ namespace Engine constexpr uint8_t numModes = static_cast(MainMenuSelection::COUNT); uint8_t modeSelected = static_cast(contextManager.stateManager.getUserMenuChoice()); - constexpr uint8_t numGames = static_cast(GameSelection::COUNT); - uint8_t gameSelected = static_cast(contextManager.stateManager.getUserGameChoice()); - - constexpr uint8_t numScenes = static_cast(SceneSelection::COUNT); - uint8_t sceneSelected = static_cast(contextManager.stateManager.getUserSceneChoice()); - uint16_t displayIndex = SystemCore::Configuration::numLeds - 1; + float inactiveSelectionDimmingScale = contextManager.stateManager.current() == SystemState::MenuHome ? 1.0f : 0.4f; for (uint8_t modeIdx = 0; modeIdx < numModes; ++modeIdx) { for (uint16_t i = 0; i < menuTileWidth; ++i) { if (modeIdx == modeSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen} * inactiveSelectionDimmingScale; else - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::Color::DarkSlateGray}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale; displayIndex--; } @@ -167,7 +162,14 @@ namespace Engine displayIndex -= menuTileWidth; } + constexpr uint8_t numGames = static_cast(GameSelection::COUNT); + uint8_t gameSelected = static_cast(contextManager.stateManager.getUserGameChoice()); + + constexpr uint8_t numScenes = static_cast(SceneSelection::COUNT); + uint8_t sceneSelected = static_cast(contextManager.stateManager.getUserSceneChoice()); + displayIndex = 0; + inactiveSelectionDimmingScale = (contextManager.stateManager.current() == SystemState::MenuGames || contextManager.stateManager.current() == SystemState::MenuScenes) ? 1.0f : 0.4f; uint16_t gamesOrScenesAvailable = contextManager.stateManager.current() == SystemState::MenuHome || contextManager.stateManager.current() == SystemState::MenuGames ? numGames : numScenes; @@ -178,9 +180,9 @@ namespace Engine if (contextManager.stateManager.current() == SystemState::MenuGames && modeIdx == gameSelected) contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue}; else if (contextManager.stateManager.current() == SystemState::MenuScenes && modeIdx == sceneSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeRed}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeYellow}; else - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::Color::DarkSlateGray}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale; displayIndex++; } diff --git a/tools/led_gui/virtualization.py b/tools/led_gui/virtualization.py index fa6ffe4..fee839f 100644 --- a/tools/led_gui/virtualization.py +++ b/tools/led_gui/virtualization.py @@ -187,7 +187,7 @@ def process_buffer(): time.sleep(1) def on_render(self, time, frametime): - self.ctx.clear(0.1, 0.1, 0.1) + self.ctx.clear(0.05, 0.05, 0.05) with self.led_lock: self.vbo_positions.write(self.positions.tobytes()) self.vbo_colors.write(self.led_colors.tobytes()) From ac84d2bb554a687dd17e44763df3a21195ab2dbf Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sat, 7 Feb 2026 11:27:49 -0600 Subject: [PATCH 4/7] Adjust submenu selection based on highlighted main menu --- src/engine/engine.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b1d1bac..b702960 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -170,12 +170,11 @@ namespace Engine displayIndex = 0; inactiveSelectionDimmingScale = (contextManager.stateManager.current() == SystemState::MenuGames || contextManager.stateManager.current() == SystemState::MenuScenes) ? 1.0f : 0.4f; - - uint16_t gamesOrScenesAvailable = contextManager.stateManager.current() == SystemState::MenuHome || contextManager.stateManager.current() == SystemState::MenuGames ? numGames : numScenes; + uint8_t gamesOrScenesAvailable = contextManager.stateManager.getUserMenuChoice() == MainMenuSelection::Games ? numGames : numScenes; for (uint8_t modeIdx = 0; modeIdx < gamesOrScenesAvailable; ++modeIdx) { - for (uint16_t i = 0; i < menuTileWidth; ++i) + for (size_t i = 0; i < menuTileWidth; ++i) { if (contextManager.stateManager.current() == SystemState::MenuGames && modeIdx == gameSelected) contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue}; From 731b01abcafc2fb2aecde083e29ab7683b9c4de3 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sat, 7 Feb 2026 11:56:50 -0600 Subject: [PATCH 5/7] Compute gaussian curve for blended menu tiles --- include/lights/color-code.h | 2 +- src/engine/engine.cpp | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/include/lights/color-code.h b/include/lights/color-code.h index a86c788..dd2bf8c 100644 --- a/include/lights/color-code.h +++ b/include/lights/color-code.h @@ -10,6 +10,6 @@ namespace Lights ThemeRed = 0xff4d4d, ThemeGreen = 0x4dff4d, ThemeYellow = 0xffd24d, - MenuUnselected = 0x404040, + MenuUnselected = 0x5a5a50, }; } \ No newline at end of file diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b702960..80fcc75 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -144,16 +144,21 @@ namespace Engine uint8_t modeSelected = static_cast(contextManager.stateManager.getUserMenuChoice()); uint16_t displayIndex = SystemCore::Configuration::numLeds - 1; - float inactiveSelectionDimmingScale = contextManager.stateManager.current() == SystemState::MenuHome ? 1.0f : 0.4f; + float inactiveSelectionDimmingScale = contextManager.stateManager.current() == SystemState::MenuHome ? 1.0f : 0.3f; + constexpr float center = (menuTileWidth - 1) / 2.0f; + constexpr double sigma = 3.0f; for (uint8_t modeIdx = 0; modeIdx < numModes; ++modeIdx) { for (uint16_t i = 0; i < menuTileWidth; ++i) { + float x = i - center; + float blend = std::exp(-(x * x) / (2 * sigma * sigma)); // computed gaussian curve + if (modeIdx == modeSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen} * inactiveSelectionDimmingScale; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen} * inactiveSelectionDimmingScale * blend; else - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; displayIndex--; } @@ -169,19 +174,22 @@ namespace Engine uint8_t sceneSelected = static_cast(contextManager.stateManager.getUserSceneChoice()); displayIndex = 0; - inactiveSelectionDimmingScale = (contextManager.stateManager.current() == SystemState::MenuGames || contextManager.stateManager.current() == SystemState::MenuScenes) ? 1.0f : 0.4f; + inactiveSelectionDimmingScale = (contextManager.stateManager.current() == SystemState::MenuGames || contextManager.stateManager.current() == SystemState::MenuScenes) ? 1.0f : 0.3f; uint8_t gamesOrScenesAvailable = contextManager.stateManager.getUserMenuChoice() == MainMenuSelection::Games ? numGames : numScenes; for (uint8_t modeIdx = 0; modeIdx < gamesOrScenesAvailable; ++modeIdx) { for (size_t i = 0; i < menuTileWidth; ++i) { + float x = i - center; + float blend = std::exp(-(x * x) / (2 * sigma * sigma)); + if (contextManager.stateManager.current() == SystemState::MenuGames && modeIdx == gameSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue} * blend; else if (contextManager.stateManager.current() == SystemState::MenuScenes && modeIdx == sceneSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeYellow}; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeYellow} * blend; else - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale; + contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; displayIndex++; } From 0c7d8101dd03f670de2efa902751eae4aa6ccb64 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sat, 7 Feb 2026 22:16:19 -0600 Subject: [PATCH 6/7] Architectural refactor (single-responsibility) --- include/core/context-manager.h | 2 + include/display/menu-navigation.h | 23 ++++++ include/engine/engine.h | 4 - src/core/context-manager.cpp | 5 +- src/display/menu-navigation.cpp | 67 +++++++++++++++++ src/engine/engine.cpp | 119 +----------------------------- 6 files changed, 97 insertions(+), 123 deletions(-) create mode 100644 include/display/menu-navigation.h create mode 100644 src/display/menu-navigation.cpp diff --git a/include/core/context-manager.h b/include/core/context-manager.h index 4d51781..e35106f 100644 --- a/include/core/context-manager.h +++ b/include/core/context-manager.h @@ -5,6 +5,7 @@ #include "player/controller.h" #include "lights/led-strip.h" #include "display/display.h" +#include "display/menu-navigation.h" namespace SystemCore { @@ -21,6 +22,7 @@ namespace SystemCore Player::Controller controller; Lights::LedStrip leds; Display::OledDisplay display; + Display::MenuTileNavigation menuNav; void navigateMainMenu(); void navigateGameMenu(); diff --git a/include/display/menu-navigation.h b/include/display/menu-navigation.h new file mode 100644 index 0000000..629f6ff --- /dev/null +++ b/include/display/menu-navigation.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace SystemCore +{ + // forward declaration because of ContextManager/MenuTileNavigation circular dependency + class ContextManager; +} + +namespace Display +{ + class MenuTileNavigation + { + public: + MenuTileNavigation(SystemCore::ContextManager *ctx) : contextManager{ctx} {} + void displayMenuNavigation(); + static constexpr uint16_t menuTileWidth = 12; + + private: + SystemCore::ContextManager *contextManager; + }; +} \ No newline at end of file diff --git a/include/engine/engine.h b/include/engine/engine.h index b3208e1..a5de381 100644 --- a/include/engine/engine.h +++ b/include/engine/engine.h @@ -20,10 +20,6 @@ namespace Engine void initializeEngine(); void renderLedStrip(); - void displayMenuNavigation(); - void displayMainMenuSelection(); - void displayGameSelection(); - void displaySceneSelection(); static void displayTask(void *param); }; } \ No newline at end of file diff --git a/src/core/context-manager.cpp b/src/core/context-manager.cpp index c26c802..17577c5 100644 --- a/src/core/context-manager.cpp +++ b/src/core/context-manager.cpp @@ -7,7 +7,7 @@ namespace SystemCore { - ContextManager::ContextManager() : display{this} {} + ContextManager::ContextManager() : display{this}, menuNav{this} {} ContextManager::~ContextManager() { @@ -68,6 +68,7 @@ namespace SystemCore break; } } + menuNav.displayMenuNavigation(); } void ContextManager::navigateGameMenu() @@ -106,6 +107,7 @@ namespace SystemCore stateManager.setNext(Engine::SystemState::MenuHome); log("Transitioning to Main Menu."); } + menuNav.displayMenuNavigation(); } void ContextManager::navigateSceneMenu() @@ -138,6 +140,7 @@ namespace SystemCore stateManager.setNext(Engine::SystemState::MenuHome); log("Transitioning to Main Menu."); } + menuNav.displayMenuNavigation(); } void ContextManager::transitionLayer() diff --git a/src/display/menu-navigation.cpp b/src/display/menu-navigation.cpp new file mode 100644 index 0000000..aa63adf --- /dev/null +++ b/src/display/menu-navigation.cpp @@ -0,0 +1,67 @@ +#include "display/menu-navigation.h" +#include "engine/state-manager.h" +#include "core/context-manager.h" + +namespace Display +{ + void MenuTileNavigation::displayMenuNavigation() + { + constexpr uint8_t numModes = static_cast(Engine::MainMenuSelection::COUNT); + uint8_t modeSelected = static_cast(contextManager->stateManager.getUserMenuChoice()); + + uint16_t displayIndex = SystemCore::Configuration::numLeds - 1; + float inactiveSelectionDimmingScale = contextManager->stateManager.current() == Engine::SystemState::MenuHome ? 1.0f : 0.3f; + constexpr float center = (menuTileWidth - 1) / 2.0f; + constexpr double sigma = 3.0f; + + for (uint8_t modeIdx = 0; modeIdx < numModes; ++modeIdx) + { + for (uint16_t i = 0; i < menuTileWidth; ++i) + { + float x = i - center; + float blend = std::exp(-(x * x) / (2 * sigma * sigma)); // computed gaussian curve + + if (modeIdx == modeSelected) + contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen} * inactiveSelectionDimmingScale * blend; + else + contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; + + displayIndex--; + } + + if (modeIdx < numModes - 1) + displayIndex -= menuTileWidth; + } + + constexpr uint8_t numGames = static_cast(Engine::GameSelection::COUNT); + uint8_t gameSelected = static_cast(contextManager->stateManager.getUserGameChoice()); + + constexpr uint8_t numScenes = static_cast(Engine::SceneSelection::COUNT); + uint8_t sceneSelected = static_cast(contextManager->stateManager.getUserSceneChoice()); + + displayIndex = 0; + inactiveSelectionDimmingScale = (contextManager->stateManager.current() == Engine::SystemState::MenuGames || contextManager->stateManager.current() == Engine::SystemState::MenuScenes) ? 1.0f : 0.3f; + uint8_t gamesOrScenesAvailable = contextManager->stateManager.getUserMenuChoice() == Engine::MainMenuSelection::Games ? numGames : numScenes; + + for (uint8_t modeIdx = 0; modeIdx < gamesOrScenesAvailable; ++modeIdx) + { + for (size_t i = 0; i < menuTileWidth; ++i) + { + float x = i - center; + float blend = std::exp(-(x * x) / (2 * sigma * sigma)); + + if (contextManager->stateManager.current() == Engine::SystemState::MenuGames && modeIdx == gameSelected) + contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue} * blend; + else if (contextManager->stateManager.current() == Engine::SystemState::MenuScenes && modeIdx == sceneSelected) + contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeYellow} * blend; + else + contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; + + displayIndex++; + } + + if (modeIdx < gamesOrScenesAvailable - 1) + displayIndex += menuTileWidth; + } + } +} \ No newline at end of file diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 80fcc75..25dcce2 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3,6 +3,7 @@ #include #include "engine/engine.h" #include "lights/color.h" +#include "display/menu-navigation.h" #include "logger.h" namespace Engine @@ -33,15 +34,12 @@ namespace Engine { case SystemState::MenuHome: contextManager.navigateMainMenu(); - displayMenuNavigation(); break; case SystemState::MenuGames: contextManager.navigateGameMenu(); - displayMenuNavigation(); break; case SystemState::MenuScenes: contextManager.navigateSceneMenu(); - displayMenuNavigation(); break; case SystemState::GameRecall: case SystemState::GamePhaseEvasion: @@ -138,121 +136,6 @@ namespace Engine disconnectedLedPhaseShift = 0; } - void GameEngine::displayMenuNavigation() - { - constexpr uint8_t numModes = static_cast(MainMenuSelection::COUNT); - uint8_t modeSelected = static_cast(contextManager.stateManager.getUserMenuChoice()); - - uint16_t displayIndex = SystemCore::Configuration::numLeds - 1; - float inactiveSelectionDimmingScale = contextManager.stateManager.current() == SystemState::MenuHome ? 1.0f : 0.3f; - constexpr float center = (menuTileWidth - 1) / 2.0f; - constexpr double sigma = 3.0f; - - for (uint8_t modeIdx = 0; modeIdx < numModes; ++modeIdx) - { - for (uint16_t i = 0; i < menuTileWidth; ++i) - { - float x = i - center; - float blend = std::exp(-(x * x) / (2 * sigma * sigma)); // computed gaussian curve - - if (modeIdx == modeSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen} * inactiveSelectionDimmingScale * blend; - else - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; - - displayIndex--; - } - - if (modeIdx < numModes - 1) - displayIndex -= menuTileWidth; - } - - constexpr uint8_t numGames = static_cast(GameSelection::COUNT); - uint8_t gameSelected = static_cast(contextManager.stateManager.getUserGameChoice()); - - constexpr uint8_t numScenes = static_cast(SceneSelection::COUNT); - uint8_t sceneSelected = static_cast(contextManager.stateManager.getUserSceneChoice()); - - displayIndex = 0; - inactiveSelectionDimmingScale = (contextManager.stateManager.current() == SystemState::MenuGames || contextManager.stateManager.current() == SystemState::MenuScenes) ? 1.0f : 0.3f; - uint8_t gamesOrScenesAvailable = contextManager.stateManager.getUserMenuChoice() == MainMenuSelection::Games ? numGames : numScenes; - - for (uint8_t modeIdx = 0; modeIdx < gamesOrScenesAvailable; ++modeIdx) - { - for (size_t i = 0; i < menuTileWidth; ++i) - { - float x = i - center; - float blend = std::exp(-(x * x) / (2 * sigma * sigma)); - - if (contextManager.stateManager.current() == SystemState::MenuGames && modeIdx == gameSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue} * blend; - else if (contextManager.stateManager.current() == SystemState::MenuScenes && modeIdx == sceneSelected) - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeYellow} * blend; - else - contextManager.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; - - displayIndex++; - } - - if (modeIdx < gamesOrScenesAvailable - 1) - displayIndex += menuTileWidth; - } - } - - void GameEngine::displayMainMenuSelection() - { - constexpr uint8_t numOfSupportedModes = static_cast(MainMenuSelection::COUNT); - uint8_t option = static_cast(contextManager.stateManager.getUserMenuChoice()); - uint16_t boundaryWidth = SystemCore::Configuration::numLeds / numOfSupportedModes; - uint16_t boundaryStart = boundaryWidth * option; - uint16_t boundaryEnd = boundaryWidth * (option + 1); - double mu = (boundaryStart + boundaryEnd) / 2.0; - constexpr double sigma = 35.0; - - for (uint16_t i = boundaryStart; i < boundaryEnd; ++i) - { - double x = static_cast(i); - double scope = std::exp(-0.5 * std::pow((x - mu) / sigma, 2.0)); - contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::ThemeGreen} * scope; - } - } - - void GameEngine::displayGameSelection() - { - constexpr uint8_t numOfSupportedModes = static_cast(GameSelection::COUNT); - uint8_t option = static_cast(contextManager.stateManager.getUserGameChoice()); - uint16_t boundaryWidth = SystemCore::Configuration::numLeds / numOfSupportedModes; - uint16_t boundaryStart = boundaryWidth * option; - uint16_t boundaryEnd = boundaryWidth * (option + 1); - double mu = (boundaryStart + boundaryEnd) / 2.0; - constexpr double sigma = 20.0; - - for (uint16_t i = boundaryStart; i < boundaryEnd; ++i) - { - double x = static_cast(i); - double scope = 100 * std::exp(-0.5 * std::pow((x - mu) / sigma, 2.0)); - contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::ThemeBlue} * (scope / 100.0); - } - } - - void GameEngine::displaySceneSelection() - { - constexpr uint8_t numOfSupportedModes = static_cast(SceneSelection::COUNT); - uint8_t option = static_cast(contextManager.stateManager.getUserSceneChoice()); - uint16_t boundaryWidth = SystemCore::Configuration::numLeds / numOfSupportedModes; - uint16_t boundaryStart = boundaryWidth * option; - uint16_t boundaryEnd = boundaryWidth * (option + 1); - double mu = (boundaryStart + boundaryEnd) / 2.0; - constexpr double sigma = 20.0; - - for (uint16_t i = boundaryStart; i < boundaryEnd; ++i) - { - double x = static_cast(i); - double scope = 100 * std::exp(-0.5 * std::pow((x - mu) / sigma, 2.0)); - contextManager.leds.buffer[i] = Lights::Color{Lights::ColorCode::ThemeBlue} * (scope / 100.0); - } - } - void GameEngine::renderLedStrip() { contextManager.leds.adjustLuminance(); From 1c565121b0a18c25eb357d2fd1869408e9c2b548 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sun, 8 Feb 2026 22:04:29 -0600 Subject: [PATCH 7/7] Adjust spacing, refactor --- src/display/menu-navigation.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/display/menu-navigation.cpp b/src/display/menu-navigation.cpp index aa63adf..ebdc564 100644 --- a/src/display/menu-navigation.cpp +++ b/src/display/menu-navigation.cpp @@ -7,10 +7,11 @@ namespace Display void MenuTileNavigation::displayMenuNavigation() { constexpr uint8_t numModes = static_cast(Engine::MainMenuSelection::COUNT); - uint8_t modeSelected = static_cast(contextManager->stateManager.getUserMenuChoice()); + Engine::MainMenuSelection modeSelected = contextManager->stateManager.getUserMenuChoice(); + Engine::SystemState currentState = contextManager->stateManager.current(); uint16_t displayIndex = SystemCore::Configuration::numLeds - 1; - float inactiveSelectionDimmingScale = contextManager->stateManager.current() == Engine::SystemState::MenuHome ? 1.0f : 0.3f; + float inactiveSelectionDimmingScale = currentState == Engine::SystemState::MenuHome ? 1.0f : 0.3f; constexpr float center = (menuTileWidth - 1) / 2.0f; constexpr double sigma = 3.0f; @@ -21,7 +22,7 @@ namespace Display float x = i - center; float blend = std::exp(-(x * x) / (2 * sigma * sigma)); // computed gaussian curve - if (modeIdx == modeSelected) + if (modeIdx == static_cast(modeSelected)) contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen} * inactiveSelectionDimmingScale * blend; else contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; @@ -30,7 +31,7 @@ namespace Display } if (modeIdx < numModes - 1) - displayIndex -= menuTileWidth; + displayIndex -= (menuTileWidth / 2); } constexpr uint8_t numGames = static_cast(Engine::GameSelection::COUNT); @@ -40,8 +41,8 @@ namespace Display uint8_t sceneSelected = static_cast(contextManager->stateManager.getUserSceneChoice()); displayIndex = 0; - inactiveSelectionDimmingScale = (contextManager->stateManager.current() == Engine::SystemState::MenuGames || contextManager->stateManager.current() == Engine::SystemState::MenuScenes) ? 1.0f : 0.3f; - uint8_t gamesOrScenesAvailable = contextManager->stateManager.getUserMenuChoice() == Engine::MainMenuSelection::Games ? numGames : numScenes; + inactiveSelectionDimmingScale = (currentState == Engine::SystemState::MenuGames || currentState == Engine::SystemState::MenuScenes) ? 1.0f : 0.3f; + uint8_t gamesOrScenesAvailable = modeSelected == Engine::MainMenuSelection::Games ? numGames : numScenes; for (uint8_t modeIdx = 0; modeIdx < gamesOrScenesAvailable; ++modeIdx) { @@ -50,9 +51,9 @@ namespace Display float x = i - center; float blend = std::exp(-(x * x) / (2 * sigma * sigma)); - if (contextManager->stateManager.current() == Engine::SystemState::MenuGames && modeIdx == gameSelected) + if (currentState == Engine::SystemState::MenuGames && modeIdx == gameSelected) contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue} * blend; - else if (contextManager->stateManager.current() == Engine::SystemState::MenuScenes && modeIdx == sceneSelected) + else if (currentState == Engine::SystemState::MenuScenes && modeIdx == sceneSelected) contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeYellow} * blend; else contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; @@ -61,7 +62,7 @@ namespace Display } if (modeIdx < gamesOrScenesAvailable - 1) - displayIndex += menuTileWidth; + displayIndex += (menuTileWidth / 2); } } } \ No newline at end of file