From 21911746812e1cf479a4f168909da2ae981ba41d Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Sun, 15 Jun 2025 00:49:30 +0200 Subject: [PATCH] implement left/right with callback Signed-off-by: Peter Siegmund --- components/insa/CMakeLists.txt | 6 +- components/insa/include/MenuOptions.h | 120 ++++++- components/insa/include/common/Common.h | 72 +++- components/insa/include/common/Menu.h | 232 ++++++++++++ components/insa/include/common/PSMenu.h | 137 ------- components/insa/include/common/ScrollBar.h | 102 ++++-- components/insa/include/common/Widget.h | 100 +++++- components/insa/include/data/MenuItem.h | 340 +++++++++++++++--- components/insa/include/ui/LightMenu.h | 137 ++++++- .../insa/include/ui/LightSettingsMenu.h | 14 + components/insa/include/ui/MainMenu.h | 95 ++++- components/insa/include/ui/SettingsMenu.h | 76 +++- components/insa/include/ui/SplashScreen.h | 165 ++++++++- components/insa/src/common/Menu.cpp | 308 ++++++++++++++++ components/insa/src/common/PSMenu.cpp | 260 -------------- components/insa/src/common/Widget.cpp | 2 +- components/insa/src/data/MenuItem.cpp | 39 +- components/insa/src/ui/LightMenu.cpp | 29 +- components/insa/src/ui/LightSettingsMenu.cpp | 65 ++++ components/insa/src/ui/MainMenu.cpp | 18 +- components/insa/src/ui/SettingsMenu.cpp | 9 +- components/ruth/CMakeLists.txt | 4 +- components/ruth/PushButton.cpp | 1 - components/ruth/include/PushButton.h | 9 - components/ruth/include/persistence.h | 8 + components/ruth/persistence.c | 3 + src/main.cpp | 14 +- src/ui/Device.cpp | 42 ++- src/ui/Device.h | 17 +- 29 files changed, 1783 insertions(+), 641 deletions(-) create mode 100644 components/insa/include/common/Menu.h delete mode 100644 components/insa/include/common/PSMenu.h create mode 100644 components/insa/include/ui/LightSettingsMenu.h create mode 100644 components/insa/src/common/Menu.cpp delete mode 100644 components/insa/src/common/PSMenu.cpp create mode 100644 components/insa/src/ui/LightSettingsMenu.cpp delete mode 100644 components/ruth/PushButton.cpp delete mode 100644 components/ruth/include/PushButton.h create mode 100644 components/ruth/include/persistence.h create mode 100644 components/ruth/persistence.c diff --git a/components/insa/CMakeLists.txt b/components/insa/CMakeLists.txt index 26ebf61..41b8508 100644 --- a/components/insa/CMakeLists.txt +++ b/components/insa/CMakeLists.txt @@ -1,10 +1,11 @@ if (DEFINED ENV{IDF_PATH}) idf_component_register(SRCS - src/common/PSMenu.cpp + src/common/Menu.cpp src/common/ScrollBar.cpp src/common/Widget.cpp src/data/MenuItem.cpp src/ui/LightMenu.cpp + src/ui/LightSettingsMenu.cpp src/ui/MainMenu.cpp src/ui/SettingsMenu.cpp src/ui/SplashScreen.cpp @@ -17,11 +18,12 @@ cmake_minimum_required(VERSION 3.30) project(insa) add_library(${PROJECT_NAME} STATIC - src/common/PSMenu.cpp + src/common/Menu.cpp src/common/ScrollBar.cpp src/common/Widget.cpp src/data/MenuItem.cpp src/ui/LightMenu.cpp + src/ui/LightSettingsMenu.cpp src/ui/MainMenu.cpp src/ui/SettingsMenu.cpp src/ui/SplashScreen.cpp diff --git a/components/insa/include/MenuOptions.h b/components/insa/include/MenuOptions.h index 7f047db..6f074c0 100644 --- a/components/insa/include/MenuOptions.h +++ b/components/insa/include/MenuOptions.h @@ -1,4 +1,14 @@ -// Prevents multiple inclusions of this header file +/** + * @file MenuOptions.h + * @brief Menu configuration structure and callback definitions + * @details This header defines the menu_options_t structure which contains all + * necessary configuration options and callback functions for menu widgets. + * It provides the interface between menu widgets and the application's + * screen management system, display context, and input handling. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once // Standard libraries for function objects and smart pointers @@ -6,28 +16,114 @@ #include // Project-specific headers +#include "persistence.h" #include "common/Widget.h" #include "u8g2.h" -// Structure for menu options and callback functions +class MenuItem; + +/** + * @struct menu_options_t + * @brief Configuration structure for menu widgets containing display context and callbacks + * @details This structure serves as a configuration container that provides menu widgets + * with access to the display system, screen management functions, and input + * handling callbacks. It acts as the bridge between individual menu widgets + * and the broader application framework. + * + * The structure contains: + * - Display context for rendering operations + * - Screen management callbacks for navigation + * - Input handling callback for button events + * + * All callback functions use std::function for type safety and flexibility, + * allowing both function pointers and lambda expressions to be used. + * + * @note This structure should be initialized by the application framework + * and passed to menu widgets during construction. + * + * @see Widget + * @see ButtonType + */ typedef struct { - // Pointer to u8g2 display object for graphics output + /** + * @brief Pointer to u8g2 display context for graphics output operations + * @details This pointer provides access to the u8g2 graphics library functions + * for rendering text, shapes, and other visual elements. It must be + * initialized and ready for drawing operations before being passed + * to menu widgets. + * + * @note The menu widgets do not take ownership of this pointer and assume + * it remains valid throughout their lifetime. Ensure the u8g2 context + * is properly managed by the application framework. + * + * @warning Must not be nullptr when passed to menu widgets + */ u8g2_t *u8g2; - // Callback function to set the current screen - // Parameter: Smart pointer to a Widget object + /** + * @brief Callback function to set the current active screen + * @param screen Smart pointer to the Widget that should become the active screen + * + * @details This callback replaces the currently active screen with the provided + * widget. It is typically used for direct screen transitions where the + * previous screen should be completely replaced rather than stacked. + * + * @note The callback takes ownership of the provided Widget through the shared_ptr. + * The previous screen will be destroyed unless other references exist. + * + * @see pushScreen for adding screens to a navigation stack + */ std::function)> setScreen; - // Callback function to add a new screen to the stack - // Parameter: Smart pointer to a Widget object + /** + * @brief Callback function to add a new screen to the navigation stack + * @param screen Smart pointer to the Widget that should be pushed onto the screen stack + * + * @details This callback adds a new screen on top of the current screen stack, + * allowing for hierarchical navigation where users can return to + * previous screens. Commonly used for sub-menus, settings screens, + * or modal dialogs. + * + * @note The callback takes ownership of the provided Widget through the shared_ptr. + * The current screen remains in memory and can be returned to via popScreen(). + * + * @see popScreen for removing screens from the navigation stack + * @see setScreen for direct screen replacement + */ std::function)> pushScreen; - // Callback function to remove the top screen from the stack - // No parameters required + /** + * @brief Callback function to remove the top screen from the navigation stack + * @details This callback removes the currently active screen and returns to the + * previous screen in the navigation stack. It is typically used for + * "back" or "cancel" operations in hierarchical menu systems. + * + * @note If the navigation stack is empty or contains only one screen, the + * behavior is implementation-dependent and should be handled gracefully + * by the application framework. + * + * @see pushScreen for adding screens to the navigation stack + */ std::function popScreen; - // Callback function to handle button presses - // Parameter: 8-bit button ID/status - std::function onButtonClicked; + /** + * @brief Callback function to handle button press events + * @param button The type of button that was pressed + * + * @details This callback is invoked when a button press event occurs that is + * not handled directly by the menu widget. It allows the application + * framework to implement global button handling logic, such as + * system-wide shortcuts or fallback behavior. + * + * @note This callback is typically used for buttons that have application-wide + * meaning (e.g., home button, menu button) rather than widget-specific + * navigation which is handled internally by the widgets. + * + * @see ButtonType for available button types + * @see Widget::onButtonClicked for widget-specific button handling + */ + std::function onButtonClicked; + + persistence_t *persistence; } menu_options_t; \ No newline at end of file diff --git a/components/insa/include/common/Common.h b/components/insa/include/common/Common.h index dca7e60..56b1090 100644 --- a/components/insa/include/common/Common.h +++ b/components/insa/include/common/Common.h @@ -1,21 +1,63 @@ +/** + * @file Common.h + * @brief Common definitions and types for the INSA component + * @details This header file contains shared enumerations, type definitions, and + * callback function types used throughout the INSA component system. + * It provides the foundation for button handling and event management. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once #include -// Enumeration defining the different types of buttons available in the system -// NONE represents no button pressed or an invalid button state -enum class ButtonType { - NONE, // No button or invalid state - UP, // Up directional button - DOWN, // Down directional button - LEFT, // Left directional button - RIGHT, // Right directional button - SELECT, // Select/confirm button - BACK // Back/cancel button +/** + * @enum ButtonType + * @brief Enumeration defining the different types of buttons available in the system + * @details This enumeration represents all possible button types that can be handled + * by the system's input management. NONE represents no button pressed or + * an invalid button state, while the other values correspond to specific + * directional and action buttons. + */ +enum class ButtonType +{ + NONE, ///< No button pressed or invalid button state + UP, ///< Up directional button for navigation + DOWN, ///< Down directional button for navigation + LEFT, ///< Left directional button for navigation + RIGHT, ///< Right directional button for navigation + SELECT, ///< Select/confirm button for accepting choices + BACK ///< Back/cancel button for returning to previous state }; -// Type alias for button event callback function -// Parameters: -// - uint8_t: Button identifier or additional data -// - ButtonType: The type of button that was pressed -typedef std::function ButtonCallback; \ No newline at end of file +// Forward declaration of MenuItem to avoid circular dependency +class MenuItem; + +/** + * @typedef ButtonCallback + * @brief Type alias for button event callback function + * @details This function type is used to define callback functions that handle + * button press events. The callback receives information about which + * button was pressed and any additional context data. + * + * @param MenuItem menu item for the specific action + * @param ButtonType The type of button that was pressed + * + * @note The first parameter can be used to distinguish between multiple instances + * of the same button type or to pass additional event-specific data. + * + * @example + * @code + * ButtonCallback myCallback = [](const MenuItem& item, ButtonType type) { + * if (type == ButtonType::SELECT) { + * // Handle select button press + * processSelection(item); + * } + * }; + * @endcode + */ +typedef std::function ButtonCallback; + +// Include MenuItem.h after the typedef to avoid circular dependency +#include "data/MenuItem.h" \ No newline at end of file diff --git a/components/insa/include/common/Menu.h b/components/insa/include/common/Menu.h new file mode 100644 index 0000000..ae8c24c --- /dev/null +++ b/components/insa/include/common/Menu.h @@ -0,0 +1,232 @@ +/** + * @file Menu.h + * @brief Menu widget class for creating interactive menu systems + * @details This header defines the Menu class which extends the Widget base class + * to provide a comprehensive, customizable menu system supporting various + * types of interactive menu items including text buttons, selections, + * number inputs, and toggles. The menu supports navigation with directional + * input and provides visual feedback through selection highlighting and scrollbars. + * @author System Control Team + * @date 2025-06-14 + */ + +#pragma once + +#include "Common.h" +#include "MenuOptions.h" +#include "Widget.h" +#include "data/MenuItem.h" + +/** + * @class Menu + * @brief A comprehensive menu widget class for interactive user interfaces + * @details This class extends the Widget base class to provide a customizable menu system + * with support for various types of interactive menu items. It handles user input + * through directional navigation and action buttons, provides visual feedback + * through selection highlighting, and supports scrolling for long menu lists. + * + * The menu system supports four types of menu items: + * - Text buttons: Simple selectable text items + * - Selection items: Dropdown/list selection with multiple options + * - Number inputs: Numeric value adjustment controls + * - Toggle items: Boolean on/off switches + * + * @note Menu items are identified by unique IDs and can be dynamically added + * after menu creation. + * + * @see Widget + * @see MenuItem + * @see menu_options_t + */ +class Menu : public Widget +{ +public: + /** + * @brief Constructs a new Menu instance with the specified configuration + * @param options Pointer to menu configuration options structure + * + * @pre options must not be nullptr and must remain valid for the menu's lifetime + * @post Menu is initialized with the provided configuration and ready for item addition + * + * @note The menu does not take ownership of the options structure and assumes + * it remains valid throughout the menu's lifetime. + */ + explicit Menu(menu_options_t *options); + + /** + * @brief Destructor - Cleans up resources when menu is destroyed + * @details Properly releases any allocated resources and ensures clean shutdown + * of the menu system. + */ + ~Menu() override; + + /** + * @brief Adds a text-based menu item (button) to the menu + * @param id Unique identifier for this menu item (must be unique within the menu) + * @param text Display text shown on the menu item + * + * @pre id must be unique within this menu instance + * @post A new text menu item is added to the menu's item collection + * + * @note Text items act as buttons and generate selection events when activated + */ + void addText(uint8_t id, const std::string &text); + + /** + * @brief Adds a selection menu item (dropdown/list selection) to the menu + * @param id Unique identifier for this menu item (must be unique within the menu) + * @param text Display text/label for the selection item + * @param values Vector of all available options to choose from + * @param index Reference to current selected value (will be modified by user interaction) + * + * @pre id must be unique within this menu instance + * @pre values vector must not be empty + * @pre value must be one of the strings in the values vector + * @post A new selection menu item is added with the specified options + * + * @note The value parameter is modified directly when the user changes the selection + */ + void addSelection(uint8_t id, const std::string &text, const std::vector &values, int index); + + /** + * @brief Adds a toggle/checkbox menu item to the menu + * @param id Unique identifier for this menu item (must be unique within the menu) + * @param text Display text/label for the toggle item + * @param selected Current state of the toggle (true = on/enabled, false = off/disabled) + * + * @pre id must be unique within this menu instance + * @post A new toggle menu item is added with the specified initial state + * + * @note Toggle state can be changed through user interaction with select button + */ + void addToggle(uint8_t id, const std::string &text, bool selected); + +protected: + /** + * @brief Virtual callback method for handling button press events on specific menu items + * @param item The menu item that received the button press + * @param button The type of button that was pressed + * + * @details This method can be overridden by derived classes to implement custom + * button handling logic for specific menu items. The base implementation + * is empty, allowing derived classes to selectively handle events. + * + * @note Override this method in derived classes to implement custom menu item + * interaction behavior beyond the standard navigation and value modification. + */ + virtual void onButtonPressed(const MenuItem &item, const ButtonType button) + { + // Base implementation intentionally empty - override in derived classes as needed + } + + MenuItem getItem(int index); + + [[nodiscard]] size_t getItemCount() const; + + void setItemSize(size_t size); + + void replaceItem(int index, const MenuItem &item); + +private: + /** + * @brief Renders the entire menu on screen + * @details Override from Widget base class. Handles the complete rendering process + * including menu items, selection highlighting, and scroll indicators. + * + * @note This method is called during each frame's render cycle + */ + void render() override; + + /** + * @brief Handles button press events from the input system + * @param button The button that was pressed + * @details Override from Widget base class. Processes user input and delegates + * to appropriate handler methods based on button type. + * + * @see ButtonType for available button types + */ + void onButtonClicked(ButtonType button) override; + + // Navigation event handlers + /** + * @brief Handles down arrow/stick input - moves selection down in the menu + * @details Moves the current selection to the next menu item, wrapping to the + * beginning if at the end of the list. + */ + void onPressedDown(); + + /** + * @brief Handles up arrow/stick input - moves selection up in the menu + * @details Moves the current selection to the previous menu item, wrapping to the + * end if at the beginning of the list. + */ + void onPressedUp(); + + /** + * @brief Handles left arrow/stick input - decreases value for current item + * @details For selection items: moves to previous option in the list + * For number items: decreases the numeric value + * For other items: no action + */ + void onPressedLeft() const; + + /** + * @brief Handles right arrow/stick input - increases value for current item + * @details For selection items: moves to next option in the list + * For number items: increases the numeric value + * For other items: no action + */ + void onPressedRight() const; + + /** + * @brief Handles select/confirm button press + * @details Activates the currently selected menu item: + * - Text items: triggers selection event + * - Toggle items: toggles the boolean state + * - Other items: context-dependent behavior + */ + void onPressedSelect() const; + + /** + * @brief Handles back/cancel button press + * @details Typically used to exit the menu or return to a previous screen. + * The specific behavior depends on the menu configuration. + */ + void onPressedBack() const; + + // Rendering helper methods + /** + * @brief Draws the scroll bar indicating position in long menus + * @details Renders a visual scroll indicator when the menu contains more items + * than can be displayed on screen simultaneously. + */ + void drawScrollBar() const; + + /** + * @brief Draws the selection highlight box around current menu item + * @details Renders visual feedback showing which menu item is currently selected + * and will respond to user input. + */ + void drawSelectionBox() const; + + /** + * @brief Renders an individual menu item widget + * @param item Pointer to the menu item to render (must not be nullptr) + * @param font Font to use for rendering text + * @param x X coordinate for rendering position + * @param y Y coordinate for rendering position + * + * @pre item must not be nullptr + * @pre font must be a valid u8g2 font + * @pre x and y must be valid screen coordinates + * + * @details Handles the rendering of a single menu item based on its type, + * including text, current values, and any type-specific visual elements. + */ + void renderWidget(const MenuItem *item, const uint8_t *font, int x, int y) const; + + // Member variables + size_t m_selected_item = 0; ///< Index of currently selected menu item (0-based) + std::vector m_items; ///< Collection of all menu items in display order + menu_options_t *m_options; ///< Pointer to menu configuration options (not owned) +}; \ No newline at end of file diff --git a/components/insa/include/common/PSMenu.h b/components/insa/include/common/PSMenu.h deleted file mode 100644 index 052891b..0000000 --- a/components/insa/include/common/PSMenu.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include - -#include "Common.h" -#include "MenuOptions.h" -#include "Widget.h" -#include "data/MenuItem.h" - -/** - * PSMenu - A menu widget class - * - * This class extends the Widget base class to provide a customizable menu system - * with various types of interactive menu items including text buttons, selections, - * number inputs, and toggles. - */ -class PSMenu : public Widget -{ -public: - /** - * Constructor - Creates a new PSMenu instance - * @param options Pointer to menu configuration options - */ - explicit PSMenu(menu_options_t *options); - - /** - * Destructor - Cleans up resources when menu is destroyed - */ - ~PSMenu() override; - - /** - * Adds a text-based menu item (button) to the menu - * @param id Unique identifier for this menu item - * @param text Display text shown on the menu - * @param callback Function to call when this item is selected - */ - void addText(uint8_t id, const std::string &text, const ButtonCallback &callback); - - /** - * Adds a selection menu item (dropdown/list selection) - * @param id Unique identifier for this menu item - * @param text Display text/label for the selection - * @param value Reference to current selected value (will be modified) - * @param values Vector of all available options to choose from - * @param callback Function to call when selection changes - */ - void addSelection(uint8_t id, const std::string &text, std::string &value, const std::vector& values, - const ButtonCallback &callback); - - /** - * Adds a numeric input menu item - * @param id Unique identifier for this menu item - * @param text Display text/label for the number input - * @param value Reference to current numeric value as string (will be modified) - * @param callback Function to call when value changes - */ - void addNumber(uint8_t id, const std::string &text, std::string &value, const ButtonCallback &callback); - - /** - * Adds a toggle/checkbox menu item - * @param id Unique identifier for this menu item - * @param text Display text/label for the toggle - * @param selected Current state of the toggle (true = on, false = off) - * @param callback Function to call when toggle state changes - */ - void addToggle(uint8_t id, const std::string &text, bool selected, const ButtonCallback &callback); - -private: - /** - * Renders the entire menu on screen - * Override from Widget base class - */ - void render() override; - - /** - * Handles button press events from the controller/input system - * @param button The button that was pressed - * Override from Widget base class - */ - void onButtonClicked(uint8_t button) override; - - // Navigation event handlers - /** - * Handles down arrow/stick input - moves selection down - */ - void onPressedDown(); - - /** - * Handles up arrow/stick input - moves selection up - */ - void onPressedUp(); - - /** - * Handles left arrow/stick input - decreases value for current item - */ - void onPressedLeft() const; - - /** - * Handles right arrow/stick input - increases value for current item - */ - void onPressedRight() const; - - /** - * Handles select/confirm button (X on PlayStation controller) - */ - void onPressedSelect() const; - - /** - * Handles back/cancel button (Circle on PlayStation controller) - */ - void onPressedBack() const; - - // Rendering helper methods - /** - * Draws the scroll bar indicating position in long menus - */ - void drawScrollBar() const; - - /** - * Draws the selection highlight box around current menu item - */ - void drawSelectionBox() const; - - /** - * Renders an individual menu item widget - * @param item Pointer to the menu item to render - * @param font Font to use for rendering text - * @param x X coordinate for rendering position - * @param y Y coordinate for rendering position - */ - void renderWidget(const MenuItem *item, const uint8_t *font, int x, int y) const; - - // Member variables - size_t m_selected_item = 0; ///< Index of currently selected menu item - std::vector m_items; ///< Collection of all menu items - menu_options_t *m_options; ///< Pointer to menu configuration options -}; \ No newline at end of file diff --git a/components/insa/include/common/ScrollBar.h b/components/insa/include/common/ScrollBar.h index b6a91d3..bd7b833 100644 --- a/components/insa/include/common/ScrollBar.h +++ b/components/insa/include/common/ScrollBar.h @@ -1,52 +1,106 @@ +/** + * @file ScrollBar.h + * @brief Vertical scrollbar widget for indicating scroll position in long content + * @details This header defines the ScrollBar class which provides a visual scrollbar + * widget for indicating the current position within scrollable content. + * The scrollbar displays a thumb that moves proportionally to represent + * the current scroll position and visible area relative to the total content. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once #include "MenuOptions.h" #include "Widget.h" /** - * ScrollBar class that represents a vertical scrollbar widget - * Inherits from Widget base class and provides scrolling functionality + * @class ScrollBar + * @brief A vertical scrollbar widget that represents scroll position and range + * @details This final class inherits from Widget and provides visual feedback for + * scrollable content. It displays a vertical track with a movable thumb + * that indicates the current position within a scrollable range. The thumb + * size is proportional to the visible area relative to the total content, + * and its position reflects the current scroll offset. + * + * The scrollbar automatically calculates thumb dimensions and position based on + * the provided scroll values (current, minimum, maximum). It is designed to be + * used alongside scrollable content like menus or lists to provide visual + * feedback about scroll state. + * + * @note This class is marked as final and cannot be inherited from. + * + * @see Widget + * @see menu_options_t */ class ScrollBar final : public Widget { public: /** - * Constructor for ScrollBar - * @param options Pointer to menu options configuration - * @param x X coordinate position of the scrollbar - * @param y Y coordinate position of the scrollbar - * @param width Width of the scrollbar - * @param height Height of the scrollbar + * @brief Constructs a ScrollBar with specified position and dimensions + * @param options Pointer to menu options configuration structure + * @param x X coordinate position of the scrollbar on screen + * @param y Y coordinate position of the scrollbar on screen + * @param width Width of the scrollbar in pixels + * @param height Height of the scrollbar in pixels + * + * @pre options must not be nullptr and must remain valid for the scrollbar's lifetime + * @pre width and height must be greater than 0 + * @pre x and y must be valid screen coordinates + * @post ScrollBar is initialized with the specified geometry and ready for use + * + * @note The scrollbar does not take ownership of the options structure and + * assumes it remains valid throughout the scrollbar's lifetime. */ ScrollBar(const menu_options_t *options, size_t x, size_t y, size_t width, size_t height); /** - * Renders the scrollbar to the screen - * Overrides the base Widget render method + * @brief Renders the scrollbar to the screen + * @details Overrides the base Widget render method to draw the scrollbar track + * and thumb. The appearance is determined by the current scroll state + * and the menu options configuration. + * + * @pre u8g2 display context must be initialized and ready for drawing + * @post Scrollbar's visual representation is drawn to the display buffer + * + * @note This method is called during each frame's render cycle. The scrollbar + * track and thumb are drawn based on the current scroll values set by refresh(). */ void render() override; /** - * Updates the scrollbar state with new values - * @param value Current scroll position value - * @param max Maximum scroll value - * @param min Minimum scroll value (default: 0) + * @brief Updates the scrollbar state with new scroll values + * @param value Current scroll position value (must be between min and max) + * @param max Maximum scroll value (total content size) + * @param min Minimum scroll value (default: 0, typically the start of content) + * + * @pre value must be between min and max (inclusive) + * @pre max must be greater than or equal to min + * @post Scrollbar thumb position and size are recalculated based on new values + * + * @details This method recalculates the thumb's height and vertical position + * based on the provided scroll range and current position. The thumb + * height represents the proportion of visible content to total content, + * while its position represents the current scroll offset within the range. + * + * @note Call this method whenever the scroll state changes to keep the + * scrollbar visualization synchronized with the actual content position. */ void refresh(size_t value, size_t max, size_t min = 0); private: // Position and dimensions - size_t m_x; // X coordinate of the scrollbar - size_t m_y; // Y coordinate of the scrollbar - size_t m_width; // Width of the scrollbar - size_t m_height; // Height of the scrollbar + size_t m_x; ///< X coordinate of the scrollbar's left edge + size_t m_y; ///< Y coordinate of the scrollbar's top edge + size_t m_width; ///< Width of the scrollbar track in pixels + size_t m_height; ///< Height of the scrollbar track in pixels // Scroll state values - size_t m_value; // Current scroll position - size_t m_max; // Maximum scroll value - size_t m_min; // Minimum scroll value + size_t m_value; ///< Current scroll position within the range [m_min, m_max] + size_t m_max; ///< Maximum scroll value representing the end of content + size_t m_min; ///< Minimum scroll value representing the start of content - // Calculated thumb properties - size_t m_thumbHeight; // Height of the scroll thumb - size_t m_thumbY; // Y position of the scroll thumb + // Calculated thumb properties (updated by refresh()) + size_t m_thumbHeight; ///< Calculated height of the scroll thumb in pixels + size_t m_thumbY; ///< Calculated Y position of the scroll thumb relative to track }; \ No newline at end of file diff --git a/components/insa/include/common/Widget.h b/components/insa/include/common/Widget.h index c010852..05a457b 100644 --- a/components/insa/include/common/Widget.h +++ b/components/insa/include/common/Widget.h @@ -1,52 +1,114 @@ +/** + * @file Widget.h + * @brief Base widget class for UI components in the INSA system + * @details This header defines the Widget base class that serves as the foundation + * for all UI components in the system. It provides a standardized interface + * for rendering, updating, and handling user input using the u8g2 graphics library. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once #include "u8g2.h" +#include "common/Common.h" + /** - * Base class for UI widgets that can be rendered and interact with user input. - * This class provides a common interface for all widgets in the system. + * @class Widget + * @brief Base class for UI widgets that can be rendered and interact with user input + * @details This abstract base class provides a common interface for all widgets in the system. + * It manages the u8g2 display context and defines the core methods that all widgets + * must implement or can override. The class follows the template method pattern, + * allowing derived classes to customize behavior while maintaining a consistent + * interface for the UI system. + * + * @note All widgets should inherit from this class to ensure compatibility with + * the UI management system. + * + * @see u8g2_t + * @see ButtonType */ class Widget { public: /** - * Constructs a widget with the given u8g2 display context. - * @param u8g2 Pointer to the u8g2 display context used for rendering + * @brief Constructs a widget with the given u8g2 display context + * @param u8g2 Pointer to the u8g2 display context used for rendering operations + * + * @pre u8g2 must not be nullptr + * @post Widget is initialized with the provided display context + * + * @note The widget does not take ownership of the u8g2 context and assumes + * it remains valid for the lifetime of the widget. */ explicit Widget(u8g2_t *u8g2); /** - * Virtual destructor to ensure proper cleanup of derived classes. + * @brief Virtual destructor to ensure proper cleanup of derived classes + * @details Ensures that derived class destructors are called correctly when + * a widget is destroyed through a base class pointer. */ virtual ~Widget() = default; /** - * Updates the widget's internal state based on elapsed time. - * This method is called once per frame to handle animations, - * timers, or other time-dependent behavior. - * @param dt Delta time in milliseconds since last update + * @brief Updates the widget's internal state based on elapsed time + * @param dt Delta time in milliseconds since the last update call + * + * @details This method is called once per frame by the UI system to handle + * animations, timers, state transitions, or other time-dependent behavior. + * The base implementation is empty, allowing derived classes to override + * only if time-based updates are needed. + * + * @note Override this method in derived classes to implement time-based behavior + * such as animations, blinking effects, or timeout handling. */ virtual void update(uint64_t dt); /** - * Renders the widget to the display. - * This method should be overridden by derived classes to implement - * their specific rendering logic using the u8g2 display context. + * @brief Renders the widget to the display + * @details This method should be overridden by derived classes to implement + * their specific rendering logic using the u8g2 display context. + * The base implementation is empty, requiring derived classes to + * provide their own rendering code. + * + * @pre u8g2 context must be initialized and ready for drawing operations + * @post Widget's visual representation is drawn to the display buffer + * + * @note This method is called during the rendering phase of each frame. + * Derived classes should use the u8g2 member variable to perform + * drawing operations. */ virtual void render(); /** - * Handles button click events. - * This method is called when a button is pressed and allows - * the widget to respond to user input. - * @param button The identifier of the button that was clicked + * @brief Handles button press events + * @param button The type of button that was pressed + * + * @details This method is called when a button press event occurs and allows + * the widget to respond to user input. The base implementation is empty, + * allowing derived classes to override only if input handling is needed. + * + * @note Override this method in derived classes to implement button-specific + * behavior such as navigation, selection, or state changes. + * + * @see ButtonType for available button types */ - virtual void onButtonClicked(uint8_t button); + virtual void onButtonClicked(ButtonType button); protected: /** - * Pointer to the u8g2 display context used for rendering operations. - * This provides access to drawing functions for text, graphics, and other UI elements. + * @brief Pointer to the u8g2 display context used for rendering operations + * @details This member provides access to the u8g2 graphics library functions + * for drawing text, shapes, bitmaps, and other UI elements. It is + * initialized during construction and remains valid for the widget's lifetime. + * + * @note This member is protected to allow derived classes direct access while + * preventing external modification. Derived classes should use this + * context for all rendering operations. + * + * @warning Do not modify or delete this pointer. The widget does not own + * the u8g2 context and assumes it is managed externally. */ u8g2_t *u8g2; }; \ No newline at end of file diff --git a/components/insa/include/data/MenuItem.h b/components/insa/include/data/MenuItem.h index 7fa21fc..8ebeaa9 100644 --- a/components/insa/include/data/MenuItem.h +++ b/components/insa/include/data/MenuItem.h @@ -1,107 +1,337 @@ +/** + * @file MenuItem.h + * @brief Menu item data structure for user interface menu systems + * @details This header defines the MenuItem class which represents individual menu + * items that can be displayed and interacted with in user interface menus. + * It supports various types of menu items including simple buttons, toggles, + * value selectors, and multi-option selections with flexible callback handling. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once #include #include +#include #include "common/Common.h" /** - * Represents a menu item that can be displayed in a user interface. - * Supports different types of menu items with various interaction capabilities. + * @class MenuItem + * @brief Flexible menu item class supporting various interaction types and behaviors + * @details This class represents individual menu items that can be displayed in user + * interface menus. It provides a flexible foundation for different types of + * menu interactions including simple navigation buttons, toggle switches, + * value adjustments, and multi-option selections. + * + * The MenuItem class supports multiple interaction patterns: + * - **Simple Actions**: Basic menu items that execute a function when activated + * - **Value Display**: Items that show a current value (read-only or editable) + * - **Selection Lists**: Items that cycle through multiple predefined values + * - **Toggle States**: Boolean items that can be switched on/off + * - **Custom Behaviors**: Flexible callback system for specialized interactions + * + * Each menu item is identified by a unique ID and has a type that defines its + * visual appearance and interaction behavior. The callback system allows for + * flexible event handling while maintaining type safety through std::function. + * + * Key features include: + * - Multiple constructor overloads for different menu item types + * - Type-safe callback system with ButtonCallback function objects + * - Support for both single values and value lists + * - Flexible text and value management + * - Efficient state management and validation + * + * @note This class is designed to be lightweight and efficient for embedded + * systems while providing rich functionality for complex user interfaces. + * + * @see ButtonCallback for callback function signature + * @see ButtonType for available button types + * @see Menu for menu container functionality */ class MenuItem { public: /** - * Constructor for a simple menu item with text and callback. - * @param id Unique identifier for this menu item - * @param type Type of the menu item (defines behavior and appearance) - * @param text Display text for the menu item + * @brief Constructs a simple action menu item with text and callback + * @param id Unique identifier for this menu item within its parent menu + * @param type Type identifier defining the item's behavior and visual appearance + * @param text Display text shown to the user for this menu item * @param callback Function to call when the item is activated + * + * @pre id must be unique within the parent menu context + * @pre text should not be empty for proper user interface display + * @pre callback should be a valid callable object + * @post MenuItem is initialized as a simple action item ready for display + * + * @details Creates a basic menu item that displays text and executes a callback + * when activated. This is the most common type of menu item used for + * navigation, simple actions, and command execution. + * + * Typical use cases include: + * - Navigation items (e.g., "Settings", "Back", "Exit") + * - Action items (e.g., "Save", "Reset", "Start") + * - Sub-menu entries (e.g., "Light Control", "System Info") + * + * @note The callback is stored as a std::function and can be a lambda, + * function pointer, or any callable object matching the ButtonCallback signature. */ MenuItem(uint8_t id, uint8_t type, std::string text, ButtonCallback callback); - + /** - * Constructor for a menu item with a value field. - * @param id Unique identifier for this menu item - * @param type Type of the menu item - * @param text Display text for the menu item - * @param value Current value associated with this item + * @brief Constructs a value-displaying menu item with text, value, and callback + * @param id Unique identifier for this menu item within its parent menu + * @param type Type identifier defining the item's behavior and visual appearance + * @param text Display text shown to the user for this menu item + * @param value Current value associated with this item (displayed to user) * @param callback Function to call when the item is activated + * + * @pre id must be unique within the parent menu context + * @pre text should not be empty for proper user interface display + * @pre callback should be a valid callable object + * @post MenuItem is initialized with text and value display capabilities + * + * @details Creates a menu item that displays both text and a current value. + * This type is commonly used for settings display, status information, + * or items where the current state needs to be visible to the user. + * + * Typical use cases include: + * - Setting displays (e.g., "Brightness: 75%") + * - Status information (e.g., "Connection: WiFi") + * - Editable values (e.g., "Timeout: 30s") + * - Current selections (e.g., "Mode: Auto") + * + * @note The value can be updated later using setValue() to reflect changes + * in the underlying system state. */ MenuItem(uint8_t id, uint8_t type, std::string text, std::string value, ButtonCallback callback); - + /** - * Constructor for a menu item with multiple selectable values. - * @param id Unique identifier for this menu item - * @param type Type of the menu item - * @param text Display text for the menu item - * @param value Currently selected value - * @param values List of all available values for selection + * @brief Constructs a multi-selection menu item with selectable values + * @param id Unique identifier for this menu item within its parent menu + * @param type Type identifier defining the item's behavior and visual appearance + * @param text Display text shown to the user for this menu item + * @param values List of all available values that can be selected + * @param index Currently selected value from the available options * @param callback Function to call when the item is activated + * + * @pre id must be unique within the parent menu context + * @pre text should not be empty for proper user interface display + * @pre value should be present in the values vector + * @pre values should not be empty and should contain valid options + * @pre callback should be a valid callable object + * @post MenuItem is initialized with multiple selectable values + * + * @details Creates a menu item that allows selection from multiple predefined + * values. This type enables cycling through options or displaying + * selection dialogs, making it ideal for configuration settings + * with discrete choices. + * + * Typical use cases include: + * - Mode selection (e.g., "Display Mode: [Day, Night, Auto]") + * - Configuration options (e.g., "Language: [English, Deutsch, Français]") + * - Preset selection (e.g., "Profile: [Home, Office, Travel]") + * - Format selection (e.g., "Time Format: [12H, 24H]") + * + * @note The callback can implement cycling logic to move through the values + * or open a selection dialog for user choice. + * @note The values vector is stored by copy, so modifications to the original + * vector after construction do not affect the menu item. */ - MenuItem(uint8_t id, uint8_t type, std::string text, std::string value, std::vector values, + MenuItem(uint8_t id, uint8_t type, std::string text, std::vector values, int index, ButtonCallback callback); - + /** - * Constructor for a boolean/toggle menu item. - * @param id Unique identifier for this menu item - * @param type Type of the menu item - * @param text Display text for the menu item - * @param selected Whether this item is currently selected/checked + * @brief Constructs a boolean/toggle menu item with on/off state + * @param id Unique identifier for this menu item within its parent menu + * @param type Type identifier defining the item's behavior and visual appearance + * @param text Display text shown to the user for this menu item + * @param selected Whether this item is currently selected/enabled/checked * @param callback Function to call when the item is activated + * + * @pre id must be unique within the parent menu context + * @pre text should not be empty for proper user interface display + * @pre callback should be a valid callable object + * @post MenuItem is initialized as a boolean toggle item + * + * @details Creates a menu item that represents a boolean state (on/off, enabled/disabled, + * checked/unchecked). This type is ideal for settings that have binary states + * and need to show their current status visually. + * + * Typical use cases include: + * - Feature toggles (e.g., "Auto-save: ON") + * - Enable/disable settings (e.g., "Sound: ENABLED") + * - Checkbox-style options (e.g., "Show notifications: ✓") + * - Boolean configurations (e.g., "Dark mode: OFF") + * + * @note The selected state is converted to a string value internally for + * consistent value handling across all menu item types. + * @note The callback typically implements toggle logic to switch between states. */ MenuItem(uint8_t id, uint8_t type, std::string text, bool selected, ButtonCallback callback); - + /** - * Gets the unique identifier of this menu item. - * @return The menu item's ID + * @brief Gets the unique identifier of this menu item + * @return The menu item's unique ID as assigned during construction + * + * @details Returns the unique identifier that distinguishes this menu item + * from others within the same menu context. This ID is used by + * menu systems for item identification, event routing, and state management. + * + * @note The ID is immutable after construction and guaranteed to be unique + * within the menu context where this item is used. */ [[nodiscard]] uint8_t getId() const; - + /** - * Gets the type of this menu item. - * @return The menu item's type + * @brief Gets the type identifier of this menu item + * @return The menu item's type identifier defining its behavior + * + * @details Returns the type identifier that defines how this menu item + * behaves and appears in the user interface. The type determines + * rendering style, interaction patterns, and event handling behavior. + * + * @note The type is immutable after construction and should correspond + * to predefined menu item type constants defined in the system. */ [[nodiscard]] uint8_t getType() const; - + /** - * Gets the display text of this menu item. - * @return Reference to the menu item's text + * @brief Gets the display text of this menu item + * @return Const reference to the menu item's display text + * + * @details Returns the text that is displayed to the user for this menu item. + * This text serves as the primary label and should be descriptive + * enough for users to understand the item's purpose. + * + * @note Returns a const reference for efficiency while preventing + * accidental modification of the text content. */ [[nodiscard]] const std::string &getText() const; - + /** - * Gets the current value of this menu item. - * @return Reference to the menu item's current value + * @brief Gets the current value of this menu item + * @return Const reference to the menu item's current value + * + * @details Returns the current value associated with this menu item. + * For simple action items, this may be empty. For value-based + * items, this represents the current state, selection, or setting. + * + * @note Returns a const reference for efficiency while preventing + * accidental modification through the getter. + * @note For boolean items, the value is typically "true"/"false" or similar. */ [[nodiscard]] const std::string &getValue() const; - + /** - * Sets a new value for this menu item. - * @param value The new value to set + * @brief Sets a new value for this menu item + * @param value The new value to assign to this menu item + * + * @details Updates the current value of this menu item. This is commonly + * used to reflect changes in system state, user selections, or + * configuration updates. The new value should be appropriate + * for the menu item's type and purpose. + * + * @note For multi-selection items, the value should be one of the + * predefined values in the values vector. + * @note For boolean items, typical values are "true"/"false", "ON"/"OFF", + * or other boolean representations. + * @note The value update does not automatically trigger the callback; + * this is purely for state management. */ void setValue(const std::string &value); - + /** - * Handles button press events for this menu item. - * Calls the associated callback function if one exists. - * @param id The ID of the item that was pressed + * @brief Handles button press events for this menu item * @param button The type of button that was pressed + * + * @details Processes button press events by invoking the associated callback + * function if one exists. This method serves as the event handler + * that connects user interactions to the menu item's functionality. + * + * The method performs the following actions: + * - Validates that the ID matches this menu item + * - Checks if a callback function is available + * - Invokes the callback with the provided button type + * - Handles any callback-related error conditions gracefully + * + * @note This method is typically called by the parent menu system when + * user interaction occurs on this menu item. + * @note If no callback is set, the method returns without error. + * @note The callback is responsible for implementing the specific behavior + * for the button press (navigation, value changes, actions, etc.). */ - void onButtonPressed(uint8_t id, ButtonType button) const; - + void onButtonPressed(ButtonType button) const; + /** - * Checks if this menu item has an associated callback function. - * @return true if a callback is set, false otherwise + * @brief Checks if this menu item has an associated callback function + * @return true if a callback function is set, false otherwise + * + * @details Determines whether this menu item has a valid callback function + * that can be invoked when the item is activated. This is useful + * for menu systems that need to distinguish between interactive + * and non-interactive items. + * + * @note Menu items without callbacks are typically used for display-only + * purposes such as headers, separators, or status information. + * @note Interactive menu items should always have callbacks to provide + * meaningful user interaction. */ [[nodiscard]] bool hasCallback() const; + [[nodiscard]] int getIndex() const; + + std::vector getValues() const; + + [[nodiscard]] size_t getItemCount() const; + + [[nodiscard]] MenuItem copyWith(size_t index) const; + private: - uint8_t m_id; // Unique identifier for this menu item - uint8_t m_type; // Type defining the item's behavior - std::string m_text; // Display text shown to the user - std::string m_value; // Current value (for value-based items) - std::vector m_values; // Available values (for selection items) - ButtonCallback m_callback; // Function to call when activated + /** + * @brief Unique identifier for this menu item + * @details Stores the unique ID that distinguishes this menu item from others + * within the same menu context. Used for event routing and item identification. + */ + uint8_t m_id; + + /** + * @brief Type identifier defining the item's behavior and appearance + * @details Stores the type that determines how this menu item behaves, + * how it's rendered, and what interaction patterns it supports. + */ + uint8_t m_type; + + /** + * @brief Display text shown to the user + * @details Stores the primary text label that is displayed to users, + * serving as the main identifier and description of the menu item's purpose. + */ + std::string m_text; + + /** + * @brief Current value associated with this menu item + * @details Stores the current value for value-based menu items, representing + * the current state, selection, or setting that should be displayed to the user. + */ + std::string m_value; + + /** + * @brief Available values for selection-based menu items + * @details Stores the list of all possible values that can be selected for + * multi-option menu items. Used for cycling through options or + * displaying selection dialogs. + */ + std::vector m_values; + + int m_index = -1; + + /** + * @brief Callback function invoked when the menu item is activated + * @details Stores the function object that implements the menu item's behavior + * when activated by user interaction. Uses std::function for flexibility + * and type safety. + */ + ButtonCallback m_callback; }; \ No newline at end of file diff --git a/components/insa/include/ui/LightMenu.h b/components/insa/include/ui/LightMenu.h index ae4f70c..e53d84b 100644 --- a/components/insa/include/ui/LightMenu.h +++ b/components/insa/include/ui/LightMenu.h @@ -1,14 +1,141 @@ +/** + * @file LightMenu.h + * @brief Light control menu implementation for lighting system management + * @details This header defines the LightMenu class which provides a specialized + * user interface for controlling lighting systems and illumination features. + * It extends the Menu base class to offer comprehensive light control + * functionality including brightness adjustment, color selection, and + * lighting mode configuration. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once -#include "common/PSMenu.h" +#include "common/Menu.h" -class LightMenu final : public PSMenu +/** + * @class LightMenu + * @brief Light control menu interface class for illumination system management + * @details This final class inherits from Menu and provides a comprehensive interface + * for controlling various aspects of the lighting system. It allows users to + * adjust brightness levels, select colors, configure lighting modes, and + * manage automated lighting behaviors. + * + * The LightMenu class extends the base Menu functionality by: + * - Providing light-specific control options (brightness, color, modes) + * - Implementing real-time lighting preview and feedback + * - Managing lighting presets and custom configurations + * - Handling immediate lighting adjustments and scheduled operations + * + * Typical lighting control features include: + * - Brightness adjustment (0-100% or similar range) + * - Color selection (RGB, HSV, or predefined colors) + * - Lighting modes (solid, fade, strobe, custom patterns) + * - Timer-based automation (on/off schedules) + * - Preset management (save/load favorite configurations) + * - Zone-based control (if multiple light zones are supported) + * + * The menu provides immediate visual feedback by applying changes to the + * connected lighting hardware in real-time as users navigate through options. + * + * @note This class is marked as final and cannot be inherited from. + * @note Lighting changes are typically applied immediately for instant feedback, + * with the option to save configurations as presets. + * + * @see Menu for base menu functionality + * @see menu_options_t for configuration structure + * @see MainMenu for navigation back to main interface + */ +class LightMenu final : public Menu { - public: +public: + /** + * @brief Constructs the light control menu with the specified configuration + * @param options Pointer to menu options configuration structure + * + * @pre options must not be nullptr and must remain valid for the menu's lifetime + * @pre options->u8g2 must be initialized and ready for graphics operations + * @pre All callback functions in options must be properly configured + * @pre Lighting hardware must be initialized and responsive + * @post LightMenu is initialized with current lighting state and ready for user interaction + * + * @details The constructor initializes the light control menu by: + * - Reading current lighting system state and parameters + * - Creating appropriate menu items for available lighting features + * - Setting up real-time preview capabilities + * - Loading saved lighting presets and configurations + * - Configuring value ranges and validation for lighting parameters + * + * The menu automatically detects available lighting capabilities and presents + * only the controls that are supported by the connected hardware. This ensures + * a consistent user experience across different lighting system configurations. + * + * @note The menu does not take ownership of the options structure and assumes + * it remains valid throughout the menu's lifetime. + * @note Current lighting state is preserved and can be restored if the user + * exits without saving changes. + * + * @see Menu::Menu for base class construction details + */ explicit LightMenu(menu_options_t *options); private: - void onButtonPressed(uint8_t id, ButtonType button) const; + /** + * @brief Handles button press events specific to light control menu items + * @param menuItem + * @param button Type of button that was pressed + * + * @details Overrides the base Menu class method to provide light control-specific + * button handling logic. This method processes user interactions with + * lighting control items and performs appropriate actions such as: + * - Adjusting brightness levels (increment/decrement) + * - Changing color values or selecting predefined colors + * - Switching between lighting modes and patterns + * - Saving/loading lighting presets + * - Toggling lighting zones on/off + * - Applying lighting changes immediately to hardware + * + * The method provides real-time feedback by immediately applying lighting + * changes to the connected hardware, allowing users to see the effects of + * their adjustments instantly. It also handles validation to ensure that + * lighting parameters remain within safe and supported ranges. + * + * Special handling includes: + * - Smooth transitions for brightness adjustments + * - Color wheel navigation for color selection + * - Mode cycling for lighting patterns + * - Confirmation prompts for preset operations + * + * @note This method is called by the base Menu class when a button press + * occurs on a menu item, after the base class has handled standard + * navigation operations. + * @note Changes are applied immediately to provide instant visual feedback, + * but can be reverted if the user cancels or exits without saving. + * + * @see Menu::onButtonPressed for the base implementation + * @see ButtonType for available button types + */ + void onButtonPressed(const MenuItem& menuItem, ButtonType button) override; + /** + * @brief Pointer to menu options configuration structure + * @details Stores a reference to the menu configuration passed during construction. + * This pointer provides access to the display context and callback functions + * needed for menu operations, screen transitions, and lighting control + * communication with the hardware subsystem. + * + * The configuration enables: + * - Display context for rendering lighting control interface + * - Screen management callbacks for navigation to other menus + * - Hardware communication for real-time lighting control + * - System callbacks for lighting state persistence + * + * @note This pointer is not owned by the LightMenu and must remain valid + * throughout the menu's lifetime. It is managed by the application framework. + * + * @warning Must not be modified after construction as it may be shared + * with other components and contains critical system callbacks. + */ menu_options_t *m_options; -}; +}; \ No newline at end of file diff --git a/components/insa/include/ui/LightSettingsMenu.h b/components/insa/include/ui/LightSettingsMenu.h new file mode 100644 index 0000000..f6c0d3a --- /dev/null +++ b/components/insa/include/ui/LightSettingsMenu.h @@ -0,0 +1,14 @@ +#pragma once + +#include "common/Menu.h" + +class LightSettingsMenu final : public Menu +{ +public: + explicit LightSettingsMenu(menu_options_t *options); + +private: + void onButtonPressed(const MenuItem& menuItem, ButtonType button) override; + + menu_options_t *m_options; +}; \ No newline at end of file diff --git a/components/insa/include/ui/MainMenu.h b/components/insa/include/ui/MainMenu.h index 7b962b6..14ac6f6 100644 --- a/components/insa/include/ui/MainMenu.h +++ b/components/insa/include/ui/MainMenu.h @@ -1,31 +1,104 @@ +/** + * @file MainMenu.h + * @brief Main menu implementation for the application's primary interface + * @details This header defines the MainMenu class which serves as the primary + * user interface entry point for the application. It extends the Menu + * base class to provide a customized main menu experience with + * application-specific menu items and navigation behavior. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once -#include "common/PSMenu.h" +#include "common/Menu.h" /** - * MainMenu class - represents the main menu interface of the application - * Inherits from PSMenu to provide menu functionality + * @class MainMenu + * @brief Main menu interface class providing the primary application navigation + * @details This final class inherits from Menu and represents the main menu interface + * of the application. It serves as the primary entry point for user interaction + * and provides navigation to all major application features and sub-menus. + * + * The MainMenu class customizes the base Menu functionality by: + * - Defining application-specific menu items during construction + * - Implementing custom button handling for main menu navigation + * - Managing transitions to sub-menus and application features + * + * This class is typically the first screen presented to users after the splash + * screen and serves as the central hub for all application functionality. + * + * @note This class is marked as final and cannot be inherited from. + * + * @see Menu for base menu functionality + * @see menu_options_t for configuration structure */ -class MainMenu final : public PSMenu +class MainMenu final : public Menu { public: /** - * Constructor - initializes the main menu with given options - * @param options Pointer to menu options configuration + * @brief Constructs the main menu with the specified configuration + * @param options Pointer to menu options configuration structure + * + * @pre options must not be nullptr and must remain valid for the menu's lifetime + * @pre options->u8g2 must be initialized and ready for graphics operations + * @pre All callback functions in options must be properly configured + * @post MainMenu is initialized with application-specific menu items and ready for use + * + * @details The constructor initializes the main menu by setting up all the + * primary application menu items such as: + * - Settings access + * - Feature-specific menus + * - System controls + * - Application exit options + * + * @note The menu does not take ownership of the options structure and assumes + * it remains valid throughout the menu's lifetime. */ explicit MainMenu(menu_options_t *options); private: /** - * Handles button press events from the menu interface - * @param id Button identifier that was pressed + * @brief Handles button press events specific to main menu items + * @param menuItem * @param button Type of button that was pressed + * + * @details Overrides the base Menu class method to provide main menu-specific + * button handling logic. This method processes user interactions with + * main menu items and initiates appropriate actions such as: + * - Navigation to sub-menus (Settings, Light Control, etc.) + * - Direct feature activation + * - System operations + * + * The method uses the menu item ID to determine which action to take and + * utilizes the callback functions provided in m_options to perform screen + * transitions or other application-level operations. + * + * @note This method is called by the base Menu class when a button press + * occurs on a menu item, after the base class has handled standard + * navigation operations. + * + * @see Menu::onButtonPressed for the base implementation + * @see ButtonType for available button types */ - void onButtonPressed(uint8_t id, ButtonType button) const; + void onButtonPressed(const MenuItem& menuItem, ButtonType button) override; /** - * Pointer to menu options configuration - * Stores the menu configuration passed during construction + * @brief Pointer to menu options configuration structure + * @details Stores a reference to the menu configuration passed during construction. + * This pointer provides access to the display context and callback functions + * needed for menu operations, screen transitions, and user interaction handling. + * + * The configuration includes: + * - Display context for rendering operations + * - Screen management callbacks for navigation + * - Input handling callbacks for button events + * + * @note This pointer is not owned by the MainMenu and must remain valid + * throughout the menu's lifetime. It is managed by the application framework. + * + * @warning Must not be modified after construction as it may be shared + * with other components. */ menu_options_t *m_options; }; \ No newline at end of file diff --git a/components/insa/include/ui/SettingsMenu.h b/components/insa/include/ui/SettingsMenu.h index 752ff09..1745b47 100644 --- a/components/insa/include/ui/SettingsMenu.h +++ b/components/insa/include/ui/SettingsMenu.h @@ -1,9 +1,77 @@ +/** + * @file SettingsMenu.h + * @brief Settings menu implementation for application configuration + * @details This header defines the SettingsMenu class which provides a user interface + * for configuring application settings and preferences. It extends the Menu + * base class to offer a specialized settings management interface with + * various configuration options and system parameters. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once -#include "common/PSMenu.h" +#include "common/Menu.h" -class SettingsMenu final : public PSMenu +/** + * @class SettingsMenu + * @brief Settings menu interface class for application configuration management + * @details This final class inherits from Menu and provides a comprehensive settings + * interface for the application. It allows users to configure various aspects + * of the system including display preferences, system behavior, network + * settings, and other configurable parameters. + * + * The SettingsMenu class extends the base Menu functionality by: + * - Providing settings-specific menu items (toggles, selections, number inputs) + * - Managing configuration persistence and validation + * - Organizing settings into logical categories and sections + * - Implementing real-time preview of setting changes where applicable + * + * Typical settings categories include: + * - Display settings (brightness, contrast, theme) + * - System preferences (timeouts, auto-save, etc.) + * - Network configuration (if applicable) + * - User interface preferences + * - Hardware-specific parameters + * + * @note This class is marked as final and cannot be inherited from. + * @note Settings changes are typically applied immediately or after confirmation, + * depending on the specific setting type and system requirements. + * + * @see Menu for base menu functionality + * @see menu_options_t for configuration structure + * @see MainMenu for navigation back to main interface + */ +class SettingsMenu final : public Menu { - public: +public: + /** + * @brief Constructs the settings menu with the specified configuration + * @param options Pointer to menu options configuration structure + * + * @pre options must not be nullptr and must remain valid for the menu's lifetime + * @pre options->u8g2 must be initialized and ready for graphics operations + * @pre All callback functions in options must be properly configured + * @post SettingsMenu is initialized with all available configuration options and ready for use + * + * @details The constructor initializes the settings menu by creating all the + * configuration menu items based on the current system state and + * available options. This includes: + * - Loading current setting values from persistent storage + * - Creating appropriate menu items (toggles, selections, number inputs) + * - Setting up validation ranges and allowed values + * - Organizing items in a logical, user-friendly order + * + * The menu automatically detects which settings are available based on + * hardware capabilities and system configuration, ensuring only relevant + * options are presented to the user. + * + * @note The menu does not take ownership of the options structure and assumes + * it remains valid throughout the menu's lifetime. + * @note Setting values are loaded from persistent storage during construction + * and changes are typically saved automatically or on confirmation. + * + * @see Menu::Menu for base class construction details + */ explicit SettingsMenu(menu_options_t *options); -}; +}; \ No newline at end of file diff --git a/components/insa/include/ui/SplashScreen.h b/components/insa/include/ui/SplashScreen.h index f3c90bc..034d2b4 100644 --- a/components/insa/include/ui/SplashScreen.h +++ b/components/insa/include/ui/SplashScreen.h @@ -1,50 +1,177 @@ +/** + * @file SplashScreen.h + * @brief Application splash screen implementation for startup presentation + * @details This header defines the SplashScreen class which provides the initial + * visual presentation when the application starts. It serves as a loading + * screen that displays branding information, initialization progress, and + * provides visual feedback during the application startup sequence. + * @author System Control Team + * @date 2025-06-14 + */ + #pragma once #include "MenuOptions.h" #include "common/Widget.h" /** - * @brief SplashScreen class represents the initial screen shown when the application starts + * @class SplashScreen + * @brief Application startup screen widget with branding and initialization feedback + * @details This final class inherits from Widget and represents the initial screen + * displayed when the application starts. It serves multiple purposes including + * brand presentation, system initialization feedback, and smooth transition + * preparation to the main application interface. * - * This class inherits from Widget and is responsible for displaying the splash screen - * that typically shows application branding, loading status, or initialization messages. - * It's marked as final to prevent further inheritance. + * The SplashScreen class provides: + * - Brand identity display (logos, company information, product name) + * - System initialization progress indication + * - Smooth animations and visual effects for professional appearance + * - Automatic transition timing to main menu after initialization + * - Error indication if initialization fails + * + * Key features include: + * - Time-based automatic progression to main menu + * - Animated elements (fade-in effects, progress indicators) + * - Version information display + * - Loading status messages + * - Graceful handling of initialization delays + * + * The splash screen typically displays for a minimum duration to ensure users + * can read branding information, even if system initialization completes quickly. + * It automatically transitions to the main menu once both the minimum display + * time and system initialization are complete. + * + * @note This class is marked as final and cannot be inherited from. + * @note The splash screen does not handle user input - it operates on timing + * and system state rather than user interaction. + * + * @see Widget for base widget functionality + * @see menu_options_t for configuration structure + * @see MainMenu for the target transition screen */ class SplashScreen final : public Widget { - public: +public: /** - * @brief Constructs a new SplashScreen object + * @brief Constructs the splash screen with specified configuration + * @param options Pointer to menu options configuration structure * - * @param options Pointer to menu options configuration that controls - * the behavior and appearance of the splash screen + * @pre options must not be nullptr and must remain valid for the splash screen's lifetime + * @pre options->u8g2 must be initialized and ready for graphics operations + * @pre Screen transition callbacks in options must be properly configured + * @post SplashScreen is initialized and ready to display startup sequence + * + * @details The constructor initializes the splash screen by: + * - Setting up the initial display state and animations + * - Preparing branding elements (logos, text, version info) + * - Initializing timing controls for minimum display duration + * - Configuring transition parameters for smooth progression + * - Loading any required graphics resources or assets + * + * The splash screen prepares all visual elements during construction to + * ensure smooth rendering performance during the critical startup phase. + * It also establishes the timing framework for controlling display duration + * and transition timing. + * + * @note The splash screen does not take ownership of the options structure + * and assumes it remains valid throughout the screen's lifetime. + * @note Graphics resources are loaded during construction, so any required + * assets must be available at initialization time. + * + * @see Widget::Widget for base class construction details */ explicit SplashScreen(menu_options_t *options); /** - * @brief Updates the splash screen state and animations + * @brief Updates the splash screen state, animations, and timing logic + * @param dt Delta time in milliseconds since the last update call * - * This method is called every frame to update the splash screen's - * internal state, handle timing, animations, or transitions. + * @details Overrides the base Widget update method to handle splash screen-specific + * logic including: + * - Animation progression (fade effects, transitions, progress indicators) + * - Timing control for minimum display duration + * - System initialization status monitoring + * - Automatic transition preparation to main menu + * - Loading progress updates and status message changes * - * @param dt Delta time in milliseconds since the last update + * The update method manages the splash screen's lifecycle by tracking + * elapsed time and system readiness state. It ensures the splash screen + * remains visible for a minimum duration while also monitoring system + * initialization completion. Once both conditions are met, it initiates + * the transition to the main application interface. + * + * Animation updates include: + * - Fade-in effects for branding elements + * - Progress bar or spinner animations + * - Text transitions for status messages + * - Smooth preparation for screen transition + * + * @note This method is called every frame during the splash screen display + * and must be efficient to maintain smooth visual presentation. + * @note The method automatically handles transition to the main menu when + * appropriate, using the callback functions provided in m_options. + * + * @see Widget::update for the base update interface */ void update(uint64_t dt) override; /** - * @brief Renders the splash screen to the display + * @brief Renders the splash screen visual elements to the display * - * This method handles all the drawing operations for the splash screen, - * including background, logos, text, and any visual effects. + * @details Overrides the base Widget render method to draw all splash screen + * elements including branding, status information, and visual effects. + * The rendering includes: + * - Company/product logos and branding elements + * - Application name and version information + * - Loading progress indicators (progress bars, spinners, etc.) + * - Status messages indicating initialization progress + * - Background graphics and visual effects + * + * The render method creates a professional, polished appearance that + * reinforces brand identity while providing useful feedback about the + * application startup process. All elements are positioned and styled + * to create a cohesive, attractive presentation. + * + * Rendering features include: + * - Centered layout with balanced visual hierarchy + * - Smooth animations and transitions + * - Consistent typography and color scheme + * - Progress feedback elements + * - Error indication if initialization problems occur + * + * @pre u8g2 display context must be initialized and ready for drawing + * @post All splash screen visual elements are drawn to the display buffer + * + * @note This method is called every frame and must be efficient to maintain + * smooth visual presentation during the startup sequence. + * @note The visual design should be consistent with the overall application + * theme and branding guidelines. + * + * @see Widget::render for the base render interface */ void render() override; - private: +private: /** - * @brief Pointer to menu options configuration + * @brief Pointer to menu options configuration structure + * @details Stores a reference to the menu configuration passed during construction. + * This provides access to the display context for rendering operations + * and screen transition callbacks for automatic progression to the main menu. * - * Stores the configuration options that control various aspects - * of the splash screen's behavior and appearance. + * The configuration enables: + * - Display context (u8g2) for graphics rendering operations + * - Screen transition callbacks for automatic progression to main menu + * - System integration for initialization status monitoring + * + * The splash screen uses the setScreen callback to automatically transition + * to the main menu once the startup sequence is complete. This ensures a + * seamless user experience from application launch to main interface. + * + * @note This pointer is not owned by the SplashScreen and must remain valid + * throughout the screen's lifetime. It is managed by the application framework. + * + * @warning Must not be modified after construction as it contains critical + * system callbacks required for proper application flow. */ menu_options_t *m_options; }; \ No newline at end of file diff --git a/components/insa/src/common/Menu.cpp b/components/insa/src/common/Menu.cpp new file mode 100644 index 0000000..f6bcf6b --- /dev/null +++ b/components/insa/src/common/Menu.cpp @@ -0,0 +1,308 @@ +#include "common/Menu.h" + +#include "common/ScrollBar.h" +#include "u8g2.h" + +// Menu item type constants for better readability +namespace MenuItemTypes +{ +constexpr uint8_t TEXT = 0; +constexpr uint8_t SELECTION = 1; +constexpr uint8_t TOGGLE = 2; +} + +// UI layout constants +namespace UIConstants +{ +constexpr int LEFT_MARGIN = 8; +constexpr int RIGHT_PADDING = 8; +constexpr int SCROLLBAR_WIDTH = 3; +constexpr int FRAME_BOX_SIZE = 14; +constexpr int FRAME_OFFSET = 11; +constexpr int SELECTION_MARGIN = 10; +constexpr int CORNER_RADIUS = 3; +constexpr int LINE_SPACING = 14; +constexpr int BOTTOM_OFFSET = 10; +} + +Menu::Menu(menu_options_t *options) : Widget(options->u8g2), m_options(options) +{ + // Set up button callback using lambda to forward to member function + m_options->onButtonClicked = [this](const ButtonType button) { + onButtonClicked(button); + }; +} + +Menu::~Menu() +{ + // Clean up callback to prevent dangling pointer + if (m_options) + { + m_options->onButtonClicked = nullptr; + } +} + +MenuItem Menu::getItem(const int index) +{ + return m_items.at(index); +} + +size_t Menu::getItemCount() const +{ + return m_items.size(); +} + +void Menu::setItemSize(const size_t size) +{ + if ((m_items.size() - 1) < size) + { + for (size_t i = m_items.size() - 1; i < size; i++) + { + auto caption = std::string("Section ") + std::to_string(i + 1); + addSelection(i + 1, caption, m_items.at(0).getValues(), 0); + } + } + else + { + m_items.erase(m_items.begin() + static_cast(size + 1), m_items.end()); + } +} + +void Menu::replaceItem(const int index, const MenuItem &item) +{ + m_items.at(index) = item; +} + +void Menu::render() +{ + // Initialize selection if not set + if (m_selected_item >= m_items.size() && !m_items.empty()) + { + m_selected_item = 0; + } + + // Early return if no items to render + if (m_items.empty()) + { + return; + } + + // Clear screen with black background + u8g2_SetDrawColor(u8g2, 0); + u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height); + + // Set white foreground color for drawing + u8g2_SetDrawColor(u8g2, 1); + + // Draw UI components + drawScrollBar(); + drawSelectionBox(); + + // Calculate center position for main item + const int centerY = u8g2->height / 2 + 3; + + // Render the currently selected item (main/center item) + const auto &selectedItem = m_items[m_selected_item]; + renderWidget(&selectedItem, u8g2_font_helvB08_tr, UIConstants::LEFT_MARGIN, centerY); + + // Render previous item (above) if available + if (m_selected_item > 0) + { + const auto &prevItem = m_items[m_selected_item - 1]; + renderWidget(&prevItem, u8g2_font_haxrcorp4089_tr, UIConstants::LEFT_MARGIN, UIConstants::LINE_SPACING); + } + + // Render next item (below) if available + if (m_selected_item < m_items.size() - 1) + { + const auto &nextItem = m_items[m_selected_item + 1]; + renderWidget(&nextItem, u8g2_font_haxrcorp4089_tr, UIConstants::LEFT_MARGIN, + u8g2->height - UIConstants::BOTTOM_OFFSET); + } +} + +void Menu::renderWidget(const MenuItem *item, const uint8_t *font, const int x, const int y) const +{ + // Set font and draw main text + u8g2_SetFont(u8g2, font); + u8g2_DrawStr(u8g2, x, y, item->getText().c_str()); + + // Render type-specific elements + switch (item->getType()) + { + case MenuItemTypes::SELECTION: { + // Format selection value with angle brackets + const std::string formattedValue = "< " + item->getValue() + " >"; + const u8g2_uint_t textWidth = u8g2_GetStrWidth(u8g2, formattedValue.c_str()); + u8g2_DrawStr(u8g2, u8g2->width - textWidth - UIConstants::SELECTION_MARGIN, y, formattedValue.c_str()); + break; + } + + case MenuItemTypes::TOGGLE: { + // Draw checkbox frame + const int frameX = u8g2->width - UIConstants::FRAME_BOX_SIZE - UIConstants::SELECTION_MARGIN; + const int frameY = y - UIConstants::FRAME_OFFSET; + u8g2_DrawFrame(u8g2, frameX, frameY, UIConstants::FRAME_BOX_SIZE, UIConstants::FRAME_BOX_SIZE); + + // Draw checkmark (X) if toggle is true + if (item->getValue() == "true") + { + const int checkX1 = frameX + 2; + const int checkY1 = frameY + 2; + const int checkX2 = frameX + UIConstants::FRAME_BOX_SIZE - 3; + const int checkY2 = frameY + UIConstants::FRAME_BOX_SIZE - 3; + + // Draw X pattern for checked state + u8g2_DrawLine(u8g2, checkX1, checkY1, checkX2, checkY2); + u8g2_DrawLine(u8g2, checkX1, checkY2, checkX2, checkY1); + } + break; + } + + case MenuItemTypes::TEXT: + default: + // No additional rendering needed for text and number types + break; + } +} + +void Menu::onButtonClicked(const ButtonType button) +{ + // Map button input to navigation functions + switch (button) + { + case ButtonType::UP: + onPressedUp(); + break; + + case ButtonType::DOWN: + onPressedDown(); + break; + + case ButtonType::LEFT: + onPressedLeft(); + break; + + case ButtonType::RIGHT: + onPressedRight(); + break; + + case ButtonType::SELECT: + onPressedSelect(); + break; + + case ButtonType::BACK: + onPressedBack(); + break; + + default: + // Ignore unknown button inputs + break; + } +} + +void Menu::onPressedDown() +{ + if (m_items.empty()) + return; + + // Wrap around to first item when at the end + m_selected_item = (m_selected_item + 1) % m_items.size(); +} + +void Menu::onPressedUp() +{ + if (m_items.empty()) + return; + + // Wrap around to last item when at the beginning + m_selected_item = (m_selected_item == 0) ? m_items.size() - 1 : m_selected_item - 1; +} + +void Menu::onPressedLeft() const +{ + if (m_selected_item < m_items.size()) + { + const auto &item = m_items.at(m_selected_item); + item.onButtonPressed(ButtonType::LEFT); + } +} + +void Menu::onPressedRight() const +{ + if (m_selected_item < m_items.size()) + { + const auto &item = m_items.at(m_selected_item); + item.onButtonPressed(ButtonType::RIGHT); + } +} + +void Menu::onPressedSelect() const +{ + if (m_selected_item < m_items.size()) + { + const auto &item = m_items.at(m_selected_item); + item.onButtonPressed(ButtonType::SELECT); + } +} + +void Menu::onPressedBack() const +{ + // Navigate back to previous screen if callback is available + if (m_options && m_options->popScreen) + { + m_options->popScreen(); + } +} + +void Menu::addText(uint8_t id, const std::string &text) +{ + auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void { + onButtonPressed(menuItem, button); + }; + m_items.emplace_back(id, MenuItemTypes::TEXT, text, callback); +} + +void Menu::addSelection(uint8_t id, const std::string &text, const std::vector &values, const int index) +{ + auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void { + onButtonPressed(menuItem, button); + }; + m_items.emplace_back(id, MenuItemTypes::SELECTION, text, values, index, callback); +} + +void Menu::addToggle(uint8_t id, const std::string &text, bool selected) +{ + auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void { + onButtonPressed(menuItem, button); + }; + m_items.emplace_back(id, MenuItemTypes::TOGGLE, text, selected, callback); +} + +void Menu::drawScrollBar() const +{ + // Create scrollbar instance + ScrollBar scrollBar(m_options, u8g2->width - UIConstants::SCROLLBAR_WIDTH, 3, 1, u8g2->height - 6); + scrollBar.refresh(m_selected_item, m_items.size()); + scrollBar.render(); +} + +void Menu::drawSelectionBox() const +{ + // Calculate dimensions for the selection box + const auto displayHeight = u8g2->height; + const auto displayWidth = u8g2->width; + const auto boxHeight = displayHeight / 3; + const auto y = boxHeight * 2 - 2; + const auto x = displayWidth - UIConstants::RIGHT_PADDING; + + // Draw the rounded frame for the selection box + u8g2_DrawRFrame(u8g2, 2, boxHeight, displayWidth - UIConstants::RIGHT_PADDING, boxHeight, + UIConstants::CORNER_RADIUS); + + // Draw horizontal line separator + u8g2_DrawLine(u8g2, 4, y, displayWidth - UIConstants::RIGHT_PADDING, y); + + // Draw vertical line on the right side + u8g2_DrawLine(u8g2, x, y - boxHeight + 3, x, y - 1); +} \ No newline at end of file diff --git a/components/insa/src/common/PSMenu.cpp b/components/insa/src/common/PSMenu.cpp deleted file mode 100644 index c35b2a6..0000000 --- a/components/insa/src/common/PSMenu.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include "common/PSMenu.h" - -#include "PushButton.h" -#include "common/ScrollBar.h" -#include "u8g2.h" - -// Menu item type constants for better readability -namespace MenuItemTypes { - constexpr uint8_t TEXT = 0; - constexpr uint8_t SELECTION = 1; - constexpr uint8_t NUMBER = 2; - constexpr uint8_t TOGGLE = 3; -} - -// UI layout constants -namespace UIConstants { - constexpr int LEFT_MARGIN = 8; - constexpr int RIGHT_PADDING = 8; - constexpr int SCROLLBAR_WIDTH = 3; - constexpr int FRAME_BOX_SIZE = 14; - constexpr int FRAME_OFFSET = 11; - constexpr int SELECTION_MARGIN = 10; - constexpr int CORNER_RADIUS = 3; - constexpr int LINE_SPACING = 14; - constexpr int BOTTOM_OFFSET = 10; -} - -PSMenu::PSMenu(menu_options_t *options) : Widget(options->u8g2), m_options(options) -{ - // Set up button callback using lambda to forward to member function - m_options->onButtonClicked = [this](const uint8_t button) { - onButtonClicked(button); - }; -} - -PSMenu::~PSMenu() -{ - // Clean up callback to prevent dangling pointer - if (m_options) { - m_options->onButtonClicked = nullptr; - } -} - -void PSMenu::render() -{ - // Initialize selection if not set - if (m_selected_item >= m_items.size() && !m_items.empty()) { - m_selected_item = 0; - } - - // Early return if no items to render - if (m_items.empty()) { - return; - } - - // Clear screen with black background - u8g2_SetDrawColor(u8g2, 0); - u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height); - - // Set white foreground color for drawing - u8g2_SetDrawColor(u8g2, 1); - - // Draw UI components - drawScrollBar(); - drawSelectionBox(); - - // Calculate center position for main item - const int centerY = u8g2->height / 2 + 3; - - // Render the currently selected item (main/center item) - const auto& selectedItem = m_items[m_selected_item]; - renderWidget(&selectedItem, u8g2_font_helvB08_tr, UIConstants::LEFT_MARGIN, centerY); - - // Render previous item (above) if available - if (m_selected_item > 0) { - const auto& prevItem = m_items[m_selected_item - 1]; - renderWidget(&prevItem, u8g2_font_haxrcorp4089_tr, UIConstants::LEFT_MARGIN, UIConstants::LINE_SPACING); - } - - // Render next item (below) if available - if (m_selected_item < m_items.size() - 1) { - const auto& nextItem = m_items[m_selected_item + 1]; - renderWidget(&nextItem, u8g2_font_haxrcorp4089_tr, UIConstants::LEFT_MARGIN, u8g2->height - UIConstants::BOTTOM_OFFSET); - } -} - -void PSMenu::renderWidget(const MenuItem *item, const uint8_t *font, const int x, const int y) const -{ - // Set font and draw main text - u8g2_SetFont(u8g2, font); - u8g2_DrawStr(u8g2, x, y, item->getText().c_str()); - - // Render type-specific elements - switch (item->getType()) { - case MenuItemTypes::SELECTION: { - // Format selection value with angle brackets - const std::string formattedValue = "< " + item->getValue() + " >"; - const u8g2_uint_t textWidth = u8g2_GetStrWidth(u8g2, formattedValue.c_str()); - u8g2_DrawStr(u8g2, u8g2->width - textWidth - UIConstants::SELECTION_MARGIN, y, formattedValue.c_str()); - break; - } - - case MenuItemTypes::TOGGLE: { - // Draw checkbox frame - const int frameX = u8g2->width - UIConstants::FRAME_BOX_SIZE - UIConstants::SELECTION_MARGIN; - const int frameY = y - UIConstants::FRAME_OFFSET; - u8g2_DrawFrame(u8g2, frameX, frameY, UIConstants::FRAME_BOX_SIZE, UIConstants::FRAME_BOX_SIZE); - - // Draw checkmark (X) if toggle is true - if (item->getValue() == "true") { - const int checkX1 = frameX + 2; - const int checkY1 = frameY + 2; - const int checkX2 = frameX + UIConstants::FRAME_BOX_SIZE - 3; - const int checkY2 = frameY + UIConstants::FRAME_BOX_SIZE - 3; - - // Draw X pattern for checked state - u8g2_DrawLine(u8g2, checkX1, checkY1, checkX2, checkY2); - u8g2_DrawLine(u8g2, checkX1, checkY2, checkX2, checkY1); - } - break; - } - - case MenuItemTypes::TEXT: - case MenuItemTypes::NUMBER: - default: - // No additional rendering needed for text and number types - break; - } -} - -void PSMenu::onButtonClicked(const uint8_t button) -{ - // Map button input to navigation functions - switch (button) { - case BUTTON_UP: - onPressedUp(); - break; - - case BUTTON_DOWN: - onPressedDown(); - break; - - case BUTTON_LEFT: - onPressedLeft(); - break; - - case BUTTON_RIGHT: - onPressedRight(); - break; - - case BUTTON_SELECT: - onPressedSelect(); - break; - - case BUTTON_BACK: - onPressedBack(); - break; - - default: - // Ignore unknown button inputs - break; - } -} - -void PSMenu::onPressedDown() -{ - if (m_items.empty()) return; - - // Wrap around to first item when at the end - m_selected_item = (m_selected_item + 1) % m_items.size(); -} - -void PSMenu::onPressedUp() -{ - if (m_items.empty()) return; - - // Wrap around to last item when at the beginning - m_selected_item = (m_selected_item == 0) ? m_items.size() - 1 : m_selected_item - 1; -} - -void PSMenu::onPressedLeft() const -{ - if (m_selected_item < m_items.size()) { - const auto& item = m_items[m_selected_item]; - item.onButtonPressed(item.getId(), ButtonType::LEFT); - } -} - -void PSMenu::onPressedRight() const -{ - if (m_selected_item < m_items.size()) { - const auto& item = m_items[m_selected_item]; - item.onButtonPressed(item.getId(), ButtonType::RIGHT); - } -} - -void PSMenu::onPressedSelect() const -{ - if (m_selected_item < m_items.size()) { - const auto& item = m_items[m_selected_item]; - item.onButtonPressed(item.getId(), ButtonType::SELECT); - } -} - -void PSMenu::onPressedBack() const -{ - // Navigate back to previous screen if callback is available - if (m_options && m_options->popScreen) { - m_options->popScreen(); - } -} - -void PSMenu::addText(uint8_t id, const std::string &text, const ButtonCallback &callback) -{ - m_items.emplace_back(id, MenuItemTypes::TEXT, text, callback); -} - -void PSMenu::addSelection(uint8_t id, const std::string &text, std::string &value, - const std::vector &values, - const ButtonCallback &callback) -{ - m_items.emplace_back(id, MenuItemTypes::SELECTION, text, value, values, callback); -} - -void PSMenu::addNumber(uint8_t id, const std::string &text, std::string &value, const ButtonCallback &callback) -{ - m_items.emplace_back(id, MenuItemTypes::NUMBER, text, value, callback); -} - -void PSMenu::addToggle(uint8_t id, const std::string &text, bool selected, const ButtonCallback &callback) -{ - m_items.emplace_back(id, MenuItemTypes::TOGGLE, text, selected, callback); -} - -void PSMenu::drawScrollBar() const -{ - // Create scrollbar instance - ScrollBar scrollBar(m_options, u8g2->width - UIConstants::SCROLLBAR_WIDTH, 3, 1, u8g2->height - 6); - scrollBar.refresh(m_selected_item, m_items.size()); - scrollBar.render(); -} - -void PSMenu::drawSelectionBox() const -{ - // Calculate dimensions for the selection box - const auto displayHeight = u8g2->height; - const auto displayWidth = u8g2->width; - const auto boxHeight = displayHeight / 3; - const auto y = boxHeight * 2 - 2; - const auto x = displayWidth - UIConstants::RIGHT_PADDING; - - // Draw the rounded frame for the selection box - u8g2_DrawRFrame(u8g2, 2, boxHeight, displayWidth - UIConstants::RIGHT_PADDING, boxHeight, UIConstants::CORNER_RADIUS); - - // Draw horizontal line separator - u8g2_DrawLine(u8g2, 4, y, displayWidth - UIConstants::RIGHT_PADDING, y); - - // Draw vertical line on the right side - u8g2_DrawLine(u8g2, x, y - boxHeight + 3, x, y - 1); -} \ No newline at end of file diff --git a/components/insa/src/common/Widget.cpp b/components/insa/src/common/Widget.cpp index 0dcef9c..3a85b84 100644 --- a/components/insa/src/common/Widget.cpp +++ b/components/insa/src/common/Widget.cpp @@ -12,6 +12,6 @@ void Widget::render() { } -void Widget::onButtonClicked(uint8_t button) +void Widget::onButtonClicked(ButtonType button) { } diff --git a/components/insa/src/data/MenuItem.cpp b/components/insa/src/data/MenuItem.cpp index 1ccb7eb..92ad5f1 100644 --- a/components/insa/src/data/MenuItem.cpp +++ b/components/insa/src/data/MenuItem.cpp @@ -11,10 +11,9 @@ MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, std:: { } -MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, std::string value, - std::vector values, +MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, std::vector values, int index, ButtonCallback callback) - : m_id(id), m_type(type), m_text(std::move(text)), m_value(std::move(value)), m_values(std::move(values)), + : m_id(id), m_type(type), m_text(std::move(text)), m_values(std::move(values)), m_index(index), m_callback(std::move(callback)) { } @@ -43,6 +42,10 @@ const std::string &MenuItem::getText() const const std::string &MenuItem::getValue() const { + if (!m_values.empty() && m_index >= 0 && m_index < m_values.size()) + { + return m_values.at(m_index); + } return m_value; } @@ -51,15 +54,41 @@ void MenuItem::setValue(const std::string &value) m_value = value; } -void MenuItem::onButtonPressed(const uint8_t id, const ButtonType button) const +void MenuItem::onButtonPressed(const ButtonType button) const { if (m_callback) { - m_callback(id, button); + m_callback(*this, button); } } bool MenuItem::hasCallback() const { return (m_callback != nullptr); +} + +int MenuItem::getIndex() const +{ + return m_index; +} + +std::vector MenuItem::getValues() const +{ + return m_values; +} + +size_t MenuItem::getItemCount() const +{ + return m_values.size(); +} + +MenuItem MenuItem::copyWith(const size_t index) const +{ + MenuItem copy = *this; + if (index > std::numeric_limits::max()) + { + throw std::overflow_error("index is too large"); + } + copy.m_index = static_cast(index); + return copy; } \ No newline at end of file diff --git a/components/insa/src/ui/LightMenu.cpp b/components/insa/src/ui/LightMenu.cpp index 3efe4aa..6d2b289 100644 --- a/components/insa/src/ui/LightMenu.cpp +++ b/components/insa/src/ui/LightMenu.cpp @@ -1,28 +1,31 @@ #include "ui/LightMenu.h" -LightMenu::LightMenu(menu_options_t *options) : PSMenu(options), m_options(options) +#include "ui/LightSettingsMenu.h" + +namespace LightMenuItem +{ +constexpr uint8_t MODE = 1; +constexpr uint8_t TOGGLE = 2; +constexpr uint8_t LED_SETTINGS = 3; +} + +LightMenu::LightMenu(menu_options_t *options) : Menu(options), m_options(options) { std::vector values; values.emplace_back("Tag"); values.emplace_back("Nacht"); - addSelection(1, "Modus", values.front(), values, [this](const uint8_t id, const ButtonType button) { - onButtonPressed(id, button); - }); + addSelection(LightMenuItem::MODE, "Modus", values, 0); - addText(2, "LED Einstellungen", [this](const uint8_t id, const ButtonType button) { - onButtonPressed(id, button); - }); - - addToggle(3, "Toggle", false, nullptr); + addText(LightMenuItem::LED_SETTINGS, "LED Einstellungen"); } -void LightMenu::onButtonPressed(const uint8_t id, const ButtonType button) const +void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button) { std::shared_ptr widget; - switch (id) + switch (menuItem.getId()) { - case 2: - widget = std::make_shared(m_options); + case LightMenuItem::LED_SETTINGS: + widget = std::make_shared(m_options); break; default: diff --git a/components/insa/src/ui/LightSettingsMenu.cpp b/components/insa/src/ui/LightSettingsMenu.cpp new file mode 100644 index 0000000..a88e79f --- /dev/null +++ b/components/insa/src/ui/LightSettingsMenu.cpp @@ -0,0 +1,65 @@ +#include "ui/LightSettingsMenu.h" + +namespace LightSettingsMenuItem +{ +constexpr uint8_t SECTION_COUNTER = 0; +} + +LightSettingsMenu::LightSettingsMenu(menu_options_t *options) : Menu(options), m_options(options) +{ + std::vector values; + for (size_t i = 1; i <= 99; i++) + { + values.emplace_back(std::to_string(i)); + } + addSelection(LightSettingsMenuItem::SECTION_COUNTER, "Sektionen", values, 0); + + addSelection(1, "Sektion 1", values, 0); +} + +void LightSettingsMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button) +{ + /// change visible counter + switch (button) + { + case ButtonType::LEFT: + if (menuItem.getIndex() > 0) + { + const auto item = menuItem.copyWith(menuItem.getIndex() - 1); + replaceItem(menuItem.getId(), item); + } + else + { + const auto item = menuItem.copyWith(menuItem.getItemCount() - 1); + replaceItem(menuItem.getId(), item); + } + break; + + case ButtonType::RIGHT: + if (menuItem.getIndex() < menuItem.getItemCount() - 1) + { + const auto item = menuItem.copyWith(menuItem.getIndex() + 1); + replaceItem(menuItem.getId(), item); + } + else + { + const auto item = menuItem.copyWith(0); + replaceItem(menuItem.getId(), item); + } + break; + + default: + break; + } + + /// change section list + setItemSize(std::stoull(getItem(0).getValue())); + + /// persist section values + if (m_options && m_options->persistence && m_options->persistence->save) + { + const auto key = "section_" + std::to_string(menuItem.getId()); + const auto &value = getItem(menuItem.getId()).getValue(); + m_options->persistence->save(key.c_str(), value.c_str()); + } +} \ No newline at end of file diff --git a/components/insa/src/ui/MainMenu.cpp b/components/insa/src/ui/MainMenu.cpp index c6914bf..905f33f 100644 --- a/components/insa/src/ui/MainMenu.cpp +++ b/components/insa/src/ui/MainMenu.cpp @@ -4,23 +4,17 @@ #include "ui/LightMenu.h" #include "ui/SettingsMenu.h" -MainMenu::MainMenu(menu_options_t *options) : PSMenu(options), m_options(options) +MainMenu::MainMenu(menu_options_t *options) : Menu(options), m_options(options) { - addText(1, "Lichtsteuerung", [this](const uint8_t id, const ButtonType button) { - onButtonPressed(id, button); - }); - addText(2, "externe Geraete", [this](const uint8_t id, const ButtonType button) { - onButtonPressed(id, button); - }); - addText(3, "Einstellungen", [this](const uint8_t id, const ButtonType button) { - onButtonPressed(id, button); - }); + addText(1, "Lichtsteuerung"); + addText(2, "externe Geraete"); + addText(3, "Einstellungen"); } -void MainMenu::onButtonPressed(const uint8_t id, const ButtonType button) const +void MainMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button) { std::shared_ptr widget; - switch (id) + switch (menuItem.getId()) { case 1: widget = std::make_shared(m_options); diff --git a/components/insa/src/ui/SettingsMenu.cpp b/components/insa/src/ui/SettingsMenu.cpp index 129dec9..395c3bc 100644 --- a/components/insa/src/ui/SettingsMenu.cpp +++ b/components/insa/src/ui/SettingsMenu.cpp @@ -1,11 +1,6 @@ #include "ui/SettingsMenu.h" -void demo(uint8_t id, ButtonType button) +SettingsMenu::SettingsMenu(menu_options_t *options) : Menu(options) { - /// -} - -SettingsMenu::SettingsMenu(menu_options_t *options) : PSMenu(options) -{ - addText(1, "OTA Einspielen", demo); + addText(1, "OTA Einspielen"); } \ No newline at end of file diff --git a/components/ruth/CMakeLists.txt b/components/ruth/CMakeLists.txt index 09da2a1..d1b69f3 100644 --- a/components/ruth/CMakeLists.txt +++ b/components/ruth/CMakeLists.txt @@ -1,6 +1,6 @@ if (DEFINED ENV{IDF_PATH}) idf_component_register(SRCS - PushButton.cpp + persistence.c INCLUDE_DIRS "include" ) return() @@ -10,7 +10,7 @@ cmake_minimum_required(VERSION 3.30) project(ruth) add_library(${PROJECT_NAME} STATIC - PushButton.cpp + persistence.c ) include_directories(include) diff --git a/components/ruth/PushButton.cpp b/components/ruth/PushButton.cpp deleted file mode 100644 index 518526e..0000000 --- a/components/ruth/PushButton.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "include/PushButton.h" diff --git a/components/ruth/include/PushButton.h b/components/ruth/include/PushButton.h deleted file mode 100644 index ac62eab..0000000 --- a/components/ruth/include/PushButton.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#define BUTTON_UP 0 -#define BUTTON_DOWN 1 -#define BUTTON_LEFT 2 -#define BUTTON_RIGHT 3 - -#define BUTTON_SELECT 4 -#define BUTTON_BACK 5 diff --git a/components/ruth/include/persistence.h b/components/ruth/include/persistence.h new file mode 100644 index 0000000..988245f --- /dev/null +++ b/components/ruth/include/persistence.h @@ -0,0 +1,8 @@ +#pragma once + +typedef struct +{ + char *name; + + void (*save)(const char *key, const char *value); +} persistence_t; \ No newline at end of file diff --git a/components/ruth/persistence.c b/components/ruth/persistence.c new file mode 100644 index 0000000..95c603c --- /dev/null +++ b/components/ruth/persistence.c @@ -0,0 +1,3 @@ +// +// Created by Siegmund, Peter on 14.06.25. +// diff --git a/src/main.cpp b/src/main.cpp index d5a14c4..318921f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,7 +8,7 @@ #include "debug/DebugOverlay.h" #include "hal/u8g2_hal_sdl.h" #include "model/AppContext.h" -#include "PushButton.h" +#include "common/Common.h" #include "ui/Device.h" #include "ui/UIWidget.h" #include "ui/widgets/Button.h" @@ -82,27 +82,27 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) return SDL_APP_SUCCESS; case SDLK_UP: - device->OnButtonClicked(BUTTON_UP); + device->OnButtonClicked(ButtonType::UP); break; case SDLK_DOWN: - device->OnButtonClicked(BUTTON_DOWN); + device->OnButtonClicked(ButtonType::DOWN); break; case SDLK_LEFT: - device->OnButtonClicked(BUTTON_LEFT); + device->OnButtonClicked(ButtonType::LEFT); break; case SDLK_RIGHT: - device->OnButtonClicked(BUTTON_RIGHT); + device->OnButtonClicked(ButtonType::RIGHT); break; case SDLK_RETURN: - device->OnButtonClicked(BUTTON_SELECT); + device->OnButtonClicked(ButtonType::SELECT); break; case SDLK_BACKSPACE: - device->OnButtonClicked(BUTTON_BACK); + device->OnButtonClicked(ButtonType::BACK); break; default: diff --git a/src/ui/Device.cpp b/src/ui/Device.cpp index a9e0472..24cee65 100644 --- a/src/ui/Device.cpp +++ b/src/ui/Device.cpp @@ -68,6 +68,12 @@ Device::Device(void *appstate) : UIWidget(appstate) u8g2_Setup_sh1106_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sdl_hw_spi, u8x8_gpio_and_delay_sdl); u8x8_InitDisplay(u8g2_GetU8x8(&u8g2)); + static char device_storage_name[] = "device_storage"; + m_persistence = { + .name = device_storage_name, + .save = savePersistence + }; + options = { .u8g2 = &u8g2, .setScreen = [this](const std::shared_ptr &screen) { @@ -79,17 +85,18 @@ Device::Device(void *appstate) : UIWidget(appstate) .popScreen = [this]() { this->PopScreen(); }, + .persistence = &m_persistence, }; - widget = std::make_shared(&options); + m_widget = std::make_shared(&options); } void Device::SetScreen(const std::shared_ptr &screen) { if (screen != nullptr) { - widget = screen; - history.clear(); - history.emplace_back(widget); + m_widget = screen; + m_history.clear(); + m_history.emplace_back(m_widget); } } @@ -97,17 +104,17 @@ void Device::PushScreen(const std::shared_ptr &screen) { if (screen != nullptr) { - widget = screen; - history.emplace_back(widget); + m_widget = screen; + m_history.emplace_back(m_widget); } } void Device::PopScreen() { - if (history.size() >= 2) + if (m_history.size() >= 2) { - history.pop_back(); - widget = history.back(); + m_history.pop_back(); + m_widget = m_history.back(); } } @@ -119,6 +126,11 @@ void Device::PushKey(const SDL_Keycode key) SDL_PushEvent(&ev); } +void Device::savePersistence(const char *key, const char *value) +{ + SDL_Log("Saving: %s = %s", key, value); +} + void Device::RenderU8G2() const { SDL_Surface *u8g2_surface = nullptr; @@ -191,10 +203,10 @@ void Device::DrawScreen() const { u8g2_ClearBuffer(&u8g2); - if (widget != nullptr) + if (m_widget != nullptr) { - widget->update(SDL_GetTicks()); - widget->render(); + m_widget->update(SDL_GetTicks()); + m_widget->render(); } RenderU8G2(); @@ -236,11 +248,11 @@ void Device::ReleaseTap(const SDL_MouseButtonEvent *event) const } -void Device::OnButtonClicked(const uint8_t button) const +void Device::OnButtonClicked(const ButtonType button) const { - if (widget != nullptr) + if (m_widget != nullptr) { - widget->onButtonClicked(button); + m_widget->onButtonClicked(button); } } diff --git a/src/ui/Device.h b/src/ui/Device.h index 402baa1..75bfea9 100644 --- a/src/ui/Device.h +++ b/src/ui/Device.h @@ -5,12 +5,14 @@ #include #include "UIWidget.h" +#include "persistence.h" +#include "common/Common.h" #include "common/Widget.h" #include "model/AppContext.h" class Device final : public UIWidget { - public: +public: explicit Device(void *appstate); void Render() const override; @@ -19,7 +21,7 @@ class Device final : public UIWidget void ReleaseTap(const SDL_MouseButtonEvent *event) const; - void OnButtonClicked(uint8_t button) const; + void OnButtonClicked(ButtonType button) const; [[nodiscard]] bool IsHit(int mouse_x, int mouse_y) const override; @@ -27,7 +29,7 @@ class Device final : public UIWidget void ReleaseTap(int mouse_x, int mouse_y) override; - private: +private: void DrawBackground() const; void DrawScreen() const; @@ -42,7 +44,10 @@ class Device final : public UIWidget static void PushKey(SDL_Keycode key); + static void savePersistence(const char *key, const char *value); + std::vector> m_children{}; - std::shared_ptr widget; - std::vector> history; -}; + std::shared_ptr m_widget; + std::vector> m_history; + persistence_t m_persistence{}; +}; \ No newline at end of file