Files
StackChan/remote/code/main/ui/ui_setup_screen.c
T
2026-04-20 16:27:57 +08:00

180 lines
7.4 KiB
C

#include "ui_setup_screen.h"
#include "../lvgl_port.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
LV_IMG_DECLARE(updown_img);
lv_obj_t *setup_screen = NULL;
lv_obj_t *channel_label = NULL;
lv_obj_t *id_label = NULL;
lv_obj_t *channel_dropdown = NULL;
lv_obj_t *id_dropdown = NULL;
/**
* @brief Create the setup screen UI with configuration options
* @note This function creates a standalone screen with multiple UI elements:
* - Title label at the top
* - Channel selection dropdown with options 1-14
* - ID selection dropdown with options 0-50
* - Start button at the bottom for transitioning to running mode
* @details The function sets up dropdown controls with initial selections and
* applies specific styling including background colors and transparency
* @warning This function should only be called once per application run to avoid memory leaks
*/
void create_setup_screen()
{
while (!lvgl_port_lock()) {
vTaskDelay(pdMS_TO_TICKS(10));
}
lv_disp_t *disp = lv_disp_get_default();
if (disp == NULL) {
ESP_LOGE("UI", "No default display found!");
lvgl_port_unlock();
return;
}
if (setup_screen == NULL) {
setup_screen = lv_obj_create(NULL);
}
lv_obj_clear_flag(setup_screen, LV_OBJ_FLAG_SCROLLABLE);
// Create title
lv_obj_t *label = lv_label_create(setup_screen);
lv_label_set_text(label, "StackChan :)");
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 10);
// Create Channel selection label
channel_label = lv_label_create(setup_screen);
lv_label_set_text(channel_label, "Channel:");
lv_obj_align(channel_label, LV_ALIGN_TOP_LEFT, 5, 30);
// Create Channel dropdown
channel_dropdown = lv_dropdown_create(setup_screen);
lv_dropdown_set_options(channel_dropdown, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14");
lv_dropdown_set_selected(channel_dropdown, 0);
lv_obj_align(channel_dropdown, LV_ALIGN_TOP_LEFT, 5, 50);
lv_dropdown_set_symbol(channel_dropdown, &updown_img);
// Set dropdown background color
lv_obj_set_style_bg_color(channel_dropdown, lv_color_make(255, 255, 255), LV_PART_MAIN); // White
lv_obj_set_style_bg_opa(channel_dropdown, LV_OPA_COVER, LV_PART_MAIN); // Ensure background is opaque
// Create ID selection label
id_label = lv_label_create(setup_screen);
lv_label_set_text(id_label, "Receiver ID:");
lv_obj_align(id_label, LV_ALIGN_TOP_LEFT, 5, 100);
// Create ID dropdown
id_dropdown = lv_dropdown_create(setup_screen);
lv_dropdown_set_options(
id_dropdown,
"0(Broadcast)"
"\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n3"
"0\n31\n32\n33\n34\n35\n36\n37\n38\n39\n40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n50");
lv_dropdown_set_selected(id_dropdown, 0);
lv_obj_align(id_dropdown, LV_ALIGN_TOP_LEFT, 5, 120);
lv_dropdown_set_symbol(id_dropdown, &updown_img);
// Set dropdown background color
lv_obj_set_style_bg_color(id_dropdown, lv_color_make(255, 255, 255), LV_PART_MAIN); // White
lv_obj_set_style_bg_opa(id_dropdown, LV_OPA_COVER, LV_PART_MAIN); // Ensure background is opaque
lv_obj_t *btn_label = lv_label_create(setup_screen);
lv_label_set_text(btn_label, "Press to Start");
lv_obj_set_style_text_font(btn_label, &lv_font_montserrat_18, 0);
lv_obj_align(btn_label, LV_ALIGN_BOTTOM_MID, 0, -30);
lv_obj_t *arrow_label = lv_label_create(setup_screen);
lv_label_set_text(arrow_label, LV_SYMBOL_DOWN);
lv_obj_align(arrow_label, LV_ALIGN_BOTTOM_MID, 0, -5);
lvgl_port_unlock();
}
/**
* @brief Update the setup screen UI based on joystick input
* @param data Pointer to joystick_data_t structure containing current joystick values and selection mode
* @note This function handles joystick input to navigate and modify settings:
* - Highlights the currently selected dropdown (Channel or ID)
* - Increases/decreases values using joystick Y-axis movement
* - Updates the internal data structure with selected values
* @details
* 1. Changes background color of dropdowns to indicate selection
* 2. Processes joystick Y-axis input for value modification
* 3. Updates dropdown selections and corresponding data values
* 4. Applies debouncing delay to prevent rapid value changes
*/
void update_setup_screen(joystick_data_t *data)
{
while (!lvgl_port_lock()) {
vTaskDelay(pdMS_TO_TICKS(10));
}
// Update setup screen
if (data->select_mode == CHANNEL_SELECT) {
lv_obj_set_style_bg_color(channel_dropdown, lv_color_make(255, 255, 0), LV_PART_MAIN); // Yellow
lv_obj_set_style_bg_color(id_dropdown, lv_color_make(255, 255, 255), LV_PART_MAIN); // White
} else if (data->select_mode == ID_SELECT) {
lv_obj_set_style_bg_color(channel_dropdown, lv_color_make(255, 255, 255), LV_PART_MAIN); // White
lv_obj_set_style_bg_color(id_dropdown, lv_color_make(255, 255, 0), LV_PART_MAIN); // Yellow
}
// In setup mode, joystick up/down controls value increment/decrement
if (data->joyY > Y_CENTER + DEAD_ZONE) {
// Move up - Increase Channel
if (data->select_mode == CHANNEL_SELECT) {
uint16_t selected = lv_dropdown_get_selected(channel_dropdown);
if (selected < 13) { // Maximum index is 13 (corresponding to Channel 14)
lv_dropdown_set_selected(channel_dropdown, selected + 1);
data->channel = selected + 2; // Index + 1 + 1 = displayed value
}
} else if (data->select_mode == ID_SELECT) {
uint16_t selected = lv_dropdown_get_selected(id_dropdown);
if (selected < 50) {
lv_dropdown_set_selected(id_dropdown, selected + 1);
data->id = selected + 1;
}
}
vTaskDelay(50 / portTICK_PERIOD_MS); // Add delay to prevent rapid changes
} else if (data->joyY < Y_CENTER - DEAD_ZONE) {
// Move down - Decrease Channel
if (data->select_mode == CHANNEL_SELECT) {
uint16_t selected = lv_dropdown_get_selected(channel_dropdown);
if (selected > 0) {
lv_dropdown_set_selected(channel_dropdown, selected - 1);
data->channel = selected; // Index - 1 + 1 = index itself
}
} else if (data->select_mode == ID_SELECT) {
uint16_t selected = lv_dropdown_get_selected(id_dropdown);
if (selected > 0) {
lv_dropdown_set_selected(id_dropdown, selected - 1);
data->id = selected - 1;
}
}
vTaskDelay(50 / portTICK_PERIOD_MS); // Add delay to prevent rapid changes
}
lvgl_port_unlock();
}
/**
* @brief Destroy the setup screen and reset all UI object pointers to NULL
* @note This function properly deletes the LVGL objects and resets internal pointers
* @details
* 1. Deletes the setup screen and all child objects using lv_obj_del
* 2. Sets all UI object pointers to NULL to prevent dangling references
*/
void ui_setup_screen_destory()
{
while (!lvgl_port_lock()) {
vTaskDelay(pdMS_TO_TICKS(10));
}
if (setup_screen != NULL) {
lv_obj_del(setup_screen);
setup_screen = NULL;
}
lvgl_port_unlock();
channel_label = NULL;
id_label = NULL;
channel_dropdown = NULL;
id_dropdown = NULL;
}