- Updated RootLayout to manage layout initialization and deinitialization more effectively. - Removed unnecessary dependencies and streamlined event handling for keyboard events. - Enhanced UIHandler to utilize shared pointers for app descriptors, improving memory management. - Added methods for showing and hiding navigation elements in RootLayout. - Introduced textarea widget with instant response by disabling animations. - Improved error handling and logging throughout the UI components.
250 lines
7.4 KiB
C++
250 lines
7.4 KiB
C++
#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<RootLayout*>(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;
|
|
}
|
|
}
|