move into firmware subfolder
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
43
firmware/components/insa/CMakeLists.txt
Normal file
43
firmware/components/insa/CMakeLists.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
# Definiere die Quelldateien in einer Variable
|
||||
set(SOURCE_FILES
|
||||
src/common/InactivityTracker.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/ScreenSaver.cpp
|
||||
src/ui/SettingsMenu.cpp
|
||||
src/ui/SplashScreen.cpp
|
||||
)
|
||||
|
||||
if (DEFINED ENV{IDF_PATH})
|
||||
idf_component_register(SRCS
|
||||
${SOURCE_FILES}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES
|
||||
u8g2
|
||||
led-manager
|
||||
persistence-manager
|
||||
)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
project(insa)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
${SOURCE_FILES}
|
||||
)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
u8g2
|
||||
led-manager
|
||||
persistence-manager
|
||||
)
|
74
firmware/components/insa/include/MenuOptions.h
Normal file
74
firmware/components/insa/include/MenuOptions.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @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
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
// Project-specific headers
|
||||
#include "common/Widget.h"
|
||||
#include "IPersistenceManager.h"
|
||||
#include "u8g2.h"
|
||||
|
||||
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, input
|
||||
* handling callbacks, and persistent storage.
|
||||
*
|
||||
* @see Widget
|
||||
* @see ButtonType
|
||||
* @see IPersistenceManager
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/**
|
||||
* @brief Pointer to u8g2 display context for graphics output operations
|
||||
*/
|
||||
u8g2_t *u8g2;
|
||||
|
||||
/**
|
||||
* @brief Callback function to set the current active screen
|
||||
*/
|
||||
std::function<void(std::shared_ptr<Widget>)> setScreen;
|
||||
|
||||
/**
|
||||
* @brief Callback function to add a new screen to the navigation stack
|
||||
*/
|
||||
std::function<void(std::shared_ptr<Widget>)> pushScreen;
|
||||
|
||||
/**
|
||||
* @brief Callback function to remove the top screen from the navigation stack
|
||||
*/
|
||||
std::function<void()> popScreen;
|
||||
|
||||
/**
|
||||
* @brief Callback function to handle button press events
|
||||
*/
|
||||
std::function<void(ButtonType button)> onButtonClicked;
|
||||
|
||||
/**
|
||||
* @brief Shared pointer to platform-independent persistence manager
|
||||
* @details This provides access to persistent key-value storage across different
|
||||
* platforms. The actual implementation (SDL3 or ESP32/NVS) is determined
|
||||
* at compile time based on the target platform.
|
||||
*
|
||||
* @note The persistence manager is shared across all menu widgets and maintains
|
||||
* its state throughout the application lifecycle.
|
||||
*/
|
||||
std::shared_ptr<IPersistenceManager> persistenceManager;
|
||||
|
||||
} menu_options_t;
|
63
firmware/components/insa/include/common/Common.h
Normal file
63
firmware/components/insa/include/common/Common.h
Normal file
@@ -0,0 +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 <functional>
|
||||
|
||||
/**
|
||||
* @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
|
||||
};
|
||||
|
||||
// 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<void(MenuItem, ButtonType)> ButtonCallback;
|
||||
|
||||
// Include MenuItem.h after the typedef to avoid circular dependency
|
||||
#include "data/MenuItem.h"
|
194
firmware/components/insa/include/common/InactivityTracker.h
Normal file
194
firmware/components/insa/include/common/InactivityTracker.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* @file InactivityTracker.h
|
||||
* @brief Inactivity tracking system for monitoring user interaction timeouts
|
||||
* @details This header defines the InactivityTracker class which monitors user
|
||||
* activity and triggers timeout callbacks when the system remains inactive
|
||||
* for a specified duration. It provides essential functionality for power
|
||||
* management, screen savers, and automatic system state transitions.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-20
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @class InactivityTracker
|
||||
* @brief Activity monitoring class for detecting user inactivity periods
|
||||
* @details This class provides a robust mechanism for tracking user activity and
|
||||
* triggering automatic actions when the system remains inactive for a
|
||||
* configured timeout period. It is commonly used for implementing power
|
||||
* saving features, automatic screen savers, session timeouts, and other
|
||||
* time-based system behaviors.
|
||||
*
|
||||
* The InactivityTracker operates by:
|
||||
* - Continuously tracking elapsed time since the last user activity
|
||||
* - Comparing elapsed time against a configurable timeout threshold
|
||||
* - Executing a callback function when the timeout is reached
|
||||
* - Providing methods to reset the timer when activity is detected
|
||||
* - Supporting enable/disable functionality for dynamic control
|
||||
*
|
||||
* Key features include:
|
||||
* - Configurable timeout duration in milliseconds
|
||||
* - Custom callback function execution on timeout
|
||||
* - Activity reset capability for responsive user interaction
|
||||
* - Enable/disable control for conditional monitoring
|
||||
* - High-resolution timing support using 64-bit millisecond precision
|
||||
*
|
||||
* Common use cases:
|
||||
* - Screen saver activation after idle periods
|
||||
* - Automatic screen dimming or shutdown
|
||||
* - Session timeout management
|
||||
* - Power management and battery conservation
|
||||
* - User interface state transitions
|
||||
* - Security lockout after inactivity
|
||||
*
|
||||
* The class is designed to be lightweight and efficient, suitable for
|
||||
* real-time applications where precise timing and minimal overhead are important.
|
||||
*
|
||||
* @note This class requires regular update calls to function properly.
|
||||
* @note The timeout callback is executed once per timeout period and will
|
||||
* not repeat until the tracker is reset and times out again.
|
||||
*
|
||||
* @see Widget for integration with UI components
|
||||
* @see Menu for menu timeout implementations
|
||||
*/
|
||||
class InactivityTracker
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs an InactivityTracker with specified timeout and callback
|
||||
* @param timeoutMs Timeout duration in milliseconds before triggering callback
|
||||
* @param onTimeout Callback function to execute when timeout is reached
|
||||
*
|
||||
* @pre timeoutMs must be greater than 0 for meaningful timeout behavior
|
||||
* @pre onTimeout must be a valid callable function object
|
||||
* @post InactivityTracker is initialized, enabled, and ready for activity monitoring
|
||||
*
|
||||
* @details The constructor initializes the inactivity tracker with the specified
|
||||
* timeout duration and callback function. The tracker starts in an enabled
|
||||
* state with zero elapsed time, ready to begin monitoring user activity.
|
||||
*
|
||||
* The timeout callback function can perform any necessary actions when inactivity
|
||||
* is detected, such as:
|
||||
* - Activating screen savers or power saving modes
|
||||
* - Transitioning to different application states
|
||||
* - Logging inactivity events
|
||||
* - Triggering security lockouts
|
||||
* - Initiating automatic save operations
|
||||
*
|
||||
* @note The tracker begins monitoring immediately upon construction.
|
||||
* @note The callback function should be lightweight to avoid blocking
|
||||
* the main application thread during timeout processing.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* auto tracker = InactivityTracker(30000, []() {
|
||||
* // Activate screen saver after 30 seconds of inactivity
|
||||
* activateScreenSaver();
|
||||
* });
|
||||
* @endcode
|
||||
*/
|
||||
InactivityTracker(uint64_t timeoutMs, const std::function<void()> &onTimeout);
|
||||
|
||||
/**
|
||||
* @brief Updates the inactivity timer and checks for timeout conditions
|
||||
* @param dt Delta time in milliseconds since the last update call
|
||||
*
|
||||
* @details This method must be called regularly (typically every frame) to
|
||||
* maintain accurate timing and timeout detection. It increments the
|
||||
* elapsed time counter and triggers the timeout callback when the
|
||||
* configured timeout duration is reached.
|
||||
*
|
||||
* The update process:
|
||||
* - Adds the delta time to the elapsed time counter (if enabled)
|
||||
* - Compares elapsed time against the configured timeout threshold
|
||||
* - Executes the timeout callback if the threshold is exceeded
|
||||
* - Continues monitoring until reset or disabled
|
||||
*
|
||||
* @note This method should be called consistently from the main application
|
||||
* loop to ensure accurate timing behavior.
|
||||
* @note The timeout callback is executed only once per timeout period.
|
||||
* @note If the tracker is disabled, elapsed time is not updated.
|
||||
*
|
||||
* @see reset() to restart the inactivity timer
|
||||
* @see setEnabled() to control monitoring state
|
||||
*/
|
||||
void update(uint64_t dt);
|
||||
|
||||
/**
|
||||
* @brief Resets the inactivity timer to indicate recent user activity
|
||||
*
|
||||
* @details This method should be called whenever user activity is detected
|
||||
* to restart the inactivity timeout period. It resets the elapsed
|
||||
* time counter to zero, effectively extending the timeout deadline
|
||||
* and preventing timeout callback execution until the full timeout
|
||||
* duration elapses again without further resets.
|
||||
*
|
||||
* Common scenarios for calling reset():
|
||||
* - Button presses or key events
|
||||
* - Mouse movement or touch input
|
||||
* - Menu navigation or selection actions
|
||||
* - Any user interface interaction
|
||||
* - System activity that should extend the timeout
|
||||
*
|
||||
* @post Elapsed time is reset to zero, restarting the timeout period
|
||||
*
|
||||
* @note This method can be called at any time, even when the tracker
|
||||
* is disabled, to prepare for future monitoring.
|
||||
* @note Frequent reset calls from active user interaction will prevent
|
||||
* timeout callback execution, which is the intended behavior.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* void onButtonPress() {
|
||||
* tracker.reset(); // User activity detected, restart timeout
|
||||
* // Handle button press...
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* @brief Enables or disables inactivity monitoring
|
||||
* @param enabled True to enable monitoring, false to disable
|
||||
*
|
||||
* @details This method controls whether the inactivity tracker actively
|
||||
* monitors for timeouts. When disabled, the elapsed time counter
|
||||
* is not updated during update() calls, effectively pausing the
|
||||
* timeout detection without losing the current elapsed time state.
|
||||
*
|
||||
* Use cases for disabling:
|
||||
* - Temporary suspension during system operations
|
||||
* - Context-sensitive monitoring (disable in certain application states)
|
||||
* - Power management control (disable during low-power modes)
|
||||
* - User preference settings (allow users to disable timeouts)
|
||||
* - Development and debugging (disable for testing)
|
||||
*
|
||||
* When re-enabled, monitoring resumes from the current elapsed time state,
|
||||
* allowing for seamless pause/resume functionality.
|
||||
*
|
||||
* @post Monitoring state is updated according to the enabled parameter
|
||||
*
|
||||
* @note Disabling the tracker does not reset the elapsed time counter.
|
||||
* @note The timeout callback will not be executed while disabled, even
|
||||
* if the timeout threshold would otherwise be exceeded.
|
||||
* @note Enabling/disabling can be done at any time during operation.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* tracker.setEnabled(false); // Pause monitoring during critical operation
|
||||
* performCriticalOperation();
|
||||
* tracker.setEnabled(true); // Resume monitoring after completion
|
||||
* @endcode
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
private:
|
||||
uint64_t m_timeoutMs; ///< Timeout duration in milliseconds before callback execution
|
||||
uint64_t m_elapsedTime; ///< Current elapsed time since last reset in milliseconds
|
||||
bool m_enabled; ///< Flag indicating whether monitoring is currently active
|
||||
std::function<void()> m_onTimeout; ///< Callback function executed when timeout threshold is reached
|
||||
};
|
223
firmware/components/insa/include/common/Menu.h
Normal file
223
firmware/components/insa/include/common/Menu.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* @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);
|
||||
|
||||
void addTextCounter(uint8_t id, const std::string &text, const uint8_t value);
|
||||
|
||||
/**
|
||||
* @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<std::string> &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
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves a menu item by its index position
|
||||
* @param index Zero-based index of the menu item to retrieve
|
||||
* @return MenuItem object at the specified index
|
||||
*
|
||||
* @pre index must be within valid range [0, getItemCount()-1]
|
||||
* @post Returns a copy of the menu item at the specified position
|
||||
*
|
||||
* @throws std::out_of_range if index is invalid
|
||||
*
|
||||
* @note This method returns a copy of the menu item, not a reference
|
||||
*/
|
||||
MenuItem getItem(int index);
|
||||
|
||||
/**
|
||||
* @brief Gets the total number of menu items in the menu
|
||||
* @return Size of the menu items collection
|
||||
*
|
||||
* @post Returns current count of menu items (>= 0)
|
||||
*
|
||||
* @note This count includes all types of menu items (text, selection, toggle)
|
||||
*/
|
||||
[[nodiscard]] size_t getItemCount() const;
|
||||
|
||||
/**
|
||||
* @brief Dynamically adjusts the number of menu items to the specified size
|
||||
* @param size Target number of menu items the menu should contain
|
||||
*
|
||||
* @details If the target size is larger than current item count, new selection
|
||||
* items are added using the first item's values as template. If the
|
||||
* target size is smaller, excess items are removed from the end.
|
||||
*
|
||||
* @pre size must be > 0 and at least one menu item must exist as template
|
||||
* @post Menu contains exactly 'size' number of items
|
||||
*
|
||||
* @note New items are created as selection items with auto-generated names
|
||||
* in the format "Section X" where X is the item number
|
||||
*/
|
||||
void setItemSize(size_t size);
|
||||
|
||||
/**
|
||||
* @brief Toggles the boolean state of a toggle menu item
|
||||
* @param menuItem The toggle menu item whose state should be flipped
|
||||
*
|
||||
* @pre menuItem must be of type TOGGLE
|
||||
* @post The menu item's value is switched between "true" and "false"
|
||||
*
|
||||
* @details Changes "true" to "false" and "false" to "true" for toggle items.
|
||||
* The modified item replaces the original in the menu's item collection.
|
||||
*
|
||||
* @note This method directly modifies the menu's internal state
|
||||
*/
|
||||
void toggle(const MenuItem &menuItem);
|
||||
|
||||
/**
|
||||
* @brief Changes the selected value of a selection menu item based on button input
|
||||
* @param menuItem The selection menu item to modify
|
||||
* @param button The directional button pressed (LEFT or RIGHT)
|
||||
*
|
||||
* @pre menuItem must be of type SELECTION with valid values array
|
||||
* @post The menu item's selected index is adjusted based on button direction
|
||||
*
|
||||
* @details LEFT button moves to previous option (wraps to end if at beginning),
|
||||
* RIGHT button moves to next option (wraps to beginning if at end).
|
||||
* Other button types are ignored.
|
||||
*
|
||||
* @note The modified item replaces the original in the menu's item collection
|
||||
*/
|
||||
MenuItem switchValue(const MenuItem &menuItem, ButtonType button);
|
||||
|
||||
private:
|
||||
MenuItem replaceItem(int index, const MenuItem &item);
|
||||
|
||||
void render() override;
|
||||
|
||||
void onButtonClicked(ButtonType button) override;
|
||||
|
||||
void onPressedDown();
|
||||
|
||||
void onPressedUp();
|
||||
|
||||
void onPressedLeft() const;
|
||||
|
||||
void onPressedRight() const;
|
||||
|
||||
void onPressedSelect() const;
|
||||
|
||||
void onPressedBack() const;
|
||||
|
||||
void drawScrollBar() const;
|
||||
|
||||
void drawSelectionBox() const;
|
||||
|
||||
void renderWidget(const MenuItem *item, const uint8_t *font, int x, int y) const;
|
||||
|
||||
// Member variables
|
||||
size_t m_selected_item = 0;
|
||||
std::vector<MenuItem> m_items;
|
||||
menu_options_t *m_options;
|
||||
};
|
106
firmware/components/insa/include/common/ScrollBar.h
Normal file
106
firmware/components/insa/include/common/ScrollBar.h
Normal file
@@ -0,0 +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"
|
||||
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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'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 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 (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
|
||||
};
|
168
firmware/components/insa/include/common/Widget.h
Normal file
168
firmware/components/insa/include/common/Widget.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @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"
|
||||
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @brief Called when the widget becomes active or enters the foreground
|
||||
* @details This method is invoked when the widget transitions from inactive
|
||||
* to active state, such as when it becomes the current screen or
|
||||
* gains focus. Derived classes can override this method to perform
|
||||
* initialization tasks, reset state, or prepare for user interaction.
|
||||
*
|
||||
* @note The base implementation is empty, allowing derived classes to override
|
||||
* only if entry behavior is needed.
|
||||
* @note This method is typically called by the UI management system during
|
||||
* screen transitions or focus changes.
|
||||
*/
|
||||
virtual void enter();
|
||||
|
||||
/**
|
||||
* @brief Called when the widget is temporarily paused or loses focus
|
||||
* @details This method is invoked when the widget needs to suspend its
|
||||
* operations temporarily, such as when another widget takes focus
|
||||
* or the system enters a paused state. Derived classes can override
|
||||
* this method to pause animations, save state, or reduce resource usage.
|
||||
*
|
||||
* @note The base implementation is empty, allowing derived classes to override
|
||||
* only if pause behavior is needed.
|
||||
* @note The widget should be prepared to resume from this state when resume() is called.
|
||||
*/
|
||||
virtual void pause();
|
||||
|
||||
/**
|
||||
* @brief Called when the widget resumes from a paused state
|
||||
* @details This method is invoked when the widget transitions from paused
|
||||
* to active state, typically after a previous pause() call. Derived
|
||||
* classes can override this method to restore animations, reload
|
||||
* resources, or continue interrupted operations.
|
||||
*
|
||||
* @note The base implementation is empty, allowing derived classes to override
|
||||
* only if resume behavior is needed.
|
||||
* @note This method should restore the widget to the state it was in before pause() was called.
|
||||
*/
|
||||
virtual void resume();
|
||||
|
||||
/**
|
||||
* @brief Called when the widget is being destroyed or exits the system
|
||||
* @details This method is invoked when the widget is about to be removed
|
||||
* from the system or transitions to an inactive state permanently.
|
||||
* Derived classes can override this method to perform cleanup tasks,
|
||||
* save final state, or release resources that are not automatically freed.
|
||||
*
|
||||
* @note The base implementation is empty, allowing derived classes to override
|
||||
* only if exit behavior is needed.
|
||||
* @note This method is called before the widget's destructor and provides
|
||||
* an opportunity for controlled shutdown of widget-specific resources.
|
||||
*/
|
||||
virtual void exit();
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* @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(ButtonType button);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @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;
|
||||
};
|
310
firmware/components/insa/include/data/MenuItem.h
Normal file
310
firmware/components/insa/include/data/MenuItem.h
Normal file
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* @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 <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/Common.h"
|
||||
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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::vector<std::string> values, int index,
|
||||
ButtonCallback callback);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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(ButtonType button) const;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
[[nodiscard]] std::vector<std::string> getValues() const;
|
||||
|
||||
[[nodiscard]] size_t getItemCount() const;
|
||||
|
||||
[[nodiscard]] MenuItem copyWith(const std::string &value) const;
|
||||
|
||||
[[nodiscard]] MenuItem copyWith(size_t index) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @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<std::string> 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;
|
||||
};
|
23
firmware/components/insa/include/data/roads.h
Normal file
23
firmware/components/insa/include/data/roads.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#define road_horizontal_width 16
|
||||
#define road_horizontal_height 16
|
||||
static unsigned char road_horizontal_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x8c, 0xc7, 0x8c, 0xc7, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
#define road_vertical_width 16
|
||||
#define road_vertical_height 16
|
||||
static unsigned char road_vertical_bits[] = {0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
#define road_t_up_width 16
|
||||
#define road_t_up_height 16
|
||||
static unsigned char road_t_up_bits[] = {0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
|
||||
0x03, 0xcc, 0xcf, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
#define road_t_crossing_width 16
|
||||
#define road_t_crossing_height 16
|
||||
static unsigned char road_t_crossing_bits[] = {0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x80,
|
||||
0x01, 0xe6, 0x67, 0xe6, 0x67, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00};
|
55
firmware/components/insa/include/data/vehicles.h
Normal file
55
firmware/components/insa/include/data/vehicles.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#define car_width 16
|
||||
#define car_height 16
|
||||
const unsigned char car_left_bits [] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0xee, 0x77, 0xee, 0x77, 0xee, 0x77,
|
||||
0x2e, 0x74, 0xde, 0x78, 0x02, 0x40, 0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00
|
||||
};
|
||||
const unsigned char car_right_bits [] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0xee, 0x77, 0xee, 0x77, 0xee, 0x77,
|
||||
0x2e, 0x74, 0x1e, 0x7b, 0x02, 0x40, 0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00
|
||||
};
|
||||
|
||||
#define convertable_width 16
|
||||
#define convertable_height 16
|
||||
static unsigned char convertable_left_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x0c, 0xee,
|
||||
0x77, 0x2e, 0x74, 0xae, 0x75, 0xae, 0x75, 0xfe, 0x7f, 0x82, 0x41,
|
||||
0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00};
|
||||
static unsigned char convertable_right_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x0c, 0xee,
|
||||
0x77, 0x2e, 0x74, 0xae, 0x75, 0xae, 0x75, 0xfe, 0x7f, 0x82, 0x41,
|
||||
0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00};
|
||||
|
||||
#define lorry_width 32
|
||||
#define lorry_height 16
|
||||
const unsigned char lorry_left_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0xe0,
|
||||
0x0b, 0x00, 0x00, 0xf0, 0x0b, 0x00, 0x00, 0xee, 0x0b, 0x00, 0x00, 0xee, 0x0f,
|
||||
0xbc, 0x07, 0x2e, 0xfa, 0xff, 0x7f, 0xae, 0xfa, 0xff, 0x7f, 0xbe, 0x0a, 0x00,
|
||||
0x60, 0x02, 0x0a, 0x00, 0x60, 0x3c, 0x0a, 0xbc, 0x67, 0x42, 0xfe, 0x43, 0x78,
|
||||
0xda, 0xff, 0x5b, 0x7b, 0x18, 0x00, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00};
|
||||
const unsigned char lorry_right_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00,
|
||||
0x00, 0xd0, 0x07, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00, 0xd0, 0x77, 0xe0, 0x3d,
|
||||
0xf0, 0x77, 0xfe, 0xff, 0x5f, 0x74, 0xfe, 0xff, 0x5f, 0x75, 0x06, 0x00, 0x50,
|
||||
0x7d, 0x06, 0x00, 0x50, 0x40, 0xe6, 0x3d, 0x50, 0x3c, 0x1e, 0xc2, 0x7f, 0x42,
|
||||
0xde, 0xda, 0xff, 0x5b, 0xc0, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00};
|
||||
#define suv_width 16
|
||||
#define suv_height 16
|
||||
const unsigned char suv_left_bits[] = {0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0xee, 0x77, 0xee,
|
||||
0x77, 0xee, 0x77, 0x2e, 0x74, 0xde, 0x78, 0x02, 0x40, 0x02, 0x40,
|
||||
0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00};
|
||||
const unsigned char suv_right_bits[] = {0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0xee, 0x77, 0xee,
|
||||
0x77, 0xee, 0x77, 0x2e, 0x74, 0x1e, 0x7b, 0x02, 0x40, 0x02, 0x40,
|
||||
0x3c, 0x3c, 0x42, 0x42, 0xda, 0x5b, 0x18, 0x18, 0x00, 0x00};
|
||||
|
||||
#define truck_width 32
|
||||
#define truck_height 16
|
||||
const unsigned char truck_left_bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x00, 0xff, 0xff, 0x7f, 0xe0,
|
||||
0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0x7f, 0xee, 0xff, 0xff, 0x7f, 0xee, 0x03,
|
||||
0x00, 0x60, 0x2e, 0x02, 0x00, 0x40, 0xae, 0x02, 0x00, 0x40, 0xbe, 0x02, 0x00,
|
||||
0x40, 0x02, 0x02, 0x00, 0x40, 0x3c, 0x02, 0xbc, 0x47, 0x42, 0x02, 0x42, 0x48,
|
||||
0xda, 0xff, 0x5b, 0x7b, 0x18, 0x38, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00};
|
||||
const unsigned char truck_right_bits[] = {0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0xfe, 0xff, 0xff, 0x00, 0xfe,
|
||||
0xff, 0xff, 0x07, 0xfe, 0xff, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0x77, 0x06, 0x00,
|
||||
0xc0, 0x77, 0x02, 0x00, 0x40, 0x74, 0x02, 0x00, 0x40, 0x75, 0x02, 0x00, 0x40,
|
||||
0x7d, 0x02, 0x00, 0x40, 0x40, 0xe2, 0x3d, 0x40, 0x3c, 0x12, 0x42, 0x40, 0x42,
|
||||
0xde, 0xda, 0xff, 0x5b, 0xc0, 0x18, 0x1c, 0x18, 0x00, 0x00, 0x00, 0x00};
|
141
firmware/components/insa/include/ui/LightMenu.h
Normal file
141
firmware/components/insa/include/ui/LightMenu.h
Normal file
@@ -0,0 +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/Menu.h"
|
||||
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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;
|
||||
};
|
35
firmware/components/insa/include/ui/LightSettingsMenu.h
Normal file
35
firmware/components/insa/include/ui/LightSettingsMenu.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Menu.h"
|
||||
|
||||
/**
|
||||
* @class LightSettingsMenu
|
||||
* @brief Menu for configuring light system settings including sections and LED parameters
|
||||
* @details This menu extends the base Menu class to provide specialized functionality
|
||||
* for managing light system configurations. It handles dynamic section management
|
||||
* and provides persistence for user settings.
|
||||
*/
|
||||
class LightSettingsMenu final : public Menu
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a LightSettingsMenu with the specified options
|
||||
* @param options Pointer to menu configuration options structure
|
||||
* @details Initializes the menu with section counter and default section settings
|
||||
*/
|
||||
explicit LightSettingsMenu(menu_options_t *options);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Handles button press events for light settings menu items
|
||||
* @param menuItem The menu item that received the button press
|
||||
* @param button The type of button that was pressed
|
||||
* @details Manages value switching, dynamic section list updates, and
|
||||
* persistence of section values when settings are modified
|
||||
*/
|
||||
void onButtonPressed(const MenuItem& menuItem, ButtonType button) override;
|
||||
|
||||
static std::string CreateKey(int index);
|
||||
|
||||
menu_options_t *m_options; ///< Pointer to menu configuration options
|
||||
};
|
104
firmware/components/insa/include/ui/MainMenu.h
Normal file
104
firmware/components/insa/include/ui/MainMenu.h
Normal file
@@ -0,0 +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/Menu.h"
|
||||
|
||||
/**
|
||||
* @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 Menu
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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(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 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;
|
||||
};
|
142
firmware/components/insa/include/ui/ScreenSaver.h
Normal file
142
firmware/components/insa/include/ui/ScreenSaver.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @file ScreenSaver.h
|
||||
* @brief Animated screensaver implementation with vehicle effect
|
||||
* @details This header defines the ScreenSaver class which provides an animated
|
||||
* vehicle screensaver that activates during periods of user inactivity.
|
||||
* The screensaver displays moving vehicles to prevent screen burn-in while
|
||||
* providing visual feedback that the system is still active and responsive.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MenuOptions.h"
|
||||
#include "common/Widget.h"
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @class ScreenSaver
|
||||
* @brief Animated vehicle screensaver widget for system idle periods
|
||||
*/
|
||||
class ScreenSaver final : public Widget
|
||||
{
|
||||
public:
|
||||
explicit ScreenSaver(menu_options_t *options);
|
||||
void update(uint64_t dt) override;
|
||||
void render() override;
|
||||
void onButtonClicked(ButtonType button) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @enum VehicleType
|
||||
* @brief Types of available vehicles
|
||||
*/
|
||||
enum class VehicleType
|
||||
{
|
||||
CAR,
|
||||
CONVERTABLE,
|
||||
SUV,
|
||||
LORRY,
|
||||
TRUCK
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum Direction
|
||||
* @brief Movement direction for vehicles
|
||||
*/
|
||||
enum class Direction
|
||||
{
|
||||
LEFT,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct Vehicle
|
||||
* @brief Individual vehicle object for animation
|
||||
*/
|
||||
struct Vehicle
|
||||
{
|
||||
int x; // X position on screen
|
||||
int y; // Y position on screen
|
||||
float speed; // Movement speed
|
||||
VehicleType type; // Type of vehicle
|
||||
Direction direction; // Movement direction
|
||||
bool active; // Whether a vehicle is currently active
|
||||
};
|
||||
|
||||
static constexpr int MAX_LEFT_VEHICLES = 2;
|
||||
static constexpr int MAX_RIGHT_VEHICLES = 2;
|
||||
static constexpr int MAX_VEHICLES = MAX_LEFT_VEHICLES + MAX_RIGHT_VEHICLES;
|
||||
static constexpr int VEHICLE_SPAWN_DELAY = 2500; // milliseconds
|
||||
static constexpr float MIN_SPEED = 1.0f;
|
||||
static constexpr float MAX_SPEED = 2.0f;
|
||||
static constexpr int MIN_SAME_DIRECTION_DISTANCE = 48; // 32 + 16 pixels
|
||||
static constexpr int MAX_SAME_DIRECTION_DISTANCE = 64; // 32 + 32 pixels
|
||||
|
||||
menu_options_t *m_options;
|
||||
uint64_t m_animationCounter;
|
||||
uint64_t m_lastSpawnTime;
|
||||
|
||||
std::vector<Vehicle> m_vehicles;
|
||||
int m_leftVehicleCount;
|
||||
int m_rightVehicleCount;
|
||||
int m_sceneOffsetX = 0;
|
||||
int m_sceneOffsetY = 0;
|
||||
uint64_t m_sceneShiftTimer = 0;
|
||||
|
||||
/**
|
||||
* @brief Initialize vehicle system
|
||||
*/
|
||||
void initVehicles();
|
||||
|
||||
/**
|
||||
* @brief Spawn a new vehicle if conditions are met
|
||||
*/
|
||||
void trySpawnVehicle();
|
||||
|
||||
/**
|
||||
* @brief Get a random vehicle type
|
||||
* @return Random VehicleType
|
||||
*/
|
||||
static VehicleType getRandomVehicleType();
|
||||
|
||||
/**
|
||||
* @brief Get a random direction with constraint checking
|
||||
* @return Direction for new vehicle
|
||||
*/
|
||||
static Direction getRandomDirection();
|
||||
|
||||
/**
|
||||
* @brief Draw a vehicle at a specified position
|
||||
* @param vehicle Vehicle to draw
|
||||
*/
|
||||
void drawVehicle(const Vehicle &vehicle) const;
|
||||
|
||||
/**
|
||||
* @brief Draw a bitmap with transparency (black pixels are transparent)
|
||||
* @param x X position
|
||||
* @param y Y position
|
||||
* @param width Bitmap width
|
||||
* @param height Bitmap height
|
||||
* @param bitmap Bitmap data
|
||||
*/
|
||||
void drawTransparentBitmap(int x, int y, int width, int height, const unsigned char *bitmap) const;
|
||||
|
||||
/**
|
||||
* @brief Get vehicle bitmap data
|
||||
* @param type Vehicle type
|
||||
* @param direction Movement direction
|
||||
* @param width Output parameter for bitmap width
|
||||
* @param height Output parameter for bitmap height
|
||||
* @return Pointer to bitmap data
|
||||
*/
|
||||
static const unsigned char *getVehicleBitmap(VehicleType type, Direction direction, int &width, int &height);
|
||||
|
||||
/**
|
||||
* @brief Check if there's enough distance to spawn a vehicle in a specific direction
|
||||
* @param direction Direction to check
|
||||
* @return true if spawning is allowed
|
||||
*/
|
||||
bool canSpawnInDirection(Direction direction) const;
|
||||
};
|
77
firmware/components/insa/include/ui/SettingsMenu.h
Normal file
77
firmware/components/insa/include/ui/SettingsMenu.h
Normal file
@@ -0,0 +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/Menu.h"
|
||||
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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);
|
||||
};
|
177
firmware/components/insa/include/ui/SplashScreen.h
Normal file
177
firmware/components/insa/include/ui/SplashScreen.h
Normal file
@@ -0,0 +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"
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* 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:
|
||||
/**
|
||||
* @brief Constructs the splash screen with specified configuration
|
||||
* @param options Pointer to menu options configuration structure
|
||||
*
|
||||
* @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, animations, and timing logic
|
||||
* @param dt Delta time in milliseconds since the last update call
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* 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 visual elements to the display
|
||||
*
|
||||
* @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:
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* 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;
|
||||
};
|
35
firmware/components/insa/src/common/InactivityTracker.cpp
Normal file
35
firmware/components/insa/src/common/InactivityTracker.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "common/InactivityTracker.h"
|
||||
|
||||
InactivityTracker::InactivityTracker(const uint64_t timeoutMs, const std::function<void()> &onTimeout)
|
||||
: m_timeoutMs(timeoutMs), m_elapsedTime(0), m_enabled(true), m_onTimeout(onTimeout)
|
||||
{
|
||||
}
|
||||
|
||||
void InactivityTracker::update(uint64_t dt)
|
||||
{
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
m_elapsedTime += dt;
|
||||
|
||||
if (m_elapsedTime >= m_timeoutMs && m_onTimeout)
|
||||
{
|
||||
m_onTimeout();
|
||||
m_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void InactivityTracker::reset()
|
||||
{
|
||||
m_elapsedTime = 0;
|
||||
m_enabled = true;
|
||||
}
|
||||
|
||||
void InactivityTracker::setEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
if (enabled)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
386
firmware/components/insa/src/common/Menu.cpp
Normal file
386
firmware/components/insa/src/common/Menu.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
#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;
|
||||
constexpr uint8_t TEXT_COUNTER = 3;
|
||||
} // namespace MenuItemTypes
|
||||
|
||||
// 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;
|
||||
} // namespace UIConstants
|
||||
|
||||
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("Bereich ") + std::to_string(i + 1);
|
||||
auto index = 0;
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
constexpr int key_length = 20;
|
||||
char key[key_length] = "";
|
||||
snprintf(key, key_length, "section_%zu", i + 1);
|
||||
index = m_options->persistenceManager->GetValue(key, index);
|
||||
}
|
||||
addSelection(i + 1, caption, m_items.at(0).getValues(), index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.erase(m_items.begin() + static_cast<int>(size + 1), m_items.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::toggle(const MenuItem &menuItem)
|
||||
{
|
||||
const auto item =
|
||||
menuItem.copyWith(menuItem.getValue() == std::to_string(true) ? std::to_string(false) : std::to_string(true));
|
||||
replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
|
||||
MenuItem Menu::switchValue(const MenuItem &menuItem, ButtonType button)
|
||||
{
|
||||
MenuItem result = menuItem;
|
||||
switch (button)
|
||||
{
|
||||
case ButtonType::LEFT:
|
||||
if (menuItem.getIndex() > 0)
|
||||
{
|
||||
const auto item = menuItem.copyWith(menuItem.getIndex() - 1);
|
||||
result = replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto item = menuItem.copyWith(menuItem.getItemCount() - 1);
|
||||
result = replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonType::RIGHT:
|
||||
if (menuItem.getIndex() < menuItem.getItemCount() - 1)
|
||||
{
|
||||
const auto item = menuItem.copyWith(menuItem.getIndex() + 1);
|
||||
result = replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto item = menuItem.copyWith(0);
|
||||
result = replaceItem(menuItem.getId(), item);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MenuItem Menu::replaceItem(const int index, const MenuItem &item)
|
||||
{
|
||||
m_items.at(index) = item;
|
||||
return 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::TEXT: {
|
||||
const std::string formattedValue = ">";
|
||||
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::TEXT_COUNTER: {
|
||||
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::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() == std::to_string(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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
addTextCounter(id, text, 0);
|
||||
}
|
||||
|
||||
void Menu::addTextCounter(uint8_t id, const std::string &text, const uint8_t value)
|
||||
{
|
||||
auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void {
|
||||
onButtonPressed(menuItem, button);
|
||||
};
|
||||
if (value > 0)
|
||||
{
|
||||
m_items.emplace_back(id, MenuItemTypes::TEXT_COUNTER, text, std::to_string(value), callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.emplace_back(id, MenuItemTypes::TEXT, text, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::addSelection(uint8_t id, const std::string &text, const std::vector<std::string> &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, const bool selected)
|
||||
{
|
||||
auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void {
|
||||
onButtonPressed(menuItem, button);
|
||||
};
|
||||
m_items.emplace_back(id, MenuItemTypes::TOGGLE, text, std::to_string(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);
|
||||
}
|
41
firmware/components/insa/src/common/ScrollBar.cpp
Normal file
41
firmware/components/insa/src/common/ScrollBar.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "common/ScrollBar.h"
|
||||
|
||||
ScrollBar::ScrollBar(const menu_options_t *options, const size_t x, const size_t y, const size_t width,
|
||||
const size_t height)
|
||||
: Widget(options->u8g2), m_x(x), m_y(y), m_width(width), m_height(height), m_value(0), m_max(0), m_min(0),
|
||||
m_thumbHeight(0), m_thumbY(0)
|
||||
{
|
||||
}
|
||||
|
||||
void ScrollBar::render()
|
||||
{
|
||||
if (m_max <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t y = m_y; y < m_y + m_height; y += 2)
|
||||
{
|
||||
u8g2_DrawPixel(u8g2, m_x, y);
|
||||
}
|
||||
|
||||
u8g2_DrawBox(u8g2, u8g2->width - 4, m_thumbY, 3, m_thumbHeight);
|
||||
}
|
||||
|
||||
void ScrollBar::refresh(const size_t value, const size_t max, const size_t min)
|
||||
{
|
||||
m_value = value;
|
||||
m_max = max;
|
||||
m_min = min;
|
||||
|
||||
if (m_max <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_thumbHeight = std::max(m_height / 4, static_cast<size_t>(3));
|
||||
|
||||
const size_t trackLength = m_height - m_thumbHeight;
|
||||
|
||||
m_thumbY = m_y + (m_value * trackLength) / (m_max - 1);
|
||||
}
|
33
firmware/components/insa/src/common/Widget.cpp
Normal file
33
firmware/components/insa/src/common/Widget.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "common/Widget.h"
|
||||
|
||||
Widget::Widget(u8g2_t *u8g2) : u8g2(u8g2)
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::enter()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::pause()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::resume()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::exit()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::update(uint64_t dt)
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::render()
|
||||
{
|
||||
}
|
||||
|
||||
void Widget::onButtonClicked(ButtonType button)
|
||||
{
|
||||
}
|
99
firmware/components/insa/src/data/MenuItem.cpp
Normal file
99
firmware/components/insa/src/data/MenuItem.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "data/MenuItem.h"
|
||||
|
||||
// Constructor for basic menu items (text buttons)
|
||||
MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, ButtonCallback callback)
|
||||
: m_id(id), m_type(type), m_text(std::move(text)), m_callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor for menu items with a single value (toggles)
|
||||
MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, std::string value, ButtonCallback callback)
|
||||
: m_id(id), m_type(type), m_text(std::move(text)), m_value(std::move(value)), m_callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor for menu items with multiple values (selections)
|
||||
MenuItem::MenuItem(const uint8_t id, const uint8_t type, std::string text, std::vector<std::string> values, int index,
|
||||
ButtonCallback callback)
|
||||
: m_id(id), m_type(type), m_text(std::move(text)), m_values(std::move(values)), m_index(index),
|
||||
m_callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t MenuItem::getId() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
uint8_t MenuItem::getType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
const std::string &MenuItem::getText() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
const std::string &MenuItem::getValue() const
|
||||
{
|
||||
// Return the selected value from values array if available and index is valid
|
||||
if (!m_values.empty() && m_index >= 0 && m_index < m_values.size())
|
||||
{
|
||||
return m_values.at(m_index);
|
||||
}
|
||||
// Otherwise return the direct value
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void MenuItem::setValue(const std::string &value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
void MenuItem::onButtonPressed(const ButtonType button) const
|
||||
{
|
||||
// Execute the callback function if one is registered
|
||||
if (m_callback)
|
||||
{
|
||||
m_callback(*this, button);
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuItem::hasCallback() const
|
||||
{
|
||||
return (m_callback != nullptr);
|
||||
}
|
||||
|
||||
int MenuItem::getIndex() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
std::vector<std::string> MenuItem::getValues() const
|
||||
{
|
||||
return m_values;
|
||||
}
|
||||
|
||||
size_t MenuItem::getItemCount() const
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
|
||||
MenuItem MenuItem::copyWith(const std::string &value) const
|
||||
{
|
||||
// Create a copy of this menu item with a new value
|
||||
MenuItem copy = *this;
|
||||
copy.m_value = value;
|
||||
return copy;
|
||||
}
|
||||
|
||||
MenuItem MenuItem::copyWith(const size_t index) const
|
||||
{
|
||||
// Create a copy of this menu item with a new selected index
|
||||
MenuItem copy = *this;
|
||||
|
||||
// Check for potential overflow when converting size_t to int
|
||||
copy.m_index = static_cast<int>(index);
|
||||
return copy;
|
||||
}
|
114
firmware/components/insa/src/ui/LightMenu.cpp
Normal file
114
firmware/components/insa/src/ui/LightMenu.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "ui/LightMenu.h"
|
||||
|
||||
#include "led_manager.h"
|
||||
#include "ui/LightSettingsMenu.h"
|
||||
|
||||
/**
|
||||
* @namespace LightMenuItem
|
||||
* @brief Constants for light menu item identifiers
|
||||
*/
|
||||
namespace LightMenuItem
|
||||
{
|
||||
constexpr uint8_t ACTIVATE = 0; ///< ID for the light activation toggle
|
||||
constexpr uint8_t MODE = 1; ///< ID for the light mode selection
|
||||
constexpr uint8_t LED_SETTINGS = 2; ///< ID for the LED settings menu item
|
||||
} // namespace LightMenuItem
|
||||
|
||||
namespace LightMenuOptions
|
||||
{
|
||||
constexpr std::string LIGHT_ACTIVE = "light_active";
|
||||
constexpr std::string LIGHT_MODE = "light_mode";
|
||||
} // namespace LightMenuOptions
|
||||
|
||||
LightMenu::LightMenu(menu_options_t *options) : Menu(options), m_options(options)
|
||||
{
|
||||
// Add toggle for enabling/disabling the light system
|
||||
bool active = false;
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
active = m_options->persistenceManager->GetValue(LightMenuOptions::LIGHT_ACTIVE, active);
|
||||
}
|
||||
addToggle(LightMenuItem::ACTIVATE, "Einschalten", active);
|
||||
|
||||
// Create mode selection options (Day/Night modes)
|
||||
std::vector<std::string> values;
|
||||
values.emplace_back("Tag"); // Day mode
|
||||
values.emplace_back("Nacht"); // Night mode
|
||||
int mode_value = 0;
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
mode_value = m_options->persistenceManager->GetValue(LightMenuOptions::LIGHT_MODE, mode_value);
|
||||
}
|
||||
addSelection(LightMenuItem::MODE, "Modus", values, mode_value);
|
||||
|
||||
// Add menu item for accessing LED settings submenu
|
||||
addText(LightMenuItem::LED_SETTINGS, "Einstellungen");
|
||||
}
|
||||
|
||||
void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button)
|
||||
{
|
||||
std::shared_ptr<Widget> widget;
|
||||
|
||||
// Handle different menu items based on their ID
|
||||
switch (menuItem.getId())
|
||||
{
|
||||
case LightMenuItem::ACTIVATE: {
|
||||
// Toggle the light activation state when SELECT is pressed
|
||||
if (button == ButtonType::SELECT)
|
||||
{
|
||||
toggle(menuItem);
|
||||
if (getItem(menuItem.getId()).getValue() == "1")
|
||||
{
|
||||
led_event_data_t payload = {.value = 42};
|
||||
send_event(EVENT_LED_ON, &payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
led_event_data_t payload = {.value = 0};
|
||||
send_event(EVENT_LED_OFF, &payload);
|
||||
}
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
const auto value = getItem(menuItem.getId()).getValue() == "1";
|
||||
m_options->persistenceManager->SetValue(LightMenuOptions::LIGHT_ACTIVE, value);
|
||||
m_options->persistenceManager->Save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LightMenuItem::MODE: {
|
||||
// Switch between day/night modes using left/right buttons
|
||||
const auto item = switchValue(menuItem, button);
|
||||
if (button == ButtonType::LEFT || button == ButtonType::RIGHT)
|
||||
{
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
const auto value = getItem(item.getId()).getIndex();
|
||||
m_options->persistenceManager->SetValue(LightMenuOptions::LIGHT_MODE, value);
|
||||
m_options->persistenceManager->Save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LightMenuItem::LED_SETTINGS: {
|
||||
// Open the LED settings submenu when SELECT is pressed
|
||||
if (button == ButtonType::SELECT)
|
||||
{
|
||||
widget = std::make_shared<LightSettingsMenu>(m_options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Handle unknown menu items (no action required)
|
||||
break;
|
||||
}
|
||||
|
||||
// Push the new widget to the screen stack if one was created
|
||||
if (m_options && m_options->pushScreen)
|
||||
{
|
||||
m_options->pushScreen(widget);
|
||||
}
|
||||
}
|
57
firmware/components/insa/src/ui/LightSettingsMenu.cpp
Normal file
57
firmware/components/insa/src/ui/LightSettingsMenu.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "ui/LightSettingsMenu.h"
|
||||
|
||||
/**
|
||||
* @namespace LightSettingsMenuItem
|
||||
* @brief Constants for light settings menu item identifiers
|
||||
*/
|
||||
namespace LightSettingsMenuItem
|
||||
{
|
||||
constexpr uint8_t SECTION_COUNTER = 0; ///< ID for the section counter menu item
|
||||
}
|
||||
|
||||
std::string LightSettingsMenu::CreateKey(const int index)
|
||||
{
|
||||
constexpr int key_length = 20;
|
||||
char key[key_length] = "";
|
||||
snprintf(key, key_length, "section_%d", index);
|
||||
return key;
|
||||
}
|
||||
|
||||
LightSettingsMenu::LightSettingsMenu(menu_options_t *options) : Menu(options), m_options(options)
|
||||
{
|
||||
// Create values vector for section counts (1-99)
|
||||
std::vector<std::string> values;
|
||||
for (size_t i = 1; i <= 99; i++)
|
||||
{
|
||||
values.emplace_back(std::to_string(i));
|
||||
}
|
||||
|
||||
// Add section counter selection (allows choosing number of sections)
|
||||
auto value = 7;
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
value = m_options->persistenceManager->GetValue(CreateKey(0), value);
|
||||
}
|
||||
addSelection(LightSettingsMenuItem::SECTION_COUNTER, "Sektionen", values, value);
|
||||
|
||||
setItemSize(std::stoull(getItem(0).getValue()));
|
||||
}
|
||||
|
||||
void LightSettingsMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button)
|
||||
{
|
||||
// Handle value switching for the current menu item
|
||||
switchValue(menuItem, button);
|
||||
|
||||
// Update the section list size based on the section counter value
|
||||
if (menuItem.getId() == 0)
|
||||
{
|
||||
setItemSize(std::stoull(getItem(0).getValue()));
|
||||
}
|
||||
|
||||
// Persist the changed section values if persistence is available
|
||||
if (m_options && m_options->persistenceManager)
|
||||
{
|
||||
const auto value = getItem(menuItem.getId()).getIndex();
|
||||
m_options->persistenceManager->SetValue(CreateKey(menuItem.getId()), value);
|
||||
}
|
||||
}
|
44
firmware/components/insa/src/ui/MainMenu.cpp
Normal file
44
firmware/components/insa/src/ui/MainMenu.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "ui/MainMenu.h"
|
||||
|
||||
#include "common/Widget.h"
|
||||
#include "ui/LightMenu.h"
|
||||
#include "ui/SettingsMenu.h"
|
||||
|
||||
namespace MainMenuItem
|
||||
{
|
||||
constexpr uint8_t LIGHT = 0;
|
||||
constexpr uint8_t EXTERNAL_DEVICES = 1;
|
||||
constexpr uint8_t SETTINGS = 2;
|
||||
} // namespace MainMenuItem
|
||||
|
||||
MainMenu::MainMenu(menu_options_t *options) : Menu(options), m_options(options)
|
||||
{
|
||||
addText(MainMenuItem::LIGHT, "Lichtsteuerung");
|
||||
addTextCounter(MainMenuItem::EXTERNAL_DEVICES, "ext. Geraete", 0);
|
||||
addText(MainMenuItem::SETTINGS, "Einstellungen");
|
||||
}
|
||||
|
||||
void MainMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button)
|
||||
{
|
||||
if (button == ButtonType::SELECT)
|
||||
{
|
||||
std::shared_ptr<Widget> widget;
|
||||
switch (menuItem.getId())
|
||||
{
|
||||
case MainMenuItem::LIGHT:
|
||||
widget = std::make_shared<LightMenu>(m_options);
|
||||
break;
|
||||
|
||||
case MainMenuItem::SETTINGS:
|
||||
widget = std::make_shared<SettingsMenu>(m_options);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_options && m_options->pushScreen)
|
||||
{
|
||||
m_options->pushScreen(widget);
|
||||
}
|
||||
}
|
||||
}
|
329
firmware/components/insa/src/ui/ScreenSaver.cpp
Normal file
329
firmware/components/insa/src/ui/ScreenSaver.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
#include "ui/ScreenSaver.h"
|
||||
#include "data/roads.h"
|
||||
#include "data/vehicles.h"
|
||||
#include <cstdlib>
|
||||
|
||||
ScreenSaver::ScreenSaver(menu_options_t *options)
|
||||
: Widget(options->u8g2), m_options(options), m_animationCounter(0), m_lastSpawnTime(0), m_leftVehicleCount(0),
|
||||
m_rightVehicleCount(0)
|
||||
{
|
||||
initVehicles();
|
||||
}
|
||||
|
||||
void ScreenSaver::initVehicles()
|
||||
{
|
||||
m_vehicles.resize(MAX_VEHICLES);
|
||||
|
||||
for (auto &vehicle : m_vehicles)
|
||||
{
|
||||
vehicle.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSaver::update(const uint64_t dt)
|
||||
{
|
||||
m_animationCounter += dt;
|
||||
m_lastSpawnTime += dt;
|
||||
m_sceneShiftTimer += dt;
|
||||
|
||||
// Shift entire scene every 30 seconds
|
||||
if (m_sceneShiftTimer > 30000)
|
||||
{
|
||||
m_sceneOffsetX = (random() % 7) - 3; // -3 to +3 pixels
|
||||
m_sceneOffsetY = (random() % 7) - 3; // -3 to +3 pixels
|
||||
m_sceneShiftTimer = 0;
|
||||
}
|
||||
|
||||
// Try to spawn a new vehicle every few seconds
|
||||
if (m_lastSpawnTime > VEHICLE_SPAWN_DELAY)
|
||||
{
|
||||
trySpawnVehicle();
|
||||
m_lastSpawnTime = 0;
|
||||
}
|
||||
|
||||
// Update vehicle positions
|
||||
if (m_animationCounter > 16) // ~60 FPS
|
||||
{
|
||||
m_animationCounter = 0;
|
||||
|
||||
for (auto &vehicle : m_vehicles)
|
||||
{
|
||||
if (!vehicle.active)
|
||||
continue;
|
||||
|
||||
// Move vehicle
|
||||
if (vehicle.direction == Direction::LEFT)
|
||||
{
|
||||
vehicle.x -= static_cast<int>(vehicle.speed);
|
||||
|
||||
// Remove the vehicle if it goes off-screen
|
||||
if (vehicle.x <= -32) // Allow for largest vehicle width
|
||||
{
|
||||
vehicle.active = false;
|
||||
m_leftVehicleCount--;
|
||||
}
|
||||
}
|
||||
else // Direction::RIGHT
|
||||
{
|
||||
vehicle.x += static_cast<int>(vehicle.speed);
|
||||
|
||||
// Remove the vehicle if it goes off-screen
|
||||
if (vehicle.x >= (u8g2->width + 32)) // Allow for largest vehicle width
|
||||
{
|
||||
vehicle.active = false;
|
||||
m_rightVehicleCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenSaver::canSpawnInDirection(Direction direction) const
|
||||
{
|
||||
// Minimalen Abstand zwischen 48 und 64 Pixel zufällig wählen
|
||||
int requiredDistance =
|
||||
MIN_SAME_DIRECTION_DISTANCE + (random() % (MAX_SAME_DIRECTION_DISTANCE - MIN_SAME_DIRECTION_DISTANCE + 1));
|
||||
|
||||
for (const auto &vehicle : m_vehicles)
|
||||
{
|
||||
if (!vehicle.active || vehicle.direction != direction)
|
||||
continue;
|
||||
|
||||
// Abstand zum nächsten Fahrzeug in gleicher Richtung prüfen
|
||||
if (direction == Direction::LEFT)
|
||||
{
|
||||
// Fahrzeuge fahren von rechts nach links
|
||||
// Neues Fahrzeug würde bei u8g2->width + 16 starten
|
||||
int newVehicleX = u8g2->width + 16;
|
||||
|
||||
// Prüfen ob genug Abstand zum existierenden Fahrzeug
|
||||
if (newVehicleX - vehicle.x < requiredDistance)
|
||||
return false;
|
||||
}
|
||||
else // Direction::RIGHT
|
||||
{
|
||||
// Fahrzeuge fahren von links nach rechts
|
||||
// Neues Fahrzeug würde bei -32 starten
|
||||
int newVehicleX = -32;
|
||||
|
||||
// Prüfen ob genug Abstand zum existierenden Fahrzeug
|
||||
if (vehicle.x - newVehicleX < requiredDistance)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScreenSaver::trySpawnVehicle()
|
||||
{
|
||||
// Check if we can spawn a new vehicle
|
||||
int activeVehicles = 0;
|
||||
int availableSlot = -1;
|
||||
|
||||
for (int i = 0; i < MAX_VEHICLES; i++)
|
||||
{
|
||||
if (m_vehicles[i].active)
|
||||
{
|
||||
activeVehicles++;
|
||||
}
|
||||
else if (availableSlot == -1)
|
||||
{
|
||||
availableSlot = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't spawn if we're at max capacity or no slot available
|
||||
if (activeVehicles >= MAX_VEHICLES || availableSlot == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Direction direction = getRandomDirection();
|
||||
|
||||
// Check direction constraints
|
||||
if ((direction == Direction::LEFT && m_leftVehicleCount >= MAX_LEFT_VEHICLES) ||
|
||||
(direction == Direction::RIGHT && m_rightVehicleCount >= MAX_RIGHT_VEHICLES))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canSpawnInDirection(direction))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new vehicle
|
||||
Vehicle &newVehicle = m_vehicles[availableSlot];
|
||||
newVehicle.type = getRandomVehicleType();
|
||||
newVehicle.direction = direction;
|
||||
newVehicle.speed = MIN_SPEED + (static_cast<float>(random()) / RAND_MAX) * (MAX_SPEED - MIN_SPEED);
|
||||
|
||||
// Set Y position based on a direction (simulate opposing traffic lanes)
|
||||
const int halfHeight = u8g2->height / 2;
|
||||
if (direction == Direction::RIGHT)
|
||||
{
|
||||
// Vehicles going LEFT use bottom half of screen
|
||||
newVehicle.y = halfHeight + 8 + (random() % (halfHeight - 24));
|
||||
m_rightVehicleCount++;
|
||||
}
|
||||
else // Direction::RIGHT
|
||||
{
|
||||
// Vehicles going RIGHT use top half of screen
|
||||
newVehicle.y = 8 + (random() % (halfHeight - 24));
|
||||
m_leftVehicleCount++;
|
||||
}
|
||||
|
||||
// Set the starting X position based on the direction
|
||||
if (direction == Direction::LEFT)
|
||||
{
|
||||
// Vehicles going LEFT (from right to left) start from RIGHT side of screen
|
||||
newVehicle.x = u8g2->width + 16;
|
||||
}
|
||||
else // Direction::RIGHT
|
||||
{
|
||||
// Vehicles going RIGHT (from left to right) start from LEFT side of screen
|
||||
newVehicle.x = -32; // Account for the largest vehicle width
|
||||
}
|
||||
|
||||
newVehicle.active = true;
|
||||
}
|
||||
|
||||
ScreenSaver::VehicleType ScreenSaver::getRandomVehicleType()
|
||||
{
|
||||
switch (random() % 5)
|
||||
{
|
||||
case 0:
|
||||
return VehicleType::CAR;
|
||||
case 1:
|
||||
return VehicleType::CONVERTABLE;
|
||||
case 2:
|
||||
return VehicleType::SUV;
|
||||
case 3:
|
||||
return VehicleType::LORRY;
|
||||
case 4:
|
||||
return VehicleType::TRUCK;
|
||||
default:
|
||||
return VehicleType::CAR;
|
||||
}
|
||||
}
|
||||
|
||||
ScreenSaver::Direction ScreenSaver::getRandomDirection()
|
||||
{
|
||||
// Simple 50/50 chance for each direction
|
||||
return (random() % 2 == 0) ? Direction::LEFT : Direction::RIGHT;
|
||||
}
|
||||
|
||||
void ScreenSaver::render()
|
||||
{
|
||||
// Clear screen with a black background
|
||||
u8g2_SetDrawColor(u8g2, 0);
|
||||
u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height);
|
||||
u8g2_SetDrawColor(u8g2, 1);
|
||||
|
||||
// Calculate offsets
|
||||
const int roadOffset = (m_animationCounter / 100) % road_horizontal_width;
|
||||
|
||||
// Draw all active vehicles with a scene offset
|
||||
for (const auto &vehicle : m_vehicles)
|
||||
{
|
||||
if (vehicle.active)
|
||||
{
|
||||
Vehicle offsetVehicle = vehicle;
|
||||
offsetVehicle.x += m_sceneOffsetX;
|
||||
offsetVehicle.y += m_sceneOffsetY;
|
||||
drawVehicle(offsetVehicle);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw road with offsets
|
||||
const int y = u8g2->height / 2 - road_horizontal_height / 2 + m_sceneOffsetY;
|
||||
for (int x = -road_horizontal_width + roadOffset + m_sceneOffsetX; x <= u8g2->width; x += road_horizontal_width)
|
||||
{
|
||||
drawTransparentBitmap(x, y, road_horizontal_width, road_horizontal_height, road_horizontal_bits);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSaver::drawVehicle(const Vehicle &vehicle) const
|
||||
{
|
||||
int width, height;
|
||||
|
||||
if (const unsigned char *bitmap = getVehicleBitmap(vehicle.type, vehicle.direction, width, height))
|
||||
{
|
||||
drawTransparentBitmap(vehicle.x, vehicle.y, width, height, bitmap);
|
||||
// u8g2_DrawXBM(u8g2, vehicle.x, vehicle.y, width, height, bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSaver::drawTransparentBitmap(const int x, const int y, const int width, const int height,
|
||||
const unsigned char *bitmap) const
|
||||
{
|
||||
for (int py = 0; py < height; py++)
|
||||
{
|
||||
for (int px = 0; px < width; px++)
|
||||
{
|
||||
// Calculate byte and a bit of position in bitmap
|
||||
const int byteIndex = (py * ((width + 7) / 8)) + (px / 8);
|
||||
|
||||
// Check if the pixel is set (white)
|
||||
if (const int bitIndex = px % 8; bitmap[byteIndex] & (1 << bitIndex))
|
||||
{
|
||||
// Only draw white pixels, skip black (transparent) pixels
|
||||
const int screenX = x + px;
|
||||
|
||||
// Bounds checking
|
||||
if (const int screenY = y + py;
|
||||
screenX >= 0 && screenX < u8g2->width && screenY >= 0 && screenY < u8g2->height)
|
||||
{
|
||||
u8g2_DrawPixel(u8g2, screenX, screenY);
|
||||
}
|
||||
}
|
||||
// Black pixels are simply not drawn (transparent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned char *ScreenSaver::getVehicleBitmap(const VehicleType type, const Direction direction, int &width,
|
||||
int &height)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VehicleType::CAR:
|
||||
width = car_width;
|
||||
height = car_height;
|
||||
return (direction == Direction::LEFT) ? car_left_bits : car_right_bits;
|
||||
|
||||
case VehicleType::CONVERTABLE:
|
||||
width = convertable_width;
|
||||
height = convertable_height;
|
||||
return (direction == Direction::LEFT) ? convertable_left_bits : convertable_right_bits;
|
||||
|
||||
case VehicleType::SUV:
|
||||
width = suv_width;
|
||||
height = suv_height;
|
||||
return (direction == Direction::LEFT) ? suv_left_bits : suv_right_bits;
|
||||
|
||||
case VehicleType::LORRY:
|
||||
width = lorry_width;
|
||||
height = lorry_height;
|
||||
return (direction == Direction::LEFT) ? lorry_left_bits : lorry_right_bits;
|
||||
|
||||
case VehicleType::TRUCK:
|
||||
width = truck_width;
|
||||
height = truck_height;
|
||||
return (direction == Direction::LEFT) ? truck_left_bits : truck_right_bits;
|
||||
|
||||
default:
|
||||
width = car_width;
|
||||
height = car_height;
|
||||
return car_left_bits;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSaver::onButtonClicked(ButtonType button)
|
||||
{
|
||||
if (m_options && m_options->popScreen)
|
||||
{
|
||||
m_options->popScreen();
|
||||
}
|
||||
}
|
11
firmware/components/insa/src/ui/SettingsMenu.cpp
Normal file
11
firmware/components/insa/src/ui/SettingsMenu.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "ui/SettingsMenu.h"
|
||||
|
||||
namespace SettingsMenuItem
|
||||
{
|
||||
constexpr uint8_t OTA_UPLOAD = 0;
|
||||
}
|
||||
|
||||
SettingsMenu::SettingsMenu(menu_options_t *options) : Menu(options)
|
||||
{
|
||||
addText(SettingsMenuItem::OTA_UPLOAD, "OTA Einspielen");
|
||||
}
|
39
firmware/components/insa/src/ui/SplashScreen.cpp
Normal file
39
firmware/components/insa/src/ui/SplashScreen.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "ui/SplashScreen.h"
|
||||
|
||||
#include "ui/MainMenu.h"
|
||||
|
||||
#ifndef ESP32
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
uint64_t counter = 0;
|
||||
|
||||
SplashScreen::SplashScreen(menu_options_t *options) : Widget(options->u8g2), m_options(options)
|
||||
{
|
||||
}
|
||||
|
||||
void SplashScreen::update(const uint64_t dt)
|
||||
{
|
||||
counter += dt;
|
||||
if (counter >= 3000)
|
||||
{
|
||||
counter = 0;
|
||||
if (m_options && m_options->setScreen)
|
||||
{
|
||||
m_options->setScreen(std::make_shared<MainMenu>(m_options));
|
||||
}
|
||||
}
|
||||
#ifndef ESP32
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SplashScreen::render()
|
||||
{
|
||||
u8g2_SetFont(u8g2, u8g2_font_DigitalDisco_tr);
|
||||
u8g2_DrawStr(u8g2, 28, u8g2->height / 2 - 10, "HO Anlage");
|
||||
u8g2_DrawStr(u8g2, 30, u8g2->height / 2 + 5, "Axel Janz");
|
||||
u8g2_SetFont(u8g2, u8g2_font_haxrcorp4089_tr);
|
||||
u8g2_DrawStr(u8g2, 35, 50, "Initialisierung...");
|
||||
}
|
Reference in New Issue
Block a user