#include "ui/root_layout.h" #include "ui/events.h" #include "esp_log.h" #include "esp_event.h" #define TAG "RootLayout" #define HEADER_HEIGHT 40 #define NAV_BAR_HEIGHT 50 RootLayout::~RootLayout() { deinit(); } esp_err_t RootLayout::init(lv_obj_t* parent, UIHandler* ui_handler) { // Configure parent as flexbox column layout lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(parent, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); lv_obj_set_style_pad_all(parent, 0, 0); lv_obj_set_style_pad_gap(parent, 0, 0); // // Create header (top, fixed height) header_obj_ = lv_obj_create(parent); lv_obj_set_width(header_obj_, lv_pct(100)); lv_obj_set_height(header_obj_, HEADER_HEIGHT); lv_obj_set_style_bg_color(header_obj_, lv_color_white(), 0); lv_obj_set_style_border_width(header_obj_, 0, 0); lv_obj_set_style_border_color(header_obj_, lv_color_black(), 0); lv_obj_set_style_border_width(header_obj_, 1, LV_BORDER_SIDE_BOTTOM); lv_obj_set_style_pad_all(header_obj_, 0, 0); lv_obj_set_style_radius(header_obj_, 0, 0); // header_label_ = lv_label_create(header_obj_); lv_label_set_text(header_label_, "App"); lv_obj_set_style_text_color(header_label_, lv_color_black(), 0); lv_obj_align(header_label_, LV_ALIGN_LEFT_MID, 10, 0); // // Create app container (middle, flexible height) app_container_ = lv_obj_create(parent); lv_obj_set_width(app_container_, lv_pct(100)); lv_obj_set_flex_grow(app_container_, 1); lv_obj_set_style_bg_color(app_container_, lv_color_white(), 0); lv_obj_set_style_border_width(app_container_, 0, 0); lv_obj_set_style_pad_all(app_container_, 0, 0); lv_obj_set_style_radius(app_container_, 0, 0); // // Create navigation bar (bottom, fixed height) nav_bar_obj_ = lv_obj_create(parent); lv_obj_set_width(nav_bar_obj_, lv_pct(100)); lv_obj_set_height(nav_bar_obj_, NAV_BAR_HEIGHT); lv_obj_set_style_bg_color(nav_bar_obj_, lv_color_white(), 0); lv_obj_set_style_border_color(nav_bar_obj_, lv_color_black(), 0); lv_obj_set_style_border_width(nav_bar_obj_, 1, LV_BORDER_SIDE_TOP); lv_obj_set_style_pad_all(nav_bar_obj_, 5, 0); lv_obj_set_style_radius(nav_bar_obj_, 0, 0); // Create back button (aligned to start by flex layout) back_button_ = lv_btn_create(nav_bar_obj_); lv_obj_set_size(back_button_, 60, NAV_BAR_HEIGHT - 10); lv_obj_set_style_bg_color(back_button_, lv_color_white(), 0); lv_obj_add_flag(back_button_, LV_OBJ_FLAG_HIDDEN); lv_obj_t* back_label = lv_label_create(back_button_); lv_label_set_text(back_label, LV_SYMBOL_LEFT); // Create home button (aligned to end by flex layout) home_button_ = lv_btn_create(nav_bar_obj_); lv_obj_set_size(home_button_, 60, NAV_BAR_HEIGHT - 10); lv_obj_set_style_bg_color(home_button_, lv_color_white(), 0); lv_obj_t* home_label = lv_label_create(home_button_); lv_label_set_text(home_label, LV_SYMBOL_HOME); lv_obj_set_style_text_color(home_label, lv_color_black(), 0); lv_obj_align(home_label, LV_ALIGN_CENTER, 0, 0); // Register keyboard event handler esp_err_t err = esp_event_handler_instance_register( UI_EVENT_BASE, ESP_EVENT_ANY_ID, [](void* handler_args, esp_event_base_t base, int32_t id, void* event_data) { RootLayout* root_layout = static_cast(handler_args); root_layout->on_keyboard_event_(handler_args, base, id, event_data); }, this, &keyboard_event_handler_instance_ ); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to register keyboard event handler: %s", esp_err_to_name(err)); } ESP_LOGI(TAG, "Layout created with flexible design: Header=%d, NavBar=%d", HEADER_HEIGHT, NAV_BAR_HEIGHT); return ESP_OK; } esp_err_t RootLayout::deinit(void) { // Unregister keyboard event handler if (keyboard_event_handler_instance_) { esp_event_handler_instance_unregister( UI_EVENT_BASE, ESP_EVENT_ANY_ID, keyboard_event_handler_instance_ ); keyboard_event_handler_instance_ = nullptr; } header_obj_ = nullptr; header_label_ = nullptr; // app_container_ = nullptr; // nav_bar_obj_ = nullptr; back_button_ = nullptr; home_button_ = nullptr; return ESP_OK; } void RootLayout::hide_nav_bar(void) const { if (nav_bar_obj_) { lv_obj_add_flag(nav_bar_obj_, LV_OBJ_FLAG_HIDDEN); } else { ESP_LOGW(TAG, "Navigation bar not initialized"); } } void RootLayout::show_nav_bar(void) const { if (nav_bar_obj_) { lv_obj_clear_flag(nav_bar_obj_, LV_OBJ_FLAG_HIDDEN); } else { ESP_LOGW(TAG, "Navigation bar not initialized"); } } void RootLayout::show_back_button(void) const { if (back_button_) { lv_obj_clear_flag(back_button_, LV_OBJ_FLAG_HIDDEN); } else { ESP_LOGW(TAG, "Back button not initialized"); } } void RootLayout::show_home_button(void) const { if (home_button_) { lv_obj_clear_flag(home_button_, LV_OBJ_FLAG_HIDDEN); } else { ESP_LOGW(TAG, "Home button not found in navigation bar"); } } void RootLayout::hide_back_button(void) const { if (back_button_) { lv_obj_add_flag(back_button_, LV_OBJ_FLAG_HIDDEN); } else { ESP_LOGW(TAG, "Back button not initialized"); } } void RootLayout::hide_home_button(void) const { if (home_button_) { lv_obj_add_flag(home_button_, LV_OBJ_FLAG_HIDDEN); } else { ESP_LOGW(TAG, "Home button not found in navigation bar"); } } esp_err_t RootLayout::register_back_button_callback(lv_event_cb_t callback, void* user_data, lv_event_dsc_t** out_event_dsc) const { if (!back_button_) { ESP_LOGE(TAG, "Back button not initialized"); return ESP_ERR_INVALID_STATE; } if (!callback) { ESP_LOGE(TAG, "Invalid argument: callback is nullptr"); return ESP_ERR_INVALID_ARG; } if (out_event_dsc == nullptr) { ESP_LOGE(TAG, "Invalid argument: out_event_dsc is nullptr"); return ESP_ERR_INVALID_ARG; } *out_event_dsc = lv_obj_add_event_cb(back_button_, callback, LV_EVENT_CLICKED, user_data); if (*out_event_dsc == nullptr) { ESP_LOGE(TAG, "Failed to register back button callback"); return ESP_FAIL; } return ESP_OK; } esp_err_t RootLayout::register_home_button_callback(lv_event_cb_t callback, void* user_data, lv_event_dsc_t** out_event_dsc) const { if (!home_button_) { ESP_LOGE(TAG, "Home button not found in navigation bar"); return ESP_ERR_NOT_FOUND; } if (!callback) { ESP_LOGE(TAG, "Invalid argument: callback is nullptr"); return ESP_ERR_INVALID_ARG; } if (out_event_dsc == nullptr) { ESP_LOGE(TAG, "Invalid argument: out_event_dsc is nullptr"); return ESP_ERR_INVALID_ARG; } *out_event_dsc = lv_obj_add_event_cb(home_button_, callback, LV_EVENT_CLICKED, user_data); if (*out_event_dsc == nullptr) { ESP_LOGE(TAG, "Failed to register home button callback"); return ESP_FAIL; } return ESP_OK; } esp_err_t RootLayout::update_header(const std::string& title) const { if (!header_label_) { return ESP_ERR_INVALID_STATE; } if (title.empty() == false) { lv_label_set_text(header_label_, title.c_str()); } else { lv_label_set_text(header_label_, "App"); } return ESP_OK; } void RootLayout::on_keyboard_event_(void* handler_args, esp_event_base_t base, int32_t id, void* event_data) { if (base != UI_EVENT_BASE) { return; } switch (id) { case UI_EVENT_KEYBOARD_SHOWN: hide_nav_bar(); break; case UI_EVENT_KEYBOARD_HIDDEN: show_nav_bar(); break; default: ESP_LOGW(TAG, "Unknown keyboard event ID: %ld", id); break; } }