diff --git a/main/ui/app_registry.h b/main/ui/app_registry.h new file mode 100644 index 0000000..1b3b463 --- /dev/null +++ b/main/ui/app_registry.h @@ -0,0 +1,39 @@ +#pragma once +#include "ui/ui_app.h" +#include + +/** + * @brief Registry for all available apps + * + * This singleton class maintains a list of all registered + * AppDescriptor instances, allowing the UIHandler or other + * components to query available apps. + */ +class AppRegistry { +public: + static AppRegistry& instance() { + static AppRegistry registry; + return registry; + } + + AppRegistry(const AppRegistry&) = delete; + void operator=(const AppRegistry&) = delete; + AppRegistry(AppRegistry&&) = delete; + void operator=(AppRegistry&&) = delete; + + + // Register a new app descriptor + // The registry takes ownership of the descriptor pointer. + void register_app(AppDescriptor* app_descriptor) { + _app_descriptors.push_back(app_descriptor); + } + + const std::vector& get_app_descriptors() const { + return _app_descriptors; + } + +private: + AppRegistry() = default; + ~AppRegistry() = default; + std::vector _app_descriptors = {}; +}; \ No newline at end of file diff --git a/main/ui/root_layout.h b/main/ui/root_layout.h new file mode 100644 index 0000000..c4a4f28 --- /dev/null +++ b/main/ui/root_layout.h @@ -0,0 +1,138 @@ +#pragma once + +#include "lvgl.h" +#include "esp_err.h" +#include + +// Forward declaration +class UIHandler; + +/** + * @brief Root Layout Manager - manages the main screen layout + * + * The RootLayout class is responsible for: + * - Creating and managing the main screen structure (header, app container, nav bar) + * - Rendering app icons from the AppRegistry + * - Managing the back button + * - Updating header content + */ +class RootLayout { +public: + /** + * @brief Construct a new RootLayout object + * + * @param ui_handler Pointer to the UIHandler (for callbacks) + */ + RootLayout(UIHandler* ui_handler); + + /** + * @brief Initialize the layout + * + * Creates the main screen with header, app container, and navigation bar. + * + * @param parent Parent LVGL object to attach layout to + * @return ESP_OK on success, error code otherwise + */ + esp_err_t init(lv_obj_t* parent); + + /** + * @brief Deinitialize the layout + * + * Cleans up all layout widgets. + * + * @return ESP_OK on success, error code otherwise + */ + esp_err_t deinit(void); + + /** + * @brief Render app icons in the navigation bar + * + * Queries the AppRegistry for all registered apps and + * renders their icons in the navigation bar. Also creates + * the back button. + * + * @return ESP_OK on success, error code otherwise + */ + esp_err_t render_app_icons(void); + + /** + * @brief Update header with app name + * + * @param app_name Name to display in header (nullptr for default) + */ + void update_header(std::string app_name); + + /** + * @brief Show the back button + */ + void show_back_button(void); + + /** + * @brief Hide the back button + */ + void hide_back_button(void); + + /** + * @brief Get the header object + * + * @return lv_obj_t* pointer to the header container + */ + lv_obj_t* get_header(void) const { + return _header; + } + + /** + * @brief Get the app container (where apps render) + * + * @return lv_obj_t* pointer to the app container + */ + lv_obj_t* get_app_container(void) const { + return _app_container; + } + + /** + * @brief Get the navigation bar object + * + * @return lv_obj_t* pointer to the navigation bar container + */ + lv_obj_t* get_nav_bar(void) const { + return _nav_bar; + } + +private: + UIHandler* _ui_handler = nullptr; ///< Reference to UIHandler for callbacks + lv_obj_t* _header = nullptr; ///< Header area (top) + lv_obj_t* _header_label = nullptr; ///< Header text label + lv_obj_t* _app_container = nullptr; ///< Container for app widgets (middle) + lv_obj_t* _nav_bar = nullptr; ///< Navigation bar (bottom) + lv_obj_t* _back_button = nullptr; ///< Back button in navigation bar + + /** + * @brief Create the layout structure + * + * Sets up header, app container, and navigation bar with + * appropriate dimensions and positioning. + * + * @param parent Parent object to attach layout to + * @return ESP_OK on success, error code otherwise + */ + esp_err_t create_layout(lv_obj_t* parent); + + /** + * @brief Handle app icon click event + * + * Static callback for LVGL event handling. + * + * @param event LVGL event object + */ + static void on_app_icon_clicked(lv_event_t* event); + + /** + * @brief Handle back button click event + * + * Static callback for LVGL event handling. + * + * @param event LVGL event object + */ + static void on_back_button_clicked(lv_event_t* event); +}; diff --git a/main/ui/ui.h b/main/ui/ui.h deleted file mode 100644 index e69de29..0000000 diff --git a/main/ui/ui_app.h b/main/ui/ui_app.h new file mode 100644 index 0000000..42b6bac --- /dev/null +++ b/main/ui/ui_app.h @@ -0,0 +1,98 @@ +#pragma once + +#include "lvgl.h" +#include "esp_err.h" +#include + +/** + * @brief Base class for all UI applications + * + * All UI applications (apps) must inherit from this class. + * Each app is responsible for managing its own widgets within + * the provided LVGL container. The UIHandler will manage the + * lifecycle of apps and event routing. + */ +class UIApp { +public: + virtual ~UIApp() = default; + + /** + * @brief Initialize the app with the given container + * + * The app should create all its widgets as children of the + * provided container. The container is already positioned + * between the header and navigation bar. + * + * @param container LVGL container object for this app + * @return ESP_OK on success, error code otherwise + */ + virtual esp_err_t init(lv_obj_t* container) = 0; + + /** + * @brief Deinitialize and clean up app resources + * + * The app should delete all widgets and release any resources. + * The container itself will be handled by UIHandler. + * + * @return ESP_OK on success, error code otherwise + */ + virtual esp_err_t deinit(void) = 0; + + /** + * @brief Get the display name of this app + * + * Used for logging and potentially showing in navigation. + * + * @return std::string app name + */ + virtual std::string get_name(void) const = 0; + + /** + * @brief Handle system events passed from UIHandler + * + * System events include network status changes, storage ready, + * display refresh, and other system-level events. + * + * @param event_type Type/ID of the event + * @param event_data Optional event data payload + */ + virtual void handle_event(uint32_t event_type, void* event_data = nullptr) { } + + virtual bool on_back_button_pressed(void) { + return false; // default: not handled + } + + /** + * @brief Get the app's root container + * + * @return lv_obj_t* pointer to the app's container + */ + lv_obj_t* get_container(void) const { + return _container; + } + +protected: + lv_obj_t* _container = nullptr; ///< LVGL container provided by UIHandler +}; + + +class AppDescriptor { +public: + virtual ~AppDescriptor() = default; + virtual void draw_icon(lv_obj_t* parent) = 0; + + std::string get_name() const { + return _name; + } + + UIApp* get_app_instance() const { + return _app_instance; + } + +protected: + AppDescriptor(std::string name, UIApp* app_instance) + : _name(name), _app_instance(app_instance) { } + + std::string _name; + UIApp* _app_instance; +}; \ No newline at end of file diff --git a/main/ui/ui_handler.h b/main/ui/ui_handler.h new file mode 100644 index 0000000..4ec419a --- /dev/null +++ b/main/ui/ui_handler.h @@ -0,0 +1,147 @@ +#pragma once + +#include "ui_app.h" +#include "app_registry.h" +#include "root_layout.h" +#include "esp_err.h" + +// Forward declaration +class RootLayout; + +/** + * @brief UI Handler - manages app lifecycle and rendering + * + * The UIHandler manages: + * - Creation and destruction of UI apps + * - Switching between apps + * - Main screen layout (header, app container, navigation bar) + * - System event routing to active app + * - Displaying special screens (shutdown, etc.) + */ +class UIHandler { +public: + /** + * @brief Initialize the UI system with default layout + * + * Creates the main screen with: + * - Header area (top) + * - App container (middle) + * - Navigation bar (bottom) + * + * @return ESP_OK on success, error code otherwise + */ + esp_err_t init(void); + + /** + * @brief Deinitialize the UI system + * + * Cleans up the current app and destroys the main screen. + * + * @return ESP_OK on success, error code otherwise + */ + esp_err_t deinit(void); + + /** + * @brief Switch to a new app + * + * Deinitializes the current app (if any), initializes the new app, + * and updates the display. + * + * @param app Pointer to the new app to switch to + * @return ESP_OK on success, error code otherwise + */ + esp_err_t switch_app(UIApp* app); + + /** + * @brief Switch to an app by its descriptor + * + * Convenience method that extracts the UIApp from the descriptor + * and calls switch_app(). + * + * @param app_descriptor Pointer to the app descriptor + * @return ESP_OK on success, error code otherwise + */ + esp_err_t switch_app(AppDescriptor* app_descriptor); + + /** + * @brief Get the currently active app + * + * @return Pointer to the active UIApp, or nullptr if none + */ + UIApp* get_active_app(void) const { + return _active_app; + } + + /** + * @brief Route a system event to the active app + * + * If an app is active, this forwards the event to it. + * + * @param event_type Type/ID of the event + * @param event_data Optional event data payload + */ + void route_event(uint32_t event_type, void* event_data = nullptr); + + /** + * @brief Display shutdown screen + * + * Shows a shutdown screen with a message. Typically called + * before the system enters deep sleep or powers off. + * + * @param message Optional message to display (e.g., "Shutting down...") + * @return ESP_OK on success, error code otherwise + */ + esp_err_t show_shutdown_screen(std::string message = ""); + + /** + * @brief Get the main screen object + * + * @return lv_obj_t* pointer to the main screen + */ + lv_obj_t* get_main_screen(void) const { + return _main_screen; + } + + /** + * @brief Get the app container (where apps render) + * + * @return lv_obj_t* pointer to the app container + */ + lv_obj_t* get_app_container(void) const { + return _root_layout ? _root_layout->get_app_container() : nullptr; + } + + /** + * @brief Get the header object + * + * @return lv_obj_t* pointer to the header container + */ + lv_obj_t* get_header(void) const { + return _root_layout ? _root_layout->get_header() : nullptr; + } + + /** + * @brief Get the navigation bar object + * + * @return lv_obj_t* pointer to the navigation bar container + */ + lv_obj_t* get_nav_bar(void) const { + return _root_layout ? _root_layout->get_nav_bar() : nullptr; + } + + /** + * @brief Return to main screen (deinit app and show app icons) + * + * Deinitializes the active app and displays the app icons + * in the navigation bar, returning to the home screen. + * + * @return ESP_OK on success, error code otherwise + */ + esp_err_t return_to_main_screen(void); + +private: + lv_obj_t* _main_screen = nullptr; ///< Root screen + RootLayout* _root_layout = nullptr; ///< Root layout manager + UIApp* _active_app = nullptr; ///< Currently active app + UIApp* _shutdown_app = nullptr; ///< Cached shutdown app +};